Initial commit.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
This commit is contained in:
Anup Patel
2018-12-11 19:24:06 +05:30
committed by Anup Patel
commit 9e8ff05cb6
67 changed files with 6084 additions and 0 deletions

23
lib/objects.mk Normal file
View File

@@ -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

275
lib/riscv_asm.c Normal file
View File

@@ -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;
}

152
lib/riscv_atomic.c Normal file
View File

@@ -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);
}

46
lib/riscv_locks.c Normal file
View File

@@ -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);
}

367
lib/sbi_console.c Normal file
View File

@@ -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);
}

98
lib/sbi_ecall.c Normal file
View File

@@ -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;
}

157
lib/sbi_emulate_csr.c Normal file
View File

@@ -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;
}

294
lib/sbi_hart.c Normal file
View File

@@ -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);
}

190
lib/sbi_illegal_insn.c Normal file
View File

@@ -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);
}

162
lib/sbi_init.c Normal file
View File

@@ -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);
}

82
lib/sbi_ipi.c Normal file
View File

@@ -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));
}

47
lib/sbi_system.c Normal file
View File

@@ -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();
}

78
lib/sbi_timer.c Normal file
View File

@@ -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));
}

110
lib/sbi_trap.c Normal file
View File

@@ -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);
}
}