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