This function will add a memory region property based on parent node's "#address-cells" and "#size-cells" properties. It will be used in implementing kdump with kexec_file_load system call at linux kernel for arm64 once it is merged into kernel tree. Signed-off-by: AKASHI Takahiro <takahiro.akashi@xxxxxxxxxx> --- libfdt/fdt_addresses.c | 49 ++++++++++++++++++++++++++++++++++++++++++ libfdt/libfdt.h | 30 ++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/libfdt/fdt_addresses.c b/libfdt/fdt_addresses.c index f13a87dfa068..08615c9f669a 100644 --- a/libfdt/fdt_addresses.c +++ b/libfdt/fdt_addresses.c @@ -95,3 +95,52 @@ int fdt_size_cells(const void *fdt, int nodeoffset) return 1; return val; } + +static void cpu64_to_fdt_cells(void *prop, uint64_t val, int cells) +{ + fdt32_t val32; + + while (cells) { + val32 = cpu_to_fdt32(val >> (32 * (--cells))); + memcpy(prop, &val32, sizeof(val32)); + prop = (uint8_t *)prop + sizeof(val32); + } +} + +/* This function assumes that [address|size]_cells is 1 or 2 */ +int fdt_setprop_reg(void *fdt, int nodeoffset, const char *name, + uint64_t addr, uint64_t size) +{ + int parent, addr_cells, size_cells, buf_size; + char buf[sizeof(fdt32_t) * 2 * 2]; + void *prop; + + parent = fdt_parent_offset(fdt, nodeoffset); + if (parent < 0) + return parent; + + addr_cells = fdt_address_cells(fdt, parent); + if (addr_cells < 0) + return addr_cells; + size_cells = fdt_size_cells(fdt, parent); + if (size_cells < 0) + return size_cells; + + if ((addr_cells == 1) && ((addr > UINT32_MAX) || + ((addr + size) > UINT32_MAX))) + return -FDT_ERR_BADVALUE; + + if ((size_cells == 1) && (size > UINT32_MAX)) + return -FDT_ERR_BADVALUE; + + buf_size = (addr_cells + size_cells) * sizeof(fdt32_t); + prop = buf; + cpu64_to_fdt_cells(prop, addr, addr_cells); + prop = (uint8_t *)prop + addr_cells * sizeof(fdt32_t); + cpu64_to_fdt_cells(prop, size, size_cells); + + if (!name) + name = "reg"; + + return fdt_setprop(fdt, nodeoffset, name, (const void *)buf, buf_size); +} diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h index fdaa3e6f39fe..6375d7cb7367 100644 --- a/libfdt/libfdt.h +++ b/libfdt/libfdt.h @@ -1690,6 +1690,36 @@ static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name, #define fdt_setprop_empty(fdt, nodeoffset, name) \ fdt_setprop((fdt), (nodeoffset), (name), NULL, 0) +/** + * fdt_setprop_reg - add/set a memory region property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to add a property at + * @name: name of property + * @addr: physical start address + * @size: size of region + * + * If name is not specified, a default "reg" is used. + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid + * #address-cells property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADVALUE, addr or size doesn't fit to respective cells size + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain a new property + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop_reg(void *fdt, int nodeoffset, const char *name, + uint64_t addr, uint64_t size); + /** * fdt_appendprop - append to or create a property * @fdt: pointer to the device tree blob -- 2.19.1