bzt a3851e0909 Multilingual GUI and some fixes 9 months ago
..
00_device.md 0c8fa906d6 Initial commit 10 months ago
01_cpucore.md 0c8fa906d6 Initial commit 10 months ago
02_dma.md 0c8fa906d6 Initial commit 10 months ago
03_irq.md 0c8fa906d6 Initial commit 10 months ago
04_intc.md 0c8fa906d6 Initial commit 10 months ago
05_pins.md 0c8fa906d6 Initial commit 10 months ago
06_leds.md 0c8fa906d6 Initial commit 10 months ago
07_clocks.md 0c8fa906d6 Initial commit 10 months ago
08_sensors.md 0c8fa906d6 Initial commit 10 months ago
09_buttons.md 0c8fa906d6 Initial commit 10 months ago
0A_amper.md 0c8fa906d6 Initial commit 10 months ago
0B_volt.md 0c8fa906d6 Initial commit 10 months ago
0C_thermal.md 0c8fa906d6 Initial commit 10 months ago
0D_freq.md 51b9260815 Fixed typos 9 months ago
0E_l0cache.md 0c8fa906d6 Initial commit 10 months ago
0F_l1cache.md 0c8fa906d6 Initial commit 10 months ago
10_l2cache.md 0c8fa906d6 Initial commit 10 months ago
11_l3cache.md 0c8fa906d6 Initial commit 10 months ago
D7_edid.md 51b9260815 Fixed typos 9 months ago
D8_fbptr.md 51b9260815 Fixed typos 9 months ago
D9_fbdim.md 51b9260815 Fixed typos 9 months ago
DA_module.md 51b9260815 Fixed typos 9 months ago
DB_cmdline.md 51b9260815 Fixed typos 9 months ago
DC_default.md 630e807cd0 Node types finalized and lots of tests 9 months ago
DD_nvsmem.md 630e807cd0 Node types finalized and lots of tests 9 months ago
DE_resvmem.md 630e807cd0 Node types finalized and lots of tests 9 months ago
DF_ram.md 630e807cd0 Node types finalized and lots of tests 9 months ago
E0_mmio.md 630e807cd0 Node types finalized and lots of tests 9 months ago
E1_ioport.md 0c8fa906d6 Initial commit 10 months ago
E2_pci.md 630e807cd0 Node types finalized and lots of tests 9 months ago
E3_ec.md 630e807cd0 Node types finalized and lots of tests 9 months ago
E4_smb.md 630e807cd0 Node types finalized and lots of tests 9 months ago
E5_nvram.md 630e807cd0 Node types finalized and lots of tests 9 months ago
E6_pcibar.md 630e807cd0 Node types finalized and lots of tests 9 months ago
E7_ipmi.md 630e807cd0 Node types finalized and lots of tests 9 months ago
E8_gpio.md 630e807cd0 Node types finalized and lots of tests 9 months ago
E9_gsb.md 630e807cd0 Node types finalized and lots of tests 9 months ago
EA_pcc.md 630e807cd0 Node types finalized and lots of tests 9 months ago
README.md a3851e0909 Multilingual GUI and some fixes 9 months ago
flags.md 630e807cd0 Node types finalized and lots of tests 9 months ago

README.md

Grand Unified Device Tree Specification

The goal of this file format is to provide a platofrm independent, standardized way of describing devices in a computer, which is more universal and a lot easier to use than the current overcomplicated solutions.

Binary Node Format

  • file extension: .gud
  • mime type: application/grand-unified-device-tree

The GUDT blob comes in two flavours, depending on the use case. The first one is small and compact, the second one is easy to manipulate. If you define GUDT_IMPLEMENTATION in exactly one of your source files before you include gudt.h then you'll get a gudt_unpack() function to convert from the former into the latter. This function does not use libc nor does it allocate any memory, so it can be used even in freestanding mode on bare metal and it is endianness-agnostic. On big endian machines, you should also define GUDT_BIGENDIAN. In this case gudt_unpack() will automatically convert all numbers in the blob to big endian for you. The other function, gudt_find() will look up and return a specific resource node for you.

The file itself consists of three parts: an 8 bytes long header (never compressed), a string table that's padded to be multiple of 8 bytes, and a node list with fixed sized elements (this two might be stored in a compressed payload).

