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
foreground_color(uint8 attr)102 foreground_color(uint8 attr)
103 {
104 return attr & 0x7;
105 }
106
107
108 static inline uint8
background_color(uint8 attr)109 background_color(uint8 attr)
110 {
111 return (attr >> 4) & 0x7;
112 }
113
114
115 static uint8*
get_palette_entry(uint8 index)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
get_font_data(uint8 glyph,int y)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
render_glyph(int32 column,int32 row,uint8 glyph,uint8 attr)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
draw_cursor(int32 x,int32 y)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
console_get_size(int32 * _width,int32 * _height)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
console_move_cursor(int32 x,int32 y)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
console_put_glyph(int32 x,int32 y,uint8 glyph,uint8 attr)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
console_fill_glyph(int32 x,int32 y,int32 width,int32 height,uint8 glyph,uint8 attr)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
console_blit(int32 srcx,int32 srcy,int32 width,int32 height,int32 destx,int32 desty)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
console_clear(uint8 attr)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
console_std_ops(int32 op,...)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
frame_buffer_console_available(void)407 frame_buffer_console_available(void)
408 {
409 return sConsole.frame_buffer != 0;
410 }
411
412
413 status_t
frame_buffer_update(addr_t baseAddress,int32 width,int32 height,int32 depth,int32 bytesPerRow)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
frame_buffer_console_init(kernel_args * args)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 #if KERNEL_PMAP_BASE
459 const addr_t frameBuffer = (KERNEL_PMAP_BASE
460 + args->frame_buffer.physical_buffer.start);
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 return B_OK;
465 #else
466 return B_NO_INIT;
467 #endif
468 }
469
470
471 status_t
frame_buffer_console_init_post_vm(kernel_args * args)472 frame_buffer_console_init_post_vm(kernel_args* args)
473 {
474 if (!args->frame_buffer.enabled)
475 return B_OK;
476
477 void* frameBuffer;
478 sConsole.area = map_physical_memory("frame buffer",
479 args->frame_buffer.physical_buffer.start,
480 args->frame_buffer.physical_buffer.size, B_ANY_KERNEL_ADDRESS,
481 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA,
482 &frameBuffer);
483 if (sConsole.area < 0)
484 return sConsole.area;
485
486 frame_buffer_update((addr_t)frameBuffer, args->frame_buffer.width,
487 args->frame_buffer.height, args->frame_buffer.depth,
488 args->frame_buffer.bytes_per_row);
489
490 sBootInfo.area = sConsole.area;
491 sBootInfo.physical_frame_buffer = args->frame_buffer.physical_buffer.start;
492 sBootInfo.frame_buffer = (addr_t)frameBuffer;
493 sBootInfo.width = args->frame_buffer.width;
494 sBootInfo.height = args->frame_buffer.height;
495 sBootInfo.depth = args->frame_buffer.depth;
496 sBootInfo.bytes_per_row = args->frame_buffer.bytes_per_row;
497 sBootInfo.vesa_capabilities = args->vesa_capabilities;
498
499 add_boot_item(FRAME_BUFFER_BOOT_INFO, &sBootInfo,
500 sizeof(frame_buffer_boot_info));
501
502 sVesaModes = (vesa_mode*)malloc(args->vesa_modes_size);
503 if (sVesaModes != NULL && args->vesa_modes_size > 0) {
504 memcpy(sVesaModes, args->vesa_modes, args->vesa_modes_size);
505 add_boot_item(VESA_MODES_BOOT_INFO, sVesaModes, args->vesa_modes_size);
506 }
507
508 if (args->edid_info != NULL) {
509 edid1_info* info = (edid1_info*)malloc(sizeof(edid1_info));
510 if (info != NULL) {
511 memcpy(info, args->edid_info, sizeof(edid1_info));
512 add_boot_item(VESA_EDID_BOOT_INFO, info, sizeof(edid1_info));
513 }
514 }
515
516 return B_OK;
517 }
518
519
520 status_t
frame_buffer_console_init_post_modules(kernel_args * args)521 frame_buffer_console_init_post_modules(kernel_args* args)
522 {
523 if (sConsole.frame_buffer == 0)
524 return B_OK;
525
526 // try to set frame buffer memory to write combined
527
528 return vm_set_area_memory_type(sConsole.area,
529 args->frame_buffer.physical_buffer.start, B_WRITE_COMBINING_MEMORY);
530 }
531
532
533 // #pragma mark -
534
535
536 status_t
_user_frame_buffer_update(addr_t baseAddress,int32 width,int32 height,int32 depth,int32 bytesPerRow)537 _user_frame_buffer_update(addr_t baseAddress, int32 width, int32 height,
538 int32 depth, int32 bytesPerRow)
539 {
540 debug_stop_screen_debug_output();
541
542 if (geteuid() != 0)
543 return B_NOT_ALLOWED;
544 if (IS_USER_ADDRESS(baseAddress) && baseAddress != 0)
545 return B_BAD_ADDRESS;
546
547 return frame_buffer_update(baseAddress, width, height, depth, bytesPerRow);
548 }
549 #endif
550
551