Proper way to deal with long-running SVC/SWI calls (ARM Cortex M4)

applefat wrote on Friday, February 23, 2018:

Hi all, I am trying to use the SVC(=SWI) instruction as a way for a “user-level” FreeRTOS task to access an internal/kernel level API without them needing to be compiled together. I have a piece of code which does this nicely and I can pass values to and from the internal API. I think this scenario is very common.

However, many of the SVC/supervisor calls can take a long time (e.g. sending a command to off-chip hardware and then waiting forthat external hardware) and I want the “user task” to to block until the API call completes.

However, my understanding is that code running in the svc/swi handler is essentially running in the interrupt context, and you cannot use blocking calls (e.g. SemaphoreTake) in interrupt context. Meanwhile the ISR versions of these functions could be used to invoke a context switch at the tail of the SWI/SVC handler into a higher priority “worker task”, but the user-level task is still fundamentally in the running state and the only thing keeping it from resuming execution is its priority level, not explicit syncrhonization with IO completion.

In short, I cannot see any way to control/sychronize the return of the SWI/SVC handler with the completion of the API I/O activity!!

rtel wrote on Friday, February 23, 2018:

To be clear:

You have two separate binaries. Service calls (SVC) are used for one
binary to [effectively] call functions in the second binary. You want
the second binary to be able to call blocking FreeRTOS functions, but
can’t because you are in an SVC handler. Is that correct?

applefat wrote on Saturday, February 24, 2018:

That’s right! To be even more clear, the “user task” binary blob is passed into “xTaskCreateStatic” of the main binary.

I believe this general approach is also used by the Linux kernel; in fact, the system calls that a user-space progam invokes for ARM platform are just a couple instructions including the SVC instruction. The implementation part is in the kernel space. And of course, the system call (like “write”) is effectively blocking return until IO is done (well… asuming buffering is turned off)

I just can’t determine how this would be done in FreeRTOS, and Linux implementation is so large I couldn’t identify which mechanism is actually doing it. Maybe something with TaskSuspend and TaskResume? But even then TaskResume does not enforce that the user task would resume at any particular time in the future…

richard_damon wrote on Saturday, February 24, 2018:

One issue I see with this plan is that FreeRTOS is (I beleive) already using the SVC handler to do some of the FreeRTOS functionality. The SVC system is designed to be used for an ‘operating system’, and generally there is only 1 arround.

applefat wrote on Saturday, February 24, 2018:

Hi Richard, you are right. But FreeRTOS only uses SVC 0; what I did was add a bit of ASM to check the immediate value to SVC and as a result my code is able to process SVC 1 - SVC 255 without stepping on FreeRTOS.

richard_damon wrote on Saturday, February 24, 2018:

Yes, but FreeRTOS isn’t expecting to be called from the SVC handler, inside the handler you may have issues accessing the routines.

applefat wrote on Saturday, February 24, 2018:

So, I had imagined that I would only need to adjust the SVC interrupt priority to make the use of FreeRTOS calls safe–same as you would need to do for any other interrupt. In the Cortex M4 port it looks like SVC appears to be invoked once for the whole lifetime of the system–just to kick off the first task. Meanwhile actual context switches are done by PendSV, a separate interrupt.

So if we at least assume they can be called, how would it be done ? I can’t believe that nobody has wanted to make a well defined “syscall” interface for FreeRTOS before, using the software interrupt…I just keep mentally spinning in circles though.

applefat wrote on Sunday, February 25, 2018:

So, I tried browsing through some other github RTOS code related to ARMmbed, Keil’s RTX, etc. Unlike FreeRTOS, these OSes already support user-defined SVC calls and APIs. However, they do not appear to do anything special in the SVC handler with respect to making the handler as short as possible. In fact they often even do “printf” calls.

This puts somewhat of a bad taste in my mouth, but it certainly does make it trival to enforce the original caller to block. I guess I can just do a busy loop in the handler, waiting for the IO to complete.

I’m not real clear on the implications though. What if another task preempts the current one? Then isn’t the machine state still stuck in “interrupt mode” for the duration of any preempting tasks? I can disable the systick, but that kills the ability of the system to do anything else while waiting. And furthermore what if some other task calls SVC while the last SVC is still waiting? All very strange…