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 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 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 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 73 br(uint32_t delta) 74 { 75 return 0xc0000000 | (((int32_t)delta >> 2) & 0x03ffffff); 76 } 77 78 static void 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 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