From: Ard Biesheuvel <[email protected]>

Invoke the Secure Launch protocol exposed by the boot loader at the
appropriate time to perform a measured launch of the decompressed
kernel after ExitBootServices().

Co-developed-by: Ross Philipson <[email protected]>
Signed-off-by: Ard Biesheuvel <[email protected]>
Signed-off-by: Ross Philipson <[email protected]>
---
 drivers/firmware/efi/libstub/Makefile      |  1 +
 drivers/firmware/efi/libstub/efistub.h     | 24 ++++++++++++++
 drivers/firmware/efi/libstub/x86-slaunch.c | 38 ++++++++++++++++++++++
 drivers/firmware/efi/libstub/x86-stub.c    | 27 ++++++++++++---
 4 files changed, 86 insertions(+), 4 deletions(-)
 create mode 100644 drivers/firmware/efi/libstub/x86-slaunch.c

diff --git a/drivers/firmware/efi/libstub/Makefile 
b/drivers/firmware/efi/libstub/Makefile
index e386ffd009b7..fd5eaf3142b2 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -86,6 +86,7 @@ lib-$(CONFIG_ARM)             += arm32-stub.o
 lib-$(CONFIG_ARM64)            += kaslr.o arm64.o arm64-stub.o smbios.o
 lib-$(CONFIG_X86)              += x86-stub.o smbios.o
 lib-$(CONFIG_X86_64)           += x86-5lvl.o
+lib-$(CONFIG_SECURE_LAUNCH)    += x86-slaunch.o
 lib-$(CONFIG_RISCV)            += kaslr.o riscv.o riscv-stub.o
 lib-$(CONFIG_LOONGARCH)                += loongarch.o loongarch-stub.o
 
diff --git a/drivers/firmware/efi/libstub/efistub.h 
b/drivers/firmware/efi/libstub/efistub.h
index 979a21818cc1..18301ba3ae0f 100644
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -1267,4 +1267,28 @@ void arch_accept_memory(phys_addr_t start, phys_addr_t 
end);
 efi_status_t efi_zboot_decompress_init(unsigned long *alloc_size);
 efi_status_t efi_zboot_decompress(u8 *out, unsigned long outlen);
 
+#ifdef CONFIG_SECURE_LAUNCH
+efi_status_t efi_secure_launch_init(efi_handle_t image_handle);
+efi_status_t efi_secure_launch_prepare(struct boot_params *boot_params,
+                                      phys_addr_t base);
+void efi_secure_launch(void);
+#else
+static inline
+efi_status_t efi_secure_launch_init(efi_handle_t image_handle)
+{
+       return EFI_UNSUPPORTED;
+}
+
+static inline
+efi_status_t efi_secure_launch_prepare(struct boot_params *boot_params,
+                                      phys_addr_t base)
+{
+       return EFI_SUCCESS;
+}
+
+static inline void efi_secure_launch(void)
+{
+}
+#endif
+
 #endif
