diff options
Diffstat (limited to 'arch/ia64/kernel/fsys.S')
| -rw-r--r-- | arch/ia64/kernel/fsys.S | 147 |
1 files changed, 84 insertions, 63 deletions
diff --git a/arch/ia64/kernel/fsys.S b/arch/ia64/kernel/fsys.S index 962b6c4e32b5..7d7684a369d3 100644 --- a/arch/ia64/kernel/fsys.S +++ b/arch/ia64/kernel/fsys.S | |||
| @@ -531,93 +531,114 @@ GLOBAL_ENTRY(fsys_bubble_down) | |||
| 531 | .altrp b6 | 531 | .altrp b6 |
| 532 | .body | 532 | .body |
| 533 | /* | 533 | /* |
| 534 | * We get here for syscalls that don't have a lightweight handler. For those, we | 534 | * We get here for syscalls that don't have a lightweight |
| 535 | * need to bubble down into the kernel and that requires setting up a minimal | 535 | * handler. For those, we need to bubble down into the kernel |
| 536 | * pt_regs structure, and initializing the CPU state more or less as if an | 536 | * and that requires setting up a minimal pt_regs structure, |
| 537 | * interruption had occurred. To make syscall-restarts work, we setup pt_regs | 537 | * and initializing the CPU state more or less as if an |
| 538 | * such that cr_iip points to the second instruction in syscall_via_break. | 538 | * interruption had occurred. To make syscall-restarts work, |
| 539 | * Decrementing the IP hence will restart the syscall via break and not | 539 | * we setup pt_regs such that cr_iip points to the second |
| 540 | * decrementing IP will return us to the caller, as usual. Note that we preserve | 540 | * instruction in syscall_via_break. Decrementing the IP |
| 541 | * the value of psr.pp rather than initializing it from dcr.pp. This makes it | 541 | * hence will restart the syscall via break and not |
| 542 | * possible to distinguish fsyscall execution from other privileged execution. | 542 | * decrementing IP will return us to the caller, as usual. |
| 543 | * Note that we preserve the value of psr.pp rather than | ||
| 544 | * initializing it from dcr.pp. This makes it possible to | ||
| 545 | * distinguish fsyscall execution from other privileged | ||
| 546 | * execution. | ||
| 543 | * | 547 | * |
| 544 | * On entry: | 548 | * On entry: |
| 545 | * - normal fsyscall handler register usage, except that we also have: | 549 | * - normal fsyscall handler register usage, except |
| 550 | * that we also have: | ||
| 546 | * - r18: address of syscall entry point | 551 | * - r18: address of syscall entry point |
| 547 | * - r21: ar.fpsr | 552 | * - r21: ar.fpsr |
| 548 | * - r26: ar.pfs | 553 | * - r26: ar.pfs |
| 549 | * - r27: ar.rsc | 554 | * - r27: ar.rsc |
| 550 | * - r29: psr | 555 | * - r29: psr |
| 556 | * | ||
| 557 | * We used to clear some PSR bits here but that requires slow | ||
| 558 | * serialization. Fortuntely, that isn't really necessary. | ||
| 559 | * The rationale is as follows: we used to clear bits | ||
| 560 | * ~PSR_PRESERVED_BITS in PSR.L. Since | ||
| 561 | * PSR_PRESERVED_BITS==PSR.{UP,MFL,MFH,PK,DT,PP,SP,RT,IC}, we | ||
| 562 | * ended up clearing PSR.{BE,AC,I,DFL,DFH,DI,DB,SI,TB}. | ||
| 563 | * However, | ||
| 564 | * | ||
| 565 | * PSR.BE : already is turned off in __kernel_syscall_via_epc() | ||
| 566 | * PSR.AC : don't care (kernel normally turns PSR.AC on) | ||
| 567 | * PSR.I : already turned off by the time fsys_bubble_down gets | ||
| 568 | * invoked | ||
| 569 | * PSR.DFL: always 0 (kernel never turns it on) | ||
| 570 | * PSR.DFH: don't care --- kernel never touches f32-f127 on its own | ||
| 571 | * initiative | ||
| 572 | * PSR.DI : always 0 (kernel never turns it on) | ||
| 573 | * PSR.SI : always 0 (kernel never turns it on) | ||
| 574 | * PSR.DB : don't care --- kernel never enables kernel-level | ||
| 575 | * breakpoints | ||
| 576 | * PSR.TB : must be 0 already; if it wasn't zero on entry to | ||
| 577 | * __kernel_syscall_via_epc, the branch to fsys_bubble_down | ||
| 578 | * will trigger a taken branch; the taken-trap-handler then | ||
| 579 | * converts the syscall into a break-based system-call. | ||
| 551 | */ | 580 | */ |
| 552 | # define PSR_PRESERVED_BITS (IA64_PSR_UP | IA64_PSR_MFL | IA64_PSR_MFH | IA64_PSR_PK \ | ||
| 553 | | IA64_PSR_DT | IA64_PSR_PP | IA64_PSR_SP | IA64_PSR_RT \ | ||
| 554 | | IA64_PSR_IC) | ||
| 555 | /* | 581 | /* |
| 556 | * Reading psr.l gives us only bits 0-31, psr.it, and psr.mc. The rest we have | 582 | * Reading psr.l gives us only bits 0-31, psr.it, and psr.mc. |
| 557 | * to synthesize. | 583 | * The rest we have to synthesize. |
| 558 | */ | 584 | */ |
| 559 | # define PSR_ONE_BITS ((3 << IA64_PSR_CPL0_BIT) | (0x1 << IA64_PSR_RI_BIT) \ | 585 | # define PSR_ONE_BITS ((3 << IA64_PSR_CPL0_BIT) \ |
| 586 | | (0x1 << IA64_PSR_RI_BIT) \ | ||
| 560 | | IA64_PSR_BN | IA64_PSR_I) | 587 | | IA64_PSR_BN | IA64_PSR_I) |
| 561 | 588 | ||
| 562 | invala | 589 | invala // M0|1 |
| 563 | movl r8=PSR_ONE_BITS | 590 | movl r14=ia64_ret_from_syscall // X |
| 564 | 591 | ||
| 565 | mov r25=ar.unat // save ar.unat (5 cyc) | 592 | nop.m 0 |
| 566 | movl r9=PSR_PRESERVED_BITS | 593 | movl r28=__kernel_syscall_via_break // X create cr.iip |
| 594 | ;; | ||
| 567 | 595 | ||
| 568 | mov ar.rsc=0 // set enforced lazy mode, pl 0, little-endian, loadrs=0 | 596 | mov r2=r16 // A get task addr to addl-addressable register |
| 569 | movl r28=__kernel_syscall_via_break | 597 | adds r16=IA64_TASK_THREAD_ON_USTACK_OFFSET,r16 // A |
| 598 | mov r31=pr // I0 save pr (2 cyc) | ||
| 570 | ;; | 599 | ;; |
| 571 | mov r23=ar.bspstore // save ar.bspstore (12 cyc) | 600 | st1 [r16]=r0 // M2|3 clear current->thread.on_ustack flag |
| 572 | mov r31=pr // save pr (2 cyc) | 601 | addl r22=IA64_RBS_OFFSET,r2 // A compute base of RBS |
| 573 | mov r20=r1 // save caller's gp in r20 | 602 | add r3=TI_FLAGS+IA64_TASK_SIZE,r2 // A |
| 574 | ;; | 603 | ;; |
| 575 | mov r2=r16 // copy current task addr to addl-addressable register | 604 | ld4 r3=[r3] // M0|1 r3 = current_thread_info()->flags |
| 576 | and r9=r9,r29 | 605 | lfetch.fault.excl.nt1 [r22] // M0|1 prefetch register backing-store |
| 577 | mov r19=b6 // save b6 (2 cyc) | 606 | nop.i 0 |
| 578 | ;; | 607 | ;; |
| 579 | mov psr.l=r9 // slam the door (17 cyc to srlz.i) | 608 | mov ar.rsc=0 // M2 set enforced lazy mode, pl 0, LE, loadrs=0 |
| 580 | or r29=r8,r29 // construct cr.ipsr value to save | 609 | nop.m 0 |
| 581 | addl r22=IA64_RBS_OFFSET,r2 // compute base of RBS | 610 | nop.i 0 |
| 582 | ;; | 611 | ;; |
| 583 | // GAS reports a spurious RAW hazard on the read of ar.rnat because it thinks | 612 | mov r23=ar.bspstore // M2 (12 cyc) save ar.bspstore |
| 584 | // we may be reading ar.itc after writing to psr.l. Avoid that message with | 613 | mov.m r24=ar.rnat // M2 (5 cyc) read ar.rnat (dual-issues!) |
| 585 | // this directive: | 614 | nop.i 0 |
| 586 | dv_serialize_data | ||
| 587 | mov.m r24=ar.rnat // read ar.rnat (5 cyc lat) | ||
| 588 | lfetch.fault.excl.nt1 [r22] | ||
| 589 | adds r16=IA64_TASK_THREAD_ON_USTACK_OFFSET,r2 | ||
| 590 | |||
| 591 | // ensure previous insn group is issued before we stall for srlz.i: | ||
| 592 | ;; | 615 | ;; |
| 593 | srlz.i // ensure new psr.l has been established | 616 | mov ar.bspstore=r22 // M2 (6 cyc) switch to kernel RBS |
| 594 | ///////////////////////////////////////////////////////////////////////////// | 617 | movl r8=PSR_ONE_BITS // X |
| 595 | ////////// from this point on, execution is not interruptible anymore | ||
| 596 | ///////////////////////////////////////////////////////////////////////////// | ||
| 597 | addl r1=IA64_STK_OFFSET-IA64_PT_REGS_SIZE,r2 // compute base of memory stack | ||
| 598 | cmp.ne pKStk,pUStk=r0,r0 // set pKStk <- 0, pUStk <- 1 | ||
| 599 | ;; | 618 | ;; |
| 600 | st1 [r16]=r0 // clear current->thread.on_ustack flag | 619 | mov r25=ar.unat // M2 (5 cyc) save ar.unat |
| 601 | mov ar.bspstore=r22 // switch to kernel RBS | 620 | mov r19=b6 // I0 save b6 (2 cyc) |
| 602 | mov b6=r18 // copy syscall entry-point to b6 (7 cyc) | 621 | mov r20=r1 // A save caller's gp in r20 |
| 603 | add r3=TI_FLAGS+IA64_TASK_SIZE,r2 | ||
| 604 | ;; | 622 | ;; |
| 605 | ld4 r3=[r3] // r2 = current_thread_info()->flags | 623 | or r29=r8,r29 // A construct cr.ipsr value to save |
| 606 | mov r18=ar.bsp // save (kernel) ar.bsp (12 cyc) | 624 | mov b6=r18 // I0 copy syscall entry-point to b6 (7 cyc) |
| 607 | mov ar.rsc=0x3 // set eager mode, pl 0, little-endian, loadrs=0 | 625 | addl r1=IA64_STK_OFFSET-IA64_PT_REGS_SIZE,r2 // A compute base of memory stack |
| 608 | br.call.sptk.many b7=ia64_syscall_setup | 626 | |
| 609 | ;; | 627 | mov r18=ar.bsp // M2 save (kernel) ar.bsp (12 cyc) |
| 610 | ssm psr.i | 628 | cmp.ne pKStk,pUStk=r0,r0 // A set pKStk <- 0, pUStk <- 1 |
| 611 | movl r2=ia64_ret_from_syscall | 629 | br.call.sptk.many b7=ia64_syscall_setup // B |
| 612 | ;; | 630 | ;; |
| 613 | mov rp=r2 // set the real return addr | 631 | mov ar.rsc=0x3 // M2 set eager mode, pl 0, LE, loadrs=0 |
| 614 | and r3=_TIF_SYSCALL_TRACEAUDIT,r3 | 632 | mov rp=r14 // I0 set the real return addr |
| 633 | and r3=_TIF_SYSCALL_TRACEAUDIT,r3 // A | ||
| 615 | ;; | 634 | ;; |
| 616 | cmp.eq p8,p0=r3,r0 | 635 | ssm psr.i // M2 we're on kernel stacks now, reenable irqs |
| 636 | cmp.eq p8,p0=r3,r0 // A | ||
| 637 | (p10) br.cond.spnt.many ia64_ret_from_syscall // B return if bad call-frame or r15 is a NaT | ||
| 617 | 638 | ||
| 618 | (p10) br.cond.spnt.many ia64_ret_from_syscall // p10==true means out registers are more than 8 | 639 | nop.m 0 |
| 619 | (p8) br.call.sptk.many b6=b6 // ignore this return addr | 640 | (p8) br.call.sptk.many b6=b6 // B (ignore return address) |
| 620 | br.cond.sptk ia64_trace_syscall | 641 | br.cond.spnt ia64_trace_syscall // B |
| 621 | END(fsys_bubble_down) | 642 | END(fsys_bubble_down) |
| 622 | 643 | ||
| 623 | .rodata | 644 | .rodata |
