From b72d84a2eaf15bfd1e20e516d381076adcd72a51 Mon Sep 17 00:00:00 2001 From: Gregory Guche <gregory.guche@univ-lille.fr> Date: Thu, 22 May 2025 15:39:31 +0200 Subject: [PATCH] Modified the file format, which has now a magic number and version. Modified crt0 to read the new file format and to check the magic number and version. Added an off-board script in fae_utils, ie read_fae.py, to read a fae file off-board. --- .gitignore | 1 + GETTING_STARTED.md | 306 +++++++++++++ Makefile | 6 +- README.md | 57 ++- crt0/Makefile | 39 +- crt0/crt0.c | 992 +++++++++++++++++++++++++---------------- crt0/crt0.h | 140 ++---- crt0/interface.h | 105 ----- crt0/link.ld | 6 +- fae_utils/build_fae.py | 256 ++++++----- fae_utils/constants.py | 95 ++++ fae_utils/read_fae.py | 357 +++++++++++++++ main.c | 93 ++-- stdriot/stdriot.c | 556 ++++++++++++++--------- stdriot/stdriot.h | 528 +--------------------- 15 files changed, 2006 insertions(+), 1531 deletions(-) create mode 100644 GETTING_STARTED.md delete mode 100644 crt0/interface.h create mode 100644 fae_utils/constants.py create mode 100755 fae_utils/read_fae.py diff --git a/.gitignore b/.gitignore index e3a9818..8b971f2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ *.fae *.elf build +__pycache__ diff --git a/GETTING_STARTED.md b/GETTING_STARTED.md new file mode 100644 index 0000000..1bc013e --- /dev/null +++ b/GETTING_STARTED.md @@ -0,0 +1,306 @@ +# Getting Started with XIPFS Format + +## Context +ELF files have a quite large memory footprint and are not that easy to parse; +two points that are not desired in an embedded software context or a post-issuance one. + +XIPFS format is a custom made file format that only needs a few information to ensure +runtime execution. + +XIPFS format toolchain parses off-board the ELF format to collect these data to +satisfy the XIPFS loader (located in `xipfs_file_exec`). + +Once the ELF file converted to a FAE file, developers only have to upload the latter onto +the board, where XIPFS has been deployed before. + +## Prerequisites +Developers will need : + ++ a GNU ARM toolchain, as of today available on [ARM developer site](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads). + + Latest version can be downloaded, but at least arm-none-eabi version 13.3.1 is required. + + *The tools* **MUST** *be available system-wide :* + - *either by creating aliases/symbolic links to their real locations,* + - *or by adding their path to the PATH environment variable on Linux and alike systems.* ++ GNU Make version 4.3. ++ python3. ++ pyelftools, at least in version 0.30. + + *On Linux, according to distibutions, one may apt install them (`python3-pyelftools`)*. + +## How to build your own code to XIPFS format ? +Because XIPFS format is a toolchain relying on different tools and linker scripts/options, +developers will first need their **sources to be included at the root of this very template**. + +Then, **root Makefile needs to be modified accordingly to the target sources in order to build an ELF version of the program.** +Developers **may add and modify target rules and dependencies**, but also add both **compilation and linker options**. + +**ELF name can be chosen by modifying the TARGET** make variable at the beginning of the Makefile. +*By default, it is defined as "hello-world".* + +Once an ELF file is obtained, the **XIPFS toolchain is able to convert this ELF file to a FAE one automatically**, without any other modification. + +No need to touch to the CRT0 or the fae_utils directories contents. + +Once the root Makefile has been edited to include project sources and build directives, **just enter `make` in a terminal**. + +The XIPFS will then **produce different files into the `build` directory** at project's root : + ++ Intermediate files such as `*.o` object files, ++ ELF files, ++ FAE files, ++ gdbinit file, which is aimed at be sourced into `gdb` to ease debugging. + +## How to use the generated gdbinit file ? +Two variables need to be completed to use the gdbinit file : + ++ `$flash_base`, to a value corresponding to the executable XIPFS file to be debugged, ++ `$ram_base`, to the beginning of the RAM of the development board. + +Then, the file can be loaded : + ++ at startup as follows, '`arm-none-eabi-gdb -x path_to/build/gdbinit`' ++ later on in `gdb` by entering '`source path_to/build/gdbinit`' + +With this file, both CRT0 and your own code can be debugged. + +## How to inspect a FAE file off-board ? +It is possible to **retrieve information on a FAE file from a command line tool named `read_fae.py` in `fae_utils` directory.** +This tool also performs different **integrity checks and emits errors on malformed FAE files**. + +Here is such an attempt on a valid FAE file : + +``` +$ fae_utils/read_fae.py build/hello-world.fae +- Binary size : 1152 bytes +- Magic Number And Version : 0xfacade10 +- CRT0 : [start : @0 byte , end : @707 bytes] (size : 708 bytes) +- Relocation entries : [start : @712 bytes , end : @715 bytes] (size : 4 bytes) + - Count : 1 +- Entrypoint offset in .rom : 213 bytes +- .rom.ram : [start : @716 bytes , end : @719 bytes] (size : 4 bytes) +- .rom : [start : @720 bytes , end : @1051 bytes] (size : 332 bytes) +- .got : [start : @1052 bytes , end : @1107 bytes] (size : 56 bytes) +.bss : 4 bytes +- Integrity check : OK +``` + +**To access to `read_fae.py`'s options**, please enter simply : + +``` +$ fae_utils/read_fae.py +usage: fae_utils/read_fae.py [-Dcrt0, -Hcrt0, -Hreloc, -Hromram, -Drom, -Hrom, -Hgot, -v] FAEFilename + +--Dcrt0 : will disassemble CRT0 +--Hcrt0 : will hexdump CRT0 +--Hreloc : will hexdump relocation table +--Hromram : will hexdump rom_ram section aka data +--Drom : will disassemble rom section aka text +--Hrom : will hexdump rom section aka text +--Hgot : will hexdump GOT +--v : will enable all options +``` + +**Hexdump options** will display an `hexdump -C` text according to the target : + +``` +$ fae_utils/read_fae.py -Hrom build/hello-world.fae +- Binary size : 1152 bytes +- Magic Number And Version : 0xfacade10 +- CRT0 : [start : @0 byte , end : @707 bytes] (size : 708 bytes) +- Relocation entries : [start : @712 bytes , end : @715 bytes] (size : 4 bytes) + - Count : 1 +- Entrypoint offset in .rom : 213 bytes +- .rom.ram : [start : @716 bytes , end : @719 bytes] (size : 4 bytes) +- .rom : [start : @720 bytes , end : @1051 bytes] (size : 332 bytes) +00000000 17 20 70 47 20 20 70 47 13 b5 1a 4b 01 28 5a f8 |. pG pG...K.(Z.| +00000010 03 30 03 dd 18 4a 5a f8 02 20 1a 60 1b 68 98 47 |.0...JZ.. .`.h.G| +00000020 16 4b 5a f8 03 10 16 4b 01 91 04 46 5a f8 03 00 |.KZ....K...FZ...| +00000030 00 f0 3c f8 13 4b 01 a9 5a f8 03 00 00 f0 36 f8 |..<..K..Z.....6.| +00000040 11 4b 5a f8 03 10 11 4b 5a f8 03 00 00 f0 2e f8 |.KZ....KZ.......| +00000050 0f 4b 5a f8 03 10 0f 4b 5a f8 03 00 00 f0 26 f8 |.KZ....KZ.....&.| +00000060 0d 4b 03 21 5a f8 03 00 00 f0 20 f8 20 46 02 b0 |.K.!Z..... . F..| +00000070 10 bd 00 bf 20 00 00 00 1c 00 00 00 00 00 00 00 |.... ...........| +00000080 04 00 00 00 08 00 00 00 24 00 00 00 0c 00 00 00 |........$.......| +00000090 28 00 00 00 10 00 00 00 14 00 00 00 02 4b 5a f8 |(............KZ.| +000000a0 03 30 1b 68 1b 68 18 47 18 00 00 00 0f b4 07 b5 |.0.h.h.G........| +000000b0 07 4b 5a f8 03 30 04 a9 1b 68 51 f8 04 0b 5b 68 |.KZ..0...hQ...[h| +000000c0 01 91 98 47 03 b0 5d f8 04 eb 04 b0 70 47 00 bf |...G..].....pG..| +000000d0 18 00 00 00 08 b5 07 4b 5a f8 03 30 00 f5 a3 62 |.......KZ..0...b| +000000e0 00 f5 83 61 d0 f8 14 04 1a 60 ff f7 8d ff ff f7 |...a.....`......| +000000f0 d5 ff fe e7 18 00 00 00 68 65 6c 6c 6f 20 77 6f |........hello wo| +00000100 72 6c 64 20 21 00 40 73 74 72 69 6e 67 20 3d 20 |rld !.@string = | +00000110 25 70 0a 00 40 26 73 74 72 69 6e 67 20 3d 20 25 |%p..@&string = %| +00000120 70 0a 00 40 26 6d 61 69 6e 20 3d 20 25 70 0a 00 |p..@&main = %p..| +00000130 26 63 73 74 20 3d 20 25 70 0a 00 63 73 74 20 3d |&cst = %p..cst =| +00000130 26 63 73 74 20 3d 20 25 70 0a 00 63 |&cst = %p..c | +- .got : [start : @1052 bytes , end : @1107 bytes] (size : 56 bytes) +.bss : 4 bytes +- Integrity check : OK +``` + +**Disassembly options** will use `arm-none-eabi-objdump` under the hood to provide output : + +``` +- Binary size : 1152 bytes +- Magic Number And Version : 0xfacade10 +- CRT0 : [start : @0 byte , end : @707 bytes] (size : 708 bytes) +- Relocation entries : [start : @712 bytes , end : @715 bytes] (size : 4 bytes) + - Count : 1 +- Entrypoint offset in .rom : 213 bytes +- .rom.ram : [start : @716 bytes , end : @719 bytes] (size : 4 bytes) +- .rom : [start : @720 bytes , end : @1051 bytes] (size : 332 bytes) + +/tmp/tmppybpuh4v/.rom: file format binary + + +Disassembly of section .data: + +00000000 <.data>: + 0: 2017 movs r0, #23 + 2: 4770 bx lr + 4: 2020 movs r0, #32 + 6: 4770 bx lr + 8: b513 push {r0, r1, r4, lr} + a: 4b1a ldr r3, [pc, #104] @ (0x74) + c: 2801 cmp r0, #1 + e: f85a 3003 ldr.w r3, [sl, r3] + 12: dd03 ble.n 0x1c + 14: 4a18 ldr r2, [pc, #96] @ (0x78) + 16: f85a 2002 ldr.w r2, [sl, r2] + 1a: 601a str r2, [r3, #0] + 1c: 681b ldr r3, [r3, #0] + 1e: 4798 blx r3 + 20: 4b16 ldr r3, [pc, #88] @ (0x7c) + 22: f85a 1003 ldr.w r1, [sl, r3] + 26: 4b16 ldr r3, [pc, #88] @ (0x80) + 28: 9101 str r1, [sp, #4] + 2a: 4604 mov r4, r0 + 2c: f85a 0003 ldr.w r0, [sl, r3] + 30: f000 f83c bl 0xac + 34: 4b13 ldr r3, [pc, #76] @ (0x84) + 36: a901 add r1, sp, #4 + 38: f85a 0003 ldr.w r0, [sl, r3] + 3c: f000 f836 bl 0xac + 40: 4b11 ldr r3, [pc, #68] @ (0x88) + 42: f85a 1003 ldr.w r1, [sl, r3] + 46: 4b11 ldr r3, [pc, #68] @ (0x8c) + 48: f85a 0003 ldr.w r0, [sl, r3] + 4c: f000 f82e bl 0xac + 50: 4b0f ldr r3, [pc, #60] @ (0x90) + 52: f85a 1003 ldr.w r1, [sl, r3] + 56: 4b0f ldr r3, [pc, #60] @ (0x94) + 58: f85a 0003 ldr.w r0, [sl, r3] + 5c: f000 f826 bl 0xac + 60: 4b0d ldr r3, [pc, #52] @ (0x98) + 62: 2103 movs r1, #3 + 64: f85a 0003 ldr.w r0, [sl, r3] + 68: f000 f820 bl 0xac + 6c: 4620 mov r0, r4 + 6e: b002 add sp, #8 + 70: bd10 pop {r4, pc} + 72: bf00 nop + 74: 0020 movs r0, r4 + 76: 0000 movs r0, r0 + 78: 001c movs r4, r3 + 7a: 0000 movs r0, r0 + 7c: 0000 movs r0, r0 + 7e: 0000 movs r0, r0 + 80: 0004 movs r4, r0 + 82: 0000 movs r0, r0 + 84: 0008 movs r0, r1 + 86: 0000 movs r0, r0 + 88: 0024 movs r4, r4 + 8a: 0000 movs r0, r0 + 8c: 000c movs r4, r1 + 8e: 0000 movs r0, r0 + 90: 0028 movs r0, r5 + 92: 0000 movs r0, r0 + 94: 0010 movs r0, r2 + 96: 0000 movs r0, r0 + 98: 0014 movs r4, r2 + 9a: 0000 movs r0, r0 + 9c: 4b02 ldr r3, [pc, #8] @ (0xa8) + 9e: f85a 3003 ldr.w r3, [sl, r3] + a2: 681b ldr r3, [r3, #0] + a4: 681b ldr r3, [r3, #0] + a6: 4718 bx r3 + a8: 0018 movs r0, r3 + aa: 0000 movs r0, r0 + ac: b40f push {r0, r1, r2, r3} + ae: b507 push {r0, r1, r2, lr} + b0: 4b07 ldr r3, [pc, #28] @ (0xd0) + b2: f85a 3003 ldr.w r3, [sl, r3] + b6: a904 add r1, sp, #16 + b8: 681b ldr r3, [r3, #0] + ba: f851 0b04 ldr.w r0, [r1], #4 + be: 685b ldr r3, [r3, #4] + c0: 9101 str r1, [sp, #4] + c2: 4798 blx r3 + c4: b003 add sp, #12 + c6: f85d eb04 ldr.w lr, [sp], #4 + ca: b004 add sp, #16 + cc: 4770 bx lr + ce: bf00 nop + d0: 0018 movs r0, r3 + d2: 0000 movs r0, r0 + d4: b508 push {r3, lr} + d6: 4b07 ldr r3, [pc, #28] @ (0xf4) + d8: f85a 3003 ldr.w r3, [sl, r3] + dc: f500 62a3 add.w r2, r0, #1304 @ 0x518 + e0: f500 6183 add.w r1, r0, #1048 @ 0x418 + e4: f8d0 0414 ldr.w r0, [r0, #1044] @ 0x414 + e8: 601a str r2, [r3, #0] + ea: f7ff ff8d bl 0x8 + ee: f7ff ffd5 bl 0x9c + f2: e7fe b.n 0xf2 + f4: 0018 movs r0, r3 + f6: 0000 movs r0, r0 + f8: 6568 str r0, [r5, #84] @ 0x54 + fa: 6c6c ldr r4, [r5, #68] @ 0x44 + fc: 206f movs r0, #111 @ 0x6f + fe: 6f77 ldr r7, [r6, #116] @ 0x74 + 100: 6c72 ldr r2, [r6, #68] @ 0x44 + 102: 2064 movs r0, #100 @ 0x64 + 104: 0021 movs r1, r4 + 106: 7340 strb r0, [r0, #13] + 108: 7274 strb r4, [r6, #9] + 10a: 6e69 ldr r1, [r5, #100] @ 0x64 + 10c: 2067 movs r0, #103 @ 0x67 + 10e: 203d movs r0, #61 @ 0x3d + 110: 7025 strb r5, [r4, #0] + 112: 000a movs r2, r1 + 114: 2640 movs r6, #64 @ 0x40 + 116: 7473 strb r3, [r6, #17] + 118: 6972 ldr r2, [r6, #20] + 11a: 676e str r6, [r5, #116] @ 0x74 + 11c: 3d20 subs r5, #32 + 11e: 2520 movs r5, #32 + 120: 0a70 lsrs r0, r6, #9 + 122: 4000 ands r0, r0 + 124: 6d26 ldr r6, [r4, #80] @ 0x50 + 126: 6961 ldr r1, [r4, #20] + 128: 206e movs r0, #110 @ 0x6e + 12a: 203d movs r0, #61 @ 0x3d + 12c: 7025 strb r5, [r4, #0] + 12e: 000a movs r2, r1 + 130: 6326 str r6, [r4, #48] @ 0x30 + 132: 7473 strb r3, [r6, #17] + 134: 3d20 subs r5, #32 + 136: 2520 movs r5, #32 + 138: 0a70 lsrs r0, r6, #9 + 13a: 6300 str r0, [r0, #48] @ 0x30 + 13c: 7473 strb r3, [r6, #17] + 13e: 3d20 subs r5, #32 + 140: 2520 movs r5, #32 + 142: 0a64 lsrs r4, r4, #9 + 144: 0000 movs r0, r0 + 146: 0000 movs r0, r0 + 148: 0003 movs r3, r0 + ... + +- .got : [start : @1052 bytes , end : @1107 bytes] (size : 56 bytes) +.bss : 4 bytes +- Integrity check : OK +``` diff --git a/Makefile b/Makefile index ad0e4ae..369a84c 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,6 @@ PREFIX = arm-none-eabi- CC = $(PREFIX)gcc LD = $(PREFIX)gcc -OBJCOPY = $(PREFIX)objcopy CFLAGS = -Wall CFLAGS += -Wextra @@ -21,7 +20,6 @@ CFLAGS += -mpic-register=sl CFLAGS += -mno-pic-data-is-text-relative CFLAGS += -fPIC CFLAGS += -ffreestanding -CFLAGS += -ffunction-sections ifdef DEBUG CFLAGS += -Og CFLAGS += -ggdb @@ -37,7 +35,6 @@ LDFLAGS += -nolibc LDFLAGS += -nostdlib LDFLAGS += -Tlink.ld LDFLAGS += -Wl,-q -LDFLAGS += -Wl,-gc-sections # Disable the new linker warning '--warn-rwx-segments' introduced by # Binutils 2.39, which causes the following message: "warning: # $(TARGET).elf has a LOAD segment with RWX permissions". @@ -55,11 +52,12 @@ build : build/$(TARGET).fae: build/$(TARGET).elf ./fae_utils/build_fae.py $< + @chmod 644 $@ build/$(TARGET).elf: build/main.o build/stdriot.o $(LD) $(LDFLAGS) $^ -o $@ -build/stdriot.o: stdriot/stdriot.c | build +build/stdriot.o: stdriot/stdriot.c stdriot/stdriot.h | build $(CC) $(CFLAGS) -c $< -o $@ build/main.o: main.c | build diff --git a/README.md b/README.md index 70ce420..9c97641 100644 --- a/README.md +++ b/README.md @@ -62,13 +62,13 @@ We have designed an *ad hoc* binary file format that keeps only the necessary information for relocation. This format consists of four main components. The first component contains the CRT0 code, responsible for performing relocation tasks before executing the software code. The -second component comprises metadata, including a table of symbol values -and relocation tables. The table of symbol values mainly stores software -section sizes and entry point offsets. The third component consists of -the software sections, including the `.text` section (software code), -`.got` section (Global Offset Table), and `.data` section (initialized -global variables). The fourth and final component is optional padding, -ensuring the binary file is a multiple of 32 bytes. +second component comprises part of metadata (ie relocation tables) and +the binary total size.The third component consists of the software sections, +including the `.text`section (software code),`.got` section (Global Offset Table), +and `.data`section (initialized global variables). The fourth and final component is +metadata footer included in optional padding, ensuring the binary file is +a multiple of 32 bytes; the MPU finest grain. This metadata footer includes +different segment sizes as well as binary entrypoint. Our *ad hoc* binary file format significantly reduces file size, shrinking it to approximately 20% of the equivalent ELF file size for @@ -79,21 +79,42 @@ sections, followed by a diagram that correlates these sections with the software components: ``` -+-------+---------------+------------+-------+------+-------+---------+ -| | | | | | | | -| .text | symbol values | relocation | .text | .got | .data | padding | -| | table | tables | | | | | -+-------+---------------+------------+-------+------+-------+---------+ - -+-------+----------------------------+----------------------+---------+ -| | | | | -| crt0 | metadata | post-issuance | padding | -| | | software | | -+-------+----------------------------+----------------------+---------+ +Not to scale. + + 4B 4B 4B 4B 4B 4B 4B 4B ++-------+---+------------+-------+------+-------+---------+---+---+---+---+---+---+---+ +| | | | | | | | | | | | | | | +| .text | | relocation | .text | .got | .data | padding | | | | | | | | +| | | table | | | | | | | | | | | | ++-------+---+------------+-------+------+-------+---------+---+---+---+---+---+---+---+ + ^ ^ ^ ^ ^ ^ ^ ^ + | | | | | | | | + .- Binary total size, | | | | | | .- Magic Number and Version. + padding included (4 Bytes). | | | | | .----- CRT0 size. + | | | | .--------- Entrypoint offset. + | | | .------------- ROM RAM size. + | | .----------------- ROM size. + | .--------------------- GOT size. + .------------------------- RAM size. + ++-------+----------------+----------------------+-------------------------------------+ +| | | | +--------------------------+| +| crt0 | metadata | post-issuance | padding | metadata footer || +| | | software | +--------------------------+| ++-------+----------------+----------------------+-------------------------------------+ ``` ## Debugging post-issuance software +Please, read also [GettingStarted document](GETTING_STARTED.md) + +### Off-board. +XiPFS' format comes with fae_utils, which include `read_fae.py`. +This script allows to read a fae file offboard and to perform integrity checks. +It can also be used to show different sections hexadecimal dump, or disassembled code +according to sections. + +### On-board. Since we only keep the necessary information from the ELF file for relocation purposes, our binary format no longer includes debugging information like DWARF. However, we generate a `gdbinit` file that diff --git a/crt0/Makefile b/crt0/Makefile index 7164290..6496897 100644 --- a/crt0/Makefile +++ b/crt0/Makefile @@ -38,18 +38,23 @@ LD := $(PREFIX)ld OBJCOPY := $(PREFIX)objcopy RM := rm -CFLAGS = -Wall -CFLAGS += -Wextra -CFLAGS += -Werror -CFLAGS += -mthumb -CFLAGS += -mcpu=cortex-m4 -CFLAGS += -ffreestanding -# Prevent GCC from generating code that uses the PIC -# register. -CFLAGS += -ffixed-sl -CFLAGS += -Os -CFLAGS += -g3 -CFLAGS += -ggdb +CFLAGS = -Wall +CFLAGS += -Wextra +CFLAGS += -Werror +CFLAGS += -mthumb +CFLAGS += -mcpu=cortex-m4 +CFLAGS += -ffreestanding +ifdef DEBUG +CFLAGS += -Og +CFLAGS += -ggdb +else +CFLAGS += -Os +endif + +LDFLAGS = -Tlink.ld + +OBJCOPYFLAGS = --input-target=elf32-littlearm +OBJCOPYFLAGS += --output-target=binary all: ../build/crt0.fae @@ -57,13 +62,11 @@ all: ../build/crt0.fae mkdir -p ../build ../build/crt0.fae: ../build/crt0.elf - $(OBJCOPY)\ - --input-target=elf32-littlearm\ - --output-target=binary\ - $< $@ + $(OBJCOPY) $(OBJCOPYFLAGS) $< $@ + @chmod 644 $@ -../build/crt0.elf: ../build/crt0.o - $(LD) -T link.ld $< -o $@ +../build/crt0.elf: ../build/crt0.o link.ld + $(LD) $(LDFLAGS) $< -o $@ ../build/crt0.o: crt0.c | ../build $(CC) $(CFLAGS) -c $< -o $@ diff --git a/crt0/crt0.c b/crt0/crt0.c index 410ecf3..5857301 100644 --- a/crt0/crt0.c +++ b/crt0/crt0.c @@ -1,396 +1,638 @@ -/*******************************************************************************/ -/* © Université de Lille, The Pip Development Team (2015-2024) */ -/* */ -/* This software is a computer program whose purpose is to run a minimal, */ -/* hypervisor relying on proven properties such as memory isolation. */ -/* */ -/* This software is governed by the CeCILL license under French law and */ -/* abiding by the rules of distribution of free software. You can use, */ -/* modify and/ or redistribute the software under the terms of the CeCILL */ -/* license as circulated by CEA, CNRS and INRIA at the following URL */ -/* "http://www.cecill.info". */ -/* */ -/* As a counterpart to the access to the source code and rights to copy, */ -/* modify and redistribute granted by the license, users are provided only */ -/* with a limited warranty and the software's author, the holder of the */ -/* economic rights, and the successive licensors have only limited */ -/* liability. */ -/* */ -/* In this respect, the user's attention is drawn to the risks associated */ -/* with loading, using, modifying and/or developing or reproducing the */ -/* software by the user in light of its specific status of free software, */ -/* that may mean that it is complicated to manipulate, and that also */ -/* therefore means that it is reserved for developers and experienced */ -/* professionals having in-depth computer knowledge. Users are therefore */ -/* encouraged to load and test the software's suitability as regards their */ -/* requirements in conditions enabling the security of their systems and/or */ -/* data to be ensured and, more generally, to use and operate it in the */ -/* same conditions as regards security. */ -/* */ -/* The fact that you are presently reading this means that you have had */ -/* knowledge of the CeCILL license and that you accept its terms. */ -/*******************************************************************************/ +/* + * Copyright (C) 2024 Université de Lille + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief CRT0 implementation + * + * @author Damien Amara <damien.amara@univ-lille.fr> + * @author Gregory Guche <gregory.guche@univ-lille.fr> + * + * @} + */ #include <stdint.h> #include <stddef.h> #include "crt0.h" -#include "interface.h" /* * WARNING: No global variable must be declared in this file! */ -/*! - * \def die +#ifdef __GNUC__ + +/** + * @internal + * + * @def NORETURN + * + * @brief Instructs the compiler that the function will not + * return + * + * @warning The _start function must have this attribute + */ +#define NORETURN __attribute__((noreturn)) + +/** + * @internal + * + * @def NAKED + * + * @brief Instructs the compiler that the function will not have + * prologue/epilogue sequences + */ +#define NAKED __attribute__((naked)) + +/** + * @internal + * + * @def UNUSED + * + * @brief Instructs the compiler that the function/variable is meant + * to be possibly unused + */ +#define UNUSED __attribute__((unused)) + +/** + * @internal + * + * @def SECTION + * + * @brief Instructs the compiler that a variable (or function) + * lives in a particular section + * + * @param name The section name + */ +#define SECTION(name) __attribute__((section(name))) + +#else + +#error "GCC is required to compile this source file" + +#endif /* __GNUC__ */ + +/** + * @internal + * + * @def LDM_STM_NB_BYTES_COPIED + * + * @brief Number of bytes copied by the LDM and STM instructions + */ +#define LDM_STM_NB_BYTES_COPIED (48) + +/** + * @internal + * + * @def LDRD_STRD_NB_BYTES_COPIED + * + * @brief Number of bytes copied by the LDRD and STRD + * instructions + */ +#define LDRD_STRD_NB_BYTES_COPIED (8) + +/** + * @internal + * + * @def LDR_STR_NB_BYTES_COPIED + * + * @brief Number of bytes copied by the LDR and STR instructions + */ +#define LDR_STR_NB_BYTES_COPIED (4) + +/** + * @internal + * + * @def LDRB_STRB_NB_BYTES_COPIED + * + * @brief Number of bytes copied by the LDRB and STRB + * instructions + */ +#define LDRB_STRB_NB_BYTES_COPIED (1) + +/** + * @internal + * + * @def ROUND + * + * @brief Round x to the next power of two y + * + * @param x The number to round to the next power of two y + * + * @param y The power of two with which to round x + */ +#define ROUND(x, y) (((x) + (y) - 1) & ~((y) - 1)) + +/** + * @internal + * + * @define THUMB_ADDRESS + * + * @brief Calculate the odd address for Thumb mode from x + * + * @param x The address to convert to thumb mode + */ +#define THUMB_ADDRESS(x) ((x) | 1) + +/** + * @internal + * + * @def ERR_MSG_PREFIX + * + * @brief Prefix of error messages + */ +#define ERR_MSG_PREFIX "crt0: " + +/** + * @internal + * + * @def ERR_MSG_0 + * + * @brief Error message number 0 + */ +#define ERR_MSG_0 "invalid file version" + +/** + * @internal + * + * @def ERR_MSG_1 + * + * @brief Error message number 1 + */ +#define ERR_MSG_1 "not enough ram" + +/** + * @internal + * + * @def ERR_MSG_2 + * + * @brief Error message number 2 + */ +#define ERR_MSG_2 "out-of-bounds offset" + +/** + * @internal + * + * @def ERR_MSG_3 + * + * @brief Error message number 3 + */ +#define ERR_MSG_3 "cannot relocate offsets in .rom" + +/** + * @internal + * + * @def ERR_MSG_4 + * + * @brief Error message number 4 + */ +#define ERR_MSG_4 "cannot relocate offsets in .got" + +/** + * @internal + * + * @def SYS_WRITE0 + * + * @brief Semihosting software interrupts (SWIs) to write a + * null-terminated string to the console. + */ +#define SYS_WRITE0 "4" + +/** + * @internal + * + * @def ANGEL_SWI + * + * @brief Value indicating to the host that we are requesting a + * semihosting operation. + */ +#define ANGEL_SWI "0xab" + +/** + * @def PANIC + * + * @brief This macro handles fatal errors + */ +#define PANIC() for (;;); + +/** + * @internal + * + * @brief Enumeration of error message identifiers + */ +typedef enum err_msg_id_u { + /** + * Identifier of error message 1 + */ + ERR_MSG_ID_0, + /** + * Identifier of error message 1 + */ + ERR_MSG_ID_1, + /** + * Identifier of error message 2 + */ + ERR_MSG_ID_2, + /** + * Identifier of error message 3 + */ + ERR_MSG_ID_3, + /** + * Identifier of error message 4 + */ + ERR_MSG_ID_4, +} err_msg_id_t; + +#define BINARY_MAGIC_NUMBER_AND_VERSION ((uint32_t)0xFACADE10) + +typedef enum binary_footer_offsets_e { + BINARY_FOOTER_RAM_SIZE_OFFSET = -28, + BINARY_FOOTER_BSS_SIZE_OFFSET = BINARY_FOOTER_RAM_SIZE_OFFSET, + BINARY_FOOTER_GOT_SIZE_OFFSET = -24, + BINARY_FOOTER_ROM_SIZE_OFFSET = -20, + BINARY_FOOTER_TEXT_SIZE_OFFSET = BINARY_FOOTER_ROM_SIZE_OFFSET, + BINARY_FOOTER_ROM_RAM_SIZE_OFFSET = -16, + BINARY_FOOTER_DATA_SIZE_OFFSET = BINARY_FOOTER_ROM_RAM_SIZE_OFFSET, + BINARY_FOOTER_ENTRYPOINT_OFFSET = -12, + BINARY_FOOTER_CRT0_OFFSET = -8, + BINARY_FOOTER_MAGIC_NUMBER_AND_VERSION_OFFSET = -4 +} binary_footer_offsets_t; + +/** + * @internal + * + * @brief Data structure that describes a patch info entry + */ +typedef struct patchinfo_entry_s +{ + /** + * The pointer offest to patch + */ + uint32_t ptr_off; +} patchinfo_entry_t; + +/** + * @internal + * + * @brief Data structure that describes a path info table + */ +typedef struct patchinfo_table_s +{ + /** + * The number of patchinfo entry + */ + uint32_t entry_number; + /** + * The patchinfo entries + */ + patchinfo_entry_t entries[]; +} patchinfo_table_t; + +/** + * @internal + * + * @brief Data structure that describes metadata + */ +typedef struct metadata_s +{ + /** + * The binary size in bytes + */ + uint32_t binary_size; + /** + * The patchinfo table + */ + patchinfo_table_t patchinfo_table; +} metadata_t; + +/** + * @internal + * + * @brief the offset of the metadata structure + */ +extern uint32_t *__metadataOff; + +/* + * Fonction prototypes + */ + +static inline void* memcpy(void *dest, const void *src, size_t n); +static NAKED void die(err_msg_id_t id UNUSED); + +/** + * @pre ctx is a pointer to a memory region containg an + * accessible and valid CRT0 data structure + * + * @warning This function must be positioned first in the file + * to ensure that the first instruction generated for it is + * located at offset 0 in the output binary file * - * \brief This macro displays the string passed in parameter - * using the semihosting and stops the execution of the - * code. + * @see -fno-toplevel-reorder GCC option in the Makefile * - * \param msg The string to be displayed. It will be placed - * right after the assembly code, so that it can be - * referred to wherever the code is loaded and without - * the need to relocate it. + * @brief The entry point of CRT0 is responsible for copying + * data from NVM to RAM, initializing RAM to zero, and applying + * patch information to enable post-issuance software deployment + * + * @param ctx A pointer to a memory region containg a CRT0 data + * structure */ -#define die(msg)\ - __asm__\ - (\ - "mov r0, #4\n"\ - "adr r1, 1f\n"\ - "bkpt 0xab\n"\ - "b .\n"\ - "1: .asciz " #msg "\n"\ - ".align 1\n"\ - ) - -static __attribute__((always_inline)) inline void* -memcpy(void *dest, const void *src, size_t n) +SECTION("._start") NORETURN void _start(crt0_ctx_t *ctx) { - /* According to Cortex-M4 Technical Reference Manual, 3 - * Programmers Model, 3.3 Instruction set summary, 3.3.1 - * Cortex-M4 instructions. */ - while (n >= 44) { - __asm__ volatile - ( - "ldmia %0!, {r2-r12}\n" - "stmia %1!, {r2-r12}\n" - : "+r" (src), "+r" (dest) - : - : "r2", "r3", "r4", "r5", - "r6", "r7", "r8", "r9", - "r10", "r11", "r12", - "memory" - ); - n -= 44; - } - - /* There is up to 43 bytes to copy. */ - while (n >= 8) { - __asm__ volatile - ( - "ldrd r2, r3, [%0], #8\n" - "strd r2, r3, [%1], #8\n" - : "+r" (src), "+r" (dest) - : - : "r2", "r3", "memory" - ); - n -= 8; - } - - /* There is up to 3 bytes to copy. */ - while (n > 0) { - __asm__ volatile - ( - "ldrb r2, [%0], #1\n" - "strb r2, [%1], #1\n" - : "+r" (src), "+r" (dest) - : - : "r2", "memory" - ); - n--; - } - - return dest; + /* get metadata */ + metadata_t *metadata = (metadata_t *) + ((uint32_t)ctx->bin_base + (uint32_t) &__metadataOff); + + uint8_t *end_of_binary = ((uint8_t *)ctx->bin_base) + metadata->binary_size; + + /* Magic number and version */ + uint32_t *value_ptr = (uint32_t *)( + end_of_binary + BINARY_FOOTER_MAGIC_NUMBER_AND_VERSION_OFFSET + ); + if ( *value_ptr != BINARY_MAGIC_NUMBER_AND_VERSION ) + die(ERR_MSG_ID_0); + + + uint32_t entry_point_offset = *(uint32_t *)( + end_of_binary + BINARY_FOOTER_ENTRYPOINT_OFFSET + ); + uint32_t rom_sec_size = *(uint32_t *)( + end_of_binary + BINARY_FOOTER_ROM_SIZE_OFFSET + ); + uint32_t got_sec_size = *(uint32_t *)( + end_of_binary + BINARY_FOOTER_GOT_SIZE_OFFSET + ); + uint32_t rom_ram_sec_size = *(uint32_t *)( + end_of_binary + BINARY_FOOTER_ROM_RAM_SIZE_OFFSET + ); + uint32_t ram_sec_size = *(uint32_t *)( + end_of_binary + BINARY_FOOTER_RAM_SIZE_OFFSET + ); + + /* calculate section start address in ROM */ + uint32_t rom_sec_addr = + (uint32_t) metadata + sizeof(metadata->binary_size) + + sizeof(metadata->patchinfo_table.entry_number) + + metadata->patchinfo_table.entry_number * + sizeof(patchinfo_entry_t); + uint32_t got_sec_addr = rom_sec_addr + rom_sec_size; + uint32_t rom_ram_sec_addr = got_sec_addr + got_sec_size; + uint32_t entry_point_addr = THUMB_ADDRESS(rom_sec_addr + entry_point_offset); + + /* calculate relocated section start address in RAM */ + uint32_t rel_got_sec_addr = (uint32_t)ctx->ram_start; + uint32_t rel_rom_ram_sec_addr = rel_got_sec_addr + got_sec_size; + uint32_t rel_ram_sec_addr = rel_rom_ram_sec_addr + rom_ram_sec_size; + + /* check if sufficient RAM is available for relocation */ + uint32_t ram_end_addr = (uint32_t)ctx->ram_end; + if (rel_got_sec_addr + got_sec_size > ram_end_addr || + rel_rom_ram_sec_addr + rom_ram_sec_size > ram_end_addr || + rel_ram_sec_addr + ram_sec_size > ram_end_addr) { + die(ERR_MSG_ID_1); + } + + /* update unused RAM value */ + ctx->ram_start = (void *)(rel_ram_sec_addr + ram_sec_size); + /* Update unused ROM value */ + ctx->nvm_start = (void *)ROUND((uint32_t)end_of_binary, 32); + + /* relocate .rom.ram section */ + (void)memcpy((void *) rel_rom_ram_sec_addr, + (void *) rom_ram_sec_addr, + (size_t) rom_ram_sec_size); + + /* initialize .ram section */ + for (size_t i = 0; (i << 2) < ram_sec_size; i++) { + ((uint32_t *) rel_ram_sec_addr)[i] = 0; + } + + /* + * Relocate the '.got' section from ROM to RAM, + * dynamically updating each global variable offset + * - originally relative to the binary file's start - to + * the new memory addresses where they are now located + */ + for (size_t i = 0; (i << 2) < got_sec_size; i++) { + uint32_t off = ((uint32_t *) got_sec_addr)[i]; + uint32_t addr = 0; + if (off < rom_sec_size) { + addr = rom_sec_addr + off; + goto valid_got_entry; + } + off -= rom_sec_size; + if (off < got_sec_size) { + /* offset must always be zero for the + * _rom_size symbol */ + addr = rel_got_sec_addr + off; + goto valid_got_entry; + } + off -= got_sec_size; + if (off < rom_ram_sec_size) { + addr = rel_rom_ram_sec_addr + off; + goto valid_got_entry; + } + off -= rom_ram_sec_size; + if (off < ram_sec_size) { + addr = rel_ram_sec_addr + off; + goto valid_got_entry; + } + die(ERR_MSG_ID_2); +valid_got_entry: + ((uint32_t *) rel_got_sec_addr)[i] = addr; + } + + /* + * Update each global pointer by assigning the relocated + * address of the value it points to the corresponding + * relocated pointer address + */ + for (size_t i = 0; i < metadata->patchinfo_table.entry_number; i++) { + uint32_t ptr_off = metadata->patchinfo_table.entries[i].ptr_off; + uint32_t off = *((uint32_t *)(rom_sec_addr + ptr_off)); + uint32_t ptr_addr = 0, addr = 0; + if (ptr_off < rom_sec_size) { + goto ptr_off_in_rom; + } + ptr_off -= rom_sec_size; + if (ptr_off < got_sec_size) { + goto off_in_got; + } + ptr_off -= got_sec_size; + if (ptr_off < rom_ram_sec_size) { + ptr_addr = rel_rom_ram_sec_addr + ptr_off; + goto valid_ptr_addr; + } + ptr_off -= rom_ram_sec_size; + if (ptr_off < ram_sec_size) { + ptr_addr = rel_ram_sec_addr + ptr_off; + goto valid_ptr_addr; + } + goto off_out_bounds; +valid_ptr_addr: + if (off < rom_sec_size) { + addr = rom_sec_addr + off; + goto valid_addr; + } + off -= rom_sec_size; + if (off < got_sec_size) { + goto off_in_got; + } + off -= got_sec_size; + if (off < rom_ram_sec_size) { + addr = rel_rom_ram_sec_addr + off; + goto valid_addr; + } + off -= rom_ram_sec_size; + if (off < ram_sec_size) { + addr = rel_ram_sec_addr + off; + goto valid_addr; + } +off_out_bounds: + die(ERR_MSG_ID_2); +ptr_off_in_rom: + die(ERR_MSG_ID_3); +off_in_got: + die(ERR_MSG_ID_4); +valid_addr: + *((uint32_t *) ptr_addr) = addr; + } + + /* + * Set R0 to the address of the first parameter passed to + * the start() function, initialize R10 with the address of + * the relocated GOT, and branch to the address of the + * start() function + */ + __asm__ volatile + ( + " mov r0, %0 \n" + " mov sl, %1 \n" + " bx %2 \n" + : + : "r" (ctx), + "r" (rel_got_sec_addr), + "r" (entry_point_addr) + : "r0", "r1", "sl" + ); + + PANIC(); } -#if 0 -__attribute__((naked)) static void* -memcpy(void *dest, const void *src, size_t n) +/** + * @brief A version of memcpy optimized for Cortex-M4 + * + * @param dest Destination memory area + * + * @param src Source memory area + * + * @param n Number of bytes to copy + * + * @return Returns a pointer to dest + * + * @see Cortex-M4 Technical Reference Manual 3.3.1 Cortex-M4 + * instructions + */ +static inline void* memcpy(void *dest, + const void *src, + size_t n) { - (void)dest; - (void)src; - (void)n; - __asm__ volatile - ( - "push {r4, lr}\n" - "cmp r2, #43\n" - "bls 2f\n" - "push {r5-r11}\n" - "1:\n" - "ldmia r1!, {r3-r12, lr}\n" - "stmia r0!, {r3-r12, lr}\n" - "subs r2, #44\n" - "cmp r2, #43\n" - "bhi 1b\n" - "pop {r5-r11}\n" - "2:\n" - "movs r4, #10\n" - "lsrs r3, #3\n" - "muls r3, r4\n" - "ldr r4, =3f\n" - "rsb r3, r4\n" - "mov pc, r3\n" - - "ldrd r3, r4, [r1], #8\n" - "strd r3, r4, [r0], #8\n" - "subs r2, #8\n" - - "ldrd r3, r4, [r1], #8\n" - "strd r3, r4, [r0], #8\n" - "subs r2, #8\n" - - "ldrd r3, r4, [r1], #8\n" - "strd r3, r4, [r0], #8\n" - "subs r2, #8\n" - - "ldrd r3, r4, [r1], #8\n" - "strd r3, r4, [r0], #8\n" - "subs r2, #8\n" - - "ldrd r3, r4, [r1], #8\n" - "strd r3, r4, [r0], #8\n" - "subs r2, #8\n" - "3:" - "pop {r4, pc}\n" - ); + const char *src0 = src; + char *dest0 = dest; + + while (n >= LDM_STM_NB_BYTES_COPIED) { + __asm__ volatile + ( + "ldmia %0!, {r2-r12,r14}\n" + "stmia %1!, {r2-r12,r14}\n" + : "+r" (src0), "+r" (dest0) + : + : "r2", "r3", "r4", "r5", + "r6", "r7", "r8", "r9", + "r10", "r11", "r12", "r14", + "memory" + ); + n -= LDM_STM_NB_BYTES_COPIED; + } + while (n >= LDRD_STRD_NB_BYTES_COPIED) { + __asm__ volatile + ( + "ldrd r2, r3, [%0], #8\n" + "strd r2, r3, [%1], #8\n" + : "+r" (src0), "+r" (dest0) + : + : "r2", "r3", "memory" + ); + n -= LDRD_STRD_NB_BYTES_COPIED; + } + if (n >= LDR_STR_NB_BYTES_COPIED) { + __asm__ volatile + ( + "ldr r2, [%0], #4\n" + "str r2, [%1], #4\n" + : "+r" (src0), "+r" (dest0) + : + : "r2", "memory" + ); + n -= LDR_STR_NB_BYTES_COPIED; + } + while (n >= LDRB_STRB_NB_BYTES_COPIED) { + __asm__ volatile + ( + "ldrb r2, [%0], #1\n" + "strb r2, [%1], #1\n" + : "+r" (src0), "+r" (dest0) + : + : "r2", "memory" + ); + n -= LDRB_STRB_NB_BYTES_COPIED; + } + + return dest; } -#endif -extern void -_start(interface_t *interface, void **syscalls) +/** + * @brief Print error message and stop execution + * + * @param Identifier of the message to print + */ +static NAKED void die(err_msg_id_t id UNUSED) { - void (*exit)(int) = NULL; - void *oldGotAddr = NULL; - int status; - - if (syscalls != NULL) { - exit = syscalls[1]; - } - - /* Retrieve current memory layout. */ - uint32_t binaryAddr = - (uint32_t) interface->root; - uint32_t unusedRamAddr = - (uint32_t) interface->unusedRamStart; - uint32_t ramEndAddr = - (uint32_t) interface->ramEnd; - - /* Retrieve metadata informations. */ - metadata_t *metadata = (metadata_t *) - (binaryAddr + (uint32_t) &__metadataOff); - uint32_t entryPointOffset = - metadata->symbolTable.entryPoint; - uint32_t romSecSize = - metadata->symbolTable.romSecSize; - uint32_t gotSecSize = - metadata->symbolTable.gotSecSize; - uint32_t romRamSecSize = - metadata->symbolTable.romRamSecSize; - uint32_t ramSecSize = - metadata->symbolTable.ramSecSize; - - /* Calculation of the section start address in ROM. */ - uint32_t romSecAddr = - (uint32_t) metadata + sizeof(metadata->symbolTable) + - sizeof(metadata->patchinfoTable.entryNumber) + - metadata->patchinfoTable.entryNumber * - sizeof(patchinfoEntry_t); - uint32_t gotSecAddr = romSecAddr + romSecSize; - uint32_t romRamSecAddr = gotSecAddr + gotSecSize; - uint32_t entryPointAddr = romSecAddr + entryPointOffset; - - /* Calculation of the relocated section start address in RAM. */ - uint32_t relGotSecAddr = unusedRamAddr; - uint32_t relRomRamSecAddr = relGotSecAddr + gotSecSize; - uint32_t relRamSecAddr = relRomRamSecAddr + romRamSecSize; - - /* Check if there is enough RAM to perform relocation. */ - if (relGotSecAddr + gotSecSize > ramEndAddr || - relRomRamSecAddr + romRamSecSize > ramEndAddr || - relRamSecAddr + ramSecSize > ramEndAddr) - { - die("CRT0: Not enough RAM to perform relocation...\n"); - } - - /* Update of the unused RAM value. */ - interface->unusedRamStart = - (void *)(relRamSecAddr + ramSecSize); - /* Update of the unused ROM value. */ - interface->unusedRomStart = (void *)relRomRamSecAddr + romRamSecSize; - - /* Relocation of the '.rom.ram' section. */ - (void)memcpy((void *) relRomRamSecAddr, - (void *) romRamSecAddr, - (size_t) romRamSecSize); - - /* Initialization of the '.ram' section. */ - for (size_t i = 0; (i << 2) < ramSecSize; i++) - { - ((uint32_t *) relRamSecAddr)[i] = 0; - } - - /* Relocation of the '.got' section from the ROM to the RAM, - * replacing on the fly each global variable offset, expressed - * relative to the beginning of the binary file, with the - * address of the memory area where they were relocated or - * loaded. */ - for (size_t i = 0; (i << 2) < gotSecSize; i++) - { - uint32_t off = ((uint32_t *) gotSecAddr)[i]; - uint32_t addr = 0; - - if (off < romSecSize) - { - addr = romSecAddr + off; - goto validGotEntry; - } - off -= romSecSize; - - if (off < gotSecSize) - { - /* - * Note that offset should always be zero. - * This should be for the _rom_size symbol. - */ - addr = relGotSecAddr + off; - goto validGotEntry; - } - off -= gotSecSize; - - if (off < romRamSecSize) - { - addr = relRomRamSecAddr + off; - goto validGotEntry; - } - off -= romRamSecSize; - - if (off < ramSecSize) - { - addr = relRamSecAddr + off; - goto validGotEntry; - } - - die("CRT0: An offset is not located in any section...\n"); - -validGotEntry: - ((uint32_t *) relGotSecAddr)[i] = addr; - } - - /* Patch each global pointer by assigning the relocated address - * of the value pointed to by the pointer to the relocated - * pointer address. */ - for (size_t i = 0; i < metadata->patchinfoTable.entryNumber; i++) - { - uint32_t ptrOff = metadata->patchinfoTable.entries[i].ptrOff; - uint32_t off = *((uint32_t *)(romSecAddr + ptrOff)); - uint32_t ptrAddr = 0, addr = 0; - - if (ptrOff < romSecSize) - { - goto ptrOffInRom; - } - ptrOff -= romSecSize; - - if (ptrOff < gotSecSize) - { - goto offInGot; - } - ptrOff -= gotSecSize; - - if (ptrOff < romRamSecSize) - { - ptrAddr = relRomRamSecAddr + ptrOff; - goto validPtrAddr; - } - ptrOff -= romRamSecSize; - - if (ptrOff < ramSecSize) - { - ptrAddr = relRamSecAddr + ptrOff; - goto validPtrAddr; - } - - goto offOutBounds; - -validPtrAddr: - if (off < romSecSize) - { - addr = romSecAddr + off; - goto validAddr; - } - off -= romSecSize; - - if (off < gotSecSize) - { - goto offInGot; - } - off -= gotSecSize; - - if (off < romRamSecSize) - { - addr = relRomRamSecAddr + off; - goto validAddr; - } - off -= romRamSecSize; - - if (off < ramSecSize) - { - addr = relRamSecAddr + off; - goto validAddr; - } - -offOutBounds: - die("CRT0: An offset is not located in any section...\n"); -ptrOffInRom: - die("CRT0: A pointer offset is located in the ROM section...\n"); -offInGot: - die("CRT0: An offet is located in the GOT section...\n"); -validAddr: - *((uint32_t *) ptrAddr) = addr; - } - - /* Initialization of the PIC register with the address of the - * relocated GOT, so that the partition can access its global - * variables. */ - __asm__("mov %0, sl" : "=r" (oldGotAddr) : :); - __asm__("mov sl, %0" : : "r" (relGotSecAddr) : "sl"); - - /* Branch to the entry point of the partition. */ - status = ((entryPoint_t) entryPointAddr)(interface, (void *) relGotSecAddr, oldGotAddr, syscalls); - - __asm__("mov sl, %0" : : "r" (oldGotAddr) : "sl"); - - /* System call to exit */ - if (exit != NULL) { - if ((uint32_t)syscalls[0] == 0) { - (*exit)(status); - } else { - /* TODO return status instead of 0. */ - __asm__ volatile - ( - "mov r0, #0\n" - "mov r1, #0\n" - "push {r0, r1}\n" - "mov r0, #0\n" - "mov r1, #54\n" - "mov r2, #0\n" - "mov r3, #1\n" - "mov r4, #1\n" - "svc #12\n" - /* UNREACHABLE */ - ); - } - } - for (;;); + __asm__ volatile + ( + " mov r2, r0 \n" + " mov r0, #" SYS_WRITE0 " \n" + " adr.w r1, 3f \n" + " bkpt " ANGEL_SWI " \n" + " mov r0, #" SYS_WRITE0 " \n" + " adr.w r3, 1f \n" + " add.w r2, r3, r2, lsl #3 \n" + " orr.w r2, #1 \n" + " bx r2 \n" + "1: adr.w r1, 4f \n" + " b.w 2f \n" + " adr.w r1, 5f \n" + " b.w 2f \n" + " adr.w r1, 6f \n" + " b.w 2f \n" + " adr.w r1, 7f \n" + " b.w 2f \n" + " adr.w r1, 8f \n" + "2: bkpt " ANGEL_SWI " \n" + " b . \n" + "3: .asciz \"" ERR_MSG_PREFIX "\" \n" + "4: .asciz \"" ERR_MSG_0 "\\n\" \n" + "5: .asciz \"" ERR_MSG_1 "\\n\" \n" + "6: .asciz \"" ERR_MSG_2 "\\n\" \n" + "7: .asciz \"" ERR_MSG_3 "\\n\" \n" + "8: .asciz \"" ERR_MSG_4 "\\n\" \n" + " .align 1 \n" + ); } diff --git a/crt0/crt0.h b/crt0/crt0.h index 81126da..62d00ef 100644 --- a/crt0/crt0.h +++ b/crt0/crt0.h @@ -1,112 +1,42 @@ -/*******************************************************************************/ -/* © Université de Lille, The Pip Development Team (2015-2022) */ -/* */ -/* This software is a computer program whose purpose is to run a minimal, */ -/* hypervisor relying on proven properties such as memory isolation. */ -/* */ -/* This software is governed by the CeCILL license under French law and */ -/* abiding by the rules of distribution of free software. You can use, */ -/* modify and/ or redistribute the software under the terms of the CeCILL */ -/* license as circulated by CEA, CNRS and INRIA at the following URL */ -/* "http://www.cecill.info". */ -/* */ -/* As a counterpart to the access to the source code and rights to copy, */ -/* modify and redistribute granted by the license, users are provided only */ -/* with a limited warranty and the software's author, the holder of the */ -/* economic rights, and the successive licensors have only limited */ -/* liability. */ -/* */ -/* In this respect, the user's attention is drawn to the risks associated */ -/* with loading, using, modifying and/or developing or reproducing the */ -/* software by the user in light of its specific status of free software, */ -/* that may mean that it is complicated to manipulate, and that also */ -/* therefore means that it is reserved for developers and experienced */ -/* professionals having in-depth computer knowledge. Users are therefore */ -/* encouraged to load and test the software's suitability as regards their */ -/* requirements in conditions enabling the security of their systems and/or */ -/* data to be ensured and, more generally, to use and operate it in the */ -/* same conditions as regards security. */ -/* */ -/* The fact that you are presently reading this means that you have had */ -/* knowledge of the CeCILL license and that you accept its terms. */ -/*******************************************************************************/ - #ifndef __CRT0_H__ #define __CRT0_H__ -#include "interface.h" - -typedef int (*entryPoint_t)(interface_t *interface, void *relGotSecAddr, void *oldGotAddr, void **syscalls); - -typedef struct symbolTable_s -{ - /*! - * \brief The entry point address within the - * partition. - */ - uint32_t entryPoint; - /*! - * \brief The '.rom' section size, in bytes, - * of the partition. - */ - uint32_t romSecSize; - /*! - * \brief The '.rom.ram' section size, in - * bytes, of the partition. - */ - uint32_t romRamSecSize; - /*! - * \brief The '.ram' section size, in bytes, - * of the partition. - */ - uint32_t ramSecSize; - /*! - * \brief The '.got' section size, in bytes, - * of the partition. - */ - uint32_t gotSecSize; - /*! - * \brief The '.romRam' section end address - * of the partition. - */ - uint32_t romRamEnd; -} symbolTable_t; - -typedef struct patchinfoEntry_s -{ - /*! - * \brief The pointer offest to patch. - */ - uint32_t ptrOff; -} patchinfoEntry_t; - -typedef struct patchinfoTable_s -{ - /*! - * \brief The number of patchinfo entry. - */ - uint32_t entryNumber; - /*! - * \brief The patchinfo entries. - */ - patchinfoEntry_t entries[]; -} patchinfoTable_t; - -typedef struct metadata_s -{ - /*! - * \brief The symbol table. - */ - symbolTable_t symbolTable; - /*! - * \brief The patchinfo table. - */ - patchinfoTable_t patchinfoTable; -} metadata_t; +/** + * @brief File format version number + * + * @warning MUST REMAIN SYNCHRONIZED with scripts/build_fae.py's definition + */ +#define CRT0_MAGIC_NUMBER_AND_VERSION ((uint32_t)0xFACADE10) -/*! - * \brief the offset of the metadata structure. +/** + * @brief Data structure that describes the memory layout + * required by the CRT0 to execute the relocatable binary + * + * @warning MUST REMAIN SYNCHRONIZED with xipfs's file definition. */ -extern uint32_t *__metadataOff; +typedef struct crt0_ctx_s { + /** + * Start address of the binary in the NVM + */ + void *bin_base; + /** + * Start address of the available free RAM + */ + void *ram_start; + /** + * End address of the available free RAM + */ + void *ram_end; + /** + * Start address of the free NVM + */ + void *nvm_start; + /** + * End address of the free NVM + */ + void *nvm_end; +} crt0_ctx_t; + +typedef void (*entryPoint_t)(crt0_ctx_t *crt0_ctx); #endif /* __CRT0_H__ */ diff --git a/crt0/interface.h b/crt0/interface.h deleted file mode 100644 index 5735197..0000000 --- a/crt0/interface.h +++ /dev/null @@ -1,105 +0,0 @@ -/*******************************************************************************/ -/* © Université de Lille, The Pip Development Team (2015-2023) */ -/* Copyright (C) 2020-2023 Orange */ -/* */ -/* This software is a computer program whose purpose is to run a minimal, */ -/* hypervisor relying on proven properties such as memory isolation. */ -/* */ -/* This software is governed by the CeCILL license under French law and */ -/* abiding by the rules of distribution of free software. You can use, */ -/* modify and/ or redistribute the software under the terms of the CeCILL */ -/* license as circulated by CEA, CNRS and INRIA at the following URL */ -/* "http://www.cecill.info". */ -/* */ -/* As a counterpart to the access to the source code and rights to copy, */ -/* modify and redistribute granted by the license, users are provided only */ -/* with a limited warranty and the software's author, the holder of the */ -/* economic rights, and the successive licensors have only limited */ -/* liability. */ -/* */ -/* In this respect, the user's attention is drawn to the risks associated */ -/* with loading, using, modifying and/or developing or reproducing the */ -/* software by the user in light of its specific status of free software, */ -/* that may mean that it is complicated to manipulate, and that also */ -/* therefore means that it is reserved for developers and experienced */ -/* professionals having in-depth computer knowledge. Users are therefore */ -/* encouraged to load and test the software's suitability as regards their */ -/* requirements in conditions enabling the security of their systems and/or */ -/* data to be ensured and, more generally, to use and operate it in the */ -/* same conditions as regards security. */ -/* */ -/* The fact that you are presently reading this means that you have had */ -/* knowledge of the CeCILL license and that you accept its terms. */ -/*******************************************************************************/ - -#ifndef __INTERFACE_H__ -#define __INTERFACE_H__ - -/*! - * \brief This structure defines the interface that - * PIP provides to partitions. - */ -typedef struct interface_s -{ - /*! - * \brief The ID of the block containing the - * partition descriptor of the root - * partition. - */ - void *partDescBlockId; - - /*! - * \brief The limit address of the stack of - * the root partition. - */ - void *stackLimit; - - /*! - * \brief The stack top address of the root - * partition. - */ - void *stackTop; - - /*! - * \brief The VIDT start address of the root - * partition. - */ - void *vidtStart; - - /*! - * \brief The VIDT end address of the root - * partition. - */ - void *vidtEnd; - - /*! - * \brief The start address of the root - * partition binary. - */ - void *root; - - /*! - * \brief The start address of the unused - * ROM. - */ - void *unusedRomStart; - - /*! - * \brief The end address of the ROM. - */ - void *romEnd; - - /*! - * \brief The start address of the unused - * RAM. - */ - void *unusedRamStart; - - /*! - * \brief The end address of the RAM. - */ - void *ramEnd; - -} interface_t; - -#endif /* __INTERFACE_H__ */ diff --git a/crt0/link.ld b/crt0/link.ld index 74e6545..097a77c 100644 --- a/crt0/link.ld +++ b/crt0/link.ld @@ -1,5 +1,5 @@ /*******************************************************************************/ -/* © Université de Lille, The Pip Development Team (2015-2024) */ +/* © Université de Lille, The Pip Development Team (2015-2025) */ /* */ /* This software is a computer program whose purpose is to run a minimal, */ /* hypervisor relying on proven properties such as memory isolation. */ @@ -46,6 +46,10 @@ SECTIONS .text : { . = ALIGN( 4 ) ; + /* + * The ._start section MUST be the first one + */ + *(._start) *(.text*) . = ALIGN( 4 ) ; __metadataOff = . ; diff --git a/fae_utils/build_fae.py b/fae_utils/build_fae.py index 10bb193..59d29a0 100755 --- a/fae_utils/build_fae.py +++ b/fae_utils/build_fae.py @@ -15,21 +15,21 @@ from elftools.elf.sections import SymbolTableSection from elftools.elf.relocation import RelocationSection from elftools.elf.enums import ENUM_RELOC_TYPE_ARM as r_types -FAE_SUFFIX = ".fae" -CRTO_CLI_OPTION = "--crt0_path" -EXPORT_CRT0_TO_BYTEARRAY_DEFAULT_PATH = "./crt0/" +from constants import FAEConstants + +CLI_OPTION_CRT0_PATH = "--crt0_path" def usage(): """Print how to to use the script and exit""" - print(f'usage: {sys.argv[0]} [{CRTO_CLI_OPTION} crt0_path] ELFFilename') + print(f'usage: {sys.argv[0]} [{CLI_OPTION_CRT0_PATH} crt0_path] ELFFilename') print('') print(f'{sys.argv[0]} will build :') - print(f' - a fae file from ELFFilename,') - print(f' - a gdbinit file, to be used with gdb after editing.') - print(f'Please note that both files will be generated to the path of ELFFilename.') + print( ' - a fae file from ELFFilename,') + print(f' - a {FAEConstants.gdbinit} file, to be used with gdb after editing.') + print( 'Please note that both files will be generated to the path of ELFFilename.') print('') - print(f'{CRTO_CLI_OPTION} crt0_path') - print(f' This option allows to indicate a path to a custom crt0') + print(f'{CLI_OPTION_CRT0_PATH} crt0_path') + print( ' This option allows to indicate a path to a custom crt0') print(f' Default is {EXPORT_CRT0_TO_BYTEARRAY_DEFAULT_PATH}') sys.exit(1) @@ -41,32 +41,24 @@ def die(message): def export_crt0_to_bytearray(path_to_crt0, to_bytearray): - make_args = f"make -C {os.path.abspath(path_to_crt0)} realclean all" + make_args = f"{FAEConstants.MAKE} -C {os.path.abspath(path_to_crt0)} realclean all" result = subprocess.run(make_args, shell=True, capture_output=True, text=True) if result.returncode != 0: die(f'export_crt0_to_bytearray : failed to build crt0 : {result.stderr}') - crt0_filepath = path_to_crt0 + "/crt0.fae" + crt0_filepath = os.path.abspath(f'build/crt0{FAEConstants.SUFFIX}') with open(crt0_filepath, "rb") as crt0_file: to_bytearray += bytearray(crt0_file.read()) + print(f'Export CRT0 : {len(to_bytearray)} bytes') def to_word(x): """Convert a python integer to a LE 4-bytes bytearray""" - return x.to_bytes(4, byteorder='little') - -EXPORTED_SYMBOLS = [ - # The order of the symbols matter, as it reflects the - # writing order in the symbols.bin file - 'start', - '__rom_size', - '__rom_ram_size', - '__ram_size', - '__got_size', - '__rom_ram_end', -] - -def export_symbols_to_bytearray(elf_file, symbols_names, to_bytearray): + return x.to_bytes(4, byteorder=FAEConstants.ENDIANNESS) + + + +def export_symbols_to_dict(elf_file, symbols_names, to_dict): """Parse the symbol table sections to extract the st_value""" sh = elf_file.get_section_by_name('.symtab') if not sh: @@ -81,11 +73,10 @@ def export_symbols_to_bytearray(elf_file, symbols_names, to_bytearray): die(f'export_symbols_to_bytearray : .symtab : {symbol_name}: no symbol with this name') if len(symbols) > 1: die(f'export_symbols_to_bytearray : .symtab : {symbol_name}: more than one symbol with this name') - to_bytearray += to_word(symbols[0].entry['st_value']) + to_dict[symbol_name] = symbols[0].entry['st_value'] + print(f'Export symbol {symbol_name} = {to_dict[symbol_name]} bytes') -EXPORTED_RELOCATION_TABLES = [ '.rel.rom.ram' ] - def get_r_type(r_info): """Get the relocation type from r_info""" return r_info & 0xff @@ -94,35 +85,31 @@ def export_relocation_table(elf_file, relocation_table_name, to_bytearray): """Parse a relocation section to extract the r_offset""" sh = elf_file.get_section_by_name(relocation_table_name) if not sh: - die(f'export_relocation_table : {relocation_table_name}: is not a relocation section') + print(f'No relocation section named {relocation_table_name}') + to_bytearray += to_word(0) + return if not isinstance(sh, RelocationSection): die(f'export_relocation_table : {relocation_table_name}: is not a relocation section') if sh.is_RELA(): die(f'export_relocation_table : {relocation_table_name} : unsupported RELA') to_bytearray += to_word(sh.num_relocations()) + print(f'Export relocation table : entries count : {sh.num_relocations()}') for i, entry in enumerate(sh.iter_relocations()): if get_r_type(entry['r_info']) != r_types['R_ARM_ABS32']: die(f'export_relocation_table : {relocation_table_name} : entry {i}: unsupported relocation type') - to_bytearray += to_word(entry['r_offset']) + offset = entry['r_offset'] + to_bytearray += to_word(offset) + print(f'\t- Exporting relocation entry {i} : offset {offset}') def export_relocation_tables(elf_file, relocation_tables_names, to_bytearray): for relocation_table_name in relocation_tables_names: export_relocation_table(elf_file, relocation_table_name, to_bytearray) -EXPORT_PARTITION_OBJCOPY_DEFAULT_NAME = "arm-none-eabi-objcopy" - -EXPORT_PARTITION_OBJCOPY_FLAGS = [ - "--input-target=elf32-littlearm", "--output-target=binary" -] - -def export_partition(elf_name, objcopy_name, objcopy_flags, to_bytearray): - partition_name = "partition.fae" +def export_partition(elf_name, to_bytearray): - objcopy_args = objcopy_name - for objcopy_flag in objcopy_flags: - objcopy_args += f' {objcopy_flag}' - objcopy_args += f' {elf_name} {partition_name}' + partition_name = FAEConstants.PARTITION_NAME + objcopy_args = f'{FAEConstants.OBJCOPY_ARGS_BASE} {elf_name} {partition_name}' result = subprocess.run(objcopy_args, shell=True, capture_output=True, text=True) if result.returncode != 0: @@ -132,80 +119,108 @@ def export_partition(elf_name, objcopy_name, objcopy_flags, to_bytearray): with open(partition_name, "rb") as partition_file: to_bytearray += bytearray(partition_file.read()) - rm_args = f'rm {partition_name}' + print(f'Export partition : {len(to_bytearray)} bytes') + + rm_args = f'{FAEConstants.RM} {partition_name}' result = subprocess.run(args=rm_args, shell=True, capture_output=True, text=True) if result.returncode != 0: die(f'export_partition : failed to remove {partition_name} : {result.stderr}') -# The default value used for padding. This value corresponds to -# the default state of non-volatile NAND flash memories -PADDING_VALUE = b'\xff' - - -# The Pip binary size must be a multiple of this value. It -# corresponds to the minimum alignment required by the MPU of -# the ARMv7-M architecture -PADDING_MPU_ALIGNMENT = 32 - def round(x, y): """Round x to the next power of two y""" return ((x + y - 1) & ~(y - 1)) -def pad_bytearray(to_bytearray): - size = len(to_bytearray) - padding = round(size, PADDING_MPU_ALIGNMENT) - size - for i in range(padding): - to_bytearray += PADDING_VALUE - - -GENERATE_GDBINIT_DEFAULT_SYMBOLS_TO_FIND = [ - '__rom_size', - '__got_size', - '__rom_ram_size', - '__ram_size', -] - -def gdbinit_find_symbols(elf_file, symbols_to_find): - """Parse the symbol table sections to extract the st_value""" - sh = elf_file.get_section_by_name('.symtab') - if not sh: - die(f'gdbinit_find_symbols : .symtab : no section with this name found') - if not isinstance(sh, SymbolTableSection): - die(f'gdbinit_find_symbols : .symtab : is not a symbol table section') - if sh['sh_type'] != 'SHT_SYMTAB': - die(f'gdbinit_find_symbols : .symtab : is not a SHT_SYMTAB section') - found_symbols = [] - for symbol_name in symbols_to_find: - symbols = sh.get_symbol_by_name(symbol_name) - if not symbols: - die(f'gdbinit_find_symbols : .symtab : {symbol_name}: no symbol with this name') - if len(symbols) > 1: - die(f'gdbinit_find_symbols : .symtab : {symbol_name}: more than one symbol with this name') - found_symbols.append(symbols[0].entry['st_value']) - return found_symbols - -def generate_gdbinit(elf_file, crt0_path, metadata_size): - found_symbols = gdbinit_find_symbols(elf_file, GENERATE_GDBINIT_DEFAULT_SYMBOLS_TO_FIND) - - text_size = found_symbols[0] - got_size = found_symbols[1] - data_size = found_symbols[2] - bss_size = found_symbols[3] +def concatenate_and_pad_bytearray( + crt0_bytearray, + exported_symbols_dictionary, + relocation_bytearray, + partition_bytearray, + to_bytearray): + + raw_binary_size = len(crt0_bytearray) \ + + FAEConstants.BINARY_SIZE_BYTESIZE \ + + len(relocation_bytearray) \ + + len(partition_bytearray) + + padding_size = \ + round(raw_binary_size, FAEConstants.PADDING_MPU_ALIGNMENT) \ + - raw_binary_size + # minimal padding size represents : + # CRT0 size, => 4 bytes. + # EntryPoint aka start => 4 bytes. + # __rom_ram_size => 4 bytes. + # Magic Number and Version => 4 bytes. + #-------------------------------------- + # 16 bytes. + if padding_size < FAEConstants.FOOTER_BYTESIZE: + padding_size += FAEConstants.PADDING_MPU_ALIGNMENT + + padding_size -= FAEConstants.FOOTER_BYTESIZE + + # CRT0 + to_bytearray += crt0_bytearray + # Binary Size + to_bytearray += to_word( \ + raw_binary_size + padding_size + FAEConstants.FOOTER_BYTESIZE) + # Relocation Table + to_bytearray += relocation_bytearray + # Partition + to_bytearray += partition_bytearray + # Padding - minimal_padding_size + for i in range(padding_size): + to_bytearray += FAEConstants.PADDING_VALUE + + # FOOTER_RAM_SIZE_OFFSET = -28 + # FOOTER_BSS_SIZE_OFFSET = FOOTER_RAM_SIZE_OFFSET + to_bytearray += to_word( \ + exported_symbols_dictionary[FAEConstants.EXPORTED_SYMBOL_RAM_SIZE]) + # FOOTER_GOT_SIZE_OFFSET = -24 + to_bytearray += to_word( \ + exported_symbols_dictionary[FAEConstants.EXPORTED_SYMBOL_GOT_SIZE]) + # FOOTER_ROM_SIZE_OFFSET = -20 + # FOOTER_TEXT_SIZE_OFFSET = FOOTER_ROM_SIZE_OFFSET + to_bytearray += to_word( \ + exported_symbols_dictionary[FAEConstants.EXPORTED_SYMBOL_ROM_SIZE]) + # FOOTER_ROM_RAM_SIZE_OFFSET = -16 + # FOOTER_DATA_SIZE_OFFSET = FOOTER_ROM_RAM_SIZE_OFFSET + to_bytearray += to_word( \ + exported_symbols_dictionary[FAEConstants.EXPORTED_SYMBOL_ROM_RAM_SIZE]) + # FOOTER_ENTRYPOINT_OFFSET = -12 + to_bytearray += to_word( \ + exported_symbols_dictionary[FAEConstants.EXPORTED_SYMBOL_START]) + # FOOTER_CRT0_OFFSET = -8 + to_bytearray += to_word(len(crt0_bytearray)) + # FOOTER_MAGIC_NUMBER_AND_VERSION_OFFSET = -4 + to_bytearray += to_word(FAEConstants.MAGIC_NUMBER_AND_VERSION) + + + +def generate_gdbinit(elf_file, crt0_path, metadata_size, exported_symbols_dictionary): + text_size = \ + exported_symbols_dictionary[FAEConstants.EXPORTED_SYMBOL_ROM_SIZE] + got_size = \ + exported_symbols_dictionary[FAEConstants.EXPORTED_SYMBOL_GOT_SIZE] + data_size = \ + exported_symbols_dictionary[FAEConstants.EXPORTED_SYMBOL_ROM_RAM_SIZE] + bss_size = \ + exported_symbols_dictionary[FAEConstants.EXPORTED_SYMBOL_RAM_SIZE] basepath = elf_file.stream.name.split("/")[0] absolute_elf_path = os.path.abspath(elf_file.stream.name) - absolute_crt0_path = os.path.abspath(crt0_path + "crt0.elf") + absolute_crt0_path = os.path.abspath(basepath + "/crt0.elf") + + gdbinit_filename = f"{basepath}/{FAEConstants.GDBINIT_FILENAME}" - with open(f"{basepath}/gdbinit", "w+") as gdbinit_file: - gdbinit_file.write(f'set $flash_base = # Define the flash base address here\n') - gdbinit_file.write(f'set $ram_base = # Define the RAM base address here\n') - gdbinit_file.write(f'set $crt0_text = $flash_base\n') + with open(gdbinit_filename, "w+") as gdbinit_file: + gdbinit_file.write('set $flash_base = # Define the flash base address here\n') + gdbinit_file.write('set $ram_base = # Define the RAM base address here\n') + gdbinit_file.write('set $crt0_text = $flash_base\n') gdbinit_file.write(f'set $text = $crt0_text + {metadata_size}\n') gdbinit_file.write(f'set $got = $text + {text_size}\n') gdbinit_file.write(f'set $data = $got + {got_size}\n') - gdbinit_file.write(f'set $rel_got = $ram_base\n') + gdbinit_file.write('set $rel_got = $ram_base\n') gdbinit_file.write(f'set $rel_data = $rel_got + {got_size}\n') gdbinit_file.write(f'set $bss = $rel_data + {data_size}\n') gdbinit_file.write(f'add-symbol-file {absolute_crt0_path} -s .text $crt0_text\n') @@ -218,6 +233,7 @@ def generate_gdbinit(elf_file, crt0_path, metadata_size): f'{metadata_size + text_size + got_size + data_size}\n') gdbinit_file.write(f'set $ram_end = $ram_base + {got_size + data_size + bss_size}\n') + print(f'{os.path.abspath(gdbinit_filename)} has been generated.') if __name__ == '__main__': argc = len(sys.argv) @@ -225,13 +241,11 @@ if __name__ == '__main__': print(f"argc {argc}") usage() - - if argc == 2: elf_filename = sys.argv[1].strip() - crt0_path = EXPORT_CRT0_TO_BYTEARRAY_DEFAULT_PATH.strip() + crt0_path = FAEConstants.CRT0_DEFAULT_PATH.strip() else : - if (sys.argv[1] != CRTO_CLI_OPTION): + if (sys.argv[1] != CLI_OPTION_CRT0_PATH): usage() crt0_path = sys.argv[2].strip() @@ -242,7 +256,7 @@ if __name__ == '__main__': print('Bad ELFFilename : should be something along the line of name.elf') usage() - output_filename = elf_filename_parts[0] + FAE_SUFFIX + output_filename = elf_filename_parts[0] + FAEConstants.SUFFIX if crt0_path.endswith('/') == False: crt0_path += '/' @@ -252,27 +266,39 @@ if __name__ == '__main__': elf_file = ELFFile(f) # Formerly known as crt0.fae - export_crt0_to_bytearray(crt0_path, array_of_bytes) + crt0_bytearray = bytearray() + export_crt0_to_bytearray(crt0_path, crt0_bytearray) # Formerly known as symbols.fae - export_symbols_to_bytearray(elf_file, EXPORTED_SYMBOLS, array_of_bytes) + exported_symbols_dictionary = dict() + export_symbols_to_dict( \ + elf_file, FAEConstants.EXPORTED_SYMBOLS, \ + exported_symbols_dictionary) # Formerly known as relocation.fae - export_relocation_tables(elf_file, EXPORTED_RELOCATION_TABLES, array_of_bytes) + relocation_bytearray = bytearray() + export_relocation_tables( \ + elf_file, FAEConstants.EXPORTED_RELOCATION_TABLES, \ + relocation_bytearray) metadata_size = len(array_of_bytes) # Formerly known as partition.fae - export_partition( - elf_filename, - EXPORT_PARTITION_OBJCOPY_DEFAULT_NAME, - EXPORT_PARTITION_OBJCOPY_FLAGS, + partition_bytearray = bytearray() + export_partition( elf_filename, partition_bytearray ) + + concatenate_and_pad_bytearray( + crt0_bytearray, + exported_symbols_dictionary, + relocation_bytearray, + partition_bytearray, array_of_bytes) - #Formerly known as padding.fae - pad_bytearray(array_of_bytes) - # gdbinit - generate_gdbinit( elf_file, crt0_path, metadata_size ) + generate_gdbinit( + elf_file, + crt0_path, + metadata_size, + exported_symbols_dictionary) if (len(array_of_bytes) <= 0): die("Nothing has been written into array_of_bytes ! Please contact authors.") diff --git a/fae_utils/constants.py b/fae_utils/constants.py new file mode 100644 index 0000000..5c7b7bb --- /dev/null +++ b/fae_utils/constants.py @@ -0,0 +1,95 @@ +class FAEConstants: + + SUFFIX = ".fae" + + + BINUTILS_PREFIX = 'arm-none-eabi-' + + OBJCOPY = BINUTILS_PREFIX + 'objcopy' + OBJDUMP = BINUTILS_PREFIX + 'objdump' + + ENDIANNESS = 'little' + + OBJCOPY_ARGS_BASE = f'{OBJCOPY} --input-target=elf32-littlearm --output-target=binary' + OBJDUMP_ARGS_BASE = f'{OBJDUMP} -b binary -marm --endian={ENDIANNESS} -Mforce-thumb -D ' + + + MAGIC_NUMBER = int(0xFACADE00) + VERSION = int(0x10) + MAGIC_NUMBER_AND_VERSION = MAGIC_NUMBER | VERSION + + CRT0_DEFAULT_PATH = "./crt0/" + + EXPORTED_SYMBOL_START = 'start' + EXPORTED_SYMBOL_ENTRYPOINT = EXPORTED_SYMBOL_START + EXPORTED_SYMBOL_ROM_RAM_SIZE = '__rom_ram_size' + EXPORTED_SYMBOL_DATA_SIZE = EXPORTED_SYMBOL_ROM_RAM_SIZE + EXPORTED_SYMBOL_ROM_SIZE = '__rom_size' + EXPORTED_SYMBOL_TEXT_SIZE = EXPORTED_SYMBOL_ROM_SIZE + EXPORTED_SYMBOL_GOT_SIZE = '__got_size' + EXPORTED_SYMBOL_RAM_SIZE = '__ram_size' + EXPORTED_SYMBOL_BSS_SIZE = EXPORTED_SYMBOL_RAM_SIZE + + EXPORTED_SYMBOLS = [ + EXPORTED_SYMBOL_START, + EXPORTED_SYMBOL_ROM_RAM_SIZE, + EXPORTED_SYMBOL_ROM_SIZE, + EXPORTED_SYMBOL_GOT_SIZE, + EXPORTED_SYMBOL_RAM_SIZE + ] + + + EXPORTED_RELOCATION_TABLES = [ '.rel.rom.ram' ] + PARTITION_NAME = 'partition.fae' + + MAKE = 'make' + RM = 'rm' + + GDBINIT_FILENAME = 'gdbinit' + + + BINARY_SIZE_BYTESIZE = 4 + RELOCATION_ENTRIES_COUNT_BYTESIZE = 4 + CRT0_SIZE_BYTESIZE = 4 + ENTRY_POINT_BYTESIZE = 4 + ROM_RAM_SIZE_BYTESIZE = 4 + ROM_SIZE_BYTESIZE = 4 + GOT_SIZE_BYTESIZE = 4 + RAM_SIZE_BYTESIZE = 4 + MAGIC_NUMBER_AND_VERSION_BYTESIZE = 4 + + FOOTER_BYTESIZE = \ + RAM_SIZE_BYTESIZE +\ + GOT_SIZE_BYTESIZE +\ + ROM_SIZE_BYTESIZE +\ + ROM_RAM_SIZE_BYTESIZE +\ + ENTRY_POINT_BYTESIZE +\ + CRT0_SIZE_BYTESIZE +\ + MAGIC_NUMBER_AND_VERSION_BYTESIZE + + # Offsets from the end of the file. + FOOTER_RAM_SIZE_OFFSET = -28 + FOOTER_BSS_SIZE_OFFSET = FOOTER_RAM_SIZE_OFFSET + FOOTER_GOT_SIZE_OFFSET = -24 + FOOTER_ROM_SIZE_OFFSET = -20 + FOOTER_TEXT_SIZE_OFFSET = FOOTER_ROM_SIZE_OFFSET + FOOTER_ROM_RAM_SIZE_OFFSET = -16 + FOOTER_DATA_SIZE_OFFSET = FOOTER_ROM_RAM_SIZE_OFFSET + FOOTER_ENTRYPOINT_OFFSET = -12 + FOOTER_CRT0_OFFSET = -8 + FOOTER_MAGIC_NUMBER_AND_VERSION_OFFSET = -4 + + MINIMAL_BYTESIZE = \ + BINARY_SIZE_BYTESIZE + \ + RELOCATION_ENTRIES_COUNT_BYTESIZE + \ + FOOTER_BYTESIZE + + + # The default value used for padding. This value corresponds to + # the default state of non-volatile NAND flash memories + PADDING_VALUE = b'\xff' + + # The binary size must be a multiple of this value. + # It corresponds to the minimum alignment required by the MPU of + # the ARMv7-M architecture + PADDING_MPU_ALIGNMENT = 32 diff --git a/fae_utils/read_fae.py b/fae_utils/read_fae.py new file mode 100755 index 0000000..1a7274f --- /dev/null +++ b/fae_utils/read_fae.py @@ -0,0 +1,357 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2025 Université de Lille +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import sys +import tempfile +import subprocess + +from constants import FAEConstants + +def die(message): + """Print error message and exit""" + print(f'\033[91;1m{sys.argv[0]} : {message}\033[0m', file=sys.stderr) + sys.exit(1) + +def die_on_invalid_file(message) : + die(f'Invalid file {sys.argv[1]} : {message}') + +def die_on_negative_value_from_file(message) : + die(f'negative value : {message}') + +def check_negative_value_from_file(message, value): + if value < 0: + die_on_negative_value_from_file(message) + +def check_less_than_binary_size(label, value, binary_size): + if value >= binary_size: + die_on_invalid_file(f'{label} ({crt0_size}) is greater than or equal to binary size') + +def check_value_from_file(label, value, binary_size): + check_negative_value_from_file(label, value) + check_less_than_binary_size(label, value, binary_size) + + +def get_word_from_memoryview(from_memoryview, start_index_in_bytes): + '''Generic word (four bytes) memoryview getter according to endianness. + Supports negative value for start_index_in_bytes''' + if start_index_in_bytes < 0 and (start_index_in_bytes + 4 == 0): + this_memoryview = from_memoryview[start_index_in_bytes:] + else: + this_memoryview = from_memoryview[ \ + start_index_in_bytes:start_index_in_bytes + 4] + these_bytes = this_memoryview.tobytes() + this_word = int.from_bytes(these_bytes, byteorder=FAEConstants.ENDIANNESS) + + return this_word + + +def get_bytes_text_suffix(value): + return 'bytes' if value > 1 else 'byte' + +def print_bytesize(label, value): + '''Prints the bytesize of a label and a corresponding value''' + bytes_text_suffix = get_bytes_text_suffix(value) + print(f'{label} : {value} {bytes_text_suffix}') + +def print_chunk(chunk_name, chunk_start, chunk_end, chunk_size): + '''Prints a memory chunk as a triplet [start,end)(size)''' + print(f'- {chunk_name} : ' + f'[start : @{chunk_start} {get_bytes_text_suffix(chunk_start)} , ' + f'end : @{chunk_end} {get_bytes_text_suffix(chunk_end)}] ' + f'(size : {chunk_size} {get_bytes_text_suffix(chunk_end)})') + +def print_disassembly(label, fae_memory, start, end): + '''Prints code disassembly for bytes in fae_memory[start:end]''' + with tempfile.TemporaryDirectory() as tmp_directory_name: + filename = tmp_directory_name + "/" + label + with open(filename, "wb") as this_file: + this_memoryview = fae_memoryview[start:end] + these_bytes = this_memoryview.tobytes() + this_file.write(these_bytes) + + objdump_args = FAEConstants.OBJDUMP_ARGS_BASE + filename + result = subprocess.run(objdump_args, shell=True, capture_output=True, text=True) + if result.returncode != 0: + die(f'Failed to disassemble {label}') + print(result.stdout) + +def print_hexdump_line(line_number, a_memoryview, start_index_in_bytes, bytescount): + line_number_string = f'{hex(line_number)}'[2:] + line_number_string = line_number_string.rjust(8, '0') + + if bytescount > 16: + this_bytes_count = 16 + these_bytes = a_memoryview.obj[start_index_in_bytes:start_index_in_bytes + 16] + a = these_bytes[0:8].hex(' ',1) + b = these_bytes[8:16].hex(' ',1) + elif bytescount > 8: + this_bytes_count = bytescount + these_bytes = a_memoryview.obj[start_index_in_bytes:start_index_in_bytes + bytescount] + a = these_bytes[0:8].hex(' ',1) + b = these_bytes[8:8+(bytescount - 8)] + b = b.hex(' ', 1).ljust(23, ' ') + else : + this_bytes_count = bytescount + these_bytes = a_memoryview.obj[start_index_in_bytes:start_index_in_bytes + bytescount] + a = these_bytes[0:bytescount].hex(' ',1) + b = ' ' + + c = '|' + for i in range(this_bytes_count): + this_byte = these_bytes[i:i+1] + if this_byte.isascii(): + d = this_byte.decode("ascii") + c += d if d.isprintable() else '.' + else: + c += '.' + c = c.ljust(17) + c += '|' + + print(f'{line_number_string} {a} {b} {c}') + +def print_hexdump(fae_memoryview): + size = len(fae_memoryview) + nb_blocks_of_16_bytes = size // 16 + remaining = size % 16 + + start = 0 + for i in range(nb_blocks_of_16_bytes): + start = i * 16 + print_hexdump_line(start, fae_memoryview, start, size) + size -= 16 + + if size > 0: + print_hexdump_line(start, fae_memoryview, start, size) + + +CLI_OPTION_DISASSEMBLE_CRT0 = "-Dcrt0" +CLI_OPTION_HEXDUMP_CRT0 = "-Hcrt0" + +CLI_OPTION_HEXDUMP_RELOCATIONS = "-Hreloc" + +CLI_OPTION_HEXDUMP_ROM_RAM = "-Hromram" + +CLI_OPTION_DISASSEMBLE_TEXT = "-Drom" +CLI_OPTION_HEXDUMP_TEXT = "-Hrom" + +CLI_OPTION_HEXDUMP_GOT = "-Hgot" + +CLI_OPTION_VERBOSE = "-v" + +CLI_OPTIONS = { \ + CLI_OPTION_DISASSEMBLE_CRT0 : 'will disassemble CRT0', \ + CLI_OPTION_HEXDUMP_CRT0 : 'will hexdump CRT0', \ + CLI_OPTION_HEXDUMP_RELOCATIONS : 'will hexdump relocation table', \ + CLI_OPTION_HEXDUMP_ROM_RAM : 'will hexdump rom_ram section aka data', \ + CLI_OPTION_DISASSEMBLE_TEXT : 'will disassemble rom section aka text', \ + CLI_OPTION_HEXDUMP_TEXT : 'will hexdump rom section aka text', \ + CLI_OPTION_HEXDUMP_GOT : "will hexdump GOT", \ + CLI_OPTION_VERBOSE : "will enable all options" +} + +def usage(): + """Print how to to use the script and exit""" + summary = f'usage: {sys.argv[0]} [' + separator = '' + for k,v in CLI_OPTIONS.items(): + summary += separator + k + separator = ", " + summary += "] FAEFilename" + print(summary) + print('') + for k,v in CLI_OPTIONS.items(): + print(f'-{k} : {v}') + sys.exit(1) + +def parse_options(): + argc = len(sys.argv) + if argc < 2: + usage() + if sys.argv[argc - 1].startswith('-'): + print("Last command line argument cannot be an option.") + print("Last command line argument must be a FAE filename.") + usage() + + options = set() + for i in range(1, argc - 1): + if sys.argv[i] in CLI_OPTIONS: + if sys.argv[i] != CLI_OPTION_VERBOSE: + options.add(sys.argv[i]) + else: + for k,v in CLI_OPTIONS.items(): + if k != CLI_OPTION_VERBOSE: + options.add(k) + else: + print(f'{sys.argv[i]} is not a recognized command line option') + usage() + + return options + + +if __name__ == '__main__': + + options = parse_options() + + fae_bytearray = bytearray() + with open(sys.argv[len(sys.argv) - 1], "rb") as fae_file: + fae_bytearray += bytearray(fae_file.read()) + + # Binary size + binary_size = len(fae_bytearray) + print_bytesize('- Binary size', binary_size) + + if binary_size == 0: + die_on_invalid_file('Empty file') + + check_negative_value_from_file('Binary size', binary_size) + + if binary_size <= FAEConstants.MINIMAL_BYTESIZE: + die_on_invalid_file(f'Binary size is less than or equal to ' + f'minimal bytesize ({FAEConstants.MINIMAL_BYTESIZE})') + + fae_memoryview = memoryview(fae_bytearray) + + # 0 : Magic number and version + magic_number_and_version = get_word_from_memoryview( + fae_memoryview, FAEConstants.FOOTER_MAGIC_NUMBER_AND_VERSION_OFFSET) + + print(f'- Magic Number And Version : {hex(magic_number_and_version)}') + + check_negative_value_from_file('Magic Number And Version', \ + magic_number_and_version) + if magic_number_and_version < FAEConstants.MAGIC_NUMBER: + die_on_invalid_file(f'invalid magic number and version : ' + f'{magic_number_and_version}') + version = magic_number_and_version - FAEConstants.MAGIC_NUMBER + if version != FAEConstants.VERSION: + die_on_invalid_file(f'version not supported : {version}') + + # 1 : CRT0size + crt0_size = get_word_from_memoryview( + fae_memoryview, FAEConstants.FOOTER_CRT0_OFFSET) + + check_value_from_file('CRT0_size', crt0_size, binary_size) + if crt0_size == 0: + die_on_invalid_file(f'CRT0_size is equal to 0') + + print_chunk('CRT0', 0, crt0_size - 1, crt0_size) + if CLI_OPTION_DISASSEMBLE_CRT0 in options: + print_disassembly('CRT0', fae_memoryview, 0, crt0_size - 1) + if CLI_OPTION_HEXDUMP_CRT0 in options: + this_bytearray = fae_memoryview.obj[0:crt0_size] + print_hexdump(memoryview(this_bytearray)) + + # Binary size hosted within the file + binary_size_from_file = get_word_from_memoryview(fae_memoryview, crt0_size) + if binary_size_from_file != binary_size: + die_on_invalid_file(f'binary_size_from_file ({binary_size_from_file}' \ + ') is different from binary size') + + # + 4 is for the hosted binary size + iterator = crt0_size + 4 + + # Relocations + relocation_entries_start = iterator + relocation_entries_count = get_word_from_memoryview( + fae_memoryview, relocation_entries_start) + check_negative_value_from_file( 'relocations entries count', \ + relocation_entries_count ) + relocation_entries_size = relocation_entries_count * 4 + check_less_than_binary_size( 'relocation entries size', \ + relocation_entries_size, binary_size) + if relocation_entries_size > 0: + relocation_entries_end = relocation_entries_start + \ + relocation_entries_size - 1 + print_chunk('Relocation entries', \ + relocation_entries_start, \ + relocation_entries_end, \ + relocation_entries_size) + print(f'\t- Count : {relocation_entries_count}') + if CLI_OPTION_HEXDUMP_RELOCATIONS in options: + this_bytearray = fae_memoryview.obj[ \ + relocation_entries_start: \ + relocation_entries_start + relocation_entries_size] + print_hexdump(memoryview(this_bytearray)) + + iterator = relocation_entries_end + 1 + else: + print('- Relocation entries : none.') + + + # 2 : Entrypoint + entrypoint_offset = get_word_from_memoryview( + fae_memoryview, FAEConstants.FOOTER_ENTRYPOINT_OFFSET) + print_bytesize('- Entrypoint offset in .rom', entrypoint_offset) + check_value_from_file('Entrypoint offset', entrypoint_offset, binary_size) + + # 3 : Rom ram size aka data size + rom_ram_size = get_word_from_memoryview( + fae_memoryview, FAEConstants.FOOTER_ROM_RAM_SIZE_OFFSET) + check_value_from_file('rom_ram_size', rom_ram_size, binary_size) + + rom_ram_start = iterator + if rom_ram_size > 0: + rom_ram_end = rom_ram_start + rom_ram_size - 1 + + print_chunk('.rom.ram', rom_ram_start, rom_ram_end, rom_ram_size) + if CLI_OPTION_HEXDUMP_ROM_RAM in options: + this_bytearray = fae_memoryview.obj[rom_ram_start:rom_ram_end + 1] + print_hexdump(memoryview(this_bytearray)) + + iterator = rom_ram_end + 1 + else: + print('- .rom.ram : none.') + + # 4 : Rom size aka text size + rom_size = get_word_from_memoryview( + fae_memoryview, FAEConstants.FOOTER_ROM_SIZE_OFFSET) + check_value_from_file('rom_size', rom_size, binary_size) + + rom_start = iterator + if rom_size > 0: + rom_end = rom_start + rom_size - 1 + + print_chunk('.rom', rom_start, rom_end, rom_size) + if CLI_OPTION_DISASSEMBLE_TEXT in options: + print_disassembly('.rom', fae_memoryview, rom_start, rom_end) + if CLI_OPTION_HEXDUMP_TEXT in options: + this_bytearray = fae_memoryview.obj[rom_start:rom_end + 1] + print_hexdump(memoryview(this_bytearray)) + + if entrypoint_offset > rom_size: + die_on_invalid_file( \ + 'Entrypoint offset is out of .rom aka text section') + + iterator = rom_end + 1 + else: + print('- .rom : none.') + + # 5 : GOT size + got_size = get_word_from_memoryview( + fae_memoryview, FAEConstants.FOOTER_GOT_SIZE_OFFSET) + check_value_from_file('got size', got_size, binary_size) + + got_start = rom_end + 1 + if got_size > 0: + got_end = got_start + got_size - 1 + print_chunk(".got", got_start, got_end, got_size) + if CLI_OPTION_HEXDUMP_GOT in options: + this_bytearray = fae_memoryview.obj[got_start:got_end + 1] + print_hexdump(memoryview(this_bytearray)) + else: + print('.got : none.') + + + # 6 : Ram size aka BSS size + ram_size = get_word_from_memoryview( + fae_memoryview, FAEConstants.FOOTER_RAM_SIZE_OFFSET) + check_negative_value_from_file("RAM size", ram_size) + + print_bytesize(".bss", ram_size) + + print("- Integrity check : OK") diff --git a/main.c b/main.c index c3f2dad..d69e4fd 100644 --- a/main.c +++ b/main.c @@ -1,64 +1,47 @@ -/* - * Copyright (C) 2024 Université de Lille - * - * This file is subject to the terms and conditions of the GNU Lesser - * General Public License v2.1. See the file LICENSE in the top level - * directory for more details. - */ - -/** - * @ingroup examples - * @{ - * - * @file - * @brief Example of a post-issuance software - * - * @author Damien Amara <damien.amara@univ-lille.fr> - * - * @} - */ - -#include <stdriot.h> - -typedef int (*func_ptr_t)(void); - -int func_a(void) { - //printf("func_a has been assigned to "); - return 23; -} - -int func_b(void) { - //printf("func_b has been assigned to "); - return 32; -} - -volatile func_ptr_t func_ptr_extern_1 = func_a; -const int cst = 3; +/*******************************************************************************/ +/* © Université de Lille, The Pip Development Team (2015-2024) */ +/* */ +/* This software is a computer program whose purpose is to run a minimal, */ +/* hypervisor relying on proven properties such as memory isolation. */ +/* */ +/* This software is governed by the CeCILL license under French law and */ +/* abiding by the rules of distribution of free software. You can use, */ +/* modify and/ or redistribute the software under the terms of the CeCILL */ +/* license as circulated by CEA, CNRS and INRIA at the following URL */ +/* "http://www.cecill.info". */ +/* */ +/* As a counterpart to the access to the source code and rights to copy, */ +/* modify and redistribute granted by the license, users are provided only */ +/* with a limited warranty and the software's author, the holder of the */ +/* economic rights, and the successive licensors have only limited */ +/* liability. */ +/* */ +/* In this respect, the user's attention is drawn to the risks associated */ +/* with loading, using, modifying and/or developing or reproducing the */ +/* software by the user in light of its specific status of free software, */ +/* that may mean that it is complicated to manipulate, and that also */ +/* therefore means that it is reserved for developers and experienced */ +/* professionals having in-depth computer knowledge. Users are therefore */ +/* encouraged to load and test the software's suitability as regards their */ +/* requirements in conditions enabling the security of their systems and/or */ +/* data to be ensured and, more generally, to use and operate it in the */ +/* same conditions as regards security. */ +/* */ +/* The fact that you are presently reading this means that you have had */ +/* knowledge of the CeCILL license and that you accept its terms. */ +/*******************************************************************************/ + +#include "stdriot.h" int main(int argc, char **argv) { int i; -/* - printf("Hello World!\n"); -*/ - for (i = 1; i < argc; i++) { - printf("arg %d : %s\n", i, argv[i]); - } + printf("Hello World!\n"); - if (argc > 1) { - func_ptr_extern_1 = func_b; + for (i = 1; i < argc; i++) { + printf("%s\n", argv[i]); } - int v = func_ptr_extern_1(); - char *string = "hello world !"; - printf("@string = %p\n", string); - - printf("@&string = %p\n", &string); - printf("@&main = %p\n", &main); - - printf("&cst = %p\n", &cst); - printf("cst = %d\n", cst); - - return v; + return 0; } diff --git a/stdriot/stdriot.c b/stdriot/stdriot.c index abd5e0f..231405b 100644 --- a/stdriot/stdriot.c +++ b/stdriot/stdriot.c @@ -31,260 +31,378 @@ /* knowledge of the CeCILL license and that you accept its terms. */ /*******************************************************************************/ +/** + * @file stdriot.c + * + * This file is the counterpart of xipfs definitions, such + * as exec_ctx_t or syscall_index_t. + * + * @warning THIS FILE MUST REMAIN SYNCHRONIZED with xipfs, otherwise crashes and UB are to + * be expected. + */ + #include <stdarg.h> -#include <stddef.h> #include "stdriot.h" -enum syscall_index_e { - PIP, - EXIT, - PRINTF, - GET_TEMP, - ISPRINT, - STRTOL, -}; - -typedef int (*exit_t)(int status); -typedef int (*vprintf_t)(const char *format, va_list ap); -typedef int (*get_temp_t)(void); -typedef int (*isprint_t)(int character); -typedef long (*strtol_t)(const char *str, char **endptr, int base); - -extern int main(int argc, char **argv); - -static void **syscall_prev_got = NULL; -static void **syscall_curr_got = NULL; -static void **syscall_table = NULL; -static int syscall_is_init = 0; - -static inline void -_set_sl(volatile void *val) -{ - __asm__ volatile ("mov sl, %0" : : "r" (val) :); -} +/** + * @warning The order of the members in the enumeration must + * remain synchronized with the order of the members of the same + * enumeration declared in caller site (xipfs.h's one). + * + * @brief An enumeration describing the index of functions. + */ +typedef enum xipfs_user_syscall_e { + XIPFS_USER_SYSCALL_PRINTF, + XIPFS_USER_SYSCALL_GET_TEMP, + XIPFS_USER_SYSCALL_ISPRINT, + XIPFS_USER_SYSCALL_STRTOL, + XIPFS_USER_SYSCALL_GET_LED, + XIPFS_USER_SYSCALL_SET_LED, + XIPFS_USER_SYSCALL_COPY_FILE, + XIPFS_USER_SYSCALL_GET_FILE_SIZE, + XIPFS_USER_SYSCALL_MEMSET, + XIPFS_USER_SYSCALL_MAX +} xipfs_user_syscall_t; -extern void -syscall_init(void *prev_got, void *curr_got, void **table) -{ - syscall_prev_got = prev_got; - syscall_curr_got = curr_got; - syscall_table = table; - syscall_is_init = 1; -} +typedef int (*xipfs_user_syscall_vprintf_t)(const char *format, va_list ap); +typedef int (*xipfs_user_syscall_get_temp_t)(void); +typedef int (*xipfs_user_syscall_isprint_t)(int character); +typedef long (*xipfs_user_syscall_strtol_t)( + const char *str, char **endptr, int base); +typedef int (*xipfs_user_syscall_get_led_t)(int pos); +typedef int (*xipfs_user_syscall_set_led_t)(int pos, int val); +typedef ssize_t (*xipfs_user_syscall_copy_file_t)( + const char *name, void *buf, size_t nbyte); +typedef int (*xipfs_user_syscall_get_file_size_t)( + const char *name, size_t *size); +typedef void *(*xipfs_user_syscall_memset_t)(void *m, int c, size_t n); + +/** + * @brief An enumeration describing the index of xipfs functions. + * + * @warning MUST REMAIN SYNCHRONIZED with xipfs file.c's one. + */ +typedef enum xipfs_syscall_e { + XIPFS_SYSCALL_EXIT, + XIPFS_SYSCALL_MAX +} xipfs_syscall_t; + +typedef int (*xipfs_syscall_exit_t)(int status); + +/** + * @internal + * + * @def SHELL_DEFAULT_BUFSIZE + * + * @brief Default shell buffer size (maximum line length shell + * can handle) + * + * @see sys/include/shell.h + */ +#define SHELL_DEFAULT_BUFSIZE 128 + +/** + * @internal + * + * @def XIPFS_FREE_RAM_SIZE + * + * @brief Amount of free RAM available for the relocatable + * binary to use + * + * @see sys/fs/xipfs/file.c + */ +#define XIPFS_FREE_RAM_SIZE (4096) + +/** + * @internal + * + * @def EXEC_STACKSIZE_DEFAULT + * + * @brief The default execution stack size of the binary + * + * @see sys/fs/xipfs/file.c + */ +#define EXEC_STACKSIZE_DEFAULT 1024 + +/** + * @internal + * + * @def EXEC_ARGC_MAX + * + * @brief The maximum number of arguments to pass to the binary + * + * @see sys/fs/xipfs/include/file.h + */ +#define EXEC_ARGC_MAX (SHELL_DEFAULT_BUFSIZE / 2) + +/** + * @internal + * + * @def PANIC + * + * @brief This macro handles fatal errors + */ +#define PANIC() for (;;); + +/* + * Internal structures + */ -extern void -exit(int status) +/** + * @internal + * + * @brief Data structure that describes the memory layout + * required by the CRT0 to execute the relocatable binary + * + * @see sys/fs/xipfs/file.c + */ +typedef struct crt0_ctx_s { + /** + * Start address of the binary in the NVM + */ + void *bin_base; + /** + * Start address of the available free RAM + */ + void *ram_start; + /** + * End address of the available free RAM + */ + void *ram_end; + /** + * Start address of the free NVM + */ + void *nvm_start; + /** + * End address of the free NVM + */ + void *nvm_end; +} crt0_ctx_t; + +/** + * @internal + * + * @brief Data structure that describes the execution context of + * a relocatable binary + * + * @warning MUST REMAIN SYNCHRONIZED with xipfs's file.c. + */ +typedef struct exec_ctx_s { + /** + * Data structure required by the CRT0 to execute the + * relocatable binary + */ + crt0_ctx_t crt0_ctx; + /** + * Reserved memory space in RAM for the stack to be used by + * the relocatable binary + */ + char stkbot[EXEC_STACKSIZE_DEFAULT-4]; + /** + * Last word of the stack indicating the top of the stack + */ + char stktop[4]; + /** + * Number of arguments passed to the relocatable binary + */ + int argc; + /** + * Arguments passed to the relocatable binary + */ + char *argv[EXEC_ARGC_MAX]; + + /** + * Table of function pointers for functions + * used by xipfs_format's CRT0 and/or stdriot. + * These functions are not meant to be shared with + * end users. + */ + const void **xipfs_syscall_table; + + /** + * Table of function pointers for the RIOT functions + * used by the relocatable binary + */ + const void **user_syscall_table; + /** + * Reserved memory space in RAM for the free RAM to be used + * by the relocatable binary + */ + char ram_start[XIPFS_FREE_RAM_SIZE-1]; + /** + * Last byte of the free RAM + */ + char ram_end; +} exec_ctx_t; + +/* + * Internal types + */ + +/* + * Global variable + */ + +/** + * @internal + * + * @brief A pointer to the xipfs syscall table + * + * @see sys/fs/xipfs/file.c + */ +static const void **xipfs_syscall_table; + +/** + * @internal + * + * @brief A pointer to the user syscall table + * + * @see sys/fs/xipfs/file.c + */ +static const void **user_syscall_table; + +/** + * @brief Wrapper that branches to the xipfs_exit(3) function + * + * @param status The exit status of the program + * + * @see sys/fs/xipfs/file.c + */ +static void exit(int status) { - volatile void *prev_got; - volatile exit_t func; - - if (syscall_table[PIP] == (void *)0) { - func = syscall_table[EXIT]; - prev_got = syscall_prev_got; - - _set_sl(prev_got); - (*func)(status); - } else { - __asm__ volatile - ( - "mov r0, #0\n" - "mov r1, %0\n" - "push {r0, r1}\n" - "mov r0, #0\n" - "mov r1, %1\n" - "mov r2, #0\n" - "mov r3, #1\n" - "mov r4, #1\n" - "svc #12\n" - /* UNREACHABLE */ - : - : "r" (status), - "r" (syscall_table[EXIT]) - : - ); - } + xipfs_syscall_exit_t func; + + /* No need to save the R10 register, which holds the address + * of the program's relocated GOT, since this register is + * callee-saved according to the ARM Architecture Procedure + * Call Standard, section 5.1.1 */ + func = xipfs_syscall_table[XIPFS_SYSCALL_EXIT]; + (*func)(status); } -extern int -printf(const char * format, ...) +/** + * @brief Wrapper that branches to the RIOT's printf(3) function + * + * @param format The formatted string to print + */ +extern int printf(const char * format, ...) { - volatile vprintf_t func; - volatile void *prev_got; - volatile void *curr_got; + xipfs_user_syscall_vprintf_t func; int res = 0; va_list ap; - if (syscall_table[PIP] == (void *)0) { - func = syscall_table[PRINTF]; - prev_got = syscall_prev_got; - curr_got = syscall_curr_got; - - va_start (ap, format); - _set_sl(prev_got); - res = (*func)(format, ap); - _set_sl(curr_got); - va_end (ap); - } else { - va_start (ap, format); - __asm__ volatile - ( - "mov r0, #1\n" - "mov r1, %1\n" - "mov r2, %2\n" - "push {r0, r1, r2}\n" - "mov r0, #0\n" - "mov r1, %3\n" - "mov r2, #0\n" - "mov r3, #1\n" - "mov r4, #1\n" - "svc #12\n" - "pop {%0}\n" - "add sp, sp, #8\n" - : "=r" (res) - : "r" (format), - "r" (ap), - "r" (syscall_table[PRINTF]) - : "r0", "r1", "r2", "r3", "r4" - ); - va_end (ap); - } + /* No need to save the R10 register, which holds the address + * of the program's relocated GOT, since this register is + * callee-saved according to the ARM Architecture Procedure + * Call Standard, section 5.1.1 */ + func = user_syscall_table[XIPFS_USER_SYSCALL_PRINTF]; + va_start(ap, format); + res = (*func)(format, ap); + va_end(ap); return res; } -extern int -get_temp(void) -{ - volatile get_temp_t func; - volatile void *prev_got; - volatile void *curr_got; - int res = 0; +extern int get_temp(void) { + xipfs_user_syscall_get_temp_t func; + int res; - if (syscall_table[PIP] == (void *)0) { - func = syscall_table[GET_TEMP]; - prev_got = syscall_prev_got; - curr_got = syscall_curr_got; - - _set_sl(prev_got); - res = (*func)(); - _set_sl(curr_got); - } else { - __asm__ volatile - ( - "mov r0, #2\n" - "push {r0}\n" - "mov r0, #0\n" - "mov r1, %1\n" - "mov r2, #0\n" - "mov r3, #1\n" - "mov r4, #1\n" - "svc #12\n" - "pop {%0}\n" - : "=r" (res) - : "r" (syscall_table[GET_TEMP]) - : "r0", "r1", "r2", "r3", "r4" - ); - } + func = user_syscall_table[XIPFS_USER_SYSCALL_GET_TEMP]; + res = (*func)(); + return res; +} + +extern int isprint(int character) { + xipfs_user_syscall_isprint_t func; + int res; + + func = user_syscall_table[XIPFS_USER_SYSCALL_ISPRINT]; + res = (*func)(character); return res; } -extern int -isprint(int character) -{ - volatile isprint_t func; - volatile void *prev_got; - volatile void *curr_got; - int res = 0; +extern long strtol(const char *str, char **endptr, int base) { + xipfs_user_syscall_strtol_t func; + long res; - if (syscall_table[PIP] == (void *)0) { - func = syscall_table[ISPRINT]; - prev_got = syscall_prev_got; - curr_got = syscall_curr_got; - - _set_sl(prev_got); - res = (*func)(character); - _set_sl(curr_got); - } else { - __asm__ volatile - ( - "mov r0, #3\n" - "mov r1, %1\n" - "push {r0, r1}\n" - "mov r0, #0\n" - "mov r1, %2\n" - "mov r2, #0\n" - "mov r3, #1\n" - "mov r4, #1\n" - "svc #12\n" - "pop {%0}\n" - "add sp, sp, #4\n" - : "=r" (res) - : "r" (character), - "r" (syscall_table[ISPRINT]) - : "r0", "r1", "r2", "r3", "r4" - ); - } + func = user_syscall_table[XIPFS_USER_SYSCALL_STRTOL]; + res = (*func)(str, endptr, base); + return res; +} + +extern int get_led(int pos) { + xipfs_user_syscall_get_led_t func; + int res; + func = user_syscall_table[XIPFS_USER_SYSCALL_GET_LED]; + res = (*func)(pos); return res; } -extern long -strtol(const char *str, char **endptr, int base) -{ - volatile strtol_t func; - volatile void *prev_got; - volatile void *curr_got; - int res = 0; +extern int set_led(int pos, int val) { + xipfs_user_syscall_set_led_t func; + int res; - if (syscall_table[PIP] == (void *)0) { - func = syscall_table[STRTOL]; - prev_got = syscall_prev_got; - curr_got = syscall_curr_got; - - _set_sl(prev_got); - res = (*func)(str, endptr, base); - _set_sl(curr_got); - } else { - __asm__ volatile - ( - "mov r0, #4\n" - "mov r1, %1\n" - "mov r2, %2\n" - "mov r3, %3\n" - "push {r0-r3}\n" - "mov r0, #0\n" - "mov r1, %4\n" - "mov r2, #0\n" - "mov r3, #1\n" - "mov r4, #1\n" - "svc #12\n" - "pop {%0}\n" - "add sp, sp, #12\n" - : "=r" (res) - : "r" (str), - "r" (endptr), - "r" (base), - "r" (syscall_table[STRTOL]) - : "r0", "r1", "r2", "r3", "r4" - ); - } + func = user_syscall_table[XIPFS_USER_SYSCALL_SET_LED]; + res = (*func)(pos, val); + return res; +} +extern ssize_t copy_file(const char *name, void *buf, size_t nbyte) { + xipfs_user_syscall_copy_file_t func; + ssize_t res; + + func = user_syscall_table[XIPFS_USER_SYSCALL_COPY_FILE]; + res = (*func)(name, buf, nbyte); + return res; +} + +extern int get_file_size(const char *name, size_t *size) { + xipfs_user_syscall_get_file_size_t func; + int res; + + func = user_syscall_table[XIPFS_USER_SYSCALL_GET_FILE_SIZE]; + res = (*func)(name, size); + return res; +} + +extern void *memset(void *m, int c, size_t n) { + xipfs_user_syscall_memset_t func; + void *res; + + func = user_syscall_table[XIPFS_USER_SYSCALL_MEMSET]; + res = (*func)(m, c, n); return res; } -extern int -start(interface_t *interface, void *gotAddr, - void *oldGotAddr, void **syscalls) +/** + * @internal + * + * @brief The function to which CRT0 branches after the + * executable has been relocated + */ +int start(exec_ctx_t *exec_ctx) { + int status, argc; char **argv; - int argc; - syscall_init(oldGotAddr, gotAddr, syscalls); + /* initialize syscall tables pointers */ + xipfs_syscall_table = exec_ctx->xipfs_syscall_table; + user_syscall_table = exec_ctx->user_syscall_table; + + /* initialize the arguments passed to the program */ + argc = exec_ctx->argc; + argv = exec_ctx->argv; + + /* branch to the main() function of the program */ + extern int main(int argc, char **argv); + status = main(argc, argv); - argc = (int)(((uint32_t *)interface->stackTop)[0]); - argv = (char **)&(((uint32_t *) interface->stackTop)[1]); + /* exit the program */ + exit(status); - return main(argc, argv); + /* should never be reached */ + PANIC(); } diff --git a/stdriot/stdriot.h b/stdriot/stdriot.h index 2008cd9..fad75e2 100644 --- a/stdriot/stdriot.h +++ b/stdriot/stdriot.h @@ -1,5 +1,5 @@ /*******************************************************************************/ -/* © Université de Lille, The Pip Development Team (2015-2025) */ +/* © Université de Lille, The Pip Development Team (2015-2024) */ /* */ /* This software is a computer program whose purpose is to run a minimal, */ /* hypervisor relying on proven properties such as memory isolation. */ @@ -35,529 +35,25 @@ #define STDRIOT_H #include <stdint.h> +#include <stddef.h> +#include <sys/types.h> -/*! - * \def VIDT_INTERRUPT_NUMBER - * - * \brief The number of interrupt for the nRF52832. - */ -#define VIDT_INTERRUPT_NUMBER 54 - -/*! - * \brief blockAttr structure - */ -typedef struct blockAttr -{ - uint32_t* blockentryaddr ; //!< Block's local id - uint32_t* blockstartaddr ; //!< Block's start address - uint32_t* blockendaddr ; //!< Block's end address - uint32_t read ; //!< Read permission bit - uint32_t write ; //!< Write permission bit - uint32_t exec ; //!< Exec permission bit - uint32_t accessible ; //!< Block accessible bit -}__attribute__((packed)) blockAttr_t; - -/*! - * \brief When the block is empty, the error flag is set to -1, - * otherwise contains a block's public attributes. - */ -typedef union blockOrError_t -{ - int32_t error ; //!< Error -1 for an empty block - blockAttr_t blockAttr ; //!< A block's publicly exposed attributes -}__attribute__((packed)) blockOrError; - -/*! - * \brief Structure representing the VIDT. - */ -typedef struct vidt_s -{ - uint32_t currentInterrupt; /*<! The last interrupt number. */ - void *contexts[VIDT_INTERRUPT_NUMBER]; /*<! Pointers to contexts to restore. */ -} vidt_t; - -static inline uint32_t -Pip_createPartition( - uint32_t *blockLocalId -) { - register uint32_t r0 __asm__("r0"); - - r0 = (uint32_t) blockLocalId; - - __asm__ volatile - ( - "svc #0" - : "+r" (r0) - : - : "memory" - ); - - return r0; -} - -static inline uint32_t* -Pip_cutMemoryBlock( - uint32_t *blockToCutLocalId, - uint32_t *cutAddr, - int32_t mpuRegionNb -) { - register uint32_t r0 __asm__("r0"); - register uint32_t r1 __asm__("r1"); - register uint32_t r2 __asm__("r2"); - - r0 = (uint32_t) blockToCutLocalId; - r1 = (uint32_t) cutAddr; - r2 = (uint32_t) mpuRegionNb; - - __asm__ volatile - ( - "svc #1" - : "+r" (r0) - : "r" (r1), - "r" (r2) - : "memory" - ); - - return (uint32_t *) r0; -} - -static inline uint32_t* -Pip_mergeMemoryBlocks( - uint32_t *blockToMerge1LocalId, - uint32_t *blockToMerge2LocalId, - int32_t mpuRegionNb -) { - register uint32_t r0 __asm__("r0"); - register uint32_t r1 __asm__("r1"); - register uint32_t r2 __asm__("r2"); - - r0 = (uint32_t) blockToMerge1LocalId; - r1 = (uint32_t) blockToMerge2LocalId; - r2 = (uint32_t) mpuRegionNb; - - __asm__ volatile - ( - "svc #2" - : "+r" (r0) - : "r" (r1), - "r" (r2) - : "memory" - ); - - return (uint32_t *) r0; -} - -static inline uint32_t -Pip_prepare( - uint32_t *partDescBlockId, - int32_t projectedSlotsNb, - uint32_t *requisitionedBlockLocalId -) { - register uint32_t r0 __asm__("r0"); - register uint32_t r1 __asm__("r1"); - register uint32_t r2 __asm__("r2"); - - r0 = (uint32_t) partDescBlockId; - r1 = (uint32_t) projectedSlotsNb; - r2 = (uint32_t) requisitionedBlockLocalId; - - __asm__ volatile - ( - "svc #3" - : "+r" (r0) - : "r" (r1), - "r" (r2) - : "memory" - ); - - return r0; -} - -static inline uint32_t* -Pip_addMemoryBlock( - uint32_t *childPartDescBlockLocalId, - uint32_t *blockToShareLocalId, - uint32_t r, - uint32_t w, - uint32_t e -) { - register uint32_t r0 __asm__("r0"); - register uint32_t r1 __asm__("r1"); - register uint32_t r2 __asm__("r2"); - - r0 = (uint32_t) childPartDescBlockLocalId; - r1 = (uint32_t) blockToShareLocalId; - r2 = ((r & 1) << 2) | ((w & 1) << 1) | (e & 1); - - __asm__ volatile - ( - "svc #4" - : "+r" (r0) - : "r" (r1), - "r" (r2) - : "memory" - ); - - return (uint32_t *) r0; -} - -static inline uint32_t -Pip_removeMemoryBlock( - uint32_t *blockToRemoveLocalId -) { - register uint32_t r0 __asm__("r0"); - - r0 = (uint32_t) blockToRemoveLocalId; - - __asm__ volatile - ( - "svc #5" - : "+r" (r0) - : - : "memory" - ); - - return r0; -} - -static inline uint32_t -Pip_deletePartition( - uint32_t *childPartDescBlockLocalId -) { - register uint32_t r0 __asm__("r0"); - - r0 = (uint32_t) childPartDescBlockLocalId; - - __asm__ volatile - ( - "svc #6" - : "+r" (r0) - : - : "memory" - ); - - return r0; -} - -static inline uint32_t* -Pip_collect( - uint32_t *partDescBlockId -) { - register uint32_t r0 __asm__("r0"); - - r0 = (uint32_t) partDescBlockId; - - __asm__ volatile - ( - "svc #7" - : "+r" (r0) - : - : "memory" - ); - - return (uint32_t *) r0; -} - -static inline uint32_t -Pip_mapMPU( - uint32_t *partDescBlockId, - uint32_t *blockToMapLocalId, - int32_t mpuRegionNb -) { - register uint32_t r0 __asm__("r0"); - register uint32_t r1 __asm__("r1"); - register uint32_t r2 __asm__("r2"); - - r0 = (uint32_t) partDescBlockId; - r1 = (uint32_t) blockToMapLocalId; - r2 = (uint32_t) mpuRegionNb; - - __asm__ volatile - ( - "svc #8" - : "+r" (r0) - : "r" (r1), - "r" (r2) - : "memory" - ); - - return r0; -} - -static inline uint32_t* -Pip_readMPU( - uint32_t *partDescBlockId, - int32_t mpuRegionNb -) { - register uint32_t r0 __asm__("r0"); - register uint32_t r1 __asm__("r1"); - - r0 = (uint32_t) partDescBlockId; - r1 = (uint32_t) mpuRegionNb; - - __asm__ volatile - ( - "svc #9" - : "+r" (r0) - : "r" (r1) - : "memory" - ); - - return (uint32_t *) r0; -} - -static inline int32_t -Pip_findBlock( - uint32_t *partDescBlockId, - uint32_t *addrInBlock, - blockOrError *blockAddr -) { - register uint32_t r0 __asm__("r0"); - register uint32_t r1 __asm__("r1"); - register uint32_t r2 __asm__("r2"); - - r0 = (uint32_t) partDescBlockId; - r1 = (uint32_t) addrInBlock; - r2 = (uint32_t) blockAddr; - - __asm__ volatile - ( - "svc #10" - : "+r" (r0) - : "r" (r1), - "r" (r2) - : "memory" - ); - - return (int32_t) r0; -} - -static inline uint32_t -Pip_setVIDT( - uint32_t *partDescBlockId, - uint32_t *vidtBlockLocalId -) { - register uint32_t r0 __asm__("r0"); - register uint32_t r1 __asm__("r1"); - - r0 = (uint32_t) partDescBlockId; - r1 = (uint32_t) vidtBlockLocalId; - - __asm__ volatile - ( - "svc #11" - : "+r" (r0) - : "r" (r1) - : "memory" - ); - - return r0; -} - -static inline uint32_t -Pip_yield( - uint32_t *calleePartDescBlockId, - uint32_t userTargetInterrupt, - uint32_t userCallerContextSaveIndex, - uint32_t flagsOnYield, - uint32_t flagsOnWake -) { - register uint32_t r0 __asm__("r0"); - register uint32_t r1 __asm__("r1"); - register uint32_t r2 __asm__("r2"); - register uint32_t r3 __asm__("r3"); - register uint32_t r4 __asm__("r4"); - - r0 = (uint32_t) calleePartDescBlockId; - r1 = userTargetInterrupt; - r2 = userCallerContextSaveIndex; - r3 = flagsOnYield; - r4 = flagsOnWake; - - __asm__ volatile - ( - "svc #12" - : "+r" (r0) - : "r" (r1), - "r" (r2), - "r" (r3), - "r" (r4) - : "memory" - ); - - return r0; -} - -static inline uint32_t -Pip_getIntState( - uint32_t *childPartDescBlockLocalId -) { - register uint32_t r0 __asm__("r0"); - - r0 = (uint32_t) childPartDescBlockLocalId; - - __asm__ volatile - ( - "svc #13" - : "+r" (r0) - : - : "memory" - ); - - return r0; -} - -static inline uint32_t -Pip_getSelfIntState(void) -{ - register uint32_t r0 __asm__("r0"); - - __asm__ volatile - ( - "svc #14" - : "=r" (r0) - : - : "memory" - ); - - return r0; -} - -static inline void -Pip_setIntState( - uint32_t interruptState -) { - register uint32_t r0 __asm__("r0"); - - r0 = interruptState; - - __asm__ volatile - ( - "svc #15" - : - : "r" (r0) - : "memory" - ); -} - -static inline uint32_t -Pip_in( - uint32_t registerId -) { - register uint32_t r0 __asm__("r0"); - register uint32_t r1 __asm__("r1"); - - r0 = registerId; - - __asm__ volatile - ( - "svc #16" - : "=r" (r1) - : "r" (r0) - : "memory" - ); - - return r1; -} - -static inline void -Pip_out( - uint32_t registerId, - uint32_t value -) { - register uint32_t r0 __asm__("r0"); - register uint32_t r1 __asm__("r1"); - - r0 = registerId; - r1 = value; - - __asm__ volatile - ( - "svc #17" - : - : "r" (r0), - "r" (r1) - : "memory" - ); -} - -/*! - * \brief This structure defines the interface that - * PIP provides to partitions. - */ -typedef struct interface_s -{ - /*! - * \brief The ID of the block containing the - * partition descriptor of the root - * partition. - */ - void *partDescBlockId; - - /*! - * \brief The limit address of the stack of - * the root partition. - */ - void *stackLimit; - - /*! - * \brief The stack top address of the root - * partition. - */ - void *stackTop; - - /*! - * \brief The VIDT start address of the root - * partition. - */ - void *vidtStart; - - /*! - * \brief The VIDT end address of the root - * partition. - */ - void *vidtEnd; - - /*! - * \brief The start address of the root - * partition binary. - */ - void *root; - - /*! - * \brief The start address of the unused - * ROM. - */ - void *unusedRomStart; - - /*! - * \brief The end address of the ROM. - */ - void *romEnd; - - /*! - * \brief The start address of the unused - * RAM. - */ - void *unusedRamStart; +extern int printf(const char * format, ...); - /*! - * \brief The end address of the RAM. - */ - void *ramEnd; +extern int get_temp(void); -} interface_t; +extern int isprint(int character); -extern void syscall_init(void *prev_got, void *curr_got, void **table); +extern long strtol(const char *str, char **endptr, int base); -extern void exit(int status); +extern int get_led(int pos); -extern int printf(const char * format, ...); +extern int set_led(int pos, int val); -extern int get_temp(void); +extern ssize_t copy_file(const char *name, void *buf, size_t nbyte); -extern int isprint(int character); +extern int get_file_size(const char *name, size_t *size); -extern long strtol(const char *str, char **endptr, int base); +extern void *memset(void *m, int c, size_t n); #endif /* STDRIOT_H */ -- GitLab