1/* 2 * Copyright (c) 2014-2015 Wind River Systems, Inc. 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7/** 8 * @file 9 * @brief Wrapper around ISRs with logic for context switching 10 * 11 * 12 * Wrapper installed in vector table for handling dynamic interrupts that accept 13 * a parameter. 14 */ 15 16#include <offsets_short.h> 17#include <zephyr/toolchain.h> 18#include <zephyr/linker/sections.h> 19#include <zephyr/sw_isr_table.h> 20#include <zephyr/kernel_structs.h> 21#include <zephyr/arch/cpu.h> 22#include <swap_macros.h> 23#include <zephyr/arch/arc/asm-compat/assembler.h> 24 25GTEXT(_isr_wrapper) 26GTEXT(_isr_demux) 27 28#if defined(CONFIG_PM) 29GTEXT(z_pm_save_idle_exit) 30#endif 31 32/* 33The symbols in this file are not real functions, and neither are 34_rirq_enter/_firq_enter: they are jump points. 35 36The flow is the following: 37 38ISR -> _isr_wrapper -- + -> _rirq_enter -> _isr_demux -> ISR -> _rirq_exit 39 | 40 + -> _firq_enter -> _isr_demux -> ISR -> _firq_exit 41 42Context switch explanation: 43 44The context switch code is spread in these files: 45 46 isr_wrapper.s, switch.s, swap_macros.h, fast_irq.s, regular_irq.s 47 48IRQ stack frame layout: 49 50 high address 51 52 status32 53 pc 54 lp_count 55 lp_start 56 lp_end 57 blink 58 r13 59 ... 60 sp -> r0 61 62 low address 63 64The context switch code adopts this standard so that it is easier to follow: 65 66 - r2 contains _kernel.current ASAP, and the incoming thread when we 67 transition from outgoing thread to incoming thread 68 69Not loading _kernel into r0 allows loading _kernel without stomping on 70the parameter in r0 in arch_switch(). 71 72 73ARCv2 processors have two kinds of interrupts: fast (FIRQ) and regular. The 74official documentation calls the regular interrupts 'IRQs', but the internals 75of the kernel call them 'RIRQs' to differentiate from the 'irq' subsystem, 76which is the interrupt API/layer of abstraction. 77 78For FIRQ, there are two cases, depending upon the value of 79CONFIG_RGF_NUM_BANKS. 80 81CONFIG_RGF_NUM_BANKS==1 case: 82Scratch registers are pushed onto the current stack just as they are with 83RIRQ. See the above frame layout. Unlike RIRQ, the status32_p0 and ilink 84registers are where status32 and the program counter are located, so these 85need to be pushed. 86 87CONFIG_RGF_NUM_BANKS!=1 case: 88The FIRQ handler has its own register bank for general purpose registers, 89and thus it doesn't have to save them on a stack. The 'loop' registers 90(lp_count, lp_end, lp_start), however, are not present in the 91second bank. The handler saves these special registers in unused callee saved 92registers (to avoid stack accesses). It is possible to register a FIRQ 93handler that operates outside of the kernel, but care must be taken to only 94use instructions that only use the banked registers. 95 96The kernel is able to handle transitions to and from FIRQ, RIRQ and threads. 97The contexts are saved 'lazily': the minimum amount of work is 98done upfront, and the rest is done when needed: 99 100o RIRQ 101 102 All needed registers to run C code in the ISR are saved automatically 103 on the outgoing thread's stack: loop, status32, pc, and the caller- 104 saved GPRs. That stack frame layout is pre-determined. If returning 105 to a thread, the stack is popped and no registers have to be saved by 106 the kernel. If a context switch is required, the callee-saved GPRs 107 are then saved in the thread's stack. 108 109o FIRQ 110 111 First, a FIRQ can be interrupting a lower-priority RIRQ: if this is 112 the case, the FIRQ does not take a scheduling decision and leaves it 113 the RIRQ to handle. This limits the amount of code that has to run at 114 interrupt-level. 115 116 CONFIG_RGF_NUM_BANKS==1 case: 117 Registers are saved on the stack frame just as they are for RIRQ. 118 Context switch can happen just as it does in the RIRQ case, however, 119 if the FIRQ interrupted a RIRQ, the FIRQ will return from interrupt 120 and let the RIRQ do the context switch. At entry, one register is 121 needed in order to have code to save other registers. r0 is saved 122 first in the stack and restored later 123 124 CONFIG_RGF_NUM_BANKS!=1 case: 125 During early initialization, the sp in the 2nd register bank is made to 126 refer to _firq_stack. This allows for the FIRQ handler to use its own 127 stack. GPRs are banked, loop registers are saved in unused callee saved 128 regs upon interrupt entry. If returning to a thread, loop registers are 129 restored and the CPU switches back to bank 0 for the GPRs. If a context 130 switch is needed, at this point only are all the registers saved. 131 First, a stack frame with the same layout as the automatic RIRQ one is 132 created and then the callee-saved GPRs are saved in the stack. 133 status32_p0 and ilink are saved in this case, not status32 and pc. 134 To create the stack frame, the FIRQ handling code must first go back to 135 using bank0 of registers, since that is where the registers containing 136 the exiting thread are saved. Care must be taken not to touch any 137 register before saving them: the only one usable at that point is the 138 stack pointer. 139 140o coop 141 142 When a coop context switch is done, the callee-saved registers are 143 saved in the stack. The other GPRs do not need to be saved, since the 144 compiler has already placed them on the stack. 145 146For restoring the contexts, there are six cases. In all cases, the 147callee-saved registers of the incoming thread have to be restored. Then, there 148are specifics for each case: 149 150From coop: 151 152 o to coop 153 154 Do a normal function call return. 155 156 o to any irq 157 158 The incoming interrupted thread has an IRQ stack frame containing the 159 caller-saved registers that has to be popped. status32 has to be 160 restored, then we jump to the interrupted instruction. 161 162From FIRQ: 163 164 When CONFIG_RGF_NUM_BANKS==1, context switch is done as it is for RIRQ. 165 When CONFIG_RGF_NUM_BANKS!=1, the processor is put back to using bank0, 166 not bank1 anymore, because it had to save the outgoing context from 167 bank0, and now has to load the incoming one into bank0. 168 169 o to coop 170 171 The address of the returning instruction from arch_switch() is loaded 172 in ilink and the saved status32 in status32_p0. 173 174 o to any irq 175 176 The IRQ has saved the caller-saved registers in a stack frame, which 177 must be popped, and status32 and pc loaded in status32_p0 and ilink. 178 179From RIRQ: 180 181 o to coop 182 183 The interrupt return mechanism in the processor expects a stack frame, 184 but the outgoing context did not create one. A fake one is created 185 here, with only the relevant values filled in: pc, status32. 186 187 There is a discrepancy between the ABI from the ARCv2 docs, 188 including the way the processor pushes GPRs in pairs in the IRQ stack 189 frame, and the ABI GCC uses. r13 should be a callee-saved register, 190 but GCC treats it as caller-saved. This means that the processor pushes 191 it in the stack frame along with r12, but the compiler does not save it 192 before entering a function. So, it is saved as part of the callee-saved 193 registers, and restored there, but the processor restores it _a second 194 time_ when popping the IRQ stack frame. Thus, the correct value must 195 also be put in the fake stack frame when returning to a thread that 196 context switched out cooperatively. 197 198 o to any irq 199 200 Both types of IRQs already have an IRQ stack frame: simply return from 201 interrupt. 202 */ 203 204SECTION_FUNC(TEXT, _isr_wrapper) 205#ifdef CONFIG_ARC_FIRQ 206#if CONFIG_RGF_NUM_BANKS == 1 207/* free r0 here, use r0 to check whether irq is firq. 208 * for rirq, as sp will not change and r0 already saved, this action 209 * in fact is useless 210 * for firq, r0 will be restored later 211 */ 212 push r0 213#endif 214 lr r0, [_ARC_V2_AUX_IRQ_ACT] 215 ffs r0, r0 216 cmp r0, 0 217#if CONFIG_RGF_NUM_BANKS == 1 218 bnz rirq_path 219 pop r0 220 /* 1-register bank FIRQ handling must save registers on stack */ 221 _create_irq_stack_frame 222 lr r0, [_ARC_V2_STATUS32_P0] 223 st_s r0, [sp, ___isf_t_status32_OFFSET] 224 st ilink, [sp, ___isf_t_pc_OFFSET] 225 226 mov_s r3, _firq_exit 227 mov_s r2, _firq_enter 228 j_s [r2] 229rirq_path: 230 add sp, sp, 4 231 mov_s r3, _rirq_exit 232 mov_s r2, _rirq_enter 233 j_s [r2] 234#else 235 mov.z r3, _firq_exit 236 mov.z r2, _firq_enter 237 mov.nz r3, _rirq_exit 238 mov.nz r2, _rirq_enter 239 j_s [r2] 240#endif 241#else 242 MOVR r3, _rirq_exit 243 MOVR r2, _rirq_enter 244 j_s [r2] 245#endif 246 247/* r0, r1, and r3 will be used in exit_tickless_idle macro */ 248.macro exit_tickless_idle 249#if defined(CONFIG_PM) 250 clri r0 /* do not interrupt exiting tickless idle operations */ 251 MOVR r1, _kernel 252 breq r3, 0, _skip_pm_save_idle_exit 253 254 st 0, [r1, _kernel_offset_to_idle] /* zero idle duration */ 255 PUSHR blink 256 jl z_pm_save_idle_exit 257 POPR blink 258 259_skip_pm_save_idle_exit: 260 seti r0 261#endif 262.endm 263 264/* when getting here, r3 contains the interrupt exit stub to call */ 265SECTION_FUNC(TEXT, _isr_demux) 266 PUSHR r3 267 268/* according to ARCv2 ISA, r25, r30, r58, r59 are caller-saved 269 * scratch registers, possibly used by interrupt handlers 270 */ 271 PUSHR r25 272 PUSHR r30 273#ifdef CONFIG_ARC_HAS_ACCL_REGS 274 PUSHR r58 275#ifndef CONFIG_64BIT 276 PUSHR r59 277#endif /* !CONFIG_64BIT */ 278#endif 279 280#ifdef CONFIG_SCHED_THREAD_USAGE 281 bl z_sched_usage_stop 282#endif 283 284#ifdef CONFIG_TRACING_ISR 285 bl sys_trace_isr_enter 286#endif 287 /* cannot be done before this point because we must be able to run C */ 288 exit_tickless_idle 289 290 lr r0, [_ARC_V2_ICAUSE] 291 /* handle software triggered interrupt */ 292 lr r3, [_ARC_V2_AUX_IRQ_HINT] 293 brne r3, r0, irq_hint_handled 294 sr 0, [_ARC_V2_AUX_IRQ_HINT] 295irq_hint_handled: 296 297 sub r0, r0, 16 298 299 MOVR r1, _sw_isr_table 300 /* SW ISR table entries are 8-bytes wide for 32bit ISA and 301 * 16-bytes wide for 64bit ISA */ 302 ASLR r0, r0, (ARC_REGSHIFT + 1) 303 ADDR r0, r1, r0 304 /* ISR into r1 */ 305 LDR r1, r0, ARC_REGSZ 306 jl_s.d [r1] 307 /* delay slot: ISR parameter into r0 */ 308 LDR r0, r0 309 310#ifdef CONFIG_TRACING_ISR 311 bl sys_trace_isr_exit 312#endif 313 314#ifdef CONFIG_ARC_HAS_ACCL_REGS 315#ifndef CONFIG_64BIT 316 POPR r59 317#endif /* !CONFIG_64BIT */ 318 POPR r58 319#endif 320 321 POPR r30 322 POPR r25 323 324 /* back from ISR, jump to exit stub */ 325 POPR r3 326 j_s [r3] 327 nop_s 328