[meta-intel] [PATCH 3/6] systemd-boot: load board-specific entry and kernel cmdline

Jianxun Zhang jianxun.zhang at linux.intel.com
Tue Jul 12 10:59:55 PDT 2016


Invoke RMC APIs in this bootloader to query board-specific data
from RMC database(DB) file on ESP. Data can be boot entries or a
global kernel boot command line fragment specific to a type of
board supported in RMC DB.

Bootloader queries a file blob named BOOTENTRY.CONFIG from RMC
DB first. In success, bootloader parses BOOTENTRY.CONFIG to get
name of each boot entry file associated to the type of running
board, and then tries to load the entry into internal config data
structure. Eventually, bootloader shows RMC boot entries as well
as other boot entries added from other sources in boot menu.

BOOTENTRY.CONFIG has a very simple format - every line is a boot
entry file's name. For example, to specify two boot entries in it:

boot.conf
install.conf

Bootloader also seeks another file named KBOOTPARAM in RMC dB.
when it can obtain this file associated to the type of running
board, it appends what in file to the end of kernel command
line before it boots up kernel. The appending is effective on
every boot entry, so it is called "global" cmdline fragment.

When Bootloader doesn't get config, an entry or cmdline fragment
for the type of board, it simply move to next steps.

Signed-off-by: Jianxun Zhang <jianxun.zhang at linux.intel.com>
---
 .../recipes-bsp/systemd-boot/systemd-boot.bbappend |  20 ++
 ...d-boot-Link-RMC-libraries-into-bootloader.patch |  29 +++
 ...d-board-specific-boot-entries-from-RMC-da.patch | 239 +++++++++++++++++++++
 ...pport-global-kernel-command-line-fragment.patch |  64 ++++++
 4 files changed, 352 insertions(+)
 create mode 100644 common/recipes-bsp/systemd-boot/systemd-boot.bbappend
 create mode 100644 common/recipes-bsp/systemd-boot/systemd-boot/0001-sd-boot-Link-RMC-libraries-into-bootloader.patch
 create mode 100644 common/recipes-bsp/systemd-boot/systemd-boot/0002-sd-boot-Load-board-specific-boot-entries-from-RMC-da.patch
 create mode 100644 common/recipes-bsp/systemd-boot/systemd-boot/0003-sd-boot-Support-global-kernel-command-line-fragment.patch

