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 arch_cpu_enable_user_access 31 #undef arch_cpu_disable_user_access 32 #define arch_cpu_enable_user_access() 33 #define arch_cpu_disable_user_access() 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 arch_cpu_enable_user_access(); 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 arch_cpu_disable_user_access(); 174 } else { 175 // VGA mode will be treated as monochrome 176 // (ie. only the first plane will be used) 177 178 uint8* base = (uint8*)(sConsole.frame_buffer 179 + sConsole.bytes_per_row * row * sConsole.font->glyphHeight 180 + column * sConsole.font->glyphWidth / 8); 181 uint8 baseOffset = (column * sConsole.font->glyphWidth) & 0x7; 182 183 arch_cpu_enable_user_access(); 184 for (int y = 0; y < sConsole.font->glyphHeight; y++) { 185 uint16_t bits = get_font_data(glyph, y); 186 uint8 offset = baseOffset; 187 uint8 mask = 1 << (7 - baseOffset); 188 189 for (int x = 0; x < sConsole.font->glyphWidth; x++) { 190 if (mask == 0) 191 mask = 128; 192 193 // black on white 194 if (bits & 1) 195 base[offset / 8] &= ~mask; 196 else 197 base[offset / 8] |= mask; 198 199 bits >>= 1; 200 mask >>= 1; 201 offset += 1; 202 } 203 204 base += sConsole.bytes_per_row; 205 } 206 arch_cpu_disable_user_access(); 207 } 208 } 209 210 211 static void 212 draw_cursor(int32 x, int32 y) 213 { 214 if (x < 0 || y < 0) 215 return; 216 217 x *= sConsole.font->glyphWidth * sConsole.bytes_per_pixel; 218 y *= sConsole.font->glyphHeight; 219 int32 endX = x + sConsole.font->glyphWidth * sConsole.bytes_per_pixel; 220 int32 endY = y + sConsole.font->glyphHeight; 221 uint8* base = (uint8*)(sConsole.frame_buffer + y * sConsole.bytes_per_row); 222 223 if (sConsole.depth < 8) { 224 x /= 8; 225 endY /= 8; 226 } 227 228 arch_cpu_enable_user_access(); 229 for (; y < endY; y++) { 230 for (int32 x2 = x; x2 < endX; x2++) 231 base[x2] = ~base[x2]; 232 233 base += sConsole.bytes_per_row; 234 } 235 arch_cpu_disable_user_access(); 236 } 237 238 239 static status_t 240 console_get_size(int32* _width, int32* _height) 241 { 242 *_width = sConsole.columns; 243 *_height = sConsole.rows; 244 245 return B_OK; 246 } 247 248 249 static void 250 console_move_cursor(int32 x, int32 y) 251 { 252 if (!frame_buffer_console_available()) 253 return; 254 255 draw_cursor(sConsole.cursor_x, sConsole.cursor_y); 256 draw_cursor(x, y); 257 258 sConsole.cursor_x = x; 259 sConsole.cursor_y = y; 260 } 261 262 263 static void 264 console_put_glyph(int32 x, int32 y, uint8 glyph, uint8 attr) 265 { 266 if (x >= sConsole.columns || y >= sConsole.rows 267 || !frame_buffer_console_available()) 268 return; 269 270 render_glyph(x, y, glyph, attr); 271 } 272 273 274 static void 275 console_fill_glyph(int32 x, int32 y, int32 width, int32 height, uint8 glyph, 276 uint8 attr) 277 { 278 if (x >= sConsole.columns || y >= sConsole.rows 279 || !frame_buffer_console_available()) 280 return; 281 282 int32 left = x + width; 283 if (left > sConsole.columns) 284 left = sConsole.columns; 285 286 int32 bottom = y + height; 287 if (bottom > sConsole.rows) 288 bottom = sConsole.rows; 289 290 for (; y < bottom; y++) { 291 for (int32 x2 = x; x2 < left; x2++) { 292 render_glyph(x2, y, glyph, attr); 293 } 294 } 295 } 296 297 298 static void 299 console_blit(int32 srcx, int32 srcy, int32 width, int32 height, int32 destx, 300 int32 desty) 301 { 302 if (!frame_buffer_console_available()) 303 return; 304 305 height *= sConsole.font->glyphHeight; 306 srcy *= sConsole.font->glyphHeight; 307 desty *= sConsole.font->glyphHeight; 308 309 if (sConsole.depth >= 8) { 310 width *= sConsole.font->glyphWidth * sConsole.bytes_per_pixel; 311 srcx *= sConsole.font->glyphWidth * sConsole.bytes_per_pixel; 312 destx *= sConsole.font->glyphWidth * sConsole.bytes_per_pixel; 313 } else { 314 // monochrome mode 315 width = width * sConsole.font->glyphWidth / 8; 316 srcx = srcx * sConsole.font->glyphWidth / 8; 317 destx = destx * sConsole.font->glyphWidth / 8; 318 } 319 320 arch_cpu_enable_user_access(); 321 for (int32 y = 0; y < height; y++) { 322 memmove((void*)(sConsole.frame_buffer + (desty + y) 323 * sConsole.bytes_per_row + destx), 324 (void*)(sConsole.frame_buffer + (srcy + y) * sConsole.bytes_per_row 325 + srcx), width); 326 } 327 arch_cpu_disable_user_access(); 328 } 329 330 331 static void 332 console_clear(uint8 attr) 333 { 334 if (!frame_buffer_console_available()) 335 return; 336 337 arch_cpu_enable_user_access(); 338 switch (sConsole.bytes_per_pixel) { 339 case 1: 340 if (sConsole.depth >= 8) { 341 memset((void*)sConsole.frame_buffer, 342 sPalette8[background_color(attr)], 343 sConsole.height * sConsole.bytes_per_row); 344 } else { 345 // special case for VGA mode 346 memset((void*)sConsole.frame_buffer, 0xff, 347 sConsole.height * sConsole.bytes_per_row); 348 } 349 break; 350 default: 351 { 352 uint8* base = (uint8*)sConsole.frame_buffer; 353 uint8* color = get_palette_entry(background_color(attr)); 354 355 for (int32 y = 0; y < sConsole.height; y++) { 356 for (int32 x = 0; x < sConsole.width; x++) { 357 for (int32 i = 0; i < sConsole.bytes_per_pixel; i++) { 358 base[x * sConsole.bytes_per_pixel + i] = color[i]; 359 } 360 } 361 base += sConsole.bytes_per_row; 362 } 363 break; 364 } 365 } 366 arch_cpu_disable_user_access(); 367 368 sConsole.cursor_x = -1; 369 sConsole.cursor_y = -1; 370 } 371 372 373 static status_t 374 console_std_ops(int32 op, ...) 375 { 376 switch (op) { 377 case B_MODULE_INIT: 378 return frame_buffer_console_available() ? B_OK : B_ERROR; 379 case B_MODULE_UNINIT: 380 return B_OK; 381 382 default: 383 return B_ERROR; 384 } 385 } 386 387 388 console_module_info gFrameBufferConsoleModule = { 389 { 390 FRAME_BUFFER_CONSOLE_MODULE_NAME, 391 0, 392 console_std_ops 393 }, 394 &console_get_size, 395 &console_move_cursor, 396 &console_put_glyph, 397 &console_fill_glyph, 398 &console_blit, 399 &console_clear, 400 }; 401 402 403 // #pragma mark - 404 405 406 bool 407 frame_buffer_console_available(void) 408 { 409 return sConsole.frame_buffer != 0; 410 } 411 412 413 status_t 414 frame_buffer_update(addr_t baseAddress, int32 width, int32 height, int32 depth, 415 int32 bytesPerRow) 416 { 417 TRACE(("frame_buffer_update(buffer = %p, width = %ld, height = %ld, " 418 "depth = %ld, bytesPerRow = %ld)\n", (void*)baseAddress, width, height, 419 depth, bytesPerRow)); 420 421 mutex_lock(&sConsole.lock); 422 423 if (width <= 1920 || height <= 1080) { 424 sConsole.font = &smallFont; 425 } else { 426 sConsole.font = &bigFont; 427 } 428 429 sConsole.frame_buffer = baseAddress; 430 sConsole.width = width; 431 sConsole.height = height; 432 sConsole.depth = depth; 433 sConsole.bytes_per_pixel = (depth + 7) / 8; 434 sConsole.bytes_per_row = bytesPerRow; 435 sConsole.columns = sConsole.width / sConsole.font->glyphWidth; 436 sConsole.rows = sConsole.height / sConsole.font->glyphHeight; 437 // initially, the cursor is hidden 438 sConsole.cursor_x = -1; 439 sConsole.cursor_y = -1; 440 441 TRACE(("framebuffer mapped at %p, %ld columns, %ld rows\n", 442 (void*)sConsole.frame_buffer, sConsole.columns, sConsole.rows)); 443 444 mutex_unlock(&sConsole.lock); 445 return B_OK; 446 } 447 448 449 #ifndef _BOOT_MODE 450 status_t 451 frame_buffer_console_init(kernel_args* args) 452 { 453 mutex_init(&sConsole.lock, "console_lock"); 454 455 if (!args->frame_buffer.enabled) 456 return B_OK; 457 458 void* frameBuffer; 459 sConsole.area = map_physical_memory("vesa frame buffer", 460 args->frame_buffer.physical_buffer.start, 461 args->frame_buffer.physical_buffer.size, B_ANY_KERNEL_ADDRESS, 462 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA, 463 &frameBuffer); 464 if (sConsole.area < 0) 465 return sConsole.area; 466 467 frame_buffer_update((addr_t)frameBuffer, args->frame_buffer.width, 468 args->frame_buffer.height, args->frame_buffer.depth, 469 args->frame_buffer.bytes_per_row); 470 471 sBootInfo.area = sConsole.area; 472 sBootInfo.physical_frame_buffer = args->frame_buffer.physical_buffer.start; 473 sBootInfo.frame_buffer = (addr_t)frameBuffer; 474 sBootInfo.width = args->frame_buffer.width; 475 sBootInfo.height = args->frame_buffer.height; 476 sBootInfo.depth = args->frame_buffer.depth; 477 sBootInfo.bytes_per_row = args->frame_buffer.bytes_per_row; 478 sBootInfo.vesa_capabilities = args->vesa_capabilities; 479 480 add_boot_item(FRAME_BUFFER_BOOT_INFO, &sBootInfo, 481 sizeof(frame_buffer_boot_info)); 482 483 sVesaModes = (vesa_mode*)malloc(args->vesa_modes_size); 484 if (sVesaModes != NULL && args->vesa_modes_size > 0) { 485 memcpy(sVesaModes, args->vesa_modes, args->vesa_modes_size); 486 add_boot_item(VESA_MODES_BOOT_INFO, sVesaModes, args->vesa_modes_size); 487 } 488 489 if (args->edid_info != NULL) { 490 edid1_info* info = (edid1_info*)malloc(sizeof(edid1_info)); 491 if (info != NULL) { 492 memcpy(info, args->edid_info, sizeof(edid1_info)); 493 add_boot_item(VESA_EDID_BOOT_INFO, info, sizeof(edid1_info)); 494 } 495 } 496 497 return B_OK; 498 } 499 500 501 status_t 502 frame_buffer_console_init_post_modules(kernel_args* args) 503 { 504 if (sConsole.frame_buffer == 0) 505 return B_OK; 506 507 // try to set frame buffer memory to write combined 508 509 return vm_set_area_memory_type(sConsole.area, 510 args->frame_buffer.physical_buffer.start, B_WRITE_COMBINING_MEMORY); 511 } 512 513 514 // #pragma mark - 515 516 517 status_t 518 _user_frame_buffer_update(addr_t baseAddress, int32 width, int32 height, 519 int32 depth, int32 bytesPerRow) 520 { 521 debug_stop_screen_debug_output(); 522 523 if (geteuid() != 0) 524 return B_NOT_ALLOWED; 525 if (IS_USER_ADDRESS(baseAddress) && baseAddress != 0) 526 return B_BAD_ADDRESS; 527 528 return frame_buffer_update(baseAddress, width, height, depth, bytesPerRow); 529 } 530 #endif 531 532