First RISC-V PR for QEMU 7.1

* Add support for Ibex SPI to OpenTitan
  * Add support for privileged spec version 1.12.0
  * Use privileged spec version 1.12.0 for virt machine by default
  * Allow software access to MIP SEIP
  * Add initial support for the Sdtrig extension
  * Optimisations for vector extensions
  * Improvements to the misa ISA string
  * Add isa extenstion strings to the device tree
  * Don't allow `-bios` options with KVM machines
  * Fix NAPOT range computation overflow
  * Fix DT property mmu-type when CPU mmu option is disabled
  * Make RISC-V ACLINT mtime MMIO register writable
  * Add and enable native debug feature
  * Support 64bit fdt address.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEE9sSsRtSTSGjTuM6PIeENKd+XcFQFAmJh+GQACgkQIeENKd+X
 cFTKZQf/UQ8yb5DozdeNbm2pmfjJnEEsnXB6k95wIX9pjrJ3HkypHzoRpLbIDzET
 KsPjRW6N5SLPINrYfgBuxUv0A/6jOG7cTC/Bimu16wPyS2zQopiTTgiJv6qLkO5G
 QUBWz/6kaXNT+fQiTnXXqjViADO49FigYRWUmRfNabeUwb6YoQwoBY6B5jpwZlbI
 B9qDdcKnYet5zwi1rGFedRC1XtP7ZDF1lylqNS2nnfr1ZvOWYkAJb5TJDi/4qUpz
 i/wGRx/8KaYD5ehGe7Xd50sMM9lLlzNgOnZL0F5cRnA8e/3nRFjTeQ7RoSKGBdaS
 7J4RqA9YMhuPL2tTq95wof6EpVsSNw==
 =yLIg
 -----END PGP SIGNATURE-----

Merge tag 'pull-riscv-to-apply-20220422-1' of github.com:alistair23/qemu into staging

First RISC-V PR for QEMU 7.1

 * Add support for Ibex SPI to OpenTitan
 * Add support for privileged spec version 1.12.0
 * Use privileged spec version 1.12.0 for virt machine by default
 * Allow software access to MIP SEIP
 * Add initial support for the Sdtrig extension
 * Optimisations for vector extensions
 * Improvements to the misa ISA string
 * Add isa extenstion strings to the device tree
 * Don't allow `-bios` options with KVM machines
 * Fix NAPOT range computation overflow
 * Fix DT property mmu-type when CPU mmu option is disabled
 * Make RISC-V ACLINT mtime MMIO register writable
 * Add and enable native debug feature
 * Support 64bit fdt address.

# -----BEGIN PGP SIGNATURE-----
#
# iQEzBAABCAAdFiEE9sSsRtSTSGjTuM6PIeENKd+XcFQFAmJh+GQACgkQIeENKd+X
# cFTKZQf/UQ8yb5DozdeNbm2pmfjJnEEsnXB6k95wIX9pjrJ3HkypHzoRpLbIDzET
# KsPjRW6N5SLPINrYfgBuxUv0A/6jOG7cTC/Bimu16wPyS2zQopiTTgiJv6qLkO5G
# QUBWz/6kaXNT+fQiTnXXqjViADO49FigYRWUmRfNabeUwb6YoQwoBY6B5jpwZlbI
# B9qDdcKnYet5zwi1rGFedRC1XtP7ZDF1lylqNS2nnfr1ZvOWYkAJb5TJDi/4qUpz
# i/wGRx/8KaYD5ehGe7Xd50sMM9lLlzNgOnZL0F5cRnA8e/3nRFjTeQ7RoSKGBdaS
# 7J4RqA9YMhuPL2tTq95wof6EpVsSNw==
# =yLIg
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 21 Apr 2022 05:35:48 PM PDT
# gpg:                using RSA key F6C4AC46D4934868D3B8CE8F21E10D29DF977054
# gpg: Good signature from "Alistair Francis <alistair@alistair23.me>" [undefined]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: F6C4 AC46 D493 4868 D3B8  CE8F 21E1 0D29 DF97 7054

* tag 'pull-riscv-to-apply-20220422-1' of github.com:alistair23/qemu: (31 commits)
  hw/riscv: boot: Support 64bit fdt address.
  hw/core: tcg-cpu-ops.h: Update comments of debug_check_watchpoint()
  target/riscv: cpu: Enable native debug feature
  target/riscv: machine: Add debug state description
  target/riscv: csr: Hook debug CSR read/write
  target/riscv: cpu: Add a config option for native debug
  target/riscv: debug: Implement debug related TCGCPUOps
  hw/intc: riscv_aclint: Add reset function of ACLINT devices
  hw/intc: Make RISC-V ACLINT mtime MMIO register writable
  hw/intc: Support 32/64-bit mtimecmp and mtime accesses in RISC-V ACLINT
  hw/intc: Add .impl.[min|max]_access_size declaration in RISC-V ACLINT
  hw/riscv: virt: fix DT property mmu-type when CPU mmu option is disabled
  target/riscv/pmp: fix NAPOT range computation overflow
  hw/riscv: virt: Exit if the user provided -bios in combination with KVM
  target/riscv: Use cpu_loop_exit_restore directly from mmu faults
  target/riscv: fix start byte for vmv<nf>r.v when vstart != 0
  target/riscv: Add isa extenstion strings to the device tree
  target/riscv: misa to ISA string conversion fix
  target/riscv: optimize helper for vmv<nr>r.v
  target/riscv: optimize condition assign for scale < 0
  ...

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2022-04-21 22:03:34 -07:00
commit 10cd282ee4
25 changed files with 1969 additions and 171 deletions

View File

