1 /*
2  * Copyright (C) 2015 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 /**
27  * @file
28  *
29  * EFI frame buffer console
30  *
31  */
32 
33 #include <string.h>
34 #include <strings.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <assert.h>
38 #include <limits.h>
39 #include <ipxe/efi/efi.h>
40 #include <ipxe/efi/Protocol/GraphicsOutput.h>
41 #include <ipxe/efi/Protocol/HiiFont.h>
42 #include <ipxe/ansicol.h>
43 #include <ipxe/fbcon.h>
44 #include <ipxe/console.h>
45 #include <ipxe/umalloc.h>
46 #include <ipxe/rotate.h>
47 #include <config/console.h>
48 
49 /* Avoid dragging in EFI console if not otherwise used */
50 extern struct console_driver efi_console;
51 struct console_driver efi_console __attribute__ (( weak ));
52 
53 /* Set default console usage if applicable
54  *
55  * We accept either CONSOLE_FRAMEBUFFER or CONSOLE_EFIFB.
56  */
57 #if ( defined ( CONSOLE_FRAMEBUFFER ) && ! defined ( CONSOLE_EFIFB ) )
58 #define CONSOLE_EFIFB CONSOLE_FRAMEBUFFER
59 #endif
60 #if ! ( defined ( CONSOLE_EFIFB ) && CONSOLE_EXPLICIT ( CONSOLE_EFIFB ) )
61 #undef CONSOLE_EFIFB
62 #define CONSOLE_EFIFB ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
63 #endif
64 
65 /* Forward declaration */
66 struct console_driver efifb_console __console_driver;
67 
68 /** An EFI frame buffer */
69 struct efifb {
70 	/** EFI graphics output protocol */
71 	EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
72 	/** EFI HII font protocol */
73 	EFI_HII_FONT_PROTOCOL *hiifont;
74 	/** Saved mode */
75 	UINT32 saved_mode;
76 
77 	/** Frame buffer console */
78 	struct fbcon fbcon;
79 	/** Physical start address */
80 	physaddr_t start;
81 	/** Pixel geometry */
82 	struct fbcon_geometry pixel;
83 	/** Colour mapping */
84 	struct fbcon_colour_map map;
85 	/** Font definition */
86 	struct fbcon_font font;
87 	/** Character glyphs */
88 	userptr_t glyphs;
89 };
90 
91 /** The EFI frame buffer */
92 static struct efifb efifb;
93 
94 /**
95  * Get character glyph
96  *
97  * @v character		Character
98  * @v glyph		Character glyph to fill in
99  */
efifb_glyph(unsigned int character,uint8_t * glyph)100 static void efifb_glyph ( unsigned int character, uint8_t *glyph ) {
101 	size_t offset = ( character * efifb.font.height );
102 
103 	copy_from_user ( glyph, efifb.glyphs, offset, efifb.font.height );
104 }
105 
106 /**
107  * Get character glyphs
108  *
109  * @ret rc		Return status code
110  */
efifb_glyphs(void)111 static int efifb_glyphs ( void ) {
112 	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
113 	EFI_IMAGE_OUTPUT *blt;
114 	EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixel;
115 	size_t offset;
116 	size_t len;
117 	uint8_t bitmask;
118 	unsigned int character;
119 	unsigned int x;
120 	unsigned int y;
121 	EFI_STATUS efirc;
122 	int rc;
123 
124 	/* Get font height.  The GetFontInfo() call nominally returns
125 	 * this information in an EFI_FONT_DISPLAY_INFO structure, but
126 	 * is known to fail on many UEFI implementations.  Instead, we
127 	 * iterate over all printable characters to find the maximum
128 	 * height.
129 	 */
130 	efifb.font.height = 0;
131 	for ( character = 0 ; character < 256 ; character++ ) {
132 
133 		/* Skip non-printable characters */
134 		if ( ! isprint ( character ) )
135 			continue;
136 
137 		/* Get glyph */
138 		blt = NULL;
139 		if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont,
140 							 character, NULL, &blt,
141 							 NULL ) ) != 0 ) {
142 			rc = -EEFI ( efirc );
143 			DBGC ( &efifb, "EFIFB could not get glyph %d: %s\n",
144 			       character, strerror ( rc ) );
145 			continue;
146 		}
147 		assert ( blt != NULL );
148 
149 		/* Calculate maximum height */
150 		if ( efifb.font.height < blt->Height )
151 			efifb.font.height = blt->Height;
152 
153 		/* Free glyph */
154 		bs->FreePool ( blt );
155 	}
156 	if ( ! efifb.font.height ) {
157 		DBGC ( &efifb, "EFIFB could not get font height\n" );
158 		return -ENOENT;
159 	}
160 
161 	/* Allocate glyph data */
162 	len = ( 256 * efifb.font.height * sizeof ( bitmask ) );
163 	efifb.glyphs = umalloc ( len );
164 	if ( ! efifb.glyphs ) {
165 		rc = -ENOMEM;
166 		goto err_alloc;
167 	}
168 	memset_user ( efifb.glyphs, 0, 0, len );
169 
170 	/* Get font data */
171 	for ( character = 0 ; character < 256 ; character++ ) {
172 
173 		/* Skip non-printable characters */
174 		if ( ! isprint ( character ) )
175 			continue;
176 
177 		/* Get glyph */
178 		blt = NULL;
179 		if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont,
180 							 character, NULL, &blt,
181 							 NULL ) ) != 0 ) {
182 			rc = -EEFI ( efirc );
183 			DBGC ( &efifb, "EFIFB could not get glyph %d: %s\n",
184 			       character, strerror ( rc ) );
185 			continue;
186 		}
187 		assert ( blt != NULL );
188 
189 		/* Sanity check */
190 		if ( blt->Width > 8 ) {
191 			DBGC ( &efifb, "EFIFB glyph %d invalid width %d\n",
192 			       character, blt->Width );
193 			continue;
194 		}
195 		if ( blt->Height > efifb.font.height ) {
196 			DBGC ( &efifb, "EFIFB glyph %d invalid height %d\n",
197 			       character, blt->Height );
198 			continue;
199 		}
200 
201 		/* Convert glyph to bitmap */
202 		pixel = blt->Image.Bitmap;
203 		offset = ( character * efifb.font.height );
204 		for ( y = 0 ; y < blt->Height ; y++ ) {
205 			bitmask = 0;
206 			for ( x = 0 ; x < blt->Width ; x++ ) {
207 				bitmask = rol8 ( bitmask, 1 );
208 				if ( pixel->Blue || pixel->Green || pixel->Red )
209 					bitmask |= 0x01;
210 				pixel++;
211 			}
212 			copy_to_user ( efifb.glyphs, offset++, &bitmask,
213 				       sizeof ( bitmask ) );
214 		}
215 
216 		/* Free glyph */
217 		bs->FreePool ( blt );
218 	}
219 
220 	efifb.font.glyph = efifb_glyph;
221 	return 0;
222 
223 	ufree ( efifb.glyphs );
224  err_alloc:
225 	return rc;
226 }
227 
228 /**
229  * Generate colour mapping for a single colour component
230  *
231  * @v mask		Mask value
232  * @v scale		Scale value to fill in
233  * @v lsb		LSB value to fill in
234  * @ret rc		Return status code
235  */
efifb_colour_map_mask(uint32_t mask,uint8_t * scale,uint8_t * lsb)236 static int efifb_colour_map_mask ( uint32_t mask, uint8_t *scale,
237 				   uint8_t *lsb ) {
238 	uint32_t check;
239 
240 	/* Fill in LSB and scale */
241 	*lsb = ( mask ? ( ffs ( mask ) - 1 ) : 0 );
242 	*scale = ( mask ? ( 8 - ( fls ( mask ) - *lsb ) ) : 8 );
243 
244 	/* Check that original mask was contiguous */
245 	check = ( ( 0xff >> *scale ) << *lsb );
246 	if ( check != mask )
247 		return -ENOTSUP;
248 
249 	return 0;
250 }
251 
252 /**
253  * Generate colour mapping
254  *
255  * @v info		EFI mode information
256  * @v map		Colour mapping to fill in
257  * @ret bpp		Number of bits per pixel, or negative error
258  */
efifb_colour_map(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION * info,struct fbcon_colour_map * map)259 static int efifb_colour_map ( EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info,
260 			      struct fbcon_colour_map *map ) {
261 	static EFI_PIXEL_BITMASK rgb_mask = {
262 		0x000000ffUL, 0x0000ff00UL, 0x00ff0000UL, 0xff000000UL
263 	};
264 	static EFI_PIXEL_BITMASK bgr_mask = {
265 		0x00ff0000UL, 0x0000ff00UL, 0x000000ffUL, 0xff000000UL
266 	};
267 	EFI_PIXEL_BITMASK *mask;
268 	uint8_t reserved_scale;
269 	uint8_t reserved_lsb;
270 	int rc;
271 
272 	/* Determine applicable mask */
273 	switch ( info->PixelFormat ) {
274 	case PixelRedGreenBlueReserved8BitPerColor:
275 		mask = &rgb_mask;
276 		break;
277 	case PixelBlueGreenRedReserved8BitPerColor:
278 		mask = &bgr_mask;
279 		break;
280 	case PixelBitMask:
281 		mask = &info->PixelInformation;
282 		break;
283 	default:
284 		DBGC ( &efifb, "EFIFB unrecognised pixel format %d\n",
285 		       info->PixelFormat );
286 		return -ENOTSUP;
287 	}
288 
289 	/* Map each colour component */
290 	if ( ( rc = efifb_colour_map_mask ( mask->RedMask, &map->red_scale,
291 					    &map->red_lsb ) ) != 0 )
292 		return rc;
293 	if ( ( rc = efifb_colour_map_mask ( mask->GreenMask, &map->green_scale,
294 					    &map->green_lsb ) ) != 0 )
295 		return rc;
296 	if ( ( rc = efifb_colour_map_mask ( mask->BlueMask, &map->blue_scale,
297 					    &map->blue_lsb ) ) != 0 )
298 		return rc;
299 	if ( ( rc = efifb_colour_map_mask ( mask->ReservedMask, &reserved_scale,
300 					    &reserved_lsb ) ) != 0 )
301 		return rc;
302 
303 	/* Calculate total number of bits per pixel */
304 	return ( 32 - ( reserved_scale + map->red_scale + map->green_scale +
305 			map->blue_scale ) );
306 }
307 
308 /**
309  * Select video mode
310  *
311  * @v min_width		Minimum required width (in pixels)
312  * @v min_height	Minimum required height (in pixels)
313  * @v min_bpp		Minimum required colour depth (in bits per pixel)
314  * @ret mode_number	Mode number, or negative error
315  */
efifb_select_mode(unsigned int min_width,unsigned int min_height,unsigned int min_bpp)316 static int efifb_select_mode ( unsigned int min_width, unsigned int min_height,
317 			       unsigned int min_bpp ) {
318 	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
319 	struct fbcon_colour_map map;
320 	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
321 	int best_mode_number = -ENOENT;
322 	unsigned int best_score = INT_MAX;
323 	unsigned int score;
324 	unsigned int mode;
325 	int bpp;
326 	UINTN size;
327 	EFI_STATUS efirc;
328 	int rc;
329 
330 	/* Find the best mode */
331 	for ( mode = 0 ; mode < efifb.gop->Mode->MaxMode ; mode++ ) {
332 
333 		/* Get mode information */
334 		if ( ( efirc = efifb.gop->QueryMode ( efifb.gop, mode, &size,
335 						      &info ) ) != 0 ) {
336 			rc = -EEFI ( efirc );
337 			DBGC ( &efifb, "EFIFB could not get mode %d "
338 			       "information: %s\n", mode, strerror ( rc ) );
339 			goto err_query;
340 		}
341 
342 		/* Skip unusable modes */
343 		bpp = efifb_colour_map ( info, &map );
344 		if ( bpp < 0 ) {
345 			rc = bpp;
346 			DBGC ( &efifb, "EFIFB could not build colour map for "
347 			       "mode %d: %s\n", mode, strerror ( rc ) );
348 			goto err_map;
349 		}
350 
351 		/* Skip modes not meeting the requirements */
352 		if ( ( info->HorizontalResolution < min_width ) ||
353 		     ( info->VerticalResolution < min_height ) ||
354 		     ( ( ( unsigned int ) bpp ) < min_bpp ) ) {
355 			goto err_requirements;
356 		}
357 
358 		/* Select this mode if it has the best (i.e. lowest)
359 		 * score.  We choose the scoring system to favour
360 		 * modes close to the specified width and height;
361 		 * within modes of the same width and height we prefer
362 		 * a higher colour depth.
363 		 */
364 		score = ( ( info->HorizontalResolution *
365 			    info->VerticalResolution ) - bpp );
366 		if ( score < best_score ) {
367 			best_mode_number = mode;
368 			best_score = score;
369 		}
370 
371 	err_requirements:
372 	err_map:
373 		bs->FreePool ( info );
374 	err_query:
375 		continue;
376 	}
377 
378 	if ( best_mode_number < 0 )
379 		DBGC ( &efifb, "EFIFB found no suitable mode\n" );
380 	return best_mode_number;
381 }
382 
383 /**
384  * Restore video mode
385  *
386  * @v rc		Return status code
387  */
efifb_restore(void)388 static int efifb_restore ( void ) {
389 	EFI_STATUS efirc;
390 	int rc;
391 
392 	/* Restore original mode */
393 	if ( ( efirc = efifb.gop->SetMode ( efifb.gop,
394 					    efifb.saved_mode ) ) != 0 ) {
395 		rc = -EEFI ( efirc );
396 		DBGC ( &efifb, "EFIFB could not restore mode %d: %s\n",
397 		       efifb.saved_mode, strerror ( rc ) );
398 		return rc;
399 	}
400 
401 	return 0;
402 }
403 
404 /**
405  * Initialise EFI frame buffer
406  *
407  * @v config		Console configuration, or NULL to reset
408  * @ret rc		Return status code
409  */
efifb_init(struct console_configuration * config)410 static int efifb_init ( struct console_configuration *config ) {
411 	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
412 	EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
413 	void *interface;
414 	int mode;
415 	int bpp;
416 	EFI_STATUS efirc;
417 	int rc;
418 
419 	/* Locate graphics output protocol */
420 	if ( ( efirc = bs->LocateProtocol ( &efi_graphics_output_protocol_guid,
421 					    NULL, &interface ) ) != 0 ) {
422 		rc = -EEFI ( efirc );
423 		DBGC ( &efifb, "EFIFB could not locate graphics output "
424 		       "protocol: %s\n", strerror ( rc ) );
425 		goto err_locate_gop;
426 	}
427 	efifb.gop = interface;
428 
429 	/* Locate HII font protocol */
430 	if ( ( efirc = bs->LocateProtocol ( &efi_hii_font_protocol_guid,
431 					    NULL, &interface ) ) != 0 ) {
432 		rc = -EEFI ( efirc );
433 		DBGC ( &efifb, "EFIFB could not locate HII font protocol: %s\n",
434 		       strerror ( rc ) );
435 		goto err_locate_hiifont;
436 	}
437 	efifb.hiifont = interface;
438 
439 	/* Locate glyphs */
440 	if ( ( rc = efifb_glyphs() ) != 0 )
441 		goto err_glyphs;
442 
443 	/* Save original mode */
444 	efifb.saved_mode = efifb.gop->Mode->Mode;
445 
446 	/* Select mode */
447 	if ( ( mode = efifb_select_mode ( config->width, config->height,
448 					  config->depth ) ) < 0 ) {
449 		rc = mode;
450 		goto err_select_mode;
451 	}
452 
453 	/* Set mode */
454 	if ( ( efirc = efifb.gop->SetMode ( efifb.gop, mode ) ) != 0 ) {
455 		rc = -EEFI ( efirc );
456 		DBGC ( &efifb, "EFIFB could not set mode %d: %s\n",
457 		       mode, strerror ( rc ) );
458 		goto err_set_mode;
459 	}
460 	info = efifb.gop->Mode->Info;
461 
462 	/* Populate colour map */
463 	bpp = efifb_colour_map ( info, &efifb.map );
464 	if ( bpp < 0 ) {
465 		rc = bpp;
466 		DBGC ( &efifb, "EFIFB could not build colour map for "
467 		       "mode %d: %s\n", mode, strerror ( rc ) );
468 		goto err_map;
469 	}
470 
471 	/* Populate pixel geometry */
472 	efifb.pixel.width = info->HorizontalResolution;
473 	efifb.pixel.height = info->VerticalResolution;
474 	efifb.pixel.len = ( ( bpp + 7 ) / 8 );
475 	efifb.pixel.stride = ( efifb.pixel.len * info->PixelsPerScanLine );
476 
477 	/* Populate frame buffer address */
478 	efifb.start = efifb.gop->Mode->FrameBufferBase;
479 	DBGC ( &efifb, "EFIFB using mode %d (%dx%d %dbpp at %#08lx)\n",
480 	       mode, efifb.pixel.width, efifb.pixel.height, bpp, efifb.start );
481 
482 	/* Initialise frame buffer console */
483 	if ( ( rc = fbcon_init ( &efifb.fbcon, phys_to_user ( efifb.start ),
484 				 &efifb.pixel, &efifb.map, &efifb.font,
485 				 config ) ) != 0 )
486 		goto err_fbcon_init;
487 
488 	return 0;
489 
490 	fbcon_fini ( &efifb.fbcon );
491  err_fbcon_init:
492  err_map:
493 	efifb_restore();
494  err_set_mode:
495  err_select_mode:
496 	ufree ( efifb.glyphs );
497  err_glyphs:
498  err_locate_hiifont:
499  err_locate_gop:
500 	return rc;
501 }
502 
503 /**
504  * Finalise EFI frame buffer
505  *
506  */
efifb_fini(void)507 static void efifb_fini ( void ) {
508 
509 	/* Finalise frame buffer console */
510 	fbcon_fini ( &efifb.fbcon );
511 
512 	/* Restore saved mode */
513 	efifb_restore();
514 
515 	/* Free glyphs */
516 	ufree ( efifb.glyphs );
517 }
518 
519 /**
520  * Print a character to current cursor position
521  *
522  * @v character		Character
523  */
efifb_putchar(int character)524 static void efifb_putchar ( int character ) {
525 
526 	fbcon_putchar ( &efifb.fbcon, character );
527 }
528 
529 /**
530  * Configure console
531  *
532  * @v config		Console configuration, or NULL to reset
533  * @ret rc		Return status code
534  */
efifb_configure(struct console_configuration * config)535 static int efifb_configure ( struct console_configuration *config ) {
536 	int rc;
537 
538 	/* Reset console, if applicable */
539 	if ( ! efifb_console.disabled ) {
540 		efifb_fini();
541 		efi_console.disabled &= ~CONSOLE_DISABLED_OUTPUT;
542 		ansicol_reset_magic();
543 	}
544 	efifb_console.disabled = CONSOLE_DISABLED;
545 
546 	/* Do nothing more unless we have a usable configuration */
547 	if ( ( config == NULL ) ||
548 	     ( config->width == 0 ) || ( config->height == 0 ) ) {
549 		return 0;
550 	}
551 
552 	/* Initialise EFI frame buffer */
553 	if ( ( rc = efifb_init ( config ) ) != 0 )
554 		return rc;
555 
556 	/* Mark console as enabled */
557 	efifb_console.disabled = 0;
558 	efi_console.disabled |= CONSOLE_DISABLED_OUTPUT;
559 
560 	/* Set magic colour to transparent if we have a background picture */
561 	if ( config->pixbuf )
562 		ansicol_set_magic_transparent();
563 
564 	return 0;
565 }
566 
567 /** EFI graphics output protocol console driver */
568 struct console_driver efifb_console __console_driver = {
569 	.usage = CONSOLE_EFIFB,
570 	.putchar = efifb_putchar,
571 	.configure = efifb_configure,
572 	.disabled = CONSOLE_DISABLED,
573 };
574