aboutsummaryrefslogtreecommitdiff
path: root/docs/Various-random-things-explained-I-dont-feel-like-creating-a-separate-txt-for-each-or-ordering-them-in-any-way.txt
blob: 0a3ae0626321fa001e82daa85f073ce8438ae576 (about) (plain)
1
2
3
4
5
6
7
Supervisor call happens, when the svc (previously called swi) instruction get executed. Exception is then entered. Supervisor call is the standard way for user process to ask the kernel for something. As user code might request many different things, the kernel must somehow know which one was requested. The svc instruction takes one immediate operand. The supervisor call exception handler can check at what address the execution was, read svc instruction from there and inspect it's bytes. This way, by executing svc with different immediate values, the used mode code can request different things from the kernel - the value in svc shall encode the request's type.
To save time and for the sake of simplicity, we don't make use of immediades in svc and instead we encode call's type in r0. In our implementation we decided, that supervisor call will preserve and clobber the same registers as function call and it will return values through r0, just as function call. This enables us to use actually perform the supervisor call as call to function defined in src/arm/PL0/svc.S. Calls from C are performed in src/arm/PL0/PL0_utils.c and request type encodings are defined in src/arm/common/svc_interface.h (they must be known to both user mode code and handler code).

We've compiled useful utilities (i.e. memcpy(), strlen(), etc.) in src/arm/common/strings.c. Those Do not depend on the environment and can be used by both user mode code, kernel code, even bootloader code.
Functions used for io (like puts()) are also defined in common way for privileged and unprivileged code. They do, however, rely on the existence of putchar() and getchar(). In PL0 code (src/arm/PL0/PL0_utils.c), putchar() and getchar() are defined to perform a supervisor call, that does that. In the PL1 code, they are defined as operations on UART.

src/arm/PL1/PL1_common/uart.c implements putchar() and getchar() in terms of UART. Those implementations are blocking - the poll UART peripheral registers in a loop, checking, if the device is ready to perform the operation. They are, however, accompanied by functions getchar_non_blocking() and putchar_non_blocking(), that check **once** if the device is ready and only perform the operation if it is. Otherwise, they return an error value, They purpose is to use them with interrupts. In interrupt-driven UART we avoid waiting in a loop - instead, an IRQ comes when desired UART's operation completes. The code that wants to write/read from UART, does, however, need to tie it's operation with IRQ handler and scheduler.