@ -38,12 +38,18 @@ typedef struct riscv_aclint_mtimer_callback {
int num;
} riscv_aclint_mtimer_callback;
static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq)
static uint64_t cpu_riscv_read_rtc_raw(uint32_t timebase_freq)
{
return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
timebase_freq, NANOSECONDS_PER_SECOND);
}
static uint64_t cpu_riscv_read_rtc(void *opaque)
{
RISCVAclintMTimerState *mtimer = opaque;
return cpu_riscv_read_rtc_raw(mtimer->timebase_freq) + mtimer->time_delta;
}
/*
* Called when timecmp is written to update the QEMU timer or immediately
* trigger timer interrupt if mtimecmp <= current timer value.
@ -51,13 +57,13 @@ static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq)
static void riscv_aclint_mtimer_write_timecmp(RISCVAclintMTimerState *mtimer,
RISCVCPU *cpu,
int hartid,
uint64_t value,
uint32_t timebase_freq)
uint64_t value)
{
uint32_t timebase_freq = mtimer->timebase_freq;
uint64_t next;
uint64_t diff;
uint64_t rtc_r = cpu_riscv_read_rtc(timebase_freq);
uint64_t rtc_r = cpu_riscv_read_rtc(mtimer);
cpu->env.timecmp = value;
if (cpu->env.timecmp <= rtc_r) {
@ -126,9 +132,9 @@ static uint64_t riscv_aclint_mtimer_read(void *opaque, hwaddr addr,
qemu_log_mask(LOG_GUEST_ERROR,
"aclint-mtimer: invalid hartid: %zu", hartid);
} else if ((addr & 0x7) == 0) {
/* timecmp_lo */
/* timecmp_lo for RV32/RV64 or timecmp for RV64 */
uint64_t timecmp = env->timecmp;
return timecmp & 0xFFFFFFFF;
return (size == 4) ? (timecmp & 0xFFFFFFFF) : timecmp;
} else if ((addr & 0x7) == 4) {
/* timecmp_hi */
uint64_t timecmp = env->timecmp;
@ -139,11 +145,12 @@ static uint64_t riscv_aclint_mtimer_read(void *opaque, hwaddr addr,
return 0;
}
} else if (addr == mtimer->time_base) {
/* time_lo */
return cpu_riscv_read_rtc(mtimer->timebase_freq) & 0xFFFFFFFF;
/* time_lo for RV32/RV64 or timecmp for RV64 */
uint64_t rtc = cpu_riscv_read_rtc(mtimer);
return (size == 4) ? (rtc & 0xFFFFFFFF) : rtc;
} else if (addr == mtimer->time_base + 4) {
/* time_hi */
return (cpu_riscv_read_rtc(mtimer->timebase_freq) >> 32) & 0xFFFFFFFF;
return (cpu_riscv_read_rtc(mtimer) >> 32) & 0xFFFFFFFF;
}
qemu_log_mask(LOG_UNIMP,
@ -156,6 +163,7 @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
RISCVAclintMTimerState *mtimer = opaque;
int i;
if (addr >= mtimer->timecmp_base &&
addr < (mtimer->timecmp_base + (mtimer->num_harts << 3))) {
@ -167,33 +175,66 @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr,
qemu_log_mask(LOG_GUEST_ERROR,
"aclint-mtimer: invalid hartid: %zu", hartid);
} else if ((addr & 0x7) == 0) {
/* timecmp_lo */
uint64_t timecmp_hi = env->timecmp >> 32;
riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid,
timecmp_hi << 32 | (value & 0xFFFFFFFF),
mtimer->timebase_freq);
return;
if (size == 4) {
/* timecmp_lo for RV32/RV64 */
uint64_t timecmp_hi = env->timecmp >> 32;
riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid,
timecmp_hi << 32 | (value & 0xFFFFFFFF));
} else {
/* timecmp for RV64 */
riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid,
value);
}
} else if ((addr & 0x7) == 4) {
/* timecmp_hi */
uint64_t timecmp_lo = env->timecmp;
riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid,
value << 32 | (timecmp_lo & 0xFFFFFFFF),
mtimer->timebase_freq);
if (size == 4) {
/* timecmp_hi for RV32/RV64 */
uint64_t timecmp_lo = env->timecmp;
riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid,
value << 32 | (timecmp_lo & 0xFFFFFFFF));
} else {
qemu_log_mask(LOG_GUEST_ERROR,
"aclint-mtimer: invalid timecmp_hi write: %08x",
(uint32_t)addr);
}
} else {
qemu_log_mask(LOG_UNIMP,
"aclint-mtimer: invalid timecmp write: %08x",
(uint32_t)addr);
}
return;
} else if (addr == mtimer->time_base) {
/* time_lo */
qemu_log_mask(LOG_UNIMP,
"aclint-mtimer: time_lo write not implemented");
return;
} else if (addr == mtimer->time_base + 4) {
/* time_hi */
qemu_log_mask(LOG_UNIMP,
"aclint-mtimer: time_hi write not implemented");
} else if (addr == mtimer->time_base || addr == mtimer->time_base + 4) {
uint64_t rtc_r = cpu_riscv_read_rtc_raw(mtimer->timebase_freq);
if (addr == mtimer->time_base) {
if (size == 4) {
/* time_lo for RV32/RV64 */
mtimer->time_delta = ((rtc_r & ~0xFFFFFFFFULL) | value) - rtc_r;
} else {
/* time for RV64 */
mtimer->time_delta = value - rtc_r;
}
} else {
if (size == 4) {
/* time_hi for RV32/RV64 */
mtimer->time_delta = (value << 32 | (rtc_r & 0xFFFFFFFF)) - rtc_r;
} else {
qemu_log_mask(LOG_GUEST_ERROR,
"aclint-mtimer: invalid time_hi write: %08x",
(uint32_t)addr);
return;
}
}
/* Check if timer interrupt is triggered for each hart. */
for (i = 0; i < mtimer->num_harts; i++) {
CPUState *cpu = qemu_get_cpu(mtimer->hartid_base + i);
CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
if (!env) {
continue;
}
riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu),
i, env->timecmp);
}
return;
}
@ -208,6 +249,10 @@ static const MemoryRegionOps riscv_aclint_mtimer_ops = {
.valid = {
.min_access_size = 4,
.max_access_size = 8
},
.impl = {
.min_access_size = 4,
.max_access_size = 8,
}
};
@ -248,11 +293,29 @@ static void riscv_aclint_mtimer_realize(DeviceState *dev, Error **errp)
}
}
static void riscv_aclint_mtimer_reset_enter(Object *obj, ResetType type)
{
/*
* According to RISC-V ACLINT spec:
* - On MTIMER device reset, the MTIME register is cleared to zero.
* - On MTIMER device reset, the MTIMECMP registers are in unknown state.
*/
RISCVAclintMTimerState *mtimer = RISCV_ACLINT_MTIMER(obj);
/*
* Clear mtime register by writing to 0 it.
* Pending mtime interrupts will also be cleared at the same time.
*/
riscv_aclint_mtimer_write(mtimer, mtimer->time_base, 0, 8);
}
static void riscv_aclint_mtimer_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = riscv_aclint_mtimer_realize;
device_class_set_props(dc, riscv_aclint_mtimer_properties);
ResettableClass *rc = RESETTABLE_CLASS(klass);
rc->phases.enter = riscv_aclint_mtimer_reset_enter;
}
static const TypeInfo riscv_aclint_mtimer_info = {
@ -299,7 +362,7 @@ DeviceState *riscv_aclint_mtimer_create(hwaddr addr, hwaddr size,
continue;
}
if (provide_rdtime) {
riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc, timebase_freq);
riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc, dev);
}
cb->s = RISCV_ACLINT_MTIMER(dev);
@ -407,11 +470,32 @@ static void riscv_aclint_swi_realize(DeviceState *dev, Error **errp)
}
}
static void riscv_aclint_swi_reset_enter(Object *obj, ResetType type)
{
/*
* According to RISC-V ACLINT spec:
* - On MSWI device reset, each MSIP register is cleared to zero.
*
* p.s. SSWI device reset does nothing since SETSIP register always reads 0.
*/
RISCVAclintSwiState *swi = RISCV_ACLINT_SWI(obj);
int i;
if (!swi->sswi) {
for (i = 0; i < swi->num_harts; i++) {
/* Clear MSIP registers by lowering software interrupts. */
qemu_irq_lower(swi->soft_irqs[i]);
}
}
}
static void riscv_aclint_swi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = riscv_aclint_swi_realize;
device_class_set_props(dc, riscv_aclint_swi_properties);
ResettableClass *rc = RESETTABLE_CLASS(klass);
rc->phases.enter = riscv_aclint_swi_reset_enter;
}
static const TypeInfo riscv_aclint_swi_info = {

View File

@ -212,9 +212,9 @@ hwaddr riscv_load_initrd(const char *filename, uint64_t mem_size,
return *start + size;
}
uint32_t riscv_load_fdt(hwaddr dram_base, uint64_t mem_size, void *fdt)
uint64_t riscv_load_fdt(hwaddr dram_base, uint64_t mem_size, void *fdt)
{
uint32_t temp, fdt_addr;
uint64_t temp, fdt_addr;
hwaddr dram_end = dram_base + mem_size;
int ret, fdtsize = fdt_totalsize(fdt);
@ -229,7 +229,7 @@ uint32_t riscv_load_fdt(hwaddr dram_base, uint64_t mem_size, void *fdt)
* Thus, put it at an 16MB aligned address that less than fdt size from the
* end of dram or 3GB whichever is lesser.
*/
temp = MIN(dram_end, 3072 * MiB);
temp = (dram_base < 3072 * MiB) ? MIN(dram_end, 3072 * MiB) : dram_end;
fdt_addr = QEMU_ALIGN_DOWN(temp - fdtsize, 16 * MiB);
ret = fdt_pack(fdt);
@ -285,13 +285,15 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts
hwaddr start_addr,
hwaddr rom_base, hwaddr rom_size,
uint64_t kernel_entry,
uint32_t fdt_load_addr, void *fdt)
uint64_t fdt_load_addr, void *fdt)
{
int i;
uint32_t start_addr_hi32 = 0x00000000;
uint32_t fdt_load_addr_hi32 = 0x00000000;
if (!riscv_is_32bit(harts)) {
start_addr_hi32 = start_addr >> 32;
fdt_load_addr_hi32 = fdt_load_addr >> 32;
}
/* reset vector */
uint32_t reset_vec[10] = {
@ -304,7 +306,7 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts
start_addr, /* start: .dword */
start_addr_hi32,
fdt_load_addr, /* fdt_laddr: .dword */
0x00000000,
fdt_load_addr_hi32,
/* fw_dyn: */
};
if (riscv_is_32bit(harts)) {

View File

@ -120,11 +120,18 @@ static void lowrisc_ibex_soc_init(Object *obj)
object_initialize_child(obj, "uart", &s->uart, TYPE_IBEX_UART);
object_initialize_child(obj, "timer", &s->timer, TYPE_IBEX_TIMER);
for (int i = 0; i < OPENTITAN_NUM_SPI_HOSTS; i++) {
object_initialize_child(obj, "spi_host[*]", &s->spi_host[i],
TYPE_IBEX_SPI_HOST);
}
}
static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp)
{
const MemMapEntry *memmap = ibex_memmap;
DeviceState *dev;
SysBusDevice *busdev;
MachineState *ms = MACHINE(qdev_get_machine());
LowRISCIbexSoCState *s = RISCV_IBEX_SOC(dev_soc);
MemoryRegion *sys_mem = get_system_memory();
@ -209,14 +216,35 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp)
qdev_get_gpio_in(DEVICE(qemu_get_cpu(0)),
IRQ_M_TIMER));
/* SPI-Hosts */
for (int i = 0; i < OPENTITAN_NUM_SPI_HOSTS; ++i) {
dev = DEVICE(&(s->spi_host[i]));
if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi_host[i]), errp)) {
return;
}
busdev = SYS_BUS_DEVICE(dev);
sysbus_mmio_map(busdev, 0, memmap[IBEX_DEV_SPI_HOST0 + i].base);
switch (i) {
case OPENTITAN_SPI_HOST0:
sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(&s->plic),
IBEX_SPI_HOST0_ERR_IRQ));
sysbus_connect_irq(busdev, 1, qdev_get_gpio_in(DEVICE(&s->plic),
IBEX_SPI_HOST0_SPI_EVENT_IRQ));
break;
case OPENTITAN_SPI_HOST1:
sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(&s->plic),
IBEX_SPI_HOST1_ERR_IRQ));
sysbus_connect_irq(busdev, 1, qdev_get_gpio_in(DEVICE(&s->plic),
IBEX_SPI_HOST1_SPI_EVENT_IRQ));
break;
}
}
create_unimplemented_device("riscv.lowrisc.ibex.gpio",
memmap[IBEX_DEV_GPIO].base, memmap[IBEX_DEV_GPIO].size);
create_unimplemented_device("riscv.lowrisc.ibex.spi_device",
memmap[IBEX_DEV_SPI_DEVICE].base, memmap[IBEX_DEV_SPI_DEVICE].size);
create_unimplemented_device("riscv.lowrisc.ibex.spi_host0",
memmap[IBEX_DEV_SPI_HOST0].base, memmap[IBEX_DEV_SPI_HOST0].size);
create_unimplemented_device("riscv.lowrisc.ibex.spi_host1",
memmap[IBEX_DEV_SPI_HOST1].base, memmap[IBEX_DEV_SPI_HOST1].size);
create_unimplemented_device("riscv.lowrisc.ibex.i2c",
memmap[IBEX_DEV_I2C].base, memmap[IBEX_DEV_I2C].size);
create_unimplemented_device("riscv.lowrisc.ibex.pattgen",

View File

@ -230,8 +230,14 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket,
cpu_name = g_strdup_printf("/cpus/cpu@%d",
s->soc[socket].hartid_base + cpu);
qemu_fdt_add_subnode(mc->fdt, cpu_name);
qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type",
(is_32_bit) ? "riscv,sv32" : "riscv,sv48");
if (riscv_feature(&s->soc[socket].harts[cpu].env,
RISCV_FEATURE_MMU)) {
qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type",
(is_32_bit) ? "riscv,sv32" : "riscv,sv48");
} else {
qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type",
"riscv,none");
}
name = riscv_isa_string(&s->soc[socket].harts[cpu]);
qemu_fdt_setprop_string(mc->fdt, cpu_name, "riscv,isa", name);
g_free(name);
@ -1308,12 +1314,18 @@ static void virt_machine_init(MachineState *machine)
/*
* Only direct boot kernel is currently supported for KVM VM,
* so the "-bios" parameter is ignored and treated like "-bios none"
* when KVM is enabled.
* so the "-bios" parameter is not supported when KVM is enabled.
*/
if (kvm_enabled()) {
g_free(machine->firmware);
machine->firmware = g_strdup("none");
if (machine->firmware) {
if (strcmp(machine->firmware, "none")) {
error_report("Machine mode firmware is not supported in "
"combination with KVM.");
exit(1);
}
} else {
machine->firmware = g_strdup("none");
}
}
if (riscv_is_32bit(&s->soc[0])) {

612
hw/ssi/ibex_spi_host.c Normal file
View File

@ -0,0 +1,612 @@
/*
* QEMU model of the Ibex SPI Controller
* SPEC Reference: https://docs.opentitan.org/hw/ip/spi_host/doc/
*
* Copyright (C) 2022 Western Digital
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "hw/ssi/ibex_spi_host.h"
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "migration/vmstate.h"
#include "trace.h"
REG32(INTR_STATE, 0x00)
FIELD(INTR_STATE, ERROR, 0, 1)
FIELD(INTR_STATE, SPI_EVENT, 1, 1)
REG32(INTR_ENABLE, 0x04)
FIELD(INTR_ENABLE, ERROR, 0, 1)
FIELD(INTR_ENABLE, SPI_EVENT, 1, 1)
REG32(INTR_TEST, 0x08)
FIELD(INTR_TEST, ERROR, 0, 1)
FIELD(INTR_TEST, SPI_EVENT, 1, 1)
REG32(ALERT_TEST, 0x0c)
FIELD(ALERT_TEST, FETAL_TEST, 0, 1)
REG32(CONTROL, 0x10)
FIELD(CONTROL, RX_WATERMARK, 0, 8)
FIELD(CONTROL, TX_WATERMARK, 1, 8)
FIELD(CONTROL, OUTPUT_EN, 29, 1)
FIELD(CONTROL, SW_RST, 30, 1)
FIELD(CONTROL, SPIEN, 31, 1)
REG32(STATUS, 0x14)
FIELD(STATUS, TXQD, 0, 8)
FIELD(STATUS, RXQD, 18, 8)
FIELD(STATUS, CMDQD, 16, 3)
FIELD(STATUS, RXWM, 20, 1)
FIELD(STATUS, BYTEORDER, 22, 1)
FIELD(STATUS, RXSTALL, 23, 1)
FIELD(STATUS, RXEMPTY, 24, 1)
FIELD(STATUS, RXFULL, 25, 1)
FIELD(STATUS, TXWM, 26, 1)
FIELD(STATUS, TXSTALL, 27, 1)
FIELD(STATUS, TXEMPTY, 28, 1)
FIELD(STATUS, TXFULL, 29, 1)
FIELD(STATUS, ACTIVE, 30, 1)
FIELD(STATUS, READY, 31, 1)
REG32(CONFIGOPTS, 0x18)
FIELD(CONFIGOPTS, CLKDIV_0, 0, 16)
FIELD(CONFIGOPTS, CSNIDLE_0, 16, 4)
FIELD(CONFIGOPTS, CSNTRAIL_0, 20, 4)
FIELD(CONFIGOPTS, CSNLEAD_0, 24, 4)
FIELD(CONFIGOPTS, FULLCYC_0, 29, 1)
FIELD(CONFIGOPTS, CPHA_0, 30, 1)
FIELD(CONFIGOPTS, CPOL_0, 31, 1)
REG32(CSID, 0x1c)
FIELD(CSID, CSID, 0, 32)
REG32(COMMAND, 0x20)
FIELD(COMMAND, LEN, 0, 8)
FIELD(COMMAND, CSAAT, 9, 1)
FIELD(COMMAND, SPEED, 10, 2)
FIELD(COMMAND, DIRECTION, 12, 2)
REG32(ERROR_ENABLE, 0x2c)
FIELD(ERROR_ENABLE, CMDBUSY, 0, 1)
FIELD(ERROR_ENABLE, OVERFLOW, 1, 1)
FIELD(ERROR_ENABLE, UNDERFLOW, 2, 1)
FIELD(ERROR_ENABLE, CMDINVAL, 3, 1)
FIELD(ERROR_ENABLE, CSIDINVAL, 4, 1)
REG32(ERROR_STATUS, 0x30)
FIELD(ERROR_STATUS, CMDBUSY, 0, 1)
FIELD(ERROR_STATUS, OVERFLOW, 1, 1)
FIELD(ERROR_STATUS, UNDERFLOW, 2, 1)
FIELD(ERROR_STATUS, CMDINVAL, 3, 1)
FIELD(ERROR_STATUS, CSIDINVAL, 4, 1)
FIELD(ERROR_STATUS, ACCESSINVAL, 5, 1)
REG32(EVENT_ENABLE, 0x30)
FIELD(EVENT_ENABLE, RXFULL, 0, 1)
FIELD(EVENT_ENABLE, TXEMPTY, 1, 1)
FIELD(EVENT_ENABLE, RXWM, 2, 1)
FIELD(EVENT_ENABLE, TXWM, 3, 1)
FIELD(EVENT_ENABLE, READY, 4, 1)
FIELD(EVENT_ENABLE, IDLE, 5, 1)
static inline uint8_t div4_round_up(uint8_t dividend)
{
return (dividend + 3) / 4;
}
static void ibex_spi_rxfifo_reset(IbexSPIHostState *s)
{
/* Empty the RX FIFO and assert RXEMPTY */
fifo8_reset(&s->rx_fifo);
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXFULL_MASK;
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXEMPTY_MASK;
}
static void ibex_spi_txfifo_reset(IbexSPIHostState *s)
{
/* Empty the TX FIFO and assert TXEMPTY */
fifo8_reset(&s->tx_fifo);
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXFULL_MASK;
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_TXEMPTY_MASK;
}
static void ibex_spi_host_reset(DeviceState *dev)
{
IbexSPIHostState *s = IBEX_SPI_HOST(dev);
trace_ibex_spi_host_reset("Resetting Ibex SPI");
/* SPI Host Register Reset */
s->regs[IBEX_SPI_HOST_INTR_STATE] = 0x00;
s->regs[IBEX_SPI_HOST_INTR_ENABLE] = 0x00;
s->regs[IBEX_SPI_HOST_INTR_TEST] = 0x00;
s->regs[IBEX_SPI_HOST_ALERT_TEST] = 0x00;
s->regs[IBEX_SPI_HOST_CONTROL] = 0x7f;
s->regs[IBEX_SPI_HOST_STATUS] = 0x00;
s->regs[IBEX_SPI_HOST_CONFIGOPTS] = 0x00;
s->regs[IBEX_SPI_HOST_CSID] = 0x00;
s->regs[IBEX_SPI_HOST_COMMAND] = 0x00;
/* RX/TX Modelled by FIFO */
s->regs[IBEX_SPI_HOST_RXDATA] = 0x00;
s->regs[IBEX_SPI_HOST_TXDATA] = 0x00;
s->regs[IBEX_SPI_HOST_ERROR_ENABLE] = 0x1F;
s->regs[IBEX_SPI_HOST_ERROR_STATUS] = 0x00;
s->regs[IBEX_SPI_HOST_EVENT_ENABLE] = 0x00;
ibex_spi_rxfifo_reset(s);
ibex_spi_txfifo_reset(s);
s->init_status = true;
return;
}
/*
* Check if we need to trigger an interrupt.
* The two interrupts lines (host_err and event) can
* be enabled separately in 'IBEX_SPI_HOST_INTR_ENABLE'.
*
* Interrupts are triggered based on the ones
* enabled in the `IBEX_SPI_HOST_EVENT_ENABLE` and `IBEX_SPI_HOST_ERROR_ENABLE`.
*/
static void ibex_spi_host_irq(IbexSPIHostState *s)
{
bool error_en = s->regs[IBEX_SPI_HOST_INTR_ENABLE]
& R_INTR_ENABLE_ERROR_MASK;
bool event_en = s->regs[IBEX_SPI_HOST_INTR_ENABLE]
& R_INTR_ENABLE_SPI_EVENT_MASK;
bool err_pending = s->regs[IBEX_SPI_HOST_INTR_STATE]
& R_INTR_STATE_ERROR_MASK;
bool status_pending = s->regs[IBEX_SPI_HOST_INTR_STATE]
& R_INTR_STATE_SPI_EVENT_MASK;
int err_irq = 0, event_irq = 0;
/* Error IRQ enabled and Error IRQ Cleared*/
if (error_en && !err_pending) {
/* Event enabled, Interrupt Test Error */
if (s->regs[IBEX_SPI_HOST_INTR_TEST] & R_INTR_TEST_ERROR_MASK) {
err_irq = 1;
} else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE]
& R_ERROR_ENABLE_CMDBUSY_MASK) &&
s->regs[IBEX_SPI_HOST_ERROR_STATUS]
& R_ERROR_STATUS_CMDBUSY_MASK) {
/* Wrote to COMMAND when not READY */
err_irq = 1;
} else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE]
& R_ERROR_ENABLE_CMDINVAL_MASK) &&
s->regs[IBEX_SPI_HOST_ERROR_STATUS]
& R_ERROR_STATUS_CMDINVAL_MASK) {
/* Invalid command segment */
err_irq = 1;
} else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE]
& R_ERROR_ENABLE_CSIDINVAL_MASK) &&
s->regs[IBEX_SPI_HOST_ERROR_STATUS]
& R_ERROR_STATUS_CSIDINVAL_MASK) {
/* Invalid value for CSID */
err_irq = 1;
}
if (err_irq) {
s->regs[IBEX_SPI_HOST_INTR_STATE] |= R_INTR_STATE_ERROR_MASK;
}
qemu_set_irq(s->host_err, err_irq);
}
/* Event IRQ Enabled and Event IRQ Cleared */
if (event_en && !status_pending) {
if (s->regs[IBEX_SPI_HOST_INTR_TEST] & R_INTR_TEST_SPI_EVENT_MASK) {
/* Event enabled, Interrupt Test Event */
event_irq = 1;
} else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE]
& R_EVENT_ENABLE_READY_MASK) &&
(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_READY_MASK)) {
/* SPI Host ready for next command */
event_irq = 1;
} else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE]
& R_EVENT_ENABLE_TXEMPTY_MASK) &&
(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_TXEMPTY_MASK)) {
/* SPI TXEMPTY, TXFIFO drained */
event_irq = 1;
} else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE]
& R_EVENT_ENABLE_RXFULL_MASK) &&
(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_RXFULL_MASK)) {
/* SPI RXFULL, RXFIFO full */
event_irq = 1;
}
if (event_irq) {
s->regs[IBEX_SPI_HOST_INTR_STATE] |= R_INTR_STATE_SPI_EVENT_MASK;
}
qemu_set_irq(s->event, event_irq);
}
}
static void ibex_spi_host_transfer(IbexSPIHostState *s)
{
uint32_t rx, tx;
/* Get num of one byte transfers */
uint8_t segment_len = ((s->regs[IBEX_SPI_HOST_COMMAND] & R_COMMAND_LEN_MASK)
>> R_COMMAND_LEN_SHIFT);
while (segment_len > 0) {
if (fifo8_is_empty(&s->tx_fifo)) {
/* Assert Stall */
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_TXSTALL_MASK;
break;
} else if (fifo8_is_full(&s->rx_fifo)) {
/* Assert Stall */
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXSTALL_MASK;
break;
} else {
tx = fifo8_pop(&s->tx_fifo);
}
rx = ssi_transfer(s->ssi, tx);
trace_ibex_spi_host_transfer(tx, rx);
if (!fifo8_is_full(&s->rx_fifo)) {
fifo8_push(&s->rx_fifo, rx);
} else {
/* Assert RXFULL */
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXFULL_MASK;
}
--segment_len;
}
/* Assert Ready */
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_READY_MASK;
/* Set RXQD */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXQD_MASK;
s->regs[IBEX_SPI_HOST_STATUS] |= (R_STATUS_RXQD_MASK
& div4_round_up(segment_len));
/* Set TXQD */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXQD_MASK;
s->regs[IBEX_SPI_HOST_STATUS] |= (fifo8_num_used(&s->tx_fifo) / 4)
& R_STATUS_TXQD_MASK;
/* Clear TXFULL */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXFULL_MASK;
/* Assert TXEMPTY and drop remaining bytes that exceed segment_len */
ibex_spi_txfifo_reset(s);
/* Reset RXEMPTY */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXEMPTY_MASK;
ibex_spi_host_irq(s);
}
static uint64_t ibex_spi_host_read(void *opaque, hwaddr addr,
unsigned int size)
{
IbexSPIHostState *s = opaque;
uint32_t rc = 0;
uint8_t rx_byte = 0;
trace_ibex_spi_host_read(addr, size);
/* Match reg index */
addr = addr >> 2;
switch (addr) {
/* Skipping any W/O registers */
case IBEX_SPI_HOST_INTR_STATE...IBEX_SPI_HOST_INTR_ENABLE:
case IBEX_SPI_HOST_CONTROL...IBEX_SPI_HOST_STATUS:
rc = s->regs[addr];
break;
case IBEX_SPI_HOST_CSID:
rc = s->regs[addr];
break;
case IBEX_SPI_HOST_CONFIGOPTS:
rc = s->config_opts[s->regs[IBEX_SPI_HOST_CSID]];
break;
case IBEX_SPI_HOST_TXDATA:
rc = s->regs[addr];
break;
case IBEX_SPI_HOST_RXDATA:
/* Clear RXFULL */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXFULL_MASK;
for (int i = 0; i < 4; ++i) {
if (fifo8_is_empty(&s->rx_fifo)) {
/* Assert RXEMPTY, no IRQ */
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXEMPTY_MASK;
s->regs[IBEX_SPI_HOST_ERROR_STATUS] |=
R_ERROR_STATUS_UNDERFLOW_MASK;
return rc;
}
rx_byte = fifo8_pop(&s->rx_fifo);
rc |= rx_byte << (i * 8);
}
break;
case IBEX_SPI_HOST_ERROR_ENABLE...IBEX_SPI_HOST_EVENT_ENABLE:
rc = s->regs[addr];
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "Bad offset 0x%" HWADDR_PRIx "\n",
addr << 2);
}
return rc;
}
static void ibex_spi_host_write(void *opaque, hwaddr addr,
uint64_t val64, unsigned int size)
{
IbexSPIHostState *s = opaque;
uint32_t val32 = val64;
uint32_t shift_mask = 0xff;
uint8_t txqd_len;
trace_ibex_spi_host_write(addr, size, val64);
/* Match reg index */
addr = addr >> 2;
switch (addr) {
/* Skipping any R/O registers */
case IBEX_SPI_HOST_INTR_STATE...IBEX_SPI_HOST_INTR_ENABLE:
s->regs[addr] = val32;
break;
case IBEX_SPI_HOST_INTR_TEST:
s->regs[addr] = val32;
ibex_spi_host_irq(s);
break;
case IBEX_SPI_HOST_ALERT_TEST:
s->regs[addr] = val32;
qemu_log_mask(LOG_UNIMP,
"%s: SPI_ALERT_TEST is not supported\n", __func__);
break;
case IBEX_SPI_HOST_CONTROL:
s->regs[addr] = val32;
if (val32 & R_CONTROL_SW_RST_MASK) {
ibex_spi_host_reset((DeviceState *)s);
/* Clear active if any */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_ACTIVE_MASK;
}
if (val32 & R_CONTROL_OUTPUT_EN_MASK) {
qemu_log_mask(LOG_UNIMP,
"%s: CONTROL_OUTPUT_EN is not supported\n", __func__);
}
break;
case IBEX_SPI_HOST_CONFIGOPTS:
/* Update the respective config-opts register based on CSIDth index */
s->config_opts[s->regs[IBEX_SPI_HOST_CSID]] = val32;
qemu_log_mask(LOG_UNIMP,
"%s: CONFIGOPTS Hardware settings not supported\n",
__func__);
break;
case IBEX_SPI_HOST_CSID:
if (val32 >= s->num_cs) {
/* CSID exceeds max num_cs */
s->regs[IBEX_SPI_HOST_ERROR_STATUS] |=
R_ERROR_STATUS_CSIDINVAL_MASK;
ibex_spi_host_irq(s);
return;
}
s->regs[addr] = val32;
break;
case IBEX_SPI_HOST_COMMAND:
s->regs[addr] = val32;
/* STALL, IP not enabled */
if (!(s->regs[IBEX_SPI_HOST_CONTROL] & R_CONTROL_SPIEN_MASK)) {
return;
}
/* SPI not ready, IRQ Error */
if (!(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_READY_MASK)) {
s->regs[IBEX_SPI_HOST_ERROR_STATUS] |= R_ERROR_STATUS_CMDBUSY_MASK;
ibex_spi_host_irq(s);
return;
}
/* Assert Not Ready */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_READY_MASK;
if (((val32 & R_COMMAND_DIRECTION_MASK) >> R_COMMAND_DIRECTION_SHIFT)
!= BIDIRECTIONAL_TRANSFER) {
qemu_log_mask(LOG_UNIMP,
"%s: Rx Only/Tx Only are not supported\n", __func__);
}
if (val32 & R_COMMAND_CSAAT_MASK) {
qemu_log_mask(LOG_UNIMP,
"%s: CSAAT is not supported\n", __func__);
}
if (val32 & R_COMMAND_SPEED_MASK) {
qemu_log_mask(LOG_UNIMP,
"%s: SPEED is not supported\n", __func__);
}
/* Set Transfer Callback */
timer_mod(s->fifo_trigger_handle,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
(TX_INTERRUPT_TRIGGER_DELAY_NS));
break;
case IBEX_SPI_HOST_TXDATA:
/*
* This is a hardware `feature` where
* the first word written TXDATA after init is omitted entirely
*/
if (s->init_status) {
s->init_status = false;
return;
}
for (int i = 0; i < 4; ++i) {
/* Attempting to write when TXFULL */
if (fifo8_is_full(&s->tx_fifo)) {
/* Assert RXEMPTY, no IRQ */
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_TXFULL_MASK;
s->regs[IBEX_SPI_HOST_ERROR_STATUS] |=
R_ERROR_STATUS_OVERFLOW_MASK;
ibex_spi_host_irq(s);
return;
}
/* Byte ordering is set by the IP */
if ((s->regs[IBEX_SPI_HOST_STATUS] &
R_STATUS_BYTEORDER_MASK) == 0) {
/* LE: LSB transmitted first (default for ibex processor) */
shift_mask = 0xff << (i * 8);
} else {
/* BE: MSB transmitted first */
qemu_log_mask(LOG_UNIMP,
"%s: Big endian is not supported\n", __func__);
}
fifo8_push(&s->tx_fifo, (val32 & shift_mask) >> (i * 8));
}
/* Reset TXEMPTY */
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXEMPTY_MASK;
/* Update TXQD */
txqd_len = (s->regs[IBEX_SPI_HOST_STATUS] &
R_STATUS_TXQD_MASK) >> R_STATUS_TXQD_SHIFT;
/* Partial bytes (size < 4) are padded, in words. */
txqd_len += 1;
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXQD_MASK;
s->regs[IBEX_SPI_HOST_STATUS] |= txqd_len;
/* Assert Ready */
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_READY_MASK;
break;
case IBEX_SPI_HOST_ERROR_ENABLE:
s->regs[addr] = val32;
if (val32 & R_ERROR_ENABLE_CMDINVAL_MASK) {
qemu_log_mask(LOG_UNIMP,
"%s: Segment Length is not supported\n", __func__);
}
break;
case IBEX_SPI_HOST_ERROR_STATUS:
/*
* Indicates that any errors that have occurred.
* When an error occurs, the corresponding bit must be cleared
* here before issuing any further commands
*/
s->regs[addr] = val32;
break;
case IBEX_SPI_HOST_EVENT_ENABLE:
/* Controls which classes of SPI events raise an interrupt. */
s->regs[addr] = val32;
if (val32 & R_EVENT_ENABLE_RXWM_MASK) {
qemu_log_mask(LOG_UNIMP,
"%s: RXWM is not supported\n", __func__);
}
if (val32 & R_EVENT_ENABLE_TXWM_MASK) {
qemu_log_mask(LOG_UNIMP,
"%s: TXWM is not supported\n", __func__);
}
if (val32 & R_EVENT_ENABLE_IDLE_MASK) {
qemu_log_mask(LOG_UNIMP,
"%s: IDLE is not supported\n", __func__);
}
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "Bad offset 0x%" HWADDR_PRIx "\n",
addr << 2);
}
}
static const MemoryRegionOps ibex_spi_ops = {
.read = ibex_spi_host_read,
.write = ibex_spi_host_write,
/* Ibex default LE */
.endianness = DEVICE_LITTLE_ENDIAN,
};
static Property ibex_spi_properties[] = {
DEFINE_PROP_UINT32("num_cs", IbexSPIHostState, num_cs, 1),
DEFINE_PROP_END_OF_LIST(),
};
static const VMStateDescription vmstate_ibex = {
.name = TYPE_IBEX_SPI_HOST,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, IbexSPIHostState, IBEX_SPI_HOST_MAX_REGS),
VMSTATE_VARRAY_UINT32(config_opts, IbexSPIHostState,
num_cs, 0, vmstate_info_uint32, uint32_t),
VMSTATE_FIFO8(rx_fifo, IbexSPIHostState),
VMSTATE_FIFO8(tx_fifo, IbexSPIHostState),
VMSTATE_TIMER_PTR(fifo_trigger_handle, IbexSPIHostState),
VMSTATE_BOOL(init_status, IbexSPIHostState),
VMSTATE_END_OF_LIST()
}
};
static void fifo_trigger_update(void *opaque)
{
IbexSPIHostState *s = opaque;
ibex_spi_host_transfer(s);
}
static void ibex_spi_host_realize(DeviceState *dev, Error **errp)
{
IbexSPIHostState *s = IBEX_SPI_HOST(dev);
int i;
s->ssi = ssi_create_bus(dev, "ssi");
s->cs_lines = g_new0(qemu_irq, s->num_cs);
for (i = 0; i < s->num_cs; ++i) {
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->cs_lines[i]);
}
/* Setup CONFIGOPTS Multi-register */
s->config_opts = g_new0(uint32_t, s->num_cs);
/* Setup FIFO Interrupt Timer */
s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
fifo_trigger_update, s);
/* FIFO sizes as per OT Spec */
fifo8_create(&s->tx_fifo, IBEX_SPI_HOST_TXFIFO_LEN);
fifo8_create(&s->rx_fifo, IBEX_SPI_HOST_RXFIFO_LEN);
}
static void ibex_spi_host_init(Object *obj)
{
IbexSPIHostState *s = IBEX_SPI_HOST(obj);
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->host_err);
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->event);
memory_region_init_io(&s->mmio, obj, &ibex_spi_ops, s,
TYPE_IBEX_SPI_HOST, 0x1000);
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
}
static void ibex_spi_host_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = ibex_spi_host_realize;
dc->reset = ibex_spi_host_reset;
dc->vmsd = &vmstate_ibex;
device_class_set_props(dc, ibex_spi_properties);
}
static const TypeInfo ibex_spi_host_info = {
.name = TYPE_IBEX_SPI_HOST,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(IbexSPIHostState),
.instance_init = ibex_spi_host_init,
.class_init = ibex_spi_host_class_init,
};
static void ibex_spi_host_register_types(void)
{
type_register_static(&ibex_spi_host_info);
}
type_init(ibex_spi_host_register_types)

