If the architecture supports an EFI stub launched version of DRTM, it
can select the kconfig and provide the hooks under its architecture
build.

Have the common code parse a drtm=enforce|auto|off kernel command line
parameter to set the boot behavior. Support commas in the args list since
there will be more options here down the road.

efi_drtm_prepare() is called before doing any decompression or image
relocation. It should figure out if DRTM is supported, policy-permitted,
and how much memory efi_drtm_get_extra_size() should return.

The image handling is revised to allocate extra_size at the end of
the normal image. The architecture can use this to store any
information needed for the DRTM flow. It is always contiguous with
Image, which is a requirement of ARM's specification.

efi_drtm_prepare_launch() is called while still in EFI boot services after
Image is fully prepared in its final location.

efi_drtm_launch() is called after exiting boot services and must
jump into the kernel through the DRTM flow. If it returns, then the
normal launch is tried (auto mode).

The extra_size is allocated directly after the Image. Handle the
trivial zboot flow now, and vmlinux in the following patch.

Signed-off-by: Jason Gunthorpe <[email protected]>
---
 .../admin-guide/kernel-parameters.txt         | 12 +++++++
 drivers/firmware/efi/Kconfig                  | 30 ++++++++++++++++
 drivers/firmware/efi/libstub/efi-stub-entry.c |  4 +++
 .../firmware/efi/libstub/efi-stub-helper.c    | 34 ++++++++++++++++++
 drivers/firmware/efi/libstub/efistub.h        | 36 +++++++++++++++++++
 drivers/firmware/efi/libstub/fdt.c            |  8 ++++-
 drivers/firmware/efi/libstub/zboot.c          | 19 +++++++---
 7 files changed, 137 insertions(+), 6 deletions(-)

diff --git a/Documentation/admin-guide/kernel-parameters.txt 
b/Documentation/admin-guide/kernel-parameters.txt
index 68647ff4bdd24b..a576e06bd43584 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1373,6 +1373,18 @@ Kernel parameters
                        data set with no connector name will be used for
                        any connectors not explicitly specified.
 
+       drtm=           [EFI,EARLY]
+                       Format: { "off" | "auto" | "enforce" }
+                       Control the EFI stub Dynamic Root of Trust for
+                       Measurement (DRTM) launch policy.
+                       off: do not attempt a DRTM launch.
+                       auto: attempt a DRTM launch when supported, but fall
+                       back to a normal boot if preparation or launch fails.
+                       enforce: require a DRTM launch and refuse to fall back
+                       to a normal boot if preparation or launch fails.
+                       The default is selected by the EFI_STUB_DRTM_DEFAULT_*
+                       configuration options.
+
        dscc4.setup=    [NET]
 
        dt_cpu_ftrs=    [PPC,EARLY]
diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
index 3d6fb3ca2806ca..f77fa1ae716843 100644
--- a/drivers/firmware/efi/Kconfig
+++ b/drivers/firmware/efi/Kconfig
@@ -75,6 +75,36 @@ config EFI_GENERIC_STUB
 config EFI_STUB_IMAGE_INFO
        bool
 
+config EFI_STUB_DRTM
+       bool
+
+choice
+       prompt "Default DRTM launch policy"
+       depends on EFI_STUB_DRTM
+       default EFI_STUB_DRTM_DEFAULT_OFF
+       help
+         Select the DRTM policy used when no drtm= option is present on the
+         kernel command line. The command-line option overrides this default.
+
+config EFI_STUB_DRTM_DEFAULT_OFF
+       bool "Off"
+       help
+         Do not attempt a DRTM launch unless it is requested explicitly.
+
+config EFI_STUB_DRTM_DEFAULT_AUTO
+       bool "Automatic with fallback"
+       help
+         Attempt a DRTM launch when the firmware supports it, but fall back to
+         normal boot if preparation or a returnable launch attempt fails.
+
+config EFI_STUB_DRTM_DEFAULT_ENFORCE
+       bool "Enforce"
+       help
+         Require a DRTM launch and refuse normal-boot fallback if preparation
+         or a returnable launch attempt fails.
+
+endchoice
+
 config EFI_ZBOOT
        bool "Enable the generic EFI decompressor"
        depends on EFI_GENERIC_STUB && !ARM
