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