1 /* 2 * Copyright 2018, Jérôme Duval, jerome.duval@gmail.com. 3 * Copyright 2005-2009, Axel Dörfler, axeld@pinc-software.de. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include <frame_buffer_console.h> 9 10 #include <stdlib.h> 11 #include <string.h> 12 #include <stdio.h> 13 #include <unistd.h> 14 15 #include <KernelExport.h> 16 #include <kernel.h> 17 #include <lock.h> 18 #include <boot_item.h> 19 #include <vm/vm.h> 20 #include <fs/devfs.h> 21 #include <boot/kernel_args.h> 22 23 #ifndef _BOOT_MODE 24 #include <vesa_info.h> 25 26 #include <edid.h> 27 #else 28 #define mutex_lock(...) 29 #define mutex_unlock(...) 30 #undef set_ac 31 #undef clear_ac 32 #define set_ac() 33 #define clear_ac() 34 #endif 35 36 #include "font.h" 37 38 39 //#define TRACE_FB_CONSOLE 40 #ifdef TRACE_FB_CONSOLE 41 # define TRACE(x) dprintf x 42 #else 43 # define TRACE(x) ; 44 #endif 45 46 47 struct console_info { 48 mutex lock; 49 area_id area; 50 51 addr_t frame_buffer; 52 int32 width; 53 int32 height; 54 int32 depth; 55 int32 bytes_per_pixel; 56 int32 bytes_per_row; 57 58 int32 columns; 59 int32 rows; 60 int32 cursor_x; 61 int32 cursor_y; 62 FramebufferFont* font; 63 }; 64 65 // Palette is (white and black are exchanged): 66 // 0 - white, 1 - blue, 2 - green, 3 - cyan, 4 - red, 5 - magenta, 6 - yellow, 67 // 7 - black 68 // 8-15 - same but bright (we're ignoring those) 69 70 static uint8 sPalette8[] = { 71 63, 32, 52, 70, 42, 88, 67, 0, 72 }; 73 static uint16 sPalette15[] = { 74 // 0bbbbbgggggrrrrr (5-5-5) 75 0x7fff, 0x1993, 0x2660, 0x0273, 0x6400, 0x390f, 0x6ea0, 0x0000, 76 }; 77 static uint16 sPalette16[] = { 78 // bbbbbggggggrrrrr (5-6-5) 79 0xffff, 0x3333, 0x4cc0, 0x04d3, 0xc800, 0x722f, 0xdd40, 0x0000, 80 }; 81 static uint32 sPalette32[] = { 82 // is also used by 24 bit modes 83 0xffffff, // white 84 0x336698, // blue 85 0x4e9a00, // green 86 0x06989a, // cyan 87 0xcc0000, // red 88 0x73447b, // magenta 89 0xdaa800, // yellow 90 0x000000, // black 91 }; 92 93 static struct console_info sConsole; 94 95 #ifndef _BOOT_MODE 96 static struct frame_buffer_boot_info sBootInfo; 97 static struct vesa_mode* sVesaModes; 98 #endif 99 100 101 static inline uint8 102 foreground_color(uint8 attr) 103 { 104 return attr & 0x7; 105 } 106 107 108 static inline uint8 109 background_color(uint8 attr) 110 { 111 return (attr >> 4) & 0x7; 112 } 113 114 115 static uint8* 116 get_palette_entry(uint8 index) 117 { 118 switch (sConsole.depth) { 119 case 8: 120 return &sPalette8[index]; 121 case 15: 122 return (uint8*)&sPalette15[index]; 123 case 16: 124 return (uint8*)&sPalette16[index]; 125 default: 126 return (uint8*)&sPalette32[index]; 127 } 128 } 129 130 131 static uint16 132 get_font_data(uint8 glyph, int y) 133 { 134 if (sConsole.font->glyphWidth > 8) { 135 uint16* data = (uint16*)sConsole.font->data; 136 return data[sConsole.font->glyphHeight * glyph + y]; 137 } else 138 return sConsole.font->data[sConsole.font->glyphHeight * glyph + y]; 139 } 140 141 142 static void 143 render_glyph(int32 column, int32 row, uint8 glyph, uint8 attr) 144 { 145 // we're ASCII only 146 if (glyph > 127) 147 glyph = 127; 148 149 if (sConsole.depth >= 8) { 150 uint8* base = (uint8*)(sConsole.frame_buffer 151 + sConsole.bytes_per_row * row * sConsole.font->glyphHeight 152 + column * sConsole.font->glyphWidth * sConsole.bytes_per_pixel); 153 uint8* color = get_palette_entry(foreground_color(attr)); 154 uint8* backgroundColor = get_palette_entry(background_color(attr)); 155 156 set_ac(); 157 for (int y = 0; y < sConsole.font->glyphHeight; y++) { 158 uint16_t bits = get_font_data(glyph, y); 159 for (int x = 0; x < sConsole.font->glyphWidth; x++) { 160 for (int32 i = 0; i < sConsole.bytes_per_pixel; i++) { 161 if (bits & 1) 162 base[x * sConsole.bytes_per_pixel + i] = color[i]; 163 else { 164 base[x * sConsole.bytes_per_pixel + i] 165 = backgroundColor[i]; 166 } 167 } 168 bits >>= 1; 169 } 170 171 base += sConsole.bytes_per_row; 172 } 173 clear_ac(); 174 175 } else { 176 // VGA mode will be treated as monochrome 177 // (ie. only the first plane will be used) 178 179 uint8* base = (uint8*)(sConsole.frame_buffer 180 + sConsole.bytes_per_row * row * sConsole.font->glyphHeight 181 + column * sConsole.font->glyphWidth / 8); 182 uint8 baseOffset = (column * sConsole.font->glyphWidth) & 0x7; 183 184 set_ac(); 185 for (int y = 0; y < sConsole.font->glyphHeight; y++) { 186 uint16_t bits = get_font_data(glyph, y); 187 uint8 offset = baseOffset; 188 uint8 mask = 1 << (7 - baseOffset); 189 190 for (int x = 0; x < sConsole.font->glyphWidth; x++) { 191 if (mask == 0) 192 mask = 128; 193 194 // black on white 195 if (bits & 1) 196 base[offset / 8] &= ~mask; 197 else 198 base[offset / 8] |= mask; 199 200 bits >>= 1; 201 mask >>= 1; 202 offset += 1; 203 } 204 205 base += sConsole.bytes_per_row; 206 } 207 clear_ac(); 208 } 209 } 210 211 212 static void 213 draw_cursor(int32 x, int32 y) 214 { 215 if (x < 0 || y < 0) 216 return; 217 218 x *= sConsole.font->glyphWidth * sConsole.bytes_per_pixel; 219 y *= sConsole.font->glyphHeight; 220 int32 endX = x + sConsole.font->glyphWidth * sConsole.bytes_per_pixel; 221 int32 endY = y + sConsole.font->glyphHeight; 222 uint8* base = (uint8*)(sConsole.frame_buffer + y * sConsole.bytes_per_row); 223 224 if (sConsole.depth < 8) { 225 x /= 8; 226 endY /= 8; 227 } 228 229 set_ac(); 230 for (; y < endY; y++) { 231 for (int32 x2 = x; x2 < endX; x2++) 232 base[x2] = ~base[x2]; 233 234 base += sConsole.bytes_per_row; 235 } 236 clear_ac(); 237 } 238 239 240 static status_t 241 console_get_size(int32* _width, int32* _height) 242 { 243 *_width = sConsole.columns; 244 *_height = sConsole.rows; 245 246 return B_OK; 247 } 248 249 250 static void 251 console_move_cursor(int32 x, int32 y) 252 { 253 if (!frame_buffer_console_available()) 254 return; 255 256 draw_cursor(sConsole.cursor_x, sConsole.cursor_y); 257 draw_cursor(x, y); 258 259 sConsole.cursor_x = x; 260 sConsole.cursor_y = y; 261 } 262 263 264 static void 265 console_put_glyph(int32 x, int32 y, uint8 glyph, uint8 attr) 266 { 267 if (x >= sConsole.columns || y >= sConsole.rows 268 || !frame_buffer_console_available()) 269 return; 270 271 render_glyph(x, y, glyph, attr); 272 } 273 274 275 static void 276 console_fill_glyph(int32 x, int32 y, int32 width, int32 height, uint8 glyph, 277 uint8 attr) 278 { 279 if (x >= sConsole.columns || y >= sConsole.rows 280 || !frame_buffer_console_available()) 281 return; 282 283 int32 left = x + width; 284 if (left > sConsole.columns) 285 left = sConsole.columns; 286 287 int32 bottom = y + height; 288 if (bottom > sConsole.rows) 289 bottom = sConsole.rows; 290 291 for (; y < bottom; y++) { 292 for (int32 x2 = x; x2 < left; x2++) { 293 render_glyph(x2, y, glyph, attr); 294 } 295 } 296 } 297 298 299 static void 300 console_blit(int32 srcx, int32 srcy, int32 width, int32 height, int32 destx, 301 int32 desty) 302 { 303 if (!frame_buffer_console_available()) 304 return; 305 306 height *= sConsole.font->glyphHeight; 307 srcy *= sConsole.font->glyphHeight; 308 desty *= sConsole.font->glyphHeight; 309 310 if (sConsole.depth >= 8) { 311 width *= sConsole.font->glyphWidth * sConsole.bytes_per_pixel; 312 srcx *= sConsole.font->glyphWidth * sConsole.bytes_per_pixel; 313 destx *= sConsole.font->glyphWidth * sConsole.bytes_per_pixel; 314 } else { 315 // monochrome mode 316 width = width * sConsole.font->glyphWidth / 8; 317 srcx = srcx * sConsole.font->glyphWidth / 8; 318 destx = destx * sConsole.font->glyphWidth / 8; 319 } 320 321 set_ac(); 322 for (int32 y = 0; y < height; y++) { 323 memmove((void*)(sConsole.frame_buffer + (desty + y) 324 * sConsole.bytes_per_row + destx), 325 (void*)(sConsole.frame_buffer + (srcy + y) * sConsole.bytes_per_row 326 + srcx), width); 327 } 328 clear_ac(); 329 } 330 331 332 static void 333 console_clear(uint8 attr) 334 { 335 if (!frame_buffer_console_available()) 336 return; 337 338 set_ac(); 339 switch (sConsole.bytes_per_pixel) { 340 case 1: 341 if (sConsole.depth >= 8) { 342 memset((void*)sConsole.frame_buffer, 343 sPalette8[background_color(attr)], 344 sConsole.height * sConsole.bytes_per_row); 345 } else { 346 // special case for VGA mode 347 memset((void*)sConsole.frame_buffer, 0xff, 348 sConsole.height * sConsole.bytes_per_row); 349 } 350 break; 351 default: 352 { 353 uint8* base = (uint8*)sConsole.frame_buffer; 354 uint8* color = get_palette_entry(background_color(attr)); 355 356 for (int32 y = 0; y < sConsole.height; y++) { 357 for (int32 x = 0; x < sConsole.width; x++) { 358 for (int32 i = 0; i < sConsole.bytes_per_pixel; i++) { 359 base[x * sConsole.bytes_per_pixel + i] = color[i]; 360 } 361 } 362 base += sConsole.bytes_per_row; 363 } 364 break; 365 } 366 } 367 368 clear_ac(); 369 sConsole.cursor_x = -1; 370 sConsole.cursor_y = -1; 371 } 372 373 374 static status_t 375 console_std_ops(int32 op, ...) 376 { 377 switch (op) { 378 case B_MODULE_INIT: 379 return frame_buffer_console_available() ? B_OK : B_ERROR; 380 case B_MODULE_UNINIT: 381 return B_OK; 382 383 default: 384 return B_ERROR; 385 } 386 } 387 388 389 console_module_info gFrameBufferConsoleModule = { 390 { 391 FRAME_BUFFER_CONSOLE_MODULE_NAME, 392 0, 393 console_std_ops 394 }, 395 &console_get_size, 396 &console_move_cursor, 397 &console_put_glyph, 398 &console_fill_glyph, 399 &console_blit, 400 &console_clear, 401 }; 402 403 404 // #pragma mark - 405 406 407 bool 408 frame_buffer_console_available(void) 409 { 410 return sConsole.frame_buffer != 0; 411 } 412 413 414 status_t 415 frame_buffer_update(addr_t baseAddress, int32 width, int32 height, int32 depth, 416 int32 bytesPerRow) 417 { 418 TRACE(("frame_buffer_update(buffer = %p, width = %ld, height = %ld, " 419 "depth = %ld, bytesPerRow = %ld)\n", (void*)baseAddress, width, height, 420 depth, bytesPerRow)); 421 422 mutex_lock(&sConsole.lock); 423 424 if (width <= 1920 || height <= 1080) { 425 sConsole.font = &smallFont; 426 } else { 427 sConsole.font = &spleen12Font; 428 } 429 430 sConsole.frame_buffer = baseAddress; 431 sConsole.width = width; 432 sConsole.height = height; 433 sConsole.depth = depth; 434 sConsole.bytes_per_pixel = (depth + 7) / 8; 435 sConsole.bytes_per_row = bytesPerRow; 436 sConsole.columns = sConsole.width / sConsole.font->glyphWidth; 437 sConsole.rows = sConsole.height / sConsole.font->glyphHeight; 438 // initially, the cursor is hidden 439 sConsole.cursor_x = -1; 440 sConsole.cursor_y = -1; 441 442 TRACE(("framebuffer mapped at %p, %ld columns, %ld rows\n", 443 (void*)sConsole.frame_buffer, sConsole.columns, sConsole.rows)); 444 445 mutex_unlock(&sConsole.lock); 446 return B_OK; 447 } 448 449 450 #ifndef _BOOT_MODE 451 status_t 452 frame_buffer_console_init(kernel_args* args) 453 { 454 mutex_init(&sConsole.lock, "console_lock"); 455 456 if (!args->frame_buffer.enabled) 457 return B_OK; 458 459 void* frameBuffer; 460 sConsole.area = map_physical_memory("vesa frame buffer", 461 args->frame_buffer.physical_buffer.start, 462 args->frame_buffer.physical_buffer.size, B_ANY_KERNEL_ADDRESS, 463 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA, 464 &frameBuffer); 465 if (sConsole.area < 0) 466 return sConsole.area; 467 468 frame_buffer_update((addr_t)frameBuffer, args->frame_buffer.width, 469 args->frame_buffer.height, args->frame_buffer.depth, 470 args->frame_buffer.bytes_per_row); 471 472 sBootInfo.area = sConsole.area; 473 sBootInfo.physical_frame_buffer = args->frame_buffer.physical_buffer.start; 474 sBootInfo.frame_buffer = (addr_t)frameBuffer; 475 sBootInfo.width = args->frame_buffer.width; 476 sBootInfo.height = args->frame_buffer.height; 477 sBootInfo.depth = args->frame_buffer.depth; 478 sBootInfo.bytes_per_row = args->frame_buffer.bytes_per_row; 479 sBootInfo.vesa_capabilities = args->vesa_capabilities; 480 481 add_boot_item(FRAME_BUFFER_BOOT_INFO, &sBootInfo, 482 sizeof(frame_buffer_boot_info)); 483 484 sVesaModes = (vesa_mode*)malloc(args->vesa_modes_size); 485 if (sVesaModes != NULL && args->vesa_modes_size > 0) { 486 memcpy(sVesaModes, args->vesa_modes, args->vesa_modes_size); 487 add_boot_item(VESA_MODES_BOOT_INFO, sVesaModes, args->vesa_modes_size); 488 } 489 490 if (args->edid_info != NULL) { 491 edid1_info* info = (edid1_info*)malloc(sizeof(edid1_info)); 492 if (info != NULL) { 493 memcpy(info, args->edid_info, sizeof(edid1_info)); 494 add_boot_item(VESA_EDID_BOOT_INFO, info, sizeof(edid1_info)); 495 } 496 } 497 498 return B_OK; 499 } 500 501 502 status_t 503 frame_buffer_console_init_post_modules(kernel_args* args) 504 { 505 if (sConsole.frame_buffer == 0) 506 return B_OK; 507 508 // try to set frame buffer memory to write combined 509 510 return vm_set_area_memory_type(sConsole.area, 511 args->frame_buffer.physical_buffer.start, B_MTR_WC); 512 } 513 514 515 // #pragma mark - 516 517 518 status_t 519 _user_frame_buffer_update(addr_t baseAddress, int32 width, int32 height, 520 int32 depth, int32 bytesPerRow) 521 { 522 debug_stop_screen_debug_output(); 523 524 if (geteuid() != 0) 525 return B_NOT_ALLOWED; 526 if (IS_USER_ADDRESS(baseAddress) && baseAddress != 0) 527 return B_BAD_ADDRESS; 528 529 return frame_buffer_update(baseAddress, width, height, depth, bytesPerRow); 530 } 531 #endif 532 533