Happy new year!

I bought a Raspberry Pi 3 several days ago. Playing with Linux seems boring for me, so I decided to pay some attention on ARM architecture. There are a lot of documents about bare metal development of Raspberry Pi, but almost all of them are using C & assembly. Since I’m learning Rust these days, it should be fun to try to implement something using Rust.

To get started, the most simplest task is to turn on the LED. Although there are some excellent resources available on how to turn on the LED for Raspberry Pi 1 & 2, unfortunately, all of them don’t work for Raspberry Pi 3, until I found this site.

Basically, the LED isn’t directly connected to GPIO anymore because GPIO pins are now used for Bluetooth and WiFi. To access the LED, you need to use Virtual GPIO, which is controlled by the VideoCore GPU. The way that ARM communicates with VideoCore is through Mailboxes.

# Assembly Version

According to the website I mentioned above, the assembly version is easy. We know the address of the mailbox, so we check the status of it and send it a message. I don’t want to get into the hardware details, so that’s it. The only thing we need to take care of is that the message itself is 16-byte aligned as only the upper 28 bits of the address can be passed via the mailbox.

## Assembly Code

The linking script is also very short. It places the .text section at 0x8000 and the .data section follows the .text section.

## Building

Make sure that you have installed arm-none-eabi toolchain.

Now we’ve generated a binary image, put it into your SD card together with bootcode.bin and start.elf.

# C Version

In fact it’s not necessary to use assembly, so I translate it into C. The first item in msg is the length of the message itself. In the assembly version, this length is calculated through PropertyInfoEnd - PropertyInfo. Here I just hard code it.

## C code

There are two differences in the linking script:

1. The entry point is the function open_led.
2. The data section should be 16-byte aligned.

## Building

The -O2 flag for gcc is necessary. Without this option, the compiler will try to store variables in the stack, but we don’t have a stack right now! The -O2 flag tells the compiler to use registers thus avoid this problem.

# Rust Version

Finally, we’re now trying to write this program in Rust. The first thing we need to do is installing the appropriate toolchain. Here is a good resource about cross-compilation of Rust. If you don’t know what “cross-compilation” is, please read it first. We use rustup to manage the installation process.

Follow the instruction in the website above to install rustup. Then add arm-unknown-linux-gnueabihf target:

## Very Basic Example

For simplicity, I want to first construct a minimal bare-metal binary, so let’s begin from a infinite loop:

Compile and get error message:

By default, the compiler tries to generate a binary, so we need to specify that we want a library:

But the compiler complains that the open_led is never used. What’s worse, the function seems to be deleted as an optimization.

In C, functions are extern by default, this error implies that Rust is different. So we explicitly declare it as global. no_mangle is used to avoid renaming.

Good, no error! Now try to link it:

OK, Rust needs this function. Either way, just define it as empty:

Compile and link it again and finally we get a binary.

## Write Code in Rust

Now we are ready to implement open_led function in Rust:

The names of the segments that rustc generates are a little complicated:
Our code is in section .text.open_led, the data can be found in .rodata.cst32. The linking script is as follows:
Place kernel.img we generate together with bitcode.bin and start.elf in your SD card. Stick the SD card into your Raspberry Pi 3 and turn it on. You will see the green LED is on!