forked from Mirrors/opensbi
		
	lib: Move sbi core library to lib/sbi
Signed-off-by: Atish Patra <atish.patra@wdc.com> Acked-by: Anup Patel <anup.patel@wdc.com>
This commit is contained in:
		
							
								
								
									
										32
									
								
								lib/sbi/objects.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								lib/sbi/objects.mk
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
#
 | 
			
		||||
# SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
#
 | 
			
		||||
# Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
#
 | 
			
		||||
# Authors:
 | 
			
		||||
#   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
libsbi-objs-y += riscv_asm.o
 | 
			
		||||
libsbi-objs-y += riscv_atomic.o
 | 
			
		||||
libsbi-objs-y += riscv_hardfp.o
 | 
			
		||||
libsbi-objs-y += riscv_locks.o
 | 
			
		||||
libsbi-objs-y += riscv_unpriv.o
 | 
			
		||||
 | 
			
		||||
libsbi-objs-y += sbi_console.o
 | 
			
		||||
libsbi-objs-y += sbi_ecall.o
 | 
			
		||||
libsbi-objs-y += sbi_emulate_csr.o
 | 
			
		||||
libsbi-objs-y += sbi_fifo.o
 | 
			
		||||
libsbi-objs-y += sbi_hart.o
 | 
			
		||||
libsbi-objs-y += sbi_illegal_insn.o
 | 
			
		||||
libsbi-objs-y += sbi_init.o
 | 
			
		||||
libsbi-objs-y += sbi_ipi.o
 | 
			
		||||
libsbi-objs-y += sbi_misaligned_ldst.o
 | 
			
		||||
libsbi-objs-y += sbi_scratch.o
 | 
			
		||||
libsbi-objs-y += sbi_system.o
 | 
			
		||||
libsbi-objs-y += sbi_timer.o
 | 
			
		||||
libsbi-objs-y += sbi_tlb.o
 | 
			
		||||
libsbi-objs-y += sbi_trap.o
 | 
			
		||||
 | 
			
		||||
# External Libraries to include
 | 
			
		||||
