bzt
Posts: 635
Joined: Sat Oct 14, 2017 9:57 pm

Yet Another Bare Metal Tutorial for the RPi3

Thu Jan 04, 2018 6:36 pm

First things first, Happy New Year to you all! :D

I've created a series of bare metal tutorials for the Raspberry Pi 3, available on github:
https://github.com/bztsrc/raspi3-tutorial

Focuses only on AArch64, and uses C as much as possible, therefore ideal for beginners. I wrote tons of comments in the source, and my guiding principle was K.I.S.S. all along. Lot of topics covered, from the very basic infinite loop in Assembly kind to the more complex how to read a file from the SD card ones. Easy to follow, as every tutorial builds on the previous one, and only small changes at once.

Quick list:
  • How to set up an AArch64 cross-compiler
  • How to compile and boot kernel8.img
  • How to use UART0 and UART1 with USB serial debug cable
  • How to use the undocumented HRNG
  • How to set screen resolution and draw a picture
  • How to write string to screen using PC Screen Font (same format that Linux Console uses)
  • How to read sectors from the SD card
  • How to interpret FAT file system without additional libraries
  • How to load and parse initial ramdisks
  • How to set up virtual address space using page translation tables and MMU
All examples are MIT licensed, without any warranty in the hope that they will be useful. If you find any problems, errors, typos, please use the issue tracker.

If moderators find my tutorials worthy, feel free to put the github repo link on the sticky topic.

Happy coding!
bzt

mathlizee
Posts: 4
Joined: Thu Jan 11, 2018 2:59 am

Re: Yet Another Bare Metal Tutorial for the RPi3

Thu Jan 11, 2018 3:04 am

Hi!

Thanks for this wonderful resource on baremetal programming. I have seen a lot of these examples, but they were almost entirely for RPI 1 and 2.
I was also wondering if it was possible to use C struct for the mailbox's message... Do you have an idea on how your "mbox" array could be replaced by a struct?

And again, thanks for sharing your tutorial. You made my day!

bzt
Posts: 635
Joined: Sat Oct 14, 2017 9:57 pm

Re: Yet Another Bare Metal Tutorial for the RPi3

Thu Jan 11, 2018 1:00 pm

Hi,

Thank you very much! Glad to hear it made your day! :-D

As for the mailbox and structs: well, struct describes a fixed structure in memory, but mailbox (specially the properties channel) is a flexible data structure. I mean you would need a lot of structs (Mailbox1Tag, Mailbox2Tags, Mailbox3Tags etc.) in the combination of all possible tag commands. Using an array of union of tag structs does not work, because the compiler would allocate memory for the largest struct in the union, producing (likely) inproper alignment for the 2nd and subsequent tags. Not to mention that some response tags returns variable size messages (like the command line tag). A workaround to the alignment problem could be to limit the number of tags to one in each message, which sounds very very inefficient, but could be viable in some cases. Still you would need lots of structs.
That implies mailbox structs should not be implemented in the mailbox interface, but in each interface that uses mailbox, where one could reduce the message types to a limited set. But since those higher level interfaces should hide the details from the user anyway, I see no real benefit there. For example the power management library should provide functions to application like power_on(deviceid) and power_off(deviceid), and not something like sendrecv_powermessage(struct powermessage_t *). Likewise it's a better practice to have a screen_setres(width,height,depth) and screen_move(x,y) functions rather than a sendrecv_screenmessage(union struct screenmessages_t *). You know what I mean? Those structs would be exclusive to the library (as mailbox sees ints, and propagating them to higher levels not recommended).

So keeping K.I.S.S. in mind for this tutorials, I've decided to simply use an int array instead. No-one stops you from casting that into a struct of your choosing when you work with that array in your library ;-)

Bests,
bzt

btauro
Posts: 30
Joined: Fri Jan 12, 2018 3:11 am

Re: Yet Another Bare Metal Tutorial for the RPi3

Fri Jan 12, 2018 3:22 am

Hey,
Thank you so much for your bare metal sources.
When i am building bin-utils when i run make-j4 .It give me the following error.Could you help me with this issue.

Code: Select all

make[1]: Leaving directory '/home/brian/rpi3-64/binutils-2.29'
Makefile:850: recipe for target 'all' failed
make: *** [all] Error 2
Thanks

bzt
Posts: 635
Joined: Sat Oct 14, 2017 9:57 pm

Re: Yet Another Bare Metal Tutorial for the RPi3

Fri Jan 12, 2018 10:47 am

btauro wrote:
Fri Jan 12, 2018 3:22 am
Could you help me with this issue.
Please use the issue tracker on github, don't pollute this forum (I've already answered your question there).

