xref: /illumos-gate/usr/src/uts/i86pc/boot/boot_vga.c (revision cbc8e155)
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  */
227c478bd9Sstevel@tonic-gate /*
23ae115bc7Smrj  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * Miniature VGA driver for bootstrap.
297c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate #include <sys/archsystm.h>
327c478bd9Sstevel@tonic-gate #include <sys/vgareg.h>
338e6d016fSToomas Soome #include <sys/framebuffer.h>
3429a77b73SToomas Soome #include <sys/boot_console.h>
35*cbc8e155SToomas Soome #include <sys/rgb.h>
3629a77b73SToomas Soome #include "boot_console_impl.h"
388e6d016fSToomas Soome #include "boot_console_impl.h"
39ae115bc7Smrj #if defined(_BOOT)
40843e1988Sjohnlev #include "../dboot/dboot_asm.h"
41ae115bc7Smrj #include "../dboot/dboot_xboot.h"
42ae115bc7Smrj #endif
44843e1988Sjohnlev #if defined(__xpv) && defined(_BOOT)
46843e1988Sjohnlev /*
47843e1988Sjohnlev  * Device memory address
48843e1988Sjohnlev  *
49843e1988Sjohnlev  * In dboot under the hypervisor we don't have any memory mappings
50843e1988Sjohnlev  * for the first meg of low memory so we can't access devices there.
51843e1988Sjohnlev  * Intead we've mapped the device memory that we need to access into
52843e1988Sjohnlev  * a local variable within dboot so we can access the device memory
53843e1988Sjohnlev  * there.
54843e1988Sjohnlev  */
55843e1988Sjohnlev extern unsigned short *video_fb;
5629a77b73SToomas Soome #define	VGA_SCREEN		(video_fb)
58843e1988Sjohnlev #else /* __xpv && _BOOT */
60843e1988Sjohnlev /* Device memory address */
618e6d016fSToomas Soome #define	VGA_SCREEN	((uint16_t *)(VGA_MEM_ADDR + VGA_COLOR_BASE))
63843e1988Sjohnlev #endif /* __xpv && _BOOT */
6529a77b73SToomas Soome static int cons_color = CONS_COLOR;
678e6d016fSToomas Soome static void vga_init(void);
6829a77b73SToomas Soome static void vga_drawc(int);
6929a77b73SToomas Soome static void vga_setpos(int, int);
7029a77b73SToomas Soome static void vga_getpos(int *, int *);
7129a77b73SToomas Soome static void vga_scroll(int);
728e6d016fSToomas Soome static void vga_clear(int);
7329a77b73SToomas Soome static void vga_shiftline(int);
7429a77b73SToomas Soome static void vga_eraseline(void);
7529a77b73SToomas Soome static void vga_cursor_display(boolean_t);
7629a77b73SToomas Soome 
777c478bd9Sstevel@tonic-gate static void vga_set_crtc(int index, unsigned char val);
787c478bd9Sstevel@tonic-gate static unsigned char vga_get_crtc(int index);
798e6d016fSToomas Soome static void vga_set_atr(int index, unsigned char val);
808e6d016fSToomas Soome static unsigned char vga_get_atr(int index);
8229a77b73SToomas Soome static int
get_vga_color(void)8329a77b73SToomas Soome get_vga_color(void)
8429a77b73SToomas Soome {
8529a77b73SToomas Soome 	int color;
8629a77b73SToomas Soome 	uint32_t fg, bg;
8729a77b73SToomas Soome 
8829a77b73SToomas Soome 	boot_get_color(&fg, &bg);
8929a77b73SToomas Soome 	color = solaris_color_to_pc_color[bg] << 4;
9029a77b73SToomas Soome 	color |= solaris_color_to_pc_color[fg];
9129a77b73SToomas Soome 	return (color);
9229a77b73SToomas Soome }
9329a77b73SToomas Soome 
94843e1988Sjohnlev void
boot_vga_init(bcons_dev_t * bcons_dev)9529a77b73SToomas Soome boot_vga_init(bcons_dev_t *bcons_dev)
968e6d016fSToomas Soome {
978e6d016fSToomas Soome 	fb_info.terminal.x = VGA_TEXT_COLS;
988e6d016fSToomas Soome 	fb_info.terminal.y = VGA_TEXT_ROWS;
9929a77b73SToomas Soome 	cons_color = get_vga_color();
1008e6d016fSToomas Soome 
1018e6d016fSToomas Soome #if defined(_BOOT)
1028e6d016fSToomas Soome 	/*
1038e6d016fSToomas Soome 	 * Note that we have to enable the cursor before clearing the
1048e6d016fSToomas Soome 	 * screen since the cursor position is dependant upon the cursor
1058e6d016fSToomas Soome 	 * skew, which is initialized by vga_cursor_display()
1068e6d016fSToomas Soome 	 */
1078e6d016fSToomas Soome 	vga_init();
1088e6d016fSToomas Soome 	fb_info.cursor.visible = B_FALSE;
10929a77b73SToomas Soome 	vga_cursor_display(B_TRUE);
1108e6d016fSToomas Soome 
1118e6d016fSToomas Soome 	/*
1128e6d016fSToomas Soome 	 * In general we should avoid resetting the display during the boot,
1138e6d016fSToomas Soome 	 * we may have valueable messages there, this why the "native" loader
1148e6d016fSToomas Soome 	 * boot does pass the console state down to kernel and we do try to
1158e6d016fSToomas Soome 	 * pick the state. However, the loader is not the only way to boot.
1168e6d016fSToomas Soome 	 * The non-native boot loaders do not implement the smooth console.
1178e6d016fSToomas Soome 	 * If we have no information about cursor location, we will get value
1188e6d016fSToomas Soome 	 * (0, 0) and that means we better clear the screen.
1198e6d016fSToomas Soome 	 */
1208e6d016fSToomas Soome 	if (fb_info.cursor.pos.x == 0 && fb_info.cursor.pos.y == 0)
1218e6d016fSToomas Soome 		vga_clear(cons_color);
1228e6d016fSToomas Soome 	vga_setpos(fb_info.cursor.pos.y, fb_info.cursor.pos.x);
1238e6d016fSToomas Soome #endif /* _BOOT */
12429a77b73SToomas Soome 
12529a77b73SToomas Soome 	bcons_dev->bd_putchar = vga_drawc;
12629a77b73SToomas Soome 	bcons_dev->bd_eraseline = vga_eraseline;
12729a77b73SToomas Soome 	bcons_dev->bd_cursor = vga_cursor_display;
12829a77b73SToomas Soome 	bcons_dev->bd_setpos = vga_setpos;
12929a77b73SToomas Soome 	bcons_dev->bd_shift = vga_shiftline;
1308e6d016fSToomas Soome }
1318e6d016fSToomas Soome 
1328e6d016fSToomas Soome static void
vga_init(void)1338e6d016fSToomas Soome vga_init(void)
1348e6d016fSToomas Soome {
1358e6d016fSToomas Soome 	unsigned char val;
1368e6d016fSToomas Soome 
1378e6d016fSToomas Soome 	/* set 16bit colors */
1388e6d016fSToomas Soome 	val = vga_get_atr(VGA_ATR_MODE);
1398e6d016fSToomas Soome 	val &= ~VGA_ATR_MODE_BLINK;
1408e6d016fSToomas Soome 	val &= ~VGA_ATR_MODE_9WIDE;
1418e6d016fSToomas Soome 	vga_set_atr(VGA_ATR_MODE, val);
1428e6d016fSToomas Soome }
1438e6d016fSToomas Soome 
1448e6d016fSToomas Soome static void
vga_cursor_display(boolean_t visible)14529a77b73SToomas Soome vga_cursor_display(boolean_t visible)
146843e1988Sjohnlev {
147843e1988Sjohnlev 	unsigned char val, msl;
14929a77b73SToomas Soome 	if (fb_info.cursor.visible == visible)
15029a77b73SToomas Soome 		return;
15129a77b73SToomas Soome 
152843e1988Sjohnlev 	/*
153843e1988Sjohnlev 	 * Figure out the maximum scan line value.  We need this to set the
154843e1988Sjohnlev 	 * cursor size.
155843e1988Sjohnlev 	 */
156843e1988Sjohnlev 	msl = vga_get_crtc(VGA_CRTC_MAX_S_LN) & 0x1f;
158843e1988Sjohnlev 	/*
159843e1988Sjohnlev 	 * Enable the cursor and set it's size.  Preserve the upper two
160843e1988Sjohnlev 	 * bits of the control register.
161843e1988Sjohnlev 	 * - Bits 0-4 are the starting scan line of the cursor.
162843e1988Sjohnlev 	 *   Scanning is done from top-to-bottom.  The top-most scan
163843e1988Sjohnlev 	 *   line is 0 and the bottom most scan line is the maximum scan
164843e1988Sjohnlev 	 *   line value.
165843e1988Sjohnlev 	 * - Bit 5 is the cursor disable bit.
166843e1988Sjohnlev 	 */
1678e6d016fSToomas Soome 	val = vga_get_crtc(VGA_CRTC_CSSL) & 0xc0;
16829a77b73SToomas Soome 
16929a77b73SToomas Soome 	if (visible == B_FALSE)
17029a77b73SToomas Soome 		val |= (1 << 5);
17129a77b73SToomas Soome 
1728e6d016fSToomas Soome 	vga_set_crtc(VGA_CRTC_CSSL, val);
174843e1988Sjohnlev 	/*
175843e1988Sjohnlev 	 * Continue setting the cursors size.
176843e1988Sjohnlev 	 * - Bits 0-4 are the ending scan line of the cursor.
177843e1988Sjohnlev 	 *   Scanning is done from top-to-bottom.  The top-most scan
178843e1988Sjohnlev 	 *   line is 0 and the bottom most scan line is the maximum scan
179843e1988Sjohnlev 	 *   line value.
180843e1988Sjohnlev 	 * - Bits 5-6 are the cursor skew.
181843e1988Sjohnlev 	 */
182843e1988Sjohnlev 	vga_set_crtc(VGA_CRTC_CESL, msl);
183843e1988Sjohnlev }
1858e6d016fSToomas Soome static void
vga_eraseline_impl(int x,int y,int color)18629a77b73SToomas Soome vga_eraseline_impl(int x, int y, int color)
1877c478bd9Sstevel@tonic-gate {
18829a77b73SToomas Soome 	unsigned short val, *buf;
1897c478bd9Sstevel@tonic-gate 	int i;
19129a77b73SToomas Soome 	buf = VGA_SCREEN + x + y * VGA_TEXT_COLS;
1927c478bd9Sstevel@tonic-gate 	val = (color << 8) | ' ';
19329a77b73SToomas Soome 	for (i = x; i < VGA_TEXT_COLS; i++)
19429a77b73SToomas Soome 		buf[i] = val;
19529a77b73SToomas Soome }
19629a77b73SToomas Soome 
19729a77b73SToomas Soome static void
vga_eraseline(void)19829a77b73SToomas Soome vga_eraseline(void)
19929a77b73SToomas Soome {
20029a77b73SToomas Soome 	int x, y;
20129a77b73SToomas Soome 
20229a77b73SToomas Soome 	x = fb_info.cursor.pos.x;
20329a77b73SToomas Soome 	y = fb_info.cursor.pos.y;
20429a77b73SToomas Soome 	vga_eraseline_impl(x, y, cons_color);
20529a77b73SToomas Soome }
20729a77b73SToomas Soome static void
vga_shiftline(int chars)20829a77b73SToomas Soome vga_shiftline(int chars)
20929a77b73SToomas Soome {
21029a77b73SToomas Soome 	unsigned short *src, *dst;
21129a77b73SToomas Soome 	int x, y, len;
21229a77b73SToomas Soome 
21329a77b73SToomas Soome 	x = fb_info.cursor.pos.x;
21429a77b73SToomas Soome 	y = fb_info.cursor.pos.y;
21529a77b73SToomas Soome 	len = VGA_TEXT_COLS - x - chars;
21629a77b73SToomas Soome 	if (len <= 0)
21729a77b73SToomas Soome 		return;
21829a77b73SToomas Soome 
21929a77b73SToomas Soome 	src = VGA_SCREEN + x + y * VGA_TEXT_COLS;
22029a77b73SToomas Soome 	dst = src + chars;
22129a77b73SToomas Soome 	if (dst <= src) {
22229a77b73SToomas Soome 		do {
22329a77b73SToomas Soome 			*dst++ = *src++;
22429a77b73SToomas Soome 		} while (--len != 0);
22529a77b73SToomas Soome 	} else {
22629a77b73SToomas Soome 		dst += len;
22729a77b73SToomas Soome 		src += len;
22829a77b73SToomas Soome 		do {
22929a77b73SToomas Soome 			*--dst = *--src;
23029a77b73SToomas Soome 		} while (--len != 0);
2317c478bd9Sstevel@tonic-gate 	}
2327c478bd9Sstevel@tonic-gate }
23429a77b73SToomas Soome static void
vga_clear(int color)23529a77b73SToomas Soome vga_clear(int color)
23629a77b73SToomas Soome {
23729a77b73SToomas Soome 	int i;
23829a77b73SToomas Soome 
23929a77b73SToomas Soome 	for (i = 0; i < VGA_TEXT_ROWS; i++)
24029a77b73SToomas Soome 		vga_eraseline_impl(0, i, color);
24129a77b73SToomas Soome }
24229a77b73SToomas Soome 
24329a77b73SToomas Soome static void
vga_drawc(int c)24429a77b73SToomas Soome vga_drawc(int c)
2457c478bd9Sstevel@tonic-gate {
2467c478bd9Sstevel@tonic-gate 	int row;
2477c478bd9Sstevel@tonic-gate 	int col;
2497c478bd9Sstevel@tonic-gate 	vga_getpos(&row, &col);
25029a77b73SToomas Soome 
25129a77b73SToomas Soome 	if (c == '\n') {
25229a77b73SToomas Soome 		if (row < fb_info.terminal.y - 1)
25329a77b73SToomas Soome 			vga_setpos(row + 1, col);
25429a77b73SToomas Soome 		else
25529a77b73SToomas Soome 			vga_scroll(cons_color);
25629a77b73SToomas Soome 		return;
25729a77b73SToomas Soome 	}
25829a77b73SToomas Soome 
25929a77b73SToomas Soome 	/*
26029a77b73SToomas Soome 	 * VGA_SCREEN is an array of 16-bit unsigned ints, we do let
26129a77b73SToomas Soome 	 * the compiler to take care of truncation here.
26229a77b73SToomas Soome 	 */
26329a77b73SToomas Soome 	VGA_SCREEN[row * VGA_TEXT_COLS + col] = (cons_color << 8) | c;
26429a77b73SToomas Soome 
26529a77b73SToomas Soome 	if (col < VGA_TEXT_COLS - 1)
26629a77b73SToomas Soome 		vga_setpos(row, col + 1);
26729a77b73SToomas Soome 	else if (row < VGA_TEXT_ROWS - 1)
26829a77b73SToomas Soome 		vga_setpos(row + 1, 0);
26929a77b73SToomas Soome 	else {
27029a77b73SToomas Soome 		vga_setpos(row, 0);
27129a77b73SToomas Soome 		vga_scroll(cons_color);
27229a77b73SToomas Soome 	}
2737c478bd9Sstevel@tonic-gate }
27529a77b73SToomas Soome static void
vga_scroll(int color)2767c478bd9Sstevel@tonic-gate vga_scroll(int color)
2777c478bd9Sstevel@tonic-gate {
2787c478bd9Sstevel@tonic-gate 	int i;
28029a77b73SToomas Soome 	for (i = 0; i < (VGA_TEXT_ROWS - 1) * VGA_TEXT_COLS; i++) {
2817c478bd9Sstevel@tonic-gate 		VGA_SCREEN[i] = VGA_SCREEN[i + VGA_TEXT_COLS];
2827c478bd9Sstevel@tonic-gate 	}
28329a77b73SToomas Soome 	vga_eraseline_impl(0, VGA_TEXT_ROWS - 1, color);
2847c478bd9Sstevel@tonic-gate }
28629a77b73SToomas Soome static void
vga_setpos(int row,int col)2877c478bd9Sstevel@tonic-gate vga_setpos(int row, int col)
2887c478bd9Sstevel@tonic-gate {
2897c478bd9Sstevel@tonic-gate 	int off;
29129a77b73SToomas Soome 	if (row < 0)
29229a77b73SToomas Soome 		row = 0;
29329a77b73SToomas Soome 	if (row >= fb_info.terminal.y)
29429a77b73SToomas Soome 		row = fb_info.terminal.y - 1;
29529a77b73SToomas Soome 	if (col < 0)
29629a77b73SToomas Soome 		col = 0;
29729a77b73SToomas Soome 	if (col >= fb_info.terminal.x)
29829a77b73SToomas Soome 		col = fb_info.terminal.x - 1;
29929a77b73SToomas Soome 
3007c478bd9Sstevel@tonic-gate 	off = row * VGA_TEXT_COLS + col;
3017c478bd9Sstevel@tonic-gate 	vga_set_crtc(VGA_CRTC_CLAH, off >> 8);
3027c478bd9Sstevel@tonic-gate 	vga_set_crtc(VGA_CRTC_CLAL, off & 0xff);
3038e6d016fSToomas Soome 
3048e6d016fSToomas Soome 	fb_info.cursor.pos.y = row;
3058e6d016fSToomas Soome 	fb_info.cursor.pos.x = col;
3067c478bd9Sstevel@tonic-gate }
30829a77b73SToomas Soome static void
vga_getpos(int * row,int * col)3097c478bd9Sstevel@tonic-gate vga_getpos(int *row, int *col)
3107c478bd9Sstevel@tonic-gate {
3117c478bd9Sstevel@tonic-gate 	int off;
313843e1988Sjohnlev 	off = (vga_get_crtc(VGA_CRTC_CLAH) << 8) + vga_get_crtc(VGA_CRTC_CLAL);
3147c478bd9Sstevel@tonic-gate 	*row = off / VGA_TEXT_COLS;
3157c478bd9Sstevel@tonic-gate 	*col = off % VGA_TEXT_COLS;
3167c478bd9Sstevel@tonic-gate }
3188e6d016fSToomas Soome static void
vga_set_atr(int index,unsigned char val)3198e6d016fSToomas Soome vga_set_atr(int index, unsigned char val)
3208e6d016fSToomas Soome {
3218e6d016fSToomas Soome 	(void) inb(VGA_REG_ADDR + CGA_STAT);
3228e6d016fSToomas Soome 	outb(VGA_REG_ADDR + VGA_ATR_AD, index);
3238e6d016fSToomas Soome 	outb(VGA_REG_ADDR + VGA_ATR_AD, val);
3248e6d016fSToomas Soome 
3258e6d016fSToomas Soome 	(void) inb(VGA_REG_ADDR + CGA_STAT);
3268e6d016fSToomas Soome 	outb(VGA_REG_ADDR + VGA_ATR_AD, VGA_ATR_ENB_PLT);
3278e6d016fSToomas Soome }
3288e6d016fSToomas Soome 
3298e6d016fSToomas Soome static unsigned char
vga_get_atr(int index)3308e6d016fSToomas Soome vga_get_atr(int index)
3318e6d016fSToomas Soome {
3328e6d016fSToomas Soome 	unsigned char val;
3338e6d016fSToomas Soome 
3348e6d016fSToomas Soome 	(void) inb(VGA_REG_ADDR + CGA_STAT);
3358e6d016fSToomas Soome 	outb(VGA_REG_ADDR + VGA_ATR_AD, index);
3368e6d016fSToomas Soome 	val = inb(VGA_REG_ADDR + VGA_ATR_DATA);
3378e6d016fSToomas Soome 
3388e6d016fSToomas Soome 	(void) inb(VGA_REG_ADDR + CGA_STAT);
3398e6d016fSToomas Soome 	outb(VGA_REG_ADDR + VGA_ATR_AD, VGA_ATR_ENB_PLT);
3408e6d016fSToomas Soome 
3418e6d016fSToomas Soome 	return (val);
3428e6d016fSToomas Soome }
3438e6d016fSToomas Soome 
3447c478bd9Sstevel@tonic-gate static void
vga_set_crtc(int index,unsigned char val)3457c478bd9Sstevel@tonic-gate vga_set_crtc(int index, unsigned char val)
3467c478bd9Sstevel@tonic-gate {
3478e6d016fSToomas Soome 	outb(VGA_REG_ADDR + VGA_CRTC_ADR, index);
3488e6d016fSToomas Soome 	outb(VGA_REG_ADDR + VGA_CRTC_DATA, val);
3497c478bd9Sstevel@tonic-gate }
3517c478bd9Sstevel@tonic-gate static unsigned char
vga_get_crtc(int index)3527c478bd9Sstevel@tonic-gate vga_get_crtc(int index)
3537c478bd9Sstevel@tonic-gate {
3548e6d016fSToomas Soome 	outb(VGA_REG_ADDR + VGA_CRTC_ADR, index);
3558e6d016fSToomas Soome 	return (inb(VGA_REG_ADDR + VGA_CRTC_DATA));
3567c478bd9Sstevel@tonic-gate }