PLATFORM_INCLUDE_LIBC=y
 | 
			
		||||
							
								
								
									
										272
									
								
								lib/sbi/riscv_asm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								lib/sbi/riscv_asm.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,272 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#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(CSR_PMPCFG0);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPCFG1:
 | 
			
		||||
		ret = csr_read(CSR_PMPCFG1);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPCFG2:
 | 
			
		||||
		ret = csr_read(CSR_PMPCFG2);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPCFG3:
 | 
			
		||||
		ret = csr_read(CSR_PMPCFG3);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR0:
 | 
			
		||||
		ret = csr_read(CSR_PMPADDR0);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR1:
 | 
			
		||||
		ret = csr_read(CSR_PMPADDR1);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR2:
 | 
			
		||||
		ret = csr_read(CSR_PMPADDR2);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR3:
 | 
			
		||||
		ret = csr_read(CSR_PMPADDR3);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR4:
 | 
			
		||||
		ret = csr_read(CSR_PMPADDR4);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR5:
 | 
			
		||||
		ret = csr_read(CSR_PMPADDR5);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR6:
 | 
			
		||||
		ret = csr_read(CSR_PMPADDR6);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR7:
 | 
			
		||||
		ret = csr_read(CSR_PMPADDR7);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR8:
 | 
			
		||||
		ret = csr_read(CSR_PMPADDR8);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR9:
 | 
			
		||||
		ret = csr_read(CSR_PMPADDR9);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR10:
 | 
			
		||||
		ret = csr_read(CSR_PMPADDR10);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR11:
 | 
			
		||||
		ret = csr_read(CSR_PMPADDR11);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR12:
 | 
			
		||||
		ret = csr_read(CSR_PMPADDR12);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR13:
 | 
			
		||||
		ret = csr_read(CSR_PMPADDR13);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR14:
 | 
			
		||||
		ret = csr_read(CSR_PMPADDR14);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR15:
 | 
			
		||||
		ret = csr_read(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(CSR_PMPCFG0, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPCFG1:
 | 
			
		||||
		csr_write(CSR_PMPCFG1, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPCFG2:
 | 
			
		||||
		csr_write(CSR_PMPCFG2, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPCFG3:
 | 
			
		||||
		csr_write(CSR_PMPCFG3, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR0:
 | 
			
		||||
		csr_write(CSR_PMPADDR0, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR1:
 | 
			
		||||
		csr_write(CSR_PMPADDR1, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR2:
 | 
			
		||||
		csr_write(CSR_PMPADDR2, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR3:
 | 
			
		||||
		csr_write(CSR_PMPADDR3, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR4:
 | 
			
		||||
		csr_write(CSR_PMPADDR4, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR5:
 | 
			
		||||
		csr_write(CSR_PMPADDR5, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR6:
 | 
			
		||||
		csr_write(CSR_PMPADDR6, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR7:
 | 
			
		||||
		csr_write(CSR_PMPADDR7, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR8:
 | 
			
		||||
		csr_write(CSR_PMPADDR8, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR9:
 | 
			
		||||
		csr_write(CSR_PMPADDR9, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR10:
 | 
			
		||||
		csr_write(CSR_PMPADDR10, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR11:
 | 
			
		||||
		csr_write(CSR_PMPADDR11, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR12:
 | 
			
		||||
		csr_write(CSR_PMPADDR12, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR13:
 | 
			
		||||
		csr_write(CSR_PMPADDR13, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR14:
 | 
			
		||||
		csr_write(CSR_PMPADDR14, val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_PMPADDR15:
 | 
			
		||||
		csr_write(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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										222
									
								
								lib/sbi/riscv_atomic.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								lib/sbi/riscv_atomic.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,222 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sbi/sbi_types.h>
 | 
			
		||||
#include <sbi/riscv_asm.h>
 | 
			
		||||
#include <sbi/riscv_atomic.h>
 | 
			
		||||
#include <sbi/riscv_barrier.h>
 | 
			
		||||
#include <sbi/sbi_bits.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)
 | 
			
		||||
{
 | 
			
		||||
#ifdef __riscv_atomic
 | 
			
		||||
	return __sync_val_compare_and_swap(&atom->counter, oldval, newval);
 | 
			
		||||
#else
 | 
			
		||||
	return cmpxchg(&atom->counter, oldval, newval);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
long arch_atomic_xchg(atomic_t *atom, long newval)
 | 
			
		||||
{
 | 
			
		||||
	/* Atomically set new value and return old value. */
 | 
			
		||||
#ifdef __riscv_atomic
 | 
			
		||||
	/*
 | 
			
		||||
	 * The name of GCC built-in macro __sync_lock_test_and_set()
 | 
			
		||||
	 * is misleading. A more appropriate name for GCC built-in
 | 
			
		||||
	 * macro would be __sync_val_exchange().
 | 
			
		||||
	 */
 | 
			
		||||
	return __sync_lock_test_and_set(&atom->counter, newval);
 | 
			
		||||
#else
 | 
			
		||||
	return xchg(&atom->counter, newval);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int atomic_raw_xchg_uint(volatile unsigned int *ptr,
 | 
			
		||||
				  unsigned int newval)
 | 
			
		||||
{
 | 
			
		||||
	/* Atomically set new value and return old value. */
 | 
			
		||||
#ifdef __riscv_atomic
 | 
			
		||||
	/*
 | 
			
		||||
	 * The name of GCC built-in macro __sync_lock_test_and_set()
 | 
			
		||||
	 * is misleading. A more appropriate name for GCC built-in
 | 
			
		||||
	 * macro would be __sync_val_exchange().
 | 
			
		||||
	 */
 | 
			
		||||
	return __sync_lock_test_and_set(ptr, newval);
 | 
			
		||||
#else
 | 
			
		||||
	return xchg(ptr, newval);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if (BITS_PER_LONG == 64)
 | 
			
		||||
#define __AMO(op) "amo" #op ".d"
 | 
			
		||||
#elif (BITS_PER_LONG == 32)
 | 
			
		||||
#define __AMO(op) "amo" #op ".w"
 | 
			
		||||
#else
 | 
			
		||||
#error "Unexpected BITS_PER_LONG"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define __atomic_op_bit_ord(op, mod, nr, addr, ord)                          \
 | 
			
		||||
	({                                                                   \
 | 
			
		||||
		unsigned long __res, __mask;                                 \
 | 
			
		||||
		__mask = BIT_MASK(nr);                                       \
 | 
			
		||||
		__asm__ __volatile__(__AMO(op) #ord " %0, %2, %1"            \
 | 
			
		||||
				     : "=r"(__res), "+A"(addr[BIT_WORD(nr)]) \
 | 
			
		||||
				     : "r"(mod(__mask))                      \
 | 
			
		||||
				     : "memory");                            \
 | 
			
		||||
		__res;                                                       \
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
#define __atomic_op_bit(op, mod, nr, addr) \
 | 
			
		||||
	__atomic_op_bit_ord(op, mod, nr, addr, .aqrl)
 | 
			
		||||
 | 
			
		||||
/* Bitmask modifiers */
 | 
			
		||||
#define __NOP(x) (x)
 | 
			
		||||
#define __NOT(x) (~(x))
 | 
			
		||||
 | 
			
		||||
inline int atomic_raw_set_bit(int nr, volatile unsigned long *addr)
 | 
			
		||||
{
 | 
			
		||||
	return __atomic_op_bit(or, __NOP, nr, addr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline int atomic_raw_clear_bit(int nr, volatile unsigned long *addr)
 | 
			
		||||
{
 | 
			
		||||
	return __atomic_op_bit(and, __NOT, nr, addr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline int atomic_set_bit(int nr, atomic_t *atom)
 | 
			
		||||
{
 | 
			
		||||
	return atomic_raw_set_bit(nr, (unsigned long *)&atom->counter);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline int atomic_clear_bit(int nr, atomic_t *atom)
 | 
			
		||||
{
 | 
			
		||||
	return atomic_raw_clear_bit(nr, (unsigned long *)&atom->counter);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										171
									
								
								lib/sbi/riscv_hardfp.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								lib/sbi/riscv_hardfp.S
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,171 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef __riscv_flen
 | 
			
		||||
 | 
			
		||||
#if __riscv_flen != 64
 | 
			
		||||
# error single-float only is not supported
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define get_f32(which) fmv.x.s a0, which; jr t0
 | 
			
		||||
#define put_f32(which) fmv.s.x which, a0; jr t0
 | 
			
		||||
#if __riscv_xlen == 64
 | 
			
		||||
# define get_f64(which) fmv.x.d a0, which; jr t0
 | 
			
		||||
# define put_f64(which) fmv.d.x which, a0; jr t0
 | 
			
		||||
#else
 | 
			
		||||
# define get_f64(which) fsd which, 0(a0); jr t0
 | 
			
		||||
# define put_f64(which) fld which, 0(a0); jr t0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	.text
 | 
			
		||||
	.option norvc
 | 
			
		||||
	.globl get_f32_reg
 | 
			
		||||
	get_f32_reg:
 | 
			
		||||
		get_f32(f0)
 | 
			
		||||
		get_f32(f1)
 | 
			
		||||
		get_f32(f2)
 | 
			
		||||
		get_f32(f3)
 | 
			
		||||
		get_f32(f4)
 | 
			
		||||
		get_f32(f5)
 | 
			
		||||
		get_f32(f6)
 | 
			
		||||
		get_f32(f7)
 | 
			
		||||
		get_f32(f8)
 | 
			
		||||
		get_f32(f9)
 | 
			
		||||
		get_f32(f10)
 | 
			
		||||
		get_f32(f11)
 | 
			
		||||
		get_f32(f12)
 | 
			
		||||
		get_f32(f13)
 | 
			
		||||
		get_f32(f14)
 | 
			
		||||
		get_f32(f15)
 | 
			
		||||
		get_f32(f16)
 | 
			
		||||
		get_f32(f17)
 | 
			
		||||
		get_f32(f18)
 | 
			
		||||
		get_f32(f19)
 | 
			
		||||
		get_f32(f20)
 | 
			
		||||
		get_f32(f21)
 | 
			
		||||
		get_f32(f22)
 | 
			
		||||
		get_f32(f23)
 | 
			
		||||
		get_f32(f24)
 | 
			
		||||
		get_f32(f25)
 | 
			
		||||
		get_f32(f26)
 | 
			
		||||
		get_f32(f27)
 | 
			
		||||
		get_f32(f28)
 | 
			
		||||
		get_f32(f29)
 | 
			
		||||
		get_f32(f30)
 | 
			
		||||
		get_f32(f31)
 | 
			
		||||
 | 
			
		||||
	.text
 | 
			
		||||
	.globl put_f32_reg
 | 
			
		||||
	put_f32_reg:
 | 
			
		||||
		put_f32(f0)
 | 
			
		||||
		put_f32(f1)
 | 
			
		||||
		put_f32(f2)
 | 
			
		||||
		put_f32(f3)
 | 
			
		||||
		put_f32(f4)
 | 
			
		||||
		put_f32(f5)
 | 
			
		||||
		put_f32(f6)
 | 
			
		||||
		put_f32(f7)
 | 
			
		||||
		put_f32(f8)
 | 
			
		||||
		put_f32(f9)
 | 
			
		||||
		put_f32(f10)
 | 
			
		||||
		put_f32(f11)
 | 
			
		||||
		put_f32(f12)
 | 
			
		||||
		put_f32(f13)
 | 
			
		||||
		put_f32(f14)
 | 
			
		||||
		put_f32(f15)
 | 
			
		||||
		put_f32(f16)
 | 
			
		||||
		put_f32(f17)
 | 
			
		||||
		put_f32(f18)
 | 
			
		||||
		put_f32(f19)
 | 
			
		||||
		put_f32(f20)
 | 
			
		||||
		put_f32(f21)
 | 
			
		||||
		put_f32(f22)
 | 
			
		||||
		put_f32(f23)
 | 
			
		||||
		put_f32(f24)
 | 
			
		||||
		put_f32(f25)
 | 
			
		||||
		put_f32(f26)
 | 
			
		||||
		put_f32(f27)
 | 
			
		||||
		put_f32(f28)
 | 
			
		||||
		put_f32(f29)
 | 
			
		||||
		put_f32(f30)
 | 
			
		||||
		put_f32(f31)
 | 
			
		||||
 | 
			
		||||
	.text
 | 
			
		||||
	.globl get_f64_reg
 | 
			
		||||
	get_f64_reg:
 | 
			
		||||
		get_f64(f0)
 | 
			
		||||
		get_f64(f1)
 | 
			
		||||
		get_f64(f2)
 | 
			
		||||
		get_f64(f3)
 | 
			
		||||
		get_f64(f4)
 | 
			
		||||
		get_f64(f5)
 | 
			
		||||
		get_f64(f6)
 | 
			
		||||
		get_f64(f7)
 | 
			
		||||
		get_f64(f8)
 | 
			
		||||
		get_f64(f9)
 | 
			
		||||
		get_f64(f10)
 | 
			
		||||
		get_f64(f11)
 | 
			
		||||
		get_f64(f12)
 | 
			
		||||
		get_f64(f13)
 | 
			
		||||
		get_f64(f14)
 | 
			
		||||
		get_f64(f15)
 | 
			
		||||
		get_f64(f16)
 | 
			
		||||
		get_f64(f17)
 | 
			
		||||
		get_f64(f18)
 | 
			
		||||
		get_f64(f19)
 | 
			
		||||
		get_f64(f20)
 | 
			
		||||
		get_f64(f21)
 | 
			
		||||
		get_f64(f22)
 | 
			
		||||
		get_f64(f23)
 | 
			
		||||
		get_f64(f24)
 | 
			
		||||
		get_f64(f25)
 | 
			
		||||
		get_f64(f26)
 | 
			
		||||
		get_f64(f27)
 | 
			
		||||
		get_f64(f28)
 | 
			
		||||
		get_f64(f29)
 | 
			
		||||
		get_f64(f30)
 | 
			
		||||
		get_f64(f31)
 | 
			
		||||
 | 
			
		||||
	.text
 | 
			
		||||
	.globl put_f64_reg
 | 
			
		||||
	put_f64_reg:
 | 
			
		||||
		put_f64(f0)
 | 
			
		||||
		put_f64(f1)
 | 
			
		||||
		put_f64(f2)
 | 
			
		||||
		put_f64(f3)
 | 
			
		||||
		put_f64(f4)
 | 
			
		||||
		put_f64(f5)
 | 
			
		||||
		put_f64(f6)
 | 
			
		||||
		put_f64(f7)
 | 
			
		||||
		put_f64(f8)
 | 
			
		||||
		put_f64(f9)
 | 
			
		||||
		put_f64(f10)
 | 
			
		||||
		put_f64(f11)
 | 
			
		||||
		put_f64(f12)
 | 
			
		||||
		put_f64(f13)
 | 
			
		||||
		put_f64(f14)
 | 
			
		||||
		put_f64(f15)
 | 
			
		||||
		put_f64(f16)
 | 
			
		||||
		put_f64(f17)
 | 
			
		||||
		put_f64(f18)
 | 
			
		||||
		put_f64(f19)
 | 
			
		||||
		put_f64(f20)
 | 
			
		||||
		put_f64(f21)
 | 
			
		||||
		put_f64(f22)
 | 
			
		||||
		put_f64(f23)
 | 
			
		||||
		put_f64(f24)
 | 
			
		||||
		put_f64(f25)
 | 
			
		||||
		put_f64(f26)
 | 
			
		||||
		put_f64(f27)
 | 
			
		||||
		put_f64(f28)
 | 
			
		||||
		put_f64(f29)
 | 
			
		||||
		put_f64(f30)
 | 
			
		||||
		put_f64(f31)
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										45
									
								
								lib/sbi/riscv_locks.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								lib/sbi/riscv_locks.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										145
									
								
								lib/sbi/riscv_unpriv.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								lib/sbi/riscv_unpriv.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,145 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sbi/riscv_encoding.h>
 | 
			
		||||
#include <sbi/riscv_unpriv.h>
 | 
			
		||||
#include <sbi/sbi_bits.h>
 | 
			
		||||
#include <sbi/sbi_hart.h>
 | 
			
		||||
#include <sbi/sbi_scratch.h>
 | 
			
		||||
 | 
			
		||||
#define DEFINE_UNPRIVILEGED_LOAD_FUNCTION(type, insn, insnlen)                \
 | 
			
		||||
	type load_##type(const type *addr,                                    \
 | 
			
		||||
			struct sbi_scratch *scratch,                          \
 | 
			
		||||
			struct unpriv_trap *trap)                             \
 | 
			
		||||
	{                                                                     \
 | 
			
		||||
		register ulong __mstatus asm("a2");                           \
 | 
			
		||||
		type val = 0;                                                 \
 | 
			
		||||
		trap->ilen = insnlen;                                         \
 | 
			
		||||
		trap->cause = 0;                                              \
 | 
			
		||||
		trap->tval = 0;                                               \
 | 
			
		||||
		sbi_hart_set_trap_info(scratch, trap);                        \
 | 
			
		||||
		asm volatile(                                                 \
 | 
			
		||||
			"csrrs %0, " STR(CSR_MSTATUS) ", %3\n"                \
 | 
			
		||||
			#insn " %1, %2\n"                                     \
 | 
			
		||||
			"csrw " STR(CSR_MSTATUS) ", %0"                       \
 | 
			
		||||
		    : "+&r"(__mstatus), "=&r"(val)                            \
 | 
			
		||||
		    : "m"(*addr), "r"(MSTATUS_MPRV));                         \
 | 
			
		||||
		sbi_hart_set_trap_info(scratch, NULL);                        \
 | 
			
		||||
		return val;                                                   \
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#define DEFINE_UNPRIVILEGED_STORE_FUNCTION(type, insn, insnlen)               \
 | 
			
		||||
	void store_##type(type *addr, type val,                               \
 | 
			
		||||
			struct sbi_scratch *scratch,                          \
 | 
			
		||||
			struct unpriv_trap *trap)                             \
 | 
			
		||||
	{                                                                     \
 | 
			
		||||
		register ulong __mstatus asm("a3");                           \
 | 
			
		||||
		trap->ilen = insnlen;                                         \
 | 
			
		||||
		trap->cause = 0;                                              \
 | 
			
		||||
		trap->tval = 0;                                               \
 | 
			
		||||
		sbi_hart_set_trap_info(scratch, trap);                        \
 | 
			
		||||
		asm volatile(                                                 \
 | 
			
		||||
			"csrrs %0, " STR(CSR_MSTATUS) ", %3\n"                \
 | 
			
		||||
			#insn " %1, %2\n"                                     \
 | 
			
		||||
			"csrw " STR(CSR_MSTATUS) ", %0"                       \
 | 
			
		||||
			: "+&r"(__mstatus)                                    \
 | 
			
		||||
			: "r"(val), "m"(*addr), "r"(MSTATUS_MPRV));           \
 | 
			
		||||
		sbi_hart_set_trap_info(scratch, NULL);                        \
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u8, lbu, 4)
 | 
			
		||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u16, lhu, 4)
 | 
			
		||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(s8, lb, 4)
 | 
			
		||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(s16, lh, 4)
 | 
			
		||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(s32, lw, 2)
 | 
			
		||||
DEFINE_UNPRIVILEGED_STORE_FUNCTION(u8, sb, 4)
 | 
			
		||||
DEFINE_UNPRIVILEGED_STORE_FUNCTION(u16, sh, 4)
 | 
			
		||||
DEFINE_UNPRIVILEGED_STORE_FUNCTION(u32, sw, 2)
 | 
			
		||||
#if __riscv_xlen == 64
 | 
			
		||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u32, lwu, 4)
 | 
			
		||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u64, ld, 2)
 | 
			
		||||
DEFINE_UNPRIVILEGED_STORE_FUNCTION(u64, sd, 2)
 | 
			
		||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(ulong, ld, 2)
 | 
			
		||||
#else
 | 
			
		||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u32, lw, 2)
 | 
			
		||||
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(ulong, lw, 2)
 | 
			
		||||
 | 
			
		||||
u64 load_u64(const u64 *addr,
 | 
			
		||||
	     struct sbi_scratch *scratch, struct unpriv_trap *trap)
 | 
			
		||||
{
 | 
			
		||||
	u64 ret = load_u32((u32 *)addr, scratch, trap);
 | 
			
		||||
 | 
			
		||||
	if (trap->cause)
 | 
			
		||||
		return 0;
 | 
			
		||||
	ret |= ((u64)load_u32((u32 *)addr + 1, scratch, trap) << 32);
 | 
			
		||||
	if (trap->cause)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void store_u64(u64 *addr, u64 val,
 | 
			
		||||
	       struct sbi_scratch *scratch, struct unpriv_trap *trap)
 | 
			
		||||
{
 | 
			
		||||
	store_u32((u32 *)addr, val, scratch, trap);
 | 
			
		||||
	if (trap->cause)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	store_u32((u32 *)addr + 1, val >> 32, scratch, trap);
 | 
			
		||||
	if (trap->cause)
 | 
			
		||||
		return;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
ulong get_insn(ulong mepc, ulong *mstatus)
 | 
			
		||||
{
 | 
			
		||||
	register ulong __mepc asm("a2") = mepc;
 | 
			
		||||
	register ulong __mstatus asm("a3");
 | 
			
		||||
	ulong val;
 | 
			
		||||
#ifndef __riscv_compressed
 | 
			
		||||
	asm("csrrs %[mstatus], " STR(CSR_MSTATUS) ", %[mprv]\n"
 | 
			
		||||
#if __riscv_xlen == 64
 | 
			
		||||
	    STR(LWU) " %[insn], (%[addr])\n"
 | 
			
		||||
#else
 | 
			
		||||
	    STR(LW) " %[insn], (%[addr])\n"
 | 
			
		||||
#endif
 | 
			
		||||
		     "csrw " STR(CSR_MSTATUS) ", %[mstatus]"
 | 
			
		||||
	    : [mstatus] "+&r"(__mstatus), [insn] "=&r"(val)
 | 
			
		||||
	    : [mprv] "r"(MSTATUS_MPRV | MSTATUS_MXR), [addr] "r"(__mepc));
 | 
			
		||||
#else
 | 
			
		||||
	ulong rvc_mask = 3, tmp;
 | 
			
		||||
	asm("csrrs %[mstatus], " STR(CSR_MSTATUS) ", %[mprv]\n"
 | 
			
		||||
						  "and %[tmp], %[addr], 2\n"
 | 
			
		||||
						  "bnez %[tmp], 1f\n"
 | 
			
		||||
#if __riscv_xlen == 64
 | 
			
		||||
	    STR(LWU) " %[insn], (%[addr])\n"
 | 
			
		||||
#else
 | 
			
		||||
	    STR(LW) " %[insn], (%[addr])\n"
 | 
			
		||||
#endif
 | 
			
		||||
		     "and %[tmp], %[insn], %[rvc_mask]\n"
 | 
			
		||||
		     "beq %[tmp], %[rvc_mask], 2f\n"
 | 
			
		||||
		     "sll %[insn], %[insn], %[xlen_minus_16]\n"
 | 
			
		||||
		     "srl %[insn], %[insn], %[xlen_minus_16]\n"
 | 
			
		||||
		     "j 2f\n"
 | 
			
		||||
		     "1:\n"
 | 
			
		||||
		     "lhu %[insn], (%[addr])\n"
 | 
			
		||||
		     "and %[tmp], %[insn], %[rvc_mask]\n"
 | 
			
		||||
		     "bne %[tmp], %[rvc_mask], 2f\n"
 | 
			
		||||
		     "lhu %[tmp], 2(%[addr])\n"
 | 
			
		||||
		     "sll %[tmp], %[tmp], 16\n"
 | 
			
		||||
		     "add %[insn], %[insn], %[tmp]\n"
 | 
			
		||||
		     "2: csrw " STR(CSR_MSTATUS) ", %[mstatus]"
 | 
			
		||||
	    : [mstatus] "+&r"(__mstatus), [insn] "=&r"(val), [tmp] "=&r"(tmp)
 | 
			
		||||
	    : [mprv] "r"(MSTATUS_MPRV | MSTATUS_MXR), [addr] "r"(__mepc),
 | 
			
		||||
	      [rvc_mask] "r"(rvc_mask), [xlen_minus_16] "i"(__riscv_xlen - 16));
 | 
			
		||||
#endif
 | 
			
		||||
	if (mstatus)
 | 
			
		||||
		*mstatus = __mstatus;
 | 
			
		||||
	return val;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										383
									
								
								lib/sbi/sbi_console.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										383
									
								
								lib/sbi/sbi_console.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,383 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sbi/sbi_platform.h>
 | 
			
		||||
#include <sbi/sbi_console.h>
 | 
			
		||||
#include <sbi/riscv_locks.h>
 | 
			
		||||
 | 
			
		||||
static const struct sbi_platform *console_plat = NULL;
 | 
			
		||||
static spinlock_t console_out_lock	       = SPIN_LOCK_INITIALIZER;
 | 
			
		||||
 | 
			
		||||
bool sbi_isprintable(char c)
 | 
			
		||||
{
 | 
			
		||||
	if (((31 < c) && (c < 127)) || (c == '\f') || (c == '\r') ||
 | 
			
		||||
	    (c == '\n') || (c == '\t')) {
 | 
			
		||||
		return TRUE;
 | 
			
		||||
	}
 | 
			
		||||
	return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sbi_getc(void)
 | 
			
		||||
{
 | 
			
		||||
	return sbi_platform_console_getc(console_plat);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void sbi_putc(char ch)
 | 
			
		||||
{
 | 
			
		||||
	if (ch == '\n')
 | 
			
		||||
		sbi_platform_console_putc(console_plat, '\r');
 | 
			
		||||
	sbi_platform_console_putc(console_plat, ch);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void sbi_puts(const char *str)
 | 
			
		||||
{
 | 
			
		||||
	spin_lock(&console_out_lock);
 | 
			
		||||
	while (*str) {
 | 
			
		||||
		sbi_putc(*str);
 | 
			
		||||
		str++;
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock(&console_out_lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void sbi_gets(char *s, int maxwidth, char endchar)
 | 
			
		||||
{
 | 
			
		||||
	int ch;
 | 
			
		||||
	char *retval = s;
 | 
			
		||||
 | 
			
		||||
	while ((ch = sbi_getc()) != endchar && ch >= 0 && maxwidth > 1) {
 | 
			
		||||
		*retval = (char)ch;
 | 
			
		||||
		retval++;
 | 
			
		||||
		maxwidth--;
 | 
			
		||||
	}
 | 
			
		||||
	*retval = '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#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) == 'u') {
 | 
			
		||||
					format += 1;
 | 
			
		||||
					pc += printi(
 | 
			
		||||
						out, out_len,
 | 
			
		||||
						va_arg(args, unsigned long), 10,
 | 
			
		||||
						0, width, flags, 'a');
 | 
			
		||||
				} 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 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;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&console_out_lock);
 | 
			
		||||
	va_start(args, format);
 | 
			
		||||
	retval = print(NULL, NULL, format, args);
 | 
			
		||||
	va_end(args);
 | 
			
		||||
	spin_unlock(&console_out_lock);
 | 
			
		||||
 | 
			
		||||
	return retval;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sbi_console_init(struct sbi_scratch *scratch)
 | 
			
		||||
{
 | 
			
		||||
	console_plat = sbi_platform_ptr(scratch);
 | 
			
		||||
 | 
			
		||||
	return sbi_platform_console_init(console_plat);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										107
									
								
								lib/sbi/sbi_ecall.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								lib/sbi/sbi_ecall.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sbi/sbi_console.h>
 | 
			
		||||
#include <sbi/sbi_ecall.h>
 | 
			
		||||
#include <sbi/sbi_ecall_interface.h>
 | 
			
		||||
#include <sbi/sbi_error.h>
 | 
			
		||||
#include <sbi/sbi_ipi.h>
 | 
			
		||||
#include <sbi/sbi_system.h>
 | 
			
		||||
#include <sbi/sbi_timer.h>
 | 
			
		||||
#include <sbi/sbi_tlb.h>
 | 
			
		||||
#include <sbi/sbi_trap.h>
 | 
			
		||||
 | 
			
		||||
#define SBI_ECALL_VERSION_MAJOR 0
 | 
			
		||||
#define SBI_ECALL_VERSION_MINOR 1
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
	struct unpriv_trap uptrap;
 | 
			
		||||
	struct sbi_tlb_info tlb_info;
 | 
			
		||||
 | 
			
		||||
	switch (regs->a7) {
 | 
			
		||||
	case SBI_ECALL_SET_TIMER:
 | 
			
		||||
#if __riscv_xlen == 32
 | 
			
		||||
		sbi_timer_event_start(scratch,
 | 
			
		||||
				      (((u64)regs->a1 << 32) | (u64)regs->a0));
 | 
			
		||||
#else
 | 
			
		||||
		sbi_timer_event_start(scratch, (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);
 | 
			
		||||
		ret = 0;
 | 
			
		||||
		break;
 | 
			
		||||
	case SBI_ECALL_SEND_IPI:
 | 
			
		||||
		ret = sbi_ipi_send_many(scratch, &uptrap, (ulong *)regs->a0,
 | 
			
		||||
					SBI_IPI_EVENT_SOFT, NULL);
 | 
			
		||||
		break;
 | 
			
		||||
	case SBI_ECALL_REMOTE_FENCE_I:
 | 
			
		||||
		ret = sbi_ipi_send_many(scratch, &uptrap, (ulong *)regs->a0,
 | 
			
		||||
					SBI_IPI_EVENT_FENCE_I, NULL);
 | 
			
		||||
		break;
 | 
			
		||||
	case SBI_ECALL_REMOTE_SFENCE_VMA:
 | 
			
		||||
		tlb_info.start = (unsigned long)regs->a1;
 | 
			
		||||
		tlb_info.size  = (unsigned long)regs->a2;
 | 
			
		||||
		tlb_info.type  = SBI_TLB_FLUSH_VMA;
 | 
			
		||||
 | 
			
		||||
		ret = sbi_ipi_send_many(scratch, &uptrap, (ulong *)regs->a0,
 | 
			
		||||
					SBI_IPI_EVENT_SFENCE_VMA, &tlb_info);
 | 
			
		||||
		break;
 | 
			
		||||
	case SBI_ECALL_REMOTE_SFENCE_VMA_ASID:
 | 
			
		||||
		tlb_info.start = (unsigned long)regs->a1;
 | 
			
		||||
		tlb_info.size  = (unsigned long)regs->a2;
 | 
			
		||||
		tlb_info.asid  = (unsigned long)regs->a3;
 | 
			
		||||
		tlb_info.type  = SBI_TLB_FLUSH_VMA_ASID;
 | 
			
		||||
 | 
			
		||||
		ret = sbi_ipi_send_many(scratch, &uptrap, (ulong *)regs->a0,
 | 
			
		||||
					SBI_IPI_EVENT_SFENCE_VMA_ASID,
 | 
			
		||||
					&tlb_info);
 | 
			
		||||
		break;
 | 
			
		||||
	case SBI_ECALL_SHUTDOWN:
 | 
			
		||||
		sbi_system_shutdown(scratch, 0);
 | 
			
		||||
		ret = 0;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		regs->a0 = SBI_ENOTSUPP;
 | 
			
		||||
		ret	 = 0;
 | 
			
		||||
		break;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	if (!ret) {
 | 
			
		||||
		regs->mepc += 4;
 | 
			
		||||
	} else if (ret == SBI_ETRAP) {
 | 
			
		||||
		ret = 0;
 | 
			
		||||
		sbi_trap_redirect(regs, scratch, regs->mepc,
 | 
			
		||||
				  uptrap.cause, uptrap.tval);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										137
									
								
								lib/sbi/sbi_emulate_csr.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								lib/sbi/sbi_emulate_csr.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#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(CSR_SCOUNTEREN);
 | 
			
		||||
 | 
			
		||||
	switch (csr_num) {
 | 
			
		||||
	case CSR_CYCLE:
 | 
			
		||||
		if (!((cen >> (CSR_CYCLE - CSR_CYCLE)) & 1))
 | 
			
		||||
			return -1;
 | 
			
		||||
		*csr_val = csr_read(CSR_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(CSR_MINSTRET);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_MHPMCOUNTER3:
 | 
			
		||||
		if (!((cen >> (3 + CSR_MHPMCOUNTER3 - CSR_MHPMCOUNTER3)) & 1))
 | 
			
		||||
			return -1;
 | 
			
		||||
		*csr_val = csr_read(CSR_MHPMCOUNTER3);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_MHPMCOUNTER4:
 | 
			
		||||
		if (!((cen >> (3 + CSR_MHPMCOUNTER4 - CSR_MHPMCOUNTER3)) & 1))
 | 
			
		||||
			return -1;
 | 
			
		||||
		*csr_val = csr_read(CSR_MHPMCOUNTER4);
 | 
			
		||||
		break;
 | 
			
		||||
#if __riscv_xlen == 32
 | 
			
		||||
	case CSR_CYCLEH:
 | 
			
		||||
		if (!((cen >> (CSR_CYCLE - CSR_CYCLE)) & 1))
 | 
			
		||||
			return -1;
 | 
			
		||||
		*csr_val = csr_read(CSR_MCYCLEH);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_TIMEH:
 | 
			
		||||
		if (!((cen >> (CSR_TIME - CSR_CYCLE)) & 1))
 | 
			
		||||
			return -1;
 | 
			
		||||
		*csr_val = sbi_timer_value(scratch) >> 32;
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_INSTRETH:
 | 
			
		||||
		if (!((cen >> (CSR_INSTRET - CSR_CYCLE)) & 1))
 | 
			
		||||
			return -1;
 | 
			
		||||
		*csr_val = csr_read(CSR_MINSTRETH);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_MHPMCOUNTER3H:
 | 
			
		||||
		if (!((cen >> (3 + CSR_MHPMCOUNTER3 - CSR_MHPMCOUNTER3)) & 1))
 | 
			
		||||
			return -1;
 | 
			
		||||
		*csr_val = csr_read(CSR_MHPMCOUNTER3H);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_MHPMCOUNTER4H:
 | 
			
		||||
		if (!((cen >> (3 + CSR_MHPMCOUNTER4 - CSR_MHPMCOUNTER3)) & 1))
 | 
			
		||||
			return -1;
 | 
			
		||||
		*csr_val = csr_read(CSR_MHPMCOUNTER4H);
 | 
			
		||||
		break;
 | 
			
		||||
#endif
 | 
			
		||||
	case CSR_MHPMEVENT3:
 | 
			
		||||
		*csr_val = csr_read(CSR_MHPMEVENT3);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_MHPMEVENT4:
 | 
			
		||||
		*csr_val = csr_read(CSR_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(CSR_MCYCLE, csr_val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_INSTRET:
 | 
			
		||||
		csr_write(CSR_MINSTRET, csr_val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_MHPMCOUNTER3:
 | 
			
		||||
		csr_write(CSR_MHPMCOUNTER3, csr_val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_MHPMCOUNTER4:
 | 
			
		||||
		csr_write(CSR_MHPMCOUNTER4, csr_val);
 | 
			
		||||
		break;
 | 
			
		||||
#if __riscv_xlen == 32
 | 
			
		||||
	case CSR_CYCLEH:
 | 
			
		||||
		csr_write(CSR_MCYCLEH, csr_val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_INSTRETH:
 | 
			
		||||
		csr_write(CSR_MINSTRETH, csr_val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_MHPMCOUNTER3H:
 | 
			
		||||
		csr_write(CSR_MHPMCOUNTER3H, csr_val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_MHPMCOUNTER4H:
 | 
			
		||||
		csr_write(CSR_MHPMCOUNTER4H, csr_val);
 | 
			
		||||
		break;
 | 
			
		||||
#endif
 | 
			
		||||
	case CSR_MHPMEVENT3:
 | 
			
		||||
		csr_write(CSR_MHPMEVENT3, csr_val);
 | 
			
		||||
		break;
 | 
			
		||||
	case CSR_MHPMEVENT4:
 | 
			
		||||
		csr_write(CSR_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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										184
									
								
								lib/sbi/sbi_fifo.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								lib/sbi/sbi_fifo.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,184 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Atish Patra<atish.patra@wdc.com>
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#include <sbi/riscv_locks.h>
 | 
			
		||||
#include <sbi/sbi_error.h>
 | 
			
		||||
#include <sbi/sbi_fifo.h>
 | 
			
		||||
#include <plat/string.h>
 | 
			
		||||
 | 
			
		||||
void sbi_fifo_init(struct sbi_fifo *fifo, void *queue_mem, u16 entries,
 | 
			
		||||
		   u16 entry_size)
 | 
			
		||||
{
 | 
			
		||||
	fifo->queue	  = queue_mem;
 | 
			
		||||
	fifo->num_entries = entries;
 | 
			
		||||
	fifo->entry_size  = entry_size;
 | 
			
		||||
	SPIN_LOCK_INIT(&fifo->qlock);
 | 
			
		||||
	fifo->avail = fifo->tail = 0;
 | 
			
		||||
	memset(fifo->queue, 0, entries * entry_size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Note: must be called with fifo->qlock held */
 | 
			
		||||
static inline bool __sbi_fifo_is_full(struct sbi_fifo *fifo)
 | 
			
		||||
{
 | 
			
		||||
	return (fifo->avail == fifo->num_entries) ? TRUE : FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u16 sbi_fifo_avail(struct sbi_fifo *fifo)
 | 
			
		||||
{
 | 
			
		||||
	u16 ret;
 | 
			
		||||
 | 
			
		||||
	if (!fifo)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&fifo->qlock);
 | 
			
		||||
	ret = fifo->avail;
 | 
			
		||||
	spin_unlock(&fifo->qlock);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool sbi_fifo_is_full(struct sbi_fifo *fifo)
 | 
			
		||||
{
 | 
			
		||||
	bool ret;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&fifo->qlock);
 | 
			
		||||
	ret = __sbi_fifo_is_full(fifo);
 | 
			
		||||
	spin_unlock(&fifo->qlock);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Note: must be called with fifo->qlock held */
 | 
			
		||||
static inline bool __sbi_fifo_is_empty(struct sbi_fifo *fifo)
 | 
			
		||||
{
 | 
			
		||||
	return (fifo->avail == 0) ? TRUE : FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool sbi_fifo_is_empty(struct sbi_fifo *fifo)
 | 
			
		||||
{
 | 
			
		||||
	bool ret;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&fifo->qlock);
 | 
			
		||||
	ret = __sbi_fifo_is_empty(fifo);
 | 
			
		||||
	spin_unlock(&fifo->qlock);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Note: must be called with fifo->qlock held */
 | 
			
		||||
static inline void __sbi_fifo_reset(struct sbi_fifo *fifo)
 | 
			
		||||
{
 | 
			
		||||
	fifo->avail = 0;
 | 
			
		||||
	fifo->tail  = 0;
 | 
			
		||||
	memset(fifo->queue, 0, fifo->num_entries * fifo->entry_size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool sbi_fifo_reset(struct sbi_fifo *fifo)
 | 
			
		||||
{
 | 
			
		||||
	if (!fifo)
 | 
			
		||||
		return FALSE;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&fifo->qlock);
 | 
			
		||||
	__sbi_fifo_reset(fifo);
 | 
			
		||||
	spin_unlock(&fifo->qlock);
 | 
			
		||||
 | 
			
		||||
	return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Provide a helper function to do inplace update to the fifo.
 | 
			
		||||
 * Note: The callback function is called with lock being held.
 | 
			
		||||
 *
 | 
			
		||||
 * **Do not** invoke any other fifo function from callback. Otherwise, it will
 | 
			
		||||
 * lead to deadlock.
 | 
			
		||||
 */
 | 
			
		||||
int sbi_fifo_inplace_update(struct sbi_fifo *fifo, void *in,
 | 
			
		||||
			    int (*fptr)(void *in, void *data))
 | 
			
		||||
{
 | 
			
		||||
	int i, index = 0;
 | 
			
		||||
	int ret = SBI_FIFO_UNCHANGED;
 | 
			
		||||
	void *entry;
 | 
			
		||||
 | 
			
		||||
	if (!fifo || !in)
 | 
			
		||||
		return ret;
 | 
			
		||||
	spin_lock(&fifo->qlock);
 | 
			
		||||
	if (__sbi_fifo_is_empty(fifo)) {
 | 
			
		||||
		spin_unlock(&fifo->qlock);
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < fifo->avail; i++) {
 | 
			
		||||
		index = fifo->tail + i;
 | 
			
		||||
		if (index >= fifo->num_entries)
 | 
			
		||||
			index = index - fifo->num_entries;
 | 
			
		||||
		entry = (void *)fifo->queue + (u32)index * fifo->entry_size;
 | 
			
		||||
		ret   = fptr(in, entry);
 | 
			
		||||
		if (ret == SBI_FIFO_SKIP || ret == SBI_FIFO_UPDATED) {
 | 
			
		||||
			break;
 | 
			
		||||
		} else if (ret == SBI_FIFO_RESET) {
 | 
			
		||||
			__sbi_fifo_reset(fifo);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock(&fifo->qlock);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sbi_fifo_enqueue(struct sbi_fifo *fifo, void *data)
 | 
			
		||||
{
 | 
			
		||||
	u32 head;
 | 
			
		||||
 | 
			
		||||
	if (!fifo || !data)
 | 
			
		||||
		return SBI_EINVAL;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&fifo->qlock);
 | 
			
		||||
 | 
			
		||||
	if (__sbi_fifo_is_full(fifo)) {
 | 
			
		||||
		spin_unlock(&fifo->qlock);
 | 
			
		||||
		return SBI_ENOSPC;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	head = (u32)fifo->tail + fifo->avail;
 | 
			
		||||
	if (head >= fifo->num_entries)
 | 
			
		||||
		head = head - fifo->num_entries;
 | 
			
		||||
 | 
			
		||||
	memcpy(fifo->queue + head * fifo->entry_size, data, fifo->entry_size);
 | 
			
		||||
 | 
			
		||||
	fifo->avail++;
 | 
			
		||||
 | 
			
		||||
	spin_unlock(&fifo->qlock);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sbi_fifo_dequeue(struct sbi_fifo *fifo, void *data)
 | 
			
		||||
{
 | 
			
		||||
	if (!fifo || !data)
 | 
			
		||||
		return SBI_EINVAL;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&fifo->qlock);
 | 
			
		||||
 | 
			
		||||
	if (__sbi_fifo_is_empty(fifo)) {
 | 
			
		||||
		spin_unlock(&fifo->qlock);
 | 
			
		||||
		return SBI_ENOENT;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memcpy(data, fifo->queue + (u32)fifo->tail * fifo->entry_size,
 | 
			
		||||
	       fifo->entry_size);
 | 
			
		||||
 | 
			
		||||
	fifo->avail--;
 | 
			
		||||
	fifo->tail++;
 | 
			
		||||
	if (fifo->tail >= fifo->num_entries)
 | 
			
		||||
		fifo->tail = 0;
 | 
			
		||||
 | 
			
		||||
	spin_unlock(&fifo->qlock);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										366
									
								
								lib/sbi/sbi_hart.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										366
									
								
								lib/sbi/sbi_hart.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,366 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sbi/riscv_asm.h>
 | 
			
		||||
#include <sbi/riscv_barrier.h>
 | 
			
		||||
#include <sbi/riscv_encoding.h>
 | 
			
		||||
#include <sbi/riscv_fp.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>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Return HART ID of the caller.
 | 
			
		||||
 */
 | 
			
		||||
unsigned int sbi_current_hartid()
 | 
			
		||||
{
 | 
			
		||||
	return (u32)csr_read(CSR_MHARTID);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void mstatus_init(struct sbi_scratch *scratch, u32 hartid)
 | 
			
		||||
{
 | 
			
		||||
	const struct sbi_platform *plat = sbi_platform_ptr(scratch);
 | 
			
		||||
 | 
			
		||||
	/* Enable FPU */
 | 
			
		||||
	if (misa_extension('D') || misa_extension('F'))
 | 
			
		||||
		csr_write(CSR_MSTATUS, MSTATUS_FS);
 | 
			
		||||
 | 
			
		||||
	/* Enable user/supervisor use of perf counters */
 | 
			
		||||
	if (misa_extension('S') && sbi_platform_has_scounteren(plat))
 | 
			
		||||
		csr_write(CSR_SCOUNTEREN, -1);
 | 
			
		||||
	if (sbi_platform_has_mcounteren(plat))
 | 
			
		||||
		csr_write(CSR_MCOUNTEREN, -1);
 | 
			
		||||
 | 
			
		||||
	/* Disable all interrupts */
 | 
			
		||||
	csr_write(CSR_MIE, 0);
 | 
			
		||||
 | 
			
		||||
	/* Disable S-mode paging */
 | 
			
		||||
	if (misa_extension('S'))
 | 
			
		||||
		csr_write(CSR_SATP, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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(CSR_MSTATUS) & MSTATUS_FS))
 | 
			
		||||
		return SBI_EINVAL;
 | 
			
		||||
 | 
			
		||||
#ifdef __riscv_flen
 | 
			
		||||
	for (i = 0; i < 32; i++)
 | 
			
		||||
		init_fp_reg(i);
 | 
			
		||||
	csr_write(CSR_FCSR, 0);
 | 
			
		||||
#else
 | 
			
		||||
	fd_mask = (1 << ('F' - 'A')) | (1 << ('D' - 'A'));
 | 
			
		||||
	csr_clear(CSR_MISA, fd_mask);
 | 
			
		||||
	if (csr_read(CSR_MISA) & fd_mask)
 | 
			
		||||
		return SBI_ENOTSUPP;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int delegate_traps(struct sbi_scratch *scratch, u32 hartid)
 | 
			
		||||
{
 | 
			
		||||
	const struct sbi_platform *plat = sbi_platform_ptr(scratch);
 | 
			
		||||
	unsigned long interrupts, exceptions;
 | 
			
		||||
 | 
			
		||||
	if (!misa_extension('S'))
 | 
			
		||||
		/* No delegation possible as mideleg does not exist*/
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	/* Send M-mode interrupts and most exceptions to S-mode */
 | 
			
		||||
	interrupts = MIP_SSIP | MIP_STIP | MIP_SEIP;
 | 
			
		||||
	exceptions = (1U << CAUSE_MISALIGNED_FETCH) | (1U << CAUSE_BREAKPOINT) |
 | 
			
		||||
		     (1U << CAUSE_USER_ECALL);
 | 
			
		||||
	if (sbi_platform_has_mfaults_delegation(plat))
 | 
			
		||||
		exceptions |= (1U << CAUSE_FETCH_PAGE_FAULT) |
 | 
			
		||||
			      (1U << CAUSE_LOAD_PAGE_FAULT) |
 | 
			
		||||
			      (1U << CAUSE_STORE_PAGE_FAULT);
 | 
			
		||||
 | 
			
		||||
	csr_write(CSR_MIDELEG, interrupts);
 | 
			
		||||
	csr_write(CSR_MEDELEG, exceptions);
 | 
			
		||||
 | 
			
		||||
	if (csr_read(CSR_MIDELEG) != interrupts)
 | 
			
		||||
		return SBI_EFAIL;
 | 
			
		||||
	if (csr_read(CSR_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(struct sbi_scratch *scratch)
 | 
			
		||||
{
 | 
			
		||||
	const struct sbi_platform *plat = sbi_platform_ptr(scratch);
 | 
			
		||||
	unsigned long prot, addr, size, l2l;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	if (!sbi_platform_has_pmp(plat))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	const struct sbi_platform *plat = sbi_platform_ptr(scratch);
 | 
			
		||||
 | 
			
		||||
	if (!sbi_platform_has_pmp(plat))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static unsigned long trap_info_offset;
 | 
			
		||||
 | 
			
		||||
int sbi_hart_init(struct sbi_scratch *scratch, u32 hartid, bool cold_boot)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	if (cold_boot) {
 | 
			
		||||
		trap_info_offset = sbi_scratch_alloc_offset(__SIZEOF_POINTER__,
 | 
			
		||||
							    "HART_TRAP_INFO");
 | 
			
		||||
		if (!trap_info_offset)
 | 
			
		||||
			return SBI_ENOMEM;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mstatus_init(scratch, hartid);
 | 
			
		||||
 | 
			
		||||
	rc = fp_init(hartid);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	rc = delegate_traps(scratch, hartid);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	return pmp_init(scratch, hartid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *sbi_hart_get_trap_info(struct sbi_scratch *scratch)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long *trap_info;
 | 
			
		||||
 | 
			
		||||
	if (!trap_info_offset)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	trap_info = sbi_scratch_offset_ptr(scratch, trap_info_offset);
 | 
			
		||||
 | 
			
		||||
	return (void *)(*trap_info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void sbi_hart_set_trap_info(struct sbi_scratch *scratch, void *data)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long *trap_info;
 | 
			
		||||
 | 
			
		||||
	if (!trap_info_offset)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	trap_info = sbi_scratch_offset_ptr(scratch, trap_info_offset);
 | 
			
		||||
	*trap_info = (unsigned long)data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __attribute__((noreturn)) sbi_hart_hang(void)
 | 
			
		||||
{
 | 
			
		||||
	while (1)
 | 
			
		||||
		wfi();
 | 
			
		||||
	__builtin_unreachable();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __attribute__((noreturn))
 | 
			
		||||
sbi_hart_switch_mode(unsigned long arg0, unsigned long arg1,
 | 
			
		||||
		     unsigned long next_addr, unsigned long next_mode)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long val;
 | 
			
		||||
 | 
			
		||||
	switch (next_mode) {
 | 
			
		||||
	case PRV_M:
 | 
			
		||||
		break;
 | 
			
		||||
	case PRV_S:
 | 
			
		||||
		if (!misa_extension('S'))
 | 
			
		||||
			sbi_hart_hang();
 | 
			
		||||
		break;
 | 
			
		||||
	case PRV_U:
 | 
			
		||||
		if (!misa_extension('U'))
 | 
			
		||||
			sbi_hart_hang();
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		sbi_hart_hang();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	val = csr_read(CSR_MSTATUS);
 | 
			
		||||
	val = INSERT_FIELD(val, MSTATUS_MPP, next_mode);
 | 
			
		||||
	val = INSERT_FIELD(val, MSTATUS_MPIE, 0);
 | 
			
		||||
 | 
			
		||||
	csr_write(CSR_MSTATUS, val);
 | 
			
		||||
	csr_write(CSR_MEPC, next_addr);
 | 
			
		||||
 | 
			
		||||
	if (next_mode == PRV_S) {
 | 
			
		||||
		csr_write(CSR_STVEC, next_addr);
 | 
			
		||||
		csr_write(CSR_SSCRATCH, 0);
 | 
			
		||||
		csr_write(CSR_SIE, 0);
 | 
			
		||||
		csr_write(CSR_SATP, 0);
 | 
			
		||||
	} else if (next_mode == PRV_U) {
 | 
			
		||||
		csr_write(CSR_UTVEC, next_addr);
 | 
			
		||||
		csr_write(CSR_USCRATCH, 0);
 | 
			
		||||
		csr_write(CSR_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 COLDBOOT_WAIT_BITMAP_SIZE __riscv_xlen
 | 
			
		||||
static spinlock_t coldboot_wait_bitmap_lock = SPIN_LOCK_INITIALIZER;
 | 
			
		||||
static unsigned long coldboot_wait_bitmap   = 0;
 | 
			
		||||
 | 
			
		||||
void sbi_hart_wait_for_coldboot(struct sbi_scratch *scratch, u32 hartid)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long mipval;
 | 
			
		||||
	const struct sbi_platform *plat = sbi_platform_ptr(scratch);
 | 
			
		||||
 | 
			
		||||
	if ((sbi_platform_hart_count(plat) <= hartid) ||
 | 
			
		||||
	    (COLDBOOT_WAIT_BITMAP_SIZE <= hartid))
 | 
			
		||||
		sbi_hart_hang();
 | 
			
		||||
 | 
			
		||||
	/* Set MSIE bit to receive IPI */
 | 
			
		||||
	csr_set(CSR_MIE, MIP_MSIP);
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		spin_lock(&coldboot_wait_bitmap_lock);
 | 
			
		||||
		coldboot_wait_bitmap |= (1UL << hartid);
 | 
			
		||||
		spin_unlock(&coldboot_wait_bitmap_lock);
 | 
			
		||||
 | 
			
		||||
		wfi();
 | 
			
		||||
		mipval = csr_read(CSR_MIP);
 | 
			
		||||
 | 
			
		||||
		spin_lock(&coldboot_wait_bitmap_lock);
 | 
			
		||||
		coldboot_wait_bitmap &= ~(1UL << hartid);
 | 
			
		||||
		spin_unlock(&coldboot_wait_bitmap_lock);
 | 
			
		||||
	} while (!(mipval && MIP_MSIP));
 | 
			
		||||
 | 
			
		||||
	csr_clear(CSR_MIP, MIP_MSIP);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void sbi_hart_wake_coldboot_harts(struct sbi_scratch *scratch, u32 hartid)
 | 
			
		||||
{
 | 
			
		||||
	const struct sbi_platform *plat = sbi_platform_ptr(scratch);
 | 
			
		||||
	int max_hart			= sbi_platform_hart_count(plat);
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < max_hart; i++) {
 | 
			
		||||
		/* send an IPI to every other hart */
 | 
			
		||||
		spin_lock(&coldboot_wait_bitmap_lock);
 | 
			
		||||
		if ((i != hartid) && (coldboot_wait_bitmap & (1UL << i)))
 | 
			
		||||
			sbi_platform_ipi_send(plat, i);
 | 
			
		||||
		spin_unlock(&coldboot_wait_bitmap_lock);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										131
									
								
								lib/sbi/sbi_illegal_insn.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								lib/sbi/sbi_illegal_insn.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sbi/riscv_asm.h>
 | 
			
		||||
#include <sbi/riscv_encoding.h>
 | 
			
		||||
#include <sbi/riscv_unpriv.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>
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
	return sbi_trap_redirect(regs, scratch, regs->mepc, mcause, insn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 insn = csr_read(mbadaddr);
 | 
			
		||||
 | 
			
		||||
	if (unlikely((insn & 3) != 3)) {
 | 
			
		||||
		if (insn == 0)
 | 
			
		||||
			insn = get_insn(regs->mepc, NULL);
 | 
			
		||||
		if ((insn & 3) != 3)
 | 
			
		||||
			return truly_illegal_insn(insn, hartid, mcause, regs,
 | 
			
		||||
						  scratch);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return illegal_insn_table[(insn & 0x7c) >> 2](insn, hartid, mcause,
 | 
			
		||||
						      regs, scratch);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										178
									
								
								lib/sbi/sbi_init.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								lib/sbi/sbi_init.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,178 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#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>
 | 
			
		||||
#include <sbi/sbi_version.h>
 | 
			
		||||
 | 
			
		||||
#define BANNER                                              \
 | 
			
		||||
	"   ____                    _____ ____ _____\n"     \
 | 
			
		||||
	"  / __ \\                  / ____|  _ \\_   _|\n"  \
 | 
			
		||||
	" | |  | |_ __   ___ _ __ | (___ | |_) || |\n"      \
 | 
			
		||||
	" | |  | | '_ \\ / _ \\ '_ \\ \\___ \\|  _ < | |\n" \
 | 
			
		||||
	" | |__| | |_) |  __/ | | |____) | |_) || |_\n"     \
 | 
			
		||||
	"  \\____/| .__/ \\___|_| |_|_____/|____/_____|\n"  \
 | 
			
		||||
	"        | |\n"                                     \
 | 
			
		||||
	"        |_|\n\n"
 | 
			
		||||
 | 
			
		||||
static void sbi_boot_prints(struct sbi_scratch *scratch, u32 hartid)
 | 
			
		||||
{
 | 
			
		||||
	char str[64];
 | 
			
		||||
	const struct sbi_platform *plat = sbi_platform_ptr(scratch);
 | 
			
		||||
 | 
			
		||||
	misa_string(str, sizeof(str));
 | 
			
		||||
	sbi_printf("\nOpenSBI v%d.%d (%s %s)\n", OPENSBI_VERSION_MAJOR,
 | 
			
		||||
		   OPENSBI_VERSION_MINOR, __DATE__, __TIME__);
 | 
			
		||||
 | 
			
		||||
	sbi_printf(BANNER);
 | 
			
		||||
 | 
			
		||||
	/* 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));
 | 
			
		||||
	sbi_printf("Current Hart           : %u\n", hartid);
 | 
			
		||||
	/* 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(scratch);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	const struct sbi_platform *plat = sbi_platform_ptr(scratch);
 | 
			
		||||
 | 
			
		||||
	rc = sbi_system_early_init(scratch, TRUE);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		sbi_hart_hang();
 | 
			
		||||
 | 
			
		||||
	rc = sbi_hart_init(scratch, hartid, TRUE);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		sbi_hart_hang();
 | 
			
		||||
 | 
			
		||||
	rc = sbi_console_init(scratch);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		sbi_hart_hang();
 | 
			
		||||
 | 
			
		||||
	rc = sbi_platform_irqchip_init(plat, TRUE);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		sbi_hart_hang();
 | 
			
		||||
 | 
			
		||||
	rc = sbi_ipi_init(scratch, TRUE);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		sbi_hart_hang();
 | 
			
		||||
 | 
			
		||||
	rc = sbi_timer_init(scratch, TRUE);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		sbi_hart_hang();
 | 
			
		||||
 | 
			
		||||
	rc = sbi_system_final_init(scratch, TRUE);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		sbi_hart_hang();
 | 
			
		||||
 | 
			
		||||
	if (!(scratch->options & SBI_SCRATCH_NO_BOOT_PRINTS))
 | 
			
		||||
		sbi_boot_prints(scratch, hartid);
 | 
			
		||||
 | 
			
		||||
	if (!sbi_platform_has_hart_hotplug(plat))
 | 
			
		||||
		sbi_hart_wake_coldboot_harts(scratch, hartid);
 | 
			
		||||
	sbi_hart_mark_available(hartid);
 | 
			
		||||
	sbi_hart_switch_mode(hartid, scratch->next_arg1, scratch->next_addr,
 | 
			
		||||
			     scratch->next_mode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __noreturn init_warmboot(struct sbi_scratch *scratch, u32 hartid)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	const struct sbi_platform *plat = sbi_platform_ptr(scratch);
 | 
			
		||||
 | 
			
		||||
	if (!sbi_platform_has_hart_hotplug(plat))
 | 
			
		||||
		sbi_hart_wait_for_coldboot(scratch, hartid);
 | 
			
		||||
 | 
			
		||||
	if (sbi_platform_hart_disabled(plat, hartid))
 | 
			
		||||
		sbi_hart_hang();
 | 
			
		||||
 | 
			
		||||
	rc = sbi_system_early_init(scratch, FALSE);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		sbi_hart_hang();
 | 
			
		||||
 | 
			
		||||
	rc = sbi_hart_init(scratch, hartid, FALSE);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		sbi_hart_hang();
 | 
			
		||||
 | 
			
		||||
	rc = sbi_platform_irqchip_init(plat, FALSE);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		sbi_hart_hang();
 | 
			
		||||
 | 
			
		||||
	rc = sbi_ipi_init(scratch, FALSE);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		sbi_hart_hang();
 | 
			
		||||
 | 
			
		||||
	rc = sbi_timer_init(scratch, FALSE);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		sbi_hart_hang();
 | 
			
		||||
 | 
			
		||||
	rc = sbi_system_final_init(scratch, FALSE);
 | 
			
		||||
	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_switch_mode(hartid, scratch->next_arg1,
 | 
			
		||||
				     scratch->next_addr, scratch->next_mode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static atomic_t coldboot_lottery = ATOMIC_INITIALIZER(0);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Initialize OpenSBI library for current HART and jump to next
 | 
			
		||||
 * booting stage.
 | 
			
		||||
 *
 | 
			
		||||
 * The function expects following:
 | 
			
		||||
 * 1. The 'mscratch' CSR is pointing to sbi_scratch of current HART
 | 
			
		||||
 * 2. Stack pointer (SP) is setup for current HART
 | 
			
		||||
 * 3. Interrupts are disabled in MSTATUS CSR
 | 
			
		||||
 * 4. All interrupts are disabled in MIE CSR
 | 
			
		||||
 *
 | 
			
		||||
 * @param scratch pointer to sbi_scratch of current HART
 | 
			
		||||
 */
 | 
			
		||||
void __noreturn sbi_init(struct sbi_scratch *scratch)
 | 
			
		||||
{
 | 
			
		||||
	bool coldboot			= FALSE;
 | 
			
		||||
	u32 hartid			= sbi_current_hartid();
 | 
			
		||||
	const struct sbi_platform *plat = sbi_platform_ptr(scratch);
 | 
			
		||||
 | 
			
		||||
	if (sbi_platform_hart_disabled(plat, hartid))
 | 
			
		||||
		sbi_hart_hang();
 | 
			
		||||
 | 
			
		||||
	if (atomic_add_return(&coldboot_lottery, 1) == 1)
 | 
			
		||||
		coldboot = TRUE;
 | 
			
		||||
 | 
			
		||||
	if (coldboot)
 | 
			
		||||
		init_coldboot(scratch, hartid);
 | 
			
		||||
	else
 | 
			
		||||
		init_warmboot(scratch, hartid);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										153
									
								
								lib/sbi/sbi_ipi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								lib/sbi/sbi_ipi.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,153 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
 *   Nick Kossifidis <mick@ics.forth.gr>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sbi/riscv_asm.h>
 | 
			
		||||
#include <sbi/riscv_barrier.h>
 | 
			
		||||
#include <sbi/riscv_atomic.h>
 | 
			
		||||
#include <sbi/riscv_unpriv.h>
 | 
			
		||||
#include <sbi/sbi_error.h>
 | 
			
		||||
#include <sbi/sbi_bitops.h>
 | 
			
		||||
#include <sbi/sbi_hart.h>
 | 
			
		||||
#include <sbi/sbi_ipi.h>
 | 
			
		||||
#include <sbi/sbi_platform.h>
 | 
			
		||||
#include <sbi/sbi_timer.h>
 | 
			
		||||
#include <sbi/sbi_tlb.h>
 | 
			
		||||
#include <plat/string.h>
 | 
			
		||||
 | 
			
		||||
static unsigned long ipi_data_off;
 | 
			
		||||
 | 
			
		||||
static int sbi_ipi_send(struct sbi_scratch *scratch, u32 hartid, u32 event,
 | 
			
		||||
			void *data)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	struct sbi_scratch *remote_scratch = NULL;
 | 
			
		||||
	const struct sbi_platform *plat = sbi_platform_ptr(scratch);
 | 
			
		||||
	struct sbi_ipi_data *ipi_data;
 | 
			
		||||
 | 
			
		||||
	if (sbi_platform_hart_disabled(plat, hartid))
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Set IPI type on remote hart's scratch area and
 | 
			
		||||
	 * trigger the interrupt
 | 
			
		||||
	 */
 | 
			
		||||
	remote_scratch = sbi_hart_id_to_scratch(scratch, hartid);
 | 
			
		||||
	ipi_data = sbi_scratch_offset_ptr(remote_scratch, ipi_data_off);
 | 
			
		||||
	if (event == SBI_IPI_EVENT_SFENCE_VMA ||
 | 
			
		||||
	    event == SBI_IPI_EVENT_SFENCE_VMA_ASID) {
 | 
			
		||||
		ret = sbi_tlb_fifo_update(remote_scratch, event, data);
 | 
			
		||||
		if (ret > 0)
 | 
			
		||||
			goto done;
 | 
			
		||||
		else if (ret < 0)
 | 
			
		||||
			return ret;
 | 
			
		||||
	}
 | 
			
		||||
	atomic_raw_set_bit(event, &ipi_data->ipi_type);
 | 
			
		||||
	mb();
 | 
			
		||||
	sbi_platform_ipi_send(plat, hartid);
 | 
			
		||||
	if (event != SBI_IPI_EVENT_SOFT)
 | 
			
		||||
		sbi_platform_ipi_sync(plat, hartid);
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sbi_ipi_send_many(struct sbi_scratch *scratch, struct unpriv_trap *uptrap,
 | 
			
		||||
		      ulong *pmask, u32 event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	ulong i, m;
 | 
			
		||||
	ulong mask = sbi_hart_available_mask();
 | 
			
		||||
	u32 hartid = sbi_current_hartid();
 | 
			
		||||
 | 
			
		||||
	if (pmask) {
 | 
			
		||||
		mask &= load_ulong(pmask, scratch, uptrap);
 | 
			
		||||
		if (uptrap->cause)
 | 
			
		||||
			return SBI_ETRAP;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* send IPIs to every other hart on the set */
 | 
			
		||||
	for (i = 0, m = mask; m; i++, m >>= 1)
 | 
			
		||||
		if ((m & 1UL) && (i != hartid))
 | 
			
		||||
			sbi_ipi_send(scratch, i, event, data);
 | 
			
		||||
 | 
			
		||||
	/* If the current hart is on the set, send an IPI
 | 
			
		||||
	 * to it as well
 | 
			
		||||
	 */
 | 
			
		||||
	if (mask & (1UL << hartid))
 | 
			
		||||
		sbi_ipi_send(scratch, hartid, event, data);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void sbi_ipi_clear_smode(struct sbi_scratch *scratch)
 | 
			
		||||
{
 | 
			
		||||
	csr_clear(CSR_MIP, MIP_SSIP);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void sbi_ipi_process(struct sbi_scratch *scratch)
 | 
			
		||||
{
 | 
			
		||||
	volatile unsigned long ipi_type;
 | 
			
		||||
	unsigned int ipi_event;
 | 
			
		||||
	const struct sbi_platform *plat = sbi_platform_ptr(scratch);
 | 
			
		||||
	struct sbi_ipi_data *ipi_data =
 | 
			
		||||
			sbi_scratch_offset_ptr(scratch, ipi_data_off);
 | 
			
		||||
 | 
			
		||||
	u32 hartid = sbi_current_hartid();
 | 
			
		||||
	sbi_platform_ipi_clear(plat, hartid);
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		ipi_type = ipi_data->ipi_type;
 | 
			
		||||
		rmb();
 | 
			
		||||
		ipi_event = __ffs(ipi_type);
 | 
			
		||||
		switch (ipi_event) {
 | 
			
		||||
		case SBI_IPI_EVENT_SOFT:
 | 
			
		||||
			csr_set(CSR_MIP, MIP_SSIP);
 | 
			
		||||
			break;
 | 
			
		||||
		case SBI_IPI_EVENT_FENCE_I:
 | 
			
		||||
			__asm__ __volatile("fence.i");
 | 
			
		||||
			break;
 | 
			
		||||
		case SBI_IPI_EVENT_SFENCE_VMA:
 | 
			
		||||
		case SBI_IPI_EVENT_SFENCE_VMA_ASID:
 | 
			
		||||
			sbi_tlb_fifo_process(scratch, ipi_event);
 | 
			
		||||
			break;
 | 
			
		||||
		case SBI_IPI_EVENT_HALT:
 | 
			
		||||
			sbi_hart_hang();
 | 
			
		||||
			break;
 | 
			
		||||
		};
 | 
			
		||||
		ipi_type = atomic_raw_clear_bit(ipi_event, &ipi_data->ipi_type);
 | 
			
		||||
	} while (ipi_type > 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sbi_ipi_init(struct sbi_scratch *scratch, bool cold_boot)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	struct sbi_ipi_data *ipi_data;
 | 
			
		||||
 | 
			
		||||
	if (cold_boot) {
 | 
			
		||||
		ipi_data_off = sbi_scratch_alloc_offset(sizeof(*ipi_data),
 | 
			
		||||
							"IPI_DATA");
 | 
			
		||||
		if (!ipi_data_off)
 | 
			
		||||
			return SBI_ENOMEM;
 | 
			
		||||
	} else {
 | 
			
		||||
		if (!ipi_data_off)
 | 
			
		||||
			return SBI_ENOMEM;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ipi_data = sbi_scratch_offset_ptr(scratch, ipi_data_off);
 | 
			
		||||
	ipi_data->ipi_type = 0x00;
 | 
			
		||||
 | 
			
		||||
	ret = sbi_tlb_fifo_init(scratch, cold_boot);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	/* Enable software interrupts */
 | 
			
		||||
	csr_set(CSR_MIE, MIP_MSIP);
 | 
			
		||||
 | 
			
		||||
	return sbi_platform_ipi_init(sbi_platform_ptr(scratch), cold_boot);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										191
									
								
								lib/sbi/sbi_misaligned_ldst.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								lib/sbi/sbi_misaligned_ldst.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,191 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sbi/riscv_asm.h>
 | 
			
		||||
#include <sbi/riscv_encoding.h>
 | 
			
		||||
#include <sbi/riscv_unpriv.h>
 | 
			
		||||
#include <sbi/riscv_fp.h>
 | 
			
		||||
#include <sbi/sbi_error.h>
 | 
			
		||||
#include <sbi/sbi_misaligned_ldst.h>
 | 
			
		||||
#include <sbi/sbi_trap.h>
 | 
			
		||||
 | 
			
		||||
union reg_data {
 | 
			
		||||
	u8 data_bytes[8];
 | 
			
		||||
	ulong data_ulong;
 | 
			
		||||
	u64 data_u64;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int sbi_misaligned_load_handler(u32 hartid, ulong mcause,
 | 
			
		||||
				struct sbi_trap_regs *regs,
 | 
			
		||||
				struct sbi_scratch *scratch)
 | 
			
		||||
{
 | 
			
		||||
	union reg_data val;
 | 
			
		||||
	struct unpriv_trap uptrap;
 | 
			
		||||
	ulong insn = get_insn(regs->mepc, NULL);
 | 
			
		||||
	ulong addr = csr_read(CSR_MTVAL);
 | 
			
		||||
	int i, fp = 0, shift = 0, len = 0;
 | 
			
		||||
 | 
			
		||||
	if ((insn & INSN_MASK_LW) == INSN_MATCH_LW) {
 | 
			
		||||
		len   = 4;
 | 
			
		||||
		shift = 8 * (sizeof(ulong) - len);
 | 
			
		||||
#if __riscv_xlen == 64
 | 
			
		||||
	} else if ((insn & INSN_MASK_LD) == INSN_MATCH_LD) {
 | 
			
		||||
		len   = 8;
 | 
			
		||||
		shift = 8 * (sizeof(ulong) - len);
 | 
			
		||||
	} else if ((insn & INSN_MASK_LWU) == INSN_MATCH_LWU) {
 | 
			
		||||
		len = 4;
 | 
			
		||||
#endif
 | 
			
		||||
	} else if ((insn & INSN_MASK_FLD) == INSN_MATCH_FLD) {
 | 
			
		||||
		fp  = 1;
 | 
			
		||||
		len = 8;
 | 
			
		||||
	} else if ((insn & INSN_MASK_FLW) == INSN_MATCH_FLW) {
 | 
			
		||||
		fp  = 1;
 | 
			
		||||
		len = 4;
 | 
			
		||||
	} else if ((insn & INSN_MASK_LH) == INSN_MATCH_LH) {
 | 
			
		||||
		len   = 2;
 | 
			
		||||
		shift = 8 * (sizeof(ulong) - len);
 | 
			
		||||
	} else if ((insn & INSN_MASK_LHU) == INSN_MATCH_LHU) {
 | 
			
		||||
		len = 2;
 | 
			
		||||
#ifdef __riscv_compressed
 | 
			
		||||
#if __riscv_xlen >= 64
 | 
			
		||||
	} else if ((insn & INSN_MASK_C_LD) == INSN_MATCH_C_LD) {
 | 
			
		||||
		len   = 8;
 | 
			
		||||
		shift = 8 * (sizeof(ulong) - len);
 | 
			
		||||
		insn  = RVC_RS2S(insn) << SH_RD;
 | 
			
		||||
	} else if ((insn & INSN_MASK_C_LDSP) == INSN_MATCH_C_LDSP &&
 | 
			
		||||
		   ((insn >> SH_RD) & 0x1f)) {
 | 
			
		||||
		len   = 8;
 | 
			
		||||
		shift = 8 * (sizeof(ulong) - len);
 | 
			
		||||
#endif
 | 
			
		||||
	} else if ((insn & INSN_MASK_C_LW) == INSN_MATCH_C_LW) {
 | 
			
		||||
		len   = 4;
 | 
			
		||||
		shift = 8 * (sizeof(ulong) - len);
 | 
			
		||||
		insn  = RVC_RS2S(insn) << SH_RD;
 | 
			
		||||
	} else if ((insn & INSN_MASK_C_LWSP) == INSN_MATCH_C_LWSP &&
 | 
			
		||||
		   ((insn >> SH_RD) & 0x1f)) {
 | 
			
		||||
		len   = 4;
 | 
			
		||||
		shift = 8 * (sizeof(ulong) - len);
 | 
			
		||||
	} else if ((insn & INSN_MASK_C_FLD) == INSN_MATCH_C_FLD) {
 | 
			
		||||
		fp   = 1;
 | 
			
		||||
		len  = 8;
 | 
			
		||||
		insn = RVC_RS2S(insn) << SH_RD;
 | 
			
		||||
	} else if ((insn & INSN_MASK_C_FLDSP) == INSN_MATCH_C_FLDSP) {
 | 
			
		||||
		fp  = 1;
 | 
			
		||||
		len = 8;
 | 
			
		||||
#if __riscv_xlen == 32
 | 
			
		||||
	} else if ((insn & INSN_MASK_C_FLW) == INSN_MATCH_C_FLW) {
 | 
			
		||||
		fp   = 1;
 | 
			
		||||
		len  = 4;
 | 
			
		||||
		insn = RVC_RS2S(insn) << SH_RD;
 | 
			
		||||
	} else if ((insn & INSN_MASK_C_FLWSP) == INSN_MATCH_C_FLWSP) {
 | 
			
		||||
		fp  = 1;
 | 
			
		||||
		len = 4;
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
	} else
 | 
			
		||||
		return SBI_EILL;
 | 
			
		||||
 | 
			
		||||
	val.data_u64 = 0;
 | 
			
		||||
	for (i = 0; i < len; i++) {
 | 
			
		||||
		val.data_bytes[i] = load_u8((void *)(addr + i),
 | 
			
		||||
					    scratch, &uptrap);
 | 
			
		||||
		if (uptrap.cause) {
 | 
			
		||||
			sbi_trap_redirect(regs, scratch, regs->mepc,
 | 
			
		||||
					  uptrap.cause, uptrap.tval);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!fp)
 | 
			
		||||
		SET_RD(insn, regs, val.data_ulong << shift >> shift);
 | 
			
		||||
	else if (len == 8)
 | 
			
		||||
		SET_F64_RD(insn, regs, val.data_u64);
 | 
			
		||||
	else
 | 
			
		||||
		SET_F32_RD(insn, regs, val.data_ulong);
 | 
			
		||||
 | 
			
		||||
	regs->mepc += INSN_LEN(insn);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sbi_misaligned_store_handler(u32 hartid, ulong mcause,
 | 
			
		||||
				 struct sbi_trap_regs *regs,
 | 
			
		||||
				 struct sbi_scratch *scratch)
 | 
			
		||||
{
 | 
			
		||||
	union reg_data val;
 | 
			
		||||
	struct unpriv_trap uptrap;
 | 
			
		||||
	ulong insn = get_insn(regs->mepc, NULL);
 | 
			
		||||
	ulong addr = csr_read(CSR_MTVAL);
 | 
			
		||||
	int i, len = 0;
 | 
			
		||||
 | 
			
		||||
	val.data_ulong = GET_RS2(insn, regs);
 | 
			
		||||
 | 
			
		||||
	if ((insn & INSN_MASK_SW) == INSN_MATCH_SW) {
 | 
			
		||||
		len = 4;
 | 
			
		||||
#if __riscv_xlen == 64
 | 
			
		||||
	} else if ((insn & INSN_MASK_SD) == INSN_MATCH_SD) {
 | 
			
		||||
		len = 8;
 | 
			
		||||
#endif
 | 
			
		||||
	} else if ((insn & INSN_MASK_FSD) == INSN_MATCH_FSD) {
 | 
			
		||||
		len	     = 8;
 | 
			
		||||
		val.data_u64 = GET_F64_RS2(insn, regs);
 | 
			
		||||
	} else if ((insn & INSN_MASK_FSW) == INSN_MATCH_FSW) {
 | 
			
		||||
		len	       = 4;
 | 
			
		||||
		val.data_ulong = GET_F32_RS2(insn, regs);
 | 
			
		||||
	} else if ((insn & INSN_MASK_SH) == INSN_MATCH_SH) {
 | 
			
		||||
		len = 2;
 | 
			
		||||
#ifdef __riscv_compressed
 | 
			
		||||
#if __riscv_xlen >= 64
 | 
			
		||||
	} else if ((insn & INSN_MASK_C_SD) == INSN_MATCH_C_SD) {
 | 
			
		||||
		len	       = 8;
 | 
			
		||||
		val.data_ulong = GET_RS2S(insn, regs);
 | 
			
		||||
	} else if ((insn & INSN_MASK_C_SDSP) == INSN_MATCH_C_SDSP &&
 | 
			
		||||
		   ((insn >> SH_RD) & 0x1f)) {
 | 
			
		||||
		len	       = 8;
 | 
			
		||||
		val.data_ulong = GET_RS2C(insn, regs);
 | 
			
		||||
#endif
 | 
			
		||||
	} else if ((insn & INSN_MASK_C_SW) == INSN_MATCH_C_SW) {
 | 
			
		||||
		len	       = 4;
 | 
			
		||||
		val.data_ulong = GET_RS2S(insn, regs);
 | 
			
		||||
	} else if ((insn & INSN_MASK_C_SWSP) == INSN_MATCH_C_SWSP &&
 | 
			
		||||
		   ((insn >> SH_RD) & 0x1f)) {
 | 
			
		||||
		len	       = 4;
 | 
			
		||||
		val.data_ulong = GET_RS2C(insn, regs);
 | 
			
		||||
	} else if ((insn & INSN_MASK_C_FSD) == INSN_MATCH_C_FSD) {
 | 
			
		||||
		len	     = 8;
 | 
			
		||||
		val.data_u64 = GET_F64_RS2S(insn, regs);
 | 
			
		||||
	} else if ((insn & INSN_MASK_C_FSDSP) == INSN_MATCH_C_FSDSP) {
 | 
			
		||||
		len	     = 8;
 | 
			
		||||
		val.data_u64 = GET_F64_RS2C(insn, regs);
 | 
			
		||||
#if __riscv_xlen == 32
 | 
			
		||||
	} else if ((insn & INSN_MASK_C_FSW) == INSN_MATCH_C_FSW) {
 | 
			
		||||
		len	       = 4;
 | 
			
		||||
		val.data_ulong = GET_F32_RS2S(insn, regs);
 | 
			
		||||
	} else if ((insn & INSN_MASK_C_FSWSP) == INSN_MATCH_C_FSWSP) {
 | 
			
		||||
		len	       = 4;
 | 
			
		||||
		val.data_ulong = GET_F32_RS2C(insn, regs);
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
	} else
 | 
			
		||||
		return SBI_EILL;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < len; i++) {
 | 
			
		||||
		store_u8((void *)(addr + i), val.data_bytes[i],
 | 
			
		||||
			 scratch, &uptrap);
 | 
			
		||||
		if (uptrap.cause) {
 | 
			
		||||
			sbi_trap_redirect(regs, scratch, regs->mepc,
 | 
			
		||||
					  uptrap.cause, uptrap.tval);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	regs->mepc += INSN_LEN(insn);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								lib/sbi/sbi_scratch.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								lib/sbi/sbi_scratch.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
 /*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sbi/riscv_locks.h>
 | 
			
		||||
#include <sbi/sbi_scratch.h>
 | 
			
		||||
 | 
			
		||||
static spinlock_t extra_lock = SPIN_LOCK_INITIALIZER;
 | 
			
		||||
static unsigned long extra_offset = SBI_SCRATCH_EXTRA_SPACE_OFFSET;
 | 
			
		||||
 | 
			
		||||
unsigned long sbi_scratch_alloc_offset(unsigned long size, const char *owner)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long ret = 0;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * We have a simple brain-dead allocator which never expects
 | 
			
		||||
	 * anything to be free-ed hence it keeps incrementing the
 | 
			
		||||
	 * next allocation offset until it runs-out of space.
 | 
			
		||||
	 *
 | 
			
		||||
	 * In future, we will have more sophisticated allocator which
 | 
			
		||||
	 * will allow us to re-claim free-ed space.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	if (!size)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	while (size & (__SIZEOF_POINTER__ - 1))
 | 
			
		||||
		size++;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&extra_lock);
 | 
			
		||||
 | 
			
		||||
	if (SBI_SCRATCH_SIZE < (extra_offset + size))
 | 
			
		||||
		goto done;
 | 
			
		||||
 | 
			
		||||
	ret = extra_offset;
 | 
			
		||||
	extra_offset += size;
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	spin_unlock(&extra_lock);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void sbi_scratch_free_offset(unsigned long offset)
 | 
			
		||||
{
 | 
			
		||||
	if ((offset < SBI_SCRATCH_EXTRA_SPACE_OFFSET) ||
 | 
			
		||||
	    (SBI_SCRATCH_SIZE <= offset))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * We don't actually free-up because it's a simple
 | 
			
		||||
	 * brain-dead allocator.
 | 
			
		||||
	 */
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										45
									
								
								lib/sbi/sbi_system.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								lib/sbi/sbi_system.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
 *   Nick Kossifidis <mick@ics.forth.gr>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sbi/sbi_hart.h>
 | 
			
		||||
#include <sbi/sbi_platform.h>
 | 
			
		||||
#include <sbi/sbi_system.h>
 | 
			
		||||
#include <sbi/sbi_ipi.h>
 | 
			
		||||
 | 
			
		||||
int sbi_system_early_init(struct sbi_scratch *scratch, bool cold_boot)
 | 
			
		||||
{
 | 
			
		||||
	return sbi_platform_early_init(sbi_platform_ptr(scratch), cold_boot);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sbi_system_final_init(struct sbi_scratch *scratch, bool cold_boot)
 | 
			
		||||
{
 | 
			
		||||
	return sbi_platform_final_init(sbi_platform_ptr(scratch), cold_boot);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
	/* First try the platform-specific method */
 | 
			
		||||
	sbi_platform_system_shutdown(sbi_platform_ptr(scratch), type);
 | 
			
		||||
 | 
			
		||||
	/* If that fails (or is not implemented) send an IPI on every
 | 
			
		||||
	 * hart to hang and then hang the current hart */
 | 
			
		||||
	sbi_ipi_send_many(scratch, NULL, NULL, SBI_IPI_EVENT_HALT, NULL);
 | 
			
		||||
 | 
			
		||||
	sbi_hart_hang();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										68
									
								
								lib/sbi/sbi_timer.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								lib/sbi/sbi_timer.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#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)
 | 
			
		||||
{
 | 
			
		||||
	const struct sbi_platform *plat = sbi_platform_ptr(scratch);
 | 
			
		||||
 | 
			
		||||
	if (sbi_platform_has_timer_value(plat))
 | 
			
		||||
		return sbi_platform_timer_value(plat);
 | 
			
		||||
	else
 | 
			
		||||
		return get_ticks();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void sbi_timer_event_stop(struct sbi_scratch *scratch)
 | 
			
		||||
{
 | 
			
		||||
	sbi_platform_timer_event_stop(sbi_platform_ptr(scratch));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void sbi_timer_event_start(struct sbi_scratch *scratch, u64 next_event)
 | 
			
		||||
{
 | 
			
		||||
	sbi_platform_timer_event_start(sbi_platform_ptr(scratch), next_event);
 | 
			
		||||
	csr_clear(CSR_MIP, MIP_STIP);
 | 
			
		||||
	csr_set(CSR_MIE, MIP_MTIP);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void sbi_timer_process(struct sbi_scratch *scratch)
 | 
			
		||||
{
 | 
			
		||||
	csr_clear(CSR_MIE, MIP_MTIP);
 | 
			
		||||
	csr_set(CSR_MIP, MIP_STIP);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sbi_timer_init(struct sbi_scratch *scratch, bool cold_boot)
 | 
			
		||||
{
 | 
			
		||||
	return sbi_platform_timer_init(sbi_platform_ptr(scratch), cold_boot);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										227
									
								
								lib/sbi/sbi_tlb.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								lib/sbi/sbi_tlb.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,227 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Atish Patra <atish.patra@wdc.com>
 | 
			
		||||
 *   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sbi/riscv_asm.h>
 | 
			
		||||
#include <sbi/riscv_barrier.h>
 | 
			
		||||
#include <sbi/sbi_error.h>
 | 
			
		||||
#include <sbi/sbi_fifo.h>
 | 
			
		||||
#include <sbi/sbi_hart.h>
 | 
			
		||||
#include <sbi/sbi_bitops.h>
 | 
			
		||||
#include <sbi/sbi_scratch.h>
 | 
			
		||||
#include <sbi/sbi_tlb.h>
 | 
			
		||||
#include <plat/string.h>
 | 
			
		||||
 | 
			
		||||
static unsigned long ipi_tlb_fifo_off;
 | 
			
		||||
static unsigned long ipi_tlb_fifo_mem_off;
 | 
			
		||||
 | 
			
		||||
static inline int __sbi_tlb_fifo_range_check(struct sbi_tlb_info *curr,
 | 
			
		||||
					     struct sbi_tlb_info *next)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long curr_end;
 | 
			
		||||
	unsigned long next_end;
 | 
			
		||||
	int ret = SBI_FIFO_UNCHANGED;
 | 
			
		||||
 | 
			
		||||
	if (!curr || !next)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	next_end = next->start + next->size;
 | 
			
		||||
	curr_end = curr->start + curr->size;
 | 
			
		||||
	if (next->start <= curr->start && next_end > curr_end) {
 | 
			
		||||
		curr->start = next->start;
 | 
			
		||||
		curr->size  = next->size;
 | 
			
		||||
		ret	    = SBI_FIFO_UPDATED;
 | 
			
		||||
	} else if (next->start >= curr->start && next_end <= curr_end) {
 | 
			
		||||
		ret = SBI_FIFO_SKIP;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Call back to decide if an inplace fifo update is required or next entry can
 | 
			
		||||
 * can be skipped. Here are the different cases that are being handled.
 | 
			
		||||
 *
 | 
			
		||||
 * Case1:
 | 
			
		||||
 *	if next flush request range lies within one of the existing entry, skip
 | 
			
		||||
 *	the next entry.
 | 
			
		||||
 * Case2:
 | 
			
		||||
 *	if flush request range in current fifo entry lies within next flush
 | 
			
		||||
 *	request, update the current entry.
 | 
			
		||||
 * Case3:
 | 
			
		||||
	if a complete vma flush is requested, then all entries can be deleted
 | 
			
		||||
	and new request can be enqueued. This will not be done for ASID case
 | 
			
		||||
	as that means we have to iterate again in the fifo to figure out which
 | 
			
		||||
	entries belong to that ASID.
 | 
			
		||||
 */
 | 
			
		||||
static int sbi_tlb_fifo_update_cb(void *in, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct sbi_tlb_info *curr;
 | 
			
		||||
	struct sbi_tlb_info *next;
 | 
			
		||||
	int ret = SBI_FIFO_UNCHANGED;
 | 
			
		||||
 | 
			
		||||
	if (!in && !!data)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	curr = (struct sbi_tlb_info *)data;
 | 
			
		||||
	next = (struct sbi_tlb_info *)in;
 | 
			
		||||
	if (next->type == SBI_TLB_FLUSH_VMA_ASID &&
 | 
			
		||||
	    curr->type == SBI_TLB_FLUSH_VMA_ASID) {
 | 
			
		||||
		if (next->asid == curr->asid)
 | 
			
		||||
			ret = __sbi_tlb_fifo_range_check(curr, next);
 | 
			
		||||
	} else if (next->type == SBI_TLB_FLUSH_VMA &&
 | 
			
		||||
		   curr->type == SBI_TLB_FLUSH_VMA) {
 | 
			
		||||
		if (next->size == SBI_TLB_FLUSH_ALL)
 | 
			
		||||
			ret = SBI_FIFO_RESET;
 | 
			
		||||
		else
 | 
			
		||||
			ret = __sbi_tlb_fifo_range_check(curr, next);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sbi_tlb_fifo_update(struct sbi_scratch *scratch, u32 event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	struct sbi_fifo *ipi_tlb_fifo;
 | 
			
		||||
	struct sbi_tlb_info *tinfo = data;
 | 
			
		||||
 | 
			
		||||
	ipi_tlb_fifo = sbi_scratch_offset_ptr(scratch,
 | 
			
		||||
					      ipi_tlb_fifo_off);
 | 
			
		||||
	/*
 | 
			
		||||
	 * If address range to flush is too big then simply
 | 
			
		||||
	 * upgrade it to flush all because we can only flush
 | 
			
		||||
	 * 4KB at a time.
 | 
			
		||||
	 */
 | 
			
		||||
	if (tinfo->size >= SBI_TLB_FLUSH_MAX_SIZE) {
 | 
			
		||||
		tinfo->start = 0;
 | 
			
		||||
		tinfo->size = SBI_TLB_FLUSH_ALL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = sbi_fifo_inplace_update(ipi_tlb_fifo, data,
 | 
			
		||||
				      sbi_tlb_fifo_update_cb);
 | 
			
		||||
	if (ret == SBI_FIFO_SKIP || ret == SBI_FIFO_UPDATED) {
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while (sbi_fifo_enqueue(ipi_tlb_fifo, data) < 0) {
 | 
			
		||||
		/**
 | 
			
		||||
		 * For now, Busy loop until there is space in the fifo.
 | 
			
		||||
		 * There may be case where target hart is also
 | 
			
		||||
		 * enqueue in source hart's fifo. Both hart may busy
 | 
			
		||||
		 * loop leading to a deadlock.
 | 
			
		||||
		 * TODO: Introduce a wait/wakeup event mechansim to handle
 | 
			
		||||
		 * this properly.
 | 
			
		||||
		 */
 | 
			
		||||
		__asm__ __volatile("nop");
 | 
			
		||||
		__asm__ __volatile("nop");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sbi_tlb_flush_all(void)
 | 
			
		||||
{
 | 
			
		||||
	__asm__ __volatile("sfence.vma");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sbi_tlb_fifo_sfence_vma(struct sbi_tlb_info *tinfo)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long start = tinfo->start;
 | 
			
		||||
	unsigned long size  = tinfo->size;
 | 
			
		||||
	unsigned long i;
 | 
			
		||||
 | 
			
		||||
	if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) {
 | 
			
		||||
		sbi_tlb_flush_all();
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < size; i += PAGE_SIZE) {
 | 
			
		||||
		__asm__ __volatile__("sfence.vma %0"
 | 
			
		||||
				     :
 | 
			
		||||
				     : "r"(start + i)
 | 
			
		||||
				     : "memory");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sbi_tlb_fifo_sfence_vma_asid(struct sbi_tlb_info *tinfo)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long start = tinfo->start;
 | 
			
		||||
	unsigned long size  = tinfo->size;
 | 
			
		||||
	unsigned long asid  = tinfo->asid;
 | 
			
		||||
	unsigned long i;
 | 
			
		||||
 | 
			
		||||
	if (start == 0 && size == 0) {
 | 
			
		||||
		sbi_tlb_flush_all();
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Flush entire MM context for a given ASID */
 | 
			
		||||
	if (size == SBI_TLB_FLUSH_ALL) {
 | 
			
		||||
		__asm__ __volatile__("sfence.vma x0, %0"
 | 
			
		||||
				     :
 | 
			
		||||
				     : "r"(asid)
 | 
			
		||||
				     : "memory");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < size; i += PAGE_SIZE) {
 | 
			
		||||
		__asm__ __volatile__("sfence.vma %0, %1"
 | 
			
		||||
				     :
 | 
			
		||||
				     : "r"(start + i), "r"(asid)
 | 
			
		||||
				     : "memory");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void sbi_tlb_fifo_process(struct sbi_scratch *scratch, u32 event)
 | 
			
		||||
{
 | 
			
		||||
	struct sbi_tlb_info tinfo;
 | 
			
		||||
	struct sbi_fifo *ipi_tlb_fifo =
 | 
			
		||||
			sbi_scratch_offset_ptr(scratch, ipi_tlb_fifo_off);
 | 
			
		||||
 | 
			
		||||
	while (!sbi_fifo_dequeue(ipi_tlb_fifo, &tinfo)) {
 | 
			
		||||
		if (tinfo.type == SBI_TLB_FLUSH_VMA)
 | 
			
		||||
			sbi_tlb_fifo_sfence_vma(&tinfo);
 | 
			
		||||
		else if (tinfo.type == SBI_TLB_FLUSH_VMA_ASID)
 | 
			
		||||
			sbi_tlb_fifo_sfence_vma_asid(&tinfo);
 | 
			
		||||
		memset(&tinfo, 0, SBI_TLB_INFO_SIZE);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sbi_tlb_fifo_init(struct sbi_scratch *scratch, bool cold_boot)
 | 
			
		||||
{
 | 
			
		||||
	void *ipi_tlb_mem;
 | 
			
		||||
	struct sbi_fifo *ipi_tlb_q;
 | 
			
		||||
 | 
			
		||||
	if (cold_boot) {
 | 
			
		||||
		ipi_tlb_fifo_off = sbi_scratch_alloc_offset(sizeof(*ipi_tlb_q),
 | 
			
		||||
							    "IPI_TLB_FIFO");
 | 
			
		||||
		if (!ipi_tlb_fifo_off)
 | 
			
		||||
			return SBI_ENOMEM;
 | 
			
		||||
		ipi_tlb_fifo_mem_off = sbi_scratch_alloc_offset(
 | 
			
		||||
				SBI_TLB_FIFO_NUM_ENTRIES * SBI_TLB_INFO_SIZE,
 | 
			
		||||
				"IPI_TLB_FIFO_MEM");
 | 
			
		||||
		if (!ipi_tlb_fifo_mem_off) {
 | 
			
		||||
			sbi_scratch_free_offset(ipi_tlb_fifo_off);
 | 
			
		||||
			return SBI_ENOMEM;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if (!ipi_tlb_fifo_off ||
 | 
			
		||||
		    !ipi_tlb_fifo_mem_off)
 | 
			
		||||
			return SBI_ENOMEM;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ipi_tlb_q = sbi_scratch_offset_ptr(scratch, ipi_tlb_fifo_off);
 | 
			
		||||
	ipi_tlb_mem = sbi_scratch_offset_ptr(scratch, ipi_tlb_fifo_mem_off);
 | 
			
		||||
 | 
			
		||||
	sbi_fifo_init(ipi_tlb_q, ipi_tlb_mem,
 | 
			
		||||
		      SBI_TLB_FIFO_NUM_ENTRIES, SBI_TLB_INFO_SIZE);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										207
									
								
								lib/sbi/sbi_trap.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								lib/sbi/sbi_trap.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,207 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *   Anup Patel <anup.patel@wdc.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sbi/riscv_asm.h>
 | 
			
		||||
#include <sbi/riscv_encoding.h>
 | 
			
		||||
#include <sbi/riscv_unpriv.h>
 | 
			
		||||
#include <sbi/sbi_console.h>
 | 
			
		||||
#include <sbi/sbi_ecall.h>
 | 
			
		||||
#include <sbi/sbi_error.h>
 | 
			
		||||
#include <sbi/sbi_hart.h>
 | 
			
		||||
#include <sbi/sbi_illegal_insn.h>
 | 
			
		||||
#include <sbi/sbi_ipi.h>
 | 
			
		||||
#include <sbi/sbi_misaligned_ldst.h>
 | 
			
		||||
#include <sbi/sbi_timer.h>
 | 
			
		||||
#include <sbi/sbi_trap.h>
 | 
			
		||||
 | 
			
		||||
static void __noreturn sbi_trap_error(const char *msg, int rc, u32 hartid,
 | 
			
		||||
				      ulong mcause, ulong mtval,
 | 
			
		||||
				      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%" PRILX " mtval=0x%" PRILX "\n",
 | 
			
		||||
		   __func__, hartid, mcause, mtval);
 | 
			
		||||
	sbi_printf("%s: hart%d: mepc=0x%" PRILX " mstatus=0x%" PRILX "\n",
 | 
			
		||||
		   __func__, hartid, regs->mepc, regs->mstatus);
 | 
			
		||||
	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
 | 
			
		||||
		   hartid, "ra", regs->ra, "sp", regs->sp);
 | 
			
		||||
	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
 | 
			
		||||
		   hartid, "gp", regs->gp, "tp", regs->tp);
 | 
			
		||||
	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
 | 
			
		||||
		   hartid, "s0", regs->s0, "s1", regs->s1);
 | 
			
		||||
	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
 | 
			
		||||
		   hartid, "a0", regs->a0, "a1", regs->a1);
 | 
			
		||||
	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
 | 
			
		||||
		   hartid, "a2", regs->a2, "a3", regs->a3);
 | 
			
		||||
	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
 | 
			
		||||
		   hartid, "a4", regs->a4, "a5", regs->a5);
 | 
			
		||||
	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
 | 
			
		||||
		   hartid, "a6", regs->a6, "a7", regs->a7);
 | 
			
		||||
	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
 | 
			
		||||
		   hartid, "s2", regs->s2, "s3", regs->s3);
 | 
			
		||||
	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
 | 
			
		||||
		   hartid, "s4", regs->s4, "s5", regs->s5);
 | 
			
		||||
	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
 | 
			
		||||
		   hartid, "s6", regs->s6, "s7", regs->s7);
 | 
			
		||||
	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
 | 
			
		||||
		   hartid, "s8", regs->s8, "s9", regs->s9);
 | 
			
		||||
	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
 | 
			
		||||
		   hartid, "s10", regs->s10, "s11", regs->s11);
 | 
			
		||||
	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
 | 
			
		||||
		   hartid, "t0", regs->t0, "t1", regs->t1);
 | 
			
		||||
	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
 | 
			
		||||
		   hartid, "t2", regs->t2, "t3", regs->t3);
 | 
			
		||||
	sbi_printf("%s: hart%d: %s=0x%" PRILX " %s=0x%" PRILX "\n", __func__,
 | 
			
		||||
		   hartid, "t4", regs->t4, "t5", regs->t5);
 | 
			
		||||
	sbi_printf("%s: hart%d: %s=0x%" PRILX "\n", __func__, hartid, "t6",
 | 
			
		||||
		   regs->t6);
 | 
			
		||||
 | 
			
		||||
	sbi_hart_hang();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Redirect trap to lower privledge mode (S-mode or U-mode)
 | 
			
		||||
 *
 | 
			
		||||
 * @param regs pointer to register state
 | 
			
		||||
 * @param scratch pointer to sbi_scratch of current HART
 | 
			
		||||
 * @param epc error PC for lower privledge mode
 | 
			
		||||
 * @param cause exception cause for lower privledge mode
 | 
			
		||||
 * @param tval trap value for lower privledge mode
 | 
			
		||||
 *
 | 
			
		||||
 * @return 0 on success and negative error code on failure
 | 
			
		||||
 */
 | 
			
		||||
int sbi_trap_redirect(struct sbi_trap_regs *regs, struct sbi_scratch *scratch,
 | 
			
		||||
		      ulong epc, ulong cause, ulong tval)
 | 
			
		||||
{
 | 
			
		||||
	ulong new_mstatus, prev_mode;
 | 
			
		||||
 | 
			
		||||
	/* Sanity check on previous mode */
 | 
			
		||||
	prev_mode = (regs->mstatus & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT;
 | 
			
		||||
	if (prev_mode != PRV_S && prev_mode != PRV_U)
 | 
			
		||||
		return SBI_ENOTSUPP;
 | 
			
		||||
 | 
			
		||||
	/* Update S-mode exception info */
 | 
			
		||||
	csr_write(CSR_STVAL, tval);
 | 
			
		||||
	csr_write(CSR_SEPC, epc);
 | 
			
		||||
	csr_write(CSR_SCAUSE, cause);
 | 
			
		||||
 | 
			
		||||
	/* Set MEPC to S-mode exception vector base */
 | 
			
		||||
	regs->mepc = csr_read(CSR_STVEC);
 | 
			
		||||
 | 
			
		||||
	/* Initial value of new MSTATUS */
 | 
			
		||||
	new_mstatus = regs->mstatus;
 | 
			
		||||
 | 
			
		||||
	/* Clear MPP, SPP, SPIE, and SIE */
 | 
			
		||||
	new_mstatus &=
 | 
			
		||||
		~(MSTATUS_MPP | MSTATUS_SPP | MSTATUS_SPIE | MSTATUS_SIE);
 | 
			
		||||
 | 
			
		||||
	/* Set SPP */
 | 
			
		||||
	if (prev_mode == PRV_S)
 | 
			
		||||
		new_mstatus |= (1UL << MSTATUS_SPP_SHIFT);
 | 
			
		||||
 | 
			
		||||
	/* Set SPIE */
 | 
			
		||||
	if (regs->mstatus & MSTATUS_SIE)
 | 
			
		||||
		new_mstatus |= (1UL << MSTATUS_SPIE_SHIFT);
 | 
			
		||||
 | 
			
		||||
	/* Set MPP */
 | 
			
		||||
	new_mstatus |= (PRV_S << MSTATUS_MPP_SHIFT);
 | 
			
		||||
 | 
			
		||||
	/* Set new value in MSTATUS */
 | 
			
		||||
	regs->mstatus = new_mstatus;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handle trap/interrupt
 | 
			
		||||
 *
 | 
			
		||||
 * This function is called by firmware linked to OpenSBI
 | 
			
		||||
 * library for handling trap/interrupt. It expects the
 | 
			
		||||
 * following:
 | 
			
		||||
 * 1. The 'mscratch' CSR is pointing to sbi_scratch of current HART
 | 
			
		||||
 * 2. The 'mcause' CSR is having exception/interrupt cause
 | 
			
		||||
 * 3. The 'mtval' CSR is having additional trap information
 | 
			
		||||
 * 4. Stack pointer (SP) is setup for current HART
 | 
			
		||||
 * 5. Interrupts are disabled in MSTATUS CSR
 | 
			
		||||
 *
 | 
			
		||||
 * @param regs pointer to register state
 | 
			
		||||
 * @param scratch pointer to sbi_scratch of current HART
 | 
			
		||||
 */
 | 
			
		||||
void sbi_trap_handler(struct sbi_trap_regs *regs, struct sbi_scratch *scratch)
 | 
			
		||||
{
 | 
			
		||||
	int rc = SBI_ENOTSUPP;
 | 
			
		||||
	const char *msg = "trap handler failed";
 | 
			
		||||
	u32 hartid = sbi_current_hartid();
 | 
			
		||||
	ulong mcause = csr_read(CSR_MCAUSE);
 | 
			
		||||
	ulong mtval = csr_read(CSR_MTVAL);
 | 
			
		||||
	struct unpriv_trap *uptrap;
 | 
			
		||||
 | 
			
		||||
	if (mcause & (1UL << (__riscv_xlen - 1))) {
 | 
			
		||||
		mcause &= ~(1UL << (__riscv_xlen - 1));
 | 
			
		||||
		switch (mcause) {
 | 
			
		||||
		case IRQ_M_TIMER:
 | 
			
		||||
			sbi_timer_process(scratch);
 | 
			
		||||
			break;
 | 
			
		||||
		case IRQ_M_SOFT:
 | 
			
		||||
			sbi_ipi_process(scratch);
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			msg = "unhandled external interrupt";
 | 
			
		||||
			goto trap_error;
 | 
			
		||||
		};
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (mcause) {
 | 
			
		||||
	case CAUSE_ILLEGAL_INSTRUCTION:
 | 
			
		||||
		rc  = sbi_illegal_insn_handler(hartid, mcause, regs, scratch);
 | 
			
		||||
		msg = "illegal instruction handler failed";
 | 
			
		||||
		break;
 | 
			
		||||
	case CAUSE_MISALIGNED_LOAD:
 | 
			
		||||
		rc = sbi_misaligned_load_handler(hartid, mcause, regs, scratch);
 | 
			
		||||
		msg = "misaligned load handler failed";
 | 
			
		||||
		break;
 | 
			
		||||
	case CAUSE_MISALIGNED_STORE:
 | 
			
		||||
		rc  = sbi_misaligned_store_handler(hartid, mcause, regs,
 | 
			
		||||
						   scratch);
 | 
			
		||||
		msg = "misaligned store handler failed";
 | 
			
		||||
		break;
 | 
			
		||||
	case CAUSE_SUPERVISOR_ECALL:
 | 
			
		||||
	case CAUSE_HYPERVISOR_ECALL:
 | 
			
		||||
		rc  = sbi_ecall_handler(hartid, mcause, regs, scratch);
 | 
			
		||||
		msg = "ecall handler failed";
 | 
			
		||||
		break;
 | 
			
		||||
	case CAUSE_LOAD_ACCESS:
 | 
			
		||||
	case CAUSE_STORE_ACCESS:
 | 
			
		||||
	case CAUSE_LOAD_PAGE_FAULT:
 | 
			
		||||
	case CAUSE_STORE_PAGE_FAULT:
 | 
			
		||||
		uptrap = sbi_hart_get_trap_info(scratch);
 | 
			
		||||
		if ((regs->mstatus & MSTATUS_MPRV) && uptrap) {
 | 
			
		||||
			rc = 0;
 | 
			
		||||
			regs->mepc += uptrap->ilen;
 | 
			
		||||
			uptrap->cause = mcause;
 | 
			
		||||
			uptrap->tval = mtval;
 | 
			
		||||
		} else {
 | 
			
		||||
			rc = sbi_trap_redirect(regs, scratch, regs->mepc,
 | 
			
		||||
					       mcause, mtval);
 | 
			
		||||
		}
 | 
			
		||||
		msg = "page/access fault handler failed";
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		/* If the trap came from S or U mode, redirect it there */
 | 
			
		||||
		rc = sbi_trap_redirect(regs, scratch, regs->mepc, mcause, mtval);
 | 
			
		||||
		break;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
trap_error:
 | 
			
		||||
	if (rc) {
 | 
			
		||||
		sbi_trap_error(msg, rc, hartid, mcause, csr_read(CSR_MTVAL),
 | 
			
		||||
			       regs);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user