Giter Club home page Giter Club logo

fdtbuspkg's Introduction

FdtBusPkg - Devicetree-based Platform Device Driver Development for Tiano UEFI.

This repo implements support for developing Tiano platform device drivers compliant to the UEFI Driver Model, by performing driver binding and configuration using a Devicetree. Such a Devicetree is typically either passed to UEFI by higher-privileged firmware.

Note

This is a staging branch created as part of the ongoing RISE collaboration. The net goal is to upstream to TianoCore edk2 and the UEFI Specification.

Advantages:

  • Allows UEFI developers to fully embrace modularity and code reuse.
  • Facilitates development of complex (graphics, NIC, etc) drivers.
  • Enables a single firmware binary to work across SoC revisions and board designs.

FdtBusPkg consists of FdtBusDxe, a bus driver, and a number of examples drivers and libraries for demoing with the RISC-V OVMF firmware. FdtBusDxe is responsible for enumerating DT controllers based on Devicetree nodes, and implementing EFI_DT_IO_PROTOCOL for basic operations on such controllers, such as device property access, register I/O, DMA buffer handling and child device enumeration.

See further documentation:

FdtBusPkg components can be used on any architecture, but have been developed and tested with RISC-V. They should be reusable out of the box on AArch64 platforms as well, barring any missing dependencies.

Note: this is Devicetree being used internally by UEFI. There is no relation to using Devicetree as possible mechanism of describing hardware configuration to an OS.

See the presentation video and slides from the UEFI Fall 2023 Developers Conference and Plugfest. Also see the short demo video published February, 2024.

Updates

When What
February 2024 Docs complete. DtInfo, DtProp and DtReg tools added. VirtNorFlashDxe, PciSioSerialDxe, PciHostBridgeFdtDxe drivers ported. Demo video at https://youtu.be/9RqKq4wGYZI.
January 2024 Open sourced. Work on documentation.
October 2023 Presented at the UEFI Fall 2023 Developers Conference and Plugfest. See the presentation slides.
2023 Reported to RISE as a 2024 priority.

Quick Start

To build RISC-V OVMF firmware enabled with FdtBusPkg components:

    $ git clone https://github.com/tianocore/edk2.git
    $ cd edk2
    $ git submodule add https://github.com/intel/FdtBusPkg
    $ git submodule update --init --recursive
    $ . edksetup.sh
    $ git am FdtBusPkg/Docs/edk2-patches/*
    $ git am FdtBusPkg/Docs/ovmf-patches/*
    $ export GCC_RISCV64_PREFIX=... (if you are on a non-RISCV64 system)
    $ build -a RISCV64  -p OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc -t GCC -b DEBUG

See the README for Developers for more directions.

License

FdtBusPkg is licensed under the BSD-2-Clause-Patent license (just like Tiano).

Security Policy

Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation.

Reporting a Vulnerability

Please report any security vulnerabilities in this project utilizing the guidelines here.

Contribute

This is a RISE Project under the Firmware WG. See the project wiki page.

Contributions are welcome. Please raise issues and pull requests.

Please see the policy on contributions and our Code of Conduct.

fdtbuspkg's People

Contributors

andreiw avatar chaievan avatar rdower avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

fdtbuspkg's Issues

Extend EFI_DT_RANGE with TranslatedBase and BusDtIo fields, just like EFI_DT_REG

PciHostBridgeLibEcam.c assumes the "ranges" map from PCI memory/IO spaces to CPU space, but that doesn't necessarily have to be true. But the code has no way of detecting whether the parent value is a CPU address or not. Like the reg parsing code, we ought to try translating all the way to the root and let clients see the additional translatedbase value.

Improve DtIo->Lookup and DtDeviceCreate

DtIoLookup first translates the ASCII DT path to an EFI_DEVICE_PATH_PROTOCOL, then uses DtPathToHandle to lookup/connect the handle. DtPathToHandle uses LocateDevicePath.

Glancing at LocateDevicePath, this seems rather suboptimal, as it can iterate over potentially quite a lot of handles (esp. if LocateDevicePath is successively called as controllers are connected).

Another issue is that LocateDevicePath has no notion of "fuzzy" matching, e.g. when we want /soc/pci to match /soc/pci@3000000 (dropping the unit address in unambiguous situations).

This should be fixed by directly traversing the ASCII DT path. DT_DEVICE has a Parent already, and could be made to keep a linked list of children as well.

The other place DtPathToHandle is used is inside DtDeviceCreate, to avoid creating duplicates. Again, if a DtDevice knows what children it has, this problem becomes much cheaper to solve.

FdtBusDxe: Don't make assumptions about RW access to DT passed by HOB

gDeviceTreeBase is initialized based on the pointer in DT HOB. This is fine, since the bus driver only does read access. However, if a setprop-like interface is ever added (matching FdtClientDxe functionality), then this will need to change. Instead, fdt_open_into should be used with an appropriately-sized buffer.

Implement DT-aware PciHostBridgeDxe.

Today FdtBusPkg provides a PciHostBridgeLib (PciHostBridgeLibEcam), for use with MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf, which is somewhat limited:

  • Only one RC node is supported.
  • Not driver-binding based.
  • No support for non-cache coherent or strange DMA ranges.

See #11, #2, #3, #4, #5.

PciHostBridgeFdtDxe: factor out ECAM-specific portions into lib

Will be necessary to use this driver on non-ecam platforms. This basically should be an init/fini pair + function to read/write a config space register. Also add a VOID *Context to _PCI_ROOT_BRIDGE_INSTANCE so the lib can maintain extra state if needed.

All state manipulated by init/fini/cfg accessors should be pushed into a common struct, i.e.:

  UINT32                                              Segment;
  EFI_DT_REG                                          ConfigReg;
  UINT64                                              Attributes;
  UINT64                                              Supports;
  EFI_DT_RANGE                                        BusRange;
  EFI_DT_RANGE                                        IoRange;
  EFI_DT_RANGE                                        MemRange;
  EFI_DT_RANGE                                        PMemRange;
  EFI_DT_RANGE                                        MemAbove4GRange;
  EFI_DT_RANGE                                        PMemAbove4GRange;
  BOOLEAN                                             DmaAbove4G;
  BOOLEAN                                             NoExtendedConfigSpace;

Originally posted by @andreiw in #12 (comment)

Implement a CpuBarrierLib and use it

Any cpu fences/barriers are implied by Map/Unmap and Reg access code. If code uses common buffer operations, it would need to explicitly use barriers/fences on architectures requiring these.

FdtBusDxe: consider a setprop like interface.

Use case is ArmVirtPkg/Library/ArmVirtPL031FdtClientLib/ArmVirtPL031FdtClientLib.c.

E.g. a driver binding to the RTC device need to disable the RTC device so that the OS always goes through UEFI RT.

Depends on #8

Upstream tech debt: HighMemDxe is not actually needed in OVMF RISC-V

The RISC-V Sec code processes all the "memory" devices in Memory.c, so HighMemDxe code does absolutely nothing.

To test HighMemDxe, this is what I do:

--- a/OvmfPkg/RiscVVirt/Sec/Memory.c
+++ b/OvmfPkg/RiscVVirt/Sec/Memory.c
@@ -253,6 +253,7 @@ MemoryPeimInitialization (
   INT32                       Node, Prev;
   INT32                       Len;
   VOID                        *FdtPointer;
+  BOOLEAN                     Once = TRUE;

   FirmwareContext = NULL;
   GetFirmwareContextPointer (&FirmwareContext);
@@ -285,18 +286,21 @@ MemoryPeimInitialization (
         CurBase = fdt64_to_cpu (ReadUnaligned64 (RegProp));
         CurSize = fdt64_to_cpu (ReadUnaligned64 (RegProp + 1));

-        DEBUG ((
-          DEBUG_INFO,
-          "%a: System RAM @ 0x%lx - 0x%lx\n",
-          __func__,
-          CurBase,
-          CurBase + CurSize - 1
-          ));
+        if (Once) {
+          DEBUG ((
+            DEBUG_INFO,
+            "%a: System RAM @ 0x%lx - 0x%lx\n",
+            __func__,
+            CurBase,
+            CurBase + CurSize - 1
+            ));

-        InitializeRamRegions (
-          CurBase,
-          CurSize
-          );
+          InitializeRamRegions (
+            CurBase,
+            CurSize
+            );
+          Once = FALSE;
+        }
       } else {
         DEBUG ((
           DEBUG_ERROR,

DtIoCopyReg improvs

  • Should enforce Width <= EfiPciWidthUint64
  • Should avoid using a temporary buffer

FdtBusDxe DMA handling.

Platform devices drivers in Tiano have common issues around DMA handling. On some platforms, the device may not be cache coherent, or have a reduced range of memory that can be addressed via DMA. This is handled by linking in the appropriate DmaLib at build time.

With an FdtBusPkg approach the bus driver itself, like the PCI bus driver, provides the DMA API and hides the complexity even from the firmware integrator/builder. That is, functionality wise, FdtBusDxe must implement both EmbeddedPkg/Library/CoherentDmaLib and EmbeddedPkg/Library/NonCoherentDmaLib internally, using the appropriate properties (cache coherence, dma-ranges) to guide choice of implementation.

There's also the the issue with ordering, e.g. ensuring correct barrier instructions are used on RV and AArch64. And there's also the issue with maintaining cache coherence on systems that use system caches (MMIO-based), that CpuDxe wouldn't be aware of (that is, FdtBusDxe would expect such cache drivers to implement a particular protocol).

DMA API changes

Need to be able to pass additional DMA constraints, e.g. a 32-bit only PCI device may be used in a system that's otherwise capable of 64-bit DMA.

The simple way is to just to make this a bitfield attribute. It should only be necessary to support PCI devices on RCs described with DT. Everything else should be describe-able in DT.

Document HighMemDxe vs HighMemDxeNoBinding

This is a good example of Uefi DM vs legacy code, as well as use of uefi,critical. Document DtIo->Lookup in PlatformBm as an alternative to uefi,critical. Document «memory» devices being treated like uefi,critical

Upstream tech debt: BaseRiscVMmuLib violates PI spec

PI spec sez:

... the DXE driver that produces the EFI_CPU_ARCH_PROTOCOL must
seed the GCD memory space map with the initial state of the^M
attributes for all the memory regions visible to the processor.

Instead the GCD attrs remain 0, even for regions that get accessible page table mappings.

GetReg & friends should ensure Reg is mapped

FS0:> dtreg /soc/rtc@101000 0
Dumping 1 bytes at offset 0x0 of reg via CPU 0x101000(1000):
!!!! RISCV64 Exception Type - 000000000000000D(EXCEPT_RISCV_LOAD_ACCESS_PAGE_FAULT) !!!!

E.g, for EFI_DT_REG that translate to a CPU address, the code should ensure
the range is registered with the gDS (e.g. AddMemorySpace + SetMemorySpaceAttributes)

Doing this in ParseProp/GetReg/GetRegByName is better then forcing callers to use yet another call (e.g. SetRegAttrs). And doing this for every (functional) device enumerated by FdtBusDxe is not a good idea because of overhead. GetReg seems like an okay compromise - it's rarely called, for one (once per driver per device, ideally).

Consider a GetPropName DtIo call

...with a signature like the following. This has no use in a driver, but it would be super-useful for the DtProp tool, to be able to explore a Devicetree in a UEFI system.

typedef
EFI_STATUS
(EFIAPI *EFI_DT_IO_PROTOCOL_GET_PROP_NAME)(
  IN  EFI_DT_IO_PROTOCOL *This,
  IN  UINTN              Index,
  OUT CONST CHAR8        **String
  );

Implementation-wise, this could do something like. Of course it would be terribly inefficient... but this shouldn't matter for a debug aid.

do {
	tag = fdt_next_tag(fdt, offset, &nextoffset);
	if (tag == FDT_END) {
		return;
	}

	if (tag == FDT_PROP) {
		prop = _fdt_offset_ptr(fdt, offset);
		n = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));

		for  (i = 0; i < depth; i++) {
			printk(" ");
		}
		printk("%s: 0x%x@0x%x\n", n,
		       fdt32_to_cpu(prop->len),
		       prop->data);
	}

	offset = nextoffset;
} while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));

Add concept of legacy devices

A legacy device is one that ought to be ignored by a UEFI Driver Model compliant device driver. Legacy devices are all bound to by FdtBusPkg (so that their children can be enumerated), and the legacy status is inherited.

A device is considered legacy under 3 circumstances:

  • uefi,legacy property
  • ScanChildrenAsLegacy (or a new param to ScanChildren?)

FdtBusDxe: add test to time DT node traversal.

Consider both a very deep hierarchy and repeated access to a particular node's properties. This is coming from a concern voiced from a 3rd party about overhead of parsing a giant DT tree, and may prompt optimizations to the FDT parsing code itself (which ultimately means taking in the libfdt code directly in instead of having this be a dependent on a Tiano library).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.