[PATCH 4/4] acpi/prmt: Use EFI runtime sandbox to invoke PRM handlers

Ard Biesheuvel
2023-08-04T16:04:31Z

Instead of bypassing the kernel's adaptation layer for performing EFI
runtime calls, wire up ACPI PRM handling into it. This means these calls
can no longer occur concurrently with EFI runtime calls, and will be
made from the EFI runtime workqueue. It also means any page faults
occurring during PRM handling will be identified correctly as
originating in firmware code.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
drivers/acpi/prmt.c | 8 ++++----
drivers/firmware/efi/runtime-wrappers.c | 20 ++++++++++++++++++++
include/linux/efi.h | 13 +++++++++++++
3 files changed, 37 insertions(+), 4 deletions(-)

diff --git a/drivers/acpi/prmt.c b/drivers/acpi/prmt.c
index 3d4c4620f9f95309..95be1c80db387faa 100644
--- a/drivers/acpi/prmt.c
+++ b/drivers/acpi/prmt.c
@@ -53,7 +53,7 @@ static LIST_HEAD(prm_module_list);

struct prm_handler_info {
guid_t guid;
- void *handler_addr;
+ efi_acpi_prm_handler_t *handler_addr;
u64 static_data_buffer_addr;
u64 acpi_param_buffer_addr;

@@ -260,9 +260,9 @@ static acpi_status acpi_platformrt_space_handler(u32 function,
context.static_data_buffer = handler->static_data_buffer_addr;
context.mmio_ranges = module->mmio_info;

- status = efi_call_virt_pointer(handler, handler_addr,
- handler->acpi_param_buffer_addr,
- &context);
+ status = efi_call_acpi_prm_handler(handler->handler_addr,
+ handler->acpi_param_buffer_addr,
+ &context);
if (status == EFI_SUCCESS) {
buffer->prm_status = PRM_HANDLER_SUCCESS;
} else {
diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c
index b3ef208299ae591e..ce306cd1efdfda21 100644
--- a/drivers/firmware/efi/runtime-wrappers.c
+++ b/drivers/firmware/efi/runtime-wrappers.c
@@ -212,6 +212,12 @@ static void efi_call_rts(struct work_struct *work)
efi_rts_work.QUERY_CAPSULE_CAPS.max_size,
efi_rts_work.QUERY_CAPSULE_CAPS.reset_type);
break;
+ case EFI_ACPI_PRM_HANDLER:
+ status = efi_call_virt_pointer(&efi_rts_work.ACPI_PRM_HANDLER,
+ handler_addr,
+ efi_rts_work.ACPI_PRM_HANDLER.param_buffer_addr,
+ efi_rts_work.ACPI_PRM_HANDLER.context);
+ break;
default:
/*
* Ideally, we should never reach here because a caller of this
@@ -475,3 +481,17 @@ void __init efi_native_runtime_setup(void)
efi.update_capsule = virt_efi_update_capsule;
efi.query_capsule_caps = virt_efi_query_capsule_caps;
}
+
+efi_status_t efi_call_acpi_prm_handler(efi_acpi_prm_handler_t *handler_addr,
+ efi_physical_addr_t param_buffer_addr,
+ void *context)
+{
+ efi_status_t status;
+
+ if (down_interruptible(&efi_runtime_lock))
+ return EFI_ABORTED;
+ status = efi_queue_work(ACPI_PRM_HANDLER, handler_addr,
+ param_buffer_addr, context);
+ up(&efi_runtime_lock);
+ return status;
+}
diff --git a/include/linux/efi.h b/include/linux/efi.h
index c72715821016851b..065af735d90a411c 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -1230,6 +1230,12 @@ extern int efi_tpm_final_log_size;

extern unsigned long rci2_table_phys;

+typedef efi_status_t (__efiapi efi_acpi_prm_handler_t)(efi_physical_addr_t, void *);
+
+efi_status_t efi_call_acpi_prm_handler(efi_acpi_prm_handler_t *handler_addr,
+ efi_physical_addr_t param_buffer_addr,
+ void *context);
+
/*
* efi_runtime_service() function identifiers.
* "NONE" is used by efi_recover_from_page_fault() to check if the page
@@ -1249,6 +1255,7 @@ enum efi_rts_ids {
EFI_RESET_SYSTEM,
EFI_UPDATE_CAPSULE,
EFI_QUERY_CAPSULE_CAPS,
+ EFI_ACPI_PRM_HANDLER,
};

/*
@@ -1324,6 +1331,12 @@ struct efi_runtime_work {
u64 *max_size;
int *reset_type;
} QUERY_CAPSULE_CAPS;
+
+ struct {
+ efi_acpi_prm_handler_t *handler_addr;
+ efi_physical_addr_t param_buffer_addr;
+ void *context;
+ } ACPI_PRM_HANDLER;
};
efi_status_t status;
struct work_struct work;
--
2.39.2