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