View File

@ -10,3 +10,4 @@ softmmu_ss.add(when: 'CONFIG_XILINX_SPIPS', if_true: files('xilinx_spips.c'))
softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-ospi.c'))
softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_spi.c'))
softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_spi.c'))
softmmu_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_spi_host.c'))

View File

@ -20,3 +20,10 @@ npcm7xx_fiu_ctrl_read(const char *id, uint64_t addr, uint32_t data) "%s offset:
npcm7xx_fiu_ctrl_write(const char *id, uint64_t addr, uint32_t data) "%s offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
npcm7xx_fiu_flash_read(const char *id, int cs, uint64_t addr, unsigned int size, uint64_t value) "%s[%d] offset: 0x%08" PRIx64 " size: %u value: 0x%" PRIx64
npcm7xx_fiu_flash_write(const char *id, unsigned cs, uint64_t addr, unsigned int size, uint64_t value) "%s[%d] offset: 0x%08" PRIx64 " size: %u value: 0x%" PRIx64
# ibex_spi_host.c
ibex_spi_host_reset(const char *msg) "%s"
ibex_spi_host_transfer(uint32_t tx_data, uint32_t rx_data) "tx_data: 0x%" PRIx32 " rx_data: @0x%" PRIx32
ibex_spi_host_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64
ibex_spi_host_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size %u:"

