xref: /illumos-gate/usr/src/uts/i86xpv/boot/boot_xconsole.c (revision c1374a13e412c4ec42cba867e57347a0e049a822)
1843e1988Sjohnlev /*
2843e1988Sjohnlev  * CDDL HEADER START
3843e1988Sjohnlev  *
4843e1988Sjohnlev  * The contents of this file are subject to the terms of the
5843e1988Sjohnlev  * Common Development and Distribution License (the "License").
6843e1988Sjohnlev  * You may not use this file except in compliance with the License.
7843e1988Sjohnlev  *
8843e1988Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9843e1988Sjohnlev  * or http://www.opensolaris.org/os/licensing.
10843e1988Sjohnlev  * See the License for the specific language governing permissions
11843e1988Sjohnlev  * and limitations under the License.
12843e1988Sjohnlev  *
13843e1988Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
14843e1988Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15843e1988Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
16843e1988Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
17843e1988Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
18843e1988Sjohnlev  *
19843e1988Sjohnlev  * CDDL HEADER END
20843e1988Sjohnlev  */
21843e1988Sjohnlev 
22843e1988Sjohnlev /*
23*c1374a13SSurya Prakki  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24843e1988Sjohnlev  * Use is subject to license terms.
25843e1988Sjohnlev  */
26843e1988Sjohnlev 
27843e1988Sjohnlev #include <sys/types.h>
28843e1988Sjohnlev 
29843e1988Sjohnlev #include <sys/hypervisor.h>
30843e1988Sjohnlev #include <sys/machparam.h>
31843e1988Sjohnlev #include <xen/public/io/console.h>
32843e1988Sjohnlev #include <sys/mach_mmu.h>
33843e1988Sjohnlev 
34843e1988Sjohnlev shared_info_t *HYPERVISOR_shared_info;
35843e1988Sjohnlev void *HYPERVISOR_console_page;
36843e1988Sjohnlev 
37843e1988Sjohnlev #if defined(_BOOT)
38843e1988Sjohnlev #include "dboot/dboot_printf.h"
39843e1988Sjohnlev char big_empty[MMU_PAGESIZE * 3];	/* room for 2 page aligned page */
40843e1988Sjohnlev #endif /* _BOOT */
41843e1988Sjohnlev 
42843e1988Sjohnlev unsigned short video_fb_buf[32 * 1024 + MMU_PAGESIZE];
43843e1988Sjohnlev unsigned char kb_status_buf[MMU_PAGESIZE * 2];
44843e1988Sjohnlev unsigned short *video_fb = NULL;
45843e1988Sjohnlev unsigned char *kb_status = NULL;
46843e1988Sjohnlev 
47843e1988Sjohnlev static volatile struct xencons_interface *cons_ifp;
48843e1988Sjohnlev 
49843e1988Sjohnlev #define	XR_FULL(r)	((r)->xr_in_cnt - (r)->xr_out_cnt == XR_SIZE)
50843e1988Sjohnlev #define	XR_EMPTY(r)	((r)->xr_in_cnt == (r)->xr_out_cnt)
51843e1988Sjohnlev 
52843e1988Sjohnlev #define	PTE_BITS	(PT_VALID | PT_WRITABLE)
53843e1988Sjohnlev #define	PTE_DEV_BITS	(PT_VALID | PT_WRITABLE | PT_NOCACHE | PT_NOCONSIST | \
54843e1988Sjohnlev 			PT_FOREIGN)
55843e1988Sjohnlev 
56843e1988Sjohnlev /*
57843e1988Sjohnlev  * For some unfortunate reason, the hypervisor doesn't bother to include the
58843e1988Sjohnlev  * shared info in the original virtual address space.  This means we can't
59843e1988Sjohnlev  * do any console I/O until we have manipulated some pagetables. So we have to
60843e1988Sjohnlev  * do this bit of code with no ability to get debug output.
61843e1988Sjohnlev  */
62843e1988Sjohnlev /*ARGSUSED*/
63843e1988Sjohnlev void
bcons_init_xen(char * cmdline)64843e1988Sjohnlev bcons_init_xen(char *cmdline)
65843e1988Sjohnlev {
66843e1988Sjohnlev #ifdef _BOOT
67843e1988Sjohnlev 	int i = 0;
68843e1988Sjohnlev 	uintptr_t vaddr;
69843e1988Sjohnlev 
70843e1988Sjohnlev 	/*
71843e1988Sjohnlev 	 * find a page aligned virtual address in "big_empty"
72843e1988Sjohnlev 	 */
73843e1988Sjohnlev 	vaddr = (uintptr_t)&big_empty;
74843e1988Sjohnlev 	vaddr = (vaddr + MMU_PAGEOFFSET) & MMU_PAGEMASK;
75843e1988Sjohnlev 	HYPERVISOR_shared_info = (shared_info_t *)vaddr;
76843e1988Sjohnlev 
77843e1988Sjohnlev 	/*
78843e1988Sjohnlev 	 * Sets the "present" and "writable" bits in the PTE
79843e1988Sjohnlev 	 * plus user for amd64.
80843e1988Sjohnlev 	 */
81*c1374a13SSurya Prakki 	(void) HYPERVISOR_update_va_mapping(vaddr,
82*c1374a13SSurya Prakki 	    xen_info->shared_info | PTE_BITS, UVMF_INVLPG | UVMF_LOCAL);
83843e1988Sjohnlev 
84843e1988Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
85843e1988Sjohnlev 		/*
86843e1988Sjohnlev 		 * map the xen console ring buffers
87843e1988Sjohnlev 		 */
88*c1374a13SSurya Prakki 		(void) HYPERVISOR_update_va_mapping(vaddr + MMU_PAGESIZE,
89843e1988Sjohnlev 		    mmu_ptob((x86pte_t)xen_info->console.domU.mfn) | PTE_BITS,
90843e1988Sjohnlev 		    UVMF_INVLPG | UVMF_LOCAL);
91843e1988Sjohnlev 	} else {
92843e1988Sjohnlev 		/*
93843e1988Sjohnlev 		 * Xen will pass dom0 information about the current
94843e1988Sjohnlev 		 * display settings via xen_info->console.dom0.  This
95843e1988Sjohnlev 		 * information includes what video mode we're in (vga
96843e1988Sjohnlev 		 * or vesa) and some basic information about the video
97843e1988Sjohnlev 		 * mode.  (screen size, cursor location, etc.)  We're
98843e1988Sjohnlev 		 * just going to ignore all this info.  Here's some
99843e1988Sjohnlev 		 * reasons why:
100843e1988Sjohnlev 		 *
101843e1988Sjohnlev 		 * - Currently Solaris itself has no support for vesa.
102843e1988Sjohnlev 		 *   Also, the only way to boot Solaris is using our
103843e1988Sjohnlev 		 *   patched version of grub, which conveniently doesn't
104843e1988Sjohnlev 		 *   support vesa either.
105843e1988Sjohnlev 		 *
106843e1988Sjohnlev 		 * - By default when solaris boots up it clears the screen
107843e1988Sjohnlev 		 *   thereby removing any previously displayed grub/xen
108843e1988Sjohnlev 		 *   console messages, so we really don't care about the
109843e1988Sjohnlev 		 *   current vga settings.
110843e1988Sjohnlev 		 *
111843e1988Sjohnlev 		 * Initially we'll map device memory for the frame buffer
112843e1988Sjohnlev 		 * and keyboard into some local memory that already has
113843e1988Sjohnlev 		 * page table entries so that we can get very basic debug
114843e1988Sjohnlev 		 * output.  Later on when we're initializing page tables
115843e1988Sjohnlev 		 * we'll map re-map these devices to be at their expected
116843e1988Sjohnlev 		 * addresses.  Note that these mappings created below will
117843e1988Sjohnlev 		 * be torn down right before the kernel boots up when
118843e1988Sjohnlev 		 * all the memory and mappings associated with dboot are
119843e1988Sjohnlev 		 * released.
120843e1988Sjohnlev 		 *
121843e1988Sjohnlev 		 * Map the frame buffer.
122843e1988Sjohnlev 		 */
123843e1988Sjohnlev 		vaddr = (uintptr_t)&video_fb_buf;
124843e1988Sjohnlev 		vaddr = (vaddr + MMU_PAGEOFFSET) & MMU_PAGEMASK;
125843e1988Sjohnlev 		for (i = 0; i < 32 * 1024; i += MMU_PAGESIZE)
126843e1988Sjohnlev 			(void) HYPERVISOR_update_va_mapping(vaddr + i,
127843e1988Sjohnlev 			    0xb8000 + i | PTE_DEV_BITS,
128843e1988Sjohnlev 			    UVMF_INVLPG | UVMF_LOCAL);
129843e1988Sjohnlev 		video_fb = (unsigned short *)vaddr;
130843e1988Sjohnlev 
131843e1988Sjohnlev 		/* Map the keyboard */
132843e1988Sjohnlev 		vaddr = (uintptr_t)&kb_status_buf;
133843e1988Sjohnlev 		vaddr = (vaddr + MMU_PAGEOFFSET) & MMU_PAGEMASK;
134843e1988Sjohnlev 		(void) HYPERVISOR_update_va_mapping(vaddr, 0x0 | PTE_DEV_BITS,
135843e1988Sjohnlev 		    UVMF_INVLPG | UVMF_LOCAL);
136843e1988Sjohnlev 		kb_status = (unsigned char *)vaddr;
137843e1988Sjohnlev 	}
138843e1988Sjohnlev 
139843e1988Sjohnlev #endif /* _BOOT */
140843e1988Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
141843e1988Sjohnlev 		HYPERVISOR_console_page =
142843e1988Sjohnlev 		    (void *)((uintptr_t)HYPERVISOR_shared_info + MMU_PAGESIZE);
143843e1988Sjohnlev 	} else {
144843e1988Sjohnlev 		HYPERVISOR_console_page = NULL;
145843e1988Sjohnlev 	}
146843e1988Sjohnlev }
147843e1988Sjohnlev 
148843e1988Sjohnlev 
149843e1988Sjohnlev /*
150843e1988Sjohnlev  * This is the equivalent of polled I/O across the hypervisor CONSOLE
151843e1988Sjohnlev  * channel to output 1 character at a time.
152843e1988Sjohnlev  */
153843e1988Sjohnlev void
bcons_putchar_xen(int c)154843e1988Sjohnlev bcons_putchar_xen(int c)
155843e1988Sjohnlev {
156843e1988Sjohnlev 	evtchn_send_t send;
157843e1988Sjohnlev 	char buffer = (char)c;
158843e1988Sjohnlev 
159843e1988Sjohnlev 	if (DOMAIN_IS_INITDOMAIN(xen_info)) {
160843e1988Sjohnlev 		(void) HYPERVISOR_console_io(CONSOLEIO_write, 1, &buffer);
161843e1988Sjohnlev 		return;
162843e1988Sjohnlev 	}
163843e1988Sjohnlev 
164843e1988Sjohnlev 	cons_ifp = (volatile struct xencons_interface *)HYPERVISOR_console_page;
165843e1988Sjohnlev 
166843e1988Sjohnlev 	/*
167843e1988Sjohnlev 	 * need to add carriage return for new lines
168843e1988Sjohnlev 	 */
169843e1988Sjohnlev 	if (c == '\n')
170843e1988Sjohnlev 		bcons_putchar_xen('\r');
171843e1988Sjohnlev 
172843e1988Sjohnlev 	/*
173843e1988Sjohnlev 	 * We have to wait till we have an open transmit slot.
174843e1988Sjohnlev 	 */
175843e1988Sjohnlev 	while (cons_ifp->out_prod - cons_ifp->out_cons >=
176843e1988Sjohnlev 	    sizeof (cons_ifp->out))
177843e1988Sjohnlev 		(void) HYPERVISOR_yield();
178843e1988Sjohnlev 
179843e1988Sjohnlev 	cons_ifp->out[MASK_XENCONS_IDX(cons_ifp->out_prod, cons_ifp->out)] =
180843e1988Sjohnlev 	    (char)c;
181843e1988Sjohnlev 	++cons_ifp->out_prod;
182843e1988Sjohnlev 
183843e1988Sjohnlev 	/*
184843e1988Sjohnlev 	 * Signal Domain 0 that it has something to do for us.
185843e1988Sjohnlev 	 */
186843e1988Sjohnlev 	send.port = xen_info->console.domU.evtchn;
187843e1988Sjohnlev 	(void) HYPERVISOR_event_channel_op(EVTCHNOP_send, &send);
188843e1988Sjohnlev }
189843e1988Sjohnlev 
190843e1988Sjohnlev static uint_t have_char = 0;
191843e1988Sjohnlev static char buffered;
192843e1988Sjohnlev 
193843e1988Sjohnlev /*
194843e1988Sjohnlev  * See if there is a character on input.
195843e1988Sjohnlev  */
196843e1988Sjohnlev int
bcons_ischar_xen(void)197843e1988Sjohnlev bcons_ischar_xen(void)
198843e1988Sjohnlev {
199843e1988Sjohnlev 	if (DOMAIN_IS_INITDOMAIN(xen_info)) {
200843e1988Sjohnlev 		if (have_char)
201843e1988Sjohnlev 			return (1);
202843e1988Sjohnlev 		if (HYPERVISOR_console_io(CONSOLEIO_read, 1, &buffered) > 0)
203843e1988Sjohnlev 			return (have_char = 1);
204843e1988Sjohnlev 		return (0);
205843e1988Sjohnlev 	}
206843e1988Sjohnlev 
207843e1988Sjohnlev 	cons_ifp = (volatile struct xencons_interface *)HYPERVISOR_console_page;
208843e1988Sjohnlev 	if (cons_ifp->in_cons == cons_ifp->in_prod)
209843e1988Sjohnlev 		return (0);
210843e1988Sjohnlev 	return (1);
211843e1988Sjohnlev }
212843e1988Sjohnlev 
213843e1988Sjohnlev /*
214843e1988Sjohnlev  * get a console input character
215843e1988Sjohnlev  */
216843e1988Sjohnlev int
bcons_getchar_xen(void)217843e1988Sjohnlev bcons_getchar_xen(void)
218843e1988Sjohnlev {
219843e1988Sjohnlev 	evtchn_send_t send;
220843e1988Sjohnlev 	char c;
221843e1988Sjohnlev 
222843e1988Sjohnlev 	if (DOMAIN_IS_INITDOMAIN(xen_info)) {
223843e1988Sjohnlev 		while (have_char == 0)
224843e1988Sjohnlev 			(void) bcons_ischar_xen();
225843e1988Sjohnlev 		have_char = 0;
226843e1988Sjohnlev 		return (buffered);
227843e1988Sjohnlev 	}
228843e1988Sjohnlev 
229843e1988Sjohnlev 	cons_ifp = (volatile struct xencons_interface *)HYPERVISOR_console_page;
230843e1988Sjohnlev 	while (cons_ifp->in_cons == cons_ifp->in_prod)
231843e1988Sjohnlev 		(void) HYPERVISOR_yield();
232843e1988Sjohnlev 
233843e1988Sjohnlev 	c = cons_ifp->in[MASK_XENCONS_IDX(cons_ifp->in_cons, cons_ifp->in)];
234843e1988Sjohnlev 	++cons_ifp->in_cons;
235843e1988Sjohnlev 
236843e1988Sjohnlev 	/*
237843e1988Sjohnlev 	 * Signal Domain 0 that we ate a character.
238843e1988Sjohnlev 	 */
239843e1988Sjohnlev 	send.port = xen_info->console.domU.evtchn;
240843e1988Sjohnlev 	(void) HYPERVISOR_event_channel_op(EVTCHNOP_send, &send);
241843e1988Sjohnlev 	return (c);
242843e1988Sjohnlev }
243