forked from Mirrors/opensbi
		
	lib: Extend sbi_trap_redirect() for hypervisor extension
When hypervisor extension is available, we can get traps from VS/VU modes. We should be able to force redirect some of these traps to HS-mode. In other words, we should be able forward traps from VS/VU mode to HS-mode using sbi_trap_redirect() hence this patch. Signed-off-by: Atish Patra <atish.patra@wdc.com> Signed-off-by: Anup Patel <anup.patel@wdc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
This commit is contained in:
		@@ -486,6 +486,16 @@ _trap_handler_all_mode:
 | 
			
		||||
	REG_S	t0, SBI_TRAP_REGS_OFFSET(mepc)(sp)
 | 
			
		||||
	csrr	t0, CSR_MSTATUS
 | 
			
		||||
	REG_S	t0, SBI_TRAP_REGS_OFFSET(mstatus)(sp)
 | 
			
		||||
	REG_S	zero, SBI_TRAP_REGS_OFFSET(mstatusH)(sp)
 | 
			
		||||
#if __riscv_xlen == 32
 | 
			
		||||
	csrr	t0, CSR_MISA
 | 
			
		||||
	srli	t0, t0, ('H' - 'A')
 | 
			
		||||
	andi	t0, t0, 0x1
 | 
			
		||||
	beq	t0, zero, _skip_mstatush_save
 | 
			
		||||
	csrr	t0, CSR_MSTATUSH
 | 
			
		||||
	REG_S	t0, SBI_TRAP_REGS_OFFSET(mstatusH)(sp)
 | 
			
		||||
_skip_mstatush_save:
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* Save all general regisers except SP and T0 */
 | 
			
		||||
	REG_S	zero, SBI_TRAP_REGS_OFFSET(zero)(sp)
 | 
			
		||||
@@ -560,6 +570,15 @@ _trap_handler_all_mode:
 | 
			
		||||
	csrw	CSR_MEPC, t0
 | 
			
		||||
	REG_L	t0, SBI_TRAP_REGS_OFFSET(mstatus)(sp)
 | 
			
		||||
	csrw	CSR_MSTATUS, t0
 | 
			
		||||
#if __riscv_xlen == 32
 | 
			
		||||
	csrr	t0, CSR_MISA
 | 
			
		||||
	srli	t0, t0, ('H' - 'A')
 | 
			
		||||
	andi	t0, t0, 0x1
 | 
			
		||||
	beq	t0, zero, _skip_mstatush_restore
 | 
			
		||||
	REG_L	t0, SBI_TRAP_REGS_OFFSET(mstatusH)(sp)
 | 
			
		||||
	csrw	CSR_MSTATUSH, t0
 | 
			
		||||
_skip_mstatush_restore:
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* Restore T0 */
 | 
			
		||||
	REG_L	t0, SBI_TRAP_REGS_OFFSET(t0)(sp)
 | 
			
		||||
 
 | 
			
		||||
@@ -58,8 +58,10 @@
 | 
			
		||||
#define SSTATUS_UIE			0x00000001
 | 
			
		||||
#define SSTATUS_SIE			0x00000002
 | 
			
		||||
#define SSTATUS_UPIE			0x00000010
 | 
			
		||||
#define SSTATUS_SPIE			0x00000020
 | 
			
		||||
#define SSTATUS_SPP			0x00000100
 | 
			
		||||
#define SSTATUS_SPIE_SHIFT		5
 | 
			
		||||
#define SSTATUS_SPIE			(1UL << MSTATUS_SPIE_SHIFT)
 | 
			
		||||
#define SSTATUS_SPP_SHIFT		8
 | 
			
		||||
#define SSTATUS_SPP			(1UL << MSTATUS_SPP_SHIFT)
 | 
			
		||||
#define SSTATUS_FS			0x00006000
 | 
			
		||||
#define SSTATUS_XS			0x00018000
 | 
			
		||||
#define SSTATUS_SUM			0x00040000
 | 
			
		||||
@@ -68,6 +70,14 @@
 | 
			
		||||
#define SSTATUS_UXL			0x0000000300000000
 | 
			
		||||
#define SSTATUS64_SD			0x8000000000000000
 | 
			
		||||
 | 
			
		||||
#define HSTATUS_VTSR			0x00400000
 | 
			
		||||
#define HSTATUS_VTVM			0x00100000
 | 
			
		||||
