From 102b0d2daa97dae68d3eed54d8fe37a9cc38a892 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 11:13:47 +0200 Subject: Adding upstream version 2.8.0+dfsg. Signed-off-by: Daniel Baumann --- plat/arm/css/sgi/sgi_ras.c | 194 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 plat/arm/css/sgi/sgi_ras.c (limited to 'plat/arm/css/sgi/sgi_ras.c') diff --git a/plat/arm/css/sgi/sgi_ras.c b/plat/arm/css/sgi/sgi_ras.c new file mode 100644 index 0000000..4f03ac4 --- /dev/null +++ b/plat/arm/css/sgi/sgi_ras.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2018-2021, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +static int sgi_ras_intr_handler(const struct err_record_info *err_rec, + int probe_data, + const struct err_handler_data *const data); +typedef struct mm_communicate_header { + struct efi_guid header_guid; + size_t message_len; + uint8_t data[8]; +} mm_communicate_header_t; + +/* + * GUID to indicate that the MM communication message is intended for DMC-620 + * MM driver. + */ +const struct efi_guid dmc620_ecc_event_guid = { + 0x5ef0afd5, 0xe01a, 0x4c30, + {0x86, 0x19, 0x45, 0x46, 0x26, 0x91, 0x80, 0x98} +}; + +struct sgi_ras_ev_map sgi575_ras_map[] = { + + /* DMC 0 error ECC error interrupt*/ + {SGI_SDEI_DS_EVENT_0, 35}, + + /* DMC 1 error ECC error interrupt*/ + {SGI_SDEI_DS_EVENT_1, 39}, +}; + +#define SGI575_RAS_MAP_SIZE ARRAY_SIZE(sgi575_ras_map) + +struct err_record_info sgi_err_records[] = { + { + /* DMC 0 error record info */ + .handler = &sgi_ras_intr_handler, + .aux_data = (void *)0, + }, { + /* DMC 1 error record info */ + .handler = &sgi_ras_intr_handler, + .aux_data = (void *)1, + }, +}; + +struct ras_interrupt sgi_ras_interrupts[] = { + { + .intr_number = 35, + .err_record = &sgi_err_records[0], + }, { + .intr_number = 39, + .err_record = &sgi_err_records[1], + } +}; + +REGISTER_ERR_RECORD_INFO(sgi_err_records); +REGISTER_RAS_INTERRUPTS(sgi_ras_interrupts); + +static struct sgi_ras_ev_map *plat_sgi_get_ras_ev_map(void) +{ + return sgi575_ras_map; +} + +static int plat_sgi_get_ras_ev_map_size(void) +{ + return SGI575_RAS_MAP_SIZE; +} + +/* + * Find event mapping for a given interrupt number: On success, returns pointer + * to the event mapping. On error, returns NULL. + */ +static struct sgi_ras_ev_map *find_ras_event_map_by_intr(uint32_t intr_num) +{ + struct sgi_ras_ev_map *map = plat_sgi_get_ras_ev_map(); + int i; + int size = plat_sgi_get_ras_ev_map_size(); + + for (i = 0; i < size; i++) { + if (map->intr == intr_num) + return map; + + map++; + } + + return NULL; +} + +static void sgi_ras_intr_configure(int intr) +{ + plat_ic_set_interrupt_type(intr, INTR_TYPE_EL3); + plat_ic_set_interrupt_priority(intr, PLAT_RAS_PRI); + plat_ic_clear_interrupt_pending(intr); + plat_ic_set_spi_routing(intr, INTR_ROUTING_MODE_ANY, + (u_register_t)read_mpidr_el1()); + plat_ic_enable_interrupt(intr); +} + +static int sgi_ras_intr_handler(const struct err_record_info *err_rec, + int probe_data, + const struct err_handler_data *const data) +{ + struct sgi_ras_ev_map *ras_map; + mm_communicate_header_t *header; + uint32_t intr; + int ret; + + cm_el1_sysregs_context_save(NON_SECURE); + intr = data->interrupt; + + /* + * Find if this is a RAS interrupt. There must be an event against + * this interrupt + */ + ras_map = find_ras_event_map_by_intr(intr); + assert(ras_map != NULL); + + /* + * Populate the MM_COMMUNICATE payload to share the + * event info with StandaloneMM code. This allows us to use + * MM_COMMUNICATE as a common entry mechanism into S-EL0. The + * header data will be parsed in StandaloneMM to process the + * corresponding event. + * + * TBD - Currently, the buffer allocated by SPM for communication + * between EL3 and S-EL0 is being used(PLAT_SPM_BUF_BASE). But this + * should happen via a dynamic mem allocation, which should be + * managed by SPM -- the individual platforms then call the mem + * alloc api to get memory for the payload. + */ + header = (void *) PLAT_SPM_BUF_BASE; + memset(header, 0, sizeof(*header)); + memcpy(&header->data, &err_rec->aux_data, sizeof(err_rec->aux_data)); + header->message_len = sizeof(err_rec->aux_data); + memcpy(&header->header_guid, (void *) &dmc620_ecc_event_guid, + sizeof(const struct efi_guid)); + + spm_mm_sp_call(MM_COMMUNICATE_AARCH64, (uint64_t)header, 0, + plat_my_core_pos()); + + /* + * Do an EOI of the RAS interrupt. This allows the + * sdei event to be dispatched at the SDEI event's + * priority. + */ + plat_ic_end_of_interrupt(intr); + + /* Dispatch the event to the SDEI client */ + ret = sdei_dispatch_event(ras_map->sdei_ev_num); + if (ret != 0) { + /* + * sdei_dispatch_event() may return failing result in some cases, + * for example kernel may not have registered a handler or RAS event + * may happen early during boot. We restore the NS context when + * sdei_dispatch_event() returns failing result. + */ + ERROR("SDEI dispatch failed: %d", ret); + cm_el1_sysregs_context_restore(NON_SECURE); + cm_set_next_eret_context(NON_SECURE); + } + + return ret; +} + +int sgi_ras_intr_handler_setup(void) +{ + int i; + struct sgi_ras_ev_map *map = plat_sgi_get_ras_ev_map(); + int size = plat_sgi_get_ras_ev_map_size(); + + for (i = 0; i < size; i++) { + sgi_ras_intr_configure(map->intr); + map++; + } + + INFO("SGI: RAS Interrupt Handler successfully registered\n"); + + return 0; +} -- cgit v1.2.3