| ================================= |
| INTERNAL KERNEL ABI FOR FR-V ARCH |
| ================================= |
| |
| The internal FRV kernel ABI is not quite the same as the userspace ABI. A number of the registers |
| are used for special purposed, and the ABI is not consistent between modules vs core, and MMU vs |
| no-MMU. |
| |
| This partly stems from the fact that FRV CPUs do not have a separate supervisor stack pointer, and |
| most of them do not have any scratch registers, thus requiring at least one general purpose |
| register to be clobbered in such an event. Also, within the kernel core, it is possible to simply |
| jump or call directly between functions using a relative offset. This cannot be extended to modules |
| for the displacement is likely to be too far. Thus in modules the address of a function to call |
| must be calculated in a register and then used, requiring two extra instructions. |
| |
| This document has the following sections: |
| |
| (*) System call register ABI |
| (*) CPU operating modes |
| (*) Internal kernel-mode register ABI |
| (*) Internal debug-mode register ABI |
| (*) Virtual interrupt handling |
| |
| |
| ======================== |
| SYSTEM CALL REGISTER ABI |
| ======================== |
| |
| When a system call is made, the following registers are effective: |
| |
| REGISTERS CALL RETURN |
| =============== ======================= ======================= |
| GR7 System call number Preserved |
| GR8 Syscall arg #1 Return value |
| GR9-GR13 Syscall arg #2-6 Preserved |
| |
| |
| =================== |
| CPU OPERATING MODES |
| =================== |
| |
| The FR-V CPU has three basic operating modes. In order of increasing capability: |
| |
| (1) User mode. |
| |
| Basic userspace running mode. |
| |
| (2) Kernel mode. |
| |
| Normal kernel mode. There are many additional control registers available that may be |
| accessed in this mode, in addition to all the stuff available to user mode. This has two |
| submodes: |
| |
| (a) Exceptions enabled (PSR.T == 1). |
| |
| Exceptions will invoke the appropriate normal kernel mode handler. On entry to the |
| handler, the PSR.T bit will be cleared. |
| |
| (b) Exceptions disabled (PSR.T == 0). |
| |
| No exceptions or interrupts may happen. Any mandatory exceptions will cause the CPU to |
| halt unless the CPU is told to jump into debug mode instead. |
| |
| (3) Debug mode. |
| |
| No exceptions may happen in this mode. Memory protection and management exceptions will be |
| flagged for later consideration, but the exception handler won't be invoked. Debugging traps |
| such as hardware breakpoints and watchpoints will be ignored. This mode is entered only by |
| debugging events obtained from the other two modes. |
| |
| All kernel mode registers may be accessed, plus a few extra debugging specific registers. |
| |
| |
| ================================= |
| INTERNAL KERNEL-MODE REGISTER ABI |
| ================================= |
| |
| There are a number of permanent register assignments that are set up by entry.S in the exception |
| prologue. Note that there is a complete set of exception prologues for each of user->kernel |
| transition and kernel->kernel transition. There are also user->debug and kernel->debug mode |
| transition prologues. |
| |
| |
| REGISTER FLAVOUR USE |
| =============== ======= ==================================================== |
| GR1 Supervisor stack pointer |
| GR15 Current thread info pointer |
| GR16 GP-Rel base register for small data |
| GR28 Current exception frame pointer (__frame) |
| GR29 Current task pointer (current) |
| GR30 Destroyed by kernel mode entry |
| GR31 NOMMU Destroyed by debug mode entry |
| GR31 MMU Destroyed by TLB miss kernel mode entry |
| CCR.ICC2 Virtual interrupt disablement tracking |
| CCCR.CC3 Cleared by exception prologue (atomic op emulation) |
| SCR0 MMU See mmu-layout.txt. |
| SCR1 MMU See mmu-layout.txt. |
| SCR2 MMU Save for EAR0 (destroyed by icache insns in debug mode) |
| SCR3 MMU Save for GR31 during debug exceptions |
| DAMR/IAMR NOMMU Fixed memory protection layout. |
| DAMR/IAMR MMU See mmu-layout.txt. |
| |
| |
| Certain registers are also used or modified across function calls: |
| |
| REGISTER CALL RETURN |
| =============== =============================== =============================== |
| GR0 Fixed Zero - |
| GR2 Function call frame pointer |
| GR3 Special Preserved |
| GR3-GR7 - Clobbered |
| GR8 Function call arg #1 Return value (or clobbered) |
| GR9 Function call arg #2 Return value MSW (or clobbered) |
| GR10-GR13 Function call arg #3-#6 Clobbered |
| GR14 - Clobbered |
| GR15-GR16 Special Preserved |
| GR17-GR27 - Preserved |
| GR28-GR31 Special Only accessed explicitly |
| LR Return address after CALL Clobbered |
| CCR/CCCR - Mostly Clobbered |
| |
| |
| ================================ |
| INTERNAL DEBUG-MODE REGISTER ABI |
| ================================ |
| |
| This is the same as the kernel-mode register ABI for functions calls. The difference is that in |
| debug-mode there's a different stack and a different exception frame. Almost all the global |
| registers from kernel-mode (including the stack pointer) may be changed. |
| |
| REGISTER FLAVOUR USE |
| =============== ======= ==================================================== |
| GR1 Debug stack pointer |
| GR16 GP-Rel base register for small data |
| GR31 Current debug exception frame pointer (__debug_frame) |
| SCR3 MMU Saved value of GR31 |
| |
| |
| Note that debug mode is able to interfere with the kernel's emulated atomic ops, so it must be |
| exceedingly careful not to do any that would interact with the main kernel in this regard. Hence |
| the debug mode code (gdbstub) is almost completely self-contained. The only external code used is |
| the sprintf family of functions. |
| |
| Futhermore, break.S is so complicated because single-step mode does not switch off on entry to an |
| exception. That means unless manually disabled, single-stepping will blithely go on stepping into |
| things like interrupts. See gdbstub.txt for more information. |
| |
| |
| ========================== |
| VIRTUAL INTERRUPT HANDLING |
| ========================== |
| |
| Because accesses to the PSR is so slow, and to disable interrupts we have to access it twice (once |
| to read and once to write), we don't actually disable interrupts at all if we don't have to. What |
| we do instead is use the ICC2 condition code flags to note virtual disablement, such that if we |
| then do take an interrupt, we note the flag, really disable interrupts, set another flag and resume |
| execution at the point the interrupt happened. Setting condition flags as a side effect of an |
| arithmetic or logical instruction is really fast. This use of the ICC2 only occurs within the |
| kernel - it does not affect userspace. |
| |
| The flags we use are: |
| |
| (*) CCR.ICC2.Z [Zero flag] |
| |
| Set to virtually disable interrupts, clear when interrupts are virtually enabled. Can be |
| modified by logical instructions without affecting the Carry flag. |
| |
| (*) CCR.ICC2.C [Carry flag] |
| |
| Clear to indicate hardware interrupts are really disabled, set otherwise. |
| |
| |
| What happens is this: |
| |
| (1) Normal kernel-mode operation. |
| |
| ICC2.Z is 0, ICC2.C is 1. |
| |
| (2) An interrupt occurs. The exception prologue examines ICC2.Z and determines that nothing needs |
| doing. This is done simply with an unlikely BEQ instruction. |
| |
| (3) The interrupts are disabled (local_irq_disable) |
| |
| ICC2.Z is set to 1. |
| |
| (4) If interrupts were then re-enabled (local_irq_enable): |
| |
| ICC2.Z would be set to 0. |
| |
| A TIHI #2 instruction (trap #2 if condition HI - Z==0 && C==0) would be used to trap if |
| interrupts were now virtually enabled, but physically disabled - which they're not, so the |
| trap isn't taken. The kernel would then be back to state (1). |
| |
| (5) An interrupt occurs. The exception prologue examines ICC2.Z and determines that the interrupt |
| shouldn't actually have happened. It jumps aside, and there disabled interrupts by setting |
| PSR.PIL to 14 and then it clears ICC2.C. |
| |
| (6) If interrupts were then saved and disabled again (local_irq_save): |
| |
| ICC2.Z would be shifted into the save variable and masked off (giving a 1). |
| |
| ICC2.Z would then be set to 1 (thus unchanged), and ICC2.C would be unaffected (ie: 0). |
| |
| (7) If interrupts were then restored from state (6) (local_irq_restore): |
| |
| ICC2.Z would be set to indicate the result of XOR'ing the saved value (ie: 1) with 1, which |
| gives a result of 0 - thus leaving ICC2.Z set. |
| |
| ICC2.C would remain unaffected (ie: 0). |
| |
| A TIHI #2 instruction would be used to again assay the current state, but this would do |
| nothing as Z==1. |
| |
| (8) If interrupts were then enabled (local_irq_enable): |
| |
| ICC2.Z would be cleared. ICC2.C would be left unaffected. Both flags would now be 0. |
| |
| A TIHI #2 instruction again issued to assay the current state would then trap as both Z==0 |
| [interrupts virtually enabled] and C==0 [interrupts really disabled] would then be true. |
| |
| (9) The trap #2 handler would simply enable hardware interrupts (set PSR.PIL to 0), set ICC2.C to |
| 1 and return. |
| |
| (10) Immediately upon returning, the pending interrupt would be taken. |
| |
| (11) The interrupt handler would take the path of actually processing the interrupt (ICC2.Z is |
| clear, BEQ fails as per step (2)). |
| |
| (12) The interrupt handler would then set ICC2.C to 1 since hardware interrupts are definitely |
| enabled - or else the kernel wouldn't be here. |
| |
| (13) On return from the interrupt handler, things would be back to state (1). |
| |
| This trap (#2) is only available in kernel mode. In user mode it will result in SIGILL. |