From f96935d3bc38a5f4b5188b6470a10e3fb8c3f0cc Mon Sep 17 00:00:00 2001 From: James Morse Date: Tue, 29 Jan 2019 18:49:01 +0000 Subject: firmware: arm_sdei: Add ACPI GHES registration helper APEI's Generic Hardware Error Source structures do not describe whether the SDEI event is shared or private, as this information is discoverable via the API. GHES needs to know whether an event is normal or critical to avoid sharing locks or fixmap entries, but GHES shouldn't have to know about the SDEI API. Add a helper to register the GHES using the appropriate normal or critical callback. Signed-off-by: James Morse Acked-by: Catalin Marinas Signed-off-by: Rafael J. Wysocki --- drivers/firmware/arm_sdei.c | 68 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) (limited to 'drivers/firmware') diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c index c64c7da73829..e6376f985ef7 100644 --- a/drivers/firmware/arm_sdei.c +++ b/drivers/firmware/arm_sdei.c @@ -2,6 +2,7 @@ // Copyright (C) 2017 Arm Ltd. #define pr_fmt(fmt) "sdei: " fmt +#include #include #include #include @@ -887,6 +888,73 @@ static void sdei_smccc_hvc(unsigned long function_id, arm_smccc_hvc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res); } +int sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb, + sdei_event_callback *critical_cb) +{ + int err; + u64 result; + u32 event_num; + sdei_event_callback *cb; + + if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES)) + return -EOPNOTSUPP; + + event_num = ghes->generic->notify.vector; + if (event_num == 0) { + /* + * Event 0 is reserved by the specification for + * SDEI_EVENT_SIGNAL. + */ + return -EINVAL; + } + + err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_PRIORITY, + &result); + if (err) + return err; + + if (result == SDEI_EVENT_PRIORITY_CRITICAL) + cb = critical_cb; + else + cb = normal_cb; + + err = sdei_event_register(event_num, cb, ghes); + if (!err) + err = sdei_event_enable(event_num); + + return err; +} + +int sdei_unregister_ghes(struct ghes *ghes) +{ + int i; + int err; + u32 event_num = ghes->generic->notify.vector; + + might_sleep(); + + if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES)) + return -EOPNOTSUPP; + + /* + * The event may be running on another CPU. Disable it + * to stop new events, then try to unregister a few times. + */ + err = sdei_event_disable(event_num); + if (err) + return err; + + for (i = 0; i < 3; i++) { + err = sdei_event_unregister(event_num); + if (err != -EINPROGRESS) + break; + + schedule(); + } + + return err; +} + static int sdei_get_conduit(struct platform_device *pdev) { const char *method; -- cgit v1.2.3-59-g8ed1b From 45b14a4ffcc1e0b5caa246638f942cbe7eaea7ad Mon Sep 17 00:00:00 2001 From: Ross Lagerwall Date: Mon, 28 Jan 2019 10:04:24 +0000 Subject: efi: cper: Fix possible out-of-bounds access When checking a generic status block, we iterate over all the generic data blocks. The loop condition only checks that the start of the generic data block is valid (within estatus->data_length) but not the whole block. Because the size of data blocks (excluding error data) may vary depending on the revision and the revision is contained within the data block, ensure that enough of the current data block is valid before dereferencing any members otherwise an out-of-bounds access may occur if estatus->data_length is invalid. This relies on the fact that struct acpi_hest_generic_data_v300 is a superset of the earlier version. Also rework the other checks to avoid potential underflow. Signed-off-by: Ross Lagerwall Acked-by: Borislav Petkov Tested-by: Tyler Baicar Signed-off-by: Rafael J. Wysocki --- drivers/firmware/efi/cper.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index a7902fccdcfa..6090d25dce85 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -546,19 +546,24 @@ EXPORT_SYMBOL_GPL(cper_estatus_check_header); int cper_estatus_check(const struct acpi_hest_generic_status *estatus) { struct acpi_hest_generic_data *gdata; - unsigned int data_len, gedata_len; + unsigned int data_len, record_size; int rc; rc = cper_estatus_check_header(estatus); if (rc) return rc; + data_len = estatus->data_length; apei_estatus_for_each_section(estatus, gdata) { - gedata_len = acpi_hest_get_error_length(gdata); - if (gedata_len > data_len - acpi_hest_get_size(gdata)) + if (sizeof(struct acpi_hest_generic_data) > data_len) + return -EINVAL; + + record_size = acpi_hest_get_record_size(gdata); + if (record_size > data_len) return -EINVAL; - data_len -= acpi_hest_get_record_size(gdata); + + data_len -= record_size; } if (data_len) return -EINVAL; -- cgit v1.2.3-59-g8ed1b