xref: /haiku/src/system/kernel/debug/frame_buffer_console.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
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 
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
102 foreground_color(uint8 attr)
103 {
104 	return attr & 0x7;
105 }
106 
107 
108 static inline uint8
109 background_color(uint8 attr)
110 {
111 	return (attr >> 4) & 0x7;
112 }
113 
114 
115 static uint8*
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
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
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 		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 
212 static void
213 draw_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 
240 static status_t
241 console_get_size(int32* _width, int32* _height)
242 {
243 	*_width = sConsole.columns;
244 	*_height = sConsole.rows;
245 
246 	return B_OK;
247 }
248 
249 
250 static void
251 console_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 
264 static void
265 console_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 
275 static void
276 console_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 
299 static void
300 console_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 
332 static void
333 console_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 
374 static status_t
375 console_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 
389 console_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 
407 bool
408 frame_buffer_console_available(void)
409 {
410 	return sConsole.frame_buffer != 0;
411 }
412 
413 
414 status_t
415 frame_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 = &spleen12Font;
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
451 status_t
452 frame_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 
502 status_t
503 frame_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 
518 status_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