Merge tag 'trivial-branch-for-7.1-pull-request' of https://gitlab.com/laurent_vivier/qemu into staging
Pull request trivial branch 20220809
# -----BEGIN PGP SIGNATURE-----
#
# iQJGBAABCAAwFiEEzS913cjjpNwuT1Fz8ww4vT8vvjwFAmLykYgSHGxhdXJlbnRA
# dml2aWVyLmV1AAoJEPMMOL0/L74881MP/1UL3bfp1/tzxeN5wvT0mAdrKcj8obPE
# 5BB0QPD48NL8JqWq9HLLhX2vmAi2ibhPptpJkn+kXsqj8OW4Qp1JyWggZgD7O3mz
# S2OjVcpWrClhugoGhRUBm6G6kPsYjIBU0dLAINsyL6ETBZpIdkU/axLUJ6b2Wrm0
# eIQm6xNkGRhFAuya7MOX5ObzNylix2t1+QLxJMbA3XOCwl9PQRh7Wb/0oRhFMWZ9
# xCjraG9Jv1IxG28gGxMbUa2Ic7VTcVs+X+iu98AP7P3HBe/I31aXAmAf47MbMirk
# NJEYI9nDLZJyQmLgmBmyrQ+FIidDVhG7FURzIYeOoREv+xotQt6CTBNLKOcc4ccO
# YRy05JeUNeCJ015r8MZdiTwAwsbY0XPMijgqngqaWCw1BJO/luCdvUX6D4ehC5aZ
# zJPr7jJdqyhIZJjvPawoJSRsvWU495BCtyORtnU1T3uunEEu9vHLQKEy8gWtyitJ
# 7R6E/Mj4IzB9T0Kvz2acGBtxmtTshonjXaYgb11QRI5SWEUUhDNN64839opDPAW8
# PSmRZwfwkhIUlb9eRIEPMFScEEbQVgU+dAngSZva9un9ED2adPMreWHQrinMxQM4
# jtGAfG3RMQUh7b3VBlS87y0N+b2FSIrL9x0ujcozwuh0JMeiWxx+9AUq4dH8m2HU
# YZb3LXjm8i/P
# =o5gT
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 09 Aug 2022 09:55:36 AM PDT
# gpg: using RSA key CD2F75DDC8E3A4DC2E4F5173F30C38BD3F2FBE3C
# gpg: issuer "laurent@vivier.eu"
# gpg: Good signature from "Laurent Vivier <lvivier@redhat.com>" [undefined]
# gpg: aka "Laurent Vivier <laurent@vivier.eu>" [undefined]
# gpg: aka "Laurent Vivier (Red Hat) <lvivier@redhat.com>" [undefined]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg: There is no indication that the signature belongs to the owner.
# Primary key fingerprint: CD2F 75DD C8E3 A4DC 2E4F 5173 F30C 38BD 3F2F BE3C
* tag 'trivial-branch-for-7.1-pull-request' of https://gitlab.com/laurent_vivier/qemu:
xlnx_dp: drop unsupported AUXCommand in xlnx_dp_aux_set_command
contrib/vhost-user-blk: Clean up deallocation of VuVirtqElement
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
diff --git a/accel/tcg/tcg-accel-ops-icount.c b/accel/tcg/tcg-accel-ops-icount.c
index 8f1dda4..84cc742 100644
--- a/accel/tcg/tcg-accel-ops-icount.c
+++ b/accel/tcg/tcg-accel-ops-icount.c
@@ -109,7 +109,13 @@
replay_mutex_lock();
if (cpu->icount_budget == 0) {
+ /*
+ * We're called without the iothread lock, so must take it while
+ * we're calling timer handlers.
+ */
+ qemu_mutex_lock_iothread();
icount_notify_aio_contexts();
+ qemu_mutex_unlock_iothread();
}
}
diff --git a/hw/mips/malta.c b/hw/mips/malta.c
index 7a0ec51..0e93298 100644
--- a/hw/mips/malta.c
+++ b/hw/mips/malta.c
@@ -1442,6 +1442,14 @@
.instance_init = mips_malta_instance_init,
};
+GlobalProperty malta_compat[] = {
+ { "PIIX4_PM", "memory-hotplug-support", "off" },
+ { "PIIX4_PM", "acpi-pci-hotplug-with-bridge-support", "off" },
+ { "PIIX4_PM", "acpi-root-pci-hotplug", "off" },
+ { "PIIX4_PM", "x-not-migrate-acpi-index", "true" },
+};
+const size_t malta_compat_len = G_N_ELEMENTS(malta_compat);
+
static void mips_malta_machine_init(MachineClass *mc)
{
mc->desc = "MIPS Malta Core LV";
@@ -1455,6 +1463,7 @@
mc->default_cpu_type = MIPS_CPU_TYPE_NAME("24Kf");
#endif
mc->default_ram_id = "mips_malta.ram";
+ compat_props_add(mc->compat_props, malta_compat, malta_compat_len);
}
DEFINE_MACHINE("malta", mips_malta_machine_init)
diff --git a/hw/misc/grlib_ahb_apb_pnp.c b/hw/misc/grlib_ahb_apb_pnp.c
index 43e001c..5b05f15 100644
--- a/hw/misc/grlib_ahb_apb_pnp.c
+++ b/hw/misc/grlib_ahb_apb_pnp.c
@@ -136,7 +136,8 @@
uint32_t val;
val = ahb_pnp->regs[offset >> 2];
- trace_grlib_ahb_pnp_read(offset, val);
+ val = extract32(val, (4 - (offset & 3) - size) * 8, size * 8);
+ trace_grlib_ahb_pnp_read(offset, size, val);
return val;
}
@@ -152,7 +153,7 @@
.write = grlib_ahb_pnp_write,
.endianness = DEVICE_BIG_ENDIAN,
.impl = {
- .min_access_size = 4,
+ .min_access_size = 1,
.max_access_size = 4,
},
};
@@ -247,7 +248,8 @@
uint32_t val;
val = apb_pnp->regs[offset >> 2];
- trace_grlib_apb_pnp_read(offset, val);
+ val = extract32(val, (4 - (offset & 3) - size) * 8, size * 8);
+ trace_grlib_apb_pnp_read(offset, size, val);
return val;
}
@@ -263,7 +265,7 @@
.write = grlib_apb_pnp_write,
.endianness = DEVICE_BIG_ENDIAN,
.impl = {
- .min_access_size = 4,
+ .min_access_size = 1,
.max_access_size = 4,
},
};
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 4d51a80..c18bc06 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -247,8 +247,8 @@
via1_auxmode(int mode) "setting auxmode to %d"
# grlib_ahb_apb_pnp.c
-grlib_ahb_pnp_read(uint64_t addr, uint32_t value) "AHB PnP read addr:0x%03"PRIx64" data:0x%08x"
-grlib_apb_pnp_read(uint64_t addr, uint32_t value) "APB PnP read addr:0x%03"PRIx64" data:0x%08x"
+grlib_ahb_pnp_read(uint64_t addr, unsigned size, uint32_t value) "AHB PnP read addr:0x%03"PRIx64" size:%u data:0x%08x"
+grlib_apb_pnp_read(uint64_t addr, unsigned size, uint32_t value) "APB PnP read addr:0x%03"PRIx64" size:%u data:0x%08x"
# led.c
led_set_intensity(const char *color, const char *desc, uint8_t intensity_percent) "LED desc:'%s' color:%s intensity: %u%%"
diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c
index 31f2340..d8f3f16 100644
--- a/hw/net/rocker/rocker.c
+++ b/hw/net/rocker/rocker.c
@@ -1010,7 +1010,7 @@
FpPort *port = r->fp_port[i];
if (fp_port_get_link_up(port)) {
- status |= 1 << (i + 1);
+ status |= 1ULL << (i + 1);
}
}
return status;
@@ -1025,7 +1025,7 @@
FpPort *port = r->fp_port[i];
if (fp_port_enabled(port)) {
- ret |= 1 << (i + 1);
+ ret |= 1ULL << (i + 1);
}
}
return ret;
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index f5cdb9a..efee673 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -1591,7 +1591,7 @@
int cmd = r->req.cmd.buf[0];
int len = r->req.cmd.xfer;
int hdr_len = (cmd == MODE_SELECT ? 4 : 8);
- int bd_len;
+ int bd_len, bs;
int pass;
if ((r->req.cmd.buf[1] & 0x11) != 0x10) {
@@ -1617,9 +1617,19 @@
}
/* Allow changing the block size */
- if (bd_len && p[6] != (s->qdev.blocksize >> 8)) {
- s->qdev.blocksize = p[6] << 8;
- trace_scsi_disk_mode_select_set_blocksize(s->qdev.blocksize);
+ if (bd_len) {
+ bs = p[5] << 16 | p[6] << 8 | p[7];
+
+ /*
+ * Since the existing code only checks/updates bits 8-15 of the block
+ * size, restrict ourselves to the same requirement for now to ensure
+ * that a block size set by a block descriptor and then read back by
+ * a subsequent SCSI command will be the same
+ */
+ if (bs && !(bs & ~0xff00) && bs != s->qdev.blocksize) {
+ s->qdev.blocksize = bs;
+ trace_scsi_disk_mode_select_set_blocksize(s->qdev.blocksize);
+ }
}
len -= bd_len;
@@ -1849,7 +1859,7 @@
uint32_t nb_sectors = scsi_data_cdb_xfer(r->req.cmd.buf);
WriteSameCBData *data;
uint8_t *buf;
- int i;
+ int i, l;
/* Fail if PBDATA=1 or LBDATA=1 or ANCHOR=1. */
if (nb_sectors == 0 || (req->cmd.buf[1] & 0x16)) {
@@ -1891,8 +1901,9 @@
data->iov.iov_len);
qemu_iovec_init_external(&data->qiov, &data->iov, 1);
- for (i = 0; i < data->iov.iov_len; i += s->qdev.blocksize) {
- memcpy(&buf[i], inbuf, s->qdev.blocksize);
+ for (i = 0; i < data->iov.iov_len; i += l) {
+ l = MIN(s->qdev.blocksize, data->iov.iov_len - i);
+ memcpy(&buf[i], inbuf, l);
}
scsi_req_ref(&r->req);
diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img
index 39f9680..554fcbd 100644
--- a/pc-bios/s390-ccw.img
+++ b/pc-bios/s390-ccw.img
Binary files differ
diff --git a/pc-bios/s390-ccw/virtio-blkdev.c b/pc-bios/s390-ccw/virtio-blkdev.c
index 8271c47..794f99b 100644
--- a/pc-bios/s390-ccw/virtio-blkdev.c
+++ b/pc-bios/s390-ccw/virtio-blkdev.c
@@ -173,7 +173,7 @@
switch (vdev->senseid.cu_model) {
case VIRTIO_ID_BLOCK:
- return vdev->config.blk.blk_size << vdev->config.blk.physical_block_exp;
+ return vdev->config.blk.blk_size;
case VIRTIO_ID_SCSI:
return vdev->scsi_block_size;
}
diff --git a/softmmu/vl.c b/softmmu/vl.c
index aabd82e..706bd7c 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -1947,27 +1947,21 @@
}
}
-static void parse_memory_options(const char *arg)
+static void parse_memory_options(void)
{
- QemuOpts *opts;
+ QemuOpts *opts = qemu_find_opts_singleton("memory");
QDict *dict, *prop;
const char *mem_str;
+ Location loc;
- opts = qemu_opts_parse_noisily(qemu_find_opts("memory"), arg, true);
- if (!opts) {
- exit(EXIT_FAILURE);
- }
+ loc_push_none(&loc);
+ qemu_opts_loc_restore(opts);
prop = qdict_new();
if (qemu_opt_get_size(opts, "size", 0) != 0) {
- mem_str = qemu_opt_get(opts, "size");
- if (!*mem_str) {
- error_report("missing 'size' option value");
- exit(EXIT_FAILURE);
- }
-
/* Fix up legacy suffix-less format */
+ mem_str = qemu_opt_get(opts, "size");
if (g_ascii_isdigit(mem_str[strlen(mem_str) - 1])) {
g_autofree char *mib_str = g_strdup_printf("%sM", mem_str);
qdict_put_str(prop, "size", mib_str);
@@ -1987,6 +1981,7 @@
qdict_put(dict, "memory", prop);
keyval_merge(machine_opts_dict, dict, &error_fatal);
qobject_unref(dict);
+ loc_pop(&loc);
}
static void qemu_create_machine(QDict *qdict)
@@ -2053,8 +2048,7 @@
if (g_str_equal(group, "object") ||
g_str_equal(group, "machine") ||
g_str_equal(group, "smp-opts") ||
- g_str_equal(group, "boot-opts") ||
- g_str_equal(group, "memory")) {
+ g_str_equal(group, "boot-opts")) {
return false;
}
return true;
@@ -2078,8 +2072,6 @@
machine_merge_property("smp", dict, &error_fatal);
} else if (g_str_equal(group, "boot-opts")) {
machine_merge_property("boot", dict, &error_fatal);
- } else if (g_str_equal(group, "memory")) {
- machine_merge_property("memory", dict, &error_fatal);
} else {
abort();
}
@@ -2882,7 +2874,10 @@
exit(0);
break;
case QEMU_OPTION_m:
- parse_memory_options(optarg);
+ opts = qemu_opts_parse_noisily(qemu_find_opts("memory"), optarg, true);
+ if (opts == NULL) {
+ exit(1);
+ }
break;
#ifdef CONFIG_TPM
case QEMU_OPTION_tpmdev:
@@ -3515,6 +3510,9 @@
configure_rtc(qemu_find_opts_singleton("rtc"));
+ /* Transfer QemuOpts options into machine options */
+ parse_memory_options();
+
qemu_create_machine(machine_opts_dict);
suspend_mux_open();
diff --git a/target/loongarch/fpu_helper.c b/target/loongarch/fpu_helper.c
index bd76529..4b96372 100644
--- a/target/loongarch/fpu_helper.c
+++ b/target/loongarch/fpu_helper.c
@@ -872,8 +872,8 @@
return fd;
}
-void helper_set_rounding_mode(CPULoongArchState *env, uint32_t fcsr0)
+void helper_set_rounding_mode(CPULoongArchState *env)
{
- set_float_rounding_mode(ieee_rm[(fcsr0 >> FCSR0_RM) & 0x3],
+ set_float_rounding_mode(ieee_rm[(env->fcsr0 >> FCSR0_RM) & 0x3],
&env->fp_status);
}
diff --git a/target/loongarch/helper.h b/target/loongarch/helper.h
index cbbe008..9c01823 100644
--- a/target/loongarch/helper.h
+++ b/target/loongarch/helper.h
@@ -91,7 +91,7 @@
DEF_HELPER_2(frint_s, i64, env, i64)
DEF_HELPER_2(frint_d, i64, env, i64)
-DEF_HELPER_FLAGS_2(set_rounding_mode, TCG_CALL_NO_RWG, void, env, i32)
+DEF_HELPER_FLAGS_1(set_rounding_mode, TCG_CALL_NO_RWG, void, env)
DEF_HELPER_1(rdtime_d, i64, env)
diff --git a/target/loongarch/insn_trans/trans_fmov.c.inc b/target/loongarch/insn_trans/trans_fmov.c.inc
index 24753d4..5537e3d 100644
--- a/target/loongarch/insn_trans/trans_fmov.c.inc
+++ b/target/loongarch/insn_trans/trans_fmov.c.inc
@@ -60,38 +60,39 @@
TCGv Rj = gpr_src(ctx, a->rj, EXT_NONE);
if (mask == UINT32_MAX) {
- tcg_gen_extrl_i64_i32(cpu_fcsr0, Rj);
+ tcg_gen_st32_i64(Rj, cpu_env, offsetof(CPULoongArchState, fcsr0));
} else {
+ TCGv_i32 fcsr0 = tcg_temp_new_i32();
TCGv_i32 temp = tcg_temp_new_i32();
+ tcg_gen_ld_i32(fcsr0, cpu_env, offsetof(CPULoongArchState, fcsr0));
tcg_gen_extrl_i64_i32(temp, Rj);
tcg_gen_andi_i32(temp, temp, mask);
- tcg_gen_andi_i32(cpu_fcsr0, cpu_fcsr0, ~mask);
- tcg_gen_or_i32(cpu_fcsr0, cpu_fcsr0, temp);
+ tcg_gen_andi_i32(fcsr0, fcsr0, ~mask);
+ tcg_gen_or_i32(fcsr0, fcsr0, temp);
+ tcg_gen_st_i32(fcsr0, cpu_env, offsetof(CPULoongArchState, fcsr0));
+
tcg_temp_free_i32(temp);
-
- /*
- * Install the new rounding mode to fpu_status, if changed.
- * Note that FCSR3 is exactly the rounding mode field.
- */
- if (mask != FCSR0_M3) {
- return true;
- }
+ tcg_temp_free_i32(fcsr0);
}
- gen_helper_set_rounding_mode(cpu_env, cpu_fcsr0);
+ /*
+ * Install the new rounding mode to fpu_status, if changed.
+ * Note that FCSR3 is exactly the rounding mode field.
+ */
+ if (mask & FCSR0_M3) {
+ gen_helper_set_rounding_mode(cpu_env);
+ }
return true;
}
static bool trans_movfcsr2gr(DisasContext *ctx, arg_movfcsr2gr *a)
{
- TCGv_i32 temp = tcg_temp_new_i32();
TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE);
- tcg_gen_andi_i32(temp, cpu_fcsr0, fcsr_mask[a->fcsrs]);
- tcg_gen_ext_i32_i64(dest, temp);
+ tcg_gen_ld32u_i64(dest, cpu_env, offsetof(CPULoongArchState, fcsr0));
+ tcg_gen_andi_i64(dest, dest, fcsr_mask[a->fcsrs]);
gen_set_gpr(a->rd, dest, EXT_NONE);
- tcg_temp_free_i32(temp);
return true;
}
diff --git a/target/loongarch/translate.c b/target/loongarch/translate.c
index c9afd11..51ba291 100644
--- a/target/loongarch/translate.c
+++ b/target/loongarch/translate.c
@@ -22,7 +22,6 @@
/* Global register indices */
TCGv cpu_gpr[32], cpu_pc;
static TCGv cpu_lladdr, cpu_llval;
-TCGv_i32 cpu_fcsr0;
TCGv_i64 cpu_fpr[32];
#include "exec/gen-icount.h"
@@ -266,8 +265,6 @@
}
cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPULoongArchState, pc), "pc");
- cpu_fcsr0 = tcg_global_mem_new_i32(cpu_env,
- offsetof(CPULoongArchState, fcsr0), "fcsr0");
cpu_lladdr = tcg_global_mem_new(cpu_env,
offsetof(CPULoongArchState, lladdr), "lladdr");
cpu_llval = tcg_global_mem_new(cpu_env,
diff --git a/target/mips/tcg/sysemu/mips-semi.c b/target/mips/tcg/sysemu/mips-semi.c
index 5fb1ad9..85f0567 100644
--- a/target/mips/tcg/sysemu/mips-semi.c
+++ b/target/mips/tcg/sysemu/mips-semi.c
@@ -321,6 +321,9 @@
if (use_gdb_syscalls()) {
addr = gpr[29] - str->len;
p = lock_user(VERIFY_WRITE, addr, str->len, 0);
+ if (!p) {
+ report_fault(env);
+ }
memcpy(p, str->str, str->len);
unlock_user(p, addr, str->len);
semihost_sys_write(cs, uhi_cb, 2, addr, str->len);
diff --git a/tests/avocado/replay_linux.py b/tests/avocado/replay_linux.py
index 40e4f69..e1f9981 100644
--- a/tests/avocado/replay_linux.py
+++ b/tests/avocado/replay_linux.py
@@ -189,3 +189,4 @@ def test_virt_gicv3(self):
self.run_rr(shift=3,
args=(*self.get_common_args(),
+ "-machine", "virt,gic-version=3"))
diff --git a/tests/qemu-iotests/264 b/tests/qemu-iotests/264
index bc431d1..289381e 100755
--- a/tests/qemu-iotests/264
+++ b/tests/qemu-iotests/264
@@ -101,7 +101,7 @@
start_t = time.time()
self.vm.event_wait('BLOCK_JOB_CANCELLED')
delta_t = time.time() - start_t
- self.assertTrue(delta_t < 2.0)
+ self.assertTrue(delta_t < 5.0)
def test_mirror_cancel(self):
# Mirror speed limit doesn't work well enough, it seems that mirror
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 3a47401..be4b30d 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -26,6 +26,7 @@
'qom-test',
'test-hmp',
'qos-test',
+ 'readconfig-test',
]
if config_host.has_key('CONFIG_MODULES')
qtests_generic += [ 'modules-test' ]
diff --git a/tests/qtest/readconfig-test.c b/tests/qtest/readconfig-test.c
new file mode 100644
index 0000000..2e604d7
--- /dev/null
+++ b/tests/qtest/readconfig-test.c
@@ -0,0 +1,195 @@
+/*
+ * Validate -readconfig
+ *
+ * Copyright (c) 2022 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qapi/error.h"
+#include "qapi/qapi-visit-machine.h"
+#include "qapi/qapi-visit-qom.h"
+#include "qapi/qapi-visit-ui.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qlist.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qapi/qmp/qstring.h"
+#include "qemu/units.h"
+
+static QTestState *qtest_init_with_config(const char *cfgdata)
+{
+ GError *error = NULL;
+ g_autofree char *args = NULL;
+ int cfgfd = -1;
+ g_autofree char *cfgpath = NULL;
+ QTestState *qts;
+ ssize_t ret;
+
+ cfgfd = g_file_open_tmp("readconfig-test-XXXXXX", &cfgpath, &error);
+ g_assert_no_error(error);
+ g_assert_cmpint(cfgfd, >=, 0);
+
+ ret = qemu_write_full(cfgfd, cfgdata, strlen(cfgdata));
+ if (ret < 0) {
+ unlink(cfgpath);
+ }
+ g_assert_cmpint(ret, ==, strlen(cfgdata));
+
+ close(cfgfd);
+
+ args = g_strdup_printf("-nodefaults -machine none -readconfig %s", cfgpath);
+
+ qts = qtest_init(args);
+
+ unlink(cfgpath);
+
+ return qts;
+}
+
+static void test_x86_memdev_resp(QObject *res)
+{
+ Visitor *v;
+ g_autoptr(MemdevList) memdevs = NULL;
+ Memdev *memdev;
+
+ g_assert(res);
+ v = qobject_input_visitor_new(res);
+ visit_type_MemdevList(v, NULL, &memdevs, &error_abort);
+
+ g_assert(memdevs);
+ g_assert(memdevs->value);
+ g_assert(!memdevs->next);
+
+ memdev = memdevs->value;
+ g_assert_cmpstr(memdev->id, ==, "ram");
+ g_assert_cmpint(memdev->size, ==, 200 * MiB);
+
+ visit_free(v);
+}
+
+static void test_x86_memdev(void)
+{
+ QDict *resp;
+ QTestState *qts;
+ const char *cfgdata =
+ "[memory]\n"
+ "size = \"200\"";
+
+ qts = qtest_init_with_config(cfgdata);
+ /* Test valid command */
+ resp = qtest_qmp(qts, "{ 'execute': 'query-memdev' }");
+ test_x86_memdev_resp(qdict_get(resp, "return"));
+ qobject_unref(resp);
+
+ qtest_quit(qts);
+}
+
+
+#ifdef CONFIG_SPICE
+static void test_spice_resp(QObject *res)
+{
+ Visitor *v;
+ g_autoptr(SpiceInfo) spice = NULL;
+
+ g_assert(res);
+ v = qobject_input_visitor_new(res);
+ visit_type_SpiceInfo(v, "spcie", &spice, &error_abort);
+
+ g_assert(spice);
+ g_assert(spice->enabled);
+
+ visit_free(v);
+}
+
+static void test_spice(void)
+{
+ QDict *resp;
+ QTestState *qts;
+ const char *cfgdata =
+ "[spice]\n"
+ "disable-ticketing = \"on\"\n"
+ "unix = \"on\"\n";
+
+ qts = qtest_init_with_config(cfgdata);
+ /* Test valid command */
+ resp = qtest_qmp(qts, "{ 'execute': 'query-spice' }");
+ test_spice_resp(qdict_get(resp, "return"));
+ qobject_unref(resp);
+
+ qtest_quit(qts);
+}
+#endif
+
+static void test_object_rng_resp(QObject *res)
+{
+ Visitor *v;
+ g_autoptr(ObjectPropertyInfoList) objs = NULL;
+ ObjectPropertyInfoList *tmp;
+ ObjectPropertyInfo *obj;
+ bool seen_rng = false;
+
+ g_assert(res);
+ v = qobject_input_visitor_new(res);
+ visit_type_ObjectPropertyInfoList(v, NULL, &objs, &error_abort);
+
+ g_assert(objs);
+ tmp = objs;
+ while (tmp) {
+ g_assert(tmp->value);
+
+ obj = tmp->value;
+ if (g_str_equal(obj->name, "rng0") &&
+ g_str_equal(obj->type, "child<rng-builtin>")) {
+ seen_rng = true;
+ }
+
+ tmp = tmp->next;
+ }
+
+ g_assert(seen_rng);
+
+ visit_free(v);
+}
+
+static void test_object_rng(void)
+{
+ QDict *resp;
+ QTestState *qts;
+ const char *cfgdata =
+ "[object]\n"
+ "qom-type = \"rng-builtin\"\n"
+ "id = \"rng0\"\n";
+
+ qts = qtest_init_with_config(cfgdata);
+ /* Test valid command */
+ resp = qtest_qmp(qts,
+ "{ 'execute': 'qom-list',"
+ " 'arguments': {'path': '/objects' }}");
+ test_object_rng_resp(qdict_get(resp, "return"));
+ qobject_unref(resp);
+
+ qtest_quit(qts);
+}
+
+int main(int argc, char *argv[])
+{
+ const char *arch;
+ g_test_init(&argc, &argv, NULL);
+
+ arch = qtest_get_arch();
+
+ if (g_str_equal(arch, "i386") ||
+ g_str_equal(arch, "x86_64")) {
+ qtest_add_func("readconfig/x86/memdev", test_x86_memdev);
+ }
+#ifdef CONFIG_SPICE
+ qtest_add_func("readconfig/spice", test_spice);
+#endif
+
+ qtest_add_func("readconfig/object-rng", test_object_rng);
+
+ return g_test_run();
+}
diff --git a/tests/tcg/loongarch64/Makefile.target b/tests/tcg/loongarch64/Makefile.target
index 0115de7..00030a10 100644
--- a/tests/tcg/loongarch64/Makefile.target
+++ b/tests/tcg/loongarch64/Makefile.target
@@ -15,5 +15,6 @@
LOONGARCH64_TESTS += test_fclass
LOONGARCH64_TESTS += test_fpcom
LOONGARCH64_TESTS += test_pcadd
+LOONGARCH64_TESTS += test_fcsr
TESTS += $(LOONGARCH64_TESTS)
diff --git a/tests/tcg/loongarch64/test_fcsr.c b/tests/tcg/loongarch64/test_fcsr.c
new file mode 100644
index 0000000..ad3609e
--- /dev/null
+++ b/tests/tcg/loongarch64/test_fcsr.c
@@ -0,0 +1,15 @@
+#include <assert.h>
+
+int main()
+{
+ unsigned fcsr;
+
+ asm("movgr2fcsr $r0,$r0\n\t"
+ "movgr2fr.d $f0,$r0\n\t"
+ "fdiv.d $f0,$f0,$f0\n\t"
+ "movfcsr2gr %0,$r0"
+ : "=r"(fcsr) : : "f0");
+
+ assert(fcsr & (16 << 16)); /* Invalid */
+ return 0;
+}