# Chapter 6: Memory Protection
:::{admonition} What this chapter answers: why does the OS survive when an app crashes?
:class: tip
On a PC, when one app crashes, the OS and the other apps keep running.
The OS ends only the program that misbehaved.
In this chapter the OS catches the exceptions (faults) the CPU raises on a
division by zero or a write to an address that does not exist, and stops only
the task that caused it. The other tasks carry on as if nothing happened.
:::
## Sample code
- Directory: `docs/os-on-arduino/code/06_memory_protection`
```bash
uv run pio run -d 06_memory_protection -t upload
uv run pio device monitor -b 115200
```
## Source code
The sketch for this chapter. Click a file name to expand. The code is mirrored automatically from the manuscript repository, so what you see here is always current.
:::{dropdown} `src/main.cpp` — 141 lines
:icon: code
[Open on GitHub](https://github.com/iory/learning-os-from-arduino/blob/main/docs/os-on-arduino/code/06_memory_protection/src/main.cpp)
```{literalinclude} ../../../../code/06_memory_protection/src/main.cpp
:language: cpp
:linenos:
```
:::
:::{dropdown} `include/os_fault.h` — 46 lines
:icon: code
[Open on GitHub](https://github.com/iory/learning-os-from-arduino/blob/main/docs/os-on-arduino/code/06_memory_protection/include/os_fault.h)
```{literalinclude} ../../../../code/06_memory_protection/include/os_fault.h
:language: cpp
:linenos:
```
:::
:::{dropdown} `include/os_kernel.h` — 74 lines
:icon: code
[Open on GitHub](https://github.com/iory/learning-os-from-arduino/blob/main/docs/os-on-arduino/code/06_memory_protection/include/os_kernel.h)
```{literalinclude} ../../../../code/06_memory_protection/include/os_kernel.h
:language: cpp
:linenos:
```
:::
:::{dropdown} `src/os_fault.cpp` — 170 lines
:icon: code
[Open on GitHub](https://github.com/iory/learning-os-from-arduino/blob/main/docs/os-on-arduino/code/06_memory_protection/src/os_fault.cpp)
```{literalinclude} ../../../../code/06_memory_protection/src/os_fault.cpp
:language: cpp
:linenos:
```
:::
:::{dropdown} `src/os_kernel.cpp` — 219 lines
:icon: code
[Open on GitHub](https://github.com/iory/learning-os-from-arduino/blob/main/docs/os-on-arduino/code/06_memory_protection/src/os_kernel.cpp)
```{literalinclude} ../../../../code/06_memory_protection/src/os_kernel.cpp
:language: cpp
:linenos:
```
:::
## On the board
```{figure} ../_static/ch06_fault.gif
:name: fig-ch06-fault
:width: 100%
Commands are single letters: `p` lists the tasks, `d` creates `Crash_Div` (a division by zero), and `m` creates `Crash_Mem`, which writes to an invalid address (`0xFFFFFFFF`) after 5 s. Here `p` lists the tasks and `d` triggers a division by zero. After the fault `Heartbeat` keeps going, and only `Crash_Div` becomes `TERMINATED`.
```
## In the simulator
Without the board, the same firmware running in the {doc}`simulator <../getting-started/simulator>` looks like this.
```{figure} ../_static/sim_ch06_fault.gif
:name: fig-ch06-sim
:width: 80%
Sending `d` makes the `Crash_Div` task fault on a division by zero, and `FAULT DETECTED` with the cause is printed. L (Heartbeat) and the `[Heartbeat]` lines keep going. (Recorded in the simulator.)
```
(ch06-break)=
## Break it
**Predict before** you open each answer. The first one needs no code change:
try it directly in
Chapter 6 in the browser version.
For the second, change the code, build it, and
{ref}`drop firmware.elf into the browser version `.
:::{note}
The outputs in the answers were captured by actually running the code in QEMU,
the same emulator the browser version uses. They have not been checked on the
real board yet.
:::
### 1. Crash five times in a row
Send `d` five times in a row, wait a little, then list the tasks with `p`.
**Predict:** how many times does `FAULT DETECTED` appear?
:::{dropdown} Answer
**Only four times.** The fifth `d` still prints `Creating div-by-zero crash task...`,
but the `[Crash] Dividing by zero in 3 seconds...` line that should follow never appears.
```text
Creating div-by-zero crash task...
[Crash] Dividing by zero in 3 seconds...
Creating div-by-zero crash task...
[Crash] Dividing by zero in 3 seconds...
[Heartbeat] 1
Creating div-by-zero crash task...
[Crash] Dividing by zero in 3 seconds...
Creating div-by-zero crash task...
[Crash] Dividing by zero in 3 seconds...
Creating div-by-zero crash task...
[Heartbeat] 2
(FAULT DETECTED four times)
ID NAME STATE
0 idle READY
1 LED BLOCKED
2 Heartbeat BLOCKED
3 Shell RUNNING
4 Crash_Div TERMINATED
5 Crash_Div TERMINATED
6 Crash_Div TERMINATED
7 Crash_Div TERMINATED
```
The task table `g_tasks` has room for only `MAX_TASKS` = 8 entries. The four
created at boot (idle, LED, Heartbeat, Shell) use half, and `Crash_Div` fills
the other four. A `TERMINATED` task's slot is never reused, so the fifth
`os_create_task()` returns -1 and does nothing. The shell does not check that
return value, so it announces the new task and never notices the failure.
Each finished task permanently uses up a slot, so this OS is not suited to
recreating crashed tasks over and over.
:::
### 2. Turn off the divide-by-zero trap
In `fault_init()` in `src/os_fault.cpp`, comment out the setting that makes a
division by zero raise an exception.
```diff
- SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk;
+ // SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk;
```
**Predict:** when you send `d`, what does `100 / 0` do?
:::{dropdown} Answer
**`FAULT DETECTED` never appears.** Yet `p` shows `Crash_Div` as `TERMINATED`.
```text
Creating div-by-zero crash task...
[Crash] Dividing by zero in 3 seconds...
[Heartbeat] 1
[Heartbeat] 2
[Heartbeat] 3
ID NAME STATE
0 idle READY
1 LED BLOCKED
2 Heartbeat BLOCKED
3 Shell RUNNING
4 Crash_Div TERMINATED
```
Without this setting (`CCR.DIV_0_TRP`), the Cortex-M divide instruction does not
raise an exception on division by zero; it returns 0 and carries on
([Arm's "Divide and Conquer"](https://developer.arm.com/community/arm-community-blogs/b/architectures-and-processors-blog/posts/divide-and-conquer)).
`c` stays 0, `task_crash_divzero()` runs to the end and returns, and
`task_exit()`, which catches returning tasks, marks it `TERMINATED`.
So the program carries on with a wrong answer and no error.
That is why `fault_init()` turns this setting on.
:::
## Pitfalls
(Collected from reader questions — ask via
[GitHub Issues](https://github.com/iory/build-your-own-arduino-rtos/issues).)