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

vesafb.c 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  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. FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  24. /** @file
  25. *
  26. * VESA frame buffer console
  27. *
  28. */
  29. #include <stdlib.h>
  30. #include <errno.h>
  31. #include <limits.h>
  32. #include <realmode.h>
  33. #include <ipxe/console.h>
  34. #include <ipxe/io.h>
  35. #include <ipxe/ansicol.h>
  36. #include <ipxe/fbcon.h>
  37. #include <ipxe/vesafb.h>
  38. #include <config/console.h>
  39. /* Avoid dragging in BIOS console if not otherwise used */
  40. extern struct console_driver bios_console;
  41. struct console_driver bios_console __attribute__ (( weak ));
  42. /* Disambiguate the various error causes */
  43. #define EIO_FAILED __einfo_error ( EINFO_EIO_FAILED )
  44. #define EINFO_EIO_FAILED \
  45. __einfo_uniqify ( EINFO_EIO, 0x01, \
  46. "Function call failed" )
  47. #define EIO_HARDWARE __einfo_error ( EINFO_EIO_HARDWARE )
  48. #define EINFO_EIO_HARDWARE \
  49. __einfo_uniqify ( EINFO_EIO, 0x02, \
  50. "Not supported in current configuration" )
  51. #define EIO_MODE __einfo_error ( EINFO_EIO_MODE )
  52. #define EINFO_EIO_MODE \
  53. __einfo_uniqify ( EINFO_EIO, 0x03, \
  54. "Invalid in current video mode" )
  55. #define EIO_VBE( code ) \
  56. EUNIQ ( EINFO_EIO, (code), EIO_FAILED, EIO_HARDWARE, EIO_MODE )
  57. /* Set default console usage if applicable */
  58. #if ! ( defined ( CONSOLE_VESAFB ) && CONSOLE_EXPLICIT ( CONSOLE_VESAFB ) )
  59. #undef CONSOLE_VESAFB
  60. #define CONSOLE_VESAFB ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
  61. #endif
  62. /** Character height */
  63. #define VESAFB_CHAR_HEIGHT 16
  64. /** Font corresponding to selected character width and height */
  65. #define VESAFB_FONT VBE_FONT_8x16
  66. /* Forward declaration */
  67. struct console_driver vesafb_console __console_driver;
  68. /** A VESA frame buffer */
  69. struct vesafb {
  70. /** Frame buffer console */
  71. struct fbcon fbcon;
  72. /** Physical start address */
  73. physaddr_t start;
  74. /** Pixel geometry */
  75. struct fbcon_geometry pixel;
  76. /** Margin */
  77. struct fbcon_margin margin;
  78. /** Colour mapping */
  79. struct fbcon_colour_map map;
  80. /** Font definition */
  81. struct fbcon_font font;
  82. /** Character glyphs */
  83. struct segoff glyphs;
  84. /** Saved VGA mode */
  85. uint8_t saved_mode;
  86. };
  87. /** The VESA frame buffer */
  88. static struct vesafb vesafb;
  89. /** Base memory buffer used for VBE calls */
  90. union vbe_buffer {
  91. /** VBE controller information block */
  92. struct vbe_controller_info controller;
  93. /** VBE mode information block */
  94. struct vbe_mode_info mode;
  95. };
  96. static union vbe_buffer __bss16 ( vbe_buf );
  97. #define vbe_buf __use_data16 ( vbe_buf )
  98. /**
  99. * Convert VBE status code to iPXE status code
  100. *
  101. * @v status VBE status code
  102. * @ret rc Return status code
  103. */
  104. static int vesafb_rc ( unsigned int status ) {
  105. unsigned int code;
  106. if ( ( status & 0xff ) != 0x4f )
  107. return -ENOTSUP;
  108. code = ( ( status >> 8 ) & 0xff );
  109. return ( code ? -EIO_VBE ( code ) : 0 );
  110. }
  111. /**
  112. * Get character glyph
  113. *
  114. * @v character Character
  115. * @v glyph Character glyph to fill in
  116. */
  117. static void vesafb_glyph ( unsigned int character, uint8_t *glyph ) {
  118. size_t offset = ( character * VESAFB_CHAR_HEIGHT );
  119. copy_from_real ( glyph, vesafb.glyphs.segment,
  120. ( vesafb.glyphs.offset + offset ), VESAFB_CHAR_HEIGHT);
  121. }
  122. /**
  123. * Get font definition
  124. *
  125. */
  126. static void vesafb_font ( void ) {
  127. /* Get font information
  128. *
  129. * Working around gcc bugs is icky here. The value we want is
  130. * returned in %ebp, but there's no way to specify %ebp in an
  131. * output constraint. We can't put %ebp in the clobber list,
  132. * because this tends to cause random build failures on some
  133. * gcc versions. We can't manually push/pop %ebp and return
  134. * the value via a generic register output constraint, because
  135. * gcc might choose to use %ebp to satisfy that constraint
  136. * (and we have no way to prevent it from so doing).
  137. *
  138. * Work around this hideous mess by using %ecx and %edx as the
  139. * output registers, since they get clobbered anyway.
  140. */
  141. __asm__ __volatile__ ( REAL_CODE ( "pushw %%bp\n\t" /* gcc bug */
  142. "int $0x10\n\t"
  143. "movw %%es, %%cx\n\t"
  144. "movw %%bp, %%dx\n\t"
  145. "popw %%bp\n\t" /* gcc bug */ )
  146. : "=c" ( vesafb.glyphs.segment ),
  147. "=d" ( vesafb.glyphs.offset )
  148. : "a" ( VBE_GET_FONT ),
  149. "b" ( VESAFB_FONT ) );
  150. DBGC ( &vbe_buf, "VESAFB has font %04x at %04x:%04x\n",
  151. VESAFB_FONT, vesafb.glyphs.segment, vesafb.glyphs.offset );
  152. vesafb.font.height = VESAFB_CHAR_HEIGHT;
  153. vesafb.font.glyph = vesafb_glyph;
  154. }
  155. /**
  156. * Get VBE mode list
  157. *
  158. * @ret mode_numbers Mode number list (terminated with VBE_MODE_END)
  159. * @ret rc Return status code
  160. *
  161. * The caller is responsible for eventually freeing the mode list.
  162. */
  163. static int vesafb_mode_list ( uint16_t **mode_numbers ) {
  164. struct vbe_controller_info *controller = &vbe_buf.controller;
  165. userptr_t video_mode_ptr;
  166. uint16_t mode_number;
  167. uint16_t status;
  168. size_t len;
  169. int rc;
  170. /* Avoid returning uninitialised data on error */
  171. *mode_numbers = NULL;
  172. /* Get controller information block */
  173. controller->vbe_signature = 0;
  174. __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
  175. : "=a" ( status )
  176. : "a" ( VBE_CONTROLLER_INFO ),
  177. "D" ( __from_data16 ( controller ) )
  178. : "memory", "ebx", "edx" );
  179. if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
  180. DBGC ( &vbe_buf, "VESAFB could not get controller information: "
  181. "[%04x] %s\n", status, strerror ( rc ) );
  182. return rc;
  183. }
  184. if ( controller->vbe_signature != VBE_CONTROLLER_SIGNATURE ) {
  185. DBGC ( &vbe_buf, "VESAFB invalid controller signature "
  186. "\"%c%c%c%c\"\n", ( controller->vbe_signature >> 0 ),
  187. ( controller->vbe_signature >> 8 ),
  188. ( controller->vbe_signature >> 16 ),
  189. ( controller->vbe_signature >> 24 ) );
  190. DBGC_HDA ( &vbe_buf, 0, controller, sizeof ( *controller ) );
  191. return -EINVAL;
  192. }
  193. DBGC ( &vbe_buf, "VESAFB found VBE version %d.%d with mode list at "
  194. "%04x:%04x\n", controller->vbe_major_version,
  195. controller->vbe_minor_version,
  196. controller->video_mode_ptr.segment,
  197. controller->video_mode_ptr.offset );
  198. /* Calculate length of mode list */
  199. video_mode_ptr = real_to_user ( controller->video_mode_ptr.segment,
  200. controller->video_mode_ptr.offset );
  201. len = 0;
  202. do {
  203. copy_from_user ( &mode_number, video_mode_ptr, len,
  204. sizeof ( mode_number ) );
  205. len += sizeof ( mode_number );
  206. } while ( mode_number != VBE_MODE_END );
  207. /* Allocate and fill mode list */
  208. *mode_numbers = malloc ( len );
  209. if ( ! *mode_numbers )
  210. return -ENOMEM;
  211. copy_from_user ( *mode_numbers, video_mode_ptr, 0, len );
  212. return 0;
  213. }
  214. /**
  215. * Get video mode information
  216. *
  217. * @v mode_number Mode number
  218. * @ret rc Return status code
  219. */
  220. static int vesafb_mode_info ( unsigned int mode_number ) {
  221. struct vbe_mode_info *mode = &vbe_buf.mode;
  222. uint16_t status;
  223. int rc;
  224. /* Get mode information */
  225. __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
  226. : "=a" ( status )
  227. : "a" ( VBE_MODE_INFO ),
  228. "c" ( mode_number ),
  229. "D" ( __from_data16 ( mode ) )
  230. : "memory" );
  231. if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
  232. DBGC ( &vbe_buf, "VESAFB could not get mode %04x information: "
  233. "[%04x] %s\n", mode_number, status, strerror ( rc ) );
  234. return rc;
  235. }
  236. DBGC ( &vbe_buf, "VESAFB mode %04x %dx%d %dbpp(%d:%d:%d:%d) model "
  237. "%02x [x%d]%s%s%s%s%s\n", mode_number, mode->x_resolution,
  238. mode->y_resolution, mode->bits_per_pixel, mode->rsvd_mask_size,
  239. mode->red_mask_size, mode->green_mask_size, mode->blue_mask_size,
  240. mode->memory_model, ( mode->number_of_image_pages + 1 ),
  241. ( ( mode->mode_attributes & VBE_MODE_ATTR_SUPPORTED ) ?
  242. "" : " [unsupported]" ),
  243. ( ( mode->mode_attributes & VBE_MODE_ATTR_TTY ) ?
  244. " [tty]" : "" ),
  245. ( ( mode->mode_attributes & VBE_MODE_ATTR_GRAPHICS ) ?
  246. "" : " [text]" ),
  247. ( ( mode->mode_attributes & VBE_MODE_ATTR_LINEAR ) ?
  248. "" : " [nonlinear]" ),
  249. ( ( mode->mode_attributes & VBE_MODE_ATTR_TRIPLE_BUF ) ?
  250. " [buf]" : "" ) );
  251. return 0;
  252. }
  253. /**
  254. * Set video mode
  255. *
  256. * @v mode_number Mode number
  257. * @ret rc Return status code
  258. */
  259. static int vesafb_set_mode ( unsigned int mode_number ) {
  260. struct vbe_mode_info *mode = &vbe_buf.mode;
  261. uint16_t status;
  262. int rc;
  263. /* Get mode information */
  264. if ( ( rc = vesafb_mode_info ( mode_number ) ) != 0 )
  265. return rc;
  266. /* Record mode parameters */
  267. vesafb.start = mode->phys_base_ptr;
  268. vesafb.pixel.width = mode->x_resolution;
  269. vesafb.pixel.height = mode->y_resolution;
  270. vesafb.pixel.len = ( ( mode->bits_per_pixel + 7 ) / 8 );
  271. vesafb.pixel.stride = mode->bytes_per_scan_line;
  272. DBGC ( &vbe_buf, "VESAFB mode %04x has frame buffer at %08x\n",
  273. mode_number, mode->phys_base_ptr );
  274. /* Initialise font colours */
  275. vesafb.map.red_scale = ( 8 - mode->red_mask_size );
  276. vesafb.map.green_scale = ( 8 - mode->green_mask_size );
  277. vesafb.map.blue_scale = ( 8 - mode->blue_mask_size );
  278. vesafb.map.red_lsb = mode->red_field_position;
  279. vesafb.map.green_lsb = mode->green_field_position;
  280. vesafb.map.blue_lsb = mode->blue_field_position;
  281. /* Select this mode */
  282. __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
  283. : "=a" ( status )
  284. : "a" ( VBE_SET_MODE ),
  285. "b" ( mode_number ) );
  286. if ( ( rc = vesafb_rc ( status ) ) != 0 ) {
  287. DBGC ( &vbe_buf, "VESAFB could not set mode %04x: [%04x] %s\n",
  288. mode_number, status, strerror ( rc ) );
  289. return rc;
  290. }
  291. return 0;
  292. }
  293. /**
  294. * Select video mode
  295. *
  296. * @v mode_numbers Mode number list (terminated with VBE_MODE_END)
  297. * @v min_width Minimum required width (in pixels)
  298. * @v min_height Minimum required height (in pixels)
  299. * @v min_bpp Minimum required colour depth (in bits per pixel)
  300. * @ret mode_number Mode number, or negative error
  301. */
  302. static int vesafb_select_mode ( const uint16_t *mode_numbers,
  303. unsigned int min_width, unsigned int min_height,
  304. unsigned int min_bpp ) {
  305. struct vbe_mode_info *mode = &vbe_buf.mode;
  306. int best_mode_number = -ENOENT;
  307. unsigned int best_score = INT_MAX;
  308. unsigned int score;
  309. uint16_t mode_number;
  310. int rc;
  311. /* Find the first suitable mode */
  312. while ( ( mode_number = *(mode_numbers++) ) != VBE_MODE_END ) {
  313. /* Force linear mode variant */
  314. mode_number |= VBE_MODE_LINEAR;
  315. /* Get mode information */
  316. if ( ( rc = vesafb_mode_info ( mode_number ) ) != 0 )
  317. continue;
  318. /* Skip unusable modes */
  319. if ( ( mode->mode_attributes & ( VBE_MODE_ATTR_SUPPORTED |
  320. VBE_MODE_ATTR_GRAPHICS |
  321. VBE_MODE_ATTR_LINEAR ) ) !=
  322. ( VBE_MODE_ATTR_SUPPORTED | VBE_MODE_ATTR_GRAPHICS |
  323. VBE_MODE_ATTR_LINEAR ) ) {
  324. continue;
  325. }
  326. if ( mode->memory_model != VBE_MODE_MODEL_DIRECT_COLOUR )
  327. continue;
  328. /* Skip modes not meeting the requirements */
  329. if ( ( mode->x_resolution < min_width ) ||
  330. ( mode->y_resolution < min_height ) ||
  331. ( mode->bits_per_pixel < min_bpp ) ) {
  332. continue;
  333. }
  334. /* Select this mode if it has the best (i.e. lowest)
  335. * score. We choose the scoring system to favour
  336. * modes close to the specified width and height;
  337. * within modes of the same width and height we prefer
  338. * a higher colour depth.
  339. */
  340. score = ( ( mode->x_resolution * mode->y_resolution ) -
  341. mode->bits_per_pixel );
  342. if ( score < best_score ) {
  343. best_mode_number = mode_number;
  344. best_score = score;
  345. }
  346. }
  347. if ( best_mode_number >= 0 ) {
  348. DBGC ( &vbe_buf, "VESAFB selected mode %04x\n",
  349. best_mode_number );
  350. } else {
  351. DBGC ( &vbe_buf, "VESAFB found no suitable mode\n" );
  352. }
  353. return best_mode_number;
  354. }
  355. /**
  356. * Restore video mode
  357. *
  358. */
  359. static void vesafb_restore ( void ) {
  360. uint32_t discard_a;
  361. /* Restore saved VGA mode */
  362. __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
  363. : "=a" ( discard_a )
  364. : "a" ( VBE_SET_VGA_MODE | vesafb.saved_mode ) );
  365. DBGC ( &vbe_buf, "VESAFB restored VGA mode %#02x\n",
  366. vesafb.saved_mode );
  367. }
  368. /**
  369. * Initialise VESA frame buffer
  370. *
  371. * @v config Console configuration, or NULL to reset
  372. * @ret rc Return status code
  373. */
  374. static int vesafb_init ( struct console_configuration *config ) {
  375. uint32_t discard_b;
  376. uint16_t *mode_numbers;
  377. unsigned int xgap;
  378. unsigned int ygap;
  379. unsigned int left;
  380. unsigned int right;
  381. unsigned int top;
  382. unsigned int bottom;
  383. int mode_number;
  384. int rc;
  385. /* Record current VGA mode */
  386. __asm__ __volatile__ ( REAL_CODE ( "int $0x10" )
  387. : "=a" ( vesafb.saved_mode ), "=b" ( discard_b )
  388. : "a" ( VBE_GET_VGA_MODE ) );
  389. DBGC ( &vbe_buf, "VESAFB saved VGA mode %#02x\n", vesafb.saved_mode );
  390. /* Get VESA mode list */
  391. if ( ( rc = vesafb_mode_list ( &mode_numbers ) ) != 0 )
  392. goto err_mode_list;
  393. /* Select mode */
  394. if ( ( mode_number = vesafb_select_mode ( mode_numbers, config->width,
  395. config->height,
  396. config->depth ) ) < 0 ) {
  397. rc = mode_number;
  398. goto err_select_mode;
  399. }
  400. /* Set mode */
  401. if ( ( rc = vesafb_set_mode ( mode_number ) ) != 0 )
  402. goto err_set_mode;
  403. /* Calculate margin. If the actual screen size is larger than
  404. * the requested screen size, then update the margins so that
  405. * the margin remains relative to the requested screen size.
  406. * (As an exception, if a zero margin was specified then treat
  407. * this as meaning "expand to edge of actual screen".)
  408. */
  409. xgap = ( vesafb.pixel.width - config->width );
  410. ygap = ( vesafb.pixel.height - config->height );
  411. left = ( xgap / 2 );
  412. right = ( xgap - left );
  413. top = ( ygap / 2 );
  414. bottom = ( ygap - top );
  415. vesafb.margin.left = ( config->left + ( config->left ? left : 0 ) );
  416. vesafb.margin.right = ( config->right + ( config->right ? right : 0 ) );
  417. vesafb.margin.top = ( config->top + ( config->top ? top : 0 ) );
  418. vesafb.margin.bottom =
  419. ( config->bottom + ( config->bottom ? bottom : 0 ) );
  420. /* Get font data */
  421. vesafb_font();
  422. /* Initialise frame buffer console */
  423. if ( ( rc = fbcon_init ( &vesafb.fbcon, phys_to_user ( vesafb.start ),
  424. &vesafb.pixel, &vesafb.margin, &vesafb.map,
  425. &vesafb.font, config->pixbuf ) ) != 0 )
  426. goto err_fbcon_init;
  427. free ( mode_numbers );
  428. return 0;
  429. fbcon_fini ( &vesafb.fbcon );
  430. err_fbcon_init:
  431. err_set_mode:
  432. vesafb_restore();
  433. err_select_mode:
  434. free ( mode_numbers );
  435. err_mode_list:
  436. return rc;
  437. }
  438. /**
  439. * Finalise VESA frame buffer
  440. *
  441. */
  442. static void vesafb_fini ( void ) {
  443. /* Finalise frame buffer console */
  444. fbcon_fini ( &vesafb.fbcon );
  445. /* Restore saved VGA mode */
  446. vesafb_restore();
  447. }
  448. /**
  449. * Print a character to current cursor position
  450. *
  451. * @v character Character
  452. */
  453. static void vesafb_putchar ( int character ) {
  454. fbcon_putchar ( &vesafb.fbcon, character );
  455. }
  456. /**
  457. * Configure console
  458. *
  459. * @v config Console configuration, or NULL to reset
  460. * @ret rc Return status code
  461. */
  462. static int vesafb_configure ( struct console_configuration *config ) {
  463. int rc;
  464. /* Reset console, if applicable */
  465. if ( ! vesafb_console.disabled ) {
  466. vesafb_fini();
  467. bios_console.disabled &= ~CONSOLE_DISABLED_OUTPUT;
  468. ansicol_reset_magic();
  469. }
  470. vesafb_console.disabled = CONSOLE_DISABLED;
  471. /* Do nothing more unless we have a usable configuration */
  472. if ( ( config == NULL ) ||
  473. ( config->width == 0 ) || ( config->height == 0 ) ) {
  474. return 0;
  475. }
  476. /* Initialise VESA frame buffer */
  477. if ( ( rc = vesafb_init ( config ) ) != 0 )
  478. return rc;
  479. /* Mark console as enabled */
  480. vesafb_console.disabled = 0;
  481. bios_console.disabled |= CONSOLE_DISABLED_OUTPUT;
  482. /* Set magic colour to transparent if we have a background picture */
  483. if ( config->pixbuf )
  484. ansicol_set_magic_transparent();
  485. return 0;
  486. }
  487. /** VESA frame buffer console driver */
  488. struct console_driver vesafb_console __console_driver = {
  489. .usage = CONSOLE_VESAFB,
  490. .putchar = vesafb_putchar,
  491. .configure = vesafb_configure,
  492. .disabled = CONSOLE_DISABLED,
  493. };