1 /*
2  * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 /** @file
27  *
28  * Frame buffer console
29  *
30  */
31 
32 #include <string.h>
33 #include <errno.h>
34 #include <assert.h>
35 #include <byteswap.h>
36 #include <ipxe/ansiesc.h>
37 #include <ipxe/image.h>
38 #include <ipxe/pixbuf.h>
39 #include <ipxe/umalloc.h>
40 #include <ipxe/console.h>
41 #include <ipxe/fbcon.h>
42 
43 /**
44  * Calculate raw colour value
45  *
46  * @v fbcon		Frame buffer console
47  * @v rgb		24-bit RGB value
48  * @ret raw		Raw colour
49  */
fbcon_colour(struct fbcon * fbcon,uint32_t rgb)50 static uint32_t fbcon_colour ( struct fbcon *fbcon, uint32_t rgb ) {
51 	struct fbcon_colour_map *map = fbcon->map;
52 	uint8_t red = ( rgb >> 16 );
53 	uint8_t green = ( rgb >> 8 );
54 	uint8_t blue = ( rgb >> 0 );
55 	uint32_t mapped;
56 
57 	mapped = ( ( ( red >> map->red_scale ) << map->red_lsb ) |
58 		   ( ( green >> map->green_scale ) << map->green_lsb ) |
59 		   ( ( blue >> map->blue_scale ) << map->blue_lsb ) );
60 	return cpu_to_le32 ( mapped );
61 }
62 
63 /**
64  * Calculate ANSI font colour
65  *
66  * @v fbcon		Frame buffer console
67  * @v ansicol		ANSI colour value (0-based)
68  * @ret colour		Raw colour
69  */
fbcon_ansi_colour(struct fbcon * fbcon,unsigned int ansicol)70 static uint32_t fbcon_ansi_colour ( struct fbcon *fbcon,
71 				    unsigned int ansicol ) {
72 	uint32_t rgb;
73 
74 	/* Treat ansicol as 3-bit BGR with intensity 0xaa */
75 	rgb = ( ( ( ansicol & ( 1 << 0 ) ) ? 0xaa0000 : 0 ) |
76 		( ( ansicol & ( 1 << 1 ) ) ? 0x00aa00 : 0 ) |
77 		( ( ansicol & ( 1 << 2 ) ) ? 0x0000aa : 0 ) );
78 
79 	return fbcon_colour ( fbcon, rgb );
80 }
81 
82 /**
83  * Set default foreground colour
84  *
85  * @v fbcon		Frame buffer console
86  */
fbcon_set_default_foreground(struct fbcon * fbcon)87 static void fbcon_set_default_foreground ( struct fbcon *fbcon ) {
88 
89 	/* Default to non-bold white foreground */
90 	fbcon->foreground = fbcon_ansi_colour ( fbcon, 0x7 );
91 	fbcon->bold = 0;
92 }
93 
94 /**
95  * Set default background colour
96  *
97  * @v fbcon		Frame buffer console
98  */
fbcon_set_default_background(struct fbcon * fbcon)99 static void fbcon_set_default_background ( struct fbcon *fbcon ) {
100 
101 	/* Default to transparent background */
102 	fbcon->background = FBCON_TRANSPARENT;
103 }
104 
105 /**
106  * Clear rows of characters
107  *
108  * @v fbcon		Frame buffer console
109  * @v ypos		Starting Y position
110  */
fbcon_clear(struct fbcon * fbcon,unsigned int ypos)111 static void fbcon_clear ( struct fbcon *fbcon, unsigned int ypos ) {
112 	struct fbcon_text_cell cell = {
113 		.foreground = fbcon->foreground,
114 		.background = fbcon->background,
115 		.character = ' ',
116 	};
117 	size_t offset;
118 	unsigned int xpos;
119 
120 	/* Clear stored character array */
121 	for ( ; ypos < fbcon->character.height ; ypos++ ) {
122 		offset = ( ypos * fbcon->character.width * sizeof ( cell ) );
123 		for ( xpos = 0 ; xpos < fbcon->character.width ; xpos++ ) {
124 			copy_to_user ( fbcon->text.start, offset, &cell,
125 				       sizeof ( cell ) );
126 			offset += sizeof ( cell );
127 		}
128 	}
129 }
130 
131 /**
132  * Store character at specified position
133  *
134  * @v fbcon		Frame buffer console
135  * @v cell		Text cell
136  * @v xpos		X position
137  * @v ypos		Y position
138  */
fbcon_store(struct fbcon * fbcon,struct fbcon_text_cell * cell,unsigned int xpos,unsigned int ypos)139 static void fbcon_store ( struct fbcon *fbcon, struct fbcon_text_cell *cell,
140 			  unsigned int xpos, unsigned int ypos ) {
141 	size_t offset;
142 
143 	/* Store cell */
144 	offset = ( ( ( ypos * fbcon->character.width ) + xpos ) *
145 		   sizeof ( *cell ) );
146 	copy_to_user ( fbcon->text.start, offset, cell, sizeof ( *cell ) );
147 }
148 
149 /**
150  * Draw character at specified position
151  *
152  * @v fbcon		Frame buffer console
153  * @v cell		Text cell
154  * @v xpos		X position
155  * @v ypos		Y position
156  */
fbcon_draw(struct fbcon * fbcon,struct fbcon_text_cell * cell,unsigned int xpos,unsigned int ypos)157 static void fbcon_draw ( struct fbcon *fbcon, struct fbcon_text_cell *cell,
158 			 unsigned int xpos, unsigned int ypos ) {
159 	uint8_t glyph[fbcon->font->height];
160 	size_t offset;
161 	size_t pixel_len;
162 	size_t skip_len;
163 	unsigned int row;
164 	unsigned int column;
165 	uint8_t bitmask;
166 	int transparent;
167 	void *src;
168 
169 	/* Get font character */
170 	fbcon->font->glyph ( cell->character, glyph );
171 
172 	/* Calculate pixel geometry */
173 	offset = ( fbcon->indent +
174 		   ( ypos * fbcon->character.stride ) +
175 		   ( xpos * fbcon->character.len ) );
176 	pixel_len = fbcon->pixel->len;
177 	skip_len = ( fbcon->pixel->stride - fbcon->character.len );
178 
179 	/* Check for transparent background colour */
180 	transparent = ( cell->background == FBCON_TRANSPARENT );
181 
182 	/* Draw character rows */
183 	for ( row = 0 ; row < fbcon->font->height ; row++ ) {
184 
185 		/* Draw background picture, if applicable */
186 		if ( transparent ) {
187 			if ( fbcon->picture.start ) {
188 				memcpy_user ( fbcon->start, offset,
189 					      fbcon->picture.start, offset,
190 					      fbcon->character.len );
191 			} else {
192 				memset_user ( fbcon->start, offset, 0,
193 					      fbcon->character.len );
194 			}
195 		}
196 
197 		/* Draw character row */
198 		for ( column = FBCON_CHAR_WIDTH, bitmask = glyph[row] ;
199 		      column ; column--, bitmask <<= 1, offset += pixel_len ) {
200 			if ( bitmask & 0x80 ) {
201 				src = &cell->foreground;
202 			} else if ( ! transparent ) {
203 				src = &cell->background;
204 			} else {
205 				continue;
206 			}
207 			copy_to_user ( fbcon->start, offset, src, pixel_len );
208 		}
209 
210 		/* Move to next row */
211 		offset += skip_len;
212 	}
213 }
214 
215 /**
216  * Redraw all characters
217  *
218  * @v fbcon		Frame buffer console
219  */
fbcon_redraw(struct fbcon * fbcon)220 static void fbcon_redraw ( struct fbcon *fbcon ) {
221 	struct fbcon_text_cell cell;
222 	size_t offset = 0;
223 	unsigned int xpos;
224 	unsigned int ypos;
225 
226 	/* Redraw characters */
227 	for ( ypos = 0 ; ypos < fbcon->character.height ; ypos++ ) {
228 		for ( xpos = 0 ; xpos < fbcon->character.width ; xpos++ ) {
229 			copy_from_user ( &cell, fbcon->text.start, offset,
230 					 sizeof ( cell ) );
231 			fbcon_draw ( fbcon, &cell, xpos, ypos );
232 			offset += sizeof ( cell );
233 		}
234 	}
235 }
236 
237 /**
238  * Scroll screen
239  *
240  * @v fbcon		Frame buffer console
241  */
fbcon_scroll(struct fbcon * fbcon)242 static void fbcon_scroll ( struct fbcon *fbcon ) {
243 	size_t row_len;
244 
245 	/* Sanity check */
246 	assert ( fbcon->ypos == fbcon->character.height );
247 
248 	/* Scroll up character array */
249 	row_len = ( fbcon->character.width * sizeof ( struct fbcon_text_cell ));
250 	memmove_user ( fbcon->text.start, 0, fbcon->text.start, row_len,
251 		       ( row_len * ( fbcon->character.height - 1 ) ) );
252 	fbcon_clear ( fbcon, ( fbcon->character.height - 1 ) );
253 
254 	/* Update cursor position */
255 	fbcon->ypos--;
256 
257 	/* Redraw all characters */
258 	fbcon_redraw ( fbcon );
259 }
260 
261 /**
262  * Draw character at cursor position
263  *
264  * @v fbcon		Frame buffer console
265  * @v show_cursor	Show cursor
266  */
fbcon_draw_cursor(struct fbcon * fbcon,int show_cursor)267 static void fbcon_draw_cursor ( struct fbcon *fbcon, int show_cursor ) {
268 	struct fbcon_text_cell cell;
269 	size_t offset;
270 
271 	offset = ( ( ( fbcon->ypos * fbcon->character.width ) + fbcon->xpos ) *
272 		   sizeof ( cell ) );
273 	copy_from_user ( &cell, fbcon->text.start, offset, sizeof ( cell ) );
274 	if ( show_cursor ) {
275 		cell.background = fbcon->foreground;
276 		cell.foreground = ( ( fbcon->background == FBCON_TRANSPARENT ) ?
277 				    0 : fbcon->background );
278 	}
279 	fbcon_draw ( fbcon, &cell, fbcon->xpos, fbcon->ypos );
280 }
281 
282 /**
283  * Handle ANSI CUP (cursor position)
284  *
285  * @v ctx		ANSI escape sequence context
286  * @v count		Parameter count
287  * @v params[0]		Row (1 is top)
288  * @v params[1]		Column (1 is left)
289  */
fbcon_handle_cup(struct ansiesc_context * ctx,unsigned int count __unused,int params[])290 static void fbcon_handle_cup ( struct ansiesc_context *ctx,
291 			       unsigned int count __unused, int params[] ) {
292 	struct fbcon *fbcon = container_of ( ctx, struct fbcon, ctx );
293 	int cx = ( params[1] - 1 );
294 	int cy = ( params[0] - 1 );
295 
296 	fbcon_draw_cursor ( fbcon, 0 );
297 	fbcon->xpos = cx;
298 	if ( fbcon->xpos >= fbcon->character.width )
299 		fbcon->xpos = 0;
300 	fbcon->ypos = cy;
301 	if ( fbcon->ypos >= fbcon->character.height )
302 		fbcon->ypos = 0;
303 	fbcon_draw_cursor ( fbcon, fbcon->show_cursor );
304 }
305 
306 /**
307  * Handle ANSI ED (erase in page)
308  *
309  * @v ctx		ANSI escape sequence context
310  * @v count		Parameter count
311  * @v params[0]		Region to erase
312  */
fbcon_handle_ed(struct ansiesc_context * ctx,unsigned int count __unused,int params[]__unused)313 static void fbcon_handle_ed ( struct ansiesc_context *ctx,
314 			      unsigned int count __unused,
315 			      int params[] __unused ) {
316 	struct fbcon *fbcon = container_of ( ctx, struct fbcon, ctx );
317 
318 	/* We assume that we always clear the whole screen */
319 	assert ( params[0] == ANSIESC_ED_ALL );
320 
321 	/* Clear character array */
322 	fbcon_clear ( fbcon, 0 );
323 
324 	/* Redraw all characters */
325 	fbcon_redraw ( fbcon );
326 
327 	/* Reset cursor position */
328 	fbcon->xpos = 0;
329 	fbcon->ypos = 0;
330 	fbcon_draw_cursor ( fbcon, fbcon->show_cursor );
331 }
332 
333 /**
334  * Handle ANSI SGR (set graphics rendition)
335  *
336  * @v ctx		ANSI escape sequence context
337  * @v count		Parameter count
338  * @v params		List of graphic rendition aspects
339  */
fbcon_handle_sgr(struct ansiesc_context * ctx,unsigned int count,int params[])340 static void fbcon_handle_sgr ( struct ansiesc_context *ctx, unsigned int count,
341 			       int params[] ) {
342 	struct fbcon *fbcon = container_of ( ctx, struct fbcon, ctx );
343 	uint32_t *custom = NULL;
344 	uint32_t rgb;
345 	unsigned int end;
346 	unsigned int i;
347 	int aspect;
348 
349 	for ( i = 0 ; i < count ; i++ ) {
350 
351 		/* Process aspect */
352 		aspect = params[i];
353 		if ( aspect == 0 ) {
354 			fbcon_set_default_foreground ( fbcon );
355 			fbcon_set_default_background ( fbcon );
356 		} else if ( aspect == 1 ) {
357 			fbcon->bold = fbcon_colour ( fbcon, FBCON_BOLD );
358 		} else if ( aspect == 22 ) {
359 			fbcon->bold = 0;
360 		} else if ( ( aspect >= 30 ) && ( aspect <= 37 ) ) {
361 			fbcon->foreground =
362 				fbcon_ansi_colour ( fbcon, aspect - 30 );
363 		} else if ( aspect == 38 ) {
364 			custom = &fbcon->foreground;
365 		} else if ( aspect == 39 ) {
366 			fbcon_set_default_foreground ( fbcon );
367 		} else if ( ( aspect >= 40 ) && ( aspect <= 47 ) ) {
368 			fbcon->background =
369 				fbcon_ansi_colour ( fbcon, aspect - 40 );
370 		} else if ( aspect == 48 ) {
371 			custom = &fbcon->background;
372 		} else if ( aspect == 49 ) {
373 			fbcon_set_default_background ( fbcon );
374 		}
375 
376 		/* Process custom RGB colour, if applicable
377 		 *
378 		 * We support the xterm-compatible
379 		 * "<ESC>[38;2;<red>;<green>;<blue>m" and
380 		 * "<ESC>[48;2;<red>;<green>;<blue>m" sequences.
381 		 */
382 		if ( custom ) {
383 			rgb = 0;
384 			end = ( i + 5 );
385 			for ( ; ( i < count ) && ( i < end ) ; i++ )
386 				rgb = ( ( rgb << 8 ) | params[i] );
387 			*custom = fbcon_colour ( fbcon, rgb );
388 			custom = NULL;
389 		}
390 	}
391 }
392 
393 /**
394  * Handle ANSI DECTCEM set (show cursor)
395  *
396  * @v ctx		ANSI escape sequence context
397  * @v count		Parameter count
398  * @v params		List of graphic rendition aspects
399  */
fbcon_handle_dectcem_set(struct ansiesc_context * ctx,unsigned int count __unused,int params[]__unused)400 static void fbcon_handle_dectcem_set ( struct ansiesc_context *ctx,
401 				       unsigned int count __unused,
402 				       int params[] __unused ) {
403 	struct fbcon *fbcon = container_of ( ctx, struct fbcon, ctx );
404 
405 	fbcon->show_cursor = 1;
406 	fbcon_draw_cursor ( fbcon, 1 );
407 }
408 
409 /**
410  * Handle ANSI DECTCEM reset (hide cursor)
411  *
412  * @v ctx		ANSI escape sequence context
413  * @v count		Parameter count
414  * @v params		List of graphic rendition aspects
415  */
fbcon_handle_dectcem_reset(struct ansiesc_context * ctx,unsigned int count __unused,int params[]__unused)416 static void fbcon_handle_dectcem_reset ( struct ansiesc_context *ctx,
417 					 unsigned int count __unused,
418 					 int params[] __unused ) {
419 	struct fbcon *fbcon = container_of ( ctx, struct fbcon, ctx );
420 
421 	fbcon->show_cursor = 0;
422 	fbcon_draw_cursor ( fbcon, 0 );
423 }
424 
425 /** ANSI escape sequence handlers */
426 static struct ansiesc_handler fbcon_ansiesc_handlers[] = {
427 	{ ANSIESC_CUP, fbcon_handle_cup },
428 	{ ANSIESC_ED, fbcon_handle_ed },
429 	{ ANSIESC_SGR, fbcon_handle_sgr },
430 	{ ANSIESC_DECTCEM_SET, fbcon_handle_dectcem_set },
431 	{ ANSIESC_DECTCEM_RESET, fbcon_handle_dectcem_reset },
432 	{ 0, NULL }
433 };
434 
435 /**
436  * Print a character to current cursor position
437  *
438  * @v fbcon		Frame buffer console
439  * @v character		Character
440  */
fbcon_putchar(struct fbcon * fbcon,int character)441 void fbcon_putchar ( struct fbcon *fbcon, int character ) {
442 	struct fbcon_text_cell cell;
443 
444 	/* Intercept ANSI escape sequences */
445 	character = ansiesc_process ( &fbcon->ctx, character );
446 	if ( character < 0 )
447 		return;
448 
449 	/* Handle control characters */
450 	switch ( character ) {
451 	case '\r':
452 		fbcon_draw_cursor ( fbcon, 0 );
453 		fbcon->xpos = 0;
454 		break;
455 	case '\n':
456 		fbcon_draw_cursor ( fbcon, 0 );
457 		fbcon->xpos = 0;
458 		fbcon->ypos++;
459 		break;
460 	case '\b':
461 		fbcon_draw_cursor ( fbcon, 0 );
462 		if ( fbcon->xpos ) {
463 			fbcon->xpos--;
464 		} else if ( fbcon->ypos ) {
465 			fbcon->xpos = ( fbcon->character.width - 1 );
466 			fbcon->ypos--;
467 		}
468 		break;
469 	default:
470 		/* Print character at current cursor position */
471 		cell.foreground = ( fbcon->foreground | fbcon->bold );
472 		cell.background = fbcon->background;
473 		cell.character = character;
474 		fbcon_store ( fbcon, &cell, fbcon->xpos, fbcon->ypos );
475 		fbcon_draw ( fbcon, &cell, fbcon->xpos, fbcon->ypos );
476 
477 		/* Advance cursor */
478 		fbcon->xpos++;
479 		if ( fbcon->xpos >= fbcon->character.width ) {
480 			fbcon->xpos = 0;
481 			fbcon->ypos++;
482 		}
483 		break;
484 	}
485 
486 	/* Scroll screen if necessary */
487 	if ( fbcon->ypos >= fbcon->character.height )
488 		fbcon_scroll ( fbcon );
489 
490 	/* Show cursor */
491 	fbcon_draw_cursor ( fbcon, fbcon->show_cursor );
492 }
493 
494 /**
495  * Initialise background picture
496  *
497  * @v fbcon		Frame buffer console
498  * @v pixbuf		Background picture
499  * @ret rc		Return status code
500  */
fbcon_picture_init(struct fbcon * fbcon,struct pixel_buffer * pixbuf)501 static int fbcon_picture_init ( struct fbcon *fbcon,
502 				struct pixel_buffer *pixbuf ) {
503 	struct fbcon_geometry *pixel = fbcon->pixel;
504 	struct fbcon_picture *picture = &fbcon->picture;
505 	size_t len;
506 	size_t pixbuf_stride;
507 	size_t indent;
508 	size_t pixbuf_indent;
509 	size_t offset;
510 	size_t pixbuf_offset;
511 	uint32_t rgb;
512 	uint32_t raw;
513 	unsigned int x;
514 	unsigned int y;
515 	unsigned int width;
516 	unsigned int height;
517 	int xgap;
518 	int ygap;
519 	int rc;
520 
521 	/* Allocate buffer */
522 	len = ( pixel->height * pixel->stride );
523 	picture->start = umalloc ( len );
524 	if ( ! picture->start ) {
525 		DBGC ( fbcon, "FBCON %p could not allocate %zd bytes for "
526 		       "picture\n", fbcon, len );
527 		rc = -ENOMEM;
528 		goto err_umalloc;
529 	}
530 
531 	/* Centre picture on console */
532 	pixbuf_stride = ( pixbuf->width * sizeof ( rgb ) );
533 	xgap = ( ( ( int ) ( pixel->width - pixbuf->width ) ) / 2 );
534 	ygap = ( ( ( int ) ( pixel->height - pixbuf->height ) ) / 2 );
535 	indent = ( ( ( ( ygap >= 0 ) ? ygap : 0 ) * pixel->stride ) +
536 		   ( ( ( xgap >= 0 ) ? xgap : 0 ) * pixel->len ) );
537 	pixbuf_indent =	( ( ( ( ygap < 0 ) ? -ygap : 0 ) * pixbuf_stride ) +
538 			  ( ( ( xgap < 0 ) ? -xgap : 0 ) * sizeof ( rgb ) ) );
539 	width = pixbuf->width;
540 	if ( width > pixel->width )
541 		width = pixel->width;
542 	height = pixbuf->height;
543 	if ( height > pixel->height )
544 		height = pixel->height;
545 	DBGC ( fbcon, "FBCON %p picture is pixel %dx%d at [%d,%d),[%d,%d)\n",
546 	       fbcon, width, height, xgap, ( xgap + pixbuf->width ), ygap,
547 	       ( ygap + pixbuf->height ) );
548 
549 	/* Convert to frame buffer raw format */
550 	memset_user ( picture->start, 0, 0, len );
551 	for ( y = 0 ; y < height ; y++ ) {
552 		offset = ( indent + ( y * pixel->stride ) );
553 		pixbuf_offset = ( pixbuf_indent + ( y * pixbuf_stride ) );
554 		for ( x = 0 ; x < width ; x++ ) {
555 			copy_from_user ( &rgb, pixbuf->data, pixbuf_offset,
556 					 sizeof ( rgb ) );
557 			raw = fbcon_colour ( fbcon, rgb );
558 			copy_to_user ( picture->start, offset, &raw,
559 				       pixel->len );
560 			offset += pixel->len;
561 			pixbuf_offset += sizeof ( rgb );
562 		}
563 	}
564 
565 	return 0;
566 
567 	ufree ( picture->start );
568  err_umalloc:
569 	return rc;
570 }
571 
572 /**
573  * Initialise frame buffer console
574  *
575  * @v fbcon		Frame buffer console
576  * @v start		Start address
577  * @v pixel		Pixel geometry
578  * @v map		Colour mapping
579  * @v font		Font definition
580  * @v config		Console configuration
581  * @ret rc		Return status code
582  */
fbcon_init(struct fbcon * fbcon,userptr_t start,struct fbcon_geometry * pixel,struct fbcon_colour_map * map,struct fbcon_font * font,struct console_configuration * config)583 int fbcon_init ( struct fbcon *fbcon, userptr_t start,
584 		 struct fbcon_geometry *pixel,
585 		 struct fbcon_colour_map *map,
586 		 struct fbcon_font *font,
587 		 struct console_configuration *config ) {
588 	int width;
589 	int height;
590 	unsigned int xgap;
591 	unsigned int ygap;
592 	unsigned int left;
593 	unsigned int right;
594 	unsigned int top;
595 	unsigned int bottom;
596 	int rc;
597 
598 	/* Initialise data structure */
599 	memset ( fbcon, 0, sizeof ( *fbcon ) );
600 	fbcon->start = start;
601 	fbcon->pixel = pixel;
602 	assert ( pixel->len <= sizeof ( uint32_t ) );
603 	fbcon->map = map;
604 	fbcon->font = font;
605 	fbcon->ctx.handlers = fbcon_ansiesc_handlers;
606 	fbcon->show_cursor = 1;
607 
608 	/* Derive overall length */
609 	fbcon->len = ( pixel->height * pixel->stride );
610 	DBGC ( fbcon, "FBCON %p at [%08lx,%08lx)\n", fbcon,
611 	       user_to_phys ( fbcon->start, 0 ),
612 	       user_to_phys ( fbcon->start, fbcon->len ) );
613 
614 	/* Calculate margin.  If the actual screen size is larger than
615 	 * the requested screen size, then update the margins so that
616 	 * the margin remains relative to the requested screen size.
617 	 * (As an exception, if a zero margin was specified then treat
618 	 * this as meaning "expand to edge of actual screen".)
619 	 */
620 	xgap = ( pixel->width - config->width );
621 	ygap = ( pixel->height - config->height );
622 	left = ( xgap / 2 );
623 	right = ( xgap - left );
624 	top = ( ygap / 2 );
625 	bottom = ( ygap - top );
626 	fbcon->margin.left = ( config->left + ( config->left ? left : 0 ) );
627 	fbcon->margin.right = ( config->right + ( config->right ? right : 0 ) );
628 	fbcon->margin.top = ( config->top + ( config->top ? top : 0 ) );
629 	fbcon->margin.bottom =
630 		( config->bottom + ( config->bottom ? bottom : 0 ) );
631 
632 	/* Expand margin to accommodate whole characters */
633 	width = ( pixel->width - fbcon->margin.left - fbcon->margin.right );
634 	height = ( pixel->height - fbcon->margin.top - fbcon->margin.bottom );
635 	if ( ( width < FBCON_CHAR_WIDTH ) ||
636 	     ( height < ( ( int ) font->height ) ) ) {
637 		DBGC ( fbcon, "FBCON %p has unusable character area "
638 		       "[%d-%d),[%d-%d)\n", fbcon, fbcon->margin.left,
639 		       ( pixel->width - fbcon->margin.right ),
640 		       fbcon->margin.top,
641 		       ( pixel->height - fbcon->margin.bottom ) );
642 		rc = -EINVAL;
643 		goto err_margin;
644 	}
645 	xgap = ( width % FBCON_CHAR_WIDTH );
646 	ygap = ( height % font->height );
647 	fbcon->margin.left += ( xgap / 2 );
648 	fbcon->margin.top += ( ygap / 2 );
649 	fbcon->margin.right += ( xgap - ( xgap / 2 ) );
650 	fbcon->margin.bottom += ( ygap - ( ygap / 2 ) );
651 	fbcon->indent = ( ( fbcon->margin.top * pixel->stride ) +
652 			  ( fbcon->margin.left * pixel->len ) );
653 
654 	/* Derive character geometry from pixel geometry */
655 	fbcon->character.width = ( width / FBCON_CHAR_WIDTH );
656 	fbcon->character.height = ( height / font->height );
657 	fbcon->character.len = ( pixel->len * FBCON_CHAR_WIDTH );
658 	fbcon->character.stride = ( pixel->stride * font->height );
659 	DBGC ( fbcon, "FBCON %p is pixel %dx%d, char %dx%d at "
660 	       "[%d-%d),[%d-%d)\n", fbcon, fbcon->pixel->width,
661 	       fbcon->pixel->height, fbcon->character.width,
662 	       fbcon->character.height, fbcon->margin.left,
663 	       ( fbcon->pixel->width - fbcon->margin.right ),
664 	       fbcon->margin.top,
665 	       ( fbcon->pixel->height - fbcon->margin.bottom ) );
666 
667 	/* Set default colours */
668 	fbcon_set_default_foreground ( fbcon );
669 	fbcon_set_default_background ( fbcon );
670 
671 	/* Allocate and initialise stored character array */
672 	fbcon->text.start = umalloc ( fbcon->character.width *
673 				      fbcon->character.height *
674 				      sizeof ( struct fbcon_text_cell ) );
675 	if ( ! fbcon->text.start ) {
676 		rc = -ENOMEM;
677 		goto err_text;
678 	}
679 	fbcon_clear ( fbcon, 0 );
680 
681 	/* Set framebuffer to all black (including margins) */
682 	memset_user ( fbcon->start, 0, 0, fbcon->len );
683 
684 	/* Generate pixel buffer from background image, if applicable */
685 	if ( config->pixbuf &&
686 	     ( ( rc = fbcon_picture_init ( fbcon, config->pixbuf ) ) != 0 ) )
687 		goto err_picture;
688 
689 	/* Draw background picture (including margins), if applicable */
690 	if ( fbcon->picture.start ) {
691 		memcpy_user ( fbcon->start, 0, fbcon->picture.start, 0,
692 			      fbcon->len );
693 	}
694 
695 	/* Update console width and height */
696 	console_set_size ( fbcon->character.width, fbcon->character.height );
697 
698 	return 0;
699 
700 	ufree ( fbcon->picture.start );
701  err_picture:
702 	ufree ( fbcon->text.start );
703  err_text:
704  err_margin:
705 	return rc;
706 }
707 
708 /**
709  * Finalise frame buffer console
710  *
711  * @v fbcon		Frame buffer console
712  */
fbcon_fini(struct fbcon * fbcon)713 void fbcon_fini ( struct fbcon *fbcon ) {
714 
715 	ufree ( fbcon->text.start );
716 	ufree ( fbcon->picture.start );
717 }
718