aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS25
-rw-r--r--accel/kvm/kvm-all.c35
-rw-r--r--accel/stubs/kvm-stub.c9
-rw-r--r--contrib/vhost-user-gpu/vhost-user-gpu.c6
-rw-r--r--contrib/vhost-user-gpu/vugpu.h8
-rw-r--r--disas/riscv.c4
-rw-r--r--docs/about/deprecated.rst35
-rw-r--r--docs/system/i386/amd-memory-encryption.rst6
-rw-r--r--gdbstub/gdbstub.c5
-rw-r--r--gdbstub/system.c6
-rw-r--r--gdbstub/user.c6
-rw-r--r--hw/char/riscv_htif.c5
-rw-r--r--hw/intc/riscv_aplic.c2
-rw-r--r--hw/mem/memory-device.c196
-rw-r--r--hw/misc/sifive_test.c9
-rw-r--r--hw/riscv/virt.c2
-rw-r--r--hw/virtio/vhost-stub.c9
-rw-r--r--hw/virtio/vhost-user.c21
-rw-r--r--hw/virtio/vhost-vdpa.c1
-rw-r--r--hw/virtio/vhost.c103
-rw-r--r--hw/virtio/virtio-mem-pci.c21
-rw-r--r--hw/virtio/virtio-mem.c330
-rw-r--r--include/exec/cpu-common.h15
-rw-r--r--include/exec/memory.h27
-rw-r--r--include/gdbstub/syscalls.h9
-rw-r--r--include/hw/boards.h14
-rw-r--r--include/hw/mem/memory-device.h57
-rw-r--r--include/hw/virtio/vhost-backend.h9
-rw-r--r--include/hw/virtio/vhost.h3
-rw-r--r--include/hw/virtio/virtio-gpu.h8
-rw-r--r--include/hw/virtio/virtio-mem.h32
-rw-r--r--include/sysemu/cpus.h7
-rw-r--r--include/sysemu/kvm.h4
-rw-r--r--include/sysemu/kvm_int.h1
-rw-r--r--include/sysemu/runstate.h2
-rw-r--r--include/sysemu/sysemu.h2
-rw-r--r--qga/commands-win32.c7
-rw-r--r--qga/commands.c4
-rw-r--r--qga/qapi-schema.json8
-rw-r--r--roms/Makefile2
-rw-r--r--[-rwxr-xr-x]scripts/xml-preprocess.py0
-rw-r--r--stubs/memory_device.c (renamed from stubs/qmp_memory_device.c)10
-rw-r--r--stubs/meson.build2
-rw-r--r--subprojects/libvduse/libvduse.c2
-rw-r--r--subprojects/libvhost-user/libvhost-user.c14
-rw-r--r--system/main.c2
-rw-r--r--system/memory.c35
-rw-r--r--system/physmem.c17
-rw-r--r--system/runstate.c16
-rw-r--r--target/i386/tcg/seg_helper.c2
-rw-r--r--target/riscv/cpu-qom.h1
-rw-r--r--target/riscv/cpu.c1030
-rw-r--r--target/riscv/cpu.h34
-rw-r--r--target/riscv/cpu_helper.c3
-rw-r--r--target/riscv/csr.c1
-rw-r--r--target/riscv/kvm-stub.c30
-rw-r--r--target/riscv/kvm/kvm-cpu.c (renamed from target/riscv/kvm.c)272
-rw-r--r--target/riscv/kvm/kvm_riscv.h (renamed from target/riscv/kvm_riscv.h)2
-rw-r--r--target/riscv/kvm/meson.build1
-rw-r--r--target/riscv/meson.build4
-rw-r--r--target/riscv/tcg/meson.build2
-rw-r--r--target/riscv/tcg/tcg-cpu.c949
-rw-r--r--target/riscv/tcg/tcg-cpu.h27
-rw-r--r--target/riscv/vector_helper.c2
-rw-r--r--tests/avocado/tuxrun_baselines.py32
65 files changed, 2497 insertions, 1048 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index c3cc12d..04e87c20 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -318,8 +318,11 @@ R: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
R: Liu Zhiwei <zhiwei_liu@linux.alibaba.com>
L: qemu-riscv@nongnu.org
S: Supported
+F: configs/targets/riscv*
+F: docs/system/target-riscv.rst
F: target/riscv/
F: hw/riscv/
+F: hw/intc/riscv*
F: include/hw/riscv/
F: linux-user/host/riscv32/
F: linux-user/host/riscv64/
@@ -331,6 +334,7 @@ L: qemu-riscv@nongnu.org
S: Supported
F: target/riscv/insn_trans/trans_xthead.c.inc
F: target/riscv/xthead*.decode
+F: disas/riscv-xthead*
RISC-V XVentanaCondOps extension
M: Philipp Tomsich <philipp.tomsich@vrull.eu>
@@ -338,6 +342,7 @@ L: qemu-riscv@nongnu.org
S: Maintained
F: target/riscv/XVentanaCondOps.decode
F: target/riscv/insn_trans/trans_xventanacondops.c.inc
+F: disas/riscv-xventana*
RENESAS RX CPUs
R: Yoshinori Sato <ysato@users.sourceforge.jp>
@@ -557,6 +562,7 @@ M: Cornelia Huck <cohuck@redhat.com>
M: Paolo Bonzini <pbonzini@redhat.com>
S: Maintained
F: linux-headers/
+F: include/standard-headers/
F: scripts/update-linux-headers.sh
POSIX
@@ -939,6 +945,9 @@ R: Marcin Juszkiewicz <marcin.juszkiewicz@linaro.org>
L: qemu-arm@nongnu.org
S: Maintained
F: hw/arm/sbsa-ref.c
+F: hw/misc/sbsa_ec.c
+F: hw/watchdog/sbsa_gwdt.c
+F: include/hw/watchdog/sbsa_gwdt.h
F: docs/system/arm/sbsa.rst
F: tests/avocado/machine_aarch64_sbsaref.py
@@ -1286,8 +1295,10 @@ M: Hervé Poussineau <hpoussin@reactos.org>
R: Aleksandar Rikalo <aleksandar.rikalo@syrmia.com>
S: Maintained
F: hw/mips/jazz.c
+F: hw/display/g364fb.c
F: hw/display/jazz_led.c
F: hw/dma/rc4030.c
+F: hw/nvram/ds1225y.c
Malta
M: Philippe Mathieu-Daudé <philmd@linaro.org>
@@ -1525,6 +1536,7 @@ Microchip PolarFire SoC Icicle Kit
M: Bin Meng <bin.meng@windriver.com>
L: qemu-riscv@nongnu.org
S: Supported
+F: docs/system/riscv/microchip-icicle-kit.rst
F: hw/riscv/microchip_pfsoc.c
F: hw/char/mchp_pfsoc_mmuart.c
F: hw/misc/mchp_pfsoc_dmc.c
@@ -1540,6 +1552,7 @@ Shakti C class SoC
M: Vijai Kumar K <vijai@behindbytes.com>
L: qemu-riscv@nongnu.org
S: Supported
+F: docs/system/riscv/shakti-c.rst
F: hw/riscv/shakti_c.c
F: hw/char/shakti_uart.c
F: include/hw/riscv/shakti_c.h
@@ -1551,6 +1564,7 @@ M: Bin Meng <bin.meng@windriver.com>
M: Palmer Dabbelt <palmer@dabbelt.com>
L: qemu-riscv@nongnu.org
S: Supported
+F: docs/system/riscv/sifive_u.rst
F: hw/*/*sifive*.c
F: include/hw/*/*sifive*.h
@@ -1978,6 +1992,7 @@ M: Marc-André Lureau <marcandre.lureau@redhat.com>
R: Paolo Bonzini <pbonzini@redhat.com>
S: Odd Fixes
F: hw/char/
+F: include/hw/char/
Network devices
M: Jason Wang <jasowang@redhat.com>
@@ -2876,6 +2891,7 @@ F: hw/mem/pc-dimm.c
F: include/hw/mem/memory-device.h
F: include/hw/mem/nvdimm.h
F: include/hw/mem/pc-dimm.h
+F: stubs/memory_device.c
F: docs/nvdimm.txt
SPICE
@@ -3406,6 +3422,12 @@ M: Viktor Prutyanov <viktor.prutyanov@phystech.edu>
S: Maintained
F: contrib/elf2dmp/
+Overall sensors
+M: Philippe Mathieu-Daudé <philmd@linaro.org>
+S: Odd Fixes
+F: hw/sensor
+F: include/hw/sensor
+
I2C and SMBus
M: Corey Minyard <cminyard@mvista.com>
S: Maintained
@@ -3571,7 +3593,7 @@ M: Alistair Francis <Alistair.Francis@wdc.com>
L: qemu-riscv@nongnu.org
S: Maintained
F: tcg/riscv/
-F: disas/riscv.c
+F: disas/riscv.[ch]
S390 TCG target
M: Richard Henderson <richard.henderson@linaro.org>
@@ -3907,6 +3929,7 @@ F: .github/workflows/lockdown.yml
F: .gitlab-ci.yml
F: .gitlab-ci.d/
F: .travis.yml
+F: docs/devel/ci*
F: scripts/ci/
F: tests/docker/
F: tests/vm/
diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index 72e1d11..3f7eafe 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -174,13 +174,31 @@ void kvm_resample_fd_notify(int gsi)
}
}
-int kvm_get_max_memslots(void)
+unsigned int kvm_get_max_memslots(void)
{
KVMState *s = KVM_STATE(current_accel());
return s->nr_slots;
}
+unsigned int kvm_get_free_memslots(void)
+{
+ unsigned int used_slots = 0;
+ KVMState *s = kvm_state;
+ int i;
+
+ kvm_slots_lock();
+ for (i = 0; i < s->nr_as; i++) {
+ if (!s->as[i].ml) {
+ continue;
+ }
+ used_slots = MAX(used_slots, s->as[i].ml->nr_used_slots);
+ }
+ kvm_slots_unlock();
+
+ return s->nr_slots - used_slots;
+}
+
/* Called with KVMMemoryListener.slots_lock held */
static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml)
{
@@ -196,19 +214,6 @@ static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml)
return NULL;
}
-bool kvm_has_free_slot(MachineState *ms)
-{
- KVMState *s = KVM_STATE(ms->accelerator);
- bool result;
- KVMMemoryListener *kml = &s->memory_listener;
-
- kvm_slots_lock();
- result = !!kvm_get_free_slot(kml);
- kvm_slots_unlock();
-
- return result;
-}
-
/* Called with KVMMemoryListener.slots_lock held */
static KVMSlot *kvm_alloc_slot(KVMMemoryListener *kml)
{
@@ -1387,6 +1392,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
}
start_addr += slot_size;
size -= slot_size;
+ kml->nr_used_slots--;
} while (size);
return;
}
@@ -1412,6 +1418,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
ram_start_offset += slot_size;
ram += slot_size;
size -= slot_size;
+ kml->nr_used_slots++;
} while (size);
}
diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c
index 235dc66..51f522e 100644
--- a/accel/stubs/kvm-stub.c
+++ b/accel/stubs/kvm-stub.c
@@ -109,9 +109,14 @@ int kvm_irqchip_remove_irqfd_notifier_gsi(KVMState *s, EventNotifier *n,
return -ENOSYS;
}
-bool kvm_has_free_slot(MachineState *ms)
+unsigned int kvm_get_max_memslots(void)
{
- return false;
+ return 0;
+}
+
+unsigned int kvm_get_free_memslots(void)
+{
+ return 0;
}
void kvm_init_cpu_signals(CPUState *cpu)
diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c
index aa30447..bb41758 100644
--- a/contrib/vhost-user-gpu/vhost-user-gpu.c
+++ b/contrib/vhost-user-gpu/vhost-user-gpu.c
@@ -834,7 +834,7 @@ vg_resource_flush(VuGpu *g,
.width = width,
.height = height,
};
- pixman_image_t *i =
+ pixman_image_t *img =
pixman_image_create_bits(pixman_image_get_format(res->image),
msg->payload.update.width,
msg->payload.update.height,
@@ -842,11 +842,11 @@ vg_resource_flush(VuGpu *g,
payload.update.data),
width * bpp);
pixman_image_composite(PIXMAN_OP_SRC,
- res->image, NULL, i,
+ res->image, NULL, img,
extents->x1, extents->y1,
0, 0, 0, 0,
width, height);
- pixman_image_unref(i);
+ pixman_image_unref(img);
vg_send_msg(g, msg, -1);
g_free(msg);
}
diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h
index 509b679..654c392 100644
--- a/contrib/vhost-user-gpu/vugpu.h
+++ b/contrib/vhost-user-gpu/vugpu.h
@@ -164,12 +164,12 @@ struct virtio_gpu_ctrl_command {
};
#define VUGPU_FILL_CMD(out) do { \
- size_t s; \
- s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, \
+ size_t vugpufillcmd_s_ = \
+ iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, \
&out, sizeof(out)); \
- if (s != sizeof(out)) { \
+ if (vugpufillcmd_s_ != sizeof(out)) { \
g_critical("%s: command size incorrect %zu vs %zu", \
- __func__, s, sizeof(out)); \
+ __func__, vugpufillcmd_s_, sizeof(out)); \
return; \
} \
} while (0)
diff --git a/disas/riscv.c b/disas/riscv.c
index 3873a69..8e89e1d 100644
--- a/disas/riscv.c
+++ b/disas/riscv.c
@@ -2116,8 +2116,8 @@ static const char *csr_name(int csrno)
case 0x03ba: return "pmpaddr10";
case 0x03bb: return "pmpaddr11";
case 0x03bc: return "pmpaddr12";
- case 0x03bd: return "pmpaddr14";
- case 0x03be: return "pmpaddr13";
+ case 0x03bd: return "pmpaddr13";
+ case 0x03be: return "pmpaddr14";
case 0x03bf: return "pmpaddr15";
case 0x0780: return "mtohost";
case 0x0781: return "mfromhost";
diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst
index 8b13632..1c4d7f3 100644
--- a/docs/about/deprecated.rst
+++ b/docs/about/deprecated.rst
@@ -327,6 +327,41 @@ QEMU's ``vhost`` feature, which would eliminate the high latency costs under
which the 9p ``proxy`` backend currently suffers. However as of to date nobody
has indicated plans for such kind of reimplementation unfortunately.
+RISC-V 'any' CPU type ``-cpu any`` (since 8.2)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The 'any' CPU type was introduced back in 2018 and has been around since the
+initial RISC-V QEMU port. Its usage has always been unclear: users don't know
+what to expect from a CPU called 'any', and in fact the CPU does not do anything
+special that isn't already done by the default CPUs rv32/rv64.
+
+After the introduction of the 'max' CPU type, RISC-V now has a good coverage
+of generic CPUs: rv32 and rv64 as default CPUs and 'max' as a feature complete
+CPU for both 32 and 64 bit builds. Users are then discouraged to use the 'any'
+CPU type starting in 8.2.
+
+RISC-V CPU properties which start with capital 'Z' (since 8.2)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+All RISC-V CPU properties which start with capital 'Z' are being deprecated
+starting in 8.2. The reason is that they were wrongly added with capital 'Z'
+in the past. CPU properties were later added with lower-case names, which
+is the format we want to use from now on.
+
+Users which try to use these deprecated properties will receive a warning
+recommending to switch to their stable counterparts:
+
+- "Zifencei" should be replaced with "zifencei"
+- "Zicsr" should be replaced with "zicsr"
+- "Zihintntl" should be replaced with "zihintntl"
+- "Zihintpause" should be replaced with "zihintpause"
+- "Zawrs" should be replaced with "zawrs"
+- "Zfa" should be replaced with "zfa"
+- "Zfh" should be replaced with "zfh"
+- "Zfhmin" should be replaced with "zfhmin"
+- "Zve32f" should be replaced with "zve32f"
+- "Zve64f" should be replaced with "zve64f"
+- "Zve64d" should be replaced with "zve64d"
Block device options
''''''''''''''''''''
diff --git a/docs/system/i386/amd-memory-encryption.rst b/docs/system/i386/amd-memory-encryption.rst
index dcf4add..e9bc142 100644
--- a/docs/system/i386/amd-memory-encryption.rst
+++ b/docs/system/i386/amd-memory-encryption.rst
@@ -183,13 +183,13 @@ References
----------
`AMD Memory Encryption whitepaper
-<https://developer.amd.com/wordpress/media/2013/12/AMD_Memory_Encryption_Whitepaper_v7-Public.pdf>`_
+<https://www.amd.com/content/dam/amd/en/documents/epyc-business-docs/white-papers/memory-encryption-white-paper.pdf>`_
.. [SEVAPI] `Secure Encrypted Virtualization API
<https://www.amd.com/system/files/TechDocs/55766_SEV-KM_API_Specification.pdf>`_
.. [APMVOL2] `AMD64 Architecture Programmer's Manual Volume 2: System Programming
- <https://www.amd.com/system/files/TechDocs/24593.pdf>`_
+ <https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/programmer-references/24593.pdf>`_
KVM Forum slides:
@@ -199,7 +199,7 @@ KVM Forum slides:
<https://www.linux-kvm.org/images/9/94/Extending-Secure-Encrypted-Virtualization-with-SEV-ES-Thomas-Lendacky-AMD.pdf>`_
`AMD64 Architecture Programmer's Manual:
-<http://support.amd.com/TechDocs/24593.pdf>`_
+<https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/programmer-references/24593.pdf>`_
* SME is section 7.10
* SEV is section 15.34
diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index b153211..1e96a71 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -1324,7 +1324,7 @@ static void handle_v_kill(GArray *params, void *user_ctx)
gdb_put_packet("OK");
error_report("QEMU: Terminated via GDBstub");
gdb_exit(0);
- exit(0);
+ gdb_qemu_exit(0);
}
static const GdbCmdParseEntry gdb_v_commands_table[] = {
@@ -1843,7 +1843,8 @@ static int gdb_handle_packet(const char *line_buf)
/* Kill the target */
error_report("QEMU: Terminated via GDBstub");
gdb_exit(0);
- exit(0);
+ gdb_qemu_exit(0);
+ break;
case 'D':
{
static const GdbCmdParseEntry detach_cmd_desc = {
diff --git a/gdbstub/system.c b/gdbstub/system.c
index 4897687..783ac14 100644
--- a/gdbstub/system.c
+++ b/gdbstub/system.c
@@ -435,6 +435,12 @@ void gdb_exit(int code)
qemu_chr_fe_deinit(&gdbserver_system_state.chr, true);
}
+void gdb_qemu_exit(int code)
+{
+ qemu_system_shutdown_request_with_code(SHUTDOWN_CAUSE_GUEST_SHUTDOWN,
+ code);
+}
+
/*
* Memory access
*/
diff --git a/gdbstub/user.c b/gdbstub/user.c
index 7ab6e5d..dbe1d9b 100644
--- a/gdbstub/user.c
+++ b/gdbstub/user.c
@@ -113,6 +113,12 @@ void gdb_exit(int code)
gdb_put_packet(buf);
gdbserver_state.allow_stop_reply = false;
}
+
+}
+
+void gdb_qemu_exit(int code)
+{
+ exit(code);
}
int gdb_handlesig(CPUState *cpu, int sig)
diff --git a/hw/char/riscv_htif.c b/hw/char/riscv_htif.c
index 40de6b8..9bef60d 100644
--- a/hw/char/riscv_htif.c
+++ b/hw/char/riscv_htif.c
@@ -32,6 +32,7 @@
#include "exec/address-spaces.h"
#include "exec/tswap.h"
#include "sysemu/dma.h"
+#include "sysemu/runstate.h"
#define RISCV_DEBUG_HTIF 0
#define HTIF_DEBUG(fmt, ...) \
@@ -206,7 +207,9 @@ static void htif_handle_tohost_write(HTIFState *s, uint64_t val_written)
g_free(sig_data);
}
- exit(exit_code);
+ qemu_system_shutdown_request_with_code(
+ SHUTDOWN_CAUSE_GUEST_SHUTDOWN, exit_code);
+ return;
} else {
uint64_t syscall[8];
cpu_physical_memory_read(payload, syscall, sizeof(syscall));
diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c
index 99aae8c..c677b5c 100644
--- a/hw/intc/riscv_aplic.c
+++ b/hw/intc/riscv_aplic.c
@@ -32,7 +32,7 @@
#include "target/riscv/cpu.h"
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
-#include "kvm_riscv.h"
+#include "kvm/kvm_riscv.h"
#include "migration/vmstate.h"
#define APLIC_MAX_IDC (1UL << 14)
diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c
index 667d56b..ae38f48 100644
--- a/hw/mem/memory-device.c
+++ b/hw/mem/memory-device.c
@@ -52,19 +52,135 @@ static int memory_device_build_list(Object *obj, void *opaque)
return 0;
}
-static void memory_device_check_addable(MachineState *ms, MemoryRegion *mr,
- Error **errp)
+static unsigned int memory_device_get_memslots(MemoryDeviceState *md)
{
+ const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md);
+
+ if (mdc->get_memslots) {
+ return mdc->get_memslots(md);
+ }
+ return 1;
+}
+
+/*
+ * Memslots that are reserved by memory devices (required but still reported
+ * as free from KVM / vhost).
+ */
+static unsigned int get_reserved_memslots(MachineState *ms)
+{
+ if (ms->device_memory->used_memslots >
+ ms->device_memory->required_memslots) {
+ /* This is unexpected, and we warned already in the memory notifier. */
+ return 0;
+ }
+ return ms->device_memory->required_memslots -
+ ms->device_memory->used_memslots;
+}
+
+unsigned int memory_devices_get_reserved_memslots(void)
+{
+ if (!current_machine->device_memory) {
+ return 0;
+ }
+ return get_reserved_memslots(current_machine);
+}
+
+bool memory_devices_memslot_auto_decision_active(void)
+{
+ if (!current_machine->device_memory) {
+ return false;
+ }
+
+ return current_machine->device_memory->memslot_auto_decision_active;
+}
+
+static unsigned int memory_device_memslot_decision_limit(MachineState *ms,
+ MemoryRegion *mr)
+{
+ const unsigned int reserved = get_reserved_memslots(ms);
+ const uint64_t size = memory_region_size(mr);
+ unsigned int max = vhost_get_max_memslots();
+ unsigned int free = vhost_get_free_memslots();
+ uint64_t available_space;
+ unsigned int memslots;
+
+ if (kvm_enabled()) {
+ max = MIN(max, kvm_get_max_memslots());
+ free = MIN(free, kvm_get_free_memslots());
+ }
+
+ /*
+ * If we only have less overall memslots than what we consider reasonable,
+ * just keep it to a minimum.
+ */
+ if (max < MEMORY_DEVICES_SAFE_MAX_MEMSLOTS) {
+ return 1;
+ }
+
+ /*
+ * Consider our soft-limit across all memory devices. We don't really
+ * expect to exceed this limit in reasonable configurations.
+ */
+ if (MEMORY_DEVICES_SOFT_MEMSLOT_LIMIT <=
+ ms->device_memory->required_memslots) {
+ return 1;
+ }
+ memslots = MEMORY_DEVICES_SOFT_MEMSLOT_LIMIT -
+ ms->device_memory->required_memslots;
+
+ /*
+ * Consider the actually still free memslots. This is only relevant if
+ * other memslot consumers would consume *significantly* more memslots than
+ * what we prepared for (> 253). Unlikely, but let's just handle it
+ * cleanly.
+ */
+ memslots = MIN(memslots, free - reserved);
+ if (memslots < 1 || unlikely(free < reserved)) {
+ return 1;
+ }
+
+ /* We cannot have any other memory devices? So give all to this device. */
+ if (size == ms->maxram_size - ms->ram_size) {
+ return memslots;
+ }
+
+ /*
+ * Simple heuristic: equally distribute the memslots over the space
+ * still available for memory devices.
+ */
+ available_space = ms->maxram_size - ms->ram_size -
+ ms->device_memory->used_region_size;
+ memslots = (double)memslots * size / available_space;
+ return memslots < 1 ? 1 : memslots;
+}
+
+static void memory_device_check_addable(MachineState *ms, MemoryDeviceState *md,
+ MemoryRegion *mr, Error **errp)
+{
+ const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md);
const uint64_t used_region_size = ms->device_memory->used_region_size;
const uint64_t size = memory_region_size(mr);
+ const unsigned int reserved_memslots = get_reserved_memslots(ms);
+ unsigned int required_memslots, memslot_limit;
+
+ /*
+ * Instruct the device to decide how many memslots to use, if applicable,
+ * before we query the number of required memslots the first time.
+ */
+ if (mdc->decide_memslots) {
+ memslot_limit = memory_device_memslot_decision_limit(ms, mr);
+ mdc->decide_memslots(md, memslot_limit);
+ }
+ required_memslots = memory_device_get_memslots(md);
- /* we will need a new memory slot for kvm and vhost */
- if (kvm_enabled() && !kvm_has_free_slot(ms)) {
- error_setg(errp, "hypervisor has no free memory slots left");
+ /* we will need memory slots for kvm and vhost */
+ if (kvm_enabled() &&
+ kvm_get_free_memslots() < required_memslots + reserved_memslots) {
+ error_setg(errp, "hypervisor has not enough free memory slots left");
return;
}
- if (!vhost_has_free_slot()) {
- error_setg(errp, "a used vhost backend has no free memory slots left");
+ if (vhost_get_free_memslots() < required_memslots + reserved_memslots) {
+ error_setg(errp, "a used vhost backend has not enough free memory slots left");
return;
}
@@ -233,7 +349,7 @@ void memory_device_pre_plug(MemoryDeviceState *md, MachineState *ms,
goto out;
}
- memory_device_check_addable(ms, mr, &local_err);
+ memory_device_check_addable(ms, md, mr, &local_err);
if (local_err) {
goto out;
}
@@ -264,6 +380,7 @@ out:
void memory_device_plug(MemoryDeviceState *md, MachineState *ms)
{
const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md);
+ const unsigned int memslots = memory_device_get_memslots(md);
const uint64_t addr = mdc->get_addr(md);
MemoryRegion *mr;
@@ -275,6 +392,11 @@ void memory_device_plug(MemoryDeviceState *md, MachineState *ms)
g_assert(ms->device_memory);
ms->device_memory->used_region_size += memory_region_size(mr);
+ ms->device_memory->required_memslots += memslots;
+ if (mdc->decide_memslots && memslots > 1) {
+ ms->device_memory->memslot_auto_decision_active++;
+ }
+
memory_region_add_subregion(&ms->device_memory->mr,
addr - ms->device_memory->base, mr);
trace_memory_device_plug(DEVICE(md)->id ? DEVICE(md)->id : "", addr);
@@ -283,6 +405,7 @@ void memory_device_plug(MemoryDeviceState *md, MachineState *ms)
void memory_device_unplug(MemoryDeviceState *md, MachineState *ms)
{
const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md);
+ const unsigned int memslots = memory_device_get_memslots(md);
MemoryRegion *mr;
/*
@@ -293,7 +416,12 @@ void memory_device_unplug(MemoryDeviceState *md, MachineState *ms)
g_assert(ms->device_memory);
memory_region_del_subregion(&ms->device_memory->mr, mr);
+
+ if (mdc->decide_memslots && memslots > 1) {
+ ms->device_memory->memslot_auto_decision_active--;
+ }
ms->device_memory->used_region_size -= memory_region_size(mr);
+ ms->device_memory->required_memslots -= memslots;
trace_memory_device_unplug(DEVICE(md)->id ? DEVICE(md)->id : "",
mdc->get_addr(md));
}
@@ -313,6 +441,50 @@ uint64_t memory_device_get_region_size(const MemoryDeviceState *md,
return memory_region_size(mr);
}
+static void memory_devices_region_mod(MemoryListener *listener,
+ MemoryRegionSection *mrs, bool add)
+{
+ DeviceMemoryState *dms = container_of(listener, DeviceMemoryState,
+ listener);
+
+ if (!memory_region_is_ram(mrs->mr)) {
+ warn_report("Unexpected memory region mapped into device memory region.");
+ return;
+ }
+
+ /*
+ * The expectation is that each distinct RAM memory region section in
+ * our region for memory devices consumes exactly one memslot in KVM
+ * and in vhost. For vhost, this is true, except:
+ * * ROM memory regions don't consume a memslot. These get used very
+ * rarely for memory devices (R/O NVDIMMs).
+ * * Memslots without a fd (memory-backend-ram) don't necessarily
+ * consume a memslot. Such setups are quite rare and possibly bogus:
+ * the memory would be inaccessible by such vhost devices.
+ *
+ * So for vhost, in corner cases we might over-estimate the number of
+ * memslots that are currently used or that might still be reserved
+ * (required - used).
+ */
+ dms->used_memslots += add ? 1 : -1;
+
+ if (dms->used_memslots > dms->required_memslots) {
+ warn_report("Memory devices use more memory slots than indicated as required.");
+ }
+}
+
+static void memory_devices_region_add(MemoryListener *listener,
+ MemoryRegionSection *mrs)
+{
+ return memory_devices_region_mod(listener, mrs, true);
+}
+
+static void memory_devices_region_del(MemoryListener *listener,
+ MemoryRegionSection *mrs)
+{
+ return memory_devices_region_mod(listener, mrs, false);
+}
+
void machine_memory_devices_init(MachineState *ms, hwaddr base, uint64_t size)
{
g_assert(size);
@@ -322,8 +494,16 @@ void machine_memory_devices_init(MachineState *ms, hwaddr base, uint64_t size)
memory_region_init(&ms->device_memory->mr, OBJECT(ms), "device-memory",
size);
+ address_space_init(&ms->device_memory->as, &ms->device_memory->mr,
+ "device-memory");
memory_region_add_subregion(get_system_memory(), ms->device_memory->base,
&ms->device_memory->mr);
+
+ /* Track the number of memslots used by memory devices. */
+ ms->device_memory->listener.region_add = memory_devices_region_add;
+ ms->device_memory->listener.region_del = memory_devices_region_del;
+ memory_listener_register(&ms->device_memory->listener,
+ &ms->device_memory->as);
}
static const TypeInfo memory_device_info = {
diff --git a/hw/misc/sifive_test.c b/hw/misc/sifive_test.c
index 56df45b..ad68807 100644
--- a/hw/misc/sifive_test.c
+++ b/hw/misc/sifive_test.c
@@ -25,6 +25,7 @@
#include "qemu/module.h"
#include "sysemu/runstate.h"
#include "hw/misc/sifive_test.h"
+#include "sysemu/sysemu.h"
static uint64_t sifive_test_read(void *opaque, hwaddr addr, unsigned int size)
{
@@ -39,9 +40,13 @@ static void sifive_test_write(void *opaque, hwaddr addr,
int code = (val64 >> 16) & 0xffff;
switch (status) {
case FINISHER_FAIL:
- exit(code);
+ qemu_system_shutdown_request_with_code(
+ SHUTDOWN_CAUSE_GUEST_PANIC, code);
+ return;
case FINISHER_PASS:
- exit(0);
+ qemu_system_shutdown_request_with_code(
+ SHUTDOWN_CAUSE_GUEST_SHUTDOWN, code);
+ return;
case FINISHER_RESET:
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
return;
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index 5edc1d9..9de578c 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -35,7 +35,7 @@
#include "hw/riscv/virt.h"
#include "hw/riscv/boot.h"
#include "hw/riscv/numa.h"
-#include "kvm_riscv.h"
+#include "kvm/kvm_riscv.h"
#include "hw/intc/riscv_aclint.h"
#include "hw/intc/riscv_aplic.h"
#include "hw/intc/riscv_imsic.h"
diff --git a/hw/virtio/vhost-stub.c b/hw/virtio/vhost-stub.c
index aa858ef..52d42ad 100644
--- a/hw/virtio/vhost-stub.c
+++ b/hw/virtio/vhost-stub.c
@@ -2,9 +2,14 @@
#include "hw/virtio/vhost.h"
#include "hw/virtio/vhost-user.h"
-bool vhost_has_free_slot(void)
+unsigned int vhost_get_max_memslots(void)
{
- return true;
+ return UINT_MAX;
+}
+
+unsigned int vhost_get_free_memslots(void)
+{
+ return UINT_MAX;
}
bool vhost_user_init(VhostUserState *user, CharBackend *chr, Error **errp)
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 3766b41..68eb1f0 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -2327,19 +2327,6 @@ static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr)
return -ENOTSUP;
}
-static bool vhost_user_can_merge(struct vhost_dev *dev,
- uint64_t start1, uint64_t size1,
- uint64_t start2, uint64_t size2)
-{
- ram_addr_t offset;
- int mfd, rfd;
-
- (void)vhost_user_get_mr_data(start1, &offset, &mfd);
- (void)vhost_user_get_mr_data(start2, &offset, &rfd);
-
- return mfd == rfd;
-}
-
static int vhost_user_net_set_mtu(struct vhost_dev *dev, uint16_t mtu)
{
VhostUserMsg msg;
@@ -2622,10 +2609,9 @@ vhost_user_crypto_close_session(struct vhost_dev *dev, uint64_t session_id)
return 0;
}
-static bool vhost_user_mem_section_filter(struct vhost_dev *dev,
- MemoryRegionSection *section)
+static bool vhost_user_no_private_memslots(struct vhost_dev *dev)
{
- return memory_region_get_fd(section->mr) >= 0;
+ return true;
}
static int vhost_user_get_inflight_fd(struct vhost_dev *dev,
@@ -2868,6 +2854,7 @@ const VhostOps user_ops = {
.vhost_backend_init = vhost_user_backend_init,
.vhost_backend_cleanup = vhost_user_backend_cleanup,
.vhost_backend_memslots_limit = vhost_user_memslots_limit,
+ .vhost_backend_no_private_memslots = vhost_user_no_private_memslots,
.vhost_set_log_base = vhost_user_set_log_base,
.vhost_set_mem_table = vhost_user_set_mem_table,
.vhost_set_vring_addr = vhost_user_set_vring_addr,
@@ -2886,7 +2873,6 @@ const VhostOps user_ops = {
.vhost_set_vring_enable = vhost_user_set_vring_enable,
.vhost_requires_shm_log = vhost_user_requires_shm_log,
.vhost_migration_done = vhost_user_migration_done,
- .vhost_backend_can_merge = vhost_user_can_merge,
.vhost_net_set_mtu = vhost_user_net_set_mtu,
.vhost_set_iotlb_callback = vhost_user_set_iotlb_callback,
.vhost_send_device_iotlb_msg = vhost_user_send_device_iotlb_msg,
@@ -2894,7 +2880,6 @@ const VhostOps user_ops = {
.vhost_set_config = vhost_user_set_config,
.vhost_crypto_create_session = vhost_user_crypto_create_session,
.vhost_crypto_close_session = vhost_user_crypto_close_session,
- .vhost_backend_mem_section_filter = vhost_user_mem_section_filter,
.vhost_get_inflight_fd = vhost_user_get_inflight_fd,
.vhost_set_inflight_fd = vhost_user_set_inflight_fd,
.vhost_dev_start = vhost_user_dev_start,
diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c
index 89ff02a..819b2d8 100644
--- a/hw/virtio/vhost-vdpa.c
+++ b/hw/virtio/vhost-vdpa.c
@@ -1512,7 +1512,6 @@ const VhostOps vdpa_ops = {
.vhost_set_config = vhost_vdpa_set_config,
.vhost_requires_shm_log = NULL,
.vhost_migration_done = NULL,
- .vhost_backend_can_merge = NULL,
.vhost_net_set_mtu = NULL,
.vhost_set_iotlb_callback = NULL,
.vhost_send_device_iotlb_msg = NULL,
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 9cfac40..9f37206 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -23,6 +23,7 @@
#include "qemu/log.h"
#include "standard-headers/linux/vhost_types.h"
#include "hw/virtio/virtio-bus.h"
+#include "hw/mem/memory-device.h"
#include "migration/blocker.h"
#include "migration/qemu-file-types.h"
#include "sysemu/dma.h"
@@ -45,20 +46,44 @@
static struct vhost_log *vhost_log;
static struct vhost_log *vhost_log_shm;
+/* Memslots used by backends that support private memslots (without an fd). */
static unsigned int used_memslots;
+
+/* Memslots used by backends that only support shared memslots (with an fd). */
+static unsigned int used_shared_memslots;
+
static QLIST_HEAD(, vhost_dev) vhost_devices =
QLIST_HEAD_INITIALIZER(vhost_devices);
-bool vhost_has_free_slot(void)
+unsigned int vhost_get_max_memslots(void)
+{
+ unsigned int max = UINT_MAX;
+ struct vhost_dev *hdev;
+
+ QLIST_FOREACH(hdev, &vhost_devices, entry) {
+ max = MIN(max, hdev->vhost_ops->vhost_backend_memslots_limit(hdev));
+ }
+ return max;
+}
+
+unsigned int vhost_get_free_memslots(void)
{
- unsigned int slots_limit = ~0U;
+ unsigned int free = UINT_MAX;
struct vhost_dev *hdev;
QLIST_FOREACH(hdev, &vhost_devices, entry) {
unsigned int r = hdev->vhost_ops->vhost_backend_memslots_limit(hdev);
- slots_limit = MIN(slots_limit, r);
+ unsigned int cur_free;
+
+ if (hdev->vhost_ops->vhost_backend_no_private_memslots &&
+ hdev->vhost_ops->vhost_backend_no_private_memslots(hdev)) {
+ cur_free = r - used_shared_memslots;
+ } else {
+ cur_free = r - used_memslots;
+ }
+ free = MIN(free, cur_free);
}
- return slots_limit > used_memslots;
+ return free;
}
static void vhost_dev_sync_region(struct vhost_dev *dev,
@@ -474,8 +499,7 @@ static int vhost_verify_ring_mappings(struct vhost_dev *dev,
* vhost_section: identify sections needed for vhost access
*
* We only care about RAM sections here (where virtqueue and guest
- * internals accessed by virtio might live). If we find one we still
- * allow the backend to potentially filter it out of our list.
+ * internals accessed by virtio might live).
*/
static bool vhost_section(struct vhost_dev *dev, MemoryRegionSection *section)
{
@@ -502,8 +526,16 @@ static bool vhost_section(struct vhost_dev *dev, MemoryRegionSection *section)
return false;
}
- if (dev->vhost_ops->vhost_backend_mem_section_filter &&
- !dev->vhost_ops->vhost_backend_mem_section_filter(dev, section)) {
+ /*
+ * Some backends (like vhost-user) can only handle memory regions
+ * that have an fd (can be mapped into a different process). Filter
+ * the ones without an fd out, if requested.
+ *
+ * TODO: we might have to limit to MAP_SHARED as well.
+ */
+ if (memory_region_get_fd(section->mr) < 0 &&
+ dev->vhost_ops->vhost_backend_no_private_memslots &&
+ dev->vhost_ops->vhost_backend_no_private_memslots(dev)) {
trace_vhost_reject_section(mr->name, 2);
return false;
}
@@ -568,7 +600,14 @@ static void vhost_commit(MemoryListener *listener)
dev->n_mem_sections * sizeof dev->mem->regions[0];
dev->mem = g_realloc(dev->mem, regions_size);
dev->mem->nregions = dev->n_mem_sections;
- used_memslots = dev->mem->nregions;
+
+ if (dev->vhost_ops->vhost_backend_no_private_memslots &&
+ dev->vhost_ops->vhost_backend_no_private_memslots(dev)) {
+ used_shared_memslots = dev->mem->nregions;
+ } else {
+ used_memslots = dev->mem->nregions;
+ }
+
for (i = 0; i < dev->n_mem_sections; i++) {
struct vhost_memory_region *cur_vmr = dev->mem->regions + i;
struct MemoryRegionSection *mrs = dev->mem_sections + i;
@@ -668,7 +707,7 @@ static void vhost_region_add_section(struct vhost_dev *dev,
mrs_size, mrs_host);
}
- if (dev->n_tmp_sections) {
+ if (dev->n_tmp_sections && !section->unmergeable) {
/* Since we already have at least one section, lets see if
* this extends it; since we're scanning in order, we only
* have to look at the last one, and the FlatView that calls
@@ -701,11 +740,7 @@ static void vhost_region_add_section(struct vhost_dev *dev,
size_t offset = mrs_gpa - prev_gpa_start;
if (prev_host_start + offset == mrs_host &&
- section->mr == prev_sec->mr &&
- (!dev->vhost_ops->vhost_backend_can_merge ||
- dev->vhost_ops->vhost_backend_can_merge(dev,
- mrs_host, mrs_size,
- prev_host_start, prev_size))) {
+ section->mr == prev_sec->mr && !prev_sec->unmergeable) {
uint64_t max_end = MAX(prev_host_end, mrs_host + mrs_size);
need_add = false;
prev_sec->offset_within_address_space =
@@ -1400,6 +1435,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
VhostBackendType backend_type, uint32_t busyloop_timeout,
Error **errp)
{
+ unsigned int used, reserved, limit;
uint64_t features;
int i, r, n_initialized_vqs = 0;
@@ -1426,6 +1462,19 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
goto fail;
}
+ limit = hdev->vhost_ops->vhost_backend_memslots_limit(hdev);
+ if (limit < MEMORY_DEVICES_SAFE_MAX_MEMSLOTS &&
+ memory_devices_memslot_auto_decision_active()) {
+ error_setg(errp, "some memory device (like virtio-mem)"
+ " decided how many memory slots to use based on the overall"
+ " number of memory slots; this vhost backend would further"
+ " restricts the overall number of memory slots");
+ error_append_hint(errp, "Try plugging this vhost backend before"
+ " plugging such memory devices.\n");
+ r = -EINVAL;
+ goto fail;
+ }
+
for (i = 0; i < hdev->nvqs; ++i, ++n_initialized_vqs) {
r = vhost_virtqueue_init(hdev, hdev->vqs + i, hdev->vq_index + i);
if (r < 0) {
@@ -1495,9 +1544,27 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque,
memory_listener_register(&hdev->memory_listener, &address_space_memory);
QLIST_INSERT_HEAD(&vhost_devices, hdev, entry);
- if (used_memslots > hdev->vhost_ops->vhost_backend_memslots_limit(hdev)) {
- error_setg(errp, "vhost backend memory slots limit is less"
- " than current number of present memory slots");
+ /*
+ * The listener we registered properly updated the corresponding counter.
+ * So we can trust that these values are accurate.
+ */
+ if (hdev->vhost_ops->vhost_backend_no_private_memslots &&
+ hdev->vhost_ops->vhost_backend_no_private_memslots(hdev)) {
+ used = used_shared_memslots;
+ } else {
+ used = used_memslots;
+ }
+ /*
+ * We assume that all reserved memslots actually require a real memslot
+ * in our vhost backend. This might not be true, for example, if the
+ * memslot would be ROM. If ever relevant, we can optimize for that --
+ * but we'll need additional information about the reservations.
+ */
+ reserved = memory_devices_get_reserved_memslots();
+ if (used + reserved > limit) {
+ error_setg(errp, "vhost backend memory slots limit (%d) is less"
+ " than current number of used (%d) and reserved (%d)"
+ " memory slots for memory devices.", limit, used, reserved);
r = -EINVAL;
goto fail_busyloop;
}
diff --git a/hw/virtio/virtio-mem-pci.c b/hw/virtio/virtio-mem-pci.c
index c4597e0..1b4e9a3 100644
--- a/hw/virtio/virtio-mem-pci.c
+++ b/hw/virtio/virtio-mem-pci.c
@@ -48,6 +48,25 @@ static MemoryRegion *virtio_mem_pci_get_memory_region(MemoryDeviceState *md,
return vmc->get_memory_region(vmem, errp);
}
+static void virtio_mem_pci_decide_memslots(MemoryDeviceState *md,
+ unsigned int limit)
+{
+ VirtIOMEMPCI *pci_mem = VIRTIO_MEM_PCI(md);
+ VirtIOMEM *vmem = VIRTIO_MEM(&pci_mem->vdev);
+ VirtIOMEMClass *vmc = VIRTIO_MEM_GET_CLASS(vmem);
+
+ vmc->decide_memslots(vmem, limit);
+}
+
+static unsigned int virtio_mem_pci_get_memslots(MemoryDeviceState *md)
+{
+ VirtIOMEMPCI *pci_mem = VIRTIO_MEM_PCI(md);
+ VirtIOMEM *vmem = VIRTIO_MEM(&pci_mem->vdev);
+ VirtIOMEMClass *vmc = VIRTIO_MEM_GET_CLASS(vmem);
+
+ return vmc->get_memslots(vmem);
+}
+
static uint64_t virtio_mem_pci_get_plugged_size(const MemoryDeviceState *md,
Error **errp)
{
@@ -150,6 +169,8 @@ static void virtio_mem_pci_class_init(ObjectClass *klass, void *data)
mdc->set_addr = virtio_mem_pci_set_addr;
mdc->get_plugged_size = virtio_mem_pci_get_plugged_size;
mdc->get_memory_region = virtio_mem_pci_get_memory_region;
+ mdc->decide_memslots = virtio_mem_pci_decide_memslots;
+ mdc->get_memslots = virtio_mem_pci_get_memslots;
mdc->fill_device_info = virtio_mem_pci_fill_device_info;
mdc->get_min_alignment = virtio_mem_pci_get_min_alignment;
diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c
index da5b09c..9dc3c61 100644
--- a/hw/virtio/virtio-mem.c
+++ b/hw/virtio/virtio-mem.c
@@ -67,6 +67,13 @@ static uint32_t virtio_mem_default_thp_size(void)
}
/*
+ * The minimum memslot size depends on this setting ("sane default"), the
+ * device block size, and the memory backend page size. The last (or single)
+ * memslot might be smaller than this constant.
+ */
+#define VIRTIO_MEM_MIN_MEMSLOT_SIZE (1 * GiB)
+
+/*
* We want to have a reasonable default block size such that
* 1. We avoid splitting THPs when unplugging memory, which degrades
* performance.
@@ -177,10 +184,10 @@ static bool virtio_mem_is_busy(void)
return migration_in_incoming_postcopy() || !migration_is_idle();
}
-typedef int (*virtio_mem_range_cb)(const VirtIOMEM *vmem, void *arg,
+typedef int (*virtio_mem_range_cb)(VirtIOMEM *vmem, void *arg,
uint64_t offset, uint64_t size);
-static int virtio_mem_for_each_unplugged_range(const VirtIOMEM *vmem, void *arg,
+static int virtio_mem_for_each_unplugged_range(VirtIOMEM *vmem, void *arg,
virtio_mem_range_cb cb)
{
unsigned long first_zero_bit, last_zero_bit;
@@ -204,7 +211,7 @@ static int virtio_mem_for_each_unplugged_range(const VirtIOMEM *vmem, void *arg,
return ret;
}
-static int virtio_mem_for_each_plugged_range(const VirtIOMEM *vmem, void *arg,
+static int virtio_mem_for_each_plugged_range(VirtIOMEM *vmem, void *arg,
virtio_mem_range_cb cb)
{
unsigned long first_bit, last_bit;
@@ -483,6 +490,96 @@ static bool virtio_mem_valid_range(const VirtIOMEM *vmem, uint64_t gpa,
return true;
}
+static void virtio_mem_activate_memslot(VirtIOMEM *vmem, unsigned int idx)
+{
+ const uint64_t memslot_offset = idx * vmem->memslot_size;
+
+ assert(vmem->memslots);
+
+ /*
+ * Instead of enabling/disabling memslots, we add/remove them. This should
+ * make address space updates faster, because we don't have to loop over
+ * many disabled subregions.
+ */
+ if (memory_region_is_mapped(&vmem->memslots[idx])) {
+ return;
+ }
+ memory_region_add_subregion(vmem->mr, memslot_offset, &vmem->memslots[idx]);
+}
+
+static void virtio_mem_deactivate_memslot(VirtIOMEM *vmem, unsigned int idx)
+{
+ assert(vmem->memslots);
+
+ if (!memory_region_is_mapped(&vmem->memslots[idx])) {
+ return;
+ }
+ memory_region_del_subregion(vmem->mr, &vmem->memslots[idx]);
+}
+
+static void virtio_mem_activate_memslots_to_plug(VirtIOMEM *vmem,
+ uint64_t offset, uint64_t size)
+{
+ const unsigned int start_idx = offset / vmem->memslot_size;
+ const unsigned int end_idx = (offset + size + vmem->memslot_size - 1) /
+ vmem->memslot_size;
+ unsigned int idx;
+
+ if (!vmem->dynamic_memslots) {
+ return;
+ }
+
+ /* Activate all involved memslots in a single transaction. */
+ memory_region_transaction_begin();
+ for (idx = start_idx; idx < end_idx; idx++) {
+ virtio_mem_activate_memslot(vmem, idx);
+ }
+ memory_region_transaction_commit();
+}
+
+static void virtio_mem_deactivate_unplugged_memslots(VirtIOMEM *vmem,
+ uint64_t offset,
+ uint64_t size)
+{
+ const uint64_t region_size = memory_region_size(&vmem->memdev->mr);
+ const unsigned int start_idx = offset / vmem->memslot_size;
+ const unsigned int end_idx = (offset + size + vmem->memslot_size - 1) /
+ vmem->memslot_size;
+ unsigned int idx;
+
+ if (!vmem->dynamic_memslots) {
+ return;
+ }
+
+ /* Deactivate all memslots with unplugged blocks in a single transaction. */
+ memory_region_transaction_begin();
+ for (idx = start_idx; idx < end_idx; idx++) {
+ const uint64_t memslot_offset = idx * vmem->memslot_size;
+ uint64_t memslot_size = vmem->memslot_size;
+
+ /* The size of the last memslot might be smaller. */
+ if (idx == vmem->nb_memslots - 1) {
+ memslot_size = region_size - memslot_offset;
+ }
+
+ /*
+ * Partially covered memslots might still have some blocks plugged and
+ * have to remain active if that's the case.
+ */
+ if (offset > memslot_offset ||
+ offset + size < memslot_offset + memslot_size) {
+ const uint64_t gpa = vmem->addr + memslot_offset;
+
+ if (!virtio_mem_is_range_unplugged(vmem, gpa, memslot_size)) {
+ continue;
+ }
+ }
+
+ virtio_mem_deactivate_memslot(vmem, idx);
+ }
+ memory_region_transaction_commit();
+}
+
static int virtio_mem_set_block_state(VirtIOMEM *vmem, uint64_t start_gpa,
uint64_t size, bool plug)
{
@@ -500,6 +597,8 @@ static int virtio_mem_set_block_state(VirtIOMEM *vmem, uint64_t start_gpa,
}
virtio_mem_notify_unplug(vmem, offset, size);
virtio_mem_set_range_unplugged(vmem, start_gpa, size);
+ /* Deactivate completely unplugged memslots after updating the state. */
+ virtio_mem_deactivate_unplugged_memslots(vmem, offset, size);
return 0;
}
@@ -527,7 +626,20 @@ static int virtio_mem_set_block_state(VirtIOMEM *vmem, uint64_t start_gpa,
}
if (!ret) {
+ /*
+ * Activate before notifying and rollback in case of any errors.
+ *
+ * When activating a yet inactive memslot, memory notifiers will get
+ * notified about the added memory region and can register with the
+ * RamDiscardManager; this will traverse all plugged blocks and skip the
+ * blocks we are plugging here. The following notification will inform
+ * registered listeners about the blocks we're plugging.
+ */
+ virtio_mem_activate_memslots_to_plug(vmem, offset, size);
ret = virtio_mem_notify_plug(vmem, offset, size);
+ if (ret) {
+ virtio_mem_deactivate_unplugged_memslots(vmem, offset, size);
+ }
}
if (ret) {
/* Could be preallocation or a notifier populated memory. */
@@ -620,6 +732,7 @@ static void virtio_mem_resize_usable_region(VirtIOMEM *vmem,
static int virtio_mem_unplug_all(VirtIOMEM *vmem)
{
+ const uint64_t region_size = memory_region_size(&vmem->memdev->mr);
RAMBlock *rb = vmem->memdev->mr.ram_block;
if (vmem->size) {
@@ -634,6 +747,9 @@ static int virtio_mem_unplug_all(VirtIOMEM *vmem)
bitmap_clear(vmem->bitmap, 0, vmem->bitmap_size);
vmem->size = 0;
notifier_list_notify(&vmem->size_change_notifiers, &vmem->size);
+
+ /* Deactivate all memslots after updating the state. */
+ virtio_mem_deactivate_unplugged_memslots(vmem, 0, region_size);
}
trace_virtio_mem_unplugged_all();
@@ -790,6 +906,49 @@ static void virtio_mem_system_reset(void *opaque)
virtio_mem_unplug_all(vmem);
}
+static void virtio_mem_prepare_mr(VirtIOMEM *vmem)
+{
+ const uint64_t region_size = memory_region_size(&vmem->memdev->mr);
+
+ assert(!vmem->mr && vmem->dynamic_memslots);
+ vmem->mr = g_new0(MemoryRegion, 1);
+ memory_region_init(vmem->mr, OBJECT(vmem), "virtio-mem",
+ region_size);
+ vmem->mr->align = memory_region_get_alignment(&vmem->memdev->mr);
+}
+
+static void virtio_mem_prepare_memslots(VirtIOMEM *vmem)
+{
+ const uint64_t region_size = memory_region_size(&vmem->memdev->mr);
+ unsigned int idx;
+
+ g_assert(!vmem->memslots && vmem->nb_memslots && vmem->dynamic_memslots);
+ vmem->memslots = g_new0(MemoryRegion, vmem->nb_memslots);
+
+ /* Initialize our memslots, but don't map them yet. */
+ for (idx = 0; idx < vmem->nb_memslots; idx++) {
+ const uint64_t memslot_offset = idx * vmem->memslot_size;
+ uint64_t memslot_size = vmem->memslot_size;
+ char name[20];
+
+ /* The size of the last memslot might be smaller. */
+ if (idx == vmem->nb_memslots - 1) {
+ memslot_size = region_size - memslot_offset;
+ }
+
+ snprintf(name, sizeof(name), "memslot-%u", idx);
+ memory_region_init_alias(&vmem->memslots[idx], OBJECT(vmem), name,
+ &vmem->memdev->mr, memslot_offset,
+ memslot_size);
+ /*
+ * We want to be able to atomically and efficiently activate/deactivate
+ * individual memslots without affecting adjacent memslots in memory
+ * notifiers.
+ */
+ memory_region_set_unmergeable(&vmem->memslots[idx], true);
+ }
+}
+
static void virtio_mem_device_realize(DeviceState *dev, Error **errp)
{
MachineState *ms = MACHINE(qdev_get_machine());
@@ -861,6 +1020,14 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp)
vmem->unplugged_inaccessible = ON_OFF_AUTO_ON;
#endif /* VIRTIO_MEM_HAS_LEGACY_GUESTS */
+ if (vmem->dynamic_memslots &&
+ vmem->unplugged_inaccessible != ON_OFF_AUTO_ON) {
+ error_setg(errp, "'%s' property set to 'on' requires '%s' to be 'on'",
+ VIRTIO_MEM_DYNAMIC_MEMSLOTS_PROP,
+ VIRTIO_MEM_UNPLUGGED_INACCESSIBLE_PROP);
+ return;
+ }
+
/*
* If the block size wasn't configured by the user, use a sane default. This
* allows using hugetlbfs backends of any page size without manual
@@ -930,6 +1097,25 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp)
virtio_init(vdev, VIRTIO_ID_MEM, sizeof(struct virtio_mem_config));
vmem->vq = virtio_add_queue(vdev, 128, virtio_mem_handle_request);
+ /*
+ * With "dynamic-memslots=off" (old behavior) we always map the whole
+ * RAM memory region directly.
+ */
+ if (vmem->dynamic_memslots) {
+ if (!vmem->mr) {
+ virtio_mem_prepare_mr(vmem);
+ }
+ if (vmem->nb_memslots <= 1) {
+ vmem->nb_memslots = 1;
+ vmem->memslot_size = memory_region_size(&vmem->memdev->mr);
+ }
+ if (!vmem->memslots) {
+ virtio_mem_prepare_memslots(vmem);
+ }
+ } else {
+ assert(!vmem->mr && !vmem->nb_memslots && !vmem->memslots);
+ }
+
host_memory_backend_set_mapped(vmem->memdev, true);
vmstate_register_ram(&vmem->memdev->mr, DEVICE(vmem));
if (vmem->early_migration) {
@@ -969,7 +1155,7 @@ static void virtio_mem_device_unrealize(DeviceState *dev)
ram_block_coordinated_discard_require(false);
}
-static int virtio_mem_discard_range_cb(const VirtIOMEM *vmem, void *arg,
+static int virtio_mem_discard_range_cb(VirtIOMEM *vmem, void *arg,
uint64_t offset, uint64_t size)
{
RAMBlock *rb = vmem->memdev->mr.ram_block;
@@ -984,13 +1170,32 @@ static int virtio_mem_restore_unplugged(VirtIOMEM *vmem)
virtio_mem_discard_range_cb);
}
-static int virtio_mem_post_load(void *opaque, int version_id)
+static int virtio_mem_activate_memslot_range_cb(VirtIOMEM *vmem, void *arg,
+ uint64_t offset, uint64_t size)
+{
+ virtio_mem_activate_memslots_to_plug(vmem, offset, size);
+ return 0;
+}
+
+static int virtio_mem_post_load_bitmap(VirtIOMEM *vmem)
{
- VirtIOMEM *vmem = VIRTIO_MEM(opaque);
RamDiscardListener *rdl;
int ret;
/*
+ * We restored the bitmap and updated the requested size; activate all
+ * memslots (so listeners register) before notifying about plugged blocks.
+ */
+ if (vmem->dynamic_memslots) {
+ /*
+ * We don't expect any active memslots at this point to deactivate: no
+ * memory was plugged on the migration destination.
+ */
+ virtio_mem_for_each_plugged_range(vmem, NULL,
+ virtio_mem_activate_memslot_range_cb);
+ }
+
+ /*
* We started out with all memory discarded and our memory region is mapped
* into an address space. Replay, now that we updated the bitmap.
*/
@@ -1001,6 +1206,20 @@ static int virtio_mem_post_load(void *opaque, int version_id)
return ret;
}
}
+ return 0;
+}
+
+static int virtio_mem_post_load(void *opaque, int version_id)
+{
+ VirtIOMEM *vmem = VIRTIO_MEM(opaque);
+ int ret;
+
+ if (!vmem->early_migration) {
+ ret = virtio_mem_post_load_bitmap(vmem);
+ if (ret) {
+ return ret;
+ }
+ }
/*
* If shared RAM is migrated using the file content and not using QEMU,
@@ -1021,7 +1240,7 @@ static int virtio_mem_post_load(void *opaque, int version_id)
return virtio_mem_restore_unplugged(vmem);
}
-static int virtio_mem_prealloc_range_cb(const VirtIOMEM *vmem, void *arg,
+static int virtio_mem_prealloc_range_cb(VirtIOMEM *vmem, void *arg,
uint64_t offset, uint64_t size)
{
void *area = memory_region_get_ram_ptr(&vmem->memdev->mr) + offset;
@@ -1043,7 +1262,7 @@ static int virtio_mem_post_load_early(void *opaque, int version_id)
int ret;
if (!vmem->prealloc) {
- return 0;
+ goto post_load_bitmap;
}
/*
@@ -1051,7 +1270,7 @@ static int virtio_mem_post_load_early(void *opaque, int version_id)
* don't mess with preallocation and postcopy.
*/
if (migrate_ram_is_ignored(rb)) {
- return 0;
+ goto post_load_bitmap;
}
/*
@@ -1084,7 +1303,10 @@ static int virtio_mem_post_load_early(void *opaque, int version_id)
return -EBUSY;
}
}
- return 0;
+
+post_load_bitmap:
+ /* Finally, update any other state to be consistent with the new bitmap. */
+ return virtio_mem_post_load_bitmap(vmem);
}
typedef struct VirtIOMEMMigSanityChecks {
@@ -1235,11 +1457,79 @@ static MemoryRegion *virtio_mem_get_memory_region(VirtIOMEM *vmem, Error **errp)
if (!vmem->memdev) {
error_setg(errp, "'%s' property must be set", VIRTIO_MEM_MEMDEV_PROP);
return NULL;
+ } else if (vmem->dynamic_memslots) {
+ if (!vmem->mr) {
+ virtio_mem_prepare_mr(vmem);
+ }
+ return vmem->mr;
}
return &vmem->memdev->mr;
}
+static void virtio_mem_decide_memslots(VirtIOMEM *vmem, unsigned int limit)
+{
+ uint64_t region_size, memslot_size, min_memslot_size;
+ unsigned int memslots;
+ RAMBlock *rb;
+
+ if (!vmem->dynamic_memslots) {
+ return;
+ }
+
+ /* We're called exactly once, before realizing the device. */
+ assert(!vmem->nb_memslots);
+
+ /* If realizing the device will fail, just assume a single memslot. */
+ if (limit <= 1 || !vmem->memdev || !vmem->memdev->mr.ram_block) {
+ vmem->nb_memslots = 1;
+ return;
+ }
+
+ rb = vmem->memdev->mr.ram_block;
+ region_size = memory_region_size(&vmem->memdev->mr);
+
+ /*
+ * Determine the default block size now, to determine the minimum memslot
+ * size. We want the minimum slot size to be at least the device block size.
+ */
+ if (!vmem->block_size) {
+ vmem->block_size = virtio_mem_default_block_size(rb);
+ }
+ /* If realizing the device will fail, just assume a single memslot. */
+ if (vmem->block_size < qemu_ram_pagesize(rb) ||
+ !QEMU_IS_ALIGNED(region_size, vmem->block_size)) {
+ vmem->nb_memslots = 1;
+ return;
+ }
+
+ /*
+ * All memslots except the last one have a reasonable minimum size, and
+ * and all memslot sizes are aligned to the device block size.
+ */
+ memslot_size = QEMU_ALIGN_UP(region_size / limit, vmem->block_size);
+ min_memslot_size = MAX(vmem->block_size, VIRTIO_MEM_MIN_MEMSLOT_SIZE);
+ memslot_size = MAX(memslot_size, min_memslot_size);
+
+ memslots = QEMU_ALIGN_UP(region_size, memslot_size) / memslot_size;
+ if (memslots != 1) {
+ vmem->memslot_size = memslot_size;
+ }
+ vmem->nb_memslots = memslots;
+}
+
+static unsigned int virtio_mem_get_memslots(VirtIOMEM *vmem)
+{
+ if (!vmem->dynamic_memslots) {
+ /* Exactly one static RAM memory region. */
+ return 1;
+ }
+
+ /* We're called after instructed to make a decision. */
+ g_assert(vmem->nb_memslots);
+ return vmem->nb_memslots;
+}
+
static void virtio_mem_add_size_change_notifier(VirtIOMEM *vmem,
Notifier *notifier)
{
@@ -1377,6 +1667,21 @@ static void virtio_mem_instance_init(Object *obj)
NULL, NULL);
}
+static void virtio_mem_instance_finalize(Object *obj)
+{
+ VirtIOMEM *vmem = VIRTIO_MEM(obj);
+
+ /*
+ * Note: the core already dropped the references on all memory regions
+ * (it's passed as the owner to memory_region_init_*()) and finalized
+ * these objects. We can simply free the memory.
+ */
+ g_free(vmem->memslots);
+ vmem->memslots = NULL;
+ g_free(vmem->mr);
+ vmem->mr = NULL;
+}
+
static Property virtio_mem_properties[] = {
DEFINE_PROP_UINT64(VIRTIO_MEM_ADDR_PROP, VirtIOMEM, addr, 0),
DEFINE_PROP_UINT32(VIRTIO_MEM_NODE_PROP, VirtIOMEM, node, 0),
@@ -1389,6 +1694,8 @@ static Property virtio_mem_properties[] = {
#endif
DEFINE_PROP_BOOL(VIRTIO_MEM_EARLY_MIGRATION_PROP, VirtIOMEM,
early_migration, true),
+ DEFINE_PROP_BOOL(VIRTIO_MEM_DYNAMIC_MEMSLOTS_PROP, VirtIOMEM,
+ dynamic_memslots, false),
DEFINE_PROP_END_OF_LIST(),
};
@@ -1556,6 +1863,8 @@ static void virtio_mem_class_init(ObjectClass *klass, void *data)
vmc->fill_device_info = virtio_mem_fill_device_info;
vmc->get_memory_region = virtio_mem_get_memory_region;
+ vmc->decide_memslots = virtio_mem_decide_memslots;
+ vmc->get_memslots = virtio_mem_get_memslots;
vmc->add_size_change_notifier = virtio_mem_add_size_change_notifier;
vmc->remove_size_change_notifier = virtio_mem_remove_size_change_notifier;
vmc->unplug_request_check = virtio_mem_unplug_request_check;
@@ -1573,6 +1882,7 @@ static const TypeInfo virtio_mem_info = {
.parent = TYPE_VIRTIO_DEVICE,
.instance_size = sizeof(VirtIOMEM),
.instance_init = virtio_mem_instance_init,
+ .instance_finalize = virtio_mem_instance_finalize,
.class_init = virtio_mem_class_init,
.class_size = sizeof(VirtIOMEMClass),
.interfaces = (InterfaceInfo[]) {
diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h
index 605b160..30c376a 100644
--- a/include/exec/cpu-common.h
+++ b/include/exec/cpu-common.h
@@ -83,6 +83,21 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length);
ram_addr_t qemu_ram_addr_from_host(void *ptr);
ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr);
RAMBlock *qemu_ram_block_by_name(const char *name);
+
+/*
+ * Translates a host ptr back to a RAMBlock and an offset in that RAMBlock.
+ *
+ * @ptr: The host pointer to translate.
+ * @round_offset: Whether to round the result offset down to a target page
+ * @offset: Will be set to the offset within the returned RAMBlock.
+ *
+ * Returns: RAMBlock (or NULL if not found)
+ *
+ * By the time this function returns, the returned pointer is not protected
+ * by RCU anymore. If the caller is not within an RCU critical section and
+ * does not hold the iothread lock, it must have other means of protecting the
+ * pointer, such as a reference to the memory region that owns the RAMBlock.
+ */
RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset,
ram_addr_t *offset);
ram_addr_t qemu_ram_block_host_offset(RAMBlock *rb, void *host);
diff --git a/include/exec/memory.h b/include/exec/memory.h
index c99842d..653a32e 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -95,6 +95,7 @@ struct ReservedRegion {
* relative to the region's address space
* @readonly: writes to this section are ignored
* @nonvolatile: this section is non-volatile
+ * @unmergeable: this section should not get merged with adjacent sections
*/
struct MemoryRegionSection {
Int128 size;
@@ -104,6 +105,7 @@ struct MemoryRegionSection {
hwaddr offset_within_address_space;
bool readonly;
bool nonvolatile;
+ bool unmergeable;
};
typedef struct IOMMUTLBEntry IOMMUTLBEntry;
@@ -599,8 +601,9 @@ typedef void (*ReplayRamDiscard)(MemoryRegionSection *section, void *opaque);
* populated (consuming memory), to be used/accessed by the VM.
*
* A #RamDiscardManager can only be set for a RAM #MemoryRegion while the
- * #MemoryRegion isn't mapped yet; it cannot change while the #MemoryRegion is
- * mapped.
+ * #MemoryRegion isn't mapped into an address space yet (either directly
+ * or via an alias); it cannot change while the #MemoryRegion is
+ * mapped into an address space.
*
* The #RamDiscardManager is intended to be used by technologies that are
* incompatible with discarding of RAM (e.g., VFIO, which may pin all
@@ -772,6 +775,7 @@ struct MemoryRegion {
bool nonvolatile;
bool rom_device;
bool flush_coalesced_mmio;
+ bool unmergeable;
uint8_t dirty_log_mask;
bool is_iommu;
RAMBlock *ram_block;
@@ -2350,6 +2354,25 @@ void memory_region_set_size(MemoryRegion *mr, uint64_t size);
void memory_region_set_alias_offset(MemoryRegion *mr,
hwaddr offset);
+/*
+ * memory_region_set_unmergeable: Set a memory region unmergeable
+ *
+ * Mark a memory region unmergeable, resulting in the memory region (or
+ * everything contained in a memory region container) not getting merged when
+ * simplifying the address space and notifying memory listeners. Consequently,
+ * memory listeners will never get notified about ranges that are larger than
+ * the original memory regions.
+ *
+ * This is primarily useful when multiple aliases to a RAM memory region are
+ * mapped into a memory region container, and updates (e.g., enable/disable or
+ * map/unmap) of individual memory region aliases are not supposed to affect
+ * other memory regions in the same container.
+ *
+ * @mr: the #MemoryRegion to be updated
+ * @unmergeable: whether to mark the #MemoryRegion unmergeable
+ */
+void memory_region_set_unmergeable(MemoryRegion *mr, bool unmergeable);
+
/**
* memory_region_present: checks if an address relative to a @container
* translates into #MemoryRegion within @container
diff --git a/include/gdbstub/syscalls.h b/include/gdbstub/syscalls.h
index 243eaf8..54ff724 100644
--- a/include/gdbstub/syscalls.h
+++ b/include/gdbstub/syscalls.h
@@ -110,4 +110,13 @@ int use_gdb_syscalls(void);
*/
void gdb_exit(int code);
+/**
+ * gdb_qemu_exit: ask qemu to exit
+ * @code: exit code reported
+ *
+ * This requests qemu to exit. This function is allowed to return as
+ * the exit request might be processed asynchronously by qemu backend.
+ */
+void gdb_qemu_exit(int code);
+
#endif /* _SYSCALLS_H_ */
diff --git a/include/hw/boards.h b/include/hw/boards.h
index 55a64a1..43a56dc 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -297,15 +297,27 @@ struct MachineClass {
* DeviceMemoryState:
* @base: address in guest physical address space where the memory
* address space for memory devices starts
- * @mr: address space container for memory devices
+ * @mr: memory region container for memory devices
+ * @as: address space for memory devices
+ * @listener: memory listener used to track used memslots in the address space
* @dimm_size: the sum of plugged DIMMs' sizes
* @used_region_size: the part of @mr already used by memory devices
+ * @required_memslots: the number of memslots required by memory devices
+ * @used_memslots: the number of memslots currently used by memory devices
+ * @memslot_auto_decision_active: whether any plugged memory device
+ * automatically decided to use more than
+ * one memslot
*/
typedef struct DeviceMemoryState {
hwaddr base;
MemoryRegion mr;
+ AddressSpace as;
+ MemoryListener listener;
uint64_t dimm_size;
uint64_t used_region_size;
+ unsigned int required_memslots;
+ unsigned int used_memslots;
+ unsigned int memslot_auto_decision_active;
} DeviceMemoryState;
/**
diff --git a/include/hw/mem/memory-device.h b/include/hw/mem/memory-device.h
index 48d2611..3354d6c 100644
--- a/include/hw/mem/memory-device.h
+++ b/include/hw/mem/memory-device.h
@@ -14,6 +14,7 @@
#define MEMORY_DEVICE_H
#include "hw/qdev-core.h"
+#include "qemu/typedefs.h"
#include "qapi/qapi-types-machine.h"
#include "qom/object.h"
@@ -41,6 +42,17 @@ typedef struct MemoryDeviceState MemoryDeviceState;
* successive memory regions are used, a covering memory region has to
* be provided. Scattered memory regions are not supported for single
* devices.
+ *
+ * The device memory region returned via @get_memory_region may either be a
+ * single RAM memory region or a memory region container with subregions
+ * that are RAM memory regions or aliases to RAM memory regions. Other
+ * memory regions or subregions are not supported.
+ *
+ * If the device memory region returned via @get_memory_region is a
+ * memory region container, it's supported to dynamically (un)map subregions
+ * as long as the number of memslots returned by @get_memslots() won't
+ * be exceeded and as long as all memory regions are of the same kind (e.g.,
+ * all RAM or all ROM).
*/
struct MemoryDeviceClass {
/* private */
@@ -89,6 +101,28 @@ struct MemoryDeviceClass {
MemoryRegion *(*get_memory_region)(MemoryDeviceState *md, Error **errp);
/*
+ * Optional: Instruct the memory device to decide how many memory slots
+ * it requires, not exceeding the given limit.
+ *
+ * Called exactly once when pre-plugging the memory device, before
+ * querying the number of memslots using @get_memslots the first time.
+ */
+ void (*decide_memslots)(MemoryDeviceState *md, unsigned int limit);
+
+ /*
+ * Optional for memory devices that require only a single memslot,
+ * required for all other memory devices: Return the number of memslots
+ * (distinct RAM memory regions in the device memory region) that are
+ * required by the device.
+ *
+ * If this function is not implemented, the assumption is "1".
+ *
+ * Called when (un)plugging the memory device, to check if the requirements
+ * can be satisfied, and to do proper accounting.
+ */
+ unsigned int (*get_memslots)(MemoryDeviceState *md);
+
+ /*
* Optional: Return the desired minimum alignment of the device in guest
* physical address space. The final alignment is computed based on this
* alignment and the alignment requirements of the memory region.
@@ -105,8 +139,31 @@ struct MemoryDeviceClass {
MemoryDeviceInfo *info);
};
+/*
+ * Traditionally, KVM/vhost in many setups supported 509 memslots, whereby
+ * 253 memslots were "reserved" for boot memory and other devices (such
+ * as PCI BARs, which can get mapped dynamically) and 256 memslots were
+ * dedicated for DIMMs. These magic numbers worked reliably in the past.
+ *
+ * Further, using many memslots can negatively affect performance, so setting
+ * the soft-limit of memslots used by memory devices to the traditional
+ * DIMM limit of 256 sounds reasonable.
+ *
+ * If we have less than 509 memslots, we will instruct memory devices that
+ * support automatically deciding how many memslots to use to only use a single
+ * one.
+ *
+ * Hotplugging vhost devices with at least 509 memslots is not expected to
+ * cause problems, not even when devices automatically decided how many memslots
+ * to use.
+ */
+#define MEMORY_DEVICES_SOFT_MEMSLOT_LIMIT 256
+#define MEMORY_DEVICES_SAFE_MAX_MEMSLOTS 509
+
MemoryDeviceInfoList *qmp_memory_device_list(void);
uint64_t get_plugged_memory_size(void);
+unsigned int memory_devices_get_reserved_memslots(void);
+bool memory_devices_memslot_auto_decision_active(void);
void memory_device_pre_plug(MemoryDeviceState *md, MachineState *ms,
const uint64_t *legacy_align, Error **errp);
void memory_device_plug(MemoryDeviceState *md, MachineState *ms);
diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h
index 1860b54..96ccc18 100644
--- a/include/hw/virtio/vhost-backend.h
+++ b/include/hw/virtio/vhost-backend.h
@@ -86,9 +86,6 @@ typedef int (*vhost_set_vring_enable_op)(struct vhost_dev *dev,
typedef bool (*vhost_requires_shm_log_op)(struct vhost_dev *dev);
typedef int (*vhost_migration_done_op)(struct vhost_dev *dev,
char *mac_addr);
-typedef bool (*vhost_backend_can_merge_op)(struct vhost_dev *dev,
- uint64_t start1, uint64_t size1,
- uint64_t start2, uint64_t size2);
typedef int (*vhost_vsock_set_guest_cid_op)(struct vhost_dev *dev,
uint64_t guest_cid);
typedef int (*vhost_vsock_set_running_op)(struct vhost_dev *dev, int start);
@@ -108,8 +105,7 @@ typedef int (*vhost_crypto_create_session_op)(struct vhost_dev *dev,
typedef int (*vhost_crypto_close_session_op)(struct vhost_dev *dev,
uint64_t session_id);
-typedef bool (*vhost_backend_mem_section_filter_op)(struct vhost_dev *dev,
- MemoryRegionSection *section);
+typedef bool (*vhost_backend_no_private_memslots_op)(struct vhost_dev *dev);
typedef int (*vhost_get_inflight_fd_op)(struct vhost_dev *dev,
uint16_t queue_size,
@@ -138,6 +134,7 @@ typedef struct VhostOps {
vhost_backend_init vhost_backend_init;
vhost_backend_cleanup vhost_backend_cleanup;
vhost_backend_memslots_limit vhost_backend_memslots_limit;
+ vhost_backend_no_private_memslots_op vhost_backend_no_private_memslots;
vhost_net_set_backend_op vhost_net_set_backend;
vhost_net_set_mtu_op vhost_net_set_mtu;
vhost_scsi_set_endpoint_op vhost_scsi_set_endpoint;
@@ -163,7 +160,6 @@ typedef struct VhostOps {
vhost_set_vring_enable_op vhost_set_vring_enable;
vhost_requires_shm_log_op vhost_requires_shm_log;
vhost_migration_done_op vhost_migration_done;
- vhost_backend_can_merge_op vhost_backend_can_merge;
vhost_vsock_set_guest_cid_op vhost_vsock_set_guest_cid;
vhost_vsock_set_running_op vhost_vsock_set_running;
vhost_set_iotlb_callback_op vhost_set_iotlb_callback;
@@ -172,7 +168,6 @@ typedef struct VhostOps {
vhost_set_config_op vhost_set_config;
vhost_crypto_create_session_op vhost_crypto_create_session;
vhost_crypto_close_session_op vhost_crypto_close_session;
- vhost_backend_mem_section_filter_op vhost_backend_mem_section_filter;
vhost_get_inflight_fd_op vhost_get_inflight_fd;
vhost_set_inflight_fd_op vhost_set_inflight_fd;
vhost_dev_start_op vhost_dev_start;
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
index 6a173cb..c7e54676 100644
--- a/include/hw/virtio/vhost.h
+++ b/include/hw/virtio/vhost.h
@@ -315,7 +315,8 @@ uint64_t vhost_get_features(struct vhost_dev *hdev, const int *feature_bits,
*/
void vhost_ack_features(struct vhost_dev *hdev, const int *feature_bits,
uint64_t features);
-bool vhost_has_free_slot(void);
+unsigned int vhost_get_max_memslots(void);
+unsigned int vhost_get_free_memslots(void);
int vhost_net_set_backend(struct vhost_dev *hdev,
struct vhost_vring_file *file);
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index 390c464..4739fa4 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -225,13 +225,13 @@ struct VhostUserGPU {
};
#define VIRTIO_GPU_FILL_CMD(out) do { \
- size_t s; \
- s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, \
+ size_t virtiogpufillcmd_s_ = \
+ iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, \
&out, sizeof(out)); \
- if (s != sizeof(out)) { \
+ if (virtiogpufillcmd_s_ != sizeof(out)) { \
qemu_log_mask(LOG_GUEST_ERROR, \
"%s: command size incorrect %zu vs %zu\n", \
- __func__, s, sizeof(out)); \
+ __func__, virtiogpufillcmd_s_, sizeof(out)); \
return; \
} \
} while (0)
diff --git a/include/hw/virtio/virtio-mem.h b/include/hw/virtio/virtio-mem.h
index ab0fe2b..5f5b02b 100644
--- a/include/hw/virtio/virtio-mem.h
+++ b/include/hw/virtio/virtio-mem.h
@@ -33,6 +33,7 @@ OBJECT_DECLARE_TYPE(VirtIOMEM, VirtIOMEMClass,
#define VIRTIO_MEM_UNPLUGGED_INACCESSIBLE_PROP "unplugged-inaccessible"
#define VIRTIO_MEM_EARLY_MIGRATION_PROP "x-early-migration"
#define VIRTIO_MEM_PREALLOC_PROP "prealloc"
+#define VIRTIO_MEM_DYNAMIC_MEMSLOTS_PROP "dynamic-memslots"
struct VirtIOMEM {
VirtIODevice parent_obj;
@@ -44,7 +45,28 @@ struct VirtIOMEM {
int32_t bitmap_size;
unsigned long *bitmap;
- /* assigned memory backend and memory region */
+ /*
+ * With "dynamic-memslots=on": Device memory region in which we dynamically
+ * map the memslots.
+ */
+ MemoryRegion *mr;
+
+ /*
+ * With "dynamic-memslots=on": The individual memslots (aliases into the
+ * memory backend).
+ */
+ MemoryRegion *memslots;
+
+ /* With "dynamic-memslots=on": The total number of memslots. */
+ uint16_t nb_memslots;
+
+ /*
+ * With "dynamic-memslots=on": Size of one memslot (the size of the
+ * last one can differ).
+ */
+ uint64_t memslot_size;
+
+ /* Assigned memory backend with the RAM memory region. */
HostMemoryBackend *memdev;
/* NUMA node */
@@ -82,6 +104,12 @@ struct VirtIOMEM {
*/
bool early_migration;
+ /*
+ * Whether we dynamically map (multiple, if possible) memslots instead of
+ * statically mapping the whole RAM memory region.
+ */
+ bool dynamic_memslots;
+
/* notifiers to notify when "size" changes */
NotifierList size_change_notifiers;
@@ -96,6 +124,8 @@ struct VirtIOMEMClass {
/* public */
void (*fill_device_info)(const VirtIOMEM *vmen, VirtioMEMDeviceInfo *vi);
MemoryRegion *(*get_memory_region)(VirtIOMEM *vmem, Error **errp);
+ void (*decide_memslots)(VirtIOMEM *vmem, unsigned int limit);
+ unsigned int (*get_memslots)(VirtIOMEM *vmem);
void (*add_size_change_notifier)(VirtIOMEM *vmem, Notifier *notifier);
void (*remove_size_change_notifier)(VirtIOMEM *vmem, Notifier *notifier);
void (*unplug_request_check)(VirtIOMEM *vmem, Error **errp);
diff --git a/include/sysemu/cpus.h b/include/sysemu/cpus.h
index 0535a4c..b4a566c 100644
--- a/include/sysemu/cpus.h
+++ b/include/sysemu/cpus.h
@@ -50,11 +50,4 @@ void cpu_synchronize_all_post_reset(void);
void cpu_synchronize_all_post_init(void);
void cpu_synchronize_all_pre_loadvm(void);
-#ifndef CONFIG_USER_ONLY
-/* vl.c */
-/* *-user doesn't have configurable SMP topology */
-extern int smp_cores;
-extern int smp_threads;
-#endif
-
#endif
diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h
index ee9025f..97a8a4f 100644
--- a/include/sysemu/kvm.h
+++ b/include/sysemu/kvm.h
@@ -215,7 +215,8 @@ typedef struct KVMRouteChange {
/* external API */
-bool kvm_has_free_slot(MachineState *ms);
+unsigned int kvm_get_max_memslots(void);
+unsigned int kvm_get_free_memslots(void);
bool kvm_has_sync_mmu(void);
int kvm_has_vcpu_events(void);
int kvm_has_robust_singlestep(void);
@@ -552,7 +553,6 @@ int kvm_set_one_reg(CPUState *cs, uint64_t id, void *source);
*/
int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target);
struct ppc_radix_page_info *kvm_get_radix_page_info(void);
-int kvm_get_max_memslots(void);
/* Notify resamplefd for EOI of specific interrupts. */
void kvm_resample_fd_notify(int gsi);
diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h
index a5b9122..075939a 100644
--- a/include/sysemu/kvm_int.h
+++ b/include/sysemu/kvm_int.h
@@ -40,6 +40,7 @@ typedef struct KVMMemoryUpdate {
typedef struct KVMMemoryListener {
MemoryListener listener;
KVMSlot *slots;
+ unsigned int nr_used_slots;
int as_id;
QSIMPLEQ_HEAD(, KVMMemoryUpdate) transaction_add;
QSIMPLEQ_HEAD(, KVMMemoryUpdate) transaction_del;
diff --git a/include/sysemu/runstate.h b/include/sysemu/runstate.h
index 08afb97..c8c2bd8 100644
--- a/include/sysemu/runstate.h
+++ b/include/sysemu/runstate.h
@@ -68,6 +68,8 @@ void qemu_system_wakeup_request(WakeupReason reason, Error **errp);
void qemu_system_wakeup_enable(WakeupReason reason, bool enabled);
void qemu_register_wakeup_notifier(Notifier *notifier);
void qemu_register_wakeup_support(void);
+void qemu_system_shutdown_request_with_code(ShutdownCause reason,
+ int exit_code);
void qemu_system_shutdown_request(ShutdownCause reason);
void qemu_system_powerdown_request(void);
void qemu_register_powerdown_notifier(Notifier *notifier);
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index 25be2a6..73a3794 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -101,7 +101,7 @@ bool defaults_enabled(void);
void qemu_init(int argc, char **argv);
int qemu_main_loop(void);
-void qemu_cleanup(void);
+void qemu_cleanup(int);
extern QemuOptsList qemu_legacy_drive_opts;
extern QemuOptsList qemu_common_drive_opts;
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 6beae65..697c655 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -501,13 +501,6 @@ static GuestDiskBusType find_bus_type(STORAGE_BUS_TYPE bus)
return win2qemu[(int)bus];
}
-DEFINE_GUID(GUID_DEVINTERFACE_DISK,
- 0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2,
- 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
-DEFINE_GUID(GUID_DEVINTERFACE_STORAGEPORT,
- 0x2accfe60L, 0xc130, 0x11d2, 0xb0, 0x82,
- 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
-
static void get_pci_address_for_device(GuestPCIAddress *pci,
HDEVINFO dev_info)
{
diff --git a/qga/commands.c b/qga/commands.c
index 09c683e..ce172ed 100644
--- a/qga/commands.c
+++ b/qga/commands.c
@@ -206,15 +206,15 @@ GuestExecStatus *qmp_guest_exec_status(int64_t pid, Error **errp)
#endif
if (gei->out.length > 0) {
ges->out_data = g_base64_encode(gei->out.data, gei->out.length);
- g_free(gei->out.data);
ges->has_out_truncated = gei->out.truncated;
}
+ g_free(gei->out.data);
if (gei->err.length > 0) {
ges->err_data = g_base64_encode(gei->err.data, gei->err.length);
- g_free(gei->err.data);
ges->has_err_truncated = gei->err.truncated;
}
+ g_free(gei->err.data);
QTAILQ_REMOVE(&guest_exec_state.processes, gei, next);
g_free(gei);
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index b720dd4..876e2a8 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -1220,11 +1220,13 @@
# @signal: signal number (linux) or unhandled exception code (windows)
# if the process was abnormally terminated.
#
-# @out-data: base64-encoded stdout of the process
+# @out-data: base64-encoded stdout of the process. This field will only
+# be populated after the process exits.
#
-# @err-data: base64-encoded stderr of the process Note: @out-data and
+# @err-data: base64-encoded stderr of the process. Note: @out-data and
# @err-data are present only if 'capture-output' was specified for
-# 'guest-exec'
+# 'guest-exec'. This field will only be populated after the process
+# exits.
#
# @out-truncated: true if stdout was not fully captured due to size
# limitation.
diff --git a/roms/Makefile b/roms/Makefile
index 6859685..67f709b 100644
--- a/roms/Makefile
+++ b/roms/Makefile
@@ -147,7 +147,7 @@ skiboot:
cp skiboot/skiboot.lid ../pc-bios/skiboot.lid
efi:
- python3 edk2-build.py --config edk2-build.config \
+ $(PYTHON) edk2-build.py --config edk2-build.config \
--version-override "edk2-stable202302-for-qemu" \
--release-date "03/01/2023"
rm -f ../pc-bios/edk2-*.fd.bz2
diff --git a/scripts/xml-preprocess.py b/scripts/xml-preprocess.py
index 57f1d28..57f1d28 100755..100644
--- a/scripts/xml-preprocess.py
+++ b/scripts/xml-preprocess.py
diff --git a/stubs/qmp_memory_device.c b/stubs/memory_device.c
index e75cac6..15fd93f 100644
--- a/stubs/qmp_memory_device.c
+++ b/stubs/memory_device.c
@@ -10,3 +10,13 @@ uint64_t get_plugged_memory_size(void)
{
return (uint64_t)-1;
}
+
+unsigned int memory_devices_get_reserved_memslots(void)
+{
+ return 0;
+}
+
+bool memory_devices_memslot_auto_decision_active(void)
+{
+ return false;
+}
diff --git a/stubs/meson.build b/stubs/meson.build
index ef6e39a..cde4497 100644
--- a/stubs/meson.build
+++ b/stubs/meson.build
@@ -32,7 +32,7 @@ stub_ss.add(files('monitor.c'))
stub_ss.add(files('monitor-core.c'))
stub_ss.add(files('physmem.c'))
stub_ss.add(files('qemu-timer-notify-cb.c'))
-stub_ss.add(files('qmp_memory_device.c'))
+stub_ss.add(files('memory_device.c'))
stub_ss.add(files('qmp-command-available.c'))
stub_ss.add(files('qmp-quit.c'))
stub_ss.add(files('qtest.c'))
diff --git a/subprojects/libvduse/libvduse.c b/subprojects/libvduse/libvduse.c
index 377959a..21ffbb5 100644
--- a/subprojects/libvduse/libvduse.c
+++ b/subprojects/libvduse/libvduse.c
@@ -1031,7 +1031,7 @@ int vduse_dev_handler(VduseDev *dev)
/* The iova will be updated by iova_to_va() later, so just remove it */
vduse_iova_remove_region(dev, req.iova.start, req.iova.last);
for (i = 0; i < dev->num_queues; i++) {
- VduseVirtq *vq = &dev->vqs[i];
+ vq = &dev->vqs[i];
if (vq->ready) {
if (vduse_queue_update_vring(vq, vq->vring.desc_addr,
vq->vring.avail_addr,
diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c
index 051a611..6684057 100644
--- a/subprojects/libvhost-user/libvhost-user.c
+++ b/subprojects/libvhost-user/libvhost-user.c
@@ -631,9 +631,9 @@ static bool
generate_faults(VuDev *dev) {
unsigned int i;
for (i = 0; i < dev->nregions; i++) {
+#ifdef UFFDIO_REGISTER
VuDevRegion *dev_region = &dev->regions[i];
int ret;
-#ifdef UFFDIO_REGISTER
struct uffdio_register reg_struct;
/*
@@ -870,10 +870,10 @@ vu_rem_mem_reg(VuDev *dev, VhostUserMsg *vmsg) {
for (i = 0; i < dev->nregions; i++) {
if (reg_equal(&dev->regions[i], msg_region)) {
VuDevRegion *r = &dev->regions[i];
- void *m = (void *) (uintptr_t) r->mmap_addr;
+ void *ma = (void *) (uintptr_t) r->mmap_addr;
- if (m) {
- munmap(m, r->size + r->mmap_offset);
+ if (ma) {
+ munmap(ma, r->size + r->mmap_offset);
}
/*
@@ -1005,10 +1005,10 @@ vu_set_mem_table_exec(VuDev *dev, VhostUserMsg *vmsg)
for (i = 0; i < dev->nregions; i++) {
VuDevRegion *r = &dev->regions[i];
- void *m = (void *) (uintptr_t) r->mmap_addr;
+ void *ma = (void *) (uintptr_t) r->mmap_addr;
- if (m) {
- munmap(m, r->size + r->mmap_offset);
+ if (ma) {
+ munmap(ma, r->size + r->mmap_offset);
}
}
dev->nregions = memory->nregions;
diff --git a/system/main.c b/system/main.c
index 694388b..9b91d21 100644
--- a/system/main.c
+++ b/system/main.c
@@ -35,7 +35,7 @@ int qemu_default_main(void)
int status;
status = qemu_main_loop();
- qemu_cleanup();
+ qemu_cleanup(status);
return status;
}
diff --git a/system/memory.c b/system/memory.c
index fa1c99f..a800fbc 100644
--- a/system/memory.c
+++ b/system/memory.c
@@ -224,6 +224,7 @@ struct FlatRange {
bool romd_mode;
bool readonly;
bool nonvolatile;
+ bool unmergeable;
};
#define FOR_EACH_FLAT_RANGE(var, view) \
@@ -240,6 +241,7 @@ section_from_flat_range(FlatRange *fr, FlatView *fv)
.offset_within_address_space = int128_get64(fr->addr.start),
.readonly = fr->readonly,
.nonvolatile = fr->nonvolatile,
+ .unmergeable = fr->unmergeable,
};
}
@@ -250,7 +252,8 @@ static bool flatrange_equal(FlatRange *a, FlatRange *b)
&& a->offset_in_region == b->offset_in_region
&& a->romd_mode == b->romd_mode
&& a->readonly == b->readonly
- && a->nonvolatile == b->nonvolatile;
+ && a->nonvolatile == b->nonvolatile
+ && a->unmergeable == b->unmergeable;
}
static FlatView *flatview_new(MemoryRegion *mr_root)
@@ -323,7 +326,8 @@ static bool can_merge(FlatRange *r1, FlatRange *r2)
&& r1->dirty_log_mask == r2->dirty_log_mask
&& r1->romd_mode == r2->romd_mode
&& r1->readonly == r2->readonly
- && r1->nonvolatile == r2->nonvolatile;
+ && r1->nonvolatile == r2->nonvolatile
+ && !r1->unmergeable && !r2->unmergeable;
}
/* Attempt to simplify a view by merging adjacent ranges */
@@ -599,7 +603,8 @@ static void render_memory_region(FlatView *view,
Int128 base,
AddrRange clip,
bool readonly,
- bool nonvolatile)
+ bool nonvolatile,
+ bool unmergeable)
{
MemoryRegion *subregion;
unsigned i;
@@ -616,6 +621,7 @@ static void render_memory_region(FlatView *view,
int128_addto(&base, int128_make64(mr->addr));
readonly |= mr->readonly;
nonvolatile |= mr->nonvolatile;
+ unmergeable |= mr->unmergeable;
tmp = addrrange_make(base, mr->size);
@@ -629,14 +635,14 @@ static void render_memory_region(FlatView *view,
int128_subfrom(&base, int128_make64(mr->alias->addr));
int128_subfrom(&base, int128_make64(mr->alias_offset));
render_memory_region(view, mr->alias, base, clip,
- readonly, nonvolatile);
+ readonly, nonvolatile, unmergeable);
return;
}
/* Render subregions in priority order. */
QTAILQ_FOREACH(subregion, &mr->subregions, subregions_link) {
render_memory_region(view, subregion, base, clip,
- readonly, nonvolatile);
+ readonly, nonvolatile, unmergeable);
}
if (!mr->terminates) {
@@ -652,6 +658,7 @@ static void render_memory_region(FlatView *view,
fr.romd_mode = mr->romd_mode;
fr.readonly = readonly;
fr.nonvolatile = nonvolatile;
+ fr.unmergeable = unmergeable;
/* Render the region itself into any gaps left by the current view. */
for (i = 0; i < view->nr && int128_nz(remain); ++i) {
@@ -753,7 +760,7 @@ static FlatView *generate_memory_topology(MemoryRegion *mr)
if (mr) {
render_memory_region(view, mr, int128_zero(),
addrrange_make(int128_zero(), int128_2_64()),
- false, false);
+ false, false, false);
}
flatview_simplify(view);
@@ -2085,7 +2092,7 @@ int memory_region_iommu_num_indexes(IOMMUMemoryRegion *iommu_mr)
RamDiscardManager *memory_region_get_ram_discard_manager(MemoryRegion *mr)
{
- if (!memory_region_is_mapped(mr) || !memory_region_is_ram(mr)) {
+ if (!memory_region_is_ram(mr)) {
return NULL;
}
return mr->rdm;
@@ -2094,7 +2101,7 @@ RamDiscardManager *memory_region_get_ram_discard_manager(MemoryRegion *mr)
void memory_region_set_ram_discard_manager(MemoryRegion *mr,
RamDiscardManager *rdm)
{
- g_assert(memory_region_is_ram(mr) && !memory_region_is_mapped(mr));
+ g_assert(memory_region_is_ram(mr));
g_assert(!rdm || !mr->rdm);
mr->rdm = rdm;
}
@@ -2755,6 +2762,18 @@ void memory_region_set_alias_offset(MemoryRegion *mr, hwaddr offset)
memory_region_transaction_commit();
}
+void memory_region_set_unmergeable(MemoryRegion *mr, bool unmergeable)
+{
+ if (unmergeable == mr->unmergeable) {
+ return;
+ }
+
+ memory_region_transaction_begin();
+ mr->unmergeable = unmergeable;
+ memory_region_update_pending |= mr->enabled;
+ memory_region_transaction_commit();
+}
+
uint64_t memory_region_get_alignment(const MemoryRegion *mr)
{
return mr->align;
diff --git a/system/physmem.c b/system/physmem.c
index edc3ed8..fc2b0fe 100644
--- a/system/physmem.c
+++ b/system/physmem.c
@@ -2221,23 +2221,6 @@ ram_addr_t qemu_ram_block_host_offset(RAMBlock *rb, void *host)
return res;
}
-/*
- * Translates a host ptr back to a RAMBlock, a ram_addr and an offset
- * in that RAMBlock.
- *
- * ptr: Host pointer to look up
- * round_offset: If true round the result offset down to a page boundary
- * *ram_addr: set to result ram_addr
- * *offset: set to result offset within the RAMBlock
- *
- * Returns: RAMBlock (or NULL if not found)
- *
- * By the time this function returns, the returned pointer is not protected
- * by RCU anymore. If the caller is not within an RCU critical section and
- * does not hold the iothread lock, it must have other means of protecting the
- * pointer, such as a reference to the region that includes the incoming
- * ram_addr_t.
- */
RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset,
ram_addr_t *offset)
{
diff --git a/system/runstate.c b/system/runstate.c
index 1652ed0..ea9d6c2 100644
--- a/system/runstate.c
+++ b/system/runstate.c
@@ -385,6 +385,7 @@ void vm_state_notify(bool running, RunState state)
static ShutdownCause reset_requested;
static ShutdownCause shutdown_requested;
+static int shutdown_exit_code = EXIT_SUCCESS;
static int shutdown_signal;
static pid_t shutdown_pid;
static int powerdown_requested;
@@ -664,6 +665,13 @@ void qemu_system_killed(int signal, pid_t pid)
qemu_notify_event();
}
+void qemu_system_shutdown_request_with_code(ShutdownCause reason,
+ int exit_code)
+{
+ shutdown_exit_code = exit_code;
+ qemu_system_shutdown_request(reason);
+}
+
void qemu_system_shutdown_request(ShutdownCause reason)
{
trace_qemu_system_shutdown_request(reason);
@@ -725,7 +733,9 @@ static bool main_loop_should_exit(int *status)
if (shutdown_action == SHUTDOWN_ACTION_PAUSE) {
vm_stop(RUN_STATE_SHUTDOWN);
} else {
- if (request == SHUTDOWN_CAUSE_GUEST_PANIC &&
+ if (shutdown_exit_code != EXIT_SUCCESS) {
+ *status = shutdown_exit_code;
+ } else if (request == SHUTDOWN_CAUSE_GUEST_PANIC &&
panic_action == PANIC_ACTION_EXIT_FAILURE) {
*status = EXIT_FAILURE;
}
@@ -824,9 +834,9 @@ void qemu_init_subsystems(void)
}
-void qemu_cleanup(void)
+void qemu_cleanup(int status)
{
- gdb_exit(0);
+ gdb_exit(status);
/*
* cleaning up the migration object cancels any existing migration
diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c
index 2b92aee..eb29a1f 100644
--- a/target/i386/tcg/seg_helper.c
+++ b/target/i386/tcg/seg_helper.c
@@ -229,7 +229,7 @@ static void tss_load_seg(CPUX86State *env, X86Seg seg_reg, int selector,
static void tss_set_busy(CPUX86State *env, int tss_selector, bool value,
uintptr_t retaddr)
{
- target_ulong ptr = env->gdt.base + (env->tr.selector & ~7);
+ target_ulong ptr = env->gdt.base + (tss_selector & ~7);
uint32_t e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr);
if (value) {
diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h
index 04af509..f3fbe37 100644
--- a/target/riscv/cpu-qom.h
+++ b/target/riscv/cpu-qom.h
@@ -30,6 +30,7 @@
#define CPU_RESOLVING_TYPE TYPE_RISCV_CPU
#define TYPE_RISCV_CPU_ANY RISCV_CPU_TYPE_NAME("any")
+#define TYPE_RISCV_CPU_MAX RISCV_CPU_TYPE_NAME("max")
#define TYPE_RISCV_CPU_BASE32 RISCV_CPU_TYPE_NAME("rv32")
#define TYPE_RISCV_CPU_BASE64 RISCV_CPU_TYPE_NAME("rv64")
#define TYPE_RISCV_CPU_BASE128 RISCV_CPU_TYPE_NAME("x-rv128")
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index f557270..ac4a6c7 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -23,9 +23,7 @@
#include "qemu/log.h"
#include "cpu.h"
#include "cpu_vendorid.h"
-#include "pmu.h"
#include "internals.h"
-#include "time_helper.h"
#include "exec/exec-all.h"
#include "qapi/error.h"
#include "qapi/visitor.h"
@@ -35,20 +33,13 @@
#include "fpu/softfloat-helpers.h"
#include "sysemu/kvm.h"
#include "sysemu/tcg.h"
-#include "kvm_riscv.h"
+#include "kvm/kvm_riscv.h"
#include "tcg/tcg.h"
/* RISC-V CPU definitions */
static const char riscv_single_letter_exts[] = "IEMAFDQCPVH";
-
-struct isa_ext_data {
- const char *name;
- int min_version;
- int ext_enable_offset;
-};
-
-#define ISA_EXT_DATA_ENTRY(_name, _min_ver, _prop) \
- {#_name, _min_ver, offsetof(struct RISCVCPUConfig, _prop)}
+const uint32_t misa_bits[] = {RVI, RVE, RVM, RVA, RVF, RVD, RVV,
+ RVC, RVS, RVU, RVH, RVJ, RVG, 0};
/*
* From vector_helper.c
@@ -61,6 +52,9 @@ struct isa_ext_data {
#define BYTE(x) (x)
#endif
+#define ISA_EXT_DATA_ENTRY(_name, _min_ver, _prop) \
+ {#_name, _min_ver, CPU_CFG_OFFSET(_prop)}
+
/*
* Here are the ordering rules of extension naming defined by RISC-V
* specification :
@@ -81,7 +75,7 @@ struct isa_ext_data {
* Single letter extensions are checked in riscv_cpu_validate_misa_priv()
* instead.
*/
-static const struct isa_ext_data isa_edata_arr[] = {
+const RISCVIsaExtData isa_edata_arr[] = {
ISA_EXT_DATA_ENTRY(zicbom, PRIV_VERSION_1_12_0, ext_icbom),
ISA_EXT_DATA_ENTRY(zicboz, PRIV_VERSION_1_12_0, ext_icboz),
ISA_EXT_DATA_ENTRY(zicond, PRIV_VERSION_1_12_0, ext_zicond),
@@ -160,20 +154,20 @@ static const struct isa_ext_data isa_edata_arr[] = {
ISA_EXT_DATA_ENTRY(xtheadmempair, PRIV_VERSION_1_11_0, ext_xtheadmempair),
ISA_EXT_DATA_ENTRY(xtheadsync, PRIV_VERSION_1_11_0, ext_xtheadsync),
ISA_EXT_DATA_ENTRY(xventanacondops, PRIV_VERSION_1_12_0, ext_XVentanaCondOps),
+
+ DEFINE_PROP_END_OF_LIST(),
};
-static bool isa_ext_is_enabled(RISCVCPU *cpu,
- const struct isa_ext_data *edata)
+bool isa_ext_is_enabled(RISCVCPU *cpu, uint32_t ext_offset)
{
- bool *ext_enabled = (void *)&cpu->cfg + edata->ext_enable_offset;
+ bool *ext_enabled = (void *)&cpu->cfg + ext_offset;
return *ext_enabled;
}
-static void isa_ext_update_enabled(RISCVCPU *cpu,
- const struct isa_ext_data *edata, bool en)
+void isa_ext_update_enabled(RISCVCPU *cpu, uint32_t ext_offset, bool en)
{
- bool *ext_enabled = (void *)&cpu->cfg + edata->ext_enable_offset;
+ bool *ext_enabled = (void *)&cpu->cfg + ext_offset;
*ext_enabled = en;
}
@@ -258,8 +252,6 @@ static const char * const riscv_intr_names[] = {
"reserved"
};
-static void riscv_cpu_add_user_properties(Object *obj);
-
const char *riscv_cpu_get_trap_name(target_ulong cause, bool async)
{
if (async) {
@@ -271,7 +263,7 @@ const char *riscv_cpu_get_trap_name(target_ulong cause, bool async)
}
}
-static void set_misa(CPURISCVState *env, RISCVMXL mxl, uint32_t ext)
+void riscv_cpu_set_misa(CPURISCVState *env, RISCVMXL mxl, uint32_t ext)
{
env->misa_mxl_max = env->misa_mxl = mxl;
env->misa_ext_mask = env->misa_ext = ext;
@@ -376,9 +368,9 @@ static void riscv_any_cpu_init(Object *obj)
RISCVCPU *cpu = RISCV_CPU(obj);
CPURISCVState *env = &cpu->env;
#if defined(TARGET_RISCV32)
- set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVD | RVC | RVU);
+ riscv_cpu_set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVD | RVC | RVU);
#elif defined(TARGET_RISCV64)
- set_misa(env, MXL_RV64, RVI | RVM | RVA | RVF | RVD | RVC | RVU);
+ riscv_cpu_set_misa(env, MXL_RV64, RVI | RVM | RVA | RVF | RVD | RVC | RVU);
#endif
#ifndef CONFIG_USER_ONLY
@@ -396,13 +388,29 @@ static void riscv_any_cpu_init(Object *obj)
cpu->cfg.pmp = true;
}
+static void riscv_max_cpu_init(Object *obj)
+{
+ RISCVCPU *cpu = RISCV_CPU(obj);
+ CPURISCVState *env = &cpu->env;
+ RISCVMXL mlx = MXL_RV64;
+
+#ifdef TARGET_RISCV32
+ mlx = MXL_RV32;
+#endif
+ riscv_cpu_set_misa(env, mlx, 0);
+ env->priv_ver = PRIV_VERSION_LATEST;
+#ifndef CONFIG_USER_ONLY
+ set_satp_mode_max_supported(RISCV_CPU(obj), mlx == MXL_RV32 ?
+ VM_1_10_SV32 : VM_1_10_SV57);
+#endif
+}
+
#if defined(TARGET_RISCV64)
static void rv64_base_cpu_init(Object *obj)
{
CPURISCVState *env = &RISCV_CPU(obj)->env;
/* We set this in the realise function */
- set_misa(env, MXL_RV64, 0);
- riscv_cpu_add_user_properties(obj);
+ riscv_cpu_set_misa(env, MXL_RV64, 0);
/* Set latest version of privileged specification */
env->priv_ver = PRIV_VERSION_LATEST;
#ifndef CONFIG_USER_ONLY
@@ -414,7 +422,8 @@ static void rv64_sifive_u_cpu_init(Object *obj)
{
RISCVCPU *cpu = RISCV_CPU(obj);
CPURISCVState *env = &cpu->env;
- set_misa(env, MXL_RV64, RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
+ riscv_cpu_set_misa(env, MXL_RV64,
+ RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
env->priv_ver = PRIV_VERSION_1_10_0;
#ifndef CONFIG_USER_ONLY
set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV39);
@@ -432,7 +441,7 @@ static void rv64_sifive_e_cpu_init(Object *obj)
CPURISCVState *env = &RISCV_CPU(obj)->env;
RISCVCPU *cpu = RISCV_CPU(obj);
- set_misa(env, MXL_RV64, RVI | RVM | RVA | RVC | RVU);
+ riscv_cpu_set_misa(env, MXL_RV64, RVI | RVM | RVA | RVC | RVU);
env->priv_ver = PRIV_VERSION_1_10_0;
#ifndef CONFIG_USER_ONLY
set_satp_mode_max_supported(cpu, VM_1_10_MBARE);
@@ -449,7 +458,7 @@ static void rv64_thead_c906_cpu_init(Object *obj)
CPURISCVState *env = &RISCV_CPU(obj)->env;
RISCVCPU *cpu = RISCV_CPU(obj);
- set_misa(env, MXL_RV64, RVG | RVC | RVS | RVU);
+ riscv_cpu_set_misa(env, MXL_RV64, RVG | RVC | RVS | RVU);
env->priv_ver = PRIV_VERSION_1_11_0;
cpu->cfg.ext_zfa = true;
@@ -480,7 +489,7 @@ static void rv64_veyron_v1_cpu_init(Object *obj)
CPURISCVState *env = &RISCV_CPU(obj)->env;
RISCVCPU *cpu = RISCV_CPU(obj);
- set_misa(env, MXL_RV64, RVG | RVC | RVS | RVU | RVH);
+ riscv_cpu_set_misa(env, MXL_RV64, RVG | RVC | RVS | RVU | RVH);
env->priv_ver = PRIV_VERSION_1_12_0;
/* Enable ISA extensions */
@@ -525,8 +534,7 @@ static void rv128_base_cpu_init(Object *obj)
}
CPURISCVState *env = &RISCV_CPU(obj)->env;
/* We set this in the realise function */
- set_misa(env, MXL_RV128, 0);
- riscv_cpu_add_user_properties(obj);
+ riscv_cpu_set_misa(env, MXL_RV128, 0);
/* Set latest version of privileged specification */
env->priv_ver = PRIV_VERSION_LATEST;
#ifndef CONFIG_USER_ONLY
@@ -538,8 +546,7 @@ static void rv32_base_cpu_init(Object *obj)
{
CPURISCVState *env = &RISCV_CPU(obj)->env;
/* We set this in the realise function */
- set_misa(env, MXL_RV32, 0);
- riscv_cpu_add_user_properties(obj);
+ riscv_cpu_set_misa(env, MXL_RV32, 0);
/* Set latest version of privileged specification */
env->priv_ver = PRIV_VERSION_LATEST;
#ifndef CONFIG_USER_ONLY
@@ -551,7 +558,8 @@ static void rv32_sifive_u_cpu_init(Object *obj)
{
RISCVCPU *cpu = RISCV_CPU(obj);
CPURISCVState *env = &cpu->env;
- set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
+ riscv_cpu_set_misa(env, MXL_RV32,
+ RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
env->priv_ver = PRIV_VERSION_1_10_0;
#ifndef CONFIG_USER_ONLY
set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV32);
@@ -569,7 +577,7 @@ static void rv32_sifive_e_cpu_init(Object *obj)
CPURISCVState *env = &RISCV_CPU(obj)->env;
RISCVCPU *cpu = RISCV_CPU(obj);
- set_misa(env, MXL_RV32, RVI | RVM | RVA | RVC | RVU);
+ riscv_cpu_set_misa(env, MXL_RV32, RVI | RVM | RVA | RVC | RVU);
env->priv_ver = PRIV_VERSION_1_10_0;
#ifndef CONFIG_USER_ONLY
set_satp_mode_max_supported(cpu, VM_1_10_MBARE);
@@ -586,7 +594,7 @@ static void rv32_ibex_cpu_init(Object *obj)
CPURISCVState *env = &RISCV_CPU(obj)->env;
RISCVCPU *cpu = RISCV_CPU(obj);
- set_misa(env, MXL_RV32, RVI | RVM | RVC | RVU);
+ riscv_cpu_set_misa(env, MXL_RV32, RVI | RVM | RVC | RVU);
env->priv_ver = PRIV_VERSION_1_11_0;
#ifndef CONFIG_USER_ONLY
set_satp_mode_max_supported(cpu, VM_1_10_MBARE);
@@ -604,7 +612,7 @@ static void rv32_imafcu_nommu_cpu_init(Object *obj)
CPURISCVState *env = &RISCV_CPU(obj)->env;
RISCVCPU *cpu = RISCV_CPU(obj);
- set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVC | RVU);
+ riscv_cpu_set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVC | RVU);
env->priv_ver = PRIV_VERSION_1_10_0;
#ifndef CONFIG_USER_ONLY
set_satp_mode_max_supported(cpu, VM_1_10_MBARE);
@@ -617,19 +625,6 @@ static void rv32_imafcu_nommu_cpu_init(Object *obj)
}
#endif
-#if defined(CONFIG_KVM)
-static void riscv_host_cpu_init(Object *obj)
-{
- CPURISCVState *env = &RISCV_CPU(obj)->env;
-#if defined(TARGET_RISCV32)
- set_misa(env, MXL_RV32, 0);
-#elif defined(TARGET_RISCV64)
- set_misa(env, MXL_RV64, 0);
-#endif
- riscv_cpu_add_user_properties(obj);
-}
-#endif /* CONFIG_KVM */
-
static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model)
{
ObjectClass *oc;
@@ -648,6 +643,17 @@ static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model)
return oc;
}
+char *riscv_cpu_get_name(RISCVCPU *cpu)
+{
+ RISCVCPUClass *rcc = RISCV_CPU_GET_CLASS(cpu);
+ const char *typename = object_class_get_name(OBJECT_CLASS(rcc));
+
+ g_assert(g_str_has_suffix(typename, RISCV_CPU_TYPE_SUFFIX));
+
+ return g_strndup(typename,
+ strlen(typename) - strlen(RISCV_CPU_TYPE_SUFFIX));
+}
+
static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags)
{
RISCVCPU *cpu = RISCV_CPU(cs);
@@ -798,24 +804,6 @@ static vaddr riscv_cpu_get_pc(CPUState *cs)
return env->pc;
}
-static void riscv_cpu_synchronize_from_tb(CPUState *cs,
- const TranslationBlock *tb)
-{
- if (!(tb_cflags(tb) & CF_PCREL)) {
- RISCVCPU *cpu = RISCV_CPU(cs);
- CPURISCVState *env = &cpu->env;
- RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL);
-
- tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL));
-
- if (xl == MXL_RV32) {
- env->pc = (int32_t) tb->pc;
- } else {
- env->pc = tb->pc;
- }
- }
-}
-
static bool riscv_cpu_has_work(CPUState *cs)
{
#ifndef CONFIG_USER_ONLY
@@ -831,29 +819,6 @@ static bool riscv_cpu_has_work(CPUState *cs)
#endif
}
-static void riscv_restore_state_to_opc(CPUState *cs,
- const TranslationBlock *tb,
- const uint64_t *data)
-{
- RISCVCPU *cpu = RISCV_CPU(cs);
- CPURISCVState *env = &cpu->env;
- RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL);
- target_ulong pc;
-
- if (tb_cflags(tb) & CF_PCREL) {
- pc = (env->pc & TARGET_PAGE_MASK) | data[0];
- } else {
- pc = data[0];
- }
-
- if (xl == MXL_RV32) {
- env->pc = (int32_t)pc;
- } else {
- env->pc = pc;
- }
- env->bins = data[1];
-}
-
static void riscv_cpu_reset_hold(Object *obj)
{
#ifndef CONFIG_USER_ONLY
@@ -956,390 +921,6 @@ static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info)
}
}
-static void riscv_cpu_validate_v(CPURISCVState *env, RISCVCPUConfig *cfg,
- Error **errp)
-{
- int vext_version = VEXT_VERSION_1_00_0;
-
- if (!is_power_of_2(cfg->vlen)) {
- error_setg(errp, "Vector extension VLEN must be power of 2");
- return;
- }
- if (cfg->vlen > RV_VLEN_MAX || cfg->vlen < 128) {
- error_setg(errp,
- "Vector extension implementation only supports VLEN "
- "in the range [128, %d]", RV_VLEN_MAX);
- return;
- }
- if (!is_power_of_2(cfg->elen)) {
- error_setg(errp, "Vector extension ELEN must be power of 2");
- return;
- }
- if (cfg->elen > 64 || cfg->elen < 8) {
- error_setg(errp,
- "Vector extension implementation only supports ELEN "
- "in the range [8, 64]");
- return;
- }
- if (cfg->vext_spec) {
- if (!g_strcmp0(cfg->vext_spec, "v1.0")) {
- vext_version = VEXT_VERSION_1_00_0;
- } else {
- error_setg(errp, "Unsupported vector spec version '%s'",
- cfg->vext_spec);
- return;
- }
- } else {
- qemu_log("vector version is not specified, "
- "use the default value v1.0\n");
- }
- env->vext_ver = vext_version;
-}
-
-static void riscv_cpu_validate_priv_spec(RISCVCPU *cpu, Error **errp)
-{
- CPURISCVState *env = &cpu->env;
- int priv_version = -1;
-
- if (cpu->cfg.priv_spec) {
- 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;
- } else {
- error_setg(errp,
- "Unsupported privilege spec version '%s'",
- cpu->cfg.priv_spec);
- return;
- }
-
- env->priv_ver = priv_version;
- }
-}
-
-static void riscv_cpu_disable_priv_spec_isa_exts(RISCVCPU *cpu)
-{
- CPURISCVState *env = &cpu->env;
- int i;
-
- /* Force disable extensions if priv spec version does not match */
- for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) {
- if (isa_ext_is_enabled(cpu, &isa_edata_arr[i]) &&
- (env->priv_ver < isa_edata_arr[i].min_version)) {
- isa_ext_update_enabled(cpu, &isa_edata_arr[i], false);
-#ifndef CONFIG_USER_ONLY
- warn_report("disabling %s extension for hart 0x" TARGET_FMT_lx
- " because privilege spec version does not match",
- isa_edata_arr[i].name, env->mhartid);
-#else
- warn_report("disabling %s extension because "
- "privilege spec version does not match",
- isa_edata_arr[i].name);
-#endif
- }
- }
-}
-
-static void riscv_cpu_validate_misa_mxl(RISCVCPU *cpu, Error **errp)
-{
- RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu);
- CPUClass *cc = CPU_CLASS(mcc);
- CPURISCVState *env = &cpu->env;
-
- /* Validate that MISA_MXL is set properly. */
- switch (env->misa_mxl_max) {
-#ifdef TARGET_RISCV64
- case MXL_RV64:
- case MXL_RV128:
- cc->gdb_core_xml_file = "riscv-64bit-cpu.xml";
- break;
-#endif
- case MXL_RV32:
- cc->gdb_core_xml_file = "riscv-32bit-cpu.xml";
- break;
- default:
- g_assert_not_reached();
- }
-
- if (env->misa_mxl_max != env->misa_mxl) {
- error_setg(errp, "misa_mxl_max must be equal to misa_mxl");
- return;
- }
-}
-
-/*
- * Check consistency between chosen extensions while setting
- * cpu->cfg accordingly.
- */
-void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
-{
- CPURISCVState *env = &cpu->env;
- Error *local_err = NULL;
-
- /* Do some ISA extension error checking */
- if (riscv_has_ext(env, RVG) &&
- !(riscv_has_ext(env, RVI) && riscv_has_ext(env, RVM) &&
- riscv_has_ext(env, RVA) && riscv_has_ext(env, RVF) &&
- riscv_has_ext(env, RVD) &&
- cpu->cfg.ext_icsr && cpu->cfg.ext_ifencei)) {
- warn_report("Setting G will also set IMAFD_Zicsr_Zifencei");
- cpu->cfg.ext_icsr = true;
- cpu->cfg.ext_ifencei = true;
-
- env->misa_ext |= RVI | RVM | RVA | RVF | RVD;
- env->misa_ext_mask |= RVI | RVM | RVA | RVF | RVD;
- }
-
- if (riscv_has_ext(env, RVI) && riscv_has_ext(env, RVE)) {
- error_setg(errp,
- "I and E extensions are incompatible");
- return;
- }
-
- if (!riscv_has_ext(env, RVI) && !riscv_has_ext(env, RVE)) {
- error_setg(errp,
- "Either I or E extension must be set");
- return;
- }
-
- if (riscv_has_ext(env, RVS) && !riscv_has_ext(env, RVU)) {
- error_setg(errp,
- "Setting S extension without U extension is illegal");
- return;
- }
-
- if (riscv_has_ext(env, RVH) && !riscv_has_ext(env, RVI)) {
- error_setg(errp,
- "H depends on an I base integer ISA with 32 x registers");
- return;
- }
-
- if (riscv_has_ext(env, RVH) && !riscv_has_ext(env, RVS)) {
- error_setg(errp, "H extension implicitly requires S-mode");
- return;
- }
-
- if (riscv_has_ext(env, RVF) && !cpu->cfg.ext_icsr) {
- error_setg(errp, "F extension requires Zicsr");
- return;
- }
-
- if ((cpu->cfg.ext_zawrs) && !riscv_has_ext(env, RVA)) {
- error_setg(errp, "Zawrs extension requires A extension");
- return;
- }
-
- if (cpu->cfg.ext_zfa && !riscv_has_ext(env, RVF)) {
- error_setg(errp, "Zfa extension requires F extension");
- return;
- }
-
- if (cpu->cfg.ext_zfh) {
- cpu->cfg.ext_zfhmin = true;
- }
-
- if (cpu->cfg.ext_zfhmin && !riscv_has_ext(env, RVF)) {
- error_setg(errp, "Zfh/Zfhmin extensions require F extension");
- return;
- }
-
- if (cpu->cfg.ext_zfbfmin && !riscv_has_ext(env, RVF)) {
- error_setg(errp, "Zfbfmin extension depends on F extension");
- return;
- }
-
- if (riscv_has_ext(env, RVD) && !riscv_has_ext(env, RVF)) {
- error_setg(errp, "D extension requires F extension");
- return;
- }
-
- if (riscv_has_ext(env, RVV)) {
- riscv_cpu_validate_v(env, &cpu->cfg, &local_err);
- if (local_err != NULL) {
- error_propagate(errp, local_err);
- return;
- }
-
- /* The V vector extension depends on the Zve64d extension */
- cpu->cfg.ext_zve64d = true;
- }
-
- /* The Zve64d extension depends on the Zve64f extension */
- if (cpu->cfg.ext_zve64d) {
- cpu->cfg.ext_zve64f = true;
- }
-
- /* The Zve64f extension depends on the Zve32f extension */
- if (cpu->cfg.ext_zve64f) {
- cpu->cfg.ext_zve32f = true;
- }
-
- if (cpu->cfg.ext_zve64d && !riscv_has_ext(env, RVD)) {
- error_setg(errp, "Zve64d/V extensions require D extension");
- return;
- }
-
- if (cpu->cfg.ext_zve32f && !riscv_has_ext(env, RVF)) {
- error_setg(errp, "Zve32f/Zve64f extensions require F extension");
- return;
- }
-
- if (cpu->cfg.ext_zvfh) {
- cpu->cfg.ext_zvfhmin = true;
- }
-
- if (cpu->cfg.ext_zvfhmin && !cpu->cfg.ext_zve32f) {
- error_setg(errp, "Zvfh/Zvfhmin extensions require Zve32f extension");
- return;
- }
-
- if (cpu->cfg.ext_zvfh && !cpu->cfg.ext_zfhmin) {
- error_setg(errp, "Zvfh extensions requires Zfhmin extension");
- return;
- }
-
- if (cpu->cfg.ext_zvfbfmin && !cpu->cfg.ext_zfbfmin) {
- error_setg(errp, "Zvfbfmin extension depends on Zfbfmin extension");
- return;
- }
-
- if (cpu->cfg.ext_zvfbfmin && !cpu->cfg.ext_zve32f) {
- error_setg(errp, "Zvfbfmin extension depends on Zve32f extension");
- return;
- }
-
- if (cpu->cfg.ext_zvfbfwma && !cpu->cfg.ext_zvfbfmin) {
- error_setg(errp, "Zvfbfwma extension depends on Zvfbfmin extension");
- return;
- }
-
- /* Set the ISA extensions, checks should have happened above */
- if (cpu->cfg.ext_zhinx) {
- cpu->cfg.ext_zhinxmin = true;
- }
-
- if ((cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinxmin) && !cpu->cfg.ext_zfinx) {
- error_setg(errp, "Zdinx/Zhinx/Zhinxmin extensions require Zfinx");
- return;
- }
-
- if (cpu->cfg.ext_zfinx) {
- if (!cpu->cfg.ext_icsr) {
- error_setg(errp, "Zfinx extension requires Zicsr");
- return;
- }
- if (riscv_has_ext(env, RVF)) {
- error_setg(errp,
- "Zfinx cannot be supported together with F extension");
- return;
- }
- }
-
- if (cpu->cfg.ext_zce) {
- cpu->cfg.ext_zca = true;
- cpu->cfg.ext_zcb = true;
- cpu->cfg.ext_zcmp = true;
- cpu->cfg.ext_zcmt = true;
- if (riscv_has_ext(env, RVF) && env->misa_mxl_max == MXL_RV32) {
- cpu->cfg.ext_zcf = true;
- }
- }
-
- /* zca, zcd and zcf has a PRIV 1.12.0 restriction */
- if (riscv_has_ext(env, RVC) && env->priv_ver >= PRIV_VERSION_1_12_0) {
- cpu->cfg.ext_zca = true;
- if (riscv_has_ext(env, RVF) && env->misa_mxl_max == MXL_RV32) {
- cpu->cfg.ext_zcf = true;
- }
- if (riscv_has_ext(env, RVD)) {
- cpu->cfg.ext_zcd = true;
- }
- }
-
- if (env->misa_mxl_max != MXL_RV32 && cpu->cfg.ext_zcf) {
- error_setg(errp, "Zcf extension is only relevant to RV32");
- return;
- }
-
- if (!riscv_has_ext(env, RVF) && cpu->cfg.ext_zcf) {
- error_setg(errp, "Zcf extension requires F extension");
- return;
- }
-
- if (!riscv_has_ext(env, RVD) && cpu->cfg.ext_zcd) {
- error_setg(errp, "Zcd extension requires D extension");
- return;
- }
-
- if ((cpu->cfg.ext_zcf || cpu->cfg.ext_zcd || cpu->cfg.ext_zcb ||
- cpu->cfg.ext_zcmp || cpu->cfg.ext_zcmt) && !cpu->cfg.ext_zca) {
- error_setg(errp, "Zcf/Zcd/Zcb/Zcmp/Zcmt extensions require Zca "
- "extension");
- return;
- }
-
- if (cpu->cfg.ext_zcd && (cpu->cfg.ext_zcmp || cpu->cfg.ext_zcmt)) {
- error_setg(errp, "Zcmp/Zcmt extensions are incompatible with "
- "Zcd extension");
- return;
- }
-
- if (cpu->cfg.ext_zcmt && !cpu->cfg.ext_icsr) {
- error_setg(errp, "Zcmt extension requires Zicsr extension");
- return;
- }
-
- /*
- * In principle Zve*x would also suffice here, were they supported
- * in qemu
- */
- if ((cpu->cfg.ext_zvbb || cpu->cfg.ext_zvkg || cpu->cfg.ext_zvkned ||
- cpu->cfg.ext_zvknha || cpu->cfg.ext_zvksed || cpu->cfg.ext_zvksh) &&
- !cpu->cfg.ext_zve32f) {
- error_setg(errp,
- "Vector crypto extensions require V or Zve* extensions");
- return;
- }
-
- if ((cpu->cfg.ext_zvbc || cpu->cfg.ext_zvknhb) && !cpu->cfg.ext_zve64f) {
- error_setg(
- errp,
- "Zvbc and Zvknhb extensions require V or Zve64{f,d} extensions");
- return;
- }
-
- if (cpu->cfg.ext_zk) {
- cpu->cfg.ext_zkn = true;
- cpu->cfg.ext_zkr = true;
- cpu->cfg.ext_zkt = true;
- }
-
- if (cpu->cfg.ext_zkn) {
- cpu->cfg.ext_zbkb = true;
- cpu->cfg.ext_zbkc = true;
- cpu->cfg.ext_zbkx = true;
- cpu->cfg.ext_zkne = true;
- cpu->cfg.ext_zknd = true;
- cpu->cfg.ext_zknh = true;
- }
-
- if (cpu->cfg.ext_zks) {
- cpu->cfg.ext_zbkb = true;
- cpu->cfg.ext_zbkc = true;
- cpu->cfg.ext_zbkx = true;
- cpu->cfg.ext_zksed = true;
- cpu->cfg.ext_zksh = true;
- }
-
- /*
- * Disable isa extensions based on priv spec after we
- * validated and set everything we need.
- */
- riscv_cpu_disable_priv_spec_isa_exts(cpu);
-}
-
#ifndef CONFIG_USER_ONLY
static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp)
{
@@ -1428,74 +1009,6 @@ static void riscv_cpu_finalize_features(RISCVCPU *cpu, Error **errp)
#endif
}
-static void riscv_cpu_validate_misa_priv(CPURISCVState *env, Error **errp)
-{
- if (riscv_has_ext(env, RVH) && env->priv_ver < PRIV_VERSION_1_12_0) {
- error_setg(errp, "H extension requires priv spec 1.12.0");
- return;
- }
-}
-
-static void riscv_cpu_realize_tcg(DeviceState *dev, Error **errp)
-{
- RISCVCPU *cpu = RISCV_CPU(dev);
- CPURISCVState *env = &cpu->env;
- Error *local_err = NULL;
-
- if (object_dynamic_cast(OBJECT(dev), TYPE_RISCV_CPU_HOST)) {
- error_setg(errp, "'host' CPU is not compatible with TCG acceleration");
- return;
- }
-
- riscv_cpu_validate_misa_mxl(cpu, &local_err);
- if (local_err != NULL) {
- error_propagate(errp, local_err);
- return;
- }
-
- riscv_cpu_validate_priv_spec(cpu, &local_err);
- if (local_err != NULL) {
- error_propagate(errp, local_err);
- return;
- }
-
- riscv_cpu_validate_misa_priv(env, &local_err);
- if (local_err != NULL) {
- error_propagate(errp, local_err);
- return;
- }
-
- if (cpu->cfg.epmp && !cpu->cfg.pmp) {
- /*
- * Enhanced PMP should only be available
- * on harts with PMP support
- */
- error_setg(errp, "Invalid configuration: EPMP requires PMP support");
- return;
- }
-
- riscv_cpu_validate_set_extensions(cpu, &local_err);
- if (local_err != NULL) {
- error_propagate(errp, local_err);
- return;
- }
-
-#ifndef CONFIG_USER_ONLY
- CPU(dev)->tcg_cflags |= CF_PCREL;
-
- if (cpu->cfg.ext_sstc) {
- riscv_timer_init(cpu);
- }
-
- if (cpu->cfg.pmu_num) {
- if (!riscv_pmu_init(cpu, cpu->cfg.pmu_num) && cpu->cfg.ext_sscofpmf) {
- cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
- riscv_pmu_timer_cb, cpu);
- }
- }
-#endif
-}
-
static void riscv_cpu_realize(DeviceState *dev, Error **errp)
{
CPUState *cs = CPU(dev);
@@ -1503,20 +1016,17 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
Error *local_err = NULL;
+ if (object_dynamic_cast(OBJECT(dev), TYPE_RISCV_CPU_ANY) != NULL) {
+ warn_report("The 'any' CPU is deprecated and will be "
+ "removed in the future.");
+ }
+
cpu_exec_realizefn(cs, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
return;
}
- if (tcg_enabled()) {
- riscv_cpu_realize_tcg(dev, &local_err);
- if (local_err != NULL) {
- error_propagate(errp, local_err);
- return;
- }
- }
-
riscv_cpu_finalize_features(cpu, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
@@ -1565,7 +1075,7 @@ static void cpu_riscv_set_satp(Object *obj, Visitor *v, const char *name,
satp_map->init |= 1 << satp;
}
-static void riscv_add_satp_mode_properties(Object *obj)
+void riscv_add_satp_mode_properties(Object *obj)
{
RISCVCPU *cpu = RISCV_CPU(obj);
@@ -1647,55 +1157,22 @@ static void riscv_cpu_set_irq(void *opaque, int irq, int level)
}
#endif /* CONFIG_USER_ONLY */
-static void riscv_cpu_init(Object *obj)
+static bool riscv_cpu_is_dynamic(Object *cpu_obj)
{
-#ifndef CONFIG_USER_ONLY
- qdev_init_gpio_in(DEVICE(obj), riscv_cpu_set_irq,
- IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX);
-#endif /* CONFIG_USER_ONLY */
+ return object_dynamic_cast(cpu_obj, TYPE_RISCV_DYNAMIC_CPU) != NULL;
}
-typedef struct RISCVCPUMisaExtConfig {
- const char *name;
- const char *description;
- target_ulong misa_bit;
- bool enabled;
-} RISCVCPUMisaExtConfig;
-
-static void cpu_set_misa_ext_cfg(Object *obj, Visitor *v, const char *name,
- void *opaque, Error **errp)
+static void riscv_cpu_post_init(Object *obj)
{
- const RISCVCPUMisaExtConfig *misa_ext_cfg = opaque;
- target_ulong misa_bit = misa_ext_cfg->misa_bit;
- RISCVCPU *cpu = RISCV_CPU(obj);
- CPURISCVState *env = &cpu->env;
- bool value;
-
- if (!visit_type_bool(v, name, &value, errp)) {
- return;
- }
-
- if (value) {
- env->misa_ext |= misa_bit;
- env->misa_ext_mask |= misa_bit;
- } else {
- env->misa_ext &= ~misa_bit;
- env->misa_ext_mask &= ~misa_bit;
- }
+ accel_cpu_instance_init(CPU(obj));
}
-static void cpu_get_misa_ext_cfg(Object *obj, Visitor *v, const char *name,
- void *opaque, Error **errp)
+static void riscv_cpu_init(Object *obj)
{
- const RISCVCPUMisaExtConfig *misa_ext_cfg = opaque;
- target_ulong misa_bit = misa_ext_cfg->misa_bit;
- RISCVCPU *cpu = RISCV_CPU(obj);
- CPURISCVState *env = &cpu->env;
- bool value;
-
- value = env->misa_ext & misa_bit;
-
- visit_type_bool(v, name, &value, errp);
+#ifndef CONFIG_USER_ONLY
+ qdev_init_gpio_in(DEVICE(obj), riscv_cpu_set_irq,
+ IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX);
+#endif /* CONFIG_USER_ONLY */
}
typedef struct misa_ext_info {
@@ -1758,231 +1235,150 @@ const char *riscv_get_misa_ext_description(uint32_t bit)
return val;
}
-#define MISA_CFG(_bit, _enabled) \
- {.misa_bit = _bit, .enabled = _enabled}
-
-static RISCVCPUMisaExtConfig misa_ext_cfgs[] = {
- MISA_CFG(RVA, true),
- MISA_CFG(RVC, true),
- MISA_CFG(RVD, true),
- MISA_CFG(RVF, true),
- MISA_CFG(RVI, true),
- MISA_CFG(RVE, false),
- MISA_CFG(RVM, true),
- MISA_CFG(RVS, true),
- MISA_CFG(RVU, true),
- MISA_CFG(RVH, true),
- MISA_CFG(RVJ, false),
- MISA_CFG(RVV, false),
- MISA_CFG(RVG, false),
-};
-
-static void riscv_cpu_add_misa_properties(Object *cpu_obj)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(misa_ext_cfgs); i++) {
- RISCVCPUMisaExtConfig *misa_cfg = &misa_ext_cfgs[i];
- int bit = misa_cfg->misa_bit;
+#define MULTI_EXT_CFG_BOOL(_name, _prop, _defval) \
+ {.name = _name, .offset = CPU_CFG_OFFSET(_prop), \
+ .enabled = _defval}
- misa_cfg->name = riscv_get_misa_ext_name(bit);
- misa_cfg->description = riscv_get_misa_ext_description(bit);
-
- /* Check if KVM already created the property */
- if (object_property_find(cpu_obj, misa_cfg->name)) {
- continue;
- }
-
- object_property_add(cpu_obj, misa_cfg->name, "bool",
- cpu_get_misa_ext_cfg,
- cpu_set_misa_ext_cfg,
- NULL, (void *)misa_cfg);
- object_property_set_description(cpu_obj, misa_cfg->name,
- misa_cfg->description);
- object_property_set_bool(cpu_obj, misa_cfg->name,
- misa_cfg->enabled, NULL);
- }
-}
-
-static Property riscv_cpu_extensions[] = {
+const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = {
/* Defaults for standard extensions */
- DEFINE_PROP_UINT8("pmu-num", RISCVCPU, cfg.pmu_num, 16),
- DEFINE_PROP_BOOL("sscofpmf", RISCVCPU, cfg.ext_sscofpmf, false),
- DEFINE_PROP_BOOL("Zifencei", RISCVCPU, cfg.ext_ifencei, true),
- DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true),
- DEFINE_PROP_BOOL("Zihintntl", RISCVCPU, cfg.ext_zihintntl, true),
- DEFINE_PROP_BOOL("Zihintpause", RISCVCPU, cfg.ext_zihintpause, true),
- DEFINE_PROP_BOOL("Zawrs", RISCVCPU, cfg.ext_zawrs, true),
- DEFINE_PROP_BOOL("Zfa", RISCVCPU, cfg.ext_zfa, true),
- DEFINE_PROP_BOOL("Zfh", RISCVCPU, cfg.ext_zfh, false),
- DEFINE_PROP_BOOL("Zfhmin", RISCVCPU, cfg.ext_zfhmin, false),
- DEFINE_PROP_BOOL("Zve32f", RISCVCPU, cfg.ext_zve32f, false),
- DEFINE_PROP_BOOL("Zve64f", RISCVCPU, cfg.ext_zve64f, false),
- DEFINE_PROP_BOOL("Zve64d", RISCVCPU, cfg.ext_zve64d, false),
- DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
- DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
- DEFINE_PROP_BOOL("sstc", RISCVCPU, cfg.ext_sstc, true),
+ MULTI_EXT_CFG_BOOL("sscofpmf", ext_sscofpmf, false),
+ MULTI_EXT_CFG_BOOL("zifencei", ext_ifencei, true),
+ MULTI_EXT_CFG_BOOL("zicsr", ext_icsr, true),
+ MULTI_EXT_CFG_BOOL("zihintntl", ext_zihintntl, true),
+ MULTI_EXT_CFG_BOOL("zihintpause", ext_zihintpause, true),
+ MULTI_EXT_CFG_BOOL("zawrs", ext_zawrs, true),
+ MULTI_EXT_CFG_BOOL("zfa", ext_zfa, true),
+ MULTI_EXT_CFG_BOOL("zfh", ext_zfh, false),
+ MULTI_EXT_CFG_BOOL("zfhmin", ext_zfhmin, false),
+ MULTI_EXT_CFG_BOOL("zve32f", ext_zve32f, false),
+ MULTI_EXT_CFG_BOOL("zve64f", ext_zve64f, false),
+ MULTI_EXT_CFG_BOOL("zve64d", ext_zve64d, false),
+ MULTI_EXT_CFG_BOOL("sstc", ext_sstc, true),
+
+ MULTI_EXT_CFG_BOOL("smstateen", ext_smstateen, false),
+ MULTI_EXT_CFG_BOOL("svadu", ext_svadu, true),
+ MULTI_EXT_CFG_BOOL("svinval", ext_svinval, false),
+ MULTI_EXT_CFG_BOOL("svnapot", ext_svnapot, false),
+ MULTI_EXT_CFG_BOOL("svpbmt", ext_svpbmt, false),
+
+ MULTI_EXT_CFG_BOOL("zba", ext_zba, true),
+ MULTI_EXT_CFG_BOOL("zbb", ext_zbb, true),
+ MULTI_EXT_CFG_BOOL("zbc", ext_zbc, true),
+ MULTI_EXT_CFG_BOOL("zbkb", ext_zbkb, false),
+ MULTI_EXT_CFG_BOOL("zbkc", ext_zbkc, false),
+ MULTI_EXT_CFG_BOOL("zbkx", ext_zbkx, false),
+ MULTI_EXT_CFG_BOOL("zbs", ext_zbs, true),
+ MULTI_EXT_CFG_BOOL("zk", ext_zk, false),
+ MULTI_EXT_CFG_BOOL("zkn", ext_zkn, false),
+ MULTI_EXT_CFG_BOOL("zknd", ext_zknd, false),
+ MULTI_EXT_CFG_BOOL("zkne", ext_zkne, false),
+ MULTI_EXT_CFG_BOOL("zknh", ext_zknh, false),
+ MULTI_EXT_CFG_BOOL("zkr", ext_zkr, false),
+ MULTI_EXT_CFG_BOOL("zks", ext_zks, false),
+ MULTI_EXT_CFG_BOOL("zksed", ext_zksed, false),
+ MULTI_EXT_CFG_BOOL("zksh", ext_zksh, false),
+ MULTI_EXT_CFG_BOOL("zkt", ext_zkt, false),
+
+ MULTI_EXT_CFG_BOOL("zdinx", ext_zdinx, false),
+ MULTI_EXT_CFG_BOOL("zfinx", ext_zfinx, false),
+ MULTI_EXT_CFG_BOOL("zhinx", ext_zhinx, false),
+ MULTI_EXT_CFG_BOOL("zhinxmin", ext_zhinxmin, false),
+
+ MULTI_EXT_CFG_BOOL("zicbom", ext_icbom, true),
+ MULTI_EXT_CFG_BOOL("zicboz", ext_icboz, true),
+
+ MULTI_EXT_CFG_BOOL("zmmul", ext_zmmul, false),
+
+ MULTI_EXT_CFG_BOOL("zca", ext_zca, false),
+ MULTI_EXT_CFG_BOOL("zcb", ext_zcb, false),
+ MULTI_EXT_CFG_BOOL("zcd", ext_zcd, false),
+ MULTI_EXT_CFG_BOOL("zce", ext_zce, false),
+ MULTI_EXT_CFG_BOOL("zcf", ext_zcf, false),
+ MULTI_EXT_CFG_BOOL("zcmp", ext_zcmp, false),
+ MULTI_EXT_CFG_BOOL("zcmt", ext_zcmt, false),
+ MULTI_EXT_CFG_BOOL("zicond", ext_zicond, false),
- DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec),
- DEFINE_PROP_STRING("vext_spec", RISCVCPU, cfg.vext_spec),
- DEFINE_PROP_UINT16("vlen", RISCVCPU, cfg.vlen, 128),
- DEFINE_PROP_UINT16("elen", RISCVCPU, cfg.elen, 64),
+ DEFINE_PROP_END_OF_LIST(),
+};
- DEFINE_PROP_BOOL("smstateen", RISCVCPU, cfg.ext_smstateen, false),
- DEFINE_PROP_BOOL("svadu", RISCVCPU, cfg.ext_svadu, true),
- DEFINE_PROP_BOOL("svinval", RISCVCPU, cfg.ext_svinval, false),
- DEFINE_PROP_BOOL("svnapot", RISCVCPU, cfg.ext_svnapot, false),
- DEFINE_PROP_BOOL("svpbmt", RISCVCPU, cfg.ext_svpbmt, false),
-
- DEFINE_PROP_BOOL("zba", RISCVCPU, cfg.ext_zba, true),
- DEFINE_PROP_BOOL("zbb", RISCVCPU, cfg.ext_zbb, true),
- DEFINE_PROP_BOOL("zbc", RISCVCPU, cfg.ext_zbc, true),
- DEFINE_PROP_BOOL("zbkb", RISCVCPU, cfg.ext_zbkb, false),
- DEFINE_PROP_BOOL("zbkc", RISCVCPU, cfg.ext_zbkc, false),
- DEFINE_PROP_BOOL("zbkx", RISCVCPU, cfg.ext_zbkx, false),
- DEFINE_PROP_BOOL("zbs", RISCVCPU, cfg.ext_zbs, true),
- DEFINE_PROP_BOOL("zk", RISCVCPU, cfg.ext_zk, false),
- DEFINE_PROP_BOOL("zkn", RISCVCPU, cfg.ext_zkn, false),
- DEFINE_PROP_BOOL("zknd", RISCVCPU, cfg.ext_zknd, false),
- DEFINE_PROP_BOOL("zkne", RISCVCPU, cfg.ext_zkne, false),
- DEFINE_PROP_BOOL("zknh", RISCVCPU, cfg.ext_zknh, false),
- DEFINE_PROP_BOOL("zkr", RISCVCPU, cfg.ext_zkr, false),
- DEFINE_PROP_BOOL("zks", RISCVCPU, cfg.ext_zks, false),
- DEFINE_PROP_BOOL("zksed", RISCVCPU, cfg.ext_zksed, false),
- DEFINE_PROP_BOOL("zksh", RISCVCPU, cfg.ext_zksh, false),
- DEFINE_PROP_BOOL("zkt", RISCVCPU, cfg.ext_zkt, false),
-
- DEFINE_PROP_BOOL("zdinx", RISCVCPU, cfg.ext_zdinx, false),
- DEFINE_PROP_BOOL("zfinx", RISCVCPU, cfg.ext_zfinx, false),
- DEFINE_PROP_BOOL("zhinx", RISCVCPU, cfg.ext_zhinx, false),
- DEFINE_PROP_BOOL("zhinxmin", RISCVCPU, cfg.ext_zhinxmin, false),
-
- DEFINE_PROP_BOOL("zicbom", RISCVCPU, cfg.ext_icbom, true),
- DEFINE_PROP_UINT16("cbom_blocksize", RISCVCPU, cfg.cbom_blocksize, 64),
- DEFINE_PROP_BOOL("zicboz", RISCVCPU, cfg.ext_icboz, true),
- DEFINE_PROP_UINT16("cboz_blocksize", RISCVCPU, cfg.cboz_blocksize, 64),
+const RISCVCPUMultiExtConfig riscv_cpu_vendor_exts[] = {
+ MULTI_EXT_CFG_BOOL("xtheadba", ext_xtheadba, false),
+ MULTI_EXT_CFG_BOOL("xtheadbb", ext_xtheadbb, false),
+ MULTI_EXT_CFG_BOOL("xtheadbs", ext_xtheadbs, false),
+ MULTI_EXT_CFG_BOOL("xtheadcmo", ext_xtheadcmo, false),
+ MULTI_EXT_CFG_BOOL("xtheadcondmov", ext_xtheadcondmov, false),
+ MULTI_EXT_CFG_BOOL("xtheadfmemidx", ext_xtheadfmemidx, false),
+ MULTI_EXT_CFG_BOOL("xtheadfmv", ext_xtheadfmv, false),
+ MULTI_EXT_CFG_BOOL("xtheadmac", ext_xtheadmac, false),
+ MULTI_EXT_CFG_BOOL("xtheadmemidx", ext_xtheadmemidx, false),
+ MULTI_EXT_CFG_BOOL("xtheadmempair", ext_xtheadmempair, false),
+ MULTI_EXT_CFG_BOOL("xtheadsync", ext_xtheadsync, false),
+ MULTI_EXT_CFG_BOOL("xventanacondops", ext_XVentanaCondOps, false),
- DEFINE_PROP_BOOL("zmmul", RISCVCPU, cfg.ext_zmmul, false),
-
- DEFINE_PROP_BOOL("zca", RISCVCPU, cfg.ext_zca, false),
- DEFINE_PROP_BOOL("zcb", RISCVCPU, cfg.ext_zcb, false),
- DEFINE_PROP_BOOL("zcd", RISCVCPU, cfg.ext_zcd, false),
- DEFINE_PROP_BOOL("zce", RISCVCPU, cfg.ext_zce, false),
- DEFINE_PROP_BOOL("zcf", RISCVCPU, cfg.ext_zcf, false),
- DEFINE_PROP_BOOL("zcmp", RISCVCPU, cfg.ext_zcmp, false),
- DEFINE_PROP_BOOL("zcmt", RISCVCPU, cfg.ext_zcmt, false),
- DEFINE_PROP_BOOL("zicond", RISCVCPU, cfg.ext_zicond, false),
-
- /* Vendor-specific custom extensions */
- DEFINE_PROP_BOOL("xtheadba", RISCVCPU, cfg.ext_xtheadba, false),
- DEFINE_PROP_BOOL("xtheadbb", RISCVCPU, cfg.ext_xtheadbb, false),
- DEFINE_PROP_BOOL("xtheadbs", RISCVCPU, cfg.ext_xtheadbs, false),
- DEFINE_PROP_BOOL("xtheadcmo", RISCVCPU, cfg.ext_xtheadcmo, false),
- DEFINE_PROP_BOOL("xtheadcondmov", RISCVCPU, cfg.ext_xtheadcondmov, false),
- DEFINE_PROP_BOOL("xtheadfmemidx", RISCVCPU, cfg.ext_xtheadfmemidx, false),
- DEFINE_PROP_BOOL("xtheadfmv", RISCVCPU, cfg.ext_xtheadfmv, false),
- DEFINE_PROP_BOOL("xtheadmac", RISCVCPU, cfg.ext_xtheadmac, false),
- DEFINE_PROP_BOOL("xtheadmemidx", RISCVCPU, cfg.ext_xtheadmemidx, false),
- DEFINE_PROP_BOOL("xtheadmempair", RISCVCPU, cfg.ext_xtheadmempair, false),
- DEFINE_PROP_BOOL("xtheadsync", RISCVCPU, cfg.ext_xtheadsync, false),
- DEFINE_PROP_BOOL("xventanacondops", RISCVCPU, cfg.ext_XVentanaCondOps, false),
-
- /* These are experimental so mark with 'x-' */
+ DEFINE_PROP_END_OF_LIST(),
+};
+/* These are experimental so mark with 'x-' */
+const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = {
/* ePMP 0.9.3 */
- DEFINE_PROP_BOOL("x-epmp", RISCVCPU, cfg.epmp, false),
- DEFINE_PROP_BOOL("x-smaia", RISCVCPU, cfg.ext_smaia, false),
- DEFINE_PROP_BOOL("x-ssaia", RISCVCPU, cfg.ext_ssaia, false),
+ MULTI_EXT_CFG_BOOL("x-epmp", epmp, false),
+ MULTI_EXT_CFG_BOOL("x-smaia", ext_smaia, false),
+ MULTI_EXT_CFG_BOOL("x-ssaia", ext_ssaia, false),
- DEFINE_PROP_BOOL("x-zvfh", RISCVCPU, cfg.ext_zvfh, false),
- DEFINE_PROP_BOOL("x-zvfhmin", RISCVCPU, cfg.ext_zvfhmin, false),
+ MULTI_EXT_CFG_BOOL("x-zvfh", ext_zvfh, false),
+ MULTI_EXT_CFG_BOOL("x-zvfhmin", ext_zvfhmin, false),
- DEFINE_PROP_BOOL("x-zfbfmin", RISCVCPU, cfg.ext_zfbfmin, false),
- DEFINE_PROP_BOOL("x-zvfbfmin", RISCVCPU, cfg.ext_zvfbfmin, false),
- DEFINE_PROP_BOOL("x-zvfbfwma", RISCVCPU, cfg.ext_zvfbfwma, false),
+ MULTI_EXT_CFG_BOOL("x-zfbfmin", ext_zfbfmin, false),
+ MULTI_EXT_CFG_BOOL("x-zvfbfmin", ext_zvfbfmin, false),
+ MULTI_EXT_CFG_BOOL("x-zvfbfwma", ext_zvfbfwma, false),
/* Vector cryptography extensions */
- DEFINE_PROP_BOOL("x-zvbb", RISCVCPU, cfg.ext_zvbb, false),
- DEFINE_PROP_BOOL("x-zvbc", RISCVCPU, cfg.ext_zvbc, false),
- DEFINE_PROP_BOOL("x-zvkg", RISCVCPU, cfg.ext_zvkg, false),
- DEFINE_PROP_BOOL("x-zvkned", RISCVCPU, cfg.ext_zvkned, false),
- DEFINE_PROP_BOOL("x-zvknha", RISCVCPU, cfg.ext_zvknha, false),
- DEFINE_PROP_BOOL("x-zvknhb", RISCVCPU, cfg.ext_zvknhb, false),
- DEFINE_PROP_BOOL("x-zvksed", RISCVCPU, cfg.ext_zvksed, false),
- DEFINE_PROP_BOOL("x-zvksh", RISCVCPU, cfg.ext_zvksh, false),
+ MULTI_EXT_CFG_BOOL("x-zvbb", ext_zvbb, false),
+ MULTI_EXT_CFG_BOOL("x-zvbc", ext_zvbc, false),
+ MULTI_EXT_CFG_BOOL("x-zvkg", ext_zvkg, false),
+ MULTI_EXT_CFG_BOOL("x-zvkned", ext_zvkned, false),
+ MULTI_EXT_CFG_BOOL("x-zvknha", ext_zvknha, false),
+ MULTI_EXT_CFG_BOOL("x-zvknhb", ext_zvknhb, false),
+ MULTI_EXT_CFG_BOOL("x-zvksed", ext_zvksed, false),
+ MULTI_EXT_CFG_BOOL("x-zvksh", ext_zvksh, false),
DEFINE_PROP_END_OF_LIST(),
};
+/* Deprecated entries marked for future removal */
+const RISCVCPUMultiExtConfig riscv_cpu_deprecated_exts[] = {
+ MULTI_EXT_CFG_BOOL("Zifencei", ext_ifencei, true),
+ MULTI_EXT_CFG_BOOL("Zicsr", ext_icsr, true),
+ MULTI_EXT_CFG_BOOL("Zihintntl", ext_zihintntl, true),
+ MULTI_EXT_CFG_BOOL("Zihintpause", ext_zihintpause, true),
+ MULTI_EXT_CFG_BOOL("Zawrs", ext_zawrs, true),
+ MULTI_EXT_CFG_BOOL("Zfa", ext_zfa, true),
+ MULTI_EXT_CFG_BOOL("Zfh", ext_zfh, false),
+ MULTI_EXT_CFG_BOOL("Zfhmin", ext_zfhmin, false),
+ MULTI_EXT_CFG_BOOL("Zve32f", ext_zve32f, false),
+ MULTI_EXT_CFG_BOOL("Zve64f", ext_zve64f, false),
+ MULTI_EXT_CFG_BOOL("Zve64d", ext_zve64d, false),
-#ifndef CONFIG_USER_ONLY
-static void cpu_set_cfg_unavailable(Object *obj, Visitor *v,
- const char *name,
- void *opaque, Error **errp)
-{
- const char *propname = opaque;
- bool value;
-
- if (!visit_type_bool(v, name, &value, errp)) {
- return;
- }
-
- if (value) {
- error_setg(errp, "extension %s is not available with KVM",
- propname);
- }
-}
-#endif
+ DEFINE_PROP_END_OF_LIST(),
+};
-/*
- * Add CPU properties with user-facing flags.
- *
- * This will overwrite existing env->misa_ext values with the
- * defaults set via riscv_cpu_add_misa_properties().
- */
-static void riscv_cpu_add_user_properties(Object *obj)
-{
- Property *prop;
- DeviceState *dev = DEVICE(obj);
+Property riscv_cpu_options[] = {
+ DEFINE_PROP_UINT8("pmu-num", RISCVCPU, cfg.pmu_num, 16),
-#ifndef CONFIG_USER_ONLY
- riscv_add_satp_mode_properties(obj);
+ DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
+ DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
- if (kvm_enabled()) {
- kvm_riscv_init_user_properties(obj);
- }
-#endif
+ DEFINE_PROP_STRING("priv_spec", RISCVCPU, cfg.priv_spec),
+ DEFINE_PROP_STRING("vext_spec", RISCVCPU, cfg.vext_spec),
- riscv_cpu_add_misa_properties(obj);
+ DEFINE_PROP_UINT16("vlen", RISCVCPU, cfg.vlen, 128),
+ DEFINE_PROP_UINT16("elen", RISCVCPU, cfg.elen, 64),
- for (prop = riscv_cpu_extensions; prop && prop->name; prop++) {
-#ifndef CONFIG_USER_ONLY
- if (kvm_enabled()) {
- /* Check if KVM created the property already */
- if (object_property_find(obj, prop->name)) {
- continue;
- }
+ DEFINE_PROP_UINT16("cbom_blocksize", RISCVCPU, cfg.cbom_blocksize, 64),
+ DEFINE_PROP_UINT16("cboz_blocksize", RISCVCPU, cfg.cboz_blocksize, 64),
- /*
- * Set the default to disabled for every extension
- * unknown to KVM and error out if the user attempts
- * to enable any of them.
- *
- * We're giving a pass for non-bool properties since they're
- * not related to the availability of extensions and can be
- * safely ignored as is.
- */
- if (prop->info == &qdev_prop_bool) {
- object_property_add(obj, prop->name, "bool",
- NULL, cpu_set_cfg_unavailable,
- NULL, (void *)prop->name);
- continue;
- }
- }
-#endif
- qdev_property_add_static(dev, prop);
- }
-}
+ DEFINE_PROP_END_OF_LIST(),
+};
static Property riscv_cpu_properties[] = {
DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true),
@@ -2051,30 +1447,6 @@ static const struct SysemuCPUOps riscv_sysemu_ops = {
};
#endif
-#include "hw/core/tcg-cpu-ops.h"
-
-static const struct TCGCPUOps riscv_tcg_ops = {
- .initialize = riscv_translate_init,
- .synchronize_from_tb = riscv_cpu_synchronize_from_tb,
- .restore_state_to_opc = riscv_restore_state_to_opc,
-
-#ifndef CONFIG_USER_ONLY
- .tlb_fill = riscv_cpu_tlb_fill,
- .cpu_exec_interrupt = riscv_cpu_exec_interrupt,
- .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 */
-};
-
-static bool riscv_cpu_is_dynamic(Object *cpu_obj)
-{
- return object_dynamic_cast(cpu_obj, TYPE_RISCV_DYNAMIC_CPU) != NULL;
-}
-
static void cpu_set_mvendorid(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
@@ -2212,7 +1584,6 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data)
#endif
cc->gdb_arch_name = riscv_gdb_arch_name;
cc->gdb_get_dynamic_xml = riscv_gdb_get_dynamic_xml;
- cc->tcg_ops = &riscv_tcg_ops;
object_class_property_add(c, "mvendorid", "uint32", cpu_get_mvendorid,
cpu_set_mvendorid, NULL, NULL);
@@ -2229,13 +1600,13 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data)
static void riscv_isa_string_ext(RISCVCPU *cpu, char **isa_str,
int max_str_len)
{
+ const RISCVIsaExtData *edata;
char *old = *isa_str;
char *new = *isa_str;
- int i;
- for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) {
- if (isa_ext_is_enabled(cpu, &isa_edata_arr[i])) {
- new = g_strconcat(old, "_", isa_edata_arr[i].name, NULL);
+ for (edata = isa_edata_arr; edata && edata->name; edata++) {
+ if (isa_ext_is_enabled(cpu, edata->ext_enable_offset)) {
+ new = g_strconcat(old, "_", edata->name, NULL);
g_free(old);
old = new;
}
@@ -2312,6 +1683,7 @@ static const TypeInfo riscv_cpu_type_infos[] = {
.instance_size = sizeof(RISCVCPU),
.instance_align = __alignof(RISCVCPU),
.instance_init = riscv_cpu_init,
+ .instance_post_init = riscv_cpu_post_init,
.abstract = true,
.class_size = sizeof(RISCVCPUClass),
.class_init = riscv_cpu_class_init,
@@ -2322,9 +1694,7 @@ static const TypeInfo riscv_cpu_type_infos[] = {
.abstract = true,
},
DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_ANY, riscv_any_cpu_init),
-#if defined(CONFIG_KVM)
- DEFINE_CPU(TYPE_RISCV_CPU_HOST, riscv_host_cpu_init),
-#endif
+ DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_MAX, riscv_max_cpu_init),
#if defined(TARGET_RISCV32)
DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE32, rv32_base_cpu_init),
DEFINE_CPU(TYPE_RISCV_CPU_IBEX, rv32_ibex_cpu_init),
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index ef9cf21..f8ffa5e 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -22,6 +22,7 @@
#include "hw/core/cpu.h"
#include "hw/registerfields.h"
+#include "hw/qdev-properties.h"
#include "exec/cpu-defs.h"
#include "qemu/cpu-float.h"
#include "qom/object.h"
@@ -42,7 +43,7 @@
#define RV(x) ((target_ulong)1 << (x - 'A'))
/*
- * Consider updating misa_ext_info_arr[] and misa_ext_cfgs[]
+ * Update misa_bits[], misa_ext_info_arr[] and misa_ext_cfgs[]
* when adding new MISA bits here.
*/
#define RVI RV('I')
@@ -59,9 +60,12 @@
#define RVJ RV('J')
#define RVG RV('G')
+extern const uint32_t misa_bits[];
const char *riscv_get_misa_ext_name(uint32_t bit);
const char *riscv_get_misa_ext_description(uint32_t bit);
+#define CPU_CFG_OFFSET(_prop) offsetof(struct RISCVCPUConfig, _prop)
+
/* Privileged specification version */
enum {
PRIV_VERSION_1_10_0 = 0,
@@ -443,7 +447,6 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
bool probe, uintptr_t retaddr);
char *riscv_isa_string(RISCVCPU *cpu);
void riscv_cpu_list(void);
-void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp);
#define cpu_list riscv_cpu_list
#define cpu_mmu_index riscv_cpu_mmu_index
@@ -705,6 +708,33 @@ enum riscv_pmu_event_idx {
RISCV_PMU_EVENT_CACHE_ITLB_PREFETCH_MISS = 0x10021,
};
+/* used by tcg/tcg-cpu.c*/
+void isa_ext_update_enabled(RISCVCPU *cpu, uint32_t ext_offset, bool en);
+bool isa_ext_is_enabled(RISCVCPU *cpu, uint32_t ext_offset);
+void riscv_cpu_set_misa(CPURISCVState *env, RISCVMXL mxl, uint32_t ext);
+
+typedef struct RISCVCPUMultiExtConfig {
+ const char *name;
+ uint32_t offset;
+ bool enabled;
+} RISCVCPUMultiExtConfig;
+
+extern const RISCVCPUMultiExtConfig riscv_cpu_extensions[];
+extern const RISCVCPUMultiExtConfig riscv_cpu_vendor_exts[];
+extern const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[];
+extern const RISCVCPUMultiExtConfig riscv_cpu_deprecated_exts[];
+extern Property riscv_cpu_options[];
+
+typedef struct isa_ext_data {
+ const char *name;
+ int min_version;
+ int ext_enable_offset;
+} RISCVIsaExtData;
+extern const RISCVIsaExtData isa_edata_arr[];
+char *riscv_cpu_get_name(RISCVCPU *cpu);
+
+void riscv_add_satp_mode_properties(Object *obj);
+
/* CSR function table */
extern riscv_csr_operations csr_ops[CSR_TABLE_SIZE];
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 3a02079..8c28241 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -65,8 +65,7 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc,
uint64_t *cs_base, uint32_t *pflags)
{
- CPUState *cs = env_cpu(env);
- RISCVCPU *cpu = RISCV_CPU(cs);
+ RISCVCPU *cpu = env_archcpu(env);
RISCVExtStatus fs, vs;
uint32_t flags = 0;
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 85a31dc..4b4ab56 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -21,6 +21,7 @@
#include "qemu/log.h"
#include "qemu/timer.h"
#include "cpu.h"
+#include "tcg/tcg-cpu.h"
#include "pmu.h"
#include "time_helper.h"
#include "exec/exec-all.h"
diff --git a/target/riscv/kvm-stub.c b/target/riscv/kvm-stub.c
deleted file mode 100644
index 4e8fc31..0000000
--- a/target/riscv/kvm-stub.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * QEMU KVM RISC-V specific function stubs
- *
- * Copyright (c) 2020 Huawei Technologies Co., Ltd
- *
- * 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 "cpu.h"
-#include "kvm_riscv.h"
-
-void kvm_riscv_reset_vcpu(RISCVCPU *cpu)
-{
- abort();
-}
-
-void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level)
-{
- abort();
-}
diff --git a/target/riscv/kvm.c b/target/riscv/kvm/kvm-cpu.c
index c01cfb0..090d617 100644
--- a/target/riscv/kvm.c
+++ b/target/riscv/kvm/kvm-cpu.c
@@ -31,6 +31,7 @@
#include "sysemu/kvm_int.h"
#include "cpu.h"
#include "trace.h"
+#include "hw/core/accel-cpu.h"
#include "hw/pci/pci.h"
#include "exec/memattrs.h"
#include "exec/address-spaces.h"
@@ -51,6 +52,8 @@ void riscv_kvm_aplic_request(void *opaque, int irq, int level)
kvm_set_irq(kvm_state, irq, !!level);
}
+static bool cap_has_mp_state;
+
static uint64_t kvm_riscv_reg_id(CPURISCVState *env, uint64_t type,
uint64_t idx)
{
@@ -205,10 +208,8 @@ static void kvm_riscv_update_cpu_misa_ext(RISCVCPU *cpu, CPUState *cs)
}
}
-#define CPUCFG(_prop) offsetof(struct RISCVCPUConfig, _prop)
-
#define KVM_EXT_CFG(_name, _prop, _reg_id) \
- {.name = _name, .offset = CPUCFG(_prop), \
+ {.name = _name, .offset = CPU_CFG_OFFSET(_prop), \
.kvm_reg_id = _reg_id}
static KVMCPUConfig kvm_multi_ext_cfgs[] = {
@@ -285,13 +286,13 @@ static void kvm_cpu_set_multi_ext_cfg(Object *obj, Visitor *v,
static KVMCPUConfig kvm_cbom_blocksize = {
.name = "cbom_blocksize",
- .offset = CPUCFG(cbom_blocksize),
+ .offset = CPU_CFG_OFFSET(cbom_blocksize),
.kvm_reg_id = KVM_REG_RISCV_CONFIG_REG(zicbom_block_size)
};
static KVMCPUConfig kvm_cboz_blocksize = {
.name = "cboz_blocksize",
- .offset = CPUCFG(cboz_blocksize),
+ .offset = CPU_CFG_OFFSET(cboz_blocksize),
.kvm_reg_id = KVM_REG_RISCV_CONFIG_REG(zicboz_block_size)
};
@@ -345,10 +346,58 @@ static void kvm_riscv_update_cpu_cfg_isa_ext(RISCVCPU *cpu, CPUState *cs)
}
}
+static void cpu_set_cfg_unavailable(Object *obj, Visitor *v,
+ const char *name,
+ void *opaque, Error **errp)
+{
+ const char *propname = opaque;
+ bool value;
+
+ if (!visit_type_bool(v, name, &value, errp)) {
+ return;
+ }
+
+ if (value) {
+ error_setg(errp, "extension %s is not available with KVM",
+ propname);
+ }
+}
+
+static void riscv_cpu_add_kvm_unavail_prop(Object *obj, const char *prop_name)
+{
+ /* Check if KVM created the property already */
+ if (object_property_find(obj, prop_name)) {
+ return;
+ }
+
+ /*
+ * Set the default to disabled for every extension
+ * unknown to KVM and error out if the user attempts
+ * to enable any of them.
+ */
+ object_property_add(obj, prop_name, "bool",
+ NULL, cpu_set_cfg_unavailable,
+ NULL, (void *)prop_name);
+}
+
+static void riscv_cpu_add_kvm_unavail_prop_array(Object *obj,
+ const RISCVCPUMultiExtConfig *array)
+{
+ const RISCVCPUMultiExtConfig *prop;
+
+ g_assert(array);
+
+ for (prop = array; prop && prop->name; prop++) {
+ riscv_cpu_add_kvm_unavail_prop(obj, prop->name);
+ }
+}
+
static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj)
{
int i;
+ riscv_add_satp_mode_properties(cpu_obj);
+
for (i = 0; i < ARRAY_SIZE(kvm_misa_ext_cfgs); i++) {
KVMCPUConfig *misa_cfg = &kvm_misa_ext_cfgs[i];
int bit = misa_cfg->offset;
@@ -364,6 +413,11 @@ static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj)
misa_cfg->description);
}
+ for (i = 0; misa_bits[i] != 0; i++) {
+ const char *ext_name = riscv_get_misa_ext_name(misa_bits[i]);
+ riscv_cpu_add_kvm_unavail_prop(cpu_obj, ext_name);
+ }
+
for (i = 0; i < ARRAY_SIZE(kvm_multi_ext_cfgs); i++) {
KVMCPUConfig *multi_cfg = &kvm_multi_ext_cfgs[i];
@@ -380,6 +434,10 @@ static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj)
object_property_add(cpu_obj, "cboz_blocksize", "uint16",
NULL, kvm_cpu_set_cbomz_blksize,
NULL, &kvm_cboz_blocksize);
+
+ riscv_cpu_add_kvm_unavail_prop_array(cpu_obj, riscv_cpu_extensions);
+ riscv_cpu_add_kvm_unavail_prop_array(cpu_obj, riscv_cpu_vendor_exts);
+ riscv_cpu_add_kvm_unavail_prop_array(cpu_obj, riscv_cpu_experimental_exts);
}
static int kvm_riscv_get_regs_core(CPUState *cs)
@@ -713,7 +771,8 @@ static void kvm_riscv_read_cbomz_blksize(RISCVCPU *cpu, KVMScratchCPU *kvmcpu,
}
}
-static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu)
+static void kvm_riscv_read_multiext_legacy(RISCVCPU *cpu,
+ KVMScratchCPU *kvmcpu)
{
CPURISCVState *env = &cpu->env;
uint64_t val;
@@ -734,8 +793,8 @@ static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu)
val = false;
} else {
error_report("Unable to read ISA_EXT KVM register %s, "
- "error %d", multi_ext_cfg->name, ret);
- kvm_riscv_destroy_scratch_vcpu(kvmcpu);
+ "error code: %s", multi_ext_cfg->name,
+ strerrorname_np(errno));
exit(EXIT_FAILURE);
}
} else {
@@ -754,7 +813,100 @@ static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu)
}
}
-void kvm_riscv_init_user_properties(Object *cpu_obj)
+static int uint64_cmp(const void *a, const void *b)
+{
+ uint64_t val1 = *(const uint64_t *)a;
+ uint64_t val2 = *(const uint64_t *)b;
+
+ if (val1 < val2) {
+ return -1;
+ }
+
+ if (val1 > val2) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu)
+{
+ KVMCPUConfig *multi_ext_cfg;
+ struct kvm_one_reg reg;
+ struct kvm_reg_list rl_struct;
+ struct kvm_reg_list *reglist;
+ uint64_t val, reg_id, *reg_search;
+ int i, ret;
+
+ rl_struct.n = 0;
+ ret = ioctl(kvmcpu->cpufd, KVM_GET_REG_LIST, &rl_struct);
+
+ /*
+ * If KVM_GET_REG_LIST isn't supported we'll get errno 22
+ * (EINVAL). Use read_legacy() in this case.
+ */
+ if (errno == EINVAL) {
+ return kvm_riscv_read_multiext_legacy(cpu, kvmcpu);
+ } else if (errno != E2BIG) {
+ /*
+ * E2BIG is an expected error message for the API since we
+ * don't know the number of registers. The right amount will
+ * be written in rl_struct.n.
+ *
+ * Error out if we get any other errno.
+ */
+ error_report("Error when accessing get-reg-list, code: %s",
+ strerrorname_np(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ reglist = g_malloc(sizeof(struct kvm_reg_list) +
+ rl_struct.n * sizeof(uint64_t));
+ reglist->n = rl_struct.n;
+ ret = ioctl(kvmcpu->cpufd, KVM_GET_REG_LIST, reglist);
+ if (ret) {
+ error_report("Error when reading KVM_GET_REG_LIST, code %s ",
+ strerrorname_np(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ /* sort reglist to use bsearch() */
+ qsort(&reglist->reg, reglist->n, sizeof(uint64_t), uint64_cmp);
+
+ for (i = 0; i < ARRAY_SIZE(kvm_multi_ext_cfgs); i++) {
+ multi_ext_cfg = &kvm_multi_ext_cfgs[i];
+ reg_id = kvm_riscv_reg_id(&cpu->env, KVM_REG_RISCV_ISA_EXT,
+ multi_ext_cfg->kvm_reg_id);
+ reg_search = bsearch(&reg_id, reglist->reg, reglist->n,
+ sizeof(uint64_t), uint64_cmp);
+ if (!reg_search) {
+ continue;
+ }
+
+ reg.id = reg_id;
+ reg.addr = (uint64_t)&val;
+ ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, &reg);
+ if (ret != 0) {
+ error_report("Unable to read ISA_EXT KVM register %s, "
+ "error code: %s", multi_ext_cfg->name,
+ strerrorname_np(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ multi_ext_cfg->supported = true;
+ kvm_cpu_cfg_set(cpu, multi_ext_cfg, val);
+ }
+
+ if (cpu->cfg.ext_icbom) {
+ kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cbom_blocksize);
+ }
+
+ if (cpu->cfg.ext_icboz) {
+ kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cboz_blocksize);
+ }
+}
+
+static void riscv_init_kvm_registers(Object *cpu_obj)
{
RISCVCPU *cpu = RISCV_CPU(cpu_obj);
KVMScratchCPU kvmcpu;
@@ -763,7 +915,6 @@ void kvm_riscv_init_user_properties(Object *cpu_obj)
return;
}
- kvm_riscv_add_cpu_user_properties(cpu_obj);
kvm_riscv_init_machine_ids(cpu, &kvmcpu);
kvm_riscv_init_misa_ext_mask(cpu, &kvmcpu);
kvm_riscv_init_multiext_cfg(cpu, &kvmcpu);
@@ -797,6 +948,24 @@ int kvm_arch_get_registers(CPUState *cs)
return ret;
}
+int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state)
+{
+ if (cap_has_mp_state) {
+ struct kvm_mp_state mp_state = {
+ .mp_state = state
+ };
+
+ int ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MP_STATE, &mp_state);
+ if (ret) {
+ fprintf(stderr, "%s: failed to sync MP_STATE %d/%s\n",
+ __func__, ret, strerror(-ret));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
int kvm_arch_put_registers(CPUState *cs, int level)
{
int ret = 0;
@@ -816,6 +985,18 @@ int kvm_arch_put_registers(CPUState *cs, int level)
return ret;
}
+ if (KVM_PUT_RESET_STATE == level) {
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ if (cs->cpu_index == 0) {
+ ret = kvm_riscv_sync_mpstate_to_kvm(cpu, KVM_MP_STATE_RUNNABLE);
+ } else {
+ ret = kvm_riscv_sync_mpstate_to_kvm(cpu, KVM_MP_STATE_STOPPED);
+ }
+ if (ret) {
+ return ret;
+ }
+ }
+
return ret;
}
@@ -928,6 +1109,7 @@ int kvm_arch_get_default_type(MachineState *ms)
int kvm_arch_init(MachineState *ms, KVMState *s)
{
+ cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE);
return 0;
}
@@ -1010,14 +1192,25 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
void kvm_riscv_reset_vcpu(RISCVCPU *cpu)
{
CPURISCVState *env = &cpu->env;
+ int i;
if (!kvm_enabled()) {
return;
}
+ for (i = 0; i < 32; i++) {
+ env->gpr[i] = 0;
+ }
env->pc = cpu->env.kernel_addr;
env->gpr[10] = kvm_arch_vcpu_id(CPU(cpu)); /* a0 */
env->gpr[11] = cpu->env.fdt_addr; /* a1 */
env->satp = 0;
+ env->mie = 0;
+ env->stvec = 0;
+ env->sscratch = 0;
+ env->sepc = 0;
+ env->scause = 0;
+ env->stval = 0;
+ env->mip = 0;
}
void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level)
@@ -1229,3 +1422,62 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled();
}
+
+static void kvm_cpu_instance_init(CPUState *cs)
+{
+ Object *obj = OBJECT(RISCV_CPU(cs));
+ DeviceState *dev = DEVICE(obj);
+
+ riscv_init_kvm_registers(obj);
+
+ kvm_riscv_add_cpu_user_properties(obj);
+
+ for (Property *prop = riscv_cpu_options; prop && prop->name; prop++) {
+ /* Check if we have a specific KVM handler for the option */
+ if (object_property_find(obj, prop->name)) {
+ continue;
+ }
+ qdev_property_add_static(dev, prop);
+ }
+}
+
+static void kvm_cpu_accel_class_init(ObjectClass *oc, void *data)
+{
+ AccelCPUClass *acc = ACCEL_CPU_CLASS(oc);
+
+ acc->cpu_instance_init = kvm_cpu_instance_init;
+}
+
+static const TypeInfo kvm_cpu_accel_type_info = {
+ .name = ACCEL_CPU_NAME("kvm"),
+
+ .parent = TYPE_ACCEL_CPU,
+ .class_init = kvm_cpu_accel_class_init,
+ .abstract = true,
+};
+static void kvm_cpu_accel_register_types(void)
+{
+ type_register_static(&kvm_cpu_accel_type_info);
+}
+type_init(kvm_cpu_accel_register_types);
+
+static void riscv_host_cpu_init(Object *obj)
+{
+ CPURISCVState *env = &RISCV_CPU(obj)->env;
+
+#if defined(TARGET_RISCV32)
+ env->misa_mxl_max = env->misa_mxl = MXL_RV32;
+#elif defined(TARGET_RISCV64)
+ env->misa_mxl_max = env->misa_mxl = MXL_RV64;
+#endif
+}
+
+static const TypeInfo riscv_kvm_cpu_type_infos[] = {
+ {
+ .name = TYPE_RISCV_CPU_HOST,
+ .parent = TYPE_RISCV_CPU,
+ .instance_init = riscv_host_cpu_init,
+ }
+};
+
+DEFINE_TYPES(riscv_kvm_cpu_type_infos)
diff --git a/target/riscv/kvm_riscv.h b/target/riscv/kvm/kvm_riscv.h
index de8c209..8329cfa 100644
--- a/target/riscv/kvm_riscv.h
+++ b/target/riscv/kvm/kvm_riscv.h
@@ -19,7 +19,6 @@
#ifndef QEMU_KVM_RISCV_H
#define QEMU_KVM_RISCV_H
-void kvm_riscv_init_user_properties(Object *cpu_obj);
void kvm_riscv_reset_vcpu(RISCVCPU *cpu);
void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level);
void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
@@ -27,5 +26,6 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
uint64_t aplic_base, uint64_t imsic_base,
uint64_t guest_num);
void riscv_kvm_aplic_request(void *opaque, int irq, int level);
+int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state);
#endif
diff --git a/target/riscv/kvm/meson.build b/target/riscv/kvm/meson.build
new file mode 100644
index 0000000..7e92415
--- /dev/null
+++ b/target/riscv/kvm/meson.build
@@ -0,0 +1 @@
+riscv_ss.add(when: 'CONFIG_KVM', if_true: files('kvm-cpu.c'))
diff --git a/target/riscv/meson.build b/target/riscv/meson.build
index ff60b21..a5e0734 100644
--- a/target/riscv/meson.build
+++ b/target/riscv/meson.build
@@ -24,7 +24,6 @@ riscv_ss.add(files(
'zce_helper.c',
'vcrypto_helper.c'
))
-riscv_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'), if_false: files('kvm-stub.c'))
riscv_system_ss = ss.source_set()
riscv_system_ss.add(files(
@@ -38,5 +37,8 @@ riscv_system_ss.add(files(
'riscv-qmp-cmds.c',
))
+subdir('tcg')
+subdir('kvm')
+
target_arch += {'riscv': riscv_ss}
target_system_arch += {'riscv': riscv_system_ss}
diff --git a/target/riscv/tcg/meson.build b/target/riscv/tcg/meson.build
new file mode 100644
index 0000000..061df3d
--- /dev/null
+++ b/target/riscv/tcg/meson.build
@@ -0,0 +1,2 @@
+riscv_ss.add(when: 'CONFIG_TCG', if_true: files(
+ 'tcg-cpu.c'))
diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c
new file mode 100644
index 0000000..a28918a
--- /dev/null
+++ b/target/riscv/tcg/tcg-cpu.c
@@ -0,0 +1,949 @@
+/*
+ * riscv TCG cpu class initialization
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * 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 "exec/exec-all.h"
+#include "tcg-cpu.h"
+#include "cpu.h"
+#include "pmu.h"
+#include "time_helper.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "qemu/accel.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "hw/core/accel-cpu.h"
+#include "hw/core/tcg-cpu-ops.h"
+#include "tcg/tcg.h"
+
+/* Hash that stores user set extensions */
+static GHashTable *multi_ext_user_opts;
+
+static bool cpu_cfg_ext_is_user_set(uint32_t ext_offset)
+{
+ return g_hash_table_contains(multi_ext_user_opts,
+ GUINT_TO_POINTER(ext_offset));
+}
+
+static void riscv_cpu_synchronize_from_tb(CPUState *cs,
+ const TranslationBlock *tb)
+{
+ if (!(tb_cflags(tb) & CF_PCREL)) {
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+ RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL);
+
+ tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL));
+
+ if (xl == MXL_RV32) {
+ env->pc = (int32_t) tb->pc;
+ } else {
+ env->pc = tb->pc;
+ }
+ }
+}
+
+static void riscv_restore_state_to_opc(CPUState *cs,
+ const TranslationBlock *tb,
+ const uint64_t *data)
+{
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+ RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL);
+ target_ulong pc;
+
+ if (tb_cflags(tb) & CF_PCREL) {
+ pc = (env->pc & TARGET_PAGE_MASK) | data[0];
+ } else {
+ pc = data[0];
+ }
+
+ if (xl == MXL_RV32) {
+ env->pc = (int32_t)pc;
+ } else {
+ env->pc = pc;
+ }
+ env->bins = data[1];
+}
+
+static const struct TCGCPUOps riscv_tcg_ops = {
+ .initialize = riscv_translate_init,
+ .synchronize_from_tb = riscv_cpu_synchronize_from_tb,
+ .restore_state_to_opc = riscv_restore_state_to_opc,
+
+#ifndef CONFIG_USER_ONLY
+ .tlb_fill = riscv_cpu_tlb_fill,
+ .cpu_exec_interrupt = riscv_cpu_exec_interrupt,
+ .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 */
+};
+
+static int cpu_cfg_ext_get_min_version(uint32_t ext_offset)
+{
+ const RISCVIsaExtData *edata;
+
+ for (edata = isa_edata_arr; edata && edata->name; edata++) {
+ if (edata->ext_enable_offset != ext_offset) {
+ continue;
+ }
+
+ return edata->min_version;
+ }
+
+ g_assert_not_reached();
+}
+
+static void cpu_cfg_ext_auto_update(RISCVCPU *cpu, uint32_t ext_offset,
+ bool value)
+{
+ CPURISCVState *env = &cpu->env;
+ bool prev_val = isa_ext_is_enabled(cpu, ext_offset);
+ int min_version;
+
+ if (prev_val == value) {
+ return;
+ }
+
+ if (cpu_cfg_ext_is_user_set(ext_offset)) {
+ return;
+ }
+
+ if (value && env->priv_ver != PRIV_VERSION_LATEST) {
+ /* Do not enable it if priv_ver is older than min_version */
+ min_version = cpu_cfg_ext_get_min_version(ext_offset);
+ if (env->priv_ver < min_version) {
+ return;
+ }
+ }
+
+ isa_ext_update_enabled(cpu, ext_offset, value);
+}
+
+static void riscv_cpu_validate_misa_priv(CPURISCVState *env, Error **errp)
+{
+ if (riscv_has_ext(env, RVH) && env->priv_ver < PRIV_VERSION_1_12_0) {
+ error_setg(errp, "H extension requires priv spec 1.12.0");
+ return;
+ }
+}
+
+static void riscv_cpu_validate_misa_mxl(RISCVCPU *cpu, Error **errp)
+{
+ RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu);
+ CPUClass *cc = CPU_CLASS(mcc);
+ CPURISCVState *env = &cpu->env;
+
+ /* Validate that MISA_MXL is set properly. */
+ switch (env->misa_mxl_max) {
+#ifdef TARGET_RISCV64
+ case MXL_RV64:
+ case MXL_RV128:
+ cc->gdb_core_xml_file = "riscv-64bit-cpu.xml";
+ break;
+#endif
+ case MXL_RV32:
+ cc->gdb_core_xml_file = "riscv-32bit-cpu.xml";
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (env->misa_mxl_max != env->misa_mxl) {
+ error_setg(errp, "misa_mxl_max must be equal to misa_mxl");
+ return;
+ }
+}
+
+static void riscv_cpu_validate_priv_spec(RISCVCPU *cpu, Error **errp)
+{
+ CPURISCVState *env = &cpu->env;
+ int priv_version = -1;
+
+ if (cpu->cfg.priv_spec) {
+ 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;
+ } else {
+ error_setg(errp,
+ "Unsupported privilege spec version '%s'",
+ cpu->cfg.priv_spec);
+ return;
+ }
+
+ env->priv_ver = priv_version;
+ }
+}
+
+static void riscv_cpu_validate_v(CPURISCVState *env, RISCVCPUConfig *cfg,
+ Error **errp)
+{
+ if (!is_power_of_2(cfg->vlen)) {
+ error_setg(errp, "Vector extension VLEN must be power of 2");
+ return;
+ }
+
+ if (cfg->vlen > RV_VLEN_MAX || cfg->vlen < 128) {
+ error_setg(errp,
+ "Vector extension implementation only supports VLEN "
+ "in the range [128, %d]", RV_VLEN_MAX);
+ return;
+ }
+
+ if (!is_power_of_2(cfg->elen)) {
+ error_setg(errp, "Vector extension ELEN must be power of 2");
+ return;
+ }
+
+ if (cfg->elen > 64 || cfg->elen < 8) {
+ error_setg(errp,
+ "Vector extension implementation only supports ELEN "
+ "in the range [8, 64]");
+ return;
+ }
+
+ if (cfg->vext_spec) {
+ if (!g_strcmp0(cfg->vext_spec, "v1.0")) {
+ env->vext_ver = VEXT_VERSION_1_00_0;
+ } else {
+ error_setg(errp, "Unsupported vector spec version '%s'",
+ cfg->vext_spec);
+ return;
+ }
+ } else if (env->vext_ver == 0) {
+ qemu_log("vector version is not specified, "
+ "use the default value v1.0\n");
+
+ env->vext_ver = VEXT_VERSION_1_00_0;
+ }
+}
+
+static void riscv_cpu_disable_priv_spec_isa_exts(RISCVCPU *cpu)
+{
+ CPURISCVState *env = &cpu->env;
+ const RISCVIsaExtData *edata;
+
+ /* Force disable extensions if priv spec version does not match */
+ for (edata = isa_edata_arr; edata && edata->name; edata++) {
+ if (isa_ext_is_enabled(cpu, edata->ext_enable_offset) &&
+ (env->priv_ver < edata->min_version)) {
+ isa_ext_update_enabled(cpu, edata->ext_enable_offset, false);
+#ifndef CONFIG_USER_ONLY
+ warn_report("disabling %s extension for hart 0x" TARGET_FMT_lx
+ " because privilege spec version does not match",
+ edata->name, env->mhartid);
+#else
+ warn_report("disabling %s extension because "
+ "privilege spec version does not match",
+ edata->name);
+#endif
+ }
+ }
+}
+
+/*
+ * Check consistency between chosen extensions while setting
+ * cpu->cfg accordingly.
+ */
+void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
+{
+ CPURISCVState *env = &cpu->env;
+ Error *local_err = NULL;
+
+ /* Do some ISA extension error checking */
+ if (riscv_has_ext(env, RVG) &&
+ !(riscv_has_ext(env, RVI) && riscv_has_ext(env, RVM) &&
+ riscv_has_ext(env, RVA) && riscv_has_ext(env, RVF) &&
+ riscv_has_ext(env, RVD) &&
+ cpu->cfg.ext_icsr && cpu->cfg.ext_ifencei)) {
+
+ if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_icsr)) &&
+ !cpu->cfg.ext_icsr) {
+ error_setg(errp, "RVG requires Zicsr but user set Zicsr to false");
+ return;
+ }
+
+ if (cpu_cfg_ext_is_user_set(CPU_CFG_OFFSET(ext_ifencei)) &&
+ !cpu->cfg.ext_ifencei) {
+ error_setg(errp, "RVG requires Zifencei but user set "
+ "Zifencei to false");
+ return;
+ }
+
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_icsr), true);
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_ifencei), true);
+
+ env->misa_ext |= RVI | RVM | RVA | RVF | RVD;
+ env->misa_ext_mask |= RVI | RVM | RVA | RVF | RVD;
+ }
+
+ if (riscv_has_ext(env, RVI) && riscv_has_ext(env, RVE)) {
+ error_setg(errp,
+ "I and E extensions are incompatible");
+ return;
+ }
+
+ if (!riscv_has_ext(env, RVI) && !riscv_has_ext(env, RVE)) {
+ error_setg(errp,
+ "Either I or E extension must be set");
+ return;
+ }
+
+ if (riscv_has_ext(env, RVS) && !riscv_has_ext(env, RVU)) {
+ error_setg(errp,
+ "Setting S extension without U extension is illegal");
+ return;
+ }
+
+ if (riscv_has_ext(env, RVH) && !riscv_has_ext(env, RVI)) {
+ error_setg(errp,
+ "H depends on an I base integer ISA with 32 x registers");
+ return;
+ }
+
+ if (riscv_has_ext(env, RVH) && !riscv_has_ext(env, RVS)) {
+ error_setg(errp, "H extension implicitly requires S-mode");
+ return;
+ }
+
+ if (riscv_has_ext(env, RVF) && !cpu->cfg.ext_icsr) {
+ error_setg(errp, "F extension requires Zicsr");
+ return;
+ }
+
+ if ((cpu->cfg.ext_zawrs) && !riscv_has_ext(env, RVA)) {
+ error_setg(errp, "Zawrs extension requires A extension");
+ return;
+ }
+
+ if (cpu->cfg.ext_zfa && !riscv_has_ext(env, RVF)) {
+ error_setg(errp, "Zfa extension requires F extension");
+ return;
+ }
+
+ if (cpu->cfg.ext_zfh) {
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zfhmin), true);
+ }
+
+ if (cpu->cfg.ext_zfhmin && !riscv_has_ext(env, RVF)) {
+ error_setg(errp, "Zfh/Zfhmin extensions require F extension");
+ return;
+ }
+
+ if (cpu->cfg.ext_zfbfmin && !riscv_has_ext(env, RVF)) {
+ error_setg(errp, "Zfbfmin extension depends on F extension");
+ return;
+ }
+
+ if (riscv_has_ext(env, RVD) && !riscv_has_ext(env, RVF)) {
+ error_setg(errp, "D extension requires F extension");
+ return;
+ }
+
+ if (riscv_has_ext(env, RVV)) {
+ riscv_cpu_validate_v(env, &cpu->cfg, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ /* The V vector extension depends on the Zve64d extension */
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve64d), true);
+ }
+
+ /* The Zve64d extension depends on the Zve64f extension */
+ if (cpu->cfg.ext_zve64d) {
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve64f), true);
+ }
+
+ /* The Zve64f extension depends on the Zve32f extension */
+ if (cpu->cfg.ext_zve64f) {
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zve32f), true);
+ }
+
+ if (cpu->cfg.ext_zve64d && !riscv_has_ext(env, RVD)) {
+ error_setg(errp, "Zve64d/V extensions require D extension");
+ return;
+ }
+
+ if (cpu->cfg.ext_zve32f && !riscv_has_ext(env, RVF)) {
+ error_setg(errp, "Zve32f/Zve64f extensions require F extension");
+ return;
+ }
+
+ if (cpu->cfg.ext_zvfh) {
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zvfhmin), true);
+ }
+
+ if (cpu->cfg.ext_zvfhmin && !cpu->cfg.ext_zve32f) {
+ error_setg(errp, "Zvfh/Zvfhmin extensions require Zve32f extension");
+ return;
+ }
+
+ if (cpu->cfg.ext_zvfh && !cpu->cfg.ext_zfhmin) {
+ error_setg(errp, "Zvfh extensions requires Zfhmin extension");
+ return;
+ }
+
+ if (cpu->cfg.ext_zvfbfmin && !cpu->cfg.ext_zfbfmin) {
+ error_setg(errp, "Zvfbfmin extension depends on Zfbfmin extension");
+ return;
+ }
+
+ if (cpu->cfg.ext_zvfbfmin && !cpu->cfg.ext_zve32f) {
+ error_setg(errp, "Zvfbfmin extension depends on Zve32f extension");
+ return;
+ }
+
+ if (cpu->cfg.ext_zvfbfwma && !cpu->cfg.ext_zvfbfmin) {
+ error_setg(errp, "Zvfbfwma extension depends on Zvfbfmin extension");
+ return;
+ }
+
+ /* Set the ISA extensions, checks should have happened above */
+ if (cpu->cfg.ext_zhinx) {
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zca), true);
+ }
+
+ if ((cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinxmin) && !cpu->cfg.ext_zfinx) {
+ error_setg(errp, "Zdinx/Zhinx/Zhinxmin extensions require Zfinx");
+ return;
+ }
+
+ if (cpu->cfg.ext_zfinx) {
+ if (!cpu->cfg.ext_icsr) {
+ error_setg(errp, "Zfinx extension requires Zicsr");
+ return;
+ }
+ if (riscv_has_ext(env, RVF)) {
+ error_setg(errp,
+ "Zfinx cannot be supported together with F extension");
+ return;
+ }
+ }
+
+ if (cpu->cfg.ext_zce) {
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zca), true);
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcb), true);
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcmp), true);
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcmt), true);
+ if (riscv_has_ext(env, RVF) && env->misa_mxl_max == MXL_RV32) {
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcf), true);
+ }
+ }
+
+ /* zca, zcd and zcf has a PRIV 1.12.0 restriction */
+ if (riscv_has_ext(env, RVC) && env->priv_ver >= PRIV_VERSION_1_12_0) {
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zca), true);
+ if (riscv_has_ext(env, RVF) && env->misa_mxl_max == MXL_RV32) {
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcf), true);
+ }
+ if (riscv_has_ext(env, RVD)) {
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcd), true);
+ }
+ }
+
+ if (env->misa_mxl_max != MXL_RV32 && cpu->cfg.ext_zcf) {
+ error_setg(errp, "Zcf extension is only relevant to RV32");
+ return;
+ }
+
+ if (!riscv_has_ext(env, RVF) && cpu->cfg.ext_zcf) {
+ error_setg(errp, "Zcf extension requires F extension");
+ return;
+ }
+
+ if (!riscv_has_ext(env, RVD) && cpu->cfg.ext_zcd) {
+ error_setg(errp, "Zcd extension requires D extension");
+ return;
+ }
+
+ if ((cpu->cfg.ext_zcf || cpu->cfg.ext_zcd || cpu->cfg.ext_zcb ||
+ cpu->cfg.ext_zcmp || cpu->cfg.ext_zcmt) && !cpu->cfg.ext_zca) {
+ error_setg(errp, "Zcf/Zcd/Zcb/Zcmp/Zcmt extensions require Zca "
+ "extension");
+ return;
+ }
+
+ if (cpu->cfg.ext_zcd && (cpu->cfg.ext_zcmp || cpu->cfg.ext_zcmt)) {
+ error_setg(errp, "Zcmp/Zcmt extensions are incompatible with "
+ "Zcd extension");
+ return;
+ }
+
+ if (cpu->cfg.ext_zcmt && !cpu->cfg.ext_icsr) {
+ error_setg(errp, "Zcmt extension requires Zicsr extension");
+ return;
+ }
+
+ /*
+ * In principle Zve*x would also suffice here, were they supported
+ * in qemu
+ */
+ if ((cpu->cfg.ext_zvbb || cpu->cfg.ext_zvkg || cpu->cfg.ext_zvkned ||
+ cpu->cfg.ext_zvknha || cpu->cfg.ext_zvksed || cpu->cfg.ext_zvksh) &&
+ !cpu->cfg.ext_zve32f) {
+ error_setg(errp,
+ "Vector crypto extensions require V or Zve* extensions");
+ return;
+ }
+
+ if ((cpu->cfg.ext_zvbc || cpu->cfg.ext_zvknhb) && !cpu->cfg.ext_zve64f) {
+ error_setg(
+ errp,
+ "Zvbc and Zvknhb extensions require V or Zve64{f,d} extensions");
+ return;
+ }
+
+ if (cpu->cfg.ext_zk) {
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zkn), true);
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zkr), true);
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zkt), true);
+ }
+
+ if (cpu->cfg.ext_zkn) {
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkb), true);
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkc), true);
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkx), true);
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zkne), true);
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zknd), true);
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zknh), true);
+ }
+
+ if (cpu->cfg.ext_zks) {
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkb), true);
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkc), true);
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zbkx), true);
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zksed), true);
+ cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zksh), true);
+ }
+
+ /*
+ * Disable isa extensions based on priv spec after we
+ * validated and set everything we need.
+ */
+ riscv_cpu_disable_priv_spec_isa_exts(cpu);
+}
+
+static bool riscv_cpu_is_generic(Object *cpu_obj)
+{
+ return object_dynamic_cast(cpu_obj, TYPE_RISCV_DYNAMIC_CPU) != NULL;
+}
+
+/*
+ * We'll get here via the following path:
+ *
+ * riscv_cpu_realize()
+ * -> cpu_exec_realizefn()
+ * -> tcg_cpu_realize() (via accel_cpu_common_realize())
+ */
+static bool tcg_cpu_realize(CPUState *cs, Error **errp)
+{
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+ Error *local_err = NULL;
+
+ if (object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_CPU_HOST)) {
+ g_autofree char *name = riscv_cpu_get_name(cpu);
+ error_setg(errp, "'%s' CPU is not compatible with TCG acceleration",
+ name);
+ return false;
+ }
+
+ riscv_cpu_validate_misa_mxl(cpu, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return false;
+ }
+
+ riscv_cpu_validate_priv_spec(cpu, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return false;
+ }
+
+ riscv_cpu_validate_misa_priv(env, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return false;
+ }
+
+ if (cpu->cfg.epmp && !cpu->cfg.pmp) {
+ /*
+ * Enhanced PMP should only be available
+ * on harts with PMP support
+ */
+ error_setg(errp, "Invalid configuration: EPMP requires PMP support");
+ return false;
+ }
+
+ riscv_cpu_validate_set_extensions(cpu, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return false;
+ }
+
+#ifndef CONFIG_USER_ONLY
+ CPU(cs)->tcg_cflags |= CF_PCREL;
+
+ if (cpu->cfg.ext_sstc) {
+ riscv_timer_init(cpu);
+ }
+
+ if (cpu->cfg.pmu_num) {
+ if (!riscv_pmu_init(cpu, cpu->cfg.pmu_num) && cpu->cfg.ext_sscofpmf) {
+ cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ riscv_pmu_timer_cb, cpu);
+ }
+ }
+#endif
+
+ return true;
+}
+
+typedef struct RISCVCPUMisaExtConfig {
+ target_ulong misa_bit;
+ bool enabled;
+} RISCVCPUMisaExtConfig;
+
+static void cpu_set_misa_ext_cfg(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ const RISCVCPUMisaExtConfig *misa_ext_cfg = opaque;
+ target_ulong misa_bit = misa_ext_cfg->misa_bit;
+ RISCVCPU *cpu = RISCV_CPU(obj);
+ CPURISCVState *env = &cpu->env;
+ bool generic_cpu = riscv_cpu_is_generic(obj);
+ bool prev_val, value;
+
+ if (!visit_type_bool(v, name, &value, errp)) {
+ return;
+ }
+
+ prev_val = env->misa_ext & misa_bit;
+
+ if (value == prev_val) {
+ return;
+ }
+
+ if (value) {
+ if (!generic_cpu) {
+ g_autofree char *cpuname = riscv_cpu_get_name(cpu);
+ error_setg(errp, "'%s' CPU does not allow enabling extensions",
+ cpuname);
+ return;
+ }
+
+ env->misa_ext |= misa_bit;
+ env->misa_ext_mask |= misa_bit;
+ } else {
+ env->misa_ext &= ~misa_bit;
+ env->misa_ext_mask &= ~misa_bit;
+ }
+}
+
+static void cpu_get_misa_ext_cfg(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ const RISCVCPUMisaExtConfig *misa_ext_cfg = opaque;
+ target_ulong misa_bit = misa_ext_cfg->misa_bit;
+ RISCVCPU *cpu = RISCV_CPU(obj);
+ CPURISCVState *env = &cpu->env;
+ bool value;
+
+ value = env->misa_ext & misa_bit;
+
+ visit_type_bool(v, name, &value, errp);
+}
+
+#define MISA_CFG(_bit, _enabled) \
+ {.misa_bit = _bit, .enabled = _enabled}
+
+static const RISCVCPUMisaExtConfig misa_ext_cfgs[] = {
+ MISA_CFG(RVA, true),
+ MISA_CFG(RVC, true),
+ MISA_CFG(RVD, true),
+ MISA_CFG(RVF, true),
+ MISA_CFG(RVI, true),
+ MISA_CFG(RVE, false),
+ MISA_CFG(RVM, true),
+ MISA_CFG(RVS, true),
+ MISA_CFG(RVU, true),
+ MISA_CFG(RVH, true),
+ MISA_CFG(RVJ, false),
+ MISA_CFG(RVV, false),
+ MISA_CFG(RVG, false),
+};
+
+/*
+ * We do not support user choice tracking for MISA
+ * extensions yet because, so far, we do not silently
+ * change MISA bits during realize() (RVG enables MISA
+ * bits but the user is warned about it).
+ */
+static void riscv_cpu_add_misa_properties(Object *cpu_obj)
+{
+ bool use_def_vals = riscv_cpu_is_generic(cpu_obj);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(misa_ext_cfgs); i++) {
+ const RISCVCPUMisaExtConfig *misa_cfg = &misa_ext_cfgs[i];
+ int bit = misa_cfg->misa_bit;
+ const char *name = riscv_get_misa_ext_name(bit);
+ const char *desc = riscv_get_misa_ext_description(bit);
+
+ /* Check if KVM already created the property */
+ if (object_property_find(cpu_obj, name)) {
+ continue;
+ }
+
+ object_property_add(cpu_obj, name, "bool",
+ cpu_get_misa_ext_cfg,
+ cpu_set_misa_ext_cfg,
+ NULL, (void *)misa_cfg);
+ object_property_set_description(cpu_obj, name, desc);
+ if (use_def_vals) {
+ object_property_set_bool(cpu_obj, name, misa_cfg->enabled, NULL);
+ }
+ }
+}
+
+static bool cpu_ext_is_deprecated(const char *ext_name)
+{
+ return isupper(ext_name[0]);
+}
+
+/*
+ * String will be allocated in the heap. Caller is responsible
+ * for freeing it.
+ */
+static char *cpu_ext_to_lower(const char *ext_name)
+{
+ char *ret = g_malloc0(strlen(ext_name) + 1);
+
+ strcpy(ret, ext_name);
+ ret[0] = tolower(ret[0]);
+
+ return ret;
+}
+
+static void cpu_set_multi_ext_cfg(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ const RISCVCPUMultiExtConfig *multi_ext_cfg = opaque;
+ RISCVCPU *cpu = RISCV_CPU(obj);
+ bool generic_cpu = riscv_cpu_is_generic(obj);
+ bool prev_val, value;
+
+ if (!visit_type_bool(v, name, &value, errp)) {
+ return;
+ }
+
+ if (cpu_ext_is_deprecated(multi_ext_cfg->name)) {
+ g_autofree char *lower = cpu_ext_to_lower(multi_ext_cfg->name);
+
+ warn_report("CPU property '%s' is deprecated. Please use '%s' instead",
+ multi_ext_cfg->name, lower);
+ }
+
+ g_hash_table_insert(multi_ext_user_opts,
+ GUINT_TO_POINTER(multi_ext_cfg->offset),
+ (gpointer)value);
+
+ prev_val = isa_ext_is_enabled(cpu, multi_ext_cfg->offset);
+
+ if (value == prev_val) {
+ return;
+ }
+
+ if (value && !generic_cpu) {
+ g_autofree char *cpuname = riscv_cpu_get_name(cpu);
+ error_setg(errp, "'%s' CPU does not allow enabling extensions",
+ cpuname);
+ return;
+ }
+
+ isa_ext_update_enabled(cpu, multi_ext_cfg->offset, value);
+}
+
+static void cpu_get_multi_ext_cfg(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ const RISCVCPUMultiExtConfig *multi_ext_cfg = opaque;
+ bool value = isa_ext_is_enabled(RISCV_CPU(obj), multi_ext_cfg->offset);
+
+ visit_type_bool(v, name, &value, errp);
+}
+
+static void cpu_add_multi_ext_prop(Object *cpu_obj,
+ const RISCVCPUMultiExtConfig *multi_cfg)
+{
+ bool generic_cpu = riscv_cpu_is_generic(cpu_obj);
+ bool deprecated_ext = cpu_ext_is_deprecated(multi_cfg->name);
+
+ object_property_add(cpu_obj, multi_cfg->name, "bool",
+ cpu_get_multi_ext_cfg,
+ cpu_set_multi_ext_cfg,
+ NULL, (void *)multi_cfg);
+
+ if (!generic_cpu || deprecated_ext) {
+ return;
+ }
+
+ /*
+ * Set def val directly instead of using
+ * object_property_set_bool() to save the set()
+ * callback hash for user inputs.
+ */
+ isa_ext_update_enabled(RISCV_CPU(cpu_obj), multi_cfg->offset,
+ multi_cfg->enabled);
+}
+
+static void riscv_cpu_add_multiext_prop_array(Object *obj,
+ const RISCVCPUMultiExtConfig *array)
+{
+ const RISCVCPUMultiExtConfig *prop;
+
+ g_assert(array);
+
+ for (prop = array; prop && prop->name; prop++) {
+ cpu_add_multi_ext_prop(obj, prop);
+ }
+}
+
+/*
+ * Add CPU properties with user-facing flags.
+ *
+ * This will overwrite existing env->misa_ext values with the
+ * defaults set via riscv_cpu_add_misa_properties().
+ */
+static void riscv_cpu_add_user_properties(Object *obj)
+{
+#ifndef CONFIG_USER_ONLY
+ riscv_add_satp_mode_properties(obj);
+#endif
+
+ riscv_cpu_add_misa_properties(obj);
+
+ riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_extensions);
+ riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_vendor_exts);
+ riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_experimental_exts);
+
+ riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_deprecated_exts);
+
+ for (Property *prop = riscv_cpu_options; prop && prop->name; prop++) {
+ qdev_property_add_static(DEVICE(obj), prop);
+ }
+}
+
+/*
+ * The 'max' type CPU will have all possible ratified
+ * non-vendor extensions enabled.
+ */
+static void riscv_init_max_cpu_extensions(Object *obj)
+{
+ RISCVCPU *cpu = RISCV_CPU(obj);
+ CPURISCVState *env = &cpu->env;
+ const RISCVCPUMultiExtConfig *prop;
+
+ /* Enable RVG, RVJ and RVV that are disabled by default */
+ riscv_cpu_set_misa(env, env->misa_mxl, env->misa_ext | RVG | RVJ | RVV);
+
+ for (prop = riscv_cpu_extensions; prop && prop->name; prop++) {
+ isa_ext_update_enabled(cpu, prop->offset, true);
+ }
+
+ /* set vector version */
+ env->vext_ver = VEXT_VERSION_1_00_0;
+
+ /* Zfinx is not compatible with F. Disable it */
+ isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zfinx), false);
+ isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zdinx), false);
+ isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zhinx), false);
+ isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zhinxmin), false);
+
+ isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zce), false);
+ isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zcmp), false);
+ isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zcmt), false);
+
+ if (env->misa_mxl != MXL_RV32) {
+ isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zcf), false);
+ }
+}
+
+static bool riscv_cpu_has_max_extensions(Object *cpu_obj)
+{
+ return object_dynamic_cast(cpu_obj, TYPE_RISCV_CPU_MAX) != NULL;
+}
+
+static void tcg_cpu_instance_init(CPUState *cs)
+{
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ Object *obj = OBJECT(cpu);
+
+ multi_ext_user_opts = g_hash_table_new(NULL, g_direct_equal);
+ riscv_cpu_add_user_properties(obj);
+
+ if (riscv_cpu_has_max_extensions(obj)) {
+ riscv_init_max_cpu_extensions(obj);
+ }
+}
+
+static void tcg_cpu_init_ops(AccelCPUClass *accel_cpu, CPUClass *cc)
+{
+ /*
+ * All cpus use the same set of operations.
+ */
+ cc->tcg_ops = &riscv_tcg_ops;
+}
+
+static void tcg_cpu_class_init(CPUClass *cc)
+{
+ cc->init_accel_cpu = tcg_cpu_init_ops;
+}
+
+static void tcg_cpu_accel_class_init(ObjectClass *oc, void *data)
+{
+ AccelCPUClass *acc = ACCEL_CPU_CLASS(oc);
+
+ acc->cpu_class_init = tcg_cpu_class_init;
+ acc->cpu_instance_init = tcg_cpu_instance_init;
+ acc->cpu_target_realize = tcg_cpu_realize;
+}
+
+static const TypeInfo tcg_cpu_accel_type_info = {
+ .name = ACCEL_CPU_NAME("tcg"),
+
+ .parent = TYPE_ACCEL_CPU,
+ .class_init = tcg_cpu_accel_class_init,
+ .abstract = true,
+};
+
+static void tcg_cpu_accel_register_types(void)
+{
+ type_register_static(&tcg_cpu_accel_type_info);
+}
+type_init(tcg_cpu_accel_register_types);
diff --git a/target/riscv/tcg/tcg-cpu.h b/target/riscv/tcg/tcg-cpu.h
new file mode 100644
index 0000000..6301847
--- /dev/null
+++ b/target/riscv/tcg/tcg-cpu.h
@@ -0,0 +1,27 @@
+/*
+ * riscv TCG cpu class initialization
+ *
+ * Copyright (c) 2023 Ventana Micro Systems Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RISCV_TCG_CPU_H
+#define RISCV_TCG_CPU_H
+
+#include "cpu.h"
+
+void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp);
+
+#endif
diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c
index c9b39fb..c1c3a4d 100644
--- a/target/riscv/vector_helper.c
+++ b/target/riscv/vector_helper.c
@@ -3361,7 +3361,7 @@ static uint32_t fwmaccbf16(uint16_t a, uint16_t b, uint32_t d, float_status *s)
RVVCALL(OPFVV3, vfwmaccbf16_vv, WOP_UUU_H, H4, H2, H2, fwmaccbf16)
GEN_VEXT_VV_ENV(vfwmaccbf16_vv, 4)
-RVVCALL(OPFVF3, vfwmaccbf16_vf, WOP_UUU_H, H4, H2, fwmacc16)
+RVVCALL(OPFVF3, vfwmaccbf16_vf, WOP_UUU_H, H4, H2, fwmaccbf16)
GEN_VEXT_VF(vfwmaccbf16_vf, 4)
static uint32_t fwnmacc16(uint16_t a, uint16_t b, uint32_t d, float_status *s)
diff --git a/tests/avocado/tuxrun_baselines.py b/tests/avocado/tuxrun_baselines.py
index e12250e..c99bea6 100644
--- a/tests/avocado/tuxrun_baselines.py
+++ b/tests/avocado/tuxrun_baselines.py
@@ -501,6 +501,38 @@ class TuxRunBaselineTest(QemuSystemTest):
self.common_tuxrun(csums=sums)
+ def test_riscv32_maxcpu(self):
+ """
+ :avocado: tags=arch:riscv32
+ :avocado: tags=machine:virt
+ :avocado: tags=cpu:max
+ :avocado: tags=tuxboot:riscv32
+ """
+ sums = { "Image" :
+ "89599407d7334de629a40e7ad6503c73670359eb5f5ae9d686353a3d6deccbd5",
+ "fw_jump.elf" :
+ "f2ef28a0b77826f79d085d3e4aa686f1159b315eff9099a37046b18936676985",
+ "rootfs.ext4.zst" :
+ "7168d296d0283238ea73cd5a775b3dd608e55e04c7b92b76ecce31bb13108cba" }
+
+ self.common_tuxrun(csums=sums)
+
+ def test_riscv64_maxcpu(self):
+ """
+ :avocado: tags=arch:riscv64
+ :avocado: tags=machine:virt
+ :avocado: tags=cpu:max
+ :avocado: tags=tuxboot:riscv64
+ """
+ sums = { "Image" :
+ "cd634badc65e52fb63465ec99e309c0de0369f0841b7d9486f9729e119bac25e",
+ "fw_jump.elf" :
+ "6e3373abcab4305fe151b564a4c71110d833c21f2c0a1753b7935459e36aedcf",
+ "rootfs.ext4.zst" :
+ "b18e3a3bdf27be03da0b285e84cb71bf09eca071c3a087b42884b6982ed679eb" }
+
+ self.common_tuxrun(csums=sums)
+
def test_s390(self):
"""
:avocado: tags=arch:s390x