1 /* $OpenBSD: fault.c,v 1.2 2014/03/29 18:09:29 guenther Exp $ */
2
3 /*
4 * Copyright (c) 2013 Miodrag Vallat.
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /*
20 * Standalone code to recover from faults. Allows for hardware detection.
21 */
22
23 #include <sys/param.h>
24
25 #include <machine/asm.h>
26 #include <machine/asm_macro.h>
27 #include <machine/psl.h>
28
29 typedef struct label_t {
30 long val[19];
31 } label_t;
32
33 extern int setjmp(label_t *);
34 extern void longjmp(label_t *);
35
36 static label_t badaddr_jmpbuf;
37 static uint32_t badaddr_psr;
38
39 static uint32_t prom_vbr;
40 static uint32_t vector_page[512 * 2] __attribute__ ((__aligned__(0x1000)));
41
42 static __inline__ uint32_t
get_vbr()43 get_vbr()
44 {
45 uint32_t vbr;
46 __asm__ volatile ("ldcr %0, %%cr7" : "=r"(vbr));
47 return vbr;
48 }
49
50 static __inline__ void
set_vbr(uint32_t vbr)51 set_vbr(uint32_t vbr)
52 {
53 __asm__ volatile ("stcr %0, %%cr7" :: "r"(vbr));
54 }
55
56 /*
57 * This is an horribly crude logic to recover from data access exceptions
58 * by longjmp'ing back to badaddr(). We should theoretically at least use
59 * an `rte' somewhere to unfreeze the scoreboard. But since we restore a
60 * PSR with interrupts disabled, this turns out to be safe.
61 */
62 static void
libsa_fault_handler(void)63 libsa_fault_handler(void)
64 {
65 set_psr(badaddr_psr | PSR_IND); /* undo SFRZ */
66 flush_pipeline();
67
68 longjmp(&badaddr_jmpbuf);
69 /* NOTREACHED */
70 }
71
72 static __inline__ uint32_t
br(uint32_t delta)73 br(uint32_t delta)
74 {
75 return 0xc0000000 | (((int32_t)delta >> 2) & 0x03ffffff);
76 }
77
78 static void
libsa_fault_init()79 libsa_fault_init()
80 {
81 int vec;
82 uint32_t *insn;
83 uint32_t br_insn;
84
85 prom_vbr = get_vbr();
86
87 insn = vector_page;
88 br_insn = br(prom_vbr - (uint32_t)&vector_page - 4);
89 for (vec = 512; vec != 0; vec--) {
90 *insn++ = 0xf4005800; /* nop */
91 *insn++ = br_insn; /* br into prom vbr page */
92 }
93
94 /* override data access exception */
95 vector_page[3 * 2 + 1] =
96 br((uint32_t)&libsa_fault_handler -
97 (uint32_t)&vector_page[3 * 2 + 1]);
98 }
99
100 int
badaddr(void * addr,int len)101 badaddr(void *addr, int len)
102 {
103 int rc;
104
105 if (vector_page[0] == 0)
106 libsa_fault_init();
107
108 badaddr_psr = get_psr();
109 set_psr(badaddr_psr | PSR_IND);
110
111 set_vbr((uint32_t)&vector_page);
112
113 if (setjmp(&badaddr_jmpbuf) == 0) {
114 switch (len) {
115 case 1:
116 (void)*(volatile uint8_t *)addr;
117 rc = 0;
118 break;
119 case 2:
120 if ((uint32_t)addr & 1)
121 rc = 1;
122 else {
123 (void)*(volatile uint16_t *)addr;
124 rc = 0;
125 }
126 break;
127 case 4:
128 if ((uint32_t)addr & 3)
129 rc = 1;
130 else {
131 (void)*(volatile uint32_t *)addr;
132 rc = 0;
133 }
134 break;
135 default:
136 rc = 1;
137 break;
138 }
139 } else {
140 rc = 1;
141 }
142
143 set_vbr(prom_vbr);
144 flush_pipeline();
145 set_psr(badaddr_psr);
146
147 return rc;
148 }
149