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