Add Linux Hafnium module socket unit test
Add a Linux module unit test that covers more cases than the basic load/unload.
This test create a secondary VM, and using the socket interface sends a message
and received the same payload as a response.
Bug: 138977432
Change-Id: Ibbc313c1a788924b2f227a28bf0ecd3bf21304a1
diff --git a/test/linux/hftest_socket.c b/test/linux/hftest_socket.c
new file mode 100644
index 0000000..b318a4f
--- /dev/null
+++ b/test/linux/hftest_socket.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2019 The Hafnium Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdalign.h>
+#include <stdint.h>
+
+#include "hf/memiter.h"
+#include "hf/spci.h"
+#include "hf/std.h"
+
+#include "vmapi/hf/call.h"
+#include "vmapi/hf/transport.h"
+
+#include "hftest.h"
+
+alignas(4096) uint8_t kstack[4096];
+
+static alignas(HF_MAILBOX_SIZE) uint8_t send[HF_MAILBOX_SIZE];
+static alignas(HF_MAILBOX_SIZE) uint8_t recv[HF_MAILBOX_SIZE];
+
+static hf_ipaddr_t send_addr = (hf_ipaddr_t)send;
+static hf_ipaddr_t recv_addr = (hf_ipaddr_t)recv;
+
+static struct hftest_context global_context;
+
+struct hftest_context *hftest_get_context(void)
+{
+ return &global_context;
+}
+
+noreturn void abort(void)
+{
+ HFTEST_LOG("Service contained failures.");
+ /* Cause a fault, as a secondary can't power down the machine. */
+ *((volatile uint8_t *)1) = 1;
+
+ /* This should never be reached, but to make the compiler happy... */
+ for (;;) {
+ }
+}
+
+static void swap(uint64_t *a, uint64_t *b)
+{
+ uint64_t t = *a;
+ *a = *b;
+ *b = t;
+}
+
+noreturn void kmain(size_t memory_size)
+{
+ struct hftest_context *ctx;
+
+ /* Prepare the context. */
+
+ /* Set up the mailbox. */
+ hf_vm_configure(send_addr, recv_addr);
+
+ hf_mailbox_clear();
+
+ /* Clean the context. */
+ ctx = hftest_get_context();
+ memset_s(ctx, sizeof(*ctx), 0, sizeof(*ctx));
+ ctx->abort = abort;
+ ctx->send = (struct spci_message *)send;
+ ctx->recv = (struct spci_message *)recv;
+ ctx->memory_size = memory_size;
+
+ /* Pause so the next time cycles are given the service will be run. */
+ spci_yield();
+
+ for (;;) {
+ struct spci_message *send_buf = (struct spci_message *)send;
+ struct spci_message *recv_buf = (struct spci_message *)recv;
+
+ /* Receive the packet. */
+ spci_msg_recv(SPCI_MSG_RECV_BLOCK);
+ EXPECT_LE(recv_buf->length, SPCI_MSG_PAYLOAD_MAX);
+
+ /* Echo the message back to the sender. */
+ memcpy_s(send_buf->payload, SPCI_MSG_PAYLOAD_MAX,
+ recv_buf->payload, recv_buf->length);
+
+ /* Swap the socket's source and destination ports */
+ struct hf_msg_hdr *hdr = (struct hf_msg_hdr *)send_buf->payload;
+ swap(&(hdr->src_port), &(hdr->dst_port));
+
+ /* Swap the destination and source ids. */
+ spci_vm_id_t dst_id = recv_buf->source_vm_id;
+ spci_vm_id_t src_id = recv_buf->target_vm_id;
+
+ spci_message_init(send_buf, recv_buf->length, dst_id, src_id);
+
+ hf_mailbox_clear();
+ EXPECT_EQ(spci_msg_send(0), SPCI_SUCCESS);
+ }
+}