[meta-intel] [PATCHv5 03/10] systemd-boot: load board-specific entry and kernel cmdline

Saul Wold sgw at linux.intel.com
Wed Aug 3 11:29:34 PDT 2016


On Wed, 2016-08-03 at 11:04 -0700, Jianxun Zhang wrote:
> 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. Once any entry is loaded from RMC DB, bootloader skips
> loading conf files on ESP.
> 
> 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 moves to the next step.
> 
> Signed-off-by: Jianxun Zhang <jianxun.zhang at linux.intel.com>

Reviewed-by: Saul Wold <sgw at linux.intel.com>

> ---
>  .../recipes-bsp/systemd-boot/systemd-boot.bbappend |  20 ++
>  ...d-boot-Link-RMC-libraries-into-bootloader.patch |  31 +++
>  ...d-board-specific-boot-entries-from-RMC-da.patch | 263
> +++++++++++++++++++++
>  ...pport-global-kernel-command-line-fragment.patch |  66 ++++++
>  4 files changed, 380 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..c8867a2
> --- /dev/null
> +++ b/common/recipes-bsp/systemd-boot/systemd-boot/0001-sd-boot-Link-
> RMC-libraries-into-bootloader.patch
> @@ -0,0 +1,31 @@
> +From b7775f24928fca01600cac1077ff3f215aa6362d 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.
> +
> +Upstream-Status: Pending
> +
> +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 305099a..ff21ebd 100644
> +--- a/Makefile.am
> ++++ b/Makefile.am
> +@@ -2802,7 +2802,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..ddad940
> --- /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,263 @@
> +From f714cdc84b791d84099f7461c4f223677456720f 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. Once any entry is loaded successfully from RMC DB,
> +bootloader skips loading any boot entries from ESP.
> +
> +Upstream-Status: Pending
> +
> +Signed-off-by: Jianxun Zhang <jianxun.zhang at linux.intel.com>
> +---
> + src/boot/efi/boot.c | 160
> +++++++++++++++++++++++++++++++++++++++++++++++++++-
> + 1 file changed, 158 insertions(+), 2 deletions(-)
> +
> +diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
> +index 30c1ead..43b0793 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,148 @@ 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;
> ++}
> ++
> ++/* load RMC entries
> ++ * return TRUE when at least one entry is loaded, otherwise, return
> FALSE
> ++ */
> ++static BOOLEAN 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;
> ++        BOOLEAN ret = FALSE;
> ++
> ++        if (!db || !fp)
> ++                return ret;
> ++
> ++        /* query boot entry config file */
> ++        if (query_policy_from_db(fp, db, RMC_POLICY_BLOB,
> "BOOTENTRY.CONFIG", &rp))
> ++                return ret;
> ++
> ++        /* 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);
> ++                        /* tell caller success when a RMC entry is
> loaded */
> ++                        ret = TRUE;
> ++                }
> ++        }
> ++
> ++        return ret;
> ++}
> ++
> + EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table)
> {
> +         CHAR16 *s;
> +         CHAR8 *b;
> +@@ -1714,6 +1861,7 @@ EFI_STATUS efi_main(EFI_HANDLE image,
> EFI_SYSTEM_TABLE *sys_table) {
> +         UINT64 init_usec;
> +         BOOLEAN menu = FALSE;
> +         CHAR16 uuid[37];
> ++        BOOLEAN rmc_entry = FALSE;
>> +         InitializeLib(image, sys_table);
> +         init_usec = time_usec();
> +@@ -1745,6 +1893,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);
> +@@ -1753,11 +1903,15 @@ EFI_STATUS efi_main(EFI_HANDLE image,
> EFI_SYSTEM_TABLE *sys_table) {
> +         ZeroMem(&config, sizeof(Config));
> +         config_load_defaults(&config, root_dir);
>> ++        if (rmc_db && rmc_fp)
> ++                rmc_entry = config_load_rmc_entries(&config,
> loaded_image->DeviceHandle, loaded_image_path, rmc_db, rmc_fp);
> ++
> +         /* scan /EFI/Linux/ directory */
> +         config_entry_add_linux(&config, loaded_image, root_dir);
>> +-        /* scan /loader/entries/\*.conf files */
> +-        config_load_entries(&config, loaded_image->DeviceHandle,
> root_dir, loaded_image_path);
> ++        /* scan /loader/entries/\*.conf files only when no RMC
> entry is loaded */
> ++        if (rmc_entry == FALSE)
> ++                config_load_entries(&config, loaded_image-
> >DeviceHandle, root_dir, loaded_image_path);
>> +         /* sort entries after version number */
> +         config_sort_entries(&config);
> +@@ -1851,6 +2005,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..6d077f1
> --- /dev/null
> +++ b/common/recipes-bsp/systemd-boot/systemd-boot/0003-sd-boot-
> Support-global-kernel-command-line-fragment.patch
> @@ -0,0 +1,66 @@
> +From a38be4fe8ffed142abbba92f7ad91a8f7b8f1ace 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.
> +
> +Upstream-Status: Pending
> +
> +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 43b0793..3dcd9a5 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(VOI
> D *) * 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