From cb0a3514f32d5295704276de8dcc618fb7a237f1 Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Tue, 26 Sep 2023 08:40:28 -0700 Subject: [PATCH] core: add support for transfer list Add supports for Transfer List on both aarch32/64. Fetch arguments from {x,r}{0-3} and check if a valid Transfer List exists, which compliant to the Firmware Handoff specification. The Transfer List will be mapped during early initialization and unmapped before exiting to next boot stage. DTB and pagable address will be parsed from the Transfer List if they exist as Transfer Entries. If Transfer List does not exist or is invalid, legacy argument handoff is backwards compatible. Signed-off-by: Raymond Mao --- core/arch/arm/kernel/boot.c | 33 +++++++++++-- core/arch/arm/kernel/entry_a32.S | 61 +++++++++++++++++++++--- core/arch/arm/kernel/entry_a64.S | 46 +++++++++++++++++- core/arch/arm/kernel/link_dummies_init.c | 5 +- core/include/kernel/boot.h | 7 ++- core/include/kernel/dt.h | 1 + core/include/mm/core_mmu.h | 1 + core/kernel/dt.c | 59 ++++++++++++++++------- 8 files changed, 182 insertions(+), 31 deletions(-) diff --git a/core/arch/arm/kernel/boot.c b/core/arch/arm/kernel/boot.c index 9a132240eb2..0467e9d01f6 100644 --- a/core/arch/arm/kernel/boot.c +++ b/core/arch/arm/kernel/boot.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1253,13 +1254,30 @@ static void init_secondary_helper(unsigned long nsec_entry) * the unpaged area so that it lies in the init area. */ void __weak boot_init_primary_early(unsigned long pageable_part, - unsigned long nsec_entry __maybe_unused) + unsigned long nsec_entry __maybe_unused, + unsigned long transfer_list) { unsigned long e = PADDR_INVALID; + struct transfer_list_entry *te = NULL; + struct transfer_list_header *tl = NULL; + struct dt_descriptor *dt = get_external_dt_desc(); #if !defined(CFG_WITH_ARM_TRUSTED_FW) e = nsec_entry; #endif + if (IS_ENABLED(CFG_TRANSFER_LIST) && transfer_list) { + /* map the TL */ + tl = transfer_list_map(transfer_list); + if (!tl) + panic("Failed to map transfer list"); + dt->tl = tl; /* save the mapped TL */ + + te = transfer_list_find(tl, TL_TAG_OPTEE_PAGABLE_PART); + if (IS_ENABLED(CFG_WITH_PAGER) && !pageable_part && te) { + pageable_part = get_le64(transfer_list_entry_data(te)); + transfer_list_rem(tl, te); + } + } init_primary(pageable_part, e); } @@ -1321,7 +1339,7 @@ struct ns_entry_context *boot_core_hpen(void) #if defined(CFG_CORE_ASLR) #if defined(CFG_DT) -unsigned long __weak get_aslr_seed(void *fdt) +unsigned long __weak get_aslr_seed(void *fdt, struct transfer_list_header *tl) { int rc = 0; const uint64_t *seed = NULL; @@ -1333,6 +1351,14 @@ unsigned long __weak get_aslr_seed(void *fdt) goto err; } + if (tl && IS_ENABLED(CFG_TRANSFER_LIST)) { + if (fdt != transfer_list_entry_data + (transfer_list_find(tl, TL_TAG_FDT))) { + EMSG("fdt does not match to the fdt entry of the TL"); + goto err; + } + } + rc = fdt_check_header(fdt); if (rc) { DMSG("Bad fdt: %d", rc); @@ -1357,7 +1383,8 @@ unsigned long __weak get_aslr_seed(void *fdt) return plat_get_aslr_seed(); } #else /*!CFG_DT*/ -unsigned long __weak get_aslr_seed(void *fdt __unused) +unsigned long __weak get_aslr_seed(void *fdt __unused, + struct transfer_list_header *tl __unused) { /* Try platform implementation */ return plat_get_aslr_seed(); diff --git a/core/arch/arm/kernel/entry_a32.S b/core/arch/arm/kernel/entry_a32.S index 2ef44c3d0d8..37efb0e669d 100644 --- a/core/arch/arm/kernel/entry_a32.S +++ b/core/arch/arm/kernel/entry_a32.S @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -162,6 +163,24 @@ END_FUNC reset_vect_table #endif write_sctlr r0 .endm +#if defined(CFG_TRANSFER_LIST) + /* + * FW Handoff register use: + * entry r0: Zero + * entry r1: TRANSFER_LIST_SIGNATURE | REGISTER_CONVENTION_VERSION_MASK + * entry r2, saved r6: device tree address + * entry r3, saved r10: Transfer list base address + * saved r4, r7: Zero + * entry lr, saved r5: non-secure entry address + */ + .macro bootargs_entry_tl + mov r5, lr /* non-secure entry address */ + mov r4, #0 /* Pagable or TOS FW config address is in TL */ + mov r6, r2 /* Save DT address */ + mov r7, #0 + mov r10, r3 /* Save TL address */ + .endm +#endif #if defined(CFG_CORE_SEL1_SPMC) && defined(CFG_WITH_ARM_TRUSTED_FW) /* @@ -179,6 +198,7 @@ END_FUNC reset_vect_table mov r4, r1 mov r5, #0 mov r7, #0 + mov r10, #0 .endm #else /* @@ -205,6 +225,7 @@ END_FUNC reset_vect_table mov r6, r2 #endif mov r7, r1 + mov r10, #0 .endm #endif @@ -239,8 +260,20 @@ END_FUNC reset_vect_table FUNC _start , : UNWIND( .cantunwind) - +#if defined(CFG_TRANSFER_LIST) + /* check if the TL signature exists */ + ldr r4, =TRANSFER_LIST_SIGNATURE + ldr r5, =REGISTER_CONVENTION_VERSION_MASK + orr r6, r4, r5 + cmp r1, r6 + bne no_tl_sig + /* TL signature exists */ + bootargs_entry_tl + b sav_arg_done +no_tl_sig: /* TL signature does not exist, legacy mode */ +#endif bootargs_entry +sav_arg_done: /* arguments saved */ /* * 32bit entry is expected to execute Supervisor mode, @@ -365,7 +398,7 @@ DECLARE_KEEP_INIT _start LOCAL_FUNC reset_primary , : , .identity_map UNWIND( .cantunwind) - /* preserve r4-r7: bootargs */ + /* preserve r4-r7 and r10: bootargs */ #ifdef CFG_WITH_PAGER /* @@ -390,8 +423,8 @@ UNWIND( .cantunwind) str r0, cached_mem_end ldr r2, =__init_start copy_init: - ldmdb r1!, {r3, r8-r12} - stmdb r0!, {r3, r8-r12} + ldmdb r1!, {r3, r8-r9, r11-r12} + stmdb r0!, {r3, r8-r9, r11-r12} cmp r0, r2 bgt copy_init #else @@ -411,8 +444,8 @@ copy_init: ldr r2, =__end copy_init: - ldmdb r1!, {r3, r8-r12} - stmdb r0!, {r3, r8-r12} + ldmdb r1!, {r3, r8-r9, r11-r12} + stmdb r0!, {r3, r8-r9, r11-r12} cmp r0, r2 bgt copy_init #endif @@ -499,8 +532,20 @@ shadow_stack_access_ok: bl arm_cl2_enable #endif +#if defined(CFG_TRANSFER_LIST) + /* check if the TL header valid */ + mov r0, r10 + bl transfer_list_check_header + ldr r1, =TL_OPS_NON + cmp r0, r1 + bne tl_valid + mov r10, #0 /* clean the address if TL is invalid */ +tl_valid: +#endif + #ifdef CFG_CORE_ASLR - mov r0, r6 + mov r0, r6 /* DT address */ + mov r1, r10 /* TL address */ bl get_aslr_seed #else mov r0, #0 @@ -549,6 +594,7 @@ shadow_stack_access_ok: mov r0, r4 /* pageable part address */ mov r1, r5 /* ns-entry address */ + mov r2, r10 /* TL address */ bl boot_init_primary_early #ifndef CFG_NS_VIRTUALIZATION mov r9, sp @@ -562,6 +608,7 @@ shadow_stack_access_ok: #endif mov r0, r6 /* DT address */ mov r1, #0 /* unused */ + /* PA of TL is not needed as it is already mapped */ bl boot_init_primary_late #ifndef CFG_NS_VIRTUALIZATION mov r0, #THREAD_CLF_TMP diff --git a/core/arch/arm/kernel/entry_a64.S b/core/arch/arm/kernel/entry_a64.S index dd82e7106cb..145e2e9c32b 100644 --- a/core/arch/arm/kernel/entry_a64.S +++ b/core/arch/arm/kernel/entry_a64.S @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -160,8 +161,28 @@ .endm FUNC _start , : +#if defined(CFG_TRANSFER_LIST) /* - * Register use: + * FW Handoff register use: + * x0 - DTB address or 0 + * x1 - TRANSFER_LIST_SIGNATURE | REGISTER_CONVENTION_VERSION_MASK + * x2 - 0 + * x3 - Transfer list base address + * + * x19 - 0 + * x20 - saved x0 + * x23 - saved x3 + */ + /* check if the TL signature exists */ + ldr x19, =TRANSFER_LIST_SIGNATURE + ldr x20, =REGISTER_CONVENTION_VERSION_MASK + orr x23, x19, x20 + cmp x1, x23 + b.eq tl_sig_exist +#endif + /* Transfer list signature does not exist, legacy mode + * + * Legacy register use: * x0 - CFG_CORE_FFA=y && CFG_CORE_SEL2_SPMC=n: * if non-NULL holds the TOS FW config [1] address * - CFG_CORE_FFA=y && CFG_CORE_SEL2_SPMC=y: @@ -173,6 +194,7 @@ FUNC _start , : * * x19 - saved x0 * x20 - saved x2 + * x23 - 0 * * [1] A TF-A concept: TOS_FW_CONFIG - Trusted OS Firmware * configuration file. Used by Trusted OS (BL32), that is, OP-TEE @@ -184,7 +206,15 @@ FUNC _start , : #else mov x20, x2 /* Save DT address */ #endif + mov x23, #0 + b sav_arg_done + +tl_sig_exist: /* Transfer list signature exists */ + mov x19, #0 /* Pagable or TOS FW config address is in TL */ + mov x20, x0 /* Save DT address */ + mov x23, x3 /* Save TL address */ +sav_arg_done: /* arguments saved */ adr x0, reset_vect_table msr vbar_el1, x0 isb @@ -326,8 +356,20 @@ clear_nex_bss: bl boot_init_memtag #endif +#if defined(CFG_TRANSFER_LIST) + /* check if the TL header valid */ + mov x0, x23 + bl transfer_list_check_header + ldr x1, =TL_OPS_NON + cmp x0, x1 + b.ne tl_valid + mov x23, #0 /* clean the address if TL is invalid */ +tl_valid: +#endif + #ifdef CFG_CORE_ASLR mov x0, x20 /* DT address */ + mov x1, x23 /* TL address */ bl get_aslr_seed #else mov x0, #0 @@ -382,6 +424,7 @@ clear_nex_bss: mov x0, x19 /* pagable part address */ #endif mov x1, #-1 + mov x2, x23 /* TL address */ bl boot_init_primary_early #ifdef CFG_MEMTAG @@ -403,6 +446,7 @@ clear_nex_bss: #else mov x1, xzr /* unused */ #endif + /* PA of TL is not needed as it is already mapped */ bl boot_init_primary_late #ifdef CFG_CORE_PAUTH init_pauth_per_cpu diff --git a/core/arch/arm/kernel/link_dummies_init.c b/core/arch/arm/kernel/link_dummies_init.c index 5c90271f785..eb4a4135ea7 100644 --- a/core/arch/arm/kernel/link_dummies_init.c +++ b/core/arch/arm/kernel/link_dummies_init.c @@ -12,7 +12,7 @@ boot_save_boot_info(void *boot_info __unused) } unsigned long __section(".text.dummy.get_aslr_seed") -get_aslr_seed(void *fdt __unused) +get_aslr_seed(void *fdt __unused, struct transfer_list_header *tl __unused) { return 0; } @@ -25,6 +25,7 @@ core_init_mmu_map(unsigned long seed __unused, void __section(".text.dummy.boot_init_primary_early") boot_init_primary_early(unsigned long pageable_part __unused, - unsigned long nsec_entry __unused) + unsigned long nsec_entry __unused, + unsigned long transfer_list __unused) { } diff --git a/core/include/kernel/boot.h b/core/include/kernel/boot.h index 14025b30cc2..cf03456ec9f 100644 --- a/core/include/kernel/boot.h +++ b/core/include/kernel/boot.h @@ -7,6 +7,7 @@ #define __KERNEL_BOOT_H #include +#include #include /* @@ -44,7 +45,8 @@ extern const struct core_mmu_config boot_mmu_config; /* @nsec_entry is unused if using CFG_WITH_ARM_TRUSTED_FW */ void boot_init_primary_early(unsigned long pageable_part, - unsigned long nsec_entry); + unsigned long nsec_entry, + unsigned long transfer_list); void boot_init_primary_late(unsigned long fdt, unsigned long manifest); void boot_init_memtag(void); void boot_save_boot_info(void *boot_info); @@ -97,9 +99,10 @@ void *get_manifest_dt(void); /* * get_aslr_seed() - return a random seed for core ASLR * @fdt: Pointer to a device tree if CFG_DT_ADDR=y + * @tl: Pointer to a transfer list * * This function has a __weak default implementation. */ -unsigned long get_aslr_seed(void *fdt); +unsigned long get_aslr_seed(void *fdt, struct transfer_list_header *tl); #endif /* __KERNEL_BOOT_H */ diff --git a/core/include/kernel/dt.h b/core/include/kernel/dt.h index 7a7bd9afdcc..16913c4256c 100644 --- a/core/include/kernel/dt.h +++ b/core/include/kernel/dt.h @@ -83,6 +83,7 @@ struct dt_descriptor { #ifdef _CFG_USE_DTB_OVERLAY int frag_id; #endif + struct transfer_list_header *tl; }; extern uint8_t embedded_secure_dtb[]; diff --git a/core/include/mm/core_mmu.h b/core/include/mm/core_mmu.h index fe9166c0051..7348f75c4d2 100644 --- a/core/include/mm/core_mmu.h +++ b/core/include/mm/core_mmu.h @@ -77,6 +77,7 @@ * MEM_AREA_IO_SEC: Secure HW mapped registers * MEM_AREA_EXT_DT: Memory loads external device tree * MEM_AREA_MANIFEST_DT: Memory loads manifest device tree + * MEM_AREA_TRANSFER_LIST: Memory area mapped for Transfer List * MEM_AREA_RES_VASPACE: Reserved virtual memory space * MEM_AREA_SHM_VASPACE: Virtual memory space for dynamic shared memory buffers * MEM_AREA_TS_VASPACE: TS va space, only used with phys_to_virt() diff --git a/core/kernel/dt.c b/core/kernel/dt.c index 299aba11017..ec683635008 100644 --- a/core/kernel/dt.c +++ b/core/kernel/dt.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -553,23 +554,36 @@ void init_external_dt(unsigned long phys_dt) if (!IS_ENABLED(CFG_EXTERNAL_DT)) return; - if (!phys_dt) { + if (IS_ENABLED(CFG_TRANSFER_LIST)) + /* get DTB from the mapped TL */ + fdt = transfer_list_entry_data + (transfer_list_find(dt->tl, TL_TAG_FDT)); + + if (!fdt) { + if (!phys_dt) { + /* + * No need to panic as we're not using the DT in OP-TEE + * yet, we're only adding some nodes for normal world + * use. + * This makes the switch to using DT easier as we can + * boot a newer OP-TEE with older boot loaders. + * Once we start to initialize devices based on DT + * we'll likely panic instead of returning here. + */ + IMSG("No non-secure external DT"); + return; + } + /* - * No need to panic as we're not using the DT in OP-TEE - * yet, we're only adding some nodes for normal world use. - * This makes the switch to using DT easier as we can boot - * a newer OP-TEE with older boot loaders. Once we start to - * initialize devices based on DT we'll likely panic - * instead of returning here. + * if DTB does not exist in the mapped TL, + * try to map the PA for backwards compatibility */ - IMSG("No non-secure external DT"); - return; + fdt = core_mmu_add_mapping(MEM_AREA_EXT_DT, phys_dt, + CFG_DTB_MAX_SIZE); + if (!fdt) + panic("Failed to map external DTB"); } - fdt = core_mmu_add_mapping(MEM_AREA_EXT_DT, phys_dt, CFG_DTB_MAX_SIZE); - if (!fdt) - panic("Failed to map external DTB"); - dt->blob = fdt; ret = init_dt_overlay(dt, CFG_DTB_MAX_SIZE); @@ -600,6 +614,8 @@ void *get_external_dt(void) static TEE_Result release_external_dt(void) { int ret = 0; + struct transfer_list_header *tl = external_dt.tl; + struct transfer_list_entry *te = NULL; if (!IS_ENABLED(CFG_EXTERNAL_DT)) return TEE_SUCCESS; @@ -614,12 +630,23 @@ static TEE_Result release_external_dt(void) panic(); } - if (core_mmu_remove_mapping(MEM_AREA_EXT_DT, external_dt.blob, - CFG_DTB_MAX_SIZE)) + if (IS_ENABLED(CFG_TRANSFER_LIST)) { + if (tl) { + te = transfer_list_find(tl, TL_TAG_FDT); + assert(external_dt.blob == + transfer_list_entry_data(te)); + transfer_list_set_data_size + (tl, te, fdt_totalsize(external_dt.blob)); + + transfer_list_unmap_sync(tl); // unmap the TL + } + } else if (core_mmu_remove_mapping(MEM_AREA_EXT_DT, external_dt.blob, + CFG_DTB_MAX_SIZE)) { panic("Failed to remove temporary Device Tree mapping"); + } /* External DTB no more reached, reset pointer to invalid */ - external_dt.blob = NULL; + memset(&external_dt, 0, sizeof(external_dt)); return TEE_SUCCESS; }