Header

Offset Size Description
0 4 magic bytes 'G', 'U', 'D', 'T'
4 2 header size
6 2 number of nodes
8 2 magic bytes 0x78, 0xDA
8 x zlib deflate (RFC 1950) compressed payload

All numbers are little-endian. The uncompressed size is ((header size + 7) & ~7) + number of nodes * 16 and the payload contains a string table followed by node records aligned on a 8 bytes boundary. This packed format is used on disk, in ROM and when the GUDT blob is transmitted over the network.

Offset Size Description
0 4 magic bytes 'G', 'U', 'D', 'T'
4 2 header size (H = header size rounded up to 8 bytes)
6 2 number of nodes (N)
8 H-8 string table (guaranteed to not start with 0x78, 0xDA)
H N*16 nodes

This unpacked format is used in RAM and it might contain big endian numbers depending on the native endianness. On big endian machines this is indicated by the last byte of the magic being 'B' (instead of 'T'). This is only allowed in memory, on disk the magic must always end in 'T' and numbers must always be in little endian, even when the blob isn't compressed.

String table

String table is a list of unique, zero terminated UTF-8 strings, header size - 8 bytes in total. The maximum size of the table is 65527 bytes. If the first two characters of the first string would happen to be 0x78 and 0xDA then the string table starts with a zero byte. The string table is padded with zero bytes so that the node list that follows always starts on a 8 byte aligned boundary (padding is not included in the header size field).

To help with the JSON format, strings are not allowed to have " double quotes, those will be converted to ' single qoutes.

Nodes

Unlike AML and FDT (both use a nested node list), here the nodes list is stored in a flattened one dimensional array of 16 bytes long elements with a parent pointer in them, so this format is extremely easy to work with. Parent nodes must always preceed their children nodes, and the very first node is the root of the tree, a "machine device" which describes the brand and model of the machine in general.

Depending on type being zero or not a node can be either a device node or a resource node.

The fields have different meaning depending on type, see the files above for a full list. If you feel that a certain resource property is missing, please open an issue and let me know! The 0 zero type is for device nodes, the last 32 types (from 0xe0 to 0xff) are reserved and must match the ACPI region space types. All the other types are free to be allocated by this specification.

For resource nodes, the flags field tells if the node points to the resource, or has inlined items. If a resource needs more than 12 inlined bytes or 6 words, 3 dwords or 1 qword, then simply more nodes are added to the list with the same type and parent. When the lower tetrad of flags is 0x0f, then size field points to another resource node (indirect reference).

Source JSON Format

The human readable textual source is a simple RFC 8259 compliant JSON string in UTF-8 encoding. It must start with the following magic line, followed by a single array of non-nested objects:

/* Grand Unified Device Tree */
[
  {
    "type": (string) node type,
    "parent": (string) parent device's name,
      ...
  },
  ...
]

Within each object, only the type and parent fields are mandatory. Some 3rd party tools don't like comments in JSON files, for those you might need to first remove the C style /* */ comments with the \/\*(.*?)\*\/ regexp, and then when you're done with editing, add the first magic line back.

Universal Device Type

The category code as well as the first 256 device type codes in each category must match with PCI-SIG base class and sub-class codes in the pci.ids database, but type codes above can be freely assigned by GUDT. (With one notable exception, base class 0x40 only exists in pci.ids, but it's not in pcisig, so Universal Device Type doesn't have that either.)

  • category code: is the same as PCI base class code
  • device code: is the same as PCI device sub-class code (must be 0 with category code 0)
  • vendor code: is the same as PCI vendor id
  • model code: is the same as PCI device id (must be 0 with vendor code 0)

If devids.txt can be found, then the tool uses it to read in EisaIDs and match those with the pci.ids' data (based on best effort, you should always double check its results manually, use -vv).

The gudt.ids file is stored next to the pci.ids file, usually under /usr/share/hwdata/gudt.ids. An up-to-date version of this database can be freely downloaded from this repository: gudt.ids. It is a simple plain text file with space separated coloumn list, where both NL and CRLF line endings are accepted.

Lines starting with # hashmark are comments till the end of the line.

V (vendorid) (name)

Lines starting with an upper-case V add a new vendor to the pci.ids list (currently not used). The name part might contain spaces, but all double quotes " in it will be replaced to single quotes '. This is true to all name coloumns in this file.

