diff options
author | Anup Patel <anup.patel@wdc.com> | 2018-12-11 16:54:06 +0300 |
---|---|---|
committer | Anup Patel <anup@brainfault.org> | 2018-12-11 16:54:06 +0300 |
commit | 9e8ff05cb61f157fb0bcb6b0071d7b6dc0763faa (patch) | |
tree | b9513a86b3b36e569cb46387846fee9c5544f566 /lib | |
download | opensbi-9e8ff05cb61f157fb0bcb6b0071d7b6dc0763faa.tar.xz |
Initial commit.
Signed-off-by: Anup Patel <anup.patel@wdc.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/objects.mk | 23 | ||||
-rw-r--r-- | lib/riscv_asm.c | 275 | ||||
-rw-r--r-- | lib/riscv_atomic.c | 152 | ||||
-rw-r--r-- | lib/riscv_locks.c | 46 | ||||
-rw-r--r-- | lib/sbi_console.c | 367 | ||||
-rw-r--r-- | lib/sbi_ecall.c | 98 | ||||
-rw-r--r-- | lib/sbi_emulate_csr.c | 157 | ||||
-rw-r--r-- | lib/sbi_hart.c | 294 | ||||
-rw-r--r-- | lib/sbi_illegal_insn.c | 190 | ||||
-rw-r--r-- | lib/sbi_init.c | 162 | ||||
-rw-r--r-- | lib/sbi_ipi.c | 82 | ||||
-rw-r--r-- | lib/sbi_system.c | 47 | ||||
-rw-r--r-- | lib/sbi_timer.c | 78 | ||||
-rw-r--r-- | lib/sbi_trap.c | 110 |
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); + } +} |