/* * Copyright (c) 2023-2024, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include /* Base Element RAM Error Record offsets. */ #define ERRSTATUS U(0) #define ERRCODE U(8) #define ERRADDR U(12) /* * Base Element RAM error information data structure communicated as part of MM * Communication data payload. */ typedef struct nrd_sram_err_info { uint32_t err_status; uint32_t err_code; uint32_t err_addr; } nrd_sram_err_info_t; /* * MM Communicate message header GUID to indicate the payload is intended for * base element RAM MM driver. */ struct efi_guid sram_ecc_event_guid = { 0x7312db4f, 0xd0c4, 0x4fb5, { 0x81, 0x2c, 0xb7, 0x4b, 0xc6, 0xc4, 0xa9, 0x38 } }; /* Base element RAM RAS error interrupt handler */ int nrd_ras_sram_intr_handler(const struct err_record_info *err_rec, int probe_data, const struct err_handler_data *const data) { struct nrd_ras_ev_map *ras_map; mm_communicate_header_t *header; nrd_sram_err_info_t sram_info; uintptr_t base_addr; uint32_t clear_status, intr; int ret; cm_el1_sysregs_context_save(NON_SECURE); intr = data->interrupt; INFO("NRD: Base element RAM interrupt [%d] handler\n", intr); /* Determine error record base address to read. */ base_addr = 0; if (intr == NRD_CSS_NS_RAM_ECC_CE_INT || intr == NRD_CSS_NS_RAM_ECC_UE_INT) { base_addr = NRD_CSS_NS_RAM_ERR_REC_BASE; } sram_info.err_status = mmio_read_32(base_addr + ERRSTATUS); sram_info.err_code = mmio_read_32(base_addr + ERRCODE); sram_info.err_addr = mmio_read_32(base_addr + ERRADDR); /* Clear the interrupt. */ clear_status = mmio_read_32(base_addr + ERRSTATUS); mmio_write_32((base_addr + ERRSTATUS), clear_status); /* * Prepare the MM Communication buffer to pass the base element RAM * error information to Secure Partition. */ header = (void *)PLAT_SPM_BUF_BASE; memset(header, 0, sizeof(*header)); memcpy(&header->data, &sram_info, sizeof(sram_info)); header->message_len = sizeof(sram_info); memcpy(&header->header_guid, (void *)&sram_ecc_event_guid, sizeof(struct efi_guid)); spm_mm_sp_call(MM_COMMUNICATE_AARCH64, (uint64_t)header, 0, plat_my_core_pos()); plat_ic_end_of_interrupt(intr); /* * Find if this is a RAS interrupt. There must be an event against * this interrupt */ ras_map = nrd_find_ras_event_map_by_intr(intr); if (ras_map == NULL) { ERROR("NRD: RAS error info for interrupt id: %d not found\n", intr); return -1; } /* 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; }