summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/lockdown.yml6
-rw-r--r--accel/tcg/cputlb.c40
-rw-r--r--block/coroutines.h5
-rw-r--r--block/monitor/bitmap-qmp-cmds.c6
-rw-r--r--block/nbd.c284
-rw-r--r--block/qapi-sysemu.c3
-rw-r--r--blockdev-nbd.c8
-rw-r--r--configs/targets/nios2-softmmu.mak1
-rw-r--r--contrib/elf2dmp/main.c6
-rw-r--r--contrib/vhost-user-blk/vhost-user-blk.c3
-rw-r--r--contrib/vhost-user-scsi/vhost-user-scsi.c79
-rw-r--r--docs/about/build-platforms.rst4
-rw-r--r--docs/about/deprecated.rst12
-rw-r--r--docs/about/removed-features.rst11
-rw-r--r--docs/pcie_sriov.txt6
-rw-r--r--docs/system/arm/virt.rst5
-rw-r--r--docs/tools/virtiofsd.rst5
-rw-r--r--dump/dump.c372
-rw-r--r--dump/win_dump.c305
-rw-r--r--fpu/softfloat-parts.c.inc11
-rw-r--r--fpu/softfloat.c20
-rw-r--r--hmp-commands.hx13
-rw-r--r--hw/arm/smmuv3.c2
-rw-r--r--hw/arm/virt.c102
-rw-r--r--hw/char/escc.c2
-rw-r--r--hw/intc/Kconfig3
-rw-r--r--hw/intc/arm_gicv3_common.c54
-rw-r--r--hw/intc/arm_gicv3_cpuif.c195
-rw-r--r--hw/intc/arm_gicv3_dist.c7
-rw-r--r--hw/intc/arm_gicv3_its.c776
-rw-r--r--hw/intc/arm_gicv3_its_kvm.c2
-rw-r--r--hw/intc/arm_gicv3_kvm.c5
-rw-r--r--hw/intc/arm_gicv3_redist.c480
-rw-r--r--hw/intc/gicv3_internal.h213
-rw-r--r--hw/intc/meson.build1
-rw-r--r--hw/intc/nios2_vic.c313
-rw-r--r--hw/intc/riscv_aclint.c144
-rw-r--r--hw/intc/trace-events18
-rw-r--r--hw/nios2/10m50_devboard.c115
-rw-r--r--hw/nios2/Kconfig1
-rw-r--r--hw/rdma/vmw/pvrdma_main.c9
-rw-r--r--hw/riscv/boot.c12
-rw-r--r--hw/riscv/opentitan.c36
-rw-r--r--hw/riscv/virt.c24
-rw-r--r--hw/ssi/ibex_spi_host.c612
-rw-r--r--hw/ssi/meson.build1
-rw-r--r--hw/ssi/trace-events7
-rw-r--r--hw/virtio/trace-events2
-rw-r--r--hw/virtio/vhost-vdpa.c2
-rw-r--r--include/block/block_int-global-state.h2
-rw-r--r--include/hw/arm/virt.h19
-rw-r--r--include/hw/core/tcg-cpu-ops.h1
-rw-r--r--include/hw/intc/arm_gicv3_common.h13
-rw-r--r--include/hw/intc/arm_gicv3_its_common.h19
-rw-r--r--include/hw/intc/nios2_vic.h64
-rw-r--r--include/hw/intc/riscv_aclint.h1
-rw-r--r--include/hw/riscv/boot.h4
-rw-r--r--include/hw/riscv/opentitan.h30
-rw-r--r--include/hw/ssi/ibex_spi_host.h94
-rw-r--r--include/qemu/osdep.h3
-rw-r--r--include/qemu/win_dump_defs.h115
-rw-r--r--include/sysemu/dump.h9
-rw-r--r--linux-user/arm/cpu_loop.c2
-rw-r--r--linux-user/elfload.c3
-rw-r--r--linux-user/nios2/cpu_loop.c96
-rw-r--r--linux-user/nios2/signal.c25
-rw-r--r--linux-user/nios2/target_cpu.h1
-rw-r--r--monitor/hmp-cmds.c4
-rw-r--r--nbd/server.c63
-rw-r--r--os-posix.c8
-rw-r--r--qapi/block-core.json6
-rw-r--r--qapi/block-export.json5
-rw-r--r--qapi/block.json6
-rw-r--r--qemu-img.c8
-rw-r--r--qemu-nbd.c11
-rw-r--r--qemu-options.hx10
-rw-r--r--target/arm/cpu.c16
-rw-r--r--target/arm/cpu.h59
-rw-r--r--target/arm/helper-a64.c4
-rw-r--r--target/arm/helper.c19
-rw-r--r--target/arm/hvf/hvf.c2
-rw-r--r--target/arm/m_helper.c6
-rw-r--r--target/arm/op_helper.c13
-rw-r--r--target/arm/translate-a32.h13
-rw-r--r--target/arm/translate-a64.c50
-rw-r--r--target/arm/translate-m-nocp.c12
-rw-r--r--target/arm/translate-neon.c21
-rw-r--r--target/arm/translate-sve.c9
-rw-r--r--target/arm/translate-vfp.c76
-rw-r--r--target/arm/translate.c101
-rw-r--r--target/arm/translate.h17
-rw-r--r--target/i386/tcg/fpu_helper.c4
-rw-r--r--target/mips/TODO51
-rw-r--r--target/nios2/cpu.c209
-rw-r--r--target/nios2/cpu.h252
-rw-r--r--target/nios2/helper.c363
-rw-r--r--target/nios2/helper.h5
-rw-r--r--target/nios2/meson.build7
-rw-r--r--target/nios2/mmu.c78
-rw-r--r--target/nios2/op_helper.c88
-rw-r--r--target/nios2/translate.c755
-rw-r--r--target/riscv/cpu.c120
-rw-r--r--target/riscv/cpu.h40
-rw-r--r--target/riscv/cpu_bits.h40
-rw-r--r--target/riscv/cpu_helper.c10
-rw-r--r--target/riscv/csr.c282
-rw-r--r--target/riscv/debug.c441
-rw-r--r--target/riscv/debug.h114
-rw-r--r--target/riscv/helper.h5
-rw-r--r--target/riscv/insn_trans/trans_rvv.c.inc25
-rw-r--r--target/riscv/machine.c55
-rw-r--r--target/riscv/meson.build1
-rw-r--r--target/riscv/pmp.c14
-rw-r--r--target/riscv/vector_helper.c31
-rw-r--r--target/s390x/tcg/translate.c2
-rwxr-xr-xtests/qemu-iotests/03085
-rwxr-xr-xtests/qemu-iotests/04053
-rwxr-xr-xtests/qemu-iotests/0562
-rwxr-xr-xtests/qemu-iotests/1082
-rwxr-xr-xtests/qemu-iotests/1496
-rwxr-xr-xtests/qemu-iotests/1635
-rwxr-xr-xtests/qemu-iotests/2054
-rwxr-xr-xtests/qemu-iotests/21612
-rwxr-xr-xtests/qemu-iotests/2185
-rwxr-xr-xtests/qemu-iotests/22316
-rw-r--r--tests/qemu-iotests/223.out47
-rwxr-xr-xtests/qemu-iotests/2244
-rwxr-xr-xtests/qemu-iotests/2426
-rwxr-xr-xtests/qemu-iotests/24517
-rwxr-xr-xtests/qemu-iotests/2554
-rwxr-xr-xtests/qemu-iotests/25811
-rwxr-xr-xtests/qemu-iotests/29817
-rwxr-xr-xtests/qemu-iotests/3034
-rwxr-xr-xtests/qemu-iotests/31022
-rw-r--r--tests/qemu-iotests/iotests.py69
-rwxr-xr-xtests/qemu-iotests/tests/image-fleecing30
-rwxr-xr-xtests/qemu-iotests/tests/migration-permissions28
-rwxr-xr-xtests/qemu-iotests/tests/mirror-ready-cancel-error2
-rwxr-xr-xtests/qemu-iotests/tests/nbd-reconnect-on-open2
-rwxr-xr-xtests/qemu-iotests/tests/stream-error-on-reset4
-rw-r--r--tests/tcg/nios2/10m50-ghrd.ld66
-rw-r--r--tests/tcg/nios2/Makefile.softmmu-target33
-rw-r--r--tests/tcg/nios2/Makefile.target11
-rw-r--r--tests/tcg/nios2/boot.S218
-rw-r--r--tests/tcg/nios2/intr.S31
-rw-r--r--tests/tcg/nios2/semicall.h28
-rw-r--r--tests/tcg/nios2/test-shadow-1.S40
-rw-r--r--tools/virtiofsd/helper.c3
-rw-r--r--ui/cocoa.m1
-rw-r--r--ui/vnc.c7
-rw-r--r--util/osdep.c28
151 files changed, 7050 insertions, 2248 deletions
diff --git a/.github/workflows/lockdown.yml b/.github/workflows/lockdown.yml
index ad8b8f7e30..d5e1265cff 100644
--- a/.github/workflows/lockdown.yml
+++ b/.github/workflows/lockdown.yml
@@ -15,7 +15,7 @@ jobs:
steps:
- uses: dessant/repo-lockdown@v2
with:
- pull-comment: |
+ pr-comment: |
Thank you for your interest in the QEMU project.
This repository is a read-only mirror of the project's repostories hosted
@@ -26,5 +26,5 @@ jobs:
functionality). However, we get a lot of patches, and so we have some
guidelines about contributing on the project website:
https://www.qemu.org/contribute/
- lock-pull: true
- close-pull: true
+ lock-pr: true
+ close-pr: true
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
index dd45e0467b..f90f4312ea 100644
--- a/accel/tcg/cputlb.c
+++ b/accel/tcg/cputlb.c
@@ -1761,7 +1761,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
MemOpIdx oi, int size, int prot,
uintptr_t retaddr)
{
- size_t mmu_idx = get_mmuidx(oi);
+ uintptr_t mmu_idx = get_mmuidx(oi);
MemOp mop = get_memop(oi);
int a_bits = get_alignment_bits(mop);
uintptr_t index;
@@ -1769,6 +1769,8 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
target_ulong tlb_addr;
void *hostaddr;
+ tcg_debug_assert(mmu_idx < NB_MMU_MODES);
+
/* Adjust the given return address. */
retaddr -= GETPC_ADJ;
@@ -1908,18 +1910,20 @@ load_helper(CPUArchState *env, target_ulong addr, MemOpIdx oi,
uintptr_t retaddr, MemOp op, bool code_read,
FullLoadHelper *full_load)
{
- uintptr_t mmu_idx = get_mmuidx(oi);
- uintptr_t index = tlb_index(env, mmu_idx, addr);
- CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
- target_ulong tlb_addr = code_read ? entry->addr_code : entry->addr_read;
const size_t tlb_off = code_read ?
offsetof(CPUTLBEntry, addr_code) : offsetof(CPUTLBEntry, addr_read);
const MMUAccessType access_type =
code_read ? MMU_INST_FETCH : MMU_DATA_LOAD;
- unsigned a_bits = get_alignment_bits(get_memop(oi));
+ const unsigned a_bits = get_alignment_bits(get_memop(oi));
+ const size_t size = memop_size(op);
+ uintptr_t mmu_idx = get_mmuidx(oi);
+ uintptr_t index;
+ CPUTLBEntry *entry;
+ target_ulong tlb_addr;
void *haddr;
uint64_t res;
- size_t size = memop_size(op);
+
+ tcg_debug_assert(mmu_idx < NB_MMU_MODES);
/* Handle CPU specific unaligned behaviour */
if (addr & ((1 << a_bits) - 1)) {
@@ -1927,6 +1931,10 @@ load_helper(CPUArchState *env, target_ulong addr, MemOpIdx oi,
mmu_idx, retaddr);
}
+ index = tlb_index(env, mmu_idx, addr);
+ entry = tlb_entry(env, mmu_idx, addr);
+ tlb_addr = code_read ? entry->addr_code : entry->addr_read;
+
/* If the TLB entry is for a different page, reload and try again. */
if (!tlb_hit(tlb_addr, addr)) {
if (!victim_tlb_hit(env, mmu_idx, index, tlb_off,
@@ -2310,14 +2318,16 @@ static inline void QEMU_ALWAYS_INLINE
store_helper(CPUArchState *env, target_ulong addr, uint64_t val,
MemOpIdx oi, uintptr_t retaddr, MemOp op)
{
- uintptr_t mmu_idx = get_mmuidx(oi);
- uintptr_t index = tlb_index(env, mmu_idx, addr);
- CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
- target_ulong tlb_addr = tlb_addr_write(entry);
const size_t tlb_off = offsetof(CPUTLBEntry, addr_write);
- unsigned a_bits = get_alignment_bits(get_memop(oi));
+ const unsigned a_bits = get_alignment_bits(get_memop(oi));
+ const size_t size = memop_size(op);
+ uintptr_t mmu_idx = get_mmuidx(oi);
+ uintptr_t index;
+ CPUTLBEntry *entry;
+ target_ulong tlb_addr;
void *haddr;
- size_t size = memop_size(op);
+
+ tcg_debug_assert(mmu_idx < NB_MMU_MODES);
/* Handle CPU specific unaligned behaviour */
if (addr & ((1 << a_bits) - 1)) {
@@ -2325,6 +2335,10 @@ store_helper(CPUArchState *env, target_ulong addr, uint64_t val,
mmu_idx, retaddr);
}
+ index = tlb_index(env, mmu_idx, addr);
+ entry = tlb_entry(env, mmu_idx, addr);
+ tlb_addr = tlb_addr_write(entry);
+
/* If the TLB entry is for a different page, reload and try again. */
if (!tlb_hit(tlb_addr, addr)) {
if (!victim_tlb_hit(env, mmu_idx, index, tlb_off,
diff --git a/block/coroutines.h b/block/coroutines.h
index b293e943c8..8ea70d45f9 100644
--- a/block/coroutines.h
+++ b/block/coroutines.h
@@ -59,7 +59,8 @@ int coroutine_fn bdrv_co_writev_vmstate(BlockDriverState *bs,
QEMUIOVector *qiov, int64_t pos);
int coroutine_fn
-nbd_co_do_establish_connection(BlockDriverState *bs, Error **errp);
+nbd_co_do_establish_connection(BlockDriverState *bs, bool blocking,
+ Error **errp);
int coroutine_fn
@@ -109,7 +110,7 @@ bdrv_common_block_status_above(BlockDriverState *bs,
BlockDriverState **file,
int *depth);
int generated_co_wrapper
-nbd_do_establish_connection(BlockDriverState *bs, Error **errp);
+nbd_do_establish_connection(BlockDriverState *bs, bool blocking, Error **errp);
int generated_co_wrapper
blk_do_preadv(BlockBackend *blk, int64_t offset, int64_t bytes,
diff --git a/block/monitor/bitmap-qmp-cmds.c b/block/monitor/bitmap-qmp-cmds.c
index 8e35616c2e..2b677c4a2f 100644
--- a/block/monitor/bitmap-qmp-cmds.c
+++ b/block/monitor/bitmap-qmp-cmds.c
@@ -257,12 +257,12 @@ void qmp_block_dirty_bitmap_disable(const char *node, const char *name,
}
BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target,
- BlockDirtyBitmapMergeSourceList *bms,
+ BlockDirtyBitmapOrStrList *bms,
HBitmap **backup, Error **errp)
{
BlockDriverState *bs;
BdrvDirtyBitmap *dst, *src, *anon;
- BlockDirtyBitmapMergeSourceList *lst;
+ BlockDirtyBitmapOrStrList *lst;
GLOBAL_STATE_CODE();
@@ -317,7 +317,7 @@ BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target,
}
void qmp_block_dirty_bitmap_merge(const char *node, const char *target,
- BlockDirtyBitmapMergeSourceList *bitmaps,
+ BlockDirtyBitmapOrStrList *bitmaps,
Error **errp)
{
block_dirty_bitmap_merge(node, target, bitmaps, NULL, errp);
diff --git a/block/nbd.c b/block/nbd.c
index 567872ac53..6085ab1d2c 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -35,7 +35,6 @@
#include "qemu/option.h"
#include "qemu/cutils.h"
#include "qemu/main-loop.h"
-#include "qemu/atomic.h"
#include "qapi/qapi-visit-sockets.h"
#include "qapi/qmp/qstring.h"
@@ -58,7 +57,6 @@ typedef struct {
Coroutine *coroutine;
uint64_t offset; /* original offset of the request */
bool receiving; /* sleeping in the yield in nbd_receive_replies */
- bool reply_possible; /* reply header not yet received */
} NBDClientRequest;
typedef enum NBDClientState {
@@ -72,18 +70,29 @@ typedef struct BDRVNBDState {
QIOChannel *ioc; /* The current I/O channel */
NBDExportInfo info;
- CoMutex send_mutex;
+ /*
+ * Protects state, free_sema, in_flight, requests[].coroutine,
+ * reconnect_delay_timer.
+ */
+ QemuMutex requests_lock;
+ NBDClientState state;
CoQueue free_sema;
+ int in_flight;
+ NBDClientRequest requests[MAX_NBD_REQUESTS];
+ QEMUTimer *reconnect_delay_timer;
+
+ /* Protects sending data on the socket. */
+ CoMutex send_mutex;
+ /*
+ * Protects receiving reply headers from the socket, as well as the
+ * fields reply and requests[].receiving
+ */
CoMutex receive_mutex;
- int in_flight;
- NBDClientState state;
+ NBDReply reply;
- QEMUTimer *reconnect_delay_timer;
QEMUTimer *open_timer;
- NBDClientRequest requests[MAX_NBD_REQUESTS];
- NBDReply reply;
BlockDriverState *bs;
/* Connection parameters */
@@ -128,12 +137,8 @@ static void nbd_clear_bdrvstate(BlockDriverState *bs)
s->x_dirty_bitmap = NULL;
}
-static bool nbd_client_connected(BDRVNBDState *s)
-{
- return qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTED;
-}
-
-static bool nbd_recv_coroutine_wake_one(NBDClientRequest *req)
+/* Called with s->receive_mutex taken. */
+static bool coroutine_fn nbd_recv_coroutine_wake_one(NBDClientRequest *req)
{
if (req->receiving) {
req->receiving = false;
@@ -144,33 +149,39 @@ static bool nbd_recv_coroutine_wake_one(NBDClientRequest *req)
return false;
}
-static void nbd_recv_coroutines_wake(BDRVNBDState *s, bool all)
+static void coroutine_fn nbd_recv_coroutines_wake(BDRVNBDState *s)
{
int i;
+ QEMU_LOCK_GUARD(&s->receive_mutex);
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
- if (nbd_recv_coroutine_wake_one(&s->requests[i]) && !all) {
+ if (nbd_recv_coroutine_wake_one(&s->requests[i])) {
return;
}
}
}
-static void nbd_channel_error(BDRVNBDState *s, int ret)
+/* Called with s->requests_lock held. */
+static void coroutine_fn nbd_channel_error_locked(BDRVNBDState *s, int ret)
{
- if (nbd_client_connected(s)) {
+ if (s->state == NBD_CLIENT_CONNECTED) {
qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
}
if (ret == -EIO) {
- if (nbd_client_connected(s)) {
+ if (s->state == NBD_CLIENT_CONNECTED) {
s->state = s->reconnect_delay ? NBD_CLIENT_CONNECTING_WAIT :
NBD_CLIENT_CONNECTING_NOWAIT;
}
} else {
s->state = NBD_CLIENT_QUIT;
}
+}
- nbd_recv_coroutines_wake(s, true);
+static void coroutine_fn nbd_channel_error(BDRVNBDState *s, int ret)
+{
+ QEMU_LOCK_GUARD(&s->requests_lock);
+ nbd_channel_error_locked(s, ret);
}
static void reconnect_delay_timer_del(BDRVNBDState *s)
@@ -185,23 +196,18 @@ static void reconnect_delay_timer_cb(void *opaque)
{
BDRVNBDState *s = opaque;
- if (qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTING_WAIT) {
- s->state = NBD_CLIENT_CONNECTING_NOWAIT;
- nbd_co_establish_connection_cancel(s->conn);
- while (qemu_co_enter_next(&s->free_sema, NULL)) {
- /* Resume all queued requests */
+ reconnect_delay_timer_del(s);
+ WITH_QEMU_LOCK_GUARD(&s->requests_lock) {
+ if (s->state != NBD_CLIENT_CONNECTING_WAIT) {
+ return;
}
+ s->state = NBD_CLIENT_CONNECTING_NOWAIT;
}
-
- reconnect_delay_timer_del(s);
+ nbd_co_establish_connection_cancel(s->conn);
}
static void reconnect_delay_timer_init(BDRVNBDState *s, uint64_t expire_time_ns)
{
- if (qatomic_load_acquire(&s->state) != NBD_CLIENT_CONNECTING_WAIT) {
- return;
- }
-
assert(!s->reconnect_delay_timer);
s->reconnect_delay_timer = aio_timer_new(bdrv_get_aio_context(s->bs),
QEMU_CLOCK_REALTIME,
@@ -224,7 +230,9 @@ static void nbd_teardown_connection(BlockDriverState *bs)
s->ioc = NULL;
}
- s->state = NBD_CLIENT_QUIT;
+ WITH_QEMU_LOCK_GUARD(&s->requests_lock) {
+ s->state = NBD_CLIENT_QUIT;
+ }
}
static void open_timer_del(BDRVNBDState *s)
@@ -253,16 +261,13 @@ static void open_timer_init(BDRVNBDState *s, uint64_t expire_time_ns)
timer_mod(s->open_timer, expire_time_ns);
}
-static bool nbd_client_connecting(BDRVNBDState *s)
-{
- NBDClientState state = qatomic_load_acquire(&s->state);
- return state == NBD_CLIENT_CONNECTING_WAIT ||
- state == NBD_CLIENT_CONNECTING_NOWAIT;
-}
-
-static bool nbd_client_connecting_wait(BDRVNBDState *s)
+static bool nbd_client_will_reconnect(BDRVNBDState *s)
{
- return qatomic_load_acquire(&s->state) == NBD_CLIENT_CONNECTING_WAIT;
+ /*
+ * Called only after a socket error, so this is not performance sensitive.
+ */
+ QEMU_LOCK_GUARD(&s->requests_lock);
+ return s->state == NBD_CLIENT_CONNECTING_WAIT;
}
/*
@@ -311,11 +316,10 @@ static int nbd_handle_updated_info(BlockDriverState *bs, Error **errp)
}
int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs,
- Error **errp)
+ bool blocking, Error **errp)
{
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
int ret;
- bool blocking = nbd_client_connecting_wait(s);
IO_CODE();
assert(!s->ioc);
@@ -350,35 +354,43 @@ int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs,
qio_channel_attach_aio_context(s->ioc, bdrv_get_aio_context(bs));
/* successfully connected */
- s->state = NBD_CLIENT_CONNECTED;
- qemu_co_queue_restart_all(&s->free_sema);
+ WITH_QEMU_LOCK_GUARD(&s->requests_lock) {
+ s->state = NBD_CLIENT_CONNECTED;
+ }
return 0;
}
-/* called under s->send_mutex */
+/* Called with s->requests_lock held. */
+static bool nbd_client_connecting(BDRVNBDState *s)
+{
+ return s->state == NBD_CLIENT_CONNECTING_WAIT ||
+ s->state == NBD_CLIENT_CONNECTING_NOWAIT;
+}
+
+/* Called with s->requests_lock taken. */
static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s)
{
+ bool blocking = s->state == NBD_CLIENT_CONNECTING_WAIT;
+
+ /*
+ * Now we are sure that nobody is accessing the channel, and no one will
+ * try until we set the state to CONNECTED.
+ */
assert(nbd_client_connecting(s));
- assert(s->in_flight == 0);
+ assert(s->in_flight == 1);
- if (nbd_client_connecting_wait(s) && s->reconnect_delay &&
- !s->reconnect_delay_timer)
- {
+ if (blocking && !s->reconnect_delay_timer) {
/*
- * It's first reconnect attempt after switching to
+ * It's the first reconnect attempt after switching to
* NBD_CLIENT_CONNECTING_WAIT
*/
+ g_assert(s->reconnect_delay);
reconnect_delay_timer_init(s,
qemu_clock_get_ns(QEMU_CLOCK_REALTIME) +
s->reconnect_delay * NANOSECONDS_PER_SECOND);
}
- /*
- * Now we are sure that nobody is accessing the channel, and no one will
- * try until we set the state to CONNECTED.
- */
-
/* Finalize previous connection if any */
if (s->ioc) {
qio_channel_detach_aio_context(QIO_CHANNEL(s->ioc));
@@ -388,7 +400,9 @@ static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s)
s->ioc = NULL;
}
- nbd_co_do_establish_connection(s->bs, NULL);
+ qemu_mutex_unlock(&s->requests_lock);
+ nbd_co_do_establish_connection(s->bs, blocking, NULL);
+ qemu_mutex_lock(&s->requests_lock);
/*
* The reconnect attempt is done (maybe successfully, maybe not), so
@@ -410,10 +424,6 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t handle)
return 0;
}
- if (!nbd_client_connected(s)) {
- return -EIO;
- }
-
if (s->reply.handle != 0) {
/*
* Some other request is being handled now. It should already be
@@ -428,11 +438,10 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t handle)
qemu_coroutine_yield();
/*
- * We may be woken for 3 reasons:
+ * We may be woken for 2 reasons:
* 1. From this function, executing in parallel coroutine, when our
* handle is received.
- * 2. From nbd_channel_error(), when connection is lost.
- * 3. From nbd_co_receive_one_chunk(), when previous request is
+ * 2. From nbd_co_receive_one_chunk(), when previous request is
* finished and s->reply.handle set to 0.
* Anyway, it's OK to lock the mutex and go to the next iteration.
*/
@@ -454,44 +463,43 @@ static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t handle)
nbd_channel_error(s, -EINVAL);
return -EINVAL;
}
- if (s->reply.handle == handle) {
- /* We are done */
- return 0;
- }
ind2 = HANDLE_TO_INDEX(s, s->reply.handle);
- if (ind2 >= MAX_NBD_REQUESTS || !s->requests[ind2].reply_possible) {
+ if (ind2 >= MAX_NBD_REQUESTS || !s->requests[ind2].coroutine) {
nbd_channel_error(s, -EINVAL);
return -EINVAL;
}
+ if (s->reply.handle == handle) {
+ /* We are done */
+ return 0;
+ }
nbd_recv_coroutine_wake_one(&s->requests[ind2]);
}
}
-static int nbd_co_send_request(BlockDriverState *bs,
- NBDRequest *request,
- QEMUIOVector *qiov)
+static int coroutine_fn nbd_co_send_request(BlockDriverState *bs,
+ NBDRequest *request,
+ QEMUIOVector *qiov)
{
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
int rc, i = -1;
- qemu_co_mutex_lock(&s->send_mutex);
-
+ qemu_mutex_lock(&s->requests_lock);
while (s->in_flight == MAX_NBD_REQUESTS ||
- (!nbd_client_connected(s) && s->in_flight > 0))
- {
- qemu_co_queue_wait(&s->free_sema, &s->send_mutex);
- }
-
- if (nbd_client_connecting(s)) {
- nbd_reconnect_attempt(s);
- }
-
- if (!nbd_client_connected(s)) {
- rc = -EIO;
- goto err;
+ (s->state != NBD_CLIENT_CONNECTED && s->in_flight > 0)) {
+ qemu_co_queue_wait(&s->free_sema, &s->requests_lock);
}
s->in_flight++;
+ if (s->state != NBD_CLIENT_CONNECTED) {
+ if (nbd_client_connecting(s)) {
+ nbd_reconnect_attempt(s);
+ qemu_co_queue_restart_all(&s->free_sema);
+ }
+ if (s->state != NBD_CLIENT_CONNECTED) {
+ rc = -EIO;
+ goto err;
+ }
+ }
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
if (s->requests[i].coroutine == NULL) {
@@ -499,14 +507,13 @@ static int nbd_co_send_request(BlockDriverState *bs,
}
}
- g_assert(qemu_in_coroutine());
assert(i < MAX_NBD_REQUESTS);
-
s->requests[i].coroutine = qemu_coroutine_self();
s->requests[i].offset = request->from;
s->requests[i].receiving = false;
- s->requests[i].reply_possible = true;
+ qemu_mutex_unlock(&s->requests_lock);
+ qemu_co_mutex_lock(&s->send_mutex);
request->handle = INDEX_TO_HANDLE(s, i);
assert(s->ioc);
@@ -514,7 +521,7 @@ static int nbd_co_send_request(BlockDriverState *bs,
if (qiov) {
qio_channel_set_cork(s->ioc, true);
rc = nbd_send_request(s->ioc, request);
- if (nbd_client_connected(s) && rc >= 0) {
+ if (rc >= 0) {
if (qio_channel_writev_all(s->ioc, qiov->iov, qiov->niov,
NULL) < 0) {
rc = -EIO;
@@ -526,17 +533,19 @@ static int nbd_co_send_request(BlockDriverState *bs,
} else {
rc = nbd_send_request(s->ioc, request);
}
+ qemu_co_mutex_unlock(&s->send_mutex);
-err:
if (rc < 0) {
- nbd_channel_error(s, rc);
+ qemu_mutex_lock(&s->requests_lock);
+err:
+ nbd_channel_error_locked(s, rc);
if (i != -1) {
s->requests[i].coroutine = NULL;
- s->in_flight--;
}
+ s->in_flight--;
qemu_co_queue_next(&s->free_sema);
+ qemu_mutex_unlock(&s->requests_lock);
}
- qemu_co_mutex_unlock(&s->send_mutex);
return rc;
}
@@ -723,9 +732,9 @@ static int nbd_parse_error_payload(NBDStructuredReplyChunk *chunk,
return 0;
}
-static int nbd_co_receive_offset_data_payload(BDRVNBDState *s,
- uint64_t orig_offset,
- QEMUIOVector *qiov, Error **errp)
+static int coroutine_fn
+nbd_co_receive_offset_data_payload(BDRVNBDState *s, uint64_t orig_offset,
+ QEMUIOVector *qiov, Error **errp)
{
QEMUIOVector sub_qiov;
uint64_t offset;
@@ -831,8 +840,8 @@ static coroutine_fn int nbd_co_do_receive_one_chunk(
}
*request_ret = 0;
- nbd_receive_replies(s, handle);
- if (!nbd_client_connected(s)) {
+ ret = nbd_receive_replies(s, handle);
+ if (ret < 0) {
error_setg(errp, "Connection closed");
return -EIO;
}
@@ -924,7 +933,7 @@ static coroutine_fn int nbd_co_receive_one_chunk(
}
s->reply.handle = 0;
- nbd_recv_coroutines_wake(s, false);
+ nbd_recv_coroutines_wake(s);
return ret;
}
@@ -984,11 +993,6 @@ static bool nbd_reply_chunk_iter_receive(BDRVNBDState *s,
NBDReply local_reply;
NBDStructuredReplyChunk *chunk;
Error *local_err = NULL;
- if (!nbd_client_connected(s)) {
- error_setg(&local_err, "Connection closed");
- nbd_iter_channel_error(iter, -EIO, &local_err);
- goto break_loop;
- }
if (iter->done) {
/* Previous iteration was last. */
@@ -1009,7 +1013,7 @@ static bool nbd_reply_chunk_iter_receive(BDRVNBDState *s,
}
/* Do not execute the body of NBD_FOREACH_REPLY_CHUNK for simple reply. */
- if (nbd_reply_is_simple(reply) || !nbd_client_connected(s)) {
+ if (nbd_reply_is_simple(reply) || iter->ret < 0) {
goto break_loop;
}
@@ -1031,18 +1035,17 @@ static bool nbd_reply_chunk_iter_receive(BDRVNBDState *s,
return true;
break_loop:
+ qemu_mutex_lock(&s->requests_lock);
s->requests[HANDLE_TO_INDEX(s, handle)].coroutine = NULL;
-
- qemu_co_mutex_lock(&s->send_mutex);
s->in_flight--;
qemu_co_queue_next(&s->free_sema);
- qemu_co_mutex_unlock(&s->send_mutex);
+ qemu_mutex_unlock(&s->requests_lock);
return false;
}
-static int nbd_co_receive_return_code(BDRVNBDState *s, uint64_t handle,
- int *request_ret, Error **errp)
+static int coroutine_fn nbd_co_receive_return_code(BDRVNBDState *s, uint64_t handle,
+ int *request_ret, Error **errp)
{
NBDReplyChunkIter iter;
@@ -1055,9 +1058,9 @@ static int nbd_co_receive_return_code(BDRVNBDState *s, uint64_t handle,
return iter.ret;
}
-static int nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t handle,
- uint64_t offset, QEMUIOVector *qiov,
- int *request_ret, Error **errp)
+static int coroutine_fn nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t handle,
+ uint64_t offset, QEMUIOVector *qiov,
+ int *request_ret, Error **errp)
{
NBDReplyChunkIter iter;
NBDReply reply;
@@ -1107,10 +1110,10 @@ static int nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t handle,
return iter.ret;
}
-static int nbd_co_receive_blockstatus_reply(BDRVNBDState *s,
- uint64_t handle, uint64_t length,
- NBDExtent *extent,
- int *request_ret, Error **errp)
+static int coroutine_fn nbd_co_receive_blockstatus_reply(BDRVNBDState *s,
+ uint64_t handle, uint64_t length,
+ NBDExtent *extent,
+ int *request_ret, Error **errp)
{
NBDReplyChunkIter iter;
NBDReply reply;
@@ -1167,8 +1170,8 @@ static int nbd_co_receive_blockstatus_reply(BDRVNBDState *s,
return iter.ret;
}
-static int nbd_co_request(BlockDriverState *bs, NBDRequest *request,
- QEMUIOVector *write_qiov)
+static int coroutine_fn nbd_co_request(BlockDriverState *bs, NBDRequest *request,
+ QEMUIOVector *write_qiov)
{
int ret, request_ret;
Error *local_err = NULL;
@@ -1199,14 +1202,14 @@ static int nbd_co_request(BlockDriverState *bs, NBDRequest *request,
error_free(local_err);
local_err = NULL;
}
- } while (ret < 0 && nbd_client_connecting_wait(s));
+ } while (ret < 0 && nbd_client_will_reconnect(s));
return ret ? ret : request_ret;
}
-static int nbd_client_co_preadv(BlockDriverState *bs, int64_t offset,
- int64_t bytes, QEMUIOVector *qiov,
- BdrvRequestFlags flags)
+static int coroutine_fn nbd_client_co_preadv(BlockDriverState *bs, int64_t offset,
+ int64_t bytes, QEMUIOVector *qiov,
+ BdrvRequestFlags flags)
{
int ret, request_ret;
Error *local_err = NULL;
@@ -1258,14 +1261,14 @@ static int nbd_client_co_preadv(BlockDriverState *bs, int64_t offset,
error_free(local_err);
local_err = NULL;
}
- } while (ret < 0 && nbd_client_connecting_wait(s));
+ } while (ret < 0 && nbd_client_will_reconnect(s));
return ret ? ret : request_ret;
}
-static int nbd_client_co_pwritev(BlockDriverState *bs, int64_t offset,
- int64_t bytes, QEMUIOVector *qiov,
- BdrvRequestFlags flags)
+static int coroutine_fn nbd_client_co_pwritev(BlockDriverState *bs, int64_t offset,
+ int64_t bytes, QEMUIOVector *qiov,
+ BdrvRequestFlags flags)
{
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
NBDRequest request = {
@@ -1288,8 +1291,8 @@ static int nbd_client_co_pwritev(BlockDriverState *bs, int64_t offset,
return nbd_co_request(bs, &request, qiov);
}
-static int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset,
- int64_t bytes, BdrvRequestFlags flags)
+static int coroutine_fn nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset,
+ int64_t bytes, BdrvRequestFlags flags)
{
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
NBDRequest request = {
@@ -1323,7 +1326,7 @@ static int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset,
return nbd_co_request(bs, &request, NULL);
}
-static int nbd_client_co_flush(BlockDriverState *bs)
+static int coroutine_fn nbd_client_co_flush(BlockDriverState *bs)
{
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
NBDRequest request = { .type = NBD_CMD_FLUSH };
@@ -1338,8 +1341,8 @@ static int nbd_client_co_flush(BlockDriverState *bs)
return nbd_co_request(bs, &request, NULL);
}
-static int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset,
- int64_t bytes)
+static int coroutine_fn nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset,
+ int64_t bytes)
{
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
NBDRequest request = {
@@ -1416,7 +1419,7 @@ static int coroutine_fn nbd_client_co_block_status(
error_free(local_err);
local_err = NULL;
}
- } while (ret < 0 && nbd_client_connecting_wait(s));
+ } while (ret < 0 && nbd_client_will_reconnect(s));
if (ret < 0 || request_ret < 0) {
return ret ? ret : request_ret;
@@ -1448,8 +1451,9 @@ static void nbd_yank(void *opaque)
BlockDriverState *bs = opaque;
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
- qatomic_store_release(&s->state, NBD_CLIENT_QUIT);
+ QEMU_LOCK_GUARD(&s->requests_lock);
qio_channel_shutdown(QIO_CHANNEL(s->ioc), QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
+ s->state = NBD_CLIENT_QUIT;
}
static void nbd_client_close(BlockDriverState *bs)
@@ -1869,8 +1873,9 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
BDRVNBDState *s = (BDRVNBDState *)bs->opaque;
s->bs = bs;
- qemu_co_mutex_init(&s->send_mutex);
+ qemu_mutex_init(&s->requests_lock);
qemu_co_queue_init(&s->free_sema);
+ qemu_co_mutex_init(&s->send_mutex);
qemu_co_mutex_init(&s->receive_mutex);
if (!yank_register_instance(BLOCKDEV_YANK_INSTANCE(bs->node_name), errp)) {
@@ -1893,7 +1898,7 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
}
s->state = NBD_CLIENT_CONNECTING_WAIT;
- ret = nbd_do_establish_connection(bs, errp);
+ ret = nbd_do_establish_connection(bs, true, errp);
if (ret < 0) {
goto fail;
}
@@ -1915,7 +1920,7 @@ fail:
return ret;
}
-static int nbd_co_flush(BlockDriverState *bs)
+static int coroutine_fn nbd_co_flush(BlockDriverState *bs)
{
return nbd_client_co_flush(bs);
}
@@ -2057,10 +2062,11 @@ static void nbd_cancel_in_flight(BlockDriverState *bs)
reconnect_delay_timer_del(s);
+ qemu_mutex_lock(&s->requests_lock);
if (s->state == NBD_CLIENT_CONNECTING_WAIT) {
s->state = NBD_CLIENT_CONNECTING_NOWAIT;
- qemu_co_queue_restart_all(&s->free_sema);
}
+ qemu_mutex_unlock(&s->requests_lock);
nbd_co_establish_connection_cancel(s->conn);
}
diff --git a/block/qapi-sysemu.c b/block/qapi-sysemu.c
index 8498402ad4..680c7ee342 100644
--- a/block/qapi-sysemu.c
+++ b/block/qapi-sysemu.c
@@ -318,6 +318,7 @@ void qmp_blockdev_change_medium(bool has_device, const char *device,
bool has_id, const char *id,
const char *filename,
bool has_format, const char *format,
+ bool has_force, bool force,
bool has_read_only,
BlockdevChangeReadOnlyMode read_only,
Error **errp)
@@ -380,7 +381,7 @@ void qmp_blockdev_change_medium(bool has_device, const char *device,
rc = do_open_tray(has_device ? device : NULL,
has_id ? id : NULL,
- false, &err);
+ force, &err);
if (rc && rc != -ENOSYS) {
error_propagate(errp, err);
goto fail;
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index 9840d25a82..7f6531cba0 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -211,8 +211,14 @@ void qmp_nbd_server_add(NbdServerAddOptions *arg, Error **errp)
QAPI_CLONE_MEMBERS(BlockExportOptionsNbdBase, &export_opts->u.nbd,
qapi_NbdServerAddOptions_base(arg));
if (arg->has_bitmap) {
+ BlockDirtyBitmapOrStr *el = g_new(BlockDirtyBitmapOrStr, 1);
+
+ *el = (BlockDirtyBitmapOrStr) {
+ .type = QTYPE_QSTRING,
+ .u.local = g_strdup(arg->bitmap),
+ };
export_opts->u.nbd.has_bitmaps = true;
- QAPI_LIST_PREPEND(export_opts->u.nbd.bitmaps, g_strdup(arg->bitmap));
+ QAPI_LIST_PREPEND(export_opts->u.nbd.bitmaps, el);
}
/*
diff --git a/configs/targets/nios2-softmmu.mak b/configs/targets/nios2-softmmu.mak
index 9a372f0717..1e93b54cd1 100644
--- a/configs/targets/nios2-softmmu.mak
+++ b/configs/targets/nios2-softmmu.mak
@@ -1 +1,2 @@
TARGET_ARCH=nios2
+TARGET_ALIGNED_ONLY=y
diff --git a/contrib/elf2dmp/main.c b/contrib/elf2dmp/main.c
index 20b477d582..b9fc6d230c 100644
--- a/contrib/elf2dmp/main.c
+++ b/contrib/elf2dmp/main.c
@@ -141,10 +141,10 @@ static KDDEBUGGER_DATA64 *get_kdbg(uint64_t KernBase, struct pdb_reader *pdb,
return kdbg;
}
-static void win_context_init_from_qemu_cpu_state(WinContext *ctx,
+static void win_context_init_from_qemu_cpu_state(WinContext64 *ctx,
QEMUCPUState *s)
{
- WinContext win_ctx = (WinContext){
+ WinContext64 win_ctx = (WinContext64){
.ContextFlags = WIN_CTX_X64 | WIN_CTX_INT | WIN_CTX_SEG | WIN_CTX_CTL,
.MxCsr = INITIAL_MXCSR,
@@ -302,7 +302,7 @@ static int fill_context(KDDEBUGGER_DATA64 *kdbg,
for (i = 0; i < qe->state_nr; i++) {
uint64_t Prcb;
uint64_t Context;
- WinContext ctx;
+ WinContext64 ctx;
QEMUCPUState *s = qe->state[i];
if (va_space_rw(vs, kdbg->KiProcessorBlock + sizeof(Prcb) * i,
diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-blk/vhost-user-blk.c
index d14b2896bf..cd4a5d7335 100644
--- a/contrib/vhost-user-blk/vhost-user-blk.c
+++ b/contrib/vhost-user-blk/vhost-user-blk.c
@@ -593,7 +593,8 @@ static GOptionEntry entries[] = {
{"blk-file", 'b', 0, G_OPTION_ARG_FILENAME, &opt_blk_file,
"block device or file path", "PATH"},
{ "read-only", 'r', 0, G_OPTION_ARG_NONE, &opt_read_only,
- "Enable read-only", NULL }
+ "Enable read-only", NULL },
+ { NULL, },
};
int main(int argc, char **argv)
diff --git a/contrib/vhost-user-scsi/vhost-user-scsi.c b/contrib/vhost-user-scsi/vhost-user-scsi.c
index 4f6e3e2a24..b2c0f98253 100644
--- a/contrib/vhost-user-scsi/vhost-user-scsi.c
+++ b/contrib/vhost-user-scsi/vhost-user-scsi.c
@@ -351,34 +351,59 @@ fail:
/** vhost-user-scsi **/
+static int opt_fdnum = -1;
+static char *opt_socket_path;
+static gboolean opt_print_caps;
+static char *iscsi_uri;
+
+static GOptionEntry entries[] = {
+ { "print-capabilities", 'c', 0, G_OPTION_ARG_NONE, &opt_print_caps,
+ "Print capabilities", NULL },
+ { "fd", 'f', 0, G_OPTION_ARG_INT, &opt_fdnum,
+ "Use inherited fd socket", "FDNUM" },
+ { "iscsi-uri", 'i', 0, G_OPTION_ARG_FILENAME, &iscsi_uri,
+ "iSCSI URI to connect to", "FDNUM" },
+ { "socket-path", 's', 0, G_OPTION_ARG_FILENAME, &opt_socket_path,
+ "Use UNIX socket path", "PATH" },
+ { NULL, }
+};
+
int main(int argc, char **argv)
{
VusDev *vdev_scsi = NULL;
- char *unix_fn = NULL;
- char *iscsi_uri = NULL;
- int lsock = -1, csock = -1, opt, err = EXIT_SUCCESS;
-
- while ((opt = getopt(argc, argv, "u:i:")) != -1) {
- switch (opt) {
- case 'h':
- goto help;
- case 'u':
- unix_fn = g_strdup(optarg);
- break;
- case 'i':
- iscsi_uri = g_strdup(optarg);
- break;
- default:
- goto help;
- }
+ int lsock = -1, csock = -1, err = EXIT_SUCCESS;
+
+ GError *error = NULL;
+ GOptionContext *context;
+
+ context = g_option_context_new(NULL);
+ g_option_context_add_main_entries(context, entries, NULL);
+ if (!g_option_context_parse(context, &argc, &argv, &error)) {
+ g_printerr("Option parsing failed: %s\n", error->message);
+ exit(EXIT_FAILURE);
}
- if (!unix_fn || !iscsi_uri) {
+
+ if (opt_print_caps) {
+ g_print("{\n");
+ g_print(" \"type\": \"scsi\"\n");
+ g_print("}\n");
+ goto out;
+ }
+
+ if (!iscsi_uri) {
goto help;
}
- lsock = unix_sock_new(unix_fn);
- if (lsock < 0) {
- goto err;
+ if (opt_socket_path) {
+ lsock = unix_sock_new(opt_socket_path);
+ if (lsock < 0) {
+ exit(EXIT_FAILURE);
+ }
+ } else if (opt_fdnum < 0) {
+ g_print("%s\n", g_option_context_get_help(context, true, NULL));
+ exit(EXIT_FAILURE);
+ } else {
+ lsock = opt_fdnum;
}
csock = accept(lsock, NULL, NULL);
@@ -408,7 +433,7 @@ out:
if (vdev_scsi) {
g_main_loop_unref(vdev_scsi->loop);
g_free(vdev_scsi);
- unlink(unix_fn);
+ unlink(opt_socket_path);
}
if (csock >= 0) {
close(csock);
@@ -416,7 +441,7 @@ out:
if (lsock >= 0) {
close(lsock);
}
- g_free(unix_fn);
+ g_free(opt_socket_path);
g_free(iscsi_uri);
return err;
@@ -426,10 +451,12 @@ err:
goto out;
help:
- fprintf(stderr, "Usage: %s [ -u unix_sock_path -i iscsi_uri ] | [ -h ]\n",
+ fprintf(stderr, "Usage: %s [ -s socket-path -i iscsi-uri -f fd -p print-capabilities ] | [ -h ]\n",
argv[0]);
- fprintf(stderr, " -u path to unix socket\n");
- fprintf(stderr, " -i iscsi uri for lun 0\n");
+ fprintf(stderr, " -s, --socket-path=SOCKET_PATH path to unix socket\n");
+ fprintf(stderr, " -i, --iscsi-uri=ISCSI_URI iscsi uri for lun 0\n");
+ fprintf(stderr, " -f, --fd=FILE_DESCRIPTOR file-descriptor\n");
+ fprintf(stderr, " -p, --print-capabilities=PRINT_CAPABILITIES denotes print-capabilities\n");
fprintf(stderr, " -h print help and quit\n");
goto err;
diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst
index c29a4b8fe6..e9163ba556 100644
--- a/docs/about/build-platforms.rst
+++ b/docs/about/build-platforms.rst
@@ -80,7 +80,7 @@ Ubuntu LTS. Other distros will be assumed to ship similar software versions.
For FreeBSD and OpenBSD, decisions will be made based on the contents of the
respective ports repository, while NetBSD will use the pkgsrc repository.
-For macOS, `HomeBrew`_ will be used, although `MacPorts`_ is expected to carry
+For macOS, `Homebrew`_ will be used, although `MacPorts`_ is expected to carry
similar versions.
Windows
@@ -92,6 +92,6 @@ hosted on Linux (Debian/Fedora).
The version of the Windows API that's currently targeted is Vista / Server
2008.
-.. _HomeBrew: https://brew.sh/
+.. _Homebrew: https://brew.sh/
.. _MacPorts: https://www.macports.org/
.. _Repology: https://repology.org/
diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst
index cf02ef6821..257cc15f82 100644
--- a/docs/about/deprecated.rst
+++ b/docs/about/deprecated.rst
@@ -67,18 +67,6 @@ and will cause a warning.
The replacement for the ``nodelay`` short-form boolean option is ``nodelay=on``
rather than ``delay=off``.
-``--enable-fips`` (since 6.0)
-'''''''''''''''''''''''''''''
-
-This option restricts usage of certain cryptographic algorithms when
-the host is operating in FIPS mode.
-
-If FIPS compliance is required, QEMU should be built with the ``libgcrypt``
-library enabled as a cryptography provider.
-
-Neither the ``nettle`` library, or the built-in cryptography provider are
-supported on FIPS enabled hosts.
-
``-writeconfig`` (since 6.0)
'''''''''''''''''''''''''''''
diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst
index b367418ca7..7c37dc2bda 100644
--- a/docs/about/removed-features.rst
+++ b/docs/about/removed-features.rst
@@ -336,6 +336,17 @@ for the RISC-V ``virt`` machine and ``sifive_u`` machine.
The ``-no-quit`` was a synonym for ``-display ...,window-close=off`` which
should be used instead.
+``--enable-fips`` (removed in 7.1)
+''''''''''''''''''''''''''''''''''
+
+This option restricted usage of certain cryptographic algorithms when
+the host is operating in FIPS mode.
+
+If FIPS compliance is required, QEMU should be built with the ``libgcrypt``
+or ``gnutls`` library enabled as a cryptography provider.
+
+Neither the ``nettle`` library, or the built-in cryptography provider are
+supported on FIPS enabled hosts.
QEMU Machine Protocol (QMP) commands
------------------------------------
diff --git a/docs/pcie_sriov.txt b/docs/pcie_sriov.txt
index f5e891e1d4..11158dbf88 100644
--- a/docs/pcie_sriov.txt
+++ b/docs/pcie_sriov.txt
@@ -8,8 +8,8 @@ of a PCI Express device. It allows a single physical function (PF) to appear as
virtual functions (VFs) for the main purpose of eliminating software
overhead in I/O from virtual machines.
-Qemu now implements the basic common functionality to enable an emulated device
-to support SR/IOV. Yet no fully implemented devices exists in Qemu, but a
+QEMU now implements the basic common functionality to enable an emulated device
+to support SR/IOV. Yet no fully implemented devices exists in QEMU, but a
proof-of-concept hack of the Intel igb can be found here:
git://github.com/knuto/qemu.git sriov_patches_v5
@@ -18,7 +18,7 @@ Implementation
==============
Implementing emulation of an SR/IOV capable device typically consists of
implementing support for two types of device classes; the "normal" physical device
-(PF) and the virtual device (VF). From Qemu's perspective, the VFs are just
+(PF) and the virtual device (VF). From QEMU's perspective, the VFs are just
like other devices, except that some of their properties are derived from
the PF.
diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst
index 1297dff522..5fe045cbf0 100644
--- a/docs/system/arm/virt.rst
+++ b/docs/system/arm/virt.rst
@@ -99,11 +99,14 @@ gic-version
GICv2. Note that this limits the number of CPUs to 8.
``3``
GICv3. This allows up to 512 CPUs.
+ ``4``
+ GICv4. Requires ``virtualization`` to be ``on``; allows up to 317 CPUs.
``host``
Use the same GIC version the host provides, when using KVM
``max``
Use the best GIC version possible (same as host when using KVM;
- currently same as ``3``` for TCG, but this may change in future)
+ with TCG this is currently ``3`` if ``virtualization`` is ``off`` and
+ ``4`` if ``virtualization`` is ``on``, but this may change in future)
its
Set ``on``/``off`` to enable/disable ITS instantiation. The default is ``on``
diff --git a/docs/tools/virtiofsd.rst b/docs/tools/virtiofsd.rst
index 0c0560203c..e457b13d56 100644
--- a/docs/tools/virtiofsd.rst
+++ b/docs/tools/virtiofsd.rst
@@ -111,6 +111,11 @@ Options
label. Server will try to set that label on newly created file
atomically wherever possible.
+ * killpriv_v2|no_killpriv_v2 -
+ Enable/disable ``FUSE_HANDLE_KILLPRIV_V2`` support. KILLPRIV_V2 is enabled
+ by default as long as the client supports it. Enabling this option helps
+ with performance in write path.
+
.. option:: --socket-path=PATH
Listen on vhost-user UNIX domain socket at PATH.
diff --git a/dump/dump.c b/dump/dump.c
index e766ce1d7d..4d9658ffa2 100644
--- a/dump/dump.c
+++ b/dump/dump.c
@@ -54,6 +54,11 @@ static Error *dump_migration_blocker;
DIV_ROUND_UP((name_size), 4) + \
DIV_ROUND_UP((desc_size), 4)) * 4)
+static inline bool dump_is_64bit(DumpState *s)
+{
+ return s->dump_info.d_class == ELFCLASS64;
+}
+
uint16_t cpu_to_dump16(DumpState *s, uint16_t val)
{
if (s->dump_info.d_endian == ELFDATA2LSB) {
@@ -123,6 +128,12 @@ static int fd_write_vmcore(const void *buf, size_t size, void *opaque)
static void write_elf64_header(DumpState *s, Error **errp)
{
+ /*
+ * phnum in the elf header is 16 bit, if we have more segments we
+ * set phnum to PN_XNUM and write the real number of segments to a
+ * special section.
+ */
+ uint16_t phnum = MIN(s->phdr_num, PN_XNUM);
Elf64_Ehdr elf_header;
int ret;
@@ -135,15 +146,13 @@ static void write_elf64_header(DumpState *s, Error **errp)
elf_header.e_machine = cpu_to_dump16(s, s->dump_info.d_machine);
elf_header.e_version = cpu_to_dump32(s, EV_CURRENT);
elf_header.e_ehsize = cpu_to_dump16(s, sizeof(elf_header));
- elf_header.e_phoff = cpu_to_dump64(s, sizeof(Elf64_Ehdr));
+ elf_header.e_phoff = cpu_to_dump64(s, s->phdr_offset);
elf_header.e_phentsize = cpu_to_dump16(s, sizeof(Elf64_Phdr));
- elf_header.e_phnum = cpu_to_dump16(s, s->phdr_num);
- if (s->have_section) {
- uint64_t shoff = sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr) * s->sh_info;
-
- elf_header.e_shoff = cpu_to_dump64(s, shoff);
+ elf_header.e_phnum = cpu_to_dump16(s, phnum);
+ if (s->shdr_num) {
+ elf_header.e_shoff = cpu_to_dump64(s, s->shdr_offset);
elf_header.e_shentsize = cpu_to_dump16(s, sizeof(Elf64_Shdr));
- elf_header.e_shnum = cpu_to_dump16(s, 1);
+ elf_header.e_shnum = cpu_to_dump16(s, s->shdr_num);
}
ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s);
@@ -154,6 +163,12 @@ static void write_elf64_header(DumpState *s, Error **errp)
static void write_elf32_header(DumpState *s, Error **errp)
{
+ /*
+ * phnum in the elf header is 16 bit, if we have more segments we
+ * set phnum to PN_XNUM and write the real number of segments to a
+ * special section.
+ */
+ uint16_t phnum = MIN(s->phdr_num, PN_XNUM);
Elf32_Ehdr elf_header;
int ret;
@@ -166,15 +181,13 @@ static void write_elf32_header(DumpState *s, Error **errp)
elf_header.e_machine = cpu_to_dump16(s, s->dump_info.d_machine);
elf_header.e_version = cpu_to_dump32(s, EV_CURRENT);
elf_header.e_ehsize = cpu_to_dump16(s, sizeof(elf_header));
- elf_header.e_phoff = cpu_to_dump32(s, sizeof(Elf32_Ehdr));
+ elf_header.e_phoff = cpu_to_dump32(s, s->phdr_offset);
elf_header.e_phentsize = cpu_to_dump16(s, sizeof(Elf32_Phdr));
- elf_header.e_phnum = cpu_to_dump16(s, s->phdr_num);
- if (s->have_section) {
- uint32_t shoff = sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr) * s->sh_info;
-
- elf_header.e_shoff = cpu_to_dump32(s, shoff);
+ elf_header.e_phnum = cpu_to_dump16(s, phnum);
+ if (s->shdr_num) {
+ elf_header.e_shoff = cpu_to_dump32(s, s->shdr_offset);
elf_header.e_shentsize = cpu_to_dump16(s, sizeof(Elf32_Shdr));
- elf_header.e_shnum = cpu_to_dump16(s, 1);
+ elf_header.e_shnum = cpu_to_dump16(s, s->shdr_num);
}
ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s);
@@ -232,25 +245,15 @@ static void write_elf32_load(DumpState *s, MemoryMapping *memory_mapping,
}
}
-static void write_elf64_note(DumpState *s, Error **errp)
+static void write_elf64_phdr_note(DumpState *s, Elf64_Phdr *phdr)
{
- Elf64_Phdr phdr;
- hwaddr begin = s->memory_offset - s->note_size;
- int ret;
-
- memset(&phdr, 0, sizeof(Elf64_Phdr));
- phdr.p_type = cpu_to_dump32(s, PT_NOTE);
- phdr.p_offset = cpu_to_dump64(s, begin);
- phdr.p_paddr = 0;
- phdr.p_filesz = cpu_to_dump64(s, s->note_size);
- phdr.p_memsz = cpu_to_dump64(s, s->note_size);
- phdr.p_vaddr = 0;
-
- ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s);
- if (ret < 0) {
- error_setg_errno(errp, -ret,
- "dump: failed to write program header table");
- }
+ memset(phdr, 0, sizeof(*phdr));
+ phdr->p_type = cpu_to_dump32(s, PT_NOTE);
+ phdr->p_offset = cpu_to_dump64(s, s->note_offset);
+ phdr->p_paddr = 0;
+ phdr->p_filesz = cpu_to_dump64(s, s->note_size);
+ phdr->p_memsz = cpu_to_dump64(s, s->note_size);
+ phdr->p_vaddr = 0;
}
static inline int cpu_index(CPUState *cpu)
@@ -298,25 +301,15 @@ static void write_elf64_notes(WriteCoreDumpFunction f, DumpState *s,
write_guest_note(f, s, errp);
}
-static void write_elf32_note(DumpState *s, Error **errp)
+static void write_elf32_phdr_note(DumpState *s, Elf32_Phdr *phdr)
{
- hwaddr begin = s->memory_offset - s->note_size;
- Elf32_Phdr phdr;
- int ret;
-
- memset(&phdr, 0, sizeof(Elf32_Phdr));
- phdr.p_type = cpu_to_dump32(s, PT_NOTE);
- phdr.p_offset = cpu_to_dump32(s, begin);
- phdr.p_paddr = 0;
- phdr.p_filesz = cpu_to_dump32(s, s->note_size);
- phdr.p_memsz = cpu_to_dump32(s, s->note_size);
- phdr.p_vaddr = 0;
-
- ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s);
- if (ret < 0) {
- error_setg_errno(errp, -ret,
- "dump: failed to write program header table");
- }
+ memset(phdr, 0, sizeof(*phdr));
+ phdr->p_type = cpu_to_dump32(s, PT_NOTE);
+ phdr->p_offset = cpu_to_dump32(s, s->note_offset);
+ phdr->p_paddr = 0;
+ phdr->p_filesz = cpu_to_dump32(s, s->note_size);
+ phdr->p_memsz = cpu_to_dump32(s, s->note_size);
+ phdr->p_vaddr = 0;
}
static void write_elf32_notes(WriteCoreDumpFunction f, DumpState *s,
@@ -346,6 +339,32 @@ static void write_elf32_notes(WriteCoreDumpFunction f, DumpState *s,
write_guest_note(f, s, errp);
}
+static void write_elf_phdr_note(DumpState *s, Error **errp)
+{
+ ERRP_GUARD();
+ Elf32_Phdr phdr32;
+ Elf64_Phdr phdr64;
+ void *phdr;
+ size_t size;
+ int ret;
+
+ if (dump_is_64bit(s)) {
+ write_elf64_phdr_note(s, &phdr64);
+ size = sizeof(phdr64);
+ phdr = &phdr64;
+ } else {
+ write_elf32_phdr_note(s, &phdr32);
+ size = sizeof(phdr32);
+ phdr = &phdr32;
+ }
+
+ ret = fd_write_vmcore(phdr, size, s);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret,
+ "dump: failed to write program header table");
+ }
+}
+
static void write_elf_section(DumpState *s, int type, Error **errp)
{
Elf32_Shdr shdr32;
@@ -357,12 +376,12 @@ static void write_elf_section(DumpState *s, int type, Error **errp)
if (type == 0) {
shdr_size = sizeof(Elf32_Shdr);
memset(&shdr32, 0, shdr_size);
- shdr32.sh_info = cpu_to_dump32(s, s->sh_info);
+ shdr32.sh_info = cpu_to_dump32(s, s->phdr_num);
shdr = &shdr32;
} else {
shdr_size = sizeof(Elf64_Shdr);
memset(&shdr64, 0, shdr_size);
- shdr64.sh_info = cpu_to_dump32(s, s->sh_info);
+ shdr64.sh_info = cpu_to_dump32(s, s->phdr_num);
shdr = &shdr64;
}
@@ -389,23 +408,21 @@ static void write_data(DumpState *s, void *buf, int length, Error **errp)
static void write_memory(DumpState *s, GuestPhysBlock *block, ram_addr_t start,
int64_t size, Error **errp)
{
+ ERRP_GUARD();
int64_t i;
- Error *local_err = NULL;
for (i = 0; i < size / s->dump_info.page_size; i++) {
write_data(s, block->host_addr + start + i * s->dump_info.page_size,
- s->dump_info.page_size, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ s->dump_info.page_size, errp);
+ if (*errp) {
return;
}
}
if ((size % s->dump_info.page_size) != 0) {
write_data(s, block->host_addr + start + i * s->dump_info.page_size,
- size % s->dump_info.page_size, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ size % s->dump_info.page_size, errp);
+ if (*errp) {
return;
}
}
@@ -475,45 +492,46 @@ static void get_offset_range(hwaddr phys_addr,
static void write_elf_loads(DumpState *s, Error **errp)
{
+ ERRP_GUARD();
hwaddr offset, filesz;
MemoryMapping *memory_mapping;
uint32_t phdr_index = 1;
- uint32_t max_index;
- Error *local_err = NULL;
-
- if (s->have_section) {
- max_index = s->sh_info;
- } else {
- max_index = s->phdr_num;
- }
QTAILQ_FOREACH(memory_mapping, &s->list.head, next) {
get_offset_range(memory_mapping->phys_addr,
memory_mapping->length,
s, &offset, &filesz);
- if (s->dump_info.d_class == ELFCLASS64) {
+ if (dump_is_64bit(s)) {
write_elf64_load(s, memory_mapping, phdr_index++, offset,
- filesz, &local_err);
+ filesz, errp);
} else {
write_elf32_load(s, memory_mapping, phdr_index++, offset,
- filesz, &local_err);
+ filesz, errp);
}
- if (local_err) {
- error_propagate(errp, local_err);
+ if (*errp) {
return;
}
- if (phdr_index >= max_index) {
+ if (phdr_index >= s->phdr_num) {
break;
}
}
}
+static void write_elf_notes(DumpState *s, Error **errp)
+{
+ if (dump_is_64bit(s)) {
+ write_elf64_notes(fd_write_vmcore, s, errp);
+ } else {
+ write_elf32_notes(fd_write_vmcore, s, errp);
+ }
+}
+
/* write elf header, PT_NOTE and elf note to vmcore. */
static void dump_begin(DumpState *s, Error **errp)
{
- Error *local_err = NULL;
+ ERRP_GUARD();
/*
* the vmcore's format is:
@@ -540,77 +558,37 @@ static void dump_begin(DumpState *s, Error **errp)
*/
/* write elf header to vmcore */
- if (s->dump_info.d_class == ELFCLASS64) {
- write_elf64_header(s, &local_err);
+ if (dump_is_64bit(s)) {
+ write_elf64_header(s, errp);
} else {
- write_elf32_header(s, &local_err);
+ write_elf32_header(s, errp);
}
- if (local_err) {
- error_propagate(errp, local_err);
+ if (*errp) {
return;
}
- if (s->dump_info.d_class == ELFCLASS64) {
- /* write PT_NOTE to vmcore */
- write_elf64_note(s, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
-
- /* write all PT_LOAD to vmcore */
- write_elf_loads(s, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
-
- /* write section to vmcore */
- if (s->have_section) {
- write_elf_section(s, 1, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
- }
-
- /* write notes to vmcore */
- write_elf64_notes(fd_write_vmcore, s, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
- } else {
- /* write PT_NOTE to vmcore */
- write_elf32_note(s, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
-
- /* write all PT_LOAD to vmcore */
- write_elf_loads(s, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
+ /* write PT_NOTE to vmcore */
+ write_elf_phdr_note(s, errp);
+ if (*errp) {
+ return;
+ }
- /* write section to vmcore */
- if (s->have_section) {
- write_elf_section(s, 0, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
- }
+ /* write all PT_LOAD to vmcore */
+ write_elf_loads(s, errp);
+ if (*errp) {
+ return;
+ }
- /* write notes to vmcore */
- write_elf32_notes(fd_write_vmcore, s, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ /* write section to vmcore */
+ if (s->shdr_num) {
+ write_elf_section(s, 1, errp);
+ if (*errp) {
return;
}
}
+
+ /* write notes to vmcore */
+ write_elf_notes(s, errp);
}
static int get_next_block(DumpState *s, GuestPhysBlock *block)
@@ -643,9 +621,9 @@ static int get_next_block(DumpState *s, GuestPhysBlock *block)
/* write all memory to vmcore */
static void dump_iterate(DumpState *s, Error **errp)
{
+ ERRP_GUARD();
GuestPhysBlock *block;
int64_t size;
- Error *local_err = NULL;
do {
block = s->next_block;
@@ -657,9 +635,8 @@ static void dump_iterate(DumpState *s, Error **errp)
size -= block->target_end - (s->begin + s->length);
}
}
- write_memory(s, block, s->start, size, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ write_memory(s, block, s->start, size, errp);
+ if (*errp) {
return;
}
@@ -668,11 +645,10 @@ static void dump_iterate(DumpState *s, Error **errp)
static void create_vmcore(DumpState *s, Error **errp)
{
- Error *local_err = NULL;
+ ERRP_GUARD();
- dump_begin(s, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ dump_begin(s, errp);
+ if (*errp) {
return;
}
@@ -771,7 +747,7 @@ static void get_note_sizes(DumpState *s, const void *note,
uint64_t name_sz;
uint64_t desc_sz;
- if (s->dump_info.d_class == ELFCLASS64) {
+ if (dump_is_64bit(s)) {
const Elf64_Nhdr *hdr = note;
note_head_sz = sizeof(Elf64_Nhdr);
name_sz = tswap64(hdr->n_namesz);
@@ -809,6 +785,7 @@ static bool note_name_equal(DumpState *s,
/* write common header, sub header and elf note to vmcore */
static void create_header32(DumpState *s, Error **errp)
{
+ ERRP_GUARD();
DiskDumpHeader32 *dh = NULL;
KdumpSubHeader32 *kh = NULL;
size_t size;
@@ -817,7 +794,6 @@ static void create_header32(DumpState *s, Error **errp)
uint32_t bitmap_blocks;
uint32_t status = 0;
uint64_t offset_note;
- Error *local_err = NULL;
/* write common header, the version of kdump-compressed format is 6th */
size = sizeof(DiskDumpHeader32);
@@ -893,9 +869,8 @@ static void create_header32(DumpState *s, Error **errp)
s->note_buf_offset = 0;
/* use s->note_buf to store notes temporarily */
- write_elf32_notes(buf_write_note, s, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ write_elf32_notes(buf_write_note, s, errp);
+ if (*errp) {
goto out;
}
if (write_buffer(s->fd, offset_note, s->note_buf,
@@ -921,6 +896,7 @@ out:
/* write common header, sub header and elf note to vmcore */
static void create_header64(DumpState *s, Error **errp)
{
+ ERRP_GUARD();
DiskDumpHeader64 *dh = NULL;
KdumpSubHeader64 *kh = NULL;
size_t size;
@@ -929,7 +905,6 @@ static void create_header64(DumpState *s, Error **errp)
uint32_t bitmap_blocks;
uint32_t status = 0;
uint64_t offset_note;
- Error *local_err = NULL;
/* write common header, the version of kdump-compressed format is 6th */
size = sizeof(DiskDumpHeader64);
@@ -1005,9 +980,8 @@ static void create_header64(DumpState *s, Error **errp)
s->note_buf_offset = 0;
/* use s->note_buf to store notes temporarily */
- write_elf64_notes(buf_write_note, s, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ write_elf64_notes(buf_write_note, s, errp);
+ if (*errp) {
goto out;
}
@@ -1033,10 +1007,10 @@ out:
static void write_dump_header(DumpState *s, Error **errp)
{
- if (s->dump_info.d_class == ELFCLASS32) {
- create_header32(s, errp);
- } else {
+ if (dump_is_64bit(s)) {
create_header64(s, errp);
+ } else {
+ create_header32(s, errp);
}
}
@@ -1463,8 +1437,8 @@ out:
static void create_kdump_vmcore(DumpState *s, Error **errp)
{
+ ERRP_GUARD();
int ret;
- Error *local_err = NULL;
/*
* the kdump-compressed format is:
@@ -1494,21 +1468,18 @@ static void create_kdump_vmcore(DumpState *s, Error **errp)
return;
}
- write_dump_header(s, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ write_dump_header(s, errp);
+ if (*errp) {
return;
}
- write_dump_bitmap(s, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ write_dump_bitmap(s, errp);
+ if (*errp) {
return;
}
- write_dump_pages(s, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ write_dump_pages(s, errp);
+ if (*errp) {
return;
}
@@ -1638,10 +1609,10 @@ static void dump_init(DumpState *s, int fd, bool has_format,
DumpGuestMemoryFormat format, bool paging, bool has_filter,
int64_t begin, int64_t length, Error **errp)
{
+ ERRP_GUARD();
VMCoreInfoState *vmci = vmcoreinfo_find();
CPUState *cpu;
int nr_cpus;
- Error *err = NULL;
int ret;
s->has_format = has_format;
@@ -1726,8 +1697,8 @@ static void dump_init(DumpState *s, int fd, bool has_format,
uint32_t size;
uint16_t format;
- note_head_size = s->dump_info.d_class == ELFCLASS32 ?
- sizeof(Elf32_Nhdr) : sizeof(Elf64_Nhdr);
+ note_head_size = dump_is_64bit(s) ?
+ sizeof(Elf64_Nhdr) : sizeof(Elf32_Nhdr);
format = le16_to_cpu(vmci->vmcoreinfo.guest_format);
size = le32_to_cpu(vmci->vmcoreinfo.size);
@@ -1760,9 +1731,8 @@ static void dump_init(DumpState *s, int fd, bool has_format,
/* get memory mapping */
if (paging) {
- qemu_get_guest_memory_mapping(&s->list, &s->guest_phys_blocks, &err);
- if (err != NULL) {
- error_propagate(errp, err);
+ qemu_get_guest_memory_mapping(&s->list, &s->guest_phys_blocks, errp);
+ if (*errp) {
goto cleanup;
}
} else {
@@ -1817,39 +1787,31 @@ static void dump_init(DumpState *s, int fd, bool has_format,
*/
s->phdr_num = 1; /* PT_NOTE */
if (s->list.num < UINT16_MAX - 2) {
+ s->shdr_num = 0;
s->phdr_num += s->list.num;
- s->have_section = false;
} else {
- s->have_section = true;
- s->phdr_num = PN_XNUM;
- s->sh_info = 1; /* PT_NOTE */
+ /* sh_info of section 0 holds the real number of phdrs */
+ s->shdr_num = 1;
/* the type of shdr->sh_info is uint32_t, so we should avoid overflow */
if (s->list.num <= UINT32_MAX - 1) {
- s->sh_info += s->list.num;
+ s->phdr_num += s->list.num;
} else {
- s->sh_info = UINT32_MAX;
+ s->phdr_num = UINT32_MAX;
}
}
- if (s->dump_info.d_class == ELFCLASS64) {
- if (s->have_section) {
- s->memory_offset = sizeof(Elf64_Ehdr) +
- sizeof(Elf64_Phdr) * s->sh_info +
- sizeof(Elf64_Shdr) + s->note_size;
- } else {
- s->memory_offset = sizeof(Elf64_Ehdr) +
- sizeof(Elf64_Phdr) * s->phdr_num + s->note_size;
- }
+ if (dump_is_64bit(s)) {
+ s->phdr_offset = sizeof(Elf64_Ehdr);
+ s->shdr_offset = s->phdr_offset + sizeof(Elf64_Phdr) * s->phdr_num;
+ s->note_offset = s->shdr_offset + sizeof(Elf64_Shdr) * s->shdr_num;
+ s->memory_offset = s->note_offset + s->note_size;
} else {
- if (s->have_section) {
- s->memory_offset = sizeof(Elf32_Ehdr) +
- sizeof(Elf32_Phdr) * s->sh_info +
- sizeof(Elf32_Shdr) + s->note_size;
- } else {
- s->memory_offset = sizeof(Elf32_Ehdr) +
- sizeof(Elf32_Phdr) * s->phdr_num + s->note_size;
- }
+
+ s->phdr_offset = sizeof(Elf32_Ehdr);
+ s->shdr_offset = s->phdr_offset + sizeof(Elf32_Phdr) * s->phdr_num;
+ s->note_offset = s->shdr_offset + sizeof(Elf32_Shdr) * s->shdr_num;
+ s->memory_offset = s->note_offset + s->note_size;
}
return;
@@ -1861,33 +1823,32 @@ cleanup:
/* this operation might be time consuming. */
static void dump_process(DumpState *s, Error **errp)
{
- Error *local_err = NULL;
+ ERRP_GUARD();
DumpQueryResult *result = NULL;
if (s->has_format && s->format == DUMP_GUEST_MEMORY_FORMAT_WIN_DMP) {
#ifdef TARGET_X86_64
- create_win_dump(s, &local_err);
+ create_win_dump(s, errp);
#endif
} else if (s->has_format && s->format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
- create_kdump_vmcore(s, &local_err);
+ create_kdump_vmcore(s, errp);
} else {
- create_vmcore(s, &local_err);
+ create_vmcore(s, errp);
}
/* make sure status is written after written_size updates */
smp_wmb();
qatomic_set(&s->status,
- (local_err ? DUMP_STATUS_FAILED : DUMP_STATUS_COMPLETED));
+ (*errp ? DUMP_STATUS_FAILED : DUMP_STATUS_COMPLETED));
/* send DUMP_COMPLETED message (unconditionally) */
result = qmp_query_dump(NULL);
/* should never fail */
assert(result);
- qapi_event_send_dump_completed(result, !!local_err, (local_err ?
- error_get_pretty(local_err) : NULL));
+ qapi_event_send_dump_completed(result, !!*errp, (*errp ?
+ error_get_pretty(*errp) : NULL));
qapi_free_DumpQueryResult(result);
- error_propagate(errp, local_err);
dump_cleanup(s);
}
@@ -1916,10 +1877,10 @@ void qmp_dump_guest_memory(bool paging, const char *file,
int64_t length, bool has_format,
DumpGuestMemoryFormat format, Error **errp)
{
+ ERRP_GUARD();
const char *p;
int fd = -1;
DumpState *s;
- Error *local_err = NULL;
bool detach_p = false;
if (runstate_check(RUN_STATE_INMIGRATE)) {
@@ -2019,9 +1980,8 @@ void qmp_dump_guest_memory(bool paging, const char *file,
dump_state_prepare(s);
dump_init(s, fd, has_format, format, paging, has_begin,
- begin, length, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ begin, length, errp);
+ if (*errp) {
qatomic_set(&s->status, DUMP_STATUS_FAILED);
return;
}
diff --git a/dump/win_dump.c b/dump/win_dump.c
index fbdbb7bd93..fd91350fbb 100644
--- a/dump/win_dump.c
+++ b/dump/win_dump.c
@@ -23,11 +23,31 @@
#include "hw/misc/vmcoreinfo.h"
#include "win_dump.h"
-static size_t write_run(WinDumpPhyMemRun64 *run, int fd, Error **errp)
+static size_t win_dump_ptr_size(bool x64)
+{
+ return x64 ? sizeof(uint64_t) : sizeof(uint32_t);
+}
+
+#define _WIN_DUMP_FIELD(f) (x64 ? h->x64.f : h->x32.f)
+#define WIN_DUMP_FIELD(field) _WIN_DUMP_FIELD(field)
+
+#define _WIN_DUMP_FIELD_PTR(f) (x64 ? (void *)&h->x64.f : (void *)&h->x32.f)
+#define WIN_DUMP_FIELD_PTR(field) _WIN_DUMP_FIELD_PTR(field)
+
+#define _WIN_DUMP_FIELD_SIZE(f) (x64 ? sizeof(h->x64.f) : sizeof(h->x32.f))
+#define WIN_DUMP_FIELD_SIZE(field) _WIN_DUMP_FIELD_SIZE(field)
+
+static size_t win_dump_ctx_size(bool x64)
+{
+ return x64 ? sizeof(WinContext64) : sizeof(WinContext32);
+}
+
+static size_t write_run(uint64_t base_page, uint64_t page_count,
+ int fd, Error **errp)
{
void *buf;
- uint64_t addr = run->BasePage << TARGET_PAGE_BITS;
- uint64_t size = run->PageCount << TARGET_PAGE_BITS;
+ uint64_t addr = base_page << TARGET_PAGE_BITS;
+ uint64_t size = page_count << TARGET_PAGE_BITS;
uint64_t len, l;
size_t total = 0;
@@ -56,15 +76,16 @@ static size_t write_run(WinDumpPhyMemRun64 *run, int fd, Error **errp)
return total;
}
-static void write_runs(DumpState *s, WinDumpHeader64 *h, Error **errp)
+static void write_runs(DumpState *s, WinDumpHeader *h, bool x64, Error **errp)
{
- WinDumpPhyMemDesc64 *desc = &h->PhysicalMemoryBlock;
- WinDumpPhyMemRun64 *run = desc->Run;
+ uint64_t BasePage, PageCount;
Error *local_err = NULL;
int i;
- for (i = 0; i < desc->NumberOfRuns; i++) {
- s->written_size += write_run(run + i, s->fd, &local_err);
+ for (i = 0; i < WIN_DUMP_FIELD(PhysicalMemoryBlock.NumberOfRuns); i++) {
+ BasePage = WIN_DUMP_FIELD(PhysicalMemoryBlock.Run[i].BasePage);
+ PageCount = WIN_DUMP_FIELD(PhysicalMemoryBlock.Run[i].PageCount);
+ s->written_size += write_run(BasePage, PageCount, s->fd, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -72,30 +93,45 @@ static void write_runs(DumpState *s, WinDumpHeader64 *h, Error **errp)
}
}
-static void patch_mm_pfn_database(WinDumpHeader64 *h, Error **errp)
+static int cpu_read_ptr(bool x64, CPUState *cpu, uint64_t addr, uint64_t *ptr)
+{
+ int ret;
+ uint32_t ptr32;
+ uint64_t ptr64;
+
+ ret = cpu_memory_rw_debug(cpu, addr, x64 ? (void *)&ptr64 : (void *)&ptr32,
+ win_dump_ptr_size(x64), 0);
+
+ *ptr = x64 ? ptr64 : ptr32;
+
+ return ret;
+}
+
+static void patch_mm_pfn_database(WinDumpHeader *h, bool x64, Error **errp)
{
if (cpu_memory_rw_debug(first_cpu,
- h->KdDebuggerDataBlock + KDBG_MM_PFN_DATABASE_OFFSET64,
- (uint8_t *)&h->PfnDatabase, sizeof(h->PfnDatabase), 0)) {
+ WIN_DUMP_FIELD(KdDebuggerDataBlock) + KDBG_MM_PFN_DATABASE_OFFSET,
+ WIN_DUMP_FIELD_PTR(PfnDatabase),
+ WIN_DUMP_FIELD_SIZE(PfnDatabase), 0)) {
error_setg(errp, "win-dump: failed to read MmPfnDatabase");
return;
}
}
-static void patch_bugcheck_data(WinDumpHeader64 *h, Error **errp)
+static void patch_bugcheck_data(WinDumpHeader *h, bool x64, Error **errp)
{
uint64_t KiBugcheckData;
- if (cpu_memory_rw_debug(first_cpu,
- h->KdDebuggerDataBlock + KDBG_KI_BUGCHECK_DATA_OFFSET64,
- (uint8_t *)&KiBugcheckData, sizeof(KiBugcheckData), 0)) {
+ if (cpu_read_ptr(x64, first_cpu,
+ WIN_DUMP_FIELD(KdDebuggerDataBlock) + KDBG_KI_BUGCHECK_DATA_OFFSET,
+ &KiBugcheckData)) {
error_setg(errp, "win-dump: failed to read KiBugcheckData");
return;
}
- if (cpu_memory_rw_debug(first_cpu,
- KiBugcheckData,
- h->BugcheckData, sizeof(h->BugcheckData), 0)) {
+ if (cpu_memory_rw_debug(first_cpu, KiBugcheckData,
+ WIN_DUMP_FIELD(BugcheckData),
+ WIN_DUMP_FIELD_SIZE(BugcheckData), 0)) {
error_setg(errp, "win-dump: failed to read bugcheck data");
return;
}
@@ -104,62 +140,72 @@ static void patch_bugcheck_data(WinDumpHeader64 *h, Error **errp)
* If BugcheckCode wasn't saved, we consider guest OS as alive.
*/
- if (!h->BugcheckCode) {
- h->BugcheckCode = LIVE_SYSTEM_DUMP;
+ if (!WIN_DUMP_FIELD(BugcheckCode)) {
+ *(uint32_t *)WIN_DUMP_FIELD_PTR(BugcheckCode) = LIVE_SYSTEM_DUMP;
}
}
/*
* This routine tries to correct mistakes in crashdump header.
*/
-static void patch_header(WinDumpHeader64 *h)
+static void patch_header(WinDumpHeader *h, bool x64)
{
Error *local_err = NULL;
- h->RequiredDumpSpace = sizeof(WinDumpHeader64) +
- (h->PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS);
- h->PhysicalMemoryBlock.unused = 0;
- h->unused1 = 0;
+ if (x64) {
+ h->x64.RequiredDumpSpace = sizeof(WinDumpHeader64) +
+ (h->x64.PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS);
+ h->x64.PhysicalMemoryBlock.unused = 0;
+ h->x64.unused1 = 0;
+ } else {
+ h->x32.RequiredDumpSpace = sizeof(WinDumpHeader32) +
+ (h->x32.PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS);
+ }
- patch_mm_pfn_database(h, &local_err);
+ patch_mm_pfn_database(h, x64, &local_err);
if (local_err) {
warn_report_err(local_err);
local_err = NULL;
}
- patch_bugcheck_data(h, &local_err);
+ patch_bugcheck_data(h, x64, &local_err);
if (local_err) {
warn_report_err(local_err);
}
}
-static void check_header(WinDumpHeader64 *h, Error **errp)
+static bool check_header(WinDumpHeader *h, bool *x64, Error **errp)
{
const char Signature[] = "PAGE";
- const char ValidDump[] = "DU64";
if (memcmp(h->Signature, Signature, sizeof(h->Signature))) {
error_setg(errp, "win-dump: invalid header, expected '%.4s',"
" got '%.4s'", Signature, h->Signature);
- return;
+ return false;
}
- if (memcmp(h->ValidDump, ValidDump, sizeof(h->ValidDump))) {
- error_setg(errp, "win-dump: invalid header, expected '%.4s',"
- " got '%.4s'", ValidDump, h->ValidDump);
- return;
+ if (!memcmp(h->ValidDump, "DUMP", sizeof(h->ValidDump))) {
+ *x64 = false;
+ } else if (!memcmp(h->ValidDump, "DU64", sizeof(h->ValidDump))) {
+ *x64 = true;
+ } else {
+ error_setg(errp, "win-dump: invalid header, expected 'DUMP' or 'DU64',"
+ " got '%.4s'", h->ValidDump);
+ return false;
}
+
+ return true;
}
-static void check_kdbg(WinDumpHeader64 *h, Error **errp)
+static void check_kdbg(WinDumpHeader *h, bool x64, Error **errp)
{
const char OwnerTag[] = "KDBG";
char read_OwnerTag[4];
- uint64_t KdDebuggerDataBlock = h->KdDebuggerDataBlock;
+ uint64_t KdDebuggerDataBlock = WIN_DUMP_FIELD(KdDebuggerDataBlock);
bool try_fallback = true;
try_again:
if (cpu_memory_rw_debug(first_cpu,
- KdDebuggerDataBlock + KDBG_OWNER_TAG_OFFSET64,
+ KdDebuggerDataBlock + KDBG_OWNER_TAG_OFFSET,
(uint8_t *)&read_OwnerTag, sizeof(read_OwnerTag), 0)) {
error_setg(errp, "win-dump: failed to read OwnerTag");
return;
@@ -173,7 +219,7 @@ try_again:
* we try to use KDBG obtained by guest driver.
*/
- KdDebuggerDataBlock = h->BugcheckParameter1;
+ KdDebuggerDataBlock = WIN_DUMP_FIELD(BugcheckParameter1);
try_fallback = false;
goto try_again;
} else {
@@ -184,7 +230,11 @@ try_again:
}
}
- h->KdDebuggerDataBlock = KdDebuggerDataBlock;
+ if (x64) {
+ h->x64.KdDebuggerDataBlock = KdDebuggerDataBlock;
+ } else {
+ h->x32.KdDebuggerDataBlock = KdDebuggerDataBlock;
+ }
}
struct saved_context {
@@ -192,24 +242,25 @@ struct saved_context {
uint64_t addr;
};
-static void patch_and_save_context(WinDumpHeader64 *h,
+static void patch_and_save_context(WinDumpHeader *h, bool x64,
struct saved_context *saved_ctx,
Error **errp)
{
+ uint64_t KdDebuggerDataBlock = WIN_DUMP_FIELD(KdDebuggerDataBlock);
uint64_t KiProcessorBlock;
uint16_t OffsetPrcbContext;
CPUState *cpu;
int i = 0;
- if (cpu_memory_rw_debug(first_cpu,
- h->KdDebuggerDataBlock + KDBG_KI_PROCESSOR_BLOCK_OFFSET64,
- (uint8_t *)&KiProcessorBlock, sizeof(KiProcessorBlock), 0)) {
+ if (cpu_read_ptr(x64, first_cpu,
+ KdDebuggerDataBlock + KDBG_KI_PROCESSOR_BLOCK_OFFSET,
+ &KiProcessorBlock)) {
error_setg(errp, "win-dump: failed to read KiProcessorBlock");
return;
}
if (cpu_memory_rw_debug(first_cpu,
- h->KdDebuggerDataBlock + KDBG_OFFSET_PRCB_CONTEXT_OFFSET64,
+ KdDebuggerDataBlock + KDBG_OFFSET_PRCB_CONTEXT_OFFSET,
(uint8_t *)&OffsetPrcbContext, sizeof(OffsetPrcbContext), 0)) {
error_setg(errp, "win-dump: failed to read OffsetPrcbContext");
return;
@@ -222,17 +273,17 @@ static void patch_and_save_context(WinDumpHeader64 *h,
uint64_t Context;
WinContext ctx;
- if (cpu_memory_rw_debug(first_cpu,
- KiProcessorBlock + i * sizeof(uint64_t),
- (uint8_t *)&Prcb, sizeof(Prcb), 0)) {
+ if (cpu_read_ptr(x64, first_cpu,
+ KiProcessorBlock + i * win_dump_ptr_size(x64),
+ &Prcb)) {
error_setg(errp, "win-dump: failed to read"
" CPU #%d PRCB location", i);
return;
}
- if (cpu_memory_rw_debug(first_cpu,
+ if (cpu_read_ptr(x64, first_cpu,
Prcb + OffsetPrcbContext,
- (uint8_t *)&Context, sizeof(Context), 0)) {
+ &Context)) {
error_setg(errp, "win-dump: failed to read"
" CPU #%d ContextFrame location", i);
return;
@@ -240,56 +291,88 @@ static void patch_and_save_context(WinDumpHeader64 *h,
saved_ctx[i].addr = Context;
- ctx = (WinContext){
- .ContextFlags = WIN_CTX_ALL,
- .MxCsr = env->mxcsr,
-
- .SegEs = env->segs[0].selector,
- .SegCs = env->segs[1].selector,
- .SegSs = env->segs[2].selector,
- .SegDs = env->segs[3].selector,
- .SegFs = env->segs[4].selector,
- .SegGs = env->segs[5].selector,
- .EFlags = cpu_compute_eflags(env),
-
- .Dr0 = env->dr[0],
- .Dr1 = env->dr[1],
- .Dr2 = env->dr[2],
- .Dr3 = env->dr[3],
- .Dr6 = env->dr[6],
- .Dr7 = env->dr[7],
-
- .Rax = env->regs[R_EAX],
- .Rbx = env->regs[R_EBX],
- .Rcx = env->regs[R_ECX],
- .Rdx = env->regs[R_EDX],
- .Rsp = env->regs[R_ESP],
- .Rbp = env->regs[R_EBP],
- .Rsi = env->regs[R_ESI],
- .Rdi = env->regs[R_EDI],
- .R8 = env->regs[8],
- .R9 = env->regs[9],
- .R10 = env->regs[10],
- .R11 = env->regs[11],
- .R12 = env->regs[12],
- .R13 = env->regs[13],
- .R14 = env->regs[14],
- .R15 = env->regs[15],
-
- .Rip = env->eip,
- .FltSave = {
+ if (x64) {
+ ctx.x64 = (WinContext64){
+ .ContextFlags = WIN_CTX64_ALL,
.MxCsr = env->mxcsr,
- },
- };
+
+ .SegEs = env->segs[0].selector,
+ .SegCs = env->segs[1].selector,
+ .SegSs = env->segs[2].selector,
+ .SegDs = env->segs[3].selector,
+ .SegFs = env->segs[4].selector,
+ .SegGs = env->segs[5].selector,
+ .EFlags = cpu_compute_eflags(env),
+
+ .Dr0 = env->dr[0],
+ .Dr1 = env->dr[1],
+ .Dr2 = env->dr[2],
+ .Dr3 = env->dr[3],
+ .Dr6 = env->dr[6],
+ .Dr7 = env->dr[7],
+
+ .Rax = env->regs[R_EAX],
+ .Rbx = env->regs[R_EBX],
+ .Rcx = env->regs[R_ECX],
+ .Rdx = env->regs[R_EDX],
+ .Rsp = env->regs[R_ESP],
+ .Rbp = env->regs[R_EBP],
+ .Rsi = env->regs[R_ESI],
+ .Rdi = env->regs[R_EDI],
+ .R8 = env->regs[8],
+ .R9 = env->regs[9],
+ .R10 = env->regs[10],
+ .R11 = env->regs[11],
+ .R12 = env->regs[12],
+ .R13 = env->regs[13],
+ .R14 = env->regs[14],
+ .R15 = env->regs[15],
+
+ .Rip = env->eip,
+ .FltSave = {
+ .MxCsr = env->mxcsr,
+ },
+ };
+ } else {
+ ctx.x32 = (WinContext32){
+ .ContextFlags = WIN_CTX32_FULL | WIN_CTX_DBG,
+
+ .SegEs = env->segs[0].selector,
+ .SegCs = env->segs[1].selector,
+ .SegSs = env->segs[2].selector,
+ .SegDs = env->segs[3].selector,
+ .SegFs = env->segs[4].selector,
+ .SegGs = env->segs[5].selector,
+ .EFlags = cpu_compute_eflags(env),
+
+ .Dr0 = env->dr[0],
+ .Dr1 = env->dr[1],
+ .Dr2 = env->dr[2],
+ .Dr3 = env->dr[3],
+ .Dr6 = env->dr[6],
+ .Dr7 = env->dr[7],
+
+ .Eax = env->regs[R_EAX],
+ .Ebx = env->regs[R_EBX],
+ .Ecx = env->regs[R_ECX],
+ .Edx = env->regs[R_EDX],
+ .Esp = env->regs[R_ESP],
+ .Ebp = env->regs[R_EBP],
+ .Esi = env->regs[R_ESI],
+ .Edi = env->regs[R_EDI],
+
+ .Eip = env->eip,
+ };
+ }
if (cpu_memory_rw_debug(first_cpu, Context,
- (uint8_t *)&saved_ctx[i].ctx, sizeof(WinContext), 0)) {
+ &saved_ctx[i].ctx, win_dump_ctx_size(x64), 0)) {
error_setg(errp, "win-dump: failed to save CPU #%d context", i);
return;
}
if (cpu_memory_rw_debug(first_cpu, Context,
- (uint8_t *)&ctx, sizeof(WinContext), 1)) {
+ &ctx, win_dump_ctx_size(x64), 1)) {
error_setg(errp, "win-dump: failed to write CPU #%d context", i);
return;
}
@@ -298,14 +381,14 @@ static void patch_and_save_context(WinDumpHeader64 *h,
}
}
-static void restore_context(WinDumpHeader64 *h,
+static void restore_context(WinDumpHeader *h, bool x64,
struct saved_context *saved_ctx)
{
int i;
- for (i = 0; i < h->NumberProcessors; i++) {
+ for (i = 0; i < WIN_DUMP_FIELD(NumberProcessors); i++) {
if (cpu_memory_rw_debug(first_cpu, saved_ctx[i].addr,
- (uint8_t *)&saved_ctx[i].ctx, sizeof(WinContext), 1)) {
+ &saved_ctx[i].ctx, win_dump_ctx_size(x64), 1)) {
warn_report("win-dump: failed to restore CPU #%d context", i);
}
}
@@ -313,69 +396,71 @@ static void restore_context(WinDumpHeader64 *h,
void create_win_dump(DumpState *s, Error **errp)
{
- WinDumpHeader64 *h = (WinDumpHeader64 *)(s->guest_note +
- VMCOREINFO_ELF_NOTE_HDR_SIZE);
+ WinDumpHeader *h = (void *)(s->guest_note + VMCOREINFO_ELF_NOTE_HDR_SIZE);
X86CPU *first_x86_cpu = X86_CPU(first_cpu);
uint64_t saved_cr3 = first_x86_cpu->env.cr[3];
struct saved_context *saved_ctx = NULL;
Error *local_err = NULL;
+ bool x64 = true;
+ size_t hdr_size;
- if (s->guest_note_size != sizeof(WinDumpHeader64) +
- VMCOREINFO_ELF_NOTE_HDR_SIZE) {
+ if (s->guest_note_size != VMCOREINFO_WIN_DUMP_NOTE_SIZE32 &&
+ s->guest_note_size != VMCOREINFO_WIN_DUMP_NOTE_SIZE64) {
error_setg(errp, "win-dump: invalid vmcoreinfo note size");
return;
}
- check_header(h, &local_err);
- if (local_err) {
+ if (!check_header(h, &x64, &local_err)) {
error_propagate(errp, local_err);
return;
}
+ hdr_size = x64 ? sizeof(WinDumpHeader64) : sizeof(WinDumpHeader32);
+
/*
* Further access to kernel structures by virtual addresses
* should be made from system context.
*/
- first_x86_cpu->env.cr[3] = h->DirectoryTableBase;
+ first_x86_cpu->env.cr[3] = WIN_DUMP_FIELD(DirectoryTableBase);
- check_kdbg(h, &local_err);
+ check_kdbg(h, x64, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto out_cr3;
}
- patch_header(h);
+ patch_header(h, x64);
- saved_ctx = g_new(struct saved_context, h->NumberProcessors);
+ saved_ctx = g_new(struct saved_context, WIN_DUMP_FIELD(NumberProcessors));
/*
* Always patch context because there is no way
* to determine if the system-saved context is valid
*/
- patch_and_save_context(h, saved_ctx, &local_err);
+ patch_and_save_context(h, x64, saved_ctx, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto out_free;
}
- s->total_size = h->RequiredDumpSpace;
+ s->total_size = WIN_DUMP_FIELD(RequiredDumpSpace);
- s->written_size = qemu_write_full(s->fd, h, sizeof(*h));
- if (s->written_size != sizeof(*h)) {
+ s->written_size = qemu_write_full(s->fd, h, hdr_size);
+ if (s->written_size != hdr_size) {
error_setg(errp, QERR_IO_ERROR);
goto out_restore;
}
- write_runs(s, h, &local_err);
+ write_runs(s, h, x64, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto out_restore;
}
out_restore:
- restore_context(h, saved_ctx);
+ restore_context(h, x64, saved_ctx);
out_free:
g_free(saved_ctx);
out_cr3:
diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc
index db3e1f393d..bbeadaa189 100644
--- a/fpu/softfloat-parts.c.inc
+++ b/fpu/softfloat-parts.c.inc
@@ -1327,16 +1327,19 @@ static FloatRelation partsN(compare)(FloatPartsN *a, FloatPartsN *b,
float_status *s, bool is_quiet)
{
int ab_mask = float_cmask(a->cls) | float_cmask(b->cls);
- int cmp;
if (likely(ab_mask == float_cmask_normal)) {
+ FloatRelation cmp;
+
if (a->sign != b->sign) {
goto a_sign;
}
- if (a->exp != b->exp) {
- cmp = a->exp < b->exp ? -1 : 1;
- } else {
+ if (a->exp == b->exp) {
cmp = frac_cmp(a, b);
+ } else if (a->exp < b->exp) {
+ cmp = float_relation_less;
+ } else {
+ cmp = float_relation_greater;
}
if (a->sign) {
cmp = -cmp;
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index 5e2cf20448..4a871ef2a1 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -874,10 +874,10 @@ static FloatParts128 *parts128_minmax(FloatParts128 *a, FloatParts128 *b,
#define parts_minmax(A, B, S, F) \
PARTS_GENERIC_64_128(minmax, A)(A, B, S, F)
-static int parts64_compare(FloatParts64 *a, FloatParts64 *b,
- float_status *s, bool q);
-static int parts128_compare(FloatParts128 *a, FloatParts128 *b,
- float_status *s, bool q);
+static FloatRelation parts64_compare(FloatParts64 *a, FloatParts64 *b,
+ float_status *s, bool q);
+static FloatRelation parts128_compare(FloatParts128 *a, FloatParts128 *b,
+ float_status *s, bool q);
#define parts_compare(A, B, S, Q) \
PARTS_GENERIC_64_128(compare, A)(A, B, S, Q)
@@ -957,21 +957,23 @@ static void frac128_allones(FloatParts128 *a)
#define frac_allones(A) FRAC_GENERIC_64_128(allones, A)(A)
-static int frac64_cmp(FloatParts64 *a, FloatParts64 *b)
+static FloatRelation frac64_cmp(FloatParts64 *a, FloatParts64 *b)
{
- return a->frac == b->frac ? 0 : a->frac < b->frac ? -1 : 1;
+ return (a->frac == b->frac ? float_relation_equal
+ : a->frac < b->frac ? float_relation_less
+ : float_relation_greater);
}
-static int frac128_cmp(FloatParts128 *a, FloatParts128 *b)
+static FloatRelation frac128_cmp(FloatParts128 *a, FloatParts128 *b)
{
uint64_t ta = a->frac_hi, tb = b->frac_hi;
if (ta == tb) {
ta = a->frac_lo, tb = b->frac_lo;
if (ta == tb) {
- return 0;
+ return float_relation_equal;
}
}
- return ta < tb ? -1 : 1;
+ return ta < tb ? float_relation_less : float_relation_greater;
}
#define frac_cmp(A, B) FRAC_GENERIC_64_128(cmp, A)(A, B)
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 808020d005..03e6a73d1f 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -202,9 +202,9 @@ ERST
{
.name = "change",
- .args_type = "device:B,target:F,arg:s?,read-only-mode:s?",
- .params = "device filename [format [read-only-mode]]",
- .help = "change a removable medium, optional format",
+ .args_type = "device:B,force:-f,target:F,arg:s?,read-only-mode:s?",
+ .params = "device [-f] filename [format [read-only-mode]]",
+ .help = "change a removable medium, optional format, use -f to force the operation",
.cmd = hmp_change,
},
@@ -212,11 +212,14 @@ SRST
``change`` *device* *setting*
Change the configuration of a device.
- ``change`` *diskdevice* *filename* [*format* [*read-only-mode*]]
+ ``change`` *diskdevice* [-f] *filename* [*format* [*read-only-mode*]]
Change the medium for a removable disk device to point to *filename*. eg::
(qemu) change ide1-cd0 /path/to/some.iso
+ ``-f``
+ forces the operation even if the guest has locked the tray.
+
*format* is optional.
*read-only-mode* may be used to change the read-only status of the device.
@@ -1065,7 +1068,7 @@ ERST
"-l: dump in kdump-compressed format, with lzo compression.\n\t\t\t"
"-s: dump in kdump-compressed format, with snappy compression.\n\t\t\t"
"-w: dump in Windows crashdump format (can be used instead of ELF-dump converting),\n\t\t\t"
- " for Windows x64 guests with vmcoreinfo driver only.\n\t\t\t"
+ " for Windows x86 and x64 guests with vmcoreinfo driver only.\n\t\t\t"
"begin: the starting physical address.\n\t\t\t"
"length: the memory size, in bytes.",
.cmd = hmp_dump_guest_memory,
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 674623aabe..707eb430c2 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -760,7 +760,7 @@ epilogue:
qemu_mutex_unlock(&s->mutex);
switch (status) {
case SMMU_TRANS_SUCCESS:
- entry.perm = flag;
+ entry.perm = cached_entry->entry.perm;
entry.translated_addr = cached_entry->entry.translated_addr +
(addr & cached_entry->entry.addr_mask);
entry.addr_mask = cached_entry->entry.addr_mask;
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index bb6a2484d8..5bdd98e4a1 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -522,7 +522,7 @@ static void fdt_add_gic_node(VirtMachineState *vms)
qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 0x2);
qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 0x2);
qemu_fdt_setprop(ms->fdt, nodename, "ranges", NULL, 0);
- if (vms->gic_version == VIRT_GIC_VERSION_3) {
+ if (vms->gic_version != VIRT_GIC_VERSION_2) {
int nb_redist_regions = virt_gicv3_redist_region_count(vms);
qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
@@ -690,14 +690,32 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
/* We create a standalone GIC */
SysBusDevice *gicbusdev;
const char *gictype;
- int type = vms->gic_version, i;
+ int i;
unsigned int smp_cpus = ms->smp.cpus;
uint32_t nb_redist_regions = 0;
+ int revision;
- gictype = (type == 3) ? gicv3_class_name() : gic_class_name();
+ if (vms->gic_version == VIRT_GIC_VERSION_2) {
+ gictype = gic_class_name();
+ } else {
+ gictype = gicv3_class_name();
+ }
+ switch (vms->gic_version) {
+ case VIRT_GIC_VERSION_2:
+ revision = 2;
+ break;
+ case VIRT_GIC_VERSION_3:
+ revision = 3;
+ break;
+ case VIRT_GIC_VERSION_4:
+ revision = 4;
+ break;
+ default:
+ g_assert_not_reached();
+ }
vms->gic = qdev_new(gictype);
- qdev_prop_set_uint32(vms->gic, "revision", type);
+ qdev_prop_set_uint32(vms->gic, "revision", revision);
qdev_prop_set_uint32(vms->gic, "num-cpu", smp_cpus);
/* Note that the num-irq property counts both internal and external
* interrupts; there are always 32 of the former (mandated by GIC spec).
@@ -707,9 +725,8 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
qdev_prop_set_bit(vms->gic, "has-security-extensions", vms->secure);
}
- if (type == 3) {
- uint32_t redist0_capacity =
- vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE;
+ if (vms->gic_version != VIRT_GIC_VERSION_2) {
+ uint32_t redist0_capacity = virt_redist_capacity(vms, VIRT_GIC_REDIST);
uint32_t redist0_count = MIN(smp_cpus, redist0_capacity);
nb_redist_regions = virt_gicv3_redist_region_count(vms);
@@ -728,7 +745,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
if (nb_redist_regions == 2) {
uint32_t redist1_capacity =
- vms->memmap[VIRT_HIGH_GIC_REDIST2].size / GICV3_REDIST_SIZE;
+ virt_redist_capacity(vms, VIRT_HIGH_GIC_REDIST2);
qdev_prop_set_uint32(vms->gic, "redist-region-count[1]",
MIN(smp_cpus - redist0_count, redist1_capacity));
@@ -742,7 +759,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
gicbusdev = SYS_BUS_DEVICE(vms->gic);
sysbus_realize_and_unref(gicbusdev, &error_fatal);
sysbus_mmio_map(gicbusdev, 0, vms->memmap[VIRT_GIC_DIST].base);
- if (type == 3) {
+ if (vms->gic_version != VIRT_GIC_VERSION_2) {
sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_REDIST].base);
if (nb_redist_regions == 2) {
sysbus_mmio_map(gicbusdev, 2,
@@ -780,7 +797,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
ppibase + timer_irq[irq]));
}
- if (type == 3) {
+ if (vms->gic_version != VIRT_GIC_VERSION_2) {
qemu_irq irq = qdev_get_gpio_in(vms->gic,
ppibase + ARCH_GIC_MAINT_IRQ);
qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt",
@@ -806,9 +823,9 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
fdt_add_gic_node(vms);
- if (type == 3 && vms->its) {
+ if (vms->gic_version != VIRT_GIC_VERSION_2 && vms->its) {
create_its(vms);
- } else if (type == 2) {
+ } else if (vms->gic_version == VIRT_GIC_VERSION_2) {
create_v2m(vms);
}
}
@@ -1658,10 +1675,10 @@ static uint64_t virt_cpu_mp_affinity(VirtMachineState *vms, int idx)
* purposes are to make TCG consistent (with 64-bit KVM hosts)
* and to improve SGI efficiency.
*/
- if (vms->gic_version == VIRT_GIC_VERSION_3) {
- clustersz = GICV3_TARGETLIST_BITS;
- } else {
+ if (vms->gic_version == VIRT_GIC_VERSION_2) {
clustersz = GIC_TARGETLIST_BITS;
+ } else {
+ clustersz = GICV3_TARGETLIST_BITS;
}
}
return arm_cpu_mp_affinity(idx, clustersz);
@@ -1794,6 +1811,10 @@ static void finalize_gic_version(VirtMachineState *vms)
error_report(
"gic-version=3 is not supported with kernel-irqchip=off");
exit(1);
+ case VIRT_GIC_VERSION_4:
+ error_report(
+ "gic-version=4 is not supported with kernel-irqchip=off");
+ exit(1);
}
}
@@ -1831,6 +1852,9 @@ static void finalize_gic_version(VirtMachineState *vms)
case VIRT_GIC_VERSION_2:
case VIRT_GIC_VERSION_3:
break;
+ case VIRT_GIC_VERSION_4:
+ error_report("gic-version=4 is not supported with KVM");
+ exit(1);
}
/* Check chosen version is effectively supported by the host */
@@ -1854,7 +1878,12 @@ static void finalize_gic_version(VirtMachineState *vms)
case VIRT_GIC_VERSION_MAX:
if (module_object_class_by_name("arm-gicv3")) {
/* CONFIG_ARM_GICV3_TCG was set */
- vms->gic_version = VIRT_GIC_VERSION_3;
+ if (vms->virt) {
+ /* GICv4 only makes sense if CPU has EL2 */
+ vms->gic_version = VIRT_GIC_VERSION_4;
+ } else {
+ vms->gic_version = VIRT_GIC_VERSION_3;
+ }
} else {
vms->gic_version = VIRT_GIC_VERSION_2;
}
@@ -1862,6 +1891,12 @@ static void finalize_gic_version(VirtMachineState *vms)
case VIRT_GIC_VERSION_HOST:
error_report("gic-version=host requires KVM");
exit(1);
+ case VIRT_GIC_VERSION_4:
+ if (!vms->virt) {
+ error_report("gic-version=4 requires virtualization enabled");
+ exit(1);
+ }
+ break;
case VIRT_GIC_VERSION_2:
case VIRT_GIC_VERSION_3:
break;
@@ -2029,16 +2064,16 @@ static void machvirt_init(MachineState *machine)
vms->psci_conduit = QEMU_PSCI_CONDUIT_HVC;
}
- /* The maximum number of CPUs depends on the GIC version, or on how
- * many redistributors we can fit into the memory map.
+ /*
+ * The maximum number of CPUs depends on the GIC version, or on how
+ * many redistributors we can fit into the memory map (which in turn
+ * depends on whether this is a GICv3 or v4).
*/
- if (vms->gic_version == VIRT_GIC_VERSION_3) {
- virt_max_cpus =
- vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE;
- virt_max_cpus +=
- vms->memmap[VIRT_HIGH_GIC_REDIST2].size / GICV3_REDIST_SIZE;
- } else {
+ if (vms->gic_version == VIRT_GIC_VERSION_2) {
virt_max_cpus = GIC_NCPU;
+ } else {
+ virt_max_cpus = virt_redist_capacity(vms, VIRT_GIC_REDIST) +
+ virt_redist_capacity(vms, VIRT_HIGH_GIC_REDIST2);
}
if (max_cpus > virt_max_cpus) {
@@ -2426,8 +2461,19 @@ static void virt_set_mte(Object *obj, bool value, Error **errp)
static char *virt_get_gic_version(Object *obj, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
- const char *val = vms->gic_version == VIRT_GIC_VERSION_3 ? "3" : "2";
+ const char *val;
+ switch (vms->gic_version) {
+ case VIRT_GIC_VERSION_4:
+ val = "4";
+ break;
+ case VIRT_GIC_VERSION_3:
+ val = "3";
+ break;
+ default:
+ val = "2";
+ break;
+ }
return g_strdup(val);
}
@@ -2435,7 +2481,9 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
- if (!strcmp(value, "3")) {
+ if (!strcmp(value, "4")) {
+ vms->gic_version = VIRT_GIC_VERSION_4;
+ } else if (!strcmp(value, "3")) {
vms->gic_version = VIRT_GIC_VERSION_3;
} else if (!strcmp(value, "2")) {
vms->gic_version = VIRT_GIC_VERSION_2;
@@ -2893,7 +2941,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
virt_set_gic_version);
object_class_property_set_description(oc, "gic-version",
"Set GIC version. "
- "Valid values are 2, 3, host and max");
+ "Valid values are 2, 3, 4, host and max");
object_class_property_add_str(oc, "iommu", virt_get_iommu, virt_set_iommu);
object_class_property_set_description(oc, "iommu",
diff --git a/hw/char/escc.c b/hw/char/escc.c
index 8755d8d34f..17a908c59b 100644
--- a/hw/char/escc.c
+++ b/hw/char/escc.c
@@ -828,7 +828,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
}
}
- if (qcode > qemu_input_map_qcode_to_sun_len) {
+ if (qcode >= qemu_input_map_qcode_to_sun_len) {
return;
}
diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
index a7cf301eab..eded1b557e 100644
--- a/hw/intc/Kconfig
+++ b/hw/intc/Kconfig
@@ -84,3 +84,6 @@ config GOLDFISH_PIC
config M68K_IRQC
bool
+
+config NIOS2_VIC
+ bool
diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index 4ca5ae9bc5..5634c6fc78 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -144,6 +144,25 @@ const VMStateDescription vmstate_gicv3_cpu_sre_el1 = {
}
};
+static bool gicv4_needed(void *opaque)
+{
+ GICv3CPUState *cs = opaque;
+
+ return cs->gic->revision > 3;
+}
+
+const VMStateDescription vmstate_gicv3_gicv4 = {
+ .name = "arm_gicv3_cpu/gicv4",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = gicv4_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(gicr_vpropbaser, GICv3CPUState),
+ VMSTATE_UINT64(gicr_vpendbaser, GICv3CPUState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_gicv3_cpu = {
.name = "arm_gicv3_cpu",
.version_id = 1,
@@ -175,6 +194,7 @@ static const VMStateDescription vmstate_gicv3_cpu = {
.subsections = (const VMStateDescription * []) {
&vmstate_gicv3_cpu_virt,
&vmstate_gicv3_cpu_sre_el1,
+ &vmstate_gicv3_gicv4,
NULL
}
};
@@ -295,7 +315,7 @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler,
memory_region_init_io(&region->iomem, OBJECT(s),
ops ? &ops[1] : NULL, region, name,
- s->redist_region_count[i] * GICV3_REDIST_SIZE);
+ s->redist_region_count[i] * gicv3_redist_size(s));
sysbus_init_mmio(sbd, &region->iomem);
g_free(name);
}
@@ -306,12 +326,14 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
GICv3State *s = ARM_GICV3_COMMON(dev);
int i, rdist_capacity, cpuidx;
- /* revision property is actually reserved and currently used only in order
- * to keep the interface compatible with GICv2 code, avoiding extra
- * conditions. However, in future it could be used, for example, if we
- * implement GICv4.
+ /*
+ * This GIC device supports only revisions 3 and 4. The GICv1/v2
+ * is a separate device.
+ * Note that subclasses of this device may impose further restrictions
+ * on the GIC revision: notably, the in-kernel KVM GIC doesn't
+ * support GICv4.
*/
- if (s->revision != 3) {
+ if (s->revision != 3 && s->revision != 4) {
error_setg(errp, "unsupported GIC revision %d", s->revision);
return;
}
@@ -328,6 +350,10 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
s->num_irq, GIC_INTERNAL);
return;
}
+ if (s->num_cpu == 0) {
+ error_setg(errp, "num-cpu must be at least 1");
+ return;
+ }
/* ITLinesNumber is represented as (N / 32) - 1, so this is an
* implementation imposed restriction, not an architectural one,
@@ -350,9 +376,9 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
for (i = 0; i < s->nb_redist_regions; i++) {
rdist_capacity += s->redist_region_count[i];
}
- if (rdist_capacity < s->num_cpu) {
+ if (rdist_capacity != s->num_cpu) {
error_setg(errp, "Capacity of the redist regions(%d) "
- "is less than number of vcpus(%d)",
+ "does not match the number of vcpus(%d)",
rdist_capacity, s->num_cpu);
return;
}
@@ -382,8 +408,8 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
* Last == 1 if this is the last redistributor in a series of
* contiguous redistributor pages
* DirectLPI == 0 (direct injection of LPIs not supported)
- * VLPIS == 0 (virtual LPIs not supported)
- * PLPIS == 0 (physical LPIs not supported)
+ * VLPIS == 1 if vLPIs supported (GICv4 and up)
+ * PLPIS == 1 if LPIs supported
*/
cpu_affid = object_property_get_uint(OBJECT(cpu), "mp-affinity", NULL);
@@ -398,6 +424,9 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
if (s->lpi_enable) {
s->cpu[i].gicr_typer |= GICR_TYPER_PLPIS;
+ if (s->revision > 3) {
+ s->cpu[i].gicr_typer |= GICR_TYPER_VLPIS;
+ }
}
}
@@ -410,6 +439,8 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
cpuidx += s->redist_region_count[i];
s->cpu[cpuidx - 1].gicr_typer |= GICR_TYPER_LAST;
}
+
+ s->itslist = g_ptr_array_new();
}
static void arm_gicv3_finalize(Object *obj)
@@ -438,6 +469,8 @@ static void arm_gicv3_common_reset(DeviceState *dev)
cs->gicr_waker = GICR_WAKER_ProcessorSleep | GICR_WAKER_ChildrenAsleep;
cs->gicr_propbaser = 0;
cs->gicr_pendbaser = 0;
+ cs->gicr_vpropbaser = 0;
+ cs->gicr_vpendbaser = 0;
/* If we're resetting a TZ-aware GIC as if secure firmware
* had set it up ready to start a kernel in non-secure, we
* need to set interrupts to group 1 so the kernel can use them.
@@ -459,6 +492,7 @@ static void arm_gicv3_common_reset(DeviceState *dev)
cs->hppi.prio = 0xff;
cs->hpplpi.prio = 0xff;
+ cs->hppvlpi.prio = 0xff;
/* State in the CPU interface must *not* be reset here, because it
* is part of the CPU's reset domain, not the GIC device's.
diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index 1a3d440a54..8404f46ee0 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -21,6 +21,12 @@
#include "hw/irq.h"
#include "cpu.h"
+/*
+ * Special case return value from hppvi_index(); must be larger than
+ * the architecturally maximum possible list register index (which is 15)
+ */
+#define HPPVI_INDEX_VLPI 16
+
static GICv3CPUState *icc_cs_from_env(CPUARMState *env)
{
return env->gicv3state;
@@ -157,10 +163,18 @@ static int ich_highest_active_virt_prio(GICv3CPUState *cs)
static int hppvi_index(GICv3CPUState *cs)
{
- /* Return the list register index of the highest priority pending
+ /*
+ * Return the list register index of the highest priority pending
* virtual interrupt, as per the HighestPriorityVirtualInterrupt
* pseudocode. If no pending virtual interrupts, return -1.
+ * If the highest priority pending virtual interrupt is a vLPI,
+ * return HPPVI_INDEX_VLPI.
+ * (The pseudocode handles checking whether the vLPI is higher
+ * priority than the highest priority list register at every
+ * callsite of HighestPriorityVirtualInterrupt; we check it here.)
*/
+ ARMCPU *cpu = ARM_CPU(cs->cpu);
+ CPUARMState *env = &cpu->env;
int idx = -1;
int i;
/* Note that a list register entry with a priority of 0xff will
@@ -202,6 +216,23 @@ static int hppvi_index(GICv3CPUState *cs)
}
}
+ /*
+ * "no pending vLPI" is indicated with prio = 0xff, which always
+ * fails the priority check here. vLPIs are only considered
+ * when we are in Non-Secure state.
+ */
+ if (cs->hppvlpi.prio < prio && !arm_is_secure(env)) {
+ if (cs->hppvlpi.grp == GICV3_G0) {
+ if (cs->ich_vmcr_el2 & ICH_VMCR_EL2_VENG0) {
+ return HPPVI_INDEX_VLPI;
+ }
+ } else {
+ if (cs->ich_vmcr_el2 & ICH_VMCR_EL2_VENG1) {
+ return HPPVI_INDEX_VLPI;
+ }
+ }
+ }
+
return idx;
}
@@ -289,6 +320,47 @@ static bool icv_hppi_can_preempt(GICv3CPUState *cs, uint64_t lr)
return false;
}
+static bool icv_hppvlpi_can_preempt(GICv3CPUState *cs)
+{
+ /*
+ * Return true if we can signal the highest priority pending vLPI.
+ * We can assume we're Non-secure because hppvi_index() already
+ * tested for that.
+ */
+ uint32_t mask, rprio, vpmr;
+
+ if (!(cs->ich_hcr_el2 & ICH_HCR_EL2_EN)) {
+ /* Virtual interface disabled */
+ return false;
+ }
+
+ vpmr = extract64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VPMR_SHIFT,
+ ICH_VMCR_EL2_VPMR_LENGTH);
+
+ if (cs->hppvlpi.prio >= vpmr) {
+ /* Priority mask masks this interrupt */
+ return false;
+ }
+
+ rprio = ich_highest_active_virt_prio(cs);
+ if (rprio == 0xff) {
+ /* No running interrupt so we can preempt */
+ return true;
+ }
+
+ mask = icv_gprio_mask(cs, cs->hppvlpi.grp);
+
+ /*
+ * We only preempt a running interrupt if the pending interrupt's
+ * group priority is sufficient (the subpriorities are not considered).
+ */
+ if ((cs->hppvlpi.prio & mask) < (rprio & mask)) {
+ return true;
+ }
+
+ return false;
+}
+
static uint32_t eoi_maintenance_interrupt_state(GICv3CPUState *cs,
uint32_t *misr)
{
@@ -370,9 +442,55 @@ static uint32_t maintenance_interrupt_state(GICv3CPUState *cs)
return value;
}
+void gicv3_cpuif_virt_irq_fiq_update(GICv3CPUState *cs)
+{
+ /*
+ * Tell the CPU about any pending virtual interrupts.
+ * This should only be called for changes that affect the
+ * vIRQ and vFIQ status and do not change the maintenance
+ * interrupt status. This means that unlike gicv3_cpuif_virt_update()
+ * this function won't recursively call back into the GIC code.
+ * The main use of this is when the redistributor has changed the
+ * highest priority pending virtual LPI.
+ */
+ int idx;
+ int irqlevel = 0;
+ int fiqlevel = 0;
+
+ idx = hppvi_index(cs);
+ trace_gicv3_cpuif_virt_update(gicv3_redist_affid(cs), idx,
+ cs->hppvlpi.irq, cs->hppvlpi.grp,
+ cs->hppvlpi.prio);
+ if (idx == HPPVI_INDEX_VLPI) {
+ if (icv_hppvlpi_can_preempt(cs)) {
+ if (cs->hppvlpi.grp == GICV3_G0) {
+ fiqlevel = 1;
+ } else {
+ irqlevel = 1;
+ }
+ }
+ } else if (idx >= 0) {
+ uint64_t lr = cs->ich_lr_el2[idx];
+
+ if (icv_hppi_can_preempt(cs, lr)) {
+ /* Virtual interrupts are simple: G0 are always FIQ, and G1 IRQ */
+ if (lr & ICH_LR_EL2_GROUP) {
+ irqlevel = 1;
+ } else {
+ fiqlevel = 1;
+ }
+ }
+ }
+
+ trace_gicv3_cpuif_virt_set_irqs(gicv3_redist_affid(cs), fiqlevel, irqlevel);
+ qemu_set_irq(cs->parent_vfiq, fiqlevel);
+ qemu_set_irq(cs->parent_virq, irqlevel);
+}
+
static void gicv3_cpuif_virt_update(GICv3CPUState *cs)
{
- /* Tell the CPU about any pending virtual interrupts or
+ /*
+ * Tell the CPU about any pending virtual interrupts or
* maintenance interrupts, following a change to the state
* of the CPU interface relevant to virtual interrupts.
*
@@ -389,37 +507,17 @@ static void gicv3_cpuif_virt_update(GICv3CPUState *cs)
* naturally as a result of there being no architectural
* linkage between the physical and virtual GIC logic.
*/
- int idx;
- int irqlevel = 0;
- int fiqlevel = 0;
- int maintlevel = 0;
ARMCPU *cpu = ARM_CPU(cs->cpu);
+ int maintlevel = 0;
- idx = hppvi_index(cs);
- trace_gicv3_cpuif_virt_update(gicv3_redist_affid(cs), idx);
- if (idx >= 0) {
- uint64_t lr = cs->ich_lr_el2[idx];
-
- if (icv_hppi_can_preempt(cs, lr)) {
- /* Virtual interrupts are simple: G0 are always FIQ, and G1 IRQ */
- if (lr & ICH_LR_EL2_GROUP) {
- irqlevel = 1;
- } else {
- fiqlevel = 1;
- }
- }
- }
+ gicv3_cpuif_virt_irq_fiq_update(cs);
if ((cs->ich_hcr_el2 & ICH_HCR_EL2_EN) &&
maintenance_interrupt_state(cs) != 0) {
maintlevel = 1;
}
- trace_gicv3_cpuif_virt_set_irqs(gicv3_redist_affid(cs), fiqlevel,
- irqlevel, maintlevel);
-
- qemu_set_irq(cs->parent_vfiq, fiqlevel);
- qemu_set_irq(cs->parent_virq, irqlevel);
+ trace_gicv3_cpuif_virt_set_maint_irq(gicv3_redist_affid(cs), maintlevel);
qemu_set_irq(cpu->gicv3_maintenance_interrupt, maintlevel);
}
@@ -445,7 +543,7 @@ static void icv_ap_write(CPUARMState *env, const ARMCPRegInfo *ri,
cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU;
- gicv3_cpuif_virt_update(cs);
+ gicv3_cpuif_virt_irq_fiq_update(cs);
return;
}
@@ -490,7 +588,7 @@ static void icv_bpr_write(CPUARMState *env, const ARMCPRegInfo *ri,
write_vbpr(cs, grp, value);
- gicv3_cpuif_virt_update(cs);
+ gicv3_cpuif_virt_irq_fiq_update(cs);
}
static uint64_t icv_pmr_read(CPUARMState *env, const ARMCPRegInfo *ri)
@@ -517,7 +615,7 @@ static void icv_pmr_write(CPUARMState *env, const ARMCPRegInfo *ri,
cs->ich_vmcr_el2 = deposit64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VPMR_SHIFT,
ICH_VMCR_EL2_VPMR_LENGTH, value);
- gicv3_cpuif_virt_update(cs);
+ gicv3_cpuif_virt_irq_fiq_update(cs);
}
static uint64_t icv_igrpen_read(CPUARMState *env, const ARMCPRegInfo *ri)
@@ -584,7 +682,7 @@ static void icv_ctlr_write(CPUARMState *env, const ARMCPRegInfo *ri,
cs->ich_vmcr_el2 = deposit64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VEOIM_SHIFT,
1, value & ICC_CTLR_EL1_EOIMODE ? 1 : 0);
- gicv3_cpuif_virt_update(cs);
+ gicv3_cpuif_virt_irq_fiq_update(cs);
}
static uint64_t icv_rpr_read(CPUARMState *env, const ARMCPRegInfo *ri)
@@ -603,7 +701,11 @@ static uint64_t icv_hppir_read(CPUARMState *env, const ARMCPRegInfo *ri)
int idx = hppvi_index(cs);
uint64_t value = INTID_SPURIOUS;
- if (idx >= 0) {
+ if (idx == HPPVI_INDEX_VLPI) {
+ if (cs->hppvlpi.grp == grp) {
+ value = cs->hppvlpi.irq;
+ }
+ } else if (idx >= 0) {
uint64_t lr = cs->ich_lr_el2[idx];
int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0;
@@ -634,6 +736,18 @@ static void icv_activate_irq(GICv3CPUState *cs, int idx, int grp)
cs->ich_apr[grp][regno] |= (1 << regbit);
}
+static void icv_activate_vlpi(GICv3CPUState *cs)
+{
+ uint32_t mask = icv_gprio_mask(cs, cs->hppvlpi.grp);
+ int prio = cs->hppvlpi.prio & mask;
+ int aprbit = prio >> (8 - cs->vprebits);
+ int regno = aprbit / 32;
+ int regbit = aprbit % 32;
+
+ cs->ich_apr[cs->hppvlpi.grp][regno] |= (1 << regbit);
+ gicv3_redist_vlpi_pending(cs, cs->hppvlpi.irq, 0);
+}
+
static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri)
{
GICv3CPUState *cs = icc_cs_from_env(env);
@@ -641,7 +755,12 @@ static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri)
int idx = hppvi_index(cs);
uint64_t intid = INTID_SPURIOUS;
- if (idx >= 0) {
+ if (idx == HPPVI_INDEX_VLPI) {
+ if (cs->hppvlpi.grp == grp && icv_hppvlpi_can_preempt(cs)) {
+ intid = cs->hppvlpi.irq;
+ icv_activate_vlpi(cs);
+ }
+ } else if (idx >= 0) {
uint64_t lr = cs->ich_lr_el2[idx];
int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0;
@@ -2333,7 +2452,7 @@ static void ich_ap_write(CPUARMState *env, const ARMCPRegInfo *ri,
trace_gicv3_ich_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value);
cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU;
- gicv3_cpuif_virt_update(cs);
+ gicv3_cpuif_virt_irq_fiq_update(cs);
}
static uint64_t ich_hcr_read(CPUARMState *env, const ARMCPRegInfo *ri)
@@ -2459,11 +2578,15 @@ static uint64_t ich_vtr_read(CPUARMState *env, const ARMCPRegInfo *ri)
uint64_t value;
value = ((cs->num_list_regs - 1) << ICH_VTR_EL2_LISTREGS_SHIFT)
- | ICH_VTR_EL2_TDS | ICH_VTR_EL2_NV4 | ICH_VTR_EL2_A3V
+ | ICH_VTR_EL2_TDS | ICH_VTR_EL2_A3V
| (1 << ICH_VTR_EL2_IDBITS_SHIFT)
| ((cs->vprebits - 1) << ICH_VTR_EL2_PREBITS_SHIFT)
| ((cs->vpribits - 1) << ICH_VTR_EL2_PRIBITS_SHIFT);
+ if (cs->gic->revision < 4) {
+ value |= ICH_VTR_EL2_NV4;
+ }
+
trace_gicv3_ich_vtr_read(gicv3_redist_affid(cs), value);
return value;
}
@@ -2616,6 +2739,12 @@ static void gicv3_cpuif_el_change_hook(ARMCPU *cpu, void *opaque)
GICv3CPUState *cs = opaque;
gicv3_cpuif_update(cs);
+ /*
+ * Because vLPIs are only pending in NonSecure state,
+ * an EL change can change the VIRQ/VFIQ status (but
+ * cannot affect the maintenance interrupt state)
+ */
+ gicv3_cpuif_virt_irq_fiq_update(cs);
}
void gicv3_init_cpuif(GICv3State *s)
diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c
index 28d913b211..b9ed955e36 100644
--- a/hw/intc/arm_gicv3_dist.c
+++ b/hw/intc/arm_gicv3_dist.c
@@ -383,7 +383,7 @@ static bool gicd_readl(GICv3State *s, hwaddr offset,
* No1N == 1 (1-of-N SPI interrupts not supported)
* A3V == 1 (non-zero values of Affinity level 3 supported)
* IDbits == 0xf (we support 16-bit interrupt identifiers)
- * DVIS == 0 (Direct virtual LPI injection not supported)
+ * DVIS == 1 (Direct virtual LPI injection supported) if GICv4
* LPIS == 1 (LPIs are supported if affinity routing is enabled)
* num_LPIs == 0b00000 (bits [15:11],Number of LPIs as indicated
* by GICD_TYPER.IDbits)
@@ -399,8 +399,9 @@ static bool gicd_readl(GICv3State *s, hwaddr offset,
* so we only need to check the DS bit.
*/
bool sec_extn = !(s->gicd_ctlr & GICD_CTLR_DS);
+ bool dvis = s->revision >= 4;
- *data = (1 << 25) | (1 << 24) | (sec_extn << 10) |
+ *data = (1 << 25) | (1 << 24) | (dvis << 18) | (sec_extn << 10) |
(s->lpi_enable << GICD_TYPER_LPIS_SHIFT) |
(0xf << 19) | itlinesnumber;
return true;
@@ -557,7 +558,7 @@ static bool gicd_readl(GICv3State *s, hwaddr offset,
}
case GICD_IDREGS ... GICD_IDREGS + 0x2f:
/* ID registers */
- *data = gicv3_idreg(offset - GICD_IDREGS);
+ *data = gicv3_idreg(s, offset - GICD_IDREGS, GICV3_PIDR0_DIST);
return true;
case GICD_SGIR:
/* WO registers, return unknown value */
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 8746673213..2ff21ed6bb 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -61,6 +61,12 @@ typedef struct ITEntry {
uint32_t vpeid;
} ITEntry;
+typedef struct VTEntry {
+ bool valid;
+ unsigned vptsize;
+ uint32_t rdbase;
+ uint64_t vptaddr;
+} VTEntry;
/*
* The ITS spec permits a range of CONSTRAINED UNPREDICTABLE options
@@ -72,13 +78,33 @@ typedef struct ITEntry {
* and continue processing.
* The process_* functions which handle individual ITS commands all
* return an ItsCmdResult which tells process_cmdq() whether it should
- * stall or keep going.
+ * stall, keep going because of an error, or keep going because the
+ * command was a success.
*/
typedef enum ItsCmdResult {
CMD_STALL = 0,
CMD_CONTINUE = 1,
+ CMD_CONTINUE_OK = 2,
} ItsCmdResult;
+/* True if the ITS supports the GICv4 virtual LPI feature */
+static bool its_feature_virtual(GICv3ITSState *s)
+{
+ return s->typer & R_GITS_TYPER_VIRTUAL_MASK;
+}
+
+static inline bool intid_in_lpi_range(uint32_t id)
+{
+ return id >= GICV3_LPI_INTID_START &&
+ id < (1 << (GICD_TYPER_IDBITS + 1));
+}
+
+static inline bool valid_doorbell(uint32_t id)
+{
+ /* Doorbell fields may be an LPI, or 1023 to mean "no doorbell" */
+ return id == INTID_SPURIOUS || intid_in_lpi_range(id);
+}
+
static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
{
uint64_t result = 0;
@@ -289,97 +315,247 @@ out:
}
/*
- * This function handles the processing of following commands based on
- * the ItsCmdType parameter passed:-
- * 1. triggering of lpi interrupt translation via ITS INT command
- * 2. triggering of lpi interrupt translation via gits_translater register
- * 3. handling of ITS CLEAR command
- * 4. handling of ITS DISCARD command
+ * Read the vPE Table entry at index @vpeid. On success (including
+ * successfully determining that there is no valid entry for this index),
+ * we return MEMTX_OK and populate the VTEntry struct accordingly.
+ * If there is an error reading memory then we return the error code.
*/
-static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid,
- uint32_t eventid, ItsCmdType cmd)
+static MemTxResult get_vte(GICv3ITSState *s, uint32_t vpeid, VTEntry *vte)
+{
+ MemTxResult res = MEMTX_OK;
+ AddressSpace *as = &s->gicv3->dma_as;
+ uint64_t entry_addr = table_entry_addr(s, &s->vpet, vpeid, &res);
+ uint64_t vteval;
+
+ if (entry_addr == -1) {
+ /* No L2 table entry, i.e. no valid VTE, or a memory error */
+ vte->valid = false;
+ goto out;
+ }
+ vteval = address_space_ldq_le(as, entry_addr, MEMTXATTRS_UNSPECIFIED, &res);
+ if (res != MEMTX_OK) {
+ goto out;
+ }
+ vte->valid = FIELD_EX64(vteval, VTE, VALID);
+ vte->vptsize = FIELD_EX64(vteval, VTE, VPTSIZE);
+ vte->vptaddr = FIELD_EX64(vteval, VTE, VPTADDR);
+ vte->rdbase = FIELD_EX64(vteval, VTE, RDBASE);
+out:
+ if (res != MEMTX_OK) {
+ trace_gicv3_its_vte_read_fault(vpeid);
+ } else {
+ trace_gicv3_its_vte_read(vpeid, vte->valid, vte->vptsize,
+ vte->vptaddr, vte->rdbase);
+ }
+ return res;
+}
+
+/*
+ * Given a (DeviceID, EventID), look up the corresponding ITE, including
+ * checking for the various invalid-value cases. If we find a valid ITE,
+ * fill in @ite and @dte and return CMD_CONTINUE_OK. Otherwise return
+ * CMD_STALL or CMD_CONTINUE as appropriate (and the contents of @ite
+ * should not be relied on).
+ *
+ * The string @who is purely for the LOG_GUEST_ERROR messages,
+ * and should indicate the name of the calling function or similar.
+ */
+static ItsCmdResult lookup_ite(GICv3ITSState *s, const char *who,
+ uint32_t devid, uint32_t eventid, ITEntry *ite,
+ DTEntry *dte)
{
uint64_t num_eventids;
- DTEntry dte;
- CTEntry cte;
- ITEntry ite;
if (devid >= s->dt.num_entries) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid command attributes: devid %d>=%d",
- __func__, devid, s->dt.num_entries);
+ who, devid, s->dt.num_entries);
return CMD_CONTINUE;
}
- if (get_dte(s, devid, &dte) != MEMTX_OK) {
+ if (get_dte(s, devid, dte) != MEMTX_OK) {
return CMD_STALL;
}
- if (!dte.valid) {
+ if (!dte->valid) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid command attributes: "
- "invalid dte for %d\n", __func__, devid);
+ "invalid dte for %d\n", who, devid);
return CMD_CONTINUE;
}
- num_eventids = 1ULL << (dte.size + 1);
+ num_eventids = 1ULL << (dte->size + 1);
if (eventid >= num_eventids) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid command attributes: eventid %d >= %"
- PRId64 "\n",
- __func__, eventid, num_eventids);
+ PRId64 "\n", who, eventid, num_eventids);
return CMD_CONTINUE;
}
- if (get_ite(s, eventid, &dte, &ite) != MEMTX_OK) {
+ if (get_ite(s, eventid, dte, ite) != MEMTX_OK) {
return CMD_STALL;
}
- if (!ite.valid || ite.inttype != ITE_INTTYPE_PHYSICAL) {
+ if (!ite->valid) {
qemu_log_mask(LOG_GUEST_ERROR,
- "%s: invalid command attributes: invalid ITE\n",
- __func__);
+ "%s: invalid command attributes: invalid ITE\n", who);
return CMD_CONTINUE;
}
- if (ite.icid >= s->ct.num_entries) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: invalid ICID 0x%x in ITE (table corrupted?)\n",
- __func__, ite.icid);
+ return CMD_CONTINUE_OK;
+}
+
+/*
+ * Given an ICID, look up the corresponding CTE, including checking for various
+ * invalid-value cases. If we find a valid CTE, fill in @cte and return
+ * CMD_CONTINUE_OK; otherwise return CMD_STALL or CMD_CONTINUE (and the
+ * contents of @cte should not be relied on).
+ *
+ * The string @who is purely for the LOG_GUEST_ERROR messages,
+ * and should indicate the name of the calling function or similar.
+ */
+static ItsCmdResult lookup_cte(GICv3ITSState *s, const char *who,
+ uint32_t icid, CTEntry *cte)
+{
+ if (icid >= s->ct.num_entries) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid ICID 0x%x\n", who, icid);
+ return CMD_CONTINUE;
+ }
+ if (get_cte(s, icid, cte) != MEMTX_OK) {
+ return CMD_STALL;
+ }
+ if (!cte->valid) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid CTE\n", who);
+ return CMD_CONTINUE;
+ }
+ if (cte->rdbase >= s->gicv3->num_cpu) {
return CMD_CONTINUE;
}
+ return CMD_CONTINUE_OK;
+}
- if (get_cte(s, ite.icid, &cte) != MEMTX_OK) {
+/*
+ * Given a VPEID, look up the corresponding VTE, including checking
+ * for various invalid-value cases. if we find a valid VTE, fill in @vte
+ * and return CMD_CONTINUE_OK; otherwise return CMD_STALL or CMD_CONTINUE
+ * (and the contents of @vte should not be relied on).
+ *
+ * The string @who is purely for the LOG_GUEST_ERROR messages,
+ * and should indicate the name of the calling function or similar.
+ */
+static ItsCmdResult lookup_vte(GICv3ITSState *s, const char *who,
+ uint32_t vpeid, VTEntry *vte)
+{
+ if (vpeid >= s->vpet.num_entries) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid VPEID 0x%x\n", who, vpeid);
+ return CMD_CONTINUE;
+ }
+
+ if (get_vte(s, vpeid, vte) != MEMTX_OK) {
return CMD_STALL;
}
- if (!cte.valid) {
+ if (!vte->valid) {
qemu_log_mask(LOG_GUEST_ERROR,
- "%s: invalid command attributes: invalid CTE\n",
- __func__);
+ "%s: invalid VTE for VPEID 0x%x\n", who, vpeid);
+ return CMD_CONTINUE;
+ }
+
+ if (vte->rdbase >= s->gicv3->num_cpu) {
+ return CMD_CONTINUE;
+ }
+ return CMD_CONTINUE_OK;
+}
+
+static ItsCmdResult process_its_cmd_phys(GICv3ITSState *s, const ITEntry *ite,
+ int irqlevel)
+{
+ CTEntry cte;
+ ItsCmdResult cmdres;
+
+ cmdres = lookup_cte(s, __func__, ite->icid, &cte);
+ if (cmdres != CMD_CONTINUE_OK) {
+ return cmdres;
+ }
+ gicv3_redist_process_lpi(&s->gicv3->cpu[cte.rdbase], ite->intid, irqlevel);
+ return CMD_CONTINUE_OK;
+}
+
+static ItsCmdResult process_its_cmd_virt(GICv3ITSState *s, const ITEntry *ite,
+ int irqlevel)
+{
+ VTEntry vte;
+ ItsCmdResult cmdres;
+
+ cmdres = lookup_vte(s, __func__, ite->vpeid, &vte);
+ if (cmdres != CMD_CONTINUE_OK) {
+ return cmdres;
+ }
+
+ if (!intid_in_lpi_range(ite->intid) ||
+ ite->intid >= (1ULL << (vte.vptsize + 1))) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: intid 0x%x out of range\n",
+ __func__, ite->intid);
return CMD_CONTINUE;
}
/*
- * Current implementation only supports rdbase == procnum
- * Hence rdbase physical address is ignored
+ * For QEMU the actual pending of the vLPI is handled in the
+ * redistributor code
*/
- if (cte.rdbase >= s->gicv3->num_cpu) {
- return CMD_CONTINUE;
+ gicv3_redist_process_vlpi(&s->gicv3->cpu[vte.rdbase], ite->intid,
+ vte.vptaddr << 16, ite->doorbell, irqlevel);
+ return CMD_CONTINUE_OK;
+}
+
+/*
+ * This function handles the processing of following commands based on
+ * the ItsCmdType parameter passed:-
+ * 1. triggering of lpi interrupt translation via ITS INT command
+ * 2. triggering of lpi interrupt translation via gits_translater register
+ * 3. handling of ITS CLEAR command
+ * 4. handling of ITS DISCARD command
+ */
+static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid,
+ uint32_t eventid, ItsCmdType cmd)
+{
+ DTEntry dte;
+ ITEntry ite;
+ ItsCmdResult cmdres;
+ int irqlevel;
+
+ cmdres = lookup_ite(s, __func__, devid, eventid, &ite, &dte);
+ if (cmdres != CMD_CONTINUE_OK) {
+ return cmdres;
}
- if ((cmd == CLEAR) || (cmd == DISCARD)) {
- gicv3_redist_process_lpi(&s->gicv3->cpu[cte.rdbase], ite.intid, 0);
- } else {
- gicv3_redist_process_lpi(&s->gicv3->cpu[cte.rdbase], ite.intid, 1);
+ irqlevel = (cmd == CLEAR || cmd == DISCARD) ? 0 : 1;
+
+ switch (ite.inttype) {
+ case ITE_INTTYPE_PHYSICAL:
+ cmdres = process_its_cmd_phys(s, &ite, irqlevel);
+ break;
+ case ITE_INTTYPE_VIRTUAL:
+ if (!its_feature_virtual(s)) {
+ /* Can't happen unless guest is illegally writing to table memory */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid type %d in ITE (table corrupted?)\n",
+ __func__, ite.inttype);
+ return CMD_CONTINUE;
+ }
+ cmdres = process_its_cmd_virt(s, &ite, irqlevel);
+ break;
+ default:
+ g_assert_not_reached();
}
- if (cmd == DISCARD) {
+ if (cmdres == CMD_CONTINUE_OK && cmd == DISCARD) {
ITEntry ite = {};
/* remove mapping from interrupt translation table */
ite.valid = false;
- return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE : CMD_STALL;
+ return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE_OK : CMD_STALL;
}
- return CMD_CONTINUE;
+ return CMD_CONTINUE_OK;
}
+
static ItsCmdResult process_its_cmd(GICv3ITSState *s, const uint64_t *cmdpkt,
ItsCmdType cmd)
{
@@ -409,7 +585,6 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt,
uint32_t devid, eventid;
uint32_t pIntid = 0;
uint64_t num_eventids;
- uint32_t num_intids;
uint16_t icid = 0;
DTEntry dte;
ITEntry ite;
@@ -437,7 +612,6 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt,
return CMD_STALL;
}
num_eventids = 1ULL << (dte.size + 1);
- num_intids = 1ULL << (GICD_TYPER_IDBITS + 1);
if (icid >= s->ct.num_entries) {
qemu_log_mask(LOG_GUEST_ERROR,
@@ -459,7 +633,7 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt,
return CMD_CONTINUE;
}
- if (pIntid < GICV3_LPI_INTID_START || pIntid >= num_intids) {
+ if (!intid_in_lpi_range(pIntid)) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid interrupt ID 0x%x\n", __func__, pIntid);
return CMD_CONTINUE;
@@ -472,7 +646,86 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt,
ite.icid = icid;
ite.doorbell = INTID_SPURIOUS;
ite.vpeid = 0;
- return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE : CMD_STALL;
+ return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE_OK : CMD_STALL;
+}
+
+static ItsCmdResult process_vmapti(GICv3ITSState *s, const uint64_t *cmdpkt,
+ bool ignore_vintid)
+{
+ uint32_t devid, eventid, vintid, doorbell, vpeid;
+ uint32_t num_eventids;
+ DTEntry dte;
+ ITEntry ite;
+
+ if (!its_feature_virtual(s)) {
+ return CMD_CONTINUE;
+ }
+
+ devid = FIELD_EX64(cmdpkt[0], VMAPTI_0, DEVICEID);
+ eventid = FIELD_EX64(cmdpkt[1], VMAPTI_1, EVENTID);
+ vpeid = FIELD_EX64(cmdpkt[1], VMAPTI_1, VPEID);
+ doorbell = FIELD_EX64(cmdpkt[2], VMAPTI_2, DOORBELL);
+ if (ignore_vintid) {
+ vintid = eventid;
+ trace_gicv3_its_cmd_vmapi(devid, eventid, vpeid, doorbell);
+ } else {
+ vintid = FIELD_EX64(cmdpkt[2], VMAPTI_2, VINTID);
+ trace_gicv3_its_cmd_vmapti(devid, eventid, vpeid, vintid, doorbell);
+ }
+
+ if (devid >= s->dt.num_entries) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid DeviceID 0x%x (must be less than 0x%x)\n",
+ __func__, devid, s->dt.num_entries);
+ return CMD_CONTINUE;
+ }
+
+ if (get_dte(s, devid, &dte) != MEMTX_OK) {
+ return CMD_STALL;
+ }
+
+ if (!dte.valid) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: no entry in device table for DeviceID 0x%x\n",
+ __func__, devid);
+ return CMD_CONTINUE;
+ }
+
+ num_eventids = 1ULL << (dte.size + 1);
+
+ if (eventid >= num_eventids) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: EventID 0x%x too large for DeviceID 0x%x "
+ "(must be less than 0x%x)\n",
+ __func__, eventid, devid, num_eventids);
+ return CMD_CONTINUE;
+ }
+ if (!intid_in_lpi_range(vintid)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: VIntID 0x%x not a valid LPI\n",
+ __func__, vintid);
+ return CMD_CONTINUE;
+ }
+ if (!valid_doorbell(doorbell)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Doorbell %d not 1023 and not a valid LPI\n",
+ __func__, doorbell);
+ return CMD_CONTINUE;
+ }
+ if (vpeid >= s->vpet.num_entries) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: VPEID 0x%x out of range (must be less than 0x%x)\n",
+ __func__, vpeid, s->vpet.num_entries);
+ return CMD_CONTINUE;
+ }
+ /* add ite entry to interrupt translation table */
+ ite.valid = true;
+ ite.inttype = ITE_INTTYPE_VIRTUAL;
+ ite.intid = vintid;
+ ite.icid = 0;
+ ite.doorbell = doorbell;
+ ite.vpeid = vpeid;
+ return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE_OK : CMD_STALL;
}
/*
@@ -533,7 +786,7 @@ static ItsCmdResult process_mapc(GICv3ITSState *s, const uint64_t *cmdpkt)
return CMD_CONTINUE;
}
- return update_cte(s, icid, &cte) ? CMD_CONTINUE : CMD_STALL;
+ return update_cte(s, icid, &cte) ? CMD_CONTINUE_OK : CMD_STALL;
}
/*
@@ -594,7 +847,7 @@ static ItsCmdResult process_mapd(GICv3ITSState *s, const uint64_t *cmdpkt)
return CMD_CONTINUE;
}
- return update_dte(s, devid, &dte) ? CMD_CONTINUE : CMD_STALL;
+ return update_dte(s, devid, &dte) ? CMD_CONTINUE_OK : CMD_STALL;
}
static ItsCmdResult process_movall(GICv3ITSState *s, const uint64_t *cmdpkt)
@@ -623,23 +876,23 @@ static ItsCmdResult process_movall(GICv3ITSState *s, const uint64_t *cmdpkt)
if (rd1 == rd2) {
/* Move to same target must succeed as a no-op */
- return CMD_CONTINUE;
+ return CMD_CONTINUE_OK;
}
/* Move all pending LPIs from redistributor 1 to redistributor 2 */
gicv3_redist_movall_lpis(&s->gicv3->cpu[rd1], &s->gicv3->cpu[rd2]);
- return CMD_CONTINUE;
+ return CMD_CONTINUE_OK;
}
static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt)
{
uint32_t devid, eventid;
uint16_t new_icid;
- uint64_t num_eventids;
DTEntry dte;
CTEntry old_cte, new_cte;
ITEntry old_ite;
+ ItsCmdResult cmdres;
devid = FIELD_EX64(cmdpkt[0], MOVI_0, DEVICEID);
eventid = FIELD_EX64(cmdpkt[1], MOVI_1, EVENTID);
@@ -647,103 +900,346 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt)
trace_gicv3_its_cmd_movi(devid, eventid, new_icid);
- if (devid >= s->dt.num_entries) {
+ cmdres = lookup_ite(s, __func__, devid, eventid, &old_ite, &dte);
+ if (cmdres != CMD_CONTINUE_OK) {
+ return cmdres;
+ }
+
+ if (old_ite.inttype != ITE_INTTYPE_PHYSICAL) {
qemu_log_mask(LOG_GUEST_ERROR,
- "%s: invalid command attributes: devid %d>=%d",
- __func__, devid, s->dt.num_entries);
+ "%s: invalid command attributes: invalid ITE\n",
+ __func__);
return CMD_CONTINUE;
}
- if (get_dte(s, devid, &dte) != MEMTX_OK) {
- return CMD_STALL;
+
+ cmdres = lookup_cte(s, __func__, old_ite.icid, &old_cte);
+ if (cmdres != CMD_CONTINUE_OK) {
+ return cmdres;
+ }
+ cmdres = lookup_cte(s, __func__, new_icid, &new_cte);
+ if (cmdres != CMD_CONTINUE_OK) {
+ return cmdres;
}
- if (!dte.valid) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: invalid command attributes: "
- "invalid dte for %d\n", __func__, devid);
+ if (old_cte.rdbase != new_cte.rdbase) {
+ /* Move the LPI from the old redistributor to the new one */
+ gicv3_redist_mov_lpi(&s->gicv3->cpu[old_cte.rdbase],
+ &s->gicv3->cpu[new_cte.rdbase],
+ old_ite.intid);
+ }
+
+ /* Update the ICID field in the interrupt translation table entry */
+ old_ite.icid = new_icid;
+ return update_ite(s, eventid, &dte, &old_ite) ? CMD_CONTINUE_OK : CMD_STALL;
+}
+
+/*
+ * Update the vPE Table entry at index @vpeid with the entry @vte.
+ * Returns true on success, false if there was a memory access error.
+ */
+static bool update_vte(GICv3ITSState *s, uint32_t vpeid, const VTEntry *vte)
+{
+ AddressSpace *as = &s->gicv3->dma_as;
+ uint64_t entry_addr;
+ uint64_t vteval = 0;
+ MemTxResult res = MEMTX_OK;
+
+ trace_gicv3_its_vte_write(vpeid, vte->valid, vte->vptsize, vte->vptaddr,
+ vte->rdbase);
+
+ if (vte->valid) {
+ vteval = FIELD_DP64(vteval, VTE, VALID, 1);
+ vteval = FIELD_DP64(vteval, VTE, VPTSIZE, vte->vptsize);
+ vteval = FIELD_DP64(vteval, VTE, VPTADDR, vte->vptaddr);
+ vteval = FIELD_DP64(vteval, VTE, RDBASE, vte->rdbase);
+ }
+
+ entry_addr = table_entry_addr(s, &s->vpet, vpeid, &res);
+ if (res != MEMTX_OK) {
+ return false;
+ }
+ if (entry_addr == -1) {
+ /* No L2 table for this index: discard write and continue */
+ return true;
+ }
+ address_space_stq_le(as, entry_addr, vteval, MEMTXATTRS_UNSPECIFIED, &res);
+ return res == MEMTX_OK;
+}
+
+static ItsCmdResult process_vmapp(GICv3ITSState *s, const uint64_t *cmdpkt)
+{
+ VTEntry vte;
+ uint32_t vpeid;
+
+ if (!its_feature_virtual(s)) {
return CMD_CONTINUE;
}
- num_eventids = 1ULL << (dte.size + 1);
- if (eventid >= num_eventids) {
+ vpeid = FIELD_EX64(cmdpkt[1], VMAPP_1, VPEID);
+ vte.rdbase = FIELD_EX64(cmdpkt[2], VMAPP_2, RDBASE);
+ vte.valid = FIELD_EX64(cmdpkt[2], VMAPP_2, V);
+ vte.vptsize = FIELD_EX64(cmdpkt[3], VMAPP_3, VPTSIZE);
+ vte.vptaddr = FIELD_EX64(cmdpkt[3], VMAPP_3, VPTADDR);
+
+ trace_gicv3_its_cmd_vmapp(vpeid, vte.rdbase, vte.valid,
+ vte.vptaddr, vte.vptsize);
+
+ /*
+ * For GICv4.0 the VPT_size field is only 5 bits, whereas we
+ * define our field macros to include the full GICv4.1 8 bits.
+ * The range check on VPT_size will catch the cases where
+ * the guest set the RES0-in-GICv4.0 bits [7:6].
+ */
+ if (vte.vptsize > FIELD_EX64(s->typer, GITS_TYPER, IDBITS)) {
qemu_log_mask(LOG_GUEST_ERROR,
- "%s: invalid command attributes: eventid %d >= %"
- PRId64 "\n",
- __func__, eventid, num_eventids);
+ "%s: invalid VPT_size 0x%x\n", __func__, vte.vptsize);
return CMD_CONTINUE;
}
- if (get_ite(s, eventid, &dte, &old_ite) != MEMTX_OK) {
- return CMD_STALL;
+ if (vte.valid && vte.rdbase >= s->gicv3->num_cpu) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid rdbase 0x%x\n", __func__, vte.rdbase);
+ return CMD_CONTINUE;
}
- if (!old_ite.valid || old_ite.inttype != ITE_INTTYPE_PHYSICAL) {
+ if (vpeid >= s->vpet.num_entries) {
qemu_log_mask(LOG_GUEST_ERROR,
- "%s: invalid command attributes: invalid ITE\n",
- __func__);
+ "%s: VPEID 0x%x out of range (must be less than 0x%x)\n",
+ __func__, vpeid, s->vpet.num_entries);
return CMD_CONTINUE;
}
- if (old_ite.icid >= s->ct.num_entries) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: invalid ICID 0x%x in ITE (table corrupted?)\n",
- __func__, old_ite.icid);
+ return update_vte(s, vpeid, &vte) ? CMD_CONTINUE_OK : CMD_STALL;
+}
+
+typedef struct VmovpCallbackData {
+ uint64_t rdbase;
+ uint32_t vpeid;
+ /*
+ * Overall command result. If more than one callback finds an
+ * error, STALL beats CONTINUE.
+ */
+ ItsCmdResult result;
+} VmovpCallbackData;
+
+static void vmovp_callback(gpointer data, gpointer opaque)
+{
+ /*
+ * This function is called to update the VPEID field in a VPE
+ * table entry for this ITS. This might be because of a VMOVP
+ * command executed on any ITS that is connected to the same GIC
+ * as this ITS. We need to read the VPE table entry for the VPEID
+ * and update its RDBASE field.
+ */
+ GICv3ITSState *s = data;
+ VmovpCallbackData *cbdata = opaque;
+ VTEntry vte;
+ ItsCmdResult cmdres;
+
+ cmdres = lookup_vte(s, __func__, cbdata->vpeid, &vte);
+ switch (cmdres) {
+ case CMD_STALL:
+ cbdata->result = CMD_STALL;
+ return;
+ case CMD_CONTINUE:
+ if (cbdata->result != CMD_STALL) {
+ cbdata->result = CMD_CONTINUE;
+ }
+ return;
+ case CMD_CONTINUE_OK:
+ break;
+ }
+
+ vte.rdbase = cbdata->rdbase;
+ if (!update_vte(s, cbdata->vpeid, &vte)) {
+ cbdata->result = CMD_STALL;
+ }
+}
+
+static ItsCmdResult process_vmovp(GICv3ITSState *s, const uint64_t *cmdpkt)
+{
+ VmovpCallbackData cbdata;
+
+ if (!its_feature_virtual(s)) {
return CMD_CONTINUE;
}
- if (new_icid >= s->ct.num_entries) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: invalid command attributes: ICID 0x%x\n",
- __func__, new_icid);
+ cbdata.vpeid = FIELD_EX64(cmdpkt[1], VMOVP_1, VPEID);
+ cbdata.rdbase = FIELD_EX64(cmdpkt[2], VMOVP_2, RDBASE);
+
+ trace_gicv3_its_cmd_vmovp(cbdata.vpeid, cbdata.rdbase);
+
+ if (cbdata.rdbase >= s->gicv3->num_cpu) {
return CMD_CONTINUE;
}
- if (get_cte(s, old_ite.icid, &old_cte) != MEMTX_OK) {
- return CMD_STALL;
+ /*
+ * Our ITS implementation reports GITS_TYPER.VMOVP == 1, which means
+ * that when the VMOVP command is executed on an ITS to change the
+ * VPEID field in a VPE table entry the change must be propagated
+ * to all the ITSes connected to the same GIC.
+ */
+ cbdata.result = CMD_CONTINUE_OK;
+ gicv3_foreach_its(s->gicv3, vmovp_callback, &cbdata);
+ return cbdata.result;
+}
+
+static ItsCmdResult process_vmovi(GICv3ITSState *s, const uint64_t *cmdpkt)
+{
+ uint32_t devid, eventid, vpeid, doorbell;
+ bool doorbell_valid;
+ DTEntry dte;
+ ITEntry ite;
+ VTEntry old_vte, new_vte;
+ ItsCmdResult cmdres;
+
+ if (!its_feature_virtual(s)) {
+ return CMD_CONTINUE;
}
- if (!old_cte.valid) {
+
+ devid = FIELD_EX64(cmdpkt[0], VMOVI_0, DEVICEID);
+ eventid = FIELD_EX64(cmdpkt[1], VMOVI_1, EVENTID);
+ vpeid = FIELD_EX64(cmdpkt[1], VMOVI_1, VPEID);
+ doorbell_valid = FIELD_EX64(cmdpkt[2], VMOVI_2, D);
+ doorbell = FIELD_EX64(cmdpkt[2], VMOVI_2, DOORBELL);
+
+ trace_gicv3_its_cmd_vmovi(devid, eventid, vpeid, doorbell_valid, doorbell);
+
+ if (doorbell_valid && !valid_doorbell(doorbell)) {
qemu_log_mask(LOG_GUEST_ERROR,
- "%s: invalid command attributes: "
- "invalid CTE for old ICID 0x%x\n",
- __func__, old_ite.icid);
+ "%s: invalid doorbell 0x%x\n", __func__, doorbell);
return CMD_CONTINUE;
}
- if (get_cte(s, new_icid, &new_cte) != MEMTX_OK) {
- return CMD_STALL;
+ cmdres = lookup_ite(s, __func__, devid, eventid, &ite, &dte);
+ if (cmdres != CMD_CONTINUE_OK) {
+ return cmdres;
}
- if (!new_cte.valid) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: invalid command attributes: "
- "invalid CTE for new ICID 0x%x\n",
- __func__, new_icid);
+
+ if (ite.inttype != ITE_INTTYPE_VIRTUAL) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: ITE is not for virtual interrupt\n",
+ __func__);
return CMD_CONTINUE;
}
- if (old_cte.rdbase >= s->gicv3->num_cpu) {
+ cmdres = lookup_vte(s, __func__, ite.vpeid, &old_vte);
+ if (cmdres != CMD_CONTINUE_OK) {
+ return cmdres;
+ }
+ cmdres = lookup_vte(s, __func__, vpeid, &new_vte);
+ if (cmdres != CMD_CONTINUE_OK) {
+ return cmdres;
+ }
+
+ if (!intid_in_lpi_range(ite.intid) ||
+ ite.intid >= (1ULL << (old_vte.vptsize + 1)) ||
+ ite.intid >= (1ULL << (new_vte.vptsize + 1))) {
qemu_log_mask(LOG_GUEST_ERROR,
- "%s: CTE has invalid rdbase 0x%x\n",
- __func__, old_cte.rdbase);
+ "%s: ITE intid 0x%x out of range\n",
+ __func__, ite.intid);
return CMD_CONTINUE;
}
- if (new_cte.rdbase >= s->gicv3->num_cpu) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: CTE has invalid rdbase 0x%x\n",
- __func__, new_cte.rdbase);
+ ite.vpeid = vpeid;
+ if (doorbell_valid) {
+ ite.doorbell = doorbell;
+ }
+
+ /*
+ * Move the LPI from the old redistributor to the new one. We don't
+ * need to do anything if the guest somehow specified the
+ * same pending table for source and destination.
+ */
+ if (old_vte.vptaddr != new_vte.vptaddr) {
+ gicv3_redist_mov_vlpi(&s->gicv3->cpu[old_vte.rdbase],
+ old_vte.vptaddr << 16,
+ &s->gicv3->cpu[new_vte.rdbase],
+ new_vte.vptaddr << 16,
+ ite.intid,
+ ite.doorbell);
+ }
+
+ /* Update the ITE to the new VPEID and possibly doorbell values */
+ return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE_OK : CMD_STALL;
+}
+
+static ItsCmdResult process_vinvall(GICv3ITSState *s, const uint64_t *cmdpkt)
+{
+ VTEntry vte;
+ uint32_t vpeid;
+ ItsCmdResult cmdres;
+
+ if (!its_feature_virtual(s)) {
return CMD_CONTINUE;
}
- if (old_cte.rdbase != new_cte.rdbase) {
- /* Move the LPI from the old redistributor to the new one */
- gicv3_redist_mov_lpi(&s->gicv3->cpu[old_cte.rdbase],
- &s->gicv3->cpu[new_cte.rdbase],
- old_ite.intid);
+ vpeid = FIELD_EX64(cmdpkt[1], VINVALL_1, VPEID);
+
+ trace_gicv3_its_cmd_vinvall(vpeid);
+
+ cmdres = lookup_vte(s, __func__, vpeid, &vte);
+ if (cmdres != CMD_CONTINUE_OK) {
+ return cmdres;
}
- /* Update the ICID field in the interrupt translation table entry */
- old_ite.icid = new_icid;
- return update_ite(s, eventid, &dte, &old_ite) ? CMD_CONTINUE : CMD_STALL;
+ gicv3_redist_vinvall(&s->gicv3->cpu[vte.rdbase], vte.vptaddr << 16);
+ return CMD_CONTINUE_OK;
+}
+
+static ItsCmdResult process_inv(GICv3ITSState *s, const uint64_t *cmdpkt)
+{
+ uint32_t devid, eventid;
+ ITEntry ite;
+ DTEntry dte;
+ CTEntry cte;
+ VTEntry vte;
+ ItsCmdResult cmdres;
+
+ devid = FIELD_EX64(cmdpkt[0], INV_0, DEVICEID);
+ eventid = FIELD_EX64(cmdpkt[1], INV_1, EVENTID);
+
+ trace_gicv3_its_cmd_inv(devid, eventid);
+
+ cmdres = lookup_ite(s, __func__, devid, eventid, &ite, &dte);
+ if (cmdres != CMD_CONTINUE_OK) {
+ return cmdres;
+ }
+
+ switch (ite.inttype) {
+ case ITE_INTTYPE_PHYSICAL:
+ cmdres = lookup_cte(s, __func__, ite.icid, &cte);
+ if (cmdres != CMD_CONTINUE_OK) {
+ return cmdres;
+ }
+ gicv3_redist_inv_lpi(&s->gicv3->cpu[cte.rdbase], ite.intid);
+ break;
+ case ITE_INTTYPE_VIRTUAL:
+ if (!its_feature_virtual(s)) {
+ /* Can't happen unless guest is illegally writing to table memory */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid type %d in ITE (table corrupted?)\n",
+ __func__, ite.inttype);
+ return CMD_CONTINUE;
+ }
+
+ cmdres = lookup_vte(s, __func__, ite.vpeid, &vte);
+ if (cmdres != CMD_CONTINUE_OK) {
+ return cmdres;
+ }
+ if (!intid_in_lpi_range(ite.intid) ||
+ ite.intid >= (1ULL << (vte.vptsize + 1))) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: intid 0x%x out of range\n",
+ __func__, ite.intid);
+ return CMD_CONTINUE;
+ }
+ gicv3_redist_inv_vlpi(&s->gicv3->cpu[vte.rdbase], ite.intid,
+ vte.vptaddr << 16);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ return CMD_CONTINUE_OK;
}
/*
@@ -782,7 +1278,7 @@ static void process_cmdq(GICv3ITSState *s)
}
while (wr_offset != rd_offset) {
- ItsCmdResult result = CMD_CONTINUE;
+ ItsCmdResult result = CMD_CONTINUE_OK;
void *hostmem;
hwaddr buflen;
uint64_t cmdpkt[GITS_CMDQ_ENTRY_WORDS];
@@ -827,6 +1323,17 @@ static void process_cmdq(GICv3ITSState *s)
*/
trace_gicv3_its_cmd_sync();
break;
+ case GITS_CMD_VSYNC:
+ /*
+ * VSYNC also is a nop, because our implementation is always
+ * in sync.
+ */
+ if (!its_feature_virtual(s)) {
+ result = CMD_CONTINUE;
+ break;
+ }
+ trace_gicv3_its_cmd_vsync();
+ break;
case GITS_CMD_MAPD:
result = process_mapd(s, cmdpkt);
break;
@@ -843,14 +1350,18 @@ static void process_cmdq(GICv3ITSState *s)
result = process_its_cmd(s, cmdpkt, DISCARD);
break;
case GITS_CMD_INV:
+ result = process_inv(s, cmdpkt);
+ break;
case GITS_CMD_INVALL:
/*
* Current implementation doesn't cache any ITS tables,
* but the calculated lpi priority information. We only
* need to trigger lpi priority re-calculation to be in
* sync with LPI config table or pending table changes.
+ * INVALL operates on a collection specified by ICID so
+ * it only affects physical LPIs.
*/
- trace_gicv3_its_cmd_inv();
+ trace_gicv3_its_cmd_invall();
for (i = 0; i < s->gicv3->num_cpu; i++) {
gicv3_redist_update_lpi(&s->gicv3->cpu[i]);
}
@@ -861,11 +1372,30 @@ static void process_cmdq(GICv3ITSState *s)
case GITS_CMD_MOVALL:
result = process_movall(s, cmdpkt);
break;
+ case GITS_CMD_VMAPTI:
+ result = process_vmapti(s, cmdpkt, false);
+ break;
+ case GITS_CMD_VMAPI:
+ result = process_vmapti(s, cmdpkt, true);
+ break;
+ case GITS_CMD_VMAPP:
+ result = process_vmapp(s, cmdpkt);
+ break;
+ case GITS_CMD_VMOVP:
+ result = process_vmovp(s, cmdpkt);
+ break;
+ case GITS_CMD_VMOVI:
+ result = process_vmovi(s, cmdpkt);
+ break;
+ case GITS_CMD_VINVALL:
+ result = process_vinvall(s, cmdpkt);
+ break;
default:
trace_gicv3_its_cmd_unknown(cmd);
break;
}
- if (result == CMD_CONTINUE) {
+ if (result != CMD_STALL) {
+ /* CMD_CONTINUE or CMD_CONTINUE_OK */
rd_offset++;
rd_offset %= s->cq.num_entries;
s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, OFFSET, rd_offset);
@@ -941,6 +1471,15 @@ static void extract_table_params(GICv3ITSState *s)
idbits = 16;
}
break;
+ case GITS_BASER_TYPE_VPE:
+ td = &s->vpet;
+ /*
+ * For QEMU vPEIDs are always 16 bits. (GICv4.1 allows an
+ * implementation to implement fewer bits and report this
+ * via GICD_TYPER2.)
+ */
+ idbits = 16;
+ break;
default:
/*
* GITS_BASER<n>.TYPE is read-only, so GITS_BASER_RO_MASK
@@ -1160,7 +1699,7 @@ static bool its_readl(GICv3ITSState *s, hwaddr offset,
break;
case GITS_IDREGS ... GITS_IDREGS + 0x2f:
/* ID registers */
- *data = gicv3_idreg(offset - GITS_IDREGS);
+ *data = gicv3_idreg(s->gicv3, offset - GITS_IDREGS, GICV3_PIDR0_ITS);
break;
case GITS_TYPER:
*data = extract64(s->typer, 0, 32);
@@ -1395,6 +1934,8 @@ static void gicv3_arm_its_realize(DeviceState *dev, Error **errp)
}
}
+ gicv3_add_its(s->gicv3, dev);
+
gicv3_its_init_mmio(s, &gicv3_its_control_ops, &gicv3_its_translation_ops);
/* set the ITS default features supported */
@@ -1405,6 +1946,11 @@ static void gicv3_arm_its_realize(DeviceState *dev, Error **errp)
s->typer = FIELD_DP64(s->typer, GITS_TYPER, DEVBITS, ITS_DEVBITS);
s->typer = FIELD_DP64(s->typer, GITS_TYPER, CIL, 1);
s->typer = FIELD_DP64(s->typer, GITS_TYPER, CIDBITS, ITS_CIDBITS);
+ if (s->gicv3->revision >= 4) {
+ /* Our VMOVP handles cross-ITS synchronization itself */
+ s->typer = FIELD_DP64(s->typer, GITS_TYPER, VMOVP, 1);
+ s->typer = FIELD_DP64(s->typer, GITS_TYPER, VIRTUAL, 1);
+ }
}
static void gicv3_its_reset(DeviceState *dev)
@@ -1420,6 +1966,7 @@ static void gicv3_its_reset(DeviceState *dev)
/*
* setting GITS_BASER0.Type = 0b001 (Device)
* GITS_BASER1.Type = 0b100 (Collection Table)
+ * GITS_BASER2.Type = 0b010 (vPE) for GICv4 and later
* GITS_BASER<n>.Type,where n = 3 to 7 are 0b00 (Unimplemented)
* GITS_BASER<0,1>.Page_Size = 64KB
* and default translation table entry size to 16 bytes
@@ -1437,6 +1984,15 @@ static void gicv3_its_reset(DeviceState *dev)
GITS_BASER_PAGESIZE_64K);
s->baser[1] = FIELD_DP64(s->baser[1], GITS_BASER, ENTRYSIZE,
GITS_CTE_SIZE - 1);
+
+ if (its_feature_virtual(s)) {
+ s->baser[2] = FIELD_DP64(s->baser[2], GITS_BASER, TYPE,
+ GITS_BASER_TYPE_VPE);
+ s->baser[2] = FIELD_DP64(s->baser[2], GITS_BASER, PAGESIZE,
+ GITS_BASER_PAGESIZE_64K);
+ s->baser[2] = FIELD_DP64(s->baser[2], GITS_BASER, ENTRYSIZE,
+ GITS_VPE_SIZE - 1);
+ }
}
static void gicv3_its_post_load(GICv3ITSState *s)
diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c
index 0b4cbed28b..529c7bd494 100644
--- a/hw/intc/arm_gicv3_its_kvm.c
+++ b/hw/intc/arm_gicv3_its_kvm.c
@@ -106,6 +106,8 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp)
kvm_arm_register_device(&s->iomem_its_cntrl, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_ITS_ADDR_TYPE, s->dev_fd, 0);
+ gicv3_add_its(s->gicv3, dev);
+
gicv3_its_init_mmio(s, NULL, NULL);
if (!kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c
index 5ec5ff9ef6..06f5aceee5 100644
--- a/hw/intc/arm_gicv3_kvm.c
+++ b/hw/intc/arm_gicv3_kvm.c
@@ -781,6 +781,11 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
return;
}
+ if (s->revision != 3) {
+ error_setg(errp, "unsupported GIC revision %d for in-kernel GIC",
+ s->revision);
+ }
+
if (s->security_extn) {
error_setg(errp, "the in-kernel VGICv3 does not implement the "
"security extensions");
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index 412a04f59c..c3d4cdd66b 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -60,6 +60,132 @@ static uint32_t gicr_read_bitmap_reg(GICv3CPUState *cs, MemTxAttrs attrs,
return reg;
}
+static bool vcpu_resident(GICv3CPUState *cs, uint64_t vptaddr)
+{
+ /*
+ * Return true if a vCPU is resident, which is defined by
+ * whether the GICR_VPENDBASER register is marked VALID and
+ * has the right virtual pending table address.
+ */
+ if (!FIELD_EX64(cs->gicr_vpendbaser, GICR_VPENDBASER, VALID)) {
+ return false;
+ }
+ return vptaddr == (cs->gicr_vpendbaser & R_GICR_VPENDBASER_PHYADDR_MASK);
+}
+
+/**
+ * update_for_one_lpi: Update pending information if this LPI is better
+ *
+ * @cs: GICv3CPUState
+ * @irq: interrupt to look up in the LPI Configuration table
+ * @ctbase: physical address of the LPI Configuration table to use
+ * @ds: true if priority value should not be shifted
+ * @hpp: points to pending information to update
+ *
+ * Look up @irq in the Configuration table specified by @ctbase
+ * to see if it is enabled and what its priority is. If it is an
+ * enabled interrupt with a higher priority than that currently
+ * recorded in @hpp, update @hpp.
+ */
+static void update_for_one_lpi(GICv3CPUState *cs, int irq,
+ uint64_t ctbase, bool ds, PendingIrq *hpp)
+{
+ uint8_t lpite;
+ uint8_t prio;
+
+ address_space_read(&cs->gic->dma_as,
+ ctbase + ((irq - GICV3_LPI_INTID_START) * sizeof(lpite)),
+ MEMTXATTRS_UNSPECIFIED, &lpite, sizeof(lpite));
+
+ if (!(lpite & LPI_CTE_ENABLED)) {
+ return;
+ }
+
+ if (ds) {
+ prio = lpite & LPI_PRIORITY_MASK;
+ } else {
+ prio = ((lpite & LPI_PRIORITY_MASK) >> 1) | 0x80;
+ }
+
+ if ((prio < hpp->prio) ||
+ ((prio == hpp->prio) && (irq <= hpp->irq))) {
+ hpp->irq = irq;
+ hpp->prio = prio;
+ /* LPIs and vLPIs are always non-secure Grp1 interrupts */
+ hpp->grp = GICV3_G1NS;
+ }
+}
+
+/**
+ * update_for_all_lpis: Fully scan LPI tables and find best pending LPI
+ *
+ * @cs: GICv3CPUState
+ * @ptbase: physical address of LPI Pending table
+ * @ctbase: physical address of LPI Configuration table
+ * @ptsizebits: size of tables, specified as number of interrupt ID bits minus 1
+ * @ds: true if priority value should not be shifted
+ * @hpp: points to pending information to set
+ *
+ * Recalculate the highest priority pending enabled LPI from scratch,
+ * and set @hpp accordingly.
+ *
+ * We scan the LPI pending table @ptbase; for each pending LPI, we read the
+ * corresponding entry in the LPI configuration table @ctbase to extract
+ * the priority and enabled information.
+ *
+ * We take @ptsizebits in the form idbits-1 because this is the way that
+ * LPI table sizes are architecturally specified in GICR_PROPBASER.IDBits
+ * and in the VMAPP command's VPT_size field.
+ */
+static void update_for_all_lpis(GICv3CPUState *cs, uint64_t ptbase,
+ uint64_t ctbase, unsigned ptsizebits,
+ bool ds, PendingIrq *hpp)
+{
+ AddressSpace *as = &cs->gic->dma_as;
+ uint8_t pend;
+ uint32_t pendt_size = (1ULL << (ptsizebits + 1));
+ int i, bit;
+
+ hpp->prio = 0xff;
+
+ for (i = GICV3_LPI_INTID_START / 8; i < pendt_size / 8; i++) {
+ address_space_read(as, ptbase + i, MEMTXATTRS_UNSPECIFIED, &pend, 1);
+ while (pend) {
+ bit = ctz32(pend);
+ update_for_one_lpi(cs, i * 8 + bit, ctbase, ds, hpp);
+ pend &= ~(1 << bit);
+ }
+ }
+}
+
+/**
+ * set_lpi_pending_bit: Set or clear pending bit for an LPI
+ *
+ * @cs: GICv3CPUState
+ * @ptbase: physical address of LPI Pending table
+ * @irq: LPI to change pending state for
+ * @level: false to clear pending state, true to set
+ *
+ * Returns true if we needed to do something, false if the pending bit
+ * was already at @level.
+ */
+static bool set_pending_table_bit(GICv3CPUState *cs, uint64_t ptbase,
+ int irq, bool level)
+{
+ AddressSpace *as = &cs->gic->dma_as;
+ uint64_t addr = ptbase + irq / 8;
+ uint8_t pend;
+
+ address_space_read(as, addr, MEMTXATTRS_UNSPECIFIED, &pend, 1);
+ if (extract32(pend, irq % 8, 1) == level) {
+ /* Bit already at requested state, no action required */
+ return false;
+ }
+ pend = deposit32(pend, irq % 8, 1, level ? 1 : 0);
+ address_space_write(as, addr, MEMTXATTRS_UNSPECIFIED, &pend, 1);
+ return true;
+}
+
static uint8_t gicr_read_ipriorityr(GICv3CPUState *cs, MemTxAttrs attrs,
int irq)
{
@@ -100,6 +226,87 @@ static void gicr_write_ipriorityr(GICv3CPUState *cs, MemTxAttrs attrs, int irq,
cs->gicr_ipriorityr[irq] = value;
}
+static void gicv3_redist_update_vlpi_only(GICv3CPUState *cs)
+{
+ uint64_t ptbase, ctbase, idbits;
+
+ if (!FIELD_EX64(cs->gicr_vpendbaser, GICR_VPENDBASER, VALID)) {
+ cs->hppvlpi.prio = 0xff;
+ return;
+ }
+
+ ptbase = cs->gicr_vpendbaser & R_GICR_VPENDBASER_PHYADDR_MASK;
+ ctbase = cs->gicr_vpropbaser & R_GICR_VPROPBASER_PHYADDR_MASK;
+ idbits = FIELD_EX64(cs->gicr_vpropbaser, GICR_VPROPBASER, IDBITS);
+
+ update_for_all_lpis(cs, ptbase, ctbase, idbits, true, &cs->hppvlpi);
+}
+
+static void gicv3_redist_update_vlpi(GICv3CPUState *cs)
+{
+ gicv3_redist_update_vlpi_only(cs);
+ gicv3_cpuif_virt_irq_fiq_update(cs);
+}
+
+static void gicr_write_vpendbaser(GICv3CPUState *cs, uint64_t newval)
+{
+ /* Write @newval to GICR_VPENDBASER, handling its effects */
+ bool oldvalid = FIELD_EX64(cs->gicr_vpendbaser, GICR_VPENDBASER, VALID);
+ bool newvalid = FIELD_EX64(newval, GICR_VPENDBASER, VALID);
+ bool pendinglast;
+
+ /*
+ * The DIRTY bit is read-only and for us is always zero;
+ * other fields are writeable.
+ */
+ newval &= R_GICR_VPENDBASER_INNERCACHE_MASK |
+ R_GICR_VPENDBASER_SHAREABILITY_MASK |
+ R_GICR_VPENDBASER_PHYADDR_MASK |
+ R_GICR_VPENDBASER_OUTERCACHE_MASK |
+ R_GICR_VPENDBASER_PENDINGLAST_MASK |
+ R_GICR_VPENDBASER_IDAI_MASK |
+ R_GICR_VPENDBASER_VALID_MASK;
+
+ if (oldvalid && newvalid) {
+ /*
+ * Changing other fields while VALID is 1 is UNPREDICTABLE;
+ * we choose to log and ignore the write.
+ */
+ if (cs->gicr_vpendbaser ^ newval) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Changing GICR_VPENDBASER when VALID=1 "
+ "is UNPREDICTABLE\n", __func__);
+ }
+ return;
+ }
+ if (!oldvalid && !newvalid) {
+ cs->gicr_vpendbaser = newval;
+ return;
+ }
+
+ if (newvalid) {
+ /*
+ * Valid going from 0 to 1: update hppvlpi from tables.
+ * If IDAI is 0 we are allowed to use the info we cached in
+ * the IMPDEF area of the table.
+ * PendingLast is RES1 when we make this transition.
+ */
+ pendinglast = true;
+ } else {
+ /*
+ * Valid going from 1 to 0:
+ * Set PendingLast if there was a pending enabled interrupt
+ * for the vPE that was just descheduled.
+ * If we cache info in the IMPDEF area, write it out here.
+ */
+ pendinglast = cs->hppvlpi.prio != 0xff;
+ }
+
+ newval = FIELD_DP64(newval, GICR_VPENDBASER, PENDINGLAST, pendinglast);
+ cs->gicr_vpendbaser = newval;
+ gicv3_redist_update_vlpi(cs);
+}
+
static MemTxResult gicr_readb(GICv3CPUState *cs, hwaddr offset,
uint64_t *data, MemTxAttrs attrs)
{
@@ -234,7 +441,24 @@ static MemTxResult gicr_readl(GICv3CPUState *cs, hwaddr offset,
*data = cs->gicr_nsacr;
return MEMTX_OK;
case GICR_IDREGS ... GICR_IDREGS + 0x2f:
- *data = gicv3_idreg(offset - GICR_IDREGS);
+ *data = gicv3_idreg(cs->gic, offset - GICR_IDREGS, GICV3_PIDR0_REDIST);
+ return MEMTX_OK;
+ /*
+ * VLPI frame registers. We don't need a version check for
+ * VPROPBASER and VPENDBASER because gicv3_redist_size() will
+ * prevent pre-v4 GIC from passing us offsets this high.
+ */
+ case GICR_VPROPBASER:
+ *data = extract64(cs->gicr_vpropbaser, 0, 32);
+ return MEMTX_OK;
+ case GICR_VPROPBASER + 4:
+ *data = extract64(cs->gicr_vpropbaser, 32, 32);
+ return MEMTX_OK;
+ case GICR_VPENDBASER:
+ *data = extract64(cs->gicr_vpendbaser, 0, 32);
+ return MEMTX_OK;
+ case GICR_VPENDBASER + 4:
+ *data = extract64(cs->gicr_vpendbaser, 32, 32);
return MEMTX_OK;
default:
return MEMTX_ERROR;
@@ -379,6 +603,23 @@ static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset,
"%s: invalid guest write to RO register at offset "
TARGET_FMT_plx "\n", __func__, offset);
return MEMTX_OK;
+ /*
+ * VLPI frame registers. We don't need a version check for
+ * VPROPBASER and VPENDBASER because gicv3_redist_size() will
+ * prevent pre-v4 GIC from passing us offsets this high.
+ */
+ case GICR_VPROPBASER:
+ cs->gicr_vpropbaser = deposit64(cs->gicr_vpropbaser, 0, 32, value);
+ return MEMTX_OK;
+ case GICR_VPROPBASER + 4:
+ cs->gicr_vpropbaser = deposit64(cs->gicr_vpropbaser, 32, 32, value);
+ return MEMTX_OK;
+ case GICR_VPENDBASER:
+ gicr_write_vpendbaser(cs, deposit64(cs->gicr_vpendbaser, 0, 32, value));
+ return MEMTX_OK;
+ case GICR_VPENDBASER + 4:
+ gicr_write_vpendbaser(cs, deposit64(cs->gicr_vpendbaser, 32, 32, value));
+ return MEMTX_OK;
default:
return MEMTX_ERROR;
}
@@ -397,6 +638,17 @@ static MemTxResult gicr_readll(GICv3CPUState *cs, hwaddr offset,
case GICR_PENDBASER:
*data = cs->gicr_pendbaser;
return MEMTX_OK;
+ /*
+ * VLPI frame registers. We don't need a version check for
+ * VPROPBASER and VPENDBASER because gicv3_redist_size() will
+ * prevent pre-v4 GIC from passing us offsets this high.
+ */
+ case GICR_VPROPBASER:
+ *data = cs->gicr_vpropbaser;
+ return MEMTX_OK;
+ case GICR_VPENDBASER:
+ *data = cs->gicr_vpendbaser;
+ return MEMTX_OK;
default:
return MEMTX_ERROR;
}
@@ -418,6 +670,17 @@ static MemTxResult gicr_writell(GICv3CPUState *cs, hwaddr offset,
"%s: invalid guest write to RO register at offset "
TARGET_FMT_plx "\n", __func__, offset);
return MEMTX_OK;
+ /*
+ * VLPI frame registers. We don't need a version check for
+ * VPROPBASER and VPENDBASER because gicv3_redist_size() will
+ * prevent pre-v4 GIC from passing us offsets this high.
+ */
+ case GICR_VPROPBASER:
+ cs->gicr_vpropbaser = value;
+ return MEMTX_OK;
+ case GICR_VPENDBASER:
+ gicr_write_vpendbaser(cs, value);
+ return MEMTX_OK;
default:
return MEMTX_ERROR;
}
@@ -442,8 +705,8 @@ MemTxResult gicv3_redist_read(void *opaque, hwaddr offset, uint64_t *data,
* in the memory map); if so then the GIC has multiple MemoryRegions
* for the redistributors.
*/
- cpuidx = region->cpuidx + offset / GICV3_REDIST_SIZE;
- offset %= GICV3_REDIST_SIZE;
+ cpuidx = region->cpuidx + offset / gicv3_redist_size(s);
+ offset %= gicv3_redist_size(s);
cs = &s->cpu[cpuidx];
@@ -501,8 +764,8 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
* in the memory map); if so then the GIC has multiple MemoryRegions
* for the redistributors.
*/
- cpuidx = region->cpuidx + offset / GICV3_REDIST_SIZE;
- offset %= GICV3_REDIST_SIZE;
+ cpuidx = region->cpuidx + offset / gicv3_redist_size(s);
+ offset %= gicv3_redist_size(s);
cs = &s->cpu[cpuidx];
@@ -542,34 +805,11 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
static void gicv3_redist_check_lpi_priority(GICv3CPUState *cs, int irq)
{
- AddressSpace *as = &cs->gic->dma_as;
- uint64_t lpict_baddr;
- uint8_t lpite;
- uint8_t prio;
-
- lpict_baddr = cs->gicr_propbaser & R_GICR_PROPBASER_PHYADDR_MASK;
+ uint64_t lpict_baddr = cs->gicr_propbaser & R_GICR_PROPBASER_PHYADDR_MASK;
- address_space_read(as, lpict_baddr + ((irq - GICV3_LPI_INTID_START) *
- sizeof(lpite)), MEMTXATTRS_UNSPECIFIED, &lpite,
- sizeof(lpite));
-
- if (!(lpite & LPI_CTE_ENABLED)) {
- return;
- }
-
- if (cs->gic->gicd_ctlr & GICD_CTLR_DS) {
- prio = lpite & LPI_PRIORITY_MASK;
- } else {
- prio = ((lpite & LPI_PRIORITY_MASK) >> 1) | 0x80;
- }
-
- if ((prio < cs->hpplpi.prio) ||
- ((prio == cs->hpplpi.prio) && (irq <= cs->hpplpi.irq))) {
- cs->hpplpi.irq = irq;
- cs->hpplpi.prio = prio;
- /* LPIs are always non-secure Grp1 interrupts */
- cs->hpplpi.grp = GICV3_G1NS;
- }
+ update_for_one_lpi(cs, irq, lpict_baddr,
+ cs->gic->gicd_ctlr & GICD_CTLR_DS,
+ &cs->hpplpi);
}
void gicv3_redist_update_lpi_only(GICv3CPUState *cs)
@@ -581,11 +821,7 @@ void gicv3_redist_update_lpi_only(GICv3CPUState *cs)
* priority is lower than the last computed high priority lpi interrupt.
* If yes, replace current LPI as the new high priority lpi interrupt.
*/
- AddressSpace *as = &cs->gic->dma_as;
- uint64_t lpipt_baddr;
- uint32_t pendt_size = 0;
- uint8_t pend;
- int i, bit;
+ uint64_t lpipt_baddr, lpict_baddr;
uint64_t idbits;
idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS),
@@ -595,23 +831,11 @@ void gicv3_redist_update_lpi_only(GICv3CPUState *cs)
return;
}
- cs->hpplpi.prio = 0xff;
-
lpipt_baddr = cs->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
+ lpict_baddr = cs->gicr_propbaser & R_GICR_PROPBASER_PHYADDR_MASK;
- /* Determine the highest priority pending interrupt among LPIs */
- pendt_size = (1ULL << (idbits + 1));
-
- for (i = GICV3_LPI_INTID_START / 8; i < pendt_size / 8; i++) {
- address_space_read(as, lpipt_baddr + i, MEMTXATTRS_UNSPECIFIED, &pend,
- sizeof(pend));
-
- while (pend) {
- bit = ctz32(pend);
- gicv3_redist_check_lpi_priority(cs, i * 8 + bit);
- pend &= ~(1 << bit);
- }
- }
+ update_for_all_lpis(cs, lpipt_baddr, lpict_baddr, idbits,
+ cs->gic->gicd_ctlr & GICD_CTLR_DS, &cs->hpplpi);
}
void gicv3_redist_update_lpi(GICv3CPUState *cs)
@@ -626,30 +850,13 @@ void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level)
* This function updates the pending bit in lpi pending table for
* the irq being activated or deactivated.
*/
- AddressSpace *as = &cs->gic->dma_as;
uint64_t lpipt_baddr;
- bool ispend = false;
- uint8_t pend;
- /*
- * get the bit value corresponding to this irq in the
- * lpi pending table
- */
lpipt_baddr = cs->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
-
- address_space_read(as, lpipt_baddr + ((irq / 8) * sizeof(pend)),
- MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend));
-
- ispend = extract32(pend, irq % 8, 1);
-
- /* no change in the value of pending bit, return */
- if (ispend == level) {
+ if (!set_pending_table_bit(cs, lpipt_baddr, irq, level)) {
+ /* no change in the value of pending bit, return */
return;
}
- pend = deposit32(pend, irq % 8, 1, level ? 1 : 0);
-
- address_space_write(as, lpipt_baddr + ((irq / 8) * sizeof(pend)),
- MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend));
/*
* check if this LPI is better than the current hpplpi, if yes
@@ -681,6 +888,17 @@ void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level)
gicv3_redist_lpi_pending(cs, irq, level);
}
+void gicv3_redist_inv_lpi(GICv3CPUState *cs, int irq)
+{
+ /*
+ * The only cached information for LPIs we have is the HPPLPI.
+ * We could be cleverer about identifying when we don't need
+ * to do a full rescan of the pending table, but until we find
+ * this is a performance issue, just always recalculate.
+ */
+ gicv3_redist_update_lpi(cs);
+}
+
void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq)
{
/*
@@ -691,11 +909,9 @@ void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq)
* we choose to NOP. If LPIs are disabled on source there's nothing
* to be transferred anyway.
*/
- AddressSpace *as = &src->gic->dma_as;
uint64_t idbits;
uint32_t pendt_size;
uint64_t src_baddr;
- uint8_t src_pend;
if (!(src->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) ||
!(dest->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) {
@@ -714,15 +930,10 @@ void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq)
src_baddr = src->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
- address_space_read(as, src_baddr + (irq / 8),
- MEMTXATTRS_UNSPECIFIED, &src_pend, sizeof(src_pend));
- if (!extract32(src_pend, irq % 8, 1)) {
+ if (!set_pending_table_bit(src, src_baddr, irq, 0)) {
/* Not pending on source, nothing to do */
return;
}
- src_pend &= ~(1 << (irq % 8));
- address_space_write(as, src_baddr + (irq / 8),
- MEMTXATTRS_UNSPECIFIED, &src_pend, sizeof(src_pend));
if (irq == src->hpplpi.irq) {
/*
* We just made this LPI not-pending so only need to update
@@ -788,6 +999,117 @@ void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest)
gicv3_redist_update_lpi(dest);
}
+void gicv3_redist_vlpi_pending(GICv3CPUState *cs, int irq, int level)
+{
+ /*
+ * Change the pending state of the specified vLPI.
+ * Unlike gicv3_redist_process_vlpi(), we know here that the
+ * vCPU is definitely resident on this redistributor, and that
+ * the irq is in range.
+ */
+ uint64_t vptbase, ctbase;
+
+ vptbase = FIELD_EX64(cs->gicr_vpendbaser, GICR_VPENDBASER, PHYADDR) << 16;
+
+ if (set_pending_table_bit(cs, vptbase, irq, level)) {
+ if (level) {
+ /* Check whether this vLPI is now the best */
+ ctbase = cs->gicr_vpropbaser & R_GICR_VPROPBASER_PHYADDR_MASK;
+ update_for_one_lpi(cs, irq, ctbase, true, &cs->hppvlpi);
+ gicv3_cpuif_virt_irq_fiq_update(cs);
+ } else {
+ /* Only need to recalculate if this was previously the best vLPI */
+ if (irq == cs->hppvlpi.irq) {
+ gicv3_redist_update_vlpi(cs);
+ }
+ }
+ }
+}
+
+void gicv3_redist_process_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr,
+ int doorbell, int level)
+{
+ bool bit_changed;
+ bool resident = vcpu_resident(cs, vptaddr);
+ uint64_t ctbase;
+
+ if (resident) {
+ uint32_t idbits = FIELD_EX64(cs->gicr_vpropbaser, GICR_VPROPBASER, IDBITS);
+ if (irq >= (1ULL << (idbits + 1))) {
+ return;
+ }
+ }
+
+ bit_changed = set_pending_table_bit(cs, vptaddr, irq, level);
+ if (resident && bit_changed) {
+ if (level) {
+ /* Check whether this vLPI is now the best */
+ ctbase = cs->gicr_vpropbaser & R_GICR_VPROPBASER_PHYADDR_MASK;
+ update_for_one_lpi(cs, irq, ctbase, true, &cs->hppvlpi);
+ gicv3_cpuif_virt_irq_fiq_update(cs);
+ } else {
+ /* Only need to recalculate if this was previously the best vLPI */
+ if (irq == cs->hppvlpi.irq) {
+ gicv3_redist_update_vlpi(cs);
+ }
+ }
+ }
+
+ if (!resident && level && doorbell != INTID_SPURIOUS &&
+ (cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) {
+ /* vCPU is not currently resident: ring the doorbell */
+ gicv3_redist_process_lpi(cs, doorbell, 1);
+ }
+}
+
+void gicv3_redist_mov_vlpi(GICv3CPUState *src, uint64_t src_vptaddr,
+ GICv3CPUState *dest, uint64_t dest_vptaddr,
+ int irq, int doorbell)
+{
+ /*
+ * Move the specified vLPI's pending state from the source redistributor
+ * to the destination.
+ */
+ if (!set_pending_table_bit(src, src_vptaddr, irq, 0)) {
+ /* Not pending on source, nothing to do */
+ return;
+ }
+ if (vcpu_resident(src, src_vptaddr) && irq == src->hppvlpi.irq) {
+ /*
+ * Update src's cached highest-priority pending vLPI if we just made
+ * it not-pending
+ */
+ gicv3_redist_update_vlpi(src);
+ }
+ /*
+ * Mark the vLPI pending on the destination (ringing the doorbell
+ * if the vCPU isn't resident)
+ */
+ gicv3_redist_process_vlpi(dest, irq, dest_vptaddr, doorbell, irq);
+}
+
+void gicv3_redist_vinvall(GICv3CPUState *cs, uint64_t vptaddr)
+{
+ if (!vcpu_resident(cs, vptaddr)) {
+ /* We don't have anything cached if the vCPU isn't resident */
+ return;
+ }
+
+ /* Otherwise, our only cached information is the HPPVLPI info */
+ gicv3_redist_update_vlpi(cs);
+}
+
+void gicv3_redist_inv_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr)
+{
+ /*
+ * The only cached information for LPIs we have is the HPPLPI.
+ * We could be cleverer about identifying when we don't need
+ * to do a full rescan of the pending table, but until we find
+ * this is a performance issue, just always recalculate.
+ */
+ gicv3_redist_vinvall(cs, vptaddr);
+}
+
void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level)
{
/* Update redistributor state for a change in an external PPI input line */
diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 2bf1baef04..29d5cdc1b6 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -77,6 +77,7 @@
* Redistributor frame offsets from RD_base
*/
#define GICR_SGI_OFFSET 0x10000
+#define GICR_VLPI_OFFSET 0x20000
/*
* Redistributor registers, offsets from RD_base
@@ -109,6 +110,10 @@
#define GICR_IGRPMODR0 (GICR_SGI_OFFSET + 0x0D00)
#define GICR_NSACR (GICR_SGI_OFFSET + 0x0E00)
+/* VLPI redistributor registers, offsets from VLPI_base */
+#define GICR_VPROPBASER (GICR_VLPI_OFFSET + 0x70)
+#define GICR_VPENDBASER (GICR_VLPI_OFFSET + 0x78)
+
#define GICR_CTLR_ENABLE_LPIS (1U << 0)
#define GICR_CTLR_CES (1U << 1)
#define GICR_CTLR_RWP (1U << 3)
@@ -143,6 +148,22 @@ FIELD(GICR_PENDBASER, PTZ, 62, 1)
#define GICR_PROPBASER_IDBITS_THRESHOLD 0xd
+/* These are the GICv4 VPROPBASER and VPENDBASER layouts; v4.1 is different */
+FIELD(GICR_VPROPBASER, IDBITS, 0, 5)
+FIELD(GICR_VPROPBASER, INNERCACHE, 7, 3)
+FIELD(GICR_VPROPBASER, SHAREABILITY, 10, 2)
+FIELD(GICR_VPROPBASER, PHYADDR, 12, 40)
+FIELD(GICR_VPROPBASER, OUTERCACHE, 56, 3)
+
+FIELD(GICR_VPENDBASER, INNERCACHE, 7, 3)
+FIELD(GICR_VPENDBASER, SHAREABILITY, 10, 2)
+FIELD(GICR_VPENDBASER, PHYADDR, 16, 36)
+FIELD(GICR_VPENDBASER, OUTERCACHE, 56, 3)
+FIELD(GICR_VPENDBASER, DIRTY, 60, 1)
+FIELD(GICR_VPENDBASER, PENDINGLAST, 61, 1)
+FIELD(GICR_VPENDBASER, IDAI, 62, 1)
+FIELD(GICR_VPENDBASER, VALID, 63, 1)
+
#define ICC_CTLR_EL1_CBPR (1U << 0)
#define ICC_CTLR_EL1_EOIMODE (1U << 1)
#define ICC_CTLR_EL1_PMHE (1U << 6)
@@ -280,6 +301,7 @@ FIELD(GITS_CTLR, ENABLED, 0, 1)
FIELD(GITS_CTLR, QUIESCENT, 31, 1)
FIELD(GITS_TYPER, PHYSICAL, 0, 1)
+FIELD(GITS_TYPER, VIRTUAL, 1, 1)
FIELD(GITS_TYPER, ITT_ENTRY_SIZE, 4, 4)
FIELD(GITS_TYPER, IDBITS, 8, 5)
FIELD(GITS_TYPER, DEVBITS, 13, 5)
@@ -287,6 +309,7 @@ FIELD(GITS_TYPER, SEIS, 18, 1)
FIELD(GITS_TYPER, PTA, 19, 1)
FIELD(GITS_TYPER, CIDBITS, 32, 4)
FIELD(GITS_TYPER, CIL, 36, 1)
+FIELD(GITS_TYPER, VMOVP, 37, 1)
#define GITS_IDREGS 0xFFD0
@@ -298,6 +321,7 @@ FIELD(GITS_TYPER, CIL, 36, 1)
#define GITS_BASER_PAGESIZE_64K 2
#define GITS_BASER_TYPE_DEVICE 1ULL
+#define GITS_BASER_TYPE_VPE 2ULL
#define GITS_BASER_TYPE_COLLECTION 4ULL
#define GITS_PAGE_SIZE_4K 0x1000
@@ -327,6 +351,13 @@ FIELD(GITS_TYPER, CIL, 36, 1)
#define GITS_CMD_INVALL 0x0D
#define GITS_CMD_MOVALL 0x0E
#define GITS_CMD_DISCARD 0x0F
+#define GITS_CMD_VMOVI 0x21
+#define GITS_CMD_VMOVP 0x22
+#define GITS_CMD_VSYNC 0x25
+#define GITS_CMD_VMAPP 0x29
+#define GITS_CMD_VMAPTI 0x2A
+#define GITS_CMD_VMAPI 0x2B
+#define GITS_CMD_VINVALL 0x2D
/* MAPC command fields */
#define ICID_LENGTH 16
@@ -366,6 +397,46 @@ FIELD(MOVI_0, DEVICEID, 32, 32)
FIELD(MOVI_1, EVENTID, 0, 32)
FIELD(MOVI_2, ICID, 0, 16)
+/* INV command fields */
+FIELD(INV_0, DEVICEID, 32, 32)
+FIELD(INV_1, EVENTID, 0, 32)
+
+/* VMAPI, VMAPTI command fields */
+FIELD(VMAPTI_0, DEVICEID, 32, 32)
+FIELD(VMAPTI_1, EVENTID, 0, 32)
+FIELD(VMAPTI_1, VPEID, 32, 16)
+FIELD(VMAPTI_2, VINTID, 0, 32) /* VMAPTI only */
+FIELD(VMAPTI_2, DOORBELL, 32, 32)
+
+/* VMAPP command fields */
+FIELD(VMAPP_0, ALLOC, 8, 1) /* GICv4.1 only */
+FIELD(VMAPP_0, PTZ, 9, 1) /* GICv4.1 only */
+FIELD(VMAPP_0, VCONFADDR, 16, 36) /* GICv4.1 only */
+FIELD(VMAPP_1, DEFAULT_DOORBELL, 0, 32) /* GICv4.1 only */
+FIELD(VMAPP_1, VPEID, 32, 16)
+FIELD(VMAPP_2, RDBASE, 16, 36)
+FIELD(VMAPP_2, V, 63, 1)
+FIELD(VMAPP_3, VPTSIZE, 0, 8) /* For GICv4.0, bits [7:6] are RES0 */
+FIELD(VMAPP_3, VPTADDR, 16, 36)
+
+/* VMOVP command fields */
+FIELD(VMOVP_0, SEQNUM, 32, 16) /* not used for GITS_TYPER.VMOVP == 1 */
+FIELD(VMOVP_1, ITSLIST, 0, 16) /* not used for GITS_TYPER.VMOVP == 1 */
+FIELD(VMOVP_1, VPEID, 32, 16)
+FIELD(VMOVP_2, RDBASE, 16, 36)
+FIELD(VMOVP_2, DB, 63, 1) /* GICv4.1 only */
+FIELD(VMOVP_3, DEFAULT_DOORBELL, 0, 32) /* GICv4.1 only */
+
+/* VMOVI command fields */
+FIELD(VMOVI_0, DEVICEID, 32, 32)
+FIELD(VMOVI_1, EVENTID, 0, 32)
+FIELD(VMOVI_1, VPEID, 32, 16)
+FIELD(VMOVI_2, D, 0, 1)
+FIELD(VMOVI_2, DOORBELL, 32, 32)
+
+/* VINVALL command fields */
+FIELD(VINVALL_1, VPEID, 32, 16)
+
/*
* 12 bytes Interrupt translation Table Entry size
* as per Table 5.3 in GICv3 spec
@@ -419,6 +490,20 @@ FIELD(DTE, ITTADDR, 6, 44)
FIELD(CTE, VALID, 0, 1)
FIELD(CTE, RDBASE, 1, RDBASE_PROCNUM_LENGTH)
+/*
+ * 8 bytes VPE table entry size:
+ * Valid = 1 bit, VPTsize = 5 bits, VPTaddr = 36 bits, RDbase = 16 bits
+ *
+ * Field sizes for Valid and size are mandated; field sizes for RDbase
+ * and VPT_addr are IMPDEF.
+ */
+#define GITS_VPE_SIZE 0x8ULL
+
+FIELD(VTE, VALID, 0, 1)
+FIELD(VTE, VPTSIZE, 1, 5)
+FIELD(VTE, VPTADDR, 6, 36)
+FIELD(VTE, RDBASE, 42, RDBASE_PROCNUM_LENGTH)
+
/* Special interrupt IDs */
#define INTID_SECURE 1020
#define INTID_NONSECURE 1021
@@ -427,6 +512,27 @@ FIELD(CTE, RDBASE, 1, RDBASE_PROCNUM_LENGTH)
/* Functions internal to the emulated GICv3 */
/**
+ * gicv3_redist_size:
+ * @s: GICv3State
+ *
+ * Return the size of the redistributor register frame in bytes
+ * (which depends on what GIC version this is)
+ */
+static inline int gicv3_redist_size(GICv3State *s)
+{
+ /*
+ * Redistributor size is controlled by the redistributor GICR_TYPER.VLPIS.
+ * It's the same for every redistributor in the GIC, so arbitrarily
+ * use the register field in the first one.
+ */
+ if (s->cpu[0].gicr_typer & GICR_TYPER_VLPIS) {
+ return GICV4_REDIST_SIZE;
+ } else {
+ return GICV3_REDIST_SIZE;
+ }
+}
+
+/**
* gicv3_intid_is_special:
* @intid: interrupt ID
*
@@ -490,6 +596,36 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
void gicv3_dist_set_irq(GICv3State *s, int irq, int level);
void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level);
void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level);
+/**
+ * gicv3_redist_process_vlpi:
+ * @cs: GICv3CPUState
+ * @irq: (virtual) interrupt number
+ * @vptaddr: (guest) address of VLPI table
+ * @doorbell: doorbell (physical) interrupt number (1023 for "no doorbell")
+ * @level: level to set @irq to
+ *
+ * Process a virtual LPI being directly injected by the ITS. This function
+ * will update the VLPI table specified by @vptaddr and @vptsize. If the
+ * vCPU corresponding to that VLPI table is currently running on
+ * the CPU associated with this redistributor, directly inject the VLPI
+ * @irq. If the vCPU is not running on this CPU, raise the doorbell
+ * interrupt instead.
+ */
+void gicv3_redist_process_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr,
+ int doorbell, int level);
+/**
+ * gicv3_redist_vlpi_pending:
+ * @cs: GICv3CPUState
+ * @irq: (virtual) interrupt number
+ * @level: level to set @irq to
+ *
+ * Set/clear the pending status of a virtual LPI in the vLPI table
+ * that this redistributor is currently using. (The difference between
+ * this and gicv3_redist_process_vlpi() is that this is called from
+ * the cpuif and does not need to do the not-running-on-this-vcpu checks.)
+ */
+void gicv3_redist_vlpi_pending(GICv3CPUState *cs, int irq, int level);
+
void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level);
/**
* gicv3_redist_update_lpi:
@@ -510,6 +646,23 @@ void gicv3_redist_update_lpi(GICv3CPUState *cs);
*/
void gicv3_redist_update_lpi_only(GICv3CPUState *cs);
/**
+ * gicv3_redist_inv_lpi:
+ * @cs: GICv3CPUState
+ * @irq: LPI to invalidate cached information for
+ *
+ * Forget or update any cached information associated with this LPI.
+ */
+void gicv3_redist_inv_lpi(GICv3CPUState *cs, int irq);
+/**
+ * gicv3_redist_inv_vlpi:
+ * @cs: GICv3CPUState
+ * @irq: vLPI to invalidate cached information for
+ * @vptaddr: (guest) address of vLPI table
+ *
+ * Forget or update any cached information associated with this vLPI.
+ */
+void gicv3_redist_inv_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr);
+/**
* gicv3_redist_mov_lpi:
* @src: source redistributor
* @dest: destination redistributor
@@ -529,6 +682,30 @@ void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq);
* by the ITS MOVALL command.
*/
void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest);
+/**
+ * gicv3_redist_mov_vlpi:
+ * @src: source redistributor
+ * @src_vptaddr: (guest) address of source VLPI table
+ * @dest: destination redistributor
+ * @dest_vptaddr: (guest) address of destination VLPI table
+ * @irq: VLPI to update
+ * @doorbell: doorbell for destination (1023 for "no doorbell")
+ *
+ * Move the pending state of the specified VLPI from @src to @dest,
+ * as required by the ITS VMOVI command.
+ */
+void gicv3_redist_mov_vlpi(GICv3CPUState *src, uint64_t src_vptaddr,
+ GICv3CPUState *dest, uint64_t dest_vptaddr,
+ int irq, int doorbell);
+/**
+ * gicv3_redist_vinvall:
+ * @cs: GICv3CPUState
+ * @vptaddr: address of VLPI pending table
+ *
+ * On redistributor @cs, invalidate all cached information associated
+ * with the vCPU defined by @vptaddr.
+ */
+void gicv3_redist_vinvall(GICv3CPUState *cs, uint64_t vptaddr);
void gicv3_redist_send_sgi(GICv3CPUState *cs, int grp, int irq, bool ns);
void gicv3_init_cpuif(GICv3State *s);
@@ -544,6 +721,17 @@ void gicv3_init_cpuif(GICv3State *s);
*/
void gicv3_cpuif_update(GICv3CPUState *cs);
+/*
+ * gicv3_cpuif_virt_irq_fiq_update:
+ * @cs: GICv3CPUState for the CPU to update
+ *
+ * Recalculate whether to assert the virtual IRQ or FIQ lines after
+ * a change to the current highest priority pending virtual interrupt.
+ * Note that this does not recalculate and change the maintenance
+ * interrupt status (for that, see gicv3_cpuif_virt_update()).
+ */
+void gicv3_cpuif_virt_irq_fiq_update(GICv3CPUState *cs);
+
static inline uint32_t gicv3_iidr(void)
{
/* Return the Implementer Identification Register value
@@ -555,17 +743,34 @@ static inline uint32_t gicv3_iidr(void)
return 0x43b;
}
-static inline uint32_t gicv3_idreg(int regoffset)
+/* CoreSight PIDR0 values for ARM GICv3 implementations */
+#define GICV3_PIDR0_DIST 0x92
+#define GICV3_PIDR0_REDIST 0x93
+#define GICV3_PIDR0_ITS 0x94
+
+static inline uint32_t gicv3_idreg(GICv3State *s, int regoffset, uint8_t pidr0)
{
/* Return the value of the CoreSight ID register at the specified
* offset from the first ID register (as found in the distributor
* and redistributor register banks).
- * These values indicate an ARM implementation of a GICv3.
+ * These values indicate an ARM implementation of a GICv3 or v4.
*/
static const uint8_t gicd_ids[] = {
- 0x44, 0x00, 0x00, 0x00, 0x92, 0xB4, 0x3B, 0x00, 0x0D, 0xF0, 0x05, 0xB1
+ 0x44, 0x00, 0x00, 0x00, 0x92, 0xB4, 0x0B, 0x00, 0x0D, 0xF0, 0x05, 0xB1
};
- return gicd_ids[regoffset / 4];
+ uint32_t id;
+
+ regoffset /= 4;
+
+ if (regoffset == 4) {
+ return pidr0;
+ }
+ id = gicd_ids[regoffset];
+ if (regoffset == 6) {
+ /* PIDR2 bits [7:4] are the GIC architecture revision */
+ id |= s->revision << 4;
+ }
+ return id;
}
/**
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index d6d012fb26..8b35139f82 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -62,3 +62,4 @@ specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'],
if_true: files('spapr_xive_kvm.c'))
specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c'))
specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c'))
+specific_ss.add(when: 'CONFIG_NIOS2_VIC', if_true: files('nios2_vic.c'))
diff --git a/hw/intc/nios2_vic.c b/hw/intc/nios2_vic.c
new file mode 100644
index 0000000000..cf63212a88
--- /dev/null
+++ b/hw/intc/nios2_vic.c
@@ -0,0 +1,313 @@
+/*
+ * Vectored Interrupt Controller for nios2 processor
+ *
+ * Copyright (c) 2022 Neuroblade
+ *
+ * Interface:
+ * QOM property "cpu": link to the Nios2 CPU (must be set)
+ * Unnamed GPIO inputs 0..NIOS2_VIC_MAX_IRQ-1: input IRQ lines
+ * IRQ should be connected to nios2 IRQ0.
+ *
+ * Reference: "Embedded Peripherals IP User Guide
+ * for Intel® Quartus® Prime Design Suite: 21.4"
+ * Chapter 38 "Vectored Interrupt Controller Core"
+ * See: https://www.intel.com/content/www/us/en/docs/programmable/683130/21-4/vectored-interrupt-controller-core.html
+ *
+ * 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 "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+#include "qom/object.h"
+#include "hw/intc/nios2_vic.h"
+#include "cpu.h"
+
+
+enum {
+ INT_CONFIG0 = 0,
+ INT_CONFIG31 = 31,
+ INT_ENABLE = 32,
+ INT_ENABLE_SET = 33,
+ INT_ENABLE_CLR = 34,
+ INT_PENDING = 35,
+ INT_RAW_STATUS = 36,
+ SW_INTERRUPT = 37,
+ SW_INTERRUPT_SET = 38,
+ SW_INTERRUPT_CLR = 39,
+ VIC_CONFIG = 40,
+ VIC_STATUS = 41,
+ VEC_TBL_BASE = 42,
+ VEC_TBL_ADDR = 43,
+ CSR_COUNT /* Last! */
+};
+
+/* Requested interrupt level (INT_CONFIG[0:5]) */
+static inline uint32_t vic_int_config_ril(const Nios2VIC *vic, int irq_num)
+{
+ return extract32(vic->int_config[irq_num], 0, 6);
+}
+
+/* Requested NMI (INT_CONFIG[6]) */
+static inline uint32_t vic_int_config_rnmi(const Nios2VIC *vic, int irq_num)
+{
+ return extract32(vic->int_config[irq_num], 6, 1);
+}
+
+/* Requested register set (INT_CONFIG[7:12]) */
+static inline uint32_t vic_int_config_rrs(const Nios2VIC *vic, int irq_num)
+{
+ return extract32(vic->int_config[irq_num], 7, 6);
+}
+
+static inline uint32_t vic_config_vec_size(const Nios2VIC *vic)
+{
+ return 1 << (2 + extract32(vic->vic_config, 0, 3));
+}
+
+static inline uint32_t vic_int_pending(const Nios2VIC *vic)
+{
+ return (vic->int_raw_status | vic->sw_int) & vic->int_enable;
+}
+
+static void vic_update_irq(Nios2VIC *vic)
+{
+ Nios2CPU *cpu = NIOS2_CPU(vic->cpu);
+ uint32_t pending = vic_int_pending(vic);
+ int irq = -1;
+ int max_ril = 0;
+ /* Note that if RIL is 0 for an interrupt it is effectively disabled */
+
+ vic->vec_tbl_addr = 0;
+ vic->vic_status = 0;
+
+ if (pending == 0) {
+ qemu_irq_lower(vic->output_int);
+ return;
+ }
+
+ for (int i = 0; i < NIOS2_VIC_MAX_IRQ; i++) {
+ if (pending & BIT(i)) {
+ int ril = vic_int_config_ril(vic, i);
+ if (ril > max_ril) {
+ irq = i;
+ max_ril = ril;
+ }
+ }
+ }
+
+ if (irq < 0) {
+ qemu_irq_lower(vic->output_int);
+ return;
+ }
+
+ vic->vec_tbl_addr = irq * vic_config_vec_size(vic) + vic->vec_tbl_base;
+ vic->vic_status = irq | BIT(31);
+
+ /*
+ * In hardware, the interface between the VIC and the CPU is via the
+ * External Interrupt Controller interface, where the interrupt controller
+ * presents the CPU with a packet of data containing:
+ * - Requested Handler Address (RHA): 32 bits
+ * - Requested Register Set (RRS) : 6 bits
+ * - Requested Interrupt Level (RIL) : 6 bits
+ * - Requested NMI flag (RNMI) : 1 bit
+ * In our emulation, we implement this by writing the data directly to
+ * fields in the CPU object and then raising the IRQ line to tell
+ * the CPU that we've done so.
+ */
+
+ cpu->rha = vic->vec_tbl_addr;
+ cpu->ril = max_ril;
+ cpu->rrs = vic_int_config_rrs(vic, irq);
+ cpu->rnmi = vic_int_config_rnmi(vic, irq);
+
+ qemu_irq_raise(vic->output_int);
+}
+
+static void vic_set_irq(void *opaque, int irq_num, int level)
+{
+ Nios2VIC *vic = opaque;
+
+ vic->int_raw_status = deposit32(vic->int_raw_status, irq_num, 1, !!level);
+ vic_update_irq(vic);
+}
+
+static void nios2_vic_reset(DeviceState *dev)
+{
+ Nios2VIC *vic = NIOS2_VIC(dev);
+
+ memset(&vic->int_config, 0, sizeof(vic->int_config));
+ vic->vic_config = 0;
+ vic->int_raw_status = 0;
+ vic->int_enable = 0;
+ vic->sw_int = 0;
+ vic->vic_status = 0;
+ vic->vec_tbl_base = 0;
+ vic->vec_tbl_addr = 0;
+}
+
+static uint64_t nios2_vic_csr_read(void *opaque, hwaddr offset, unsigned size)
+{
+ Nios2VIC *vic = opaque;
+ int index = offset / 4;
+
+ switch (index) {
+ case INT_CONFIG0 ... INT_CONFIG31:
+ return vic->int_config[index - INT_CONFIG0];
+ case INT_ENABLE:
+ return vic->int_enable;
+ case INT_PENDING:
+ return vic_int_pending(vic);
+ case INT_RAW_STATUS:
+ return vic->int_raw_status;
+ case SW_INTERRUPT:
+ return vic->sw_int;
+ case VIC_CONFIG:
+ return vic->vic_config;
+ case VIC_STATUS:
+ return vic->vic_status;
+ case VEC_TBL_BASE:
+ return vic->vec_tbl_base;
+ case VEC_TBL_ADDR:
+ return vic->vec_tbl_addr;
+ default:
+ return 0;
+ }
+}
+
+static void nios2_vic_csr_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ Nios2VIC *vic = opaque;
+ int index = offset / 4;
+
+ switch (index) {
+ case INT_CONFIG0 ... INT_CONFIG31:
+ vic->int_config[index - INT_CONFIG0] = value;
+ break;
+ case INT_ENABLE:
+ vic->int_enable = value;
+ break;
+ case INT_ENABLE_SET:
+ vic->int_enable |= value;
+ break;
+ case INT_ENABLE_CLR:
+ vic->int_enable &= ~value;
+ break;
+ case SW_INTERRUPT:
+ vic->sw_int = value;
+ break;
+ case SW_INTERRUPT_SET:
+ vic->sw_int |= value;
+ break;
+ case SW_INTERRUPT_CLR:
+ vic->sw_int &= ~value;
+ break;
+ case VIC_CONFIG:
+ vic->vic_config = value;
+ break;
+ case VEC_TBL_BASE:
+ vic->vec_tbl_base = value;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "nios2-vic: write to invalid CSR address %#"
+ HWADDR_PRIx "\n", offset);
+ }
+
+ vic_update_irq(vic);
+}
+
+static const MemoryRegionOps nios2_vic_csr_ops = {
+ .read = nios2_vic_csr_read,
+ .write = nios2_vic_csr_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = { .min_access_size = 4, .max_access_size = 4 }
+};
+
+static void nios2_vic_realize(DeviceState *dev, Error **errp)
+{
+ Nios2VIC *vic = NIOS2_VIC(dev);
+
+ if (!vic->cpu) {
+ /* This is a programming error in the code using this device */
+ error_setg(errp, "nios2-vic 'cpu' link property was not set");
+ return;
+ }
+
+ sysbus_init_irq(SYS_BUS_DEVICE(dev), &vic->output_int);
+ qdev_init_gpio_in(dev, vic_set_irq, NIOS2_VIC_MAX_IRQ);
+
+ memory_region_init_io(&vic->csr, OBJECT(dev), &nios2_vic_csr_ops, vic,
+ "nios2.vic.csr", CSR_COUNT * sizeof(uint32_t));
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &vic->csr);
+}
+
+static Property nios2_vic_properties[] = {
+ DEFINE_PROP_LINK("cpu", Nios2VIC, cpu, TYPE_CPU, CPUState *),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static const VMStateDescription nios2_vic_vmstate = {
+ .name = "nios2-vic",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]){
+ VMSTATE_UINT32_ARRAY(int_config, Nios2VIC, 32),
+ VMSTATE_UINT32(vic_config, Nios2VIC),
+ VMSTATE_UINT32(int_raw_status, Nios2VIC),
+ VMSTATE_UINT32(int_enable, Nios2VIC),
+ VMSTATE_UINT32(sw_int, Nios2VIC),
+ VMSTATE_UINT32(vic_status, Nios2VIC),
+ VMSTATE_UINT32(vec_tbl_base, Nios2VIC),
+ VMSTATE_UINT32(vec_tbl_addr, Nios2VIC),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static void nios2_vic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = nios2_vic_reset;
+ dc->realize = nios2_vic_realize;
+ dc->vmsd = &nios2_vic_vmstate;
+ device_class_set_props(dc, nios2_vic_properties);
+}
+
+static const TypeInfo nios2_vic_info = {
+ .name = TYPE_NIOS2_VIC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Nios2VIC),
+ .class_init = nios2_vic_class_init,
+};
+
+static void nios2_vic_register_types(void)
+{
+ type_register_static(&nios2_vic_info);
+}
+
+type_init(nios2_vic_register_types);
diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c
index e43b050e92..0412edc982 100644
--- a/hw/intc/riscv_aclint.c
+++ b/hw/intc/riscv_aclint.c
@@ -38,12 +38,18 @@ typedef struct riscv_aclint_mtimer_callback {
int num;
} riscv_aclint_mtimer_callback;
-static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq)
+static uint64_t cpu_riscv_read_rtc_raw(uint32_t timebase_freq)
{
return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL),
timebase_freq, NANOSECONDS_PER_SECOND);
}
+static uint64_t cpu_riscv_read_rtc(void *opaque)
+{
+ RISCVAclintMTimerState *mtimer = opaque;
+ return cpu_riscv_read_rtc_raw(mtimer->timebase_freq) + mtimer->time_delta;
+}
+
/*
* Called when timecmp is written to update the QEMU timer or immediately
* trigger timer interrupt if mtimecmp <= current timer value.
@@ -51,13 +57,13 @@ static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq)
static void riscv_aclint_mtimer_write_timecmp(RISCVAclintMTimerState *mtimer,
RISCVCPU *cpu,
int hartid,
- uint64_t value,
- uint32_t timebase_freq)
+ uint64_t value)
{
+ uint32_t timebase_freq = mtimer->timebase_freq;
uint64_t next;
uint64_t diff;
- uint64_t rtc_r = cpu_riscv_read_rtc(timebase_freq);
+ uint64_t rtc_r = cpu_riscv_read_rtc(mtimer);
cpu->env.timecmp = value;
if (cpu->env.timecmp <= rtc_r) {
@@ -126,9 +132,9 @@ static uint64_t riscv_aclint_mtimer_read(void *opaque, hwaddr addr,
qemu_log_mask(LOG_GUEST_ERROR,
"aclint-mtimer: invalid hartid: %zu", hartid);
} else if ((addr & 0x7) == 0) {
- /* timecmp_lo */
+ /* timecmp_lo for RV32/RV64 or timecmp for RV64 */
uint64_t timecmp = env->timecmp;
- return timecmp & 0xFFFFFFFF;
+ return (size == 4) ? (timecmp & 0xFFFFFFFF) : timecmp;
} else if ((addr & 0x7) == 4) {
/* timecmp_hi */
uint64_t timecmp = env->timecmp;
@@ -139,11 +145,12 @@ static uint64_t riscv_aclint_mtimer_read(void *opaque, hwaddr addr,
return 0;
}
} else if (addr == mtimer->time_base) {
- /* time_lo */
- return cpu_riscv_read_rtc(mtimer->timebase_freq) & 0xFFFFFFFF;
+ /* time_lo for RV32/RV64 or timecmp for RV64 */
+ uint64_t rtc = cpu_riscv_read_rtc(mtimer);
+ return (size == 4) ? (rtc & 0xFFFFFFFF) : rtc;
} else if (addr == mtimer->time_base + 4) {
/* time_hi */
- return (cpu_riscv_read_rtc(mtimer->timebase_freq) >> 32) & 0xFFFFFFFF;
+ return (cpu_riscv_read_rtc(mtimer) >> 32) & 0xFFFFFFFF;
}
qemu_log_mask(LOG_UNIMP,
@@ -156,6 +163,7 @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
RISCVAclintMTimerState *mtimer = opaque;
+ int i;
if (addr >= mtimer->timecmp_base &&
addr < (mtimer->timecmp_base + (mtimer->num_harts << 3))) {
@@ -167,33 +175,66 @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr,
qemu_log_mask(LOG_GUEST_ERROR,
"aclint-mtimer: invalid hartid: %zu", hartid);
} else if ((addr & 0x7) == 0) {
- /* timecmp_lo */
- uint64_t timecmp_hi = env->timecmp >> 32;
- riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid,
- timecmp_hi << 32 | (value & 0xFFFFFFFF),
- mtimer->timebase_freq);
- return;
+ if (size == 4) {
+ /* timecmp_lo for RV32/RV64 */
+ uint64_t timecmp_hi = env->timecmp >> 32;
+ riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid,
+ timecmp_hi << 32 | (value & 0xFFFFFFFF));
+ } else {
+ /* timecmp for RV64 */
+ riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid,
+ value);
+ }
} else if ((addr & 0x7) == 4) {
- /* timecmp_hi */
- uint64_t timecmp_lo = env->timecmp;
- riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid,
- value << 32 | (timecmp_lo & 0xFFFFFFFF),
- mtimer->timebase_freq);
+ if (size == 4) {
+ /* timecmp_hi for RV32/RV64 */
+ uint64_t timecmp_lo = env->timecmp;
+ riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), hartid,
+ value << 32 | (timecmp_lo & 0xFFFFFFFF));
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "aclint-mtimer: invalid timecmp_hi write: %08x",
+ (uint32_t)addr);
+ }
} else {
qemu_log_mask(LOG_UNIMP,
"aclint-mtimer: invalid timecmp write: %08x",
(uint32_t)addr);
}
return;
- } else if (addr == mtimer->time_base) {
- /* time_lo */
- qemu_log_mask(LOG_UNIMP,
- "aclint-mtimer: time_lo write not implemented");
- return;
- } else if (addr == mtimer->time_base + 4) {
- /* time_hi */
- qemu_log_mask(LOG_UNIMP,
- "aclint-mtimer: time_hi write not implemented");
+ } else if (addr == mtimer->time_base || addr == mtimer->time_base + 4) {
+ uint64_t rtc_r = cpu_riscv_read_rtc_raw(mtimer->timebase_freq);
+
+ if (addr == mtimer->time_base) {
+ if (size == 4) {
+ /* time_lo for RV32/RV64 */
+ mtimer->time_delta = ((rtc_r & ~0xFFFFFFFFULL) | value) - rtc_r;
+ } else {
+ /* time for RV64 */
+ mtimer->time_delta = value - rtc_r;
+ }
+ } else {
+ if (size == 4) {
+ /* time_hi for RV32/RV64 */
+ mtimer->time_delta = (value << 32 | (rtc_r & 0xFFFFFFFF)) - rtc_r;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "aclint-mtimer: invalid time_hi write: %08x",
+ (uint32_t)addr);
+ return;
+ }
+ }
+
+ /* Check if timer interrupt is triggered for each hart. */
+ for (i = 0; i < mtimer->num_harts; i++) {
+ CPUState *cpu = qemu_get_cpu(mtimer->hartid_base + i);
+ CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+ if (!env) {
+ continue;
+ }
+ riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu),
+ i, env->timecmp);
+ }
return;
}
@@ -208,6 +249,10 @@ static const MemoryRegionOps riscv_aclint_mtimer_ops = {
.valid = {
.min_access_size = 4,
.max_access_size = 8
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 8,
}
};
@@ -248,11 +293,29 @@ static void riscv_aclint_mtimer_realize(DeviceState *dev, Error **errp)
}
}
+static void riscv_aclint_mtimer_reset_enter(Object *obj, ResetType type)
+{
+ /*
+ * According to RISC-V ACLINT spec:
+ * - On MTIMER device reset, the MTIME register is cleared to zero.
+ * - On MTIMER device reset, the MTIMECMP registers are in unknown state.
+ */
+ RISCVAclintMTimerState *mtimer = RISCV_ACLINT_MTIMER(obj);
+
+ /*
+ * Clear mtime register by writing to 0 it.
+ * Pending mtime interrupts will also be cleared at the same time.
+ */
+ riscv_aclint_mtimer_write(mtimer, mtimer->time_base, 0, 8);
+}
+
static void riscv_aclint_mtimer_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = riscv_aclint_mtimer_realize;
device_class_set_props(dc, riscv_aclint_mtimer_properties);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+ rc->phases.enter = riscv_aclint_mtimer_reset_enter;
}
static const TypeInfo riscv_aclint_mtimer_info = {
@@ -299,7 +362,7 @@ DeviceState *riscv_aclint_mtimer_create(hwaddr addr, hwaddr size,
continue;
}
if (provide_rdtime) {
- riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc, timebase_freq);
+ riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc, dev);
}
cb->s = RISCV_ACLINT_MTIMER(dev);
@@ -407,11 +470,32 @@ static void riscv_aclint_swi_realize(DeviceState *dev, Error **errp)
}
}
+static void riscv_aclint_swi_reset_enter(Object *obj, ResetType type)
+{
+ /*
+ * According to RISC-V ACLINT spec:
+ * - On MSWI device reset, each MSIP register is cleared to zero.
+ *
+ * p.s. SSWI device reset does nothing since SETSIP register always reads 0.
+ */
+ RISCVAclintSwiState *swi = RISCV_ACLINT_SWI(obj);
+ int i;
+
+ if (!swi->sswi) {
+ for (i = 0; i < swi->num_harts; i++) {
+ /* Clear MSIP registers by lowering software interrupts. */
+ qemu_irq_lower(swi->soft_irqs[i]);
+ }
+ }
+}
+
static void riscv_aclint_swi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = riscv_aclint_swi_realize;
device_class_set_props(dc, riscv_aclint_swi_properties);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+ rc->phases.enter = riscv_aclint_swi_reset_enter;
}
static const TypeInfo riscv_aclint_swi_info = {
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 53414aa197..5271590304 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -151,8 +151,9 @@ gicv3_icv_hppir_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_HPPIR%d rea
gicv3_icv_dir_write(uint32_t cpu, uint64_t val) "GICv3 ICV_DIR write cpu 0x%x value 0x%" PRIx64
gicv3_icv_iar_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_IAR%d read cpu 0x%x value 0x%" PRIx64
gicv3_icv_eoir_write(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_EOIR%d write cpu 0x%x value 0x%" PRIx64
-gicv3_cpuif_virt_update(uint32_t cpuid, int idx) "GICv3 CPU i/f 0x%x virt HPPI update LR index %d"
-gicv3_cpuif_virt_set_irqs(uint32_t cpuid, int fiqlevel, int irqlevel, int maintlevel) "GICv3 CPU i/f 0x%x virt HPPI update: setting FIQ %d IRQ %d maintenance-irq %d"
+gicv3_cpuif_virt_update(uint32_t cpuid, int idx, int hppvlpi, int grp, int prio) "GICv3 CPU i/f 0x%x virt HPPI update LR index %d HPPVLPI %d grp %d prio %d"
+gicv3_cpuif_virt_set_irqs(uint32_t cpuid, int fiqlevel, int irqlevel) "GICv3 CPU i/f 0x%x virt HPPI update: setting FIQ %d IRQ %d"
+gicv3_cpuif_virt_set_maint_irq(uint32_t cpuid, int maintlevel) "GICv3 CPU i/f 0x%x virt HPPI update: setting maintenance-irq %d"
# arm_gicv3_dist.c
gicv3_dist_read(uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 distributor read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d"
@@ -184,9 +185,17 @@ gicv3_its_cmd_mapd(uint32_t devid, uint32_t size, uint64_t ittaddr, int valid) "
gicv3_its_cmd_mapc(uint32_t icid, uint64_t rdbase, int valid) "GICv3 ITS: command MAPC ICID 0x%x RDbase 0x%" PRIx64 " V %d"
gicv3_its_cmd_mapi(uint32_t devid, uint32_t eventid, uint32_t icid) "GICv3 ITS: command MAPI DeviceID 0x%x EventID 0x%x ICID 0x%x"
gicv3_its_cmd_mapti(uint32_t devid, uint32_t eventid, uint32_t icid, uint32_t intid) "GICv3 ITS: command MAPTI DeviceID 0x%x EventID 0x%x ICID 0x%x pINTID 0x%x"
-gicv3_its_cmd_inv(void) "GICv3 ITS: command INV or INVALL"
+gicv3_its_cmd_inv(uint32_t devid, uint32_t eventid) "GICv3 ITS: command INV DeviceID 0x%x EventID 0x%x"
+gicv3_its_cmd_invall(void) "GICv3 ITS: command INVALL"
gicv3_its_cmd_movall(uint64_t rd1, uint64_t rd2) "GICv3 ITS: command MOVALL RDbase1 0x%" PRIx64 " RDbase2 0x%" PRIx64
gicv3_its_cmd_movi(uint32_t devid, uint32_t eventid, uint32_t icid) "GICv3 ITS: command MOVI DeviceID 0x%x EventID 0x%x ICID 0x%x"
+gicv3_its_cmd_vmapi(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t doorbell) "GICv3 ITS: command VMAPI DeviceID 0x%x EventID 0x%x vPEID 0x%x Dbell_pINTID 0x%x"
+gicv3_its_cmd_vmapti(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t vintid, uint32_t doorbell) "GICv3 ITS: command VMAPI DeviceID 0x%x EventID 0x%x vPEID 0x%x vINTID 0x%x Dbell_pINTID 0x%x"
+gicv3_its_cmd_vmapp(uint32_t vpeid, uint64_t rdbase, int valid, uint64_t vptaddr, uint32_t vptsize) "GICv3 ITS: command VMAPP vPEID 0x%x RDbase 0x%" PRIx64 " V %d VPT_addr 0x%" PRIx64 " VPT_size 0x%x"
+gicv3_its_cmd_vmovp(uint32_t vpeid, uint64_t rdbase) "GICv3 ITS: command VMOVP vPEID 0x%x RDbase 0x%" PRIx64
+gicv3_its_cmd_vsync(void) "GICv3 ITS: command VSYNC"
+gicv3_its_cmd_vmovi(uint32_t devid, uint32_t eventid, uint32_t vpeid, int dbvalid, uint32_t doorbell) "GICv3 ITS: command VMOVI DeviceID 0x%x EventID 0x%x vPEID 0x%x D %d Dbell_pINTID 0x%x"
+gicv3_its_cmd_vinvall(uint32_t vpeid) "GICv3 ITS: command VINVALL vPEID 0x%x"
gicv3_its_cmd_unknown(unsigned cmd) "GICv3 ITS: unknown command 0x%x"
gicv3_its_cte_read(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table read for ICID 0x%x: valid %d RDBase 0x%x"
gicv3_its_cte_write(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table write for ICID 0x%x: valid %d RDBase 0x%x"
@@ -197,6 +206,9 @@ gicv3_its_ite_write(uint64_t ittaddr, uint32_t eventid, int valid, int inttype,
gicv3_its_dte_read(uint32_t devid, int valid, uint32_t size, uint64_t ittaddr) "GICv3 ITS: Device Table read for DeviceID 0x%x: valid %d size 0x%x ITTaddr 0x%" PRIx64
gicv3_its_dte_write(uint32_t devid, int valid, uint32_t size, uint64_t ittaddr) "GICv3 ITS: Device Table write for DeviceID 0x%x: valid %d size 0x%x ITTaddr 0x%" PRIx64
gicv3_its_dte_read_fault(uint32_t devid) "GICv3 ITS: Device Table read for DeviceID 0x%x: faulted"
+gicv3_its_vte_read(uint32_t vpeid, int valid, uint32_t vptsize, uint64_t vptaddr, uint32_t rdbase) "GICv3 ITS: vPE Table read for vPEID 0x%x: valid %d VPTsize 0x%x VPTaddr 0x%" PRIx64 " RDbase 0x%x"
+gicv3_its_vte_read_fault(uint32_t vpeid) "GICv3 ITS: vPE Table read for vPEID 0x%x: faulted"
+gicv3_its_vte_write(uint32_t vpeid, int valid, uint32_t vptsize, uint64_t vptaddr, uint32_t rdbase) "GICv3 ITS: vPE Table write for vPEID 0x%x: valid %d VPTsize 0x%x VPTaddr 0x%" PRIx64 " RDbase 0x%x"
# armv7m_nvic.c
nvic_recompute_state(int vectpending, int vectpending_prio, int exception_prio) "NVIC state recomputed: vectpending %d vectpending_prio %d exception_prio %d"
diff --git a/hw/nios2/10m50_devboard.c b/hw/nios2/10m50_devboard.c
index 3d1205b8bd..91383fb097 100644
--- a/hw/nios2/10m50_devboard.c
+++ b/hw/nios2/10m50_devboard.c
@@ -27,6 +27,7 @@
#include "hw/sysbus.h"
#include "hw/char/serial.h"
+#include "hw/intc/nios2_vic.h"
#include "hw/qdev-properties.h"
#include "sysemu/sysemu.h"
#include "hw/boards.h"
@@ -36,17 +37,28 @@
#include "boot.h"
+struct Nios2MachineState {
+ MachineState parent_obj;
+
+ MemoryRegion phys_tcm;
+ MemoryRegion phys_tcm_alias;
+ MemoryRegion phys_ram;
+ MemoryRegion phys_ram_alias;
+
+ bool vic;
+};
+
+#define TYPE_NIOS2_MACHINE MACHINE_TYPE_NAME("10m50-ghrd")
+OBJECT_DECLARE_TYPE(Nios2MachineState, MachineClass, NIOS2_MACHINE)
+
#define BINARY_DEVICE_TREE_FILE "10m50-devboard.dtb"
static void nios2_10m50_ghrd_init(MachineState *machine)
{
+ Nios2MachineState *nms = NIOS2_MACHINE(machine);
Nios2CPU *cpu;
DeviceState *dev;
MemoryRegion *address_space_mem = get_system_memory();
- MemoryRegion *phys_tcm = g_new(MemoryRegion, 1);
- MemoryRegion *phys_tcm_alias = g_new(MemoryRegion, 1);
- MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
- MemoryRegion *phys_ram_alias = g_new(MemoryRegion, 1);
ram_addr_t tcm_base = 0x0;
ram_addr_t tcm_size = 0x1000; /* 1 kiB, but QEMU limit is 4 kiB */
ram_addr_t ram_base = 0x08000000;
@@ -55,27 +67,56 @@ static void nios2_10m50_ghrd_init(MachineState *machine)
int i;
/* Physical TCM (tb_ram_1k) with alias at 0xc0000000 */
- memory_region_init_ram(phys_tcm, NULL, "nios2.tcm", tcm_size,
+ memory_region_init_ram(&nms->phys_tcm, NULL, "nios2.tcm", tcm_size,
&error_abort);
- memory_region_init_alias(phys_tcm_alias, NULL, "nios2.tcm.alias",
- phys_tcm, 0, tcm_size);
- memory_region_add_subregion(address_space_mem, tcm_base, phys_tcm);
+ memory_region_init_alias(&nms->phys_tcm_alias, NULL, "nios2.tcm.alias",
+ &nms->phys_tcm, 0, tcm_size);
+ memory_region_add_subregion(address_space_mem, tcm_base, &nms->phys_tcm);
memory_region_add_subregion(address_space_mem, 0xc0000000 + tcm_base,
- phys_tcm_alias);
+ &nms->phys_tcm_alias);
/* Physical DRAM with alias at 0xc0000000 */
- memory_region_init_ram(phys_ram, NULL, "nios2.ram", ram_size,
+ memory_region_init_ram(&nms->phys_ram, NULL, "nios2.ram", ram_size,
&error_abort);
- memory_region_init_alias(phys_ram_alias, NULL, "nios2.ram.alias",
- phys_ram, 0, ram_size);
- memory_region_add_subregion(address_space_mem, ram_base, phys_ram);
+ memory_region_init_alias(&nms->phys_ram_alias, NULL, "nios2.ram.alias",
+ &nms->phys_ram, 0, ram_size);
+ memory_region_add_subregion(address_space_mem, ram_base, &nms->phys_ram);
memory_region_add_subregion(address_space_mem, 0xc0000000 + ram_base,
- phys_ram_alias);
+ &nms->phys_ram_alias);
- /* Create CPU -- FIXME */
- cpu = NIOS2_CPU(cpu_create(TYPE_NIOS2_CPU));
- for (i = 0; i < 32; i++) {
- irq[i] = qdev_get_gpio_in_named(DEVICE(cpu), "IRQ", i);
+ /* Create CPU. We need to set eic_present between init and realize. */
+ cpu = NIOS2_CPU(object_new(TYPE_NIOS2_CPU));
+
+ /* Enable the External Interrupt Controller within the CPU. */
+ cpu->eic_present = nms->vic;
+
+ /* Configure new exception vectors. */
+ cpu->reset_addr = 0xd4000000;
+ cpu->exception_addr = 0xc8000120;
+ cpu->fast_tlb_miss_addr = 0xc0000100;
+
+ qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal);
+
+ if (nms->vic) {
+ DeviceState *dev = qdev_new(TYPE_NIOS2_VIC);
+ MemoryRegion *dev_mr;
+ qemu_irq cpu_irq;
+
+ object_property_set_link(OBJECT(dev), "cpu", OBJECT(cpu), &error_fatal);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+ cpu_irq = qdev_get_gpio_in_named(DEVICE(cpu), "EIC", 0);
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, cpu_irq);
+ for (int i = 0; i < 32; i++) {
+ irq[i] = qdev_get_gpio_in(dev, i);
+ }
+
+ dev_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
+ memory_region_add_subregion(address_space_mem, 0x18002000, dev_mr);
+ } else {
+ for (i = 0; i < 32; i++) {
+ irq[i] = qdev_get_gpio_in_named(DEVICE(cpu), "IRQ", i);
+ }
}
/* Register: Altera 16550 UART */
@@ -96,20 +137,44 @@ static void nios2_10m50_ghrd_init(MachineState *machine)
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xe0000880);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[5]);
- /* Configure new exception vectors and reset CPU for it to take effect. */
- cpu->reset_addr = 0xd4000000;
- cpu->exception_addr = 0xc8000120;
- cpu->fast_tlb_miss_addr = 0xc0000100;
-
nios2_load_kernel(cpu, ram_base, ram_size, machine->initrd_filename,
BINARY_DEVICE_TREE_FILE, NULL);
}
-static void nios2_10m50_ghrd_machine_init(struct MachineClass *mc)
+static bool get_vic(Object *obj, Error **errp)
+{
+ Nios2MachineState *nms = NIOS2_MACHINE(obj);
+ return nms->vic;
+}
+
+static void set_vic(Object *obj, bool value, Error **errp)
+{
+ Nios2MachineState *nms = NIOS2_MACHINE(obj);
+ nms->vic = value;
+}
+
+static void nios2_10m50_ghrd_class_init(ObjectClass *oc, void *data)
{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
mc->desc = "Altera 10M50 GHRD Nios II design";
mc->init = nios2_10m50_ghrd_init;
mc->is_default = true;
+
+ object_class_property_add_bool(oc, "vic", get_vic, set_vic);
+ object_class_property_set_description(oc, "vic",
+ "Set on/off to enable/disable the Vectored Interrupt Controller");
}
-DEFINE_MACHINE("10m50-ghrd", nios2_10m50_ghrd_machine_init);
+static const TypeInfo nios2_10m50_ghrd_type_info = {
+ .name = TYPE_NIOS2_MACHINE,
+ .parent = TYPE_MACHINE,
+ .instance_size = sizeof(Nios2MachineState),
+ .class_init = nios2_10m50_ghrd_class_init,
+};
+
+static void nios2_10m50_ghrd_type_init(void)
+{
+ type_register_static(&nios2_10m50_ghrd_type_info);
+}
+type_init(nios2_10m50_ghrd_type_init);
diff --git a/hw/nios2/Kconfig b/hw/nios2/Kconfig
index b10ea640da..4748ae27b6 100644
--- a/hw/nios2/Kconfig
+++ b/hw/nios2/Kconfig
@@ -3,6 +3,7 @@ config NIOS2_10M50
select NIOS2
select SERIAL
select ALTERA_TIMER
+ select NIOS2_VIC
config NIOS2_GENERIC_NOMMU
bool
diff --git a/hw/rdma/vmw/pvrdma_main.c b/hw/rdma/vmw/pvrdma_main.c
index 27a7622432..58db0b8e3b 100644
--- a/hw/rdma/vmw/pvrdma_main.c
+++ b/hw/rdma/vmw/pvrdma_main.c
@@ -159,13 +159,13 @@ static void free_dsr(PVRDMADev *dev)
free_dev_ring(pci_dev, &dev->dsr_info.cq, dev->dsr_info.cq_ring_state);
rdma_pci_dma_unmap(pci_dev, dev->dsr_info.req,
- sizeof(union pvrdma_cmd_req));
+ sizeof(union pvrdma_cmd_req));
rdma_pci_dma_unmap(pci_dev, dev->dsr_info.rsp,
- sizeof(union pvrdma_cmd_resp));
+ sizeof(union pvrdma_cmd_resp));
rdma_pci_dma_unmap(pci_dev, dev->dsr_info.dsr,
- sizeof(struct pvrdma_device_shared_region));
+ sizeof(struct pvrdma_device_shared_region));
dev->dsr_info.dsr = NULL;
}
@@ -249,7 +249,8 @@ static void init_dsr_dev_caps(PVRDMADev *dev)
{
struct pvrdma_device_shared_region *dsr;
- if (dev->dsr_info.dsr == NULL) {
+ if (!dev->dsr_info.dsr) {
+ /* Buggy or malicious guest driver */
rdma_error_report("Can't initialized DSR");
return;
}
diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c
index 0f179d3601..57a41df8e9 100644
--- a/hw/riscv/boot.c
+++ b/hw/riscv/boot.c
@@ -212,9 +212,9 @@ hwaddr riscv_load_initrd(const char *filename, uint64_t mem_size,
return *start + size;
}
-uint32_t riscv_load_fdt(hwaddr dram_base, uint64_t mem_size, void *fdt)
+uint64_t riscv_load_fdt(hwaddr dram_base, uint64_t mem_size, void *fdt)
{
- uint32_t temp, fdt_addr;
+ uint64_t temp, fdt_addr;
hwaddr dram_end = dram_base + mem_size;
int ret, fdtsize = fdt_totalsize(fdt);
@@ -229,7 +229,7 @@ uint32_t riscv_load_fdt(hwaddr dram_base, uint64_t mem_size, void *fdt)
* Thus, put it at an 16MB aligned address that less than fdt size from the
* end of dram or 3GB whichever is lesser.
*/
- temp = MIN(dram_end, 3072 * MiB);
+ temp = (dram_base < 3072 * MiB) ? MIN(dram_end, 3072 * MiB) : dram_end;
fdt_addr = QEMU_ALIGN_DOWN(temp - fdtsize, 16 * MiB);
ret = fdt_pack(fdt);
@@ -285,13 +285,15 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts
hwaddr start_addr,
hwaddr rom_base, hwaddr rom_size,
uint64_t kernel_entry,
- uint32_t fdt_load_addr, void *fdt)
+ uint64_t fdt_load_addr, void *fdt)
{
int i;
uint32_t start_addr_hi32 = 0x00000000;
+ uint32_t fdt_load_addr_hi32 = 0x00000000;
if (!riscv_is_32bit(harts)) {
start_addr_hi32 = start_addr >> 32;
+ fdt_load_addr_hi32 = fdt_load_addr >> 32;
}
/* reset vector */
uint32_t reset_vec[10] = {
@@ -304,7 +306,7 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts
start_addr, /* start: .dword */
start_addr_hi32,
fdt_load_addr, /* fdt_laddr: .dword */
- 0x00000000,
+ fdt_load_addr_hi32,
/* fw_dyn: */
};
if (riscv_is_32bit(harts)) {
diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c
index 833624d66c..2d401dcb23 100644
--- a/hw/riscv/opentitan.c
+++ b/hw/riscv/opentitan.c
@@ -120,11 +120,18 @@ static void lowrisc_ibex_soc_init(Object *obj)
object_initialize_child(obj, "uart", &s->uart, TYPE_IBEX_UART);
object_initialize_child(obj, "timer", &s->timer, TYPE_IBEX_TIMER);
+
+ for (int i = 0; i < OPENTITAN_NUM_SPI_HOSTS; i++) {
+ object_initialize_child(obj, "spi_host[*]", &s->spi_host[i],
+ TYPE_IBEX_SPI_HOST);
+ }
}
static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp)
{
const MemMapEntry *memmap = ibex_memmap;
+ DeviceState *dev;
+ SysBusDevice *busdev;
MachineState *ms = MACHINE(qdev_get_machine());
LowRISCIbexSoCState *s = RISCV_IBEX_SOC(dev_soc);
MemoryRegion *sys_mem = get_system_memory();
@@ -209,14 +216,35 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp)
qdev_get_gpio_in(DEVICE(qemu_get_cpu(0)),
IRQ_M_TIMER));
+ /* SPI-Hosts */
+ for (int i = 0; i < OPENTITAN_NUM_SPI_HOSTS; ++i) {
+ dev = DEVICE(&(s->spi_host[i]));
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi_host[i]), errp)) {
+ return;
+ }
+ busdev = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(busdev, 0, memmap[IBEX_DEV_SPI_HOST0 + i].base);
+
+ switch (i) {
+ case OPENTITAN_SPI_HOST0:
+ sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(&s->plic),
+ IBEX_SPI_HOST0_ERR_IRQ));
+ sysbus_connect_irq(busdev, 1, qdev_get_gpio_in(DEVICE(&s->plic),
+ IBEX_SPI_HOST0_SPI_EVENT_IRQ));
+ break;
+ case OPENTITAN_SPI_HOST1:
+ sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(&s->plic),
+ IBEX_SPI_HOST1_ERR_IRQ));
+ sysbus_connect_irq(busdev, 1, qdev_get_gpio_in(DEVICE(&s->plic),
+ IBEX_SPI_HOST1_SPI_EVENT_IRQ));
+ break;
+ }
+ }
+
create_unimplemented_device("riscv.lowrisc.ibex.gpio",
memmap[IBEX_DEV_GPIO].base, memmap[IBEX_DEV_GPIO].size);
create_unimplemented_device("riscv.lowrisc.ibex.spi_device",
memmap[IBEX_DEV_SPI_DEVICE].base, memmap[IBEX_DEV_SPI_DEVICE].size);
- create_unimplemented_device("riscv.lowrisc.ibex.spi_host0",
- memmap[IBEX_DEV_SPI_HOST0].base, memmap[IBEX_DEV_SPI_HOST0].size);
- create_unimplemented_device("riscv.lowrisc.ibex.spi_host1",
- memmap[IBEX_DEV_SPI_HOST1].base, memmap[IBEX_DEV_SPI_HOST1].size);
create_unimplemented_device("riscv.lowrisc.ibex.i2c",
memmap[IBEX_DEV_I2C].base, memmap[IBEX_DEV_I2C].size);
create_unimplemented_device("riscv.lowrisc.ibex.pattgen",
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index da50cbed43..b49c5361bd 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -230,8 +230,14 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket,
cpu_name = g_strdup_printf("/cpus/cpu@%d",
s->soc[socket].hartid_base + cpu);
qemu_fdt_add_subnode(mc->fdt, cpu_name);
- qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type",
- (is_32_bit) ? "riscv,sv32" : "riscv,sv48");
+ if (riscv_feature(&s->soc[socket].harts[cpu].env,
+ RISCV_FEATURE_MMU)) {
+ qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type",
+ (is_32_bit) ? "riscv,sv32" : "riscv,sv48");
+ } else {
+ qemu_fdt_setprop_string(mc->fdt, cpu_name, "mmu-type",
+ "riscv,none");
+ }
name = riscv_isa_string(&s->soc[socket].harts[cpu]);
qemu_fdt_setprop_string(mc->fdt, cpu_name, "riscv,isa", name);
g_free(name);
@@ -1308,12 +1314,18 @@ static void virt_machine_init(MachineState *machine)
/*
* Only direct boot kernel is currently supported for KVM VM,
- * so the "-bios" parameter is ignored and treated like "-bios none"
- * when KVM is enabled.
+ * so the "-bios" parameter is not supported when KVM is enabled.
*/
if (kvm_enabled()) {
- g_free(machine->firmware);
- machine->firmware = g_strdup("none");
+ if (machine->firmware) {
+ if (strcmp(machine->firmware, "none")) {
+ error_report("Machine mode firmware is not supported in "
+ "combination with KVM.");
+ exit(1);
+ }
+ } else {
+ machine->firmware = g_strdup("none");
+ }
}
if (riscv_is_32bit(&s->soc[0])) {
diff --git a/hw/ssi/ibex_spi_host.c b/hw/ssi/ibex_spi_host.c
new file mode 100644
index 0000000000..d14580b409
--- /dev/null
+++ b/hw/ssi/ibex_spi_host.c
@@ -0,0 +1,612 @@
+/*
+ * QEMU model of the Ibex SPI Controller
+ * SPEC Reference: https://docs.opentitan.org/hw/ip/spi_host/doc/
+ *
+ * Copyright (C) 2022 Western Digital
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/ssi/ibex_spi_host.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-properties-system.h"
+#include "migration/vmstate.h"
+#include "trace.h"
+
+REG32(INTR_STATE, 0x00)
+ FIELD(INTR_STATE, ERROR, 0, 1)
+ FIELD(INTR_STATE, SPI_EVENT, 1, 1)
+REG32(INTR_ENABLE, 0x04)
+ FIELD(INTR_ENABLE, ERROR, 0, 1)
+ FIELD(INTR_ENABLE, SPI_EVENT, 1, 1)
+REG32(INTR_TEST, 0x08)
+ FIELD(INTR_TEST, ERROR, 0, 1)
+ FIELD(INTR_TEST, SPI_EVENT, 1, 1)
+REG32(ALERT_TEST, 0x0c)
+ FIELD(ALERT_TEST, FETAL_TEST, 0, 1)
+REG32(CONTROL, 0x10)
+ FIELD(CONTROL, RX_WATERMARK, 0, 8)
+ FIELD(CONTROL, TX_WATERMARK, 1, 8)
+ FIELD(CONTROL, OUTPUT_EN, 29, 1)
+ FIELD(CONTROL, SW_RST, 30, 1)
+ FIELD(CONTROL, SPIEN, 31, 1)
+REG32(STATUS, 0x14)
+ FIELD(STATUS, TXQD, 0, 8)
+ FIELD(STATUS, RXQD, 18, 8)
+ FIELD(STATUS, CMDQD, 16, 3)
+ FIELD(STATUS, RXWM, 20, 1)
+ FIELD(STATUS, BYTEORDER, 22, 1)
+ FIELD(STATUS, RXSTALL, 23, 1)
+ FIELD(STATUS, RXEMPTY, 24, 1)
+ FIELD(STATUS, RXFULL, 25, 1)
+ FIELD(STATUS, TXWM, 26, 1)
+ FIELD(STATUS, TXSTALL, 27, 1)
+ FIELD(STATUS, TXEMPTY, 28, 1)
+ FIELD(STATUS, TXFULL, 29, 1)
+ FIELD(STATUS, ACTIVE, 30, 1)
+ FIELD(STATUS, READY, 31, 1)
+REG32(CONFIGOPTS, 0x18)
+ FIELD(CONFIGOPTS, CLKDIV_0, 0, 16)
+ FIELD(CONFIGOPTS, CSNIDLE_0, 16, 4)
+ FIELD(CONFIGOPTS, CSNTRAIL_0, 20, 4)
+ FIELD(CONFIGOPTS, CSNLEAD_0, 24, 4)
+ FIELD(CONFIGOPTS, FULLCYC_0, 29, 1)
+ FIELD(CONFIGOPTS, CPHA_0, 30, 1)
+ FIELD(CONFIGOPTS, CPOL_0, 31, 1)
+REG32(CSID, 0x1c)
+ FIELD(CSID, CSID, 0, 32)
+REG32(COMMAND, 0x20)
+ FIELD(COMMAND, LEN, 0, 8)
+ FIELD(COMMAND, CSAAT, 9, 1)
+ FIELD(COMMAND, SPEED, 10, 2)
+ FIELD(COMMAND, DIRECTION, 12, 2)
+REG32(ERROR_ENABLE, 0x2c)
+ FIELD(ERROR_ENABLE, CMDBUSY, 0, 1)
+ FIELD(ERROR_ENABLE, OVERFLOW, 1, 1)
+ FIELD(ERROR_ENABLE, UNDERFLOW, 2, 1)
+ FIELD(ERROR_ENABLE, CMDINVAL, 3, 1)
+ FIELD(ERROR_ENABLE, CSIDINVAL, 4, 1)
+REG32(ERROR_STATUS, 0x30)
+ FIELD(ERROR_STATUS, CMDBUSY, 0, 1)
+ FIELD(ERROR_STATUS, OVERFLOW, 1, 1)
+ FIELD(ERROR_STATUS, UNDERFLOW, 2, 1)
+ FIELD(ERROR_STATUS, CMDINVAL, 3, 1)
+ FIELD(ERROR_STATUS, CSIDINVAL, 4, 1)
+ FIELD(ERROR_STATUS, ACCESSINVAL, 5, 1)
+REG32(EVENT_ENABLE, 0x30)
+ FIELD(EVENT_ENABLE, RXFULL, 0, 1)
+ FIELD(EVENT_ENABLE, TXEMPTY, 1, 1)
+ FIELD(EVENT_ENABLE, RXWM, 2, 1)
+ FIELD(EVENT_ENABLE, TXWM, 3, 1)
+ FIELD(EVENT_ENABLE, READY, 4, 1)
+ FIELD(EVENT_ENABLE, IDLE, 5, 1)
+
+static inline uint8_t div4_round_up(uint8_t dividend)
+{
+ return (dividend + 3) / 4;
+}
+
+static void ibex_spi_rxfifo_reset(IbexSPIHostState *s)
+{
+ /* Empty the RX FIFO and assert RXEMPTY */
+ fifo8_reset(&s->rx_fifo);
+ s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXFULL_MASK;
+ s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXEMPTY_MASK;
+}
+
+static void ibex_spi_txfifo_reset(IbexSPIHostState *s)
+{
+ /* Empty the TX FIFO and assert TXEMPTY */
+ fifo8_reset(&s->tx_fifo);
+ s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXFULL_MASK;
+ s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_TXEMPTY_MASK;
+}
+
+static void ibex_spi_host_reset(DeviceState *dev)
+{
+ IbexSPIHostState *s = IBEX_SPI_HOST(dev);
+ trace_ibex_spi_host_reset("Resetting Ibex SPI");
+
+ /* SPI Host Register Reset */
+ s->regs[IBEX_SPI_HOST_INTR_STATE] = 0x00;
+ s->regs[IBEX_SPI_HOST_INTR_ENABLE] = 0x00;
+ s->regs[IBEX_SPI_HOST_INTR_TEST] = 0x00;
+ s->regs[IBEX_SPI_HOST_ALERT_TEST] = 0x00;
+ s->regs[IBEX_SPI_HOST_CONTROL] = 0x7f;
+ s->regs[IBEX_SPI_HOST_STATUS] = 0x00;
+ s->regs[IBEX_SPI_HOST_CONFIGOPTS] = 0x00;
+ s->regs[IBEX_SPI_HOST_CSID] = 0x00;
+ s->regs[IBEX_SPI_HOST_COMMAND] = 0x00;
+ /* RX/TX Modelled by FIFO */
+ s->regs[IBEX_SPI_HOST_RXDATA] = 0x00;
+ s->regs[IBEX_SPI_HOST_TXDATA] = 0x00;
+
+ s->regs[IBEX_SPI_HOST_ERROR_ENABLE] = 0x1F;
+ s->regs[IBEX_SPI_HOST_ERROR_STATUS] = 0x00;
+ s->regs[IBEX_SPI_HOST_EVENT_ENABLE] = 0x00;
+
+ ibex_spi_rxfifo_reset(s);
+ ibex_spi_txfifo_reset(s);
+
+ s->init_status = true;
+ return;
+}
+
+/*
+ * Check if we need to trigger an interrupt.
+ * The two interrupts lines (host_err and event) can
+ * be enabled separately in 'IBEX_SPI_HOST_INTR_ENABLE'.
+ *
+ * Interrupts are triggered based on the ones
+ * enabled in the `IBEX_SPI_HOST_EVENT_ENABLE` and `IBEX_SPI_HOST_ERROR_ENABLE`.
+ */
+static void ibex_spi_host_irq(IbexSPIHostState *s)
+{
+ bool error_en = s->regs[IBEX_SPI_HOST_INTR_ENABLE]
+ & R_INTR_ENABLE_ERROR_MASK;
+ bool event_en = s->regs[IBEX_SPI_HOST_INTR_ENABLE]
+ & R_INTR_ENABLE_SPI_EVENT_MASK;
+ bool err_pending = s->regs[IBEX_SPI_HOST_INTR_STATE]
+ & R_INTR_STATE_ERROR_MASK;
+ bool status_pending = s->regs[IBEX_SPI_HOST_INTR_STATE]
+ & R_INTR_STATE_SPI_EVENT_MASK;
+ int err_irq = 0, event_irq = 0;
+
+ /* Error IRQ enabled and Error IRQ Cleared*/
+ if (error_en && !err_pending) {
+ /* Event enabled, Interrupt Test Error */
+ if (s->regs[IBEX_SPI_HOST_INTR_TEST] & R_INTR_TEST_ERROR_MASK) {
+ err_irq = 1;
+ } else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE]
+ & R_ERROR_ENABLE_CMDBUSY_MASK) &&
+ s->regs[IBEX_SPI_HOST_ERROR_STATUS]
+ & R_ERROR_STATUS_CMDBUSY_MASK) {
+ /* Wrote to COMMAND when not READY */
+ err_irq = 1;
+ } else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE]
+ & R_ERROR_ENABLE_CMDINVAL_MASK) &&
+ s->regs[IBEX_SPI_HOST_ERROR_STATUS]
+ & R_ERROR_STATUS_CMDINVAL_MASK) {
+ /* Invalid command segment */
+ err_irq = 1;
+ } else if ((s->regs[IBEX_SPI_HOST_ERROR_ENABLE]
+ & R_ERROR_ENABLE_CSIDINVAL_MASK) &&
+ s->regs[IBEX_SPI_HOST_ERROR_STATUS]
+ & R_ERROR_STATUS_CSIDINVAL_MASK) {
+ /* Invalid value for CSID */
+ err_irq = 1;
+ }
+ if (err_irq) {
+ s->regs[IBEX_SPI_HOST_INTR_STATE] |= R_INTR_STATE_ERROR_MASK;
+ }
+ qemu_set_irq(s->host_err, err_irq);
+ }
+
+ /* Event IRQ Enabled and Event IRQ Cleared */
+ if (event_en && !status_pending) {
+ if (s->regs[IBEX_SPI_HOST_INTR_TEST] & R_INTR_TEST_SPI_EVENT_MASK) {
+ /* Event enabled, Interrupt Test Event */
+ event_irq = 1;
+ } else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE]
+ & R_EVENT_ENABLE_READY_MASK) &&
+ (s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_READY_MASK)) {
+ /* SPI Host ready for next command */
+ event_irq = 1;
+ } else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE]
+ & R_EVENT_ENABLE_TXEMPTY_MASK) &&
+ (s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_TXEMPTY_MASK)) {
+ /* SPI TXEMPTY, TXFIFO drained */
+ event_irq = 1;
+ } else if ((s->regs[IBEX_SPI_HOST_EVENT_ENABLE]
+ & R_EVENT_ENABLE_RXFULL_MASK) &&
+ (s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_RXFULL_MASK)) {
+ /* SPI RXFULL, RXFIFO full */
+ event_irq = 1;
+ }
+ if (event_irq) {
+ s->regs[IBEX_SPI_HOST_INTR_STATE] |= R_INTR_STATE_SPI_EVENT_MASK;
+ }
+ qemu_set_irq(s->event, event_irq);
+ }
+}
+
+static void ibex_spi_host_transfer(IbexSPIHostState *s)
+{
+ uint32_t rx, tx;
+ /* Get num of one byte transfers */
+ uint8_t segment_len = ((s->regs[IBEX_SPI_HOST_COMMAND] & R_COMMAND_LEN_MASK)
+ >> R_COMMAND_LEN_SHIFT);
+ while (segment_len > 0) {
+ if (fifo8_is_empty(&s->tx_fifo)) {
+ /* Assert Stall */
+ s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_TXSTALL_MASK;
+ break;
+ } else if (fifo8_is_full(&s->rx_fifo)) {
+ /* Assert Stall */
+ s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXSTALL_MASK;
+ break;
+ } else {
+ tx = fifo8_pop(&s->tx_fifo);
+ }
+
+ rx = ssi_transfer(s->ssi, tx);
+
+ trace_ibex_spi_host_transfer(tx, rx);
+
+ if (!fifo8_is_full(&s->rx_fifo)) {
+ fifo8_push(&s->rx_fifo, rx);
+ } else {
+ /* Assert RXFULL */
+ s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXFULL_MASK;
+ }
+ --segment_len;
+ }
+
+ /* Assert Ready */
+ s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_READY_MASK;
+ /* Set RXQD */
+ s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXQD_MASK;
+ s->regs[IBEX_SPI_HOST_STATUS] |= (R_STATUS_RXQD_MASK
+ & div4_round_up(segment_len));
+ /* Set TXQD */
+ s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXQD_MASK;
+ s->regs[IBEX_SPI_HOST_STATUS] |= (fifo8_num_used(&s->tx_fifo) / 4)
+ & R_STATUS_TXQD_MASK;
+ /* Clear TXFULL */
+ s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXFULL_MASK;
+ /* Assert TXEMPTY and drop remaining bytes that exceed segment_len */
+ ibex_spi_txfifo_reset(s);
+ /* Reset RXEMPTY */
+ s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXEMPTY_MASK;
+
+ ibex_spi_host_irq(s);
+}
+
+static uint64_t ibex_spi_host_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ IbexSPIHostState *s = opaque;
+ uint32_t rc = 0;
+ uint8_t rx_byte = 0;
+
+ trace_ibex_spi_host_read(addr, size);
+
+ /* Match reg index */
+ addr = addr >> 2;
+ switch (addr) {
+ /* Skipping any W/O registers */
+ case IBEX_SPI_HOST_INTR_STATE...IBEX_SPI_HOST_INTR_ENABLE:
+ case IBEX_SPI_HOST_CONTROL...IBEX_SPI_HOST_STATUS:
+ rc = s->regs[addr];
+ break;
+ case IBEX_SPI_HOST_CSID:
+ rc = s->regs[addr];
+ break;
+ case IBEX_SPI_HOST_CONFIGOPTS:
+ rc = s->config_opts[s->regs[IBEX_SPI_HOST_CSID]];
+ break;
+ case IBEX_SPI_HOST_TXDATA:
+ rc = s->regs[addr];
+ break;
+ case IBEX_SPI_HOST_RXDATA:
+ /* Clear RXFULL */
+ s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_RXFULL_MASK;
+
+ for (int i = 0; i < 4; ++i) {
+ if (fifo8_is_empty(&s->rx_fifo)) {
+ /* Assert RXEMPTY, no IRQ */
+ s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_RXEMPTY_MASK;
+ s->regs[IBEX_SPI_HOST_ERROR_STATUS] |=
+ R_ERROR_STATUS_UNDERFLOW_MASK;
+ return rc;
+ }
+ rx_byte = fifo8_pop(&s->rx_fifo);
+ rc |= rx_byte << (i * 8);
+ }
+ break;
+ case IBEX_SPI_HOST_ERROR_ENABLE...IBEX_SPI_HOST_EVENT_ENABLE:
+ rc = s->regs[addr];
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "Bad offset 0x%" HWADDR_PRIx "\n",
+ addr << 2);
+ }
+ return rc;
+}
+
+
+static void ibex_spi_host_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ IbexSPIHostState *s = opaque;
+ uint32_t val32 = val64;
+ uint32_t shift_mask = 0xff;
+ uint8_t txqd_len;
+
+ trace_ibex_spi_host_write(addr, size, val64);
+
+ /* Match reg index */
+ addr = addr >> 2;
+
+ switch (addr) {
+ /* Skipping any R/O registers */
+ case IBEX_SPI_HOST_INTR_STATE...IBEX_SPI_HOST_INTR_ENABLE:
+ s->regs[addr] = val32;
+ break;
+ case IBEX_SPI_HOST_INTR_TEST:
+ s->regs[addr] = val32;
+ ibex_spi_host_irq(s);
+ break;
+ case IBEX_SPI_HOST_ALERT_TEST:
+ s->regs[addr] = val32;
+ qemu_log_mask(LOG_UNIMP,
+ "%s: SPI_ALERT_TEST is not supported\n", __func__);
+ break;
+ case IBEX_SPI_HOST_CONTROL:
+ s->regs[addr] = val32;
+
+ if (val32 & R_CONTROL_SW_RST_MASK) {
+ ibex_spi_host_reset((DeviceState *)s);
+ /* Clear active if any */
+ s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_ACTIVE_MASK;
+ }
+
+ if (val32 & R_CONTROL_OUTPUT_EN_MASK) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: CONTROL_OUTPUT_EN is not supported\n", __func__);
+ }
+ break;
+ case IBEX_SPI_HOST_CONFIGOPTS:
+ /* Update the respective config-opts register based on CSIDth index */
+ s->config_opts[s->regs[IBEX_SPI_HOST_CSID]] = val32;
+ qemu_log_mask(LOG_UNIMP,
+ "%s: CONFIGOPTS Hardware settings not supported\n",
+ __func__);
+ break;
+ case IBEX_SPI_HOST_CSID:
+ if (val32 >= s->num_cs) {
+ /* CSID exceeds max num_cs */
+ s->regs[IBEX_SPI_HOST_ERROR_STATUS] |=
+ R_ERROR_STATUS_CSIDINVAL_MASK;
+ ibex_spi_host_irq(s);
+ return;
+ }
+ s->regs[addr] = val32;
+ break;
+ case IBEX_SPI_HOST_COMMAND:
+ s->regs[addr] = val32;
+
+ /* STALL, IP not enabled */
+ if (!(s->regs[IBEX_SPI_HOST_CONTROL] & R_CONTROL_SPIEN_MASK)) {
+ return;
+ }
+
+ /* SPI not ready, IRQ Error */
+ if (!(s->regs[IBEX_SPI_HOST_STATUS] & R_STATUS_READY_MASK)) {
+ s->regs[IBEX_SPI_HOST_ERROR_STATUS] |= R_ERROR_STATUS_CMDBUSY_MASK;
+ ibex_spi_host_irq(s);
+ return;
+ }
+ /* Assert Not Ready */
+ s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_READY_MASK;
+
+ if (((val32 & R_COMMAND_DIRECTION_MASK) >> R_COMMAND_DIRECTION_SHIFT)
+ != BIDIRECTIONAL_TRANSFER) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Rx Only/Tx Only are not supported\n", __func__);
+ }
+
+ if (val32 & R_COMMAND_CSAAT_MASK) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: CSAAT is not supported\n", __func__);
+ }
+ if (val32 & R_COMMAND_SPEED_MASK) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: SPEED is not supported\n", __func__);
+ }
+
+ /* Set Transfer Callback */
+ timer_mod(s->fifo_trigger_handle,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ (TX_INTERRUPT_TRIGGER_DELAY_NS));
+
+ break;
+ case IBEX_SPI_HOST_TXDATA:
+ /*
+ * This is a hardware `feature` where
+ * the first word written TXDATA after init is omitted entirely
+ */
+ if (s->init_status) {
+ s->init_status = false;
+ return;
+ }
+
+ for (int i = 0; i < 4; ++i) {
+ /* Attempting to write when TXFULL */
+ if (fifo8_is_full(&s->tx_fifo)) {
+ /* Assert RXEMPTY, no IRQ */
+ s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_TXFULL_MASK;
+ s->regs[IBEX_SPI_HOST_ERROR_STATUS] |=
+ R_ERROR_STATUS_OVERFLOW_MASK;
+ ibex_spi_host_irq(s);
+ return;
+ }
+ /* Byte ordering is set by the IP */
+ if ((s->regs[IBEX_SPI_HOST_STATUS] &
+ R_STATUS_BYTEORDER_MASK) == 0) {
+ /* LE: LSB transmitted first (default for ibex processor) */
+ shift_mask = 0xff << (i * 8);
+ } else {
+ /* BE: MSB transmitted first */
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Big endian is not supported\n", __func__);
+ }
+
+ fifo8_push(&s->tx_fifo, (val32 & shift_mask) >> (i * 8));
+ }
+
+ /* Reset TXEMPTY */
+ s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXEMPTY_MASK;
+ /* Update TXQD */
+ txqd_len = (s->regs[IBEX_SPI_HOST_STATUS] &
+ R_STATUS_TXQD_MASK) >> R_STATUS_TXQD_SHIFT;
+ /* Partial bytes (size < 4) are padded, in words. */
+ txqd_len += 1;
+ s->regs[IBEX_SPI_HOST_STATUS] &= ~R_STATUS_TXQD_MASK;
+ s->regs[IBEX_SPI_HOST_STATUS] |= txqd_len;
+ /* Assert Ready */
+ s->regs[IBEX_SPI_HOST_STATUS] |= R_STATUS_READY_MASK;
+ break;
+ case IBEX_SPI_HOST_ERROR_ENABLE:
+ s->regs[addr] = val32;
+
+ if (val32 & R_ERROR_ENABLE_CMDINVAL_MASK) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Segment Length is not supported\n", __func__);
+ }
+ break;
+ case IBEX_SPI_HOST_ERROR_STATUS:
+ /*
+ * Indicates that any errors that have occurred.
+ * When an error occurs, the corresponding bit must be cleared
+ * here before issuing any further commands
+ */
+ s->regs[addr] = val32;
+ break;
+ case IBEX_SPI_HOST_EVENT_ENABLE:
+ /* Controls which classes of SPI events raise an interrupt. */
+ s->regs[addr] = val32;
+
+ if (val32 & R_EVENT_ENABLE_RXWM_MASK) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: RXWM is not supported\n", __func__);
+ }
+ if (val32 & R_EVENT_ENABLE_TXWM_MASK) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: TXWM is not supported\n", __func__);
+ }
+
+ if (val32 & R_EVENT_ENABLE_IDLE_MASK) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: IDLE is not supported\n", __func__);
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "Bad offset 0x%" HWADDR_PRIx "\n",
+ addr << 2);
+ }
+}
+
+static const MemoryRegionOps ibex_spi_ops = {
+ .read = ibex_spi_host_read,
+ .write = ibex_spi_host_write,
+ /* Ibex default LE */
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static Property ibex_spi_properties[] = {
+ DEFINE_PROP_UINT32("num_cs", IbexSPIHostState, num_cs, 1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription vmstate_ibex = {
+ .name = TYPE_IBEX_SPI_HOST,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, IbexSPIHostState, IBEX_SPI_HOST_MAX_REGS),
+ VMSTATE_VARRAY_UINT32(config_opts, IbexSPIHostState,
+ num_cs, 0, vmstate_info_uint32, uint32_t),
+ VMSTATE_FIFO8(rx_fifo, IbexSPIHostState),
+ VMSTATE_FIFO8(tx_fifo, IbexSPIHostState),
+ VMSTATE_TIMER_PTR(fifo_trigger_handle, IbexSPIHostState),
+ VMSTATE_BOOL(init_status, IbexSPIHostState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void fifo_trigger_update(void *opaque)
+{
+ IbexSPIHostState *s = opaque;
+ ibex_spi_host_transfer(s);
+}
+
+static void ibex_spi_host_realize(DeviceState *dev, Error **errp)
+{
+ IbexSPIHostState *s = IBEX_SPI_HOST(dev);
+ int i;
+
+ s->ssi = ssi_create_bus(dev, "ssi");
+ s->cs_lines = g_new0(qemu_irq, s->num_cs);
+
+ for (i = 0; i < s->num_cs; ++i) {
+ sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->cs_lines[i]);
+ }
+
+ /* Setup CONFIGOPTS Multi-register */
+ s->config_opts = g_new0(uint32_t, s->num_cs);
+
+ /* Setup FIFO Interrupt Timer */
+ s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ fifo_trigger_update, s);
+
+ /* FIFO sizes as per OT Spec */
+ fifo8_create(&s->tx_fifo, IBEX_SPI_HOST_TXFIFO_LEN);
+ fifo8_create(&s->rx_fifo, IBEX_SPI_HOST_RXFIFO_LEN);
+}
+
+static void ibex_spi_host_init(Object *obj)
+{
+ IbexSPIHostState *s = IBEX_SPI_HOST(obj);
+
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->host_err);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->event);
+
+ memory_region_init_io(&s->mmio, obj, &ibex_spi_ops, s,
+ TYPE_IBEX_SPI_HOST, 0x1000);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static void ibex_spi_host_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ dc->realize = ibex_spi_host_realize;
+ dc->reset = ibex_spi_host_reset;
+ dc->vmsd = &vmstate_ibex;
+ device_class_set_props(dc, ibex_spi_properties);
+}
+
+static const TypeInfo ibex_spi_host_info = {
+ .name = TYPE_IBEX_SPI_HOST,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IbexSPIHostState),
+ .instance_init = ibex_spi_host_init,
+ .class_init = ibex_spi_host_class_init,
+};
+
+static void ibex_spi_host_register_types(void)
+{
+ type_register_static(&ibex_spi_host_info);
+}
+
+type_init(ibex_spi_host_register_types)
diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build
index 0ded9cd092..702aa5e4df 100644
--- a/hw/ssi/meson.build
+++ b/hw/ssi/meson.build
@@ -10,3 +10,4 @@ softmmu_ss.add(when: 'CONFIG_XILINX_SPIPS', if_true: files('xilinx_spips.c'))
softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-ospi.c'))
softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_spi.c'))
softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_spi.c'))
+softmmu_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_spi_host.c'))
diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events
index 612d3d6087..c707d4aaba 100644
--- a/hw/ssi/trace-events
+++ b/hw/ssi/trace-events
@@ -20,3 +20,10 @@ npcm7xx_fiu_ctrl_read(const char *id, uint64_t addr, uint32_t data) "%s offset:
npcm7xx_fiu_ctrl_write(const char *id, uint64_t addr, uint32_t data) "%s offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
npcm7xx_fiu_flash_read(const char *id, int cs, uint64_t addr, unsigned int size, uint64_t value) "%s[%d] offset: 0x%08" PRIx64 " size: %u value: 0x%" PRIx64
npcm7xx_fiu_flash_write(const char *id, unsigned cs, uint64_t addr, unsigned int size, uint64_t value) "%s[%d] offset: 0x%08" PRIx64 " size: %u value: 0x%" PRIx64
+
+# ibex_spi_host.c
+
+ibex_spi_host_reset(const char *msg) "%s"
+ibex_spi_host_transfer(uint32_t tx_data, uint32_t rx_data) "tx_data: 0x%" PRIx32 " rx_data: @0x%" PRIx32
+ibex_spi_host_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64
+ibex_spi_host_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size %u:"
diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
index a5102eac9e..333348d9d5 100644
--- a/hw/virtio/trace-events
+++ b/hw/virtio/trace-events
@@ -25,6 +25,8 @@ vhost_user_postcopy_waker_nomatch(const char *rb, uint64_t rb_offset) "%s + 0x%"
# vhost-vdpa.c
vhost_vdpa_dma_map(void *vdpa, int fd, uint32_t msg_type, uint64_t iova, uint64_t size, uint64_t uaddr, uint8_t perm, uint8_t type) "vdpa:%p fd: %d msg_type: %"PRIu32" iova: 0x%"PRIx64" size: 0x%"PRIx64" uaddr: 0x%"PRIx64" perm: 0x%"PRIx8" type: %"PRIu8
vhost_vdpa_dma_unmap(void *vdpa, int fd, uint32_t msg_type, uint64_t iova, uint64_t size, uint8_t type) "vdpa:%p fd: %d msg_type: %"PRIu32" iova: 0x%"PRIx64" size: 0x%"PRIx64" type: %"PRIu8
+vhost_vdpa_listener_begin_batch(void *v, int fd, uint32_t msg_type, uint8_t type) "vdpa:%p fd: %d msg_type: %"PRIu32" type: %"PRIu8
+vhost_vdpa_listener_commit(void *v, int fd, uint32_t msg_type, uint8_t type) "vdpa:%p fd: %d msg_type: %"PRIu32" type: %"PRIu8
vhost_vdpa_listener_region_add(void *vdpa, uint64_t iova, uint64_t llend, void *vaddr, bool readonly) "vdpa: %p iova 0x%"PRIx64" llend 0x%"PRIx64" vaddr: %p read-only: %d"
vhost_vdpa_listener_region_del(void *vdpa, uint64_t iova, uint64_t llend) "vdpa: %p iova 0x%"PRIx64" llend 0x%"PRIx64
vhost_vdpa_add_status(void *dev, uint8_t status) "dev: %p status: 0x%"PRIx8
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
index b57be529c7..a30510ed17 100644
--- a/hw/virtio/vhost-vdpa.c
+++ b/hw/virtio/vhost-vdpa.c
@@ -129,6 +129,7 @@ static void vhost_vdpa_listener_begin_batch(struct vhost_vdpa *v)
.iotlb.type = VHOST_IOTLB_BATCH_BEGIN,
};
+ trace_vhost_vdpa_listener_begin_batch(v, fd, msg.type, msg.iotlb.type);
if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) {
error_report("failed to write, fd=%d, errno=%d (%s)",
fd, errno, strerror(errno));
@@ -163,6 +164,7 @@ static void vhost_vdpa_listener_commit(MemoryListener *listener)
msg.type = v->msg_type;
msg.iotlb.type = VHOST_IOTLB_BATCH_END;
+ trace_vhost_vdpa_listener_commit(v, fd, msg.type, msg.iotlb.type);
if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) {
error_report("failed to write, fd=%d, errno=%d (%s)",
fd, errno, strerror(errno));
diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h
index 0f21b0570b..8b2e95f5ff 100644
--- a/include/block/block_int-global-state.h
+++ b/include/block/block_int-global-state.h
@@ -262,7 +262,7 @@ BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node,
BlockDriverState **pbs,
Error **errp);
BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target,
- BlockDirtyBitmapMergeSourceList *bms,
+ BlockDirtyBitmapOrStrList *bms,
HBitmap **backup, Error **errp);
BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name,
bool release,
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 7e76ee2619..15feabac63 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -113,6 +113,7 @@ typedef enum VirtGICType {
VIRT_GIC_VERSION_HOST,
VIRT_GIC_VERSION_2,
VIRT_GIC_VERSION_3,
+ VIRT_GIC_VERSION_4,
VIRT_GIC_VERSION_NOSEL,
} VirtGICType;
@@ -185,13 +186,25 @@ OBJECT_DECLARE_TYPE(VirtMachineState, VirtMachineClass, VIRT_MACHINE)
void virt_acpi_setup(VirtMachineState *vms);
bool virt_is_acpi_enabled(VirtMachineState *vms);
+/* Return number of redistributors that fit in the specified region */
+static uint32_t virt_redist_capacity(VirtMachineState *vms, int region)
+{
+ uint32_t redist_size;
+
+ if (vms->gic_version == VIRT_GIC_VERSION_3) {
+ redist_size = GICV3_REDIST_SIZE;
+ } else {
+ redist_size = GICV4_REDIST_SIZE;
+ }
+ return vms->memmap[region].size / redist_size;
+}
+
/* Return the number of used redistributor regions */
static inline int virt_gicv3_redist_region_count(VirtMachineState *vms)
{
- uint32_t redist0_capacity =
- vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE;
+ uint32_t redist0_capacity = virt_redist_capacity(vms, VIRT_GIC_REDIST);
- assert(vms->gic_version == VIRT_GIC_VERSION_3);
+ assert(vms->gic_version != VIRT_GIC_VERSION_2);
return (MACHINE(vms)->smp.cpus > redist0_capacity &&
vms->highmem_redists) ? 2 : 1;
diff --git a/include/hw/core/tcg-cpu-ops.h b/include/hw/core/tcg-cpu-ops.h
index fbe6b76764..78c6c6635d 100644
--- a/include/hw/core/tcg-cpu-ops.h
+++ b/include/hw/core/tcg-cpu-ops.h
@@ -90,6 +90,7 @@ struct TCGCPUOps {
/**
* @debug_check_watchpoint: return true if the architectural
* watchpoint whose address has matched should really fire, used by ARM
+ * and RISC-V
*/
bool (*debug_check_watchpoint)(CPUState *cpu, CPUWatchpoint *wp);
diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
index fc38e4b7dc..4e41610055 100644
--- a/include/hw/intc/arm_gicv3_common.h
+++ b/include/hw/intc/arm_gicv3_common.h
@@ -38,7 +38,12 @@
#define GICV3_LPI_INTID_START 8192
+/*
+ * The redistributor in GICv3 has two 64KB frames per CPU; in
+ * GICv4 it has four 64KB frames per CPU.
+ */
#define GICV3_REDIST_SIZE 0x20000
+#define GICV4_REDIST_SIZE 0x40000
/* Number of SGI target-list bits */
#define GICV3_TARGETLIST_BITS 16
@@ -174,6 +179,9 @@ struct GICv3CPUState {
uint32_t gicr_igrpmodr0;
uint32_t gicr_nsacr;
uint8_t gicr_ipriorityr[GIC_INTERNAL];
+ /* VLPI_base page registers */
+ uint64_t gicr_vpropbaser;
+ uint64_t gicr_vpendbaser;
/* CPU interface */
uint64_t icc_sre_el1;
@@ -211,6 +219,9 @@ struct GICv3CPUState {
*/
PendingIrq hpplpi;
+ /* Cached information recalculated from vLPI tables in guest memory */
+ PendingIrq hppvlpi;
+
/* This is temporary working state, to avoid a malloc in gicv3_update() */
bool seenbetter;
};
@@ -272,6 +283,8 @@ struct GICv3State {
uint32_t gicd_nsacr[DIV_ROUND_UP(GICV3_MAXIRQ, 16)];
GICv3CPUState *cpu;
+ /* List of all ITSes connected to this GIC */
+ GPtrArray *itslist;
};
#define GICV3_BITMAP_ACCESSORS(BMP) \
diff --git a/include/hw/intc/arm_gicv3_its_common.h b/include/hw/intc/arm_gicv3_its_common.h
index 0f130494dd..a11a0f6654 100644
--- a/include/hw/intc/arm_gicv3_its_common.h
+++ b/include/hw/intc/arm_gicv3_its_common.h
@@ -78,6 +78,7 @@ struct GICv3ITSState {
TableDesc dt;
TableDesc ct;
+ TableDesc vpet;
CmdQDesc cq;
Error *migration_blocker;
@@ -88,6 +89,24 @@ typedef struct GICv3ITSState GICv3ITSState;
void gicv3_its_init_mmio(GICv3ITSState *s, const MemoryRegionOps *ops,
const MemoryRegionOps *tops);
+/*
+ * The ITS should call this when it is realized to add itself
+ * to its GIC's list of connected ITSes.
+ */
+static inline void gicv3_add_its(GICv3State *s, DeviceState *its)
+{
+ g_ptr_array_add(s->itslist, its);
+}
+
+/*
+ * The ITS can use this for operations that must be performed on
+ * every ITS connected to the same GIC that it is
+ */
+static inline void gicv3_foreach_its(GICv3State *s, GFunc func, void *opaque)
+{
+ g_ptr_array_foreach(s->itslist, func, opaque);
+}
+
#define TYPE_ARM_GICV3_ITS_COMMON "arm-gicv3-its-common"
typedef struct GICv3ITSCommonClass GICv3ITSCommonClass;
DECLARE_OBJ_CHECKERS(GICv3ITSState, GICv3ITSCommonClass,
diff --git a/include/hw/intc/nios2_vic.h b/include/hw/intc/nios2_vic.h
new file mode 100644
index 0000000000..af1517a967
--- /dev/null
+++ b/include/hw/intc/nios2_vic.h
@@ -0,0 +1,64 @@
+/*
+ * Vectored Interrupt Controller for nios2 processor
+ *
+ * Copyright (c) 2022 Neuroblade
+ *
+ * Interface:
+ * QOM property "cpu": link to the Nios2 CPU (must be set)
+ * Unnamed GPIO inputs 0..NIOS2_VIC_MAX_IRQ-1: input IRQ lines
+ * IRQ should be connected to nios2 IRQ0.
+ *
+ * Reference: "Embedded Peripherals IP User Guide
+ * for Intel® Quartus® Prime Design Suite: 21.4"
+ * Chapter 38 "Vectored Interrupt Controller Core"
+ * See: https://www.intel.com/content/www/us/en/docs/programmable/683130/21-4/vectored-interrupt-controller-core.html
+ *
+ * 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 HW_INTC_NIOS2_VIC
+#define HW_INTC_NIOS2_VIC
+
+#define TYPE_NIOS2_VIC "nios2-vic"
+OBJECT_DECLARE_SIMPLE_TYPE(Nios2VIC, NIOS2_VIC)
+
+#define NIOS2_VIC_MAX_IRQ 32
+
+struct Nios2VIC {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ qemu_irq output_int;
+
+ /* properties */
+ CPUState *cpu;
+ MemoryRegion csr;
+
+ uint32_t int_config[NIOS2_VIC_MAX_IRQ];
+ uint32_t vic_config;
+ uint32_t int_raw_status;
+ uint32_t int_enable;
+ uint32_t sw_int;
+ uint32_t vic_status;
+ uint32_t vec_tbl_base;
+ uint32_t vec_tbl_addr;
+};
+
+#endif /* HW_INTC_NIOS2_VIC */
diff --git a/include/hw/intc/riscv_aclint.h b/include/hw/intc/riscv_aclint.h
index 229bd08d25..26d4048687 100644
--- a/include/hw/intc/riscv_aclint.h
+++ b/include/hw/intc/riscv_aclint.h
@@ -31,6 +31,7 @@
typedef struct RISCVAclintMTimerState {
/*< private >*/
SysBusDevice parent_obj;
+ uint64_t time_delta;
/*< public >*/
MemoryRegion mmio;
diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h
index d937c5c224..d2db29721a 100644
--- a/include/hw/riscv/boot.h
+++ b/include/hw/riscv/boot.h
@@ -46,12 +46,12 @@ target_ulong riscv_load_kernel(const char *kernel_filename,
symbol_fn_t sym_cb);
hwaddr riscv_load_initrd(const char *filename, uint64_t mem_size,
uint64_t kernel_entry, hwaddr *start);
-uint32_t riscv_load_fdt(hwaddr dram_start, uint64_t dram_size, void *fdt);
+uint64_t riscv_load_fdt(hwaddr dram_start, uint64_t dram_size, void *fdt);
void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts,
hwaddr saddr,
hwaddr rom_base, hwaddr rom_size,
uint64_t kernel_entry,
- uint32_t fdt_load_addr, void *fdt);
+ uint64_t fdt_load_addr, void *fdt);
void riscv_rom_copy_firmware_info(MachineState *machine, hwaddr rom_base,
hwaddr rom_size,
uint32_t reset_vec_size,
diff --git a/include/hw/riscv/opentitan.h b/include/hw/riscv/opentitan.h
index 00da9ded43..68892cd8e5 100644
--- a/include/hw/riscv/opentitan.h
+++ b/include/hw/riscv/opentitan.h
@@ -23,11 +23,18 @@
#include "hw/intc/sifive_plic.h"
#include "hw/char/ibex_uart.h"
#include "hw/timer/ibex_timer.h"
+#include "hw/ssi/ibex_spi_host.h"
#include "qom/object.h"
#define TYPE_RISCV_IBEX_SOC "riscv.lowrisc.ibex.soc"
OBJECT_DECLARE_SIMPLE_TYPE(LowRISCIbexSoCState, RISCV_IBEX_SOC)
+enum {
+ OPENTITAN_SPI_HOST0,
+ OPENTITAN_SPI_HOST1,
+ OPENTITAN_NUM_SPI_HOSTS,
+};
+
struct LowRISCIbexSoCState {
/*< private >*/
SysBusDevice parent_obj;
@@ -37,6 +44,7 @@ struct LowRISCIbexSoCState {
SiFivePLICState plic;
IbexUartState uart;
IbexTimerState timer;
+ IbexSPIHostState spi_host[OPENTITAN_NUM_SPI_HOSTS];
MemoryRegion flash_mem;
MemoryRegion rom;
@@ -89,15 +97,19 @@ enum {
};
enum {
- IBEX_TIMER_TIMEREXPIRED0_0 = 126,
- IBEX_UART0_RX_PARITY_ERR_IRQ = 8,
- IBEX_UART0_RX_TIMEOUT_IRQ = 7,
- IBEX_UART0_RX_BREAK_ERR_IRQ = 6,
- IBEX_UART0_RX_FRAME_ERR_IRQ = 5,
- IBEX_UART0_RX_OVERFLOW_IRQ = 4,
- IBEX_UART0_TX_EMPTY_IRQ = 3,
- IBEX_UART0_RX_WATERMARK_IRQ = 2,
- IBEX_UART0_TX_WATERMARK_IRQ = 1,
+ IBEX_UART0_TX_WATERMARK_IRQ = 1,
+ IBEX_UART0_RX_WATERMARK_IRQ = 2,
+ IBEX_UART0_TX_EMPTY_IRQ = 3,
+ IBEX_UART0_RX_OVERFLOW_IRQ = 4,
+ IBEX_UART0_RX_FRAME_ERR_IRQ = 5,
+ IBEX_UART0_RX_BREAK_ERR_IRQ = 6,
+ IBEX_UART0_RX_TIMEOUT_IRQ = 7,
+ IBEX_UART0_RX_PARITY_ERR_IRQ = 8,
+ IBEX_TIMER_TIMEREXPIRED0_0 = 126,
+ IBEX_SPI_HOST0_ERR_IRQ = 150,
+ IBEX_SPI_HOST0_SPI_EVENT_IRQ = 151,
+ IBEX_SPI_HOST1_ERR_IRQ = 152,
+ IBEX_SPI_HOST1_SPI_EVENT_IRQ = 153,
};
#endif
diff --git a/include/hw/ssi/ibex_spi_host.h b/include/hw/ssi/ibex_spi_host.h
new file mode 100644
index 0000000000..3fedcb6805
--- /dev/null
+++ b/include/hw/ssi/ibex_spi_host.h
@@ -0,0 +1,94 @@
+
+/*
+ * QEMU model of the Ibex SPI Controller
+ * SPEC Reference: https://docs.opentitan.org/hw/ip/spi_host/doc/
+ *
+ * Copyright (C) 2022 Western Digital
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef IBEX_SPI_HOST_H
+#define IBEX_SPI_HOST_H
+
+#include "hw/sysbus.h"
+#include "hw/hw.h"
+#include "hw/ssi/ssi.h"
+#include "qemu/fifo8.h"
+#include "qom/object.h"
+#include "hw/registerfields.h"
+#include "qemu/timer.h"
+
+#define TYPE_IBEX_SPI_HOST "ibex-spi"
+#define IBEX_SPI_HOST(obj) \
+ OBJECT_CHECK(IbexSPIHostState, (obj), TYPE_IBEX_SPI_HOST)
+
+/* SPI Registers */
+#define IBEX_SPI_HOST_INTR_STATE (0x00 / 4) /* rw */
+#define IBEX_SPI_HOST_INTR_ENABLE (0x04 / 4) /* rw */
+#define IBEX_SPI_HOST_INTR_TEST (0x08 / 4) /* wo */
+#define IBEX_SPI_HOST_ALERT_TEST (0x0c / 4) /* wo */
+#define IBEX_SPI_HOST_CONTROL (0x10 / 4) /* rw */
+#define IBEX_SPI_HOST_STATUS (0x14 / 4) /* ro */
+#define IBEX_SPI_HOST_CONFIGOPTS (0x18 / 4) /* rw */
+#define IBEX_SPI_HOST_CSID (0x1c / 4) /* rw */
+#define IBEX_SPI_HOST_COMMAND (0x20 / 4) /* wo */
+/* RX/TX Modelled by FIFO */
+#define IBEX_SPI_HOST_RXDATA (0x24 / 4)
+#define IBEX_SPI_HOST_TXDATA (0x28 / 4)
+
+#define IBEX_SPI_HOST_ERROR_ENABLE (0x2c / 4) /* rw */
+#define IBEX_SPI_HOST_ERROR_STATUS (0x30 / 4) /* rw */
+#define IBEX_SPI_HOST_EVENT_ENABLE (0x34 / 4) /* rw */
+
+/* FIFO Len in Bytes */
+#define IBEX_SPI_HOST_TXFIFO_LEN 288
+#define IBEX_SPI_HOST_RXFIFO_LEN 256
+
+/* Max Register (Based on addr) */
+#define IBEX_SPI_HOST_MAX_REGS (IBEX_SPI_HOST_EVENT_ENABLE + 1)
+
+/* MISC */
+#define TX_INTERRUPT_TRIGGER_DELAY_NS 100
+#define BIDIRECTIONAL_TRANSFER 3
+
+typedef struct {
+ /* <private> */
+ SysBusDevice parent_obj;
+
+ /* <public> */
+ MemoryRegion mmio;
+ uint32_t regs[IBEX_SPI_HOST_MAX_REGS];
+ /* Multi-reg that sets config opts per CS */
+ uint32_t *config_opts;
+ Fifo8 rx_fifo;
+ Fifo8 tx_fifo;
+ QEMUTimer *fifo_trigger_handle;
+
+ qemu_irq event;
+ qemu_irq host_err;
+ uint32_t num_cs;
+ qemu_irq *cs_lines;
+ SSIBus *ssi;
+
+ /* Used to track the init status, for replicating TXDATA ghost writes */
+ bool init_status;
+} IbexSPIHostState;
+
+#endif
diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h
index baaa23c156..52d81c027b 100644
--- a/include/qemu/osdep.h
+++ b/include/qemu/osdep.h
@@ -553,9 +553,6 @@ int qemu_pipe(int pipefd[2]);
void qemu_set_cloexec(int fd);
-void fips_set_state(bool requested);
-bool fips_get_state(void);
-
/* Return a dynamically allocated directory path that is appropriate for storing
* local state.
*
diff --git a/include/qemu/win_dump_defs.h b/include/qemu/win_dump_defs.h
index 145096e8ee..73a44e2408 100644
--- a/include/qemu/win_dump_defs.h
+++ b/include/qemu/win_dump_defs.h
@@ -11,11 +11,22 @@
#ifndef QEMU_WIN_DUMP_DEFS_H
#define QEMU_WIN_DUMP_DEFS_H
+typedef struct WinDumpPhyMemRun32 {
+ uint32_t BasePage;
+ uint32_t PageCount;
+} QEMU_PACKED WinDumpPhyMemRun32;
+
typedef struct WinDumpPhyMemRun64 {
uint64_t BasePage;
uint64_t PageCount;
} QEMU_PACKED WinDumpPhyMemRun64;
+typedef struct WinDumpPhyMemDesc32 {
+ uint32_t NumberOfRuns;
+ uint32_t NumberOfPages;
+ WinDumpPhyMemRun32 Run[86];
+} QEMU_PACKED WinDumpPhyMemDesc32;
+
typedef struct WinDumpPhyMemDesc64 {
uint32_t NumberOfRuns;
uint32_t unused;
@@ -33,6 +44,39 @@ typedef struct WinDumpExceptionRecord {
uint64_t ExceptionInformation[15];
} QEMU_PACKED WinDumpExceptionRecord;
+typedef struct WinDumpHeader32 {
+ char Signature[4];
+ char ValidDump[4];
+ uint32_t MajorVersion;
+ uint32_t MinorVersion;
+ uint32_t DirectoryTableBase;
+ uint32_t PfnDatabase;
+ uint32_t PsLoadedModuleList;
+ uint32_t PsActiveProcessHead;
+ uint32_t MachineImageType;
+ uint32_t NumberProcessors;
+ union {
+ struct {
+ uint32_t BugcheckCode;
+ uint32_t BugcheckParameter1;
+ uint32_t BugcheckParameter2;
+ uint32_t BugcheckParameter3;
+ uint32_t BugcheckParameter4;
+ };
+ uint8_t BugcheckData[20];
+ };
+ uint8_t VersionUser[32];
+ uint32_t reserved0;
+ uint32_t KdDebuggerDataBlock;
+ union {
+ WinDumpPhyMemDesc32 PhysicalMemoryBlock;
+ uint8_t PhysicalMemoryBlockBuffer[700];
+ };
+ uint8_t reserved1[3200];
+ uint32_t RequiredDumpSpace;
+ uint8_t reserved2[92];
+} QEMU_PACKED WinDumpHeader32;
+
typedef struct WinDumpHeader64 {
char Signature[4];
char ValidDump[4];
@@ -81,24 +125,48 @@ typedef struct WinDumpHeader64 {
uint8_t reserved[4018];
} QEMU_PACKED WinDumpHeader64;
+typedef union WinDumpHeader {
+ struct {
+ char Signature[4];
+ char ValidDump[4];
+ };
+ WinDumpHeader32 x32;
+ WinDumpHeader64 x64;
+} WinDumpHeader;
+
#define KDBG_OWNER_TAG_OFFSET64 0x10
#define KDBG_MM_PFN_DATABASE_OFFSET64 0xC0
#define KDBG_KI_BUGCHECK_DATA_OFFSET64 0x88
#define KDBG_KI_PROCESSOR_BLOCK_OFFSET64 0x218
#define KDBG_OFFSET_PRCB_CONTEXT_OFFSET64 0x338
+#define KDBG_OWNER_TAG_OFFSET KDBG_OWNER_TAG_OFFSET64
+#define KDBG_MM_PFN_DATABASE_OFFSET KDBG_MM_PFN_DATABASE_OFFSET64
+#define KDBG_KI_BUGCHECK_DATA_OFFSET KDBG_KI_BUGCHECK_DATA_OFFSET64
+#define KDBG_KI_PROCESSOR_BLOCK_OFFSET KDBG_KI_PROCESSOR_BLOCK_OFFSET64
+#define KDBG_OFFSET_PRCB_CONTEXT_OFFSET KDBG_OFFSET_PRCB_CONTEXT_OFFSET64
+
#define VMCOREINFO_ELF_NOTE_HDR_SIZE 24
+#define VMCOREINFO_WIN_DUMP_NOTE_SIZE64 (sizeof(WinDumpHeader64) + \
+ VMCOREINFO_ELF_NOTE_HDR_SIZE)
+#define VMCOREINFO_WIN_DUMP_NOTE_SIZE32 (sizeof(WinDumpHeader32) + \
+ VMCOREINFO_ELF_NOTE_HDR_SIZE)
#define WIN_CTX_X64 0x00100000L
+#define WIN_CTX_X86 0x00010000L
#define WIN_CTX_CTL 0x00000001L
#define WIN_CTX_INT 0x00000002L
#define WIN_CTX_SEG 0x00000004L
#define WIN_CTX_FP 0x00000008L
#define WIN_CTX_DBG 0x00000010L
+#define WIN_CTX_EXT 0x00000020L
+
+#define WIN_CTX64_FULL (WIN_CTX_X64 | WIN_CTX_CTL | WIN_CTX_INT | WIN_CTX_FP)
+#define WIN_CTX64_ALL (WIN_CTX64_FULL | WIN_CTX_SEG | WIN_CTX_DBG)
-#define WIN_CTX_FULL (WIN_CTX_X64 | WIN_CTX_CTL | WIN_CTX_INT | WIN_CTX_FP)
-#define WIN_CTX_ALL (WIN_CTX_FULL | WIN_CTX_SEG | WIN_CTX_DBG)
+#define WIN_CTX32_FULL (WIN_CTX_X86 | WIN_CTX_CTL | WIN_CTX_INT | WIN_CTX_SEG)
+#define WIN_CTX32_ALL (WIN_CTX32_FULL | WIN_CTX_FP | WIN_CTX_DBG | WIN_CTX_EXT)
#define LIVE_SYSTEM_DUMP 0x00000161
@@ -107,7 +175,41 @@ typedef struct WinM128A {
int64_t high;
} QEMU_ALIGNED(16) WinM128A;
-typedef struct WinContext {
+typedef struct WinContext32 {
+ uint32_t ContextFlags;
+
+ uint32_t Dr0;
+ uint32_t Dr1;
+ uint32_t Dr2;
+ uint32_t Dr3;
+ uint32_t Dr6;
+ uint32_t Dr7;
+
+ uint8_t FloatSave[112];
+
+ uint32_t SegGs;
+ uint32_t SegFs;
+ uint32_t SegEs;
+ uint32_t SegDs;
+
+ uint32_t Edi;
+ uint32_t Esi;
+ uint32_t Ebx;
+ uint32_t Edx;
+ uint32_t Ecx;
+ uint32_t Eax;
+
+ uint32_t Ebp;
+ uint32_t Eip;
+ uint32_t SegCs;
+ uint32_t EFlags;
+ uint32_t Esp;
+ uint32_t SegSs;
+
+ uint8_t ExtendedRegisters[512];
+} QEMU_ALIGNED(16) WinContext32;
+
+typedef struct WinContext64 {
uint64_t PHome[6];
uint32_t ContextFlags;
@@ -174,6 +276,11 @@ typedef struct WinContext {
uint64_t LastBranchFromRip;
uint64_t LastExceptionToRip;
uint64_t LastExceptionFromRip;
-} QEMU_ALIGNED(16) WinContext;
+} QEMU_ALIGNED(16) WinContext64;
+
+typedef union WinContext {
+ WinContext32 x32;
+ WinContext64 x64;
+} WinContext;
#endif /* QEMU_WIN_DUMP_DEFS_H */
diff --git a/include/sysemu/dump.h b/include/sysemu/dump.h
index 250143cb5a..ffc2ea1072 100644
--- a/include/sysemu/dump.h
+++ b/include/sysemu/dump.h
@@ -154,12 +154,15 @@ typedef struct DumpState {
GuestPhysBlockList guest_phys_blocks;
ArchDumpInfo dump_info;
MemoryMappingList list;
- uint16_t phdr_num;
- uint32_t sh_info;
- bool have_section;
+ uint32_t phdr_num;
+ uint32_t shdr_num;
bool resume;
bool detached;
ssize_t note_size;
+ hwaddr shdr_offset;
+ hwaddr phdr_offset;
+ hwaddr section_offset;
+ hwaddr note_offset;
hwaddr memory_offset;
int fd;
diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c
index 3268e5f1f1..d950409d5b 100644
--- a/linux-user/arm/cpu_loop.c
+++ b/linux-user/arm/cpu_loop.c
@@ -230,7 +230,7 @@ do_kernel_trap(CPUARMState *env)
/* Jump back to the caller. */
addr = env->regs[14];
if (addr & 1) {
- env->thumb = 1;
+ env->thumb = true;
addr &= ~1;
}
env->regs[15] = addr;
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index d6bb1fc7ca..61063fd974 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1096,7 +1096,6 @@ static void init_thread(struct target_pt_regs *regs, struct image_info *infop)
{
regs->ea = infop->entry;
regs->sp = infop->start_stack;
- regs->estatus = 0x3;
}
#define LO_COMMPAGE TARGET_PAGE_SIZE
@@ -1170,7 +1169,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs,
(*regs)[30] = -1; /* R_SSTATUS */
(*regs)[31] = tswapreg(env->regs[R_RA]);
- (*regs)[32] = tswapreg(env->regs[R_PC]);
+ (*regs)[32] = tswapreg(env->pc);
(*regs)[33] = -1; /* R_STATUS */
(*regs)[34] = tswapreg(env->regs[CR_ESTATUS]);
diff --git a/linux-user/nios2/cpu_loop.c b/linux-user/nios2/cpu_loop.c
index 1e93ef34e6..da77ede76b 100644
--- a/linux-user/nios2/cpu_loop.c
+++ b/linux-user/nios2/cpu_loop.c
@@ -26,7 +26,6 @@
void cpu_loop(CPUNios2State *env)
{
CPUState *cs = env_cpu(env);
- target_siginfo_t info;
int trapnr, ret;
for (;;) {
@@ -39,6 +38,30 @@ void cpu_loop(CPUNios2State *env)
/* just indicate that signals should be handled asap */
break;
+ case EXCP_DIV:
+ /* Match kernel's handle_diverror_c(). */
+ env->pc -= 4;
+ force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTDIV, env->pc);
+ break;
+
+ case EXCP_UNALIGN:
+ case EXCP_UNALIGND:
+ force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN,
+ env->ctrl[CR_BADADDR]);
+ break;
+
+ case EXCP_ILLEGAL:
+ case EXCP_UNIMPL:
+ /* Match kernel's handle_illegal_c(). */
+ env->pc -= 4;
+ force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->pc);
+ break;
+ case EXCP_SUPERI:
+ /* Match kernel's handle_supervisor_instr(). */
+ env->pc -= 4;
+ force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->pc);
+ break;
+
case EXCP_TRAP:
switch (env->error_code) {
case 0:
@@ -49,32 +72,41 @@ void cpu_loop(CPUNios2State *env)
env->regs[7], env->regs[8], env->regs[9],
0, 0);
- if (env->regs[2] == 0) { /* FIXME: syscall 0 workaround */
- ret = 0;
+ if (ret == -QEMU_ESIGRETURN) {
+ /* rt_sigreturn has set all state. */
+ break;
}
-
+ if (ret == -QEMU_ERESTARTSYS) {
+ env->pc -= 4;
+ break;
+ }
+ /*
+ * See the code after translate_rc_and_ret: all negative
+ * values are errors (aided by userspace restricted to 2G),
+ * errno is returned positive in r2, and error indication
+ * is a boolean in r7.
+ */
env->regs[2] = abs(ret);
- /* Return value is 0..4096 */
- env->regs[7] = ret > 0xfffff000u;
- env->regs[R_PC] += 4;
+ env->regs[7] = ret < 0;
break;
case 1:
qemu_log_mask(CPU_LOG_INT, "\nTrap 1\n");
- force_sig_fault(TARGET_SIGUSR1, 0, env->regs[R_PC]);
+ force_sig_fault(TARGET_SIGUSR1, 0, env->pc);
break;
case 2:
qemu_log_mask(CPU_LOG_INT, "\nTrap 2\n");
- force_sig_fault(TARGET_SIGUSR2, 0, env->regs[R_PC]);
+ force_sig_fault(TARGET_SIGUSR2, 0, env->pc);
break;
case 31:
qemu_log_mask(CPU_LOG_INT, "\nTrap 31\n");
- force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->regs[R_PC]);
+ /* Match kernel's breakpoint_c(). */
+ env->pc -= 4;
+ force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc);
break;
default:
qemu_log_mask(CPU_LOG_INT, "\nTrap %d\n", env->error_code);
- force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLTRP,
- env->regs[R_PC]);
+ force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLTRP, env->pc);
break;
case 16: /* QEMU specific, for __kuser_cmpxchg */
@@ -99,27 +131,13 @@ void cpu_loop(CPUNios2State *env)
o = env->regs[5];
n = env->regs[6];
env->regs[2] = qatomic_cmpxchg(h, o, n) - o;
- env->regs[R_PC] += 4;
}
break;
}
break;
case EXCP_DEBUG:
- info.si_signo = TARGET_SIGTRAP;
- info.si_errno = 0;
- info.si_code = TARGET_TRAP_BRKPT;
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- break;
- case 0xaa:
- {
- info.si_signo = TARGET_SIGSEGV;
- info.si_errno = 0;
- /* TODO: check env->error_code */
- info.si_code = TARGET_SEGV_MAPERR;
- info._sifields._sigfault._addr = env->regs[R_PC];
- queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
- }
+ force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc);
break;
default:
EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n",
@@ -133,28 +151,6 @@ void cpu_loop(CPUNios2State *env)
void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
{
- env->regs[0] = 0;
- env->regs[1] = regs->r1;
- env->regs[2] = regs->r2;
- env->regs[3] = regs->r3;
- env->regs[4] = regs->r4;
- env->regs[5] = regs->r5;
- env->regs[6] = regs->r6;
- env->regs[7] = regs->r7;
- env->regs[8] = regs->r8;
- env->regs[9] = regs->r9;
- env->regs[10] = regs->r10;
- env->regs[11] = regs->r11;
- env->regs[12] = regs->r12;
- env->regs[13] = regs->r13;
- env->regs[14] = regs->r14;
- env->regs[15] = regs->r15;
- /* TODO: unsigned long orig_r2; */
- env->regs[R_RA] = regs->ra;
- env->regs[R_FP] = regs->fp;
env->regs[R_SP] = regs->sp;
- env->regs[R_GP] = regs->gp;
- env->regs[CR_ESTATUS] = regs->estatus;
- env->regs[R_PC] = regs->ea;
- /* TODO: unsigned long orig_r7; */
+ env->pc = regs->ea;
}
diff --git a/linux-user/nios2/signal.c b/linux-user/nios2/signal.c
index 517cd39270..32b3dc99c6 100644
--- a/linux-user/nios2/signal.c
+++ b/linux-user/nios2/signal.c
@@ -73,12 +73,11 @@ static void rt_setup_ucontext(struct target_ucontext *uc, CPUNios2State *env)
__put_user(env->regs[R_RA], &gregs[23]);
__put_user(env->regs[R_FP], &gregs[24]);
__put_user(env->regs[R_GP], &gregs[25]);
- __put_user(env->regs[R_PC], &gregs[27]);
+ __put_user(env->pc, &gregs[27]);
__put_user(env->regs[R_SP], &gregs[28]);
}
-static int rt_restore_ucontext(CPUNios2State *env, struct target_ucontext *uc,
- int *pr2)
+static int rt_restore_ucontext(CPUNios2State *env, struct target_ucontext *uc)
{
int temp;
unsigned long *gregs = uc->tuc_mcontext.gregs;
@@ -122,14 +121,12 @@ static int rt_restore_ucontext(CPUNios2State *env, struct target_ucontext *uc,
__get_user(env->regs[R_GP], &gregs[25]);
/* Not really necessary no user settable bits */
__get_user(temp, &gregs[26]);
- __get_user(env->regs[R_PC], &gregs[27]);
+ __get_user(env->pc, &gregs[27]);
__get_user(env->regs[R_RA], &gregs[23]);
__get_user(env->regs[R_SP], &gregs[28]);
target_restore_altstack(&uc->tuc_stack, env);
-
- *pr2 = env->regs[2];
return 0;
}
@@ -180,25 +177,17 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
env->regs[4] = sig;
env->regs[5] = frame_addr + offsetof(struct target_rt_sigframe, info);
env->regs[6] = frame_addr + offsetof(struct target_rt_sigframe, uc);
- env->regs[R_PC] = ka->_sa_handler;
+ env->pc = ka->_sa_handler;
unlock_user_struct(frame, frame_addr, 1);
}
-long do_sigreturn(CPUNios2State *env)
-{
- trace_user_do_sigreturn(env, 0);
- qemu_log_mask(LOG_UNIMP, "do_sigreturn: not implemented\n");
- return -TARGET_ENOSYS;
-}
-
long do_rt_sigreturn(CPUNios2State *env)
{
/* Verify, can we follow the stack back */
abi_ulong frame_addr = env->regs[R_SP];
struct target_rt_sigframe *frame;
sigset_t set;
- int rval;
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
goto badframe;
@@ -207,15 +196,15 @@ long do_rt_sigreturn(CPUNios2State *env)
target_to_host_sigset(&set, &frame->uc.tuc_sigmask);
set_sigmask(&set);
- if (rt_restore_ucontext(env, &frame->uc, &rval)) {
+ if (rt_restore_ucontext(env, &frame->uc)) {
goto badframe;
}
unlock_user_struct(frame, frame_addr, 0);
- return rval;
+ return -QEMU_ESIGRETURN;
badframe:
unlock_user_struct(frame, frame_addr, 0);
force_sig(TARGET_SIGSEGV);
- return 0;
+ return -QEMU_ESIGRETURN;
}
diff --git a/linux-user/nios2/target_cpu.h b/linux-user/nios2/target_cpu.h
index 2d2008f002..830b4c0741 100644
--- a/linux-user/nios2/target_cpu.h
+++ b/linux-user/nios2/target_cpu.h
@@ -27,6 +27,7 @@ static inline void cpu_clone_regs_child(CPUNios2State *env, target_ulong newsp,
env->regs[R_SP] = newsp;
}
env->regs[R_RET0] = 0;
+ env->regs[7] = 0;
}
static inline void cpu_clone_regs_parent(CPUNios2State *env, unsigned flags)
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
index 2442bfa989..93061a11af 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -1472,6 +1472,7 @@ void hmp_change(Monitor *mon, const QDict *qdict)
const char *target = qdict_get_str(qdict, "target");
const char *arg = qdict_get_try_str(qdict, "arg");
const char *read_only = qdict_get_try_str(qdict, "read-only-mode");
+ bool force = qdict_get_try_bool(qdict, "force", false);
BlockdevChangeReadOnlyMode read_only_mode = 0;
Error *err = NULL;
@@ -1508,7 +1509,8 @@ void hmp_change(Monitor *mon, const QDict *qdict)
}
qmp_blockdev_change_medium(true, device, false, NULL, target,
- !!arg, arg, !!read_only, read_only_mode,
+ !!arg, arg, true, force,
+ !!read_only, read_only_mode,
&err);
}
diff --git a/nbd/server.c b/nbd/server.c
index c5644fd3f6..4cdbc062c1 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -1643,7 +1643,7 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args,
uint64_t perm, shared_perm;
bool readonly = !exp_args->writable;
bool shared = !exp_args->writable;
- strList *bitmaps;
+ BlockDirtyBitmapOrStrList *bitmaps;
size_t i;
int ret;
@@ -1709,37 +1709,56 @@ static int nbd_export_create(BlockExport *blk_exp, BlockExportOptions *exp_args,
}
exp->export_bitmaps = g_new0(BdrvDirtyBitmap *, exp->nr_export_bitmaps);
for (i = 0, bitmaps = arg->bitmaps; bitmaps;
- i++, bitmaps = bitmaps->next) {
- const char *bitmap = bitmaps->value;
+ i++, bitmaps = bitmaps->next)
+ {
+ const char *bitmap;
BlockDriverState *bs = blk_bs(blk);
BdrvDirtyBitmap *bm = NULL;
- while (bs) {
- bm = bdrv_find_dirty_bitmap(bs, bitmap);
- if (bm != NULL) {
- break;
+ switch (bitmaps->value->type) {
+ case QTYPE_QSTRING:
+ bitmap = bitmaps->value->u.local;
+ while (bs) {
+ bm = bdrv_find_dirty_bitmap(bs, bitmap);
+ if (bm != NULL) {
+ break;
+ }
+
+ bs = bdrv_filter_or_cow_bs(bs);
}
- bs = bdrv_filter_or_cow_bs(bs);
- }
+ if (bm == NULL) {
+ ret = -ENOENT;
+ error_setg(errp, "Bitmap '%s' is not found",
+ bitmaps->value->u.local);
+ goto fail;
+ }
- if (bm == NULL) {
- ret = -ENOENT;
- error_setg(errp, "Bitmap '%s' is not found", bitmap);
- goto fail;
+ if (readonly && bdrv_is_writable(bs) &&
+ bdrv_dirty_bitmap_enabled(bm)) {
+ ret = -EINVAL;
+ error_setg(errp, "Enabled bitmap '%s' incompatible with "
+ "readonly export", bitmap);
+ goto fail;
+ }
+ break;
+ case QTYPE_QDICT:
+ bitmap = bitmaps->value->u.external.name;
+ bm = block_dirty_bitmap_lookup(bitmaps->value->u.external.node,
+ bitmap, NULL, errp);
+ if (!bm) {
+ ret = -ENOENT;
+ goto fail;
+ }
+ break;
+ default:
+ abort();
}
- if (bdrv_dirty_bitmap_check(bm, BDRV_BITMAP_ALLOW_RO, errp)) {
- ret = -EINVAL;
- goto fail;
- }
+ assert(bm);
- if (readonly && bdrv_is_writable(bs) &&
- bdrv_dirty_bitmap_enabled(bm)) {
+ if (bdrv_dirty_bitmap_check(bm, BDRV_BITMAP_ALLOW_RO, errp)) {
ret = -EINVAL;
- error_setg(errp,
- "Enabled bitmap '%s' incompatible with readonly export",
- bitmap);
goto fail;
}
diff --git a/os-posix.c b/os-posix.c
index faf6e6978b..1b746dba97 100644
--- a/os-posix.c
+++ b/os-posix.c
@@ -150,14 +150,6 @@ int os_parse_cmd_args(int index, const char *optarg)
case QEMU_OPTION_daemonize:
daemonize = 1;
break;
-#if defined(CONFIG_LINUX)
- case QEMU_OPTION_enablefips:
- warn_report("-enable-fips is deprecated, please build QEMU with "
- "the `libgcrypt` library as the cryptography provider "
- "to enable FIPS compliance");
- fips_set_state(true);
- break;
-#endif
default:
return -1;
}
diff --git a/qapi/block-core.json b/qapi/block-core.json
index beeb91952a..b66494e8c5 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2079,7 +2079,7 @@
'*persistent': 'bool', '*disabled': 'bool' } }
##
-# @BlockDirtyBitmapMergeSource:
+# @BlockDirtyBitmapOrStr:
#
# @local: name of the bitmap, attached to the same node as target bitmap.
#
@@ -2087,7 +2087,7 @@
#
# Since: 4.1
##
-{ 'alternate': 'BlockDirtyBitmapMergeSource',
+{ 'alternate': 'BlockDirtyBitmapOrStr',
'data': { 'local': 'str',
'external': 'BlockDirtyBitmap' } }
@@ -2106,7 +2106,7 @@
##
{ 'struct': 'BlockDirtyBitmapMerge',
'data': { 'node': 'str', 'target': 'str',
- 'bitmaps': ['BlockDirtyBitmapMergeSource'] } }
+ 'bitmaps': ['BlockDirtyBitmapOrStr'] } }
##
# @block-dirty-bitmap-add:
diff --git a/qapi/block-export.json b/qapi/block-export.json
index 1e34927f85..1de16d2589 100644
--- a/qapi/block-export.json
+++ b/qapi/block-export.json
@@ -6,6 +6,7 @@
##
{ 'include': 'sockets.json' }
+{ 'include': 'block-core.json' }
##
# @NbdServerOptions:
@@ -89,6 +90,7 @@
# @device, so the NBD client can use NBD_OPT_SET_META_CONTEXT with
# the metadata context name "qemu:dirty-bitmap:BITMAP" to inspect
# each bitmap.
+# Since 7.1 bitmap may be specified by node/name pair.
#
# @allocation-depth: Also export the allocation depth map for @device, so
# the NBD client can use NBD_OPT_SET_META_CONTEXT with
@@ -99,7 +101,8 @@
##
{ 'struct': 'BlockExportOptionsNbd',
'base': 'BlockExportOptionsNbdBase',
- 'data': { '*bitmaps': ['str'], '*allocation-depth': 'bool' } }
+ 'data': { '*bitmaps': ['BlockDirtyBitmapOrStr'],
+ '*allocation-depth': 'bool' } }
##
# @BlockExportOptionsVhostUserBlk:
diff --git a/qapi/block.json b/qapi/block.json
index 82fcf2c914..3f100d4887 100644
--- a/qapi/block.json
+++ b/qapi/block.json
@@ -326,6 +326,11 @@
# @read-only-mode: change the read-only mode of the device; defaults
# to 'retain'
#
+# @force: if false (the default), an eject request through blockdev-open-tray
+# will be sent to the guest if it has locked the tray (and the tray
+# will not be opened immediately); if true, the tray will be opened
+# regardless of whether it is locked. (since 7.1)
+#
# Features:
# @deprecated: Member @device is deprecated. Use @id instead.
#
@@ -367,6 +372,7 @@
'*id': 'str',
'filename': 'str',
'*format': 'str',
+ '*force': 'bool',
'*read-only-mode': 'BlockdevChangeReadOnlyMode' } }
diff --git a/qemu-img.c b/qemu-img.c
index 4c84134a1e..4cf4d2423d 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -1623,16 +1623,16 @@ static void do_dirty_bitmap_merge(const char *dst_node, const char *dst_name,
const char *src_node, const char *src_name,
Error **errp)
{
- BlockDirtyBitmapMergeSource *merge_src;
- BlockDirtyBitmapMergeSourceList *list = NULL;
+ BlockDirtyBitmapOrStr *merge_src;
+ BlockDirtyBitmapOrStrList *list = NULL;
- merge_src = g_new0(BlockDirtyBitmapMergeSource, 1);
+ merge_src = g_new0(BlockDirtyBitmapOrStr, 1);
merge_src->type = QTYPE_QDICT;
merge_src->u.external.node = g_strdup(src_node);
merge_src->u.external.name = g_strdup(src_name);
QAPI_LIST_PREPEND(list, merge_src);
qmp_block_dirty_bitmap_merge(dst_node, dst_name, list, errp);
- qapi_free_BlockDirtyBitmapMergeSourceList(list);
+ qapi_free_BlockDirtyBitmapOrStrList(list);
}
enum ImgConvertBlockStatus {
diff --git a/qemu-nbd.c b/qemu-nbd.c
index 397ffa64d7..db63980df1 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -567,7 +567,7 @@ int main(int argc, char **argv)
QDict *options = NULL;
const char *export_name = NULL; /* defaults to "" later for server mode */
const char *export_description = NULL;
- strList *bitmaps = NULL;
+ BlockDirtyBitmapOrStrList *bitmaps = NULL;
bool alloc_depth = false;
const char *tlscredsid = NULL;
const char *tlshostname = NULL;
@@ -687,7 +687,14 @@ int main(int argc, char **argv)
alloc_depth = true;
break;
case 'B':
- QAPI_LIST_PREPEND(bitmaps, g_strdup(optarg));
+ {
+ BlockDirtyBitmapOrStr *el = g_new(BlockDirtyBitmapOrStr, 1);
+ *el = (BlockDirtyBitmapOrStr) {
+ .type = QTYPE_QSTRING,
+ .u.local = g_strdup(optarg),
+ };
+ QAPI_LIST_PREPEND(bitmaps, el);
+ }
break;
case 'k':
sockpath = optarg;
diff --git a/qemu-options.hx b/qemu-options.hx
index 34e9b32a5c..1764eebfaf 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4673,16 +4673,6 @@ HXCOMM Internal use
DEF("qtest", HAS_ARG, QEMU_OPTION_qtest, "", QEMU_ARCH_ALL)
DEF("qtest-log", HAS_ARG, QEMU_OPTION_qtest_log, "", QEMU_ARCH_ALL)
-#ifdef __linux__
-DEF("enable-fips", 0, QEMU_OPTION_enablefips,
- "-enable-fips enable FIPS 140-2 compliance\n",
- QEMU_ARCH_ALL)
-#endif
-SRST
-``-enable-fips``
- Enable FIPS 140-2 compliance mode.
-ERST
-
DEF("msg", HAS_ARG, QEMU_OPTION_msg,
"-msg [timestamp[=on|off]][,guest-name=[on|off]]\n"
" control error message format\n"
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 3609de0888..e3f8215203 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -51,7 +51,7 @@ static void arm_cpu_set_pc(CPUState *cs, vaddr value)
if (is_a64(env)) {
env->pc = value;
- env->thumb = 0;
+ env->thumb = false;
} else {
env->regs[15] = value & ~1;
env->thumb = value & 1;
@@ -189,7 +189,7 @@ static void arm_cpu_reset(DeviceState *dev)
if (arm_feature(env, ARM_FEATURE_AARCH64)) {
/* 64 bit CPUs always start in 64 bit mode */
- env->aarch64 = 1;
+ env->aarch64 = true;
#if defined(CONFIG_USER_ONLY)
env->pstate = PSTATE_MODE_EL0t;
/* Userspace expects access to DC ZVA, CTL_EL0 and the cache ops */
@@ -694,6 +694,16 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level)
[ARM_CPU_VFIQ] = CPU_INTERRUPT_VFIQ
};
+ if (!arm_feature(env, ARM_FEATURE_EL2) &&
+ (irq == ARM_CPU_VIRQ || irq == ARM_CPU_VFIQ)) {
+ /*
+ * The GIC might tell us about VIRQ and VFIQ state, but if we don't
+ * have EL2 support we don't care. (Unless the guest is doing something
+ * silly this will only be calls saying "level is still 0".)
+ */
+ return;
+ }
+
if (level) {
env->irq_line_state |= mask[irq];
} else {
@@ -702,11 +712,9 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level)
switch (irq) {
case ARM_CPU_VIRQ:
- assert(arm_feature(env, ARM_FEATURE_EL2));
arm_cpu_update_virq(cpu);
break;
case ARM_CPU_VFIQ:
- assert(arm_feature(env, ARM_FEATURE_EL2));
arm_cpu_update_vfiq(cpu);
break;
case ARM_CPU_IRQ:
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index cb5359a747..db8ff04449 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -259,7 +259,8 @@ typedef struct CPUArchState {
* all other bits are stored in their correct places in env->pstate
*/
uint32_t pstate;
- uint32_t aarch64; /* 1 if CPU is in aarch64 state; inverse of PSTATE.nRW */
+ bool aarch64; /* True if CPU is in aarch64 state; inverse of PSTATE.nRW */
+ bool thumb; /* True if CPU is in thumb mode; cpsr[5] */
/* Cached TBFLAGS state. See below for which bits are included. */
CPUARMTBFlags hflags;
@@ -286,7 +287,6 @@ typedef struct CPUArchState {
uint32_t ZF; /* Z set if zero. */
uint32_t QF; /* 0 or 1 */
uint32_t GE; /* cpsr[19:16] */
- uint32_t thumb; /* cpsr[5]. 0 = arm mode, 1 = thumb mode. */
uint32_t condexec_bits; /* IT bits. cpsr[15:10,26:25]. */
uint32_t btype; /* BTI branch type. spsr[11:10]. */
uint64_t daif; /* exception masks, in the bits they are in PSTATE */
@@ -1233,6 +1233,20 @@ void pmu_init(ARMCPU *cpu);
#define SCTLR_ATA0 (1ULL << 42) /* v8.5-MemTag */
#define SCTLR_ATA (1ULL << 43) /* v8.5-MemTag */
#define SCTLR_DSSBS_64 (1ULL << 44) /* v8.5, AArch64 only */
+#define SCTLR_TWEDEn (1ULL << 45) /* FEAT_TWED */
+#define SCTLR_TWEDEL MAKE_64_MASK(46, 4) /* FEAT_TWED */
+#define SCTLR_TMT0 (1ULL << 50) /* FEAT_TME */
+#define SCTLR_TMT (1ULL << 51) /* FEAT_TME */
+#define SCTLR_TME0 (1ULL << 52) /* FEAT_TME */
+#define SCTLR_TME (1ULL << 53) /* FEAT_TME */
+#define SCTLR_EnASR (1ULL << 54) /* FEAT_LS64_V */
+#define SCTLR_EnAS0 (1ULL << 55) /* FEAT_LS64_ACCDATA */
+#define SCTLR_EnALS (1ULL << 56) /* FEAT_LS64 */
+#define SCTLR_EPAN (1ULL << 57) /* FEAT_PAN3 */
+#define SCTLR_EnTP2 (1ULL << 60) /* FEAT_SME */
+#define SCTLR_NMI (1ULL << 61) /* FEAT_NMI */
+#define SCTLR_SPINTMASK (1ULL << 62) /* FEAT_NMI */
+#define SCTLR_TIDCP (1ULL << 63) /* FEAT_TIDCP1 */
#define CPTR_TCPAC (1U << 31)
#define CPTR_TTA (1U << 20)
@@ -1545,6 +1559,18 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
#define SCR_FIEN (1U << 21)
#define SCR_ENSCXT (1U << 25)
#define SCR_ATA (1U << 26)
+#define SCR_FGTEN (1U << 27)
+#define SCR_ECVEN (1U << 28)
+#define SCR_TWEDEN (1U << 29)
+#define SCR_TWEDEL MAKE_64BIT_MASK(30, 4)
+#define SCR_TME (1ULL << 34)
+#define SCR_AMVOFFEN (1ULL << 35)
+#define SCR_ENAS0 (1ULL << 36)
+#define SCR_ADEN (1ULL << 37)
+#define SCR_HXEN (1ULL << 38)
+#define SCR_TRNDR (1ULL << 40)
+#define SCR_ENTP2 (1ULL << 41)
+#define SCR_GPF (1ULL << 48)
#define HSTR_TTEE (1 << 16)
#define HSTR_TJDBX (1 << 17)
@@ -1934,6 +1960,7 @@ FIELD(ID_MMFR4, CCIDX, 24, 4)
FIELD(ID_MMFR4, EVT, 28, 4)
FIELD(ID_MMFR5, ETS, 0, 4)
+FIELD(ID_MMFR5, NTLBPA, 4, 4)
FIELD(ID_PFR0, STATE0, 0, 4)
FIELD(ID_PFR0, STATE1, 4, 4)
@@ -1986,6 +2013,16 @@ FIELD(ID_AA64ISAR1, SPECRES, 40, 4)
FIELD(ID_AA64ISAR1, BF16, 44, 4)
FIELD(ID_AA64ISAR1, DGH, 48, 4)
FIELD(ID_AA64ISAR1, I8MM, 52, 4)
+FIELD(ID_AA64ISAR1, XS, 56, 4)
+FIELD(ID_AA64ISAR1, LS64, 60, 4)
+
+FIELD(ID_AA64ISAR2, WFXT, 0, 4)
+FIELD(ID_AA64ISAR2, RPRES, 4, 4)
+FIELD(ID_AA64ISAR2, GPA3, 8, 4)
+FIELD(ID_AA64ISAR2, APA3, 12, 4)
+FIELD(ID_AA64ISAR2, MOPS, 16, 4)
+FIELD(ID_AA64ISAR2, BC, 20, 4)
+FIELD(ID_AA64ISAR2, PAC_FRAC, 24, 4)
FIELD(ID_AA64PFR0, EL0, 0, 4)
FIELD(ID_AA64PFR0, EL1, 4, 4)
@@ -2008,6 +2045,10 @@ FIELD(ID_AA64PFR1, SSBS, 4, 4)
FIELD(ID_AA64PFR1, MTE, 8, 4)
FIELD(ID_AA64PFR1, RAS_FRAC, 12, 4)
FIELD(ID_AA64PFR1, MPAM_FRAC, 16, 4)
+FIELD(ID_AA64PFR1, SME, 24, 4)
+FIELD(ID_AA64PFR1, RNDR_TRAP, 28, 4)
+FIELD(ID_AA64PFR1, CSV2_FRAC, 32, 4)
+FIELD(ID_AA64PFR1, NMI, 36, 4)
FIELD(ID_AA64MMFR0, PARANGE, 0, 4)
FIELD(ID_AA64MMFR0, ASIDBITS, 4, 4)
@@ -2034,6 +2075,11 @@ FIELD(ID_AA64MMFR1, SPECSEI, 24, 4)
FIELD(ID_AA64MMFR1, XNX, 28, 4)
FIELD(ID_AA64MMFR1, TWED, 32, 4)
FIELD(ID_AA64MMFR1, ETS, 36, 4)
+FIELD(ID_AA64MMFR1, HCX, 40, 4)
+FIELD(ID_AA64MMFR1, AFP, 44, 4)
+FIELD(ID_AA64MMFR1, NTLBPA, 48, 4)
+FIELD(ID_AA64MMFR1, TIDCP1, 52, 4)
+FIELD(ID_AA64MMFR1, CMOW, 56, 4)
FIELD(ID_AA64MMFR2, CNP, 0, 4)
FIELD(ID_AA64MMFR2, UAO, 4, 4)
@@ -2060,7 +2106,10 @@ FIELD(ID_AA64DFR0, CTX_CMPS, 28, 4)
FIELD(ID_AA64DFR0, PMSVER, 32, 4)
FIELD(ID_AA64DFR0, DOUBLELOCK, 36, 4)
FIELD(ID_AA64DFR0, TRACEFILT, 40, 4)
+FIELD(ID_AA64DFR0, TRACEBUFFER, 44, 4)
FIELD(ID_AA64DFR0, MTPMU, 48, 4)
+FIELD(ID_AA64DFR0, BRBE, 52, 4)
+FIELD(ID_AA64DFR0, HPMN0, 60, 4)
FIELD(ID_AA64ZFR0, SVEVER, 0, 4)
FIELD(ID_AA64ZFR0, AES, 4, 4)
@@ -2082,6 +2131,7 @@ FIELD(ID_DFR0, PERFMON, 24, 4)
FIELD(ID_DFR0, TRACEFILT, 28, 4)
FIELD(ID_DFR1, MTPMU, 0, 4)
+FIELD(ID_DFR1, HPMN0, 4, 4)
FIELD(DBGDIDR, SE_IMP, 12, 1)
FIELD(DBGDIDR, NSUHD_IMP, 14, 1)
@@ -2757,11 +2807,6 @@ typedef enum CPAccessResult {
/* As CP_ACCESS_UNCATEGORIZED, but for traps directly to EL2 or EL3 */
CP_ACCESS_TRAP_UNCATEGORIZED_EL2 = 5,
CP_ACCESS_TRAP_UNCATEGORIZED_EL3 = 6,
- /* Access fails and results in an exception syndrome for an FP access,
- * trapped directly to EL2 or EL3
- */
- CP_ACCESS_TRAP_FP_EL2 = 7,
- CP_ACCESS_TRAP_FP_EL3 = 8,
} CPAccessResult;
/* Access functions for coprocessor registers. These cannot fail and
diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c
index 7cf953b1e6..77a8502b6b 100644
--- a/target/arm/helper-a64.c
+++ b/target/arm/helper-a64.c
@@ -952,7 +952,7 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc)
qemu_mutex_unlock_iothread();
if (!return_to_aa64) {
- env->aarch64 = 0;
+ env->aarch64 = false;
/* We do a raw CPSR write because aarch64_sync_64_to_32()
* will sort the register banks out for us, and we've already
* caught all the bad-mode cases in el_from_spsr().
@@ -975,7 +975,7 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc)
} else {
int tbii;
- env->aarch64 = 1;
+ env->aarch64 = true;
spsr &= aarch64_pstate_valid_mask(&env_archcpu(env)->isar);
pstate_write(env, spsr);
if (!arm_singlestep_active(env)) {
diff --git a/target/arm/helper.c b/target/arm/helper.c
index d7715c911a..63397bbac1 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -4784,18 +4784,6 @@ static void sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri,
}
}
-static CPAccessResult fpexc32_access(CPUARMState *env, const ARMCPRegInfo *ri,
- bool isread)
-{
- if ((env->cp15.cptr_el[2] & CPTR_TFP) && arm_current_el(env) == 2) {
- return CP_ACCESS_TRAP_FP_EL2;
- }
- if (env->cp15.cptr_el[3] & CPTR_TFP) {
- return CP_ACCESS_TRAP_FP_EL3;
- }
- return CP_ACCESS_OK;
-}
-
static void sdcr_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
@@ -5097,9 +5085,8 @@ static const ARMCPRegInfo v8_cp_reginfo[] = {
.access = PL1_RW, .readfn = spsel_read, .writefn = spsel_write },
{ .name = "FPEXC32_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 5, .crm = 3, .opc2 = 0,
- .type = ARM_CP_ALIAS,
- .fieldoffset = offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPEXC]),
- .access = PL2_RW, .accessfn = fpexc32_access },
+ .access = PL2_RW, .type = ARM_CP_ALIAS | ARM_CP_FPU,
+ .fieldoffset = offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPEXC]) },
{ .name = "DACR32_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .crn = 3, .crm = 0, .opc2 = 0,
.access = PL2_RW, .resetvalue = 0,
@@ -10181,7 +10168,7 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs)
}
pstate_write(env, PSTATE_DAIF | new_mode);
- env->aarch64 = 1;
+ env->aarch64 = true;
aarch64_restore_sp(env, new_el);
helper_rebuild_hflags_a64(env, new_el);
diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c
index 567e296b21..b11a8b9a18 100644
--- a/target/arm/hvf/hvf.c
+++ b/target/arm/hvf/hvf.c
@@ -564,7 +564,7 @@ int hvf_arch_init_vcpu(CPUState *cpu)
hv_return_t ret;
int i;
- env->aarch64 = 1;
+ env->aarch64 = true;
asm volatile("mrs %0, cntfrq_el0" : "=r"(arm_cpu->gt_cntfrq_hz));
/* Allocate enough space for our sysreg sync */
diff --git a/target/arm/m_helper.c b/target/arm/m_helper.c
index b7a0fe0114..a740c3e160 100644
--- a/target/arm/m_helper.c
+++ b/target/arm/m_helper.c
@@ -564,7 +564,7 @@ void HELPER(v7m_bxns)(CPUARMState *env, uint32_t dest)
env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK;
}
switch_v7m_security_state(env, dest & 1);
- env->thumb = 1;
+ env->thumb = true;
env->regs[15] = dest & ~1;
arm_rebuild_hflags(env);
}
@@ -590,7 +590,7 @@ void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest)
* except that the low bit doesn't indicate Thumb/not.
*/
env->regs[14] = nextinst;
- env->thumb = 1;
+ env->thumb = true;
env->regs[15] = dest & ~1;
return;
}
@@ -626,7 +626,7 @@ void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest)
}
env->v7m.control[M_REG_S] &= ~R_V7M_CONTROL_SFPA_MASK;
switch_v7m_security_state(env, 0);
- env->thumb = 1;
+ env->thumb = true;
env->regs[15] = dest;
arm_rebuild_hflags(env);
}
diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c
index 70b42b55fd..2b87e8808b 100644
--- a/target/arm/op_helper.c
+++ b/target/arm/op_helper.c
@@ -691,19 +691,6 @@ void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome,
target_el = 3;
syndrome = syn_uncategorized();
break;
- case CP_ACCESS_TRAP_FP_EL2:
- target_el = 2;
- /* Since we are an implementation that takes exceptions on a trapped
- * conditional insn only if the insn has passed its condition code
- * check, we take the IMPDEF choice to always report CV=1 COND=0xe
- * (which is also the required value for AArch64 traps).
- */
- syndrome = syn_fp_access_trap(1, 0xe, false);
- break;
- case CP_ACCESS_TRAP_FP_EL3:
- target_el = 3;
- syndrome = syn_fp_access_trap(1, 0xe, false);
- break;
default:
g_assert_not_reached();
}
diff --git a/target/arm/translate-a32.h b/target/arm/translate-a32.h
index 5be4b9b834..09010ad2da 100644
--- a/target/arm/translate-a32.h
+++ b/target/arm/translate-a32.h
@@ -61,17 +61,14 @@ static inline TCGv_i32 load_cpu_offset(int offset)
#define load_cpu_field(name) load_cpu_offset(offsetof(CPUARMState, name))
-static inline void store_cpu_offset(TCGv_i32 var, int offset)
-{
- tcg_gen_st_i32(var, cpu_env, offset);
- tcg_temp_free_i32(var);
-}
+void store_cpu_offset(TCGv_i32 var, int offset, int size);
-#define store_cpu_field(var, name) \
- store_cpu_offset(var, offsetof(CPUARMState, name))
+#define store_cpu_field(var, name) \
+ store_cpu_offset(var, offsetof(CPUARMState, name), \
+ sizeof_field(CPUARMState, name))
#define store_cpu_field_constant(val, name) \
- tcg_gen_st_i32(tcg_constant_i32(val), cpu_env, offsetof(CPUARMState, name))
+ store_cpu_field(tcg_constant_i32(val), name)
/* Create a new temporary and set it to the value of a CPU register. */
static inline TCGv_i32 load_reg(DisasContext *s, int reg)
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index 19c09c3b53..adbcd99941 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -128,29 +128,28 @@ static int get_a64_user_mem_index(DisasContext *s)
return arm_to_core_mmu_idx(useridx);
}
-static void reset_btype(DisasContext *s)
+static void set_btype_raw(int val)
{
- if (s->btype != 0) {
- TCGv_i32 zero = tcg_const_i32(0);
- tcg_gen_st_i32(zero, cpu_env, offsetof(CPUARMState, btype));
- tcg_temp_free_i32(zero);
- s->btype = 0;
- }
+ tcg_gen_st_i32(tcg_constant_i32(val), cpu_env,
+ offsetof(CPUARMState, btype));
}
static void set_btype(DisasContext *s, int val)
{
- TCGv_i32 tcg_val;
-
/* BTYPE is a 2-bit field, and 0 should be done with reset_btype. */
tcg_debug_assert(val >= 1 && val <= 3);
-
- tcg_val = tcg_const_i32(val);
- tcg_gen_st_i32(tcg_val, cpu_env, offsetof(CPUARMState, btype));
- tcg_temp_free_i32(tcg_val);
+ set_btype_raw(val);
s->btype = -1;
}
+static void reset_btype(DisasContext *s)
+{
+ if (s->btype != 0) {
+ set_btype_raw(0);
+ s->btype = 0;
+ }
+}
+
void gen_a64_set_pc_im(uint64_t val)
{
tcg_gen_movi_i64(cpu_pc, val);
@@ -342,6 +341,11 @@ static void a64_free_cc(DisasCompare64 *c64)
tcg_temp_free_i64(c64->value);
}
+static void gen_rebuild_hflags(DisasContext *s)
+{
+ gen_helper_rebuild_hflags_a64(cpu_env, tcg_constant_i32(s->current_el));
+}
+
static void gen_exception_internal(int excp)
{
TCGv_i32 tcg_excp = tcg_const_i32(excp);
@@ -1668,9 +1672,7 @@ static void handle_msr_i(DisasContext *s, uint32_t insn,
} else {
clear_pstate_bits(PSTATE_UAO);
}
- t1 = tcg_const_i32(s->current_el);
- gen_helper_rebuild_hflags_a64(cpu_env, t1);
- tcg_temp_free_i32(t1);
+ gen_rebuild_hflags(s);
break;
case 0x04: /* PAN */
@@ -1682,9 +1684,7 @@ static void handle_msr_i(DisasContext *s, uint32_t insn,
} else {
clear_pstate_bits(PSTATE_PAN);
}
- t1 = tcg_const_i32(s->current_el);
- gen_helper_rebuild_hflags_a64(cpu_env, t1);
- tcg_temp_free_i32(t1);
+ gen_rebuild_hflags(s);
break;
case 0x05: /* SPSel */
@@ -1742,9 +1742,7 @@ static void handle_msr_i(DisasContext *s, uint32_t insn,
} else {
clear_pstate_bits(PSTATE_TCO);
}
- t1 = tcg_const_i32(s->current_el);
- gen_helper_rebuild_hflags_a64(cpu_env, t1);
- tcg_temp_free_i32(t1);
+ gen_rebuild_hflags(s);
/* Many factors, including TCO, go into MTE_ACTIVE. */
s->base.is_jmp = DISAS_UPDATE_NOCHAIN;
} else if (dc_isar_feature(aa64_mte_insn_reg, s)) {
@@ -1991,9 +1989,7 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
* A write to any coprocessor regiser that ends a TB
* must rebuild the hflags for the next TB.
*/
- TCGv_i32 tcg_el = tcg_const_i32(s->current_el);
- gen_helper_rebuild_hflags_a64(cpu_env, tcg_el);
- tcg_temp_free_i32(tcg_el);
+ gen_rebuild_hflags(s);
/*
* We default to ending the TB on a coprocessor register write,
* but allow this to be suppressed by the register definition
@@ -14664,13 +14660,13 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase,
dc->isar = &arm_cpu->isar;
dc->condjmp = 0;
- dc->aarch64 = 1;
+ dc->aarch64 = true;
/* If we are coming from secure EL0 in a system with a 32-bit EL3, then
* there is no secure EL1, so we route exceptions to EL3.
*/
dc->secure_routed_to_el3 = arm_feature(env, ARM_FEATURE_EL3) &&
!arm_el_is_aa64(env, 3);
- dc->thumb = 0;
+ dc->thumb = false;
dc->sctlr_b = 0;
dc->be_data = EX_TBFLAG_ANY(tb_flags, BE_DATA) ? MO_BE : MO_LE;
dc->condexec_mask = 0;
diff --git a/target/arm/translate-m-nocp.c b/target/arm/translate-m-nocp.c
index d9e144e8eb..27363a7b4e 100644
--- a/target/arm/translate-m-nocp.c
+++ b/target/arm/translate-m-nocp.c
@@ -173,7 +173,7 @@ static bool trans_VSCCLRM(DisasContext *s, arg_VSCCLRM *a)
}
/* Zero the Sregs from btmreg to topreg inclusive. */
- zero = tcg_const_i64(0);
+ zero = tcg_constant_i64(0);
if (btmreg & 1) {
write_neon_element64(zero, btmreg >> 1, 1, MO_32);
btmreg++;
@@ -187,8 +187,7 @@ static bool trans_VSCCLRM(DisasContext *s, arg_VSCCLRM *a)
}
assert(btmreg == topreg + 1);
if (dc_isar_feature(aa32_mve, s)) {
- TCGv_i32 z32 = tcg_const_i32(0);
- store_cpu_field(z32, v7m.vpr);
+ store_cpu_field(tcg_constant_i32(0), v7m.vpr);
}
clear_eci_state(s);
@@ -512,7 +511,7 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno,
}
case ARM_VFP_FPCXT_NS:
{
- TCGv_i32 control, sfpa, fpscr, fpdscr, zero;
+ TCGv_i32 control, sfpa, fpscr, fpdscr;
TCGLabel *lab_active = gen_new_label();
lookup_tb = true;
@@ -552,10 +551,9 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno,
storefn(s, opaque, tmp, true);
/* If SFPA is zero then set FPSCR from FPDSCR_NS */
fpdscr = load_cpu_field(v7m.fpdscr[M_REG_NS]);
- zero = tcg_const_i32(0);
- tcg_gen_movcond_i32(TCG_COND_EQ, fpscr, sfpa, zero, fpdscr, fpscr);
+ tcg_gen_movcond_i32(TCG_COND_EQ, fpscr, sfpa, tcg_constant_i32(0),
+ fpdscr, fpscr);
gen_helper_vfp_set_fpscr(cpu_env, fpscr);
- tcg_temp_free_i32(zero);
tcg_temp_free_i32(sfpa);
tcg_temp_free_i32(fpdscr);
tcg_temp_free_i32(fpscr);
diff --git a/target/arm/translate-neon.c b/target/arm/translate-neon.c
index 384604c009..2e4d1ec87d 100644
--- a/target/arm/translate-neon.c
+++ b/target/arm/translate-neon.c
@@ -447,7 +447,7 @@ static bool trans_VLDST_multiple(DisasContext *s, arg_VLDST_multiple *a)
int mmu_idx = get_mem_index(s);
int size = a->size;
TCGv_i64 tmp64;
- TCGv_i32 addr, tmp;
+ TCGv_i32 addr;
if (!arm_dc_feature(s, ARM_FEATURE_NEON)) {
return false;
@@ -513,7 +513,6 @@ static bool trans_VLDST_multiple(DisasContext *s, arg_VLDST_multiple *a)
tmp64 = tcg_temp_new_i64();
addr = tcg_temp_new_i32();
- tmp = tcg_const_i32(1 << size);
load_reg_var(s, addr, a->rn);
mop = endian | size | align;
@@ -530,7 +529,7 @@ static bool trans_VLDST_multiple(DisasContext *s, arg_VLDST_multiple *a)
neon_load_element64(tmp64, tt, n, size);
gen_aa32_st_internal_i64(s, tmp64, addr, mmu_idx, mop);
}
- tcg_gen_add_i32(addr, addr, tmp);
+ tcg_gen_addi_i32(addr, addr, 1 << size);
/* Subsequent memory operations inherit alignment */
mop &= ~MO_AMASK;
@@ -538,7 +537,6 @@ static bool trans_VLDST_multiple(DisasContext *s, arg_VLDST_multiple *a)
}
}
tcg_temp_free_i32(addr);
- tcg_temp_free_i32(tmp);
tcg_temp_free_i64(tmp64);
gen_neon_ldst_base_update(s, a->rm, a->rn, nregs * interleave * 8);
@@ -1348,7 +1346,7 @@ static bool do_2shift_env_64(DisasContext *s, arg_2reg_shift *a,
* To avoid excessive duplication of ops we implement shift
* by immediate using the variable shift operations.
*/
- constimm = tcg_const_i64(dup_const(a->size, a->shift));
+ constimm = tcg_constant_i64(dup_const(a->size, a->shift));
for (pass = 0; pass < a->q + 1; pass++) {
TCGv_i64 tmp = tcg_temp_new_i64();
@@ -1358,7 +1356,6 @@ static bool do_2shift_env_64(DisasContext *s, arg_2reg_shift *a,
write_neon_element64(tmp, a->vd, pass, MO_64);
tcg_temp_free_i64(tmp);
}
- tcg_temp_free_i64(constimm);
return true;
}
@@ -1394,7 +1391,7 @@ static bool do_2shift_env_32(DisasContext *s, arg_2reg_shift *a,
* To avoid excessive duplication of ops we implement shift
* by immediate using the variable shift operations.
*/
- constimm = tcg_const_i32(dup_const(a->size, a->shift));
+ constimm = tcg_constant_i32(dup_const(a->size, a->shift));
tmp = tcg_temp_new_i32();
for (pass = 0; pass < (a->q ? 4 : 2); pass++) {
@@ -1403,7 +1400,6 @@ static bool do_2shift_env_32(DisasContext *s, arg_2reg_shift *a,
write_neon_element32(tmp, a->vd, pass, MO_32);
}
tcg_temp_free_i32(tmp);
- tcg_temp_free_i32(constimm);
return true;
}
@@ -1457,7 +1453,7 @@ static bool do_2shift_narrow_64(DisasContext *s, arg_2reg_shift *a,
* This is always a right shift, and the shiftfn is always a
* left-shift helper, which thus needs the negated shift count.
*/
- constimm = tcg_const_i64(-a->shift);
+ constimm = tcg_constant_i64(-a->shift);
rm1 = tcg_temp_new_i64();
rm2 = tcg_temp_new_i64();
rd = tcg_temp_new_i32();
@@ -1477,7 +1473,6 @@ static bool do_2shift_narrow_64(DisasContext *s, arg_2reg_shift *a,
tcg_temp_free_i32(rd);
tcg_temp_free_i64(rm1);
tcg_temp_free_i64(rm2);
- tcg_temp_free_i64(constimm);
return true;
}
@@ -1521,7 +1516,7 @@ static bool do_2shift_narrow_32(DisasContext *s, arg_2reg_shift *a,
/* size == 2 */
imm = -a->shift;
}
- constimm = tcg_const_i32(imm);
+ constimm = tcg_constant_i32(imm);
/* Load all inputs first to avoid potential overwrite */
rm1 = tcg_temp_new_i32();
@@ -1546,7 +1541,6 @@ static bool do_2shift_narrow_32(DisasContext *s, arg_2reg_shift *a,
shiftfn(rm3, rm3, constimm);
shiftfn(rm4, rm4, constimm);
- tcg_temp_free_i32(constimm);
tcg_gen_concat_i32_i64(rtmp, rm3, rm4);
tcg_temp_free_i32(rm4);
@@ -2911,7 +2905,7 @@ static bool trans_VTBL(DisasContext *s, arg_VTBL *a)
return true;
}
- desc = tcg_const_i32((a->vn << 2) | a->len);
+ desc = tcg_constant_i32((a->vn << 2) | a->len);
def = tcg_temp_new_i64();
if (a->op) {
read_neon_element64(def, a->vd, 0, MO_64);
@@ -2926,7 +2920,6 @@ static bool trans_VTBL(DisasContext *s, arg_VTBL *a)
tcg_temp_free_i64(def);
tcg_temp_free_i64(val);
- tcg_temp_free_i32(desc);
return true;
}
diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c
index 180e14d9f8..726cf88d7c 100644
--- a/target/arm/translate-sve.c
+++ b/target/arm/translate-sve.c
@@ -1916,8 +1916,6 @@ static bool trans_PNEXT(DisasContext *s, arg_rr_esz *a)
static void do_sat_addsub_32(TCGv_i64 reg, TCGv_i64 val, bool u, bool d)
{
int64_t ibound;
- TCGv_i64 bound;
- TCGCond cond;
/* Use normal 64-bit arithmetic to detect 32-bit overflow. */
if (u) {
@@ -1928,15 +1926,12 @@ static void do_sat_addsub_32(TCGv_i64 reg, TCGv_i64 val, bool u, bool d)
if (d) {
tcg_gen_sub_i64(reg, reg, val);
ibound = (u ? 0 : INT32_MIN);
- cond = TCG_COND_LT;
+ tcg_gen_smax_i64(reg, reg, tcg_constant_i64(ibound));
} else {
tcg_gen_add_i64(reg, reg, val);
ibound = (u ? UINT32_MAX : INT32_MAX);
- cond = TCG_COND_GT;
+ tcg_gen_smin_i64(reg, reg, tcg_constant_i64(ibound));
}
- bound = tcg_const_i64(ibound);
- tcg_gen_movcond_i64(cond, reg, reg, bound, bound, reg);
- tcg_temp_free_i64(bound);
}
/* Similarly with 64-bit values. */
diff --git a/target/arm/translate-vfp.c b/target/arm/translate-vfp.c
index 6a95a67a69..40a513b822 100644
--- a/target/arm/translate-vfp.c
+++ b/target/arm/translate-vfp.c
@@ -180,8 +180,7 @@ static void gen_update_fp_context(DisasContext *s)
gen_helper_vfp_set_fpscr(cpu_env, fpscr);
tcg_temp_free_i32(fpscr);
if (dc_isar_feature(aa32_mve, s)) {
- TCGv_i32 z32 = tcg_const_i32(0);
- store_cpu_field(z32, v7m.vpr);
+ store_cpu_field(tcg_constant_i32(0), v7m.vpr);
}
/*
* We just updated the FPSCR and VPR. Some of this state is cached
@@ -317,7 +316,7 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a)
TCGv_i64 frn, frm, dest;
TCGv_i64 tmp, zero, zf, nf, vf;
- zero = tcg_const_i64(0);
+ zero = tcg_constant_i64(0);
frn = tcg_temp_new_i64();
frm = tcg_temp_new_i64();
@@ -335,27 +334,22 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a)
vfp_load_reg64(frm, rm);
switch (a->cc) {
case 0: /* eq: Z */
- tcg_gen_movcond_i64(TCG_COND_EQ, dest, zf, zero,
- frn, frm);
+ tcg_gen_movcond_i64(TCG_COND_EQ, dest, zf, zero, frn, frm);
break;
case 1: /* vs: V */
- tcg_gen_movcond_i64(TCG_COND_LT, dest, vf, zero,
- frn, frm);
+ tcg_gen_movcond_i64(TCG_COND_LT, dest, vf, zero, frn, frm);
break;
case 2: /* ge: N == V -> N ^ V == 0 */
tmp = tcg_temp_new_i64();
tcg_gen_xor_i64(tmp, vf, nf);
- tcg_gen_movcond_i64(TCG_COND_GE, dest, tmp, zero,
- frn, frm);
+ tcg_gen_movcond_i64(TCG_COND_GE, dest, tmp, zero, frn, frm);
tcg_temp_free_i64(tmp);
break;
case 3: /* gt: !Z && N == V */
- tcg_gen_movcond_i64(TCG_COND_NE, dest, zf, zero,
- frn, frm);
+ tcg_gen_movcond_i64(TCG_COND_NE, dest, zf, zero, frn, frm);
tmp = tcg_temp_new_i64();
tcg_gen_xor_i64(tmp, vf, nf);
- tcg_gen_movcond_i64(TCG_COND_GE, dest, tmp, zero,
- dest, frm);
+ tcg_gen_movcond_i64(TCG_COND_GE, dest, tmp, zero, dest, frm);
tcg_temp_free_i64(tmp);
break;
}
@@ -367,13 +361,11 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a)
tcg_temp_free_i64(zf);
tcg_temp_free_i64(nf);
tcg_temp_free_i64(vf);
-
- tcg_temp_free_i64(zero);
} else {
TCGv_i32 frn, frm, dest;
TCGv_i32 tmp, zero;
- zero = tcg_const_i32(0);
+ zero = tcg_constant_i32(0);
frn = tcg_temp_new_i32();
frm = tcg_temp_new_i32();
@@ -382,27 +374,22 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a)
vfp_load_reg32(frm, rm);
switch (a->cc) {
case 0: /* eq: Z */
- tcg_gen_movcond_i32(TCG_COND_EQ, dest, cpu_ZF, zero,
- frn, frm);
+ tcg_gen_movcond_i32(TCG_COND_EQ, dest, cpu_ZF, zero, frn, frm);
break;
case 1: /* vs: V */
- tcg_gen_movcond_i32(TCG_COND_LT, dest, cpu_VF, zero,
- frn, frm);
+ tcg_gen_movcond_i32(TCG_COND_LT, dest, cpu_VF, zero, frn, frm);
break;
case 2: /* ge: N == V -> N ^ V == 0 */
tmp = tcg_temp_new_i32();
tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF);
- tcg_gen_movcond_i32(TCG_COND_GE, dest, tmp, zero,
- frn, frm);
+ tcg_gen_movcond_i32(TCG_COND_GE, dest, tmp, zero, frn, frm);
tcg_temp_free_i32(tmp);
break;
case 3: /* gt: !Z && N == V */
- tcg_gen_movcond_i32(TCG_COND_NE, dest, cpu_ZF, zero,
- frn, frm);
+ tcg_gen_movcond_i32(TCG_COND_NE, dest, cpu_ZF, zero, frn, frm);
tmp = tcg_temp_new_i32();
tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF);
- tcg_gen_movcond_i32(TCG_COND_GE, dest, tmp, zero,
- dest, frm);
+ tcg_gen_movcond_i32(TCG_COND_GE, dest, tmp, zero, dest, frm);
tcg_temp_free_i32(tmp);
break;
}
@@ -414,8 +401,6 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a)
tcg_temp_free_i32(frn);
tcg_temp_free_i32(frm);
tcg_temp_free_i32(dest);
-
- tcg_temp_free_i32(zero);
}
return true;
@@ -547,7 +532,7 @@ static bool trans_VCVT(DisasContext *s, arg_VCVT *a)
fpst = fpstatus_ptr(FPST_FPCR);
}
- tcg_shift = tcg_const_i32(0);
+ tcg_shift = tcg_constant_i32(0);
tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rounding));
gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst);
@@ -595,8 +580,6 @@ static bool trans_VCVT(DisasContext *s, arg_VCVT *a)
gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst);
tcg_temp_free_i32(tcg_rmode);
- tcg_temp_free_i32(tcg_shift);
-
tcg_temp_free_ptr(fpst);
return true;
@@ -850,15 +833,11 @@ static bool trans_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a)
case ARM_VFP_MVFR2:
case ARM_VFP_FPSID:
if (s->current_el == 1) {
- TCGv_i32 tcg_reg, tcg_rt;
-
gen_set_condexec(s);
gen_set_pc_im(s, s->pc_curr);
- tcg_reg = tcg_const_i32(a->reg);
- tcg_rt = tcg_const_i32(a->rt);
- gen_helper_check_hcr_el2_trap(cpu_env, tcg_rt, tcg_reg);
- tcg_temp_free_i32(tcg_reg);
- tcg_temp_free_i32(tcg_rt);
+ gen_helper_check_hcr_el2_trap(cpu_env,
+ tcg_constant_i32(a->rt),
+ tcg_constant_i32(a->reg));
}
/* fall through */
case ARM_VFP_FPEXC:
@@ -2388,8 +2367,6 @@ MAKE_VFM_TRANS_FNS(dp)
static bool trans_VMOV_imm_hp(DisasContext *s, arg_VMOV_imm_sp *a)
{
- TCGv_i32 fd;
-
if (!dc_isar_feature(aa32_fp16_arith, s)) {
return false;
}
@@ -2402,9 +2379,7 @@ static bool trans_VMOV_imm_hp(DisasContext *s, arg_VMOV_imm_sp *a)
return true;
}
- fd = tcg_const_i32(vfp_expand_imm(MO_16, a->imm));
- vfp_store_reg32(fd, a->vd);
- tcg_temp_free_i32(fd);
+ vfp_store_reg32(tcg_constant_i32(vfp_expand_imm(MO_16, a->imm)), a->vd);
return true;
}
@@ -2440,7 +2415,7 @@ static bool trans_VMOV_imm_sp(DisasContext *s, arg_VMOV_imm_sp *a)
}
}
- fd = tcg_const_i32(vfp_expand_imm(MO_32, a->imm));
+ fd = tcg_constant_i32(vfp_expand_imm(MO_32, a->imm));
for (;;) {
vfp_store_reg32(fd, vd);
@@ -2454,7 +2429,6 @@ static bool trans_VMOV_imm_sp(DisasContext *s, arg_VMOV_imm_sp *a)
vd = vfp_advance_sreg(vd, delta_d);
}
- tcg_temp_free_i32(fd);
return true;
}
@@ -2495,7 +2469,7 @@ static bool trans_VMOV_imm_dp(DisasContext *s, arg_VMOV_imm_dp *a)
}
}
- fd = tcg_const_i64(vfp_expand_imm(MO_64, a->imm));
+ fd = tcg_constant_i64(vfp_expand_imm(MO_64, a->imm));
for (;;) {
vfp_store_reg64(fd, vd);
@@ -2509,7 +2483,6 @@ static bool trans_VMOV_imm_dp(DisasContext *s, arg_VMOV_imm_dp *a)
vd = vfp_advance_dreg(vd, delta_d);
}
- tcg_temp_free_i64(fd);
return true;
}
@@ -3294,7 +3267,7 @@ static bool trans_VCVT_fix_hp(DisasContext *s, arg_VCVT_fix_sp *a)
vfp_load_reg32(vd, a->vd);
fpst = fpstatus_ptr(FPST_FPCR_F16);
- shift = tcg_const_i32(frac_bits);
+ shift = tcg_constant_i32(frac_bits);
/* Switch on op:U:sx bits */
switch (a->opc) {
@@ -3328,7 +3301,6 @@ static bool trans_VCVT_fix_hp(DisasContext *s, arg_VCVT_fix_sp *a)
vfp_store_reg32(vd, a->vd);
tcg_temp_free_i32(vd);
- tcg_temp_free_i32(shift);
tcg_temp_free_ptr(fpst);
return true;
}
@@ -3353,7 +3325,7 @@ static bool trans_VCVT_fix_sp(DisasContext *s, arg_VCVT_fix_sp *a)
vfp_load_reg32(vd, a->vd);
fpst = fpstatus_ptr(FPST_FPCR);
- shift = tcg_const_i32(frac_bits);
+ shift = tcg_constant_i32(frac_bits);
/* Switch on op:U:sx bits */
switch (a->opc) {
@@ -3387,7 +3359,6 @@ static bool trans_VCVT_fix_sp(DisasContext *s, arg_VCVT_fix_sp *a)
vfp_store_reg32(vd, a->vd);
tcg_temp_free_i32(vd);
- tcg_temp_free_i32(shift);
tcg_temp_free_ptr(fpst);
return true;
}
@@ -3418,7 +3389,7 @@ static bool trans_VCVT_fix_dp(DisasContext *s, arg_VCVT_fix_dp *a)
vfp_load_reg64(vd, a->vd);
fpst = fpstatus_ptr(FPST_FPCR);
- shift = tcg_const_i32(frac_bits);
+ shift = tcg_constant_i32(frac_bits);
/* Switch on op:U:sx bits */
switch (a->opc) {
@@ -3452,7 +3423,6 @@ static bool trans_VCVT_fix_dp(DisasContext *s, arg_VCVT_fix_dp *a)
vfp_store_reg64(vd, a->vd);
tcg_temp_free_i64(vd);
- tcg_temp_free_i32(shift);
tcg_temp_free_ptr(fpst);
return true;
}
diff --git a/target/arm/translate.c b/target/arm/translate.c
index 38e7a38f28..d09692c125 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -180,6 +180,25 @@ typedef enum ISSInfo {
ISSIs16Bit = (1 << 8),
} ISSInfo;
+/*
+ * Store var into env + offset to a member with size bytes.
+ * Free var after use.
+ */
+void store_cpu_offset(TCGv_i32 var, int offset, int size)
+{
+ switch (size) {
+ case 1:
+ tcg_gen_st8_i32(var, cpu_env, offset);
+ break;
+ case 4:
+ tcg_gen_st_i32(var, cpu_env, offset);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ tcg_temp_free_i32(var);
+}
+
/* Save the syndrome information for a Data Abort */
static void disas_set_da_iss(DisasContext *s, MemOp memop, ISSInfo issinfo)
{
@@ -332,6 +351,26 @@ void gen_set_cpsr(TCGv_i32 var, uint32_t mask)
tcg_temp_free_i32(tmp_mask);
}
+static void gen_rebuild_hflags(DisasContext *s, bool new_el)
+{
+ bool m_profile = arm_dc_feature(s, ARM_FEATURE_M);
+
+ if (new_el) {
+ if (m_profile) {
+ gen_helper_rebuild_hflags_m32_newel(cpu_env);
+ } else {
+ gen_helper_rebuild_hflags_a32_newel(cpu_env);
+ }
+ } else {
+ TCGv_i32 tcg_el = tcg_constant_i32(s->current_el);
+ if (m_profile) {
+ gen_helper_rebuild_hflags_m32(cpu_env, tcg_el);
+ } else {
+ gen_helper_rebuild_hflags_a32(cpu_env, tcg_el);
+ }
+ }
+}
+
static void gen_exception_internal(int excp)
{
TCGv_i32 tcg_excp = tcg_const_i32(excp);
@@ -513,16 +552,14 @@ static void gen_sbc_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1)
#define GEN_SHIFT(name) \
static void gen_##name(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) \
{ \
- TCGv_i32 tmp1, tmp2, tmp3; \
- tmp1 = tcg_temp_new_i32(); \
- tcg_gen_andi_i32(tmp1, t1, 0xff); \
- tmp2 = tcg_const_i32(0); \
- tmp3 = tcg_const_i32(0x1f); \
- tcg_gen_movcond_i32(TCG_COND_GTU, tmp2, tmp1, tmp3, tmp2, t0); \
- tcg_temp_free_i32(tmp3); \
- tcg_gen_andi_i32(tmp1, tmp1, 0x1f); \
- tcg_gen_##name##_i32(dest, tmp2, tmp1); \
- tcg_temp_free_i32(tmp2); \
+ TCGv_i32 tmpd = tcg_temp_new_i32(); \
+ TCGv_i32 tmp1 = tcg_temp_new_i32(); \
+ TCGv_i32 zero = tcg_constant_i32(0); \
+ tcg_gen_andi_i32(tmp1, t1, 0x1f); \
+ tcg_gen_##name##_i32(tmpd, t0, tmp1); \
+ tcg_gen_andi_i32(tmp1, t1, 0xe0); \
+ tcg_gen_movcond_i32(TCG_COND_NE, dest, tmp1, zero, zero, tmpd); \
+ tcg_temp_free_i32(tmpd); \
tcg_temp_free_i32(tmp1); \
}
GEN_SHIFT(shl)
@@ -531,12 +568,10 @@ GEN_SHIFT(shr)
static void gen_sar(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1)
{
- TCGv_i32 tmp1, tmp2;
- tmp1 = tcg_temp_new_i32();
+ TCGv_i32 tmp1 = tcg_temp_new_i32();
+
tcg_gen_andi_i32(tmp1, t1, 0xff);
- tmp2 = tcg_const_i32(0x1f);
- tcg_gen_movcond_i32(TCG_COND_GTU, tmp1, tmp1, tmp2, tmp2, tmp1);
- tcg_temp_free_i32(tmp2);
+ tcg_gen_umin_i32(tmp1, tmp1, tcg_constant_i32(31));
tcg_gen_sar_i32(dest, t0, tmp1);
tcg_temp_free_i32(tmp1);
}
@@ -4852,7 +4887,7 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64,
tcg_temp_free_i32(tmp);
} else {
TCGv_i32 tmp = load_reg(s, rt);
- store_cpu_offset(tmp, ri->fieldoffset);
+ store_cpu_offset(tmp, ri->fieldoffset, 4);
}
}
}
@@ -4866,17 +4901,7 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64,
* A write to any coprocessor register that ends a TB
* must rebuild the hflags for the next TB.
*/
- TCGv_i32 tcg_el = tcg_const_i32(s->current_el);
- if (arm_dc_feature(s, ARM_FEATURE_M)) {
- gen_helper_rebuild_hflags_m32(cpu_env, tcg_el);
- } else {
- if (ri->type & ARM_CP_NEWEL) {
- gen_helper_rebuild_hflags_a32_newel(cpu_env);
- } else {
- gen_helper_rebuild_hflags_a32(cpu_env, tcg_el);
- }
- }
- tcg_temp_free_i32(tcg_el);
+ gen_rebuild_hflags(s, ri->type & ARM_CP_NEWEL);
/*
* We default to ending the TB on a coprocessor register write,
* but allow this to be suppressed by the register definition
@@ -6426,7 +6451,7 @@ static bool trans_MSR_v7m(DisasContext *s, arg_MSR_v7m *a)
tcg_temp_free_i32(addr);
tcg_temp_free_i32(reg);
/* If we wrote to CONTROL, the EL might have changed */
- gen_helper_rebuild_hflags_m32_newel(cpu_env);
+ gen_rebuild_hflags(s, true);
gen_lookup_tb(s);
return true;
}
@@ -8878,7 +8903,7 @@ static bool trans_CPS(DisasContext *s, arg_CPS *a)
static bool trans_CPS_v7m(DisasContext *s, arg_CPS_v7m *a)
{
- TCGv_i32 tmp, addr, el;
+ TCGv_i32 tmp, addr;
if (!arm_dc_feature(s, ARM_FEATURE_M)) {
return false;
@@ -8901,9 +8926,7 @@ static bool trans_CPS_v7m(DisasContext *s, arg_CPS_v7m *a)
gen_helper_v7m_msr(cpu_env, addr, tmp);
tcg_temp_free_i32(addr);
}
- el = tcg_const_i32(s->current_el);
- gen_helper_rebuild_hflags_m32(cpu_env, el);
- tcg_temp_free_i32(el);
+ gen_rebuild_hflags(s, false);
tcg_temp_free_i32(tmp);
gen_lookup_tb(s);
return true;
@@ -9334,7 +9357,7 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
dc->isar = &cpu->isar;
dc->condjmp = 0;
- dc->aarch64 = 0;
+ dc->aarch64 = false;
/* If we are coming from secure EL0 in a system with a 32-bit EL3, then
* there is no secure EL1, so we route exceptions to EL3.
*/
@@ -9847,18 +9870,14 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
/* nothing more to generate */
break;
case DISAS_WFI:
- {
- TCGv_i32 tmp = tcg_const_i32((dc->thumb &&
- !(dc->insn & (1U << 31))) ? 2 : 4);
-
- gen_helper_wfi(cpu_env, tmp);
- tcg_temp_free_i32(tmp);
- /* The helper doesn't necessarily throw an exception, but we
+ gen_helper_wfi(cpu_env,
+ tcg_constant_i32(dc->base.pc_next - dc->pc_curr));
+ /*
+ * The helper doesn't necessarily throw an exception, but we
* must go back to the main loop to check for interrupts anyway.
*/
tcg_gen_exit_tb(NULL, 0);
break;
- }
case DISAS_WFE:
gen_helper_wfe(cpu_env);
break;
diff --git a/target/arm/translate.h b/target/arm/translate.h
index 3a0db801d3..6f0ebdc88e 100644
--- a/target/arm/translate.h
+++ b/target/arm/translate.h
@@ -30,7 +30,6 @@ typedef struct DisasContext {
bool eci_handled;
/* TCG op to rewind to if this turns out to be an invalid ECI state */
TCGOp *insn_eci_rewind;
- int thumb;
int sctlr_b;
MemOp be_data;
#if !defined(CONFIG_USER_ONLY)
@@ -59,12 +58,13 @@ typedef struct DisasContext {
* so that top level loop can generate correct syndrome information.
*/
uint32_t svc_imm;
- int aarch64;
int current_el;
/* Debug target exception level for single-step exceptions */
int debug_target_el;
GHashTable *cp_regs;
uint64_t features; /* CPU features bits */
+ bool aarch64;
+ bool thumb;
/* Because unallocated encodings generate different exception syndrome
* information from traps due to FP being disabled, we can't do a single
* "is fp access disabled" check at a high level in the decode tree.
@@ -332,16 +332,9 @@ static inline void gen_ss_advance(DisasContext *s)
static inline void gen_exception(int excp, uint32_t syndrome,
uint32_t target_el)
{
- TCGv_i32 tcg_excp = tcg_const_i32(excp);
- TCGv_i32 tcg_syn = tcg_const_i32(syndrome);
- TCGv_i32 tcg_el = tcg_const_i32(target_el);
-
- gen_helper_exception_with_syndrome(cpu_env, tcg_excp,
- tcg_syn, tcg_el);
-
- tcg_temp_free_i32(tcg_el);
- tcg_temp_free_i32(tcg_syn);
- tcg_temp_free_i32(tcg_excp);
+ gen_helper_exception_with_syndrome(cpu_env, tcg_constant_i32(excp),
+ tcg_constant_i32(syndrome),
+ tcg_constant_i32(target_el));
}
/* Generate an architectural singlestep exception */
diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c
index ebf5e73df9..30bc44fcf8 100644
--- a/target/i386/tcg/fpu_helper.c
+++ b/target/i386/tcg/fpu_helper.c
@@ -2466,7 +2466,7 @@ static void do_fsave(CPUX86State *env, target_ulong ptr, int data32,
do_fstenv(env, ptr, data32, retaddr);
- ptr += (14 << data32);
+ ptr += (target_ulong)14 << data32;
for (i = 0; i < 8; i++) {
tmp = ST(i);
do_fstt(env, tmp, ptr, retaddr);
@@ -2488,7 +2488,7 @@ static void do_frstor(CPUX86State *env, target_ulong ptr, int data32,
int i;
do_fldenv(env, ptr, data32, retaddr);
- ptr += (14 << data32);
+ ptr += (target_ulong)14 << data32;
for (i = 0; i < 8; i++) {
tmp = do_fldt(env, ptr, retaddr);
diff --git a/target/mips/TODO b/target/mips/TODO
deleted file mode 100644
index 1d782d8027..0000000000
--- a/target/mips/TODO
+++ /dev/null
@@ -1,51 +0,0 @@
-Unsolved issues/bugs in the mips/mipsel backend
------------------------------------------------
-
-General
--------
-- Unimplemented ASEs:
- - MDMX
- - SmartMIPS
- - microMIPS DSP r1 & r2 encodings
-- MT ASE only partially implemented and not functional
-- Shadow register support only partially implemented,
- lacks set switching on interrupt/exception.
-- 34K ITC not implemented.
-- A general lack of documentation, especially for technical internals.
- Existing documentation is x86-centric.
-- Reverse endianness bit not implemented
-- The TLB emulation is very inefficient:
- QEMU's softmmu implements a x86-style MMU, with separate entries
- for read/write/execute, a TLB index which is just a modulo of the
- virtual address, and a set of TLBs for each user/kernel/supervisor
- MMU mode.
- MIPS has a single entry for read/write/execute and only one MMU mode.
- But it is fully associative with randomized entry indices, and uses
- up to 256 ASID tags as additional matching criterion (which roughly
- equates to 256 MMU modes). It also has a global flag which causes
- entries to match regardless of ASID.
- To cope with these differences, QEMU currently flushes the TLB at
- each ASID change. Using the MMU modes to implement ASIDs hinges on
- implementing the global bit efficiently.
-- save/restore of the CPU state is not implemented (see machine.c).
-
-MIPS64
-------
-- Userland emulation (both n32 and n64) not functional.
-
-"Generic" 4Kc system emulation
-------------------------------
-- Doesn't correspond to any real hardware. Should be removed some day,
- U-Boot is the last remaining user.
-
-PICA 61 system emulation
-------------------------
-- No framebuffer support yet.
-
-MALTA system emulation
-----------------------
-- We fake firmware support instead of doing the real thing
-- Real firmware (YAMON) falls over when trying to init RAM, presumably
- due to lacking system controller emulation.
-- Bonito system controller not implemented
-- MSC1 system controller not implemented
diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c
index b0877cb39e..19b2409974 100644
--- a/target/nios2/cpu.c
+++ b/target/nios2/cpu.c
@@ -31,12 +31,12 @@ static void nios2_cpu_set_pc(CPUState *cs, vaddr value)
Nios2CPU *cpu = NIOS2_CPU(cs);
CPUNios2State *env = &cpu->env;
- env->regs[R_PC] = value;
+ env->pc = value;
}
static bool nios2_cpu_has_work(CPUState *cs)
{
- return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
+ return cs->interrupt_request & CPU_INTERRUPT_HARD;
}
static void nios2_cpu_reset(DeviceState *dev)
@@ -48,27 +48,42 @@ static void nios2_cpu_reset(DeviceState *dev)
ncc->parent_reset(dev);
- memset(env->regs, 0, sizeof(uint32_t) * NUM_CORE_REGS);
- env->regs[R_PC] = cpu->reset_addr;
+ memset(env->ctrl, 0, sizeof(env->ctrl));
+ env->pc = cpu->reset_addr;
#if defined(CONFIG_USER_ONLY)
/* Start in user mode with interrupts enabled. */
- env->regs[CR_STATUS] = CR_STATUS_U | CR_STATUS_PIE;
+ env->ctrl[CR_STATUS] = CR_STATUS_RSIE | CR_STATUS_U | CR_STATUS_PIE;
+ memset(env->regs, 0, sizeof(env->regs));
#else
- env->regs[CR_STATUS] = 0;
+ env->ctrl[CR_STATUS] = CR_STATUS_RSIE;
+ nios2_update_crs(env);
+ memset(env->shadow_regs, 0, sizeof(env->shadow_regs));
#endif
}
#ifndef CONFIG_USER_ONLY
-static void nios2_cpu_set_irq(void *opaque, int irq, int level)
+static void eic_set_irq(void *opaque, int irq, int level)
+{
+ Nios2CPU *cpu = opaque;
+ CPUState *cs = CPU(cpu);
+
+ if (level) {
+ cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+ } else {
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+ }
+}
+
+static void iic_set_irq(void *opaque, int irq, int level)
{
Nios2CPU *cpu = opaque;
CPUNios2State *env = &cpu->env;
CPUState *cs = CPU(cpu);
- env->regs[CR_IPENDING] = deposit32(env->regs[CR_IPENDING], irq, 1, !!level);
+ env->ctrl[CR_IPENDING] = deposit32(env->ctrl[CR_IPENDING], irq, 1, !!level);
- if (env->regs[CR_IPENDING]) {
+ if (env->ctrl[CR_IPENDING]) {
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
} else {
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
@@ -84,15 +99,6 @@ static void nios2_cpu_initfn(Object *obj)
#if !defined(CONFIG_USER_ONLY)
mmu_init(&cpu->env);
-
- /*
- * These interrupt lines model the IIC (internal interrupt
- * controller). QEMU does not currently support the EIC
- * (external interrupt controller) -- if we did it would be
- * a separate device in hw/intc with a custom interface to
- * the CPU, and boards using it would not wire up these IRQ lines.
- */
- qdev_init_gpio_in_named(DEVICE(cpu), nios2_cpu_set_irq, "IRQ", 32);
#endif
}
@@ -101,37 +107,148 @@ static ObjectClass *nios2_cpu_class_by_name(const char *cpu_model)
return object_class_by_name(TYPE_NIOS2_CPU);
}
+static void realize_cr_status(CPUState *cs)
+{
+ Nios2CPU *cpu = NIOS2_CPU(cs);
+
+ /* Begin with all fields of all registers are reserved. */
+ memset(cpu->cr_state, 0, sizeof(cpu->cr_state));
+
+ /*
+ * The combination of writable and readonly is the set of all
+ * non-reserved fields. We apply writable as a mask to bits,
+ * and merge in existing readonly bits, before storing.
+ */
+#define WR_REG(C) cpu->cr_state[C].writable = -1
+#define RO_REG(C) cpu->cr_state[C].readonly = -1
+#define WR_FIELD(C, F) cpu->cr_state[C].writable |= R_##C##_##F##_MASK
+#define RO_FIELD(C, F) cpu->cr_state[C].readonly |= R_##C##_##F##_MASK
+
+ WR_FIELD(CR_STATUS, PIE);
+ WR_REG(CR_ESTATUS);
+ WR_REG(CR_BSTATUS);
+ RO_REG(CR_CPUID);
+ RO_REG(CR_EXCEPTION);
+ WR_REG(CR_BADADDR);
+
+ if (cpu->eic_present) {
+ WR_FIELD(CR_STATUS, RSIE);
+ RO_FIELD(CR_STATUS, NMI);
+ WR_FIELD(CR_STATUS, PRS);
+ RO_FIELD(CR_STATUS, CRS);
+ WR_FIELD(CR_STATUS, IL);
+ WR_FIELD(CR_STATUS, IH);
+ } else {
+ RO_FIELD(CR_STATUS, RSIE);
+ WR_REG(CR_IENABLE);
+ RO_REG(CR_IPENDING);
+ }
+
+ if (cpu->mmu_present) {
+ WR_FIELD(CR_STATUS, U);
+ WR_FIELD(CR_STATUS, EH);
+
+ WR_FIELD(CR_PTEADDR, VPN);
+ WR_FIELD(CR_PTEADDR, PTBASE);
+
+ RO_FIELD(CR_TLBMISC, D);
+ RO_FIELD(CR_TLBMISC, PERM);
+ RO_FIELD(CR_TLBMISC, BAD);
+ RO_FIELD(CR_TLBMISC, DBL);
+ WR_FIELD(CR_TLBMISC, PID);
+ WR_FIELD(CR_TLBMISC, WE);
+ WR_FIELD(CR_TLBMISC, RD);
+ WR_FIELD(CR_TLBMISC, WAY);
+
+ WR_REG(CR_TLBACC);
+ }
+
+ /*
+ * TODO: ECC (config, eccinj) and MPU (config, mpubase, mpuacc) are
+ * unimplemented, so their corresponding control regs remain reserved.
+ */
+
+#undef WR_REG
+#undef RO_REG
+#undef WR_FIELD
+#undef RO_FIELD
+}
+
static void nios2_cpu_realizefn(DeviceState *dev, Error **errp)
{
CPUState *cs = CPU(dev);
+ Nios2CPU *cpu = NIOS2_CPU(cs);
Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(dev);
Error *local_err = NULL;
+#ifndef CONFIG_USER_ONLY
+ if (cpu->eic_present) {
+ qdev_init_gpio_in_named(DEVICE(cpu), eic_set_irq, "EIC", 1);
+ } else {
+ qdev_init_gpio_in_named(DEVICE(cpu), iic_set_irq, "IRQ", 32);
+ }
+#endif
+
cpu_exec_realizefn(cs, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
return;
}
+ realize_cr_status(cs);
qemu_init_vcpu(cs);
cpu_reset(cs);
+ /* We have reserved storage for cpuid; might as well use it. */
+ cpu->env.ctrl[CR_CPUID] = cs->cpu_index;
+
ncc->parent_realize(dev, errp);
}
#ifndef CONFIG_USER_ONLY
-static bool nios2_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
+static bool eic_take_interrupt(Nios2CPU *cpu)
{
- Nios2CPU *cpu = NIOS2_CPU(cs);
CPUNios2State *env = &cpu->env;
+ const uint32_t status = env->ctrl[CR_STATUS];
- if ((interrupt_request & CPU_INTERRUPT_HARD) &&
- (env->regs[CR_STATUS] & CR_STATUS_PIE) &&
- (env->regs[CR_IPENDING] & env->regs[CR_IENABLE])) {
- cs->exception_index = EXCP_IRQ;
- nios2_cpu_do_interrupt(cs);
+ if (cpu->rnmi) {
+ return !(status & CR_STATUS_NMI);
+ }
+ if (!(status & CR_STATUS_PIE)) {
+ return false;
+ }
+ if (cpu->ril <= FIELD_EX32(status, CR_STATUS, IL)) {
+ return false;
+ }
+ if (cpu->rrs != FIELD_EX32(status, CR_STATUS, CRS)) {
return true;
}
+ return status & CR_STATUS_RSIE;
+}
+
+static bool iic_take_interrupt(Nios2CPU *cpu)
+{
+ CPUNios2State *env = &cpu->env;
+
+ if (!(env->ctrl[CR_STATUS] & CR_STATUS_PIE)) {
+ return false;
+ }
+ return env->ctrl[CR_IPENDING] & env->ctrl[CR_IENABLE];
+}
+
+static bool nios2_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
+{
+ Nios2CPU *cpu = NIOS2_CPU(cs);
+
+ if (interrupt_request & CPU_INTERRUPT_HARD) {
+ if (cpu->eic_present
+ ? eic_take_interrupt(cpu)
+ : iic_take_interrupt(cpu)) {
+ cs->exception_index = EXCP_IRQ;
+ nios2_cpu_do_interrupt(cs);
+ return true;
+ }
+ }
return false;
}
#endif /* !CONFIG_USER_ONLY */
@@ -146,23 +263,26 @@ static void nios2_cpu_disas_set_info(CPUState *cpu, disassemble_info *info)
static int nios2_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
{
Nios2CPU *cpu = NIOS2_CPU(cs);
- CPUClass *cc = CPU_GET_CLASS(cs);
CPUNios2State *env = &cpu->env;
-
- if (n > cc->gdb_num_core_regs) {
- return 0;
- }
+ uint32_t val;
if (n < 32) { /* GP regs */
- return gdb_get_reg32(mem_buf, env->regs[n]);
+ val = env->regs[n];
} else if (n == 32) { /* PC */
- return gdb_get_reg32(mem_buf, env->regs[R_PC]);
+ val = env->pc;
} else if (n < 49) { /* Status regs */
- return gdb_get_reg32(mem_buf, env->regs[n - 1]);
+ unsigned cr = n - 33;
+ if (nios2_cr_reserved(&cpu->cr_state[cr])) {
+ val = 0;
+ } else {
+ val = env->ctrl[n - 33];
+ }
+ } else {
+ /* Invalid regs */
+ return 0;
}
- /* Invalid regs */
- return 0;
+ return gdb_get_reg32(mem_buf, val);
}
static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
@@ -170,23 +290,32 @@ static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
Nios2CPU *cpu = NIOS2_CPU(cs);
CPUClass *cc = CPU_GET_CLASS(cs);
CPUNios2State *env = &cpu->env;
+ uint32_t val;
if (n > cc->gdb_num_core_regs) {
return 0;
}
+ val = ldl_p(mem_buf);
if (n < 32) { /* GP regs */
- env->regs[n] = ldl_p(mem_buf);
+ env->regs[n] = val;
} else if (n == 32) { /* PC */
- env->regs[R_PC] = ldl_p(mem_buf);
+ env->pc = val;
} else if (n < 49) { /* Status regs */
- env->regs[n - 1] = ldl_p(mem_buf);
+ unsigned cr = n - 33;
+ /* ??? Maybe allow the debugger to write to readonly fields. */
+ val &= cpu->cr_state[cr].writable;
+ val |= cpu->cr_state[cr].readonly & env->ctrl[cr];
+ env->ctrl[cr] = val;
+ } else {
+ g_assert_not_reached();
}
return 4;
}
static Property nios2_properties[] = {
+ DEFINE_PROP_BOOL("diverr_present", Nios2CPU, diverr_present, true),
DEFINE_PROP_BOOL("mmu_present", Nios2CPU, mmu_present, true),
/* ALTR,pid-num-bits */
DEFINE_PROP_UINT32("mmu_pid_num_bits", Nios2CPU, pid_num_bits, 8),
@@ -210,9 +339,7 @@ static const struct SysemuCPUOps nios2_sysemu_ops = {
static const struct TCGCPUOps nios2_tcg_ops = {
.initialize = nios2_tcg_init,
-#ifdef CONFIG_USER_ONLY
- .record_sigsegv = nios2_cpu_record_sigsegv,
-#else
+#ifndef CONFIG_USER_ONLY
.tlb_fill = nios2_cpu_tlb_fill,
.cpu_exec_interrupt = nios2_cpu_exec_interrupt,
.do_interrupt = nios2_cpu_do_interrupt,
diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h
index 1bab805bb0..f85581ee56 100644
--- a/target/nios2/cpu.h
+++ b/target/nios2/cpu.h
@@ -23,6 +23,7 @@
#include "exec/cpu-defs.h"
#include "hw/core/cpu.h"
+#include "hw/registerfields.h"
#include "qom/object.h"
typedef struct CPUArchState CPUNios2State;
@@ -56,83 +57,114 @@ struct Nios2CPUClass {
#define EXCEPTION_ADDRESS 0x00000004
#define FAST_TLB_MISS_ADDRESS 0x00000008
+#define NUM_GP_REGS 32
+#define NUM_CR_REGS 32
-/* GP regs + CR regs + PC */
-#define NUM_CORE_REGS (32 + 32 + 1)
+#ifndef CONFIG_USER_ONLY
+/* 63 shadow register sets; index 0 is the primary register set. */
+#define NUM_REG_SETS 64
+#endif
/* General purpose register aliases */
-#define R_ZERO 0
-#define R_AT 1
-#define R_RET0 2
-#define R_RET1 3
-#define R_ARG0 4
-#define R_ARG1 5
-#define R_ARG2 6
-#define R_ARG3 7
-#define R_ET 24
-#define R_BT 25
-#define R_GP 26
-#define R_SP 27
-#define R_FP 28
-#define R_EA 29
-#define R_BA 30
-#define R_RA 31
+enum {
+ R_ZERO = 0,
+ R_AT = 1,
+ R_RET0 = 2,
+ R_RET1 = 3,
+ R_ARG0 = 4,
+ R_ARG1 = 5,
+ R_ARG2 = 6,
+ R_ARG3 = 7,
+ R_ET = 24,
+ R_BT = 25,
+ R_GP = 26,
+ R_SP = 27,
+ R_FP = 28,
+ R_EA = 29,
+ R_BA = 30,
+ R_SSTATUS = 30,
+ R_RA = 31,
+};
/* Control register aliases */
-#define CR_BASE 32
-#define CR_STATUS (CR_BASE + 0)
-#define CR_STATUS_PIE (1 << 0)
-#define CR_STATUS_U (1 << 1)
-#define CR_STATUS_EH (1 << 2)
-#define CR_STATUS_IH (1 << 3)
-#define CR_STATUS_IL (63 << 4)
-#define CR_STATUS_CRS (63 << 10)
-#define CR_STATUS_PRS (63 << 16)
-#define CR_STATUS_NMI (1 << 22)
-#define CR_STATUS_RSIE (1 << 23)
-#define CR_ESTATUS (CR_BASE + 1)
-#define CR_BSTATUS (CR_BASE + 2)
-#define CR_IENABLE (CR_BASE + 3)
-#define CR_IPENDING (CR_BASE + 4)
-#define CR_CPUID (CR_BASE + 5)
-#define CR_CTL6 (CR_BASE + 6)
-#define CR_EXCEPTION (CR_BASE + 7)
-#define CR_PTEADDR (CR_BASE + 8)
-#define CR_PTEADDR_PTBASE_SHIFT 22
-#define CR_PTEADDR_PTBASE_MASK (0x3FF << CR_PTEADDR_PTBASE_SHIFT)
-#define CR_PTEADDR_VPN_SHIFT 2
-#define CR_PTEADDR_VPN_MASK (0xFFFFF << CR_PTEADDR_VPN_SHIFT)
-#define CR_TLBACC (CR_BASE + 9)
-#define CR_TLBACC_IGN_SHIFT 25
-#define CR_TLBACC_IGN_MASK (0x7F << CR_TLBACC_IGN_SHIFT)
-#define CR_TLBACC_C (1 << 24)
-#define CR_TLBACC_R (1 << 23)
-#define CR_TLBACC_W (1 << 22)
-#define CR_TLBACC_X (1 << 21)
-#define CR_TLBACC_G (1 << 20)
-#define CR_TLBACC_PFN_MASK 0x000FFFFF
-#define CR_TLBMISC (CR_BASE + 10)
-#define CR_TLBMISC_WAY_SHIFT 20
-#define CR_TLBMISC_WAY_MASK (0xF << CR_TLBMISC_WAY_SHIFT)
-#define CR_TLBMISC_RD (1 << 19)
-#define CR_TLBMISC_WR (1 << 18)
-#define CR_TLBMISC_PID_SHIFT 4
-#define CR_TLBMISC_PID_MASK (0x3FFF << CR_TLBMISC_PID_SHIFT)
-#define CR_TLBMISC_DBL (1 << 3)
-#define CR_TLBMISC_BAD (1 << 2)
-#define CR_TLBMISC_PERM (1 << 1)
-#define CR_TLBMISC_D (1 << 0)
-#define CR_ENCINJ (CR_BASE + 11)
-#define CR_BADADDR (CR_BASE + 12)
-#define CR_CONFIG (CR_BASE + 13)
-#define CR_MPUBASE (CR_BASE + 14)
-#define CR_MPUACC (CR_BASE + 15)
-
-/* Other registers */
-#define R_PC 64
+enum {
+ CR_STATUS = 0,
+ CR_ESTATUS = 1,
+ CR_BSTATUS = 2,
+ CR_IENABLE = 3,
+ CR_IPENDING = 4,
+ CR_CPUID = 5,
+ CR_EXCEPTION = 7,
+ CR_PTEADDR = 8,
+ CR_TLBACC = 9,
+ CR_TLBMISC = 10,
+ CR_ENCINJ = 11,
+ CR_BADADDR = 12,
+ CR_CONFIG = 13,
+ CR_MPUBASE = 14,
+ CR_MPUACC = 15,
+};
+
+FIELD(CR_STATUS, PIE, 0, 1)
+FIELD(CR_STATUS, U, 1, 1)
+FIELD(CR_STATUS, EH, 2, 1)
+FIELD(CR_STATUS, IH, 3, 1)
+FIELD(CR_STATUS, IL, 4, 6)
+FIELD(CR_STATUS, CRS, 10, 6)
+FIELD(CR_STATUS, PRS, 16, 6)
+FIELD(CR_STATUS, NMI, 22, 1)
+FIELD(CR_STATUS, RSIE, 23, 1)
+FIELD(CR_STATUS, SRS, 31, 1) /* only in sstatus */
+
+#define CR_STATUS_PIE R_CR_STATUS_PIE_MASK
+#define CR_STATUS_U R_CR_STATUS_U_MASK
+#define CR_STATUS_EH R_CR_STATUS_EH_MASK
+#define CR_STATUS_IH R_CR_STATUS_IH_MASK
+#define CR_STATUS_NMI R_CR_STATUS_NMI_MASK
+#define CR_STATUS_RSIE R_CR_STATUS_RSIE_MASK
+#define CR_STATUS_SRS R_CR_STATUS_SRS_MASK
+
+FIELD(CR_EXCEPTION, CAUSE, 2, 5)
+FIELD(CR_EXCEPTION, ECCFTL, 31, 1)
+
+FIELD(CR_PTEADDR, VPN, 2, 20)
+FIELD(CR_PTEADDR, PTBASE, 22, 10)
+
+FIELD(CR_TLBACC, PFN, 0, 20)
+FIELD(CR_TLBACC, G, 20, 1)
+FIELD(CR_TLBACC, X, 21, 1)
+FIELD(CR_TLBACC, W, 22, 1)
+FIELD(CR_TLBACC, R, 23, 1)
+FIELD(CR_TLBACC, C, 24, 1)
+FIELD(CR_TLBACC, IG, 25, 7)
+
+#define CR_TLBACC_C R_CR_TLBACC_C_MASK
+#define CR_TLBACC_R R_CR_TLBACC_R_MASK
+#define CR_TLBACC_W R_CR_TLBACC_W_MASK
+#define CR_TLBACC_X R_CR_TLBACC_X_MASK
+#define CR_TLBACC_G R_CR_TLBACC_G_MASK
+
+FIELD(CR_TLBMISC, D, 0, 1)
+FIELD(CR_TLBMISC, PERM, 1, 1)
+FIELD(CR_TLBMISC, BAD, 2, 1)
+FIELD(CR_TLBMISC, DBL, 3, 1)
+FIELD(CR_TLBMISC, PID, 4, 14)
+FIELD(CR_TLBMISC, WE, 18, 1)
+FIELD(CR_TLBMISC, RD, 19, 1)
+FIELD(CR_TLBMISC, WAY, 20, 4)
+FIELD(CR_TLBMISC, EE, 24, 1)
+
+#define CR_TLBMISC_EE R_CR_TLBMISC_EE_MASK
+#define CR_TLBMISC_RD R_CR_TLBMISC_RD_MASK
+#define CR_TLBMISC_WE R_CR_TLBMISC_WE_MASK
+#define CR_TLBMISC_DBL R_CR_TLBMISC_DBL_MASK
+#define CR_TLBMISC_BAD R_CR_TLBMISC_BAD_MASK
+#define CR_TLBMISC_PERM R_CR_TLBMISC_PERM_MASK
+#define CR_TLBMISC_D R_CR_TLBMISC_D_MASK
/* Exceptions */
#define EXCP_BREAK 0x1000
+#define EXCP_SEMIHOST 0x1001
#define EXCP_RESET 0
#define EXCP_PRESET 1
#define EXCP_IRQ 2
@@ -142,20 +174,27 @@ struct Nios2CPUClass {
#define EXCP_UNALIGN 6
#define EXCP_UNALIGND 7
#define EXCP_DIV 8
-#define EXCP_SUPERA 9
+#define EXCP_SUPERA_X 9
#define EXCP_SUPERI 10
-#define EXCP_SUPERD 11
-#define EXCP_TLBD 12
-#define EXCP_TLBX 13
-#define EXCP_TLBR 14
-#define EXCP_TLBW 15
+#define EXCP_SUPERA_D 11
+#define EXCP_TLB_X 12
+#define EXCP_TLB_D (0x1000 | EXCP_TLB_X)
+#define EXCP_PERM_X 13
+#define EXCP_PERM_R 14
+#define EXCP_PERM_W 15
#define EXCP_MPUI 16
#define EXCP_MPUD 17
-#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3
-
struct CPUArchState {
- uint32_t regs[NUM_CORE_REGS];
+#ifdef CONFIG_USER_ONLY
+ uint32_t regs[NUM_GP_REGS];
+#else
+ uint32_t shadow_regs[NUM_REG_SETS][NUM_GP_REGS];
+ /* Pointer into shadow_regs for the current register set. */
+ uint32_t *regs;
+#endif
+ uint32_t ctrl[NUM_CR_REGS];
+ uint32_t pc;
#if !defined(CONFIG_USER_ONLY)
Nios2MMU mmu;
@@ -163,6 +202,11 @@ struct CPUArchState {
int error_code;
};
+typedef struct {
+ uint32_t writable;
+ uint32_t readonly;
+} ControlRegState;
+
/**
* Nios2CPU:
* @env: #CPUNios2State
@@ -177,7 +221,10 @@ struct ArchCPU {
CPUNegativeOffsetState neg;
CPUNios2State env;
+ bool diverr_present;
bool mmu_present;
+ bool eic_present;
+
uint32_t pid_num_bits;
uint32_t tlb_num_ways;
uint32_t tlb_num_entries;
@@ -186,9 +233,31 @@ struct ArchCPU {
uint32_t reset_addr;
uint32_t exception_addr;
uint32_t fast_tlb_miss_addr;
+
+ /* Bits within each control register which are reserved or readonly. */
+ ControlRegState cr_state[NUM_CR_REGS];
+
+ /* External Interrupt Controller Interface */
+ uint32_t rha; /* Requested handler address */
+ uint32_t ril; /* Requested interrupt level */
+ uint32_t rrs; /* Requested register set */
+ bool rnmi; /* Requested nonmaskable interrupt */
};
+static inline bool nios2_cr_reserved(const ControlRegState *s)
+{
+ return (s->writable | s->readonly) == 0;
+}
+
+static inline void nios2_update_crs(CPUNios2State *env)
+{
+#ifndef CONFIG_USER_ONLY
+ unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS);
+ env->regs = env->shadow_regs[crs];
+#endif
+}
+
void nios2_tcg_init(void);
void nios2_cpu_do_interrupt(CPUState *cs);
void dump_mmu(CPUNios2State *env);
@@ -197,6 +266,8 @@ hwaddr nios2_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
G_NORETURN void nios2_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
MMUAccessType access_type, int mmu_idx,
uintptr_t retaddr);
+G_NORETURN void nios2_cpu_loop_exit_advance(CPUNios2State *env,
+ uintptr_t retaddr);
void do_nios2_semihosting(CPUNios2State *env);
@@ -212,36 +283,35 @@ void do_nios2_semihosting(CPUNios2State *env);
static inline int cpu_mmu_index(CPUNios2State *env, bool ifetch)
{
- return (env->regs[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX :
+ return (env->ctrl[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX :
MMU_SUPERVISOR_IDX;
}
-#ifdef CONFIG_USER_ONLY
-void nios2_cpu_record_sigsegv(CPUState *cpu, vaddr addr,
- MMUAccessType access_type,
- bool maperr, uintptr_t ra);
-#else
+#ifndef CONFIG_USER_ONLY
bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
bool probe, uintptr_t retaddr);
#endif
-static inline int cpu_interrupts_enabled(CPUNios2State *env)
-{
- return env->regs[CR_STATUS] & CR_STATUS_PIE;
-}
-
typedef CPUNios2State CPUArchState;
typedef Nios2CPU ArchCPU;
#include "exec/cpu-all.h"
+FIELD(TBFLAGS, CRS0, 0, 1) /* Set if CRS == 0. */
+FIELD(TBFLAGS, U, 1, 1) /* Overlaps CR_STATUS_U */
+FIELD(TBFLAGS, R0_0, 2, 1) /* Set if R0 == 0. */
+
static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc,
target_ulong *cs_base, uint32_t *flags)
{
- *pc = env->regs[R_PC];
+ unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS);
+
+ *pc = env->pc;
*cs_base = 0;
- *flags = (env->regs[CR_STATUS] & (CR_STATUS_EH | CR_STATUS_U));
+ *flags = (env->ctrl[CR_STATUS] & CR_STATUS_U)
+ | (crs ? 0 : R_TBFLAGS_CRS0_MASK)
+ | (env->regs[0] ? 0 : R_TBFLAGS_R0_0_MASK);
}
#endif /* NIOS2_CPU_H */
diff --git a/target/nios2/helper.c b/target/nios2/helper.c
index e5c98650e1..bb3b09e5a7 100644
--- a/target/nios2/helper.c
+++ b/target/nios2/helper.c
@@ -28,176 +28,234 @@
#include "exec/helper-proto.h"
#include "semihosting/semihost.h"
-#if defined(CONFIG_USER_ONLY)
-void nios2_cpu_do_interrupt(CPUState *cs)
+static void do_exception(Nios2CPU *cpu, uint32_t exception_addr,
+ uint32_t tlbmisc_set, bool is_break)
{
- Nios2CPU *cpu = NIOS2_CPU(cs);
CPUNios2State *env = &cpu->env;
- cs->exception_index = -1;
- env->regs[R_EA] = env->regs[R_PC] + 4;
-}
+ CPUState *cs = CPU(cpu);
+ uint32_t old_status = env->ctrl[CR_STATUS];
+ uint32_t new_status = old_status;
-void nios2_cpu_record_sigsegv(CPUState *cs, vaddr addr,
- MMUAccessType access_type,
- bool maperr, uintptr_t retaddr)
-{
- /* FIXME: Disentangle kuser page from linux-user sigsegv handling. */
- cs->exception_index = 0xaa;
- cpu_loop_exit_restore(cs, retaddr);
-}
+ /* With shadow regs, exceptions are always taken into CRS 0. */
+ new_status &= ~R_CR_STATUS_CRS_MASK;
+ env->regs = env->shadow_regs[0];
-#else /* !CONFIG_USER_ONLY */
+ if ((old_status & CR_STATUS_EH) == 0) {
+ int r_ea = R_EA, cr_es = CR_ESTATUS;
-void nios2_cpu_do_interrupt(CPUState *cs)
-{
- Nios2CPU *cpu = NIOS2_CPU(cs);
- CPUNios2State *env = &cpu->env;
+ if (is_break) {
+ r_ea = R_BA;
+ cr_es = CR_BSTATUS;
+ }
+ env->ctrl[cr_es] = old_status;
+ env->regs[r_ea] = env->pc;
+
+ if (cpu->mmu_present) {
+ new_status |= CR_STATUS_EH;
+
+ /*
+ * There are 4 bits that are always written.
+ * Explicitly clear them, to be set via the argument.
+ */
+ env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D |
+ CR_TLBMISC_PERM |
+ CR_TLBMISC_BAD |
+ CR_TLBMISC_DBL);
+ env->ctrl[CR_TLBMISC] |= tlbmisc_set;
+ }
- switch (cs->exception_index) {
- case EXCP_IRQ:
- assert(env->regs[CR_STATUS] & CR_STATUS_PIE);
+ /*
+ * With shadow regs, and EH == 0, PRS is set from CRS.
+ * At least, so says Table 3-9, and some other text,
+ * though Table 3-38 says otherwise.
+ */
+ new_status = FIELD_DP32(new_status, CR_STATUS, PRS,
+ FIELD_EX32(old_status, CR_STATUS, CRS));
+ }
- qemu_log_mask(CPU_LOG_INT, "interrupt at pc=%x\n", env->regs[R_PC]);
+ new_status &= ~(CR_STATUS_PIE | CR_STATUS_U);
- env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
- env->regs[CR_STATUS] |= CR_STATUS_IH;
- env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
+ env->ctrl[CR_STATUS] = new_status;
+ if (!is_break) {
+ env->ctrl[CR_EXCEPTION] = FIELD_DP32(0, CR_EXCEPTION, CAUSE,
+ cs->exception_index);
+ }
+ env->pc = exception_addr;
+}
- env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
- env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+static void do_iic_irq(Nios2CPU *cpu)
+{
+ do_exception(cpu, cpu->exception_addr, 0, false);
+}
- env->regs[R_EA] = env->regs[R_PC] + 4;
- env->regs[R_PC] = cpu->exception_addr;
- break;
+static void do_eic_irq(Nios2CPU *cpu)
+{
+ CPUNios2State *env = &cpu->env;
+ uint32_t old_status = env->ctrl[CR_STATUS];
+ uint32_t new_status = old_status;
+ uint32_t old_rs = FIELD_EX32(old_status, CR_STATUS, CRS);
+ uint32_t new_rs = cpu->rrs;
+
+ new_status = FIELD_DP32(new_status, CR_STATUS, CRS, new_rs);
+ new_status = FIELD_DP32(new_status, CR_STATUS, IL, cpu->ril);
+ new_status = FIELD_DP32(new_status, CR_STATUS, NMI, cpu->rnmi);
+ new_status &= ~(CR_STATUS_RSIE | CR_STATUS_U);
+ new_status |= CR_STATUS_IH;
+
+ if (!(new_status & CR_STATUS_EH)) {
+ new_status = FIELD_DP32(new_status, CR_STATUS, PRS, old_rs);
+ if (new_rs == 0) {
+ env->ctrl[CR_ESTATUS] = old_status;
+ } else {
+ if (new_rs != old_rs) {
+ old_status |= CR_STATUS_SRS;
+ }
+ env->shadow_regs[new_rs][R_SSTATUS] = old_status;
+ }
+ env->shadow_regs[new_rs][R_EA] = env->pc;
+ }
- case EXCP_TLBD:
- if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
- qemu_log_mask(CPU_LOG_INT, "TLB MISS (fast) at pc=%x\n",
- env->regs[R_PC]);
+ env->ctrl[CR_STATUS] = new_status;
+ nios2_update_crs(env);
- /* Fast TLB miss */
- /* Variation from the spec. Table 3-35 of the cpu reference shows
- * estatus not being changed for TLB miss but this appears to
- * be incorrect. */
- env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
- env->regs[CR_STATUS] |= CR_STATUS_EH;
- env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
+ env->pc = cpu->rha;
+}
- env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
- env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+void nios2_cpu_do_interrupt(CPUState *cs)
+{
+ Nios2CPU *cpu = NIOS2_CPU(cs);
+ CPUNios2State *env = &cpu->env;
+ uint32_t tlbmisc_set = 0;
- env->regs[CR_TLBMISC] &= ~CR_TLBMISC_DBL;
- env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
+ if (qemu_loglevel_mask(CPU_LOG_INT)) {
+ const char *name = NULL;
- env->regs[R_EA] = env->regs[R_PC] + 4;
- env->regs[R_PC] = cpu->fast_tlb_miss_addr;
+ switch (cs->exception_index) {
+ case EXCP_IRQ:
+ name = "interrupt";
+ break;
+ case EXCP_TLB_X:
+ case EXCP_TLB_D:
+ if (env->ctrl[CR_STATUS] & CR_STATUS_EH) {
+ name = "TLB MISS (double)";
+ } else {
+ name = "TLB MISS (fast)";
+ }
+ break;
+ case EXCP_PERM_R:
+ case EXCP_PERM_W:
+ case EXCP_PERM_X:
+ name = "TLB PERM";
+ break;
+ case EXCP_SUPERA_X:
+ case EXCP_SUPERA_D:
+ name = "SUPERVISOR (address)";
+ break;
+ case EXCP_SUPERI:
+ name = "SUPERVISOR (insn)";
+ break;
+ case EXCP_ILLEGAL:
+ name = "ILLEGAL insn";
+ break;
+ case EXCP_UNALIGN:
+ name = "Misaligned (data)";
+ break;
+ case EXCP_UNALIGND:
+ name = "Misaligned (destination)";
+ break;
+ case EXCP_DIV:
+ name = "DIV error";
+ break;
+ case EXCP_TRAP:
+ name = "TRAP insn";
+ break;
+ case EXCP_BREAK:
+ name = "BREAK insn";
+ break;
+ case EXCP_SEMIHOST:
+ name = "SEMIHOST insn";
+ break;
+ }
+ if (name) {
+ qemu_log("%s at pc=0x%08x\n", name, env->pc);
} else {
- qemu_log_mask(CPU_LOG_INT, "TLB MISS (double) at pc=%x\n",
- env->regs[R_PC]);
-
- /* Double TLB miss */
- env->regs[CR_STATUS] |= CR_STATUS_EH;
- env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
-
- env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
- env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
-
- env->regs[CR_TLBMISC] |= CR_TLBMISC_DBL;
+ qemu_log("Unknown exception %d at pc=0x%08x\n",
+ cs->exception_index, env->pc);
+ }
+ }
- env->regs[R_PC] = cpu->exception_addr;
+ switch (cs->exception_index) {
+ case EXCP_IRQ:
+ /* Note that PC is advanced for interrupts as well. */
+ env->pc += 4;
+ if (cpu->eic_present) {
+ do_eic_irq(cpu);
+ } else {
+ do_iic_irq(cpu);
}
break;
- case EXCP_TLBR:
- case EXCP_TLBW:
- case EXCP_TLBX:
- qemu_log_mask(CPU_LOG_INT, "TLB PERM at pc=%x\n", env->regs[R_PC]);
-
- env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
- env->regs[CR_STATUS] |= CR_STATUS_EH;
- env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
-
- env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
- env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
-
- if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
- env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
+ case EXCP_TLB_D:
+ tlbmisc_set = CR_TLBMISC_D;
+ /* fall through */
+ case EXCP_TLB_X:
+ if (env->ctrl[CR_STATUS] & CR_STATUS_EH) {
+ tlbmisc_set |= CR_TLBMISC_DBL;
+ /*
+ * Normally, we don't write to tlbmisc unless !EH,
+ * so do it manually for the double-tlb miss exception.
+ */
+ env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D |
+ CR_TLBMISC_PERM |
+ CR_TLBMISC_BAD);
+ env->ctrl[CR_TLBMISC] |= tlbmisc_set;
+ do_exception(cpu, cpu->exception_addr, 0, false);
+ } else {
+ tlbmisc_set |= CR_TLBMISC_WE;
+ do_exception(cpu, cpu->fast_tlb_miss_addr, tlbmisc_set, false);
}
-
- env->regs[R_EA] = env->regs[R_PC] + 4;
- env->regs[R_PC] = cpu->exception_addr;
break;
- case EXCP_SUPERA:
- case EXCP_SUPERI:
- case EXCP_SUPERD:
- qemu_log_mask(CPU_LOG_INT, "SUPERVISOR exception at pc=%x\n",
- env->regs[R_PC]);
-
- if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
- env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
- env->regs[R_EA] = env->regs[R_PC] + 4;
+ case EXCP_PERM_R:
+ case EXCP_PERM_W:
+ tlbmisc_set = CR_TLBMISC_D;
+ /* fall through */
+ case EXCP_PERM_X:
+ tlbmisc_set |= CR_TLBMISC_PERM;
+ if (!(env->ctrl[CR_STATUS] & CR_STATUS_EH)) {
+ tlbmisc_set |= CR_TLBMISC_WE;
}
+ do_exception(cpu, cpu->exception_addr, tlbmisc_set, false);
+ break;
- env->regs[CR_STATUS] |= CR_STATUS_EH;
- env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
-
- env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
- env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
-
- env->regs[R_PC] = cpu->exception_addr;
+ case EXCP_SUPERA_D:
+ case EXCP_UNALIGN:
+ tlbmisc_set = CR_TLBMISC_D;
+ /* fall through */
+ case EXCP_SUPERA_X:
+ case EXCP_UNALIGND:
+ tlbmisc_set |= CR_TLBMISC_BAD;
+ do_exception(cpu, cpu->exception_addr, tlbmisc_set, false);
break;
+ case EXCP_SUPERI:
case EXCP_ILLEGAL:
+ case EXCP_DIV:
case EXCP_TRAP:
- qemu_log_mask(CPU_LOG_INT, "TRAP exception at pc=%x\n",
- env->regs[R_PC]);
-
- if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
- env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
- env->regs[R_EA] = env->regs[R_PC] + 4;
- }
-
- env->regs[CR_STATUS] |= CR_STATUS_EH;
- env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
-
- env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
- env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
-
- env->regs[R_PC] = cpu->exception_addr;
+ do_exception(cpu, cpu->exception_addr, 0, false);
break;
case EXCP_BREAK:
- qemu_log_mask(CPU_LOG_INT, "BREAK exception at pc=%x\n",
- env->regs[R_PC]);
- /* The semihosting instruction is "break 1". */
- if (semihosting_enabled() &&
- cpu_ldl_code(env, env->regs[R_PC]) == 0x003da07a) {
- qemu_log_mask(CPU_LOG_INT, "Entering semihosting\n");
- env->regs[R_PC] += 4;
- do_nios2_semihosting(env);
- break;
- }
-
- if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
- env->regs[CR_BSTATUS] = env->regs[CR_STATUS];
- env->regs[R_BA] = env->regs[R_PC] + 4;
- }
-
- env->regs[CR_STATUS] |= CR_STATUS_EH;
- env->regs[CR_STATUS] &= ~(CR_STATUS_PIE | CR_STATUS_U);
-
- env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
- env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+ do_exception(cpu, cpu->exception_addr, 0, true);
+ break;
- env->regs[R_PC] = cpu->exception_addr;
+ case EXCP_SEMIHOST:
+ do_nios2_semihosting(env);
break;
default:
- cpu_abort(cs, "unhandled exception type=%d\n",
- cs->exception_index);
- break;
+ cpu_abort(cs, "unhandled exception type=%d\n", cs->exception_index);
}
}
@@ -232,9 +290,9 @@ void nios2_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
Nios2CPU *cpu = NIOS2_CPU(cs);
CPUNios2State *env = &cpu->env;
- env->regs[CR_BADADDR] = addr;
- env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2;
- helper_raise_exception(env, EXCP_UNALIGN);
+ env->ctrl[CR_BADADDR] = addr;
+ cs->exception_index = EXCP_UNALIGN;
+ nios2_cpu_loop_exit_advance(env, retaddr);
}
bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
@@ -243,7 +301,7 @@ bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
{
Nios2CPU *cpu = NIOS2_CPU(cs);
CPUNios2State *env = &cpu->env;
- unsigned int excp = EXCP_TLBD;
+ unsigned int excp;
target_ulong vaddr, paddr;
Nios2MMULookup lu;
unsigned int hit;
@@ -270,9 +328,10 @@ bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
if (probe) {
return false;
}
- cs->exception_index = EXCP_SUPERA;
- env->regs[CR_BADADDR] = address;
- cpu_loop_exit_restore(cs, retaddr);
+ cs->exception_index = (access_type == MMU_INST_FETCH
+ ? EXCP_SUPERA_X : EXCP_SUPERA_D);
+ env->ctrl[CR_BADADDR] = address;
+ nios2_cpu_loop_exit_advance(env, retaddr);
}
}
@@ -291,25 +350,23 @@ bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
}
/* Permission violation */
- excp = (access_type == MMU_DATA_LOAD ? EXCP_TLBR :
- access_type == MMU_DATA_STORE ? EXCP_TLBW : EXCP_TLBX);
+ excp = (access_type == MMU_DATA_LOAD ? EXCP_PERM_R :
+ access_type == MMU_DATA_STORE ? EXCP_PERM_W : EXCP_PERM_X);
+ } else {
+ excp = (access_type == MMU_INST_FETCH ? EXCP_TLB_X: EXCP_TLB_D);
}
if (probe) {
return false;
}
- if (access_type == MMU_INST_FETCH) {
- env->regs[CR_TLBMISC] &= ~CR_TLBMISC_D;
- } else {
- env->regs[CR_TLBMISC] |= CR_TLBMISC_D;
- }
- env->regs[CR_PTEADDR] &= CR_PTEADDR_PTBASE_MASK;
- env->regs[CR_PTEADDR] |= (address >> 10) & CR_PTEADDR_VPN_MASK;
- env->mmu.pteaddr_wr = env->regs[CR_PTEADDR];
+ env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC], CR_TLBMISC, D,
+ access_type != MMU_INST_FETCH);
+ env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR], CR_PTEADDR, VPN,
+ address >> TARGET_PAGE_BITS);
+ env->mmu.pteaddr_wr = env->ctrl[CR_PTEADDR];
cs->exception_index = excp;
- env->regs[CR_BADADDR] = address;
- cpu_loop_exit_restore(cs, retaddr);
+ env->ctrl[CR_BADADDR] = address;
+ nios2_cpu_loop_exit_advance(env, retaddr);
}
-#endif /* !CONFIG_USER_ONLY */
diff --git a/target/nios2/helper.h b/target/nios2/helper.h
index a44ecfdf7a..1648d76ade 100644
--- a/target/nios2/helper.h
+++ b/target/nios2/helper.h
@@ -19,8 +19,13 @@
*/
DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, i32)
+DEF_HELPER_FLAGS_3(divs, TCG_CALL_NO_WG, s32, env, s32, s32)
+DEF_HELPER_FLAGS_3(divu, TCG_CALL_NO_WG, i32, env, i32, i32)
#if !defined(CONFIG_USER_ONLY)
+DEF_HELPER_3(eret, noreturn, env, i32, i32)
+DEF_HELPER_FLAGS_2(rdprs, TCG_CALL_NO_WG, i32, env, i32)
+DEF_HELPER_3(wrprs, void, env, i32, i32)
DEF_HELPER_2(mmu_write_tlbacc, void, env, i32)
DEF_HELPER_2(mmu_write_tlbmisc, void, env, i32)
DEF_HELPER_2(mmu_write_pteaddr, void, env, i32)
diff --git a/target/nios2/meson.build b/target/nios2/meson.build
index 62b384702d..2bd60ba306 100644
--- a/target/nios2/meson.build
+++ b/target/nios2/meson.build
@@ -1,14 +1,17 @@
nios2_ss = ss.source_set()
nios2_ss.add(files(
'cpu.c',
- 'helper.c',
'nios2-semi.c',
'op_helper.c',
'translate.c',
))
nios2_softmmu_ss = ss.source_set()
-nios2_softmmu_ss.add(files('monitor.c', 'mmu.c'))
+nios2_softmmu_ss.add(files(
+ 'helper.c',
+ 'monitor.c',
+ 'mmu.c'
+))
target_arch += {'nios2': nios2_ss}
target_softmmu_arch += {'nios2': nios2_softmmu_ss}
diff --git a/target/nios2/mmu.c b/target/nios2/mmu.c
index 4daab2a7ab..d9b690b78e 100644
--- a/target/nios2/mmu.c
+++ b/target/nios2/mmu.c
@@ -33,7 +33,7 @@ unsigned int mmu_translate(CPUNios2State *env,
target_ulong vaddr, int rw, int mmu_idx)
{
Nios2CPU *cpu = env_archcpu(env);
- int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
+ int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID);
int vpn = vaddr >> 12;
int way, n_ways = cpu->tlb_num_ways;
@@ -49,7 +49,7 @@ unsigned int mmu_translate(CPUNios2State *env,
}
lu->vaddr = vaddr & TARGET_PAGE_MASK;
- lu->paddr = (entry->data & CR_TLBACC_PFN_MASK) << TARGET_PAGE_BITS;
+ lu->paddr = FIELD_EX32(entry->data, CR_TLBACC, PFN) << TARGET_PAGE_BITS;
lu->prot = ((entry->data & CR_TLBACC_R) ? PAGE_READ : 0) |
((entry->data & CR_TLBACC_W) ? PAGE_WRITE : 0) |
((entry->data & CR_TLBACC_X) ? PAGE_EXEC : 0);
@@ -86,27 +86,27 @@ void helper_mmu_write_tlbacc(CPUNios2State *env, uint32_t v)
CPUState *cs = env_cpu(env);
Nios2CPU *cpu = env_archcpu(env);
- trace_nios2_mmu_write_tlbacc(v >> CR_TLBACC_IGN_SHIFT,
+ trace_nios2_mmu_write_tlbacc(FIELD_EX32(v, CR_TLBACC, IG),
(v & CR_TLBACC_C) ? 'C' : '.',
(v & CR_TLBACC_R) ? 'R' : '.',
(v & CR_TLBACC_W) ? 'W' : '.',
(v & CR_TLBACC_X) ? 'X' : '.',
(v & CR_TLBACC_G) ? 'G' : '.',
- v & CR_TLBACC_PFN_MASK);
+ FIELD_EX32(v, CR_TLBACC, PFN));
/* if tlbmisc.WE == 1 then trigger a TLB write on writes to TLBACC */
- if (env->regs[CR_TLBMISC] & CR_TLBMISC_WR) {
- int way = (env->regs[CR_TLBMISC] >> CR_TLBMISC_WAY_SHIFT);
- int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
- int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
- int g = (v & CR_TLBACC_G) ? 1 : 0;
- int valid = ((vpn & CR_TLBACC_PFN_MASK) < 0xC0000) ? 1 : 0;
+ if (env->ctrl[CR_TLBMISC] & CR_TLBMISC_WE) {
+ int way = FIELD_EX32(env->ctrl[CR_TLBMISC], CR_TLBMISC, WAY);
+ int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN);
+ int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID);
+ int g = FIELD_EX32(v, CR_TLBACC, G);
+ int valid = FIELD_EX32(vpn, CR_TLBACC, PFN) < 0xC0000;
Nios2TLBEntry *entry =
&env->mmu.tlb[(way * cpu->tlb_num_ways) +
(vpn & env->mmu.tlb_entry_mask)];
uint32_t newTag = (vpn << 12) | (g << 11) | (valid << 10) | pid;
uint32_t newData = v & (CR_TLBACC_C | CR_TLBACC_R | CR_TLBACC_W |
- CR_TLBACC_X | CR_TLBACC_PFN_MASK);
+ CR_TLBACC_X | R_CR_TLBACC_PFN_MASK);
if ((entry->tag != newTag) || (entry->data != newData)) {
if (entry->tag & (1 << 10)) {
@@ -117,10 +117,9 @@ void helper_mmu_write_tlbacc(CPUNios2State *env, uint32_t v)
entry->data = newData;
}
/* Auto-increment tlbmisc.WAY */
- env->regs[CR_TLBMISC] =
- (env->regs[CR_TLBMISC] & ~CR_TLBMISC_WAY_MASK) |
- (((way + 1) & (cpu->tlb_num_ways - 1)) <<
- CR_TLBMISC_WAY_SHIFT);
+ env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC],
+ CR_TLBMISC, WAY,
+ (way + 1) & (cpu->tlb_num_ways - 1));
}
/* Writes to TLBACC don't change the read-back value */
@@ -130,40 +129,41 @@ void helper_mmu_write_tlbacc(CPUNios2State *env, uint32_t v)
void helper_mmu_write_tlbmisc(CPUNios2State *env, uint32_t v)
{
Nios2CPU *cpu = env_archcpu(env);
+ uint32_t new_pid = FIELD_EX32(v, CR_TLBMISC, PID);
+ uint32_t old_pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID);
+ uint32_t way = FIELD_EX32(v, CR_TLBMISC, WAY);
- trace_nios2_mmu_write_tlbmisc(v >> CR_TLBMISC_WAY_SHIFT,
+ trace_nios2_mmu_write_tlbmisc(way,
(v & CR_TLBMISC_RD) ? 'R' : '.',
- (v & CR_TLBMISC_WR) ? 'W' : '.',
+ (v & CR_TLBMISC_WE) ? 'W' : '.',
(v & CR_TLBMISC_DBL) ? '2' : '.',
(v & CR_TLBMISC_BAD) ? 'B' : '.',
(v & CR_TLBMISC_PERM) ? 'P' : '.',
(v & CR_TLBMISC_D) ? 'D' : '.',
- (v & CR_TLBMISC_PID_MASK) >> 4);
+ new_pid);
- if ((v & CR_TLBMISC_PID_MASK) !=
- (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK)) {
- mmu_flush_pid(env, (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >>
- CR_TLBMISC_PID_SHIFT);
+ if (new_pid != old_pid) {
+ mmu_flush_pid(env, old_pid);
}
+
/* if tlbmisc.RD == 1 then trigger a TLB read on writes to TLBMISC */
if (v & CR_TLBMISC_RD) {
- int way = (v >> CR_TLBMISC_WAY_SHIFT);
- int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
+ int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN);
Nios2TLBEntry *entry =
&env->mmu.tlb[(way * cpu->tlb_num_ways) +
(vpn & env->mmu.tlb_entry_mask)];
- env->regs[CR_TLBACC] &= CR_TLBACC_IGN_MASK;
- env->regs[CR_TLBACC] |= entry->data;
- env->regs[CR_TLBACC] |= (entry->tag & (1 << 11)) ? CR_TLBACC_G : 0;
- env->regs[CR_TLBMISC] =
- (v & ~CR_TLBMISC_PID_MASK) |
- ((entry->tag & ((1 << cpu->pid_num_bits) - 1)) <<
- CR_TLBMISC_PID_SHIFT);
- env->regs[CR_PTEADDR] &= ~CR_PTEADDR_VPN_MASK;
- env->regs[CR_PTEADDR] |= (entry->tag >> 12) << CR_PTEADDR_VPN_SHIFT;
+ env->ctrl[CR_TLBACC] &= R_CR_TLBACC_IG_MASK;
+ env->ctrl[CR_TLBACC] |= entry->data;
+ env->ctrl[CR_TLBACC] |= (entry->tag & (1 << 11)) ? CR_TLBACC_G : 0;
+ env->ctrl[CR_TLBMISC] = FIELD_DP32(v, CR_TLBMISC, PID,
+ entry->tag &
+ ((1 << cpu->pid_num_bits) - 1));
+ env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR],
+ CR_PTEADDR, VPN,
+ entry->tag >> TARGET_PAGE_BITS);
} else {
- env->regs[CR_TLBMISC] = v;
+ env->ctrl[CR_TLBMISC] = v;
}
env->mmu.tlbmisc_wr = v;
@@ -171,12 +171,12 @@ void helper_mmu_write_tlbmisc(CPUNios2State *env, uint32_t v)
void helper_mmu_write_pteaddr(CPUNios2State *env, uint32_t v)
{
- trace_nios2_mmu_write_pteaddr(v >> CR_PTEADDR_PTBASE_SHIFT,
- (v & CR_PTEADDR_VPN_MASK) >> CR_PTEADDR_VPN_SHIFT);
+ trace_nios2_mmu_write_pteaddr(FIELD_EX32(v, CR_PTEADDR, PTBASE),
+ FIELD_EX32(v, CR_PTEADDR, VPN));
/* Writes to PTEADDR don't change the read-back VPN value */
- env->regs[CR_PTEADDR] = (v & ~CR_PTEADDR_VPN_MASK) |
- (env->regs[CR_PTEADDR] & CR_PTEADDR_VPN_MASK);
+ env->ctrl[CR_PTEADDR] = ((v & ~R_CR_PTEADDR_VPN_MASK) |
+ (env->ctrl[CR_PTEADDR] & R_CR_PTEADDR_VPN_MASK));
env->mmu.pteaddr_wr = v;
}
@@ -207,7 +207,7 @@ void dump_mmu(CPUNios2State *env)
entry->tag >> 12,
entry->tag & ((1 << cpu->pid_num_bits) - 1),
(entry->tag & (1 << 11)) ? 'G' : '-',
- entry->data & CR_TLBACC_PFN_MASK,
+ FIELD_EX32(entry->data, CR_TLBACC, PFN),
(entry->data & CR_TLBACC_C) ? 'C' : '-',
(entry->data & CR_TLBACC_R) ? 'R' : '-',
(entry->data & CR_TLBACC_W) ? 'W' : '-',
diff --git a/target/nios2/op_helper.c b/target/nios2/op_helper.c
index caa885f7b4..2e30d0a908 100644
--- a/target/nios2/op_helper.c
+++ b/target/nios2/op_helper.c
@@ -30,3 +30,91 @@ void helper_raise_exception(CPUNios2State *env, uint32_t index)
cs->exception_index = index;
cpu_loop_exit(cs);
}
+
+void nios2_cpu_loop_exit_advance(CPUNios2State *env, uintptr_t retaddr)
+{
+ CPUState *cs = env_cpu(env);
+
+ /*
+ * Note that PC is advanced for all hardware exceptions.
+ * Do this here, rather than in restore_state_to_opc(),
+ * lest we affect QEMU internal exceptions, like EXCP_DEBUG.
+ */
+ cpu_restore_state(cs, retaddr, true);
+ env->pc += 4;
+ cpu_loop_exit(cs);
+}
+
+static void maybe_raise_div(CPUNios2State *env, uintptr_t ra)
+{
+ Nios2CPU *cpu = env_archcpu(env);
+ CPUState *cs = env_cpu(env);
+
+ if (cpu->diverr_present) {
+ cs->exception_index = EXCP_DIV;
+ nios2_cpu_loop_exit_advance(env, ra);
+ }
+}
+
+int32_t helper_divs(CPUNios2State *env, int32_t num, int32_t den)
+{
+ if (unlikely(den == 0) || unlikely(den == -1 && num == INT32_MIN)) {
+ maybe_raise_div(env, GETPC());
+ return num; /* undefined */
+ }
+ return num / den;
+}
+
+uint32_t helper_divu(CPUNios2State *env, uint32_t num, uint32_t den)
+{
+ if (unlikely(den == 0)) {
+ maybe_raise_div(env, GETPC());
+ return num; /* undefined */
+ }
+ return num / den;
+}
+
+#ifndef CONFIG_USER_ONLY
+void helper_eret(CPUNios2State *env, uint32_t new_status, uint32_t new_pc)
+{
+ Nios2CPU *cpu = env_archcpu(env);
+ CPUState *cs = env_cpu(env);
+
+ if (unlikely(new_pc & 3)) {
+ env->ctrl[CR_BADADDR] = new_pc;
+ cs->exception_index = EXCP_UNALIGND;
+ nios2_cpu_loop_exit_advance(env, GETPC());
+ }
+
+ /*
+ * None of estatus, bstatus, or sstatus have constraints on write;
+ * do not allow reserved fields in status to be set.
+ * When shadow registers are enabled, eret *does* restore CRS.
+ * Rather than testing eic_present to decide, mask CRS out of
+ * the set of readonly fields.
+ */
+ new_status &= cpu->cr_state[CR_STATUS].writable |
+ (cpu->cr_state[CR_STATUS].readonly & R_CR_STATUS_CRS_MASK);
+
+ env->ctrl[CR_STATUS] = new_status;
+ env->pc = new_pc;
+ nios2_update_crs(env);
+ cpu_loop_exit(cs);
+}
+
+/*
+ * RDPRS and WRPRS are implemented out of line so that if PRS == CRS,
+ * all of the tcg global temporaries are synced back to ENV.
+ */
+uint32_t helper_rdprs(CPUNios2State *env, uint32_t regno)
+{
+ unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS);
+ return env->shadow_regs[prs][regno];
+}
+
+void helper_wrprs(CPUNios2State *env, uint32_t regno, uint32_t val)
+{
+ unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS);
+ env->shadow_regs[prs][regno] = val;
+}
+#endif /* !CONFIG_USER_ONLY */
diff --git a/target/nios2/translate.c b/target/nios2/translate.c
index 89b97ef520..3a037a68cc 100644
--- a/target/nios2/translate.c
+++ b/target/nios2/translate.c
@@ -33,9 +33,9 @@
#include "exec/translator.h"
#include "qemu/qemu-print.h"
#include "exec/gen-icount.h"
+#include "semihosting/semihost.h"
/* is_jmp field values */
-#define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */
#define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */
#define INSTRUCTION_FLG(func, flags) { (func), (flags) }
@@ -52,32 +52,53 @@
#define INSN_R_TYPE 0x3A
/* I-Type instruction parsing */
+typedef struct {
+ uint8_t op;
+ union {
+ uint16_t u;
+ int16_t s;
+ } imm16;
+ uint8_t b;
+ uint8_t a;
+} InstrIType;
+
#define I_TYPE(instr, code) \
- struct { \
- uint8_t op; \
- union { \
- uint16_t u; \
- int16_t s; \
- } imm16; \
- uint8_t b; \
- uint8_t a; \
- } (instr) = { \
+ InstrIType (instr) = { \
.op = extract32((code), 0, 6), \
.imm16.u = extract32((code), 6, 16), \
.b = extract32((code), 22, 5), \
.a = extract32((code), 27, 5), \
}
+typedef target_ulong ImmFromIType(const InstrIType *);
+
+static target_ulong imm_unsigned(const InstrIType *i)
+{
+ return i->imm16.u;
+}
+
+static target_ulong imm_signed(const InstrIType *i)
+{
+ return i->imm16.s;
+}
+
+static target_ulong imm_shifted(const InstrIType *i)
+{
+ return i->imm16.u << 16;
+}
+
/* R-Type instruction parsing */
+typedef struct {
+ uint8_t op;
+ uint8_t imm5;
+ uint8_t opx;
+ uint8_t c;
+ uint8_t b;
+ uint8_t a;
+} InstrRType;
+
#define R_TYPE(instr, code) \
- struct { \
- uint8_t op; \
- uint8_t imm5; \
- uint8_t opx; \
- uint8_t c; \
- uint8_t b; \
- uint8_t a; \
- } (instr) = { \
+ InstrRType (instr) = { \
.op = extract32((code), 0, 6), \
.imm5 = extract32((code), 6, 5), \
.opx = extract32((code), 11, 6), \
@@ -87,23 +108,36 @@
}
/* J-Type instruction parsing */
+typedef struct {
+ uint8_t op;
+ uint32_t imm26;
+} InstrJType;
+
#define J_TYPE(instr, code) \
- struct { \
- uint8_t op; \
- uint32_t imm26; \
- } (instr) = { \
+ InstrJType (instr) = { \
.op = extract32((code), 0, 6), \
.imm26 = extract32((code), 6, 26), \
}
+typedef void GenFn2i(TCGv, TCGv, target_long);
+typedef void GenFn3(TCGv, TCGv, TCGv);
+typedef void GenFn4(TCGv, TCGv, TCGv, TCGv);
+
typedef struct DisasContext {
DisasContextBase base;
- TCGv_i32 zero;
target_ulong pc;
int mem_idx;
+ uint32_t tb_flags;
+ TCGv sink;
+ const ControlRegState *cr_state;
+ bool eic_present;
} DisasContext;
-static TCGv cpu_R[NUM_CORE_REGS];
+static TCGv cpu_R[NUM_GP_REGS];
+static TCGv cpu_pc;
+#ifndef CONFIG_USER_ONLY
+static TCGv cpu_crs_R[NUM_GP_REGS];
+#endif
typedef struct Nios2Instruction {
void (*handler)(DisasContext *dc, uint32_t code, uint32_t flags);
@@ -122,31 +156,57 @@ static uint8_t get_opxcode(uint32_t code)
return instr.opx;
}
-static TCGv load_zero(DisasContext *dc)
+static TCGv load_gpr(DisasContext *dc, unsigned reg)
{
- if (!dc->zero) {
- dc->zero = tcg_const_i32(0);
+ assert(reg < NUM_GP_REGS);
+
+ /*
+ * With shadow register sets, register r0 does not necessarily contain 0,
+ * but it is overwhelmingly likely that it does -- software is supposed
+ * to have set r0 to 0 in every shadow register set before use.
+ */
+ if (unlikely(reg == R_ZERO) && FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0)) {
+ return tcg_constant_tl(0);
+ }
+ if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) {
+ return cpu_R[reg];
}
- return dc->zero;
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
+ return cpu_crs_R[reg];
+#endif
}
-static TCGv load_gpr(DisasContext *dc, uint8_t reg)
+static TCGv dest_gpr(DisasContext *dc, unsigned reg)
{
- if (likely(reg != R_ZERO)) {
+ assert(reg < NUM_GP_REGS);
+
+ /*
+ * The spec for shadow register sets isn't clear, but we assume that
+ * writes to r0 are discarded regardless of CRS.
+ */
+ if (unlikely(reg == R_ZERO)) {
+ if (dc->sink == NULL) {
+ dc->sink = tcg_temp_new();
+ }
+ return dc->sink;
+ }
+ if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) {
return cpu_R[reg];
- } else {
- return load_zero(dc);
}
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
+ return cpu_crs_R[reg];
+#endif
}
-static void t_gen_helper_raise_exception(DisasContext *dc,
- uint32_t index)
+static void t_gen_helper_raise_exception(DisasContext *dc, uint32_t index)
{
- TCGv_i32 tmp = tcg_const_i32(index);
-
- tcg_gen_movi_tl(cpu_R[R_PC], dc->pc);
- gen_helper_raise_exception(cpu_env, tmp);
- tcg_temp_free_i32(tmp);
+ /* Note that PC is advanced for all hardware exceptions. */
+ tcg_gen_movi_tl(cpu_pc, dc->base.pc_next);
+ gen_helper_raise_exception(cpu_env, tcg_constant_i32(index));
dc->base.is_jmp = DISAS_NORETURN;
}
@@ -156,12 +216,36 @@ static void gen_goto_tb(DisasContext *dc, int n, uint32_t dest)
if (translator_use_goto_tb(&dc->base, dest)) {
tcg_gen_goto_tb(n);
- tcg_gen_movi_tl(cpu_R[R_PC], dest);
+ tcg_gen_movi_tl(cpu_pc, dest);
tcg_gen_exit_tb(tb, n);
} else {
- tcg_gen_movi_tl(cpu_R[R_PC], dest);
- tcg_gen_exit_tb(NULL, 0);
+ tcg_gen_movi_tl(cpu_pc, dest);
+ tcg_gen_lookup_and_goto_ptr();
}
+ dc->base.is_jmp = DISAS_NORETURN;
+}
+
+static void gen_jumpr(DisasContext *dc, int regno, bool is_call)
+{
+ TCGLabel *l = gen_new_label();
+ TCGv test = tcg_temp_new();
+ TCGv dest = load_gpr(dc, regno);
+
+ tcg_gen_andi_tl(test, dest, 3);
+ tcg_gen_brcondi_tl(TCG_COND_NE, test, 0, l);
+ tcg_temp_free(test);
+
+ tcg_gen_mov_tl(cpu_pc, dest);
+ if (is_call) {
+ tcg_gen_movi_tl(dest_gpr(dc, R_RA), dc->base.pc_next);
+ }
+ tcg_gen_lookup_and_goto_ptr();
+
+ gen_set_label(l);
+ tcg_gen_st_tl(dest, cpu_env, offsetof(CPUNios2State, ctrl[CR_BADADDR]));
+ t_gen_helper_raise_exception(dc, EXCP_UNALIGND);
+
+ dc->base.is_jmp = DISAS_NORETURN;
}
static void gen_excp(DisasContext *dc, uint32_t code, uint32_t flags)
@@ -169,12 +253,14 @@ static void gen_excp(DisasContext *dc, uint32_t code, uint32_t flags)
t_gen_helper_raise_exception(dc, flags);
}
-static void gen_check_supervisor(DisasContext *dc)
+static bool gen_check_supervisor(DisasContext *dc)
{
- if (dc->base.tb->flags & CR_STATUS_U) {
+ if (FIELD_EX32(dc->tb_flags, TBFLAGS, U)) {
/* CPU in user mode, privileged instruction called, stop. */
t_gen_helper_raise_exception(dc, EXCP_SUPERI);
+ return false;
}
+ return true;
}
/*
@@ -193,12 +279,11 @@ static void jmpi(DisasContext *dc, uint32_t code, uint32_t flags)
{
J_TYPE(instr, code);
gen_goto_tb(dc, 0, (dc->pc & 0xF0000000) | (instr.imm26 << 2));
- dc->base.is_jmp = DISAS_NORETURN;
}
static void call(DisasContext *dc, uint32_t code, uint32_t flags)
{
- tcg_gen_movi_tl(cpu_R[R_RA], dc->base.pc_next);
+ tcg_gen_movi_tl(dest_gpr(dc, R_RA), dc->base.pc_next);
jmpi(dc, code, flags);
}
@@ -211,27 +296,10 @@ static void gen_ldx(DisasContext *dc, uint32_t code, uint32_t flags)
I_TYPE(instr, code);
TCGv addr = tcg_temp_new();
- TCGv data;
-
- /*
- * WARNING: Loads into R_ZERO are ignored, but we must generate the
- * memory access itself to emulate the CPU precisely. Load
- * from a protected page to R_ZERO will cause SIGSEGV on
- * the Nios2 CPU.
- */
- if (likely(instr.b != R_ZERO)) {
- data = cpu_R[instr.b];
- } else {
- data = tcg_temp_new();
- }
+ TCGv data = dest_gpr(dc, instr.b);
tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s);
tcg_gen_qemu_ld_tl(data, addr, dc->mem_idx, flags);
-
- if (unlikely(instr.b == R_ZERO)) {
- tcg_temp_free(data);
- }
-
tcg_temp_free(addr);
}
@@ -253,7 +321,6 @@ static void br(DisasContext *dc, uint32_t code, uint32_t flags)
I_TYPE(instr, code);
gen_goto_tb(dc, 0, dc->base.pc_next + (instr.imm16.s & -4));
- dc->base.is_jmp = DISAS_NORETURN;
}
static void gen_bxx(DisasContext *dc, uint32_t code, uint32_t flags)
@@ -261,48 +328,86 @@ static void gen_bxx(DisasContext *dc, uint32_t code, uint32_t flags)
I_TYPE(instr, code);
TCGLabel *l1 = gen_new_label();
- tcg_gen_brcond_tl(flags, cpu_R[instr.a], cpu_R[instr.b], l1);
+ tcg_gen_brcond_tl(flags, load_gpr(dc, instr.a), load_gpr(dc, instr.b), l1);
gen_goto_tb(dc, 0, dc->base.pc_next);
gen_set_label(l1);
gen_goto_tb(dc, 1, dc->base.pc_next + (instr.imm16.s & -4));
- dc->base.is_jmp = DISAS_NORETURN;
}
/* Comparison instructions */
-#define gen_i_cmpxx(fname, op3) \
-static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
-{ \
- I_TYPE(instr, (code)); \
- tcg_gen_setcondi_tl(flags, cpu_R[instr.b], cpu_R[instr.a], (op3)); \
+static void do_i_cmpxx(DisasContext *dc, uint32_t insn,
+ TCGCond cond, ImmFromIType *imm)
+{
+ I_TYPE(instr, insn);
+ tcg_gen_setcondi_tl(cond, dest_gpr(dc, instr.b),
+ load_gpr(dc, instr.a), imm(&instr));
}
-gen_i_cmpxx(gen_cmpxxsi, instr.imm16.s)
-gen_i_cmpxx(gen_cmpxxui, instr.imm16.u)
+#define gen_i_cmpxx(fname, imm) \
+ static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
+ { do_i_cmpxx(dc, code, flags, imm); }
+
+gen_i_cmpxx(gen_cmpxxsi, imm_signed)
+gen_i_cmpxx(gen_cmpxxui, imm_unsigned)
/* Math/logic instructions */
-#define gen_i_math_logic(fname, insn, resimm, op3) \
-static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
-{ \
- I_TYPE(instr, (code)); \
- if (unlikely(instr.b == R_ZERO)) { /* Store to R_ZERO is ignored */ \
- return; \
- } else if (instr.a == R_ZERO) { /* MOVxI optimizations */ \
- tcg_gen_movi_tl(cpu_R[instr.b], (resimm) ? (op3) : 0); \
- } else { \
- tcg_gen_##insn##_tl(cpu_R[instr.b], cpu_R[instr.a], (op3)); \
- } \
-}
-
-gen_i_math_logic(addi, addi, 1, instr.imm16.s)
-gen_i_math_logic(muli, muli, 0, instr.imm16.s)
-
-gen_i_math_logic(andi, andi, 0, instr.imm16.u)
-gen_i_math_logic(ori, ori, 1, instr.imm16.u)
-gen_i_math_logic(xori, xori, 1, instr.imm16.u)
-
-gen_i_math_logic(andhi, andi, 0, instr.imm16.u << 16)
-gen_i_math_logic(orhi , ori, 1, instr.imm16.u << 16)
-gen_i_math_logic(xorhi, xori, 1, instr.imm16.u << 16)
+static void do_i_math_logic(DisasContext *dc, uint32_t insn,
+ GenFn2i *fn, ImmFromIType *imm,
+ bool x_op_0_eq_x)
+{
+ I_TYPE(instr, insn);
+ target_ulong val;
+
+ if (unlikely(instr.b == R_ZERO)) {
+ /* Store to R_ZERO is ignored -- this catches the canonical NOP. */
+ return;
+ }
+
+ val = imm(&instr);
+
+ if (instr.a == R_ZERO && FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0)) {
+ /* This catches the canonical expansions of movi and movhi. */
+ tcg_gen_movi_tl(dest_gpr(dc, instr.b), x_op_0_eq_x ? val : 0);
+ } else {
+ fn(dest_gpr(dc, instr.b), load_gpr(dc, instr.a), val);
+ }
+}
+
+#define gen_i_math_logic(fname, insn, x_op_0, imm) \
+ static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
+ { do_i_math_logic(dc, code, tcg_gen_##insn##_tl, imm, x_op_0); }
+
+gen_i_math_logic(addi, addi, 1, imm_signed)
+gen_i_math_logic(muli, muli, 0, imm_signed)
+
+gen_i_math_logic(andi, andi, 0, imm_unsigned)
+gen_i_math_logic(ori, ori, 1, imm_unsigned)
+gen_i_math_logic(xori, xori, 1, imm_unsigned)
+
+gen_i_math_logic(andhi, andi, 0, imm_shifted)
+gen_i_math_logic(orhi , ori, 1, imm_shifted)
+gen_i_math_logic(xorhi, xori, 1, imm_shifted)
+
+/* rB <- prs.rA + sigma(IMM16) */
+static void rdprs(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+ if (!dc->eic_present) {
+ t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
+ return;
+ }
+ if (!gen_check_supervisor(dc)) {
+ return;
+ }
+
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
+ I_TYPE(instr, code);
+ TCGv dest = dest_gpr(dc, instr.b);
+ gen_helper_rdprs(dest, cpu_env, tcg_constant_i32(instr.a));
+ tcg_gen_addi_tl(dest, dest, instr.imm16.s);
+#endif
+}
/* Prototype only, defined below */
static void handle_r_type_instr(DisasContext *dc, uint32_t code,
@@ -365,7 +470,7 @@ static const Nios2Instruction i_type_instructions[] = {
INSTRUCTION_FLG(gen_stx, MO_SL), /* stwio */
INSTRUCTION_FLG(gen_bxx, TCG_COND_LTU), /* bltu */
INSTRUCTION_FLG(gen_ldx, MO_UL), /* ldwio */
- INSTRUCTION_UNIMPLEMENTED(), /* rdprs */
+ INSTRUCTION(rdprs), /* rdprs */
INSTRUCTION_ILLEGAL(),
INSTRUCTION_FLG(handle_r_type_instr, 0), /* R-Type */
INSTRUCTION_NOP(), /* flushd */
@@ -384,26 +489,51 @@ static const Nios2Instruction i_type_instructions[] = {
*/
static void eret(DisasContext *dc, uint32_t code, uint32_t flags)
{
- tcg_gen_mov_tl(cpu_R[CR_STATUS], cpu_R[CR_ESTATUS]);
- tcg_gen_mov_tl(cpu_R[R_PC], cpu_R[R_EA]);
+ if (!gen_check_supervisor(dc)) {
+ return;
+ }
- dc->base.is_jmp = DISAS_JUMP;
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
+ if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) {
+ TCGv tmp = tcg_temp_new();
+ tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPUNios2State, ctrl[CR_ESTATUS]));
+ gen_helper_eret(cpu_env, tmp, load_gpr(dc, R_EA));
+ tcg_temp_free(tmp);
+ } else {
+ gen_helper_eret(cpu_env, load_gpr(dc, R_SSTATUS), load_gpr(dc, R_EA));
+ }
+ dc->base.is_jmp = DISAS_NORETURN;
+#endif
}
/* PC <- ra */
static void ret(DisasContext *dc, uint32_t code, uint32_t flags)
{
- tcg_gen_mov_tl(cpu_R[R_PC], cpu_R[R_RA]);
-
- dc->base.is_jmp = DISAS_JUMP;
+ gen_jumpr(dc, R_RA, false);
}
-/* PC <- ba */
+/*
+ * status <- bstatus
+ * PC <- ba
+ */
static void bret(DisasContext *dc, uint32_t code, uint32_t flags)
{
- tcg_gen_mov_tl(cpu_R[R_PC], cpu_R[R_BA]);
+ if (!gen_check_supervisor(dc)) {
+ return;
+ }
- dc->base.is_jmp = DISAS_JUMP;
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
+ TCGv tmp = tcg_temp_new();
+ tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPUNios2State, ctrl[CR_BSTATUS]));
+ gen_helper_eret(cpu_env, tmp, load_gpr(dc, R_BA));
+ tcg_temp_free(tmp);
+
+ dc->base.is_jmp = DISAS_NORETURN;
+#endif
}
/* PC <- rA */
@@ -411,9 +541,7 @@ static void jmp(DisasContext *dc, uint32_t code, uint32_t flags)
{
R_TYPE(instr, code);
- tcg_gen_mov_tl(cpu_R[R_PC], load_gpr(dc, instr.a));
-
- dc->base.is_jmp = DISAS_JUMP;
+ gen_jumpr(dc, instr.a, false);
}
/* rC <- PC + 4 */
@@ -421,9 +549,7 @@ static void nextpc(DisasContext *dc, uint32_t code, uint32_t flags)
{
R_TYPE(instr, code);
- if (likely(instr.c != R_ZERO)) {
- tcg_gen_movi_tl(cpu_R[instr.c], dc->base.pc_next);
- }
+ tcg_gen_movi_tl(dest_gpr(dc, instr.c), dc->base.pc_next);
}
/*
@@ -434,24 +560,29 @@ static void callr(DisasContext *dc, uint32_t code, uint32_t flags)
{
R_TYPE(instr, code);
- tcg_gen_mov_tl(cpu_R[R_PC], load_gpr(dc, instr.a));
- tcg_gen_movi_tl(cpu_R[R_RA], dc->base.pc_next);
-
- dc->base.is_jmp = DISAS_JUMP;
+ gen_jumpr(dc, instr.a, true);
}
/* rC <- ctlN */
static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags)
{
- R_TYPE(instr, code);
+ if (!gen_check_supervisor(dc)) {
+ return;
+ }
- gen_check_supervisor(dc);
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
+ R_TYPE(instr, code);
+ TCGv t1, t2, dest = dest_gpr(dc, instr.c);
- if (unlikely(instr.c == R_ZERO)) {
+ /* Reserved registers read as zero. */
+ if (nios2_cr_reserved(&dc->cr_state[instr.imm5])) {
+ tcg_gen_movi_tl(dest, 0);
return;
}
- switch (instr.imm5 + CR_BASE) {
+ switch (instr.imm5) {
case CR_IPENDING:
/*
* The value of the ipending register is synthetic.
@@ -461,24 +592,44 @@ static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags)
* must perform the AND here, and anywhere else we need the
* guest value of ipending.
*/
- tcg_gen_and_tl(cpu_R[instr.c], cpu_R[CR_IPENDING], cpu_R[CR_IENABLE]);
+ t1 = tcg_temp_new();
+ t2 = tcg_temp_new();
+ tcg_gen_ld_tl(t1, cpu_env, offsetof(CPUNios2State, ctrl[CR_IPENDING]));
+ tcg_gen_ld_tl(t2, cpu_env, offsetof(CPUNios2State, ctrl[CR_IENABLE]));
+ tcg_gen_and_tl(dest, t1, t2);
+ tcg_temp_free(t1);
+ tcg_temp_free(t2);
break;
default:
- tcg_gen_mov_tl(cpu_R[instr.c], cpu_R[instr.imm5 + CR_BASE]);
+ tcg_gen_ld_tl(dest, cpu_env,
+ offsetof(CPUNios2State, ctrl[instr.imm5]));
break;
}
+#endif
}
/* ctlN <- rA */
static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags)
{
- gen_check_supervisor(dc);
+ if (!gen_check_supervisor(dc)) {
+ return;
+ }
-#ifndef CONFIG_USER_ONLY
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
R_TYPE(instr, code);
TCGv v = load_gpr(dc, instr.a);
+ uint32_t ofs = offsetof(CPUNios2State, ctrl[instr.imm5]);
+ uint32_t wr = dc->cr_state[instr.imm5].writable;
+ uint32_t ro = dc->cr_state[instr.imm5].readonly;
+
+ /* Skip reserved or readonly registers. */
+ if (wr == 0) {
+ return;
+ }
- switch (instr.imm5 + CR_BASE) {
+ switch (instr.imm5) {
case CR_PTEADDR:
gen_helper_mmu_write_pteaddr(cpu_env, v);
break;
@@ -488,145 +639,163 @@ static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags)
case CR_TLBMISC:
gen_helper_mmu_write_tlbmisc(cpu_env, v);
break;
- case CR_IPENDING:
- /* ipending is read only, writes ignored. */
- break;
case CR_STATUS:
case CR_IENABLE:
/* If interrupts were enabled using WRCTL, trigger them. */
dc->base.is_jmp = DISAS_UPDATE;
/* fall through */
default:
- tcg_gen_mov_tl(cpu_R[instr.imm5 + CR_BASE], v);
+ if (wr == -1) {
+ /* The register is entirely writable. */
+ tcg_gen_st_tl(v, cpu_env, ofs);
+ } else {
+ /*
+ * The register is partially read-only or reserved:
+ * merge the value.
+ */
+ TCGv n = tcg_temp_new();
+
+ tcg_gen_andi_tl(n, v, wr);
+
+ if (ro != 0) {
+ TCGv o = tcg_temp_new();
+ tcg_gen_ld_tl(o, cpu_env, ofs);
+ tcg_gen_andi_tl(o, o, ro);
+ tcg_gen_or_tl(n, n, o);
+ tcg_temp_free(o);
+ }
+
+ tcg_gen_st_tl(n, cpu_env, ofs);
+ tcg_temp_free(n);
+ }
break;
}
#endif
}
+/* prs.rC <- rA */
+static void wrprs(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+ if (!dc->eic_present) {
+ t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
+ return;
+ }
+ if (!gen_check_supervisor(dc)) {
+ return;
+ }
+
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
+ R_TYPE(instr, code);
+ gen_helper_wrprs(cpu_env, tcg_constant_i32(instr.c),
+ load_gpr(dc, instr.a));
+ /*
+ * The expected write to PRS[r0] is 0, from CRS[r0].
+ * If not, and CRS == PRS (which we cannot tell from here),
+ * we may now have a non-zero value in our current r0.
+ * By ending the TB, we re-evaluate tb_flags and find out.
+ */
+ if (instr.c == 0
+ && (instr.a != 0 || !FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0))) {
+ dc->base.is_jmp = DISAS_UPDATE;
+ }
+#endif
+}
+
/* Comparison instructions */
static void gen_cmpxx(DisasContext *dc, uint32_t code, uint32_t flags)
{
R_TYPE(instr, code);
- if (likely(instr.c != R_ZERO)) {
- tcg_gen_setcond_tl(flags, cpu_R[instr.c], cpu_R[instr.a],
- cpu_R[instr.b]);
- }
+ tcg_gen_setcond_tl(flags, dest_gpr(dc, instr.c),
+ load_gpr(dc, instr.a), load_gpr(dc, instr.b));
}
/* Math/logic instructions */
-#define gen_r_math_logic(fname, insn, op3) \
-static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
-{ \
- R_TYPE(instr, (code)); \
- if (likely(instr.c != R_ZERO)) { \
- tcg_gen_##insn(cpu_R[instr.c], load_gpr((dc), instr.a), (op3)); \
- } \
-}
-
-gen_r_math_logic(add, add_tl, load_gpr(dc, instr.b))
-gen_r_math_logic(sub, sub_tl, load_gpr(dc, instr.b))
-gen_r_math_logic(mul, mul_tl, load_gpr(dc, instr.b))
-
-gen_r_math_logic(and, and_tl, load_gpr(dc, instr.b))
-gen_r_math_logic(or, or_tl, load_gpr(dc, instr.b))
-gen_r_math_logic(xor, xor_tl, load_gpr(dc, instr.b))
-gen_r_math_logic(nor, nor_tl, load_gpr(dc, instr.b))
-
-gen_r_math_logic(srai, sari_tl, instr.imm5)
-gen_r_math_logic(srli, shri_tl, instr.imm5)
-gen_r_math_logic(slli, shli_tl, instr.imm5)
-gen_r_math_logic(roli, rotli_tl, instr.imm5)
-
-#define gen_r_mul(fname, insn) \
-static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
-{ \
- R_TYPE(instr, (code)); \
- if (likely(instr.c != R_ZERO)) { \
- TCGv t0 = tcg_temp_new(); \
- tcg_gen_##insn(t0, cpu_R[instr.c], \
- load_gpr(dc, instr.a), load_gpr(dc, instr.b)); \
- tcg_temp_free(t0); \
- } \
-}
-
-gen_r_mul(mulxss, muls2_tl)
-gen_r_mul(mulxuu, mulu2_tl)
-gen_r_mul(mulxsu, mulsu2_tl)
-
-#define gen_r_shift_s(fname, insn) \
-static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
-{ \
- R_TYPE(instr, (code)); \
- if (likely(instr.c != R_ZERO)) { \
- TCGv t0 = tcg_temp_new(); \
- tcg_gen_andi_tl(t0, load_gpr((dc), instr.b), 31); \
- tcg_gen_##insn(cpu_R[instr.c], load_gpr((dc), instr.a), t0); \
- tcg_temp_free(t0); \
- } \
-}
-
-gen_r_shift_s(sra, sar_tl)
-gen_r_shift_s(srl, shr_tl)
-gen_r_shift_s(sll, shl_tl)
-gen_r_shift_s(rol, rotl_tl)
-gen_r_shift_s(ror, rotr_tl)
+static void do_ri_math_logic(DisasContext *dc, uint32_t insn, GenFn2i *fn)
+{
+ R_TYPE(instr, insn);
+ fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), instr.imm5);
+}
-static void divs(DisasContext *dc, uint32_t code, uint32_t flags)
+static void do_rr_math_logic(DisasContext *dc, uint32_t insn, GenFn3 *fn)
{
- R_TYPE(instr, (code));
+ R_TYPE(instr, insn);
+ fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), load_gpr(dc, instr.b));
+}
- /* Stores into R_ZERO are ignored */
- if (unlikely(instr.c == R_ZERO)) {
- return;
- }
+#define gen_ri_math_logic(fname, insn) \
+ static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
+ { do_ri_math_logic(dc, code, tcg_gen_##insn##_tl); }
+
+#define gen_rr_math_logic(fname, insn) \
+ static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
+ { do_rr_math_logic(dc, code, tcg_gen_##insn##_tl); }
+
+gen_rr_math_logic(add, add)
+gen_rr_math_logic(sub, sub)
+gen_rr_math_logic(mul, mul)
- TCGv t0 = tcg_temp_new();
- TCGv t1 = tcg_temp_new();
- TCGv t2 = tcg_temp_new();
- TCGv t3 = tcg_temp_new();
+gen_rr_math_logic(and, and)
+gen_rr_math_logic(or, or)
+gen_rr_math_logic(xor, xor)
+gen_rr_math_logic(nor, nor)
- tcg_gen_ext32s_tl(t0, load_gpr(dc, instr.a));
- tcg_gen_ext32s_tl(t1, load_gpr(dc, instr.b));
- tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, INT_MIN);
- tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1);
- tcg_gen_and_tl(t2, t2, t3);
- tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0);
- tcg_gen_or_tl(t2, t2, t3);
- tcg_gen_movi_tl(t3, 0);
- tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1);
- tcg_gen_div_tl(cpu_R[instr.c], t0, t1);
- tcg_gen_ext32s_tl(cpu_R[instr.c], cpu_R[instr.c]);
+gen_ri_math_logic(srai, sari)
+gen_ri_math_logic(srli, shri)
+gen_ri_math_logic(slli, shli)
+gen_ri_math_logic(roli, rotli)
- tcg_temp_free(t3);
- tcg_temp_free(t2);
- tcg_temp_free(t1);
- tcg_temp_free(t0);
+static void do_rr_mul_high(DisasContext *dc, uint32_t insn, GenFn4 *fn)
+{
+ R_TYPE(instr, insn);
+ TCGv discard = tcg_temp_new();
+
+ fn(discard, dest_gpr(dc, instr.c),
+ load_gpr(dc, instr.a), load_gpr(dc, instr.b));
+ tcg_temp_free(discard);
}
-static void divu(DisasContext *dc, uint32_t code, uint32_t flags)
+#define gen_rr_mul_high(fname, insn) \
+ static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
+ { do_rr_mul_high(dc, code, tcg_gen_##insn##_tl); }
+
+gen_rr_mul_high(mulxss, muls2)
+gen_rr_mul_high(mulxuu, mulu2)
+gen_rr_mul_high(mulxsu, mulsu2)
+
+static void do_rr_shift(DisasContext *dc, uint32_t insn, GenFn3 *fn)
{
- R_TYPE(instr, (code));
+ R_TYPE(instr, insn);
+ TCGv sh = tcg_temp_new();
- /* Stores into R_ZERO are ignored */
- if (unlikely(instr.c == R_ZERO)) {
- return;
- }
+ tcg_gen_andi_tl(sh, load_gpr(dc, instr.b), 31);
+ fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), sh);
+ tcg_temp_free(sh);
+}
- TCGv t0 = tcg_temp_new();
- TCGv t1 = tcg_temp_new();
- TCGv t2 = tcg_const_tl(0);
- TCGv t3 = tcg_const_tl(1);
+#define gen_rr_shift(fname, insn) \
+ static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \
+ { do_rr_shift(dc, code, tcg_gen_##insn##_tl); }
- tcg_gen_ext32u_tl(t0, load_gpr(dc, instr.a));
- tcg_gen_ext32u_tl(t1, load_gpr(dc, instr.b));
- tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1);
- tcg_gen_divu_tl(cpu_R[instr.c], t0, t1);
- tcg_gen_ext32s_tl(cpu_R[instr.c], cpu_R[instr.c]);
+gen_rr_shift(sra, sar)
+gen_rr_shift(srl, shr)
+gen_rr_shift(sll, shl)
+gen_rr_shift(rol, rotl)
+gen_rr_shift(ror, rotr)
- tcg_temp_free(t3);
- tcg_temp_free(t2);
- tcg_temp_free(t1);
- tcg_temp_free(t0);
+static void divs(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+ R_TYPE(instr, (code));
+ gen_helper_divs(dest_gpr(dc, instr.c), cpu_env,
+ load_gpr(dc, instr.a), load_gpr(dc, instr.b));
+}
+
+static void divu(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+ R_TYPE(instr, (code));
+ gen_helper_divu(dest_gpr(dc, instr.c), cpu_env,
+ load_gpr(dc, instr.a), load_gpr(dc, instr.b));
}
static void trap(DisasContext *dc, uint32_t code, uint32_t flags)
@@ -644,6 +813,20 @@ static void trap(DisasContext *dc, uint32_t code, uint32_t flags)
t_gen_helper_raise_exception(dc, EXCP_TRAP);
}
+static void gen_break(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+#ifndef CONFIG_USER_ONLY
+ /* The semihosting instruction is "break 1". */
+ R_TYPE(instr, code);
+ if (semihosting_enabled() && instr.imm5 == 1) {
+ t_gen_helper_raise_exception(dc, EXCP_SEMIHOST);
+ return;
+ }
+#endif
+
+ t_gen_helper_raise_exception(dc, EXCP_BREAK);
+}
+
static const Nios2Instruction r_type_instructions[] = {
INSTRUCTION_ILLEGAL(),
INSTRUCTION(eret), /* eret */
@@ -665,7 +848,7 @@ static const Nios2Instruction r_type_instructions[] = {
INSTRUCTION_ILLEGAL(),
INSTRUCTION(slli), /* slli */
INSTRUCTION(sll), /* sll */
- INSTRUCTION_UNIMPLEMENTED(), /* wrprs */
+ INSTRUCTION(wrprs), /* wrprs */
INSTRUCTION_ILLEGAL(),
INSTRUCTION(or), /* or */
INSTRUCTION(mulxsu), /* mulxsu */
@@ -697,7 +880,7 @@ static const Nios2Instruction r_type_instructions[] = {
INSTRUCTION(add), /* add */
INSTRUCTION_ILLEGAL(),
INSTRUCTION_ILLEGAL(),
- INSTRUCTION_FLG(gen_excp, EXCP_BREAK), /* break */
+ INSTRUCTION(gen_break), /* break */
INSTRUCTION_ILLEGAL(),
INSTRUCTION(nop), /* nop */
INSTRUCTION_ILLEGAL(),
@@ -730,7 +913,7 @@ illegal_op:
t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
}
-static const char * const regnames[] = {
+static const char * const gr_regnames[NUM_GP_REGS] = {
"zero", "at", "r2", "r3",
"r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11",
@@ -739,16 +922,20 @@ static const char * const regnames[] = {
"r20", "r21", "r22", "r23",
"et", "bt", "gp", "sp",
"fp", "ea", "ba", "ra",
+};
+
+#ifndef CONFIG_USER_ONLY
+static const char * const cr_regnames[NUM_CR_REGS] = {
"status", "estatus", "bstatus", "ienable",
- "ipending", "cpuid", "reserved0", "exception",
+ "ipending", "cpuid", "res6", "exception",
"pteaddr", "tlbacc", "tlbmisc", "reserved1",
"badaddr", "config", "mpubase", "mpuacc",
- "reserved2", "reserved3", "reserved4", "reserved5",
- "reserved6", "reserved7", "reserved8", "reserved9",
- "reserved10", "reserved11", "reserved12", "reserved13",
- "reserved14", "reserved15", "reserved16", "reserved17",
- "rpc"
+ "res16", "res17", "res18", "res19",
+ "res20", "res21", "res22", "res23",
+ "res24", "res25", "res26", "res27",
+ "res28", "res29", "res30", "res31",
};
+#endif
#include "exec/gen-icount.h"
@@ -757,9 +944,13 @@ static void nios2_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
{
DisasContext *dc = container_of(dcbase, DisasContext, base);
CPUNios2State *env = cs->env_ptr;
+ Nios2CPU *cpu = env_archcpu(env);
int page_insns;
dc->mem_idx = cpu_mmu_index(env, false);
+ dc->cr_state = cpu->cr_state;
+ dc->tb_flags = dc->base.tb->flags;
+ dc->eic_present = cpu->eic_present;
/* Bound the number of insns to execute to those left on the page. */
page_insns = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4;
@@ -796,13 +987,13 @@ static void nios2_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
return;
}
- dc->zero = NULL;
+ dc->sink = NULL;
instr = &i_type_instructions[op];
instr->handler(dc, code, instr->flags);
- if (dc->zero) {
- tcg_temp_free(dc->zero);
+ if (dc->sink) {
+ tcg_temp_free(dc->sink);
}
}
@@ -813,14 +1004,12 @@ static void nios2_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
/* Indicate where the next block should start */
switch (dc->base.is_jmp) {
case DISAS_TOO_MANY:
- case DISAS_UPDATE:
- /* Save the current PC back into the CPU register */
- tcg_gen_movi_tl(cpu_R[R_PC], dc->base.pc_next);
- tcg_gen_exit_tb(NULL, 0);
+ gen_goto_tb(dc, 0, dc->base.pc_next);
break;
- case DISAS_JUMP:
- /* The jump will already have updated the PC register */
+ case DISAS_UPDATE:
+ /* Save the current PC, and return to the main loop. */
+ tcg_gen_movi_tl(cpu_pc, dc->base.pc_next);
tcg_gen_exit_tb(NULL, 0);
break;
@@ -861,41 +1050,67 @@ void nios2_cpu_dump_state(CPUState *cs, FILE *f, int flags)
CPUNios2State *env = &cpu->env;
int i;
- if (!env) {
- return;
- }
-
- qemu_fprintf(f, "IN: PC=%x %s\n",
- env->regs[R_PC], lookup_symbol(env->regs[R_PC]));
+ qemu_fprintf(f, "IN: PC=%x %s\n", env->pc, lookup_symbol(env->pc));
- for (i = 0; i < NUM_CORE_REGS; i++) {
- qemu_fprintf(f, "%9s=%8.8x ", regnames[i], env->regs[i]);
+ for (i = 0; i < NUM_GP_REGS; i++) {
+ qemu_fprintf(f, "%9s=%8.8x ", gr_regnames[i], env->regs[i]);
if ((i + 1) % 4 == 0) {
qemu_fprintf(f, "\n");
}
}
+
#if !defined(CONFIG_USER_ONLY)
- qemu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n",
- env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK,
- (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4,
- env->mmu.tlbacc_wr);
+ int j;
+
+ for (i = j = 0; i < NUM_CR_REGS; i++) {
+ if (!nios2_cr_reserved(&cpu->cr_state[i])) {
+ qemu_fprintf(f, "%9s=%8.8x ", cr_regnames[i], env->ctrl[i]);
+ if (++j % 4 == 0) {
+ qemu_fprintf(f, "\n");
+ }
+ }
+ }
+ if (j % 4 != 0) {
+ qemu_fprintf(f, "\n");
+ }
+ if (cpu->mmu_present) {
+ qemu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n",
+ env->mmu.pteaddr_wr & R_CR_PTEADDR_VPN_MASK,
+ FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID),
+ env->mmu.tlbacc_wr);
+ }
#endif
qemu_fprintf(f, "\n\n");
}
void nios2_tcg_init(void)
{
- int i;
+#ifndef CONFIG_USER_ONLY
+ TCGv_ptr crs = tcg_global_mem_new_ptr(cpu_env,
+ offsetof(CPUNios2State, regs), "crs");
- for (i = 0; i < NUM_CORE_REGS; i++) {
- cpu_R[i] = tcg_global_mem_new(cpu_env,
- offsetof(CPUNios2State, regs[i]),
- regnames[i]);
+ for (int i = 0; i < NUM_GP_REGS; i++) {
+ cpu_crs_R[i] = tcg_global_mem_new(crs, 4 * i, gr_regnames[i]);
}
+
+#define offsetof_regs0(N) offsetof(CPUNios2State, shadow_regs[0][N])
+#else
+#define offsetof_regs0(N) offsetof(CPUNios2State, regs[N])
+#endif
+
+ for (int i = 0; i < NUM_GP_REGS; i++) {
+ cpu_R[i] = tcg_global_mem_new(cpu_env, offsetof_regs0(i),
+ gr_regnames[i]);
+ }
+
+#undef offsetof_regs0
+
+ cpu_pc = tcg_global_mem_new(cpu_env,
+ offsetof(CPUNios2State, pc), "pc");
}
void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb,
target_ulong *data)
{
- env->regs[R_PC] = data[0];
+ env->pc = data[0];
}
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index ddda4906ff..0c774056c5 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -34,7 +34,12 @@
/* RISC-V CPU definitions */
-static const char riscv_exts[26] = "IEMAFDQCLBJTPVNSUHKORWXYZG";
+static const char riscv_single_letter_exts[] = "IEMAFDQCPVH";
+
+struct isa_ext_data {
+ const char *name;
+ bool enabled;
+};
const char * const riscv_int_regnames[] = {
"x0/zero", "x1/ra", "x2/sp", "x3/gp", "x4/tp", "x5/t0", "x6/t1",
@@ -150,7 +155,7 @@ static void riscv_any_cpu_init(Object *obj)
#elif defined(TARGET_RISCV64)
set_misa(env, MXL_RV64, RVI | RVM | RVA | RVF | RVD | RVC | RVU);
#endif
- set_priv_version(env, PRIV_VERSION_1_11_0);
+ set_priv_version(env, PRIV_VERSION_1_12_0);
}
#if defined(TARGET_RISCV64)
@@ -461,6 +466,10 @@ static void riscv_cpu_reset(DeviceState *dev)
set_default_nan_mode(1, &env->fp_status);
#ifndef CONFIG_USER_ONLY
+ if (riscv_feature(env, RISCV_FEATURE_DEBUG)) {
+ riscv_trigger_init(env);
+ }
+
if (kvm_enabled()) {
kvm_riscv_reset_vcpu(cpu);
}
@@ -503,7 +512,9 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
}
if (cpu->cfg.priv_spec) {
- if (!g_strcmp0(cpu->cfg.priv_spec, "v1.11.0")) {
+ if (!g_strcmp0(cpu->cfg.priv_spec, "v1.12.0")) {
+ priv_version = PRIV_VERSION_1_12_0;
+ } else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.11.0")) {
priv_version = PRIV_VERSION_1_11_0;
} else if (!g_strcmp0(cpu->cfg.priv_spec, "v1.10.0")) {
priv_version = PRIV_VERSION_1_10_0;
@@ -518,7 +529,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
if (priv_version) {
set_priv_version(env, priv_version);
} else if (!env->priv_ver) {
- set_priv_version(env, PRIV_VERSION_1_11_0);
+ set_priv_version(env, PRIV_VERSION_1_12_0);
}
if (cpu->cfg.mmu) {
@@ -541,6 +552,10 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
riscv_set_feature(env, RISCV_FEATURE_AIA);
}
+ if (cpu->cfg.debug) {
+ riscv_set_feature(env, RISCV_FEATURE_DEBUG);
+ }
+
set_resetvec(env, cpu->cfg.resetvec);
/* Validate that MISA_MXL is set properly. */
@@ -567,18 +582,18 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
if (cpu->cfg.ext_i && cpu->cfg.ext_e) {
error_setg(errp,
"I and E extensions are incompatible");
- return;
- }
+ return;
+ }
if (!cpu->cfg.ext_i && !cpu->cfg.ext_e) {
error_setg(errp,
"Either I or E extension must be set");
- return;
- }
+ return;
+ }
- if (cpu->cfg.ext_g && !(cpu->cfg.ext_i & cpu->cfg.ext_m &
- cpu->cfg.ext_a & cpu->cfg.ext_f &
- cpu->cfg.ext_d)) {
+ if (cpu->cfg.ext_g && !(cpu->cfg.ext_i & cpu->cfg.ext_m &
+ cpu->cfg.ext_a & cpu->cfg.ext_f &
+ cpu->cfg.ext_d)) {
warn_report("Setting G will also set IMAFD");
cpu->cfg.ext_i = true;
cpu->cfg.ext_m = true;
@@ -706,15 +721,23 @@ static void riscv_cpu_set_irq(void *opaque, int irq, int level)
case IRQ_VS_TIMER:
case IRQ_M_TIMER:
case IRQ_U_EXT:
- case IRQ_S_EXT:
case IRQ_VS_EXT:
case IRQ_M_EXT:
- if (kvm_enabled()) {
+ if (kvm_enabled()) {
kvm_riscv_set_irq(cpu, irq, level);
- } else {
+ } else {
riscv_cpu_update_mip(cpu, 1 << irq, BOOL_TO_MASK(level));
- }
+ }
break;
+ case IRQ_S_EXT:
+ if (kvm_enabled()) {
+ kvm_riscv_set_irq(cpu, irq, level);
+ } else {
+ env->external_seip = level;
+ riscv_cpu_update_mip(cpu, 1 << irq,
+ BOOL_TO_MASK(level | env->software_seip));
+ }
+ break;
default:
g_assert_not_reached();
}
@@ -780,6 +803,7 @@ static Property riscv_cpu_properties[] = {
DEFINE_PROP_BOOL("Zve64f", RISCVCPU, cfg.ext_zve64f, false),
DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
+ DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true),
DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec),
DEFINE_PROP_STRING("vext_spec", RISCVCPU, cfg.vext_spec),
@@ -865,6 +889,9 @@ static const struct TCGCPUOps riscv_tcg_ops = {
.do_interrupt = riscv_cpu_do_interrupt,
.do_transaction_failed = riscv_cpu_do_transaction_failed,
.do_unaligned_access = riscv_cpu_do_unaligned_access,
+ .debug_excp_handler = riscv_cpu_debug_excp_handler,
+ .debug_check_breakpoint = riscv_cpu_debug_check_breakpoint,
+ .debug_check_watchpoint = riscv_cpu_debug_check_watchpoint,
#endif /* !CONFIG_USER_ONLY */
};
@@ -898,18 +925,73 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data)
device_class_set_props(dc, riscv_cpu_properties);
}
+#define ISA_EDATA_ENTRY(name, prop) {#name, cpu->cfg.prop}
+
+static void riscv_isa_string_ext(RISCVCPU *cpu, char **isa_str, int max_str_len)
+{
+ char *old = *isa_str;
+ char *new = *isa_str;
+ int i;
+
+ /**
+ * Here are the ordering rules of extension naming defined by RISC-V
+ * specification :
+ * 1. All extensions should be separated from other multi-letter extensions
+ * by an underscore.
+ * 2. The first letter following the 'Z' conventionally indicates the most
+ * closely related alphabetical extension category, IMAFDQLCBKJTPVH.
+ * If multiple 'Z' extensions are named, they should be ordered first
+ * by category, then alphabetically within a category.
+ * 3. Standard supervisor-level extensions (starts with 'S') should be
+ * listed after standard unprivileged extensions. If multiple
+ * supervisor-level extensions are listed, they should be ordered
+ * alphabetically.
+ * 4. Non-standard extensions (starts with 'X') must be listed after all
+ * standard extensions. They must be separated from other multi-letter
+ * extensions by an underscore.
+ */
+ struct isa_ext_data isa_edata_arr[] = {
+ ISA_EDATA_ENTRY(zfh, ext_zfh),
+ ISA_EDATA_ENTRY(zfhmin, ext_zfhmin),
+ ISA_EDATA_ENTRY(zfinx, ext_zfinx),
+ ISA_EDATA_ENTRY(zhinx, ext_zhinx),
+ ISA_EDATA_ENTRY(zhinxmin, ext_zhinxmin),
+ ISA_EDATA_ENTRY(zdinx, ext_zdinx),
+ ISA_EDATA_ENTRY(zba, ext_zba),
+ ISA_EDATA_ENTRY(zbb, ext_zbb),
+ ISA_EDATA_ENTRY(zbc, ext_zbc),
+ ISA_EDATA_ENTRY(zbs, ext_zbs),
+ ISA_EDATA_ENTRY(zve32f, ext_zve32f),
+ ISA_EDATA_ENTRY(zve64f, ext_zve64f),
+ ISA_EDATA_ENTRY(svinval, ext_svinval),
+ ISA_EDATA_ENTRY(svnapot, ext_svnapot),
+ ISA_EDATA_ENTRY(svpbmt, ext_svpbmt),
+ };
+
+ for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) {
+ if (isa_edata_arr[i].enabled) {
+ new = g_strconcat(old, "_", isa_edata_arr[i].name, NULL);
+ g_free(old);
+ old = new;
+ }
+ }
+
+ *isa_str = new;
+}
+
char *riscv_isa_string(RISCVCPU *cpu)
{
int i;
- const size_t maxlen = sizeof("rv128") + sizeof(riscv_exts) + 1;
+ const size_t maxlen = sizeof("rv128") + sizeof(riscv_single_letter_exts);
char *isa_str = g_new(char, maxlen);
char *p = isa_str + snprintf(isa_str, maxlen, "rv%d", TARGET_LONG_BITS);
- for (i = 0; i < sizeof(riscv_exts); i++) {
- if (cpu->env.misa_ext & RV(riscv_exts[i])) {
- *p++ = qemu_tolower(riscv_exts[i]);
+ for (i = 0; i < sizeof(riscv_single_letter_exts) - 1; i++) {
+ if (cpu->env.misa_ext & RV(riscv_single_letter_exts[i])) {
+ *p++ = qemu_tolower(riscv_single_letter_exts[i]);
}
}
*p = '\0';
+ riscv_isa_string_ext(cpu, &isa_str, maxlen);
return isa_str;
}
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 72f1c9451e..34c22d5d3b 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -79,11 +79,16 @@ enum {
RISCV_FEATURE_PMP,
RISCV_FEATURE_EPMP,
RISCV_FEATURE_MISA,
- RISCV_FEATURE_AIA
+ RISCV_FEATURE_AIA,
+ RISCV_FEATURE_DEBUG
};
-#define PRIV_VERSION_1_10_0 0x00011000
-#define PRIV_VERSION_1_11_0 0x00011100
+/* Privileged specification version */
+enum {
+ PRIV_VERSION_1_10_0 = 0,
+ PRIV_VERSION_1_11_0,
+ PRIV_VERSION_1_12_0,
+};
#define VEXT_VERSION_1_00_0 0x00010000
@@ -102,6 +107,7 @@ typedef struct CPUArchState CPURISCVState;
#if !defined(CONFIG_USER_ONLY)
#include "pmp.h"
+#include "debug.h"
#endif
#define RV_VLEN_MAX 1024
@@ -173,6 +179,14 @@ struct CPUArchState {
uint64_t mstatus;
uint64_t mip;
+ /*
+ * MIP contains the software writable version of SEIP ORed with the
+ * external interrupt value. The MIP register is always up-to-date.
+ * To keep track of the current source, we also save booleans of the values
+ * here.
+ */
+ bool external_seip;
+ bool software_seip;
uint64_t miclaim;
@@ -267,9 +281,13 @@ struct CPUArchState {
pmp_table_t pmp_state;
target_ulong mseccfg;
+ /* trigger module */
+ target_ulong trigger_cur;
+ type2_trigger_t type2_trig[TRIGGER_TYPE2_NUM];
+
/* machine specific rdtime callback */
- uint64_t (*rdtime_fn)(uint32_t);
- uint32_t rdtime_fn_arg;
+ uint64_t (*rdtime_fn)(void *);
+ void *rdtime_fn_arg;
/* machine specific AIA ireg read-modify-write callback */
#define AIA_MAKE_IREG(__isel, __priv, __virt, __vgein, __xlen) \
@@ -300,6 +318,11 @@ struct CPUArchState {
target_ulong spmbase;
target_ulong upmmask;
target_ulong upmbase;
+
+ /* CSRs for execution enviornment configuration */
+ uint64_t menvcfg;
+ target_ulong senvcfg;
+ uint64_t henvcfg;
#endif
target_ulong cur_pmmask;
target_ulong cur_pmbase;
@@ -383,6 +406,7 @@ struct RISCVCPUConfig {
bool pmp;
bool epmp;
bool aia;
+ bool debug;
uint64_t resetvec;
};
@@ -474,8 +498,8 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env);
int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts);
uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value);
#define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */
-void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t),
- uint32_t arg);
+void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *),
+ void *arg);
void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv,
int (*rmw_fn)(void *arg,
target_ulong reg,
@@ -654,6 +678,8 @@ typedef struct {
riscv_csr_op_fn op;
riscv_csr_read128_fn read128;
riscv_csr_write128_fn write128;
+ /* The default priv spec version should be PRIV_VERSION_1_10_0 (i.e 0) */
+ uint32_t min_priv_ver;
} riscv_csr_operations;
/* CSR function table constants */
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 0fe01d7da5..4a9e4f7d09 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -148,6 +148,7 @@
#define CSR_MARCHID 0xf12
#define CSR_MIMPID 0xf13
#define CSR_MHARTID 0xf14
+#define CSR_MCONFIGPTR 0xf15
/* Machine Trap Setup */
#define CSR_MSTATUS 0x300
@@ -201,6 +202,9 @@
#define CSR_STVEC 0x105
#define CSR_SCOUNTEREN 0x106
+/* Supervisor Configuration CSRs */
+#define CSR_SENVCFG 0x10A
+
/* Supervisor Trap Handling */
#define CSR_SSCRATCH 0x140
#define CSR_SEPC 0x141
@@ -246,6 +250,10 @@
#define CSR_HTIMEDELTA 0x605
#define CSR_HTIMEDELTAH 0x615
+/* Hypervisor Configuration CSRs */
+#define CSR_HENVCFG 0x60A
+#define CSR_HENVCFGH 0x61A
+
/* Virtual CSRs */
#define CSR_VSSTATUS 0x200
#define CSR_VSIE 0x204
@@ -289,6 +297,10 @@
#define CSR_VSIEH 0x214
#define CSR_VSIPH 0x254
+/* Machine Configuration CSRs */
+#define CSR_MENVCFG 0x30A
+#define CSR_MENVCFGH 0x31A
+
/* Enhanced Physical Memory Protection (ePMP) */
#define CSR_MSECCFG 0x747
#define CSR_MSECCFGH 0x757
@@ -662,6 +674,34 @@ typedef enum RISCVException {
#define PM_EXT_CLEAN 0x00000002ULL
#define PM_EXT_DIRTY 0x00000003ULL
+/* Execution enviornment configuration bits */
+#define MENVCFG_FIOM BIT(0)
+#define MENVCFG_CBIE (3UL << 4)
+#define MENVCFG_CBCFE BIT(6)
+#define MENVCFG_CBZE BIT(7)
+#define MENVCFG_PBMTE (1ULL << 62)
+#define MENVCFG_STCE (1ULL << 63)
+
+/* For RV32 */
+#define MENVCFGH_PBMTE BIT(30)
+#define MENVCFGH_STCE BIT(31)
+
+#define SENVCFG_FIOM MENVCFG_FIOM
+#define SENVCFG_CBIE MENVCFG_CBIE
+#define SENVCFG_CBCFE MENVCFG_CBCFE
+#define SENVCFG_CBZE MENVCFG_CBZE
+
+#define HENVCFG_FIOM MENVCFG_FIOM
+#define HENVCFG_CBIE MENVCFG_CBIE
+#define HENVCFG_CBCFE MENVCFG_CBCFE
+#define HENVCFG_CBZE MENVCFG_CBZE
+#define HENVCFG_PBMTE MENVCFG_PBMTE
+#define HENVCFG_STCE MENVCFG_STCE
+
+/* For RV32 */
+#define HENVCFGH_PBMTE MENVCFGH_PBMTE
+#define HENVCFGH_STCE MENVCFGH_STCE
+
/* Offsets for every pair of control bits per each priv level */
#define XS_OFFSET 0ULL
#define U_OFFSET 2ULL
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 1c60fb2e80..e1aa4f2097 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -632,8 +632,8 @@ uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value)
return old;
}
-void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t),
- uint32_t arg)
+void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void *),
+ void *arg)
{
env->rdtime_fn = fn;
env->rdtime_fn_arg = arg;
@@ -1150,7 +1150,7 @@ void riscv_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
env->badaddr = addr;
env->two_stage_lookup = riscv_cpu_virt_enabled(env) ||
riscv_cpu_two_stage_lookup(mmu_idx);
- riscv_raise_exception(&cpu->env, cs->exception_index, retaddr);
+ cpu_loop_exit_restore(cs, retaddr);
}
void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
@@ -1175,7 +1175,7 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
env->badaddr = addr;
env->two_stage_lookup = riscv_cpu_virt_enabled(env) ||
riscv_cpu_two_stage_lookup(mmu_idx);
- riscv_raise_exception(env, cs->exception_index, retaddr);
+ cpu_loop_exit_restore(cs, retaddr);
}
bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
@@ -1311,7 +1311,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
first_stage_error,
riscv_cpu_virt_enabled(env) ||
riscv_cpu_two_stage_lookup(mmu_idx));
- riscv_raise_exception(env, cs->exception_index, retaddr);
+ cpu_loop_exit_restore(cs, retaddr);
}
return true;
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 341c2e6f23..6ba85e7b5d 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -290,6 +290,15 @@ static RISCVException epmp(CPURISCVState *env, int csrno)
return RISCV_EXCP_ILLEGAL_INST;
}
+
+static RISCVException debug(CPURISCVState *env, int csrno)
+{
+ if (riscv_feature(env, RISCV_FEATURE_DEBUG)) {
+ return RISCV_EXCP_NONE;
+ }
+
+ return RISCV_EXCP_ILLEGAL_INST;
+}
#endif
/* User Floating-Point CSRs */
@@ -1398,15 +1407,114 @@ static RISCVException write_mtval(CPURISCVState *env, int csrno,
return RISCV_EXCP_NONE;
}
+/* Execution environment configuration setup */
+static RISCVException read_menvcfg(CPURISCVState *env, int csrno,
+ target_ulong *val)
+{
+ *val = env->menvcfg;
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException write_menvcfg(CPURISCVState *env, int csrno,
+ target_ulong val)
+{
+ uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | MENVCFG_CBZE;
+
+ if (riscv_cpu_mxl(env) == MXL_RV64) {
+ mask |= MENVCFG_PBMTE | MENVCFG_STCE;
+ }
+ env->menvcfg = (env->menvcfg & ~mask) | (val & mask);
+
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException read_menvcfgh(CPURISCVState *env, int csrno,
+ target_ulong *val)
+{
+ *val = env->menvcfg >> 32;
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException write_menvcfgh(CPURISCVState *env, int csrno,
+ target_ulong val)
+{
+ uint64_t mask = MENVCFG_PBMTE | MENVCFG_STCE;
+ uint64_t valh = (uint64_t)val << 32;
+
+ env->menvcfg = (env->menvcfg & ~mask) | (valh & mask);
+
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException read_senvcfg(CPURISCVState *env, int csrno,
+ target_ulong *val)
+{
+ *val = env->senvcfg;
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException write_senvcfg(CPURISCVState *env, int csrno,
+ target_ulong val)
+{
+ uint64_t mask = SENVCFG_FIOM | SENVCFG_CBIE | SENVCFG_CBCFE | SENVCFG_CBZE;
+
+ env->senvcfg = (env->senvcfg & ~mask) | (val & mask);
+
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException read_henvcfg(CPURISCVState *env, int csrno,
+ target_ulong *val)
+{
+ *val = env->henvcfg;
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException write_henvcfg(CPURISCVState *env, int csrno,
+ target_ulong val)
+{
+ uint64_t mask = HENVCFG_FIOM | HENVCFG_CBIE | HENVCFG_CBCFE | HENVCFG_CBZE;
+
+ if (riscv_cpu_mxl(env) == MXL_RV64) {
+ mask |= HENVCFG_PBMTE | HENVCFG_STCE;
+ }
+
+ env->henvcfg = (env->henvcfg & ~mask) | (val & mask);
+
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException read_henvcfgh(CPURISCVState *env, int csrno,
+ target_ulong *val)
+{
+ *val = env->henvcfg >> 32;
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException write_henvcfgh(CPURISCVState *env, int csrno,
+ target_ulong val)
+{
+ uint64_t mask = HENVCFG_PBMTE | HENVCFG_STCE;
+ uint64_t valh = (uint64_t)val << 32;
+
+ env->henvcfg = (env->henvcfg & ~mask) | (valh & mask);
+
+ return RISCV_EXCP_NONE;
+}
+
static RISCVException rmw_mip64(CPURISCVState *env, int csrno,
uint64_t *ret_val,
uint64_t new_val, uint64_t wr_mask)
{
RISCVCPU *cpu = env_archcpu(env);
- /* Allow software control of delegable interrupts not claimed by hardware */
- uint64_t old_mip, mask = wr_mask & delegable_ints & ~env->miclaim;
+ uint64_t old_mip, mask = wr_mask & delegable_ints;
uint32_t gin;
+ if (mask & MIP_SEIP) {
+ env->software_seip = new_val & MIP_SEIP;
+ new_val |= env->external_seip * MIP_SEIP;
+ }
+
if (mask) {
old_mip = riscv_cpu_update_mip(cpu, mask, (new_val & mask));
} else {
@@ -2578,6 +2686,48 @@ static RISCVException write_pmpaddr(CPURISCVState *env, int csrno,
return RISCV_EXCP_NONE;
}
+static RISCVException read_tselect(CPURISCVState *env, int csrno,
+ target_ulong *val)
+{
+ *val = tselect_csr_read(env);
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException write_tselect(CPURISCVState *env, int csrno,
+ target_ulong val)
+{
+ tselect_csr_write(env, val);
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException read_tdata(CPURISCVState *env, int csrno,
+ target_ulong *val)
+{
+ /* return 0 in tdata1 to end the trigger enumeration */
+ if (env->trigger_cur >= TRIGGER_NUM && csrno == CSR_TDATA1) {
+ *val = 0;
+ return RISCV_EXCP_NONE;
+ }
+
+ if (!tdata_available(env, csrno - CSR_TDATA1)) {
+ return RISCV_EXCP_ILLEGAL_INST;
+ }
+
+ *val = tdata_csr_read(env, csrno - CSR_TDATA1);
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException write_tdata(CPURISCVState *env, int csrno,
+ target_ulong val)
+{
+ if (!tdata_available(env, csrno - CSR_TDATA1)) {
+ return RISCV_EXCP_ILLEGAL_INST;
+ }
+
+ tdata_csr_write(env, csrno - CSR_TDATA1, val);
+ return RISCV_EXCP_NONE;
+}
+
/*
* Functions to access Pointer Masking feature registers
* We have to check if current priv lvl could modify
@@ -2880,6 +3030,7 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env,
{
/* check privileges and return RISCV_EXCP_ILLEGAL_INST if check fails */
int read_only = get_field(csrno, 0xC00) == 3;
+ int csr_min_priv = csr_ops[csrno].min_priv_ver;
#if !defined(CONFIG_USER_ONLY)
int effective_priv = env->priv;
@@ -2912,6 +3063,10 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env,
return RISCV_EXCP_ILLEGAL_INST;
}
+ if (env->priv_ver < csr_min_priv) {
+ return RISCV_EXCP_ILLEGAL_INST;
+ }
+
return csr_ops[csrno].predicate(env, csrno);
}
@@ -3070,13 +3225,20 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_FRM] = { "frm", fs, read_frm, write_frm },
[CSR_FCSR] = { "fcsr", fs, read_fcsr, write_fcsr },
/* Vector CSRs */
- [CSR_VSTART] = { "vstart", vs, read_vstart, write_vstart },
- [CSR_VXSAT] = { "vxsat", vs, read_vxsat, write_vxsat },
- [CSR_VXRM] = { "vxrm", vs, read_vxrm, write_vxrm },
- [CSR_VCSR] = { "vcsr", vs, read_vcsr, write_vcsr },
- [CSR_VL] = { "vl", vs, read_vl },
- [CSR_VTYPE] = { "vtype", vs, read_vtype },
- [CSR_VLENB] = { "vlenb", vs, read_vlenb },
+ [CSR_VSTART] = { "vstart", vs, read_vstart, write_vstart,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_VXSAT] = { "vxsat", vs, read_vxsat, write_vxsat,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_VXRM] = { "vxrm", vs, read_vxrm, write_vxrm,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_VCSR] = { "vcsr", vs, read_vcsr, write_vcsr,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_VL] = { "vl", vs, read_vl,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_VTYPE] = { "vtype", vs, read_vtype,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_VLENB] = { "vlenb", vs, read_vlenb,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
/* User Timers and Counters */
[CSR_CYCLE] = { "cycle", ctr, read_instret },
[CSR_INSTRET] = { "instret", ctr, read_instret },
@@ -3103,6 +3265,8 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_MIMPID] = { "mimpid", any, read_zero },
[CSR_MHARTID] = { "mhartid", any, read_mhartid },
+ [CSR_MCONFIGPTR] = { "mconfigptr", any, read_zero,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
/* Machine Trap Setup */
[CSR_MSTATUS] = { "mstatus", any, read_mstatus, write_mstatus, NULL,
read_mstatus_i128 },
@@ -3149,6 +3313,18 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_MVIPH] = { "mviph", aia_any32, read_zero, write_ignore },
[CSR_MIPH] = { "miph", aia_any32, NULL, NULL, rmw_miph },
+ /* Execution environment configuration */
+ [CSR_MENVCFG] = { "menvcfg", any, read_menvcfg, write_menvcfg,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_MENVCFGH] = { "menvcfgh", any32, read_menvcfgh, write_menvcfgh,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_SENVCFG] = { "senvcfg", smode, read_senvcfg, write_senvcfg,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_HENVCFG] = { "henvcfg", hmode, read_henvcfg, write_henvcfg,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_HENVCFGH] = { "henvcfgh", hmode32, read_henvcfgh, write_henvcfgh,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+
/* Supervisor Trap Setup */
[CSR_SSTATUS] = { "sstatus", smode, read_sstatus, write_sstatus, NULL,
read_sstatus_i128 },
@@ -3185,33 +3361,58 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_SIEH] = { "sieh", aia_smode32, NULL, NULL, rmw_sieh },
[CSR_SIPH] = { "siph", aia_smode32, NULL, NULL, rmw_siph },
- [CSR_HSTATUS] = { "hstatus", hmode, read_hstatus, write_hstatus },
- [CSR_HEDELEG] = { "hedeleg", hmode, read_hedeleg, write_hedeleg },
- [CSR_HIDELEG] = { "hideleg", hmode, NULL, NULL, rmw_hideleg },
- [CSR_HVIP] = { "hvip", hmode, NULL, NULL, rmw_hvip },
- [CSR_HIP] = { "hip", hmode, NULL, NULL, rmw_hip },
- [CSR_HIE] = { "hie", hmode, NULL, NULL, rmw_hie },
- [CSR_HCOUNTEREN] = { "hcounteren", hmode, read_hcounteren, write_hcounteren },
- [CSR_HGEIE] = { "hgeie", hmode, read_hgeie, write_hgeie },
- [CSR_HTVAL] = { "htval", hmode, read_htval, write_htval },
- [CSR_HTINST] = { "htinst", hmode, read_htinst, write_htinst },
- [CSR_HGEIP] = { "hgeip", hmode, read_hgeip, NULL },
- [CSR_HGATP] = { "hgatp", hmode, read_hgatp, write_hgatp },
- [CSR_HTIMEDELTA] = { "htimedelta", hmode, read_htimedelta, write_htimedelta },
- [CSR_HTIMEDELTAH] = { "htimedeltah", hmode32, read_htimedeltah, write_htimedeltah },
-
- [CSR_VSSTATUS] = { "vsstatus", hmode, read_vsstatus, write_vsstatus },
- [CSR_VSIP] = { "vsip", hmode, NULL, NULL, rmw_vsip },
- [CSR_VSIE] = { "vsie", hmode, NULL, NULL, rmw_vsie },
- [CSR_VSTVEC] = { "vstvec", hmode, read_vstvec, write_vstvec },
- [CSR_VSSCRATCH] = { "vsscratch", hmode, read_vsscratch, write_vsscratch },
- [CSR_VSEPC] = { "vsepc", hmode, read_vsepc, write_vsepc },
- [CSR_VSCAUSE] = { "vscause", hmode, read_vscause, write_vscause },
- [CSR_VSTVAL] = { "vstval", hmode, read_vstval, write_vstval },
- [CSR_VSATP] = { "vsatp", hmode, read_vsatp, write_vsatp },
-
- [CSR_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2 },
- [CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst },
+ [CSR_HSTATUS] = { "hstatus", hmode, read_hstatus, write_hstatus,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_HEDELEG] = { "hedeleg", hmode, read_hedeleg, write_hedeleg,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_HIDELEG] = { "hideleg", hmode, NULL, NULL, rmw_hideleg,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_HVIP] = { "hvip", hmode, NULL, NULL, rmw_hvip,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_HIP] = { "hip", hmode, NULL, NULL, rmw_hip,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_HIE] = { "hie", hmode, NULL, NULL, rmw_hie,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_HCOUNTEREN] = { "hcounteren", hmode, read_hcounteren, write_hcounteren,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_HGEIE] = { "hgeie", hmode, read_hgeie, write_hgeie,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_HTVAL] = { "htval", hmode, read_htval, write_htval,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_HTINST] = { "htinst", hmode, read_htinst, write_htinst,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_HGEIP] = { "hgeip", hmode, read_hgeip,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_HGATP] = { "hgatp", hmode, read_hgatp, write_hgatp,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_HTIMEDELTA] = { "htimedelta", hmode, read_htimedelta, write_htimedelta,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_HTIMEDELTAH] = { "htimedeltah", hmode32, read_htimedeltah, write_htimedeltah,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+
+ [CSR_VSSTATUS] = { "vsstatus", hmode, read_vsstatus, write_vsstatus,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_VSIP] = { "vsip", hmode, NULL, NULL, rmw_vsip,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_VSIE] = { "vsie", hmode, NULL, NULL, rmw_vsie ,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_VSTVEC] = { "vstvec", hmode, read_vstvec, write_vstvec,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_VSSCRATCH] = { "vsscratch", hmode, read_vsscratch, write_vsscratch,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_VSEPC] = { "vsepc", hmode, read_vsepc, write_vsepc,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_VSCAUSE] = { "vscause", hmode, read_vscause, write_vscause,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_VSTVAL] = { "vstval", hmode, read_vstval, write_vstval,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_VSATP] = { "vsatp", hmode, read_vsatp, write_vsatp,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+
+ [CSR_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
+ [CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
/* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */
[CSR_HVIEN] = { "hvien", aia_hmode, read_zero, write_ignore },
@@ -3245,7 +3446,8 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_VSIPH] = { "vsiph", aia_hmode32, NULL, NULL, rmw_vsiph },
/* Physical Memory Protection */
- [CSR_MSECCFG] = { "mseccfg", epmp, read_mseccfg, write_mseccfg },
+ [CSR_MSECCFG] = { "mseccfg", epmp, read_mseccfg, write_mseccfg,
+ .min_priv_ver = PRIV_VERSION_1_12_0 },
[CSR_PMPCFG0] = { "pmpcfg0", pmp, read_pmpcfg, write_pmpcfg },
[CSR_PMPCFG1] = { "pmpcfg1", pmp, read_pmpcfg, write_pmpcfg },
[CSR_PMPCFG2] = { "pmpcfg2", pmp, read_pmpcfg, write_pmpcfg },
@@ -3267,6 +3469,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_PMPADDR14] = { "pmpaddr14", pmp, read_pmpaddr, write_pmpaddr },
[CSR_PMPADDR15] = { "pmpaddr15", pmp, read_pmpaddr, write_pmpaddr },
+ /* Debug CSRs */
+ [CSR_TSELECT] = { "tselect", debug, read_tselect, write_tselect },
+ [CSR_TDATA1] = { "tdata1", debug, read_tdata, write_tdata },
+ [CSR_TDATA2] = { "tdata2", debug, read_tdata, write_tdata },
+ [CSR_TDATA3] = { "tdata3", debug, read_tdata, write_tdata },
+
/* User Pointer Masking */
[CSR_UMTE] = { "umte", pointer_masking, read_umte, write_umte },
[CSR_UPMMASK] = { "upmmask", pointer_masking, read_upmmask, write_upmmask },
diff --git a/target/riscv/debug.c b/target/riscv/debug.c
new file mode 100644
index 0000000000..2f2a51c732
--- /dev/null
+++ b/target/riscv/debug.c
@@ -0,0 +1,441 @@
+/*
+ * QEMU RISC-V Native Debug Support
+ *
+ * Copyright (c) 2022 Wind River Systems, Inc.
+ *
+ * Author:
+ * Bin Meng <bin.meng@windriver.com>
+ *
+ * This provides the native debug support via the Trigger Module, as defined
+ * in the RISC-V Debug Specification:
+ * https://github.com/riscv/riscv-debug-spec/raw/master/riscv-debug-stable.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "cpu.h"
+#include "trace.h"
+#include "exec/exec-all.h"
+
+/*
+ * The following M-mode trigger CSRs are implemented:
+ *
+ * - tselect
+ * - tdata1
+ * - tdata2
+ * - tdata3
+ *
+ * We don't support writable 'type' field in the tdata1 register, so there is
+ * no need to implement the "tinfo" CSR.
+ *
+ * The following triggers are implemented:
+ *
+ * Index | Type | tdata mapping | Description
+ * ------+------+------------------------+------------
+ * 0 | 2 | tdata1, tdata2 | Address / Data Match
+ * 1 | 2 | tdata1, tdata2 | Address / Data Match
+ */
+
+/* tdata availability of a trigger */
+typedef bool tdata_avail[TDATA_NUM];
+
+static tdata_avail tdata_mapping[TRIGGER_NUM] = {
+ [TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = { true, true, false },
+};
+
+/* only breakpoint size 1/2/4/8 supported */
+static int access_size[SIZE_NUM] = {
+ [SIZE_ANY] = 0,
+ [SIZE_1B] = 1,
+ [SIZE_2B] = 2,
+ [SIZE_4B] = 4,
+ [SIZE_6B] = -1,
+ [SIZE_8B] = 8,
+ [6 ... 15] = -1,
+};
+
+static inline target_ulong trigger_type(CPURISCVState *env,
+ trigger_type_t type)
+{
+ target_ulong tdata1;
+
+ switch (riscv_cpu_mxl(env)) {
+ case MXL_RV32:
+ tdata1 = RV32_TYPE(type);
+ break;
+ case MXL_RV64:
+ tdata1 = RV64_TYPE(type);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ return tdata1;
+}
+
+bool tdata_available(CPURISCVState *env, int tdata_index)
+{
+ if (unlikely(tdata_index >= TDATA_NUM)) {
+ return false;
+ }
+
+ if (unlikely(env->trigger_cur >= TRIGGER_NUM)) {
+ return false;
+ }
+
+ return tdata_mapping[env->trigger_cur][tdata_index];
+}
+
+target_ulong tselect_csr_read(CPURISCVState *env)
+{
+ return env->trigger_cur;
+}
+
+void tselect_csr_write(CPURISCVState *env, target_ulong val)
+{
+ /* all target_ulong bits of tselect are implemented */
+ env->trigger_cur = val;
+}
+
+static target_ulong tdata1_validate(CPURISCVState *env, target_ulong val,
+ trigger_type_t t)
+{
+ uint32_t type, dmode;
+ target_ulong tdata1;
+
+ switch (riscv_cpu_mxl(env)) {
+ case MXL_RV32:
+ type = extract32(val, 28, 4);
+ dmode = extract32(val, 27, 1);
+ tdata1 = RV32_TYPE(t);
+ break;
+ case MXL_RV64:
+ type = extract64(val, 60, 4);
+ dmode = extract64(val, 59, 1);
+ tdata1 = RV64_TYPE(t);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (type != t) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "ignoring type write to tdata1 register\n");
+ }
+ if (dmode != 0) {
+ qemu_log_mask(LOG_UNIMP, "debug mode is not supported\n");
+ }
+
+ return tdata1;
+}
+
+static inline void warn_always_zero_bit(target_ulong val, target_ulong mask,
+ const char *msg)
+{
+ if (val & mask) {
+ qemu_log_mask(LOG_UNIMP, "%s bit is always zero\n", msg);
+ }
+}
+
+static uint32_t type2_breakpoint_size(CPURISCVState *env, target_ulong ctrl)
+{
+ uint32_t size, sizelo, sizehi = 0;
+
+ if (riscv_cpu_mxl(env) == MXL_RV64) {
+ sizehi = extract32(ctrl, 21, 2);
+ }
+ sizelo = extract32(ctrl, 16, 2);
+ size = (sizehi << 2) | sizelo;
+
+ return size;
+}
+
+static inline bool type2_breakpoint_enabled(target_ulong ctrl)
+{
+ bool mode = !!(ctrl & (TYPE2_U | TYPE2_S | TYPE2_M));
+ bool rwx = !!(ctrl & (TYPE2_LOAD | TYPE2_STORE | TYPE2_EXEC));
+
+ return mode && rwx;
+}
+
+static target_ulong type2_mcontrol_validate(CPURISCVState *env,
+ target_ulong ctrl)
+{
+ target_ulong val;
+ uint32_t size;
+
+ /* validate the generic part first */
+ val = tdata1_validate(env, ctrl, TRIGGER_TYPE_AD_MATCH);
+
+ /* validate unimplemented (always zero) bits */
+ warn_always_zero_bit(ctrl, TYPE2_MATCH, "match");
+ warn_always_zero_bit(ctrl, TYPE2_CHAIN, "chain");
+ warn_always_zero_bit(ctrl, TYPE2_ACTION, "action");
+ warn_always_zero_bit(ctrl, TYPE2_TIMING, "timing");
+ warn_always_zero_bit(ctrl, TYPE2_SELECT, "select");
+ warn_always_zero_bit(ctrl, TYPE2_HIT, "hit");
+
+ /* validate size encoding */
+ size = type2_breakpoint_size(env, ctrl);
+ if (access_size[size] == -1) {
+ qemu_log_mask(LOG_UNIMP, "access size %d is not supported, using SIZE_ANY\n",
+ size);
+ } else {
+ val |= (ctrl & TYPE2_SIZELO);
+ if (riscv_cpu_mxl(env) == MXL_RV64) {
+ val |= (ctrl & TYPE2_SIZEHI);
+ }
+ }
+
+ /* keep the mode and attribute bits */
+ val |= (ctrl & (TYPE2_U | TYPE2_S | TYPE2_M |
+ TYPE2_LOAD | TYPE2_STORE | TYPE2_EXEC));
+
+ return val;
+}
+
+static void type2_breakpoint_insert(CPURISCVState *env, target_ulong index)
+{
+ target_ulong ctrl = env->type2_trig[index].mcontrol;
+ target_ulong addr = env->type2_trig[index].maddress;
+ bool enabled = type2_breakpoint_enabled(ctrl);
+ CPUState *cs = env_cpu(env);
+ int flags = BP_CPU | BP_STOP_BEFORE_ACCESS;
+ uint32_t size;
+
+ if (!enabled) {
+ return;
+ }
+
+ if (ctrl & TYPE2_EXEC) {
+ cpu_breakpoint_insert(cs, addr, flags, &env->type2_trig[index].bp);
+ }
+
+ if (ctrl & TYPE2_LOAD) {
+ flags |= BP_MEM_READ;
+ }
+ if (ctrl & TYPE2_STORE) {
+ flags |= BP_MEM_WRITE;
+ }
+
+ if (flags & BP_MEM_ACCESS) {
+ size = type2_breakpoint_size(env, ctrl);
+ if (size != 0) {
+ cpu_watchpoint_insert(cs, addr, size, flags,
+ &env->type2_trig[index].wp);
+ } else {
+ cpu_watchpoint_insert(cs, addr, 8, flags,
+ &env->type2_trig[index].wp);
+ }
+ }
+}
+
+static void type2_breakpoint_remove(CPURISCVState *env, target_ulong index)
+{
+ CPUState *cs = env_cpu(env);
+
+ if (env->type2_trig[index].bp) {
+ cpu_breakpoint_remove_by_ref(cs, env->type2_trig[index].bp);
+ env->type2_trig[index].bp = NULL;
+ }
+
+ if (env->type2_trig[index].wp) {
+ cpu_watchpoint_remove_by_ref(cs, env->type2_trig[index].wp);
+ env->type2_trig[index].wp = NULL;
+ }
+}
+
+static target_ulong type2_reg_read(CPURISCVState *env,
+ target_ulong trigger_index, int tdata_index)
+{
+ uint32_t index = trigger_index - TRIGGER_TYPE2_IDX_0;
+ target_ulong tdata;
+
+ switch (tdata_index) {
+ case TDATA1:
+ tdata = env->type2_trig[index].mcontrol;
+ break;
+ case TDATA2:
+ tdata = env->type2_trig[index].maddress;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ return tdata;
+}
+
+static void type2_reg_write(CPURISCVState *env, target_ulong trigger_index,
+ int tdata_index, target_ulong val)
+{
+ uint32_t index = trigger_index - TRIGGER_TYPE2_IDX_0;
+ target_ulong new_val;
+
+ switch (tdata_index) {
+ case TDATA1:
+ new_val = type2_mcontrol_validate(env, val);
+ if (new_val != env->type2_trig[index].mcontrol) {
+ env->type2_trig[index].mcontrol = new_val;
+ type2_breakpoint_remove(env, index);
+ type2_breakpoint_insert(env, index);
+ }
+ break;
+ case TDATA2:
+ if (val != env->type2_trig[index].maddress) {
+ env->type2_trig[index].maddress = val;
+ type2_breakpoint_remove(env, index);
+ type2_breakpoint_insert(env, index);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ return;
+}
+
+typedef target_ulong (*tdata_read_func)(CPURISCVState *env,
+ target_ulong trigger_index,
+ int tdata_index);
+
+static tdata_read_func trigger_read_funcs[TRIGGER_NUM] = {
+ [TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = type2_reg_read,
+};
+
+typedef void (*tdata_write_func)(CPURISCVState *env,
+ target_ulong trigger_index,
+ int tdata_index,
+ target_ulong val);
+
+static tdata_write_func trigger_write_funcs[TRIGGER_NUM] = {
+ [TRIGGER_TYPE2_IDX_0 ... TRIGGER_TYPE2_IDX_1] = type2_reg_write,
+};
+
+target_ulong tdata_csr_read(CPURISCVState *env, int tdata_index)
+{
+ tdata_read_func read_func = trigger_read_funcs[env->trigger_cur];
+
+ return read_func(env, env->trigger_cur, tdata_index);
+}
+
+void tdata_csr_write(CPURISCVState *env, int tdata_index, target_ulong val)
+{
+ tdata_write_func write_func = trigger_write_funcs[env->trigger_cur];
+
+ return write_func(env, env->trigger_cur, tdata_index, val);
+}
+
+void riscv_cpu_debug_excp_handler(CPUState *cs)
+{
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+
+ if (cs->watchpoint_hit) {
+ if (cs->watchpoint_hit->flags & BP_CPU) {
+ cs->watchpoint_hit = NULL;
+ riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, 0);
+ }
+ } else {
+ if (cpu_breakpoint_test(cs, env->pc, BP_CPU)) {
+ riscv_raise_exception(env, RISCV_EXCP_BREAKPOINT, 0);
+ }
+ }
+}
+
+bool riscv_cpu_debug_check_breakpoint(CPUState *cs)
+{
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+ CPUBreakpoint *bp;
+ target_ulong ctrl;
+ target_ulong pc;
+ int i;
+
+ QTAILQ_FOREACH(bp, &cs->breakpoints, entry) {
+ for (i = 0; i < TRIGGER_TYPE2_NUM; i++) {
+ ctrl = env->type2_trig[i].mcontrol;
+ pc = env->type2_trig[i].maddress;
+
+ if ((ctrl & TYPE2_EXEC) && (bp->pc == pc)) {
+ /* check U/S/M bit against current privilege level */
+ if ((ctrl >> 3) & BIT(env->priv)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp)
+{
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+ target_ulong ctrl;
+ target_ulong addr;
+ int flags;
+ int i;
+
+ for (i = 0; i < TRIGGER_TYPE2_NUM; i++) {
+ ctrl = env->type2_trig[i].mcontrol;
+ addr = env->type2_trig[i].maddress;
+ flags = 0;
+
+ if (ctrl & TYPE2_LOAD) {
+ flags |= BP_MEM_READ;
+ }
+ if (ctrl & TYPE2_STORE) {
+ flags |= BP_MEM_WRITE;
+ }
+
+ if ((wp->flags & flags) && (wp->vaddr == addr)) {
+ /* check U/S/M bit against current privilege level */
+ if ((ctrl >> 3) & BIT(env->priv)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void riscv_trigger_init(CPURISCVState *env)
+{
+ target_ulong type2 = trigger_type(env, TRIGGER_TYPE_AD_MATCH);
+ int i;
+
+ /* type 2 triggers */
+ for (i = 0; i < TRIGGER_TYPE2_NUM; i++) {
+ /*
+ * type = TRIGGER_TYPE_AD_MATCH
+ * dmode = 0 (both debug and M-mode can write tdata)
+ * maskmax = 0 (unimplemented, always 0)
+ * sizehi = 0 (match against any size, RV64 only)
+ * hit = 0 (unimplemented, always 0)
+ * select = 0 (always 0, perform match on address)
+ * timing = 0 (always 0, trigger before instruction)
+ * sizelo = 0 (match against any size)
+ * action = 0 (always 0, raise a breakpoint exception)
+ * chain = 0 (unimplemented, always 0)
+ * match = 0 (always 0, when any compare value equals tdata2)
+ */
+ env->type2_trig[i].mcontrol = type2;
+ env->type2_trig[i].maddress = 0;
+ env->type2_trig[i].bp = NULL;
+ env->type2_trig[i].wp = NULL;
+ }
+}
diff --git a/target/riscv/debug.h b/target/riscv/debug.h
new file mode 100644
index 0000000000..27b9cac6b4
--- /dev/null
+++ b/target/riscv/debug.h
@@ -0,0 +1,114 @@
+/*
+ * QEMU RISC-V Native Debug Support
+ *
+ * Copyright (c) 2022 Wind River Systems, Inc.
+ *
+ * Author:
+ * Bin Meng <bin.meng@windriver.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RISCV_DEBUG_H
+#define RISCV_DEBUG_H
+
+/* trigger indexes implemented */
+enum {
+ TRIGGER_TYPE2_IDX_0 = 0,
+ TRIGGER_TYPE2_IDX_1,
+ TRIGGER_TYPE2_NUM,
+ TRIGGER_NUM = TRIGGER_TYPE2_NUM
+};
+
+/* register index of tdata CSRs */
+enum {
+ TDATA1 = 0,
+ TDATA2,
+ TDATA3,
+ TDATA_NUM
+};
+
+typedef enum {
+ TRIGGER_TYPE_NO_EXIST = 0, /* trigger does not exist */
+ TRIGGER_TYPE_AD_MATCH = 2, /* address/data match trigger */
+ TRIGGER_TYPE_INST_CNT = 3, /* instruction count trigger */
+ TRIGGER_TYPE_INT = 4, /* interrupt trigger */
+ TRIGGER_TYPE_EXCP = 5, /* exception trigger */
+ TRIGGER_TYPE_AD_MATCH6 = 6, /* new address/data match trigger */
+ TRIGGER_TYPE_EXT_SRC = 7, /* external source trigger */
+ TRIGGER_TYPE_UNAVAIL = 15 /* trigger exists, but unavailable */
+} trigger_type_t;
+
+typedef struct {
+ target_ulong mcontrol;
+ target_ulong maddress;
+ struct CPUBreakpoint *bp;
+ struct CPUWatchpoint *wp;
+} type2_trigger_t;
+
+/* tdata field masks */
+
+#define RV32_TYPE(t) ((uint32_t)(t) << 28)
+#define RV32_TYPE_MASK (0xf << 28)
+#define RV32_DMODE BIT(27)
+#define RV64_TYPE(t) ((uint64_t)(t) << 60)
+#define RV64_TYPE_MASK (0xfULL << 60)
+#define RV64_DMODE BIT_ULL(59)
+
+/* mcontrol field masks */
+
+#define TYPE2_LOAD BIT(0)
+#define TYPE2_STORE BIT(1)
+#define TYPE2_EXEC BIT(2)
+#define TYPE2_U BIT(3)
+#define TYPE2_S BIT(4)
+#define TYPE2_M BIT(6)
+#define TYPE2_MATCH (0xf << 7)
+#define TYPE2_CHAIN BIT(11)
+#define TYPE2_ACTION (0xf << 12)
+#define TYPE2_SIZELO (0x3 << 16)
+#define TYPE2_TIMING BIT(18)
+#define TYPE2_SELECT BIT(19)
+#define TYPE2_HIT BIT(20)
+#define TYPE2_SIZEHI (0x3 << 21) /* RV64 only */
+
+/* access size */
+enum {
+ SIZE_ANY = 0,
+ SIZE_1B,
+ SIZE_2B,
+ SIZE_4B,
+ SIZE_6B,
+ SIZE_8B,
+ SIZE_10B,
+ SIZE_12B,
+ SIZE_14B,
+ SIZE_16B,
+ SIZE_NUM = 16
+};
+
+bool tdata_available(CPURISCVState *env, int tdata_index);
+
+target_ulong tselect_csr_read(CPURISCVState *env);
+void tselect_csr_write(CPURISCVState *env, target_ulong val);
+
+target_ulong tdata_csr_read(CPURISCVState *env, int tdata_index);
+void tdata_csr_write(CPURISCVState *env, int tdata_index, target_ulong val);
+
+void riscv_cpu_debug_excp_handler(CPUState *cs);
+bool riscv_cpu_debug_check_breakpoint(CPUState *cs);
+bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp);
+
+void riscv_trigger_init(CPURISCVState *env);
+
+#endif /* RISCV_DEBUG_H */
diff --git a/target/riscv/helper.h b/target/riscv/helper.h
index 26bbab2fab..a669d0187b 100644
--- a/target/riscv/helper.h
+++ b/target/riscv/helper.h
@@ -1086,10 +1086,7 @@ DEF_HELPER_6(vcompress_vm_h, void, ptr, ptr, ptr, ptr, env, i32)
DEF_HELPER_6(vcompress_vm_w, void, ptr, ptr, ptr, ptr, env, i32)
DEF_HELPER_6(vcompress_vm_d, void, ptr, ptr, ptr, ptr, env, i32)
-DEF_HELPER_4(vmv1r_v, void, ptr, ptr, env, i32)
-DEF_HELPER_4(vmv2r_v, void, ptr, ptr, env, i32)
-DEF_HELPER_4(vmv4r_v, void, ptr, ptr, env, i32)
-DEF_HELPER_4(vmv8r_v, void, ptr, ptr, env, i32)
+DEF_HELPER_4(vmvr_v, void, ptr, ptr, env, i32)
DEF_HELPER_5(vzext_vf2_h, void, ptr, ptr, ptr, env, i32)
DEF_HELPER_5(vzext_vf2_w, void, ptr, ptr, ptr, env, i32)
diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc
index 8d675db9a2..90327509f7 100644
--- a/target/riscv/insn_trans/trans_rvv.c.inc
+++ b/target/riscv/insn_trans/trans_rvv.c.inc
@@ -1198,7 +1198,7 @@ GEN_LDST_WHOLE_TRANS(vs8r_v, 8, true)
static inline uint32_t MAXSZ(DisasContext *s)
{
int scale = s->lmul - 3;
- return scale < 0 ? s->cfg_ptr->vlen >> -scale : s->cfg_ptr->vlen << scale;
+ return s->cfg_ptr->vlen >> -scale;
}
static bool opivv_check(DisasContext *s, arg_rmrr *a)
@@ -3597,8 +3597,7 @@ static bool trans_vrgather_vx(DisasContext *s, arg_rmrr *a)
if (a->vm && s->vl_eq_vlmax) {
int scale = s->lmul - (s->sew + 3);
- int vlmax = scale < 0 ?
- s->cfg_ptr->vlen >> -scale : s->cfg_ptr->vlen << scale;
+ int vlmax = s->cfg_ptr->vlen >> -scale;
TCGv_i64 dest = tcg_temp_new_i64();
if (a->rs1 == 0) {
@@ -3630,8 +3629,7 @@ static bool trans_vrgather_vi(DisasContext *s, arg_rmrr *a)
if (a->vm && s->vl_eq_vlmax) {
int scale = s->lmul - (s->sew + 3);
- int vlmax = scale < 0 ?
- s->cfg_ptr->vlen >> -scale : s->cfg_ptr->vlen << scale;
+ int vlmax = s->cfg_ptr->vlen >> -scale;
if (a->rs1 >= vlmax) {
tcg_gen_gvec_dup_imm(MO_64, vreg_ofs(s, a->rd),
MAXSZ(s), MAXSZ(s), 0);
@@ -3697,7 +3695,7 @@ static bool trans_vcompress_vm(DisasContext *s, arg_r *a)
* Whole Vector Register Move Instructions ignore vtype and vl setting.
* Thus, we don't need to check vill bit. (Section 16.6)
*/
-#define GEN_VMV_WHOLE_TRANS(NAME, LEN, SEQ) \
+#define GEN_VMV_WHOLE_TRANS(NAME, LEN) \
static bool trans_##NAME(DisasContext *s, arg_##NAME * a) \
{ \
if (require_rvv(s) && \
@@ -3712,13 +3710,8 @@ static bool trans_##NAME(DisasContext *s, arg_##NAME * a) \
} else { \
TCGLabel *over = gen_new_label(); \
tcg_gen_brcondi_tl(TCG_COND_GEU, cpu_vstart, maxsz, over); \
- \
- static gen_helper_gvec_2_ptr * const fns[4] = { \
- gen_helper_vmv1r_v, gen_helper_vmv2r_v, \
- gen_helper_vmv4r_v, gen_helper_vmv8r_v, \
- }; \
tcg_gen_gvec_2_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, a->rs2), \
- cpu_env, maxsz, maxsz, 0, fns[SEQ]); \
+ cpu_env, maxsz, maxsz, 0, gen_helper_vmvr_v); \
mark_vs_dirty(s); \
gen_set_label(over); \
} \
@@ -3727,10 +3720,10 @@ static bool trans_##NAME(DisasContext *s, arg_##NAME * a) \
return false; \
}
-GEN_VMV_WHOLE_TRANS(vmv1r_v, 1, 0)
-GEN_VMV_WHOLE_TRANS(vmv2r_v, 2, 1)
-GEN_VMV_WHOLE_TRANS(vmv4r_v, 4, 2)
-GEN_VMV_WHOLE_TRANS(vmv8r_v, 8, 3)
+GEN_VMV_WHOLE_TRANS(vmv1r_v, 1)
+GEN_VMV_WHOLE_TRANS(vmv2r_v, 2)
+GEN_VMV_WHOLE_TRANS(vmv4r_v, 4)
+GEN_VMV_WHOLE_TRANS(vmv8r_v, 8)
static bool int_ext_check(DisasContext *s, arg_rmr *a, uint8_t div)
{
diff --git a/target/riscv/machine.c b/target/riscv/machine.c
index 5178b3fec9..2a437b29a1 100644
--- a/target/riscv/machine.c
+++ b/target/riscv/machine.c
@@ -216,7 +216,38 @@ static const VMStateDescription vmstate_kvmtimer = {
VMSTATE_UINT64(env.kvm_timer_time, RISCVCPU),
VMSTATE_UINT64(env.kvm_timer_compare, RISCVCPU),
VMSTATE_UINT64(env.kvm_timer_state, RISCVCPU),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static bool debug_needed(void *opaque)
+{
+ RISCVCPU *cpu = opaque;
+ CPURISCVState *env = &cpu->env;
+
+ return riscv_feature(env, RISCV_FEATURE_DEBUG);
+}
+
+static const VMStateDescription vmstate_debug_type2 = {
+ .name = "cpu/debug/type2",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINTTL(mcontrol, type2_trigger_t),
+ VMSTATE_UINTTL(maddress, type2_trigger_t),
+ VMSTATE_END_OF_LIST()
+ }
+};
+static const VMStateDescription vmstate_debug = {
+ .name = "cpu/debug",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = debug_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINTTL(env.trigger_cur, RISCVCPU),
+ VMSTATE_STRUCT_ARRAY(env.type2_trig, RISCVCPU, TRIGGER_TYPE2_NUM,
+ 0, vmstate_debug_type2, type2_trigger_t),
VMSTATE_END_OF_LIST()
}
};
@@ -231,6 +262,28 @@ static int riscv_cpu_post_load(void *opaque, int version_id)
return 0;
}
+static bool envcfg_needed(void *opaque)
+{
+ RISCVCPU *cpu = opaque;
+ CPURISCVState *env = &cpu->env;
+
+ return (env->priv_ver >= PRIV_VERSION_1_12_0 ? 1 : 0);
+}
+
+static const VMStateDescription vmstate_envcfg = {
+ .name = "cpu/envcfg",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = envcfg_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(env.menvcfg, RISCVCPU),
+ VMSTATE_UINTTL(env.senvcfg, RISCVCPU),
+ VMSTATE_UINT64(env.henvcfg, RISCVCPU),
+
+ VMSTATE_END_OF_LIST()
+ }
+};
+
const VMStateDescription vmstate_riscv_cpu = {
.name = "cpu",
.version_id = 3,
@@ -292,6 +345,8 @@ const VMStateDescription vmstate_riscv_cpu = {
&vmstate_pointermasking,
&vmstate_rv128,
&vmstate_kvmtimer,
+ &vmstate_envcfg,
+ &vmstate_debug,
NULL
}
};
diff --git a/target/riscv/meson.build b/target/riscv/meson.build
index 91f0ac32ff..2c20f3dd8e 100644
--- a/target/riscv/meson.build
+++ b/target/riscv/meson.build
@@ -27,6 +27,7 @@ riscv_softmmu_ss = ss.source_set()
riscv_softmmu_ss.add(files(
'arch_dump.c',
'pmp.c',
+ 'debug.c',
'monitor.c',
'machine.c'
))
diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
index 81b61bb65c..151da3fa08 100644
--- a/target/riscv/pmp.c
+++ b/target/riscv/pmp.c
@@ -141,17 +141,9 @@ static void pmp_decode_napot(target_ulong a, target_ulong *sa, target_ulong *ea)
0111...1111 2^(XLEN+2)-byte NAPOT range
1111...1111 Reserved
*/
- if (a == -1) {
- *sa = 0u;
- *ea = -1;
- return;
- } else {
- target_ulong t1 = ctz64(~a);
- target_ulong base = (a & ~(((target_ulong)1 << t1) - 1)) << 2;
- target_ulong range = ((target_ulong)1 << (t1 + 3)) - 1;
- *sa = base;
- *ea = base + range;
- }
+ a = (a << 2) | 0x3;
+ *sa = a & (a + 1);
+ *ea = a | (a + 1);
}
void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index)
diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c
index 7a6ce0a3bc..576b14e5a3 100644
--- a/target/riscv/vector_helper.c
+++ b/target/riscv/vector_helper.c
@@ -4888,25 +4888,20 @@ GEN_VEXT_VCOMPRESS_VM(vcompress_vm_w, uint32_t, H4)
GEN_VEXT_VCOMPRESS_VM(vcompress_vm_d, uint64_t, H8)
/* Vector Whole Register Move */
-#define GEN_VEXT_VMV_WHOLE(NAME, LEN) \
-void HELPER(NAME)(void *vd, void *vs2, CPURISCVState *env, \
- uint32_t desc) \
-{ \
- /* EEW = 8 */ \
- uint32_t maxsz = simd_maxsz(desc); \
- uint32_t i = env->vstart; \
- \
- memcpy((uint8_t *)vd + H1(i), \
- (uint8_t *)vs2 + H1(i), \
- maxsz - env->vstart); \
- \
- env->vstart = 0; \
-}
+void HELPER(vmvr_v)(void *vd, void *vs2, CPURISCVState *env, uint32_t desc)
+{
+ /* EEW = SEW */
+ uint32_t maxsz = simd_maxsz(desc);
+ uint32_t sewb = 1 << FIELD_EX64(env->vtype, VTYPE, VSEW);
+ uint32_t startb = env->vstart * sewb;
+ uint32_t i = startb;
+
+ memcpy((uint8_t *)vd + H1(i),
+ (uint8_t *)vs2 + H1(i),
+ maxsz - startb);
-GEN_VEXT_VMV_WHOLE(vmv1r_v, 1)
-GEN_VEXT_VMV_WHOLE(vmv2r_v, 2)
-GEN_VEXT_VMV_WHOLE(vmv4r_v, 4)
-GEN_VEXT_VMV_WHOLE(vmv8r_v, 8)
+ env->vstart = 0;
+}
/* Vector Integer Extension */
#define GEN_VEXT_INT_EXT(NAME, ETYPE, DTYPE, HD, HS1) \
diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c
index ae70368966..8f092dab95 100644
--- a/target/s390x/tcg/translate.c
+++ b/target/s390x/tcg/translate.c
@@ -2622,7 +2622,7 @@ static DisasJumpType op_icm(DisasContext *s, DisasOps *o)
tcg_gen_qemu_ld8u(tmp, o->in2, get_mem_index(s));
tcg_gen_addi_i64(o->in2, o->in2, 1);
tcg_gen_deposit_i64(o->out, o->out, tmp, pos, 8);
- ccm |= 0xff << pos;
+ ccm |= 0xffull << pos;
}
m3 = (m3 << 1) & 0xf;
pos -= 8;
diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
index 18eddcc734..98595d47fe 100755
--- a/tests/qemu-iotests/030
+++ b/tests/qemu-iotests/030
@@ -64,16 +64,18 @@ class TestSingleDrive(iotests.QMPTestCase):
self.assert_no_active_block_jobs()
self.vm.shutdown()
- self.assertEqual(qemu_io('-f', 'raw', '-c', 'map', backing_img),
- qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img),
- 'image file map does not match backing file after streaming')
+ self.assertEqual(
+ qemu_io('-f', 'raw', '-c', 'map', backing_img).stdout,
+ qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img).stdout,
+ 'image file map does not match backing file after streaming')
def test_stream_intermediate(self):
self.assert_no_active_block_jobs()
- self.assertNotEqual(qemu_io('-f', 'raw', '-rU', '-c', 'map', backing_img),
- qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', mid_img),
- 'image file map matches backing file before streaming')
+ self.assertNotEqual(
+ qemu_io('-f', 'raw', '-rU', '-c', 'map', backing_img).stdout,
+ qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', mid_img).stdout,
+ 'image file map matches backing file before streaming')
result = self.vm.qmp('block-stream', device='mid', job_id='stream-mid')
self.assert_qmp(result, 'return', {})
@@ -83,9 +85,10 @@ class TestSingleDrive(iotests.QMPTestCase):
self.assert_no_active_block_jobs()
self.vm.shutdown()
- self.assertEqual(qemu_io('-f', 'raw', '-c', 'map', backing_img),
- qemu_io('-f', iotests.imgfmt, '-c', 'map', mid_img),
- 'image file map does not match backing file after streaming')
+ self.assertEqual(
+ qemu_io('-f', 'raw', '-c', 'map', backing_img).stdout,
+ qemu_io('-f', iotests.imgfmt, '-c', 'map', mid_img).stdout,
+ 'image file map does not match backing file after streaming')
def test_stream_pause(self):
self.assert_no_active_block_jobs()
@@ -113,15 +116,17 @@ class TestSingleDrive(iotests.QMPTestCase):
self.assert_no_active_block_jobs()
self.vm.shutdown()
- self.assertEqual(qemu_io('-f', 'raw', '-c', 'map', backing_img),
- qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img),
- 'image file map does not match backing file after streaming')
+ self.assertEqual(
+ qemu_io('-f', 'raw', '-c', 'map', backing_img).stdout,
+ qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img).stdout,
+ 'image file map does not match backing file after streaming')
def test_stream_no_op(self):
self.assert_no_active_block_jobs()
# The image map is empty before the operation
- empty_map = qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', test_img)
+ empty_map = qemu_io(
+ '-f', iotests.imgfmt, '-rU', '-c', 'map', test_img).stdout
# This is a no-op: no data should ever be copied from the base image
result = self.vm.qmp('block-stream', device='drive0', base=mid_img)
@@ -132,8 +137,9 @@ class TestSingleDrive(iotests.QMPTestCase):
self.assert_no_active_block_jobs()
self.vm.shutdown()
- self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img),
- empty_map, 'image file map changed after a no-op')
+ self.assertEqual(
+ qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img).stdout,
+ empty_map, 'image file map changed after a no-op')
def test_stream_partial(self):
self.assert_no_active_block_jobs()
@@ -146,9 +152,10 @@ class TestSingleDrive(iotests.QMPTestCase):
self.assert_no_active_block_jobs()
self.vm.shutdown()
- self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', mid_img),
- qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img),
- 'image file map does not match backing file after streaming')
+ self.assertEqual(
+ qemu_io('-f', iotests.imgfmt, '-c', 'map', mid_img).stdout,
+ qemu_io('-f', iotests.imgfmt, '-c', 'map', test_img).stdout,
+ 'image file map does not match backing file after streaming')
def test_device_not_found(self):
result = self.vm.qmp('block-stream', device='nonexistent')
@@ -236,9 +243,10 @@ class TestParallelOps(iotests.QMPTestCase):
# Check that the maps don't match before the streaming operations
for i in range(2, self.num_imgs, 2):
- self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[i]),
- qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[i-1]),
- 'image file map matches backing file before streaming')
+ self.assertNotEqual(
+ qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[i]).stdout,
+ qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[i-1]).stdout,
+ 'image file map matches backing file before streaming')
# Create all streaming jobs
pending_jobs = []
@@ -278,9 +286,10 @@ class TestParallelOps(iotests.QMPTestCase):
# Check that all maps match now
for i in range(2, self.num_imgs, 2):
- self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i]),
- qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i-1]),
- 'image file map does not match backing file after streaming')
+ self.assertEqual(
+ qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i]).stdout,
+ qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[i-1]).stdout,
+ 'image file map does not match backing file after streaming')
# Test that it's not possible to perform two block-stream
# operations if there are nodes involved in both.
@@ -514,9 +523,10 @@ class TestParallelOps(iotests.QMPTestCase):
def test_stream_base_node_name(self):
self.assert_no_active_block_jobs()
- self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[4]),
- qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[3]),
- 'image file map matches backing file before streaming')
+ self.assertNotEqual(
+ qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[4]).stdout,
+ qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.imgs[3]).stdout,
+ 'image file map matches backing file before streaming')
# Error: the base node does not exist
result = self.vm.qmp('block-stream', device='node4', base_node='none', job_id='stream')
@@ -547,9 +557,10 @@ class TestParallelOps(iotests.QMPTestCase):
self.assert_no_active_block_jobs()
self.vm.shutdown()
- self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[4]),
- qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[3]),
- 'image file map matches backing file after streaming')
+ self.assertEqual(
+ qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[4]).stdout,
+ qemu_io('-f', iotests.imgfmt, '-c', 'map', self.imgs[3]).stdout,
+ 'image file map matches backing file after streaming')
class TestQuorum(iotests.QMPTestCase):
num_children = 3
@@ -588,9 +599,10 @@ class TestQuorum(iotests.QMPTestCase):
os.remove(img)
def test_stream_quorum(self):
- self.assertNotEqual(qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.children[0]),
- qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.backing[0]),
- 'image file map matches backing file before streaming')
+ self.assertNotEqual(
+ qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.children[0]).stdout,
+ qemu_io('-f', iotests.imgfmt, '-rU', '-c', 'map', self.backing[0]).stdout,
+ 'image file map matches backing file before streaming')
self.assert_no_active_block_jobs()
@@ -602,9 +614,10 @@ class TestQuorum(iotests.QMPTestCase):
self.assert_no_active_block_jobs()
self.vm.shutdown()
- self.assertEqual(qemu_io('-f', iotests.imgfmt, '-c', 'map', self.children[0]),
- qemu_io('-f', iotests.imgfmt, '-c', 'map', self.backing[0]),
- 'image file map does not match backing file after streaming')
+ self.assertEqual(
+ qemu_io('-f', iotests.imgfmt, '-c', 'map', self.children[0]).stdout,
+ qemu_io('-f', iotests.imgfmt, '-c', 'map', self.backing[0]).stdout,
+ 'image file map does not match backing file after streaming')
class TestSmallerBackingFile(iotests.QMPTestCase):
backing_len = 1 * 1024 * 1024 # MB
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
index 0e1cfd7e49..30eb97829e 100755
--- a/tests/qemu-iotests/040
+++ b/tests/qemu-iotests/040
@@ -86,8 +86,10 @@ class TestSingleDrive(ImageCommitTestCase):
qemu_img('create', '-f', iotests.imgfmt,
'-o', 'backing_file=%s' % mid_img,
'-F', iotests.imgfmt, test_img)
- qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', backing_img)
- qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', mid_img)
+ if self.image_len:
+ qemu_io('-f', 'raw', '-c', 'write -P 0xab 0 524288', backing_img)
+ qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288',
+ mid_img)
self.vm = iotests.VM().add_drive(test_img, "node-name=top,backing.node-name=mid,backing.backing.node-name=base", interface="none")
self.vm.add_device('virtio-scsi')
self.vm.add_device("scsi-hd,id=scsi0,drive=drive0")
@@ -101,13 +103,17 @@ class TestSingleDrive(ImageCommitTestCase):
def test_commit(self):
self.run_commit_test(mid_img, backing_img)
- self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
- self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
+ if not self.image_len:
+ return
+ qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img)
+ qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img)
def test_commit_node(self):
self.run_commit_test("mid", "base", node_names=True)
- self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
- self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
+ if not self.image_len:
+ return
+ qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img)
+ qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img)
@iotests.skip_if_unsupported(['throttle'])
def test_commit_with_filter_and_quit(self):
@@ -192,13 +198,17 @@ class TestSingleDrive(ImageCommitTestCase):
def test_top_is_active(self):
self.run_commit_test(test_img, backing_img, need_ready=True)
- self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
- self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
+ if not self.image_len:
+ return
+ qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img)
+ qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img)
def test_top_is_default_active(self):
self.run_default_commit_test()
- self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed"))
- self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed"))
+ if not self.image_len:
+ return
+ qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img)
+ qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img)
def test_top_and_base_reversed(self):
self.assert_no_active_block_jobs()
@@ -334,8 +344,8 @@ class TestRelativePaths(ImageCommitTestCase):
def test_commit(self):
self.run_commit_test(self.mid_img, self.backing_img)
- self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed"))
- self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed"))
+ qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs)
+ qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs)
def test_device_not_found(self):
result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % self.mid_img)
@@ -361,8 +371,8 @@ class TestRelativePaths(ImageCommitTestCase):
def test_top_is_active(self):
self.run_commit_test(self.test_img, self.backing_img)
- self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs).find("verification failed"))
- self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs).find("verification failed"))
+ qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', self.backing_img_abs)
+ qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', self.backing_img_abs)
def test_top_and_base_reversed(self):
self.assert_no_active_block_jobs()
@@ -738,11 +748,10 @@ class TestCommitWithFilters(iotests.QMPTestCase):
def do_test_io(self, read_or_write):
for index, pattern_file in enumerate(self.pattern_files):
- result = qemu_io('-f', iotests.imgfmt,
- '-c',
- f'{read_or_write} -P {index + 1} {index}M 1M',
- pattern_file)
- self.assertFalse('Pattern verification failed' in result)
+ qemu_io('-f', iotests.imgfmt,
+ '-c',
+ f'{read_or_write} -P {index + 1} {index}M 1M',
+ pattern_file)
@iotests.skip_if_unsupported(['throttle'])
def setUp(self):
@@ -827,7 +836,8 @@ class TestCommitWithFilters(iotests.QMPTestCase):
job_id='commit',
device='top-filter',
top_node='cow-2',
- base_node='cow-1')
+ base_node='cow-1',
+ backing_file=self.img1)
self.assert_qmp(result, 'return', {})
self.wait_until_completed(drive='commit')
@@ -843,7 +853,8 @@ class TestCommitWithFilters(iotests.QMPTestCase):
job_id='commit',
device='top-filter',
top_node='cow-1',
- base_node='cow-0')
+ base_node='cow-0',
+ backing_file=self.img0)
self.assert_qmp(result, 'return', {})
self.wait_until_completed(drive='commit')
diff --git a/tests/qemu-iotests/056 b/tests/qemu-iotests/056
index b459a3f1e8..bef865eec4 100755
--- a/tests/qemu-iotests/056
+++ b/tests/qemu-iotests/056
@@ -102,7 +102,7 @@ class TestSyncModesNoneAndTop(iotests.QMPTestCase):
self.vm.shutdown()
time.sleep(1)
- self.assertEqual(-1, qemu_io('-c', 'read -P0x41 0 512', target_img).find("verification failed"))
+ qemu_io('-c', 'read -P0x41 0 512', target_img)
class TestBeforeWriteNotifier(iotests.QMPTestCase):
def setUp(self):
diff --git a/tests/qemu-iotests/108 b/tests/qemu-iotests/108
index 688d3ae8f6..9e923d6a59 100755
--- a/tests/qemu-iotests/108
+++ b/tests/qemu-iotests/108
@@ -326,7 +326,7 @@ else
$QSD \
--blockdev file,node-name=export-node,filename="$TEST_IMG" \
- --export fuse,id=fuse-export,node-name=export-node,mountpoint="$export_mp",writable=on,growable=off \
+ --export fuse,id=fuse-export,node-name=export-node,mountpoint="$export_mp",writable=on,growable=off,allow-other=off \
--pidfile "$TEST_DIR/qsd.pid" \
--daemonize
fi
diff --git a/tests/qemu-iotests/149 b/tests/qemu-iotests/149
index 9bb96d6a1d..2ae318f16f 100755
--- a/tests/qemu-iotests/149
+++ b/tests/qemu-iotests/149
@@ -295,7 +295,8 @@ def qemu_io_write_pattern(config, pattern, offset_mb, size_mb, dev=False):
args = ["-c", "write -P 0x%x %dM %dM" % (pattern, offset_mb, size_mb)]
args.extend(qemu_io_image_args(config, dev))
iotests.log("qemu-io " + " ".join(args), filters=[iotests.filter_test_dir])
- iotests.log(check_cipher_support(config, iotests.qemu_io(*args)),
+ output = iotests.qemu_io(*args, check=False).stdout
+ iotests.log(check_cipher_support(config, output),
filters=[iotests.filter_test_dir, iotests.filter_qemu_io])
@@ -307,7 +308,8 @@ def qemu_io_read_pattern(config, pattern, offset_mb, size_mb, dev=False):
args = ["-c", "read -P 0x%x %dM %dM" % (pattern, offset_mb, size_mb)]
args.extend(qemu_io_image_args(config, dev))
iotests.log("qemu-io " + " ".join(args), filters=[iotests.filter_test_dir])
- iotests.log(check_cipher_support(config, iotests.qemu_io(*args)),
+ output = iotests.qemu_io(*args, check=False).stdout
+ iotests.log(check_cipher_support(config, output),
filters=[iotests.filter_test_dir, iotests.filter_qemu_io])
diff --git a/tests/qemu-iotests/163 b/tests/qemu-iotests/163
index e4cd4b230f..c94ad16f4a 100755
--- a/tests/qemu-iotests/163
+++ b/tests/qemu-iotests/163
@@ -113,10 +113,7 @@ class ShrinkBaseClass(iotests.QMPTestCase):
qemu_img('resize', '-f', iotests.imgfmt, '--shrink', test_img,
self.shrink_size)
- self.assertEqual(
- qemu_io('-c', 'read -P 0x00 %s'%self.shrink_size, test_img),
- qemu_io('-c', 'read -P 0x00 %s'%self.shrink_size, check_img),
- "Verifying image content")
+ qemu_io('-c', f"read -P 0x00 0 {self.shrink_size}", test_img)
self.image_verify()
diff --git a/tests/qemu-iotests/205 b/tests/qemu-iotests/205
index c0e107328f..15f798288a 100755
--- a/tests/qemu-iotests/205
+++ b/tests/qemu-iotests/205
@@ -85,13 +85,13 @@ class TestNbdServerRemove(iotests.QMPTestCase):
def do_test_connect_after_remove(self, mode=None):
args = ('-r', '-f', 'raw', '-c', 'read 0 512', nbd_uri)
- self.assertReadOk(qemu_io(*args))
+ self.assertReadOk(qemu_io(*args).stdout)
result = self.remove_export('exp', mode)
self.assert_qmp(result, 'return', {})
self.assertExportNotFound('exp')
- self.assertConnectFailed(qemu_io(*args))
+ self.assertConnectFailed(qemu_io(*args, check=False).stdout)
def test_connect_after_remove_default(self):
self.do_test_connect_after_remove()
diff --git a/tests/qemu-iotests/216 b/tests/qemu-iotests/216
index c531abfded..311e02af3a 100755
--- a/tests/qemu-iotests/216
+++ b/tests/qemu-iotests/216
@@ -21,7 +21,7 @@
# Creator/Owner: Hanna Reitz <hreitz@redhat.com>
import iotests
-from iotests import log, qemu_img, qemu_io_silent
+from iotests import log, qemu_img, qemu_io
# Need backing file support
iotests.script_initialize(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk'],
@@ -52,10 +52,10 @@ with iotests.FilePath('base.img') as base_img_path, \
log('')
qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M')
- assert qemu_io_silent(base_img_path, '-c', 'write -P 1 0M 1M') == 0
+ qemu_io(base_img_path, '-c', 'write -P 1 0M 1M')
qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path,
'-F', iotests.imgfmt, top_img_path)
- assert qemu_io_silent(top_img_path, '-c', 'write -P 2 1M 1M') == 0
+ qemu_io(top_img_path, '-c', 'write -P 2 1M 1M')
log('Done')
@@ -110,8 +110,8 @@ with iotests.FilePath('base.img') as base_img_path, \
log('--- Checking COR result ---')
log('')
- assert qemu_io_silent(base_img_path, '-c', 'discard 0 64M') == 0
- assert qemu_io_silent(top_img_path, '-c', 'read -P 1 0M 1M') == 0
- assert qemu_io_silent(top_img_path, '-c', 'read -P 2 1M 1M') == 0
+ qemu_io(base_img_path, '-c', 'discard 0 64M')
+ qemu_io(top_img_path, '-c', 'read -P 1 0M 1M')
+ qemu_io(top_img_path, '-c', 'read -P 2 1M 1M')
log('Done')
diff --git a/tests/qemu-iotests/218 b/tests/qemu-iotests/218
index 8345793902..6320c4cb56 100755
--- a/tests/qemu-iotests/218
+++ b/tests/qemu-iotests/218
@@ -28,7 +28,7 @@
# Creator/Owner: Hanna Reitz <hreitz@redhat.com>
import iotests
-from iotests import log, qemu_img, qemu_io_silent
+from iotests import log, qemu_img, qemu_io
iotests.script_initialize(supported_fmts=['qcow2', 'raw'])
@@ -146,8 +146,7 @@ with iotests.VM() as vm, \
iotests.FilePath('src.img') as src_img_path:
qemu_img('create', '-f', iotests.imgfmt, src_img_path, '64M')
- assert qemu_io_silent('-f', iotests.imgfmt, src_img_path,
- '-c', 'write -P 42 0M 64M') == 0
+ qemu_io('-f', iotests.imgfmt, src_img_path, '-c', 'write -P 42 0M 64M')
vm.launch()
diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223
index da87f2f4a2..0bbb283010 100755
--- a/tests/qemu-iotests/223
+++ b/tests/qemu-iotests/223
@@ -120,6 +120,11 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"blockdev-add",
"file":{"driver":"file", "filename":"'"$TEST_IMG"'"}}}' "return"
_send_qemu_cmd $QEMU_HANDLE '{"execute":"block-dirty-bitmap-disable",
"arguments":{"node":"n", "name":"b"}}' "return"
+_send_qemu_cmd $QEMU_HANDLE '{"execute":"blockdev-add",
+ "arguments":{"driver":"null-co", "node-name":"null",
+ "size": 4194304}}' "return"
+_send_qemu_cmd $QEMU_HANDLE '{"execute":"block-dirty-bitmap-add",
+ "arguments":{"node":"null", "name":"b3"}}' "return"
for attempt in normal iothread; do
@@ -155,6 +160,9 @@ _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add",
"arguments":{"device":"n", "name":"n2", "writable":true,
"description":"some text", "bitmap":"b2"}}' "return"
+_send_qemu_cmd $QEMU_HANDLE '{"execute":"block-export-add",
+ "arguments":{"type": "nbd", "node-name":"n", "id":"n3", "name": "n3",
+ "bitmaps":[{"node":"null","name":"b3"}]}}' "return"
$QEMU_NBD_PROG -L -k "$SOCK_DIR/nbd"
echo
@@ -179,6 +187,14 @@ $QEMU_IMG map --output=json --image-opts \
"$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b2" | _filter_qemu_img_map
echo
+echo "=== Check bitmap taken from another node ==="
+echo
+
+IMG="driver=nbd,export=n3,server.type=unix,server.path=$SOCK_DIR/nbd"
+$QEMU_IMG map --output=json --image-opts \
+ "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b3" | _filter_qemu_img_map
+
+echo
echo "=== End qemu NBD server ==="
echo
diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out
index e58ea5abbd..0647941531 100644
--- a/tests/qemu-iotests/223.out
+++ b/tests/qemu-iotests/223.out
@@ -33,6 +33,13 @@ wrote 2097152/2097152 bytes at offset 2097152
{"execute":"block-dirty-bitmap-disable",
"arguments":{"node":"n", "name":"b"}}
{"return": {}}
+{"execute":"blockdev-add",
+ "arguments":{"driver":"null-co", "node-name":"null",
+ "size": 4194304}}
+{"return": {}}
+{"execute":"block-dirty-bitmap-add",
+ "arguments":{"node":"null", "name":"b3"}}
+{"return": {}}
=== Set up NBD with normal access ===
@@ -69,7 +76,11 @@ exports available: 0
"arguments":{"device":"n", "name":"n2", "writable":true,
"description":"some text", "bitmap":"b2"}}
{"return": {}}
-exports available: 2
+{"execute":"block-export-add",
+ "arguments":{"type": "nbd", "node-name":"n", "id":"n3", "name": "n3",
+ "bitmaps":[{"node":"null","name":"b3"}]}}
+{"return": {}}
+exports available: 3
export: 'n'
size: 4194304
flags: 0x58f ( readonly flush fua df multi cache )
@@ -89,6 +100,15 @@ exports available: 2
available meta contexts: 2
base:allocation
qemu:dirty-bitmap:b2
+ export: 'n3'
+ size: 4194304
+ flags: 0x58f ( readonly flush fua df multi cache )
+ min block: 1
+ opt block: 4096
+ max block: 33554432
+ available meta contexts: 2
+ base:allocation
+ qemu:dirty-bitmap:b3
=== Contrast normal status to large granularity dirty-bitmap ===
@@ -114,6 +134,10 @@ read 2097152/2097152 bytes at offset 2097152
{ "start": 1024, "length": 2096128, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET},
{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false}]
+=== Check bitmap taken from another node ===
+
+[{ "start": 0, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}]
+
=== End qemu NBD server ===
{"execute":"nbd-server-remove",
@@ -128,6 +152,7 @@ read 2097152/2097152 bytes at offset 2097152
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}}
{"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}}
{"execute":"nbd-server-stop"}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n3"}}
{"return": {}}
{"execute":"nbd-server-stop"}
{"error": {"class": "GenericError", "desc": "NBD server not running"}}
@@ -170,7 +195,11 @@ exports available: 0
"arguments":{"device":"n", "name":"n2", "writable":true,
"description":"some text", "bitmap":"b2"}}
{"return": {}}
-exports available: 2
+{"execute":"block-export-add",
+ "arguments":{"type": "nbd", "node-name":"n", "id":"n3", "name": "n3",
+ "bitmaps":[{"node":"null","name":"b3"}]}}
+{"return": {}}
+exports available: 3
export: 'n'
size: 4194304
flags: 0x58f ( readonly flush fua df multi cache )
@@ -190,6 +219,15 @@ exports available: 2
available meta contexts: 2
base:allocation
qemu:dirty-bitmap:b2
+ export: 'n3'
+ size: 4194304
+ flags: 0x58f ( readonly flush fua df multi cache )
+ min block: 1
+ opt block: 4096
+ max block: 33554432
+ available meta contexts: 2
+ base:allocation
+ qemu:dirty-bitmap:b3
=== Contrast normal status to large granularity dirty-bitmap ===
@@ -215,6 +253,10 @@ read 2097152/2097152 bytes at offset 2097152
{ "start": 1024, "length": 2096128, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET},
{ "start": 2097152, "length": 2097152, "depth": 0, "present": false, "zero": false, "data": false}]
+=== Check bitmap taken from another node ===
+
+[{ "start": 0, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET}]
+
=== End qemu NBD server ===
{"execute":"nbd-server-remove",
@@ -229,6 +271,7 @@ read 2097152/2097152 bytes at offset 2097152
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n2"}}
{"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}}
{"execute":"nbd-server-stop"}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_EXPORT_DELETED", "data": {"id": "n3"}}
{"return": {}}
{"execute":"nbd-server-stop"}
{"error": {"class": "GenericError", "desc": "NBD server not running"}}
diff --git a/tests/qemu-iotests/224 b/tests/qemu-iotests/224
index 4df5157e8d..542d0eefa6 100755
--- a/tests/qemu-iotests/224
+++ b/tests/qemu-iotests/224
@@ -22,7 +22,7 @@
# Creator/Owner: Hanna Reitz <hreitz@redhat.com>
import iotests
-from iotests import log, qemu_img, qemu_io_silent, filter_qmp_testfiles, \
+from iotests import log, qemu_img, qemu_io, filter_qmp_testfiles, \
filter_qmp_imgfmt
import json
@@ -54,7 +54,7 @@ for filter_node_name in False, True:
'-F', iotests.imgfmt, top_img_path)
# Something to commit
- assert qemu_io_silent(mid_img_path, '-c', 'write -P 1 0 1M') == 0
+ qemu_io(mid_img_path, '-c', 'write -P 1 0 1M')
vm.launch()
diff --git a/tests/qemu-iotests/242 b/tests/qemu-iotests/242
index b3afd36d72..c89f0c6cb3 100755
--- a/tests/qemu-iotests/242
+++ b/tests/qemu-iotests/242
@@ -22,8 +22,8 @@
import iotests
import json
import struct
-from iotests import qemu_img_create, qemu_io, qemu_img_info, \
- file_path, img_info_log, log, filter_qemu_io
+from iotests import qemu_img_create, qemu_io_log, qemu_img_info, \
+ file_path, img_info_log, log
iotests.script_initialize(supported_fmts=['qcow2'],
supported_protocols=['file'],
@@ -61,7 +61,7 @@ def add_bitmap(bitmap_number, persistent, disabled):
def write_to_disk(offset, size):
write = 'write {} {}'.format(offset, size)
- log(qemu_io('-c', write, disk), filters=[filter_qemu_io])
+ qemu_io_log('-c', write, disk)
def toggle_flag(offset):
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
index 8cbed7821b..edaf29094b 100755
--- a/tests/qemu-iotests/245
+++ b/tests/qemu-iotests/245
@@ -20,11 +20,13 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
+import copy
+import json
import os
import re
+from subprocess import CalledProcessError
+
import iotests
-import copy
-import json
from iotests import qemu_img, qemu_io
hd_path = [
@@ -216,11 +218,14 @@ class TestBlockdevReopen(iotests.QMPTestCase):
# Reopen an image several times changing some of its options
def test_reopen(self):
- # Check whether the filesystem supports O_DIRECT
- if 'O_DIRECT' in qemu_io('-f', 'raw', '-t', 'none', '-c', 'quit', hd_path[0]):
- supports_direct = False
- else:
+ try:
+ qemu_io('-f', 'raw', '-t', 'none', '-c', 'quit', hd_path[0])
supports_direct = True
+ except CalledProcessError as exc:
+ if 'O_DIRECT' in exc.stdout:
+ supports_direct = False
+ else:
+ raise
# Open the hd1 image passing all backing options
opts = hd_opts(1)
diff --git a/tests/qemu-iotests/255 b/tests/qemu-iotests/255
index f86fa851b6..88b29d64b4 100755
--- a/tests/qemu-iotests/255
+++ b/tests/qemu-iotests/255
@@ -95,9 +95,7 @@ with iotests.FilePath('src.qcow2') as src_path, \
iotests.qemu_img_create('-f', iotests.imgfmt, src_path, size_str)
iotests.qemu_img_create('-f', iotests.imgfmt, dst_path, size_str)
- iotests.log(iotests.qemu_io('-f', iotests.imgfmt, '-c', 'write 0 1M',
- src_path),
- filters=[iotests.filter_test_dir, iotests.filter_qemu_io])
+ iotests.qemu_io_log('-f', iotests.imgfmt, '-c', 'write 0 1M', src_path),
vm.add_object('throttle-group,x-bps-read=4096,id=throttle0')
diff --git a/tests/qemu-iotests/258 b/tests/qemu-iotests/258
index cfd536d6dc..73d4af645f 100755
--- a/tests/qemu-iotests/258
+++ b/tests/qemu-iotests/258
@@ -21,7 +21,7 @@
# Creator/Owner: Hanna Reitz <hreitz@redhat.com>
import iotests
-from iotests import log, qemu_img, qemu_io_silent, \
+from iotests import log, qemu_img, qemu_io, \
filter_qmp_testfiles, filter_qmp_imgfmt
# Returns a node for blockdev-add
@@ -86,15 +86,14 @@ def test_concurrent_finish(write_to_stream_node):
if write_to_stream_node:
# This is what (most of the time) makes commit finish
# earlier and then pull in stream
- assert qemu_io_silent(node2_path,
- '-c', 'write %iK 64K' % (65536 - 192),
- '-c', 'write %iK 64K' % (65536 - 64)) == 0
+ qemu_io(node2_path,
+ '-c', 'write %iK 64K' % (65536 - 192),
+ '-c', 'write %iK 64K' % (65536 - 64))
stream_throttle='tg'
else:
# And this makes stream finish earlier
- assert qemu_io_silent(node1_path,
- '-c', 'write %iK 64K' % (65536 - 64)) == 0
+ qemu_io(node1_path, '-c', 'write %iK 64K' % (65536 - 64))
commit_throttle='tg'
diff --git a/tests/qemu-iotests/298 b/tests/qemu-iotests/298
index fae72211b1..ad560e2941 100755
--- a/tests/qemu-iotests/298
+++ b/tests/qemu-iotests/298
@@ -129,16 +129,13 @@ class TestTruncate(iotests.QMPTestCase):
os.remove(refdisk)
def do_test(self, prealloc_mode, new_size):
- ret = iotests.qemu_io_silent('--image-opts', '-c', 'write 0 10M', '-c',
- f'truncate -m {prealloc_mode} {new_size}',
- drive_opts)
- self.assertEqual(ret, 0)
-
- ret = iotests.qemu_io_silent('-f', iotests.imgfmt, '-c', 'write 0 10M',
- '-c',
- f'truncate -m {prealloc_mode} {new_size}',
- refdisk)
- self.assertEqual(ret, 0)
+ iotests.qemu_io('--image-opts', '-c', 'write 0 10M', '-c',
+ f'truncate -m {prealloc_mode} {new_size}',
+ drive_opts)
+
+ iotests.qemu_io('-f', iotests.imgfmt, '-c', 'write 0 10M',
+ '-c', f'truncate -m {prealloc_mode} {new_size}',
+ refdisk)
stat = os.stat(disk)
refstat = os.stat(refdisk)
diff --git a/tests/qemu-iotests/303 b/tests/qemu-iotests/303
index 40e947f26c..a8cc6a23df 100755
--- a/tests/qemu-iotests/303
+++ b/tests/qemu-iotests/303
@@ -21,7 +21,7 @@
import iotests
import subprocess
-from iotests import qemu_img_create, qemu_io, file_path, log, filter_qemu_io, \
+from iotests import qemu_img_create, qemu_io_log, file_path, log, \
verify_qcow2_zstd_compression
iotests.script_initialize(supported_fmts=['qcow2'],
@@ -45,7 +45,7 @@ def create_bitmap(bitmap_number, disabled):
def write_to_disk(offset, size):
write = f'write {offset} {size}'
- log(qemu_io('-c', write, disk), filters=[filter_qemu_io])
+ qemu_io_log('-c', write, disk)
def add_bitmap(num, begin, end, disabled):
diff --git a/tests/qemu-iotests/310 b/tests/qemu-iotests/310
index 00fc5618f6..650d2cb6fb 100755
--- a/tests/qemu-iotests/310
+++ b/tests/qemu-iotests/310
@@ -21,7 +21,7 @@
#
import iotests
-from iotests import log, qemu_img, qemu_io_silent
+from iotests import log, qemu_img, qemu_io
# Need backing file support
iotests.script_initialize(supported_fmts=['qcow2'],
@@ -44,15 +44,15 @@ with iotests.FilePath('base.img') as base_img_path, \
log('')
qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M')
- assert qemu_io_silent(base_img_path, '-c', 'write -P 1 0M 1M') == 0
- assert qemu_io_silent(base_img_path, '-c', 'write -P 1 3M 1M') == 0
+ qemu_io(base_img_path, '-c', 'write -P 1 0M 1M')
+ qemu_io(base_img_path, '-c', 'write -P 1 3M 1M')
qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path,
'-F', iotests.imgfmt, mid_img_path)
- assert qemu_io_silent(mid_img_path, '-c', 'write -P 3 2M 1M') == 0
- assert qemu_io_silent(mid_img_path, '-c', 'write -P 3 4M 1M') == 0
+ qemu_io(mid_img_path, '-c', 'write -P 3 2M 1M')
+ qemu_io(mid_img_path, '-c', 'write -P 3 4M 1M')
qemu_img('create', '-f', iotests.imgfmt, '-b', mid_img_path,
'-F', iotests.imgfmt, top_img_path)
- assert qemu_io_silent(top_img_path, '-c', 'write -P 2 1M 1M') == 0
+ qemu_io(top_img_path, '-c', 'write -P 2 1M 1M')
# 0 1 2 3 4
# top 2
@@ -107,10 +107,10 @@ with iotests.FilePath('base.img') as base_img_path, \
# Detach backing to check that we can read the data from the top level now
qemu_img('rebase', '-u', '-b', '', '-f', iotests.imgfmt, top_img_path)
- assert qemu_io_silent(top_img_path, '-c', 'read -P 0 0 1M') == 0
- assert qemu_io_silent(top_img_path, '-c', 'read -P 2 1M 1M') == 0
- assert qemu_io_silent(top_img_path, '-c', 'read -P 3 2M 1M') == 0
- assert qemu_io_silent(top_img_path, '-c', 'read -P 0 3M 1M') == 0
- assert qemu_io_silent(top_img_path, '-c', 'read -P 3 4M 1M') == 0
+ qemu_io(top_img_path, '-c', 'read -P 0 0 1M')
+ qemu_io(top_img_path, '-c', 'read -P 2 1M 1M')
+ qemu_io(top_img_path, '-c', 'read -P 3 2M 1M')
+ qemu_io(top_img_path, '-c', 'read -P 0 3M 1M')
+ qemu_io(top_img_path, '-c', 'read -P 3 4M 1M')
log('Done')
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 33a44671aa..da7d6637e1 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -206,15 +206,13 @@ def qemu_img_create_prepare_args(args: List[str]) -> List[str]:
return result
-def qemu_img(*args: str, check: bool = True, combine_stdio: bool = True
- ) -> 'subprocess.CompletedProcess[str]':
- """
- Run qemu_img and return the status code and console output.
- This function always prepends QEMU_IMG_OPTIONS and may further alter
- the args for 'create' commands.
+def qemu_tool(*args: str, check: bool = True, combine_stdio: bool = True
+ ) -> 'subprocess.CompletedProcess[str]':
+ """
+ Run a qemu tool and return its status code and console output.
- :param args: command-line arguments to qemu-img.
+ :param args: full command line to run.
:param check: Enforce a return code of zero.
:param combine_stdio: set to False to keep stdout/stderr separated.
@@ -231,10 +229,8 @@ def qemu_img(*args: str, check: bool = True, combine_stdio: bool = True
properties. If streams are not combined, it will also have a
stderr property.
"""
- full_args = qemu_img_args + qemu_img_create_prepare_args(list(args))
-
subp = subprocess.run(
- full_args,
+ args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT if combine_stdio else subprocess.PIPE,
universal_newlines=True,
@@ -243,7 +239,7 @@ def qemu_img(*args: str, check: bool = True, combine_stdio: bool = True
if check and subp.returncode or (subp.returncode < 0):
raise VerboseProcessError(
- subp.returncode, full_args,
+ subp.returncode, args,
output=subp.stdout,
stderr=subp.stderr,
)
@@ -251,6 +247,20 @@ def qemu_img(*args: str, check: bool = True, combine_stdio: bool = True
return subp
+def qemu_img(*args: str, check: bool = True, combine_stdio: bool = True
+ ) -> 'subprocess.CompletedProcess[str]':
+ """
+ Run QEMU_IMG_PROG and return its status code and console output.
+
+ This function always prepends QEMU_IMG_OPTIONS and may further alter
+ the args for 'create' commands.
+
+ See `qemu_tool()` for greater detail.
+ """
+ full_args = qemu_img_args + qemu_img_create_prepare_args(list(args))
+ return qemu_tool(*full_args, check=check, combine_stdio=combine_stdio)
+
+
def ordered_qmp(qmsg, conv_keys=True):
# Dictionaries are not ordered prior to 3.6, therefore:
if isinstance(qmsg, list):
@@ -343,34 +353,23 @@ def qemu_io_wrap_args(args: Sequence[str]) -> List[str]:
def qemu_io_popen(*args):
return qemu_tool_popen(qemu_io_wrap_args(args))
-def qemu_io(*args):
- '''Run qemu-io and return the stdout data'''
- return qemu_tool_pipe_and_status('qemu-io', qemu_io_wrap_args(args))[0]
+def qemu_io(*args: str, check: bool = True, combine_stdio: bool = True
+ ) -> 'subprocess.CompletedProcess[str]':
+ """
+ Run QEMU_IO_PROG and return the status code and console output.
-def qemu_io_pipe_and_status(*args):
- return qemu_tool_pipe_and_status('qemu-io', qemu_io_wrap_args(args))
+ This function always prepends either QEMU_IO_OPTIONS or
+ QEMU_IO_OPTIONS_NO_FMT.
+ """
+ return qemu_tool(*qemu_io_wrap_args(args),
+ check=check, combine_stdio=combine_stdio)
-def qemu_io_log(*args):
- result = qemu_io(*args)
- log(result, filters=[filter_testfiles, filter_qemu_io])
+def qemu_io_log(*args: str, check: bool = True
+ ) -> 'subprocess.CompletedProcess[str]':
+ result = qemu_io(*args, check=check)
+ log(result.stdout, filters=[filter_testfiles, filter_qemu_io])
return result
-def qemu_io_silent(*args):
- '''Run qemu-io and return the exit code, suppressing stdout'''
- args = qemu_io_wrap_args(args)
- result = subprocess.run(args, stdout=subprocess.DEVNULL, check=False)
- if result.returncode < 0:
- sys.stderr.write('qemu-io received signal %i: %s\n' %
- (-result.returncode, ' '.join(args)))
- return result.returncode
-
-def qemu_io_silent_check(*args):
- '''Run qemu-io and return the true if subprocess returned 0'''
- args = qemu_io_wrap_args(args)
- result = subprocess.run(args, stdout=subprocess.DEVNULL,
- stderr=subprocess.STDOUT, check=False)
- return result.returncode == 0
-
class QemuIoInteractive:
def __init__(self, *args):
self.args = qemu_io_wrap_args(args)
diff --git a/tests/qemu-iotests/tests/image-fleecing b/tests/qemu-iotests/tests/image-fleecing
index b7e5076104..f6e449d071 100755
--- a/tests/qemu-iotests/tests/image-fleecing
+++ b/tests/qemu-iotests/tests/image-fleecing
@@ -22,9 +22,10 @@
#
# Creator/Owner: John Snow <jsnow@redhat.com>
+from subprocess import CalledProcessError
+
import iotests
-from iotests import log, qemu_img, qemu_io, qemu_io_silent, \
- qemu_io_pipe_and_status
+from iotests import log, qemu_img, qemu_io
iotests.script_initialize(
supported_fmts=['qcow2'],
@@ -185,10 +186,14 @@ def do_test(vm, use_cbw, use_snapshot_access_filter, base_img_path,
for p in patterns + zeroes:
cmd = 'read -P%s %s %s' % p
log(cmd)
- out, ret = qemu_io_pipe_and_status('-r', '-f', 'raw', '-c', cmd,
- nbd_uri)
- if ret != 0:
- print(out)
+
+ try:
+ qemu_io('-r', '-f', 'raw', '-c', cmd, nbd_uri)
+ except CalledProcessError as exc:
+ if bitmap and p in zeroes:
+ log(exc.stdout)
+ else:
+ raise
log('')
log('--- Testing COW ---')
@@ -228,9 +233,14 @@ def do_test(vm, use_cbw, use_snapshot_access_filter, base_img_path,
args += [target_img_path]
else:
args += ['-f', 'raw', nbd_uri]
- out, ret = qemu_io_pipe_and_status(*args)
- if ret != 0:
- print(out)
+
+ try:
+ qemu_io(*args)
+ except CalledProcessError as exc:
+ if bitmap and p in zeroes:
+ log(exc.stdout)
+ else:
+ raise
log('')
log('--- Cleanup ---')
@@ -260,7 +270,7 @@ def do_test(vm, use_cbw, use_snapshot_access_filter, base_img_path,
for p in overwrite + remainder:
cmd = 'read -P%s %s %s' % p
log(cmd)
- assert qemu_io_silent(base_img_path, '-c', cmd) == 0
+ qemu_io(base_img_path, '-c', cmd)
log('')
log('Done')
diff --git a/tests/qemu-iotests/tests/migration-permissions b/tests/qemu-iotests/tests/migration-permissions
index 6be02581c7..4e1da369c9 100755
--- a/tests/qemu-iotests/tests/migration-permissions
+++ b/tests/qemu-iotests/tests/migration-permissions
@@ -18,6 +18,8 @@
#
import os
+from subprocess import CalledProcessError
+
import iotests
from iotests import imgfmt, qemu_img_create, qemu_io
@@ -69,13 +71,12 @@ class TestMigrationPermissions(iotests.QMPTestCase):
def test_post_migration_permissions(self):
# Try to access the image R/W, which should fail because virtio-blk
# has not been configured with share-rw=on
- log = qemu_io('-f', imgfmt, '-c', 'quit', test_img)
- if not log.strip():
- print('ERROR (pre-migration): qemu-io should not be able to '
- 'access this image, but it reported no error')
- else:
- # This is the expected output
- assert 'Is another process using the image' in log
+ emsg = ('ERROR (pre-migration): qemu-io should not be able to '
+ 'access this image, but it reported no error')
+ with self.assertRaises(CalledProcessError, msg=emsg) as ctx:
+ qemu_io('-f', imgfmt, '-c', 'quit', test_img)
+ if 'Is another process using the image' not in ctx.exception.stdout:
+ raise ctx.exception
# Now migrate the VM
self.vm_s.qmp('migrate', uri=f'unix:{mig_sock}')
@@ -84,13 +85,12 @@ class TestMigrationPermissions(iotests.QMPTestCase):
# Try the same qemu-io access again, verifying that the WRITE
# permission remains unshared
- log = qemu_io('-f', imgfmt, '-c', 'quit', test_img)
- if not log.strip():
- print('ERROR (post-migration): qemu-io should not be able to '
- 'access this image, but it reported no error')
- else:
- # This is the expected output
- assert 'Is another process using the image' in log
+ emsg = ('ERROR (post-migration): qemu-io should not be able to '
+ 'access this image, but it reported no error')
+ with self.assertRaises(CalledProcessError, msg=emsg) as ctx:
+ qemu_io('-f', imgfmt, '-c', 'quit', test_img)
+ if 'Is another process using the image' not in ctx.exception.stdout:
+ raise ctx.exception
if __name__ == '__main__':
diff --git a/tests/qemu-iotests/tests/mirror-ready-cancel-error b/tests/qemu-iotests/tests/mirror-ready-cancel-error
index 1d0e333b5e..01217459b9 100755
--- a/tests/qemu-iotests/tests/mirror-ready-cancel-error
+++ b/tests/qemu-iotests/tests/mirror-ready-cancel-error
@@ -37,7 +37,7 @@ class TestMirrorReadyCancelError(iotests.QMPTestCase):
# Ensure that mirror will copy something before READY so the
# target format layer will forward the pre-READY flush to its
# file child
- assert iotests.qemu_io_silent('-c', 'write -P 1 0 64k', source) == 0
+ iotests.qemu_io('-c', 'write -P 1 0 64k', source)
self.vm = iotests.VM()
self.vm.launch()
diff --git a/tests/qemu-iotests/tests/nbd-reconnect-on-open b/tests/qemu-iotests/tests/nbd-reconnect-on-open
index 8be721a24f..d0b401b060 100755
--- a/tests/qemu-iotests/tests/nbd-reconnect-on-open
+++ b/tests/qemu-iotests/tests/nbd-reconnect-on-open
@@ -39,7 +39,7 @@ def check_fail_to_connect(open_timeout):
log(f'Check fail to connect with {open_timeout} seconds of timeout')
start_t = time.time()
- qemu_io_log(*create_args(open_timeout))
+ qemu_io_log(*create_args(open_timeout), check=False)
delta_t = time.time() - start_t
max_delta = open_timeout + 0.2
diff --git a/tests/qemu-iotests/tests/stream-error-on-reset b/tests/qemu-iotests/tests/stream-error-on-reset
index 389ae822b8..5a8c3a9e8d 100755
--- a/tests/qemu-iotests/tests/stream-error-on-reset
+++ b/tests/qemu-iotests/tests/stream-error-on-reset
@@ -21,7 +21,7 @@
import os
import iotests
-from iotests import imgfmt, qemu_img_create, qemu_io_silent, QMPTestCase
+from iotests import imgfmt, qemu_img_create, qemu_io, QMPTestCase
image_size = 1 * 1024 * 1024
@@ -55,7 +55,7 @@ class TestStreamErrorOnReset(QMPTestCase):
- top image is attached to a virtio-scsi device
"""
qemu_img_create('-f', imgfmt, base, str(image_size))
- assert qemu_io_silent('-c', f'write 0 {data_size}', base) == 0
+ qemu_io('-c', f'write 0 {data_size}', base)
qemu_img_create('-f', imgfmt, top, str(image_size))
self.vm = iotests.VM()
diff --git a/tests/tcg/nios2/10m50-ghrd.ld b/tests/tcg/nios2/10m50-ghrd.ld
new file mode 100644
index 0000000000..7db0d59ad7
--- /dev/null
+++ b/tests/tcg/nios2/10m50-ghrd.ld
@@ -0,0 +1,66 @@
+/*
+ * Link script for the Nios2 10m50-ghrd board.
+ *
+ * Copyright Linaro Ltd 2022
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+MEMORY
+{
+ tpf (rx) : ORIGIN = 0xc0000000, LENGTH = 1K
+ ram (rwx) : ORIGIN = 0xc8000000, LENGTH = 128M
+}
+
+PHDRS
+{
+ RAM PT_LOAD;
+}
+
+ENTRY(_start)
+EXTERN(_start)
+EXTERN(_interrupt)
+EXTERN(_fast_tlb_miss)
+
+SECTIONS
+{
+ /* Begin at the (hardcoded) _interrupt entry point. */
+ .text 0xc8000120 : {
+ *(.text.intr)
+ *(.text .text.* .gnu.linkonce.t.*)
+ } >ram :RAM
+
+ .rodata : ALIGN(4) {
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+ } > ram :RAM
+
+ .eh_frame_hdr : ALIGN (4) {
+ KEEP (*(.eh_frame_hdr))
+ *(.eh_frame_entry .eh_frame_entry.*)
+ } >ram :RAM
+ .eh_frame : ALIGN (4) {
+ KEEP (*(.eh_frame)) *(.eh_frame.*)
+ } >ram :RAM
+
+ .data : ALIGN(4) {
+ *(.shdata)
+ *(.data .data.* .gnu.linkonce.d.*)
+ . = ALIGN(4);
+ _gp = ABSOLUTE(. + 0x8000);
+ *(.got.plt) *(.got)
+ *(.lit8)
+ *(.lit4)
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ } >ram :RAM
+
+ .bss : ALIGN(4) {
+ __bss_start = ABSOLUTE(.);
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ . = ALIGN(4);
+ __bss_end = ABSOLUTE(.);
+ } >ram :RAM
+
+ __stack = ORIGIN(ram) + LENGTH(ram);
+}
diff --git a/tests/tcg/nios2/Makefile.softmmu-target b/tests/tcg/nios2/Makefile.softmmu-target
new file mode 100644
index 0000000000..c3d0594a39
--- /dev/null
+++ b/tests/tcg/nios2/Makefile.softmmu-target
@@ -0,0 +1,33 @@
+#
+# Nios2 system tests
+#
+# Copyright Linaro Ltd 2022
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+
+NIOS2_SYSTEM_SRC = $(SRC_PATH)/tests/tcg/nios2
+VPATH += $(NIOS2_SYSTEM_SRC)
+
+# These objects provide the basic boot code and helper functions for all tests
+CRT_OBJS = boot.o intr.o $(MINILIB_OBJS)
+LINK_SCRIPT = $(NIOS2_SYSTEM_SRC)/10m50-ghrd.ld
+
+CFLAGS += -nostdlib -g -O0 $(MINILIB_INC)
+LDFLAGS += -Wl,-T$(LINK_SCRIPT) -static -nostdlib $(CRT_OBJS) -lgcc
+
+%.o: %.S
+ $(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -c $< -o $@, AS, $@)
+
+%.o: %.c
+ $(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@, CC, $@)
+
+# Build and link the tests
+%: %.o $(LINK_SCRIPT) $(CRT_OBJS)
+ $(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS), LD, $@)
+
+# FIXME: nios2 semihosting writes to stdout, not a chardev
+QEMU_OPTS = -M 10m50-ghrd,vic=on -semihosting >$@.out -kernel
+
+memory: CFLAGS+=-DCHECK_UNALIGNED=0
+TESTS += $(MULTIARCH_TESTS)
+TESTS += test-shadow-1
diff --git a/tests/tcg/nios2/Makefile.target b/tests/tcg/nios2/Makefile.target
deleted file mode 100644
index b38e2352b7..0000000000
--- a/tests/tcg/nios2/Makefile.target
+++ /dev/null
@@ -1,11 +0,0 @@
-# nios2 specific test tweaks
-
-# Currently nios2 signal handling is broken
-run-signals: signals
- $(call skip-test, $<, "BROKEN")
-run-plugin-signals-with-%:
- $(call skip-test, $<, "BROKEN")
-run-linux-test: linux-test
- $(call skip-test, $<, "BROKEN")
-run-plugin-linux-test-with-%:
- $(call skip-test, $<, "BROKEN")
diff --git a/tests/tcg/nios2/boot.S b/tests/tcg/nios2/boot.S
new file mode 100644
index 0000000000..f6771cbc81
--- /dev/null
+++ b/tests/tcg/nios2/boot.S
@@ -0,0 +1,218 @@
+/*
+ * Minimal Nios2 system boot code.
+ *
+ * Copyright Linaro Ltd 2022
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "semicall.h"
+
+ .text
+ .set noat
+
+_start:
+ /* Linker script defines stack at end of ram. */
+ movia sp, __stack
+
+ /* Install trampoline to _fast_tlb_miss at hardcoded vector. */
+ movia r4, 0xc0000100
+ movia r5, _ftm_tramp
+ movi r6, .L__ftm_end - _ftm_tramp
+ call memcpy
+
+ /* Zero the bss to satisfy C. */
+ movia r4, __bss_start
+ movia r6, __bss_end
+ sub r6, r6, r4
+ movi r5, 0
+ call memset
+
+ /* Test! */
+ call main
+
+ /* Exit with main's return value. */
+ movi r4, HOSTED_EXIT
+ mov r5, r2
+ semihosting_call
+
+ .globl _start
+ .type _start, @function
+ .size _start, . - _start
+
+_ftm_tramp:
+ movia et, _fast_tlb_miss
+ jmp et
+.L__ftm_end:
+
+ .type _ftm_tramp, @function
+ .size _ftm_tramp, . - _ftm_tramp
+
+#define dst r4
+#define src r5
+#define len r6
+
+memcpy:
+ /* Store return value right away, per API */
+ mov r2, dst
+
+ /* Check for both dst and src aligned. */
+ or at, dst, src
+ andi at, at, 3
+ bne at, zero, .L_mc_test1
+
+ /* Copy blocks of 8. */
+
+ movi at, 8
+ bltu len, at, .L_mc_test4
+
+.L_mc_loop8:
+ ldw r8, 0(src)
+ ldw r9, 4(src)
+ addi src, src, 8
+ addi dst, dst, 8
+ subi len, len, 8
+ stw r8, -8(dst)
+ stw r9, -4(dst)
+ bgeu len, at, .L_mc_loop8
+
+ /* Copy final aligned block of 4. */
+
+.L_mc_test4:
+ movi at, 4
+ bltu len, at, .L_mc_test1
+
+ ldw r8, 0(src)
+ addi src, src, 4
+ addi dst, dst, 4
+ subi len, len, 4
+ stw r8, -4(dst)
+
+ /* Copy single bytes to finish. */
+
+.L_mc_test1:
+ beq len, zero, .L_mc_done
+
+.L_mc_loop1:
+ ldb r8, 0(src)
+ addi src, src, 1
+ addi dst, dst, 1
+ subi len, len, 1
+ stb r8, -1(dst)
+ bne len, zero, .L_mc_loop1
+
+.L_mc_done:
+ ret
+
+#undef dst
+#undef src
+#undef len
+
+ .global memcpy
+ .type memcpy, @function
+ .size memcpy, . - memcpy
+
+#define dst r4
+#define val r5
+#define len r6
+
+memset:
+ /* Store return value right away, per API */
+ mov r2, dst
+
+ /* Check for small blocks; fall back to bytewise. */
+ movi r3, 8
+ bltu len, r3, .L_ms_test1
+
+ /* Replicate the byte across the word. */
+ andi val, val, 0xff
+ slli at, val, 8
+ or val, val, at
+ slli at, val, 16
+ or val, val, at
+
+ /* Check for destination alignment; realign if needed. */
+ andi at, dst, 3
+ bne at, zero, .L_ms_align
+
+ /* Set blocks of 8. */
+
+.L_ms_loop8:
+ stw val, 0(dst)
+ stw val, 4(dst)
+ addi dst, dst, 8
+ subi len, len, 8
+ bgeu len, r3, .L_ms_loop8
+
+ /* Set final aligned block of 4. */
+
+.L_ms_test4:
+ movi at, 4
+ bltu len, at, .L_ms_test1
+
+ stw r8, 0(dst)
+ addi dst, dst, 4
+ subi len, len, 4
+ stw r8, -4(dst)
+
+ /* Set single bytes to finish. */
+
+.L_ms_test1:
+ beq len, zero, .L_ms_done
+
+.L_ms_loop1:
+ stb r8, 0(dst)
+ addi dst, dst, 1
+ subi len, len, 1
+ bne len, zero, .L_ms_loop1
+
+.L_ms_done:
+ ret
+
+ /* Realign for a large block, len >= 8. */
+.L_ms_align:
+ andi at, dst, 1
+ beq at, zero, 2f
+
+ stb val, 0(dst)
+ addi dst, dst, 1
+ subi len, len, 1
+
+2: andi at, dst, 2
+ beq at, zero, 4f
+
+ sth val, 0(dst)
+ addi dst, dst, 2
+ subi len, len, 2
+
+4: bgeu len, r3, .L_ms_loop8
+ br .L_ms_test4
+
+#undef dst
+#undef val
+#undef len
+
+ .global memset
+ .type memset, @function
+ .size memset, . - memset
+
+/*
+ * void __sys_outc(char c);
+ */
+__sys_outc:
+ subi sp, sp, 16
+ stb r4, 0(sp) /* buffer[0] = c */
+ movi at, 1
+ stw at, 4(sp) /* STDOUT_FILENO */
+ stw sp, 8(sp) /* buffer */
+ stw at, 12(sp) /* len */
+
+ movi r4, HOSTED_WRITE
+ addi r5, sp, 4
+ semihosting_call
+
+ addi sp, sp, 16
+ ret
+
+ .global __sys_outc
+ .type __sys_outc, @function
+ .size __sys_outc, . - __sys_outc
diff --git a/tests/tcg/nios2/intr.S b/tests/tcg/nios2/intr.S
new file mode 100644
index 0000000000..c1730692ba
--- /dev/null
+++ b/tests/tcg/nios2/intr.S
@@ -0,0 +1,31 @@
+/*
+ * Minimal Nios2 system boot code -- exit on interrupt.
+ *
+ * Copyright Linaro Ltd 2022
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "semicall.h"
+
+ .section .text.intr, "ax"
+ .global _interrupt
+ .type _interrupt, @function
+
+_interrupt:
+ rdctl r5, exception /* extract exception.CAUSE */
+ srli r5, r5, 2
+ movi r4, HOSTED_EXIT
+ semihosting_call
+
+ .size _interrupt, . - _interrupt
+
+ .text
+ .global _fast_tlb_miss
+ .type _fast_tlb_miss, @function
+
+_fast_tlb_miss:
+ movi r5, 32
+ movi r4, HOSTED_EXIT
+ semihosting_call
+
+ .size _fast_tlb_miss, . - _fast_tlb_miss
diff --git a/tests/tcg/nios2/semicall.h b/tests/tcg/nios2/semicall.h
new file mode 100644
index 0000000000..6ad4978099
--- /dev/null
+++ b/tests/tcg/nios2/semicall.h
@@ -0,0 +1,28 @@
+/*
+ * Nios2 semihosting interface.
+ *
+ * Copyright Linaro Ltd 2022
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef SEMICALL_H
+#define SEMICALL_H
+
+#define HOSTED_EXIT 0
+#define HOSTED_INIT_SIM 1
+#define HOSTED_OPEN 2
+#define HOSTED_CLOSE 3
+#define HOSTED_READ 4
+#define HOSTED_WRITE 5
+#define HOSTED_LSEEK 6
+#define HOSTED_RENAME 7
+#define HOSTED_UNLINK 8
+#define HOSTED_STAT 9
+#define HOSTED_FSTAT 10
+#define HOSTED_GETTIMEOFDAY 11
+#define HOSTED_ISATTY 12
+#define HOSTED_SYSTEM 13
+
+#define semihosting_call break 1
+
+#endif /* SEMICALL_H */
diff --git a/tests/tcg/nios2/test-shadow-1.S b/tests/tcg/nios2/test-shadow-1.S
new file mode 100644
index 0000000000..79ef69db12
--- /dev/null
+++ b/tests/tcg/nios2/test-shadow-1.S
@@ -0,0 +1,40 @@
+/*
+ * Regression test for TCG indirect global lowering.
+ *
+ * Copyright Linaro Ltd 2022
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "semicall.h"
+
+ .text
+ .set noat
+ .align 2
+ .globl main
+ .type main, @function
+
+main:
+ /* Initialize r0 in shadow register set 1. */
+ movhi at, 1 /* PRS=1, CRS=0, RSIE=0, PIE=0 */
+ wrctl status, at
+ wrprs zero, zero
+
+ /* Change current register set to 1. */
+ movi at, 1 << 10 /* PRS=0, CRS=1, RSIE=0, PIE=0 */
+ wrctl estatus, at
+ movia ea, 1f
+ eret
+
+ /* Load address for callr, then end TB. */
+1: movia at, 3f
+ br 2f
+
+ /* Test case! TCG abort on indirect lowering across brcond. */
+2: callr at
+
+ /* exit(0) */
+3: movi r4, HOSTED_EXIT
+ movi r5, 0
+ semihosting_call
+
+ .size main, . - main
diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c
index e226fc590f..f8981e5bdf 100644
--- a/tools/virtiofsd/helper.c
+++ b/tools/virtiofsd/helper.c
@@ -188,6 +188,9 @@ void fuse_cmdline_help(void)
" -o announce_submounts Announce sub-mount points to the guest\n"
" -o posix_acl/no_posix_acl Enable/Disable posix_acl. (default: disabled)\n"
" -o security_label/no_security_label Enable/Disable security label. (default: disabled)\n"
+ " -o killpriv_v2/no_killpriv_v2\n"
+ " Enable/Disable FUSE_HANDLE_KILLPRIV_V2.\n"
+ " (default: enabled as long as client supports it)\n"
);
}
diff --git a/ui/cocoa.m b/ui/cocoa.m
index 68bff4ff37..09a62817f2 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -1531,6 +1531,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven
[file cStringUsingEncoding:
NSASCIIStringEncoding],
true, "raw",
+ true, false,
false, 0,
&err);
});
diff --git a/ui/vnc.c b/ui/vnc.c
index b02cb3f405..6a05d06147 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -4058,13 +4058,6 @@ void vnc_display_open(const char *id, Error **errp)
password = qemu_opt_get_bool(opts, "password", false);
}
if (password) {
- if (fips_get_state()) {
- error_setg(errp,
- "VNC password auth disabled due to FIPS mode, "
- "consider using the VeNCrypt or SASL authentication "
- "methods as an alternative");
- goto fail;
- }
if (!qcrypto_cipher_supports(
QCRYPTO_CIPHER_ALG_DES, QCRYPTO_CIPHER_MODE_ECB)) {
error_setg(errp,
diff --git a/util/osdep.c b/util/osdep.c
index c7aec36f22..60fcbbaebe 100644
--- a/util/osdep.c
+++ b/util/osdep.c
@@ -31,8 +31,6 @@
#include "qemu/hw-version.h"
#include "monitor/monitor.h"
-static bool fips_enabled = false;
-
static const char *hw_version = QEMU_HW_VERSION;
int socket_set_cork(int fd, int v)
@@ -514,32 +512,6 @@ const char *qemu_hw_version(void)
return hw_version;
}
-void fips_set_state(bool requested)
-{
-#ifdef __linux__
- if (requested) {
- FILE *fds = fopen("/proc/sys/crypto/fips_enabled", "r");
- if (fds != NULL) {
- fips_enabled = (fgetc(fds) == '1');
- fclose(fds);
- }
- }
-#else
- fips_enabled = false;
-#endif /* __linux__ */
-
-#ifdef _FIPS_DEBUG
- fprintf(stderr, "FIPS mode %s (requested %s)\n",
- (fips_enabled ? "enabled" : "disabled"),
- (requested ? "enabled" : "disabled"));
-#endif
-}
-
-bool fips_get_state(void)
-{
- return fips_enabled;
-}
-
#ifdef _WIN32
static void socket_cleanup(void)
{