Under the Hook - The Cortex Runtime
Published: 2024-04-16 14:44:57

Refer to [the cortex-m-rt documentation](https://docs.rs/cortex-m-rt/latest/cortex_m_rt/) and its [source code](https://crates.io/crates/cortex-m-rt) for more information.

## 1. Memory Layout
To understand more about the memory layout on ARM Cortex-M devices, let's look at the `link.x.in` and `memory.x` file.

### 1.1 Linker Script
according to the _developer notes_ in the `linker.x.in` file, we make a few clarifications
#### Naming Conventions of Linker Symbols
We need to clarify here, that, 
- symbols with the `_` (single underscore) prefix are __semi-public__, in the way that:
	- users can __ONLY__ override them in the linker script
	- users are __NOT__ allowed to reference them in rust code as external symbols
- symbols with the `__` (double underscore) prefix are private, and
	- users should __NOT__ in any way use or modify it

#### Weak Aliasing at the Linker Script Level
Symbols using `PROVIDE` are "__weak aliases at the linker level__", so that "users can __override any of these aliases__ by ___defining the corresponding symbol themselves___" .
- `EXTERN` forces the linker to keep a symbol in the final binary (even if it is not needed)
- `PROVIDE` provides __default__ values that can be overridden by a user linker script
For example, the each exception vectors can be overridden with use defined `exception!` macros
```
EXTERN(__EXCEPTIONS); /* depends on all the these PROVIDED symbols */

EXTERN(DefaultHandler);

PROVIDE(NonMaskableInt = DefaultHandler);
EXTERN(HardFaultTrampoline);
PROVIDE(MemoryManagement = DefaultHandler);
PROVIDE(BusFault = DefaultHandler);
PROVIDE(UsageFault = DefaultHandler);
PROVIDE(SecureFault = DefaultHandler);
PROVIDE(SVCall = DefaultHandler);
PROVIDE(DebugMonitor = DefaultHandler);
PROVIDE(PendSV = DefaultHandler);
PROVIDE(SysTick = DefaultHandler);

PROVIDE(DefaultHandler = DefaultHandler_);
PROVIDE(HardFault = HardFault_);
```

#### Virtual and Load Memory Addresses
***The location counter ALWAYS tracks VMA***, and
***To use LMA, use `LOADADDR(...)`***

##### Code Section
The `.text` section only needs to be stored in the `FLASH` memory because it is only a sequence of read-only executable code.
```
.text {
	*(.text)
} > FLASH
```

Since the `.text` section resides in the `FLASH` memory, there's no need of a write permission, we do not need to do anything special when booting.

##### Initialised Data Section
The `.data` section (for initialised `static`  data). It should be read from the `FLASH` memory (Read Only) into the `RAM` (Read/Write). The loader needs to know its compile-time initialised value and its `RAM` location, so that when loading the data, it will copy it directly from `FLASH` to `RAM`.
```
.data {
	*(.data)
} > RAM AT> FLASH
```

Here is how the `.data` section is initialised
The `.data` section needs to have a copy of initialised values of a static variable stored in `FLASH`, but it is mutable, so we need to copy it into our memory.
Note that `r0 = __sdata` and `r1 = __edata` are assigned as the __VMA__ value (in `RAM`) of the variable, while `r2 = __sidata` is the __LMA__ value (in `FLASH`) of the variable.
```asm
3:
	ldr r0, =__sdata
	ldr r1, =__edata
	ldr r2, =__sidata
4:
	cmp r1, r0
	beq 5f
	ldm r2!, {{r3}}
	stm r0!, {{r3}}
	b 4b
5:
```

##### Uninitialised Data Section
The `.bss` section contains all uninitialised `static` data, it needs not to be stored in the `FLASH` because the loader only needs to know its address in `RAM`, such that when loading the address, the loader will assign it the default value.
```
.bss {
	*(.bss)
} > RAM
```

Here is how the `.bss` section is initialised
As is introduced above, `.bss` saves the uninitialised static variables, it only records its __VMA__ (i.e. the destination address in `RAM`), and is initialised with zeros, here's the code

```asm
1:
	ldr r0, =__sbss
	ldr r1, =__ebss
	movs r2, #0
2:
	cmp r1, r0
	beq 3f
	stm r0!, {{r2}}
	b 2b
3:
```

### Customisable Layout
The user is allowed to customise the memory layout inside the `memory.x`
- device memory
	- i.e. the `MEMORY` section
- start of the stack
	- It is places at the end of `RAM` by default (if omitted)
	- It grows downwards
- start of the code (`.text`) section
	- It is placed at the beginning of `FLASH` by default (if omitted)
Here is an example of such file
```
/* Linker script for the STM32F303VCT6 */
MEMORY
{
    FLASH : ORIGIN = 0x08000000, LENGTH = 256K

    /* .bss, .data and the heap go in this region */
    RAM : ORIGIN = 0x20000000, LENGTH = 40K

    /* Core coupled (faster) RAM dedicated to hold the stack */
    CCRAM : ORIGIN = 0x10000000, LENGTH = 8K
}

_stack_start = ORIGIN(CCRAM) + LENGTH(CCRAM);
```

## 2. The Booting Process
The overall layout can be found in `link.x.in`
### 2.1 Initialising the Stack Pointer
We use the `_stack_start` symbol in the assembly code to load it into out stack pointer
```asm
ldr r0, =_stack_start
msr msp, r0
```

## Exception and Interrupt Handling
__An overview of ARM Cortex-M exception handling can be found in [Chris Coleman: A Practical guide to ARM Cortex-M Exception Handling](https://interrupt.memfault.com/blog/arm-cortex-m-exceptions-and-nvic)__. Most explanations related to the exception/interrupts and their handling mechanisms are ___borrowed__ from this post.

> [!info] Exception and Interrupt in ARM
> in the ARM documentation, “interrupt” is used to describe a type of “exception”
### Vector Table
A vector table stores the address of the handler routines for its corresponding exceptions/interrupts.

The in-memory layout (`FLASH` memory) of the vector table is as follows.
```
/* ## Sections in FLASH */
/* ### Vector table */
.vector_table ORIGIN(FLASH) :
{
  __vector_table = .;
  /* Initial Stack Pointer (SP) value.
   * We mask the bottom three bits to force 8-byte alignment.
   * Despite having an assert for this later, it's possible that a separate
   * linker script could override _stack_start after the assert is checked.
   */
  LONG(_stack_start & 0xFFFFFFF8);
  /* Reset vector */
  KEEP(*(.vector_table.reset_vector)); /* this is the `__RESET_VECTOR` symbol */
  /* Exceptions */
  __exceptions = .; /* start of exceptions */
  KEEP(*(.vector_table.exceptions)); /* this is the `__EXCEPTIONS` symbol */
  __eexceptions = .; /* end of exceptions */
  /* Device specific interrupts */
  KEEP(*(.vector_table.interrupts)); /* this is the `__INTERRUPTS` symbol */
} > FLASH
```

In rust code, each vector is represented by the `Vector` struct. It is a `Union` type, can either be a __handler pointer__ or __reserved__ (as in reserved, or not used, by the ARM architecture)

```rust
#[repr(C)]
pub union Vector {
    handler: unsafe extern "C" fn(),
    reserved: usize,
}
```


> [!Info] Initial Stack Pointer
> the first entry of the index table holds the reset (initial) value of the stack pointer (8-bit aligned).
> We can find it in the `link.x.in` file, where it uses `LONG(_stack_start & 0xFFFFFFF8)` at the start of the `.vector_table` section

> [!info] The Reset Exception
> The __`Reset` routine__ is executed when a chip resets, it is places at the second entry of the vector table.

### Activation of Vector Tables
The address of the memory-mapped register `VTOR` (Vector Table Offset Register) is `0xe000ed08`. 

To set its value, we simply write to the address of our vector table (provided by the linker symbol`__vector_table`) to the corresponding `VTOR` address.
```asm
ldr r0, = 0xe000ed08
ldr r1, =__vector_table
str r1, [r0]
```

### Built-in Exceptions

> [!info] SCS: System Control Space
>  
#### Exception Handlers

For more information, please refer to the [Exception Macro Documentation](https://docs.rs/cortex-m-rt/latest/cortex_m_rt/attr.exception.html)

Exception handlers can __ONLY__ be called by hardware.

Exception handlers are divided into four categories, namely, the `DefaultHandler`, the `HardFault` handler, the `NonMaskableInt`-errupt exception handler , and `Other` exception handler.

```rust
#[derive(Debug, PartialEq)]
enum Exception {
    DefaultHandler,
    HardFault(HardFaultArgs),
    NonMaskableInt,
    Other,
}
```

- The `HardFault` exception is the __catch all__ exception for assorted system failures
	- such as divide-by-zero exception, bad memory accesses etc.
	- Finer granularity fault handlers: `MemManage`, `BusFault` etc.  
	- Its arguments are passed as __macro arguments__
- The `NonMaskableInt`-errrupt exception is interrupt that cannot be disabled by setting `PRIMASK` or `BASEPRIO`.
	- Besides the `Reset` exception, it has the highest priority of all other exceptions
	- It is __NOT__ generally safe to define handlers for non-maskable interrupt since it may break critical sections
	- Its implementation should guarantee that: it does not access any data that is
		- protected by a critical section, and
		- shared with other interrupts that may be preempted by the NMI handler
- `Other` exceptions are
	- `MemoryManagement`
	- `BusFault`
	- `UsageFault`
	- `SecureFault`
	- `SVCall`: invoked when making a supervisor call (`svc`)
	- `DebugMonitor`
	- `PendSV`: system-level interrupts triggered by software
		-  Typically used by a RTOS to manage when the scheduler runs and when context switches take place.
	- `SysTick`: system-level interrupts triggered by software
		-  Typically used by a RTOS to manage when the scheduler runs and when context switches take place.

The parsing code makes sure that they are checked
```rust
let exn = match &*ident_s {
    "DefaultHandler" => {
        if !args.is_empty() {
            return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
                .to_compile_error()
                .into();
        }
        Exception::DefaultHandler
    }
    "HardFault" => Exception::HardFault(parse_macro_input!(args)),
    "NonMaskableInt" => {
        if !args.is_empty() {
            return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
                .to_compile_error()
                .into();
        }
        Exception::NonMaskableInt
    }
    // NOTE that at this point we don't check if the exception is available on the target (e.g.
    // MemoryManagement is not available on Cortex-M0)
    "MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault" | "SVCall"
    | "DebugMonitor" | "PendSV" | "SysTick" => {
        if !args.is_empty() {
            return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
                .to_compile_error()
                .into();
        }
        Exception::Other
    }
    _ => {
        return parse::Error::new(ident.span(), "This is not a valid exception name")
            .to_compile_error()
            .into();
    }
};
```

#### Exception Vector Table

To construct a vector table, we declare a slice with 14 entries of such `Vector`s, and place it in the `.vector_table.exceptions` section, which is defined in the linker script. 

The entries to the exception vector tables are stored in`__EXCEPTIONS` (apart from the two above-mentioned entries) 

```rust
#[cfg_attr(cortex_m, link_section = ".vector_table.exceptions")]
#[no_mangle]
pub static __EXCEPTIONS: [Vector; 14] = [
    // Exception 2: Non Maskable Interrupt.
    Vector {
        handler: NonMaskableInt,
    },
    // Exception 3: Hard Fault Interrupt.
    Vector { handler: HardFault },
    // Exception 4: Memory Management Interrupt [not on Cortex-M0 variants].
    #[cfg(not(armv6m))]
    Vector {
        handler: MemoryManagement,
    },
    #[cfg(armv6m)]
    Vector { reserved: 0 },
    // Exception 5: Bus Fault Interrupt [not on Cortex-M0 variants].
    #[cfg(not(armv6m))]
    Vector { handler: BusFault },
    #[cfg(armv6m)]
    Vector { reserved: 0 },
    // Exception 6: Usage Fault Interrupt [not on Cortex-M0 variants].
    #[cfg(not(armv6m))]
    Vector {
        handler: UsageFault,
    },
    #[cfg(armv6m)]
    Vector { reserved: 0 },
    // Exception 7: Secure Fault Interrupt [only on Armv8-M].
    #[cfg(armv8m)]
    Vector {
        handler: SecureFault,
    },
    #[cfg(not(armv8m))]
    Vector { reserved: 0 },
    // 8-10: Reserved
    Vector { reserved: 0 },
    Vector { reserved: 0 },
    Vector { reserved: 0 },
    // Exception 11: SV Call Interrupt.
    Vector { handler: SVCall },
    // Exception 12: Debug Monitor Interrupt [not on Cortex-M0 variants].
    #[cfg(not(armv6m))]
    Vector {
        handler: DebugMonitor,
    },
    #[cfg(armv6m)]
    Vector { reserved: 0 },
    // 13: Reserved
    Vector { reserved: 0 },
    // Exception 14: Pend SV Interrupt [not on Cortex-M0 variants].
    Vector { handler: PendSV },
    // Exception 15: System Tick Interrupt.
    Vector { handler: SysTick },
];
```
These handler symbols are by default the `DefaultHandler`, as declared in the linker script
```
PROVIDE(NonMaskableInt = DefaultHandler);
PROVIDE(MemoryManagement = DefaultHandler);
PROVIDE(BusFault = DefaultHandler);
PROVIDE(UsageFault = DefaultHandler);
PROVIDE(SecureFault = DefaultHandler);
PROVIDE(SVCall = DefaultHandler);
PROVIDE(DebugMonitor = DefaultHandler);
PROVIDE(PendSV = DefaultHandler);
PROVIDE(SysTick = DefaultHandler);
```

These are their signatures in rust
```rust
extern "C" {
    fn Reset() -> !;
    fn NonMaskableInt();
    fn HardFault();
    #[cfg(not(armv6m))]
    fn MemoryManagement();
    #[cfg(not(armv6m))]
    fn BusFault();
    #[cfg(not(armv6m))]
    fn UsageFault();
    #[cfg(armv8m)]
    fn SecureFault();
    fn SVCall();
    #[cfg(not(armv6m))]
    fn DebugMonitor();
    fn PendSV();
    fn SysTick();
}
```

### External Interrupts

> [!info] NVIC: Nested Vectored Interrupt Controller
> All external interrupts are configured via the NCIV __peripheral__ 

#### External Interrupt Handlers

For more information, please refer to the [Interrupt Macro Documentation](https://docs.rs/cortex-m-rt/latest/cortex_m_rt/attr.interrupt.html)

It is allowed to use __static__ variables inside a interrupt handler. Every __static__ variables are shared across each invocation of its handler.
It is achieved by converting `static mut` into `mut &`, and passing it as an argument to the interrupt handler (Its signature is extended to incorporate these extra arguments, and the original declarations are removed from the function). 

We can use a code snipped of the `interrupt` macro to explain the process.

__Firstly__, we extract every `static mut` declaration in the block statements of the function
```rust
pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
    // ...
	let (statics, stmts) = match extract_static_muts(f.block.stmts.iter().cloned()) {
        Err(e) => return e.to_compile_error().into(),
        Ok(x) => x,
    };
	//...
}

/// Extracts `static mut` vars from the beginning of the given statements
fn extract_static_muts(
    stmts: impl IntoIterator<Item = Stmt>,
) -> Result<(Vec<ItemStatic>, Vec<Stmt>), parse::Error> {
    let mut istmts = stmts.into_iter();

    let mut seen = HashSet::new();
    let mut statics = vec![];
    let mut stmts = vec![];
    for stmt in istmts.by_ref() {
        match stmt {
            Stmt::Item(Item::Static(var)) => match var.mutability {
                syn::StaticMutability::Mut(_) => {
                    if seen.contains(&var.ident) {
                        return Err(parse::Error::new(
                            var.ident.span(),
                            format!("the name `{}` is defined multiple times", var.ident),
                        ));
                    }

                    seen.insert(var.ident.clone());
                    statics.push(var);
                }
                _ => stmts.push(Stmt::Item(Item::Static(var))),
            },
            _ => {
                stmts.push(stmt);
                break;
            }
        }
    }

    stmts.extend(istmts);

    Ok((statics, stmts))
}
```

__Then__ we can push these `static mut` variables to the input argument list of the function. Note that the `input` field of a function signature is of type `FnArg`. It is either a receiver, i.e. all kinds of `self`, or a typed argument.
```rust
/// An argument in a function signature: the `n: usize` in `fn f(n: usize)`.
pub enum FnArg {
    /// The `self` argument of an associated method.
    Receiver(Receiver),
    /// A function argument accepted by pattern and type.
    Typed(PatType),
}

pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
    // ...
    f.sig.inputs.extend(statics.iter().map(|statik| {
        let ident = &statik.ident;
        let ty = &statik.ty;
        let attrs = &statik.attrs;
        syn::parse::<FnArg>(quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into())
            .unwrap()
    }));
	//...
}
```

Now, we can construct a wrapper function `{interrupt-name}_trampoline` as the corresponding interrupt handler by exporting it as `{interupt-name}`. The wrapper handler calls our user-defined interrupt handler, which is now called `__cortex_m_rt_{interrupt-name}`. The `static mut` resources are passed as arguments to our user-defined handler.

The arguments are constructed in a block which evaluates to the `&mut` value

```rust
pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
	// ...
    let resource_args = statics
        .iter()
        .map(|statik| {
            let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone());
            let ident = &statik.ident;
            let ty = &statik.ty;
            let expr = &statik.expr;
            quote! {
                #(#cfgs)*
                {
                    #(#attrs)*
                    static mut #ident: #ty = #expr;
                    &mut #ident
                }
            }
        })
        .collect::<Vec<_>>();

	// ...
}
```

The final layout of the code generated

```rust
pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
	// ...
    quote!(
        #(#cfgs)*
        #(#attrs)*
        #[doc(hidden)]
        #[export_name = #ident_s]
        pub unsafe extern "C" fn #tramp_ident() {
            #ident(
                #(#resource_args),*
            )
        }

        #f
    )
    .into()
	// end of function
}
```

#### External Interrupt Vector Table
It is initialised all interrupt handlers to the `DefaultHandler` declared in `src/lib.rs`.

```rust
#[cfg(all(any(not(feature = "device"), test), not(armv6m)))]
#[cfg_attr(cortex_m, link_section = ".vector_table.interrupts")]
#[no_mangle]
pub static __INTERRUPTS: [unsafe extern "C" fn(); 240] = [{
    extern "C" {
        fn DefaultHandler();
    }

    DefaultHandler
}; 240];
```

### Default Handlers

The library provided default handler is simply an infinite loop
```rust
#[no_mangle]
pub unsafe extern "C" fn DefaultHandler_() -> ! {
    #[allow(clippy::empty_loop)]
    loop {}
}
```

But users can easily override it by providing their own `DefaultHandler` definitions, since `PROVIDE` is weak aliasing
```
PROVIDE(DefaultHandler = DefaultHandler_);
```

The default handler for `HardFault` is also an infinite loop
```rust
#[cfg_attr(cortex_m, link_section = ".HardFault.default")]
#[no_mangle]
pub unsafe extern "C" fn HardFault_() -> ! {
    #[allow(clippy::empty_loop)]
    loop {}
}
```