diff mbox series

Add some documentation for "dtb" devices tree blobs

Message ID 20220625223458.1273408-1-simon.sapin@exyr.org
State New
Headers show
Series Add some documentation for "dtb" devices tree blobs | expand

Commit Message

Simon Sapin June 25, 2022, 10:34 p.m. UTC
Signed-off-by: Simon Sapin <simon.sapin@exyr.org>
---
 docs/specs/device-trees.rst                | 57 ++++++++++++++++++++++
 docs/specs/index.rst                       |  1 +
 docs/system/arm/virt.rst                   |  5 +-
 docs/system/arm/xlnx-versal-virt.rst       |  3 +-
 docs/system/ppc/ppce500.rst                |  3 +-
 docs/system/riscv/microchip-icicle-kit.rst |  3 +-
 docs/system/riscv/sifive_u.rst             |  3 +-
 docs/system/riscv/virt.rst                 |  3 +-
 qemu-options.hx                            |  5 ++
 9 files changed, 77 insertions(+), 6 deletions(-)
 create mode 100644 docs/specs/device-trees.rst

Comments

Simon Sapin June 25, 2022, 11:03 p.m. UTC | #1
On 26/06/2022 00:34, Simon Sapin wrote:
> +On startup, the dtb is memory-mapped and its address is passed to the guest
> +in a target-specific way:
> +
> +* Arm: :ref:`arm-baremetal`
> +* **TODO**: document other targets

Hello,

My current interest is playing with bare-metal / freestanding RISC-V, using QEMU as a 
reference emulator. Based on various blog posts, reading QEMU source code, and lots 
of trial-and-error I’ve managed to get something running[1] but it wasn’t easy.

In comparison, the docs for Arm virt have a very helpful section[2] for this 
scenario. I would like to contribute similar docs for RISC-V virt but I’d need 
confirmation of the information to put in it:

* Through `dumpdtb` I see that flash memory starts at address 0x2_000_0000, and RAM 
at 0x8_000_0000. Is this information that guest code can rely on and hard-code? What 
details can or cannot be similarly relied on?

* With `qemu-system-riscv32 -machine virt -bios none -kernel something.elf -s -S`, 
GDB shows that execution starts at the lowest address of RAM, not of flash like I 
expected. Then what is emulated flash for?

* What’s the difference between a bios and a kernel? The previous command is based on 
a blog post but I don’t fully quite the details.

* I see in source code[3] that QEMU passes some arguments to the firmware. Register 
a0 gets the hart ID, a1 is the dtb address, but what’s in a2?

* To what extent is the above calling convention standardized? I found similar things 
in coreboot[4] and in OpenSBI[5]


[1] https://github.com/SimonSapin/riscv-qemu-demo

[2] 
https://www.qemu.org/docs/master/system/arm/virt.html#hardware-configuration-information-for-bare-metal-programming

[3] https://gitlab.com/qemu-project/qemu/-/blob/v7.0.0/hw/riscv/boot.c#L297-317

[4] https://doc.coreboot.org/arch/riscv/index.html#stage-handoff-protocol

[5] 
https://github.com/riscv-software-src/opensbi/blob/v1.1/platform/generic/platform.c#L59-L75


Thanks!
Simon Sapin June 25, 2022, 11:10 p.m. UTC | #2
On 26/06/2022 00:34, Simon Sapin wrote:
> diff --git qemu-options.hx qemu-options.hx
> index 377d22fbd8..eea75ddb37 100644
> --- qemu-options.hx
> +++ qemu-options.hx
> @@ -38,6 +38,7 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \
>       "                hmat=on|off controls ACPI HMAT support (default=off)\n"
>       "                memory-backend='backend-id' specifies explicitly provided backend for main RAM (default=none)\n"
>       "                cxl-fmw.0.targets.0=firsttarget,cxl-fmw.0.targets.1=secondtarget,cxl-fmw.0.size=size[,cxl-fmw.0.interleave-granularity=granularity]\n",
> +    "                dumpdtb=file dump current device tree blob to a file and quit\n"
>       QEMU_ARCH_ALL)
>   SRST
>   ``-machine [type=]name[,prop=value[,...]]``


Before I added the above chunk I found no trace of `dumpdtb` in --help or Sphinx 
docs, but a code search finds two C string literals that seemed to already to 
document its existence:

