Dependency-free, single header AML and FDT parser in ANSI C

bzt fa8e277c5f More bullet-proof AML parsing 9 kuukautta sitten
docs fa8e277c5f More bullet-proof AML parsing 9 kuukautta sitten
LICENSE 194cde15d1 Initial commit 1 vuosi sitten
README.md 57c52c91d5 AML fixes 9 kuukautta sitten
hwdet.h fa8e277c5f More bullet-proof AML parsing 9 kuukautta sitten
test.c 2e5b8c06a9 Added GUDT 10 kuukautta sitten

README.md

Hardware Detection Library

The hwdet library is an easily embeddable, single header hardware resource detection library.

Features

  • Supports AML (in ACPI DSDT/SSDT tables) as well as FDT (dtb) and GUDT formats.
  • It's extremely small (ca. 56k, 1600 SLoC) in a single ANSI C header file.
  • Dependency-free, does not allocate memory (ideal for early kernel environments, only requires memcmp and memset).
  • Uses very small amount of stack (with real world DSDT files, it never used more than 32k, FDT needs a bit more, about 64k).
  • Easy to use and integrate: you provide two hooks and call one function with your binary blobs.
  • Licensed under GPLv3+, so it is Free and Open Source, but you can only use it if your kernel is GPL licensed as well.

NOTE: if GPL license does not suit your use case, then you can use a command line utility to convert AML and DTB blobs into GUDT, and embed the single header gudt.h library in your kernel which is MIT licensed.

Usage

You include hwdet.h in exactly one of your source files. But before you do that, you must tell the hook functions with two defines:

#define HWDET_RESVMEM  mem_exclude
#define HWDET_RESOURCE drv_resource
#include "hwdet.h"

Then you pass a list of data pointers to the one and only function. These should point to either ACPI DSDT/SSDT/MADT tables, FDT or GUDT blobs.

void hwdet(int num, uint8_t **ds);

This iterates on num blobs, parses their bytecode and calls the first hook whenever an unusable memory region found.

void HWDET_RESVMEM(uint64_t base, uint64_t size);

In your hook, you should exclude this region from your free memory list in your PMM. The region starts at base and it is size bytes in length.

The second hook is called whenever a resource is found.

void HWDET_RESOURCE(char *name, char *driver, char *altdriver, int type, uint64_t arg1, uint64_t arg2);

In your hook, you should pass these to your driver manager. The name contains a unique device name, driver is the type of the device, aka. the name of the driver that can drive the device. If possible, then altdriver is also reported, which is the name of an alternative compatible driver. The type is a resource code, that determines how arg1 and arg2 is used:

  • 0: HWDET_NONE no arguments.
  • 1: HWDET_CPU arg1 is the spin-lock release address, arg2 is the unique id of the core (lAPIC id on x86).
  • 2: HWDET_IO, arg1 is an IO port number, arg2 is length in bytes.
  • 3: HWDET_IRQ, arg1 is an IRQ number, arg2 is the affinity level.
  • 4: HWDET_DMA, arg1 is a DMA number, arg2 is the transfer width.
  • 5: HWDET_MMIO, arg1 is a memory address, arg2 is length in bytes.
  • 6: HWDET_PCI, arg1 and arg2 describes an address to PCI configuration space.
  • 7: HWDET_EC, arg1 is an Embedded Control address, arg2 is length in bytes.
  • 8: HWDET_SMB, arg1 is a System Management Bus address, arg2 is length in bytes.
  • 9: HWDET_CMOS, arg1 is a CMOS address, arg2 is length in bytes.

NOTE: multiple calls might be made with the same name if multiple resources belong to that device. But because of buggy firmware, always be prepared that you might get multiple calls with exactly the same arguments. See docs for real life examples.

That's all. Simplicity is the ultimate sophistication!

Configuration

No need really. But if you want, you can specify the minimum number of AML nodes with AML_MINNODES to make a fix sized stack. This is just to make place for method calls, if more static nodes needed by a certain DSDT, then stack will be expanded as necessary. An average DSDT declares 300-400 nodes, hwdet detects this and by default adds space for 128 more should dynamic method calls create temporary nodes. The number of supported nested nodes (scopes) and method calls can be configured with the AML_MAXLEVEL define, being 32 by default.

Dependencies

No library dependency of any kind. You only have to provide two memory related functions from libc, memcmp and memset.

Debugging

Provide some kind of printf and define AML_DEBUG before the include to generate debug messages. Define it as 2 to get a full dump of all parsed opcodes and their positions.

License

Licensed under the terms of the copyleft GNU General Public License version 3 or any later version.

Cheers, bzt