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