diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/sbi/objects.mk | 1 | ||||
| -rw-r--r-- | lib/sbi/sbi_trap_ldst.c | 23 | ||||
| -rw-r--r-- | lib/sbi/sbi_trap_v_ldst.c | 343 |
3 files changed, 359 insertions, 8 deletions
diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index 12e9e2bd..9cb28422 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -95,6 +95,7 @@ libsbi-objs-y += sbi_timer.o libsbi-objs-y += sbi_tlb.o libsbi-objs-y += sbi_trap.o libsbi-objs-y += sbi_trap_ldst.o +libsbi-objs-y += sbi_trap_v_ldst.o libsbi-objs-y += sbi_unpriv.o libsbi-objs-y += sbi_expected_trap.o libsbi-objs-y += sbi_cppc.o diff --git a/lib/sbi/sbi_trap_ldst.c b/lib/sbi/sbi_trap_ldst.c index ebc4a924..448406b1 100644 --- a/lib/sbi/sbi_trap_ldst.c +++ b/lib/sbi/sbi_trap_ldst.c @@ -32,7 +32,7 @@ typedef int (*sbi_trap_ld_emulator)(int rlen, union sbi_ldst_data *out_val, typedef int (*sbi_trap_st_emulator)(int wlen, union sbi_ldst_data in_val, struct sbi_trap_context *tcntx); -static ulong sbi_misaligned_tinst_fixup(ulong orig_tinst, ulong new_tinst, +ulong sbi_misaligned_tinst_fixup(ulong orig_tinst, ulong new_tinst, ulong addr_offset) { if (new_tinst == INSN_PSEUDO_VS_LOAD || @@ -52,7 +52,7 @@ static int sbi_trap_emulate_load(struct sbi_trap_context *tcntx, ulong insn, insn_len; union sbi_ldst_data val = { 0 }; struct sbi_trap_info uptrap; - int rc, fp = 0, shift = 0, len = 0; + int rc, fp = 0, shift = 0, len = 0, vector = 0; if (orig_trap->tinst & 0x1) { /* @@ -144,6 +144,9 @@ static int sbi_trap_emulate_load(struct sbi_trap_context *tcntx, len = 2; shift = 8 * (sizeof(ulong) - len); insn = RVC_RS2S(insn) << SH_RD; + } else if (IS_VECTOR_LOAD_STORE(insn)) { + vector = 1; + emu = sbi_misaligned_v_ld_emulator; } else { return sbi_trap_redirect(regs, orig_trap); } @@ -152,14 +155,16 @@ static int sbi_trap_emulate_load(struct sbi_trap_context *tcntx, if (rc <= 0) return rc; - if (!fp) - SET_RD(insn, regs, ((long)(val.data_ulong << shift)) >> shift); + if (!vector) { + if (!fp) + SET_RD(insn, regs, ((long)(val.data_ulong << shift)) >> shift); #ifdef __riscv_flen - else if (len == 8) - SET_F64_RD(insn, regs, val.data_u64); - else - SET_F32_RD(insn, regs, val.data_ulong); + else if (len == 8) + SET_F64_RD(insn, regs, val.data_u64); + else + SET_F32_RD(insn, regs, val.data_ulong); #endif + } regs->mepc += insn_len; @@ -248,6 +253,8 @@ static int sbi_trap_emulate_store(struct sbi_trap_context *tcntx, } else if ((insn & INSN_MASK_C_SH) == INSN_MATCH_C_SH) { len = 2; val.data_ulong = GET_RS2S(insn, regs); + } else if (IS_VECTOR_LOAD_STORE(insn)) { + emu = sbi_misaligned_v_st_emulator; } else { return sbi_trap_redirect(regs, orig_trap); } diff --git a/lib/sbi/sbi_trap_v_ldst.c b/lib/sbi/sbi_trap_v_ldst.c new file mode 100644 index 00000000..9929215c --- /dev/null +++ b/lib/sbi/sbi_trap_v_ldst.c @@ -0,0 +1,343 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 SiFive Inc. + * + * Authors: + * Andrew Waterman <[email protected]> + * Nylon Chen <[email protected]> + * Zong Li <[email protected]> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_encoding.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_trap_ldst.h> +#include <sbi/sbi_trap.h> +#include <sbi/sbi_unpriv.h> +#include <sbi/sbi_trap.h> + +#ifdef __riscv_vector +#define VLEN_MAX 65536 + +static inline void set_vreg(ulong vlenb, ulong which, + ulong pos, ulong size, const uint8_t *bytes) +{ + pos += (which % 8) * vlenb; + bytes -= pos; + + asm volatile ( + " .option push\n\t" + " .option arch, +v\n\t" + " vsetvli x0, %0, e8, m8, tu, ma\n\t" + " .option pop\n\t" + :: "r" (pos + size)); + + csr_write(CSR_VSTART, pos); + + switch (which / 8) { + case 0: + asm volatile ( + " .option push\n\t" + " .option arch, +v\n\t" + " vle8.v v0, (%0)\n\t" + " .option pop\n\t" + :: "r" (bytes) : "memory"); + break; + case 1: + asm volatile ( + " .option push\n\t" + " .option arch, +v\n\t" + " vle8.v v8, (%0)\n\t" + " .option pop\n\t" + :: "r" (bytes) : "memory"); + break; + case 2: + asm volatile ( + " .option push\n\t" + " .option arch, +v\n\t" + " vle8.v v16, (%0)\n\t" + " .option pop\n\t" + :: "r" (bytes) : "memory"); + break; + case 3: + asm volatile ( + " .option push\n\t" + " .option arch, +v\n\t" + " vle8.v v24, (%0)\n\t" + " .option pop\n\t" + :: "r" (bytes) : "memory"); + break; + default: + break; + } +} + +static inline void get_vreg(ulong vlenb, ulong which, + ulong pos, ulong size, uint8_t *bytes) +{ + pos += (which % 8) * vlenb; + bytes -= pos; + + asm volatile ( + " .option push\n\t" + " .option arch, +v\n\t" + " vsetvli x0, %0, e8, m8, tu, ma\n\t" + " .option pop\n\t" + :: "r" (pos + size)); + + csr_write(CSR_VSTART, pos); + + switch (which / 8) { + case 0: + asm volatile ( + " .option push\n\t" + " .option arch, +v\n\t" + " vse8.v v0, (%0)\n\t" + " .option pop\n\t" + :: "r" (bytes) : "memory"); + break; + case 1: + asm volatile ( + " .option push\n\t" + " .option arch, +v\n\t" + " vse8.v v8, (%0)\n\t" + " .option pop\n\t" + :: "r" (bytes) : "memory"); + break; + case 2: + asm volatile ( + " .option push\n\t" + " .option arch, +v\n\t" + " vse8.v v16, (%0)\n\t" + " .option pop\n\t" + :: "r" (bytes) : "memory"); + break; + case 3: + asm volatile ( + ".option push\n\t" + ".option arch, +v\n\t" + "vse8.v v24, (%0)\n\t" + ".option pop\n\t" + :: "r" (bytes) : "memory"); + break; + default: + break; + } +} + +static inline void vsetvl(ulong vl, ulong vtype) +{ + asm volatile ( + " .option push\n\t" + " .option arch, +v\n\t" + " vsetvl x0, %0, %1\n\t" + " .option pop\n\t" + :: "r" (vl), "r" (vtype)); +} + +int sbi_misaligned_v_ld_emulator(int rlen, union sbi_ldst_data *out_val, + struct sbi_trap_context *tcntx) +{ + const struct sbi_trap_info *orig_trap = &tcntx->trap; + struct sbi_trap_regs *regs = &tcntx->regs; + struct sbi_trap_info uptrap; + ulong insn = sbi_get_insn(regs->mepc, &uptrap); + ulong vl = csr_read(CSR_VL); + ulong vtype = csr_read(CSR_VTYPE); + ulong vlenb = csr_read(CSR_VLENB); + ulong vstart = csr_read(CSR_VSTART); + ulong base = GET_RS1(insn, regs); + ulong stride = GET_RS2(insn, regs); + ulong vd = GET_VD(insn); + ulong vs2 = GET_VS2(insn); + ulong view = GET_VIEW(insn); + ulong vsew = GET_VSEW(vtype); + ulong vlmul = GET_VLMUL(vtype); + bool illegal = GET_MEW(insn); + bool masked = IS_MASKED(insn); + uint8_t mask[VLEN_MAX / 8]; + uint8_t bytes[8 * sizeof(uint64_t)]; + ulong len = GET_LEN(view); + ulong nf = GET_NF(insn); + ulong vemul = GET_VEMUL(vlmul, view, vsew); + ulong emul = GET_EMUL(vemul); + + if (IS_UNIT_STRIDE_LOAD(insn) || IS_FAULT_ONLY_FIRST_LOAD(insn)) { + stride = nf * len; + } else if (IS_WHOLE_REG_LOAD(insn)) { + vl = (nf * vlenb) >> view; + nf = 1; + vemul = 0; + emul = 1; + stride = nf * len; + } else if (IS_INDEXED_LOAD(insn)) { + len = 1 << vsew; + vemul = (vlmul + vsew - vsew) & 7; + emul = 1 << ((vemul & 4) ? 0 : vemul); + stride = nf * len; + } + + if (illegal || vlenb > VLEN_MAX / 8) { + struct sbi_trap_info trap = { + uptrap.cause = CAUSE_ILLEGAL_INSTRUCTION, + uptrap.tval = insn, + }; + return sbi_trap_redirect(regs, &trap); + } + + if (masked) + get_vreg(vlenb, 0, 0, vlenb, mask); + + do { + if (!masked || ((mask[vstart / 8] >> (vstart % 8)) & 1)) { + /* compute element address */ + ulong addr = base + vstart * stride; + + if (IS_INDEXED_LOAD(insn)) { + ulong offset = 0; + + get_vreg(vlenb, vs2, vstart << view, 1 << view, (uint8_t *)&offset); + addr = base + offset; + } + + csr_write(CSR_VSTART, vstart); + + /* obtain load data from memory */ + for (ulong seg = 0; seg < nf; seg++) { + for (ulong i = 0; i < len; i++) { + bytes[seg * len + i] = + sbi_load_u8((void *)(addr + seg * len + i), + &uptrap); + + if (uptrap.cause) { + if (IS_FAULT_ONLY_FIRST_LOAD(insn) && vstart != 0) { + vl = vstart; + break; + } + vsetvl(vl, vtype); + uptrap.tinst = sbi_misaligned_tinst_fixup( + orig_trap->tinst, uptrap.tinst, i); + return sbi_trap_redirect(regs, &uptrap); + } + } + } + + /* write load data to regfile */ + for (ulong seg = 0; seg < nf; seg++) + set_vreg(vlenb, vd + seg * emul, vstart * len, + len, &bytes[seg * len]); + } + } while (++vstart < vl); + + /* restore clobbered vl/vtype */ + vsetvl(vl, vtype); + + return vl; +} + +int sbi_misaligned_v_st_emulator(int wlen, union sbi_ldst_data in_val, + struct sbi_trap_context *tcntx) +{ + const struct sbi_trap_info *orig_trap = &tcntx->trap; + struct sbi_trap_regs *regs = &tcntx->regs; + struct sbi_trap_info uptrap; + ulong insn = sbi_get_insn(regs->mepc, &uptrap); + ulong vl = csr_read(CSR_VL); + ulong vtype = csr_read(CSR_VTYPE); + ulong vlenb = csr_read(CSR_VLENB); + ulong vstart = csr_read(CSR_VSTART); + ulong base = GET_RS1(insn, regs); + ulong stride = GET_RS2(insn, regs); + ulong vd = GET_VD(insn); + ulong vs2 = GET_VS2(insn); + ulong view = GET_VIEW(insn); + ulong vsew = GET_VSEW(vtype); + ulong vlmul = GET_VLMUL(vtype); + bool illegal = GET_MEW(insn); + bool masked = IS_MASKED(insn); + uint8_t mask[VLEN_MAX / 8]; + uint8_t bytes[8 * sizeof(uint64_t)]; + ulong len = GET_LEN(view); + ulong nf = GET_NF(insn); + ulong vemul = GET_VEMUL(vlmul, view, vsew); + ulong emul = GET_EMUL(vemul); + + if (IS_UNIT_STRIDE_STORE(insn)) { + stride = nf * len; + } else if (IS_WHOLE_REG_STORE(insn)) { + vl = (nf * vlenb) >> view; + nf = 1; + vemul = 0; + emul = 1; + stride = nf * len; + } else if (IS_INDEXED_STORE(insn)) { + len = 1 << vsew; + vemul = (vlmul + vsew - vsew) & 7; + emul = 1 << ((vemul & 4) ? 0 : vemul); + stride = nf * len; + } + + if (illegal || vlenb > VLEN_MAX / 8) { + struct sbi_trap_info trap = { + uptrap.cause = CAUSE_ILLEGAL_INSTRUCTION, + uptrap.tval = insn, + }; + return sbi_trap_redirect(regs, &trap); + } + + if (masked) + get_vreg(vlenb, 0, 0, vlenb, mask); + + do { + if (!masked || ((mask[vstart / 8] >> (vstart % 8)) & 1)) { + /* compute element address */ + ulong addr = base + vstart * stride; + + if (IS_INDEXED_STORE(insn)) { + ulong offset = 0; + + get_vreg(vlenb, vs2, vstart << view, 1 << view, (uint8_t *)&offset); + addr = base + offset; + } + + /* obtain store data from regfile */ + for (ulong seg = 0; seg < nf; seg++) + get_vreg(vlenb, vd + seg * emul, vstart * len, + len, &bytes[seg * len]); + + csr_write(CSR_VSTART, vstart); + + /* write store data to memory */ + for (ulong seg = 0; seg < nf; seg++) { + for (ulong i = 0; i < len; i++) { + sbi_store_u8((void *)(addr + seg * len + i), + bytes[seg * len + i], &uptrap); + if (uptrap.cause) { + vsetvl(vl, vtype); + uptrap.tinst = sbi_misaligned_tinst_fixup( + orig_trap->tinst, uptrap.tinst, i); + return sbi_trap_redirect(regs, &uptrap); + } + } + } + } + } while (++vstart < vl); + + /* restore clobbered vl/vtype */ + vsetvl(vl, vtype); + + return vl; +} +#else +int sbi_misaligned_v_ld_emulator(int rlen, union sbi_ldst_data *out_val, + struct sbi_trap_context *tcntx) +{ + return 0; +} +int sbi_misaligned_v_st_emulator(int wlen, union sbi_ldst_data in_val, + struct sbi_trap_context *tcntx) +{ + return 0; +} +#endif /* __riscv_vector */ |
