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