iPXE
Data Structures | Defines | Functions | Variables
efi_fbcon.c File Reference

EFI frame buffer console. More...

#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>
#include <limits.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/Protocol/GraphicsOutput.h>
#include <ipxe/efi/Protocol/HiiFont.h>
#include <ipxe/ansicol.h>
#include <ipxe/fbcon.h>
#include <ipxe/console.h>
#include <ipxe/umalloc.h>
#include <ipxe/rotate.h>
#include <config/console.h>

Go to the source code of this file.

Data Structures

struct  efifb
 An EFI frame buffer. More...

Defines

#define CONSOLE_EFIFB   ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static void efifb_glyph (unsigned int character, uint8_t *glyph)
 Get character glyph.
static int efifb_glyphs (void)
 Get character glyphs.
static int efifb_colour_map_mask (uint32_t mask, uint8_t *scale, uint8_t *lsb)
 Generate colour mapping for a single colour component.
static int efifb_colour_map (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info, struct fbcon_colour_map *map)
 Generate colour mapping.
static int efifb_select_mode (unsigned int min_width, unsigned int min_height, unsigned int min_bpp)
 Select video mode.
static int efifb_restore (void)
 Restore video mode.
static int efifb_init (struct console_configuration *config)
 Initialise EFI frame buffer.
static void efifb_fini (void)
 Finalise EFI frame buffer.
static void efifb_putchar (int character)
 Print a character to current cursor position.
static int efifb_configure (struct console_configuration *config)
 Configure console.

Variables

struct console_driver efi_console
struct console_driver efifb_console __console_driver
 EFI graphics output protocol console driver.
static struct efifb efifb
 The EFI frame buffer.

Detailed Description

EFI frame buffer console.

Definition in file efi_fbcon.c.


Define Documentation

Definition at line 62 of file efi_fbcon.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static void efifb_glyph ( unsigned int  character,
uint8_t glyph 
) [static]

Get character glyph.

Parameters:
characterCharacter
glyphCharacter glyph to fill in

Definition at line 100 of file efi_fbcon.c.

References copy_from_user(), efifb::font, efifb::glyphs, fbcon_font::height, and offset.

Referenced by efifb_glyphs().

                                                                   {
        size_t offset = ( character * efifb.font.height );

        copy_from_user ( glyph, efifb.glyphs, offset, efifb.font.height );
}
static int efifb_glyphs ( void  ) [static]

Get character glyphs.

Return values:
rcReturn status code

Definition at line 111 of file efi_fbcon.c.

References assert, _EFI_IMAGE_OUTPUT::Bitmap, EFI_GRAPHICS_OUTPUT_BLT_PIXEL::Blue, EFI_SYSTEM_TABLE::BootServices, copy_to_user(), DBGC, EEFI, efi_systab, efifb_glyph(), ENOENT, ENOMEM, efifb::font, EFI_BOOT_SERVICES::FreePool, _EFI_HII_FONT_PROTOCOL::GetGlyph, fbcon_font::glyph, efifb::glyphs, EFI_GRAPHICS_OUTPUT_BLT_PIXEL::Green, fbcon_font::height, _EFI_IMAGE_OUTPUT::Height, efifb::hiifont, _EFI_IMAGE_OUTPUT::Image, isprint(), len, memset_user(), NULL, offset, efifb::pixel, rc, EFI_GRAPHICS_OUTPUT_BLT_PIXEL::Red, strerror(), ufree(), umalloc(), and _EFI_IMAGE_OUTPUT::Width.

