wolfBoot/include/fdt.h

218 lines
8.7 KiB
C

/* fdt.h
*
* Functions to help with flattened device tree (DTB) parsing
*
*
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#ifndef FDT_H
#define FDT_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#define FDT_MAGIC 0xD00DFEEDUL
#define FDT_SW_MAGIC (uint32_t)(~FDT_MAGIC) /* marker for run-time creation/edit of FDT */
struct fdt_header {
uint32_t magic;
uint32_t totalsize;
uint32_t off_dt_struct;
uint32_t off_dt_strings;
uint32_t off_mem_rsvmap;
uint32_t version;
uint32_t last_comp_version;
uint32_t boot_cpuid_phys;
uint32_t size_dt_strings;
uint32_t size_dt_struct;
};
struct fdt_reserve_entry {
uint64_t address;
uint64_t size;
};
struct fdt_prop {
uint32_t len;
uint32_t nameoff;
};
struct fdt_node_header {
uint32_t tag;
char name[0];
};
struct fdt_property {
uint32_t tag;
uint32_t len;
uint32_t nameoff;
char data[0];
};
#define FDT_TAGSIZE sizeof(uint32_t)
#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE))
#define FDT_FIRST_SUPPORTED_VERSION 0x10
#define FDT_LAST_SUPPORTED_VERSION 0x11
#define FDT_BEGIN_NODE 0x00000001UL
#define FDT_END_NODE 0x00000002UL
#define FDT_PROP 0x00000003UL
#define FDT_NOP 0x00000004UL
#define FDT_END 0x00000009UL
#define FDT_ERR_BADMAGIC 1
#define FDT_ERR_BADVERSION 2
#define FDT_ERR_BADSTRUCTURE 3
#define FDT_ERR_BADOFFSET 4
#define FDT_ERR_BADSTATE 5
#define FDT_ERR_NOTFOUND 6
#define FDT_ERR_NOSPACE 7
#define FDT_ERR_TRUNCATED 8
#define FDT_ERR_INTERNAL 9
#define FDT_ERR_EXISTS 10
#define FDT_PCI_PREFETCH (0x40000000)
#define FDT_PCI_MEM32 (0x02000000)
#define FDT_PCI_IO (0x01000000)
#define FDT_PCI_MEM64 (0x03000000)
uint32_t cpu_to_fdt32(uint32_t x);
uint64_t cpu_to_fdt64(uint64_t x);
uint32_t fdt32_to_cpu(uint32_t x);
uint64_t fdt64_to_cpu(uint64_t x);
#define fdt_get_header(fdt, field) (fdt32_to_cpu(((const struct fdt_header *)(fdt))->field))
#define fdt_magic(fdt) (fdt_get_header(fdt, magic))
#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize))
#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct))
#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings))
#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap))
#define fdt_version(fdt) (fdt_get_header(fdt, version))
#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version))
#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys))
#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings))
#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct))
#define fdt_set_header(fdt, field, val) (((struct fdt_header *)fdt)->field = cpu_to_fdt32(val))
#define fdt_set_magic(fdt, val) (fdt_set_header(fdt, magic, (val)))
#define fdt_set_totalsize(fdt, val) (fdt_set_header(fdt, totalsize, (val)))
#define fdt_set_off_dt_struct(fdt, val) (fdt_set_header(fdt, off_dt_struct, (val)))
#define fdt_set_off_dt_strings(fdt, val) (fdt_set_header(fdt, off_dt_strings, (val)))
#define fdt_set_off_mem_rsvmap(fdt, val) (fdt_set_header(fdt, off_mem_rsvmap, (val)))
#define fdt_set_version(fdt, val) (fdt_set_header(fdt, version, (val)))
#define fdt_set_last_comp_version(fdt, val) (fdt_set_header(fdt, last_comp_version, (val)))
#define fdt_set_boot_cpuid_phys(fdt, val) (fdt_set_header(fdt, boot_cpuid_phys, (val)))
#define fdt_set_size_dt_strings(fdt, val) (fdt_set_header(fdt, size_dt_strings, (val)))
#define fdt_set_size_dt_struct(fdt, val) (fdt_set_header(fdt, size_dt_struct, (val)))
/* Headroom (bytes) appended to fdt_totalsize() before wolfBoot inserts
* /chosen properties. Sized to comfortably hold a full LINUX_BOOTARGS
* plus, when WOLFBOOT_FIT_RAMDISK is enabled, two 64-bit
* linux,initrd-{start,end} cells with property-name overhead. A target
* whose hal_dts_fixup() inserts more chosen entries can override this
* with -DWOLFBOOT_FDT_FIXUP_HEADROOM=<bytes>. */
#ifndef WOLFBOOT_FDT_FIXUP_HEADROOM
#define WOLFBOOT_FDT_FIXUP_HEADROOM 768
#endif
int fdt_check_header(const void *fdt);
int fdt_next_node(const void *fdt, int offset, int *depth);
int fdt_first_property_offset(const void *fdt, int nodeoffset);
int fdt_next_property_offset(const void *fdt, int offset);
const struct fdt_property *fdt_get_property_by_offset(const void *fdt, int offset, int *lenp);
const char* fdt_get_name(const void *fdt, int nodeoffset, int *len);
const char* fdt_get_string(const void *fdt, int stroffset, int *lenp);
const void *fdt_getprop(const void *fdt, int nodeoffset, const char *name, int *lenp);
int fdt_setprop(void *fdt, int nodeoffset, const char *name, const void *val, int len);
void* fdt_getprop_address(const void *fdt, int nodeoffset, const char *name);
int fdt_find_node_offset(void* fdt, int startoff, const char* nodename);
int fdt_find_prop_offset(void* fdt, int startoff, const char* propname, const char* propval);
int fdt_find_devtype(void* fdt, int startoff, const char* node);
int fdt_node_check_compatible(const void *fdt, int nodeoffset, const char *compatible);
int fdt_node_offset_by_compatible(const void *fdt, int startoffset, const char *compatible);
int fdt_add_subnode(void* fdt, int parentoff, const char* name);
int fdt_del_node(void *fdt, int nodeoffset);
/* helpers to fix/append a property to a node */
int fdt_fixup_str(void* fdt, int off, const char* node, const char* name, const char* str);
int fdt_fixup_val(void* fdt, int off, const char* node, const char* name, uint32_t val);
int fdt_fixup_val64(void* fdt, int off, const char* node, const char* name, uint64_t val);
int fdt_shrink(void* fdt);
int fdt_add_mem_rsv(void* fdt, uint64_t address, uint64_t size);
/* FIT */
const char* fit_find_images(void* fdt, const char** pkernel, const char** pflat_dt,
const char** pramdisk, const char** pfpga);
/* Return a pointer to a subimage's "compatible" property, or NULL if
* absent. NOTE: "compatible" is a devicetree string-list (one or more
* NUL-separated strings); this returns a pointer to the raw property
* data, i.e. the FIRST string only. Callers that must inspect every
* entry (e.g. to detect "partial") have to obtain the property length
* via fdt_getprop() and scan the whole buffer - fit_load_fpga() does
* this internally rather than relying on this helper. */
const char* fit_get_compatible(void* fdt, const char* image);
void* fit_load_image(void* fdt, const char* image, int* lenp);
void* fit_load_image_ex(void* fdt, const char* image, int* lenp, uint32_t out_max);
/* Load (and, if compressed, decompress) a FIT subimage directly to a
* caller-supplied destination buffer. Overrides the FIT image's
* `load` and `entry` properties - dst is both the destination and the
* value returned. dst_max bounds the decompressed size. */
void* fit_load_image_to(void* fdt, const char* image, void* dst,
uint32_t dst_max, int* lenp);
/* FDT initrd fixup: writes /chosen/linux,initrd-{start,end} as 64-bit
* big-endian properties. Creates /chosen if missing. Returns 0 on success
* or a negative FDT_ERR_*. */
int fdt_fixup_initrd(void* fdt, uint64_t start, uint64_t size);
#ifdef WOLFBOOT_FIT_RAMDISK
/* Load a FIT ramdisk subimage (optionally relocated to
* WOLFBOOT_LOAD_RAMDISK_ADDRESS) and patch /chosen/linux,initrd-*
* in the supplied DTB. Returns 0 on success, -1 on load failure.
* Callers typically ignore the return value (log-and-continue). */
int fit_load_ramdisk(void* fit, const char* ramdisk_node, void* dts_addr);
#endif
#ifdef WOLFBOOT_FPGA_BITSTREAM
/* Locate a FIT fpga subimage, stage it to its `load` address (or use
* the in-FIT data pointer) and program the PL via hal_fpga_load().
* Returns 0 on success (including when fpga_node is NULL), negative on
* failure. When WOLFBOOT_FPGA_NONFATAL is defined a programming failure
* is logged and 0 is returned instead. */
int fit_load_fpga(void* fdt, const char* fpga_node);
#endif
#ifdef __cplusplus
}
#endif
#endif /* !FDT_H */