root/usr/src/uts/i86xpv/boot/boot_xconsole.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/types.h>

#include <sys/hypervisor.h>
#include <sys/machparam.h>
#include <xen/public/io/console.h>
#include <sys/mach_mmu.h>

shared_info_t *HYPERVISOR_shared_info;
void *HYPERVISOR_console_page;

#if defined(_BOOT)
#include "dboot/dboot_printf.h"
char big_empty[MMU_PAGESIZE * 3];       /* room for 2 page aligned page */
#endif /* _BOOT */

unsigned short video_fb_buf[32 * 1024 + MMU_PAGESIZE];
unsigned char kb_status_buf[MMU_PAGESIZE * 2];
unsigned short *video_fb = NULL;
unsigned char *kb_status = NULL;

static volatile struct xencons_interface *cons_ifp;

#define XR_FULL(r)      ((r)->xr_in_cnt - (r)->xr_out_cnt == XR_SIZE)
#define XR_EMPTY(r)     ((r)->xr_in_cnt == (r)->xr_out_cnt)

#define PTE_BITS        (PT_VALID | PT_WRITABLE)
#define PTE_DEV_BITS    (PT_VALID | PT_WRITABLE | PT_NOCACHE | PT_NOCONSIST | \
                        PT_FOREIGN)

/*
 * For some unfortunate reason, the hypervisor doesn't bother to include the
 * shared info in the original virtual address space.  This means we can't
 * do any console I/O until we have manipulated some pagetables. So we have to
 * do this bit of code with no ability to get debug output.
 */
/*ARGSUSED*/
void
bcons_init_xen(char *cmdline)
{
#ifdef _BOOT
        int i = 0;
        uintptr_t vaddr;

        /*
         * find a page aligned virtual address in "big_empty"
         */
        vaddr = (uintptr_t)&big_empty;
        vaddr = (vaddr + MMU_PAGEOFFSET) & MMU_PAGEMASK;
        HYPERVISOR_shared_info = (shared_info_t *)vaddr;

        /*
         * Sets the "present" and "writable" bits in the PTE
         * plus user for amd64.
         */
        (void) HYPERVISOR_update_va_mapping(vaddr,
            xen_info->shared_info | PTE_BITS, UVMF_INVLPG | UVMF_LOCAL);

        if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
                /*
                 * map the xen console ring buffers
                 */
                (void) HYPERVISOR_update_va_mapping(vaddr + MMU_PAGESIZE,
                    mmu_ptob((x86pte_t)xen_info->console.domU.mfn) | PTE_BITS,
                    UVMF_INVLPG | UVMF_LOCAL);
        } else {
                /*
                 * Xen will pass dom0 information about the current
                 * display settings via xen_info->console.dom0.  This
                 * information includes what video mode we're in (vga
                 * or vesa) and some basic information about the video
                 * mode.  (screen size, cursor location, etc.)  We're
                 * just going to ignore all this info.  Here's some
                 * reasons why:
                 *
                 * - Currently Solaris itself has no support for vesa.
                 *   Also, the only way to boot Solaris is using our
                 *   patched version of grub, which conveniently doesn't
                 *   support vesa either.
                 *
                 * - By default when solaris boots up it clears the screen
                 *   thereby removing any previously displayed grub/xen
                 *   console messages, so we really don't care about the
                 *   current vga settings.
                 *
                 * Initially we'll map device memory for the frame buffer
                 * and keyboard into some local memory that already has
                 * page table entries so that we can get very basic debug
                 * output.  Later on when we're initializing page tables
                 * we'll map re-map these devices to be at their expected
                 * addresses.  Note that these mappings created below will
                 * be torn down right before the kernel boots up when
                 * all the memory and mappings associated with dboot are
                 * released.
                 *
                 * Map the frame buffer.
                 */
                vaddr = (uintptr_t)&video_fb_buf;
                vaddr = (vaddr + MMU_PAGEOFFSET) & MMU_PAGEMASK;
                for (i = 0; i < 32 * 1024; i += MMU_PAGESIZE)
                        (void) HYPERVISOR_update_va_mapping(vaddr + i,
                            0xb8000 + i | PTE_DEV_BITS,
                            UVMF_INVLPG | UVMF_LOCAL);
                video_fb = (unsigned short *)vaddr;

                /* Map the keyboard */
                vaddr = (uintptr_t)&kb_status_buf;
                vaddr = (vaddr + MMU_PAGEOFFSET) & MMU_PAGEMASK;
                (void) HYPERVISOR_update_va_mapping(vaddr, 0x0 | PTE_DEV_BITS,
                    UVMF_INVLPG | UVMF_LOCAL);
                kb_status = (unsigned char *)vaddr;
        }

#endif /* _BOOT */
        if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
                HYPERVISOR_console_page =
                    (void *)((uintptr_t)HYPERVISOR_shared_info + MMU_PAGESIZE);
        } else {
                HYPERVISOR_console_page = NULL;
        }
}


/*
 * This is the equivalent of polled I/O across the hypervisor CONSOLE
 * channel to output 1 character at a time.
 */
void
bcons_putchar_xen(int c)
{
        evtchn_send_t send;
        char buffer = (char)c;

        if (DOMAIN_IS_INITDOMAIN(xen_info)) {
                (void) HYPERVISOR_console_io(CONSOLEIO_write, 1, &buffer);
                return;
        }

        cons_ifp = (volatile struct xencons_interface *)HYPERVISOR_console_page;

        /*
         * need to add carriage return for new lines
         */
        if (c == '\n')
                bcons_putchar_xen('\r');

        /*
         * We have to wait till we have an open transmit slot.
         */
        while (cons_ifp->out_prod - cons_ifp->out_cons >=
            sizeof (cons_ifp->out))
                (void) HYPERVISOR_yield();

        cons_ifp->out[MASK_XENCONS_IDX(cons_ifp->out_prod, cons_ifp->out)] =
            (char)c;
        ++cons_ifp->out_prod;

        /*
         * Signal Domain 0 that it has something to do for us.
         */
        send.port = xen_info->console.domU.evtchn;
        (void) HYPERVISOR_event_channel_op(EVTCHNOP_send, &send);
}

static uint_t have_char = 0;
static char buffered;

/*
 * See if there is a character on input.
 */
int
bcons_ischar_xen(void)
{
        if (DOMAIN_IS_INITDOMAIN(xen_info)) {
                if (have_char)
                        return (1);
                if (HYPERVISOR_console_io(CONSOLEIO_read, 1, &buffered) > 0)
                        return (have_char = 1);
                return (0);
        }

        cons_ifp = (volatile struct xencons_interface *)HYPERVISOR_console_page;
        if (cons_ifp->in_cons == cons_ifp->in_prod)
                return (0);
        return (1);
}

/*
 * get a console input character
 */
int
bcons_getchar_xen(void)
{
        evtchn_send_t send;
        char c;

        if (DOMAIN_IS_INITDOMAIN(xen_info)) {
                while (have_char == 0)
                        (void) bcons_ischar_xen();
                have_char = 0;
                return (buffered);
        }

        cons_ifp = (volatile struct xencons_interface *)HYPERVISOR_console_page;
        while (cons_ifp->in_cons == cons_ifp->in_prod)
                (void) HYPERVISOR_yield();

        c = cons_ifp->in[MASK_XENCONS_IDX(cons_ifp->in_cons, cons_ifp->in)];
        ++cons_ifp->in_cons;

        /*
         * Signal Domain 0 that we ate a character.
         */
        send.port = xen_info->console.domU.evtchn;
        (void) HYPERVISOR_event_channel_op(EVTCHNOP_send, &send);
        return (c);
}