Referenced by efifb_init().

                                 {
        EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
        EFI_IMAGE_OUTPUT *blt;
        EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixel;
        size_t offset;
        size_t len;
        uint8_t bitmask;
        unsigned int character;
        unsigned int x;
        unsigned int y;
        EFI_STATUS efirc;
        int rc;

        /* Get font height.  The GetFontInfo() call nominally returns
         * this information in an EFI_FONT_DISPLAY_INFO structure, but
         * is known to fail on many UEFI implementations.  Instead, we
         * iterate over all printable characters to find the maximum
         * height.
         */
        efifb.font.height = 0;
        for ( character = 0 ; character < 256 ; character++ ) {

                /* Skip non-printable characters */
                if ( ! isprint ( character ) )
                        continue;

                /* Get glyph */
                blt = NULL;
                if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont,
                                                         character, NULL, &blt,
                                                         NULL ) ) != 0 ) {
                        rc = -EEFI ( efirc );
                        DBGC ( &efifb, "EFIFB could not get glyph %d: %s\n",
                               character, strerror ( rc ) );
                        continue;
                }
                assert ( blt != NULL );

                /* Calculate maximum height */
                if ( efifb.font.height < blt->Height )
                        efifb.font.height = blt->Height;

                /* Free glyph */
                bs->FreePool ( blt );
        }
        if ( ! efifb.font.height ) {
                DBGC ( &efifb, "EFIFB could not get font height\n" );
                return -ENOENT;
        }

        /* Allocate glyph data */
        len = ( 256 * efifb.font.height * sizeof ( bitmask ) );
        efifb.glyphs = umalloc ( len );
        if ( ! efifb.glyphs ) {
                rc = -ENOMEM;
                goto err_alloc;
        }
        memset_user ( efifb.glyphs, 0, 0, len );

        /* Get font data */
        for ( character = 0 ; character < 256 ; character++ ) {

                /* Skip non-printable characters */
                if ( ! isprint ( character ) )
                        continue;

                /* Get glyph */
                blt = NULL;
                if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont,
                                                         character, NULL, &blt,
                                                         NULL ) ) != 0 ) {
                        rc = -EEFI ( efirc );
                        DBGC ( &efifb, "EFIFB could not get glyph %d: %s\n",
                               character, strerror ( rc ) );
                        continue;
                }
                assert ( blt != NULL );

                /* Sanity check */
                if ( blt->Width > 8 ) {
                        DBGC ( &efifb, "EFIFB glyph %d invalid width %d\n",
                               character, blt->Width );
                        continue;
                }
                if ( blt->Height > efifb.font.height ) {
                        DBGC ( &efifb, "EFIFB glyph %d invalid height %d\n",
                               character, blt->Height );
                        continue;
                }

                /* Convert glyph to bitmap */
                pixel = blt->Image.Bitmap;
                offset = ( character * efifb.font.height );
                for ( y = 0 ; y < blt->Height ; y++ ) {
                        bitmask = 0;
                        for ( x = 0 ; x < blt->Width ; x++ ) {
                                bitmask = rol8 ( bitmask, 1 );
                                if ( pixel->Blue || pixel->Green || pixel->Red )
                                        bitmask |= 0x01;
                                pixel++;
                        }
                        copy_to_user ( efifb.glyphs, offset++, &bitmask,
                                       sizeof ( bitmask ) );
                }

                /* Free glyph */
                bs->FreePool ( blt );
        }

        efifb.font.glyph = efifb_glyph;
        return 0;

        ufree ( efifb.glyphs );
 err_alloc:
        return rc;
}
static int efifb_colour_map_mask ( uint32_t  mask,
uint8_t scale,
uint8_t lsb 
) [static]

Generate colour mapping for a single colour component.

Parameters:
maskMask value
scaleScale value to fill in
lsbLSB value to fill in
Return values:
rcReturn status code

Definition at line 236 of file efi_fbcon.c.

References ENOTSUP, ffs, and fls.

Referenced by efifb_colour_map().

                                                  {
        uint32_t check;

        /* Fill in LSB and scale */
        *lsb = ( mask ? ( ffs ( mask ) - 1 ) : 0 );
        *scale = ( mask ? ( 8 - ( fls ( mask ) - *lsb ) ) : 8 );

        /* Check that original mask was contiguous */
        check = ( ( 0xff >> *scale ) << *lsb );
        if ( check != mask )
                return -ENOTSUP;

        return 0;
}
static int efifb_colour_map ( EFI_GRAPHICS_OUTPUT_MODE_INFORMATION info,
struct fbcon_colour_map map 
) [static]

Generate colour mapping.

Parameters:
infoEFI mode information
mapColour mapping to fill in
Return values:
bppNumber of bits per pixel, or negative error

Definition at line 259 of file efi_fbcon.c.

References fbcon_colour_map::blue_lsb, fbcon_colour_map::blue_scale, EFI_PIXEL_BITMASK::BlueMask, DBGC, efifb_colour_map_mask(), ENOTSUP, fbcon_colour_map::green_lsb, fbcon_colour_map::green_scale, EFI_PIXEL_BITMASK::GreenMask, PixelBitMask, PixelBlueGreenRedReserved8BitPerColor, EFI_GRAPHICS_OUTPUT_MODE_INFORMATION::PixelFormat, EFI_GRAPHICS_OUTPUT_MODE_INFORMATION::PixelInformation, PixelRedGreenBlueReserved8BitPerColor, rc, fbcon_colour_map::red_lsb, fbcon_colour_map::red_scale, EFI_PIXEL_BITMASK::RedMask, and EFI_PIXEL_BITMASK::ReservedMask.

