Need help printing a solid color to the screen from bare metal (very long post)

The Raspberry Pi is a series of credit card-sized single-board computers developed in the United Kingdom by the Raspberry Pi Foundation to promote the teaching of basic computer science in schools and developing countries.

Post Reply
User avatar
/RaspberryPi
Corporate
Posts: 2449
Joined: Wed Jun 05, 2019 1:29 am

Need help printing a solid color to the screen from bare metal (very long post)

Post by /RaspberryPi »

I'm a totally blind programmer and, to challenge myself, I decided to try making a voxel world like MineCraft's without acceleration on my Raspberry Pi 4 B using only Rust and assembly on bare metal. The problem is that I'm even failing to get the Pi get past the rainbow screen and have no idea why, so I decided to try to implement a small stub in assembly that just tries to get the location and size of the frame buffer and print black pixels on it so that I can create a debugging environment which I can use to continue my project with the help of sighted (though not technical) people.. Therefore I'd be really thankful if someone could help me debug my code below.
The toolchain that I'm using is nothing more than the regular AArch64 GNU assembler and linker that come with Raspberry Pi OS as well as Rust and llvm for higher level code, but for the purpose of this thread, it's only the GNU assembler and linker since I'm not posting any Rust code. Some people suggest using a toolchain targeting the aarch64-unknown-elf triplet, but since the end result is a raw binary, that requirement doesn't make much sense to me, so if you do know why this is suggested, please enlighten me.
The development is being done on the Raspberry Pi itself; I've changed its settings so that it boots off USB first so testing my code is just a matter of inserting a thumb drive with the firmware files into the Pi and give it my own kernel8.img and config.txt. There's no problem with the hardware itself since it boots normally from Linux distributions both on USB and SD card.
Below is the assembly code that I wrote in test.s (sorry about the length, but I'm trying to cover most cases without risking writing over a firmware or something by accident):
.section .text .globl start start: // Halt all the cores except for the first. mrs x0, mpidr_el1 ands x0, x0, #0x3 beq 2f // Set aside some registers to point at specific locations. ldr x15, =dyndata_start ldr x14, =mailbox_inbox_status ldr x13, =mailbox_inbox_data ldr x12, =mailbox_outbox_status ldr x11, =mailbox_outbox_data // Ask for a frame buffer allocation through the mailbox. mov w0, #256 // Buffer size. mov w1, wzr // Request value. ldr w2, =0x48003 // Set frame buffer size tag. mov w3, #12 // Value length. mov w4, #8 // Length. mov w5, #1280 // Width. mov w6, #720 // Height. ldr w7, =0x40001 // Allocate frame buffer tag. mov w8, #8 // Value length. mov w9, #4 // Length. mov w10, #0x10 // Alignment of the requested frame buffer. // Fill the buffer. stp w0, w1, [x15] stp w2, w3, [x15, #0x8] stp w4, w5, [x15, #0x10] stp w6, w7, [x15, #0x18] stp w8, w9, [x15, #0x20] stp w10, wzr, [x15, #0x28] // Wait until the outbox is not full. 0: ldr w0, [x12] tbnz w0, #31, 0b // Outbox full bit. // Outbox is no longer full, so write to it, but not before flushing everything to memory. orr w0, w15, #0x8 // Set the first 4 bits to the channel. stlr w0, [x11] // Wait until we get a response on the same channel. 0: // Wait until the inbox is not empty. 1: ldr w0, [x14] tbnz w0, #30, 1b // Inbox empty bit. // Mailbox is not empty, so read its content, ensuring that the buffer is only read afterwards. ldar w0, [x13] and w0, w0, 0xf // Extract the channel. cmp w0, #0x8 bne 0b // Got a reply, so look for the frame buffer creation response in the buffer. add x15, x15, #0x4 // Skip the buffer length. ldr w0, [x15] // Response status. mov w1, #0x80000000 cmp w0, w1 bne 2f mov w0, #0x7fffffff // Value length mask. ldr w1, =0x40001 // Frame buffer creation tag. 0: ldp w2, w3, [x15] // Tag ID and value length. cbz w2, 2f // End tag. add x15, x15, #0x8 cmp w2, w1 beq 0f and w3, w3, w0 add x15, x15, x3 b 0b 0: // Found the tag response we were looking for, now parse it. add x15, x15, #0x4 // Skip the length. ldp w0, w1, [x15] // Base and size of the frame buffer. // Finally got the frame buffer, so paint it black byte by byte so as to not overflow. add x1, x0, x1 // Get the end address. 0: cmp x0, x1 beq 2f strb wzr, [x0] add x0, x0, 0x1 b 0b // Halt the system. 2: wfi b 2b The linker script named linker.ld which contains some of the values used in the assembly code above looks like this:
ENTRY(start) . = 0x200000; SECTIONS { .text : {*(.text)} } dyndata_start = 0x40000000; dyndata_end = dyndata_start + 1024M; mailbox = 0xfe00b880; mailbox_inbox_data = mailbox + 0x0; mailbox_inbox_status = mailbox + 0x14; mailbox_outbox_data = mailbox + 0x20; mailbox_outbox_status = mailbox + 0x34; And finally I assemble, link, and format the code as follows:
as -o test.o test.s ld -Tlinker.ld -nostdlib -o test.elf test.o objcopy -O binary test.elf test.bin Which produces the final test.bin image that I can copy over kernel8.img in the thumb drive and at least in theory should boot the Pi and print black pixels to the new frame buffer.
In the thumb drive, my config.txt looks like this:
disable_overscan=1 display_auto_detect=1 arm_boost=1 disable_commandline_tags=1 arm_64bit=1 The base address values in the linker were gathered from /proc/iomem as seen by the root user on Raspberry Pi OS, and the offsets were gathered from the official Raspberry Firmware wiki on GitHub. There are actually two mailboxes mapped in /proc/iomem, but I tested both of them and the results were the same. The full and empty mailbox bits were gathered from a thread on the Raspberry Pi official forums that had some C code. Finally the property tag IDs and formats were also found in the official Raspberry Pi firmware wiki on GitHub.
From what I'm told, during the boot process of my code the LEDs on the Pi do not flash in regular patterns like the ones mentioned in this thread on the official Raspberry Pi forums, so I guess that the system is actually booting but my boot code isn't managing to paint pixels on the screen, however since I don't have a serial connection between the Pi and my computer I cannot confirm this..
Can anyone here help? I'm sure I must be either doing something stupid or have missed something, but I've been stuck on this problem for some days now and would really like to advance.
Thanks in advance!

submitted by /u/Fridux
[link] [comments]

More...
Post Reply

Return to “Raspberry Pi Forum”