• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <assert.h>
8 #include <string.h>
9 
10 #include <bl31/interrupt_mgmt.h>
11 #include <lib/el3_runtime/context_mgmt.h>
12 #include <lib/extensions/ras.h>
13 #include <plat/arm/common/arm_spm_def.h>
14 #include <plat/common/platform.h>
15 #include <services/sdei.h>
16 #include <services/spm_mm_svc.h>
17 
18 #include <sgi_ras.h>
19 
20 static int sgi_ras_intr_handler(const struct err_record_info *err_rec,
21 				int probe_data,
22 				const struct err_handler_data *const data);
23 struct efi_guid {
24 	uint32_t	data1;
25 	uint16_t	data2;
26 	uint16_t	data3;
27 	uint8_t		data4[8];
28 };
29 
30 typedef struct mm_communicate_header {
31 	struct efi_guid	header_guid;
32 	size_t		message_len;
33 	uint8_t		data[8];
34 } mm_communicate_header_t;
35 
36 struct sgi_ras_ev_map sgi575_ras_map[] = {
37 
38 	/* DMC620 error overflow interrupt*/
39 	{SP_DMC_ERROR_OVERFLOW_EVENT_AARCH64, SGI_SDEI_DS_EVENT_1, 33},
40 
41 	/* DMC620 error ECC error interrupt*/
42 	{SP_DMC_ERROR_ECC_EVENT_AARCH64, SGI_SDEI_DS_EVENT_0, 35},
43 };
44 
45 #define SGI575_RAS_MAP_SIZE	ARRAY_SIZE(sgi575_ras_map)
46 
47 struct err_record_info sgi_err_records[] = {
48 	{
49 		.handler = &sgi_ras_intr_handler,
50 	},
51 };
52 
53 struct ras_interrupt sgi_ras_interrupts[] = {
54 	{
55 		.intr_number = 33,
56 		.err_record = &sgi_err_records[0],
57 	},
58 	{
59 		.intr_number = 35,
60 		.err_record = &sgi_err_records[0],
61 	}
62 };
63 
64 REGISTER_ERR_RECORD_INFO(sgi_err_records);
65 REGISTER_RAS_INTERRUPTS(sgi_ras_interrupts);
66 
plat_sgi_get_ras_ev_map(void)67 static struct sgi_ras_ev_map *plat_sgi_get_ras_ev_map(void)
68 {
69 	return sgi575_ras_map;
70 }
71 
plat_sgi_get_ras_ev_map_size(void)72 static int plat_sgi_get_ras_ev_map_size(void)
73 {
74 	return SGI575_RAS_MAP_SIZE;
75 }
76 
77 /*
78  * Find event mapping for a given interrupt number: On success, returns pointer
79  * to the event mapping. On error, returns NULL.
80  */
find_ras_event_map_by_intr(uint32_t intr_num)81 static struct sgi_ras_ev_map *find_ras_event_map_by_intr(uint32_t intr_num)
82 {
83 	struct sgi_ras_ev_map *map = plat_sgi_get_ras_ev_map();
84 	int i;
85 	int size = plat_sgi_get_ras_ev_map_size();
86 
87 	for (i = 0; i < size; i++) {
88 		if (map->intr == intr_num)
89 			return map;
90 
91 		map++;
92 	}
93 
94 	return NULL;
95 }
96 
sgi_ras_intr_configure(int intr)97 static void sgi_ras_intr_configure(int intr)
98 {
99 	plat_ic_set_interrupt_type(intr, INTR_TYPE_EL3);
100 	plat_ic_set_interrupt_priority(intr, PLAT_RAS_PRI);
101 	plat_ic_clear_interrupt_pending(intr);
102 	plat_ic_set_spi_routing(intr, INTR_ROUTING_MODE_ANY,
103 				(u_register_t)read_mpidr_el1());
104 	plat_ic_enable_interrupt(intr);
105 }
106 
sgi_ras_intr_handler(const struct err_record_info * err_rec,int probe_data,const struct err_handler_data * const data)107 static int sgi_ras_intr_handler(const struct err_record_info *err_rec,
108 				int probe_data,
109 				const struct err_handler_data *const data)
110 {
111 	struct sgi_ras_ev_map *ras_map;
112 	mm_communicate_header_t *header;
113 	uint32_t intr;
114 	int ret;
115 
116 	cm_el1_sysregs_context_save(NON_SECURE);
117 	intr = data->interrupt;
118 
119 	/*
120 	 * Find if this is a RAS interrupt. There must be an event against
121 	 * this interrupt
122 	 */
123 	ras_map = find_ras_event_map_by_intr(intr);
124 	assert(ras_map != NULL);
125 
126 	/*
127 	 * Populate the MM_COMMUNICATE payload to share the
128 	 * event info with StandaloneMM code. This allows us to use
129 	 * MM_COMMUNICATE as a common entry mechanism into S-EL0. The
130 	 * header data will be parsed in StandaloneMM to process the
131 	 * corresponding event.
132 	 *
133 	 * TBD - Currently, the buffer allocated by SPM for communication
134 	 * between EL3 and S-EL0 is being used(PLAT_SPM_BUF_BASE). But this
135 	 * should happen via a dynamic mem allocation, which should be
136 	 * managed by SPM -- the individual platforms then call the mem
137 	 * alloc api to get memory for the payload.
138 	 */
139 	header = (void *) PLAT_SPM_BUF_BASE;
140 	memset(header, 0, sizeof(*header));
141 	memcpy(&header->data, &ras_map->ras_ev_num,
142 	       sizeof(ras_map->ras_ev_num));
143 	header->message_len = 4;
144 
145 	spm_mm_sp_call(MM_COMMUNICATE_AARCH64, (uint64_t)header, 0,
146 		       plat_my_core_pos());
147 
148 	/*
149 	 * Do an EOI of the RAS interrupt. This allows the
150 	 * sdei event to be dispatched at the SDEI event's
151 	 * priority.
152 	 */
153 	plat_ic_end_of_interrupt(intr);
154 
155 	/* Dispatch the event to the SDEI client */
156 	ret = sdei_dispatch_event(ras_map->sdei_ev_num);
157 	if (ret != 0) {
158 		/*
159 		 * sdei_dispatch_event() may return failing result in some cases,
160 		 * for example kernel may not have registered a handler or RAS event
161 		 * may happen early during boot. We restore the NS context when
162 		 * sdei_dispatch_event() returns failing result.
163 		 */
164 		ERROR("SDEI dispatch failed: %d", ret);
165 		cm_el1_sysregs_context_restore(NON_SECURE);
166 		cm_set_next_eret_context(NON_SECURE);
167 	}
168 
169 	return ret;
170 }
171 
sgi_ras_intr_handler_setup(void)172 int sgi_ras_intr_handler_setup(void)
173 {
174 	int i;
175 	struct sgi_ras_ev_map *map = plat_sgi_get_ras_ev_map();
176 	int size = plat_sgi_get_ras_ev_map_size();
177 
178 	for (i = 0; i < size; i++) {
179 		sgi_ras_intr_configure(map->intr);
180 		map++;
181 	}
182 
183 	INFO("SGI: RAS Interrupt Handler successfully registered\n");
184 
185 	return 0;
186 }
187