blob: d2b2a1448c3a52b67b6938cd22c876764b98b00f [file] [log] [blame]
/*
* Copyright (c) 2022-2023, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdlib.h>
#include <cactus_test_cmds.h>
#include <ffa_endpoints.h>
#include <ffa_helpers.h>
#include <fpu.h>
#include <host_realm_helper.h>
#include <host_realm_mem_layout.h>
#include <host_shared_data.h>
#include <test_helpers.h>
#define REALM_TIME_SLEEP 300U
#define SENDER HYP_ID
#define RECEIVER SP_ID(1)
static const struct ffa_uuid expected_sp_uuids[] = { {PRIMARY_UUID} };
static struct mailbox_buffers mb;
static bool secure_mailbox_initialised;
static fpu_reg_state_t fpu_temp_ns;
typedef enum test_rl_sec_fp_cmd {
CMD_SIMD_NS_FILL = 0U,
CMD_SIMD_NS_CMP,
CMD_SIMD_RL_FILL,
CMD_SIMD_RL_CMP,
CMD_MAX_THREE_WORLD,
CMD_SIMD_SEC_FILL,
CMD_SIMD_SEC_CMP,
CMD_MAX_COUNT
} realm_test_cmd_t;
/*
* This function helps to Initialise secure_mailbox, creates realm payload and
* shared memory to be used between Host and Realm.
* Skip test if RME is not supported or not the right RMM version is begin used
*/
static test_result_t init_sp(void)
{
/* Verify that FFA is there and that it has the correct version. */
SKIP_TEST_IF_FFA_VERSION_LESS_THAN(1, 1);
if (!secure_mailbox_initialised) {
GET_TFTF_MAILBOX(mb);
CHECK_SPMC_TESTING_SETUP(1, 1, expected_sp_uuids);
secure_mailbox_initialised = true;
}
return TEST_RESULT_SUCCESS;
}
static test_result_t init_realm(void)
{
u_register_t retrmm;
if (get_armv9_2_feat_rme_support() == 0U) {
return TEST_RESULT_SKIPPED;
}
retrmm = host_rmi_version();
/*
* Skip test if RMM is TRP, TRP version is always null.
*/
if (retrmm == 0UL) {
return TEST_RESULT_SKIPPED;
}
/*
* Initialise Realm payload
*/
if (!host_create_realm_payload((u_register_t)REALM_IMAGE_BASE,
(u_register_t)PAGE_POOL_BASE,
(u_register_t)(PAGE_POOL_MAX_SIZE +
NS_REALM_SHARED_MEM_SIZE),
(u_register_t)PAGE_POOL_MAX_SIZE, 0UL)) {
return TEST_RESULT_FAIL;
}
/*
* Create shared memory between Host and Realm
*/
if (!host_create_shared_mem(NS_REALM_SHARED_MEM_BASE,
NS_REALM_SHARED_MEM_SIZE)) {
return TEST_RESULT_FAIL;
}
return TEST_RESULT_SUCCESS;
}
static bool host_realm_handle_fiq_exit(struct realm *realm_ptr)
{
struct rmi_rec_run *run = (struct rmi_rec_run *)realm_ptr->run;
if (run->exit.exit_reason == RMI_EXIT_FIQ) {
return true;
}
return false;
}
/* Send request to SP to fill FPU/SIMD regs with secure template values */
static bool fpu_fill_sec(void)
{
struct ffa_value ret = cactus_req_simd_fill_send_cmd(SENDER, RECEIVER);
if (!is_ffa_direct_response(ret)) {
ERROR("%s failed %d\n", __func__, __LINE__);
return false;
}
if (cactus_get_response(ret) == CACTUS_ERROR) {
ERROR("%s failed %d\n", __func__, __LINE__);
return false;
}
return true;
}
/* Send request to SP to compare FPU/SIMD regs with secure template values */
static bool fpu_cmp_sec(void)
{
struct ffa_value ret = cactus_req_simd_compare_send_cmd(SENDER, RECEIVER);
if (!is_ffa_direct_response(ret)) {
ERROR("%s failed %d\n", __func__, __LINE__);
return false;
}
if (cactus_get_response(ret) == CACTUS_ERROR) {
ERROR("%s failed %d\n", __func__, __LINE__);
return false;
}
return true;
}
/* Send request to Realm to fill FPU/SIMD regs with realm template values */
static bool fpu_fill_rl(void)
{
if (!host_enter_realm_execute(REALM_REQ_FPU_FILL_CMD, NULL, RMI_EXIT_HOST_CALL)) {
ERROR("%s failed %d\n", __func__, __LINE__);
return false;
}
return true;
}
/* Send request to Realm to compare FPU/SIMD regs with previous realm template values */
static bool fpu_cmp_rl(void)
{
if (!host_enter_realm_execute(REALM_REQ_FPU_FILL_CMD, NULL, RMI_EXIT_HOST_CALL)) {
ERROR("%s failed %d\n", __func__, __LINE__);
return false;
}
return true;
}
/*
* @Test_Aim@ Test secure interrupt handling while Secure Partition is in waiting
* state and Realm world runs a busy loop at R-EL1.
*
* 1. Send a direct message request command to first Cactus SP to start the
* trusted watchdog timer.
*
* 2. Once the SP returns with a direct response message, it moves to WAITING
* state.
*
* 3. Create and execute a busy loop to sleep the PE in the realm world for
* REALM_TIME_SLEEP ms.
*
* 4. Trusted watchdog timer expires during this time which leads to secure
* interrupt being triggered while cpu is executing in realm world.
*
* 5. Realm EL1 exits to host, but because the FIQ is still pending,
* the Host will be pre-empted to EL3.
*
* 6. The interrupt is trapped to BL31/SPMD as FIQ and later synchronously
* delivered to SPM.
*
* 7. SPM injects a virtual IRQ to first Cactus Secure Partition.
*
* 8. Once the SP has handled the interrupt, it returns execution back to normal
* world using FFA_MSG_WAIT call.
*
* 9. TFTF parses REC's exit reason (FIQ in this case).
*
* 10. TFTF sends a direct request message to SP to query the ID of last serviced
* secure virtual interrupt.
*
* 121. Further, TFTF expects SP to return the ID of Trusted Watchdog timer
* interrupt through a direct response message.
*
* 13. Test finishes successfully once the TFTF disables the trusted watchdog
* interrupt through a direct message request command.
*
* 14. TFTF then proceed to destroy the Realm.
*
*/
test_result_t host_realm_sec_interrupt_can_preempt_rl(void)
{
struct realm *realm_ptr;
struct ffa_value ret_values;
test_result_t res;
res = init_sp();
if (res != TEST_RESULT_SUCCESS) {
return res;
}
res = init_realm();
if (res != TEST_RESULT_SUCCESS) {
return res;
}
/* Enable trusted watchdog interrupt as IRQ in the secure side. */
if (!enable_trusted_wdog_interrupt(SENDER, RECEIVER)) {
return TEST_RESULT_FAIL;
}
/*
* Send a message to SP1 through direct messaging.
*/
ret_values = cactus_send_twdog_cmd(SENDER, RECEIVER, (REALM_TIME_SLEEP/2));
if (!is_ffa_direct_response(ret_values)) {
ERROR("Expected a direct response for starting TWDOG timer\n");
return TEST_RESULT_FAIL;
}
/*
* Spin Realm payload for REALM_TIME_SLEEP ms, This ensures secure wdog
* timer triggers during this time.
*/
realm_shared_data_set_host_val(HOST_SLEEP_INDEX, REALM_TIME_SLEEP);
host_enter_realm_execute(REALM_SLEEP_CMD, &realm_ptr, RMI_EXIT_FIQ);
/*
* Check if Realm exit reason is FIQ.
*/
if (!host_realm_handle_fiq_exit(realm_ptr)) {
ERROR("Trusted watchdog timer interrupt not fired\n");
host_destroy_realm();
return TEST_RESULT_FAIL;
}
/* Check for the last serviced secure virtual interrupt. */
ret_values = cactus_get_last_interrupt_cmd(SENDER, RECEIVER);
if (!is_ffa_direct_response(ret_values)) {
ERROR("Expected a direct response for last serviced interrupt"
" command\n");
return TEST_RESULT_FAIL;
}
/* Make sure Trusted Watchdog timer interrupt was serviced*/
if (cactus_get_response(ret_values) != IRQ_TWDOG_INTID) {
ERROR("Trusted watchdog timer interrupt not serviced by SP\n");
return TEST_RESULT_FAIL;
}
/* Disable Trusted Watchdog interrupt. */
if (!disable_trusted_wdog_interrupt(SENDER, RECEIVER)) {
return TEST_RESULT_FAIL;
}
if (!host_destroy_realm()) {
ERROR("host_destroy_realm error\n");
return TEST_RESULT_FAIL;
}
return TEST_RESULT_SUCCESS;
}
/*
* Test that FPU/SIMD state are preserved during a randomly context switch
* between secure/non-secure/realm(R-EL1)worlds.
* FPU/SIMD state consist of the 32 SIMD vectors, FPCR and FPSR registers,
* the test runs for 1000 iterations with random combination of:
* SECURE_FILL_FPU, SECURE_READ_FPU, REALM_FILL_FPU, REALM_READ_FPU,
* NONSECURE_FILL_FPU, NONSECURE_READ_FPU commands,to test all possible situations
* of synchronous context switch between worlds, while the content of those registers
* is being used.
*/
test_result_t host_realm_fpu_access_in_rl_ns_se(void)
{
int cmd = -1, old_cmd = -1, cmd_max;
test_result_t res;
res = init_sp();
if (res != TEST_RESULT_SUCCESS) {
cmd_max = CMD_MAX_THREE_WORLD;
} else {
cmd_max = CMD_MAX_COUNT;
if (!fpu_fill_sec()) {
ERROR("fpu_fill_sec error\n");
return TEST_RESULT_FAIL;
}
}
res = init_realm();
if (res != TEST_RESULT_SUCCESS) {
return res;
}
/*
* Fill all 3 world's FPU/SIMD state regs with some known values in the
* beginning to have something later to compare to.
*/
fpu_state_fill_regs_and_template(&fpu_temp_ns);
if (!fpu_fill_rl()) {
ERROR("fpu_fill_rl error\n");
goto destroy_realm;
}
for (uint32_t i = 0; i < 1000; i++) {
cmd = rand() % cmd_max;
if ((cmd == old_cmd) || cmd == CMD_MAX_THREE_WORLD) {
continue;
}
old_cmd = cmd;
switch (cmd) {
case CMD_SIMD_NS_FILL:
/* Non secure world fill FPU/SIMD state registers */
fpu_state_fill_regs_and_template(&fpu_temp_ns);
break;
case CMD_SIMD_NS_CMP:
/* Normal world verify its FPU/SIMD state registers data */
if (!fpu_state_compare_template(&fpu_temp_ns)) {
ERROR("%s failed %d\n", __func__, __LINE__);
goto destroy_realm;
}
break;
case CMD_SIMD_SEC_FILL:
/* secure world fill FPU/SIMD state registers */
if (!fpu_fill_sec()) {
goto destroy_realm;
}
break;
case CMD_SIMD_SEC_CMP:
/* Secure world verify its FPU/SIMD state registers data */
if (!fpu_cmp_sec()) {
goto destroy_realm;
}
break;
case CMD_SIMD_RL_FILL:
/* Realm R-EL1 world fill FPU/SIMD state registers */
if (!fpu_fill_rl()) {
goto destroy_realm;
}
break;
case CMD_SIMD_RL_CMP:
/* Realm R-EL1 world verify its FPU/SIMD state registers data */
if (!fpu_cmp_rl()) {
goto destroy_realm;
}
break;
default:
break;
}
}
if (!host_destroy_realm()) {
ERROR("host_destroy_realm error\n");
return TEST_RESULT_FAIL;
}
return TEST_RESULT_SUCCESS;
destroy_realm:
host_destroy_realm();
return TEST_RESULT_FAIL;
}