View File

@ -90,6 +90,7 @@ struct TCGCPUOps {
/**
* @debug_check_watchpoint: return true if the architectural
* watchpoint whose address has matched should really fire, used by ARM
* and RISC-V
*/
bool (*debug_check_watchpoint)(CPUState *cpu, CPUWatchpoint *wp);

View File

@ -31,6 +31,7 @@
typedef struct RISCVAclintMTimerState {
/*< private >*/
SysBusDevice parent_obj;
uint64_t time_delta;
/*< public >*/
MemoryRegion mmio;

View File

@ -46,12 +46,12 @@ target_ulong riscv_load_kernel(const char *kernel_filename,
symbol_fn_t sym_cb);
hwaddr riscv_load_initrd(const char *filename, uint64_t mem_size,
uint64_t kernel_entry, hwaddr *start);
uint32_t riscv_load_fdt(hwaddr dram_start, uint64_t dram_size, void *fdt);
uint64_t riscv_load_fdt(hwaddr dram_start, uint64_t dram_size, void *fdt);
void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts,
hwaddr saddr,
hwaddr rom_base, hwaddr rom_size,
uint64_t kernel_entry,
uint32_t fdt_load_addr, void *fdt);
uint64_t fdt_load_addr, void *fdt);
void riscv_rom_copy_firmware_info(MachineState *machine, hwaddr rom_base,
hwaddr rom_size,
uint32_t reset_vec_size,

View File

@ -23,11 +23,18 @@
#include "hw/intc/sifive_plic.h"
#include "hw/char/ibex_uart.h"
#include "hw/timer/ibex_timer.h"
#include "hw/ssi/ibex_spi_host.h"
#include "qom/object.h"
#define TYPE_RISCV_IBEX_SOC "riscv.lowrisc.ibex.soc"
OBJECT_DECLARE_SIMPLE_TYPE(LowRISCIbexSoCState, RISCV_IBEX_SOC)
enum {
OPENTITAN_SPI_HOST0,
OPENTITAN_SPI_HOST1,
OPENTITAN_NUM_SPI_HOSTS,
};
struct LowRISCIbexSoCState {
/*< private >*/
SysBusDevice parent_obj;
@ -37,6 +44,7 @@ struct LowRISCIbexSoCState {
SiFivePLICState plic;
IbexUartState uart;
IbexTimerState timer;
IbexSPIHostState spi_host[OPENTITAN_NUM_SPI_HOSTS];
MemoryRegion flash_mem;
MemoryRegion rom;
@ -89,15 +97,19 @@ enum {
};
enum {
IBEX_TIMER_TIMEREXPIRED0_0 = 126,
IBEX_UART0_RX_PARITY_ERR_IRQ = 8,
IBEX_UART0_RX_TIMEOUT_IRQ = 7,
IBEX_UART0_RX_BREAK_ERR_IRQ = 6,
IBEX_UART0_RX_FRAME_ERR_IRQ = 5,
IBEX_UART0_RX_OVERFLOW_IRQ = 4,
IBEX_UART0_TX_EMPTY_IRQ = 3,
IBEX_UART0_RX_WATERMARK_IRQ = 2,
IBEX_UART0_TX_WATERMARK_IRQ = 1,
IBEX_UART0_TX_WATERMARK_IRQ = 1,
IBEX_UART0_RX_WATERMARK_IRQ = 2,
IBEX_UART0_TX_EMPTY_IRQ = 3,
IBEX_UART0_RX_OVERFLOW_IRQ = 4,
IBEX_UART0_RX_FRAME_ERR_IRQ = 5,
IBEX_UART0_RX_BREAK_ERR_IRQ = 6,
IBEX_UART0_RX_TIMEOUT_IRQ = 7,
IBEX_UART0_RX_PARITY_ERR_IRQ = 8,
IBEX_TIMER_TIMEREXPIRED0_0 = 126,
IBEX_SPI_HOST0_ERR_IRQ = 150,
IBEX_SPI_HOST0_SPI_EVENT_IRQ = 151,
IBEX_SPI_HOST1_ERR_IRQ = 152,
IBEX_SPI_HOST1_SPI_EVENT_IRQ = 153,
};
#endif

View File

@ -0,0 +1,94 @@
/*
* QEMU model of the Ibex SPI Controller
* SPEC Reference: https://docs.opentitan.org/hw/ip/spi_host/doc/
*
* Copyright (C) 2022 Western Digital
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef IBEX_SPI_HOST_H
#define IBEX_SPI_HOST_H
#include "hw/sysbus.h"
#include "hw/hw.h"
#include "hw/ssi/ssi.h"
#include "qemu/fifo8.h"
#include "qom/object.h"
#include "hw/registerfields.h"
#include "qemu/timer.h"
#define TYPE_IBEX_SPI_HOST "ibex-spi"
#define IBEX_SPI_HOST(obj) \
OBJECT_CHECK(IbexSPIHostState, (obj), TYPE_IBEX_SPI_HOST)
/* SPI Registers */
#define IBEX_SPI_HOST_INTR_STATE (0x00 / 4) /* rw */
#define IBEX_SPI_HOST_INTR_ENABLE (0x04 / 4) /* rw */
#define IBEX_SPI_HOST_INTR_TEST (0x08 / 4) /* wo */
#define IBEX_SPI_HOST_ALERT_TEST (0x0c / 4) /* wo */
#define IBEX_SPI_HOST_CONTROL (0x10 / 4) /* rw */
#define IBEX_SPI_HOST_STATUS (0x14 / 4) /* ro */
#define IBEX_SPI_HOST_CONFIGOPTS (0x18 / 4) /* rw */
#define IBEX_SPI_HOST_CSID (0x1c / 4) /* rw */
#define IBEX_SPI_HOST_COMMAND (0x20 / 4) /* wo */
/* RX/TX Modelled by FIFO */
#define IBEX_SPI_HOST_RXDATA (0x24 / 4)
#define IBEX_SPI_HOST_TXDATA (0x28 / 4)
#define IBEX_SPI_HOST_ERROR_ENABLE (0x2c / 4) /* rw */
#define IBEX_SPI_HOST_ERROR_STATUS (0x30 / 4) /* rw */
#define IBEX_SPI_HOST_EVENT_ENABLE (0x34 / 4) /* rw */
/* FIFO Len in Bytes */
#define IBEX_SPI_HOST_TXFIFO_LEN 288
#define IBEX_SPI_HOST_RXFIFO_LEN 256
/* Max Register (Based on addr) */
#define IBEX_SPI_HOST_MAX_REGS (IBEX_SPI_HOST_EVENT_ENABLE + 1)
/* MISC */
#define TX_INTERRUPT_TRIGGER_DELAY_NS 100
#define BIDIRECTIONAL_TRANSFER 3
typedef struct {
/* <private> */
SysBusDevice parent_obj;
/* <public> */
MemoryRegion mmio;
uint32_t regs[IBEX_SPI_HOST_MAX_REGS];
/* Multi-reg that sets config opts per CS */
uint32_t *config_opts;
Fifo8 rx_fifo;
Fifo8 tx_fifo;
QEMUTimer *fifo_trigger_handle;
qemu_irq event;
qemu_irq host_err;
uint32_t num_cs;
qemu_irq *cs_lines;
SSIBus *ssi;
/* Used to track the init status, for replicating TXDATA ghost writes */
bool init_status;
} IbexSPIHostState;
#endif

View File

@ -34,7 +34,12 @@
/* RISC-V CPU definitions */
static const char riscv_exts[26] = "IEMAFDQCLBJTPVNSUHKORWXYZG";
static const char riscv_single_letter_exts[] = "IEMAFDQCPVH";
struct isa_ext_data {
const char *name;
bool enabled;
};
const char * const riscv_int_regnames[] = {
"x0/zero", "x1/ra", "x2/sp", "x3/gp", "x4/tp", "x5/t0", "x6/t1",
@ -150,7 +155,7 @@ static void riscv_any_cpu_init(Object *obj)
#elif defined(TARGET_RISCV64)
set_misa(env, MXL_RV64, RVI | RVM | RVA | RVF | RVD | RVC | RVU);
#endif
set_priv_version(env, PRIV_VERSION_1_11_0);
set_priv_version(env, PRIV_VERSION_1_12_0);
}
#if defined(TARGET_RISCV64)
@ -461,6 +466,10 @@ static void riscv_cpu_reset(DeviceState *dev)
set_default_nan_mode(1, &env->fp_status);
#ifndef CONFIG_USER_ONLY
if (riscv_feature(env, RISCV_FEATURE_DEBUG)) {
riscv_trigger_init(env);
}
if (kvm_enabled()) {
kvm_riscv_reset_vcpu(cpu);
}
@ -503,7 +512,9 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
}
if (cpu->cfg.priv_spec) {
if (!g_strcmp0(cpu->cfg.priv_spec, "v1.11.0")) {
if (!g_strcmp0(cpu->cfg.priv_spec, "v1.12.0")) {
priv_version = PRIV_VERSION_1_12_0;
} else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.11.0")) {
priv_version = PRIV_VERSION_1_11_0;
} else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.10.0")) {
priv_version = PRIV_VERSION_1_10_0;
@ -518,7 +529,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
if (priv_version) {
set_priv_version(env, priv_version);
} else if (!env->priv_ver) {
set_priv_version(env, PRIV_VERSION_1_11_0);
set_priv_version(env, PRIV_VERSION_1_12_0);
}
if (cpu->cfg.mmu) {
@ -541,6 +552,10 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
riscv_set_feature(env, RISCV_FEATURE_AIA);
}
if (cpu->cfg.debug) {
riscv_set_feature(env, RISCV_FEATURE_DEBUG);
}
set_resetvec(env, cpu->cfg.resetvec);
/* Validate that MISA_MXL is set properly. */
@ -567,18 +582,18 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
if (cpu->cfg.ext_i && cpu->cfg.ext_e) {
error_setg(errp,
"I and E extensions are incompatible");
return;
}
return;
}
if (!cpu->cfg.ext_i && !cpu->cfg.ext_e) {
error_setg(errp,
"Either I or E extension must be set");
return;
}
return;
}
if (cpu->cfg.ext_g && !(cpu->cfg.ext_i & cpu->cfg.ext_m &
cpu->cfg.ext_a & cpu->cfg.ext_f &
cpu->cfg.ext_d)) {
if (cpu->cfg.ext_g && !(cpu->cfg.ext_i & cpu->cfg.ext_m &
cpu->cfg.ext_a & cpu->cfg.ext_f &
cpu->cfg.ext_d)) {
warn_report("Setting G will also set IMAFD");
cpu->cfg.ext_i = true;
cpu->cfg.ext_m = true;
@ -706,15 +721,23 @@ static void riscv_cpu_set_irq(void *opaque, int irq, int level)
case IRQ_VS_TIMER:
case IRQ_M_TIMER:
case IRQ_U_EXT:
case IRQ_S_EXT:
case IRQ_VS_EXT:
case IRQ_M_EXT:
if (kvm_enabled()) {
if (kvm_enabled()) {
kvm_riscv_set_irq(cpu, irq, level);
} else {
} else {
riscv_cpu_update_mip(cpu, 1 << irq, BOOL_TO_MASK(level));
}
}
break;
case IRQ_S_EXT:
if (kvm_enabled()) {
kvm_riscv_set_irq(cpu, irq, level);
} else {
env->external_seip = level;
riscv_cpu_update_mip(cpu, 1 << irq,
BOOL_TO_MASK(level | env->software_seip));
}
break;
default:
g_assert_not_reached();
}
@ -780,6 +803,7 @@ static Property riscv_cpu_properties[] = {
DEFINE_PROP_BOOL("Zve64f", RISCVCPU, cfg.ext_zve64f, false),
DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true),
DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec),
DEFINE_PROP_STRING("vext_spec", RISCVCPU, cfg.vext_spec),
@ -865,6 +889,9 @@ static const struct TCGCPUOps riscv_tcg_ops = {
.do_interrupt = riscv_cpu_do_interrupt,
.do_transaction_failed = riscv_cpu_do_transaction_failed,
.do_unaligned_access = riscv_cpu_do_unaligned_access,
.debug_excp_handler = riscv_cpu_debug_excp_handler,
.debug_check_breakpoint = riscv_cpu_debug_check_breakpoint,
.debug_check_watchpoint = riscv_cpu_debug_check_watchpoint,
#endif /* !CONFIG_USER_ONLY */
};
@ -898,18 +925,73 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data)
device_class_set_props(dc, riscv_cpu_properties);
}
#define ISA_EDATA_ENTRY(name, prop) {#name, cpu->cfg.prop}
static void riscv_isa_string_ext(RISCVCPU *cpu, char **isa_str, int max_str_len)
{
char *old = *isa_str;
char *new = *isa_str;
int i;
/**
* Here are the ordering rules of extension naming defined by RISC-V
* specification :
* 1. All extensions should be separated from other multi-letter extensions
* by an underscore.
* 2. The first letter following the 'Z' conventionally indicates the most
* closely related alphabetical extension category, IMAFDQLCBKJTPVH.
* If multiple 'Z' extensions are named, they should be ordered first
* by category, then alphabetically within a category.
* 3. Standard supervisor-level extensions (starts with 'S') should be
* listed after standard unprivileged extensions. If multiple
* supervisor-level extensions are listed, they should be ordered
* alphabetically.
* 4. Non-standard extensions (starts with 'X') must be listed after all
* standard extensions. They must be separated from other multi-letter
* extensions by an underscore.
*/
struct isa_ext_data isa_edata_arr[] = {
ISA_EDATA_ENTRY(zfh, ext_zfh),
ISA_EDATA_ENTRY(zfhmin, ext_zfhmin),
ISA_EDATA_ENTRY(zfinx, ext_zfinx),
ISA_EDATA_ENTRY(zhinx, ext_zhinx),
ISA_EDATA_ENTRY(zhinxmin, ext_zhinxmin),
ISA_EDATA_ENTRY(zdinx, ext_zdinx),
ISA_EDATA_ENTRY(zba, ext_zba),
ISA_EDATA_ENTRY(zbb, ext_zbb),
ISA_EDATA_ENTRY(zbc, ext_zbc),
ISA_EDATA_ENTRY(zbs, ext_zbs),
ISA_EDATA_ENTRY(zve32f, ext_zve32f),
ISA_EDATA_ENTRY(zve64f, ext_zve64f),
ISA_EDATA_ENTRY(svinval, ext_svinval),
ISA_EDATA_ENTRY(svnapot, ext_svnapot),
ISA_EDATA_ENTRY(svpbmt, ext_svpbmt),
};
for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) {
if (isa_edata_arr[i].enabled) {
new = g_strconcat(old, "_", isa_edata_arr[i].name, NULL);
g_free(old);
old = new;
}
}
*isa_str = new;
}
char *riscv_isa_string(RISCVCPU *cpu)
{
int i;
const size_t maxlen = sizeof("rv128") + sizeof(riscv_exts) + 1;
const size_t maxlen = sizeof("rv128") + sizeof(riscv_single_letter_exts);
char *isa_str = g_new(char, maxlen);
char *p = isa_str + snprintf(isa_str, maxlen, "rv%d", TARGET_LONG_BITS);
for (i = 0; i < sizeof(riscv_exts); i++) {
if (cpu->env.misa_ext & RV(riscv_exts[i])) {
*p++ = qemu_tolower(riscv_exts[i]);
for (i = 0; i < sizeof(riscv_single_letter_exts) - 1; i++) {
if (cpu->env.misa_ext & RV(riscv_single_letter_exts[i])) {
*p++ = qemu_tolower(riscv_single_letter_exts[i]);
}
}
*p = '\0';
riscv_isa_string_ext(cpu, &isa_str, maxlen);
return isa_str;
}

View File

@ -79,11 +79,16 @@ enum {
RISCV_FEATURE_PMP,
RISCV_FEATURE_EPMP,
RISCV_FEATURE_MISA,
RISCV_FEATURE_AIA
RISCV_FEATURE_AIA,
RISCV_FEATURE_DEBUG
};
#define PRIV_VERSION_1_10_0 0x00011000
#define PRIV_VERSION_1_11_0 0x00011100
/* Privileged specification version */
enum {
PRIV_VERSION_1_10_0 = 0,
PRIV_VERSION_1_11_0,
PRIV_VERSION_1_12_0,
};
#define VEXT_VERSION_1_00_0 0x00010000
@ -102,6 +107,7 @@ typedef struct CPUArchState CPURISCVState;
#if !defined(CONFIG_USER_ONLY)
#include "pmp.h"
#include "debug.h"
#endif
#define RV_VLEN_MAX 1024
@ -173,6 +179,14 @@ struct CPUArchState {
uint64_t mstatus;
uint64_t mip;
/*
* MIP contains the software writable version of SEIP ORed with the
* external interrupt value. The MIP register is always up-to-date.
* To keep track of the current source, we also save booleans of the values
* here.
*/
bool external_seip;
bool software_seip;
uint64_t miclaim;
@ -267,9 +281,13 @@ struct CPUArchState {
pmp_table_t pmp_state;
target_ulong mseccfg;
/* trigger module */
target_ulong trigger_cur;
type2_trigger_t type2_trig[TRIGGER_TYPE2_NUM];
/* machine specific rdtime callback */
uint64_t (*rdtime_fn)(uint32_t);
uint32_t rdtime_fn_arg;
uint64_t (*rdtime_fn)(void *);
void *rdtime_fn_arg;
/* machine specific AIA ireg read-modify-write callback */
#define AIA_MAKE_IREG(__isel, __priv, __virt, __vgein, __xlen) \
@ -300,6 +318,11 @@ struct CPUArchState {
target_ulong spmbase;
target_ulong upmmask;
target_ulong upmbase;
/* CSRs for execution enviornment configuration */
uint64_t menvcfg;
target_ulong senvcfg;
uint64_t henvcfg;
#endif
target_ulong cur_pmmask;
target_ulong cur_pmbase;
@ -383,6 +406,7 @@ struct RISCVCPUConfig {
bool pmp;
bool epmp;
bool aia;
bool debug;
uint64_t resetvec;
};
@ -474,8 +498,8 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env);
int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts);
uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value);
#define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */
void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t),
uint32_t arg);
void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *),
void *arg);
void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv,
int (*rmw_fn)(void *arg,
target_ulong reg,
@ -654,6 +678,8 @@ typedef struct {
riscv_csr_op_fn op;
riscv_csr_read128_fn read128;
riscv_csr_write128_fn write128;
/* The default priv spec version should be PRIV_VERSION_1_10_0 (i.e 0) */
uint32_t min_priv_ver;
} riscv_csr_operations;
/* CSR function table constants */

View File

@ -148,6 +148,7 @@
#define CSR_MARCHID 0xf12
#define CSR_MIMPID 0xf13
#define CSR_MHARTID 0xf14
#define CSR_MCONFIGPTR 0xf15
/* Machine Trap Setup */
#define CSR_MSTATUS 0x300
@ -201,6 +202,9 @@
#define CSR_STVEC 0x105
#define CSR_SCOUNTEREN 0x106
/* Supervisor Configuration CSRs */
#define CSR_SENVCFG 0x10A
/* Supervisor Trap Handling */
#define CSR_SSCRATCH 0x140
#define CSR_SEPC 0x141
@ -246,6 +250,10 @@
#define CSR_HTIMEDELTA 0x605
#define CSR_HTIMEDELTAH 0x615
/* Hypervisor Configuration CSRs */
#define CSR_HENVCFG 0x60A
#define CSR_HENVCFGH 0x61A
/* Virtual CSRs */
#define CSR_VSSTATUS 0x200
#define CSR_VSIE 0x204
@ -289,6 +297,10 @@
#define CSR_VSIEH 0x214
#define CSR_VSIPH 0x254
/* Machine Configuration CSRs */
#define CSR_MENVCFG 0x30A
#define CSR_MENVCFGH 0x31A
/* Enhanced Physical Memory Protection (ePMP) */
#define CSR_MSECCFG 0x747
#define CSR_MSECCFGH 0x757
@ -662,6 +674,34 @@ typedef enum RISCVException {
#define PM_EXT_CLEAN 0x00000002ULL
#define PM_EXT_DIRTY 0x00000003ULL
/* Execution enviornment configuration bits */
#define MENVCFG_FIOM BIT(0)
#define MENVCFG_CBIE (3UL << 4)
#define MENVCFG_CBCFE BIT(6)
#define MENVCFG_CBZE BIT(7)
#define MENVCFG_PBMTE (1ULL << 62)
#define MENVCFG_STCE (1ULL << 63)
/* For RV32 */
#define MENVCFGH_PBMTE BIT(30)
#define MENVCFGH_STCE BIT(31)
#define SENVCFG_FIOM MENVCFG_FIOM
#define SENVCFG_CBIE MENVCFG_CBIE
#define SENVCFG_CBCFE MENVCFG_CBCFE
#define SENVCFG_CBZE MENVCFG_CBZE
#define HENVCFG_FIOM MENVCFG_FIOM
#define HENVCFG_CBIE MENVCFG_CBIE
#define HENVCFG_CBCFE MENVCFG_CBCFE
#define HENVCFG_CBZE MENVCFG_CBZE
#define HENVCFG_PBMTE MENVCFG_PBMTE
#define HENVCFG_STCE MENVCFG_STCE
/* For RV32 */
#define HENVCFGH_PBMTE MENVCFGH_PBMTE
#define HENVCFGH_STCE MENVCFGH_STCE
/* Offsets for every pair of control bits per each priv level */
#define XS_OFFSET 0ULL
#define U_OFFSET 2ULL

View File

@ -632,8 +632,8 @@ uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value)
return old;
}
void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t),
uint32_t arg)
void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *),
void *arg)
{
env->rdtime_fn = fn;
env->rdtime_fn_arg = arg;
@ -1150,7 +1150,7 @@ void riscv_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
env->badaddr = addr;
env->two_stage_lookup = riscv_cpu_virt_enabled(env) ||
riscv_cpu_two_stage_lookup(mmu_idx);
riscv_raise_exception(&cpu->env, cs->exception_index, retaddr);
cpu_loop_exit_restore(cs, retaddr);
}
void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
@ -1175,7 +1175,7 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
env->badaddr = addr;
env->two_stage_lookup = riscv_cpu_virt_enabled(env) ||
riscv_cpu_two_stage_lookup(mmu_idx);
riscv_raise_exception(env, cs->exception_index, retaddr);
cpu_loop_exit_restore(cs, retaddr);
}
bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
@ -1311,7 +1311,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
first_stage_error,
riscv_cpu_virt_enabled(env) ||
riscv_cpu_two_stage_lookup(mmu_idx));
riscv_raise_exception(env, cs->exception_index, retaddr);
cpu_loop_exit_restore(cs, retaddr);
}
return true;

