EFI updates for v5.19

- Allow runtime services to be re-enabled at boot on RT kernels.
 - Provide access to secrets injected into the boot image by CoCo
   hypervisors (COnfidential COmputing)
 - Use DXE services on x86 to make the boot image executable after
   relocation, if needed.
 - Prefer mirrored memory for randomized allocations.
 - Only randomize the placement of the kernel image on arm64 if the
   loader has not already done so.
 - Add support for obtaining the boot hartid from EFI on RISC-V.
 -----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE+9lifEBpyUIVN1cpw08iOZLZjyQFAmKHRF4ACgkQw08iOZLZ
 jyTAlQv9GSctgp3ItPEG7/dF90f2u/ezaqiyLt1ug3cnOrzZL6cbaQPJt/XtxeMY
 XA4eO8aNrMyioClKu2+KEqQgIiNc30HgwOWMxfZpWBWLVlrx5PhvTbwJB6Wfb8r3
 WFze5lc6X2Yttp3jxUU9jLUTPVTJx8SjyhGwBXbzN63aiGv8+bGjD5e4pPg1axP/
 HvUwVpRzK5uU0ju1IM7BPvIjjAOiciwC+KbLjj8Hm++LIbwju7QHlJWy9oMKD1X5
 yuZsIan2dTM+4OclTji7HlSg6c4IFlhMj7GHGJD62aWNyM0/tZokOCIVY1wITXyS
 KRsxag4gjtkVBRNvAHsRsYe3aZ+jQ5DzhGEGTipNGnj3b8FOecuWFSn5a/aMdNkV
 kMSOAbdjZu8xGllroFWS199BamCb6SHijnbv8EzeWNgJXofwxn8vumdgxXZuHIe9
 md1gP2QIuo3/R15zcgy54buB11JD4PeDV7NuovuTQUzFuvsIyIKbEkLMBwEl3j4N
 TIlijEyI
 =xqxQ
 -----END PGP SIGNATURE-----

Merge tag 'efi-next-for-v5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi

Pull EFI updates from Ard Biesheuvel:

 - Allow runtime services to be re-enabled at boot on RT kernels.

 - Provide access to secrets injected into the boot image by CoCo
   hypervisors (COnfidential COmputing)

 - Use DXE services on x86 to make the boot image executable after
   relocation, if needed.

 - Prefer mirrored memory for randomized allocations.

 - Only randomize the placement of the kernel image on arm64 if the
   loader has not already done so.

 - Add support for obtaining the boot hartid from EFI on RISC-V.

* tag 'efi-next-for-v5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi:
  riscv/efi_stub: Add support for RISCV_EFI_BOOT_PROTOCOL
  efi: stub: prefer mirrored memory for randomized allocations
  efi/arm64: libstub: run image in place if randomized by the loader
  efi: libstub: pass image handle to handle_kernel_image()
  efi: x86: Set the NX-compatibility flag in the PE header
  efi: libstub: ensure allocated memory to be executable
  efi: libstub: declare DXE services table
  efi: Add missing prototype for efi_capsule_setup_info
  docs: security: Add secrets/coco documentation
  efi: Register efi_secret platform device if EFI secret area is declared
  virt: Add efi_secret module to expose confidential computing secrets
  efi: Save location of EFI confidential computing area
  efi: Allow to enable EFI runtime services by default on RT
This commit is contained in:
Linus Torvalds 2022-05-23 11:27:24 -07:00
commit bf2431021c
22 changed files with 876 additions and 20 deletions

View File

@ -0,0 +1,51 @@
What: security/secrets/coco
Date: February 2022
Contact: Dov Murik <dovmurik@linux.ibm.com>
Description:
Exposes confidential computing (coco) EFI secrets to
userspace via securityfs.
EFI can declare memory area used by confidential computing
platforms (such as AMD SEV and SEV-ES) for secret injection by
the Guest Owner during VM's launch. The secrets are encrypted
by the Guest Owner and decrypted inside the trusted enclave,
and therefore are not readable by the untrusted host.
The efi_secret module exposes the secrets to userspace. Each
secret appears as a file under <securityfs>/secrets/coco,
where the filename is the GUID of the entry in the secrets
table. This module is loaded automatically by the EFI driver
if the EFI secret area is populated.
Two operations are supported for the files: read and unlink.
Reading the file returns the content of secret entry.
Unlinking the file overwrites the secret data with zeroes and
removes the entry from the filesystem. A secret cannot be read
after it has been unlinked.
For example, listing the available secrets::
# modprobe efi_secret
# ls -l /sys/kernel/security/secrets/coco
-r--r----- 1 root root 0 Jun 28 11:54 736870e5-84f0-4973-92ec-06879ce3da0b
-r--r----- 1 root root 0 Jun 28 11:54 83c83f7f-1356-4975-8b7e-d3a0b54312c6
-r--r----- 1 root root 0 Jun 28 11:54 9553f55d-3da2-43ee-ab5d-ff17f78864d2
-r--r----- 1 root root 0 Jun 28 11:54 e6f5a162-d67f-4750-a67c-5d065f2a9910
Reading the secret data by reading a file::
# cat /sys/kernel/security/secrets/coco/e6f5a162-d67f-4750-a67c-5d065f2a9910
the-content-of-the-secret-data
Wiping a secret by unlinking a file::
# rm /sys/kernel/security/secrets/coco/e6f5a162-d67f-4750-a67c-5d065f2a9910
# ls -l /sys/kernel/security/secrets/coco
-r--r----- 1 root root 0 Jun 28 11:54 736870e5-84f0-4973-92ec-06879ce3da0b
-r--r----- 1 root root 0 Jun 28 11:54 83c83f7f-1356-4975-8b7e-d3a0b54312c6
-r--r----- 1 root root 0 Jun 28 11:54 9553f55d-3da2-43ee-ab5d-ff17f78864d2
Note: The binary format of the secrets table injected by the
Guest Owner is described in
drivers/virt/coco/efi_secret/efi_secret.c under "Structure of
the EFI secret area".

