[systemd-devel] [PATCH v2 6/8] add GOP mode setting and splash drawing support

Joonas Lahtinen joonas.lahtinen at linux.intel.com
Tue Dec 10 01:23:41 PST 2013


Add support for two new configuration directives gfxmode and splash
which respectively allow setting the screen graphics mode and drawing
splash image during gummiboot execution. See README.gop for more
details.

Signed-off-by: Joonas Lahtinen <joonas.lahtinen at linux.intel.com>
Reviewed-by: Mikko Ylinen <mikko.ylinen at intel.com>
Acked-by: Darren Hart <dvhart at linux.intel.com>
---
 Makefile.am         |   5 ++
 README.gop          |  46 ++++++++++++++++++
 configure.ac        |   7 +++
 src/efi/gop.c       | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/efi/gop.h       |  48 ++++++++++++++++++
 src/efi/gummiboot.c | 100 +++++++++++++++++++++++++++++++++++++-
 6 files changed, 342 insertions(+), 1 deletion(-)
 create mode 100644 README.gop
 create mode 100644 src/efi/gop.c
 create mode 100644 src/efi/gop.h

diff --git a/Makefile.am b/Makefile.am
index a17493d..7922f00 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -76,6 +76,11 @@ efi_loadername = gummiboot$(MACHINE_TYPE_NAME).efi
 efi_sources = \
 	src/efi/gummiboot.c
 
+if ENABLE_GOP
+efi_sources += \
+	src/efi/gop.c
+endif
+
 efi_cppflags = \
 	$(EFI_CPPFLAGS) \
 	-I$(top_builddir) -include config.h \
