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
get_vga_color(void)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
boot_vga_init(bcons_dev_t * bcons_dev)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
vga_init(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
vga_cursor_display(boolean_t visible)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
vga_eraseline_impl(int x,int y,int color)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
vga_eraseline(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
vga_shiftline(int chars)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
vga_clear(int color)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
vga_drawc(int c)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
vga_scroll(int color)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
vga_setpos(int row,int col)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
vga_getpos(int * row,int * col)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
vga_set_atr(int index,unsigned char val)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
vga_get_atr(int index)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
vga_set_crtc(int index,unsigned char val)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
vga_get_crtc(int index)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