Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
RISC-V SBI and the full boot process (popovicu.com)
120 points by snvzz on Sept 10, 2023 | hide | past | favorite | 37 comments


It's worth understanding that the initial hard coded (pre OpenSBI) code on a real CPU (rather than an emulator like QEMU) is likely doing a bunch of things:

   - Initialising the DRAM controller and sizing memory
   - optionally doing some crypto auth of the OpenSBI/etc to make sure they're not bogus
   - Copying OpenSBI (and possibly a subsequent U-boot or kernel) into it from ROM


Also the RISC-V ABI requires that the initial code passes a pointer to a minimal device tree to OpenSBI - this means it can tell it where the UART is, how many CPUs to expect, where the interrupt controllers are (and what type they are) etc etc - this can then be passed on to uboot/uefi/linux or overridden or edited as they like


This is mentioned in the article but under the “intentionally skipped details” section. I’m just starting to learn risc-v, but I assume pointing a1 to the device tree would be all you have to do to pass it to OpenSBI.


yes, but you do have to put it somewhere that normal load/stores can reach it, which probably means it's copied to DRAM


Why cannot UART address be fixed by a standard? Is there any merit in having UART at different addresses on different machines?


>Is there any merit in having UART at different addresses on different machines?

Is there any advantage to encumbering the ISA by having a fixed memory address?

If what you need is a debug console, use SBI Debug Console Extension[0].

Otherwise, get the address from the device tree, or from the pointer you've been passed by the SPL, if you are the SBI or you run without SBI.

Note that, in the first place, you will be accessing the UART registers using a register as base + an offset.

0. https://github.com/riscv-non-isa/riscv-sbi-doc/blob/master/s...


> Is there any advantage to encumbering the ISA by having a fixed memory address?

The software becomes much simpler. No need to parse any device trees. When something changes (for example, 128-bit quantum memory becomes available), simply make revision 2 for standard and move device addresses to other place.


You can certainly make some RISC-V platform standard that fixes addresses if you like -- as the PC did by totally copying IBM's machines -- but people want to avoid that.

There was a large ecosystem of MS-DOS computers before the IBM PC took over that had very diverse hardware and depended on the BIOS for compatibility.

We lost a lot when we all had to copy IBM's 640k RAM limit and A20 bug. Other, earlier, MS DOS machines allowed more RAM than that e.g. DEC Rainbow supported 896k of RAM. With a suitable expansion card the Sanyo MBC-550 could make 960k of RAM available to MSDOS.


>No need to parse any device trees.

If you just use the UART address that has been passed directly in a register, there's no need to look at the device tree.

If the UART base address was fixed, you'd normally need to load this base address in a register yourself and offset the hardware registers of the UART from it.

Yet, the way it actually is, this non-fixed base address is already in a register; You need to do even less work.


The problem with this approach is that usually there are more devices that registers. And registers are needed for other purposes rather than constantly storing UART's base address.


What if you have no UARTs? Multiple UARTs?


You can reserve multiple addresses. 64-bit address space is not small. And even 32-bit address space is huge.


Only if you think of it in advance, when you fix the standard for where everything goes.

But how many UARTs should you allow for? 2? 16? 256? No matter what you choose, someone many want to build a system with more, and if you choose 256 (or more) then that's getting to be significant wasted address space for people who only have one UART.

All the more so for things that use a lot more address space than half a dozen control registers for a UART. What about 4k (3840x2560) x32 bit frame buffers? Those are 37.5 MB each. Some people want one. Some people want to drive a wall of monitors.

Much more flexible to let people configure their address space how they want and supply a devicetree in ROM / SPI flash etc.


you could - but you'd have to start defining other stuff like where physical DRAM starts, where other devices like timers, interrupt controllers etc live because they all get reported that way too


So why not reserve fixed addresses for everything? What is the merit of having devices located at different addresses?

For example, on IBM PC devices like keyboard or mouse or beeper had fixed addresses and it caused no issues.


and everyone who made add-in cards had to add jumpers so that they could choose I/O and interrupt addresses that didn't collide, it was a nightmare - PCI (and Nubus before it) solved those problems with forms of geographical addressing, that required meta-data somewhat equivalent to device trees.

