diff options
author | Brad Bishop <bradleyb@fuzziesquirrel.com> | 2022-08-03 16:55:16 +0300 |
---|---|---|
committer | Brad Bishop <bradleyb@fuzziesquirrel.com> | 2022-08-03 17:56:03 +0300 |
commit | bec4ebc22c43c1ff5c3fddb820d44a88bd3aebf0 (patch) | |
tree | cd378e3e0eaff8fe11880bd397f41671e2347a39 /meta-arm/meta-arm-bsp/recipes-bsp | |
parent | 79161d7a7126cad324ff0c11a93d8e57d80203ed (diff) | |
download | openbmc-bec4ebc22c43c1ff5c3fddb820d44a88bd3aebf0.tar.xz |
Import 80d60e7 from yoctoproject.org meta-arm
To support ARMv8 SoCs.
meta-arm has several patch files. Since they are maintained by the
upstream meta-arm community, add meta-arm to the ignore list in
run-repotest.
Change-Id: Ia87a2e947bbabd347d256eccc47a343e1c885479
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
Diffstat (limited to 'meta-arm/meta-arm-bsp/recipes-bsp')
124 files changed, 23374 insertions, 0 deletions
diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/boot-wrapper-aarch64-fvp-baser-aemv8r64.inc b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/boot-wrapper-aarch64-fvp-baser-aemv8r64.inc new file mode 100644 index 0000000000..8ffa0aa2e0 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/boot-wrapper-aarch64-fvp-baser-aemv8r64.inc @@ -0,0 +1,36 @@ +COMPATIBLE_MACHINE = "fvp-baser-aemv8r64" + +FILESEXTRAPATHS:prepend := "${THISDIR}/files/${MACHINE}:" +SRC_URI:append = " \ + file://0001-aarch64-Rename-labels-and-prepare-for-lower-EL-booti.patch \ + file://0002-aarch64-Prepare-for-EL1-booting.patch \ + file://0003-aarch64-Prepare-for-lower-EL-booting.patch \ + file://0004-gic-v3-Prepare-for-gicv3-with-EL2.patch \ + file://0005-aarch64-Prepare-for-booting-with-EL2.patch \ + file://0006-aarch64-Introduce-EL2-boot-code-for-Armv8-R-AArch64.patch \ + file://0007-Allow-enable-psci-to-choose-between-smc-and-hvc.patch \ + file://0008-aarch64-Disable-CNTPCT_EL0-trap-for-v8-R64.patch \ + file://0009-lds-Mark-the-mem-range.patch \ + file://0010-common-Introduce-the-libfdt.patch \ + file://0011-common-Add-essential-libc-functions.patch \ + file://0012-Makefile-Add-the-libfdt-to-the-Makefile-system.patch \ + file://0013-platform-Add-print_hex-func.patch \ + file://0014-common-Add-mem-usage-to-memreserve.patch \ + file://0015-boot-Add-the-enable-keep-el-compile-option.patch \ + file://0016-Makefile-Change-COUNTER_FREQ-to-100-MHz.patch \ + file://0017-PSCI-Apply-flush-cache-after-setting-branch_data.patch \ + file://0018-PSCI-Add-function-call-entry-point.patch \ + file://0019-lds-Rearrange-and-mark-the-sections.patch \ + file://0020-common-Provide-firmware-info-using-libfdt.patch \ + file://0021-boot-Enable-firmware-node-initialization.patch \ + " + +BOOT_WRAPPER_AARCH64_CMDLINE = "\ +earlycon console=ttyAMA0 loglevel=8 rootfstype=ext4 root=/dev/vda1 rw" + +EXTRA_OECONF += "--enable-psci=hvc --enable-keep-el" + +TUNE_CCARGS = "" + +BOOT_WRAPPER_AARCH64_KERNEL = "u-boot.bin" +do_deploy[depends] += "u-boot:do_deploy" diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/boot-wrapper-aarch64_%.bbappend b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/boot-wrapper-aarch64_%.bbappend new file mode 100644 index 0000000000..c2e7e6e3ff --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/boot-wrapper-aarch64_%.bbappend @@ -0,0 +1,4 @@ +MACHINE_BOOT_WRAPPER_AARCH64_REQUIRE ?= "" +MACHINE_BOOT_WRAPPER_AARCH64_REQUIRE:fvp-baser-aemv8r64 ?= "boot-wrapper-aarch64-fvp-baser-aemv8r64.inc" + +require ${MACHINE_BOOT_WRAPPER_AARCH64_REQUIRE} diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0001-aarch64-Rename-labels-and-prepare-for-lower-EL-booti.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0001-aarch64-Rename-labels-and-prepare-for-lower-EL-booti.patch new file mode 100644 index 0000000000..566070a4f3 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0001-aarch64-Rename-labels-and-prepare-for-lower-EL-booti.patch @@ -0,0 +1,135 @@ +From 3e7cfbe39a2a053d2a6b0d928cc172ed9d1c6da8 Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Tue, 25 May 2021 07:25:00 +0100 +Subject: [PATCH] aarch64: Rename labels and prepare for lower EL booting + +Prepare for booting from lower EL. Rename *_el3 relavant labels with +*_el_max and *_no_el3 with *_keep_el. Since the original _no_el3 means +"We neither do init sequence at this highest EL nor drop to lower EL +when entering to kernel", we rename it with _keep_el to make it more +clear for lower EL initialisation. + +Upstream-Status: Pending +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +--- + arch/aarch64/boot.S | 28 ++++++++++++++++++++-------- + arch/aarch64/psci.S | 9 +++++---- + arch/aarch64/spin.S | 4 ++-- + 3 files changed, 27 insertions(+), 14 deletions(-) + +diff --git a/arch/aarch64/boot.S b/arch/aarch64/boot.S +index 27ba449..84e1646 100644 +--- a/arch/aarch64/boot.S ++++ b/arch/aarch64/boot.S +@@ -21,18 +21,30 @@ ASM_FUNC(_start) + + /* + * EL3 initialisation ++ * Boot sequence ++ * If CurrentEL == EL3, then goto EL3 initialisation and drop to ++ * lower EL before entering the kernel. ++ * Else, no initialisation and keep the current EL before ++ * entering the kernel. + */ + mrs x0, CurrentEL + cmp x0, #CURRENTEL_EL3 +- b.eq 1f ++ b.eq el3_init + ++ /* ++ * We stay in the current EL for entering the kernel ++ */ + mov w0, #1 +- ldr x1, =flag_no_el3 ++ ldr x1, =flag_keep_el + str w0, [x1] + +- b start_no_el3 ++ b start_keep_el + +-1: mov x0, #0x30 // RES1 ++ /* ++ * EL3 initialisation ++ */ ++el3_init: ++ mov x0, #0x30 // RES1 + orr x0, x0, #(1 << 0) // Non-secure EL1 + orr x0, x0, #(1 << 8) // HVC enable + +@@ -124,7 +136,7 @@ ASM_FUNC(_start) + + bl gic_secure_init + +- b start_el3 ++ b start_el_max + + err_invalid_id: + b . +@@ -151,7 +163,7 @@ ASM_FUNC(jump_kernel) + bl find_logical_id + bl setup_stack // Reset stack pointer + +- ldr w0, flag_no_el3 ++ ldr w0, flag_keep_el + cmp w0, #0 // Prepare Z flag + + mov x0, x20 +@@ -160,7 +172,7 @@ ASM_FUNC(jump_kernel) + mov x3, x23 + + b.eq 1f +- br x19 // No EL3 ++ br x19 // Keep current EL + + 1: mov x4, #SPSR_KERNEL + +@@ -178,5 +190,5 @@ ASM_FUNC(jump_kernel) + + .data + .align 3 +-flag_no_el3: ++flag_keep_el: + .long 0 +diff --git a/arch/aarch64/psci.S b/arch/aarch64/psci.S +index 8bd224b..7b8919a 100644 +--- a/arch/aarch64/psci.S ++++ b/arch/aarch64/psci.S +@@ -79,7 +79,7 @@ smc_exit: + ldp x18, x19, [sp], #16 + eret + +-ASM_FUNC(start_el3) ++ASM_FUNC(start_el_max) + ldr x0, =vector + bl setup_vector + +@@ -89,10 +89,11 @@ ASM_FUNC(start_el3) + b psci_first_spin + + /* +- * This PSCI implementation requires EL3. Without EL3 we'll only boot the +- * primary cpu, all others will be trapped in an infinite loop. ++ * This PSCI implementation requires the highest EL(EL3 or Armv8-R EL2). ++ * Without the highest EL, we'll only boot the primary cpu, all othersr ++ * will be trapped in an infinite loop. + */ +-ASM_FUNC(start_no_el3) ++ASM_FUNC(start_keep_el) + cpuid x0, x1 + bl find_logical_id + cbz x0, psci_first_spin +diff --git a/arch/aarch64/spin.S b/arch/aarch64/spin.S +index 1ea1c0b..bfb1d47 100644 +--- a/arch/aarch64/spin.S ++++ b/arch/aarch64/spin.S +@@ -12,8 +12,8 @@ + + .text + +-ASM_FUNC(start_el3) +-ASM_FUNC(start_no_el3) ++ASM_FUNC(start_el_max) ++ASM_FUNC(start_keep_el) + cpuid x0, x1 + bl find_logical_id + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0002-aarch64-Prepare-for-EL1-booting.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0002-aarch64-Prepare-for-EL1-booting.patch new file mode 100644 index 0000000000..46447b8f28 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0002-aarch64-Prepare-for-EL1-booting.patch @@ -0,0 +1,48 @@ +From 26f9b5354c2de9cc052531096ff92b04c3a3846f Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Tue, 25 May 2021 07:25:00 +0100 +Subject: [PATCH] aarch64: Prepare for EL1 booting + +When booting from EL1, add a check and skip the init of +sctlr_el2 in jump_kernel + +Upstream-Status: Pending +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +Reviewed-by: Andre Przywara <andre.przywara@arm.com> +--- + arch/aarch64/boot.S | 6 +++++- + arch/aarch64/include/asm/cpu.h | 1 + + 2 files changed, 6 insertions(+), 1 deletion(-) + +diff --git a/arch/aarch64/boot.S b/arch/aarch64/boot.S +index 84e1646..b589744 100644 +--- a/arch/aarch64/boot.S ++++ b/arch/aarch64/boot.S +@@ -156,10 +156,14 @@ ASM_FUNC(jump_kernel) + ldr x0, =SCTLR_EL1_KERNEL + msr sctlr_el1, x0 + ++ mrs x0, CurrentEL ++ cmp x0, #CURRENTEL_EL2 ++ b.lt 1f ++ + ldr x0, =SCTLR_EL2_KERNEL + msr sctlr_el2, x0 + +- cpuid x0, x1 ++1: cpuid x0, x1 + bl find_logical_id + bl setup_stack // Reset stack pointer + +diff --git a/arch/aarch64/include/asm/cpu.h b/arch/aarch64/include/asm/cpu.h +index 63eb1c3..b1003f4 100644 +--- a/arch/aarch64/include/asm/cpu.h ++++ b/arch/aarch64/include/asm/cpu.h +@@ -11,6 +11,7 @@ + + #define MPIDR_ID_BITS 0xff00ffffff + ++#define CURRENTEL_EL2 (2 << 2) + #define CURRENTEL_EL3 (3 << 2) + + /* diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0003-aarch64-Prepare-for-lower-EL-booting.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0003-aarch64-Prepare-for-lower-EL-booting.patch new file mode 100644 index 0000000000..db81355b66 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0003-aarch64-Prepare-for-lower-EL-booting.patch @@ -0,0 +1,55 @@ +From ce628de7699dd6401ddf713efaa49872e2733619 Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Tue, 25 May 2021 07:25:00 +0100 +Subject: [PATCH] aarch64: Prepare for lower EL booting + +Save SPSR_KERNEL into spsr_to_elx during el3_init. +The jump_kernel will load spsr_to_elx into spsr_el3. + +This change will make it easier to control whether drop to lower EL +before jumping to the kernel. + +Upstream-Status: Pending +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +Reviewed-by: Andre Przywara <andre.przywara@arm.com> +--- + arch/aarch64/boot.S | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +diff --git a/arch/aarch64/boot.S b/arch/aarch64/boot.S +index b589744..6b45afc 100644 +--- a/arch/aarch64/boot.S ++++ b/arch/aarch64/boot.S +@@ -130,7 +130,16 @@ el3_init: + mov x0, #ZCR_EL3_LEN_MASK // SVE: Enable full vector len + msr ZCR_EL3, x0 // for EL2. + +-1: ++ /* ++ * Save SPSR_KERNEL into spsr_to_elx. ++ * The jump_kernel will load spsr_to_elx into spsr_el3 ++ */ ++1: mov w0, #SPSR_KERNEL ++ ldr x1, =spsr_to_elx ++ str w0, [x1] ++ b el_max_init ++ ++el_max_init: + ldr x0, =COUNTER_FREQ + msr cntfrq_el0, x0 + +@@ -178,7 +187,7 @@ ASM_FUNC(jump_kernel) + b.eq 1f + br x19 // Keep current EL + +-1: mov x4, #SPSR_KERNEL ++1: ldr w4, spsr_to_elx + + /* + * If bit 0 of the kernel address is set, we're entering in AArch32 +@@ -196,3 +205,5 @@ ASM_FUNC(jump_kernel) + .align 3 + flag_keep_el: + .long 0 ++spsr_to_elx: ++ .long 0 diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0004-gic-v3-Prepare-for-gicv3-with-EL2.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0004-gic-v3-Prepare-for-gicv3-with-EL2.patch new file mode 100644 index 0000000000..e10182e1ab --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0004-gic-v3-Prepare-for-gicv3-with-EL2.patch @@ -0,0 +1,105 @@ +From 483d363bf825082b6db6de3c57d169e741861891 Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Tue, 25 May 2021 07:25:00 +0100 +Subject: [PATCH] gic-v3: Prepare for gicv3 with EL2 + +This is a preparation for allowing boot-wrapper configuring the gicv3 +with EL2. + +When confiuring with EL2, since there is no ICC_CTLR_EL2, the +ICC_CTLR_EL3 cannot be replaced with ICC_CTLR_EL2 simply. +See [https://developer.arm.com/documentation/ihi0069/latest/]. + +As the caller, gic_secure_init expects the ICC_CTLR to be written, +we change the function into gic_init_icc_ctlr(). In the GIC spec, +the r/w bits in this register ([6:0]) either affect EL3 IRQ routing +(not applicable since no EL3), non-secure IRQ handling (not applicable +since only secure state in Armv8-R aarch64), or are aliased to +ICC_CTLR_EL1 bits. +So, based on this, the new gic_init_icc_ctlr() would be: +When currentEL is EL3, init ICC_CTLR_EL3 as before. +When currentEL is not EL3, init ICC_CTLR_EL1 with ICC_CTLR_EL1_RESET. + +Upstream-Status: Pending +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +Reviewed-by: Andre Przywara <andre.przywara@arm.com> +--- + arch/aarch32/include/asm/gic-v3.h | 7 +++++++ + arch/aarch64/include/asm/gic-v3.h | 23 ++++++++++++++++++++--- + common/gic-v3.c | 2 +- + 3 files changed, 28 insertions(+), 4 deletions(-) + +diff --git a/arch/aarch32/include/asm/gic-v3.h b/arch/aarch32/include/asm/gic-v3.h +index 65f38de..11e7bc7 100644 +--- a/arch/aarch32/include/asm/gic-v3.h ++++ b/arch/aarch32/include/asm/gic-v3.h +@@ -9,6 +9,8 @@ + #ifndef __ASM_AARCH32_GICV3_H + #define __ASM_AARCH32_GICV3_H + ++#define ICC_CTLR_RESET (0UL) ++ + static inline void gic_write_icc_sre(uint32_t val) + { + asm volatile ("mcr p15, 6, %0, c12, c12, 5" : : "r" (val)); +@@ -19,4 +21,9 @@ static inline void gic_write_icc_ctlr(uint32_t val) + asm volatile ("mcr p15, 6, %0, c12, c12, 4" : : "r" (val)); + } + ++static inline void gic_init_icc_ctlr() ++{ ++ gic_write_icc_ctlr(ICC_CTLR_RESET); ++} ++ + #endif +diff --git a/arch/aarch64/include/asm/gic-v3.h b/arch/aarch64/include/asm/gic-v3.h +index 5b32380..090ab0b 100644 +--- a/arch/aarch64/include/asm/gic-v3.h ++++ b/arch/aarch64/include/asm/gic-v3.h +@@ -15,14 +15,31 @@ + #define ICC_CTLR_EL3 "S3_6_C12_C12_4" + #define ICC_PMR_EL1 "S3_0_C4_C6_0" + ++#define ICC_CTLR_EL3_RESET (0UL) ++#define ICC_CTLR_EL1_RESET (0UL) ++ ++static inline uint32_t current_el(void) ++{ ++ uint32_t val; ++ ++ asm volatile ("mrs %0, CurrentEL" : "=r" (val)); ++ return val; ++} ++ + static inline void gic_write_icc_sre(uint32_t val) + { +- asm volatile ("msr " ICC_SRE_EL3 ", %0" : : "r" (val)); ++ if (current_el() == CURRENTEL_EL3) ++ asm volatile ("msr " ICC_SRE_EL3 ", %0" : : "r" (val)); ++ else ++ asm volatile ("msr " ICC_SRE_EL2 ", %0" : : "r" (val)); + } + +-static inline void gic_write_icc_ctlr(uint32_t val) ++static inline void gic_init_icc_ctlr() + { +- asm volatile ("msr " ICC_CTLR_EL3 ", %0" : : "r" (val)); ++ if (current_el() == CURRENTEL_EL3) ++ asm volatile ("msr " ICC_CTLR_EL3 ", %0" : : "r" (ICC_CTLR_EL3_RESET)); ++ else ++ asm volatile ("msr " ICC_CTLR_EL1 ", %0" : : "r" (ICC_CTLR_EL1_RESET)); + } + + #endif +diff --git a/common/gic-v3.c b/common/gic-v3.c +index 6207007..a0fe564 100644 +--- a/common/gic-v3.c ++++ b/common/gic-v3.c +@@ -117,6 +117,6 @@ void gic_secure_init(void) + gic_write_icc_sre(ICC_SRE_Enable | ICC_SRE_DIB | ICC_SRE_DFB | ICC_SRE_SRE); + isb(); + +- gic_write_icc_ctlr(0); ++ gic_init_icc_ctlr(); + isb(); + } diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0005-aarch64-Prepare-for-booting-with-EL2.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0005-aarch64-Prepare-for-booting-with-EL2.patch new file mode 100644 index 0000000000..3b6f78a579 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0005-aarch64-Prepare-for-booting-with-EL2.patch @@ -0,0 +1,63 @@ +From be814863cdd5f61d9a16eec012d500550053c8c6 Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Tue, 25 May 2021 07:25:00 +0100 +Subject: [PATCH] aarch64: Prepare for booting with EL2 + +Prepare for allowing boot-wrapper to be entered in EL2. +Detect current EL and set the corresponding EL registers. + +Upstream-Status: Pending +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +Reviewed-by: Andre Przywara <andre.przywara@arm.com> +--- + arch/aarch64/boot.S | 8 ++++++++ + arch/aarch64/utils.S | 10 +++++++++- + 2 files changed, 17 insertions(+), 1 deletion(-) + +diff --git a/arch/aarch64/boot.S b/arch/aarch64/boot.S +index 6b45afc..908764a 100644 +--- a/arch/aarch64/boot.S ++++ b/arch/aarch64/boot.S +@@ -195,10 +195,18 @@ ASM_FUNC(jump_kernel) + */ + bfi x4, x19, #5, #1 + ++ mrs x5, CurrentEL ++ cmp x5, #CURRENTEL_EL2 ++ b.eq 1f ++ + msr elr_el3, x19 + msr spsr_el3, x4 + eret + ++1: msr elr_el2, x19 ++ msr spsr_el2, x4 ++ eret ++ + .ltorg + + .data +diff --git a/arch/aarch64/utils.S b/arch/aarch64/utils.S +index 85c7f8a..f02a249 100644 +--- a/arch/aarch64/utils.S ++++ b/arch/aarch64/utils.S +@@ -34,10 +34,18 @@ ASM_FUNC(find_logical_id) + ret + + /* +- * Setup EL3 vectors ++ * Setup EL3/EL2 vectors + * x0: vector address + */ + ASM_FUNC(setup_vector) ++ mrs x1, CurrentEL ++ cmp x1, #CURRENTEL_EL2 ++ b.eq 1f ++ + msr VBAR_EL3, x0 + isb + ret ++ ++1: msr VBAR_EL2, x0 ++ isb ++ ret diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0006-aarch64-Introduce-EL2-boot-code-for-Armv8-R-AArch64.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0006-aarch64-Introduce-EL2-boot-code-for-Armv8-R-AArch64.patch new file mode 100644 index 0000000000..aaacc72945 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0006-aarch64-Introduce-EL2-boot-code-for-Armv8-R-AArch64.patch @@ -0,0 +1,182 @@ +From 81df76f8d94cb6c31c01739b078a72bdb8497441 Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Tue, 25 May 2021 07:25:00 +0100 +Subject: [PATCH] aarch64: Introduce EL2 boot code for Armv8-R AArch64 + +The Armv8-R AArch64 profile does not support the EL3 exception level. +The Armv8-R AArch64 profile allows for an (optional) VMSAv8-64 MMU +at EL1, which allows to run off-the-shelf Linux. However EL2 only +supports a PMSA, which is not supported by Linux, so we need to drop +into EL1 before entering the kernel. + +We add a new err_invalid_arch symbol as a dead loop. If we detect the +current Armv8-R aarch64 only supports with PMSA, meaning we cannot boot +Linux anymore, then we jump to err_invalid_arch. + +During Armv8-R aarch64 init, to make sure nothing unexpected traps into +EL2, we auto-detect and config FIEN and EnSCXT in HCR_EL2. + +The boot sequence is: +If CurrentEL == EL3, then goto EL3 initialisation and drop to lower EL + before entering the kernel. +If CurrentEL == EL2 && id_aa64mmfr0_el1.MSA == 0xf (Armv8-R aarch64), + if id_aa64mmfr0_el1.MSA_frac == 0x2, + then goto Armv8-R AArch64 initialisation and drop to EL1 before + entering the kernel. + else, which means VMSA unsupported and cannot boot Linux, + goto err_invalid_arch (dead loop). +Else, no initialisation and keep the current EL before entering the + kernel. + +Upstream-Status: Pending +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +--- + arch/aarch64/boot.S | 92 +++++++++++++++++++++++++++++++++- + arch/aarch64/include/asm/cpu.h | 2 + + 2 files changed, 92 insertions(+), 2 deletions(-) + +diff --git a/arch/aarch64/boot.S b/arch/aarch64/boot.S +index 908764a..def9192 100644 +--- a/arch/aarch64/boot.S ++++ b/arch/aarch64/boot.S +@@ -24,16 +24,24 @@ ASM_FUNC(_start) + * Boot sequence + * If CurrentEL == EL3, then goto EL3 initialisation and drop to + * lower EL before entering the kernel. ++ * If CurrentEL == EL2 && id_aa64mmfr0_el1.MSA == 0xf, then ++ * If id_aa64mmfr0_el1.MSA_frac == 0x2, then goto ++ * Armv8-R AArch64 initialisation and drop to EL1 before ++ * entering the kernel. ++ * Else, which means VMSA unsupported and cannot boot Linux, ++ * goto err_invalid_arch (dead loop). + * Else, no initialisation and keep the current EL before + * entering the kernel. + */ + mrs x0, CurrentEL +- cmp x0, #CURRENTEL_EL3 +- b.eq el3_init ++ cmp x0, #CURRENTEL_EL2 ++ bgt el3_init ++ beq el2_init + + /* + * We stay in the current EL for entering the kernel + */ ++keep_el: + mov w0, #1 + ldr x1, =flag_keep_el + str w0, [x1] +@@ -139,6 +147,85 @@ el3_init: + str w0, [x1] + b el_max_init + ++ /* ++ * EL2 Armv8-R AArch64 initialisation ++ */ ++el2_init: ++ /* Detect Armv8-R AArch64 */ ++ mrs x1, id_aa64mmfr0_el1 ++ /* ++ * Check MSA, bits [51:48]: ++ * 0xf means Armv8-R AArch64. ++ * If not 0xf, proceed in Armv8-A EL2. ++ */ ++ ubfx x0, x1, #48, #4 // MSA ++ cmp x0, 0xf ++ bne keep_el ++ /* ++ * Check MSA_frac, bits [55:52]: ++ * 0x2 means EL1&0 translation regime also supports VMSAv8-64. ++ */ ++ ubfx x0, x1, #52, #4 // MSA_frac ++ cmp x0, 0x2 ++ /* ++ * If not 0x2, no VMSA, so cannot boot Linux and dead loop. ++ * Also, since the architecture guarantees that those CPUID ++ * fields never lose features when the value in a field ++ * increases, we use blt to cover it. ++ */ ++ blt err_invalid_arch ++ ++ mrs x0, midr_el1 ++ msr vpidr_el2, x0 ++ ++ mrs x0, mpidr_el1 ++ msr vmpidr_el2, x0 ++ ++ mov x0, #(1 << 31) // VTCR_MSA: VMSAv8-64 support ++ msr vtcr_el2, x0 ++ ++ /* Init HCR_EL2 */ ++ mov x0, #(1 << 31) // RES1: Armv8-R aarch64 only ++ ++ mrs x1, id_aa64pfr0_el1 ++ ubfx x2, x1, #56, 4 // ID_AA64PFR0_EL1.CSV2 ++ cmp x2, 0x2 ++ b.lt 1f ++ /* ++ * Disable trap when accessing SCTXNUM_EL0 or SCTXNUM_EL1 ++ * if FEAT_CSV2. ++ */ ++ orr x0, x0, #(1 << 53) // HCR_EL2.EnSCXT ++ ++1: ubfx x2, x1, #28, 4 // ID_AA64PFR0_EL1.RAS ++ cmp x2, 0x2 ++ b.lt 1f ++ /* Disable trap when accessing ERXPFGCDN_EL1 if FEAT_RASv1p1. */ ++ orr x0, x0, #(1 << 47) // HCR_EL2.FIEN ++ ++ /* Enable pointer authentication if present */ ++1: mrs x1, id_aa64isar1_el1 ++ /* ++ * If ID_AA64ISAR1_EL1.{GPI, GPA, API, APA} == {0000, 0000, 0000, 0000} ++ * then HCR_EL2.APK and HCR_EL2.API are RES 0. ++ * Else ++ * set HCR_EL2.APK and HCR_EL2.API. ++ */ ++ ldr x2, =(((0xff) << 24) | (0xff << 4)) ++ and x1, x1, x2 ++ cbz x1, 1f ++ ++ orr x0, x0, #(1 << 40) // HCR_EL2.APK ++ orr x0, x0, #(1 << 41) // HCR_EL2.API ++ ++1: msr hcr_el2, x0 ++ isb ++ ++ mov w0, #SPSR_KERNEL_EL1 ++ ldr x1, =spsr_to_elx ++ str w0, [x1] ++ // fall through ++ + el_max_init: + ldr x0, =COUNTER_FREQ + msr cntfrq_el0, x0 +@@ -148,6 +235,7 @@ el_max_init: + b start_el_max + + err_invalid_id: ++err_invalid_arch: + b . + + /* +diff --git a/arch/aarch64/include/asm/cpu.h b/arch/aarch64/include/asm/cpu.h +index b1003f4..91f803c 100644 +--- a/arch/aarch64/include/asm/cpu.h ++++ b/arch/aarch64/include/asm/cpu.h +@@ -25,6 +25,7 @@ + #define SPSR_I (1 << 7) /* IRQ masked */ + #define SPSR_F (1 << 6) /* FIQ masked */ + #define SPSR_T (1 << 5) /* Thumb */ ++#define SPSR_EL1H (5 << 0) /* EL1 Handler mode */ + #define SPSR_EL2H (9 << 0) /* EL2 Handler mode */ + #define SPSR_HYP (0x1a << 0) /* M[3:0] = hyp, M[4] = AArch32 */ + +@@ -43,6 +44,7 @@ + #else + #define SCTLR_EL1_KERNEL SCTLR_EL1_RES1 + #define SPSR_KERNEL (SPSR_A | SPSR_D | SPSR_I | SPSR_F | SPSR_EL2H) ++#define SPSR_KERNEL_EL1 (SPSR_A | SPSR_D | SPSR_I | SPSR_F | SPSR_EL1H) + #endif + + #ifndef __ASSEMBLY__ diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0007-Allow-enable-psci-to-choose-between-smc-and-hvc.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0007-Allow-enable-psci-to-choose-between-smc-and-hvc.patch new file mode 100644 index 0000000000..b130854895 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0007-Allow-enable-psci-to-choose-between-smc-and-hvc.patch @@ -0,0 +1,89 @@ +From f5a31b4f4ea8daaa0d337d5a2322ddb1912083fc Mon Sep 17 00:00:00 2001 +From: Qi Feng <qi.feng@arm.com> +Date: Wed, 26 May 2021 17:52:01 +0800 +Subject: [PATCH] Allow --enable-psci to choose between smc and hvc + +According to Armv8-R AArch64 manual [1], Armv8-R AArch64 does not +support smc: + +- Pseudocode for AArch64.CheckForSMCUndefOrTrap has this snippet: + + if !HaveEL(EL3) || PSTATE.EL == EL0 then + UNDEFINED; + + And Armv8-R AArch64 does not have EL3. + +- In the document of HCR_EL2 TSC bit: + If EL3 is not implemented and HCR_EL2.NV is 0, it is IMPLEMENTATION + DEFINED whether this bit is: + - RES0. + - Implemented with the functionality as described in HCR_EL2.TSC. + +So hvc is needed in this situation. And due to the lack of libfdt, the +psci method cannot be modified at runtime. + +To use smc, use --enable-psci or --enable-psci=smc. +To use hvc, use --enable-psci=hvc. + +[1]: https://developer.arm.com/documentation/ddi0600/latest/ + +Issue-Id: SCM-2654 +Upstream-Status: Pending +Signed-off-by: Qi Feng <qi.feng@arm.com> +Change-Id: Ib8afabdad2d98bc37371d165bbb6f1f9b88bfc87 + +Upstream-Status: Pending +Signed-off-by: Huifeng Zhang <Huifeng.Zhang@arm.com> +--- + Makefile.am | 10 +++++----- + configure.ac | 14 +++++++++----- + 2 files changed, 14 insertions(+), 10 deletions(-) + +diff --git a/Makefile.am b/Makefile.am +index f941b07..88a27de 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -50,11 +50,11 @@ endif + if PSCI + ARCH_OBJ += psci.o + COMMON_OBJ += psci.o +-PSCI_NODE := psci { \ +- compatible = \"arm,psci\"; \ +- method = \"smc\"; \ +- cpu_on = <$(PSCI_CPU_ON)>; \ +- cpu_off = <$(PSCI_CPU_OFF)>; \ ++PSCI_NODE := psci { \ ++ compatible = \"arm,psci\"; \ ++ method = \"$(PSCI_METHOD)\"; \ ++ cpu_on = <$(PSCI_CPU_ON)>; \ ++ cpu_off = <$(PSCI_CPU_OFF)>; \ + }; + CPU_NODES := $(shell perl -I $(SCRIPT_DIR) $(SCRIPT_DIR)/addpsci.pl $(KERNEL_DTB)) + else +diff --git a/configure.ac b/configure.ac +index 9e3b722..53e51be 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -83,13 +83,17 @@ AS_IF([test "x$X_IMAGE" != "x"], + # Allow a user to pass --enable-psci + AC_ARG_ENABLE([psci], + AS_HELP_STRING([--disable-psci], [disable the psci boot method]), +- [USE_PSCI=$enableval], [USE_PSCI="yes"]) +-AM_CONDITIONAL([PSCI], [test "x$USE_PSCI" = "xyes"]) +-AS_IF([test "x$USE_PSCI" = "xyes"], [], [USE_PSCI=no]) +- +-AS_IF([test "x$USE_PSCI" != "xyes" -a "x$KERNEL_ES" = "x32"], ++ [case "${enableval}" in ++ yes|smc) USE_PSCI=smc ;; ++ hvc) USE_PSCI=hvc ;; ++ *) AC_MSG_ERROR([Bad value "${enableval}" for --enable-psci. Use "smc" or "hvc"]) ;; ++ esac], [USE_PSCI="yes"]) ++AM_CONDITIONAL([PSCI], [test "x$USE_PSCI" = "xyes" -o "x$USE_PSCI" = "xsmc" -o "x$USE_PSCI" = "xhvc"]) ++ ++AS_IF([test "x$USE_PSCI" = "xno" -a "x$KERNEL_ES" = "x32"], + [AC_MSG_ERROR([With an AArch32 kernel, boot method must be PSCI.])] + ) ++AC_SUBST([PSCI_METHOD], [$USE_PSCI]) + + # Allow a user to pass --with-initrd + AC_ARG_WITH([initrd], diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0008-aarch64-Disable-CNTPCT_EL0-trap-for-v8-R64.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0008-aarch64-Disable-CNTPCT_EL0-trap-for-v8-R64.patch new file mode 100644 index 0000000000..2ce28b7071 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0008-aarch64-Disable-CNTPCT_EL0-trap-for-v8-R64.patch @@ -0,0 +1,48 @@ +From 3f4614e02f0f8d2522510578da2752f8e3511bb3 Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Mon, 25 Oct 2021 17:09:13 +0800 +Subject: [PATCH] aarch64: Disable CNTPCT_EL0 trap for v8-R64 + +To allow EL1 to access CNTPCT_EL0 without traping into EL2, we need to +set CNTHCTL_EL2.EL1PCTEN to 1. + +For v8-R64, the CNTHCTL_EL2 register follows the v8-A architecture. +However, as described in the v8-A architecture profile, the +CNTHCTL_EL2's bit assignments are different according to whether the +FEAT_VHE is implemented. + +Since v8-R64 does not support FEAT_VHE, we do not need to detect +FEAT_VHE. We can simply set CNTHCTL_EL2.EL1PCTEN to 1. + +Issue-ID: SCM-3508 +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +Change-Id: I4147e66341c8153312021e6f2ab67d0037246da1 +--- + arch/aarch64/boot.S | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/arch/aarch64/boot.S b/arch/aarch64/boot.S +index def9192..6dbd5cc 100644 +--- a/arch/aarch64/boot.S ++++ b/arch/aarch64/boot.S +@@ -219,6 +219,18 @@ el2_init: + orr x0, x0, #(1 << 41) // HCR_EL2.API + + 1: msr hcr_el2, x0 ++ ++ /* ++ * To disable trap when accessing CNTPCT_EL0, we need to set ++ * CNTHCTL_EL2.EL1PCTEN to 1. However, the CNTHCTL_EL2 bit assignments ++ * are different according to whether the FEAT_VHE is implemented. ++ * ++ * For Armv8-R AArch64, FEAT_VHE is not supported, so we do not need to ++ * detect FEAT_VHE(ID_AA64MMFR1_EL1.VH) and simply set ++ * CNTHCTL_EL2.EL1PCTEN to 1. ++ */ ++ mov x0, #1 // CNTHCTL_EL2.EL1PCTEN ++ msr cnthctl_el2, x0 + isb + + mov w0, #SPSR_KERNEL_EL1 diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0009-lds-Mark-the-mem-range.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0009-lds-Mark-the-mem-range.patch new file mode 100644 index 0000000000..0c310eb553 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0009-lds-Mark-the-mem-range.patch @@ -0,0 +1,38 @@ +From 2851f0e6c1216894b9498d7b91256bb1ef49e544 Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Tue, 2 Nov 2021 15:10:28 +0800 +Subject: [PATCH] lds: Mark the mem range + +Add firmware_start and firmware_end, so that we can use them to +calculate the mem range of boot-wrapper and then set the range to +/memreserve/ of dtb. + +Issue-ID: SCM-3815 +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +Change-Id: Idc5a2894e193c75381049a0f359b4b2a51c567ee +--- + model.lds.S | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/model.lds.S b/model.lds.S +index d4e7e13..ab98ddf 100644 +--- a/model.lds.S ++++ b/model.lds.S +@@ -64,6 +64,7 @@ SECTIONS + #endif + + .boot PHYS_OFFSET: { ++ PROVIDE(firmware_start = .); + *(.init) + *(.text*) + *(.data* .rodata* .bss* COMMON) +@@ -76,6 +77,7 @@ SECTIONS + mbox = .; + QUAD(0x0) + } ++ PROVIDE(firmware_end = .); + + ASSERT(etext <= (PHYS_OFFSET + TEXT_LIMIT), ".text overflow!") + } diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0010-common-Introduce-the-libfdt.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0010-common-Introduce-the-libfdt.patch new file mode 100644 index 0000000000..0305f8ba00 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0010-common-Introduce-the-libfdt.patch @@ -0,0 +1,6044 @@ +From fadf04f44b679d85e55b2e5f220fecbebb52ad03 Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Tue, 28 Dec 2021 17:02:17 +0800 +Subject: [PATCH] common: Introduce the libfdt + +We introduce libfdt (v1.6.1) [1] to boot-wrapper, so we can dynamically +add the firmware node. + +According to [2]: The libfdt is GPL/BSD dual-licensed which means it can +be used either under the terms of GPL, or under the terms of BSD. +We choose BSD because the boot-wrapper is under BSD. + +[1]: https://github.com/dgibson/dtc/tree/v1.6.1/libfdt +[2]: https://github.com/dgibson/dtc/blob/v1.6.1/README.license + +Issue-Id: SCM-3814 +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +Change-Id: Iec2f469053c8ac0ed38838c597b21a42bdf67b38 +--- + common/libfdt/README.license | 56 + + common/libfdt/fdt.c | 335 +++++ + common/libfdt/fdt_addresses.c | 101 ++ + common/libfdt/fdt_check.c | 93 ++ + common/libfdt/fdt_empty_tree.c | 38 + + common/libfdt/fdt_overlay.c | 882 +++++++++++++ + common/libfdt/fdt_ro.c | 859 +++++++++++++ + common/libfdt/fdt_rw.c | 500 +++++++ + common/libfdt/fdt_strerror.c | 59 + + common/libfdt/fdt_sw.c | 384 ++++++ + common/libfdt/fdt_wip.c | 94 ++ + common/libfdt/libfdt_internal.h | 192 +++ + include/fdt.h | 66 + + include/libfdt.h | 2147 +++++++++++++++++++++++++++++++ + include/libfdt_env.h | 95 ++ + 15 files changed, 5901 insertions(+) + create mode 100644 common/libfdt/README.license + create mode 100644 common/libfdt/fdt.c + create mode 100644 common/libfdt/fdt_addresses.c + create mode 100644 common/libfdt/fdt_check.c + create mode 100644 common/libfdt/fdt_empty_tree.c + create mode 100644 common/libfdt/fdt_overlay.c + create mode 100644 common/libfdt/fdt_ro.c + create mode 100644 common/libfdt/fdt_rw.c + create mode 100644 common/libfdt/fdt_strerror.c + create mode 100644 common/libfdt/fdt_sw.c + create mode 100644 common/libfdt/fdt_wip.c + create mode 100644 common/libfdt/libfdt_internal.h + create mode 100644 include/fdt.h + create mode 100644 include/libfdt.h + create mode 100644 include/libfdt_env.h + +diff --git a/common/libfdt/README.license b/common/libfdt/README.license +new file mode 100644 +index 0000000..102b004 +--- /dev/null ++++ b/common/libfdt/README.license +@@ -0,0 +1,56 @@ ++Licensing and contribution policy of dtc and libfdt ++=================================================== ++ ++This dtc package contains two pieces of software: dtc itself, and ++libfdt which comprises the files in the libfdt/ subdirectory. These ++two pieces of software, although closely related, are quite distinct. ++dtc does not incorporate or rely on libfdt for its operation, nor vice ++versa. It is important that these two pieces of software have ++different license conditions. ++ ++As SPDX license tags in each source file attest, dtc is licensed ++under the GNU GPL. The full text of the GPL can be found in the file ++entitled 'GPL' which should be included in this package. dtc code, ++therefore, may not be incorporated into works which do not have a GPL ++compatible license. ++ ++libfdt, however, is GPL/BSD dual-licensed. That is, it may be used ++either under the terms of the GPL, or under the terms of the 2-clause ++BSD license (aka the ISC license). The full terms of that license can ++be found are in the file entitled 'BSD-2-Clause'. This is, in ++practice, equivalent to being BSD licensed, since the terms of the BSD ++license are strictly more permissive than the GPL. ++ ++I made the decision to license libfdt in this way because I want to ++encourage widespread and correct usage of flattened device trees, ++including by proprietary or otherwise GPL-incompatible firmware or ++tools. Allowing libfdt to be used under the terms of the BSD license ++makes that it easier for vendors or authors of such software to do so. ++ ++This does mean that libfdt code could be "stolen" - say, included in a ++proprietary fimware and extended without contributing those extensions ++back to the libfdt mainline. While I hope that doesn't happen, I ++believe the goal of allowing libfdt to be widely used is more ++important than avoiding that. libfdt is quite small, and hardly ++rocket science; so the incentive for such impolite behaviour is small, ++and the inconvenience caused thereby is not dire. ++ ++Licenses such as the LGPL which would allow code to be used in non-GPL ++software, but also require contributions to be returned were ++considered. However, libfdt is designed to be used in firmwares and ++other environments with unusual technical constraints. It's difficult ++to anticipate all possible changes which might be needed to meld ++libfdt into such environments and so difficult to suitably word a ++license that puts the boundary between what is and isn't permitted in ++the intended place. Again, I judged encouraging widespread use of ++libfdt by keeping the license terms simple and familiar to be the more ++important goal. ++ ++**IMPORTANT** It's intended that all of libfdt as released remain ++permissively licensed this way. Therefore only contributions which ++are released under these terms can be merged into the libfdt mainline. ++ ++ ++David Gibson <david@gibson.dropbear.id.au> ++(principal original author of dtc and libfdt) ++2 November 2007 +diff --git a/common/libfdt/fdt.c b/common/libfdt/fdt.c +new file mode 100644 +index 0000000..9fe7cf4 +--- /dev/null ++++ b/common/libfdt/fdt.c +@@ -0,0 +1,335 @@ ++// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) ++/* ++ * libfdt - Flat Device Tree manipulation ++ * Copyright (C) 2006 David Gibson, IBM Corporation. ++ */ ++#include "libfdt_env.h" ++ ++#include <fdt.h> ++#include <libfdt.h> ++ ++#include "libfdt_internal.h" ++ ++/* ++ * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks ++ * that the given buffer contains what appears to be a flattened ++ * device tree with sane information in its header. ++ */ ++int32_t fdt_ro_probe_(const void *fdt) ++{ ++ uint32_t totalsize = fdt_totalsize(fdt); ++ ++ if (can_assume(VALID_DTB)) ++ return totalsize; ++ ++ /* The device tree must be at an 8-byte aligned address */ ++ if ((uintptr_t)fdt & 7) ++ return -FDT_ERR_ALIGNMENT; ++ ++ if (fdt_magic(fdt) == FDT_MAGIC) { ++ /* Complete tree */ ++ if (!can_assume(LATEST)) { ++ if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) ++ return -FDT_ERR_BADVERSION; ++ if (fdt_last_comp_version(fdt) > ++ FDT_LAST_SUPPORTED_VERSION) ++ return -FDT_ERR_BADVERSION; ++ } ++ } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { ++ /* Unfinished sequential-write blob */ ++ if (!can_assume(VALID_INPUT) && fdt_size_dt_struct(fdt) == 0) ++ return -FDT_ERR_BADSTATE; ++ } else { ++ return -FDT_ERR_BADMAGIC; ++ } ++ ++ if (totalsize < INT32_MAX) ++ return totalsize; ++ else ++ return -FDT_ERR_TRUNCATED; ++} ++ ++static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off) ++{ ++ return (off >= hdrsize) && (off <= totalsize); ++} ++ ++static int check_block_(uint32_t hdrsize, uint32_t totalsize, ++ uint32_t base, uint32_t size) ++{ ++ if (!check_off_(hdrsize, totalsize, base)) ++ return 0; /* block start out of bounds */ ++ if ((base + size) < base) ++ return 0; /* overflow */ ++ if (!check_off_(hdrsize, totalsize, base + size)) ++ return 0; /* block end out of bounds */ ++ return 1; ++} ++ ++size_t fdt_header_size_(uint32_t version) ++{ ++ if (version <= 1) ++ return FDT_V1_SIZE; ++ else if (version <= 2) ++ return FDT_V2_SIZE; ++ else if (version <= 3) ++ return FDT_V3_SIZE; ++ else if (version <= 16) ++ return FDT_V16_SIZE; ++ else ++ return FDT_V17_SIZE; ++} ++ ++size_t fdt_header_size(const void *fdt) ++{ ++ return can_assume(LATEST) ? FDT_V17_SIZE : ++ fdt_header_size_(fdt_version(fdt)); ++} ++ ++int fdt_check_header(const void *fdt) ++{ ++ size_t hdrsize; ++ ++ /* The device tree must be at an 8-byte aligned address */ ++ if ((uintptr_t)fdt & 7) ++ return -FDT_ERR_ALIGNMENT; ++ ++ if (fdt_magic(fdt) != FDT_MAGIC) ++ return -FDT_ERR_BADMAGIC; ++ if (!can_assume(LATEST)) { ++ if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) ++ || (fdt_last_comp_version(fdt) > ++ FDT_LAST_SUPPORTED_VERSION)) ++ return -FDT_ERR_BADVERSION; ++ if (fdt_version(fdt) < fdt_last_comp_version(fdt)) ++ return -FDT_ERR_BADVERSION; ++ } ++ hdrsize = fdt_header_size(fdt); ++ if (!can_assume(VALID_DTB)) { ++ ++ if ((fdt_totalsize(fdt) < hdrsize) ++ || (fdt_totalsize(fdt) > INT_MAX)) ++ return -FDT_ERR_TRUNCATED; ++ ++ /* Bounds check memrsv block */ ++ if (!check_off_(hdrsize, fdt_totalsize(fdt), ++ fdt_off_mem_rsvmap(fdt))) ++ return -FDT_ERR_TRUNCATED; ++ } ++ ++ if (!can_assume(VALID_DTB)) { ++ /* Bounds check structure block */ ++ if (!can_assume(LATEST) && fdt_version(fdt) < 17) { ++ if (!check_off_(hdrsize, fdt_totalsize(fdt), ++ fdt_off_dt_struct(fdt))) ++ return -FDT_ERR_TRUNCATED; ++ } else { ++ if (!check_block_(hdrsize, fdt_totalsize(fdt), ++ fdt_off_dt_struct(fdt), ++ fdt_size_dt_struct(fdt))) ++ return -FDT_ERR_TRUNCATED; ++ } ++ ++ /* Bounds check strings block */ ++ if (!check_block_(hdrsize, fdt_totalsize(fdt), ++ fdt_off_dt_strings(fdt), ++ fdt_size_dt_strings(fdt))) ++ return -FDT_ERR_TRUNCATED; ++ } ++ ++ return 0; ++} ++ ++const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) ++{ ++ unsigned int uoffset = offset; ++ unsigned int absoffset = offset + fdt_off_dt_struct(fdt); ++ ++ if (offset < 0) ++ return NULL; ++ ++ if (!can_assume(VALID_INPUT)) ++ if ((absoffset < uoffset) ++ || ((absoffset + len) < absoffset) ++ || (absoffset + len) > fdt_totalsize(fdt)) ++ return NULL; ++ ++ if (can_assume(LATEST) || fdt_version(fdt) >= 0x11) ++ if (((uoffset + len) < uoffset) ++ || ((offset + len) > fdt_size_dt_struct(fdt))) ++ return NULL; ++ ++ return fdt_offset_ptr_(fdt, offset); ++} ++ ++uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) ++{ ++ const fdt32_t *tagp, *lenp; ++ uint32_t tag; ++ int offset = startoffset; ++ const char *p; ++ ++ *nextoffset = -FDT_ERR_TRUNCATED; ++ tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); ++ if (!can_assume(VALID_DTB) && !tagp) ++ return FDT_END; /* premature end */ ++ tag = fdt32_to_cpu(*tagp); ++ offset += FDT_TAGSIZE; ++ ++ *nextoffset = -FDT_ERR_BADSTRUCTURE; ++ switch (tag) { ++ case FDT_BEGIN_NODE: ++ /* skip name */ ++ do { ++ p = fdt_offset_ptr(fdt, offset++, 1); ++ } while (p && (*p != '\0')); ++ if (!can_assume(VALID_DTB) && !p) ++ return FDT_END; /* premature end */ ++ break; ++ ++ case FDT_PROP: ++ lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); ++ if (!can_assume(VALID_DTB) && !lenp) ++ return FDT_END; /* premature end */ ++ /* skip-name offset, length and value */ ++ offset += sizeof(struct fdt_property) - FDT_TAGSIZE ++ + fdt32_to_cpu(*lenp); ++ if (!can_assume(LATEST) && ++ fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 && ++ ((offset - fdt32_to_cpu(*lenp)) % 8) != 0) ++ offset += 4; ++ break; ++ ++ case FDT_END: ++ case FDT_END_NODE: ++ case FDT_NOP: ++ break; ++ ++ default: ++ return FDT_END; ++ } ++ ++ if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) ++ return FDT_END; /* premature end */ ++ ++ *nextoffset = FDT_TAGALIGN(offset); ++ return tag; ++} ++ ++int fdt_check_node_offset_(const void *fdt, int offset) ++{ ++ if (!can_assume(VALID_INPUT) ++ && ((offset < 0) || (offset % FDT_TAGSIZE))) ++ return -FDT_ERR_BADOFFSET; ++ ++ if (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE) ++ return -FDT_ERR_BADOFFSET; ++ ++ return offset; ++} ++ ++int fdt_check_prop_offset_(const void *fdt, int offset) ++{ ++ if (!can_assume(VALID_INPUT) ++ && ((offset < 0) || (offset % FDT_TAGSIZE))) ++ return -FDT_ERR_BADOFFSET; ++ ++ if (fdt_next_tag(fdt, offset, &offset) != FDT_PROP) ++ return -FDT_ERR_BADOFFSET; ++ ++ return offset; ++} ++ ++int fdt_next_node(const void *fdt, int offset, int *depth) ++{ ++ int nextoffset = 0; ++ uint32_t tag; ++ ++ if (offset >= 0) ++ if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0) ++ return nextoffset; ++ ++ do { ++ offset = nextoffset; ++ tag = fdt_next_tag(fdt, offset, &nextoffset); ++ ++ switch (tag) { ++ case FDT_PROP: ++ case FDT_NOP: ++ break; ++ ++ case FDT_BEGIN_NODE: ++ if (depth) ++ (*depth)++; ++ break; ++ ++ case FDT_END_NODE: ++ if (depth && ((--(*depth)) < 0)) ++ return nextoffset; ++ break; ++ ++ case FDT_END: ++ if ((nextoffset >= 0) ++ || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) ++ return -FDT_ERR_NOTFOUND; ++ else ++ return nextoffset; ++ } ++ } while (tag != FDT_BEGIN_NODE); ++ ++ return offset; ++} ++ ++int fdt_first_subnode(const void *fdt, int offset) ++{ ++ int depth = 0; ++ ++ offset = fdt_next_node(fdt, offset, &depth); ++ if (offset < 0 || depth != 1) ++ return -FDT_ERR_NOTFOUND; ++ ++ return offset; ++} ++ ++int fdt_next_subnode(const void *fdt, int offset) ++{ ++ int depth = 1; ++ ++ /* ++ * With respect to the parent, the depth of the next subnode will be ++ * the same as the last. ++ */ ++ do { ++ offset = fdt_next_node(fdt, offset, &depth); ++ if (offset < 0 || depth < 1) ++ return -FDT_ERR_NOTFOUND; ++ } while (depth > 1); ++ ++ return offset; ++} ++ ++const char *fdt_find_string_(const char *strtab, int tabsize, const char *s) ++{ ++ int len = strlen(s) + 1; ++ const char *last = strtab + tabsize - len; ++ const char *p; ++ ++ for (p = strtab; p <= last; p++) ++ if (memcmp(p, s, len) == 0) ++ return p; ++ return NULL; ++} ++ ++int fdt_move(const void *fdt, void *buf, int bufsize) ++{ ++ if (!can_assume(VALID_INPUT) && bufsize < 0) ++ return -FDT_ERR_NOSPACE; ++ ++ FDT_RO_PROBE(fdt); ++ ++ if (fdt_totalsize(fdt) > (unsigned int)bufsize) ++ return -FDT_ERR_NOSPACE; ++ ++ memmove(buf, fdt, fdt_totalsize(fdt)); ++ return 0; ++} +diff --git a/common/libfdt/fdt_addresses.c b/common/libfdt/fdt_addresses.c +new file mode 100644 +index 0000000..9a82cd0 +--- /dev/null ++++ b/common/libfdt/fdt_addresses.c +@@ -0,0 +1,101 @@ ++// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) ++/* ++ * libfdt - Flat Device Tree manipulation ++ * Copyright (C) 2014 David Gibson <david@gibson.dropbear.id.au> ++ * Copyright (C) 2018 embedded brains GmbH ++ */ ++#include "libfdt_env.h" ++ ++#include <fdt.h> ++#include <libfdt.h> ++ ++#include "libfdt_internal.h" ++ ++static int fdt_cells(const void *fdt, int nodeoffset, const char *name) ++{ ++ const fdt32_t *c; ++ uint32_t val; ++ int len; ++ ++ c = fdt_getprop(fdt, nodeoffset, name, &len); ++ if (!c) ++ return len; ++ ++ if (len != sizeof(*c)) ++ return -FDT_ERR_BADNCELLS; ++ ++ val = fdt32_to_cpu(*c); ++ if (val > FDT_MAX_NCELLS) ++ return -FDT_ERR_BADNCELLS; ++ ++ return (int)val; ++} ++ ++int fdt_address_cells(const void *fdt, int nodeoffset) ++{ ++ int val; ++ ++ val = fdt_cells(fdt, nodeoffset, "#address-cells"); ++ if (val == 0) ++ return -FDT_ERR_BADNCELLS; ++ if (val == -FDT_ERR_NOTFOUND) ++ return 2; ++ return val; ++} ++ ++int fdt_size_cells(const void *fdt, int nodeoffset) ++{ ++ int val; ++ ++ val = fdt_cells(fdt, nodeoffset, "#size-cells"); ++ if (val == -FDT_ERR_NOTFOUND) ++ return 1; ++ return val; ++} ++ ++/* This function assumes that [address|size]_cells is 1 or 2 */ ++int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset, ++ const char *name, uint64_t addr, uint64_t size) ++{ ++ int addr_cells, size_cells, ret; ++ uint8_t data[sizeof(fdt64_t) * 2], *prop; ++ ++ ret = fdt_address_cells(fdt, parent); ++ if (ret < 0) ++ return ret; ++ addr_cells = ret; ++ ++ ret = fdt_size_cells(fdt, parent); ++ if (ret < 0) ++ return ret; ++ size_cells = ret; ++ ++ /* check validity of address */ ++ prop = data; ++ if (addr_cells == 1) { ++ if ((addr > UINT32_MAX) || ((UINT32_MAX + 1 - addr) < size)) ++ return -FDT_ERR_BADVALUE; ++ ++ fdt32_st(prop, (uint32_t)addr); ++ } else if (addr_cells == 2) { ++ fdt64_st(prop, addr); ++ } else { ++ return -FDT_ERR_BADNCELLS; ++ } ++ ++ /* check validity of size */ ++ prop += addr_cells * sizeof(fdt32_t); ++ if (size_cells == 1) { ++ if (size > UINT32_MAX) ++ return -FDT_ERR_BADVALUE; ++ ++ fdt32_st(prop, (uint32_t)size); ++ } else if (size_cells == 2) { ++ fdt64_st(prop, size); ++ } else { ++ return -FDT_ERR_BADNCELLS; ++ } ++ ++ return fdt_appendprop(fdt, nodeoffset, name, data, ++ (addr_cells + size_cells) * sizeof(fdt32_t)); ++} +diff --git a/common/libfdt/fdt_check.c b/common/libfdt/fdt_check.c +new file mode 100644 +index 0000000..fa410a8 +--- /dev/null ++++ b/common/libfdt/fdt_check.c +@@ -0,0 +1,93 @@ ++// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) ++/* ++ * libfdt - Flat Device Tree manipulation ++ * Copyright (C) 2006 David Gibson, IBM Corporation. ++ */ ++#include "libfdt_env.h" ++ ++#include <fdt.h> ++#include <libfdt.h> ++ ++#include "libfdt_internal.h" ++ ++int fdt_check_full(const void *fdt, size_t bufsize) ++{ ++ int err; ++ int num_memrsv; ++ int offset, nextoffset = 0; ++ uint32_t tag; ++ unsigned int depth = 0; ++ const void *prop; ++ const char *propname; ++ bool expect_end = false; ++ ++ if (bufsize < FDT_V1_SIZE) ++ return -FDT_ERR_TRUNCATED; ++ if (bufsize < fdt_header_size(fdt)) ++ return -FDT_ERR_TRUNCATED; ++ err = fdt_check_header(fdt); ++ if (err != 0) ++ return err; ++ if (bufsize < fdt_totalsize(fdt)) ++ return -FDT_ERR_TRUNCATED; ++ ++ num_memrsv = fdt_num_mem_rsv(fdt); ++ if (num_memrsv < 0) ++ return num_memrsv; ++ ++ while (1) { ++ offset = nextoffset; ++ tag = fdt_next_tag(fdt, offset, &nextoffset); ++ ++ if (nextoffset < 0) ++ return nextoffset; ++ ++ /* If we see two root nodes, something is wrong */ ++ if (expect_end && tag != FDT_END) ++ return -FDT_ERR_BADSTRUCTURE; ++ ++ switch (tag) { ++ case FDT_NOP: ++ break; ++ ++ case FDT_END: ++ if (depth != 0) ++ return -FDT_ERR_BADSTRUCTURE; ++ return 0; ++ ++ case FDT_BEGIN_NODE: ++ depth++; ++ if (depth > INT_MAX) ++ return -FDT_ERR_BADSTRUCTURE; ++ ++ /* The root node must have an empty name */ ++ if (depth == 1) { ++ const char *name; ++ int len; ++ ++ name = fdt_get_name(fdt, offset, &len); ++ if (*name || len) ++ return -FDT_ERR_BADSTRUCTURE; ++ } ++ break; ++ ++ case FDT_END_NODE: ++ if (depth == 0) ++ return -FDT_ERR_BADSTRUCTURE; ++ depth--; ++ if (depth == 0) ++ expect_end = true; ++ break; ++ ++ case FDT_PROP: ++ prop = fdt_getprop_by_offset(fdt, offset, &propname, ++ &err); ++ if (!prop) ++ return err; ++ break; ++ ++ default: ++ return -FDT_ERR_INTERNAL; ++ } ++ } ++} +diff --git a/common/libfdt/fdt_empty_tree.c b/common/libfdt/fdt_empty_tree.c +new file mode 100644 +index 0000000..49d54d4 +--- /dev/null ++++ b/common/libfdt/fdt_empty_tree.c +@@ -0,0 +1,38 @@ ++// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) ++/* ++ * libfdt - Flat Device Tree manipulation ++ * Copyright (C) 2012 David Gibson, IBM Corporation. ++ */ ++#include "libfdt_env.h" ++ ++#include <fdt.h> ++#include <libfdt.h> ++ ++#include "libfdt_internal.h" ++ ++int fdt_create_empty_tree(void *buf, int bufsize) ++{ ++ int err; ++ ++ err = fdt_create(buf, bufsize); ++ if (err) ++ return err; ++ ++ err = fdt_finish_reservemap(buf); ++ if (err) ++ return err; ++ ++ err = fdt_begin_node(buf, ""); ++ if (err) ++ return err; ++ ++ err = fdt_end_node(buf); ++ if (err) ++ return err; ++ ++ err = fdt_finish(buf); ++ if (err) ++ return err; ++ ++ return fdt_open_into(buf, buf, bufsize); ++} +diff --git a/common/libfdt/fdt_overlay.c b/common/libfdt/fdt_overlay.c +new file mode 100644 +index 0000000..d217e79 +--- /dev/null ++++ b/common/libfdt/fdt_overlay.c +@@ -0,0 +1,882 @@ ++// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) ++/* ++ * libfdt - Flat Device Tree manipulation ++ * Copyright (C) 2016 Free Electrons ++ * Copyright (C) 2016 NextThing Co. ++ */ ++#include "libfdt_env.h" ++ ++#include <fdt.h> ++#include <libfdt.h> ++ ++#include "libfdt_internal.h" ++ ++/** ++ * overlay_get_target_phandle - retrieves the target phandle of a fragment ++ * @fdto: pointer to the device tree overlay blob ++ * @fragment: node offset of the fragment in the overlay ++ * ++ * overlay_get_target_phandle() retrieves the target phandle of an ++ * overlay fragment when that fragment uses a phandle (target ++ * property) instead of a path (target-path property). ++ * ++ * returns: ++ * the phandle pointed by the target property ++ * 0, if the phandle was not found ++ * -1, if the phandle was malformed ++ */ ++static uint32_t overlay_get_target_phandle(const void *fdto, int fragment) ++{ ++ const fdt32_t *val; ++ int len; ++ ++ val = fdt_getprop(fdto, fragment, "target", &len); ++ if (!val) ++ return 0; ++ ++ if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1)) ++ return (uint32_t)-1; ++ ++ return fdt32_to_cpu(*val); ++} ++ ++/** ++ * overlay_get_target - retrieves the offset of a fragment's target ++ * @fdt: Base device tree blob ++ * @fdto: Device tree overlay blob ++ * @fragment: node offset of the fragment in the overlay ++ * @pathp: pointer which receives the path of the target (or NULL) ++ * ++ * overlay_get_target() retrieves the target offset in the base ++ * device tree of a fragment, no matter how the actual targeting is ++ * done (through a phandle or a path) ++ * ++ * returns: ++ * the targeted node offset in the base device tree ++ * Negative error code on error ++ */ ++static int overlay_get_target(const void *fdt, const void *fdto, ++ int fragment, char const **pathp) ++{ ++ uint32_t phandle; ++ const char *path = NULL; ++ int path_len = 0, ret; ++ ++ /* Try first to do a phandle based lookup */ ++ phandle = overlay_get_target_phandle(fdto, fragment); ++ if (phandle == (uint32_t)-1) ++ return -FDT_ERR_BADPHANDLE; ++ ++ /* no phandle, try path */ ++ if (!phandle) { ++ /* And then a path based lookup */ ++ path = fdt_getprop(fdto, fragment, "target-path", &path_len); ++ if (path) ++ ret = fdt_path_offset(fdt, path); ++ else ++ ret = path_len; ++ } else ++ ret = fdt_node_offset_by_phandle(fdt, phandle); ++ ++ /* ++ * If we haven't found either a target or a ++ * target-path property in a node that contains a ++ * __overlay__ subnode (we wouldn't be called ++ * otherwise), consider it a improperly written ++ * overlay ++ */ ++ if (ret < 0 && path_len == -FDT_ERR_NOTFOUND) ++ ret = -FDT_ERR_BADOVERLAY; ++ ++ /* return on error */ ++ if (ret < 0) ++ return ret; ++ ++ /* return pointer to path (if available) */ ++ if (pathp) ++ *pathp = path ? path : NULL; ++ ++ return ret; ++} ++ ++/** ++ * overlay_phandle_add_offset - Increases a phandle by an offset ++ * @fdt: Base device tree blob ++ * @node: Device tree overlay blob ++ * @name: Name of the property to modify (phandle or linux,phandle) ++ * @delta: offset to apply ++ * ++ * overlay_phandle_add_offset() increments a node phandle by a given ++ * offset. ++ * ++ * returns: ++ * 0 on success. ++ * Negative error code on error ++ */ ++static int overlay_phandle_add_offset(void *fdt, int node, ++ const char *name, uint32_t delta) ++{ ++ const fdt32_t *val; ++ uint32_t adj_val; ++ int len; ++ ++ val = fdt_getprop(fdt, node, name, &len); ++ if (!val) ++ return len; ++ ++ if (len != sizeof(*val)) ++ return -FDT_ERR_BADPHANDLE; ++ ++ adj_val = fdt32_to_cpu(*val); ++ if ((adj_val + delta) < adj_val) ++ return -FDT_ERR_NOPHANDLES; ++ ++ adj_val += delta; ++ if (adj_val == (uint32_t)-1) ++ return -FDT_ERR_NOPHANDLES; ++ ++ return fdt_setprop_inplace_u32(fdt, node, name, adj_val); ++} ++ ++/** ++ * overlay_adjust_node_phandles - Offsets the phandles of a node ++ * @fdto: Device tree overlay blob ++ * @node: Offset of the node we want to adjust ++ * @delta: Offset to shift the phandles of ++ * ++ * overlay_adjust_node_phandles() adds a constant to all the phandles ++ * of a given node. This is mainly use as part of the overlay ++ * application process, when we want to update all the overlay ++ * phandles to not conflict with the overlays of the base device tree. ++ * ++ * returns: ++ * 0 on success ++ * Negative error code on failure ++ */ ++static int overlay_adjust_node_phandles(void *fdto, int node, ++ uint32_t delta) ++{ ++ int child; ++ int ret; ++ ++ ret = overlay_phandle_add_offset(fdto, node, "phandle", delta); ++ if (ret && ret != -FDT_ERR_NOTFOUND) ++ return ret; ++ ++ ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta); ++ if (ret && ret != -FDT_ERR_NOTFOUND) ++ return ret; ++ ++ fdt_for_each_subnode(child, fdto, node) { ++ ret = overlay_adjust_node_phandles(fdto, child, delta); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/** ++ * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay ++ * @fdto: Device tree overlay blob ++ * @delta: Offset to shift the phandles of ++ * ++ * overlay_adjust_local_phandles() adds a constant to all the ++ * phandles of an overlay. This is mainly use as part of the overlay ++ * application process, when we want to update all the overlay ++ * phandles to not conflict with the overlays of the base device tree. ++ * ++ * returns: ++ * 0 on success ++ * Negative error code on failure ++ */ ++static int overlay_adjust_local_phandles(void *fdto, uint32_t delta) ++{ ++ /* ++ * Start adjusting the phandles from the overlay root ++ */ ++ return overlay_adjust_node_phandles(fdto, 0, delta); ++} ++ ++/** ++ * overlay_update_local_node_references - Adjust the overlay references ++ * @fdto: Device tree overlay blob ++ * @tree_node: Node offset of the node to operate on ++ * @fixup_node: Node offset of the matching local fixups node ++ * @delta: Offset to shift the phandles of ++ * ++ * overlay_update_local_nodes_references() update the phandles ++ * pointing to a node within the device tree overlay by adding a ++ * constant delta. ++ * ++ * This is mainly used as part of a device tree application process, ++ * where you want the device tree overlays phandles to not conflict ++ * with the ones from the base device tree before merging them. ++ * ++ * returns: ++ * 0 on success ++ * Negative error code on failure ++ */ ++static int overlay_update_local_node_references(void *fdto, ++ int tree_node, ++ int fixup_node, ++ uint32_t delta) ++{ ++ int fixup_prop; ++ int fixup_child; ++ int ret; ++ ++ fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) { ++ const fdt32_t *fixup_val; ++ const char *tree_val; ++ const char *name; ++ int fixup_len; ++ int tree_len; ++ int i; ++ ++ fixup_val = fdt_getprop_by_offset(fdto, fixup_prop, ++ &name, &fixup_len); ++ if (!fixup_val) ++ return fixup_len; ++ ++ if (fixup_len % sizeof(uint32_t)) ++ return -FDT_ERR_BADOVERLAY; ++ fixup_len /= sizeof(uint32_t); ++ ++ tree_val = fdt_getprop(fdto, tree_node, name, &tree_len); ++ if (!tree_val) { ++ if (tree_len == -FDT_ERR_NOTFOUND) ++ return -FDT_ERR_BADOVERLAY; ++ ++ return tree_len; ++ } ++ ++ for (i = 0; i < fixup_len; i++) { ++ fdt32_t adj_val; ++ uint32_t poffset; ++ ++ poffset = fdt32_to_cpu(fixup_val[i]); ++ ++ /* ++ * phandles to fixup can be unaligned. ++ * ++ * Use a memcpy for the architectures that do ++ * not support unaligned accesses. ++ */ ++ memcpy(&adj_val, tree_val + poffset, sizeof(adj_val)); ++ ++ adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta); ++ ++ ret = fdt_setprop_inplace_namelen_partial(fdto, ++ tree_node, ++ name, ++ strlen(name), ++ poffset, ++ &adj_val, ++ sizeof(adj_val)); ++ if (ret == -FDT_ERR_NOSPACE) ++ return -FDT_ERR_BADOVERLAY; ++ ++ if (ret) ++ return ret; ++ } ++ } ++ ++ fdt_for_each_subnode(fixup_child, fdto, fixup_node) { ++ const char *fixup_child_name = fdt_get_name(fdto, fixup_child, ++ NULL); ++ int tree_child; ++ ++ tree_child = fdt_subnode_offset(fdto, tree_node, ++ fixup_child_name); ++ if (tree_child == -FDT_ERR_NOTFOUND) ++ return -FDT_ERR_BADOVERLAY; ++ if (tree_child < 0) ++ return tree_child; ++ ++ ret = overlay_update_local_node_references(fdto, ++ tree_child, ++ fixup_child, ++ delta); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/** ++ * overlay_update_local_references - Adjust the overlay references ++ * @fdto: Device tree overlay blob ++ * @delta: Offset to shift the phandles of ++ * ++ * overlay_update_local_references() update all the phandles pointing ++ * to a node within the device tree overlay by adding a constant ++ * delta to not conflict with the base overlay. ++ * ++ * This is mainly used as part of a device tree application process, ++ * where you want the device tree overlays phandles to not conflict ++ * with the ones from the base device tree before merging them. ++ * ++ * returns: ++ * 0 on success ++ * Negative error code on failure ++ */ ++static int overlay_update_local_references(void *fdto, uint32_t delta) ++{ ++ int fixups; ++ ++ fixups = fdt_path_offset(fdto, "/__local_fixups__"); ++ if (fixups < 0) { ++ /* There's no local phandles to adjust, bail out */ ++ if (fixups == -FDT_ERR_NOTFOUND) ++ return 0; ++ ++ return fixups; ++ } ++ ++ /* ++ * Update our local references from the root of the tree ++ */ ++ return overlay_update_local_node_references(fdto, 0, fixups, ++ delta); ++} ++ ++/** ++ * overlay_fixup_one_phandle - Set an overlay phandle to the base one ++ * @fdt: Base Device Tree blob ++ * @fdto: Device tree overlay blob ++ * @symbols_off: Node offset of the symbols node in the base device tree ++ * @path: Path to a node holding a phandle in the overlay ++ * @path_len: number of path characters to consider ++ * @name: Name of the property holding the phandle reference in the overlay ++ * @name_len: number of name characters to consider ++ * @poffset: Offset within the overlay property where the phandle is stored ++ * @label: Label of the node referenced by the phandle ++ * ++ * overlay_fixup_one_phandle() resolves an overlay phandle pointing to ++ * a node in the base device tree. ++ * ++ * This is part of the device tree overlay application process, when ++ * you want all the phandles in the overlay to point to the actual ++ * base dt nodes. ++ * ++ * returns: ++ * 0 on success ++ * Negative error code on failure ++ */ ++static int overlay_fixup_one_phandle(void *fdt, void *fdto, ++ int symbols_off, ++ const char *path, uint32_t path_len, ++ const char *name, uint32_t name_len, ++ int poffset, const char *label) ++{ ++ const char *symbol_path; ++ uint32_t phandle; ++ fdt32_t phandle_prop; ++ int symbol_off, fixup_off; ++ int prop_len; ++ ++ if (symbols_off < 0) ++ return symbols_off; ++ ++ symbol_path = fdt_getprop(fdt, symbols_off, label, ++ &prop_len); ++ if (!symbol_path) ++ return prop_len; ++ ++ symbol_off = fdt_path_offset(fdt, symbol_path); ++ if (symbol_off < 0) ++ return symbol_off; ++ ++ phandle = fdt_get_phandle(fdt, symbol_off); ++ if (!phandle) ++ return -FDT_ERR_NOTFOUND; ++ ++ fixup_off = fdt_path_offset_namelen(fdto, path, path_len); ++ if (fixup_off == -FDT_ERR_NOTFOUND) ++ return -FDT_ERR_BADOVERLAY; ++ if (fixup_off < 0) ++ return fixup_off; ++ ++ phandle_prop = cpu_to_fdt32(phandle); ++ return fdt_setprop_inplace_namelen_partial(fdto, fixup_off, ++ name, name_len, poffset, ++ &phandle_prop, ++ sizeof(phandle_prop)); ++}; ++ ++/** ++ * overlay_fixup_phandle - Set an overlay phandle to the base one ++ * @fdt: Base Device Tree blob ++ * @fdto: Device tree overlay blob ++ * @symbols_off: Node offset of the symbols node in the base device tree ++ * @property: Property offset in the overlay holding the list of fixups ++ * ++ * overlay_fixup_phandle() resolves all the overlay phandles pointed ++ * to in a __fixups__ property, and updates them to match the phandles ++ * in use in the base device tree. ++ * ++ * This is part of the device tree overlay application process, when ++ * you want all the phandles in the overlay to point to the actual ++ * base dt nodes. ++ * ++ * returns: ++ * 0 on success ++ * Negative error code on failure ++ */ ++static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, ++ int property) ++{ ++ const char *value; ++ const char *label; ++ int len; ++ ++ value = fdt_getprop_by_offset(fdto, property, ++ &label, &len); ++ if (!value) { ++ if (len == -FDT_ERR_NOTFOUND) ++ return -FDT_ERR_INTERNAL; ++ ++ return len; ++ } ++ ++ do { ++ const char *path, *name, *fixup_end; ++ const char *fixup_str = value; ++ uint32_t path_len, name_len; ++ uint32_t fixup_len; ++ char *sep, *endptr; ++ int poffset, ret; ++ ++ fixup_end = memchr(value, '\0', len); ++ if (!fixup_end) ++ return -FDT_ERR_BADOVERLAY; ++ fixup_len = fixup_end - fixup_str; ++ ++ len -= fixup_len + 1; ++ value += fixup_len + 1; ++ ++ path = fixup_str; ++ sep = memchr(fixup_str, ':', fixup_len); ++ if (!sep || *sep != ':') ++ return -FDT_ERR_BADOVERLAY; ++ ++ path_len = sep - path; ++ if (path_len == (fixup_len - 1)) ++ return -FDT_ERR_BADOVERLAY; ++ ++ fixup_len -= path_len + 1; ++ name = sep + 1; ++ sep = memchr(name, ':', fixup_len); ++ if (!sep || *sep != ':') ++ return -FDT_ERR_BADOVERLAY; ++ ++ name_len = sep - name; ++ if (!name_len) ++ return -FDT_ERR_BADOVERLAY; ++ ++ poffset = strtoul(sep + 1, &endptr, 10); ++ if ((*endptr != '\0') || (endptr <= (sep + 1))) ++ return -FDT_ERR_BADOVERLAY; ++ ++ ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off, ++ path, path_len, name, name_len, ++ poffset, label); ++ if (ret) ++ return ret; ++ } while (len > 0); ++ ++ return 0; ++} ++ ++/** ++ * overlay_fixup_phandles - Resolve the overlay phandles to the base ++ * device tree ++ * @fdt: Base Device Tree blob ++ * @fdto: Device tree overlay blob ++ * ++ * overlay_fixup_phandles() resolves all the overlay phandles pointing ++ * to nodes in the base device tree. ++ * ++ * This is one of the steps of the device tree overlay application ++ * process, when you want all the phandles in the overlay to point to ++ * the actual base dt nodes. ++ * ++ * returns: ++ * 0 on success ++ * Negative error code on failure ++ */ ++static int overlay_fixup_phandles(void *fdt, void *fdto) ++{ ++ int fixups_off, symbols_off; ++ int property; ++ ++ /* We can have overlays without any fixups */ ++ fixups_off = fdt_path_offset(fdto, "/__fixups__"); ++ if (fixups_off == -FDT_ERR_NOTFOUND) ++ return 0; /* nothing to do */ ++ if (fixups_off < 0) ++ return fixups_off; ++ ++ /* And base DTs without symbols */ ++ symbols_off = fdt_path_offset(fdt, "/__symbols__"); ++ if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND))) ++ return symbols_off; ++ ++ fdt_for_each_property_offset(property, fdto, fixups_off) { ++ int ret; ++ ++ ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/** ++ * overlay_apply_node - Merges a node into the base device tree ++ * @fdt: Base Device Tree blob ++ * @target: Node offset in the base device tree to apply the fragment to ++ * @fdto: Device tree overlay blob ++ * @node: Node offset in the overlay holding the changes to merge ++ * ++ * overlay_apply_node() merges a node into a target base device tree ++ * node pointed. ++ * ++ * This is part of the final step in the device tree overlay ++ * application process, when all the phandles have been adjusted and ++ * resolved and you just have to merge overlay into the base device ++ * tree. ++ * ++ * returns: ++ * 0 on success ++ * Negative error code on failure ++ */ ++static int overlay_apply_node(void *fdt, int target, ++ void *fdto, int node) ++{ ++ int property; ++ int subnode; ++ ++ fdt_for_each_property_offset(property, fdto, node) { ++ const char *name; ++ const void *prop; ++ int prop_len; ++ int ret; ++ ++ prop = fdt_getprop_by_offset(fdto, property, &name, ++ &prop_len); ++ if (prop_len == -FDT_ERR_NOTFOUND) ++ return -FDT_ERR_INTERNAL; ++ if (prop_len < 0) ++ return prop_len; ++ ++ ret = fdt_setprop(fdt, target, name, prop, prop_len); ++ if (ret) ++ return ret; ++ } ++ ++ fdt_for_each_subnode(subnode, fdto, node) { ++ const char *name = fdt_get_name(fdto, subnode, NULL); ++ int nnode; ++ int ret; ++ ++ nnode = fdt_add_subnode(fdt, target, name); ++ if (nnode == -FDT_ERR_EXISTS) { ++ nnode = fdt_subnode_offset(fdt, target, name); ++ if (nnode == -FDT_ERR_NOTFOUND) ++ return -FDT_ERR_INTERNAL; ++ } ++ ++ if (nnode < 0) ++ return nnode; ++ ++ ret = overlay_apply_node(fdt, nnode, fdto, subnode); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/** ++ * overlay_merge - Merge an overlay into its base device tree ++ * @fdt: Base Device Tree blob ++ * @fdto: Device tree overlay blob ++ * ++ * overlay_merge() merges an overlay into its base device tree. ++ * ++ * This is the next to last step in the device tree overlay application ++ * process, when all the phandles have been adjusted and resolved and ++ * you just have to merge overlay into the base device tree. ++ * ++ * returns: ++ * 0 on success ++ * Negative error code on failure ++ */ ++static int overlay_merge(void *fdt, void *fdto) ++{ ++ int fragment; ++ ++ fdt_for_each_subnode(fragment, fdto, 0) { ++ int overlay; ++ int target; ++ int ret; ++ ++ /* ++ * Each fragments will have an __overlay__ node. If ++ * they don't, it's not supposed to be merged ++ */ ++ overlay = fdt_subnode_offset(fdto, fragment, "__overlay__"); ++ if (overlay == -FDT_ERR_NOTFOUND) ++ continue; ++ ++ if (overlay < 0) ++ return overlay; ++ ++ target = overlay_get_target(fdt, fdto, fragment, NULL); ++ if (target < 0) ++ return target; ++ ++ ret = overlay_apply_node(fdt, target, fdto, overlay); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int get_path_len(const void *fdt, int nodeoffset) ++{ ++ int len = 0, namelen; ++ const char *name; ++ ++ FDT_RO_PROBE(fdt); ++ ++ for (;;) { ++ name = fdt_get_name(fdt, nodeoffset, &namelen); ++ if (!name) ++ return namelen; ++ ++ /* root? we're done */ ++ if (namelen == 0) ++ break; ++ ++ nodeoffset = fdt_parent_offset(fdt, nodeoffset); ++ if (nodeoffset < 0) ++ return nodeoffset; ++ len += namelen + 1; ++ } ++ ++ /* in case of root pretend it's "/" */ ++ if (len == 0) ++ len++; ++ return len; ++} ++ ++/** ++ * overlay_symbol_update - Update the symbols of base tree after a merge ++ * @fdt: Base Device Tree blob ++ * @fdto: Device tree overlay blob ++ * ++ * overlay_symbol_update() updates the symbols of the base tree with the ++ * symbols of the applied overlay ++ * ++ * This is the last step in the device tree overlay application ++ * process, allowing the reference of overlay symbols by subsequent ++ * overlay operations. ++ * ++ * returns: ++ * 0 on success ++ * Negative error code on failure ++ */ ++static int overlay_symbol_update(void *fdt, void *fdto) ++{ ++ int root_sym, ov_sym, prop, path_len, fragment, target; ++ int len, frag_name_len, ret, rel_path_len; ++ const char *s, *e; ++ const char *path; ++ const char *name; ++ const char *frag_name; ++ const char *rel_path; ++ const char *target_path; ++ char *buf; ++ void *p; ++ ++ ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__"); ++ ++ /* if no overlay symbols exist no problem */ ++ if (ov_sym < 0) ++ return 0; ++ ++ root_sym = fdt_subnode_offset(fdt, 0, "__symbols__"); ++ ++ /* it no root symbols exist we should create them */ ++ if (root_sym == -FDT_ERR_NOTFOUND) ++ root_sym = fdt_add_subnode(fdt, 0, "__symbols__"); ++ ++ /* any error is fatal now */ ++ if (root_sym < 0) ++ return root_sym; ++ ++ /* iterate over each overlay symbol */ ++ fdt_for_each_property_offset(prop, fdto, ov_sym) { ++ path = fdt_getprop_by_offset(fdto, prop, &name, &path_len); ++ if (!path) ++ return path_len; ++ ++ /* verify it's a string property (terminated by a single \0) */ ++ if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1]) ++ return -FDT_ERR_BADVALUE; ++ ++ /* keep end marker to avoid strlen() */ ++ e = path + path_len; ++ ++ if (*path != '/') ++ return -FDT_ERR_BADVALUE; ++ ++ /* get fragment name first */ ++ s = strchr(path + 1, '/'); ++ if (!s) { ++ /* Symbol refers to something that won't end ++ * up in the target tree */ ++ continue; ++ } ++ ++ frag_name = path + 1; ++ frag_name_len = s - path - 1; ++ ++ /* verify format; safe since "s" lies in \0 terminated prop */ ++ len = sizeof("/__overlay__/") - 1; ++ if ((e - s) > len && (memcmp(s, "/__overlay__/", len) == 0)) { ++ /* /<fragment-name>/__overlay__/<relative-subnode-path> */ ++ rel_path = s + len; ++ rel_path_len = e - rel_path - 1; ++ } else if ((e - s) == len ++ && (memcmp(s, "/__overlay__", len - 1) == 0)) { ++ /* /<fragment-name>/__overlay__ */ ++ rel_path = ""; ++ rel_path_len = 0; ++ } else { ++ /* Symbol refers to something that won't end ++ * up in the target tree */ ++ continue; ++ } ++ ++ /* find the fragment index in which the symbol lies */ ++ ret = fdt_subnode_offset_namelen(fdto, 0, frag_name, ++ frag_name_len); ++ /* not found? */ ++ if (ret < 0) ++ return -FDT_ERR_BADOVERLAY; ++ fragment = ret; ++ ++ /* an __overlay__ subnode must exist */ ++ ret = fdt_subnode_offset(fdto, fragment, "__overlay__"); ++ if (ret < 0) ++ return -FDT_ERR_BADOVERLAY; ++ ++ /* get the target of the fragment */ ++ ret = overlay_get_target(fdt, fdto, fragment, &target_path); ++ if (ret < 0) ++ return ret; ++ target = ret; ++ ++ /* if we have a target path use */ ++ if (!target_path) { ++ ret = get_path_len(fdt, target); ++ if (ret < 0) ++ return ret; ++ len = ret; ++ } else { ++ len = strlen(target_path); ++ } ++ ++ ret = fdt_setprop_placeholder(fdt, root_sym, name, ++ len + (len > 1) + rel_path_len + 1, &p); ++ if (ret < 0) ++ return ret; ++ ++ if (!target_path) { ++ /* again in case setprop_placeholder changed it */ ++ ret = overlay_get_target(fdt, fdto, fragment, &target_path); ++ if (ret < 0) ++ return ret; ++ target = ret; ++ } ++ ++ buf = p; ++ if (len > 1) { /* target is not root */ ++ if (!target_path) { ++ ret = fdt_get_path(fdt, target, buf, len + 1); ++ if (ret < 0) ++ return ret; ++ } else ++ memcpy(buf, target_path, len + 1); ++ ++ } else ++ len--; ++ ++ buf[len] = '/'; ++ memcpy(buf + len + 1, rel_path, rel_path_len); ++ buf[len + 1 + rel_path_len] = '\0'; ++ } ++ ++ return 0; ++} ++ ++int fdt_overlay_apply(void *fdt, void *fdto) ++{ ++ uint32_t delta; ++ int ret; ++ ++ FDT_RO_PROBE(fdt); ++ FDT_RO_PROBE(fdto); ++ ++ ret = fdt_find_max_phandle(fdt, &delta); ++ if (ret) ++ goto err; ++ ++ ret = overlay_adjust_local_phandles(fdto, delta); ++ if (ret) ++ goto err; ++ ++ ret = overlay_update_local_references(fdto, delta); ++ if (ret) ++ goto err; ++ ++ ret = overlay_fixup_phandles(fdt, fdto); ++ if (ret) ++ goto err; ++ ++ ret = overlay_merge(fdt, fdto); ++ if (ret) ++ goto err; ++ ++ ret = overlay_symbol_update(fdt, fdto); ++ if (ret) ++ goto err; ++ ++ /* ++ * The overlay has been damaged, erase its magic. ++ */ ++ fdt_set_magic(fdto, ~0); ++ ++ return 0; ++ ++err: ++ /* ++ * The overlay might have been damaged, erase its magic. ++ */ ++ fdt_set_magic(fdto, ~0); ++ ++ /* ++ * The base device tree might have been damaged, erase its ++ * magic. ++ */ ++ fdt_set_magic(fdt, ~0); ++ ++ return ret; ++} +diff --git a/common/libfdt/fdt_ro.c b/common/libfdt/fdt_ro.c +new file mode 100644 +index 0000000..17584da +--- /dev/null ++++ b/common/libfdt/fdt_ro.c +@@ -0,0 +1,859 @@ ++// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) ++/* ++ * libfdt - Flat Device Tree manipulation ++ * Copyright (C) 2006 David Gibson, IBM Corporation. ++ */ ++#include "libfdt_env.h" ++ ++#include <fdt.h> ++#include <libfdt.h> ++ ++#include "libfdt_internal.h" ++ ++static int fdt_nodename_eq_(const void *fdt, int offset, ++ const char *s, int len) ++{ ++ int olen; ++ const char *p = fdt_get_name(fdt, offset, &olen); ++ ++ if (!p || olen < len) ++ /* short match */ ++ return 0; ++ ++ if (memcmp(p, s, len) != 0) ++ return 0; ++ ++ if (p[len] == '\0') ++ return 1; ++ else if (!memchr(s, '@', len) && (p[len] == '@')) ++ return 1; ++ else ++ return 0; ++} ++ ++const char *fdt_get_string(const void *fdt, int stroffset, int *lenp) ++{ ++ int32_t totalsize; ++ uint32_t absoffset; ++ size_t len; ++ int err; ++ const char *s, *n; ++ ++ if (can_assume(VALID_INPUT)) { ++ s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; ++ ++ if (lenp) ++ *lenp = strlen(s); ++ return s; ++ } ++ totalsize = fdt_ro_probe_(fdt); ++ err = totalsize; ++ if (totalsize < 0) ++ goto fail; ++ ++ err = -FDT_ERR_BADOFFSET; ++ absoffset = stroffset + fdt_off_dt_strings(fdt); ++ if (absoffset >= (unsigned)totalsize) ++ goto fail; ++ len = totalsize - absoffset; ++ ++ if (fdt_magic(fdt) == FDT_MAGIC) { ++ if (stroffset < 0) ++ goto fail; ++ if (can_assume(LATEST) || fdt_version(fdt) >= 17) { ++ if ((unsigned)stroffset >= fdt_size_dt_strings(fdt)) ++ goto fail; ++ if ((fdt_size_dt_strings(fdt) - stroffset) < len) ++ len = fdt_size_dt_strings(fdt) - stroffset; ++ } ++ } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { ++ unsigned int sw_stroffset = -stroffset; ++ ++ if ((stroffset >= 0) || ++ (sw_stroffset > fdt_size_dt_strings(fdt))) ++ goto fail; ++ if (sw_stroffset < len) ++ len = sw_stroffset; ++ } else { ++ err = -FDT_ERR_INTERNAL; ++ goto fail; ++ } ++ ++ s = (const char *)fdt + absoffset; ++ n = memchr(s, '\0', len); ++ if (!n) { ++ /* missing terminating NULL */ ++ err = -FDT_ERR_TRUNCATED; ++ goto fail; ++ } ++ ++ if (lenp) ++ *lenp = n - s; ++ return s; ++ ++fail: ++ if (lenp) ++ *lenp = err; ++ return NULL; ++} ++ ++const char *fdt_string(const void *fdt, int stroffset) ++{ ++ return fdt_get_string(fdt, stroffset, NULL); ++} ++ ++static int fdt_string_eq_(const void *fdt, int stroffset, ++ const char *s, int len) ++{ ++ int slen; ++ const char *p = fdt_get_string(fdt, stroffset, &slen); ++ ++ return p && (slen == len) && (memcmp(p, s, len) == 0); ++} ++ ++int fdt_find_max_phandle(const void *fdt, uint32_t *phandle) ++{ ++ uint32_t max = 0; ++ int offset = -1; ++ ++ while (true) { ++ uint32_t value; ++ ++ offset = fdt_next_node(fdt, offset, NULL); ++ if (offset < 0) { ++ if (offset == -FDT_ERR_NOTFOUND) ++ break; ++ ++ return offset; ++ } ++ ++ value = fdt_get_phandle(fdt, offset); ++ ++ if (value > max) ++ max = value; ++ } ++ ++ if (phandle) ++ *phandle = max; ++ ++ return 0; ++} ++ ++int fdt_generate_phandle(const void *fdt, uint32_t *phandle) ++{ ++ uint32_t max; ++ int err; ++ ++ err = fdt_find_max_phandle(fdt, &max); ++ if (err < 0) ++ return err; ++ ++ if (max == FDT_MAX_PHANDLE) ++ return -FDT_ERR_NOPHANDLES; ++ ++ if (phandle) ++ *phandle = max + 1; ++ ++ return 0; ++} ++ ++static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n) ++{ ++ unsigned int offset = n * sizeof(struct fdt_reserve_entry); ++ unsigned int absoffset = fdt_off_mem_rsvmap(fdt) + offset; ++ ++ if (!can_assume(VALID_INPUT)) { ++ if (absoffset < fdt_off_mem_rsvmap(fdt)) ++ return NULL; ++ if (absoffset > fdt_totalsize(fdt) - ++ sizeof(struct fdt_reserve_entry)) ++ return NULL; ++ } ++ return fdt_mem_rsv_(fdt, n); ++} ++ ++int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) ++{ ++ const struct fdt_reserve_entry *re; ++ ++ FDT_RO_PROBE(fdt); ++ re = fdt_mem_rsv(fdt, n); ++ if (!can_assume(VALID_INPUT) && !re) ++ return -FDT_ERR_BADOFFSET; ++ ++ *address = fdt64_ld_(&re->address); ++ *size = fdt64_ld_(&re->size); ++ return 0; ++} ++ ++int fdt_num_mem_rsv(const void *fdt) ++{ ++ int i; ++ const struct fdt_reserve_entry *re; ++ ++ for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) { ++ if (fdt64_ld_(&re->size) == 0) ++ return i; ++ } ++ return -FDT_ERR_TRUNCATED; ++} ++ ++static int nextprop_(const void *fdt, int offset) ++{ ++ uint32_t tag; ++ int nextoffset; ++ ++ do { ++ tag = fdt_next_tag(fdt, offset, &nextoffset); ++ ++ switch (tag) { ++ case FDT_END: ++ if (nextoffset >= 0) ++ return -FDT_ERR_BADSTRUCTURE; ++ else ++ return nextoffset; ++ ++ case FDT_PROP: ++ return offset; ++ } ++ offset = nextoffset; ++ } while (tag == FDT_NOP); ++ ++ return -FDT_ERR_NOTFOUND; ++} ++ ++int fdt_subnode_offset_namelen(const void *fdt, int offset, ++ const char *name, int namelen) ++{ ++ int depth; ++ ++ FDT_RO_PROBE(fdt); ++ ++ for (depth = 0; ++ (offset >= 0) && (depth >= 0); ++ offset = fdt_next_node(fdt, offset, &depth)) ++ if ((depth == 1) ++ && fdt_nodename_eq_(fdt, offset, name, namelen)) ++ return offset; ++ ++ if (depth < 0) ++ return -FDT_ERR_NOTFOUND; ++ return offset; /* error */ ++} ++ ++int fdt_subnode_offset(const void *fdt, int parentoffset, ++ const char *name) ++{ ++ return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); ++} ++ ++int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) ++{ ++ const char *end = path + namelen; ++ const char *p = path; ++ int offset = 0; ++ ++ FDT_RO_PROBE(fdt); ++ ++ /* see if we have an alias */ ++ if (*path != '/') { ++ const char *q = memchr(path, '/', end - p); ++ ++ if (!q) ++ q = end; ++ ++ p = fdt_get_alias_namelen(fdt, p, q - p); ++ if (!p) ++ return -FDT_ERR_BADPATH; ++ offset = fdt_path_offset(fdt, p); ++ ++ p = q; ++ } ++ ++ while (p < end) { ++ const char *q; ++ ++ while (*p == '/') { ++ p++; ++ if (p == end) ++ return offset; ++ } ++ q = memchr(p, '/', end - p); ++ if (! q) ++ q = end; ++ ++ offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); ++ if (offset < 0) ++ return offset; ++ ++ p = q; ++ } ++ ++ return offset; ++} ++ ++int fdt_path_offset(const void *fdt, const char *path) ++{ ++ return fdt_path_offset_namelen(fdt, path, strlen(path)); ++} ++ ++const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) ++{ ++ const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset); ++ const char *nameptr; ++ int err; ++ ++ if (((err = fdt_ro_probe_(fdt)) < 0) ++ || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)) ++ goto fail; ++ ++ nameptr = nh->name; ++ ++ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { ++ /* ++ * For old FDT versions, match the naming conventions of V16: ++ * give only the leaf name (after all /). The actual tree ++ * contents are loosely checked. ++ */ ++ const char *leaf; ++ leaf = strrchr(nameptr, '/'); ++ if (leaf == NULL) { ++ err = -FDT_ERR_BADSTRUCTURE; ++ goto fail; ++ } ++ nameptr = leaf+1; ++ } ++ ++ if (len) ++ *len = strlen(nameptr); ++ ++ return nameptr; ++ ++ fail: ++ if (len) ++ *len = err; ++ return NULL; ++} ++ ++int fdt_first_property_offset(const void *fdt, int nodeoffset) ++{ ++ int offset; ++ ++ if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) ++ return offset; ++ ++ return nextprop_(fdt, offset); ++} ++ ++int fdt_next_property_offset(const void *fdt, int offset) ++{ ++ if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0) ++ return offset; ++ ++ return nextprop_(fdt, offset); ++} ++ ++static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt, ++ int offset, ++ int *lenp) ++{ ++ int err; ++ const struct fdt_property *prop; ++ ++ if (!can_assume(VALID_INPUT) && ++ (err = fdt_check_prop_offset_(fdt, offset)) < 0) { ++ if (lenp) ++ *lenp = err; ++ return NULL; ++ } ++ ++ prop = fdt_offset_ptr_(fdt, offset); ++ ++ if (lenp) ++ *lenp = fdt32_ld_(&prop->len); ++ ++ return prop; ++} ++ ++const struct fdt_property *fdt_get_property_by_offset(const void *fdt, ++ int offset, ++ int *lenp) ++{ ++ /* Prior to version 16, properties may need realignment ++ * and this API does not work. fdt_getprop_*() will, however. */ ++ ++ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { ++ if (lenp) ++ *lenp = -FDT_ERR_BADVERSION; ++ return NULL; ++ } ++ ++ return fdt_get_property_by_offset_(fdt, offset, lenp); ++} ++ ++static const struct fdt_property *fdt_get_property_namelen_(const void *fdt, ++ int offset, ++ const char *name, ++ int namelen, ++ int *lenp, ++ int *poffset) ++{ ++ for (offset = fdt_first_property_offset(fdt, offset); ++ (offset >= 0); ++ (offset = fdt_next_property_offset(fdt, offset))) { ++ const struct fdt_property *prop; ++ ++ prop = fdt_get_property_by_offset_(fdt, offset, lenp); ++ if (!can_assume(LIBFDT_FLAWLESS) && !prop) { ++ offset = -FDT_ERR_INTERNAL; ++ break; ++ } ++ if (fdt_string_eq_(fdt, fdt32_ld_(&prop->nameoff), ++ name, namelen)) { ++ if (poffset) ++ *poffset = offset; ++ return prop; ++ } ++ } ++ ++ if (lenp) ++ *lenp = offset; ++ return NULL; ++} ++ ++ ++const struct fdt_property *fdt_get_property_namelen(const void *fdt, ++ int offset, ++ const char *name, ++ int namelen, int *lenp) ++{ ++ /* Prior to version 16, properties may need realignment ++ * and this API does not work. fdt_getprop_*() will, however. */ ++ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { ++ if (lenp) ++ *lenp = -FDT_ERR_BADVERSION; ++ return NULL; ++ } ++ ++ return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp, ++ NULL); ++} ++ ++ ++const struct fdt_property *fdt_get_property(const void *fdt, ++ int nodeoffset, ++ const char *name, int *lenp) ++{ ++ return fdt_get_property_namelen(fdt, nodeoffset, name, ++ strlen(name), lenp); ++} ++ ++const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, ++ const char *name, int namelen, int *lenp) ++{ ++ int poffset; ++ const struct fdt_property *prop; ++ ++ prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp, ++ &poffset); ++ if (!prop) ++ return NULL; ++ ++ /* Handle realignment */ ++ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 && ++ (poffset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8) ++ return prop->data + 4; ++ return prop->data; ++} ++ ++const void *fdt_getprop_by_offset(const void *fdt, int offset, ++ const char **namep, int *lenp) ++{ ++ const struct fdt_property *prop; ++ ++ prop = fdt_get_property_by_offset_(fdt, offset, lenp); ++ if (!prop) ++ return NULL; ++ if (namep) { ++ const char *name; ++ int namelen; ++ ++ if (!can_assume(VALID_INPUT)) { ++ name = fdt_get_string(fdt, fdt32_ld_(&prop->nameoff), ++ &namelen); ++ if (!name) { ++ if (lenp) ++ *lenp = namelen; ++ return NULL; ++ } ++ *namep = name; ++ } else { ++ *namep = fdt_string(fdt, fdt32_ld_(&prop->nameoff)); ++ } ++ } ++ ++ /* Handle realignment */ ++ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 && ++ (offset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8) ++ return prop->data + 4; ++ return prop->data; ++} ++ ++const void *fdt_getprop(const void *fdt, int nodeoffset, ++ const char *name, int *lenp) ++{ ++ return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); ++} ++ ++uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) ++{ ++ const fdt32_t *php; ++ int len; ++ ++ /* FIXME: This is a bit sub-optimal, since we potentially scan ++ * over all the properties twice. */ ++ php = fdt_getprop(fdt, nodeoffset, "phandle", &len); ++ if (!php || (len != sizeof(*php))) { ++ php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); ++ if (!php || (len != sizeof(*php))) ++ return 0; ++ } ++ ++ return fdt32_ld_(php); ++} ++ ++const char *fdt_get_alias_namelen(const void *fdt, ++ const char *name, int namelen) ++{ ++ int aliasoffset; ++ ++ aliasoffset = fdt_path_offset(fdt, "/aliases"); ++ if (aliasoffset < 0) ++ return NULL; ++ ++ return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); ++} ++ ++const char *fdt_get_alias(const void *fdt, const char *name) ++{ ++ return fdt_get_alias_namelen(fdt, name, strlen(name)); ++} ++ ++int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) ++{ ++ int pdepth = 0, p = 0; ++ int offset, depth, namelen; ++ const char *name; ++ ++ FDT_RO_PROBE(fdt); ++ ++ if (buflen < 2) ++ return -FDT_ERR_NOSPACE; ++ ++ for (offset = 0, depth = 0; ++ (offset >= 0) && (offset <= nodeoffset); ++ offset = fdt_next_node(fdt, offset, &depth)) { ++ while (pdepth > depth) { ++ do { ++ p--; ++ } while (buf[p-1] != '/'); ++ pdepth--; ++ } ++ ++ if (pdepth >= depth) { ++ name = fdt_get_name(fdt, offset, &namelen); ++ if (!name) ++ return namelen; ++ if ((p + namelen + 1) <= buflen) { ++ memcpy(buf + p, name, namelen); ++ p += namelen; ++ buf[p++] = '/'; ++ pdepth++; ++ } ++ } ++ ++ if (offset == nodeoffset) { ++ if (pdepth < (depth + 1)) ++ return -FDT_ERR_NOSPACE; ++ ++ if (p > 1) /* special case so that root path is "/", not "" */ ++ p--; ++ buf[p] = '\0'; ++ return 0; ++ } ++ } ++ ++ if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) ++ return -FDT_ERR_BADOFFSET; ++ else if (offset == -FDT_ERR_BADOFFSET) ++ return -FDT_ERR_BADSTRUCTURE; ++ ++ return offset; /* error from fdt_next_node() */ ++} ++ ++int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, ++ int supernodedepth, int *nodedepth) ++{ ++ int offset, depth; ++ int supernodeoffset = -FDT_ERR_INTERNAL; ++ ++ FDT_RO_PROBE(fdt); ++ ++ if (supernodedepth < 0) ++ return -FDT_ERR_NOTFOUND; ++ ++ for (offset = 0, depth = 0; ++ (offset >= 0) && (offset <= nodeoffset); ++ offset = fdt_next_node(fdt, offset, &depth)) { ++ if (depth == supernodedepth) ++ supernodeoffset = offset; ++ ++ if (offset == nodeoffset) { ++ if (nodedepth) ++ *nodedepth = depth; ++ ++ if (supernodedepth > depth) ++ return -FDT_ERR_NOTFOUND; ++ else ++ return supernodeoffset; ++ } ++ } ++ ++ if (!can_assume(VALID_INPUT)) { ++ if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) ++ return -FDT_ERR_BADOFFSET; ++ else if (offset == -FDT_ERR_BADOFFSET) ++ return -FDT_ERR_BADSTRUCTURE; ++ } ++ ++ return offset; /* error from fdt_next_node() */ ++} ++ ++int fdt_node_depth(const void *fdt, int nodeoffset) ++{ ++ int nodedepth; ++ int err; ++ ++ err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); ++ if (err) ++ return (can_assume(LIBFDT_FLAWLESS) || err < 0) ? err : ++ -FDT_ERR_INTERNAL; ++ return nodedepth; ++} ++ ++int fdt_parent_offset(const void *fdt, int nodeoffset) ++{ ++ int nodedepth = fdt_node_depth(fdt, nodeoffset); ++ ++ if (nodedepth < 0) ++ return nodedepth; ++ return fdt_supernode_atdepth_offset(fdt, nodeoffset, ++ nodedepth - 1, NULL); ++} ++ ++int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, ++ const char *propname, ++ const void *propval, int proplen) ++{ ++ int offset; ++ const void *val; ++ int len; ++ ++ FDT_RO_PROBE(fdt); ++ ++ /* FIXME: The algorithm here is pretty horrible: we scan each ++ * property of a node in fdt_getprop(), then if that didn't ++ * find what we want, we scan over them again making our way ++ * to the next node. Still it's the easiest to implement ++ * approach; performance can come later. */ ++ for (offset = fdt_next_node(fdt, startoffset, NULL); ++ offset >= 0; ++ offset = fdt_next_node(fdt, offset, NULL)) { ++ val = fdt_getprop(fdt, offset, propname, &len); ++ if (val && (len == proplen) ++ && (memcmp(val, propval, len) == 0)) ++ return offset; ++ } ++ ++ return offset; /* error from fdt_next_node() */ ++} ++ ++int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) ++{ ++ int offset; ++ ++ if ((phandle == 0) || (phandle == ~0U)) ++ return -FDT_ERR_BADPHANDLE; ++ ++ FDT_RO_PROBE(fdt); ++ ++ /* FIXME: The algorithm here is pretty horrible: we ++ * potentially scan each property of a node in ++ * fdt_get_phandle(), then if that didn't find what ++ * we want, we scan over them again making our way to the next ++ * node. Still it's the easiest to implement approach; ++ * performance can come later. */ ++ for (offset = fdt_next_node(fdt, -1, NULL); ++ offset >= 0; ++ offset = fdt_next_node(fdt, offset, NULL)) { ++ if (fdt_get_phandle(fdt, offset) == phandle) ++ return offset; ++ } ++ ++ return offset; /* error from fdt_next_node() */ ++} ++ ++int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) ++{ ++ int len = strlen(str); ++ const char *p; ++ ++ while (listlen >= len) { ++ if (memcmp(str, strlist, len+1) == 0) ++ return 1; ++ p = memchr(strlist, '\0', listlen); ++ if (!p) ++ return 0; /* malformed strlist.. */ ++ listlen -= (p-strlist) + 1; ++ strlist = p + 1; ++ } ++ return 0; ++} ++ ++int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) ++{ ++ const char *list, *end; ++ int length, count = 0; ++ ++ list = fdt_getprop(fdt, nodeoffset, property, &length); ++ if (!list) ++ return length; ++ ++ end = list + length; ++ ++ while (list < end) { ++ length = strnlen(list, end - list) + 1; ++ ++ /* Abort if the last string isn't properly NUL-terminated. */ ++ if (list + length > end) ++ return -FDT_ERR_BADVALUE; ++ ++ list += length; ++ count++; ++ } ++ ++ return count; ++} ++ ++int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, ++ const char *string) ++{ ++ int length, len, idx = 0; ++ const char *list, *end; ++ ++ list = fdt_getprop(fdt, nodeoffset, property, &length); ++ if (!list) ++ return length; ++ ++ len = strlen(string) + 1; ++ end = list + length; ++ ++ while (list < end) { ++ length = strnlen(list, end - list) + 1; ++ ++ /* Abort if the last string isn't properly NUL-terminated. */ ++ if (list + length > end) ++ return -FDT_ERR_BADVALUE; ++ ++ if (length == len && memcmp(list, string, length) == 0) ++ return idx; ++ ++ list += length; ++ idx++; ++ } ++ ++ return -FDT_ERR_NOTFOUND; ++} ++ ++const char *fdt_stringlist_get(const void *fdt, int nodeoffset, ++ const char *property, int idx, ++ int *lenp) ++{ ++ const char *list, *end; ++ int length; ++ ++ list = fdt_getprop(fdt, nodeoffset, property, &length); ++ if (!list) { ++ if (lenp) ++ *lenp = length; ++ ++ return NULL; ++ } ++ ++ end = list + length; ++ ++ while (list < end) { ++ length = strnlen(list, end - list) + 1; ++ ++ /* Abort if the last string isn't properly NUL-terminated. */ ++ if (list + length > end) { ++ if (lenp) ++ *lenp = -FDT_ERR_BADVALUE; ++ ++ return NULL; ++ } ++ ++ if (idx == 0) { ++ if (lenp) ++ *lenp = length - 1; ++ ++ return list; ++ } ++ ++ list += length; ++ idx--; ++ } ++ ++ if (lenp) ++ *lenp = -FDT_ERR_NOTFOUND; ++ ++ return NULL; ++} ++ ++int fdt_node_check_compatible(const void *fdt, int nodeoffset, ++ const char *compatible) ++{ ++ const void *prop; ++ int len; ++ ++ prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); ++ if (!prop) ++ return len; ++ ++ return !fdt_stringlist_contains(prop, len, compatible); ++} ++ ++int fdt_node_offset_by_compatible(const void *fdt, int startoffset, ++ const char *compatible) ++{ ++ int offset, err; ++ ++ FDT_RO_PROBE(fdt); ++ ++ /* FIXME: The algorithm here is pretty horrible: we scan each ++ * property of a node in fdt_node_check_compatible(), then if ++ * that didn't find what we want, we scan over them again ++ * making our way to the next node. Still it's the easiest to ++ * implement approach; performance can come later. */ ++ for (offset = fdt_next_node(fdt, startoffset, NULL); ++ offset >= 0; ++ offset = fdt_next_node(fdt, offset, NULL)) { ++ err = fdt_node_check_compatible(fdt, offset, compatible); ++ if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) ++ return err; ++ else if (err == 0) ++ return offset; ++ } ++ ++ return offset; /* error from fdt_next_node() */ ++} +diff --git a/common/libfdt/fdt_rw.c b/common/libfdt/fdt_rw.c +new file mode 100644 +index 0000000..3621d36 +--- /dev/null ++++ b/common/libfdt/fdt_rw.c +@@ -0,0 +1,500 @@ ++// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) ++/* ++ * libfdt - Flat Device Tree manipulation ++ * Copyright (C) 2006 David Gibson, IBM Corporation. ++ */ ++#include "libfdt_env.h" ++ ++#include <fdt.h> ++#include <libfdt.h> ++ ++#include "libfdt_internal.h" ++ ++static int fdt_blocks_misordered_(const void *fdt, ++ int mem_rsv_size, int struct_size) ++{ ++ return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8)) ++ || (fdt_off_dt_struct(fdt) < ++ (fdt_off_mem_rsvmap(fdt) + mem_rsv_size)) ++ || (fdt_off_dt_strings(fdt) < ++ (fdt_off_dt_struct(fdt) + struct_size)) ++ || (fdt_totalsize(fdt) < ++ (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); ++} ++ ++static int fdt_rw_probe_(void *fdt) ++{ ++ if (can_assume(VALID_DTB)) ++ return 0; ++ FDT_RO_PROBE(fdt); ++ ++ if (!can_assume(LATEST) && fdt_version(fdt) < 17) ++ return -FDT_ERR_BADVERSION; ++ if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry), ++ fdt_size_dt_struct(fdt))) ++ return -FDT_ERR_BADLAYOUT; ++ if (!can_assume(LATEST) && fdt_version(fdt) > 17) ++ fdt_set_version(fdt, 17); ++ ++ return 0; ++} ++ ++#define FDT_RW_PROBE(fdt) \ ++ { \ ++ int err_; \ ++ if ((err_ = fdt_rw_probe_(fdt)) != 0) \ ++ return err_; \ ++ } ++ ++static inline unsigned int fdt_data_size_(void *fdt) ++{ ++ return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); ++} ++ ++static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen) ++{ ++ char *p = splicepoint; ++ unsigned int dsize = fdt_data_size_(fdt); ++ size_t soff = p - (char *)fdt; ++ ++ if ((oldlen < 0) || (soff + oldlen < soff) || (soff + oldlen > dsize)) ++ return -FDT_ERR_BADOFFSET; ++ if ((p < (char *)fdt) || (dsize + newlen < (unsigned)oldlen)) ++ return -FDT_ERR_BADOFFSET; ++ if (dsize - oldlen + newlen > fdt_totalsize(fdt)) ++ return -FDT_ERR_NOSPACE; ++ memmove(p + newlen, p + oldlen, ((char *)fdt + dsize) - (p + oldlen)); ++ return 0; ++} ++ ++static int fdt_splice_mem_rsv_(void *fdt, struct fdt_reserve_entry *p, ++ int oldn, int newn) ++{ ++ int delta = (newn - oldn) * sizeof(*p); ++ int err; ++ err = fdt_splice_(fdt, p, oldn * sizeof(*p), newn * sizeof(*p)); ++ if (err) ++ return err; ++ fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta); ++ fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); ++ return 0; ++} ++ ++static int fdt_splice_struct_(void *fdt, void *p, ++ int oldlen, int newlen) ++{ ++ int delta = newlen - oldlen; ++ int err; ++ ++ if ((err = fdt_splice_(fdt, p, oldlen, newlen))) ++ return err; ++ ++ fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta); ++ fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); ++ return 0; ++} ++ ++/* Must only be used to roll back in case of error */ ++static void fdt_del_last_string_(void *fdt, const char *s) ++{ ++ int newlen = strlen(s) + 1; ++ ++ fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) - newlen); ++} ++ ++static int fdt_splice_string_(void *fdt, int newlen) ++{ ++ void *p = (char *)fdt ++ + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); ++ int err; ++ ++ if ((err = fdt_splice_(fdt, p, 0, newlen))) ++ return err; ++ ++ fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen); ++ return 0; ++} ++ ++/** ++ * fdt_find_add_string_() - Find or allocate a string ++ * ++ * @fdt: pointer to the device tree to check/adjust ++ * @s: string to find/add ++ * @allocated: Set to 0 if the string was found, 1 if not found and so ++ * allocated. Ignored if can_assume(NO_ROLLBACK) ++ * @return offset of string in the string table (whether found or added) ++ */ ++static int fdt_find_add_string_(void *fdt, const char *s, int *allocated) ++{ ++ char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); ++ const char *p; ++ char *new; ++ int len = strlen(s) + 1; ++ int err; ++ ++ if (!can_assume(NO_ROLLBACK)) ++ *allocated = 0; ++ ++ p = fdt_find_string_(strtab, fdt_size_dt_strings(fdt), s); ++ if (p) ++ /* found it */ ++ return (p - strtab); ++ ++ new = strtab + fdt_size_dt_strings(fdt); ++ err = fdt_splice_string_(fdt, len); ++ if (err) ++ return err; ++ ++ if (!can_assume(NO_ROLLBACK)) ++ *allocated = 1; ++ ++ memcpy(new, s, len); ++ return (new - strtab); ++} ++ ++int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size) ++{ ++ struct fdt_reserve_entry *re; ++ int err; ++ ++ FDT_RW_PROBE(fdt); ++ ++ re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt)); ++ err = fdt_splice_mem_rsv_(fdt, re, 0, 1); ++ if (err) ++ return err; ++ ++ re->address = cpu_to_fdt64(address); ++ re->size = cpu_to_fdt64(size); ++ return 0; ++} ++ ++int fdt_del_mem_rsv(void *fdt, int n) ++{ ++ struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n); ++ ++ FDT_RW_PROBE(fdt); ++ ++ if (n >= fdt_num_mem_rsv(fdt)) ++ return -FDT_ERR_NOTFOUND; ++ ++ return fdt_splice_mem_rsv_(fdt, re, 1, 0); ++} ++ ++static int fdt_resize_property_(void *fdt, int nodeoffset, const char *name, ++ int len, struct fdt_property **prop) ++{ ++ int oldlen; ++ int err; ++ ++ *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); ++ if (!*prop) ++ return oldlen; ++ ++ if ((err = fdt_splice_struct_(fdt, (*prop)->data, FDT_TAGALIGN(oldlen), ++ FDT_TAGALIGN(len)))) ++ return err; ++ ++ (*prop)->len = cpu_to_fdt32(len); ++ return 0; ++} ++ ++static int fdt_add_property_(void *fdt, int nodeoffset, const char *name, ++ int len, struct fdt_property **prop) ++{ ++ int proplen; ++ int nextoffset; ++ int namestroff; ++ int err; ++ int allocated; ++ ++ if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) ++ return nextoffset; ++ ++ namestroff = fdt_find_add_string_(fdt, name, &allocated); ++ if (namestroff < 0) ++ return namestroff; ++ ++ *prop = fdt_offset_ptr_w_(fdt, nextoffset); ++ proplen = sizeof(**prop) + FDT_TAGALIGN(len); ++ ++ err = fdt_splice_struct_(fdt, *prop, 0, proplen); ++ if (err) { ++ /* Delete the string if we failed to add it */ ++ if (!can_assume(NO_ROLLBACK) && allocated) ++ fdt_del_last_string_(fdt, name); ++ return err; ++ } ++ ++ (*prop)->tag = cpu_to_fdt32(FDT_PROP); ++ (*prop)->nameoff = cpu_to_fdt32(namestroff); ++ (*prop)->len = cpu_to_fdt32(len); ++ return 0; ++} ++ ++int fdt_set_name(void *fdt, int nodeoffset, const char *name) ++{ ++ char *namep; ++ int oldlen, newlen; ++ int err; ++ ++ FDT_RW_PROBE(fdt); ++ ++ namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen); ++ if (!namep) ++ return oldlen; ++ ++ newlen = strlen(name); ++ ++ err = fdt_splice_struct_(fdt, namep, FDT_TAGALIGN(oldlen+1), ++ FDT_TAGALIGN(newlen+1)); ++ if (err) ++ return err; ++ ++ memcpy(namep, name, newlen+1); ++ return 0; ++} ++ ++int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, ++ int len, void **prop_data) ++{ ++ struct fdt_property *prop; ++ int err; ++ ++ FDT_RW_PROBE(fdt); ++ ++ err = fdt_resize_property_(fdt, nodeoffset, name, len, &prop); ++ if (err == -FDT_ERR_NOTFOUND) ++ err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); ++ if (err) ++ return err; ++ ++ *prop_data = prop->data; ++ return 0; ++} ++ ++int fdt_setprop(void *fdt, int nodeoffset, const char *name, ++ const void *val, int len) ++{ ++ void *prop_data; ++ int err; ++ ++ err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data); ++ if (err) ++ return err; ++ ++ if (len) ++ memcpy(prop_data, val, len); ++ return 0; ++} ++ ++int fdt_appendprop(void *fdt, int nodeoffset, const char *name, ++ const void *val, int len) ++{ ++ struct fdt_property *prop; ++ int err, oldlen, newlen; ++ ++ FDT_RW_PROBE(fdt); ++ ++ prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); ++ if (prop) { ++ newlen = len + oldlen; ++ err = fdt_splice_struct_(fdt, prop->data, ++ FDT_TAGALIGN(oldlen), ++ FDT_TAGALIGN(newlen)); ++ if (err) ++ return err; ++ prop->len = cpu_to_fdt32(newlen); ++ memcpy(prop->data + oldlen, val, len); ++ } else { ++ err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); ++ if (err) ++ return err; ++ memcpy(prop->data, val, len); ++ } ++ return 0; ++} ++ ++int fdt_delprop(void *fdt, int nodeoffset, const char *name) ++{ ++ struct fdt_property *prop; ++ int len, proplen; ++ ++ FDT_RW_PROBE(fdt); ++ ++ prop = fdt_get_property_w(fdt, nodeoffset, name, &len); ++ if (!prop) ++ return len; ++ ++ proplen = sizeof(*prop) + FDT_TAGALIGN(len); ++ return fdt_splice_struct_(fdt, prop, proplen, 0); ++} ++ ++int fdt_add_subnode_namelen(void *fdt, int parentoffset, ++ const char *name, int namelen) ++{ ++ struct fdt_node_header *nh; ++ int offset, nextoffset; ++ int nodelen; ++ int err; ++ uint32_t tag; ++ fdt32_t *endtag; ++ ++ FDT_RW_PROBE(fdt); ++ ++ offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen); ++ if (offset >= 0) ++ return -FDT_ERR_EXISTS; ++ else if (offset != -FDT_ERR_NOTFOUND) ++ return offset; ++ ++ /* Try to place the new node after the parent's properties */ ++ tag = fdt_next_tag(fdt, parentoffset, &nextoffset); ++ /* the fdt_subnode_offset_namelen() should ensure this never hits */ ++ if (!can_assume(LIBFDT_FLAWLESS) && (tag != FDT_BEGIN_NODE)) ++ return -FDT_ERR_INTERNAL; ++ do { ++ offset = nextoffset; ++ tag = fdt_next_tag(fdt, offset, &nextoffset); ++ } while ((tag == FDT_PROP) || (tag == FDT_NOP)); ++ ++ nh = fdt_offset_ptr_w_(fdt, offset); ++ nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE; ++ ++ err = fdt_splice_struct_(fdt, nh, 0, nodelen); ++ if (err) ++ return err; ++ ++ nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); ++ memset(nh->name, 0, FDT_TAGALIGN(namelen+1)); ++ memcpy(nh->name, name, namelen); ++ endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE); ++ *endtag = cpu_to_fdt32(FDT_END_NODE); ++ ++ return offset; ++} ++ ++int fdt_add_subnode(void *fdt, int parentoffset, const char *name) ++{ ++ return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name)); ++} ++ ++int fdt_del_node(void *fdt, int nodeoffset) ++{ ++ int endoffset; ++ ++ FDT_RW_PROBE(fdt); ++ ++ endoffset = fdt_node_end_offset_(fdt, nodeoffset); ++ if (endoffset < 0) ++ return endoffset; ++ ++ return fdt_splice_struct_(fdt, fdt_offset_ptr_w_(fdt, nodeoffset), ++ endoffset - nodeoffset, 0); ++} ++ ++static void fdt_packblocks_(const char *old, char *new, ++ int mem_rsv_size, ++ int struct_size, ++ int strings_size) ++{ ++ int mem_rsv_off, struct_off, strings_off; ++ ++ mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8); ++ struct_off = mem_rsv_off + mem_rsv_size; ++ strings_off = struct_off + struct_size; ++ ++ memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size); ++ fdt_set_off_mem_rsvmap(new, mem_rsv_off); ++ ++ memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size); ++ fdt_set_off_dt_struct(new, struct_off); ++ fdt_set_size_dt_struct(new, struct_size); ++ ++ memmove(new + strings_off, old + fdt_off_dt_strings(old), strings_size); ++ fdt_set_off_dt_strings(new, strings_off); ++ fdt_set_size_dt_strings(new, fdt_size_dt_strings(old)); ++} ++ ++int fdt_open_into(const void *fdt, void *buf, int bufsize) ++{ ++ int err; ++ int mem_rsv_size, struct_size; ++ int newsize; ++ const char *fdtstart = fdt; ++ const char *fdtend = fdtstart + fdt_totalsize(fdt); ++ char *tmp; ++ ++ FDT_RO_PROBE(fdt); ++ ++ mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) ++ * sizeof(struct fdt_reserve_entry); ++ ++ if (can_assume(LATEST) || fdt_version(fdt) >= 17) { ++ struct_size = fdt_size_dt_struct(fdt); ++ } else if (fdt_version(fdt) == 16) { ++ struct_size = 0; ++ while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END) ++ ; ++ if (struct_size < 0) ++ return struct_size; ++ } else { ++ return -FDT_ERR_BADVERSION; ++ } ++ ++ if (can_assume(LIBFDT_ORDER) || ++ !fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) { ++ /* no further work necessary */ ++ err = fdt_move(fdt, buf, bufsize); ++ if (err) ++ return err; ++ fdt_set_version(buf, 17); ++ fdt_set_size_dt_struct(buf, struct_size); ++ fdt_set_totalsize(buf, bufsize); ++ return 0; ++ } ++ ++ /* Need to reorder */ ++ newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size ++ + struct_size + fdt_size_dt_strings(fdt); ++ ++ if (bufsize < newsize) ++ return -FDT_ERR_NOSPACE; ++ ++ /* First attempt to build converted tree at beginning of buffer */ ++ tmp = buf; ++ /* But if that overlaps with the old tree... */ ++ if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) { ++ /* Try right after the old tree instead */ ++ tmp = (char *)(uintptr_t)fdtend; ++ if ((tmp + newsize) > ((char *)buf + bufsize)) ++ return -FDT_ERR_NOSPACE; ++ } ++ ++ fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size, ++ fdt_size_dt_strings(fdt)); ++ memmove(buf, tmp, newsize); ++ ++ fdt_set_magic(buf, FDT_MAGIC); ++ fdt_set_totalsize(buf, bufsize); ++ fdt_set_version(buf, 17); ++ fdt_set_last_comp_version(buf, 16); ++ fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt)); ++ ++ return 0; ++} ++ ++int fdt_pack(void *fdt) ++{ ++ int mem_rsv_size; ++ ++ FDT_RW_PROBE(fdt); ++ ++ mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) ++ * sizeof(struct fdt_reserve_entry); ++ fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt), ++ fdt_size_dt_strings(fdt)); ++ fdt_set_totalsize(fdt, fdt_data_size_(fdt)); ++ ++ return 0; ++} +diff --git a/common/libfdt/fdt_strerror.c b/common/libfdt/fdt_strerror.c +new file mode 100644 +index 0000000..b435693 +--- /dev/null ++++ b/common/libfdt/fdt_strerror.c +@@ -0,0 +1,59 @@ ++// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) ++/* ++ * libfdt - Flat Device Tree manipulation ++ * Copyright (C) 2006 David Gibson, IBM Corporation. ++ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++#include "libfdt_env.h" ++ ++#include <fdt.h> ++#include <libfdt.h> ++ ++#include "libfdt_internal.h" ++ ++struct fdt_errtabent { ++ const char *str; ++}; ++ ++#define FDT_ERRTABENT(val) \ ++ [(val)] = { .str = #val, } ++ ++static struct fdt_errtabent fdt_errtable[] = { ++ FDT_ERRTABENT(FDT_ERR_NOTFOUND), ++ FDT_ERRTABENT(FDT_ERR_EXISTS), ++ FDT_ERRTABENT(FDT_ERR_NOSPACE), ++ ++ FDT_ERRTABENT(FDT_ERR_BADOFFSET), ++ FDT_ERRTABENT(FDT_ERR_BADPATH), ++ FDT_ERRTABENT(FDT_ERR_BADPHANDLE), ++ FDT_ERRTABENT(FDT_ERR_BADSTATE), ++ ++ FDT_ERRTABENT(FDT_ERR_TRUNCATED), ++ FDT_ERRTABENT(FDT_ERR_BADMAGIC), ++ FDT_ERRTABENT(FDT_ERR_BADVERSION), ++ FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE), ++ FDT_ERRTABENT(FDT_ERR_BADLAYOUT), ++ FDT_ERRTABENT(FDT_ERR_INTERNAL), ++ FDT_ERRTABENT(FDT_ERR_BADNCELLS), ++ FDT_ERRTABENT(FDT_ERR_BADVALUE), ++ FDT_ERRTABENT(FDT_ERR_BADOVERLAY), ++ FDT_ERRTABENT(FDT_ERR_NOPHANDLES), ++ FDT_ERRTABENT(FDT_ERR_BADFLAGS), ++}; ++#define FDT_ERRTABSIZE ((int)(sizeof(fdt_errtable) / sizeof(fdt_errtable[0]))) ++ ++const char *fdt_strerror(int errval) ++{ ++ if (errval > 0) ++ return "<valid offset/length>"; ++ else if (errval == 0) ++ return "<no error>"; ++ else if (-errval < FDT_ERRTABSIZE) { ++ const char *s = fdt_errtable[-errval].str; ++ ++ if (s) ++ return s; ++ } ++ ++ return "<unknown error>"; ++} +diff --git a/common/libfdt/fdt_sw.c b/common/libfdt/fdt_sw.c +new file mode 100644 +index 0000000..4c569ee +--- /dev/null ++++ b/common/libfdt/fdt_sw.c +@@ -0,0 +1,384 @@ ++// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) ++/* ++ * libfdt - Flat Device Tree manipulation ++ * Copyright (C) 2006 David Gibson, IBM Corporation. ++ */ ++#include "libfdt_env.h" ++ ++#include <fdt.h> ++#include <libfdt.h> ++ ++#include "libfdt_internal.h" ++ ++static int fdt_sw_probe_(void *fdt) ++{ ++ if (!can_assume(VALID_INPUT)) { ++ if (fdt_magic(fdt) == FDT_MAGIC) ++ return -FDT_ERR_BADSTATE; ++ else if (fdt_magic(fdt) != FDT_SW_MAGIC) ++ return -FDT_ERR_BADMAGIC; ++ } ++ ++ return 0; ++} ++ ++#define FDT_SW_PROBE(fdt) \ ++ { \ ++ int err; \ ++ if ((err = fdt_sw_probe_(fdt)) != 0) \ ++ return err; \ ++ } ++ ++/* 'memrsv' state: Initial state after fdt_create() ++ * ++ * Allowed functions: ++ * fdt_add_reservemap_entry() ++ * fdt_finish_reservemap() [moves to 'struct' state] ++ */ ++static int fdt_sw_probe_memrsv_(void *fdt) ++{ ++ int err = fdt_sw_probe_(fdt); ++ if (err) ++ return err; ++ ++ if (!can_assume(VALID_INPUT) && fdt_off_dt_strings(fdt) != 0) ++ return -FDT_ERR_BADSTATE; ++ return 0; ++} ++ ++#define FDT_SW_PROBE_MEMRSV(fdt) \ ++ { \ ++ int err; \ ++ if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \ ++ return err; \ ++ } ++ ++/* 'struct' state: Enter this state after fdt_finish_reservemap() ++ * ++ * Allowed functions: ++ * fdt_begin_node() ++ * fdt_end_node() ++ * fdt_property*() ++ * fdt_finish() [moves to 'complete' state] ++ */ ++static int fdt_sw_probe_struct_(void *fdt) ++{ ++ int err = fdt_sw_probe_(fdt); ++ if (err) ++ return err; ++ ++ if (!can_assume(VALID_INPUT) && ++ fdt_off_dt_strings(fdt) != fdt_totalsize(fdt)) ++ return -FDT_ERR_BADSTATE; ++ return 0; ++} ++ ++#define FDT_SW_PROBE_STRUCT(fdt) \ ++ { \ ++ int err; \ ++ if ((err = fdt_sw_probe_struct_(fdt)) != 0) \ ++ return err; \ ++ } ++ ++static inline uint32_t sw_flags(void *fdt) ++{ ++ /* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */ ++ return fdt_last_comp_version(fdt); ++} ++ ++/* 'complete' state: Enter this state after fdt_finish() ++ * ++ * Allowed functions: none ++ */ ++ ++static void *fdt_grab_space_(void *fdt, size_t len) ++{ ++ unsigned int offset = fdt_size_dt_struct(fdt); ++ unsigned int spaceleft; ++ ++ spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) ++ - fdt_size_dt_strings(fdt); ++ ++ if ((offset + len < offset) || (offset + len > spaceleft)) ++ return NULL; ++ ++ fdt_set_size_dt_struct(fdt, offset + len); ++ return fdt_offset_ptr_w_(fdt, offset); ++} ++ ++int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags) ++{ ++ const int hdrsize = FDT_ALIGN(sizeof(struct fdt_header), ++ sizeof(struct fdt_reserve_entry)); ++ void *fdt = buf; ++ ++ if (bufsize < hdrsize) ++ return -FDT_ERR_NOSPACE; ++ ++ if (flags & ~FDT_CREATE_FLAGS_ALL) ++ return -FDT_ERR_BADFLAGS; ++ ++ memset(buf, 0, bufsize); ++ ++ /* ++ * magic and last_comp_version keep intermediate state during the fdt ++ * creation process, which is replaced with the proper FDT format by ++ * fdt_finish(). ++ * ++ * flags should be accessed with sw_flags(). ++ */ ++ fdt_set_magic(fdt, FDT_SW_MAGIC); ++ fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); ++ fdt_set_last_comp_version(fdt, flags); ++ ++ fdt_set_totalsize(fdt, bufsize); ++ ++ fdt_set_off_mem_rsvmap(fdt, hdrsize); ++ fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); ++ fdt_set_off_dt_strings(fdt, 0); ++ ++ return 0; ++} ++ ++int fdt_create(void *buf, int bufsize) ++{ ++ return fdt_create_with_flags(buf, bufsize, 0); ++} ++ ++int fdt_resize(void *fdt, void *buf, int bufsize) ++{ ++ size_t headsize, tailsize; ++ char *oldtail, *newtail; ++ ++ FDT_SW_PROBE(fdt); ++ ++ if (bufsize < 0) ++ return -FDT_ERR_NOSPACE; ++ ++ headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); ++ tailsize = fdt_size_dt_strings(fdt); ++ ++ if (!can_assume(VALID_DTB) && ++ headsize + tailsize > fdt_totalsize(fdt)) ++ return -FDT_ERR_INTERNAL; ++ ++ if ((headsize + tailsize) > (unsigned)bufsize) ++ return -FDT_ERR_NOSPACE; ++ ++ oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; ++ newtail = (char *)buf + bufsize - tailsize; ++ ++ /* Two cases to avoid clobbering data if the old and new ++ * buffers partially overlap */ ++ if (buf <= fdt) { ++ memmove(buf, fdt, headsize); ++ memmove(newtail, oldtail, tailsize); ++ } else { ++ memmove(newtail, oldtail, tailsize); ++ memmove(buf, fdt, headsize); ++ } ++ ++ fdt_set_totalsize(buf, bufsize); ++ if (fdt_off_dt_strings(buf)) ++ fdt_set_off_dt_strings(buf, bufsize); ++ ++ return 0; ++} ++ ++int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) ++{ ++ struct fdt_reserve_entry *re; ++ int offset; ++ ++ FDT_SW_PROBE_MEMRSV(fdt); ++ ++ offset = fdt_off_dt_struct(fdt); ++ if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) ++ return -FDT_ERR_NOSPACE; ++ ++ re = (struct fdt_reserve_entry *)((char *)fdt + offset); ++ re->address = cpu_to_fdt64(addr); ++ re->size = cpu_to_fdt64(size); ++ ++ fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); ++ ++ return 0; ++} ++ ++int fdt_finish_reservemap(void *fdt) ++{ ++ int err = fdt_add_reservemap_entry(fdt, 0, 0); ++ ++ if (err) ++ return err; ++ ++ fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt)); ++ return 0; ++} ++ ++int fdt_begin_node(void *fdt, const char *name) ++{ ++ struct fdt_node_header *nh; ++ int namelen; ++ ++ FDT_SW_PROBE_STRUCT(fdt); ++ ++ namelen = strlen(name) + 1; ++ nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); ++ if (! nh) ++ return -FDT_ERR_NOSPACE; ++ ++ nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); ++ memcpy(nh->name, name, namelen); ++ return 0; ++} ++ ++int fdt_end_node(void *fdt) ++{ ++ fdt32_t *en; ++ ++ FDT_SW_PROBE_STRUCT(fdt); ++ ++ en = fdt_grab_space_(fdt, FDT_TAGSIZE); ++ if (! en) ++ return -FDT_ERR_NOSPACE; ++ ++ *en = cpu_to_fdt32(FDT_END_NODE); ++ return 0; ++} ++ ++static int fdt_add_string_(void *fdt, const char *s) ++{ ++ char *strtab = (char *)fdt + fdt_totalsize(fdt); ++ unsigned int strtabsize = fdt_size_dt_strings(fdt); ++ unsigned int len = strlen(s) + 1; ++ unsigned int struct_top, offset; ++ ++ offset = strtabsize + len; ++ struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); ++ if (fdt_totalsize(fdt) - offset < struct_top) ++ return 0; /* no more room :( */ ++ ++ memcpy(strtab - offset, s, len); ++ fdt_set_size_dt_strings(fdt, strtabsize + len); ++ return -offset; ++} ++ ++/* Must only be used to roll back in case of error */ ++static void fdt_del_last_string_(void *fdt, const char *s) ++{ ++ int strtabsize = fdt_size_dt_strings(fdt); ++ int len = strlen(s) + 1; ++ ++ fdt_set_size_dt_strings(fdt, strtabsize - len); ++} ++ ++static int fdt_find_add_string_(void *fdt, const char *s, int *allocated) ++{ ++ char *strtab = (char *)fdt + fdt_totalsize(fdt); ++ int strtabsize = fdt_size_dt_strings(fdt); ++ const char *p; ++ ++ *allocated = 0; ++ ++ p = fdt_find_string_(strtab - strtabsize, strtabsize, s); ++ if (p) ++ return p - strtab; ++ ++ *allocated = 1; ++ ++ return fdt_add_string_(fdt, s); ++} ++ ++int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp) ++{ ++ struct fdt_property *prop; ++ int nameoff; ++ int allocated; ++ ++ FDT_SW_PROBE_STRUCT(fdt); ++ ++ /* String de-duplication can be slow, _NO_NAME_DEDUP skips it */ ++ if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) { ++ allocated = 1; ++ nameoff = fdt_add_string_(fdt, name); ++ } else { ++ nameoff = fdt_find_add_string_(fdt, name, &allocated); ++ } ++ if (nameoff == 0) ++ return -FDT_ERR_NOSPACE; ++ ++ prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); ++ if (! prop) { ++ if (allocated) ++ fdt_del_last_string_(fdt, name); ++ return -FDT_ERR_NOSPACE; ++ } ++ ++ prop->tag = cpu_to_fdt32(FDT_PROP); ++ prop->nameoff = cpu_to_fdt32(nameoff); ++ prop->len = cpu_to_fdt32(len); ++ *valp = prop->data; ++ return 0; ++} ++ ++int fdt_property(void *fdt, const char *name, const void *val, int len) ++{ ++ void *ptr; ++ int ret; ++ ++ ret = fdt_property_placeholder(fdt, name, len, &ptr); ++ if (ret) ++ return ret; ++ memcpy(ptr, val, len); ++ return 0; ++} ++ ++int fdt_finish(void *fdt) ++{ ++ char *p = (char *)fdt; ++ fdt32_t *end; ++ int oldstroffset, newstroffset; ++ uint32_t tag; ++ int offset, nextoffset; ++ ++ FDT_SW_PROBE_STRUCT(fdt); ++ ++ /* Add terminator */ ++ end = fdt_grab_space_(fdt, sizeof(*end)); ++ if (! end) ++ return -FDT_ERR_NOSPACE; ++ *end = cpu_to_fdt32(FDT_END); ++ ++ /* Relocate the string table */ ++ oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); ++ newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); ++ memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); ++ fdt_set_off_dt_strings(fdt, newstroffset); ++ ++ /* Walk the structure, correcting string offsets */ ++ offset = 0; ++ while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { ++ if (tag == FDT_PROP) { ++ struct fdt_property *prop = ++ fdt_offset_ptr_w_(fdt, offset); ++ int nameoff; ++ ++ nameoff = fdt32_to_cpu(prop->nameoff); ++ nameoff += fdt_size_dt_strings(fdt); ++ prop->nameoff = cpu_to_fdt32(nameoff); ++ } ++ offset = nextoffset; ++ } ++ if (nextoffset < 0) ++ return nextoffset; ++ ++ /* Finally, adjust the header */ ++ fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); ++ ++ /* And fix up fields that were keeping intermediate state. */ ++ fdt_set_last_comp_version(fdt, FDT_LAST_COMPATIBLE_VERSION); ++ fdt_set_magic(fdt, FDT_MAGIC); ++ ++ return 0; ++} +diff --git a/common/libfdt/fdt_wip.c b/common/libfdt/fdt_wip.c +new file mode 100644 +index 0000000..c2d7566 +--- /dev/null ++++ b/common/libfdt/fdt_wip.c +@@ -0,0 +1,94 @@ ++// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) ++/* ++ * libfdt - Flat Device Tree manipulation ++ * Copyright (C) 2006 David Gibson, IBM Corporation. ++ */ ++#include "libfdt_env.h" ++ ++#include <fdt.h> ++#include <libfdt.h> ++ ++#include "libfdt_internal.h" ++ ++int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, ++ const char *name, int namelen, ++ uint32_t idx, const void *val, ++ int len) ++{ ++ void *propval; ++ int proplen; ++ ++ propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen, ++ &proplen); ++ if (!propval) ++ return proplen; ++ ++ if ((unsigned)proplen < (len + idx)) ++ return -FDT_ERR_NOSPACE; ++ ++ memcpy((char *)propval + idx, val, len); ++ return 0; ++} ++ ++int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, ++ const void *val, int len) ++{ ++ const void *propval; ++ int proplen; ++ ++ propval = fdt_getprop(fdt, nodeoffset, name, &proplen); ++ if (!propval) ++ return proplen; ++ ++ if (proplen != len) ++ return -FDT_ERR_NOSPACE; ++ ++ return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name, ++ strlen(name), 0, ++ val, len); ++} ++ ++static void fdt_nop_region_(void *start, int len) ++{ ++ fdt32_t *p; ++ ++ for (p = start; (char *)p < ((char *)start + len); p++) ++ *p = cpu_to_fdt32(FDT_NOP); ++} ++ ++int fdt_nop_property(void *fdt, int nodeoffset, const char *name) ++{ ++ struct fdt_property *prop; ++ int len; ++ ++ prop = fdt_get_property_w(fdt, nodeoffset, name, &len); ++ if (!prop) ++ return len; ++ ++ fdt_nop_region_(prop, len + sizeof(*prop)); ++ ++ return 0; ++} ++ ++int fdt_node_end_offset_(void *fdt, int offset) ++{ ++ int depth = 0; ++ ++ while ((offset >= 0) && (depth >= 0)) ++ offset = fdt_next_node(fdt, offset, &depth); ++ ++ return offset; ++} ++ ++int fdt_nop_node(void *fdt, int nodeoffset) ++{ ++ int endoffset; ++ ++ endoffset = fdt_node_end_offset_(fdt, nodeoffset); ++ if (endoffset < 0) ++ return endoffset; ++ ++ fdt_nop_region_(fdt_offset_ptr_w(fdt, nodeoffset, 0), ++ endoffset - nodeoffset); ++ return 0; ++} +diff --git a/common/libfdt/libfdt_internal.h b/common/libfdt/libfdt_internal.h +new file mode 100644 +index 0000000..16bda19 +--- /dev/null ++++ b/common/libfdt/libfdt_internal.h +@@ -0,0 +1,192 @@ ++/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ ++#ifndef LIBFDT_INTERNAL_H ++#define LIBFDT_INTERNAL_H ++/* ++ * libfdt - Flat Device Tree manipulation ++ * Copyright (C) 2006 David Gibson, IBM Corporation. ++ */ ++#include <fdt.h> ++ ++#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) ++#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE)) ++ ++int32_t fdt_ro_probe_(const void *fdt); ++#define FDT_RO_PROBE(fdt) \ ++ { \ ++ int32_t totalsize_; \ ++ if ((totalsize_ = fdt_ro_probe_(fdt)) < 0) \ ++ return totalsize_; \ ++ } ++ ++int fdt_check_node_offset_(const void *fdt, int offset); ++int fdt_check_prop_offset_(const void *fdt, int offset); ++const char *fdt_find_string_(const char *strtab, int tabsize, const char *s); ++int fdt_node_end_offset_(void *fdt, int nodeoffset); ++ ++static inline const void *fdt_offset_ptr_(const void *fdt, int offset) ++{ ++ return (const char *)fdt + fdt_off_dt_struct(fdt) + offset; ++} ++ ++static inline void *fdt_offset_ptr_w_(void *fdt, int offset) ++{ ++ return (void *)(uintptr_t)fdt_offset_ptr_(fdt, offset); ++} ++ ++static inline const struct fdt_reserve_entry *fdt_mem_rsv_(const void *fdt, int n) ++{ ++ const struct fdt_reserve_entry *rsv_table = ++ (const struct fdt_reserve_entry *) ++ ((const char *)fdt + fdt_off_mem_rsvmap(fdt)); ++ ++ return rsv_table + n; ++} ++static inline struct fdt_reserve_entry *fdt_mem_rsv_w_(void *fdt, int n) ++{ ++ return (void *)(uintptr_t)fdt_mem_rsv_(fdt, n); ++} ++ ++/* ++ * Internal helpers to access tructural elements of the device tree ++ * blob (rather than for exaple reading integers from within property ++ * values). We assume that we are either given a naturally aligned ++ * address for the platform or if we are not, we are on a platform ++ * where unaligned memory reads will be handled in a graceful manner. ++ * If not the external helpers fdtXX_ld() from libfdt.h can be used ++ * instead. ++ */ ++static inline uint32_t fdt32_ld_(const fdt32_t *p) ++{ ++ return fdt32_to_cpu(*p); ++} ++ ++static inline uint64_t fdt64_ld_(const fdt64_t *p) ++{ ++ return fdt64_to_cpu(*p); ++} ++ ++#define FDT_SW_MAGIC (~FDT_MAGIC) ++ ++/**********************************************************************/ ++/* Checking controls */ ++/**********************************************************************/ ++ ++#ifndef FDT_ASSUME_MASK ++#define FDT_ASSUME_MASK 0 ++#endif ++ ++/* ++ * Defines assumptions which can be enabled. Each of these can be enabled ++ * individually. For maximum safety, don't enable any assumptions! ++ * ++ * For minimal code size and no safety, use ASSUME_PERFECT at your own risk. ++ * You should have another method of validating the device tree, such as a ++ * signature or hash check before using libfdt. ++ * ++ * For situations where security is not a concern it may be safe to enable ++ * ASSUME_SANE. ++ */ ++enum { ++ /* ++ * This does essentially no checks. Only the latest device-tree ++ * version is correctly handled. Inconsistencies or errors in the device ++ * tree may cause undefined behaviour or crashes. Invalid parameters ++ * passed to libfdt may do the same. ++ * ++ * If an error occurs when modifying the tree it may leave the tree in ++ * an intermediate (but valid) state. As an example, adding a property ++ * where there is insufficient space may result in the property name ++ * being added to the string table even though the property itself is ++ * not added to the struct section. ++ * ++ * Only use this if you have a fully validated device tree with ++ * the latest supported version and wish to minimise code size. ++ */ ++ ASSUME_PERFECT = 0xff, ++ ++ /* ++ * This assumes that the device tree is sane. i.e. header metadata ++ * and basic hierarchy are correct. ++ * ++ * With this assumption enabled, normal device trees produced by libfdt ++ * and the compiler should be handled safely. Malicious device trees and ++ * complete garbage may cause libfdt to behave badly or crash. Truncated ++ * device trees (e.g. those only partially loaded) can also cause ++ * problems. ++ * ++ * Note: Only checks that relate exclusively to the device tree itself ++ * (not the parameters passed to libfdt) are disabled by this ++ * assumption. This includes checking headers, tags and the like. ++ */ ++ ASSUME_VALID_DTB = 1 << 0, ++ ++ /* ++ * This builds on ASSUME_VALID_DTB and further assumes that libfdt ++ * functions are called with valid parameters, i.e. not trigger ++ * FDT_ERR_BADOFFSET or offsets that are out of bounds. It disables any ++ * extensive checking of parameters and the device tree, making various ++ * assumptions about correctness. ++ * ++ * It doesn't make sense to enable this assumption unless ++ * ASSUME_VALID_DTB is also enabled. ++ */ ++ ASSUME_VALID_INPUT = 1 << 1, ++ ++ /* ++ * This disables checks for device-tree version and removes all code ++ * which handles older versions. ++ * ++ * Only enable this if you know you have a device tree with the latest ++ * version. ++ */ ++ ASSUME_LATEST = 1 << 2, ++ ++ /* ++ * This assumes that it is OK for a failed addition to the device tree, ++ * due to lack of space or some other problem, to skip any rollback ++ * steps (such as dropping the property name from the string table). ++ * This is safe to enable in most circumstances, even though it may ++ * leave the tree in a sub-optimal state. ++ */ ++ ASSUME_NO_ROLLBACK = 1 << 3, ++ ++ /* ++ * This assumes that the device tree components appear in a 'convenient' ++ * order, i.e. the memory reservation block first, then the structure ++ * block and finally the string block. ++ * ++ * This order is not specified by the device-tree specification, ++ * but is expected by libfdt. The device-tree compiler always created ++ * device trees with this order. ++ * ++ * This assumption disables a check in fdt_open_into() and removes the ++ * ability to fix the problem there. This is safe if you know that the ++ * device tree is correctly ordered. See fdt_blocks_misordered_(). ++ */ ++ ASSUME_LIBFDT_ORDER = 1 << 4, ++ ++ /* ++ * This assumes that libfdt itself does not have any internal bugs. It ++ * drops certain checks that should never be needed unless libfdt has an ++ * undiscovered bug. ++ * ++ * This can generally be considered safe to enable. ++ */ ++ ASSUME_LIBFDT_FLAWLESS = 1 << 5, ++}; ++ ++/** ++ * can_assume_() - check if a particular assumption is enabled ++ * ++ * @mask: Mask to check (ASSUME_...) ++ * @return true if that assumption is enabled, else false ++ */ ++static inline bool can_assume_(int mask) ++{ ++ return FDT_ASSUME_MASK & mask; ++} ++ ++/** helper macros for checking assumptions */ ++#define can_assume(_assume) can_assume_(ASSUME_ ## _assume) ++ ++#endif /* LIBFDT_INTERNAL_H */ +diff --git a/include/fdt.h b/include/fdt.h +new file mode 100644 +index 0000000..f2e6880 +--- /dev/null ++++ b/include/fdt.h +@@ -0,0 +1,66 @@ ++/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ ++#ifndef FDT_H ++#define FDT_H ++/* ++ * libfdt - Flat Device Tree manipulation ++ * Copyright (C) 2006 David Gibson, IBM Corporation. ++ * Copyright 2012 Kim Phillips, Freescale Semiconductor. ++ */ ++ ++#ifndef __ASSEMBLY__ ++ ++struct fdt_header { ++ fdt32_t magic; /* magic word FDT_MAGIC */ ++ fdt32_t totalsize; /* total size of DT block */ ++ fdt32_t off_dt_struct; /* offset to structure */ ++ fdt32_t off_dt_strings; /* offset to strings */ ++ fdt32_t off_mem_rsvmap; /* offset to memory reserve map */ ++ fdt32_t version; /* format version */ ++ fdt32_t last_comp_version; /* last compatible version */ ++ ++ /* version 2 fields below */ ++ fdt32_t boot_cpuid_phys; /* Which physical CPU id we're ++ booting on */ ++ /* version 3 fields below */ ++ fdt32_t size_dt_strings; /* size of the strings block */ ++ ++ /* version 17 fields below */ ++ fdt32_t size_dt_struct; /* size of the structure block */ ++}; ++ ++struct fdt_reserve_entry { ++ fdt64_t address; ++ fdt64_t size; ++}; ++ ++struct fdt_node_header { ++ fdt32_t tag; ++ char name[0]; ++}; ++ ++struct fdt_property { ++ fdt32_t tag; ++ fdt32_t len; ++ fdt32_t nameoff; ++ char data[0]; ++}; ++ ++#endif /* !__ASSEMBLY */ ++ ++#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */ ++#define FDT_TAGSIZE sizeof(fdt32_t) ++ ++#define FDT_BEGIN_NODE 0x1 /* Start node: full name */ ++#define FDT_END_NODE 0x2 /* End node */ ++#define FDT_PROP 0x3 /* Property: name off, ++ size, content */ ++#define FDT_NOP 0x4 /* nop */ ++#define FDT_END 0x9 ++ ++#define FDT_V1_SIZE (7*sizeof(fdt32_t)) ++#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t)) ++#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t)) ++#define FDT_V16_SIZE FDT_V3_SIZE ++#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t)) ++ ++#endif /* FDT_H */ +diff --git a/include/libfdt.h b/include/libfdt.h +new file mode 100644 +index 0000000..a7f432c +--- /dev/null ++++ b/include/libfdt.h +@@ -0,0 +1,2147 @@ ++/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ ++#ifndef LIBFDT_H ++#define LIBFDT_H ++/* ++ * libfdt - Flat Device Tree manipulation ++ * Copyright (C) 2006 David Gibson, IBM Corporation. ++ */ ++ ++#include <libfdt_env.h> ++#include <fdt.h> ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define FDT_FIRST_SUPPORTED_VERSION 0x02 ++#define FDT_LAST_COMPATIBLE_VERSION 0x10 ++#define FDT_LAST_SUPPORTED_VERSION 0x11 ++ ++/* Error codes: informative error codes */ ++#define FDT_ERR_NOTFOUND 1 ++ /* FDT_ERR_NOTFOUND: The requested node or property does not exist */ ++#define FDT_ERR_EXISTS 2 ++ /* FDT_ERR_EXISTS: Attempted to create a node or property which ++ * already exists */ ++#define FDT_ERR_NOSPACE 3 ++ /* FDT_ERR_NOSPACE: Operation needed to expand the device ++ * tree, but its buffer did not have sufficient space to ++ * contain the expanded tree. Use fdt_open_into() to move the ++ * device tree to a buffer with more space. */ ++ ++/* Error codes: codes for bad parameters */ ++#define FDT_ERR_BADOFFSET 4 ++ /* FDT_ERR_BADOFFSET: Function was passed a structure block ++ * offset which is out-of-bounds, or which points to an ++ * unsuitable part of the structure for the operation. */ ++#define FDT_ERR_BADPATH 5 ++ /* FDT_ERR_BADPATH: Function was passed a badly formatted path ++ * (e.g. missing a leading / for a function which requires an ++ * absolute path) */ ++#define FDT_ERR_BADPHANDLE 6 ++ /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle. ++ * This can be caused either by an invalid phandle property ++ * length, or the phandle value was either 0 or -1, which are ++ * not permitted. */ ++#define FDT_ERR_BADSTATE 7 ++ /* FDT_ERR_BADSTATE: Function was passed an incomplete device ++ * tree created by the sequential-write functions, which is ++ * not sufficiently complete for the requested operation. */ ++ ++/* Error codes: codes for bad device tree blobs */ ++#define FDT_ERR_TRUNCATED 8 ++ /* FDT_ERR_TRUNCATED: FDT or a sub-block is improperly ++ * terminated (overflows, goes outside allowed bounds, or ++ * isn't properly terminated). */ ++#define FDT_ERR_BADMAGIC 9 ++ /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a ++ * device tree at all - it is missing the flattened device ++ * tree magic number. */ ++#define FDT_ERR_BADVERSION 10 ++ /* FDT_ERR_BADVERSION: Given device tree has a version which ++ * can't be handled by the requested operation. For ++ * read-write functions, this may mean that fdt_open_into() is ++ * required to convert the tree to the expected version. */ ++#define FDT_ERR_BADSTRUCTURE 11 ++ /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt ++ * structure block or other serious error (e.g. misnested ++ * nodes, or subnodes preceding properties). */ ++#define FDT_ERR_BADLAYOUT 12 ++ /* FDT_ERR_BADLAYOUT: For read-write functions, the given ++ * device tree has it's sub-blocks in an order that the ++ * function can't handle (memory reserve map, then structure, ++ * then strings). Use fdt_open_into() to reorganize the tree ++ * into a form suitable for the read-write operations. */ ++ ++/* "Can't happen" error indicating a bug in libfdt */ ++#define FDT_ERR_INTERNAL 13 ++ /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion. ++ * Should never be returned, if it is, it indicates a bug in ++ * libfdt itself. */ ++ ++/* Errors in device tree content */ ++#define FDT_ERR_BADNCELLS 14 ++ /* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells ++ * or similar property with a bad format or value */ ++ ++#define FDT_ERR_BADVALUE 15 ++ /* FDT_ERR_BADVALUE: Device tree has a property with an unexpected ++ * value. For example: a property expected to contain a string list ++ * is not NUL-terminated within the length of its value. */ ++ ++#define FDT_ERR_BADOVERLAY 16 ++ /* FDT_ERR_BADOVERLAY: The device tree overlay, while ++ * correctly structured, cannot be applied due to some ++ * unexpected or missing value, property or node. */ ++ ++#define FDT_ERR_NOPHANDLES 17 ++ /* FDT_ERR_NOPHANDLES: The device tree doesn't have any ++ * phandle available anymore without causing an overflow */ ++ ++#define FDT_ERR_BADFLAGS 18 ++ /* FDT_ERR_BADFLAGS: The function was passed a flags field that ++ * contains invalid flags or an invalid combination of flags. */ ++ ++#define FDT_ERR_ALIGNMENT 19 ++ /* FDT_ERR_ALIGNMENT: The device tree base address is not 8-byte ++ * aligned. */ ++ ++#define FDT_ERR_MAX 19 ++ ++/* constants */ ++#define FDT_MAX_PHANDLE 0xfffffffe ++ /* Valid values for phandles range from 1 to 2^32-2. */ ++ ++/**********************************************************************/ ++/* Low-level functions (you probably don't need these) */ ++/**********************************************************************/ ++ ++#ifndef SWIG /* This function is not useful in Python */ ++const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen); ++#endif ++static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen) ++{ ++ return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen); ++} ++ ++uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset); ++ ++/* ++ * External helpers to access words from a device tree blob. They're built ++ * to work even with unaligned pointers on platforms (such as ARMv5) that don't ++ * like unaligned loads and stores. ++ */ ++static inline uint16_t fdt16_ld(const fdt16_t *p) ++{ ++ const uint8_t *bp = (const uint8_t *)p; ++ ++ return ((uint16_t)bp[0] << 8) | bp[1]; ++} ++ ++static inline uint32_t fdt32_ld(const fdt32_t *p) ++{ ++ const uint8_t *bp = (const uint8_t *)p; ++ ++ return ((uint32_t)bp[0] << 24) ++ | ((uint32_t)bp[1] << 16) ++ | ((uint32_t)bp[2] << 8) ++ | bp[3]; ++} ++ ++static inline void fdt32_st(void *property, uint32_t value) ++{ ++ uint8_t *bp = (uint8_t *)property; ++ ++ bp[0] = value >> 24; ++ bp[1] = (value >> 16) & 0xff; ++ bp[2] = (value >> 8) & 0xff; ++ bp[3] = value & 0xff; ++} ++ ++static inline uint64_t fdt64_ld(const fdt64_t *p) ++{ ++ const uint8_t *bp = (const uint8_t *)p; ++ ++ return ((uint64_t)bp[0] << 56) ++ | ((uint64_t)bp[1] << 48) ++ | ((uint64_t)bp[2] << 40) ++ | ((uint64_t)bp[3] << 32) ++ | ((uint64_t)bp[4] << 24) ++ | ((uint64_t)bp[5] << 16) ++ | ((uint64_t)bp[6] << 8) ++ | bp[7]; ++} ++ ++static inline void fdt64_st(void *property, uint64_t value) ++{ ++ uint8_t *bp = (uint8_t *)property; ++ ++ bp[0] = value >> 56; ++ bp[1] = (value >> 48) & 0xff; ++ bp[2] = (value >> 40) & 0xff; ++ bp[3] = (value >> 32) & 0xff; ++ bp[4] = (value >> 24) & 0xff; ++ bp[5] = (value >> 16) & 0xff; ++ bp[6] = (value >> 8) & 0xff; ++ bp[7] = value & 0xff; ++} ++ ++/**********************************************************************/ ++/* Traversal functions */ ++/**********************************************************************/ ++ ++int fdt_next_node(const void *fdt, int offset, int *depth); ++ ++/** ++ * fdt_first_subnode() - get offset of first direct subnode ++ * @fdt: FDT blob ++ * @offset: Offset of node to check ++ * ++ * Return: offset of first subnode, or -FDT_ERR_NOTFOUND if there is none ++ */ ++int fdt_first_subnode(const void *fdt, int offset); ++ ++/** ++ * fdt_next_subnode() - get offset of next direct subnode ++ * @fdt: FDT blob ++ * @offset: Offset of previous subnode ++ * ++ * After first calling fdt_first_subnode(), call this function repeatedly to ++ * get direct subnodes of a parent node. ++ * ++ * Return: offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more ++ * subnodes ++ */ ++int fdt_next_subnode(const void *fdt, int offset); ++ ++/** ++ * fdt_for_each_subnode - iterate over all subnodes of a parent ++ * ++ * @node: child node (int, lvalue) ++ * @fdt: FDT blob (const void *) ++ * @parent: parent node (int) ++ * ++ * This is actually a wrapper around a for loop and would be used like so: ++ * ++ * fdt_for_each_subnode(node, fdt, parent) { ++ * Use node ++ * ... ++ * } ++ * ++ * if ((node < 0) && (node != -FDT_ERR_NOTFOUND)) { ++ * Error handling ++ * } ++ * ++ * Note that this is implemented as a macro and @node is used as ++ * iterator in the loop. The parent variable be constant or even a ++ * literal. ++ */ ++#define fdt_for_each_subnode(node, fdt, parent) \ ++ for (node = fdt_first_subnode(fdt, parent); \ ++ node >= 0; \ ++ node = fdt_next_subnode(fdt, node)) ++ ++/**********************************************************************/ ++/* General functions */ ++/**********************************************************************/ ++#define fdt_get_header(fdt, field) \ ++ (fdt32_ld(&((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_hdr_(name) \ ++ static inline void fdt_set_##name(void *fdt, uint32_t val) \ ++ { \ ++ struct fdt_header *fdth = (struct fdt_header *)fdt; \ ++ fdth->name = cpu_to_fdt32(val); \ ++ } ++fdt_set_hdr_(magic); ++fdt_set_hdr_(totalsize); ++fdt_set_hdr_(off_dt_struct); ++fdt_set_hdr_(off_dt_strings); ++fdt_set_hdr_(off_mem_rsvmap); ++fdt_set_hdr_(version); ++fdt_set_hdr_(last_comp_version); ++fdt_set_hdr_(boot_cpuid_phys); ++fdt_set_hdr_(size_dt_strings); ++fdt_set_hdr_(size_dt_struct); ++#undef fdt_set_hdr_ ++ ++/** ++ * fdt_header_size - return the size of the tree's header ++ * @fdt: pointer to a flattened device tree ++ * ++ * Return: size of DTB header in bytes ++ */ ++size_t fdt_header_size(const void *fdt); ++ ++/** ++ * fdt_header_size_ - internal function to get header size from a version number ++ * @version: devicetree version number ++ * ++ * Return: size of DTB header in bytes ++ */ ++size_t fdt_header_size_(uint32_t version); ++ ++/** ++ * fdt_check_header - sanity check a device tree header ++ * @fdt: pointer to data which might be a flattened device tree ++ * ++ * fdt_check_header() checks that the given buffer contains what ++ * appears to be a flattened device tree, and that the header contains ++ * valid information (to the extent that can be determined from the ++ * header alone). ++ * ++ * returns: ++ * 0, if the buffer appears to contain a valid device tree ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_TRUNCATED, standard meanings, as above ++ */ ++int fdt_check_header(const void *fdt); ++ ++/** ++ * fdt_move - move a device tree around in memory ++ * @fdt: pointer to the device tree to move ++ * @buf: pointer to memory where the device is to be moved ++ * @bufsize: size of the memory space at buf ++ * ++ * fdt_move() relocates, if possible, the device tree blob located at ++ * fdt to the buffer at buf of size bufsize. The buffer may overlap ++ * with the existing device tree blob at fdt. Therefore, ++ * fdt_move(fdt, fdt, fdt_totalsize(fdt)) ++ * should always succeed. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, standard meanings ++ */ ++int fdt_move(const void *fdt, void *buf, int bufsize); ++ ++/**********************************************************************/ ++/* Read-only functions */ ++/**********************************************************************/ ++ ++int fdt_check_full(const void *fdt, size_t bufsize); ++ ++/** ++ * fdt_get_string - retrieve a string from the strings block of a device tree ++ * @fdt: pointer to the device tree blob ++ * @stroffset: offset of the string within the strings block (native endian) ++ * @lenp: optional pointer to return the string's length ++ * ++ * fdt_get_string() retrieves a pointer to a single string from the ++ * strings block of the device tree blob at fdt, and optionally also ++ * returns the string's length in *lenp. ++ * ++ * returns: ++ * a pointer to the string, on success ++ * NULL, if stroffset is out of bounds, or doesn't point to a valid string ++ */ ++const char *fdt_get_string(const void *fdt, int stroffset, int *lenp); ++ ++/** ++ * fdt_string - retrieve a string from the strings block of a device tree ++ * @fdt: pointer to the device tree blob ++ * @stroffset: offset of the string within the strings block (native endian) ++ * ++ * fdt_string() retrieves a pointer to a single string from the ++ * strings block of the device tree blob at fdt. ++ * ++ * returns: ++ * a pointer to the string, on success ++ * NULL, if stroffset is out of bounds, or doesn't point to a valid string ++ */ ++const char *fdt_string(const void *fdt, int stroffset); ++ ++/** ++ * fdt_find_max_phandle - find and return the highest phandle in a tree ++ * @fdt: pointer to the device tree blob ++ * @phandle: return location for the highest phandle value found in the tree ++ * ++ * fdt_find_max_phandle() finds the highest phandle value in the given device ++ * tree. The value returned in @phandle is only valid if the function returns ++ * success. ++ * ++ * returns: ++ * 0 on success or a negative error code on failure ++ */ ++int fdt_find_max_phandle(const void *fdt, uint32_t *phandle); ++ ++/** ++ * fdt_get_max_phandle - retrieves the highest phandle in a tree ++ * @fdt: pointer to the device tree blob ++ * ++ * fdt_get_max_phandle retrieves the highest phandle in the given ++ * device tree. This will ignore badly formatted phandles, or phandles ++ * with a value of 0 or -1. ++ * ++ * This function is deprecated in favour of fdt_find_max_phandle(). ++ * ++ * returns: ++ * the highest phandle on success ++ * 0, if no phandle was found in the device tree ++ * -1, if an error occurred ++ */ ++static inline uint32_t fdt_get_max_phandle(const void *fdt) ++{ ++ uint32_t phandle; ++ int err; ++ ++ err = fdt_find_max_phandle(fdt, &phandle); ++ if (err < 0) ++ return (uint32_t)-1; ++ ++ return phandle; ++} ++ ++/** ++ * fdt_generate_phandle - return a new, unused phandle for a device tree blob ++ * @fdt: pointer to the device tree blob ++ * @phandle: return location for the new phandle ++ * ++ * Walks the device tree blob and looks for the highest phandle value. On ++ * success, the new, unused phandle value (one higher than the previously ++ * highest phandle value in the device tree blob) will be returned in the ++ * @phandle parameter. ++ * ++ * Return: 0 on success or a negative error-code on failure ++ */ ++int fdt_generate_phandle(const void *fdt, uint32_t *phandle); ++ ++/** ++ * fdt_num_mem_rsv - retrieve the number of memory reserve map entries ++ * @fdt: pointer to the device tree blob ++ * ++ * Returns the number of entries in the device tree blob's memory ++ * reservation map. This does not include the terminating 0,0 entry ++ * or any other (0,0) entries reserved for expansion. ++ * ++ * returns: ++ * the number of entries ++ */ ++int fdt_num_mem_rsv(const void *fdt); ++ ++/** ++ * fdt_get_mem_rsv - retrieve one memory reserve map entry ++ * @fdt: pointer to the device tree blob ++ * @n: index of reserve map entry ++ * @address: pointer to 64-bit variable to hold the start address ++ * @size: pointer to 64-bit variable to hold the size of the entry ++ * ++ * On success, @address and @size will contain the address and size of ++ * the n-th reserve map entry from the device tree blob, in ++ * native-endian format. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, standard meanings ++ */ ++int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size); ++ ++/** ++ * fdt_subnode_offset_namelen - find a subnode based on substring ++ * @fdt: pointer to the device tree blob ++ * @parentoffset: structure block offset of a node ++ * @name: name of the subnode to locate ++ * @namelen: number of characters of name to consider ++ * ++ * Identical to fdt_subnode_offset(), but only examine the first ++ * namelen characters of name for matching the subnode name. This is ++ * useful for finding subnodes based on a portion of a larger string, ++ * such as a full path. ++ * ++ * Return: offset of the subnode or -FDT_ERR_NOTFOUND if name not found. ++ */ ++#ifndef SWIG /* Not available in Python */ ++int fdt_subnode_offset_namelen(const void *fdt, int parentoffset, ++ const char *name, int namelen); ++#endif ++/** ++ * fdt_subnode_offset - find a subnode of a given node ++ * @fdt: pointer to the device tree blob ++ * @parentoffset: structure block offset of a node ++ * @name: name of the subnode to locate ++ * ++ * fdt_subnode_offset() finds a subnode of the node at structure block ++ * offset parentoffset with the given name. name may include a unit ++ * address, in which case fdt_subnode_offset() will find the subnode ++ * with that unit address, or the unit address may be omitted, in ++ * which case fdt_subnode_offset() will find an arbitrary subnode ++ * whose name excluding unit address matches the given name. ++ * ++ * returns: ++ * structure block offset of the requested subnode (>=0), on success ++ * -FDT_ERR_NOTFOUND, if the requested subnode does not exist ++ * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE ++ * tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_TRUNCATED, standard meanings. ++ */ ++int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name); ++ ++/** ++ * fdt_path_offset_namelen - find a tree node by its full path ++ * @fdt: pointer to the device tree blob ++ * @path: full path of the node to locate ++ * @namelen: number of characters of path to consider ++ * ++ * Identical to fdt_path_offset(), but only consider the first namelen ++ * characters of path as the path name. ++ * ++ * Return: offset of the node or negative libfdt error value otherwise ++ */ ++#ifndef SWIG /* Not available in Python */ ++int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen); ++#endif ++ ++/** ++ * fdt_path_offset - find a tree node by its full path ++ * @fdt: pointer to the device tree blob ++ * @path: full path of the node to locate ++ * ++ * fdt_path_offset() finds a node of a given path in the device tree. ++ * Each path component may omit the unit address portion, but the ++ * results of this are undefined if any such path component is ++ * ambiguous (that is if there are multiple nodes at the relevant ++ * level matching the given component, differentiated only by unit ++ * address). ++ * ++ * returns: ++ * structure block offset of the node with the requested path (>=0), on ++ * success ++ * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid ++ * -FDT_ERR_NOTFOUND, if the requested node does not exist ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_TRUNCATED, standard meanings. ++ */ ++int fdt_path_offset(const void *fdt, const char *path); ++ ++/** ++ * fdt_get_name - retrieve the name of a given node ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: structure block offset of the starting node ++ * @lenp: pointer to an integer variable (will be overwritten) or NULL ++ * ++ * fdt_get_name() retrieves the name (including unit address) of the ++ * device tree node at structure block offset nodeoffset. If lenp is ++ * non-NULL, the length of this name is also returned, in the integer ++ * pointed to by lenp. ++ * ++ * returns: ++ * pointer to the node's name, on success ++ * If lenp is non-NULL, *lenp contains the length of that name ++ * (>=0) ++ * NULL, on error ++ * if lenp is non-NULL *lenp contains an error code (<0): ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE ++ * tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, standard meanings ++ */ ++const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp); ++ ++/** ++ * fdt_first_property_offset - find the offset of a node's first property ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: structure block offset of a node ++ * ++ * fdt_first_property_offset() finds the first property of the node at ++ * the given structure block offset. ++ * ++ * returns: ++ * structure block offset of the property (>=0), on success ++ * -FDT_ERR_NOTFOUND, if the requested node has no properties ++ * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_TRUNCATED, standard meanings. ++ */ ++int fdt_first_property_offset(const void *fdt, int nodeoffset); ++ ++/** ++ * fdt_next_property_offset - step through a node's properties ++ * @fdt: pointer to the device tree blob ++ * @offset: structure block offset of a property ++ * ++ * fdt_next_property_offset() finds the property immediately after the ++ * one at the given structure block offset. This will be a property ++ * of the same node as the given property. ++ * ++ * returns: ++ * structure block offset of the next property (>=0), on success ++ * -FDT_ERR_NOTFOUND, if the given property is the last in its node ++ * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_TRUNCATED, standard meanings. ++ */ ++int fdt_next_property_offset(const void *fdt, int offset); ++ ++/** ++ * fdt_for_each_property_offset - iterate over all properties of a node ++ * ++ * @property: property offset (int, lvalue) ++ * @fdt: FDT blob (const void *) ++ * @node: node offset (int) ++ * ++ * This is actually a wrapper around a for loop and would be used like so: ++ * ++ * fdt_for_each_property_offset(property, fdt, node) { ++ * Use property ++ * ... ++ * } ++ * ++ * if ((property < 0) && (property != -FDT_ERR_NOTFOUND)) { ++ * Error handling ++ * } ++ * ++ * Note that this is implemented as a macro and property is used as ++ * iterator in the loop. The node variable can be constant or even a ++ * literal. ++ */ ++#define fdt_for_each_property_offset(property, fdt, node) \ ++ for (property = fdt_first_property_offset(fdt, node); \ ++ property >= 0; \ ++ property = fdt_next_property_offset(fdt, property)) ++ ++/** ++ * fdt_get_property_by_offset - retrieve the property at a given offset ++ * @fdt: pointer to the device tree blob ++ * @offset: offset of the property to retrieve ++ * @lenp: pointer to an integer variable (will be overwritten) or NULL ++ * ++ * fdt_get_property_by_offset() retrieves a pointer to the ++ * fdt_property structure within the device tree blob at the given ++ * offset. If lenp is non-NULL, the length of the property value is ++ * also returned, in the integer pointed to by lenp. ++ * ++ * Note that this code only works on device tree versions >= 16. fdt_getprop() ++ * works on all versions. ++ * ++ * returns: ++ * pointer to the structure representing the property ++ * if lenp is non-NULL, *lenp contains the length of the property ++ * value (>=0) ++ * NULL, on error ++ * if lenp is non-NULL, *lenp contains an error code (<0): ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++const struct fdt_property *fdt_get_property_by_offset(const void *fdt, ++ int offset, ++ int *lenp); ++ ++/** ++ * fdt_get_property_namelen - find a property based on substring ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to find ++ * @name: name of the property to find ++ * @namelen: number of characters of name to consider ++ * @lenp: pointer to an integer variable (will be overwritten) or NULL ++ * ++ * Identical to fdt_get_property(), but only examine the first namelen ++ * characters of name for matching the property name. ++ * ++ * Return: pointer to the structure representing the property, or NULL ++ * if not found ++ */ ++#ifndef SWIG /* Not available in Python */ ++const struct fdt_property *fdt_get_property_namelen(const void *fdt, ++ int nodeoffset, ++ const char *name, ++ int namelen, int *lenp); ++#endif ++ ++/** ++ * fdt_get_property - find a given property in a given node ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to find ++ * @name: name of the property to find ++ * @lenp: pointer to an integer variable (will be overwritten) or NULL ++ * ++ * fdt_get_property() retrieves a pointer to the fdt_property ++ * structure within the device tree blob corresponding to the property ++ * named 'name' of the node at offset nodeoffset. If lenp is ++ * non-NULL, the length of the property value is also returned, in the ++ * integer pointed to by lenp. ++ * ++ * returns: ++ * pointer to the structure representing the property ++ * if lenp is non-NULL, *lenp contains the length of the property ++ * value (>=0) ++ * NULL, on error ++ * if lenp is non-NULL, *lenp contains an error code (<0): ++ * -FDT_ERR_NOTFOUND, node does not have named property ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE ++ * tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, ++ const char *name, int *lenp); ++static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset, ++ const char *name, ++ int *lenp) ++{ ++ return (struct fdt_property *)(uintptr_t) ++ fdt_get_property(fdt, nodeoffset, name, lenp); ++} ++ ++/** ++ * fdt_getprop_by_offset - retrieve the value of a property at a given offset ++ * @fdt: pointer to the device tree blob ++ * @offset: offset of the property to read ++ * @namep: pointer to a string variable (will be overwritten) or NULL ++ * @lenp: pointer to an integer variable (will be overwritten) or NULL ++ * ++ * fdt_getprop_by_offset() retrieves a pointer to the value of the ++ * property at structure block offset 'offset' (this will be a pointer ++ * to within the device blob itself, not a copy of the value). If ++ * lenp is non-NULL, the length of the property value is also ++ * returned, in the integer pointed to by lenp. If namep is non-NULL, ++ * the property's namne will also be returned in the char * pointed to ++ * by namep (this will be a pointer to within the device tree's string ++ * block, not a new copy of the name). ++ * ++ * returns: ++ * pointer to the property's value ++ * if lenp is non-NULL, *lenp contains the length of the property ++ * value (>=0) ++ * if namep is non-NULL *namep contiains a pointer to the property ++ * name. ++ * NULL, on error ++ * if lenp is non-NULL, *lenp contains an error code (<0): ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++#ifndef SWIG /* This function is not useful in Python */ ++const void *fdt_getprop_by_offset(const void *fdt, int offset, ++ const char **namep, int *lenp); ++#endif ++ ++/** ++ * fdt_getprop_namelen - get property value based on substring ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to find ++ * @name: name of the property to find ++ * @namelen: number of characters of name to consider ++ * @lenp: pointer to an integer variable (will be overwritten) or NULL ++ * ++ * Identical to fdt_getprop(), but only examine the first namelen ++ * characters of name for matching the property name. ++ * ++ * Return: pointer to the property's value or NULL on error ++ */ ++#ifndef SWIG /* Not available in Python */ ++const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, ++ const char *name, int namelen, int *lenp); ++static inline void *fdt_getprop_namelen_w(void *fdt, int nodeoffset, ++ const char *name, int namelen, ++ int *lenp) ++{ ++ return (void *)(uintptr_t)fdt_getprop_namelen(fdt, nodeoffset, name, ++ namelen, lenp); ++} ++#endif ++ ++/** ++ * fdt_getprop - retrieve the value of a given property ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to find ++ * @name: name of the property to find ++ * @lenp: pointer to an integer variable (will be overwritten) or NULL ++ * ++ * fdt_getprop() retrieves a pointer to the value of the property ++ * named @name of the node at offset @nodeoffset (this will be a ++ * pointer to within the device blob itself, not a copy of the value). ++ * If @lenp is non-NULL, the length of the property value is also ++ * returned, in the integer pointed to by @lenp. ++ * ++ * returns: ++ * pointer to the property's value ++ * if lenp is non-NULL, *lenp contains the length of the property ++ * value (>=0) ++ * NULL, on error ++ * if lenp is non-NULL, *lenp contains an error code (<0): ++ * -FDT_ERR_NOTFOUND, node does not have named property ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE ++ * tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++const void *fdt_getprop(const void *fdt, int nodeoffset, ++ const char *name, int *lenp); ++static inline void *fdt_getprop_w(void *fdt, int nodeoffset, ++ const char *name, int *lenp) ++{ ++ return (void *)(uintptr_t)fdt_getprop(fdt, nodeoffset, name, lenp); ++} ++ ++/** ++ * fdt_get_phandle - retrieve the phandle of a given node ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: structure block offset of the node ++ * ++ * fdt_get_phandle() retrieves the phandle of the device tree node at ++ * structure block offset nodeoffset. ++ * ++ * returns: ++ * the phandle of the node at nodeoffset, on success (!= 0, != -1) ++ * 0, if the node has no phandle, or another error occurs ++ */ ++uint32_t fdt_get_phandle(const void *fdt, int nodeoffset); ++ ++/** ++ * fdt_get_alias_namelen - get alias based on substring ++ * @fdt: pointer to the device tree blob ++ * @name: name of the alias th look up ++ * @namelen: number of characters of name to consider ++ * ++ * Identical to fdt_get_alias(), but only examine the first @namelen ++ * characters of @name for matching the alias name. ++ * ++ * Return: a pointer to the expansion of the alias named @name, if it exists, ++ * NULL otherwise ++ */ ++#ifndef SWIG /* Not available in Python */ ++const char *fdt_get_alias_namelen(const void *fdt, ++ const char *name, int namelen); ++#endif ++ ++/** ++ * fdt_get_alias - retrieve the path referenced by a given alias ++ * @fdt: pointer to the device tree blob ++ * @name: name of the alias th look up ++ * ++ * fdt_get_alias() retrieves the value of a given alias. That is, the ++ * value of the property named @name in the node /aliases. ++ * ++ * returns: ++ * a pointer to the expansion of the alias named 'name', if it exists ++ * NULL, if the given alias or the /aliases node does not exist ++ */ ++const char *fdt_get_alias(const void *fdt, const char *name); ++ ++/** ++ * fdt_get_path - determine the full path of a node ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose path to find ++ * @buf: character buffer to contain the returned path (will be overwritten) ++ * @buflen: size of the character buffer at buf ++ * ++ * fdt_get_path() computes the full path of the node at offset ++ * nodeoffset, and records that path in the buffer at buf. ++ * ++ * NOTE: This function is expensive, as it must scan the device tree ++ * structure from the start to nodeoffset. ++ * ++ * returns: ++ * 0, on success ++ * buf contains the absolute path of the node at ++ * nodeoffset, as a NUL-terminated string. ++ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag ++ * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1) ++ * characters and will not fit in the given buffer. ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, standard meanings ++ */ ++int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen); ++ ++/** ++ * fdt_supernode_atdepth_offset - find a specific ancestor of a node ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose parent to find ++ * @supernodedepth: depth of the ancestor to find ++ * @nodedepth: pointer to an integer variable (will be overwritten) or NULL ++ * ++ * fdt_supernode_atdepth_offset() finds an ancestor of the given node ++ * at a specific depth from the root (where the root itself has depth ++ * 0, its immediate subnodes depth 1 and so forth). So ++ * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL); ++ * will always return 0, the offset of the root node. If the node at ++ * nodeoffset has depth D, then: ++ * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL); ++ * will return nodeoffset itself. ++ * ++ * NOTE: This function is expensive, as it must scan the device tree ++ * structure from the start to nodeoffset. ++ * ++ * returns: ++ * structure block offset of the node at node offset's ancestor ++ * of depth supernodedepth (>=0), on success ++ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag ++ * -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of ++ * nodeoffset ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, standard meanings ++ */ ++int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, ++ int supernodedepth, int *nodedepth); ++ ++/** ++ * fdt_node_depth - find the depth of a given node ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose parent to find ++ * ++ * fdt_node_depth() finds the depth of a given node. The root node ++ * has depth 0, its immediate subnodes depth 1 and so forth. ++ * ++ * NOTE: This function is expensive, as it must scan the device tree ++ * structure from the start to nodeoffset. ++ * ++ * returns: ++ * depth of the node at nodeoffset (>=0), on success ++ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, standard meanings ++ */ ++int fdt_node_depth(const void *fdt, int nodeoffset); ++ ++/** ++ * fdt_parent_offset - find the parent of a given node ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose parent to find ++ * ++ * fdt_parent_offset() locates the parent node of a given node (that ++ * is, it finds the offset of the node which contains the node at ++ * nodeoffset as a subnode). ++ * ++ * NOTE: This function is expensive, as it must scan the device tree ++ * structure from the start to nodeoffset, *twice*. ++ * ++ * returns: ++ * structure block offset of the parent of the node at nodeoffset ++ * (>=0), on success ++ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, standard meanings ++ */ ++int fdt_parent_offset(const void *fdt, int nodeoffset); ++ ++/** ++ * fdt_node_offset_by_prop_value - find nodes with a given property value ++ * @fdt: pointer to the device tree blob ++ * @startoffset: only find nodes after this offset ++ * @propname: property name to check ++ * @propval: property value to search for ++ * @proplen: length of the value in propval ++ * ++ * fdt_node_offset_by_prop_value() returns the offset of the first ++ * node after startoffset, which has a property named propname whose ++ * value is of length proplen and has value equal to propval; or if ++ * startoffset is -1, the very first such node in the tree. ++ * ++ * To iterate through all nodes matching the criterion, the following ++ * idiom can be used: ++ * offset = fdt_node_offset_by_prop_value(fdt, -1, propname, ++ * propval, proplen); ++ * while (offset != -FDT_ERR_NOTFOUND) { ++ * // other code here ++ * offset = fdt_node_offset_by_prop_value(fdt, offset, propname, ++ * propval, proplen); ++ * } ++ * ++ * Note the -1 in the first call to the function, if 0 is used here ++ * instead, the function will never locate the root node, even if it ++ * matches the criterion. ++ * ++ * returns: ++ * structure block offset of the located node (>= 0, >startoffset), ++ * on success ++ * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the ++ * tree after startoffset ++ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, standard meanings ++ */ ++int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, ++ const char *propname, ++ const void *propval, int proplen); ++ ++/** ++ * fdt_node_offset_by_phandle - find the node with a given phandle ++ * @fdt: pointer to the device tree blob ++ * @phandle: phandle value ++ * ++ * fdt_node_offset_by_phandle() returns the offset of the node ++ * which has the given phandle value. If there is more than one node ++ * in the tree with the given phandle (an invalid tree), results are ++ * undefined. ++ * ++ * returns: ++ * structure block offset of the located node (>= 0), on success ++ * -FDT_ERR_NOTFOUND, no node with that phandle exists ++ * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1) ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, standard meanings ++ */ ++int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle); ++ ++/** ++ * fdt_node_check_compatible - check a node's compatible property ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of a tree node ++ * @compatible: string to match against ++ * ++ * fdt_node_check_compatible() returns 0 if the given node contains a ++ * @compatible property with the given string as one of its elements, ++ * it returns non-zero otherwise, or on error. ++ * ++ * returns: ++ * 0, if the node has a 'compatible' property listing the given string ++ * 1, if the node has a 'compatible' property, but it does not list ++ * the given string ++ * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property ++ * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, standard meanings ++ */ ++int fdt_node_check_compatible(const void *fdt, int nodeoffset, ++ const char *compatible); ++ ++/** ++ * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value ++ * @fdt: pointer to the device tree blob ++ * @startoffset: only find nodes after this offset ++ * @compatible: 'compatible' string to match against ++ * ++ * fdt_node_offset_by_compatible() returns the offset of the first ++ * node after startoffset, which has a 'compatible' property which ++ * lists the given compatible string; or if startoffset is -1, the ++ * very first such node in the tree. ++ * ++ * To iterate through all nodes matching the criterion, the following ++ * idiom can be used: ++ * offset = fdt_node_offset_by_compatible(fdt, -1, compatible); ++ * while (offset != -FDT_ERR_NOTFOUND) { ++ * // other code here ++ * offset = fdt_node_offset_by_compatible(fdt, offset, compatible); ++ * } ++ * ++ * Note the -1 in the first call to the function, if 0 is used here ++ * instead, the function will never locate the root node, even if it ++ * matches the criterion. ++ * ++ * returns: ++ * structure block offset of the located node (>= 0, >startoffset), ++ * on success ++ * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the ++ * tree after startoffset ++ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, standard meanings ++ */ ++int fdt_node_offset_by_compatible(const void *fdt, int startoffset, ++ const char *compatible); ++ ++/** ++ * fdt_stringlist_contains - check a string list property for a string ++ * @strlist: Property containing a list of strings to check ++ * @listlen: Length of property ++ * @str: String to search for ++ * ++ * This is a utility function provided for convenience. The list contains ++ * one or more strings, each terminated by \0, as is found in a device tree ++ * "compatible" property. ++ * ++ * Return: 1 if the string is found in the list, 0 not found, or invalid list ++ */ ++int fdt_stringlist_contains(const char *strlist, int listlen, const char *str); ++ ++/** ++ * fdt_stringlist_count - count the number of strings in a string list ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of a tree node ++ * @property: name of the property containing the string list ++ * ++ * Return: ++ * the number of strings in the given property ++ * -FDT_ERR_BADVALUE if the property value is not NUL-terminated ++ * -FDT_ERR_NOTFOUND if the property does not exist ++ */ ++int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property); ++ ++/** ++ * fdt_stringlist_search - find a string in a string list and return its index ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of a tree node ++ * @property: name of the property containing the string list ++ * @string: string to look up in the string list ++ * ++ * Note that it is possible for this function to succeed on property values ++ * that are not NUL-terminated. That's because the function will stop after ++ * finding the first occurrence of @string. This can for example happen with ++ * small-valued cell properties, such as #address-cells, when searching for ++ * the empty string. ++ * ++ * return: ++ * the index of the string in the list of strings ++ * -FDT_ERR_BADVALUE if the property value is not NUL-terminated ++ * -FDT_ERR_NOTFOUND if the property does not exist or does not contain ++ * the given string ++ */ ++int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, ++ const char *string); ++ ++/** ++ * fdt_stringlist_get() - obtain the string at a given index in a string list ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of a tree node ++ * @property: name of the property containing the string list ++ * @index: index of the string to return ++ * @lenp: return location for the string length or an error code on failure ++ * ++ * Note that this will successfully extract strings from properties with ++ * non-NUL-terminated values. For example on small-valued cell properties ++ * this function will return the empty string. ++ * ++ * If non-NULL, the length of the string (on success) or a negative error-code ++ * (on failure) will be stored in the integer pointer to by lenp. ++ * ++ * Return: ++ * A pointer to the string at the given index in the string list or NULL on ++ * failure. On success the length of the string will be stored in the memory ++ * location pointed to by the lenp parameter, if non-NULL. On failure one of ++ * the following negative error codes will be returned in the lenp parameter ++ * (if non-NULL): ++ * -FDT_ERR_BADVALUE if the property value is not NUL-terminated ++ * -FDT_ERR_NOTFOUND if the property does not exist ++ */ ++const char *fdt_stringlist_get(const void *fdt, int nodeoffset, ++ const char *property, int index, ++ int *lenp); ++ ++/**********************************************************************/ ++/* Read-only functions (addressing related) */ ++/**********************************************************************/ ++ ++/** ++ * FDT_MAX_NCELLS - maximum value for #address-cells and #size-cells ++ * ++ * This is the maximum value for #address-cells, #size-cells and ++ * similar properties that will be processed by libfdt. IEE1275 ++ * requires that OF implementations handle values up to 4. ++ * Implementations may support larger values, but in practice higher ++ * values aren't used. ++ */ ++#define FDT_MAX_NCELLS 4 ++ ++/** ++ * fdt_address_cells - retrieve address size for a bus represented in the tree ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node to find the address size for ++ * ++ * When the node has a valid #address-cells property, returns its value. ++ * ++ * returns: ++ * 0 <= n < FDT_MAX_NCELLS, on success ++ * 2, if the node has no #address-cells property ++ * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid ++ * #address-cells property ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++int fdt_address_cells(const void *fdt, int nodeoffset); ++ ++/** ++ * fdt_size_cells - retrieve address range size for a bus represented in the ++ * tree ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node to find the address range size for ++ * ++ * When the node has a valid #size-cells property, returns its value. ++ * ++ * returns: ++ * 0 <= n < FDT_MAX_NCELLS, on success ++ * 1, if the node has no #size-cells property ++ * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid ++ * #size-cells property ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++int fdt_size_cells(const void *fdt, int nodeoffset); ++ ++ ++/**********************************************************************/ ++/* Write-in-place functions */ ++/**********************************************************************/ ++ ++/** ++ * fdt_setprop_inplace_namelen_partial - change a property's value, ++ * but not its size ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to change ++ * @name: name of the property to change ++ * @namelen: number of characters of name to consider ++ * @idx: index of the property to change in the array ++ * @val: pointer to data to replace the property value with ++ * @len: length of the property value ++ * ++ * Identical to fdt_setprop_inplace(), but modifies the given property ++ * starting from the given index, and using only the first characters ++ * of the name. It is useful when you want to manipulate only one value of ++ * an array and you have a string that doesn't end with \0. ++ * ++ * Return: 0 on success, negative libfdt error value otherwise ++ */ ++#ifndef SWIG /* Not available in Python */ ++int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, ++ const char *name, int namelen, ++ uint32_t idx, const void *val, ++ int len); ++#endif ++ ++/** ++ * fdt_setprop_inplace - change a property's value, but not its size ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to change ++ * @name: name of the property to change ++ * @val: pointer to data to replace the property value with ++ * @len: length of the property value ++ * ++ * fdt_setprop_inplace() replaces the value of a given property with ++ * the data in val, of length len. This function cannot change the ++ * size of a property, and so will only work if len is equal to the ++ * current length of the property. ++ * ++ * This function will alter only the bytes in the blob which contain ++ * the given property value, and will not alter or move any other part ++ * of the tree. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, if len is not equal to the property's current length ++ * -FDT_ERR_NOTFOUND, node does not have the named property ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++#ifndef SWIG /* Not available in Python */ ++int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, ++ const void *val, int len); ++#endif ++ ++/** ++ * fdt_setprop_inplace_u32 - change the value of a 32-bit integer property ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to change ++ * @name: name of the property to change ++ * @val: 32-bit integer value to replace the property with ++ * ++ * fdt_setprop_inplace_u32() replaces the value of a given property ++ * with the 32-bit integer value in val, converting val to big-endian ++ * if necessary. This function cannot change the size of a property, ++ * and so will only work if the property already exists and has length ++ * 4. ++ * ++ * This function will alter only the bytes in the blob which contain ++ * the given property value, and will not alter or move any other part ++ * of the tree. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, if the property's length is not equal to 4 ++ * -FDT_ERR_NOTFOUND, node does not have the named property ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++static inline int fdt_setprop_inplace_u32(void *fdt, int nodeoffset, ++ const char *name, uint32_t val) ++{ ++ fdt32_t tmp = cpu_to_fdt32(val); ++ return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); ++} ++ ++/** ++ * fdt_setprop_inplace_u64 - change the value of a 64-bit integer property ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to change ++ * @name: name of the property to change ++ * @val: 64-bit integer value to replace the property with ++ * ++ * fdt_setprop_inplace_u64() replaces the value of a given property ++ * with the 64-bit integer value in val, converting val to big-endian ++ * if necessary. This function cannot change the size of a property, ++ * and so will only work if the property already exists and has length ++ * 8. ++ * ++ * This function will alter only the bytes in the blob which contain ++ * the given property value, and will not alter or move any other part ++ * of the tree. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, if the property's length is not equal to 8 ++ * -FDT_ERR_NOTFOUND, node does not have the named property ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++static inline int fdt_setprop_inplace_u64(void *fdt, int nodeoffset, ++ const char *name, uint64_t val) ++{ ++ fdt64_t tmp = cpu_to_fdt64(val); ++ return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); ++} ++ ++/** ++ * fdt_setprop_inplace_cell - change the value of a single-cell property ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node containing the property ++ * @name: name of the property to change the value of ++ * @val: new value of the 32-bit cell ++ * ++ * This is an alternative name for fdt_setprop_inplace_u32() ++ * Return: 0 on success, negative libfdt error number otherwise. ++ */ ++static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset, ++ const char *name, uint32_t val) ++{ ++ return fdt_setprop_inplace_u32(fdt, nodeoffset, name, val); ++} ++ ++/** ++ * fdt_nop_property - replace a property with nop tags ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to nop ++ * @name: name of the property to nop ++ * ++ * fdt_nop_property() will replace a given property's representation ++ * in the blob with FDT_NOP tags, effectively removing it from the ++ * tree. ++ * ++ * This function will alter only the bytes in the blob which contain ++ * the property, and will not alter or move any other part of the ++ * tree. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOTFOUND, node does not have the named property ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++int fdt_nop_property(void *fdt, int nodeoffset, const char *name); ++ ++/** ++ * fdt_nop_node - replace a node (subtree) with nop tags ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node to nop ++ * ++ * fdt_nop_node() will replace a given node's representation in the ++ * blob, including all its subnodes, if any, with FDT_NOP tags, ++ * effectively removing it from the tree. ++ * ++ * This function will alter only the bytes in the blob which contain ++ * the node and its properties and subnodes, and will not alter or ++ * move any other part of the tree. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++int fdt_nop_node(void *fdt, int nodeoffset); ++ ++/**********************************************************************/ ++/* Sequential write functions */ ++/**********************************************************************/ ++ ++/* fdt_create_with_flags flags */ ++#define FDT_CREATE_FLAG_NO_NAME_DEDUP 0x1 ++ /* FDT_CREATE_FLAG_NO_NAME_DEDUP: Do not try to de-duplicate property ++ * names in the fdt. This can result in faster creation times, but ++ * a larger fdt. */ ++ ++#define FDT_CREATE_FLAGS_ALL (FDT_CREATE_FLAG_NO_NAME_DEDUP) ++ ++/** ++ * fdt_create_with_flags - begin creation of a new fdt ++ * @buf: pointer to memory allocated where fdt will be created ++ * @bufsize: size of the memory space at fdt ++ * @flags: a valid combination of FDT_CREATE_FLAG_ flags, or 0. ++ * ++ * fdt_create_with_flags() begins the process of creating a new fdt with ++ * the sequential write interface. ++ * ++ * fdt creation process must end with fdt_finished() to produce a valid fdt. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, bufsize is insufficient for a minimal fdt ++ * -FDT_ERR_BADFLAGS, flags is not valid ++ */ ++int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags); ++ ++/** ++ * fdt_create - begin creation of a new fdt ++ * @buf: pointer to memory allocated where fdt will be created ++ * @bufsize: size of the memory space at fdt ++ * ++ * fdt_create() is equivalent to fdt_create_with_flags() with flags=0. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, bufsize is insufficient for a minimal fdt ++ */ ++int fdt_create(void *buf, int bufsize); ++ ++int fdt_resize(void *fdt, void *buf, int bufsize); ++int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size); ++int fdt_finish_reservemap(void *fdt); ++int fdt_begin_node(void *fdt, const char *name); ++int fdt_property(void *fdt, const char *name, const void *val, int len); ++static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val) ++{ ++ fdt32_t tmp = cpu_to_fdt32(val); ++ return fdt_property(fdt, name, &tmp, sizeof(tmp)); ++} ++static inline int fdt_property_u64(void *fdt, const char *name, uint64_t val) ++{ ++ fdt64_t tmp = cpu_to_fdt64(val); ++ return fdt_property(fdt, name, &tmp, sizeof(tmp)); ++} ++ ++#ifndef SWIG /* Not available in Python */ ++static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val) ++{ ++ return fdt_property_u32(fdt, name, val); ++} ++#endif ++ ++/** ++ * fdt_property_placeholder - add a new property and return a ptr to its value ++ * ++ * @fdt: pointer to the device tree blob ++ * @name: name of property to add ++ * @len: length of property value in bytes ++ * @valp: returns a pointer to where where the value should be placed ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_NOSPACE, standard meanings ++ */ ++int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp); ++ ++#define fdt_property_string(fdt, name, str) \ ++ fdt_property(fdt, name, str, strlen(str)+1) ++int fdt_end_node(void *fdt); ++int fdt_finish(void *fdt); ++ ++/**********************************************************************/ ++/* Read-write functions */ ++/**********************************************************************/ ++ ++int fdt_create_empty_tree(void *buf, int bufsize); ++int fdt_open_into(const void *fdt, void *buf, int bufsize); ++int fdt_pack(void *fdt); ++ ++/** ++ * fdt_add_mem_rsv - add one memory reserve map entry ++ * @fdt: pointer to the device tree blob ++ * @address: 64-bit start address of the reserve map entry ++ * @size: 64-bit size of the reserved region ++ * ++ * Adds a reserve map entry to the given blob reserving a region at ++ * address address of length size. ++ * ++ * This function will insert data into the reserve map and will ++ * therefore change the indexes of some entries in the table. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to ++ * contain the new reservation entry ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size); ++ ++/** ++ * fdt_del_mem_rsv - remove a memory reserve map entry ++ * @fdt: pointer to the device tree blob ++ * @n: entry to remove ++ * ++ * fdt_del_mem_rsv() removes the n-th memory reserve map entry from ++ * the blob. ++ * ++ * This function will delete data from the reservation table and will ++ * therefore change the indexes of some entries in the table. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there ++ * are less than n+1 reserve map entries) ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++int fdt_del_mem_rsv(void *fdt, int n); ++ ++/** ++ * fdt_set_name - change the name of a given node ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: structure block offset of a node ++ * @name: name to give the node ++ * ++ * fdt_set_name() replaces the name (including unit address, if any) ++ * of the given node with the given string. NOTE: this function can't ++ * efficiently check if the new name is unique amongst the given ++ * node's siblings; results are undefined if this function is invoked ++ * with a name equal to one of the given node's siblings. ++ * ++ * This function may insert or delete data from the blob, and will ++ * therefore change the offsets of some existing nodes. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob ++ * to contain the new name ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, standard meanings ++ */ ++int fdt_set_name(void *fdt, int nodeoffset, const char *name); ++ ++/** ++ * fdt_setprop - create or change a property ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to change ++ * @name: name of the property to change ++ * @val: pointer to data to set the property value to ++ * @len: length of the property value ++ * ++ * fdt_setprop() sets the value of the named property in the given ++ * node to the given value and length, creating the property if it ++ * does not already exist. ++ * ++ * This function may insert or delete data from the blob, and will ++ * therefore change the offsets of some existing nodes. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to ++ * contain the new property value ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++int fdt_setprop(void *fdt, int nodeoffset, const char *name, ++ const void *val, int len); ++ ++/** ++ * fdt_setprop_placeholder - allocate space for a property ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to change ++ * @name: name of the property to change ++ * @len: length of the property value ++ * @prop_data: return pointer to property data ++ * ++ * fdt_setprop_placeholer() allocates the named property in the given node. ++ * If the property exists it is resized. In either case a pointer to the ++ * property data is returned. ++ * ++ * This function may insert or delete data from the blob, and will ++ * therefore change the offsets of some existing nodes. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to ++ * contain the new property value ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, ++ int len, void **prop_data); ++ ++/** ++ * fdt_setprop_u32 - set a property to a 32-bit integer ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to change ++ * @name: name of the property to change ++ * @val: 32-bit integer value for the property (native endian) ++ * ++ * fdt_setprop_u32() sets the value of the named property in the given ++ * node to the given 32-bit integer value (converting to big-endian if ++ * necessary), or creates a new property with that value if it does ++ * not already exist. ++ * ++ * This function may insert or delete data from the blob, and will ++ * therefore change the offsets of some existing nodes. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to ++ * contain the new property value ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++static inline int fdt_setprop_u32(void *fdt, int nodeoffset, const char *name, ++ uint32_t val) ++{ ++ fdt32_t tmp = cpu_to_fdt32(val); ++ return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); ++} ++ ++/** ++ * fdt_setprop_u64 - set a property to a 64-bit integer ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to change ++ * @name: name of the property to change ++ * @val: 64-bit integer value for the property (native endian) ++ * ++ * fdt_setprop_u64() sets the value of the named property in the given ++ * node to the given 64-bit integer value (converting to big-endian if ++ * necessary), or creates a new property with that value if it does ++ * not already exist. ++ * ++ * This function may insert or delete data from the blob, and will ++ * therefore change the offsets of some existing nodes. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to ++ * contain the new property value ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++static inline int fdt_setprop_u64(void *fdt, int nodeoffset, const char *name, ++ uint64_t val) ++{ ++ fdt64_t tmp = cpu_to_fdt64(val); ++ return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); ++} ++ ++/** ++ * fdt_setprop_cell - set a property to a single cell value ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to change ++ * @name: name of the property to change ++ * @val: 32-bit integer value for the property (native endian) ++ * ++ * This is an alternative name for fdt_setprop_u32() ++ * ++ * Return: 0 on success, negative libfdt error value otherwise. ++ */ ++static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name, ++ uint32_t val) ++{ ++ return fdt_setprop_u32(fdt, nodeoffset, name, val); ++} ++ ++/** ++ * fdt_setprop_string - set a property to a string value ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to change ++ * @name: name of the property to change ++ * @str: string value for the property ++ * ++ * fdt_setprop_string() sets the value of the named property in the ++ * given node to the given string value (using the length of the ++ * string to determine the new length of the property), or creates a ++ * new property with that value if it does not already exist. ++ * ++ * This function may insert or delete data from the blob, and will ++ * therefore change the offsets of some existing nodes. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to ++ * contain the new property value ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++#define fdt_setprop_string(fdt, nodeoffset, name, str) \ ++ fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) ++ ++ ++/** ++ * fdt_setprop_empty - set a property to an empty value ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to change ++ * @name: name of the property to change ++ * ++ * fdt_setprop_empty() sets the value of the named property in the ++ * given node to an empty (zero length) value, or creates a new empty ++ * property if it does not already exist. ++ * ++ * This function may insert or delete data from the blob, and will ++ * therefore change the offsets of some existing nodes. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to ++ * contain the new property value ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++#define fdt_setprop_empty(fdt, nodeoffset, name) \ ++ fdt_setprop((fdt), (nodeoffset), (name), NULL, 0) ++ ++/** ++ * fdt_appendprop - append to or create a property ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to change ++ * @name: name of the property to append to ++ * @val: pointer to data to append to the property value ++ * @len: length of the data to append to the property value ++ * ++ * fdt_appendprop() appends the value to the named property in the ++ * given node, creating the property if it does not already exist. ++ * ++ * This function may insert data into the blob, and will therefore ++ * change the offsets of some existing nodes. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to ++ * contain the new property value ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++int fdt_appendprop(void *fdt, int nodeoffset, const char *name, ++ const void *val, int len); ++ ++/** ++ * fdt_appendprop_u32 - append a 32-bit integer value to a property ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to change ++ * @name: name of the property to change ++ * @val: 32-bit integer value to append to the property (native endian) ++ * ++ * fdt_appendprop_u32() appends the given 32-bit integer value ++ * (converting to big-endian if necessary) to the value of the named ++ * property in the given node, or creates a new property with that ++ * value if it does not already exist. ++ * ++ * This function may insert data into the blob, and will therefore ++ * change the offsets of some existing nodes. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to ++ * contain the new property value ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++static inline int fdt_appendprop_u32(void *fdt, int nodeoffset, ++ const char *name, uint32_t val) ++{ ++ fdt32_t tmp = cpu_to_fdt32(val); ++ return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); ++} ++ ++/** ++ * fdt_appendprop_u64 - append a 64-bit integer value to a property ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to change ++ * @name: name of the property to change ++ * @val: 64-bit integer value to append to the property (native endian) ++ * ++ * fdt_appendprop_u64() appends the given 64-bit integer value ++ * (converting to big-endian if necessary) to the value of the named ++ * property in the given node, or creates a new property with that ++ * value if it does not already exist. ++ * ++ * This function may insert data into the blob, and will therefore ++ * change the offsets of some existing nodes. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to ++ * contain the new property value ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++static inline int fdt_appendprop_u64(void *fdt, int nodeoffset, ++ const char *name, uint64_t val) ++{ ++ fdt64_t tmp = cpu_to_fdt64(val); ++ return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); ++} ++ ++/** ++ * fdt_appendprop_cell - append a single cell value to a property ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to change ++ * @name: name of the property to change ++ * @val: 32-bit integer value to append to the property (native endian) ++ * ++ * This is an alternative name for fdt_appendprop_u32() ++ * ++ * Return: 0 on success, negative libfdt error value otherwise. ++ */ ++static inline int fdt_appendprop_cell(void *fdt, int nodeoffset, ++ const char *name, uint32_t val) ++{ ++ return fdt_appendprop_u32(fdt, nodeoffset, name, val); ++} ++ ++/** ++ * fdt_appendprop_string - append a string to a property ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to change ++ * @name: name of the property to change ++ * @str: string value to append to the property ++ * ++ * fdt_appendprop_string() appends the given string to the value of ++ * the named property in the given node, or creates a new property ++ * with that value if it does not already exist. ++ * ++ * This function may insert data into the blob, and will therefore ++ * change the offsets of some existing nodes. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to ++ * contain the new property value ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++#define fdt_appendprop_string(fdt, nodeoffset, name, str) \ ++ fdt_appendprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) ++ ++/** ++ * fdt_appendprop_addrrange - append a address range property ++ * @fdt: pointer to the device tree blob ++ * @parent: offset of the parent node ++ * @nodeoffset: offset of the node to add a property at ++ * @name: name of property ++ * @addr: start address of a given range ++ * @size: size of a given range ++ * ++ * fdt_appendprop_addrrange() appends an address range value (start ++ * address and size) to the value of the named property in the given ++ * node, or creates a new property with that value if it does not ++ * already exist. ++ * If "name" is not specified, a default "reg" is used. ++ * Cell sizes are determined by parent's #address-cells and #size-cells. ++ * ++ * 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_appendprop_addrrange(void *fdt, int parent, int nodeoffset, ++ const char *name, uint64_t addr, uint64_t size); ++ ++/** ++ * fdt_delprop - delete a property ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node whose property to nop ++ * @name: name of the property to nop ++ * ++ * fdt_del_property() will delete the given property. ++ * ++ * This function will delete data from the blob, and will therefore ++ * change the offsets of some existing nodes. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOTFOUND, node does not have the named property ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++int fdt_delprop(void *fdt, int nodeoffset, const char *name); ++ ++/** ++ * fdt_add_subnode_namelen - creates a new node based on substring ++ * @fdt: pointer to the device tree blob ++ * @parentoffset: structure block offset of a node ++ * @name: name of the subnode to create ++ * @namelen: number of characters of name to consider ++ * ++ * Identical to fdt_add_subnode(), but use only the first @namelen ++ * characters of @name as the name of the new node. This is useful for ++ * creating subnodes based on a portion of a larger string, such as a ++ * full path. ++ * ++ * Return: structure block offset of the created subnode (>=0), ++ * negative libfdt error value otherwise ++ */ ++#ifndef SWIG /* Not available in Python */ ++int fdt_add_subnode_namelen(void *fdt, int parentoffset, ++ const char *name, int namelen); ++#endif ++ ++/** ++ * fdt_add_subnode - creates a new node ++ * @fdt: pointer to the device tree blob ++ * @parentoffset: structure block offset of a node ++ * @name: name of the subnode to locate ++ * ++ * fdt_add_subnode() creates a new node as a subnode of the node at ++ * structure block offset parentoffset, with the given name (which ++ * should include the unit address, if any). ++ * ++ * This function will insert data into the blob, and will therefore ++ * change the offsets of some existing nodes. ++ * ++ * returns: ++ * structure block offset of the created nodeequested subnode (>=0), on ++ * success ++ * -FDT_ERR_NOTFOUND, if the requested subnode does not exist ++ * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE ++ * tag ++ * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of ++ * the given name ++ * -FDT_ERR_NOSPACE, if there is insufficient free space in the ++ * blob to contain the new node ++ * -FDT_ERR_NOSPACE ++ * -FDT_ERR_BADLAYOUT ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_TRUNCATED, standard meanings. ++ */ ++int fdt_add_subnode(void *fdt, int parentoffset, const char *name); ++ ++/** ++ * fdt_del_node - delete a node (subtree) ++ * @fdt: pointer to the device tree blob ++ * @nodeoffset: offset of the node to nop ++ * ++ * fdt_del_node() will remove the given node, including all its ++ * subnodes if any, from the blob. ++ * ++ * This function will delete data from the blob, and will therefore ++ * change the offsets of some existing nodes. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++int fdt_del_node(void *fdt, int nodeoffset); ++ ++/** ++ * fdt_overlay_apply - Applies a DT overlay on a base DT ++ * @fdt: pointer to the base device tree blob ++ * @fdto: pointer to the device tree overlay blob ++ * ++ * fdt_overlay_apply() will apply the given device tree overlay on the ++ * given base device tree. ++ * ++ * Expect the base device tree to be modified, even if the function ++ * returns an error. ++ * ++ * returns: ++ * 0, on success ++ * -FDT_ERR_NOSPACE, there's not enough space in the base device tree ++ * -FDT_ERR_NOTFOUND, the overlay points to some inexistant nodes or ++ * properties in the base DT ++ * -FDT_ERR_BADPHANDLE, ++ * -FDT_ERR_BADOVERLAY, ++ * -FDT_ERR_NOPHANDLES, ++ * -FDT_ERR_INTERNAL, ++ * -FDT_ERR_BADLAYOUT, ++ * -FDT_ERR_BADMAGIC, ++ * -FDT_ERR_BADOFFSET, ++ * -FDT_ERR_BADPATH, ++ * -FDT_ERR_BADVERSION, ++ * -FDT_ERR_BADSTRUCTURE, ++ * -FDT_ERR_BADSTATE, ++ * -FDT_ERR_TRUNCATED, standard meanings ++ */ ++int fdt_overlay_apply(void *fdt, void *fdto); ++ ++/** ++ * fdt_overlay_target_offset - retrieves the offset of a fragment's target ++ * @fdt: Base device tree blob ++ * @fdto: Device tree overlay blob ++ * @fragment_offset: node offset of the fragment in the overlay ++ * @pathp: pointer which receives the path of the target (or NULL) ++ * ++ * fdt_overlay_target_offset() retrieves the target offset in the base ++ * device tree of a fragment, no matter how the actual targeting is ++ * done (through a phandle or a path) ++ * ++ * returns: ++ * the targeted node offset in the base device tree ++ * Negative error code on error ++ */ ++int fdt_overlay_target_offset(const void *fdt, const void *fdto, ++ int fragment_offset, char const **pathp); ++ ++/**********************************************************************/ ++/* Debugging / informational functions */ ++/**********************************************************************/ ++ ++const char *fdt_strerror(int errval); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* LIBFDT_H */ +diff --git a/include/libfdt_env.h b/include/libfdt_env.h +new file mode 100644 +index 0000000..51b31d1 +--- /dev/null ++++ b/include/libfdt_env.h +@@ -0,0 +1,95 @@ ++/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ ++#ifndef LIBFDT_ENV_H ++#define LIBFDT_ENV_H ++/* ++ * libfdt - Flat Device Tree manipulation ++ * Copyright (C) 2006 David Gibson, IBM Corporation. ++ * Copyright 2012 Kim Phillips, Freescale Semiconductor. ++ */ ++ ++#include <stdbool.h> ++#include <stddef.h> ++#include <stdint.h> ++#include <limits.h> ++#include <string.h> ++ ++#ifdef __CHECKER__ ++#define FDT_FORCE __attribute__((force)) ++#define FDT_BITWISE __attribute__((bitwise)) ++#else ++#define FDT_FORCE ++#define FDT_BITWISE ++#endif ++ ++typedef uint16_t FDT_BITWISE fdt16_t; ++typedef uint32_t FDT_BITWISE fdt32_t; ++typedef uint64_t FDT_BITWISE fdt64_t; ++ ++#define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n]) ++#define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1)) ++#define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \ ++ (EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3)) ++#define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \ ++ (EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \ ++ (EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \ ++ (EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7)) ++ ++static inline uint16_t fdt16_to_cpu(fdt16_t x) ++{ ++ return (FDT_FORCE uint16_t)CPU_TO_FDT16(x); ++} ++static inline fdt16_t cpu_to_fdt16(uint16_t x) ++{ ++ return (FDT_FORCE fdt16_t)CPU_TO_FDT16(x); ++} ++ ++static inline uint32_t fdt32_to_cpu(fdt32_t x) ++{ ++ return (FDT_FORCE uint32_t)CPU_TO_FDT32(x); ++} ++static inline fdt32_t cpu_to_fdt32(uint32_t x) ++{ ++ return (FDT_FORCE fdt32_t)CPU_TO_FDT32(x); ++} ++ ++static inline uint64_t fdt64_to_cpu(fdt64_t x) ++{ ++ return (FDT_FORCE uint64_t)CPU_TO_FDT64(x); ++} ++static inline fdt64_t cpu_to_fdt64(uint64_t x) ++{ ++ return (FDT_FORCE fdt64_t)CPU_TO_FDT64(x); ++} ++#undef CPU_TO_FDT64 ++#undef CPU_TO_FDT32 ++#undef CPU_TO_FDT16 ++#undef EXTRACT_BYTE ++ ++#ifdef __APPLE__ ++#include <AvailabilityMacros.h> ++ ++/* strnlen() is not available on Mac OS < 10.7 */ ++# if !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < \ ++ MAC_OS_X_VERSION_10_7) ++ ++#define strnlen fdt_strnlen ++ ++/* ++ * fdt_strnlen: returns the length of a string or max_count - which ever is ++ * smallest. ++ * Input 1 string: the string whose size is to be determined ++ * Input 2 max_count: the maximum value returned by this function ++ * Output: length of the string or max_count (the smallest of the two) ++ */ ++static inline size_t fdt_strnlen(const char *string, size_t max_count) ++{ ++ const char *p = memchr(string, 0, max_count); ++ return p ? p - string : max_count; ++} ++ ++#endif /* !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < ++ MAC_OS_X_VERSION_10_7) */ ++ ++#endif /* __APPLE__ */ ++ ++#endif /* LIBFDT_ENV_H */ diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0011-common-Add-essential-libc-functions.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0011-common-Add-essential-libc-functions.patch new file mode 100644 index 0000000000..871a178f98 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0011-common-Add-essential-libc-functions.patch @@ -0,0 +1,101 @@ +From 0f2c7ca446063be6b193fbf870d38c0af19e15c5 Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Tue, 28 Dec 2021 17:28:25 +0800 +Subject: [PATCH] common: Add essential libc functions + +The libfdt uses some of the libc functions, e.g. memcmp, memmove, +strlen .etc. Add them in lib.c. + +The code is copied from TF-A (v2.5) [1] project, which is under the +terms of BSD license. It is the same with boot-wrapper. + +[1]: https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git + +Issue-Id: SCM-3814 +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +Change-Id: If3b55b00afa8694c7522df989a41e0b38eda1d38 +--- + common/lib.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 70 insertions(+), 1 deletion(-) + +diff --git a/common/lib.c b/common/lib.c +index fcf5f69..0be1c4a 100644 +--- a/common/lib.c ++++ b/common/lib.c +@@ -32,4 +32,73 @@ void *memset(void *s, int c, size_t n) + return s; + } + +-/* TODO: memmove and memcmp could also be called */ ++int memcmp(const void *s1, const void *s2, size_t len) ++{ ++ const unsigned char *s = s1; ++ const unsigned char *d = s2; ++ unsigned char sc; ++ unsigned char dc; ++ ++ while (len--) { ++ sc = *s++; ++ dc = *d++; ++ if (sc - dc) ++ return (sc - dc); ++ } ++ ++ return 0; ++} ++ ++void *memmove(void *dst, const void *src, size_t len) ++{ ++ if ((size_t)dst - (size_t)src >= len) { ++ /* destination not in source data, so can safely use memcpy */ ++ return memcpy(dst, src, len); ++ } else { ++ /* copy backwards... */ ++ const char *end = dst; ++ const char *s = (const char *)src + len; ++ char *d = (char *)dst + len; ++ while (d != end) ++ *--d = *--s; ++ } ++ return dst; ++} ++ ++void *memchr(const void *src, int c, size_t len) ++{ ++ const unsigned char *s = src; ++ ++ while (len--) { ++ if (*s == (unsigned char)c) ++ return (void *) s; ++ s++; ++ } ++ ++ return NULL; ++} ++ ++char *strrchr(const char *p, int ch) ++{ ++ char *save; ++ char c; ++ ++ c = ch; ++ for (save = NULL;; ++p) { ++ if (*p == c) ++ save = (char *)p; ++ if (*p == '\0') ++ return (save); ++ } ++ /* NOTREACHED */ ++} ++ ++size_t strlen(const char *s) ++{ ++ const char *cursor = s; ++ ++ while (*cursor) ++ cursor++; ++ ++ return cursor - s; ++} diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0012-Makefile-Add-the-libfdt-to-the-Makefile-system.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0012-Makefile-Add-the-libfdt-to-the-Makefile-system.patch new file mode 100644 index 0000000000..5917ef2052 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0012-Makefile-Add-the-libfdt-to-the-Makefile-system.patch @@ -0,0 +1,61 @@ +From de5d2b6c200ae5dd8113751e58bf7cf5844eec5a Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Tue, 28 Dec 2021 17:42:48 +0800 +Subject: [PATCH] Makefile: Add the libfdt to the Makefile system + +Add the libfdt into Makefile system. The libfdt uses const value and +thus gcc will enable the stack guard. The stack guard will fail the +compile. Add -fno-stack-protector to fix it. + +Issue-Id: SCM-3814 +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +Change-Id: I472bc28cdc5cde3b22461a4b7d7a3752ae382b4b +--- + Makefile.am | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/Makefile.am b/Makefile.am +index 88a27de..5e8668a 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -36,6 +36,9 @@ PSCI_CPU_OFF := 0x84000002 + COMMON_SRC := common/ + COMMON_OBJ := boot.o bakery_lock.o platform.o lib.o + ++LIBFDT_SRC := common/libfdt/ ++LIBFDT_OBJS := fdt.o fdt_ro.o fdt_rw.o ++ + ARCH_OBJ := boot.o stack.o utils.o + + if BOOTWRAPPER_32 +@@ -125,11 +128,12 @@ CHOSEN_NODE := chosen { \ + CPPFLAGS += $(INITRD_FLAGS) + CFLAGS += -I$(top_srcdir)/include/ -I$(top_srcdir)/$(ARCH_SRC)/include/ + CFLAGS += -Wall -fomit-frame-pointer ++CFLAGS += -fno-stack-protector + CFLAGS += -ffunction-sections -fdata-sections + CFLAGS += -fno-pic -fno-pie + LDFLAGS += --gc-sections + +-OBJ := $(addprefix $(ARCH_SRC),$(ARCH_OBJ)) $(addprefix $(COMMON_SRC),$(COMMON_OBJ)) ++OBJ := $(addprefix $(ARCH_SRC),$(ARCH_OBJ)) $(addprefix $(COMMON_SRC),$(COMMON_OBJ)) $(addprefix $(LIBFDT_SRC),$(LIBFDT_OBJS)) + + # Don't lookup all prerequisites in $(top_srcdir), only the source files. When + # building outside the source tree $(ARCH_SRC) needs to be created. +@@ -150,10 +154,13 @@ $(ARCH_SRC): + $(COMMON_SRC): + $(MKDIR_P) $@ + ++$(LIBFDT_SRC): ++ $(MKDIR_P) $@ ++ + %.o: %.S Makefile | $(ARCH_SRC) + $(CC) $(CPPFLAGS) -D__ASSEMBLY__ $(CFLAGS) $(DEFINES) -c -o $@ $< + +-%.o: %.c Makefile | $(COMMON_SRC) ++%.o: %.c Makefile | $(COMMON_SRC) $(LIBFDT_SRC) + $(CC) $(CPPFLAGS) $(CFLAGS) $(DEFINES) -c -o $@ $< + + model.lds: $(LD_SCRIPT) Makefile diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0013-platform-Add-print_hex-func.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0013-platform-Add-print_hex-func.patch new file mode 100644 index 0000000000..136e18ed2e --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0013-platform-Add-print_hex-func.patch @@ -0,0 +1,67 @@ +From 5b8cb5192dbd0332e027e8999c3afe4433983291 Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Wed, 29 Dec 2021 10:50:21 +0800 +Subject: [PATCH] platform: Add print_hex func + +Refine the print functions, and add a new print_hex func to print hex +numbers. + +Issue-Id: SCM-3814 +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +Change-Id: Ic960345d9ef0b41d81d30c4a4dbd9c31139907c4 +--- + common/platform.c | 33 +++++++++++++++++++++++++-------- + 1 file changed, 25 insertions(+), 8 deletions(-) + +diff --git a/common/platform.c b/common/platform.c +index d11f568..8269392 100644 +--- a/common/platform.c ++++ b/common/platform.c +@@ -30,20 +30,37 @@ + #define V2M_SYS(reg) ((void *)SYSREGS_BASE + V2M_SYS_##reg) + #endif + +-static void print_string(const char *str) ++static void print_char(const char c) + { + uint32_t flags; ++ do { ++ flags = raw_readl(PL011(UARTFR)); ++ } while (flags & PL011_UARTFR_FIFO_FULL); + ++ raw_writel(c, PL011(UARTDR)); ++ ++ do { ++ flags = raw_readl(PL011(UARTFR)); ++ } while (flags & PL011_UARTFR_BUSY); ++} ++ ++void print_string(const char *str) ++{ + while (*str) { +- do +- flags = raw_readl(PL011(UARTFR)); +- while (flags & PL011_UARTFR_FIFO_FULL); ++ print_char(*str++); ++ } ++} + +- raw_writel(*str++, PL011(UARTDR)); ++#define HEX_CHARS_PER_INT (2 * sizeof(int)) ++ ++void print_hex(unsigned int val) ++{ + +- do +- flags = raw_readl(PL011(UARTFR)); +- while (flags & PL011_UARTFR_BUSY); ++ const char hex_chars[16] = "0123456789abcdef"; ++ int i; ++ for (i = HEX_CHARS_PER_INT - 1; i >= 0; i--) { ++ int v = (val >> (4 * i)) & 0xf; ++ print_char(hex_chars[v]); + } + } + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0014-common-Add-mem-usage-to-memreserve.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0014-common-Add-mem-usage-to-memreserve.patch new file mode 100644 index 0000000000..ea51816029 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0014-common-Add-mem-usage-to-memreserve.patch @@ -0,0 +1,96 @@ +From b447242cd2457bec20d47fe6a8a5758d97a3bde3 Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Wed, 19 Jan 2022 16:19:02 +0800 +Subject: [PATCH] common: Add mem usage to /memreserve/ + +Set /memreserve/ to prevent next boot stages from overrding PSCI +services with libfdt. + +Issue-Id: SCM-3815 +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +Change-Id: I2ea80cdf736a910fa2c3deb622e21d50f04be960 +--- + Makefile.am | 2 +- + common/boot.c | 1 + + common/device_tree.c | 34 ++++++++++++++++++++++++++++++++++ + include/boot.h | 1 + + 4 files changed, 37 insertions(+), 1 deletion(-) + create mode 100644 common/device_tree.c + +diff --git a/Makefile.am b/Makefile.am +index 5e8668a..734de92 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -34,7 +34,7 @@ endif + PSCI_CPU_OFF := 0x84000002 + + COMMON_SRC := common/ +-COMMON_OBJ := boot.o bakery_lock.o platform.o lib.o ++COMMON_OBJ := boot.o bakery_lock.o platform.o lib.o device_tree.o + + LIBFDT_SRC := common/libfdt/ + LIBFDT_OBJS := fdt.o fdt_ro.o fdt_rw.o +diff --git a/common/boot.c b/common/boot.c +index c74d34c..ee2bea0 100644 +--- a/common/boot.c ++++ b/common/boot.c +@@ -63,6 +63,7 @@ void __noreturn first_spin(unsigned int cpu, unsigned long *mbox, + { + if (cpu == 0) { + init_platform(); ++ dt_add_memreserve(); + + *mbox = (unsigned long)&entrypoint; + sevl(); +diff --git a/common/device_tree.c b/common/device_tree.c +new file mode 100644 +index 0000000..4d0876c +--- /dev/null ++++ b/common/device_tree.c +@@ -0,0 +1,34 @@ ++/* ++ * device_tree.c - Basic device tree node handler ++ * ++ * Copyright (C) 2021 ARM Limited. All rights reserved. ++ * ++ * Use of this source code is governed by a BSD-style license that can be ++ * found in the LICENSE.txt file. ++ */ ++#include <libfdt.h> ++ ++extern unsigned long dtb; ++extern char firmware_start[], firmware_end[]; ++ ++extern void print_string(const char *str); ++ ++static void *blob; ++ ++ ++void dt_add_memreserve(void) ++{ ++ int ret; ++ ++ blob = (void*)&dtb; ++ print_string("Add /memreserve/\n\r"); ++ ++ fdt_open_into(blob, blob, fdt_totalsize(blob) + ++ sizeof(struct fdt_reserve_entry)); ++ ret = fdt_add_mem_rsv(blob, (uint64_t)firmware_start, ++ (uint64_t)(firmware_end - firmware_start)); ++ ++ if(ret < 0) { ++ print_string("reserve mem add err\n\r"); ++ } ++} +diff --git a/include/boot.h b/include/boot.h +index d75e013..c3e2ec1 100644 +--- a/include/boot.h ++++ b/include/boot.h +@@ -16,4 +16,5 @@ void __noreturn spin(unsigned long *mbox, unsigned long invalid, int is_entry); + void __noreturn first_spin(unsigned int cpu, unsigned long *mbox, + unsigned long invalid_addr); + ++void dt_add_memreserve(void); + #endif diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0015-boot-Add-the-enable-keep-el-compile-option.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0015-boot-Add-the-enable-keep-el-compile-option.patch new file mode 100644 index 0000000000..0411ef0229 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0015-boot-Add-the-enable-keep-el-compile-option.patch @@ -0,0 +1,102 @@ +From 8271c21bcff260295203214b7b8c87cdb8236453 Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Tue, 4 Jan 2022 17:01:55 +0800 +Subject: [PATCH] boot: Add the --enable-keep-el compile option + +Add --enable-keep-el compile option to enable boot-wrapper booting next +stage at EL2. +The Armv8R AArch64 boots at EL2. If the next stage requires EL2 booting, +the boot-wrapper should not drop to EL1. +Currently, this option only works for Armv8R AArch64. Also, to work with +Linux PSCI, this option will cause secondary cores booting at EL1. + +Issue-Id: SCM-3813 +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +Change-Id: I3ba9c87cf0b59d163ca433f74c9e3a46e5ca2c63 +--- + Makefile.am | 4 ++++ + arch/aarch64/boot.S | 6 +++++- + common/psci.c | 6 ++++++ + configure.ac | 5 +++++ + 4 files changed, 20 insertions(+), 1 deletion(-) + +diff --git a/Makefile.am b/Makefile.am +index 734de92..054becd 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -33,6 +33,10 @@ PSCI_CPU_ON := 0xc4000003 + endif + PSCI_CPU_OFF := 0x84000002 + ++if KEEP_EL ++DEFINES += -DKEEP_EL ++endif ++ + COMMON_SRC := common/ + COMMON_OBJ := boot.o bakery_lock.o platform.o lib.o device_tree.o + +diff --git a/arch/aarch64/boot.S b/arch/aarch64/boot.S +index 6dbd5cc..157c097 100644 +--- a/arch/aarch64/boot.S ++++ b/arch/aarch64/boot.S +@@ -233,7 +233,11 @@ el2_init: + msr cnthctl_el2, x0 + isb + ++#ifdef KEEP_EL ++ mov w0, #SPSR_KERNEL ++#else + mov w0, #SPSR_KERNEL_EL1 ++#endif + ldr x1, =spsr_to_elx + str w0, [x1] + // fall through +@@ -313,5 +317,5 @@ ASM_FUNC(jump_kernel) + .align 3 + flag_keep_el: + .long 0 +-spsr_to_elx: ++ASM_DATA(spsr_to_elx) + .long 0 +diff --git a/common/psci.c b/common/psci.c +index a0e8700..945780b 100644 +--- a/common/psci.c ++++ b/common/psci.c +@@ -18,6 +18,8 @@ + #error "No MPIDRs provided" + #endif + ++extern unsigned int spsr_to_elx; ++ + static unsigned long branch_table[NR_CPUS]; + + bakery_ticket_t branch_table_lock[NR_CPUS]; +@@ -44,6 +46,10 @@ static int psci_cpu_on(unsigned long target_mpidr, unsigned long address) + ret = psci_store_address(cpu, address); + bakery_unlock(branch_table_lock, this_cpu); + ++#ifdef KEEP_EL ++ spsr_to_elx = SPSR_KERNEL_EL1; ++#endif ++ + return ret; + } + +diff --git a/configure.ac b/configure.ac +index 53e51be..0e07db3 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -25,6 +25,11 @@ AS_IF([test "x$BOOTWRAPPER_ES" = x32 -a "x$KERNEL_ES" != x32], + [AC_MSG_ERROR([a 32-bit boot-wrapper cannot launch a 64-bit kernel])] + ) + ++AC_ARG_ENABLE([keep-el], ++ AC_HELP_STRING([--enable-keep-el], [keep exception level when start kernel]), ++ [KEEP_EL=yes], [KEEP_EL=no]) ++AM_CONDITIONAL([KEEP_EL], [test "x$KEEP_EL" = xyes]) ++ + # Allow a user to pass --with-kernel-dir + AC_ARG_WITH([kernel-dir], + AS_HELP_STRING([--with-kernel-dir], [specify the root Linux kernel build directory (required)]), diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0016-Makefile-Change-COUNTER_FREQ-to-100-MHz.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0016-Makefile-Change-COUNTER_FREQ-to-100-MHz.patch new file mode 100644 index 0000000000..a6b16e403a --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0016-Makefile-Change-COUNTER_FREQ-to-100-MHz.patch @@ -0,0 +1,34 @@ +From dd3e3f414d0e6ed1643c2e2ccac676b7fc1dc7a9 Mon Sep 17 00:00:00 2001 +From: Peter Hoyes <Peter.Hoyes@arm.com> +Date: Tue, 1 Feb 2022 11:28:46 +0000 +Subject: [PATCH] Makefile: Change COUNTER_FREQ to 100 MHz + +Older Arm Fast Models (AEM < RevC) had a base frequency of 24 MHz. but +the RevC base models use 100 MHz. There is not a robust method of +determining the configured base frequency at runtime, so update +COUNTER_FREQ to be 100 MHz. + +Issue-Id: SCM-3871 +Upstream-Status: Pending +Signed-off-by: Peter Hoyes <Peter.Hoyes@arm.com> +Change-Id: Ia9ad0f8ee488d1a887791f1fa1d8f3bf9c5887fd +--- + Makefile.am | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Makefile.am b/Makefile.am +index 40bc5d6..b48173c 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -13,7 +13,7 @@ SCRIPT_DIR := $(top_srcdir)/scripts + PHYS_OFFSET := $(shell perl -I $(SCRIPT_DIR) $(SCRIPT_DIR)/findmem.pl $(KERNEL_DTB)) + UART_BASE := $(shell perl -I $(SCRIPT_DIR) $(SCRIPT_DIR)/findbase.pl $(KERNEL_DTB) 0 'arm,pl011') + SYSREGS_BASE := $(shell perl -I $(SCRIPT_DIR) $(SCRIPT_DIR)/findbase.pl $(KERNEL_DTB) 0 'arm,vexpress-sysreg' 2> /dev/null) +-COUNTER_FREQ := 24000000 ++COUNTER_FREQ := 100000000 + + CPU_IDS := $(shell perl -I $(SCRIPT_DIR) $(SCRIPT_DIR)/findcpuids.pl $(KERNEL_DTB)) + NR_CPUS := $(shell echo $(CPU_IDS) | tr ',' ' ' | wc -w) +-- +2.25.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0017-PSCI-Apply-flush-cache-after-setting-branch_data.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0017-PSCI-Apply-flush-cache-after-setting-branch_data.patch new file mode 100644 index 0000000000..8d981f525c --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0017-PSCI-Apply-flush-cache-after-setting-branch_data.patch @@ -0,0 +1,52 @@ +From 6923f2a0c59cf92ba5ad50ec1d658a357b4ba5d7 Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Tue, 2 Nov 2021 10:48:39 +0800 +Subject: [PATCH] PSCI: Apply flush cache after setting branch_data + +For v8-R64, Hypervisor calls boot-wrapper's PSCI service using simple +function call (instead of hvc). + +In this case, hypervisor's main core has enabled MPU and cache, but +the secondary cores which are spinning have not enabled cache. +That means if the main core set the branch_data to 1 to boot other +cores, the secondary cores cannot see the change of branch_data and +also cannot break the spin. + +Thus, the PSCI service in boot-wrapper needs a cache flush after +setting branch_data in order to let other cores see the change. + +Issue-ID: SCM-3816 +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +Change-Id: Ifc282091c54d8fb2ffdb8cfa7fd3ffc1f4be717e +--- + common/psci.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/common/psci.c b/common/psci.c +index 945780b..6efc695 100644 +--- a/common/psci.c ++++ b/common/psci.c +@@ -24,12 +24,18 @@ static unsigned long branch_table[NR_CPUS]; + + bakery_ticket_t branch_table_lock[NR_CPUS]; + ++static inline void flush_per_cpu_data(void *data) ++{ ++ asm volatile ("dc cvac, %0" : : "r" (data)); ++} ++ + static int psci_store_address(unsigned int cpu, unsigned long address) + { + if (branch_table[cpu] != PSCI_ADDR_INVALID) + return PSCI_RET_ALREADY_ON; + + branch_table[cpu] = address; ++ flush_per_cpu_data((void*)&(branch_table[cpu])); + return PSCI_RET_SUCCESS; + } + +-- +2.25.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0018-PSCI-Add-function-call-entry-point.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0018-PSCI-Add-function-call-entry-point.patch new file mode 100644 index 0000000000..97cd3cb9e0 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0018-PSCI-Add-function-call-entry-point.patch @@ -0,0 +1,74 @@ +From ed46e83df2400b1b3f3364169aacf787bd91bd45 Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Tue, 25 Jan 2022 14:56:36 +0800 +Subject: [PATCH] PSCI: Add function call entry point + +The max exception level of Armv8R AArch64 is EL2, which means it has no +exclusive EL for firmware. That is, firmware and hypervisors have to share +the EL2. Also, hypervisors cannot call firmware services via a 'smc' +instruction. Thus, boot-wrapper has to provide a function entry point +for Armv8R AArch64. + +Issue-Id: SCM-3816 +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +Change-Id: I06ec8e50298603155c6d8ae2330e71db2f111182 +--- + common/psci.c | 24 ++++++++++++++++++++---- + 1 file changed, 20 insertions(+), 4 deletions(-) + +diff --git a/common/psci.c b/common/psci.c +index 6efc695..8fdefb5 100644 +--- a/common/psci.c ++++ b/common/psci.c +@@ -20,6 +20,8 @@ + + extern unsigned int spsr_to_elx; + ++unsigned long flag_from_smc_fn[NR_CPUS]; ++ + static unsigned long branch_table[NR_CPUS]; + + bakery_ticket_t branch_table_lock[NR_CPUS]; +@@ -49,12 +51,14 @@ static int psci_cpu_on(unsigned long target_mpidr, unsigned long address) + return PSCI_RET_INVALID_PARAMETERS; + + bakery_lock(branch_table_lock, this_cpu); +- ret = psci_store_address(cpu, address); +- bakery_unlock(branch_table_lock, this_cpu); +- + #ifdef KEEP_EL +- spsr_to_elx = SPSR_KERNEL_EL1; ++ if (!flag_from_smc_fn[this_cpu]) { ++ spsr_to_elx = SPSR_KERNEL_EL1; ++ flush_per_cpu_data((void*)&(spsr_to_elx)); ++ } + #endif ++ ret = psci_store_address(cpu, address); ++ bakery_unlock(branch_table_lock, this_cpu); + + return ret; + } +@@ -90,6 +94,18 @@ long psci_call(unsigned long fid, unsigned long arg1, unsigned long arg2) + } + } + ++long smc_fn_entry(unsigned long fid, unsigned long arg1, unsigned long arg2) ++{ ++ long ret; ++ unsigned int this_cpu = this_cpu_logical_id(); ++ ++ flag_from_smc_fn[this_cpu] = 1; ++ ret = psci_call(fid, arg1, arg2); ++ flag_from_smc_fn[this_cpu] = 0; ++ ++ return ret; ++} ++ + void __noreturn psci_first_spin(unsigned int cpu) + { + if (cpu == MPIDR_INVALID) +-- +2.25.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0019-lds-Rearrange-and-mark-the-sections.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0019-lds-Rearrange-and-mark-the-sections.patch new file mode 100644 index 0000000000..1f10209da1 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0019-lds-Rearrange-and-mark-the-sections.patch @@ -0,0 +1,61 @@ +From 36b5fa3f4db49ac7aef42ff1d58a895226c7e96c Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Tue, 2 Nov 2021 15:10:28 +0800 +Subject: [PATCH] lds: Rearrange and mark the sections + +To make it possible for the next stage to protect sections with MPU, +boot-wrapper needs to provide the text and data section information. +By rearranging the .data .rodata and .vector sections, all sections +can be split into 2 big sections: + - RO and Executable + - RW and Non-Executable +Add firmware_data to mark the boundry, thus: +firmware_start to firmware_data - 1 indicates RO and Executable section, +firmware_data to firmware_end - 1 indicates RW and Non-Executable +section. + +Also, the firmware_data and firmware_end should align with 64 bytes, +since Armv8R AArch64 MPU requires it. + +Issue-ID: SCM-3816 +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +Change-Id: I55342aa7492f2c7b5c16ab9a6472c8cb45cff8fd +--- + model.lds.S | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/model.lds.S b/model.lds.S +index ab98ddf..85451f9 100644 +--- a/model.lds.S ++++ b/model.lds.S +@@ -63,12 +63,16 @@ SECTIONS + } + #endif + ++#define FIRMWARE_ALIGN . = ALIGN(1 << 6) + .boot PHYS_OFFSET: { + PROVIDE(firmware_start = .); + *(.init) + *(.text*) +- *(.data* .rodata* .bss* COMMON) + *(.vectors) ++ *(.rodata*) ++ FIRMWARE_ALIGN; ++ PROVIDE(firmware_data = .); ++ *(.data* .bss* COMMON) + *(.stack) + PROVIDE(etext = .); + } +@@ -77,6 +81,7 @@ SECTIONS + mbox = .; + QUAD(0x0) + } ++ FIRMWARE_ALIGN; + PROVIDE(firmware_end = .); + + ASSERT(etext <= (PHYS_OFFSET + TEXT_LIMIT), ".text overflow!") +-- +2.25.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0020-common-Provide-firmware-info-using-libfdt.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0020-common-Provide-firmware-info-using-libfdt.patch new file mode 100644 index 0000000000..cafcc09bed --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0020-common-Provide-firmware-info-using-libfdt.patch @@ -0,0 +1,345 @@ +From 8bdbb64d13f14d40546b71dbcfee2b2a8ea002a5 Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Wed, 29 Dec 2021 15:17:38 +0800 +Subject: [PATCH] common: Provide firmware info using libfdt + +Boot-wrapper uses libfdt to provide more info in device tree. +We add a new node to include those new firmware relevant infomation. +The new node defined as follows: + fw-shared-info { + compatible = "firmware,shared_info"; + + #address-cells = <0x02>; + #size-cells = <0x02>; + + version = "1.0"; + regions = <START_ADDR_HIGH START_ADDR_LOW SIZE_HIGH SIZE_LOW + 0x0 0x80000000 0x0 0x400000 + 0x0 0x90000000 0x0 0x400000 + 0x0 0xA0000000 0x0 0x400000>; + regions-permission = "RX", "R", "RWX", "RW"; + regions-cache = "Cache", "NCache", "Cache", "Device" + + function_entry = <ENTRY_ADDR_HIGH ENRTY_ADDR_LOW>; + }; +The node path is /fw-shared-info. +For boot-wrapper, in real case, it will be: + fw-shared-info { + compatible = "firmware,shared_info"; + + #address-cells = <0x02>; + #size-cells = <0x02>; + + version = "1.0"; + regions = <0x0 firmware_start 0x0 firmware_code_size + 0x0 firmware_data 0x0 firmware_data_size>; + regions-permission = "RX", "RW"; + regions-cache = "Cache", "Cache"; + + function_entry = <0x0 smc_fn_entry>; + }; + +Issue-Id: SCM-3816 +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +Change-Id: I6ebc59ce2bd3939b0fe066720d57821eaa1bed27 +--- + common/device_tree.c | 271 ++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 270 insertions(+), 1 deletion(-) + +diff --git a/common/device_tree.c b/common/device_tree.c +index 4d0876c..7f7befc 100644 +--- a/common/device_tree.c ++++ b/common/device_tree.c +@@ -8,13 +8,225 @@ + */ + #include <libfdt.h> + ++#define DEVICE_TREE_DEBUG 1 ++ ++#define FW_NODE_NAME "/fw-shared-info" ++#define FW_COMPAT "firmware,shared_info" ++#define FW_INFO_VER "1.0" ++ ++#ifdef BOOTWRAPPER_32 ++#define CELL_NUM 1 ++#define VAL_TYPE uint32_t ++#else ++#define CELL_NUM 2 ++#define VAL_TYPE uint64_t ++#endif ++ ++#define ALIGN(x) (((x) + (FDT_TAGSIZE) - 1) & ~((FDT_TAGSIZE) - 1)) ++ + extern unsigned long dtb; +-extern char firmware_start[], firmware_end[]; ++extern char firmware_start[], firmware_data[], firmware_end[]; ++ ++extern long smc_fn_entry(unsigned long, unsigned long, unsigned long); + + extern void print_string(const char *str); ++extern void print_hex(unsigned int val); + + static void *blob; + ++static char *realloc_node(char *fdt, const char *name) ++{ ++ int delta; ++ int new_sz; ++ /* FDT_BEGIN_NODE, node name in off_struct and FDT_END_NODE */ ++ delta = sizeof(struct fdt_node_header) + ALIGN(strlen(name) + 1) ++ + FDT_TAGSIZE; ++ new_sz = fdt_totalsize(fdt) + delta; ++ fdt_open_into(fdt, fdt, new_sz); ++ return fdt; ++} ++ ++static int create_node(const char *node_name) ++{ ++ int node = 0; ++ char *p; ++ ++ p = strrchr(node_name, '/'); ++ if (!p) { ++ print_string("node name without '/'\r\n"); ++ return -1; ++ } ++ *p = '\0'; ++ ++ blob = realloc_node(blob, p + 1); ++ ++ if (p > node_name) { ++ node = fdt_path_offset(blob, node_name); ++ if (node < 0) { ++ print_string("no node name\r\n"); ++ return -1; ++ } ++ } ++ ++ node = fdt_add_subnode(blob, node, p + 1); ++ if (node < 0) { ++ print_string("add subnode err\r\n"); ++ return -1; ++ } ++ ++ return node; ++} ++ ++static int dt_create_fw_node(void) { ++ int fw_node; ++ ++ fw_node = fdt_path_offset(blob, FW_NODE_NAME); ++ ++ if(fw_node < 0) { ++ fw_node = create_node(FW_NODE_NAME); ++ } ++ ++ return fw_node; ++} ++ ++static char *realloc_property(char *fdt, int nodeoffset, const char *name, ++ int newlen) ++{ ++ int delta = 0; ++ int oldlen = 0; ++ int new_sz; ++ ++ if (!fdt_get_property(fdt, nodeoffset, name, &oldlen)) ++ delta = sizeof(struct fdt_property) + strlen(name) + 1; ++ ++ if (newlen > oldlen) ++ delta += ALIGN(newlen) - ALIGN(oldlen); ++ ++ new_sz = fdt_totalsize(fdt) + delta; ++ fdt_open_into(fdt, fdt, new_sz); ++ return fdt; ++} ++ ++static void dt_set_prop(int node, char *property, void *buf, int len) ++{ ++ int err; ++ ++ err = fdt_setprop(blob, node, property, buf, len); ++ if (err == -FDT_ERR_NOSPACE) { ++ blob = realloc_property(blob, node, property, len); ++ err = fdt_setprop(blob, node, property, buf, len); ++ } ++ if (err) { ++ print_string("fdt error\n\r"); ++ } ++} ++ ++static void dt_set_prop_u32(int node, char *property, uint32_t val) ++{ ++ fdt32_t fdt_val = cpu_to_fdt32(val); ++ int len = sizeof(fdt32_t); ++ ++ dt_set_prop(node, property, (void*)&fdt_val, len); ++} ++ ++static void dt_set_prop_u64(int node, char *property, uint64_t val) ++{ ++ fdt64_t fdt_val = cpu_to_fdt64(val); ++ int len = sizeof(fdt64_t); ++ ++ dt_set_prop(node, property, (void*)&fdt_val, len); ++} ++ ++/* This dt_set_prop_u32_array maybe unused according to the BOOTWRAPPER_32 */ ++__attribute__((unused)) ++static void dt_set_prop_u32_array(int node, char *property, uint32_t *vals, ++ int size) ++{ ++ fdt32_t *fdt_vals = (fdt32_t*)vals; ++ int len = sizeof(fdt32_t) * size; ++ ++ for (int i = 0; i < size; i++) { ++ fdt_vals[i] = cpu_to_fdt32(vals[i]); ++ } ++ ++ dt_set_prop(node, property, (void*)fdt_vals, len); ++} ++ ++static void dt_set_prop_u64_array(int node, char *property, uint64_t *vals, ++ int size) ++{ ++ fdt64_t *fdt_vals = (fdt64_t*)vals; ++ int len = sizeof(fdt64_t) * size; ++ ++ for (int i = 0; i < size; i++) { ++ fdt_vals[i] = cpu_to_fdt64(vals[i]); ++ } ++ ++ dt_set_prop(node, property, (void*)fdt_vals, len); ++} ++ ++#if DEVICE_TREE_DEBUG ++static void dt_dump_string(const void *s, int len) ++{ ++ char *sub = (char*)s; ++ int sublen; ++ while(*sub && ((uint64_t)sub - (uint64_t)s) < len) { ++ sublen = strlen(sub) + 1; ++ print_string(sub); ++ print_string(" "); ++ sub += sublen; ++ } ++ print_string("\n\r"); ++} ++ ++static void dt_dump_fdt32_array(const void *vals, int len) ++{ ++ fdt32_t *fdt_vals = (fdt32_t*)vals; ++ len = len / sizeof(fdt32_t); ++ for (int i = 0; i < len; i++) { ++ print_hex(fdt32_to_cpu(fdt_vals[i])); ++ print_string(" "); ++ } ++ print_string("\n\r"); ++} ++ ++static void dt_dump(int node, char *property, char type) ++{ ++ const void *val; ++ int len; ++ ++ val = fdt_getprop(blob, node, property, &len); ++ print_string(property); ++ print_string(": "); ++ ++ if (type == 's') { ++ /* string type */ ++ dt_dump_string(val, len); ++ return; ++ } ++ ++ /* uint type */ ++ dt_dump_fdt32_array(val, len); ++} ++ ++void dt_dump_all(int node) ++{ ++ if (node >= 0) { ++ print_string(FW_NODE_NAME" info:\r\n"); ++ dt_dump(node, "compatible", 's'); ++ dt_dump(node, "version", 's'); ++ dt_dump(node, "function_entry", 'i'); ++ dt_dump(node, "address-cells", 'i'); ++ dt_dump(node, "size-cells", 'i'); ++ dt_dump(node, "regions", 'i'); ++ dt_dump(node, "regions-permission", 's'); ++ dt_dump(node, "regions-cache", 's'); ++ print_string("\r\n"); ++ } ++} ++#else ++void dt_dump_all(int node) { (void*)node; return; } ++#endif + + void dt_add_memreserve(void) + { +@@ -32,3 +244,60 @@ void dt_add_memreserve(void) + print_string("reserve mem add err\n\r"); + } + } ++ ++void dt_fw_node_init(int enable) ++{ ++ int fw_node; ++ ++ VAL_TYPE regions[] = { ++ /* code region: start, end, ro, x, cachable */ ++ (VAL_TYPE)firmware_start, ++ (VAL_TYPE)(firmware_data - firmware_start), ++ /* data region: start, end, rw, xn, cachable */ ++ (VAL_TYPE)firmware_data, ++ (VAL_TYPE)(firmware_end - firmware_data), ++ }; ++ int regions_num = sizeof(regions) / sizeof(VAL_TYPE); ++ char regions_permission[] = "RX\0RW"; ++ char regions_cache[] = "Cache\0Cache"; ++ ++ if (!enable) ++ return; ++ ++ print_string("Prepare "FW_NODE_NAME" node\n\r"); ++ ++ blob = (void*)&dtb; ++ ++ if(fdt_path_offset(blob, "/psci") < 0) { ++ print_string("/psci node not found\n\r"); ++ return; ++ } ++ ++ fw_node = dt_create_fw_node(); ++ ++ if(fw_node < 0) { ++ print_string(FW_NODE_NAME" node create err\n\r"); ++ } ++ ++ dt_set_prop(fw_node, "compatible", FW_COMPAT, sizeof(FW_COMPAT)); ++ dt_set_prop(fw_node, "version", FW_INFO_VER, sizeof(FW_INFO_VER)); ++ ++ dt_set_prop_u32(fw_node, "address-cells", CELL_NUM); ++ dt_set_prop_u32(fw_node, "size-cells", CELL_NUM); ++ dt_set_prop(fw_node, "regions-permission", regions_permission, ++ sizeof(regions_permission)); ++ dt_set_prop(fw_node, "regions-cache", regions_cache, ++ sizeof(regions_cache)); ++ ++#ifdef BOOTWRAPPER_32 ++ dt_set_prop_u32_array(fw_node, "regions", regions, regions_num); ++ dt_set_prop_u32(fw_node, "function_entry", (VAL_TYPE)smc_fn_entry); ++#else ++ dt_set_prop_u64_array(fw_node, "regions", regions, regions_num); ++ dt_set_prop_u64(fw_node, "function_entry", (VAL_TYPE)smc_fn_entry); ++#endif ++ ++ fdt_pack(blob); ++ ++ dt_dump_all(fw_node); ++} +-- +2.25.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0021-boot-Enable-firmware-node-initialization.patch b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0021-boot-Enable-firmware-node-initialization.patch new file mode 100644 index 0000000000..943afdee3a --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/boot-wrapper-aarch64/files/fvp-baser-aemv8r64/0021-boot-Enable-firmware-node-initialization.patch @@ -0,0 +1,98 @@ +From 6dfc937d1ae54d2ae9f8c60ca29ba73ca14dc8c4 Mon Sep 17 00:00:00 2001 +From: Jaxson Han <jaxson.han@arm.com> +Date: Wed, 29 Dec 2021 15:33:17 +0800 +Subject: [PATCH] boot: Enable firmware node initialization + +Enable the firmware node initialization, so that the next stage +(hypervisor) could share the EL2 with firmware (boot-wrapper). The next +stage (hypervisor) get the smccc entry point, code/data sections, the +sections attrs and firmware node version and so on. +It is worth noting that this EL2 sharing mechanism is only for Armv8R +AArch64, thus add flag_v8r to record if the arch is Armv8R AArch64. +Enable the firmware node initialization only if it is Armv8R AArch64. +Also, we increase the stack size to 1024 to fix the stack overflow issue +when using the libfdt. + +Add -fno-builtin options to CFLAGS to avoid the issue that the 'memset' +in common/lib.c conflicts with builtin 'memset' function. GCC version +>= 10 will have an incorrect compilation without -fno-builtin; + +Issue-Id: SCM-3816 +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Jaxson Han <jaxson.han@arm.com> +Change-Id: Ib274485a34d26215595fd0cd737be86610289817 +--- + Makefile.am | 4 ++-- + arch/aarch64/boot.S | 6 ++++++ + common/boot.c | 4 ++++ + 3 files changed, 12 insertions(+), 2 deletions(-) + +diff --git a/Makefile.am b/Makefile.am +index 054becd..b01809c 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -23,7 +23,7 @@ DEFINES += -DCPU_IDS=$(CPU_IDS) + DEFINES += -DNR_CPUS=$(NR_CPUS) + DEFINES += $(if $(SYSREGS_BASE), -DSYSREGS_BASE=$(SYSREGS_BASE), ) + DEFINES += -DUART_BASE=$(UART_BASE) +-DEFINES += -DSTACK_SIZE=256 ++DEFINES += -DSTACK_SIZE=1024 + + if KERNEL_32 + DEFINES += -DKERNEL_32 +@@ -132,7 +132,7 @@ CHOSEN_NODE := chosen { \ + CPPFLAGS += $(INITRD_FLAGS) + CFLAGS += -I$(top_srcdir)/include/ -I$(top_srcdir)/$(ARCH_SRC)/include/ + CFLAGS += -Wall -fomit-frame-pointer +-CFLAGS += -fno-stack-protector ++CFLAGS += -fno-stack-protector -fno-builtin + CFLAGS += -ffunction-sections -fdata-sections + CFLAGS += -fno-pic -fno-pie + LDFLAGS += --gc-sections +diff --git a/arch/aarch64/boot.S b/arch/aarch64/boot.S +index 157c097..f310387 100644 +--- a/arch/aarch64/boot.S ++++ b/arch/aarch64/boot.S +@@ -240,6 +240,10 @@ el2_init: + #endif + ldr x1, =spsr_to_elx + str w0, [x1] ++ ++ mov w0, #1 ++ ldr x1, =flag_v8r ++ str w0, [x1] + // fall through + + el_max_init: +@@ -319,3 +323,5 @@ flag_keep_el: + .long 0 + ASM_DATA(spsr_to_elx) + .long 0 ++ASM_DATA(flag_v8r) ++ .long 0 +diff --git a/common/boot.c b/common/boot.c +index ee2bea0..38b2dca 100644 +--- a/common/boot.c ++++ b/common/boot.c +@@ -11,6 +11,9 @@ + + extern unsigned long entrypoint; + extern unsigned long dtb; ++extern unsigned int flag_v8r; ++ ++extern void dt_fw_node_init(int enable); + + void init_platform(void); + +@@ -64,6 +67,7 @@ void __noreturn first_spin(unsigned int cpu, unsigned long *mbox, + if (cpu == 0) { + init_platform(); + dt_add_memreserve(); ++ dt_fw_node_init(flag_v8r == 1); + + *mbox = (unsigned long)&entrypoint; + sevl(); +-- +2.25.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/files/tc/0001-feat-emulate-cntp-timer-register-accesses-using-cnth.patch b/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/files/tc/0001-feat-emulate-cntp-timer-register-accesses-using-cnth.patch new file mode 100644 index 0000000000..7094c8bc8f --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/files/tc/0001-feat-emulate-cntp-timer-register-accesses-using-cnth.patch @@ -0,0 +1,252 @@ +From c8bd941579fb062359b683b184b851eea2ddb761 Mon Sep 17 00:00:00 2001 +From: Ben Horgan <ben.horgan@arm.com> +Date: Fri, 4 Mar 2022 16:48:14 +0000 +Subject: [PATCH 1/5] feat: emulate cntp timer register accesses using cnthps + +Upstream-Status: Inappropriate [Experimental feature] +Signed-off-by: Ben Horgan <ben.horgan@arm.com> +Change-Id: I67508203273baf3bd8e6be2d99717028db945715 +--- + Makefile | 3 +- + src/arch/aarch64/hypervisor/BUILD.gn | 1 + + src/arch/aarch64/hypervisor/cpu.c | 11 ++- + src/arch/aarch64/hypervisor/handler.c | 6 ++ + src/arch/aarch64/hypervisor/timer_el1.c | 104 ++++++++++++++++++++++++ + src/arch/aarch64/hypervisor/timer_el1.h | 20 +++++ + src/arch/aarch64/msr.h | 8 ++ + 7 files changed, 150 insertions(+), 3 deletions(-) + create mode 100644 src/arch/aarch64/hypervisor/timer_el1.c + create mode 100644 src/arch/aarch64/hypervisor/timer_el1.h + +diff --git a/Makefile b/Makefile +index c9fb16f..6371a8a 100644 +--- a/Makefile ++++ b/Makefile +@@ -59,7 +59,8 @@ CHECKPATCH := $(CURDIR)/third_party/linux/scripts/checkpatch.pl \ + # debug_el1.c : uses XMACROS, which checkpatch doesn't understand. + # perfmon.c : uses XMACROS, which checkpatch doesn't understand. + # feature_id.c : uses XMACROS, which checkpatch doesn't understand. +-CHECKPATCH_IGNORE := "src/arch/aarch64/hypervisor/debug_el1.c\|src/arch/aarch64/hypervisor/perfmon.c\|src/arch/aarch64/hypervisor/feature_id.c" ++# timer_el1.c : uses XMACROS, which checkpatch doesn't understand. ++CHECKPATCH_IGNORE := "src/arch/aarch64/hypervisor/debug_el1.c\|src/arch/aarch64/hypervisor/perfmon.c\|src/arch/aarch64/hypervisor/feature_id.c\|src/arch/aarch64/hypervisor/timer_el1.c" + + OUT ?= out/$(PROJECT) + OUT_DIR = out/$(PROJECT) +diff --git a/src/arch/aarch64/hypervisor/BUILD.gn b/src/arch/aarch64/hypervisor/BUILD.gn +index 6068d1e..de1a414 100644 +--- a/src/arch/aarch64/hypervisor/BUILD.gn ++++ b/src/arch/aarch64/hypervisor/BUILD.gn +@@ -45,6 +45,7 @@ source_set("hypervisor") { + "handler.c", + "perfmon.c", + "psci_handler.c", ++ "timer_el1.c", + "vm.c", + ] + +diff --git a/src/arch/aarch64/hypervisor/cpu.c b/src/arch/aarch64/hypervisor/cpu.c +index c6cebdd..cb41e6e 100644 +--- a/src/arch/aarch64/hypervisor/cpu.c ++++ b/src/arch/aarch64/hypervisor/cpu.c +@@ -91,13 +91,20 @@ void arch_regs_reset(struct vcpu *vcpu) + if (is_primary) { + /* + * cnthctl_el2 is redefined when VHE is enabled. +- * EL1PCTEN, don't trap phys cnt access. +- * EL1PCEN, don't trap phys timer access. ++ * EL1PCTEN, don't trap phys cnt access. Except when in ++ * secure world without vhe. ++ * EL1PCEN, don't trap phys timer access. Except when in ++ * secure world without vhe. + */ + if (has_vhe_support()) { + cnthctl |= (1U << 10) | (1U << 11); + } else { ++#if SECURE_WORLD == 1 ++ cnthctl &= ~(1U << 0); ++ cnthctl &= ~(1U << 1); ++#else + cnthctl |= (1U << 0) | (1U << 1); ++#endif + } + } + +diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c +index cd64d68..c9068c5 100644 +--- a/src/arch/aarch64/hypervisor/handler.c ++++ b/src/arch/aarch64/hypervisor/handler.c +@@ -34,6 +34,7 @@ + #include "psci_handler.h" + #include "smc.h" + #include "sysregs.h" ++#include "timer_el1.h" + + /** + * Hypervisor Fault Address Register Non-Secure. +@@ -1276,6 +1277,11 @@ void handle_system_register_access(uintreg_t esr_el2) + inject_el1_unknown_exception(vcpu, esr_el2); + return; + } ++ } else if (timer_el1_is_register_access(esr_el2)) { ++ if (!timer_el1_process_access(vcpu, vm_id, esr_el2)) { ++ inject_el1_unknown_exception(vcpu, esr_el2); ++ return; ++ } + } else { + inject_el1_unknown_exception(vcpu, esr_el2); + return; +diff --git a/src/arch/aarch64/hypervisor/timer_el1.c b/src/arch/aarch64/hypervisor/timer_el1.c +new file mode 100644 +index 0000000..c30e554 +--- /dev/null ++++ b/src/arch/aarch64/hypervisor/timer_el1.c +@@ -0,0 +1,104 @@ ++/* ++ * Copyright 2022 The Hafnium Authors. ++ * ++ * Use of this source code is governed by a BSD-style ++ * license that can be found in the LICENSE file or at ++ * https://opensource.org/licenses/BSD-3-Clause. ++ */ ++ ++#include "timer_el1.h" ++ ++#include "hf/dlog.h" ++ ++#include "msr.h" ++#include "sysregs.h" ++ ++/* ++ * Physical timer (CNTP) register encodings as defined in ++ * table D13-8 of the ARMv8 ARM (DDI0487F). ++ * TYPE, op0, op1, crn, crm, op2 ++ * The register names are the concatenation of ++ * "CNTP_", TYPE and "_EL2". ++ */ ++#define CNTP_REGISTERS \ ++ X(CTL, 3, 3, 14, 2, 1) \ ++ X(CVAL, 3, 3, 14, 2, 2) \ ++ X(TVAL, 3, 3, 14, 2, 0) \ ++ ++bool timer_el1_is_register_access(uintreg_t esr) ++{ ++ uintreg_t sys_register = GET_ISS_SYSREG(esr); ++ bool is_timer_access; ++ switch (sys_register) { ++#define X(type, op0, op1, crn, crm, op2) \ ++ case (GET_ISS_ENCODING(op0, op1, crn, crm, op2)): \ ++ is_timer_access = true; \ ++ break; ++ CNTP_REGISTERS ++#undef X ++ case (GET_ISS_ENCODING(3, 3, 14, 0, 1)): ++ is_timer_access = true; ++ break; ++ default: ++ is_timer_access = false; ++ } ++ ++ return is_timer_access; ++} ++ ++/* Accesses to CNTP timer emulated with CNTHPS */ ++bool timer_el1_process_access(struct vcpu *vcpu, ffa_vm_id_t vm_id, ++ uintreg_t esr) ++{ ++ uintreg_t sys_register = GET_ISS_SYSREG(esr); ++ uintreg_t rt_register = GET_ISS_RT(esr); ++ uintreg_t value; ++ ++ if (ISS_IS_READ(esr)) { ++ switch (sys_register) { ++#define X(type, op0, op1, crn, crm, op2) \ ++ case (GET_ISS_ENCODING(op0, op1, crn, crm, op2)): \ ++ value = read_msr(MSR_CNTHPS_##type##_EL2); \ ++ vcpu->regs.r[rt_register] = value; \ ++ break; ++ CNTP_REGISTERS ++#undef X ++ case (GET_ISS_ENCODING(3, 3, 14, 0, 1)): ++ value = read_msr(cntpct_el0); ++ vcpu->regs.r[rt_register] = value; ++ break; ++ default: ++ dlog_notice( ++ "Unsupported timer register " ++ "read: " ++ "op0=%d, op1=%d, crn=%d, crm=%d, op2=%d, " ++ "rt=%d.\n", ++ GET_ISS_OP0(esr), GET_ISS_OP1(esr), ++ GET_ISS_CRN(esr), GET_ISS_CRM(esr), ++ GET_ISS_OP2(esr), GET_ISS_RT(esr)); ++ break; ++ } ++ } else { ++ value = vcpu->regs.r[rt_register]; ++ switch (sys_register) { ++#define X(type, op0, op1, crn, crm, op2) \ ++ case (GET_ISS_ENCODING(op0, op1, crn, crm, op2)): \ ++ write_msr(MSR_CNTHPS_##type##_EL2, value); \ ++ break; ++ CNTP_REGISTERS ++#undef X ++ default: ++ dlog_notice( ++ "Unsupported timer register " ++ "write: " ++ "op0=%d, op1=%d, crn=%d, crm=%d, op2=%d, " ++ "rt=%d, value=%d.\n", ++ GET_ISS_OP0(esr), GET_ISS_OP1(esr), ++ GET_ISS_CRN(esr), GET_ISS_CRM(esr), ++ GET_ISS_OP2(esr), GET_ISS_RT(esr), value); ++ break; ++ } ++ } ++ ++ return true; ++} +diff --git a/src/arch/aarch64/hypervisor/timer_el1.h b/src/arch/aarch64/hypervisor/timer_el1.h +new file mode 100644 +index 0000000..04a43b6 +--- /dev/null ++++ b/src/arch/aarch64/hypervisor/timer_el1.h +@@ -0,0 +1,20 @@ ++/* ++ * Copyright 2022 The Hafnium Authors. ++ * ++ * Use of this source code is governed by a BSD-style ++ * license that can be found in the LICENSE file or at ++ * https://opensource.org/licenses/BSD-3-Clause. ++ */ ++ ++#pragma once ++ ++#include "hf/arch/types.h" ++ ++#include "hf/cpu.h" ++ ++#include "vmapi/hf/ffa.h" ++ ++bool timer_el1_is_register_access(uintreg_t esr); ++ ++bool timer_el1_process_access(struct vcpu *vcpu, ffa_vm_id_t vm_id, ++ uintreg_t esr); +diff --git a/src/arch/aarch64/msr.h b/src/arch/aarch64/msr.h +index cd6778b..55e7833 100644 +--- a/src/arch/aarch64/msr.h ++++ b/src/arch/aarch64/msr.h +@@ -126,3 +126,11 @@ + #define MSR_ELR_EL12 S3_5_C4_C0_1 + + #endif ++ ++/* ++ * Secure EL2 Physical timer (CNTHPS) register encodings as defined in ++ * table D13-8 of the ARMv8 ARM (DDI0487F). ++ */ ++#define MSR_CNTHPS_CTL_EL2 S3_4_C14_C5_1 ++#define MSR_CNTHPS_CVAL_EL2 S3_4_C14_C5_2 ++#define MSR_CNTHPS_TVAL_EL2 S3_4_C14_C5_0 +-- +2.17.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/files/tc/0002-fix-ff-a-Use-FFA_INTERRUPT-to-signal-an-interrupted-.patch b/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/files/tc/0002-fix-ff-a-Use-FFA_INTERRUPT-to-signal-an-interrupted-.patch new file mode 100644 index 0000000000..2b57b23905 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/files/tc/0002-fix-ff-a-Use-FFA_INTERRUPT-to-signal-an-interrupted-.patch @@ -0,0 +1,152 @@ +From e6bcc390749f0560b3bc92507ecbaaabc7145200 Mon Sep 17 00:00:00 2001 +From: Lucian Paul-Trifu <lucian.paul-trifu@arm.com> +Date: Wed, 10 Mar 2021 11:31:02 +0000 +Subject: [PATCH 2/5] fix(ff-a): Use FFA_INTERRUPT to signal an interrupted + FFA_MSG_WAIT + +Rather than FFA_ERROR(INTERRUPTED). + +Change-Id: I6b23a442714852b6183e0e46af6f0504ec0ee8f4 +Signed-off-by: Ben Horgan <ben.horgan@arm.com> +Upstream-Status: Pending [Not submitted to upstream yet] +--- + src/api.c | 2 +- + test/inc/test/vmapi/ffa.h | 7 +++++++ + test/vmapi/arch/aarch64/gicv3/services/common.c | 3 +-- + test/vmapi/arch/aarch64/gicv3/services/timer.c | 2 +- + test/vmapi/el0_partitions/services/interruptible.c | 3 +-- + test/vmapi/el0_partitions/services/interruptible_echo.c | 3 +-- + test/vmapi/el0_partitions/services/receive_block.c | 2 +- + .../primary_with_secondaries/services/interruptible.c | 3 +-- + .../primary_with_secondaries/services/receive_block.c | 2 +- + 9 files changed, 15 insertions(+), 12 deletions(-) + +diff --git a/src/api.c b/src/api.c +index b713b7c..00c4d44 100644 +--- a/src/api.c ++++ b/src/api.c +@@ -1588,7 +1588,7 @@ struct ffa_value api_ffa_msg_recv(bool block, struct vcpu *current, + * received. If a message is received the return value will be set at + * that time to FFA_SUCCESS. + */ +- return_code = ffa_error(FFA_INTERRUPTED); ++ return_code = (struct ffa_value){.func = FFA_INTERRUPT_32}; + if (api_ffa_msg_recv_block_interrupted(current)) { + goto out; + } +diff --git a/test/inc/test/vmapi/ffa.h b/test/inc/test/vmapi/ffa.h +index 8fc1223..f0f3e75 100644 +--- a/test/inc/test/vmapi/ffa.h ++++ b/test/inc/test/vmapi/ffa.h +@@ -24,6 +24,13 @@ + EXPECT_EQ(ffa_error_code(v), (ffa_error)); \ + } while (0) + ++#define EXPECT_FFA_INTERRUPT(value) \ ++ do { \ ++ struct ffa_value v = (value); \ ++ EXPECT_EQ(v.func, FFA_INTERRUPT_32); \ ++ } while (0) ++ ++ + /* + * The bit 15 of the FF-A ID indicates whether the partition is executing + * in the normal world, in case it is a Virtual Machine (VM); or in the +diff --git a/test/vmapi/arch/aarch64/gicv3/services/common.c b/test/vmapi/arch/aarch64/gicv3/services/common.c +index 06df28d..4ada9e2 100644 +--- a/test/vmapi/arch/aarch64/gicv3/services/common.c ++++ b/test/vmapi/arch/aarch64/gicv3/services/common.c +@@ -22,8 +22,7 @@ struct ffa_value mailbox_receive_retry(void) + + do { + received = ffa_msg_wait(); +- } while (received.func == FFA_ERROR_32 && +- received.arg2 == FFA_INTERRUPTED); ++ } while (received.func == FFA_INTERRUPT_32); + + return received; + } +diff --git a/test/vmapi/arch/aarch64/gicv3/services/timer.c b/test/vmapi/arch/aarch64/gicv3/services/timer.c +index 156f160..d5d2816 100644 +--- a/test/vmapi/arch/aarch64/gicv3/services/timer.c ++++ b/test/vmapi/arch/aarch64/gicv3/services/timer.c +@@ -104,7 +104,7 @@ TEST_SERVICE(timer) + } else if (receive) { + struct ffa_value res = ffa_msg_wait(); + +- EXPECT_FFA_ERROR(res, FFA_INTERRUPTED); ++ EXPECT_FFA_INTERRUPT(res); + } else { + /* Busy wait until the timer fires. */ + while (!timer_fired) { +diff --git a/test/vmapi/el0_partitions/services/interruptible.c b/test/vmapi/el0_partitions/services/interruptible.c +index 0d00b16..4c9f099 100644 +--- a/test/vmapi/el0_partitions/services/interruptible.c ++++ b/test/vmapi/el0_partitions/services/interruptible.c +@@ -50,8 +50,7 @@ static struct ffa_value mailbox_receive_retry() + do { + irq(); + received = ffa_msg_wait(); +- } while (received.func == FFA_ERROR_32 && +- ffa_error_code(received) == FFA_INTERRUPTED); ++ } while (received.func == FFA_INTERRUPT_32); + + return received; + } +diff --git a/test/vmapi/el0_partitions/services/interruptible_echo.c b/test/vmapi/el0_partitions/services/interruptible_echo.c +index b618cf2..a857783 100644 +--- a/test/vmapi/el0_partitions/services/interruptible_echo.c ++++ b/test/vmapi/el0_partitions/services/interruptible_echo.c +@@ -39,8 +39,7 @@ static struct ffa_value mailbox_receive_retry() + do { + irq(); + received = ffa_msg_wait(); +- } while (received.func == FFA_ERROR_32 && +- received.arg2 == FFA_INTERRUPTED); ++ } while (received.func == FFA_INTERRUPT_32); + + return received; + } +diff --git a/test/vmapi/el0_partitions/services/receive_block.c b/test/vmapi/el0_partitions/services/receive_block.c +index 05a22f3..60da28b 100644 +--- a/test/vmapi/el0_partitions/services/receive_block.c ++++ b/test/vmapi/el0_partitions/services/receive_block.c +@@ -27,7 +27,7 @@ TEST_SERVICE(receive_block) + + for (i = 0; i < 10; ++i) { + struct ffa_value res = ffa_msg_wait(); +- EXPECT_FFA_ERROR(res, FFA_INTERRUPTED); ++ EXPECT_FFA_INTERRUPT(res); + } + + memcpy_s(SERVICE_SEND_BUFFER(), FFA_MSG_PAYLOAD_MAX, message, +diff --git a/test/vmapi/primary_with_secondaries/services/interruptible.c b/test/vmapi/primary_with_secondaries/services/interruptible.c +index cc1c1f9..005d1ff 100644 +--- a/test/vmapi/primary_with_secondaries/services/interruptible.c ++++ b/test/vmapi/primary_with_secondaries/services/interruptible.c +@@ -46,8 +46,7 @@ struct ffa_value mailbox_receive_retry() + + do { + received = ffa_msg_wait(); +- } while (received.func == FFA_ERROR_32 && +- ffa_error_code(received) == FFA_INTERRUPTED); ++ } while (received.func == FFA_INTERRUPT_32); + + return received; + } +diff --git a/test/vmapi/primary_with_secondaries/services/receive_block.c b/test/vmapi/primary_with_secondaries/services/receive_block.c +index edb4e3c..a6805ae 100644 +--- a/test/vmapi/primary_with_secondaries/services/receive_block.c ++++ b/test/vmapi/primary_with_secondaries/services/receive_block.c +@@ -40,7 +40,7 @@ TEST_SERVICE(receive_block) + + for (i = 0; i < 10; ++i) { + struct ffa_value res = ffa_msg_wait(); +- EXPECT_FFA_ERROR(res, FFA_INTERRUPTED); ++ EXPECT_FFA_INTERRUPT(res); + } + + memcpy_s(SERVICE_SEND_BUFFER(), FFA_MSG_PAYLOAD_MAX, message, +-- +2.17.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/files/tc/0003-fix-ff-a-Add-FFA_SECONDARY_EP_REGISTER_64-to-list-of.patch b/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/files/tc/0003-fix-ff-a-Add-FFA_SECONDARY_EP_REGISTER_64-to-list-of.patch new file mode 100644 index 0000000000..8d2cc13938 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/files/tc/0003-fix-ff-a-Add-FFA_SECONDARY_EP_REGISTER_64-to-list-of.patch @@ -0,0 +1,28 @@ +From a6f466c2594b2f56d34fee72494fbd29ea9c7d21 Mon Sep 17 00:00:00 2001 +From: Ben Horgan <ben.horgan@arm.com> +Date: Tue, 26 Apr 2022 12:59:42 +0000 +Subject: [PATCH 3/5] fix(ff-a): Add FFA_SECONDARY_EP_REGISTER_64 to list of + features + +Signed-off-by: Ben Horgan <ben.horgan@arm.com> +Change-Id: Ic1344eb2c982c195210dc2c86aa6845f3e037077 +Upstream-Status: Pending [Not submitted to upstream yet] +--- + src/api.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/api.c b/src/api.c +index 00c4d44..33a9b42 100644 +--- a/src/api.c ++++ b/src/api.c +@@ -2021,6 +2021,7 @@ struct ffa_value api_ffa_features(uint32_t feature_function_id) + case FFA_MEM_PERM_SET_32: + case FFA_MEM_PERM_GET_64: + case FFA_MEM_PERM_SET_64: ++ case FFA_SECONDARY_EP_REGISTER_64: + #endif + return (struct ffa_value){.func = FFA_SUCCESS_32}; + +-- +2.17.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/files/tc/0004-feat-emulate-interrupt-controller-register-access.patch b/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/files/tc/0004-feat-emulate-interrupt-controller-register-access.patch new file mode 100644 index 0000000000..95f1651a84 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/files/tc/0004-feat-emulate-interrupt-controller-register-access.patch @@ -0,0 +1,159 @@ +From 380f2cf944dd5db36c168a11d31a46ad14cdcb6d Mon Sep 17 00:00:00 2001 +From: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Date: Tue, 26 Apr 2022 14:43:58 +0100 +Subject: [PATCH 4/5] feat: emulate interrupt controller register access + +This emulates ICC_SGI1R_EL1 and ICC_IGRPEN1_EL1 register + +Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Change-Id: I0c11f034f3676067597461a183a341c809adcaa4 +Upstream-Status: Inappropriate [Experimental feature] +--- + src/arch/aarch64/hypervisor/handler.c | 5 ++ + src/arch/aarch64/hypervisor/perfmon.c | 84 +++++++++++++++++++++++++++ + src/arch/aarch64/hypervisor/perfmon.h | 5 ++ + src/arch/aarch64/msr.h | 3 + + 4 files changed, 97 insertions(+) + +diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c +index c9068c5..b9aa5d8 100644 +--- a/src/arch/aarch64/hypervisor/handler.c ++++ b/src/arch/aarch64/hypervisor/handler.c +@@ -1282,6 +1282,11 @@ void handle_system_register_access(uintreg_t esr_el2) + inject_el1_unknown_exception(vcpu, esr_el2); + return; + } ++ } else if (intr_ctrl_is_register_access(esr_el2)) { ++ if (!intr_ctrl_el1_process_access(vcpu, vm_id, esr_el2)) { ++ inject_el1_unknown_exception(vcpu, esr_el2); ++ return; ++ } + } else { + inject_el1_unknown_exception(vcpu, esr_el2); + return; +diff --git a/src/arch/aarch64/hypervisor/perfmon.c b/src/arch/aarch64/hypervisor/perfmon.c +index f13b035..05e216c 100644 +--- a/src/arch/aarch64/hypervisor/perfmon.c ++++ b/src/arch/aarch64/hypervisor/perfmon.c +@@ -116,6 +116,10 @@ + X(PMEVTYPER30_EL0 , 3, 3, 14, 15, 6) \ + X(PMCCFILTR_EL0 , 3, 3, 14, 15, 7) + ++#define INTR_CTRL_REGISTERS \ ++ X(ICC_IGRPEN1_EL1 , 3, 0, 12, 12, 7) \ ++ X(ICC_SGI1R_EL1 , 3, 0, 12, 11, 5) \ ++ + /* clang-format on */ + + /** +@@ -232,3 +236,83 @@ uintreg_t perfmon_get_pmccfiltr_el0_init_value(ffa_vm_id_t vm_id) + + return 0; + } ++ ++bool intr_ctrl_is_register_access(uintreg_t esr) ++{ ++ uintreg_t op0 = GET_ISS_OP0(esr); ++ uintreg_t op1 = GET_ISS_OP1(esr); ++ uintreg_t crn = GET_ISS_CRN(esr); ++ uintreg_t crm = GET_ISS_CRM(esr); ++ ++ if (op0 == 3 && op1 == 0 && crn == 12 && crm == 12) { ++ return true; ++ } ++ ++ if (op0 == 3 && op1 == 0 && crn == 12 && crm == 11) { ++ return true; ++ } ++ ++ return false; ++} ++ ++bool intr_ctrl_el1_process_access(struct vcpu *vcpu, ffa_vm_id_t vm_id, ++ uintreg_t esr) ++{ ++ uintreg_t sys_register = GET_ISS_SYSREG(esr); ++ uintreg_t rt_register = GET_ISS_RT(esr); ++ uintreg_t value; ++ ++ /* +1 because Rt can access register XZR */ ++ CHECK(rt_register < NUM_GP_REGS + 1); ++ ++ if (ISS_IS_READ(esr)) { ++ switch (sys_register) { ++#define X(reg_name, op0, op1, crn, crm, op2) \ ++ case (GET_ISS_ENCODING(op0, op1, crn, crm, op2)): \ ++ value = read_msr(reg_name); \ ++ break; ++ INTR_CTRL_REGISTERS ++#undef X ++ default: ++ value = vcpu->regs.r[rt_register]; ++ dlog_notice( ++ "Unsupported interrupt control register " ++ "read: " ++ "op0=%d, op1=%d, crn=%d, crm=%d, op2=%d, " ++ "rt=%d.\n", ++ GET_ISS_OP0(esr), GET_ISS_OP1(esr), ++ GET_ISS_CRN(esr), GET_ISS_CRM(esr), ++ GET_ISS_OP2(esr), GET_ISS_RT(esr)); ++ break; ++ } ++ if (rt_register != RT_REG_XZR) { ++ vcpu->regs.r[rt_register] = value; ++ } ++ } else { ++ if (rt_register != RT_REG_XZR) { ++ value = vcpu->regs.r[rt_register]; ++ } else { ++ value = 0; ++ } ++ switch (sys_register) { ++#define X(reg_name, op0, op1, crn, crm, op2) \ ++ case (GET_ISS_ENCODING(op0, op1, crn, crm, op2)): \ ++ write_msr(reg_name, value); \ ++ break; ++ INTR_CTRL_REGISTERS ++#undef X ++ default: ++ dlog_notice( ++ "Unsupported interrupt control register " ++ "write: " ++ "op0=%d, op1=%d, crn=%d, crm=%d, op2=%d, " ++ "rt=%d.\n", ++ GET_ISS_OP0(esr), GET_ISS_OP1(esr), ++ GET_ISS_CRN(esr), GET_ISS_CRM(esr), ++ GET_ISS_OP2(esr), GET_ISS_RT(esr)); ++ break; ++ } ++ } ++ ++ return true; ++} +diff --git a/src/arch/aarch64/hypervisor/perfmon.h b/src/arch/aarch64/hypervisor/perfmon.h +index 81669ba..c90d45b 100644 +--- a/src/arch/aarch64/hypervisor/perfmon.h ++++ b/src/arch/aarch64/hypervisor/perfmon.h +@@ -70,3 +70,8 @@ bool perfmon_process_access(struct vcpu *vcpu, ffa_vm_id_t vm_id, + uintreg_t esr_el2); + + uintreg_t perfmon_get_pmccfiltr_el0_init_value(ffa_vm_id_t vm_id); ++ ++bool intr_ctrl_is_register_access(uintreg_t esr); ++ ++bool intr_ctrl_el1_process_access(struct vcpu *vcpu, ffa_vm_id_t vm_id, ++ uintreg_t esr); +diff --git a/src/arch/aarch64/msr.h b/src/arch/aarch64/msr.h +index 55e7833..82aa884 100644 +--- a/src/arch/aarch64/msr.h ++++ b/src/arch/aarch64/msr.h +@@ -134,3 +134,6 @@ + #define MSR_CNTHPS_CTL_EL2 S3_4_C14_C5_1 + #define MSR_CNTHPS_CVAL_EL2 S3_4_C14_C5_2 + #define MSR_CNTHPS_TVAL_EL2 S3_4_C14_C5_0 ++ ++#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 ++#define ICC_SGI1R_EL1 S3_0_C12_C11_5 +-- +2.17.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/files/tc/0005-Revert-fix-ff-a-check-receiver-s-attributes-on-memor.patch b/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/files/tc/0005-Revert-fix-ff-a-check-receiver-s-attributes-on-memor.patch new file mode 100644 index 0000000000..e5f9489e7f --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/files/tc/0005-Revert-fix-ff-a-check-receiver-s-attributes-on-memor.patch @@ -0,0 +1,318 @@ +From 64d5628c8439e4649e9c1da9b9e02ebd5c7fb8cf Mon Sep 17 00:00:00 2001 +From: Ben Horgan <ben.horgan@arm.com> +Date: Thu, 28 Apr 2022 15:53:31 +0000 +Subject: [PATCH 5/5] Revert "fix(ff-a): check receiver's attributes on memory + retrieve" + +This reverts commit a98603aa965e3ff3ca5383249213e2fd1a96d850. + +Change-Id: Ia71ce3ac52e9b2e85578372c24eb8d593b62435f +Signed-off-by: Ben Horgan <ben.horgan@arm.com> +--- + src/ffa_memory.c | 76 ++++++----------- + test/vmapi/el0_partitions/memory_sharing.c | 81 ------------------- + .../primary_with_secondaries/memory_sharing.c | 81 ------------------- + 3 files changed, 25 insertions(+), 213 deletions(-) + +diff --git a/src/ffa_memory.c b/src/ffa_memory.c +index ab47929..2fcc386 100644 +--- a/src/ffa_memory.c ++++ b/src/ffa_memory.c +@@ -1344,42 +1344,6 @@ static struct ffa_value ffa_memory_send_complete( + return ffa_mem_success(share_state->memory_region->handle); + } + +-/** +- * Check that the memory attributes match Hafnium expectations: +- * Normal Memory, Inner shareable, Write-Back Read-Allocate +- * Write-Allocate Cacheable. +- */ +-static struct ffa_value ffa_memory_attributes_validate( +- ffa_memory_access_permissions_t attributes) +-{ +- enum ffa_memory_type memory_type; +- enum ffa_memory_cacheability cacheability; +- enum ffa_memory_shareability shareability; +- +- memory_type = ffa_get_memory_type_attr(attributes); +- if (memory_type != FFA_MEMORY_NORMAL_MEM) { +- dlog_verbose("Invalid memory type %#x, expected %#x.\n", +- memory_type, FFA_MEMORY_NORMAL_MEM); +- return ffa_error(FFA_DENIED); +- } +- +- cacheability = ffa_get_memory_cacheability_attr(attributes); +- if (cacheability != FFA_MEMORY_CACHE_WRITE_BACK) { +- dlog_verbose("Invalid cacheability %#x, expected %#x.\n", +- cacheability, FFA_MEMORY_CACHE_WRITE_BACK); +- return ffa_error(FFA_DENIED); +- } +- +- shareability = ffa_get_memory_shareability_attr(attributes); +- if (shareability != FFA_MEMORY_INNER_SHAREABLE) { +- dlog_verbose("Invalid shareability %#x, expected #%x.\n", +- shareability, FFA_MEMORY_INNER_SHAREABLE); +- return ffa_error(FFA_DENIED); +- } +- +- return (struct ffa_value){.func = FFA_SUCCESS_32}; +-} +- + /** + * Check that the given `memory_region` represents a valid memory send request + * of the given `share_func` type, return the clear flag and permissions via the +@@ -1400,7 +1364,10 @@ static struct ffa_value ffa_memory_send_validate( + uint32_t constituents_length; + enum ffa_data_access data_access; + enum ffa_instruction_access instruction_access; +- struct ffa_value ret; ++ ffa_memory_access_permissions_t attributes; ++ enum ffa_memory_type memory_type; ++ enum ffa_memory_cacheability memory_cacheability; ++ enum ffa_memory_shareability memory_shareability; + + assert(permissions != NULL); + +@@ -1536,9 +1503,26 @@ static struct ffa_value ffa_memory_send_validate( + * Normal Memory, Inner shareable, Write-Back Read-Allocate + * Write-Allocate Cacheable. + */ +- ret = ffa_memory_attributes_validate(memory_region->attributes); +- if (ret.func != FFA_SUCCESS_32) { +- return ret; ++ attributes = memory_region->attributes; ++ memory_type = ffa_get_memory_type_attr(attributes); ++ if (memory_type != FFA_MEMORY_NORMAL_MEM) { ++ dlog_verbose("Invalid memory type %#x, expected %#x.\n", ++ memory_type, FFA_MEMORY_NORMAL_MEM); ++ return ffa_error(FFA_INVALID_PARAMETERS); ++ } ++ ++ memory_cacheability = ffa_get_memory_cacheability_attr(attributes); ++ if (memory_cacheability != FFA_MEMORY_CACHE_WRITE_BACK) { ++ dlog_verbose("Invalid cacheability %#x, expected %#x.\n", ++ memory_cacheability, FFA_MEMORY_CACHE_WRITE_BACK); ++ return ffa_error(FFA_INVALID_PARAMETERS); ++ } ++ ++ memory_shareability = ffa_get_memory_shareability_attr(attributes); ++ if (memory_shareability != FFA_MEMORY_INNER_SHAREABLE) { ++ dlog_verbose("Invalid shareability %#x, expected %#x.\n", ++ memory_shareability, FFA_MEMORY_INNER_SHAREABLE); ++ return ffa_error(FFA_INVALID_PARAMETERS); + } + + return (struct ffa_value){.func = FFA_SUCCESS_32}; +@@ -2376,6 +2360,7 @@ struct ffa_value ffa_memory_retrieve(struct vm_locked to_locked, + * Check permissions from sender against permissions requested by + * receiver. + */ ++ /* TODO: Check attributes too. */ + sent_permissions = + memory_region->receivers[0].receiver_permissions.permissions; + sent_data_access = ffa_get_data_access_attr(sent_permissions); +@@ -2453,17 +2438,6 @@ struct ffa_value ffa_memory_retrieve(struct vm_locked to_locked, + panic("Got unexpected FFA_INSTRUCTION_ACCESS_RESERVED. Should " + "be checked before this point."); + } +- +- /* +- * Ensure receiver's attributes are compatible with how Hafnium maps +- * memory: Normal Memory, Inner shareable, Write-Back Read-Allocate +- * Write-Allocate Cacheable. +- */ +- ret = ffa_memory_attributes_validate(retrieve_request->attributes); +- if (ret.func != FFA_SUCCESS_32) { +- goto out; +- } +- + memory_to_attributes = ffa_memory_permissions_to_mode( + permissions, share_state->sender_orig_mode); + ret = ffa_retrieve_check_update( +diff --git a/test/vmapi/el0_partitions/memory_sharing.c b/test/vmapi/el0_partitions/memory_sharing.c +index 3756d7d..c29f029 100644 +--- a/test/vmapi/el0_partitions/memory_sharing.c ++++ b/test/vmapi/el0_partitions/memory_sharing.c +@@ -2160,87 +2160,6 @@ TEST(memory_sharing, ffa_validate_retrieve_req_mbz) + } + } + +-/** +- * Memory can't be shared with arbitrary attributes because Hafnium maps pages +- * with hardcoded values and doesn't support custom mappings. +- */ +-TEST(memory_sharing, ffa_validate_retrieve_req_attributes) +-{ +- struct ffa_value ret; +- struct mailbox_buffers mb = set_up_mailbox(); +- uint32_t msg_size; +- ffa_memory_handle_t handle; +- +- struct ffa_value (*send_function[])(uint32_t, uint32_t) = { +- ffa_mem_share, +- ffa_mem_lend, +- }; +- +- struct ffa_memory_region_constituent constituents[] = { +- {.address = (uint64_t)pages, .page_count = 2}, +- {.address = (uint64_t)pages + PAGE_SIZE * 3, .page_count = 1}, +- }; +- +- SERVICE_SELECT(SERVICE_VM1, "ffa_memory_share_fail", mb.send); +- +- struct { +- enum ffa_memory_type memory_type; +- enum ffa_memory_cacheability memory_cacheability; +- enum ffa_memory_shareability memory_shareability; +- } invalid_attributes[] = { +- /* Invalid memory type */ +- {FFA_MEMORY_DEVICE_MEM, FFA_MEMORY_CACHE_WRITE_BACK, +- FFA_MEMORY_INNER_SHAREABLE}, +- /* Invalid cacheability */ +- {FFA_MEMORY_NORMAL_MEM, FFA_MEMORY_CACHE_NON_CACHEABLE, +- FFA_MEMORY_INNER_SHAREABLE}, +- /* Invalid shareability */ +- {FFA_MEMORY_NORMAL_MEM, FFA_MEMORY_CACHE_WRITE_BACK, +- FFA_MEMORY_SHARE_NON_SHAREABLE}, +- {FFA_MEMORY_NORMAL_MEM, FFA_MEMORY_CACHE_WRITE_BACK, +- FFA_MEMORY_OUTER_SHAREABLE}}; +- +- for (uint32_t i = 0; i < ARRAY_SIZE(send_function); i++) { +- /* Prepare memory region, and set all flags */ +- EXPECT_EQ(ffa_memory_region_init( +- mb.send, HF_MAILBOX_SIZE, HF_PRIMARY_VM_ID, +- SERVICE_VM1, constituents, +- ARRAY_SIZE(constituents), 0, 0, +- FFA_DATA_ACCESS_RW, +- FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED, +- FFA_MEMORY_NORMAL_MEM, +- FFA_MEMORY_CACHE_WRITE_BACK, +- FFA_MEMORY_INNER_SHAREABLE, NULL, &msg_size), +- 0); +- +- ret = send_function[i](msg_size, msg_size); +- EXPECT_EQ(ret.func, FFA_SUCCESS_32); +- +- handle = ffa_mem_success_handle(ret); +- +- for (uint32_t j = 0; j < ARRAY_SIZE(invalid_attributes); ++j) { +- msg_size = ffa_memory_retrieve_request_init( +- mb.send, handle, HF_PRIMARY_VM_ID, SERVICE_VM1, +- 0, 0, FFA_DATA_ACCESS_RW, +- FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED, +- invalid_attributes[j].memory_type, +- invalid_attributes[j].memory_cacheability, +- invalid_attributes[j].memory_shareability); +- +- EXPECT_LE(msg_size, HF_MAILBOX_SIZE); +- +- EXPECT_EQ(ffa_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM1, +- msg_size, 0) +- .func, +- FFA_SUCCESS_32); +- +- ffa_run(SERVICE_VM1, 0); +- } +- +- EXPECT_EQ(ffa_mem_reclaim(handle, 0).func, FFA_SUCCESS_32); +- } +-} +- + /** + * If memory is shared can't request zeroing of memory at both send and + * relinquish. +diff --git a/test/vmapi/primary_with_secondaries/memory_sharing.c b/test/vmapi/primary_with_secondaries/memory_sharing.c +index 6080709..4bcf252 100644 +--- a/test/vmapi/primary_with_secondaries/memory_sharing.c ++++ b/test/vmapi/primary_with_secondaries/memory_sharing.c +@@ -2307,87 +2307,6 @@ TEST(memory_sharing, ffa_validate_retrieve_req_mbz) + } + } + +-/** +- * Memory can't be shared with arbitrary attributes because Hafnium maps pages +- * with hardcoded values and doesn't support custom mappings. +- */ +-TEST(memory_sharing, ffa_validate_retrieve_req_attributes) +-{ +- struct ffa_value ret; +- struct mailbox_buffers mb = set_up_mailbox(); +- uint32_t msg_size; +- ffa_memory_handle_t handle; +- +- struct ffa_value (*send_function[])(uint32_t, uint32_t) = { +- ffa_mem_share, +- ffa_mem_lend, +- }; +- +- struct ffa_memory_region_constituent constituents[] = { +- {.address = (uint64_t)pages, .page_count = 2}, +- {.address = (uint64_t)pages + PAGE_SIZE * 3, .page_count = 1}, +- }; +- +- SERVICE_SELECT(SERVICE_VM1, "ffa_memory_share_fail_denied", mb.send); +- +- struct { +- enum ffa_memory_type memory_type; +- enum ffa_memory_cacheability memory_cacheability; +- enum ffa_memory_shareability memory_shareability; +- } invalid_attributes[] = { +- /* Invalid memory type */ +- {FFA_MEMORY_DEVICE_MEM, FFA_MEMORY_CACHE_WRITE_BACK, +- FFA_MEMORY_INNER_SHAREABLE}, +- /* Invalid cacheability */ +- {FFA_MEMORY_NORMAL_MEM, FFA_MEMORY_CACHE_NON_CACHEABLE, +- FFA_MEMORY_INNER_SHAREABLE}, +- /* Invalid shareability */ +- {FFA_MEMORY_NORMAL_MEM, FFA_MEMORY_CACHE_WRITE_BACK, +- FFA_MEMORY_SHARE_NON_SHAREABLE}, +- {FFA_MEMORY_NORMAL_MEM, FFA_MEMORY_CACHE_WRITE_BACK, +- FFA_MEMORY_OUTER_SHAREABLE}}; +- +- for (uint32_t i = 0; i < ARRAY_SIZE(send_function); i++) { +- /* Prepare memory region, and set all flags */ +- EXPECT_EQ(ffa_memory_region_init( +- mb.send, HF_MAILBOX_SIZE, HF_PRIMARY_VM_ID, +- SERVICE_VM1, constituents, +- ARRAY_SIZE(constituents), 0, 0, +- FFA_DATA_ACCESS_RW, +- FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED, +- FFA_MEMORY_NORMAL_MEM, +- FFA_MEMORY_CACHE_WRITE_BACK, +- FFA_MEMORY_INNER_SHAREABLE, NULL, &msg_size), +- 0); +- +- ret = send_function[i](msg_size, msg_size); +- EXPECT_EQ(ret.func, FFA_SUCCESS_32); +- +- handle = ffa_mem_success_handle(ret); +- +- for (uint32_t j = 0; j < ARRAY_SIZE(invalid_attributes); ++j) { +- msg_size = ffa_memory_retrieve_request_init( +- mb.send, handle, HF_PRIMARY_VM_ID, SERVICE_VM1, +- 0, 0, FFA_DATA_ACCESS_RW, +- FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED, +- invalid_attributes[j].memory_type, +- invalid_attributes[j].memory_cacheability, +- invalid_attributes[j].memory_shareability); +- +- EXPECT_LE(msg_size, HF_MAILBOX_SIZE); +- +- EXPECT_EQ(ffa_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM1, +- msg_size, 0) +- .func, +- FFA_SUCCESS_32); +- +- ffa_run(SERVICE_VM1, 0); +- } +- +- EXPECT_EQ(ffa_mem_reclaim(handle, 0).func, FFA_SUCCESS_32); +- } +-} +- + /** + * If memory is shared can't request zeroing of memory at both send and + * relinquish. +-- +2.17.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/files/tc/0006-tc-increase-heap-pages.patch b/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/files/tc/0006-tc-increase-heap-pages.patch new file mode 100644 index 0000000000..671f6a5c04 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/files/tc/0006-tc-increase-heap-pages.patch @@ -0,0 +1,26 @@ +From e918cc5179241e1d35ba4b465b035b74b88e55d2 Mon Sep 17 00:00:00 2001 +From: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Date: Fri, 29 Apr 2022 20:07:50 +0100 +Subject: [PATCH] tc: increase heap pages + +Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +--- + project/reference/BUILD.gn | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/project/reference/BUILD.gn b/project/reference/BUILD.gn +index 5d84d13..4ea0890 100644 +--- a/project/reference/BUILD.gn ++++ b/project/reference/BUILD.gn +@@ -233,7 +233,7 @@ aarch64_toolchains("secure_tc") { + gicd_base_address = "0x30000000" + gicr_base_address = "0x30080000" + gicr_frames = 8 +- heap_pages = 60 ++ heap_pages = 120 + max_cpus = 8 + max_vms = 16 + branch_protection = "standard" +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/hafnium-tc.inc b/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/hafnium-tc.inc new file mode 100644 index 0000000000..c8f77dc6d3 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/hafnium-tc.inc @@ -0,0 +1,24 @@ +# TC specific configuration + +COMPATIBLE_MACHINE = "(tc?)" +HAFNIUM_PLATFORM = "secure_tc" + +SRCREV = "4eb7b54348234d7f4bbac64bac28f683d6574ba9" +FILESEXTRAPATHS:prepend:tc := "${THISDIR}/files/tc:" + +SRC_URI:remove = " \ + file://host-ld.patch \ + file://native-dtc.patch" + +SRC_URI:append = " \ + file://0001-feat-emulate-cntp-timer-register-accesses-using-cnth.patch \ + file://0002-fix-ff-a-Use-FFA_INTERRUPT-to-signal-an-interrupted-.patch \ + file://0003-fix-ff-a-Add-FFA_SECONDARY_EP_REGISTER_64-to-list-of.patch \ + file://0004-feat-emulate-interrupt-controller-register-access.patch \ + file://0005-Revert-fix-ff-a-check-receiver-s-attributes-on-memor.patch \ + file://0006-tc-increase-heap-pages.patch \ + " + +do_compile() { + PATH="${S}/prebuilts/linux-x64/clang/bin:$PATH" oe_runmake -C ${S} +} diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/hafnium_%.bbappend b/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/hafnium_%.bbappend new file mode 100644 index 0000000000..bfc2c464a1 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/hafnium/hafnium_%.bbappend @@ -0,0 +1,6 @@ +# Machine specific configurations + +MACHINE_HAFNIUM_REQUIRE ?= "" +MACHINE_HAFNIUM_REQUIRE:tc = "hafnium-tc.inc" + +require ${MACHINE_HAFNIUM_REQUIRE} diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/images/corstone1000-image.bb b/meta-arm/meta-arm-bsp/recipes-bsp/images/corstone1000-image.bb new file mode 100644 index 0000000000..ad5ec958fe --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/images/corstone1000-image.bb @@ -0,0 +1,13 @@ +SUMARY = "Corstone1000 platform Image" +DESCRIPTION = "This is the main image which is the container of all the binaries \ + generated for the Corstone1000 platform." +LICENSE = "MIT" + +COMPATIBLE_MACHINE = "corstone1000" + +inherit image +inherit wic_nopt + +PACKAGE_INSTALL = "" + +IMAGE_FSTYPES += "wic wic.nopt" diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/images/corstone1000-initramfs-image.bb b/meta-arm/meta-arm-bsp/recipes-bsp/images/corstone1000-initramfs-image.bb new file mode 100644 index 0000000000..b778a0080f --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/images/corstone1000-initramfs-image.bb @@ -0,0 +1,30 @@ +SUMARY = "Corstone1000 platform Initramfs Image" +DESCRIPTION = "This is the main Linux image which includes an initramfs kernel/rootfs bundle." + +LICENSE = "MIT" + +COMPATIBLE_MACHINE = "corstone1000" + +IMAGE_FSTYPES = "${INITRAMFS_FSTYPES}" + +inherit core-image + +# By default all basic packages required for a bootable system are installed +# by core-image . These packages are: packagegroup-core-boot and +# packagegroup-base-extended + +inherit image-buildinfo + +IMAGE_FEATURES += "debug-tweaks" + +#package management is not supported in corstone1000 +IMAGE_FEATURES:remove = "package-management" + +# all optee packages +IMAGE_INSTALL += "optee-client" + +# FF-A Debugfs driver +IMAGE_INSTALL += "ffa-debugfs-mod" + +# psa-arch-tests linux userspace application +IMAGE_INSTALL += "secure-partitions-psa-api-tests" diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/images/firmware-image-juno.bb b/meta-arm/meta-arm-bsp/recipes-bsp/images/firmware-image-juno.bb new file mode 100644 index 0000000000..80565af633 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/images/firmware-image-juno.bb @@ -0,0 +1,79 @@ +DESCRIPTION = "Firmware Image for Juno to be copied to the Configuration \ +microSD card" + +LICENSE = "BSD-3-Clause" +SECTION = "firmware" +LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/BSD-3-Clause;md5=550794465ba0ec5312d6919e203a55f9" + +INHIBIT_DEFAULT_DEPS = "1" +DEPENDS = "trusted-firmware-a virtual/kernel virtual/control-processor-firmware" + +PACKAGE_ARCH = "${MACHINE_ARCH}" + +COMPATIBLE_MACHINE = "juno" + +LINARO_RELEASE = "19.06" + +SRC_URI = "http://releases.linaro.org/members/arm/platforms/${LINARO_RELEASE}/juno-latest-oe-uboot.zip;subdir=${UNPACK_DIR} \ + file://images-r0.txt \ + file://images-r1.txt \ + file://images-r2.txt \ + file://uEnv.txt \ +" +SRC_URI[md5sum] = "01b662b81fa409d55ff298238ad24003" +SRC_URI[sha256sum] = "b8a3909bb3bc4350a8771b863193a3e33b358e2a727624a77c9ecf13516cec82" + +UNPACK_DIR = "juno-firmware-${LINARO_RELEASE}" + +inherit deploy nopackages + +do_configure[noexec] = "1" +do_compile[noexec] = "1" + +# The ${D} is used as a temporary directory and we don't generate any +# packages for this recipe. +do_install() { + cp -a ${WORKDIR}/${UNPACK_DIR} ${D} + cp -f ${RECIPE_SYSROOT}/firmware/bl1-juno.bin \ + ${D}/${UNPACK_DIR}/SOFTWARE/bl1.bin + + cp -f ${RECIPE_SYSROOT}/firmware/fip-juno.bin \ + ${D}/${UNPACK_DIR}/SOFTWARE/fip.bin + + cp -f ${RECIPE_SYSROOT}/firmware/scp_romfw_bypass.bin \ + ${D}/${UNPACK_DIR}/SOFTWARE/scp_bl1.bin + + # u-boot environment file + cp -f ${WORKDIR}/uEnv.txt ${D}/${UNPACK_DIR}/SOFTWARE/ + + # Juno images list file + cp -f ${WORKDIR}/images-r0.txt ${D}/${UNPACK_DIR}/SITE1/HBI0262B/images.txt + cp -f ${WORKDIR}/images-r1.txt ${D}/${UNPACK_DIR}/SITE1/HBI0262C/images.txt + cp -f ${WORKDIR}/images-r2.txt ${D}/${UNPACK_DIR}/SITE1/HBI0262D/images.txt +} + +do_deploy() { + # To avoid dependency loop between firmware-image-juno:do_install + # and virtual/kernel:do_deploy when INITRAMFS_IMAGE_BUNDLE = "1", + # we need to handle the kernel binaries copying in the do_deploy + # task. + for f in ${KERNEL_DEVICETREE}; do + install -m 755 -c ${DEPLOY_DIR_IMAGE}/$(basename $f) \ + ${D}/${UNPACK_DIR}/SOFTWARE/. + done + + if [ "${INITRAMFS_IMAGE_BUNDLE}" -eq 1 ]; then + cp -L -f ${DEPLOY_DIR_IMAGE}/Image-initramfs-juno.bin \ + ${D}/${UNPACK_DIR}/SOFTWARE/Image + else + cp -L -f ${DEPLOY_DIR_IMAGE}/Image ${D}/${UNPACK_DIR}/SOFTWARE/ + fi + + # Compress the files + tar -C ${D}/${UNPACK_DIR} -zcvf ${WORKDIR}/${PN}.tar.gz ./ + + # Deploy the compressed archive to the deploy folder + install -D -p -m0644 ${WORKDIR}/${PN}.tar.gz ${DEPLOYDIR}/${PN}.tar.gz +} +do_deploy[depends] += "virtual/kernel:do_deploy" +addtask deploy after do_install diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/images/firmware-image-juno/images-r0.txt b/meta-arm/meta-arm-bsp/recipes-bsp/images/firmware-image-juno/images-r0.txt new file mode 100644 index 0000000000..3b36ed1349 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/images/firmware-image-juno/images-r0.txt @@ -0,0 +1,71 @@ +TITLE: Versatile Express Images Configuration File + +[IMAGES] +TOTALIMAGES: 10 ;Number of Images (Max: 32) + +NOR0UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR0ADDRESS: 0x00000000 ;Image Flash Address +NOR0FILE: \SOFTWARE\fip.bin ;Image File Name +NOR0LOAD: 00000000 ;Image Load Address +NOR0ENTRY: 00000000 ;Image Entry Point + +NOR1UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR1ADDRESS: 0x03EC0000 ;Image Flash Address +NOR1FILE: \SOFTWARE\bl1.bin ;Image File Name +NOR1LOAD: 00000000 ;Image Load Address +NOR1ENTRY: 00000000 ;Image Entry Point + +NOR2UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR2ADDRESS: 0x00500000 ;Image Flash Address +NOR2FILE: \SOFTWARE\Image ;Image File Name +NOR2NAME: norkern ;Rename kernel to norkern +NOR2LOAD: 00000000 ;Image Load Address +NOR2ENTRY: 00000000 ;Image Entry Point + +NOR3UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR3ADDRESS: 0x03000000 ;Image Flash Address +NOR3FILE: \SOFTWARE\juno.dtb ;Image File Name +NOR3NAME: board.dtb ;Specify target filename to preserve file extension +NOR3LOAD: 00000000 ;Image Load Address +NOR3ENTRY: 00000000 ;Image Entry Point + +NOR4UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR4ADDRESS: 0x030C0000 ;Image Flash Address +NOR4FILE: \SOFTWARE\hdlcdclk.dat ;Image File Name +NOR4LOAD: 00000000 ;Image Load Address +NOR4ENTRY: 00000000 ;Image Entry Point + +NOR5UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR5ADDRESS: 0x03E40000 ;Image Flash Address +NOR5FILE: \SOFTWARE\scp_bl1.bin ;Image File Name +NOR5LOAD: 00000000 ;Image Load Address +NOR5ENTRY: 00000000 ;Image Entry Point + +NOR6UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR6ADDRESS: 0x0BF00000 ;Image Flash Address +NOR6FILE: \SOFTWARE\startup.nsh ;Image File Name +NOR6NAME: startup.nsh +NOR6LOAD: 00000000 ;Image Load Address +NOR6ENTRY: 00000000 ;Image Entry Point + +NOR7UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR7ADDRESS: 0x0BFC0000 ;Image Flash Address +NOR7FILE: \SOFTWARE\blank.img ;Image File Name +NOR7NAME: BOOTENV +NOR7LOAD: 00000000 ;Image Load Address +NOR7ENTRY: 00000000 ;Image Entry Point + +NOR8UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR8ADDRESS: 0x03100000 ;Image Flash Address +NOR8FILE: \SOFTWARE\selftest ;Image File Name +NOR8LOAD: 00000000 ;Image Load Address +NOR8ENTRY: 00000000 ;Image Entry Point + +NOR9UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR9ADDRESS: 0x03180000 ;Image Flash Address +NOR9NAME: uEnv.txt +NOR9FILE: \SOFTWARE\uEnv.txt ;Image File Name +NOR9LOAD: 00000000 ;Image Load Address +NOR9ENTRY: 00000000 ;Image Entry Point + + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/images/firmware-image-juno/images-r1.txt b/meta-arm/meta-arm-bsp/recipes-bsp/images/firmware-image-juno/images-r1.txt new file mode 100644 index 0000000000..5db13affaf --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/images/firmware-image-juno/images-r1.txt @@ -0,0 +1,71 @@ +TITLE: Versatile Express Images Configuration File + +[IMAGES] +TOTALIMAGES: 10 ;Number of Images (Max: 32) + +NOR0UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR0ADDRESS: 0x00000000 ;Image Flash Address +NOR0FILE: \SOFTWARE\fip.bin ;Image File Name +NOR0LOAD: 00000000 ;Image Load Address +NOR0ENTRY: 00000000 ;Image Entry Point + +NOR1UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR1ADDRESS: 0x03EC0000 ;Image Flash Address +NOR1FILE: \SOFTWARE\bl1.bin ;Image File Name +NOR1LOAD: 00000000 ;Image Load Address +NOR1ENTRY: 00000000 ;Image Entry Point + +NOR2UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR2ADDRESS: 0x00500000 ;Image Flash Address +NOR2FILE: \SOFTWARE\Image ;Image File Name +NOR2NAME: norkern ;Rename kernel to norkern +NOR2LOAD: 00000000 ;Image Load Address +NOR2ENTRY: 00000000 ;Image Entry Point + +NOR3UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR3ADDRESS: 0x03000000 ;Image Flash Address +NOR3FILE: \SOFTWARE\juno-r1.dtb ;Image File Name +NOR3NAME: board.dtb ;Specify target filename to preserve file extension +NOR3LOAD: 00000000 ;Image Load Address +NOR3ENTRY: 00000000 ;Image Entry Point + +NOR4UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR4ADDRESS: 0x030C0000 ;Image Flash Address +NOR4FILE: \SOFTWARE\hdlcdclk.dat ;Image File Name +NOR4LOAD: 00000000 ;Image Load Address +NOR4ENTRY: 00000000 ;Image Entry Point + +NOR5UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR5ADDRESS: 0x03E40000 ;Image Flash Address +NOR5FILE: \SOFTWARE\scp_bl1.bin ;Image File Name +NOR5LOAD: 00000000 ;Image Load Address +NOR5ENTRY: 00000000 ;Image Entry Point + +NOR6UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR6ADDRESS: 0x0BF00000 ;Image Flash Address +NOR6FILE: \SOFTWARE\startup.nsh ;Image File Name +NOR6NAME: startup.nsh +NOR6LOAD: 00000000 ;Image Load Address +NOR6ENTRY: 00000000 ;Image Entry Point + +NOR7UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR7ADDRESS: 0x0BFC0000 ;Image Flash Address +NOR7FILE: \SOFTWARE\blank.img ;Image File Name +NOR7NAME: BOOTENV +NOR7LOAD: 00000000 ;Image Load Address +NOR7ENTRY: 00000000 ;Image Entry Point + +NOR8UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR8ADDRESS: 0x03100000 ;Image Flash Address +NOR8FILE: \SOFTWARE\selftest ;Image File Name +NOR8LOAD: 00000000 ;Image Load Address +NOR8ENTRY: 00000000 ;Image Entry Point + +NOR9UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR9ADDRESS: 0x03180000 ;Image Flash Address +NOR9NAME: uEnv.txt +NOR9FILE: \SOFTWARE\uEnv.txt ;Image File Name +NOR9LOAD: 00000000 ;Image Load Address +NOR9ENTRY: 00000000 ;Image Entry Point + + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/images/firmware-image-juno/images-r2.txt b/meta-arm/meta-arm-bsp/recipes-bsp/images/firmware-image-juno/images-r2.txt new file mode 100644 index 0000000000..7c499bfbda --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/images/firmware-image-juno/images-r2.txt @@ -0,0 +1,71 @@ +TITLE: Versatile Express Images Configuration File + +[IMAGES] +TOTALIMAGES: 10 ;Number of Images (Max: 32) + +NOR0UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR0ADDRESS: 0x00000000 ;Image Flash Address +NOR0FILE: \SOFTWARE\fip.bin ;Image File Name +NOR0LOAD: 00000000 ;Image Load Address +NOR0ENTRY: 00000000 ;Image Entry Point + +NOR1UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR1ADDRESS: 0x03EC0000 ;Image Flash Address +NOR1FILE: \SOFTWARE\bl1.bin ;Image File Name +NOR1LOAD: 00000000 ;Image Load Address +NOR1ENTRY: 00000000 ;Image Entry Point + +NOR2UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR2ADDRESS: 0x00500000 ;Image Flash Address +NOR2FILE: \SOFTWARE\Image ;Image File Name +NOR2NAME: norkern ;Rename kernel to norkern +NOR2LOAD: 00000000 ;Image Load Address +NOR2ENTRY: 00000000 ;Image Entry Point + +NOR3UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR3ADDRESS: 0x03000000 ;Image Flash Address +NOR3FILE: \SOFTWARE\juno-r2.dtb ;Image File Name +NOR3NAME: board.dtb ;Specify target filename to preserve file extension +NOR3LOAD: 00000000 ;Image Load Address +NOR3ENTRY: 00000000 ;Image Entry Point + +NOR4UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR4ADDRESS: 0x030C0000 ;Image Flash Address +NOR4FILE: \SOFTWARE\hdlcdclk.dat ;Image File Name +NOR4LOAD: 00000000 ;Image Load Address +NOR4ENTRY: 00000000 ;Image Entry Point + +NOR5UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR5ADDRESS: 0x03E40000 ;Image Flash Address +NOR5FILE: \SOFTWARE\scp_bl1.bin ;Image File Name +NOR5LOAD: 00000000 ;Image Load Address +NOR5ENTRY: 00000000 ;Image Entry Point + +NOR6UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR6ADDRESS: 0x0BF00000 ;Image Flash Address +NOR6FILE: \SOFTWARE\startup.nsh ;Image File Name +NOR6NAME: startup.nsh +NOR6LOAD: 00000000 ;Image Load Address +NOR6ENTRY: 00000000 ;Image Entry Point + +NOR7UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR7ADDRESS: 0x0BFC0000 ;Image Flash Address +NOR7FILE: \SOFTWARE\blank.img ;Image File Name +NOR7NAME: BOOTENV +NOR7LOAD: 00000000 ;Image Load Address +NOR7ENTRY: 00000000 ;Image Entry Point + +NOR8UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR8ADDRESS: 0x03100000 ;Image Flash Address +NOR8FILE: \SOFTWARE\selftest ;Image File Name +NOR8LOAD: 00000000 ;Image Load Address +NOR8ENTRY: 00000000 ;Image Entry Point + +NOR9UPDATE: AUTO ;Image Update:NONE/AUTO/FORCE +NOR9ADDRESS: 0x03180000 ;Image Flash Address +NOR9NAME: uEnv.txt +NOR9FILE: \SOFTWARE\uEnv.txt ;Image File Name +NOR9LOAD: 00000000 ;Image Load Address +NOR9ENTRY: 00000000 ;Image Entry Point + + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/images/firmware-image-juno/uEnv.txt b/meta-arm/meta-arm-bsp/recipes-bsp/images/firmware-image-juno/uEnv.txt new file mode 100644 index 0000000000..77c02e3677 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/images/firmware-image-juno/uEnv.txt @@ -0,0 +1,11 @@ +uenvcmd=run mybootcmd +mybootcmd=echo Loading custom boot command; \ +echo Loading kernel; \ +afs load ${kernel_name} ${kernel_addr_r} ; \ +if test $? -eq 1; then echo Loading ${kernel_alt_name} instead of ${kernel_name}; afs load ${kernel_alt_name} ${kernel_addr_r}; fi; \ +echo Loading device tree; \ +afs load ${fdtfile} ${fdt_addr_r}; \ +if test $? -eq 1; then echo Loading ${fdt_alt_name} instead of ${fdtfile}; \ +afs load ${fdt_alt_name} ${fdt_addr_r}; fi; fdt addr ${fdt_addr_r}; fdt resize; \ +booti ${kernel_addr_r} - ${fdt_addr_r}; + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/images/n1sdp-board-firmware_2021.10.12.bb b/meta-arm/meta-arm-bsp/recipes-bsp/images/n1sdp-board-firmware_2021.10.12.bb new file mode 100644 index 0000000000..dda99e74c1 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/images/n1sdp-board-firmware_2021.10.12.bb @@ -0,0 +1,35 @@ +SUMMARY = "Board Firmware binaries for N1SDP" +SECTION = "firmware" + +LICENSE = "STM-SLA0044-Rev5" +LIC_FILES_CHKSUM = "file://LICENSES/STM.TXT;md5=cd18335eff80d0a690a650f0e6748baf" + +inherit deploy + +INHIBIT_DEFAULT_DEPS = "1" + +PACKAGE_ARCH = "${MACHINE_ARCH}" +COMPATIBLE_MACHINE = "n1sdp" + +SRC_URI = "git://git.gitlab.arm.com/arm-reference-solutions/board-firmware.git;protocol=https;branch=n1sdp" + +SRCREV = "e6cd91c7a9733e501bc3b57ff6f9eb2461ffee54" + +S = "${WORKDIR}/git" + +INSTALL_DIR = "/n1sdp-board-firmware_source" + +do_install() { + rm -rf ${S}/SOFTWARE + install -d ${D}${INSTALL_DIR} + cp -Rp --no-preserve=ownership ${S}/* ${D}${INSTALL_DIR} +} + +FILES:${PN} = "${INSTALL_DIR}" +SYSROOT_DIRS += "${INSTALL_DIR}" + +do_deploy() { + install -d ${DEPLOYDIR}${INSTALL_DIR} + cp -Rp --no-preserve=ownership ${S}/* ${DEPLOYDIR}${INSTALL_DIR} +} +addtask deploy after do_install before do_build diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/images/sdcard-image-n1sdp_0.1.bb b/meta-arm/meta-arm-bsp/recipes-bsp/images/sdcard-image-n1sdp_0.1.bb new file mode 100644 index 0000000000..3ed71c57d2 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/images/sdcard-image-n1sdp_0.1.bb @@ -0,0 +1,85 @@ +SUMMARY = "Firmware image recipe for generating SD-Card artifacts." + +inherit deploy nopackages + +DEPENDS = "trusted-firmware-a \ + virtual/control-processor-firmware \ + n1sdp-board-firmware" + +LICENSE = "MIT" +PACKAGE_ARCH = "${MACHINE_ARCH}" +COMPATIBLE_MACHINE = "n1sdp" +RM_WORK_EXCLUDE += "${PN}" +do_configure[noexec] = "1" +do_compile[noexec] = "1" +do_install[noexec] = "1" + +FIRMWARE_DIR = "n1sdp-board-firmware_source" +PRIMARY_DIR = "${WORKDIR}/n1sdp-board-firmware_primary" +SECONDARY_DIR = "${WORKDIR}/n1sdp-board-firmware_secondary" + +SOC_BINARIES = "mcp_fw.bin scp_fw.bin mcp_rom.bin scp_rom.bin" + +prepare_package() { + cd ${WORKDIR} + + # Master/Primary + cp -av ${RECIPE_SYSROOT}/${FIRMWARE_DIR}/* ${PRIMARY_DIR} + mkdir -p ${PRIMARY_DIR}/SOFTWARE/ + + # Copy FIP binary + cp -v ${RECIPE_SYSROOT}/firmware/fip.bin ${PRIMARY_DIR}/SOFTWARE/ + + # Copy SOC binaries + for f in ${SOC_BINARIES}; do + cp -v ${RECIPE_SYSROOT}/firmware/${f} ${PRIMARY_DIR}/SOFTWARE/ + done + + sed -i -e 's|^C2C_ENABLE.*|C2C_ENABLE: TRUE ;C2C enable TRUE/FALSE|' \ + ${PRIMARY_DIR}/MB/HBI0316A/io_v123f.txt + sed -i -e 's|^C2C_SIDE.*|C2C_SIDE: MASTER ;C2C side SLAVE/MASTER|' \ + ${PRIMARY_DIR}/MB/HBI0316A/io_v123f.txt + sed -i -e 's|.*SOCCON: 0x1170.*PLATFORM_CTRL.*|SOCCON: 0x1170 0x00000100 ;SoC SCC PLATFORM_CTRL|' \ + ${PRIMARY_DIR}/MB/HBI0316A/io_v123f.txt + + # Update load address for trusted boot + sed -i -e '/^IMAGE4ADDRESS:/ s|0x60200000|0x64200000|' ${PRIMARY_DIR}/MB/HBI0316A/images.txt + sed -i -e '/^IMAGE4UPDATE:/ s|FORCE |SCP_AUTO|' ${PRIMARY_DIR}/MB/HBI0316A/images.txt + sed -i -e '/^IMAGE4FILE: \\SOFTWARE\\/s|uefi.bin|fip.bin |' ${PRIMARY_DIR}/MB/HBI0316A/images.txt + + # Slave/Secondary + cp -av ${RECIPE_SYSROOT}/${FIRMWARE_DIR}/* ${SECONDARY_DIR} + mkdir -p ${SECONDARY_DIR}/SOFTWARE/ + + # Copy SOC binaries + for f in ${SOC_BINARIES}; do + cp -v ${RECIPE_SYSROOT}/firmware/${f} ${SECONDARY_DIR}/SOFTWARE/ + done + + sed -i -e 's|^C2C_ENABLE.*|C2C_ENABLE: TRUE ;C2C enable TRUE/FALSE|' \ + ${SECONDARY_DIR}/MB/HBI0316A/io_v123f.txt + sed -i -e 's|^C2C_SIDE.*|C2C_SIDE: SLAVE ;C2C side SLAVE/MASTER|' \ + ${SECONDARY_DIR}/MB/HBI0316A/io_v123f.txt + sed -i -e 's|.*SOCCON: 0x1170.*PLATFORM_CTRL.*|SOCCON: 0x1170 0x00000101 ;SoC SCC PLATFORM_CTRL|' \ + ${SECONDARY_DIR}/MB/HBI0316A/io_v123f.txt + sed -i -e '/^TOTALIMAGES:/ s|5|4|' ${SECONDARY_DIR}/MB/HBI0316A/images.txt + sed -i -e 's|^IMAGE4|;&|' ${SECONDARY_DIR}/MB/HBI0316A/images.txt +} + +do_deploy() { + # prepare Master & Slave packages + prepare_package + + for dir in ${PRIMARY_DIR} ${SECONDARY_DIR}; do + dir_name=$(basename ${dir}) + mkdir -p ${D}/${dir_name} + cp -av ${dir} ${D} + + # Compress the files + tar -C ${D}/${dir_name} -zcvf ${DEPLOYDIR}/${dir_name}.tar.gz ./ + done +} +do_deploy[dirs] += "${PRIMARY_DIR} ${SECONDARY_DIR}" +do_deploy[cleandirs] += "${PRIMARY_DIR} ${SECONDARY_DIR}" +do_deploy[umask] = "022" +addtask deploy after do_prepare_recipe_sysroot diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/images/tc-artifacts-image.bb b/meta-arm/meta-arm-bsp/recipes-bsp/images/tc-artifacts-image.bb new file mode 100644 index 0000000000..ded7404b57 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/images/tc-artifacts-image.bb @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2020 Arm Limited +# +SUMMARY = "Total Compute Images" +DESCRIPTION = "Build all the images required for Total Compute platform" +LICENSE = "Apache-2.0" + +COMPATIBLE_MACHINE = "(tc?)" + +inherit nopackages + +# The last image to be built is trusted-firmware-a +DEPENDS += " trusted-firmware-a" diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/files/tc/0002-tc0-fix-mpmm-config.patch b/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/files/tc/0002-tc0-fix-mpmm-config.patch new file mode 100644 index 0000000000..f2044a9539 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/files/tc/0002-tc0-fix-mpmm-config.patch @@ -0,0 +1,92 @@ +From 736bd8aeceefd474c15a97e4a4ec99f07ef9a82c Mon Sep 17 00:00:00 2001 +From: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Date: Fri, 11 Feb 2022 18:28:43 +0000 +Subject: [PATCH 2/4] tc0: fix mpmm config + +Do not enable MPMM in standard features set. + +Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Change-Id: I7b273a2055452e2e8cd78a0d932514a6f2947ec5 +Upstream-Status: Pending [Not submitted to upstream yet] +--- + product/tc0/scp_ramfw/config_mpmm.c | 15 --------------- + 1 file changed, 15 deletions(-) + +diff --git a/product/tc0/scp_ramfw/config_mpmm.c b/product/tc0/scp_ramfw/config_mpmm.c +index 3bfe99d3..13d866a5 100644 +--- a/product/tc0/scp_ramfw/config_mpmm.c ++++ b/product/tc0/scp_ramfw/config_mpmm.c +@@ -27,7 +27,6 @@ enum core_pd_idx { + CORE7_IDX + }; + +-#if defined(PLATFORM_VARIANT) && (PLATFORM_VARIANT == TC0_VARIANT_STD) + static struct mod_mpmm_pct_table k_pct[] = { + { .cores_online = 4, + .default_perf_limit = 1153 * 1000000UL, +@@ -115,7 +114,6 @@ static struct mod_mpmm_pct_table m_pct[] = { + }, + } }, + }; +-#endif + + static struct mod_mpmm_pct_table m_elp_pct[] = { + { .cores_online = 1, +@@ -132,7 +130,6 @@ static struct mod_mpmm_pct_table m_elp_pct[] = { + } }, + }; + +-#if defined(PLATFORM_VARIANT) && (PLATFORM_VARIANT == TC0_VARIANT_STD) + static const struct mod_mpmm_core_config k_core_config[] = { + [0] = { + .pd_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_POWER_DOMAIN, CORE0_IDX), +@@ -180,7 +177,6 @@ static const struct mod_mpmm_core_config m_core_config[] = { + .core_starts_online = false, + }, + }; +-#endif + + static const struct mod_mpmm_core_config m_elp_core_config[] = { + [0] = { +@@ -191,7 +187,6 @@ static const struct mod_mpmm_core_config m_elp_core_config[] = { + }, + }; + +-#if defined(PLATFORM_VARIANT) && (PLATFORM_VARIANT == TC0_VARIANT_STD) + static const struct mod_mpmm_domain_config k_domain_conf[] = { + [0] = { + .perf_id = FWK_ID_ELEMENT_INIT( +@@ -219,7 +214,6 @@ static const struct mod_mpmm_domain_config m_domain_conf[] = { + }, + [1] = {0}, + }; +-#endif + + static const struct mod_mpmm_domain_config m_elp_domain_conf[] = { + [0] = { +@@ -236,14 +230,6 @@ static const struct mod_mpmm_domain_config m_elp_domain_conf[] = { + }; + + static const struct fwk_element element_table[] = { +-#if defined(PLATFORM_VARIANT) && (PLATFORM_VARIANT == TC0_VAR_EXPERIMENT_POWER) +- [0] = { +- .name = "MPMM_MATTERHORN_ELP_ARM_ELEM", +- .sub_element_count = 1, +- .data = m_elp_domain_conf, +- }, +- [1] = { 0 }, +-#else + [0] = { + .name = "MPMM_KLEIN_ELEM", + .sub_element_count = 4, +@@ -260,7 +246,6 @@ static const struct fwk_element element_table[] = { + .data = m_elp_domain_conf, + }, + [3] = { 0 }, +-#endif + }; + + static const struct fwk_element *mpmm_get_element_table(fwk_id_t module_id) +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/files/tc/0003-tc0-rename-platform-variant-to-platform-feature-set.patch b/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/files/tc/0003-tc0-rename-platform-variant-to-platform-feature-set.patch new file mode 100644 index 0000000000..87dfbfacf1 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/files/tc/0003-tc0-rename-platform-variant-to-platform-feature-set.patch @@ -0,0 +1,203 @@ +From 50e63f11762348bcd95d809af248f620f03d9ce4 Mon Sep 17 00:00:00 2001 +From: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Date: Fri, 11 Feb 2022 18:16:54 +0000 +Subject: [PATCH 3/4] tc0: rename platform variant to platform feature set + +THe PLATFORM_VARIANT flag was added to differentiate the software +features enabled in SCP firmware. But this flag misleads to a new +variant of same platform. This commits renames PLATFORM_VARIANT to +PLATFORM_FEATURE_SET + +Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Change-Id: I93c0bc3e11fe18192bb8246df851345bdc473974 +Upstream-Status: Pending [Not submitted to upstream yet] +Signed-off-by: Rupinderjit Singh <rupinderjit.singh@arm.com> +--- + product/tc0/doc/{variants.md => features.md} | 28 +++++++++----------- + product/tc0/scp_ramfw/CMakeLists.txt | 26 +++--------------- + product/tc0/scp_ramfw/Firmware.cmake | 2 +- + product/tc0/scp_ramfw/config_scmi_perf.c | 8 +++--- + product/tc0/scp_romfw/CMakeLists.txt | 6 ++--- + product/tc0/scp_romfw/Firmware.cmake | 2 +- + 6 files changed, 25 insertions(+), 47 deletions(-) + rename product/tc0/doc/{variants.md => features.md} (77%) + +diff --git a/product/tc0/doc/variants.md b/product/tc0/doc/features.md +similarity index 77% +rename from product/tc0/doc/variants.md +rename to product/tc0/doc/features.md +index fbf616db..3ef520e2 100644 +--- a/product/tc0/doc/variants.md ++++ b/product/tc0/doc/features.md +@@ -1,4 +1,4 @@ +-# TC0 Platform Variants ++# TC0 Platform Features + + Copyright (c) 2022, Arm Limited. All rights reserved. + +@@ -7,30 +7,27 @@ Copyright (c) 2022, Arm Limited. All rights reserved. + + Documentation for TC0 platform can be found at [1]. + ++### Standard ++ ++The standard build provides all the features described in [1]. ++For this default features, it's not required to provide any extra parameters in ++the build commands. ++ ++### MPMM/Power/Performance (Experimental) ++ + For the purpose of experimenting some of the software features that have been +-introduced in SCP-firmware a new variant of TC0 has been created. +-The variant(s) can be chosen at build time by adding: ++introduced in SCP-firmware of TC0. This can be enabled at build time, by adding: + + ```sh + + make -f Makefile.cmake \ + PRODUCT=tc0 \ + MODE=<debug,release> \ +- PLATFORM_VARIANT=<0,1> ++ EXTRA_CONFIG_ARGS+=-DSCP_PLATFORM_FEATURE_SET=1 + + ``` + +- +-### Variant 0 (Standard build) +- +-The standard build provides all the features described in [1]. +-For this default variant, it's not required to provide any extra parameters in +-the build commands. +- +- +-### Variant 1 (Power/Performance testing) +- +-This variant adds support for the following software features: ++This adds support for the following software features: + - Traffic Cop + - MPMM (Maximum Power Mitigation Mechanism) + - Thermal Management +@@ -63,7 +60,6 @@ Once built, the features above will act as: + + ## Limitations + +-- The "variant" option is available only with the CMake build. + - The Thermal functionality is limited at this time cause the constant + temperature being sampled. + +diff --git a/product/tc0/scp_ramfw/CMakeLists.txt b/product/tc0/scp_ramfw/CMakeLists.txt +index 96310320..ce3178ee 100644 +--- a/product/tc0/scp_ramfw/CMakeLists.txt ++++ b/product/tc0/scp_ramfw/CMakeLists.txt +@@ -11,25 +11,13 @@ + + add_executable(tc0-bl2) + ++set(SCP_PLATFORM_FEATURE_SET ${SCP_PLATFORM_FEATURE_SET_INIT} CACHE STRING "1") + +-# SCP_PLATFORM_VARIANT options: +-# - 'TC0_VARIANT_STD' for TC0 standard build +-# - 'TC0_VAR_EXPERIMENT_POWER' for TC0 with power/performance plugins used for +-# evaluation purposes +- +- +-target_compile_definitions(tc0-bl2 PUBLIC -DTC0_VARIANT_STD=0) +-target_compile_definitions(tc0-bl2 PUBLIC -DTC0_VAR_EXPERIMENT_POWER=1) +- +- +-set(SCP_PLATFORM_VARIANT ${SCP_PLATFORM_VARIANT_INIT} CACHE STRING "1") +- +- +-if (SCP_PLATFORM_VARIANT STREQUAL "1") +- message(NOTICE "SCP_PLATFORM_VARIANT set to EXPERIMENT_POWER (tc0-bl2)\n") ++if (SCP_PLATFORM_FEATURE_SET STREQUAL "1") ++ message(NOTICE "TC0 platform features MPMM/POWER/PERFORMANCE is experimental (tc0-bl2)\n") + + target_compile_definitions(tc0-bl2 +- PUBLIC -DPLATFORM_VARIANT=TC0_VAR_EXPERIMENT_POWER) ++ PUBLIC -DTC0_FEATURES_MPMM_POWER_PERF) + + set(SCP_ENABLE_PLUGIN_HANDLER TRUE PARENT_SCOPE) + set(SCP_ENABLE_FAST_CHANNELS TRUE PARENT_SCOPE) +@@ -56,12 +44,6 @@ if (SCP_PLATFORM_VARIANT STREQUAL "1") + list(PREPEND SCP_MODULE_PATHS + "${CMAKE_CURRENT_LIST_DIR}/../module/tc0_power_model") + target_sources(tc0-bl2 PRIVATE "config_tc0_power_model.c") +- +-else() +- message(NOTICE "SCP_PLATFORM_VARIANT set to STANDARD (tc0-bl2)\n") +- +- target_compile_definitions(tc0-bl2 +- PUBLIC -DPLATFORM_VARIANT=TC0_VARIANT_STD) + endif() + + +diff --git a/product/tc0/scp_ramfw/Firmware.cmake b/product/tc0/scp_ramfw/Firmware.cmake +index 11d8eaab..4a555296 100644 +--- a/product/tc0/scp_ramfw/Firmware.cmake ++++ b/product/tc0/scp_ramfw/Firmware.cmake +@@ -27,7 +27,7 @@ set(SCP_ENABLE_FAST_CHANNELS_INIT FALSE) + + set(SCP_ENABLE_PLUGIN_HANDLER_INIT FALSE) + +-set(SCP_PLATFORM_VARIANT_INIT 0) ++set(SCP_PLATFORM_FEATURE_SET_INIT 0) + + set(SCP_ARCHITECTURE "armv7-m") + +diff --git a/product/tc0/scp_ramfw/config_scmi_perf.c b/product/tc0/scp_ramfw/config_scmi_perf.c +index a4a47b3a..3e91939a 100644 +--- a/product/tc0/scp_ramfw/config_scmi_perf.c ++++ b/product/tc0/scp_ramfw/config_scmi_perf.c +@@ -129,7 +129,7 @@ static const struct mod_scmi_perf_domain_config domains[] = { + }, + }; + +-#if defined(PLATFORM_VARIANT) && (PLATFORM_VARIANT == TC0_VAR_EXPERIMENT_POWER) ++#ifdef TC0_FEATURES_MPMM_POWER_PERF + static const struct mod_scmi_plugin_config plugins_table[] = { + [0] = { + .id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_TRAFFIC_COP), +@@ -156,9 +156,9 @@ const struct fwk_module_config config_scmi_perf = { + #else + .fast_channels_alarm_id = FWK_ID_NONE_INIT, + #endif +-#if defined(PLATFORM_VARIANT) && (PLATFORM_VARIANT == TC0_VAR_EXPERIMENT_POWER) +- .plugins = plugins_table, +- .plugins_count = FWK_ARRAY_SIZE(plugins_table), ++#ifdef TC0_FEATURES_MPMM_POWER_PERF ++ .plugins = plugins_table, ++ .plugins_count = FWK_ARRAY_SIZE(plugins_table), + #endif + }) + }; +diff --git a/product/tc0/scp_romfw/CMakeLists.txt b/product/tc0/scp_romfw/CMakeLists.txt +index f9f40ad3..09cd2f5d 100644 +--- a/product/tc0/scp_romfw/CMakeLists.txt ++++ b/product/tc0/scp_romfw/CMakeLists.txt +@@ -48,6 +48,6 @@ target_include_directories(tc0-bl1 + PUBLIC $<TARGET_PROPERTY:cmsis::core-m,INTERFACE_INCLUDE_DIRECTORIES>) + + cmake_dependent_option( +- SCP_PLATFORM_VARIANT "Choose platform software variant?" +- "${SCP_PLATFORM_VARIANT_INIT}" "DEFINED SCP_PLATFORM_VARIANT_INIT" +- "${SCP_PLATFORM_VARIANT}") ++ SCP_PLATFORM_FEATURE_SET "Choose platform software features?" ++ "${SCP_PLATFORM_FEATURE_SET_INIT}" "DEFINED SCP_PLATFORM_FEATURE_SET_INIT" ++ "${SCP_PLATFORM_FEATURE_SET}") +diff --git a/product/tc0/scp_romfw/Firmware.cmake b/product/tc0/scp_romfw/Firmware.cmake +index ab4468be..e1360159 100644 +--- a/product/tc0/scp_romfw/Firmware.cmake ++++ b/product/tc0/scp_romfw/Firmware.cmake +@@ -21,7 +21,7 @@ set(SCP_ENABLE_NOTIFICATIONS_INIT TRUE) + + set(SCP_ENABLE_IPO_INIT FALSE) + +-set(SCP_PLATFORM_VARIANT_INIT 0) ++set(SCP_PLATFORM_FEATURE_SET_INIT 0) + + set(SCP_ARCHITECTURE "armv7-m") + +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/files/tc/0004-tc0-support-platform-feature-set-options-in-firmware.patch b/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/files/tc/0004-tc0-support-platform-feature-set-options-in-firmware.patch new file mode 100644 index 0000000000..aa83332bd6 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/files/tc/0004-tc0-support-platform-feature-set-options-in-firmware.patch @@ -0,0 +1,114 @@ +From 3e737dd47b228bdeffb06e39bffec7a4a436b244 Mon Sep 17 00:00:00 2001 +From: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Date: Wed, 9 Feb 2022 16:02:10 +0000 +Subject: [PATCH 4/4] tc0: support platform feature set options in firmware.mk + +Support existing platform feature set options that is in cmake to +firmware.mk. Two feature set for TC0 are +0. Standard +1. MPMM/Power/Performance (Experimental) + +Build option to select the feature set is using: +make PRODUCT=tc0 MODE=<debug,release> SCP_PLATFORM_FEATURE_SET=<0,1> + +The default value is set to 0 (Standard). +Refer product/tc0/doc/features.md for more details. + +Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Change-Id: I4028686a8f8461e0e2c29e15d5e52eb1d37ca60a +Upstream-Status: Pending [Not submitted to upstream yet] +--- + product/tc0/scp_ramfw/firmware.mk | 41 +++++++++++++++++++++++++++++-- + product/tc0/scp_romfw/firmware.mk | 12 +++++++++ + 2 files changed, 51 insertions(+), 2 deletions(-) + +diff --git a/product/tc0/scp_ramfw/firmware.mk b/product/tc0/scp_ramfw/firmware.mk +index ec6e6679..d7515f5b 100644 +--- a/product/tc0/scp_ramfw/firmware.mk ++++ b/product/tc0/scp_ramfw/firmware.mk +@@ -9,8 +9,24 @@ BS_FIRMWARE_CPU := cortex-m3 + BS_FIRMWARE_HAS_NOTIFICATION := yes + BS_FIRMWARE_HAS_RESOURCE_PERMISSIONS := yes + BS_FIRMWARE_USE_NEWLIB_NANO_SPECS := yes +-BS_FIRMWARE_HAS_FAST_CHANNELS := no +-BS_FIRMWARE_HAS_PERF_PLUGIN_HANDLER := no ++ ++DEFAULT_SCP_PLATFORM_FEATURE_SET := 0 ++ ++export SCP_PLATFORM_FEATURE_SET ?= $(DEFAULT_SCP_PLATFORM_FEATURE_SET) ++ifneq ($(filter-out 0 1, $(SCP_PLATFORM_FEATURE_SET)),) ++ $(error "Invalid for SCP_PLATFORM_FEATURE_SET parameter. Valid options are \ ++ 0 or 1. Aborting...") ++endif ++ ++ifeq ($(SCP_PLATFORM_FEATURE_SET),0) ++ BS_FIRMWARE_HAS_PERF_PLUGIN_HANDLER := no ++ BS_FIRMWARE_HAS_FAST_CHANNELS := no ++else ++ DEFINES += TC0_FEATURES_MPMM_POWER_PERF ++ BS_FIRMWARE_HAS_PERF_PLUGIN_HANDLER := yes ++ BS_FIRMWARE_HAS_FAST_CHANNELS := yes ++ $(info "TC0 platform features POWER/PERFORMANCE is experimental") ++endif + + BS_FIRMWARE_MODULES := \ + armv7m_mpu \ +@@ -44,6 +60,16 @@ ifeq ($(BS_FIRMWARE_HAS_RESOURCE_PERMISSIONS),yes) + BS_FIRMWARE_MODULES += resource_perms + endif + ++ifeq ($(SCP_PLATFORM_FEATURE_SET),1) ++BS_FIRMWARE_MODULES += \ ++ traffic_cop \ ++ mpmm \ ++ sensor \ ++ reg_sensor \ ++ thermal_mgmt \ ++ tc0_power_model ++endif ++ + BS_FIRMWARE_SOURCES := \ + config_system_power.c \ + config_armv7m_mpu.c \ +@@ -75,4 +101,15 @@ ifeq ($(BS_FIRMWARE_HAS_RESOURCE_PERMISSIONS),yes) + BS_FIRMWARE_SOURCES += config_resource_perms.c + endif + ++ifeq ($(SCP_PLATFORM_FEATURE_SET),1) ++ BS_FIRMWARE_SOURCES += \ ++ config_traffic_cop.c \ ++ config_mpmm.c \ ++ config_sensor.c \ ++ config_reg_sensor.c \ ++ config_thermal_mgmt.c \ ++ config_tc0_power_model.c ++endif ++ ++ + include $(BS_DIR)/firmware.mk +diff --git a/product/tc0/scp_romfw/firmware.mk b/product/tc0/scp_romfw/firmware.mk +index 9977712f..0012b9fa 100644 +--- a/product/tc0/scp_romfw/firmware.mk ++++ b/product/tc0/scp_romfw/firmware.mk +@@ -9,6 +9,18 @@ BS_FIRMWARE_CPU := cortex-m3 + BS_FIRMWARE_HAS_NOTIFICATION := yes + BS_FIRMWARE_USE_NEWLIB_NANO_SPECS := yes + ++DEFAULT_SCP_PLATFORM_FEATURE_SET := 0 ++ ++export SCP_PLATFORM_FEATURE_SET ?= $(DEFAULT_SCP_PLATFORM_FEATURE_SET) ++ifneq ($(filter-out 0 1, $(SCP_PLATFORM_FEATURE_SET)),) ++ $(error "Invalid for SCP_PLATFORM_FEATURE_SET parameter. Valid options are \ ++ 0 or 1. Aborting...") ++endif ++ ++ifeq ($(SCP_PLATFORM_FEATURE_SET),1) ++ $(info "TC0 platform features POWER/PERFORMANCE is experimental") ++endif ++ + BS_FIRMWARE_MODULE_HEADERS_ONLY := \ + power_domain \ + timer +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/scp-firmware-juno.inc b/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/scp-firmware-juno.inc new file mode 100644 index 0000000000..ea2faceb4a --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/scp-firmware-juno.inc @@ -0,0 +1,16 @@ +# juno specific SCP configuration + +COMPATIBLE_MACHINE = "juno" + +SCP_PLATFORM = "juno" +FW_TARGETS = "scp" +FW_INSTALL:append = " romfw_bypass" + +do_install:append() { + for TYPE in ${FW_INSTALL}; do + if [ "$TYPE" = "romfw_bypass" ]; then + install -D "${B}/${TYPE}/${FW_TARGETS}/bin/${SCP_PLATFORM}-bl1-bypass.bin" "${D}/firmware/${FW}_${TYPE}.bin" + install -D "${B}/${TYPE}/${FW_TARGETS}/bin/${SCP_PLATFORM}-bl1-bypass" "${D}/firmware/${FW}_${TYPE}.elf" + fi + done +} diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/scp-firmware-n1sdp.inc b/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/scp-firmware-n1sdp.inc new file mode 100644 index 0000000000..e66469c36e --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/scp-firmware-n1sdp.inc @@ -0,0 +1,33 @@ +# N1SDP specific SCP configurations and build instructions + +SCP_PLATFORM = "n1sdp" +SCP_LOG_LEVEL = "INFO" + +# master branch at n1sdp: Introduce trusted board boot +SRCREV = "3e4c34ceccc1c960eb3a4adaa922f2a0c6b36be3" +PV .= "+git${SRCPV}" + +COMPATIBLE_MACHINE:n1sdp = "n1sdp" + +DEPENDS += "fiptool-native" +DEPENDS += "trusted-firmware-a" + +do_install:append() { + fiptool \ + create \ + --scp-fw "${D}/firmware/scp_ramfw.bin" \ + --blob uuid=cfacc2c4-15e8-4668-82be-430a38fad705,file="${RECIPE_SYSROOT}/firmware/bl1.bin" \ + "scp_fw.bin" + + # This UUID is FIP_UUID_MCP_BL2 in SCP-Firmware. + fiptool \ + create \ + --blob uuid=54464222-a4cf-4bf8-b1b6-cee7dade539e,file="${D}/firmware/mcp_ramfw.bin" \ + "mcp_fw.bin" + + install "scp_fw.bin" "${D}/firmware/scp_fw.bin" + install "mcp_fw.bin" "${D}/firmware/mcp_fw.bin" + + ln -sf "scp_romfw.bin" "${D}/firmware/scp_rom.bin" + ln -sf "mcp_romfw.bin" "${D}/firmware/mcp_rom.bin" +} diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/scp-firmware-sgi575.inc b/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/scp-firmware-sgi575.inc new file mode 100644 index 0000000000..e1b0a854af --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/scp-firmware-sgi575.inc @@ -0,0 +1,6 @@ +# SGI575 specific SCP configurations and build instructions + +SCP_PLATFORM = "sgi575" +SCP_LOG_LEVEL = "INFO" + +COMPATIBLE_MACHINE:sgi575 = "sgi575" diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/scp-firmware-tc.inc b/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/scp-firmware-tc.inc new file mode 100644 index 0000000000..a6a005c1a7 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/scp-firmware-tc.inc @@ -0,0 +1,14 @@ +# TC specific SCP configuration + +FILESEXTRAPATHS:prepend := "${THISDIR}/files/tc:" +SRC_URI:append:tc = " \ + file://0002-tc0-fix-mpmm-config.patch \ + file://0003-tc0-rename-platform-variant-to-platform-feature-set.patch \ + file://0004-tc0-support-platform-feature-set-options-in-firmware.patch \ + " + +COMPATIBLE_MACHINE = "(tc?)" + +SCP_PLATFORM:tc0 = "tc0" +SCP_PLATFORM:tc1 = "tc1" +FW_TARGETS = "scp" diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/scp-firmware_2.10.%.bbappend b/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/scp-firmware_2.10.%.bbappend new file mode 100644 index 0000000000..bb1a48c76a --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/scp-firmware/scp-firmware_2.10.%.bbappend @@ -0,0 +1,10 @@ +# Include machine specific SCP configurations + +MACHINE_SCP_REQUIRE ?= "" + +MACHINE_SCP_REQUIRE:juno = "scp-firmware-juno.inc" +MACHINE_SCP_REQUIRE:n1sdp = "scp-firmware-n1sdp.inc" +MACHINE_SCP_REQUIRE:sgi575 = "scp-firmware-sgi575.inc" +MACHINE_SCP_REQUIRE:tc = "scp-firmware-tc.inc" + +require ${MACHINE_SCP_REQUIRE} diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/corstone1000/0001-Fix-FF-A-version-in-SPMC-manifest.patch b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/corstone1000/0001-Fix-FF-A-version-in-SPMC-manifest.patch new file mode 100644 index 0000000000..016de8d3de --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/corstone1000/0001-Fix-FF-A-version-in-SPMC-manifest.patch @@ -0,0 +1,34 @@ +Upstream-Status: Inappropriate +Signed-off-by: Emekcan Aras <Emekcan.Aras@arm.com> + +From a31aee0988ef64724ec5866f10709f51f8cb3237 Mon Sep 17 00:00:00 2001 +From: emeara01 <emekcan.aras@arm.com> +Date: Wed, 11 May 2022 14:37:06 +0100 +Subject: [PATCH] Fix FF-A version in SPMC manifest + +OPTEE does not support FF-A version 1.1 in SPMC at the moment. +This commit corrects the FF-A version in corstone1000_spmc_manifest.dts. +This patch will not be upstreamed and will be dropped once +OPTEE version is updated for Corstone1000. + +Signed-off-by: Emekcan Aras <Emekcan.Aras@arm.com> +--- + .../corstone1000/common/fdts/corstone1000_spmc_manifest.dts | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/plat/arm/board/corstone1000/common/fdts/corstone1000_spmc_manifest.dts b/plat/arm/board/corstone1000/common/fdts/corstone1000_spmc_manifest.dts +index 8e49ab83f..5baa1b115 100644 +--- a/plat/arm/board/corstone1000/common/fdts/corstone1000_spmc_manifest.dts ++++ b/plat/arm/board/corstone1000/common/fdts/corstone1000_spmc_manifest.dts +@@ -20,7 +20,7 @@ + attribute { + spmc_id = <0x8000>; + maj_ver = <0x1>; +- min_ver = <0x1>; ++ min_ver = <0x0>; + exec_state = <0x0>; + load_address = <0x0 0x2002000>; + entrypoint = <0x0 0x2002000>; +-- +2.17.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/n1sdp/bl_size.patch b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/n1sdp/bl_size.patch new file mode 100644 index 0000000000..a5b30195d4 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/n1sdp/bl_size.patch @@ -0,0 +1,40 @@ +From 80b1efa92486a87f9e82dbf665ef612291148de8 Mon Sep 17 00:00:00 2001 +From: Adam Johnston <adam.johnston@arm.com> +Date: Tue, 14 Jun 2022 11:19:30 +0000 +Subject: [PATCH] arm-bsp/trusted-firmware-a: N1SDP trusted boot + +Increase max size of BL2 on N1SDP by 4KB to enable trusted boot +Decrease max size of BL1 on N1SDP by 8KB so BL1/BL2 fits above BL31 progbits + +Signed-off-by: Adam Johnston <adam.johnston@arm.com> +Upstream-Status: Pending [Flagged to upstream] + +--- + plat/arm/board/n1sdp/include/platform_def.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/plat/arm/board/n1sdp/include/platform_def.h b/plat/arm/board/n1sdp/include/platform_def.h +index c9b81bafa..7468a31ed 100644 +--- a/plat/arm/board/n1sdp/include/platform_def.h ++++ b/plat/arm/board/n1sdp/include/platform_def.h +@@ -91,7 +91,7 @@ + * PLAT_ARM_MAX_BL1_RW_SIZE is calculated using the current BL1 RW debug size + * plus a little space for growth. + */ +-#define PLAT_ARM_MAX_BL1_RW_SIZE 0xE000 ++#define PLAT_ARM_MAX_BL1_RW_SIZE 0xC000 + + /* + * PLAT_ARM_MAX_ROMLIB_RW_SIZE is define to use a full page +@@ -110,7 +110,7 @@ + * little space for growth. + */ + #if TRUSTED_BOARD_BOOT +-# define PLAT_ARM_MAX_BL2_SIZE 0x20000 ++# define PLAT_ARM_MAX_BL2_SIZE 0x21000 + #else + # define PLAT_ARM_MAX_BL2_SIZE 0x14000 + #endif +-- +2.35.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0001-plat-tc-Increase-maximum-BL2-size.patch b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0001-plat-tc-Increase-maximum-BL2-size.patch new file mode 100644 index 0000000000..74ab3612c1 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0001-plat-tc-Increase-maximum-BL2-size.patch @@ -0,0 +1,43 @@ +From 008cfc6457c239466ca62610d59aaf1a78f6b2f6 Mon Sep 17 00:00:00 2001 +From: Tudor Cretu <tudor.cretu@arm.com> +Date: Fri, 21 May 2021 14:56:37 +0000 +Subject: [PATCH 1/7] plat: tc: Increase maximum BL2 size. + +BL2 size gets increased due to the firmware update changes. +Increase the MAX_BL2_SIZE by 8Kb. + +Signed-off-by: Tudor Cretu <tudor.cretu@arm.com> +Change-Id: I1cb28b0eb7f834426873ff9f4c40bd496413806f +Upstream-Status: Pending [Not submitted to upstream yet] +--- + plat/arm/board/tc/include/platform_def.h | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/plat/arm/board/tc/include/platform_def.h b/plat/arm/board/tc/include/platform_def.h +index 745d91cab..cd77773aa 100644 +--- a/plat/arm/board/tc/include/platform_def.h ++++ b/plat/arm/board/tc/include/platform_def.h +@@ -120,9 +120,9 @@ + * little space for growth. + */ + #if TRUSTED_BOARD_BOOT +-# define PLAT_ARM_MAX_BL2_SIZE 0x20000 ++# define PLAT_ARM_MAX_BL2_SIZE 0x25000 + #else +-# define PLAT_ARM_MAX_BL2_SIZE 0x14000 ++# define PLAT_ARM_MAX_BL2_SIZE 0x19000 + #endif + + /* +@@ -130,7 +130,7 @@ + * calculated using the current BL31 PROGBITS debug size plus the sizes of + * BL2 and BL1-RW + */ +-#define PLAT_ARM_MAX_BL31_SIZE 0x3F000 ++#define PLAT_ARM_MAX_BL31_SIZE 0x4F000 + + /* + * Size of cacheable stacks +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0002-Makefile-add-trusty_sp_fw_config-build-option.patch b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0002-Makefile-add-trusty_sp_fw_config-build-option.patch new file mode 100644 index 0000000000..75cabdd8d1 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0002-Makefile-add-trusty_sp_fw_config-build-option.patch @@ -0,0 +1,46 @@ +From 2f8b0cc6be3787717247d1c02a45181a5ac6f125 Mon Sep 17 00:00:00 2001 +From: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Date: Mon, 11 Apr 2022 14:36:54 +0100 +Subject: [PATCH 2/7] Makefile: add trusty_sp_fw_config build option + +Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Change-Id: Ief90ae9113d32265ee2200f35f3e517b7b9a4bea +Upstream-Status: Pending [Not submitted to upstream yet] +--- + Makefile | 4 ++++ + docs/plat/arm/arm-build-options.rst | 4 ++++ + 2 files changed, 8 insertions(+) + +diff --git a/Makefile b/Makefile +index 3941f8698..a20d647a2 100644 +--- a/Makefile ++++ b/Makefile +@@ -531,6 +531,10 @@ ifneq (${SPD},none) + DTC_CPPFLAGS += -DOPTEE_SP_FW_CONFIG + endif + ++ ifeq ($(findstring trusty_sp,$(ARM_SPMC_MANIFEST_DTS)),trusty_sp) ++ DTC_CPPFLAGS += -DTRUSTY_SP_FW_CONFIG ++ endif ++ + ifeq ($(TS_SP_FW_CONFIG),1) + DTC_CPPFLAGS += -DTS_SP_FW_CONFIG + endif +diff --git a/docs/plat/arm/arm-build-options.rst b/docs/plat/arm/arm-build-options.rst +index 339ebbe33..3c9a41fb8 100644 +--- a/docs/plat/arm/arm-build-options.rst ++++ b/docs/plat/arm/arm-build-options.rst +@@ -107,6 +107,10 @@ Arm Platform Build Options + device tree. This flag is defined only when ``ARM_SPMC_MANIFEST_DTS`` manifest + file name contains pattern optee_sp. + ++- ``TRUSTY_SP_FW_CONFIG``: DTC build flag to include Trusty as SP in ++ tb_fw_config device tree. This flag is defined only when ++ ``ARM_SPMC_MANIFEST_DTS`` manifest file name contains pattern trusty_sp. ++ + - ``TS_SP_FW_CONFIG``: DTC build flag to include Trusted Services (Crypto and + internal-trusted-storage) as SP in tb_fw_config device tree. + +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0003-fix-plat-arm-increase-sp-max-image-size.patch b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0003-fix-plat-arm-increase-sp-max-image-size.patch new file mode 100644 index 0000000000..6807191e6c --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0003-fix-plat-arm-increase-sp-max-image-size.patch @@ -0,0 +1,30 @@ +From 0060b1a4fbe3bc9992f59a2d4cb986821f7bcf13 Mon Sep 17 00:00:00 2001 +From: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Date: Mon, 11 Apr 2022 18:31:01 +0100 +Subject: [PATCH 3/7] fix(plat/arm): increase sp max image size + +Increase ARM_SP_MAX_SIZE to support Trusty image. + +Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Change-Id: I9ef9e755769445aee998062a7fba508fad50b33e +Upstream-Status: Pending [Not submitted to upstream yet] +--- + include/plat/arm/common/fconf_arm_sp_getter.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/plat/arm/common/fconf_arm_sp_getter.h b/include/plat/arm/common/fconf_arm_sp_getter.h +index aa628dfd3..3ed953d1c 100644 +--- a/include/plat/arm/common/fconf_arm_sp_getter.h ++++ b/include/plat/arm/common/fconf_arm_sp_getter.h +@@ -13,7 +13,7 @@ + /* arm_sp getter */ + #define arm__sp_getter(prop) arm_sp.prop + +-#define ARM_SP_MAX_SIZE U(0xb0000) ++#define ARM_SP_MAX_SIZE U(0x2000000) + #define ARM_SP_OWNER_NAME_LEN U(8) + + struct arm_sp_t { +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0004-fix-plat-tc-increase-tc_tzc_dram1_size.patch b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0004-fix-plat-tc-increase-tc_tzc_dram1_size.patch new file mode 100644 index 0000000000..aec8be0c60 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0004-fix-plat-tc-increase-tc_tzc_dram1_size.patch @@ -0,0 +1,69 @@ +From 000e19d360a5ad9abd7d823af86a364bac2afc58 Mon Sep 17 00:00:00 2001 +From: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Date: Mon, 11 Apr 2022 17:38:17 +0100 +Subject: [PATCH 4/7] fix(plat/tc): increase tc_tzc_dram1_size + +Increase TC_TZC_DRAM1_SIZE for Trusty image and its memory size. +Update OP-TEE reserved memory range in DTS + +Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Change-Id: Iad433c3c155f28860b15bde2398df653487189dd +Upstream-Status: Pending [Not submitted to upstream yet] +--- + fdts/tc.dts | 4 ++-- + plat/arm/board/tc/include/platform_def.h | 10 ++++++---- + 2 files changed, 8 insertions(+), 6 deletions(-) + +diff --git a/fdts/tc.dts b/fdts/tc.dts +index 20992294b..af64504a4 100644 +--- a/fdts/tc.dts ++++ b/fdts/tc.dts +@@ -213,8 +213,8 @@ + linux,cma-default; + }; + +- optee@0xfce00000 { +- reg = <0x00000000 0xfce00000 0 0x00200000>; ++ optee@0xf8e00000 { ++ reg = <0x00000000 0xf8e00000 0 0x00200000>; + no-map; + }; + }; +diff --git a/plat/arm/board/tc/include/platform_def.h b/plat/arm/board/tc/include/platform_def.h +index cd77773aa..35d8fd24e 100644 +--- a/plat/arm/board/tc/include/platform_def.h ++++ b/plat/arm/board/tc/include/platform_def.h +@@ -31,7 +31,7 @@ + */ + #define TC_TZC_DRAM1_BASE (ARM_AP_TZC_DRAM1_BASE - \ + TC_TZC_DRAM1_SIZE) +-#define TC_TZC_DRAM1_SIZE UL(0x02000000) /* 32 MB */ ++#define TC_TZC_DRAM1_SIZE UL(0x06000000) /* 96 MB */ + #define TC_TZC_DRAM1_END (TC_TZC_DRAM1_BASE + \ + TC_TZC_DRAM1_SIZE - 1) + +@@ -68,7 +68,9 @@ + * max size of BL32 image. + */ + #if defined(SPD_spmd) +-#define PLAT_ARM_SPMC_BASE TC_TZC_DRAM1_BASE ++#define TC_EL2SPMC_LOAD_ADDR (TC_TZC_DRAM1_BASE + 0x04000000) ++ ++#define PLAT_ARM_SPMC_BASE TC_EL2SPMC_LOAD_ADDR + #define PLAT_ARM_SPMC_SIZE UL(0x200000) /* 2 MB */ + #endif + +@@ -259,8 +261,8 @@ + (TZC_REGION_ACCESS_RDWR(TZC_NSAID_DEFAULT)) + + /* +- * The first region below, TC_TZC_DRAM1_BASE (0xfd000000) to +- * ARM_SCP_TZC_DRAM1_END (0xffffffff) will mark the last 48 MB of DRAM as ++ * The first region below, TC_TZC_DRAM1_BASE (0xf9000000) to ++ * ARM_SCP_TZC_DRAM1_END (0xffffffff) will mark the last 112 MB of DRAM as + * secure. The second and third regions gives non secure access to rest of DRAM. + */ + #define TC_TZC_REGIONS_DEF \ +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0005-feat-plat-tc-add-spmc-manifest-with-trusty-sp.patch b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0005-feat-plat-tc-add-spmc-manifest-with-trusty-sp.patch new file mode 100644 index 0000000000..0b34683ee2 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0005-feat-plat-tc-add-spmc-manifest-with-trusty-sp.patch @@ -0,0 +1,169 @@ +From a04466ceb81a04c5179e8064837c34a89c2b11bd Mon Sep 17 00:00:00 2001 +From: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Date: Mon, 11 Apr 2022 14:43:15 +0100 +Subject: [PATCH 5/7] feat(plat/tc): add spmc manifest with trusty sp + +Add SPMC manifest with Trusty SP. Define Trusty's load address, +vcpu count, memory size. + +Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Change-Id: If4363580a478776d233f7f391a30e1cb345453c2 +Upstream-Status: Pending [Not submitted to upstream yet] +--- + .../tc/fdts/tc_spmc_trusty_sp_manifest.dts | 120 ++++++++++++++++++ + plat/arm/board/tc/fdts/tc_tb_fw_config.dts | 7 +- + 2 files changed, 126 insertions(+), 1 deletion(-) + create mode 100644 plat/arm/board/tc/fdts/tc_spmc_trusty_sp_manifest.dts + +diff --git a/plat/arm/board/tc/fdts/tc_spmc_trusty_sp_manifest.dts b/plat/arm/board/tc/fdts/tc_spmc_trusty_sp_manifest.dts +new file mode 100644 +index 000000000..e2ea7b811 +--- /dev/null ++++ b/plat/arm/board/tc/fdts/tc_spmc_trusty_sp_manifest.dts +@@ -0,0 +1,120 @@ ++/* ++ * Copyright (c) 2022, Arm Limited. All rights reserved. ++ * ++ * SPDX-License-Identifier: BSD-3-Clause ++ */ ++/dts-v1/; ++ ++/ { ++ compatible = "arm,ffa-core-manifest-1.0"; ++ #address-cells = <2>; ++ #size-cells = <1>; ++ ++ attribute { ++ spmc_id = <0x8000>; ++ maj_ver = <0x1>; ++ min_ver = <0x1>; ++ exec_state = <0x0>; ++ load_address = <0x0 0xfd000000>; ++ entrypoint = <0x0 0xfd000000>; ++ binary_size = <0x80000>; ++ }; ++ ++ hypervisor { ++ compatible = "hafnium,hafnium"; ++ vm1 { ++ is_ffa_partition; ++ debug_name = "trusty"; ++ load_address = <0xf901f000>; ++ vcpu_count = <8>; ++ mem_size = <0x3f00000>; /* 64MB TZC DRAM - 1MB align */ ++ }; ++#ifdef TS_SP_FW_CONFIG ++ vm2 { ++ is_ffa_partition; ++ debug_name = "internal-trusted-storage"; ++ load_address = <0xfee00000>; ++ vcpu_count = <1>; ++ mem_size = <2097152>; /* 2MB TZC DRAM */ ++ }; ++ vm3 { ++ is_ffa_partition; ++ debug_name = "crypto"; ++ load_address = <0xfec00000>; ++ vcpu_count = <1>; ++ mem_size = <2097152>; /* 2MB TZC DRAM */ ++ }; ++#endif ++ }; ++ ++ cpus { ++ #address-cells = <0x2>; ++ #size-cells = <0x0>; ++ ++ CPU0:cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,armv8"; ++ reg = <0x0 0x0>; ++ enable-method = "psci"; ++ }; ++ ++ /* ++ * SPMC (Hafnium) requires secondary cpu nodes are declared in ++ * descending order ++ */ ++ CPU7:cpu@700 { ++ device_type = "cpu"; ++ compatible = "arm,armv8"; ++ reg = <0x0 0x700>; ++ enable-method = "psci"; ++ }; ++ ++ CPU6:cpu@600 { ++ device_type = "cpu"; ++ compatible = "arm,armv8"; ++ reg = <0x0 0x600>; ++ enable-method = "psci"; ++ }; ++ ++ CPU5:cpu@500 { ++ device_type = "cpu"; ++ compatible = "arm,armv8"; ++ reg = <0x0 0x500>; ++ enable-method = "psci"; ++ }; ++ ++ CPU4:cpu@400 { ++ device_type = "cpu"; ++ compatible = "arm,armv8"; ++ reg = <0x0 0x400>; ++ enable-method = "psci"; ++ }; ++ ++ CPU3:cpu@300 { ++ device_type = "cpu"; ++ compatible = "arm,armv8"; ++ reg = <0x0 0x300>; ++ enable-method = "psci"; ++ }; ++ ++ CPU2:cpu@200 { ++ device_type = "cpu"; ++ compatible = "arm,armv8"; ++ reg = <0x0 0x200>; ++ enable-method = "psci"; ++ }; ++ ++ CPU1:cpu@100 { ++ device_type = "cpu"; ++ compatible = "arm,armv8"; ++ reg = <0x0 0x100>; ++ enable-method = "psci"; ++ }; ++ }; ++ ++ /* 96MB of TC_TZC_DRAM1_BASE */ ++ memory@f9000000 { ++ device_type = "memory"; ++ reg = <0x0 0xf9000000 0x6000000>; ++ }; ++}; +diff --git a/plat/arm/board/tc/fdts/tc_tb_fw_config.dts b/plat/arm/board/tc/fdts/tc_tb_fw_config.dts +index 4c6ccef25..a72772fb3 100644 +--- a/plat/arm/board/tc/fdts/tc_tb_fw_config.dts ++++ b/plat/arm/board/tc/fdts/tc_tb_fw_config.dts +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2020-2021, Arm Limited. All rights reserved. ++ * Copyright (c) 2020-2022, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +@@ -47,6 +47,11 @@ + uuid = "486178e0-e7f8-11e3-bc5e-0002a5d5c51b"; + load-address = <0xfd280000>; + }; ++#elif TRUSTY_SP_FW_CONFIG ++ trusty { ++ uuid = "40ee25f0-a2bc-304c-8c4c-a173c57d8af1"; ++ load-address = <0xf901f000>; ++ }; + #else + cactus-primary { + uuid = "b4b5671e-4a90-4fe1-b81f-fb13dae1dacb"; +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0006-feat-plat-tc-update-dts-with-trusty-compatible-strin.patch b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0006-feat-plat-tc-update-dts-with-trusty-compatible-strin.patch new file mode 100644 index 0000000000..e2bfb2c5f5 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0006-feat-plat-tc-update-dts-with-trusty-compatible-strin.patch @@ -0,0 +1,50 @@ +From 96151af7eed28d63fdaa1ac6de2d14a9c71f1d4a Mon Sep 17 00:00:00 2001 +From: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Date: Wed, 30 Mar 2022 12:14:49 +0000 +Subject: [PATCH 6/7] feat(plat/tc): update dts with trusty compatible string + +Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Change-Id: Ic6661df479e114bf3f464165c14df5fa02dc0139 +Upstream-Status: Pending [Not submitted to upstream yet] +--- + fdts/tc.dts | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/fdts/tc.dts b/fdts/tc.dts +index af64504a4..dc86958bf 100644 +--- a/fdts/tc.dts ++++ b/fdts/tc.dts +@@ -555,4 +555,30 @@ + compatible = "arm,trace-buffer-extension"; + interrupts = <1 2 4>; + }; ++ ++ trusty { ++ #size-cells = <0x02>; ++ #address-cells = <0x02>; ++ ranges = <0x00>; ++ compatible = "android,trusty-v1"; ++ ++ virtio { ++ compatible = "android,trusty-virtio-v1"; ++ }; ++ ++ test { ++ compatible = "android,trusty-test-v1"; ++ }; ++ ++ log { ++ compatible = "android,trusty-log-v1"; ++ }; ++ ++ irq { ++ ipi-range = <0x08 0x0f 0x08>; ++ interrupt-ranges = <0x00 0x0f 0x00 0x10 0x1f 0x01 0x20 0x3f 0x02>; ++ interrupt-templates = <0x01 0x00 0x8001 0x01 0x01 0x04 0x8001 0x01 0x00 0x04>; ++ compatible = "android,trusty-irq-v1"; ++ }; ++ }; + }; +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0007-fix-plat-tc-disable-smmu.patch b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0007-fix-plat-tc-disable-smmu.patch new file mode 100644 index 0000000000..91f5a9efa9 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/0007-fix-plat-tc-disable-smmu.patch @@ -0,0 +1,64 @@ +From 1a051bef6c63f6871edd8d87e969460f073820a7 Mon Sep 17 00:00:00 2001 +From: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Date: Wed, 27 Apr 2022 18:15:47 +0100 +Subject: [PATCH 7/7] fix(plat/tc): disable smmu + +Reserve static shared-dma-pool below 4GB. This removes dependency on +SMMU driver. As there are stability issues in SMMU driver, it is +disabled. This change is temporary and will be reverted upon proper +fix and testing. + +Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Change-Id: I6b1b4c2a0acdf62df8c26007c7ca596774e13710 +Upstream-Status: Pending [Not submitted to upstream yet] +--- + fdts/tc.dts | 16 +++------------- + 1 file changed, 3 insertions(+), 13 deletions(-) + +diff --git a/fdts/tc.dts b/fdts/tc.dts +index dc86958bf..fbae3e3e8 100644 +--- a/fdts/tc.dts ++++ b/fdts/tc.dts +@@ -209,12 +209,12 @@ + linux,cma { + compatible = "shared-dma-pool"; + reusable; +- size = <0x0 0x8000000>; ++ reg = <0x0 0xf1000000 0x0 0x8000000>; + linux,cma-default; + }; + +- optee@0xf8e00000 { +- reg = <0x00000000 0xf8e00000 0 0x00200000>; ++ optee@0xf0e00000 { ++ reg = <0x0 0xf0e00000 0 0x00200000>; + no-map; + }; + }; +@@ -460,13 +460,6 @@ + >; + }; + +- smmu: smmu@2ce00000 { +- #iommu-cells = <1>; +- compatible = "arm,smmu-v3"; +- reg = <0x0 0x2ce00000 0x0 0x20000>; +- status = "okay"; +- }; +- + dp0: display@2cc00000 { + #address-cells = <1>; + #size-cells = <0>; +@@ -476,9 +469,6 @@ + interrupt-names = "DPU"; + clocks = <&scmi_clk 0>; + clock-names = "aclk"; +- iommus = <&smmu 0>, <&smmu 1>, <&smmu 2>, <&smmu 3>, +- <&smmu 4>, <&smmu 5>, <&smmu 6>, <&smmu 7>, +- <&smmu 8>, <&smmu 9>; + pl0: pipeline@0 { + reg = <0>; + clocks = <&scmi_clk 1>; +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/generate_metadata.py b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/generate_metadata.py new file mode 100644 index 0000000000..f3670ce1d1 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/files/tc/generate_metadata.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# Copyright (c) 2021, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +import argparse +import uuid +import zlib + +def main(metadata_file, img_type_uuids, location_uuids, img_uuids): + def add_field_to_metadata(value): + # Write the integer values to file in little endian representation + with open(metadata_file, "ab") as fp: + fp.write(value.to_bytes(4, byteorder='little')) + + def add_uuid_to_metadata(uuid_str): + # Validate UUID string and write to file in little endian representation + uuid_val = uuid.UUID(uuid_str) + with open(metadata_file, "ab") as fp: + fp.write(uuid_val.bytes_le) + + # Fill metadata preamble + add_field_to_metadata(1) #version=1 + add_field_to_metadata(0) #active_index=0 + add_field_to_metadata(0) #previous_active_index=0 + + for img_type_uuid, location_uuid in zip(img_type_uuids, location_uuids): + # Fill metadata image entry + add_uuid_to_metadata(img_type_uuid) # img_type_uuid + add_uuid_to_metadata(location_uuid) # location_uuid + + for img_uuid in img_uuids: + # Fill metadata bank image info + add_uuid_to_metadata(img_uuid) # image unique bank_uuid + add_field_to_metadata(1) # accepted=1 + add_field_to_metadata(0) # reserved (MBZ) + + # Prepend CRC32 + with open(metadata_file, 'rb+') as fp: + content = fp.read() + crc = zlib.crc32(content) + fp.seek(0) + fp.write(crc.to_bytes(4, byteorder='little') + content) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('--metadata_file', required=True, + help='Output binary file to store the metadata') + parser.add_argument('--img_type_uuids', type=str, nargs='+', required=True, + help='A list of UUIDs identifying the image types') + parser.add_argument('--location_uuids', type=str, nargs='+', required=True, + help='A list of UUIDs of the storage volumes where the images are located. ' + 'Must have the same length as img_type_uuids.') + parser.add_argument('--img_uuids', type=str, nargs='+', required=True, + help='A list UUIDs of the images in a firmware bank') + + args = parser.parse_args() + + if len(args.img_type_uuids) != len(args.location_uuids): + parser.print_help() + raise argparse.ArgumentError(None, 'Arguments img_type_uuids and location_uuids must have the same length.') + + main(args.metadata_file, args.img_type_uuids, args.location_uuids, args.img_uuids) diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/tf-a-tests_2.7.0.bbappend b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/tf-a-tests_2.7.0.bbappend new file mode 100644 index 0000000000..ff22ff12de --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/tf-a-tests_2.7.0.bbappend @@ -0,0 +1,3 @@ +# Machine specific TFAs + +COMPATIBLE_MACHINE:corstone1000 = "corstone1000" diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-corstone1000.inc b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-corstone1000.inc new file mode 100644 index 0000000000..341c8a278a --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-corstone1000.inc @@ -0,0 +1,42 @@ +# Corstone1000 64-bit machines specific TFA support + +COMPATIBLE_MACHINE = "(corstone1000)" + +FILESEXTRAPATHS:prepend := "${THISDIR}/files/corstone1000:" + +SRC_URI:append = " \ + file://0001-Fix-FF-A-version-in-SPMC-manifest.patch \ + " + +TFA_DEBUG = "1" +TFA_UBOOT ?= "1" +TFA_MBEDTLS = "1" +TFA_BUILD_TARGET = "bl2 bl31 fip" + +# Enabling Secure-EL1 Payload Dispatcher (SPD) +TFA_SPD = "spmd" +# Cortex-A35 supports Armv8.0-A (no S-EL2 execution state). +# So, the SPD SPMC component should run at the S-EL1 execution state +TFA_SPMD_SPM_AT_SEL2 = "0" + +# BL2 loads BL32 (optee). So, optee needs to be built first: +DEPENDS += "optee-os" + +EXTRA_OEMAKE:append = " \ + ARCH=aarch64 \ + TARGET_PLATFORM=${TFA_TARGET_PLATFORM} \ + ENABLE_STACK_PROTECTOR=strong \ + ENABLE_PIE=1 \ + BL2_AT_EL3=1 \ + CREATE_KEYS=1 \ + GENERATE_COT=1 \ + TRUSTED_BOARD_BOOT=1 \ + COT=tbbr \ + ARM_ROTPK_LOCATION=devel_rsa \ + ROT_KEY=plat/arm/board/common/rotpk/arm_rotprivk_rsa.pem \ + BL32=${RECIPE_SYSROOT}/lib/firmware/tee-pager_v2.bin \ + LOG_LEVEL=50 \ + " + +# trigger TF-M build so TF-A binaries get signed +do_deploy[depends]+= "virtual/trusted-firmware-m:do_prepare_recipe_sysroot" diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-corstone500.inc b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-corstone500.inc new file mode 100644 index 0000000000..acd9e3dbfb --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-corstone500.inc @@ -0,0 +1,17 @@ +# Corstone-500 specific TFA support + +COMPATIBLE_MACHINE = "corstone500" +TFA_PLATFORM = "a5ds" +TFA_DEBUG = "1" +TFA_UBOOT = "1" +TFA_BUILD_TARGET = "all fip" +TFA_INSTALL_TARGET = "bl1.bin fip.bin" + +EXTRA_OEMAKE:append = " \ + ARCH=aarch32 \ + FVP_HW_CONFIG_DTS=fdts/a5ds.dts \ + ARM_ARCH_MAJOR=7 \ + AARCH32_SP=sp_min \ + ARM_CORTEX_A5=yes \ + ARM_XLAT_TABLES_LIB_V1=1 \ + " diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-fvp-arm32.inc b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-fvp-arm32.inc new file mode 100644 index 0000000000..fdaadb9fc6 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-fvp-arm32.inc @@ -0,0 +1,12 @@ +# Armv7-A FVP specific TFA parameters + +COMPATIBLE_MACHINE = "fvp-base-arm32" +TFA_PLATFORM = "fvp" +TFA_UBOOT = "1" +TFA_BUILD_TARGET = "dtbs bl1 bl32 fip" + +EXTRA_OEMAKE:append = " \ + ARCH=aarch32 \ + AARCH32_SP=sp_min \ + " + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-fvp.inc b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-fvp.inc new file mode 100644 index 0000000000..43340cdc7b --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-fvp.inc @@ -0,0 +1,12 @@ +# FVP specific TFA parameters + +# +# Armv8-A Base Platform FVP +# + +COMPATIBLE_MACHINE = "fvp-base" +TFA_PLATFORM = "fvp" +TFA_DEBUG = "1" +TFA_MBEDTLS = "1" +TFA_UBOOT = "1" +TFA_BUILD_TARGET = "bl1 bl2 bl31 dtbs fip" diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-juno.inc b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-juno.inc new file mode 100644 index 0000000000..3ddd8cb5f2 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-juno.inc @@ -0,0 +1,13 @@ +# Juno specific TFA support + +COMPATIBLE_MACHINE = "juno" +TFA_PLATFORM = "juno" +TFA_DEBUG = "1" +TFA_MBEDTLS = "1" +TFA_UBOOT ?= "1" +TFA_BUILD_TARGET = "bl1 bl2 bl31 dtbs fip" + +# Juno needs the System Control Processor Firmware +DEPENDS += "virtual/control-processor-firmware" + +EXTRA_OEMAKE:append = " SCP_BL2=${RECIPE_SYSROOT}/firmware/scp_ramfw.bin" diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-n1sdp.inc b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-n1sdp.inc new file mode 100644 index 0000000000..f8a0b8d517 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-n1sdp.inc @@ -0,0 +1,24 @@ +# N1SDP specific TFA support + +COMPATIBLE_MACHINE = "n1sdp" +TFA_PLATFORM = "n1sdp" +TFA_BUILD_TARGET = "all fip" +TFA_INSTALL_TARGET = "bl1 bl2 bl31 n1sdp-multi-chip n1sdp-single-chip n1sdp_fw_config n1sdp_tb_fw_config fip" +TFA_DEBUG = "1" +TFA_MBEDTLS = "1" +TFA_UBOOT = "0" +TFA_UEFI = "1" + +SRC_URI:append = " file://bl_size.patch" + +TFA_ROT_KEY= "plat/arm/board/common/rotpk/arm_rotprivk_rsa.pem" + +EXTRA_OEMAKE:append = "\ + TRUSTED_BOARD_BOOT=1 \ + GENERATE_COT=1 \ + CREATE_KEYS=1 \ + ENABLE_PIE=0 \ + ARM_ROTPK_LOCATION="devel_rsa" \ + ROT_KEY="${TFA_ROT_KEY}" \ + BL33=${RECIPE_SYSROOT}/firmware/uefi.bin \ + " diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-sgi575.inc b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-sgi575.inc new file mode 100644 index 0000000000..20904b34cb --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-sgi575.inc @@ -0,0 +1,13 @@ +# SGI575 specific TFA support + +COMPATIBLE_MACHINE = "sgi575" +TFA_PLATFORM = "sgi575" +TFA_BUILD_TARGET = "all fip" +TFA_INSTALL_TARGET = "bl1 fip" +TFA_DEBUG = "1" +TFA_MBEDTLS = "1" +TFA_UBOOT = "0" +TFA_UEFI = "1" + +EXTRA_OEMAKE += "TRUSTED_BOARD_BOOT=1 GENERATE_COT=1 ARM_ROTPK_LOCATION=devel_rsa \ + ROT_KEY=plat/arm/board/common/rotpk/arm_rotprivk_rsa.pem" diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-tc.inc b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-tc.inc new file mode 100644 index 0000000000..8cb53dece5 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a-tc.inc @@ -0,0 +1,140 @@ +# TC0 specific TFA configuration + +DEPENDS += "scp-firmware util-linux-native gptfdisk-native" + +FILESEXTRAPATHS:prepend := "${THISDIR}/files/tc:" +SRC_URI:append = " \ + file://0001-plat-tc-Increase-maximum-BL2-size.patch \ + file://0002-Makefile-add-trusty_sp_fw_config-build-option.patch \ + file://0003-fix-plat-arm-increase-sp-max-image-size.patch \ + file://0004-fix-plat-tc-increase-tc_tzc_dram1_size.patch \ + file://0005-feat-plat-tc-add-spmc-manifest-with-trusty-sp.patch \ + file://0006-feat-plat-tc-update-dts-with-trusty-compatible-strin.patch \ + file://0007-fix-plat-tc-disable-smmu.patch \ + file://generate_metadata.py \ + " + +COMPATIBLE_MACHINE = "(tc?)" + +TFA_PLATFORM = "tc" +TFA_BUILD_TARGET = "all fip" +TFA_UBOOT = "1" +TFA_INSTALL_TARGET = "bl1 fip" +TFA_MBEDTLS = "1" +TFA_DEBUG = "1" + +TFA_SPD = "spmd" +TFA_SPMD_SPM_AT_SEL2 = "1" + +TFA_TARGET_PLATFORM:tc0 = "0" +TFA_TARGET_PLATFORM:tc1 = "1" + +EXTRA_OEMAKE += "TARGET_PLATFORM=${TFA_TARGET_PLATFORM}" + +# Set optee as SP. Set spmc manifest and sp layout file to optee +DEPENDS += "optee-os" + +TFA_SP_LAYOUT_FILE = "${RECIPE_SYSROOT}/lib/firmware/sp_layout.json" +TFA_ARM_SPMC_MANIFEST_DTS = "plat/arm/board/tc/fdts/tc_spmc_optee_sp_manifest.dts" + +EXTRA_OEMAKE += "SCP_BL2=${RECIPE_SYSROOT}/firmware/scp_ramfw.bin" +EXTRA_OEMAKE += "TRUSTED_BOARD_BOOT=1 GENERATE_COT=1 ARM_ROTPK_LOCATION=devel_rsa \ + ROT_KEY=plat/arm/board/common/rotpk/arm_rotprivk_rsa.pem" +EXTRA_OEMAKE += "PSA_FWU_SUPPORT=1 ARM_GPT_SUPPORT=1" + +do_generate_gpt() { + gpt_image="${BUILD_DIR}/fip_gpt.bin" + fip_bin="${BUILD_DIR}/fip.bin" + # the FIP partition type is not standardized, so generate one + fip_type_uuid=`uuidgen --sha1 --namespace @dns --name "fip_type_uuid"` + # metadata partition type UUID, specified by the document: + # Platform Security Firmware Update for the A-profile Arm Architecture + # version: 1.0BET0 + metadata_type_uuid="8a7a84a0-8387-40f6-ab41-a8b9a5a60d23" + location_uuid=`uuidgen` + FIP_A_uuid=`uuidgen` + FIP_B_uuid=`uuidgen` + + # maximum FIP size 4MB. This is the current size of the FIP rounded up to an integer number of MB. + fip_max_size=4194304 + fip_bin_size=$(stat -c %s $fip_bin) + if [ $fip_max_size -lt $fip_bin_size ]; then + bberror "FIP binary ($fip_bin_size bytes) is larger than the GPT partition ($fip_max_size bytes)" + fi + + # maximum metadata size 512B. This is the current size of the metadata rounded up to an integer number of sectors. + metadata_max_size=512 + metadata_file="${BUILD_DIR}/metadata.bin" + python3 ${WORKDIR}/generate_metadata.py --metadata_file $metadata_file \ + --img_type_uuids $fip_type_uuid \ + --location_uuids $location_uuid \ + --img_uuids $FIP_A_uuid $FIP_B_uuid + + # create GPT image. The GPT contains 2 FIP partitions: FIP_A and FIP_B, and 2 metadata partitions: FWU-Metadata and Bkup-FWU-Metadata. + # the GPT layout is the following: + # ----------------------- + # Protective MBR + # ----------------------- + # Primary GPT Header + # ----------------------- + # FIP_A + # ----------------------- + # FIP_B + # ----------------------- + # FWU-Metadata + # ----------------------- + # Bkup-FWU-Metadata + # ----------------------- + # Secondary GPT Header + # ----------------------- + + sector_size=512 + gpt_header_size=33 # valid only for 512-byte sectors + num_sectors_fip=`expr $fip_max_size / $sector_size` + num_sectors_metadata=`expr $metadata_max_size / $sector_size` + start_sector_1=`expr 1 + $gpt_header_size` # size of MBR is 1 sector + start_sector_2=`expr $start_sector_1 + $num_sectors_fip` + start_sector_3=`expr $start_sector_2 + $num_sectors_fip` + start_sector_4=`expr $start_sector_3 + $num_sectors_metadata` + num_sectors_gpt=`expr $start_sector_4 + $num_sectors_metadata + $gpt_header_size` + gpt_size=`expr $num_sectors_gpt \* $sector_size` + + # create raw image + dd if=/dev/zero of=$gpt_image bs=$gpt_size count=1 + + # create the GPT layout + sgdisk $gpt_image \ + --set-alignment 1 \ + --disk-guid $location_uuid \ + \ + --new 1:$start_sector_1:+$num_sectors_fip \ + --change-name 1:FIP_A \ + --typecode 1:$fip_type_uuid \ + --partition-guid 1:$FIP_A_uuid \ + \ + --new 2:$start_sector_2:+$num_sectors_fip \ + --change-name 2:FIP_B \ + --typecode 2:$fip_type_uuid \ + --partition-guid 2:$FIP_B_uuid \ + \ + --new 3:$start_sector_3:+$num_sectors_metadata \ + --change-name 3:FWU-Metadata \ + --typecode 3:$metadata_type_uuid \ + \ + --new 4:$start_sector_4:+$num_sectors_metadata \ + --change-name 4:Bkup-FWU-Metadata \ + --typecode 4:$metadata_type_uuid + + # populate the GPT partitions + dd if=$fip_bin of=$gpt_image bs=$sector_size seek=$start_sector_1 count=$num_sectors_fip conv=notrunc + dd if=$fip_bin of=$gpt_image bs=$sector_size seek=$start_sector_2 count=$num_sectors_fip conv=notrunc + dd if=$metadata_file of=$gpt_image bs=$sector_size seek=$start_sector_3 count=$num_sectors_metadata conv=notrunc + dd if=$metadata_file of=$gpt_image bs=$sector_size seek=$start_sector_4 count=$num_sectors_metadata conv=notrunc +} + +addtask do_generate_gpt after do_compile before do_install + +do_install:append() { + install -m 0644 ${BUILD_DIR}/fip_gpt.bin ${D}/firmware/fip_gpt-tc.bin + ln -sf fip_gpt-tc.bin ${D}/firmware/fip_gpt.bin +} diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a_2.7.%.bbappend b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a_2.7.%.bbappend new file mode 100644 index 0000000000..09ed3f793a --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-a/trusted-firmware-a_2.7.%.bbappend @@ -0,0 +1,15 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/files/:" + +# Machine specific TFAs + +MACHINE_TFA_REQUIRE ?= "" +MACHINE_TFA_REQUIRE:corstone500 = "trusted-firmware-a-corstone500.inc" +MACHINE_TFA_REQUIRE:corstone1000 = "trusted-firmware-a-corstone1000.inc" +MACHINE_TFA_REQUIRE:fvp-base = "trusted-firmware-a-fvp.inc" +MACHINE_TFA_REQUIRE:fvp-base-arm32 = "trusted-firmware-a-fvp-arm32.inc" +MACHINE_TFA_REQUIRE:juno = "trusted-firmware-a-juno.inc" +MACHINE_TFA_REQUIRE:n1sdp = "trusted-firmware-a-n1sdp.inc" +MACHINE_TFA_REQUIRE:sgi575 = "trusted-firmware-a-sgi575.inc" +MACHINE_TFA_REQUIRE:tc = "trusted-firmware-a-tc.inc" + +require ${MACHINE_TFA_REQUIRE} diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m-corstone1000.inc b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m-corstone1000.inc new file mode 100644 index 0000000000..eb400e5278 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m-corstone1000.inc @@ -0,0 +1,49 @@ +# Corstone1000 machines specific TFM support + +COMPATIBLE_MACHINE = "(corstone1000)" + +TFM_PLATFORM = "arm/corstone1000" + +TFM_DEBUG = "1" + +## Default is the MPS3 board +TFM_PLATFORM_IS_FVP ?= "FALSE" +EXTRA_OECMAKE += "-DPLATFORM_IS_FVP=${TFM_PLATFORM_IS_FVP}" +EXTRA_OECMAKE += "-DCC312_LEGACY_DRIVER_API_ENABLED=OFF" + +# libmetal +LICENSE += "& BSD-3-Clause" +LIC_FILES_CHKSUM += "file://../libmetal/LICENSE.md;md5=fe0b8a4beea8f0813b606d15a3df3d3c" +SRC_URI += "git://github.com/OpenAMP/libmetal.git;protocol=https;branch=main;name=libmetal;destsuffix=git/libmetal" +SRCREV_libmetal = "f252f0e007fbfb8b3a52b1d5901250ddac96baad" +EXTRA_OECMAKE += "-DLIBMETAL_SRC_PATH=${WORKDIR}/git/libmetal -DLIBMETAL_BIN_PATH=${B}/libmetal-build" + +# OpenAMP +LICENSE += "& BSD-2-Clause & BSD-3-Clause" +LIC_FILES_CHKSUM += "file://../openamp/LICENSE.md;md5=a8d8cf662ef6bf9936a1e1413585ecbf" +SRC_URI += "git://github.com/OpenAMP/open-amp.git;protocol=https;branch=main;name=openamp;destsuffix=git/openamp" +SRCREV_openamp = "347397decaa43372fc4d00f965640ebde042966d" +EXTRA_OECMAKE += "-DLIBOPENAMP_SRC_PATH=${WORKDIR}/git/openamp -DLIBOPENAMP_BIN_PATH=${B}/libopenamp-build" + +DEPENDS += "trusted-firmware-a" + +# adding host images signing support +require trusted-firmware-m-sign-host-images.inc + +do_install() { + install -D -p -m 0644 ${B}/install/outputs/tfm_s_signed.bin ${D}/firmware/tfm_s_signed.bin + install -D -p -m 0644 ${B}/install/outputs/bl2_signed.bin ${D}/firmware/bl2_signed.bin + install -D -p -m 0644 ${B}/install/outputs/bl1.bin ${D}/firmware/bl1.bin + + # + # Signing TF-A BL2 and the FIP image + # + + sign_host_image ${TFA_BL2_BINARY} ${RECIPE_SYSROOT}/firmware ${TFA_BL2_RE_IMAGE_LOAD_ADDRESS} ${TFA_BL2_RE_SIGN_BIN_SIZE} + + fiptool update \ + --tb-fw ${D}/firmware/signed_${TFA_BL2_BINARY} \ + ${RECIPE_SYSROOT}/firmware/${TFA_FIP_BINARY} + + sign_host_image ${TFA_FIP_BINARY} ${RECIPE_SYSROOT}/firmware ${TFA_FIP_RE_IMAGE_LOAD_ADDRESS} ${TFA_FIP_RE_SIGN_BIN_SIZE} +} diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m-sign-host-images.inc b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m-sign-host-images.inc new file mode 100644 index 0000000000..49af35683d --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m-sign-host-images.inc @@ -0,0 +1,50 @@ +# Signing host images using TF-M tools + +DEPENDS += "python3-imgtool-native fiptool-native" + +# +# sign_host_image +# +# Description: +# +# A generic function that signs a host image +# using MCUBOOT format +# +# Arguments: +# +# $1 ... host binary to sign +# $2 ... host binary path +# $3 ... load address of the given binary +# $4 ... signed binary size +# +# Note: The signed binary is copied to ${D}/firmware +# +sign_host_image() { + + host_binary_filename="`basename -s .bin ${1}`" + host_binary_layout="${host_binary_filename}_ns" + + cat << EOF > ${B}/${host_binary_layout} +enum image_attributes { + RE_IMAGE_LOAD_ADDRESS = ${3}, + RE_SIGN_BIN_SIZE = ${4}, +}; +EOF + + host_binary="${2}/`basename ${1}`" + host_binary_signed="${D}/firmware/signed_`basename ${1}`" + + ${PYTHON} ${S}/bl2/ext/mcuboot/scripts/wrapper/wrapper.py \ + -v ${RE_LAYOUT_WRAPPER_VERSION} \ + --layout ${B}/${host_binary_layout} \ + -k ${TFM_SIGN_PRIVATE_KEY} \ + --public-key-format full \ + --align 1 \ + --pad \ + --pad-header \ + -H ${RE_IMAGE_OFFSET} \ + -s auto \ + ${host_binary} \ + ${host_binary_signed} + +} diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m_1.6.%.bbappend b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m_1.6.%.bbappend new file mode 100644 index 0000000000..da70bc7806 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m_1.6.%.bbappend @@ -0,0 +1,6 @@ +# Machine specific configurations + +MACHINE_TFM_REQUIRE ?= "" +MACHINE_TFM_REQUIRE:corstone1000 = "trusted-firmware-m-corstone1000.inc" + +require ${MACHINE_TFM_REQUIRE} diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0001-cmd-load-add-load-command-for-memory-mapped.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0001-cmd-load-add-load-command-for-memory-mapped.patch new file mode 100644 index 0000000000..d0ae964f1b --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0001-cmd-load-add-load-command-for-memory-mapped.patch @@ -0,0 +1,185 @@ +From c047b15fba9da36616bc9a346d9fe4d76e7ca4b2 Mon Sep 17 00:00:00 2001 +From: Rui Miguel Silva <rui.silva@linaro.org> +Date: Thu, 24 Jun 2021 09:25:00 +0100 +Subject: [PATCH 01/27] cmd: load: add load command for memory mapped + +cp.b is used a lot as a way to load binaries to memory and execute +them, however we may need to integrate this with the efi subsystem to +set it up as a bootdev. + +So, introduce a loadm command that will be consistent with the other +loadX commands and will call the efi API's. + +ex: loadm $kernel_addr $kernel_addr_r $kernel_size + +with this a kernel with CONFIG_EFI_STUB enabled will be loaded and +then subsequently booted with bootefi command. + +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + README | 1 + + cmd/Kconfig | 6 ++++ + cmd/bootefi.c | 12 ++++++++ + cmd/load.c | 48 ++++++++++++++++++++++++++++++++ + include/efi_loader.h | 2 ++ + lib/efi_loader/efi_device_path.c | 9 ++++++ + 6 files changed, 78 insertions(+) + +diff --git a/README b/README +index f51f392111f9..049bcd108980 100644 +--- a/README ++++ b/README +@@ -2760,6 +2760,7 @@ rarpboot- boot image via network using RARP/TFTP protocol + diskboot- boot from IDE devicebootd - boot default, i.e., run 'bootcmd' + loads - load S-Record file over serial line + loadb - load binary file over serial line (kermit mode) ++loadm - load binary blob from source address to destination address + md - memory display + mm - memory modify (auto-incrementing) + nm - memory modify (constant address) +diff --git a/cmd/Kconfig b/cmd/Kconfig +index 5e25e45fd288..ff50102a89c7 100644 +--- a/cmd/Kconfig ++++ b/cmd/Kconfig +@@ -1076,6 +1076,12 @@ config CMD_LOADB + help + Load a binary file over serial line. + ++config CMD_LOADM ++ bool "loadm" ++ default y ++ help ++ Load a binary over memory mapped. ++ + config CMD_LOADS + bool "loads" + default y +diff --git a/cmd/bootefi.c b/cmd/bootefi.c +index 53d9f0e0dcca..8d492ea9e70c 100644 +--- a/cmd/bootefi.c ++++ b/cmd/bootefi.c +@@ -34,6 +34,18 @@ static struct efi_device_path *bootefi_device_path; + static void *image_addr; + static size_t image_size; + ++/** ++ * efi_get_image_parameters() - return image parameters ++ * ++ * @img_addr: address of loaded image in memory ++ * @img_size: size of loaded image ++ */ ++void efi_get_image_parameters(void **img_addr, size_t *img_size) ++{ ++ *img_addr = image_addr; ++ *img_size = image_size; ++} ++ + /** + * efi_clear_bootdev() - clear boot device + */ +diff --git a/cmd/load.c b/cmd/load.c +index 7e4a552d90ef..1224a7f85bb3 100644 +--- a/cmd/load.c ++++ b/cmd/load.c +@@ -1063,6 +1063,44 @@ static ulong load_serial_ymodem(ulong offset, int mode) + + #endif + ++#if defined(CONFIG_CMD_LOADM) ++static int do_load_memory_bin(struct cmd_tbl *cmdtp, int flag, int argc, ++ char *const argv[]) ++{ ++ ulong addr, dest, size; ++ void *src, *dst; ++ ++ if (argc != 4) ++ return CMD_RET_USAGE; ++ ++ addr = simple_strtoul(argv[1], NULL, 16); ++ ++ dest = simple_strtoul(argv[2], NULL, 16); ++ ++ size = simple_strtoul(argv[3], NULL, 16); ++ ++ if (!size) { ++ printf("loadm: can not load zero bytes\n"); ++ return 1; ++ } ++ ++ src = map_sysmem(addr, size); ++ dst = map_sysmem(dest, size); ++ ++ memcpy(dst, src, size); ++ ++ unmap_sysmem(src); ++ unmap_sysmem(dst); ++ ++ if (IS_ENABLED(CONFIG_CMD_BOOTEFI)) ++ efi_set_bootdev("Mem", "", "", map_sysmem(dest, 0), size); ++ ++ printf("loaded bin to memory: size: %lu\n", size); ++ ++ return 0; ++} ++#endif ++ + /* -------------------------------------------------------------------- */ + + #if defined(CONFIG_CMD_LOADS) +@@ -1137,3 +1175,13 @@ U_BOOT_CMD( + ); + + #endif /* CONFIG_CMD_LOADB */ ++ ++#if defined(CONFIG_CMD_LOADM) ++U_BOOT_CMD( ++ loadm, 4, 0, do_load_memory_bin, ++ "load binary blob from source address to destination address", ++ "[src_addr] [dst_addr] [size]\n" ++ " - load a binary blob from one memory location to other" ++ " from src_addr to dst_addr by size bytes" ++); ++#endif /* CONFIG_CMD_LOADM */ +diff --git a/include/efi_loader.h b/include/efi_loader.h +index af36639ec6a7..126db279dd3e 100644 +--- a/include/efi_loader.h ++++ b/include/efi_loader.h +@@ -585,6 +585,8 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, + void efi_save_gd(void); + /* Call this to relocate the runtime section to an address space */ + void efi_runtime_relocate(ulong offset, struct efi_mem_desc *map); ++/* Call this to get image parameters */ ++void efi_get_image_parameters(void **img_addr, size_t *img_size); + /* Add a new object to the object list. */ + void efi_add_handle(efi_handle_t obj); + /* Create handle */ +diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c +index 0542aaae16c7..d8dc59b2c95c 100644 +--- a/lib/efi_loader/efi_device_path.c ++++ b/lib/efi_loader/efi_device_path.c +@@ -1138,6 +1138,8 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr, + { + struct blk_desc *desc = NULL; + struct disk_partition fs_partition; ++ size_t image_size; ++ void *image_addr; + int part = 0; + char *filename; + char *s; +@@ -1153,6 +1155,13 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr, + } else if (!strcmp(dev, "Uart")) { + if (device) + *device = efi_dp_from_uart(); ++ } else if (!strcmp(dev, "Mem")) { ++ efi_get_image_parameters(&image_addr, &image_size); ++ ++ if (device) ++ *device = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE, ++ (uintptr_t)image_addr, ++ image_size); + } else { + part = blk_get_device_part_str(dev, devnr, &desc, &fs_partition, + 1); +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0002-arm-add-support-to-corstone1000-platform.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0002-arm-add-support-to-corstone1000-platform.patch new file mode 100644 index 0000000000..e0caaa44b9 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0002-arm-add-support-to-corstone1000-platform.patch @@ -0,0 +1,677 @@ +From 5cc889db3279ef4944ab64e36db3dbab1bf9ffa5 Mon Sep 17 00:00:00 2001 +From: Rui Miguel Silva <rui.silva@linaro.org> +Date: Tue, 15 Feb 2022 09:44:10 +0000 +Subject: [PATCH 02/27] arm: add support to corstone1000 platform + +Corstone1000 is a platform from arm, which includes pre +verified Corstone SSE710 sub-system that combines Cortex-A and +Cortex-M processors [0]. + +This code adds the support for the Cortex-A35 implementation +at host side, it contains also the necessary bits to support +the Corstone 1000 FVP (Fixed Virtual Platform) [1] and also the +FPGA MPS3 board implementation of this platform. [2] + +0: https://documentation-service.arm.com/static/619e02b1f45f0b1fbf3a8f16 +1: https://developer.arm.com/tools-and-software/open-source-software/arm-platforms-software/arm-ecosystem-fvps +2: https://documentation-service.arm.com/static/61f3f4d7fa8173727a1b71bf + +Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + arch/arm/Kconfig | 8 ++ + arch/arm/dts/Makefile | 3 + + arch/arm/dts/corstone1000-fvp.dts | 23 +++ + arch/arm/dts/corstone1000-mps3.dts | 32 +++++ + arch/arm/dts/corstone1000.dtsi | 169 +++++++++++++++++++++++ + board/armltd/corstone1000/Kconfig | 12 ++ + board/armltd/corstone1000/MAINTAINERS | 7 + + board/armltd/corstone1000/Makefile | 7 + + board/armltd/corstone1000/corstone1000.c | 121 ++++++++++++++++ + configs/corstone1000_defconfig | 80 +++++++++++ + include/configs/corstone1000.h | 86 ++++++++++++ + 11 files changed, 548 insertions(+) + create mode 100644 arch/arm/dts/corstone1000-fvp.dts + create mode 100644 arch/arm/dts/corstone1000-mps3.dts + create mode 100644 arch/arm/dts/corstone1000.dtsi + create mode 100644 board/armltd/corstone1000/Kconfig + create mode 100644 board/armltd/corstone1000/MAINTAINERS + create mode 100644 board/armltd/corstone1000/Makefile + create mode 100644 board/armltd/corstone1000/corstone1000.c + create mode 100644 configs/corstone1000_defconfig + create mode 100644 include/configs/corstone1000.h + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 4567c183fb84..d64051b533a7 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -1266,6 +1266,12 @@ config TARGET_VEXPRESS64_JUNO + select USB + imply OF_HAS_PRIOR_STAGE + ++config TARGET_CORSTONE1000 ++ bool "Support Corstone1000 Platform" ++ select ARM64 ++ select PL01X_SERIAL ++ select DM ++ + config TARGET_TOTAL_COMPUTE + bool "Support Total Compute Platform" + select ARM64 +@@ -2198,6 +2204,8 @@ source "arch/arm/mach-nexell/Kconfig" + + source "board/armltd/total_compute/Kconfig" + ++source "board/armltd/corstone1000/Kconfig" ++ + source "board/bosch/shc/Kconfig" + source "board/bosch/guardian/Kconfig" + source "board/Marvell/octeontx/Kconfig" +diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile +index 644ba961a223..7de25d09c9fe 100644 +--- a/arch/arm/dts/Makefile ++++ b/arch/arm/dts/Makefile +@@ -1215,6 +1215,9 @@ dtb-$(CONFIG_TARGET_EA_LPC3250DEVKITV2) += lpc3250-ea3250.dtb + + dtb-$(CONFIG_ARCH_QEMU) += qemu-arm.dtb qemu-arm64.dtb + ++dtb-$(CONFIG_TARGET_CORSTONE1000) += corstone1000-mps3.dtb \ ++ corstone1000-fvp.dtb ++ + include $(srctree)/scripts/Makefile.dts + + targets += $(dtb-y) +diff --git a/arch/arm/dts/corstone1000-fvp.dts b/arch/arm/dts/corstone1000-fvp.dts +new file mode 100644 +index 000000000000..1fcc137a493c +--- /dev/null ++++ b/arch/arm/dts/corstone1000-fvp.dts +@@ -0,0 +1,23 @@ ++// SPDX-License-Identifier: GPL-2.0 or MIT ++/* ++ * Copyright (c) 2022, Arm Limited. All rights reserved. ++ * Copyright (c) 2022, Linaro Limited. All rights reserved. ++ * ++ */ ++ ++/dts-v1/; ++ ++#include "corstone1000.dtsi" ++ ++/ { ++ model = "ARM Corstone1000 FVP (Fixed Virtual Platform)"; ++ compatible = "arm,corstone1000-fvp"; ++ ++ smsc: ethernet@4010000 { ++ compatible = "smsc,lan91c111"; ++ reg = <0x40100000 0x10000>; ++ phy-mode = "mii"; ++ interrupts = <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>; ++ reg-io-width = <2>; ++ }; ++}; +diff --git a/arch/arm/dts/corstone1000-mps3.dts b/arch/arm/dts/corstone1000-mps3.dts +new file mode 100644 +index 000000000000..e3146747c2d9 +--- /dev/null ++++ b/arch/arm/dts/corstone1000-mps3.dts +@@ -0,0 +1,32 @@ ++// SPDX-License-Identifier: GPL-2.0 or MIT ++/* ++ * Copyright (c) 2022, Arm Limited. All rights reserved. ++ * Copyright (c) 2022, Linaro Limited. All rights reserved. ++ * ++ */ ++ ++/dts-v1/; ++ ++#include "corstone1000.dtsi" ++ ++/ { ++ model = "ARM Corstone1000 FPGA MPS3 board"; ++ compatible = "arm,corstone1000-mps3"; ++ ++ smsc: ethernet@4010000 { ++ compatible = "smsc,lan9220", "smsc,lan9115"; ++ reg = <0x40100000 0x10000>; ++ phy-mode = "mii"; ++ interrupts = <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>; ++ reg-io-width = <2>; ++ smsc,irq-push-pull; ++ }; ++ ++ usb_host: usb@40200000 { ++ compatible = "nxp,usb-isp1763"; ++ reg = <0x40200000 0x100000>; ++ interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>; ++ bus-width = <16>; ++ dr_mode = "host"; ++ }; ++}; +diff --git a/arch/arm/dts/corstone1000.dtsi b/arch/arm/dts/corstone1000.dtsi +new file mode 100644 +index 000000000000..d0194aa893f2 +--- /dev/null ++++ b/arch/arm/dts/corstone1000.dtsi +@@ -0,0 +1,169 @@ ++// SPDX-License-Identifier: GPL-2.0 or MIT ++/* ++ * Copyright (c) 2022, Arm Limited. All rights reserved. ++ * Copyright (c) 2022, Linaro Limited. All rights reserved. ++ * ++ */ ++ ++#include <dt-bindings/interrupt-controller/arm-gic.h> ++ ++/ { ++ interrupt-parent = <&gic>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ aliases { ++ serial0 = &uart0; ++ serial1 = &uart1; ++ }; ++ ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ ++ cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cpu: cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a35"; ++ reg = <0>; ++ next-level-cache = <&L2_0>; ++ }; ++ }; ++ ++ memory@88200000 { ++ device_type = "memory"; ++ reg = <0x88200000 0x77e00000>; ++ }; ++ ++ gic: interrupt-controller@1c000000 { ++ compatible = "arm,gic-400"; ++ #interrupt-cells = <3>; ++ #address-cells = <0>; ++ interrupt-controller; ++ reg = <0x1c010000 0x1000>, ++ <0x1c02f000 0x2000>, ++ <0x1c04f000 0x1000>, ++ <0x1c06f000 0x2000>; ++ interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(1) | ++ IRQ_TYPE_LEVEL_LOW)>; ++ }; ++ ++ L2_0: l2-cache0 { ++ compatible = "cache"; ++ cache-level = <2>; ++ cache-size = <0x80000>; ++ cache-line-size = <64>; ++ cache-sets = <1024>; ++ }; ++ ++ refclk100mhz: refclk100mhz { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <100000000>; ++ clock-output-names = "apb_pclk"; ++ }; ++ ++ smbclk: refclk24mhzx2 { ++ /* Reference 24MHz clock x 2 */ ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <48000000>; ++ clock-output-names = "smclk"; ++ }; ++ ++ timer { ++ compatible = "arm,armv8-timer"; ++ interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(1) | ++ IRQ_TYPE_LEVEL_LOW)>, ++ <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(1) | ++ IRQ_TYPE_LEVEL_LOW)>, ++ <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(1) | ++ IRQ_TYPE_LEVEL_LOW)>, ++ <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(1) | ++ IRQ_TYPE_LEVEL_LOW)>; ++ }; ++ ++ uartclk: uartclk { ++ /* UART clock - 50MHz */ ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <50000000>; ++ clock-output-names = "uartclk"; ++ }; ++ ++ psci { ++ compatible = "arm,psci-1.0", "arm,psci-0.2"; ++ method = "smc"; ++ }; ++ ++ soc { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ interrupt-parent = <&gic>; ++ ranges; ++ ++ timer@1a220000 { ++ compatible = "arm,armv7-timer-mem"; ++ reg = <0x1a220000 0x1000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ clock-frequency = <50000000>; ++ ranges; ++ ++ frame@1a230000 { ++ frame-number = <0>; ++ interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>; ++ reg = <0x1a230000 0x1000>; ++ }; ++ }; ++ ++ uart0: serial@1a510000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x1a510000 0x1000>; ++ interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&uartclk>, <&refclk100mhz>; ++ clock-names = "uartclk", "apb_pclk"; ++ }; ++ ++ uart1: serial@1a520000 { ++ compatible = "arm,pl011", "arm,primecell"; ++ reg = <0x1a520000 0x1000>; ++ interrupts = <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>; ++ clocks = <&uartclk>, <&refclk100mhz>; ++ clock-names = "uartclk", "apb_pclk"; ++ }; ++ ++ mhu_hse1: mailbox@1b820000 { ++ compatible = "arm,mhuv2-tx", "arm,primecell"; ++ reg = <0x1b820000 0x1000>; ++ clocks = <&refclk100mhz>; ++ clock-names = "apb_pclk"; ++ interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>; ++ #mbox-cells = <2>; ++ arm,mhuv2-protocols = <0 0>; ++ secure-status = "okay"; /* secure-world-only */ ++ status = "disabled"; ++ }; ++ ++ mhu_seh1: mailbox@1b830000 { ++ compatible = "arm,mhuv2-rx", "arm,primecell"; ++ reg = <0x1b830000 0x1000>; ++ clocks = <&refclk100mhz>; ++ clock-names = "apb_pclk"; ++ interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>; ++ #mbox-cells = <2>; ++ arm,mhuv2-protocols = <0 0>; ++ secure-status = "okay"; /* secure-world-only */ ++ status = "disabled"; ++ }; ++ }; ++ ++ arm_ffa: arm_ffa { ++ compatible = "arm,ffa"; ++ method = "smc"; ++ }; ++}; +diff --git a/board/armltd/corstone1000/Kconfig b/board/armltd/corstone1000/Kconfig +new file mode 100644 +index 000000000000..709674d4cf7d +--- /dev/null ++++ b/board/armltd/corstone1000/Kconfig +@@ -0,0 +1,12 @@ ++if TARGET_CORSTONE1000 ++ ++config SYS_BOARD ++ default "corstone1000" ++ ++config SYS_VENDOR ++ default "armltd" ++ ++config SYS_CONFIG_NAME ++ default "corstone1000" ++ ++endif +diff --git a/board/armltd/corstone1000/MAINTAINERS b/board/armltd/corstone1000/MAINTAINERS +new file mode 100644 +index 000000000000..8c905686de76 +--- /dev/null ++++ b/board/armltd/corstone1000/MAINTAINERS +@@ -0,0 +1,7 @@ ++CORSTONE1000 BOARD ++M: Rui Miguel Silva <rui.silva@linaro.org> ++M: Vishnu Banavath <vishnu.banavath@arm.com> ++S: Maintained ++F: board/armltd/corstone1000/ ++F: include/configs/corstone1000.h ++F: configs/corstone1000_defconfig +diff --git a/board/armltd/corstone1000/Makefile b/board/armltd/corstone1000/Makefile +new file mode 100644 +index 000000000000..77a82c28929b +--- /dev/null ++++ b/board/armltd/corstone1000/Makefile +@@ -0,0 +1,7 @@ ++# SPDX-License-Identifier: GPL-2.0+ ++# ++# (C) Copyright 2022 Arm Limited ++# (C) Copyright 2022 Linaro ++# Rui Miguel Silva <rui.silva@linaro.org> ++ ++obj-y := corstone1000.o +diff --git a/board/armltd/corstone1000/corstone1000.c b/board/armltd/corstone1000/corstone1000.c +new file mode 100644 +index 000000000000..eff1739f0b02 +--- /dev/null ++++ b/board/armltd/corstone1000/corstone1000.c +@@ -0,0 +1,121 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * (C) Copyright 2022 ARM Limited ++ * (C) Copyright 2022 Linaro ++ * Rui Miguel Silva <rui.silva@linaro.org> ++ */ ++ ++#include <common.h> ++#include <dm.h> ++#include <dm/platform_data/serial_pl01x.h> ++#include <asm/armv8/mmu.h> ++#include <asm/global_data.h> ++ ++ ++static const struct pl01x_serial_plat serial_plat = { ++ .base = V2M_UART0, ++ .type = TYPE_PL011, ++ .clock = CONFIG_PL011_CLOCK, ++}; ++ ++U_BOOT_DRVINFO(corstone1000_serials) = { ++ .name = "serial_pl01x", ++ .plat = &serial_plat, ++}; ++ ++static struct mm_region corstone1000_mem_map[] = { ++ { ++ /* CVM */ ++ .virt = 0x02000000UL, ++ .phys = 0x02000000UL, ++ .size = 0x02000000UL, ++ .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | ++ PTE_BLOCK_INNER_SHARE ++ }, { ++ /* QSPI */ ++ .virt = 0x08000000UL, ++ .phys = 0x08000000UL, ++ .size = 0x08000000UL, ++ .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | ++ PTE_BLOCK_INNER_SHARE ++ }, { ++ /* Host Peripherals */ ++ .virt = 0x1A000000UL, ++ .phys = 0x1A000000UL, ++ .size = 0x26000000UL, ++ .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | ++ PTE_BLOCK_NON_SHARE | ++ PTE_BLOCK_PXN | PTE_BLOCK_UXN ++ }, { ++ /* USB */ ++ .virt = 0x40200000UL, ++ .phys = 0x40200000UL, ++ .size = 0x00100000UL, ++ .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | ++ PTE_BLOCK_NON_SHARE | ++ PTE_BLOCK_PXN | PTE_BLOCK_UXN ++ }, { ++ /* ethernet */ ++ .virt = 0x40100000UL, ++ .phys = 0x40100000UL, ++ .size = 0x00100000UL, ++ .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | ++ PTE_BLOCK_NON_SHARE | ++ PTE_BLOCK_PXN | PTE_BLOCK_UXN ++ }, { ++ /* OCVM */ ++ .virt = 0x80000000UL, ++ .phys = 0x80000000UL, ++ .size = 0x80000000UL, ++ .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | ++ PTE_BLOCK_INNER_SHARE ++ }, { ++ /* List terminator */ ++ 0, ++ } ++}; ++ ++struct mm_region *mem_map = corstone1000_mem_map; ++ ++int board_init(void) ++{ ++ return 0; ++} ++ ++int dram_init(void) ++{ ++ gd->ram_size = PHYS_SDRAM_1_SIZE; ++ ++ return 0; ++} ++ ++int dram_init_banksize(void) ++{ ++ gd->bd->bi_dram[0].start = PHYS_SDRAM_1; ++ gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; ++ ++ return 0; ++} ++ ++/* ++ * Board specific ethernet initialization routine. ++ * */ ++int board_eth_init(struct bd_info *bis) ++{ ++ int rc = 0; ++ ++#ifndef CONFIG_DM_ETH ++#ifdef CONFIG_SMC91111 ++ rc = smc91111_initialize(0, CONFIG_SMC91111_BASE); ++#endif ++#ifdef CONFIG_SMC911X ++ rc = smc911x_initialize(0, CONFIG_SMC911X_BASE); ++#endif ++#endif ++ ++ return rc; ++} ++ ++void reset_cpu(ulong addr) ++{ ++} +diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig +new file mode 100644 +index 000000000000..02f931b0d469 +--- /dev/null ++++ b/configs/corstone1000_defconfig +@@ -0,0 +1,80 @@ ++CONFIG_ARM=y ++CONFIG_TARGET_CORSTONE1000=y ++CONFIG_SYS_TEXT_BASE=0x80000000 ++CONFIG_SYS_MALLOC_F_LEN=0x2000 ++CONFIG_SYS_MALLOC_LEN=0x2000000 ++CONFIG_SYS_LOAD_ADDR=0x82100000 ++CONFIG_NR_DRAM_BANKS=1 ++CONFIG_IDENT_STRING=" corstone1000 aarch64 " ++CONFIG_FIT=y ++CONFIG_BOOTDELAY=3 ++CONFIG_USE_BOOTARGS=y ++CONFIG_BOOTARGS="console=ttyAMA0 loglevel=9 ip=dhcp earlyprintk" ++CONFIG_LOGLEVEL=7 ++# CONFIG_DISPLAY_CPUINFO is not set ++# CONFIG_DISPLAY_BOARDINFO is not set ++CONFIG_HUSH_PARSER=y ++CONFIG_SYS_PROMPT="corstone1000# " ++# CONFIG_CMD_CONSOLE is not set ++CONFIG_CMD_BOOTZ=y ++CONFIG_CMD_BOOTM=y ++CONFIG_CMD_LOADM=y ++CONFIG_CMD_BOOTEFI=y ++CONFIG_EFI_LOADER=y ++CONFIG_EFI_PARTITION=y ++CONFIG_CMD_BOOTEFI_HELLO_COMPILE=y ++CONFIG_CMD_BOOTEFI_HELLO=y ++# CONFIG_CMD_XIMG is not set ++# CONFIG_CMD_ENV_EXISTS is not set ++CONFIG_CMD_NVEDIT_EFI=y ++# CONFIG_CMD_LOADS is not set ++CONFIG_CMD_USB=y ++CONFIG_CMD_ITEST=y ++# CONFIG_CMD_SETEXPR is not set ++# CONFIG_CMD_NFS is not set ++CONFIG_CMD_MII=y ++CONFIG_CMD_CACHE=y ++CONFIG_CMD_EFIDEBUG=y ++CONFIG_CMD_FAT=y ++CONFIG_OF_CONTROL=y ++CONFIG_REGMAP=y ++# CONFIG_MMC is not set ++CONFIG_DM_SERIAL=y ++CONFIG_USB=y ++CONFIG_DM_USB=y ++CONFIG_USB_STORAGE=y ++CONFIG_EFI_MM_COMM_TEE=y ++# CONFIG_OPTEE is not set ++# CONFIG_GENERATE_SMBIOS_TABLE is not set ++# CONFIG_HEXDUMP is not set ++CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y ++CONFIG_EFI_CAPSULE_ON_DISK=y ++# CONFIG_EFI_CAPSULE_ON_DISK_EARLY is not set ++# CONFIG_EFI_CAPSULE_AUTHENTICATE is not set ++CONFIG_EFI_HAVE_CAPSULE_SUPPORT=y ++CONFIG_EFI_CAPSULE_FIRMWARE_FIT=y ++CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y ++CONFIG_EFI_SECURE_BOOT=y ++CONFIG_DM_RTC=y ++CONFIG_CMD_RTC=y ++CONFIG_EFI_GET_TIME=y ++CONFIG_EFI_SET_TIME=y ++CONFIG_RTC_EMULATION=y ++CONFIG_PSCI_RESET=y ++CONFIG_DISTRO_DEFAULTS=y ++CONFIG_CMD_DHCP=y ++CONFIG_SMC911X=y ++CONFIG_SMC911X_BASE=0x40100000 ++CONFIG_DM_ETH=y ++CONFIG_PHY_SMSC=y ++CONFIG_CMD_BOOTEFI_SELFTEST=y ++CONFIG_CMD_TIME=y ++CONFIG_CMD_GETTIME=y ++CONFIG_NET_RANDOM_ETHADDR=y ++CONFIG_VERSION_VARIABLE=y ++CONFIG_PHYLIB=y ++CONFIG_PHY=y ++CONFIG_RAM=y ++CONFIG_ERRNO_STR=y ++CONFIG_CMD_EDITENV=y ++CONFIG_MISC=y +diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h +new file mode 100644 +index 000000000000..cf166f107efd +--- /dev/null ++++ b/include/configs/corstone1000.h +@@ -0,0 +1,86 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * (C) Copyright 2022 ARM Limited ++ * (C) Copyright 2022 Linaro ++ * Rui Miguel Silva <rui.silva@linaro.org> ++ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> ++ * ++ * Configuration for Corstone1000. Parts were derived from other ARM ++ * configurations. ++ */ ++ ++#ifndef __CORSTONE1000_H ++#define __CORSTONE1000_H ++ ++#include <linux/sizes.h> ++ ++#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x03f00000) ++#define CONFIG_SKIP_LOWLEVEL_INIT ++ ++#define CONFIG_SYS_HZ 1000 ++ ++#define V2M_SRAM0 0x02000000 ++#define V2M_QSPI 0x08000000 ++ ++#define V2M_DEBUG 0x10000000 ++#define V2M_BASE_PERIPH 0x1A000000 ++ ++#define V2M_BASE 0x80000000 ++ ++#define V2M_PERIPH_OFFSET(x) (x << 16) ++ ++#define V2M_SYSID (V2M_BASE_PERIPH) ++#define V2M_SYSCTL (V2M_BASE_PERIPH + V2M_PERIPH_OFFSET(1)) ++ ++#define V2M_COUNTER_CTL (V2M_BASE_PERIPH + V2M_PERIPH_OFFSET(32)) ++#define V2M_COUNTER_READ (V2M_BASE_PERIPH + V2M_PERIPH_OFFSET(33)) ++ ++#define V2M_TIMER_CTL (V2M_BASE_PERIPH + V2M_PERIPH_OFFSET(34)) ++#define V2M_TIMER_BASE0 (V2M_BASE_PERIPH + V2M_PERIPH_OFFSET(35)) ++ ++#define V2M_UART0 (V2M_BASE_PERIPH + V2M_PERIPH_OFFSET(81)) ++#define V2M_UART1 (V2M_BASE_PERIPH + V2M_PERIPH_OFFSET(82)) ++ ++#define CONFIG_PL011_CLOCK 50000000 ++ ++/* Physical Memory Map */ ++#define PHYS_SDRAM_1 (V2M_BASE) ++#define PHYS_SDRAM_1_SIZE 0x80000000 ++ ++#define CONFIG_ENV_SECT_SIZE SZ_64K ++ ++#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 ++ ++/* Monitor Command Prompt */ ++#define CONFIG_SYS_CBSIZE 512 /* Console I/O Buffer Size */ ++#define CONFIG_SYS_MAXARGS 64 /* max command args */ ++ ++#define CONFIG_EXTRA_ENV_SETTINGS \ ++ "usb_pgood_delay=250\0" \ ++ "boot_bank_flag=0x08002000\0" \ ++ "kernel_addr_bank_0=0x083EE000\0" \ ++ "kernel_addr_bank_1=0x0936E000\0" \ ++ "retrieve_kernel_load_addr=" \ ++ "if itest.l *${boot_bank_flag} == 0; then " \ ++ "setenv kernel_addr $kernel_addr_bank_0;" \ ++ "else " \ ++ "setenv kernel_addr $kernel_addr_bank_1;" \ ++ "fi;" \ ++ "\0" \ ++ "kernel_addr_r=0x88200000\0" \ ++ "fdt_high=0xffffffff\0" ++ ++/* ++ * config_distro_bootcmd define the boot command to distro_bootcmd, but we here ++ * want to first try to load a kernel if exists, override that config then ++ */ ++#undef CONFIG_BOOTCOMMAND ++ ++#define CONFIG_BOOTCOMMAND \ ++ "run retrieve_kernel_load_addr;" \ ++ "echo Loading kernel from $kernel_addr to memory ... ;" \ ++ "loadm $kernel_addr $kernel_addr_r 0xc00000;" \ ++ "usb start; usb reset;" \ ++ "run distro_bootcmd;" \ ++ "bootefi $kernel_addr_r $fdtcontroladdr;" ++#endif +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0003-usb-common-move-urb-code-to-common.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0003-usb-common-move-urb-code-to-common.patch new file mode 100644 index 0000000000..9974915a0b --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0003-usb-common-move-urb-code-to-common.patch @@ -0,0 +1,497 @@ +From b8f4f76e135ff2061d0032292f5209ac911dba16 Mon Sep 17 00:00:00 2001 +From: Rui Miguel Silva <rui.silva@linaro.org> +Date: Mon, 28 Jun 2021 23:20:55 +0100 +Subject: [PATCH 03/27] usb: common: move urb code to common + +Move urb code from musb only use to a more common scope, so other +drivers in the future can use the handling of urb in usb. + +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + drivers/usb/common/Makefile | 2 + + drivers/usb/common/usb_urb.c | 160 ++++++++++++++++++ + drivers/usb/host/r8a66597-hcd.c | 30 +--- + drivers/usb/musb-new/musb_core.c | 2 +- + drivers/usb/musb-new/musb_host.c | 2 +- + drivers/usb/musb-new/musb_host.h | 2 +- + drivers/usb/musb-new/musb_uboot.c | 38 +---- + drivers/usb/musb-new/musb_uboot.h | 2 +- + .../linux/usb/usb_urb_compat.h | 46 ++++- + include/usb_defs.h | 32 ++++ + 10 files changed, 240 insertions(+), 76 deletions(-) + create mode 100644 drivers/usb/common/usb_urb.c + rename drivers/usb/musb-new/usb-compat.h => include/linux/usb/usb_urb_compat.h (60%) + +diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile +index 3bedbf213f47..dc05cb0a5077 100644 +--- a/drivers/usb/common/Makefile ++++ b/drivers/usb/common/Makefile +@@ -4,5 +4,7 @@ + # + + obj-$(CONFIG_$(SPL_)DM_USB) += common.o ++obj-$(CONFIG_USB_MUSB_HCD) += usb_urb.o ++obj-$(CONFIG_USB_MUSB_UDC) += usb_urb.o + obj-$(CONFIG_USB_EHCI_FSL) += fsl-dt-fixup.o fsl-errata.o + obj-$(CONFIG_USB_XHCI_FSL) += fsl-dt-fixup.o fsl-errata.o +diff --git a/drivers/usb/common/usb_urb.c b/drivers/usb/common/usb_urb.c +new file mode 100644 +index 000000000000..be3b6b9f32e8 +--- /dev/null ++++ b/drivers/usb/common/usb_urb.c +@@ -0,0 +1,160 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Common code for usb urb handling, based on the musb-new code ++ * ++ * Copyright 2021 Linaro, Rui Miguel Silva <rui.silva@linaro.org> ++ * ++ */ ++ ++#include <dm/device.h> ++#include <dm/device_compat.h> ++#include <linux/usb/usb_urb_compat.h> ++ ++#include <time.h> ++#include <usb.h> ++ ++#if CONFIG_IS_ENABLED(DM_USB) ++struct usb_device *usb_dev_get_parent(struct usb_device *udev) ++{ ++ struct udevice *parent = udev->dev->parent; ++ ++ /* ++ * When called from usb-uclass.c: usb_scan_device() udev->dev points ++ * to the parent udevice, not the actual udevice belonging to the ++ * udev as the device is not instantiated yet. ++ * ++ * If dev is an usb-bus, then we are called from usb_scan_device() for ++ * an usb-device plugged directly into the root port, return NULL. ++ */ ++ if (device_get_uclass_id(udev->dev) == UCLASS_USB) ++ return NULL; ++ ++ /* ++ * If these 2 are not the same we are being called from ++ * usb_scan_device() and udev itself is the parent. ++ */ ++ if (dev_get_parent_priv(udev->dev) != udev) ++ return udev; ++ ++ /* We are being called normally, use the parent pointer */ ++ if (device_get_uclass_id(parent) == UCLASS_USB_HUB) ++ return dev_get_parent_priv(parent); ++ ++ return NULL; ++} ++#else ++struct usb_device *usb_dev_get_parent(struct usb_device *udev) ++{ ++ return udev->parent; ++} ++#endif ++ ++static void usb_urb_complete(struct urb *urb) ++{ ++ urb->dev->status &= ~USB_ST_NOT_PROC; ++ urb->dev->act_len = urb->actual_length; ++ ++ if (urb->status == -EINPROGRESS) ++ urb->status = 0; ++} ++ ++void usb_urb_fill(struct urb *urb, struct usb_host_endpoint *hep, ++ struct usb_device *dev, int endpoint_type, ++ unsigned long pipe, void *buffer, int len, ++ struct devrequest *setup, int interval) ++{ ++ int epnum = usb_pipeendpoint(pipe); ++ int is_in = usb_pipein(pipe); ++ u16 maxpacketsize = is_in ? dev->epmaxpacketin[epnum] : ++ dev->epmaxpacketout[epnum]; ++ ++ memset(urb, 0, sizeof(struct urb)); ++ memset(hep, 0, sizeof(struct usb_host_endpoint)); ++ INIT_LIST_HEAD(&hep->urb_list); ++ INIT_LIST_HEAD(&urb->urb_list); ++ urb->ep = hep; ++ urb->complete = usb_urb_complete; ++ urb->status = -EINPROGRESS; ++ urb->dev = dev; ++ urb->pipe = pipe; ++ urb->transfer_buffer = buffer; ++ urb->transfer_dma = (unsigned long)buffer; ++ urb->transfer_buffer_length = len; ++ urb->setup_packet = (unsigned char *)setup; ++ ++ urb->ep->desc.wMaxPacketSize = __cpu_to_le16(maxpacketsize); ++ urb->ep->desc.bmAttributes = endpoint_type; ++ urb->ep->desc.bEndpointAddress = ((is_in ? USB_DIR_IN : USB_DIR_OUT) | ++ epnum); ++ urb->ep->desc.bInterval = interval; ++} ++ ++int usb_urb_submit(struct usb_hcd *hcd, struct urb *urb) ++{ ++ const struct usb_urb_ops *ops = hcd->urb_ops; ++ unsigned long timeout; ++ int ret; ++ ++ if (!ops) ++ return -EINVAL; ++ ++ ret = ops->urb_enqueue(hcd, urb, 0); ++ if (ret < 0) { ++ printf("Failed to enqueue URB to controller\n"); ++ return ret; ++ } ++ ++ timeout = get_timer(0) + USB_TIMEOUT_MS(urb->pipe); ++ do { ++ if (ctrlc()) ++ return -EIO; ++ ops->isr(0, hcd); ++ } while (urb->status == -EINPROGRESS && get_timer(0) < timeout); ++ ++ if (urb->status == -EINPROGRESS) ++ ops->urb_dequeue(hcd, urb, -ETIME); ++ ++ return urb->status; ++} ++ ++int usb_urb_submit_control(struct usb_hcd *hcd, struct urb *urb, ++ struct usb_host_endpoint *hep, ++ struct usb_device *dev, unsigned long pipe, ++ void *buffer, int len, struct devrequest *setup, ++ int interval, enum usb_device_speed speed) ++{ ++ const struct usb_urb_ops *ops = hcd->urb_ops; ++ ++ usb_urb_fill(urb, hep, dev, USB_ENDPOINT_XFER_CONTROL, pipe, buffer, ++ len, setup, 0); ++ ++ /* Fix speed for non hub-attached devices */ ++ if (!usb_dev_get_parent(dev)) { ++ dev->speed = speed; ++ if (ops->hub_control) ++ return ops->hub_control(hcd, dev, pipe, buffer, len, ++ setup); ++ } ++ ++ return usb_urb_submit(hcd, urb); ++} ++ ++int usb_urb_submit_bulk(struct usb_hcd *hcd, struct urb *urb, ++ struct usb_host_endpoint *hep, struct usb_device *dev, ++ unsigned long pipe, void *buffer, int len) ++{ ++ usb_urb_fill(urb, hep, dev, USB_ENDPOINT_XFER_BULK, pipe, buffer, len, ++ NULL, 0); ++ ++ return usb_urb_submit(hcd, urb); ++} ++ ++int usb_urb_submit_irq(struct usb_hcd *hcd, struct urb *urb, ++ struct usb_host_endpoint *hep, struct usb_device *dev, ++ unsigned long pipe, void *buffer, int len, int interval) ++{ ++ usb_urb_fill(urb, hep, dev, USB_ENDPOINT_XFER_INT, pipe, buffer, len, ++ NULL, interval); ++ ++ return usb_urb_submit(hcd, urb); ++} +diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c +index f1fc93f3d403..3ccbc16da379 100644 +--- a/drivers/usb/host/r8a66597-hcd.c ++++ b/drivers/usb/host/r8a66597-hcd.c +@@ -14,6 +14,7 @@ + #include <dm/device_compat.h> + #include <linux/delay.h> + #include <linux/iopoll.h> ++#include <linux/usb/usb_urb_compat.h> + #include <power/regulator.h> + + #include "r8a66597.h" +@@ -24,35 +25,6 @@ + #define R8A66597_DPRINT(...) + #endif + +-static inline struct usb_device *usb_dev_get_parent(struct usb_device *udev) +-{ +- struct udevice *parent = udev->dev->parent; +- +- /* +- * When called from usb-uclass.c: usb_scan_device() udev->dev points +- * to the parent udevice, not the actual udevice belonging to the +- * udev as the device is not instantiated yet. +- * +- * If dev is an usb-bus, then we are called from usb_scan_device() for +- * an usb-device plugged directly into the root port, return NULL. +- */ +- if (device_get_uclass_id(udev->dev) == UCLASS_USB) +- return NULL; +- +- /* +- * If these 2 are not the same we are being called from +- * usb_scan_device() and udev itself is the parent. +- */ +- if (dev_get_parent_priv(udev->dev) != udev) +- return udev; +- +- /* We are being called normally, use the parent pointer */ +- if (device_get_uclass_id(parent) == UCLASS_USB_HUB) +- return dev_get_parent_priv(parent); +- +- return NULL; +-} +- + static void get_hub_data(struct usb_device *dev, u16 *hub_devnum, u16 *hubport) + { + struct usb_device *parent = usb_dev_get_parent(dev); +diff --git a/drivers/usb/musb-new/musb_core.c b/drivers/usb/musb-new/musb_core.c +index 18d9bc805f8a..fc7af7484e4c 100644 +--- a/drivers/usb/musb-new/musb_core.c ++++ b/drivers/usb/musb-new/musb_core.c +@@ -89,9 +89,9 @@ + #include <linux/usb/ch9.h> + #include <linux/usb/gadget.h> + #include <linux/usb/musb.h> ++#include <linux/usb/usb_urb_compat.h> + #include <asm/io.h> + #include "linux-compat.h" +-#include "usb-compat.h" + #endif + + #include "musb_core.h" +diff --git a/drivers/usb/musb-new/musb_host.c b/drivers/usb/musb-new/musb_host.c +index acb2d40f3b5a..e5905d90d66f 100644 +--- a/drivers/usb/musb-new/musb_host.c ++++ b/drivers/usb/musb-new/musb_host.c +@@ -26,8 +26,8 @@ + #include <dm/device_compat.h> + #include <usb.h> + #include <linux/bug.h> ++#include <linux/usb/usb_urb_compat.h> + #include "linux-compat.h" +-#include "usb-compat.h" + #endif + + #include "musb_core.h" +diff --git a/drivers/usb/musb-new/musb_host.h b/drivers/usb/musb-new/musb_host.h +index afc8fa35a738..5a604bdb0cf2 100644 +--- a/drivers/usb/musb-new/musb_host.h ++++ b/drivers/usb/musb-new/musb_host.h +@@ -10,7 +10,7 @@ + #ifndef _MUSB_HOST_H + #define _MUSB_HOST_H + #ifdef __UBOOT__ +-#include "usb-compat.h" ++#include <linux/usb/usb_urb_compat.h> + #endif + + static inline struct usb_hcd *musb_to_hcd(struct musb *musb) +diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c +index 61ff68def2fa..d186facc7e02 100644 +--- a/drivers/usb/musb-new/musb_uboot.c ++++ b/drivers/usb/musb-new/musb_uboot.c +@@ -8,10 +8,10 @@ + #include <linux/errno.h> + #include <linux/usb/ch9.h> + #include <linux/usb/gadget.h> ++#include <linux/usb/usb_urb_compat.h> + + #include <usb.h> + #include "linux-compat.h" +-#include "usb-compat.h" + #include "musb_core.h" + #include "musb_host.h" + #include "musb_gadget.h" +@@ -453,39 +453,3 @@ struct musb *musb_register(struct musb_hdrc_platform_data *plat, void *bdata, + + return *musbp; + } +- +-#if CONFIG_IS_ENABLED(DM_USB) +-struct usb_device *usb_dev_get_parent(struct usb_device *udev) +-{ +- struct udevice *parent = udev->dev->parent; +- +- /* +- * When called from usb-uclass.c: usb_scan_device() udev->dev points +- * to the parent udevice, not the actual udevice belonging to the +- * udev as the device is not instantiated yet. +- * +- * If dev is an usb-bus, then we are called from usb_scan_device() for +- * an usb-device plugged directly into the root port, return NULL. +- */ +- if (device_get_uclass_id(udev->dev) == UCLASS_USB) +- return NULL; +- +- /* +- * If these 2 are not the same we are being called from +- * usb_scan_device() and udev itself is the parent. +- */ +- if (dev_get_parent_priv(udev->dev) != udev) +- return udev; +- +- /* We are being called normally, use the parent pointer */ +- if (device_get_uclass_id(parent) == UCLASS_USB_HUB) +- return dev_get_parent_priv(parent); +- +- return NULL; +-} +-#else +-struct usb_device *usb_dev_get_parent(struct usb_device *udev) +-{ +- return udev->parent; +-} +-#endif +diff --git a/drivers/usb/musb-new/musb_uboot.h b/drivers/usb/musb-new/musb_uboot.h +index 18282efccc9d..6b162f03b19e 100644 +--- a/drivers/usb/musb-new/musb_uboot.h ++++ b/drivers/usb/musb-new/musb_uboot.h +@@ -8,8 +8,8 @@ + #define __MUSB_UBOOT_H__ + + #include <usb.h> ++#include <linux/usb/usb_urb_compat.h> + #include "linux-compat.h" +-#include "usb-compat.h" + #include "musb_core.h" + + struct musb_host_data { +diff --git a/drivers/usb/musb-new/usb-compat.h b/include/linux/usb/usb_urb_compat.h +similarity index 60% +rename from drivers/usb/musb-new/usb-compat.h +rename to include/linux/usb/usb_urb_compat.h +index df68c9220a7a..5ed96fa64e96 100644 +--- a/drivers/usb/musb-new/usb-compat.h ++++ b/include/linux/usb/usb_urb_compat.h +@@ -1,16 +1,31 @@ +-#ifndef __USB_COMPAT_H__ +-#define __USB_COMPAT_H__ ++#ifndef __USB_URB_COMPAT_H__ ++#define __USB_URB_COMPAT_H__ + +-#include "usb.h" ++#include <linux/compat.h> ++#include <usb.h> + + struct udevice; ++struct urb; ++struct usb_hcd; ++ ++ ++struct usb_urb_ops { ++ int (*urb_enqueue)(struct usb_hcd *hcd, struct urb *urb, ++ gfp_t mem_flags); ++ int (*urb_dequeue)(struct usb_hcd *hcd, struct urb *urb, int status); ++ int (*hub_control)(struct usb_hcd *hcd, struct usb_device *dev, ++ unsigned long pipe, void *buffer, int len, ++ struct devrequest *setup); ++ irqreturn_t (*isr)(int irq, void *priv); ++}; + + struct usb_hcd { + void *hcd_priv; ++ const struct usb_urb_ops *urb_ops; + }; + + struct usb_host_endpoint { +- struct usb_endpoint_descriptor desc; ++ struct usb_endpoint_descriptor desc; + struct list_head urb_list; + void *hcpriv; + }; +@@ -23,8 +38,6 @@ struct usb_host_endpoint { + #define URB_SHORT_NOT_OK 0x0001 /* report short reads as errors */ + #define URB_ZERO_PACKET 0x0040 /* Finish bulk OUT with short packet */ + +-struct urb; +- + typedef void (*usb_complete_t)(struct urb *); + + struct urb { +@@ -76,4 +89,25 @@ static inline int usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, + */ + struct usb_device *usb_dev_get_parent(struct usb_device *udev); + ++int usb_urb_submit_control(struct usb_hcd *hcd, struct urb *urb, ++ struct usb_host_endpoint *hep, ++ struct usb_device *dev, unsigned long pipe, ++ void *buffer, int len, struct devrequest *setup, ++ int interval, enum usb_device_speed speed); ++ ++int usb_urb_submit_bulk(struct usb_hcd *hcd, struct urb *urb, ++ struct usb_host_endpoint *hep, struct usb_device *dev, ++ unsigned long pipe, void *buffer, int len); ++ ++int usb_urb_submit_irq(struct usb_hcd *hcd, struct urb *urb, ++ struct usb_host_endpoint *hep, struct usb_device *dev, ++ unsigned long pipe, void *buffer, int len, int interval); ++ ++void usb_urb_fill(struct urb *urb, struct usb_host_endpoint *hep, ++ struct usb_device *dev, int endpoint_type, ++ unsigned long pipe, void *buffer, int len, ++ struct devrequest *setup, int interval); ++ ++int usb_urb_submit(struct usb_hcd *hcd, struct urb *urb); ++ + #endif /* __USB_COMPAT_H__ */ +diff --git a/include/usb_defs.h b/include/usb_defs.h +index 6dd2c997f9b3..ec00161710a5 100644 +--- a/include/usb_defs.h ++++ b/include/usb_defs.h +@@ -81,6 +81,32 @@ + #define EndpointOutRequest \ + ((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) + ++/* class requests from the USB 2.0 hub spec, table 11-15 */ ++#define HUB_CLASS_REQ(dir, type, request) ((((dir) | (type)) << 8) | (request)) ++/* GetBusState and SetHubDescriptor are optional, omitted */ ++#define ClearHubFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_HUB, \ ++ USB_REQ_CLEAR_FEATURE) ++#define ClearPortFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, \ ++ USB_REQ_CLEAR_FEATURE) ++#define GetHubDescriptor HUB_CLASS_REQ(USB_DIR_IN, USB_RT_HUB, \ ++ USB_REQ_GET_DESCRIPTOR) ++#define GetHubStatus HUB_CLASS_REQ(USB_DIR_IN, USB_RT_HUB, \ ++ USB_REQ_GET_STATUS) ++#define GetPortStatus HUB_CLASS_REQ(USB_DIR_IN, USB_RT_PORT, \ ++ USB_REQ_GET_STATUS) ++#define SetHubFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_HUB, \ ++ USB_REQ_SET_FEATURE) ++#define SetPortFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, \ ++ USB_REQ_SET_FEATURE) ++#define ClearTTBuffer HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, \ ++ HUB_CLEAR_TT_BUFFER) ++#define ResetTT HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, \ ++ HUB_RESET_TT) ++#define GetTTState HUB_CLASS_REQ(USB_DIR_IN, USB_RT_PORT, \ ++ HUB_GET_TT_STATE) ++#define StopTT HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, \ ++ HUB_STOP_TT) ++ + /* Descriptor types */ + #define USB_DT_DEVICE 0x01 + #define USB_DT_CONFIG 0x02 +@@ -289,10 +315,16 @@ + #define USB_SS_PORT_STAT_C_CONFIG_ERROR 0x0080 + + /* wHubCharacteristics (masks) */ ++#define HUB_CHAR_COMMON_OCPM 0x0000 /* All ports Over-Current reporting */ ++#define HUB_CHAR_INDV_PORT_LPSM 0x0001 /* per-port power control */ ++#define HUB_CHAR_NO_LPSM 0x0002 /* no power switching */ + #define HUB_CHAR_LPSM 0x0003 + #define HUB_CHAR_COMPOUND 0x0004 ++#define HUB_CHAR_INDV_PORT_OCPM 0x0008 /* per-port Over-current reporting */ ++#define HUB_CHAR_NO_OCPM 0x0010 /* No Over-current Protection support */ + #define HUB_CHAR_OCPM 0x0018 + #define HUB_CHAR_TTTT 0x0060 /* TT Think Time mask */ ++#define HUB_CHAR_PORTIND 0x0080 /* per-port indicators (LEDs) */ + + /* + * Hub Status & Hub Change bit masks +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0004-usb-add-isp1760-family-driver.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0004-usb-add-isp1760-family-driver.patch new file mode 100644 index 0000000000..5940f06d4d --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0004-usb-add-isp1760-family-driver.patch @@ -0,0 +1,3805 @@ +From 62a666a83766f5517e90464638455286bb33f433 Mon Sep 17 00:00:00 2001 +From: Rui Miguel Silva <rui.silva@linaro.org> +Date: Mon, 28 Jun 2021 23:31:25 +0100 +Subject: [PATCH 04/27] usb: add isp1760 family driver + +ISP1760/61/63 are a family of usb controllers, blah, blah, more info +here. + +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + Makefile | 1 + + drivers/usb/Kconfig | 2 + + drivers/usb/common/Makefile | 1 + + drivers/usb/isp1760/Kconfig | 12 + + drivers/usb/isp1760/Makefile | 6 + + drivers/usb/isp1760/isp1760-core.c | 378 ++++ + drivers/usb/isp1760/isp1760-core.h | 96 + + drivers/usb/isp1760/isp1760-hcd.c | 2574 +++++++++++++++++++++++++++ + drivers/usb/isp1760/isp1760-hcd.h | 82 + + drivers/usb/isp1760/isp1760-if.c | 127 ++ + drivers/usb/isp1760/isp1760-regs.h | 292 +++ + drivers/usb/isp1760/isp1760-uboot.c | 76 + + drivers/usb/isp1760/isp1760-uboot.h | 27 + + 13 files changed, 3674 insertions(+) + create mode 100644 drivers/usb/isp1760/Kconfig + create mode 100644 drivers/usb/isp1760/Makefile + create mode 100644 drivers/usb/isp1760/isp1760-core.c + create mode 100644 drivers/usb/isp1760/isp1760-core.h + create mode 100644 drivers/usb/isp1760/isp1760-hcd.c + create mode 100644 drivers/usb/isp1760/isp1760-hcd.h + create mode 100644 drivers/usb/isp1760/isp1760-if.c + create mode 100644 drivers/usb/isp1760/isp1760-regs.h + create mode 100644 drivers/usb/isp1760/isp1760-uboot.c + create mode 100644 drivers/usb/isp1760/isp1760-uboot.h + +diff --git a/Makefile b/Makefile +index ad83d60dc39d..2c857c1fe2cc 100644 +--- a/Makefile ++++ b/Makefile +@@ -834,6 +834,7 @@ libs-y += drivers/usb/host/ + libs-y += drivers/usb/mtu3/ + libs-y += drivers/usb/musb/ + libs-y += drivers/usb/musb-new/ ++libs-y += drivers/usb/isp1760/ + libs-y += drivers/usb/phy/ + libs-y += drivers/usb/ulpi/ + ifdef CONFIG_POST +diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig +index ab1d061bd0d5..bbe07be02cab 100644 +--- a/drivers/usb/Kconfig ++++ b/drivers/usb/Kconfig +@@ -78,6 +78,8 @@ source "drivers/usb/musb/Kconfig" + + source "drivers/usb/musb-new/Kconfig" + ++source "drivers/usb/isp1760/Kconfig" ++ + source "drivers/usb/emul/Kconfig" + + source "drivers/usb/phy/Kconfig" +diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile +index dc05cb0a5077..f08b064d2493 100644 +--- a/drivers/usb/common/Makefile ++++ b/drivers/usb/common/Makefile +@@ -4,6 +4,7 @@ + # + + obj-$(CONFIG_$(SPL_)DM_USB) += common.o ++obj-$(CONFIG_USB_ISP1760) += usb_urb.o + obj-$(CONFIG_USB_MUSB_HCD) += usb_urb.o + obj-$(CONFIG_USB_MUSB_UDC) += usb_urb.o + obj-$(CONFIG_USB_EHCI_FSL) += fsl-dt-fixup.o fsl-errata.o +diff --git a/drivers/usb/isp1760/Kconfig b/drivers/usb/isp1760/Kconfig +new file mode 100644 +index 000000000000..993d71e74cd2 +--- /dev/null ++++ b/drivers/usb/isp1760/Kconfig +@@ -0,0 +1,12 @@ ++# SPDX-License-Identifier: GPL-2.0 ++ ++config USB_ISP1760 ++ tristate "NXP ISP 1760/1761/1763 support" ++ select DM_USB ++ select USB_HOST ++ help ++ Say Y or M here if your system as an ISP1760/1761/1763 USB host ++ controller. ++ ++ This USB controller is usually attached to a non-DMA-Master ++ capable bus. +diff --git a/drivers/usb/isp1760/Makefile b/drivers/usb/isp1760/Makefile +new file mode 100644 +index 000000000000..2c809c01b118 +--- /dev/null ++++ b/drivers/usb/isp1760/Makefile +@@ -0,0 +1,6 @@ ++# SPDX-License-Identifier: GPL-2.0 ++isp1760-y := isp1760-core.o isp1760-if.o isp1760-uboot.o isp1760-hcd.o ++ ++#isp1760-hcd.o ++ ++obj-$(CONFIG_USB_ISP1760) += isp1760.o +diff --git a/drivers/usb/isp1760/isp1760-core.c b/drivers/usb/isp1760/isp1760-core.c +new file mode 100644 +index 000000000000..3080595549c5 +--- /dev/null ++++ b/drivers/usb/isp1760/isp1760-core.c +@@ -0,0 +1,378 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Driver for the NXP ISP1760 chip ++ * ++ * Copyright 2021 Linaro, Rui Miguel Silva <rui.silva@linaro.org> ++ * ++ * This is based on linux kernel driver, original developed: ++ * Copyright 2014 Laurent Pinchart ++ * Copyright 2007 Sebastian Siewior ++ * ++ */ ++ ++#include <dm.h> ++#include <dm/device-internal.h> ++#include <dm/device_compat.h> ++#include <dm/devres.h> ++#include <linux/compat.h> ++#include <linux/delay.h> ++#include <linux/io.h> ++#include <linux/kernel.h> ++#include <regmap.h> ++#include <usb.h> ++ ++#include "isp1760-core.h" ++#include "isp1760-hcd.h" ++#include "isp1760-regs.h" ++ ++#define msleep(a) udelay(a * 1000) ++ ++static int isp1760_init_core(struct isp1760_device *isp) ++{ ++ struct isp1760_hcd *hcd = &isp->hcd; ++ ++ /* ++ * Reset the host controller, including the CPU interface ++ * configuration. ++ */ ++ isp1760_field_set(hcd->fields, SW_RESET_RESET_ALL); ++ msleep(100); ++ ++ /* Setup HW Mode Control: This assumes a level active-low interrupt */ ++ if ((isp->devflags & ISP1760_FLAG_ANALOG_OC) && hcd->is_isp1763) ++ return -EINVAL; ++ ++ if (isp->devflags & ISP1760_FLAG_BUS_WIDTH_16) ++ isp1760_field_clear(hcd->fields, HW_DATA_BUS_WIDTH); ++ if (isp->devflags & ISP1760_FLAG_BUS_WIDTH_8) ++ isp1760_field_set(hcd->fields, HW_DATA_BUS_WIDTH); ++ if (isp->devflags & ISP1760_FLAG_ANALOG_OC) ++ isp1760_field_set(hcd->fields, HW_ANA_DIGI_OC); ++ if (isp->devflags & ISP1760_FLAG_DACK_POL_HIGH) ++ isp1760_field_set(hcd->fields, HW_DACK_POL_HIGH); ++ if (isp->devflags & ISP1760_FLAG_DREQ_POL_HIGH) ++ isp1760_field_set(hcd->fields, HW_DREQ_POL_HIGH); ++ if (isp->devflags & ISP1760_FLAG_INTR_POL_HIGH) ++ isp1760_field_set(hcd->fields, HW_INTR_HIGH_ACT); ++ if (isp->devflags & ISP1760_FLAG_INTR_EDGE_TRIG) ++ isp1760_field_set(hcd->fields, HW_INTR_EDGE_TRIG); ++ ++ /* ++ * The ISP1761 has a dedicated DC IRQ line but supports sharing the HC ++ * IRQ line for both the host and device controllers. Hardcode IRQ ++ * sharing for now and disable the DC interrupts globally to avoid ++ * spurious interrupts during HCD registration. ++ */ ++ if (isp->devflags & ISP1760_FLAG_ISP1761) { ++ isp1760_reg_write(hcd->regs, ISP176x_DC_MODE, 0); ++ isp1760_field_set(hcd->fields, HW_COMN_IRQ); ++ } ++ ++ /* ++ * PORT 1 Control register of the ISP1760 is the OTG control register ++ * on ISP1761. ++ * ++ * TODO: Really support OTG. For now we configure port 1 in device mode ++ */ ++ if (((isp->devflags & ISP1760_FLAG_ISP1761) || ++ (isp->devflags & ISP1760_FLAG_ISP1763)) && ++ (isp->devflags & ISP1760_FLAG_PERIPHERAL_EN)) { ++ isp1760_field_set(hcd->fields, HW_DM_PULLDOWN); ++ isp1760_field_set(hcd->fields, HW_DP_PULLDOWN); ++ isp1760_field_set(hcd->fields, HW_OTG_DISABLE); ++ } else { ++ isp1760_field_set(hcd->fields, HW_SW_SEL_HC_DC); ++ isp1760_field_set(hcd->fields, HW_VBUS_DRV); ++ isp1760_field_set(hcd->fields, HW_SEL_CP_EXT); ++ } ++ ++ printf( "%s bus width: %u, oc: %s\n", ++ hcd->is_isp1763 ? "isp1763" : "isp1760", ++ isp->devflags & ISP1760_FLAG_BUS_WIDTH_8 ? 8 : ++ isp->devflags & ISP1760_FLAG_BUS_WIDTH_16 ? 16 : 32, ++ hcd->is_isp1763 ? "not available" : ++ isp->devflags & ISP1760_FLAG_ANALOG_OC ? "analog" : "digital"); ++ ++ return 0; ++} ++ ++void isp1760_set_pullup(struct isp1760_device *isp, bool enable) ++{ ++ struct isp1760_hcd *hcd = &isp->hcd; ++ ++ if (enable) ++ isp1760_field_set(hcd->fields, HW_DP_PULLUP); ++ else ++ isp1760_field_set(hcd->fields, HW_DP_PULLUP_CLEAR); ++} ++ ++/* ++ * ISP1760/61: ++ * ++ * 60kb divided in: ++ * - 32 blocks @ 256 bytes ++ * - 20 blocks @ 1024 bytes ++ * - 4 blocks @ 8192 bytes ++ */ ++static const struct isp1760_memory_layout isp176x_memory_conf = { ++ .blocks[0] = 32, ++ .blocks_size[0] = 256, ++ .blocks[1] = 20, ++ .blocks_size[1] = 1024, ++ .blocks[2] = 4, ++ .blocks_size[2] = 8192, ++ ++ .slot_num = 32, ++ .payload_blocks = 32 + 20 + 4, ++ .payload_area_size = 0xf000, ++}; ++ ++/* ++ * ISP1763: ++ * ++ * 20kb divided in: ++ * - 8 blocks @ 256 bytes ++ * - 2 blocks @ 1024 bytes ++ * - 4 blocks @ 4096 bytes ++ */ ++static const struct isp1760_memory_layout isp1763_memory_conf = { ++ .blocks[0] = 8, ++ .blocks_size[0] = 256, ++ .blocks[1] = 2, ++ .blocks_size[1] = 1024, ++ .blocks[2] = 4, ++ .blocks_size[2] = 4096, ++ ++ .slot_num = 16, ++ .payload_blocks = 8 + 2 + 4, ++ .payload_area_size = 0x5000, ++}; ++ ++static const struct regmap_config isp1760_hc_regmap_conf = { ++ .width = REGMAP_SIZE_16, ++}; ++ ++static const struct reg_field isp1760_hc_reg_fields[] = { ++ [HCS_PPC] = REG_FIELD(ISP176x_HC_HCSPARAMS, 4, 4), ++ [HCS_N_PORTS] = REG_FIELD(ISP176x_HC_HCSPARAMS, 0, 3), ++ [HCC_ISOC_CACHE] = REG_FIELD(ISP176x_HC_HCCPARAMS, 7, 7), ++ [HCC_ISOC_THRES] = REG_FIELD(ISP176x_HC_HCCPARAMS, 4, 6), ++ [CMD_LRESET] = REG_FIELD(ISP176x_HC_USBCMD, 7, 7), ++ [CMD_RESET] = REG_FIELD(ISP176x_HC_USBCMD, 1, 1), ++ [CMD_RUN] = REG_FIELD(ISP176x_HC_USBCMD, 0, 0), ++ [STS_PCD] = REG_FIELD(ISP176x_HC_USBSTS, 2, 2), ++ [HC_FRINDEX] = REG_FIELD(ISP176x_HC_FRINDEX, 0, 13), ++ [FLAG_CF] = REG_FIELD(ISP176x_HC_CONFIGFLAG, 0, 0), ++ [HC_ISO_PTD_DONEMAP] = REG_FIELD(ISP176x_HC_ISO_PTD_DONEMAP, 0, 31), ++ [HC_ISO_PTD_SKIPMAP] = REG_FIELD(ISP176x_HC_ISO_PTD_SKIPMAP, 0, 31), ++ [HC_ISO_PTD_LASTPTD] = REG_FIELD(ISP176x_HC_ISO_PTD_LASTPTD, 0, 31), ++ [HC_INT_PTD_DONEMAP] = REG_FIELD(ISP176x_HC_INT_PTD_DONEMAP, 0, 31), ++ [HC_INT_PTD_SKIPMAP] = REG_FIELD(ISP176x_HC_INT_PTD_SKIPMAP, 0, 31), ++ [HC_INT_PTD_LASTPTD] = REG_FIELD(ISP176x_HC_INT_PTD_LASTPTD, 0, 31), ++ [HC_ATL_PTD_DONEMAP] = REG_FIELD(ISP176x_HC_ATL_PTD_DONEMAP, 0, 31), ++ [HC_ATL_PTD_SKIPMAP] = REG_FIELD(ISP176x_HC_ATL_PTD_SKIPMAP, 0, 31), ++ [HC_ATL_PTD_LASTPTD] = REG_FIELD(ISP176x_HC_ATL_PTD_LASTPTD, 0, 31), ++ [PORT_OWNER] = REG_FIELD(ISP176x_HC_PORTSC1, 13, 13), ++ [PORT_POWER] = REG_FIELD(ISP176x_HC_PORTSC1, 12, 12), ++ [PORT_LSTATUS] = REG_FIELD(ISP176x_HC_PORTSC1, 10, 11), ++ [PORT_RESET] = REG_FIELD(ISP176x_HC_PORTSC1, 8, 8), ++ [PORT_SUSPEND] = REG_FIELD(ISP176x_HC_PORTSC1, 7, 7), ++ [PORT_RESUME] = REG_FIELD(ISP176x_HC_PORTSC1, 6, 6), ++ [PORT_PE] = REG_FIELD(ISP176x_HC_PORTSC1, 2, 2), ++ [PORT_CSC] = REG_FIELD(ISP176x_HC_PORTSC1, 1, 1), ++ [PORT_CONNECT] = REG_FIELD(ISP176x_HC_PORTSC1, 0, 0), ++ [ALL_ATX_RESET] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 31, 31), ++ [HW_ANA_DIGI_OC] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 15, 15), ++ [HW_COMN_IRQ] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 10, 10), ++ [HW_DATA_BUS_WIDTH] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 8, 8), ++ [HW_DACK_POL_HIGH] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 6, 6), ++ [HW_DREQ_POL_HIGH] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 5, 5), ++ [HW_INTR_HIGH_ACT] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 2, 2), ++ [HW_INTR_EDGE_TRIG] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 1, 1), ++ [HW_GLOBAL_INTR_EN] = REG_FIELD(ISP176x_HC_HW_MODE_CTRL, 0, 0), ++ [HC_CHIP_REV] = REG_FIELD(ISP176x_HC_CHIP_ID, 16, 31), ++ [HC_CHIP_ID_HIGH] = REG_FIELD(ISP176x_HC_CHIP_ID, 8, 15), ++ [HC_CHIP_ID_LOW] = REG_FIELD(ISP176x_HC_CHIP_ID, 0, 7), ++ [HC_SCRATCH] = REG_FIELD(ISP176x_HC_SCRATCH, 0, 31), ++ [SW_RESET_RESET_ALL] = REG_FIELD(ISP176x_HC_RESET, 0, 0), ++ [ISO_BUF_FILL] = REG_FIELD(ISP176x_HC_BUFFER_STATUS, 2, 2), ++ [INT_BUF_FILL] = REG_FIELD(ISP176x_HC_BUFFER_STATUS, 1, 1), ++ [ATL_BUF_FILL] = REG_FIELD(ISP176x_HC_BUFFER_STATUS, 0, 0), ++ [MEM_BANK_SEL] = REG_FIELD(ISP176x_HC_MEMORY, 16, 17), ++ [MEM_START_ADDR] = REG_FIELD(ISP176x_HC_MEMORY, 0, 15), ++ [HC_INTERRUPT] = REG_FIELD(ISP176x_HC_INTERRUPT, 0, 9), ++ [HC_ATL_IRQ_ENABLE] = REG_FIELD(ISP176x_HC_INTERRUPT_ENABLE, 8, 8), ++ [HC_INT_IRQ_ENABLE] = REG_FIELD(ISP176x_HC_INTERRUPT_ENABLE, 7, 7), ++ [HC_ISO_IRQ_MASK_OR] = REG_FIELD(ISP176x_HC_ISO_IRQ_MASK_OR, 0, 31), ++ [HC_INT_IRQ_MASK_OR] = REG_FIELD(ISP176x_HC_INT_IRQ_MASK_OR, 0, 31), ++ [HC_ATL_IRQ_MASK_OR] = REG_FIELD(ISP176x_HC_ATL_IRQ_MASK_OR, 0, 31), ++ [HC_ISO_IRQ_MASK_AND] = REG_FIELD(ISP176x_HC_ISO_IRQ_MASK_AND, 0, 31), ++ [HC_INT_IRQ_MASK_AND] = REG_FIELD(ISP176x_HC_INT_IRQ_MASK_AND, 0, 31), ++ [HC_ATL_IRQ_MASK_AND] = REG_FIELD(ISP176x_HC_ATL_IRQ_MASK_AND, 0, 31), ++ [HW_OTG_DISABLE] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 10, 10), ++ [HW_SW_SEL_HC_DC] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 7, 7), ++ [HW_VBUS_DRV] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 4, 4), ++ [HW_SEL_CP_EXT] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 3, 3), ++ [HW_DM_PULLDOWN] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 2, 2), ++ [HW_DP_PULLDOWN] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 1, 1), ++ [HW_DP_PULLUP] = REG_FIELD(ISP176x_HC_OTG_CTRL_SET, 0, 0), ++ [HW_OTG_DISABLE_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 10, 10), ++ [HW_SW_SEL_HC_DC_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 7, 7), ++ [HW_VBUS_DRV_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 4, 4), ++ [HW_SEL_CP_EXT_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 3, 3), ++ [HW_DM_PULLDOWN_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 2, 2), ++ [HW_DP_PULLDOWN_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 1, 1), ++ [HW_DP_PULLUP_CLEAR] = REG_FIELD(ISP176x_HC_OTG_CTRL_CLEAR, 0, 0), ++}; ++ ++static const struct regmap_config isp1763_hc_regmap_conf = { ++ .width = REGMAP_SIZE_16, ++}; ++ ++static const struct reg_field isp1763_hc_reg_fields[] = { ++ [CMD_LRESET] = REG_FIELD(ISP1763_HC_USBCMD, 7, 7), ++ [CMD_RESET] = REG_FIELD(ISP1763_HC_USBCMD, 1, 1), ++ [CMD_RUN] = REG_FIELD(ISP1763_HC_USBCMD, 0, 0), ++ [STS_PCD] = REG_FIELD(ISP1763_HC_USBSTS, 2, 2), ++ [HC_FRINDEX] = REG_FIELD(ISP1763_HC_FRINDEX, 0, 13), ++ [FLAG_CF] = REG_FIELD(ISP1763_HC_CONFIGFLAG, 0, 0), ++ [HC_ISO_PTD_DONEMAP] = REG_FIELD(ISP1763_HC_ISO_PTD_DONEMAP, 0, 15), ++ [HC_ISO_PTD_SKIPMAP] = REG_FIELD(ISP1763_HC_ISO_PTD_SKIPMAP, 0, 15), ++ [HC_ISO_PTD_LASTPTD] = REG_FIELD(ISP1763_HC_ISO_PTD_LASTPTD, 0, 15), ++ [HC_INT_PTD_DONEMAP] = REG_FIELD(ISP1763_HC_INT_PTD_DONEMAP, 0, 15), ++ [HC_INT_PTD_SKIPMAP] = REG_FIELD(ISP1763_HC_INT_PTD_SKIPMAP, 0, 15), ++ [HC_INT_PTD_LASTPTD] = REG_FIELD(ISP1763_HC_INT_PTD_LASTPTD, 0, 15), ++ [HC_ATL_PTD_DONEMAP] = REG_FIELD(ISP1763_HC_ATL_PTD_DONEMAP, 0, 15), ++ [HC_ATL_PTD_SKIPMAP] = REG_FIELD(ISP1763_HC_ATL_PTD_SKIPMAP, 0, 15), ++ [HC_ATL_PTD_LASTPTD] = REG_FIELD(ISP1763_HC_ATL_PTD_LASTPTD, 0, 15), ++ [PORT_OWNER] = REG_FIELD(ISP1763_HC_PORTSC1, 13, 13), ++ [PORT_POWER] = REG_FIELD(ISP1763_HC_PORTSC1, 12, 12), ++ [PORT_LSTATUS] = REG_FIELD(ISP1763_HC_PORTSC1, 10, 11), ++ [PORT_RESET] = REG_FIELD(ISP1763_HC_PORTSC1, 8, 8), ++ [PORT_SUSPEND] = REG_FIELD(ISP1763_HC_PORTSC1, 7, 7), ++ [PORT_RESUME] = REG_FIELD(ISP1763_HC_PORTSC1, 6, 6), ++ [PORT_PE] = REG_FIELD(ISP1763_HC_PORTSC1, 2, 2), ++ [PORT_CSC] = REG_FIELD(ISP1763_HC_PORTSC1, 1, 1), ++ [PORT_CONNECT] = REG_FIELD(ISP1763_HC_PORTSC1, 0, 0), ++ [HW_DATA_BUS_WIDTH] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 4, 4), ++ [HW_DACK_POL_HIGH] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 6, 6), ++ [HW_DREQ_POL_HIGH] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 5, 5), ++ [HW_INTF_LOCK] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 3, 3), ++ [HW_INTR_HIGH_ACT] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 2, 2), ++ [HW_INTR_EDGE_TRIG] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 1, 1), ++ [HW_GLOBAL_INTR_EN] = REG_FIELD(ISP1763_HC_HW_MODE_CTRL, 0, 0), ++ [SW_RESET_RESET_ATX] = REG_FIELD(ISP1763_HC_RESET, 3, 3), ++ [SW_RESET_RESET_ALL] = REG_FIELD(ISP1763_HC_RESET, 0, 0), ++ [HC_CHIP_ID_HIGH] = REG_FIELD(ISP1763_HC_CHIP_ID, 0, 15), ++ [HC_CHIP_ID_LOW] = REG_FIELD(ISP1763_HC_CHIP_REV, 8, 15), ++ [HC_CHIP_REV] = REG_FIELD(ISP1763_HC_CHIP_REV, 0, 7), ++ [HC_SCRATCH] = REG_FIELD(ISP1763_HC_SCRATCH, 0, 15), ++ [ISO_BUF_FILL] = REG_FIELD(ISP1763_HC_BUFFER_STATUS, 2, 2), ++ [INT_BUF_FILL] = REG_FIELD(ISP1763_HC_BUFFER_STATUS, 1, 1), ++ [ATL_BUF_FILL] = REG_FIELD(ISP1763_HC_BUFFER_STATUS, 0, 0), ++ [MEM_START_ADDR] = REG_FIELD(ISP1763_HC_MEMORY, 0, 15), ++ [HC_DATA] = REG_FIELD(ISP1763_HC_DATA, 0, 15), ++ [HC_INTERRUPT] = REG_FIELD(ISP1763_HC_INTERRUPT, 0, 10), ++ [HC_ATL_IRQ_ENABLE] = REG_FIELD(ISP1763_HC_INTERRUPT_ENABLE, 8, 8), ++ [HC_INT_IRQ_ENABLE] = REG_FIELD(ISP1763_HC_INTERRUPT_ENABLE, 7, 7), ++ [HC_ISO_IRQ_MASK_OR] = REG_FIELD(ISP1763_HC_ISO_IRQ_MASK_OR, 0, 15), ++ [HC_INT_IRQ_MASK_OR] = REG_FIELD(ISP1763_HC_INT_IRQ_MASK_OR, 0, 15), ++ [HC_ATL_IRQ_MASK_OR] = REG_FIELD(ISP1763_HC_ATL_IRQ_MASK_OR, 0, 15), ++ [HC_ISO_IRQ_MASK_AND] = REG_FIELD(ISP1763_HC_ISO_IRQ_MASK_AND, 0, 15), ++ [HC_INT_IRQ_MASK_AND] = REG_FIELD(ISP1763_HC_INT_IRQ_MASK_AND, 0, 15), ++ [HC_ATL_IRQ_MASK_AND] = REG_FIELD(ISP1763_HC_ATL_IRQ_MASK_AND, 0, 15), ++ [HW_HC_2_DIS] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 15, 15), ++ [HW_OTG_DISABLE] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 10, 10), ++ [HW_SW_SEL_HC_DC] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 7, 7), ++ [HW_VBUS_DRV] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 4, 4), ++ [HW_SEL_CP_EXT] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 3, 3), ++ [HW_DM_PULLDOWN] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 2, 2), ++ [HW_DP_PULLDOWN] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 1, 1), ++ [HW_DP_PULLUP] = REG_FIELD(ISP1763_HC_OTG_CTRL_SET, 0, 0), ++ [HW_HC_2_DIS_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 15, 15), ++ [HW_OTG_DISABLE_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 10, 10), ++ [HW_SW_SEL_HC_DC_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 7, 7), ++ [HW_VBUS_DRV_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 4, 4), ++ [HW_SEL_CP_EXT_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 3, 3), ++ [HW_DM_PULLDOWN_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 2, 2), ++ [HW_DP_PULLDOWN_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 1, 1), ++ [HW_DP_PULLUP_CLEAR] = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 0, 0), ++}; ++ ++int isp1760_register(struct isp1760_device *isp, struct resource *mem, int irq, ++ unsigned long irqflags) ++{ ++ const struct regmap_config *hc_regmap; ++ const struct reg_field *hc_reg_fields; ++ struct isp1760_hcd *hcd; ++ struct regmap_field *f; ++ unsigned int devflags; ++ struct udevice *dev; ++ int ret; ++ int i; ++ ++ hcd = &isp->hcd; ++ devflags = isp->devflags; ++ dev = isp->dev; ++ ++ hcd->is_isp1763 = !!(devflags & ISP1760_FLAG_ISP1763); ++ ++ if (!hcd->is_isp1763 && (devflags & ISP1760_FLAG_BUS_WIDTH_8)) { ++ dev_err(dev, "isp1760/61 do not support data width 8\n"); ++ return -EINVAL; ++ } ++ ++ if (hcd->is_isp1763) { ++ hc_regmap = &isp1763_hc_regmap_conf; ++ hc_reg_fields = &isp1763_hc_reg_fields[0]; ++ } else { ++ hc_regmap = &isp1760_hc_regmap_conf; ++ hc_reg_fields = &isp1760_hc_reg_fields[0]; ++ } ++ ++ hcd->base = devm_ioremap(dev, mem->start, resource_size(mem)); ++ if (IS_ERR(hcd->base)) ++ return PTR_ERR(hcd->base); ++ ++ hcd->regs = devm_regmap_init(dev, NULL, NULL, hc_regmap); ++ if (IS_ERR(hcd->regs)) ++ return PTR_ERR(hcd->regs); ++ ++ for (i = 0; i < HC_FIELD_MAX; i++) { ++ f = devm_regmap_field_alloc(dev, hcd->regs, hc_reg_fields[i]); ++ if (IS_ERR(f)) ++ return PTR_ERR(f); ++ ++ hcd->fields[i] = f; ++ } ++ ++ if (hcd->is_isp1763) ++ hcd->memory_layout = &isp1763_memory_conf; ++ else ++ hcd->memory_layout = &isp176x_memory_conf; ++ ++ ret = isp1760_init_core(isp); ++ if (ret < 0) ++ return ret; ++ ++ hcd->dev = dev; ++ ++ ret = isp1760_hcd_register(hcd, mem, irq, irqflags, dev); ++ if (ret < 0) ++ return ret; ++ ++ ret = isp1760_hcd_lowlevel_init(hcd); ++ if (ret < 0) ++ return ret; ++ ++ dev_set_drvdata(dev, isp); ++ ++ return 0; ++} ++ ++void isp1760_unregister(struct isp1760_device *isp) ++{ ++ isp1760_hcd_unregister(&isp->hcd); ++ ++ return; ++} +diff --git a/drivers/usb/isp1760/isp1760-core.h b/drivers/usb/isp1760/isp1760-core.h +new file mode 100644 +index 000000000000..0a60e30b5fe7 +--- /dev/null ++++ b/drivers/usb/isp1760/isp1760-core.h +@@ -0,0 +1,96 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Driver for the NXP ISP1760 chip ++ * ++ * Copyright 2021 Linaro, Rui Miguel Silva ++ * Copyright 2014 Laurent Pinchart ++ * Copyright 2007 Sebastian Siewior ++ * ++ * Contacts: ++ * Sebastian Siewior <bigeasy@linutronix.de> ++ * Laurent Pinchart <laurent.pinchart@ideasonboard.com> ++ * Rui Miguel Silva <rui.silva@linaro.org> ++ */ ++ ++#ifndef _ISP1760_CORE_H_ ++#define _ISP1760_CORE_H_ ++ ++#include <linux/compat.h> ++#include <linux/ioport.h> ++#include <regmap.h> ++ ++#include "isp1760-hcd.h" ++ ++struct device; ++struct gpio_desc; ++ ++/* ++ * Device flags that can vary from board to board. All of these ++ * indicate the most "atypical" case, so that a devflags of 0 is ++ * a sane default configuration. ++ */ ++#define ISP1760_FLAG_BUS_WIDTH_16 0x00000002 /* 16-bit data bus width */ ++#define ISP1760_FLAG_PERIPHERAL_EN 0x00000004 /* Port 1 supports Peripheral mode*/ ++#define ISP1760_FLAG_ANALOG_OC 0x00000008 /* Analog overcurrent */ ++#define ISP1760_FLAG_DACK_POL_HIGH 0x00000010 /* DACK active high */ ++#define ISP1760_FLAG_DREQ_POL_HIGH 0x00000020 /* DREQ active high */ ++#define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */ ++#define ISP1760_FLAG_INTR_POL_HIGH 0x00000080 /* Interrupt polarity active high */ ++#define ISP1760_FLAG_INTR_EDGE_TRIG 0x00000100 /* Interrupt edge triggered */ ++#define ISP1760_FLAG_ISP1763 0x00000200 /* Chip is ISP1763 */ ++#define ISP1760_FLAG_BUS_WIDTH_8 0x00000400 /* 8-bit data bus width */ ++ ++struct isp1760_device { ++ struct udevice *dev; ++ ++ unsigned int devflags; ++ struct gpio_desc *rst_gpio; ++ ++ struct isp1760_hcd hcd; ++}; ++ ++int isp1760_register(struct isp1760_device *isp, struct resource *mem, int irq, ++ unsigned long irqflags); ++void isp1760_unregister(struct isp1760_device *isp); ++ ++void isp1760_set_pullup(struct isp1760_device *isp, bool enable); ++ ++static inline u32 isp1760_field_read(struct regmap_field **fields, u32 field) ++{ ++ unsigned int val; ++ ++ regmap_field_read(fields[field], &val); ++ ++ return val; ++} ++ ++static inline void isp1760_field_write(struct regmap_field **fields, u32 field, ++ u32 val) ++{ ++ regmap_field_write(fields[field], val); ++} ++ ++static inline void isp1760_field_set(struct regmap_field **fields, u32 field) ++{ ++ isp1760_field_write(fields, field, 0xFFFFFFFF); ++} ++ ++static inline void isp1760_field_clear(struct regmap_field **fields, u32 field) ++{ ++ isp1760_field_write(fields, field, 0); ++} ++ ++static inline u32 isp1760_reg_read(struct regmap *regs, u32 reg) ++{ ++ unsigned int val; ++ ++ regmap_read(regs, reg, &val); ++ ++ return val; ++} ++ ++static inline void isp1760_reg_write(struct regmap *regs, u32 reg, u32 val) ++{ ++ regmap_write(regs, reg, val); ++} ++#endif +diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c +new file mode 100644 +index 000000000000..b1d86dd69b94 +--- /dev/null ++++ b/drivers/usb/isp1760/isp1760-hcd.c +@@ -0,0 +1,2574 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Driver for the NXP ISP1760 chip ++ * ++ * Copyright 2021 Linaro, Rui Miguel Silva <rui.silva@linaro.org> ++ * ++ */ ++ ++#include <hexdump.h> ++#include <common.h> ++#include <asm/cache.h> ++#include <cpu_func.h> ++#include <dm.h> ++#include <dm/device-internal.h> ++#include <dm/device_compat.h> ++#include <linux/bug.h> ++#include <linux/kernel.h> ++#include <linux/list.h> ++#include <linux/usb/usb_urb_compat.h> ++#include <usb.h> ++#include <linux/io.h> ++#include <linux/iopoll.h> ++#include <asm/unaligned.h> ++ ++#include "isp1760-core.h" ++#include "isp1760-hcd.h" ++#include "isp1760-regs.h" ++#include "isp1760-uboot.h" ++ ++static struct kmem_cache *qtd_cachep; ++static struct kmem_cache *qh_cachep; ++static struct kmem_cache *urb_listitem_cachep; ++ ++typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh, ++ struct isp1760_qtd *qtd); ++ ++static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd) ++{ ++ return hcd->hcd_priv; ++} ++ ++#define dw_to_le32(x) (cpu_to_le32((__force u32)x)) ++#define le32_to_dw(x) ((__force __dw)(le32_to_cpu(x))) ++ ++/* urb state*/ ++#define DELETE_URB (0x0008) ++#define NO_TRANSFER_ACTIVE (0xffffffff) ++ ++/* Philips Proprietary Transfer Descriptor (PTD) */ ++typedef __u32 __bitwise __dw; ++struct ptd { ++ __dw dw0; ++ __dw dw1; ++ __dw dw2; ++ __dw dw3; ++ __dw dw4; ++ __dw dw5; ++ __dw dw6; ++ __dw dw7; ++}; ++ ++struct ptd_le32 { ++ __le32 dw0; ++ __le32 dw1; ++ __le32 dw2; ++ __le32 dw3; ++ __le32 dw4; ++ __le32 dw5; ++ __le32 dw6; ++ __le32 dw7; ++}; ++ ++#define PTD_OFFSET 0x0400 ++#define ISO_PTD_OFFSET 0x0400 ++#define INT_PTD_OFFSET 0x0800 ++#define ATL_PTD_OFFSET 0x0c00 ++#define PAYLOAD_OFFSET 0x1000 ++ ++#define ISP_BANK_0 0x00 ++#define ISP_BANK_1 0x01 ++#define ISP_BANK_2 0x02 ++#define ISP_BANK_3 0x03 ++ ++#define TO_DW(x) ((__force __dw)x) ++#define TO_U32(x) ((__force u32)x) ++ ++ /* ATL */ ++ /* DW0 */ ++#define DW0_VALID_BIT TO_DW(1) ++#define FROM_DW0_VALID(x) (TO_U32(x) & 0x01) ++#define TO_DW0_LENGTH(x) TO_DW((((u32)x) << 3)) ++#define TO_DW0_MAXPACKET(x) TO_DW((((u32)x) << 18)) ++#define TO_DW0_MULTI(x) TO_DW((((u32)x) << 29)) ++#define TO_DW0_ENDPOINT(x) TO_DW((((u32)x) << 31)) ++/* DW1 */ ++#define TO_DW1_DEVICE_ADDR(x) TO_DW((((u32)x) << 3)) ++#define TO_DW1_PID_TOKEN(x) TO_DW((((u32)x) << 10)) ++#define DW1_TRANS_BULK TO_DW(((u32)2 << 12)) ++#define DW1_TRANS_INT TO_DW(((u32)3 << 12)) ++#define DW1_TRANS_SPLIT TO_DW(((u32)1 << 14)) ++#define DW1_SE_USB_LOSPEED TO_DW(((u32)2 << 16)) ++#define TO_DW1_PORT_NUM(x) TO_DW((((u32)x) << 18)) ++#define TO_DW1_HUB_NUM(x) TO_DW((((u32)x) << 25)) ++/* DW2 */ ++#define TO_DW2_DATA_START_ADDR(x) TO_DW((((u32)x) << 8)) ++#define TO_DW2_RL(x) TO_DW(((x) << 25)) ++#define FROM_DW2_RL(x) ((TO_U32(x) >> 25) & 0xf) ++/* DW3 */ ++#define FROM_DW3_NRBYTESTRANSFERRED(x) TO_U32((x) & 0x3fff) ++#define FROM_DW3_SCS_NRBYTESTRANSFERRED(x) TO_U32((x) & 0x07ff) ++#define TO_DW3_NAKCOUNT(x) TO_DW(((x) << 19)) ++#define FROM_DW3_NAKCOUNT(x) ((TO_U32(x) >> 19) & 0xf) ++#define TO_DW3_CERR(x) TO_DW(((x) << 23)) ++#define FROM_DW3_CERR(x) ((TO_U32(x) >> 23) & 0x3) ++#define TO_DW3_DATA_TOGGLE(x) TO_DW(((x) << 25)) ++#define FROM_DW3_DATA_TOGGLE(x) ((TO_U32(x) >> 25) & 0x1) ++#define TO_DW3_PING(x) TO_DW(((x) << 26)) ++#define FROM_DW3_PING(x) ((TO_U32(x) >> 26) & 0x1) ++#define DW3_ERROR_BIT TO_DW((1 << 28)) ++#define DW3_BABBLE_BIT TO_DW((1 << 29)) ++#define DW3_HALT_BIT TO_DW((1 << 30)) ++#define DW3_ACTIVE_BIT TO_DW((1 << 31)) ++#define FROM_DW3_ACTIVE(x) ((TO_U32(x) >> 31) & 0x01) ++ ++#define INT_UNDERRUN (1 << 2) ++#define INT_BABBLE (1 << 1) ++#define INT_EXACT (1 << 0) ++ ++#define SETUP_PID (2) ++#define IN_PID (1) ++#define OUT_PID (0) ++ ++/* Errata 1 */ ++#define RL_COUNTER (0) ++#define NAK_COUNTER (0) ++#define ERR_COUNTER (3) ++ ++struct isp1760_qtd { ++ u8 packet_type; ++ void *data_buffer; ++ u32 payload_addr; ++ ++ /* the rest is HCD-private */ ++ struct list_head qtd_list; ++ struct urb *urb; ++ size_t length; ++ size_t actual_length; ++ ++ /* QTD_ENQUEUED: waiting for transfer (inactive) */ ++ /* QTD_PAYLOAD_ALLOC: chip mem has been allocated for payload */ ++ /* QTD_XFER_STARTED: valid ptd has been written to isp176x - only ++ interrupt handler may touch this qtd! */ ++ /* QTD_XFER_COMPLETE: payload has been transferred successfully */ ++ /* QTD_RETIRE: transfer error/abort qtd */ ++#define QTD_ENQUEUED 0 ++#define QTD_PAYLOAD_ALLOC 1 ++#define QTD_XFER_STARTED 2 ++#define QTD_XFER_COMPLETE 3 ++#define QTD_RETIRE 4 ++ u32 status; ++}; ++ ++/* Queue head, one for each active endpoint */ ++struct isp1760_qh { ++ struct list_head qh_list; ++ struct list_head qtd_list; ++ int epnum; ++ u32 toggle; ++ u32 ping; ++ int slot; ++ int tt_buffer_dirty; /* See USB2.0 spec section 11.17.5 */ ++}; ++ ++struct urb_listitem { ++ struct list_head urb_list; ++ struct urb *urb; ++}; ++ ++static const u32 isp1763_hc_portsc1_fields[] = { ++ [PORT_OWNER] = BIT(13), ++ [PORT_POWER] = BIT(12), ++ [PORT_LSTATUS] = BIT(10), ++ [PORT_RESET] = BIT(8), ++ [PORT_SUSPEND] = BIT(7), ++ [PORT_RESUME] = BIT(6), ++ [PORT_PE] = BIT(2), ++ [PORT_CSC] = BIT(1), ++ [PORT_CONNECT] = BIT(0), ++}; ++ ++static struct descriptor { ++ struct usb_device_descriptor device; ++ struct usb_config_descriptor config; ++ struct usb_interface_descriptor interface; ++ struct usb_endpoint_descriptor endpoint; ++} __packed rh_descriptor = { ++ { ++ /* usb 2.0 root hub device descriptor */ ++ 0x12, /* __u8 bLength; */ ++ USB_DT_DEVICE, /* __u8 bDescriptorType; Device */ ++ 0x0002, /* __le16 bcdUSB; v2.0 */ ++ ++ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ ++ 0x00, /* __u8 bDeviceSubClass; */ ++ 0x00, /* __u8 bDeviceProtocol; [ usb 2.0 no TT ] */ ++ 0x40, /* __u8 bMaxPacketSize0; 64 Bytes */ ++ ++ 0x6b1d, /* __le16 idVendor; Linux Foundation 0x1d6b */ ++ 0x0200, /* __le16 idProduct; device 0x0002 */ ++ 0x0001, /* __le16 bcdDevice */ ++ ++ 0x03, /* __u8 iManufacturer; */ ++ 0x02, /* __u8 iProduct; */ ++ 0x01, /* __u8 iSerialNumber; */ ++ 0x01 /* __u8 bNumConfigurations; */ ++ }, { ++ /* one configuration */ ++ 0x09, /* __u8 bLength; */ ++ USB_DT_CONFIG, /* __u8 bDescriptorType; Configuration */ ++ 0x1900, /* __le16 wTotalLength; */ ++ 0x01, /* __u8 bNumInterfaces; (1) */ ++ 0x01, /* __u8 bConfigurationValue; */ ++ 0x00, /* __u8 iConfiguration; */ ++ 0xc0, /* __u8 bmAttributes; ++ Bit 7: must be set, ++ 6: Self-powered, ++ 5: Remote wakeup, ++ 4..0: resvd */ ++ 0x00, /* __u8 MaxPower; */ ++ }, { ++ /* one interface */ ++ 0x09, /* __u8 if_bLength; */ ++ USB_DT_INTERFACE, /* __u8 if_bDescriptorType; Interface */ ++ 0x00, /* __u8 if_bInterfaceNumber; */ ++ 0x00, /* __u8 if_bAlternateSetting; */ ++ 0x01, /* __u8 if_bNumEndpoints; */ ++ 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ ++ 0x00, /* __u8 if_bInterfaceSubClass; */ ++ 0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ ++ 0x00, /* __u8 if_iInterface; */ ++ }, { ++ /* one endpoint (status change endpoint) */ ++ 0x07, /* __u8 ep_bLength; */ ++ USB_DT_ENDPOINT, /* __u8 ep_bDescriptorType; Endpoint */ ++ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ ++ 0x03, /* __u8 ep_bmAttributes; Interrupt */ ++ /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) ++ * see hub.c:hub_configure() for details. */ ++ (USB_MAXCHILDREN + 1 + 7) / 8, 0x00, ++ 0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */ ++ }, ++}; ++ ++/* ++ * Access functions for isp176x registers regmap fields ++ */ ++static u32 isp1760_hcd_read(struct usb_hcd *hcd, u32 field) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ ++ return isp1760_field_read(priv->fields, field); ++} ++ ++/* ++ * We need, in isp1763, to write directly the values to the portsc1 ++ * register so it will make the other values to trigger. ++ */ ++static void isp1760_hcd_portsc1_set_clear(struct isp1760_hcd *priv, u32 field, ++ u32 val) ++{ ++ u32 bit = isp1763_hc_portsc1_fields[field]; ++ u32 port_status = readl(priv->base + ISP1763_HC_PORTSC1); ++ ++ if (val) ++ writel(port_status | bit, priv->base + ISP1763_HC_PORTSC1); ++ else ++ writel(port_status & ~bit, priv->base + ISP1763_HC_PORTSC1); ++} ++ ++static void isp1760_hcd_write(struct usb_hcd *hcd, u32 field, u32 val) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ ++ if (unlikely(priv->is_isp1763 && ++ (field >= PORT_OWNER && field <= PORT_CONNECT))) ++ return isp1760_hcd_portsc1_set_clear(priv, field, val); ++ ++ isp1760_field_write(priv->fields, field, val); ++} ++ ++static void isp1760_hcd_set(struct usb_hcd *hcd, u32 field) ++{ ++ isp1760_hcd_write(hcd, field, 0xFFFFFFFF); ++} ++ ++static void isp1760_hcd_clear(struct usb_hcd *hcd, u32 field) ++{ ++ isp1760_hcd_write(hcd, field, 0); ++} ++ ++static int isp1760_hcd_set_and_wait(struct usb_hcd *hcd, u32 field, ++ u32 timeout_us) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ u32 val; ++ ++ isp1760_hcd_set(hcd, field); ++ ++ return regmap_field_read_poll_timeout(priv->fields[field], val, ++ val, 10, timeout_us); ++} ++ ++static int isp1760_hcd_set_and_wait_swap(struct usb_hcd *hcd, u32 field, ++ u32 timeout_us) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ u32 val; ++ ++ isp1760_hcd_set(hcd, field); ++ ++ return regmap_field_read_poll_timeout(priv->fields[field], val, ++ !val, 10, timeout_us); ++} ++ ++static int isp1760_hcd_clear_and_wait(struct usb_hcd *hcd, u32 field, ++ u32 timeout_us) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ u32 val; ++ ++ isp1760_hcd_clear(hcd, field); ++ ++ return regmap_field_read_poll_timeout(priv->fields[field], val, ++ !val, 10, timeout_us); ++} ++ ++static bool isp1760_hcd_is_set(struct usb_hcd *hcd, u32 field) ++{ ++ return !!isp1760_hcd_read(hcd, field); ++} ++ ++static bool isp1760_hcd_ppc_is_set(struct usb_hcd *hcd) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ ++ if (priv->is_isp1763) ++ return true; ++ ++ return isp1760_hcd_is_set(hcd, HCS_PPC); ++} ++ ++static u32 isp1760_hcd_n_ports(struct usb_hcd *hcd) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ ++ if (priv->is_isp1763) ++ return 1; ++ ++ return isp1760_hcd_read(hcd, HCS_N_PORTS); ++} ++ ++/* ++ * Access functions for isp176x memory (offset >= 0x0400). ++ * ++ * bank_reads8() reads memory locations prefetched by an earlier write to ++ * HC_MEMORY_REG (see isp176x datasheet). Unless you want to do fancy multi- ++ * bank optimizations, you should use the more generic mem_read() below. ++ * ++ * For access to ptd memory, use the specialized ptd_read() and ptd_write() ++ * below. ++ * ++ * These functions copy via MMIO data to/from the device. memcpy_{to|from}io() ++ * doesn't quite work because some people have to enforce 32-bit access ++ */ ++static void bank_reads8(void __iomem *src_base, u32 src_offset, u32 bank_addr, ++ __u32 *dst, u32 bytes) ++{ ++ __u32 __iomem *src; ++ u32 val; ++ __u8 *src_byteptr; ++ __u8 *dst_byteptr; ++ ++ src = src_base + (bank_addr | src_offset); ++ ++ if (src_offset < PAYLOAD_OFFSET) { ++ while (bytes >= 4) { ++ *dst = readl_relaxed(src); ++ bytes -= 4; ++ src++; ++ dst++; ++ } ++ } else { ++ while (bytes >= 4) { ++ *dst = __raw_readl(src); ++ bytes -= 4; ++ src++; ++ dst++; ++ } ++ } ++ ++ if (!bytes) ++ return; ++ ++ /* in case we have 3, 2 or 1 by left. The dst buffer may not be fully ++ * allocated. ++ */ ++ if (src_offset < PAYLOAD_OFFSET) ++ val = readl_relaxed(src); ++ else ++ val = __raw_readl(src); ++ ++ dst_byteptr = (void *) dst; ++ src_byteptr = (void *) &val; ++ while (bytes > 0) { ++ *dst_byteptr = *src_byteptr; ++ dst_byteptr++; ++ src_byteptr++; ++ bytes--; ++ } ++} ++ ++static void isp1760_mem_read(struct usb_hcd *hcd, u32 src_offset, void *dst, ++ u32 bytes) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ ++ isp1760_hcd_write(hcd, MEM_BANK_SEL, ISP_BANK_0); ++ isp1760_hcd_write(hcd, MEM_START_ADDR, src_offset); ++ ndelay(100); ++ ++ bank_reads8(priv->base, src_offset, ISP_BANK_0, dst, bytes); ++} ++ ++/* ++ * ISP1763 does not have the banks direct host controller memory access, ++ * needs to use the HC_DATA register. Add data read/write according to this, ++ * and also adjust 16bit access. ++ */ ++static void isp1763_mem_read(struct usb_hcd *hcd, u16 srcaddr, ++ u16 *dstptr, u32 bytes) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ ++ ++ /* Write the starting device address to the hcd memory register */ ++ isp1760_reg_write(priv->regs, ISP1763_HC_MEMORY, srcaddr); ++ ndelay(100); /* Delay between consecutive access */ ++ ++ /* As long there are at least 16-bit to read ... */ ++ while (bytes >= 2) { ++ *dstptr = __raw_readw(priv->base + ISP1763_HC_DATA); ++ bytes -= 2; ++ dstptr++; ++ } ++ ++ /* If there are no more bytes to read, return */ ++ if (bytes <= 0) ++ return; ++ ++ *((u8 *)dstptr) = (u8)(readw(priv->base + ISP1763_HC_DATA) & 0xFF); ++} ++ ++static void mem_read(struct usb_hcd *hcd, u32 src_offset, __u32 *dst, ++ u32 bytes) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ ++ if (!priv->is_isp1763) ++ return isp1760_mem_read(hcd, src_offset, (u16 *)dst, bytes); ++ ++ isp1763_mem_read(hcd, (u16)src_offset, (u16 *)dst, bytes); ++} ++ ++static void isp1760_mem_write(void __iomem *dst_base, u32 dst_offset, ++ __u32 const *src, u32 bytes) ++{ ++ __u32 __iomem *dst; ++ ++ dst = dst_base + dst_offset; ++ ++ if (dst_offset < PAYLOAD_OFFSET) { ++ while (bytes >= 4) { ++ writel_relaxed(*src, dst); ++ bytes -= 4; ++ src++; ++ dst++; ++ } ++ } else { ++ while (bytes >= 4) { ++ __raw_writel(*src, dst); ++ bytes -= 4; ++ src++; ++ dst++; ++ } ++ } ++ ++ if (!bytes) ++ return; ++ /* in case we have 3, 2 or 1 bytes left. The buffer is allocated and the ++ * extra bytes should not be read by the HW. ++ */ ++ ++ if (dst_offset < PAYLOAD_OFFSET) ++ writel_relaxed(*src, dst); ++ else ++ __raw_writel(*src, dst); ++} ++ ++static void isp1763_mem_write(struct usb_hcd *hcd, u16 dstaddr, u16 *src, ++ u32 bytes) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ ++ /* Write the starting device address to the hcd memory register */ ++ isp1760_reg_write(priv->regs, ISP1763_HC_MEMORY, dstaddr); ++ ndelay(100); /* Delay between consecutive access */ ++ ++ while (bytes >= 2) { ++ /* Get and write the data; then adjust the data ptr and len */ ++ __raw_writew(*src, priv->base + ISP1763_HC_DATA); ++ bytes -= 2; ++ src++; ++ } ++ ++ /* If there are no more bytes to process, return */ ++ if (bytes <= 0) ++ return; ++ ++ /* ++ * The only way to get here is if there is a single byte left, ++ * get it and write it to the data reg; ++ */ ++ writew(*((u8 *)src), priv->base + ISP1763_HC_DATA); ++} ++ ++static void mem_write(struct usb_hcd *hcd, u32 dst_offset, __u32 *src, ++ u32 bytes) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ ++ if (!priv->is_isp1763) ++ return isp1760_mem_write(priv->base, dst_offset, src, bytes); ++ ++ isp1763_mem_write(hcd, dst_offset, (u16 *)src, bytes); ++} ++ ++/* ++ * Read and write ptds. 'ptd_offset' should be one of ISO_PTD_OFFSET, ++ * INT_PTD_OFFSET, and ATL_PTD_OFFSET. 'slot' should be less than 32. ++ */ ++static void isp1760_ptd_read(struct usb_hcd *hcd, u32 ptd_offset, u32 slot, ++ struct ptd *ptd) ++{ ++ u16 src_offset = ptd_offset + slot * sizeof(*ptd); ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ ++ isp1760_hcd_write(hcd, MEM_BANK_SEL, ISP_BANK_0); ++ isp1760_hcd_write(hcd, MEM_START_ADDR, src_offset); ++ ndelay(90); ++ ++ bank_reads8(priv->base, src_offset, ISP_BANK_0, (void *)ptd, ++ sizeof(*ptd)); ++} ++ ++static void isp1763_ptd_read(struct usb_hcd *hcd, u32 ptd_offset, u32 slot, ++ struct ptd *ptd) ++{ ++ u16 src_offset = ptd_offset + slot * sizeof(*ptd); ++ struct ptd_le32 le32_ptd; ++ ++ isp1763_mem_read(hcd, src_offset, (u16 *)&le32_ptd, sizeof(le32_ptd)); ++ /* Normalize the data obtained */ ++ ptd->dw0 = le32_to_dw(le32_ptd.dw0); ++ ptd->dw1 = le32_to_dw(le32_ptd.dw1); ++ ptd->dw2 = le32_to_dw(le32_ptd.dw2); ++ ptd->dw3 = le32_to_dw(le32_ptd.dw3); ++ ptd->dw4 = le32_to_dw(le32_ptd.dw4); ++ ptd->dw5 = le32_to_dw(le32_ptd.dw5); ++ ptd->dw6 = le32_to_dw(le32_ptd.dw6); ++ ptd->dw7 = le32_to_dw(le32_ptd.dw7); ++} ++ ++static void ptd_read(struct usb_hcd *hcd, u32 ptd_offset, u32 slot, ++ struct ptd *ptd) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ ++ if (!priv->is_isp1763) ++ return isp1760_ptd_read(hcd, ptd_offset, slot, ptd); ++ ++ isp1763_ptd_read(hcd, ptd_offset, slot, ptd); ++} ++ ++static void isp1763_ptd_write(struct usb_hcd *hcd, u32 ptd_offset, u32 slot, ++ struct ptd *cpu_ptd) ++{ ++ u16 dst_offset = ptd_offset + slot * sizeof(*cpu_ptd); ++ struct ptd_le32 ptd; ++ ++ ptd.dw0 = dw_to_le32(cpu_ptd->dw0); ++ ptd.dw1 = dw_to_le32(cpu_ptd->dw1); ++ ptd.dw2 = dw_to_le32(cpu_ptd->dw2); ++ ptd.dw3 = dw_to_le32(cpu_ptd->dw3); ++ ptd.dw4 = dw_to_le32(cpu_ptd->dw4); ++ ptd.dw5 = dw_to_le32(cpu_ptd->dw5); ++ ptd.dw6 = dw_to_le32(cpu_ptd->dw6); ++ ptd.dw7 = dw_to_le32(cpu_ptd->dw7); ++ ++ isp1763_mem_write(hcd, dst_offset, (u16 *)&ptd.dw0, ++ 8 * sizeof(ptd.dw0)); ++} ++ ++static void isp1760_ptd_write(void __iomem *base, u32 ptd_offset, u32 slot, ++ struct ptd *ptd) ++{ ++ u32 dst_offset = ptd_offset + slot * sizeof(*ptd); ++ ++ /* ++ * Make sure dw0 gets written last (after other dw's and after payload) ++ * since it contains the enable bit ++ */ ++ isp1760_mem_write(base, dst_offset + sizeof(ptd->dw0), ++ (__force u32 *)&ptd->dw1, 7 * sizeof(ptd->dw1)); ++ wmb(); ++ isp1760_mem_write(base, dst_offset, (__force u32 *)&ptd->dw0, ++ sizeof(ptd->dw0)); ++} ++ ++static void ptd_write(struct usb_hcd *hcd, u32 ptd_offset, u32 slot, ++ struct ptd *ptd) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ ++ if (!priv->is_isp1763) ++ return isp1760_ptd_write(priv->base, ptd_offset, slot, ptd); ++ ++ isp1763_ptd_write(hcd, ptd_offset, slot, ptd); ++} ++ ++/* memory management of the 60kb on the chip from 0x1000 to 0xffff */ ++static void init_memory(struct isp1760_hcd *priv) ++{ ++ const struct isp1760_memory_layout *mem = priv->memory_layout; ++ int i, j, curr; ++ u32 payload_addr; ++ ++ payload_addr = PAYLOAD_OFFSET; ++ ++ for (i = 0, curr = 0; i < ARRAY_SIZE(mem->blocks); i++) { ++ for (j = 0; j < mem->blocks[i]; j++, curr++) { ++ priv->memory_pool[curr + j].start = payload_addr; ++ priv->memory_pool[curr + j].size = mem->blocks_size[i]; ++ priv->memory_pool[curr + j].free = 1; ++ payload_addr += priv->memory_pool[curr + j].size; ++ } ++ } ++ ++ WARN_ON(payload_addr - priv->memory_pool[0].start > ++ mem->payload_area_size); ++} ++ ++static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ const struct isp1760_memory_layout *mem = priv->memory_layout; ++ int i; ++ ++ WARN_ON(qtd->payload_addr); ++ ++ if (!qtd->length) ++ return; ++ ++ for (i = 0; i < mem->payload_blocks; i++) { ++ if (priv->memory_pool[i].size >= qtd->length && ++ priv->memory_pool[i].free) { ++ priv->memory_pool[i].free = 0; ++ qtd->payload_addr = priv->memory_pool[i].start; ++ return; ++ } ++ } ++} ++ ++static void free_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ const struct isp1760_memory_layout *mem = priv->memory_layout; ++ int i; ++ ++ if (!qtd->payload_addr) ++ return; ++ ++ for (i = 0; i < mem->payload_blocks; i++) { ++ if (priv->memory_pool[i].start == qtd->payload_addr) { ++ WARN_ON(priv->memory_pool[i].free); ++ priv->memory_pool[i].free = 1; ++ qtd->payload_addr = 0; ++ return; ++ } ++ } ++ ++ WARN_ON(1); ++ qtd->payload_addr = 0; ++} ++ ++/* reset a non-running (STS_HALT == 1) controller */ ++static int ehci_reset(struct usb_hcd *hcd) ++{ ++ return isp1760_hcd_set_and_wait_swap(hcd, CMD_RESET, 250 * 1000); ++} ++ ++static struct isp1760_qh *qh_alloc(gfp_t flags) ++{ ++ struct isp1760_qh *qh; ++ ++ qh = kmem_cache_alloc(qh_cachep, flags); ++ if (!qh) ++ return NULL; ++ ++ memset(qh, '\0', qh_cachep->sz); ++ INIT_LIST_HEAD(&qh->qh_list); ++ INIT_LIST_HEAD(&qh->qtd_list); ++ qh->slot = -1; ++ ++ return qh; ++} ++ ++static void qh_free(struct isp1760_qh *qh) ++{ ++ WARN_ON(!list_empty(&qh->qtd_list)); ++ WARN_ON(qh->slot > -1); ++ kmem_cache_free(qh_cachep, qh); ++} ++ ++/* one-time init, only for memory state */ ++static int priv_init(struct usb_hcd *hcd) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ u32 isoc_cache; ++ u32 isoc_thres; ++ int i; ++ ++ spin_lock_init(&priv->lock); ++ ++ for (i = 0; i < QH_END; i++) ++ INIT_LIST_HEAD(&priv->qh_list[i]); ++ ++ /* ++ * hw default: 1K periodic list heads, one per frame. ++ * periodic_size can shrink by USBCMD update if hcc_params allows. ++ */ ++ priv->periodic_size = DEFAULT_I_TDPS; ++ ++ if (priv->is_isp1763) { ++ priv->i_thresh = 2; ++ return 0; ++ } ++ ++ /* controllers may cache some of the periodic schedule ... */ ++ isoc_cache = isp1760_hcd_read(hcd, HCC_ISOC_CACHE); ++ isoc_thres = isp1760_hcd_read(hcd, HCC_ISOC_THRES); ++ ++ /* full frame cache */ ++ if (isoc_cache) ++ priv->i_thresh = 8; ++ else /* N microframes cached */ ++ priv->i_thresh = 2 + isoc_thres; ++ ++ return 0; ++} ++ ++static int isp1760_hc_setup(struct usb_hcd *hcd) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ u32 atx_reset; ++ int result; ++ u32 scratch; ++ u32 pattern; ++ ++ if (priv->is_isp1763) ++ pattern = 0xcafe; ++ else ++ pattern = 0xdeadcafe; ++ ++ isp1760_hcd_write(hcd, HC_SCRATCH, pattern); ++ ++ /* Change bus pattern */ ++ scratch = isp1760_hcd_read(hcd, HC_CHIP_ID_HIGH); ++ scratch = isp1760_hcd_read(hcd, HC_SCRATCH); ++ if (scratch != pattern) { ++ printf("Scratch test failed. 0x%08x\n", scratch); ++ return -ENODEV; ++ } ++ ++ /* ++ * The RESET_HC bit in the SW_RESET register is supposed to reset the ++ * host controller without touching the CPU interface registers, but at ++ * least on the ISP1761 it seems to behave as the RESET_ALL bit and ++ * reset the whole device. We thus can't use it here, so let's reset ++ * the host controller through the EHCI USB Command register. The device ++ * has been reset in core code anyway, so this shouldn't matter. ++ */ ++ isp1760_hcd_clear(hcd, ISO_BUF_FILL); ++ isp1760_hcd_clear(hcd, INT_BUF_FILL); ++ isp1760_hcd_clear(hcd, ATL_BUF_FILL); ++ ++ isp1760_hcd_set(hcd, HC_ATL_PTD_SKIPMAP); ++ isp1760_hcd_set(hcd, HC_INT_PTD_SKIPMAP); ++ isp1760_hcd_set(hcd, HC_ISO_PTD_SKIPMAP); ++ ++ result = ehci_reset(hcd); ++ if (result) ++ return result; ++ ++ /* Step 11 passed */ ++ ++ /* ATL reset */ ++ if (priv->is_isp1763) ++ atx_reset = SW_RESET_RESET_ATX; ++ else ++ atx_reset = ALL_ATX_RESET; ++ ++ isp1760_hcd_set(hcd, atx_reset); ++ mdelay(10); ++ isp1760_hcd_clear(hcd, atx_reset); ++ ++ if (priv->is_isp1763) { ++ isp1760_hcd_set(hcd, HW_OTG_DISABLE); ++ isp1760_hcd_set(hcd, HW_SW_SEL_HC_DC_CLEAR); ++ isp1760_hcd_set(hcd, HW_HC_2_DIS_CLEAR); ++ isp1760_hcd_set(hcd, HW_DM_PULLDOWN); ++ isp1760_hcd_set(hcd, HW_DP_PULLDOWN); ++ mdelay(10); ++ ++ isp1760_hcd_set(hcd, HW_INTF_LOCK); ++ } ++ ++ isp1760_hcd_set(hcd, HC_INT_IRQ_ENABLE); ++ isp1760_hcd_set(hcd, HC_ATL_IRQ_ENABLE); ++ ++ return priv_init(hcd); ++} ++ ++static u32 base_to_chip(u32 base) ++{ ++ return ((base - 0x400) >> 3); ++} ++ ++static int last_qtd_of_urb(struct isp1760_qtd *qtd, struct isp1760_qh *qh) ++{ ++ struct urb *urb; ++ ++ if (list_is_last(&qtd->qtd_list, &qh->qtd_list)) ++ return 1; ++ ++ urb = qtd->urb; ++ qtd = list_entry(qtd->qtd_list.next, typeof(*qtd), qtd_list); ++ ++ return (qtd->urb != urb); ++} ++ ++/* magic numbers that can affect system performance */ ++#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ ++#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ ++#define EHCI_TUNE_RL_TT 0 ++#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ ++#define EHCI_TUNE_MULT_TT 1 ++#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ ++ ++static void create_ptd_atl(struct isp1760_qh *qh, struct isp1760_qtd *qtd, ++ struct ptd *ptd) ++{ ++ u32 maxpacket; ++ u32 multi; ++ u32 rl = RL_COUNTER; ++ u32 nak = NAK_COUNTER; ++ u8 portnr; ++ u8 hubaddr; ++ ++ memset(ptd, 0, sizeof(*ptd)); ++ ++ /* according to 3.6.2, max packet len can not be > 0x400 */ ++ maxpacket = usb_maxpacket(qtd->urb->dev, qtd->urb->pipe); ++ multi = 1 + ((maxpacket >> 11) & 0x3); ++ maxpacket &= 0x7ff; ++ ++ /* DW0 */ ++ ptd->dw0 = DW0_VALID_BIT; ++ ptd->dw0 |= TO_DW0_LENGTH(qtd->length); ++ ptd->dw0 |= TO_DW0_MAXPACKET(maxpacket); ++ ptd->dw0 |= TO_DW0_ENDPOINT(usb_pipeendpoint(qtd->urb->pipe)); ++ ++ /* DW1 */ ++ ptd->dw1 = TO_DW((usb_pipeendpoint(qtd->urb->pipe) >> 1)); ++ ptd->dw1 |= TO_DW1_DEVICE_ADDR(usb_pipedevice(qtd->urb->pipe)); ++ ptd->dw1 |= TO_DW1_PID_TOKEN(qtd->packet_type); ++ ++ if (usb_pipebulk(qtd->urb->pipe)) ++ ptd->dw1 |= DW1_TRANS_BULK; ++ else if (usb_pipeint(qtd->urb->pipe)) ++ ptd->dw1 |= DW1_TRANS_INT; ++ ++ if (qtd->urb->dev->speed != USB_SPEED_HIGH) { ++ /* split transaction */ ++ ++ ptd->dw1 |= DW1_TRANS_SPLIT; ++ if (qtd->urb->dev->speed == USB_SPEED_LOW) ++ ptd->dw1 |= DW1_SE_USB_LOSPEED; ++ ++ if (!qtd->urb->dev->dev->parent_priv_) { ++ portnr = qtd->urb->dev->portnr; ++ hubaddr = qtd->urb->dev->devnum; ++ } else { ++ usb_find_usb2_hub_address_port(qtd->urb->dev, &hubaddr, ++ &portnr); ++ } ++ ++ ptd->dw1 |= TO_DW1_PORT_NUM(portnr); ++ ptd->dw1 |= TO_DW1_HUB_NUM(hubaddr); ++ ++ /* SE bit for Split INT transfers */ ++ if (usb_pipeint(qtd->urb->pipe) && ++ (qtd->urb->dev->speed == USB_SPEED_LOW)) ++ ptd->dw1 |= DW1_SE_USB_LOSPEED; ++ ++ rl = 0; ++ nak = 0; ++ } else { ++ ptd->dw0 |= TO_DW0_MULTI(multi); ++ if (usb_pipecontrol(qtd->urb->pipe) || ++ usb_pipebulk(qtd->urb->pipe)) ++ ptd->dw3 |= TO_DW3_PING(qh->ping); ++ } ++ /* DW2 */ ++ ptd->dw2 = 0; ++ ptd->dw2 |= TO_DW2_DATA_START_ADDR(base_to_chip(qtd->payload_addr)); ++ ptd->dw2 |= TO_DW2_RL(rl); ++ ++ /* DW3 */ ++ ptd->dw3 |= TO_DW3_NAKCOUNT(nak); ++ ptd->dw3 |= TO_DW3_DATA_TOGGLE(qh->toggle); ++ ++ if (usb_pipecontrol(qtd->urb->pipe)) { ++ if (qtd->data_buffer == qtd->urb->setup_packet) { ++ ptd->dw3 &= ~TO_DW3_DATA_TOGGLE(1); ++ } else if (last_qtd_of_urb(qtd, qh)) { ++ ptd->dw3 |= TO_DW3_DATA_TOGGLE(1); ++ } ++ } ++ ++ ptd->dw3 |= DW3_ACTIVE_BIT; ++ /* Cerr */ ++ ptd->dw3 |= TO_DW3_CERR(ERR_COUNTER); ++} ++ ++static void transform_add_int(struct isp1760_qh *qh, struct isp1760_qtd *qtd, ++ struct ptd *ptd) ++{ ++ struct usb_host_endpoint *hep = qtd->urb->ep; ++ struct usb_endpoint_descriptor *epd = &hep->desc; ++ u32 usof; ++ u32 period; ++ ++ /* ++ * Most of this is guessing. ISP1761 datasheet is quite unclear, and ++ * the algorithm from the original Philips driver code, which was ++ * pretty much used in this driver before as well, is quite horrendous ++ * and, i believe, incorrect. The code below follows the datasheet and ++ * USB2.0 spec as far as I can tell, and plug/unplug seems to be much ++ * more reliable this way (fingers crossed...). ++ */ ++ ++ if (qtd->urb->dev->speed == USB_SPEED_HIGH) { ++ /* urb->interval is in units of microframes (1/8 ms) */ ++ period = epd->bInterval >> 3; ++ ++ if (epd->bInterval > 4) ++ usof = 0x01; /* One bit set => ++ interval 1 ms * uFrame-match */ ++ else if (epd->bInterval > 2) ++ usof = 0x22; /* Two bits set => interval 1/2 ms */ ++ else if (epd->bInterval > 1) ++ usof = 0x55; /* Four bits set => interval 1/4 ms */ ++ else ++ usof = 0xff; /* All bits set => interval 1/8 ms */ ++ } else { ++ /* urb->interval is in units of frames (1 ms) */ ++ period = epd->bInterval; ++ usof = 0x0f; /* Execute Start Split on any of the ++ four first uFrames */ ++ ++ /* ++ * First 8 bits in dw5 is uSCS and "specifies which uSOF the ++ * complete split needs to be sent. Valid only for IN." Also, ++ * "All bits can be set to one for every transfer." (p 82, ++ * ISP1761 data sheet.) 0x1c is from Philips driver. Where did ++ * that number come from? 0xff seems to work fine... ++ */ ++ /* ptd->dw5 = 0x1c; */ ++ ptd->dw5 = TO_DW(0xff); /* Execute Complete Split on any uFrame */ ++ } ++ ++ period = period >> 1;/* Ensure equal or shorter period than requested */ ++ period &= 0xf8; /* Mask off too large values and lowest unused 3 bits */ ++ ++ ptd->dw2 |= TO_DW(period); ++ ptd->dw4 = TO_DW(usof); ++} ++ ++static void create_ptd_int(struct isp1760_qh *qh, struct isp1760_qtd *qtd, ++ struct ptd *ptd) ++{ ++ create_ptd_atl(qh, qtd, ptd); ++ transform_add_int(qh, qtd, ptd); ++} ++ ++static void isp1760_urb_done(struct usb_hcd *hcd, struct urb *urb) ++{ ++ if (usb_pipein(urb->pipe) && usb_pipetype(urb->pipe) != PIPE_CONTROL) { ++ void *ptr; ++ for (ptr = urb->transfer_buffer; ++ ptr < urb->transfer_buffer + urb->transfer_buffer_length; ++ ptr += PAGE_SIZE) ++ flush_dcache_range((unsigned long)ptr, ++ (unsigned long)ptr + PAGE_SIZE); ++ } ++ ++ /* complete() can reenter this HCD */ ++ usb_hcd_unlink_urb_from_ep(hcd, urb); ++ usb_hcd_giveback_urb(hcd, urb, urb->status); ++} ++ ++static struct isp1760_qtd *qtd_alloc(gfp_t flags, struct urb *urb, ++ u8 packet_type) ++{ ++ struct isp1760_qtd *qtd; ++ ++ qtd = kmem_cache_alloc(qtd_cachep, flags); ++ if (!qtd) ++ return NULL; ++ ++ memset(qtd, '\0', sizeof(*qtd)); ++ INIT_LIST_HEAD(&qtd->qtd_list); ++ qtd->urb = urb; ++ qtd->packet_type = packet_type; ++ qtd->status = QTD_ENQUEUED; ++ qtd->actual_length = 0; ++ ++ return qtd; ++} ++ ++static void qtd_free(struct isp1760_qtd *qtd) ++{ ++ WARN_ON(qtd->payload_addr); ++ kmem_cache_free(qtd_cachep, qtd); ++} ++ ++static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot, ++ struct isp1760_slotinfo *slots, ++ struct isp1760_qtd *qtd, struct isp1760_qh *qh, ++ struct ptd *ptd) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ const struct isp1760_memory_layout *mem = priv->memory_layout; ++ int skip_map; ++ ++ WARN_ON((slot < 0) || (slot > mem->slot_num - 1)); ++ WARN_ON(qtd->length && !qtd->payload_addr); ++ WARN_ON(slots[slot].qtd); ++ WARN_ON(slots[slot].qh); ++ WARN_ON(qtd->status != QTD_PAYLOAD_ALLOC); ++ ++ if (priv->is_isp1763) ++ ndelay(100); ++ ++ /* Make sure done map has not triggered from some unlinked transfer */ ++ if (ptd_offset == ATL_PTD_OFFSET) { ++ skip_map = isp1760_hcd_read(hcd, HC_ATL_PTD_SKIPMAP); ++ isp1760_hcd_write(hcd, HC_ATL_PTD_SKIPMAP, ++ skip_map | (1 << slot)); ++ priv->atl_done_map |= isp1760_hcd_read(hcd, HC_ATL_PTD_DONEMAP); ++ priv->atl_done_map &= ~(1 << slot); ++ } else { ++ skip_map = isp1760_hcd_read(hcd, HC_INT_PTD_SKIPMAP); ++ isp1760_hcd_write(hcd, HC_INT_PTD_SKIPMAP, ++ skip_map | (1 << slot)); ++ priv->int_done_map |= isp1760_hcd_read(hcd, HC_INT_PTD_DONEMAP); ++ priv->int_done_map &= ~(1 << slot); ++ } ++ ++ skip_map &= ~(1 << slot); ++ qh->slot = slot; ++ qtd->status = QTD_XFER_STARTED; ++ slots[slot].qtd = qtd; ++ slots[slot].qh = qh; ++ ++ ptd_write(hcd, ptd_offset, slot, ptd); ++ ++ if (ptd_offset == ATL_PTD_OFFSET) ++ isp1760_hcd_write(hcd, HC_ATL_PTD_SKIPMAP, skip_map); ++ else ++ isp1760_hcd_write(hcd, HC_INT_PTD_SKIPMAP, skip_map); ++} ++ ++static int is_short_bulk(struct isp1760_qtd *qtd) ++{ ++ return (usb_pipebulk(qtd->urb->pipe) && ++ (qtd->actual_length < qtd->length)); ++} ++ ++static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh, ++ struct list_head *urb_list) ++{ ++ struct isp1760_qtd *qtd, *qtd_next; ++ struct urb_listitem *urb_listitem; ++ int last_qtd; ++ ++ list_for_each_entry_safe(qtd, qtd_next, &qh->qtd_list, qtd_list) { ++ if (qtd->status < QTD_XFER_COMPLETE) ++ break; ++ ++ last_qtd = last_qtd_of_urb(qtd, qh); ++ ++ if ((!last_qtd) && (qtd->status == QTD_RETIRE)) ++ qtd_next->status = QTD_RETIRE; ++ ++ if (qtd->status == QTD_XFER_COMPLETE) { ++ if (qtd->actual_length) { ++ switch (qtd->packet_type) { ++ case IN_PID: ++ mem_read(hcd, qtd->payload_addr, ++ qtd->data_buffer, ++ qtd->actual_length); ++ fallthrough; ++ case OUT_PID: ++ qtd->urb->actual_length += ++ qtd->actual_length; ++ fallthrough; ++ case SETUP_PID: ++ break; ++ } ++ } ++ ++ if (is_short_bulk(qtd)) { ++ if (qtd->urb->transfer_flags & URB_SHORT_NOT_OK) ++ qtd->urb->status = -EREMOTEIO; ++ if (!last_qtd) ++ qtd_next->status = QTD_RETIRE; ++ } ++ } ++ ++ if (qtd->payload_addr) ++ free_mem(hcd, qtd); ++ ++ if (last_qtd) { ++ if ((qtd->status == QTD_RETIRE) && ++ (qtd->urb->status == -EINPROGRESS)) ++ qtd->urb->status = -EPIPE; ++ /* Defer calling of urb_done() since it releases lock */ ++ urb_listitem = kmem_cache_alloc(urb_listitem_cachep, ++ GFP_ATOMIC); ++ if (unlikely(!urb_listitem)) ++ break; /* Try again on next call */ ++ urb_listitem->urb = qtd->urb; ++ list_add_tail(&urb_listitem->urb_list, urb_list); ++ } ++ ++ list_del(&qtd->qtd_list); ++ qtd_free(qtd); ++ } ++} ++ ++#define ENQUEUE_DEPTH 2 ++static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ const struct isp1760_memory_layout *mem = priv->memory_layout; ++ int slot_num = mem->slot_num; ++ int ptd_offset; ++ struct isp1760_slotinfo *slots; ++ int curr_slot, free_slot; ++ int n; ++ struct ptd ptd; ++ struct isp1760_qtd *qtd; ++ ++ if (unlikely(list_empty(&qh->qtd_list))) ++ return; ++ ++ /* Make sure this endpoint's TT buffer is clean before queueing ptds */ ++ if (qh->tt_buffer_dirty) ++ return; ++ ++ if (usb_pipeint(list_entry(qh->qtd_list.next, struct isp1760_qtd, ++ qtd_list)->urb->pipe)) { ++ ptd_offset = INT_PTD_OFFSET; ++ slots = priv->int_slots; ++ } else { ++ ptd_offset = ATL_PTD_OFFSET; ++ slots = priv->atl_slots; ++ } ++ ++ free_slot = -1; ++ for (curr_slot = 0; curr_slot < slot_num; curr_slot++) { ++ if ((free_slot == -1) && (slots[curr_slot].qtd == NULL)) ++ free_slot = curr_slot; ++ if (slots[curr_slot].qh == qh) ++ break; ++ } ++ ++ n = 0; ++ list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { ++ if (qtd->status == QTD_ENQUEUED) { ++ WARN_ON(qtd->payload_addr); ++ alloc_mem(hcd, qtd); ++ if ((qtd->length) && (!qtd->payload_addr)) ++ break; ++ ++ if (qtd->length && (qtd->packet_type == SETUP_PID || ++ qtd->packet_type == OUT_PID)) { ++ mem_write(hcd, qtd->payload_addr, ++ qtd->data_buffer, qtd->length); ++ } ++ ++ qtd->status = QTD_PAYLOAD_ALLOC; ++ } ++ ++ if (qtd->status == QTD_PAYLOAD_ALLOC) { ++/* ++ if ((curr_slot > 31) && (free_slot == -1)) ++ printf("%s: No slot " ++ "available for transfer\n", __func__); ++*/ ++ /* Start xfer for this endpoint if not already done */ ++ if ((curr_slot > slot_num - 1) && (free_slot > -1)) { ++ if (usb_pipeint(qtd->urb->pipe)) ++ create_ptd_int(qh, qtd, &ptd); ++ else ++ create_ptd_atl(qh, qtd, &ptd); ++ ++ start_bus_transfer(hcd, ptd_offset, free_slot, ++ slots, qtd, qh, &ptd); ++ curr_slot = free_slot; ++ } ++ ++ n++; ++ if (n >= ENQUEUE_DEPTH) ++ break; ++ } ++ } ++} ++ ++static void schedule_ptds(struct usb_hcd *hcd) ++{ ++ struct isp1760_hcd *priv; ++ struct isp1760_qh *qh, *qh_next; ++ struct list_head *ep_queue; ++ LIST_HEAD(urb_list); ++ struct urb_listitem *urb_listitem, *urb_listitem_next; ++ int i; ++ ++ if (!hcd) { ++ WARN_ON(1); ++ return; ++ } ++ ++ priv = hcd_to_priv(hcd); ++ ++ /* ++ * check finished/retired xfers, transfer payloads, call urb_done() ++ */ ++ for (i = 0; i < QH_END; i++) { ++ ep_queue = &priv->qh_list[i]; ++ list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list) ++ collect_qtds(hcd, qh, &urb_list); ++ } ++ ++ list_for_each_entry_safe(urb_listitem, urb_listitem_next, &urb_list, ++ urb_list) { ++ isp1760_urb_done(hcd, urb_listitem->urb); ++ kmem_cache_free(urb_listitem_cachep, urb_listitem); ++ } ++ ++ /* ++ * Schedule packets for transfer. ++ * ++ * According to USB2.0 specification: ++ * ++ * 1st prio: interrupt xfers, up to 80 % of bandwidth ++ * 2nd prio: control xfers ++ * 3rd prio: bulk xfers ++ * ++ * ... but let's use a simpler scheme here (mostly because ISP1761 doc ++ * is very unclear on how to prioritize traffic): ++ * ++ * 1) Enqueue any queued control transfers, as long as payload chip mem ++ * and PTD ATL slots are available. ++ * 2) Enqueue any queued INT transfers, as long as payload chip mem ++ * and PTD INT slots are available. ++ * 3) Enqueue any queued bulk transfers, as long as payload chip mem ++ * and PTD ATL slots are available. ++ * ++ * Use double buffering (ENQUEUE_DEPTH==2) as a compromise between ++ * conservation of chip mem and performance. ++ * ++ * I'm sure this scheme could be improved upon! ++ */ ++ for (i = 0; i < QH_END; i++) { ++ ep_queue = &priv->qh_list[i]; ++ list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list) ++ enqueue_qtds(hcd, qh); ++ } ++} ++ ++#define PTD_STATE_QTD_DONE 1 ++#define PTD_STATE_QTD_RELOAD 2 ++#define PTD_STATE_URB_RETIRE 3 ++ ++static int check_int_transfer(struct usb_hcd *hcd, struct ptd *ptd, ++ struct urb *urb) ++{ ++ u32 dw4; ++ int i; ++ ++ dw4 = TO_U32(ptd->dw4); ++ dw4 >>= 8; ++ ++ /* FIXME: ISP1761 datasheet does not say what to do with these. Do we ++ need to handle these errors? Is it done in hardware? */ ++ if (ptd->dw3 & DW3_HALT_BIT) { ++ ++ urb->status = -EPROTO; /* Default unknown error */ ++ ++ for (i = 0; i < 8; i++) { ++ switch (dw4 & 0x7) { ++ case INT_UNDERRUN: ++ printf("underrun during uFrame %d\n", i); ++ urb->status = -ECOMM; /* Could not write data */ ++ break; ++ case INT_EXACT: ++ printf("transaction error uFrame %d\n", i); ++ urb->status = -EPROTO; /* timeout, bad CRC, PID ++ error etc. */ ++ break; ++ case INT_BABBLE: ++ printf("babble error during uFrame %d\n", i); ++ urb->status = -EOVERFLOW; ++ break; ++ } ++ dw4 >>= 3; ++ } ++ ++ return PTD_STATE_URB_RETIRE; ++ } ++ ++ return PTD_STATE_QTD_DONE; ++} ++ ++static int check_atl_transfer(struct usb_hcd *hcd, struct ptd *ptd, ++ struct urb *urb) ++{ ++ WARN_ON(!ptd); ++ if (ptd->dw3 & DW3_HALT_BIT) { ++ if (ptd->dw3 & DW3_BABBLE_BIT) ++ urb->status = -EOVERFLOW; ++ else if (FROM_DW3_CERR(ptd->dw3)) ++ urb->status = -EPIPE; /* Stall */ ++ else ++ urb->status = -EPROTO; /* Unknown */ ++ ++ /* usefull debug ++ printf("%s: ptd error:\n" ++ " dw0: %08x dw1: %08x dw2: %08x dw3: %08x\n" ++ " dw4: %08x dw5: %08x dw6: %08x dw7: %08x\n", ++ __func__, ++ ptd->dw0, ptd->dw1, ptd->dw2, ptd->dw3, ++ ptd->dw4, ptd->dw5, ptd->dw6, ptd->dw7); ++ */ ++ ++ return PTD_STATE_URB_RETIRE; ++ } ++ ++ /* Transfer Error, *but* active and no HALT -> reload */ ++ if ((ptd->dw3 & DW3_ERROR_BIT) && (ptd->dw3 & DW3_ACTIVE_BIT)) ++ return PTD_STATE_QTD_RELOAD; ++ ++ /* ++ * NAKs are handled in HW by the chip. Usually if the ++ * device is not able to send data fast enough. ++ * This happens mostly on slower hardware. ++ */ ++ if (!FROM_DW3_NAKCOUNT(ptd->dw3) && (ptd->dw3 & DW3_ACTIVE_BIT)) ++ return PTD_STATE_QTD_RELOAD; ++ ++ return PTD_STATE_QTD_DONE; ++} ++ ++static void handle_done_ptds(struct usb_hcd *hcd) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ struct isp1760_slotinfo *slots; ++ struct isp1760_qtd *qtd; ++ struct isp1760_qh *qh; ++ struct ptd ptd; ++ u32 ptd_offset; ++ int modified; ++ int skip_map; ++ int state; ++ int slot; ++ ++ skip_map = isp1760_hcd_read(hcd, HC_INT_PTD_SKIPMAP); ++ priv->int_done_map &= ~skip_map; ++ skip_map = isp1760_hcd_read(hcd, HC_ATL_PTD_SKIPMAP); ++ priv->atl_done_map &= ~skip_map; ++ ++ modified = priv->int_done_map || priv->atl_done_map; ++ ++ while (priv->int_done_map || priv->atl_done_map) { ++ if (priv->int_done_map) { ++ /* INT ptd */ ++ slot = __ffs(priv->int_done_map); ++ priv->int_done_map &= ~(1 << slot); ++ slots = priv->int_slots; ++ /* This should not trigger, and could be removed if ++ noone have any problems with it triggering: */ ++ if (!slots[slot].qh) { ++ WARN_ON(1); ++ continue; ++ } ++ ptd_offset = INT_PTD_OFFSET; ++ ptd_read(hcd, INT_PTD_OFFSET, slot, &ptd); ++ state = check_int_transfer(hcd, &ptd, ++ slots[slot].qtd->urb); ++ } else { ++ /* ATL ptd */ ++ slot = __ffs(priv->atl_done_map); ++ priv->atl_done_map &= ~(1 << slot); ++ slots = priv->atl_slots; ++ /* This should not trigger, and could be removed if ++ noone have any problems with it triggering: */ ++ if (!slots[slot].qh) { ++ WARN_ON(1); ++ continue; ++ } ++ ptd_offset = ATL_PTD_OFFSET; ++ ptd_read(hcd, ATL_PTD_OFFSET, slot, &ptd); ++ state = check_atl_transfer(hcd, &ptd, ++ slots[slot].qtd->urb); ++ } ++ ++ qtd = slots[slot].qtd; ++ slots[slot].qtd = NULL; ++ qh = slots[slot].qh; ++ slots[slot].qh = NULL; ++ qh->slot = -1; ++ ++ WARN_ON(qtd->status != QTD_XFER_STARTED); ++ ++ switch (state) { ++ case PTD_STATE_QTD_DONE: ++ if ((usb_pipeint(qtd->urb->pipe)) && ++ (qtd->urb->dev->speed != USB_SPEED_HIGH)) ++ qtd->actual_length = ++ FROM_DW3_SCS_NRBYTESTRANSFERRED(ptd.dw3); ++ else ++ qtd->actual_length = ++ FROM_DW3_NRBYTESTRANSFERRED(ptd.dw3); ++ ++ qtd->status = QTD_XFER_COMPLETE; ++ ++ if (list_is_last(&qtd->qtd_list, &qh->qtd_list) || ++ is_short_bulk(qtd)) ++ qtd = NULL; ++ else ++ qtd = list_entry(qtd->qtd_list.next, ++ typeof(*qtd), qtd_list); ++ ++ qh->toggle = FROM_DW3_DATA_TOGGLE(ptd.dw3); ++ qh->ping = FROM_DW3_PING(ptd.dw3); ++ ++ break; ++ ++ case PTD_STATE_QTD_RELOAD: /* QTD_RETRY, for atls only */ ++ qtd->status = QTD_PAYLOAD_ALLOC; ++ ptd.dw0 |= DW0_VALID_BIT; ++ /* RL counter = ERR counter */ ++ ptd.dw3 &= ~TO_DW3_NAKCOUNT(0xf); ++ ptd.dw3 |= TO_DW3_NAKCOUNT(FROM_DW2_RL(ptd.dw2)); ++ ptd.dw3 &= ~TO_DW3_CERR(3); ++ ptd.dw3 |= TO_DW3_CERR(ERR_COUNTER); ++ ++ qh->toggle = FROM_DW3_DATA_TOGGLE(ptd.dw3); ++ qh->ping = FROM_DW3_PING(ptd.dw3); ++ break; ++ ++ case PTD_STATE_URB_RETIRE: ++ qtd->status = QTD_RETIRE; ++ qtd = NULL; ++ qh->toggle = 0; ++ qh->ping = 0; ++ break; ++ ++ default: ++ WARN_ON(1); ++ continue; ++ } ++ ++ if (qtd && (qtd->status == QTD_PAYLOAD_ALLOC)) { ++ if (slots == priv->int_slots) { ++ if (state == PTD_STATE_QTD_RELOAD) ++ dev_err(priv->dev, ++ "%s: PTD_STATE_QTD_RELOAD on " ++ "interrupt packet\n", __func__); ++ if (state != PTD_STATE_QTD_RELOAD) ++ create_ptd_int(qh, qtd, &ptd); ++ } else { ++ if (state != PTD_STATE_QTD_RELOAD) ++ create_ptd_atl(qh, qtd, &ptd); ++ } ++ ++ start_bus_transfer(hcd, ptd_offset, slot, slots, qtd, ++ qh, &ptd); ++ } ++ } ++ ++ if (modified) ++ schedule_ptds(hcd); ++} ++ ++static irqreturn_t isp1760_irq(int irq, void *__hci) ++{ ++ struct usb_hcd *hcd = __hci; ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ irqreturn_t irqret = IRQ_NONE; ++ u32 int_reg; ++ u32 imask; ++ ++ imask = isp1760_hcd_read(hcd, HC_INTERRUPT); ++ if (unlikely(!imask)) ++ return irqret; ++ ++ int_reg = priv->is_isp1763 ? ISP1763_HC_INTERRUPT : ++ ISP176x_HC_INTERRUPT; ++ isp1760_reg_write(priv->regs, int_reg, imask); ++ ++ priv->int_done_map |= isp1760_hcd_read(hcd, HC_INT_PTD_DONEMAP); ++ priv->atl_done_map |= isp1760_hcd_read(hcd, HC_ATL_PTD_DONEMAP); ++ ++ handle_done_ptds(hcd); ++ ++ irqret = IRQ_HANDLED; ++ ++ return irqret; ++} ++ ++static int isp1763_run(struct usb_hcd *hcd) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ int retval; ++ u32 chipid_h; ++ u32 chipid_l; ++ u32 chip_rev; ++ u32 ptd_atl_int; ++ u32 ptd_iso; ++ ++ chipid_h = isp1760_hcd_read(hcd, HC_CHIP_ID_HIGH); ++ chipid_l = isp1760_hcd_read(hcd, HC_CHIP_ID_LOW); ++ chip_rev = isp1760_hcd_read(hcd, HC_CHIP_REV); ++ printf("USB ISP %02x%02x HW rev. %d started\n", chipid_h, ++ chipid_l, chip_rev); ++ ++ isp1760_hcd_clear(hcd, ISO_BUF_FILL); ++ isp1760_hcd_clear(hcd, INT_BUF_FILL); ++ isp1760_hcd_clear(hcd, ATL_BUF_FILL); ++ ++ isp1760_hcd_set(hcd, HC_ATL_PTD_SKIPMAP); ++ isp1760_hcd_set(hcd, HC_INT_PTD_SKIPMAP); ++ isp1760_hcd_set(hcd, HC_ISO_PTD_SKIPMAP); ++ ndelay(100); ++ isp1760_hcd_clear(hcd, HC_ATL_PTD_DONEMAP); ++ isp1760_hcd_clear(hcd, HC_INT_PTD_DONEMAP); ++ isp1760_hcd_clear(hcd, HC_ISO_PTD_DONEMAP); ++ ++ isp1760_hcd_set(hcd, HW_OTG_DISABLE); ++ isp1760_reg_write(priv->regs, ISP1763_HC_OTG_CTRL_CLEAR, BIT(7)); ++ isp1760_reg_write(priv->regs, ISP1763_HC_OTG_CTRL_CLEAR, BIT(15)); ++ mdelay(10); ++ ++ isp1760_hcd_set(hcd, HC_INT_IRQ_ENABLE); ++ isp1760_hcd_set(hcd, HC_ATL_IRQ_ENABLE); ++ ++ isp1760_hcd_set(hcd, HW_GLOBAL_INTR_EN); ++ ++ isp1760_hcd_clear(hcd, HC_ATL_IRQ_MASK_AND); ++ isp1760_hcd_clear(hcd, HC_INT_IRQ_MASK_AND); ++ isp1760_hcd_clear(hcd, HC_ISO_IRQ_MASK_AND); ++ ++ isp1760_hcd_set(hcd, HC_ATL_IRQ_MASK_OR); ++ isp1760_hcd_set(hcd, HC_INT_IRQ_MASK_OR); ++ isp1760_hcd_set(hcd, HC_ISO_IRQ_MASK_OR); ++ ++ ptd_atl_int = 0x8000; ++ ptd_iso = 0x0001; ++ ++ isp1760_hcd_write(hcd, HC_ATL_PTD_LASTPTD, ptd_atl_int); ++ isp1760_hcd_write(hcd, HC_INT_PTD_LASTPTD, ptd_atl_int); ++ isp1760_hcd_write(hcd, HC_ISO_PTD_LASTPTD, ptd_iso); ++ ++ isp1760_hcd_set(hcd, ATL_BUF_FILL); ++ isp1760_hcd_set(hcd, INT_BUF_FILL); ++ ++ isp1760_hcd_clear(hcd, CMD_LRESET); ++ isp1760_hcd_clear(hcd, CMD_RESET); ++ ++ retval = isp1760_hcd_set_and_wait(hcd, CMD_RUN, 250 * 1000); ++ if (retval) ++ return retval; ++ ++ down_write(&ehci_cf_port_reset_rwsem); ++ retval = isp1760_hcd_set_and_wait(hcd, FLAG_CF, 250 * 1000); ++ up_write(&ehci_cf_port_reset_rwsem); ++ retval = 0; ++ if (retval) ++ return retval; ++ ++ return 0; ++} ++ ++static int isp1760_run(struct usb_hcd *hcd) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ int retval; ++ u32 chipid_h; ++ u32 chipid_l; ++ u32 chip_rev; ++ u32 ptd_atl_int; ++ u32 ptd_iso; ++ ++ /* ++ * ISP1763 have some differences in the setup and order to enable ++ * the ports, disable otg, setup buffers, and ATL, INT, ISO status. ++ * So, just handle it a separate sequence. ++ */ ++ if (priv->is_isp1763) ++ return isp1763_run(hcd); ++ ++ /* Set PTD interrupt AND & OR maps */ ++ isp1760_hcd_clear(hcd, HC_ATL_IRQ_MASK_AND); ++ isp1760_hcd_clear(hcd, HC_INT_IRQ_MASK_AND); ++ isp1760_hcd_clear(hcd, HC_ISO_IRQ_MASK_AND); ++ ++ isp1760_hcd_set(hcd, HC_ATL_IRQ_MASK_OR); ++ isp1760_hcd_set(hcd, HC_INT_IRQ_MASK_OR); ++ isp1760_hcd_set(hcd, HC_ISO_IRQ_MASK_OR); ++ ++ /* step 23 passed */ ++ ++ isp1760_hcd_set(hcd, HW_GLOBAL_INTR_EN); ++ ++ isp1760_hcd_clear(hcd, CMD_LRESET); ++ isp1760_hcd_clear(hcd, CMD_RESET); ++ ++ retval = isp1760_hcd_set_and_wait(hcd, CMD_RUN, 250 * 1000); ++ if (retval) ++ return retval; ++ ++ /* ++ * XXX ++ * Spec says to write FLAG_CF as last config action, priv code grabs ++ * the semaphore while doing so. ++ */ ++ down_write(&ehci_cf_port_reset_rwsem); ++ ++ retval = isp1760_hcd_set_and_wait(hcd, FLAG_CF, 250 * 1000); ++ up_write(&ehci_cf_port_reset_rwsem); ++ if (retval) ++ return retval; ++ ++ chipid_h = isp1760_hcd_read(hcd, HC_CHIP_ID_HIGH); ++ chipid_l = isp1760_hcd_read(hcd, HC_CHIP_ID_LOW); ++ chip_rev = isp1760_hcd_read(hcd, HC_CHIP_REV); ++ dev_info(priv->dev, "USB ISP %02x%02x HW rev. %d started\n", ++ chipid_h, chipid_l, chip_rev); ++ ++ /* PTD Register Init Part 2, Step 28 */ ++ ++ /* Setup registers controlling PTD checking */ ++ ptd_atl_int = 0x80000000; ++ ptd_iso = 0x00000001; ++ ++ isp1760_hcd_write(hcd, HC_ATL_PTD_LASTPTD, ptd_atl_int); ++ isp1760_hcd_write(hcd, HC_INT_PTD_LASTPTD, ptd_atl_int); ++ isp1760_hcd_write(hcd, HC_ISO_PTD_LASTPTD, ptd_iso); ++ ++ isp1760_hcd_set(hcd, HC_ATL_PTD_SKIPMAP); ++ isp1760_hcd_set(hcd, HC_INT_PTD_SKIPMAP); ++ isp1760_hcd_set(hcd, HC_ISO_PTD_SKIPMAP); ++ ++ isp1760_hcd_set(hcd, ATL_BUF_FILL); ++ isp1760_hcd_set(hcd, INT_BUF_FILL); ++ ++ /* GRR this is run-once init(), being done every time the HC starts. ++ * So long as they're part of class devices, we can't do it init() ++ * since the class device isn't created that early. ++ */ ++ return 0; ++} ++ ++static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len) ++{ ++ qtd->data_buffer = databuffer; ++ ++ qtd->length = len; ++ ++ return qtd->length; ++} ++ ++static void qtd_list_free(struct list_head *qtd_list) ++{ ++ struct isp1760_qtd *qtd, *qtd_next; ++ ++ list_for_each_entry_safe(qtd, qtd_next, qtd_list, qtd_list) { ++ list_del(&qtd->qtd_list); ++ qtd_free(qtd); ++ } ++} ++ ++/* ++ * Packetize urb->transfer_buffer into list of packets of size wMaxPacketSize. ++ * Also calculate the PID type (SETUP/IN/OUT) for each packet. ++ */ ++#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) ++static void packetize_urb(struct usb_hcd *hcd, ++ struct urb *urb, struct list_head *head, gfp_t flags) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ const struct isp1760_memory_layout *mem = priv->memory_layout; ++ struct isp1760_qtd *qtd; ++ void *buf; ++ int len, maxpacketsize; ++ u8 packet_type; ++ ++ /* ++ * URBs map to sequences of QTDs: one logical transaction ++ */ ++ ++ if (!urb->transfer_buffer && urb->transfer_buffer_length) { ++ /* XXX This looks like usb storage / SCSI bug */ ++ dev_err(priv->dev, "buf is null, dma is %08lx len is %d\n", ++ (long unsigned)urb->transfer_dma, ++ urb->transfer_buffer_length); ++ WARN_ON(1); ++ } ++ ++ if (usb_pipein(urb->pipe)) ++ packet_type = IN_PID; ++ else ++ packet_type = OUT_PID; ++ ++ if (usb_pipecontrol(urb->pipe)) { ++ qtd = qtd_alloc(flags, urb, SETUP_PID); ++ if (!qtd) ++ goto cleanup; ++ qtd_fill(qtd, urb->setup_packet, sizeof(struct usb_ctrlrequest)); ++ list_add_tail(&qtd->qtd_list, head); ++ ++ /* for zero length DATA stages, STATUS is always IN */ ++ if (urb->transfer_buffer_length == 0) ++ packet_type = IN_PID; ++ } ++ ++ maxpacketsize = max_packet(usb_maxpacket(urb->dev, urb->pipe)); ++ ++ /* ++ * buffer gets wrapped in one or more qtds; ++ * last one may be "short" (including zero len) ++ * and may serve as a control status ack ++ */ ++ buf = urb->transfer_buffer; ++ len = urb->transfer_buffer_length; ++ ++ for (;;) { ++ int this_qtd_len; ++ ++ qtd = qtd_alloc(flags, urb, packet_type); ++ if (!qtd) ++ goto cleanup; ++ ++ if (len > mem->blocks_size[ISP176x_BLOCK_NUM - 1]) ++ len = mem->blocks_size[ISP176x_BLOCK_NUM - 1]; ++ ++ this_qtd_len = qtd_fill(qtd, buf, len); ++ list_add_tail(&qtd->qtd_list, head); ++ ++ len -= this_qtd_len; ++ buf += this_qtd_len; ++ ++ if (len <= 0) ++ break; ++ } ++ ++ /* ++ * control requests may need a terminating data "status" ack; ++ * bulk ones may need a terminating short packet (zero length). ++ */ ++ if (urb->transfer_buffer_length != 0) { ++ int one_more = 0; ++ ++ if (usb_pipecontrol(urb->pipe)) { ++ one_more = 1; ++ if (packet_type == IN_PID) ++ packet_type = OUT_PID; ++ else ++ packet_type = IN_PID; ++ } else if (usb_pipebulk(urb->pipe) ++ && (urb->transfer_flags & URB_ZERO_PACKET) ++ && !(urb->transfer_buffer_length % ++ maxpacketsize)) { ++ one_more = 1; ++ } ++ if (one_more) { ++ qtd = qtd_alloc(flags, urb, packet_type); ++ if (!qtd) ++ goto cleanup; ++ ++ /* never any data in such packets */ ++ qtd_fill(qtd, NULL, 0); ++ list_add_tail(&qtd->qtd_list, head); ++ } ++ } ++ ++ return; ++ ++cleanup: ++ qtd_list_free(head); ++} ++ ++static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, ++ gfp_t mem_flags) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ struct isp1760_qh *qh = NULL; ++ struct list_head *ep_queue; ++ LIST_HEAD(new_qtds); ++ int qh_in_queue; ++ int retval; ++ int epnum; ++ ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_CONTROL: ++ ep_queue = &priv->qh_list[QH_CONTROL]; ++ break; ++ case PIPE_BULK: ++ ep_queue = &priv->qh_list[QH_BULK]; ++ break; ++ case PIPE_INTERRUPT: ++ ep_queue = &priv->qh_list[QH_INTERRUPT]; ++ break; ++ case PIPE_ISOCHRONOUS: ++ printf("isochronous USB packets not yet supported\n"); ++ return -EPIPE; ++ default: ++ printf("unknown pipe type\n"); ++ return -EPIPE; ++ } ++ ++ if (usb_pipein(urb->pipe)) ++ urb->actual_length = 0; ++ ++ packetize_urb(hcd, urb, &new_qtds, mem_flags); ++ if (list_empty(&new_qtds)) ++ return -ENOMEM; ++ ++ retval = usb_hcd_link_urb_to_ep(hcd, urb); ++ if (retval) { ++ qtd_list_free(&new_qtds); ++ goto out; ++ } ++ ++ epnum = usb_pipeendpoint(urb->pipe); ++ ++ qh_in_queue = 0; ++ list_for_each_entry(qh, ep_queue, qh_list) { ++ if (qh->epnum == epnum) { ++ qh_in_queue = 1; ++ break; ++ } ++ } ++ ++ if (!qh_in_queue) { ++ qh = qh_alloc(GFP_ATOMIC); ++ if (!qh) { ++ retval = -ENOMEM; ++ usb_hcd_unlink_urb_from_ep(hcd, urb); ++ qtd_list_free(&new_qtds); ++ goto out; ++ } ++ ++ qh->epnum = epnum; ++ list_add_tail(&qh->qh_list, ep_queue); ++ urb->ep->hcpriv = qh; ++ } ++ ++ list_splice_tail(&new_qtds, &qh->qtd_list); ++ schedule_ptds(hcd); ++ ++out: ++ return retval; ++} ++ ++static void kill_transfer(struct usb_hcd *hcd, struct urb *urb, ++ struct isp1760_qh *qh) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ int skip_map; ++ ++ WARN_ON(qh->slot == -1); ++ ++ /* We need to forcefully reclaim the slot since some transfers never ++ return, e.g. interrupt transfers and NAKed bulk transfers. */ ++ if (usb_pipecontrol(urb->pipe) || usb_pipebulk(urb->pipe)) { ++ skip_map = isp1760_hcd_read(hcd, HC_ATL_PTD_SKIPMAP); ++ skip_map |= (1 << qh->slot); ++ isp1760_hcd_write(hcd, HC_ATL_PTD_SKIPMAP, skip_map); ++ ndelay(100); ++ priv->atl_slots[qh->slot].qh = NULL; ++ priv->atl_slots[qh->slot].qtd = NULL; ++ } else { ++ skip_map = isp1760_hcd_read(hcd, HC_INT_PTD_SKIPMAP); ++ skip_map |= (1 << qh->slot); ++ isp1760_hcd_write(hcd, HC_INT_PTD_SKIPMAP, skip_map); ++ priv->int_slots[qh->slot].qh = NULL; ++ priv->int_slots[qh->slot].qtd = NULL; ++ } ++ ++ qh->slot = -1; ++} ++ ++/* ++ * Retire the qtds beginning at 'qtd' and belonging all to the same urb, killing ++ * any active transfer belonging to the urb in the process. ++ */ ++static void dequeue_urb_from_qtd(struct usb_hcd *hcd, struct isp1760_qh *qh, ++ struct isp1760_qtd *qtd) ++{ ++ struct urb *urb; ++ int urb_was_running; ++ ++ urb = qtd->urb; ++ urb_was_running = 0; ++ list_for_each_entry_from(qtd, &qh->qtd_list, qtd_list) { ++ if (qtd->urb != urb) ++ break; ++ ++ if (qtd->status >= QTD_XFER_STARTED) ++ urb_was_running = 1; ++ if (last_qtd_of_urb(qtd, qh) && ++ (qtd->status >= QTD_XFER_COMPLETE)) ++ urb_was_running = 0; ++ ++ if (qtd->status == QTD_XFER_STARTED) ++ kill_transfer(hcd, urb, qh); ++ qtd->status = QTD_RETIRE; ++ } ++} ++ ++int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ++{ ++ struct isp1760_qtd *qtd; ++ struct isp1760_qh *qh; ++ int retval = 0; ++ ++ retval = usb_hcd_check_unlink_urb(hcd, urb, status); ++ if (retval) ++ goto out; ++ ++ qh = urb->ep->hcpriv; ++ if (!qh) { ++ retval = -EINVAL; ++ goto out; ++ } ++ ++ list_for_each_entry(qtd, &qh->qtd_list, qtd_list) ++ if (qtd->urb == urb) { ++ dequeue_urb_from_qtd(hcd, qh, qtd); ++ list_move(&qtd->qtd_list, &qh->qtd_list); ++ break; ++ } ++ ++ urb->status = status; ++ schedule_ptds(hcd); ++ ++out: ++ return retval; ++} ++ ++static void isp1760_hub_descriptor(struct isp1760_hcd *priv, ++ struct usb_hub_descriptor *desc) ++{ ++ int ports; ++ u16 temp; ++ ++ ports = isp1760_hcd_n_ports(priv->hcd); ++ ++ desc->bDescriptorType = USB_DT_HUB; ++ /* priv 1.0, 2.3.9 says 20ms max */ ++ desc->bPwrOn2PwrGood = 10; ++ desc->bHubContrCurrent = 0; ++ ++ desc->bNbrPorts = ports; ++ temp = 1 + (ports / 8); ++ desc->bLength = 7 + 2 * temp; ++ ++ /* ports removable, and usb 1.0 legacy PortPwrCtrlMask */ ++ memset(&desc->u.hs.DeviceRemovable[0], 0, temp); ++ memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp); ++ ++ /* per-port overcurrent reporting */ ++ temp = HUB_CHAR_INDV_PORT_OCPM; ++ if (isp1760_hcd_ppc_is_set(priv->hcd)) ++ /* per-port power control */ ++ temp |= HUB_CHAR_INDV_PORT_LPSM; ++ else ++ /* no power switching */ ++ temp |= HUB_CHAR_NO_LPSM; ++ desc->wHubCharacteristics = cpu_to_le16(temp); ++} ++ ++#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E) ++ ++static void check_reset_complete(struct usb_hcd *hcd, int index) ++{ ++ if (!(isp1760_hcd_is_set(hcd, PORT_CONNECT))) ++ return; ++ ++ /* if reset finished and it's still not enabled -- handoff */ ++ if (!isp1760_hcd_is_set(hcd, PORT_PE)) { ++ printf("port %d full speed --> companion\n", index + 1); ++ ++ isp1760_hcd_set(hcd, PORT_OWNER); ++ ++ isp1760_hcd_clear(hcd, PORT_CSC); ++ } else { ++ printf("port %d high speed\n", index + 1); ++ } ++ ++ return; ++} ++ ++static int isp1760_hub_control(struct usb_hcd *hcd, struct usb_device *dev, ++ unsigned long pipe, void *buffer, int length, ++ struct devrequest *setup) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ u16 typeReq, wValue, wIndex; ++ unsigned long flags; ++ char *buf = buffer; ++ void *src = NULL; ++ int src_len = 0; ++ int retval = 0; ++ u32 status; ++ int ports; ++ ++ if (!setup) ++ return -EINVAL; ++ ++ ports = isp1760_hcd_n_ports(hcd); ++ ++ typeReq = setup->request | (setup->requesttype << 8); ++ wValue = le16_to_cpu(setup->value); ++ wIndex = le16_to_cpu(setup->index); ++ ++ /* ++ * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. ++ * HCS_INDICATOR may say we can change LEDs to off/amber/green. ++ * (track current state ourselves) ... blink for diagnostics, ++ * power, "this is the one", etc. EHCI spec supports this. ++ */ ++ ++ switch (typeReq) { ++ case DeviceOutRequest | USB_REQ_SET_ADDRESS: ++ break; ++ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: ++ /* Nothing to do */ ++ break; ++ case DeviceRequest | USB_REQ_GET_DESCRIPTOR: ++ switch (wValue & 0xff00) { ++ case USB_DT_DEVICE << 8: ++ src = &rh_descriptor.device; ++ src_len = 0x12; ++ break; ++ case USB_DT_CONFIG << 8: ++ src = &rh_descriptor.config; ++ src_len = 0x09; ++ break; ++ case USB_DT_STRING << 8: ++ switch (wValue & 0xff) { ++ case 0: /* Language */ ++ src = "\4\3\19\4"; ++ src_len = 4; ++ break; ++ case 1: /* Vendor String */ ++ src = "\16\3U\0-\0B\0o\0o\0t\0"; ++ src_len = 14; ++ break; ++ case 2: /* Product Name */ ++ src = "\52\3I\0S\0P\0-\0 " ++ "\0H\0o\0s\0t\0 " ++ "\0C\0o\0n\0t\0r\0o\0l\0l\0e\0r\0"; ++ src_len = 42; ++ break; ++ default: ++ goto error; ++ } ++ break; ++ } ++ break; ++ case ClearHubFeature: ++ switch (wValue) { ++ case C_HUB_LOCAL_POWER: ++ case C_HUB_OVER_CURRENT: ++ /* no hub-wide feature/status flags */ ++ break; ++ default: ++ goto error; ++ } ++ break; ++ case ClearPortFeature: ++ if (!wIndex || wIndex > ports) ++ goto error; ++ wIndex--; ++ ++ /* ++ * Even if OWNER is set, so the port is owned by the ++ * companion controller, hub_wq needs to be able to clear ++ * the port-change status bits (especially ++ * USB_PORT_STAT_C_CONNECTION). ++ */ ++ ++ switch (wValue) { ++ case USB_PORT_FEAT_ENABLE: ++ isp1760_hcd_clear(hcd, PORT_PE); ++ break; ++ case USB_PORT_FEAT_C_ENABLE: ++ /* XXX error? */ ++ break; ++ case USB_PORT_FEAT_SUSPEND: ++ if (isp1760_hcd_is_set(hcd, PORT_RESET)) ++ goto error; ++ ++ if (isp1760_hcd_is_set(hcd, PORT_SUSPEND)) { ++ if (!isp1760_hcd_is_set(hcd, PORT_PE)) ++ goto error; ++ /* resume signaling for 20 msec */ ++ isp1760_hcd_clear(hcd, PORT_CSC); ++ isp1760_hcd_set(hcd, PORT_RESUME); ++ ++ priv->reset_done = get_timer(0) + 40; ++ } ++ break; ++ case USB_PORT_FEAT_C_SUSPEND: ++ /* we auto-clear this feature */ ++ break; ++ case USB_PORT_FEAT_POWER: ++ if (isp1760_hcd_ppc_is_set(hcd)) ++ isp1760_hcd_clear(hcd, PORT_POWER); ++ break; ++ case USB_PORT_FEAT_C_CONNECTION: ++ isp1760_hcd_set(hcd, PORT_CSC); ++ break; ++ case USB_PORT_FEAT_C_OVER_CURRENT: ++ /* XXX error ?*/ ++ break; ++ case USB_PORT_FEAT_C_RESET: ++ /* GetPortStatus clears reset */ ++ break; ++ default: ++ goto error; ++ } ++ isp1760_hcd_read(hcd, CMD_RUN); ++ break; ++ case GetHubDescriptor: ++ isp1760_hub_descriptor(priv, (struct usb_hub_descriptor *) buf); ++ break; ++ case GetHubStatus: ++ /* no hub-wide feature/status flags */ ++ memset(buf, 0, 4); ++ break; ++ case GetPortStatus: ++ if (!wIndex || wIndex > ports) ++ goto error; ++ wIndex--; ++ status = 0; ++ ++ /* wPortChange bits */ ++ if (isp1760_hcd_is_set(hcd, PORT_CSC)) ++ status |= USB_PORT_STAT_C_CONNECTION << 16; ++ ++ /* whoever resumes must GetPortStatus to complete it!! */ ++ if (isp1760_hcd_is_set(hcd, PORT_RESUME)) { ++ status |= USB_PORT_STAT_C_SUSPEND << 16; ++ ++ if (!priv->reset_done) { ++ priv->reset_done = get_timer(0) + 20; ++ } else if (get_timer(0) > priv->reset_done) { ++ /* stop resume signaling */ ++ isp1760_hcd_clear(hcd, PORT_CSC); ++ ++ retval = isp1760_hcd_clear_and_wait(hcd, ++ PORT_RESUME, 2000); ++ if (retval != 0) { ++ printf("port %d resume error %d\n", ++ wIndex + 1, retval); ++ goto error; ++ } ++ } ++ } ++ ++ /* whoever resets must GetPortStatus to complete it!! */ ++ if (isp1760_hcd_is_set(hcd, PORT_RESET) && ++ get_timer(0) > priv->reset_done) { ++ status |= USB_PORT_STAT_C_RESET << 16; ++ priv->reset_done = 0; ++ ++ /* force reset to complete */ ++ /* REVISIT: some hardware needs 550+ usec to clear ++ * this bit; seems too long to spin routinely... ++ */ ++ retval = isp1760_hcd_clear_and_wait(hcd, PORT_RESET, ++ 750); ++ if (retval != 0) { ++ printf("port %d reset error %d\n", wIndex + 1, ++ retval); ++ goto error; ++ } ++ ++ /* see what we found out */ ++ check_reset_complete(hcd, wIndex); ++ } ++ /* ++ * Even if OWNER is set, there's no harm letting hub_wq ++ * see the wPortStatus values (they should all be 0 except ++ * for PORT_POWER anyway). ++ */ ++ ++ if (isp1760_hcd_is_set(hcd, PORT_OWNER)) ++ printf("PORT_OWNER is set\n"); ++ ++ if (isp1760_hcd_is_set(hcd, PORT_CONNECT)) { ++ status |= USB_PORT_STAT_CONNECTION; ++ ++ /* status may be from integrated TT */ ++ status |= USB_PORT_STAT_HIGH_SPEED; ++ } ++ if (isp1760_hcd_is_set(hcd, PORT_PE)) ++ status |= USB_PORT_STAT_ENABLE; ++ if (isp1760_hcd_is_set(hcd, PORT_SUSPEND) && ++ isp1760_hcd_is_set(hcd, PORT_RESUME)) ++ status |= USB_PORT_STAT_SUSPEND; ++ if (isp1760_hcd_is_set(hcd, PORT_RESET)) ++ status |= USB_PORT_STAT_RESET; ++ if (isp1760_hcd_is_set(hcd, PORT_POWER)) ++ status |= USB_PORT_STAT_POWER; ++ ++ put_unaligned(cpu_to_le32(status), (__le32 *) buf); ++ break; ++ case SetHubFeature: ++ switch (wValue) { ++ case C_HUB_LOCAL_POWER: ++ case C_HUB_OVER_CURRENT: ++ /* no hub-wide feature/status flags */ ++ break; ++ default: ++ goto error; ++ } ++ break; ++ case SetPortFeature: ++ wIndex &= 0xff; ++ if (!wIndex || wIndex > ports) ++ goto error; ++ wIndex--; ++ ++ if (isp1760_hcd_is_set(hcd, PORT_OWNER)) ++ break; ++ ++ switch (wValue) { ++ case USB_PORT_FEAT_ENABLE: ++ isp1760_hcd_set(hcd, PORT_PE); ++ break; ++ ++ case USB_PORT_FEAT_SUSPEND: ++ if (!isp1760_hcd_is_set(hcd, PORT_PE) || ++ isp1760_hcd_is_set(hcd, PORT_RESET)) ++ goto error; ++ ++ isp1760_hcd_set(hcd, PORT_SUSPEND); ++ break; ++ case USB_PORT_FEAT_POWER: ++ if (isp1760_hcd_ppc_is_set(hcd)) ++ isp1760_hcd_set(hcd, PORT_POWER); ++ break; ++ case USB_PORT_FEAT_RESET: ++ if (isp1760_hcd_is_set(hcd, PORT_RESUME)) ++ goto error; ++ /* line status bits may report this as low speed, ++ * which can be fine if this root hub has a ++ * transaction translator built in. ++ */ ++ if ((isp1760_hcd_is_set(hcd, PORT_CONNECT) && ++ !isp1760_hcd_is_set(hcd, PORT_PE)) && ++ (isp1760_hcd_read(hcd, PORT_LSTATUS) == 1)) { ++ isp1760_hcd_set(hcd, PORT_OWNER); ++ } else { ++ isp1760_hcd_set(hcd, PORT_RESET); ++ isp1760_hcd_clear(hcd, PORT_PE); ++ ++ priv->reset_done = get_timer(0) + 50; ++ } ++ break; ++ default: ++ goto error; ++ } ++ break; ++ ++ default: ++ printf("root: unknown request: 0x%0x\n", typeReq); ++ goto error; ++ } ++ spin_unlock_irqrestore(&priv->lock, flags); ++ ++ if (src_len) { ++ length = min(src_len, length); ++ ++ if (src != NULL && length > 0) ++ memcpy(buffer, src, length); ++ else ++ printf("zero copy USB descriptor\n"); ++ } ++ ++ dev->act_len = length; ++ dev->status = 0; ++ ++ return 0; ++ ++error: ++ /* "stall" on error */ ++ dev->act_len = 0; ++ dev->status = USB_ST_STALLED; ++ return -EPIPE; ++} ++ ++#ifndef __UBOOT__ ++static int isp1760_hub_status_data(struct usb_hcd *hcd, char *buf) ++{ ++ u32 status = 0; ++ int retval = 1; ++ ++ /* init status to no-changes */ ++ buf[0] = 0; ++ ++ if (isp1760_hcd_is_set(hcd, PORT_OWNER) && ++ isp1760_hcd_is_set(hcd, PORT_CSC)) { ++ isp1760_hcd_clear(hcd, PORT_CSC); ++ goto done; ++ } ++ ++done: ++ return status ? retval : 0; ++} ++ ++static void isp1760_endpoint_disable(struct usb_hcd *hcd, ++ struct usb_host_endpoint *ep) ++{ ++ struct isp1760_qh *qh, *qh_iter; ++ unsigned long spinflags; ++ int i; ++ ++ qh = ep->hcpriv; ++ if (!qh) ++ return; ++ ++ WARN_ON(!list_empty(&qh->qtd_list)); ++ ++ for (i = 0; i < QH_END; i++) ++ list_for_each_entry(qh_iter, &priv->qh_list[i], qh_list) ++ if (qh_iter == qh) { ++ list_del(&qh_iter->qh_list); ++ i = QH_END; ++ break; ++ } ++ qh_free(qh); ++ ep->hcpriv = NULL; ++ ++ schedule_ptds(hcd); ++} ++ ++static int isp1760_get_frame(struct usb_hcd *hcd) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ u32 fr; ++ ++ fr = isp1760_hcd_read(hcd, HC_FRINDEX); ++ return (fr >> 3) % priv->periodic_size; ++} ++ ++static void isp1760_stop(struct usb_hcd *hcd) ++{ ++ struct isp1760_hcd *priv = hcd_to_priv(hcd); ++ ++ msleep(20); ++ ++ spin_lock_irq(&priv->lock); ++ ehci_reset(hcd); ++ /* Disable IRQ */ ++ isp1760_hcd_clear(hcd, HW_GLOBAL_INTR_EN); ++ spin_unlock_irq(&priv->lock); ++ ++ isp1760_hcd_clear(hcd, FLAG_CF); ++} ++ ++static void isp1760_shutdown(struct usb_hcd *hcd) ++{ ++ isp1760_stop(hcd); ++ ++ isp1760_hcd_clear(hcd, HW_GLOBAL_INTR_EN); ++ ++ isp1760_hcd_clear(hcd, CMD_RUN); ++} ++ ++static void isp1760_clear_tt_buffer_complete(struct usb_hcd *hcd, ++ struct usb_host_endpoint *ep) ++{ ++ struct isp1760_qh *qh = ep->hcpriv; ++ unsigned long spinflags; ++ ++ if (!qh) ++ return; ++ ++ qh->tt_buffer_dirty = 0; ++ schedule_ptds(hcd); ++} ++ ++ ++static const struct hc_driver isp1760_hc_driver = { ++ .description = "isp1760-hcd", ++ .product_desc = "NXP ISP1760 USB Host Controller", ++ .hcd_priv_size = sizeof(struct isp1760_hcd *), ++ .irq = isp1760_irq, ++ .flags = HCD_MEMORY | HCD_USB2, ++ .reset = isp1760_hc_setup, ++ .start = isp1760_run, ++ .stop = isp1760_stop, ++ .shutdown = isp1760_shutdown, ++ .urb_enqueue = isp1760_urb_enqueue, ++ .urb_dequeue = isp1760_urb_dequeue, ++ .endpoint_disable = isp1760_endpoint_disable, ++ .get_frame_number = isp1760_get_frame, ++ .hub_status_data = isp1760_hub_status_data, ++ .hub_control = isp1760_hub_control, ++ .clear_tt_buffer_complete = isp1760_clear_tt_buffer_complete, ++}; ++#endif // __UBOOT__ ++ ++int __init isp1760_init_kmem_once(void) ++{ ++ urb_listitem_cachep = kmem_cache_create("isp1760_urb_listitem", ++ sizeof(struct urb_listitem), 0, SLAB_TEMPORARY | ++ SLAB_MEM_SPREAD, NULL); ++ ++ if (!urb_listitem_cachep) ++ return -ENOMEM; ++ ++ qtd_cachep = kmem_cache_create("isp1760_qtd", ++ sizeof(struct isp1760_qtd), 0, SLAB_TEMPORARY | ++ SLAB_MEM_SPREAD, NULL); ++ ++ if (!qtd_cachep) ++ return -ENOMEM; ++ ++ qh_cachep = kmem_cache_create("isp1760_qh", sizeof(struct isp1760_qh), ++ 0, SLAB_TEMPORARY | SLAB_MEM_SPREAD, NULL); ++ ++ if (!qh_cachep) { ++ kmem_cache_destroy(qtd_cachep); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++void isp1760_deinit_kmem_cache(void) ++{ ++ kmem_cache_destroy(qtd_cachep); ++ kmem_cache_destroy(qh_cachep); ++ kmem_cache_destroy(urb_listitem_cachep); ++} ++ ++int isp1760_hcd_lowlevel_init(struct isp1760_hcd *priv) ++{ ++ int ret; ++ ++ ret = isp1760_hc_setup(priv->hcd); ++ if (ret < 0) ++ return ret; ++ ++ ret = isp1760_run(priv->hcd); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static const struct usb_urb_ops isp1760_urb_ops = { ++ .urb_enqueue = isp1760_urb_enqueue, ++ .urb_dequeue = isp1760_urb_dequeue, ++ .hub_control = isp1760_hub_control, ++ .isr = isp1760_irq, ++}; ++ ++int isp1760_hcd_register(struct isp1760_hcd *priv, struct resource *mem, ++ int irq, unsigned long irqflags, ++ struct udevice *dev) ++{ ++ const struct isp1760_memory_layout *mem_layout = priv->memory_layout; ++ struct isp1760_host_data *host = dev_get_priv(dev); ++ struct usb_hcd *hcd = &host->hcd; ++ int ret; ++ ++ priv->hcd = hcd; ++ ++ hcd->hcd_priv = priv; ++ ++ priv->hcd = hcd; ++ ++ hcd->urb_ops = &isp1760_urb_ops; ++ ++ priv->atl_slots = kcalloc(mem_layout->slot_num, ++ sizeof(struct isp1760_slotinfo), GFP_KERNEL); ++ if (!priv->atl_slots) ++ return -ENOMEM; ++ ++ priv->int_slots = kcalloc(mem_layout->slot_num, ++ sizeof(struct isp1760_slotinfo), GFP_KERNEL); ++ if (!priv->int_slots) { ++ ret = -ENOMEM; ++ goto free_atl_slots; ++ } ++ ++ host->host_speed = USB_SPEED_HIGH; ++ ++ init_memory(priv); ++ ++ return 0; ++ ++free_atl_slots: ++ kfree(priv->atl_slots); ++ ++ return ret; ++} ++ ++void isp1760_hcd_unregister(struct isp1760_hcd *priv) ++{ ++ struct isp1760_qh *qh, *qh_next; ++ int i; ++ ++ for (i = 0; i < QH_END; i++) ++ list_for_each_entry_safe(qh, qh_next, &priv->qh_list[i], ++ qh_list) { ++ qtd_list_free(&qh->qtd_list); ++ list_del(&qh->qh_list); ++ qh_free(qh); ++ } ++ ++ kfree(priv->atl_slots); ++ kfree(priv->int_slots); ++} +diff --git a/drivers/usb/isp1760/isp1760-hcd.h b/drivers/usb/isp1760/isp1760-hcd.h +new file mode 100644 +index 000000000000..00f5ca8c1f75 +--- /dev/null ++++ b/drivers/usb/isp1760/isp1760-hcd.h +@@ -0,0 +1,82 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef _ISP1760_HCD_H_ ++#define _ISP1760_HCD_H_ ++ ++#include <regmap.h> ++ ++#include "isp1760-regs.h" ++ ++struct isp1760_qh; ++struct isp1760_qtd; ++struct resource; ++struct usb_hcd; ++ ++struct isp1760_slotinfo { ++ struct isp1760_qh *qh; ++ struct isp1760_qtd *qtd; ++ unsigned long timestamp; ++}; ++ ++/* chip memory management */ ++#define ISP176x_BLOCK_MAX (32 + 20 + 4) ++#define ISP176x_BLOCK_NUM 3 ++ ++struct isp1760_memory_layout { ++ unsigned int blocks[ISP176x_BLOCK_NUM]; ++ unsigned int blocks_size[ISP176x_BLOCK_NUM]; ++ ++ unsigned int slot_num; ++ unsigned int payload_blocks; ++ unsigned int payload_area_size; ++}; ++ ++struct isp1760_memory_chunk { ++ unsigned int start; ++ unsigned int size; ++ unsigned int free; ++}; ++ ++enum isp1760_queue_head_types { ++ QH_CONTROL, ++ QH_BULK, ++ QH_INTERRUPT, ++ QH_END ++}; ++ ++struct isp1760_hcd { ++ struct usb_hcd *hcd; ++ struct udevice *dev; ++ ++ void __iomem *base; ++ ++ struct regmap *regs; ++ struct regmap_field *fields[HC_FIELD_MAX]; ++ ++ bool is_isp1763; ++ const struct isp1760_memory_layout *memory_layout; ++ ++ spinlock_t lock; ++ struct isp1760_slotinfo *atl_slots; ++ int atl_done_map; ++ struct isp1760_slotinfo *int_slots; ++ int int_done_map; ++ struct isp1760_memory_chunk memory_pool[ISP176x_BLOCK_MAX]; ++ struct list_head qh_list[QH_END]; ++ ++ /* periodic schedule support */ ++#define DEFAULT_I_TDPS 1024 ++ unsigned periodic_size; ++ unsigned i_thresh; ++ unsigned long reset_done; ++ unsigned long next_statechange; ++}; ++ ++int isp1760_hcd_register(struct isp1760_hcd *priv, struct resource *mem, ++ int irq, unsigned long irqflags, struct udevice *dev); ++void isp1760_hcd_unregister(struct isp1760_hcd *priv); ++int isp1760_hcd_lowlevel_init(struct isp1760_hcd *priv); ++ ++int isp1760_init_kmem_once(void); ++void isp1760_deinit_kmem_cache(void); ++ ++#endif /* _ISP1760_HCD_H_ */ +diff --git a/drivers/usb/isp1760/isp1760-if.c b/drivers/usb/isp1760/isp1760-if.c +new file mode 100644 +index 000000000000..c610da6b23fb +--- /dev/null ++++ b/drivers/usb/isp1760/isp1760-if.c +@@ -0,0 +1,127 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright 2021 Linaro, Rui Miguel Silva <rui.silva@linaro.org> ++ * ++ * based on original code from: ++ * (c) 2007 Sebastian Siewior <bigeasy@linutronix.de> ++ */ ++ ++#include <common.h> ++#include <dm.h> ++#include <dm/device-internal.h> ++#include <dm/device_compat.h> ++#include <dm/devres.h> ++#include <dm/lists.h> ++#include <linux/bug.h> ++#include <linux/io.h> ++#include <linux/kernel.h> ++#include <linux/usb/otg.h> ++#include <log.h> ++#include <usb.h> ++ ++#include "isp1760-core.h" ++#include "isp1760-regs.h" ++#include "isp1760-uboot.h" ++ ++ ++static int isp1760_of_to_plat(struct udevice *dev) ++{ ++ struct isp1760_device *isp = dev_get_plat(dev); ++ unsigned int devflags = 0; ++ u32 bus_width = 0; ++ ofnode dp; ++ ++ ++ if (!dev_has_ofnode(dev)) { ++ /* select isp1763 as the default device */ ++ devflags = ISP1760_FLAG_ISP1763 | ISP1760_FLAG_BUS_WIDTH_16; ++ pr_err("isp1760: no platform data\n"); ++ goto isp_setup; ++ } ++ ++ dp = dev_ofnode(dev); ++ ++ if (ofnode_device_is_compatible(dp, "nxp,usb-isp1761")) ++ devflags |= ISP1760_FLAG_ISP1761; ++ ++ if (ofnode_device_is_compatible(dp, "nxp,usb-isp1763")) ++ devflags |= ISP1760_FLAG_ISP1763; ++ ++ /* ++ * Some systems wire up only 8 of 16 data lines or ++ * 16 of the 32 data lines ++ */ ++ bus_width = ofnode_read_u32_default(dp, "bus-width", 16); ++ if (bus_width == 16) ++ devflags |= ISP1760_FLAG_BUS_WIDTH_16; ++ else if (bus_width == 8) ++ devflags |= ISP1760_FLAG_BUS_WIDTH_8; ++ ++ if (usb_get_dr_mode(dev_ofnode(dev)) == USB_DR_MODE_PERIPHERAL) ++ devflags |= ISP1760_FLAG_PERIPHERAL_EN; ++ ++ if (ofnode_read_bool(dp, "analog-oc")) ++ devflags |= ISP1760_FLAG_ANALOG_OC; ++ ++ if (ofnode_read_bool(dp, "dack-polarity")) ++ devflags |= ISP1760_FLAG_DACK_POL_HIGH; ++ ++ if (ofnode_read_bool(dp, "dreq-polarity")) ++ devflags |= ISP1760_FLAG_DREQ_POL_HIGH; ++ ++isp_setup: ++ isp->devflags = devflags; ++ isp->dev = dev; ++ ++ return 0; ++} ++ ++static int isp1760_plat_probe(struct udevice *dev) ++{ ++ struct isp1760_device *isp = dev_get_plat(dev); ++ struct resource mem_res; ++ struct resource irq_res; ++ int ret; ++ ++ dev_read_resource(dev, 0, &mem_res); ++ dev_read_resource(dev, 1, &irq_res); ++ ++ isp1760_init_kmem_once(); ++ ++ ret = isp1760_register(isp, &mem_res, irq_res.start, irq_res.flags); ++ if (ret < 0) { ++ isp1760_deinit_kmem_cache(); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int isp1760_plat_remove(struct udevice *dev) ++{ ++ struct isp1760_device *isp = dev_get_plat(dev); ++ ++ isp1760_deinit_kmem_cache(); ++ isp1760_unregister(isp); ++ ++ return 0; ++} ++ ++static const struct udevice_id isp1760_ids[] = { ++ { .compatible = "nxp,usb-isp1760", }, ++ { .compatible = "nxp,usb-isp1761", }, ++ { .compatible = "nxp,usb-isp1763", }, ++ { }, ++}; ++ ++U_BOOT_DRIVER(isp1760) = { ++ .name = "isp1760", ++ .id = UCLASS_USB, ++ .of_match = isp1760_ids, ++ .of_to_plat = isp1760_of_to_plat, ++ .ops = &isp1760_usb_ops, ++ .probe = isp1760_plat_probe, ++ .remove = isp1760_plat_remove, ++ .plat_auto = sizeof(struct isp1760_device), ++ .priv_auto = sizeof(struct isp1760_host_data), ++}; +diff --git a/drivers/usb/isp1760/isp1760-regs.h b/drivers/usb/isp1760/isp1760-regs.h +new file mode 100644 +index 000000000000..94ea60c20b2a +--- /dev/null ++++ b/drivers/usb/isp1760/isp1760-regs.h +@@ -0,0 +1,292 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Driver for the NXP ISP1760 chip ++ * ++ * Copyright 2021 Linaro, Rui Miguel Silva ++ * Copyright 2014 Laurent Pinchart ++ * Copyright 2007 Sebastian Siewior ++ * ++ * Contacts: ++ * Sebastian Siewior <bigeasy@linutronix.de> ++ * Laurent Pinchart <laurent.pinchart@ideasonboard.com> ++ * Rui Miguel Silva <rui.silva@linaro.org> ++ */ ++ ++#ifndef _ISP176x_REGS_H_ ++#define _ISP176x_REGS_H_ ++ ++/* ----------------------------------------------------------------------------- ++ * Host Controller ++ */ ++ ++/* ISP1760/31 */ ++/* EHCI capability registers */ ++#define ISP176x_HC_VERSION 0x002 ++#define ISP176x_HC_HCSPARAMS 0x004 ++#define ISP176x_HC_HCCPARAMS 0x008 ++ ++/* EHCI operational registers */ ++#define ISP176x_HC_USBCMD 0x020 ++#define ISP176x_HC_USBSTS 0x024 ++#define ISP176x_HC_FRINDEX 0x02c ++ ++#define ISP176x_HC_CONFIGFLAG 0x060 ++#define ISP176x_HC_PORTSC1 0x064 ++ ++#define ISP176x_HC_ISO_PTD_DONEMAP 0x130 ++#define ISP176x_HC_ISO_PTD_SKIPMAP 0x134 ++#define ISP176x_HC_ISO_PTD_LASTPTD 0x138 ++#define ISP176x_HC_INT_PTD_DONEMAP 0x140 ++#define ISP176x_HC_INT_PTD_SKIPMAP 0x144 ++#define ISP176x_HC_INT_PTD_LASTPTD 0x148 ++#define ISP176x_HC_ATL_PTD_DONEMAP 0x150 ++#define ISP176x_HC_ATL_PTD_SKIPMAP 0x154 ++#define ISP176x_HC_ATL_PTD_LASTPTD 0x158 ++ ++/* Configuration Register */ ++#define ISP176x_HC_HW_MODE_CTRL 0x300 ++#define ISP176x_HC_CHIP_ID 0x304 ++#define ISP176x_HC_SCRATCH 0x308 ++#define ISP176x_HC_RESET 0x30c ++#define ISP176x_HC_BUFFER_STATUS 0x334 ++#define ISP176x_HC_MEMORY 0x33c ++ ++/* Interrupt Register */ ++#define ISP176x_HC_INTERRUPT 0x310 ++#define ISP176x_HC_INTERRUPT_ENABLE 0x314 ++#define ISP176x_HC_ISO_IRQ_MASK_OR 0x318 ++#define ISP176x_HC_INT_IRQ_MASK_OR 0x31c ++#define ISP176x_HC_ATL_IRQ_MASK_OR 0x320 ++#define ISP176x_HC_ISO_IRQ_MASK_AND 0x324 ++#define ISP176x_HC_INT_IRQ_MASK_AND 0x328 ++#define ISP176x_HC_ATL_IRQ_MASK_AND 0x32c ++ ++#define ISP176x_HC_OTG_CTRL_SET 0x374 ++#define ISP176x_HC_OTG_CTRL_CLEAR 0x376 ++ ++enum isp176x_host_controller_fields { ++ /* HC_PORTSC1 */ ++ PORT_OWNER, PORT_POWER, PORT_LSTATUS, PORT_RESET, PORT_SUSPEND, ++ PORT_RESUME, PORT_PE, PORT_CSC, PORT_CONNECT, ++ /* HC_HCSPARAMS */ ++ HCS_PPC, HCS_N_PORTS, ++ /* HC_HCCPARAMS */ ++ HCC_ISOC_CACHE, HCC_ISOC_THRES, ++ /* HC_USBCMD */ ++ CMD_LRESET, CMD_RESET, CMD_RUN, ++ /* HC_USBSTS */ ++ STS_PCD, ++ /* HC_FRINDEX */ ++ HC_FRINDEX, ++ /* HC_CONFIGFLAG */ ++ FLAG_CF, ++ /* ISO/INT/ATL PTD */ ++ HC_ISO_PTD_DONEMAP, HC_ISO_PTD_SKIPMAP, HC_ISO_PTD_LASTPTD, ++ HC_INT_PTD_DONEMAP, HC_INT_PTD_SKIPMAP, HC_INT_PTD_LASTPTD, ++ HC_ATL_PTD_DONEMAP, HC_ATL_PTD_SKIPMAP, HC_ATL_PTD_LASTPTD, ++ /* HC_HW_MODE_CTRL */ ++ ALL_ATX_RESET, HW_ANA_DIGI_OC, HW_DEV_DMA, HW_COMN_IRQ, HW_COMN_DMA, ++ HW_DATA_BUS_WIDTH, HW_DACK_POL_HIGH, HW_DREQ_POL_HIGH, HW_INTR_HIGH_ACT, ++ HW_INTF_LOCK, HW_INTR_EDGE_TRIG, HW_GLOBAL_INTR_EN, ++ /* HC_CHIP_ID */ ++ HC_CHIP_ID_HIGH, HC_CHIP_ID_LOW, HC_CHIP_REV, ++ /* HC_SCRATCH */ ++ HC_SCRATCH, ++ /* HC_RESET */ ++ SW_RESET_RESET_ATX, SW_RESET_RESET_HC, SW_RESET_RESET_ALL, ++ /* HC_BUFFER_STATUS */ ++ ISO_BUF_FILL, INT_BUF_FILL, ATL_BUF_FILL, ++ /* HC_MEMORY */ ++ MEM_BANK_SEL, MEM_START_ADDR, ++ /* HC_DATA */ ++ HC_DATA, ++ /* HC_INTERRUPT */ ++ HC_INTERRUPT, ++ /* HC_INTERRUPT_ENABLE */ ++ HC_INT_IRQ_ENABLE, HC_ATL_IRQ_ENABLE, ++ /* INTERRUPT MASKS */ ++ HC_ISO_IRQ_MASK_OR, HC_INT_IRQ_MASK_OR, HC_ATL_IRQ_MASK_OR, ++ HC_ISO_IRQ_MASK_AND, HC_INT_IRQ_MASK_AND, HC_ATL_IRQ_MASK_AND, ++ /* HW_OTG_CTRL_SET */ ++ HW_OTG_DISABLE, HW_SW_SEL_HC_DC, HW_VBUS_DRV, HW_SEL_CP_EXT, ++ HW_DM_PULLDOWN, HW_DP_PULLDOWN, HW_DP_PULLUP, HW_HC_2_DIS, ++ /* HW_OTG_CTRL_CLR */ ++ HW_OTG_DISABLE_CLEAR, HW_SW_SEL_HC_DC_CLEAR, HW_VBUS_DRV_CLEAR, ++ HW_SEL_CP_EXT_CLEAR, HW_DM_PULLDOWN_CLEAR, HW_DP_PULLDOWN_CLEAR, ++ HW_DP_PULLUP_CLEAR, HW_HC_2_DIS_CLEAR, ++ /* Last element */ ++ HC_FIELD_MAX, ++}; ++ ++/* ISP1763 */ ++/* EHCI operational registers */ ++#define ISP1763_HC_USBCMD 0x8c ++#define ISP1763_HC_USBSTS 0x90 ++#define ISP1763_HC_FRINDEX 0x98 ++ ++#define ISP1763_HC_CONFIGFLAG 0x9c ++#define ISP1763_HC_PORTSC1 0xa0 ++ ++#define ISP1763_HC_ISO_PTD_DONEMAP 0xa4 ++#define ISP1763_HC_ISO_PTD_SKIPMAP 0xa6 ++#define ISP1763_HC_ISO_PTD_LASTPTD 0xa8 ++#define ISP1763_HC_INT_PTD_DONEMAP 0xaa ++#define ISP1763_HC_INT_PTD_SKIPMAP 0xac ++#define ISP1763_HC_INT_PTD_LASTPTD 0xae ++#define ISP1763_HC_ATL_PTD_DONEMAP 0xb0 ++#define ISP1763_HC_ATL_PTD_SKIPMAP 0xb2 ++#define ISP1763_HC_ATL_PTD_LASTPTD 0xb4 ++ ++/* Configuration Register */ ++#define ISP1763_HC_HW_MODE_CTRL 0xb6 ++#define ISP1763_HC_CHIP_REV 0x70 ++#define ISP1763_HC_CHIP_ID 0x72 ++#define ISP1763_HC_SCRATCH 0x78 ++#define ISP1763_HC_RESET 0xb8 ++#define ISP1763_HC_BUFFER_STATUS 0xba ++#define ISP1763_HC_MEMORY 0xc4 ++#define ISP1763_HC_DATA 0xc6 ++ ++/* Interrupt Register */ ++#define ISP1763_HC_INTERRUPT 0xd4 ++#define ISP1763_HC_INTERRUPT_ENABLE 0xd6 ++#define ISP1763_HC_ISO_IRQ_MASK_OR 0xd8 ++#define ISP1763_HC_INT_IRQ_MASK_OR 0xda ++#define ISP1763_HC_ATL_IRQ_MASK_OR 0xdc ++#define ISP1763_HC_ISO_IRQ_MASK_AND 0xde ++#define ISP1763_HC_INT_IRQ_MASK_AND 0xe0 ++#define ISP1763_HC_ATL_IRQ_MASK_AND 0xe2 ++ ++#define ISP1763_HC_OTG_CTRL_SET 0xe4 ++#define ISP1763_HC_OTG_CTRL_CLEAR 0xe6 ++ ++/* ----------------------------------------------------------------------------- ++ * Peripheral Controller ++ */ ++ ++#define DC_IEPTX(n) (1 << (11 + 2 * (n))) ++#define DC_IEPRX(n) (1 << (10 + 2 * (n))) ++#define DC_IEPRXTX(n) (3 << (10 + 2 * (n))) ++ ++#define ISP176x_DC_CDBGMOD_ACK BIT(6) ++#define ISP176x_DC_DDBGMODIN_ACK BIT(4) ++#define ISP176x_DC_DDBGMODOUT_ACK BIT(2) ++ ++#define ISP176x_DC_IEP0SETUP BIT(8) ++#define ISP176x_DC_IEVBUS BIT(7) ++#define ISP176x_DC_IEHS_STA BIT(5) ++#define ISP176x_DC_IERESM BIT(4) ++#define ISP176x_DC_IESUSP BIT(3) ++#define ISP176x_DC_IEBRST BIT(0) ++ ++#define ISP176x_DC_ENDPTYP_ISOC 0x01 ++#define ISP176x_DC_ENDPTYP_BULK 0x02 ++#define ISP176x_DC_ENDPTYP_INTERRUPT 0x03 ++ ++/* Initialization Registers */ ++#define ISP176x_DC_ADDRESS 0x0200 ++#define ISP176x_DC_MODE 0x020c ++#define ISP176x_DC_INTCONF 0x0210 ++#define ISP176x_DC_DEBUG 0x0212 ++#define ISP176x_DC_INTENABLE 0x0214 ++ ++/* Data Flow Registers */ ++#define ISP176x_DC_EPMAXPKTSZ 0x0204 ++#define ISP176x_DC_EPTYPE 0x0208 ++ ++#define ISP176x_DC_BUFLEN 0x021c ++#define ISP176x_DC_BUFSTAT 0x021e ++#define ISP176x_DC_DATAPORT 0x0220 ++ ++#define ISP176x_DC_CTRLFUNC 0x0228 ++#define ISP176x_DC_EPINDEX 0x022c ++ ++/* DMA Registers */ ++#define ISP176x_DC_DMACMD 0x0230 ++#define ISP176x_DC_DMATXCOUNT 0x0234 ++#define ISP176x_DC_DMACONF 0x0238 ++#define ISP176x_DC_DMAHW 0x023c ++#define ISP176x_DC_DMAINTREASON 0x0250 ++#define ISP176x_DC_DMAINTEN 0x0254 ++#define ISP176x_DC_DMAEP 0x0258 ++#define ISP176x_DC_DMABURSTCOUNT 0x0264 ++ ++/* General Registers */ ++#define ISP176x_DC_INTERRUPT 0x0218 ++#define ISP176x_DC_CHIPID 0x0270 ++#define ISP176x_DC_FRAMENUM 0x0274 ++#define ISP176x_DC_SCRATCH 0x0278 ++#define ISP176x_DC_UNLOCKDEV 0x027c ++#define ISP176x_DC_INTPULSEWIDTH 0x0280 ++#define ISP176x_DC_TESTMODE 0x0284 ++ ++enum isp176x_device_controller_fields { ++ /* DC_ADDRESS */ ++ DC_DEVEN, DC_DEVADDR, ++ /* DC_MODE */ ++ DC_VBUSSTAT, DC_SFRESET, DC_GLINTENA, ++ /* DC_INTCONF */ ++ DC_CDBGMOD_ACK, DC_DDBGMODIN_ACK, DC_DDBGMODOUT_ACK, DC_INTPOL, ++ /* DC_INTENABLE */ ++ DC_IEPRXTX_7, DC_IEPRXTX_6, DC_IEPRXTX_5, DC_IEPRXTX_4, DC_IEPRXTX_3, ++ DC_IEPRXTX_2, DC_IEPRXTX_1, DC_IEPRXTX_0, ++ DC_IEP0SETUP, DC_IEVBUS, DC_IEHS_STA, DC_IERESM, DC_IESUSP, DC_IEBRST, ++ /* DC_EPINDEX */ ++ DC_EP0SETUP, DC_ENDPIDX, DC_EPDIR, ++ /* DC_CTRLFUNC */ ++ DC_CLBUF, DC_VENDP, DC_DSEN, DC_STATUS, DC_STALL, ++ /* DC_BUFLEN */ ++ DC_BUFLEN, ++ /* DC_EPMAXPKTSZ */ ++ DC_FFOSZ, ++ /* DC_EPTYPE */ ++ DC_EPENABLE, DC_ENDPTYP, ++ /* DC_FRAMENUM */ ++ DC_FRAMENUM, DC_UFRAMENUM, ++ /* DC_CHIP_ID */ ++ DC_CHIP_ID_HIGH, DC_CHIP_ID_LOW, ++ /* DC_SCRATCH */ ++ DC_SCRATCH, ++ /* Last element */ ++ DC_FIELD_MAX, ++}; ++ ++/* ISP1763 */ ++/* Initialization Registers */ ++#define ISP1763_DC_ADDRESS 0x00 ++#define ISP1763_DC_MODE 0x0c ++#define ISP1763_DC_INTCONF 0x10 ++#define ISP1763_DC_INTENABLE 0x14 ++ ++/* Data Flow Registers */ ++#define ISP1763_DC_EPMAXPKTSZ 0x04 ++#define ISP1763_DC_EPTYPE 0x08 ++ ++#define ISP1763_DC_BUFLEN 0x1c ++#define ISP1763_DC_BUFSTAT 0x1e ++#define ISP1763_DC_DATAPORT 0x20 ++ ++#define ISP1763_DC_CTRLFUNC 0x28 ++#define ISP1763_DC_EPINDEX 0x2c ++ ++/* DMA Registers */ ++#define ISP1763_DC_DMACMD 0x30 ++#define ISP1763_DC_DMATXCOUNT 0x34 ++#define ISP1763_DC_DMACONF 0x38 ++#define ISP1763_DC_DMAHW 0x3c ++#define ISP1763_DC_DMAINTREASON 0x50 ++#define ISP1763_DC_DMAINTEN 0x54 ++#define ISP1763_DC_DMAEP 0x58 ++#define ISP1763_DC_DMABURSTCOUNT 0x64 ++ ++/* General Registers */ ++#define ISP1763_DC_INTERRUPT 0x18 ++#define ISP1763_DC_CHIPID_LOW 0x70 ++#define ISP1763_DC_CHIPID_HIGH 0x72 ++#define ISP1763_DC_FRAMENUM 0x74 ++#define ISP1763_DC_SCRATCH 0x78 ++#define ISP1763_DC_UNLOCKDEV 0x7c ++#define ISP1763_DC_INTPULSEWIDTH 0x80 ++#define ISP1763_DC_TESTMODE 0x84 ++ ++#endif +diff --git a/drivers/usb/isp1760/isp1760-uboot.c b/drivers/usb/isp1760/isp1760-uboot.c +new file mode 100644 +index 000000000000..7635210fe2b4 +--- /dev/null ++++ b/drivers/usb/isp1760/isp1760-uboot.c +@@ -0,0 +1,76 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Driver for the NXP ISP1760 chip ++ * ++ * Copyright 2021 Linaro, Rui Miguel Silva <rui.silva@linaro.org> ++ * ++ */ ++ ++#include <common.h> ++#include <dm.h> ++#include <dm/device-internal.h> ++#include <dm/device_compat.h> ++#include <dm/devres.h> ++#include <dm/lists.h> ++#include <linux/bug.h> ++#include <linux/io.h> ++#include <linux/kernel.h> ++#include <linux/usb/otg.h> ++#include <linux/usb/usb_urb_compat.h> ++#include <log.h> ++#include <usb.h> ++ ++#include "isp1760-core.h" ++#include "isp1760-hcd.h" ++#include "isp1760-regs.h" ++#include "isp1760-uboot.h" ++ ++static int isp1760_msg_submit_control(struct udevice *dev, ++ struct usb_device *udev, ++ unsigned long pipe, void *buffer, ++ int length, struct devrequest *setup) ++{ ++ struct isp1760_host_data *host = dev_get_priv(dev); ++ ++ return usb_urb_submit_control(&host->hcd, &host->urb, &host->hep, udev, ++ pipe, buffer, length, setup, 0, ++ host->host_speed); ++} ++ ++static int isp1760_msg_submit_bulk(struct udevice *dev, struct usb_device *udev, ++ unsigned long pipe, void *buffer, int length) ++{ ++ struct isp1760_host_data *host = dev_get_priv(dev); ++ ++ return usb_urb_submit_bulk(&host->hcd, &host->urb, &host->hep, udev, ++ pipe, buffer, length); ++} ++ ++static int isp1760_msg_submit_irq(struct udevice *dev, struct usb_device *udev, ++ unsigned long pipe, void *buffer, int length, ++ int interval, bool nonblock) ++{ ++ struct isp1760_host_data *host = dev_get_priv(dev); ++ ++ return usb_urb_submit_irq(&host->hcd, &host->urb, &host->hep, udev, ++ pipe, buffer, length, interval); ++} ++ ++static int isp1760_get_max_xfer_size(struct udevice *dev, size_t *size) ++{ ++ struct isp1760_host_data *host = dev_get_priv(dev); ++ struct isp1760_hcd *priv = host->hcd.hcd_priv; ++ const struct isp1760_memory_layout *mem = priv->memory_layout; ++ ++ *size = mem->blocks_size[ISP176x_BLOCK_NUM - 1]; ++ ++ return 0; ++} ++ ++ ++struct dm_usb_ops isp1760_usb_ops = { ++ .control = isp1760_msg_submit_control, ++ .bulk = isp1760_msg_submit_bulk, ++ .interrupt = isp1760_msg_submit_irq, ++ .get_max_xfer_size = isp1760_get_max_xfer_size, ++}; +diff --git a/drivers/usb/isp1760/isp1760-uboot.h b/drivers/usb/isp1760/isp1760-uboot.h +new file mode 100644 +index 000000000000..2486de6f9e27 +--- /dev/null ++++ b/drivers/usb/isp1760/isp1760-uboot.h +@@ -0,0 +1,27 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Driver for the NXP ISP1760 chip ++ * ++ * Copyright 2021 Linaro, Rui Miguel Silva <rui.silva@linaro.org> ++ * ++ */ ++ ++#ifndef __ISP1760_UBOOT_H__ ++#define __ISP1760_UBOOT_H__ ++ ++#include <linux/usb/usb_urb_compat.h> ++#include <usb.h> ++ ++#include "isp1760-core.h" ++ ++struct isp1760_host_data { ++ struct isp1760_hcd *priv; ++ struct usb_hcd hcd; ++ enum usb_device_speed host_speed; ++ struct usb_host_endpoint hep; ++ struct urb urb; ++}; ++ ++extern struct dm_usb_ops isp1760_usb_ops; ++ ++#endif +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0005-corstone1000-enable-isp1763-usb-controller.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0005-corstone1000-enable-isp1763-usb-controller.patch new file mode 100644 index 0000000000..2ce03b6401 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0005-corstone1000-enable-isp1763-usb-controller.patch @@ -0,0 +1,48 @@ +From b7fb62e512e00a5fdbb4321bb0b4109261518481 Mon Sep 17 00:00:00 2001 +From: Rui Miguel Silva <rui.silva@linaro.org> +Date: Thu, 3 Mar 2022 16:52:02 +0000 +Subject: [PATCH 05/27] corstone1000: enable isp1763 usb controller + +MPS3 board have a ISP1763 usb controller, add the +correspondent mmio area and enable it to be used for mass +storage access for example. + +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + configs/corstone1000_defconfig | 1 + + include/configs/corstone1000.h | 6 ++++++ + 2 files changed, 7 insertions(+) + +diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig +index 02f931b0d469..e573fe6fe6a2 100644 +--- a/configs/corstone1000_defconfig ++++ b/configs/corstone1000_defconfig +@@ -42,6 +42,7 @@ CONFIG_REGMAP=y + CONFIG_DM_SERIAL=y + CONFIG_USB=y + CONFIG_DM_USB=y ++CONFIG_USB_ISP1760=y + CONFIG_USB_STORAGE=y + CONFIG_EFI_MM_COMM_TEE=y + # CONFIG_OPTEE is not set +diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h +index cf166f107efd..8ba0effb0ab2 100644 +--- a/include/configs/corstone1000.h ++++ b/include/configs/corstone1000.h +@@ -55,7 +55,13 @@ + #define CONFIG_SYS_CBSIZE 512 /* Console I/O Buffer Size */ + #define CONFIG_SYS_MAXARGS 64 /* max command args */ + ++#define BOOT_TARGET_DEVICES(func) \ ++ func(USB, usb, 0) ++ ++#include <config_distro_bootcmd.h> ++ + #define CONFIG_EXTRA_ENV_SETTINGS \ ++ BOOTENV \ + "usb_pgood_delay=250\0" \ + "boot_bank_flag=0x08002000\0" \ + "kernel_addr_bank_0=0x083EE000\0" \ +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0006-arm_ffa-introducing-Arm-FF-A-low-level-driver.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0006-arm_ffa-introducing-Arm-FF-A-low-level-driver.patch new file mode 100644 index 0000000000..8cc848fec3 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0006-arm_ffa-introducing-Arm-FF-A-low-level-driver.patch @@ -0,0 +1,2610 @@ +From ede21dc1ca75132698cc81e88357c5789ccf67c7 Mon Sep 17 00:00:00 2001 +From: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> +Date: Tue, 16 Nov 2021 12:34:52 +0000 +Subject: [PATCH 06/27] arm_ffa: introducing Arm FF-A low-level driver + +This driver implements Arm Firmware Framework for Armv8-A on u-boot + +The Firmware Framework for Arm A-profile processors (FF-A) +describes interfaces (ABIs) that standardize communication +between the Secure World and Normal World leveraging TrustZone +technology. + +This driver is based on FF-A specification v1.0 and uses SMC32 +calling convention. + +FF-A specification: + +https://developer.arm.com/documentation/den0077/a/?lang=en + +The driver provides helper FF-A interfaces for user layers. +These helper functions allow clients to pass data and select the +FF-A function to use for the communication with secure world. + +Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + MAINTAINERS | 8 + + arch/arm/cpu/armv8/smccc-call.S | 27 + + arch/arm/lib/asm-offsets.c | 6 + + common/board_r.c | 6 + + drivers/Kconfig | 2 + + drivers/Makefile | 1 + + drivers/arm-ffa/Kconfig | 26 + + drivers/arm-ffa/Makefile | 3 + + drivers/arm-ffa/arm-ffa-uclass.c | 67 ++ + drivers/arm-ffa/arm_ffa_prv.h | 199 ++++ + drivers/arm-ffa/core.c | 1484 ++++++++++++++++++++++++++++++ + include/arm_ffa.h | 191 ++++ + include/arm_ffa_helper.h | 45 + + include/dm/uclass-id.h | 1 + + include/linux/arm-smccc.h | 28 +- + lib/Kconfig | 1 + + lib/Makefile | 1 + + lib/arm-ffa/Kconfig | 6 + + lib/arm-ffa/Makefile | 8 + + lib/arm-ffa/arm_ffa_helper.c | 188 ++++ + lib/efi_loader/efi_boottime.c | 17 + + 21 files changed, 2314 insertions(+), 1 deletion(-) + create mode 100644 drivers/arm-ffa/Kconfig + create mode 100644 drivers/arm-ffa/Makefile + create mode 100644 drivers/arm-ffa/arm-ffa-uclass.c + create mode 100644 drivers/arm-ffa/arm_ffa_prv.h + create mode 100644 drivers/arm-ffa/core.c + create mode 100644 include/arm_ffa.h + create mode 100644 include/arm_ffa_helper.h + create mode 100644 lib/arm-ffa/Kconfig + create mode 100644 lib/arm-ffa/Makefile + create mode 100644 lib/arm-ffa/arm_ffa_helper.c + +diff --git a/MAINTAINERS b/MAINTAINERS +index 96582fc67777..14307e6da644 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -232,6 +232,14 @@ F: board/CZ.NIC/ + F: configs/turris_*_defconfig + F: include/configs/turris_*.h + ++ARM FF-A ++M: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> ++S: Maintained ++F: drivers/arm-ffa/ ++F: include/arm_ffa.h ++F: include/arm_ffa_helper.h ++F: lib/arm-ffa/ ++ + ARM FREESCALE IMX + M: Stefano Babic <sbabic@denx.de> + M: Fabio Estevam <festevam@gmail.com> +diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S +index dc92b28777c3..ffc39c9fefa2 100644 +--- a/arch/arm/cpu/armv8/smccc-call.S ++++ b/arch/arm/cpu/armv8/smccc-call.S +@@ -1,6 +1,8 @@ + /* SPDX-License-Identifier: GPL-2.0 */ + /* + * Copyright (c) 2015, Linaro Limited ++ * (C) Copyright 2021 ARM Limited ++ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> + */ + #include <linux/linkage.h> + #include <linux/arm-smccc.h> +@@ -45,3 +47,28 @@ ENDPROC(__arm_smccc_smc) + ENTRY(__arm_smccc_hvc) + SMCCC hvc + ENDPROC(__arm_smccc_hvc) ++ ++#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) ++ ++ .macro FFASMCCC instr ++ .cfi_startproc ++ \instr #0 ++ ldr x9, [sp] ++ stp x0, x1, [x9, #ARM_SMCCC_RES_X0_OFFS] ++ stp x2, x3, [x9, #ARM_SMCCC_RES_X2_OFFS] ++ stp x4, x5, [x9, #ARM_SMCCC_RES_X4_OFFS] ++ stp x6, x7, [x9, #ARM_SMCCC_RES_X6_OFFS] ++ ret ++ .cfi_endproc ++ .endm ++ ++/* ++ * void arm_ffa_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2, ++ * unsigned long a3, unsigned long a4, unsigned long a5, ++ * unsigned long a6, unsigned long a7, struct arm_smccc_res *res) ++ */ ++ENTRY(__arm_ffa_smccc_smc) ++ FFASMCCC smc ++ENDPROC(__arm_ffa_smccc_smc) ++ ++#endif +diff --git a/arch/arm/lib/asm-offsets.c b/arch/arm/lib/asm-offsets.c +index 22fd541f9a28..45eca83a473c 100644 +--- a/arch/arm/lib/asm-offsets.c ++++ b/arch/arm/lib/asm-offsets.c +@@ -9,6 +9,8 @@ + * generate asm statements containing #defines, + * compile this file to assembler, and then extract the + * #defines from the assembly-language output. ++ * ++ * (C) Copyright 2021 ARM Limited + */ + + #include <common.h> +@@ -115,6 +117,10 @@ int main(void) + #ifdef CONFIG_ARM_SMCCC + DEFINE(ARM_SMCCC_RES_X0_OFFS, offsetof(struct arm_smccc_res, a0)); + DEFINE(ARM_SMCCC_RES_X2_OFFS, offsetof(struct arm_smccc_res, a2)); ++#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) ++ DEFINE(ARM_SMCCC_RES_X4_OFFS, offsetof(struct arm_smccc_res, a4)); ++ DEFINE(ARM_SMCCC_RES_X6_OFFS, offsetof(struct arm_smccc_res, a6)); ++#endif + DEFINE(ARM_SMCCC_QUIRK_ID_OFFS, offsetof(struct arm_smccc_quirk, id)); + DEFINE(ARM_SMCCC_QUIRK_STATE_OFFS, offsetof(struct arm_smccc_quirk, state)); + #endif +diff --git a/common/board_r.c b/common/board_r.c +index c24d9b4e220b..af20f38b104c 100644 +--- a/common/board_r.c ++++ b/common/board_r.c +@@ -61,6 +61,9 @@ + #include <wdt.h> + #include <asm-generic/gpio.h> + #include <efi_loader.h> ++#ifdef CONFIG_ARM_FFA_TRANSPORT ++#include <arm_ffa_helper.h> ++#endif + + DECLARE_GLOBAL_DATA_PTR; + +@@ -770,6 +773,9 @@ static init_fnc_t init_sequence_r[] = { + INIT_FUNC_WATCHDOG_RESET + initr_net, + #endif ++#ifdef CONFIG_ARM_FFA_TRANSPORT ++ ffa_helper_init_device, ++#endif + #ifdef CONFIG_POST + initr_post, + #endif +diff --git a/drivers/Kconfig b/drivers/Kconfig +index b26ca8cf70c9..e83c23789d1b 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -6,6 +6,8 @@ source "drivers/core/Kconfig" + + source "drivers/adc/Kconfig" + ++source "drivers/arm-ffa/Kconfig" ++ + source "drivers/ata/Kconfig" + + source "drivers/axi/Kconfig" +diff --git a/drivers/Makefile b/drivers/Makefile +index 4e7cf284405a..6671d2a604ab 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -107,6 +107,7 @@ obj-y += iommu/ + obj-y += smem/ + obj-y += thermal/ + obj-$(CONFIG_TEE) += tee/ ++obj-$(CONFIG_ARM_FFA_TRANSPORT) += arm-ffa/ + obj-y += axi/ + obj-y += ufs/ + obj-$(CONFIG_W1) += w1/ +diff --git a/drivers/arm-ffa/Kconfig b/drivers/arm-ffa/Kconfig +new file mode 100644 +index 000000000000..d71444c1fa90 +--- /dev/null ++++ b/drivers/arm-ffa/Kconfig +@@ -0,0 +1,26 @@ ++# SPDX-License-Identifier: GPL-2.0 ++ ++config ARM_FFA_TRANSPORT ++ bool "Enable Arm Firmware Framework for Armv8-A driver" ++ depends on DM && ARM64 ++ select ARM_SMCCC ++ select LIB_UUID ++ select ARM_FFA_TRANSPORT_HELPERS ++ select CMD_ARMFFA ++ help ++ The Firmware Framework for Arm A-profile processors (FF-A) ++ describes interfaces (ABIs) that standardize communication ++ between the Secure World and Normal World leveraging TrustZone ++ technology. ++ ++ This driver is based on FF-A specification v1.0 and uses SMC32 ++ calling convention. ++ ++ FF-A specification: ++ ++ https://developer.arm.com/documentation/den0077/a/?lang=en ++ ++ In u-boot FF-A design, the Secure World is considered as one ++ entity to communicate with. FF-A communication is handled by ++ one device and one instance. This device takes care of ++ all the interactions between Normal world and Secure World. +diff --git a/drivers/arm-ffa/Makefile b/drivers/arm-ffa/Makefile +new file mode 100644 +index 000000000000..9fb5bea52299 +--- /dev/null ++++ b/drivers/arm-ffa/Makefile +@@ -0,0 +1,3 @@ ++# SPDX-License-Identifier: GPL-2.0+ ++ ++obj-y += arm-ffa-uclass.o core.o +diff --git a/drivers/arm-ffa/arm-ffa-uclass.c b/drivers/arm-ffa/arm-ffa-uclass.c +new file mode 100644 +index 000000000000..43f6066281fe +--- /dev/null ++++ b/drivers/arm-ffa/arm-ffa-uclass.c +@@ -0,0 +1,67 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * (C) Copyright 2021 ARM Limited ++ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> ++ */ ++ ++#include <common.h> ++#include <dm.h> ++#include <arm_ffa.h> ++#include <errno.h> ++#include <log.h> ++#include <asm/global_data.h> ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++UCLASS_DRIVER(ffa) = { ++ .name = "ffa", ++ .id = UCLASS_FFA, ++}; ++ ++/** ++ * ffa_get_invoke_func - performs a call to the FF-A driver dispatcher ++ * @func_id: The FF-A function to be used ++ * @func_data: Pointer to the FF-A function arguments ++ * container structure. This also includes ++ * pointers to the returned data needed by ++ * clients. ++ * ++ * This runtime function passes the FF-A function ID and its arguments to ++ * the FF-A driver dispatcher. ++ * This function is called by the FF-A helper functions. ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++int __ffa_runtime ffa_get_invoke_func(u32 func_id, struct ffa_interface_data *func_data) ++{ ++ if (!ffa_device_get_ops()->invoke_func) ++ return -EINVAL; ++ ++ return ffa_device_get_ops()->invoke_func(func_id, func_data); ++} ++ ++/** ++ * ffa_init_device - probes the arm_ffa device ++ * ++ * This boot time function makes sure the arm_ffa device is probed ++ * and ready for use. ++ * This function is called automatically at initcalls ++ * level (after u-boot relocation). ++ * ++ * Arm FF-A transport is implemented through a single u-boot ++ * device (arm_ffa). So, there is only one device belonging to UCLASS_FFA. ++ * All FF-A clients should use the arm_ffa device to use the FF-A ++ * transport. ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++int ffa_init_device(void) ++{ ++ ffa_dbg("[%s]", __func__); ++ ++ return ffa_get_device(); ++} +diff --git a/drivers/arm-ffa/arm_ffa_prv.h b/drivers/arm-ffa/arm_ffa_prv.h +new file mode 100644 +index 000000000000..38ea4ba83efc +--- /dev/null ++++ b/drivers/arm-ffa/arm_ffa_prv.h +@@ -0,0 +1,199 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * (C) Copyright 2021 ARM Limited ++ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> ++ */ ++ ++#ifndef __ARM_FFA_PRV_H ++#define __ARM_FFA_PRV_H ++ ++#include <arm_ffa.h> ++#include <linux/bitfield.h> ++#include <linux/bitops.h> ++ ++/* ++ * This header is private. It is exclusively used by the FF-A driver ++ */ ++ ++/* FF-A driver version definitions */ ++ ++#define MAJOR_VERSION_MASK GENMASK(30, 16) ++#define MINOR_VERSION_MASK GENMASK(15, 0) ++#define GET_FFA_MAJOR_VERSION(x) \ ++ ((u16)(FIELD_GET(MAJOR_VERSION_MASK, (x)))) ++#define GET_FFA_MINOR_VERSION(x) \ ++ ((u16)(FIELD_GET(MINOR_VERSION_MASK, (x)))) ++#define PACK_VERSION_INFO(major, minor) \ ++ (FIELD_PREP(MAJOR_VERSION_MASK, (major)) | \ ++ FIELD_PREP(MINOR_VERSION_MASK, (minor))) ++ ++#define FFA_MAJOR_VERSION (1) ++#define FFA_MINOR_VERSION (0) ++#define FFA_VERSION_1_0 \ ++ PACK_VERSION_INFO(FFA_MAJOR_VERSION, FFA_MINOR_VERSION) ++ ++/* Endpoint ID mask (u-boot endpoint ID) */ ++ ++#define GET_SELF_ENDPOINT_ID_MASK GENMASK(15, 0) ++#define GET_SELF_ENDPOINT_ID(x) \ ++ ((u16)(FIELD_GET(GET_SELF_ENDPOINT_ID_MASK, (x)))) ++ ++#define PREP_SELF_ENDPOINT_ID_MASK GENMASK(31, 16) ++#define PREP_SELF_ENDPOINT_ID(x) \ ++ ((u16)(FIELD_PREP(PREP_SELF_ENDPOINT_ID_MASK, (x)))) ++ ++/* Partition endpoint ID mask (partition with which u-boot communicates with) */ ++ ++#define PREP_PART_ENDPOINT_ID_MASK GENMASK(15, 0) ++#define PREP_PART_ENDPOINT_ID(x) \ ++ ((u16)(FIELD_PREP(PREP_PART_ENDPOINT_ID_MASK, (x)))) ++ ++/* The FF-A SMC function prototype definition */ ++ ++typedef void (*invoke_ffa_fn_t)(unsigned long a0, unsigned long a1, ++ unsigned long a2, unsigned long a3, unsigned long a4, ++ unsigned long a5, unsigned long a6, unsigned long a7, ++ struct arm_smccc_res *res); ++ ++/** ++ * enum ffa_conduit - Arm FF-A conduits supported by the Arm FF-A driver ++ * Currently only SMC32 is supported. ++ */ ++enum ffa_conduit { ++ FFA_CONDUIT_SMC = 0, ++}; ++ ++/** ++ * FFA_DECLARE_ARGS - FF-A functions local variables ++ * @a0-a7: local variables used to set registers x0-x7 ++ * @res: the structure hosting the FF-A function return data ++ * ++ * A helper macro for declaring local variables for the FF-A functions arguments. ++ * The x0-x7 registers are used to exchange data with the secure world. ++ * But, only the bottom 32-bit of thes registers contains the data. ++ */ ++#define FFA_DECLARE_ARGS \ ++ unsigned long a0 = 0; \ ++ unsigned long a1 = 0; \ ++ unsigned long a2 = 0; \ ++ unsigned long a3 = 0; \ ++ unsigned long a4 = 0; \ ++ unsigned long a5 = 0; \ ++ unsigned long a6 = 0; \ ++ unsigned long a7 = 0; \ ++ struct arm_smccc_res res = {0} ++ ++/* FF-A error codes */ ++#define FFA_ERR_STAT_NOT_SUPPORTED (-1) ++#define FFA_ERR_STAT_INVALID_PARAMETERS (-2) ++#define FFA_ERR_STAT_NO_MEMORY (-3) ++#define FFA_ERR_STAT_BUSY (-4) ++#define FFA_ERR_STAT_INTERRUPTED (-5) ++#define FFA_ERR_STAT_DENIED (-6) ++#define FFA_ERR_STAT_RETRY (-7) ++#define FFA_ERR_STAT_ABORTED (-8) ++ ++/** ++ * struct ffa_features_desc - FF-A functions features ++ * @func_id: FF-A function ++ * @field1: features read from register w2 ++ * @field2: features read from register w3 ++ * ++ * Data structure describing the features of the FF-A functions queried by ++ * FFA_FEATURES ++ */ ++struct ffa_features_desc { ++ u32 func_id; ++ u32 field1; ++ u32 field2; ++}; ++ ++/** ++ * enum ffa_rxtx_buf_sizes - minimum sizes supported ++ * for the RX/TX buffers ++ */ ++enum ffa_rxtx_buf_sizes { ++ RXTX_4K, ++ RXTX_64K, ++ RXTX_16K ++}; ++ ++/* ++ * Number of the FF-A interfaces features descriptors ++ * currently only FFA_RXTX_MAP descriptor is supported ++ */ ++#define FFA_FEATURE_DESC_CNT (1) ++ ++/** ++ * struct ffa_pdata - platform data for the arm_ffa device ++ * @conduit: The FF-A conduit used ++ * ++ * Platform data structure read from the device tree ++ */ ++struct ffa_pdata { ++ enum ffa_conduit conduit; ++}; ++ ++/** ++ * struct ffa_rxtxpair - structure hosting the RX/TX buffers physical addresses ++ * @rxbuf: physical address of the RX buffer ++ * @txbuf: physical address of the TX buffer ++ * ++ * Data structure hosting the physical addresses of the mapped RX/TX buffers ++ * These physical address are used by the FF-A functions that use the RX/TX buffers ++ */ ++struct ffa_rxtxpair { ++ u64 rxbuf; /* physical address */ ++ u64 txbuf; /* physical address */ ++}; ++ ++/** ++ * struct ffa_partition_desc - the secure partition descriptor ++ * @info: partition information ++ * @UUID: UUID ++ * ++ * Each partition has its descriptor containing the partitions information and the UUID ++ */ ++struct ffa_partition_desc { ++ struct ffa_partition_info info; ++ union ffa_partition_uuid UUID; ++}; ++ ++/** ++ * struct ffa_partitions - descriptors for all secure partitions ++ * @count: The number of partitions descriptors ++ * @descs The partitions descriptors table ++ * ++ * This data structure contains the partitions descriptors table ++ */ ++struct ffa_partitions { ++ u32 count; ++ struct ffa_partition_desc *descs; /* virtual address */ ++}; ++ ++/** ++ * struct ffa_prvdata - the driver private data structure ++ * ++ * @dev: The arm_ffa device under u-boot driver model ++ * @fwk_version: FF-A framework version ++ * @id: u-boot endpoint ID ++ * @partitions: The partitions descriptors structure ++ * @pair: The RX/TX buffers pair ++ * @conduit: The selected conduit ++ * @invoke_ffa_fn: The function executing the FF-A function ++ * @features: Table of the FF-A functions having features ++ * ++ * The driver data structure hosting all resident data. ++ */ ++struct ffa_prvdata { ++ struct udevice *dev; ++ u32 fwk_version; ++ u16 id; ++ struct ffa_partitions partitions; ++ struct ffa_rxtxpair pair; ++ enum ffa_conduit conduit; ++ invoke_ffa_fn_t invoke_ffa_fn; ++ struct ffa_features_desc features[FFA_FEATURE_DESC_CNT]; ++}; ++ ++#endif +diff --git a/drivers/arm-ffa/core.c b/drivers/arm-ffa/core.c +new file mode 100644 +index 000000000000..98e2d2fa1767 +--- /dev/null ++++ b/drivers/arm-ffa/core.c +@@ -0,0 +1,1484 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * (C) Copyright 2021 ARM Limited ++ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> ++ */ ++ ++#include "arm_ffa_prv.h" ++#include <asm/global_data.h> ++#include <asm/io.h> ++#include <common.h> ++#include <dm.h> ++#include <fdtdec.h> ++#include <linux/errno.h> ++#include <linux/sizes.h> ++#include <log.h> ++#include <malloc.h> ++#include <mapmem.h> ++#include <string.h> ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++/** ++ * The device private data structure containing all the resident ++ * data read from secure world ++ */ ++struct ffa_prvdata __ffa_runtime_data ffa_priv_data = {0}; ++ ++/* ++ * Driver functions ++ */ ++ ++/** ++ * ffa_get_device - probes the arm_ffa device ++ * ++ * This boot time function makes sure the arm_ffa device is probed ++ * and ready for use. This is done using uclass_get_device. ++ * The arm_ffa driver belongs to UCLASS_FFA. ++ * This function should be called before using the driver. ++ * ++ * Arm FF-A transport is implemented through a single u-boot ++ * device (arm_ffa). So, there is only one device belonging to UCLASS_FFA. ++ * All FF-A clients should use the arm_ffa device to use the FF-A ++ * transport. ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++int ffa_get_device(void) ++{ ++ int ret; ++ int devnum = 0; ++ ++ ffa_dbg("[%s]", __func__); ++ ++ if (ffa_priv_data.dev) ++ return FFA_ERR_STAT_SUCCESS; ++ ++ /* ++ * searching and probing the device ++ */ ++ ret = uclass_get_device(UCLASS_FFA, devnum, &ffa_priv_data.dev); ++ if (ret) { ++ ffa_err("can not find the device"); ++ ffa_priv_data.dev = NULL; ++ return -ENODEV; ++ } ++ ++ return FFA_ERR_STAT_SUCCESS; ++} ++ ++/** ++ * ffa_get_version - FFA_VERSION handler function ++ * ++ * This is the boot time function that implements FFA_VERSION FF-A function ++ * to get from the secure world the FF-A framework version ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++static int ffa_get_version(void) ++{ ++ u16 major, minor; ++ ++ FFA_DECLARE_ARGS; ++ ++ ffa_dbg("[%s]", __func__); ++ ++ if (!ffa_priv_data.invoke_ffa_fn) ++ panic("[FFA] no private data found\n"); ++ ++ a0 = FFA_VERSION; ++ a1 = FFA_VERSION_1_0; ++ ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); ++ ++ if (res.a0 == FFA_ERR_STAT_NOT_SUPPORTED) { ++ ffa_err("A Firmware Framework implementation does not exist"); ++ return -EOPNOTSUPP; ++ } ++ ++ major = GET_FFA_MAJOR_VERSION(res.a0); ++ minor = GET_FFA_MINOR_VERSION(res.a0); ++ ++ ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d", ++ FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor); ++ ++ if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) { ++ ffa_info("Versions are compatible "); ++ ++ ffa_priv_data.fwk_version = res.a0; ++ ++ return FFA_ERR_STAT_SUCCESS; ++ } ++ ++ ffa_info("Versions are incompatible "); ++ return -EPROTONOSUPPORT; ++} ++ ++/** ++ * ffa_get_endpoint_id - FFA_ID_GET handler function ++ * ++ * This is the boot time function that implements FFA_ID_GET FF-A function ++ * to get from the secure world u-boot endpoint ID ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++static int ffa_get_endpoint_id(void) ++{ ++ FFA_DECLARE_ARGS; ++ ++ ffa_dbg("[%s]", __func__); ++ ++ if (!ffa_priv_data.invoke_ffa_fn) ++ panic("[FFA] no private data found\n"); ++ ++ a0 = FFA_ID_GET; ++ ++ ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); ++ ++ switch (res.a0) { ++ case FFA_ERROR: ++ { ++ if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) { ++ ffa_err("This function is not implemented at this FF-A instance"); ++ return -EOPNOTSUPP; ++ } ++ ++ ffa_err("Undefined error code (%d)", ((int)res.a2)); ++ return -EINVAL; ++ } ++ case FFA_SUCCESS: ++ { ++ ffa_priv_data.id = GET_SELF_ENDPOINT_ID(res.a2); ++ ffa_info("endpoint ID is %u", ffa_priv_data.id); ++ ++ return FFA_ERR_STAT_SUCCESS; ++ } ++ default: ++ { ++ ffa_err("Undefined response function (0x%lx)", res.a0); ++ return -EINVAL; ++ } ++ } ++} ++ ++/** ++ * ffa_get_features_desc - returns the features descriptor of the specified ++ * FF-A function ++ * @func_id: the FF-A function which the features are to be retrieved ++ * ++ * This is a boot time function that searches the features descriptor of the ++ * specified FF-A function ++ * ++ * Return: ++ * ++ * When found, the address of the features descriptor is returned. Otherwise, NULL. ++ */ ++static struct ffa_features_desc *ffa_get_features_desc(u32 func_id) ++{ ++ u32 desc_idx; ++ ++ /* ++ * search for the descriptor of the selected FF-A interface ++ */ ++ for (desc_idx = 0; desc_idx < FFA_FEATURE_DESC_CNT ; desc_idx++) ++ if (ffa_priv_data.features[desc_idx].func_id == func_id) ++ return &ffa_priv_data.features[desc_idx]; ++ ++ return NULL; ++} ++ ++/** ++ * ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP ++ * argument ++ * ++ * This is the boot time function that implements FFA_FEATURES FF-A function ++ * to retrieve the FFA_RXTX_MAP features ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++static int ffa_get_rxtx_map_features(void) ++{ ++ FFA_DECLARE_ARGS; ++ ++ ffa_dbg("[%s]", __func__); ++ ++ if (!ffa_priv_data.invoke_ffa_fn) ++ panic("[FFA] no private data found\n"); ++ ++ a0 = FFA_FEATURES; ++ a1 = FFA_RXTX_MAP; ++ ++ ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); ++ ++ switch (res.a0) { ++ case FFA_ERROR: ++ { ++ if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) { ++ ffa_err("FFA_RXTX_MAP is not implemented at this FF-A instance"); ++ return -EOPNOTSUPP; ++ } ++ ++ ffa_err("Undefined error code (%d)", ((int)res.a2)); ++ return -EINVAL; ++ } ++ case FFA_SUCCESS: ++ { ++ u32 desc_idx; ++ ++ /* ++ * search for an empty descriptor ++ */ ++ for (desc_idx = 0; desc_idx < FFA_FEATURE_DESC_CNT ; desc_idx++) ++ if (!ffa_priv_data.features[desc_idx].func_id) { ++ /* ++ * populate the descriptor with ++ * the interface features data ++ */ ++ ffa_priv_data.features[desc_idx].func_id = ++ FFA_RXTX_MAP; ++ ffa_priv_data.features[desc_idx].field1 = ++ res.a2; ++ ++ ffa_info("FFA_RXTX_MAP features data 0x%lx", ++ res.a2); ++ ++ return FFA_ERR_STAT_SUCCESS; ++ } ++ ++ ffa_err("Cannot save FFA_RXTX_MAP features data. Descriptors table full"); ++ return -ENOBUFS; ++ } ++ default: ++ { ++ ffa_err("Undefined response function (0x%lx)", ++ res.a0); ++ return -EINVAL; ++ } ++ } ++} ++ ++/** ++ * ffa_get_rxtx_buffers_pages_cnt - reads from the features data descriptors ++ * the minimum number of pages in each of the RX/TX ++ * buffers ++ * @buf_4k_pages: Pointer to the minimum number of pages ++ * ++ * This is the boot time function that returns the minimum number of pages ++ * in each of the RX/TX buffers ++ * ++ * Return: ++ * ++ * buf_4k_pages points to the returned number of pages ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++static int ffa_get_rxtx_buffers_pages_cnt(size_t *buf_4k_pages) ++{ ++ struct ffa_features_desc *desc = NULL; ++ ++ ffa_dbg("[%s]", __func__); ++ ++ if (!buf_4k_pages) ++ return -EINVAL; ++ ++ desc = ffa_get_features_desc(FFA_RXTX_MAP); ++ if (!desc) ++ return -EINVAL; ++ ++ ffa_dbg("FFA_RXTX_MAP descriptor found"); ++ ++ switch (desc->field1) { ++ case RXTX_4K: ++ *buf_4k_pages = 1; ++ break; ++ case RXTX_16K: ++ *buf_4k_pages = 4; ++ break; ++ case RXTX_64K: ++ *buf_4k_pages = 16; ++ break; ++ default: ++ ffa_err("RX/TX buffer size not supported"); ++ return -EINVAL; ++ } ++ ++ return FFA_ERR_STAT_SUCCESS; ++} ++ ++/** ++ * ffa_free_rxtx_buffers - frees the RX/TX buffers ++ * @buf_4k_pages: the minimum number of pages in each of the RX/TX ++ * buffers ++ * ++ * This is the boot time function used to free the RX/TX buffers ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++static int ffa_free_rxtx_buffers(size_t buf_4k_pages) ++{ ++ efi_status_t free_rxbuf_ret, free_txbuf_ret; ++ ++ ffa_info("Freeing RX/TX buffers"); ++ ++ free_rxbuf_ret = efi_free_pages(ffa_priv_data.pair.rxbuf, buf_4k_pages); ++ free_txbuf_ret = efi_free_pages(ffa_priv_data.pair.txbuf, buf_4k_pages); ++ ++ if (free_rxbuf_ret != EFI_SUCCESS || free_txbuf_ret != EFI_SUCCESS) { ++ ffa_err("Failed to free RX/TX buffers (rx: %lu , tx: %lu)", ++ free_rxbuf_ret, ++ free_txbuf_ret); ++ return -EINVAL; ++ } ++ ++ return FFA_ERR_STAT_SUCCESS; ++} ++ ++/** ++ * ffa_alloc_rxtx_buffers - allocates the RX/TX buffers ++ * @buf_4k_pages: the minimum number of pages in each of the RX/TX ++ * buffers ++ * ++ * This is the boot time function used by ffa_map_rxtx_buffers to allocate ++ * the RX/TX buffers before mapping them ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++static int ffa_alloc_rxtx_buffers(size_t buf_4k_pages) ++{ ++ ffa_dbg("[%s]", __func__); ++ ++#if CONFIG_IS_ENABLED(EFI_LOADER) ++ ++ efi_status_t efi_ret; ++ void *virt_txbuf; ++ void *virt_rxbuf; ++ ++ ffa_info("Using %lu 4KB page(s) for RX/TX buffers size", ++ buf_4k_pages); ++ ++ efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, ++ EFI_BOOT_SERVICES_DATA, ++ buf_4k_pages, ++ &ffa_priv_data.pair.rxbuf); ++ ++ if (efi_ret != EFI_SUCCESS) { ++ ffa_priv_data.pair.rxbuf = 0; ++ ffa_err("Failure to allocate RX buffer (EFI error: 0x%lx)", ++ efi_ret); ++ ++ return -ENOBUFS; ++ } ++ ++ ffa_info("RX buffer at phys 0x%llx", ++ ffa_priv_data.pair.rxbuf); ++ ++ /* ++ * convert the RX buffer physical address to virtual address ++ */ ++ virt_rxbuf = (void *)map_sysmem((phys_addr_t)ffa_priv_data.pair.rxbuf, 0); ++ ++ /* ++ * make sure the buffer is clean before use ++ */ ++ memset(virt_rxbuf, 0, buf_4k_pages * SZ_4K); ++ ++ unmap_sysmem(virt_rxbuf); ++ ++ efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, ++ EFI_RUNTIME_SERVICES_DATA, ++ buf_4k_pages, ++ &ffa_priv_data.pair.txbuf); ++ ++ if (efi_ret != EFI_SUCCESS) { ++ ffa_dbg("FFA_RXTX_MAP: freeing RX buffer"); ++ efi_free_pages(ffa_priv_data.pair.rxbuf, buf_4k_pages); ++ ffa_priv_data.pair.rxbuf = 0; ++ ffa_priv_data.pair.txbuf = 0; ++ ffa_err("Failure to allocate the TX buffer (EFI error: 0x%lx)" ++ , efi_ret); ++ ++ return -ENOBUFS; ++ } ++ ++ ffa_info("TX buffer at phys 0x%llx", ++ ffa_priv_data.pair.txbuf); ++ ++ /* ++ * convert the TX buffer physical address to virtual address ++ */ ++ virt_txbuf = (void *)map_sysmem((phys_addr_t)ffa_priv_data.pair.txbuf, 0); ++ ++ /* ++ * make sure the buffer is clean before use ++ */ ++ memset(virt_txbuf, 0, buf_4k_pages * SZ_4K); ++ ++ unmap_sysmem(virt_txbuf); ++ ++ return FFA_ERR_STAT_SUCCESS; ++ ++#else ++ return -ENOBUFS; ++#endif ++} ++ ++/** ++ * ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function ++ * @buf_4k_pages: the minimum number of pages in each of the RX/TX ++ * buffers ++ * ++ * This is the boot time function that implements FFA_RXTX_MAP FF-A function ++ * to map the RX/TX buffers ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++static int ffa_map_rxtx_buffers(size_t buf_4k_pages) ++{ ++ int ret; ++ ++ FFA_DECLARE_ARGS; ++ ++ ffa_dbg("[%s]", __func__); ++ ++ if (!ffa_priv_data.invoke_ffa_fn) ++ panic("[FFA] no private data found\n"); ++ ++ ret = ffa_alloc_rxtx_buffers(buf_4k_pages); ++ if (ret != FFA_ERR_STAT_SUCCESS) ++ return ret; ++ ++ a0 = FFA_RXTX_MAP; ++ a1 = ffa_priv_data.pair.txbuf; ++ a2 = ffa_priv_data.pair.rxbuf; ++ a3 = buf_4k_pages; ++ ++ ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); ++ ++ switch (res.a0) { ++ case FFA_ERROR: ++ { ++ switch (((int)res.a2)) { ++ case FFA_ERR_STAT_INVALID_PARAMETERS: ++ ffa_err("One or more fields in input parameters is incorrectly encoded"); ++ ret = -EPERM; ++ break; ++ case FFA_ERR_STAT_NO_MEMORY: ++ ffa_err("Not enough memory"); ++ ret = -ENOMEM; ++ break; ++ case FFA_ERR_STAT_DENIED: ++ ffa_err("Buffer pair already registered"); ++ ret = -EACCES; ++ break; ++ case FFA_ERR_STAT_NOT_SUPPORTED: ++ ffa_err("This function is not implemented at this FF-A instance"); ++ ret = -EOPNOTSUPP; ++ break; ++ default: ++ ffa_err("Undefined error (%d)", ++ ((int)res.a2)); ++ ret = -EINVAL; ++ } ++ break; ++ } ++ case FFA_SUCCESS: ++ ffa_info("RX/TX buffers mapped"); ++ return FFA_ERR_STAT_SUCCESS; ++ default: ++ ffa_err("Undefined response function (0x%lx)", ++ res.a0); ++ ret = -EINVAL; ++ } ++ ++ ffa_free_rxtx_buffers(buf_4k_pages); ++ ++ return ret; ++} ++ ++/** ++ * ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function ++ * ++ * This is the boot time function that implements FFA_RXTX_UNMAP FF-A function ++ * to unmap the RX/TX buffers ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++static int ffa_unmap_rxtx_buffers(void) ++{ ++ FFA_DECLARE_ARGS; ++ ++ ffa_dbg("[%s]", __func__); ++ ++ if (!ffa_priv_data.invoke_ffa_fn) ++ panic("[FFA] no private data found\n"); ++ ++ a0 = FFA_RXTX_UNMAP; ++ a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data.id); ++ ++ ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); ++ ++ switch (res.a0) { ++ case FFA_ERROR: ++ { ++ if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) ++ panic("[FFA] FFA_RXTX_UNMAP is not implemented at this FF-A instance\n"); ++ else if (((int)res.a2) == FFA_ERR_STAT_INVALID_PARAMETERS) ++ panic("[FFA] There is no buffer pair registered on behalf of the caller\n"); ++ else ++ panic("[FFA] Undefined error (%d)\n", ((int)res.a2)); ++ } ++ case FFA_SUCCESS: ++ { ++ size_t buf_4k_pages = 0; ++ int ret; ++ ++ ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages); ++ if (ret != FFA_ERR_STAT_SUCCESS) ++ panic("[FFA] RX/TX buffers unmapped but failure in getting pages count\n"); ++ ++ ret = ffa_free_rxtx_buffers(buf_4k_pages); ++ if (ret != FFA_ERR_STAT_SUCCESS) ++ panic("[FFA] RX/TX buffers unmapped but failure in freeing the memory\n"); ++ ++ ffa_info("RX/TX buffers unmapped and memory freed"); ++ ++ return FFA_ERR_STAT_SUCCESS; ++ } ++ default: ++ panic("[FFA] Undefined response function (0x%lx)", res.a0); ++ } ++} ++ ++/** ++ * ffa_release_rx_buffer - FFA_RX_RELEASE handler function ++ * ++ * This is the boot time function that invokes FFA_RX_RELEASE FF-A function ++ * to release the ownership of the RX buffer ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++static int ffa_release_rx_buffer(void) ++{ ++ FFA_DECLARE_ARGS; ++ ++ ffa_dbg("[%s]", __func__); ++ ++ if (!ffa_priv_data.invoke_ffa_fn) ++ panic("[FFA] no private data found\n"); ++ ++ a0 = FFA_RX_RELEASE; ++ ++ ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); ++ ++ switch (res.a0) { ++ case FFA_ERROR: ++ { ++ if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) ++ panic("[FFA] FFA_RX_RELEASE is not implemented at this FF-A instance\n"); ++ else if (((int)res.a2) == FFA_ERR_STAT_DENIED) ++ panic("[FFA] Caller did not have ownership of the RX buffer\n"); ++ else ++ panic("[FFA] Undefined error (%d)\n", ((int)res.a2)); ++ } ++ case FFA_SUCCESS: ++ ffa_info("RX buffer released"); ++ return FFA_ERR_STAT_SUCCESS; ++ ++ default: ++ panic("[FFA] Undefined response function (0x%lx)\n", res.a0); ++ } ++} ++ ++/** ++ * ffa_uuid_are_identical - checks whether two given UUIDs are identical ++ * @uuid1: first UUID ++ * @uuid2: second UUID ++ * ++ * This is a boot time function used by ffa_read_partitions_info to search ++ * for a UUID in the partitions descriptors table ++ * ++ * Return: ++ * ++ * 1 when UUIDs match. Otherwise, 0 ++ */ ++int ffa_uuid_are_identical(const union ffa_partition_uuid *uuid1, ++ const union ffa_partition_uuid *uuid2) ++{ ++ if (!uuid1 || !uuid2) ++ return 0; ++ ++ return (!memcmp(uuid1, uuid2, sizeof(union ffa_partition_uuid))); ++} ++ ++/** ++ * ffa_read_partitions_info - reads the data queried by FFA_PARTITION_INFO_GET ++ * and saves it in the private structure ++ * @count: The number of partitions queried ++ * @part_uuid: Pointer to the partition(s) UUID ++ * ++ * This is the boot time function that reads the partitions information ++ * returned by the FFA_PARTITION_INFO_GET and saves it in the private ++ * data structure. ++ * ++ * Return: ++ * ++ * The private data structure is updated with the partition(s) information ++ * FFA_ERR_STAT_SUCCESS is returned on success. Otherwise, failure ++ */ ++static int ffa_read_partitions_info(u32 count, union ffa_partition_uuid *part_uuid) ++{ ++ ffa_dbg("[%s]", __func__); ++ ++ if (!count) { ++ ffa_err("No partition detected"); ++ return -ENODATA; ++ } ++ ++ ffa_info("Reading partitions data from the RX buffer"); ++ ++#if CONFIG_IS_ENABLED(EFI_LOADER) ++ ++ if (!part_uuid) { ++ /* ++ * querying information of all partitions ++ */ ++ u64 data_pages; ++ u64 data_bytes; ++ efi_status_t efi_ret; ++ size_t buf_4k_pages = 0; ++ u32 desc_idx; ++ struct ffa_partition_info *parts_info; ++ int ret; ++ ++ data_bytes = count * sizeof(struct ffa_partition_desc); ++ data_pages = efi_size_in_pages(data_bytes); ++ ++ /* ++ * get the RX buffer size in pages ++ */ ++ ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages); ++ if (ret != FFA_ERR_STAT_SUCCESS) { ++ ffa_err("Can not get the RX buffer size (error %d)", ret); ++ return ret; ++ } ++ ++ if (data_pages > buf_4k_pages) { ++ ffa_err("Partitions data size exceeds the RX buffer size:"); ++ ffa_err(" Sizes in pages: data %llu , RX buffer %lu ", ++ data_pages, ++ buf_4k_pages); ++ ++ return -ENOMEM; ++ } ++ ++ efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, ++ EFI_RUNTIME_SERVICES_DATA, ++ data_pages, ++ (u64 *)&ffa_priv_data.partitions.descs); ++ ++ if (efi_ret != EFI_SUCCESS) { ++ ffa_priv_data.partitions.descs = NULL; ++ ++ ffa_err("Cannot allocate partitions data buffer (EFI error 0x%lx)", ++ efi_ret); ++ ++ return -ENOBUFS; ++ } ++ ++ /* ++ * convert the descs buffer physical address ++ * to virtual address ++ * This virtual address should not be unmapped ++ * descs is expected to be a virtual address ++ */ ++ ffa_priv_data.partitions.descs = ++ (struct ffa_partition_desc *) ++ map_sysmem((phys_addr_t) ++ ffa_priv_data.partitions.descs, 0); ++ ++ /* ++ * make sure the buffer is clean before use ++ */ ++ memset(ffa_priv_data.partitions.descs, 0, ++ data_pages * SZ_4K); ++ ++ ffa_info("Copying %lld page(s) of partitions data from RX buffer", ++ data_pages); ++ ++ /* ++ * convert the RX buffer physical address to ++ * virtual address ++ */ ++ parts_info = (struct ffa_partition_info *) ++ map_sysmem((phys_addr_t) ++ ffa_priv_data.pair.rxbuf, 0); ++ ++ for (desc_idx = 0 ; desc_idx < count ; desc_idx++) { ++ ffa_priv_data.partitions.descs[desc_idx].info = ++ parts_info[desc_idx]; ++ ++ ffa_info("Partition ID %x : info cached", ++ ffa_priv_data.partitions.descs[desc_idx].info.id); ++ } ++ unmap_sysmem(parts_info); ++ ++ ffa_priv_data.partitions.count = count; ++ ++ ffa_info("%d partition(s) found and cached", count); ++ ++ } else { ++ u32 rx_desc_idx, cached_desc_idx; ++ struct ffa_partition_info *parts_info; ++ u8 desc_found; ++ ++ /* ++ * convert the RX buffer physical address to virtual address ++ */ ++ parts_info = (struct ffa_partition_info *) ++ map_sysmem((phys_addr_t)ffa_priv_data.pair.rxbuf, 0); ++ ++ /* ++ * search for the SP IDs read from the RX buffer ++ * in the already cached SPs. ++ * Update the UUID when ID found. ++ */ ++ for (rx_desc_idx = 0; rx_desc_idx < count ; rx_desc_idx++) { ++ desc_found = 0; ++ ++ /* ++ * search the current ID in the cached partitions ++ */ ++ for (cached_desc_idx = 0; ++ cached_desc_idx < ffa_priv_data.partitions.count; ++ cached_desc_idx++) { ++ /* ++ * save the UUID ++ */ ++ if (ffa_priv_data.partitions.descs[cached_desc_idx].info.id == ++ parts_info[rx_desc_idx].id) { ++ ffa_priv_data.partitions.descs[cached_desc_idx].UUID = ++ *part_uuid; ++ ++ desc_found = 1; ++ break; ++ } ++ } ++ ++ if (!desc_found) { ++ unmap_sysmem(parts_info); ++ return -ENODATA; ++ } ++ } ++ unmap_sysmem(parts_info); ++ } ++#else ++#warning "arm_ffa: reading FFA_PARTITION_INFO_GET data not implemented" ++#endif ++ ++ return FFA_ERR_STAT_SUCCESS; ++} ++ ++/** ++ * ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET ++ * and saves partitions data ++ * @part_uuid: Pointer to the partition(s) UUID ++ * @pcount: Pointer to the number of partitions variable filled when querying ++ * ++ * This is the boot time function that executes the FFA_PARTITION_INFO_GET ++ * to query the partitions data. Then, it calls ffa_read_partitions_info ++ * to save the data in the private data structure. ++ * ++ * After reading the data the RX buffer is released using ffa_release_rx_buffer ++ * ++ * Return: ++ * ++ * When part_uuid is NULL, all partitions data are retrieved from secure world ++ * When part_uuid is non NULL, data for partitions matching the given UUID are ++ * retrieved and the number of partitions is returned ++ * FFA_ERR_STAT_SUCCESS is returned on success. Otherwise, failure ++ */ ++static int ffa_query_partitions_info(union ffa_partition_uuid *part_uuid, ++ u32 *pcount) ++{ ++ unsigned long a0 = 0; ++ union ffa_partition_uuid query_uuid = {0}; ++ unsigned long a5 = 0; ++ unsigned long a6 = 0; ++ unsigned long a7 = 0; ++ struct arm_smccc_res res = {0}; ++ ++ ffa_dbg("[%s]", __func__); ++ ++ if (!ffa_priv_data.invoke_ffa_fn) ++ panic("[FFA] no private data found\n"); ++ ++ a0 = FFA_PARTITION_INFO_GET; ++ ++ /* ++ * If a UUID is specified. Information for one or more ++ * partitions in the system is queried. Otherwise, information ++ * for all installed partitions is queried ++ */ ++ ++ if (part_uuid) { ++ if (!pcount) ++ return -EINVAL; ++ ++ query_uuid = *part_uuid; ++ } ++ ++ ffa_priv_data.invoke_ffa_fn(a0, query_uuid.words.a1, query_uuid.words.a2, ++ query_uuid.words.a3, query_uuid.words.a4, ++ a5, a6, a7, &res); ++ ++ switch (res.a0) { ++ case FFA_ERROR: ++ { ++ switch (((int)res.a2)) { ++ case FFA_ERR_STAT_INVALID_PARAMETERS: ++ ffa_err("Unrecognized UUID"); ++ return -EPERM; ++ case FFA_ERR_STAT_NO_MEMORY: ++ ffa_err("Results cannot fit in RX buffer of the caller"); ++ return -ENOMEM; ++ case FFA_ERR_STAT_DENIED: ++ ffa_err("Callee is not in a state to handle this request"); ++ return -EACCES; ++ case FFA_ERR_STAT_NOT_SUPPORTED: ++ ffa_err("This function is not implemented at this FF-A instance"); ++ return -EOPNOTSUPP; ++ case FFA_ERR_STAT_BUSY: ++ ffa_err("RX buffer of the caller is not free"); ++ return -EBUSY; ++ default: ++ ffa_err("Undefined error (%d)", ((int)res.a2)); ++ return -EINVAL; ++ } ++ } ++ case FFA_SUCCESS: ++ { ++ int ret; ++ ++ /* ++ * res.a2 contains the count of partition information descriptors ++ * populated in the RX buffer ++ */ ++ if (res.a2) { ++ ret = ffa_read_partitions_info(res.a2, part_uuid); ++ if (ret) ++ ffa_err("Failed to read partition(s) data , error (%d)", ret); ++ } ++ ++ /* ++ * return the SP count ++ */ ++ if (part_uuid) { ++ if (!ret) ++ *pcount = res.a2; ++ else ++ *pcount = 0; ++ } ++ /* ++ * After calling FFA_PARTITION_INFO_GET the buffer ownership ++ * is assigned to the consumer (u-boot). So, we need to give ++ * the ownership back to the secure world ++ */ ++ ret = ffa_release_rx_buffer(); ++ ++ if (!part_uuid && !res.a2) { ++ ffa_err("[FFA] no partition installed in the system"); ++ return -ENODEV; ++ } ++ ++ return ret; ++ } ++ default: ++ ffa_err("Undefined response function (0x%lx)", res.a0); ++ return -EINVAL; ++ } ++} ++ ++/** ++ * ffa_get_partitions_info - FFA_PARTITION_INFO_GET handler function ++ * @func_data: Pointer to the FF-A function arguments container structure. ++ * The passed arguments: ++ * Mode 1: When getting from the driver the number of ++ * secure partitions: ++ * @data0_size: UUID size ++ * @data0: pointer to the UUID (little endian) ++ * @data1_size: size of the number of partitions ++ * variable ++ * @data1: pointer to the number of partitions ++ * variable. The variable will be set ++ * by the driver ++ * Mode 2: When requesting the driver to return the ++ * partitions information: ++ * @data0_size: UUID size ++ * @data0: pointer to the UUID (little endian) ++ * @data1_size: size of the SPs information buffer ++ * @data1: pointer to SPs information buffer ++ * (allocated by the client). ++ * The buffer will be filled by the driver ++ * ++ * This is the boot time function that queries the secure partition data from ++ * the private data structure. If not found, it invokes FFA_PARTITION_INFO_GET ++ * FF-A function to query the partition information from secure world. ++ * ++ * A client of the FF-A driver should know the UUID of the service it wants to ++ * access. It should use the UUID to request the FF-A driver to provide the ++ * partition(s) information of the service. The FF-A driver uses ++ * PARTITION_INFO_GET to obtain this information. This is implemented through ++ * ffa_get_partitions_info function. ++ * A new FFA_PARTITION_INFO_GET call is issued (first one performed through ++ * ffa_cache_partitions_info) allowing to retrieve the partition(s) information. ++ * They are not saved (already done). We only update the UUID in the cached area. ++ * This assumes that partitions data does not change in the secure world. ++ * Otherwise u-boot will have an outdated partition data. The benefit of caching ++ * the information in the FF-A driver is to accommodate discovery after ++ * ExitBootServices(). ++ * ++ * When invoked through a client request, ffa_get_partitions_info should be ++ * called twice. First call is to get from the driver the number of secure ++ * partitions (SPs) associated to a particular UUID. ++ * Then, the caller (client) allocates the buffer to host the SPs data and ++ * issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated ++ * buffer. ++ * ++ * To achieve the mechanism described above, ffa_get_partitions_info uses the ++ * following functions: ++ * ffa_read_partitions_info ++ * ffa_query_partitions_info ++ * ++ * Return: ++ * ++ * @data1: When pointing to the number of partitions variable, the number is ++ * set by the driver. ++ * When pointing to the partitions information buffer, the buffer will be ++ * filled by the driver. ++ * ++ * On success FFA_ERR_STAT_SUCCESS is returned. Otherwise, failure ++ */ ++static int ffa_get_partitions_info(struct ffa_interface_data *func_data) ++{ ++ /* ++ * fill_data: ++ * 0: return the SP count ++ * 1: fill SP data and return it to the caller ++ * -1: undefined mode ++ */ ++ int fill_data = -1; ++ u32 desc_idx, client_desc_idx; ++ union ffa_partition_uuid *part_uuid; ++ u32 client_desc_max_cnt; ++ u32 parts_found = 0; ++ ++ ffa_dbg("[%s]", __func__); ++ ++ if (!func_data) { ++ ffa_err("No function data provided"); ++ return -EINVAL; ++ } ++ ++ if (!ffa_priv_data.partitions.count || !ffa_priv_data.partitions.descs) ++ panic("[FFA] No partition installed\n"); ++ ++ if (func_data->data0_size == sizeof(union ffa_partition_uuid) && ++ func_data->data0 && ++ func_data->data1_size == sizeof(u32) && ++ func_data->data1) { ++ /* ++ * data0 (in): pointer to UUID ++ * data1 (in): pointer to SP count ++ * Out: SP count returned in the count variable pointed by data1 ++ */ ++ ++ fill_data = 0; ++ ++ ffa_info("Preparing for checking partitions count"); ++ ++ } else if ((func_data->data0_size == sizeof(union ffa_partition_uuid)) && ++ func_data->data0 && ++ (func_data->data1_size >= sizeof(struct ffa_partition_info)) && ++ !(func_data->data1_size % sizeof(struct ffa_partition_info)) && ++ func_data->data1) { ++ /* ++ * data0 (in): pointer to UUID ++ * data1 (in): pointer to SPs descriptors buffer ++ * (created by the client) ++ * Out: SPs descriptors returned in the buffer ++ * pointed by data1 ++ */ ++ ++ fill_data = 1; ++ ++ client_desc_idx = 0; ++ ++ /* ++ * number of empty descriptors preallocated by the caller ++ */ ++ client_desc_max_cnt = ++ func_data->data1_size / sizeof(struct ffa_partition_info); ++ ++ ffa_info("Preparing for filling partitions info"); ++ ++ } else { ++ ffa_err("Invalid function arguments provided"); ++ return -EINVAL; ++ } ++ ++ part_uuid = (union ffa_partition_uuid *)func_data->data0; ++ ++ ffa_info("Searching partitions using the provided UUID"); ++ ++#ifdef DEBUG ++ { ++ u32 dbg_uuid_cnt; ++ ++ ffa_dbg("UUID: [LSB]"); ++ ++ for (dbg_uuid_cnt = 0 ; dbg_uuid_cnt < UUID_SIZE ; dbg_uuid_cnt++) ++ ffa_dbg(" %02x", part_uuid->bytes[dbg_uuid_cnt]); ++ } ++#endif ++ ++ /* ++ * search in the cached partitions ++ */ ++ for (desc_idx = 0; ++ desc_idx < ffa_priv_data.partitions.count; ++ desc_idx++) { ++ if (ffa_uuid_are_identical(&ffa_priv_data.partitions.descs[desc_idx].UUID, ++ part_uuid)) { ++ ffa_info("Partition ID %x matches the provided UUID", ++ ffa_priv_data.partitions.descs[desc_idx].info.id); ++ ++ parts_found++; ++ ++ if (fill_data) { ++ /* ++ * trying to fill the partition info in data1 ++ */ ++ ++ if (client_desc_idx < client_desc_max_cnt) { ++ ((struct ffa_partition_info *) ++ func_data->data1)[client_desc_idx++] = ++ ffa_priv_data.partitions.descs[desc_idx].info; ++ continue; ++ } ++ ++ ffa_err("Failed to fill the current descriptor client buffer full"); ++ return -ENOBUFS; ++ } ++ } ++ } ++ ++ if (!parts_found) { ++ int ret; ++ ++ ffa_info("No partition found. Querying framework ..."); ++ ++ ret = ffa_query_partitions_info(part_uuid, &parts_found); ++ ++ if (ret == FFA_ERR_STAT_SUCCESS) { ++ if (!fill_data) { ++ *((u32 *)func_data->data1) = parts_found; ++ ++ ffa_info("Number of partition(s) found matching the UUID: %d", ++ parts_found); ++ } else { ++ /* ++ * we want to read SPs info ++ */ ++ ++ /* ++ * If SPs data filled, retry searching SP info again ++ */ ++ if (parts_found) ++ ret = ffa_get_partitions_info(func_data); ++ else ++ ret = -ENODATA; ++ } ++ } ++ ++ return ret; ++ } ++ ++ /* partition(s) found */ ++ if (!fill_data) ++ *((u32 *)func_data->data1) = parts_found; ++ ++ return FFA_ERR_STAT_SUCCESS; ++} ++ ++/** ++ * ffa_cache_partitions_info - Queries and saves all secure partitions data ++ * ++ * This is a boot time function that invokes FFA_PARTITION_INFO_GET FF-A ++ * function to query from secure world all partitions information. ++ * ++ * The FFA_PARTITION_INFO_GET call is issued with nil UUID as an argument. ++ * All installed partitions information are returned. We cache them in the ++ * resident private data structure and we keep the UUID field empty ++ * (in FF-A 1.0 UUID is not provided by the partition descriptor) ++ * ++ * This function is called at the device probing level. ++ * ffa_cache_partitions_info uses ffa_query_partitions_info to get the data ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++static int ffa_cache_partitions_info(void) ++{ ++ ffa_dbg("[%s]", __func__); ++ return ffa_query_partitions_info(NULL, NULL); ++} ++ ++/** ++ * ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function ++ * @func_data: Pointer to the FF-A function arguments container structure. ++ * The passed arguments: ++ * @data0_size: partition ID size ++ * @data0: pointer to the partition ID ++ * @data1_size: exchanged data size ++ * @data1: pointer to the data buffer preallocated by ++ * the client (in/out) ++ * ++ * This is the runtime function that implements FFA_MSG_SEND_DIRECT_{REQ,RESP} ++ * FF-A functions. ++ * ++ * FFA_MSG_SEND_DIRECT_REQ is used to send the data to the secure partition. ++ * The response from the secure partition is handled by reading the ++ * FFA_MSG_SEND_DIRECT_RESP arguments. ++ * ++ * The maximum size of the data that can be exchanged is 20 bytes which is ++ * sizeof(struct ffa_send_direct_data) as defined by the FF-A specification 1.0 ++ * in the section relevant to FFA_MSG_SEND_DIRECT_{REQ,RESP} ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++static int __ffa_runtime ffa_msg_send_direct_req(struct ffa_interface_data ++ *func_data) ++{ ++ u16 dst_part_id; ++ unsigned long a0 = 0; ++ unsigned long a1 = 0; ++ unsigned long a2 = 0; ++ struct ffa_send_direct_data *msg; ++ struct arm_smccc_res res = {0}; ++ ++ ffa_dbg("[%s]", __func__); ++ ++ if (!ffa_priv_data.invoke_ffa_fn) ++ panic("[FFA] no private data found\n"); ++ ++ if (!func_data) ++ return -EINVAL; ++ ++ if (!ffa_priv_data.partitions.count || !ffa_priv_data.partitions.descs) ++ panic("[FFA] no partition installed\n"); ++ ++ if (func_data->data0_size != sizeof(u16) || ++ !func_data->data0 || ++ func_data->data1_size != FFA_MSG_SEND_DIRECT_MAX_SIZE || ++ !func_data->data1) { ++ ffa_err("Undefined interface parameters"); ++ return -EINVAL; ++ } ++ ++ dst_part_id = *((u16 *)func_data->data0); ++ msg = func_data->data1; ++ ++ ffa_dbg("Sending data to partition ID 0x%x", dst_part_id); ++ ++ a0 = FFA_MSG_SEND_DIRECT_REQ; ++ ++ a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data.id) | ++ PREP_PART_ENDPOINT_ID(dst_part_id); ++ ++ ffa_priv_data.invoke_ffa_fn(a0, a1, a2, ++ msg->a3, ++ msg->a4, ++ msg->a5, ++ msg->a6, ++ msg->a7, ++ &res); ++ ++ while (res.a0 == FFA_INTERRUPT) ++ ffa_priv_data.invoke_ffa_fn(FFA_RUN, res.a1, ++ 0, 0, 0, 0, 0, 0, ++ &res); ++ ++ switch (res.a0) { ++ case FFA_ERROR: ++ { ++ switch (((int)res.a2)) { ++ case FFA_ERR_STAT_INVALID_PARAMETERS: ++ ffa_err("Invalid endpoint ID or non-zero reserved register"); ++ return -EPERM; ++ case FFA_ERR_STAT_ABORTED: ++ panic("[FFA] Message target ran into unexpected error and has aborted\n"); ++ case FFA_ERR_STAT_DENIED: ++ panic("[FFA] Callee is not in a state to handle this request\n"); ++ case FFA_ERR_STAT_NOT_SUPPORTED: ++ panic("[FFA] This function is not implemented at this FF-A instance\n"); ++ case FFA_ERR_STAT_BUSY: ++ panic("[FFA] Message target is busy\n"); ++ default: ++ panic("[FFA] Undefined error (%d)\n", ((int)res.a2)); ++ } ++ } ++ case FFA_SUCCESS: ++ ++ ffa_dbg("Message sent with no response"); ++ return FFA_ERR_STAT_SUCCESS; ++ ++ case FFA_MSG_SEND_DIRECT_RESP: ++ ++ ffa_dbg("Message sent with response"); ++ ++ /* ++ * extract the 32-bit wide return data ++ */ ++ msg->a3 = (u32)res.a3; ++ msg->a4 = (u32)res.a4; ++ msg->a5 = (u32)res.a5; ++ msg->a6 = (u32)res.a6; ++ msg->a7 = (u32)res.a7; ++ ++ return FFA_ERR_STAT_SUCCESS; ++ ++ default: ++ panic("[FFA] Undefined response function (0x%lx)\n", res.a0); ++ } ++} ++ ++/** ++ * invoke_ffa_drv_api - The driver dispatcher function ++ * @func_id: The FF-A function to be used ++ * @func_data: Pointer to the FF-A function arguments container ++ * structure. This also includes pointers to the ++ * returned data needed by clients. ++ * The dispatcher is a runtime function that selects the FF-A function handler ++ * based on the input FF-A function ID. ++ * The input arguments are passed to the handler function. ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++int __ffa_runtime invoke_ffa_drv_api(u32 func_id, ++ struct ffa_interface_data *func_data) ++{ ++ if (!ffa_priv_data.dev) ++ panic("[FFA] no device found\n"); ++ ++ switch (func_id) { ++ case FFA_PARTITION_INFO_GET: ++ return ffa_get_partitions_info(func_data); ++ case FFA_RXTX_UNMAP: ++ return ffa_unmap_rxtx_buffers(); ++ case FFA_MSG_SEND_DIRECT_REQ: ++ return ffa_msg_send_direct_req(func_data); ++ default: ++ ++ ffa_err("Undefined FF-A interface (%d)", func_id); ++ ++ return -EINVAL; ++ } ++} ++ ++/** ++ * ffa_init_private_data - Initialization of the private data ++ * @dev: the arm_ffa device ++ * ++ * This boot time function reads data from the platform data structure ++ * and populates the private data structure ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++static int ffa_init_private_data(struct udevice *dev) ++{ ++ struct ffa_pdata *pdata = dev_get_plat(dev); ++ ++ ffa_priv_data.conduit = pdata->conduit; ++ ++ if (ffa_priv_data.conduit == FFA_CONDUIT_SMC) { ++ ffa_priv_data.invoke_ffa_fn = arm_ffa_smccc_smc; ++ } else { ++ ffa_err("Undefined FF-A conduit (%d)", ffa_priv_data.conduit); ++ return -EINVAL; ++ } ++ ++ ffa_info("Conduit is %s", ++ ((ffa_priv_data.conduit == FFA_CONDUIT_SMC) ? ++ "SMC" : "NOT SUPPORTED")); ++ ++ return FFA_ERR_STAT_SUCCESS; ++} ++ ++/** ++ * ffa_probe - The driver probe function ++ * @dev: the arm_ffa device ++ * ++ * Probing is done at boot time and triggered by the uclass device discovery. ++ * At probe level the following actions are done: ++ * - initialization of the driver private data structure ++ * - querying from secure world the FF-A framework version ++ * - querying from secure world the u-boot endpoint ID ++ * - querying from secure world the supported features of the specified FF-A calls ++ * - mapping the RX/TX buffers ++ * - querying from secure world all the partitions information ++ * ++ * All data queried from secure world is saved in the resident private data structure. ++ * ++ * The probe will fail if either FF-A framework is not detected or the ++ * FF-A requests are not behaving correctly. This ensures that the ++ * driver is not installed and its operations are not exported to the clients. ++ * However, once the driver is successfully probed and an FF-A anomaly is ++ * detected when clients invoke the driver operations, the driver cause ++ * u-boot to panic because the client would not know what to do in such conditions. ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++static int ffa_probe(struct udevice *dev) ++{ ++ int ret; ++ size_t buf_4k_pages = 0; ++ ++ ffa_dbg("[%s]: initializing the FF-A driver", __func__); ++ ++ ret = ffa_init_private_data(dev); ++ ++ if (ret != FFA_ERR_STAT_SUCCESS) ++ return ret; ++ ++ ret = ffa_get_version(); ++ ++ if (ret != FFA_ERR_STAT_SUCCESS) ++ return ret; ++ ++ ret = ffa_get_endpoint_id(); ++ ++ if (ret != FFA_ERR_STAT_SUCCESS) ++ return ret; ++ ++ ret = ffa_get_rxtx_map_features(); ++ ++ if (ret != FFA_ERR_STAT_SUCCESS) ++ return ret; ++ ++ ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages); ++ ++ if (ret != FFA_ERR_STAT_SUCCESS) ++ return ret; ++ ++ ret = ffa_map_rxtx_buffers(buf_4k_pages); ++ ++ if (ret != FFA_ERR_STAT_SUCCESS) ++ return ret; ++ ++ ret = ffa_cache_partitions_info(); ++ ++ if (ret != FFA_ERR_STAT_SUCCESS) { ++ ffa_free_rxtx_buffers(buf_4k_pages); ++ return ret; ++ } ++ ++ ffa_dbg("[%s]: initialization done", __func__); ++ ++ return FFA_ERR_STAT_SUCCESS; ++} ++ ++/** ++ * ffa_of_to_plat - Reads the device tree node ++ * @dev: the arm_ffa device ++ * ++ * This boot time function reads data from the device tree node and populates ++ * the platform data structure ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++static int ffa_of_to_plat(struct udevice *dev) ++{ ++ struct ffa_pdata *pdata = dev_get_plat(dev); ++ const char *conduit; ++ ++ ffa_dbg("[%s]", __func__); ++ ++ conduit = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "method", NULL); ++ ++ if (strcmp("smc", conduit)) { ++ ffa_err("Unsupported conduit"); ++ return -EINVAL; ++ } ++ ++ pdata->conduit = FFA_CONDUIT_SMC; ++ ++ return FFA_ERR_STAT_SUCCESS; ++} ++ ++/** ++ * ffa_drv_ops - The driver operations runtime structure ++ * @invoke_func: The driver dispatcher ++ */ ++struct ffa_ops __ffa_runtime_data ffa_drv_ops = { ++ .invoke_func = invoke_ffa_drv_api ++}; ++ ++/** ++ * ffa_device_get_ops - driver operations getter ++ * ++ * Return: ++ * This runtime function returns a pointer to the driver operations structure ++ */ ++const struct ffa_ops * __ffa_runtime ffa_device_get_ops(void) ++{ ++ return &ffa_drv_ops; ++} ++ ++/** ++ * Defining the device tree compatible string ++ */ ++ ++static const struct udevice_id ffa_match_id[] = { ++ {"arm,ffa", 0}, ++ {}, ++}; ++ ++/** ++ * Declaring the arm_ffa driver under UCLASS_FFA ++ */ ++ ++U_BOOT_DRIVER(arm_ffa) = { ++ .name = "arm_ffa", ++ .of_match = ffa_match_id, ++ .id = UCLASS_FFA, ++ .of_to_plat = ffa_of_to_plat, ++ .probe = ffa_probe, ++ .plat_auto = sizeof(struct ffa_pdata), ++}; +diff --git a/include/arm_ffa.h b/include/arm_ffa.h +new file mode 100644 +index 000000000000..313f46f74764 +--- /dev/null ++++ b/include/arm_ffa.h +@@ -0,0 +1,191 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * (C) Copyright 2021 ARM Limited ++ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> ++ */ ++ ++#ifndef __ARM_FFA_H ++#define __ARM_FFA_H ++ ++#include <linux/arm-smccc.h> ++#include <linux/printk.h> ++ ++/* ++ * This header is public. It can be used by clients to access ++ * data structures and definitions they need ++ */ ++ ++/* ++ * Macros for displaying logs ++ */ ++ ++#define ffa_dbg(fmt, ...) pr_debug("[FFA] " fmt "\n", ##__VA_ARGS__) ++#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) ++#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__) ++ ++/* ++ * The driver operations success error code ++ */ ++#define FFA_ERR_STAT_SUCCESS (0) ++ ++#if CONFIG_IS_ENABLED(EFI_LOADER) ++ ++#include <efi_loader.h> ++ ++/* ++ * __ffa_runtime_data and __ffa_runtime - controls whether data/code are ++ * available after calling the EFI ExitBootServices service. ++ * Data/code tagged with these keywords are resident (available at boot time and ++ * at runtime) ++ */ ++ ++#define __ffa_runtime_data __efi_runtime_data ++#define __ffa_runtime __efi_runtime ++ ++#else ++ ++#define __ffa_runtime_data ++#define __ffa_runtime ++ ++#endif ++ ++/* ++ * Definitions of the Arm FF-A interfaces supported by the Arm FF-A driver ++ */ ++ ++#define FFA_SMC(calling_convention, func_num) \ ++ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, (calling_convention), \ ++ ARM_SMCCC_OWNER_STANDARD, (func_num)) ++ ++#define FFA_SMC_32(func_num) FFA_SMC(ARM_SMCCC_SMC_32, (func_num)) ++ ++#define FFA_VERSION FFA_SMC_32(0x63) ++#define FFA_ID_GET FFA_SMC_32(0x69) ++#define FFA_FEATURES FFA_SMC_32(0x64) ++#define FFA_PARTITION_INFO_GET FFA_SMC_32(0x68) ++#define FFA_RXTX_MAP FFA_SMC_32(0x66) ++#define FFA_RXTX_UNMAP FFA_SMC_32(0x67) ++#define FFA_RX_RELEASE FFA_SMC_32(0x65) ++#define FFA_MSG_SEND_DIRECT_REQ FFA_SMC_32(0x6F) ++#define FFA_MSG_SEND_DIRECT_RESP FFA_SMC_32(0x70) ++#define FFA_RUN FFA_SMC_32(0x6D) ++#define FFA_ERROR FFA_SMC_32(0x60) ++#define FFA_SUCCESS FFA_SMC_32(0x61) ++#define FFA_INTERRUPT FFA_SMC_32(0x62) ++ ++/* ++ * struct ffa_partition_info - Partition information descriptor ++ * @id: Partition ID ++ * @exec_ctxt: Execution context count ++ * @properties: Partition properties ++ * ++ * Data structure containing information about partitions instantiated in the system ++ * This structure is filled with the data queried by FFA_PARTITION_INFO_GET ++ */ ++struct __packed ffa_partition_info { ++ u16 id; ++ u16 exec_ctxt; ++/* partition supports receipt of direct requests */ ++#define FFA_PARTITION_DIRECT_RECV BIT(0) ++/* partition can send direct requests. */ ++#define FFA_PARTITION_DIRECT_SEND BIT(1) ++/* partition can send and receive indirect messages. */ ++#define FFA_PARTITION_INDIRECT_MSG BIT(2) ++ u32 properties; ++}; ++ ++/* ++ * struct ffa_send_direct_data - Data structure hosting the data ++ * used by FFA_MSG_SEND_DIRECT_{REQ,RESP} ++ * @a3-a7: Data read/written from/to w3-w7 registers ++ * ++ * Data structure containing the data to be sent by FFA_MSG_SEND_DIRECT_REQ ++ * or read from FFA_MSG_SEND_DIRECT_RESP ++ */ ++struct __packed ffa_send_direct_data { ++ u32 a3; /* w3 */ ++ u32 a4; /* w4 */ ++ u32 a5; /* w5 */ ++ u32 a6; /* w6 */ ++ u32 a7; /* w7 */ ++}; ++ ++#define FFA_MSG_SEND_DIRECT_MAX_SIZE (sizeof(struct ffa_send_direct_data)) ++ ++/* UUID data size */ ++#define UUID_SIZE (16) ++ ++/* ++ * union ffa_partition_uuid - Data union hosting the UUID ++ * transmitted by FFA_PARTITION_INFO_GET ++ * @words: data structure giving 32-bit words access to the UUID data ++ * @bytes: data structure giving byte access to the UUID data ++ * ++ * The structure holds little-endian UUID data. ++ */ ++union ffa_partition_uuid { ++ struct __packed words { ++ u32 a1; /* w1 */ ++ u32 a2; /* w2 */ ++ u32 a3; /* w3 */ ++ u32 a4; /* w4 */ ++ } words; ++ u8 bytes[UUID_SIZE]; ++}; ++ ++/** ++ * struct ffa_interface_data - generic FF-A interface data structure used to exchange ++ * data between user layers and the driver ++ * @data0_size: size of the first argument ++ * @data0: pointer to the first argument ++ * @data1_size>: size of the second argument ++ * @data1: pointer to the second argument ++ * ++ * Using this structure user layers can pass various types of data with different sizes. ++ * The driver internal functions can detect the nature of this data, verfy compliance ++ * then execute the request when appropriate. ++ */ ++struct ffa_interface_data { ++ u32 data0_size; /* size of the first argument */ ++ void *data0; /* pointer to the first argument */ ++ u32 data1_size; /* size of the second argument */ ++ void *data1; /* pointer to the second argument */ ++}; ++ ++/** ++ * struct ffa_ops - The driver operations structure ++ * @invoke_func: function pointer to the invoke function ++ * ++ * The data structure providing all the operations supported by the driver. ++ * This structure is resident. ++ */ ++struct ffa_ops { ++ /* the driver dispatcher */ ++ int (*invoke_func)(u32 func_id, struct ffa_interface_data *func_data); ++}; ++ ++/** ++ * The device driver and the Uclass driver public functions ++ */ ++ ++/** ++ * ffa_get_invoke_func - performs a call to the FF-A driver dispatcher ++ */ ++int __ffa_runtime ffa_get_invoke_func(u32 func_id, ++ struct ffa_interface_data *func_data); ++ ++/** ++ * ffa_device_get_ops - driver operations getter ++ */ ++const struct ffa_ops * __ffa_runtime ffa_device_get_ops(void); ++ ++/** ++ * ffa_get_device - probes the arm_ffa device ++ */ ++int ffa_get_device(void); ++ ++/** ++ * ffa_init_device - probes the arm_ffa device ++ */ ++int ffa_init_device(void); ++#endif +diff --git a/include/arm_ffa_helper.h b/include/arm_ffa_helper.h +new file mode 100644 +index 000000000000..0e143e54511e +--- /dev/null ++++ b/include/arm_ffa_helper.h +@@ -0,0 +1,45 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * (C) Copyright 2021 ARM Limited ++ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> ++ */ ++ ++#ifndef __ARM_FFA_HELPER_H ++#define __ARM_FFA_HELPER_H ++ ++#include <arm_ffa.h> ++ ++/* ++ * This header is public. Including this header provides all data structures ++ * and definitions needed by clients to use the FF-A transport driver ++ * ++ * It also provides helper functions allowing to pass data and invoke FF-A functions ++ */ ++ ++/** ++ * ffa_helper_get_partitions_info - Wrapper function for FFA_PARTITION_INFO_GET ++ */ ++int ffa_helper_get_partitions_info(struct ffa_interface_data *func_data); ++ ++/** ++ * ffa_helper_unmap_rxtx_buffers - Wrapper function for FFA_RXTX_UNMAP ++ */ ++int ffa_helper_unmap_rxtx_buffers(void); ++ ++/** ++ * ffa_helper_msg_send_direct_req - Wrapper function for ++ * FFA_MSG_SEND_DIRECT_{REQ,RESP} ++ */ ++int __ffa_runtime ffa_helper_msg_send_direct_req(struct ffa_interface_data ++ *func_data); ++ ++/** ++ * ffa_helper_init_device - Wrapper function for probing the arm_ffa device ++ */ ++int ffa_helper_init_device(void); ++ ++/** ++ * ffa_uuid_str_to_bin - Converts a big endian UUID string to a little endian buffer ++ */ ++int ffa_uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin); ++#endif +diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h +index 0e26e1d13824..a1181b8f48e7 100644 +--- a/include/dm/uclass-id.h ++++ b/include/dm/uclass-id.h +@@ -52,6 +52,7 @@ enum uclass_id { + UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ + UCLASS_ETH, /* Ethernet device */ + UCLASS_ETH_PHY, /* Ethernet PHY device */ ++ UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ + UCLASS_FIRMWARE, /* Firmware */ + UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */ + UCLASS_GPIO, /* Bank of general-purpose I/O pins */ +diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h +index 7f2be2339475..54980a130fdb 100644 +--- a/include/linux/arm-smccc.h ++++ b/include/linux/arm-smccc.h +@@ -1,6 +1,8 @@ + /* SPDX-License-Identifier: GPL-2.0 */ + /* + * Copyright (c) 2015, Linaro Limited ++ * (C) Copyright 2021 ARM Limited ++ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> + */ + #ifndef __LINUX_ARM_SMCCC_H + #define __LINUX_ARM_SMCCC_H +@@ -57,13 +59,17 @@ + #include <linux/types.h> + /** + * struct arm_smccc_res - Result from SMC/HVC call +- * @a0-a3 result values from registers 0 to 3 ++ * @a0-a7 result values from registers 0 to 7 + */ + struct arm_smccc_res { + unsigned long a0; + unsigned long a1; + unsigned long a2; + unsigned long a3; ++ unsigned long a4; ++ unsigned long a5; ++ unsigned long a6; ++ unsigned long a7; + }; + + /** +@@ -113,6 +119,26 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1, + unsigned long a5, unsigned long a6, unsigned long a7, + struct arm_smccc_res *res, struct arm_smccc_quirk *quirk); + ++#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) ++/** ++ * __arm_ffa_smccc_smc() - make SMC calls used for FF-A transport ++ * @a0-a7: arguments passed in 64-bit registers x0 to x7 ++ * @res: result values from 64-bit registers x0 to x7 ++ * ++ * This function is used to make SMC calls following SMC32 Calling Convention. ++ * The content of the supplied parameters is copied to registers x0 to x7 prior ++ * to the SMC instruction. The SMC call return data is 32-bit data read from ++ * registers x0 tp x7. ++ */ ++asmlinkage void __arm_ffa_smccc_smc(unsigned long a0, unsigned long a1, ++ unsigned long a2, unsigned long a3, unsigned long a4, ++ unsigned long a5, unsigned long a6, unsigned long a7, ++ struct arm_smccc_res *res); ++ ++#define arm_ffa_smccc_smc __arm_ffa_smccc_smc ++ ++#endif ++ + #define arm_smccc_smc(...) __arm_smccc_smc(__VA_ARGS__, NULL) + + #define arm_smccc_smc_quirk(...) __arm_smccc_smc(__VA_ARGS__) +diff --git a/lib/Kconfig b/lib/Kconfig +index 3c6fa99b1a6a..473821b882e2 100644 +--- a/lib/Kconfig ++++ b/lib/Kconfig +@@ -810,6 +810,7 @@ config SMBIOS_PARSER + source lib/efi/Kconfig + source lib/efi_loader/Kconfig + source lib/optee/Kconfig ++source lib/arm-ffa/Kconfig + + config TEST_FDTDEC + bool "enable fdtdec test" +diff --git a/lib/Makefile b/lib/Makefile +index 11b03d1cbec8..8e6fad613067 100644 +--- a/lib/Makefile ++++ b/lib/Makefile +@@ -9,6 +9,7 @@ obj-$(CONFIG_EFI) += efi/ + obj-$(CONFIG_EFI_LOADER) += efi_driver/ + obj-$(CONFIG_EFI_LOADER) += efi_loader/ + obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += efi_selftest/ ++obj-$(CONFIG_ARM_FFA_TRANSPORT_HELPERS) += arm-ffa/ + obj-$(CONFIG_LZMA) += lzma/ + obj-$(CONFIG_BZIP2) += bzip2/ + obj-$(CONFIG_TIZEN) += tizen/ +diff --git a/lib/arm-ffa/Kconfig b/lib/arm-ffa/Kconfig +new file mode 100644 +index 000000000000..79acbc5a8fe3 +--- /dev/null ++++ b/lib/arm-ffa/Kconfig +@@ -0,0 +1,6 @@ ++config ARM_FFA_TRANSPORT_HELPERS ++ bool "Enable interface helpers for Arm Firmware Framework for Armv8-A" ++ depends on ARM_FFA_TRANSPORT ++ help ++ User layers call FF-A interfaces using helper functions which ++ pass the data and the FF-A function ID to the low level driver +diff --git a/lib/arm-ffa/Makefile b/lib/arm-ffa/Makefile +new file mode 100644 +index 000000000000..c30c0f398126 +--- /dev/null ++++ b/lib/arm-ffa/Makefile +@@ -0,0 +1,8 @@ ++# SPDX-License-Identifier: GPL-2.0+ ++# ++# (C) Copyright 2021 Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> ++# ++ ++# This file only gets included when CONFIG_ARM_FFA_TRANSPORT_HELPERS is set ++ ++obj-y += arm_ffa_helper.o +diff --git a/lib/arm-ffa/arm_ffa_helper.c b/lib/arm-ffa/arm_ffa_helper.c +new file mode 100644 +index 000000000000..623899d38044 +--- /dev/null ++++ b/lib/arm-ffa/arm_ffa_helper.c +@@ -0,0 +1,188 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * (C) Copyright 2021 ARM Limited ++ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> ++ */ ++ ++#include <common.h> ++#include <arm_ffa_helper.h> ++#include <uuid.h> ++ ++/** ++ * ffa_helper_get_partitions_info - Wrapper function for FFA_PARTITION_INFO_GET ++ * ++ * @func_data: Pointer to the FF-A function arguments container ++ * structure. ++ * The passed arguments: ++ * Mode 1: When getting from the driver the number of ++ * secure partitions: ++ * @data0_size: UUID size ++ * @data0: pointer to the UUID (little endian) ++ * @data1_size: size of the number of partitions ++ * variable ++ * @data1: pointer to the number of partitions ++ * variable. The variable will be set ++ * by the driver ++ * Mode 2: When requesting the driver to return the ++ * partitions information: ++ * @data0_size: UUID size ++ * @data0: pointer to the UUID (little endian) ++ * @data1_size: size of the SPs information buffer ++ * @data1: pointer to SPs information buffer ++ * (allocated by the client). ++ * The buffer will be filled by the driver ++ * ++ * This is the boot time function used by clients who wants to get from secure ++ * world the partition(s) information. ++ * ++ * A client of the FF-A driver should know the UUID of the service it wants to ++ * access. It should use the UUID to request the FF-A driver to provide the ++ * partition(s) information of the service. The client should use ++ * ffa_helper_get_partitions_info to pass the UUID information to the driver ++ * which uses PARTITION_INFO_GET to obtain the partition(s) information. ++ * ++ * ffa_helper_get_partitions_info should be called twice. First call is to get ++ * from the driver the number of secure partitions (SPs) associated to a ++ * particular UUID. Then, the caller (client) allocates the buffer to host the ++ * SPs data and issues a 2nd call. Then, the driver fills the SPs data in the ++ * pre-allocated buffer. ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++int ffa_helper_get_partitions_info(struct ffa_interface_data *func_data) ++{ ++ return ffa_get_invoke_func(FFA_PARTITION_INFO_GET, func_data); ++} ++ ++/** ++ * ffa_helper_unmap_rxtx_buffers - Wrapper function for FFA_RXTX_UNMAP ++ * ++ * This is the boot time function that allows clients to unmap the RX/TX ++ * buffers ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++int ffa_helper_unmap_rxtx_buffers(void) ++{ ++ return ffa_get_invoke_func(FFA_RXTX_UNMAP, NULL); ++} ++ ++/** ++ * ffa_helper_msg_send_direct_req - Wrapper function for ++ * FFA_MSG_SEND_DIRECT_{REQ,RESP} ++ * @func_data: Pointer to the FF-A function arguments container structure. ++ * The passed arguments: ++ * @data0_size: partition ID size ++ * @data0: pointer to the partition ID ++ * @data1_size: exchanged data size ++ * @data1: pointer to the data buffer preallocated by the client (in/out) ++ * ++ * This is the runtime function that allows clients to send data to the secure ++ * world partitions. The arm_ffa driver uses FFA_MSG_SEND_DIRECT_REQ to send the ++ * data to the secure partition. The response from the secure partition is ++ * handled internally by the driver using FFA_MSG_SEND_DIRECT_RESP and returned ++ * to ffa_helper_msg_send_direct_req through @func_data ++ * ++ * The maximum size of the data that can be exchanged is 20 bytes which is ++ * sizeof(struct ffa_send_direct_data) as defined by the FF-A specification 1.0 ++ * in the section relevant to FFA_MSG_SEND_DIRECT_{REQ,RESP} ++ * ++ * The client should pre-allocate a buffer pointed by @data1 which the size ++ * is sizeof(struct ffa_send_direct_data) ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++int __ffa_runtime ffa_helper_msg_send_direct_req(struct ffa_interface_data ++ *func_data) ++{ ++ return ffa_get_invoke_func(FFA_MSG_SEND_DIRECT_REQ, func_data); ++} ++ ++/** ++ * ffa_helper_init_device - Wrapper function for probing the arm_ffa device ++ * ++ * This boot time function should be called to probe the arm_ffa device so ++ * it becomes ready for use. ++ * To achieve that, this function is called automatically at initcalls ++ * level (after u-boot relocation). ++ * ++ * Return: ++ * ++ * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure ++ */ ++int ffa_helper_init_device(void) ++{ ++ return ffa_init_device(); ++} ++ ++/** ++ * ffa_uuid_str_to_bin - Converts a big endian UUID string to a little endian buffer ++ * @uuid_str: UUID string in big endian format (36 bytes wide + '/0') ++ * @uuid_bin: preallocated 16 bytes UUID buffer in little endian format ++ * ++ * UUID binary format used by the FF-A framework (16 bytes): ++ * ++ * [LSB] 4B-2B-2B-2B-6B (little endian data fields) ++ * ++ * UUID string is 36 length of characters (36 bytes): ++ * ++ * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx ++ * be be be be be ++ * ++ * where x is a hexadecimal character. Fields are separated by '-'s. ++ * When converting to a binary UUID, these endianness rules apply: ++ * be: means the field in the string is considered a big endian hex number ++ * and should be converted to little endian binary format ++ * ++ * Return: ++ * ++ * uuid_bin filled with little endian UUID data ++ * On success 0 is returned. Otherwise, failure code. ++ */ ++int ffa_uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin) ++{ ++ u16 tmp16 = 0; ++ u32 tmp32 = 0; ++ u64 tmp64 = 0; ++ ++ if (!uuid_str_valid(uuid_str) || !uuid_bin) ++ return -EINVAL; ++ ++ /* ++ * reverse bytes from big to little endian ++ */ ++ tmp32 = simple_strtoul(uuid_str, NULL, 16); ++ memcpy(uuid_bin, &tmp32, 4); ++ ++ /* ++ * reverse bytes from big to little endian ++ */ ++ tmp16 = simple_strtoul(uuid_str + 9, NULL, 16); ++ memcpy(uuid_bin + 4, &tmp16, 2); ++ ++ /* ++ * reverse bytes from big to little endian ++ */ ++ tmp16 = simple_strtoul(uuid_str + 14, NULL, 16); ++ memcpy(uuid_bin + 6, &tmp16, 2); ++ ++ /* ++ * reverse bytes from big to little endian ++ */ ++ tmp16 = simple_strtoul(uuid_str + 19, NULL, 16); ++ memcpy(uuid_bin + 8, &tmp16, 2); ++ ++ /* ++ * reverse bytes from big to little endian ++ */ ++ tmp64 = simple_strtoull(uuid_str + 24, NULL, 16); ++ memcpy(uuid_bin + 10, (char *)&tmp64, 6); ++ ++ return 0; ++} +diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c +index 5bcb8253edba..cffa2c69d621 100644 +--- a/lib/efi_loader/efi_boottime.c ++++ b/lib/efi_loader/efi_boottime.c +@@ -23,6 +23,10 @@ + #include <asm/setjmp.h> + #include <linux/libfdt_env.h> + ++#if defined(CONFIG_ARM_FFA_TRANSPORT) ++#include <arm_ffa_helper.h> ++#endif ++ + DECLARE_GLOBAL_DATA_PTR; + + /* Task priority level */ +@@ -2114,6 +2118,10 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, + struct efi_event *evt, *next_event; + efi_status_t ret = EFI_SUCCESS; + ++#if defined(CONFIG_ARM_FFA_TRANSPORT) ++ int ffa_ret; ++#endif ++ + EFI_ENTRY("%p, %zx", image_handle, map_key); + + /* Check that the caller has read the current memory map */ +@@ -2174,6 +2182,15 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, + dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); + } + ++#if defined(CONFIG_ARM_FFA_TRANSPORT) ++ /* unmap FF-A RX/TX buffers */ ++ ffa_ret = ffa_helper_unmap_rxtx_buffers(); ++ if (ffa_ret) ++ debug("[efi_boottime][ERROR]: can not unmap FF-A RX/TX buffers\n"); ++ else ++ debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n"); ++#endif ++ + /* Patch out unsupported runtime function */ + efi_runtime_detach(); + +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0007-arm_ffa-introducing-armffa-command.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0007-arm_ffa-introducing-armffa-command.patch new file mode 100644 index 0000000000..83f0547ea9 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0007-arm_ffa-introducing-armffa-command.patch @@ -0,0 +1,342 @@ +From 541b2b51dc77832ab5845cab4762d29976838d36 Mon Sep 17 00:00:00 2001 +From: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> +Date: Tue, 16 Nov 2021 12:36:27 +0000 +Subject: [PATCH 07/27] arm_ffa: introducing armffa command + +A new armffa command is provided as an example of how to use +the FF-A helper functions to communicate with secure world. + +The armffa command allows to query secure partitions data from +the secure world and exchanging messages with the partitions. + +Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + MAINTAINERS | 1 + + cmd/Kconfig | 10 ++ + cmd/Makefile | 2 + + cmd/armffa.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 279 insertions(+) + create mode 100644 cmd/armffa.c + +diff --git a/MAINTAINERS b/MAINTAINERS +index 14307e6da644..f3fd559da54a 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -235,6 +235,7 @@ F: include/configs/turris_*.h + ARM FF-A + M: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> + S: Maintained ++F: cmd/armffa.c + F: drivers/arm-ffa/ + F: include/arm_ffa.h + F: include/arm_ffa_helper.h +diff --git a/cmd/Kconfig b/cmd/Kconfig +index ff50102a89c7..ff124bf4bad0 100644 +--- a/cmd/Kconfig ++++ b/cmd/Kconfig +@@ -813,6 +813,16 @@ endmenu + + menu "Device access commands" + ++config CMD_ARMFFA ++ bool "Arm FF-A test command" ++ depends on ARM_FFA_TRANSPORT ++ help ++ Provides a test command for the Arm FF-A driver ++ supported options: ++ - Listing the partition(s) info ++ - Sending a data pattern to the specified partition ++ - Displaying the arm_ffa device info ++ + config CMD_ARMFLASH + #depends on FLASH_CFI_DRIVER + bool "armflash" +diff --git a/cmd/Makefile b/cmd/Makefile +index 166c652d9825..770b846c44e0 100644 +--- a/cmd/Makefile ++++ b/cmd/Makefile +@@ -12,6 +12,8 @@ obj-y += panic.o + obj-y += version.o + + # command ++ ++obj-$(CONFIG_CMD_ARMFFA) += armffa.o + obj-$(CONFIG_CMD_ACPI) += acpi.o + obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o + obj-$(CONFIG_CMD_AES) += aes.o +diff --git a/cmd/armffa.c b/cmd/armffa.c +new file mode 100644 +index 000000000000..71a6ebb656d1 +--- /dev/null ++++ b/cmd/armffa.c +@@ -0,0 +1,266 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * (C) Copyright 2021 ARM Limited ++ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> ++ */ ++ ++#include <arm_ffa_helper.h> ++#include <asm/io.h> ++#include <common.h> ++#include <command.h> ++#include <dm.h> ++#include <mapmem.h> ++#include <stdlib.h> ++ ++/** ++ * do_ffa_get_singular_partition_info - implementation of the getpart subcommand ++ * @cmdtp: Command Table ++ * @flag: flags ++ * @argc: number of arguments ++ * @argv: arguments ++ * ++ * This function queries the secure partition information which the UUID is provided ++ * as an argument. The function uses the arm_ffa driver helper function ++ * to retrieve the data. ++ * The input UUID string is expected to be in big endian format. ++ * ++ * Return: ++ * ++ * CMD_RET_SUCCESS: on success, otherwise failure ++ */ ++static int do_ffa_get_singular_partition_info(struct cmd_tbl *cmdtp, int flag, int argc, ++ char *const argv[]) ++{ ++ struct ffa_interface_data func_data = {0}; ++ u32 count = 0; ++ int ret; ++ union ffa_partition_uuid service_uuid = {0}; ++ struct ffa_partition_info *parts_info; ++ u32 info_idx; ++ ++ if (argc != 1) ++ return -EINVAL; ++ ++ if (ffa_uuid_str_to_bin(argv[0], (unsigned char *)&service_uuid)) { ++ ffa_err("Invalid UUID"); ++ return -EINVAL; ++ } ++ ++ /* ++ * get from the driver the count of the SPs matching the UUID ++ */ ++ func_data.data0_size = sizeof(service_uuid); ++ func_data.data0 = &service_uuid; ++ func_data.data1_size = sizeof(count); ++ func_data.data1 = &count; ++ ++ ret = ffa_helper_get_partitions_info(&func_data); ++ if (ret != FFA_ERR_STAT_SUCCESS) { ++ ffa_err("Failure in querying partitions count (error code: %d)", ret); ++ return ret; ++ } ++ ++ if (!count) { ++ ffa_info("No secure partition found"); ++ return ret; ++ } ++ ++ /* ++ * pre-allocate a buffer to be filled by the driver ++ * with ffa_partition_info structs ++ */ ++ ++ parts_info = calloc(count, sizeof(struct ffa_partition_info)); ++ if (!parts_info) ++ return -EINVAL; ++ ++ ffa_info("Pre-allocating %d partition(s) info structures", count); ++ ++ func_data.data1_size = count * sizeof(struct ffa_partition_info); ++ func_data.data1 = parts_info; ++ ++ /* ++ * ask the driver to fill the buffer with the SPs info ++ */ ++ ret = ffa_helper_get_partitions_info(&func_data); ++ if (ret != FFA_ERR_STAT_SUCCESS) { ++ ffa_err("Failure in querying partition(s) info (error code: %d)", ret); ++ free(parts_info); ++ return ret; ++ } ++ ++ /* ++ * SPs found , show the partition information ++ */ ++ for (info_idx = 0; info_idx < count ; info_idx++) { ++ ffa_info("Partition: id = 0x%x , exec_ctxt 0x%x , properties 0x%x", ++ parts_info[info_idx].id, ++ parts_info[info_idx].exec_ctxt, ++ parts_info[info_idx].properties); ++ } ++ ++ free(parts_info); ++ ++ return 0; ++} ++ ++/** ++ * do_ffa_msg_send_direct_req - implementation of the ping subcommand ++ * @cmdtp: Command Table ++ * @flag: flags ++ * @argc: number of arguments ++ * @argv: arguments ++ * ++ * This function sends data to the secure partition which the ID is provided ++ * as an argument. The function uses the arm_ffa driver helper function ++ * to send data. ++ * ++ * Return: ++ * ++ * CMD_RET_SUCCESS: on success, otherwise failure ++ */ ++int do_ffa_msg_send_direct_req(struct cmd_tbl *cmdtp, int flag, int argc, ++ char *const argv[]) ++{ ++ struct ffa_interface_data func_data = {0}; ++ struct ffa_send_direct_data msg = {0}; ++ u32 pattern = 0xaabbccd0; ++ u16 part_id; ++ int ret; ++ ++ if (argc != 1) ++ return -EINVAL; ++ ++ errno = 0; ++ part_id = strtoul(argv[0], NULL, 16); ++ ++ if (errno) { ++ ffa_err("Invalid partition ID"); ++ return -EINVAL; ++ } ++ ++ /* ++ * telling the driver which partition to use ++ */ ++ func_data.data0_size = sizeof(part_id); ++ func_data.data0 = &part_id; ++ ++ /* ++ * filling the message data ++ */ ++ msg.a3 = ++pattern; ++ msg.a4 = ++pattern; ++ msg.a5 = ++pattern; ++ msg.a6 = ++pattern; ++ msg.a7 = ++pattern; ++ func_data.data1_size = sizeof(msg); ++ func_data.data1 = &msg; ++ ++ ret = ffa_helper_msg_send_direct_req(&func_data); ++ if (ret == FFA_ERR_STAT_SUCCESS) { ++ u8 cnt; ++ ++ ffa_info("SP response:\n[LSB]"); ++ for (cnt = 0; ++ cnt < sizeof(struct ffa_send_direct_data) / sizeof(u32); ++ cnt++) ++ ffa_info("0x%x", ((u32 *)&msg)[cnt]); ++ } else { ++ ffa_err("Sending direct request error (%d)", ret); ++ } ++ ++ return ret; ++} ++ ++/** ++ *do_ffa_dev_list - implementation of the devlist subcommand ++ * @cmdtp: [in] Command Table ++ * @flag: flags ++ * @argc: number of arguments ++ * @argv: arguments ++ * ++ * This function queries the devices belonging to the UCLASS_FFA ++ * class. Currently, one device is expected to show up: the arm_ffa device ++ * ++ * Return: ++ * ++ * CMD_RET_SUCCESS: on success, otherwise failure ++ */ ++int do_ffa_dev_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ struct udevice *dev = NULL; ++ int i, ret; ++ ++ ffa_info("arm_ffa uclass entries:"); ++ ++ for (i = 0, ret = uclass_first_device(UCLASS_FFA, &dev); ++ dev; ++ ret = uclass_next_device(&dev), i++) { ++ if (ret) ++ break; ++ ++ ffa_info("entry %d - instance %08x, ops %08x, plat %08x", ++ i, ++ (u32)map_to_sysmem(dev), ++ (u32)map_to_sysmem(dev->driver->ops), ++ (u32)map_to_sysmem(dev_get_plat(dev))); ++ } ++ ++ return cmd_process_error(cmdtp, ret); ++} ++ ++static struct cmd_tbl armffa_commands[] = { ++ U_BOOT_CMD_MKENT(getpart, 1, 1, do_ffa_get_singular_partition_info, "", ""), ++ U_BOOT_CMD_MKENT(ping, 1, 1, do_ffa_msg_send_direct_req, "", ""), ++ U_BOOT_CMD_MKENT(devlist, 0, 1, do_ffa_dev_list, "", ""), ++}; ++ ++/** ++ * do_armffa - the armffa command main function ++ * @cmdtp: Command Table ++ * @flag: flags ++ * @argc: number of arguments ++ * @argv: arguments ++ * ++ * This function identifies which armffa subcommand to run. ++ * Then, it makes sure the arm_ffa device is probed and ++ * ready for use. ++ * Then, it runs the subcommand. ++ * ++ * Return: ++ * ++ * CMD_RET_SUCCESS: on success, otherwise failure ++ */ ++static int do_armffa(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) ++{ ++ struct cmd_tbl *armffa_cmd; ++ int ret; ++ ++ if (argc < 2) ++ return CMD_RET_USAGE; ++ ++ armffa_cmd = find_cmd_tbl(argv[1], armffa_commands, ARRAY_SIZE(armffa_commands)); ++ ++ argc -= 2; ++ argv += 2; ++ ++ if (!armffa_cmd || argc > armffa_cmd->maxargs) ++ return CMD_RET_USAGE; ++ ++ ret = ffa_helper_init_device(); ++ if (ret != FFA_ERR_STAT_SUCCESS) ++ return cmd_process_error(cmdtp, ret); ++ ++ ret = armffa_cmd->cmd(armffa_cmd, flag, argc, argv); ++ ++ return cmd_process_error(armffa_cmd, ret); ++} ++ ++U_BOOT_CMD(armffa, 4, 1, do_armffa, ++ "Arm FF-A operations test command", ++ "getpart <partition UUID>\n" ++ " - lists the partition(s) info\n" ++ "ping <partition ID>\n" ++ " - sends a data pattern to the specified partition\n" ++ "devlist\n" ++ " - displays the arm_ffa device info\n"); +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0008-arm_ffa-introducing-MM-communication-with-FF-A.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0008-arm_ffa-introducing-MM-communication-with-FF-A.patch new file mode 100644 index 0000000000..9b1383ebe4 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0008-arm_ffa-introducing-MM-communication-with-FF-A.patch @@ -0,0 +1,383 @@ +From 2f09d4a2e87febd7365b9e18d669208ff2c35edc Mon Sep 17 00:00:00 2001 +From: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> +Date: Wed, 13 Oct 2021 17:51:44 +0100 +Subject: [PATCH 08/27] arm_ffa: introducing MM communication with FF-A + +This commit allows to perform MM communication using FF-A transport. + +The MM SP (also called partition) can be StandAlonneMM or smm-gateway. +Both partitions run in OP-TEE. + +When using the u-boot FF-A driver, StandAlonneMM and smm-gateway are +supported. + +On EFI services such as GetVariable()/SetVariable(), the data +is copied from the communication buffer to the MM shared buffer. + +Then, notifies the MM SP about data availability in the MM shared buffer. +Communication with the MM SP is performed using FF-A transport. + +On such event, MM SP can read the data and updates the MM shared buffer +with response data. + +The response data is copied back to the communication buffer. + +Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + lib/efi_loader/Kconfig | 14 +- + lib/efi_loader/efi_variable_tee.c | 265 +++++++++++++++++++++++++++++- + 2 files changed, 273 insertions(+), 6 deletions(-) + +diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig +index e5e35fe51f65..6827b821545e 100644 +--- a/lib/efi_loader/Kconfig ++++ b/lib/efi_loader/Kconfig +@@ -56,13 +56,23 @@ config EFI_VARIABLE_FILE_STORE + stored as file /ubootefi.var on the EFI system partition. + + config EFI_MM_COMM_TEE +- bool "UEFI variables storage service via OP-TEE" +- depends on OPTEE ++ bool "UEFI variables storage service via the trusted world" ++ depends on OPTEE || ARM_FFA_TRANSPORT + help ++ The MM SP (also called partition) can be StandAlonneMM or smm-gateway. ++ When using the u-boot OP-TEE driver, StandAlonneMM is supported. ++ When using the u-boot FF-A driver, StandAlonneMM and smm-gateway are supported. ++ + If OP-TEE is present and running StandAloneMM, dispatch all UEFI + variable related operations to that. The application will verify, + authenticate and store the variables on an RPMB. + ++ When ARM_FFA_TRANSPORT is used, dispatch all UEFI variable related ++ operations to the MM SP running under Optee in the trusted world. ++ A door bell mechanism is used to notify the SP when there is data in the shared ++ MM buffer. The data is copied by u-boot to thea shared buffer before issuing ++ the door bell event. ++ + endchoice + + config EFI_VARIABLES_PRESEED +diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c +index dfef18435dfa..9cb8cfb9c779 100644 +--- a/lib/efi_loader/efi_variable_tee.c ++++ b/lib/efi_loader/efi_variable_tee.c +@@ -15,6 +15,28 @@ + #include <malloc.h> + #include <mm_communication.h> + ++#if (IS_ENABLED(CONFIG_OPTEE)) ++#define OPTEE_PAGE_SIZE BIT(12) ++#endif ++ ++#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) ++ ++#include <arm_ffa_helper.h> ++#include <mapmem.h> ++ ++/* MM return codes */ ++#define MM_SUCCESS (0) ++ ++#define ARM_SVC_ID_SP_EVENT_COMPLETE_AARCH64 (0xC4000061) ++#define ARM_SVC_ID_SP_EVENT_COMPLETE ARM_SVC_ID_SP_EVENT_COMPLETE_AARCH64 ++ ++/* MM_SP_UUID_DATA defined by the platform */ ++union ffa_partition_uuid mm_sp_svc_uuid = {.bytes = {MM_SP_UUID_DATA}}; ++ ++static u16 __efi_runtime_data mm_sp_id; ++ ++#endif ++ + extern struct efi_var_file __efi_runtime_data *efi_var_buf; + static efi_uintn_t max_buffer_size; /* comm + var + func + data */ + static efi_uintn_t max_payload_size; /* func + data */ +@@ -24,6 +46,7 @@ struct mm_connection { + u32 session; + }; + ++#if (IS_ENABLED(CONFIG_OPTEE)) + /** + * get_connection() - Retrieve OP-TEE session for a specific UUID. + * +@@ -143,16 +166,229 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) + + return ret; + } ++#endif ++ ++#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) ++ ++/** ++ * ffa_notify_mm_sp() - Announce there is data in the shared buffer ++ * ++ * Notifies the MM partition in the trusted world that ++ * data is available in the shared buffer. ++ * This is a blocking call during which trusted world has exclusive access ++ * to the MM shared buffer. ++ * ++ * Return: ++ * ++ * 0 on success ++ */ ++static int __efi_runtime ffa_notify_mm_sp(void) ++{ ++ struct ffa_interface_data func_data = {0}; ++ struct ffa_send_direct_data msg = {0}; ++ int ret; ++ u32 sp_event_complete; ++ int sp_event_ret; ++ ++ func_data.data0_size = sizeof(mm_sp_id); ++ func_data.data0 = &mm_sp_id; ++ ++ msg.a3 = FFA_SHARED_MM_BUFFER_ADDR; ++ msg.a4 = FFA_SHARED_MM_BUFFER_SIZE; ++ func_data.data1_size = sizeof(msg); ++ func_data.data1 = &msg; ++ ++ ret = ffa_helper_msg_send_direct_req(&func_data); ++ if (ret != FFA_ERR_STAT_SUCCESS) { ++ log_err("EFI: Failure to notify the MM SP , FF-A error (%d)\n", ret); ++ return ret; ++ } ++ ++ sp_event_complete = msg.a3; ++ sp_event_ret = (int)msg.a4; ++ ++ if (sp_event_complete == ARM_SVC_ID_SP_EVENT_COMPLETE && sp_event_ret == MM_SUCCESS) ++ return 0; ++ ++ log_err("EFI: Failure to notify the MM SP (0x%x , %d)\n", ++ sp_event_complete, ++ sp_event_ret); ++ ++ return -EACCES; ++} ++ ++/** ++ * ffa_discover_mm_sp_id() - Query the MM partition ID ++ * ++ * Use the FF-A driver to get the MM partition ID. ++ * If multiple partitions are found, use the first one ++ * ++ * Return: ++ * ++ * 0 on success ++ */ ++static int __efi_runtime ffa_discover_mm_sp_id(void) ++{ ++ struct ffa_interface_data func_data = {0}; ++ u32 count = 0; ++ int ret; ++ struct ffa_partition_info *parts_info; ++ ++ /* ++ * get from the driver the count of the SPs matching the UUID ++ */ ++ func_data.data0_size = sizeof(mm_sp_svc_uuid); ++ func_data.data0 = &mm_sp_svc_uuid; ++ func_data.data1_size = sizeof(count); ++ func_data.data1 = &count; ++ ++ ret = ffa_helper_get_partitions_info(&func_data); ++ if (ret != FFA_ERR_STAT_SUCCESS) { ++ log_err("EFI: Failure in querying partitions count (error code: %d)\n", ret); ++ return ret; ++ } ++ ++ if (!count) { ++ log_info("EFI: No MM partition found\n"); ++ return ret; ++ } ++ ++ /* ++ * pre-allocate a buffer to be filled by the driver ++ * with ffa_partition_info structs ++ */ ++ ++ parts_info = calloc(count, sizeof(struct ffa_partition_info)); ++ if (!parts_info) ++ return -EINVAL; ++ ++ log_info("EFI: Pre-allocating %d partition(s) info structures\n", count); ++ ++ func_data.data1_size = count * ++ sizeof(struct ffa_partition_info); ++ func_data.data1 = parts_info; ++ ++ /* ++ * ask the driver to fill the ++ * buffer with the SPs info ++ */ ++ ret = ffa_helper_get_partitions_info(&func_data); ++ if (ret != FFA_ERR_STAT_SUCCESS) { ++ log_err("EFI: Failure in querying partition(s) info (error code: %d)\n", ret); ++ free(parts_info); ++ return ret; ++ } ++ ++ /* ++ * MM SPs found , use the first one ++ */ ++ ++ mm_sp_id = parts_info[0].id; ++ ++ log_info("EFI: MM partition ID 0x%x\n", mm_sp_id); ++ ++ free(parts_info); ++ ++ return 0; ++} + + /** +- * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send ++ * ffa_mm_communicate() - Exchange EFI services data with the MM partition using FF-A ++ * @comm_buf: locally allocated communication buffer used for for rx/tx ++ * @dsize: communication buffer size ++ * ++ * Issues a door bell event to notify the MM partition (SP) running in OP-TEE ++ * that there is data to read from the shared buffer. ++ * Communication with the MM SP is performed using FF-A transport. ++ * On the event, MM SP can read the data from the buffer and ++ * update the MM shared buffer with response data. ++ * The response data is copied back to the communication buffer. ++ * ++ * Return: ++ * ++ * EFI status code ++ */ ++static efi_status_t __efi_runtime ffa_mm_communicate(void *comm_buf, ulong comm_buf_size) ++{ ++ ulong tx_data_size; ++ int ffa_ret; ++ struct efi_mm_communicate_header *mm_hdr; ++ void *virt_shared_buf; ++ ++ if (!comm_buf) ++ return EFI_INVALID_PARAMETER; ++ ++ /* Discover MM partition ID */ ++ if (!mm_sp_id && ffa_discover_mm_sp_id() != FFA_ERR_STAT_SUCCESS) { ++ log_err("EFI: Failure to discover MM partition ID\n"); ++ return EFI_UNSUPPORTED; ++ } ++ ++ mm_hdr = (struct efi_mm_communicate_header *)comm_buf; ++ tx_data_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t); ++ ++ if (comm_buf_size != tx_data_size || tx_data_size > FFA_SHARED_MM_BUFFER_SIZE) ++ return EFI_INVALID_PARAMETER; ++ ++ /* Copy the data to the shared buffer */ ++ ++ virt_shared_buf = (void *)map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_ADDR, 0); ++ efi_memcpy_runtime(virt_shared_buf, comm_buf, tx_data_size); ++ ++ /* Announce there is data in the shared buffer */ ++ ++ ffa_ret = ffa_notify_mm_sp(); ++ if (ffa_ret) ++ unmap_sysmem(virt_shared_buf); ++ ++ switch (ffa_ret) { ++ case 0: ++ { ++ ulong rx_data_size; ++ /* Copy the MM SP response from the shared buffer to the communication buffer */ ++ rx_data_size = ((struct efi_mm_communicate_header *)virt_shared_buf)->message_len + ++ sizeof(efi_guid_t) + ++ sizeof(size_t); ++ ++ if (rx_data_size > comm_buf_size) { ++ unmap_sysmem(virt_shared_buf); ++ return EFI_OUT_OF_RESOURCES; ++ } ++ ++ efi_memcpy_runtime(comm_buf, virt_shared_buf, rx_data_size); ++ unmap_sysmem(virt_shared_buf); ++ ++ return EFI_SUCCESS; ++ } ++ case -EINVAL: ++ return EFI_DEVICE_ERROR; ++ case -EPERM: ++ return EFI_INVALID_PARAMETER; ++ case -EACCES: ++ return EFI_ACCESS_DENIED; ++ case -EBUSY: ++ return EFI_OUT_OF_RESOURCES; ++ default: ++ return EFI_ACCESS_DENIED; ++ } ++} ++#endif ++ ++/** ++ * mm_communicate() - Adjust the communication buffer to the MM SP and send + * it to OP-TEE + * +- * @comm_buf: locally allocted communcation buffer ++ * @comm_buf: locally allocted communication buffer + * @dsize: buffer size ++ * ++ * The MM SP (also called partition) can be StandAlonneMM or smm-gateway. ++ * The comm_buf format is the same for both partitions. ++ * When using the u-boot OP-TEE driver, StandAlonneMM is supported. ++ * When using the u-boot FF-A driver, StandAlonneMM and smm-gateway are supported. ++ * + * Return: status code + */ +-static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) ++static efi_status_t __efi_runtime mm_communicate(u8 *comm_buf, efi_uintn_t dsize) + { + efi_status_t ret; + struct efi_mm_communicate_header *mm_hdr; +@@ -162,7 +398,11 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) + mm_hdr = (struct efi_mm_communicate_header *)comm_buf; + var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data; + ++ #if (IS_ENABLED(CONFIG_OPTEE)) + ret = optee_mm_communicate(comm_buf, dsize); ++ #elif (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) ++ ret = ffa_mm_communicate(comm_buf, dsize); ++ #endif + if (ret != EFI_SUCCESS) { + log_err("%s failed!\n", __func__); + return ret; +@@ -258,6 +498,23 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) + goto out; + } + *size = var_payload->size; ++ ++ #if (IS_ENABLED(CONFIG_OPTEE)) ++ /* ++ * Although the max payload is configurable on StMM, we only share a ++ * single page from OP-TEE for the non-secure buffer used to communicate ++ * with StMM. Since OP-TEE will reject to map anything bigger than that, ++ * make sure we are in bounds. ++ */ ++ if (*size > OPTEE_PAGE_SIZE) ++ *size = OPTEE_PAGE_SIZE - MM_COMMUNICATE_HEADER_SIZE - ++ MM_VARIABLE_COMMUNICATE_SIZE; ++ #elif (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) ++ if (*size > FFA_SHARED_MM_BUFFER_SIZE) ++ *size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE - ++ MM_VARIABLE_COMMUNICATE_SIZE; ++ #endif ++ + /* + * There seems to be a bug in EDK2 miscalculating the boundaries and + * size checks, so deduct 2 more bytes to fulfill this requirement. Fix +@@ -697,7 +954,7 @@ void efi_variables_boot_exit_notify(void) + ret = EFI_NOT_FOUND; + + if (ret != EFI_SUCCESS) +- log_err("Unable to notify StMM for ExitBootServices\n"); ++ log_err("Unable to notify the MM partition for ExitBootServices\n"); + free(comm_buf); + + /* +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0009-arm_ffa-introducing-test-module-for-UCLASS_FFA.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0009-arm_ffa-introducing-test-module-for-UCLASS_FFA.patch new file mode 100644 index 0000000000..4fb317ba85 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0009-arm_ffa-introducing-test-module-for-UCLASS_FFA.patch @@ -0,0 +1,132 @@ +From c9a2c457648b732292482fae59a7fd61cefffd33 Mon Sep 17 00:00:00 2001 +From: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> +Date: Tue, 16 Nov 2021 12:38:48 +0000 +Subject: [PATCH 09/27] arm_ffa: introducing test module for UCLASS_FFA + +This is the test module for the UCLASS_FFA class. + +Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + MAINTAINERS | 1 + + test/dm/Makefile | 1 + + test/dm/ffa.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ + test/dm/ffa.h | 19 ++++++++++++++++ + 4 files changed, 77 insertions(+) + create mode 100644 test/dm/ffa.c + create mode 100644 test/dm/ffa.h + +diff --git a/MAINTAINERS b/MAINTAINERS +index f3fd559da54a..6510f844fe09 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -240,6 +240,7 @@ F: drivers/arm-ffa/ + F: include/arm_ffa.h + F: include/arm_ffa_helper.h + F: lib/arm-ffa/ ++F: test/dm/ffa.c + + ARM FREESCALE IMX + M: Stefano Babic <sbabic@denx.de> +diff --git a/test/dm/Makefile b/test/dm/Makefile +index d46552fbf320..ddac250cdff0 100644 +--- a/test/dm/Makefile ++++ b/test/dm/Makefile +@@ -79,6 +79,7 @@ obj-$(CONFIG_POWER_DOMAIN) += power-domain.o + obj-$(CONFIG_ACPI_PMC) += pmc.o + obj-$(CONFIG_DM_PMIC) += pmic.o + obj-$(CONFIG_DM_PWM) += pwm.o ++obj-$(CONFIG_ARM_FFA_TRANSPORT) += ffa.o + obj-$(CONFIG_QFW) += qfw.o + obj-$(CONFIG_RAM) += ram.o + obj-y += regmap.o +diff --git a/test/dm/ffa.c b/test/dm/ffa.c +new file mode 100644 +index 000000000000..b937cea57b80 +--- /dev/null ++++ b/test/dm/ffa.c +@@ -0,0 +1,56 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Test for UCLASS_FFA class ++ * ++ * (C) Copyright 2021 ARM Limited ++ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> ++ */ ++ ++#include <common.h> ++#include <dm.h> ++#include <dm/test.h> ++#include <test/test.h> ++#include <test/ut.h> ++#include <arm_ffa_helper.h> ++#include "ffa.h" ++ ++/* Basic test of 'armffa' command */ ++static int dm_test_armffa_cmd(struct unit_test_state *uts) ++{ ++ ut_assertok(ffa_helper_init_device()); ++ ++ ut_assertok(console_record_reset_enable()); ++ ++ /* armffa getpart <UUID> */ ++ ut_assertok(run_command("armffa getpart " SE_PROXY_PARTITION_UUID, 0)); ++ ut_assert_console_end(); ++ ++ /* armffa ping <ID> */ ++ ut_assertok(run_command("armffa ping " SE_PROXY_PARTITION_ID, 0)); ++ ut_assert_console_end(); ++ ++ /* armffa devlist */ ++ ut_assertok(run_command("armffa devlist", 0)); ++ ut_assert_console_end(); ++ ++ return 0; ++} ++ ++DM_TEST(dm_test_armffa_cmd, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC); ++ ++static int test_ffa_msg_send_direct_req(void) ++{ ++ char *const argv[1] = {SE_PROXY_PARTITION_ID}; /* Corstone1000 SE Proxy ID */ ++ ++ return do_ffa_msg_send_direct_req(NULL, 0, 1, argv); ++} ++ ++/* Basic test of the FFA uclass */ ++static int dm_test_ffa_uclass(struct unit_test_state *uts) ++{ ++ ut_assertok(ffa_init_device()); ++ ut_assertok(test_ffa_msg_send_direct_req()); ++ return 0; ++} ++ ++DM_TEST(dm_test_ffa_uclass, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); +diff --git a/test/dm/ffa.h b/test/dm/ffa.h +new file mode 100644 +index 000000000000..a0802bd6928a +--- /dev/null ++++ b/test/dm/ffa.h +@@ -0,0 +1,19 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * (C) Copyright 2021 ARM Limited ++ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> ++ */ ++ ++#ifndef __TEST_DM_FFA_H ++#define __TEST_DM_FFA_H ++ ++#define SE_PROXY_PARTITION_ID "0x8002" ++#define SE_PROXY_PARTITION_UUID "46bb39d1-b4d9-45b5-88ff-040027dab249" ++ ++/** ++ * do_ffa_msg_send_direct_req - implementation of the ping subcommand ++ */ ++int do_ffa_msg_send_direct_req(struct cmd_tbl *cmdtp, int flag, int argc, ++ char *const argv[]); ++ ++#endif /*__TEST_DM_FFA_H */ +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0010-arm_ffa-corstone1000-enable-FF-A-and-MM-support.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0010-arm_ffa-corstone1000-enable-FF-A-and-MM-support.patch new file mode 100644 index 0000000000..bc96fc4619 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0010-arm_ffa-corstone1000-enable-FF-A-and-MM-support.patch @@ -0,0 +1,57 @@ +From ce6598d255113458fd5c9d19bb7469b721e37f6f Mon Sep 17 00:00:00 2001 +From: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> +Date: Tue, 2 Nov 2021 16:44:39 +0000 +Subject: [PATCH 10/27] arm_ffa: corstone1000: enable FF-A and MM support + +This commit allows corstone1000 platform to perform +MM communication between u-boot and the secure world +using FF-A transport. + +Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + configs/corstone1000_defconfig | 1 + + include/configs/corstone1000.h | 15 +++++++++++++++ + 2 files changed, 16 insertions(+) + +diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig +index e573fe6fe6a2..b042d4e49419 100644 +--- a/configs/corstone1000_defconfig ++++ b/configs/corstone1000_defconfig +@@ -44,6 +44,7 @@ CONFIG_USB=y + CONFIG_DM_USB=y + CONFIG_USB_ISP1760=y + CONFIG_USB_STORAGE=y ++CONFIG_ARM_FFA_TRANSPORT=y + CONFIG_EFI_MM_COMM_TEE=y + # CONFIG_OPTEE is not set + # CONFIG_GENERATE_SMBIOS_TABLE is not set +diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h +index 8ba0effb0ab2..afc9ccfc192b 100644 +--- a/include/configs/corstone1000.h ++++ b/include/configs/corstone1000.h +@@ -14,6 +14,21 @@ + + #include <linux/sizes.h> + ++/* MM SP UUID binary data (little-endian format) */ ++#define MM_SP_UUID_DATA \ ++ 0xed, 0x32, 0xd5, 0x33, \ ++ 0x99, 0xe6, 0x42, 0x09, \ ++ 0x9c, 0xc0, 0x2d, 0x72, \ ++ 0xcd, 0xd9, 0x98, 0xa7 ++ ++#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */ ++ ++/* ++ * shared buffer physical address used for communication between ++ * u-boot and the MM SP ++ */ ++#define FFA_SHARED_MM_BUFFER_ADDR (0x023F8000) ++ + #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x03f00000) + #define CONFIG_SKIP_LOWLEVEL_INIT + +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0011-efi-corstone1000-introduce-EFI-capsule-update.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0011-efi-corstone1000-introduce-EFI-capsule-update.patch new file mode 100644 index 0000000000..cb24ec3e14 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0011-efi-corstone1000-introduce-EFI-capsule-update.patch @@ -0,0 +1,359 @@ +From e70d0128090158872847b82b82cdbcf0e2f13885 Mon Sep 17 00:00:00 2001 +From: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> +Date: Thu, 11 Nov 2021 16:27:59 +0000 +Subject: [PATCH 11/27] efi: corstone1000: introduce EFI capsule update + +This commit provides capsule update feature for Corstone1000. + +This feature is available before and after ExitBootServices(). + +A platform specific capsule buffer is allocated. This buffer +is physically contiguous and allocated at the start of the DDR +memory after u-boot relocation to the end of DDR. + +The capsule buffer is shared between u-boot and the secure world. +On UpdateCapsule() , capsule data is copied to the buffer +and a buffer ready event is generated using FF-A transport. + +On ExitBootServices() a kernel started event is sent to the +SE Proxy FW update service. This event is generated on each boot. + +Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + include/configs/corstone1000.h | 18 +++++ + include/efi_loader.h | 4 +- + lib/efi_loader/efi_boottime.c | 47 ++++++++++++ + lib/efi_loader/efi_capsule.c | 135 ++++++++++++++++++++++++++++++++- + lib/efi_loader/efi_setup.c | 15 ++++ + 5 files changed, 215 insertions(+), 4 deletions(-) + +diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h +index afc9ccfc192b..a400cdef69d0 100644 +--- a/include/configs/corstone1000.h ++++ b/include/configs/corstone1000.h +@@ -14,6 +14,24 @@ + + #include <linux/sizes.h> + ++/* The SE Proxy partition ID*/ ++#define CORSTONE1000_SEPROXY_PART_ID (0x8002) ++ ++/* Update service ID provided by the SE Proxy secure partition*/ ++#define CORSTONE1000_SEPROXY_UPDATE_SVC_ID (0x4) ++ ++/* Notification events used with SE Proxy update service */ ++#define CORSTONE1000_BUFFER_READY_EVT (0x1) ++#define CORSTONE1000_KERNEL_STARTED_EVT (0x2) ++ ++/* Size in 4KB pages of the EFI capsule buffer */ ++#define CORSTONE1000_CAPSULE_BUFFER_SIZE (8192) /* 32 MB */ ++ ++/* Capsule GUID */ ++#define EFI_CORSTONE1000_CAPSULE_ID_GUID \ ++ EFI_GUID(0x3a770ddc, 0x409b, 0x48b2, 0x81, 0x41, \ ++ 0x93, 0xb7, 0xc6, 0x0b, 0x20, 0x9e) ++ + /* MM SP UUID binary data (little-endian format) */ + #define MM_SP_UUID_DATA \ + 0xed, 0x32, 0xd5, 0x33, \ +diff --git a/include/efi_loader.h b/include/efi_loader.h +index 126db279dd3e..01b432e6184b 100644 +--- a/include/efi_loader.h ++++ b/include/efi_loader.h +@@ -965,11 +965,11 @@ extern const struct efi_firmware_management_protocol efi_fmp_fit; + extern const struct efi_firmware_management_protocol efi_fmp_raw; + + /* Capsule update */ +-efi_status_t EFIAPI efi_update_capsule( ++efi_status_t __efi_runtime EFIAPI efi_update_capsule( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 scatter_gather_list); +-efi_status_t EFIAPI efi_query_capsule_caps( ++efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 *maximum_capsule_size, +diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c +index cffa2c69d621..5c77a40c3ebe 100644 +--- a/lib/efi_loader/efi_boottime.c ++++ b/lib/efi_loader/efi_boottime.c +@@ -2096,6 +2096,44 @@ static void efi_exit_caches(void) + #endif + } + ++#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000) ++/** ++ * efi_corstone1000_kernel_started_event - notifies SE Proxy FW update service ++ * ++ * This function notifies the SE Proxy update service that the kernel has already started ++ * ++ * Return: ++ * ++ * 0: on success, otherwise failure ++ */ ++static int efi_corstone1000_kernel_started_event(void) ++{ ++ struct ffa_interface_data func_data = {0}; ++ struct ffa_send_direct_data msg = {0}; ++ u16 part_id = CORSTONE1000_SEPROXY_PART_ID; ++ ++ log_debug("[%s]\n", __func__); ++ ++ /* ++ * telling the driver which partition to use ++ */ ++ func_data.data0_size = sizeof(part_id); ++ func_data.data0 = &part_id; ++ ++ /* ++ * setting the kernel started event arguments ++ */ ++ msg.a3 = CORSTONE1000_SEPROXY_UPDATE_SVC_ID; ++ msg.a5 = CORSTONE1000_KERNEL_STARTED_EVT; ++ ++ func_data.data1_size = sizeof(msg); ++ func_data.data1 = &msg; ++ ++ return ffa_helper_msg_send_direct_req(&func_data); ++} ++ ++#endif ++ + /** + * efi_exit_boot_services() - stop all boot services + * @image_handle: handle of the loaded image +@@ -2209,6 +2247,15 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, + /* Recalculate CRC32 */ + efi_update_table_header_crc32(&systab.hdr); + ++#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000) ++ /* Notifying SE Proxy FW update service */ ++ ffa_ret = efi_corstone1000_kernel_started_event(); ++ if (ffa_ret) ++ debug("[efi_boottime][ERROR]: Failure to notify SE Proxy FW update service\n"); ++ else ++ debug("[efi_boottime][INFO]: SE Proxy FW update service notified\n"); ++#endif ++ + /* Give the payload some time to boot */ + efi_set_watchdog(0); + WATCHDOG_RESET(); +diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c +index f00440163d41..c100c1b95298 100644 +--- a/lib/efi_loader/efi_capsule.c ++++ b/lib/efi_loader/efi_capsule.c +@@ -24,6 +24,14 @@ + #include <crypto/pkcs7_parser.h> + #include <linux/err.h> + ++#ifdef CONFIG_TARGET_CORSTONE1000 ++#include <arm_ffa_helper.h> ++#include <cpu_func.h> ++ ++void *__efi_runtime_data corstone1000_capsule_buf; /* capsule shared buffer virtual address */ ++efi_guid_t corstone1000_capsule_guid = EFI_CORSTONE1000_CAPSULE_ID_GUID; ++#endif ++ + DECLARE_GLOBAL_DATA_PTR; + + const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID; +@@ -509,6 +517,89 @@ static efi_status_t efi_capsule_update_firmware( + } + #endif /* CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT */ + ++#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000) ++ ++/** ++ * efi_corstone1000_alloc_capsule_shared_buf - allocate capsule shared buffer ++ * @capsule_image_size: The capsule data (header + payload) ++ * ++ * This function allocates the physically contiguous buffer shared between u-boot ++ * and the secure world. On UpdateCapsule() capsule data is copied to the buffer ++ * and a door bell event is generated. ++ * The buffer is allocated at the start of the DDR memory after u-boot has been relocated ++ * to the end of DDR. ++ * ++ * Return: ++ * ++ * 0: on success, otherwise failure ++ */ ++efi_status_t efi_corstone1000_alloc_capsule_shared_buf(void) ++{ ++ efi_status_t efi_ret; ++ u64 ram_base = CONFIG_SYS_SDRAM_BASE; ++ ++ log_debug("[%s]\n", __func__); ++ ++ efi_ret = efi_allocate_pages(EFI_ALLOCATE_ADDRESS, ++ EFI_RUNTIME_SERVICES_DATA, ++ CORSTONE1000_CAPSULE_BUFFER_SIZE, ++ &ram_base); ++ ++ if (efi_ret != EFI_SUCCESS) { ++ corstone1000_capsule_buf = NULL; ++ log_err("EFI: Corstone1000: Allocating capsule shared buffer error (%d)\n" ++ , (int)efi_ret); ++ return efi_ret; ++ } ++ ++ log_info("EFI: Corstone1000: Capsule shared buffer at 0x%x , size %d pages\n" ++ , (unsigned int)ram_base, ++ CORSTONE1000_CAPSULE_BUFFER_SIZE); ++ ++ corstone1000_capsule_buf = (void *)map_sysmem((phys_addr_t)ram_base, 0); ++ ++ return EFI_SUCCESS; ++} ++ ++/** ++ * efi_corstone1000_buffer_ready_event - issue door bell event ++ * @capsule_image_size: The capsule data (header + payload) ++ * ++ * This function notifies the SE Proxy update service that capsule data is available ++ * in the capsule shared buffer. ++ * ++ * Return: ++ * ++ * 0: on success, otherwise failure ++ */ ++static int __efi_runtime efi_corstone1000_buffer_ready_event(u32 capsule_image_size) ++{ ++ struct ffa_interface_data func_data = {0}; ++ struct ffa_send_direct_data msg = {0}; ++ u16 part_id = CORSTONE1000_SEPROXY_PART_ID; ++ ++ log_debug("[%s]\n", __func__); ++ ++ /* ++ * telling the driver which partition to use ++ */ ++ func_data.data0_size = sizeof(part_id); ++ func_data.data0 = &part_id; ++ ++ /* ++ * setting the buffer ready event arguments ++ */ ++ msg.a3 = CORSTONE1000_SEPROXY_UPDATE_SVC_ID; ++ msg.a4 = capsule_image_size; ++ msg.a5 = CORSTONE1000_BUFFER_READY_EVT; ++ ++ func_data.data1_size = sizeof(msg); ++ func_data.data1 = &msg; ++ ++ return ffa_helper_msg_send_direct_req(&func_data); ++} ++#endif ++ + /** + * efi_update_capsule() - process information from operating system + * @capsule_header_array: Array of virtual address pointers +@@ -522,7 +613,7 @@ static efi_status_t efi_capsule_update_firmware( + * + * Return: status code + */ +-efi_status_t EFIAPI efi_update_capsule( ++efi_status_t __efi_runtime EFIAPI efi_update_capsule( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 scatter_gather_list) +@@ -539,6 +630,13 @@ efi_status_t EFIAPI efi_update_capsule( + goto out; + } + ++#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000) ++ if (capsule_count != 1 || !corstone1000_capsule_buf) { ++ ret = EFI_INVALID_PARAMETER; ++ goto out; ++ } ++#endif ++ + ret = EFI_SUCCESS; + for (i = 0, capsule = *capsule_header_array; i < capsule_count; + i++, capsule = *(++capsule_header_array)) { +@@ -551,6 +649,39 @@ efi_status_t EFIAPI efi_update_capsule( + + log_debug("Capsule[%d] (guid:%pUs)\n", + i, &capsule->capsule_guid); ++ ++#if CONFIG_IS_ENABLED(TARGET_CORSTONE1000) ++ if (guidcmp(&corstone1000_capsule_guid, &capsule->capsule_guid)) { ++ ret = EFI_INVALID_PARAMETER; ++ log_err("Corstone1000: Invalid capsule GUID\n"); ++ goto out; ++ } ++ ++ if (efi_size_in_pages(capsule->capsule_image_size) > ++ CORSTONE1000_CAPSULE_BUFFER_SIZE) { ++ log_err("Corstone1000: Capsule data size exceeds the shared buffer size\n"); ++ ret = EFI_BUFFER_TOO_SMALL; ++ goto out; ++ } ++ ++ /* copy the data to the contiguous buffer */ ++ efi_memcpy_runtime(corstone1000_capsule_buf, capsule, capsule->capsule_image_size); ++ ++ /* invalidate the data cache */ ++ invalidate_dcache_all(); ++ ++ /* issue buffer ready event */ ++ ret = efi_corstone1000_buffer_ready_event(capsule->capsule_image_size); ++ if (ret) { ++ log_err("EFI: Corstone1000: Buffer ready event error (%d)\n", (int)ret); ++ ret = EFI_DEVICE_ERROR; ++ } else { ++ ret = EFI_SUCCESS; ++ } ++ ++ goto out; ++#endif ++ + if (!guidcmp(&capsule->capsule_guid, + &efi_guid_firmware_management_capsule_id)) { + ret = efi_capsule_update_firmware(capsule); +@@ -589,7 +720,7 @@ out: + * + * Return: status code + */ +-efi_status_t EFIAPI efi_query_capsule_caps( ++efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 *maximum_capsule_size, +diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c +index eee54e48784f..989380d4f8cd 100644 +--- a/lib/efi_loader/efi_setup.c ++++ b/lib/efi_loader/efi_setup.c +@@ -16,6 +16,13 @@ + + efi_status_t efi_obj_list_initialized = OBJ_LIST_NOT_INITIALIZED; + ++#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000) ++/** ++ * efi_corstone1000_alloc_capsule_shared_buf - allocate capsule shared buffer ++ */ ++extern efi_status_t efi_corstone1000_alloc_capsule_shared_buf(void); ++#endif ++ + /* + * Allow unaligned memory access. + * +@@ -128,6 +135,14 @@ static efi_status_t efi_init_capsule(void) + { + efi_status_t ret = EFI_SUCCESS; + ++#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000) ++ ret = efi_corstone1000_alloc_capsule_shared_buf(); ++ if (ret != EFI_SUCCESS) { ++ printf("EFI: Corstone-1000: cannot allocate caspsule shared buffer\n"); ++ return ret; ++ } ++#endif ++ + if (IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_UPDATE)) { + ret = efi_set_variable_int(u"CapsuleMax", + &efi_guid_capsule_report, +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0012-corstone1000-Update-FFA-shared-buffer-address.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0012-corstone1000-Update-FFA-shared-buffer-address.patch new file mode 100644 index 0000000000..60dc8501b4 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0012-corstone1000-Update-FFA-shared-buffer-address.patch @@ -0,0 +1,37 @@ +From b2d752b4bbd5b2dc4cb22d2d652a261287505926 Mon Sep 17 00:00:00 2001 +From: Gowtham Suresh Kumar <gowtham.sureshkumar@arm.com> +Date: Wed, 17 Nov 2021 15:28:06 +0000 +Subject: [PATCH 12/27] corstone1000: Update FFA shared buffer address + +FFA shared buffer address changed to 0x02000000. + +The existing address 0x023F8000 is currently being used by +Optee so the virtual address returned to the SMM gateway is 0x0000. +So the buffer is moved to 0x02000000. + +Signed-off-by: Gowtham Suresh Kumar <gowtham.sureshkumar@arm.com> +%% original patch: 0025-Update-FFA-shared-buffer-address.patch + +%% original patch: 0025-Update-FFA-shared-buffer-address.patch + +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + include/configs/corstone1000.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h +index a400cdef69d0..db0f91335cef 100644 +--- a/include/configs/corstone1000.h ++++ b/include/configs/corstone1000.h +@@ -45,7 +45,7 @@ + * shared buffer physical address used for communication between + * u-boot and the MM SP + */ +-#define FFA_SHARED_MM_BUFFER_ADDR (0x023F8000) ++#define FFA_SHARED_MM_BUFFER_ADDR (0x02000000) + + #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x03f00000) + #define CONFIG_SKIP_LOWLEVEL_INIT +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0013-corstone1000-Make-sure-shared-buffer-contents-are-no.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0013-corstone1000-Make-sure-shared-buffer-contents-are-no.patch new file mode 100644 index 0000000000..2495538d91 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0013-corstone1000-Make-sure-shared-buffer-contents-are-no.patch @@ -0,0 +1,52 @@ +From 67a755f74716068cfd44a8897c31151fe9ee4328 Mon Sep 17 00:00:00 2001 +From: Gowtham Suresh Kumar <gowtham.sureshkumar@arm.com> +Date: Thu, 18 Nov 2021 16:42:59 +0000 +Subject: [PATCH 13/27] corstone1000: Make sure shared buffer contents are not + cached + +After updating the shared buffer, it is required to flush the cache +to ensure that the secure world sees expected the shared buffer +contents. + +The MM communication shared buffer is configured in device region of optee +which has cache disabled. So we need to invalidate the cache every time we +update the buffer on uboot otherwise the secure world does not see the +accurate values. + +Signed-off-by: Gowtham Suresh Kumar <gowtham.sureshkumar@arm.com> +%% original patch: 0027-Make-sure-shared-buffer-contents-are-not-cached.patch + +%% original patch: 0027-Make-sure-shared-buffer-contents-are-not-cached.patch + +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + lib/efi_loader/efi_variable_tee.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c +index 9cb8cfb9c779..b6be2b54a030 100644 +--- a/lib/efi_loader/efi_variable_tee.c ++++ b/lib/efi_loader/efi_variable_tee.c +@@ -22,6 +22,7 @@ + #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + + #include <arm_ffa_helper.h> ++#include <cpu_func.h> + #include <mapmem.h> + + /* MM return codes */ +@@ -335,6 +336,11 @@ static efi_status_t __efi_runtime ffa_mm_communicate(void *comm_buf, ulong comm_ + virt_shared_buf = (void *)map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_ADDR, 0); + efi_memcpy_runtime(virt_shared_buf, comm_buf, tx_data_size); + ++ /* The secure world has cache disabled for device region which we use for shared buffer ++ So, the secure world reads the data from DDR. Let's flush the cache so the DDR is ++ updated with the latest data */ ++ invalidate_dcache_all(); ++ + /* Announce there is data in the shared buffer */ + + ffa_ret = ffa_notify_mm_sp(); +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0014-arm-corstone1000-fix-unrecognized-filesystem-type.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0014-arm-corstone1000-fix-unrecognized-filesystem-type.patch new file mode 100644 index 0000000000..fa201eb9c8 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0014-arm-corstone1000-fix-unrecognized-filesystem-type.patch @@ -0,0 +1,30 @@ +From e2463e3ef52260b38131085c0901de8708d52693 Mon Sep 17 00:00:00 2001 +From: Rui Miguel Silva <rui.silva@linaro.org> +Date: Fri, 4 Mar 2022 15:56:09 +0000 +Subject: [PATCH 14/27] arm: corstone1000: fix unrecognized filesystem type + +Some usb sticks are not recognized by usb, just add a +delay before checking status. + +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + common/usb_storage.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/common/usb_storage.c b/common/usb_storage.c +index c9e2d7343ce2..ae72338323ba 100644 +--- a/common/usb_storage.c ++++ b/common/usb_storage.c +@@ -769,6 +769,9 @@ static int usb_stor_BBB_transport(struct scsi_cmd *srb, struct us_data *us) + st: + retry = 0; + again: ++ if (srb->cmd[0] == SCSI_TST_U_RDY) ++ mdelay(100); ++ + debug("STATUS phase\n"); + result = usb_bulk_msg(us->pusb_dev, pipein, csw, UMASS_BBB_CSW_SIZE, + &actlen, USB_CNTL_TIMEOUT*5); +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0015-efi_capsule-corstone1000-pass-interface-id-and-buffe.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0015-efi_capsule-corstone1000-pass-interface-id-and-buffe.patch new file mode 100644 index 0000000000..0d1912c880 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0015-efi_capsule-corstone1000-pass-interface-id-and-buffe.patch @@ -0,0 +1,73 @@ +From 81bf9ed7e8e858cef13cfc3d1435c44445e523df Mon Sep 17 00:00:00 2001 +From: Vishnu Banavath <vishnu.banavath@arm.com> +Date: Fri, 10 Dec 2021 20:03:35 +0000 +Subject: [PATCH 15/27] efi_capsule: corstone1000: pass interface id and buffer + event id using register w4 + +Initially the interface/event IDs are passed to the SP using register +w3 and w5. + +Now the SE proxy SP requires this arguments to be in register w4. + +This change is to pass interface ID(31:16) and event ID(15:0) +to SE proxy SP to trigger an event to secure enclave about +firmware update. + +Signed-off-by: Vishnu Banavath <vishnu.banavath@arm.com> +Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + include/configs/corstone1000.h | 6 ++++++ + lib/efi_loader/efi_capsule.c | 11 +++++++---- + 2 files changed, 13 insertions(+), 4 deletions(-) + +diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h +index db0f91335cef..a7445e61348b 100644 +--- a/include/configs/corstone1000.h ++++ b/include/configs/corstone1000.h +@@ -24,6 +24,12 @@ + #define CORSTONE1000_BUFFER_READY_EVT (0x1) + #define CORSTONE1000_KERNEL_STARTED_EVT (0x2) + ++#define PREP_SEPROXY_SVC_ID_MASK GENMASK(31, 16) ++#define PREP_SEPROXY_SVC_ID(x) (FIELD_PREP(PREP_SEPROXY_SVC_ID_MASK, (x))) ++ ++#define PREP_SEPROXY_EVT_MASK GENMASK(15, 0) ++#define PREP_SEPROXY_EVT(x) (FIELD_PREP(PREP_SEPROXY_EVT_MASK, (x))) ++ + /* Size in 4KB pages of the EFI capsule buffer */ + #define CORSTONE1000_CAPSULE_BUFFER_SIZE (8192) /* 32 MB */ + +diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c +index c100c1b95298..17d769803b2a 100644 +--- a/lib/efi_loader/efi_capsule.c ++++ b/lib/efi_loader/efi_capsule.c +@@ -27,6 +27,8 @@ + #ifdef CONFIG_TARGET_CORSTONE1000 + #include <arm_ffa_helper.h> + #include <cpu_func.h> ++#include <linux/bitfield.h> ++#include <linux/bitops.h> + + void *__efi_runtime_data corstone1000_capsule_buf; /* capsule shared buffer virtual address */ + efi_guid_t corstone1000_capsule_guid = EFI_CORSTONE1000_CAPSULE_ID_GUID; +@@ -587,11 +589,12 @@ static int __efi_runtime efi_corstone1000_buffer_ready_event(u32 capsule_image_s + func_data.data0 = &part_id; + + /* +- * setting the buffer ready event arguments ++ * setting the buffer ready event arguments in register w4: ++ * - capsule update interface ID (31:16) ++ * - the buffer ready event ID (15:0) + */ +- msg.a3 = CORSTONE1000_SEPROXY_UPDATE_SVC_ID; +- msg.a4 = capsule_image_size; +- msg.a5 = CORSTONE1000_BUFFER_READY_EVT; ++ msg.a4 = PREP_SEPROXY_SVC_ID(CORSTONE1000_SEPROXY_UPDATE_SVC_ID) | ++ PREP_SEPROXY_EVT(CORSTONE1000_BUFFER_READY_EVT); + + func_data.data1_size = sizeof(msg); + func_data.data1 = &msg; +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0016-efi_boottime-corstone1000-pass-interface-id-and-kern.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0016-efi_boottime-corstone1000-pass-interface-id-and-kern.patch new file mode 100644 index 0000000000..f460fad8ec --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0016-efi_boottime-corstone1000-pass-interface-id-and-kern.patch @@ -0,0 +1,57 @@ +From 10d0ffc26ddcecd83921c2b3b37cb4eff54a154f Mon Sep 17 00:00:00 2001 +From: Vishnu Banavath <vishnu.banavath@arm.com> +Date: Fri, 10 Dec 2021 20:10:41 +0000 +Subject: [PATCH 16/27] efi_boottime: corstone1000: pass interface id and + kernel event id using register w4 + +Initially the interface/event IDs are passed to the SP using register +w3 and w5. + +Now the SE proxy SP requires this arguments to be in register w4. + +This change is to pass interface ID(31:16) and kernel started +event ID(15:0) to SE proxy SP to trigger an event to +secure enclave just before ExitbootService(). + +Signed-off-by: Vishnu Banavath <vishnu.banavath@arm.com> +Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + lib/efi_loader/efi_boottime.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c +index 5c77a40c3ebe..b58a8c98fd05 100644 +--- a/lib/efi_loader/efi_boottime.c ++++ b/lib/efi_loader/efi_boottime.c +@@ -27,6 +27,11 @@ + #include <arm_ffa_helper.h> + #endif + ++#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000) ++#include <linux/bitfield.h> ++#include <linux/bitops.h> ++#endif ++ + DECLARE_GLOBAL_DATA_PTR; + + /* Task priority level */ +@@ -2121,10 +2126,12 @@ static int efi_corstone1000_kernel_started_event(void) + func_data.data0 = &part_id; + + /* +- * setting the kernel started event arguments ++ * setting the kernel started event arguments: ++ * setting capsule update interface ID(31:16) ++ * the kernel started event ID(15:0) + */ +- msg.a3 = CORSTONE1000_SEPROXY_UPDATE_SVC_ID; +- msg.a5 = CORSTONE1000_KERNEL_STARTED_EVT; ++ msg.a4 = PREP_SEPROXY_SVC_ID(CORSTONE1000_SEPROXY_UPDATE_SVC_ID) | ++ PREP_SEPROXY_EVT(CORSTONE1000_KERNEL_STARTED_EVT); + + func_data.data1_size = sizeof(msg); + func_data.data1 = &msg; +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0017-efi_loader-corstone1000-remove-guid-check-from-corst.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0017-efi_loader-corstone1000-remove-guid-check-from-corst.patch new file mode 100644 index 0000000000..fa6ab326ad --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0017-efi_loader-corstone1000-remove-guid-check-from-corst.patch @@ -0,0 +1,52 @@ +From c463798489e41725f8ba33debeedc7c4011cda38 Mon Sep 17 00:00:00 2001 +From: Vishnu Banavath <vishnu.banavath@arm.com> +Date: Sat, 11 Dec 2021 13:23:55 +0000 +Subject: [PATCH 17/27] efi_loader: corstone1000: remove guid check from + corstone1000 config option + +Use generic fmp guid and no separte check is required for +CORSTONE1000 target. + +Signed-off-by: Vishnu Banavath <vishnu.banavath@arm.com> +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + lib/efi_loader/efi_capsule.c | 16 +--------------- + 1 file changed, 1 insertion(+), 15 deletions(-) + +diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c +index 17d769803b2a..939040d2755e 100644 +--- a/lib/efi_loader/efi_capsule.c ++++ b/lib/efi_loader/efi_capsule.c +@@ -654,12 +654,6 @@ efi_status_t __efi_runtime EFIAPI efi_update_capsule( + i, &capsule->capsule_guid); + + #if CONFIG_IS_ENABLED(TARGET_CORSTONE1000) +- if (guidcmp(&corstone1000_capsule_guid, &capsule->capsule_guid)) { +- ret = EFI_INVALID_PARAMETER; +- log_err("Corstone1000: Invalid capsule GUID\n"); +- goto out; +- } +- + if (efi_size_in_pages(capsule->capsule_image_size) > + CORSTONE1000_CAPSULE_BUFFER_SIZE) { + log_err("Corstone1000: Capsule data size exceeds the shared buffer size\n"); +@@ -685,15 +679,7 @@ efi_status_t __efi_runtime EFIAPI efi_update_capsule( + goto out; + #endif + +- if (!guidcmp(&capsule->capsule_guid, +- &efi_guid_firmware_management_capsule_id)) { +- ret = efi_capsule_update_firmware(capsule); +- } else { +- log_err("Unsupported capsule type: %pUs\n", +- &capsule->capsule_guid); +- ret = EFI_UNSUPPORTED; +- } +- ++ ret = efi_capsule_update_firmware(capsule); + if (ret != EFI_SUCCESS) + goto out; + } +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0018-arm_ffa-removing-the-cast-when-using-binary-OR-on-FI.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0018-arm_ffa-removing-the-cast-when-using-binary-OR-on-FI.patch new file mode 100644 index 0000000000..4ee10a0be1 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0018-arm_ffa-removing-the-cast-when-using-binary-OR-on-FI.patch @@ -0,0 +1,40 @@ +From 1cfca60850727448bdbfe720d98d9e0d4523f6aa Mon Sep 17 00:00:00 2001 +From: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> +Date: Sat, 11 Dec 2021 21:05:10 +0000 +Subject: [PATCH 18/27] arm_ffa: removing the cast when using binary OR on + FIELD_PREP macros + +When the GENMASK used is above 16-bits wide a u16 cast will cause +loss of data. + +This commit fixes that. + +Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + drivers/arm-ffa/arm_ffa_prv.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/arm-ffa/arm_ffa_prv.h b/drivers/arm-ffa/arm_ffa_prv.h +index 38ea4ba83efc..d0db3ef508a1 100644 +--- a/drivers/arm-ffa/arm_ffa_prv.h ++++ b/drivers/arm-ffa/arm_ffa_prv.h +@@ -40,13 +40,13 @@ + + #define PREP_SELF_ENDPOINT_ID_MASK GENMASK(31, 16) + #define PREP_SELF_ENDPOINT_ID(x) \ +- ((u16)(FIELD_PREP(PREP_SELF_ENDPOINT_ID_MASK, (x)))) ++ (FIELD_PREP(PREP_SELF_ENDPOINT_ID_MASK, (x))) + + /* Partition endpoint ID mask (partition with which u-boot communicates with) */ + + #define PREP_PART_ENDPOINT_ID_MASK GENMASK(15, 0) + #define PREP_PART_ENDPOINT_ID(x) \ +- ((u16)(FIELD_PREP(PREP_PART_ENDPOINT_ID_MASK, (x)))) ++ (FIELD_PREP(PREP_PART_ENDPOINT_ID_MASK, (x))) + + /* The FF-A SMC function prototype definition */ + +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0019-Return-proper-error-code-when-rx-buffer-is-larger.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0019-Return-proper-error-code-when-rx-buffer-is-larger.patch new file mode 100644 index 0000000000..21a89a4001 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0019-Return-proper-error-code-when-rx-buffer-is-larger.patch @@ -0,0 +1,31 @@ +From 7db27eeaba0fd5ddb1e49977bb7e342a1980aa3d Mon Sep 17 00:00:00 2001 +From: Gowtham Suresh Kumar <gowtham.sureshkumar@arm.com> +Date: Sun, 12 Dec 2021 17:51:17 +0000 +Subject: [PATCH 19/27] Return proper error code when rx buffer is larger + +ffa_mm_communicate should return EFI_BUFFER_TOO_SMALL when +the buffer received from the secure world is larger than the +comm buffer as this value is forwarded by mm_communicate. + +Signed-off-by: Gowtham Suresh Kumar <gowtham.sureshkumar@arm.com> +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + lib/efi_loader/efi_variable_tee.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c +index b6be2b54a030..38655a9dbb7c 100644 +--- a/lib/efi_loader/efi_variable_tee.c ++++ b/lib/efi_loader/efi_variable_tee.c +@@ -358,7 +358,7 @@ static efi_status_t __efi_runtime ffa_mm_communicate(void *comm_buf, ulong comm_ + + if (rx_data_size > comm_buf_size) { + unmap_sysmem(virt_shared_buf); +- return EFI_OUT_OF_RESOURCES; ++ return EFI_BUFFER_TOO_SMALL; + } + + efi_memcpy_runtime(comm_buf, virt_shared_buf, rx_data_size); +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0020-Use-correct-buffer-size.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0020-Use-correct-buffer-size.patch new file mode 100644 index 0000000000..54328a73d8 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0020-Use-correct-buffer-size.patch @@ -0,0 +1,40 @@ +From 9ad9ead58e8e9e4f9e7a283c916421b443b424ce Mon Sep 17 00:00:00 2001 +From: Gowtham Suresh Kumar <gowtham.sureshkumar@arm.com> +Date: Mon, 13 Dec 2021 15:25:23 +0000 +Subject: [PATCH 20/27] Use correct buffer size + +The comm buffer created has additional 4 bytes length which +needs to be trimmed. This change will reduce the size of the +comm buffer to what is expected. + +Signed-off-by: Gowtham Suresh Kumar <gowtham.sureshkumar@arm.com> +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + include/mm_communication.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/include/mm_communication.h b/include/mm_communication.h +index e65fbde60d0a..bb9919095649 100644 +--- a/include/mm_communication.h ++++ b/include/mm_communication.h +@@ -123,7 +123,7 @@ struct __packed efi_mm_communicate_header { + * + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_HEADER. + */ +-struct smm_variable_communicate_header { ++struct __packed smm_variable_communicate_header { + efi_uintn_t function; + efi_status_t ret_status; + u8 data[]; +@@ -145,7 +145,7 @@ struct smm_variable_communicate_header { + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE. + * + */ +-struct smm_variable_access { ++struct __packed smm_variable_access { + efi_guid_t guid; + efi_uintn_t data_size; + efi_uintn_t name_size; +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0021-Update-comm_buf-when-EFI_BUFFER_TOO_SMALL.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0021-Update-comm_buf-when-EFI_BUFFER_TOO_SMALL.patch new file mode 100644 index 0000000000..c7ac38f77c --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0021-Update-comm_buf-when-EFI_BUFFER_TOO_SMALL.patch @@ -0,0 +1,30 @@ +From b81214dea7056c3877aa9eb775557dc4702660ec Mon Sep 17 00:00:00 2001 +From: Gowtham Suresh Kumar <gowtham.sureshkumar@arm.com> +Date: Sun, 12 Dec 2021 17:58:08 +0000 +Subject: [PATCH 21/27] Update comm_buf when EFI_BUFFER_TOO_SMALL + +When the received buffer is larger than the comm buffer, +the contents of the shared buffer which can fit in the +comm buffer should be read before returning. + +Signed-off-by: Gowtham Suresh Kumar <gowtham.sureshkumar@arm.com> +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + lib/efi_loader/efi_variable_tee.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c +index 38655a9dbb7c..67743d1f8fce 100644 +--- a/lib/efi_loader/efi_variable_tee.c ++++ b/lib/efi_loader/efi_variable_tee.c +@@ -357,6 +357,7 @@ static efi_status_t __efi_runtime ffa_mm_communicate(void *comm_buf, ulong comm_ + sizeof(size_t); + + if (rx_data_size > comm_buf_size) { ++ efi_memcpy_runtime(comm_buf, virt_shared_buf, comm_buf_size); + unmap_sysmem(virt_shared_buf); + return EFI_BUFFER_TOO_SMALL; + } +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0022-efi_loader-populate-ESRT-table-if-EFI_ESRT-config-op.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0022-efi_loader-populate-ESRT-table-if-EFI_ESRT-config-op.patch new file mode 100644 index 0000000000..aaea20e632 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0022-efi_loader-populate-ESRT-table-if-EFI_ESRT-config-op.patch @@ -0,0 +1,36 @@ +From 5fec641015f8f1ca80f55f05b5e1f67653321303 Mon Sep 17 00:00:00 2001 +From: Vishnu Banavath <vishnu.banavath@arm.com> +Date: Fri, 17 Dec 2021 19:49:02 +0000 +Subject: [PATCH 22/27] efi_loader: populate ESRT table if EFI_ESRT config + option is set + +This change is to call efi_esrt_populate function if CONFIG_EFI_ESRT +is set. This will populte esrt table with firmware image info + +Signed-off-by: Vishnu Banavath <vishnu.banavath@arm.com> +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + lib/efi_loader/efi_capsule.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c +index 939040d2755e..790d2ba8fe19 100644 +--- a/lib/efi_loader/efi_capsule.c ++++ b/lib/efi_loader/efi_capsule.c +@@ -676,6 +676,13 @@ efi_status_t __efi_runtime EFIAPI efi_update_capsule( + ret = EFI_SUCCESS; + } + ++ if (IS_ENABLED(CONFIG_EFI_ESRT)) { ++ /* Rebuild the ESRT to reflect any updated FW images. */ ++ ret = efi_esrt_populate(); ++ if (ret != EFI_SUCCESS) ++ log_warning("EFI Capsule: failed to update ESRT\n"); ++ } ++ + goto out; + #endif + +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0023-efi_firmware-add-get_image_info-for-corstone1000.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0023-efi_firmware-add-get_image_info-for-corstone1000.patch new file mode 100644 index 0000000000..c86b658158 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0023-efi_firmware-add-get_image_info-for-corstone1000.patch @@ -0,0 +1,121 @@ +From 34fadec4f659248a6020676f5894895977ccf79d Mon Sep 17 00:00:00 2001 +From: Vishnu Banavath <vishnu.banavath@arm.com> +Date: Fri, 17 Dec 2021 19:50:25 +0000 +Subject: [PATCH 23/27] efi_firmware: add get_image_info for corstone1000 + +This change is to populate get_image_info which eventually +will be populated in ESRT table + +Signed-off-by: Vishnu Banavath <vishnu.banavath@arm.com> + +%% original patch: 0047-efi_firmware-add-get_image_info-for-corstone1000.patch + +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + lib/efi_loader/efi_firmware.c | 64 ++++++++++++++++++++++++++++++++++- + 1 file changed, 63 insertions(+), 1 deletion(-) + +diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c +index a5ff32f121f4..9eb89849b28d 100644 +--- a/lib/efi_loader/efi_firmware.c ++++ b/lib/efi_loader/efi_firmware.c +@@ -241,6 +241,7 @@ const efi_guid_t efi_firmware_image_type_uboot_fit = + * + * Return status code + */ ++ + static + efi_status_t EFIAPI efi_firmware_fit_get_image_info( + struct efi_firmware_management_protocol *this, +@@ -332,6 +333,56 @@ const struct efi_firmware_management_protocol efi_fmp_fit = { + const efi_guid_t efi_firmware_image_type_uboot_raw = + EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID; + ++#if CONFIG_IS_ENABLED(TARGET_CORSTONE1000) ++static efi_status_t efi_corstone1000_img_info_get ( ++ efi_uintn_t *image_info_size, ++ struct efi_firmware_image_descriptor *image_info, ++ u32 *descriptor_version, ++ u8 *descriptor_count, ++ efi_uintn_t *descriptor_size, ++ u32 *package_version, ++ u16 **package_version_name, ++ const efi_guid_t *image_type) ++{ ++ int i = 0; ++ ++ *image_info_size = sizeof(*image_info); ++ *descriptor_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION; ++ *descriptor_count = 1;//dfu_num; ++ *descriptor_size = sizeof(*image_info); ++ if (package_version) ++ *package_version = 0xffffffff; /* not supported */ ++ if(package_version_name) ++ *package_version_name = NULL; /* not supported */ ++ ++ if(image_info == NULL) { ++ log_warning("image_info is null\n"); ++ return EFI_BUFFER_TOO_SMALL; ++ } ++ ++ image_info[i].image_index = i; ++ image_info[i].image_type_id = *image_type; ++ image_info[i].image_id = 0; ++ image_info[i].image_id_name = "wic"; ++ image_info[i].version = 1; ++ image_info[i].version_name = NULL; ++ image_info[i].size = 0x1000; ++ image_info[i].attributes_supported = IMAGE_ATTRIBUTE_IMAGE_UPDATABLE | ++ IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED; ++ image_info[i].attributes_setting = IMAGE_ATTRIBUTE_IMAGE_UPDATABLE; ++ /* Check if the capsule authentication is enabled */ ++ if (IS_ENABLED(CONFIG_EFI_CAPSULE_AUTHENTICATE)) ++ image_info[0].attributes_setting |= ++ IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED; ++ image_info[i].lowest_supported_image_version = 0; ++ image_info[i].last_attempt_version = 0; ++ image_info[i].last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS; ++ image_info[i].hardware_instance = 1; ++ image_info[i].dependencies = NULL; ++ ++ return EFI_SUCCESS; ++} ++#endif + /** + * efi_firmware_raw_get_image_info - return information about the current + firmware image +@@ -376,12 +427,20 @@ efi_status_t EFIAPI efi_firmware_raw_get_image_info( + !descriptor_size || !package_version || !package_version_name)) + return EFI_EXIT(EFI_INVALID_PARAMETER); + +- ret = efi_get_dfu_info(image_info_size, image_info, ++#if CONFIG_IS_ENABLED(TARGET_CORSTONE1000) ++ ret = efi_corstone1000_img_info_get(image_info_size, image_info, + descriptor_version, descriptor_count, + descriptor_size, + package_version, package_version_name, + &efi_firmware_image_type_uboot_raw); ++#else + ++ ret = efi_get_dfu_info(image_info_size, image_info, ++ descriptor_version, descriptor_count, ++ descriptor_size, ++ package_version, package_version_name, ++ &efi_firmware_image_type_uboot_raw); ++#endif + return EFI_EXIT(ret); + } + +@@ -462,6 +521,9 @@ efi_status_t EFIAPI efi_firmware_raw_set_image( + + } + ++#if CONFIG_IS_ENABLED(TARGET_CORSTONE1000) ++ return EFI_EXIT(EFI_SUCCESS); ++#endif + if (dfu_write_by_alt(image_index - 1, (void *)image, image_size, + NULL, NULL)) + return EFI_EXIT(EFI_DEVICE_ERROR); +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0024-Comment-mm_communicate-failure-log.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0024-Comment-mm_communicate-failure-log.patch new file mode 100644 index 0000000000..c6a1aed2e6 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0024-Comment-mm_communicate-failure-log.patch @@ -0,0 +1,34 @@ +From c0c6e4c1166c4868afc36649b9ed98081a6966e1 Mon Sep 17 00:00:00 2001 +From: Gowtham Suresh Kumar <gowtham.sureshkumar@arm.com> +Date: Fri, 24 Dec 2021 14:22:52 +0000 +Subject: [PATCH 24/27] Comment mm_communicate failure log + +When a getVariable() call is made with data size set to 0, +mm_communicate should return EFI_BUFFER_TOO_SMALL. This is +an expected behavior. There should not be any failure logs +in this case. So the error log is commented here. + +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + lib/efi_loader/efi_variable_tee.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c +index 67743d1f8fce..a34989efac83 100644 +--- a/lib/efi_loader/efi_variable_tee.c ++++ b/lib/efi_loader/efi_variable_tee.c +@@ -411,7 +411,10 @@ static efi_status_t __efi_runtime mm_communicate(u8 *comm_buf, efi_uintn_t dsize + ret = ffa_mm_communicate(comm_buf, dsize); + #endif + if (ret != EFI_SUCCESS) { +- log_err("%s failed!\n", __func__); ++ /* mm_communicate failure is logged even when getVariable() is called ++ * with data size set to 0. This is not expected so logging is commented. ++ */ ++ //log_err("%s failed!\n", __func__); + return ret; + } + +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0025-efi_loader-send-bootcomplete-message-to-secure-encla.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0025-efi_loader-send-bootcomplete-message-to-secure-encla.patch new file mode 100644 index 0000000000..d5a0ec0b1b --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0025-efi_loader-send-bootcomplete-message-to-secure-encla.patch @@ -0,0 +1,191 @@ +From af2defbfaffa4264052e30f269b91794068e4773 Mon Sep 17 00:00:00 2001 +From: Vishnu Banavath <vishnu.banavath@arm.com> +Date: Wed, 5 Jan 2022 17:56:09 +0000 +Subject: [PATCH 25/27] efi_loader: send bootcomplete message to secure enclave + +On corstone1000 platform, Secure Enclave will be expecting +an event from uboot when it performs capsule update. Previously, +an event is sent at exitbootservice level. This will create a problem +when user wants to interrupt at UEFI shell, hence, it is required +to send an uboot efi initialized event at efi sub-system initialization +stage. + +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + include/configs/corstone1000.h | 2 +- + lib/efi_loader/efi_boottime.c | 49 ---------------------------------- + lib/efi_loader/efi_firmware.c | 2 +- + lib/efi_loader/efi_setup.c | 48 +++++++++++++++++++++++++++++++++ + 4 files changed, 50 insertions(+), 51 deletions(-) + +diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h +index a7445e61348b..06b605e43bdf 100644 +--- a/include/configs/corstone1000.h ++++ b/include/configs/corstone1000.h +@@ -22,7 +22,7 @@ + + /* Notification events used with SE Proxy update service */ + #define CORSTONE1000_BUFFER_READY_EVT (0x1) +-#define CORSTONE1000_KERNEL_STARTED_EVT (0x2) ++#define CORSTONE1000_UBOOT_EFI_STARTED_EVT (0x2) + + #define PREP_SEPROXY_SVC_ID_MASK GENMASK(31, 16) + #define PREP_SEPROXY_SVC_ID(x) (FIELD_PREP(PREP_SEPROXY_SVC_ID_MASK, (x))) +diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c +index b58a8c98fd05..d0703060491b 100644 +--- a/lib/efi_loader/efi_boottime.c ++++ b/lib/efi_loader/efi_boottime.c +@@ -2101,46 +2101,6 @@ static void efi_exit_caches(void) + #endif + } + +-#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000) +-/** +- * efi_corstone1000_kernel_started_event - notifies SE Proxy FW update service +- * +- * This function notifies the SE Proxy update service that the kernel has already started +- * +- * Return: +- * +- * 0: on success, otherwise failure +- */ +-static int efi_corstone1000_kernel_started_event(void) +-{ +- struct ffa_interface_data func_data = {0}; +- struct ffa_send_direct_data msg = {0}; +- u16 part_id = CORSTONE1000_SEPROXY_PART_ID; +- +- log_debug("[%s]\n", __func__); +- +- /* +- * telling the driver which partition to use +- */ +- func_data.data0_size = sizeof(part_id); +- func_data.data0 = &part_id; +- +- /* +- * setting the kernel started event arguments: +- * setting capsule update interface ID(31:16) +- * the kernel started event ID(15:0) +- */ +- msg.a4 = PREP_SEPROXY_SVC_ID(CORSTONE1000_SEPROXY_UPDATE_SVC_ID) | +- PREP_SEPROXY_EVT(CORSTONE1000_KERNEL_STARTED_EVT); +- +- func_data.data1_size = sizeof(msg); +- func_data.data1 = &msg; +- +- return ffa_helper_msg_send_direct_req(&func_data); +-} +- +-#endif +- + /** + * efi_exit_boot_services() - stop all boot services + * @image_handle: handle of the loaded image +@@ -2254,15 +2214,6 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, + /* Recalculate CRC32 */ + efi_update_table_header_crc32(&systab.hdr); + +-#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000) +- /* Notifying SE Proxy FW update service */ +- ffa_ret = efi_corstone1000_kernel_started_event(); +- if (ffa_ret) +- debug("[efi_boottime][ERROR]: Failure to notify SE Proxy FW update service\n"); +- else +- debug("[efi_boottime][INFO]: SE Proxy FW update service notified\n"); +-#endif +- + /* Give the payload some time to boot */ + efi_set_watchdog(0); + WATCHDOG_RESET(); +diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c +index 9eb89849b28d..477ad072070e 100644 +--- a/lib/efi_loader/efi_firmware.c ++++ b/lib/efi_loader/efi_firmware.c +@@ -356,7 +356,7 @@ static efi_status_t efi_corstone1000_img_info_get ( + *package_version_name = NULL; /* not supported */ + + if(image_info == NULL) { +- log_warning("image_info is null\n"); ++ log_info("image_info is null\n"); + return EFI_BUFFER_TOO_SMALL; + } + +diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c +index 989380d4f8cd..515a0bdf74ef 100644 +--- a/lib/efi_loader/efi_setup.c ++++ b/lib/efi_loader/efi_setup.c +@@ -17,6 +17,9 @@ + efi_status_t efi_obj_list_initialized = OBJ_LIST_NOT_INITIALIZED; + + #if IS_ENABLED(CONFIG_TARGET_CORSTONE1000) ++#include <linux/bitfield.h> ++#include <linux/bitops.h> ++#include <arm_ffa_helper.h> + /** + * efi_corstone1000_alloc_capsule_shared_buf - allocate capsule shared buffer + */ +@@ -126,6 +129,44 @@ static efi_status_t efi_init_secure_boot(void) + } + #endif /* CONFIG_EFI_SECURE_BOOT */ + ++#if IS_ENABLED(CONFIG_TARGET_CORSTONE1000) ++/** ++ * efi_corstone1000_uboot-efi_started_event - notifies SE Proxy FW update service ++ * ++ * This function notifies the SE Proxy update service that uboot efi has already started ++ * ++ * Return: ++ * ++ * 0: on success, otherwise failure ++ * */ ++static int efi_corstone1000_uboot_efi_started_event(void) ++{ ++ struct ffa_interface_data func_data = {0}; ++ struct ffa_send_direct_data msg = {0}; ++ u16 part_id = CORSTONE1000_SEPROXY_PART_ID; ++ ++ log_debug("[%s]\n", __func__); ++ ++ /* ++ * telling the driver which partition to use ++ */ ++ func_data.data0_size = sizeof(part_id); ++ func_data.data0 = &part_id; ++ /* ++ * setting the uboot efi subsystem started event arguments: ++ * setting capsule update interface ID(31:16) ++ * the uboot efi subsystem started event ID(15:0) ++ */ ++ msg.a4 = PREP_SEPROXY_SVC_ID(CORSTONE1000_SEPROXY_UPDATE_SVC_ID) | ++ PREP_SEPROXY_EVT(CORSTONE1000_UBOOT_EFI_STARTED_EVT); ++ ++ func_data.data1_size = sizeof(msg); ++ func_data.data1 = &msg; ++ ++ return ffa_helper_msg_send_direct_req(&func_data); ++} ++#endif ++ + /** + * efi_init_capsule - initialize capsule update state + * +@@ -134,8 +175,15 @@ static efi_status_t efi_init_secure_boot(void) + static efi_status_t efi_init_capsule(void) + { + efi_status_t ret = EFI_SUCCESS; ++ int ffa_ret; + + #if IS_ENABLED(CONFIG_TARGET_CORSTONE1000) ++ ffa_ret = efi_corstone1000_uboot_efi_started_event(); ++ if (ffa_ret) ++ debug("[efi_boottime][ERROR]: Failure to notify SE Proxy FW update service\n"); ++ else ++ debug("[efi_boottime][INFO]: SE Proxy FW update service notified\n"); ++ + ret = efi_corstone1000_alloc_capsule_shared_buf(); + if (ret != EFI_SUCCESS) { + printf("EFI: Corstone-1000: cannot allocate caspsule shared buffer\n"); +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0026-efi_loader-fix-null-pointer-exception-with-get_image.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0026-efi_loader-fix-null-pointer-exception-with-get_image.patch new file mode 100644 index 0000000000..532e872677 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0026-efi_loader-fix-null-pointer-exception-with-get_image.patch @@ -0,0 +1,62 @@ +From 2da8554ab732c59c7ca624ac4b16412fa9c2e39c Mon Sep 17 00:00:00 2001 +From: Vishnu Banavath <vishnu.banavath@arm.com> +Date: Fri, 14 Jan 2022 15:24:18 +0000 +Subject: [PATCH 26/27] efi_loader: fix null pointer exception with + get_image_info + +get_img_info API implemented for corstone1000 target does not +check the input attributes and as a result uboot crash's with +null pointer access. This change is to fix the null pointer +exception. + +Signed-off-by: Vishnu Banavath <vishnu.banavath@arm.com> +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + lib/efi_loader/efi_firmware.c | 19 +++++++++++-------- + 1 file changed, 11 insertions(+), 8 deletions(-) + +diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c +index 477ad072070e..f99c57fde576 100644 +--- a/lib/efi_loader/efi_firmware.c ++++ b/lib/efi_loader/efi_firmware.c +@@ -347,26 +347,29 @@ static efi_status_t efi_corstone1000_img_info_get ( + int i = 0; + + *image_info_size = sizeof(*image_info); +- *descriptor_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION; +- *descriptor_count = 1;//dfu_num; +- *descriptor_size = sizeof(*image_info); ++ if(descriptor_version) ++ *descriptor_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION; ++ if(descriptor_count) ++ *descriptor_count = 1; ++ if(descriptor_size) ++ *descriptor_size = sizeof(*image_info); + if (package_version) + *package_version = 0xffffffff; /* not supported */ + if(package_version_name) + *package_version_name = NULL; /* not supported */ + + if(image_info == NULL) { +- log_info("image_info is null\n"); ++ log_debug("image_info is null\n"); + return EFI_BUFFER_TOO_SMALL; + } + +- image_info[i].image_index = i; ++ image_info[i].image_index = 1; + image_info[i].image_type_id = *image_type; + image_info[i].image_id = 0; +- image_info[i].image_id_name = "wic"; +- image_info[i].version = 1; ++ image_info[i].image_id_name = L"wic image"; ++ image_info[i].version = 0; + image_info[i].version_name = NULL; +- image_info[i].size = 0x1000; ++ image_info[i].size = 0; + image_info[i].attributes_supported = IMAGE_ATTRIBUTE_IMAGE_UPDATABLE | + IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED; + image_info[i].attributes_setting = IMAGE_ATTRIBUTE_IMAGE_UPDATABLE; +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0027-arm-corstone1000-add-mmc-for-fvp.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0027-arm-corstone1000-add-mmc-for-fvp.patch new file mode 100644 index 0000000000..bf95ed7273 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0027-arm-corstone1000-add-mmc-for-fvp.patch @@ -0,0 +1,148 @@ +From cbf16548dc6dcc8eea97aa18c6ae17fb848e5c6c Mon Sep 17 00:00:00 2001 +From: Rui Miguel Silva <rui.silva@linaro.org> +Date: Tue, 5 Apr 2022 10:24:38 +0100 +Subject: [PATCH 27/27] arm:corstone1000: add mmc for fvp + +Enable support mmc/sdcard for the corstone1000 FVP. + +Signed-off-by: Vishnu Banavath <vishnu.banavath@arm.com> +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +--- + arch/arm/dts/corstone1000-fvp.dts | 28 +++++++++++++++ + board/armltd/corstone1000/corstone1000.c | 46 ++++++++++++++++-------- + configs/corstone1000_defconfig | 8 ++++- + include/configs/corstone1000.h | 4 ++- + 4 files changed, 69 insertions(+), 17 deletions(-) + +diff --git a/arch/arm/dts/corstone1000-fvp.dts b/arch/arm/dts/corstone1000-fvp.dts +index 1fcc137a493c..26b0f1b3cea6 100644 +--- a/arch/arm/dts/corstone1000-fvp.dts ++++ b/arch/arm/dts/corstone1000-fvp.dts +@@ -20,4 +20,32 @@ + interrupts = <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>; + reg-io-width = <2>; + }; ++ ++ vmmc_v3_3d: fixed_v3_3d { ++ compatible = "regulator-fixed"; ++ regulator-name = "vmmc_supply"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ }; ++ ++ sdmmc0: mmc@40300000 { ++ compatible = "arm,pl18x", "arm,primecell"; ++ reg = <0x40300000 0x1000>; ++ interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>; ++ max-frequency = <12000000>; ++ vmmc-supply = <&vmmc_v3_3d>; ++ clocks = <&smbclk>, <&refclk100mhz>; ++ clock-names = "smclk", "apb_pclk"; ++ }; ++ ++ sdmmc1: mmc@50000000 { ++ compatible = "arm,pl18x", "arm,primecell"; ++ reg = <0x50000000 0x10000>; ++ interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>; ++ max-frequency = <12000000>; ++ vmmc-supply = <&vmmc_v3_3d>; ++ clocks = <&smbclk>, <&refclk100mhz>; ++ clock-names = "smclk", "apb_pclk"; ++ }; + }; +diff --git a/board/armltd/corstone1000/corstone1000.c b/board/armltd/corstone1000/corstone1000.c +index eff1739f0b02..936a6c9f8b89 100644 +--- a/board/armltd/corstone1000/corstone1000.c ++++ b/board/armltd/corstone1000/corstone1000.c +@@ -46,22 +46,38 @@ static struct mm_region corstone1000_mem_map[] = { + .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | + PTE_BLOCK_NON_SHARE | + PTE_BLOCK_PXN | PTE_BLOCK_UXN +- }, { +- /* USB */ +- .virt = 0x40200000UL, +- .phys = 0x40200000UL, +- .size = 0x00100000UL, +- .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | +- PTE_BLOCK_NON_SHARE | +- PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { +- /* ethernet */ +- .virt = 0x40100000UL, +- .phys = 0x40100000UL, +- .size = 0x00100000UL, +- .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | +- PTE_BLOCK_NON_SHARE | +- PTE_BLOCK_PXN | PTE_BLOCK_UXN ++ /* USB */ ++ .virt = 0x40200000UL, ++ .phys = 0x40200000UL, ++ .size = 0x00100000UL, ++ .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | ++ PTE_BLOCK_NON_SHARE | ++ PTE_BLOCK_PXN | PTE_BLOCK_UXN ++ }, { ++ /* MMC0 */ ++ .virt = 0x40300000UL, ++ .phys = 0x40300000UL, ++ .size = 0x00100000UL, ++ .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | ++ PTE_BLOCK_NON_SHARE | ++ PTE_BLOCK_PXN | PTE_BLOCK_UXN ++ }, { ++ /* ethernet */ ++ .virt = 0x40100000UL, ++ .phys = 0x40100000UL, ++ .size = 0x00100000UL, ++ .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | ++ PTE_BLOCK_NON_SHARE | ++ PTE_BLOCK_PXN | PTE_BLOCK_UXN ++ }, { ++ /* MMC1 */ ++ .virt = 0x50000000UL, ++ .phys = 0x50000000UL, ++ .size = 0x00100000UL, ++ .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | ++ PTE_BLOCK_NON_SHARE | ++ PTE_BLOCK_PXN | PTE_BLOCK_UXN + }, { + /* OCVM */ + .virt = 0x80000000UL, +diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig +index b042d4e49419..147c14c94865 100644 +--- a/configs/corstone1000_defconfig ++++ b/configs/corstone1000_defconfig +@@ -38,7 +38,13 @@ CONFIG_CMD_EFIDEBUG=y + CONFIG_CMD_FAT=y + CONFIG_OF_CONTROL=y + CONFIG_REGMAP=y +-# CONFIG_MMC is not set ++CONFIG_CLK=y ++CONFIG_CMD_MMC=y ++CONFIG_DM_MMC=y ++CONFIG_ARM_PL180_MMCI=y ++CONFIG_MMC_SDHCI_ADMA_HELPERS=y ++CONFIG_MMC_WRITE=y ++CONFIG_DM_GPIO=y + CONFIG_DM_SERIAL=y + CONFIG_USB=y + CONFIG_DM_USB=y +diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h +index 06b605e43bdf..d9855bf91ebf 100644 +--- a/include/configs/corstone1000.h ++++ b/include/configs/corstone1000.h +@@ -95,7 +95,9 @@ + #define CONFIG_SYS_MAXARGS 64 /* max command args */ + + #define BOOT_TARGET_DEVICES(func) \ +- func(USB, usb, 0) ++ func(USB, usb, 0) \ ++ func(MMC, mmc, 0) \ ++ func(MMC, mmc, 1) + + #include <config_distro_bootcmd.h> + +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone500/0001-armv7-adding-generic-timer-access-through-MMIO.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone500/0001-armv7-adding-generic-timer-access-through-MMIO.patch new file mode 100644 index 0000000000..8a98f9dedc --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone500/0001-armv7-adding-generic-timer-access-through-MMIO.patch @@ -0,0 +1,138 @@ +From fff63cfd7d9654dc9ed0c106f29d3a7ad01b0502 Mon Sep 17 00:00:00 2001 +From: Rui Miguel Silva <rui.silva@linaro.org> +Date: Wed, 18 Dec 2019 21:52:34 +0000 +Subject: [PATCH 1/2] armv7: adding generic timer access through MMIO + +Upstream-Status: Pending [Not submitted to upstream yet] +Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> + +This driver enables the ARMv7 generic timer. + +The access to the timer registers is through memory mapping (MMIO). + +This driver can be used by u-boot to access to the timer through MMIO +when arch_timer is not available in the core (access using system +instructions not possible), for example, in case of Cortex-A5. + +This driver configures and enables the generic timer at +the u-boot initcall level (timer_init) before u-boot relocation. + +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> +Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> + +%% original patch: 0001-armv7-adding-generic-timer-access-through-MMIO.patch +--- + arch/arm/cpu/armv7/Makefile | 1 + + arch/arm/cpu/armv7/mmio_timer.c | 75 +++++++++++++++++++++++++++++++++ + scripts/config_whitelist.txt | 1 + + 3 files changed, 77 insertions(+) + create mode 100644 arch/arm/cpu/armv7/mmio_timer.c + +diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile +index bfbd85ae64..1a0a24e531 100644 +--- a/arch/arm/cpu/armv7/Makefile ++++ b/arch/arm/cpu/armv7/Makefile +@@ -28,6 +28,7 @@ obj-$(CONFIG_ARMV7_PSCI) += psci.o psci-common.o + obj-$(CONFIG_IPROC) += iproc-common/ + obj-$(CONFIG_KONA) += kona-common/ + obj-$(CONFIG_SYS_ARCH_TIMER) += arch_timer.o ++obj-$(CONFIG_SYS_MMIO_TIMER) += mmio_timer.o + + ifneq (,$(filter s5pc1xx exynos,$(SOC))) + obj-y += s5p-common/ +diff --git a/arch/arm/cpu/armv7/mmio_timer.c b/arch/arm/cpu/armv7/mmio_timer.c +new file mode 100644 +index 0000000000..edd806e06e +--- /dev/null ++++ b/arch/arm/cpu/armv7/mmio_timer.c +@@ -0,0 +1,75 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright (c) 2019, Arm Limited. All rights reserved. ++ * ++ */ ++ ++#include <common.h> ++#include <asm/io.h> ++#include <div64.h> ++#include <bootstage.h> ++#include <asm/global_data.h> ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++#define CNTCTLBASE 0x1a020000UL ++#define CNTREADBASE 0x1a030000UL ++#define CNTEN (1 << 0) ++#define CNTFCREQ (1 << 8) ++ ++static inline uint32_t mmio_read32(uintptr_t addr) ++{ ++ return *(volatile uint32_t*)addr; ++} ++ ++static inline void mmio_write32(uintptr_t addr, uint32_t data) ++{ ++ *(volatile uint32_t*)addr = data; ++} ++ ++int timer_init(void) ++{ ++ /* calculate the frequency in ms */ ++ gd->arch.timer_rate_hz = COUNTER_FREQUENCY / CONFIG_SYS_HZ; ++ ++ /* configure CNTFID0 register: set the base frequency */ ++ mmio_write32(CNTCTLBASE + 0x20, COUNTER_FREQUENCY); ++ ++ /* ++ * configure CNTCR register: ++ * enable the generic counter and; ++ * select the first frequency entry ++ */ ++ mmio_write32(CNTCTLBASE, CNTFCREQ | CNTEN); ++ ++ return 0; ++} ++ ++unsigned long long get_ticks(void) ++{ ++ return (((u64)(mmio_read32(CNTREADBASE + 0x4)) << 32) | ++ mmio_read32(CNTREADBASE)); ++} ++ ++ulong get_timer(ulong base) ++{ ++ return lldiv(get_ticks(), gd->arch.timer_rate_hz) - base; ++} ++ ++void __udelay(unsigned long usec) ++{ ++ unsigned long endtime; ++ ++ endtime = lldiv((unsigned long long)usec * gd->arch.timer_rate_hz, ++ 1000UL); ++ ++ endtime += get_ticks(); ++ ++ while (get_ticks() < endtime) ++ ; ++} ++ ++ulong get_tbclk(void) ++{ ++ return gd->arch.timer_rate_hz; ++} +diff --git a/scripts/config_whitelist.txt b/scripts/config_whitelist.txt +index a6bc234f51..8d5cd67ace 100644 +--- a/scripts/config_whitelist.txt ++++ b/scripts/config_whitelist.txt +@@ -1524,6 +1524,7 @@ CONFIG_SYS_MMC_U_BOOT_DST + CONFIG_SYS_MMC_U_BOOT_OFFS + CONFIG_SYS_MMC_U_BOOT_SIZE + CONFIG_SYS_MMC_U_BOOT_START ++CONFIG_SYS_MMIO_TIMER + CONFIG_SYS_MONITOR_BASE + CONFIG_SYS_MONITOR_LEN + CONFIG_SYS_MONITOR_SEC +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone500/0002-board-arm-add-corstone500-board.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone500/0002-board-arm-add-corstone500-board.patch new file mode 100644 index 0000000000..29b29438c8 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone500/0002-board-arm-add-corstone500-board.patch @@ -0,0 +1,307 @@ +From 73c319a1096259652853fa2538a733a8ebea96a8 Mon Sep 17 00:00:00 2001 +From: Rui Miguel Silva <rui.silva@linaro.org> +Date: Wed, 8 Jan 2020 09:48:11 +0000 +Subject: [PATCH 2/2] board: arm: add corstone500 board + +Upstream-Status: Pending [Not submitted to upstream yet] +Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com> + +Add support for the Arm corstone500 platform, with a cortex-a5 +chip, add the default configuration, initialization and +makefile for this system. + +Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org> + +%% original patch: 0002-board-arm-add-corstone500-board.patch +--- + arch/arm/Kconfig | 10 +++ + board/armltd/corstone500/Kconfig | 12 +++ + board/armltd/corstone500/Makefile | 8 ++ + board/armltd/corstone500/corstone500.c | 48 +++++++++++ + configs/corstone500_defconfig | 40 +++++++++ + include/configs/corstone500.h | 109 +++++++++++++++++++++++++ + 6 files changed, 227 insertions(+) + create mode 100644 board/armltd/corstone500/Kconfig + create mode 100644 board/armltd/corstone500/Makefile + create mode 100644 board/armltd/corstone500/corstone500.c + create mode 100644 configs/corstone500_defconfig + create mode 100644 include/configs/corstone500.h + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 4567c183fb..66f99fdf4f 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -641,6 +641,15 @@ config ARCH_BCMSTB + This enables support for Broadcom ARM-based set-top box + chipsets, including the 7445 family of chips. + ++config TARGET_CORSTONE500 ++ bool "Support Corstone500" ++ select CPU_V7A ++ select SEMIHOSTING ++ select PL01X_SERIAL ++ help ++ This enables support for Corstone500 ARM which is a ++ Cortex-A5 system ++ + config TARGET_VEXPRESS_CA9X4 + bool "Support vexpress_ca9x4" + select CPU_V7A +@@ -2202,6 +2211,7 @@ source "board/bosch/shc/Kconfig" + source "board/bosch/guardian/Kconfig" + source "board/Marvell/octeontx/Kconfig" + source "board/Marvell/octeontx2/Kconfig" ++source "board/armltd/corstone500/Kconfig" + source "board/armltd/vexpress/Kconfig" + source "board/armltd/vexpress64/Kconfig" + source "board/cortina/presidio-asic/Kconfig" +diff --git a/board/armltd/corstone500/Kconfig b/board/armltd/corstone500/Kconfig +new file mode 100644 +index 0000000000..8e689bd1fd +--- /dev/null ++++ b/board/armltd/corstone500/Kconfig +@@ -0,0 +1,12 @@ ++if TARGET_CORSTONE500 ++ ++config SYS_BOARD ++ default "corstone500" ++ ++config SYS_VENDOR ++ default "armltd" ++ ++config SYS_CONFIG_NAME ++ default "corstone500" ++ ++endif +diff --git a/board/armltd/corstone500/Makefile b/board/armltd/corstone500/Makefile +new file mode 100644 +index 0000000000..6598fdd3ae +--- /dev/null ++++ b/board/armltd/corstone500/Makefile +@@ -0,0 +1,8 @@ ++# SPDX-License-Identifier: GPL-2.0+ ++# ++# (C) Copyright 2022 ARM Limited ++# (C) Copyright 2022 Linaro ++# Rui Miguel Silva <rui.silva@linaro.org> ++# ++ ++obj-y := corstone500.o +diff --git a/board/armltd/corstone500/corstone500.c b/board/armltd/corstone500/corstone500.c +new file mode 100644 +index 0000000000..e878f5c6a5 +--- /dev/null ++++ b/board/armltd/corstone500/corstone500.c +@@ -0,0 +1,48 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * (C) Copyright 2022 ARM Limited ++ * (C) Copyright 2022 Linaro ++ * Rui Miguel Silva <rui.silva@linaro.org> ++ */ ++ ++#include <common.h> ++#include <dm.h> ++#include <dm/platform_data/serial_pl01x.h> ++#include <malloc.h> ++#include <asm/global_data.h> ++ ++static const struct pl01x_serial_plat serial_platdata = { ++ .base = V2M_UART0, ++ .type = TYPE_PL011, ++ .clock = CONFIG_PL011_CLOCK, ++}; ++ ++U_BOOT_DRVINFO(corstone500_serials) = { ++ .name = "serial_pl01x", ++ .plat = &serial_platdata, ++}; ++ ++int board_init(void) ++{ ++ return 0; ++} ++ ++int dram_init(void) ++{ ++ gd->ram_size = PHYS_SDRAM_1_SIZE; ++ ++ return 0; ++} ++ ++int dram_init_banksize(void) ++{ ++ gd->bd->bi_dram[0].start = PHYS_SDRAM_1; ++ gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; ++ ++ return 0; ++} ++ ++void reset_cpu(ulong addr) ++{ ++} ++ +diff --git a/configs/corstone500_defconfig b/configs/corstone500_defconfig +new file mode 100644 +index 0000000000..d3161a4b40 +--- /dev/null ++++ b/configs/corstone500_defconfig +@@ -0,0 +1,40 @@ ++CONFIG_ARM=y ++CONFIG_SKIP_LOWLEVEL_INIT=y ++CONFIG_TARGET_CORSTONE500=y ++CONFIG_SYS_TEXT_BASE=0x88000000 ++CONFIG_SYS_MALLOC_LEN=0x840000 ++CONFIG_SYS_MALLOC_F_LEN=0x2000 ++CONFIG_NR_DRAM_BANKS=1 ++CONFIG_SYS_MEMTEST_START=0x80000000 ++CONFIG_SYS_MEMTEST_END=0xff000000 ++CONFIG_ENV_SIZE=0x40000 ++CONFIG_IDENT_STRING=" corstone500 aarch32" ++CONFIG_SYS_LOAD_ADDR=0x90000000 ++CONFIG_SUPPORT_RAW_INITRD=y ++CONFIG_BOOTDELAY=1 ++CONFIG_USE_BOOTARGS=y ++CONFIG_BOOTARGS="console=ttyAMA0 earlycon=pl011,0x1a200000 root=/dev/ram0 rw loglevel=9" ++# CONFIG_DISPLAY_CPUINFO is not set ++# CONFIG_DISPLAY_BOARDINFO is not set ++CONFIG_HUSH_PARSER=y ++CONFIG_SYS_PROMPT="corstone500# " ++# CONFIG_CMD_CONSOLE is not set ++CONFIG_CMD_BOOTZ=y ++# CONFIG_CMD_XIMG is not set ++# CONFIG_CMD_EDITENV is not set ++# CONFIG_CMD_ENV_EXISTS is not set ++CONFIG_CMD_MEMTEST=y ++CONFIG_CMD_ARMFLASH=y ++# CONFIG_CMD_LOADS is not set ++# CONFIG_CMD_ITEST is not set ++# CONFIG_CMD_SETEXPR is not set ++CONFIG_CMD_DHCP=y ++# CONFIG_CMD_NFS is not set ++CONFIG_CMD_MII=y ++CONFIG_CMD_PING=y ++CONFIG_CMD_CACHE=y ++CONFIG_CMD_FAT=y ++CONFIG_DM=y ++CONFIG_MTD_NOR_FLASH=y ++CONFIG_DM_SERIAL=y ++CONFIG_OF_LIBFDT=y +diff --git a/include/configs/corstone500.h b/include/configs/corstone500.h +new file mode 100644 +index 0000000000..93c397d2f5 +--- /dev/null ++++ b/include/configs/corstone500.h +@@ -0,0 +1,109 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * (C) Copyright 2022 ARM Limited ++ * (C) Copyright 2022 Linaro ++ * Rui Miguel Silva <rui.silva@linaro.org> ++ * ++ * Configuration for Cortex-A5 Corstone500. Parts were derived from other ARM ++ * configurations. ++ */ ++ ++#ifndef __CORSTONE500_H ++#define __CORSTONE500_H ++ ++#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x03f00000) ++ ++/* Generic Timer Definitions */ ++#define CONFIG_SYS_HZ_CLOCK 7500000 ++#define CONFIG_SYS_HZ 1000 ++#define COUNTER_FREQUENCY CONFIG_SYS_HZ_CLOCK ++ ++#ifdef CONFIG_CORSTONE500_MEMORY_MAP_EXTENDED ++#define V2M_SRAM0 0x00010000 ++#define V2M_SRAM1 0x02200000 ++#define V2M_QSPI 0x0a800000 ++#else ++#define V2M_SRAM0 0x00000000 ++#define V2M_SRAM1 0x02000000 ++#define V2M_QSPI 0x08000000 ++#endif ++ ++#define V2M_DEBUG 0x10000000 ++#define V2M_BASE_PERIPH 0x1a000000 ++#define V2M_A5_PERIPH 0x1c000000 ++#define V2M_L2CC_PERIPH 0x1c010000 ++ ++#define V2M_MASTER_EXPANSION0 0x40000000 ++#define V2M_MASTER_EXPANSION1 0x60000000 ++ ++#define V2M_BASE 0x80000000 ++ ++#define V2M_PERIPH_OFFSET(x) (x << 16) ++ ++#define V2M_SYSID (V2M_BASE_PERIPH) ++#define V2M_SYCTL (V2M_BASE_PERIPH + V2M_PERIPH_OFFSET(1)) ++#define V2M_COUNTER_CTL (V2M_BASE_PERIPH + V2M_PERIPH_OFFSET(2)) ++#define V2M_COUNTER_READ (V2M_BASE_PERIPH + V2M_PERIPH_OFFSET(3)) ++#define V2M_TIMER_CTL (V2M_BASE_PERIPH + V2M_PERIPH_OFFSET(4)) ++#define V2M_TIMER0 (V2M_BASE_PERIPH + V2M_PERIPH_OFFSET(5)) ++ ++#define V2M_WATCHDOG_CTL (V2M_BASE_PERIPH + V2M_PERIPH_OFFSET(16)) ++#define V2M_WATCHDOG_REFRESH (V2M_BASE_PERIPH + V2M_PERIPH_OFFSET(17)) ++ ++#define V2M_UART0 (V2M_BASE_PERIPH + V2M_PERIPH_OFFSET(32)) ++#define V2M_UART1 (V2M_BASE_PERIPH + V2M_PERIPH_OFFSET(33)) ++ ++#define V2M_RTC (V2M_BASE_PERIPH + V2M_PERIPH_OFFSET(34)) ++#define V2M_TRNG (V2M_BASE_PERIPH + V2M_PERIPH_OFFSET(35)) ++ ++/* PL011 Serial Configuration */ ++#define CONFIG_CONS_INDEX 0 ++#define CONFIG_PL011_CLOCK 7500000 ++ ++/* Physical Memory Map */ ++#define PHYS_SDRAM_1 (V2M_BASE) ++ ++/* Top 16MB reserved for secure world use */ ++#define DRAM_SEC_SIZE 0x01000000 ++#define PHYS_SDRAM_1_SIZE (0x80000000 - DRAM_SEC_SIZE) ++ ++/* Miscellaneous configurable options */ ++#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 ++ ++#define CONFIG_SYS_MMIO_TIMER ++ ++#define CONFIG_EXTRA_ENV_SETTINGS \ ++ "kernel_name=Image\0" \ ++ "kernel_addr=0x80f00000\0" \ ++ "initrd_name=ramdisk.img\0" \ ++ "initrd_addr=0x84000000\0" \ ++ "fdt_name=devtree.dtb\0" \ ++ "fdt_addr=0x83000000\0" \ ++ "fdt_high=0xffffffff\0" \ ++ "initrd_high=0xffffffff\0" ++ ++#define CONFIG_BOOTCOMMAND "echo copy to RAM...; " \ ++ "cp.b 0x80100000 $kernel_addr 0xb00000; " \ ++ "cp.b 0x80d00000 $initrd_addr 0x800000; " \ ++ "bootz $kernel_addr $initrd_addr:0x800000 $fdt_addr" ++ ++/* Monitor Command Prompt */ ++#define CONFIG_SYS_CBSIZE 512 /* Console I/O Buffer Size */ ++#define CONFIG_SYS_MAXARGS 64 /* max command args */ ++ ++#define CONFIG_SYS_FLASH_BASE 0x80000000 ++/* 256 x 256KiB sectors */ ++#define CONFIG_SYS_MAX_FLASH_SECT 256 ++/* Store environment at top of flash */ ++#define CONFIG_ENV_ADDR 0x0a7c0000 ++#define CONFIG_ENV_SECT_SIZE 0x0040000 ++ ++#define CONFIG_SYS_FLASH_CFI 1 ++#define CONFIG_FLASH_CFI_DRIVER 1 ++#define CONFIG_SYS_FLASH_CFI_WIDTH FLASH_CFI_32BIT ++#define CONFIG_SYS_MAX_FLASH_BANKS 1 ++ ++#define CONFIG_SYS_FLASH_EMPTY_INFO /* flinfo indicates empty blocks */ ++#define FLASH_MAX_SECTOR_SI 0x00040000 ++#define CONFIG_ENV_IS_IN_FLASH 1 ++#endif +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-base-arm32/0001-Add-vexpress_aemv8a_aarch32-variant.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-base-arm32/0001-Add-vexpress_aemv8a_aarch32-variant.patch new file mode 100644 index 0000000000..5138335eb4 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-base-arm32/0001-Add-vexpress_aemv8a_aarch32-variant.patch @@ -0,0 +1,184 @@ +From 424d186ab0a0c4dd62dfb13ac87e8d1fd26c101e Mon Sep 17 00:00:00 2001 +From: Anders Dellien <anders.dellien@arm.com> +Date: Thu, 23 Jul 2020 17:32:55 +0100 +Subject: [PATCH 1/2] Add vexpress_aemv8a_aarch32 variant + +The ARM AEMv8 FVP model can be run in Aarch64 or Aarch32 mode. Aarch32 +support is enable per-CPU when launching the model, eg: + +-C cluster0.cpu0.CONFIG64=0 + +This patch adds a new defconfig and some variant specific selections in +vexpress_armv8a.h. + +This patch is co-authored with Soby Mathew <Soby.Mathew@arm.com>. + +Upstream-Status: Denied + +For upstream discussion, please visit +https://www.mail-archive.com/u-boot@lists.denx.de/msg233429.html + +Signed-off-by: Ryan Harkin <ryan.harkin@linaro.org> +Signed-off-by: Asha R <asha.r@arm.com> +Signed-off-by: Anders Dellien <anders.dellien@arm.com> +--- + arch/arm/Kconfig | 5 +++ + board/armltd/vexpress64/Kconfig | 2 +- + configs/vexpress_aemv8a_aarch32_defconfig | 40 ++++++++++++++++++ + include/configs/vexpress_aemv8.h | 50 +++++++++++++++-------- + 4 files changed, 80 insertions(+), 17 deletions(-) + create mode 100644 configs/vexpress_aemv8a_aarch32_defconfig + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 4567c183fb84..99cc414d6760 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -1250,6 +1250,11 @@ config TARGET_VEXPRESS64_BASE_FVP + select PL01X_SERIAL + select SEMIHOSTING + ++config TARGET_VEXPRESS64_BASE_FVP_AARCH32 ++ bool "Support Versatile Express ARMv8a 32-bit FVP BASE model" ++ select CPU_V7A ++ select SEMIHOSTING ++ + config TARGET_VEXPRESS64_JUNO + bool "Support Versatile Express Juno Development Platform" + select ARM64 +diff --git a/board/armltd/vexpress64/Kconfig b/board/armltd/vexpress64/Kconfig +index 4aab3f092ecb..0a5e3fcc004a 100644 +--- a/board/armltd/vexpress64/Kconfig ++++ b/board/armltd/vexpress64/Kconfig +@@ -1,4 +1,4 @@ +-if TARGET_VEXPRESS64_BASE_FVP || TARGET_VEXPRESS64_JUNO ++if TARGET_VEXPRESS64_BASE_FVP || TARGET_VEXPRESS64_JUNO || TARGET_VEXPRESS64_BASE_FVP_AARCH32 + + config SYS_BOARD + default "vexpress64" +diff --git a/configs/vexpress_aemv8a_aarch32_defconfig b/configs/vexpress_aemv8a_aarch32_defconfig +new file mode 100644 +index 000000000000..9c5c3367ec4d +--- /dev/null ++++ b/configs/vexpress_aemv8a_aarch32_defconfig +@@ -0,0 +1,40 @@ ++CONFIG_ARM=y ++CONFIG_SYS_ARCH_TIMER=y ++CONFIG_TARGET_VEXPRESS64_BASE_FVP_AARCH32=y ++CONFIG_SYS_TEXT_BASE=0x88000000 ++CONFIG_SYS_MALLOC_F_LEN=0x2000 ++CONFIG_NR_DRAM_BANKS=2 ++CONFIG_IDENT_STRING=" vexpress_aemv8a fvp aarch32" ++CONFIG_REMAKE_ELF=y ++CONFIG_SYS_LOAD_ADDR=0x90000000 ++CONFIG_BOOTDELAY=1 ++CONFIG_USE_BOOTARGS=y ++CONFIG_BOOTARGS="console=ttyAMA0 earlycon=pl011,0x1c090000 debug user_debug=31 systemd.log_target=null root=/dev/vda1 rw androidboot.hardware=fvpbase rootwait loglevel=9" ++# CONFIG_DISPLAY_CPUINFO is not set ++# CONFIG_DISPLAY_BOARDINFO is not set ++CONFIG_HUSH_PARSER=y ++CONFIG_SYS_PROMPT="fvp32# " ++# CONFIG_CMD_CONSOLE is not set ++CONFIG_CMD_BOOTZ=y ++# CONFIG_CMD_XIMG is not set ++# CONFIG_CMD_EDITENV is not set ++# CONFIG_CMD_ENV_EXISTS is not set ++CONFIG_CMD_MEMTEST=y ++CONFIG_CMD_ARMFLASH=y ++# CONFIG_CMD_LOADS is not set ++# CONFIG_CMD_ITEST is not set ++# CONFIG_CMD_SETEXPR is not set ++CONFIG_CMD_DHCP=y ++# CONFIG_CMD_NFS is not set ++CONFIG_CMD_MII=y ++CONFIG_CMD_PING=y ++CONFIG_CMD_CACHE=y ++CONFIG_CMD_FAT=y ++CONFIG_DM=y ++CONFIG_MTD_NOR_FLASH=y ++CONFIG_FLASH_CFI_DRIVER=y ++CONFIG_SYS_FLASH_CFI=y ++CONFIG_DM_SERIAL=y ++CONFIG_PL01X_SERIAL=y ++CONFIG_OF_LIBFDT=y ++CONFIG_REMAKE_ELF=y +diff --git a/include/configs/vexpress_aemv8.h b/include/configs/vexpress_aemv8.h +index f0c5ceb3849a..854fbb41bfc1 100644 +--- a/include/configs/vexpress_aemv8.h ++++ b/include/configs/vexpress_aemv8.h +@@ -86,7 +86,7 @@ + #endif + #endif /* !CONFIG_GICV3 */ + +-#if defined(CONFIG_TARGET_VEXPRESS64_BASE_FVP) && !defined(CONFIG_DM_ETH) ++#if (defined(CONFIG_TARGET_VEXPRESS64_BASE_FVP) || defined(CONFIG_TARGET_VEXPRESS64_BASE_FVP_AARCH32)) && !defined(CONFIG_DM_ETH) + /* The Vexpress64 BASE_FVP simulator uses SMSC91C111 */ + #define CONFIG_SMC91111 1 + #define CONFIG_SMC91111_BASE (V2M_PA_BASE + 0x01A000000) +@@ -114,7 +114,7 @@ + #ifdef CONFIG_TARGET_VEXPRESS64_JUNO + #define PHYS_SDRAM_2 (0x880000000) + #define PHYS_SDRAM_2_SIZE 0x180000000 +-#elif CONFIG_NR_DRAM_BANKS == 2 ++#elif CONFIG_TARGET_VEXPRESS64_BASE_FVP && CONFIG_NR_DRAM_BANKS == 2 + #define PHYS_SDRAM_2 (0x880000000) + #define PHYS_SDRAM_2_SIZE 0x80000000 + #endif +@@ -171,23 +171,41 @@ + "fdt_addr_r=0x80000000\0" \ + BOOTENV + +-#elif CONFIG_TARGET_VEXPRESS64_BASE_FVP ++#elif defined(CONFIG_TARGET_VEXPRESS64_BASE_FVP) || \ ++ defined(CONFIG_TARGET_VEXPRESS64_BASE_FVP_AARCH32) + +-#define VEXPRESS_KERNEL_ADDR 0x80080000 +-#define VEXPRESS_FDT_ADDR 0x8fc00000 +-#define VEXPRESS_BOOT_ADDR 0x8fd00000 +-#define VEXPRESS_RAMDISK_ADDR 0x8fe00000 ++#define VEXPRESS_KERNEL_ADDR 0x80080000 ++#define VEXPRESS_FDT_ADDR 0x8fc00000 ++#define VEXPRESS_BOOT_ADDR 0x8fd00000 ++#define VEXPRESS_RAMDISK_ADDR 0x8fe00000 + +-#define CONFIG_EXTRA_ENV_SETTINGS \ ++#define CONFIG_EXTRA_ENV_SETTINGS \ + "kernel_name=Image\0" \ +- "kernel_addr_r=" __stringify(VEXPRESS_KERNEL_ADDR) "\0" \ +- "ramdisk_name=ramdisk.img\0" \ +- "ramdisk_addr_r=" __stringify(VEXPRESS_RAMDISK_ADDR) "\0" \ +- "fdtfile=devtree.dtb\0" \ +- "fdt_addr_r=" __stringify(VEXPRESS_FDT_ADDR) "\0" \ +- "boot_name=boot.img\0" \ +- "boot_addr_r=" __stringify(VEXPRESS_BOOT_ADDR) "\0" +- ++ "kernel_addr_r=" __stringify(VEXPRESS_KERNEL_ADDR) "\0" \ ++ "ramdisk_name=ramdisk.img\0" \ ++ "ramdisk_addr_r=" __stringify(VEXPRESS_RAMDISK_ADDR) "\0" \ ++ "fdtfile=devtree.dtb\0" \ ++ "fdt_addr_r=" __stringify(VEXPRESS_FDT_ADDR) "\0" \ ++ "boot_name=boot.img\0" \ ++ "boot_addr_r=" __stringify(VEXPRESS_BOOT_ADDR) "\0" ++ ++#ifndef CONFIG_BOOTCOMMAND ++#define CONFIG_BOOTCOMMAND "if smhload ${boot_name} ${boot_addr_r}; then " \ ++ " set bootargs; " \ ++ " abootimg addr ${boot_addr_r}; " \ ++ " abootimg get dtb --index=0 fdt_addr_r; " \ ++ " bootm ${boot_addr_r} ${boot_addr_r} " \ ++ " ${fdt_addr_r}; " \ ++ "else; " \ ++ " smhload ${kernel_name} ${kernel_addr_r}; " \ ++ " smhload ${fdtfile} ${fdt_addr_r}; " \ ++ " smhload ${ramdisk_name} ${initrd_addr_r} "\ ++ " initrd_end; " \ ++ " fdt addr ${fdt_addr_r}; fdt resize; " \ ++ " fdt chosen ${ramdisk_addr_r} ${initrd_end}; " \ ++ " bootz $kernel_addr_r - $fdt_addr_r; " \ ++ "fi" ++#endif + #endif + + /* Monitor Command Prompt */ +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-base-arm32/0002-Revert-vexpress64-Enable-OF_CONTROL-and-OF_BOARD-for.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-base-arm32/0002-Revert-vexpress64-Enable-OF_CONTROL-and-OF_BOARD-for.patch new file mode 100644 index 0000000000..d916d42061 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-base-arm32/0002-Revert-vexpress64-Enable-OF_CONTROL-and-OF_BOARD-for.patch @@ -0,0 +1,111 @@ +From e896d48c57d272327410416887f34ac0db550390 Mon Sep 17 00:00:00 2001 +From: Jon Mason <jdmason@kudzu.us> +Date: Mon, 13 Jun 2022 10:59:53 -0400 +Subject: [PATCH 2/2] Revert "vexpress64: Enable OF_CONTROL and OF_BOARD for + VExpress64" + +This patch only works for aarch64 (as the 'x' registers are not +available for ARMv7). Since this platform is ARMv7 in the previous +patch, this either needs to be changed or removed. I opted to remove +it, as it doesn't seem to be necessary to boot the virtual hardware. +Given that the previous patch was rejected upstream, it is not +appropriate to fix this upstream. + +Upstream-Status: Inappropriate +Signed-off-by: Jon Mason <jon.mason@arm.com> + +This reverts commit 2661397464e47d45cd25bbc5e6b9de7594b3268d. +--- + board/armltd/vexpress64/Makefile | 2 +- + board/armltd/vexpress64/lowlevel_init.S | 12 ------------ + board/armltd/vexpress64/vexpress64.c | 26 ------------------------- + 3 files changed, 1 insertion(+), 39 deletions(-) + delete mode 100644 board/armltd/vexpress64/lowlevel_init.S + +diff --git a/board/armltd/vexpress64/Makefile b/board/armltd/vexpress64/Makefile +index 1878fbed4ec9..868dc4f629f2 100644 +--- a/board/armltd/vexpress64/Makefile ++++ b/board/armltd/vexpress64/Makefile +@@ -3,5 +3,5 @@ + # (C) Copyright 2000-2004 + # Wolfgang Denk, DENX Software Engineering, wd@denx.de. + +-obj-y := vexpress64.o lowlevel_init.o ++obj-y := vexpress64.o + obj-$(CONFIG_TARGET_VEXPRESS64_JUNO) += pcie.o +diff --git a/board/armltd/vexpress64/lowlevel_init.S b/board/armltd/vexpress64/lowlevel_init.S +deleted file mode 100644 +index 3dcfb85d0e9a..000000000000 +--- a/board/armltd/vexpress64/lowlevel_init.S ++++ /dev/null +@@ -1,12 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0 */ +-/* +- * (C) Copyright 2021 Arm Limited +- */ +- +-.global save_boot_params +-save_boot_params: +- +- adr x8, prior_stage_fdt_address +- str x0, [x8] +- +- b save_boot_params_ret +diff --git a/board/armltd/vexpress64/vexpress64.c b/board/armltd/vexpress64/vexpress64.c +index 5e22e89824ee..cedab86d984b 100644 +--- a/board/armltd/vexpress64/vexpress64.c ++++ b/board/armltd/vexpress64/vexpress64.c +@@ -92,15 +92,7 @@ int dram_init_banksize(void) + return 0; + } + +-/* Assigned in lowlevel_init.S +- * Push the variable into the .data section so that it +- * does not get cleared later. +- */ +-unsigned long __section(".data") prior_stage_fdt_address; +- + #ifdef CONFIG_OF_BOARD +- +-#ifdef CONFIG_TARGET_VEXPRESS64_JUNO + #define JUNO_FLASH_SEC_SIZE (256 * 1024) + static phys_addr_t find_dtb_in_nor_flash(const char *partname) + { +@@ -145,11 +137,9 @@ static phys_addr_t find_dtb_in_nor_flash(const char *partname) + + return ~0; + } +-#endif + + void *board_fdt_blob_setup(int *err) + { +-#ifdef CONFIG_TARGET_VEXPRESS64_JUNO + phys_addr_t fdt_rom_addr = find_dtb_in_nor_flash(CONFIG_JUNO_DTB_PART); + + *err = 0; +@@ -159,22 +149,6 @@ void *board_fdt_blob_setup(int *err) + } + + return (void *)fdt_rom_addr; +-#endif +- +-#ifdef VEXPRESS_FDT_ADDR +- if (fdt_magic(VEXPRESS_FDT_ADDR) == FDT_MAGIC) { +- *err = 0; +- return (void *)VEXPRESS_FDT_ADDR; +- } +-#endif +- +- if (fdt_magic(prior_stage_fdt_address) == FDT_MAGIC) { +- *err = 0; +- return (void *)prior_stage_fdt_address; +- } +- +- *err = -ENXIO; +- return NULL; + } + #endif + +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-base/bootargs.cfg b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-base/bootargs.cfg new file mode 100644 index 0000000000..716600f482 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-base/bootargs.cfg @@ -0,0 +1,3 @@ +CONFIG_BOOTARGS="console=ttyAMA0 earlycon=pl011,0x1c090000 root=/dev/vda1 rw rootwait" +# Our FVP support CRC instructions +CONFIG_ARM64_CRC32=y diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0001-armv8-Add-ARMv8-MPU-configuration-logic.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0001-armv8-Add-ARMv8-MPU-configuration-logic.patch new file mode 100644 index 0000000000..dd6b77d3a5 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0001-armv8-Add-ARMv8-MPU-configuration-logic.patch @@ -0,0 +1,259 @@ +From e90aa7853ae32cb03c86249a6c572ec88cdebaa2 Mon Sep 17 00:00:00 2001 +From: Peter Hoyes <Peter.Hoyes@arm.com> +Date: Wed, 26 May 2021 17:41:10 +0100 +Subject: [PATCH 1/9] armv8: Add ARMv8 MPU configuration logic + +Detect whether an MMU is present at the current exception level. If +not, initialize the MPU instead of the MMU during init, and clear the +MPU regions before transition to Linux. + +The MSA in use at EL1&0 may be configurable but can only by determined +by inspecting VTCR_EL2 at EL2, so assume that there is an MMU for +backwards compatibility. + +Provide a default (blank) MPU memory map, which can be overridden by +board configurations. + +Issue-Id: SCM-2443 +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Peter Hoyes <Peter.Hoyes@arm.com> +Change-Id: I0ee3879f9d7f03fe940664b3551c68eeaa458d17 +--- + arch/arm/cpu/armv8/cache_v8.c | 101 ++++++++++++++++++++++++++++++- + arch/arm/include/asm/armv8/mpu.h | 59 ++++++++++++++++++ + arch/arm/include/asm/system.h | 19 ++++++ + 3 files changed, 176 insertions(+), 3 deletions(-) + create mode 100644 arch/arm/include/asm/armv8/mpu.h + +diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c +index e4736e5643..798aed8058 100644 +--- a/arch/arm/cpu/armv8/cache_v8.c ++++ b/arch/arm/cpu/armv8/cache_v8.c +@@ -15,6 +15,7 @@ + #include <asm/global_data.h> + #include <asm/system.h> + #include <asm/armv8/mmu.h> ++#include <asm/armv8/mpu.h> + + DECLARE_GLOBAL_DATA_PTR; + +@@ -385,6 +386,91 @@ __weak u64 get_page_table_size(void) + return size; + } + ++static void mpu_clear_regions(void) ++{ ++ int i; ++ ++ for (i = 0; mpu_mem_map[i].end || mpu_mem_map[i].attrs; i++) { ++ setup_el2_mpu_region(i, 0, 0); ++ } ++} ++ ++static struct mpu_region default_mpu_mem_map[] = {{0,}}; ++__weak struct mpu_region *mpu_mem_map = default_mpu_mem_map; ++ ++static void mpu_setup(void) ++{ ++ int i; ++ ++ if (current_el() != 2) { ++ panic("MPU configuration is only supported at EL2"); ++ } ++ ++ set_sctlr(get_sctlr() & ~(CR_M | CR_WXN)); ++ ++ asm volatile("msr MAIR_EL2, %0" : : "r" MEMORY_ATTRIBUTES); ++ ++ for (i = 0; mpu_mem_map[i].end || mpu_mem_map[i].attrs; i++) { ++ setup_el2_mpu_region(i, ++ PRBAR_ADDRESS(mpu_mem_map[i].start) ++ | PRBAR_OUTER_SH | PRBAR_AP_RW_ANY, ++ PRLAR_ADDRESS(mpu_mem_map[i].end) ++ | mpu_mem_map[i].attrs | PRLAR_EN_BIT ++ ); ++ } ++ ++ set_sctlr(get_sctlr() | CR_M); ++} ++ ++static bool el_has_mmu(void) ++{ ++ if (current_el() < 2) { ++ // We have no way of knowing, so assuming we have an MMU ++ return true; ++ } ++ ++ uint64_t id_aa64mmfr0; ++ asm volatile("mrs %0, id_aa64mmfr0_el1" ++ : "=r" (id_aa64mmfr0) : : "cc"); ++ uint64_t msa = id_aa64mmfr0 & ID_AA64MMFR0_EL1_MSA_MASK; ++ uint64_t msa_frac = id_aa64mmfr0 & ID_AA64MMFR0_EL1_MSA_FRAC_MASK; ++ ++ switch (msa) { ++ case ID_AA64MMFR0_EL1_MSA_VMSA: ++ /* ++ * VMSA supported in all translation regimes. ++ * No support for PMSA. ++ */ ++ return true; ++ case ID_AA64MMFR0_EL1_MSA_USE_FRAC: ++ /* See MSA_frac for the supported MSAs. */ ++ switch (msa_frac) { ++ case ID_AA64MMFR0_EL1_MSA_FRAC_NO_PMSA: ++ /* ++ * PMSA not supported in any translation ++ * regime. ++ */ ++ return true; ++ case ID_AA64MMFR0_EL1_MSA_FRAC_VMSA: ++ /* ++ * PMSA supported in all translation ++ * regimes. No support for VMSA. ++ */ ++ case ID_AA64MMFR0_EL1_MSA_FRAC_PMSA: ++ /* ++ * PMSA supported in all translation ++ * regimes. ++ */ ++ return false; ++ default: ++ panic("Unsupported id_aa64mmfr0_el1 " \ ++ "MSA_frac value"); ++ } ++ default: ++ panic("Unsupported id_aa64mmfr0_el1 MSA value"); ++ } ++} ++ + void setup_pgtables(void) + { + int i; +@@ -499,8 +585,13 @@ void dcache_enable(void) + /* The data cache is not active unless the mmu is enabled */ + if (!(get_sctlr() & CR_M)) { + invalidate_dcache_all(); +- __asm_invalidate_tlb_all(); +- mmu_setup(); ++ ++ if (el_has_mmu()) { ++ __asm_invalidate_tlb_all(); ++ mmu_setup(); ++ } else { ++ mpu_setup(); ++ } + } + + set_sctlr(get_sctlr() | CR_C); +@@ -519,7 +610,11 @@ void dcache_disable(void) + set_sctlr(sctlr & ~(CR_C|CR_M)); + + flush_dcache_all(); +- __asm_invalidate_tlb_all(); ++ ++ if (el_has_mmu()) ++ __asm_invalidate_tlb_all(); ++ else ++ mpu_clear_regions(); + } + + int dcache_status(void) +diff --git a/arch/arm/include/asm/armv8/mpu.h b/arch/arm/include/asm/armv8/mpu.h +new file mode 100644 +index 0000000000..8de627cafd +--- /dev/null ++++ b/arch/arm/include/asm/armv8/mpu.h +@@ -0,0 +1,59 @@ ++/* ++ * SPDX-License-Identifier: GPL-2.0+ ++ * ++ * (C) Copyright 2021 Arm Limited ++ */ ++ ++#ifndef _ASM_ARMV8_MPU_H_ ++#define _ASM_ARMV8_MPU_H_ ++ ++#include <asm/armv8/mmu.h> ++#include <linux/stringify.h> ++ ++#define PRSELR_EL2 S3_4_c6_c2_1 ++#define PRBAR_EL2 S3_4_c6_c8_0 ++#define PRLAR_EL2 S3_4_c6_c8_1 ++#define MPUIR_EL2 S3_4_c0_c0_4 ++ ++#define PRBAR_ADDRESS(addr) ((addr) & ~(0x3fULL)) ++ ++/* Access permissions */ ++#define PRBAR_AP(val) (((val) & 0x3) << 2) ++#define PRBAR_AP_RW_HYP PRBAR_AP(0x0) ++#define PRBAR_AP_RW_ANY PRBAR_AP(0x1) ++#define PRBAR_AP_RO_HYP PRBAR_AP(0x2) ++#define PRBAR_AP_RO_ANY PRBAR_AP(0x3) ++ ++/* Shareability */ ++#define PRBAR_SH(val) (((val) & 0x3) << 4) ++#define PRBAR_NON_SH PRBAR_SH(0x0) ++#define PRBAR_OUTER_SH PRBAR_SH(0x2) ++#define PRBAR_INNER_SH PRBAR_SH(0x3) ++ ++/* Memory attribute (MAIR idx) */ ++#define PRLAR_ATTRIDX(val) (((val) & 0x7) << 1) ++#define PRLAR_EN_BIT (0x1) ++#define PRLAR_ADDRESS(addr) ((addr) & ~(0x3fULL)) ++ ++#ifndef __ASSEMBLY__ ++ ++static inline void setup_el2_mpu_region(uint8_t region, uint64_t base, uint64_t limit) ++{ ++ asm volatile("msr " __stringify(PRSELR_EL2) ", %0" : : "r" (region)); ++ asm volatile("msr " __stringify(PRBAR_EL2) ", %0" : : "r" (base)); ++ asm volatile("msr " __stringify(PRLAR_EL2) ", %0" : : "r" (limit)); ++ ++ asm volatile("isb"); ++} ++ ++#endif ++ ++struct mpu_region { ++ u64 start; ++ u64 end; ++ u64 attrs; ++}; ++ ++extern struct mpu_region *mpu_mem_map; ++ ++#endif /* _ASM_ARMV8_MPU_H_ */ +diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h +index 87d1c77e8b..4510db98a2 100644 +--- a/arch/arm/include/asm/system.h ++++ b/arch/arm/include/asm/system.h +@@ -95,6 +95,25 @@ + auth algorithm */ + #define ID_AA64ISAR1_EL1_APA (0xF << 4) /* QARMA address auth algorithm */ + ++/* ++ * ID_AA64MMFR0_EL1 bits definitions ++ */ ++#define ID_AA64MMFR0_EL1_MSA_FRAC_MASK (0xFUL << 52) /* Memory system ++ architecture ++ frac */ ++#define ID_AA64MMFR0_EL1_MSA_FRAC_VMSA (0x2UL << 52) /* EL1&0 supports ++ VMSA */ ++#define ID_AA64MMFR0_EL1_MSA_FRAC_PMSA (0x1UL << 52) /* EL1&0 only ++ supports PMSA*/ ++#define ID_AA64MMFR0_EL1_MSA_FRAC_NO_PMSA (0x0UL << 52) /* No PMSA ++ support */ ++#define ID_AA64MMFR0_EL1_MSA_MASK (0xFUL << 48) /* Memory system ++ architecture */ ++#define ID_AA64MMFR0_EL1_MSA_USE_FRAC (0xFUL << 48) /* Use MSA_FRAC */ ++#define ID_AA64MMFR0_EL1_MSA_VMSA (0x0UL << 48) /* Memory system ++ architecture ++ is VMSA */ ++ + /* + * ID_AA64PFR0_EL1 bits definitions + */ +-- +2.25.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0002-vexpress64-add-MPU-memory-map-for-the-BASER_FVP.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0002-vexpress64-add-MPU-memory-map-for-the-BASER_FVP.patch new file mode 100644 index 0000000000..f405457f4c --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0002-vexpress64-add-MPU-memory-map-for-the-BASER_FVP.patch @@ -0,0 +1,61 @@ +From 80c64d0c035a5cd0cb23c7b01be39f25e15c7193 Mon Sep 17 00:00:00 2001 +From: Qi Feng <qi.feng@arm.com> +Date: Tue, 26 Jul 2022 18:13:23 +0800 +Subject: [PATCH 2/9] vexpress64: add MPU memory map for the BASER_FVP + +The previous patch added support for initializing an Armv8 MPU. There is only an +MPU at S-EL2 on the BASER_FVP, so add a platform-specific MPU memory map. + +See https://developer.arm.com/documentation/100964/1117/Base-Platform/Base---memory/BaseR-Platform-memory-map + +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Peter Hoyes <Peter.Hoyes@arm.com> +Signed-off-by: Qi Feng <qi.feng@arm.com> +--- + board/armltd/vexpress64/vexpress64.c | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/board/armltd/vexpress64/vexpress64.c b/board/armltd/vexpress64/vexpress64.c +index 709ebf3fb0..1791cd986f 100644 +--- a/board/armltd/vexpress64/vexpress64.c ++++ b/board/armltd/vexpress64/vexpress64.c +@@ -19,6 +19,7 @@ + #include <dm/platform_data/serial_pl01x.h> + #include "pcie.h" + #include <asm/armv8/mmu.h> ++#include <asm/armv8/mpu.h> + #ifdef CONFIG_VIRTIO_NET + #include <virtio_types.h> + #include <virtio.h> +@@ -37,6 +38,27 @@ U_BOOT_DRVINFO(vexpress_serials) = { + .plat = &serial_plat, + }; + ++static struct mpu_region vexpress64_aemv8r_mem_map[] = { ++ { ++ .start = 0x0UL, ++ .end = 0x7fffffffUL, ++ .attrs = PRLAR_ATTRIDX(MT_NORMAL) ++ }, { ++ .start = 0x80000000UL, ++ .end = 0xffffffffUL, ++ .attrs = PRLAR_ATTRIDX(MT_DEVICE_NGNRNE) ++ }, { ++ .start = 0x100000000UL, ++ .end = 0xffffffffffUL, ++ .attrs = PRLAR_ATTRIDX(MT_NORMAL) ++ }, { ++ /* List terminator */ ++ 0, ++ } ++}; ++ ++struct mpu_region *mpu_mem_map = vexpress64_aemv8r_mem_map; ++ + static struct mm_region vexpress64_mem_map[] = { + { + .virt = V2M_PA_BASE, +-- +2.25.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0003-armv8-Allow-disabling-exception-vectors-on-non-SPL-b.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0003-armv8-Allow-disabling-exception-vectors-on-non-SPL-b.patch new file mode 100644 index 0000000000..e6ccc1749f --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0003-armv8-Allow-disabling-exception-vectors-on-non-SPL-b.patch @@ -0,0 +1,107 @@ +From 61a2df4467c13bbc83bff8a3429e9ebb4b957b56 Mon Sep 17 00:00:00 2001 +From: Peter Hoyes <Peter.Hoyes@arm.com> +Date: Fri, 10 Dec 2021 11:41:19 +0000 +Subject: [PATCH 3/9] armv8: Allow disabling exception vectors on non-SPL + builds + +On the BASER_FVP, U-Boot shares EL2 with another bootloader, so we do +not wish to overide the exception vector, but we are also not using an +SPL build. + +Therefore, add ARMV8_EXCEPTION_VECTORS, which disables exception vectors +in a similar way to ARMV8_SPL_EXCEPTION_VECTORS. + +Rename ARMV8_SPL_EXCEPTION_VECTORS -> SPL_ARMV8_EXCEPTION_VECTORS so +that both config flags be be targeted using CONFIG_IS_ENABLED. + +Issue-Id: SCM-3728 +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Peter Hoyes <Peter.Hoyes@arm.com> +Change-Id: I0cf0fc6d7ef4d45791411cf1f67c65e198cc8b2b +--- + arch/arm/cpu/armv8/Kconfig | 10 ++++++++-- + arch/arm/cpu/armv8/Makefile | 6 ++---- + arch/arm/cpu/armv8/start.S | 4 ++-- + configs/vexpress_aemv8r_defconfig | 1 + + 4 files changed, 13 insertions(+), 8 deletions(-) + +diff --git a/arch/arm/cpu/armv8/Kconfig b/arch/arm/cpu/armv8/Kconfig +index 09f3f50fa2..031faa909c 100644 +--- a/arch/arm/cpu/armv8/Kconfig ++++ b/arch/arm/cpu/armv8/Kconfig +@@ -1,8 +1,8 @@ + if ARM64 + +-config ARMV8_SPL_EXCEPTION_VECTORS ++config ARMV8_EXCEPTION_VECTORS + bool "Install crash dump exception vectors" +- depends on SPL ++ default y + help + The default exception vector table is only used for the crash + dump, but still takes quite a lot of space in the image size. +@@ -10,6 +10,12 @@ config ARMV8_SPL_EXCEPTION_VECTORS + Say N here if you are running out of code space in the image + and want to save some space at the cost of less debugging info. + ++config SPL_ARMV8_EXCEPTION_VECTORS ++ bool "Install crash dump exception vectors in the SPL" ++ depends on SPL ++ help ++ Same as ARMV8_EXCEPTION_VECTORS, but for SPL builds ++ + config ARMV8_MULTIENTRY + bool "Enable multiple CPUs to enter into U-Boot" + +diff --git a/arch/arm/cpu/armv8/Makefile b/arch/arm/cpu/armv8/Makefile +index 85fe0475c8..8b3f695835 100644 +--- a/arch/arm/cpu/armv8/Makefile ++++ b/arch/arm/cpu/armv8/Makefile +@@ -13,10 +13,8 @@ ifndef CONFIG_$(SPL_)SYS_DCACHE_OFF + obj-y += cache_v8.o + obj-y += cache.o + endif +-ifdef CONFIG_SPL_BUILD +-obj-$(CONFIG_ARMV8_SPL_EXCEPTION_VECTORS) += exceptions.o +-else +-obj-y += exceptions.o ++obj-$(CONFIG_$(SPL_)ARMV8_EXCEPTION_VECTORS) += exceptions.o ++ifndef CONFIG_SPL_BUILD + obj-y += exception_level.o + endif + obj-y += tlb.o +diff --git a/arch/arm/cpu/armv8/start.S b/arch/arm/cpu/armv8/start.S +index 28f0df13f0..f831e77af3 100644 +--- a/arch/arm/cpu/armv8/start.S ++++ b/arch/arm/cpu/armv8/start.S +@@ -104,7 +104,7 @@ pie_skip_reloc: + pie_fixup_done: + #endif + +-#if defined(CONFIG_ARMV8_SPL_EXCEPTION_VECTORS) || !defined(CONFIG_SPL_BUILD) ++#if CONFIG_IS_ENABLED(ARMV8_EXCEPTION_VECTORS) + .macro set_vbar, regname, reg + msr \regname, \reg + .endm +@@ -354,7 +354,7 @@ ENDPROC(smp_kick_all_cpus) + /*-----------------------------------------------------------------------*/ + + ENTRY(c_runtime_cpu_setup) +-#if defined(CONFIG_ARMV8_SPL_EXCEPTION_VECTORS) || !defined(CONFIG_SPL_BUILD) ++#if CONFIG_IS_ENABLED(ARMV8_EXCEPTION_VECTORS) + /* Relocate vBAR */ + adr x0, vectors + switch_el x1, 3f, 2f, 1f +diff --git a/configs/vexpress_aemv8r_defconfig b/configs/vexpress_aemv8r_defconfig +index a1c5d88717..1d5b7411f0 100644 +--- a/configs/vexpress_aemv8r_defconfig ++++ b/configs/vexpress_aemv8r_defconfig +@@ -13,3 +13,4 @@ CONFIG_BOOTARGS="console=ttyAMA0 earlycon=pl011,0x9c090000 rootfstype=ext4 root= + CONFIG_SYS_PROMPT="VExpress64# " + # CONFIG_MMC is not set + CONFIG_VIRTIO_MMIO=y ++CONFIG_ARMV8_EXCEPTION_VECTORS=n +-- +2.25.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0004-armv8-ARMV8_SWITCH_TO_EL1-improvements.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0004-armv8-ARMV8_SWITCH_TO_EL1-improvements.patch new file mode 100644 index 0000000000..84f6b4bb84 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0004-armv8-ARMV8_SWITCH_TO_EL1-improvements.patch @@ -0,0 +1,160 @@ +From dbc1a218e9837e39cd50dd3c19f603f29a08ddba Mon Sep 17 00:00:00 2001 +From: Peter Hoyes <Peter.Hoyes@arm.com> +Date: Wed, 14 Jul 2021 12:44:27 +0100 +Subject: [PATCH 4/9] armv8: ARMV8_SWITCH_TO_EL1 improvements + +Convert CONFIG_ARMV8_SWITCH_TO_EL1 to a Kconfig variable. + +Add support for switching to EL1 to bootefi. + +Add the environment variable armv8_switch_to_el1 to allow configuring +whether to switch to EL1 at runtime. This overrides the compile-time +option. + +Issue-Id: SCM-3728 +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Peter Hoyes <Peter.Hoyes@arm.com> +Change-Id: If98478148d6d8d1f732acac5439276700614815f +--- + arch/arm/cpu/armv8/Kconfig | 8 +++++++ + arch/arm/cpu/armv8/exception_level.c | 21 ++++++++++++++-- + arch/arm/lib/bootm.c | 36 ++++++++++++++++------------ + configs/vexpress_aemv8r_defconfig | 1 + + 4 files changed, 49 insertions(+), 17 deletions(-) + +diff --git a/arch/arm/cpu/armv8/Kconfig b/arch/arm/cpu/armv8/Kconfig +index 031faa909c..110adf63b3 100644 +--- a/arch/arm/cpu/armv8/Kconfig ++++ b/arch/arm/cpu/armv8/Kconfig +@@ -191,4 +191,12 @@ config ARMV8_EA_EL3_FIRST + Exception handling at all exception levels for External Abort and + SError interrupt exception are taken in EL3. + ++config ARMV8_SWITCH_TO_EL1 ++ bool "Switch to EL1 before booting the operating system" ++ default n ++ help ++ Switch to EL1 before booting the operating system, if for example the ++ operating system does not support booting at EL2, or you wish to prevent ++ any hypervisors from running. Supported for bootm, booti and bootefi. ++ + endif +diff --git a/arch/arm/cpu/armv8/exception_level.c b/arch/arm/cpu/armv8/exception_level.c +index b11936548f..4aad1550f4 100644 +--- a/arch/arm/cpu/armv8/exception_level.c ++++ b/arch/arm/cpu/armv8/exception_level.c +@@ -40,19 +40,36 @@ static void entry_non_secure(struct jmp_buf_data *non_secure_jmp) + * trusted firmware being one embodiment). The operating system shall be + * started at exception level EL2. So here we check the exception level + * and switch it if necessary. ++ * ++ * If armv8_switch_to_el1 (config or env var) is enabled, also switch to EL1 ++ * before booting the operating system. + */ + void switch_to_non_secure_mode(void) + { + struct jmp_buf_data non_secure_jmp; + + /* On AArch64 we need to make sure we call our payload in < EL3 */ +- if (current_el() == 3) { ++ ++ int switch_to_el1 = env_get_yesno("armv8_switch_to_el1"); ++#ifdef CONFIG_ARMV8_SWITCH_TO_EL1 ++ if (switch_to_el1 == -1) { ++ switch_to_el1 = 1; ++ } ++#endif ++ ++ if (current_el() > 2) { + if (setjmp(&non_secure_jmp)) + return; + dcache_disable(); /* flush cache before switch to EL2 */ +- + /* Move into EL2 and keep running there */ + armv8_switch_to_el2((uintptr_t)&non_secure_jmp, 0, 0, 0, + (uintptr_t)entry_non_secure, ES_TO_AARCH64); ++ } else if (switch_to_el1 == 1 && current_el() > 1) { ++ if (setjmp(&non_secure_jmp)) ++ return; ++ dcache_disable(); /* flush cache before switch to EL1 */ ++ /* Move into EL1 and keep running there */ ++ armv8_switch_to_el1((uintptr_t)&non_secure_jmp, 0, 0, 0, ++ (uintptr_t)entry_non_secure, ES_TO_AARCH64); + } + } +diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c +index a59a5e6c0e..e2cf2e6ec4 100644 +--- a/arch/arm/lib/bootm.c ++++ b/arch/arm/lib/bootm.c +@@ -272,7 +272,6 @@ __weak void update_os_arch_secondary_cores(uint8_t os_arch) + { + } + +-#ifdef CONFIG_ARMV8_SWITCH_TO_EL1 + static void switch_to_el1(void) + { + if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) && +@@ -287,7 +286,6 @@ static void switch_to_el1(void) + ES_TO_AARCH64); + } + #endif +-#endif + + /* Subcommand: GO */ + static void boot_jump_linux(bootm_headers_t *images, int flag) +@@ -314,21 +312,29 @@ static void boot_jump_linux(bootm_headers_t *images, int flag) + + update_os_arch_secondary_cores(images->os.arch); + ++ int armv8_switch_to_el1 = env_get_yesno("armv8_switch_to_el1"); + #ifdef CONFIG_ARMV8_SWITCH_TO_EL1 +- armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0, +- (u64)switch_to_el1, ES_TO_AARCH64); +-#else +- if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) && +- (images->os.arch == IH_ARCH_ARM)) +- armv8_switch_to_el2(0, (u64)gd->bd->bi_arch_number, +- (u64)images->ft_addr, 0, +- (u64)images->ep, +- ES_TO_AARCH32); +- else +- armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0, +- images->ep, +- ES_TO_AARCH64); ++ if (armv8_switch_to_el1 == -1) { ++ armv8_switch_to_el1 = 1; ++ } + #endif ++ if (armv8_switch_to_el1 == 1) { ++ armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0, ++ (u64)switch_to_el1, ES_TO_AARCH64); ++ } else { ++ if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) && ++ (images->os.arch == IH_ARCH_ARM)) ++ armv8_switch_to_el2(0, ++ (u64)gd->bd->bi_arch_number, ++ (u64)images->ft_addr, 0, ++ (u64)images->ep, ++ ES_TO_AARCH32); ++ else ++ armv8_switch_to_el2((u64)images->ft_addr, ++ 0, 0, 0, ++ images->ep, ++ ES_TO_AARCH64); ++ } + } + #else + unsigned long machid = gd->bd->bi_arch_number; +diff --git a/configs/vexpress_aemv8r_defconfig b/configs/vexpress_aemv8r_defconfig +index 1d5b7411f0..35e5e8a5e1 100644 +--- a/configs/vexpress_aemv8r_defconfig ++++ b/configs/vexpress_aemv8r_defconfig +@@ -14,3 +14,4 @@ CONFIG_SYS_PROMPT="VExpress64# " + # CONFIG_MMC is not set + CONFIG_VIRTIO_MMIO=y + CONFIG_ARMV8_EXCEPTION_VECTORS=n ++CONFIG_ARMV8_SWITCH_TO_EL1=y +-- +2.25.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0005-armv8-Make-disabling-HVC-configurable-when-switching.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0005-armv8-Make-disabling-HVC-configurable-when-switching.patch new file mode 100644 index 0000000000..b5460268cb --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0005-armv8-Make-disabling-HVC-configurable-when-switching.patch @@ -0,0 +1,83 @@ +From f75060be04aaed4bb5a07cb40361d694b3775f4a Mon Sep 17 00:00:00 2001 +From: Peter Hoyes <Peter.Hoyes@arm.com> +Date: Fri, 10 Dec 2021 16:37:26 +0000 +Subject: [PATCH 5/9] armv8: Make disabling HVC configurable when switching to + EL1 + +On the BASER_FVP there is no EL3, so HVC is used to provide PSCI +services. Therefore we cannot disable hypercalls. + +Create CONFIG_ARMV8_DISABLE_HVC (dependent on CONFIG_ARMV8_TO_EL1) to +control whether to disable HVC exceptions in HCR_EL2->HCD + +Issue-Id: SCM-3728 +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Peter Hoyes <Peter.Hoyes@arm.com> +Change-Id: I463d82f1db8a3cafcab40a9c0c208753569cc300 +--- + arch/arm/cpu/armv8/Kconfig | 9 +++++++++ + arch/arm/include/asm/macro.h | 10 ++++++++-- + configs/vexpress_aemv8r_defconfig | 1 + + 3 files changed, 18 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/cpu/armv8/Kconfig b/arch/arm/cpu/armv8/Kconfig +index 110adf63b3..a821e219d8 100644 +--- a/arch/arm/cpu/armv8/Kconfig ++++ b/arch/arm/cpu/armv8/Kconfig +@@ -199,4 +199,13 @@ config ARMV8_SWITCH_TO_EL1 + operating system does not support booting at EL2, or you wish to prevent + any hypervisors from running. Supported for bootm, booti and bootefi. + ++config ARMV8_DISABLE_HVC ++ bool "Disable HVC calls before switching to EL1" ++ depends on ARMV8_SWITCH_TO_EL1 ++ default y ++ help ++ If switching to EL1 before loading the operating system, disable taking ++ hypercalls back to EL2. May be disabled if, for example, PSCI services are ++ running at EL2. ++ + endif +diff --git a/arch/arm/include/asm/macro.h b/arch/arm/include/asm/macro.h +index 1a1edc9870..7167739210 100644 +--- a/arch/arm/include/asm/macro.h ++++ b/arch/arm/include/asm/macro.h +@@ -296,9 +296,12 @@ lr .req x30 + ldr \tmp2, =(ID_AA64ISAR1_EL1_GPI | ID_AA64ISAR1_EL1_GPA | \ + ID_AA64ISAR1_EL1_API | ID_AA64ISAR1_EL1_APA) + tst \tmp, \tmp2 +- mov \tmp2, #(HCR_EL2_RW_AARCH64 | HCR_EL2_HCD_DIS) ++ mov \tmp2, #(HCR_EL2_RW_AARCH64) + orr \tmp, \tmp2, #(HCR_EL2_APK | HCR_EL2_API) + csel \tmp, \tmp2, \tmp, eq ++#ifdef CONFIG_ARMV8_DISABLE_HVC ++ orr \tmp, \tmp, #(HCR_EL2_HCD_DIS) ++#endif + msr hcr_el2, \tmp + + /* Return to the EL1_SP1 mode from EL2 */ +@@ -311,7 +314,10 @@ lr .req x30 + + 1: + /* Initialize HCR_EL2 */ +- ldr \tmp, =(HCR_EL2_RW_AARCH32 | HCR_EL2_HCD_DIS) ++ ldr \tmp, =(HCR_EL2_RW_AARCH32) ++#ifdef CONFIG_ARMV8_DISABLE_HVC ++ orr \tmp, \tmp, #(HCR_EL2_HCD_DIS) ++#endif + msr hcr_el2, \tmp + + /* Return to AArch32 Supervisor mode from EL2 */ +diff --git a/configs/vexpress_aemv8r_defconfig b/configs/vexpress_aemv8r_defconfig +index 35e5e8a5e1..605a724a61 100644 +--- a/configs/vexpress_aemv8r_defconfig ++++ b/configs/vexpress_aemv8r_defconfig +@@ -15,3 +15,4 @@ CONFIG_SYS_PROMPT="VExpress64# " + CONFIG_VIRTIO_MMIO=y + CONFIG_ARMV8_EXCEPTION_VECTORS=n + CONFIG_ARMV8_SWITCH_TO_EL1=y ++CONFIG_ARMV8_DISABLE_HVC=n +-- +2.25.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0006-vexpress64-Do-not-set-COUNTER_FREQUENCY.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0006-vexpress64-Do-not-set-COUNTER_FREQUENCY.patch new file mode 100644 index 0000000000..ca99d0e651 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0006-vexpress64-Do-not-set-COUNTER_FREQUENCY.patch @@ -0,0 +1,32 @@ +From db7d4cc91ae19d0cbd05c0ff1b9bf6a55ac0d04a Mon Sep 17 00:00:00 2001 +From: Qi Feng <qi.feng@arm.com> +Date: Thu, 28 Jul 2022 17:47:18 +0800 +Subject: [PATCH 6/9] vexpress64: Do not set COUNTER_FREQUENCY + +VExpress boards normally run as a second-stage bootloader so should not +need to modify CNTFRQ_EL0. On the BASER_FVP, U-Boot can modify it if +running at EL2, but shouldn't because it might be different from the +value being used by the first-stage bootloader (which might be +providing PSCI services). + +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Peter Hoyes <Peter.Hoyes@arm.com> +Signed-off-by: Qi Feng <qi.feng@arm.com> +--- + configs/vexpress_aemv8r_defconfig | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/configs/vexpress_aemv8r_defconfig b/configs/vexpress_aemv8r_defconfig +index 605a724a61..153fa14efc 100644 +--- a/configs/vexpress_aemv8r_defconfig ++++ b/configs/vexpress_aemv8r_defconfig +@@ -1,5 +1,4 @@ + CONFIG_ARM=y +-CONFIG_COUNTER_FREQUENCY=24000000 + CONFIG_ARCH_VEXPRESS64=y + CONFIG_NR_DRAM_BANKS=2 + CONFIG_DEFAULT_DEVICE_TREE="arm_fvp" +-- +2.25.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0007-vexpress64-Enable-LIBFDT_OVERLAY-in-the-vexpress_aem.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0007-vexpress64-Enable-LIBFDT_OVERLAY-in-the-vexpress_aem.patch new file mode 100644 index 0000000000..467fb4060a --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0007-vexpress64-Enable-LIBFDT_OVERLAY-in-the-vexpress_aem.patch @@ -0,0 +1,27 @@ +From f4986ed954ffad36abfa27db5520e702cba2531e Mon Sep 17 00:00:00 2001 +From: Peter Hoyes <Peter.Hoyes@arm.com> +Date: Tue, 22 Feb 2022 15:32:51 +0000 +Subject: [PATCH 7/9] vexpress64: Enable LIBFDT_OVERLAY in the vexpress_aemv8r + defconfig + +Issue-Id: SCM-3874 +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Signed-off-by: Peter Hoyes <Peter.Hoyes@arm.com> +Change-Id: Ide0532cf2de89f1bca9c8d4bd2ed0c1a1c57599f +--- + configs/vexpress_aemv8r_defconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/configs/vexpress_aemv8r_defconfig b/configs/vexpress_aemv8r_defconfig +index 153fa14efc..94651768b3 100644 +--- a/configs/vexpress_aemv8r_defconfig ++++ b/configs/vexpress_aemv8r_defconfig +@@ -15,3 +15,4 @@ CONFIG_VIRTIO_MMIO=y + CONFIG_ARMV8_EXCEPTION_VECTORS=n + CONFIG_ARMV8_SWITCH_TO_EL1=y + CONFIG_ARMV8_DISABLE_HVC=n ++CONFIG_OF_LIBFDT_OVERLAY=y +-- +2.25.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0008-armv8-Allow-PRBAR-MPU-attributes-to-be-configured.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0008-armv8-Allow-PRBAR-MPU-attributes-to-be-configured.patch new file mode 100644 index 0000000000..4b6daf8d0e --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0008-armv8-Allow-PRBAR-MPU-attributes-to-be-configured.patch @@ -0,0 +1,105 @@ +From af05764e7b64e0704291ba2e71138f2345737afa Mon Sep 17 00:00:00 2001 +From: Peter Hoyes <Peter.Hoyes@arm.com> +Date: Wed, 18 May 2022 15:24:19 +0100 +Subject: [PATCH 8/9] armv8: Allow PRBAR MPU attributes to be configured + +In a previous patch, support was added to initialize an S-EL2 MPU on +armv8r64 machines. This implementation allowed the PRLAR attribute +index to be configured, but not the shareability and access permission +attributes in PRBAR. These attributes were hard-coded as "outer +shareable" and "read/write at EL1 and EL0". + +Add separate prlar_attrs and prbar_attrs to the MPU region struct so +that these attributes can be configured on a per-region basis. + +For the BASER_FVP, ensure the MPU memory attributes match those in the +existing vexpress64 board MMU configuration ("non shareable" for device +memory and "inner shareable" for normal memory). + +Issue-Id: SCM-4641 +Signed-off-by: Peter Hoyes <Peter.Hoyes@arm.com> +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Change-Id: I6b72aead91ad12412262aa32c61a53e12eab3984 +--- + arch/arm/cpu/armv8/cache_v8.c | 12 ++++++++---- + arch/arm/include/asm/armv8/mpu.h | 3 ++- + board/armltd/vexpress64/vexpress64.c | 9 ++++++--- + 3 files changed, 16 insertions(+), 8 deletions(-) + +diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c +index 798aed8058..e336339281 100644 +--- a/arch/arm/cpu/armv8/cache_v8.c ++++ b/arch/arm/cpu/armv8/cache_v8.c +@@ -390,7 +390,9 @@ static void mpu_clear_regions(void) + { + int i; + +- for (i = 0; mpu_mem_map[i].end || mpu_mem_map[i].attrs; i++) { ++ for (i = 0; mpu_mem_map[i].end || ++ mpu_mem_map[i].prbar_attrs || ++ mpu_mem_map[i].prlar_attrs; i++) { + setup_el2_mpu_region(i, 0, 0); + } + } +@@ -410,12 +412,14 @@ static void mpu_setup(void) + + asm volatile("msr MAIR_EL2, %0" : : "r" MEMORY_ATTRIBUTES); + +- for (i = 0; mpu_mem_map[i].end || mpu_mem_map[i].attrs; i++) { ++ for (i = 0; mpu_mem_map[i].end || ++ mpu_mem_map[i].prbar_attrs || ++ mpu_mem_map[i].prlar_attrs; i++) { + setup_el2_mpu_region(i, + PRBAR_ADDRESS(mpu_mem_map[i].start) +- | PRBAR_OUTER_SH | PRBAR_AP_RW_ANY, ++ | mpu_mem_map[i].prbar_attrs, + PRLAR_ADDRESS(mpu_mem_map[i].end) +- | mpu_mem_map[i].attrs | PRLAR_EN_BIT ++ | mpu_mem_map[i].prlar_attrs | PRLAR_EN_BIT + ); + } + +diff --git a/arch/arm/include/asm/armv8/mpu.h b/arch/arm/include/asm/armv8/mpu.h +index 8de627cafd..dd4c689ea6 100644 +--- a/arch/arm/include/asm/armv8/mpu.h ++++ b/arch/arm/include/asm/armv8/mpu.h +@@ -51,7 +51,8 @@ static inline void setup_el2_mpu_region(uint8_t region, uint64_t base, uint64_t + struct mpu_region { + u64 start; + u64 end; +- u64 attrs; ++ u64 prbar_attrs; ++ u64 prlar_attrs; + }; + + extern struct mpu_region *mpu_mem_map; +diff --git a/board/armltd/vexpress64/vexpress64.c b/board/armltd/vexpress64/vexpress64.c +index 1791cd986f..7858521fec 100644 +--- a/board/armltd/vexpress64/vexpress64.c ++++ b/board/armltd/vexpress64/vexpress64.c +@@ -42,15 +42,18 @@ static struct mpu_region vexpress64_aemv8r_mem_map[] = { + { + .start = 0x0UL, + .end = 0x7fffffffUL, +- .attrs = PRLAR_ATTRIDX(MT_NORMAL) ++ .prbar_attrs = PRBAR_INNER_SH | PRBAR_AP_RW_ANY, ++ .prlar_attrs = PRLAR_ATTRIDX(MT_NORMAL) + }, { + .start = 0x80000000UL, + .end = 0xffffffffUL, +- .attrs = PRLAR_ATTRIDX(MT_DEVICE_NGNRNE) ++ .prbar_attrs = PRBAR_OUTER_SH | PRBAR_AP_RW_ANY, ++ .prlar_attrs = PRLAR_ATTRIDX(MT_DEVICE_NGNRNE) + }, { + .start = 0x100000000UL, + .end = 0xffffffffffUL, +- .attrs = PRLAR_ATTRIDX(MT_NORMAL) ++ .prbar_attrs = PRBAR_INNER_SH | PRBAR_AP_RW_ANY, ++ .prlar_attrs = PRLAR_ATTRIDX(MT_NORMAL) + }, { + /* List terminator */ + 0, +-- +2.25.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0009-armv8-Enable-icache-when-switching-exception-levels-.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0009-armv8-Enable-icache-when-switching-exception-levels-.patch new file mode 100644 index 0000000000..0e0a248136 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/fvp-baser-aemv8r64/0009-armv8-Enable-icache-when-switching-exception-levels-.patch @@ -0,0 +1,63 @@ +From 0f15f6b02825b042ddc1d753f62cf87f30b1fe12 Mon Sep 17 00:00:00 2001 +From: Peter Hoyes <Peter.Hoyes@arm.com> +Date: Thu, 19 May 2022 09:02:32 +0100 +Subject: [PATCH 9/9] armv8: Enable icache when switching exception levels in + bootefi + +bootefi calls the function switch_to_non_secure_mode before calling the +UEFI payload to handle the case where U-Boot is running at EL3. + +For AArch64, the UEFI specification states that: + The core will be configured as follows: + * MMU enabled + * Instruction and data caches enabled + +These requirements should be followed when switching exception levels +for EFI applications. + +This function already disables and re-enables the data cache prior to +switching exception levels, but omits the instruction cache, meaning +the function returns with the instruction cache disabled at the new +exception level. Fix this by calling icache_disable prior to switching +exception levels and icache_enable afterwards. + +Issue-Id: SCM-4641 +Signed-off-by: Peter Hoyes <Peter.Hoyes@arm.com> +Upstream-Status: Inappropriate [other] + Implementation pending further discussion +Change-Id: I678cd5ba39b56e124ab7854608289cd14651ce65 +--- + arch/arm/cpu/armv8/exception_level.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/arm/cpu/armv8/exception_level.c b/arch/arm/cpu/armv8/exception_level.c +index 4aad1550f4..0a3e5428e7 100644 +--- a/arch/arm/cpu/armv8/exception_level.c ++++ b/arch/arm/cpu/armv8/exception_level.c +@@ -27,6 +27,7 @@ + static void entry_non_secure(struct jmp_buf_data *non_secure_jmp) + { + dcache_enable(); ++ icache_enable(); + debug("Reached non-secure mode\n"); + + /* Restore stack and registers saved in switch_to_non_secure_mode() */ +@@ -61,6 +62,7 @@ void switch_to_non_secure_mode(void) + if (setjmp(&non_secure_jmp)) + return; + dcache_disable(); /* flush cache before switch to EL2 */ ++ icache_disable(); + /* Move into EL2 and keep running there */ + armv8_switch_to_el2((uintptr_t)&non_secure_jmp, 0, 0, 0, + (uintptr_t)entry_non_secure, ES_TO_AARCH64); +@@ -68,6 +70,7 @@ void switch_to_non_secure_mode(void) + if (setjmp(&non_secure_jmp)) + return; + dcache_disable(); /* flush cache before switch to EL1 */ ++ icache_disable(); + /* Move into EL1 and keep running there */ + armv8_switch_to_el1((uintptr_t)&non_secure_jmp, 0, 0, 0, + (uintptr_t)entry_non_secure, ES_TO_AARCH64); +-- +2.25.1 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/juno/0001-arm-juno-add-custom-bootcmd-to-autoboot-from-uEnv.tx.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/juno/0001-arm-juno-add-custom-bootcmd-to-autoboot-from-uEnv.tx.patch new file mode 100644 index 0000000000..a6838390c7 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/juno/0001-arm-juno-add-custom-bootcmd-to-autoboot-from-uEnv.tx.patch @@ -0,0 +1,50 @@ +From 13dde05efae99c5261ed213108087d0f7ac9581e Mon Sep 17 00:00:00 2001 +From: Damodar Santhapuri <damodar.santhapuri@arm.com> +Date: Thu, 5 Nov 2020 22:40:48 +0530 +Subject: [PATCH] arm: juno: add custom bootcmd to autoboot from uEnv.txt file + +enable autoboot support with custom bootcmd loads uEnv.txt +from NOR to DRAM and import. + +Signed-off-by: Damodar Santhapuri <damodar.santhapuri@arm.com> +Upstream-Status: Pending +--- + include/configs/vexpress_aemv8.h | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/include/configs/vexpress_aemv8.h b/include/configs/vexpress_aemv8.h +index f0c5ceb3849a..cd7f6c1b9ba0 100644 +--- a/include/configs/vexpress_aemv8.h ++++ b/include/configs/vexpress_aemv8.h +@@ -137,13 +137,13 @@ + " afs load ${fdt_alt_name} ${fdt_addr_r}; "\ + "fi ; "\ + "fdt addr ${fdt_addr_r}; fdt resize; " \ +- "if afs load ${ramdisk_name} ${ramdisk_addr_r} ; "\ ++ "if afs load ${initrd_name} ${initrd_addr_r} ; "\ + "then "\ +- " setenv ramdisk_param ${ramdisk_addr_r}; "\ ++ " setenv initrd_param ${initrd_addr_r}; "\ + "else "\ +- " setenv ramdisk_param -; "\ ++ " setenv initrd_param -; "\ + "fi ; " \ +- "booti ${kernel_addr_r} ${ramdisk_param} ${fdt_addr_r}\0" ++ "booti ${kernel_addr_r} ${initrd_param} ${fdt_addr_r}" + #define BOOTENV_DEV_NAME_AFS(devtypeu, devtypel, instance) "afs " + + #define BOOT_TARGET_DEVICES(func) \ +@@ -164,8 +164,8 @@ + "kernel_name=norkern\0" \ + "kernel_alt_name=Image\0" \ + "kernel_addr_r=0x80080000\0" \ +- "ramdisk_name=ramdisk.img\0" \ +- "ramdisk_addr_r=0x88000000\0" \ ++ "initrd_name=ramdisk.img\0" \ ++ "initrd_addr_r=0x88000000\0" \ + "fdtfile=board.dtb\0" \ + "fdt_alt_name=juno\0" \ + "fdt_addr_r=0x80000000\0" \ +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/tc/0001-arm-total_compute-update-secure-dram-size.patch b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/tc/0001-arm-total_compute-update-secure-dram-size.patch new file mode 100644 index 0000000000..85b14b03e8 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/tc/0001-arm-total_compute-update-secure-dram-size.patch @@ -0,0 +1,33 @@ +From f7c24393604e45012447b16aaa95eb5e7224ba07 Mon Sep 17 00:00:00 2001 +From: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Date: Tue, 12 Apr 2022 12:43:49 +0100 +Subject: [PATCH] arm: total_compute: update secure dram size + +Update secure DRAM size as it is increased by 64MB for additional +secure partitions. + +Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> +Change-Id: Id8ce99c7a5330d3c28d473009c4db04141e6fa4d +Upstream-Status: Pending [Not submitted to upstream yet] +--- + include/configs/total_compute.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/include/configs/total_compute.h b/include/configs/total_compute.h +index 0324b1e1b217..62bdb4f6a3ae 100644 +--- a/include/configs/total_compute.h ++++ b/include/configs/total_compute.h +@@ -23,8 +23,8 @@ + + /* Physical Memory Map */ + #define PHYS_SDRAM_1 0x80000000 +-/* Top 48MB reserved for secure world use */ +-#define DRAM_SEC_SIZE 0x03000000 ++/* Top 112MB reserved for secure world use */ ++#define DRAM_SEC_SIZE 0x07000000 + #define PHYS_SDRAM_1_SIZE 0x80000000 - DRAM_SEC_SIZE + #define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 + +-- +2.30.2 + diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/tc/bootargs.cfg b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/tc/bootargs.cfg new file mode 100644 index 0000000000..8c3160249c --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot/tc/bootargs.cfg @@ -0,0 +1,2 @@ +CONFIG_USE_BOOTARGS=y +CONFIG_BOOTARGS="console=ttyAMA0 debug user_debug=31 earlycon=pl011,0x7ff80000 loglevel=9 androidboot.hardware=total_compute androidboot.boot_devices=1c050000.mmci ip=dhcp androidboot.selinux=permissive allow_mismatched_32bit_el0" diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot_%.bbappend b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot_%.bbappend new file mode 100644 index 0000000000..e254d41b4c --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot_%.bbappend @@ -0,0 +1,90 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" + +# +# Corstone-500 MACHINE +# +SRC_URI:append:corstone500 = " \ + file://0001-armv7-adding-generic-timer-access-through-MMIO.patch \ + file://0002-board-arm-add-corstone500-board.patch" + +# +# Corstone1000 64-bit machines +# +DEPENDS:append:corstone1000 = " gnutls-native" +CORSTONE1000_DEVICE_TREE:corstone1000-mps3 = "corstone1000-mps3" +CORSTONE1000_DEVICE_TREE:corstone1000-fvp = "corstone1000-fvp" +EXTRA_OEMAKE:append:corstone1000 = ' DEVICE_TREE=${CORSTONE1000_DEVICE_TREE}' + +SYSROOT_DIRS:append:corstone1000 = " /boot" + +SRC_URI:append:corstone1000 = " \ + file://0001-cmd-load-add-load-command-for-memory-mapped.patch \ + file://0002-arm-add-support-to-corstone1000-platform.patch \ + file://0003-usb-common-move-urb-code-to-common.patch \ + file://0004-usb-add-isp1760-family-driver.patch \ + file://0005-corstone1000-enable-isp1763-usb-controller.patch \ + file://0006-arm_ffa-introducing-Arm-FF-A-low-level-driver.patch \ + file://0007-arm_ffa-introducing-armffa-command.patch \ + file://0008-arm_ffa-introducing-MM-communication-with-FF-A.patch \ + file://0009-arm_ffa-introducing-test-module-for-UCLASS_FFA.patch \ + file://0010-arm_ffa-corstone1000-enable-FF-A-and-MM-support.patch \ + file://0011-efi-corstone1000-introduce-EFI-capsule-update.patch \ + file://0012-corstone1000-Update-FFA-shared-buffer-address.patch \ + file://0013-corstone1000-Make-sure-shared-buffer-contents-are-no.patch \ + file://0014-arm-corstone1000-fix-unrecognized-filesystem-type.patch \ + file://0015-efi_capsule-corstone1000-pass-interface-id-and-buffe.patch \ + file://0016-efi_boottime-corstone1000-pass-interface-id-and-kern.patch \ + file://0017-efi_loader-corstone1000-remove-guid-check-from-corst.patch \ + file://0018-arm_ffa-removing-the-cast-when-using-binary-OR-on-FI.patch \ + file://0019-Return-proper-error-code-when-rx-buffer-is-larger.patch \ + file://0020-Use-correct-buffer-size.patch \ + file://0021-Update-comm_buf-when-EFI_BUFFER_TOO_SMALL.patch \ + file://0022-efi_loader-populate-ESRT-table-if-EFI_ESRT-config-op.patch \ + file://0023-efi_firmware-add-get_image_info-for-corstone1000.patch \ + file://0024-Comment-mm_communicate-failure-log.patch \ + file://0025-efi_loader-send-bootcomplete-message-to-secure-encla.patch \ + file://0026-efi_loader-fix-null-pointer-exception-with-get_image.patch \ + file://0027-arm-corstone1000-add-mmc-for-fvp.patch \ + " + +# +# FVP BASE +# +SRC_URI:append:fvp-base = " file://bootargs.cfg" + +# +# FVP BASE ARM32 +# +SRC_URI:append:fvp-base-arm32 = " file://0001-Add-vexpress_aemv8a_aarch32-variant.patch \ + file://0002-Revert-vexpress64-Enable-OF_CONTROL-and-OF_BOARD-for.patch \ + " + +# +# FVP BASER +# +SRC_URI:append:fvp-baser-aemv8r64 = " \ + file://0001-armv8-Add-ARMv8-MPU-configuration-logic.patch \ + file://0002-vexpress64-add-MPU-memory-map-for-the-BASER_FVP.patch \ + file://0003-armv8-Allow-disabling-exception-vectors-on-non-SPL-b.patch \ + file://0004-armv8-ARMV8_SWITCH_TO_EL1-improvements.patch \ + file://0005-armv8-Make-disabling-HVC-configurable-when-switching.patch \ + file://0006-vexpress64-Do-not-set-COUNTER_FREQUENCY.patch \ + file://0007-vexpress64-Enable-LIBFDT_OVERLAY-in-the-vexpress_aem.patch \ + file://0008-armv8-Allow-PRBAR-MPU-attributes-to-be-configured.patch \ + file://0009-armv8-Enable-icache-when-switching-exception-levels-.patch \ + " + + +# +# Juno Machines +# +SRC_URI:append:juno = " file://0001-arm-juno-add-custom-bootcmd-to-autoboot-from-uEnv.tx.patch" + + +# +# TC0 and TC1 MACHINES +# +SRC_URI:append:tc = " \ + file://bootargs.cfg \ + file://0001-arm-total_compute-update-secure-dram-size.patch \ + " diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot_2022.04.bb b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot_2022.04.bb new file mode 100644 index 0000000000..bc2d6d427e --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/u-boot/u-boot_2022.04.bb @@ -0,0 +1,26 @@ +HOMEPAGE = "http://www.denx.de/wiki/U-Boot/WebHome" +DESCRIPTION = "U-Boot, a boot loader for Embedded boards based on PowerPC, \ +ARM, MIPS and several other processors, which can be installed in a boot \ +ROM and used to initialize and test the hardware or to download and run \ +application code." +SECTION = "bootloaders" +DEPENDS += "flex-native bison-native" + +LICENSE = "GPL-2.0-or-later" +LIC_FILES_CHKSUM = "file://Licenses/README;md5=5a7450c57ffe5ae63fd732446b988025" +PE = "1" + +# We use the revision in order to avoid having to fetch it from the +# repo during parse +SRCREV = "e4b6ebd3de982ae7185dbf689a030e73fd06e0d2" + +SRC_URI = "git://git.denx.de/u-boot.git;branch=master \ + " + +S = "${WORKDIR}/git" +B = "${WORKDIR}/build" +do_configure[cleandirs] = "${B}" + +require recipes-bsp/u-boot/u-boot.inc + +DEPENDS += "bc-native dtc-native" diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/uefi/edk2-firmware-fvp-base.inc b/meta-arm/meta-arm-bsp/recipes-bsp/uefi/edk2-firmware-fvp-base.inc new file mode 100644 index 0000000000..7069c4d7f3 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/uefi/edk2-firmware-fvp-base.inc @@ -0,0 +1,6 @@ +EDK2_BUILD_RELEASE = "0" +EDK2_PLATFORM = "ArmVExpress-FVP-AArch64" +EDK2_PLATFORM_DSC = "Platform/ARM/VExpressPkg/ArmVExpress-FVP-AArch64.dsc" +EDK2_BIN_NAME = "FVP_AARCH64_EFI.fd" + +COMPATIBLE_MACHINE = "fvp-base" diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/uefi/edk2-firmware-juno.inc b/meta-arm/meta-arm-bsp/recipes-bsp/uefi/edk2-firmware-juno.inc new file mode 100644 index 0000000000..aac0d1cb5b --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/uefi/edk2-firmware-juno.inc @@ -0,0 +1,9 @@ +EDK2_PLATFORM = "ArmJuno" +EDK2_PLATFORM_DSC = "Platform/ARM/JunoPkg/ArmJuno.dsc" +EDK2_BIN_NAME = "BL33_AP_UEFI.fd" + +COMPATIBLE_MACHINE = "juno" + +# As of 2022-06-14 with 2022.05, clang builds fail: +# "The required fv image size 0x104048 exceeds the set fv image size 0xf9000" +TOOLCHAIN = "gcc" diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/uefi/edk2-firmware-n1sdp.inc b/meta-arm/meta-arm-bsp/recipes-bsp/uefi/edk2-firmware-n1sdp.inc new file mode 100644 index 0000000000..90c3f2ae70 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/uefi/edk2-firmware-n1sdp.inc @@ -0,0 +1,23 @@ +# N1SDP specific EDK2 configurations +EDK2_BUILD_RELEASE = "0" +EDK2_PLATFORM = "n1sdp" +EDK2_PLATFORM_DSC = "Platform/ARM/N1Sdp/N1SdpPlatform.dsc" +EDK2_BIN_NAME = "BL33_AP_UEFI.fd" + +COMPATIBLE_MACHINE = "n1sdp" + +# UEFI EDK2 on N1SDP is unable to detect FS2 during boot resulting in launching of +# EDK2 shell instead of launching grub. The startup.nsh will force launching of grub +EFIDIR = "/EFI/BOOT" +EFI_BOOT_IMAGE = "bootaa64.efi" + +FILESEXTRAPATHS:prepend := "${THISDIR}/files/edk2-platforms:" + +SRC_URI:append = "\ + file://add-nt-fw-config.patch;patchdir=edk2-platforms \ +" + +do_deploy:append() { + EFIPATH=$(echo "${EFIDIR}" | sed 's/\//\\/g') + printf 'FS2:%s\%s\n' "$EFIPATH" "${EFI_BOOT_IMAGE}" > ${DEPLOYDIR}/startup.nsh +} diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/uefi/edk2-firmware-sgi575.inc b/meta-arm/meta-arm-bsp/recipes-bsp/uefi/edk2-firmware-sgi575.inc new file mode 100644 index 0000000000..e26225f87a --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/uefi/edk2-firmware-sgi575.inc @@ -0,0 +1,7 @@ +# SGI575 specific EDK2 configurations +EDK2_BUILD_RELEASE = "0" +EDK2_PLATFORM = "Sgi575" +EDK2_PLATFORM_DSC = "Platform/ARM/SgiPkg/Sgi575/Sgi575.dsc" +EDK2_BIN_NAME = "BL33_AP_UEFI.fd" + +COMPATIBLE_MACHINE = "sgi575" diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/uefi/edk2-firmware_%.bbappend b/meta-arm/meta-arm-bsp/recipes-bsp/uefi/edk2-firmware_%.bbappend new file mode 100644 index 0000000000..e5018bb00b --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/uefi/edk2-firmware_%.bbappend @@ -0,0 +1,10 @@ +# Include machine specific configurations for UEFI EDK2 + +MACHINE_EDK2_REQUIRE ?= "" + +MACHINE_EDK2_REQUIRE:fvp-base = "edk2-firmware-fvp-base.inc" +MACHINE_EDK2_REQUIRE:juno = "edk2-firmware-juno.inc" +MACHINE_EDK2_REQUIRE:sgi575 = "edk2-firmware-sgi575.inc" +MACHINE_EDK2_REQUIRE:n1sdp = "edk2-firmware-n1sdp.inc" + +require ${MACHINE_EDK2_REQUIRE} diff --git a/meta-arm/meta-arm-bsp/recipes-bsp/uefi/files/edk2-platforms/add-nt-fw-config.patch b/meta-arm/meta-arm-bsp/recipes-bsp/uefi/files/edk2-platforms/add-nt-fw-config.patch new file mode 100644 index 0000000000..f6f1895116 --- /dev/null +++ b/meta-arm/meta-arm-bsp/recipes-bsp/uefi/files/edk2-platforms/add-nt-fw-config.patch @@ -0,0 +1,474 @@ +From cc58709b32d74273736886ccfc08e4723a436ea4 Mon Sep 17 00:00:00 2001 +From: sahil <sahil@arm.com> +Date: Thu, 17 Mar 2022 16:28:05 +0530 +Subject: [PATCH] Platform/ARM/N1sdp: Add support to parse NT_FW_CONFIG + +NT_FW_CONFIG DTB contains platform information passed by +Tf-A boot stage. +This information is used for Virtual memory map generation +during PEI phase and passed on to DXE phase as a HOB, where +it is used in ConfigurationManagerDxe. + +Upstream-Status: Pending +Signed-off-by: Adam Johnston <adam.johnston@arm.com> +Signed-off-by: sahil <sahil@arm.com> +Change-Id: Ib82571280bf1ca5febe5766e618de09e7b70bb02 + +--- + .../ConfigurationManager.c | 24 ++-- + .../ConfigurationManagerDxe.inf | 3 +- + .../ARM/NeoverseN1Soc/Include/NeoverseN1Soc.h | 16 +-- + .../Library/PlatformLib/AArch64/Helper.S | 4 +- + .../Library/PlatformLib/PlatformLib.c | 12 +- + .../Library/PlatformLib/PlatformLib.inf | 8 +- + .../Library/PlatformLib/PlatformLibMem.c | 103 +++++++++++++++++- + Silicon/ARM/NeoverseN1Soc/NeoverseN1Soc.dec | 7 +- + 8 files changed, 152 insertions(+), 25 deletions(-) + +diff --git a/Platform/ARM/N1Sdp/ConfigurationManager/ConfigurationManagerDxe/ConfigurationManager.c b/Platform/ARM/N1Sdp/ConfigurationManager/ConfigurationManagerDxe/ConfigurationManager.c +index f50623ae3f..e023d47cfd 100644 +--- a/Platform/ARM/N1Sdp/ConfigurationManager/ConfigurationManagerDxe/ConfigurationManager.c ++++ b/Platform/ARM/N1Sdp/ConfigurationManager/ConfigurationManagerDxe/ConfigurationManager.c +@@ -1,7 +1,7 @@ + /** @file
+ Configuration Manager Dxe
+
+- Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
++ Copyright (c) 2021 - 2022, ARM Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+@@ -16,6 +16,7 @@ + #include <IndustryStandard/SerialPortConsoleRedirectionTable.h>
+ #include <Library/ArmLib.h>
+ #include <Library/DebugLib.h>
++#include <Library/HobLib.h>
+ #include <Library/IoLib.h>
+ #include <Library/PcdLib.h>
+ #include <Library/UefiBootServicesTableLib.h>
+@@ -28,6 +29,7 @@ + #include "Platform.h"
+
+ extern struct EFI_ACPI_HETEROGENEOUS_MEMORY_ATTRIBUTE_TABLE Hmat;
++static NEOVERSEN1SOC_PLAT_INFO *PlatInfo;
+
+ /** The platform configuration repository information.
+ */
+@@ -1242,13 +1244,11 @@ InitializePlatformRepository ( + IN EDKII_PLATFORM_REPOSITORY_INFO * CONST PlatRepoInfo
+ )
+ {
+- NEOVERSEN1SOC_PLAT_INFO *PlatInfo;
+ UINT64 Dram2Size;
+ UINT64 RemoteDdrSize;
+
+ RemoteDdrSize = 0;
+
+- PlatInfo = (NEOVERSEN1SOC_PLAT_INFO *)NEOVERSEN1SOC_PLAT_INFO_STRUCT_BASE;
+ Dram2Size = ((PlatInfo->LocalDdrSize - 2) * SIZE_1GB);
+
+ PlatRepoInfo->MemAffInfo[LOCAL_DDR_REGION2].Length = Dram2Size;
+@@ -1512,7 +1512,6 @@ GetGicCInfo ( + )
+ {
+ EDKII_PLATFORM_REPOSITORY_INFO * PlatformRepo;
+- NEOVERSEN1SOC_PLAT_INFO *PlatInfo;
+ UINT32 TotalObjCount;
+ UINT32 ObjIndex;
+
+@@ -1523,7 +1522,6 @@ GetGicCInfo ( + }
+
+ PlatformRepo = This->PlatRepoInfo;
+- PlatInfo = (NEOVERSEN1SOC_PLAT_INFO *)NEOVERSEN1SOC_PLAT_INFO_STRUCT_BASE;
+
+ if (PlatInfo->MultichipMode == 1) {
+ TotalObjCount = PLAT_CPU_COUNT * 2;
+@@ -1623,7 +1621,6 @@ GetStandardNameSpaceObject ( + {
+ EFI_STATUS Status;
+ EDKII_PLATFORM_REPOSITORY_INFO * PlatformRepo;
+- NEOVERSEN1SOC_PLAT_INFO *PlatInfo;
+ UINT32 AcpiTableCount;
+
+ if ((This == NULL) || (CmObject == NULL)) {
+@@ -1634,7 +1631,7 @@ GetStandardNameSpaceObject ( +
+ Status = EFI_NOT_FOUND;
+ PlatformRepo = This->PlatRepoInfo;
+- PlatInfo = (NEOVERSEN1SOC_PLAT_INFO *)NEOVERSEN1SOC_PLAT_INFO_STRUCT_BASE;
++
+ AcpiTableCount = ARRAY_SIZE (PlatformRepo->CmAcpiTableList);
+ if (PlatInfo->MultichipMode == 0)
+ AcpiTableCount -= 1;
+@@ -1697,7 +1694,6 @@ GetArmNameSpaceObject ( + {
+ EFI_STATUS Status;
+ EDKII_PLATFORM_REPOSITORY_INFO * PlatformRepo;
+- NEOVERSEN1SOC_PLAT_INFO *PlatInfo;
+ UINT32 GicRedistCount;
+ UINT32 GicCpuCount;
+ UINT32 ProcHierarchyInfoCount;
+@@ -1718,8 +1714,6 @@ GetArmNameSpaceObject ( + Status = EFI_NOT_FOUND;
+ PlatformRepo = This->PlatRepoInfo;
+
+- // Probe for multi chip information
+- PlatInfo = (NEOVERSEN1SOC_PLAT_INFO *)NEOVERSEN1SOC_PLAT_INFO_STRUCT_BASE;
+ if (PlatInfo->MultichipMode == 1) {
+ GicRedistCount = 2;
+ GicCpuCount = PLAT_CPU_COUNT * 2;
+@@ -2162,8 +2156,18 @@ ConfigurationManagerDxeInitialize ( + IN EFI_SYSTEM_TABLE * SystemTable
+ )
+ {
++ VOID *PlatInfoHob;
+ EFI_STATUS Status;
+
++ PlatInfoHob = GetFirstGuidHob (&gArmNeoverseN1SocPlatformInfoDescriptorGuid);
++
++ if (PlatInfoHob == NULL) {
++ DEBUG ((DEBUG_ERROR, "Platform HOB is NULL\n"));
++ return EFI_NOT_FOUND;
++ }
++
++ PlatInfo = (NEOVERSEN1SOC_PLAT_INFO *)GET_GUID_HOB_DATA (PlatInfoHob);
++
+ // Initialize the Platform Configuration Repository before installing the
+ // Configuration Manager Protocol
+ Status = InitializePlatformRepository (
+diff --git a/Platform/ARM/N1Sdp/ConfigurationManager/ConfigurationManagerDxe/ConfigurationManagerDxe.inf b/Platform/ARM/N1Sdp/ConfigurationManager/ConfigurationManagerDxe/ConfigurationManagerDxe.inf +index 4f8e7f1302..fb59c29501 100644 +--- a/Platform/ARM/N1Sdp/ConfigurationManager/ConfigurationManagerDxe/ConfigurationManagerDxe.inf ++++ b/Platform/ARM/N1Sdp/ConfigurationManager/ConfigurationManagerDxe/ConfigurationManagerDxe.inf +@@ -1,7 +1,7 @@ + ## @file
+ # Configuration Manager Dxe
+ #
+-# Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
++# Copyright (c) 2021 - 2022, ARM Limited. All rights reserved.<BR>
+ #
+ # SPDX-License-Identifier: BSD-2-Clause-Patent
+ #
+@@ -42,6 +42,7 @@ +
+ [LibraryClasses]
+ ArmPlatformLib
++ HobLib
+ PrintLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+diff --git a/Silicon/ARM/NeoverseN1Soc/Include/NeoverseN1Soc.h b/Silicon/ARM/NeoverseN1Soc/Include/NeoverseN1Soc.h +index 097160c7e2..63cebaf0e0 100644 +--- a/Silicon/ARM/NeoverseN1Soc/Include/NeoverseN1Soc.h ++++ b/Silicon/ARM/NeoverseN1Soc/Include/NeoverseN1Soc.h +@@ -1,6 +1,6 @@ + /** @file
+ *
+-* Copyright (c) 2018 - 2020, ARM Limited. All rights reserved.
++* Copyright (c) 2018 - 2022, ARM Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+@@ -41,11 +41,6 @@ + #define NEOVERSEN1SOC_EXP_PERIPH_BASE0 0x1C000000
+ #define NEOVERSEN1SOC_EXP_PERIPH_BASE0_SZ 0x1300000
+
+-// Base address to a structure of type NEOVERSEN1SOC_PLAT_INFO which is
+-// pre-populated by a earlier boot stage
+-#define NEOVERSEN1SOC_PLAT_INFO_STRUCT_BASE (NEOVERSEN1SOC_NON_SECURE_SRAM_BASE + \
+- 0x00008000)
+-
+ /*
+ * Platform information structure stored in Non-secure SRAM. Platform
+ * information are passed from the trusted firmware with the below structure
+@@ -55,12 +50,17 @@ + typedef struct {
+ /*! 0 - Single Chip, 1 - Chip to Chip (C2C) */
+ UINT8 MultichipMode;
+- /*! Slave count in C2C mode */
+- UINT8 SlaveCount;
++ /*! Secondary chip count in C2C mode */
++ UINT8 SecondaryChipCount;
+ /*! Local DDR memory size in GigaBytes */
+ UINT8 LocalDdrSize;
+ /*! Remote DDR memory size in GigaBytes */
+ UINT8 RemoteDdrSize;
+ } NEOVERSEN1SOC_PLAT_INFO;
+
++// NT_FW_CONFIG DT structure
++typedef struct {
++ UINT64 NtFwConfigDtAddr;
++} NEOVERSEN1SOC_NT_FW_CONFIG_INFO_PPI;
++
+ #endif
+diff --git a/Silicon/ARM/NeoverseN1Soc/Library/PlatformLib/AArch64/Helper.S b/Silicon/ARM/NeoverseN1Soc/Library/PlatformLib/AArch64/Helper.S +index 8d2069dea8..88ed640d29 100644 +--- a/Silicon/ARM/NeoverseN1Soc/Library/PlatformLib/AArch64/Helper.S ++++ b/Silicon/ARM/NeoverseN1Soc/Library/PlatformLib/AArch64/Helper.S +@@ -1,6 +1,6 @@ + /** @file
+ *
+-* Copyright (c) 2019 - 2020, ARM Limited. All rights reserved.
++* Copyright (c) 2019 - 2022, ARM Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+@@ -25,6 +25,8 @@ GCC_ASM_EXPORT(ArmPlatformIsPrimaryCore) + // the UEFI firmware through the CPU registers.
+ //
+ ASM_PFX(ArmPlatformPeiBootAction):
++ adr x10, NtFwConfigDtBlob
++ str x0, [x10]
+ ret
+
+ //
+diff --git a/Silicon/ARM/NeoverseN1Soc/Library/PlatformLib/PlatformLib.c b/Silicon/ARM/NeoverseN1Soc/Library/PlatformLib/PlatformLib.c +index c0effd37f3..fabe902cd0 100644 +--- a/Silicon/ARM/NeoverseN1Soc/Library/PlatformLib/PlatformLib.c ++++ b/Silicon/ARM/NeoverseN1Soc/Library/PlatformLib/PlatformLib.c +@@ -1,6 +1,6 @@ + /** @file
+
+- Copyright (c) 2018-2021, ARM Limited. All rights reserved.<BR>
++ Copyright (c) 2018 - 2022, ARM Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+@@ -8,8 +8,12 @@ +
+ #include <Library/ArmPlatformLib.h>
+ #include <Library/BaseLib.h>
++#include <NeoverseN1Soc.h>
+ #include <Ppi/ArmMpCoreInfo.h>
+
++UINT64 NtFwConfigDtBlob;
++STATIC NEOVERSEN1SOC_NT_FW_CONFIG_INFO_PPI mNtFwConfigDtInfoPpi;
++
+ STATIC ARM_CORE_INFO mCoreInfoTable[] = {
+ { 0x0, 0x0 }, // Cluster 0, Core 0
+ { 0x0, 0x1 }, // Cluster 0, Core 1
+@@ -46,6 +50,7 @@ ArmPlatformInitialize ( + IN UINTN MpId
+ )
+ {
++ mNtFwConfigDtInfoPpi.NtFwConfigDtAddr = NtFwConfigDtBlob;
+ return RETURN_SUCCESS;
+ }
+
+@@ -80,6 +85,11 @@ EFI_PEI_PPI_DESCRIPTOR gPlatformPpiTable[] = { + EFI_PEI_PPI_DESCRIPTOR_PPI,
+ &gArmMpCoreInfoPpiGuid,
+ &mMpCoreInfoPpi
++ },
++ {
++ EFI_PEI_PPI_DESCRIPTOR_PPI,
++ &gNtFwConfigDtInfoPpiGuid,
++ &mNtFwConfigDtInfoPpi
+ }
+ };
+
+diff --git a/Silicon/ARM/NeoverseN1Soc/Library/PlatformLib/PlatformLib.inf b/Silicon/ARM/NeoverseN1Soc/Library/PlatformLib/PlatformLib.inf +index 96e590cdd8..6f9c9d5ab6 100644 +--- a/Silicon/ARM/NeoverseN1Soc/Library/PlatformLib/PlatformLib.inf ++++ b/Silicon/ARM/NeoverseN1Soc/Library/PlatformLib/PlatformLib.inf +@@ -1,7 +1,7 @@ + ## @file
+ # Platform Library for N1Sdp.
+ #
+-# Copyright (c) 2018-2021, ARM Limited. All rights reserved.<BR>
++# Copyright (c) 2018 - 2022, ARM Limited. All rights reserved.<BR>
+ #
+ # SPDX-License-Identifier: BSD-2-Clause-Patent
+ #
+@@ -18,10 +18,14 @@ + [Packages]
+ ArmPkg/ArmPkg.dec
+ ArmPlatformPkg/ArmPlatformPkg.dec
++ EmbeddedPkg/EmbeddedPkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+ Silicon/ARM/NeoverseN1Soc/NeoverseN1Soc.dec
+
++[LibraryClasses]
++ FdtLib
++
+ [Sources.common]
+ PlatformLibMem.c
+ PlatformLib.c
+@@ -59,7 +63,9 @@ + gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress
+
+ [Guids]
++ gArmNeoverseN1SocPlatformInfoDescriptorGuid
+ gEfiHobListGuid ## CONSUMES ## SystemTable
+
+ [Ppis]
+ gArmMpCoreInfoPpiGuid
++ gNtFwConfigDtInfoPpiGuid
+diff --git a/Silicon/ARM/NeoverseN1Soc/Library/PlatformLib/PlatformLibMem.c b/Silicon/ARM/NeoverseN1Soc/Library/PlatformLib/PlatformLibMem.c +index 339fa07b32..b58bda4b76 100644 +--- a/Silicon/ARM/NeoverseN1Soc/Library/PlatformLib/PlatformLibMem.c ++++ b/Silicon/ARM/NeoverseN1Soc/Library/PlatformLib/PlatformLibMem.c +@@ -1,6 +1,6 @@ + /** @file
+
+- Copyright (c) 2018 - 2021, ARM Limited. All rights reserved.<BR>
++ Copyright (c) 2018 - 2022, ARM Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+@@ -10,11 +10,95 @@ + #include <Library/DebugLib.h>
+ #include <Library/HobLib.h>
+ #include <Library/MemoryAllocationLib.h>
++#include <Library/PeiServicesLib.h>
++#include <libfdt.h>
+ #include <NeoverseN1Soc.h>
+
+ // The total number of descriptors, including the final "end-of-table" descriptor.
+ #define MAX_VIRTUAL_MEMORY_MAP_DESCRIPTORS 19
+
++/** A helper function to locate the NtFwConfig PPI and get the base address of
++ NT_FW_CONFIG DT from which values are obtained using FDT helper functions.
++
++ @param [out] plat_info Pointer to the NeoverseN1Soc PLATFORM_INFO HOB
++
++ @retval EFI_SUCCESS Success.
++ returns EFI_INVALID_PARAMETER A parameter is invalid.
++**/
++EFI_STATUS
++GetNeoverseN1SocPlatInfo (
++ OUT NEOVERSEN1SOC_PLAT_INFO *plat_info
++ )
++{
++ CONST UINT32 *Property;
++ INT32 Offset;
++ CONST VOID *NtFwCfgDtBlob;
++ NEOVERSEN1SOC_NT_FW_CONFIG_INFO_PPI *NtFwConfigInfoPpi;
++ EFI_STATUS Status;
++
++ Status = PeiServicesLocatePpi (
++ &gNtFwConfigDtInfoPpiGuid,
++ 0,
++ NULL,
++ (VOID **)&NtFwConfigInfoPpi
++ );
++
++ if (EFI_ERROR (Status)) {
++ DEBUG ((
++ DEBUG_ERROR,
++ "PeiServicesLocatePpi failed with error %r\n",
++ Status
++ ));
++ return EFI_INVALID_PARAMETER;
++ }
++
++ NtFwCfgDtBlob = (VOID *)(UINTN)NtFwConfigInfoPpi->NtFwConfigDtAddr;
++ if (fdt_check_header (NtFwCfgDtBlob) != 0) {
++ DEBUG ((DEBUG_ERROR, "Invalid DTB file %p passed\n", NtFwCfgDtBlob));
++ return EFI_INVALID_PARAMETER;
++ }
++
++ Offset = fdt_subnode_offset (NtFwCfgDtBlob, 0, "platform-info");
++ if (Offset == -FDT_ERR_NOTFOUND) {
++ DEBUG ((DEBUG_ERROR, "Invalid DTB : platform-info node not found\n"));
++ return EFI_INVALID_PARAMETER;
++ }
++
++ Property = fdt_getprop (NtFwCfgDtBlob, Offset, "local-ddr-size", NULL);
++ if (Property == NULL) {
++ DEBUG ((DEBUG_ERROR, "local-ddr-size property not found\n"));
++ return EFI_INVALID_PARAMETER;
++ }
++
++ plat_info->LocalDdrSize = fdt32_to_cpu (*Property);
++
++ Property = fdt_getprop (NtFwCfgDtBlob, Offset, "remote-ddr-size", NULL);
++ if (Property == NULL) {
++ DEBUG ((DEBUG_ERROR, "remote-ddr-size property not found\n"));
++ return EFI_INVALID_PARAMETER;
++ }
++
++ plat_info->RemoteDdrSize = fdt32_to_cpu (*Property);
++
++ Property = fdt_getprop (NtFwCfgDtBlob, Offset, "secondary-chip-count", NULL);
++ if (Property == NULL) {
++ DEBUG ((DEBUG_ERROR, "secondary-chip-count property not found\n"));
++ return EFI_INVALID_PARAMETER;
++ }
++
++ plat_info->SecondaryChipCount = fdt32_to_cpu (*Property);
++
++ Property = fdt_getprop (NtFwCfgDtBlob, Offset, "multichip-mode", NULL);
++ if (Property == NULL) {
++ DEBUG ((DEBUG_ERROR, "multichip-mode property not found\n"));
++ return EFI_INVALID_PARAMETER;
++ }
++
++ plat_info->MultichipMode = fdt32_to_cpu (*Property);
++
++ return EFI_SUCCESS;
++}
++
+ /**
+ Returns the Virtual Memory Map of the platform.
+
+@@ -36,9 +120,24 @@ ArmPlatformGetVirtualMemoryMap ( + NEOVERSEN1SOC_PLAT_INFO *PlatInfo;
+ UINT64 DramBlock2Size;
+ UINT64 RemoteDdrSize;
++ EFI_STATUS Status;
+
+ Index = 0;
+- PlatInfo = (NEOVERSEN1SOC_PLAT_INFO *)NEOVERSEN1SOC_PLAT_INFO_STRUCT_BASE;
++
++ // Create platform info HOB
++ PlatInfo = (NEOVERSEN1SOC_PLAT_INFO *)BuildGuidHob (
++ &gArmNeoverseN1SocPlatformInfoDescriptorGuid,
++ sizeof (NEOVERSEN1SOC_PLAT_INFO)
++ );
++
++ if (PlatInfo == NULL) {
++ DEBUG ((DEBUG_ERROR, "Platform HOB is NULL\n"));
++ ASSERT (FALSE);
++ return;
++ }
++
++ Status = GetNeoverseN1SocPlatInfo (PlatInfo);
++ ASSERT (Status == 0);
+ DramBlock2Size = ((UINT64)(PlatInfo->LocalDdrSize -
+ NEOVERSEN1SOC_DRAM_BLOCK1_SIZE / SIZE_1GB) *
+ (UINT64)SIZE_1GB);
+diff --git a/Silicon/ARM/NeoverseN1Soc/NeoverseN1Soc.dec b/Silicon/ARM/NeoverseN1Soc/NeoverseN1Soc.dec +index d59f25a5b9..4dea8fe1e8 100644 +--- a/Silicon/ARM/NeoverseN1Soc/NeoverseN1Soc.dec ++++ b/Silicon/ARM/NeoverseN1Soc/NeoverseN1Soc.dec +@@ -1,7 +1,7 @@ + ## @file
+ # Describes the entire platform configuration.
+ #
+-# Copyright (c) 2018 - 2021, ARM Limited. All rights reserved.<BR>
++# Copyright (c) 2018 - 2022, ARM Limited. All rights reserved.<BR>
+ #
+ # SPDX-License-Identifier: BSD-2-Clause-Patent
+ #
+@@ -22,6 +22,8 @@ + Include # Root include for the package
+
+ [Guids.common]
++ # ARM NeoverseN1Soc Platform Info descriptor
++ gArmNeoverseN1SocPlatformInfoDescriptorGuid = { 0x095cb024, 0x1e00, 0x4d6f, { 0xaa, 0x34, 0x4a, 0xf8, 0xaf, 0x0e, 0xad, 0x99 } }
+ gArmNeoverseN1SocTokenSpaceGuid = { 0xab93eb78, 0x60d7, 0x4099, { 0xac, 0xeb, 0x6d, 0xb5, 0x02, 0x58, 0x7c, 0x24 } }
+
+ [PcdsFixedAtBuild]
+@@ -83,3 +85,6 @@ + gArmNeoverseN1SocTokenSpaceGuid.PcdRemotePcieMmio32Translation|0x40000000000|UINT64|0x0000004F
+ gArmNeoverseN1SocTokenSpaceGuid.PcdRemotePcieMmio64Translation|0x40000000000|UINT64|0x00000050
+ gArmNeoverseN1SocTokenSpaceGuid.PcdRemotePcieSegmentNumber|2|UINT32|0x00000051
++
++[Ppis]
++ gNtFwConfigDtInfoPpiGuid = { 0xb50dee0e, 0x577f, 0x47fb, { 0x83, 0xd0, 0x41, 0x78, 0x61, 0x8b, 0x33, 0x8a } }
+-- +2.17.1 + |