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