RISC-V cores start their physical DRAM addresses at different addresses, some at 0, some at an offset close to 0, some way high. Some embedded systems might have SRAM at one address and DRAM at another - any device addresses have to work around that - if you hard code the UART address then you force people to choose where their memory blocks live, it likely also forces the address range in which other embedded devices live (interrupt controllers, timers etc). RISC-V specs define what the various registers look like, but not where they are (other than offsets wrt other registers of the same type).

Multi-vendor architectural specs like RISC-V are a careful balance between making sure that things work and leaving the design space free for innovation - RISC-V doesn't carry a lot of baggage from previous systems which is a great thing


Does that mean RISC-V doesn't support ACPI at all, even in theory?


Yes there's ACPI & UEFI. Sunil V L from Ventana has been gradually pushing ECRs through the ACPI organization to add features and getting the corresponding stuff into the Linux kernel, eg: https://lore.kernel.org/lkml/20230216182043.1946553-4-sunilv...


There's a RISC-V ACPI spec[0].

Note anything to do with ACPI would run after, not before, OpenSBI.

0. https://github.com/riscv-non-isa/riscv-acpi/releases


Ah, I was thinking OpenSBI was more like the ME for RISC-V, I probably should have googled it.


Yep, and if you're playing with your own homebrew OS or other non-linux OSes you don't really need all of OpenSBI - implementing the equivalent of putchar() and a few timer calls will get you up and running. (Source - did bring up for rv32 simulators running TockOS and seL4).


Are there any highly regarded bringup docs out there? I.e. docs from an OS, emulator, etc. that do a great job of explaining how to get the OS running on new hardware. Or is it usually just done by studying previous code examples and asking experts for help?


Plugging osdev.org in case its new to you.


As it applies to RISC-V, you'll want to get started by looking at the privileged ISA spec, the SBI spec and the platform spec.


you may need the emulation of unaligned memory accesses - which is something else that OpenSBI does for you


> - Initialising the DRAM controller and sizing memory

Hum I don't really understand that one. Based on the article I understood that the code before SBI was directly bootrom, and usually bootrom doesn't init DRAM (at least that's not how rockchip/allwinner/amlogic SoCs behave). So what's the expectation on how SoC vendors implement DDR init code? It's expected that SoC vendor push this code to bootrom (thus costing more in maskrom)?


Depends on what sort of boot rom you have - typically the code before SBI is hard baked into the CPU while SBI, uboot, linux etc are copied into DRAM and run there - this may not be true on embedded systems which may have SRAM for loading initial code into.

These days though SDRAM controllers are complex, and need careful tuning when the power comes on (and the chip heats up), the code that runs cycles to initialise DRAM is often very complex and proprietary.

If you're building a high performance chip you typically have very big wide L1 caches, and relatively wide paths to DRAM - you don't want to hamper them by having to attach them to a much slower 8-bit wide ROM (typically eMMC these days) - better to have boot code do the copy (eMMC looks like a fast SD card) - there are tricks you can do with L1 cache tag management that turns it into an SRAM until DRAM is initialised


Nothing forces it to be in ROM. See for example https://github.com/carlosedp/riscv-bringup/blob/master/unlea... where DRAM init seems to be in the FSBL part which is loaded from the SD card.


you forgot "adding a backdoor for remote access by manufacturer"


I would like to read a brief review of OpenSBI functions, or a rather minimum required set to get Linux like OS to run. How much Linux kernel depends on supervisor ? Also, worth pointing that there's U-Boot which also does some hardware initialization on which kernel relies on.


UBoot typically runs in supervisor mode while OpenSBI runs in machine mode (as would OPTEE) - OpenSBI's (and OPTEE's) memory is locked behind a PMAP block so it can't be hacked from supervisor mode

If your riscv core has hypervisor support that sits between machine mode and supervisor modes


isn's OpenSBI's S means 'supervisor' mode? so OpenSBI is supposed to be in supervisor mode?


Nope, it runs in machine mode (I've ported it) - the S does stand for Supervisor, but I guess that's because it supervises the system


SBI is the ABI for the Supervisor to call to get certain services that can't be done in S mode.


The SBI spec[0] is not a long read.

0. https://github.com/riscv-non-isa/riscv-sbi-doc/releases


Great info, though very verbose!


It is still not the "full" boot process.

Modern hardware need lots of code to initialize from boot to usable.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: