forked from Mirrors/opensbi
		
	lib: Redirect illegal instruction trap to S-mode when not handled
Currently, we fail with error SBI_ENOTSUPP when we are not able to handle illegal instruction trap. Instead, we should just redirect illegal instruction trap to S-mode when not handled. This redirection of illegal instruction trap will help lazy save/restore of floating point registers to work correctly in Linux kernel. Signed-off-by: Anup Patel <anup.patel@wdc.com>
This commit is contained in:
		@@ -19,12 +19,15 @@
 | 
			
		||||
#define MSTATUS_HIE			0x00000004
 | 
			
		||||
#define MSTATUS_MIE			0x00000008
 | 
			
		||||
#define MSTATUS_UPIE			0x00000010
 | 
			
		||||
#define MSTATUS_SPIE			0x00000020
 | 
			
		||||
#define MSTATUS_SPIE_SHIFT		5
 | 
			
		||||
#define MSTATUS_SPIE			(1UL << MSTATUS_SPIE_SHIFT)
 | 
			
		||||
#define MSTATUS_HPIE			0x00000040
 | 
			
		||||
#define MSTATUS_MPIE			0x00000080
 | 
			
		||||
#define MSTATUS_SPP			0x00000100
 | 
			
		||||
#define MSTATUS_SPP_SHIFT		8
 | 
			
		||||
#define MSTATUS_SPP			(1UL << MSTATUS_SPP_SHIFT)
 | 
			
		||||
#define MSTATUS_HPP			0x00000600
 | 
			
		||||
#define MSTATUS_MPP			0x00001800
 | 
			
		||||
#define MSTATUS_MPP_SHIFT		11
 | 
			
		||||
#define MSTATUS_MPP			(3UL << MSTATUS_MPP_SHIFT)
 | 
			
		||||
#define MSTATUS_FS			0x00006000
 | 
			
		||||
#define MSTATUS_XS			0x00018000
 | 
			
		||||
#define MSTATUS_MPRV			0x00020000
 | 
			
		||||
 
 | 
			
		||||
@@ -51,6 +51,10 @@ struct sbi_trap_regs {
 | 
			
		||||
 | 
			
		||||
struct sbi_scratch;
 | 
			
		||||
 | 
			
		||||
int sbi_trap_redirect(struct sbi_trap_regs *regs,
 | 
			
		||||
		      struct sbi_scratch *scratch,
 | 
			
		||||
		      ulong epc, ulong cause, ulong tval);
 | 
			
		||||
 | 
			
		||||
void sbi_trap_handler(struct sbi_trap_regs *regs,
 | 
			
		||||
		      struct sbi_scratch *scratch);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -26,8 +26,7 @@ static int truly_illegal_insn(ulong insn,
 | 
			
		||||
			      struct sbi_trap_regs *regs,
 | 
			
		||||
			      struct sbi_scratch *scratch)
 | 
			
		||||
{
 | 
			
		||||
	/* For now, always fails */
 | 
			
		||||
	return SBI_ENOTSUPP;
 | 
			
		||||
	return sbi_trap_redirect(regs, scratch, regs->mepc, mcause, insn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int system_opcode_insn(ulong insn,
 | 
			
		||||
@@ -132,7 +131,8 @@ int sbi_illegal_insn_handler(u32 hartid, ulong mcause,
 | 
			
		||||
			insn = get_insn(regs->mepc, &mstatus);
 | 
			
		||||
		}
 | 
			
		||||
		if ((insn & 3) != 3)
 | 
			
		||||
			return SBI_ENOTSUPP;
 | 
			
		||||
			return truly_illegal_insn(insn, hartid, mcause,
 | 
			
		||||
						  regs, scratch);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return illegal_insn_table[(insn & 0x7c) >> 2](insn, hartid, mcause,
 | 
			
		||||
 
 | 
			
		||||
@@ -66,6 +66,49 @@ static void __attribute__((noreturn)) sbi_trap_error(const char *msg,
 | 
			
		||||
	sbi_hart_hang();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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(stval, tval);
 | 
			
		||||
	csr_write(sepc, epc);
 | 
			
		||||
	csr_write(scause, cause);
 | 
			
		||||
 | 
			
		||||
	/* Set MEPC to S-mode exception vector base */
 | 
			
		||||
	regs->mepc = csr_read(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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void sbi_trap_handler(struct sbi_trap_regs *regs,
 | 
			
		||||
		      struct sbi_scratch *scratch)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user