summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKay Sievers <kay@vrfy.org>2013-12-15 19:15:30 +0100
committerKay Sievers <kay@vrfy.org>2013-12-15 23:32:37 +0100
commite51ec5722c7f90d89aadf4178cf47e919c551b1f (patch)
tree195243328ce582e3875466927b1aec5a574d5a3a
parentbaba0868df6c6eb97a02832a42ed2de560d36175 (diff)
add splash support
-rw-r--r--src/efi/graphics.c221
-rw-r--r--src/efi/graphics.h1
-rw-r--r--src/efi/gummiboot.c23
-rw-r--r--src/efi/util.c11
-rw-r--r--src/efi/util.h2
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