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/driver/linux b/driver/linux
index 5da4b6b..3e669bc 160000
--- a/driver/linux
+++ b/driver/linux
@@ -1 +1 @@
-Subproject commit 5da4b6b4a3a24e7e3eb4dc5aabd0e601d5673aad
+Subproject commit 3e669bc98235cbaf0addc3176711ad28b62a17eb
diff --git a/inc/vmapi/hf/transport.h b/inc/vmapi/hf/transport.h
new file mode 100644
index 0000000..686143c
--- /dev/null
+++ b/inc/vmapi/hf/transport.h
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+/**
+ * Header for Hafnium messages
+ *
+ * NOTE: This is a work in progress. The final form of a Hafnium message header
+ * is likely to change.
+ */
+struct hf_msg_hdr {
+ uint64_t src_port;
+ uint64_t dst_port;
+};
diff --git a/test/linux/BUILD.gn b/test/linux/BUILD.gn
index 13bd807..8ec6af2 100644
--- a/test/linux/BUILD.gn
+++ b/test/linux/BUILD.gn
@@ -13,8 +13,14 @@
# limitations under the License.
import("//build/image/image.gni")
+import("//build/toolchain/platform.gni")
executable("test_binary") {
+ include_dirs = [
+ "//driver/linux/inc/uapi",
+ "//third_party/linux/include/uapi/",
+ ]
+
testonly = true
sources = [
"linux.c",
@@ -25,6 +31,14 @@
output_name = "test_binary"
}
+vm_kernel("socket_vm0") {
+ testonly = true
+
+ deps = [
+ ":hftest_secondary_vm_socket",
+ ]
+}
+
linux_initrd("linux_test_initrd") {
testonly = true
@@ -45,6 +59,12 @@
primary_vm = "//third_party:linux__prebuilt"
primary_initrd = ":linux_test_initrd"
+ secondary_vms = [ [
+ "1048576",
+ "1",
+ "socket0",
+ ":socket_vm0",
+ ] ]
}
group("linux") {
@@ -54,3 +74,23 @@
":linux_test",
]
}
+
+# Testing framework for a secondary VM with socket.
+source_set("hftest_secondary_vm_socket") {
+ testonly = true
+
+ configs += [ "//test/hftest:hftest_config" ]
+
+ sources = [
+ "hftest_socket.c",
+ ]
+
+ deps = [
+ "//src:dlog",
+ "//src:panic",
+ "//src:std",
+ "//src/arch/${plat_arch}:entry",
+ "//src/arch/${plat_arch}/hftest:entry",
+ "//src/arch/${plat_arch}/hftest:power_mgmt",
+ ]
+}
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);
+ }
+}
diff --git a/test/linux/linux.c b/test/linux/linux.c
index 3300799..f0798e9 100644
--- a/test/linux/linux.c
+++ b/test/linux/linux.c
@@ -14,13 +14,23 @@
* limitations under the License.
*/
+#include <errno.h>
#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
#include "hf/dlog.h"
+#include "hf/socket.h"
#include "hftest.h"
+#include <sys/socket.h>
#include <sys/syscall.h>
+#include <sys/types.h>
+
+#define MAX_BUF_SIZE 256
static int finit_module(int fd, const char *param_values, int flags)
{
@@ -48,8 +58,75 @@
EXPECT_EQ(delete_module("hafnium", 0), 0);
}
+/**
+ * Loads and unloads the Hafnium kernel module.
+ */
TEST(linux, load_hafnium)
{
insmod_hafnium();
rmmod_hafnium();
}
+
+/**
+ * Uses the kernel module to send a socket message from the primary VM to a
+ * secondary VM and echoes it back to the primary.
+ */
+TEST(linux, socket_echo_hafnium)
+{
+ spci_vm_id_t vm_id = HF_VM_ID_OFFSET + 1;
+ int port = 10;
+ int socket_id;
+ struct hf_sockaddr addr;
+ const char send_buf[] = "The quick brown fox jumps over the lazy dogs.";
+ size_t send_len = strlen(send_buf);
+ char resp_buf[MAX_BUF_SIZE];
+ ssize_t recv_len;
+
+ ASSERT_LT(send_len, MAX_BUF_SIZE);
+
+ insmod_hafnium();
+
+ /* Create Hafnium socket. */
+ socket_id = socket(PF_HF, SOCK_DGRAM, 0);
+ if (socket_id == -1) {
+ FAIL("Socket creation failed: %s", strerror(errno));
+ return;
+ }
+ HFTEST_LOG("Socket created successfully.");
+
+ /* Connect to requested VM & port. */
+ addr.family = PF_HF;
+ addr.vm_id = vm_id;
+ addr.port = port;
+ if (connect(socket_id, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ FAIL("Socket connection failed: %s", strerror(errno));
+ return;
+ }
+ HFTEST_LOG("Socket to secondary VM %d connected on port %d.", vm_id,
+ port);
+
+ /*
+ * Send a message to the secondary VM.
+ * Enable the confirm flag to try again in case port is busy.
+ */
+ if (send(socket_id, send_buf, send_len, MSG_CONFIRM) < 0) {
+ FAIL("Socket send() failed: %s", strerror(errno));
+ return;
+ }
+ HFTEST_LOG("Packet with length %d sent.", send_len);
+
+ /* Receive a response, which should be an echo of the sent packet. */
+ recv_len = recv(socket_id, resp_buf, sizeof(resp_buf) - 1, 0);
+
+ if (recv_len == -1) {
+ FAIL("Socket recv() failed: %s", strerror(errno));
+ return;
+ }
+ HFTEST_LOG("Packet with length %d received.", recv_len);
+
+ EXPECT_EQ(recv_len, send_len);
+ EXPECT_EQ(memcmp(send_buf, resp_buf, send_len), 0);
+
+ EXPECT_EQ(close(socket_id), 0);
+ rmmod_hafnium();
+}