您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

efi_fbcon.c 14KB

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