xref: /haiku/src/system/kernel/debug/frame_buffer_console.cpp (revision 020cbad9d40235a2c50a81a42d69912a5ff8fbc4)
1 /*
2  * Copyright 2005-2007, 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,
226 	uint8 attr)
227 {
228 	if (x >= sConsole.columns || y >= sConsole.rows
229 		|| !frame_buffer_console_available())
230 		return;
231 
232 	int32 left = x + width;
233 	if (left > sConsole.columns)
234 		left = sConsole.columns;
235 
236 	int32 bottom = y + height;
237 	if (bottom > sConsole.rows)
238 		bottom = sConsole.rows;
239 
240 	for (; y < bottom; y++) {
241 		for (int32 x2 = x; x2 < left; x2++) {
242 			render_glyph(x2, y, glyph, attr);
243 		}
244 	}
245 }
246 
247 
248 static void
249 console_blit(int32 srcx, int32 srcy, int32 width, int32 height, int32 destx,
250 	int32 desty)
251 {
252 	if (!frame_buffer_console_available())
253 		return;
254 
255 	height *= CHAR_HEIGHT;
256 	srcy *= CHAR_HEIGHT;
257 	desty *= CHAR_HEIGHT;
258 
259 	if (sConsole.depth >= 8) {
260 		width *= CHAR_WIDTH * sConsole.bytes_per_pixel;
261 		srcx *= CHAR_WIDTH * sConsole.bytes_per_pixel;
262 		destx *= CHAR_WIDTH * sConsole.bytes_per_pixel;
263 	} else {
264 		// monochrome mode
265 		width = width * CHAR_WIDTH / 8;
266 		srcx = srcx * CHAR_WIDTH / 8;
267 		destx = destx * CHAR_WIDTH / 8;
268 	}
269 
270 	for (int32 y = 0; y < height; y++) {
271 		memmove((void *)(sConsole.frame_buffer + (desty + y)
272 				* sConsole.bytes_per_row + destx),
273 			(void *)(sConsole.frame_buffer + (srcy + y) * sConsole.bytes_per_row
274 				+ srcx), width);
275 	}
276 }
277 
278 
279 static void
280 console_clear(uint8 attr)
281 {
282 	if (!frame_buffer_console_available())
283 		return;
284 
285 	switch (sConsole.bytes_per_pixel) {
286 		case 1:
287 			if (sConsole.depth >= 8) {
288 				memset((void *)sConsole.frame_buffer,
289 					sPalette8[background_color(attr)],
290 					sConsole.height * sConsole.bytes_per_row);
291 			} else {
292 				// special case for VGA mode
293 				memset((void *)sConsole.frame_buffer, 0xff,
294 					sConsole.height * sConsole.bytes_per_row);
295 			}
296 			break;
297 		default:
298 		{
299 			uint8 *base = (uint8 *)sConsole.frame_buffer;
300 			uint8 *color = get_palette_entry(background_color(attr));
301 
302 			for (int32 y = 0; y < sConsole.height; y++) {
303 				for (int32 x = 0; x < sConsole.width; x++) {
304 					for (int32 i = 0; i < sConsole.bytes_per_pixel; i++) {
305 						base[x * sConsole.bytes_per_pixel + i] = color[i];
306 					}
307 				}
308 				base += sConsole.bytes_per_row;
309 			}
310 			break;
311 		}
312 	}
313 
314 	sConsole.cursor_x = -1;
315 	sConsole.cursor_y = -1;
316 }
317 
318 
319 static status_t
320 console_std_ops(int32 op, ...)
321 {
322 	switch (op) {
323 		case B_MODULE_INIT:
324 			return frame_buffer_console_available() ? B_OK : B_ERROR;
325 		case B_MODULE_UNINIT:
326 			return B_OK;
327 
328 		default:
329 			return B_ERROR;
330 	}
331 }
332 
333 
334 console_module_info gFrameBufferConsoleModule = {
335 	{
336 		FRAME_BUFFER_CONSOLE_MODULE_NAME,
337 		0,
338 		console_std_ops
339 	},
340 	&console_get_size,
341 	&console_move_cursor,
342 	&console_put_glyph,
343 	&console_fill_glyph,
344 	&console_blit,
345 	&console_clear,
346 };
347 
348 
349 static status_t
350 frame_buffer_update(addr_t baseAddress, int32 width, int32 height, int32 depth, int32 bytesPerRow)
351 {
352 	TRACE(("frame_buffer_update(buffer = %p, width = %ld, height = %ld, depth = %ld, bytesPerRow = %ld)\n",
353 		(void *)baseAddress, width, height, depth, bytesPerRow));
354 
355 	mutex_lock(&sConsole.lock);
356 
357 	sConsole.frame_buffer = baseAddress;
358 	sConsole.width = width;
359 	sConsole.height = height;
360 	sConsole.depth = depth;
361 	sConsole.bytes_per_pixel = (depth + 7) / 8;
362 	sConsole.bytes_per_row = bytesPerRow;
363 	sConsole.columns = sConsole.width / CHAR_WIDTH;
364 	sConsole.rows = sConsole.height / CHAR_HEIGHT;
365 
366 	// initially, the cursor is hidden
367 	sConsole.cursor_x = -1;
368 	sConsole.cursor_y = -1;
369 
370 	TRACE(("framebuffer mapped at %p, %ld columns, %ld rows\n",
371 		(void *)sConsole.frame_buffer, sConsole.columns, sConsole.rows));
372 
373 	mutex_unlock(&sConsole.lock);
374 	return B_OK;
375 }
376 
377 
378 //	#pragma mark -
379 
380 
381 bool
382 frame_buffer_console_available(void)
383 {
384 	return sConsole.frame_buffer != NULL;
385 }
386 
387 
388 status_t
389 frame_buffer_console_init(kernel_args *args)
390 {
391 	if (!args->frame_buffer.enabled)
392 		return B_OK;
393 
394 	void *frameBuffer;
395 	sConsole.area = map_physical_memory("vesa_fb",
396 		(void *)args->frame_buffer.physical_buffer.start,
397 		args->frame_buffer.physical_buffer.size, B_ANY_KERNEL_ADDRESS,
398 		B_READ_AREA | B_WRITE_AREA | B_USER_CLONEABLE_AREA, &frameBuffer);
399 	if (sConsole.area < B_OK)
400 		return sConsole.area;
401 
402 	if (args->frame_buffer.depth == 4) {
403 		// VGA mode will be treated as monochrome
404 		args->frame_buffer.bytes_per_row /= 8;
405 	}
406 
407 	frame_buffer_update((addr_t)frameBuffer, args->frame_buffer.width,
408 		args->frame_buffer.height, args->frame_buffer.depth,
409 		args->frame_buffer.bytes_per_row);
410 
411 	sBootInfo.frame_buffer = (addr_t)frameBuffer;
412 	sBootInfo.width = args->frame_buffer.width;
413 	sBootInfo.height = args->frame_buffer.height;
414 	sBootInfo.depth = args->frame_buffer.depth;
415 	sBootInfo.bytes_per_row = args->frame_buffer.bytes_per_row;
416 	add_boot_item(FRAME_BUFFER_BOOT_INFO, &sBootInfo,
417 		sizeof(frame_buffer_boot_info));
418 
419 	return B_OK;
420 }
421 
422 
423 status_t
424 frame_buffer_console_init_post_modules(kernel_args *args)
425 {
426 	mutex_init(&sConsole.lock, "console_lock");
427 
428 	// TODO: enable MTRR in VESA mode!
429 	if (sConsole.frame_buffer == NULL)
430 		return B_OK;
431 
432 	// try to set frame buffer memory to write combined
433 
434 	return vm_set_area_memory_type(sConsole.area,
435 		args->frame_buffer.physical_buffer.start, B_MTR_WC);
436 }
437 
438 
439 //	#pragma mark -
440 
441 
442 status_t
443 _user_frame_buffer_update(addr_t baseAddress, int32 width, int32 height,
444 	int32 depth, int32 bytesPerRow)
445 {
446 	debug_stop_screen_debug_output();
447 
448 	if (geteuid() != 0)
449 		return B_NOT_ALLOWED;
450 	if (IS_USER_ADDRESS(baseAddress) && baseAddress != NULL)
451 		return B_BAD_ADDRESS;
452 
453 	return frame_buffer_update(baseAddress, width, height, depth, bytesPerRow);
454 }
455 
456