View File

@ -17,3 +17,4 @@ Security Documentation
tpm/index
digsig
landlock
secrets/index

View File

@ -0,0 +1,103 @@
.. SPDX-License-Identifier: GPL-2.0
==============================
Confidential Computing secrets
==============================
This document describes how Confidential Computing secret injection is handled
from the firmware to the operating system, in the EFI driver and the efi_secret
kernel module.
Introduction
============
Confidential Computing (coco) hardware such as AMD SEV (Secure Encrypted
Virtualization) allows guest owners to inject secrets into the VMs
memory without the host/hypervisor being able to read them. In SEV,
secret injection is performed early in the VM launch process, before the
guest starts running.
The efi_secret kernel module allows userspace applications to access these
secrets via securityfs.
Secret data flow
================
The guest firmware may reserve a designated memory area for secret injection,
and publish its location (base GPA and length) in the EFI configuration table
under a ``LINUX_EFI_COCO_SECRET_AREA_GUID`` entry
(``adf956ad-e98c-484c-ae11-b51c7d336447``). This memory area should be marked
by the firmware as ``EFI_RESERVED_TYPE``, and therefore the kernel should not
be use it for its own purposes.
During the VM's launch, the virtual machine manager may inject a secret to that
area. In AMD SEV and SEV-ES this is performed using the
``KVM_SEV_LAUNCH_SECRET`` command (see [sev]_). The strucutre of the injected
Guest Owner secret data should be a GUIDed table of secret values; the binary
format is described in ``drivers/virt/coco/efi_secret/efi_secret.c`` under
"Structure of the EFI secret area".
On kernel start, the kernel's EFI driver saves the location of the secret area
(taken from the EFI configuration table) in the ``efi.coco_secret`` field.
Later it checks if the secret area is populated: it maps the area and checks
whether its content begins with ``EFI_SECRET_TABLE_HEADER_GUID``
(``1e74f542-71dd-4d66-963e-ef4287ff173b``). If the secret area is populated,
the EFI driver will autoload the efi_secret kernel module, which exposes the
secrets to userspace applications via securityfs. The details of the
efi_secret filesystem interface are in [secrets-coco-abi]_.
Application usage example
=========================
Consider a guest performing computations on encrypted files. The Guest Owner
provides the decryption key (= secret) using the secret injection mechanism.
The guest application reads the secret from the efi_secret filesystem and
proceeds to decrypt the files into memory and then performs the needed
computations on the content.
In this example, the host can't read the files from the disk image
because they are encrypted. Host can't read the decryption key because
it is passed using the secret injection mechanism (= secure channel).
Host can't read the decrypted content from memory because it's a
confidential (memory-encrypted) guest.
Here is a simple example for usage of the efi_secret module in a guest
to which an EFI secret area with 4 secrets was injected during launch::
# ls -la /sys/kernel/security/secrets/coco
total 0
drwxr-xr-x 2 root root 0 Jun 28 11:54 .
drwxr-xr-x 3 root root 0 Jun 28 11:54 ..
-r--r----- 1 root root 0 Jun 28 11:54 736870e5-84f0-4973-92ec-06879ce3da0b
-r--r----- 1 root root 0 Jun 28 11:54 83c83f7f-1356-4975-8b7e-d3a0b54312c6
-r--r----- 1 root root 0 Jun 28 11:54 9553f55d-3da2-43ee-ab5d-ff17f78864d2
-r--r----- 1 root root 0 Jun 28 11:54 e6f5a162-d67f-4750-a67c-5d065f2a9910
# hd /sys/kernel/security/secrets/coco/e6f5a162-d67f-4750-a67c-5d065f2a9910
00000000 74 68 65 73 65 2d 61 72 65 2d 74 68 65 2d 6b 61 |these-are-the-ka|
00000010 74 61 2d 73 65 63 72 65 74 73 00 01 02 03 04 05 |ta-secrets......|
00000020 06 07 |..|
00000022
# rm /sys/kernel/security/secrets/coco/e6f5a162-d67f-4750-a67c-5d065f2a9910
# ls -la /sys/kernel/security/secrets/coco
total 0
drwxr-xr-x 2 root root 0 Jun 28 11:55 .
drwxr-xr-x 3 root root 0 Jun 28 11:54 ..
-r--r----- 1 root root 0 Jun 28 11:54 736870e5-84f0-4973-92ec-06879ce3da0b
-r--r----- 1 root root 0 Jun 28 11:54 83c83f7f-1356-4975-8b7e-d3a0b54312c6
-r--r----- 1 root root 0 Jun 28 11:54 9553f55d-3da2-43ee-ab5d-ff17f78864d2
References
==========
See [sev-api-spec]_ for more info regarding SEV ``LAUNCH_SECRET`` operation.
.. [sev] Documentation/virt/kvm/amd-memory-encryption.rst
.. [secrets-coco-abi] Documentation/ABI/testing/securityfs-secrets-coco
.. [sev-api-spec] https://www.amd.com/system/files/TechDocs/55766_SEV-KM_API_Specification.pdf

View File

@ -0,0 +1,9 @@
.. SPDX-License-Identifier: GPL-2.0
=====================
Secrets documentation
=====================
.. toctree::
coco

View File

@ -163,7 +163,11 @@ extra_header_fields:
.long 0x200 # SizeOfHeaders
.long 0 # CheckSum
.word IMAGE_SUBSYSTEM_EFI_APPLICATION # Subsystem (EFI application)
#ifdef CONFIG_DXE_MEM_ATTRIBUTES
.word IMAGE_DLL_CHARACTERISTICS_NX_COMPAT # DllCharacteristics
#else
.word 0 # DllCharacteristics
#endif
#ifdef CONFIG_X86_32
.long 0 # SizeOfStackReserve
.long 0 # SizeOfStackCommit

