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