htest: service boilerplate for secondary VMs.
In order to keep it easy to add tests, we need to avoid requiring new
VMs for each case as this will lead to an explosion of duplication and
build artifacts.
Allowing secondary VMs to contain selectable services for different
tests means they don't each require a separate VM and run into resource
limits. The resulting services are also avoid boilerplate so the source
gets to the point of the test more easily.
The primary VMs are also refactored to encourage splitting tests across
file but bundling them into the same VM so as to cut down on build
artifacts and make it easier to find and run tests.
Assertions can now be used in any context of a test, including in a test
service.
Change-Id: Id3b8a7579d0facdfceb9d77f62ef57241b31a88a
diff --git a/test/hftest/hftest_service.c b/test/hftest/hftest_service.c
new file mode 100644
index 0000000..048d267
--- /dev/null
+++ b/test/hftest/hftest_service.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * 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/std.h"
+
+#include "vmapi/hf/call.h"
+
+#include "hftest.h"
+
+alignas(4096) uint8_t kstack[4096];
+
+HFTEST_ENABLE();
+
+extern struct hftest_test hftest_begin[];
+extern struct hftest_test hftest_end[];
+
+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;
+}
+
+/** Find the service with the name passed in the arguments. */
+static hftest_test_fn find_service(struct memiter *args)
+{
+ struct memiter service_name;
+ struct hftest_test *test;
+
+ if (!memiter_parse_str(args, &service_name)) {
+ return NULL;
+ }
+
+ for (test = hftest_begin; test < hftest_end; ++test) {
+ if (test->kind == HFTEST_KIND_SERVICE &&
+ memiter_iseq(&service_name, test->name)) {
+ return test->fn;
+ }
+ }
+
+ return NULL;
+}
+
+static noreturn void abort(void)
+{
+ HFTEST_LOG("Service contained failures.");
+ for (;;) {
+ /*
+ * Hang if the service aborts as a secondary can't power down
+ * the machine.
+ */
+ }
+}
+
+noreturn void kmain(void)
+{
+ struct memiter args;
+ hftest_test_fn service;
+ struct hf_mailbox_receive_return res;
+ struct hftest_context *ctx;
+
+ /* Prepare the context. */
+
+ /* Set up the mailbox. */
+ hf_vm_configure(send_addr, recv_addr);
+
+ /* Receive the name of the service to run. */
+ res = hf_mailbox_receive(true);
+ memiter_init(&args, recv, res.size);
+ service = find_service(&args);
+ hf_mailbox_clear();
+
+ /* Check the service was found. */
+ if (service == NULL) {
+ HFTEST_LOG_FAILURE();
+ HFTEST_LOG(HFTEST_LOG_INDENT
+ "Unable to find requested service");
+ for (;;) {
+ /* Hang if the service was unknown. */
+ }
+ }
+
+ /* Clean the context. */
+ ctx = hftest_get_context();
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->abort = abort;
+ ctx->send = send;
+ ctx->recv = recv;
+
+ /* Pause so the next time cycles are given the service will be run. */
+ hf_vcpu_yield();
+
+ /* Let the service run. */
+ service();
+
+ /* Cleanly handle it if the service returns. */
+ if (ctx->failures) {
+ abort();
+ }
+
+ for (;;) {
+ /* Hang if the service returns. */
+ }
+}