lib: Handle page/access fault caused by unpriv load/store

The unpriv load/store instruction from M-mode can cause page/access
fault to M-mode if S-mode page table did not have mappings OR it did
not have PMP access permission.

To tackle this, we redirect trap back to S-mode if unpriv load/store
instruction traps in M-mode.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Reviewed-by: Atish Patra <atish.patra@wdc.com>
This commit is contained in:
Anup Patel
2019-05-23 13:30:58 +05:30
committed by Anup Patel
parent bb915780ac
commit a6395acd6c
9 changed files with 131 additions and 44 deletions

View File

@@ -12,11 +12,23 @@
#include <sbi/sbi_types.h> #include <sbi/sbi_types.h>
#define DECLARE_UNPRIVILEGED_LOAD_FUNCTION(type) \ struct sbi_scratch;
type load_##type(const type *addr);
#define DECLARE_UNPRIVILEGED_STORE_FUNCTION(type) \ struct unpriv_trap {
void store_##type(type *addr, type val); unsigned long ilen;
unsigned long cause;
unsigned long tval;
};
#define DECLARE_UNPRIVILEGED_LOAD_FUNCTION(type) \
type load_##type(const type *addr, \
struct sbi_scratch *scratch, \
struct unpriv_trap *trap);
#define DECLARE_UNPRIVILEGED_STORE_FUNCTION(type) \
void store_##type(type *addr, type val, \
struct sbi_scratch *scratch, \
struct unpriv_trap *trap);
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u8) DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u8)
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u16) DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u16)

View File

@@ -25,6 +25,7 @@
#define SBI_EILL -10 #define SBI_EILL -10
#define SBI_ENOSPC -11 #define SBI_ENOSPC -11
#define SBI_ENOMEM -12 #define SBI_ENOMEM -12
#define SBI_ETRAP -13
/* clang-format on */ /* clang-format on */

View File

@@ -10,6 +10,7 @@
#ifndef __SBI_IPI_H__ #ifndef __SBI_IPI_H__
#define __SBI_IPI_H__ #define __SBI_IPI_H__
#include <sbi/riscv_unpriv.h>
#include <sbi/sbi_types.h> #include <sbi/sbi_types.h>
/* clang-format off */ /* clang-format off */
@@ -28,8 +29,8 @@ struct sbi_ipi_data {
unsigned long ipi_type; unsigned long ipi_type;
}; };
int sbi_ipi_send_many(struct sbi_scratch *scratch, ulong *pmask, u32 event, int sbi_ipi_send_many(struct sbi_scratch *scratch, struct unpriv_trap *uptrap,
void *data); ulong *pmask, u32 event, void *data);
void sbi_ipi_clear_smode(struct sbi_scratch *scratch); void sbi_ipi_clear_smode(struct sbi_scratch *scratch);

View File

