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