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