You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

efi_fbcon.c 14KB

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