View File

@ -357,6 +357,11 @@ static inline u32 efi64_convert_status(efi_status_t status)
runtime), \
func, __VA_ARGS__))
#define efi_dxe_call(func, ...) \
(efi_is_native() \
? efi_dxe_table->func(__VA_ARGS__) \
: __efi64_thunk_map(efi_dxe_table, func, __VA_ARGS__))
#else /* CONFIG_EFI_MIXED */
static inline bool efi_is_64bit(void)

View File

@ -93,6 +93,9 @@ static const unsigned long * const efi_tables[] = {
#ifdef CONFIG_LOAD_UEFI_KEYS
&efi.mokvar_table,
#endif
#ifdef CONFIG_EFI_COCO_SECRET
&efi.coco_secret,
#endif
};
u64 efi_setup; /* efi setup_data physical address */

View File

@ -91,6 +91,18 @@ config EFI_SOFT_RESERVE
If unsure, say Y.
config EFI_DXE_MEM_ATTRIBUTES
bool "Adjust memory attributes in EFISTUB"
depends on EFI && EFI_STUB && X86
default y
help
UEFI specification does not guarantee all memory to be
accessible for both write and execute as the kernel expects
it to be.
Use DXE services to check and alter memory protection
attributes during boot via EFISTUB to ensure that memory
ranges used by the kernel are writable and executable.
config EFI_PARAMS_FROM_FDT
bool
help
@ -284,3 +296,34 @@ config EFI_CUSTOM_SSDT_OVERLAYS
See Documentation/admin-guide/acpi/ssdt-overlays.rst for more
information.
config EFI_DISABLE_RUNTIME
bool "Disable EFI runtime services support by default"
default y if PREEMPT_RT
help
Allow to disable the EFI runtime services support by default. This can
already be achieved by using the efi=noruntime option, but it could be
useful to have this default without any kernel command line parameter.
The EFI runtime services are disabled by default when PREEMPT_RT is
enabled, because measurements have shown that some EFI functions calls
might take too much time to complete, causing large latencies which is
an issue for Real-Time kernels.
This default can be overridden by using the efi=runtime option.
config EFI_COCO_SECRET
bool "EFI Confidential Computing Secret Area Support"
depends on EFI
help
Confidential Computing platforms (such as AMD SEV) allow the
Guest Owner to securely inject secrets during guest VM launch.
The secrets are placed in a designated EFI reserved memory area.
In order to use the secrets in the kernel, the location of the secret
area (as published in the EFI config table) must be kept.
If you say Y here, the address of the EFI secret area will be kept
for usage inside the kernel. This will allow the
virt/coco/efi_secret module to access the secrets, which in turn
allows userspace programs to access the injected secrets.

View File

@ -46,6 +46,9 @@ struct efi __read_mostly efi = {
#ifdef CONFIG_LOAD_UEFI_KEYS
.mokvar_table = EFI_INVALID_TABLE_ADDR,
#endif
#ifdef CONFIG_EFI_COCO_SECRET
.coco_secret = EFI_INVALID_TABLE_ADDR,
#endif
};
EXPORT_SYMBOL(efi);
@ -66,7 +69,7 @@ struct mm_struct efi_mm = {
struct workqueue_struct *efi_rts_wq;
static bool disable_runtime = IS_ENABLED(CONFIG_PREEMPT_RT);
static bool disable_runtime = IS_ENABLED(CONFIG_EFI_DISABLE_RUNTIME);
static int __init setup_noefi(char *arg)
{
disable_runtime = true;
@ -422,6 +425,11 @@ static int __init efisubsys_init(void)
if (efi_enabled(EFI_DBG) && efi_enabled(EFI_PRESERVE_BS_REGIONS))
efi_debugfs_init();
#ifdef CONFIG_EFI_COCO_SECRET
if (efi.coco_secret != EFI_INVALID_TABLE_ADDR)
platform_device_register_simple("efi_secret", 0, NULL, 0);
#endif
return 0;
err_remove_group:
@ -528,6 +536,9 @@ static const efi_config_table_type_t common_tables[] __initconst = {
#endif
#ifdef CONFIG_LOAD_UEFI_KEYS
{LINUX_EFI_MOK_VARIABLE_TABLE_GUID, &efi.mokvar_table, "MOKvar" },
#endif
#ifdef CONFIG_EFI_COCO_SECRET
{LINUX_EFI_COCO_SECRET_AREA_GUID, &efi.coco_secret, "CocoSecret" },
#endif
{},
};

View File

@ -117,7 +117,8 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
unsigned long *image_size,
unsigned long *reserve_addr,
unsigned long *reserve_size,
efi_loaded_image_t *image)
efi_loaded_image_t *image,
efi_handle_t image_handle)
{
const int slack = TEXT_OFFSET - 5 * PAGE_SIZE;
int alloc_size = MAX_UNCOMP_KERNEL_SIZE + EFI_PHYS_ALIGN;

View File

@ -83,7 +83,8 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
unsigned long *image_size,
unsigned long *reserve_addr,
unsigned long *reserve_size,
efi_loaded_image_t *image)
efi_loaded_image_t *image,
efi_handle_t image_handle)
{
efi_status_t status;
unsigned long kernel_size, kernel_memsize = 0;
@ -100,7 +101,15 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
u64 min_kimg_align = efi_nokaslr ? MIN_KIMG_ALIGN : EFI_KIMG_ALIGN;
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
if (!efi_nokaslr) {
efi_guid_t li_fixed_proto = LINUX_EFI_LOADED_IMAGE_FIXED_GUID;
void *p;
if (efi_nokaslr) {
efi_info("KASLR disabled on kernel command line\n");
} else if (efi_bs_call(handle_protocol, image_handle,
&li_fixed_proto, &p) == EFI_SUCCESS) {
efi_info("Image placement fixed by loader\n");
} else {
status = efi_get_random_bytes(sizeof(phys_seed),
(u8 *)&phys_seed);
if (status == EFI_NOT_FOUND) {
@ -111,8 +120,6 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
status);
efi_nokaslr = true;
}
} else {
efi_info("KASLR disabled on kernel command line\n");
}
}