diff --git a/drivers/firmware/efi/libstub/efi-stub-entry.c 
b/drivers/firmware/efi/libstub/efi-stub-entry.c
index 83fade2b0d3b84..5b356c4be78975 100644
--- a/drivers/firmware/efi/libstub/efi-stub-entry.c
+++ b/drivers/firmware/efi/libstub/efi-stub-entry.c
@@ -67,6 +67,10 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
        if (status != EFI_SUCCESS)
                return status;
 
+       status = efi_drtm_prepare();
+       if (status != EFI_SUCCESS)
+               return status;
+
        efi_info("Booting Linux Kernel...\n");
 
        status = handle_kernel_image(&image_addr, &image_size,
diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c 
b/drivers/firmware/efi/libstub/efi-stub-helper.c
index f27f2e1f001997..2dd4a5ac916000 100644
--- a/drivers/firmware/efi/libstub/efi-stub-helper.c
+++ b/drivers/firmware/efi/libstub/efi-stub-helper.c
@@ -27,6 +27,37 @@ static bool efi_disable_pci_dma = 
IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA);
 
 int efi_mem_encrypt;
 
+#ifdef CONFIG_EFI_STUB_DRTM
+enum efi_drtm_policy efi_drtm_policy =
+       IS_ENABLED(CONFIG_EFI_STUB_DRTM_DEFAULT_ENFORCE) ? EFI_DRTM_ENFORCE :
+       IS_ENABLED(CONFIG_EFI_STUB_DRTM_DEFAULT_AUTO) ? EFI_DRTM_AUTO :
+       EFI_DRTM_OFF;
+
+static void efi_drtm_parse_options(char *options)
+{
+       char *keyword;
+
+       while (options) {
+               keyword = options;
+               while (*options && *options != ',')
+                       options++;
+               if (*options)
+                       *options++ = '\0';
+               else
+                       options = NULL;
+
+               if (!strcmp(keyword, "off"))
+                       efi_drtm_policy = EFI_DRTM_OFF;
+               else if (!strcmp(keyword, "auto"))
+                       efi_drtm_policy = EFI_DRTM_AUTO;
+               else if (!strcmp(keyword, "enforce"))
+                       efi_drtm_policy = EFI_DRTM_ENFORCE;
+       }
+}
+#else
+static void efi_drtm_parse_options(char *options) {}
+#endif
+
 bool __pure __efi_soft_reserve_enabled(void)
 {
        return !efi_nosoftreserve;
@@ -89,6 +120,9 @@ efi_status_t efi_parse_options(char const *cmdline)
                                efi_mem_encrypt = 1;
                        else if (parse_option_str(val, "off"))
                                efi_mem_encrypt = -1;
+               } else if (IS_ENABLED(CONFIG_EFI_STUB_DRTM) &&
+                          !strcmp(param, "drtm") && val) {
+                       efi_drtm_parse_options(val);
                } else if (!strcmp(param, "efi") && val) {
                        efi_nochunk = parse_option_str(val, "nochunk");
                        efi_novamap |= parse_option_str(val, "novamap");
diff --git a/drivers/firmware/efi/libstub/efistub.h 
b/drivers/firmware/efi/libstub/efistub.h
index da01d6005a62af..f3a6aefdc052ca 100644
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -1078,6 +1078,42 @@ efi_status_t check_platform_features(void);
 
 void *get_efi_config_table(efi_guid_t guid);
 
+enum efi_drtm_policy {
+       EFI_DRTM_OFF,
+       EFI_DRTM_AUTO,
+       EFI_DRTM_ENFORCE,
+};
+
+#ifdef CONFIG_EFI_STUB_DRTM
+extern enum efi_drtm_policy efi_drtm_policy;
+efi_status_t efi_drtm_prepare(void);
+unsigned long efi_drtm_get_extra_size(void);
+efi_status_t efi_drtm_prepare_launch(unsigned long image_base,
+                                    unsigned long fdt_addr);
+void efi_drtm_launch(void);
+#else
+enum {efi_drtm_policy = EFI_DRTM_OFF};
+static inline efi_status_t efi_drtm_prepare(void)
+{
+       return EFI_SUCCESS;
+}
+
+static inline unsigned long efi_drtm_get_extra_size(void)
+{
+       return 0;
+}
+
+static inline efi_status_t
+efi_drtm_prepare_launch(unsigned long image_base, unsigned long fdt_addr)
+{
+       return EFI_SUCCESS;
+}
+
+static inline void efi_drtm_launch(void)
+{
+}
+#endif
+
 /* NOTE: These functions do not print a trailing newline after the string */
 void efi_char16_puts(efi_char16_t *);
 void efi_puts(const char *str);
diff --git a/drivers/firmware/efi/libstub/fdt.c 
b/drivers/firmware/efi/libstub/fdt.c
index 5b2dd709d7b151..15bb331f2dc56c 100644
--- a/drivers/firmware/efi/libstub/fdt.c
+++ b/drivers/firmware/efi/libstub/fdt.c
@@ -223,6 +223,7 @@ static
 efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
                                            efi_loaded_image_t *image,
                                            unsigned long *new_fdt_addr,
+                                           unsigned long kernel_addr,
                                            char *cmdline_ptr)
 {
        unsigned long desc_size;
@@ -289,6 +290,10 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
                goto fail_free_new_fdt;
        }
 