View File

@ -290,6 +290,15 @@ static RISCVException epmp(CPURISCVState *env, int csrno)
return RISCV_EXCP_ILLEGAL_INST;
}
static RISCVException debug(CPURISCVState *env, int csrno)
{
if (riscv_feature(env, RISCV_FEATURE_DEBUG)) {
return RISCV_EXCP_NONE;
}
return RISCV_EXCP_ILLEGAL_INST;
}
#endif
/* User Floating-Point CSRs */
@ -1398,15 +1407,114 @@ static RISCVException write_mtval(CPURISCVState *env, int csrno,
return RISCV_EXCP_NONE;
}
/* Execution environment configuration setup */
static RISCVException read_menvcfg(CPURISCVState *env, int csrno,
target_ulong *val)
{
*val = env->menvcfg;
return RISCV_EXCP_NONE;
}
static RISCVException write_menvcfg(CPURISCVState *env, int csrno,
target_ulong val)
{
uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | MENVCFG_CBZE;
if (riscv_cpu_mxl(env) == MXL_RV64) {
mask |= MENVCFG_PBMTE | MENVCFG_STCE;
}
env->menvcfg = (env->menvcfg & ~mask) | (val & mask);
return RISCV_EXCP_NONE;
}
static RISCVException read_menvcfgh(CPURISCVState *env, int csrno,
target_ulong *val)
{
*val = env->menvcfg >> 32;
return RISCV_EXCP_NONE;
}
static RISCVException write_menvcfgh(CPURISCVState *env, int csrno,
target_ulong val)
{
uint64_t mask = MENVCFG_PBMTE | MENVCFG_STCE;
uint64_t valh = (uint64_t)val << 32;
env->menvcfg = (env->menvcfg & ~mask) | (valh & mask);
return RISCV_EXCP_NONE;
}
static RISCVException read_senvcfg(CPURISCVState *env, int csrno,
target_ulong *val)
{
*val = env->senvcfg;
return RISCV_EXCP_NONE;
}
static RISCVException write_senvcfg(CPURISCVState *env, int csrno,
target_ulong val)
{
uint64_t mask = SENVCFG_FIOM | SENVCFG_CBIE | SENVCFG_CBCFE | SENVCFG_CBZE;
env->senvcfg = (env->senvcfg & ~mask) | (val & mask);
return RISCV_EXCP_NONE;
}
static RISCVException read_henvcfg(CPURISCVState *env, int csrno,
target_ulong *val)
{
*val = env->henvcfg;
return RISCV_EXCP_NONE;
}
static RISCVException write_henvcfg(CPURISCVState *env, int csrno,
target_ulong val)
{
uint64_t mask = HENVCFG_FIOM | HENVCFG_CBIE | HENVCFG_CBCFE | HENVCFG_CBZE;
if (riscv_cpu_mxl(env) == MXL_RV64) {
mask |= HENVCFG_PBMTE | HENVCFG_STCE;
}
env->henvcfg = (env->henvcfg & ~mask) | (val & mask);
return RISCV_EXCP_NONE;
}
static RISCVException read_henvcfgh(CPURISCVState *env, int csrno,
target_ulong *val)
{
*val = env->henvcfg >> 32;
return RISCV_EXCP_NONE;
}
static RISCVException write_henvcfgh(CPURISCVState *env, int csrno,
target_ulong val)
{
uint64_t mask = HENVCFG_PBMTE | HENVCFG_STCE;
uint64_t valh = (uint64_t)val << 32;
env->henvcfg = (env->henvcfg & ~mask) | (valh & mask);
return RISCV_EXCP_NONE;
}
static RISCVException rmw_mip64(CPURISCVState *env, int csrno,
uint64_t *ret_val,
uint64_t new_val, uint64_t wr_mask)
{
RISCVCPU *cpu = env_archcpu(env);
/* Allow software control of delegable interrupts not claimed by hardware */
uint64_t old_mip, mask = wr_mask & delegable_ints & ~env->miclaim;
uint64_t old_mip, mask = wr_mask & delegable_ints;
uint32_t gin;
if (mask & MIP_SEIP) {
env->software_seip = new_val & MIP_SEIP;
new_val |= env->external_seip * MIP_SEIP;
}
if (mask) {
old_mip = riscv_cpu_update_mip(cpu, mask, (new_val & mask));
} else {
@ -2578,6 +2686,48 @@ static RISCVException write_pmpaddr(CPURISCVState *env, int csrno,
return RISCV_EXCP_NONE;
}
static RISCVException read_tselect(CPURISCVState *env, int csrno,
target_ulong *val)
{
*val = tselect_csr_read(env);
return RISCV_EXCP_NONE;
}
static RISCVException write_tselect(CPURISCVState *env, int csrno,
target_ulong val)
{
tselect_csr_write(env, val);
return RISCV_EXCP_NONE;
}
static RISCVException read_tdata(CPURISCVState *env, int csrno,
target_ulong *val)
{
/* return 0 in tdata1 to end the trigger enumeration */
if (env->trigger_cur >= TRIGGER_NUM && csrno == CSR_TDATA1) {
*val = 0;
return RISCV_EXCP_NONE;
}
if (!tdata_available(env, csrno - CSR_TDATA1)) {
return RISCV_EXCP_ILLEGAL_INST;
}
*val = tdata_csr_read(env, csrno - CSR_TDATA1);
return RISCV_EXCP_NONE;
}
static RISCVException write_tdata(CPURISCVState *env, int csrno,
target_ulong val)
{
if (!tdata_available(env, csrno - CSR_TDATA1)) {
return RISCV_EXCP_ILLEGAL_INST;
}
tdata_csr_write(env, csrno - CSR_TDATA1, val);
return RISCV_EXCP_NONE;
}
/*
* Functions to access Pointer Masking feature registers
* We have to check if current priv lvl could modify
@ -2880,6 +3030,7 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env,
{
/* check privileges and return RISCV_EXCP_ILLEGAL_INST if check fails */
int read_only = get_field(csrno, 0xC00) == 3;
int csr_min_priv = csr_ops[csrno].min_priv_ver;
#if !defined(CONFIG_USER_ONLY)
int effective_priv = env->priv;
@ -2912,6 +3063,10 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env,
return RISCV_EXCP_ILLEGAL_INST;
}
if (env->priv_ver < csr_min_priv) {
return RISCV_EXCP_ILLEGAL_INST;
}
return csr_ops[csrno].predicate(env, csrno);
}
@ -3070,13 +3225,20 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_FRM] = { "frm", fs, read_frm, write_frm },
[CSR_FCSR] = { "fcsr", fs, read_fcsr, write_fcsr },
/* Vector CSRs */
[CSR_VSTART] = { "vstart", vs, read_vstart, write_vstart },
[CSR_VXSAT] = { "vxsat", vs, read_vxsat, write_vxsat },
[CSR_VXRM] = { "vxrm", vs, read_vxrm, write_vxrm },
[CSR_VCSR] = { "vcsr", vs, read_vcsr, write_vcsr },
[CSR_VL] = { "vl", vs, read_vl },
[CSR_VTYPE] = { "vtype", vs, read_vtype },
[CSR_VLENB] = { "vlenb", vs, read_vlenb },
[CSR_VSTART] = { "vstart", vs, read_vstart, write_vstart,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_VXSAT] = { "vxsat", vs, read_vxsat, write_vxsat,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_VXRM] = { "vxrm", vs, read_vxrm, write_vxrm,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_VCSR] = { "vcsr", vs, read_vcsr, write_vcsr,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_VL] = { "vl", vs, read_vl,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_VTYPE] = { "vtype", vs, read_vtype,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_VLENB] = { "vlenb", vs, read_vlenb,
.min_priv_ver = PRIV_VERSION_1_12_0 },
/* User Timers and Counters */
[CSR_CYCLE] = { "cycle", ctr, read_instret },
[CSR_INSTRET] = { "instret", ctr, read_instret },
@ -3103,6 +3265,8 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_MIMPID] = { "mimpid", any, read_zero },
[CSR_MHARTID] = { "mhartid", any, read_mhartid },
[CSR_MCONFIGPTR] = { "mconfigptr", any, read_zero,
.min_priv_ver = PRIV_VERSION_1_12_0 },
/* Machine Trap Setup */
[CSR_MSTATUS] = { "mstatus", any, read_mstatus, write_mstatus, NULL,
read_mstatus_i128 },
@ -3149,6 +3313,18 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_MVIPH] = { "mviph", aia_any32, read_zero, write_ignore },
[CSR_MIPH] = { "miph", aia_any32, NULL, NULL, rmw_miph },
/* Execution environment configuration */
[CSR_MENVCFG] = { "menvcfg", any, read_menvcfg, write_menvcfg,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_MENVCFGH] = { "menvcfgh", any32, read_menvcfgh, write_menvcfgh,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_SENVCFG] = { "senvcfg", smode, read_senvcfg, write_senvcfg,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_HENVCFG] = { "henvcfg", hmode, read_henvcfg, write_henvcfg,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_HENVCFGH] = { "henvcfgh", hmode32, read_henvcfgh, write_henvcfgh,
.min_priv_ver = PRIV_VERSION_1_12_0 },
/* Supervisor Trap Setup */
[CSR_SSTATUS] = { "sstatus", smode, read_sstatus, write_sstatus, NULL,
read_sstatus_i128 },
@ -3185,33 +3361,58 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_SIEH] = { "sieh", aia_smode32, NULL, NULL, rmw_sieh },
[CSR_SIPH] = { "siph", aia_smode32, NULL, NULL, rmw_siph },
[CSR_HSTATUS] = { "hstatus", hmode, read_hstatus, write_hstatus },
[CSR_HEDELEG] = { "hedeleg", hmode, read_hedeleg, write_hedeleg },
[CSR_HIDELEG] = { "hideleg", hmode, NULL, NULL, rmw_hideleg },
[CSR_HVIP] = { "hvip", hmode, NULL, NULL, rmw_hvip },
[CSR_HIP] = { "hip", hmode, NULL, NULL, rmw_hip },
[CSR_HIE] = { "hie", hmode, NULL, NULL, rmw_hie },
[CSR_HCOUNTEREN] = { "hcounteren", hmode, read_hcounteren, write_hcounteren },
[CSR_HGEIE] = { "hgeie", hmode, read_hgeie, write_hgeie },
[CSR_HTVAL] = { "htval", hmode, read_htval, write_htval },
[CSR_HTINST] = { "htinst", hmode, read_htinst, write_htinst },
[CSR_HGEIP] = { "hgeip", hmode, read_hgeip, NULL },
[CSR_HGATP] = { "hgatp", hmode, read_hgatp, write_hgatp },
[CSR_HTIMEDELTA] = { "htimedelta", hmode, read_htimedelta, write_htimedelta },
[CSR_HTIMEDELTAH] = { "htimedeltah", hmode32, read_htimedeltah, write_htimedeltah },
[CSR_HSTATUS] = { "hstatus", hmode, read_hstatus, write_hstatus,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_HEDELEG] = { "hedeleg", hmode, read_hedeleg, write_hedeleg,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_HIDELEG] = { "hideleg", hmode, NULL, NULL, rmw_hideleg,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_HVIP] = { "hvip", hmode, NULL, NULL, rmw_hvip,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_HIP] = { "hip", hmode, NULL, NULL, rmw_hip,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_HIE] = { "hie", hmode, NULL, NULL, rmw_hie,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_HCOUNTEREN] = { "hcounteren", hmode, read_hcounteren, write_hcounteren,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_HGEIE] = { "hgeie", hmode, read_hgeie, write_hgeie,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_HTVAL] = { "htval", hmode, read_htval, write_htval,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_HTINST] = { "htinst", hmode, read_htinst, write_htinst,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_HGEIP] = { "hgeip", hmode, read_hgeip,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_HGATP] = { "hgatp", hmode, read_hgatp, write_hgatp,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_HTIMEDELTA] = { "htimedelta", hmode, read_htimedelta, write_htimedelta,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_HTIMEDELTAH] = { "htimedeltah", hmode32, read_htimedeltah, write_htimedeltah,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_VSSTATUS] = { "vsstatus", hmode, read_vsstatus, write_vsstatus },
[CSR_VSIP] = { "vsip", hmode, NULL, NULL, rmw_vsip },
[CSR_VSIE] = { "vsie", hmode, NULL, NULL, rmw_vsie },
[CSR_VSTVEC] = { "vstvec", hmode, read_vstvec, write_vstvec },
[CSR_VSSCRATCH] = { "vsscratch", hmode, read_vsscratch, write_vsscratch },
[CSR_VSEPC] = { "vsepc", hmode, read_vsepc, write_vsepc },
[CSR_VSCAUSE] = { "vscause", hmode, read_vscause, write_vscause },
[CSR_VSTVAL] = { "vstval", hmode, read_vstval, write_vstval },
[CSR_VSATP] = { "vsatp", hmode, read_vsatp, write_vsatp },
[CSR_VSSTATUS] = { "vsstatus", hmode, read_vsstatus, write_vsstatus,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_VSIP] = { "vsip", hmode, NULL, NULL, rmw_vsip,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_VSIE] = { "vsie", hmode, NULL, NULL, rmw_vsie ,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_VSTVEC] = { "vstvec", hmode, read_vstvec, write_vstvec,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_VSSCRATCH] = { "vsscratch", hmode, read_vsscratch, write_vsscratch,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_VSEPC] = { "vsepc", hmode, read_vsepc, write_vsepc,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_VSCAUSE] = { "vscause", hmode, read_vscause, write_vscause,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_VSTVAL] = { "vstval", hmode, read_vstval, write_vstval,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_VSATP] = { "vsatp", hmode, read_vsatp, write_vsatp,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2 },
[CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst },
[CSR_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst,
.min_priv_ver = PRIV_VERSION_1_12_0 },
/* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */
[CSR_HVIEN] = { "hvien", aia_hmode, read_zero, write_ignore },
@ -3245,7 +3446,8 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_VSIPH] = { "vsiph", aia_hmode32, NULL, NULL, rmw_vsiph },
/* Physical Memory Protection */
[CSR_MSECCFG] = { "mseccfg", epmp, read_mseccfg, write_mseccfg },
[CSR_MSECCFG] = { "mseccfg", epmp, read_mseccfg, write_mseccfg,
.min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_PMPCFG0] = { "pmpcfg0", pmp, read_pmpcfg, write_pmpcfg },
[CSR_PMPCFG1] = { "pmpcfg1", pmp, read_pmpcfg, write_pmpcfg },
[CSR_PMPCFG2] = { "pmpcfg2", pmp, read_pmpcfg, write_pmpcfg },
@ -3267,6 +3469,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_PMPADDR14] = { "pmpaddr14", pmp, read_pmpaddr, write_pmpaddr },
[CSR_PMPADDR15] = { "pmpaddr15", pmp, read_pmpaddr, write_pmpaddr },
/* Debug CSRs */
[CSR_TSELECT] = { "tselect", debug, read_tselect, write_tselect },
[CSR_TDATA1] = { "tdata1", debug, read_tdata, write_tdata },
[CSR_TDATA2] = { "tdata2", debug, read_tdata, write_tdata },
[CSR_TDATA3] = { "tdata3", debug, read_tdata, write_tdata },
/* User Pointer Masking */
[CSR_UMTE] = { "umte", pointer_masking, read_umte, write_umte },
[CSR_UPMMASK] = { "upmmask", pointer_masking, read_upmmask, write_upmmask },