View File

@ -198,7 +198,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
status = handle_kernel_image(&image_addr, &image_size,
&reserve_addr,
&reserve_size,
image);
image, handle);
if (status != EFI_SUCCESS) {
efi_err("Failed to relocate kernel\n");
goto fail_free_screeninfo;

View File

@ -36,6 +36,9 @@ extern bool efi_novamap;
extern const efi_system_table_t *efi_system_table;
typedef union efi_dxe_services_table efi_dxe_services_table_t;
extern const efi_dxe_services_table_t *efi_dxe_table;
efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
efi_system_table_t *sys_table_arg);
@ -44,6 +47,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
#define efi_is_native() (true)
#define efi_bs_call(func, ...) efi_system_table->boottime->func(__VA_ARGS__)
#define efi_rt_call(func, ...) efi_system_table->runtime->func(__VA_ARGS__)
#define efi_dxe_call(func, ...) efi_dxe_table->func(__VA_ARGS__)
#define efi_table_attr(inst, attr) (inst->attr)
#define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__)
@ -329,6 +333,76 @@ union efi_boot_services {
} mixed_mode;
};
typedef enum {
EfiGcdMemoryTypeNonExistent,
EfiGcdMemoryTypeReserved,
EfiGcdMemoryTypeSystemMemory,
EfiGcdMemoryTypeMemoryMappedIo,
EfiGcdMemoryTypePersistent,
EfiGcdMemoryTypeMoreReliable,
EfiGcdMemoryTypeMaximum
} efi_gcd_memory_type_t;
typedef struct {
efi_physical_addr_t base_address;
u64 length;
u64 capabilities;
u64 attributes;
efi_gcd_memory_type_t gcd_memory_type;
void *image_handle;
void *device_handle;
} efi_gcd_memory_space_desc_t;
/*
* EFI DXE Services table
*/
union efi_dxe_services_table {
struct {
efi_table_hdr_t hdr;
void *add_memory_space;
void *allocate_memory_space;
void *free_memory_space;
void *remove_memory_space;
efi_status_t (__efiapi *get_memory_space_descriptor)(efi_physical_addr_t,
efi_gcd_memory_space_desc_t *);
efi_status_t (__efiapi *set_memory_space_attributes)(efi_physical_addr_t,
u64, u64);
void *get_memory_space_map;
void *add_io_space;
void *allocate_io_space;
void *free_io_space;
void *remove_io_space;
void *get_io_space_descriptor;
void *get_io_space_map;
void *dispatch;
void *schedule;
void *trust;
void *process_firmware_volume;
void *set_memory_space_capabilities;
};
struct {
efi_table_hdr_t hdr;
u32 add_memory_space;
u32 allocate_memory_space;
u32 free_memory_space;
u32 remove_memory_space;
u32 get_memory_space_descriptor;
u32 set_memory_space_attributes;
u32 get_memory_space_map;
u32 add_io_space;
u32 allocate_io_space;
u32 free_io_space;
u32 remove_io_space;
u32 get_io_space_descriptor;
u32 get_io_space_map;
u32 dispatch;
u32 schedule;
u32 trust;
u32 process_firmware_volume;
u32 set_memory_space_capabilities;
} mixed_mode;
};
typedef union efi_uga_draw_protocol efi_uga_draw_protocol_t;
union efi_uga_draw_protocol {
@ -720,6 +794,13 @@ union efi_tcg2_protocol {
} mixed_mode;
};
struct riscv_efi_boot_protocol {
u64 revision;
efi_status_t (__efiapi *get_boot_hartid)(struct riscv_efi_boot_protocol *,
unsigned long *boot_hartid);
};
typedef union efi_load_file_protocol efi_load_file_protocol_t;
typedef union efi_load_file_protocol efi_load_file2_protocol_t;
@ -865,7 +946,8 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
unsigned long *image_size,
unsigned long *reserve_addr,
unsigned long *reserve_size,
efi_loaded_image_t *image);
efi_loaded_image_t *image,
efi_handle_t image_handle);
asmlinkage void __noreturn efi_enter_kernel(unsigned long entrypoint,
unsigned long fdt_addr,

View File

@ -56,6 +56,7 @@ efi_status_t efi_random_alloc(unsigned long size,
unsigned long random_seed)
{
unsigned long map_size, desc_size, total_slots = 0, target_slot;
unsigned long total_mirrored_slots = 0;
unsigned long buff_size;
efi_status_t status;
efi_memory_desc_t *memory_map;
@ -86,8 +87,14 @@ efi_status_t efi_random_alloc(unsigned long size,
slots = get_entry_num_slots(md, size, ilog2(align));
MD_NUM_SLOTS(md) = slots;
total_slots += slots;
if (md->attribute & EFI_MEMORY_MORE_RELIABLE)
total_mirrored_slots += slots;
}
/* consider only mirrored slots for randomization if any exist */
if (total_mirrored_slots > 0)
total_slots = total_mirrored_slots;
/* find a random number between 0 and total_slots */
target_slot = (total_slots * (u64)(random_seed & U32_MAX)) >> 32;
@ -107,6 +114,10 @@ efi_status_t efi_random_alloc(unsigned long size,
efi_physical_addr_t target;
unsigned long pages;
if (total_mirrored_slots > 0 &&
!(md->attribute & EFI_MEMORY_MORE_RELIABLE))
continue;
if (target_slot >= MD_NUM_SLOTS(md)) {
target_slot -= MD_NUM_SLOTS(md);
continue;

View File

@ -21,9 +21,9 @@
#define MIN_KIMG_ALIGN SZ_4M
#endif
typedef void __noreturn (*jump_kernel_func)(unsigned int, unsigned long);
typedef void __noreturn (*jump_kernel_func)(unsigned long, unsigned long);
static u32 hartid;
static unsigned long hartid;
static int get_boot_hartid_from_fdt(void)
{
@ -47,14 +47,31 @@ static int get_boot_hartid_from_fdt(void)
return 0;
}
static efi_status_t get_boot_hartid_from_efi(void)
{
efi_guid_t boot_protocol_guid = RISCV_EFI_BOOT_PROTOCOL_GUID;
struct riscv_efi_boot_protocol *boot_protocol;
efi_status_t status;
status = efi_bs_call(locate_protocol, &boot_protocol_guid, NULL,
(void **)&boot_protocol);
if (status != EFI_SUCCESS)
return status;
return efi_call_proto(boot_protocol, get_boot_hartid, &hartid);
}
efi_status_t check_platform_features(void)
{
efi_status_t status;
int ret;
ret = get_boot_hartid_from_fdt();
if (ret) {
efi_err("/chosen/boot-hartid missing or invalid!\n");
return EFI_UNSUPPORTED;
status = get_boot_hartid_from_efi();
if (status != EFI_SUCCESS) {
ret = get_boot_hartid_from_fdt();
if (ret) {
efi_err("Failed to get boot hartid!\n");
return EFI_UNSUPPORTED;
}
}
return EFI_SUCCESS;
}
@ -80,7 +97,8 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
unsigned long *image_size,
unsigned long *reserve_addr,
unsigned long *reserve_size,
efi_loaded_image_t *image)
efi_loaded_image_t *image,
efi_handle_t image_handle)
{
unsigned long kernel_size = 0;
unsigned long preferred_addr;

View File

@ -22,6 +22,7 @@
#define MAXMEM_X86_64_4LEVEL (1ull << 46)
const efi_system_table_t *efi_system_table;
const efi_dxe_services_table_t *efi_dxe_table;
extern u32 image_offset;
static efi_loaded_image_t *image = NULL;
@ -211,9 +212,110 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params)
}
}
static void
adjust_memory_range_protection(unsigned long start, unsigned long size)
{
efi_status_t status;
efi_gcd_memory_space_desc_t desc;
unsigned long end, next;
unsigned long rounded_start, rounded_end;
unsigned long unprotect_start, unprotect_size;
int has_system_memory = 0;
if (efi_dxe_table == NULL)
return;
rounded_start = rounddown(start, EFI_PAGE_SIZE);
rounded_end = roundup(start + size, EFI_PAGE_SIZE);
/*
* Don't modify memory region attributes, they are
* already suitable, to lower the possibility to
* encounter firmware bugs.
*/
for (end = start + size; start < end; start = next) {
status = efi_dxe_call(get_memory_space_descriptor, start, &desc);
if (status != EFI_SUCCESS)
return;
next = desc.base_address + desc.length;
/*
* Only system memory is suitable for trampoline/kernel image placement,
* so only this type of memory needs its attributes to be modified.
*/
if (desc.gcd_memory_type != EfiGcdMemoryTypeSystemMemory ||
(desc.attributes & (EFI_MEMORY_RO | EFI_MEMORY_XP)) == 0)
continue;
unprotect_start = max(rounded_start, (unsigned long)desc.base_address);
unprotect_size = min(rounded_end, next) - unprotect_start;
status = efi_dxe_call(set_memory_space_attributes,
unprotect_start, unprotect_size,
EFI_MEMORY_WB);
if (status != EFI_SUCCESS) {
efi_warn("Unable to unprotect memory range [%08lx,%08lx]: %d\n",
unprotect_start,
unprotect_start + unprotect_size,
(int)status);
}
}
}
/*
* Trampoline takes 2 pages and can be loaded in first megabyte of memory
* with its end placed between 128k and 640k where BIOS might start.
* (see arch/x86/boot/compressed/pgtable_64.c)
*
* We cannot find exact trampoline placement since memory map
* can be modified by UEFI, and it can alter the computed address.
*/
#define TRAMPOLINE_PLACEMENT_BASE ((128 - 8)*1024)
#define TRAMPOLINE_PLACEMENT_SIZE (640*1024 - (128 - 8)*1024)
void startup_32(struct boot_params *boot_params);
static void
setup_memory_protection(unsigned long image_base, unsigned long image_size)
{
/*
* Allow execution of possible trampoline used
* for switching between 4- and 5-level page tables
* and relocated kernel image.
*/
adjust_memory_range_protection(TRAMPOLINE_PLACEMENT_BASE,
TRAMPOLINE_PLACEMENT_SIZE);
#ifdef CONFIG_64BIT
if (image_base != (unsigned long)startup_32)
adjust_memory_range_protection(image_base, image_size);
#else
/*
* Clear protection flags on a whole range of possible
* addresses used for KASLR. We don't need to do that
* on x86_64, since KASLR/extraction is performed after
* dedicated identity page tables are built and we only
* need to remove possible protection on relocated image
* itself disregarding further relocations.
*/
adjust_memory_range_protection(LOAD_PHYSICAL_ADDR,
KERNEL_IMAGE_SIZE - LOAD_PHYSICAL_ADDR);
#endif
}
static const efi_char16_t apple[] = L"Apple";
static void setup_quirks(struct boot_params *boot_params)
static void setup_quirks(struct boot_params *boot_params,
unsigned long image_base,
unsigned long image_size)
{
efi_char16_t *fw_vendor = (efi_char16_t *)(unsigned long)
efi_table_attr(efi_system_table, fw_vendor);
@ -222,6 +324,9 @@ static void setup_quirks(struct boot_params *boot_params)
if (IS_ENABLED(CONFIG_APPLE_PROPERTIES))
retrieve_apple_device_properties(boot_params);
}
if (IS_ENABLED(CONFIG_EFI_DXE_MEM_ATTRIBUTES))
setup_memory_protection(image_base, image_size);
}
/*
@ -341,8 +446,6 @@ static void __noreturn efi_exit(efi_handle_t handle, efi_status_t status)
asm("hlt");
}
void startup_32(struct boot_params *boot_params);
void __noreturn efi_stub_entry(efi_handle_t handle,
efi_system_table_t *sys_table_arg,
struct boot_params *boot_params);
@ -677,11 +780,17 @@ unsigned long efi_main(efi_handle_t handle,
efi_status_t status;
efi_system_table = sys_table_arg;
/* Check if we were booted by the EFI firmware */
if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
efi_exit(handle, EFI_INVALID_PARAMETER);
efi_dxe_table = get_efi_config_table(EFI_DXE_SERVICES_TABLE_GUID);
if (efi_dxe_table &&
efi_dxe_table->hdr.signature != EFI_DXE_SERVICES_TABLE_SIGNATURE) {
efi_warn("Ignoring DXE services table: invalid signature\n");
efi_dxe_table = NULL;
}
/*
* If the kernel isn't already loaded at a suitable address,
* relocate it.
@ -791,7 +900,7 @@ unsigned long efi_main(efi_handle_t handle,
setup_efi_pci(boot_params);
setup_quirks(boot_params);
setup_quirks(boot_params, bzimage_addr, buffer_end - buffer_start);
status = exit_boot(boot_params, handle);
if (status != EFI_SUCCESS) {

View File

@ -47,4 +47,7 @@ source "drivers/virt/vboxguest/Kconfig"
source "drivers/virt/nitro_enclaves/Kconfig"
source "drivers/virt/acrn/Kconfig"
source "drivers/virt/coco/efi_secret/Kconfig"
endif

View File

@ -9,3 +9,4 @@ obj-y += vboxguest/
obj-$(CONFIG_NITRO_ENCLAVES) += nitro_enclaves/
obj-$(CONFIG_ACRN_HSM) += acrn/
obj-$(CONFIG_EFI_SECRET) += coco/efi_secret/

View File

@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0-only
config EFI_SECRET
tristate "EFI secret area securityfs support"
depends on EFI && X86_64
select EFI_COCO_SECRET
select SECURITYFS
help
This is a driver for accessing the EFI secret area via securityfs.
The EFI secret area is a memory area designated by the firmware for
confidential computing secret injection (for example for AMD SEV
guests). The driver exposes the secrets as files in
<securityfs>/secrets/coco. Files can be read and deleted (deleting
a file wipes the secret from memory).
To compile this driver as a module, choose M here.
The module will be called efi_secret.

View File

@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_EFI_SECRET) += efi_secret.o

View File

@ -0,0 +1,349 @@
// SPDX-License-Identifier: GPL-2.0
/*
* efi_secret module
*
* Copyright (C) 2022 IBM Corporation
* Author: Dov Murik <dovmurik@linux.ibm.com>
*/
/**
* DOC: efi_secret: Allow reading EFI confidential computing (coco) secret area
* via securityfs interface.
*
* When the module is loaded (and securityfs is mounted, typically under
* /sys/kernel/security), a "secrets/coco" directory is created in securityfs.
* In it, a file is created for each secret entry. The name of each such file
* is the GUID of the secret entry, and its content is the secret data.
*/
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/security.h>
#include <linux/efi.h>
#include <linux/cacheflush.h>
#define EFI_SECRET_NUM_FILES 64
struct efi_secret {
struct dentry *secrets_dir;
struct dentry *fs_dir;
struct dentry *fs_files[EFI_SECRET_NUM_FILES];
void __iomem *secret_data;
u64 secret_data_len;
};
/*
* Structure of the EFI secret area
*
* Offset Length
* (bytes) (bytes) Usage
* ------- ------- -----
* 0 16 Secret table header GUID (must be 1e74f542-71dd-4d66-963e-ef4287ff173b)
* 16 4 Length of bytes of the entire secret area
*
* 20 16 First secret entry's GUID
* 36 4 First secret entry's length in bytes (= 16 + 4 + x)
* 40 x First secret entry's data
*
* 40+x 16 Second secret entry's GUID
* 56+x 4 Second secret entry's length in bytes (= 16 + 4 + y)
* 60+x y Second secret entry's data
*
* (... and so on for additional entries)
*
* The GUID of each secret entry designates the usage of the secret data.
*/
/**
* struct secret_header - Header of entire secret area; this should be followed
* by instances of struct secret_entry.
* @guid: Must be EFI_SECRET_TABLE_HEADER_GUID
* @len: Length in bytes of entire secret area, including header
*/
struct secret_header {
efi_guid_t guid;
u32 len;
} __attribute((packed));
/**
* struct secret_entry - Holds one secret entry
* @guid: Secret-specific GUID (or NULL_GUID if this secret entry was deleted)
* @len: Length of secret entry, including its guid and len fields
* @data: The secret data (full of zeros if this secret entry was deleted)
*/
struct secret_entry {
efi_guid_t guid;
u32 len;
u8 data[];
} __attribute((packed));
static size_t secret_entry_data_len(struct secret_entry *e)
{
return e->len - sizeof(*e);
}
static struct efi_secret the_efi_secret;
static inline struct efi_secret *efi_secret_get(void)
{
return &the_efi_secret;
}
static int efi_secret_bin_file_show(struct seq_file *file, void *data)
{
struct secret_entry *e = file->private;
if (e)
seq_write(file, e->data, secret_entry_data_len(e));
return 0;
}
DEFINE_SHOW_ATTRIBUTE(efi_secret_bin_file);
/*
* Overwrite memory content with zeroes, and ensure that dirty cache lines are
* actually written back to memory, to clear out the secret.
*/
static void wipe_memory(void *addr, size_t size)
{
memzero_explicit(addr, size);
#ifdef CONFIG_X86
clflush_cache_range(addr, size);
#endif
}
static int efi_secret_unlink(struct inode *dir, struct dentry *dentry)
{
struct efi_secret *s = efi_secret_get();
struct inode *inode = d_inode(dentry);
struct secret_entry *e = (struct secret_entry *)inode->i_private;
int i;
if (e) {
/* Zero out the secret data */
wipe_memory(e->data, secret_entry_data_len(e));
e->guid = NULL_GUID;
}
inode->i_private = NULL;
for (i = 0; i < EFI_SECRET_NUM_FILES; i++)
if (s->fs_files[i] == dentry)
s->fs_files[i] = NULL;
/*
* securityfs_remove tries to lock the directory's inode, but we reach
* the unlink callback when it's already locked
*/
inode_unlock(dir);
securityfs_remove(dentry);
inode_lock(dir);
return 0;
}
static const struct inode_operations efi_secret_dir_inode_operations = {
.lookup = simple_lookup,
.unlink = efi_secret_unlink,
};
static int efi_secret_map_area(struct platform_device *dev)
{
int ret;
struct efi_secret *s = efi_secret_get();
struct linux_efi_coco_secret_area *secret_area;
if (efi.coco_secret == EFI_INVALID_TABLE_ADDR) {
dev_err(&dev->dev, "Secret area address is not available\n");
return -EINVAL;
}
secret_area = memremap(efi.coco_secret, sizeof(*secret_area), MEMREMAP_WB);
if (secret_area == NULL) {
dev_err(&dev->dev, "Could not map secret area EFI config entry\n");
return -ENOMEM;
}
if (!secret_area->base_pa || secret_area->size < sizeof(struct secret_header)) {
dev_err(&dev->dev,
"Invalid secret area memory location (base_pa=0x%llx size=0x%llx)\n",
secret_area->base_pa, secret_area->size);
ret = -EINVAL;
goto unmap;
}
s->secret_data = ioremap_encrypted(secret_area->base_pa, secret_area->size);
if (s->secret_data == NULL) {
dev_err(&dev->dev, "Could not map secret area\n");
ret = -ENOMEM;
goto unmap;
}
s->secret_data_len = secret_area->size;
ret = 0;
unmap:
memunmap(secret_area);
return ret;
}
static void efi_secret_securityfs_teardown(struct platform_device *dev)
{
struct efi_secret *s = efi_secret_get();
int i;
for (i = (EFI_SECRET_NUM_FILES - 1); i >= 0; i--) {
securityfs_remove(s->fs_files[i]);
s->fs_files[i] = NULL;
}
securityfs_remove(s->fs_dir);
s->fs_dir = NULL;
securityfs_remove(s->secrets_dir);
s->secrets_dir = NULL;
dev_dbg(&dev->dev, "Removed securityfs entries\n");
}
static int efi_secret_securityfs_setup(struct platform_device *dev)
{
struct efi_secret *s = efi_secret_get();
int ret = 0, i = 0, bytes_left;
unsigned char *ptr;
struct secret_header *h;
struct secret_entry *e;
struct dentry *dent;
char guid_str[EFI_VARIABLE_GUID_LEN + 1];
ptr = (void __force *)s->secret_data;
h = (struct secret_header *)ptr;
if (efi_guidcmp(h->guid, EFI_SECRET_TABLE_HEADER_GUID)) {
/*
* This is not an error: it just means that EFI defines secret
* area but it was not populated by the Guest Owner.
*/
dev_dbg(&dev->dev, "EFI secret area does not start with correct GUID\n");
return -ENODEV;
}
if (h->len < sizeof(*h)) {
dev_err(&dev->dev, "EFI secret area reported length is too small\n");
return -EINVAL;
}
if (h->len > s->secret_data_len) {
dev_err(&dev->dev, "EFI secret area reported length is too big\n");
return -EINVAL;
}
s->secrets_dir = NULL;
s->fs_dir = NULL;
memset(s->fs_files, 0, sizeof(s->fs_files));
dent = securityfs_create_dir("secrets", NULL);
if (IS_ERR(dent)) {
dev_err(&dev->dev, "Error creating secrets securityfs directory entry err=%ld\n",
PTR_ERR(dent));
return PTR_ERR(dent);
}
s->secrets_dir = dent;
dent = securityfs_create_dir("coco", s->secrets_dir);
if (IS_ERR(dent)) {
dev_err(&dev->dev, "Error creating coco securityfs directory entry err=%ld\n",
PTR_ERR(dent));
return PTR_ERR(dent);
}
d_inode(dent)->i_op = &efi_secret_dir_inode_operations;
s->fs_dir = dent;
bytes_left = h->len - sizeof(*h);
ptr += sizeof(*h);
while (bytes_left >= (int)sizeof(*e) && i < EFI_SECRET_NUM_FILES) {
e = (struct secret_entry *)ptr;
if (e->len < sizeof(*e) || e->len > (unsigned int)bytes_left) {
dev_err(&dev->dev, "EFI secret area is corrupted\n");
ret = -EINVAL;
goto err_cleanup;
}
/* Skip deleted entries (which will have NULL_GUID) */
if (efi_guidcmp(e->guid, NULL_GUID)) {
efi_guid_to_str(&e->guid, guid_str);
dent = securityfs_create_file(guid_str, 0440, s->fs_dir, (void *)e,
&efi_secret_bin_file_fops);
if (IS_ERR(dent)) {
dev_err(&dev->dev, "Error creating efi_secret securityfs entry\n");
ret = PTR_ERR(dent);
goto err_cleanup;
}
s->fs_files[i++] = dent;
}
ptr += e->len;
bytes_left -= e->len;
}
dev_info(&dev->dev, "Created %d entries in securityfs secrets/coco\n", i);
return 0;
err_cleanup:
efi_secret_securityfs_teardown(dev);
return ret;
}
static void efi_secret_unmap_area(void)
{
struct efi_secret *s = efi_secret_get();
if (s->secret_data) {
iounmap(s->secret_data);
s->secret_data = NULL;
s->secret_data_len = 0;
}
}
static int efi_secret_probe(struct platform_device *dev)
{
int ret;
ret = efi_secret_map_area(dev);
if (ret)
return ret;
ret = efi_secret_securityfs_setup(dev);
if (ret)
goto err_unmap;
return ret;
err_unmap:
efi_secret_unmap_area();
return ret;
}
static int efi_secret_remove(struct platform_device *dev)
{
efi_secret_securityfs_teardown(dev);
efi_secret_unmap_area();
return 0;
}
static struct platform_driver efi_secret_driver = {
.probe = efi_secret_probe,
.remove = efi_secret_remove,
.driver = {
.name = "efi_secret",
},
};
module_platform_driver(efi_secret_driver);
MODULE_DESCRIPTION("Confidential computing EFI secret area access");
MODULE_AUTHOR("IBM");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:efi_secret");

