blob: c5cbbfea3b4dceef9330108da26620a4c6d4656c [file] [log] [blame]
Furquan Shaikh2af76f42014-04-28 16:39:40 -07001/*
2 * This file is part of the libpayload project.
3 *
4 * Copyright 2013 Google Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <stdint.h>
31#include <types.h>
Aaron Durbin4f89d972014-09-16 22:23:57 -050032#include <arch/barrier.h>
Furquan Shaikh2af76f42014-04-28 16:39:40 -070033#include <arch/cache.h>
34#include <arch/exception.h>
Furquan Shaikh4aa76162014-09-08 18:04:18 -070035#include <arch/transition.h>
Furquan Shaikh2af76f42014-04-28 16:39:40 -070036#include <console/console.h>
Furquan Shaikh69761cd2014-08-21 10:31:00 -070037#include <arch/lib_helpers.h>
Furquan Shaikh2af76f42014-04-28 16:39:40 -070038
Julius Werner66a476a2015-10-12 16:45:21 -070039uint8_t exception_stack[0x200] __attribute__((aligned(16)));
40
Aaron Durbin4f89d972014-09-16 22:23:57 -050041static const char *exception_names[NUM_EXC_VIDS] = {
42 [EXC_VID_CUR_SP_EL0_SYNC] = "_sync_sp_el0",
43 [EXC_VID_CUR_SP_EL0_IRQ] = "_irq_sp_el0",
44 [EXC_VID_CUR_SP_EL0_FIRQ] = "_fiq_sp_el0",
45 [EXC_VID_CUR_SP_EL0_SERR] = "_serror_sp_el0",
46 [EXC_VID_CUR_SP_ELX_SYNC] = "_sync_sp_el3",
47 [EXC_VID_CUR_SP_ELX_IRQ] = "_irq_sp_el3",
48 [EXC_VID_CUR_SP_ELX_FIQ] = "_fiq_sp_el3",
49 [EXC_VID_CUR_SP_ELX_SERR] = "_serror_sp_el3",
50 [EXC_VID_LOW64_SYNC] = "_sync_elx_64",
51 [EXC_VID_LOW64_IRQ] = "_irq_elx_64",
52 [EXC_VID_LOW64_FIQ] = "_fiq_elx_64",
53 [EXC_VID_LOW64_SERR] = "_serror_elx_64",
54 [EXC_VID_LOW32_SYNC] = "_sync_elx_32",
55 [EXC_VID_LOW32_IRQ] = "_irq_elx_32",
56 [EXC_VID_LOW32_FIQ] = "_fiq_elx_32",
57 [EXC_VID_LOW32_SERR] = "_serror_elx_32"
Furquan Shaikh69761cd2014-08-21 10:31:00 -070058};
59
Julius Werner2c515722016-05-20 18:06:22 -070060static void dump_stack(uintptr_t addr, size_t bytes)
61{
62 int i, j;
63 const int words_per_line = 8;
64 uint64_t *ptr = (void *)ALIGN_DOWN(addr, words_per_line * sizeof(*ptr));
65
66 printk(BIOS_DEBUG, "Dumping stack:\n");
67 for (i = bytes / sizeof(*ptr); i >= 0; i -= words_per_line) {
68 printk(BIOS_DEBUG, "%p: ", ptr + i);
69 for (j = i; j < i + words_per_line; j++)
70 printk(BIOS_DEBUG, "%016llx ", *(ptr + j));
71 printk(BIOS_DEBUG, "\n");
72 }
73}
74
Furquan Shaikh4aa76162014-09-08 18:04:18 -070075static void print_regs(struct exc_state *exc_state)
Furquan Shaikh2af76f42014-04-28 16:39:40 -070076{
77 int i;
Furquan Shaikh4aa76162014-09-08 18:04:18 -070078 struct elx_state *elx = &exc_state->elx;
79 struct regs *regs = &exc_state->regs;
Furquan Shaikh2af76f42014-04-28 16:39:40 -070080
Julius Werner2c515722016-05-20 18:06:22 -070081 printk(BIOS_DEBUG, "ELR = 0x%016llx ESR = 0x%08x\n",
82 elx->elr, raw_read_esr_current());
83 printk(BIOS_DEBUG, "FAR = 0x%016llx SPSR = 0x%08x\n",
84 raw_read_far_current(), raw_read_spsr_current());
85 for (i = 0; i < 30; i += 2) {
86 printk(BIOS_DEBUG,
87 "X%02d = 0x%016llx X%02d = 0x%016llx\n",
88 i, regs->x[i], i + 1, regs->x[i + 1]);
89 }
90 printk(BIOS_DEBUG, "X30 = 0x%016llx SP = 0x%016llx\n",
91 regs->x[30], regs->sp);
Furquan Shaikh69761cd2014-08-21 10:31:00 -070092}
93
Aaron Durbin4f89d972014-09-16 22:23:57 -050094
95static struct exception_handler *handlers[NUM_EXC_VIDS];
96
97
98int exception_handler_register(uint64_t vid, struct exception_handler *h)
Furquan Shaikh69761cd2014-08-21 10:31:00 -070099{
Aaron Durbin4f89d972014-09-16 22:23:57 -0500100 if (vid >= NUM_EXC_VIDS)
101 return -1;
Furquan Shaikh69761cd2014-08-21 10:31:00 -0700102
Aaron Durbin4f89d972014-09-16 22:23:57 -0500103 /* Just place at head of queue. */
104 h->next = handlers[vid];
105 store_release(&handlers[vid], h);
106
107 return 0;
108}
109
110int exception_handler_unregister(uint64_t vid, struct exception_handler *h)
111{
112 struct exception_handler *cur;
113 struct exception_handler **prev;
114
115 if (vid >= NUM_EXC_VIDS)
116 return -1;
117
118 prev = &handlers[vid];
119
120 for (cur = handlers[vid]; cur != NULL; cur = cur->next) {
121 if (cur != h)
122 continue;
123 /* Update previous pointer. */
124 store_release(prev, cur->next);
125 return 0;
Furquan Shaikh2af76f42014-04-28 16:39:40 -0700126 }
Furquan Shaikh69761cd2014-08-21 10:31:00 -0700127
Aaron Durbin4f89d972014-09-16 22:23:57 -0500128 /* Not found */
129 return -1;
130}
Furquan Shaikh4aa76162014-09-08 18:04:18 -0700131
Aaron Durbin4f89d972014-09-16 22:23:57 -0500132static void print_exception_info(struct exc_state *state, uint64_t idx)
133{
134 if (idx < NUM_EXC_VIDS)
135 printk(BIOS_DEBUG, "exception %s\n", exception_names[idx]);
136
137 print_regs(state);
Julius Werner2c515722016-05-20 18:06:22 -0700138 /* Few words below SP in case we need state from a returned function. */
139 dump_stack(state->regs.sp - 32, 512);
Aaron Durbin4f89d972014-09-16 22:23:57 -0500140}
141
142static void print_exception_and_die(struct exc_state *state, uint64_t idx)
143{
144 print_exception_info(state, idx);
145 die("exception death");
146}
147
148
149static int handle_exception(struct exc_state *state, uint64_t idx)
150{
151 int ret = EXC_RET_ABORT;
152
153 struct exception_handler *h;
154
155 for (h = handlers[idx]; h != NULL; h = h->next) {
156 int hret;
157
158 hret = h->handler(state, idx);
159
160 if (hret > ret)
161 ret = hret;
162 }
163
164 return ret;
165}
166
167void exc_dispatch(struct exc_state *state, uint64_t idx)
168{
169 int ret;
170
171 if (idx >= NUM_EXC_VIDS) {
172 printk(BIOS_DEBUG, "Bad exception index %x.\n", (int)idx);
173 print_exception_and_die(state, idx);
174 }
175
176 ret = handle_exception(state, idx);
177
178 if (ret == EXC_RET_ABORT)
179 print_exception_and_die(state, idx);
180
181 if (ret == EXC_RET_IGNORED || ret == EXC_RET_HANDLED_DUMP_STATE)
182 print_exception_info(state, idx);
183
184 exc_exit(&state->regs);
185}
186
Aaron Durbin4f89d972014-09-16 22:23:57 -0500187static int test_exception_handler(struct exc_state *state, uint64_t vector_id)
188{
189 /* Update instruction pointer to next instrution. */
190 state->elx.elr += sizeof(uint32_t);
191 raw_write_elr_current(state->elx.elr);
192 return EXC_RET_HANDLED;
Furquan Shaikh2af76f42014-04-28 16:39:40 -0700193}
194
Furquan Shaikh69761cd2014-08-21 10:31:00 -0700195static uint64_t test_exception(void)
Furquan Shaikh2af76f42014-04-28 16:39:40 -0700196{
Aaron Durbin4f89d972014-09-16 22:23:57 -0500197 struct exception_handler sync_elx;
198 struct exception_handler sync_el0;
199 unsigned long long *a = (void *)0xffffffff00000000ULL;
Furquan Shaikh2af76f42014-04-28 16:39:40 -0700200
Aaron Durbin4f89d972014-09-16 22:23:57 -0500201 sync_elx.handler = &test_exception_handler;
202 sync_el0.handler = &test_exception_handler;
Furquan Shaikh2af76f42014-04-28 16:39:40 -0700203
Aaron Durbin4f89d972014-09-16 22:23:57 -0500204 exception_handler_register(EXC_VID_CUR_SP_ELX_SYNC, &sync_elx);
205 exception_handler_register(EXC_VID_CUR_SP_EL0_SYNC, &sync_el0);
206
207 force_read(*a);
208
209 exception_handler_unregister(EXC_VID_CUR_SP_ELX_SYNC, &sync_elx);
210 exception_handler_unregister(EXC_VID_CUR_SP_EL0_SYNC, &sync_el0);
Furquan Shaikh2af76f42014-04-28 16:39:40 -0700211
Furquan Shaikh69761cd2014-08-21 10:31:00 -0700212 return 0;
Furquan Shaikh2af76f42014-04-28 16:39:40 -0700213}
214
Aaron Durbincc175762014-08-27 16:45:12 -0500215void exception_init(void)
216{
Julius Werner66a476a2015-10-12 16:45:21 -0700217 /* Load the exception table and initialize SP_EL3. */
218 exception_init_asm(exception_stack + ARRAY_SIZE(exception_stack));
Furquan Shaikh2af76f42014-04-28 16:39:40 -0700219
Furquan Shaikh69761cd2014-08-21 10:31:00 -0700220 printk(BIOS_DEBUG, "ARM64: Exception handlers installed.\n");
221
Julius Werner66a476a2015-10-12 16:45:21 -0700222 /* Only spend time testing on debug builds that are trying to detect more errors. */
223 if (IS_ENABLED(CONFIG_FATAL_ASSERTS)) {
224 printk(BIOS_DEBUG, "ARM64: Testing exception\n");
225 test_exception();
226 printk(BIOS_DEBUG, "ARM64: Done test exception\n");
227 }
Furquan Shaikh2af76f42014-04-28 16:39:40 -0700228}