diff --git a/README.gop b/README.gop
new file mode 100644
index 0000000..fef8882
--- /dev/null
+++ b/README.gop
@@ -0,0 +1,46 @@
+WHAT IS GOP USEFUL FOR?
+
+Using UEFI GOP (Graphics Output Protocol) allows gummiboot to draw splash
+screen instantly after UEFI firmware has finished. This should give the user a
+instant on feeling instead of just seeing a black screen during the boot.
+
+ENABLING MODE SETTING AND SPLASH SCREEN DRAWING
+
+The GOP features, display mode setting and splash drawing, can be enabled 
+globally or per entry by adding a configuration keyword to the respective
+configuration file.
+
+Graphics mode can be attempted to be set with the following line:
+
+	gfxmode 1920x1080
+
+This would attempt to use the GOP protocol to set certain resolution to the
+screen, FullHD in this case.
+
+Splash screen can be drawn to the screen with the following directive:
+
+	splash /splash.bgrx
+
+This would attempt to draw a splash screen from a file named "splash.bgrx"
+located in the EFI System Partition root directory. The file needs to be
+properly encoded into BGRX format which is described below. If the splash
+screen resolution is less than the active screen resolution, the image will
+be drawn centered vertically and horizontally.
+
+BGRX FORMAT
+
+The file format is extremely simple. BGRX file can be identified by the first
+four bytes which are "BGRX" in ASCII encoding. The full format is as follows:
+
+4-byte          Header (should be "BGRX")
+2-byte          Width as 16-bit unsigned integer stored in MSB
+2-byte          Height as 16-bit unsigned integer stored in MSB
+[w*h*4-byte]	Multiple pixels each with 1-byte for B, G, R and X channels in
+                order, where X is discarded.
+
+EXTRA FOR INTEL I915 GRAPHICS
+
+Combined with the i915.fastboot patches for detecting the BIOS/UEFI initialized
+graphics mode and using weston with --current-mode option you should be able to
+keep the graphical splash screen drawn during gummiboot execution to stay on the
+screen until the desktop shell is drawn. How cool is that?
diff --git a/configure.ac b/configure.ac
index f8ce22b..6bf62fa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -131,6 +131,12 @@ AS_IF([test "x$enable_manpages" != xno], [
 AM_CONDITIONAL(ENABLE_MANPAGES, [test "x$have_manpages" = "xyes"])
 
 # ------------------------------------------------------------------------------
+AC_ARG_ENABLE(gop, AS_HELP_STRING([--enable-gop], [enable GOP mode setting and bootsplash]), enable_gop=yes, enable_gop=no)
+AM_CONDITIONAL(ENABLE_GOP, [test "x$enable_gop" = "xyes"])
+AH_TEMPLATE([ENABLE_GOP], [Define to enable support for GOP mode setting and bootsplash.])
+AS_IF([test "x$enable_gop" = "xyes"], [AC_DEFINE([ENABLE_GOP], [1])])
+
+# ------------------------------------------------------------------------------
 AC_CONFIG_FILES([
         Makefile
 ])
@@ -142,6 +148,7 @@ AC_MSG_RESULT([
         prefix:                  ${prefix}
         arch:                    $ARCH
         EFI machine type:        $MACHINE_TYPE_NAME
+        GOP:                     ${enable_gop}
 
         EFI libdir:              ${EFI_LIB_DIR}
         EFI ldsdir:              ${EFI_LDS_DIR}
diff --git a/src/efi/gop.c b/src/efi/gop.c
new file mode 100644
index 0000000..1e54444
--- /dev/null
+++ b/src/efi/gop.c
@@ -0,0 +1,137 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * Simple UEFI boot loader which executes configured EFI images, where the
+ * default entry is selected by a configured pattern (glob) or an on-screen
+ * menu.
+ *
+ * All gummiboot code is LGPL not GPL, to stay out of politics and to give
+ * the freedom of copying code from programs to possible future libraries.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * Copyright (C) 2013 Intel Corporation, EFI GOP parts
+ *        Authored by Joonas Lahtinen <joonas.lahtinen at linux.intel.com>
+ *
+ * "Everything should be made as simple as possible, but not simpler."
+ *   -- Albert Einstein
+ */
+
+#include "gop.h"
+#include <efilib.h>
+
+EFI_STATUS gop_graphics_mode(GopGfxMode *gfxmode) {
+        EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+        EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;
+        EFI_STATUS err;
+        UINTN i;
+
+        if (!gfxmode)
+                return EFI_SUCCESS;
+
+        err = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
+        if (EFI_ERROR(err))
+                return err;
+
+        for (i = 0; i < GraphicsOutput->Mode->MaxMode; ++i) {
+                EFI_GRAPHICS_OUTPUT_MODE_INFORMATION* info;
+                UINTN info_size;
+ 
+                err = uefi_call_wrapper(GraphicsOutput->QueryMode, 4, GraphicsOutput, i, &info_size, &info);
+                if (EFI_ERROR(err))
+                        return err;
+                if ((info->HorizontalResolution == gfxmode->width) && (info->VerticalResolution == gfxmode->height))
+                        break;
+        }
+
+        if (i >= GraphicsOutput->Mode->MaxMode)
+                 return EFI_NOT_FOUND;
+
+        if (GraphicsOutput->Mode->Mode != i) {
+                err = uefi_call_wrapper(GraphicsOutput->SetMode, 2, GraphicsOutput, i);
+                if (EFI_ERROR(err))
+                        return err;
+        }
+        return EFI_SUCCESS;
+}
+
+extern UINTN file_read(EFI_FILE_HANDLE, CHAR16 *, CHAR8 **);
+
+EFI_STATUS gop_draw_splash(EFI_FILE *root_dir, CHAR16 *path) {
+        EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+        EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;
+        EFI_STATUS err;
+        UINT16 w;
+        UINT16 h;
+        UINTN dst_x;
+        UINTN dst_y;
+        UINTN len;
+        CHAR8* content;
+        
+        if (!path || !path[0])
+                return EFI_SUCCESS;
+
+        err = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&GraphicsOutput);
+        if (EFI_ERROR(err))
+                goto err;
+
+        len = file_read(root_dir, path, &content);
+
+        /* must contain at least 4-byte header, 2-byte w and 2-byte h. */
+        if (len < 8) {
+                err = EFI_LOAD_ERROR;
+                if (len > 0)
+                        goto err2;
+                goto err;
+        }
+
+        CHAR8* p;
+        p = content;
+        if(strncmpa((CHAR8*)"BGRX", p, 4)) {
+                err = EFI_LOAD_ERROR;
+                Print(L"\nNot a BGRX splash file: '%s'!\n", path);
+                goto err2;
+        }
+        p += 4;
+
+        w = (UINT16)*p++;
+        w <<= 8;
+        w |= (UINT16)*p++;
+
+        h = (UINT16)*p++;
+        h <<= 8;
+        h |= (UINT16)*p++;
+
+        /* must contain exactly h*w 4-byte BGRX tuples */
+        if ((UINTN)(len - 8) != h*w*4U) {
+                err = EFI_LOAD_ERROR;
+                Print(L"\nTruncated splash file: '%s'!\n", path);
+                goto err2;
+        }
+                
+        if(w >= GraphicsOutput->Mode->Info->HorizontalResolution)
+                dst_x = 0;
+        else
+                dst_x = (GraphicsOutput->Mode->Info->HorizontalResolution - w)/2;
+
+        if(h >= GraphicsOutput->Mode->Info->VerticalResolution)
+                dst_y = 0;
+        else
+                dst_y = (GraphicsOutput->Mode->Info->VerticalResolution - h)/2;
+
+        err = uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput, (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)p,
+                                EfiBltBufferToVideo, 0, 0, dst_x, dst_y, w, h, w*4);
+err2:
+        FreePool(content);
+err:
+        return err;
+}
+
diff --git a/src/efi/gop.h b/src/efi/gop.h
new file mode 100644
index 0000000..76a1d9d
--- /dev/null
+++ b/src/efi/gop.h
@@ -0,0 +1,48 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * Simple UEFI boot loader which executes configured EFI images, where the
+ * default entry is selected by a configured pattern (glob) or an on-screen
+ * menu.
+ *
+ * All gummiboot code is LGPL not GPL, to stay out of politics and to give
+ * the freedom of copying code from programs to possible future libraries.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * Copyright (C) 2013 Intel Corporation
+ *        Authored by Joonas Lahtinen <joonas.lahtinen at linux.intel.com>
+ *
+ * "Everything should be made as simple as possible, but not simpler."
+ *   -- Albert Einstein
+ */
+#include <efi.h>
+
+typedef struct {
+        UINTN width;
+        UINTN height;
+} GopGfxMode;
+
+#ifdef ENABLE_GOP
+EFI_STATUS gop_graphics_mode(GopGfxMode *gfxmode);
+EFI_STATUS gop_draw_splash(EFI_FILE *root_dir, CHAR16 *path);
+#else
+static EFI_STATUS gop_graphics_mode(GopGfxMode *gfxmode) {
+        (void)gfxmode;
+        return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS gop_draw_splash(EFI_FILE *root_dir, CHAR16 *path) {
+        (void)root_dir;
+        (void)path;
+        return EFI_UNSUPPORTED;
+}
+#endif
diff --git a/src/efi/gummiboot.c b/src/efi/gummiboot.c
index 6c66f63..25f2fd2 100644
--- a/src/efi/gummiboot.c
+++ b/src/efi/gummiboot.c
@@ -20,6 +20,8 @@
  *
  * Copyright (C) 2012-2013 Kay Sievers <kay at vrfy.org>
  * Copyright (C) 2012 Harald Hoyer <harald at redhat.com>
+ * Copyright (C) 2013 Intel Corporation, EFI GOP parts
+ *        Authored by Joonas Lahtinen <joonas.lahtinen at linux.intel.com>
  *
  * "Any intelligent fool can make things bigger, more complex, and more violent."
  *   -- Albert Einstein
@@ -36,6 +38,8 @@
 #define EFI_SECURITY_VIOLATION      EFIERR(26)
 #endif
 
+#include "gop.h"
+
 /* magic string to find in the binary image */
 static const char __attribute__((used)) magic[] = "#### LoaderInfo: gummiboot " VERSION " ####";
 
@@ -69,6 +73,8 @@ typedef struct {
         enum loader_type type;
         CHAR16 *loader;
         CHAR16 *options;
+        GopGfxMode *gfxmode;
+        CHAR16 *splash;
         CHAR16 key;
         EFI_STATUS (*call)(void);
         BOOLEAN no_autoselect;
@@ -87,6 +93,8 @@ typedef struct {
         CHAR16 *entry_oneshot;
         CHAR16 *options_edit;
         CHAR16 *entries_auto;
+        GopGfxMode *gfxmode;
+        CHAR16 *splash;
 } Config;
 
 static CHAR16 *stra_to_str(CHAR8 *stra);
@@ -730,6 +738,10 @@ 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->gfxmode)
+                        Print(L"gfxmode                 %dx%d\n", entry->gfxmode->width, entry->gfxmode->height);
+                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");
@@ -1398,7 +1410,7 @@ static INTN atoi(CHAR16  *str)
         }
 
         i = 0;
-        while (c = *(str++)) {
+        while ((c = *(str++))) {
                 if (c >= '0' && c <= '9') {
                         i = (i * 10) + (c - '0');
                 } else {
@@ -1408,6 +1420,36 @@ static INTN atoi(CHAR16  *str)
         return sign ? -i : i;
 }
 
+static GopGfxMode *str_to_gfxmode(CHAR16 *str) {
+        GopGfxMode *gfxmode;
+
+        if (!str)
+                goto err;
+
+        gfxmode = AllocatePool(sizeof(GopGfxMode));
+
+        while(*str == ' ')
+                ++str;
+
+        gfxmode->width = atoi(str);
+        if (!gfxmode->width)
+                goto err2;
+
+        if (*str != 'x' || *str != 'X' || *str != '*')
+                goto err2;
+        str += 1;
+
+        gfxmode->height = atoi(str);
+        if (!gfxmode->height)
+                goto err2;
+
+        return gfxmode;
+err2:
+        FreePool(gfxmode);
+err:
+        return NULL;
+}
+
 static CHAR8 *line_get_key_value(CHAR8 *content, UINTN *pos, CHAR8 **key_ret, CHAR8 **value_ret) {
         CHAR8 *line;
         UINTN linelen;
@@ -1485,6 +1527,18 @@ static VOID config_defaults_load_from_file(Config *config, CHAR8 *content) {
                         StrLwr(config->entry_default_pattern);
                         continue;
                 }
+                if (strcmpa((CHAR8 *)"gfxmode", key) == 0) {
+                        CHAR16 *s;
+
+                        s = stra_to_str(value);
+                        config->gfxmode = str_to_gfxmode(s);
+                        FreePool(s);
+                        continue;
+                }
+                if (strcmpa((CHAR8 *)"splash", key) == 0) {
+                        config->splash = stra_to_path(value);
+                        continue;
+                }
         }
 }
 
@@ -1572,6 +1626,22 @@ 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 (strcmpa((CHAR8 *)"gfxmode", key) == 0) {
+                        CHAR16 *s;
+
+                        s = stra_to_str(value);
+                        FreePool(entry->gfxmode);
+                        entry->gfxmode = str_to_gfxmode(s);
+                        FreePool(s);
+                        continue;
+                }
         }
 
         if (entry->type == LOADER_UNDEFINED) {
@@ -2260,6 +2330,34 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
                                 entry->call();
                                 continue;
                         }
+                } else {
+                        if (config.gfxmode) {
+                                console_mode(CONSOLE_GRAPHICS);
+                                err = gop_graphics_mode(config.gfxmode);
+                                if (err == EFI_SUCCESS) {
+                                        if (config.splash) {
+                                                err = gop_draw_splash(root_dir, config.splash);
+                                                if (EFI_ERROR(err))
+                                                        Print(L"Error drawing splash '%s': %r\n", config.splash, err);
+                                        }
+                                } else {
+                                        Print(L"Error setting graphics mode '%dx%d': %r\n", config.gfxmode->width, config.gfxmode->height, err);
+                                }
+                        }
+                }
+
+                if (entry->gfxmode) {
+                        console_mode(CONSOLE_GRAPHICS);
+                        err = gop_graphics_mode(entry->gfxmode);
+                        if (err == EFI_SUCCESS) {
+                                if (entry->splash) {
+                                        err = gop_draw_splash(root_dir, entry->splash);
+                                        if (EFI_ERROR(err))
+                                                Print(L"Error drawing entry splash '%s': %r\n", entry->splash, err);
+                                }
+                        } else {
+                                Print(L"Error setting entry graphics mode '%dx%d': %r\n", entry->gfxmode->width, entry->gfxmode->height, err);
+                        }
                 }
 
                 /* export the selected boot entry to the system */
-- 
1.8.1.4



More information about the systemd-devel mailing list