I want to write some drivers that run in privilege mode. For example if I have a UART write function I want to make it easy to wrap the write function such that it runs in privilege mode. My thought was it would be nice to just have a macro/attribute I could wrap code in that would cause it to run in privilege mode but could not figure out a way to do it.
size_t write(uint8_t *ptrData, size_t len)
{
PRIVILEGE {
//run code to access peripheral in privilege mode
}
The best I could come up with is a wrapper such that I use the SVC interrupt to call a function.
size_t _write(uint8_t *ptrData, size_t len)
return SVC_CALL(_write);
Basically the SVC_CALL would push the function address on the stack and then call SVC #0 (or any number) and then the SVC interrupt would pop the function address and the stack for the function arguments, then call the _write() function in privilege mode. The issue here is that it is not as clean as the first example, and you have to be careful as the ABI and return values. For example knowing that the _write() function has two arguments passed in verse one, or three.
https://blog.stratifylabs.co/device/2013-10-12-Effective-Use-of-ARM-Cortex-M3-SVCall/
Has someone has figured out a good way to write drivers that run in privilege mode and abstract the call to drivers, if not how do people currently do it?
I had considered using SVC numbers per driver function. The SVC numbers are great for implementing something like serial UART for debugging that is SVC #0 could always be debugging UART write regardless of processor the code was ran one. I really did not want to do this for every driver call as the complexity grows over time as you get more and more drivers. For example if two people write drivers they might use the same number, and so you need to manage the numbers, which quickly gets messy with multiple developers. Then eventually you run out of numbers and you have to do something different anyway.
Basically I just want to have a way to enter privilege mode for block of code and exit at the end. Then people can write drivers and have them run in privilege mode by just wrapping code.
For example I imagine that I could at the start of a function call the SVC interrupt and then copy the stack frame from the PSP to the MSP. Then branch to the code, ie the return address of the SVC interrupt. Then at the end of the function I could insert code that copies the MSP frame back to the PSP and exits the privilege mode. This would allow me to have a function like:
ENTER_PRIVILEGE
//insert code to run in privilege mode
EXIT_PRIVILEGE
It might be possible to remove the EXIT_PRIVILEGE and use the return. Again I am not sure if this possible but figured others must have had this problems before and figured out a way to solve it?
You should consider an RTOS ;-) That makes separating easier. Esp. not using direct function calls to the driver.
That is the plan, specifically the drivers are for an RTOS. The problem is how to allow other developers to write drivers for the RTOS easily.
Most embedded guys I know can write drivers for single threaded projects, but doing for an RTOS is a problem.
I looked at using mutex for drivers but you can get into situations where the mutex can create race conditions. For example if thread #1 gets lock for I2C and then thread #2 gets lock for UART, then thread #1 waits for UART and thread #2 waits for I2C you have locked up the code. To avoid this a lot of OSs (like Linux) run drivers in privilege mode (kernel space) and the driver code must run to completion and be 'atomic'. That is on the early versions of Linux with single core processor a call to a hardware driver write blocked all other code from running except interrupts until it completed such that you did not get race conditions.
So it is easy to setup the drivers to be atomic, however abstracting how to make driver run in the privilege mode is more of a problem. So if a developer could just wrap code in the privilege mode macros it makes it easy for them to port driver code to the RTOS. Then what happens is that code runs to completion before anything else runs (other than interrupts) such that you remove/reduce risks of race conditions.
I looked at freeRTOS and found that they just provide a kernel and say "how you handle drivers and such are up to you." So if you know of another RTOS that has solved this problem, I would love to know.
Let me just say: Check my profile ;-)
I check the SCIOPTA RTOS user manual and there was no information about how it handles drivers and has solved these problems.
It is a direct message passing OS: You send a message "write" to the driver and it is handled in the respective driver process.
Driver processes may run in privileged mode and client process in user mode. Message passing separates the two. Even placing them in different MPU region to minimize interference.
You may even have the driver on a separate core (think of the Cortex-M7/M4 SoCs) and still use the same messages.
The "API" is the message interface. There is a proposal (call GDD - generic device driver), but you are free to choose your own API.