https://gitlab.com/qemu-project/qemu/-/blob/v7.0.0/util/qemu-config.c#L192
https://gitlab.com/qemu-project/qemu/-/blob/v7.0.0/hw/core/machine.c#L810

However I had trouble to find in the code: where are these strings used? Are they 
ever shown to a user?
Alistair Francis June 27, 2022, 5:25 a.m. UTC | #3
On Sun, Jun 26, 2022 at 8:40 AM Simon Sapin <simon.sapin@exyr.org> wrote:
>
> Signed-off-by: Simon Sapin <simon.sapin@exyr.org>

Reviewed-by: Alistair Francis <alistair.francis@wdc.com>

Alistair

> ---
>  docs/specs/device-trees.rst                | 57 ++++++++++++++++++++++
>  docs/specs/index.rst                       |  1 +
>  docs/system/arm/virt.rst                   |  5 +-
>  docs/system/arm/xlnx-versal-virt.rst       |  3 +-
>  docs/system/ppc/ppce500.rst                |  3 +-
>  docs/system/riscv/microchip-icicle-kit.rst |  3 +-
>  docs/system/riscv/sifive_u.rst             |  3 +-
>  docs/system/riscv/virt.rst                 |  3 +-
>  qemu-options.hx                            |  5 ++
>  9 files changed, 77 insertions(+), 6 deletions(-)
>  create mode 100644 docs/specs/device-trees.rst
>
> diff --git docs/specs/device-trees.rst docs/specs/device-trees.rst
> new file mode 100644
> index 0000000000..8160342124
> --- /dev/null
> +++ docs/specs/device-trees.rst
> @@ -0,0 +1,57 @@
> +============
> +Device Trees
> +============
> +
> +On some targets, guests can find out what devices are emulated and how to access them
> +through a *Device Tree Blob* (dtb), also called *Flattened Device Tree* (fdt).
> +The dtb can be passed by the user through the ``-dtb file`` command-line options,
> +or automatically generated by QEMU.
> +
> +Host: dumping the dtb
> +=====================
> +
> +The (possibly generated) dtb can be written to a file with
> +the ``dumpdtb`` property of the ``machine`` command-line option.
> +Then `dtc <Device Tree Compiler_>`_ can convert it to Device Tree Source text "dts" format
> +For example::
> +
> +    qemu-system-riscv32 -machine virt,dumpdtb=rv32-virt.dtb
> +    dtc -q rv32-virt.dtb -o rv32-virt.dts
> +    head -n 7 rv32-virt.dts
> +
> +::
> +
> +    qemu-system-riscv32: info: dtb dumped to rv32-virt.dtb. Exiting.
> +    /dts-v1/;
> +
> +    / {
> +        #address-cells = <0x02>;
> +        #size-cells = <0x02>;
> +        compatible = "riscv-virtio";
> +        model = "riscv-virtio,qemu";
> +
> +Guest: finding the dtb
> +======================
> +
> +On startup, the dtb is memory-mapped and its address is passed to the guest
> +in a target-specific way:
> +
> +* Arm: :ref:`arm-baremetal`
> +* **TODO**: document other targets
> +
> +Resources
> +=========
> +
> +* `Devicetree Specification <https://www.devicetree.org/specifications/>`_.
> +
> +* Embedded Linux Wiki:
> +
> +  - `Device Tree: What It Is <https://elinux.org/Device_Tree_What_It_Is>`_
> +  - `Device Tree Usage <https://elinux.org/Device_Tree_Usage>`_
> +
> +* `Device Tree Compiler <https://git.kernel.org/pub/scm/utils/dtc/dtc.git>`_:
> +
> +  - ``dtc`` CLI tool (package name might be ``device-tree-compiler``)
> +  - ``libfdt`` C library
> +
> +* ``fdt`` `Rust library <https://crates.io/crates/fdt>`_
> diff --git docs/specs/index.rst docs/specs/index.rst
> index a58d9311cb..3bd69305e2 100644
> --- docs/specs/index.rst
> +++ docs/specs/index.rst
> @@ -8,6 +8,7 @@ guest hardware that is specific to QEMU.
>  .. toctree::
>     :maxdepth: 2
>
> +   device-trees
>     ppc-xive
>     ppc-spapr-xive
>     ppc-spapr-numa
> diff --git docs/system/arm/virt.rst docs/system/arm/virt.rst
> index 3d1058a80c..04a90df613 100644
> --- docs/system/arm/virt.rst
> +++ docs/system/arm/virt.rst
> @@ -153,10 +153,13 @@ need::
>    CONFIG_DRM=y
>    CONFIG_DRM_VIRTIO_GPU=y
>
> +.. _arm-baremetal:
> +
>  Hardware configuration information for bare-metal programming
>  """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
>
> -The ``virt`` board automatically generates a device tree blob ("dtb")
> +The ``virt`` board automatically generates a
> +:doc:`device tree blob ("dtb") </specs/device-trees>`
>  which it passes to the guest. This provides information about the
>  addresses, interrupt lines and other configuration of the various devices
>  in the system. Guest code can rely on and hard-code the following
> diff --git docs/system/arm/xlnx-versal-virt.rst docs/system/arm/xlnx-versal-virt.rst
> index 92ad10d2da..3387c74bfa 100644
> --- docs/system/arm/xlnx-versal-virt.rst
> +++ docs/system/arm/xlnx-versal-virt.rst
> @@ -53,7 +53,8 @@ to use the ``-kernel`` command line option.
>
>  Users can load firmware or boot-loaders with the ``-device loader`` options.
>
> -When loading an OS, QEMU generates a DTB and selects an appropriate address
> +When loading an OS, QEMU generates a :doc:`DTB </specs/device-trees>`
> +and selects an appropriate address
>  where it gets loaded. This DTB will be passed to the kernel in register x0.
>
>  If there's no ``-kernel`` option, we generate a DTB and place it at 0x1000
> diff --git docs/system/ppc/ppce500.rst docs/system/ppc/ppce500.rst
> index 9beef39171..24fd91a084 100644
> --- docs/system/ppc/ppce500.rst
> +++ docs/system/ppc/ppce500.rst
> @@ -24,7 +24,8 @@ The ``ppce500`` machine supports the following devices:
>  Hardware configuration information
>  ----------------------------------
>
> -The ``ppce500`` machine automatically generates a device tree blob ("dtb")
> +The ``ppce500`` machine automatically generates a
> +:doc:`device tree blob ("dtb") </specs/device-trees>`
>  which it passes to the guest, if there is no ``-dtb`` option. This provides
>  information about the addresses, interrupt lines and other configuration of
>  the various devices in the system.
> diff --git docs/system/riscv/microchip-icicle-kit.rst docs/system/riscv/microchip-icicle-kit.rst
> index 40798b1aae..a6c8b46263 100644
> --- docs/system/riscv/microchip-icicle-kit.rst
> +++ docs/system/riscv/microchip-icicle-kit.rst
> @@ -37,7 +37,8 @@ can be loaded from U-Boot. It also supports direct kernel booting via the
>  boot is used, the OpenSBI fw_dynamic BIOS image is used to boot a payload
>  like U-Boot or OS kernel directly.
>
> -The user provided DTB should have the following requirements:
> +The user provided :doc:`DTB </specs/device-trees>`
> +should have the following requirements:
>
>  * The /cpus node should contain at least one subnode for E51 and the number
>    of subnodes should match QEMU's ``-smp`` option
> diff --git docs/system/riscv/sifive_u.rst docs/system/riscv/sifive_u.rst
> index 7b166567f9..fd70ee8278 100644
> --- docs/system/riscv/sifive_u.rst
> +++ docs/system/riscv/sifive_u.rst
> @@ -36,7 +36,8 @@ testing of 32-bit guest software.
>  Hardware configuration information
>  ----------------------------------
>
> -The ``sifive_u`` machine automatically generates a device tree blob ("dtb")
> +The ``sifive_u`` machine automatically generates a
> +:doc:`device tree blob ("dtb") </specs/device-trees>`
>  which it passes to the guest, if there is no ``-dtb`` option. This provides
>  information about the addresses, interrupt lines and other configuration of
>  the various devices in the system. Guest software should discover the devices
> diff --git docs/system/riscv/virt.rst docs/system/riscv/virt.rst
> index f8ecec95f3..81ea53eb20 100644
> --- docs/system/riscv/virt.rst
> +++ docs/system/riscv/virt.rst
> @@ -30,7 +30,8 @@ declaring.
>  Hardware configuration information
>  ----------------------------------
>
> -The ``virt`` machine automatically generates a device tree blob ("dtb")
> +The ``virt`` machine automatically generates a
> +:doc:`device tree blob ("dtb") </specs/device-trees>`
>  which it passes to the guest, if there is no ``-dtb`` option. This provides
>  information about the addresses, interrupt lines and other configuration of
>  the various devices in the system. Guest software should discover the devices
> diff --git qemu-options.hx qemu-options.hx
> index 377d22fbd8..eea75ddb37 100644
> --- qemu-options.hx
> +++ qemu-options.hx
> @@ -38,6 +38,7 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \
>      "                hmat=on|off controls ACPI HMAT support (default=off)\n"
>      "                memory-backend='backend-id' specifies explicitly provided backend for main RAM (default=none)\n"
>      "                cxl-fmw.0.targets.0=firsttarget,cxl-fmw.0.targets.1=secondtarget,cxl-fmw.0.size=size[,cxl-fmw.0.interleave-granularity=granularity]\n",
> +    "                dumpdtb=file dump current device tree blob to a file and quit\n"
>      QEMU_ARCH_ALL)
>  SRST
>  ``-machine [type=]name[,prop=value[,...]]``
> @@ -157,6 +158,10 @@ SRST
>          ::
>
>              -machine cxl-fmw.0.targets.0=cxl.0,cxl-fmw.0.targets.1=cxl.1,cxl-fmw.0.size=128G,cxl-fmw.0.interleave-granularity=512k
> +
> +    ``dumpdtb=file``
> +        Dump the current :doc:`Device Tree Blob </specs/device-trees>`
> +        to the give file name, then exit.
>  ERST
>
>  DEF("M", HAS_ARG, QEMU_OPTION_M,
> --
> 2.36.1
>
>
Alistair Francis June 27, 2022, 5:40 a.m. UTC | #4
On Sun, Jun 26, 2022 at 9:04 AM Simon Sapin <simon.sapin@exyr.org> wrote:
>
> On 26/06/2022 00:34, Simon Sapin wrote:
> > +On startup, the dtb is memory-mapped and its address is passed to the guest
> > +in a target-specific way:
> > +
> > +* Arm: :ref:`arm-baremetal`
> > +* **TODO**: document other targets
>
> Hello,

Hello

>
> My current interest is playing with bare-metal / freestanding RISC-V, using QEMU as a
> reference emulator. Based on various blog posts, reading QEMU source code, and lots
> of trial-and-error I’ve managed to get something running[1] but it wasn’t easy.

Written in Rust as well, nice!

I'm sorry to hear that you had so much trouble getting started with
RISC-V QEMU. We do try to make it easy, but everyone is busy and
documentation usually ends up being the last thing we do.

>
> In comparison, the docs for Arm virt have a very helpful section[2] for this
> scenario. I would like to contribute similar docs for RISC-V virt but I’d need
> confirmation of the information to put in it:
>
> * Through `dumpdtb` I see that flash memory starts at address 0x2_000_0000, and RAM
> at 0x8_000_0000. Is this information that guest code can rely on and hard-code? What
> details can or cannot be similarly relied on?

Good question.

So first up, you can see all of the memory mappings in the
hw/riscv/virt.c file, if you find that easier than dumping device
trees.

We have previously kept the addresses backwards compatible. So that
software for an older virt machine will work on a newer one. There is
currently talks about changing the virt machine memory layout in a
breaking way and versioning in the current one though.

So I don't really have a good answer for you. I would recommend
reading as much as possible from the device tree dynamically at boot.

In general though we don't want to break people, we just might have to
make changes in the future to allow for new functionality.

>
> * With `qemu-system-riscv32 -machine virt -bios none -kernel something.elf -s -S`,
> GDB shows that execution starts at the lowest address of RAM, not of flash like I
> expected. Then what is emulated flash for?

If you supply a flash image we will start executing from flash automatically.

>
> * What’s the difference between a bios and a kernel? The previous command is based on
> a blog post but I don’t fully quite the details.

For a bare metal setup like yours there isn't really a difference. We
use -bios to specify the OpenSBI firmware and -kernel to specify a
Linux kernel. For your use you can use `-bios none -kernel ...`

>
> * I see in source code[3] that QEMU passes some arguments to the firmware. Register
> a0 gets the hart ID, a1 is the dtb address, but what’s in a2?

a2 stores the "dynamic firmware info" which is used by OpenSBI. The
riscv_rom_copy_firmware_info() copies the data to memory

>
> * To what extent is the above calling convention standardized? I found similar things
> in coreboot[4] and in OpenSBI[5]

Good question. I don't think it's specified in a spec, but it is very common

Alistair

>
>
> [1] https://github.com/SimonSapin/riscv-qemu-demo
>
> [2]
> https://www.qemu.org/docs/master/system/arm/virt.html#hardware-configuration-information-for-bare-metal-programming
>
> [3] https://gitlab.com/qemu-project/qemu/-/blob/v7.0.0/hw/riscv/boot.c#L297-317
>
> [4] https://doc.coreboot.org/arch/riscv/index.html#stage-handoff-protocol
>
> [5]
> https://github.com/riscv-software-src/opensbi/blob/v1.1/platform/generic/platform.c#L59-L75
>
>
> Thanks!
> --
> Simon Sapin
>
Simon Sapin June 27, 2022, 6:15 a.m. UTC | #5
On 27/06/2022 07:40, Alistair Francis wrote:
> We have previously kept the addresses backwards compatible. So that
> software for an older virt machine will work on a newer one. There is
> currently talks about changing the virt machine memory layout in a
> breaking way and versioning in the current one though.
> 
> So I don't really have a good answer for you. I would recommend
> reading as much as possible from the device tree dynamically at boot.
> 
> In general though we don't want to break people, we just might have to
> make changes in the future to allow for new functionality.

I agree that reading from the device tree as much as possible is good. We there’s 
still a need to get code running at all, and finding the device tree.

So it would be good to decide to make stable what’s needed to get there (like was 
apparently decided for ARM) and document it.

On principle maybe a firmware/bootloader could be entirely position-independent? But 
in what I’ve done/seen so far https://docs.rs/riscv-rt/latest/riscv_rt/ has address 
ranges hard-coded in a linker script for different regions, and when passing an ELF 
file to -kernel, QEMU maps it to those addresses but boots at 0x8000_0000 regardless.


>> * With `qemu-system-riscv32 -machine virt -bios none -kernel something.elf -s -S`,
>> GDB shows that execution starts at the lowest address of RAM, not of flash like I
>> expected. Then what is emulated flash for?
> 
> If you supply a flash image we will start executing from flash automatically.

Passing with -drive? Should I use that instead of -kernel?


>> * To what extent is the above calling convention standardized? I found similar things
>> in coreboot[4] and in OpenSBI[5]
> 
> Good question. I don't think it's specified in a spec, but it is very common

Should we document this convention as something guest code can rely on?
Peter Maydell July 1, 2022, 9:58 a.m. UTC | #6
On Sat, 25 Jun 2022 at 23:39, Simon Sapin <simon.sapin@exyr.org> wrote:
>
> Signed-off-by: Simon Sapin <simon.sapin@exyr.org>
> ---
>  docs/specs/device-trees.rst                | 57 ++++++++++++++++++++++
>  docs/specs/index.rst                       |  1 +
>  docs/system/arm/virt.rst                   |  5 +-
>  docs/system/arm/xlnx-versal-virt.rst       |  3 +-
>  docs/system/ppc/ppce500.rst                |  3 +-
>  docs/system/riscv/microchip-icicle-kit.rst |  3 +-
>  docs/system/riscv/sifive_u.rst             |  3 +-
>  docs/system/riscv/virt.rst                 |  3 +-
>  qemu-options.hx                            |  5 ++
>  9 files changed, 77 insertions(+), 6 deletions(-)
>  create mode 100644 docs/specs/device-trees.rst

Hi; thanks for writing this documentation.

> diff --git docs/specs/device-trees.rst docs/specs/device-trees.rst
> new file mode 100644
> index 0000000000..8160342124
> --- /dev/null
> +++ docs/specs/device-trees.rst
> @@ -0,0 +1,57 @@
> +============
> +Device Trees
> +============
> +
> +On some targets, guests can find out what devices are emulated and how to access them
> +through a *Device Tree Blob* (dtb), also called *Flattened Device Tree* (fdt).
> +The dtb can be passed by the user through the ``-dtb file`` command-line options,
> +or automatically generated by QEMU.
> +
> +Host: dumping the dtb
> +=====================
> +
> +The (possibly generated) dtb can be written to a file with
> +the ``dumpdtb`` property of the ``machine`` command-line option.
> +Then `dtc <Device Tree Compiler_>`_ can convert it to Device Tree Source text "dts" format
> +For example::
> +
> +    qemu-system-riscv32 -machine virt,dumpdtb=rv32-virt.dtb
> +    dtc -q rv32-virt.dtb -o rv32-virt.dts
> +    head -n 7 rv32-virt.dts
> +
> +::
> +
> +    qemu-system-riscv32: info: dtb dumped to rv32-virt.dtb. Exiting.
> +    /dts-v1/;
> +
> +    / {
> +        #address-cells = <0x02>;
> +        #size-cells = <0x02>;
> +        compatible = "riscv-virtio";
> +        model = "riscv-virtio,qemu";
> +
> +Guest: finding the dtb
> +======================
> +
> +On startup, the dtb is memory-mapped and its address is passed to the guest
> +in a target-specific way:
> +
> +* Arm: :ref:`arm-baremetal`
> +* **TODO**: document other targets

Don't leave TODO notes in the docs, please.

Also, the DTB is not necessarily memory-mapped. For instance on
Arm platforms if the user requests a bios/firmware image to be
run, then we put the dtb (like the kernel and initrd) in the
fw_cfg device for the firmware to extract.

> diff --git qemu-options.hx qemu-options.hx
> index 377d22fbd8..eea75ddb37 100644
> --- qemu-options.hx
> +++ qemu-options.hx
> @@ -38,6 +38,7 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \
>      "                hmat=on|off controls ACPI HMAT support (default=off)\n"
>      "                memory-backend='backend-id' specifies explicitly provided backend for main RAM (default=none)\n"
>      "                cxl-fmw.0.targets.0=firsttarget,cxl-fmw.0.targets.1=secondtarget,cxl-fmw.0.size=size[,cxl-fmw.0.interleave-granularity=granularity]\n",
> +    "                dumpdtb=file dump current device tree blob to a file and quit\n"
>      QEMU_ARCH_ALL)
>  SRST
>  ``-machine [type=]name[,prop=value[,...]]``
> @@ -157,6 +158,10 @@ SRST
>          ::
>
>              -machine cxl-fmw.0.targets.0=cxl.0,cxl-fmw.0.targets.1=cxl.1,cxl-fmw.0.size=128G,cxl-fmw.0.interleave-granularity=512k
> +
> +    ``dumpdtb=file``
> +        Dump the current :doc:`Device Tree Blob </specs/device-trees>`
> +        to the give file name, then exit.

 to the given file name, then exit QEMU without running the guest.
 This is primarily useful for debugging, as it allows you to see the
 contents of the DTB including all additions and modifications that
 QEMU makes before handing it to the guest.

 This option is ignored on machine types which do not use a DTB.

>  ERST
>
>  DEF("M", HAS_ARG, QEMU_OPTION_M,


thanks
-- PMM
Alistair Francis July 12, 2022, 4:53 a.m. UTC | #7
On Mon, Jun 27, 2022 at 4:15 PM Simon Sapin <simon.sapin@exyr.org> wrote:
>
> On 27/06/2022 07:40, Alistair Francis wrote:
> > We have previously kept the addresses backwards compatible. So that
> > software for an older virt machine will work on a newer one. There is
> > currently talks about changing the virt machine memory layout in a
> > breaking way and versioning in the current one though.
> >
> > So I don't really have a good answer for you. I would recommend
> > reading as much as possible from the device tree dynamically at boot.
> >
> > In general though we don't want to break people, we just might have to
> > make changes in the future to allow for new functionality.
>
> I agree that reading from the device tree as much as possible is good. We there’s
> still a need to get code running at all, and finding the device tree.
>
> So it would be good to decide to make stable what’s needed to get there (like was
> apparently decided for ARM) and document it.

Yeah, we are working towards that

>
> On principle maybe a firmware/bootloader could be entirely position-independent? But

I don't link the RISC-V toolchains suppor fully position independent code

> in what I’ve done/seen so far https://docs.rs/riscv-rt/latest/riscv_rt/ has address
> ranges hard-coded in a linker script for different regions, and when passing an ELF
> file to -kernel, QEMU maps it to those addresses but boots at 0x8000_0000 regardless.

Yeah, I suspect we will keep the 0x8000_0000 as that's pretty standard

>
>
> >> * With `qemu-system-riscv32 -machine virt -bios none -kernel something.elf -s -S`,
> >> GDB shows that execution starts at the lowest address of RAM, not of flash like I
> >> expected. Then what is emulated flash for?
> >
> > If you supply a flash image we will start executing from flash automatically.
>
> Passing with -drive? Should I use that instead of -kernel?

If you want to pass a drive then yes, that's the better option

>
>
> >> * To what extent is the above calling convention standardized? I found similar things
> >> in coreboot[4] and in OpenSBI[5]
> >
> > Good question. I don't think it's specified in a spec, but it is very common
>
> Should we document this convention as something guest code can rely on?

We probably should at some point

Alistair

>
> --
> Simon Sapin
diff mbox series

Patch

diff --git docs/specs/device-trees.rst docs/specs/device-trees.rst
new file mode 100644
index 0000000000..8160342124
--- /dev/null
+++ docs/specs/device-trees.rst
@@ -0,0 +1,57 @@ 
+============
+Device Trees
+============
+
+On some targets, guests can find out what devices are emulated and how to access them
+through a *Device Tree Blob* (dtb), also called *Flattened Device Tree* (fdt).
+The dtb can be passed by the user through the ``-dtb file`` command-line options,
+or automatically generated by QEMU.
+
+Host: dumping the dtb
+=====================
+
+The (possibly generated) dtb can be written to a file with
+the ``dumpdtb`` property of the ``machine`` command-line option.
+Then `dtc <Device Tree Compiler_>`_ can convert it to Device Tree Source text "dts" format
+For example::
+
+    qemu-system-riscv32 -machine virt,dumpdtb=rv32-virt.dtb
+    dtc -q rv32-virt.dtb -o rv32-virt.dts
+    head -n 7 rv32-virt.dts
+
+::
+
+    qemu-system-riscv32: info: dtb dumped to rv32-virt.dtb. Exiting.
+    /dts-v1/;
+
+    / {
+        #address-cells = <0x02>;
+        #size-cells = <0x02>;
+        compatible = "riscv-virtio";
+        model = "riscv-virtio,qemu";
+
+Guest: finding the dtb
+======================
+
+On startup, the dtb is memory-mapped and its address is passed to the guest
+in a target-specific way:
+
+* Arm: :ref:`arm-baremetal`
+* **TODO**: document other targets
+
+Resources
+=========
+
+* `Devicetree Specification <https://www.devicetree.org/specifications/>`_.
+
+* Embedded Linux Wiki:
+
+  - `Device Tree: What It Is <https://elinux.org/Device_Tree_What_It_Is>`_
+  - `Device Tree Usage <https://elinux.org/Device_Tree_Usage>`_
+
+* `Device Tree Compiler <https://git.kernel.org/pub/scm/utils/dtc/dtc.git>`_:
+
+  - ``dtc`` CLI tool (package name might be ``device-tree-compiler``)
+  - ``libfdt`` C library
+
+* ``fdt`` `Rust library <https://crates.io/crates/fdt>`_
diff --git docs/specs/index.rst docs/specs/index.rst
index a58d9311cb..3bd69305e2 100644
--- docs/specs/index.rst
+++ docs/specs/index.rst
@@ -8,6 +8,7 @@  guest hardware that is specific to QEMU.
 .. toctree::
    :maxdepth: 2
 
+   device-trees
    ppc-xive
    ppc-spapr-xive
    ppc-spapr-numa
diff --git docs/system/arm/virt.rst docs/system/arm/virt.rst
index 3d1058a80c..04a90df613 100644
--- docs/system/arm/virt.rst
+++ docs/system/arm/virt.rst
@@ -153,10 +153,13 @@  need::
   CONFIG_DRM=y
   CONFIG_DRM_VIRTIO_GPU=y
 
+.. _arm-baremetal:
+
 Hardware configuration information for bare-metal programming
 """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 
-The ``virt`` board automatically generates a device tree blob ("dtb")
+The ``virt`` board automatically generates a
+:doc:`device tree blob ("dtb") </specs/device-trees>`
 which it passes to the guest. This provides information about the
 addresses, interrupt lines and other configuration of the various devices
 in the system. Guest code can rely on and hard-code the following
diff --git docs/system/arm/xlnx-versal-virt.rst docs/system/arm/xlnx-versal-virt.rst
index 92ad10d2da..3387c74bfa 100644
--- docs/system/arm/xlnx-versal-virt.rst
+++ docs/system/arm/xlnx-versal-virt.rst
@@ -53,7 +53,8 @@  to use the ``-kernel`` command line option.
 
 Users can load firmware or boot-loaders with the ``-device loader`` options.
 
-When loading an OS, QEMU generates a DTB and selects an appropriate address
+When loading an OS, QEMU generates a :doc:`DTB </specs/device-trees>`
+and selects an appropriate address
 where it gets loaded. This DTB will be passed to the kernel in register x0.
 
 If there's no ``-kernel`` option, we generate a DTB and place it at 0x1000
diff --git docs/system/ppc/ppce500.rst docs/system/ppc/ppce500.rst
index 9beef39171..24fd91a084 100644
--- docs/system/ppc/ppce500.rst
+++ docs/system/ppc/ppce500.rst
@@ -24,7 +24,8 @@  The ``ppce500`` machine supports the following devices:
 Hardware configuration information
 ----------------------------------
 
-The ``ppce500`` machine automatically generates a device tree blob ("dtb")
+The ``ppce500`` machine automatically generates a
+:doc:`device tree blob ("dtb") </specs/device-trees>`
 which it passes to the guest, if there is no ``-dtb`` option. This provides
 information about the addresses, interrupt lines and other configuration of
 the various devices in the system.
diff --git docs/system/riscv/microchip-icicle-kit.rst docs/system/riscv/microchip-icicle-kit.rst
index 40798b1aae..a6c8b46263 100644
--- docs/system/riscv/microchip-icicle-kit.rst
+++ docs/system/riscv/microchip-icicle-kit.rst
@@ -37,7 +37,8 @@  can be loaded from U-Boot. It also supports direct kernel booting via the
 boot is used, the OpenSBI fw_dynamic BIOS image is used to boot a payload
 like U-Boot or OS kernel directly.
 
-The user provided DTB should have the following requirements:
+The user provided :doc:`DTB </specs/device-trees>`
+should have the following requirements:
 
 * The /cpus node should contain at least one subnode for E51 and the number
   of subnodes should match QEMU's ``-smp`` option
diff --git docs/system/riscv/sifive_u.rst docs/system/riscv/sifive_u.rst
index 7b166567f9..fd70ee8278 100644
--- docs/system/riscv/sifive_u.rst
+++ docs/system/riscv/sifive_u.rst
@@ -36,7 +36,8 @@  testing of 32-bit guest software.
 Hardware configuration information
 ----------------------------------
 
-The ``sifive_u`` machine automatically generates a device tree blob ("dtb")
+The ``sifive_u`` machine automatically generates a 
+:doc:`device tree blob ("dtb") </specs/device-trees>`
 which it passes to the guest, if there is no ``-dtb`` option. This provides
 information about the addresses, interrupt lines and other configuration of
 the various devices in the system. Guest software should discover the devices
diff --git docs/system/riscv/virt.rst docs/system/riscv/virt.rst
index f8ecec95f3..81ea53eb20 100644
--- docs/system/riscv/virt.rst
+++ docs/system/riscv/virt.rst
@@ -30,7 +30,8 @@  declaring.
 Hardware configuration information
 ----------------------------------
 
-The ``virt`` machine automatically generates a device tree blob ("dtb")
+The ``virt`` machine automatically generates a
+:doc:`device tree blob ("dtb") </specs/device-trees>`
 which it passes to the guest, if there is no ``-dtb`` option. This provides
 information about the addresses, interrupt lines and other configuration of
 the various devices in the system. Guest software should discover the devices
diff --git qemu-options.hx qemu-options.hx
index 377d22fbd8..eea75ddb37 100644
--- qemu-options.hx
+++ qemu-options.hx
@@ -38,6 +38,7 @@  DEF("machine", HAS_ARG, QEMU_OPTION_machine, \
     "                hmat=on|off controls ACPI HMAT support (default=off)\n"
     "                memory-backend='backend-id' specifies explicitly provided backend for main RAM (default=none)\n"
     "                cxl-fmw.0.targets.0=firsttarget,cxl-fmw.0.targets.1=secondtarget,cxl-fmw.0.size=size[,cxl-fmw.0.interleave-granularity=granularity]\n",
+    "                dumpdtb=file dump current device tree blob to a file and quit\n"
     QEMU_ARCH_ALL)
 SRST
 ``-machine [type=]name[,prop=value[,...]]``
@@ -157,6 +158,10 @@  SRST
         ::
 
             -machine cxl-fmw.0.targets.0=cxl.0,cxl-fmw.0.targets.1=cxl.1,cxl-fmw.0.size=128G,cxl-fmw.0.interleave-granularity=512k
+    
+    ``dumpdtb=file``
+        Dump the current :doc:`Device Tree Blob </specs/device-trees>` 
+        to the give file name, then exit.
 ERST
 
 DEF("M", HAS_ARG, QEMU_OPTION_M,