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