From 74c0ea1e835de38e13be7c8e6440bde94bcc07d0 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Fri, 23 Oct 2020 15:47:39 -0700 Subject: [PATCH] lib: utils: Implement "ranges" property parsing The "reg" property in a device node may not be the correct address always. If a parent node defines a "ranges" property, the child address need to be translated with respect to parents address. If the ranges property is not present, it will just use 1:1 translation. Signed-off-by: Atish Patra Reviewed-by: Anup Patel --- lib/utils/fdt/fdt_helper.c | 50 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/lib/utils/fdt/fdt_helper.c b/lib/utils/fdt/fdt_helper.c index aec73a0..bf19ff9 100644 --- a/lib/utils/fdt/fdt_helper.c +++ b/lib/utils/fdt/fdt_helper.c @@ -71,10 +71,48 @@ int fdt_find_match(void *fdt, int startoff, return SBI_ENODEV; } +static int fdt_translate_address(void *fdt, uint64_t reg, int parent, + unsigned long *addr) +{ + int i, rlen; + int cell_addr, cell_size; + const fdt32_t *ranges; + uint64_t offset = 0, caddr = 0, paddr = 0, rsize = 0; + + cell_addr = fdt_address_cells(fdt, parent); + if (cell_addr < 1) + return SBI_ENODEV; + + cell_size = fdt_size_cells(fdt, parent); + if (cell_size < 0) + return SBI_ENODEV; + + ranges = fdt_getprop(fdt, parent, "ranges", &rlen); + if (ranges && rlen > 0) { + for (i = 0; i < cell_addr; i++) + caddr = (caddr << 32) | fdt32_to_cpu(*ranges++); + for (i = 0; i < cell_addr; i++) + paddr = (paddr << 32) | fdt32_to_cpu(*ranges++); + for (i = 0; i < cell_size; i++) + rsize = (rsize << 32) | fdt32_to_cpu(*ranges++); + if (reg < caddr || caddr >= (reg + rsize )) { + sbi_printf("invalid address translation\n"); + return SBI_ENODEV; + } + offset = reg - caddr; + *addr = paddr + offset; + } else { + /* No translation required */ + *addr = reg; + } + + return 0; +} + int fdt_get_node_addr_size(void *fdt, int node, unsigned long *addr, unsigned long *size) { - int parent, len, i; + int parent, len, i, rc; int cell_addr, cell_size; const fdt32_t *prop_addr, *prop_size; uint64_t temp = 0; @@ -98,7 +136,15 @@ int fdt_get_node_addr_size(void *fdt, int node, unsigned long *addr, if (addr) { for (i = 0; i < cell_addr; i++) temp = (temp << 32) | fdt32_to_cpu(*prop_addr++); - *addr = temp; + do { + if (parent < 0) + break; + rc = fdt_translate_address(fdt, temp, parent, addr); + if (rc) + break; + parent = fdt_parent_offset(fdt, parent); + temp = *addr; + } while (1); } temp = 0; -- 2.7.4