summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/sbi/objects.mk1
-rw-r--r--lib/sbi/sbi_hart.c25
-rw-r--r--lib/sbi/sbi_hsm.c222
-rw-r--r--lib/sbi/sbi_init.c13
-rw-r--r--lib/sbi/sbi_ipi.c3
-rw-r--r--lib/sbi/sbi_system.c5
6 files changed, 267 insertions, 2 deletions
diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk
index fac980f..dc52a9f 100644
--- a/lib/sbi/objects.mk
+++ b/lib/sbi/objects.mk
@@ -22,6 +22,7 @@ libsbi-objs-y += sbi_emulate_csr.o
libsbi-objs-y += sbi_fifo.o
libsbi-objs-y += sbi_hfence.o
libsbi-objs-y += sbi_hart.o
+libsbi-objs-y += sbi_hsm.o
libsbi-objs-y += sbi_illegal_insn.o
libsbi-objs-y += sbi_init.o
libsbi-objs-y += sbi_ipi.o
diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c
index 1d62862..3de46a2 100644
--- a/lib/sbi/sbi_hart.c
+++ b/lib/sbi/sbi_hart.c
@@ -166,6 +166,31 @@ void sbi_hart_pmp_dump(struct sbi_scratch *scratch)
}
}
+int sbi_hart_pmp_check_addr(struct sbi_scratch *scratch, unsigned long addr,
+ unsigned long attr)
+{
+ unsigned long prot, size, l2l, i, tempaddr;
+ const struct sbi_platform *plat = sbi_platform_ptr(scratch);
+
+ if (!sbi_platform_has_pmp(plat))
+ return SBI_OK;
+
+ for (i = 0; i < PMP_COUNT; i++) {
+ pmp_get(i, &prot, &tempaddr, &l2l);
+ if (!(prot & PMP_A))
+ continue;
+ if (l2l < __riscv_xlen)
+ size = (1UL << l2l);
+ else
+ size = 0;
+ if (tempaddr <= addr && addr <= tempaddr + size)
+ if (!(prot & attr))
+ return SBI_INVALID_ADDR;
+ }
+
+ return SBI_OK;
+}
+
static int pmp_init(struct sbi_scratch *scratch, u32 hartid)
{
u32 i, count;
diff --git a/lib/sbi/sbi_hsm.c b/lib/sbi/sbi_hsm.c
new file mode 100644
index 0000000..f3207ee
--- /dev/null
+++ b/lib/sbi/sbi_hsm.c
@@ -0,0 +1,222 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Atish Patra <atish.patra@wdc.com>
+ */
+
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_barrier.h>
+#include <sbi/riscv_encoding.h>
+#include <sbi/riscv_atomic.h>
+#include <sbi/sbi_bits.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_hsm.h>
+#include <sbi/sbi_init.h>
+#include <sbi/sbi_ipi.h>
+#include <sbi/sbi_platform.h>
+#include <sbi/sbi_system.h>
+#include <sbi/sbi_timer.h>
+#include <sbi/sbi_console.h>
+
+static unsigned long hart_data_offset;
+
+/** Per hart specific data to manage state transition **/
+struct sbi_hsm_data {
+ atomic_t state;
+};
+
+int sbi_hsm_hart_get_state(struct sbi_scratch *scratch, u32 hartid)
+{
+ struct sbi_hsm_data *hdata;
+ u32 hstate;
+
+ if (hartid != sbi_current_hartid())
+ scratch = sbi_hart_id_to_scratch(scratch, hartid);
+
+ hdata = sbi_scratch_offset_ptr(scratch, hart_data_offset);
+ hstate = atomic_read(&hdata->state);
+
+ return hstate;
+}
+
+int sbi_hsm_hart_started(struct sbi_scratch *scratch, u32 hartid)
+{
+
+ if (sbi_hsm_hart_get_state(scratch, hartid) == SBI_HART_STARTED)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+void sbi_hsm_prepare_next_jump(struct sbi_scratch *scratch, u32 hartid)
+{
+ u32 oldstate;
+ struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch,
+ hart_data_offset);
+
+ oldstate = arch_atomic_cmpxchg(&hdata->state, SBI_HART_STARTING,
+ SBI_HART_STARTED);
+ if (oldstate != SBI_HART_STARTING)
+ sbi_hart_hang();
+}
+
+static void sbi_hsm_hart_wait(struct sbi_scratch *scratch, u32 hartid)
+{
+ unsigned long saved_mie;
+ const struct sbi_platform *plat = sbi_platform_ptr(scratch);
+ struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch,
+ hart_data_offset);
+ /* Save MIE CSR */
+ saved_mie = csr_read(CSR_MIE);
+
+ /* Set MSIE bit to receive IPI */
+ csr_set(CSR_MIE, MIP_MSIP);
+
+ /* Wait for hart_add call*/
+ while (atomic_read(&hdata->state) != SBI_HART_STARTING) {
+ wfi();
+ };
+
+ /* Restore MIE CSR */
+ csr_write(CSR_MIE, saved_mie);
+
+ /* Clear current HART IPI */
+ sbi_platform_ipi_clear(plat, hartid);
+}
+
+int sbi_hsm_init(struct sbi_scratch *scratch, u32 hartid, bool cold_boot)
+{
+ struct sbi_scratch *rscratch;
+ struct sbi_hsm_data *hdata;
+ u32 hart_count, i;
+ const struct sbi_platform *plat = sbi_platform_ptr(scratch);
+
+ if (cold_boot) {
+ hart_data_offset = sbi_scratch_alloc_offset(sizeof(*hdata),
+ "HART_DATA");
+ if (!hart_data_offset)
+ return SBI_ENOMEM;
+ hart_count = sbi_platform_hart_count(plat);
+
+ /* Initialize hart state data for every hart */
+ for (i = 0; i < hart_count; i++) {
+ rscratch = sbi_hart_id_to_scratch(scratch, i);
+ hdata = sbi_scratch_offset_ptr(rscratch,
+ hart_data_offset);
+ ATOMIC_INIT(&hdata->state,
+ (i == hartid) ? SBI_HART_STARTING : SBI_HART_STOPPED);
+ }
+ } else {
+ sbi_hsm_hart_wait(scratch, hartid);
+ }
+
+ return 0;
+}
+
+void __noreturn sbi_hsm_exit(struct sbi_scratch *scratch)
+{
+ u32 hstate;
+ const struct sbi_platform *plat = sbi_platform_ptr(scratch);
+ struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch,
+ hart_data_offset);
+ void (*jump_warmboot)(void) = (void (*)(void))scratch->warmboot_addr;
+
+ hstate = arch_atomic_cmpxchg(&hdata->state, SBI_HART_STOPPING,
+ SBI_HART_STOPPED);
+ if (hstate != SBI_HART_STOPPING)
+ goto fail_exit;
+
+ if (sbi_platform_has_hart_hotplug(plat)) {
+ sbi_platform_hart_stop(plat);
+ /* It should never reach here */
+ goto fail_exit;
+ }
+
+ /**
+ * As platform is lacking support for hotplug, directly jump to warmboot
+ * and wait for interrupts in warmboot. We do it preemptively in order
+ * preserve the hart states and reuse the code path for hotplug.
+ */
+ jump_warmboot();
+
+fail_exit:
+ /* It should never reach here */
+ sbi_printf("ERR: Failed stop hart [%u]\n", sbi_current_hartid());
+ sbi_hart_hang();
+}
+
+int sbi_hsm_hart_start(struct sbi_scratch *scratch, u32 hartid,
+ ulong saddr, ulong priv)
+{
+ unsigned long init_count;
+ unsigned int hstate;
+ int rc;
+ const struct sbi_platform *plat = sbi_platform_ptr(scratch);
+ struct sbi_scratch *rscratch = sbi_hart_id_to_scratch(scratch, hartid);
+ struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(rscratch,
+ hart_data_offset);
+
+ if (sbi_platform_hart_disabled(plat, hartid))
+ return SBI_EINVAL;
+ hstate = arch_atomic_cmpxchg(&hdata->state, SBI_HART_STOPPED,
+ SBI_HART_STARTING);
+ if (hstate == SBI_HART_STARTED)
+ return SBI_EALREADY_STARTED;
+
+ /**
+ * if a hart is already transition to start or stop, another start call
+ * is considered as invalid request.
+ */
+ if (hstate != SBI_HART_STOPPED)
+ return SBI_EINVAL;
+
+ rc = sbi_hart_pmp_check_addr(scratch, saddr, PMP_X);
+ if (rc)
+ return rc;
+ //TODO: We also need to check saddr for valid physical address as well.
+
+ init_count = sbi_init_count(hartid);
+ rscratch->next_arg1 = priv;
+ rscratch->next_addr = saddr;
+
+ if (sbi_platform_has_hart_hotplug(plat) ||
+ (sbi_platform_has_hart_secondary_boot(plat) && !init_count)) {
+ return sbi_platform_hart_start(plat, hartid,
+ scratch->warmboot_addr, priv);
+ } else {
+ sbi_platform_ipi_send(plat, hartid);
+ }
+
+ return 0;
+}
+
+int sbi_hsm_hart_stop(struct sbi_scratch *scratch, bool exitnow)
+{
+ int oldstate;
+ u32 hartid = sbi_current_hartid();
+ const struct sbi_platform *plat = sbi_platform_ptr(scratch);
+ struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch,
+ hart_data_offset);
+
+ if (sbi_platform_hart_disabled(plat, hartid) ||
+ !sbi_hsm_hart_started(scratch, hartid))
+ return SBI_EINVAL;
+
+ oldstate = arch_atomic_cmpxchg(&hdata->state, SBI_HART_STARTED,
+ SBI_HART_STOPPING);
+ if (oldstate != SBI_HART_STARTED) {
+ sbi_printf("%s: ERR: The hart is in invalid state [%u]\n",
+ __func__, oldstate);
+ return SBI_DENIED;
+ }
+
+ if (exitnow)
+ sbi_exit(scratch);
+
+ return 0;
+}
diff --git a/lib/sbi/sbi_init.c b/lib/sbi/sbi_init.c
index 48bc653..1f113b9 100644
--- a/lib/sbi/sbi_init.c
+++ b/lib/sbi/sbi_init.c
@@ -12,6 +12,7 @@
#include <sbi/sbi_console.h>
#include <sbi/sbi_ecall.h>
#include <sbi/sbi_hart.h>
+#include <sbi/sbi_hsm.h>
#include <sbi/sbi_ipi.h>
#include <sbi/sbi_platform.h>
#include <sbi/sbi_system.h>
@@ -85,6 +86,10 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
if (!init_count_offset)
sbi_hart_hang();
+ rc = sbi_hsm_init(scratch, hartid, TRUE);
+ if (rc)
+ sbi_hart_hang();
+
rc = sbi_system_early_init(scratch, TRUE);
if (rc)
sbi_hart_hang();
@@ -131,6 +136,7 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
init_count = sbi_scratch_offset_ptr(scratch, init_count_offset);
(*init_count)++;
+ sbi_hsm_prepare_next_jump(scratch, hartid);
sbi_hart_switch_mode(hartid, scratch->next_arg1, scratch->next_addr,
scratch->next_mode, FALSE);
}
@@ -146,6 +152,10 @@ static void __noreturn init_warmboot(struct sbi_scratch *scratch, u32 hartid)
if (!init_count_offset)
sbi_hart_hang();
+ rc = sbi_hsm_init(scratch, hartid, FALSE);
+ if (rc)
+ sbi_hart_hang();
+
rc = sbi_system_early_init(scratch, FALSE);
if (rc)
sbi_hart_hang();
@@ -179,6 +189,7 @@ static void __noreturn init_warmboot(struct sbi_scratch *scratch, u32 hartid)
init_count = sbi_scratch_offset_ptr(scratch, init_count_offset);
(*init_count)++;
+ sbi_hsm_prepare_next_jump(scratch, hartid);
sbi_hart_switch_mode(hartid, scratch->next_arg1,
scratch->next_addr,
scratch->next_mode, FALSE);
@@ -260,5 +271,5 @@ void __noreturn sbi_exit(struct sbi_scratch *scratch)
sbi_platform_final_exit(plat);
- sbi_hart_hang();
+ sbi_hsm_exit(scratch);
}
diff --git a/lib/sbi/sbi_ipi.c b/lib/sbi/sbi_ipi.c
index d3b48fe..006844b 100644
--- a/lib/sbi/sbi_ipi.c
+++ b/lib/sbi/sbi_ipi.c
@@ -14,6 +14,7 @@
#include <sbi/sbi_bitops.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_hart.h>
+#include <sbi/sbi_hsm.h>
#include <sbi/sbi_init.h>
#include <sbi/sbi_ipi.h>
#include <sbi/sbi_platform.h>
@@ -153,7 +154,7 @@ void sbi_ipi_clear_smode(struct sbi_scratch *scratch)
static void sbi_ipi_process_halt(struct sbi_scratch *scratch)
{
- sbi_exit(scratch);
+ sbi_hsm_hart_stop(scratch, TRUE);
}
static struct sbi_ipi_event_ops ipi_halt_ops = {
diff --git a/lib/sbi/sbi_system.c b/lib/sbi/sbi_system.c
index 0fd4999..c6aa36c 100644
--- a/lib/sbi/sbi_system.c
+++ b/lib/sbi/sbi_system.c
@@ -9,6 +9,7 @@
*/
#include <sbi/sbi_hart.h>
+#include <sbi/sbi_hsm.h>
#include <sbi/sbi_platform.h>
#include <sbi/sbi_system.h>
#include <sbi/sbi_ipi.h>
@@ -42,6 +43,8 @@ void __noreturn sbi_system_reboot(struct sbi_scratch *scratch, u32 type)
sbi_ipi_send_halt(scratch,
sbi_hart_available_mask() & ~current_hartid_mask, 0);
+ sbi_hsm_hart_stop(scratch, FALSE);
+
/* Platform specific reooot */
sbi_platform_system_reboot(sbi_platform_ptr(scratch), type);
@@ -57,6 +60,8 @@ void __noreturn sbi_system_shutdown(struct sbi_scratch *scratch, u32 type)
sbi_ipi_send_halt(scratch,
sbi_hart_available_mask() & ~current_hartid_mask, 0);
+ sbi_hsm_hart_stop(scratch, FALSE);
+
/* Platform specific shutdown */
sbi_platform_system_shutdown(sbi_platform_ptr(scratch), type);