xref: /haiku/src/system/kernel/debug/frame_buffer_console.cpp (revision 9a6a20d4689307142a7ed26a1437ba47e244e73f)
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
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 		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
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
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
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
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
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
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
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
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
407 frame_buffer_console_available(void)
408 {
409 	return sConsole.frame_buffer != 0;
410 }
411 
412 
413 status_t
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
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 	void* frameBuffer;
459 	sConsole.area = map_physical_memory("vesa frame buffer",
460 		args->frame_buffer.physical_buffer.start,
461 		args->frame_buffer.physical_buffer.size, B_ANY_KERNEL_ADDRESS,
462 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA,
463 		&frameBuffer);
464 	if (sConsole.area < 0)
465 		return sConsole.area;
466 
467 	frame_buffer_update((addr_t)frameBuffer, args->frame_buffer.width,
468 		args->frame_buffer.height, args->frame_buffer.depth,
469 		args->frame_buffer.bytes_per_row);
470 
471 	sBootInfo.area = sConsole.area;
472 	sBootInfo.physical_frame_buffer = args->frame_buffer.physical_buffer.start;
473 	sBootInfo.frame_buffer = (addr_t)frameBuffer;
474 	sBootInfo.width = args->frame_buffer.width;
475 	sBootInfo.height = args->frame_buffer.height;
476 	sBootInfo.depth = args->frame_buffer.depth;
477 	sBootInfo.bytes_per_row = args->frame_buffer.bytes_per_row;
478 	sBootInfo.vesa_capabilities = args->vesa_capabilities;
479 
480 	add_boot_item(FRAME_BUFFER_BOOT_INFO, &sBootInfo,
481 		sizeof(frame_buffer_boot_info));
482 
483 	sVesaModes = (vesa_mode*)malloc(args->vesa_modes_size);
484 	if (sVesaModes != NULL && args->vesa_modes_size > 0) {
485 		memcpy(sVesaModes, args->vesa_modes, args->vesa_modes_size);
486 		add_boot_item(VESA_MODES_BOOT_INFO, sVesaModes, args->vesa_modes_size);
487 	}
488 
489 	if (args->edid_info != NULL) {
490 		edid1_info* info = (edid1_info*)malloc(sizeof(edid1_info));
491 		if (info != NULL) {
492 			memcpy(info, args->edid_info, sizeof(edid1_info));
493 			add_boot_item(VESA_EDID_BOOT_INFO, info, sizeof(edid1_info));
494 		}
495 	}
496 
497 	return B_OK;
498 }
499 
500 
501 status_t
502 frame_buffer_console_init_post_modules(kernel_args* args)
503 {
504 	if (sConsole.frame_buffer == 0)
505 		return B_OK;
506 
507 	// try to set frame buffer memory to write combined
508 
509 	return vm_set_area_memory_type(sConsole.area,
510 		args->frame_buffer.physical_buffer.start, B_WRITE_COMBINING_MEMORY);
511 }
512 
513 
514 //	#pragma mark -
515 
516 
517 status_t
518 _user_frame_buffer_update(addr_t baseAddress, int32 width, int32 height,
519 	int32 depth, int32 bytesPerRow)
520 {
521 	debug_stop_screen_debug_output();
522 
523 	if (geteuid() != 0)
524 		return B_NOT_ALLOWED;
525 	if (IS_USER_ADDRESS(baseAddress) && baseAddress != 0)
526 		return B_BAD_ADDRESS;
527 
528 	return frame_buffer_update(baseAddress, width, height, depth, bytesPerRow);
529 }
530 #endif
531 
532