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 47struct 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 70static uint8 sPalette8[] = { 71 63, 32, 52, 70, 42, 88, 67, 0, 72}; 73static uint16 sPalette15[] = { 74 // 0bbbbbgggggrrrrr (5-5-5) 75 0x7fff, 0x1993, 0x2660, 0x0273, 0x6400, 0x390f, 0x6ea0, 0x0000, 76}; 77static uint16 sPalette16[] = { 78 // bbbbbggggggrrrrr (5-6-5) 79 0xffff, 0x3333, 0x4cc0, 0x04d3, 0xc800, 0x722f, 0xdd40, 0x0000, 80}; 81static 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 93static struct console_info sConsole; 94 95#ifndef _BOOT_MODE 96static struct frame_buffer_boot_info sBootInfo; 97static struct vesa_mode* sVesaModes; 98#endif 99 100 101static inline uint8 102foreground_color(uint8 attr) 103{ 104 return attr & 0x7; 105} 106 107 108static inline uint8 109background_color(uint8 attr) 110{ 111 return (attr >> 4) & 0x7; 112} 113 114 115static uint8* 116get_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 131static uint16 132get_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 142static void 143render_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 212static void 213draw_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 240static status_t 241console_get_size(int32* _width, int32* _height) 242{ 243 *_width = sConsole.columns; 244 *_height = sConsole.rows; 245 246 return B_OK; 247} 248 249 250static void 251console_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 264static void 265console_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 275static void 276console_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 299static void 300console_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 332static void 333console_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 374static status_t 375console_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 389console_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 407bool 408frame_buffer_console_available(void) 409{ 410 return sConsole.frame_buffer != 0; 411} 412 413 414status_t 415frame_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 = &bigFont; 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 451status_t 452frame_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 502status_t 503frame_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 518status_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