/* * * Copyright (c) 2019, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * * Description: * File containing various unit tests for the Corstone-700 test platform. * The contents of this file utilize handling of the transmitted commands * on respectively the ES and SE sides. * Test verification is not performed by this program. Instead, all test * variables are printed to the console, which will then be used to externally * verify the test. * Tests may be selected by providing the test index as an argument of this * program. */ #include #include #include #include #include #include #include #include #include #include enum test_apps { INVALID, ES_RESET, ES_MHU, SE_MHU, SE_TIMER, NUM_TEST_APPS }; enum es_command { ES_NONE, /* Send the value to the PE, the PE will then increment the value and send it back to here (Host) */ ES_INC_RETURN, /* Send the value to the ES, the ES will then increment the value and send it to Secure enclave*/ ES_INC_SEND_TO_SE, /* Send the value to the ES, make the ES send this * value to the secure enclave, and make the secure enclave increment and echo * the value back to the ES */ ES_INC_ECHO_SE, ES_NUM_COMMANDS, }; /* Macros for executing API commands, which will fail the test if the command fails. */ #define TRY_OPEN(FD, DEVICE, ...) \ int FD = open(DEVICE, ##__VA_ARGS__); \ if (FD == -1) \ { \ printf("Could not open " DEVICE " device, exiting...\n"); \ return 1; \ } #define TRY_CLOSE(FD, ...) \ FD = close(FD, ##__VA_ARGS__); \ if (FD == -1) \ { \ printf("Could not close file descriptor " #FD " device, exiting...\n"); \ return 1; \ } #define TRY_IOCTL(STATUS, FD, ...) \ STATUS = ioctl(FD, ##__VA_ARGS__); \ if (STATUS != 0) \ { \ printf("ioctl on file descriptor '" #FD "' failed with status code: %d", STATUS); \ return 1; \ } /** * struct rpmsg_endpoint_info - endpoint info representation * @name: name of service * @src: local address * @dst: destination address */ struct rpmsg_endpoint_info { char name[32]; u_int32_t src; u_int32_t dst; }; #define RPMSG_CREATE_EPT_IOCTL _IOW(0xb5, 0x1, struct rpmsg_endpoint_info) #define RPMSG_DESTROY_EPT_IOCTL _IO(0xb5, 0x2) #define EXTSYS_CPU_WAIT_DISABLE 0x0 /* MHU test command encoding */ #define BITMASK(x) ((unsigned)-1 >> ((sizeof(int) * CHAR_BIT) - x)) #define CMD_WIDTH 4 #define COMMAND(message) (message & BITMASK(CMD_WIDTH)) #define VALUE(message) ((unsigned)message >> CMD_WIDTH) #define ENCODE(command, value) ((command & BITMASK(CMD_WIDTH)) | (value << CMD_WIDTH)) enum se_command { SE_NONE, // Send the value to the PE, the PE will then increment the value // and send it back to here (Host) SE_INC_RETURN, // Start a single-shot timer which will be routed to the boot processor SE_TIMER_ONCE, SE_NUM_COMMANDS }; /* Desassert the reset signal of the external system#0 harness */ int es_reset_test() { int status; /* Bring external system out of reset */ TRY_OPEN(fd_sdk, "/dev/extsys_ctrl", O_RDWR); TRY_IOCTL(status, fd_sdk, EXTSYS_CPU_WAIT_DISABLE, 0); TRY_CLOSE(fd_sdk); return 0; } /* Test MHU connection between HOST <=> ES0 MHU 0 and 1, and SE <=> ES0 MHU 0 and 1 */ int es_mhu_test() { int status; int message; const int value = 0xABCDEF; struct rpmsg_endpoint_info es0mhu0_eptinfo = {"es0mhu0", 0XFFFFFFFF, 0xFFFFFFFF}; struct rpmsg_endpoint_info es0mhu1_eptinfo = {"es0mhu1", 0XFFFFFFFF, 0xFFFFFFFF}; TRY_OPEN(fd, "/dev/rpmsg_ctrl0", O_RDWR); /* Create endpoint interfaces for each MHU */ TRY_IOCTL(status, fd, RPMSG_CREATE_EPT_IOCTL, &es0mhu0_eptinfo); /* /dev/rpmsg1 is created */ TRY_IOCTL(status, fd, RPMSG_CREATE_EPT_IOCTL, &es0mhu1_eptinfo); /* /dev/rpmsg2 is created */ /* create endpoints for each mhu */ TRY_OPEN(fd_es0mhu0_ept, "/dev/rpmsg0", O_RDWR); TRY_OPEN(fd_es0mhu1_ept, "/dev/rpmsg1", O_RDWR); int epts[2]; epts[0] = fd_es0mhu0_ept; epts[1] = fd_es0mhu1_ept; /* Bring external system out of reset */ es_reset_test(); /* Await es system boot. Currently there is no signalling mechanism for * this, so revert to sleeping */ sleep(1); /* External system test */ for (int i = 0; i < 2; i++) { int ept = epts[i]; char *name; /* ========== ES < = > HOST TESTS ========== */ /* Make ES echo the value back to host with the incremented value */ message = ENCODE(ES_INC_RETURN, value); write(ept, &message, sizeof(message)); /* External system cannot execute the test command within an interrupt * handler. Since no command buffering has been implemented on the ES, a * sleep allows the ES to process a command before the host transmits * subsequent commands. */ sleep(1); /* Read the data transmitted by the previous command */ read(ept, &message, sizeof(message)); printf("Received %x from %s\n", message, ept == fd_es0mhu0_ept ? "es0mhu0" : "es0mhu1"); /* ================== ES < = > SE ================== */ /* Make ES send a message to the SE with the incremented value */ message = ENCODE(ES_INC_SEND_TO_SE, value); write(ept, &message, sizeof(message)); sleep(1); } /* destroy endpoints */ TRY_IOCTL(status, fd_es0mhu0_ept, RPMSG_DESTROY_EPT_IOCTL); TRY_IOCTL(status, fd_es0mhu1_ept, RPMSG_DESTROY_EPT_IOCTL); TRY_CLOSE(fd_es0mhu0_ept); TRY_CLOSE(fd_es0mhu1_ept); TRY_CLOSE(fd); return 0; } /* Test MHU connection between HOST <=> BP MHU 1 */ int se_mhu_test() { int status; struct rpmsg_endpoint_info semhu1_eptinfo = {"semhu1", 0XFFFFFFFF, 0xFFFFFFFF}; TRY_OPEN(fd, "/dev/rpmsg_ctrl0", O_RDWR); // Create endpoint interface TRY_IOCTL(status, fd, RPMSG_CREATE_EPT_IOCTL, &semhu1_eptinfo); // /dev/rpmsg0 is created // create endpoint TRY_OPEN(fd_semhu1_ept, "/dev/rpmsg0", O_RDWR); int message; const int value = 0xABCDEF; /* ========== BP < = > HOST TESTS ========== */ message = ENCODE(SE_INC_RETURN, value); write(fd_semhu1_ept, &message, sizeof(message)); sleep(1); read(fd_semhu1_ept, &message, sizeof(message)); printf("Received %x from boot processor\n", message); // destroy endpoints TRY_IOCTL(status, fd_semhu1_ept, RPMSG_DESTROY_EPT_IOCTL); TRY_CLOSE(fd_semhu1_ept); TRY_CLOSE(fd); return 0; } /* Test timer and interrupt driver in boot processor */ int se_timer_test() { int status; struct rpmsg_endpoint_info semhu1_eptinfo = {"semhu1", 0XFFFFFFFF, 0xFFFFFFFF}; TRY_OPEN(fd, "/dev/rpmsg_ctrl0", O_RDWR); // Create endpoint interface TRY_IOCTL(status, fd, RPMSG_CREATE_EPT_IOCTL, &semhu1_eptinfo); // /dev/rpmsg0 is created // create endpoint TRY_OPEN(fd_semhu1_ept, "/dev/rpmsg0", O_RDWR); int message = ENCODE(SE_TIMER_ONCE, 0); write(fd_semhu1_ept, &message, sizeof(message)); printf("Sent timer test command to boot processor\n", message); // destroy endpoints TRY_IOCTL(status, fd_semhu1_ept, RPMSG_DESTROY_EPT_IOCTL); TRY_CLOSE(fd_semhu1_ept); TRY_CLOSE(fd); return 0; } int main(int argc, char *argv[]) { if (argc != 2) { printf("Usage: ./test-app \n"); printf("\t test app number : 1 for boot processor MHU.\n"); printf("\t\tThis tests MHU communication between Host and Boot processor\n"); printf("\t test app number : 2 for boot processor Timer.\n"); printf("\t\tThis test Host Timer usage on Boot processor\n"); printf("\t\tusing interrupt router and interrupt collator \n"); return 1; } switch (atoi(argv[1])) { default: case INVALID: printf("Invalid test app specified\n"); printf("%d test apps are available\n", NUM_TEST_APPS - 1); break; case ES_RESET: return es_reset_test(); case ES_MHU: return es_mhu_test(); case SE_MHU: return se_mhu_test(); case SE_TIMER: return se_timer_test(); } return 0; }