acpi/prmt: Use EFI runtime sandbox to invoke PRM handlers

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.

Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
This commit is contained in:
Ard Biesheuvel 2023-07-02 11:57:34 +02:00
parent 3c17ae4161
commit 5894cf571e
4 changed files with 40 additions and 4 deletions

View File

@ -581,7 +581,7 @@ config ACPI_VIOT
config ACPI_PRMT config ACPI_PRMT
bool "Platform Runtime Mechanism Support" bool "Platform Runtime Mechanism Support"
depends on EFI && (X86_64 || ARM64) depends on EFI_RUNTIME_WRAPPERS && (X86_64 || ARM64)
default y default y
help help
Platform Runtime Mechanism (PRM) is a firmware interface exposing a Platform Runtime Mechanism (PRM) is a firmware interface exposing a

View File

@ -260,9 +260,9 @@ static acpi_status acpi_platformrt_space_handler(u32 function,
context.static_data_buffer = handler->static_data_buffer_addr; context.static_data_buffer = handler->static_data_buffer_addr;
context.mmio_ranges = module->mmio_info; context.mmio_ranges = module->mmio_info;
status = efi_call_virt_pointer(handler, handler_addr, status = efi_call_acpi_prm_handler(handler->handler_addr,
handler->acpi_param_buffer_addr, handler->acpi_param_buffer_addr,
&context); &context);
if (status == EFI_SUCCESS) { if (status == EFI_SUCCESS) {
buffer->prm_status = PRM_HANDLER_SUCCESS; buffer->prm_status = PRM_HANDLER_SUCCESS;
} else { } else {

View File

@ -108,6 +108,12 @@ union efi_rts_args {
u64 *max_size; u64 *max_size;
int *reset_type; int *reset_type;
} QUERY_CAPSULE_CAPS; } QUERY_CAPSULE_CAPS;
struct {
efi_status_t (__efiapi *acpi_prm_handler)(u64, void *);
u64 param_buffer_addr;
void *context;
} ACPI_PRM_HANDLER;
}; };
struct efi_runtime_work efi_rts_work; struct efi_runtime_work efi_rts_work;
@ -283,6 +289,13 @@ static void efi_call_rts(struct work_struct *work)
args->QUERY_CAPSULE_CAPS.max_size, args->QUERY_CAPSULE_CAPS.max_size,
args->QUERY_CAPSULE_CAPS.reset_type); args->QUERY_CAPSULE_CAPS.reset_type);
break; break;
case EFI_ACPI_PRM_HANDLER:
#ifdef CONFIG_ACPI_PRMT
status = arch_efi_call_virt(args, ACPI_PRM_HANDLER.acpi_prm_handler,
args->ACPI_PRM_HANDLER.param_buffer_addr,
args->ACPI_PRM_HANDLER.context);
break;
#endif
default: default:
/* /*
* Ideally, we should never reach here because a caller of this * Ideally, we should never reach here because a caller of this
@ -560,3 +573,21 @@ void efi_native_runtime_setup(void)
efi.update_capsule = virt_efi_update_capsule; efi.update_capsule = virt_efi_update_capsule;
efi.query_capsule_caps = virt_efi_query_capsule_caps; efi.query_capsule_caps = virt_efi_query_capsule_caps;
} }
#ifdef CONFIG_ACPI_PRMT
efi_status_t
efi_call_acpi_prm_handler(efi_status_t (__efiapi *handler_addr)(u64, void *),
u64 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;
}
#endif

View File

@ -1228,6 +1228,10 @@ extern int efi_tpm_final_log_size;
extern unsigned long rci2_table_phys; extern unsigned long rci2_table_phys;
efi_status_t
efi_call_acpi_prm_handler(efi_status_t (__efiapi *handler_addr)(u64, void *),
u64 param_buffer_addr, void *context);
/* /*
* efi_runtime_service() function identifiers. * efi_runtime_service() function identifiers.
* "NONE" is used by efi_recover_from_page_fault() to check if the page * "NONE" is used by efi_recover_from_page_fault() to check if the page
@ -1247,6 +1251,7 @@ enum efi_rts_ids {
EFI_RESET_SYSTEM, EFI_RESET_SYSTEM,
EFI_UPDATE_CAPSULE, EFI_UPDATE_CAPSULE,
EFI_QUERY_CAPSULE_CAPS, EFI_QUERY_CAPSULE_CAPS,
EFI_ACPI_PRM_HANDLER,
}; };
union efi_rts_args; union efi_rts_args;