Referenced by efifb_init(), and efifb_select_mode().

                                                             {
        static EFI_PIXEL_BITMASK rgb_mask = {
                0x000000ffUL, 0x0000ff00UL, 0x00ff0000UL, 0xff000000UL
        };
        static EFI_PIXEL_BITMASK bgr_mask = {
                0x00ff0000UL, 0x0000ff00UL, 0x000000ffUL, 0xff000000UL
        };
        EFI_PIXEL_BITMASK *mask;
        uint8_t reserved_scale;
        uint8_t reserved_lsb;
        int rc;

        /* Determine applicable mask */
        switch ( info->PixelFormat ) {
        case PixelRedGreenBlueReserved8BitPerColor:
                mask = &rgb_mask;
                break;
        case PixelBlueGreenRedReserved8BitPerColor:
                mask = &bgr_mask;
                break;
        case PixelBitMask:
                mask = &info->PixelInformation;
                break;
        default:
                DBGC ( &efifb, "EFIFB unrecognised pixel format %d\n",
                       info->PixelFormat );
                return -ENOTSUP;
        }

        /* Map each colour component */
        if ( ( rc = efifb_colour_map_mask ( mask->RedMask, &map->red_scale,
                                            &map->red_lsb ) ) != 0 )
                return rc;
        if ( ( rc = efifb_colour_map_mask ( mask->GreenMask, &map->green_scale,
                                            &map->green_lsb ) ) != 0 )
                return rc;
        if ( ( rc = efifb_colour_map_mask ( mask->BlueMask, &map->blue_scale,
                                            &map->blue_lsb ) ) != 0 )
                return rc;
        if ( ( rc = efifb_colour_map_mask ( mask->ReservedMask, &reserved_scale,
                                            &reserved_lsb ) ) != 0 )
                return rc;

        /* Calculate total number of bits per pixel */
        return ( 32 - ( reserved_scale + map->red_scale + map->green_scale +
                        map->blue_scale ) );
}
static int efifb_select_mode ( unsigned int  min_width,
unsigned int  min_height,
unsigned int  min_bpp 
) [static]

Select video mode.

Parameters:
min_widthMinimum required width (in pixels)
min_heightMinimum required height (in pixels)
min_bppMinimum required colour depth (in bits per pixel)
Return values:
mode_numberMode number, or negative error

Definition at line 316 of file efi_fbcon.c.

References EFI_SYSTEM_TABLE::BootServices, DBGC, EEFI, efi_systab, efifb_colour_map(), ENOENT, EFI_BOOT_SERVICES::FreePool, efifb::gop, info, INT_MAX, EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE::MaxMode, _EFI_GRAPHICS_OUTPUT_PROTOCOL::Mode, _EFI_GRAPHICS_OUTPUT_PROTOCOL::QueryMode, rc, size, and strerror().

Referenced by efifb_init().

                                                      {
        EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
        struct fbcon_colour_map map;
        EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
        int best_mode_number = -ENOENT;
        unsigned int best_score = INT_MAX;
        unsigned int score;
        unsigned int mode;
        int bpp;
        UINTN size;
        EFI_STATUS efirc;
        int rc;

        /* Find the best mode */
        for ( mode = 0 ; mode < efifb.gop->Mode->MaxMode ; mode++ ) {

                /* Get mode information */
                if ( ( efirc = efifb.gop->QueryMode ( efifb.gop, mode, &size,
                                                      &info ) ) != 0 ) {
                        rc = -EEFI ( efirc );
                        DBGC ( &efifb, "EFIFB could not get mode %d "
                               "information: %s\n", mode, strerror ( rc ) );
                        goto err_query;
                }

                /* Skip unusable modes */
                bpp = efifb_colour_map ( info, &map );
                if ( bpp < 0 ) {
                        rc = bpp;
                        DBGC ( &efifb, "EFIFB could not build colour map for "
                               "mode %d: %s\n", mode, strerror ( rc ) );
                        goto err_map;
                }

                /* Skip modes not meeting the requirements */
                if ( ( info->HorizontalResolution < min_width ) ||
                     ( info->VerticalResolution < min_height ) ||
                     ( ( ( unsigned int ) bpp ) < min_bpp ) ) {
                        goto err_requirements;
                }

                /* Select this mode if it has the best (i.e. lowest)
                 * score.  We choose the scoring system to favour
                 * modes close to the specified width and height;
                 * within modes of the same width and height we prefer
                 * a higher colour depth.
                 */
                score = ( ( info->HorizontalResolution *
                            info->VerticalResolution ) - bpp );
                if ( score < best_score ) {
                        best_mode_number = mode;
                        best_score = score;
                }

        err_requirements:
        err_map:
                bs->FreePool ( info );
        err_query:
                continue;
        }

        if ( best_mode_number < 0 )
                DBGC ( &efifb, "EFIFB found no suitable mode\n" );
        return best_mode_number;
}
static int efifb_restore ( void  ) [static]

