Add test for starting cpus.
Change-Id: Ia1959e6cf1018722ae140eeed4ffb1fb166ceb0e
diff --git a/src/arch/aarch64/inc/hf/arch/vm/shutdown.h b/src/arch/aarch64/inc/hf/arch/vm/power_mgmt.h
similarity index 76%
copy from src/arch/aarch64/inc/hf/arch/vm/shutdown.h
copy to src/arch/aarch64/inc/hf/arch/vm/power_mgmt.h
index cc179c5..7128b06 100644
--- a/src/arch/aarch64/inc/hf/arch/vm/shutdown.h
+++ b/src/arch/aarch64/inc/hf/arch/vm/power_mgmt.h
@@ -16,6 +16,14 @@
#pragma once
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
#include <stdnoreturn.h>
noreturn void shutdown(void);
+
+bool cpu_start(uintptr_t id, void *stack, size_t stack_size,
+ void (*entry)(uintptr_t arg), uintptr_t arg);
+
+noreturn void cpu_stop(void);
diff --git a/src/arch/aarch64/vm/BUILD.gn b/src/arch/aarch64/vm/BUILD.gn
index 8171764..b6dee03 100644
--- a/src/arch/aarch64/vm/BUILD.gn
+++ b/src/arch/aarch64/vm/BUILD.gn
@@ -28,10 +28,11 @@
]
}
-# Shutdown the system or exit emulation.
-source_set("shutdown") {
+# Shutdown the system or exit emulation, start/stop CPUs.
+source_set("power_mgmt") {
sources = [
- "shutdown.c",
+ "cpu_entry.S",
+ "power_mgmt.c",
]
deps = [
diff --git a/src/arch/aarch64/inc/hf/arch/vm/shutdown.h b/src/arch/aarch64/vm/cpu_entry.S
similarity index 77%
rename from src/arch/aarch64/inc/hf/arch/vm/shutdown.h
rename to src/arch/aarch64/vm/cpu_entry.S
index cc179c5..1d800c7 100644
--- a/src/arch/aarch64/inc/hf/arch/vm/shutdown.h
+++ b/src/arch/aarch64/vm/cpu_entry.S
@@ -14,8 +14,11 @@
* limitations under the License.
*/
-#pragma once
+.globl vm_cpu_entry_raw
+vm_cpu_entry_raw:
+ /* Initialise stack from the cpu_start_state struct. */
+ ldr x1, [x0]
+ mov sp, x1
-#include <stdnoreturn.h>
-
-noreturn void shutdown(void);
+ /* Jump to C entry point. */
+ b vm_cpu_entry
diff --git a/src/arch/aarch64/vm/power_mgmt.c b/src/arch/aarch64/vm/power_mgmt.c
new file mode 100644
index 0000000..50b5e47
--- /dev/null
+++ b/src/arch/aarch64/vm/power_mgmt.c
@@ -0,0 +1,106 @@
+/*
+ * 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 "hf/arch/vm/power_mgmt.h"
+
+#include "hf/spinlock.h"
+
+#include "vmapi/hf/call.h"
+
+#include "../psci.h"
+
+/**
+ * Holds temporary state used to set up the environment on which CPUs will
+ * start executing.
+ *
+ * vm_cpu_entry_raw requires that the first field of cpu_start_state be the
+ * initial stack pointer.
+ */
+struct cpu_start_state {
+ uintptr_t initial_sp;
+ void (*entry)(uintptr_t arg);
+ uintreg_t arg;
+ struct spinlock lock;
+};
+
+/**
+ * Releases the given cpu_start_state struct by releasing its lock, then calls
+ * the entry point specified by the caller of cpu_start.
+ */
+void vm_cpu_entry(struct cpu_start_state *s)
+{
+ struct cpu_start_state local = *(volatile struct cpu_start_state *)s;
+ sl_unlock(&s->lock);
+
+ local.entry(local.arg);
+
+ /* Turn off CPU if the entry point ever returns. */
+ cpu_stop();
+}
+
+/**
+ * Starts the CPU with the given ID. It will start at the provided entry point
+ * with the provided argument.
+ */
+bool cpu_start(uintptr_t id, void *stack, size_t stack_size,
+ void (*entry)(uintptr_t arg), uintptr_t arg)
+{
+ struct cpu_start_state s;
+ void vm_cpu_entry_raw(uintptr_t arg);
+
+ /* Initialise the temporary state we'll hold on the stack. */
+ sl_init(&s.lock);
+ sl_lock(&s.lock);
+ s.initial_sp = (uintptr_t)stack + stack_size;
+ s.entry = entry;
+ s.arg = arg;
+
+ /* Try to start the CPU. */
+ if (hf_call(PSCI_CPU_ON, id, (size_t)&vm_cpu_entry_raw, (size_t)&s) !=
+ PSCI_RETURN_SUCCESS) {
+ return false;
+ }
+
+ /*
+ * Wait for the starting cpu to release the spin lock, which indicates
+ * that it won't touch the state we hold on the stack anymore.
+ */
+ sl_lock(&s.lock);
+
+ return true;
+}
+
+/**
+ * Stops the current CPU.
+ */
+noreturn void cpu_stop(void)
+{
+ hf_call(PSCI_CPU_OFF, 0, 0, 0);
+ for (;;) {
+ /* This should never be reached. */
+ }
+}
+
+/**
+ * Shuts down the system or exits emulation.
+ */
+noreturn void shutdown(void)
+{
+ hf_call(PSCI_SYSTEM_OFF, 0, 0, 0);
+ for (;;) {
+ /* This should never be reached. */
+ }
+}
diff --git a/src/arch/aarch64/vm/shutdown.c b/src/arch/aarch64/vm/shutdown.c
deleted file mode 100644
index 4bc48cd..0000000
--- a/src/arch/aarch64/vm/shutdown.c
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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 "hf/arch/vm/shutdown.h"
-
-#include "vmapi/hf/call.h"
-
-#include "../psci.h"
-
-/*
- * Shutdown the system or exit emulation.
- */
-noreturn void shutdown(void)
-{
- hf_call(PSCI_SYSTEM_OFF, 0, 0, 0);
- for (;;) {
- /* This should never be reached. */
- }
-}
diff --git a/test/vm/BUILD.gn b/test/vm/BUILD.gn
index 0ac7cde..7fe6e6a 100644
--- a/test/vm/BUILD.gn
+++ b/test/vm/BUILD.gn
@@ -40,7 +40,7 @@
"//src:memiter",
"//src/arch/${plat_arch}:entry",
"//src/arch/${plat_arch}/vm:hf_call",
- "//src/arch/${plat_arch}/vm:shutdown",
+ "//src/arch/${plat_arch}/vm:power_mgmt",
"//src/arch/${plat_arch}/vm:vm_entry",
]
}
@@ -54,7 +54,7 @@
"//src:dlog",
"//src/arch/${plat_arch}:entry",
"//src/arch/${plat_arch}/vm:hf_call",
- "//src/arch/${plat_arch}/vm:shutdown",
+ "//src/arch/${plat_arch}/vm:power_mgmt",
"//src/arch/${plat_arch}/vm:vm_entry",
]
}
diff --git a/test/vm/hftest.py b/test/vm/hftest.py
index ac0f247..d80d26b 100755
--- a/test/vm/hftest.py
+++ b/test/vm/hftest.py
@@ -36,7 +36,7 @@
qemu_args = [
"timeout", "--foreground", "5s",
"./prebuilts/linux-x64/qemu/qemu-system-aarch64", "-M", "virt,gic_version=3", "-cpu",
- "cortex-a57", "-m", "16M", "-machine", "virtualization=true",
+ "cortex-a57", "-smp", "4", "-m", "16M", "-machine", "virtualization=true",
"-nographic", "-nodefaults", "-serial", "stdio", "-kernel", hafnium,
"-initrd", initrd
]
diff --git a/test/vm/primary_only.c b/test/vm/primary_only.c
index dd7ab48..ba9e6d7 100644
--- a/test/vm/primary_only.c
+++ b/test/vm/primary_only.c
@@ -14,6 +14,12 @@
* limitations under the License.
*/
+#include <stdalign.h>
+
+#include "hf/arch/vm/power_mgmt.h"
+
+#include "hf/spinlock.h"
+
#include "vmapi/hf/call.h"
#include "hftest.h"
@@ -49,3 +55,29 @@
struct hf_vcpu_run_return res = hf_vcpu_run(1, 0);
EXPECT_EQ(res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
}
+
+/**
+ * Releases the lock passed in.
+ */
+static void vm_cpu_entry(uintptr_t arg)
+{
+ struct spinlock *lock = (struct spinlock *)arg;
+
+ dlog("Second CPU started.\n");
+ sl_unlock(lock);
+}
+
+TEST(cpus, start)
+{
+ struct spinlock lock = SPINLOCK_INIT;
+ alignas(4096) static char other_stack[4096];
+
+ /* Start secondary while holding lock. */
+ sl_lock(&lock);
+ EXPECT_EQ(cpu_start(1, other_stack, sizeof(other_stack), vm_cpu_entry,
+ (uintptr_t)&lock),
+ true);
+
+ /* Wait for CPU to release the lock. */
+ sl_lock(&lock);
+}