docs: document startup sequence, including boot arguments
Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
parent
a98a37c3b1
commit
89fcfd520f
44
arguments.md
Normal file
44
arguments.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# Kernel Arguments
|
||||||
|
|
||||||
|
Kernel arguments are passed by giving a page-aligned address pointed to by
|
||||||
|
register `$a0`. This uses IFF-style tags with the following format:
|
||||||
|
|
||||||
|
```
|
||||||
|
TagType,Size
|
||||||
|
```
|
||||||
|
|
||||||
|
The size is in native byte order, and does not include the 8 bytes describing the tag type or the size.
|
||||||
|
|
||||||
|
## Tag Types
|
||||||
|
|
||||||
|
| Tag | Description
|
||||||
|
| ---- | ------------
|
||||||
|
| XASZ | Xous Args Size. This indicates the size of the entire block (including the XASZ tag and size), and can be used to determine how many pages to save for this block. This should be the first argument.
|
||||||
|
| MBLK | Memory page blocks. This is a series of offset/size pairs indicating memory regions in the system, as well as a code name for the memory page. The first region is defined to be "RAM", and is where memory will be allocated from.
|
||||||
|
| XKRN | Kernel memory specification. Includes the offset of the kernel in RAM as well as its size. Does not need to be page-aligned.
|
||||||
|
| PID1 | Initial program specification. This includes the offset of PID1 in RAM as well as its size. Does not need to be page-aligned.
|
||||||
|
|
||||||
|
### PID1
|
||||||
|
|
||||||
|
The `PID1` argument describes how to load PID1. It has the following values:
|
||||||
|
|
||||||
|
* LOAD_OFFSET -- Address in RAM where PID1 is stored
|
||||||
|
* LOAD_SIZE -- Size (in bytes) of PID1 binary, not including this header
|
||||||
|
* TEXT_OFFSET -- Virtual memory address where PID1 expects the program image to live
|
||||||
|
* DATA_OFFSET -- Virtual memory address where PID1 expects the .data/.bss section to be
|
||||||
|
* DATA_SIZE -- Size of the .data+.bss section
|
||||||
|
* ENTRYPOINT - Virtual memory address of the main() function
|
||||||
|
* STACK -- 32-bits of stack address, where the program expects `$sp` to point
|
||||||
|
|
||||||
|
Note that the .bss and .data sections are combined together. This merely indicates how pages will get allocated for this new program. Also note that these sections will be cleared to 0 due to how Xous processes start up.
|
||||||
|
|
||||||
|
### XKRN
|
||||||
|
|
||||||
|
This describes the kernel.
|
||||||
|
|
||||||
|
* LOAD_OFFSET -- Address in RAM where the kernel is stored
|
||||||
|
* LOAD_SIZE -- Size (in bytes) of the kernel binary, not including this header
|
||||||
|
* TEXT_OFFSET -- Virtual memory address where the kernel expects the program image to live. This should be `0x02000000`
|
||||||
|
* DATA_OFFSET -- Virtual memory address where the kernel expects the .data/.bss section to be. This should be `0x04000000`
|
||||||
|
* DATA_SIZE -- Size of the .data+.bss section
|
||||||
|
* TRAP_HANDLER -- Virtual memory of the `stvec` trap handler
|
85
memory.md
85
memory.md
@ -1,6 +1,20 @@
|
|||||||
# Memory Management
|
|
||||||
|
|
||||||
In general, memory cannot be mapped to more than one process. This includes RAM, storage, and io peripherals.
|
# Memory Layout
|
||||||
|
|
||||||
|
Xous assumes a memory-mapped IO system. Furthermore, it assumes there
|
||||||
|
is one section of "general-purpose RAM", and zero or more additional
|
||||||
|
memory sections.
|
||||||
|
|
||||||
|
Memory is divided into "pages" of 4096 bytes, and are allocated based on
|
||||||
|
this number. It is considered an error to request memory that isn't
|
||||||
|
aligned to this address, and it is an error to request a multiple of pages
|
||||||
|
that is different from this number. For example, you cannot request 4097
|
||||||
|
bytes -- you must request 8192 bytes.
|
||||||
|
|
||||||
|
Memory is allocated on a first-come, first-served basis. Physical addresses
|
||||||
|
may be specified when allocating memory, in which case they are taken from
|
||||||
|
that physical address. Otherwise, they are pulled from the "general-purpose
|
||||||
|
RAM" section.
|
||||||
|
|
||||||
A process can request specific memory ranges to be allocated. For example, a `uart_server` might request the UART memory region be allocated so that it can handle that device and provide a service. This region cannot be re-mapped to another process until it is freed.
|
A process can request specific memory ranges to be allocated. For example, a `uart_server` might request the UART memory region be allocated so that it can handle that device and provide a service. This region cannot be re-mapped to another process until it is freed.
|
||||||
|
|
||||||
@ -8,6 +22,16 @@ A process can request more memory for its heap. This will pull memory from the
|
|||||||
|
|
||||||
If a process intends to spawn multiple threads, then it must malloc that memory prior to creating the thread.
|
If a process intends to spawn multiple threads, then it must malloc that memory prior to creating the thread.
|
||||||
|
|
||||||
|
## Special Virtual Memory Addresses
|
||||||
|
|
||||||
|
These addresses are statically mapped in virtual memory. They are only visible in "Supervisor" mode. However, they are globally mapped, and are available in every process.
|
||||||
|
|
||||||
|
| Address | Description
|
||||||
|
| ---------- | -----------
|
||||||
|
| 0x01000000 | Kernel arguments, allocation tables
|
||||||
|
| 0x02000000 | Kernel binary image
|
||||||
|
| 0x04000000 | Kernel data section
|
||||||
|
|
||||||
## Memory Whitelist
|
## Memory Whitelist
|
||||||
|
|
||||||
Memory is kept in a whitelist. That is, when calling `sys_memory_allocate()`, the address is first validated against a list of known ranges. This has two major benefits:
|
Memory is kept in a whitelist. That is, when calling `sys_memory_allocate()`, the address is first validated against a list of known ranges. This has two major benefits:
|
||||||
@ -20,3 +44,60 @@ Memory is kept in a whitelist. That is, when calling `sys_memory_allocate()`, t
|
|||||||
Each valid memory page has an associated table entry. This entry simply contains a `XousPid`, to indicate which process the memory block belongs to. A `XousPid` of `0` is invalid, and indicates the region is free. A `XousPid` of `1` indicates the page belongs to the kernel.
|
Each valid memory page has an associated table entry. This entry simply contains a `XousPid`, to indicate which process the memory block belongs to. A `XousPid` of `0` is invalid, and indicates the region is free. A `XousPid` of `1` indicates the page belongs to the kernel.
|
||||||
|
|
||||||
In a system with ample amounts of memory, all valid memory page would get its own memory table. However, in resource-constrained systems, a simple array is not suitable, and so a programmatic lookup table is used instead.
|
In a system with ample amounts of memory, all valid memory page would get its own memory table. However, in resource-constrained systems, a simple array is not suitable, and so a programmatic lookup table is used instead.
|
||||||
|
|
||||||
|
## Kernel Arguments
|
||||||
|
|
||||||
|
There are several arguments that specify where kernel structures should go.
|
||||||
|
|
||||||
|
### Memory Blocks
|
||||||
|
|
||||||
|
The kernel needs to know the range of pages. This is passed to the kernel
|
||||||
|
as a list in the following form:
|
||||||
|
|
||||||
|
```
|
||||||
|
MBLK,$count,
|
||||||
|
$start1,$len1,$name
|
||||||
|
$start2,$len2,$name
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
The first memory range that is listed is always RAM, which is the range that will be used for dealing with `sbrk` and unspecified memory allocations. The name should be printable ASCII, and is primarily used for debugging.
|
||||||
|
|
||||||
|
### Kernel Memory
|
||||||
|
|
||||||
|
This structure specifies how kernel memory is laid out.
|
||||||
|
|
||||||
|
## Allocation Tables
|
||||||
|
|
||||||
|
Each page of memory has an entry in the allocation tables. When allocating
|
||||||
|
a new page, Xous ensures that page is not currently allocated to another
|
||||||
|
process. This ensures that each page of memory is only assigned to one
|
||||||
|
process at a time, unless that page is handed out as shared.
|
||||||
|
|
||||||
|
Allocation tables have the following layout:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct AllocationEntry {
|
||||||
|
/// PID that owns this page. `0` if this
|
||||||
|
/// page is unallocated.
|
||||||
|
pid: XousPid,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PageRange {
|
||||||
|
/// A slice of all allocations within this range.
|
||||||
|
entries: &[AllocationEntry],
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PageAllocations {
|
||||||
|
/// Each range of memory gets its own allocation table.
|
||||||
|
/// ranges[0] is always defined as RAM,
|
||||||
|
/// and is where memory comes from when
|
||||||
|
/// no physical address is specified.
|
||||||
|
ranges: &[PageRange],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Page Tables
|
||||||
|
|
||||||
|
Each process requires its own page table. The kernel will be mapped to a fixed offset in each process, in order to save some RAM and make
|
||||||
|
context switches easier.
|
||||||
|
43
startup.md
Normal file
43
startup.md
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Xous Startup Process
|
||||||
|
|
||||||
|
This document describes how a running system is established.
|
||||||
|
|
||||||
|
## Pre-Boot Environment
|
||||||
|
|
||||||
|
A pre-boot environment is needed to pass kernel arguments. It simply loads the argument structure into RAM, sets `$a0`, and jumps to the stage-1 kernel.
|
||||||
|
|
||||||
|
## Stage 1: Allocating memory pages
|
||||||
|
|
||||||
|
This stage-0 kernel allocates space for various kernel data structures according to kernel arguments. At the end of this, the kernel will not rely on anything from the pre-boot environment.
|
||||||
|
|
||||||
|
1. Determine how many pages to allocate from the end of RAM by reading `XASZ` and adding the total of `MBLK`.
|
||||||
|
1. Allocate that number of pages from the end of RAM
|
||||||
|
1. Copy the args to the first page
|
||||||
|
1. Create PageAllocations tables from args, just following the args array
|
||||||
|
1. Allocate one more page for stack, and set $sp to point there.
|
||||||
|
|
||||||
|
At this point, we don't need to rely on external memory anymore. There is no MMU, so we're still running in Machine Mode. We still need to copy data off of the SPI flash to set up things like the kernel and PID1.
|
||||||
|
|
||||||
|
## Stage 2: Loading the kernel and creating the first process
|
||||||
|
|
||||||
|
The first process will eventually get turned into PID1, however at the start we're running without an associated userspace process. In fact, we're even running without kernel loaded.
|
||||||
|
|
||||||
|
1. Allocate pages for main kernel, assigning them to PID1
|
||||||
|
1. Copy main kernel into newly-allocated pages
|
||||||
|
1. Allocate page for PID1 SATP
|
||||||
|
1. Allocate second-level pages for kernel
|
||||||
|
1. Map our current stack to PID1
|
||||||
|
1. Delegate all interrupts to supervisor mode
|
||||||
|
1. Set up a `trap_handler` to handle machine mode traps, even though they should never happen
|
||||||
|
1. Return to kernel and set the stack pointer, enabling MMU
|
||||||
|
|
||||||
|
The kernel is now running in Supervisor mode. The MMU is enabled, but there still is no PID1.
|
||||||
|
|
||||||
|
## Stage 3: Getting the first process running
|
||||||
|
|
||||||
|
In this final stage, PID1 is transformed into a full-fledged process.
|
||||||
|
|
||||||
|
1. Allocate process ID for PID1
|
||||||
|
1. Allocate stack pages for PID1
|
||||||
|
1. Map text section for PID1
|
||||||
|
1. Enable MMU by returning to PID1 -- free existing stack beforehand.
|
Loading…
Reference in New Issue
Block a user