First RISC-V PR for QEMU 7.1
* Add support for Ibex SPI to OpenTitan * Add support for privileged spec version 1.12.0 * Use privileged spec version 1.12.0 for virt machine by default * Allow software access to MIP SEIP * Add initial support for the Sdtrig extension * Optimisations for vector extensions * Improvements to the misa ISA string * Add isa extenstion strings to the device tree * Don't allow `-bios` options with KVM machines * Fix NAPOT range computation overflow * Fix DT property mmu-type when CPU mmu option is disabled * Make RISC-V ACLINT mtime MMIO register writable * Add and enable native debug feature * Support 64bit fdt address. -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEE9sSsRtSTSGjTuM6PIeENKd+XcFQFAmJh+GQACgkQIeENKd+X cFTKZQf/UQ8yb5DozdeNbm2pmfjJnEEsnXB6k95wIX9pjrJ3HkypHzoRpLbIDzET KsPjRW6N5SLPINrYfgBuxUv0A/6jOG7cTC/Bimu16wPyS2zQopiTTgiJv6qLkO5G QUBWz/6kaXNT+fQiTnXXqjViADO49FigYRWUmRfNabeUwb6YoQwoBY6B5jpwZlbI B9qDdcKnYet5zwi1rGFedRC1XtP7ZDF1lylqNS2nnfr1ZvOWYkAJb5TJDi/4qUpz i/wGRx/8KaYD5ehGe7Xd50sMM9lLlzNgOnZL0F5cRnA8e/3nRFjTeQ7RoSKGBdaS 7J4RqA9YMhuPL2tTq95wof6EpVsSNw== =yLIg -----END PGP SIGNATURE----- Merge tag 'pull-riscv-to-apply-20220422-1' of github.com:alistair23/qemu into staging First RISC-V PR for QEMU 7.1 * Add support for Ibex SPI to OpenTitan * Add support for privileged spec version 1.12.0 * Use privileged spec version 1.12.0 for virt machine by default * Allow software access to MIP SEIP * Add initial support for the Sdtrig extension * Optimisations for vector extensions * Improvements to the misa ISA string * Add isa extenstion strings to the device tree * Don't allow `-bios` options with KVM machines * Fix NAPOT range computation overflow * Fix DT property mmu-type when CPU mmu option is disabled * Make RISC-V ACLINT mtime MMIO register writable * Add and enable native debug feature * Support 64bit fdt address. # -----BEGIN PGP SIGNATURE----- # # iQEzBAABCAAdFiEE9sSsRtSTSGjTuM6PIeENKd+XcFQFAmJh+GQACgkQIeENKd+X # cFTKZQf/UQ8yb5DozdeNbm2pmfjJnEEsnXB6k95wIX9pjrJ3HkypHzoRpLbIDzET # KsPjRW6N5SLPINrYfgBuxUv0A/6jOG7cTC/Bimu16wPyS2zQopiTTgiJv6qLkO5G # QUBWz/6kaXNT+fQiTnXXqjViADO49FigYRWUmRfNabeUwb6YoQwoBY6B5jpwZlbI # B9qDdcKnYet5zwi1rGFedRC1XtP7ZDF1lylqNS2nnfr1ZvOWYkAJb5TJDi/4qUpz # i/wGRx/8KaYD5ehGe7Xd50sMM9lLlzNgOnZL0F5cRnA8e/3nRFjTeQ7RoSKGBdaS # 7J4RqA9YMhuPL2tTq95wof6EpVsSNw== # =yLIg # -----END PGP SIGNATURE----- # gpg: Signature made Thu 21 Apr 2022 05:35:48 PM PDT # gpg: using RSA key F6C4AC46D4934868D3B8CE8F21E10D29DF977054 # gpg: Good signature from "Alistair Francis <alistair@alistair23.me>" [undefined] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: F6C4 AC46 D493 4868 D3B8 CE8F 21E1 0D29 DF97 7054 * tag 'pull-riscv-to-apply-20220422-1' of github.com:alistair23/qemu: (31 commits) hw/riscv: boot: Support 64bit fdt address. hw/core: tcg-cpu-ops.h: Update comments of debug_check_watchpoint() target/riscv: cpu: Enable native debug feature target/riscv: machine: Add debug state description target/riscv: csr: Hook debug CSR read/write target/riscv: cpu: Add a config option for native debug target/riscv: debug: Implement debug related TCGCPUOps hw/intc: riscv_aclint: Add reset function of ACLINT devices hw/intc: Make RISC-V ACLINT mtime MMIO register writable hw/intc: Support 32/64-bit mtimecmp and mtime accesses in RISC-V ACLINT hw/intc: Add .impl.[min|max]_access_size declaration in RISC-V ACLINT hw/riscv: virt: fix DT property mmu-type when CPU mmu option is disabled target/riscv/pmp: fix NAPOT range computation overflow hw/riscv: virt: Exit if the user provided -bios in combination with KVM target/riscv: Use cpu_loop_exit_restore directly from mmu faults target/riscv: fix start byte for vmv<nf>r.v when vstart != 0 target/riscv: Add isa extenstion strings to the device tree target/riscv: misa to ISA string conversion fix target/riscv: optimize helper for vmv<nr>r.v target/riscv: optimize condition assign for scale < 0 ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
commit
10cd282ee4
@ -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 = {
|
||||
|
@ -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)) {
|
||||
|
@ -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",
|
||||
|
@ -230,8 +230,14 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket,
|
||||
cpu_name = g_strdup_printf("/cpus/cpu@%d",
|
||||
s->soc[socket].hartid_base + cpu);
|
||||
qemu_fdt_add_subnode(mc->fdt, cpu_name);
|
||||
qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type",
|
||||
(is_32_bit) ? "riscv,sv32" : "riscv,sv48");
|
||||
if (riscv_feature(&s->soc[socket].harts[cpu].env,
|
||||
RISCV_FEATURE_MMU)) {
|
||||
qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type",
|
||||
(is_32_bit) ? "riscv,sv32" : "riscv,sv48");
|
||||
} else {
|
||||
qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type",
|
||||
"riscv,none");
|
||||
}
|
||||
name = riscv_isa_string(&s->soc[socket].harts[cpu]);
|
||||
qemu_fdt_setprop_string(mc->fdt, cpu_name, "riscv,isa", name);
|
||||
g_free(name);
|
||||
@ -1308,12 +1314,18 @@ static void virt_machine_init(MachineState *machine)
|
||||
|
||||
/*
|
||||
* Only direct boot kernel is currently supported for KVM VM,
|
||||
* so the "-bios" parameter is ignored and treated like "-bios none"
|
||||
* when KVM is enabled.
|
||||
* so the "-bios" parameter is not supported when KVM is enabled.
|
||||
*/
|
||||
if (kvm_enabled()) {
|
||||
g_free(machine->firmware);
|
||||
machine->firmware = g_strdup("none");
|
||||
if (machine->firmware) {
|
||||
if (strcmp(machine->firmware, "none")) {
|
||||
error_report("Machine mode firmware is not supported in "
|
||||
"combination with KVM.");
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
machine->firmware = g_strdup("none");
|
||||
}
|
||||
}
|
||||
|
||||
if (riscv_is_32bit(&s->soc[0])) {
|
||||
|
612
hw/ssi/ibex_spi_host.c
Normal file
612
hw/ssi/ibex_spi_host.c
Normal file
@ -0,0 +1,612 @@
|
||||
/*
|
||||
* QEMU model of the Ibex SPI Controller
|
||||
* SPEC Reference: https://docs.opentitan.org/hw/ip/spi_host/doc/
|
||||
*
|
||||
* Copyright (C) 2022 Western Digital
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/ssi/ibex_spi_host.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-properties-system.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "trace.h"
|
||||
|
||||
REG32(INTR_STATE, 0x00)
|
||||
FIELD(INTR_STATE, ERROR, 0, 1)
|
||||
FIELD(INTR_STATE, SPI_EVENT, 1, 1)
|
||||
REG32(INTR_ENABLE, 0x04)
|
||||
FIELD(INTR_ENABLE, ERROR, 0, 1)
|
||||
FIELD(INTR_ENABLE, SPI_EVENT, 1, 1)
|
||||
REG32(INTR_TEST, 0x08)
|
||||
FIELD(INTR_TEST, ERROR, 0, 1)
|
||||
FIELD(INTR_TEST, SPI_EVENT, 1, 1)
|
||||
REG32(ALERT_TEST, 0x0c)
|
||||
FIELD(ALERT_TEST, FETAL_TEST, 0, 1)
|
||||
REG32(CONTROL, 0x10)
|
||||
FIELD(CONTROL, RX_WATERMARK, 0, 8)
|
||||
FIELD(CONTROL, TX_WATERMARK, 1, 8)
|
||||
FIELD(CONTROL, OUTPUT_EN, 29, 1)
|
||||
FIELD(CONTROL, SW_RST, 30, 1)
|
||||
FIELD(CONTROL, SPIEN, 31, 1)
|
||||
REG32(STATUS, 0x14)
|
||||
FIELD(STATUS, TXQD, 0, 8)
|
||||
FIELD(STATUS, RXQD, 18, 8)
|
||||
FIELD(STATUS, CMDQD, 16, 3)
|
||||
FIELD(STATUS, RXWM, 20, 1)
|
||||
FIELD(STATUS, BYTEORDER, 22, 1)
|
||||
FIELD(STATUS, RXSTALL, 23, 1)
|
||||
FIELD(STATUS, RXEMPTY, 24, 1)
|
||||
FIELD(STATUS, RXFULL, 25, 1)
|
||||
FIELD(STATUS, TXWM, 26, 1)
|
||||
FIELD(STATUS, TXSTALL, 27, 1)
|
||||
FIELD(STATUS, TXEMPTY, 28, 1)
|
||||
FIELD(STATUS, TXFULL, 29, 1)
|
||||
FIELD(STATUS, ACTIVE, 30, 1)
|
||||
FIELD(STATUS, READY, 31, 1)
|
||||
REG32(CONFIGOPTS, 0x18)
|
||||
FIELD(CONFIGOPTS, CLKDIV_0, 0, 16)
|
||||
FIELD(CONFIGOPTS, CSNIDLE_0, 16, 4)
|
||||
FIELD(CONFIGOPTS, CSNTRAIL_0, 20, 4)
|
||||
FIELD(CONFIGOPTS, CSNLEAD_0, 24, 4)
|
||||
FIELD(CONFIGOPTS, FULLCYC_0, 29, 1)
|
||||
FIELD(CONFIGOPTS, CPHA_0, 30, 1)
|
||||
FIELD(CONFIGOPTS, CPOL_0, 31, 1)
|
||||
REG32(CSID, 0x1c)
|
||||
FIELD(CSID, CSID, 0, 32)
|
||||
REG32(COMMAND, 0x20)
|
||||
FIELD(COMMAND, LEN, 0, 8)
|
||||
FIELD(COMMAND, CSAAT, 9, 1)
|
||||
FIELD(COMMAND, SPEED, 10, 2)
|
||||
FIELD(COMMAND, DIRECTION, 12, 2)
|
||||
REG32(ERROR_ENABLE, 0x2c)
|
||||
FIELD(ERROR_ENABLE, CMDBUSY, 0, 1)
|
||||
FIELD(ERROR_ENABLE, OVERFLOW, 1, 1)
|
||||
FIELD(ERROR_ENABLE, UNDERFLOW, 2, 1)
|
||||
FIELD(ERROR_ENABLE, CMDINVAL, 3, 1)
|
||||
FIELD(ERROR_ENABLE, CSIDINVAL, 4, 1)
|
||||
REG32(ERROR_STATUS, 0x30)
|
||||
FIELD(ERROR_STATUS, CMDBUSY, 0, 1)
|
||||
FIELD(ERROR_STATUS, OVERFLOW, 1, 1)
|
||||
FIELD(ERROR_STATUS, UNDERFLOW, 2, 1)
|
||||
FIELD(ERROR_STATUS, CMDINVAL, 3, 1)
|
||||
FIELD(ERROR_STATUS, CSIDINVAL, 4, 1)
|
||||
FIELD(ERROR_STATUS, ACCESSINVAL, 5, 1)
|
||||
REG32(EVENT_ENABLE, 0x30)
|
||||
FIELD(EVENT_ENABLE, RXFULL, 0, 1)
|
||||
FIELD(EVENT_ENABLE, TXEMPTY, 1, 1)
|
||||
FIELD(EVENT_ENABLE, RXWM, 2, 1)
|
||||
FIELD(EVENT_ENABLE, TXWM, 3, 1)
|
||||
FIELD(EVENT_ENABLE, READY, 4, 1)
|
||||
FIELD(EVENT_ENABLE, IDLE, 5, 1)
|
||||
|
||||
static inline uint8_t div4_round_up(uint8_t dividend)
|
||||
{
|
||||
return (dividend + 3) / 4;
|
||||
}
|
||||
|
||||
static void ibex_spi_rxfifo_reset(IbexSPIHostState *s)
|
||||
{
|
||||
/* Empty the RX FIFO and assert RXEMPTY */
|
||||
fifo8_reset(&s->rx_fifo);
|
||||
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXFULL_MASK;
|
||||
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXEMPTY_MASK;
|
||||
}
|
||||
|
||||
static void ibex_spi_txfifo_reset(IbexSPIHostState *s)
|
||||
{
|
||||
/* Empty the TX FIFO and assert TXEMPTY */
|
||||
fifo8_reset(&s->tx_fifo);
|
||||
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXFULL_MASK;
|
||||
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_TXEMPTY_MASK;
|
||||
}
|
||||
|
||||
static void ibex_spi_host_reset(DeviceState *dev)
|
||||
{
|
||||
IbexSPIHostState *s = IBEX_SPI_HOST(dev);
|
||||
trace_ibex_spi_host_reset("Resetting Ibex SPI");
|
||||
|
||||
/* SPI Host Register Reset */
|
||||
s->regs[IBEX_SPI_HOST_INTR_STATE] = 0x00;
|
||||
s->regs[IBEX_SPI_HOST_INTR_ENABLE] = 0x00;
|
||||
s->regs[IBEX_SPI_HOST_INTR_TEST] = 0x00;
|
||||
s->regs[IBEX_SPI_HOST_ALERT_TEST] = 0x00;
|
||||
s->regs[IBEX_SPI_HOST_CONTROL] = 0x7f;
|
||||
s->regs[IBEX_SPI_HOST_STATUS] = 0x00;
|
||||
s->regs[IBEX_SPI_HOST_CONFIGOPTS] = 0x00;
|
||||
s->regs[IBEX_SPI_HOST_CSID] = 0x00;
|
||||
s->regs[IBEX_SPI_HOST_COMMAND] = 0x00;
|
||||
/* RX/TX Modelled by FIFO */
|
||||
s->regs[IBEX_SPI_HOST_RXDATA] = 0x00;
|
||||
s->regs[IBEX_SPI_HOST_TXDATA] = 0x00;
|
||||
|
||||
s->regs[IBEX_SPI_HOST_ERROR_ENABLE] = 0x1F;
|
||||
s->regs[IBEX_SPI_HOST_ERROR_STATUS] = 0x00;
|
||||
s->regs[IBEX_SPI_HOST_EVENT_ENABLE] = 0x00;
|
||||
|
||||
ibex_spi_rxfifo_reset(s);
|
||||
ibex_spi_txfifo_reset(s);
|
||||
|
||||
s->init_status = true;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if we need to trigger an interrupt.
|
||||
* The two interrupts lines (host_err and event) can
|
||||
* be enabled separately in 'IBEX_SPI_HOST_INTR_ENABLE'.
|
||||
*
|
||||
* Interrupts are triggered based on the ones
|
||||
* enabled in the `IBEX_SPI_HOST_EVENT_ENABLE` and `IBEX_SPI_HOST_ERROR_ENABLE`.
|
||||
*/
|
||||
static void ibex_spi_host_irq(IbexSPIHostState *s)
|
||||
{
|
||||
bool error_en = s->regs[IBEX_SPI_HOST_INTR_ENABLE]
|
||||
& R_INTR_ENABLE_ERROR_MASK;
|
||||
bool event_en = s->regs[IBEX_SPI_HOST_INTR_ENABLE]
|
||||
& R_INTR_ENABLE_SPI_EVENT_MASK;
|
||||
bool err_pending = s->regs[IBEX_SPI_HOST_INTR_STATE]
|
||||
& R_INTR_STATE_ERROR_MASK;
|
||||
bool status_pending = s->regs[IBEX_SPI_HOST_INTR_STATE]
|
||||
& R_INTR_STATE_SPI_EVENT_MASK;
|
||||
int err_irq = 0, event_irq = 0;
|
||||
|
||||
/* Error IRQ enabled and Error IRQ Cleared*/
|
||||
if (error_en && !err_pending) {
|
||||
/* Event enabled, Interrupt Test Error */
|
||||
if (s->regs[IBEX_SPI_HOST_INTR_TEST] & R_INTR_TEST_ERROR_MASK) {
|
||||
err_irq = 1;
|
||||
} else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE]
|
||||
& R_ERROR_ENABLE_CMDBUSY_MASK) &&
|
||||
s->regs[IBEX_SPI_HOST_ERROR_STATUS]
|
||||
& R_ERROR_STATUS_CMDBUSY_MASK) {
|
||||
/* Wrote to COMMAND when not READY */
|
||||
err_irq = 1;
|
||||
} else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE]
|
||||
& R_ERROR_ENABLE_CMDINVAL_MASK) &&
|
||||
s->regs[IBEX_SPI_HOST_ERROR_STATUS]
|
||||
& R_ERROR_STATUS_CMDINVAL_MASK) {
|
||||
/* Invalid command segment */
|
||||
err_irq = 1;
|
||||
} else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE]
|
||||
& R_ERROR_ENABLE_CSIDINVAL_MASK) &&
|
||||
s->regs[IBEX_SPI_HOST_ERROR_STATUS]
|
||||
& R_ERROR_STATUS_CSIDINVAL_MASK) {
|
||||
/* Invalid value for CSID */
|
||||
err_irq = 1;
|
||||
}
|
||||
if (err_irq) {
|
||||
s->regs[IBEX_SPI_HOST_INTR_STATE] |= R_INTR_STATE_ERROR_MASK;
|
||||
}
|
||||
qemu_set_irq(s->host_err, err_irq);
|
||||
}
|
||||
|
||||
/* Event IRQ Enabled and Event IRQ Cleared */
|
||||
if (event_en && !status_pending) {
|
||||
if (s->regs[IBEX_SPI_HOST_INTR_TEST] & R_INTR_TEST_SPI_EVENT_MASK) {
|
||||
/* Event enabled, Interrupt Test Event */
|
||||
event_irq = 1;
|
||||
} else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE]
|
||||
& R_EVENT_ENABLE_READY_MASK) &&
|
||||
(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_READY_MASK)) {
|
||||
/* SPI Host ready for next command */
|
||||
event_irq = 1;
|
||||
} else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE]
|
||||
& R_EVENT_ENABLE_TXEMPTY_MASK) &&
|
||||
(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_TXEMPTY_MASK)) {
|
||||
/* SPI TXEMPTY, TXFIFO drained */
|
||||
event_irq = 1;
|
||||
} else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE]
|
||||
& R_EVENT_ENABLE_RXFULL_MASK) &&
|
||||
(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_RXFULL_MASK)) {
|
||||
/* SPI RXFULL, RXFIFO full */
|
||||
event_irq = 1;
|
||||
}
|
||||
if (event_irq) {
|
||||
s->regs[IBEX_SPI_HOST_INTR_STATE] |= R_INTR_STATE_SPI_EVENT_MASK;
|
||||
}
|
||||
qemu_set_irq(s->event, event_irq);
|
||||
}
|
||||
}
|
||||
|
||||
static void ibex_spi_host_transfer(IbexSPIHostState *s)
|
||||
{
|
||||
uint32_t rx, tx;
|
||||
/* Get num of one byte transfers */
|
||||
uint8_t segment_len = ((s->regs[IBEX_SPI_HOST_COMMAND] & R_COMMAND_LEN_MASK)
|
||||
>> R_COMMAND_LEN_SHIFT);
|
||||
while (segment_len > 0) {
|
||||
if (fifo8_is_empty(&s->tx_fifo)) {
|
||||
/* Assert Stall */
|
||||
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_TXSTALL_MASK;
|
||||
break;
|
||||
} else if (fifo8_is_full(&s->rx_fifo)) {
|
||||
/* Assert Stall */
|
||||
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXSTALL_MASK;
|
||||
break;
|
||||
} else {
|
||||
tx = fifo8_pop(&s->tx_fifo);
|
||||
}
|
||||
|
||||
rx = ssi_transfer(s->ssi, tx);
|
||||
|
||||
trace_ibex_spi_host_transfer(tx, rx);
|
||||
|
||||
if (!fifo8_is_full(&s->rx_fifo)) {
|
||||
fifo8_push(&s->rx_fifo, rx);
|
||||
} else {
|
||||
/* Assert RXFULL */
|
||||
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXFULL_MASK;
|
||||
}
|
||||
--segment_len;
|
||||
}
|
||||
|
||||
/* Assert Ready */
|
||||
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_READY_MASK;
|
||||
/* Set RXQD */
|
||||
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXQD_MASK;
|
||||
s->regs[IBEX_SPI_HOST_STATUS] |= (R_STATUS_RXQD_MASK
|
||||
& div4_round_up(segment_len));
|
||||
/* Set TXQD */
|
||||
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXQD_MASK;
|
||||
s->regs[IBEX_SPI_HOST_STATUS] |= (fifo8_num_used(&s->tx_fifo) / 4)
|
||||
& R_STATUS_TXQD_MASK;
|
||||
/* Clear TXFULL */
|
||||
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXFULL_MASK;
|
||||
/* Assert TXEMPTY and drop remaining bytes that exceed segment_len */
|
||||
ibex_spi_txfifo_reset(s);
|
||||
/* Reset RXEMPTY */
|
||||
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXEMPTY_MASK;
|
||||
|
||||
ibex_spi_host_irq(s);
|
||||
}
|
||||
|
||||
static uint64_t ibex_spi_host_read(void *opaque, hwaddr addr,
|
||||
unsigned int size)
|
||||
{
|
||||
IbexSPIHostState *s = opaque;
|
||||
uint32_t rc = 0;
|
||||
uint8_t rx_byte = 0;
|
||||
|
||||
trace_ibex_spi_host_read(addr, size);
|
||||
|
||||
/* Match reg index */
|
||||
addr = addr >> 2;
|
||||
switch (addr) {
|
||||
/* Skipping any W/O registers */
|
||||
case IBEX_SPI_HOST_INTR_STATE...IBEX_SPI_HOST_INTR_ENABLE:
|
||||
case IBEX_SPI_HOST_CONTROL...IBEX_SPI_HOST_STATUS:
|
||||
rc = s->regs[addr];
|
||||
break;
|
||||
case IBEX_SPI_HOST_CSID:
|
||||
rc = s->regs[addr];
|
||||
break;
|
||||
case IBEX_SPI_HOST_CONFIGOPTS:
|
||||
rc = s->config_opts[s->regs[IBEX_SPI_HOST_CSID]];
|
||||
break;
|
||||
case IBEX_SPI_HOST_TXDATA:
|
||||
rc = s->regs[addr];
|
||||
break;
|
||||
case IBEX_SPI_HOST_RXDATA:
|
||||
/* Clear RXFULL */
|
||||
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXFULL_MASK;
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (fifo8_is_empty(&s->rx_fifo)) {
|
||||
/* Assert RXEMPTY, no IRQ */
|
||||
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXEMPTY_MASK;
|
||||
s->regs[IBEX_SPI_HOST_ERROR_STATUS] |=
|
||||
R_ERROR_STATUS_UNDERFLOW_MASK;
|
||||
return rc;
|
||||
}
|
||||
rx_byte = fifo8_pop(&s->rx_fifo);
|
||||
rc |= rx_byte << (i * 8);
|
||||
}
|
||||
break;
|
||||
case IBEX_SPI_HOST_ERROR_ENABLE...IBEX_SPI_HOST_EVENT_ENABLE:
|
||||
rc = s->regs[addr];
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "Bad offset 0x%" HWADDR_PRIx "\n",
|
||||
addr << 2);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static void ibex_spi_host_write(void *opaque, hwaddr addr,
|
||||
uint64_t val64, unsigned int size)
|
||||
{
|
||||
IbexSPIHostState *s = opaque;
|
||||
uint32_t val32 = val64;
|
||||
uint32_t shift_mask = 0xff;
|
||||
uint8_t txqd_len;
|
||||
|
||||
trace_ibex_spi_host_write(addr, size, val64);
|
||||
|
||||
/* Match reg index */
|
||||
addr = addr >> 2;
|
||||
|
||||
switch (addr) {
|
||||
/* Skipping any R/O registers */
|
||||
case IBEX_SPI_HOST_INTR_STATE...IBEX_SPI_HOST_INTR_ENABLE:
|
||||
s->regs[addr] = val32;
|
||||
break;
|
||||
case IBEX_SPI_HOST_INTR_TEST:
|
||||
s->regs[addr] = val32;
|
||||
ibex_spi_host_irq(s);
|
||||
break;
|
||||
case IBEX_SPI_HOST_ALERT_TEST:
|
||||
s->regs[addr] = val32;
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: SPI_ALERT_TEST is not supported\n", __func__);
|
||||
break;
|
||||
case IBEX_SPI_HOST_CONTROL:
|
||||
s->regs[addr] = val32;
|
||||
|
||||
if (val32 & R_CONTROL_SW_RST_MASK) {
|
||||
ibex_spi_host_reset((DeviceState *)s);
|
||||
/* Clear active if any */
|
||||
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_ACTIVE_MASK;
|
||||
}
|
||||
|
||||
if (val32 & R_CONTROL_OUTPUT_EN_MASK) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: CONTROL_OUTPUT_EN is not supported\n", __func__);
|
||||
}
|
||||
break;
|
||||
case IBEX_SPI_HOST_CONFIGOPTS:
|
||||
/* Update the respective config-opts register based on CSIDth index */
|
||||
s->config_opts[s->regs[IBEX_SPI_HOST_CSID]] = val32;
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: CONFIGOPTS Hardware settings not supported\n",
|
||||
__func__);
|
||||
break;
|
||||
case IBEX_SPI_HOST_CSID:
|
||||
if (val32 >= s->num_cs) {
|
||||
/* CSID exceeds max num_cs */
|
||||
s->regs[IBEX_SPI_HOST_ERROR_STATUS] |=
|
||||
R_ERROR_STATUS_CSIDINVAL_MASK;
|
||||
ibex_spi_host_irq(s);
|
||||
return;
|
||||
}
|
||||
s->regs[addr] = val32;
|
||||
break;
|
||||
case IBEX_SPI_HOST_COMMAND:
|
||||
s->regs[addr] = val32;
|
||||
|
||||
/* STALL, IP not enabled */
|
||||
if (!(s->regs[IBEX_SPI_HOST_CONTROL] & R_CONTROL_SPIEN_MASK)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* SPI not ready, IRQ Error */
|
||||
if (!(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_READY_MASK)) {
|
||||
s->regs[IBEX_SPI_HOST_ERROR_STATUS] |= R_ERROR_STATUS_CMDBUSY_MASK;
|
||||
ibex_spi_host_irq(s);
|
||||
return;
|
||||
}
|
||||
/* Assert Not Ready */
|
||||
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_READY_MASK;
|
||||
|
||||
if (((val32 & R_COMMAND_DIRECTION_MASK) >> R_COMMAND_DIRECTION_SHIFT)
|
||||
!= BIDIRECTIONAL_TRANSFER) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: Rx Only/Tx Only are not supported\n", __func__);
|
||||
}
|
||||
|
||||
if (val32 & R_COMMAND_CSAAT_MASK) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: CSAAT is not supported\n", __func__);
|
||||
}
|
||||
if (val32 & R_COMMAND_SPEED_MASK) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: SPEED is not supported\n", __func__);
|
||||
}
|
||||
|
||||
/* Set Transfer Callback */
|
||||
timer_mod(s->fifo_trigger_handle,
|
||||
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
|
||||
(TX_INTERRUPT_TRIGGER_DELAY_NS));
|
||||
|
||||
break;
|
||||
case IBEX_SPI_HOST_TXDATA:
|
||||
/*
|
||||
* This is a hardware `feature` where
|
||||
* the first word written TXDATA after init is omitted entirely
|
||||
*/
|
||||
if (s->init_status) {
|
||||
s->init_status = false;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
/* Attempting to write when TXFULL */
|
||||
if (fifo8_is_full(&s->tx_fifo)) {
|
||||
/* Assert RXEMPTY, no IRQ */
|
||||
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_TXFULL_MASK;
|
||||
s->regs[IBEX_SPI_HOST_ERROR_STATUS] |=
|
||||
R_ERROR_STATUS_OVERFLOW_MASK;
|
||||
ibex_spi_host_irq(s);
|
||||
return;
|
||||
}
|
||||
/* Byte ordering is set by the IP */
|
||||
if ((s->regs[IBEX_SPI_HOST_STATUS] &
|
||||
R_STATUS_BYTEORDER_MASK) == 0) {
|
||||
/* LE: LSB transmitted first (default for ibex processor) */
|
||||
shift_mask = 0xff << (i * 8);
|
||||
} else {
|
||||
/* BE: MSB transmitted first */
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: Big endian is not supported\n", __func__);
|
||||
}
|
||||
|
||||
fifo8_push(&s->tx_fifo, (val32 & shift_mask) >> (i * 8));
|
||||
}
|
||||
|
||||
/* Reset TXEMPTY */
|
||||
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXEMPTY_MASK;
|
||||
/* Update TXQD */
|
||||
txqd_len = (s->regs[IBEX_SPI_HOST_STATUS] &
|
||||
R_STATUS_TXQD_MASK) >> R_STATUS_TXQD_SHIFT;
|
||||
/* Partial bytes (size < 4) are padded, in words. */
|
||||
txqd_len += 1;
|
||||
s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXQD_MASK;
|
||||
s->regs[IBEX_SPI_HOST_STATUS] |= txqd_len;
|
||||
/* Assert Ready */
|
||||
s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_READY_MASK;
|
||||
break;
|
||||
case IBEX_SPI_HOST_ERROR_ENABLE:
|
||||
s->regs[addr] = val32;
|
||||
|
||||
if (val32 & R_ERROR_ENABLE_CMDINVAL_MASK) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: Segment Length is not supported\n", __func__);
|
||||
}
|
||||
break;
|
||||
case IBEX_SPI_HOST_ERROR_STATUS:
|
||||
/*
|
||||
* Indicates that any errors that have occurred.
|
||||
* When an error occurs, the corresponding bit must be cleared
|
||||
* here before issuing any further commands
|
||||
*/
|
||||
s->regs[addr] = val32;
|
||||
break;
|
||||
case IBEX_SPI_HOST_EVENT_ENABLE:
|
||||
/* Controls which classes of SPI events raise an interrupt. */
|
||||
s->regs[addr] = val32;
|
||||
|
||||
if (val32 & R_EVENT_ENABLE_RXWM_MASK) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: RXWM is not supported\n", __func__);
|
||||
}
|
||||
if (val32 & R_EVENT_ENABLE_TXWM_MASK) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: TXWM is not supported\n", __func__);
|
||||
}
|
||||
|
||||
if (val32 & R_EVENT_ENABLE_IDLE_MASK) {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"%s: IDLE is not supported\n", __func__);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "Bad offset 0x%" HWADDR_PRIx "\n",
|
||||
addr << 2);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ibex_spi_ops = {
|
||||
.read = ibex_spi_host_read,
|
||||
.write = ibex_spi_host_write,
|
||||
/* Ibex default LE */
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static Property ibex_spi_properties[] = {
|
||||
DEFINE_PROP_UINT32("num_cs", IbexSPIHostState, num_cs, 1),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_ibex = {
|
||||
.name = TYPE_IBEX_SPI_HOST,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32_ARRAY(regs, IbexSPIHostState, IBEX_SPI_HOST_MAX_REGS),
|
||||
VMSTATE_VARRAY_UINT32(config_opts, IbexSPIHostState,
|
||||
num_cs, 0, vmstate_info_uint32, uint32_t),
|
||||
VMSTATE_FIFO8(rx_fifo, IbexSPIHostState),
|
||||
VMSTATE_FIFO8(tx_fifo, IbexSPIHostState),
|
||||
VMSTATE_TIMER_PTR(fifo_trigger_handle, IbexSPIHostState),
|
||||
VMSTATE_BOOL(init_status, IbexSPIHostState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void fifo_trigger_update(void *opaque)
|
||||
{
|
||||
IbexSPIHostState *s = opaque;
|
||||
ibex_spi_host_transfer(s);
|
||||
}
|
||||
|
||||
static void ibex_spi_host_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
IbexSPIHostState *s = IBEX_SPI_HOST(dev);
|
||||
int i;
|
||||
|
||||
s->ssi = ssi_create_bus(dev, "ssi");
|
||||
s->cs_lines = g_new0(qemu_irq, s->num_cs);
|
||||
|
||||
for (i = 0; i < s->num_cs; ++i) {
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->cs_lines[i]);
|
||||
}
|
||||
|
||||
/* Setup CONFIGOPTS Multi-register */
|
||||
s->config_opts = g_new0(uint32_t, s->num_cs);
|
||||
|
||||
/* Setup FIFO Interrupt Timer */
|
||||
s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||
fifo_trigger_update, s);
|
||||
|
||||
/* FIFO sizes as per OT Spec */
|
||||
fifo8_create(&s->tx_fifo, IBEX_SPI_HOST_TXFIFO_LEN);
|
||||
fifo8_create(&s->rx_fifo, IBEX_SPI_HOST_RXFIFO_LEN);
|
||||
}
|
||||
|
||||
static void ibex_spi_host_init(Object *obj)
|
||||
{
|
||||
IbexSPIHostState *s = IBEX_SPI_HOST(obj);
|
||||
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->host_err);
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->event);
|
||||
|
||||
memory_region_init_io(&s->mmio, obj, &ibex_spi_ops, s,
|
||||
TYPE_IBEX_SPI_HOST, 0x1000);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
|
||||
}
|
||||
|
||||
static void ibex_spi_host_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
dc->realize = ibex_spi_host_realize;
|
||||
dc->reset = ibex_spi_host_reset;
|
||||
dc->vmsd = &vmstate_ibex;
|
||||
device_class_set_props(dc, ibex_spi_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo ibex_spi_host_info = {
|
||||
.name = TYPE_IBEX_SPI_HOST,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IbexSPIHostState),
|
||||
.instance_init = ibex_spi_host_init,
|
||||
.class_init = ibex_spi_host_class_init,
|
||||
};
|
||||
|
||||
static void ibex_spi_host_register_types(void)
|
||||
{
|
||||
type_register_static(&ibex_spi_host_info);
|
||||
}
|
||||
|
||||
type_init(ibex_spi_host_register_types)
|
@ -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'))
|
||||
|
@ -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:"
|
||||
|
@ -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);
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
typedef struct RISCVAclintMTimerState {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
uint64_t time_delta;
|
||||
|
||||
/*< public >*/
|
||||
MemoryRegion mmio;
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
94
include/hw/ssi/ibex_spi_host.h
Normal file
94
include/hw/ssi/ibex_spi_host.h
Normal file
@ -0,0 +1,94 @@
|
||||
|
||||
/*
|
||||
* QEMU model of the Ibex SPI Controller
|
||||
* SPEC Reference: https://docs.opentitan.org/hw/ip/spi_host/doc/
|
||||
*
|
||||
* Copyright (C) 2022 Western Digital
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef IBEX_SPI_HOST_H
|
||||
#define IBEX_SPI_HOST_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/ssi/ssi.h"
|
||||
#include "qemu/fifo8.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "qemu/timer.h"
|
||||
|
||||
#define TYPE_IBEX_SPI_HOST "ibex-spi"
|
||||
#define IBEX_SPI_HOST(obj) \
|
||||
OBJECT_CHECK(IbexSPIHostState, (obj), TYPE_IBEX_SPI_HOST)
|
||||
|
||||
/* SPI Registers */
|
||||
#define IBEX_SPI_HOST_INTR_STATE (0x00 / 4) /* rw */
|
||||
#define IBEX_SPI_HOST_INTR_ENABLE (0x04 / 4) /* rw */
|
||||
#define IBEX_SPI_HOST_INTR_TEST (0x08 / 4) /* wo */
|
||||
#define IBEX_SPI_HOST_ALERT_TEST (0x0c / 4) /* wo */
|
||||
#define IBEX_SPI_HOST_CONTROL (0x10 / 4) /* rw */
|
||||
#define IBEX_SPI_HOST_STATUS (0x14 / 4) /* ro */
|
||||
#define IBEX_SPI_HOST_CONFIGOPTS (0x18 / 4) /* rw */
|
||||
#define IBEX_SPI_HOST_CSID (0x1c / 4) /* rw */
|
||||
#define IBEX_SPI_HOST_COMMAND (0x20 / 4) /* wo */
|
||||
/* RX/TX Modelled by FIFO */
|
||||
#define IBEX_SPI_HOST_RXDATA (0x24 / 4)
|
||||
#define IBEX_SPI_HOST_TXDATA (0x28 / 4)
|
||||
|
||||
#define IBEX_SPI_HOST_ERROR_ENABLE (0x2c / 4) /* rw */
|
||||
#define IBEX_SPI_HOST_ERROR_STATUS (0x30 / 4) /* rw */
|
||||
#define IBEX_SPI_HOST_EVENT_ENABLE (0x34 / 4) /* rw */
|
||||
|
||||
/* FIFO Len in Bytes */
|
||||
#define IBEX_SPI_HOST_TXFIFO_LEN 288
|
||||
#define IBEX_SPI_HOST_RXFIFO_LEN 256
|
||||
|
||||
/* Max Register (Based on addr) */
|
||||
#define IBEX_SPI_HOST_MAX_REGS (IBEX_SPI_HOST_EVENT_ENABLE + 1)
|
||||
|
||||
/* MISC */
|
||||
#define TX_INTERRUPT_TRIGGER_DELAY_NS 100
|
||||
#define BIDIRECTIONAL_TRANSFER 3
|
||||
|
||||
typedef struct {
|
||||
/* <private> */
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
/* <public> */
|
||||
MemoryRegion mmio;
|
||||
uint32_t regs[IBEX_SPI_HOST_MAX_REGS];
|
||||
/* Multi-reg that sets config opts per CS */
|
||||
uint32_t *config_opts;
|
||||
Fifo8 rx_fifo;
|
||||
Fifo8 tx_fifo;
|
||||
QEMUTimer *fifo_trigger_handle;
|
||||
|
||||
qemu_irq event;
|
||||
qemu_irq host_err;
|
||||
uint32_t num_cs;
|
||||
qemu_irq *cs_lines;
|
||||
SSIBus *ssi;
|
||||
|
||||
/* Used to track the init status, for replicating TXDATA ghost writes */
|
||||
bool init_status;
|
||||
} IbexSPIHostState;
|
||||
|
||||
#endif
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -290,6 +290,15 @@ static RISCVException epmp(CPURISCVState *env, int csrno)
|
||||
|
||||
return RISCV_EXCP_ILLEGAL_INST;
|
||||
}
|
||||
|
||||
static RISCVException debug(CPURISCVState *env, int csrno)
|
||||
{
|
||||
if (riscv_feature(env, RISCV_FEATURE_DEBUG)) {
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
return RISCV_EXCP_ILLEGAL_INST;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* User Floating-Point CSRs */
|
||||
@ -1398,15 +1407,114 @@ static RISCVException write_mtval(CPURISCVState *env, int csrno,
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
/* Execution environment configuration setup */
|
||||
static RISCVException read_menvcfg(CPURISCVState *env, int csrno,
|
||||
target_ulong *val)
|
||||
{
|
||||
*val = env->menvcfg;
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
static RISCVException write_menvcfg(CPURISCVState *env, int csrno,
|
||||
target_ulong val)
|
||||
{
|
||||
uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | MENVCFG_CBZE;
|
||||
|
||||
if (riscv_cpu_mxl(env) == MXL_RV64) {
|
||||
mask |= MENVCFG_PBMTE | MENVCFG_STCE;
|
||||
}
|
||||
env->menvcfg = (env->menvcfg & ~mask) | (val & mask);
|
||||
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
static RISCVException read_menvcfgh(CPURISCVState *env, int csrno,
|
||||
target_ulong *val)
|
||||
{
|
||||
*val = env->menvcfg >> 32;
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
static RISCVException write_menvcfgh(CPURISCVState *env, int csrno,
|
||||
target_ulong val)
|
||||
{
|
||||
uint64_t mask = MENVCFG_PBMTE | MENVCFG_STCE;
|
||||
uint64_t valh = (uint64_t)val << 32;
|
||||
|
||||
env->menvcfg = (env->menvcfg & ~mask) | (valh & mask);
|
||||
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
static RISCVException read_senvcfg(CPURISCVState *env, int csrno,
|
||||
target_ulong *val)
|
||||
{
|
||||
*val = env->senvcfg;
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
static RISCVException write_senvcfg(CPURISCVState *env, int csrno,
|
||||
target_ulong val)
|
||||
{
|
||||
uint64_t mask = SENVCFG_FIOM | SENVCFG_CBIE | SENVCFG_CBCFE | SENVCFG_CBZE;
|
||||
|
||||
env->senvcfg = (env->senvcfg & ~mask) | (val & mask);
|
||||
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
static RISCVException read_henvcfg(CPURISCVState *env, int csrno,
|
||||
target_ulong *val)
|
||||
{
|
||||
*val = env->henvcfg;
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
static RISCVException write_henvcfg(CPURISCVState *env, int csrno,
|
||||
target_ulong val)
|
||||
{
|
||||
uint64_t mask = HENVCFG_FIOM | HENVCFG_CBIE | HENVCFG_CBCFE | HENVCFG_CBZE;
|
||||
|
||||
if (riscv_cpu_mxl(env) == MXL_RV64) {
|
||||
mask |= HENVCFG_PBMTE | HENVCFG_STCE;
|
||||
}
|
||||
|
||||
env->henvcfg = (env->henvcfg & ~mask) | (val & mask);
|
||||
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
static RISCVException read_henvcfgh(CPURISCVState *env, int csrno,
|
||||
target_ulong *val)
|
||||
{
|
||||
*val = env->henvcfg >> 32;
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
static RISCVException write_henvcfgh(CPURISCVState *env, int csrno,
|
||||
target_ulong val)
|
||||
{
|
||||
uint64_t mask = HENVCFG_PBMTE | HENVCFG_STCE;
|
||||
uint64_t valh = (uint64_t)val << 32;
|
||||
|
||||
env->henvcfg = (env->henvcfg & ~mask) | (valh & mask);
|
||||
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
static RISCVException rmw_mip64(CPURISCVState *env, int csrno,
|
||||
uint64_t *ret_val,
|
||||
uint64_t new_val, uint64_t wr_mask)
|
||||
{
|
||||
RISCVCPU *cpu = env_archcpu(env);
|
||||
/* Allow software control of delegable interrupts not claimed by hardware */
|
||||
uint64_t old_mip, mask = wr_mask & delegable_ints & ~env->miclaim;
|
||||
uint64_t old_mip, mask = wr_mask & delegable_ints;
|
||||
uint32_t gin;
|
||||
|
||||
if (mask & MIP_SEIP) {
|
||||
env->software_seip = new_val & MIP_SEIP;
|
||||
new_val |= env->external_seip * MIP_SEIP;
|
||||
}
|
||||
|
||||
if (mask) {
|
||||
old_mip = riscv_cpu_update_mip(cpu, mask, (new_val & mask));
|
||||
} else {
|
||||
@ -2578,6 +2686,48 @@ static RISCVException write_pmpaddr(CPURISCVState *env, int csrno,
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
static RISCVException read_tselect(CPURISCVState *env, int csrno,
|
||||
target_ulong *val)
|
||||
{
|
||||
*val = tselect_csr_read(env);
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
static RISCVException write_tselect(CPURISCVState *env, int csrno,
|
||||
target_ulong val)
|
||||
{
|
||||
tselect_csr_write(env, val);
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
static RISCVException read_tdata(CPURISCVState *env, int csrno,
|
||||
target_ulong *val)
|
||||
{
|
||||
/* return 0 in tdata1 to end the trigger enumeration */
|
||||
if (env->trigger_cur >= TRIGGER_NUM && csrno == CSR_TDATA1) {
|
||||
*val = 0;
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
if (!tdata_available(env, csrno - CSR_TDATA1)) {
|
||||
return RISCV_EXCP_ILLEGAL_INST;
|
||||
}
|
||||
|
||||
*val = tdata_csr_read(env, csrno - CSR_TDATA1);
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
static RISCVException write_tdata(CPURISCVState *env, int csrno,
|
||||
target_ulong val)
|
||||
{
|
||||
if (!tdata_available(env, csrno - CSR_TDATA1)) {
|
||||
return RISCV_EXCP_ILLEGAL_INST;
|
||||
}
|
||||
|
||||
tdata_csr_write(env, csrno - CSR_TDATA1, val);
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions to access Pointer Masking feature registers
|
||||
* We have to check if current priv lvl could modify
|
||||
@ -2880,6 +3030,7 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env,
|
||||
{
|
||||
/* check privileges and return RISCV_EXCP_ILLEGAL_INST if check fails */
|
||||
int read_only = get_field(csrno, 0xC00) == 3;
|
||||
int csr_min_priv = csr_ops[csrno].min_priv_ver;
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
int effective_priv = env->priv;
|
||||
|
||||
@ -2912,6 +3063,10 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env,
|
||||
return RISCV_EXCP_ILLEGAL_INST;
|
||||
}
|
||||
|
||||
if (env->priv_ver < csr_min_priv) {
|
||||
return RISCV_EXCP_ILLEGAL_INST;
|
||||
}
|
||||
|
||||
return csr_ops[csrno].predicate(env, csrno);
|
||||
}
|
||||
|
||||
@ -3070,13 +3225,20 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
|
||||
[CSR_FRM] = { "frm", fs, read_frm, write_frm },
|
||||
[CSR_FCSR] = { "fcsr", fs, read_fcsr, write_fcsr },
|
||||
/* Vector CSRs */
|
||||
[CSR_VSTART] = { "vstart", vs, read_vstart, write_vstart },
|
||||
[CSR_VXSAT] = { "vxsat", vs, read_vxsat, write_vxsat },
|
||||
[CSR_VXRM] = { "vxrm", vs, read_vxrm, write_vxrm },
|
||||
[CSR_VCSR] = { "vcsr", vs, read_vcsr, write_vcsr },
|
||||
[CSR_VL] = { "vl", vs, read_vl },
|
||||
[CSR_VTYPE] = { "vtype", vs, read_vtype },
|
||||
[CSR_VLENB] = { "vlenb", vs, read_vlenb },
|
||||
[CSR_VSTART] = { "vstart", vs, read_vstart, write_vstart,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_VXSAT] = { "vxsat", vs, read_vxsat, write_vxsat,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_VXRM] = { "vxrm", vs, read_vxrm, write_vxrm,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_VCSR] = { "vcsr", vs, read_vcsr, write_vcsr,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_VL] = { "vl", vs, read_vl,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_VTYPE] = { "vtype", vs, read_vtype,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_VLENB] = { "vlenb", vs, read_vlenb,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
/* User Timers and Counters */
|
||||
[CSR_CYCLE] = { "cycle", ctr, read_instret },
|
||||
[CSR_INSTRET] = { "instret", ctr, read_instret },
|
||||
@ -3103,6 +3265,8 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
|
||||
[CSR_MIMPID] = { "mimpid", any, read_zero },
|
||||
[CSR_MHARTID] = { "mhartid", any, read_mhartid },
|
||||
|
||||
[CSR_MCONFIGPTR] = { "mconfigptr", any, read_zero,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
/* Machine Trap Setup */
|
||||
[CSR_MSTATUS] = { "mstatus", any, read_mstatus, write_mstatus, NULL,
|
||||
read_mstatus_i128 },
|
||||
@ -3149,6 +3313,18 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
|
||||
[CSR_MVIPH] = { "mviph", aia_any32, read_zero, write_ignore },
|
||||
[CSR_MIPH] = { "miph", aia_any32, NULL, NULL, rmw_miph },
|
||||
|
||||
/* Execution environment configuration */
|
||||
[CSR_MENVCFG] = { "menvcfg", any, read_menvcfg, write_menvcfg,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_MENVCFGH] = { "menvcfgh", any32, read_menvcfgh, write_menvcfgh,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_SENVCFG] = { "senvcfg", smode, read_senvcfg, write_senvcfg,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_HENVCFG] = { "henvcfg", hmode, read_henvcfg, write_henvcfg,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_HENVCFGH] = { "henvcfgh", hmode32, read_henvcfgh, write_henvcfgh,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
|
||||
/* Supervisor Trap Setup */
|
||||
[CSR_SSTATUS] = { "sstatus", smode, read_sstatus, write_sstatus, NULL,
|
||||
read_sstatus_i128 },
|
||||
@ -3185,33 +3361,58 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
|
||||
[CSR_SIEH] = { "sieh", aia_smode32, NULL, NULL, rmw_sieh },
|
||||
[CSR_SIPH] = { "siph", aia_smode32, NULL, NULL, rmw_siph },
|
||||
|
||||
[CSR_HSTATUS] = { "hstatus", hmode, read_hstatus, write_hstatus },
|
||||
[CSR_HEDELEG] = { "hedeleg", hmode, read_hedeleg, write_hedeleg },
|
||||
[CSR_HIDELEG] = { "hideleg", hmode, NULL, NULL, rmw_hideleg },
|
||||
[CSR_HVIP] = { "hvip", hmode, NULL, NULL, rmw_hvip },
|
||||
[CSR_HIP] = { "hip", hmode, NULL, NULL, rmw_hip },
|
||||
[CSR_HIE] = { "hie", hmode, NULL, NULL, rmw_hie },
|
||||
[CSR_HCOUNTEREN] = { "hcounteren", hmode, read_hcounteren, write_hcounteren },
|
||||
[CSR_HGEIE] = { "hgeie", hmode, read_hgeie, write_hgeie },
|
||||
[CSR_HTVAL] = { "htval", hmode, read_htval, write_htval },
|
||||
[CSR_HTINST] = { "htinst", hmode, read_htinst, write_htinst },
|
||||
[CSR_HGEIP] = { "hgeip", hmode, read_hgeip, NULL },
|
||||
[CSR_HGATP] = { "hgatp", hmode, read_hgatp, write_hgatp },
|
||||
[CSR_HTIMEDELTA] = { "htimedelta", hmode, read_htimedelta, write_htimedelta },
|
||||
[CSR_HTIMEDELTAH] = { "htimedeltah", hmode32, read_htimedeltah, write_htimedeltah },
|
||||
[CSR_HSTATUS] = { "hstatus", hmode, read_hstatus, write_hstatus,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_HEDELEG] = { "hedeleg", hmode, read_hedeleg, write_hedeleg,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_HIDELEG] = { "hideleg", hmode, NULL, NULL, rmw_hideleg,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_HVIP] = { "hvip", hmode, NULL, NULL, rmw_hvip,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_HIP] = { "hip", hmode, NULL, NULL, rmw_hip,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_HIE] = { "hie", hmode, NULL, NULL, rmw_hie,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_HCOUNTEREN] = { "hcounteren", hmode, read_hcounteren, write_hcounteren,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_HGEIE] = { "hgeie", hmode, read_hgeie, write_hgeie,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_HTVAL] = { "htval", hmode, read_htval, write_htval,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_HTINST] = { "htinst", hmode, read_htinst, write_htinst,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_HGEIP] = { "hgeip", hmode, read_hgeip,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_HGATP] = { "hgatp", hmode, read_hgatp, write_hgatp,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_HTIMEDELTA] = { "htimedelta", hmode, read_htimedelta, write_htimedelta,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_HTIMEDELTAH] = { "htimedeltah", hmode32, read_htimedeltah, write_htimedeltah,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
|
||||
[CSR_VSSTATUS] = { "vsstatus", hmode, read_vsstatus, write_vsstatus },
|
||||
[CSR_VSIP] = { "vsip", hmode, NULL, NULL, rmw_vsip },
|
||||
[CSR_VSIE] = { "vsie", hmode, NULL, NULL, rmw_vsie },
|
||||
[CSR_VSTVEC] = { "vstvec", hmode, read_vstvec, write_vstvec },
|
||||
[CSR_VSSCRATCH] = { "vsscratch", hmode, read_vsscratch, write_vsscratch },
|
||||
[CSR_VSEPC] = { "vsepc", hmode, read_vsepc, write_vsepc },
|
||||
[CSR_VSCAUSE] = { "vscause", hmode, read_vscause, write_vscause },
|
||||
[CSR_VSTVAL] = { "vstval", hmode, read_vstval, write_vstval },
|
||||
[CSR_VSATP] = { "vsatp", hmode, read_vsatp, write_vsatp },
|
||||
[CSR_VSSTATUS] = { "vsstatus", hmode, read_vsstatus, write_vsstatus,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_VSIP] = { "vsip", hmode, NULL, NULL, rmw_vsip,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_VSIE] = { "vsie", hmode, NULL, NULL, rmw_vsie ,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_VSTVEC] = { "vstvec", hmode, read_vstvec, write_vstvec,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_VSSCRATCH] = { "vsscratch", hmode, read_vsscratch, write_vsscratch,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_VSEPC] = { "vsepc", hmode, read_vsepc, write_vsepc,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_VSCAUSE] = { "vscause", hmode, read_vscause, write_vscause,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_VSTVAL] = { "vstval", hmode, read_vstval, write_vstval,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_VSATP] = { "vsatp", hmode, read_vsatp, write_vsatp,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
|
||||
[CSR_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2 },
|
||||
[CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst },
|
||||
[CSR_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
|
||||
/* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */
|
||||
[CSR_HVIEN] = { "hvien", aia_hmode, read_zero, write_ignore },
|
||||
@ -3245,7 +3446,8 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
|
||||
[CSR_VSIPH] = { "vsiph", aia_hmode32, NULL, NULL, rmw_vsiph },
|
||||
|
||||
/* Physical Memory Protection */
|
||||
[CSR_MSECCFG] = { "mseccfg", epmp, read_mseccfg, write_mseccfg },
|
||||
[CSR_MSECCFG] = { "mseccfg", epmp, read_mseccfg, write_mseccfg,
|
||||
.min_priv_ver = PRIV_VERSION_1_12_0 },
|
||||
[CSR_PMPCFG0] = { "pmpcfg0", pmp, read_pmpcfg, write_pmpcfg },
|
||||
[CSR_PMPCFG1] = { "pmpcfg1", pmp, read_pmpcfg, write_pmpcfg },
|
||||
[CSR_PMPCFG2] = { "pmpcfg2", pmp, read_pmpcfg, write_pmpcfg },
|
||||
@ -3267,6 +3469,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
|
||||
[CSR_PMPADDR14] = { "pmpaddr14", pmp, read_pmpaddr, write_pmpaddr },
|
||||
[CSR_PMPADDR15] = { "pmpaddr15", pmp, read_pmpaddr, write_pmpaddr },
|
||||
|
||||
/* Debug CSRs */
|
||||
[CSR_TSELECT] = { "tselect", debug, read_tselect, write_tselect },
|
||||
[CSR_TDATA1] = { "tdata1", debug, read_tdata, write_tdata },
|
||||
[CSR_TDATA2] = { "tdata2", debug, read_tdata, write_tdata },
|
||||
[CSR_TDATA3] = { "tdata3", debug, read_tdata, write_tdata },
|
||||
|
||||
/* User Pointer Masking */
|
||||
[CSR_UMTE] = { "umte", pointer_masking, read_umte, write_umte },
|
||||
[CSR_UPMMASK] = { "upmmask", pointer_masking, read_upmmask, write_upmmask },
|
||||
|
441
target/riscv/debug.c
Normal file
441
target/riscv/debug.c
Normal file
@ -0,0 +1,441 @@
|
||||
/*
|
||||
* QEMU RISC-V Native Debug Support
|
||||
*
|
||||
* Copyright (c) 2022 Wind River Systems, Inc.
|
||||
*
|
||||
* Author:
|
||||
* Bin Meng <bin.meng@windriver.com>
|
||||
*
|
||||
* This provides the native debug support via the Trigger Module, as defined
|
||||
* in the RISC-V Debug Specification:
|
||||
* https://github.com/riscv/riscv-debug-spec/raw/master/riscv-debug-stable.pdf
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qapi/error.h"
|
||||
#include "cpu.h"
|
||||
#include "trace.h"
|
||||
#include "exec/exec-all.h"
|
||||
|
||||
/*
|
||||
* The following M-mode trigger CSRs are implemented:
|
||||
*
|
||||
* - tselect
|
||||
* - tdata1
|
||||
* - tdata2
|
||||
* - tdata3
|
||||
*
|
||||
* We don't support writable 'type' field in the tdata1 register, so there is
|
||||
* no need to implement the "tinfo" CSR.
|
||||
*
|
||||
* The following triggers are implemented:
|
||||
*
|
||||
* Index | Type | tdata mapping | Description
|
||||
* ------+------+------------------------+------------
|
||||
* 0 | 2 | tdata1, tdata2 | Address / Data Match
|
||||
* 1 | 2 | tdata1, tdata2 | Address / Data Match
|
||||
*/
|
||||
|
||||
/* tdata availability of a trigger */
|
||||
typedef bool tdata_avail[TDATA_NUM];
|
||||
|
||||
static tdata_avail tdata_mapping[TRIGGER_NUM] = {
|
||||
[TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = { true, true, false },
|
||||
};
|
||||
|
||||
/* only breakpoint size 1/2/4/8 supported */
|
||||
static int access_size[SIZE_NUM] = {
|
||||
[SIZE_ANY] = 0,
|
||||
[SIZE_1B] = 1,
|
||||
[SIZE_2B] = 2,
|
||||
[SIZE_4B] = 4,
|
||||
[SIZE_6B] = -1,
|
||||
[SIZE_8B] = 8,
|
||||
[6 ... 15] = -1,
|
||||
};
|
||||
|
||||
static inline target_ulong trigger_type(CPURISCVState *env,
|
||||
trigger_type_t type)
|
||||
{
|
||||
target_ulong tdata1;
|
||||
|
||||
switch (riscv_cpu_mxl(env)) {
|
||||
case MXL_RV32:
|
||||
tdata1 = RV32_TYPE(type);
|
||||
break;
|
||||
case MXL_RV64:
|
||||
tdata1 = RV64_TYPE(type);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
return tdata1;
|
||||
}
|
||||
|
||||
bool tdata_available(CPURISCVState *env, int tdata_index)
|
||||
{
|
||||
if (unlikely(tdata_index >= TDATA_NUM)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (unlikely(env->trigger_cur >= TRIGGER_NUM)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return tdata_mapping[env->trigger_cur][tdata_index];
|
||||
}
|
||||
|
||||
target_ulong tselect_csr_read(CPURISCVState *env)
|
||||
{
|
||||
return env->trigger_cur;
|
||||
}
|
||||
|
||||
void tselect_csr_write(CPURISCVState *env, target_ulong val)
|
||||
{
|
||||
/* all target_ulong bits of tselect are implemented */
|
||||
env->trigger_cur = val;
|
||||
}
|
||||
|
||||
static target_ulong tdata1_validate(CPURISCVState *env, target_ulong val,
|
||||
trigger_type_t t)
|
||||
{
|
||||
uint32_t type, dmode;
|
||||
target_ulong tdata1;
|
||||
|
||||
switch (riscv_cpu_mxl(env)) {
|
||||
case MXL_RV32:
|
||||
type = extract32(val, 28, 4);
|
||||
dmode = extract32(val, 27, 1);
|
||||
tdata1 = RV32_TYPE(t);
|
||||
break;
|
||||
case MXL_RV64:
|
||||
type = extract64(val, 60, 4);
|
||||
dmode = extract64(val, 59, 1);
|
||||
tdata1 = RV64_TYPE(t);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
if (type != t) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"ignoring type write to tdata1 register\n");
|
||||
}
|
||||
if (dmode != 0) {
|
||||
qemu_log_mask(LOG_UNIMP, "debug mode is not supported\n");
|
||||
}
|
||||
|
||||
return tdata1;
|
||||
}
|
||||
|
||||
static inline void warn_always_zero_bit(target_ulong val, target_ulong mask,
|
||||
const char *msg)
|
||||
{
|
||||
if (val & mask) {
|
||||
qemu_log_mask(LOG_UNIMP, "%s bit is always zero\n", msg);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t type2_breakpoint_size(CPURISCVState *env, target_ulong ctrl)
|
||||
{
|
||||
uint32_t size, sizelo, sizehi = 0;
|
||||
|
||||
if (riscv_cpu_mxl(env) == MXL_RV64) {
|
||||
sizehi = extract32(ctrl, 21, 2);
|
||||
}
|
||||
sizelo = extract32(ctrl, 16, 2);
|
||||
size = (sizehi << 2) | sizelo;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static inline bool type2_breakpoint_enabled(target_ulong ctrl)
|
||||
{
|
||||
bool mode = !!(ctrl & (TYPE2_U | TYPE2_S | TYPE2_M));
|
||||
bool rwx = !!(ctrl & (TYPE2_LOAD | TYPE2_STORE | TYPE2_EXEC));
|
||||
|
||||
return mode && rwx;
|
||||
}
|
||||
|
||||
static target_ulong type2_mcontrol_validate(CPURISCVState *env,
|
||||
target_ulong ctrl)
|
||||
{
|
||||
target_ulong val;
|
||||
uint32_t size;
|
||||
|
||||
/* validate the generic part first */
|
||||
val = tdata1_validate(env, ctrl, TRIGGER_TYPE_AD_MATCH);
|
||||
|
||||
/* validate unimplemented (always zero) bits */
|
||||
warn_always_zero_bit(ctrl, TYPE2_MATCH, "match");
|
||||
warn_always_zero_bit(ctrl, TYPE2_CHAIN, "chain");
|
||||
warn_always_zero_bit(ctrl, TYPE2_ACTION, "action");
|
||||
warn_always_zero_bit(ctrl, TYPE2_TIMING, "timing");
|
||||
warn_always_zero_bit(ctrl, TYPE2_SELECT, "select");
|
||||
warn_always_zero_bit(ctrl, TYPE2_HIT, "hit");
|
||||
|
||||
/* validate size encoding */
|
||||
size = type2_breakpoint_size(env, ctrl);
|
||||
if (access_size[size] == -1) {
|
||||
qemu_log_mask(LOG_UNIMP, "access size %d is not supported, using SIZE_ANY\n",
|
||||
size);
|
||||
} else {
|
||||
val |= (ctrl & TYPE2_SIZELO);
|
||||
if (riscv_cpu_mxl(env) == MXL_RV64) {
|
||||
val |= (ctrl & TYPE2_SIZEHI);
|
||||
}
|
||||
}
|
||||
|
||||
/* keep the mode and attribute bits */
|
||||
val |= (ctrl & (TYPE2_U | TYPE2_S | TYPE2_M |
|
||||
TYPE2_LOAD | TYPE2_STORE | TYPE2_EXEC));
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void type2_breakpoint_insert(CPURISCVState *env, target_ulong index)
|
||||
{
|
||||
target_ulong ctrl = env->type2_trig[index].mcontrol;
|
||||
target_ulong addr = env->type2_trig[index].maddress;
|
||||
bool enabled = type2_breakpoint_enabled(ctrl);
|
||||
CPUState *cs = env_cpu(env);
|
||||
int flags = BP_CPU | BP_STOP_BEFORE_ACCESS;
|
||||
uint32_t size;
|
||||
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctrl & TYPE2_EXEC) {
|
||||
cpu_breakpoint_insert(cs, addr, flags, &env->type2_trig[index].bp);
|
||||
}
|
||||
|
||||
if (ctrl & TYPE2_LOAD) {
|
||||
flags |= BP_MEM_READ;
|
||||
}
|
||||
if (ctrl & TYPE2_STORE) {
|
||||
flags |= BP_MEM_WRITE;
|
||||
}
|
||||
|
||||
if (flags & BP_MEM_ACCESS) {
|
||||
size = type2_breakpoint_size(env, ctrl);
|
||||
if (size != 0) {
|
||||
cpu_watchpoint_insert(cs, addr, size, flags,
|
||||
&env->type2_trig[index].wp);
|
||||
} else {
|
||||
cpu_watchpoint_insert(cs, addr, 8, flags,
|
||||
&env->type2_trig[index].wp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void type2_breakpoint_remove(CPURISCVState *env, target_ulong index)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
|
||||
if (env->type2_trig[index].bp) {
|
||||
cpu_breakpoint_remove_by_ref(cs, env->type2_trig[index].bp);
|
||||
env->type2_trig[index].bp = NULL;
|
||||
}
|
||||
|
||||
if (env->type2_trig[index].wp) {
|
||||
cpu_watchpoint_remove_by_ref(cs, env->type2_trig[index].wp);
|
||||
env->type2_trig[index].wp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static target_ulong type2_reg_read(CPURISCVState *env,
|
||||
target_ulong trigger_index, int tdata_index)
|
||||
{
|
||||
uint32_t index = trigger_index - TRIGGER_TYPE2_IDX_0;
|
||||
target_ulong tdata;
|
||||
|
||||
switch (tdata_index) {
|
||||
case TDATA1:
|
||||
tdata = env->type2_trig[index].mcontrol;
|
||||
break;
|
||||
case TDATA2:
|
||||
tdata = env->type2_trig[index].maddress;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
return tdata;
|
||||
}
|
||||
|
||||
static void type2_reg_write(CPURISCVState *env, target_ulong trigger_index,
|
||||
int tdata_index, target_ulong val)
|
||||
{
|
||||
uint32_t index = trigger_index - TRIGGER_TYPE2_IDX_0;
|
||||
target_ulong new_val;
|
||||
|
||||
switch (tdata_index) {
|
||||
case TDATA1:
|
||||
new_val = type2_mcontrol_validate(env, val);
|
||||
if (new_val != env->type2_trig[index].mcontrol) {
|
||||
env->type2_trig[index].mcontrol = new_val;
|
||||
type2_breakpoint_remove(env, index);
|
||||
type2_breakpoint_insert(env, index);
|
||||
}
|
||||
break;
|
||||
case TDATA2:
|
||||
if (val != env->type2_trig[index].maddress) {
|
||||
env->type2_trig[index].maddress = val;
|
||||
type2_breakpoint_remove(env, index);
|
||||
type2_breakpoint_insert(env, index);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
typedef target_ulong (*tdata_read_func)(CPURISCVState *env,
|
||||
target_ulong trigger_index,
|
||||
int tdata_index);
|
||||
|
||||
static tdata_read_func trigger_read_funcs[TRIGGER_NUM] = {
|
||||
[TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = type2_reg_read,
|
||||
};
|
||||
|
||||
typedef void (*tdata_write_func)(CPURISCVState *env,
|
||||
target_ulong trigger_index,
|
||||
int tdata_index,
|
||||
target_ulong val);
|
||||
|
||||
static tdata_write_func trigger_write_funcs[TRIGGER_NUM] = {
|
||||
[TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = type2_reg_write,
|
||||
};
|
||||
|
||||
target_ulong tdata_csr_read(CPURISCVState *env, int tdata_index)
|
||||
{
|
||||
tdata_read_func read_func = trigger_read_funcs[env->trigger_cur];
|
||||
|
||||
return read_func(env, env->trigger_cur, tdata_index);
|
||||
}
|
||||
|
||||
void tdata_csr_write(CPURISCVState *env, int tdata_index, target_ulong val)
|
||||
{
|
||||
tdata_write_func write_func = trigger_write_funcs[env->trigger_cur];
|
||||
|
||||
return write_func(env, env->trigger_cur, tdata_index, val);
|
||||
}
|
||||
|
||||
void riscv_cpu_debug_excp_handler(CPUState *cs)
|
||||
{
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
|
||||
if (cs->watchpoint_hit) {
|
||||
if (cs->watchpoint_hit->flags & BP_CPU) {
|
||||
cs->watchpoint_hit = NULL;
|
||||
riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, 0);
|
||||
}
|
||||
} else {
|
||||
if (cpu_breakpoint_test(cs, env->pc, BP_CPU)) {
|
||||
riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool riscv_cpu_debug_check_breakpoint(CPUState *cs)
|
||||
{
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
CPUBreakpoint *bp;
|
||||
target_ulong ctrl;
|
||||
target_ulong pc;
|
||||
int i;
|
||||
|
||||
QTAILQ_FOREACH(bp, &cs->breakpoints, entry) {
|
||||
for (i = 0; i < TRIGGER_TYPE2_NUM; i++) {
|
||||
ctrl = env->type2_trig[i].mcontrol;
|
||||
pc = env->type2_trig[i].maddress;
|
||||
|
||||
if ((ctrl & TYPE2_EXEC) && (bp->pc == pc)) {
|
||||
/* check U/S/M bit against current privilege level */
|
||||
if ((ctrl >> 3) & BIT(env->priv)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp)
|
||||
{
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
target_ulong ctrl;
|
||||
target_ulong addr;
|
||||
int flags;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < TRIGGER_TYPE2_NUM; i++) {
|
||||
ctrl = env->type2_trig[i].mcontrol;
|
||||
addr = env->type2_trig[i].maddress;
|
||||
flags = 0;
|
||||
|
||||
if (ctrl & TYPE2_LOAD) {
|
||||
flags |= BP_MEM_READ;
|
||||
}
|
||||
if (ctrl & TYPE2_STORE) {
|
||||
flags |= BP_MEM_WRITE;
|
||||
}
|
||||
|
||||
if ((wp->flags & flags) && (wp->vaddr == addr)) {
|
||||
/* check U/S/M bit against current privilege level */
|
||||
if ((ctrl >> 3) & BIT(env->priv)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void riscv_trigger_init(CPURISCVState *env)
|
||||
{
|
||||
target_ulong type2 = trigger_type(env, TRIGGER_TYPE_AD_MATCH);
|
||||
int i;
|
||||
|
||||
/* type 2 triggers */
|
||||
for (i = 0; i < TRIGGER_TYPE2_NUM; i++) {
|
||||
/*
|
||||
* type = TRIGGER_TYPE_AD_MATCH
|
||||
* dmode = 0 (both debug and M-mode can write tdata)
|
||||
* maskmax = 0 (unimplemented, always 0)
|
||||
* sizehi = 0 (match against any size, RV64 only)
|
||||
* hit = 0 (unimplemented, always 0)
|
||||
* select = 0 (always 0, perform match on address)
|
||||
* timing = 0 (always 0, trigger before instruction)
|
||||
* sizelo = 0 (match against any size)
|
||||
* action = 0 (always 0, raise a breakpoint exception)
|
||||
* chain = 0 (unimplemented, always 0)
|
||||
* match = 0 (always 0, when any compare value equals tdata2)
|
||||
*/
|
||||
env->type2_trig[i].mcontrol = type2;
|
||||
env->type2_trig[i].maddress = 0;
|
||||
env->type2_trig[i].bp = NULL;
|
||||
env->type2_trig[i].wp = NULL;
|
||||
}
|
||||
}
|
114
target/riscv/debug.h
Normal file
114
target/riscv/debug.h
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* QEMU RISC-V Native Debug Support
|
||||
*
|
||||
* Copyright (c) 2022 Wind River Systems, Inc.
|
||||
*
|
||||
* Author:
|
||||
* Bin Meng <bin.meng@windriver.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RISCV_DEBUG_H
|
||||
#define RISCV_DEBUG_H
|
||||
|
||||
/* trigger indexes implemented */
|
||||
enum {
|
||||
TRIGGER_TYPE2_IDX_0 = 0,
|
||||
TRIGGER_TYPE2_IDX_1,
|
||||
TRIGGER_TYPE2_NUM,
|
||||
TRIGGER_NUM = TRIGGER_TYPE2_NUM
|
||||
};
|
||||
|
||||
/* register index of tdata CSRs */
|
||||
enum {
|
||||
TDATA1 = 0,
|
||||
TDATA2,
|
||||
TDATA3,
|
||||
TDATA_NUM
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
TRIGGER_TYPE_NO_EXIST = 0, /* trigger does not exist */
|
||||
TRIGGER_TYPE_AD_MATCH = 2, /* address/data match trigger */
|
||||
TRIGGER_TYPE_INST_CNT = 3, /* instruction count trigger */
|
||||
TRIGGER_TYPE_INT = 4, /* interrupt trigger */
|
||||
TRIGGER_TYPE_EXCP = 5, /* exception trigger */
|
||||
TRIGGER_TYPE_AD_MATCH6 = 6, /* new address/data match trigger */
|
||||
TRIGGER_TYPE_EXT_SRC = 7, /* external source trigger */
|
||||
TRIGGER_TYPE_UNAVAIL = 15 /* trigger exists, but unavailable */
|
||||
} trigger_type_t;
|
||||
|
||||
typedef struct {
|
||||
target_ulong mcontrol;
|
||||
target_ulong maddress;
|
||||
struct CPUBreakpoint *bp;
|
||||
struct CPUWatchpoint *wp;
|
||||
} type2_trigger_t;
|
||||
|
||||
/* tdata field masks */
|
||||
|
||||
#define RV32_TYPE(t) ((uint32_t)(t) << 28)
|
||||
#define RV32_TYPE_MASK (0xf << 28)
|
||||
#define RV32_DMODE BIT(27)
|
||||
#define RV64_TYPE(t) ((uint64_t)(t) << 60)
|
||||
#define RV64_TYPE_MASK (0xfULL << 60)
|
||||
#define RV64_DMODE BIT_ULL(59)
|
||||
|
||||
/* mcontrol field masks */
|
||||
|
||||
#define TYPE2_LOAD BIT(0)
|
||||
#define TYPE2_STORE BIT(1)
|
||||
#define TYPE2_EXEC BIT(2)
|
||||
#define TYPE2_U BIT(3)
|
||||
#define TYPE2_S BIT(4)
|
||||
#define TYPE2_M BIT(6)
|
||||
#define TYPE2_MATCH (0xf << 7)
|
||||
#define TYPE2_CHAIN BIT(11)
|
||||
#define TYPE2_ACTION (0xf << 12)
|
||||
#define TYPE2_SIZELO (0x3 << 16)
|
||||
#define TYPE2_TIMING BIT(18)
|
||||
#define TYPE2_SELECT BIT(19)
|
||||
#define TYPE2_HIT BIT(20)
|
||||
#define TYPE2_SIZEHI (0x3 << 21) /* RV64 only */
|
||||
|
||||
/* access size */
|
||||
enum {
|
||||
SIZE_ANY = 0,
|
||||
SIZE_1B,
|
||||
SIZE_2B,
|
||||
SIZE_4B,
|
||||
SIZE_6B,
|
||||
SIZE_8B,
|
||||
SIZE_10B,
|
||||
SIZE_12B,
|
||||
SIZE_14B,
|
||||
SIZE_16B,
|
||||
SIZE_NUM = 16
|
||||
};
|
||||
|
||||
bool tdata_available(CPURISCVState *env, int tdata_index);
|
||||
|
||||
target_ulong tselect_csr_read(CPURISCVState *env);
|
||||
void tselect_csr_write(CPURISCVState *env, target_ulong val);
|
||||
|
||||
target_ulong tdata_csr_read(CPURISCVState *env, int tdata_index);
|
||||
void tdata_csr_write(CPURISCVState *env, int tdata_index, target_ulong val);
|
||||
|
||||
void riscv_cpu_debug_excp_handler(CPUState *cs);
|
||||
bool riscv_cpu_debug_check_breakpoint(CPUState *cs);
|
||||
bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp);
|
||||
|
||||
void riscv_trigger_init(CPURISCVState *env);
|
||||
|
||||
#endif /* RISCV_DEBUG_H */
|
@ -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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
}
|
||||
};
|
||||
|
@ -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'
|
||||
))
|
||||
|
@ -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)
|
||||
|
@ -4888,25 +4888,20 @@ GEN_VEXT_VCOMPRESS_VM(vcompress_vm_w, uint32_t, H4)
|
||||
GEN_VEXT_VCOMPRESS_VM(vcompress_vm_d, uint64_t, H8)
|
||||
|
||||
/* Vector Whole Register Move */
|
||||
#define GEN_VEXT_VMV_WHOLE(NAME, LEN) \
|
||||
void HELPER(NAME)(void *vd, void *vs2, CPURISCVState *env, \
|
||||
uint32_t desc) \
|
||||
{ \
|
||||
/* EEW = 8 */ \
|
||||
uint32_t maxsz = simd_maxsz(desc); \
|
||||
uint32_t i = env->vstart; \
|
||||
\
|
||||
memcpy((uint8_t *)vd + H1(i), \
|
||||
(uint8_t *)vs2 + H1(i), \
|
||||
maxsz - env->vstart); \
|
||||
\
|
||||
env->vstart = 0; \
|
||||
}
|
||||
void HELPER(vmvr_v)(void *vd, void *vs2, CPURISCVState *env, uint32_t desc)
|
||||
{
|
||||
/* EEW = SEW */
|
||||
uint32_t maxsz = simd_maxsz(desc);
|
||||
uint32_t sewb = 1 << FIELD_EX64(env->vtype, VTYPE, VSEW);
|
||||
uint32_t startb = env->vstart * sewb;
|
||||
uint32_t i = startb;
|
||||
|
||||
GEN_VEXT_VMV_WHOLE(vmv1r_v, 1)
|
||||
GEN_VEXT_VMV_WHOLE(vmv2r_v, 2)
|
||||
GEN_VEXT_VMV_WHOLE(vmv4r_v, 4)
|
||||
GEN_VEXT_VMV_WHOLE(vmv8r_v, 8)
|
||||
memcpy((uint8_t *)vd + H1(i),
|
||||
(uint8_t *)vs2 + H1(i),
|
||||
maxsz - startb);
|
||||
|
||||
env->vstart = 0;
|
||||
}
|
||||
|
||||
/* Vector Integer Extension */
|
||||
#define GEN_VEXT_INT_EXT(NAME, ETYPE, DTYPE, HD, HS1) \
|
||||
|
Loading…
Reference in New Issue
Block a user