| /* |
| * Copyright (c) 2021-2023, Arm Limited. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <stdlib.h> |
| |
| #include <assert.h> |
| #include <arch_features.h> |
| #include <debug.h> |
| #include <irq.h> |
| #include <drivers/arm/arm_gic.h> |
| #include <drivers/arm/gic_v3.h> |
| |
| #include <host_realm_helper.h> |
| #include <host_realm_mem_layout.h> |
| #include <host_realm_pmu.h> |
| #include <host_shared_data.h> |
| |
| #define SLEEP_TIME_MS 200U |
| |
| extern const char *rmi_exit[]; |
| |
| /* |
| * @Test_Aim@ Test realm payload creation and execution |
| */ |
| test_result_t host_test_realm_create_enter(void) |
| { |
| bool ret1, ret2; |
| u_register_t retrmm; |
| |
| if (get_armv9_2_feat_rme_support() == 0U) { |
| INFO("platform doesn't support RME\n"); |
| return TEST_RESULT_SKIPPED; |
| } |
| |
| host_rmi_init_cmp_result(); |
| |
| retrmm = host_rmi_version(); |
| VERBOSE("RMM version is: %lu.%lu\n", |
| RMI_ABI_VERSION_GET_MAJOR(retrmm), |
| RMI_ABI_VERSION_GET_MINOR(retrmm)); |
| /* |
| * Skip the test if RMM is TRP, TRP version is always null. |
| */ |
| if (retrmm == 0UL) { |
| INFO("Test case not supported for TRP as RMM\n"); |
| return TEST_RESULT_SKIPPED; |
| } |
| |
| 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; |
| } |
| if (!host_create_shared_mem(NS_REALM_SHARED_MEM_BASE, |
| NS_REALM_SHARED_MEM_SIZE)) { |
| return TEST_RESULT_FAIL; |
| } |
| |
| realm_shared_data_set_host_val(HOST_SLEEP_INDEX, SLEEP_TIME_MS); |
| ret1 = host_enter_realm_execute(REALM_SLEEP_CMD, NULL); |
| ret2 = host_destroy_realm(); |
| |
| if (!ret1 || !ret2) { |
| ERROR("%s(): enter=%d destroy=%d\n", |
| __func__, ret1, ret2); |
| return TEST_RESULT_FAIL; |
| } |
| |
| return host_cmp_result(); |
| } |
| /* |
| * This function is called on REC exit due to IRQ. |
| * By checking Realm PMU state in RecExit object this finction |
| * detects if the exit was caused by PMU interrupt. In that |
| * case it disables physical PMU interrupt and sets virtual |
| * PMU interrupt pending by writing to gicv3_lrs attribute |
| * of RecEntry object and re-enters the Realm. |
| * |
| * @return true in case of PMU interrupt, false otherwise. |
| */ |
| static bool host_realm_handle_irq_exit(struct realm *realm_ptr) |
| { |
| struct rmi_rec_run *run = (struct rmi_rec_run *)realm_ptr->run; |
| |
| /* Check PMU state */ |
| if ((run->exit.pmu_ovf & run->exit.pmu_intr_en & |
| run->exit.pmu_cntr_en) != 0UL) { |
| unsigned int host_call_result; |
| u_register_t exit_reason, retrmm; |
| int ret; |
| |
| tftf_irq_disable(PMU_PPI); |
| ret = tftf_irq_unregister_handler(PMU_PPI); |
| if (ret != 0) { |
| ERROR("Failed to %sregister IRQ handler\n", "un"); |
| return false; |
| } |
| |
| /* Inject PMU virtual interrupt */ |
| run->entry.gicv3_lrs[0] = |
| ICH_LRn_EL2_STATE_Pending | ICH_LRn_EL2_Group_1 | |
| (PMU_VIRQ << ICH_LRn_EL2_vINTID_SHIFT); |
| |
| /* Re-enter Realm */ |
| INFO("Re-entering Realm with vIRQ %lu pending\n", PMU_VIRQ); |
| |
| retrmm = host_realm_rec_enter(realm_ptr, &exit_reason, |
| &host_call_result); |
| if ((retrmm == REALM_SUCCESS) && |
| (exit_reason == RMI_EXIT_HOST_CALL) && |
| (host_call_result == TEST_RESULT_SUCCESS)) { |
| return true; |
| } |
| |
| ERROR("%s() failed, ret=%lx host_call_result %u\n", |
| "host_realm_rec_enter", retrmm, host_call_result); |
| } |
| return false; |
| } |
| |
| /* |
| * @Test_Aim@ Test realm PMU |
| * |
| * This function tests PMU functionality in Realm |
| * |
| * @cmd: PMU test number |
| * @return test result |
| */ |
| static test_result_t host_test_realm_pmuv3(uint8_t cmd) |
| { |
| struct realm *realm_ptr; |
| u_register_t retrmm; |
| bool ret1, ret2; |
| |
| if (get_armv9_2_feat_rme_support() == 0U) { |
| INFO("platform doesn't support RME\n"); |
| return TEST_RESULT_SKIPPED; |
| } |
| |
| host_rmi_init_cmp_result(); |
| |
| retrmm = host_rmi_version(); |
| VERBOSE("RMM version is: %lu.%lu\n", |
| RMI_ABI_VERSION_GET_MAJOR(retrmm), |
| RMI_ABI_VERSION_GET_MINOR(retrmm)); |
| /* |
| * Skip the test if RMM is TRP, TRP version is always null. |
| */ |
| if (retrmm == 0UL) { |
| INFO("Test case not supported for TRP as RMM\n"); |
| return TEST_RESULT_SKIPPED; |
| } |
| |
| host_set_pmu_state(); |
| |
| 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, |
| RMI_FEATURE_REGISTER_0_PMU_EN)) { |
| return TEST_RESULT_FAIL; |
| } |
| if (!host_create_shared_mem(NS_REALM_SHARED_MEM_BASE, |
| NS_REALM_SHARED_MEM_SIZE)) { |
| return TEST_RESULT_FAIL; |
| } |
| |
| ret1 = host_enter_realm_execute(cmd, &realm_ptr); |
| if (!ret1 || (cmd != REALM_PMU_INTERRUPT)) { |
| goto test_exit; |
| } |
| |
| ret1 = host_realm_handle_irq_exit(realm_ptr); |
| |
| test_exit: |
| ret2 = host_destroy_realm(); |
| if (!ret1 || !ret2) { |
| ERROR("%s() enter=%u destroy=%u\n", __func__, ret1, ret2); |
| return TEST_RESULT_FAIL; |
| } |
| |
| if (!host_check_pmu_state()) { |
| return TEST_RESULT_FAIL; |
| } |
| |
| return host_cmp_result(); |
| } |
| |
| /* |
| * Test if the cycle counter works in Realm with NOPs execution |
| */ |
| test_result_t host_realm_pmuv3_cycle_works(void) |
| { |
| return host_test_realm_pmuv3(REALM_PMU_CYCLE); |
| } |
| |
| /* |
| * Test if the event counter works in Realm with NOPs execution |
| */ |
| test_result_t host_realm_pmuv3_event_works(void) |
| { |
| return host_test_realm_pmuv3(REALM_PMU_EVENT); |
| } |
| |
| /* |
| * Test if Realm entering/exiting RMM preserves PMU state |
| */ |
| test_result_t host_realm_pmuv3_rmm_preserves(void) |
| { |
| return host_test_realm_pmuv3(REALM_PMU_PRESERVE); |
| } |
| |
| /* |
| * IRQ handler for PMU_PPI #23. |
| * This handler should not be called, as RMM handles IRQs. |
| */ |
| static int host_overflow_interrupt(void *data) |
| { |
| (void)data; |
| |
| assert(false); |
| return -1; |
| } |
| |
| /* |
| * Test PMU interrupt functionality in Realm |
| */ |
| test_result_t host_realm_pmuv3_overflow_interrupt(void) |
| { |
| /* Register PMU IRQ handler */ |
| int ret = tftf_irq_register_handler(PMU_PPI, host_overflow_interrupt); |
| |
| if (ret != 0) { |
| tftf_testcase_printf("Failed to %sregister IRQ handler\n", |
| ""); |
| return TEST_RESULT_FAIL; |
| } |
| |
| tftf_irq_enable(PMU_PPI, GIC_HIGHEST_NS_PRIORITY); |
| return host_test_realm_pmuv3(REALM_PMU_INTERRUPT); |
| } |