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