From a3d289b06bc380e3fc0deadab8a3f0062d9830f6 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 4 Jan 2024 14:41:24 -0800 Subject: [PATCH] Add automated test case for NXP T1024 FDT. --- .github/workflows/test-parse-tools.yml | 46 ++++++ hal/nxp_t1024.c | 21 ++- include/fdt.h | 6 +- src/fdt.c | 22 ++- test-app/Makefile | 9 +- tools/elf-parser/Makefile | 2 +- tools/fdt-parser/bcm2710-rpi-3-b.dtb | Bin 0 -> 33555 bytes tools/fdt-parser/fdt-parser.c | 201 ++++++++++++++++++++++++- tools/fdt-parser/nxp_t1024.dtb | Bin 0 -> 65537 bytes 9 files changed, 284 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/test-parse-tools.yml create mode 100644 tools/fdt-parser/bcm2710-rpi-3-b.dtb create mode 100755 tools/fdt-parser/nxp_t1024.dtb diff --git a/.github/workflows/test-parse-tools.yml b/.github/workflows/test-parse-tools.yml new file mode 100644 index 000000000..f8a2f1468 --- /dev/null +++ b/.github/workflows/test-parse-tools.yml @@ -0,0 +1,46 @@ +name: Test parsing tools (elf and fdt) + +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + +jobs: + + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - name: Install cross compilers + run: | + sudo apt-get install -y gcc-arm-none-eabi gcc-powerpc-linux-gnu + + - name: make distclean + run: | + make distclean + + - name: Select config + run: | + cp config/examples/nxp-t1024.config .config + + - name: Build wolfBoot + run: | + make + + - name: Build tools + run: | + make elf-parser + make fdt-parser + + - name: Run elf-parser test + run: | + ./tools/elf-parser/elf-parser + + - name: Run fdt-parser test (nxp_t1024.dtb) + run: | + ./tools/fdt-parser/fdt-parser ./tools/fdt-parser/nxp_t1024.dtb -t diff --git a/hal/nxp_t1024.c b/hal/nxp_t1024.c index 48ccf02fc..6a0323750 100644 --- a/hal/nxp_t1024.c +++ b/hal/nxp_t1024.c @@ -1984,9 +1984,12 @@ int hal_dts_fixup(void* dts_addr) uint64_t core_spin_table; reg = (uint32_t*)fdt_getprop(fdt, off, "reg", NULL); - if (reg == NULL || *reg >= CPU_NUMCORES) + if (reg == NULL) break; - core = *reg; + core = (int)fdt32_to_cpu(*reg); + if (core >= CPU_NUMCORES) { + break; /* invalid core index */ + } /* calculate location of spin table for core */ core_spin_table = (uint64_t)((uintptr_t)( @@ -2035,14 +2038,18 @@ int hal_dts_fixup(void* dts_addr) /* fixup the QMAN portals */ off = fdt_node_offset_by_compatible(fdt, -1, "fsl,qman-portal"); while (off != -FDT_ERR_NOTFOUND) { - const int *ci = fdt_getprop(fdt, off, "cell-index", NULL); uint32_t liodns[2]; - if (!ci) - break; - i = *ci; + reg = (uint32_t*)fdt_getprop(fdt, off, "cell-index", NULL); + if (reg == NULL) + break; + i = (int)fdt32_to_cpu(*reg); + if (i >= QMAN_NUM_PORTALS) { + break; /* invalid index */ + } liodns[0] = qp_info[i].dliodn; liodns[1] = qp_info[i].fliodn; + wolfBoot_printf("FDT: Set %s@%d (%d), %s=%d,%d\n", "qman-portal", i, off, "fsl,liodn", liodns[0], liodns[1]); fdt_setprop(fdt, off, "fsl,liodn", liodns, sizeof(liodns)); @@ -2055,8 +2062,6 @@ int hal_dts_fixup(void* dts_addr) if (off != -FDT_ERR_NOTFOUND) { fdt_fixup_val(fdt, off, "open-pic", "clock-frequency", hal_get_bus_clk()); } - - #endif /* !BUILD_LOADER_STAGE1 */ return 0; } diff --git a/include/fdt.h b/include/fdt.h index 24efb3a6b..7a74a8106 100644 --- a/include/fdt.h +++ b/include/fdt.h @@ -130,8 +130,8 @@ 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 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); @@ -145,6 +145,8 @@ int fdt_fixup_str(void* fdt, int off, const char* node, const char* name, const 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); + #ifdef __cplusplus } #endif diff --git a/src/fdt.c b/src/fdt.c index f2bffcd5d..91039c7c3 100644 --- a/src/fdt.c +++ b/src/fdt.c @@ -30,6 +30,7 @@ #include "hal.h" #include "printf.h" #include "string.h" +#include uint32_t cpu_to_fdt32(uint32_t x) { @@ -293,7 +294,7 @@ const struct fdt_property *fdt_get_property_by_offset(const void *fdt, return prop; } -const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) +const char* fdt_get_name(const void *fdt, int nodeoffset, int *len) { int err; const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset); @@ -315,7 +316,7 @@ const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) return name; } -const char *fdt_get_string(const void *fdt, int stroffset, int *lenp) +const char* fdt_get_string(const void *fdt, int stroffset, int *lenp) { const char *s = (const char*)fdt + fdt_off_dt_strings(fdt) + stroffset; if (lenp) { @@ -370,7 +371,7 @@ const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, return fdt_get_property_namelen(fdt, nodeoffset, name, strlen(name), lenp); } -struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset, +static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset, const char *name, int *lenp) { return (struct fdt_property*)(uintptr_t) @@ -443,7 +444,7 @@ static int fdt_splice_string_(void *fdt, int newlen) return 0; } -const char *fdt_find_string_(const char *strtab, int tabsize, const char *s) +const char* fdt_find_string_(const char *strtab, int tabsize, const char *s) { int len = strlen(s) + 1; const char *last = strtab + tabsize - len; @@ -584,7 +585,8 @@ int fdt_fixup_val(void* fdt, int off, const char* node, const char* name, int fdt_fixup_val64(void* fdt, int off, const char* node, const char* name, uint64_t val) { - wolfBoot_printf("FDT: Set %s (%d), %s=%lu\n", node, off, name, val); + wolfBoot_printf("FDT: Set %s (%d), %s=%llu\n", + node, off, name, (unsigned long long)val); val = cpu_to_fdt64(val); fdt_setprop(fdt, off, name, &val, sizeof(val)); return off; @@ -657,4 +659,14 @@ int fdt_node_offset_by_compatible(const void *fdt, int startoffset, return offset; } +int fdt_shrink(void* fdt) +{ + uint32_t total_size; + /* the last part of the FDT is the DT string, so use that offset to + * determine total size */ + total_size = fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); + + return fdt_set_totalsize(fdt, total_size); +} + #endif /* MMU && !BUILD_LOADER_STAGE1 */ diff --git a/test-app/Makefile b/test-app/Makefile index e0bca642c..91398def6 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -166,8 +166,13 @@ endif ifeq ($(TARGET),sim) APP_OBJS=app_$(TARGET).o ../test-app/libwolfboot.o ../hal/$(TARGET).o - # Override linker flags - LDFLAGS=-Wl,-Map=image.map + # LD on MacOS does not support "-Map=" + LDMAPSUPPORTED=$(shell $(CC) -Wl,-Map=image.map 2>&1 | grep 'unknown option') + LDFLAGS= + ifeq ($(LDMAPSUPPORTED),) + # Override linker flags + LDFLAGS+=-Wl,-Map=image.map + endif endif ifeq ($(EXT_FLASH),1) diff --git a/tools/elf-parser/Makefile b/tools/elf-parser/Makefile index fd89e343c..585d8a973 100644 --- a/tools/elf-parser/Makefile +++ b/tools/elf-parser/Makefile @@ -4,7 +4,7 @@ CC=gcc CFLAGS=-Wall -g -ggdb -CFLAGS+=-I../../include -DWOLFBOOT_ELF -DELF_PARSER -DPRINTF_ENABLED +CFLAGS+=-I../../include -DWOLFBOOT_ELF -DELF_PARSER -DDEBUG_ELF -DPRINTF_ENABLED EXE=elf-parser LIBS= diff --git a/tools/fdt-parser/bcm2710-rpi-3-b.dtb b/tools/fdt-parser/bcm2710-rpi-3-b.dtb new file mode 100644 index 0000000000000000000000000000000000000000..8a13a454f25f74122ec22b32863e379a3dc84446 GIT binary patch literal 33555 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 ZY@YyCN@MS>luFzaCY!(M$Lo~%{|{=r@ecq1 literal 0 HcmV?d00001 diff --git a/tools/fdt-parser/fdt-parser.c b/tools/fdt-parser/fdt-parser.c index e5a9306ca..aabe1bcf9 100644 --- a/tools/fdt-parser/fdt-parser.c +++ b/tools/fdt-parser/fdt-parser.c @@ -29,6 +29,182 @@ #include #include +static int gEnableUnitTest = 0; +#define UNIT_TEST_GROW_SIZE 1024 + +/* Test case for "nxp_t1024.dtb" */ +static int fdt_test(void* fdt) +{ + int off, i; + uint32_t *reg, oldsize; + + #define DDR_ADDRESS 0 + #define DDR_SIZE (2048ULL * 1024ULL * 1024ULL) + #define CPU_NUMCORES 2 + #define SPIN_TABLE_ADDR 0x7FF01900 + #define ENTRY_SIZE 64 + #define SYS_CLK (100000000) /* 100MHz */ + #define PLAT_CLK (SYS_CLK * 4) + #define BUS_CLK (PLAT_CLK / 2) + #define TIMEBASE_HZ (PLAT_CLK / 16) + + struct qportal_info { + uint16_t dliodn; /* DQRR LIODN */ + uint16_t fliodn; /* frame data LIODN */ + uint16_t liodn_offset; + uint8_t sdest; + }; + + #define SET_QP_INFO(dqrr, fdata, off, dest) \ + { .dliodn = dqrr, .fliodn = fdata, .liodn_offset = off, .sdest = dest } + + #define QMAN_NUM_PORTALS 10 + const struct qportal_info qp_info[QMAN_NUM_PORTALS] = { + /* dqrr liodn, frame data liodn, liodn off, sdest */ + SET_QP_INFO(1, 27, 1, 0), + SET_QP_INFO(2, 28, 1, 0), + SET_QP_INFO(3, 29, 1, 1), + SET_QP_INFO(4, 30, 1, 1), + SET_QP_INFO(5, 31, 1, 2), + SET_QP_INFO(6, 32, 1, 2), + SET_QP_INFO(7, 33, 1, 3), + SET_QP_INFO(8, 34, 1, 3), + SET_QP_INFO(9, 35, 1, 0), + SET_QP_INFO(10, 36, 1, 0) + }; + + struct liodn_id_table { + const char* compat; + uint32_t id; + }; + #define SET_LIODN(fdtcomp, liodn) \ + {.compat = fdtcomp, .id = liodn} + + const struct liodn_id_table liodn_tbl[] = { + SET_LIODN("fsl-usb2-mph", 553), + SET_LIODN("fsl-usb2-dr", 554), + SET_LIODN("fsl,esdhc", 552), + SET_LIODN("fsl,pq-sata-v2", 555), + SET_LIODN("fsl,tdm1.0", 560), + SET_LIODN("fsl,qe", 559), + SET_LIODN("fsl,elo3-dma", 147), + SET_LIODN("fsl,elo3-dma", 227), + + SET_LIODN("fsl,qman", 62), + SET_LIODN("fsl,bman", 63), + SET_LIODN("fsl,qoriq-pcie-v2.4", 148), + SET_LIODN("fsl,qoriq-pcie-v2.4", 228), + SET_LIODN("fsl,qoriq-pcie-v2.4", 308), + }; + + /* expand total size */ + oldsize = fdt_totalsize(fdt); + + /* fixup the memory region - single bank */ + off = fdt_find_devtype(fdt, -1, "memory"); + if (off != -FDT_ERR_NOTFOUND) { + /* build addr/size as 64-bit */ + uint8_t ranges[sizeof(uint64_t) * 2], *p = ranges; + *(uint64_t*)p = cpu_to_fdt64(DDR_ADDRESS); + p += sizeof(uint64_t); + *(uint64_t*)p = cpu_to_fdt64(DDR_SIZE); + p += sizeof(uint64_t); + fdt_setprop(fdt, off, "reg", ranges, (int)(p - ranges)); + wolfBoot_printf("FDT: Set memory, start=0x%x, size=0x%x\n", + DDR_ADDRESS, (uint32_t)DDR_SIZE); + } + + /* fixup CPU status and, release address and enable method */ + off = fdt_find_devtype(fdt, -1, "cpu"); + while (off != -FDT_ERR_NOTFOUND) { + int core; + uint64_t core_spin_table_addr; + + reg = (uint32_t*)fdt_getprop(fdt, off, "reg", NULL); + if (reg == NULL) + break; + core = (int)fdt32_to_cpu(*reg); + if (core >= CPU_NUMCORES) { + break; /* invalid core index */ + } + + /* calculate location of spin table for core */ + core_spin_table_addr = (uint64_t)((uintptr_t)( + (uint8_t*)SPIN_TABLE_ADDR + (core * ENTRY_SIZE))); + + fdt_fixup_str(fdt, off, "cpu", "status", (core == 0) ? "okay" : "disabled"); + fdt_fixup_val64(fdt, off, "cpu", "cpu-release-addr", core_spin_table_addr); + fdt_fixup_str(fdt, off, "cpu", "enable-method", "spin-table"); + fdt_fixup_val(fdt, off, "cpu", "timebase-frequency", TIMEBASE_HZ); + fdt_fixup_val(fdt, off, "cpu", "clock-frequency", PLAT_CLK); + fdt_fixup_val(fdt, off, "cpu", "bus-frequency", PLAT_CLK); + + off = fdt_find_devtype(fdt, off, "cpu"); + } + + /* fixup the soc clock */ + off = fdt_find_devtype(fdt, -1, "soc"); + if (off != -FDT_ERR_NOTFOUND) { + fdt_fixup_val(fdt, off, "soc", "bus-frequency", PLAT_CLK); + } + + /* fixup the serial clocks */ + off = fdt_find_devtype(fdt, -1, "serial"); + while (off != -FDT_ERR_NOTFOUND) { + fdt_fixup_val(fdt, off, "serial", "clock-frequency", BUS_CLK); + off = fdt_find_devtype(fdt, off, "serial"); + } + + /* fixup the QE bridge and bus blocks */ + off = fdt_find_devtype(fdt, -1, "qe"); + if (off != -FDT_ERR_NOTFOUND) { + fdt_fixup_val(fdt, off, "qe", "clock-frequency", BUS_CLK); + fdt_fixup_val(fdt, off, "qe", "bus-frequency", BUS_CLK); + fdt_fixup_val(fdt, off, "qe", "brg-frequency", BUS_CLK/2); + } + + /* fixup the LIODN */ + for (i=0; i<(int)(sizeof(liodn_tbl)/sizeof(struct liodn_id_table)); i++) { + off = fdt_node_offset_by_compatible(fdt, -1, liodn_tbl[i].compat); + if (off >= 0) { + fdt_fixup_val(fdt, off, liodn_tbl[i].compat, "fsl,liodn", + liodn_tbl[i].id); + } + } + + /* fixup the QMAN portals */ + off = fdt_node_offset_by_compatible(fdt, -1, "fsl,qman-portal"); + while (off != -FDT_ERR_NOTFOUND) { + const int *ci = fdt_getprop(fdt, off, "cell-index", NULL); + uint32_t liodns[2]; + if (!ci) + break; + i = fdt32_to_cpu(*ci); + + liodns[0] = qp_info[i].dliodn; + liodns[1] = qp_info[i].fliodn; + wolfBoot_printf("FDT: Set %s@%d (%d), %s=%d,%d\n", + "qman-portal", i, off, "fsl,liodn", liodns[0], liodns[1]); + fdt_setprop(fdt, off, "fsl,liodn", liodns, sizeof(liodns)); + + off = fdt_node_offset_by_compatible(fdt, off, "fsl,qman-portal"); + } + + /* mpic clock */ + off = fdt_find_devtype(fdt, -1, "open-pic"); + if (off != -FDT_ERR_NOTFOUND) { + fdt_fixup_val(fdt, off, "open-pic", "clock-frequency", BUS_CLK); + } + + /* resize the device tree */ + fdt_shrink(fdt); + + /* display information */ + printf("FDT Updated: Size %d -> %d\n", oldsize, fdt_totalsize(fdt)); + + return 0; +} + void print_bin(const uint8_t* buffer, uint32_t length) { uint32_t i, notprintable = 0; @@ -59,7 +235,7 @@ void print_bin(const uint8_t* buffer, uint32_t length) int dts_parse(void* dts_addr) { - int ret; + int ret = 0; struct fdt_header *fdt = (struct fdt_header *)dts_addr; const struct fdt_property* prop; int nlen, plen, slen; @@ -113,7 +289,7 @@ int dts_parse(void* dts_addr) } } - return 0; + return ret; } int main(int argc, char *argv[]) @@ -127,6 +303,12 @@ int main(int argc, char *argv[]) if (argc >= 2) { filename = argv[1]; } + while (argc > 2) { + if (strcmp(argv[argc-1], "-t") == 0) { + gEnableUnitTest = 1; + } + argc--; + } printf("FDT Parser (%s):\n", filename); if (filename == NULL) { @@ -144,7 +326,13 @@ int main(int argc, char *argv[]) fseek(f, 0, SEEK_END); imageSz = ftell(f); fseek(f, 0, SEEK_SET); - image = malloc(imageSz); + if (gEnableUnitTest) { + /* add extra 1KB for testing expansion room */ + image = malloc(imageSz + UNIT_TEST_GROW_SIZE); + } + else { + image = malloc(imageSz); + } if (image == NULL) { printf("Allocate %lu failed!\n", imageSz); ret = -1; @@ -159,13 +347,16 @@ int main(int argc, char *argv[]) } fclose(f); + if (ret == 0 && gEnableUnitTest) { + ret = fdt_test(image); + } if (ret == 0) { ret = dts_parse(image); } - printf("Return %d\n", ret); - free(image); + printf("Return %d\n", ret); + return ret; } diff --git a/tools/fdt-parser/nxp_t1024.dtb b/tools/fdt-parser/nxp_t1024.dtb new file mode 100755 index 0000000000000000000000000000000000000000..03acde96ff8a6c30b0fc15eaeeb4ec6046d8292a GIT binary patch literal 65537 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