441
target/riscv/debug.c Normal file
View File

@ -0,0 +1,441 @@
/*
* QEMU RISC-V Native Debug Support
*
* Copyright (c) 2022 Wind River Systems, Inc.
*
* Author:
* Bin Meng <bin.meng@windriver.com>
*
* This provides the native debug support via the Trigger Module, as defined
* in the RISC-V Debug Specification:
* https://github.com/riscv/riscv-debug-spec/raw/master/riscv-debug-stable.pdf
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2 or later, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qapi/error.h"
#include "cpu.h"
#include "trace.h"
#include "exec/exec-all.h"
/*
* The following M-mode trigger CSRs are implemented:
*
* - tselect
* - tdata1
* - tdata2
* - tdata3
*
* We don't support writable 'type' field in the tdata1 register, so there is
* no need to implement the "tinfo" CSR.
*
* The following triggers are implemented:
*
* Index | Type | tdata mapping | Description
* ------+------+------------------------+------------
* 0 | 2 | tdata1, tdata2 | Address / Data Match
* 1 | 2 | tdata1, tdata2 | Address / Data Match
*/
/* tdata availability of a trigger */
typedef bool tdata_avail[TDATA_NUM];
static tdata_avail tdata_mapping[TRIGGER_NUM] = {
[TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = { true, true, false },
};
/* only breakpoint size 1/2/4/8 supported */
static int access_size[SIZE_NUM] = {
[SIZE_ANY] = 0,
[SIZE_1B] = 1,
[SIZE_2B] = 2,
[SIZE_4B] = 4,
[SIZE_6B] = -1,
[SIZE_8B] = 8,
[6 ... 15] = -1,
};
static inline target_ulong trigger_type(CPURISCVState *env,
trigger_type_t type)
{
target_ulong tdata1;
switch (riscv_cpu_mxl(env)) {
case MXL_RV32:
tdata1 = RV32_TYPE(type);
break;
case MXL_RV64:
tdata1 = RV64_TYPE(type);
break;
default:
g_assert_not_reached();
}
return tdata1;
}
bool tdata_available(CPURISCVState *env, int tdata_index)
{
if (unlikely(tdata_index >= TDATA_NUM)) {
return false;
}
if (unlikely(env->trigger_cur >= TRIGGER_NUM)) {
return false;
}
return tdata_mapping[env->trigger_cur][tdata_index];
}
target_ulong tselect_csr_read(CPURISCVState *env)
{
return env->trigger_cur;
}
void tselect_csr_write(CPURISCVState *env, target_ulong val)
{
/* all target_ulong bits of tselect are implemented */
env->trigger_cur = val;
}
static target_ulong tdata1_validate(CPURISCVState *env, target_ulong val,
trigger_type_t t)
{
uint32_t type, dmode;
target_ulong tdata1;
switch (riscv_cpu_mxl(env)) {
case MXL_RV32:
type = extract32(val, 28, 4);
dmode = extract32(val, 27, 1);
tdata1 = RV32_TYPE(t);
break;
case MXL_RV64:
type = extract64(val, 60, 4);
dmode = extract64(val, 59, 1);
tdata1 = RV64_TYPE(t);
break;
default:
g_assert_not_reached();
}
if (type != t) {
qemu_log_mask(LOG_GUEST_ERROR,
"ignoring type write to tdata1 register\n");
}
if (dmode != 0) {
qemu_log_mask(LOG_UNIMP, "debug mode is not supported\n");
}
return tdata1;
}
static inline void warn_always_zero_bit(target_ulong val, target_ulong mask,
const char *msg)
{
if (val & mask) {
qemu_log_mask(LOG_UNIMP, "%s bit is always zero\n", msg);
}
}
static uint32_t type2_breakpoint_size(CPURISCVState *env, target_ulong ctrl)
{
uint32_t size, sizelo, sizehi = 0;
if (riscv_cpu_mxl(env) == MXL_RV64) {
sizehi = extract32(ctrl, 21, 2);
}
sizelo = extract32(ctrl, 16, 2);
size = (sizehi << 2) | sizelo;
return size;
}
static inline bool type2_breakpoint_enabled(target_ulong ctrl)
{
bool mode = !!(ctrl & (TYPE2_U | TYPE2_S | TYPE2_M));
bool rwx = !!(ctrl & (TYPE2_LOAD | TYPE2_STORE | TYPE2_EXEC));
return mode && rwx;
}
static target_ulong type2_mcontrol_validate(CPURISCVState *env,
target_ulong ctrl)
{
target_ulong val;
uint32_t size;
/* validate the generic part first */
val = tdata1_validate(env, ctrl, TRIGGER_TYPE_AD_MATCH);
/* validate unimplemented (always zero) bits */
warn_always_zero_bit(ctrl, TYPE2_MATCH, "match");
warn_always_zero_bit(ctrl, TYPE2_CHAIN, "chain");
warn_always_zero_bit(ctrl, TYPE2_ACTION, "action");
warn_always_zero_bit(ctrl, TYPE2_TIMING, "timing");
warn_always_zero_bit(ctrl, TYPE2_SELECT, "select");
warn_always_zero_bit(ctrl, TYPE2_HIT, "hit");
/* validate size encoding */
size = type2_breakpoint_size(env, ctrl);
if (access_size[size] == -1) {
qemu_log_mask(LOG_UNIMP, "access size %d is not supported, using SIZE_ANY\n",
size);
} else {
val |= (ctrl & TYPE2_SIZELO);
if (riscv_cpu_mxl(env) == MXL_RV64) {
val |= (ctrl & TYPE2_SIZEHI);
}
}
/* keep the mode and attribute bits */
val |= (ctrl & (TYPE2_U | TYPE2_S | TYPE2_M |
TYPE2_LOAD | TYPE2_STORE | TYPE2_EXEC));
return val;
}
static void type2_breakpoint_insert(CPURISCVState *env, target_ulong index)
{
target_ulong ctrl = env->type2_trig[index].mcontrol;
target_ulong addr = env->type2_trig[index].maddress;
bool enabled = type2_breakpoint_enabled(ctrl);
CPUState *cs = env_cpu(env);
int flags = BP_CPU | BP_STOP_BEFORE_ACCESS;
uint32_t size;
if (!enabled) {
return;
}
if (ctrl & TYPE2_EXEC) {
cpu_breakpoint_insert(cs, addr, flags, &env->type2_trig[index].bp);
}
if (ctrl & TYPE2_LOAD) {
flags |= BP_MEM_READ;
}
if (ctrl & TYPE2_STORE) {
flags |= BP_MEM_WRITE;
}
if (flags & BP_MEM_ACCESS) {
size = type2_breakpoint_size(env, ctrl);
if (size != 0) {
cpu_watchpoint_insert(cs, addr, size, flags,
&env->type2_trig[index].wp);
} else {
cpu_watchpoint_insert(cs, addr, 8, flags,
&env->type2_trig[index].wp);
}
}
}
static void type2_breakpoint_remove(CPURISCVState *env, target_ulong index)
{
CPUState *cs = env_cpu(env);
if (env->type2_trig[index].bp) {
cpu_breakpoint_remove_by_ref(cs, env->type2_trig[index].bp);
env->type2_trig[index].bp = NULL;
}
if (env->type2_trig[index].wp) {
cpu_watchpoint_remove_by_ref(cs, env->type2_trig[index].wp);
env->type2_trig[index].wp = NULL;
}
}
static target_ulong type2_reg_read(CPURISCVState *env,
target_ulong trigger_index, int tdata_index)
{
uint32_t index = trigger_index - TRIGGER_TYPE2_IDX_0;
target_ulong tdata;
switch (tdata_index) {
case TDATA1:
tdata = env->type2_trig[index].mcontrol;
break;
case TDATA2:
tdata = env->type2_trig[index].maddress;
break;
default:
g_assert_not_reached();
}
return tdata;
}
static void type2_reg_write(CPURISCVState *env, target_ulong trigger_index,
int tdata_index, target_ulong val)
{
uint32_t index = trigger_index - TRIGGER_TYPE2_IDX_0;
target_ulong new_val;
switch (tdata_index) {
case TDATA1:
new_val = type2_mcontrol_validate(env, val);
if (new_val != env->type2_trig[index].mcontrol) {
env->type2_trig[index].mcontrol = new_val;
type2_breakpoint_remove(env, index);
type2_breakpoint_insert(env, index);
}
break;
case TDATA2:
if (val != env->type2_trig[index].maddress) {
env->type2_trig[index].maddress = val;
type2_breakpoint_remove(env, index);
type2_breakpoint_insert(env, index);
}
break;
default:
g_assert_not_reached();
}
return;
}
typedef target_ulong (*tdata_read_func)(CPURISCVState *env,
target_ulong trigger_index,
int tdata_index);
static tdata_read_func trigger_read_funcs[TRIGGER_NUM] = {
[TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = type2_reg_read,
};
typedef void (*tdata_write_func)(CPURISCVState *env,
target_ulong trigger_index,
int tdata_index,
target_ulong val);
static tdata_write_func trigger_write_funcs[TRIGGER_NUM] = {
[TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = type2_reg_write,
};
target_ulong tdata_csr_read(CPURISCVState *env, int tdata_index)
{
tdata_read_func read_func = trigger_read_funcs[env->trigger_cur];
return read_func(env, env->trigger_cur, tdata_index);
}
void tdata_csr_write(CPURISCVState *env, int tdata_index, target_ulong val)
{
tdata_write_func write_func = trigger_write_funcs[env->trigger_cur];
return write_func(env, env->trigger_cur, tdata_index, val);
}
void riscv_cpu_debug_excp_handler(CPUState *cs)
{
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
if (cs->watchpoint_hit) {
if (cs->watchpoint_hit->flags & BP_CPU) {
cs->watchpoint_hit = NULL;
riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, 0);
}
} else {
if (cpu_breakpoint_test(cs, env->pc, BP_CPU)) {
riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, 0);
}
}
}
bool riscv_cpu_debug_check_breakpoint(CPUState *cs)
{
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
CPUBreakpoint *bp;
target_ulong ctrl;
target_ulong pc;
int i;
QTAILQ_FOREACH(bp, &cs->breakpoints, entry) {
for (i = 0; i < TRIGGER_TYPE2_NUM; i++) {
ctrl = env->type2_trig[i].mcontrol;
pc = env->type2_trig[i].maddress;
if ((ctrl & TYPE2_EXEC) && (bp->pc == pc)) {
/* check U/S/M bit against current privilege level */
if ((ctrl >> 3) & BIT(env->priv)) {
return true;
}
}
}
}
return false;
}
bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp)
{
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
target_ulong ctrl;
target_ulong addr;
int flags;
int i;
for (i = 0; i < TRIGGER_TYPE2_NUM; i++) {
ctrl = env->type2_trig[i].mcontrol;
addr = env->type2_trig[i].maddress;
flags = 0;
if (ctrl & TYPE2_LOAD) {
flags |= BP_MEM_READ;
}
if (ctrl & TYPE2_STORE) {
flags |= BP_MEM_WRITE;
}
if ((wp->flags & flags) && (wp->vaddr == addr)) {
/* check U/S/M bit against current privilege level */
if ((ctrl >> 3) & BIT(env->priv)) {
return true;
}
}
}
return false;
}
void riscv_trigger_init(CPURISCVState *env)
{
target_ulong type2 = trigger_type(env, TRIGGER_TYPE_AD_MATCH);
int i;
/* type 2 triggers */
for (i = 0; i < TRIGGER_TYPE2_NUM; i++) {
/*
* type = TRIGGER_TYPE_AD_MATCH
* dmode = 0 (both debug and M-mode can write tdata)
* maskmax = 0 (unimplemented, always 0)
* sizehi = 0 (match against any size, RV64 only)
* hit = 0 (unimplemented, always 0)
* select = 0 (always 0, perform match on address)
* timing = 0 (always 0, trigger before instruction)
* sizelo = 0 (match against any size)
* action = 0 (always 0, raise a breakpoint exception)
* chain = 0 (unimplemented, always 0)
* match = 0 (always 0, when any compare value equals tdata2)
*/
env->type2_trig[i].mcontrol = type2;
env->type2_trig[i].maddress = 0;
env->type2_trig[i].bp = NULL;
env->type2_trig[i].wp = NULL;
}
}

