summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--md/writeup/raspberry5_baremetal_framebuffer.md320
1 files changed, 320 insertions, 0 deletions
diff --git a/md/writeup/raspberry5_baremetal_framebuffer.md b/md/writeup/raspberry5_baremetal_framebuffer.md
new file mode 100644
index 0000000..681c486
--- /dev/null
+++ b/md/writeup/raspberry5_baremetal_framebuffer.md
@@ -0,0 +1,320 @@
+title: Raspberry 5 baremetal framebuffer example
+keywords:raspi5,asm,c,kernel,arm64
+
+# Raspberry 5 baremetal framebuffer example
+
+
+## Intro
+
+The Raspberry Pi 5 has an easy way to draw graphics on the display by using a
+communication protocol through mailboxes. Here we will get framebuffer
+address and draw some simple primitives onto screen. There is not much clear
+documentation on how things should work all based on current examples and experimenting.
+
+## Programming mailbox
+
+### Packets
+
+To communicate with the mailbox, you need to create an array with commands, input and
+output elements. Array elements are uint32_t.
+
+```c
+array[0] //first element
+...
+array[n] //last element
+```
+
+First element is size of array in bytes and request tag, last value is
+the last tag value
+
+Here is the list of mailbox commands https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
+commands consist of tag, amount of input bytes and output bytes.
+
+Example of allocate buffer command:
+```txt
+Tag: 0x00040001
+Request:
+ Length: 4
+ Value:
+ u32: alignment in bytes
+Response:
+ Length: 8
+ Value:
+ u32: frame buffer base address in bytes
+ u32: frame buffer size in bytes
+```
+
+this translates to array values
+```c
+cmd[n+0] = 0x00040001
+cmd[n+1] = 8
+cmd[n+2] = 4
+cmd[n+3] = 0
+cmd[n+4] = 0
+```
+
+### Addresses
+
+## Setting up the framebuffer
+
+Raspberry Pi5 doesn't provide much that you can do with framebuffers
+comparing to version 4. And there isn't much documentation on full
+capabilities. Simple example how to use framebuffer is request for
+framebuffer address and directly write values to the memmory range.
+
+```c
+fb_mbox[0] = 8*4;
+fb_mbox[1] = MBOX_REQUEST;
+fb_mbox[2] = MBOX_TAG_GETFB;
+fb_mbox[3] = 8; //buffer size
+fb_mbox[4] = 4; //alignment
+fb_mbox[5] = 0; //frame buffer address
+fb_mbox[6] = 0; //size in bytes
+
+fb_mbox[7] = MBOX_TAG_LAST;
+```
+
+The result will be in fb_mbox[5], which needs to be masked and can then be accessed directly:
+
+```c
+fb_address = fb_mbox[5] & 0x3fffffff;
+```
+
+buffer must be aligned by 16-bytes.
+
+To submit the buffer to mailbox for processing write to MBOX1_WRITE register.
+Check for response by reading MBOX1_STATUS register. Check MBOX0_READ that
+its matches the request address. And check the response status status it will
+be located at second index of request buffer.
+
+
+
+## Drawing pixel
+
+Drawing a pixel is as simple as calculating pixel position and write 32bit value
+
+```c
+void fb_draw_pixel(unsigned int x, unsigned int y) {
+ mmio_write(fb_address + (((SCREEN_X*y)+x)*4),0xFFFFFFFF);
+}
+```
+
+extended example is with using colors in format ARGB32
+
+```c
+void fb_draw_pixel_rgb(unsigned int x, unsigned int y, unsigned char r, unsigned char g, unsigned char b) {
+ mmio_write(fb_address + (((SCREEN_X*y)+x)*4),0xFF000000|((unsigned int)r<<16)|((unsigned int)g<<8)|((unsigned int)b));
+}
+```
+
+## Source code
+__config.txt__
+```txt
+# Core settings
+arm_64bit=1
+kernel=kernel8.img
+[all]
+# UART configuration for debugging
+enable_uart=1
+uart_2ndstage=1
+uart_debug=1
+uart_clkrate=48000000
+uart_baudrate=115200
+
+# RP1 chip configuration (Pi5 I/O chip)
+# Disable PCIe reset to access RP1 from bare metal
+pciex4_reset=0
+
+# Memory settings
+gpu_mem=64
+total_mem=1024
+
+# Boot settings
+disable_splash=1
+boot_delay=0
+
+# Debug settings
+uart_2ndstage=1
+
+# Pi5 specific GPIO/UART
+dtparam=uart0=on
+
+# Disable unused services to reduce boot time
+enable_jtag_gpio=1
+enable_rp1_uart=1
+pciex4_reset=0
+core_freq_min=500
+hdmi_group=1
+hdmi_mode=16
+```
+
+__mailbox.h__
+```c
+#ifndef __MAILBOX_H
+#define __MAILBOX_H
+
+//#define MMIO_BASE 0x1c00000000UL
+#define MMIO_BASE_1 0x107C000000
+//#define MMIO_BASE 0xc00000000UL
+//#define MBOX_REG_OFFSET (MMIO_BASE+0x40008000)
+//#define MBOX_REG_OFFSET (MMIO_BASE+0x8000)
+#define MBOX_REG_OFFSET (MMIO_BASE_1+0x13880)
+//#define MBOX_REG_OFFSET (MMIO_BASE+0xB880)
+
+
+#define MBOX_COMMAND_BUFFER_SIZE 36
+#define MBOX1_OFFSSET 0x20
+
+#define MBOX0_READ (MBOX_REG_OFFSET + 0x0)
+#define MBOX0_POLL (MBOX_REG_OFFSET + 0x10)
+#define MBOX0_SENDER (MBOX_REG_OFFSET + 0x14)
+#define MBOX0_STATUS (MBOX_REG_OFFSET + 0x18)
+#define MBOX0_CONFIG (MBOX_REG_OFFSET + 0x1C)
+#define MBOX1_WRITE (MBOX_REG_OFFSET+MBOX1_OFFSSET + 0x00)
+#define MBOX1_STATUS (MBOX_REG_OFFSET+MBOX1_OFFSSET + 0x18)
+#define MBOX_RESPONSE 0x80000000
+#define MBOX_RESP_ERR 0x80000001
+#define MBOX_FULL 0x80000000
+#define MBOX_EMPTY 0x40000000
+#define MBOX_REQUEST 0x00000000
+
+#define MBOX_CH_VIDEOCORE 0x8
+
+#define MBOX_TAG_FW_VERSION 0x00001
+#define MBOX_TAG_SETPOWER 0x28001
+#define MBOX_TAG_SETCLKRATE 0x38002
+#define MBOX_TAG_SETPHYWH 0x48003
+#define MBOX_TAG_SETVIRTWH 0x48004
+#define MBOX_TAG_SETVIRTOFF 0x48009
+#define MBOX_TAG_SETDEPTH 0x48005
+#define MBOX_TAG_SETPXLORDR 0x48006
+#define MBOX_TAG_GETFB 0x40001
+#define MBOX_TAG_GETPITCH 0x40008
+#define MBOX_TAG_LAST 0x00000
+
+//unsigned int mbox_call(volatile unsigned int *request, unsigned char channel);
+unsigned int mbox_call(unsigned char channel);
+#endif
+```
+__mailbox.c__
+```c
+#include <mailbox.h>
+#include <pl011.h>
+#include <mmio.h>
+
+extern volatile unsigned int __attribute__((aligned(16))) fb_mbox[MBOX_COMMAND_BUFFER_SIZE];
+extern unsigned int fb_address;
+
+//unsigned int mbox_call(volatile unsigned int *request, unsigned char channel) {
+unsigned int mbox_call(unsigned char channel) {
+ unsigned int addr = ((unsigned int)((unsigned long) &fb_mbox) & ~0xF) | (channel & 0xF);
+ while ((mmio_read(MBOX1_STATUS) & MBOX_FULL) != 0);
+
+ unsigned int p_addr = (uint32_t)fb_mbox;
+ mmio_write(MBOX1_WRITE,MBOX_CH_VIDEOCORE|p_addr);
+
+ while (1) {
+ while (mmio_read(MBOX1_STATUS) & MBOX_EMPTY);
+ if (addr == mmio_read(MBOX0_READ)) {
+ return fb_mbox[1] == MBOX_RESPONSE;
+ }
+ }
+}
+```
+
+__framebuffer.h__
+```c
+#ifndef __FRAMEBUFFER_H
+#define __FRAMEBUFFER_H
+
+void fb_init();
+void fb_fw_version();
+void fb_draw_pixel(unsigned int x, unsigned int y);
+
+#endif
+```
+
+__framebuffer.c__
+```c
+#include <framebuffer.h>
+#include <mailbox.h>
+#include <pl011.h>
+#include <mmio.h>
+
+#define FB_SCREEN_WIDTH 1920
+#define FB_SCREEN_HEIGHT 1080
+
+volatile unsigned int __attribute__((aligned(16))) fb_mbox[MBOX_COMMAND_BUFFER_SIZE];
+unsigned int fb_address = 0x0;
+
+
+unsigned int width, height, pitch, isrgb;
+unsigned char *fb;
+
+void fb_init() {
+ fb_mbox[0] = 8*4;
+ fb_mbox[1] = MBOX_REQUEST;
+ fb_mbox[2] = MBOX_TAG_GETFB;
+ fb_mbox[3] = 8;
+ fb_mbox[4] = 4;
+ fb_mbox[5] = 0; //frame buffer address
+ fb_mbox[6] = 0;
+ fb_mbox[7] = MBOX_TAG_LAST;
+
+ mbox_call(MBOX_CH_VIDEOCORE);
+ pl011_puts("mbox_call read\r\n");
+ pl011_puts("fw version ");
+ pl011_putu32(fb_mbox[5]);
+ fb_address = fb_mbox[5] & 0x3fffffff;
+ pl011_puts("\r\n");
+}
+
+void fb_fw_version() {
+ fb_mbox[0] = 5*4;
+ fb_mbox[1] = MBOX_REQUEST;
+
+ fb_mbox[2] = MBOX_TAG_FW_VERSION;
+ fb_mbox[3] = 4;
+ fb_mbox[4] = 0; //fw version
+
+ fb_mbox[5] = MBOX_TAG_LAST;
+
+ mbox_call(MBOX_CH_VIDEOCORE);
+ //pl011_puts("mbox_call read\r\n");
+ pl011_puts("fw version ");
+ pl011_putu32(fb_mbox[4]);
+ pl011_puts("\r\n");
+}
+
+#define SCREEN_X 1920
+#define SCREEN_Y 1080
+#define BITS_PER_PIXEL 32
+
+void fb_draw_pixel(unsigned int x, unsigned int y) {
+ mmio_write(fb_address + (((SCREEN_X*y)+x)*4),0xFFFFFFFF);
+}
+```
+
+## Links
+[/writeup/raspberry5_baremetal_helloworld.md](/writeup/raspberry5_baremetal_helloworld.md)
+https://www.rpi4os.com/part5-framebuffer/
+https://github.com/babbleberry/rpi4-osdev/blob/master/part5-framebuffer/mb.h
+https://github.com/valvers/arm-tutorial-rpi/blob/master/part-5/armc-017/rpi-mailbox-interface.h
+https://www.valvers.com/open-software/raspberry-pi/bare-metal-programming-in-c-part-5/#mailboxes
+https://github.com/raspberrypi/linux/blob/rpi-6.12.y/arch/arm64/boot/dts/broadcom/bcm2712.dtsi#L229C3-L234C5
+https://blog.benjdoherty.com/2019/05/21/Exploring-Hardware-Compositing-With-the-Raspberry-Pi/#Double-Buffering-the-Display-List
+https://github.com/bejado/raspberry-pi-os/blob/master/src/hvs/src/kernel.c
+https://github.com/librerpi/lk-overlay/blob/master/platform/bcm28xx/mailbox/mailbox.c
+https://github.com/raspberrypi/linux/pull/6312/files
+https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
+https://github.com/raspberrypi/linux/blob/rpi-6.12.y/drivers/mailbox/bcm2835-mailbox.c
+https://github.com/rsta2/circle/blob/master/include/circle/bcm2835.h
+https://github.com/raspberrypi/linux/blob/rpi-6.12.y/include/soc/bcm2835/raspberrypi-firmware.h
+https://bitbanged.com/posts/understanding-rpi/the-mailbox/
+https://github.com/BrianSidebotham/arm-tutorial-rpi/blob/master/part-5/readme.md
+https://fossies.org/linux/qemu/hw/misc/bcm2835_mbox.c
+https://satyria.de/arm/index.php?title=Graphics_in_C_(PI5)
+https://forums.raspberrypi.com/viewtopic.php?t=380434
+https://grasslab.github.io/osdi/en/hardware/mailbox.html
+
+