Booting Raspberry Pi 3: Difference between revisions

[unchecked revision][unchecked revision]
Content deleted Content added
No edit summary
No edit summary
 
(16 intermediate revisions by 6 users not shown)
Line 1:
{{Tone}}
{{Spelling}}
{{FirstPerson}}
{{You}}
{{Rating|1}}
This is a tutorial on bare-metal OS development on the Raspberry Pi 3 (RPi 3).
 
'''THIS IS NOT A PRACTICAL IMPLEMENTATION NOR A REFERENCE! It'S SIMPLY SOMETHING TO BE BUILT UP ON!'''
This is a tutorial on bare-metal OS development on the AArch64 architecture. This article oriented to the Raspberry Pi 3 (RPi 3), but is aimed to be as device agnostic as possible. Therefore, some sections will be specific to the RPi 3 and will be marked to represent that.
 
This is the author's very first ARM system and wiki page. I learn as I write and will continue to develop this page once I feel comfortable enough at the subjet to share.
 
== Preamble ==
Line 20 ⟶ 24:
https://aur.archlinux.org/packages/aarch64-elf-newlib-linaro-bin/
 
====RPi 3: Firmware====
 
Firmware is required for the RPi 3 to boot properly. The firmware needed can be found at these link:
Line 30 ⟶ 34:
There is also another file required to boot properly. A config.txt file must be supplied to provide configuration details for the device and the OS. Here are the only entries you need:
 
<syntaxhighlight lang="ini">
<pre>
boot_delay=1
force_turbo=1
enable_uart=1
</syntaxhighlight>
</pre>
 
More details about booting and configuration can be found at thse links.
Line 62 ⟶ 66:
Linker script allows us to segment, organize, and align our kernel image. More information about linker scripts can be found osdev's [https://wiki.osdev.org/Linker_Scripts Linker Scripts].
 
'''The start address and alignment are for the RPi3. Use whatever is applicable to you.'''
<syntaxhighlight lang="c">
<pre>
SECTIONS
{
. = 0x80000x80000;
.text : {
Line 82 ⟶ 86:
.bss (NOLOAD) : {
. = ALIGN(16);
__bss_start = .;
*(.bss*)
. = ALIGN(16);
__bss_nlwords = (. - __bss_start);
}
}
</syntaxhighlight>
</pre>
 
== Booting the Kernel ==
 
AfterFor the RPi 3, after the start.elf finishes, the CPU and SRAM have been enabled and control is given to the kernel image. But there are a few things we must do in order to set up a basic C environment. This is known as a bootstrap stage which initializes our OS and programming environment on startup and hands control to the kernel. Keep in mind that this is for a minimalistic development environment on a single core.
 
<syntaxhighlight lang="asm">
When the CPU starts running the kernel, all cores will be executing the same code. This can be tricky for multiple reasons, which will be described later. For now, all we want is just one core running the kernel. To do that, we need get the core identification number and set all but one core to run an infinite loop.
 
<pre>
.section .boot
 
Line 103 ⟶ 101:
 
_start:
mrs x0x4, mpidr_el1
and x0x4, x0x4, #3
cbz x0x4, _init
0: wfe
b 0b
</syntaxhighlight>
</pre>
 
The mrs instruction loads data from specialty registers into a standard register. This special register in question is called the MPIDR or the Multiprocessor Affinity Register. Caring only about the first two bits only we use a bitwise and to weed out any core ID's that aren't zero. Then we apply the cbz instruction, which is a shorthand instruction for comparing the x0x4 register with 0 and branching if they are equal, i.e. core 0.
 
If the core ID is 0, we branch to another section that will initialize the environment and will hand control over to the kernel. For now, the C function for our kernel will simply by kern_main.
 
<syntaxhighlight lang="asm">
<pre>
_init: ldr x4, =_start
cbzmov x0sp, _initx4
b kern_main
</syntaxhighlight>
</pre>
 
Note that with the latest firmware, this is not necessary, as only the primary core runs. It is enough to set up stack and branch to kern_main.
 
<syntaxhighlight lang="asm">
// for latest firmware, after 2020-01-01
.section .boot
 
.global _start
 
_start: ldr x4, =_start
andmov x0, x0sp, #3x4
b kern_main
</syntaxhighlight>
 
== Writing the Kernel ==
 
The kernel is as simple as creating a kern_main function for the bootstrap stage to transfer control to. You notice that in start.S we have the program branch into the function. All we need to do is provide one.
== Conclusion ==
 
<syntaxhighlight lang="c">
Here is the code for all of the files.
 
#include <stdbool.h>
start.S
<pre>
.section .boot
 
void
.global _start
kern_main(void)
{
while (true);
}
</syntaxhighlight>
 
== Conclusion ==
_start:
mrs x0, mpidr_el1
and x0, x0, #3
cbz x0, _init
0: wfe
b 0b
 
At the time of writing, QEMU does not yet emulate the RPi 3. The only way to test is to hook up the RPi 3 to an HDMI monitor. If start.elf was successfully able to run, then a square with interpolated colors will appear on the screen. That paird with the ACT (green LED) flashing even after the square shows means that it most likely has loaded the kernel and started executing. As this lacks a UART example, it will be difficult to get concrete proof that the kernel has booted.
_init:
 
b kern_main
== See Also ==
</pre>
 
=== Articles ===
* [[Raspberry Pi Bare Bones]] - a much more detailed introduction to booting on Raspberry Pi
 
[[Category:Raspberry Pi]]
[[Category:Booting]]