diff --git a/common/recipes-bsp/systemd-boot/systemd-boot.bbappend b/common/recipes-bsp/systemd-boot/systemd-boot.bbappend
new file mode 100644
index 0000000..f42434c
--- /dev/null
+++ b/common/recipes-bsp/systemd-boot/systemd-boot.bbappend
@@ -0,0 +1,20 @@
+DEPENDS += "rmc"
+
+FILESEXTRAPATHS_prepend := "${THISDIR}/systemd-boot:"
+
+EXTRA_OEMAKE += 'EFI_LDFLAGS="-L${STAGING_DIR_HOST}/usr/lib" EFI_CFLAGS="-I${STAGING_INCDIR}/rmc/efi -DRMC_EFI"'
+
+# Pin systemd revision down for systemd-boot recipe.
+# Patches could not be applied cleanly when systemd in OE is updated,
+# though we don't expect a lot of changes could happen in bootloader.
+# RMC is designed to support a large number of types of boards, so we
+# should do explicit update with validation to prevent regression even
+# resolving conflicts for a new tip could be done in a short time.
+
+# Revision: systemd 230 in OE
+SRCREV = "3a74d4fc90cb322a4784a3515bef7118c8f8c5ba"
+
+SRC_URI += "file://0001-sd-boot-Link-RMC-libraries-into-bootloader.patch \
+            file://0002-sd-boot-Load-board-specific-boot-entries-from-RMC-da.patch \
+            file://0003-sd-boot-Support-global-kernel-command-line-fragment.patch \
+           "
diff --git a/common/recipes-bsp/systemd-boot/systemd-boot/0001-sd-boot-Link-RMC-libraries-into-bootloader.patch b/common/recipes-bsp/systemd-boot/systemd-boot/0001-sd-boot-Link-RMC-libraries-into-bootloader.patch
new file mode 100644
index 0000000..b6729d5
--- /dev/null
+++ b/common/recipes-bsp/systemd-boot/systemd-boot/0001-sd-boot-Link-RMC-libraries-into-bootloader.patch
@@ -0,0 +1,29 @@
+From fa25b5301ead85505ddeec77b76b5ea6af78fef1 Mon Sep 17 00:00:00 2001
+From: Jianxun Zhang <jianxun.zhang at linux.intel.com>
+Date: Sat, 21 May 2016 18:52:07 -0700
+Subject: [PATCH 1/3] sd-boot: Link RMC libraries into bootloader
+
+Add two RMC libraries into bootloader binary. EFI stub is not changed
+until we really need rmc in stub.
+
+Signed-off-by: Jianxun Zhang <jianxun.zhang at linux.intel.com>
+---
+ Makefile.am | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index 03341fc..57c107b 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -2677,7 +2677,7 @@ $(top_builddir)/src/boot/efi/%.o: $(top_srcdir)/src/boot/efi/%.c $(addprefix $(t
+ 
+ $(systemd_boot_solib): $(systemd_boot_objects)
+ 	$(AM_V_CCLD)$(LD) $(efi_ldflags) $(systemd_boot_objects) \
+-		-o $@ -lefi -lgnuefi $(shell $(CC) -print-libgcc-file-name); \
++		-o $@ -lefi -lgnuefi -lrmclefi -lrsmpefi $(shell $(CC) -print-libgcc-file-name); \
+ 	nm -D -u $@ | grep ' U ' && exit 1 || :
+ 
+ $(systemd_boot): $(systemd_boot_solib)
+-- 
+2.7.4
+
diff --git a/common/recipes-bsp/systemd-boot/systemd-boot/0002-sd-boot-Load-board-specific-boot-entries-from-RMC-da.patch b/common/recipes-bsp/systemd-boot/systemd-boot/0002-sd-boot-Load-board-specific-boot-entries-from-RMC-da.patch
new file mode 100644
index 0000000..55704ac
--- /dev/null
+++ b/common/recipes-bsp/systemd-boot/systemd-boot/0002-sd-boot-Load-board-specific-boot-entries-from-RMC-da.patch
@@ -0,0 +1,239 @@
+From 3c02c2b1aea109489045525992165cdfac5b321f Mon Sep 17 00:00:00 2001
+From: Jianxun Zhang <jianxun.zhang at linux.intel.com>
+Date: Wed, 1 Jun 2016 16:32:22 -0700
+Subject: [PATCH 2/3] sd-boot: Load board-specific boot entries from RMC
+ database
+
+RMC provides a centralized database file on ESP. The DB contains
+fingerprints and any file blobs associated to physical boards.
+Callers can fetch board-specific data with fingerprint info
+collected from board at runtime if there is any record matched
+board's fingerprint.
+
+To let bootloader know which file blob in RMC should be queried,
+a special config file BOOTENTRY.CONFIG is defined as:
+
+boot.conf
+install.conf
+
+Bootloader calls RMC APIs and other functions to perform these
+tasks before it shows boot menu to user:
+
+(1) Load RMC database file from ESP
+(2) Collect fingerprint data from board
+(3) Query BOOTENTRY.CONFIG from RMC DB with fingerprint
+(4) Parse BOOTENTRY.CONFIG to know names of boot entry files
+(5) Query boot entry files one by one from RMC DB, and add
+    them into sd-boot config data.
+
+The final effect is that bootloader will show board-specific
+boot entries in boot menu to user. User then can choose one
+of them to boot system with the selected configuration.
+
+If any of these steps fails, bootloader simply skips loading
+RMC configs or any entry file not successfully fetched from
+RMC DB.
+
+Signed-off-by: Jianxun Zhang <jianxun.zhang at linux.intel.com>
+---
+ src/boot/efi/boot.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 149 insertions(+)
+
+diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
+index 30c1ead..d8c722a 100644
+--- a/src/boot/efi/boot.c
++++ b/src/boot/efi/boot.c
+@@ -15,6 +15,8 @@
+ 
+ #include <efi.h>
+ #include <efilib.h>
++#include <rmcl.h>
++#include <rsmp.h>
+ 
+ #include "console.h"
+ #include "disk.h"
+@@ -33,6 +35,9 @@ static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-boot
+ 
+ static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE;
+ 
++static CHAR8* rmc_db;
++static rmc_fingerprint_t *rmc_fp;
++
+ enum loader_type {
+         LOADER_UNDEFINED,
+         LOADER_EFI,
+@@ -1702,6 +1707,142 @@ static VOID config_free(Config *config) {
+         FreePool(config->entry_oneshot);
+ }
+ 
++/* Derived from line_get_key_value(), we could consolidate two functions later */
++static CHAR8 *get_line(CHAR8 *content, UINT64 *pos) {
++        CHAR8 *line;
++        UINT64 linelen;
++
++skip:
++        line = content + *pos;
++        if (*line == '\0')
++                return NULL;
++
++        linelen = 0;
++        while (line[linelen] && !strchra((CHAR8 *)"\n\r", line[linelen]))
++               linelen++;
++
++        /* move pos to next line */
++        *pos += linelen;
++        if (content[*pos])
++                (*pos)++;
++
++        /* empty line */
++        if (linelen == 0)
++                goto skip;
++
++        /* terminate line */
++        line[linelen] = '\0';
++
++        /* remove leading whitespace */
++        while (strchra((CHAR8 *)" \t", *line)) {
++                line++;
++                linelen--;
++        }
++
++        /* remove trailing whitespace */
++        while (linelen > 0 && strchra((CHAR8 *)" \t", line[linelen-1]))
++                linelen--;
++        line[linelen] = '\0';
++
++        if (*line == '#')
++                goto skip;
++
++        return line;
++}
++
++/* load rmc database file from ESP and try to get fingerprint. These
++ * are essential information indicating we could query rmc data for
++ * this board at least
++ * return 0 if both database file and fingerprint can be obtained, otherwise
++ * non-zero value is returned.
++ *
++ * Note: db and fp hold valid values only when this function returns 0.
++ * Caller is responsible to free allocated memory pointed by *db and *fp when
++ * this function returns 0.
++ */
++
++static UINTN rmc_initialize(EFI_FILE *root_dir, CHAR8 **db, rmc_fingerprint_t **fp) {
++        UINTN len;
++        EFI_GUID smbios_guid = SMBIOS_TABLE_GUID;
++        EFI_GUID smbios3_guid = SMBIOS3_TABLE_GUID;
++        VOID *smbios_entry = NULL;
++        UINT64 smbios_struct_addr = 0;
++        UINT16 smbios_struct_len = 0;
++        UINTN ret = 1;
++
++        if (!db || !fp)
++                return ret;
++
++        *db = NULL;
++        *fp = NULL;
++
++        /* load rmc database */
++        len = file_read(root_dir, L"\\rmc.db", 0, 0, db);
++
++        if (len <= 0)
++                goto done;
++
++        /* locate smbios entry table, try both 32 and 64 bit */
++        if (LibGetSystemConfigurationTable(&smbios3_guid, &smbios_entry) != EFI_SUCCESS
++                        && LibGetSystemConfigurationTable(&smbios_guid, &smbios_entry) != EFI_SUCCESS)
++                goto done;
++
++        /* call rsmp to get fp */
++        if (rsmp_get_smbios_strcut(smbios_entry, &smbios_struct_addr, &smbios_struct_len))
++                goto done;
++
++        *fp = AllocateZeroPool(sizeof(rmc_fingerprint_t));
++
++        if (rsmp_get_fingerprint_from_smbios_struct((BYTE *) smbios_struct_addr, *fp))
++                goto done;
++
++        ret = 0;
++done:
++        if (ret) {
++                FreePool(*db);
++                FreePool(*fp);
++        }
++
++        return ret;
++}
++
++static VOID config_load_rmc_entries(Config *config, EFI_HANDLE *device, CHAR16 *loaded_image_path, CHAR8 *db, rmc_fingerprint_t *fp) {
++        CHAR8 *boot_entry = NULL;
++        CHAR8 *boot_config = NULL;
++        rmc_policy_file_t rp;
++        CHAR8 *line;
++        UINT64 pos = 0;
++
++        if (!db || !fp)
++                return;
++
++        /* query boot entry config file */
++        if (query_policy_from_db(fp, db, RMC_POLICY_BLOB, "BOOTENTRY.CONFIG", &rp))
++                return;
++
++        /* file blob read from rmc db is not necessarily null-terminated, and we
++         * should keep mem where rmc db lives from change during parsing
++         */
++        boot_config = AllocatePool(rp.blob_len * sizeof(CHAR8) + 1);
++        CopyMem(boot_config, rp.blob, rp.blob_len);
++        boot_config[rp.blob_len] = '\0';
++        /* parse boot entry config */
++        while ((line = get_line(boot_config, &pos))) {
++                if (query_policy_from_db(fp, db, RMC_POLICY_BLOB, (char *)line, &rp))
++                        continue;
++                if (rp.blob_len > 0) {
++                        boot_entry = AllocatePool(rp.blob_len * sizeof(CHAR8) + 1);
++                        CopyMem(boot_entry, rp.blob, rp.blob_len);
++                        boot_entry[rp.blob_len] = '\0';
++                        config_entry_add_from_file(config, device,
++                                        stra_to_str(line), boot_entry,
++                                        loaded_image_path);
++                }
++        }
++
++        return;
++}
++
+ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+         CHAR16 *s;
+         CHAR8 *b;
+@@ -1745,6 +1886,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+                 return EFI_LOAD_ERROR;
+         }
+ 
++        /* Initialize rmc before loading any config */
++        rmc_initialize(root_dir, &rmc_db, &rmc_fp);
+ 
+         /* the filesystem path to this image, to prevent adding ourselves to the menu */
+         loaded_image_path = DevicePathToStr(loaded_image->FilePath);
+@@ -1762,6 +1905,10 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+         /* sort entries after version number */
+         config_sort_entries(&config);
+ 
++        /* load entries from rmc database after sorting, if rmc presents */
++        if (rmc_db && rmc_fp)
++                config_load_rmc_entries(&config, loaded_image->DeviceHandle, loaded_image_path, rmc_db, rmc_fp);
++
+         /* if we find some well-known loaders, add them to the end of the list */
+         config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
+                                      L"auto-windows", 'w', L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
+@@ -1851,6 +1998,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+ out:
+         FreePool(loaded_image_path);
+         config_free(&config);
++        FreePool(rmc_db);
++        FreePool(rmc_fp);
+         uefi_call_wrapper(root_dir->Close, 1, root_dir);
+         uefi_call_wrapper(BS->CloseProtocol, 4, image, &LoadedImageProtocol, image, NULL);
+         return err;
+-- 
+2.7.4
+
diff --git a/common/recipes-bsp/systemd-boot/systemd-boot/0003-sd-boot-Support-global-kernel-command-line-fragment.patch b/common/recipes-bsp/systemd-boot/systemd-boot/0003-sd-boot-Support-global-kernel-command-line-fragment.patch
new file mode 100644
index 0000000..924e705
--- /dev/null
+++ b/common/recipes-bsp/systemd-boot/systemd-boot/0003-sd-boot-Support-global-kernel-command-line-fragment.patch
@@ -0,0 +1,64 @@
+From dc0816837b92c72cbfec21a2f112919512f5ad7f Mon Sep 17 00:00:00 2001
+From: Jianxun Zhang <jianxun.zhang at linux.intel.com>
+Date: Mon, 20 Jun 2016 13:08:20 -0700
+Subject: [PATCH 3/3] sd-boot: Support global kernel command line fragment
+
+Query file blob KBOOTPARAM from RMC. If it exists, we append
+it to the new linux boot entry's cmdline. A boot entry could
+be read from a .conf file on ESP, RMC database, or embedded
+linux image. content in KBOOTPARAM is effective in all of
+these cases.
+
+Signed-off-by: Jianxun Zhang <jianxun.zhang at linux.intel.com>
+---
+ src/boot/efi/boot.c | 34 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 34 insertions(+)
+
+diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
+index d8c722a..ce199a4 100644
+--- a/src/boot/efi/boot.c
++++ b/src/boot/efi/boot.c
+@@ -847,6 +847,40 @@ static VOID config_add_entry(Config *config, ConfigEntry *entry) {
+                         config->entries = ReallocatePool(config->entries,
+                                                          sizeof(VOID *) * config->entry_count, sizeof(VOID *) * i);
+         }
++
++        /* rmc: a linux entry could be added from .conf file or an embedded linux image
++         * we put appending global command line here to cover both of two cases.
++         */
++        if (entry->type == LOADER_LINUX && rmc_db && rmc_fp) {
++                rmc_policy_file_t rmc_kp;
++
++                if (!query_policy_from_db(rmc_fp, rmc_db, RMC_POLICY_BLOB, "KBOOTPARAM", &rmc_kp)) {
++                        CHAR8 *cmdline;
++                        CHAR16 *s;
++                        CHAR16 *t;
++                        CHAR16 *p;
++
++                        cmdline = AllocatePool(rmc_kp.blob_len * sizeof(CHAR8) + 1);
++                        CopyMem(cmdline, rmc_kp.blob, rmc_kp.blob_len);
++                        cmdline[rmc_kp.blob_len] = '\0';
++                        p = stra_to_str(cmdline);
++                        t = p;
++
++                        while (*t) {
++                                if (*t == '\n')
++                                        *t = '\0';
++                                t++;
++                        }
++
++                        s = PoolPrint(L"%s %s", entry->options, p);
++                        FreePool(entry->options);
++                        FreePool(p);
++                        FreePool(cmdline);
++
++                        entry->options = s;
++                }
++        }
++
+         config->entries[config->entry_count++] = entry;
+ }
+ 
+-- 
+2.7.4
+
-- 
2.7.4



More information about the meta-intel mailing list