xref: /illumos-gate/usr/src/uts/i86xpv/boot/boot_xconsole.c (revision 533affcbc7fc4d0c8132976ea454aaa715fe2307)
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 }
243