M (vendorid) (deviceid) (name)

Lines starting with an upper-case M add a new model, a vendorid + deviceid pair with a name to the list (currently not used).

C (class) (subclass) (name)

Lines starting with an upper-case C add a new category (or class in PCI parlance) to the list. Currently only used to add some missing CPU architectures.

(class) (sub-class) (vendorid) (deviceid) (driver/name)

All the other lines starting with a hexadecimal number have 5 coloumns. These assign a category, device, vendor and model id to a particular driver string or device name string. When a device tree (in any format) is imported by the gudt tool, this name string is matched and if found, the corresponding coloumns in this line will be used for the node.

ACPI Power Management

You'll need the following GUDT nodes:

  • driver PNP0C20, type GUDT_T_IOPORT is the SMI_CMD port.
  • driver PNP0C23, type GUDT_T_IOPORT is the PM1a_CNT port.
  • driver PNP0C24, type GUDT_T_IOPORT is the PM1b_CNT port.
  • driver PNP0C20, type GUDT_T_DEFAULT is inlined data, data[0] is the ACPI enable value, data[1] is the ACPI disable value.
  • driver PNP0C23, type GUDT_T_DEFAULT is inlined data, stores S3, S4, S5 system state values (one node for each state).

To enable ACPI, write out its first default value, data[0] to the SMI_CMD port.

For example to shutdown the computer, you'd need the S5 system state. So search the default values in PNP0C23 and look for the one that starts with 5 (has 0x35 in its data[0]). Write out data[1] with bit 13 set to the PM1a_CNT port, and if port b exists, then data[2] likewise with bit 13 set to the PM1b_CNT port. That's all.

Here's a useful C code snippet to do all of this (error handling omited for clearity):

/* node list starts at header size rounded up to 8 bytes */
gudt_node_t *nodes = (gudt_node_t*)((uint8_t*)gudt + ((gudt->hdrsize + 7) & ~7));
gudt_device_t *parent;

/* iterate on GUDT nodes */
for(i = 0; i < gudt->numnodes; i++) {

    /* we are looking for resource nodes, so their parents will be device nodes with the driver */
    parent = (gudt_device_t*)&nodes[nodes[i].parent];
    device_driver = (char*)gudt + parent->driver;

    /* depending on the resource's type, we look for... */
    switch(nodes[i].type) {
        case GUDT_T_DEFAULT:
            if(!strcmp(device_driver, "PNP0C20")) {
                acpi_enable = nodes[i].r.b.data[0];
                acpi_disable = nodes[i].r.b.data[1];
            } else
            if(!strcmp(device_driver, "PNP0C23") && nodes[i].r.w.data[0] == '5') {
                slp_typa = nodes[i].r.w.data[1];
                slp_typb = nodes[i].r.w.data[2];
            }
        break;
        case GUDT_T_IOPORT:
            if(!strcmp(device_driver, "PNP0C20")) smi_cmd = nodes[i].r.p.base; else
            if(!strcmp(device_driver, "PNP0C23")) pm1a_cnt = nodes[i].r.p.base; else
            if(!strcmp(device_driver, "PNP0C24")) pm1b_cnt = nodes[i].r.p.base; else
            if(!strcmp(device_driver, "PNP0C26")) pm_tmr = nodes[i].r.p.base;
        break;
        /* these might be useful to have too */
        case GUDT_T_MMIO:
            if(!strcmp(device_driver, "PNP0C08")) ioapic_ptr = nodes[i].r.p.base; else
            if(!strcmp(device_driver, "PNP0103")) hpet_ptr = nodes[i].r.p.base; else
            if(!strcmp(device_driver, "PNP0003")) lapic_ptr = nodes[i].r.p.base;
        break;
    }
}

/* enable ACPI power management */
outb(smi_cmd, acpi_enable);

/* shutdown, power off machine */
outw(pm1a_cnt, slp_typa | (1 << 13));
if(pm1b_cnt) outw(pm1b_cnt, slp_typb | (1 << 13));
/* never reached */

To achieve the other system states (like sleep, hibernate etc.), consult the ACPI specification. All those cryptic PNP IDs (used to identify the devices) are as well defined by ACPI, do not hate GUDT for those!