#define HSTATUS_SP2V			0x00000200
 | 
			
		||||
#define HSTATUS_SP2P			0x00000100
 | 
			
		||||
#define HSTATUS_SPV			0x00000080
 | 
			
		||||
#define HSTATUS_STL			0x00000040
 | 
			
		||||
#define HSTATUS_SPRV			0x00000001
 | 
			
		||||
 | 
			
		||||
#define DCSR_XDEBUGVER			(3U<<30)
 | 
			
		||||
#define DCSR_NDRESET			(1<<29)
 | 
			
		||||
#define DCSR_FULLRESET			(1<<28)
 | 
			
		||||
@@ -262,6 +272,23 @@
 | 
			
		||||
#define CSR_STVAL			0x143
 | 
			
		||||
#define CSR_SIP				0x144
 | 
			
		||||
#define CSR_SATP			0x180
 | 
			
		||||
 | 
			
		||||
#define CSR_HSTATUS			0x600
 | 
			
		||||
#define CSR_HEDELEG			0x602
 | 
			
		||||
#define CSR_HIDELEG			0x603
 | 
			
		||||
#define CSR_HCOUNTERNEN		0x606
 | 
			
		||||
#define CSR_HGATP			0x680
 | 
			
		||||
 | 
			
		||||
#define CSR_VSSTATUS			0x200
 | 
			
		||||
#define CSR_VSIE			0x204
 | 
			
		||||
#define CSR_VSTVEC			0x205
 | 
			
		||||
#define CSR_VSSCRATCH			0x240
 | 
			
		||||
#define CSR_VSEPC			0x241
 | 
			
		||||
#define CSR_VSCAUSE			0x242
 | 
			
		||||
#define CSR_VSTVAL			0x243
 | 
			
		||||
#define CSR_VSIP			0x244
 | 
			
		||||
#define CSR_VSATP			0x280
 | 
			
		||||
 | 
			
		||||
#define CSR_MSTATUS			0x300
 | 
			
		||||
#define CSR_MISA			0x301
 | 
			
		||||
#define CSR_MEDELEG			0x302
 | 
			
		||||
@@ -302,6 +329,7 @@
 | 
			
		||||
#define CSR_DCSR			0x7b0
 | 
			
		||||
#define CSR_DPC				0x7b1
 | 
			
		||||
#define CSR_DSCRATCH			0x7b2
 | 
			
		||||
 | 
			
		||||
#define CSR_MCYCLE			0xb00
 | 
			
		||||
#define CSR_MINSTRET			0xb02
 | 
			
		||||
#define CSR_MHPMCOUNTER3		0xb03
 | 
			
		||||
 
 | 
			
		||||
@@ -80,8 +80,10 @@
 | 
			
		||||
#define SBI_TRAP_REGS_mepc			32
 | 
			
		||||
/** Index of mstatus member in sbi_trap_regs */
 | 
			
		||||
#define SBI_TRAP_REGS_mstatus			33
 | 
			
		||||
/** Index of mstatusH member in sbi_trap_regs */
 | 
			
		||||
#define SBI_TRAP_REGS_mstatusH			34
 | 
			
		||||
/** Last member index in sbi_trap_regs */
 | 
			
		||||
#define SBI_TRAP_REGS_last			34
 | 
			
		||||
#define SBI_TRAP_REGS_last			35
 | 
			
		||||
 | 
			
		||||
/* clang-format on */
 | 
			
		||||
 | 
			
		||||
@@ -164,6 +166,8 @@ struct sbi_trap_regs {
 | 
			
		||||
	unsigned long mepc;
 | 
			
		||||
	/** mstatus register state */
 | 
			
		||||
	unsigned long mstatus;
 | 
			
		||||
	/** mstatusH register state (only for 32-bit) */
 | 
			
		||||
	unsigned long mstatusH;
 | 
			
		||||
} __packed;
 | 
			
		||||
 | 
			
		||||
struct sbi_scratch;
 | 
			
		||||
 
 | 
			
		||||
