From 834ea4ab334344d47967c97ee73e16754e3b9de4 Mon Sep 17 00:00:00 2001 From: Jeenu Viswambharan Date: Wed, 15 Mar 2017 12:58:14 +0000 Subject: Add test cases for state switch ARM SiP service This test suite validates the execution state switch of a non-secure EL (from AArch32 to AArch64, and vice versa) by issuing ARM SiP service SMC with varying parameters. A cookie is shared between both states. A field in the cooke is updated from the other state to signal that state switch did indeed happen. The suite is not AArch32-ready. All test cases will report as skipped. Change-Id: I269788fb19ed5d366c05fcf2cbd9d0b2cae6e2d0 Signed-off-by: Jeenu Viswambharan --- .../sip_service/test_exec_state_switch.c | 337 +++++++++++++++++++++ .../sip_service/test_exec_state_switch_asm.S | 202 ++++++++++++ tests/tests-common.xml | 12 + tests/tests.mk | 2 + 4 files changed, 553 insertions(+) create mode 100644 tests/runtime_services/sip_service/test_exec_state_switch.c create mode 100644 tests/runtime_services/sip_service/test_exec_state_switch_asm.S (limited to 'tests') diff --git a/tests/runtime_services/sip_service/test_exec_state_switch.c b/tests/runtime_services/sip_service/test_exec_state_switch.c new file mode 100644 index 0000000..ef60bd8 --- /dev/null +++ b/tests/runtime_services/sip_service/test_exec_state_switch.c @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This test suite validates the execution state switch of a non-secure EL (from + * AArch32 to AArch64, and vice versa) by issuing ARM SiP service SMC with + * varying parameters. A cookie is shared between both states. A field in the + * cookie is updated from the other state to signal that state switch did indeed + * happen. + * + * Note that the suite is not AArch32-ready. All test cases will report as + * skipped. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Definitions from TF arm_sip_svc.h */ +#define ARM_SIP_SVC_VERSION 0x8200ff03 +#define ARM_SIP_SVC_EXE_STATE_SWITCH 0x82000020 + +/* State switch error codes from SiP service */ +#define STATE_SW_E_PARAM (-2) +#define STATE_SW_E_DENIED (-3) + +#define SIP_VERSION_OK 1 + +#define HI32(val) (((u_register_t) (val)) >> 32) +#define LO32(val) ((uint32_t) (u_register_t) (val)) + +/* A cookie shared between states for information exchange */ +typedef struct { + uint32_t pc_hi; + uint32_t pc_lo; + uint64_t sp; + uint32_t success; +} state_switch_cookie_t; + +state_switch_cookie_t state_switch_cookie; +static event_t secondary_booted __unused; + +/* + * SiP service version check. Also a signal for test cases to execute or skip + * altogether. + */ +static int sip_version_check __unused; + +/* AArch32 instructions to switch state back to AArch64, stored as data */ +extern void *state_switch_a32_entry; + +extern int do_state_switch(void *); + +/* + * @Test_Aim@ Issue a system reset to initiate state switch SMC call that's part + * of ARM SiP service. System reset is required because the state switch SMC + * requires that no secondaries have been brought up since booting. + */ +test_result_t test_exec_state_switch_reset_before(void) +{ +#ifdef AARCH64 + int version; + smc_args sip_version_smc = { ARM_SIP_SVC_VERSION }; + smc_args reset = { SMC_PSCI_SYSTEM_RESET }; + smc_ret_values smc_ret; + +#if NEW_TEST_SESSION + /* + * This tests suite must start with a system reset. Following a reset, + * we expect TFTF to proceed with the rest of test cases. With + * NEW_TEST_SESSION set when built, TFTF will run this test case again + * after reset. Thus we'll continue resetting forever. + * + * If NEW_TEST_SESSION is set, skip this test case. sip_version_check + * won't be set to SIP_VERSION_OK, thereby skipping rest of test cases + * as well. + */ + tftf_testcase_printf("This suite needs TFTF built with NEW_TEST_SESSION=0\n"); + return TEST_RESULT_SKIPPED; +#endif + + /* + * Query system ARM SiP service version. State switch is available since + * version 0.2. + */ + smc_ret = tftf_smc(&sip_version_smc); + if (((int) smc_ret.ret0) >= 0) { + version = (smc_ret.ret0 << 8) | (smc_ret.ret1 & 0xff); + if (version >= 0x02) + sip_version_check = SIP_VERSION_OK; + } else { + tftf_testcase_printf("Test needs SiP service version 0.2 or later\n"); + return TEST_RESULT_SKIPPED; + } + + /* + * This test will be continuously re-entered after reboot, until it + * returns success. + */ + if (tftf_is_rebooted()) + return TEST_RESULT_SUCCESS; + + tftf_testcase_printf("Issuing system reset before state switch\n"); + + tftf_notify_reboot(); + tftf_smc(&reset); + + /* System reset is not expected to return */ + return TEST_RESULT_FAIL; +#else + tftf_testcase_printf("Test not ported to AArch32\n"); + return TEST_RESULT_SKIPPED; +#endif +} + +/* + * @Test_Aim@ Request execution state switch with invalid entry point. Expect + * parameter error when switching from AArch64 to AArch32. + */ +test_result_t test_exec_state_switch_invalid_pc(void) +{ +#ifdef AARCH64 + int ret; + + smc_args args = { + .arg0 = ARM_SIP_SVC_EXE_STATE_SWITCH, + .arg1 = (u_register_t) -1, + .arg2 = LO32(&state_switch_a32_entry), + .arg3 = HI32(&state_switch_cookie), + .arg4 = LO32(&state_switch_cookie) + }; + + if (sip_version_check != SIP_VERSION_OK) + return TEST_RESULT_SKIPPED; + + state_switch_cookie.success = 0; + ret = do_state_switch(&args); + if (state_switch_cookie.success || (ret != STATE_SW_E_PARAM)) + return TEST_RESULT_FAIL; + + return TEST_RESULT_SUCCESS; +#else + tftf_testcase_printf("Test not ported to AArch32\n"); + return TEST_RESULT_SKIPPED; +#endif +} + +/* + * @Test_Aim@ Request execution state switch with context_hi, and upper part of + * context_lo set. Expect failure as they're not supposed to be set when + * switching from AArch64 to AArch32. + */ +test_result_t test_exec_state_switch_invalid_ctx(void) +{ +#ifdef AARCH64 + int ret; + + smc_args args = { + .arg0 = ARM_SIP_SVC_EXE_STATE_SWITCH, + .arg1 = HI32(&state_switch_a32_entry), + .arg2 = LO32(&state_switch_a32_entry), + .arg3 = -1, + .arg4 = LO32(&state_switch_cookie) + }; + + if (sip_version_check != SIP_VERSION_OK) + return TEST_RESULT_SKIPPED; + + state_switch_cookie.success = 0; + ret = do_state_switch(&args); + if (state_switch_cookie.success || (ret != STATE_SW_E_PARAM)) + return TEST_RESULT_FAIL; + + return TEST_RESULT_SUCCESS; +#else + tftf_testcase_printf("Test not ported to AArch32\n"); + return TEST_RESULT_SKIPPED; +#endif +} + +/* + * @Test_Aim@ Perform execution state switch, and back. We don't expect any + * failures. + */ +test_result_t test_exec_state_switch_valid(void) +{ +#ifdef AARCH64 + int ret; + + smc_args args = { + .arg0 = ARM_SIP_SVC_EXE_STATE_SWITCH, + .arg1 = HI32(&state_switch_a32_entry), + .arg2 = LO32(&state_switch_a32_entry), + .arg3 = HI32(&state_switch_cookie), + .arg4 = LO32(&state_switch_cookie) + }; + + if (sip_version_check != SIP_VERSION_OK) + return TEST_RESULT_SKIPPED; + + /* Make sure that we've a 32-bit PC to enter AArch32 */ + if (HI32(&state_switch_a32_entry)) { + tftf_testcase_printf("AArch32 PC wider than 32 bits. Test skipped; needs re-link\n"); + return TEST_RESULT_SKIPPED; + } + + /* + * Perform a state switch to 32 and back. Expect success field in the + * cookie to be set and return code zero, + */ + state_switch_cookie.success = 0; + ret = do_state_switch(&args); + if (!state_switch_cookie.success || (ret != 0)) + return TEST_RESULT_FAIL; + + return TEST_RESULT_SUCCESS; +#else + tftf_testcase_printf("Test not ported to AArch32\n"); + return TEST_RESULT_SKIPPED; +#endif +} + +/* + * Entry point for the secondary CPU. Send an event to the caller and returns + * immediately. + */ +static inline test_result_t cpu_ping(void) +{ +#ifdef AARCH64 + /* Tell the lead CPU that the calling CPU has entered the test */ + tftf_send_event(&secondary_booted); + + /* + * When returning from the function, the TFTF framework will power CPUs + * down, without this test needing to do anything + */ + return TEST_RESULT_SUCCESS; +#else + tftf_testcase_printf("Test not ported to AArch32\n"); + return TEST_RESULT_SKIPPED; +#endif +} + +/* + * @Test_Aim@ Power on any secondary and request a state switch. We expect the + * request to be denied because a secondary had been brought up. + */ +test_result_t test_exec_state_switch_after_cpu_on(void) +{ +#ifdef AARCH64 + u_register_t other_mpidr, my_mpidr; + int ret; + + smc_args args = { + .arg0 = ARM_SIP_SVC_EXE_STATE_SWITCH, + .arg1 = HI32(&state_switch_a32_entry), + .arg2 = LO32(&state_switch_a32_entry), + .arg3 = HI32(&state_switch_cookie), + .arg4 = LO32(&state_switch_cookie) + }; + + if (sip_version_check != SIP_VERSION_OK) + return TEST_RESULT_SKIPPED; + + /* Make sure that we've a 32-bit PC to enter AArch32 */ + if (HI32(&state_switch_a32_entry)) { + tftf_testcase_printf("AArch32 PC wider than 32 bits. Test skipped; needs re-link\n"); + return TEST_RESULT_SKIPPED; + } + + tftf_init_event(&secondary_booted); + + /* Find a valid CPU to power on */ + my_mpidr = read_mpidr_el1() & MPID_MASK; + other_mpidr = tftf_find_any_cpu_other_than(my_mpidr); + if (other_mpidr == INVALID_MPID) { + tftf_testcase_printf("Couldn't find a valid other CPU\n"); + return TEST_RESULT_FAIL; + } + + /* Power on the other CPU */ + ret = tftf_cpu_on(other_mpidr, (uintptr_t) cpu_ping, 0); + if (ret != PSCI_E_SUCCESS) { + INFO("powering on %llx failed", (unsigned long long) + other_mpidr); + return TEST_RESULT_FAIL; + } + + /* Wait for flag to proceed */ + tftf_wait_for_event(&secondary_booted); + + /* + * Request a state switch to 32 and back. Expect failure since we've + * powerd a secondary on. + */ + state_switch_cookie.success = 0; + ret = do_state_switch(&args); + if ((state_switch_cookie.success != 0) || (ret != STATE_SW_E_DENIED)) + return TEST_RESULT_FAIL; + else + return TEST_RESULT_SUCCESS; +#else + tftf_testcase_printf("Test not ported to AArch32\n"); + return TEST_RESULT_SKIPPED; +#endif +} diff --git a/tests/runtime_services/sip_service/test_exec_state_switch_asm.S b/tests/runtime_services/sip_service/test_exec_state_switch_asm.S new file mode 100644 index 0000000..d010231 --- /dev/null +++ b/tests/runtime_services/sip_service/test_exec_state_switch_asm.S @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#define COOKIE_SIZE 20 + +#ifdef AARCH64 +/* int do_state_switch(void *) */ + .globl do_state_switch +func do_state_switch + /* Temporarily save beginning of stack */ + mov x7, sp + + /* + * When asking to switch execution state, we can't expect general + * purpose registers hold their values. EL3 might clear them all; even + * if EL3 were to preserve them, the register width shrinks and then + * expands, leaving the upper part unknown. So save them before and + * restore after call to switch. + */ + stp x8, x9, [sp, #-16]! + stp x10, x11, [sp, #-16]! + stp x12, x13, [sp, #-16]! + stp x14, x15, [sp, #-16]! + stp x16, x17, [sp, #-16]! + stp x18, x19, [sp, #-16]! + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + stp x28, x29, [sp, #-16]! + + /* + * State switch effectively means a soft reset; so SCTLR will lose its + * value too. + */ + mrs x1, CurrentEL + cmp x1, #(2 << 2) + b.ne 1f + mrs x1, sctlr_el2 + b 2f +1: + mrs x1, sctlr_el1 +2: + stp x30, x1, [sp, #-16]! + + /* Store the PC in the cookie when switching back to AArch64 */ + ldr x4, =state_switch_cookie + adr x2, do_switch_back + mov w1, w2 + lsr x1, x1, #32 + str w1, [x4, #0] /* PC hi */ + str w2, [x4, #4] /* PC lo */ + + /* Store valid stack pointer in cookie */ + mov x8, sp + str x8, [x4, #8] + + /* Stash stack and LR before calling functions */ + mov x28, x7 + mov x29, x30 + + mov x10, x0 + + /* + * Clean and invalidate cookie memory as it's going to be accessed with + * MMU off in the new state. + */ + mov x0, x4 + ldr x1, =COOKIE_SIZE + bl flush_dcache_range + + /* + * Flush stack context saved on stack as it'll be accessed immediately + * after switching back, with MMU off. + */ + mov x0, x8 + sub x1, x28, x8 + bl flush_dcache_range + + /* Prepare arguments for state switch SMC */ + ldr x0, [x10], #8 + ldr x1, [x10], #8 + ldr x2, [x10], #8 + ldr x3, [x10], #8 + ldr x4, [x10], #8 + + /* Switch state */ + smc #0 + + /* + * We reach here only if the SMC failed. If so, restore previously + * modified callee-saved registers, rewind stack, and return to caller + * with the error code from SMC. + */ + mov x1, x28 + mov x2, x29 + ldp x28, x29, [sp, #16] + mov sp, x1 + ret x2 + +restore_context: + /* Restore context */ + ldp x30, x1, [sp], #16 + ldp x28, x29, [sp], #16 + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + ldp x18, x19, [sp], #16 + ldp x16, x17, [sp], #16 + ldp x14, x15, [sp], #16 + ldp x12, x13, [sp], #16 + ldp x10, x11, [sp], #16 + ldp x8, x9, [sp], #16 + + dsb sy + mrs x0, CurrentEL + cmp x0, #(2 << 2) + b.ne 1f + msr sctlr_el2, x1 + b 2f +1: + msr sctlr_el1, x1 +2: + isb + + mov x0, #0 + ret +endfunc do_state_switch + +/* AArch64 entry point when switching back from AArch32 */ +do_switch_back: + /* w0 and w1 have the cookie */ + lsl x0, x0, #32 + orr x0, x1, x0 + + ldr x1, [x0, #8] + mov sp, x1 + + b restore_context + + .section .data, "aw" + +/* AArch32 instructions to switch state back to AArch64, stored as data */ + .align 2 + .globl state_switch_a32_entry +state_switch_a32_entry: + /* Use the same context when switching back */ + .word 0xe1a03000 /* mov r3, r0 */ + .word 0xe1a04001 /* mov r4, r1 */ + + /* Set success flag in cookie */ + .word 0xe3a00001 /* mov r0, #1 */ + .word 0xe5810010 /* str r0, [r1, #16] */ + + /* Setup arguments for SMC */ + .word 0xe3a00020 /* mov r0, #0x0020 */ + .word 0xe3480200 /* movt r0, #0x8200 */ + + .word 0xe5912004 /* ldr r2, [r1, #4] */ + .word 0xe5911000 /* ldr r1, [r1, #0] */ + .word 0xe1600070 /* smc #0x0 */ + .word 0xeafffffe /* b . */ + +#else /* !AARCH64 */ + +/* Not supported on AArch32 yet */ +func do_state_switch + mov r0, #-1 + bx lr +endfunc do_state_switch + +#endif /* AARCH64 */ diff --git a/tests/tests-common.xml b/tests/tests-common.xml index fc793f9..feea931 100644 --- a/tests/tests-common.xml +++ b/tests/tests-common.xml @@ -147,6 +147,18 @@ + + + + + + + + +