summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/objects.mk23
-rw-r--r--lib/riscv_asm.c275
-rw-r--r--lib/riscv_atomic.c152
-rw-r--r--lib/riscv_locks.c46
-rw-r--r--lib/sbi_console.c367
-rw-r--r--lib/sbi_ecall.c98
-rw-r--r--lib/sbi_emulate_csr.c157
-rw-r--r--lib/sbi_hart.c294
-rw-r--r--lib/sbi_illegal_insn.c190
-rw-r--r--lib/sbi_init.c162
-rw-r--r--lib/sbi_ipi.c82
-rw-r--r--lib/sbi_system.c47
-rw-r--r--lib/sbi_timer.c78
-rw-r--r--lib/sbi_trap.c110
14 files changed, 2081 insertions, 0 deletions
diff --git a/lib/objects.mk b/lib/objects.mk
new file mode 100644
index 0000000..ed14677
--- /dev/null
+++ b/lib/objects.mk
@@ -0,0 +1,23 @@
+#
+# Copyright (c) 2018 Western Digital Corporation or its affiliates.
+#
+# Authors:
+# Anup Patel <anup.patel@wdc.com>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+lib-objs-y += riscv_asm.o
+lib-objs-y += riscv_atomic.o
+lib-objs-y += riscv_locks.o
+
+lib-objs-y += sbi_console.o
+lib-objs-y += sbi_ecall.o
+lib-objs-y += sbi_emulate_csr.o
+lib-objs-y += sbi_hart.o
+lib-objs-y += sbi_illegal_insn.o
+lib-objs-y += sbi_init.o
+lib-objs-y += sbi_ipi.o
+lib-objs-y += sbi_system.o
+lib-objs-y += sbi_timer.o
+lib-objs-y += sbi_trap.o
diff --git a/lib/riscv_asm.c b/lib/riscv_asm.c
new file mode 100644
index 0000000..0fbdb31
--- /dev/null
+++ b/lib/riscv_asm.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2018 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel <anup.patel@wdc.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_encoding.h>
+#include <sbi/sbi_error.h>
+
+unsigned long csr_read_num(int csr_num)
+{
+ unsigned long ret = 0;
+
+ switch (csr_num) {
+ case CSR_PMPCFG0:
+ ret = csr_read_n(CSR_PMPCFG0);
+ break;
+ case CSR_PMPCFG1:
+ ret = csr_read_n(CSR_PMPCFG1);
+ break;
+ case CSR_PMPCFG2:
+ ret = csr_read_n(CSR_PMPCFG2);
+ break;
+ case CSR_PMPCFG3:
+ ret = csr_read_n(CSR_PMPCFG3);
+ break;
+ case CSR_PMPADDR0:
+ ret = csr_read_n(CSR_PMPADDR0);
+ break;
+ case CSR_PMPADDR1:
+ ret = csr_read_n(CSR_PMPADDR1);
+ break;
+ case CSR_PMPADDR2:
+ ret = csr_read_n(CSR_PMPADDR2);
+ break;
+ case CSR_PMPADDR3:
+ ret = csr_read_n(CSR_PMPADDR3);
+ break;
+ case CSR_PMPADDR4:
+ ret = csr_read_n(CSR_PMPADDR4);
+ break;
+ case CSR_PMPADDR5:
+ ret = csr_read_n(CSR_PMPADDR5);
+ break;
+ case CSR_PMPADDR6:
+ ret = csr_read_n(CSR_PMPADDR6);
+ break;
+ case CSR_PMPADDR7:
+ ret = csr_read_n(CSR_PMPADDR7);
+ break;
+ case CSR_PMPADDR8:
+ ret = csr_read_n(CSR_PMPADDR8);
+ break;
+ case CSR_PMPADDR9:
+ ret = csr_read_n(CSR_PMPADDR9);
+ break;
+ case CSR_PMPADDR10:
+ ret = csr_read_n(CSR_PMPADDR10);
+ break;
+ case CSR_PMPADDR11:
+ ret = csr_read_n(CSR_PMPADDR11);
+ break;
+ case CSR_PMPADDR12:
+ ret = csr_read_n(CSR_PMPADDR12);
+ break;
+ case CSR_PMPADDR13:
+ ret = csr_read_n(CSR_PMPADDR13);
+ break;
+ case CSR_PMPADDR14:
+ ret = csr_read_n(CSR_PMPADDR14);
+ break;
+ case CSR_PMPADDR15:
+ ret = csr_read_n(CSR_PMPADDR15);
+ break;
+ default:
+ break;
+ };
+
+ return ret;
+}
+
+void csr_write_num(int csr_num, unsigned long val)
+{
+ switch (csr_num) {
+ case CSR_PMPCFG0:
+ csr_write_n(CSR_PMPCFG0, val);
+ break;
+ case CSR_PMPCFG1:
+ csr_write_n(CSR_PMPCFG1, val);
+ break;
+ case CSR_PMPCFG2:
+ csr_write_n(CSR_PMPCFG2, val);
+ break;
+ case CSR_PMPCFG3:
+ csr_write_n(CSR_PMPCFG3, val);
+ break;
+ case CSR_PMPADDR0:
+ csr_write_n(CSR_PMPADDR0, val);
+ break;
+ case CSR_PMPADDR1:
+ csr_write_n(CSR_PMPADDR1, val);
+ break;
+ case CSR_PMPADDR2:
+ csr_write_n(CSR_PMPADDR2, val);
+ break;
+ case CSR_PMPADDR3:
+ csr_write_n(CSR_PMPADDR3, val);
+ break;
+ case CSR_PMPADDR4:
+ csr_write_n(CSR_PMPADDR4, val);
+ break;
+ case CSR_PMPADDR5:
+ csr_write_n(CSR_PMPADDR5, val);
+ break;
+ case CSR_PMPADDR6:
+ csr_write_n(CSR_PMPADDR6, val);
+ break;
+ case CSR_PMPADDR7:
+ csr_write_n(CSR_PMPADDR7, val);
+ break;
+ case CSR_PMPADDR8:
+ csr_write_n(CSR_PMPADDR8, val);
+ break;
+ case CSR_PMPADDR9:
+ csr_write_n(CSR_PMPADDR9, val);
+ break;
+ case CSR_PMPADDR10:
+ csr_write_n(CSR_PMPADDR10, val);
+ break;
+ case CSR_PMPADDR11:
+ csr_write_n(CSR_PMPADDR11, val);
+ break;
+ case CSR_PMPADDR12:
+ csr_write_n(CSR_PMPADDR12, val);
+ break;
+ case CSR_PMPADDR13:
+ csr_write_n(CSR_PMPADDR13, val);
+ break;
+ case CSR_PMPADDR14:
+ csr_write_n(CSR_PMPADDR14, val);
+ break;
+ case CSR_PMPADDR15:
+ csr_write_n(CSR_PMPADDR15, val);
+ break;
+ default:
+ break;
+ };
+}
+
+static unsigned long ctz(unsigned long x)
+{
+ unsigned long ret = 0;
+
+ while (!(x & 1UL)) {
+ ret++;
+ x = x >> 1;
+ }
+
+ return ret;
+}
+
+int pmp_set(unsigned int n, unsigned long prot,
+ unsigned long addr, unsigned long log2len)
+{
+ int pmpcfg_csr, pmpcfg_shift, pmpaddr_csr;
+ unsigned long cfgmask, pmpcfg;
+ unsigned long addrmask, pmpaddr;
+
+ /* check parameters */
+ if (n >= PMP_COUNT ||
+ log2len > __riscv_xlen ||
+ log2len < PMP_SHIFT)
+ return SBI_EINVAL;
+
+ /* calculate PMP register and offset */
+#if __riscv_xlen == 32
+ pmpcfg_csr = CSR_PMPCFG0 + (n >> 2);
+ pmpcfg_shift = (n & 3) << 3;
+#elif __riscv_xlen == 64
+ pmpcfg_csr = (CSR_PMPCFG0 + (n >> 2)) & ~1;
+ pmpcfg_shift = (n & 7) << 3;
+#else
+ pmpcfg_csr = -1;
+ pmpcfg_shift = -1;
+#endif
+ pmpaddr_csr = CSR_PMPADDR0 + n;
+ if (pmpcfg_csr < 0 || pmpcfg_shift < 0)
+ return SBI_ENOTSUPP;
+
+ /* encode PMP config */
+ prot |= (log2len == PMP_SHIFT) ? PMP_A_NA4 : PMP_A_NAPOT;
+ cfgmask = ~(0xff << pmpcfg_shift);
+ pmpcfg = (csr_read_num(pmpcfg_csr) & cfgmask);
+ pmpcfg |= ((prot << pmpcfg_shift) & ~cfgmask);
+
+ /* encode PMP address */
+ if (log2len == PMP_SHIFT) {
+ pmpaddr = (addr >> PMP_SHIFT);
+ } else {
+ if (log2len == __riscv_xlen) {
+ pmpaddr = -1UL;
+ } else {
+ addrmask = (1UL << (log2len - PMP_SHIFT)) - 1;
+ pmpaddr = ((addr >> PMP_SHIFT) & ~addrmask);
+ pmpaddr |= (addrmask >> 1);
+ }
+ }
+
+ /* write csrs */
+ csr_write_num(pmpaddr_csr, pmpaddr);
+ csr_write_num(pmpcfg_csr, pmpcfg);
+
+ return 0;
+}
+
+int pmp_get(unsigned int n, unsigned long *prot_out,
+ unsigned long *addr_out, unsigned long *log2len_out)
+{
+ int pmpcfg_csr, pmpcfg_shift, pmpaddr_csr;
+ unsigned long cfgmask, pmpcfg, prot;
+ unsigned long t1, addr, log2len;
+
+ /* check parameters */
+ if (n >= PMP_COUNT || !prot_out ||
+ !addr_out || !log2len_out)
+ return SBI_EINVAL;
+ *prot_out = *addr_out = *log2len_out = 0;
+
+ /* calculate PMP register and offset */
+#if __riscv_xlen == 32
+ pmpcfg_csr = CSR_PMPCFG0 + (n >> 2);
+ pmpcfg_shift = (n & 3) << 3;
+#elif __riscv_xlen == 64
+ pmpcfg_csr = (CSR_PMPCFG0 + (n >> 2)) & ~1;
+ pmpcfg_shift = (n & 7) << 3;
+#else
+ pmpcfg_csr = -1;
+ pmpcfg_shift = -1;
+#endif
+ pmpaddr_csr = CSR_PMPADDR0 + n;
+ if (pmpcfg_csr < 0 || pmpcfg_shift < 0)
+ return SBI_ENOTSUPP;
+
+ /* decode PMP config */
+ cfgmask = (0xff << pmpcfg_shift);
+ pmpcfg = csr_read_num(pmpcfg_csr) & cfgmask;
+ prot = pmpcfg >> pmpcfg_shift;
+
+ /* decode PMP address */
+ if ((prot & PMP_A) == PMP_A_NAPOT) {
+ addr = csr_read_num(pmpaddr_csr);
+ if (addr == -1UL) {
+ addr = 0;
+ log2len = __riscv_xlen;
+ } else {
+ t1 = ctz(~addr);
+ addr = (addr & ~((1UL << t1) - 1)) << PMP_SHIFT;
+ log2len = (t1 + PMP_SHIFT + 1);
+ }
+ } else {
+ addr = csr_read_num(pmpaddr_csr) << PMP_SHIFT;
+ log2len = PMP_SHIFT;
+ }
+
+ /* return details */
+ *prot_out = prot;
+ *addr_out = addr;
+ *log2len_out = log2len;
+
+ return 0;
+}
diff --git a/lib/riscv_atomic.c b/lib/riscv_atomic.c
new file mode 100644
index 0000000..3a599f5
--- /dev/null
+++ b/lib/riscv_atomic.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2018 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel <anup.patel@wdc.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sbi/sbi_types.h>
+#include <sbi/riscv_atomic.h>
+#include <sbi/riscv_barrier.h>
+
+long atomic_read(atomic_t *atom)
+{
+ long ret = atom->counter;
+ rmb();
+ return ret;
+}
+
+void atomic_write(atomic_t *atom, long value)
+{
+ atom->counter = value;
+ wmb();
+}
+
+long atomic_add_return(atomic_t *atom, long value)
+{
+ long ret;
+
+ __asm__ __volatile__ (
+ " amoadd.w.aqrl %1, %2, %0"
+ : "+A" (atom->counter), "=r" (ret)
+ : "r" (value)
+ : "memory");
+
+ return ret + value;
+}
+
+long atomic_sub_return(atomic_t *atom, long value)
+{
+ long ret;
+
+ __asm__ __volatile__ (
+ " amoadd.w.aqrl %1, %2, %0"
+ : "+A" (atom->counter), "=r" (ret)
+ : "r" (-value)
+ : "memory");
+
+ return ret - value;
+}
+
+#define __xchg(ptr, new, size) \
+({ \
+ __typeof__(ptr) __ptr = (ptr); \
+ __typeof__(*(ptr)) __new = (new); \
+ __typeof__(*(ptr)) __ret; \
+ register unsigned int __rc; \
+ switch (size) { \
+ case 4: \
+ __asm__ __volatile__ ( \
+ "0: lr.w %0, %2\n" \
+ " sc.w.rl %1, %z3, %2\n" \
+ " bnez %1, 0b\n" \
+ " fence rw, rw\n" \
+ : "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
+ : "rJ" (__new) \
+ : "memory"); \
+ break; \
+ case 8: \
+ __asm__ __volatile__ ( \
+ "0: lr.d %0, %2\n" \
+ " sc.d.rl %1, %z3, %2\n" \
+ " bnez %1, 0b\n" \
+ " fence rw, rw\n" \
+ : "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
+ : "rJ" (__new) \
+ : "memory"); \
+ break; \
+ default: \
+ break; \
+ } \
+ __ret; \
+})
+
+#define xchg(ptr, n) \
+({ \
+ __typeof__(*(ptr)) _n_ = (n); \
+ (__typeof__(*(ptr))) __xchg((ptr), _n_, sizeof(*(ptr))); \
+})
+
+#define __cmpxchg(ptr, old, new, size) \
+({ \
+ __typeof__(ptr) __ptr = (ptr); \
+ __typeof__(*(ptr)) __old = (old); \
+ __typeof__(*(ptr)) __new = (new); \
+ __typeof__(*(ptr)) __ret; \
+ register unsigned int __rc; \
+ switch (size) { \
+ case 4: \
+ __asm__ __volatile__ ( \
+ "0: lr.w %0, %2\n" \
+ " bne %0, %z3, 1f\n" \
+ " sc.w.rl %1, %z4, %2\n" \
+ " bnez %1, 0b\n" \
+ " fence rw, rw\n" \
+ "1:\n" \
+ : "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
+ : "rJ" (__old), "rJ" (__new) \
+ : "memory"); \
+ break; \
+ case 8: \
+ __asm__ __volatile__ ( \
+ "0: lr.d %0, %2\n" \
+ " bne %0, %z3, 1f\n" \
+ " sc.d.rl %1, %z4, %2\n" \
+ " bnez %1, 0b\n" \
+ " fence rw, rw\n" \
+ "1:\n" \
+ : "=&r" (__ret), "=&r" (__rc), "+A" (*__ptr) \
+ : "rJ" (__old), "rJ" (__new) \
+ : "memory"); \
+ break; \
+ default: \
+ break; \
+ } \
+ __ret; \
+})
+
+#define cmpxchg(ptr, o, n) \
+({ \
+ __typeof__(*(ptr)) _o_ = (o); \
+ __typeof__(*(ptr)) _n_ = (n); \
+ (__typeof__(*(ptr))) __cmpxchg((ptr), \
+ _o_, _n_, sizeof(*(ptr))); \
+})
+
+long arch_atomic_cmpxchg(atomic_t *atom, long oldval, long newval)
+{
+ return cmpxchg(&atom->counter, oldval, newval);
+}
+
+long arch_atomic_xchg(atomic_t *atom, long newval)
+{
+ return xchg(&atom->counter, newval);
+}
+
+unsigned int atomic_raw_xchg_uint(volatile unsigned int *ptr,
+ unsigned int newval)
+{
+ return xchg(ptr, newval);
+}
diff --git a/lib/riscv_locks.c b/lib/riscv_locks.c
new file mode 100644
index 0000000..0f94986
--- /dev/null
+++ b/lib/riscv_locks.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel <anup.patel@wdc.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sbi/riscv_barrier.h>
+#include <sbi/riscv_locks.h>
+
+int spin_lock_check(spinlock_t *lock)
+{
+ return (lock->lock == __RISCV_SPIN_UNLOCKED) ? 0 : 1;
+}
+
+int spin_trylock(spinlock_t *lock)
+{
+ int tmp = 1, busy;
+
+ __asm__ __volatile__ (
+ " amoswap.w %0, %2, %1\n"
+ RISCV_ACQUIRE_BARRIER
+ : "=r" (busy), "+A" (lock->lock)
+ : "r" (tmp)
+ : "memory");
+
+ return !busy;
+}
+
+void spin_lock(spinlock_t *lock)
+{
+ while (1) {
+ if (spin_lock_check(lock))
+ continue;
+
+ if (spin_trylock(lock))
+ break;
+ }
+}
+
+void spin_unlock(spinlock_t *lock)
+{
+ __smp_store_release(&lock->lock, __RISCV_SPIN_UNLOCKED);
+}
diff --git a/lib/sbi_console.c b/lib/sbi_console.c
new file mode 100644
index 0000000..24b9d3c
--- /dev/null
+++ b/lib/sbi_console.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2018 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel <anup.patel@wdc.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sbi/sbi_platform.h>
+#include <sbi/sbi_console.h>
+
+static struct sbi_platform *console_plat = NULL;
+
+bool sbi_isprintable(char c)
+{
+ if (((31 < c) && (c < 127)) ||
+ (c == '\f') ||
+ (c == '\r') ||
+ (c == '\n') ||
+ (c == '\t')) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+char sbi_getc(void)
+{
+ return sbi_platform_console_getc(console_plat);
+}
+
+void sbi_putc(char ch)
+{
+ sbi_platform_console_putc(console_plat, ch);
+}
+
+void sbi_puts(const char *str)
+{
+ while (*str) {
+ sbi_putc(*str);
+ str++;
+ }
+}
+
+void sbi_gets(char *s, int maxwidth, char endchar)
+{
+ char *retval;
+ char ch;
+ retval = s;
+ ch = sbi_getc();
+ while (ch != endchar && maxwidth > 0) {
+ *retval = ch;
+ retval++;
+ maxwidth--;
+ if (maxwidth == 0)
+ break;
+ ch = sbi_getc();
+ }
+ *retval = '\0';
+ return;
+}
+
+#define PAD_RIGHT 1
+#define PAD_ZERO 2
+#define PAD_ALTERNATE 4
+#define PRINT_BUF_LEN 64
+
+#define va_start(v,l) __builtin_va_start((v),l)
+#define va_end __builtin_va_end
+#define va_arg __builtin_va_arg
+typedef __builtin_va_list va_list;
+
+static void printc(char **out, u32 *out_len, char ch)
+{
+ if (out) {
+ if (*out) {
+ if (out_len && (0 < *out_len)) {
+ **out = ch;
+ ++(*out);
+ (*out_len)--;
+ } else {
+ **out = ch;
+ ++(*out);
+ }
+ }
+ } else {
+ sbi_putc(ch);
+ }
+}
+
+static int prints(char **out, u32 *out_len, const char *string, int width, int flags)
+{
+ int pc = 0;
+ char padchar = ' ';
+
+ if (width > 0) {
+ int len = 0;
+ const char *ptr;
+ for (ptr = string; *ptr; ++ptr)
+ ++len;
+ if (len >= width)
+ width = 0;
+ else
+ width -= len;
+ if (flags & PAD_ZERO)
+ padchar = '0';
+ }
+ if (!(flags & PAD_RIGHT)) {
+ for (; width > 0; --width) {
+ printc(out, out_len, padchar);
+ ++pc;
+ }
+ }
+ for (; *string; ++string) {
+ printc(out, out_len, *string);
+ ++pc;
+ }
+ for (; width > 0; --width) {
+ printc(out, out_len, padchar);
+ ++pc;
+ }
+
+ return pc;
+}
+
+static int printi(char **out, u32 *out_len, long long i, int b, int sg,
+ int width, int flags, int letbase)
+{
+ char print_buf[PRINT_BUF_LEN];
+ char *s;
+ int neg = 0, pc = 0;
+ u64 t;
+ unsigned long long u = i;
+
+ if (sg && b == 10 && i < 0) {
+ neg = 1;
+ u = -i;
+ }
+
+ s = print_buf + PRINT_BUF_LEN - 1;
+ *s = '\0';
+
+ if (!u) {
+ *--s = '0';
+ } else {
+ while (u) {
+ t = u % b;
+ u = u / b;
+ if (t >= 10)
+ t += letbase - '0' - 10;
+ *--s = t + '0';
+ }
+ }
+
+ if (flags & PAD_ALTERNATE) {
+ if ((b == 16) && (letbase == 'A')) {
+ *--s = 'X';
+ } else if ((b == 16) && (letbase == 'a')) {
+ *--s = 'x';
+ }
+ *--s = '0';
+ }
+
+ if (neg) {
+ if (width && (flags & PAD_ZERO)) {
+ printc(out, out_len, '-');
+ ++pc;
+ --width;
+ } else {
+ *--s = '-';
+ }
+ }
+
+ return pc + prints(out, out_len, s, width, flags);
+}
+
+static int print(char **out, u32 *out_len, const char *format, va_list args)
+{
+ int width, flags, acnt = 0;
+ int pc = 0;
+ char scr[2];
+ unsigned long long tmp;
+
+ for (; *format != 0; ++format) {
+ if (*format == '%') {
+ ++format;
+ width = flags = 0;
+ if (*format == '\0')
+ break;
+ if (*format == '%')
+ goto out;
+ /* Get flags */
+ if (*format == '-') {
+ ++format;
+ flags = PAD_RIGHT;
+ }
+ if (*format == '#') {
+ ++format;
+ flags |= PAD_ALTERNATE;
+ }
+ while (*format == '0') {
+ ++format;
+ flags |= PAD_ZERO;
+ }
+ /* Get width */
+ for (; *format >= '0' && *format <= '9'; ++format) {
+ width *= 10;
+ width += *format - '0';
+ }
+ if (*format == 's') {
+ char *s = va_arg(args, char *);
+ acnt += sizeof(char *);
+ pc += prints(out, out_len,
+ s ? s : "(null)", width, flags);
+ continue;
+ }
+ if ((*format == 'd') || (*format == 'i')) {
+ pc += printi(out, out_len,
+ va_arg(args, int),
+ 10, 1, width, flags, '0');
+ acnt += sizeof(int);
+ continue;
+ }
+ if (*format == 'x') {
+ pc += printi(out, out_len,
+ va_arg(args, unsigned int),
+ 16, 0, width, flags, 'a');
+ acnt += sizeof(unsigned int);
+ continue;
+ }
+ if (*format == 'X') {
+ pc += printi(out, out_len,
+ va_arg(args, unsigned int),
+ 16, 0, width, flags, 'A');
+ acnt += sizeof(unsigned int);
+ continue;
+ }
+ if (*format == 'u') {
+ pc += printi(out, out_len,
+ va_arg(args, unsigned int),
+ 10, 0, width, flags, 'a');
+ acnt += sizeof(unsigned int);
+ continue;
+ }
+ if (*format == 'p') {
+ pc += printi(out, out_len,
+ va_arg(args, unsigned long),
+ 16, 0, width, flags, 'a');
+ acnt += sizeof(unsigned long);
+ continue;
+ }
+ if (*format == 'P') {
+ pc += printi(out, out_len,
+ va_arg(args, unsigned long),
+ 16, 0, width, flags, 'A');
+ acnt += sizeof(unsigned long);
+ continue;
+ }
+ if (*format == 'l' && *(format + 1) == 'l') {
+ while (acnt & (sizeof(unsigned long long)-1)) {
+ va_arg(args, int);
+ acnt += sizeof(int);
+ }
+ if (sizeof(unsigned long long) ==
+ sizeof(unsigned long)) {
+ tmp = va_arg(args, unsigned long long);
+ acnt += sizeof(unsigned long long);
+ } else {
+ ((unsigned long *)&tmp)[0] =
+ va_arg(args, unsigned long);
+ ((unsigned long *)&tmp)[1] =
+ va_arg(args, unsigned long);
+ acnt += 2*sizeof(unsigned long);
+ }
+ if (*(format + 2) == 'u') {
+ format += 2;
+ pc += printi(out, out_len, tmp,
+ 10, 0, width, flags, 'a');
+ } else if (*(format + 2) == 'x') {
+ format += 2;
+ pc += printi(out, out_len, tmp,
+ 16, 0, width, flags, 'a');
+ } else if (*(format + 2) == 'X') {
+ format += 2;
+ pc += printi(out, out_len, tmp,
+ 16, 0, width, flags, 'A');
+ } else {
+ format += 1;
+ pc += printi(out, out_len, tmp,
+ 10, 1, width, flags, '0');
+ }
+ continue;
+ } else if (*format == 'l') {
+ if (*(format + 1) == 'x') {
+ format += 1;
+ pc += printi(out, out_len,
+ va_arg(args, unsigned long),
+ 16, 0, width, flags, 'a');
+ acnt += sizeof(unsigned long);
+ } else if (*(format + 1) == 'X') {
+ format += 1;
+ pc += printi(out, out_len,
+ va_arg(args, unsigned long),
+ 16, 0, width, flags, 'A');
+ acnt += sizeof(unsigned long);
+ } else {
+ pc += printi(out, out_len,
+ va_arg(args, long),
+ 10, 1, width, flags, '0');
+ acnt += sizeof(long);
+ }
+ }
+ if (*format == 'c') {
+ /* char are converted to int then pushed on the stack */
+ scr[0] = va_arg(args, int);
+ scr[1] = '\0';
+ pc += prints(out, out_len, scr, width, flags);
+ acnt += sizeof(int);
+ continue;
+ }
+ } else {
+out:
+ printc(out, out_len, *format);
+ ++pc;
+ }
+ }
+ if (out)
+ **out = '\0';
+ return pc;
+}
+
+int sbi_sprintf(char *out, const char *format, ...)
+{
+ va_list args;
+ int retval;
+ va_start(args, format);
+ retval = print(&out, NULL, format, args);
+ va_end(args);
+ return retval;
+}
+
+int sbi_snprintf(char *out, u32 out_sz, const char *format, ...)
+{
+ va_list args;
+ int retval;
+ va_start(args, format);
+ retval = print(&out, &out_sz, format, args);
+ va_end(args);
+ return retval;
+}
+
+int sbi_printf(const char *format, ...)
+{
+ va_list args;
+ int retval;
+ va_start(args, format);
+ retval = print(NULL, NULL, format, args);
+ va_end(args);
+ return retval;
+}
+
+int sbi_console_init(struct sbi_scratch *scratch)
+{
+ console_plat = sbi_platform_ptr(scratch);
+
+ return sbi_platform_console_init(console_plat);
+}
diff --git a/lib/sbi_ecall.c b/lib/sbi_ecall.c
new file mode 100644
index 0000000..928a8b7
--- /dev/null
+++ b/lib/sbi_ecall.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2018 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel <anup.patel@wdc.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_ecall.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_ipi.h>
+#include <sbi/sbi_system.h>
+#include <sbi/sbi_timer.h>
+#include <sbi/sbi_trap.h>
+
+#define SBI_ECALL_VERSION_MAJOR 0
+#define SBI_ECALL_VERSION_MINOR 1
+
+#define SBI_ECALL_SET_TIMER 0
+#define SBI_ECALL_CONSOLE_PUTCHAR 1
+#define SBI_ECALL_CONSOLE_GETCHAR 2
+#define SBI_ECALL_CLEAR_IPI 3
+#define SBI_ECALL_SEND_IPI 4
+#define SBI_ECALL_REMOTE_FENCE_I 5
+#define SBI_ECALL_REMOTE_SFENCE_VMA 6
+#define SBI_ECALL_REMOTE_SFENCE_VMA_ASID 7
+#define SBI_ECALL_SHUTDOWN 8
+
+u16 sbi_ecall_version_major(void)
+{
+ return SBI_ECALL_VERSION_MAJOR;
+}
+
+u16 sbi_ecall_version_minor(void)
+{
+ return SBI_ECALL_VERSION_MINOR;
+}
+
+int sbi_ecall_handler(u32 hartid, ulong mcause,
+ struct sbi_trap_regs *regs,
+ struct sbi_scratch *scratch)
+{
+ int ret = SBI_ENOTSUPP;
+
+ switch (regs->a7) {
+ case SBI_ECALL_SET_TIMER:
+#if __riscv_xlen == 32
+ sbi_timer_event_start(scratch, hartid,
+ (((u64)regs->a1 << 32) || (u64)regs->a0));
+#else
+ sbi_timer_event_start(scratch, hartid, (u64)regs->a0);
+#endif
+ ret = 0;
+ break;
+ case SBI_ECALL_CONSOLE_PUTCHAR:
+ sbi_putc(regs->a0);
+ ret = 0;
+ break;
+ case SBI_ECALL_CONSOLE_GETCHAR:
+ regs->a0 = sbi_getc();
+ ret = 0;
+ break;
+ case SBI_ECALL_CLEAR_IPI:
+ sbi_ipi_clear_smode(scratch, hartid);
+ ret = 0;
+ break;
+ case SBI_ECALL_SEND_IPI:
+ ret = sbi_ipi_send_many(scratch, hartid,
+ (ulong *)regs->a0,
+ SBI_IPI_EVENT_SOFT);
+ break;
+ case SBI_ECALL_REMOTE_FENCE_I:
+ ret = sbi_ipi_send_many(scratch, hartid,
+ (ulong *)regs->a0,
+ SBI_IPI_EVENT_FENCE_I);
+ break;
+ case SBI_ECALL_REMOTE_SFENCE_VMA:
+ case SBI_ECALL_REMOTE_SFENCE_VMA_ASID:
+ ret = sbi_ipi_send_many(scratch, hartid,
+ (ulong *)regs->a0,
+ SBI_IPI_EVENT_SFENCE_VMA);
+ break;
+ case SBI_ECALL_SHUTDOWN:
+ sbi_system_shutdown(scratch, 0);
+ ret = 0;
+ break;
+ default:
+ break;
+ };
+
+ if (!ret) {
+ regs->mepc += 4;
+ }
+
+ return ret;
+}
diff --git a/lib/sbi_emulate_csr.c b/lib/sbi_emulate_csr.c
new file mode 100644
index 0000000..cc74f96
--- /dev/null
+++ b/lib/sbi_emulate_csr.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2018 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel <anup.patel@wdc.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_encoding.h>
+#include <sbi/sbi_bits.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_emulate_csr.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_timer.h>
+
+int sbi_emulate_csr_read(int csr_num,
+ u32 hartid, ulong mstatus,
+ struct sbi_scratch *scratch,
+ ulong *csr_val)
+{
+ ulong cen = -1UL;
+
+ if (EXTRACT_FIELD(mstatus, MSTATUS_MPP) == PRV_U)
+ cen = csr_read(scounteren);
+
+ switch (csr_num) {
+ case CSR_MISA:
+ *csr_val = csr_read(misa);
+ break;
+ case CSR_MVENDORID:
+ *csr_val = csr_read(mvendorid);
+ break;
+ case CSR_MARCHID:
+ *csr_val = csr_read(marchid);
+ break;
+ case CSR_MIMPID:
+ *csr_val = csr_read(mimpid);
+ break;
+ case CSR_MHARTID:
+ *csr_val = csr_read(mhartid);
+ break;
+ case CSR_CYCLE:
+ if (!((cen >> (CSR_CYCLE - CSR_CYCLE)) & 1))
+ return -1;
+ *csr_val = csr_read(mcycle);
+ break;
+ case CSR_TIME:
+ if (!((cen >> (CSR_TIME - CSR_CYCLE)) & 1))
+ return -1;
+ *csr_val = sbi_timer_value(scratch);
+ break;
+ case CSR_INSTRET:
+ if (!((cen >> (CSR_INSTRET - CSR_CYCLE)) & 1))
+ return -1;
+ *csr_val = csr_read(minstret);
+ break;
+ case CSR_MHPMCOUNTER3:
+ if (!((cen >> (3 + CSR_MHPMCOUNTER3 - CSR_MHPMCOUNTER3)) & 1))
+ return -1;
+ *csr_val = csr_read(mhpmcounter3);
+ break;
+ case CSR_MHPMCOUNTER4:
+ if (!((cen >> (3 + CSR_MHPMCOUNTER4 - CSR_MHPMCOUNTER3)) & 1))
+ return -1;
+ *csr_val = csr_read(mhpmcounter4);
+ break;
+#if __riscv_xlen == 32
+ case CSR_CYCLEH:
+ if (!((cen >> (CSR_CYCLE - CSR_CYCLE)) & 1))
+ return -1;
+ *csr_val = csr_read(mcycleh);
+ break;
+ case CSR_TIMEH:
+ if (!((cen >> (CSR_TIME - CSR_CYCLE)) & 1))
+ return -1;
+ *csr_val = sbi_timer_value(scratch);
+ *csr_val = *csr_val >> 32;
+ break;
+ case CSR_INSTRETH:
+ if (!((cen >> (CSR_INSTRET - CSR_CYCLE)) & 1))
+ return -1;
+ *csr_val = csr_read(minstreth);
+ break;
+ case CSR_MHPMCOUNTER3H:
+ if (!((cen >> (3 + CSR_MHPMCOUNTER3 - CSR_MHPMCOUNTER3)) & 1))
+ return -1;
+ *csr_val = csr_read(mhpmcounter3h);
+ break;
+ case CSR_MHPMCOUNTER4H:
+ if (!((cen >> (3 + CSR_MHPMCOUNTER4 - CSR_MHPMCOUNTER3)) & 1))
+ return -1;
+ *csr_val = csr_read(mhpmcounter4h);
+ break;
+#endif
+ case CSR_MHPMEVENT3:
+ *csr_val = csr_read(mhpmevent3);
+ break;
+ case CSR_MHPMEVENT4:
+ *csr_val = csr_read(mhpmevent4);
+ break;
+ default:
+ sbi_printf("%s: hartid%d: invalid csr_num=0x%x\n",
+ __func__, hartid, csr_num);
+ return SBI_ENOTSUPP;
+ };
+
+ return 0;
+}
+
+int sbi_emulate_csr_write(int csr_num,
+ u32 hartid, ulong mstatus,
+ struct sbi_scratch *scratch,
+ ulong csr_val)
+{
+ switch (csr_num) {
+ case CSR_CYCLE:
+ csr_write(mcycle, csr_val);
+ break;
+ case CSR_INSTRET:
+ csr_write(minstret, csr_val);
+ break;
+ case CSR_MHPMCOUNTER3:
+ csr_write(mhpmcounter3, csr_val);
+ break;
+ case CSR_MHPMCOUNTER4:
+ csr_write(mhpmcounter4, csr_val);
+ break;
+#if __riscv_xlen == 32
+ case CSR_CYCLEH:
+ csr_write(mcycleh, csr_val);
+ break;
+ case CSR_INSTRETH:
+ csr_write(minstreth, csr_val);
+ break;
+ case CSR_MHPMCOUNTER3H:
+ csr_write(mhpmcounter3h, csr_val);
+ break;
+ case CSR_MHPMCOUNTER4H:
+ csr_write(mhpmcounter4h, csr_val);
+ break;
+#endif
+ case CSR_MHPMEVENT3:
+ csr_write(mhpmevent3, csr_val);
+ break;
+ case CSR_MHPMEVENT4:
+ csr_write(mhpmevent4, csr_val);
+ break;
+ default:
+ sbi_printf("%s: hartid%d: invalid csr_num=0x%x\n",
+ __func__, hartid, csr_num);
+ return SBI_ENOTSUPP;
+ };
+
+ return 0;
+}
diff --git a/lib/sbi_hart.c b/lib/sbi_hart.c
new file mode 100644
index 0000000..db833c7
--- /dev/null
+++ b/lib/sbi_hart.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2018 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel <anup.patel@wdc.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_barrier.h>
+#include <sbi/riscv_encoding.h>
+#include <sbi/riscv_locks.h>
+#include <sbi/sbi_bits.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_platform.h>
+
+static int mstatus_init(u32 hartid)
+{
+ /* Enable FPU */
+ if (misa_extension('D') || misa_extension('F'))
+ csr_write(mstatus, MSTATUS_FS);
+
+ /* Enable user/supervisor use of perf counters */
+ if (misa_extension('S'))
+ csr_write(scounteren, -1);
+ csr_write(mcounteren, -1);
+
+ /* Disable all interrupts */
+ csr_write(mie, 0);
+
+ /* Disable S-mode paging */
+ if (misa_extension('S'))
+ csr_write(sptbr, 0);
+
+ return 0;
+}
+
+#ifdef __riscv_flen
+static void init_fp_reg(int i)
+{
+ /* TODO: */
+}
+#endif
+
+static int fp_init(u32 hartid)
+{
+#ifdef __riscv_flen
+ int i;
+#else
+ unsigned long fd_mask;
+#endif
+
+ if (!misa_extension('D') && !misa_extension('F'))
+ return 0;
+
+ if (!(csr_read(mstatus) & MSTATUS_FS))
+ return SBI_EINVAL;
+
+#ifdef __riscv_flen
+ for (i = 0; i < 32; i++)
+ init_fp_reg(i);
+ csr_write(fcsr, 0);
+#else
+ fd_mask = (1 << ('F' - 'A')) | (1 << ('D' - 'A'));
+ csr_clear(misa, fd_mask);
+ if (csr_read(misa) & fd_mask)
+ return SBI_ENOTSUPP;
+#endif
+
+ return 0;
+}
+
+static int delegate_traps(u32 hartid)
+{
+ /* send S-mode interrupts and most exceptions straight to S-mode */
+ unsigned long interrupts = MIP_SSIP | MIP_STIP | MIP_SEIP;
+ unsigned long exceptions = (1U << CAUSE_MISALIGNED_FETCH) |
+ (1U << CAUSE_FETCH_PAGE_FAULT) |
+ (1U << CAUSE_BREAKPOINT) |
+ (1U << CAUSE_LOAD_PAGE_FAULT) |
+ (1U << CAUSE_STORE_PAGE_FAULT) |
+ (1U << CAUSE_USER_ECALL);
+
+ if (!misa_extension('S'))
+ return 0;
+
+ csr_write(mideleg, interrupts);
+ csr_write(medeleg, exceptions);
+
+ if (csr_read(mideleg) != interrupts)
+ return SBI_EFAIL;
+ if (csr_read(medeleg) != exceptions)
+ return SBI_EFAIL;
+
+ return 0;
+}
+
+unsigned long log2roundup(unsigned long x)
+{
+ unsigned long ret = 0;
+
+ while (ret < __riscv_xlen) {
+ if (x <= (1UL << ret))
+ break;
+ ret++;
+ }
+
+ return ret;
+}
+
+void sbi_hart_pmp_dump(void)
+{
+ unsigned int i;
+ unsigned long prot, addr, size, l2l;
+
+ for (i = 0; i < PMP_COUNT; i++) {
+ pmp_get(i, &prot, &addr, &l2l);
+ if (!(prot & PMP_A))
+ continue;
+ if (l2l < __riscv_xlen)
+ size = (1UL << l2l);
+ else
+ size = 0;
+#if __riscv_xlen == 32
+ sbi_printf("PMP%d: 0x%08lx-0x%08lx (A",
+#else
+ sbi_printf("PMP%d: 0x%016lx-0x%016lx (A",
+#endif
+ i, addr, addr + size - 1);
+ if (prot & PMP_L)
+ sbi_printf(",L");
+ if (prot & PMP_R)
+ sbi_printf(",R");
+ if (prot & PMP_W)
+ sbi_printf(",W");
+ if (prot & PMP_X)
+ sbi_printf(",X");
+ sbi_printf(")\n");
+ }
+}
+
+static int pmp_init(struct sbi_scratch *scratch, u32 hartid)
+{
+ u32 i, count;
+ unsigned long fw_start, fw_size_log2;
+ ulong prot, addr, log2size;
+ struct sbi_platform *plat = sbi_platform_ptr(scratch);
+
+ fw_size_log2 = log2roundup(scratch->fw_size);
+ fw_start = scratch->fw_start & ~((1UL << fw_size_log2) - 1UL);
+
+ pmp_set(0, 0, fw_start, fw_size_log2);
+
+ count = sbi_platform_pmp_region_count(plat, hartid);
+ if ((PMP_COUNT - 1) < count)
+ count = (PMP_COUNT - 1);
+
+ for (i = 0; i < count; i++) {
+ if (sbi_platform_pmp_region_info(plat, hartid, i,
+ &prot, &addr, &log2size))
+ continue;
+ pmp_set(i + 1, prot, addr, log2size);
+ }
+
+ return 0;
+}
+
+int sbi_hart_init(struct sbi_scratch *scratch, u32 hartid)
+{
+ int rc;
+
+ rc = mstatus_init(hartid);
+ if (rc)
+ return rc;
+
+ rc = fp_init(hartid);
+ if (rc)
+ return rc;
+
+ rc = delegate_traps(hartid);
+ if (rc)
+ return rc;
+
+ return pmp_init(scratch, hartid);
+}
+
+void __attribute__((noreturn)) sbi_hart_hang(void)
+{
+ while (1)
+ wfi();
+ __builtin_unreachable();
+}
+
+void __attribute__((noreturn)) sbi_hart_boot_next(unsigned long arg0,
+ unsigned long arg1,
+ unsigned long next_addr,
+ unsigned long next_mode)
+{
+ unsigned long val;
+
+ if (next_mode != PRV_S && next_mode != PRV_M && next_mode != PRV_U)
+ sbi_hart_hang();
+
+ val = csr_read(mstatus);
+ val = INSERT_FIELD(val, MSTATUS_MPP, next_mode);
+ val = INSERT_FIELD(val, MSTATUS_MPIE, 0);
+ csr_write(mstatus, val);
+ csr_write(mepc, next_addr);
+
+ if (next_mode == PRV_S) {
+ csr_write(stvec, next_addr);
+ csr_write(sscratch, 0);
+ csr_write(sie, 0);
+ csr_write(satp, 0);
+ } else if (next_mode == PRV_U) {
+ csr_write(utvec, next_addr);
+ csr_write(uscratch, 0);
+ csr_write(uie, 0);
+ }
+
+ register unsigned long a0 asm ("a0") = arg0;
+ register unsigned long a1 asm ("a1") = arg1;
+ __asm__ __volatile__ ("mret" : : "r" (a0), "r" (a1));
+ __builtin_unreachable();
+}
+
+static spinlock_t avail_hart_mask_lock = SPIN_LOCK_INITIALIZER;
+static volatile unsigned long avail_hart_mask = 0;
+
+void sbi_hart_mark_available(u32 hartid)
+{
+ spin_lock(&avail_hart_mask_lock);
+ avail_hart_mask |= (1UL << hartid);
+ spin_unlock(&avail_hart_mask_lock);
+}
+
+void sbi_hart_unmark_available(u32 hartid)
+{
+ spin_lock(&avail_hart_mask_lock);
+ avail_hart_mask &= ~(1UL << hartid);
+ spin_unlock(&avail_hart_mask_lock);
+}
+
+ulong sbi_hart_available_mask(void)
+{
+ ulong ret;
+
+ spin_lock(&avail_hart_mask_lock);
+ ret = avail_hart_mask;
+ spin_unlock(&avail_hart_mask_lock);
+
+ return ret;
+}
+
+typedef struct sbi_scratch *(*h2s)(ulong hartid);
+
+struct sbi_scratch *sbi_hart_id_to_scratch(struct sbi_scratch *scratch,
+ u32 hartid)
+{
+ return ((h2s)scratch->hartid_to_scratch)(hartid);
+}
+
+#define NO_HOTPLUG_BITMAP_SIZE __riscv_xlen
+static spinlock_t coldboot_holding_pen_lock = SPIN_LOCK_INITIALIZER;
+static volatile unsigned long coldboot_holding_pen = 0;
+
+void sbi_hart_wait_for_coldboot(struct sbi_scratch *scratch, u32 hartid)
+{
+ unsigned long done;
+ struct sbi_platform *plat = sbi_platform_ptr(scratch);
+
+ if ((sbi_platform_hart_count(plat) <= hartid) ||
+ (NO_HOTPLUG_BITMAP_SIZE <= hartid))
+ sbi_hart_hang();
+
+ while (1) {
+ spin_lock(&coldboot_holding_pen_lock);
+ done = coldboot_holding_pen;
+ spin_unlock(&coldboot_holding_pen_lock);
+ if (done)
+ break;
+ cpu_relax();
+ }
+}
+
+void sbi_hart_wake_coldboot_harts(struct sbi_scratch *scratch)
+{
+ spin_lock(&coldboot_holding_pen_lock);
+ coldboot_holding_pen = 1;
+ spin_unlock(&coldboot_holding_pen_lock);
+}
diff --git a/lib/sbi_illegal_insn.c b/lib/sbi_illegal_insn.c
new file mode 100644
index 0000000..e8edd0e
--- /dev/null
+++ b/lib/sbi_illegal_insn.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2018 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel <anup.patel@wdc.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_encoding.h>
+#include <sbi/sbi_bits.h>
+#include <sbi/sbi_emulate_csr.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_illegal_insn.h>
+#include <sbi/sbi_trap.h>
+#include <sbi/sbi_unpriv.h>
+
+#define SH_RD 7
+#define SH_RS1 15
+#define SH_RS2 20
+#define SH_RS2C 2
+
+#define RV_X(x, s, n) (((x) >> (s)) & ((1 << (n)) - 1))
+#define RVC_LW_IMM(x) ((RV_X(x, 6, 1) << 2) | \
+ (RV_X(x, 10, 3) << 3) | \
+ (RV_X(x, 5, 1) << 6))
+#define RVC_LD_IMM(x) ((RV_X(x, 10, 3) << 3) | \
+ (RV_X(x, 5, 2) << 6))
+#define RVC_LWSP_IMM(x) ((RV_X(x, 4, 3) << 2) | \
+ (RV_X(x, 12, 1) << 5) | \
+ (RV_X(x, 2, 2) << 6))
+#define RVC_LDSP_IMM(x) ((RV_X(x, 5, 2) << 3) | \
+ (RV_X(x, 12, 1) << 5) | \
+ (RV_X(x, 2, 3) << 6))
+#define RVC_SWSP_IMM(x) ((RV_X(x, 9, 4) << 2) | \
+ (RV_X(x, 7, 2) << 6))
+#define RVC_SDSP_IMM(x) ((RV_X(x, 10, 3) << 3) | \
+ (RV_X(x, 7, 3) << 6))
+#define RVC_RS1S(insn) (8 + RV_X(insn, SH_RD, 3))
+#define RVC_RS2S(insn) (8 + RV_X(insn, SH_RS2C, 3))
+#define RVC_RS2(insn) RV_X(insn, SH_RS2C, 5)
+
+#define SHIFT_RIGHT(x, y) ((y) < 0 ? ((x) << -(y)) : ((x) >> (y)))
+
+#define REG_MASK \
+((1 << (5 + LOG_REGBYTES)) - (1 << LOG_REGBYTES))
+
+#define REG_OFFSET(insn, pos) \
+(SHIFT_RIGHT((insn), (pos) - LOG_REGBYTES) & REG_MASK)
+
+#define REG_PTR(insn, pos, regs)\
+(ulong *)((ulong)(regs) + REG_OFFSET(insn, pos))
+
+#define GET_RM(insn) (((insn) >> 12) & 7)
+
+#define GET_RS1(insn, regs) (*REG_PTR(insn, SH_RS1, regs))
+#define GET_RS2(insn, regs) (*REG_PTR(insn, SH_RS2, regs))
+#define GET_RS1S(insn, regs) (*REG_PTR(RVC_RS1S(insn), 0, regs))
+#define GET_RS2S(insn, regs) (*REG_PTR(RVC_RS2S(insn), 0, regs))
+#define GET_RS2C(insn, regs) (*REG_PTR(insn, SH_RS2C, regs))
+#define GET_SP(regs) (*REG_PTR(2, 0, regs))
+#define SET_RD(insn, regs, val) (*REG_PTR(insn, SH_RD, regs) = (val))
+#define IMM_I(insn) ((s32)(insn) >> 20)
+#define IMM_S(insn) (((s32)(insn) >> 25 << 5) | \
+ (s32)(((insn) >> 7) & 0x1f))
+#define MASK_FUNCT3 0x7000
+
+typedef int (*illegal_insn_func)(ulong insn,
+ u32 hartid, ulong mcause,
+ struct sbi_trap_regs *regs,
+ struct sbi_scratch *scratch);
+
+static int truly_illegal_insn(ulong insn,
+ u32 hartid, ulong mcause,
+ struct sbi_trap_regs *regs,
+ struct sbi_scratch *scratch)
+{
+ /* For now, always fails */
+ return SBI_ENOTSUPP;
+}
+
+static int system_opcode_insn(ulong insn,
+ u32 hartid, ulong mcause,
+ struct sbi_trap_regs *regs,
+ struct sbi_scratch *scratch)
+{
+ int do_write, rs1_num = (insn >> 15) & 0x1f;
+ ulong rs1_val = GET_RS1(insn, regs);
+ int csr_num = (u32)insn >> 20;
+ ulong csr_val, new_csr_val;
+
+ if (sbi_emulate_csr_read(csr_num, hartid, regs->mstatus,
+ scratch, &csr_val))
+ return truly_illegal_insn(insn, hartid, mcause,
+ regs, scratch);
+
+ do_write = rs1_num;
+ switch (GET_RM(insn)) {
+ case 1:
+ new_csr_val = rs1_val;
+ do_write = 1;
+ break;
+ case 2:
+ new_csr_val = csr_val | rs1_val;
+ break;
+ case 3: new_csr_val = csr_val & ~rs1_val;
+ break;
+ case 5:
+ new_csr_val = rs1_num;
+ do_write = 1;
+ break;
+ case 6:
+ new_csr_val = csr_val | rs1_num;
+ break;
+ case 7:
+ new_csr_val = csr_val & ~rs1_num;
+ break;
+ default:
+ return truly_illegal_insn(insn, hartid, mcause,
+ regs, scratch);
+ };
+
+ if (do_write &&
+ sbi_emulate_csr_write(csr_num, hartid, regs->mstatus,
+ scratch, new_csr_val))
+ return truly_illegal_insn(insn, hartid, mcause,
+ regs, scratch);
+
+ SET_RD(insn, regs, csr_val);
+
+ regs->mepc += 4;
+
+ return 0;
+}
+
+static illegal_insn_func illegal_insn_table[32] = {
+ truly_illegal_insn, /* 0 */
+ truly_illegal_insn, /* 1 */
+ truly_illegal_insn, /* 2 */
+ truly_illegal_insn, /* 3 */
+ truly_illegal_insn, /* 4 */
+ truly_illegal_insn, /* 5 */
+ truly_illegal_insn, /* 6 */
+ truly_illegal_insn, /* 7 */
+ truly_illegal_insn, /* 8 */
+ truly_illegal_insn, /* 9 */
+ truly_illegal_insn, /* 10 */
+ truly_illegal_insn, /* 11 */
+ truly_illegal_insn, /* 12 */
+ truly_illegal_insn, /* 13 */
+ truly_illegal_insn, /* 14 */
+ truly_illegal_insn, /* 15 */
+ truly_illegal_insn, /* 16 */
+ truly_illegal_insn, /* 17 */
+ truly_illegal_insn, /* 18 */
+ truly_illegal_insn, /* 19 */
+ truly_illegal_insn, /* 20 */
+ truly_illegal_insn, /* 21 */
+ truly_illegal_insn, /* 22 */
+ truly_illegal_insn, /* 23 */
+ truly_illegal_insn, /* 24 */
+ truly_illegal_insn, /* 25 */
+ truly_illegal_insn, /* 26 */
+ truly_illegal_insn, /* 27 */
+ system_opcode_insn, /* 28 */
+ truly_illegal_insn, /* 29 */
+ truly_illegal_insn, /* 30 */
+ truly_illegal_insn /* 31 */
+};
+
+int sbi_illegal_insn_handler(u32 hartid, ulong mcause,
+ struct sbi_trap_regs *regs,
+ struct sbi_scratch *scratch)
+{
+ ulong mstatus;
+ ulong insn = csr_read(mbadaddr);
+
+ if (unlikely((insn & 3) != 3)) {
+ if (insn == 0) {
+ mstatus = csr_read(mstatus);
+ insn = get_insn(regs->mepc, &mstatus);
+ }
+ if ((insn & 3) != 3)
+ return SBI_ENOTSUPP;
+ }
+
+ return illegal_insn_table[(insn & 0x7c) >> 2](insn, hartid, mcause,
+ regs, scratch);
+}
diff --git a/lib/sbi_init.c b/lib/sbi_init.c
new file mode 100644
index 0000000..52c63fa
--- /dev/null
+++ b/lib/sbi_init.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2018 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel <anup.patel@wdc.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_atomic.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_ecall.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_ipi.h>
+#include <sbi/sbi_platform.h>
+#include <sbi/sbi_system.h>
+#include <sbi/sbi_timer.h>
+
+static void __attribute__((noreturn)) init_coldboot(struct sbi_scratch *scratch,
+ u32 hartid)
+{
+ int rc;
+ char str[64];
+ struct sbi_platform *plat = sbi_platform_ptr(scratch);
+
+ rc = sbi_system_cold_early_init(scratch);
+ if (rc)
+ sbi_hart_hang();
+
+ rc = sbi_system_warm_early_init(scratch, hartid);
+ if (rc)
+ sbi_hart_hang();
+
+ rc = sbi_hart_init(scratch, hartid);
+ if (rc)
+ sbi_hart_hang();
+
+ rc = sbi_console_init(scratch);
+ if (rc)
+ sbi_hart_hang();
+
+ rc = sbi_platform_cold_irqchip_init(plat);
+ if (rc)
+ sbi_hart_hang();
+
+ rc = sbi_platform_warm_irqchip_init(plat, hartid);
+ if (rc)
+ sbi_hart_hang();
+
+ rc = sbi_ipi_cold_init(scratch);
+ if (rc)
+ sbi_hart_hang();
+
+ rc = sbi_ipi_warm_init(scratch, hartid);
+ if (rc)
+ sbi_hart_hang();
+
+ rc = sbi_timer_cold_init(scratch);
+ if (rc)
+ sbi_hart_hang();
+
+ rc = sbi_timer_warm_init(scratch, hartid);
+ if (rc)
+ sbi_hart_hang();
+
+ rc = sbi_system_cold_final_init(scratch);
+ if (rc)
+ sbi_hart_hang();
+
+ rc = sbi_system_warm_final_init(scratch, hartid);
+ if (rc)
+ sbi_hart_hang();
+
+ misa_string(str, sizeof(str));
+ sbi_printf("OpenSBI v%d.%d (%s %s)\n",
+ OPENSBI_MAJOR, OPENSBI_MINOR,
+ __DATE__, __TIME__);
+ sbi_printf("\n");
+ /* Platform details */
+ sbi_printf("Platform Name : %s\n", sbi_platform_name(plat));
+ sbi_printf("Platform HART Features : RV%d%s\n", misa_xlen(), str);
+ sbi_printf("Platform Max HARTs : %d\n",
+ sbi_platform_hart_count(plat));
+ /* Firmware details */
+ sbi_printf("Firmware Base : 0x%lx\n", scratch->fw_start);
+ sbi_printf("Firmware Size : %d KB\n",
+ (u32)(scratch->fw_size / 1024));
+ /* Generic details */
+ sbi_printf("Runtime SBI Version : %d.%d\n",
+ sbi_ecall_version_major(), sbi_ecall_version_minor());
+ sbi_printf("\n");
+
+ sbi_hart_pmp_dump();
+
+ sbi_hart_mark_available(hartid);
+
+ if (!sbi_platform_has_hart_hotplug(plat))
+ sbi_hart_wake_coldboot_harts(scratch);
+
+ sbi_hart_boot_next(hartid, scratch->next_arg1,
+ scratch->next_addr, scratch->next_mode);
+}
+
+static void __attribute__((noreturn)) init_warmboot(struct sbi_scratch *scratch,
+ u32 hartid)
+{
+ int rc;
+ struct sbi_platform *plat = sbi_platform_ptr(scratch);
+
+ if (!sbi_platform_has_hart_hotplug(plat))
+ sbi_hart_wait_for_coldboot(scratch, hartid);
+
+ rc = sbi_system_warm_early_init(scratch, hartid);
+ if (rc)
+ sbi_hart_hang();
+
+ rc = sbi_hart_init(scratch, hartid);
+ if (rc)
+ sbi_hart_hang();
+
+ rc = sbi_platform_warm_irqchip_init(plat, hartid);
+ if (rc)
+ sbi_hart_hang();
+
+ rc = sbi_ipi_warm_init(scratch, hartid);
+ if (rc)
+ sbi_hart_hang();
+
+ rc = sbi_timer_warm_init(scratch, hartid);
+ if (rc)
+ sbi_hart_hang();
+
+ rc = sbi_system_warm_final_init(scratch, hartid);
+ if (rc)
+ sbi_hart_hang();
+
+ sbi_hart_mark_available(hartid);
+
+ if (sbi_platform_has_hart_hotplug(plat))
+ /* TODO: To be implemented in-future. */
+ sbi_hart_hang();
+ else
+ sbi_hart_boot_next(hartid, scratch->next_arg1,
+ scratch->next_addr, scratch->next_mode);
+}
+
+static atomic_t coldboot_lottery = ATOMIC_INITIALIZER(0);
+
+void __attribute__((noreturn)) sbi_init(struct sbi_scratch *scratch)
+{
+ bool coldboot = FALSE;
+ u32 hartid = csr_read(mhartid);
+
+ if (atomic_add_return(&coldboot_lottery, 1) == 1)
+ coldboot = TRUE;
+
+ if (coldboot)
+ init_coldboot(scratch, hartid);
+ else
+ init_warmboot(scratch, hartid);
+}
diff --git a/lib/sbi_ipi.c b/lib/sbi_ipi.c
new file mode 100644
index 0000000..f3e68de
--- /dev/null
+++ b/lib/sbi_ipi.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2018 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel <anup.patel@wdc.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_barrier.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_ipi.h>
+#include <sbi/sbi_platform.h>
+#include <sbi/sbi_timer.h>
+#include <sbi/sbi_unpriv.h>
+
+int sbi_ipi_send_many(struct sbi_scratch *scratch,
+ u32 hartid, ulong *pmask, u32 event)
+{
+ ulong i, m;
+ struct sbi_scratch *oth;
+ ulong mask = sbi_hart_available_mask();
+ struct sbi_platform *plat = sbi_platform_ptr(scratch);
+
+ if (pmask)
+ mask &= load_ulong(pmask, csr_read(mepc));
+
+ /* send IPIs to everyone */
+ for (i = 0, m = mask; m; i++, m >>= 1) {
+ if ((m & 1) && (i != hartid)) {
+ oth = sbi_hart_id_to_scratch(scratch, i);
+ oth->ipi_type = event;
+ mb();
+ sbi_platform_ipi_inject(plat, i, hartid);
+ if (event != SBI_IPI_EVENT_SOFT)
+ sbi_platform_ipi_sync(plat, i, hartid);
+ }
+ }
+
+ return 0;
+}
+
+void sbi_ipi_clear_smode(struct sbi_scratch *scratch, u32 hartid)
+{
+ csr_clear(mip, MIP_SSIP);
+}
+
+void sbi_ipi_process(struct sbi_scratch *scratch, u32 hartid)
+{
+ struct sbi_platform *plat = sbi_platform_ptr(scratch);
+
+ sbi_platform_ipi_clear(plat, hartid);
+ switch (scratch->ipi_type) {
+ case SBI_IPI_EVENT_SOFT:
+ csr_set(mip, MIP_SSIP);
+ break;
+ case SBI_IPI_EVENT_FENCE_I:
+ __asm__ __volatile("fence.i");
+ break;
+ case SBI_IPI_EVENT_SFENCE_VMA:
+ __asm__ __volatile("sfence.vma");
+ break;
+ case SBI_IPI_EVENT_HALT:
+ sbi_hart_hang();
+ break;
+ };
+ scratch->ipi_type = 0;
+}
+
+int sbi_ipi_warm_init(struct sbi_scratch *scratch, u32 hartid)
+{
+ /* Enable software interrupts */
+ csr_set(mie, MIP_MSIP);
+
+ return sbi_platform_warm_ipi_init(sbi_platform_ptr(scratch), hartid);
+}
+
+int sbi_ipi_cold_init(struct sbi_scratch *scratch)
+{
+ return sbi_platform_cold_ipi_init(sbi_platform_ptr(scratch));
+}
diff --git a/lib/sbi_system.c b/lib/sbi_system.c
new file mode 100644
index 0000000..cd250f5
--- /dev/null
+++ b/lib/sbi_system.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel <anup.patel@wdc.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_platform.h>
+#include <sbi/sbi_system.h>
+
+int sbi_system_warm_early_init(struct sbi_scratch *scratch, u32 hartid)
+{
+ return sbi_platform_warm_early_init(sbi_platform_ptr(scratch), hartid);
+}
+
+int sbi_system_warm_final_init(struct sbi_scratch *scratch, u32 hartid)
+{
+ return sbi_platform_warm_final_init(sbi_platform_ptr(scratch), hartid);
+}
+
+int sbi_system_cold_early_init(struct sbi_scratch *scratch)
+{
+ return sbi_platform_cold_early_init(sbi_platform_ptr(scratch));
+}
+
+int sbi_system_cold_final_init(struct sbi_scratch *scratch)
+{
+ return sbi_platform_cold_final_init(sbi_platform_ptr(scratch));
+}
+
+void __attribute__((noreturn)) sbi_system_reboot(struct sbi_scratch *scratch,
+ u32 type)
+
+{
+ sbi_platform_system_reboot(sbi_platform_ptr(scratch), type);
+ sbi_hart_hang();
+}
+
+void __attribute__((noreturn)) sbi_system_shutdown(struct sbi_scratch *scratch,
+ u32 type)
+{
+ sbi_platform_system_shutdown(sbi_platform_ptr(scratch), type);
+ sbi_hart_hang();
+}
diff --git a/lib/sbi_timer.c b/lib/sbi_timer.c
new file mode 100644
index 0000000..355bc64
--- /dev/null
+++ b/lib/sbi_timer.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2018 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel <anup.patel@wdc.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_encoding.h>
+#include <sbi/sbi_platform.h>
+#include <sbi/sbi_timer.h>
+
+#if __riscv_xlen == 32
+u64 get_ticks(void)
+{
+ u32 lo, hi, tmp;
+ __asm__ __volatile__ (
+ "1:\n"
+ "rdtimeh %0\n"
+ "rdtime %1\n"
+ "rdtimeh %2\n"
+ "bne %0, %2, 1b"
+ : "=&r" (hi), "=&r" (lo), "=&r" (tmp));
+ return ((u64)hi << 32) | lo;
+}
+#else
+u64 get_ticks(void)
+{
+ unsigned long n;
+
+ __asm__ __volatile__ (
+ "rdtime %0"
+ : "=r" (n));
+ return n;
+}
+#endif
+
+u64 sbi_timer_value(struct sbi_scratch *scratch)
+{
+ struct sbi_platform *plat = sbi_platform_ptr(scratch);
+
+ if (sbi_platform_has_mmio_timer_value(plat))
+ return sbi_platform_timer_value(plat);
+ else
+ return get_ticks();
+}
+
+void sbi_timer_event_stop(struct sbi_scratch *scratch, u32 hartid)
+{
+ sbi_platform_timer_event_stop(sbi_platform_ptr(scratch), hartid);
+}
+
+void sbi_timer_event_start(struct sbi_scratch *scratch, u32 hartid,
+ u64 next_event)
+{
+ sbi_platform_timer_event_start(sbi_platform_ptr(scratch),
+ hartid, next_event);
+ csr_clear(mip, MIP_STIP);
+ csr_set(mie, MIP_MTIP);
+}
+
+void sbi_timer_process(struct sbi_scratch *scratch, u32 hartid)
+{
+ csr_clear(mie, MIP_MTIP);
+ csr_set(mip, MIP_STIP);
+}
+
+int sbi_timer_warm_init(struct sbi_scratch *scratch, u32 hartid)
+{
+ return sbi_platform_warm_timer_init(sbi_platform_ptr(scratch), hartid);
+}
+
+int sbi_timer_cold_init(struct sbi_scratch *scratch)
+{
+ return sbi_platform_cold_timer_init(sbi_platform_ptr(scratch));
+}
diff --git a/lib/sbi_trap.c b/lib/sbi_trap.c
new file mode 100644
index 0000000..f9c70a6
--- /dev/null
+++ b/lib/sbi_trap.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2018 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel <anup.patel@wdc.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_encoding.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_ecall.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_illegal_insn.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_ipi.h>
+#include <sbi/sbi_timer.h>
+#include <sbi/sbi_trap.h>
+
+static void __attribute__((noreturn)) sbi_trap_error(const char *msg,
+ int rc, u32 hartid,
+ ulong mcause,
+ struct sbi_trap_regs *regs)
+{
+ sbi_printf("%s: hart%d: %s (error %d)\n",
+ __func__, hartid, msg, rc);
+ sbi_printf("%s: hart%d: mcause=0x%lx mepc=0x%lx mstatus=0x%lx\n",
+ __func__, hartid, mcause, regs->mepc, regs->mstatus);
+ sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n",
+ __func__, hartid, "ra", regs->ra, "sp", regs->sp);
+ sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n",
+ __func__, hartid, "gp", regs->gp, "tp", regs->tp);
+ sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n",
+ __func__, hartid, "s0", regs->s0, "s1", regs->s1);
+ sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n",
+ __func__, hartid, "a0", regs->a0, "a1", regs->a1);
+ sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n",
+ __func__, hartid, "a2", regs->a2, "a3", regs->a3);
+ sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n",
+ __func__, hartid, "a4", regs->a4, "a5", regs->a5);
+ sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n",
+ __func__, hartid, "a6", regs->a6, "a7", regs->a7);
+ sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n",
+ __func__, hartid, "s2", regs->s2, "s3", regs->s3);
+ sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n",
+ __func__, hartid, "s4", regs->s4, "s5", regs->s5);
+ sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n",
+ __func__, hartid, "s6", regs->s6, "s7", regs->s7);
+ sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n",
+ __func__, hartid, "s8", regs->s8, "s9", regs->s9);
+ sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n",
+ __func__, hartid, "s10", regs->s10, "s11", regs->s11);
+ sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n",
+ __func__, hartid, "t0", regs->t0, "t1", regs->t1);
+ sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n",
+ __func__, hartid, "t2", regs->t2, "t3", regs->t3);
+ sbi_printf("%s: hart%d: %s=0x%lx %s=0x%lx\n",
+ __func__, hartid, "t4", regs->t4, "t5", regs->t5);
+ sbi_printf("%s: hart%d: %s=0x%lx\n",
+ __func__, hartid, "t6", regs->t6);
+
+ sbi_hart_hang();
+}
+
+void sbi_trap_handler(struct sbi_trap_regs *regs,
+ struct sbi_scratch *scratch)
+{
+ int rc;
+ const char *msg;
+ u32 hartid = csr_read(mhartid);
+ ulong mcause = csr_read(mcause);
+
+ if (mcause & (1UL << (__riscv_xlen - 1))) {
+ mcause &= ~(1UL << (__riscv_xlen - 1));
+ switch (mcause) {
+ case IRQ_M_TIMER:
+ sbi_timer_process(scratch, hartid);
+ break;
+ case IRQ_M_SOFT:
+ sbi_ipi_process(scratch, hartid);
+ break;
+ default:
+ sbi_trap_error("unhandled external interrupt",
+ SBI_ENOTSUPP, hartid, mcause, regs);
+ break;
+ };
+ return;
+ }
+
+ rc = SBI_ENOTSUPP;
+ msg = "trap handler failed";
+ switch (mcause) {
+ case CAUSE_ILLEGAL_INSTRUCTION:
+ rc = sbi_illegal_insn_handler(hartid, mcause, regs, scratch);
+ msg = "illegal instruction handler failed";
+ break;
+ case CAUSE_SUPERVISOR_ECALL:
+ case CAUSE_HYPERVISOR_ECALL:
+ rc = sbi_ecall_handler(hartid, mcause, regs, scratch);
+ msg = "ecall handler failed";
+ break;
+ default:
+ break;
+ };
+
+ if (rc) {
+ sbi_trap_error(msg, rc, hartid, mcause, regs);
+ }
+}