114
target/riscv/debug.h Normal file
View File

@ -0,0 +1,114 @@
/*
* QEMU RISC-V Native Debug Support
*
* Copyright (c) 2022 Wind River Systems, Inc.
*
* Author:
* Bin Meng <bin.meng@windriver.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2 or later, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RISCV_DEBUG_H
#define RISCV_DEBUG_H
/* trigger indexes implemented */
enum {
TRIGGER_TYPE2_IDX_0 = 0,
TRIGGER_TYPE2_IDX_1,
TRIGGER_TYPE2_NUM,
TRIGGER_NUM = TRIGGER_TYPE2_NUM
};
/* register index of tdata CSRs */
enum {
TDATA1 = 0,
TDATA2,
TDATA3,
TDATA_NUM
};
typedef enum {
TRIGGER_TYPE_NO_EXIST = 0, /* trigger does not exist */
TRIGGER_TYPE_AD_MATCH = 2, /* address/data match trigger */
TRIGGER_TYPE_INST_CNT = 3, /* instruction count trigger */
TRIGGER_TYPE_INT = 4, /* interrupt trigger */
TRIGGER_TYPE_EXCP = 5, /* exception trigger */
TRIGGER_TYPE_AD_MATCH6 = 6, /* new address/data match trigger */
TRIGGER_TYPE_EXT_SRC = 7, /* external source trigger */
TRIGGER_TYPE_UNAVAIL = 15 /* trigger exists, but unavailable */
} trigger_type_t;
typedef struct {
target_ulong mcontrol;
target_ulong maddress;
struct CPUBreakpoint *bp;
struct CPUWatchpoint *wp;
} type2_trigger_t;
/* tdata field masks */
#define RV32_TYPE(t) ((uint32_t)(t) << 28)
#define RV32_TYPE_MASK (0xf << 28)
#define RV32_DMODE BIT(27)
#define RV64_TYPE(t) ((uint64_t)(t) << 60)
#define RV64_TYPE_MASK (0xfULL << 60)
#define RV64_DMODE BIT_ULL(59)
/* mcontrol field masks */
#define TYPE2_LOAD BIT(0)
#define TYPE2_STORE BIT(1)
#define TYPE2_EXEC BIT(2)
#define TYPE2_U BIT(3)
#define TYPE2_S BIT(4)
#define TYPE2_M BIT(6)
#define TYPE2_MATCH (0xf << 7)
#define TYPE2_CHAIN BIT(11)
#define TYPE2_ACTION (0xf << 12)
#define TYPE2_SIZELO (0x3 << 16)
#define TYPE2_TIMING BIT(18)
#define TYPE2_SELECT BIT(19)
#define TYPE2_HIT BIT(20)
#define TYPE2_SIZEHI (0x3 << 21) /* RV64 only */
/* access size */
enum {
SIZE_ANY = 0,
SIZE_1B,
SIZE_2B,
SIZE_4B,
SIZE_6B,
SIZE_8B,
SIZE_10B,
SIZE_12B,
SIZE_14B,
SIZE_16B,
SIZE_NUM = 16
};
bool tdata_available(CPURISCVState *env, int tdata_index);
target_ulong tselect_csr_read(CPURISCVState *env);
void tselect_csr_write(CPURISCVState *env, target_ulong val);
target_ulong tdata_csr_read(CPURISCVState *env, int tdata_index);
void tdata_csr_write(CPURISCVState *env, int tdata_index, target_ulong val);
void riscv_cpu_debug_excp_handler(CPUState *cs);
bool riscv_cpu_debug_check_breakpoint(CPUState *cs);
bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp);
void riscv_trigger_init(CPURISCVState *env);
#endif /* RISCV_DEBUG_H */

