How to use SWI on ARM?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
xmm15
Member
Member
Posts: 27
Joined: Mon Dec 16, 2013 6:50 pm

How to use SWI on ARM?

Post by xmm15 »

Hi, I'm trying to understand what would be the best way to use software interrupts on ARM.

What confuses me, is that calling SWI will disable interrupts and switch to Supervisor mode. This complicates things because I don't feel like interrupts should be disabled.
If my system call takes too much time, I don't want to block the scheduler. But if I do enable interrupts, then it becomes difficult to save the banked registers while I am in supervisor mode. Because two tasks could be in SWI and I would corrupt the supervisor mode. I thought about switching to System mode as soon as I get into the SWI but I'm not sure that's the way to go.

So what is the best way to write SWI handler? is the idea just to write very small and fast handlers so that it would be OK to keep interrupts disabled? Or should I switch to system mode?

I will dig into the linux code to see what they are doing, but meanwhile, I would appreciate any suggestions.

Thank you
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: How to use SWI on ARM?

Post by Owen »

SWI is the pre-UAL mnemonic for the SVC instruction. Prefer SVC.

The CPS (Change Processor State) instruction used to switch mode can be used to set or clear the Interrupt, Abort and FIQ masks.

For example, to switch to supervisor mode and re-enable Interrupts, Aborts and FIQ:

Code: Select all

cpsie aif, #0b10011
In addition, it is possible to configure which bits are cleared when the SVC instruction is executed. Consult the ARMv7 ARM.

Typically all of your other exception types will save the return information to the supervisor stack, and then switch to supervisor state. For example:

Code: Select all

_undefined:
    srsdb sp!, #0b10011
    cpsie aif, #0b10011
It is up to you which of asynchronous aborts, interrupts and FIQs you enable at each time. Initially, I would suggest simplifying matters by only permitting them (perhaps) in the SVC and Undefned Instruction handlers; the others are somewhat more involved and probably want to do at least some initial checks without re-entrancy issues.
xmm15
Member
Member
Posts: 27
Joined: Mon Dec 16, 2013 6:50 pm

Re: How to use SWI on ARM?

Post by xmm15 »

Thank you. but I should have mentionned that I'm on ARMv5.

I understand how to enable interrupts and how to clear them. I'm just wondering if I am wrong in wanting them to be enabled while in SVC.
If task1 enters SVC, then the timer IRQ kicks in, switch task, and task2 becomes active and task2 enters SVC. I will run into problems
since I have two concurent tasks running in SVC. My scheduler will save r0-r14 but will not try to save r13-r14 for the SVC mode.

So, do people usually allow context switches while a task is already in SVC? if so, how should I cope with that?

Right now, my idea is to:
push r0
push r13 with ^
pop r0: r0 = user mode stack
push r14 in r0 stack
pop r0
switch mode, use r14 to read spsr, change mode and write to cpsr. This will re-enable interrupts.
WE ARE IN SYSTEM MODE. context switches are safe
sub r14,4 to make corretion on stack since we pushed something on it.
do work (push/pop whatever I would need)
go in usermode
pop(r15)

But that looks like a big hack. I can't believe that the ARM people would have made it so complicated. There has to be something I am missing
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: How to use SWI on ARM?

Post by Owen »

Traditionally kernels run in SVC mode at all times. The usermode return information ends up on the SVC stack; when you context switch, you switch between kernel stacks and the rest of the state is pretty much switched implicitly (e.g. the user mode stack pointer and PC get recorded on the supervisor mode stack)

Switching form the abort/interrupt modes to SVC mode pre-v6 is tricky. You need to save LR + SPSR to the current stack, then switch to SVC mode (with interrupts disabled), where you can save the rest of the registers you need to (i.e. anything that the EABI defines as caller save), as well as copying the LR+SPSR from the IRQ stack to the SVC stack. Basically you probably need to "dedicate" an 8 byte chunk of memory somewhere to be the "IRQ temporary stack" (and similar for the abort modes)

Really, I have to question why you're targetting a pre-v7 device. Everything pre-v6 is thoroughly ancient and obsolete; anything pre-v7 is largely obsolete (i.e. with the single notable exception of the Raspberry Pi, which should enter consideration only if you are especially cash strapped - devices like the BeagleBone White are much superior development tools)
xmm15
Member
Member
Posts: 27
Joined: Mon Dec 16, 2013 6:50 pm