@@ -10,59 +10,90 @@
#include <sbi/riscv_encoding.h> #include <sbi/riscv_encoding.h>
#include <sbi/riscv_unpriv.h> #include <sbi/riscv_unpriv.h>
#include <sbi/sbi_bits.h> #include <sbi/sbi_bits.h>
#include <sbi/sbi_hart.h>
#include <sbi/sbi_scratch.h>
#define DEFINE_UNPRIVILEGED_LOAD_FUNCTION(type, insn) \ #define DEFINE_UNPRIVILEGED_LOAD_FUNCTION(type, insn, insnlen) \
type load_##type(const type *addr) \ type load_##type(const type *addr, \
struct sbi_scratch *scratch, \
struct unpriv_trap *trap) \
{ \ { \
register ulong __mstatus asm("a2"); \ register ulong __mstatus asm("a2"); \
type val; \ type val = 0; \
trap->ilen = insnlen; \
trap->cause = 0; \
trap->tval = 0; \
sbi_hart_set_trap_info(scratch, trap); \
asm volatile( \ asm volatile( \
"csrrs %0, " STR(CSR_MSTATUS) ", %3\n" \ "csrrs %0, " STR(CSR_MSTATUS) ", %3\n" \
#insn " %1, %2\n" \ #insn " %1, %2\n" \
"csrw " STR(CSR_MSTATUS) ", %0" \ "csrw " STR(CSR_MSTATUS) ", %0" \
: "+&r"(__mstatus), "=&r"(val) \ : "+&r"(__mstatus), "=&r"(val) \
: "m"(*addr), "r"(MSTATUS_MPRV)); \ : "m"(*addr), "r"(MSTATUS_MPRV)); \
sbi_hart_set_trap_info(scratch, NULL); \
return val; \ return val; \
} }
#define DEFINE_UNPRIVILEGED_STORE_FUNCTION(type, insn) \ #define DEFINE_UNPRIVILEGED_STORE_FUNCTION(type, insn, insnlen) \
void store_##type(type *addr, type val) \ void store_##type(type *addr, type val, \
struct sbi_scratch *scratch, \
struct unpriv_trap *trap) \
{ \ { \
register ulong __mstatus asm("a3"); \ register ulong __mstatus asm("a3"); \
trap->ilen = insnlen; \
trap->cause = 0; \
trap->tval = 0; \
sbi_hart_set_trap_info(scratch, trap); \
asm volatile( \ asm volatile( \
"csrrs %0, " STR(CSR_MSTATUS) ", %3\n" \ "csrrs %0, " STR(CSR_MSTATUS) ", %3\n" \
#insn " %1, %2\n" \ #insn " %1, %2\n" \
"csrw " STR(CSR_MSTATUS) ", %0" \ "csrw " STR(CSR_MSTATUS) ", %0" \
: "+&r"(__mstatus) \ : "+&r"(__mstatus) \
: "r"(val), "m"(*addr), "r"(MSTATUS_MPRV)); \ : "r"(val), "m"(*addr), "r"(MSTATUS_MPRV)); \
sbi_hart_set_trap_info(scratch, NULL); \
} }
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u8, lbu) DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u8, lbu, 4)
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u16, lhu) DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u16, lhu, 4)
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(s8, lb) DEFINE_UNPRIVILEGED_LOAD_FUNCTION(s8, lb, 4)
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(s16, lh) DEFINE_UNPRIVILEGED_LOAD_FUNCTION(s16, lh, 4)
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(s32, lw) DEFINE_UNPRIVILEGED_LOAD_FUNCTION(s32, lw, 2)
DEFINE_UNPRIVILEGED_STORE_FUNCTION(u8, sb) DEFINE_UNPRIVILEGED_STORE_FUNCTION(u8, sb, 4)
DEFINE_UNPRIVILEGED_STORE_FUNCTION(u16, sh) DEFINE_UNPRIVILEGED_STORE_FUNCTION(u16, sh, 4)
DEFINE_UNPRIVILEGED_STORE_FUNCTION(u32, sw) DEFINE_UNPRIVILEGED_STORE_FUNCTION(u32, sw, 2)
#if __riscv_xlen == 64 #if __riscv_xlen == 64
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u32, lwu) DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u32, lwu, 4)
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u64, ld) DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u64, ld, 2)
DEFINE_UNPRIVILEGED_STORE_FUNCTION(u64, sd) DEFINE_UNPRIVILEGED_STORE_FUNCTION(u64, sd, 2)
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(ulong, ld) DEFINE_UNPRIVILEGED_LOAD_FUNCTION(ulong, ld, 2)
#else #else
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u32, lw) DEFINE_UNPRIVILEGED_LOAD_FUNCTION(u32, lw, 2)
DEFINE_UNPRIVILEGED_LOAD_FUNCTION(ulong, lw) DEFINE_UNPRIVILEGED_LOAD_FUNCTION(ulong, lw, 2)
u64 load_u64(const u64 *addr) u64 load_u64(const u64 *addr,
struct sbi_scratch *scratch, struct unpriv_trap *trap)
{ {
return load_u32((u32 *)addr) + ((u64)load_u32((u32 *)addr + 1) << 32); 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) void store_u64(u64 *addr, u64 val,
struct sbi_scratch *scratch, struct unpriv_trap *trap)
{ {
store_u32((u32 *)addr, val); store_u32((u32 *)addr, val, scratch, trap);
store_u32((u32 *)addr + 1, val >> 32); if (trap->cause)
return;
store_u32((u32 *)addr + 1, val >> 32, scratch, trap);
if (trap->cause)
return;
} }
#endif #endif

View File

@@ -34,6 +34,7 @@ int sbi_ecall_handler(u32 hartid, ulong mcause, struct sbi_trap_regs *regs,
struct sbi_scratch *scratch) struct sbi_scratch *scratch)
{ {
int ret = SBI_ENOTSUPP; int ret = SBI_ENOTSUPP;
struct unpriv_trap uptrap;
struct sbi_tlb_info tlb_info; struct sbi_tlb_info tlb_info;
switch (regs->a7) { switch (regs->a7) {
@@ -59,11 +60,11 @@ int sbi_ecall_handler(u32 hartid, ulong mcause, struct sbi_trap_regs *regs,
ret = 0; ret = 0;
break; break;
case SBI_ECALL_SEND_IPI: case SBI_ECALL_SEND_IPI:
ret = sbi_ipi_send_many(scratch, (ulong *)regs->a0, ret = sbi_ipi_send_many(scratch, &uptrap, (ulong *)regs->a0,
SBI_IPI_EVENT_SOFT, NULL); SBI_IPI_EVENT_SOFT, NULL);
break; break;
case SBI_ECALL_REMOTE_FENCE_I: case SBI_ECALL_REMOTE_FENCE_I:
ret = sbi_ipi_send_many(scratch, (ulong *)regs->a0, ret = sbi_ipi_send_many(scratch, &uptrap, (ulong *)regs->a0,
SBI_IPI_EVENT_FENCE_I, NULL); SBI_IPI_EVENT_FENCE_I, NULL);
break; break;
case SBI_ECALL_REMOTE_SFENCE_VMA: case SBI_ECALL_REMOTE_SFENCE_VMA:
@@ -71,7 +72,7 @@ int sbi_ecall_handler(u32 hartid, ulong mcause, struct sbi_trap_regs *regs,
tlb_info.size = (unsigned long)regs->a2; tlb_info.size = (unsigned long)regs->a2;
tlb_info.type = SBI_TLB_FLUSH_VMA; tlb_info.type = SBI_TLB_FLUSH_VMA;
ret = sbi_ipi_send_many(scratch, (ulong *)regs->a0, ret = sbi_ipi_send_many(scratch, &uptrap, (ulong *)regs->a0,
SBI_IPI_EVENT_SFENCE_VMA, &tlb_info); SBI_IPI_EVENT_SFENCE_VMA, &tlb_info);
break; break;
case SBI_ECALL_REMOTE_SFENCE_VMA_ASID: case SBI_ECALL_REMOTE_SFENCE_VMA_ASID:
@@ -80,7 +81,7 @@ int sbi_ecall_handler(u32 hartid, ulong mcause, struct sbi_trap_regs *regs,
tlb_info.asid = (unsigned long)regs->a3; tlb_info.asid = (unsigned long)regs->a3;
tlb_info.type = SBI_TLB_FLUSH_VMA_ASID; tlb_info.type = SBI_TLB_FLUSH_VMA_ASID;
ret = sbi_ipi_send_many(scratch, (ulong *)regs->a0, ret = sbi_ipi_send_many(scratch, &uptrap, (ulong *)regs->a0,
SBI_IPI_EVENT_SFENCE_VMA_ASID, SBI_IPI_EVENT_SFENCE_VMA_ASID,
&tlb_info); &tlb_info);
break; break;
@@ -96,6 +97,10 @@ int sbi_ecall_handler(u32 hartid, ulong mcause, struct sbi_trap_regs *regs,
if (!ret) { if (!ret) {
regs->mepc += 4; regs->mepc += 4;
} else if (ret == SBI_ETRAP) {
ret = 0;
sbi_trap_redirect(regs, scratch, regs->mepc,
uptrap.cause, uptrap.tval);
} }
return ret; return ret;

View File

@@ -58,15 +58,18 @@ done:
return 0; return 0;
} }
int sbi_ipi_send_many(struct sbi_scratch *scratch, ulong *pmask, u32 event, int sbi_ipi_send_many(struct sbi_scratch *scratch, struct unpriv_trap *uptrap,
void *data) ulong *pmask, u32 event, void *data)
{ {
ulong i, m; ulong i, m;
ulong mask = sbi_hart_available_mask(); ulong mask = sbi_hart_available_mask();
u32 hartid = sbi_current_hartid(); u32 hartid = sbi_current_hartid();
if (pmask) if (pmask) {
mask &= load_ulong(pmask); mask &= load_ulong(pmask, scratch, uptrap);
if (uptrap->cause)
return SBI_ETRAP;
}
/* send IPIs to every other hart on the set */ /* send IPIs to every other hart on the set */
for (i = 0, m = mask; m; i++, m >>= 1) for (i = 0, m = mask; m; i++, m >>= 1)

View File

@@ -26,6 +26,7 @@ int sbi_misaligned_load_handler(u32 hartid, ulong mcause,
struct sbi_scratch *scratch) struct sbi_scratch *scratch)
{ {
union reg_data val; union reg_data val;
struct unpriv_trap uptrap;
ulong insn = get_insn(regs->mepc, NULL); ulong insn = get_insn(regs->mepc, NULL);
ulong addr = csr_read(CSR_MTVAL); ulong addr = csr_read(CSR_MTVAL);
int i, fp = 0, shift = 0, len = 0; int i, fp = 0, shift = 0, len = 0;
@@ -91,8 +92,15 @@ int sbi_misaligned_load_handler(u32 hartid, ulong mcause,
return SBI_EILL; return SBI_EILL;
val.data_u64 = 0; val.data_u64 = 0;
for (i = 0; i < len; i++) for (i = 0; i < len; i++) {
val.data_bytes[i] = load_u8((void *)(addr + 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) if (!fp)
SET_RD(insn, regs, val.data_ulong << shift >> shift); SET_RD(insn, regs, val.data_ulong << shift >> shift);
@@ -111,6 +119,7 @@ int sbi_misaligned_store_handler(u32 hartid, ulong mcause,
struct sbi_scratch *scratch) struct sbi_scratch *scratch)
{ {
union reg_data val; union reg_data val;
struct unpriv_trap uptrap;
ulong insn = get_insn(regs->mepc, NULL); ulong insn = get_insn(regs->mepc, NULL);
ulong addr = csr_read(CSR_MTVAL); ulong addr = csr_read(CSR_MTVAL);
int i, len = 0; int i, len = 0;
@@ -166,8 +175,15 @@ int sbi_misaligned_store_handler(u32 hartid, ulong mcause,
} else } else
return SBI_EILL; return SBI_EILL;
for (i = 0; i < len; i++) for (i = 0; i < len; i++) {
store_u8((void *)(addr + i), val.data_bytes[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); regs->mepc += INSN_LEN(insn);

View File

@@ -39,7 +39,7 @@ sbi_system_shutdown(struct sbi_scratch *scratch, u32 type)
/* If that fails (or is not implemented) send an IPI on every /* If that fails (or is not implemented) send an IPI on every
* hart to hang and then hang the current hart */ * hart to hang and then hang the current hart */
sbi_ipi_send_many(scratch, NULL, SBI_IPI_EVENT_HALT, NULL); sbi_ipi_send_many(scratch, NULL, NULL, SBI_IPI_EVENT_HALT, NULL);
sbi_hart_hang(); sbi_hart_hang();
} }

View File

@@ -9,6 +9,7 @@
#include <sbi/riscv_asm.h> #include <sbi/riscv_asm.h>
#include <sbi/riscv_encoding.h> #include <sbi/riscv_encoding.h>
#include <sbi/riscv_unpriv.h>
#include <sbi/sbi_console.h> #include <sbi/sbi_console.h>
#include <sbi/sbi_ecall.h> #include <sbi/sbi_ecall.h>
#include <sbi/sbi_error.h> #include <sbi/sbi_error.h>
@@ -134,11 +135,12 @@ int sbi_trap_redirect(struct sbi_trap_regs *regs, struct sbi_scratch *scratch,
*/ */
void sbi_trap_handler(struct sbi_trap_regs *regs, struct sbi_scratch *scratch) void sbi_trap_handler(struct sbi_trap_regs *regs, struct sbi_scratch *scratch)
{ {
int rc = SBI_ENOTSUPP; int rc = SBI_ENOTSUPP;
const char *msg = "trap handler failed"; const char *msg = "trap handler failed";
u32 hartid = sbi_current_hartid(); u32 hartid = sbi_current_hartid();
ulong mcause = csr_read(CSR_MCAUSE); ulong mcause = csr_read(CSR_MCAUSE);
ulong mtval = csr_read(CSR_MTVAL); ulong mtval = csr_read(CSR_MTVAL);
struct unpriv_trap *uptrap;
if (mcause & (1UL << (__riscv_xlen - 1))) { if (mcause & (1UL << (__riscv_xlen - 1))) {
mcause &= ~(1UL << (__riscv_xlen - 1)); mcause &= ~(1UL << (__riscv_xlen - 1));
@@ -175,6 +177,22 @@ void sbi_trap_handler(struct sbi_trap_regs *regs, struct sbi_scratch *scratch)
rc = sbi_ecall_handler(hartid, mcause, regs, scratch); rc = sbi_ecall_handler(hartid, mcause, regs, scratch);
msg = "ecall handler failed"; msg = "ecall handler failed";
break; 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: default:
/* If the trap came from S or U mode, redirect it there */ /* 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);