| /* |
| * Copyright (c) 2024-2025, Arm Limited. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <string.h> |
| |
| #include <heap/page_alloc.h> |
| #include <host_crypto_utils.h> |
| #include <host_da_helper.h> |
| #include <host_realm_helper.h> |
| #include <host_realm_mem_layout.h> |
| #include <host_shared_data.h> |
| #include <mmio.h> |
| #include <pcie.h> |
| #include <pcie_doe.h> |
| #include <pcie_spec.h> |
| #include <platform.h> |
| #include <spdm.h> |
| #include <test_helpers.h> |
| |
| struct host_pdev gbl_host_pdev; |
| struct host_vdev gbl_host_vdev; |
| |
| /* |
| * This invokes various RMI calls related to PDEV, VDEV management that does |
| * PDEV create/communicate/set_key/abort/stop/destroy and assigns the device |
| * to a Realm using RMI VDEV ABIs |
| * |
| * 1. Create a Realm with DA feature enabled |
| * 2. Find a known PCIe endpoint and connect with TSM to get_cert and establish |
| * secure session |
| * 3. Assign the PCIe endpoint (a PF) to the Realm |
| * 4. Call Realm to do DA related RSI calls |
| * 5. Unassign the PCIe endpoint from the Realm |
| * 6. Delete the Realm |
| * 7. Reclaim the PCIe TDI from TSM |
| */ |
| test_result_t host_invoke_rmi_da_flow(void) |
| { |
| u_register_t rmi_feat_reg0; |
| uint32_t pdev_bdf, doe_cap_base; |
| struct host_pdev *h_pdev; |
| struct host_vdev *h_vdev; |
| uint8_t public_key_algo; |
| int rc; |
| bool realm_rc; |
| struct realm realm; |
| test_result_t result = TEST_RESULT_FAIL; |
| |
| CHECK_DA_SUPPORT_IN_RMI(rmi_feat_reg0); |
| SKIP_TEST_IF_DOE_NOT_SUPPORTED(pdev_bdf, doe_cap_base); |
| |
| INFO("DA on bdf: 0x%x, doe_cap_base: 0x%x\n", pdev_bdf, doe_cap_base); |
| |
| /* |
| * Create a Realm with DA feature enabled |
| * |
| * todo: creating this after host_pdev_setup causes Realm create to |
| * fail. |
| */ |
| rc = host_create_realm_with_feat_da(&realm); |
| if (rc != 0) { |
| INFO("Realm create with feat_da failed\n"); |
| return TEST_RESULT_FAIL; |
| } |
| |
| INFO("Realm created with feat_da enabled\n"); |
| |
| h_pdev = &gbl_host_pdev; |
| h_vdev = &gbl_host_vdev; |
| |
| /* Allocate granules. Skip DA ABIs if host_pdev_setup fails */ |
| rc = host_pdev_setup(h_pdev); |
| if (rc == -1) { |
| INFO("host_pdev_setup failed.\n"); |
| (void)host_destroy_realm(&realm); |
| return TEST_RESULT_FAIL; |
| } |
| |
| /* todo: move to tdi_pdev_setup */ |
| h_pdev->bdf = pdev_bdf; |
| h_pdev->doe_cap_base = doe_cap_base; |
| |
| /* Call rmi_pdev_create to transition PDEV to STATE_NEW */ |
| rc = host_pdev_transition(h_pdev, RMI_PDEV_STATE_NEW); |
| if (rc != 0) { |
| ERROR("PDEV transition: NULL -> STATE_NEW failed\n"); |
| goto err_pdev_reclaim; |
| } |
| |
| /* Call rmi_pdev_communicate to transition PDEV to NEEDS_KEY */ |
| rc = host_pdev_transition(h_pdev, RMI_PDEV_STATE_NEEDS_KEY); |
| if (rc != 0) { |
| ERROR("PDEV transition: PDEV_NEW -> PDEV_NEEDS_KEY failed\n"); |
| goto err_pdev_reclaim; |
| } |
| |
| /* Get public key. Verifying cert_chain not done by host but by Realm? */ |
| rc = host_get_public_key_from_cert_chain(h_pdev->cert_chain, |
| h_pdev->cert_chain_len, |
| h_pdev->public_key, |
| &h_pdev->public_key_len, |
| h_pdev->public_key_metadata, |
| &h_pdev->public_key_metadata_len, |
| &public_key_algo); |
| if (rc != 0) { |
| ERROR("Get public key failed\n"); |
| goto err_pdev_reclaim; |
| } |
| |
| if (public_key_algo == PUBLIC_KEY_ALGO_ECDSA_ECC_NIST_P256) { |
| h_pdev->public_key_sig_algo = RMI_SIGNATURE_ALGORITHM_ECDSA_P256; |
| } else if (public_key_algo == PUBLIC_KEY_ALGO_ECDSA_ECC_NIST_P384) { |
| h_pdev->public_key_sig_algo = RMI_SIGNATURE_ALGORITHM_ECDSA_P384; |
| } else { |
| h_pdev->public_key_sig_algo = RMI_SIGNATURE_ALGORITHM_RSASSA_3072; |
| } |
| INFO("DEV public key len/sig_algo: %ld/%d\n", h_pdev->public_key_len, |
| h_pdev->public_key_sig_algo); |
| |
| /* Call rmi_pdev_set_key transition PDEV to HAS_KEY */ |
| rc = host_pdev_transition(h_pdev, RMI_PDEV_STATE_HAS_KEY); |
| if (rc != 0) { |
| INFO("PDEV transition: PDEV_NEEDS_KEY -> PDEV_HAS_KEY failed\n"); |
| goto err_pdev_reclaim; |
| } |
| |
| /* Call rmi_pdev_comminucate to transition PDEV to READY state */ |
| rc = host_pdev_transition(h_pdev, RMI_PDEV_STATE_READY); |
| if (rc != 0) { |
| INFO("PDEV transition: PDEV_HAS_KEY -> PDEV_READY failed\n"); |
| goto err_pdev_reclaim; |
| } |
| |
| |
| /* |
| * 3 Assign VDEV (the PCIe endpoint) from the Realm |
| */ |
| rc = host_assign_vdev_to_realm(&realm, h_pdev, h_vdev); |
| if (rc != 0) { |
| INFO("VDEV assign to realm failed\n"); |
| /* TF-RMM has support till here. Change error code temporarily */ |
| result = TEST_RESULT_SUCCESS; |
| goto err_pdev_reclaim; |
| } |
| |
| /* |
| * 4 Call Realm to do DA related RSI calls |
| */ |
| realm_rc = host_enter_realm_execute(&realm, REALM_DA_RSI_CALLS, |
| RMI_EXIT_HOST_CALL, 0U); |
| if (!realm_rc) { |
| INFO("Realm DA_RSI_CALLS failed\n"); |
| goto err_pdev_reclaim; |
| } |
| |
| /* |
| * 5 Unassign VDEV (the PCIe endpoint) from the Realm |
| */ |
| rc = host_unassign_vdev_from_realm(&realm, h_pdev, h_vdev); |
| if (rc != 0) { |
| INFO("VDEV unassign to realm failed\n"); |
| goto err_pdev_reclaim; |
| } |
| |
| /* |
| * 6 Destroy the Realm |
| */ |
| if (!host_destroy_realm(&realm)) { |
| INFO("Realm destroy failed\n"); |
| (void)host_pdev_reclaim(h_pdev); |
| return TEST_RESULT_FAIL; |
| } |
| |
| /* |
| * 7 Reclaim PDEV (the PCIe TDI) from TSM |
| */ |
| rc = host_pdev_reclaim(h_pdev); |
| if (rc != 0) { |
| INFO("Reclaim PDEV from TSM failed\n"); |
| return TEST_RESULT_FAIL; |
| } |
| |
| return TEST_RESULT_SUCCESS; |
| |
| err_pdev_reclaim: |
| (void)host_destroy_realm(&realm); |
| (void)host_pdev_reclaim(h_pdev); |
| return result; |
| } |
| |
| /* |
| * This function invokes PDEV_CREATE on TRP and tries to test |
| * the EL3 RMM-EL3 IDE KM interface. Will be skipped on TF-RMM. |
| */ |
| test_result_t host_realm_test_root_port_key_management(void) |
| { |
| struct host_pdev *h_pdev; |
| int ret; |
| |
| if (host_rmi_version(RMI_ABI_VERSION_VAL) != 0U) { |
| tftf_testcase_printf("RMM is not TRP\n"); |
| return TEST_RESULT_SKIPPED; |
| } |
| |
| /* Initialize Host NS heap memory */ |
| ret = page_pool_init((u_register_t)PAGE_POOL_BASE, |
| (u_register_t)PAGE_POOL_MAX_SIZE); |
| if (ret != HEAP_INIT_SUCCESS) { |
| ERROR("Failed to init heap pool %d\n", ret); |
| return TEST_RESULT_FAIL; |
| } |
| |
| h_pdev = &gbl_host_pdev; |
| |
| /* |
| * Call rmi_pdev_create with invalid pdev, expect an error |
| * to be returned from TRP. |
| */ |
| ret = host_pdev_create(h_pdev); |
| if (ret != 0) { |
| return TEST_RESULT_SUCCESS; |
| } |
| |
| ERROR("RMI_PDEV_CREATE did not return error as expected\n"); |
| return TEST_RESULT_FAIL; |
| } |