summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Henderson2022-04-22 07:03:34 +0200
committerRichard Henderson2022-04-22 07:03:34 +0200
commit10cd282ee44e4bc4a4b9751bccfcc597b4e7f830 (patch)
tree2f644d3da0c593aee6eb7b73272920e02e87e967
parentMerge tag 'pull-migration-20220421a' of https://gitlab.com/dagrh/qemu into st... (diff)
parenthw/riscv: boot: Support 64bit fdt address. (diff)
downloadqemu-10cd282ee44e4bc4a4b9751bccfcc597b4e7f830.tar.gz
qemu-10cd282ee44e4bc4a4b9751bccfcc597b4e7f830.tar.xz
qemu-10cd282ee44e4bc4a4b9751bccfcc597b4e7f830.zip
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>
-rw-r--r--hw/intc/riscv_aclint.c144
-rw-r--r--hw/riscv/boot.c12
-rw-r--r--hw/riscv/opentitan.c36
-rw-r--r--hw/riscv/virt.c24
-rw-r--r--hw/ssi/ibex_spi_host.c612
-rw-r--r--hw/ssi/meson.build1
-rw-r--r--hw/ssi/trace-events7
-rw-r--r--include/hw/core/tcg-cpu-ops.h1
-rw-r--r--include/hw/intc/riscv_aclint.h1
-rw-r--r--include/hw/riscv/boot.h4
-rw-r--r--include/hw/riscv/opentitan.h30
-rw-r--r--include/hw/ssi/ibex_spi_host.h94
-rw-r--r--target/riscv/cpu.c120
-rw-r--r--target/riscv/cpu.h40
-rw-r--r--target/riscv/cpu_bits.h40
-rw-r--r--target/riscv/cpu_helper.c10
-rw-r--r--target/riscv/csr.c282
-rw-r--r--target/riscv/debug.c441
-rw-r--r--target/riscv/debug.h114
-rw-r--r--target/riscv/helper.h5
-rw-r--r--target/riscv/insn_trans/trans_rvv.c.inc25
-rw-r--r--target/riscv/machine.c55
-rw-r--r--target/riscv/meson.build1
-rw-r--r--target/riscv/pmp.c14
-rw-r--r--target/riscv/vector_helper.c31
25 files changed, 1971 insertions, 173 deletions
diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c
index e43b050e92..0412edc982 100644
--- a/hw/intc/riscv_aclint.c
+++ b/hw/intc/riscv_aclint.c
@@ -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 = {
diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c
index 0f179d3601..57a41df8e9 100644
--- a/hw/riscv/boot.c
+++ b/hw/riscv/boot.c
@@ -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)) {
diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c
index 833624d66c..2d401dcb23 100644
--- a/hw/riscv/opentitan.c
+++ b/hw/riscv/opentitan.c
@@ -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",
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index da50cbed43..b49c5361bd 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -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])) {
diff --git a/hw/ssi/ibex_spi_host.c b/hw/ssi/ibex_spi_host.c
new file mode 100644
index 0000000000..d14580b409
--- /dev/null
+++ b/hw/ssi/ibex_spi_host.c
@@ -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)
diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build
index 0ded9cd092..702aa5e4df 100644
--- a/hw/ssi/meson.build
+++ b/hw/ssi/meson.build
@@ -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'))
diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events
index 612d3d6087..c707d4aaba 100644
--- a/hw/ssi/trace-events
+++ b/hw/ssi/trace-events
@@ -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:"
diff --git a/include/hw/core/tcg-cpu-ops.h b/include/hw/core/tcg-cpu-ops.h
index fbe6b76764..78c6c6635d 100644
--- a/include/hw/core/tcg-cpu-ops.h
+++ b/include/hw/core/tcg-cpu-ops.h
@@ -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);
diff --git a/include/hw/intc/riscv_aclint.h b/include/hw/intc/riscv_aclint.h
index 229bd08d25..26d4048687 100644
--- a/include/hw/intc/riscv_aclint.h
+++ b/include/hw/intc/riscv_aclint.h
@@ -31,6 +31,7 @@
typedef struct RISCVAclintMTimerState {
/*< private >*/
SysBusDevice parent_obj;
+ uint64_t time_delta;
/*< public >*/
MemoryRegion mmio;
diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h
index d937c5c224..d2db29721a 100644
--- a/include/hw/riscv/boot.h
+++ b/include/hw/riscv/boot.h
@@ -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,
diff --git a/include/hw/riscv/opentitan.h b/include/hw/riscv/opentitan.h
index 00da9ded43..68892cd8e5 100644
--- a/include/hw/riscv/opentitan.h
+++ b/include/hw/riscv/opentitan.h
@@ -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
diff --git a/include/hw/ssi/ibex_spi_host.h b/include/hw/ssi/ibex_spi_host.h
new file mode 100644
index 0000000000..3fedcb6805
--- /dev/null
+++ b/include/hw/ssi/ibex_spi_host.h
@@ -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
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index ddda4906ff..0c774056c5 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -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;
}
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 72f1c9451e..34c22d5d3b 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -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 */
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 0fe01d7da5..4a9e4f7d09 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -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
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 1c60fb2e80..e1aa4f2097 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -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;
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 341c2e6f23..6ba85e7b5d 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -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_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_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2 },
- [CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst },
+ [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,
+ .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,
+ .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 },
diff --git a/target/riscv/debug.c b/target/riscv/debug.c
new file mode 100644
index 0000000000..2f2a51c732
--- /dev/null
+++ b/target/riscv/debug.c
@@ -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;
+ }
+}
diff --git a/target/riscv/debug.h b/target/riscv/debug.h
new file mode 100644
index 0000000000..27b9cac6b4
--- /dev/null
+++ b/target/riscv/debug.h
@@ -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 */
diff --git a/target/riscv/helper.h b/target/riscv/helper.h
index 26bbab2fab..a669d0187b 100644
--- a/target/riscv/helper.h
+++ b/target/riscv/helper.h
@@ -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)
diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc
index 8d675db9a2..90327509f7 100644
--- a/target/riscv/insn_trans/trans_rvv.c.inc
+++ b/target/riscv/insn_trans/trans_rvv.c.inc
@@ -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)
{
diff --git a/target/riscv/machine.c b/target/riscv/machine.c
index 5178b3fec9..2a437b29a1 100644
--- a/target/riscv/machine.c
+++ b/target/riscv/machine.c
@@ -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
}
};
diff --git a/target/riscv/meson.build b/target/riscv/meson.build
index 91f0ac32ff..2c20f3dd8e 100644
--- a/target/riscv/meson.build
+++ b/target/riscv/meson.build
@@ -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'
))
diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
index 81b61bb65c..151da3fa08 100644
--- a/target/riscv/pmp.c
+++ b/target/riscv/pmp.c
@@ -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)
diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c
index 7a6ce0a3bc..576b14e5a3 100644
--- a/target/riscv/vector_helper.c
+++ b/target/riscv/vector_helper.c
@@ -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;
+
+ memcpy((uint8_t *)vd + H1(i),
+ (uint8_t *)vs2 + H1(i),
+ maxsz - 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)
+ env->vstart = 0;
+}
/* Vector Integer Extension */
#define GEN_VEXT_INT_EXT(NAME, ETYPE, DTYPE, HD, HS1) \