@@ -79,41 +79,119 @@ static void __noreturn sbi_trap_error(const char *msg, int rc, u32 hartid,
 | 
			
		||||
int sbi_trap_redirect(struct sbi_trap_regs *regs, struct sbi_scratch *scratch,
 | 
			
		||||
		      ulong epc, ulong cause, ulong tval)
 | 
			
		||||
{
 | 
			
		||||
	ulong new_mstatus, prev_mode;
 | 
			
		||||
	ulong hstatus, vsstatus, prev_mode;
 | 
			
		||||
#if __riscv_xlen == 32
 | 
			
		||||
	bool prev_virt = (regs->mstatusH & MSTATUSH_MPV) ? TRUE : FALSE;
 | 
			
		||||
	bool prev_stage2 = (regs->mstatusH & MSTATUSH_MTL) ? TRUE : FALSE;
 | 
			
		||||
#else
 | 
			
		||||
	bool prev_virt = (regs->mstatus & MSTATUS_MPV) ? TRUE : FALSE;
 | 
			
		||||
	bool prev_stage2 = (regs->mstatus & MSTATUS_MTL) ? TRUE : FALSE;
 | 
			
		||||
#endif
 | 
			
		||||
	/* By default, we redirect to HS-mode */
 | 
			
		||||
	bool next_virt = FALSE;
 | 
			
		||||
 | 
			
		||||
	/* 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);
 | 
			
		||||
	/* For certain exceptions from VS/VU-mode we redirect to VS-mode */
 | 
			
		||||
	if (misa_extension('H') && prev_virt && !prev_stage2) {
 | 
			
		||||
		switch (cause) {
 | 
			
		||||
		case CAUSE_FETCH_PAGE_FAULT:
 | 
			
		||||
		case CAUSE_LOAD_PAGE_FAULT:
 | 
			
		||||
		case CAUSE_STORE_PAGE_FAULT:
 | 
			
		||||
			next_virt = TRUE;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Set MEPC to S-mode exception vector base */
 | 
			
		||||
	regs->mepc = csr_read(CSR_STVEC);
 | 
			
		||||
	/* Update MSTATUS MPV and MTL bits */
 | 
			
		||||
#if __riscv_xlen == 32
 | 
			
		||||
	regs->mstatusH &= ~MSTATUSH_MPV;
 | 
			
		||||
	regs->mstatusH |= (next_virt) ? MSTATUSH_MPV : 0UL;
 | 
			
		||||
	regs->mstatusH &= ~MSTATUSH_MTL;
 | 
			
		||||
#else
 | 
			
		||||
	regs->mstatus &= ~MSTATUS_MPV;
 | 
			
		||||
	regs->mstatus |= (next_virt) ? MSTATUS_MPV : 0UL;
 | 
			
		||||
	regs->mstatus &= ~MSTATUS_MTL;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	/* Initial value of new MSTATUS */
 | 
			
		||||
	new_mstatus = regs->mstatus;
 | 
			
		||||
	/* Update HSTATUS for VS/VU-mode to HS-mode transition */
 | 
			
		||||
	if (misa_extension('H') && prev_virt && !next_virt) {
 | 
			
		||||
		/* Update HSTATUS SP2P, SP2V, SPV, and STL bits */
 | 
			
		||||
		hstatus = csr_read(CSR_HSTATUS);
 | 
			
		||||
		hstatus &= ~HSTATUS_SP2P;
 | 
			
		||||
		hstatus |= (regs->mstatus & MSTATUS_SPP) ? HSTATUS_SP2P : 0;
 | 
			
		||||
		hstatus &= ~HSTATUS_SP2V;
 | 
			
		||||
		hstatus |= (hstatus & HSTATUS_SPV) ? HSTATUS_SP2V : 0;
 | 
			
		||||
		hstatus &= ~HSTATUS_SPV;
 | 
			
		||||
		hstatus |= (prev_virt) ? HSTATUS_SPV : 0;
 | 
			
		||||
		hstatus &= ~HSTATUS_STL;
 | 
			
		||||
		hstatus |= (prev_stage2) ? HSTATUS_STL : 0;
 | 
			
		||||
		csr_write(CSR_HSTATUS, hstatus);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Clear MPP, SPP, SPIE, and SIE */
 | 
			
		||||
	new_mstatus &=
 | 
			
		||||
		~(MSTATUS_MPP | MSTATUS_SPP | MSTATUS_SPIE | MSTATUS_SIE);
 | 
			
		||||
	/* Update exception related CSRs */
 | 
			
		||||
	if (next_virt) {
 | 
			
		||||
		/* Update VS-mode exception info */
 | 
			
		||||
		csr_write(CSR_VSTVAL, tval);
 | 
			
		||||
		csr_write(CSR_VSEPC, epc);
 | 
			
		||||
		csr_write(CSR_VSCAUSE, cause);
 | 
			
		||||
 | 
			
		||||
	/* Set SPP */
 | 
			
		||||
	if (prev_mode == PRV_S)
 | 
			
		||||
		new_mstatus |= (1UL << MSTATUS_SPP_SHIFT);
 | 
			
		||||
		/* Set MEPC to VS-mode exception vector base */
 | 
			
		||||
		regs->mepc = csr_read(CSR_VSTVEC);
 | 
			
		||||
 | 
			
		||||
	/* Set SPIE */
 | 
			
		||||
	if (regs->mstatus & MSTATUS_SIE)
 | 
			
		||||
		new_mstatus |= (1UL << MSTATUS_SPIE_SHIFT);
 | 
			
		||||
		/* Set MPP to VS-mode */
 | 
			
		||||
		regs->mstatus &= ~MSTATUS_MPP;
 | 
			
		||||
		regs->mstatus |= (PRV_S << MSTATUS_MPP_SHIFT);
 | 
			
		||||
 | 
			
		||||
	/* Set MPP */
 | 
			
		||||
	new_mstatus |= (PRV_S << MSTATUS_MPP_SHIFT);
 | 
			
		||||
		/* Get VS-mode SSTATUS CSR */
 | 
			
		||||
		vsstatus = csr_read(CSR_VSSTATUS);
 | 
			
		||||
 | 
			
		||||
	/* Set new value in MSTATUS */
 | 
			
		||||
	regs->mstatus = new_mstatus;
 | 
			
		||||
		/* Set SPP for VS-mode */
 | 
			
		||||
		vsstatus &= ~SSTATUS_SPP;
 | 
			
		||||
		if (prev_mode == PRV_S)
 | 
			
		||||
			vsstatus |= (1UL << SSTATUS_SPP_SHIFT);
 | 
			
		||||
 | 
			
		||||
		/* Set SPIE for VS-mode */
 | 
			
		||||
		vsstatus &= ~SSTATUS_SPIE;
 | 
			
		||||
		if (vsstatus & SSTATUS_SIE)
 | 
			
		||||
			vsstatus |= (1UL << SSTATUS_SPIE_SHIFT);
 | 
			
		||||
 | 
			
		||||
		/* Clear SIE for VS-mode */
 | 
			
		||||
		vsstatus &= ~SSTATUS_SIE;
 | 
			
		||||
 | 
			
		||||
		/* Update VS-mode SSTATUS CSR */
 | 
			
		||||
		csr_write(CSR_VSSTATUS, vsstatus);
 | 
			
		||||
	} else {
 | 
			
		||||
		/* 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);
 | 
			
		||||
 | 
			
		||||
		/* Set MPP to S-mode */
 | 
			
		||||
		regs->mstatus &= ~MSTATUS_MPP;
 | 
			
		||||
		regs->mstatus |= (PRV_S << MSTATUS_MPP_SHIFT);
 | 
			
		||||
 | 
			
		||||
		/* Set SPP for S-mode*/
 | 
			
		||||
		regs->mstatus &= ~MSTATUS_SPP;
 | 
			
		||||
		if (prev_mode == PRV_S)
 | 
			
		||||
			regs->mstatus |= (1UL << MSTATUS_SPP_SHIFT);
 | 
			
		||||
 | 
			
		||||
		/* Set SPIE for S-mode */
 | 
			
		||||
		regs->mstatus &= ~MSTATUS_SPIE;
 | 
			
		||||
		if (regs->mstatus & MSTATUS_SIE)
 | 
			
		||||
			regs->mstatus |= (1UL << MSTATUS_SPIE_SHIFT);
 | 
			
		||||
 | 
			
		||||
		/* Clear SIE for S-mode */
 | 
			
		||||
		regs->mstatus &= ~MSTATUS_SIE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -195,7 +273,8 @@ void sbi_trap_handler(struct sbi_trap_regs *regs, struct sbi_scratch *scratch)
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		/* If the trap came from S or U mode, redirect it there */
 | 
			
		||||
		rc = sbi_trap_redirect(regs, scratch, regs->mepc, mcause, mtval);
 | 
			
		||||
		rc = sbi_trap_redirect(regs, scratch, regs->mepc,
 | 
			
		||||
				       mcause, mtval);
 | 
			
		||||
		break;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user