mirror of
https://github.com/riscv-software-src/opensbi.git
synced 2026-06-17 16:41:19 +01:00
lib: sbi: Add variable-length unprivilege access functions
sbi_load/store_loop read/write variable-length buffer unprivileged. Both function use the widest aligned 8/4/2/1 byte load/stores in each loop to reduce the total number of iterations. Also switch the scalar/vector misaligned handlers to make use of such functions to simplify code. Miscellaneous: remove the unnecessary [taddr] in inline assembly Signed-off-by: Bo Gan <ganboing@gmail.com> Tested-by: Anirudh Srinivasan <asrinivasan@oss.tenstorrent.com> Reviewed-by: Anup Patel <anup@brainfault.org> Link: https://lore.kernel.org/r/20260609060024.706-4-ganboing@gmail.com Signed-off-by: Anup Patel <anup@brainfault.org>
This commit is contained in:
@@ -36,6 +36,12 @@ DECLARE_UNPRIVILEGED_LOAD_FUNCTION(u64)
|
|||||||
DECLARE_UNPRIVILEGED_STORE_FUNCTION(u64)
|
DECLARE_UNPRIVILEGED_STORE_FUNCTION(u64)
|
||||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(ulong)
|
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(ulong)
|
||||||
|
|
||||||
|
void sbi_load_loop(u8 *buffer, ulong addr, ulong len,
|
||||||
|
struct sbi_trap_info *trap);
|
||||||
|
|
||||||
|
void sbi_store_loop(u8 *buffer, ulong addr, ulong len,
|
||||||
|
struct sbi_trap_info *trap);
|
||||||
|
|
||||||
ulong sbi_get_insn(ulong mepc, struct sbi_trap_info *trap);
|
ulong sbi_get_insn(ulong mepc, struct sbi_trap_info *trap);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+8
-16
@@ -471,7 +471,6 @@ static int sbi_misaligned_ld_emulator(ulong insn, int rlen, ulong addr,
|
|||||||
const struct sbi_trap_info *orig_trap = &tcntx->trap;
|
const struct sbi_trap_info *orig_trap = &tcntx->trap;
|
||||||
struct sbi_trap_regs *regs = &tcntx->regs;
|
struct sbi_trap_regs *regs = &tcntx->regs;
|
||||||
struct sbi_trap_info uptrap;
|
struct sbi_trap_info uptrap;
|
||||||
int i;
|
|
||||||
|
|
||||||
if (!rlen) {
|
if (!rlen) {
|
||||||
if (IS_VECTOR_LOAD_STORE(insn))
|
if (IS_VECTOR_LOAD_STORE(insn))
|
||||||
@@ -484,13 +483,10 @@ static int sbi_misaligned_ld_emulator(ulong insn, int rlen, ulong addr,
|
|||||||
if (addr != orig_trap->tval)
|
if (addr != orig_trap->tval)
|
||||||
return SBI_EFAIL;
|
return SBI_EFAIL;
|
||||||
|
|
||||||
for (i = 0; i < rlen; i++) {
|
sbi_load_loop(out_val->data_bytes, addr, rlen, &uptrap);
|
||||||
out_val->data_bytes[i] =
|
if (uptrap.cause) {
|
||||||
sbi_load_u8((void *)(addr + i), &uptrap);
|
sbi_misaligned_tinst_fixup(orig_trap, &uptrap);
|
||||||
if (uptrap.cause) {
|
return sbi_trap_redirect(regs, &uptrap);
|
||||||
sbi_misaligned_tinst_fixup(orig_trap, &uptrap);
|
|
||||||
return sbi_trap_redirect(regs, &uptrap);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return rlen;
|
return rlen;
|
||||||
}
|
}
|
||||||
@@ -507,7 +503,6 @@ static int sbi_misaligned_st_emulator(ulong insn, int wlen, ulong addr,
|
|||||||
const struct sbi_trap_info *orig_trap = &tcntx->trap;
|
const struct sbi_trap_info *orig_trap = &tcntx->trap;
|
||||||
struct sbi_trap_regs *regs = &tcntx->regs;
|
struct sbi_trap_regs *regs = &tcntx->regs;
|
||||||
struct sbi_trap_info uptrap;
|
struct sbi_trap_info uptrap;
|
||||||
int i;
|
|
||||||
|
|
||||||
if (!wlen) {
|
if (!wlen) {
|
||||||
if (IS_VECTOR_LOAD_STORE(insn))
|
if (IS_VECTOR_LOAD_STORE(insn))
|
||||||
@@ -520,13 +515,10 @@ static int sbi_misaligned_st_emulator(ulong insn, int wlen, ulong addr,
|
|||||||
if (addr != orig_trap->tval)
|
if (addr != orig_trap->tval)
|
||||||
return SBI_EFAIL;
|
return SBI_EFAIL;
|
||||||
|
|
||||||
for (i = 0; i < wlen; i++) {
|
sbi_store_loop(in_val.data_bytes, addr, wlen, &uptrap);
|
||||||
sbi_store_u8((void *)(addr + i),
|
if (uptrap.cause) {
|
||||||
in_val.data_bytes[i], &uptrap);
|
sbi_misaligned_tinst_fixup(orig_trap, &uptrap);
|
||||||
if (uptrap.cause) {
|
return sbi_trap_redirect(regs, &uptrap);
|
||||||
sbi_misaligned_tinst_fixup(orig_trap, &uptrap);
|
|
||||||
return sbi_trap_redirect(regs, &uptrap);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return wlen;
|
return wlen;
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-20
@@ -229,20 +229,17 @@ int sbi_misaligned_v_ld_emulator(ulong insn, struct sbi_trap_context *tcntx)
|
|||||||
|
|
||||||
/* obtain load data from memory */
|
/* obtain load data from memory */
|
||||||
for (ulong seg = 0; seg < nf; seg++) {
|
for (ulong seg = 0; seg < nf; seg++) {
|
||||||
for (ulong i = 0; i < len; i++) {
|
sbi_load_loop(bytes + seg * len,
|
||||||
bytes[seg * len + i] =
|
addr + seg * len, len, &uptrap);
|
||||||
sbi_load_u8((void *)(addr + seg * len + i),
|
|
||||||
&uptrap);
|
|
||||||
|
|
||||||
if (uptrap.cause) {
|
if (uptrap.cause) {
|
||||||
if (IS_FAULT_ONLY_FIRST_LOAD(insn) && vstart != 0) {
|
if (IS_FAULT_ONLY_FIRST_LOAD(insn) && vstart != 0) {
|
||||||
vl = vstart;
|
vl = vstart;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
vsetvl(vl, vtype);
|
|
||||||
sbi_misaligned_v_tinst_fixup(&uptrap);
|
|
||||||
return sbi_trap_redirect(regs, &uptrap);
|
|
||||||
}
|
}
|
||||||
|
vsetvl(vl, vtype);
|
||||||
|
sbi_misaligned_v_tinst_fixup(&uptrap);
|
||||||
|
return sbi_trap_redirect(regs, &uptrap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,14 +329,13 @@ int sbi_misaligned_v_st_emulator(ulong insn, struct sbi_trap_context *tcntx)
|
|||||||
|
|
||||||
/* write store data to memory */
|
/* write store data to memory */
|
||||||
for (ulong seg = 0; seg < nf; seg++) {
|
for (ulong seg = 0; seg < nf; seg++) {
|
||||||
for (ulong i = 0; i < len; i++) {
|
sbi_store_loop(bytes + seg * len,
|
||||||
sbi_store_u8((void *)(addr + seg * len + i),
|
addr + seg * len, len, &uptrap);
|
||||||
bytes[seg * len + i], &uptrap);
|
|
||||||
if (uptrap.cause) {
|
if (uptrap.cause) {
|
||||||
vsetvl(vl, vtype);
|
vsetvl(vl, vtype);
|
||||||
sbi_misaligned_v_tinst_fixup(&uptrap);
|
sbi_misaligned_v_tinst_fixup(&uptrap);
|
||||||
return sbi_trap_redirect(regs, &uptrap);
|
return sbi_trap_redirect(regs, &uptrap);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (++vstart < vl);
|
} while (++vstart < vl);
|
||||||
|
|||||||
+92
-6
@@ -11,9 +11,20 @@
|
|||||||
#include <sbi/sbi_bitops.h>
|
#include <sbi/sbi_bitops.h>
|
||||||
#include <sbi/sbi_hart.h>
|
#include <sbi/sbi_hart.h>
|
||||||
#include <sbi/sbi_scratch.h>
|
#include <sbi/sbi_scratch.h>
|
||||||
|
#include <sbi/sbi_string.h>
|
||||||
#include <sbi/sbi_trap.h>
|
#include <sbi/sbi_trap.h>
|
||||||
#include <sbi/sbi_unpriv.h>
|
#include <sbi/sbi_unpriv.h>
|
||||||
|
|
||||||
|
union sbi_unpriv_data {
|
||||||
|
u8 b;
|
||||||
|
u16 h;
|
||||||
|
u32 w;
|
||||||
|
#if __riscv_xlen == 64
|
||||||
|
u64 d;
|
||||||
|
#endif
|
||||||
|
u8 bytes[__riscv_xlen / 8];
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* a3 must a pointer to the sbi_trap_info and a4 is used as a temporary
|
* a3 must a pointer to the sbi_trap_info and a4 is used as a temporary
|
||||||
* register in the trap handler. Make sure that compiler doesn't use a3 & a4.
|
* register in the trap handler. Make sure that compiler doesn't use a3 & a4.
|
||||||
@@ -22,13 +33,12 @@
|
|||||||
type sbi_load_##type(const type *addr, \
|
type sbi_load_##type(const type *addr, \
|
||||||
struct sbi_trap_info *trap) \
|
struct sbi_trap_info *trap) \
|
||||||
{ \
|
{ \
|
||||||
register ulong tinfo asm("a3"); \
|
register ulong tinfo asm("a3") = (ulong)trap; \
|
||||||
register ulong mstatus = 0; \
|
register ulong mstatus = 0; \
|
||||||
register ulong mtvec = (ulong)sbi_hart_expected_trap; \
|
register ulong mtvec = (ulong)sbi_hart_expected_trap; \
|
||||||
type ret = 0; \
|
type ret = 0; \
|
||||||
trap->cause = 0; \
|
trap->cause = 0; \
|
||||||
asm volatile( \
|
asm volatile( \
|
||||||
"add %[tinfo], %[taddr], zero\n" \
|
|
||||||
"csrrw %[mtvec], " STR(CSR_MTVEC) ", %[mtvec]\n" \
|
"csrrw %[mtvec], " STR(CSR_MTVEC) ", %[mtvec]\n" \
|
||||||
"csrrs %[mstatus], " STR(CSR_MSTATUS) ", %[mprv]\n" \
|
"csrrs %[mstatus], " STR(CSR_MSTATUS) ", %[mprv]\n" \
|
||||||
".option push\n" \
|
".option push\n" \
|
||||||
@@ -39,8 +49,7 @@
|
|||||||
"csrw " STR(CSR_MTVEC) ", %[mtvec]" \
|
"csrw " STR(CSR_MTVEC) ", %[mtvec]" \
|
||||||
: [mstatus] "+&r"(mstatus), [mtvec] "+&r"(mtvec), \
|
: [mstatus] "+&r"(mstatus), [mtvec] "+&r"(mtvec), \
|
||||||
[tinfo] "+&r"(tinfo), [ret] "=&r"(ret) \
|
[tinfo] "+&r"(tinfo), [ret] "=&r"(ret) \
|
||||||
: [addr] "m"(*addr), [mprv] "r"(MSTATUS_MPRV), \
|
: [addr] "m"(*addr), [mprv] "r"(MSTATUS_MPRV) \
|
||||||
[taddr] "r"((ulong)trap) \
|
|
||||||
: "a4", "memory"); \
|
: "a4", "memory"); \
|
||||||
return ret; \
|
return ret; \
|
||||||
}
|
}
|
||||||
@@ -54,7 +63,6 @@
|
|||||||
register ulong mtvec = (ulong)sbi_hart_expected_trap; \
|
register ulong mtvec = (ulong)sbi_hart_expected_trap; \
|
||||||
trap->cause = 0; \
|
trap->cause = 0; \
|
||||||
asm volatile( \
|
asm volatile( \
|
||||||
"add %[tinfo], %[taddr], zero\n" \
|
|
||||||
"csrrw %[mtvec], " STR(CSR_MTVEC) ", %[mtvec]\n" \
|
"csrrw %[mtvec], " STR(CSR_MTVEC) ", %[mtvec]\n" \
|
||||||
"csrrs %[mstatus], " STR(CSR_MSTATUS) ", %[mprv]\n" \
|
"csrrs %[mstatus], " STR(CSR_MSTATUS) ", %[mprv]\n" \
|
||||||
".option push\n" \
|
".option push\n" \
|
||||||
@@ -66,7 +74,7 @@
|
|||||||
: [mstatus] "+&r"(mstatus), [mtvec] "+&r"(mtvec), \
|
: [mstatus] "+&r"(mstatus), [mtvec] "+&r"(mtvec), \
|
||||||
[tinfo] "+&r"(tinfo) \
|
[tinfo] "+&r"(tinfo) \
|
||||||
: [addr] "m"(*addr), [mprv] "r"(MSTATUS_MPRV), \
|
: [addr] "m"(*addr), [mprv] "r"(MSTATUS_MPRV), \
|
||||||
[val] "r"(val), [taddr] "r"((ulong)trap) \
|
[val] "r"(val) \
|
||||||
: "a4", "memory"); \
|
: "a4", "memory"); \
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +124,84 @@ void sbi_store_u64(u64 *addr, u64 val,
|
|||||||
# error "Unexpected __riscv_xlen"
|
# error "Unexpected __riscv_xlen"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void sbi_load_loop(u8 *buffer, ulong addr, ulong len,
|
||||||
|
struct sbi_trap_info *trap)
|
||||||
|
{
|
||||||
|
union sbi_unpriv_data data;
|
||||||
|
|
||||||
|
trap->cause = 0;
|
||||||
|
while (len) {
|
||||||
|
unsigned int width = __riscv_xlen / 8;
|
||||||
|
void *ptr = (void*)addr;
|
||||||
|
|
||||||
|
while (len < width || (addr & (width - 1)))
|
||||||
|
width /= 2;
|
||||||
|
|
||||||
|
switch (width) {
|
||||||
|
case 1:
|
||||||
|
data.b = sbi_load_u8(ptr, trap);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
data.h = sbi_load_u16(ptr, trap);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
data.w = sbi_load_u32(ptr, trap);
|
||||||
|
break;
|
||||||
|
#if __riscv_xlen == 64
|
||||||
|
case 8:
|
||||||
|
data.d = sbi_load_u64(ptr, trap);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (trap->cause)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sbi_memcpy(buffer, data.bytes, width);
|
||||||
|
len -= width;
|
||||||
|
addr += width;
|
||||||
|
buffer += width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sbi_store_loop(u8 *buffer, ulong addr, ulong len,
|
||||||
|
struct sbi_trap_info *trap)
|
||||||
|
{
|
||||||
|
union sbi_unpriv_data data;
|
||||||
|
|
||||||
|
trap->cause = 0;
|
||||||
|
while (len) {
|
||||||
|
unsigned int width = __riscv_xlen / 8;
|
||||||
|
void *ptr = (void*)addr;
|
||||||
|
|
||||||
|
while (len < width || (addr & (width - 1)))
|
||||||
|
width /= 2;
|
||||||
|
|
||||||
|
sbi_memcpy(data.bytes, buffer, width);
|
||||||
|
switch (width) {
|
||||||
|
case 1:
|
||||||
|
sbi_store_u8(ptr, data.b, trap);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
sbi_store_u16(ptr, data.h, trap);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
sbi_store_u32(ptr, data.w, trap);
|
||||||
|
break;
|
||||||
|
#if __riscv_xlen == 64
|
||||||
|
case 8:
|
||||||
|
sbi_store_u64(ptr, data.d, trap);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (trap->cause)
|
||||||
|
return;
|
||||||
|
|
||||||
|
len -= width;
|
||||||
|
addr += width;
|
||||||
|
buffer += width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ulong sbi_get_insn(ulong mepc, struct sbi_trap_info *trap)
|
ulong sbi_get_insn(ulong mepc, struct sbi_trap_info *trap)
|
||||||
{
|
{
|
||||||
register ulong tinfo asm("a3");
|
register ulong tinfo asm("a3");
|
||||||
|
|||||||
Reference in New Issue
Block a user