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