NameDateSize

..22-Jun-202015

CMakeLists.txtH A D13-Jul-202014.1 KiB

Findelfloader-tool.cmakeH A D09-Mar-2020491

helpers.cmakeH A D09-Mar-2020353

include/H09-Mar-202016

LICENSE_GPLv2.txtH A D25-Jul-201915.5 KiB

README.mdH A D09-Mar-20206.5 KiB

src/H09-Mar-202017

README.md

1<!--
2     Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3
4     SPDX-License-Identifier: GPL-2.0-only
5-->
6# Elfloader
7
8The elfloader is responsible for preparing the hardware for seL4 on ARM
9and RISC-V. It loads the kernel and user image from an embedded CPIO archive,
10initialises secondary cores (if SMP is enabled), and sets up an initial set of page
11tables for the kernel.
12
13## ARM
14
15On ARM platforms, the elfloader supports being booted in four ways: as a binary image,
16as a u-boot uImage, as an ELF file, and as an EFI executable. Each of these methods differs slightly.
17It can also provide seL4 with a DTB - either from the bootloader or included in the embedded CPIO archive.
18
191. (EFI only) Elfloader is entered at `_gnuefi_start` entry point.
202. (EFI only) Elfloader relocates itself
213. Elfloader `_start` is called. This is in `arch-arm/<arch_bitness>/crt0.S`.
224. Elfloader initialises the [driver framework](#driver-framework), which enables UART/printf.
235. Elfloader loads the kernel, user image, and DTB, determining where the kernel needs to be mapped in memory.
246. If the kernel window overlaps the elfloader's code:
25    * (AArch32 EFI only) the elfloader relocates itself.
26     See `relocate_below_kernel` for a detailed explanation of the relocation logic.
27    * (Other platforms) the elfloader aborts.
287. The elfloader resumes booting. If it relocated itself, it will re-initialise the driver model.
298. If the elfloader is in HYP mode but seL4 is not configured to support HYP, it will leave HYP mode.
309. The elfloader sets up the initial page tables for the kernel (see `init_hyp_boot_vspace` or `init_boot_vspace`).
3110. If SMP is enabled, the elfloader boots all secondary cores.
3211. The elfloader enables the MMU.
3312. The elfloader launches seL4, passing information about the user image and the DTB.
34
35### Binary
36
37The elfloader expects to be executed with a base address as generated by the `shoehorn` utility.
38You can determine the correct address for a given image by running
39```
40aarch64-linux-gnu-objdump -t elfloader/elfloader | grep _text
41```
42from the kernel build directory. The first field in the output contains the base address.
43
44On aarch64, the elfloader will try and move itself to the right address, however, this will fail
45if the load address and the correct address are too close, as the relocation code will be overwritten.
46
47It is also possible to override `shoehorn` and hardcode a load address by setting IMAGE_START_ADDR in CMake.
48
49### U-Boot
50
51The elfloader can be booted according to the Linux kernel's booting convention for ARM/ARM64.
52The DTB, if provided, will be passed to seL4 (which will then pass it to the root task).
53
54### ELF
55
56The elfloader supports being executed as an ELF image (via `bootelf` in U-Boot or similar).
57
58### EFI
59
60The elfloader integrates EFI support based on the `gnu-efi` project. It will relocate itself as appropriate,
61and supports loading a DTB from the EFI implementation.
62
63## RISC-V
64
65On RISC-V the elfloader is launched by the `bbl`, which is integrated in the seL4 build system.
66The `bbl` brings up secondary cores, and the elfloader uses SBI to provide a serial output on RISC-V.
67
68## Driver framework
69
70The elfloader provides a driver framework to reduce code duplication between platforms.
71Currently the driver framework is only used for UART output, however it is designed with extensibility in mind.
72In practice, this is currently only used on ARM, as RISC-V uses SBI for UART, and SBI has no device tree entries.
73However, in the future it may become useful on RISC-V.
74
75The driver framework uses a header file containing a list of devices generated by the `hardware_gen.py` utility
76included in seL4. Currently, this header only includes the UART specified by the `stdout-path` property in the DTB.
77Each device in the list has a compatible string (`compat`), and a list of addresses (`region_bases[]`) which correspond to the regions specified
78by the `reg` property in the DTB.
79
80Each driver in the elfloader has a list of compatible strings, matching those found in the device tree.
81For instance, the 8250 UART driver, used on Tegra and TI platforms has the following:
82
83```c
84static const struct dtb_match_table uart_8250_matches[] = {
85    { .compatible = "nvidia,tegra20-uart" },
86    { .compatible = "ti,omap3-uart" },
87    { .compatible = "snps,dw-apb-uart" },
88    { .compatible = NULL /* sentinel */ },
89};
90```
91
92Each driver also has a 'type'. Currently the only type supported is `DRIVER_UART`. The `type`
93indicates the type of struct that is found in the `ops` pointer of each driver object,
94and provides type-specific functionality.
95(For instance, UART drivers have a `elfloader_uart_ops` struct which contains a `putc` function).
96Finally, drivers also provide an `init` function, which is called when the driver is matched with a device,
97and can be used to perform device-specific setup (e.g. setting the device as the UART output).
98
99Finally, each driver has a `struct elfloader_driver` and a corresponding `ELFLOADER_DRIVER` statement.
100Taking the 8250 UART driver as an example again:
101
102```c
103static const struct elfloader_driver uart_8250 = {
104    .match_table = uart_8250_matches,
105    .type = DRIVER_UART,
106    .init = &uart_8250_init,
107    .ops = &uart_8250_ops,
108};
109
110ELFLOADER_DRIVER(uart_8250);
111```
112
113#### UART
114
115The driver framework provides a "default" (`__attribute__((weak))`) implementation of `plat_console_putchar`, which calls
116the `putc` function for the elfloader device provided to `uart_set_out` - discarding all characters
117that are given to it before `uart_set_out` is called. This can be overridden if you do not wish to use
118the driver framework (e.g. for very early debugging).
119
120
121
122## Porting the elfloader
123
124### To ARM
125
126Once a kernel port has been started (and a DTB provided), porting the elfloader to a platform is reasonably
127straightforward.
128
129Most platform-specific information is extracted from a DTB, including available physical memory ranges. If the
130platform uses a UART compatible with another platform, even the UART will work out of the box. In other cases,
131it might be necessary to add a new `dtb_match_table` entry to an existing driver, or add a new driver
132(which is fairly trivial - only the `match_table` and `putchar` functions from an existing driver would
133need to be changed).
134
135An appropriate image type needs to be selected. By default `ElfloaderImage` is set to `elf`, however,
136various platform-specific overrides exist and can be found in `ApplyData61ElfLoaderSettings` in this repo, at
137`cmake-tool/helpers/application_settings.cmake`.
138
139### To RISC-V
140
141TODO - it seems there's not actually that much that needs to be done on the elfloader side.
142