xref: /illumos-gate/usr/src/uts/i86pc/boot/boot_vga.c (revision 29a77b73)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5ae115bc7Smrj  * Common Development and Distribution License (the "License").
6ae115bc7Smrj  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21ae115bc7Smrj 
227c478bd9Sstevel@tonic-gate /*
23ae115bc7Smrj  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * Miniature VGA driver for bootstrap.
297c478bd9Sstevel@tonic-gate  */
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate #include <sys/archsystm.h>
327c478bd9Sstevel@tonic-gate #include <sys/vgareg.h>
338e6d016fSToomas Soome #include <sys/framebuffer.h>
34*29a77b73SToomas Soome #include <sys/boot_console.h>
35*29a77b73SToomas Soome #include "boot_console_impl.h"
36ae115bc7Smrj 
378e6d016fSToomas Soome #include "boot_console_impl.h"
38ae115bc7Smrj #if defined(_BOOT)
39843e1988Sjohnlev #include "../dboot/dboot_asm.h"
40ae115bc7Smrj #include "../dboot/dboot_xboot.h"
41ae115bc7Smrj #endif
427c478bd9Sstevel@tonic-gate 
43843e1988Sjohnlev #if defined(__xpv) && defined(_BOOT)
44843e1988Sjohnlev 
45843e1988Sjohnlev /*
46843e1988Sjohnlev  * Device memory address
47843e1988Sjohnlev  *
48843e1988Sjohnlev  * In dboot under the hypervisor we don't have any memory mappings
49843e1988Sjohnlev  * for the first meg of low memory so we can't access devices there.
50843e1988Sjohnlev  * Intead we've mapped the device memory that we need to access into
51843e1988Sjohnlev  * a local variable within dboot so we can access the device memory
52843e1988Sjohnlev  * there.
53843e1988Sjohnlev  */
54843e1988Sjohnlev extern unsigned short *video_fb;
55*29a77b73SToomas Soome #define	VGA_SCREEN		(video_fb)
56843e1988Sjohnlev 
57843e1988Sjohnlev #else /* __xpv && _BOOT */
58843e1988Sjohnlev 
59843e1988Sjohnlev /* Device memory address */
608e6d016fSToomas Soome #define	VGA_SCREEN	((uint16_t *)(VGA_MEM_ADDR + VGA_COLOR_BASE))
617c478bd9Sstevel@tonic-gate 
62843e1988Sjohnlev #endif /* __xpv && _BOOT */
63843e1988Sjohnlev 
64*29a77b73SToomas Soome static int cons_color = CONS_COLOR;
65843e1988Sjohnlev 
668e6d016fSToomas Soome static void vga_init(void);
67*29a77b73SToomas Soome static void vga_drawc(int);
68*29a77b73SToomas Soome static void vga_setpos(int, int);
69*29a77b73SToomas Soome static void vga_getpos(int *, int *);
70*29a77b73SToomas Soome static void vga_scroll(int);
718e6d016fSToomas Soome static void vga_clear(int);
72*29a77b73SToomas Soome static void vga_shiftline(int);
73*29a77b73SToomas Soome static void vga_eraseline(void);
74*29a77b73SToomas Soome static void vga_cursor_display(boolean_t);
75*29a77b73SToomas Soome 
767c478bd9Sstevel@tonic-gate static void vga_set_crtc(int index, unsigned char val);
777c478bd9Sstevel@tonic-gate static unsigned char vga_get_crtc(int index);
788e6d016fSToomas Soome static void vga_set_atr(int index, unsigned char val);
798e6d016fSToomas Soome static unsigned char vga_get_atr(int index);
807c478bd9Sstevel@tonic-gate 
81*29a77b73SToomas Soome static int
82*29a77b73SToomas Soome get_vga_color(void)
83*29a77b73SToomas Soome {
84*29a77b73SToomas Soome 	int color;
85*29a77b73SToomas Soome 	uint32_t fg, bg;
86*29a77b73SToomas Soome 	uint8_t solaris_color_to_pc_color[16] = {
87*29a77b73SToomas Soome 		15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
88*29a77b73SToomas Soome 	};
89*29a77b73SToomas Soome 
90*29a77b73SToomas Soome 	boot_get_color(&fg, &bg);
91*29a77b73SToomas Soome 	color = solaris_color_to_pc_color[bg] << 4;
92*29a77b73SToomas Soome 	color |= solaris_color_to_pc_color[fg];
93*29a77b73SToomas Soome 	return (color);
94*29a77b73SToomas Soome }
95*29a77b73SToomas Soome 
96843e1988Sjohnlev void
97*29a77b73SToomas Soome boot_vga_init(bcons_dev_t *bcons_dev)
988e6d016fSToomas Soome {
998e6d016fSToomas Soome 	fb_info.terminal.x = VGA_TEXT_COLS;
1008e6d016fSToomas Soome 	fb_info.terminal.y = VGA_TEXT_ROWS;
101*29a77b73SToomas Soome 	cons_color = get_vga_color();
1028e6d016fSToomas Soome 
1038e6d016fSToomas Soome #if defined(_BOOT)
1048e6d016fSToomas Soome 	/*
1058e6d016fSToomas Soome 	 * Note that we have to enable the cursor before clearing the
1068e6d016fSToomas Soome 	 * screen since the cursor position is dependant upon the cursor
1078e6d016fSToomas Soome 	 * skew, which is initialized by vga_cursor_display()
1088e6d016fSToomas Soome 	 */
1098e6d016fSToomas Soome 	vga_init();
1108e6d016fSToomas Soome 	fb_info.cursor.visible = B_FALSE;
111*29a77b73SToomas Soome 	vga_cursor_display(B_TRUE);
1128e6d016fSToomas Soome 
1138e6d016fSToomas Soome 	/*
1148e6d016fSToomas Soome 	 * In general we should avoid resetting the display during the boot,
1158e6d016fSToomas Soome 	 * we may have valueable messages there, this why the "native" loader
1168e6d016fSToomas Soome 	 * boot does pass the console state down to kernel and we do try to
1178e6d016fSToomas Soome 	 * pick the state. However, the loader is not the only way to boot.
1188e6d016fSToomas Soome 	 * The non-native boot loaders do not implement the smooth console.
1198e6d016fSToomas Soome 	 * If we have no information about cursor location, we will get value
1208e6d016fSToomas Soome 	 * (0, 0) and that means we better clear the screen.
1218e6d016fSToomas Soome 	 */
1228e6d016fSToomas Soome 	if (fb_info.cursor.pos.x == 0 && fb_info.cursor.pos.y == 0)
1238e6d016fSToomas Soome 		vga_clear(cons_color);
1248e6d016fSToomas Soome 	vga_setpos(fb_info.cursor.pos.y, fb_info.cursor.pos.x);
1258e6d016fSToomas Soome #endif /* _BOOT */
126*29a77b73SToomas Soome 
127*29a77b73SToomas Soome 	bcons_dev->bd_putchar = vga_drawc;
128*29a77b73SToomas Soome 	bcons_dev->bd_eraseline = vga_eraseline;
129*29a77b73SToomas Soome 	bcons_dev->bd_cursor = vga_cursor_display;
130*29a77b73SToomas Soome 	bcons_dev->bd_setpos = vga_setpos;
131*29a77b73SToomas Soome 	bcons_dev->bd_shift = vga_shiftline;
1328e6d016fSToomas Soome }
1338e6d016fSToomas Soome 
1348e6d016fSToomas Soome static void
1358e6d016fSToomas Soome vga_init(void)
1368e6d016fSToomas Soome {
1378e6d016fSToomas Soome 	unsigned char val;
1388e6d016fSToomas Soome 
1398e6d016fSToomas Soome 	/* set 16bit colors */
1408e6d016fSToomas Soome 	val = vga_get_atr(VGA_ATR_MODE);
1418e6d016fSToomas Soome 	val &= ~VGA_ATR_MODE_BLINK;
1428e6d016fSToomas Soome 	val &= ~VGA_ATR_MODE_9WIDE;
1438e6d016fSToomas Soome 	vga_set_atr(VGA_ATR_MODE, val);
1448e6d016fSToomas Soome }
1458e6d016fSToomas Soome 
1468e6d016fSToomas Soome static void
147*29a77b73SToomas Soome vga_cursor_display(boolean_t visible)
148843e1988Sjohnlev {
149843e1988Sjohnlev 	unsigned char val, msl;
150843e1988Sjohnlev 
151*29a77b73SToomas Soome 	if (fb_info.cursor.visible == visible)
152*29a77b73SToomas Soome 		return;
153*29a77b73SToomas Soome 
154843e1988Sjohnlev 	/*
155843e1988Sjohnlev 	 * Figure out the maximum scan line value.  We need this to set the
156843e1988Sjohnlev 	 * cursor size.
157843e1988Sjohnlev 	 */
158843e1988Sjohnlev 	msl = vga_get_crtc(VGA_CRTC_MAX_S_LN) & 0x1f;
159843e1988Sjohnlev 
160843e1988Sjohnlev 	/*
161843e1988Sjohnlev 	 * Enable the cursor and set it's size.  Preserve the upper two
162843e1988Sjohnlev 	 * bits of the control register.
163843e1988Sjohnlev 	 * - Bits 0-4 are the starting scan line of the cursor.
164843e1988Sjohnlev 	 *   Scanning is done from top-to-bottom.  The top-most scan
165843e1988Sjohnlev 	 *   line is 0 and the bottom most scan line is the maximum scan
166843e1988Sjohnlev 	 *   line value.
167843e1988Sjohnlev 	 * - Bit 5 is the cursor disable bit.
168843e1988Sjohnlev 	 */
1698e6d016fSToomas Soome 	val = vga_get_crtc(VGA_CRTC_CSSL) & 0xc0;
170*29a77b73SToomas Soome 
171*29a77b73SToomas Soome 	if (visible == B_FALSE)
172*29a77b73SToomas Soome 		val |= (1 << 5);
173*29a77b73SToomas Soome 
1748e6d016fSToomas Soome 	vga_set_crtc(VGA_CRTC_CSSL, val);
175843e1988Sjohnlev 
176843e1988Sjohnlev 	/*
177843e1988Sjohnlev 	 * Continue setting the cursors size.
178843e1988Sjohnlev 	 * - Bits 0-4 are the ending scan line of the cursor.
179843e1988Sjohnlev 	 *   Scanning is done from top-to-bottom.  The top-most scan
180843e1988Sjohnlev 	 *   line is 0 and the bottom most scan line is the maximum scan
181843e1988Sjohnlev 	 *   line value.
182843e1988Sjohnlev 	 * - Bits 5-6 are the cursor skew.
183843e1988Sjohnlev 	 */
184843e1988Sjohnlev 	vga_set_crtc(VGA_CRTC_CESL, msl);
185843e1988Sjohnlev }
186843e1988Sjohnlev 
1878e6d016fSToomas Soome static void
188*29a77b73SToomas Soome vga_eraseline_impl(int x, int y, int color)
1897c478bd9Sstevel@tonic-gate {
190*29a77b73SToomas Soome 	unsigned short val, *buf;
1917c478bd9Sstevel@tonic-gate 	int i;
1927c478bd9Sstevel@tonic-gate 
193*29a77b73SToomas Soome 	buf = VGA_SCREEN + x + y * VGA_TEXT_COLS;
1947c478bd9Sstevel@tonic-gate 	val = (color << 8) | ' ';
195*29a77b73SToomas Soome 	for (i = x; i < VGA_TEXT_COLS; i++)
196*29a77b73SToomas Soome 		buf[i] = val;
197*29a77b73SToomas Soome }
198*29a77b73SToomas Soome 
199*29a77b73SToomas Soome static void
200*29a77b73SToomas Soome vga_eraseline(void)
201*29a77b73SToomas Soome {
202*29a77b73SToomas Soome 	int x, y;
203*29a77b73SToomas Soome 
204*29a77b73SToomas Soome 	x = fb_info.cursor.pos.x;
205*29a77b73SToomas Soome 	y = fb_info.cursor.pos.y;
206*29a77b73SToomas Soome 	vga_eraseline_impl(x, y, cons_color);
207*29a77b73SToomas Soome }
2087c478bd9Sstevel@tonic-gate 
209*29a77b73SToomas Soome static void
210*29a77b73SToomas Soome vga_shiftline(int chars)
211*29a77b73SToomas Soome {
212*29a77b73SToomas Soome 	unsigned short *src, *dst;
213*29a77b73SToomas Soome 	int x, y, len;
214*29a77b73SToomas Soome 
215*29a77b73SToomas Soome 	x = fb_info.cursor.pos.x;
216*29a77b73SToomas Soome 	y = fb_info.cursor.pos.y;
217*29a77b73SToomas Soome 	len = VGA_TEXT_COLS - x - chars;
218*29a77b73SToomas Soome 	if (len <= 0)
219*29a77b73SToomas Soome 		return;
220*29a77b73SToomas Soome 
221*29a77b73SToomas Soome 	src = VGA_SCREEN + x + y * VGA_TEXT_COLS;
222*29a77b73SToomas Soome 	dst = src + chars;
223*29a77b73SToomas Soome 	if (dst <= src) {
224*29a77b73SToomas Soome 		do {
225*29a77b73SToomas Soome 			*dst++ = *src++;
226*29a77b73SToomas Soome 		} while (--len != 0);
227*29a77b73SToomas Soome 	} else {
228*29a77b73SToomas Soome 		dst += len;
229*29a77b73SToomas Soome 		src += len;
230*29a77b73SToomas Soome 		do {
231*29a77b73SToomas Soome 			*--dst = *--src;
232*29a77b73SToomas Soome 		} while (--len != 0);
2337c478bd9Sstevel@tonic-gate 	}
2347c478bd9Sstevel@tonic-gate }
2357c478bd9Sstevel@tonic-gate 
236*29a77b73SToomas Soome static void
237*29a77b73SToomas Soome vga_clear(int color)
238*29a77b73SToomas Soome {
239*29a77b73SToomas Soome 	int i;
240*29a77b73SToomas Soome 
241*29a77b73SToomas Soome 	for (i = 0; i < VGA_TEXT_ROWS; i++)
242*29a77b73SToomas Soome 		vga_eraseline_impl(0, i, color);
243*29a77b73SToomas Soome }
244*29a77b73SToomas Soome 
245*29a77b73SToomas Soome static void
246*29a77b73SToomas Soome vga_drawc(int c)
2477c478bd9Sstevel@tonic-gate {
2487c478bd9Sstevel@tonic-gate 	int row;
2497c478bd9Sstevel@tonic-gate 	int col;
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 	vga_getpos(&row, &col);
252*29a77b73SToomas Soome 
253*29a77b73SToomas Soome 	if (c == '\n') {
254*29a77b73SToomas Soome 		if (row < fb_info.terminal.y - 1)
255*29a77b73SToomas Soome 			vga_setpos(row + 1, col);
256*29a77b73SToomas Soome 		else
257*29a77b73SToomas Soome 			vga_scroll(cons_color);
258*29a77b73SToomas Soome 		return;
259*29a77b73SToomas Soome 	}
260*29a77b73SToomas Soome 
261*29a77b73SToomas Soome 	/*
262*29a77b73SToomas Soome 	 * VGA_SCREEN is an array of 16-bit unsigned ints, we do let
263*29a77b73SToomas Soome 	 * the compiler to take care of truncation here.
264*29a77b73SToomas Soome 	 */
265*29a77b73SToomas Soome 	VGA_SCREEN[row * VGA_TEXT_COLS + col] = (cons_color << 8) | c;
266*29a77b73SToomas Soome 
267*29a77b73SToomas Soome 	if (col < VGA_TEXT_COLS - 1)
268*29a77b73SToomas Soome 		vga_setpos(row, col + 1);
269*29a77b73SToomas Soome 	else if (row < VGA_TEXT_ROWS - 1)
270*29a77b73SToomas Soome 		vga_setpos(row + 1, 0);
271*29a77b73SToomas Soome 	else {
272*29a77b73SToomas Soome 		vga_setpos(row, 0);
273*29a77b73SToomas Soome 		vga_scroll(cons_color);
274*29a77b73SToomas Soome 	}
2757c478bd9Sstevel@tonic-gate }
2767c478bd9Sstevel@tonic-gate 
277*29a77b73SToomas Soome static void
2787c478bd9Sstevel@tonic-gate vga_scroll(int color)
2797c478bd9Sstevel@tonic-gate {
2807c478bd9Sstevel@tonic-gate 	int i;
2817c478bd9Sstevel@tonic-gate 
282*29a77b73SToomas Soome 	for (i = 0; i < (VGA_TEXT_ROWS - 1) * VGA_TEXT_COLS; i++) {
2837c478bd9Sstevel@tonic-gate 		VGA_SCREEN[i] = VGA_SCREEN[i + VGA_TEXT_COLS];
2847c478bd9Sstevel@tonic-gate 	}
285*29a77b73SToomas Soome 	vga_eraseline_impl(0, VGA_TEXT_ROWS - 1, color);
2867c478bd9Sstevel@tonic-gate }
2877c478bd9Sstevel@tonic-gate 
288*29a77b73SToomas Soome static void
2897c478bd9Sstevel@tonic-gate vga_setpos(int row, int col)
2907c478bd9Sstevel@tonic-gate {
2917c478bd9Sstevel@tonic-gate 	int off;
2927c478bd9Sstevel@tonic-gate 
293*29a77b73SToomas Soome 	if (row < 0)
294*29a77b73SToomas Soome 		row = 0;
295*29a77b73SToomas Soome 	if (row >= fb_info.terminal.y)
296*29a77b73SToomas Soome 		row = fb_info.terminal.y - 1;
297*29a77b73SToomas Soome 	if (col < 0)
298*29a77b73SToomas Soome 		col = 0;
299*29a77b73SToomas Soome 	if (col >= fb_info.terminal.x)
300*29a77b73SToomas Soome 		col = fb_info.terminal.x - 1;
301*29a77b73SToomas Soome 
3027c478bd9Sstevel@tonic-gate 	off = row * VGA_TEXT_COLS + col;
3037c478bd9Sstevel@tonic-gate 	vga_set_crtc(VGA_CRTC_CLAH, off >> 8);
3047c478bd9Sstevel@tonic-gate 	vga_set_crtc(VGA_CRTC_CLAL, off & 0xff);
3058e6d016fSToomas Soome 
3068e6d016fSToomas Soome 	fb_info.cursor.pos.y = row;
3078e6d016fSToomas Soome 	fb_info.cursor.pos.x = col;
3087c478bd9Sstevel@tonic-gate }
3097c478bd9Sstevel@tonic-gate 
310*29a77b73SToomas Soome static void
3117c478bd9Sstevel@tonic-gate vga_getpos(int *row, int *col)
3127c478bd9Sstevel@tonic-gate {
3137c478bd9Sstevel@tonic-gate 	int off;
3147c478bd9Sstevel@tonic-gate 
315843e1988Sjohnlev 	off = (vga_get_crtc(VGA_CRTC_CLAH) << 8) + vga_get_crtc(VGA_CRTC_CLAL);
3167c478bd9Sstevel@tonic-gate 	*row = off / VGA_TEXT_COLS;
3177c478bd9Sstevel@tonic-gate 	*col = off % VGA_TEXT_COLS;
3187c478bd9Sstevel@tonic-gate }
3197c478bd9Sstevel@tonic-gate 
3208e6d016fSToomas Soome static void
3218e6d016fSToomas Soome vga_set_atr(int index, unsigned char val)
3228e6d016fSToomas Soome {
3238e6d016fSToomas Soome 	(void) inb(VGA_REG_ADDR + CGA_STAT);
3248e6d016fSToomas Soome 	outb(VGA_REG_ADDR + VGA_ATR_AD, index);
3258e6d016fSToomas Soome 	outb(VGA_REG_ADDR + VGA_ATR_AD, val);
3268e6d016fSToomas Soome 
3278e6d016fSToomas Soome 	(void) inb(VGA_REG_ADDR + CGA_STAT);
3288e6d016fSToomas Soome 	outb(VGA_REG_ADDR + VGA_ATR_AD, VGA_ATR_ENB_PLT);
3298e6d016fSToomas Soome }
3308e6d016fSToomas Soome 
3318e6d016fSToomas Soome static unsigned char
3328e6d016fSToomas Soome vga_get_atr(int index)
3338e6d016fSToomas Soome {
3348e6d016fSToomas Soome 	unsigned char val;
3358e6d016fSToomas Soome 
3368e6d016fSToomas Soome 	(void) inb(VGA_REG_ADDR + CGA_STAT);
3378e6d016fSToomas Soome 	outb(VGA_REG_ADDR + VGA_ATR_AD, index);
3388e6d016fSToomas Soome 	val = inb(VGA_REG_ADDR + VGA_ATR_DATA);
3398e6d016fSToomas Soome 
3408e6d016fSToomas Soome 	(void) inb(VGA_REG_ADDR + CGA_STAT);
3418e6d016fSToomas Soome 	outb(VGA_REG_ADDR + VGA_ATR_AD, VGA_ATR_ENB_PLT);
3428e6d016fSToomas Soome 
3438e6d016fSToomas Soome 	return (val);
3448e6d016fSToomas Soome }
3458e6d016fSToomas Soome 
3467c478bd9Sstevel@tonic-gate static void
3477c478bd9Sstevel@tonic-gate vga_set_crtc(int index, unsigned char val)
3487c478bd9Sstevel@tonic-gate {
3498e6d016fSToomas Soome 	outb(VGA_REG_ADDR + VGA_CRTC_ADR, index);
3508e6d016fSToomas Soome 	outb(VGA_REG_ADDR + VGA_CRTC_DATA, val);
3517c478bd9Sstevel@tonic-gate }
3527c478bd9Sstevel@tonic-gate 
3537c478bd9Sstevel@tonic-gate static unsigned char
3547c478bd9Sstevel@tonic-gate vga_get_crtc(int index)
3557c478bd9Sstevel@tonic-gate {
3568e6d016fSToomas Soome 	outb(VGA_REG_ADDR + VGA_CRTC_ADR, index);
3578e6d016fSToomas Soome 	return (inb(VGA_REG_ADDR + VGA_CRTC_DATA));
3587c478bd9Sstevel@tonic-gate }
359