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