+       status = efi_drtm_prepare_launch(kernel_addr, *new_fdt_addr);
+       if (status != EFI_SUCCESS)
+               goto fail_free_new_fdt;
+
        priv.new_fdt_addr = (void *)*new_fdt_addr;
 
        status = efi_exit_boot_services(handle, &priv, exit_boot_func);
@@ -350,7 +355,7 @@ efi_status_t efi_boot_kernel(void *handle, 
efi_loaded_image_t *image,
        efi_status_t status;
 
        status = allocate_new_fdt_and_exit_boot(handle, image, &fdt_addr,
-                                               cmdline_ptr);
+                                               kernel_addr, cmdline_ptr);
        if (status != EFI_SUCCESS) {
                efi_err("Failed to update FDT and exit boot services\n");
                return status;
@@ -359,6 +364,7 @@ efi_status_t efi_boot_kernel(void *handle, 
efi_loaded_image_t *image,
        if (IS_ENABLED(CONFIG_ARM))
                efi_handle_post_ebs_state();
 
+       efi_drtm_launch();
        efi_enter_kernel(kernel_addr, fdt_addr, fdt_totalsize((void 
*)fdt_addr));
        /* not reached */
 }
diff --git a/drivers/firmware/efi/libstub/zboot.c 
b/drivers/firmware/efi/libstub/zboot.c
index 960a542881d875..6ca231ad593b20 100644
--- a/drivers/firmware/efi/libstub/zboot.c
+++ b/drivers/firmware/efi/libstub/zboot.c
@@ -35,7 +35,7 @@ asmlinkage efi_status_t __efiapi
 efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab)
 {
        char *cmdline_ptr __free(efi_pool) = NULL;
-       unsigned long image_base, alloc_size;
+       unsigned long image_base, image_size, alloc_size;
        efi_loaded_image_t *image;
        efi_status_t status;
 
@@ -52,12 +52,17 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t 
*systab)
        if (status != EFI_SUCCESS)
                return status;
 
-       efi_info("Decompressing Linux Kernel...\n");
-
-       status = efi_zboot_decompress_init(&alloc_size);
+       status = efi_drtm_prepare();
        if (status != EFI_SUCCESS)
                return status;
 
+       efi_info("Decompressing Linux Kernel...\n");
+
+       status = efi_zboot_decompress_init(&image_size);
+       if (status != EFI_SUCCESS)
+               return status;
+       alloc_size = image_size + efi_drtm_get_extra_size();
+
         // If the architecture has a preferred address for the image,
         // try that first.
        image_base = alloc_preferred_address(alloc_size);
@@ -92,8 +97,12 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t 
*systab)
        }
 
        // Decompress the payload into the newly allocated buffer
-       status = efi_zboot_decompress((void *)image_base, alloc_size);
+       status = efi_zboot_decompress((void *)image_base, image_size);
        if (status == EFI_SUCCESS) {
+               /*
+                * Have to sync the entire allocation because the sync also
+                * remaps and changes the permissions.
+                */
                efi_cache_sync_image(image_base, alloc_size);
                status =
                        efi_stub_common(handle, image, image_base, cmdline_ptr);
-- 
2.43.0


Reply via email to