1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2016 Toomas Soome <tsoome@me.com> 14 */ 15 16 /* 17 * dboot and early kernel needs simple putchar(int) interface to implement 18 * printf() support. So we implement simple interface on top of 19 * linear frame buffer, since we can not use tem directly, we are 20 * just borrowing bits from it. 21 * 22 * Note, this implementation is assuming UEFI linear frame buffer and 23 * 32-bit depth, which should not be issue as GOP is supposed to provide those. 24 * At the time of writing, this is the only case for frame buffer anyhow. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/systm.h> 29 #include <sys/multiboot2.h> 30 #include <sys/framebuffer.h> 31 #include <sys/bootinfo.h> 32 #include <sys/boot_console.h> 33 #include <sys/bootconf.h> 34 35 #define P2ROUNDUP(x, align) (-(-(x) & -(align))) 36 #define MIN(a, b) ((a) < (b) ? (a) : (b)) 37 38 /* 39 * Simplified visual_io data structures from visual_io.h 40 */ 41 42 struct vis_consdisplay { 43 uint16_t row; /* Row to display data at */ 44 uint16_t col; /* Col to display data at */ 45 uint16_t width; /* Width of data */ 46 uint16_t height; /* Height of data */ 47 uint8_t *data; /* Data to display */ 48 }; 49 50 struct vis_conscopy { 51 uint16_t s_row; /* Starting row */ 52 uint16_t s_col; /* Starting col */ 53 uint16_t e_row; /* Ending row */ 54 uint16_t e_col; /* Ending col */ 55 uint16_t t_row; /* Row to move to */ 56 uint16_t t_col; /* Col to move to */ 57 }; 58 59 /* we have built in fonts 12x22, 6x10, 7x14 and depth 32. */ 60 #define MAX_GLYPH (12 * 22 * 4) 61 62 static struct font boot_fb_font; /* set by set_font() */ 63 static uint8_t glyph[MAX_GLYPH]; 64 static uint32_t last_line_size; 65 static fb_info_pixel_coord_t last_line; 66 67 /* color translation */ 68 typedef struct { 69 uint8_t red[16]; 70 uint8_t green[16]; 71 uint8_t blue[16]; 72 } text_cmap_t; 73 74 /* BEGIN CSTYLED */ 75 /* Bk Rd Gr Br Bl Mg Cy Wh */ 76 static uint8_t dim_xlate[] = { 1, 5, 3, 7, 2, 6, 4, 8 }; 77 static uint8_t brt_xlate[] = { 9, 13, 11, 15, 10, 14, 12, 0 }; 78 /* END CSTYLED */ 79 80 static text_cmap_t cmap4_to_24 = { 81 /* BEGIN CSTYLED */ 82 /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 83 Wh+ Bk Bl Gr Cy Rd Mg Br Wh Bk+ Bl+ Gr+ Cy+ Rd+ Mg+ Yw */ 84 0xff,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x40,0x00,0x00,0x00,0xff,0xff,0xff, 85 0xff,0x00,0x00,0x80,0x80,0x00,0x00,0x80,0x80,0x40,0x00,0xff,0xff,0x00,0x00,0xff, 86 0xff,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x40,0xff,0x00,0xff,0x00,0xff,0x00 87 /* END CSTYLED */ 88 }; 89 90 /* 91 * extract data from MB2 framebuffer tag and set up initial frame buffer. 92 */ 93 boolean_t 94 xbi_fb_init(struct xboot_info *xbi) 95 { 96 multiboot_tag_framebuffer_t *tag; 97 boot_framebuffer_t *xbi_fb; 98 99 xbi_fb = (boot_framebuffer_t *)(uintptr_t)xbi->bi_framebuffer; 100 if (xbi_fb == NULL) 101 return (B_FALSE); 102 103 #if !defined(_BOOT) 104 /* For early kernel, we get cursor position from dboot. */ 105 fb_info.cursor.origin.x = xbi_fb->cursor.origin.x; 106 fb_info.cursor.origin.y = xbi_fb->cursor.origin.y; 107 fb_info.cursor.pos.x = xbi_fb->cursor.pos.x; 108 fb_info.cursor.pos.y = xbi_fb->cursor.pos.y; 109 fb_info.cursor.visible = xbi_fb->cursor.visible; 110 #endif 111 112 tag = (multiboot_tag_framebuffer_t *)(uintptr_t)xbi_fb->framebuffer; 113 if (tag == NULL) { 114 return (B_FALSE); 115 } 116 117 fb_info.paddr = tag->framebuffer_common.framebuffer_addr; 118 fb_info.pitch = tag->framebuffer_common.framebuffer_pitch; 119 fb_info.depth = tag->framebuffer_common.framebuffer_bpp; 120 fb_info.bpp = P2ROUNDUP(fb_info.depth, 8) >> 3; 121 fb_info.screen.x = tag->framebuffer_common.framebuffer_width; 122 fb_info.screen.y = tag->framebuffer_common.framebuffer_height; 123 fb_info.fb_size = fb_info.screen.y * fb_info.pitch; 124 125 if (fb_info.paddr == 0) 126 fb_info.fb_type = FB_TYPE_UNKNOWN; 127 128 switch (tag->framebuffer_common.framebuffer_type) { 129 case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT: 130 fb_info.fb_type = FB_TYPE_EGA_TEXT; 131 return (B_FALSE); 132 133 case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED: 134 if (fb_info.paddr != 0) 135 fb_info.fb_type = FB_TYPE_INDEXED; 136 return (B_TRUE); 137 138 case MULTIBOOT_FRAMEBUFFER_TYPE_RGB: 139 if (fb_info.paddr != 0) 140 fb_info.fb_type = FB_TYPE_RGB; 141 break; 142 143 default: 144 return (B_FALSE); 145 } 146 147 fb_info.rgb.red.size = tag->u.fb2.framebuffer_red_mask_size; 148 fb_info.rgb.red.pos = tag->u.fb2.framebuffer_red_field_position; 149 fb_info.rgb.green.size = tag->u.fb2.framebuffer_green_mask_size; 150 fb_info.rgb.green.pos = tag->u.fb2.framebuffer_green_field_position; 151 fb_info.rgb.blue.size = tag->u.fb2.framebuffer_blue_mask_size; 152 fb_info.rgb.blue.pos = tag->u.fb2.framebuffer_blue_field_position; 153 154 return (B_TRUE); 155 } 156 157 /* set font and pass the data to fb_info */ 158 static void 159 boot_fb_set_font(uint16_t height, uint16_t width) 160 { 161 short h, w; 162 163 h = MIN(height, 4096); 164 w = MIN(width, 4096); 165 166 set_font(&boot_fb_font, (short *)&fb_info.terminal.y, 167 (short *)&fb_info.terminal.x, h, w); 168 fb_info.font_width = boot_fb_font.width; 169 fb_info.font_height = boot_fb_font.height; 170 } 171 172 /* fill framebuffer */ 173 static void 174 boot_fb_fill(uint8_t *dst, uint32_t data, uint32_t len) 175 { 176 uint16_t *dst16; 177 uint32_t *dst32; 178 uint32_t i; 179 180 switch (fb_info.depth) { 181 case 24: 182 case 8: 183 for (i = 0; i < len; i++) 184 dst[i] = (uint8_t)data; 185 break; 186 case 15: 187 case 16: 188 dst16 = (uint16_t *)dst; 189 len /= 2; 190 for (i = 0; i < len; i++) { 191 dst16[i] = (uint16_t)data; 192 } 193 break; 194 case 32: 195 dst32 = (uint32_t *)dst; 196 len /= 4; 197 for (i = 0; i < len; i++) { 198 dst32[i] = data; 199 } 200 break; 201 } 202 } 203 204 /* copy data to framebuffer */ 205 static void 206 boot_fb_cpy(uint8_t *dst, uint8_t *src, uint32_t len) 207 { 208 uint16_t *dst16, *src16; 209 uint32_t *dst32, *src32; 210 uint32_t i; 211 212 switch (fb_info.depth) { 213 case 24: 214 case 8: 215 for (i = 0; i < len; i++) 216 dst[i] = src[i]; 217 break; 218 case 15: 219 case 16: 220 dst16 = (uint16_t *)dst; 221 src16 = (uint16_t *)src; 222 for (i = 0; i < len >> 1; i++) { 223 dst16[i] = src16[i]; 224 } 225 break; 226 case 32: 227 dst32 = (uint32_t *)dst; 228 src32 = (uint32_t *)src; 229 for (i = 0; i < len >> 2; i++) { 230 dst32[i] = src32[i]; 231 } 232 break; 233 } 234 } 235 236 /* 237 * Allocate shadow frame buffer, called from fakebop.c when early boot 238 * allocator is ready. 239 */ 240 void 241 boot_fb_shadow_init(bootops_t *bops) 242 { 243 if (boot_console_type(NULL) != CONS_FRAMEBUFFER) 244 return; /* nothing to do */ 245 246 fb_info.shadow_fb = (uint8_t *)bops->bsys_alloc(NULL, NULL, 247 fb_info.fb_size, MMU_PAGESIZE); 248 249 if (fb_info.shadow_fb == NULL) 250 return; 251 252 /* Copy FB to shadow */ 253 boot_fb_cpy(fb_info.shadow_fb, fb_info.fb, fb_info.fb_size); 254 } 255 256 /* 257 * Translate ansi color based on inverses and brightness. 258 */ 259 static void 260 boot_get_color(uint32_t *fg, uint32_t *bg) 261 { 262 /* ansi to solaris colors, see also boot_console.c */ 263 if (fb_info.inverse == B_TRUE || 264 fb_info.inverse_screen == B_TRUE) { 265 *bg = dim_xlate[fb_info.fg_color]; 266 *fg = brt_xlate[fb_info.bg_color]; 267 } else { 268 if (fb_info.bg_color == 7) 269 *bg = brt_xlate[fb_info.bg_color]; 270 else 271 *bg = dim_xlate[fb_info.bg_color]; 272 *fg = dim_xlate[fb_info.fg_color]; 273 } 274 } 275 276 /* 277 * Map indexed color to RGB value. 278 */ 279 static uint32_t 280 boot_color_map(uint8_t index) 281 { 282 uint8_t c; 283 int pos, size; 284 uint32_t color; 285 286 /* 8bit depth is for indexed colors */ 287 if (fb_info.depth == 8) 288 return (index); 289 290 if (index >= sizeof (cmap4_to_24.red)) 291 index = 0; 292 293 c = cmap4_to_24.red[index]; 294 pos = fb_info.rgb.red.pos; 295 size = fb_info.rgb.red.size; 296 color = ((c >> 8 - size) & ((1 << size) - 1)) << pos; 297 298 c = cmap4_to_24.green[index]; 299 pos = fb_info.rgb.green.pos; 300 size = fb_info.rgb.green.size; 301 color |= ((c >> 8 - size) & ((1 << size) - 1)) << pos; 302 303 c = cmap4_to_24.blue[index]; 304 pos = fb_info.rgb.blue.pos; 305 size = fb_info.rgb.blue.size; 306 color |= ((c >> 8 - size) & ((1 << size) - 1)) << pos; 307 308 return (color); 309 } 310 311 /* set up out simple console. */ 312 /*ARGSUSED*/ 313 void 314 boot_fb_init(int console) 315 { 316 fb_info_pixel_coord_t window; 317 318 /* frame buffer address is mapped in dboot. */ 319 fb_info.fb = (uint8_t *)(uintptr_t)fb_info.paddr; 320 321 boot_fb_set_font(fb_info.screen.y, fb_info.screen.x); 322 window.x = 323 (fb_info.screen.x - fb_info.terminal.x * boot_fb_font.width) / 2; 324 window.y = 325 (fb_info.screen.y - fb_info.terminal.y * boot_fb_font.height) / 2; 326 fb_info.terminal_origin.x = window.x; 327 fb_info.terminal_origin.y = window.y; 328 329 #if defined(_BOOT) 330 /* 331 * Being called from dboot, we can have cursor terminal 332 * position passed from boot loader. In such case, fix the 333 * cursor screen coords. 334 */ 335 if (fb_info.cursor.pos.x != 0 || fb_info.cursor.pos.y != 0) { 336 fb_info.cursor.origin.x = window.x + 337 fb_info.cursor.pos.x * boot_fb_font.width; 338 fb_info.cursor.origin.y = window.y + 339 fb_info.cursor.pos.y * boot_fb_font.height; 340 } 341 #endif 342 343 /* If the cursor terminal position is 0,0 just reset screen coords */ 344 if (fb_info.cursor.pos.x == 0 && fb_info.cursor.pos.y == 0) { 345 fb_info.cursor.origin.x = window.x; 346 fb_info.cursor.origin.y = window.y; 347 } 348 349 /* 350 * Validate cursor coords with screen/terminal dimensions, 351 * if anything is off, reset to 0,0 352 */ 353 if (fb_info.cursor.pos.x > fb_info.terminal.x || 354 fb_info.cursor.pos.y > fb_info.terminal.y || 355 fb_info.cursor.origin.x > fb_info.screen.x || 356 fb_info.cursor.origin.y > fb_info.screen.y) { 357 358 fb_info.cursor.origin.x = window.x; 359 fb_info.cursor.origin.y = window.y; 360 fb_info.cursor.pos.x = 0; 361 fb_info.cursor.pos.y = 0; 362 } 363 364 #if defined(_BOOT) 365 /* clear the screen if cursor is set to 0,0 */ 366 fb_info.cursor.pos.x = fb_info.cursor.pos.y = 0; 367 if (console == CONS_FRAMEBUFFER && 368 fb_info.cursor.pos.x == 0 && fb_info.cursor.pos.y == 0) { 369 uint32_t fg, bg; 370 int i; 371 372 boot_get_color(&fg, &bg); 373 bg = boot_color_map(bg); 374 375 for (i = 0; i < fb_info.screen.y; i++) { 376 uint8_t *dest = fb_info.fb + i * fb_info.pitch; 377 boot_fb_fill(dest, bg, fb_info.pitch); 378 } 379 } 380 #endif 381 /* set up pre-calculated last line */ 382 last_line_size = fb_info.terminal.x * boot_fb_font.width * 383 fb_info.bpp; 384 last_line.x = window.x; 385 last_line.y = window.y + (fb_info.terminal.y - 1) * boot_fb_font.height; 386 387 } 388 389 /* copy rectangle to framebuffer. */ 390 static void 391 boot_fb_blit(struct vis_consdisplay *rect) 392 { 393 uint32_t size; /* write size per scanline */ 394 uint8_t *fbp, *sfbp; /* fb + calculated offset */ 395 int i; 396 397 /* make sure we will not write past FB */ 398 if (rect->col >= fb_info.screen.x || 399 rect->row >= fb_info.screen.y || 400 rect->col + rect->width >= fb_info.screen.x || 401 rect->row + rect->height >= fb_info.screen.y) 402 return; 403 404 size = rect->width * fb_info.bpp; 405 fbp = fb_info.fb + rect->col * fb_info.bpp + 406 rect->row * fb_info.pitch; 407 if (fb_info.shadow_fb != NULL) { 408 sfbp = fb_info.shadow_fb + rect->col * fb_info.bpp + 409 rect->row * fb_info.pitch; 410 } else { 411 sfbp = NULL; 412 } 413 414 /* write all scanlines in rectangle */ 415 for (i = 0; i < rect->height; i++) { 416 uint8_t *dest = fbp + i * fb_info.pitch; 417 uint8_t *src = rect->data + i * size; 418 boot_fb_cpy(dest, src, size); 419 if (sfbp != NULL) { 420 dest = sfbp + i * fb_info.pitch; 421 boot_fb_cpy(dest, src, size); 422 } 423 } 424 } 425 426 static void 427 bit_to_pix(uchar_t c) 428 { 429 uint32_t fg, bg; 430 431 boot_get_color(&fg, &bg); 432 fg = boot_color_map(fg); 433 bg = boot_color_map(bg); 434 435 switch (fb_info.depth) { 436 case 8: 437 font_bit_to_pix8(&boot_fb_font, (uint8_t *)glyph, c, fg, bg); 438 break; 439 case 15: 440 case 16: 441 font_bit_to_pix16(&boot_fb_font, (uint16_t *)glyph, c, 442 (uint16_t)fg, (uint16_t)bg); 443 break; 444 case 24: 445 font_bit_to_pix24(&boot_fb_font, (uint8_t *)glyph, c, fg, bg); 446 break; 447 case 32: 448 font_bit_to_pix32(&boot_fb_font, (uint32_t *)glyph, c, fg, bg); 449 break; 450 } 451 } 452 453 /* 454 * move the terminal window lines [1..y] to [0..y-1] and clear last line. 455 */ 456 static void 457 boot_fb_scroll(void) 458 { 459 struct vis_conscopy c_copy; 460 uint32_t soffset, toffset; 461 uint32_t width, height; 462 uint32_t fg, bg; 463 uint8_t *src, *dst, *sdst; 464 int i; 465 466 boot_get_color(&fg, &bg); 467 bg = boot_color_map(bg); 468 469 /* support for scrolling. set up the console copy data and last line */ 470 c_copy.s_row = fb_info.terminal_origin.y + boot_fb_font.height; 471 c_copy.s_col = fb_info.terminal_origin.x; 472 c_copy.e_row = fb_info.screen.y - fb_info.terminal_origin.y; 473 c_copy.e_col = fb_info.screen.x - fb_info.terminal_origin.x; 474 c_copy.t_row = fb_info.terminal_origin.y; 475 c_copy.t_col = fb_info.terminal_origin.x; 476 477 soffset = c_copy.s_col * fb_info.bpp + c_copy.s_row * fb_info.pitch; 478 toffset = c_copy.t_col * fb_info.bpp + c_copy.t_row * fb_info.pitch; 479 if (fb_info.shadow_fb != NULL) { 480 src = fb_info.shadow_fb + soffset; 481 sdst = fb_info.shadow_fb + toffset; 482 } else { 483 src = fb_info.fb + soffset; 484 sdst = NULL; 485 } 486 dst = fb_info.fb + toffset; 487 488 width = (c_copy.e_col - c_copy.s_col + 1) * fb_info.bpp; 489 height = c_copy.e_row - c_copy.s_row + 1; 490 for (i = 0; i < height; i++) { 491 uint32_t increment = i * fb_info.pitch; 492 boot_fb_cpy(dst + increment, src + increment, width); 493 if (sdst != NULL) 494 boot_fb_cpy(sdst + increment, src + increment, width); 495 } 496 497 /* now clean up the last line */ 498 toffset = last_line.x * fb_info.bpp + last_line.y * fb_info.pitch; 499 dst = fb_info.fb + toffset; 500 if (fb_info.shadow_fb != NULL) 501 sdst = fb_info.shadow_fb + toffset; 502 503 for (i = 0; i < boot_fb_font.height; i++) { 504 uint8_t *dest = dst + i * fb_info.pitch; 505 if (fb_info.fb + fb_info.fb_size >= dest + last_line_size) 506 boot_fb_fill(dest, bg, last_line_size); 507 if (sdst != NULL) { 508 dest = sdst + i * fb_info.pitch; 509 if (fb_info.shadow_fb + fb_info.fb_size >= 510 dest + last_line_size) { 511 boot_fb_fill(dest, bg, last_line_size); 512 } 513 } 514 } 515 } 516 517 /* 518 * Very simple block cursor. Save space below the cursor and restore 519 * when cursor is invisible. 520 */ 521 void 522 boot_fb_cursor(boolean_t visible) 523 { 524 uint32_t offset, size; 525 uint32_t *fb32, *sfb32 = NULL; 526 uint32_t fg, bg; 527 uint16_t *fb16, *sfb16 = NULL; 528 uint8_t *fb8, *sfb8 = NULL; 529 int i, pitch; 530 531 if (fb_info.cursor.visible == visible) 532 return; 533 534 boot_get_color(&fg, &bg); 535 fg = boot_color_map(fg); 536 bg = boot_color_map(bg); 537 538 fb_info.cursor.visible = visible; 539 pitch = fb_info.pitch; 540 size = boot_fb_font.width * fb_info.bpp; 541 542 /* 543 * Build cursor image. We are building mirror image of data on 544 * frame buffer by (D xor FG) xor BG. 545 */ 546 offset = fb_info.cursor.origin.x * fb_info.bpp + 547 fb_info.cursor.origin.y * pitch; 548 switch (fb_info.depth) { 549 case 8: 550 for (i = 0; i < boot_fb_font.height; i++) { 551 fb8 = fb_info.fb + offset + i * pitch; 552 if (fb_info.shadow_fb != NULL) 553 sfb8 = fb_info.shadow_fb + offset + i * pitch; 554 for (uint32_t j = 0; j < size; j += 1) { 555 fb8[j] = (fb8[j] ^ (fg & 0xff)) ^ (bg & 0xff); 556 557 if (sfb8 == NULL) 558 continue; 559 560 sfb8[j] = (sfb8[j] ^ (fg & 0xff)) ^ (bg & 0xff); 561 } 562 } 563 break; 564 case 15: 565 case 16: 566 for (i = 0; i < boot_fb_font.height; i++) { 567 fb16 = (uint16_t *)(fb_info.fb + offset + i * pitch); 568 if (fb_info.shadow_fb != NULL) 569 sfb16 = (uint16_t *) 570 (fb_info.shadow_fb + offset + i * pitch); 571 for (int j = 0; j < boot_fb_font.width; j++) { 572 fb16[j] = (fb16[j] ^ (fg & 0xffff)) ^ 573 (bg & 0xffff); 574 575 if (sfb16 == NULL) 576 continue; 577 578 sfb16[j] = (sfb16[j] ^ (fg & 0xffff)) ^ 579 (bg & 0xffff); 580 } 581 } 582 break; 583 case 24: 584 for (i = 0; i < boot_fb_font.height; i++) { 585 fb8 = fb_info.fb + offset + i * pitch; 586 if (fb_info.shadow_fb != NULL) 587 sfb8 = fb_info.shadow_fb + offset + i * pitch; 588 for (uint32_t j = 0; j < size; j += 3) { 589 fb8[j] = (fb8[j] ^ ((fg >> 16) & 0xff)) ^ 590 ((bg >> 16) & 0xff); 591 fb8[j+1] = (fb8[j+1] ^ ((fg >> 8) & 0xff)) ^ 592 ((bg >> 8) & 0xff); 593 fb8[j+2] = (fb8[j+2] ^ (fg & 0xff)) ^ 594 (bg & 0xff); 595 596 if (sfb8 == NULL) 597 continue; 598 599 sfb8[j] = (sfb8[j] ^ ((fg >> 16) & 0xff)) ^ 600 ((bg >> 16) & 0xff); 601 sfb8[j+1] = (sfb8[j+1] ^ ((fg >> 8) & 0xff)) ^ 602 ((bg >> 8) & 0xff); 603 sfb8[j+2] = (sfb8[j+2] ^ (fg & 0xff)) ^ 604 (bg & 0xff); 605 } 606 } 607 break; 608 case 32: 609 for (i = 0; i < boot_fb_font.height; i++) { 610 fb32 = (uint32_t *)(fb_info.fb + offset + i * pitch); 611 if (fb_info.shadow_fb != NULL) { 612 sfb32 = (uint32_t *) 613 (fb_info.shadow_fb + offset + i * pitch); 614 } 615 for (int j = 0; j < boot_fb_font.width; j++) { 616 fb32[j] = (fb32[j] ^ fg) ^ bg; 617 618 if (sfb32 == NULL) 619 continue; 620 621 sfb32[j] = (sfb32[j] ^ fg) ^ bg; 622 } 623 } 624 break; 625 } 626 } 627 628 static void 629 set_cursor_row(void) 630 { 631 fb_info.cursor.pos.y++; 632 fb_info.cursor.pos.x = 0; 633 fb_info.cursor.origin.x = fb_info.terminal_origin.x; 634 635 if (fb_info.cursor.pos.y < fb_info.terminal.y && 636 fb_info.cursor.origin.y + boot_fb_font.height < fb_info.screen.y) { 637 fb_info.cursor.origin.y += boot_fb_font.height; 638 } else { 639 fb_info.cursor.pos.y = fb_info.terminal.y - 1; 640 /* fix the cursor origin y */ 641 fb_info.cursor.origin.y = fb_info.terminal_origin.y + 642 boot_fb_font.height * fb_info.cursor.pos.y; 643 boot_fb_scroll(); 644 } 645 } 646 647 static void 648 set_cursor_col(void) 649 { 650 fb_info.cursor.pos.x++; 651 if (fb_info.cursor.pos.x < fb_info.terminal.x && 652 fb_info.cursor.origin.x + boot_fb_font.width < fb_info.screen.x) { 653 fb_info.cursor.origin.x += boot_fb_font.width; 654 } else { 655 fb_info.cursor.pos.x = 0; 656 fb_info.cursor.origin.x = fb_info.terminal_origin.x; 657 set_cursor_row(); 658 } 659 } 660 661 void 662 boot_fb_putchar(uint8_t c) 663 { 664 struct vis_consdisplay display; 665 boolean_t bs = B_FALSE; 666 667 /* early tem startup will switch cursor off, if so, keep it off */ 668 boot_fb_cursor(B_FALSE); /* cursor off */ 669 switch (c) { 670 case '\n': 671 set_cursor_row(); 672 boot_fb_cursor(B_TRUE); 673 return; 674 case '\r': 675 fb_info.cursor.pos.x = 0; 676 fb_info.cursor.origin.x = fb_info.terminal_origin.x; 677 boot_fb_cursor(B_TRUE); 678 return; 679 case '\b': 680 if (fb_info.cursor.pos.x > 0) { 681 fb_info.cursor.pos.x--; 682 fb_info.cursor.origin.x -= boot_fb_font.width; 683 } 684 c = ' '; 685 bs = B_TRUE; 686 break; 687 } 688 689 bit_to_pix(c); 690 display.col = fb_info.cursor.origin.x; 691 display.row = fb_info.cursor.origin.y; 692 display.width = boot_fb_font.width; 693 display.height = boot_fb_font.height; 694 display.data = glyph; 695 696 boot_fb_blit(&display); 697 if (bs == B_FALSE) 698 set_cursor_col(); 699 boot_fb_cursor(B_TRUE); 700 } 701