blob: 60c402f039ec0c8bb6e7843db2764f412379d13f [file] [log] [blame]
Sughosh Ganu18f513d2018-05-16 17:22:35 +05301/*
2 * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <arm_spm_def.h>
8#include <assert.h>
9#include <context_mgmt.h>
10#include <interrupt_mgmt.h>
11#include <mm_svc.h>
12#include <ras.h>
13#include <sgi_ras.h>
14#include <platform.h>
15#include <spm_svc.h>
16#include <sdei.h>
17#include <string.h>
18
19static int sgi_ras_intr_handler(const struct err_record_info *err_rec,
20 int probe_data,
21 const struct err_handler_data *const data);
22struct efi_guid {
23 uint32_t data1;
24 uint16_t data2;
25 uint16_t data3;
26 uint8_t data4[8];
27};
28
29typedef struct mm_communicate_header {
30 struct efi_guid header_guid;
31 size_t message_len;
32 uint8_t data[8];
33} mm_communicate_header_t;
34
35/*
36 * Find event mapping for a given interrupt number: On success, returns pointer
37 * to the event mapping. On error, returns NULL.
38 */
39static struct sgi_ras_ev_map *find_ras_event_map_by_intr(uint32_t intr_num)
40{
41 struct sgi_ras_ev_map *map = plat_sgi_get_ras_ev_map();
42 int i;
43 int size = plat_sgi_get_ras_ev_map_size();
44
45 for (i = 0; i < size; i++) {
46 if (map->intr == intr_num)
47 return map;
48
49 map++;
50 }
51
52 return NULL;
53}
54
55static void sgi_ras_intr_configure(int intr)
56{
57 plat_ic_set_interrupt_type(intr, INTR_TYPE_EL3);
58 plat_ic_set_interrupt_priority(intr, PLAT_RAS_PRI);
59 plat_ic_clear_interrupt_pending(intr);
60 plat_ic_set_spi_routing(intr, INTR_ROUTING_MODE_ANY,
61 (u_register_t)read_mpidr_el1());
62 plat_ic_enable_interrupt(intr);
63}
64
65static int sgi_ras_intr_handler(const struct err_record_info *err_rec,
66 int probe_data,
67 const struct err_handler_data *const data)
68{
69 struct sgi_ras_ev_map *ras_map;
70 mm_communicate_header_t *header;
71 uint32_t intr;
72
73 cm_el1_sysregs_context_save(NON_SECURE);
74 intr = data->interrupt;
75
76 /*
77 * Find if this is a RAS interrupt. There must be an event against
78 * this interrupt
79 */
80 ras_map = find_ras_event_map_by_intr(intr);
81 assert(ras_map);
82
83 /*
84 * Populate the MM_COMMUNICATE payload to share the
85 * event info with StandaloneMM code. This allows us to use
86 * MM_COMMUNICATE as a common entry mechanism into S-EL0. The
87 * header data will be parsed in StandaloneMM to process the
88 * corresponding event.
89 *
90 * TBD - Currently, the buffer allocated by SPM for communication
91 * between EL3 and S-EL0 is being used(PLAT_SPM_BUF_BASE). But this
92 * should happen via a dynamic mem allocation, which should be
93 * managed by SPM -- the individual platforms then call the mem
94 * alloc api to get memory for the payload.
95 */
96 header = (void *) PLAT_SPM_BUF_BASE;
97 memset(header, 0, sizeof(*header));
98 memcpy(&header->data, &ras_map->ras_ev_num,
99 sizeof(ras_map->ras_ev_num));
100 header->message_len = 4;
101
102 spm_sp_call(MM_COMMUNICATE_AARCH64, (uint64_t)header, 0,
103 plat_my_core_pos());
104
105 /*
106 * Do an EOI of the RAS interuupt. This allows the
107 * sdei event to be dispatched at the SDEI event's
108 * priority.
109 */
110 plat_ic_end_of_interrupt(intr);
111
112 /* Dispatch the event to the SDEI client */
113 sdei_dispatch_event(ras_map->sdei_ev_num);
114
115 return 0;
116}
117
118int sgi_ras_intr_handler_setup(void)
119{
120 int i;
121 struct sgi_ras_ev_map *map = plat_sgi_get_ras_ev_map();
122 int size = plat_sgi_get_ras_ev_map_size();
123
124 for (i = 0; i < size; i++) {
125 sgi_ras_intr_configure(map->intr);
126 map++;
127 }
128
129 INFO("SGI: RAS Interrupt Handler successfully registered\n");
130
131 return 0;
132}