#define SBI_ETRAP -13
#define SBI_EUNKNOWN -14
#define SBI_ENOENT -15
+#define SBI_EALREADY_STARTED -16
/* clang-format on */
void sbi_hart_delegation_dump(struct sbi_scratch *scratch);
void sbi_hart_pmp_dump(struct sbi_scratch *scratch);
+int sbi_hart_pmp_check_addr(struct sbi_scratch *scratch, unsigned long daddr,
+ unsigned long attr);
void __attribute__((noreturn)) sbi_hart_hang(void);
--- /dev/null
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2020 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Atish Patra <atish.patra@wdc.com>
+ */
+
+#ifndef __SBI_HSM_H__
+#define __SBI_HSM_H__
+
+#include <sbi/sbi_types.h>
+
+/** Hart state values **/
+#define SBI_HART_STOPPED 0
+#define SBI_HART_STOPPING 1
+#define SBI_HART_STARTING 2
+#define SBI_HART_STARTED 3
+
+int sbi_hsm_init(struct sbi_scratch *scratch, u32 hartid, bool cold_boot);
+void __noreturn sbi_hsm_exit(struct sbi_scratch *scratch);
+
+int sbi_hsm_hart_start(struct sbi_scratch *scratch, u32 hartid,
+ ulong saddr, ulong priv);
+int sbi_hsm_hart_stop(struct sbi_scratch *scratch, bool exitnow);
+int sbi_hsm_hart_get_state(struct sbi_scratch *scratch, u32 hartid);
+bool sbi_hsm_hart_started(struct sbi_scratch *scratch, u32 hartid);
+void sbi_hsm_prepare_next_jump(struct sbi_scratch *scratch, u32 hartid);
+#endif
/** Exit platform timer for current HART */
void (*timer_exit)(void);
+ /** Bringup the given hart from previous stage **/
+ int (*hart_start)(u32 hartid, ulong saddr, ulong priv);
+ /**
+ * Stop the current hart from running. This call doesn't expect to
+ * return if success.
+ */
+ int (*hart_stop)(void);
+
/** Reboot the platform */
int (*system_reboot)(u32 type);
/** Shutdown or poweroff the platform */
}
/**
+ * Bringup a given hart from previous stage. Platform should implement this
+ * operation if they support a custom mechanism to start a hart. Otherwise,
+ * a generic WFI based approach will be used to start/stop a hart in OpenSBI.
+ *
+ * @param plat pointer to struct sbi_platform
+ * @param hartid Hart ID
+ * @param saddr Physical address in supervisor mode for hart to jump after
+ * OpenSBI
+ * @param priv A private context data from the caller
+ *
+ * @return 0 if sucessful and negative error code on failure
+ */
+static inline int sbi_platform_hart_start(const struct sbi_platform *plat,
+ u32 hartid, ulong saddr, ulong priv)
+{
+ if (plat && sbi_platform_ops(plat)->hart_start)
+ return sbi_platform_ops(plat)->hart_start(hartid, saddr, priv);
+ return SBI_ENOTSUPP;
+}
+
+/**
+ * Stop the current hart in OpenSBI.
+ *
+ * @param plat pointer to struct sbi_platform
+ *
+ * @return Negative error code on failure. It doesn't return on success.
+ */
+static inline int sbi_platform_hart_stop(const struct sbi_platform *plat)
+{
+ if (plat && sbi_platform_ops(plat)->hart_stop)
+ return sbi_platform_ops(plat)->hart_stop();
+ return SBI_ENOTSUPP;
+}
+
+/**
* Early initialization for current HART
*
* @param plat pointer to struct sbi_platform
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
}
}
+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;
--- /dev/null
+/*
+ * 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;
+}
#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>
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();
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);
}
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();
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);
sbi_platform_final_exit(plat);
- sbi_hart_hang();
+ sbi_hsm_exit(scratch);
}
#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>
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 = {
*/
#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>
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);
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);