Restore video mode.

Parameters:
rcReturn status code

Definition at line 388 of file efi_fbcon.c.

References DBGC, EEFI, efifb::gop, rc, efifb::saved_mode, _EFI_GRAPHICS_OUTPUT_PROTOCOL::SetMode, and strerror().

Referenced by efifb_fini(), and efifb_init().

                                  {
        EFI_STATUS efirc;
        int rc;

        /* Restore original mode */
        if ( ( efirc = efifb.gop->SetMode ( efifb.gop,
                                            efifb.saved_mode ) ) != 0 ) {
                rc = -EEFI ( efirc );
                DBGC ( &efifb, "EFIFB could not restore mode %d: %s\n",
                       efifb.saved_mode, strerror ( rc ) );
                return rc;
        }

        return 0;
}
static int efifb_init ( struct console_configuration config) [static]

Initialise EFI frame buffer.

Parameters:
configConsole configuration, or NULL to reset
Return values:
rcReturn status code

Definition at line 410 of file efi_fbcon.c.

References EFI_SYSTEM_TABLE::BootServices, DBGC, console_configuration::depth, EEFI, efi_graphics_output_protocol_guid, efi_hii_font_protocol_guid, efi_systab, efifb_colour_map(), efifb_glyphs(), efifb_restore(), efifb_select_mode(), efifb::fbcon, fbcon_fini(), fbcon_init(), efifb::font, EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE::FrameBufferBase, efifb::glyphs, efifb::gop, console_configuration::height, fbcon_geometry::height, efifb::hiifont, EFI_GRAPHICS_OUTPUT_MODE_INFORMATION::HorizontalResolution, info, EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE::Info, fbcon_geometry::len, EFI_BOOT_SERVICES::LocateProtocol, efifb::map, EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE::Mode, _EFI_GRAPHICS_OUTPUT_PROTOCOL::Mode, NULL, phys_to_user(), efifb::pixel, EFI_GRAPHICS_OUTPUT_MODE_INFORMATION::PixelsPerScanLine, rc, efifb::saved_mode, _EFI_GRAPHICS_OUTPUT_PROTOCOL::SetMode, efifb::start, strerror(), fbcon_geometry::stride, ufree(), EFI_GRAPHICS_OUTPUT_MODE_INFORMATION::VerticalResolution, console_configuration::width, and fbcon_geometry::width.

