1 /* 2 * Copyright 2005-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <KernelExport.h> 8 #include <kernel.h> 9 #include <lock.h> 10 #include <boot_item.h> 11 #include <vm.h> 12 #include <fs/devfs.h> 13 #include <boot/kernel_args.h> 14 15 #include <frame_buffer_console.h> 16 #include "font.h" 17 18 #include <stdlib.h> 19 #include <string.h> 20 #include <stdio.h> 21 #include <unistd.h> 22 23 24 //#define TRACE_FB_CONSOLE 25 #ifdef TRACE_FB_CONSOLE 26 # define TRACE(x) dprintf x 27 #else 28 # define TRACE(x) ; 29 #endif 30 31 32 struct console_info { 33 mutex lock; 34 area_id area; 35 36 addr_t frame_buffer; 37 int32 width; 38 int32 height; 39 int32 depth; 40 int32 bytes_per_pixel; 41 int32 bytes_per_row; 42 43 int32 columns; 44 int32 rows; 45 int32 cursor_x; 46 int32 cursor_y; 47 }; 48 49 // Palette is (white and black are exchanged): 50 // 0 - white, 1 - blue, 2 - green, 3 - cyan, 4 - red, 5 - magenta, 6 - yellow, 7 - black 51 // 8-15 - same but bright (we're ignoring those) 52 53 static uint8 sPalette8[] = { 54 63, 32, 52, 70, 42, 88, 67, 0, 55 }; 56 static uint16 sPalette15[] = { 57 // 0bbbbbgggggrrrrr (5-5-5) 58 0x7fff, 0x001f, 0x03e0, 0x03ff, 0x7c00, 0x7c1f, 0x7fe0, 0x0000, 59 }; 60 static uint16 sPalette16[] = { 61 // bbbbbggggggrrrrr (5-6-5) 62 0xffff, 0x001f, 0x07e0, 0x07ff, 0xf800, 0xf81f, 0xffe0, 0x0000, 63 }; 64 static uint32 sPalette32[] = { 65 // is also used by 24 bit modes 66 0xffffff, 0x0000ff, 0x00ff00, 0x00ffff, 0xff0000, 0xff00ff, 0xffff00, 0x000000, 67 }; 68 69 static struct console_info sConsole; 70 static struct frame_buffer_boot_info sBootInfo; 71 72 73 static inline uint8 74 foreground_color(uint8 attr) 75 { 76 return attr & 0x7; 77 } 78 79 80 static inline uint8 81 background_color(uint8 attr) 82 { 83 return (attr >> 4) & 0x7; 84 } 85 86 87 static uint8 * 88 get_palette_entry(uint8 index) 89 { 90 switch (sConsole.depth) { 91 case 8: 92 return &sPalette8[index]; 93 case 15: 94 return (uint8 *)&sPalette15[index]; 95 case 16: 96 return (uint8 *)&sPalette16[index]; 97 default: 98 return (uint8 *)&sPalette32[index]; 99 } 100 } 101 102 103 static void 104 render_glyph(int32 x, int32 y, uint8 glyph, uint8 attr) 105 { 106 // we're ASCII only 107 if (glyph > 127) 108 glyph = 127; 109 110 if (sConsole.depth >= 8) { 111 uint8 *base = (uint8 *)(sConsole.frame_buffer + sConsole.bytes_per_row * y * CHAR_HEIGHT 112 + x * CHAR_WIDTH * sConsole.bytes_per_pixel); 113 uint8 *color = get_palette_entry(foreground_color(attr)); 114 uint8 *backgroundColor = get_palette_entry(background_color(attr)); 115 116 for (y = 0; y < CHAR_HEIGHT; y++) { 117 uint8 bits = FONT[CHAR_HEIGHT * glyph + y]; 118 for (x = 0; x < CHAR_WIDTH; x++) { 119 for (int32 i = 0; i < sConsole.bytes_per_pixel; i++) { 120 if (bits & 1) 121 base[x * sConsole.bytes_per_pixel + i] = color[i]; 122 else 123 base[x * sConsole.bytes_per_pixel + i] = backgroundColor[i]; 124 } 125 bits >>= 1; 126 } 127 128 base += sConsole.bytes_per_row; 129 } 130 } else { 131 // monochrome mode 132 133 uint8 *base = (uint8 *)(sConsole.frame_buffer + sConsole.bytes_per_row * y * CHAR_HEIGHT 134 + x * CHAR_WIDTH / 8); 135 uint8 baseOffset = (x * CHAR_WIDTH) & 0x7; 136 137 for (y = 0; y < CHAR_HEIGHT; y++) { 138 uint8 bits = FONT[CHAR_HEIGHT * glyph + y]; 139 uint8 offset = baseOffset; 140 uint8 mask = 1 << (7 - baseOffset); 141 142 for (x = 0; x < CHAR_WIDTH; x++) { 143 if (mask == 0) 144 mask = 128; 145 146 // black on white 147 if (bits & 1) 148 base[offset / 8] &= ~mask; 149 else 150 base[offset / 8] |= mask; 151 152 bits >>= 1; 153 mask >>= 1; 154 offset += 1; 155 } 156 157 base += sConsole.bytes_per_row; 158 } 159 } 160 } 161 162 163 static void 164 draw_cursor(int32 x, int32 y) 165 { 166 if (x < 0 || y < 0) 167 return; 168 169 x *= CHAR_WIDTH * sConsole.bytes_per_pixel; 170 y *= CHAR_HEIGHT; 171 int32 endX = x + CHAR_WIDTH * sConsole.bytes_per_pixel; 172 int32 endY = y + CHAR_HEIGHT; 173 uint8 *base = (uint8 *)(sConsole.frame_buffer + y * sConsole.bytes_per_row); 174 175 if (sConsole.depth < 8) { 176 x /= 8; 177 endY /= 8; 178 } 179 180 for (; y < endY; y++) { 181 for (int32 x2 = x; x2 < endX; x2++) 182 base[x2] = ~base[x2]; 183 184 base += sConsole.bytes_per_row; 185 } 186 } 187 188 189 static status_t 190 console_get_size(int32 *_width, int32 *_height) 191 { 192 *_width = sConsole.columns; 193 *_height = sConsole.rows; 194 195 return B_OK; 196 } 197 198 199 static void 200 console_move_cursor(int32 x, int32 y) 201 { 202 if (!frame_buffer_console_available()) 203 return; 204 205 draw_cursor(sConsole.cursor_x, sConsole.cursor_y); 206 draw_cursor(x, y); 207 208 sConsole.cursor_x = x; 209 sConsole.cursor_y = y; 210 } 211 212 213 static void 214 console_put_glyph(int32 x, int32 y, uint8 glyph, uint8 attr) 215 { 216 if (x >= sConsole.columns || y >= sConsole.rows 217 || !frame_buffer_console_available()) 218 return; 219 220 render_glyph(x, y, glyph, attr); 221 } 222 223 224 static void 225 console_fill_glyph(int32 x, int32 y, int32 width, int32 height, uint8 glyph, 226 uint8 attr) 227 { 228 if (x >= sConsole.columns || y >= sConsole.rows 229 || !frame_buffer_console_available()) 230 return; 231 232 int32 left = x + width; 233 if (left > sConsole.columns) 234 left = sConsole.columns; 235 236 int32 bottom = y + height; 237 if (bottom > sConsole.rows) 238 bottom = sConsole.rows; 239 240 for (; y < bottom; y++) { 241 for (int32 x2 = x; x2 < left; x2++) { 242 render_glyph(x2, y, glyph, attr); 243 } 244 } 245 } 246 247 248 static void 249 console_blit(int32 srcx, int32 srcy, int32 width, int32 height, int32 destx, 250 int32 desty) 251 { 252 if (!frame_buffer_console_available()) 253 return; 254 255 height *= CHAR_HEIGHT; 256 srcy *= CHAR_HEIGHT; 257 desty *= CHAR_HEIGHT; 258 259 if (sConsole.depth >= 8) { 260 width *= CHAR_WIDTH * sConsole.bytes_per_pixel; 261 srcx *= CHAR_WIDTH * sConsole.bytes_per_pixel; 262 destx *= CHAR_WIDTH * sConsole.bytes_per_pixel; 263 } else { 264 // monochrome mode 265 width = width * CHAR_WIDTH / 8; 266 srcx = srcx * CHAR_WIDTH / 8; 267 destx = destx * CHAR_WIDTH / 8; 268 } 269 270 for (int32 y = 0; y < height; y++) { 271 memmove((void *)(sConsole.frame_buffer + (desty + y) 272 * sConsole.bytes_per_row + destx), 273 (void *)(sConsole.frame_buffer + (srcy + y) * sConsole.bytes_per_row 274 + srcx), width); 275 } 276 } 277 278 279 static void 280 console_clear(uint8 attr) 281 { 282 if (!frame_buffer_console_available()) 283 return; 284 285 switch (sConsole.bytes_per_pixel) { 286 case 1: 287 if (sConsole.depth >= 8) { 288 memset((void *)sConsole.frame_buffer, 289 sPalette8[background_color(attr)], 290 sConsole.height * sConsole.bytes_per_row); 291 } else { 292 // special case for VGA mode 293 memset((void *)sConsole.frame_buffer, 0xff, 294 sConsole.height * sConsole.bytes_per_row); 295 } 296 break; 297 default: 298 { 299 uint8 *base = (uint8 *)sConsole.frame_buffer; 300 uint8 *color = get_palette_entry(background_color(attr)); 301 302 for (int32 y = 0; y < sConsole.height; y++) { 303 for (int32 x = 0; x < sConsole.width; x++) { 304 for (int32 i = 0; i < sConsole.bytes_per_pixel; i++) { 305 base[x * sConsole.bytes_per_pixel + i] = color[i]; 306 } 307 } 308 base += sConsole.bytes_per_row; 309 } 310 break; 311 } 312 } 313 314 sConsole.cursor_x = -1; 315 sConsole.cursor_y = -1; 316 } 317 318 319 static status_t 320 console_std_ops(int32 op, ...) 321 { 322 switch (op) { 323 case B_MODULE_INIT: 324 return frame_buffer_console_available() ? B_OK : B_ERROR; 325 case B_MODULE_UNINIT: 326 return B_OK; 327 328 default: 329 return B_ERROR; 330 } 331 } 332 333 334 console_module_info gFrameBufferConsoleModule = { 335 { 336 FRAME_BUFFER_CONSOLE_MODULE_NAME, 337 0, 338 console_std_ops 339 }, 340 &console_get_size, 341 &console_move_cursor, 342 &console_put_glyph, 343 &console_fill_glyph, 344 &console_blit, 345 &console_clear, 346 }; 347 348 349 static status_t 350 frame_buffer_update(addr_t baseAddress, int32 width, int32 height, int32 depth, int32 bytesPerRow) 351 { 352 TRACE(("frame_buffer_update(buffer = %p, width = %ld, height = %ld, depth = %ld, bytesPerRow = %ld)\n", 353 (void *)baseAddress, width, height, depth, bytesPerRow)); 354 355 mutex_lock(&sConsole.lock); 356 357 sConsole.frame_buffer = baseAddress; 358 sConsole.width = width; 359 sConsole.height = height; 360 sConsole.depth = depth; 361 sConsole.bytes_per_pixel = (depth + 7) / 8; 362 sConsole.bytes_per_row = bytesPerRow; 363 sConsole.columns = sConsole.width / CHAR_WIDTH; 364 sConsole.rows = sConsole.height / CHAR_HEIGHT; 365 366 // initially, the cursor is hidden 367 sConsole.cursor_x = -1; 368 sConsole.cursor_y = -1; 369 370 TRACE(("framebuffer mapped at %p, %ld columns, %ld rows\n", 371 (void *)sConsole.frame_buffer, sConsole.columns, sConsole.rows)); 372 373 mutex_unlock(&sConsole.lock); 374 return B_OK; 375 } 376 377 378 // #pragma mark - 379 380 381 bool 382 frame_buffer_console_available(void) 383 { 384 return sConsole.frame_buffer != NULL; 385 } 386 387 388 status_t 389 frame_buffer_console_init(kernel_args *args) 390 { 391 if (!args->frame_buffer.enabled) 392 return B_OK; 393 394 void *frameBuffer; 395 sConsole.area = map_physical_memory("vesa_fb", 396 (void *)args->frame_buffer.physical_buffer.start, 397 args->frame_buffer.physical_buffer.size, B_ANY_KERNEL_ADDRESS, 398 B_READ_AREA | B_WRITE_AREA | B_USER_CLONEABLE_AREA, &frameBuffer); 399 if (sConsole.area < B_OK) 400 return sConsole.area; 401 402 if (args->frame_buffer.depth == 4) { 403 // VGA mode will be treated as monochrome 404 args->frame_buffer.bytes_per_row /= 8; 405 } 406 407 frame_buffer_update((addr_t)frameBuffer, args->frame_buffer.width, 408 args->frame_buffer.height, args->frame_buffer.depth, 409 args->frame_buffer.bytes_per_row); 410 411 sBootInfo.frame_buffer = (addr_t)frameBuffer; 412 sBootInfo.width = args->frame_buffer.width; 413 sBootInfo.height = args->frame_buffer.height; 414 sBootInfo.depth = args->frame_buffer.depth; 415 sBootInfo.bytes_per_row = args->frame_buffer.bytes_per_row; 416 add_boot_item(FRAME_BUFFER_BOOT_INFO, &sBootInfo, 417 sizeof(frame_buffer_boot_info)); 418 419 return B_OK; 420 } 421 422 423 status_t 424 frame_buffer_console_init_post_modules(kernel_args *args) 425 { 426 mutex_init(&sConsole.lock, "console_lock"); 427 428 // TODO: enable MTRR in VESA mode! 429 if (sConsole.frame_buffer == NULL) 430 return B_OK; 431 432 // try to set frame buffer memory to write combined 433 434 return vm_set_area_memory_type(sConsole.area, 435 args->frame_buffer.physical_buffer.start, B_MTR_WC); 436 } 437 438 439 // #pragma mark - 440 441 442 status_t 443 _user_frame_buffer_update(addr_t baseAddress, int32 width, int32 height, 444 int32 depth, int32 bytesPerRow) 445 { 446 debug_stop_screen_debug_output(); 447 448 if (geteuid() != 0) 449 return B_NOT_ALLOWED; 450 if (IS_USER_ADDRESS(baseAddress) && baseAddress != NULL) 451 return B_BAD_ADDRESS; 452 453 return frame_buffer_update(baseAddress, width, height, depth, bytesPerRow); 454 } 455 456