Firmware Interface
Design Philosophy
Xita tries to enforce zero-trust on all hardware, firmware, and OS resources system wide. As a result, the firmware interface has incredibly strict policies on how its resources (eg. memory-mapped peripheral device registers) can be accessed by drivers.
There are two files that are used in defining Xita's firmware behavior:
device.chip
*.chip
files are used to define the firmware layout of the target AArch64 chip- (other architectures planned for future support).
- The chip defines several "firmware interfaces".
driver.xs
- Each firmware interface can link up with one single driver source file at any time
- Drivers are unique in that they are able to directly access/modify memory addresses within its predefined space
- They are not able to access any memory outside of their designated range
Format/Structure for *.chip files
(Obviously subject to change for security refinement)
-- rpi3.chip
-- Chip Name (used in terminal options, etc.)
rpi3
-- Set Desired Assembly Language
-- Currently, we support "Arm64" and "Arm32" architectures
arch="Arm64"
-- Desired Memory Mappings and Firmware Interfaces
GPIO: 0x3F215000 + 0xD4; -- The first number represents an initial memory address
BSC: 0x3F205000 + 0x1C, -- The second number represents the number of bytes
0x3F804000 + 0x1C,
0x3F805000 + 0x1C;
DMA: 0x3F007000 + 0x1400, -- Separated ranges can be denoted by commas
0x3FE05000 + 0x100; -- And terminated by semi-colins
SPI: 0x3F204000 + 0x14;
Timer: 0x3F003000 + 0x18 -- Make sure there is no ';' at the end
NOTE: Alternatively, you can define the drivers using -
operator (in contrast to +
operator). The -
syntax will instead define the lower
GPIO: 0x3F215000 - 0x3F2150D4; -- The first number represents an initial memory address
BSC: 0x3F205000 - 0x3F20501C, -- The second number represents the number of bytes
0x3F804000 - 0x3F80401C,
0x3F805000 - 0x3F80501C;
DMA: 0x3F007000 - 0x3F008400, -- Separated ranges can be denoted by commas
0x3FE05000 - 0x3FE05100; -- And terminated by semi-colins
SPI: 0x3F204000 - 0x3F204014;
Timer: 0x3F003000 - 0x3F003018 -- Make sure there is no ';' at the end
Firmware Drivers
Each chip can have a collection of firmware drivers written in Xita's functional language. Each firmware driver addresses a unique named Firmware Interface, defined in the loaded .chip
file (above).
Once the firmware driver is defined correctly, it can freely access any memory address within the predefined interface range. As an example, see the following Driver file:
-- Match up with firmware interfaces named "Timer" in `.chip` files
!DRIVER Timer
-- Legal, because Timer owns the requested address
-- getStatus will return the data value in address: `0x3F003000`
let getStatus = $ 0x3F003000 ;;
-- Illegal and will throw compiler error
-- Timer does not own this address for the defined chip
let getSPIStatus = $0x3F204000
Memory Access Operators
Firmware Drivers have the unique ability to directly access the data held at specific memory addresses, but they can only access data in the range owned by the firmware driver (defined in the *.chip file).
To read or write data to a specfic memory address, the integer address must be known at compile time. Therefore, it can only be an integer literal (e.g. 10
) or a defined integer constant identifier (e.g. const Int addr = 10
).
Read Data in Memory Address
There are two mechanisms for using data in a specific memory address.
$ addr
Notation
const Int text_start = 0xB8000;;
-- Usage
$text_start;;
$0xB8000
addr *-> var in exp
Notation
let adjust_color =
0xB8000 *-> buf in
(buf + 10) -- parenthesis are not required. Added for Readability
Write Data to Memory Address
addr <-* data
Notation
let put_char c index =
(0xB8000 + index) <-* c