View File

@ -213,6 +213,8 @@ struct capsule_info {
size_t page_bytes_remain;
};
int efi_capsule_setup_info(struct capsule_info *cap_info, void *kbuff,
size_t hdr_bytes);
int __efi_capsule_setup_info(struct capsule_info *cap_info);
/*
@ -383,6 +385,7 @@ void efi_native_runtime_setup(void);
#define EFI_LOAD_FILE_PROTOCOL_GUID EFI_GUID(0x56ec3091, 0x954c, 0x11d2, 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b)
#define EFI_LOAD_FILE2_PROTOCOL_GUID EFI_GUID(0x4006c0c1, 0xfcb3, 0x403e, 0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d)
#define EFI_RT_PROPERTIES_TABLE_GUID EFI_GUID(0xeb66918a, 0x7eef, 0x402a, 0x84, 0x2e, 0x93, 0x1d, 0x21, 0xc3, 0x8a, 0xe9)
#define EFI_DXE_SERVICES_TABLE_GUID EFI_GUID(0x05ad34ba, 0x6f02, 0x4214, 0x95, 0x2e, 0x4d, 0xa0, 0x39, 0x8e, 0x2b, 0xb9)
#define EFI_IMAGE_SECURITY_DATABASE_GUID EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f)
#define EFI_SHIM_LOCK_GUID EFI_GUID(0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23)
@ -405,6 +408,20 @@ void efi_native_runtime_setup(void);
#define LINUX_EFI_MEMRESERVE_TABLE_GUID EFI_GUID(0x888eb0c6, 0x8ede, 0x4ff5, 0xa8, 0xf0, 0x9a, 0xee, 0x5c, 0xb9, 0x77, 0xc2)
#define LINUX_EFI_INITRD_MEDIA_GUID EFI_GUID(0x5568e427, 0x68fc, 0x4f3d, 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68)
#define LINUX_EFI_MOK_VARIABLE_TABLE_GUID EFI_GUID(0xc451ed2b, 0x9694, 0x45d3, 0xba, 0xba, 0xed, 0x9f, 0x89, 0x88, 0xa3, 0x89)
#define LINUX_EFI_COCO_SECRET_AREA_GUID EFI_GUID(0xadf956ad, 0xe98c, 0x484c, 0xae, 0x11, 0xb5, 0x1c, 0x7d, 0x33, 0x64, 0x47)
#define RISCV_EFI_BOOT_PROTOCOL_GUID EFI_GUID(0xccd15fec, 0x6f73, 0x4eec, 0x83, 0x95, 0x3e, 0x69, 0xe4, 0xb9, 0x40, 0xbf)
/*
* This GUID may be installed onto the kernel image's handle as a NULL protocol
* to signal to the stub that the placement of the image should be respected,
* and moving the image in physical memory is undesirable. To ensure
* compatibility with 64k pages kernels with virtually mapped stacks, and to
* avoid defeating physical randomization, this protocol should only be
* installed if the image was placed at a randomized 128k aligned address in
* memory.
*/
#define LINUX_EFI_LOADED_IMAGE_FIXED_GUID EFI_GUID(0xf5a37b6d, 0x3344, 0x42a5, 0xb6, 0xbb, 0x97, 0x86, 0x48, 0xc1, 0x89, 0x0a)
/* OEM GUIDs */
#define DELLEMC_EFI_RCI2_TABLE_GUID EFI_GUID(0x2d9f28a2, 0xa886, 0x456a, 0x97, 0xa8, 0xf1, 0x1e, 0xf2, 0x4f, 0xf4, 0x55)
@ -435,6 +452,7 @@ typedef struct {
} efi_config_table_type_t;
#define EFI_SYSTEM_TABLE_SIGNATURE ((u64)0x5453595320494249ULL)
#define EFI_DXE_SERVICES_TABLE_SIGNATURE ((u64)0x565245535f455844ULL)
#define EFI_2_30_SYSTEM_TABLE_REVISION ((2 << 16) | (30))
#define EFI_2_20_SYSTEM_TABLE_REVISION ((2 << 16) | (20))
@ -596,6 +614,7 @@ extern struct efi {
unsigned long tpm_log; /* TPM2 Event Log table */
unsigned long tpm_final_log; /* TPM2 Final Events Log table */
unsigned long mokvar_table; /* MOK variable config table */
unsigned long coco_secret; /* Confidential computing secret table */
efi_get_time_t *get_time;
efi_set_time_t *set_time;
@ -1335,4 +1354,12 @@ extern void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
static inline void efifb_setup_from_dmi(struct screen_info *si, const char *opt) { }
#endif
struct linux_efi_coco_secret_area {
u64 base_pa;
u64 size;
};
/* Header of a populated EFI secret area */
#define EFI_SECRET_TABLE_HEADER_GUID EFI_GUID(0x1e74f542, 0x71dd, 0x4d66, 0x96, 0x3e, 0xef, 0x42, 0x87, 0xff, 0x17, 0x3b)
#endif /* _LINUX_EFI_H */