1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #include <sys/types.h>
  28 
  29 #include <sys/hypervisor.h>
  30 #include <sys/machparam.h>
  31 #include <xen/public/io/console.h>
  32 #include <sys/mach_mmu.h>
  33 
  34 shared_info_t *HYPERVISOR_shared_info;
  35 void *HYPERVISOR_console_page;
  36 
  37 #if defined(_BOOT)
  38 #include "dboot/dboot_printf.h"
  39 char big_empty[MMU_PAGESIZE * 3];       /* room for 2 page aligned page */
  40 #endif /* _BOOT */
  41 
  42 unsigned short video_fb_buf[32 * 1024 + MMU_PAGESIZE];
  43 unsigned char kb_status_buf[MMU_PAGESIZE * 2];
  44 unsigned short *video_fb = NULL;
  45 unsigned char *kb_status = NULL;
  46 
  47 static volatile struct xencons_interface *cons_ifp;
  48 
  49 #define XR_FULL(r)      ((r)->xr_in_cnt - (r)->xr_out_cnt == XR_SIZE)
  50 #define XR_EMPTY(r)     ((r)->xr_in_cnt == (r)->xr_out_cnt)
  51 
  52 #define PTE_BITS        (PT_VALID | PT_WRITABLE)
  53 #define PTE_DEV_BITS    (PT_VALID | PT_WRITABLE | PT_NOCACHE | PT_NOCONSIST | \
  54                         PT_FOREIGN)
  55 
  56 /*
  57  * For some unfortunate reason, the hypervisor doesn't bother to include the
  58  * shared info in the original virtual address space.  This means we can't
  59  * do any console I/O until we have manipulated some pagetables. So we have to
  60  * do this bit of code with no ability to get debug output.
  61  */
  62 /*ARGSUSED*/
  63 void
  64 bcons_init_xen(char *cmdline)
  65 {
  66 #ifdef _BOOT
  67         int i = 0;
  68         uintptr_t vaddr;
  69 
  70         /*
  71          * find a page aligned virtual address in "big_empty"
  72          */
  73         vaddr = (uintptr_t)&big_empty;
  74         vaddr = (vaddr + MMU_PAGEOFFSET) & MMU_PAGEMASK;
  75         HYPERVISOR_shared_info = (shared_info_t *)vaddr;
  76 
  77         /*
  78          * Sets the "present" and "writable" bits in the PTE
  79          * plus user for amd64.
  80          */
  81         (void) HYPERVISOR_update_va_mapping(vaddr,
  82             xen_info->shared_info | PTE_BITS, UVMF_INVLPG | UVMF_LOCAL);
  83 
  84         if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
  85                 /*
  86                  * map the xen console ring buffers
  87                  */
  88                 (void) HYPERVISOR_update_va_mapping(vaddr + MMU_PAGESIZE,
  89                     mmu_ptob((x86pte_t)xen_info->console.domU.mfn) | PTE_BITS,
  90                     UVMF_INVLPG | UVMF_LOCAL);
  91         } else {
  92                 /*
  93                  * Xen will pass dom0 information about the current
  94                  * display settings via xen_info->console.dom0.  This
  95                  * information includes what video mode we're in (vga
  96                  * or vesa) and some basic information about the video
  97                  * mode.  (screen size, cursor location, etc.)  We're
  98                  * just going to ignore all this info.  Here's some
  99                  * reasons why:
 100                  *
 101                  * - Currently Solaris itself has no support for vesa.
 102                  *   Also, the only way to boot Solaris is using our
 103                  *   patched version of grub, which conveniently doesn't
 104                  *   support vesa either.
 105                  *
 106                  * - By default when solaris boots up it clears the screen
 107                  *   thereby removing any previously displayed grub/xen
 108                  *   console messages, so we really don't care about the
 109                  *   current vga settings.
 110                  *
 111                  * Initially we'll map device memory for the frame buffer
 112                  * and keyboard into some local memory that already has
 113                  * page table entries so that we can get very basic debug
 114                  * output.  Later on when we're initializing page tables
 115                  * we'll map re-map these devices to be at their expected
 116                  * addresses.  Note that these mappings created below will
 117                  * be torn down right before the kernel boots up when
 118                  * all the memory and mappings associated with dboot are
 119                  * released.
 120                  *
 121                  * Map the frame buffer.
 122                  */
 123                 vaddr = (uintptr_t)&video_fb_buf;
 124                 vaddr = (vaddr + MMU_PAGEOFFSET) & MMU_PAGEMASK;
 125                 for (i = 0; i < 32 * 1024; i += MMU_PAGESIZE)
 126                         (void) HYPERVISOR_update_va_mapping(vaddr + i,
 127                             0xb8000 + i | PTE_DEV_BITS,
 128                             UVMF_INVLPG | UVMF_LOCAL);
 129                 video_fb = (unsigned short *)vaddr;
 130 
 131                 /* Map the keyboard */
 132                 vaddr = (uintptr_t)&kb_status_buf;
 133                 vaddr = (vaddr + MMU_PAGEOFFSET) & MMU_PAGEMASK;
 134                 (void) HYPERVISOR_update_va_mapping(vaddr, 0x0 | PTE_DEV_BITS,
 135                     UVMF_INVLPG | UVMF_LOCAL);
 136                 kb_status = (unsigned char *)vaddr;
 137         }
 138 
 139 #endif /* _BOOT */
 140         if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
 141                 HYPERVISOR_console_page =
 142                     (void *)((uintptr_t)HYPERVISOR_shared_info + MMU_PAGESIZE);
 143         } else {
 144                 HYPERVISOR_console_page = NULL;
 145         }
 146 }
 147 
 148 
 149 /*
 150  * This is the equivalent of polled I/O across the hypervisor CONSOLE
 151  * channel to output 1 character at a time.
 152  */
 153 void
 154 bcons_putchar_xen(int c)
 155 {
 156         evtchn_send_t send;
 157         char buffer = (char)c;
 158 
 159         if (DOMAIN_IS_INITDOMAIN(xen_info)) {
 160                 (void) HYPERVISOR_console_io(CONSOLEIO_write, 1, &buffer);
 161                 return;
 162         }
 163 
 164         cons_ifp = (volatile struct xencons_interface *)HYPERVISOR_console_page;
 165 
 166         /*
 167          * need to add carriage return for new lines
 168          */
 169         if (c == '\n')
 170                 bcons_putchar_xen('\r');
 171 
 172         /*
 173          * We have to wait till we have an open transmit slot.
 174          */
 175         while (cons_ifp->out_prod - cons_ifp->out_cons >=
 176             sizeof (cons_ifp->out))
 177                 (void) HYPERVISOR_yield();
 178 
 179         cons_ifp->out[MASK_XENCONS_IDX(cons_ifp->out_prod, cons_ifp->out)] =
 180             (char)c;
 181         ++cons_ifp->out_prod;
 182 
 183         /*
 184          * Signal Domain 0 that it has something to do for us.
 185          */
 186         send.port = xen_info->console.domU.evtchn;
 187         (void) HYPERVISOR_event_channel_op(EVTCHNOP_send, &send);
 188 }
 189 
 190 static uint_t have_char = 0;
 191 static char buffered;
 192 
 193 /*
 194  * See if there is a character on input.
 195  */
 196 int
 197 bcons_ischar_xen(void)
 198 {
 199         if (DOMAIN_IS_INITDOMAIN(xen_info)) {
 200                 if (have_char)
 201                         return (1);
 202                 if (HYPERVISOR_console_io(CONSOLEIO_read, 1, &buffered) > 0)
 203                         return (have_char = 1);
 204                 return (0);
 205         }
 206 
 207         cons_ifp = (volatile struct xencons_interface *)HYPERVISOR_console_page;
 208         if (cons_ifp->in_cons == cons_ifp->in_prod)
 209                 return (0);
 210         return (1);
 211 }
 212 
 213 /*
 214  * get a console input character
 215  */
 216 int
 217 bcons_getchar_xen(void)
 218 {
 219         evtchn_send_t send;
 220         char c;
 221 
 222         if (DOMAIN_IS_INITDOMAIN(xen_info)) {
 223                 while (have_char == 0)
 224                         (void) bcons_ischar_xen();
 225                 have_char = 0;
 226                 return (buffered);
 227         }
 228 
 229         cons_ifp = (volatile struct xencons_interface *)HYPERVISOR_console_page;
 230         while (cons_ifp->in_cons == cons_ifp->in_prod)
 231                 (void) HYPERVISOR_yield();
 232 
 233         c = cons_ifp->in[MASK_XENCONS_IDX(cons_ifp->in_cons, cons_ifp->in)];
 234         ++cons_ifp->in_cons;
 235 
 236         /*
 237          * Signal Domain 0 that we ate a character.
 238          */
 239         send.port = xen_info->console.domU.evtchn;
 240         (void) HYPERVISOR_event_channel_op(EVTCHNOP_send, &send);
 241         return (c);
 242 }