aboutsummaryrefslogtreecommitdiff
path: root/arch/riscv/supervisor/hsm/hsm.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/riscv/supervisor/hsm/hsm.c')
-rw-r--r--arch/riscv/supervisor/hsm/hsm.c215
1 files changed, 215 insertions, 0 deletions
diff --git a/arch/riscv/supervisor/hsm/hsm.c b/arch/riscv/supervisor/hsm/hsm.c
new file mode 100644
index 0000000..0b2daca
--- /dev/null
+++ b/arch/riscv/supervisor/hsm/hsm.c
@@ -0,0 +1,215 @@
+#include "compiler.h"
+#include "config.h"
+#include "sbi.h"
+
+// Implemented in head.S. Here we need the symbol in order to pass the address
+// of the function when starting secondary harts.
+extern void _start(void);
+
+/*
+ * Similar to the Linux kernel and what I did in https://github.com/mssola/fbos,
+ * harts will enter in a lottery when starting. See the README.md file for more
+ * details on this.
+ */
+atomic_t hart_lottery __attribute__((__section__(".sdata")));
+atomic_t hart_failed_lottery __attribute__((__section__(".sdata")));
+
+// Fake a delay so it's easier on the eyes.
+#define SLEEP() \
+ for (int i = 0; i < 1000000000; i++) { \
+ }
+
+/*
+ * Printing utilities.
+ */
+
+void printk(const char *const message);
+
+void write(const char *const message, size_t n)
+{
+ struct sbi_ret ret = sbi_ecall2(DBCN_EXT, DBCN_WRITE, n, (unsigned long)message);
+ if (ret.error != SBI_SUCCESS) {
+ for (;;) {
+ ;
+ }
+ }
+}
+
+size_t strlen(const char *const message)
+{
+ size_t i = 0;
+
+ for (; message[i] != '\0'; i++)
+ ;
+
+ return i;
+}
+
+__attribute__((__noreturn__)) void die(const char *const message)
+{
+ if (message) {
+ printk(message);
+ }
+
+ for (;;)
+ ;
+}
+
+void print_digit(uint64_t digit)
+{
+ char buffer[2];
+
+ if (digit > 9) {
+ die("We cannot print numbers with two or more digits :D\n");
+ }
+
+ buffer[0] = '0' + digit;
+ buffer[1] = '\0';
+ write(buffer, 2);
+}
+
+void printk(const char *const message)
+{
+ size_t len = strlen(message);
+ if (!len) {
+ return;
+ }
+
+ write(message, len);
+}
+
+/*
+ * Printing/asserting the stats of the lottery.
+ */
+
+void assert_lottery(uint64_t participated, uint64_t failed)
+{
+ if (hart_lottery.value != participated) {
+ die("Bad number for participating harts\n");
+ }
+ if (hart_failed_lottery.value != failed) {
+ die("Bad number for failed harts\n");
+ }
+}
+
+void print_lottery(void)
+{
+ printk("How many harts have _participated_ in the lottery? ");
+ print_digit(hart_lottery.value);
+ write("\n", 1);
+
+ printk("How many harts have _failed_ in the lottery? ");
+ print_digit(hart_failed_lottery.value);
+ write("\n", 1);
+}
+
+// Called at the very end of the program. It will shutdown the machine.
+void end(void)
+{
+ printk("\nTHE END\n");
+
+ // SBI call to the SRST extension to gracefully shutdown the machine.
+ sbi_ecall2(SRST_EXT, SRST_RESET, SRST_SHUTDOWN, SRST_NO_REASON);
+}
+
+void start_kernel(uint64_t hart_id, uintptr_t opaque)
+{
+ printk("Hello, world!\n");
+
+ /*
+ * Figure out the harts that are meant to be stopped for now.
+ */
+
+ unsigned int other_harts[NR_CPUS - 1];
+ struct sbi_ret ret;
+
+ printk("Hart that won the lottery: ");
+ print_digit(hart_id);
+ write("\n", 1);
+
+ printk("Hart still asleep: ");
+ for (unsigned int i = 0, j = 0; i < NR_CPUS; i++) {
+ if (i != hart_id) {
+ print_digit(i);
+ if (j + 1 < NR_CPUS - 1) {
+ write(", ", 2);
+ }
+ other_harts[j++] = i;
+ }
+ }
+ write("\n", 1);
+
+ /*
+ * ASSERT: all harts in 'other_harts' are marked as stopped by HSM.
+ */
+
+ for (unsigned int i = 0; i < NR_CPUS - 1; i++) {
+ ret = sbi_ecall1(HSM_EXT, HSM_HART_GET_STATUS, other_harts[i]);
+
+ if (ret.error != SBI_SUCCESS) {
+ die("HSM_HART_GET_STATUS: unexpected error from SBI\n");
+ }
+ if (ret.value != HSM_HART_STOPPED) {
+ die("HSM_HART_GET_STATUS: hart that was supposed to be stopped is not!\n");
+ }
+ }
+
+ // Initial state of the lottery.
+ print_lottery();
+ assert_lottery(1, 0);
+ write("\n", 1);
+
+ /*
+ * Wake up the first hart.
+ */
+
+ printk("Waking up first secondary hart\n");
+
+ ret = sbi_ecall3(HSM_EXT, HSM_HART_START, other_harts[0], (uintptr_t)&_start, opaque);
+ if (ret.error != SBI_SUCCESS) {
+ die("HSM_HART_START: unexpected error from SBI\n");
+ }
+
+ SLEEP();
+
+ ret = sbi_ecall1(HSM_EXT, HSM_HART_GET_STATUS, other_harts[0]);
+ if (ret.error != SBI_SUCCESS) {
+ die("HSM_HART_GET_STATUS: unexpected error from SBI\n");
+ }
+ if (ret.value != HSM_HART_STARTED) {
+ die("HSM_HART_GET_STATUS: hart that was supposed to be started is not!\n");
+ }
+
+ // Another hart should have entered the lottery and failed.
+ print_lottery();
+ assert_lottery(2, 1);
+ write("\n", 1);
+
+ /*
+ * Now wake up the rest.
+ */
+
+ printk("Waking up the rest\n");
+
+ for (unsigned int i = 1; i < NR_CPUS - 1; i++) {
+ ret = sbi_ecall3(HSM_EXT, HSM_HART_START, other_harts[i], (uintptr_t)&_start, opaque);
+ if (ret.error != SBI_SUCCESS) {
+ die("HSM_HART_START: unexpected error from SBI\n");
+ }
+ }
+
+ SLEEP();
+
+ // All awake, only one good.
+ print_lottery();
+ assert_lottery(NR_CPUS, NR_CPUS - 1);
+ write("\n", 1);
+
+ /*
+ * Kernels usually don't quit, but let's do it for the heck of it; head.S
+ * will handle it.
+ */
+
+ printk("Goodbye, cruel world!\n");
+ SLEEP();
+}