rust-raspberrypi-OS-tutorials/06_raspbootin64
Andre Richter c5981b6ccd
Don't invoke clippy via xargo
Despite invoking clippy with xargo, it wasnt actually using the custom
target, so we don't need xargo here for now...

The target argument was missing anyways. Using it throws an error.
Needs some more investigation.
2018-08-19 00:42:36 +02:00
..
raspi3_boot Sync with the newest Embedonomicon 2018-08-12 16:41:45 +02:00
src Sync with the newest Embedonomicon 2018-08-12 16:41:45 +02:00
aarch64-raspi3-none-elf.json Make it compile on newest nightly 2018-07-16 21:37:40 +02:00
Cargo.lock Sync with the newest Embedonomicon 2018-08-12 16:41:45 +02:00
Cargo.toml Sync with the newest Embedonomicon 2018-08-12 16:41:45 +02:00
kernel8.img Sync with the newest Embedonomicon 2018-08-12 16:41:45 +02:00
link.ld Sync with the newest Embedonomicon 2018-08-12 16:41:45 +02:00
Makefile Don't invoke clippy via xargo 2018-08-19 00:42:36 +02:00
README.md Sync with the newest Embedonomicon 2018-08-12 16:41:45 +02:00

Tutorial 06 - Raspbootin64

We are now at a point where we have a running serial connection, but for each new feature we want to try, we still have to write and exchange the SD card every time.

As this tends to get very annoying and also to avoid potential SD card damage, we create a kernel8.img that will load the real kernel8.img over serial.

This tutorial is a rewrite of the well known serial boot loader raspbootin in 64-bit. We only provide one part of the loader, the kernel receiver, which runs on the RPi. For the other part, the sender, which runs on your PC, we will rely on the original raspbootcom utility.

For convenience, it is already packaged in our raspi3-utils docker container. So if you are running a Linux host, it will be as easy as calling another Makefile target. It will be included starting with the next tutorial, 07_abstraction. You can invoke it with

make raspboot

If you want to use it with earlier versions of this tutorial, here is a bash command to invoke it:

docker run -it --rm \
           --privileged -v /dev/:/dev/ \
           -v $PWD:/work -w /work \
           raspi3-utils \
           raspbootcom /dev/ttyUSB0 kernel8.img

In any case, if your USB device is enumerated differently, adapt accordingly.

If you want to send kernels from a Windows machine, I suggest to take a look at John Cronin's rewrite, raspbootin-server which can be compiled for the Win32 API. Even more, @milanvidakovic was so kind to share a Java version of the kernel sender with you.

Chain Loading

In order to load the new kernel to the same address, we have to move ourself out of the way. It's called chain loading: One code loads the next code to the same position in memory, therefore the latter thinks it was loaded by the firmware. To implement that, we use a different linking address this time, and since the GPU loads us to 0x80_000 regardless, we have to copy our code to that link address. When we're done, the memory at 0x80_000 is free to use. You can check that with:

$ cargo nm -- kernel8 | grep reset
000000000007ffc0 T reset

We also should minimize the size of the loader, since it will be overwritten by the newly loaded code anyway. By removing Uart::puts() and other functions, we've managed to shrink the loader's size to 1024 bytes.

boot_cores.S

First, we have to save the arguments in registers passed by the firmware. Second, we added a loop to relocate our code to the address it should have been loaded to. And last, since rustc generates RIP-relative jumps, we must adjust the branch instruction to jump to the relocated Rust code.

Linker and Boot Code

We use a different linking address this time. We calculate our code's size to know how many bytes we have to copy.

Additionally, we can remove the bss section entirely, since our loader does not use any static variables.

main.rs

We print 'RBIN64', receive the new kernel over serial and save it at the memory address where the start.elf would have been loaded it. When finished, we restore the arguments and jump to the new kernel using an absolute address.