diff --git a/drivers/firmware/efi/libstub/x86-slaunch.c 
b/drivers/firmware/efi/libstub/x86-slaunch.c
new file mode 100644
index 000000000000..98ff15f94996
--- /dev/null
+++ b/drivers/firmware/efi/libstub/x86-slaunch.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/efi.h>
+#include <linux/pci.h>
+#include <linux/stddef.h>
+#include <linux/slr_efi.h>
+#include <linux/slaunch.h>
+
+#include <asm/boot.h>
+#include <asm/bootparam.h>
+#include <asm/efi.h>
+
+#include "efistub.h"
+
+static struct efi_slaunch_protocol *slaunch;
+
+efi_status_t efi_secure_launch_init(efi_handle_t image_handle)
+{
+       return efi_bs_call(handle_protocol, image_handle,
+                          &EFI_SLAUNCH_PROTOCOL_GUID, (void **)&slaunch);
+}
+
+efi_status_t efi_secure_launch_prepare(struct boot_params *boot_params,
+                                      phys_addr_t base)
+{
+       if (!slaunch)
+               return EFI_SUCCESS;
+
+       return slaunch->setup_dlme(slaunch, base, mle_header_offset, 
(u64)boot_params);
+}
+
+void efi_secure_launch(void)
+{
+       if (!slaunch)
+               return;
+
+       slaunch->launch(slaunch);
+}
diff --git a/drivers/firmware/efi/libstub/x86-stub.c 
b/drivers/firmware/efi/libstub/x86-stub.c
index cef32e2c82d8..339e63ae84ef 100644
--- a/drivers/firmware/efi/libstub/x86-stub.c
+++ b/drivers/firmware/efi/libstub/x86-stub.c
@@ -833,7 +833,8 @@ static efi_status_t parse_options(const char *cmdline)
 }
 
 static efi_status_t efi_decompress_kernel(unsigned long *kernel_entry,
-                                         struct boot_params *boot_params)
+                                         struct boot_params *boot_params,
+                                         unsigned long alloc_limit)
 {
        unsigned long virt_addr = LOAD_PHYSICAL_ADDR;
        unsigned long addr, alloc_size, entry;
@@ -877,8 +878,7 @@ static efi_status_t efi_decompress_kernel(unsigned long 
*kernel_entry,
 
        status = efi_random_alloc(alloc_size, CONFIG_PHYSICAL_ALIGN, &addr,
                                  seed[0], EFI_LOADER_CODE,
-                                 LOAD_PHYSICAL_ADDR,
-                                 EFI_X86_KERNEL_ALLOC_LIMIT);
+                                 LOAD_PHYSICAL_ADDR, alloc_limit);
        if (status != EFI_SUCCESS)
                return status;
 
@@ -890,6 +890,10 @@ static efi_status_t efi_decompress_kernel(unsigned long 
*kernel_entry,
 
        *kernel_entry = addr + entry;
 
+       status = efi_secure_launch_prepare(boot_params, addr);
+       if (status != EFI_SUCCESS)
+               return status;
+
        return efi_adjust_memory_range_protection(addr, kernel_text_size) ?:
               efi_adjust_memory_range_protection(addr + kernel_inittext_offset,
                                                  kernel_inittext_size);
@@ -914,6 +918,7 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
                               struct boot_params *boot_params)
 
 {
+       unsigned long alloc_limit = EFI_X86_KERNEL_ALLOC_LIMIT;
        efi_guid_t guid = EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID;
        const struct linux_efi_initrd *initrd = NULL;
        unsigned long kernel_entry;
@@ -925,6 +930,17 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
        if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
                efi_exit(handle, EFI_INVALID_PARAMETER);
 
+       status = efi_secure_launch_init(handle);
+       switch (status) {
+       case EFI_SUCCESS:
+               alloc_limit = U32_MAX;
+               break;
+       case EFI_UNSUPPORTED:
+               break;
+       default:
+               efi_exit(handle, status);
+       }
+
        if (!IS_ENABLED(CONFIG_EFI_HANDOVER_PROTOCOL) || !boot_params) {
                status = efi_allocate_bootparams(handle, &boot_params);
                if (status != EFI_SUCCESS)
@@ -974,7 +990,7 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
        if (efi_mem_encrypt > 0)
                hdr->xloadflags |= XLF_MEM_ENCRYPTION;
 
-       status = efi_decompress_kernel(&kernel_entry, boot_params);
+       status = efi_decompress_kernel(&kernel_entry, boot_params, alloc_limit);
        if (status != EFI_SUCCESS) {
                efi_err("Failed to decompress kernel\n");
                goto fail;
@@ -1029,6 +1045,9 @@ void __noreturn efi_stub_entry(efi_handle_t handle,
                goto fail;
        }
 
+       /* If a Secure Launch is in progress, this never returns */
+       efi_secure_launch();
+
        /*
         * Call the SEV init code while still running with the firmware's
         * GDT/IDT, so #VC exceptions will be handled by EFI.
-- 
2.47.3


Reply via email to