Thank you!
bzt

btauro
Posts: 30
Joined: Fri Jan 12, 2018 3:11 am

Re: Yet Another Bare Metal Tutorial for the RPi3

Mon Jan 15, 2018 2:28 am

Hey,
why in Tutorial 03_uart1 do we need this line

Code: Select all

r&=~((7<<12)|(7<<15));
. I know that by shifting by by 2 or 4 give us the alternative function?
But why this code .I could not find any good post explaining this.Why do we need 7 to be left shifted by 12 and 15..

LdB
Posts: 1706
Joined: Wed Dec 07, 2016 2:29 pm

Re: Yet Another Bare Metal Tutorial for the RPi3

Mon Jan 15, 2018 3:13 am

btauro wrote:
Mon Jan 15, 2018 2:28 am
But why this code .I could not find any good post explaining this.Why do we need 7 to be left shifted by 12 and 15..
The "OR" operation by it's nature can't clear bits, it is how OR works :-)

You need to clear the 3 bits before the OR operation which mean shift 0b111 to position and AND NOT onto the value

Try it with a byte 0bxx111xxx

Write the code to clear the 1's and leave the x's whatever they already are in C.
You will find you require two operation and AND NOT and an OR

0bxx111xxx AND NOT 0bxx111xxx gives 0bxx000xxx
then
0bxx000xxx OR 0bxxYYYxxx gives 0bxxYYYxxx

That is the result you wanted 3 bits OR'ed into the position and the other bits left alone.

btauro
Posts: 30
Joined: Fri Jan 12, 2018 3:11 am

Re: Yet Another Bare Metal Tutorial for the RPi3

Mon Jan 15, 2018 7:15 am

So in-order to clear the position bits we basically perform the operation.

Thank you.

LizardLad_1
Posts: 133
Joined: Sat Jan 13, 2018 12:29 am

Re: Yet Another Bare Metal Tutorial for the RPi3

Wed Jan 17, 2018 4:26 am

How do I use gpio.h to control the gpio pins?

bzt
Posts: 635
Joined: Sat Oct 14, 2017 9:57 pm

Re: Yet Another Bare Metal Tutorial for the RPi3

Wed Jan 17, 2018 5:38 pm

LizardLad_1 wrote:
Wed Jan 17, 2018 4:26 am
How do I use gpio.h to control the gpio pins?
As you can see, gpio.h holds the memory addresses for the GPIO controller, nothing more. So the answer is, you include the header and read/write to those addresses, like in this example. I suggest to read the BCM2835 peripherals manual to figure out what those addresses are used for.

There are many sources on the net about gpio handling, like this, this, this, this and this. I personally prefer dwelch67's tutorials, like this. I've learned a lot from his examples (Thx dwelch67!). If you'd rather prefer a ready-to-use library for the GPIO, I suggest to take a look at the 2nd and 3rd link.

Bests,
bzt

jamesmintram
Posts: 31
Joined: Mon Jan 15, 2018 12:14 am

Re: Yet Another Bare Metal Tutorial for the RPi3

Thu Jan 18, 2018 12:26 am

Hi,

I have a question regarding the currentEL register. You have this piece of code:
// set up EL1
mrs x0, CurrentEL
cmp x0, #4
beq 5f
Which I assume is checking that we are not already in EL1.

I have also been looking at this code: https://github.com/zxombie/aarch64-free ... boot.S#L17

Which I assume does the same check.

I have read the ARM documentation, which states that you can read currentEL as if it were a register, but I cannot find anywhere a description of currentEL and how to interpret the various bits.

I am guessing you do know how it works, and (possibly) have a link to its specification that you could share?

Regards,
James

bzt
Posts: 635
Joined: Sat Oct 14, 2017 9:57 pm

Re: Yet Another Bare Metal Tutorial for the RPi3

Thu Jan 18, 2018 2:26 pm

jamesmintram wrote:
Thu Jan 18, 2018 12:26 am
I have read the ARM documentation, which states that you can read currentEL as if it were a register, but I cannot find anywhere a description of currentEL and how to interpret the various bits.
I think you're reading the wrong documentation then. Use the ARM DDI0487
CurrentEL.png
CurrentEL.png (42.14 KiB) Viewed 90794 times
Cheers,
bzt

jamesmintram
Posts: 31
Joined: Mon Jan 15, 2018 12:14 am

Re: Yet Another Bare Metal Tutorial for the RPi3

Thu Jan 18, 2018 3:23 pm

Hey, thanks!

Yes, I was reading the documentation on their website. There is a lot of it!

Thanks for pointing me in the right direction.

