xref: /illumos-gate/usr/src/uts/i86pc/boot/boot_vga.c (revision 8a2b682e57a046b828f37bcde1776f131ef4629f)
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 /*
28  * Miniature VGA driver for bootstrap.
29  */
30 
31 #include <sys/archsystm.h>
32 #include <sys/vgareg.h>
33 #include <sys/framebuffer.h>
34 #include <sys/boot_console.h>
35 #include <sys/rgb.h>
36 #include "boot_console_impl.h"
37 
38 #include "boot_console_impl.h"
39 #if defined(_BOOT)
40 #include "../dboot/dboot_asm.h"
41 #include "../dboot/dboot_xboot.h"
42 #endif
43 
44 #if defined(__xpv) && defined(_BOOT)
45 
46 /*
47  * Device memory address
48  *
49  * In dboot under the hypervisor we don't have any memory mappings
50  * for the first meg of low memory so we can't access devices there.
51  * Intead we've mapped the device memory that we need to access into
52  * a local variable within dboot so we can access the device memory
53  * there.
54  */
55 extern unsigned short *video_fb;
56 #define	VGA_SCREEN		(video_fb)
57 
58 #else /* __xpv && _BOOT */
59 
60 /* Device memory address */
61 #define	VGA_SCREEN	((uint16_t *)(VGA_MEM_ADDR + VGA_COLOR_BASE))
62 
63 #endif /* __xpv && _BOOT */
64 
65 static int cons_color = CONS_COLOR;
66 
67 static void vga_init(void);
68 static void vga_drawc(int);
69 static void vga_setpos(int, int);
70 static void vga_getpos(int *, int *);
71 static void vga_scroll(int);
72 static void vga_clear(int);
73 static void vga_shiftline(int);
74 static void vga_eraseline(void);
75 static void vga_cursor_display(boolean_t);
76 
77 static void vga_set_crtc(int index, unsigned char val);
78 static unsigned char vga_get_crtc(int index);
79 static void vga_set_atr(int index, unsigned char val);
80 static unsigned char vga_get_atr(int index);
81 
82 static int
83 get_vga_color(void)
84 {
85 	int color;
86 	uint32_t fg, bg;
87 
88 	boot_get_color(&fg, &bg);
89 	color = solaris_color_to_pc_color[bg] << 4;
90 	color |= solaris_color_to_pc_color[fg];
91 	return (color);
92 }
93 
94 void
95 boot_vga_init(bcons_dev_t *bcons_dev)
96 {
97 	fb_info.terminal.x = VGA_TEXT_COLS;
98 	fb_info.terminal.y = VGA_TEXT_ROWS;
99 	cons_color = get_vga_color();
100 
101 #if defined(_BOOT)
102 	/*
103 	 * Note that we have to enable the cursor before clearing the
104 	 * screen since the cursor position is dependant upon the cursor
105 	 * skew, which is initialized by vga_cursor_display()
106 	 */
107 	vga_init();
108 	fb_info.cursor.visible = B_FALSE;
109 	vga_cursor_display(B_TRUE);
110 
111 	/*
112 	 * In general we should avoid resetting the display during the boot,
113 	 * we may have valueable messages there, this why the "native" loader
114 	 * boot does pass the console state down to kernel and we do try to
115 	 * pick the state. However, the loader is not the only way to boot.
116 	 * The non-native boot loaders do not implement the smooth console.
117 	 * If we have no information about cursor location, we will get value
118 	 * (0, 0) and that means we better clear the screen.
119 	 */
120 	if (fb_info.cursor.pos.x == 0 && fb_info.cursor.pos.y == 0)
121 		vga_clear(cons_color);
122 	vga_setpos(fb_info.cursor.pos.y, fb_info.cursor.pos.x);
123 #endif /* _BOOT */
124 
125 	bcons_dev->bd_putchar = vga_drawc;
126 	bcons_dev->bd_eraseline = vga_eraseline;
127 	bcons_dev->bd_cursor = vga_cursor_display;
128 	bcons_dev->bd_setpos = vga_setpos;
129 	bcons_dev->bd_shift = vga_shiftline;
130 }
131 
132 static void
133 vga_init(void)
134 {
135 	unsigned char val;
136 
137 	/* set 16bit colors */
138 	val = vga_get_atr(VGA_ATR_MODE);
139 	val &= ~VGA_ATR_MODE_BLINK;
140 	val &= ~VGA_ATR_MODE_9WIDE;
141 	vga_set_atr(VGA_ATR_MODE, val);
142 }
143 
144 static void
145 vga_cursor_display(boolean_t visible)
146 {
147 	unsigned char val, msl;
148 
149 	if (fb_info.cursor.visible == visible)
150 		return;
151 
152 	/*
153 	 * Figure out the maximum scan line value.  We need this to set the
154 	 * cursor size.
155 	 */
156 	msl = vga_get_crtc(VGA_CRTC_MAX_S_LN) & 0x1f;
157 
158 	/*
159 	 * Enable the cursor and set it's size.  Preserve the upper two
160 	 * bits of the control register.
161 	 * - Bits 0-4 are the starting scan line of the cursor.
162 	 *   Scanning is done from top-to-bottom.  The top-most scan
163 	 *   line is 0 and the bottom most scan line is the maximum scan
164 	 *   line value.
165 	 * - Bit 5 is the cursor disable bit.
166 	 */
167 	val = vga_get_crtc(VGA_CRTC_CSSL) & 0xc0;
168 
169 	if (visible == B_FALSE)
170 		val |= (1 << 5);
171 
172 	vga_set_crtc(VGA_CRTC_CSSL, val);
173 
174 	/*
175 	 * Continue setting the cursors size.
176 	 * - Bits 0-4 are the ending scan line of the cursor.
177 	 *   Scanning is done from top-to-bottom.  The top-most scan
178 	 *   line is 0 and the bottom most scan line is the maximum scan
179 	 *   line value.
180 	 * - Bits 5-6 are the cursor skew.
181 	 */
182 	vga_set_crtc(VGA_CRTC_CESL, msl);
183 }
184 
185 static void
186 vga_eraseline_impl(int x, int y, int color)
187 {
188 	unsigned short val, *buf;
189 	int i;
190 
191 	buf = VGA_SCREEN + x + y * VGA_TEXT_COLS;
192 	val = (color << 8) | ' ';
193 	for (i = x; i < VGA_TEXT_COLS; i++)
194 		buf[i] = val;
195 }
196 
197 static void
198 vga_eraseline(void)
199 {
200 	int x, y;
201 
202 	x = fb_info.cursor.pos.x;
203 	y = fb_info.cursor.pos.y;
204 	vga_eraseline_impl(x, y, cons_color);
205 }
206 
207 static void
208 vga_shiftline(int chars)
209 {
210 	unsigned short *src, *dst;
211 	int x, y, len;
212 
213 	x = fb_info.cursor.pos.x;
214 	y = fb_info.cursor.pos.y;
215 	len = VGA_TEXT_COLS - x - chars;
216 	if (len <= 0)
217 		return;
218 
219 	src = VGA_SCREEN + x + y * VGA_TEXT_COLS;
220 	dst = src + chars;
221 	if (dst <= src) {
222 		do {
223 			*dst++ = *src++;
224 		} while (--len != 0);
225 	} else {
226 		dst += len;
227 		src += len;
228 		do {
229 			*--dst = *--src;
230 		} while (--len != 0);
231 	}
232 }
233 
234 static void
235 vga_clear(int color)
236 {
237 	int i;
238 
239 	for (i = 0; i < VGA_TEXT_ROWS; i++)
240 		vga_eraseline_impl(0, i, color);
241 }
242 
243 static void
244 vga_drawc(int c)
245 {
246 	int row;
247 	int col;
248 
249 	vga_getpos(&row, &col);
250 
251 	if (c == '\n') {
252 		if (row < fb_info.terminal.y - 1)
253 			vga_setpos(row + 1, col);
254 		else
255 			vga_scroll(cons_color);
256 		return;
257 	}
258 
259 	/*
260 	 * VGA_SCREEN is an array of 16-bit unsigned ints, we do let
261 	 * the compiler to take care of truncation here.
262 	 */
263 	VGA_SCREEN[row * VGA_TEXT_COLS + col] = (cons_color << 8) | c;
264 
265 	if (col < VGA_TEXT_COLS - 1)
266 		vga_setpos(row, col + 1);
267 	else if (row < VGA_TEXT_ROWS - 1)
268 		vga_setpos(row + 1, 0);
269 	else {
270 		vga_setpos(row, 0);
271 		vga_scroll(cons_color);
272 	}
273 }
274 
275 static void
276 vga_scroll(int color)
277 {
278 	int i;
279 
280 	for (i = 0; i < (VGA_TEXT_ROWS - 1) * VGA_TEXT_COLS; i++) {
281 		VGA_SCREEN[i] = VGA_SCREEN[i + VGA_TEXT_COLS];
282 	}
283 	vga_eraseline_impl(0, VGA_TEXT_ROWS - 1, color);
284 }
285 
286 static void
287 vga_setpos(int row, int col)
288 {
289 	int off;
290 
291 	if (row < 0)
292 		row = 0;
293 	if (row >= fb_info.terminal.y)
294 		row = fb_info.terminal.y - 1;
295 	if (col < 0)
296 		col = 0;
297 	if (col >= fb_info.terminal.x)
298 		col = fb_info.terminal.x - 1;
299 
300 	off = row * VGA_TEXT_COLS + col;
301 	vga_set_crtc(VGA_CRTC_CLAH, off >> 8);
302 	vga_set_crtc(VGA_CRTC_CLAL, off & 0xff);
303 
304 	fb_info.cursor.pos.y = row;
305 	fb_info.cursor.pos.x = col;
306 }
307 
308 static void
309 vga_getpos(int *row, int *col)
310 {
311 	int off;
312 
313 	off = (vga_get_crtc(VGA_CRTC_CLAH) << 8) + vga_get_crtc(VGA_CRTC_CLAL);
314 	*row = off / VGA_TEXT_COLS;
315 	*col = off % VGA_TEXT_COLS;
316 }
317 
318 static void
319 vga_set_atr(int index, unsigned char val)
320 {
321 	(void) inb(VGA_REG_ADDR + CGA_STAT);
322 	outb(VGA_REG_ADDR + VGA_ATR_AD, index);
323 	outb(VGA_REG_ADDR + VGA_ATR_AD, val);
324 
325 	(void) inb(VGA_REG_ADDR + CGA_STAT);
326 	outb(VGA_REG_ADDR + VGA_ATR_AD, VGA_ATR_ENB_PLT);
327 }
328 
329 static unsigned char
330 vga_get_atr(int index)
331 {
332 	unsigned char val;
333 
334 	(void) inb(VGA_REG_ADDR + CGA_STAT);
335 	outb(VGA_REG_ADDR + VGA_ATR_AD, index);
336 	val = inb(VGA_REG_ADDR + VGA_ATR_DATA);
337 
338 	(void) inb(VGA_REG_ADDR + CGA_STAT);
339 	outb(VGA_REG_ADDR + VGA_ATR_AD, VGA_ATR_ENB_PLT);
340 
341 	return (val);
342 }
343 
344 static void
345 vga_set_crtc(int index, unsigned char val)
346 {
347 	outb(VGA_REG_ADDR + VGA_CRTC_ADR, index);
348 	outb(VGA_REG_ADDR + VGA_CRTC_DATA, val);
349 }
350 
351 static unsigned char
352 vga_get_crtc(int index)
353 {
354 	outb(VGA_REG_ADDR + VGA_CRTC_ADR, index);
355 	return (inb(VGA_REG_ADDR + VGA_CRTC_DATA));
356 }
357