设备树
在前一启动阶段跳转OpenSBI时,可以将设备树的地址通过参数a1
传递过来。
OpenSBI相关的配置(opensbi_config)也可以添加到设备树节点中,OpenSBI执行时会解析和使用这些配置,并在启动结束时删除该节点,节点的示例如下:
chosen {opensbi-config {compatible = "opensbi,config";cold-boot-harts = <&cpu1 &cpu2 &cpu3 &cpu4>;system-suspend-test;};};cpus {#address-cells = <1>;#size-cells = <0>;timebase-frequency = <10000000>;cpu0: cpu@0 {device_type = "cpu";reg = <0x00>;compatible = "riscv";...};cpu1: cpu@1 {device_type = "cpu";reg = <0x01>;compatible = "riscv";...};cpu2: cpu@2 {device_type = "cpu";reg = <0x02>;compatible = "riscv";...};cpu3: cpu@3 {device_type = "cpu";reg = <0x03>;compatible = "riscv";...};cpu4: cpu@4 {device_type = "cpu";reg = <0x04>;compatible = "riscv";...};};uart1: serial@10011000 {...};
其中关于OpenSBI配置节点的属性如下:
- compatible:强制,值须为**“opensbi,config”**
- cold-boot-harts:可选,表示哪些HART支持冷启动
- compatible:可选,如果存在,系统将等待5秒,再进入WFI
设备树导出
以QEMU启动传递给OpenSBI的设备树为例。
方法一
在启动QEMU时,使用 -M virt,dumpdtb=<file.dtb>
参数将设备树以二进制格式.dtb
导出到指定文件。
$ qemu-system-riscv64 -M virt,dumpdtb=qemu-virt.dtb -m 256M -nographic \-bios build/platform/generic/firmware/fw_jump.bin
方法二
当执行到fw_platform_init
时,将arg1
参数地址处的内容导出。
unsigned long fw_platform_init(unsigned long arg0, unsigned long arg1,unsigned long arg2, unsigned long arg3,unsigned long arg4)
{const char *model;void *fdt = (void *)arg1;u32 hartid, hart_count = 0;int rc, root_offset, cpus_offset, cpu_offset, len;root_offset = fdt_path_offset(fdt, "/");......
}
使用GDB的dump
命令,假设arg1
的地址为0x80060000
:
(gdb) dump memory qemu-virt.dtb 0x80060000 0x80064000
导出后的dtb
文件是二进制格式,使用dtc
工具将导出的dtb
文件转换为易读的dts
格式。
$ dtc -I dtb -O dts -o qemu-virt.dts qemu-virt.dtb
设备树分析
查看qemu-virt.dts
设备树的内容如下(有删减):
/dts-v1/;/ {#address-cells = <0x02>;#size-cells = <0x02>;compatible = "riscv-virtio";model = "riscv-virtio,qemu";chosen {bootargs = [00];stdout-path = "/uart@10000000";};uart@10000000 {interrupts = <0x0a>;interrupt-parent = <0x03>;clock-frequency = <0x384000>;reg = <0x00 0x10000000 0x00 0x100>;compatible = "ns16550a";};test@100000 {reg = <0x00 0x100000 0x00 0x1000>;compatible = "sifive,test1\0sifive,test0";};cpus {#address-cells = <0x01>;#size-cells = <0x00>;timebase-frequency = <0x989680>;cpu-map {cluster0 {core0 {cpu = <0x01>;};};};cpu@0 {phandle = <0x01>;device_type = "cpu";reg = <0x00>;status = "okay";compatible = "riscv";riscv,isa = "rv64imafdcsu";mmu-type = "riscv,sv48";interrupt-controller {#interrupt-cells = <0x01>;interrupt-controller;compatible = "riscv,cpu-intc";phandle = <0x02>;};};};memory@80000000 {device_type = "memory";reg = <0x00 0x80000000 0x00 0x10000000>;};soc {#address-cells = <0x02>;#size-cells = <0x02>;compatible = "simple-bus";ranges;interrupt-controller@c000000 {phandle = <0x03>;riscv,ndev = <0x35>;reg = <0x00 0xc000000 0x00 0x4000000>;interrupts-extended = <0x02 0x0b 0x02 0x09>;interrupt-controller;compatible = "riscv,plic0";#interrupt-cells = <0x01>;#address-cells = <0x00>;};clint@2000000 {interrupts-extended = <0x02 0x03 0x02 0x07>;reg = <0x00 0x2000000 0x00 0x10000>;compatible = "riscv,clint0";};};
};
从上面内容可以得出:
- 平台设备为
riscv-virtio,qemu
- UART兼容
ns16550a
设备,基地址为0x10000000
,这个串口也是OpenSBI的打印终端 - CLINT本地中断控制器兼容
riscv,clint0
,基地址为0x02000000
- PLIC中断控制器兼容
riscv,plic0
,基地址为0x0c000000
- 内存空间基地址为
0x80000000
,其也是OpenSBI固件的链接地址 - HART个数为1
上面这些信息其实来自QEMU Virt定义的地址空间布局:
static const MemMapEntry virt_memmap[] = {[VIRT_DEBUG] = { 0x0, 0x100 },[VIRT_MROM] = { 0x1000, 0xf000 },[VIRT_TEST] = { 0x100000, 0x1000 },[VIRT_RTC] = { 0x101000, 0x1000 },[VIRT_CLINT] = { 0x2000000, 0x10000 },[VIRT_ACLINT_SSWI] = { 0x2F00000, 0x4000 },[VIRT_PCIE_PIO] = { 0x3000000, 0x10000 },[VIRT_PLATFORM_BUS] = { 0x4000000, 0x2000000 },[VIRT_PLIC] = { 0xc000000, VIRT_PLIC_SIZE(VIRT_CPUS_MAX * 2) },[VIRT_APLIC_M] = { 0xc000000, APLIC_SIZE(VIRT_CPUS_MAX) },[VIRT_APLIC_S] = { 0xd000000, APLIC_SIZE(VIRT_CPUS_MAX) },[VIRT_UART0] = { 0x10000000, 0x100 },[VIRT_VIRTIO] = { 0x10001000, 0x1000 },[VIRT_FW_CFG] = { 0x10100000, 0x18 },[VIRT_FLASH] = { 0x20000000, 0x4000000 },[VIRT_IMSIC_M] = { 0x24000000, VIRT_IMSIC_MAX_SIZE },[VIRT_IMSIC_S] = { 0x28000000, VIRT_IMSIC_MAX_SIZE },[VIRT_PCIE_ECAM] = { 0x30000000, 0x10000000 },[VIRT_PCIE_MMIO] = { 0x40000000, 0x40000000 },[VIRT_DRAM] = { 0x80000000, 0x0 },
};
代码
OpenSBI在启动时会解析设备树,代码如下:
/** The fw_platform_init() function is called very early on the boot HART* OpenSBI reference firmwares so that platform specific code get chance* to update "platform" instance before it is used.** The arguments passed to fw_platform_init() function are boot time state* of A0 to A4 register. The "arg0" will be boot HART id and "arg1" will* be address of FDT passed by previous booting stage.** The return value of fw_platform_init() function is the FDT location. If* FDT is unchanged (or FDT is modified in-place) then fw_platform_init()* can always return the original FDT location (i.e. 'arg1') unmodified.*/
unsigned long fw_platform_init(unsigned long arg0, unsigned long arg1,unsigned long arg2, unsigned long arg3,unsigned long arg4)
{const char *model;void *fdt = (void *)arg1;u32 hartid, hart_count = 0;int rc, root_offset, cpus_offset, cpu_offset, len;root_offset = fdt_path_offset(fdt, "/");if (root_offset < 0)goto fail;fw_platform_lookup_special(fdt, root_offset);if (generic_plat && generic_plat->fw_init)generic_plat->fw_init(fdt, generic_plat_match);model = fdt_getprop(fdt, root_offset, "model", &len);if (model)sbi_strncpy(platform.name, model, sizeof(platform.name) - 1);if (generic_plat && generic_plat->features)platform.features = generic_plat->features(generic_plat_match);cpus_offset = fdt_path_offset(fdt, "/cpus");if (cpus_offset < 0)goto fail;fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) {rc = fdt_parse_hart_id(fdt, cpu_offset, &hartid);if (rc)continue;if (SBI_HARTMASK_MAX_BITS <= hartid)continue;if (!fdt_node_is_enabled(fdt, cpu_offset))continue;generic_hart_index2id[hart_count++] = hartid;}platform.hart_count = hart_count;platform.heap_size = fw_platform_calculate_heap_size(hart_count);platform_has_mlevel_imsic = fdt_check_imsic_mlevel(fdt);fw_platform_coldboot_harts_init(fdt);/* Return original FDT pointer */return arg1;fail:while (1)wfi();
}
上面代码先解析了平台名、HART个数等,最后调用fw_platform_coldboot_harts_init
解析出冷启动的HART ID,如下:
/** The fw_platform_coldboot_harts_init() function is called by fw_platform_init() * function to initialize the cold boot harts allowed by the generic platform* according to the DT property "cold-boot-harts" in "/chosen/opensbi-config" * DT node. If there is no "cold-boot-harts" in DT, all harts will be allowed.*/
static void fw_platform_coldboot_harts_init(void *fdt)
{int chosen_offset, config_offset, cpu_offset, len, err;u32 val32;const u32 *val;bitmap_zero(generic_coldboot_harts, SBI_HARTMASK_MAX_BITS);chosen_offset = fdt_path_offset(fdt, "/chosen");if (chosen_offset < 0)goto default_config;config_offset = fdt_node_offset_by_compatible(fdt, chosen_offset, "opensbi,config");if (config_offset < 0)goto default_config;val = fdt_getprop(fdt, config_offset, "cold-boot-harts", &len);len = len / sizeof(u32);if (val && len) {for (int i = 0; i < len; i++) {cpu_offset = fdt_node_offset_by_phandle(fdt,fdt32_to_cpu(val[i]));if (cpu_offset < 0)goto default_config;err = fdt_parse_hart_id(fdt, cpu_offset, &val32);if (err)goto default_config;if (!fdt_node_is_enabled(fdt, cpu_offset))continue;for (int i = 0; i < platform.hart_count; i++) {if (val32 == generic_hart_index2id[i])bitmap_set(generic_coldboot_harts, i, 1);}}}return;default_config:bitmap_fill(generic_coldboot_harts, SBI_HARTMASK_MAX_BITS);return;
}
由于上面的qemu-virt.dts
设备树并未包括OpenSBI节点,因此所有HARTs均允许冷启动。
再结合OpenSBI打印信息看下,其确实是解析了设备数,如Platform Name
、Platform HART Count
和Domain使用的地址信息等:
OpenSBI v1.5____ _____ ____ _____/ __ \ / ____| _ \_ _|| | | |_ __ ___ _ __ | (___ | |_) || || | | | '_ \ / _ \ '_ \ \___ \| _ < | || |__| | |_) | __/ | | |____) | |_) || |_\____/| .__/ \___|_| |_|_____/|____/_____|| ||_|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 : uart8250
Platform HSM Device : ---
Platform PMU Device : ---
Platform Reboot Device : ---
Platform Shutdown Device : ---
Platform Suspend Device : ---
Platform CPPC Device : ---
Firmware Base : 0x80000000
Firmware Size : 327 KB
Firmware RW Offset : 0x40000
Firmware RW Size : 71 KB
Firmware Heap Offset : 0x49000
Firmware Heap Size : 35 KB (total), 2 KB (reserved), 9 KB (used), 23 KB (free)
Firmware Scratch Size : 4096 B (total), 408 B (used), 3688 B (free)
Runtime SBI Version : 2.0Domain0 Name : root
Domain0 Boot HART : 0
Domain0 HARTs : 0*
Domain0 Region00 : 0x0000000010000000-0x0000000010000fff M: (I,R,W) S/U: (R,W)
Domain0 Region01 : 0x0000000002000000-0x000000000200ffff M: (I,R,W) S/U: ()
Domain0 Region02 : 0x0000000080040000-0x000000008005ffff M: (R,W) S/U: ()
Domain0 Region03 : 0x0000000080000000-0x000000008003ffff M: (R,X) S/U: ()
Domain0 Region04 : 0x000000000c000000-0x000000000fffffff M: (I,R,W) S/U: (R,W)
Domain0 Region05 : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X)
参考
- opensbi_config