diff options
author | Kay Sievers <kay@vrfy.org> | 2013-12-15 19:15:30 +0100 |
---|---|---|
committer | Kay Sievers <kay@vrfy.org> | 2013-12-15 23:32:37 +0100 |
commit | e51ec5722c7f90d89aadf4178cf47e919c551b1f (patch) | |
tree | 195243328ce582e3875466927b1aec5a574d5a3a | |
parent | baba0868df6c6eb97a02832a42ed2de560d36175 (diff) |
add splash support
-rw-r--r-- | src/efi/graphics.c | 221 | ||||
-rw-r--r-- | src/efi/graphics.h | 1 | ||||
-rw-r--r-- | src/efi/gummiboot.c | 23 | ||||
-rw-r--r-- | src/efi/util.c | 11 | ||||
-rw-r--r-- | src/efi/util.h | 2 |
5 files changed, 251 insertions, 7 deletions
diff --git a/src/efi/graphics.c b/src/efi/graphics.c index ac12cf2..22b4642 100644 --- a/src/efi/graphics.c +++ b/src/efi/graphics.c @@ -20,6 +20,7 @@ #include <efi.h> #include <efilib.h> +#include "util.h" #include "graphics.h" EFI_STATUS graphics_mode(BOOLEAN on) { @@ -82,3 +83,223 @@ EFI_STATUS graphics_mode(BOOLEAN on) { return uefi_call_wrapper(ConsoleControl->SetMode, 2, ConsoleControl, new); } + +struct bmp_file { + CHAR8 signature[2]; + UINT32 size; + UINT16 reserved[2]; + UINT32 offset; +} __attribute__((packed)); + +struct bmp_dib { + UINT32 size; + UINT32 x; + UINT32 y; + UINT16 planes; + UINT16 depth; + UINT32 compression; + UINT32 image_size; + INT32 x_pixel_meter; + INT32 y_pixel_meter; + UINT32 colors_used; + UINT32 colors_important; +} __attribute__((packed)); + +struct bmp_map { + UINT8 blue; + UINT8 green; + UINT8 red; + UINT8 reserved; +} __attribute__((packed)); + +EFI_STATUS bmp_to_blt(UINT8 *bmp, UINTN size, + VOID **blt, UINTN *blt_size, + UINTN *blt_x, UINTN *blt_y) { + struct bmp_file *file; + struct bmp_dib *dib; + struct bmp_map *map; + UINTN row_size; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf; + UINT64 buf_size; + UINT8 *in; + UINTN y; + + if (size < sizeof(struct bmp_file) + sizeof(struct bmp_dib)) + return EFI_INVALID_PARAMETER; + + /* check file header */ + file = (struct bmp_file *)bmp; + if (file->signature[0] != 'B' || file->signature[1] != 'M') + return EFI_INVALID_PARAMETER; + if (file->size != size) + return EFI_INVALID_PARAMETER; + if (file->size < file->offset) + return EFI_INVALID_PARAMETER; + + /* check device-independent bitmap */ + dib = (struct bmp_dib *)(bmp + sizeof(struct bmp_file)); + if (dib->compression != 0) + return EFI_UNSUPPORTED; + if (dib->size != sizeof(struct bmp_dib)) + return EFI_UNSUPPORTED; + + switch (dib->depth) { + case 1: + case 4: + case 8: + case 24: + break; + default: + return EFI_UNSUPPORTED; + } + + row_size = (((dib->depth * dib->x) + 31) / 32) * 4; + if (file->size - file->offset != dib->y * row_size) + return EFI_INVALID_PARAMETER; + if (row_size * dib->y > 64 * 1024 * 1024) + return EFI_INVALID_PARAMETER; + + /* check color table */ + map = (struct bmp_map *)(bmp + sizeof(struct bmp_file) + sizeof(struct bmp_dib)); + if (file->offset < sizeof(struct bmp_file) + sizeof(struct bmp_dib)) + return EFI_INVALID_PARAMETER; + + if (file->offset > sizeof(struct bmp_file) + sizeof(struct bmp_dib)) { + UINT32 map_count; + UINTN map_size; + + switch (dib->depth) { + case 1: + case 4: + case 8: + map_count = 1 << dib->depth; + break; + + default: + map_count = 0; + break; + } + + map_size = file->offset - (sizeof(struct bmp_file) + sizeof(struct bmp_dib)); + if (map_size != sizeof(struct bmp_map) * map_count) + return EFI_INVALID_PARAMETER; + } + + /* EFI buffer */ + buf_size = dib->x * dib->y * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + buf = AllocatePool(buf_size); + if (!buf) + return EFI_OUT_OF_RESOURCES; + + /* transform and copy pixels */ + in = bmp + file->offset; + for (y = 0; y < dib->y; y++) { + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *out; + UINTN row_size; + UINTN x; + + out = &buf[(dib->y - y - 1) * dib->x]; + for (x = 0; x < dib->x; x++, in++, out++) { + switch (dib->depth) { + case 1: { + UINTN i; + + for (i = 0; i < 8 && x < dib->x; i++) { + out->Red = map[((*in) >> (7 - i)) & 1].red; + out->Green = map[((*in) >> (7 - i)) & 1].green; + out->Blue = map[((*in) >> (7 - i)) & 1].blue; + out++; + x++; + } + out--; + x--; + break; + } + + case 4: { + UINTN i; + + i = (*in) >> 4; + out->Red = map[i].red; + out->Green = map[i].green; + out->Blue = map[i].blue; + if (x < (dib->x - 1)) { + out++; + x++; + i = (*in) & 0x0f; + out->Red = map[i].red; + out->Green = map[i].green; + out->Blue = map[i].blue; + } + break; + } + + case 8: + out->Red = map[*in].red; + out->Green = map[*in].green; + out->Blue = map[*in].blue; + break; + + case 24: + out->Red = in[2]; + out->Green = in[1]; + out->Blue = in[0]; + in += 2; + break; + } + } + + /* add row padding */ + row_size = in - (bmp + file->offset); + in += 4 - (row_size % 4); + } + + *blt = buf; + *blt_size = buf_size; + *blt_x = dib->x; + *blt_y = dib->y; + return EFI_SUCCESS; +} + +EFI_STATUS graphics_splash(EFI_FILE *root_dir, CHAR16 *path) { + EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL; + UINT8 *content; + INTN len; + VOID *blt = NULL; + UINTN size; + UINTN x; + UINTN y; + UINTN x_pos = 0; + UINTN y_pos = 0; + EFI_STATUS err; + + err = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput); + if (EFI_ERROR(err)) + goto err; + + len = file_read(root_dir, path, &content); + if (len < 0) + return EFI_LOAD_ERROR; + + err = bmp_to_blt(content, len, &blt, &size, &x, &y); + if (EFI_ERROR(err)) + goto err; + + if(x < GraphicsOutput->Mode->Info->HorizontalResolution) + x_pos = (GraphicsOutput->Mode->Info->HorizontalResolution - x) / 2; + if(y < GraphicsOutput->Mode->Info->VerticalResolution) + y_pos = (GraphicsOutput->Mode->Info->VerticalResolution - y) / 2; + + err = graphics_mode(TRUE); + if (EFI_ERROR(err)) + goto err; + + err = uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput, + blt, EfiBltBufferToVideo, 0, 0, x_pos, y_pos, + x, y, 0); +err: + FreePool(blt); + FreePool(content); + return err; +} diff --git a/src/efi/graphics.h b/src/efi/graphics.h index bd378d4..c5c08b4 100644 --- a/src/efi/graphics.h +++ b/src/efi/graphics.h @@ -21,4 +21,5 @@ #define __GUMMIBOOT_GRAPHICS_H EFI_STATUS graphics_mode(BOOLEAN on); +EFI_STATUS graphics_splash(EFI_FILE *root_dir, CHAR16 *path); #endif diff --git a/src/efi/gummiboot.c b/src/efi/gummiboot.c index 0e72dab..a91dae9 100644 --- a/src/efi/gummiboot.c +++ b/src/efi/gummiboot.c @@ -61,6 +61,7 @@ typedef struct { enum loader_type type; CHAR16 *loader; CHAR16 *options; + CHAR16 *splash; CHAR16 key; EFI_STATUS (*call)(void); BOOLEAN no_autoselect; @@ -76,6 +77,7 @@ typedef struct { UINTN timeout_sec_config; INTN timeout_sec_efivar; CHAR16 *entry_default_pattern; + CHAR16 *splash; CHAR16 *entry_oneshot; CHAR16 *options_edit; CHAR16 *entries_auto; @@ -402,6 +404,8 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) { Print(L"timeout (config): %d\n", config->timeout_sec_config); if (config->entry_default_pattern) Print(L"default pattern: '%s'\n", config->entry_default_pattern); + if (config->splash) + Print(L"splash '%s'\n", config->splash); Print(L"\n"); Print(L"config entry count: %d\n", config->entry_count); @@ -462,6 +466,8 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) { Print(L"loader '%s'\n", entry->loader); if (entry->options) Print(L"options '%s'\n", entry->options); + if (entry->splash) + Print(L"splash '%s'\n", entry->splash); Print(L"auto-select %s\n", entry->no_autoselect ? L"no" : L"yes"); if (entry->call) Print(L"internal call yes\n"); @@ -995,11 +1001,17 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) { FreePool(s); continue; } + if (strcmpa((CHAR8 *)"default", key) == 0) { config->entry_default_pattern = stra_to_str(value); StrLwr(config->entry_default_pattern); continue; } + + if (strcmpa((CHAR8 *)"splash", key) == 0) { + config->splash = stra_to_path(value); + continue; + } } } @@ -1087,6 +1099,12 @@ static VOID config_entry_add_from_file(Config *config, EFI_HANDLE *device, CHAR1 FreePool(new); continue; } + + if (strcmpa((CHAR8 *)"splash", key) == 0) { + FreePool(entry->splash); + entry->splash = stra_to_path(value); + continue; + } } if (entry->type == LOADER_UNDEFINED) { @@ -1745,7 +1763,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { entry->call(); continue; } - } + } else if (entry->splash) + graphics_splash(root_dir, entry->splash); + else if (config.splash) + graphics_splash(root_dir, config.splash); /* export the selected boot entry to the system */ efivar_set(L"LoaderEntrySelected", entry->file, FALSE); diff --git a/src/efi/util.c b/src/efi/util.c index 3a296cb..4715c57 100644 --- a/src/efi/util.c +++ b/src/efi/util.c @@ -280,17 +280,17 @@ CHAR8 *strchra(CHAR8 *s, CHAR8 c) { return NULL; } -UINTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, CHAR8 **content) { +INTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, CHAR8 **content) { EFI_FILE_HANDLE handle; EFI_FILE_INFO *info; CHAR8 *buf; UINTN buflen; EFI_STATUS err; - UINTN len = 0; + UINTN len; err = uefi_call_wrapper(dir->Open, 5, dir, &handle, name, EFI_FILE_MODE_READ, 0ULL); if (EFI_ERROR(err)) - goto out; + return err; info = LibFileInfo(handle); buflen = info->FileSize+1; @@ -301,11 +301,12 @@ UINTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, CHAR8 **content) { buf[buflen] = '\0'; *content = buf; len = buflen; - } else + } else { + len = err; FreePool(buf); + } FreePool(info); uefi_call_wrapper(handle->Close, 1, handle); -out: return len; } diff --git a/src/efi/util.h b/src/efi/util.h index c2e4e70..f3d4255 100644 --- a/src/efi/util.h +++ b/src/efi/util.h @@ -38,5 +38,5 @@ CHAR8 *strchra(CHAR8 *s, CHAR8 c); CHAR16 *stra_to_path(CHAR8 *stra); CHAR16 *stra_to_str(CHAR8 *stra); -UINTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, CHAR8 **content); +INTN file_read(EFI_FILE_HANDLE dir, CHAR16 *name, CHAR8 **content); #endif |