# Baremetal RISC-V Ok this chuck guy knows his stuff it seems https://www.youtube.com/watch?v=qLzD33xVcRE `barmetal.ld` contains the linker scripts. The `SECTIONS` keyword indicates the beginning of the linker script sections where he specifies the memory layout, and the sections of the program. ``` { . = 0x800000000; } ``` This sets the location counter to the 0x800000000 memory address. This means that the following sections of the linker script will be placed as if starting from this address in memory. This is important because we need to know where in memory to load our application, so that the cpu boots we know what address will be loaded into the program counter to start executing instructions. ADD MORE DETAILS ## QEMU Run this command ``` qemu-system-riscv32 -nographic -serial mon:stdio -machine virt ``` If you encounter ``` qemu-system-riscv32: Unable to load the RISC-V firmware "opensbi-riscv32-generic-fw_dynamic.bin" ``` Well it's going to suck a little. Running `sudo apt install opensbi` only appears to include qemu-system-riscv64 builds. I tried building from source and hit a ton of issues, so instead I untarred a binary release in my home directory ``` wget https://github.com/riscv-software-src/opensbi/releases/download/v1.5.1/opensbi-1.5.1-rv-bin.tar.xz tar -xf opensbi-1.5.1-rv-bin.tar.xz -C ~/ ls ~/opensbi-1.5.1-rv-bin ``` ``` qemu-system-riscv32 -nographic -serial mon:stdio -machine virt -bios ~/opensbi-1.5.1-rv-bin/share/opensbi/ilp32/generic/firmware/fw_dynamic.bin ``` Now we can continue... We start qemu in `-nographic` mode. We redirect the console input/output to our terminal `-serial mon:stdio`, and we start the `virt` machine type that we will do the simulation under. ``` OpenSBI v1.5.1 ____ _____ ____ _____ / __ \ / ____| _ \_ _| | | | |_ __ ___ _ __ | (___ | |_) || | | | | | '_ \ / _ \ '_ \ \___ \| _ < | | | |__| | |_) | __/ | | |____) | |_) || |_ \____/| .__/ \___|_| |_|_____/|____/_____| | | |_| Platform Name : riscv-virtio,qemu Platform Features : medeleg Platform HART Count : 1 Platform IPI Device : aclint-mswi Platform Timer Device : aclint-mtimer @ 10000000Hz Platform Console Device : semihosting Platform HSM Device : --- Platform PMU Device : --- Platform Reboot Device : syscon-reboot Platform Shutdown Device : syscon-poweroff Platform Suspend Device : --- Platform CPPC Device : --- Firmware Base : 0x80000000 Firmware Size : 319 KB Firmware RW Offset : 0x40000 Firmware RW Size : 63 KB Firmware Heap Offset : 0x47000 Firmware Heap Size : 35 KB (total), 2 KB (reserved), 9 KB (used), 23 KB (free) Firmware Scratch Size : 4096 B (total), 240 B (used), 3856 B (free) Runtime SBI Version : 2.0 Domain0 Name : root Domain0 Boot HART : 0 Domain0 HARTs : 0* Domain0 Region00 : 0x00100000-0x00100fff M: (I,R,W) S/U: (R,W) Domain0 Region01 : 0x02000000-0x0200ffff M: (I,R,W) S/U: () Domain0 Region02 : 0x0c200000-0x0c20ffff M: (I,R,W) S/U: (R,W) Domain0 Region03 : 0x80040000-0x8004ffff M: (R,W) S/U: () Domain0 Region04 : 0x80000000-0x8003ffff M: (R,X) S/U: () Domain0 Region05 : 0x0c000000-0x0c1fffff M: (I,R,W) S/U: (R,W) Domain0 Region06 : 0x00000000-0xffffffff M: () S/U: (R,W,X) Domain0 Next Address : 0x00000000 Domain0 Next Arg1 : 0x87000000 Domain0 Next Mode : S-mode Domain0 SysReset : yes Domain0 SysSuspend : yes Boot HART ID : 0 Boot HART Domain : root Boot HART Priv Version : v1.10 Boot HART Base ISA : rv32imafdc Boot HART ISA Extensions : zicntr Boot HART PMP Count : 16 Boot HART PMP Granularity : 2 bits Boot HART PMP Address Bits: 32 Boot HART MHPM Info : 0 (0x00000000) Boot HART Debug Triggers : 0 triggers Boot HART MIDELEG : 0x00000222 Boot HART MEDELEG : 0x0000b109 ``` We see that our `Firmware Base : 0x80000000` is address where the firmware should be loaded from. You can also see that `Domain0 Region04 : 0x80000000-0x8003ffff M: (R,X) S/U: ()` this region is marked as X for executable. How do we know that the serial UART is at 0x10000000? This UART is defined as a NS16550 Uart in https://www.qemu.org/docs/master/system/riscv/virt.html Looking at the [UART16550 Core technical manual](https://uart16550.readthedocs.io/en/latest/uart16550doc.html#registers) we see that `Transmitter Holding Register (THR)` is at Address 0. Lastly, we want to enter the qemu console, so `Ctrl-A C` will take us to the qemu console. We want to inspect the `info mtree` output we can see that the `0000000010000000-0000000010000007 (prio 0, i/o): serial` serial device memory location starts at 0x10000000. So since the register the THR is at 0, we know we can use that as our UART target. ``` (qemu) info mtree (qemu) info mtree address-space: memory 0000000000000000-ffffffffffffffff (prio 0, i/o): system 0000000000001000-000000000000ffff (prio 0, rom): riscv_virt_board.mrom 0000000000100000-0000000000100fff (prio 0, i/o): riscv.sifive.test 0000000000101000-0000000000101023 (prio 0, i/o): goldfish_rtc 0000000002000000-0000000002003fff (prio 0, i/o): riscv.aclint.swi 0000000002004000-000000000200bfff (prio 0, i/o): riscv.aclint.mtimer 0000000003000000-000000000300ffff (prio 0, i/o): gpex_ioport_window 0000000003000000-000000000300ffff (prio 0, i/o): gpex_ioport 000000000c000000-000000000c20ffff (prio 0, i/o): riscv.sifive.plic 0000000010000000-0000000010000007 (prio 0, i/o): serial 0000000010001000-00000000100011ff (prio 0, i/o): virtio-mmio 0000000010002000-00000000100021ff (prio 0, i/o): virtio-mmio 0000000010003000-00000000100031ff (prio 0, i/o): virtio-mmio 0000000010004000-00000000100041ff (prio 0, i/o): virtio-mmio 0000000010005000-00000000100051ff (prio 0, i/o): virtio-mmio 0000000010006000-00000000100061ff (prio 0, i/o): virtio-mmio 0000000010007000-00000000100071ff (prio 0, i/o): virtio-mmio 0000000010008000-00000000100081ff (prio 0, i/o): virtio-mmio 0000000010100000-0000000010100007 (prio 0, i/o): fwcfg.data 0000000010100008-0000000010100009 (prio 0, i/o): fwcfg.ctl 0000000010100010-0000000010100017 (prio 0, i/o): fwcfg.dma 0000000020000000-0000000021ffffff (prio 0, romd): virt.flash0 0000000022000000-0000000023ffffff (prio 0, romd): virt.flash1 0000000030000000-000000003fffffff (prio 0, i/o): alias pcie-ecam @pcie-mmcfg-mmio 0000000000000000-000000000fffffff 0000000040000000-000000007fffffff (prio 0, i/o): alias pcie-mmio @gpex_mmio_window 0000000040000000-000000007fffffff 0000000080000000-0000000087ffffff (prio 0, ram): riscv_virt_board.ram 0000000300000000-00000003ffffffff (prio 0, i/o): alias pcie-mmio-high @gpex_mmio_window 0000000300000000-00000003ffffffff address-space: I/O 0000000000000000-000000000000ffff (prio 0, i/o): io address-space: cpu-memory-0 0000000000000000-ffffffffffffffff (prio 0, i/o): system 0000000000001000-000000000000ffff (prio 0, rom): riscv_virt_board.mrom 0000000000100000-0000000000100fff (prio 0, i/o): riscv.sifive.test 0000000000101000-0000000000101023 (prio 0, i/o): goldfish_rtc 0000000002000000-0000000002003fff (prio 0, i/o): riscv.aclint.swi 0000000002004000-000000000200bfff (prio 0, i/o): riscv.aclint.mtimer 0000000003000000-000000000300ffff (prio 0, i/o): gpex_ioport_window 0000000003000000-000000000300ffff (prio 0, i/o): gpex_ioport 000000000c000000-000000000c20ffff (prio 0, i/o): riscv.sifive.plic 0000000010000000-0000000010000007 (prio 0, i/o): serial 0000000010001000-00000000100011ff (prio 0, i/o): virtio-mmio 0000000010002000-00000000100021ff (prio 0, i/o): virtio-mmio 0000000010003000-00000000100031ff (prio 0, i/o): virtio-mmio 0000000010004000-00000000100041ff (prio 0, i/o): virtio-mmio 0000000010005000-00000000100051ff (prio 0, i/o): virtio-mmio 0000000010006000-00000000100061ff (prio 0, i/o): virtio-mmio 0000000010007000-00000000100071ff (prio 0, i/o): virtio-mmio 0000000010008000-00000000100081ff (prio 0, i/o): virtio-mmio 0000000010100000-0000000010100007 (prio 0, i/o): fwcfg.data 0000000010100008-0000000010100009 (prio 0, i/o): fwcfg.ctl 0000000010100010-0000000010100017 (prio 0, i/o): fwcfg.dma 0000000020000000-0000000021ffffff (prio 0, romd): virt.flash0 0000000022000000-0000000023ffffff (prio 0, romd): virt.flash1 0000000030000000-000000003fffffff (prio 0, i/o): alias pcie-ecam @pcie-mmcfg-mmio 0000000000000000-000000000fffffff 0000000040000000-000000007fffffff (prio 0, i/o): alias pcie-mmio @gpex_mmio_window 0000000040000000-000000007fffffff 0000000080000000-0000000087ffffff (prio 0, ram): riscv_virt_board.ram 0000000300000000-00000003ffffffff (prio 0, i/o): alias pcie-mmio-high @gpex_mmio_window 0000000300000000-00000003ffffffff address-space: gpex-root 0000000000000000-ffffffffffffffff (prio 0, i/o): bus master container memory-region: pcie-mmcfg-mmio 0000000000000000-000000001fffffff (prio 0, i/o): pcie-mmcfg-mmio memory-region: gpex_mmio_window 0000000000000000-ffffffffffffffff (prio 0, i/o): gpex_mmio_window 0000000000000000-ffffffffffffffff (prio 0, i/o): gpex_mmio memory-region: system 0000000000000000-ffffffffffffffff (prio 0, i/o): system 0000000000001000-000000000000ffff (prio 0, rom): riscv_virt_board.mrom 0000000000100000-0000000000100fff (prio 0, i/o): riscv.sifive.test 0000000000101000-0000000000101023 (prio 0, i/o): goldfish_rtc 0000000002000000-0000000002003fff (prio 0, i/o): riscv.aclint.swi 0000000002004000-000000000200bfff (prio 0, i/o): riscv.aclint.mtimer 0000000003000000-000000000300ffff (prio 0, i/o): gpex_ioport_window 0000000003000000-000000000300ffff (prio 0, i/o): gpex_ioport 000000000c000000-000000000c20ffff (prio 0, i/o): riscv.sifive.plic 0000000010000000-0000000010000007 (prio 0, i/o): serial 0000000010001000-00000000100011ff (prio 0, i/o): virtio-mmio 0000000010002000-00000000100021ff (prio 0, i/o): virtio-mmio 0000000010003000-00000000100031ff (prio 0, i/o): virtio-mmio 0000000010004000-00000000100041ff (prio 0, i/o): virtio-mmio 0000000010005000-00000000100051ff (prio 0, i/o): virtio-mmio 0000000010006000-00000000100061ff (prio 0, i/o): virtio-mmio 0000000010007000-00000000100071ff (prio 0, i/o): virtio-mmio 0000000010008000-00000000100081ff (prio 0, i/o): virtio-mmio 0000000010100000-0000000010100007 (prio 0, i/o): fwcfg.data 0000000010100008-0000000010100009 (prio 0, i/o): fwcfg.ctl 0000000010100010-0000000010100017 (prio 0, i/o): fwcfg.dma 0000000020000000-0000000021ffffff (prio 0, romd): virt.flash0 0000000022000000-0000000023ffffff (prio 0, romd): virt.flash1 0000000030000000-000000003fffffff (prio 0, i/o): alias pcie-ecam @pcie-mmcfg-mmio 0000000000000000-000000000fffffff 0000000040000000-000000007fffffff (prio 0, i/o): alias pcie-mmio @gpex_mmio_window 0000000040000000-000000007fffffff 0000000080000000-0000000087ffffff (prio 0, ram): riscv_virt_board.ram 0000000300000000-00000003ffffffff (prio 0, i/o): alias pcie-mmio-high @gpex_mmio_window 0000000300000000-00000003ffffffff ```