Re: How to use SWI on ARM?

Post by xmm15 »

As per your recommendation, I switched to armv7. I am using a cortex-a8 on qemu.

I'm not sure I understand though. I have no "kernel" threads right now. My kernel hands off execution to a user thread and then an IRQ preempts and gives a time slice to another user thread.

When a thread calls SVC, I understand that this is within kernel space but it is still executing under the context of the user thread (but in svc mode, so with banked registers).

But if I enable interrupts back when entering SVC, I risk being context-switched to another thread that will probably enter SVC mode at some point.

Everywhere I look, people talk about saving the user mode context with push(r0-r14)^. but what about the SVC registers? those need to be saved as well right? But the fact that nobody talks about that makes me wonder if I am really supposed to let a context switch occur while a thread is in SVC mode.
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: How to use SWI on ARM?

Post by Owen »

xmm15 wrote:As per your recommendation, I switched to armv7. I am using a cortex-a8 on qemu.

I'm not sure I understand though. I have no "kernel" threads right now. My kernel hands off execution to a user thread and then an IRQ preempts and gives a time slice to another user thread.

When a thread calls SVC, I understand that this is within kernel space but it is still executing under the context of the user thread (but in svc mode, so with banked registers).

But if I enable interrupts back when entering SVC, I risk being context-switched to another thread that will probably enter SVC mode at some point.

Everywhere I look, people talk about saving the user mode context with push(r0-r14)^. but what about the SVC registers? those need to be saved as well right? But the fact that nobody talks about that makes me wonder if I am really supposed to let a context switch occur while a thread is in SVC mode.
You're mixing up context switching and the timer. They're not intimately related: one normally triggers the other (i.e. the timer normally triggers context switches), but all sorts of other things can too (e.g. waiting for I/O)

The general strategy most kernels use for all non-SVC exceptions is to use SRS to save the SPSR, LR to the SVC stack, then switch to SVC mode (and then save off the rest of the necessary registers, e.g. perhaps user-SP), and then they handle everything in SVC mode. Obviously the SVC exception looks slightly different (because its' already in SVC mode).

The next bit is architecture generic: The timer tick interrupt* then just becomes "yet another thing" which can cause the scheduler to do a context switch, presumably by calling some function. You need to take some precautions to make sure only one thread is in the scheduler (e.g. the scheduler typically might be called with interrupts disabled, and you might have a spinlock to take on SMP systems**)

(ARM specific again) So now every thread has a user mode stack (typically kept in the user-SP register) and a kernel mode stack (typically kept in the SVC-SP register). Somewhere you need to save the User-SP, perhaps on the kernel mode stack or in a thread information structure. Your scheduler's switch task function then just needs to save the SVC-SP and PC and any registers required by the ABI, and when it restores them it will return things to the previous kernel mode context and you can return back up the call tree until you end up in userspace again.

A "kernel thread" then just becomes a thread which never enters userspace. Linux, for example, has a few which babysit devices or make power management decisions. You might account them against a special "fake" kernel process (on Unixy systems this is commonly internally notated as "PID 0") or against some special user mode process (e.g. init/PID 1 - most systems have some core system process which, if it dies, causes a kernel panic)

You certainly can decide not to take interrupts in kernel space. Non-preemptible kernels are a thing. If simplicity is your goal, its a valid decision to take. If you have higher aspirations, you might want to focus more on it now such that you don't have to go through the difficulty of retrofitting it later (note that the same considerations apply for SMP)

* Note that from both power and performance perspectives a regular timer tick is not ideal. Recent Linux and Windows versions are now 'tickless', and vary the timer period appropriately (e.g. longer if the app has a bigger timeslice), running it in one-shot mode, and even turn it off (if there is only one thread on the run queue). A tick-based kernel is of course much simpler, and you might decide to go that road
** Obviously its not ideal that only one thread can be in the scheduler at a time on a multicore system, but it will probably Do For Now.

I'm sure Brendan will say that not having a properly multicore aware scheduler and/or having a non-tickless kernel is Bad, and he is right, but we must account for limited time and prioritize things based upon our aims
Post Reply