Referenced by efifb_configure().

                                                               {
        EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
        EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
        void *interface;
        int mode;
        int bpp;
        EFI_STATUS efirc;
        int rc;

        /* Locate graphics output protocol */
        if ( ( efirc = bs->LocateProtocol ( &efi_graphics_output_protocol_guid,
                                            NULL, &interface ) ) != 0 ) {
                rc = -EEFI ( efirc );
                DBGC ( &efifb, "EFIFB could not locate graphics output "
                       "protocol: %s\n", strerror ( rc ) );
                goto err_locate_gop;
        }
        efifb.gop = interface;

        /* Locate HII font protocol */
        if ( ( efirc = bs->LocateProtocol ( &efi_hii_font_protocol_guid,
                                            NULL, &interface ) ) != 0 ) {
                rc = -EEFI ( efirc );
                DBGC ( &efifb, "EFIFB could not locate HII font protocol: %s\n",
                       strerror ( rc ) );
                goto err_locate_hiifont;
        }
        efifb.hiifont = interface;

        /* Locate glyphs */
        if ( ( rc = efifb_glyphs() ) != 0 )
                goto err_glyphs;

        /* Save original mode */
        efifb.saved_mode = efifb.gop->Mode->Mode;

        /* Select mode */
        if ( ( mode = efifb_select_mode ( config->width, config->height,
                                          config->depth ) ) < 0 ) {
                rc = mode;
                goto err_select_mode;
        }

        /* Set mode */
        if ( ( efirc = efifb.gop->SetMode ( efifb.gop, mode ) ) != 0 ) {
                rc = -EEFI ( efirc );
                DBGC ( &efifb, "EFIFB could not set mode %d: %s\n",
                       mode, strerror ( rc ) );
                goto err_set_mode;
        }
        info = efifb.gop->Mode->Info;

        /* Populate colour map */
        bpp = efifb_colour_map ( info, &efifb.map );
        if ( bpp < 0 ) {
                rc = bpp;
                DBGC ( &efifb, "EFIFB could not build colour map for "
                       "mode %d: %s\n", mode, strerror ( rc ) );
                goto err_map;
        }

        /* Populate pixel geometry */
        efifb.pixel.width = info->HorizontalResolution;
        efifb.pixel.height = info->VerticalResolution;
        efifb.pixel.len = ( ( bpp + 7 ) / 8 );
        efifb.pixel.stride = ( efifb.pixel.len * info->PixelsPerScanLine );

        /* Populate frame buffer address */
        efifb.start = efifb.gop->Mode->FrameBufferBase;
        DBGC ( &efifb, "EFIFB using mode %d (%dx%d %dbpp at %#08lx)\n",
               mode, efifb.pixel.width, efifb.pixel.height, bpp, efifb.start );

        /* Initialise frame buffer console */
        if ( ( rc = fbcon_init ( &efifb.fbcon, phys_to_user ( efifb.start ),
                                 &efifb.pixel, &efifb.map, &efifb.font,
                                 config ) ) != 0 )
                goto err_fbcon_init;

        return 0;

        fbcon_fini ( &efifb.fbcon );
 err_fbcon_init:
 err_map:
        efifb_restore();
 err_set_mode:
 err_select_mode:
        ufree ( efifb.glyphs );
 err_glyphs:
 err_locate_hiifont:
 err_locate_gop:
        return rc;
}
static void efifb_fini ( void  ) [static]

Finalise EFI frame buffer.

Definition at line 507 of file efi_fbcon.c.

References efifb_restore(), efifb::fbcon, fbcon_fini(), efifb::glyphs, and ufree().

Referenced by efifb_configure().

                                {

        /* Finalise frame buffer console */
        fbcon_fini ( &efifb.fbcon );

        /* Restore saved mode */
        efifb_restore();

        /* Free glyphs */
        ufree ( efifb.glyphs );
}
static void efifb_putchar ( int  character) [static]

Print a character to current cursor position.

Parameters:
characterCharacter

Definition at line 524 of file efi_fbcon.c.

References efifb::fbcon, and fbcon_putchar().

                                            {

        fbcon_putchar ( &efifb.fbcon, character );
}
static int efifb_configure ( struct console_configuration config) [static]

Configure console.

Parameters:
configConsole configuration, or NULL to reset
Return values:
rcReturn status code

Definition at line 535 of file efi_fbcon.c.

References ansicol_reset_magic(), ansicol_set_magic_transparent(), CONSOLE_DISABLED, CONSOLE_DISABLED_OUTPUT, console_driver::disabled, efi_console, efifb_fini(), efifb_init(), console_configuration::height, NULL, console_configuration::pixbuf, rc, and console_configuration::width.

                                                                    {
        int rc;

        /* Reset console, if applicable */
        if ( ! efifb_console.disabled ) {
                efifb_fini();
                efi_console.disabled &= ~CONSOLE_DISABLED_OUTPUT;
                ansicol_reset_magic();
        }
        efifb_console.disabled = CONSOLE_DISABLED;

        /* Do nothing more unless we have a usable configuration */
        if ( ( config == NULL ) ||
             ( config->width == 0 ) || ( config->height == 0 ) ) {
                return 0;
        }

        /* Initialise EFI frame buffer */
        if ( ( rc = efifb_init ( config ) ) != 0 )
                return rc;

        /* Mark console as enabled */
        efifb_console.disabled = 0;
        efi_console.disabled |= CONSOLE_DISABLED_OUTPUT;

        /* Set magic colour to transparent if we have a background picture */
        if ( config->pixbuf )
                ansicol_set_magic_transparent();

        return 0;
}

Variable Documentation

Definition at line 51 of file efi_fbcon.c.

Referenced by efifb_configure().

struct console_driver efifb_console __console_driver
Initial value:
 {
        .usage = CONSOLE_EFIFB,
        .putchar = efifb_putchar,
        .configure = efifb_configure,
        .disabled = CONSOLE_DISABLED,
}

EFI graphics output protocol console driver.

Definition at line 66 of file efi_fbcon.c.

struct efifb efifb [static]

The EFI frame buffer.

Definition at line 92 of file efi_fbcon.c.