View File

@ -1086,10 +1086,7 @@ DEF_HELPER_6(vcompress_vm_h, void, ptr, ptr, ptr, ptr, env, i32)
DEF_HELPER_6(vcompress_vm_w, void, ptr, ptr, ptr, ptr, env, i32)
DEF_HELPER_6(vcompress_vm_d, void, ptr, ptr, ptr, ptr, env, i32)
DEF_HELPER_4(vmv1r_v, void, ptr, ptr, env, i32)
DEF_HELPER_4(vmv2r_v, void, ptr, ptr, env, i32)
DEF_HELPER_4(vmv4r_v, void, ptr, ptr, env, i32)
DEF_HELPER_4(vmv8r_v, void, ptr, ptr, env, i32)
DEF_HELPER_4(vmvr_v, void, ptr, ptr, env, i32)
DEF_HELPER_5(vzext_vf2_h, void, ptr, ptr, ptr, env, i32)
DEF_HELPER_5(vzext_vf2_w, void, ptr, ptr, ptr, env, i32)

View File

@ -1198,7 +1198,7 @@ GEN_LDST_WHOLE_TRANS(vs8r_v, 8, true)
static inline uint32_t MAXSZ(DisasContext *s)
{
int scale = s->lmul - 3;
return scale < 0 ? s->cfg_ptr->vlen >> -scale : s->cfg_ptr->vlen << scale;
return s->cfg_ptr->vlen >> -scale;
}
static bool opivv_check(DisasContext *s, arg_rmrr *a)
@ -3597,8 +3597,7 @@ static bool trans_vrgather_vx(DisasContext *s, arg_rmrr *a)
if (a->vm && s->vl_eq_vlmax) {
int scale = s->lmul - (s->sew + 3);
int vlmax = scale < 0 ?
s->cfg_ptr->vlen >> -scale : s->cfg_ptr->vlen << scale;
int vlmax = s->cfg_ptr->vlen >> -scale;
TCGv_i64 dest = tcg_temp_new_i64();
if (a->rs1 == 0) {
@ -3630,8 +3629,7 @@ static bool trans_vrgather_vi(DisasContext *s, arg_rmrr *a)
if (a->vm && s->vl_eq_vlmax) {
int scale = s->lmul - (s->sew + 3);
int vlmax = scale < 0 ?
s->cfg_ptr->vlen >> -scale : s->cfg_ptr->vlen << scale;
int vlmax = s->cfg_ptr->vlen >> -scale;
if (a->rs1 >= vlmax) {
tcg_gen_gvec_dup_imm(MO_64, vreg_ofs(s, a->rd),
MAXSZ(s), MAXSZ(s), 0);
@ -3697,7 +3695,7 @@ static bool trans_vcompress_vm(DisasContext *s, arg_r *a)
* Whole Vector Register Move Instructions ignore vtype and vl setting.
* Thus, we don't need to check vill bit. (Section 16.6)
*/
#define GEN_VMV_WHOLE_TRANS(NAME, LEN, SEQ) \
#define GEN_VMV_WHOLE_TRANS(NAME, LEN) \
static bool trans_##NAME(DisasContext *s, arg_##NAME * a) \
{ \
if (require_rvv(s) && \
@ -3712,13 +3710,8 @@ static bool trans_##NAME(DisasContext *s, arg_##NAME * a) \
} else { \
TCGLabel *over = gen_new_label(); \
tcg_gen_brcondi_tl(TCG_COND_GEU, cpu_vstart, maxsz, over); \
\
static gen_helper_gvec_2_ptr * const fns[4] = { \
gen_helper_vmv1r_v, gen_helper_vmv2r_v, \
gen_helper_vmv4r_v, gen_helper_vmv8r_v, \
}; \
tcg_gen_gvec_2_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, a->rs2), \
cpu_env, maxsz, maxsz, 0, fns[SEQ]); \
cpu_env, maxsz, maxsz, 0, gen_helper_vmvr_v); \
mark_vs_dirty(s); \
gen_set_label(over); \
} \
@ -3727,10 +3720,10 @@ static bool trans_##NAME(DisasContext *s, arg_##NAME * a) \
return false; \
}
GEN_VMV_WHOLE_TRANS(vmv1r_v, 1, 0)
GEN_VMV_WHOLE_TRANS(vmv2r_v, 2, 1)
GEN_VMV_WHOLE_TRANS(vmv4r_v, 4, 2)
GEN_VMV_WHOLE_TRANS(vmv8r_v, 8, 3)
GEN_VMV_WHOLE_TRANS(vmv1r_v, 1)
GEN_VMV_WHOLE_TRANS(vmv2r_v, 2)
GEN_VMV_WHOLE_TRANS(vmv4r_v, 4)
GEN_VMV_WHOLE_TRANS(vmv8r_v, 8)
static bool int_ext_check(DisasContext *s, arg_rmr *a, uint8_t div)
{

View File

@ -216,7 +216,38 @@ static const VMStateDescription vmstate_kvmtimer = {
VMSTATE_UINT64(env.kvm_timer_time, RISCVCPU),
VMSTATE_UINT64(env.kvm_timer_compare, RISCVCPU),
VMSTATE_UINT64(env.kvm_timer_state, RISCVCPU),
VMSTATE_END_OF_LIST()
}
};
static bool debug_needed(void *opaque)
{
RISCVCPU *cpu = opaque;
CPURISCVState *env = &cpu->env;
return riscv_feature(env, RISCV_FEATURE_DEBUG);
}
static const VMStateDescription vmstate_debug_type2 = {
.name = "cpu/debug/type2",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINTTL(mcontrol, type2_trigger_t),
VMSTATE_UINTTL(maddress, type2_trigger_t),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_debug = {
.name = "cpu/debug",
.version_id = 1,
.minimum_version_id = 1,
.needed = debug_needed,
.fields = (VMStateField[]) {
VMSTATE_UINTTL(env.trigger_cur, RISCVCPU),
VMSTATE_STRUCT_ARRAY(env.type2_trig, RISCVCPU, TRIGGER_TYPE2_NUM,
0, vmstate_debug_type2, type2_trigger_t),
VMSTATE_END_OF_LIST()
}
};
@ -231,6 +262,28 @@ static int riscv_cpu_post_load(void *opaque, int version_id)
return 0;
}
static bool envcfg_needed(void *opaque)
{
RISCVCPU *cpu = opaque;
CPURISCVState *env = &cpu->env;
return (env->priv_ver >= PRIV_VERSION_1_12_0 ? 1 : 0);
}
static const VMStateDescription vmstate_envcfg = {
.name = "cpu/envcfg",
.version_id = 1,
.minimum_version_id = 1,
.needed = envcfg_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(env.menvcfg, RISCVCPU),
VMSTATE_UINTTL(env.senvcfg, RISCVCPU),
VMSTATE_UINT64(env.henvcfg, RISCVCPU),
VMSTATE_END_OF_LIST()
}
};
const VMStateDescription vmstate_riscv_cpu = {
.name = "cpu",
.version_id = 3,
@ -292,6 +345,8 @@ const VMStateDescription vmstate_riscv_cpu = {
&vmstate_pointermasking,
&vmstate_rv128,
&vmstate_kvmtimer,
&vmstate_envcfg,
&vmstate_debug,
NULL
}
};

View File

@ -27,6 +27,7 @@ riscv_softmmu_ss = ss.source_set()
riscv_softmmu_ss.add(files(
'arch_dump.c',
'pmp.c',
'debug.c',
'monitor.c',
'machine.c'
))

View File

@ -141,17 +141,9 @@ static void pmp_decode_napot(target_ulong a, target_ulong *sa, target_ulong *ea)
0111...1111 2^(XLEN+2)-byte NAPOT range
1111...1111 Reserved
*/
if (a == -1) {
*sa = 0u;
*ea = -1;
return;
} else {
target_ulong t1 = ctz64(~a);
target_ulong base = (a & ~(((target_ulong)1 << t1) - 1)) << 2;
target_ulong range = ((target_ulong)1 << (t1 + 3)) - 1;
*sa = base;
*ea = base + range;
}
a = (a << 2) | 0x3;
*sa = a & (a + 1);
*ea = a | (a + 1);
}
void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index)

View File

@ -4888,25 +4888,20 @@ GEN_VEXT_VCOMPRESS_VM(vcompress_vm_w, uint32_t, H4)
GEN_VEXT_VCOMPRESS_VM(vcompress_vm_d, uint64_t, H8)
/* Vector Whole Register Move */
#define GEN_VEXT_VMV_WHOLE(NAME, LEN) \
void HELPER(NAME)(void *vd, void *vs2, CPURISCVState *env, \
uint32_t desc) \
{ \
/* EEW = 8 */ \
uint32_t maxsz = simd_maxsz(desc); \
uint32_t i = env->vstart; \
\
memcpy((uint8_t *)vd + H1(i), \
(uint8_t *)vs2 + H1(i), \
maxsz - env->vstart); \
\
env->vstart = 0; \
}
void HELPER(vmvr_v)(void *vd, void *vs2, CPURISCVState *env, uint32_t desc)
{
/* EEW = SEW */
uint32_t maxsz = simd_maxsz(desc);
uint32_t sewb = 1 << FIELD_EX64(env->vtype, VTYPE, VSEW);
uint32_t startb = env->vstart * sewb;
uint32_t i = startb;
GEN_VEXT_VMV_WHOLE(vmv1r_v, 1)
GEN_VEXT_VMV_WHOLE(vmv2r_v, 2)
GEN_VEXT_VMV_WHOLE(vmv4r_v, 4)
GEN_VEXT_VMV_WHOLE(vmv8r_v, 8)
memcpy((uint8_t *)vd + H1(i),
(uint8_t *)vs2 + H1(i),
maxsz - startb);
env->vstart = 0;
}
/* Vector Integer Extension */
#define GEN_VEXT_INT_EXT(NAME, ETYPE, DTYPE, HD, HS1) \