Context¶
A Context models the processor’s unprivileged state with Brittle-specific extensions:
- The sixteen general-purpose registers plus the program status word, for storing the program’s execution state.
- A separate copy of the
BASEPRIregister, so that separate Contexts can mask and unmask interrupts independently. - Sixteen Key Registers for storing keys.
- A configurable number [1] of MPU Region Registers for defining the program’s address space.
One Context is created by the kernel during boot.
Programs can create additional Contexts using the Become (4) method on Memory.
Branding¶
Brands separate Context keys into two classes, reply keys and service keys.
Reply Keys¶
A reply key grants the authority to deliver a single reply message to the program inhabiting the Context. Reply keys have the top bit of their brand set; the other 63 bits encode a call count. The Context internally tracks the call count, and only honors reply keys with the right count, to ensure that reply keys cannot be reused. (The call count is incremented at each successful reply, and whenever Make Runnable (7) is used to interrupt a Context awaiting reply.)
Reply keys are transparent — they implement no native methods, and instead just forward any received messages through to the program inhabiting the Context.
The only way to obtain a reply key [2] is by invoking a key with a call-style invocation. The kernel will internally fabricate a reply key and send it with the message. When the send phase completes, the Context atomically switches to expecting a reply through a reply key.
Service Keys¶
A service key grants the authority to perform maintenance on the Context, e.g. inspecting and altering register values. Service keys have the top bit of their brand clear, and implement the methods described below.
Warning
Currently, any Context service key can be used to call any method listed below. This is temporary. The service key brand will be further defined to allow weakened service keys.
Invalidation¶
At invalidation of a Context, the kernel does the following additional work:
- Removes it from any wait queues.
- Resets its state to
stopped. - Advances the expected brand of its kor-context-reply-key, invalidating them as well.
- If the invalidated Context is current (i.e. a program is invalidating its own Context), causes a context switch to choose a new current Context.
Methods¶
Read Register (1)¶
Reads a register, by index, from this Context. Only the registers specified as
“callee-save” by the ARM Procedure Call Standard, plus BASEPRI and the
stack pointer, are stored in the Context; others are stored on the stack.
Indices for use with this method are as follows:
| Index | Register |
|---|---|
| 0 | r4 |
| 1 | r5 |
| 2 | r6 |
| 3 | r7 |
| 4 | r8 |
| 5 | r9 |
| 6 | r10 |
| 7 | r11 |
| 8 | BASEPRI |
| 9 | r13 (stack pointer) |
Warning
Because this method reads a single register per IPC, it can become a bottleneck for programs (like debuggers) that need to read the entire register set. For better performance in such programs, see Read (Low/High) Registers (10/11).
Call¶
- d0: register index.
Reply¶
- d0: register contents.
Exceptions¶
k.bad_argumentif the register index is invalid.
Write Register (2)¶
Writes a register, by index, in this context. Only the registers specified as
“callee-save” by the ARM Procedure Call Standard, plus BASEPRI and the
stack pointer, are stored in the context; others are stored on the stack.
Indices for use with this method are identical to those used with
Read Register (1).
Warning
Because this method writes a single register per IPC, it can become a bottleneck for programs (like debuggers) that need to write the entire register set. For better performance in such programs, see Write (Low/High) Registers (12/13).
Call¶
- d0: register index
- d1: value
Reply¶
Empty.
Exceptions¶
k.bad_argumentif the register index is invalid.
Read Key Register (3)¶
Reads a key from one of this Context’s key registers, by index. Note that there are currently 16 key registers.
Key register 0 always reads as null.
Call¶
- d0: key index.
Exceptions¶
k.bad_argumentif the key index is not valid.
Write Key Register (4)¶
Writes a key into one of this Context’s key registers, by index. Note that there are currently 16 key registers. Because key register 0 is permanently null, index 0 is treated as invalid for this operation and will return an exception.
Call¶
- d0: key index
- k1: key
Reply¶
Empty.
Exceptions¶
k.bad_argumentif the key index is not valid (including 0).
Read MPU Region Register (5)¶
Reads out the contents of one of this Context’s MPU region registers. The number of MPU region registers per Context is configurable at build time [1].
Call¶
- d0: region index
Exceptions¶
k.bad_argumentif the region index is not valid for this Context.
Write MPU Region Register (6)¶
Alters one of this Context’s MPU region registers. The number of MPU region registers per Context is configurable at build time [1].
The change takes effect when this Context next becomes current, unless it is already current (i.e. it is modifying itself), in which case it takes effect immediately, before the reply is sent. This implies that a Context using this operation to remove its own access to stack will never receive the reply, taking a fault instead.
Real Memory keys can be loaded directly into the region registers. Any other type of key will be treated as a null key and confer no authority.
Note
This is probably going to change; bogus keys should be rejected.
Call¶
- d0: region index
- k1: region key
Reply¶
Empty.
Exceptions¶
k.bad_argumentif the region register index is not valid for this Context.
Make Runnable (7)¶
Switches this Context into “runnable” state. The practical effect of this depends on this Context’s current state:
- If blocked waiting to send, without receive phase, the send is cancelled and the Context receives no notification.
- If blocked in receive, without send phase, the Context immediately receives
a
k.would_blockexception. - If blocked in send-receive or call, the send phase is skipped and the Context
immediately receives a
k.would_blockexception. In the case of a call, any issued reply keys become invalid. - If stopped, the Context is simply resumed.
- If already runnable, nothing happens.
Note
Careful reading of this list above will show that a Context trying to make itself runnable will always succeed but receive an exception anyway.
Call¶
Empty.
Reply¶
Empty.
Set Priority (9)¶
Alters the current priority of this Context. If this Context is runnable, this might trigger a Context switch.
Call¶
- d0: priority
Read (Low/High) Registers (10/11)¶
Each of these two methods reads a block of five kernel-maintained registers from this Context. There are ten total such registers, so five are the “low” registers, and five are the “high”. They are ordered in the same way as for Read Register (1).
This operation is intended to make “swapping” — multiplexing multiple logical tasks across a single Context — faster, and is also useful in debuggers.
Call¶
Empty.
Reply¶
| dn | Low | High |
|---|---|---|
| d0 | r4 |
r9 |
| d1 | r5 |
r10 |
| d2 | r6 |
r11 |
| d3 | r7 |
r12 |
| d4 | r8 |
r13 |
Write (Low/High) Registers (12/13)¶
Each of these two methods writes a block of five kernel-maintained registers into this Context. There are ten total such registers, so five are the “low” registers, and five are the “high”. They are ordered in the same way as for Read Register (1).
This operation is intended to make “swapping” — multiplexing multiple logical tasks across a single Context — faster, and is also useful in debuggers.
Call¶
| dn | Low | High |
|---|---|---|
| d0 | r4 |
r9 |
| d1 | r5 |
r10 |
| d2 | r6 |
r11 |
| d3 | r7 |
r12 |
| d4 | r8 |
r13 |
Reply¶
Empty.
Footnotes
| [1] | (1, 2, 3) The number of MPU region registers can be configured at build time. The current default is six. The number of MPU region registers must be no larger than the actual number of MPU regions implemented by the hardware. |
| [2] | Except by guessing, of course. With an Object Table key a program can fabricate fake reply keys. If it can guess the call count, it can fake a reply. This is one reason why call counts are so large (63 bits). Currently they’re zeroed at Context creation, but we may randomize them in the future to make guessing really hard. |