1 /* $NetBSD: fault.c,v 1.7 2002/01/05 22:41:47 chris Exp $ */ 2 3 /* 4 * Copyright (c) 1994-1997 Mark Brinicombe. 5 * Copyright (c) 1994 Brini. 6 * All rights reserved. 7 * 8 * This code is derived from software written for Brini by Mark Brinicombe 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by Brini. 21 * 4. The name of the company nor the name of the author may be used to 22 * endorse or promote products derived from this software without specific 23 * prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY BRINI ``AS IS'' AND ANY EXPRESS OR IMPLIED 26 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 27 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28 * IN NO EVENT SHALL BRINI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * 37 * RiscBSD kernel project 38 * 39 * fault.c 40 * 41 * Fault handlers 42 * 43 * Created : 28/11/94 44 */ 45 46 #include "opt_ddb.h" 47 #include "opt_pmap_debug.h" 48 49 #include <sys/types.h> 50 #include <sys/param.h> 51 #include <sys/systm.h> 52 #include <sys/proc.h> 53 #include <sys/user.h> 54 #include <sys/kernel.h> 55 56 #include <uvm/uvm_extern.h> 57 58 #include <machine/frame.h> 59 #include <arm/arm32/katelib.h> 60 #include <machine/cpu.h> 61 #include <machine/intr.h> 62 #ifdef DDB 63 #include <machine/db_machdep.h> 64 #endif 65 66 #include <arch/arm/arm/disassem.h> 67 #include <arm/arm32/machdep.h> 68 69 int cowfault __P((vaddr_t)); 70 int fetchuserword __P((u_int address, u_int *location)); 71 extern char fusubailout[]; 72 73 static void report_abort __P((const char *, u_int, u_int, u_int)); 74 75 /* Abort code */ 76 77 /* Define text descriptions of the different aborts */ 78 79 static const char *aborts[16] = { 80 "Write buffer fault", 81 "Alignment fault", 82 "Write buffer fault", 83 "Alignment fault", 84 "Bus error (LF section)", 85 "Translation fault (section)", 86 "Bus error (page)", 87 "Translation fault (page)", 88 "Bus error (section)", 89 "Domain error (section)", 90 "Bus error (page)", 91 "Domain error (page)", 92 "Bus error trans (L1)", 93 "Permission error (section)", 94 "Bus error trans (L2)", 95 "Permission error (page)" 96 }; 97 98 static void 99 report_abort(prefix, fault_status, fault_address, fault_pc) 100 const char *prefix; 101 u_int fault_status; 102 u_int fault_address; 103 u_int fault_pc; 104 { 105 #ifndef DEBUG 106 if (prefix == NULL) { 107 #endif 108 if (prefix) 109 printf("%s ", prefix); 110 printf("Data abort: '%s' status=%03x address=%08x PC=%08x\n", 111 aborts[fault_status & FAULT_TYPE_MASK], 112 fault_status & 0xfff, fault_address, fault_pc); 113 #ifndef DEBUG 114 } 115 #endif 116 } 117 118 static __volatile int data_abort_expected; 119 static __volatile int data_abort_received; 120 121 int 122 badaddr_read(void *addr, size_t size, void *rptr) 123 { 124 u_long rcpt; 125 int rv; 126 127 /* Tell the Data Abort handler that we're expecting one. */ 128 data_abort_received = 0; 129 data_abort_expected = 1; 130 131 cpu_drain_writebuf(); 132 133 /* Read from the test address. */ 134 switch (size) { 135 case sizeof(uint8_t): 136 __asm __volatile("ldrb %0, [%1]" 137 : "=r" (rcpt) 138 : "r" (addr)); 139 break; 140 141 case sizeof(uint16_t): 142 __asm __volatile("ldrh %0, [%1]" 143 : "=r" (rcpt) 144 : "r" (addr)); 145 break; 146 147 case sizeof(uint32_t): 148 __asm __volatile("ldr %0, [%1]" 149 : "=r" (rcpt) 150 : "r" (addr)); 151 break; 152 153 default: 154 data_abort_expected = 0; 155 panic("badaddr: invalid size (%lu)\n", (u_long) size); 156 } 157 158 /* Disallow further Data Aborts. */ 159 data_abort_expected = 0; 160 161 rv = data_abort_received; 162 data_abort_received = 0; 163 164 /* Copy the data back if no fault occurred. */ 165 if (rptr != NULL && rv == 0) { 166 switch (size) { 167 case sizeof(uint8_t): 168 *(uint8_t *) rptr = rcpt; 169 break; 170 171 case sizeof(uint16_t): 172 *(uint16_t *) rptr = rcpt; 173 break; 174 175 case sizeof(uint32_t): 176 *(uint32_t *) rptr = rcpt; 177 break; 178 } 179 } 180 181 /* Return true if the address was invalid. */ 182 return (rv); 183 } 184 185 /* 186 * void data_abort_handler(trapframe_t *frame) 187 * 188 * Abort handler called when read/write occurs at an address of 189 * a non existent or restricted (access permissions) memory page. 190 * We first need to identify the type of page fault. 191 */ 192 193 #define TRAP_CODE ((fault_status & 0x0f) | (fault_address & 0xfffffff0)) 194 195 void 196 data_abort_handler(frame) 197 trapframe_t *frame; 198 { 199 struct proc *p; 200 struct pcb *pcb; 201 u_int fault_address; 202 u_int fault_status; 203 u_int fault_pc; 204 u_int fault_instruction; 205 int fault_code; 206 int user; 207 int error; 208 void *onfault; 209 210 /* 211 * If we were expecting a Data Abort, signal that we got 212 * one, adjust the PC to skip the faulting insn, and 213 * return. 214 */ 215 if (data_abort_expected) { 216 data_abort_received = 1; 217 frame->tf_pc += INSN_SIZE; 218 return; 219 } 220 221 /* 222 * Must get fault address and status from the CPU before 223 * re-enabling interrupts. (Interrupt handlers may take 224 * R/M emulation faults.) 225 */ 226 fault_address = cpu_faultaddress(); 227 fault_status = cpu_faultstatus(); 228 fault_pc = frame->tf_pc; 229 230 /* 231 * Enable IRQ's (disabled by CPU on abort) if trapframe 232 * shows they were enabled. 233 */ 234 if (!(frame->tf_spsr & I32_bit)) 235 enable_interrupts(I32_bit); 236 237 #ifdef DEBUG 238 if ((GetCPSR() & PSR_MODE) != PSR_SVC32_MODE) 239 panic("data_abort_handler: not in SVC32 mode"); 240 #endif 241 242 /* Update vmmeter statistics */ 243 uvmexp.traps++; 244 245 /* Extract the fault code from the fault status */ 246 fault_code = fault_status & FAULT_TYPE_MASK; 247 248 /* Get the current proc structure or proc0 if there is none */ 249 if ((p = curproc) == NULL) 250 p = &proc0; 251 252 /* 253 * can't use curpcb, as it might be NULL; and we have p in 254 * a register anyway 255 */ 256 pcb = &p->p_addr->u_pcb; 257 258 /* fusubailout is used by [fs]uswintr to avoid page faulting */ 259 if (pcb->pcb_onfault 260 && ((fault_code != FAULT_TRANS_S && fault_code != FAULT_TRANS_P && 261 fault_code != FAULT_PERM_S && fault_code != FAULT_PERM_P) 262 || pcb->pcb_onfault == fusubailout)) { 263 264 copyfault: 265 #ifdef DEBUG 266 printf("Using pcb_onfault=%p addr=%08x st=%08x p=%p\n", 267 pcb->pcb_onfault, fault_address, fault_status, p); 268 #endif 269 frame->tf_pc = (u_int)pcb->pcb_onfault; 270 if ((frame->tf_spsr & PSR_MODE) == PSR_USR32_MODE) 271 panic("Yikes pcb_onfault=%p during USR mode fault\n", 272 pcb->pcb_onfault); 273 return; 274 } 275 276 /* More debug stuff */ 277 278 fault_instruction = ReadWord(fault_pc); 279 280 #ifdef PMAP_DEBUG 281 if (pmap_debug_level >= 0) { 282 report_abort(NULL, fault_status, fault_address, fault_pc); 283 printf("Instruction @V%08x = %08x\n", 284 fault_pc, fault_instruction); 285 } 286 #endif 287 288 /* Call the cpu specific abort fixup routine */ 289 error = cpu_dataabt_fixup(frame); 290 if (error == ABORT_FIXUP_RETURN) 291 return; 292 if (error == ABORT_FIXUP_FAILED) { 293 printf("pc = 0x%08x, insn = ", fault_pc); 294 disassemble(fault_pc); 295 panic("data abort fixup failed\n"); 296 } 297 298 #ifdef PMAP_DEBUG 299 if (pmap_debug_level >= 0) 300 printf("fault in process %p\n", p); 301 #endif 302 303 #ifdef DEBUG 304 /* Is this needed ? */ 305 if (pcb != curpcb) { 306 printf("data_abort: Alert ! pcb(%p) != curpcb(%p)\n", 307 pcb, curpcb); 308 printf("data_abort: Alert ! proc(%p), curproc(%p)\n", 309 p, curproc); 310 } 311 #endif /* DEBUG */ 312 313 /* Were we in user mode when the abort occurred ? */ 314 if ((frame->tf_spsr & PSR_MODE) == PSR_USR32_MODE) { 315 /* 316 * Note that the fault was from USR mode. 317 */ 318 user = 1; 319 p->p_addr->u_pcb.pcb_tf = frame; 320 } else 321 user = 0; 322 323 /* Now act on the fault type */ 324 switch (fault_code) { 325 case FAULT_WRTBUF_0: /* Write Buffer Fault */ 326 case FAULT_WRTBUF_1: /* Write Buffer Fault */ 327 /* If this happens forget it no point in continuing */ 328 329 /* FALLTHROUGH */ 330 331 case FAULT_ALIGN_0: /* Alignment Fault */ 332 case FAULT_ALIGN_1: /* Alignment Fault */ 333 /* 334 * Really this should just kill the process. 335 * Alignment faults are turned off in the kernel 336 * in order to get better performance from shorts with 337 * GCC so an alignment fault means somebody has played 338 * with the control register in the CPU. Might as well 339 * panic as the kernel was not compiled for aligned accesses. 340 */ 341 342 /* FALLTHROUGH */ 343 344 case FAULT_BUSERR_0: /* Bus Error LF Section */ 345 case FAULT_BUSERR_1: /* Bus Error Page */ 346 case FAULT_BUSERR_2: /* Bus Error Section */ 347 case FAULT_BUSERR_3: /* Bus Error Page */ 348 /* What will accutally cause a bus error ? */ 349 /* Real bus errors are not a process problem but hardware */ 350 351 /* FALLTHROUGH */ 352 353 case FAULT_DOMAIN_S: /* Section Domain Error Fault */ 354 case FAULT_DOMAIN_P: /* Page Domain Error Fault*/ 355 /* 356 * Right well we dont use domains, everything is 357 * always a client and thus subject to access permissions. 358 * If we get a domain error then we have corrupts PTE's 359 * so we might as well die ! 360 * I suppose eventually this should just kill the process 361 * who owns the PTE's but if this happens it implies a 362 * kernel problem. 363 */ 364 365 /* FALLTHROUGH */ 366 367 case FAULT_BUSTRNL1: /* Bus Error Trans L1 Fault */ 368 case FAULT_BUSTRNL2: /* Bus Error Trans L2 Fault */ 369 /* 370 * These faults imply that the PTE is corrupt. 371 * Likely to be a kernel fault so we had better stop. 372 */ 373 374 /* FALLTHROUGH */ 375 376 default : 377 /* Are there any combinations I have missed ? */ 378 report_abort(NULL, fault_status, fault_address, fault_pc); 379 380 we_re_toast: 381 /* 382 * Were are dead, try and provide some debug 383 * information before dying. 384 */ 385 #ifdef DDB 386 printf("Unhandled trap (frame = %p)\n", frame); 387 report_abort(NULL, fault_status, fault_address, fault_pc); 388 kdb_trap(-1, frame); 389 return; 390 #else 391 panic("Unhandled trap (frame = %p)", frame); 392 #endif /* DDB */ 393 394 case FAULT_TRANS_P: /* Page Translation Fault */ 395 case FAULT_PERM_P: /* Page Permission Fault */ 396 case FAULT_TRANS_S: /* Section Translation Fault */ 397 case FAULT_PERM_S: /* Section Permission Fault */ 398 /* 399 * Page/section translation/permission fault -- need to fault in 400 * the page and possibly the page table page. 401 */ 402 { 403 register vaddr_t va; 404 register struct vmspace *vm = p->p_vmspace; 405 register struct vm_map *map; 406 int rv; 407 vm_prot_t ftype; 408 extern struct vm_map *kernel_map; 409 410 va = trunc_page((vaddr_t)fault_address); 411 412 #ifdef PMAP_DEBUG 413 if (pmap_debug_level >= 0) 414 printf("page fault: addr=V%08lx ", va); 415 #endif 416 417 /* 418 * It is only a kernel address space fault iff: 419 * 1. user == 0 and 420 * 2. pcb_onfault not set or 421 * 3. pcb_onfault set but supervisor space fault 422 * The last can occur during an exec() copyin where the 423 * argument space is lazy-allocated. 424 */ 425 if (!user && 426 (va >= VM_MIN_KERNEL_ADDRESS || va < VM_MIN_ADDRESS)) { 427 /* Was the fault due to the FPE/IPKDB ? */ 428 if ((frame->tf_spsr & PSR_MODE) == PSR_UND32_MODE) { 429 report_abort("UND32", fault_status, 430 fault_address, fault_pc); 431 trapsignal(p, SIGSEGV, TRAP_CODE); 432 433 /* 434 * Force exit via userret() 435 * This is necessary as the FPE is an extension 436 * to userland that actually runs in a 437 * priveledged mode but uses USR mode 438 * permissions for its accesses. 439 */ 440 userret(p); 441 return; 442 } 443 map = kernel_map; 444 } else 445 map = &vm->vm_map; 446 447 #ifdef PMAP_DEBUG 448 if (pmap_debug_level >= 0) 449 printf("vmmap=%p ", map); 450 #endif 451 452 if (map == NULL) 453 panic("No map for fault address\n"); 454 455 /* 456 * We need to know whether the page should be mapped 457 * as R or R/W. The MMU does not give us the info as 458 * to whether the fault was caused by a read or a write. 459 * This means we need to disassemble the instruction 460 * responsible and determine if it was a read or write 461 * instruction. 462 */ 463 /* STR instruction ? */ 464 if ((fault_instruction & 0x0c100000) == 0x04000000) 465 ftype = VM_PROT_READ | VM_PROT_WRITE; 466 /* STM or CDT instruction ? */ 467 else if ((fault_instruction & 0x0a100000) == 0x08000000) 468 ftype = VM_PROT_READ | VM_PROT_WRITE; 469 /* STRH, STRSH or STRSB instruction ? */ 470 else if ((fault_instruction & 0x0e100090) == 0x00000090) 471 ftype = VM_PROT_READ | VM_PROT_WRITE; 472 /* SWP instruction ? */ 473 else if ((fault_instruction & 0x0fb00ff0) == 0x01000090) 474 ftype = VM_PROT_READ | VM_PROT_WRITE; 475 else 476 ftype = VM_PROT_READ; 477 478 #ifdef PMAP_DEBUG 479 if (pmap_debug_level >= 0) 480 printf("fault protection = %d\n", ftype); 481 #endif 482 483 if ((ftype & VM_PROT_WRITE) ? 484 pmap_modified_emulation(map->pmap, va) : 485 pmap_handled_emulation(map->pmap, va)) 486 goto out; 487 488 if (current_intr_depth > 0) { 489 #ifdef DDB 490 printf("Non-emulated page fault with intr_depth > 0\n"); 491 report_abort(NULL, fault_status, fault_address, fault_pc); 492 kdb_trap(-1, frame); 493 return; 494 #else 495 panic("Fault with intr_depth > 0"); 496 #endif /* DDB */ 497 } 498 499 onfault = pcb->pcb_onfault; 500 pcb->pcb_onfault = NULL; 501 rv = uvm_fault(map, va, 0, ftype); 502 pcb->pcb_onfault = onfault; 503 if (rv == 0) 504 goto out; 505 506 if (user == 0) { 507 if (pcb->pcb_onfault) 508 goto copyfault; 509 printf("[u]vm_fault(%p, %lx, %x, 0) -> %x\n", 510 map, va, ftype, rv); 511 goto we_re_toast; 512 } 513 514 report_abort("", fault_status, fault_address, fault_pc); 515 if (rv == ENOMEM) { 516 printf("UVM: pid %d (%s), uid %d killed: " 517 "out of swap\n", p->p_pid, p->p_comm, 518 p->p_cred && p->p_ucred ? 519 p->p_ucred->cr_uid : -1); 520 trapsignal(p, SIGKILL, TRAP_CODE); 521 } else 522 trapsignal(p, SIGSEGV, TRAP_CODE); 523 break; 524 } 525 } 526 527 out: 528 /* Call userret() if it was a USR mode fault */ 529 if (user) 530 userret(p); 531 } 532 533 534 /* 535 * void prefetch_abort_handler(trapframe_t *frame) 536 * 537 * Abort handler called when instruction execution occurs at 538 * a non existent or restricted (access permissions) memory page. 539 * If the address is invalid and we were in SVC mode then panic as 540 * the kernel should never prefetch abort. 541 * If the address is invalid and the page is mapped then the user process 542 * does no have read permission so send it a signal. 543 * Otherwise fault the page in and try again. 544 */ 545 546 extern int kernel_debug; 547 548 void 549 prefetch_abort_handler(frame) 550 trapframe_t *frame; 551 { 552 register u_int fault_pc; 553 register struct proc *p; 554 register struct pcb *pcb; 555 u_int fault_instruction; 556 pt_entry_t *pte; 557 int error; 558 559 /* 560 * Enable IRQ's (disabled by the abort) This always comes 561 * from user mode so we know interrupts were not disabled. 562 * But we check anyway. 563 */ 564 if (!(frame->tf_spsr & I32_bit)) 565 enable_interrupts(I32_bit); 566 567 #ifdef DEBUG 568 if ((GetCPSR() & PSR_MODE) != PSR_SVC32_MODE) 569 panic("prefetch_abort_handler: not in SVC32 mode"); 570 #endif 571 572 /* Update vmmeter statistics */ 573 uvmexp.traps++; 574 575 /* Call the cpu specific abort fixup routine */ 576 error = cpu_prefetchabt_fixup(frame); 577 if (error == ABORT_FIXUP_RETURN) 578 return; 579 if (error == ABORT_FIXUP_FAILED) 580 panic("prefetch abort fixup failed\n"); 581 582 /* Get the current proc structure or proc0 if there is none */ 583 if ((p = curproc) == 0) { 584 p = &proc0; 585 #ifdef DEBUG 586 printf("Prefetch abort with curproc == 0\n"); 587 #endif 588 } 589 590 #ifdef PMAP_DEBUG 591 if (pmap_debug_level >= 0) 592 printf("prefetch fault in process %p %s\n", p, p->p_comm); 593 #endif 594 /* 595 * can't use curpcb, as it might be NULL; and we have p in a 596 * register anyway 597 */ 598 pcb = &p->p_addr->u_pcb; 599 if (pcb == 0) 600 panic("prefetch_abort_handler: no pcb ... we're toast !\n"); 601 602 #ifdef DEBUG 603 if (pcb != curpcb) { 604 printf("data_abort: Alert ! pcb(%p) != curpcb(%p)\n", 605 pcb, curpcb); 606 printf("data_abort: Alert ! proc(%p), curproc(%p)\n", 607 p, curproc); 608 } 609 #endif /* DEBUG */ 610 611 /* Get fault address */ 612 fault_pc = frame->tf_pc; 613 614 /* Was the prefectch abort from USR32 mode ? */ 615 if ((frame->tf_spsr & PSR_MODE) == PSR_USR32_MODE) { 616 p->p_addr->u_pcb.pcb_tf = frame; 617 } else { 618 /* 619 * All the kernel code pages are loaded at boot time 620 * and do not get paged 621 */ 622 panic("Prefetch abort in non-USR mode (frame=%p PC=0x%08x)\n", 623 frame, fault_pc); 624 } 625 626 #ifdef PMAP_DEBUG 627 if (pmap_debug_level >= 0) 628 printf("prefetch_abort: PC = %08x\n", fault_pc); 629 #endif 630 /* Ok validate the address, can only execute in USER space */ 631 if (fault_pc < VM_MIN_ADDRESS || fault_pc >= VM_MAXUSER_ADDRESS) { 632 #ifdef DEBUG 633 printf("prefetch: pc (%08x) not in user process space\n", 634 fault_pc); 635 #endif 636 trapsignal(p, SIGSEGV, fault_pc); 637 userret(p); 638 return; 639 } 640 641 /* Is the page already mapped ? */ 642 /* This is debugging for rev K SA110 silicon */ 643 pte = pmap_pte(p->p_vmspace->vm_map.pmap, (vaddr_t)fault_pc); 644 if (pte && *pte != 0) { 645 if (kernel_debug & 1) { 646 printf("prefetch_abort: page is already mapped - pte=%p *pte=%08x\n", 647 pte, *pte); 648 printf("prefetch_abort: pc=%08x proc=%p process=%s\n", fault_pc, p, p->p_comm); 649 printf("prefetch_abort: far=%08x fs=%x\n", cpu_faultaddress(), cpu_faultstatus()); 650 printf("prefetch_abort: trapframe=%08x\n", (u_int)frame); 651 } 652 #ifdef DDB 653 if (kernel_debug & 2) 654 Debugger(); 655 #endif 656 } 657 658 /* Ok read the fault address. This will fault the page in for us */ 659 if (fetchuserword(fault_pc, &fault_instruction) != 0) { 660 #ifdef DEBUG 661 printf("prefetch: faultin failed for address %08x\n", 662 fault_pc); 663 #endif 664 trapsignal(p, SIGSEGV, fault_pc); 665 } else { 666 667 #ifdef DIAGNOSTIC 668 /* More debug stuff */ 669 670 #ifdef PMAP_DEBUG 671 if (pmap_debug_level >= 0) { 672 printf("Instruction @V%08x = %08x\n", fault_pc, 673 fault_instruction); 674 disassemble(fault_pc); 675 printf("return addr=%08x", frame->tf_pc); 676 pte = pmap_pte(p->p_vmspace->vm_map.pmap, 677 (vaddr_t)fault_pc); 678 if (pte) 679 printf(" pte=%p *pte=%08x\n", pte, *pte); 680 else 681 printf("\n"); 682 683 } 684 #endif /* PMAP_DEBUG */ 685 #endif /* DIAGNOSTIC */ 686 } 687 688 userret(p); 689 } 690 691 int 692 cowfault(va) 693 vaddr_t va; 694 { 695 struct vmspace *vm; 696 int error; 697 698 if (va >= VM_MAXUSER_ADDRESS) 699 return (EFAULT); 700 701 /* uvm_fault can't be called from within an interrupt */ 702 KASSERT(current_intr_depth == 0); 703 704 vm = curproc->p_vmspace; 705 error = uvm_fault(&vm->vm_map, va, 0, VM_PROT_READ | VM_PROT_WRITE); 706 return error; 707 } 708 709 /* End of fault.c */ 710