diff --git a/book.toml b/book.toml index 77157de..7f6ab09 100644 --- a/book.toml +++ b/book.toml @@ -5,13 +5,13 @@ multilingual = false src = "src" title = "rustBoot Documentation" -[preprocessor] +# [preprocessor] -[preprocessor.mermaid] -command = "mdbook-mermaid" +# [preprocessor.mermaid] +# command = "mdbook-mermaid" -[preprocessor.svgbob2] -font_family = "Lucida Console" +# [preprocessor.svgbob2] +# font_family = "Lucida Console" [output] @@ -19,5 +19,5 @@ font_family = "Lucida Console" additional-js = ["mermaid.min.js", "mermaid-init.js"] git-repository-url = "https://github.com/nihalpasham/rustBoot-book" git-repository-icon = "fa-github" -default-theme = "rust" -preferred-dark-theme = "navy" \ No newline at end of file +default-theme = "light" +preferred-dark-theme = "ayu" \ No newline at end of file diff --git a/ci/spellcheck.sh b/ci/spellcheck.sh old mode 100644 new mode 100755 diff --git a/src/arch/images.md b/src/arch/images.md index 66ab4e7..d523f5f 100644 --- a/src/arch/images.md +++ b/src/arch/images.md @@ -1,12 +1,17 @@ # rustBoot Images -rustBoot images comprise of a `256-byte header` pre-pended to a firmware binary and are deliberately designed to be as simple as possible. +rustBoot supports 2 types of firmware image formats, depending on the underlying device. It could either be an +- [`mcu-image:`](./images.md#mcu-image-format) a simple 256-byte firmware image format for microcontrollers or a +- [`fit-image:`](./images.md#fit-image-format) the flattened-image-tree format for systems capable of booting linux. -- it does not rely on the use of complex digital certificate formats to [`reduce its TCB`](../index.md#trusted-computing-base) and avoids unnecessary [code-complexity](../index.md#complexity--boot-time) +## MCU image format +rustBoot mcu-images comprise of a `256-byte header` pre-pended to a firmware binary and are deliberately designed to be as simple as possible. + +- it does not rely on the use of complex digital certificate formats which keeps the [`TCB`](../index.md#trusted-computing-base) small and avoids unnecessary [code-complexity](../index.md#complexity--boot-time) [![rustBoot_header](https://github.com/imrank03/rustBoot-book-diagrams/blob/main/rustBoot_header.svg?raw=true "Simplified Block Diagram, 256 byte rustBoot header")](https://github.com/imrank03/rustBoot-book-diagrams/blob/main/rustBoot_header.svg?raw=true) -### rustBoot Image header layout: +### rustBoot image header layout: The header always starts with a 4-byte magic number, followed by a 4-byte field indicating the size of the firmware image (excluding the header). All header contents are stored in little-endian format. @@ -16,7 +21,7 @@ The 2 (`magic and size`) fixed fields are followed by one or more `TLV(s) or Typ - **Length:** 2 bytes to indicate the `length in bytes` of the tag (excluding the type and size bytes). - **Value:** N bytes of tag content -### Padding and End of Header bytes: +### Padding and End of header bytes: - An `0xFF` byte in the `Type` field indicates a padding byte. A 'padding' byte does *NOT* have a size field, and the next byte is interpreted as `Type` again. - A 2 byte value of `0x0000` signals the end of the rustBoot header. @@ -42,16 +47,249 @@ Each tag represents some information about the firmware. `rustBoot` requires the - Length: 64 bytes -### Optional Tags: +### Optional tags: - **Pubkey Hint:** A `pubkey hint digest` tag can be included in the header. - Type: `0x1000` - Length: 32 bytes - This tag contains the SHA256 digest of the public key of the corresponding private-key used by the signing tool. The bootloader may use this field to locate the correct public key in case multiple keys are available. -> **rustBoot Defaults:** +> **MCU defaults:** > - By default, a valid rustBoot image is always signed. > - It relies on the 256-byte header for firmware validation. > - It will fail to boot an image - - if it does not possess a [`valid rustBoot header`](images.md#rustboot-images) or - - if it isn't signed or if it cannot be verified using the included the [`authentication-type`](images.md#tags). \ No newline at end of file +> - if it does not possess a [`valid rustBoot header`](images.md#rustboot-images) or +> - if it isn't signed or if it cannot be verified using the included the [`authentication-type`](images.md#tags). + +## FIT-image format +rustBoot leverages Uboot's [`flattened-uImage-tree`](https://raw.githubusercontent.com/u-boot/u-boot/master/doc/uImage.FIT/howto.txt) format when booting the linux kernel. + +The FIT format is essentially an extension of the [`device-tree`](https://github.com/devicetree-org/devicetree-specification/releases/tag/v0.4-rc1) format. FIT allows us to combine multiple binaries such as the kernel, ramdisk, device-tree-blob etc. into a single image. + +A typical rustBoot fit-image contains 4 items in the following order +-   kernel +-   fdt +-   initrd +-   rbconfig + +**Here's an example fit-image source file :** It is also referred to as an image-tree source file or `.its` file. + +```json +/dts-v1/; + +/ { + description = "rustBoot FIT Image"; + #address-cells = <1>; + + images { + kernel { + description = "Kernel"; + data = /incbin/("vmlinuz"); + type = "kernel"; + arch = "arm64"; + os = "linux"; + compression = "none"; + load = <0x40480000>; + entry = <0x40480000>; + hash { + algo = "sha256"; + }; + }; + fdt { + description = "DTB"; + data = /incbin/("unpatched-bcm2711-rpi-4-b.dtb"); + type = "flat_dt"; + arch = "arm64"; + compression = "none"; + load = <0x43000000>; + entry = <0x43000000>; + hash { + algo = "sha256"; + }; + }; + initrd { + description = "Initrd"; + data = /incbin/("initramfs"); + type = "ramdisk"; + arch = "arm64"; + os = "linux"; + compression = "none"; + hash { + algo = "sha256"; + }; + }; + rbconfig { + description = "rustBoot Config"; + data = /incbin/("rbconfig.txt"); + type = "rustBoot cmdline config"; + arch = "none"; + os = "linux"; + compression = "none"; + hash { + algo = "sha256"; + }; + }; + }; + + configurations { + default = "bootconfig"; + bootconfig { + description = "Boot Config"; + kernel = "kernel"; + fdt = "fdt"; + ramdisk = "initrd"; + rbconfig = "rbconfig"; + signature@1 { + algo = "sha256,ecdsa256,nistp256"; + key-name-hint = "dev"; + signed-images = "fdt", "kernel", "ramdisk", "rbconfig"; + value = ""; + }; + }; + }; + +}; +``` +The default configuration of an `.its` file determines which kernel, initrd, fdt and rbconfig is to be used for booting. In the above example, `bootconfig` is our default configuration. + +> rustBoot's FIT parser will select the corresponding kernel, fdt, initrd and rbconfig associated with `bootconfig` for booting + +### Building a rustBoot compliant fit-image: +As shown in the example above, a rustBoot compliant fit-image contains 4 items - + +- `kernel` - the linux kernel +- `fdt` - the flattened device tree or device tree blob +- `ramdisk`- a root filesystem that is embedded into the kernel and loaded at an early stage of the boot process. It is the successor of initrd. It can do things the kernel can't easily do by itself during the boot process. For example: customize the boot process (e.g., print a welcome message) +- `rbconfig` - this is rustBoot's kernel configuraton. A simple `txt` file to add kernel command-line arguments. + +You can retrieve the first 3 (i.e. kernel, fdt, ramdisk) from a pre-built OS image: +- Maintainers of a particular linux distribution provide pre-built OS images. These images usually contain several partitions such as - + - `boot:` contains the bootloader, kernel, dtb, ramdisk and other stuff + - `system:` contains the root file system + - `others:` may contain other partitions for things such as storage etc. +- simply download an OS image or a pre-built linux distribution from the maintainers website. + - In this example, I'll be using the `apertis` distribution. +- it’s usually a compressed (zImage) format. Decompress it using a tool like unarchiver to get a disk image. +- use `partx --show` to list all partitions +```powershell +$ partx --show __linux_image_filepath__ +NR START END SECTORS SIZE NAME UUID + 1 8192 532479 524288 256M 9730496b-01 + 2 532480 3661823 3129344 1.5G 9730496b-02 +``` +In the above case, the first partition with a size of 256MB contains our boot-files. It's usually named `boot`. We can calculate the offset to `boot` volume/partition with the following command +```powershell +$ bc +bc 1.07.1 +Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006, 2008, 2012-2017 Free Software Foundation, Inc. +This is free software with ABSOLUTELY NO WARRANTY. +For details type `warranty'. +> 8192 * 512 +> 4194304 +> quit +``` +> `512` is the sector-size. We multiply sector-size with the sector offset to get the actual starting (byte) location of `boot`. + +mount the partition as an `ext4` file-system (or `fat` file-system, whichever) +``` +$ sudo mkdir /mnt/other +$ sudo mount -v -o offset=272629760 -t ext4 /_path_to_file_image/__filename__.img /mnt/other +mount: /dev/loop0 mounted on /mnt/other. + +Check mounted image + +$ ls /mnt/other +``` +Finally copy the `dtb`, `ramdisk` and `vmlinuz` image (i.e. kernel) from the mounted partition to a new folder. You can give it any name you want. I'll use `pkg` in this example. +> vmlinuz is a PE (portable executable) i.e. we can jump to it and it will in-turn jump to the kernel's entry point. + +After storing all 4 items in the `pkg` folder, you can build a fit-image by running the following commands. +- the `.its` file is the input to `mkimage` +- and the `.itb` filename is the generated fit-image, stored in the same `pkg` folder. + +**On a mac:** +```powershell +brew install u-boot-tools +``` +**On a linux machine:** +```powershell +sudo apt install u-boot-tools +``` +and then run + +```powershell +mkimage -f rpi4-apertis.its rpi4-test-apertis.itb + +Output: + +rpi4-apertis.its:65.37-70.6: Warning (unit_address_vs_reg): /configurations/bootconfig/signature@1: node has a unit name, but no reg or ranges property +Image contains unit addresses @, this will break signing +FIT description: rustBoot FIT Image +Created: Sat Jun 4 13:18:45 2022 + Image 0 (kernel) + Description: Kernel + Created: Sat Jun 4 13:18:45 2022 + Type: Kernel Image + Compression: uncompressed + Data Size: 29272576 Bytes = 28586.50 KiB = 27.92 MiB + Architecture: AArch64 + OS: Linux + Load Address: 0x40480000 + Entry Point: 0x40480000 + Hash algo: sha256 + Hash value: 97dcbff24ad0a60514e31a7a6b34a765681fea81f8dd11e4644f3ec81e1044fb + Image 1 (fdt) + Description: DTB + Created: Sat Jun 4 13:18:45 2022 + Type: Flat Device Tree + Compression: uncompressed + Data Size: 25713 Bytes = 25.11 KiB = 0.02 MiB + Architecture: AArch64 + Load Address: 0x43000000 + Hash algo: sha256 + Hash value: 3572783be74511b710ed7fca9b3131e97fd8073c620a94269a4e4ce79d331540 + Image 2 (initrd) + Description: Initrd + Created: Sat Jun 4 13:18:45 2022 + Type: RAMDisk Image + Compression: uncompressed + Data Size: 32901194 Bytes = 32130.07 KiB = 31.38 MiB + Architecture: AArch64 + OS: Linux + Load Address: unavailable + Entry Point: unavailable + Hash algo: sha256 + Hash value: f1290587e2155e3a5c2c870fa1d6e3e2252fb0dddf74992113d2ed86bc67f37c + Image 3 (rbconfig) + Description: rustBoot Config + Created: Sat Jun 4 13:18:45 2022 + Type: Unknown Image + Compression: uncompressed + Data Size: 141 Bytes = 0.14 KiB = 0.00 MiB + Hash algo: sha256 + Hash value: b16d058c4f09abdb8da98561f3a15d06ff271c38a4655c2be11dec23567fd519 + Default Configuration: 'bootconfig' + Configuration 0 (bootconfig) + Description: Boot Config + Kernel: kernel + Init Ramdisk: initrd + FDT: fdt + Sign algo: sha256,ecdsa256,nistp256:dev + Sign value: 00 + Timestamp: unavailable +``` +The `.itb` file is our fit-image. It does not contain a signature yet i.e. it is not signed. You'll notice `sign-value` field is empty. +### Signing fit-images +rustBoot fit-images are signed with `ecdsa256`. The signature includes the kernel, fdt, initrd and rbconfig. + +Signing a rustBoot fit-image involves 2 steps: +- **Building a fit-image:** As explained in [preceding section](./images.md#building-a-rustboot-compliant-fit-image), FIT images can be built using `mkimage` - a command-line utility from the `uboot-tools` package i.e. you can pass an `.its` file to the mkimage tool and mkimage will produce an `.itb` blob or a image-tree blob. +- **signing the fit-image:** once you've built your fit-image, you can pass the it along with a signing key to rustBoot's `rbsigner` utility to [generate a signed fit-image](./signing_utilities.md#signed-fit-image). + +> **FIT-image defaults:** +> - By default, valid rustBoot images are always signed. +> - It will fail to boot an image +> - if the image fails fit-validation i.e. if its not a properly formatted fit-image or if the fit-parser cant find the specified default config or its components. +> - if it isn't signed or if it cannot be verified using the included algo. +> - rustBoot's fit parser currently supports the following architectures +> - `Aarch64` \ No newline at end of file diff --git a/src/arch/partitions.md b/src/arch/partitions.md index b9b6345..a9487bb 100644 --- a/src/arch/partitions.md +++ b/src/arch/partitions.md @@ -1,10 +1,10 @@ # rustBoot Partitions -`rustBoot` offers 2 `update-types`, depending on the type of the underlying system. -- **micro-controller updates:** uses the concept of [`swappable flash partitions`](https://github.com/nihalpasham/rustBoot/issues/2) to update micro-controller firmware. +`rustBoot` has 2 distinct partioning schemes, depending on the type of the underlying system. +- [**micro-controller partitions:**](./partitions.md#micro-controller-partitions) uses the concept of [`swappable flash partitions`](https://github.com/nihalpasham/rustBoot/issues/2) to update micro-controller firmware. > This usually means bare-metal firmware but it is also applicable to `RTOS(s)`. -- **linux system updates:** uses the traditional `ram based swap` method to update linux distributions running atop micro-processors. -## Micro-controller Updates: +- [**linux system partitions:**](./partitions.md#linux-system-partitions) uses a single fat32 partition to host the `rustBoot-bootloader` and (boot/update) fit-images. This method uses a `rustBoot-state` file to determine which image is to be booted. +## Micro-controller Partitions: [![partition](https://github.com/imrank03/rustBoot-book-diagrams/blob/main/partition.svg?raw=true "Simplified Block Diagram, 256 byte rustBoot header")](https://github.com/imrank03/rustBoot-book-diagrams/blob/main/partition.svg?raw=true) @@ -34,9 +34,18 @@ MCU flash memory is partitioned as follows: BOOT, UPDATE, SWAP addresses and SECTOR_SIZE, PARTITION_SIZE values can be set via command line options or developers `constants.rs`. -> **rustBoot Defaults:** +> **MCU defaults:** > - By default, public keys used for firmware validation are embedded in `rustBoot-firmware` during a factory-image-burn. However, rustBoot also offers the option to retrieve them from secure-hardware (ex: crypto-elements). > - The `BOOT` partiton is the only partition from which we can boot a firmware image. The firmware image must be linked so that its entry-point is at address `256 + BOOT_PARTITION_ADDRESS`. > - `BOOT` firmware is responsible for downloading a new firmware image via a secure channel and installing it in the `UPDATE` partition. To trigger an update, the `BOOT` firmware updates the `status byte` of the `UPDATE` partition and performs a reboot. This will allow the bootloader to `swap the contents` of `BOOT` partition with that of the `UPDATE` partition. -## Linux System Updates: \ No newline at end of file +## Linux System Updates: + +To boot into linux systems, rustBoot includes support for the fat32 file-system. + +Boot-storage media must contain a fat32 partition +- of at least 150 MiB to accomodate the bootloader, boot + update fit-images and other vendor-specific boot files. +- in order to integrate rustBoot, you can either implement the `BlockDevice` trait for your board's boot-storage media `controller` or simply use an existing implementation included in the repo. + +> **note:** rustBoot comes with batteries-included. It provides `rusty` implementations for basic peripherals such as flash, uart, crypto, gpio (out of the box) along with the necessary arch-specific initialization routines. +> - for example: the rustBoot implementation for `rpi4` includes bare-metal drivers for the on-board emmc controller, gpio and uart peripherals. \ No newline at end of file diff --git a/src/arch/signing_utilities.md b/src/arch/signing_utilities.md index 1a6cdc1..6f879ca 100644 --- a/src/arch/signing_utilities.md +++ b/src/arch/signing_utilities.md @@ -1 +1,32 @@ # Signing Utilities + +As rustBoot supports 2 types of firmware image formats, depending on the underlying device i.e. either an +- [`mcu-image:`](./images.md#mcu-image-format) a simple 256-byte firmware image format for microcontrollers or a +- [`fit-image:`](./images.md#fit-image-format) the flattened-image-tree format for systems capable of booting linux. + +rustBoot `rbsigner` utility can produce 2 different types signed images. +### Signed mcu-image: + [TODO ..] +### Signed fit-image: +To sign a fit-image, rustBoot's [image signing utility](https://github.com/nihalpasham/rustBoot/tree/main/rbsigner) takes 3 inputs +- an unsigned fit-image in the above format +- a raw signing-key or ecdsa private key +- the ecdsa curve-type - (nistp256 only for now). + +Here's an example for how to sign a fit-image: +- `../boards/bootloaders/rpi4/apertis/rpi4-test-apertis.itb` is the path to my fit-image +- `../boards/rbSigner/keygen/ecc256.der` is the path to my `test` signing-key +- `nistp256` is the type ecdsa curve I'd like to use. Its the only one supported for now. + +Simply run the following command from root directory of the rustBoot project. +``` +cargo run ../boards/bootloaders/rpi4/apertis/rpi4-test-apertis.itb ../boards/rbSigner/keygen/ecc256.der nistp256 + +Output: + + Finished dev [unoptimized + debuginfo] target(s) in 0.04s + Running `/Users/nihal.pasham/devspace/rust/projects/prod/rustBoot/target/debug/rbsigner ../boards/bootloaders/rpi4/apertis/rpi4-test-apertis.itb ../boards/rbSigner/keygen/ecc256.der nistp256` +signature: ecdsa::Signature([64, 147, 93, 99, 241, 5, 118, 167, 156, 150, 203, 234, 74, 207, 182, 243, 129, 143, 38, 2, 107, 85, 114, 145, 178, 163, 33, 153, 2, 100, 0, 114, 135, 18, 174, 183, 194, 110, 24, 186, 33, 36, 39, 105, 116, 74, 8, 118, 171, 237, 30, 108, 64, 205, 206, 14, 110, 226, 43, 143, 180, 193, 19, 33]) +bytes_written: 62202019 +``` +In this case, the `signed fit-image` will be stored at the following path - `../boards/bootloaders/rpi4/apertis/signed-rpi4-apertis.itb` \ No newline at end of file diff --git a/src/architecture.md b/src/architecture.md index 31b475d..cb15474 100644 --- a/src/architecture.md +++ b/src/architecture.md @@ -1,6 +1,6 @@ # High Level Overview -rustBoot's architecture reflects its focus on `simplicity and security above all else`. +rustBoot's architecture reflects its focus on `simplicity and security`, above everything else. For a high-level overview, you can think of rustBoot as operating in 2 independent stages. diff --git a/src/objectives.md.new b/src/objectives.md.new new file mode 100644 index 0000000..e69de29 diff --git a/src/usage/rpi4.md b/src/usage/rpi4.md index 601b3ba..ab1b382 100644 --- a/src/usage/rpi4.md +++ b/src/usage/rpi4.md @@ -1 +1,255 @@ -# rpi4 +# raspberry-pi 4 + +## Table of contents: + +-   [raspberry-pi 4 boot-sequence](./rpi4.md#raspberry-pi-4-boot-sequence)   πŸ₯§ +-   [rustBoot execution-sequence:](./rpi4.md#rustboot-execution-sequence)   πŸ¦€ +-   [Booting from an SD card:](./rpi4.md#booting-from-an-sd-card)   πŸ’Ύ +-   [Compiling rustBoot:](./rpi4.md#compiling-rustboot)   βŒ› +-   [Copying a root file system:](./rpi4.md#copying-a-root-file-system)   πŸ’Ό +-   [UART communication:](./rpi4.md#uart-communication)   🚌 +-   [Power-on and test:](./rpi4.md#power-on-and-test)   πŸ§ͺ + +### raspberry-pi 4 boot-sequence: +rpi4 has an unconventional boot process +- Upon initial power-on, the `bcm2711` SoC (CPU is offline but GPU is powered on) executes from the onboard bootROM i.e. `1st stage bootloader` +> Note: the GPU contains a tiny risc core that executes the `bootROM`. +- `bootROM` checks an onboard SPI-EEPROM for a 2nd stage bootloader +- This `2nd stage bootloader` is loaded into the GPU's L2 cache for the GPU to execute. + - It initializes system clocks and SDRAM + - loads GPU firmware (start4.elf) into RAM +- the `GPU firmware` performs RAM allocations i.e. + - RAM is shared between CPU and GPU + - enables BCM7211 CPU. + - loads `rustBoot-bootloader` from the SD card into CPU-assigned RAM and passes control of the ARMv8 core to rustBoot +> Note: At this point, rustBoot has complete control over the CPU. + +### rustBoot execution-sequence: +- By default, rpi4 will always start executing in EL2. Since we are booting a traditional Kernel (i.e. linux), we have to transition into the more appropriate EL1. +> Note: EL1 and EL2 are abbreviations for ARMv8-A exception levels + +So, rustBoot checks the following +- is the core executing in `EL2`? +- are we executing on the `boot-core` i.e. is it core 0? +- if the answer to any of the above questions is `no`, then we park the core i.e. go into an inifinte wait state. +- If yes, then we initialize `DRAM`, `zero out bss`, transition to EL1 and finally jump to an early initialization routine called kernel_init. +- `kernel_init` is an early initialization routine. It takes care of the following - + - enables exception handling + - enales the MMU along with instruction + data caching + - initializes a small set of peripheral drivers i.e EMMC controller, UART, GPIO + - and passes control to the core bootloader rountine called kernel_main. +- `kernel_main` takes care of loading, verifying and booting fit-images. + - it uses rustBoot's (fat32) file-system to retrieve the first partition (or volume). + > Note: rustBoot does not support GUID Partition Table disks. + - If the first volume is a `valid fat32` partition, it loads the supplied fit-image into RAM and attempts to verify its signature using the ecdsa algorithm. + - If the fit-image is authentic i.e. the signature check passes, it relocates the following components to an approriate location in memory. + - `linux kernel` + - `fdt or dtb` + - `ramdisk or initramfs` + - additionally, it will patch the dtb with any supplied (boot-time) kernel command-line arguments. + > Note: kernel cmd-line arguments are set at package-build time i.e. when building the fit-image and cannot be interactively set at runtime. + - Finally, it disables the MMU and boots the linux kernel by jumping to its (relocated) entry point. + +### Booting from an SD card: +Raspberry Pi computers use a micro SD card to store a bootable image. + +*SD card preparation:* +- make 2 partitions + - the first one must be a fat32 partition named `firmware`. It must at least 150 MB in size. For simplicity, you can use a 256MB partition. + - the second one can be ext2/3/4 partition. This is used to host the root file system. + +*FAT32 partition contents:* +- On the card, generate a file named config.txt with the following contents: +``` + arm_64bit=1 + enable_uart=1 + init_uart_clock=4000000 + kernel=rustBoot.bin +``` +- Copy the following files from the Raspberry Pi firmware repo onto the SD card: + - [fixup4.dat](https://github.com/raspberrypi/firmware/raw/master/boot/fixup4.dat) + - [start4.elf](https://github.com/raspberrypi/firmware/raw/master/boot/start4.elf) + - [bcm2711-rpi-4-b.dtb](https://github.com/raspberrypi/firmware/raw/master/boot/bcm2711-rpi-4-b.dtb) + +> Note: Should it not work on your rpi4, try renaming start4.elf to start.elf (without the 4) on the SD card. + +### Compiling rustBoot: +You must have rust installed. You can install rust by following the installation instructions [here](https://www.rust-lang.org/tools/install). After installing rust, you'll need to switch to nightly toolchain and add the aarch64 compilation-target. This allows us to compile code for the rpi4 + +```powershell +rustup default nightly +rustup target add aarch64-unknown-none-softfloat +``` +To verify that you have the pre-requisites installed, run the following command +```powershell +rustup show +``` +In my case, `rustup show` returns the following output. + +```powershell +rustup show +Default host: aarch64-apple-darwin +rustup home: /Users/nihal.pasham/.rustup + +installed toolchains +-------------------- + +stable-aarch64-apple-darwin +nightly-aarch64-apple-darwin (default) + +installed targets for active toolchain +-------------------------------------- + +aarch64-apple-darwin +aarch64-unknown-none-softfloat +thumbv7em-none-eabihf + +active toolchain +---------------- + +nightly-aarch64-apple-darwin (default) +rustc 1.63.0-nightly (ee160f2f5 2022-05-23) +``` +As you can see from the `installed targets for active toolchain` section, the `aarch64-unknown-none-softfloat` target is installed. To compile and extract rustBoot-bootloader, simply clone the [`rustBoot repo`](https://github.com/nihalpasham/rustBoot) and run the following command + +```powershell +cargo rpi4 build rustBoot-only +``` +The above command should output the following (output will be longer when compilng for the first time) logs, produce an executable bootloader named `rustBoot.bin` and store it in the following path `./rustBoot/boards/bootloaders/rpi4` + +```powershell +Output: + Compiling xtask v0.1.0 (/Users/nihal.pasham/devspace/rust/projects/prod/rustBoot/xtask) + Finished dev [unoptimized + debuginfo] target(s) in 0.64s + Running `target/debug/xtask rpi4 build rustBoot-only` +$ cargo build --release + Compiling rustBoot v0.1.0 (/Users/nihal.pasham/devspace/rust/projects/prod/rustBoot/rustBoot) + Compiling rustBoot-hal v0.1.0 (/Users/nihal.pasham/devspace/rust/projects/prod/rustBoot/boards/hal) + Compiling rpi4 v0.1.0 (/Users/nihal.pasham/devspace/rust/projects/prod/rustBoot/boards/bootloaders/rpi4) + Finished release [optimized] target(s) in 4.77s +$ rust-objcopy --strip-all -O binary ../../target/aarch64-unknown-none-softfloat/release/kernel rustBoot.bin +``` +After compiling rustBoot, copy `rustBoot.bin` file onto the sd card's fat32 partiton. + +The last step in preparing a bootable SD card is to copy a rustBoot fit-image that you'd like to boot onto the sd card's fat32 partition. prepar + +> Note: +> - to build a rustBoot fit-image, you can follow [these instructions](../arch/images.md#building-a-rustboot-compliant-fit-image) and +> - to sign a fit-image, you can follow [these instructions](../arch/images.md#signing-fit-images). + +Finally, once you've added the above mentioned files to your sd card. The fat32 partition should contain the following files: +-   config.txt +-   fixup4.dat +-   start4.elf +-   bcm2711-rpi-4-b.dtb +-   rustBoot.bin +-   signed-example-image.itb + +### Copying a root file system: +There are many ways to add a root file-system to the second ext2/3/4 partition. One way is to copy a root file system to an empty ext2/3/4 drive: +- Maintainers of a particular linux distribution provide pre-built OS images. These images usually contain several partitions such as - + - `boot:` contains the bootloader, kernel, dtb, ramdisk and other stuff + - `system:` contains the root file system + - `others:` may contain other partitions for things such as storage etc. +- simply download an OS image or a pre-built linux distribution from the maintainers website. + - In this example, I'll be using the `apertis` distribution. +- it’s usually in a compressed (zImage) format. Decompress it using a tool like unarchiver to get a disk image. +- use `partx --show` to list all partitions +```powershell +$ partx --show __linux_image_filepath__ +NR START END SECTORS SIZE NAME UUID + 1 8192 532479 524288 256M 9730496b-01 + 2 532480 3661823 3129344 1.5G 9730496b-02 +``` +- in the above case, partition2 with a size of 1.5G contains the root file system. It's usually named `system`. +- calculate the offset to `system` volume/partition. You can do this like so. +> `512` is the sector-size. We multiply sector-size with the sector offset to get the actual starting (byte) location of `system`. +```powershell +$ bc +bc 1.07.1 +Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006, 2008, 2012-2017 Free Software Foundation, Inc. +This is free software with ABSOLUTELY NO WARRANTY. +For details type `warranty'. +> 532480 * 512 +> 272629760 +> quit +``` +- mount the partition as an `ext4 filesystem` +``` +$ sudo mkdir /mnt/other +$ sudo mount -v -o offset=272629760 -t ext4 /_path_to_file_image/__filename__.img /mnt/other +mount: /dev/loop0 mounted on /mnt/other. + +Check mounted image + +$ ls /mnt/other +``` +- copy all of (system partition's) contents to sd card's ext2/3/4 partition using the `cp` command. + +> Note: +> - the above method only works on linux or macOS. +> - all symbolic links need to be copied. If required you can create symbolic links using `ln` command. Here' an example that creates a symbolic link called `sbin` to `usr/bin` +> ```powershell +> ln -s usr/sbin sbin +> ``` + +### UART communication: +rustBoot will output boot-logs via the raspberry-pi 4's UART interface. These logs can be sent to a host computer (i.e. laptop/desktop). + +We'll need extra hardware for this: +- **a usb-to-serial ttl converter:** is a tiny piece of hardware that allows us to send serial data from the rpi4's uart interface to the host + - You can find USB-to-serial cables that should work right away at [[1]](https://www.amazon.de/dp/B0757FQ5CX/ref=cm_sw_r_tw_dp_U_x_ozGRDbVTJAG4Q) [[2]](https://www.adafruit.com/product/954), but many others will work too. Ideally, your cable is based on the CP2102 chip. + - You connect it to GND and GPIO pins 14/15 as shown below. + +Connect the USB-serial converter to your host computer as shown in the wiring diagram +[![wiring diagram](https://www.jeffgeerling.com/sites/default/files/images/raspberry-pi-serial-cable-connection.png?raw=true "USB-Serial UART connection")](https://www.jeffgeerling.com/sites/default/files/images/raspberry-pi-serial-cable-connection.png?raw=true). +- make sure that you DO NOT connect the power pin of the USB serial. Only RX/TX and GND. +- connect the rpi4 to the (USB) power cable and observe the output: + +**Serial console:** +To view rpi4's output on a host machine, you'll need a program/app/console that handles sending and receiving of serial data. There are a number of ways to interact with a serial console. I'll be using +- `minicom` on linux +- `screen` on the mac +- `terminal-s` on windows + +>❗ NOTE: +> Depending on your host operating system, the device name might differ. For example, on macOS, it might be something like /dev/tty.usbserial-0001. In this case, please give the name explicitly: + +- **Using minicom:** + - install minicom with `sudo apt-get install minicom`, so you can emulate a terminal connected over serial. + - and run + ```powershell + minicom -b 115200 -D /dev/tty.usbserial-0001 + ``` + - boot the Pi. + - within a few seconds, you should see data in your session. +- **Using screen:** + - on a mac, run + ```powershell + screen /dev/tty.usbserial-0001 115200 + ``` + - boot the Pi. + - within a few seconds, you should see data in your session. +- **Using terminal-s:** + - on a windows machine, install terminal-s (a python-based serial terminal) with `pip install terminal-s` + - and run + ```powershell + terminal-s + ``` + - no need to provide a baud-rate. It will auto-detect the port and baud-rate (assuming its lower than 115200). + +> Note: +> - To exit the screen session, press Ctrl-A, then Ctrl-K, and confirm you want to exit when using minicom or screen +> - To exit terminal-s, press Ctrl-] + +### Power-on and test +Now that you have a fully bootable SD card containing +- a `fat32 formatted` boot partition populated with the relevant boot files and +- a `ext2/3/4 formatted` root-file-system partition + +and have your uart-usb interface set-up, you are now ready to flip the switch i.e. +- insert the sd card into the pi's sd slot and +- supply power to your pi. + +Your serial console should now start [receiving boot-logs from the rpi4](https://github.com/nihalpasham/rustBoot/blob/main/boards/bootloaders/rpi4/debug.md) +