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:
commit
bf2431021c
51
Documentation/ABI/testing/securityfs-secrets-coco
Normal file
51
Documentation/ABI/testing/securityfs-secrets-coco
Normal 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".
|
@ -17,3 +17,4 @@ Security Documentation
|
||||
tpm/index
|
||||
digsig
|
||||
landlock
|
||||
secrets/index
|
||||
|
103
Documentation/security/secrets/coco.rst
Normal file
103
Documentation/security/secrets/coco.rst
Normal 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
|
9
Documentation/security/secrets/index.rst
Normal file
9
Documentation/security/secrets/index.rst
Normal file
@ -0,0 +1,9 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=====================
|
||||
Secrets documentation
|
||||
=====================
|
||||
|
||||
.. toctree::
|
||||
|
||||
coco
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 */
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
{},
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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/
|
||||
|
16
drivers/virt/coco/efi_secret/Kconfig
Normal file
16
drivers/virt/coco/efi_secret/Kconfig
Normal 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.
|
2
drivers/virt/coco/efi_secret/Makefile
Normal file
2
drivers/virt/coco/efi_secret/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_EFI_SECRET) += efi_secret.o
|
349
drivers/virt/coco/efi_secret/efi_secret.c
Normal file
349
drivers/virt/coco/efi_secret/efi_secret.c
Normal 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");
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user