LizardLad_1
Posts: 133
Joined: Sat Jan 13, 2018 12:29 am

Re: Yet Another Bare Metal Tutorial for the RPi3

Sun Jan 28, 2018 3:35 am

Can you use the mmu to do dynamic memory allocation and if you can't how do you perform dynamic memory allocation?

bzt
Posts: 635
Joined: Sat Oct 14, 2017 9:57 pm

Re: Yet Another Bare Metal Tutorial for the RPi3

Mon Jan 29, 2018 10:21 am

LizardLad_1 wrote:
Sun Jan 28, 2018 3:35 am
Can you use the mmu to do dynamic memory allocation and if you can't how do you perform dynamic memory allocation?
No, mmu is for translating virtual addresses to physical addresses (among other things). Allocation is done at a totally different abstraction level (actually levels, there are more). For dynamic memory, you have to write your own allocator, which in turn should map a physical memory for the virtual address. So there are:
  • physical memory allocator (in kernel, keeps track of RAM, smallest unit is pageframe)
  • mmu paging table updater (in kernel, invoked by brk, sbrk, mmap, munmap etc. system calls)
  • virtual memory allocator (in userspace, smallest unit is usually 8 or 16 bytes, provides malloc(),realloc(),calloc(),free())
There are many open source allocators out there: jemalloc, dlmalloc, ptalloc, etc. Pick one, if you don't feel skilled enough to write one. They are all virtual memory allocators.

I've wrote my own because neither of the existing allocators could fulfill all of my goals. Which were: localized memory management (so called arenas), therefore don't use brk system call (but mmap), don't mix allocator data with application data ever, give back freed memory to the system as soon as possible. If you want to take a look, here is the code (CC-by-nc-sa licensed). There bzt_free() updates virtual memory allocation tables and calls munmap() to tell the kernel to remove entries from mmu paging tables (which in turn will call pmm_free() to free physical memory as well). Likewise, bzt_alloc() calls mmap(), which asks the kernel to use pmm_alloc() to allocate physical memory and adds that to the mmu paging tables. (You can call bzt_dumpmem() to dump virtual memory allocation tables in a human readable format.)

For physical memory allocation I use a very simple method, I keep track of free areas in an array with a starting address and a size pair.

Hope this helps to understand the topic a bit,
bzt

meteturedi
Posts: 1
Joined: Fri Feb 02, 2018 5:51 am

Re: Yet Another Bare Metal Tutorial for the RPi3

Fri Feb 02, 2018 5:56 am

Sire,

Thank you for the awesome tutorial. I have, however, a trivial question. I quickly went through all documentation but cannot find where you got the information to put your code text at 0x80000 phyiscal address.

Could you please share the document you retrieved this information from?

SECTIONS
{
. = 0x80000;
.text : { *(.text.boot) }

/DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
}

Best Regards,
Mete

bzt
Posts: 635
Joined: Sat Oct 14, 2017 9:57 pm

Re: Yet Another Bare Metal Tutorial for the RPi3

Sat Feb 03, 2018 5:24 pm

Hi Mete,
meteturedi wrote:
Fri Feb 02, 2018 5:56 am
Could you please share the document you retrieved this information from?
There are many references all over the net, e.g. here is one (see section kernel_address):

Code: Select all

32-bit kernels are loaded to address 0x8000 by default, and 64-bit kernels to address 0x80000.
Cheers,
bzt

richardwiggler
Posts: 1
Joined: Thu Feb 15, 2018 9:48 pm

Re: Yet Another Bare Metal Tutorial for the RPi3

Fri Feb 16, 2018 12:06 am

I just started messing around with these tutorials and I have run into an issue on Tutorial 03, UART 01.

So in minicom I see "Hello. world!" being displayed alright, but I'm not getting any of the echoing.

To troubleshoot I added a uart_puts call in the while loop, and it looks like the loop is only running one time.

Any idea what I may have screwed up?

StevoD
Posts: 34
Joined: Tue Aug 29, 2017 11:37 am

Re: Yet Another Bare Metal Tutorial for the RPi3

Sat Feb 17, 2018 10:27 am

Why is this post a sticky?

Add a link to this tutorial in the official sticky post and let this one (which has become polluted with "I can't get xyz to work") come to the top only when someone has a question, like all the other projects (FreeRTOS, Circle, Ultibo etc etc etc).

bzt
Posts: 635
Joined: Sat Oct 14, 2017 9:57 pm

Re: Yet Another Bare Metal Tutorial for the RPi3

Tue Feb 20, 2018 1:15 am

