| Commit | Line | Data |
|---|---|---|
| 82a2bc67 | 1 | /* |
| 2 | ||
| 82a2bc67 | 3 | This code was written as part of the CMU Common Lisp project at |
| 4 | Carnegie Mellon University, and has been placed in the public domain. | |
| 5 | ||
| 6 | */ | |
| 7 | ||
| 36232b68 | 8 | #include <stdio.h> |
| 571df509 | 9 | #ifdef SOLARIS |
| 10 | #include <sys/trap.h> | |
| 11 | #else | |
| 36232b68 | 12 | #include <machine/trap.h> |
| 571df509 | 13 | #endif |
| 36232b68 | 14 | |
| 571df509 | 15 | #include "arch.h" |
| 36232b68 | 16 | #include "lisp.h" |
| 17 | #include "internals.h" | |
| 18 | #include "globals.h" | |
| 19 | #include "validate.h" | |
| 20 | #include "os.h" | |
| 36232b68 | 21 | #include "lispregs.h" |
| 22 | #include "signal.h" | |
| 23 | #include "interrupt.h" | |
| 015a25b6 | 24 | #include "gencgc.h" |
| 25 | #include "breakpoint.h" | |
| 26 | #include "interr.h" | |
| 36232b68 | 27 | |
| 9a8c1c2f | 28 | char * |
| cb786538 | 29 | arch_init(fpu_mode_t mode) |
| 36232b68 | 30 | { |
| 571df509 | 31 | return 0; |
| 36232b68 | 32 | } |
| 33 | ||
| 9a8c1c2f | 34 | os_vm_address_t |
| 35 | arch_get_bad_addr(HANDLER_ARGS) | |
| 36232b68 | 36 | { |
| 015a25b6 | 37 | unsigned int badinst; |
| 36232b68 | 38 | int rs1; |
| cba6f60b RT |
39 | os_context_t *os_context = (os_context_t *) context; |
| 40 | ||
| 36232b68 | 41 | /* On the sparc, we have to decode the instruction. */ |
| 42 | ||
| 43 | /* Make sure it's not the pc thats bogus, and that it was lisp code */ | |
| 44 | /* that caused the fault. */ | |
| cba6f60b RT |
45 | if ((SC_PC(os_context) & 3) != 0 || |
| 46 | ((SC_PC(os_context) < READ_ONLY_SPACE_START || | |
| 47 | SC_PC(os_context) >= READ_ONLY_SPACE_START + read_only_space_size) && | |
| 48 | ((lispobj *) SC_PC(os_context) < current_dynamic_space && | |
| 49 | (lispobj *) SC_PC(os_context) >= | |
| 9a8c1c2f | 50 | current_dynamic_space + dynamic_space_size))) return 0; |
| 36232b68 | 51 | |
| cba6f60b | 52 | badinst = *(unsigned int *) SC_PC(os_context); |
| 36232b68 | 53 | |
| 54 | if ((badinst >> 30) != 3) | |
| 55 | /* All load/store instructions have op = 11 (binary) */ | |
| 571df509 | 56 | return 0; |
| 36232b68 | 57 | |
| 9a8c1c2f | 58 | rs1 = (badinst >> 14) & 0x1f; |
| 36232b68 | 59 | |
| 9a8c1c2f | 60 | if (badinst & (1 << 13)) { |
| 36232b68 | 61 | /* r[rs1] + simm(13) */ |
| 62 | int simm13 = badinst & 0x1fff; | |
| 63 | ||
| 9a8c1c2f | 64 | if (simm13 & (1 << 12)) |
| 65 | simm13 |= -1 << 13; | |
| 36232b68 | 66 | |
| cba6f60b | 67 | return (os_vm_address_t) (SC_REG(os_context, rs1) + simm13); |
| 9a8c1c2f | 68 | } else { |
| 36232b68 | 69 | /* r[rs1] + r[rs2] */ |
| 70 | int rs2 = badinst & 0x1f; | |
| 71 | ||
| cba6f60b | 72 | return (os_vm_address_t) (SC_REG(os_context, rs1) + SC_REG(os_context, rs2)); |
| 36232b68 | 73 | } |
| 74 | ||
| 75 | } | |
| 76 | ||
| 9a8c1c2f | 77 | void |
| cba6f60b | 78 | arch_skip_instruction(os_context_t *context) |
| 36232b68 | 79 | { |
| 80 | /* Skip the offending instruction */ | |
| 571df509 | 81 | SC_PC(context) = SC_NPC(context); |
| 82 | SC_NPC(context) += 4; | |
| 36232b68 | 83 | } |
| 84 | ||
| 9a8c1c2f | 85 | unsigned char * |
| 86 | arch_internal_error_arguments(struct sigcontext *scp) | |
| 36232b68 | 87 | { |
| 9a8c1c2f | 88 | return (unsigned char *) (SC_PC(scp) + 4); |
| 36232b68 | 89 | } |
| 90 | ||
| 9a8c1c2f | 91 | boolean |
| 92 | arch_pseudo_atomic_atomic(struct sigcontext *scp) | |
| 36232b68 | 93 | { |
| 263f0353 | 94 | return (SC_REG(scp, reg_ALLOC) & pseudo_atomic_Value); |
| 36232b68 | 95 | } |
| 96 | ||
| 9a8c1c2f | 97 | void |
| 98 | arch_set_pseudo_atomic_interrupted(struct sigcontext *scp) | |
| 36232b68 | 99 | { |
| 263f0353 | 100 | SC_REG(scp, reg_ALLOC) |= pseudo_atomic_InterruptedValue; |
| 36232b68 | 101 | } |
| 102 | ||
| 9a8c1c2f | 103 | unsigned long |
| 104 | arch_install_breakpoint(void *pc) | |
| 36232b68 | 105 | { |
| 9a8c1c2f | 106 | unsigned int *ptr = (unsigned int *) pc; |
| 015a25b6 | 107 | unsigned int result = *ptr; |
| 9a8c1c2f | 108 | |
| 36232b68 | 109 | *ptr = trap_Breakpoint; |
| 015a25b6 | 110 | os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int)); |
| 9a8c1c2f | 111 | |
| 36232b68 | 112 | return result; |
| 113 | } | |
| 114 | ||
| 9a8c1c2f | 115 | void |
| 116 | arch_remove_breakpoint(void *pc, unsigned long orig_inst) | |
| 36232b68 | 117 | { |
| 9a8c1c2f | 118 | *(unsigned int *) pc = (unsigned int) orig_inst; |
| 119 | os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int)); | |
| 36232b68 | 120 | } |
| 121 | ||
| 015a25b6 | 122 | static unsigned int *skipped_break_addr, displaced_after_inst; |
| 9a8c1c2f | 123 | |
| 571df509 | 124 | static sigset_t orig_sigmask; |
| 36232b68 | 125 | |
| 9a8c1c2f | 126 | void |
| 127 | arch_do_displaced_inst(struct sigcontext *scp, unsigned long orig_inst) | |
| 36232b68 | 128 | { |
| 9a8c1c2f | 129 | unsigned int *pc = (unsigned int *) SC_PC(scp); |
| 130 | unsigned int *npc = (unsigned int *) SC_NPC(scp); | |
| 571df509 | 131 | |
| 571df509 | 132 | orig_sigmask = scp->uc_sigmask; |
| 133 | sigemptyset(&scp->uc_sigmask); | |
| 134 | FILLBLOCKSET(&scp->uc_sigmask); | |
| 36232b68 | 135 | |
| 136 | *pc = orig_inst; | |
| 015a25b6 | 137 | os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int)); |
| 9a8c1c2f | 138 | |
| 36232b68 | 139 | skipped_break_addr = pc; |
| 140 | displaced_after_inst = *npc; | |
| 141 | *npc = trap_AfterBreakpoint; | |
| 015a25b6 | 142 | os_flush_icache((os_vm_address_t) npc, sizeof(unsigned int)); |
| 36232b68 | 143 | |
| 571df509 | 144 | #ifdef SOLARIS |
| 145 | /* XXX never tested */ | |
| 146 | setcontext(scp); | |
| 147 | #else | |
| 36232b68 | 148 | sigreturn(scp); |
| 571df509 | 149 | #endif |
| 36232b68 | 150 | } |
| 151 | ||
| af867264 | 152 | /* |
| 153 | * Look at the instruction at address PC and see if it's a trap | |
| 154 | * instruction with an immediate value. If so, set trapno to the trap | |
| 155 | * number, and return non-zero. If it's not a trap instruction, | |
| 156 | * return 0. | |
| 157 | */ | |
| 9a8c1c2f | 158 | boolean |
| 159 | trap_inst_p(unsigned int *pc, int *trapno) | |
| af867264 | 160 | { |
| 9a8c1c2f | 161 | unsigned int trap_inst; |
| 162 | ||
| 163 | trap_inst = *pc; | |
| 164 | ||
| 165 | if (((trap_inst >> 30) == 2) | |
| 166 | && (((trap_inst >> 19) & 0x3f) == 0x3a) | |
| 167 | && (((trap_inst >> 14) & 0x1f) == reg_ZERO) | |
| 168 | && (((trap_inst >> 13) & 1) == 1)) { | |
| 169 | /* | |
| 170 | * Got a trap instruction with immediate trap value. | |
| 171 | * Get the value and return. | |
| 172 | */ | |
| 173 | *trapno = (trap_inst & 0x3f); | |
| 174 | ||
| 175 | return 1; | |
| 176 | } else { | |
| 177 | *trapno = -1; | |
| 178 | return 0; | |
| af867264 | 179 | } |
| 180 | } | |
| 181 | ||
| 182 | ||
| 9a8c1c2f | 183 | static int |
| 184 | pseudo_atomic_trap_p(struct sigcontext *context) | |
| 36232b68 | 185 | { |
| 9a8c1c2f | 186 | unsigned int *pc; |
| 187 | unsigned int badinst; | |
| 188 | int trapno; | |
| 189 | int result; | |
| 190 | ||
| 191 | ||
| 192 | pc = (unsigned int *) SC_PC(context); | |
| 193 | badinst = *pc; | |
| 194 | result = 0; | |
| 195 | ||
| 196 | /* | |
| 197 | * Check to see if the current instruction is a trap #16. We check | |
| 198 | * to make sure this instruction was a trap instruction with rs1 = 0 | |
| 199 | * and a software trap number (immediate value) of 16. | |
| 200 | */ | |
| 201 | if (trap_inst_p(pc, &trapno) && (trapno == trap_PseudoAtomic)) { | |
| 202 | unsigned int previnst; | |
| 203 | ||
| 204 | previnst = pc[-1]; | |
| 205 | /* | |
| 206 | * Check to see if the previous instruction was an andcc alloc-tn, | |
| 207 | * pseudo_atomic_InterruptedValue, zero-tn instruction. | |
| 208 | */ | |
| 209 | if (((previnst >> 30) == 2) && (((previnst >> 19) & 0x3f) == 0x11) | |
| 210 | && (((previnst >> 14) & 0x1f) == reg_ALLOC) | |
| 211 | && (((previnst >> 25) & 0x1f) == reg_ZERO) | |
| 212 | && (((previnst >> 13) & 1) == 1) | |
| 213 | && ((previnst & 0x1fff) == pseudo_atomic_InterruptedValue)) { | |
| 214 | result = 1; | |
| 215 | } else { | |
| 216 | fprintf(stderr, | |
| 217 | "Oops! Got a pseudo atomic trap without a preceeding andcc!\n"); | |
| 218 | } | |
| cbc96305 | 219 | } |
| 9a8c1c2f | 220 | return result; |
| cbc96305 | 221 | } |
| 222 | ||
| af867264 | 223 | #ifdef GENCGC |
| 224 | /* | |
| 225 | * Return non-zero if the instruction is a trap 31 instruction | |
| 226 | */ | |
| 227 | ||
| 9a8c1c2f | 228 | boolean |
| 229 | allocation_trap_p(struct sigcontext * context) | |
| af867264 | 230 | { |
| 9a8c1c2f | 231 | int result; |
| 232 | unsigned int *pc; | |
| 233 | unsigned int or_inst; | |
| 234 | int trapno; | |
| 235 | ||
| 236 | result = 0; | |
| 237 | ||
| 238 | /* | |
| 239 | * Make sure this is a trap 31 instruction preceeded by an OR | |
| 240 | * instruction. | |
| 241 | */ | |
| 242 | ||
| 243 | pc = (unsigned int *) SC_PC(context); | |
| 244 | ||
| 245 | if (trap_inst_p(pc, &trapno) && (trapno == trap_Allocation)) { | |
| 246 | /* Got the trap. Is it preceeded by an OR instruction or SUB | |
| 247 | instruction? */ | |
| 248 | or_inst = pc[-1]; | |
| 249 | if ((((or_inst >> 30) == 2) && (((or_inst >> 19) & 0x1f) == 2)) || | |
| 250 | (((or_inst >> 30) == 2) && (((or_inst >> 19) & 0x1f) == 4))) { | |
| 251 | result = 1; | |
| 252 | } else { | |
| 253 | fprintf(stderr, | |
| 254 | "Whoa!!! Got an allocation trap not preceeded by an OR inst: 0x%08x!\n", | |
| 255 | or_inst); | |
| 256 | } | |
| af867264 | 257 | } |
| 258 | ||
| 9a8c1c2f | 259 | return result; |
| af867264 | 260 | } |
| 261 | #endif | |
| 262 | ||
| 263 | #if 0 | |
| 264 | /* Pop the stack frame that build_fake_control_stack_frame makes */ | |
| 9a8c1c2f | 265 | static void |
| 266 | pop_fake_control_stack_frame(struct sigcontext *context) | |
| af867264 | 267 | { |
| 9a8c1c2f | 268 | current_control_frame_pointer = (lispobj *) SC_REG(context, reg_CFP); |
| 269 | SC_REG(context, reg_OCFP) = current_control_frame_pointer[0]; | |
| 270 | SC_REG(context, reg_CODE) = current_control_frame_pointer[1]; | |
| 271 | SC_REG(context, reg_CSP) = SC_REG(context, reg_CFP); | |
| 272 | SC_REG(context, reg_CFP) = SC_REG(context, reg_OCFP); | |
| af867264 | 273 | } |
| 274 | #endif | |
| 275 | ||
| ba62ca95 | 276 | /* |
| 277 | * Use this function to enable the minimum number of signals we need | |
| 278 | * when our trap handler needs to call Lisp code that might cons. For | |
| 279 | * consing to work with gencgc, we need to be able to trap the SIGILL | |
| 280 | * signal to perform allocation. | |
| 281 | */ | |
| 9a8c1c2f | 282 | void |
| b8d0dfaf | 283 | enable_some_signals(void) |
| ba62ca95 | 284 | { |
| 285 | #ifdef GENCGC | |
| 9a8c1c2f | 286 | sigset_t sigs; |
| 287 | ||
| 288 | sigemptyset(&sigs); | |
| 289 | sigaddset(&sigs, SIGILL); | |
| 290 | sigprocmask(SIG_UNBLOCK, &sigs, NULL); | |
| ba62ca95 | 291 | #endif |
| 292 | } | |
| 293 | ||
| af867264 | 294 | #ifdef GENCGC |
| 9a8c1c2f | 295 | void |
| 296 | handle_allocation_trap(struct sigcontext *context) | |
| af867264 | 297 | { |
| 9a8c1c2f | 298 | unsigned int *pc; |
| 299 | unsigned int or_inst; | |
| 300 | int target; | |
| 301 | int size; | |
| 302 | int immed; | |
| 303 | boolean were_in_lisp; | |
| 304 | char *memory; | |
| 305 | sigset_t block; | |
| 306 | ||
| 307 | target = 0; | |
| 308 | size = 0; | |
| 309 | ||
| 6779af26 | 310 | #if 0 |
| 9a8c1c2f | 311 | /* |
| 312 | * Block all blockable signals. Need to do this because | |
| 313 | * sigill_handler enables the signals. When the handler returns, | |
| 314 | * signals should be enabled again, automatically. | |
| 315 | */ | |
| 316 | ||
| 317 | sigemptyset(&block); | |
| 318 | FILLBLOCKSET(&block); | |
| 319 | sigprocmask(SIG_BLOCK, &block, 0); | |
| 6779af26 | 320 | #else |
| 9a8c1c2f | 321 | /* |
| 322 | * Well, maybe not. sigill_handler probably shouldn't be unblocking | |
| 323 | * all signals. So, let's enable just the signals we need. Since | |
| 324 | * alloc might call GC, we need to have SIGILL enabled so we can do | |
| 325 | * allocation. Do we need more? | |
| 326 | */ | |
| 327 | enable_some_signals(); | |
| 6779af26 | 328 | #endif |
| 9a8c1c2f | 329 | |
| 330 | pc = (unsigned int *) SC_PC(context); | |
| 331 | or_inst = pc[-1]; | |
| 332 | ||
| 333 | /* | |
| 334 | * The instruction before this trap instruction had better be an OR | |
| 335 | * instruction or SUB instruction! | |
| 336 | */ | |
| 337 | ||
| 338 | if (((or_inst >> 30) == 2) && (((or_inst >> 19) & 0x1f) == 2)) { | |
| 339 | /* | |
| 340 | * An OR instruction. RS1 is the register we want to allocate | |
| 341 | * to. RS2 (or an immediate) is the size. | |
| 342 | */ | |
| 343 | ||
| 344 | target = (or_inst >> 14) & 0x1f; | |
| 345 | ||
| 346 | immed = (or_inst >> 13) & 1; | |
| 347 | ||
| 348 | if (immed == 1) { | |
| 349 | size = or_inst & 0x1fff; | |
| 350 | } else { | |
| 351 | size = or_inst & 0x1f; | |
| 352 | size = SC_REG(context, size); | |
| 353 | } | |
| 354 | } else if (((or_inst >> 30) == 2) && (((or_inst >> 19) & 0x1f) == 4)) { | |
| 355 | /* | |
| 356 | * A SUB instruction. RD is the register to allocate to, RS2 | |
| 357 | * (or an immediate) is the size. | |
| 358 | */ | |
| 359 | ||
| 360 | target = (or_inst >> 25) & 0x1f; | |
| 361 | immed = (or_inst >> 13) & 1; | |
| 362 | if (immed == 1) { | |
| 363 | size = or_inst & 0x1fff; | |
| 364 | } else { | |
| 365 | size = or_inst & 0x1f; | |
| 366 | size = SC_REG(context, size); | |
| 367 | } | |
| af867264 | 368 | } |
| 9a8c1c2f | 369 | |
| 370 | ||
| 371 | /* | |
| 372 | * I don't think it's possible for us NOT to be in lisp when we get | |
| 373 | * here. Remove this later? | |
| 374 | */ | |
| 375 | were_in_lisp = !foreign_function_call_active; | |
| 376 | ||
| 377 | if (were_in_lisp) { | |
| 378 | fake_foreign_function_call(context); | |
| 379 | } else { | |
| 380 | fprintf(stderr, "**** Whoa! allocation trap and we weren't in lisp!\n"); | |
| af867264 | 381 | } |
| 723055bb | 382 | |
| 9a8c1c2f | 383 | /* |
| 384 | * alloc-tn was incremented by size. If we get here, we need to | |
| 385 | * decrement it by size to restore it's original value. | |
| 386 | */ | |
| 387 | /* current_dynamic_space_free_pointer = (lispobj *) SC_REG(context, reg_ALLOC); */ | |
| 388 | current_dynamic_space_free_pointer = | |
| 389 | (lispobj *) ((long) current_dynamic_space_free_pointer - size); | |
| 390 | ||
| 391 | /* | |
| 392 | * Allocate some memory, store the memory address in target. | |
| 393 | */ | |
| af867264 | 394 | |
| 395 | #if 0 | |
| 9a8c1c2f | 396 | fprintf(stderr, "Alloc %d to %s\n", size, lisp_register_names[target]); |
| af867264 | 397 | #endif |
| 55c3e8e3 | 398 | |
| 9a8c1c2f | 399 | memory = (char *) alloc(size); |
| 400 | SC_REG(context, target) = (unsigned long) memory; | |
| 401 | SC_REG(context, reg_ALLOC) = | |
| 402 | (unsigned long) current_dynamic_space_free_pointer; | |
| af867264 | 403 | |
| 9a8c1c2f | 404 | if (were_in_lisp) { |
| 405 | undo_fake_foreign_function_call(context); | |
| af867264 | 406 | } |
| 9a8c1c2f | 407 | |
| af867264 | 408 | } |
| 409 | #endif | |
| 36232b68 | 410 | |
| 887ae1dd | 411 | /* |
| 412 | * How to identify an illegal instruction trap and a trap instruction | |
| 413 | * trap. | |
| 414 | */ | |
| 415 | #ifdef SOLARIS | |
| 416 | #define ILLTRAP_INST ILL_ILLOPC | |
| 417 | #define TRAP_INST(code) (CODE(code) == ILL_ILLTRP) | |
| 418 | #else | |
| 419 | #define ILLTRAP_INST T_UNIMP_INSTR | |
| 420 | #define TRAP_INST(code) ((CODE(code) >= T_SOFTWARE_TRAP + 16) && (CODE(code) < T_SOFTWARE_TRAP + 32)) | |
| 421 | #endif | |
| cbc96305 | 422 | |
| 9a8c1c2f | 423 | static void |
| 424 | sigill_handler(HANDLER_ARGS) | |
| cbc96305 | 425 | { |
| cba6f60b RT |
426 | os_context_t *os_context = (os_context_t *) context; |
| 427 | ||
| 571df509 | 428 | SAVE_CONTEXT(); |
| 429 | ||
| 6779af26 | 430 | /* |
| 431 | * Do we really want to have the same signals as in the context? | |
| 432 | * This would typically enable all signals, I think. But there | |
| 433 | * are comments in interrupt_handle_now that says we want to | |
| 434 | * alloc_sap while interrupts are disabled. The interrupt | |
| 435 | * handlers that eventually get called from here will re-enable | |
| 436 | * interrupts at the appropriate time, so we don't do anything | |
| 437 | * here. | |
| 438 | * | |
| 439 | * (I'm guessing here. I don't really know if this is right or | |
| 440 | * not, but it doesn't seem to cause harm, and it does seem to be | |
| 441 | * a bad idea to have interrupts enabled here.) | |
| 442 | */ | |
| 443 | #if 0 | |
| cba6f60b | 444 | sigprocmask(SIG_SETMASK, &os_context->uc_sigmask, 0); |
| 6779af26 | 445 | #endif |
| 36232b68 | 446 | |
| 9a8c1c2f | 447 | if (CODE(code) == ILLTRAP_INST) { |
| 887ae1dd | 448 | int illtrap_code; |
| cbc96305 | 449 | unsigned int inst; |
| cba6f60b | 450 | unsigned int *pc = (unsigned int *) (SC_PC(os_context)); |
| 36232b68 | 451 | |
| 686f8394 | 452 | inst = *pc; |
| 36232b68 | 453 | |
| 887ae1dd | 454 | illtrap_code = inst & 0x3fffff; |
| 455 | ||
| 456 | switch (illtrap_code) { | |
| 36232b68 | 457 | case trap_PendingInterrupt: |
| cba6f60b RT |
458 | arch_skip_instruction(os_context); |
| 459 | interrupt_handle_pending(os_context); | |
| 9a8c1c2f | 460 | break; |
| 36232b68 | 461 | |
| 462 | case trap_Halt: | |
| cba6f60b | 463 | fake_foreign_function_call(os_context); |
| 9a8c1c2f | 464 | lose("%%primitive halt called; the party is over.\n"); |
| 36232b68 | 465 | |
| 466 | case trap_Error: | |
| 467 | case trap_Cerror: | |
| cba6f60b | 468 | interrupt_internal_error(signal, code, os_context, |
| 9a8c1c2f | 469 | illtrap_code == trap_Cerror); |
| 470 | break; | |
| 36232b68 | 471 | |
| 472 | case trap_Breakpoint: | |
| 9a8c1c2f | 473 | enable_some_signals(); |
| cba6f60b | 474 | handle_breakpoint(signal, CODE(code), os_context); |
| 9a8c1c2f | 475 | break; |
| 36232b68 | 476 | |
| 477 | case trap_FunctionEndBreakpoint: | |
| 9a8c1c2f | 478 | enable_some_signals(); |
| cba6f60b | 479 | SC_PC(os_context) = |
| 9a8c1c2f | 480 | (long) handle_function_end_breakpoint(signal, CODE(code), |
| cba6f60b RT |
481 | os_context); |
| 482 | SC_NPC(os_context) = SC_PC(os_context) + 4; | |
| 9a8c1c2f | 483 | break; |
| 36232b68 | 484 | |
| 485 | case trap_AfterBreakpoint: | |
| 9a8c1c2f | 486 | *skipped_break_addr = trap_Breakpoint; |
| 487 | skipped_break_addr = NULL; | |
| cba6f60b RT |
488 | *(unsigned long *) SC_PC(os_context) = displaced_after_inst; |
| 489 | os_context->uc_sigmask = orig_sigmask; | |
| 490 | os_flush_icache((os_vm_address_t) SC_PC(os_context), | |
| 9a8c1c2f | 491 | |
| 492 | sizeof(unsigned long)); | |
| 493 | break; | |
| 36232b68 | 494 | |
| 3e309c44 | 495 | #ifdef trap_DynamicSpaceOverflowWarning |
| 9a8c1c2f | 496 | case trap_DynamicSpaceOverflowWarning: |
| cba6f60b | 497 | arch_skip_instruction(os_context); |
| 9a8c1c2f | 498 | enable_some_signals(); |
| 499 | interrupt_handle_space_overflow(SymbolFunction | |
| 500 | (DYNAMIC_SPACE_OVERFLOW_WARNING_HIT), | |
| cba6f60b | 501 | os_context); |
| 9a8c1c2f | 502 | break; |
| 3e309c44 | 503 | #endif |
| 504 | #ifdef trap_DynamicSpaceOverflowError | |
| 9a8c1c2f | 505 | case trap_DynamicSpaceOverflowError: |
| cba6f60b | 506 | arch_skip_instruction(os_context); |
| 9a8c1c2f | 507 | enable_some_signals(); |
| 508 | interrupt_handle_space_overflow(SymbolFunction | |
| 509 | (DYNAMIC_SPACE_OVERFLOW_ERROR_HIT), | |
| cba6f60b | 510 | os_context); |
| 9a8c1c2f | 511 | break; |
| 3e309c44 | 512 | #endif |
| 36232b68 | 513 | default: |
| cba6f60b | 514 | interrupt_handle_now(signal, code, os_context); |
| 9a8c1c2f | 515 | break; |
| 516 | } | |
| 517 | } else if (TRAP_INST(code)) { | |
| cba6f60b | 518 | if (pseudo_atomic_trap_p(os_context)) { |
| 9a8c1c2f | 519 | /* A trap instruction from a pseudo-atomic. We just need |
| 520 | to fixup up alloc-tn to remove the interrupted flag, | |
| 521 | skip over the trap instruction, and then handle the | |
| 522 | pending interrupt(s). */ | |
| cba6f60b RT |
523 | SC_REG(os_context, reg_ALLOC) &= ~lowtag_Mask; |
| 524 | arch_skip_instruction(os_context); | |
| 525 | interrupt_handle_pending(os_context); | |
| 36232b68 | 526 | } |
| af867264 | 527 | #ifdef GENCGC |
| cba6f60b | 528 | else if (allocation_trap_p(os_context)) { |
| 9a8c1c2f | 529 | /* An allocation trap. Call the trap handler and then skip |
| 530 | this instruction */ | |
| cba6f60b RT |
531 | handle_allocation_trap(os_context); |
| 532 | arch_skip_instruction(os_context); | |
| 9a8c1c2f | 533 | } |
| af867264 | 534 | #endif |
| 9a8c1c2f | 535 | else { |
| cba6f60b | 536 | interrupt_internal_error(signal, code, os_context, FALSE); |
| 9a8c1c2f | 537 | } |
| 538 | } else { | |
| cba6f60b | 539 | interrupt_handle_now(signal, code, os_context); |
| 9a8c1c2f | 540 | } |
| 36232b68 | 541 | } |
| 542 | ||
| 9a8c1c2f | 543 | void |
| b8d0dfaf | 544 | arch_install_interrupt_handlers(void) |
| 36232b68 | 545 | { |
| 9a8c1c2f | 546 | interrupt_install_low_level_handler(SIGILL, sigill_handler); |
| 36232b68 | 547 | } |
| 548 | ||
| 549 | ||
| 9a8c1c2f | 550 | extern lispobj call_into_lisp(lispobj fun, lispobj * args, int nargs); |
| 36232b68 | 551 | |
| 9a8c1c2f | 552 | lispobj |
| 553 | funcall0(lispobj function) | |
| 36232b68 | 554 | { |
| 555 | lispobj *args = current_control_stack_pointer; | |
| 556 | ||
| 557 | return call_into_lisp(function, args, 0); | |
| 558 | } | |
| 559 | ||
| 9a8c1c2f | 560 | lispobj |
| 561 | funcall1(lispobj function, lispobj arg0) | |
| 36232b68 | 562 | { |
| 563 | lispobj *args = current_control_stack_pointer; | |
| 564 | ||
| 565 | current_control_stack_pointer += 1; | |
| 566 | args[0] = arg0; | |
| 567 | ||
| 568 | return call_into_lisp(function, args, 1); | |
| 569 | } | |
| 570 | ||
| 9a8c1c2f | 571 | lispobj |
| 572 | funcall2(lispobj function, lispobj arg0, lispobj arg1) | |
| 36232b68 | 573 | { |
| 574 | lispobj *args = current_control_stack_pointer; | |
| 575 | ||
| 576 | current_control_stack_pointer += 2; | |
| 577 | args[0] = arg0; | |
| 578 | args[1] = arg1; | |
| 579 | ||
| 580 | return call_into_lisp(function, args, 2); | |
| 581 | } | |
| 582 | ||
| 9a8c1c2f | 583 | lispobj |
| 584 | funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2) | |
| 36232b68 | 585 | { |
| 586 | lispobj *args = current_control_stack_pointer; | |
| 587 | ||
| 588 | current_control_stack_pointer += 3; | |
| 589 | args[0] = arg0; | |
| 590 | args[1] = arg1; | |
| 591 | args[2] = arg2; | |
| 592 | ||
| 593 | return call_into_lisp(function, args, 3); | |
| 594 | } | |
| 49fdb025 | 595 | |
| 596 | #ifdef LINKAGE_TABLE | |
| 597 | ||
| 598 | /* This is mostly stolen from the x86 version, with adjustments for sparc */ | |
| 599 | ||
| 600 | /* | |
| 601 | * Linkage entry size is 16, because we need at least 3 instruction to | |
| 602 | * implement a jump: | |
| 603 | * | |
| 604 | * sethi %hi(addr), %g4 | |
| 605 | * jmpl [%g4 + %lo(addr)], %g5 | |
| 606 | * nop | |
| 607 | * | |
| 608 | * The Sparc V9 ABI seems to use 8 words for its jump tables. Maybe | |
| 609 | * we should do the same? | |
| 610 | */ | |
| 611 | ||
| 612 | /* | |
| 613 | * This had better match lisp::target-foreign-linkage-entry-size in | |
| 614 | * sparc/parms.lisp! Each entry is 4 instructions long, so 16 bytes. | |
| 615 | */ | |
| 616 | #ifndef LinkageEntrySize | |
| 617 | #define LinkageEntrySize (4*4) | |
| 618 | #endif | |
| 619 | ||
| 620 | ||
| 621 | /* | |
| 622 | * Define the registers to use in the linkage jump table. Can be the | |
| 623 | * same. This MUST be coordinated with resolve_linkage_tramp which | |
| 624 | * needs to know the register used for LINKAGE_ADDR_REG. | |
| 625 | * | |
| 626 | * Some care must be exercised when choosing these. It has to be a | |
| 627 | * register that is not otherwise being used. reg_L0 is a good | |
| 628 | * choice. call_into_c trashes reg_L0 without preserving it, so we | |
| 629 | * can trash it in the linkage jump table. For the linkage entries | |
| 630 | * that call resolve_linkage_tramp, we can use reg_L0 too because | |
| 631 | * resolve_linkage_tramp is always called from call_into_c. (This is | |
| 632 | * enforced by having new-genesis create an entry for call_into_c, so | |
| 633 | * we never have to do a lookup for call_into_c.) | |
| eb6ce575 | 634 | * |
| 635 | * The LINKAGE_ADDR_REG is important! resolve_linkage_tramp needs to | |
| 636 | * be coordinated with this because that's how resolve_linkage_tramp | |
| 637 | * figures out what linkage entry it's being called from. | |
| 49fdb025 | 638 | */ |
| 639 | #define LINKAGE_TEMP_REG reg_L0 | |
| 640 | #define LINKAGE_ADDR_REG reg_L0 | |
| 641 | ||
| 642 | /* | |
| 643 | * Insert the necessary jump instructions at the given address. | |
| 644 | * Return the address of the next word | |
| 645 | */ | |
| 9a8c1c2f | 646 | void * |
| 647 | arch_make_jump_entry(void *reloc_addr, void *target_addr) | |
| 49fdb025 | 648 | { |
| 9a8c1c2f | 649 | |
| 650 | /* | |
| 651 | * Make JMP to function entry. | |
| 652 | * | |
| 653 | * The instruction sequence is: | |
| 654 | * | |
| 655 | * sethi %hi(addr), temp_reg | |
| 656 | * jmpl %temp_reg + %lo(addr), %addr_reg | |
| 657 | * nop | |
| 658 | * nop | |
| 659 | * | |
| 660 | */ | |
| 661 | int *inst_ptr; | |
| 662 | unsigned long hi; /* Top 22 bits of address */ | |
| 663 | unsigned long lo; /* Low 10 bits of address */ | |
| 664 | unsigned int inst; | |
| 665 | ||
| 666 | inst_ptr = (int *) reloc_addr; | |
| 667 | ||
| 668 | /* | |
| 669 | * Split the target address into hi and lo parts for the sethi | |
| 670 | * instruction. hi is the top 22 bits. lo is the low 10 bits. | |
| 671 | */ | |
| 672 | hi = (unsigned long) target_addr; | |
| 673 | lo = hi & 0x3ff; | |
| 674 | hi >>= 10; | |
| 675 | ||
| 676 | /* | |
| 677 | * sethi %hi(addr), temp_reg | |
| 678 | */ | |
| 679 | ||
| 680 | inst = (0 << 30) | (LINKAGE_TEMP_REG << 25) | (4 << 22) | hi; | |
| 681 | *inst_ptr++ = inst; | |
| 682 | ||
| 683 | /* | |
| 684 | * jmpl [temp_reg + %lo(addr)], addr_reg | |
| 685 | */ | |
| 686 | ||
| 687 | inst = (2U << 30) | (LINKAGE_ADDR_REG << 25) | (0x38 << 19) | |
| 688 | | (LINKAGE_TEMP_REG << 14) | (1 << 13) | lo; | |
| 689 | *inst_ptr++ = inst; | |
| 690 | ||
| 691 | /* nop (really sethi 0, %g0) */ | |
| 692 | ||
| 693 | inst = (0 << 30) | (0 << 25) | (4 << 22) | 0; | |
| 694 | ||
| 695 | *inst_ptr++ = inst; | |
| 696 | *inst_ptr++ = inst; | |
| 697 | ||
| 698 | os_flush_icache((os_vm_address_t) reloc_addr, | |
| 699 | (char *) inst_ptr - (char *) reloc_addr); | |
| 700 | return reloc_addr; | |
| 49fdb025 | 701 | } |
| 702 | ||
| 9a8c1c2f | 703 | void |
| 704 | arch_make_linkage_entry(long linkage_entry, void *target_addr, long type) | |
| 49fdb025 | 705 | { |
| 9a8c1c2f | 706 | int *reloc_addr = (int *) (FOREIGN_LINKAGE_SPACE_START |
| 49fdb025 | 707 | |
| 9a8c1c2f | 708 | + linkage_entry * LinkageEntrySize); |
| 709 | ||
| 710 | if (type == 1) { /* code reference */ | |
| 711 | arch_make_jump_entry(reloc_addr, target_addr); | |
| 712 | } else if (type == 2) { | |
| 713 | *(unsigned long *) reloc_addr = (unsigned long) target_addr; | |
| 49fdb025 | 714 | } |
| 715 | } | |
| 716 | ||
| 717 | /* Make a the entry a jump to resolve_linkage_tramp. */ | |
| 718 | ||
| 719 | extern void resolve_linkage_tramp(void); | |
| 720 | ||
| 9a8c1c2f | 721 | void |
| 722 | arch_make_lazy_linkage(long linkage_entry) | |
| 49fdb025 | 723 | { |
| 9a8c1c2f | 724 | arch_make_linkage_entry(linkage_entry, (void *) resolve_linkage_tramp, 1); |
| 49fdb025 | 725 | } |
| 726 | ||
| 727 | /* Get linkage entry. We're given the return address which should be | |
| 728 | the address of the jmpl instruction (2nd word) of the linkage | |
| 729 | entry. Figure out which entry this address belong to. */ | |
| 730 | ||
| 9a8c1c2f | 731 | long |
| 732 | arch_linkage_entry(unsigned long retaddr) | |
| 49fdb025 | 733 | { |
| 9a8c1c2f | 734 | return (retaddr - (FOREIGN_LINKAGE_SPACE_START)) |
| 735 | / LinkageEntrySize; | |
| 49fdb025 | 736 | } |
| 737 | #endif /* LINKAGE_TABLE */ |