| /* |
| * Adapted from Hafnium. |
| * TODO: Figure out the right way of specifying the license header. |
| */ |
| |
| #include "arm64/ffa_utils.h" |
| |
| |
| static struct mailbox_buffers mb; |
| |
| #ifndef be16toh |
| #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
| |
| #define be16toh(v) __builtin_bswap16(v) |
| #define be32toh(v) __builtin_bswap32(v) |
| #define be64toh(v) __builtin_bswap64(v) |
| |
| #define htobe16(v) __builtin_bswap16(v) |
| #define htobe32(v) __builtin_bswap32(v) |
| #define htobe64(v) __builtin_bswap64(v) |
| |
| #define le16toh(v) (v) |
| #define le32toh(v) (v) |
| #define le64toh(v) (v) |
| |
| #define htole16(v) (v) |
| #define htole32(v) (v) |
| #define htole64(v) (v) |
| |
| #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
| |
| #define be16toh(v) (v) |
| #define be32toh(v) (v) |
| #define be64toh(v) (v) |
| |
| #define htobe16(v) (v) |
| #define htobe32(v) (v) |
| #define htobe64(v) (v) |
| |
| #define le16toh(v) __builtin_bswap16(v) |
| #define le32toh(v) __builtin_bswap32(v) |
| #define le64toh(v) __builtin_bswap64(v) |
| |
| #define htole16(v) __builtin_bswap16(v) |
| #define htole32(v) __builtin_bswap32(v) |
| #define htole64(v) __builtin_bswap64(v) |
| |
| #else |
| |
| /* |
| * __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ && |
| * __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ |
| */ |
| |
| #error "Unsupported byte order" |
| |
| #endif |
| #endif |
| |
| |
| /** |
| * Reverses the order of the elements in the given array. |
| */ |
| static void reverse(char *s, size_t len) |
| { |
| size_t i; |
| |
| for (i = 0; i < len / 2; i++) { |
| char t = s[i]; |
| s[i] = s[len - 1 - i]; |
| s[len - 1 - i] = t; |
| } |
| } |
| |
| /** |
| * Finds the next lexicographic permutation of the given array, if there is one. |
| */ |
| static void next_permutation(char *s, size_t len) |
| { |
| size_t i; |
| size_t j; |
| |
| for (i = len - 2; i < len; i--) { |
| const char t = s[i]; |
| if (t >= s[i + 1]) { |
| continue; |
| } |
| |
| for (j = len - 1; t >= s[j]; j--) { |
| } |
| |
| s[i] = s[j]; |
| s[j] = t; |
| reverse(s + i + 1, len - i - 1); |
| return; |
| } |
| } |
| |
| static void service_echo(int count) |
| { |
| report_info("%s: %d: VM id %d is running!", __func__, __LINE__, get_vm_id()); |
| |
| /* Loop, echo messages back to the sender. */ |
| while (count--) { |
| struct ffa_value ret = ffa_msg_wait(); |
| ffa_vm_id_t target_vm_id = ffa_msg_send_receiver(ret); |
| ffa_vm_id_t source_vm_id = ffa_msg_send_sender(ret); |
| void *send_buf = mb.send; |
| void *recv_buf = mb.recv; |
| |
| ASSERT_EQ(ret.func, FFA_MSG_SEND_32); |
| memcpy_s(send_buf, FFA_MSG_PAYLOAD_MAX, recv_buf, |
| ffa_msg_send_size(ret)); |
| |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| ffa_msg_send(target_vm_id, source_vm_id, ffa_msg_send_size(ret), |
| 0); |
| } |
| |
| report_info("%s: %d: VM id %d is done!", __func__, __LINE__, get_vm_id()); |
| } |
| |
| static void service_relay(int count) |
| { |
| report_info("%s: %d: VM id %d is running!", __func__, __LINE__, get_vm_id()); |
| |
| /* |
| * Loop, forward messages to the next VM. |
| * |
| * The first 32-bits of the message are the little-endian 32-bit ID of |
| * the VM to forward the message to. This ID will be dropped from the |
| * message so multiple IDs can be places at the start of the message. |
| */ |
| while (count--) { |
| ffa_vm_id_t *chain; |
| ffa_vm_id_t next_vm_id; |
| void *next_message; |
| uint32_t next_message_size; |
| |
| /* Receive the message to relay. */ |
| struct ffa_value ret = ffa_msg_wait(); |
| ASSERT_EQ(ret.func, FFA_MSG_SEND_32); |
| |
| /* Prepare to relay the message. */ |
| void *send_buf = mb.send; |
| void *recv_buf = mb.recv; |
| ASSERT_GE(ffa_msg_send_size(ret), sizeof(ffa_vm_id_t)); |
| |
| chain = (ffa_vm_id_t *)recv_buf; |
| next_vm_id = le16toh(*chain); |
| next_message = chain + 1; |
| next_message_size = |
| ffa_msg_send_size(ret) - sizeof(ffa_vm_id_t); |
| |
| /* Send the message to the next stage. */ |
| memcpy_s(send_buf, FFA_MSG_PAYLOAD_MAX, next_message, |
| next_message_size); |
| |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| ffa_msg_send(get_vm_id(), next_vm_id, next_message_size, 0); |
| } |
| |
| report_info("%s: %d: VM id %d is done!", __func__, __LINE__, get_vm_id()); |
| } |
| |
| static void service_clear_empty(void) |
| { |
| report_info("%s: %d: VM id %d is running!", __func__, __LINE__, get_vm_id()); |
| report_info("%s: %d: VM id %d is done!", __func__, __LINE__, get_vm_id()); |
| } |
| |
| static void echo(void) |
| { |
| const char message[] = "Echo this back to me!"; |
| struct ffa_value run_res; |
| |
| EXPECT(is_primary_vm()); |
| |
| run_res = ffa_run(SERVICE_VM1, 0); |
| EXPECT_EQ(run_res.func, FFA_MSG_WAIT_32); |
| EXPECT_EQ(run_res.arg2, FFA_SLEEP_INDEFINITE); |
| |
| /* Set the message, echo it and check it didn't change. */ |
| memcpy_s(mb.send, FFA_MSG_PAYLOAD_MAX, message, sizeof(message)); |
| EXPECT_EQ( |
| ffa_msg_send(get_vm_id(), SERVICE_VM1, sizeof(message), 0) |
| .func, |
| FFA_SUCCESS_32); |
| run_res = ffa_run(SERVICE_VM1, 0); |
| EXPECT_EQ(run_res.func, FFA_MSG_SEND_32); |
| EXPECT_EQ(ffa_msg_send_size(run_res), sizeof(message)); |
| EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0); |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| |
| report(true, "%s", __func__); |
| } |
| |
| static void repeated_echo(int count) |
| { |
| char message[] = "Echo this back to me!"; |
| struct ffa_value run_res; |
| |
| while (count--) { |
| /* Run secondary until it reaches the wait for messages. */ |
| run_res = ffa_run(SERVICE_VM1, 0); |
| EXPECT_EQ(run_res.func, FFA_MSG_WAIT_32); |
| EXPECT_EQ(run_res.arg2, FFA_SLEEP_INDEFINITE); |
| |
| /* Set the message, echo it and check it didn't change. */ |
| next_permutation(message, sizeof(message) - 1); |
| memcpy_s(mb.send, FFA_MSG_PAYLOAD_MAX, message, |
| sizeof(message)); |
| EXPECT_EQ(ffa_msg_send(get_vm_id(), SERVICE_VM1, |
| sizeof(message), 0) |
| .func, |
| FFA_SUCCESS_32); |
| run_res = ffa_run(SERVICE_VM1, 0); |
| EXPECT_EQ(run_res.func, FFA_MSG_SEND_32); |
| EXPECT_EQ(ffa_msg_send_size(run_res), sizeof(message)); |
| EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0); |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| } |
| |
| report(true, "%s", __func__); |
| } |
| |
| /** |
| * Send a message to relay_a which will forward it to relay_b where it will be |
| * sent back here. |
| */ |
| static void relay(void) |
| { |
| const char message[] = "Send this round the relay!"; |
| struct ffa_value run_res; |
| |
| run_res = ffa_run(SERVICE_VM1, 0); |
| EXPECT_EQ(run_res.func, FFA_MSG_WAIT_32); |
| EXPECT_EQ(run_res.arg2, FFA_SLEEP_INDEFINITE); |
| run_res = ffa_run(SERVICE_VM2, 0); |
| EXPECT_EQ(run_res.func, FFA_MSG_WAIT_32); |
| EXPECT_EQ(run_res.arg2, FFA_SLEEP_INDEFINITE); |
| |
| /* |
| * Build the message chain so the message is sent from here to |
| * SERVICE_VM1, then to SERVICE_VM2 and finally back to here. |
| */ |
| { |
| ffa_vm_id_t *chain = (ffa_vm_id_t *)mb.send; |
| *chain++ = htole32(SERVICE_VM2); |
| *chain++ = htole32(get_vm_id()); |
| memcpy_s(chain, FFA_MSG_PAYLOAD_MAX - (2 * sizeof(ffa_vm_id_t)), |
| message, sizeof(message)); |
| |
| EXPECT_EQ( |
| ffa_msg_send( |
| get_vm_id(), SERVICE_VM1, |
| sizeof(message) + (2 * sizeof(ffa_vm_id_t)), 0) |
| .func, |
| FFA_SUCCESS_32); |
| } |
| |
| /* Let SERVICE_VM1 forward the message. */ |
| run_res = ffa_run(SERVICE_VM1, 0); |
| EXPECT_EQ(run_res.func, FFA_MSG_SEND_32); |
| EXPECT_EQ(ffa_msg_send_receiver(run_res), SERVICE_VM2); |
| EXPECT_EQ(ffa_msg_send_size(run_res), 0); |
| |
| /* Let SERVICE_VM2 forward the message. */ |
| run_res = ffa_run(SERVICE_VM2, 0); |
| EXPECT_EQ(run_res.func, FFA_MSG_SEND_32); |
| |
| /* Ensure the message is intact. */ |
| EXPECT_EQ(ffa_msg_send_receiver(run_res), get_vm_id()); |
| EXPECT_EQ(ffa_msg_send_size(run_res), sizeof(message)); |
| EXPECT_EQ(memcmp(mb.recv, message, sizeof(message)), 0); |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| |
| report(true, "%s", __func__); |
| } |
| |
| |
| /** |
| * Clearing an empty mailbox is an error. |
| */ |
| static void clear_empty(void) |
| { |
| EXPECT_FFA_ERROR(ffa_rx_release(), FFA_DENIED); |
| EXPECT_FFA_ERROR(ffa_rx_release(), FFA_DENIED); |
| EXPECT_FFA_ERROR(ffa_rx_release(), FFA_DENIED); |
| report(true, "%s", __func__); |
| } |
| |
| static void test_echo(void) |
| { |
| if (is_primary_vm()) { |
| echo(); |
| } else { |
| service_echo(1); |
| } |
| } |
| |
| static void test_repeated_echo(void) |
| { |
| int count = 100; |
| |
| if (is_primary_vm()) { |
| repeated_echo(count); |
| } else { |
| service_echo(count); |
| } |
| } |
| |
| static void test_relay(void) |
| { |
| if (is_primary_vm()) { |
| relay(); |
| } else { |
| service_relay(1); |
| } |
| } |
| |
| static void test_clear_empty(void) |
| { |
| if (is_primary_vm()) { |
| clear_empty(); |
| } else { |
| service_clear_empty(); |
| } |
| } |
| |
| int main(void) |
| { |
| mb = set_up_mailbox(); |
| |
| if (0) { |
| test_echo(); |
| test_repeated_echo(); |
| test_clear_empty();} |
| test_relay(); |
| |
| if (is_primary_vm()) { |
| return report_summary(); |
| } else { |
| return 0; |
| } |
| } |