richardwiggler wrote:
Fri Feb 16, 2018 12:06 am
To troubleshoot I added a uart_puts call in the while loop, and it looks like the loop is only running one time.
There's only one possible explanation, the loop inside uart_getc() runs forever. This means that bit 1 in AUX_MU_LSR never set to high. Are you sure your cabling is ok?

bzt
Posts: 635
Joined: Sat Oct 14, 2017 9:57 pm

Re: Yet Another Bare Metal Tutorial for the RPi3

Tue Feb 20, 2018 1:17 am

StevoD wrote:
Sat Feb 17, 2018 10:27 am
Why is this post a sticky?
Don't know.
Add a link to this tutorial in the official sticky post
Actually, that was my original intention:
If moderators find my tutorials worthy, feel free to put the github repo link on the sticky topic.
Cheers,
bzt

StevoD
Posts: 34
Joined: Tue Aug 29, 2017 11:37 am

Re: Yet Another Bare Metal Tutorial for the RPi3

Tue Feb 20, 2018 9:30 am

bzt wrote:
Tue Feb 20, 2018 1:17 am
If moderators find my tutorials worthy, feel free to put the github repo link on the sticky topic.
Hmm, that might explain it. You just add it to the sticky yourself, the moderators don't need to do it for you :shock:.

Paxo
Posts: 4
Joined: Thu Mar 15, 2018 2:38 pm

Re: Yet Another Bare Metal Tutorial for the RPi3

Fri Mar 16, 2018 2:13 pm

Hi, thanks for this great Tutorial.
I have a Problem at Tutorial 14 - Raspbootin64:

The new loaded kernel.img won`t start.


I´ve loaded your raspbootin64 kernel on the SD Card.
I´ve tried sending the new kernel via raspbootcom https://github.com/mrvn/raspbootin/blob ... bootcom.cc and with a self written Python-File (on Windows) https://github.com/Paxoo/Raspbootcom--- ... er/Main.py.
I get the same Problem in both cases.


What I´ve checked so far:
I checked the sended kernel size, which seems to be correct.
I checked if the Sender (Python-File or raspbootcom) is sending the new kernel, which seems to be correct.
I checked if the Raspberry Pi quits the

Code: Select all

while(size--) *kernel++ = uart_getc();
loop, which seems not to be the case.
So I checked the variable "size" if its counting down -> It stops counting down after 404 received bytes in every test. The Raspberry Pi is stuck in the while-loop and thats why the new loaded kernel won´t start.

I tried different kernel-images from your tutorial.

------------------------

Apparently I´m doing something wrong but I can´t figure out what it is. Hopefully you can help me out.

mvidakovicns
Posts: 6
Joined: Sun Mar 25, 2018 11:26 am

Re: Yet Another Bare Metal Tutorial for the RPi3

Sun Mar 25, 2018 11:38 am

Hello,
Thank you for the project. Unfortunately, I am not able to run any of the examples. First of all, is this a good place to ask for help, or should I go to the github and post an issue?
Just to make sure, here is the procedure I did:
1. format the sd card as FAT32
2. copy bootcode.bin and start.elf to the sd card (got them from the official raspberry github/firmware/boot)
3. copy kernel8.img from any of the examples onto the sd card
4. put the sd card into the rpi3
5. power on

Some of the examples should send a text via serial port (GPIO 14 and 15). I do have a working serial to USB adapter which works for sure. I don't get anything on the serial.

How can I troubleshoot this?

Thanks in advance!

bzt
Posts: 635
Joined: Sat Oct 14, 2017 9:57 pm

Re: Yet Another Bare Metal Tutorial for the RPi3

Mon Mar 26, 2018 10:06 am

@paxo: I've fixed two bugs in the chainloader, please try it again with the newest version. Due to an absolute/relative addressing issue (my bad) it wasn't jumping to the relocated main, which means it was running the code that was overwritten by the loop. Definitely could cause the kind of trouble you experienced. I gave a detailed explanation in the other thread of yours.

@mvidakovicns: I'd prefer github issues, I've already answered your question there. In short, you haven't said what kind of partitioning table you're using. RPi3 fw requires MBR, not GPT. Second, to check if bootcode.bin and start.elf working properly, you should boot for example a led blinking kernel7.img first. Finally you should try to download the raspbian lite image, remove the kernel*.img and config.txt files and use that as a starting point. If that works, then the problem is somewhere with your partitioning for sure. Just for the records, I've written a small utility that maps an EFI System Partition (with FAT) into the MBR partitining table so that RPi3 fw can load files from there. You can also try that if you really want to use GPT.

Thank you guys for your feedbacks on my tutorials!
bzt

Return to “Bare metal, Assembly language”