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