mirror of https://github.com/xemu-project/xemu.git
* qtest documentation improvements (Eduardo, myself)
* libqtest event buffering (Maxim) * use RCU for list of children of a bus (Maxim) * move more files to softmmu/ (myself) * meson.build cleanups, qemu-storage-daemon fix (Philippe) -----BEGIN PGP SIGNATURE----- iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAl+EfGcUHHBib256aW5p QHJlZGhhdC5jb20ACgkQv/vSX3jHroNbRwf/aOYHLDFtZJdalb8/g9X+n0jK/Ew9 XuSy7vC5xGYcVP+haPfHQD75iiTTm5hEw/ooffAByHSqEFBmPJm5Uogx6eHbXjzm rZrsU3MdwfTz/T+XPtp/g3H9mAZ0tQboU5fR7euO6LVGOS+886LDJyIyZpPr6pwH XhyzqTcpZhV+aphfCXGRRUlUJuYKaYxXz60VLVVS2k7RnQJvnZP5cpIxD+PnaN6L PyA+aL/J4LkUm/nX9NkinJPxmxXBnhk3C+XKPJ1gV54XEC6AiUkoCxeCeBxc/zcU AKAIAgMc8D/oMwyRYV+FKDuhRIDGJ+D2h4VOITnyunur7phfQLb5cUM+hA== =Pvrq -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/bonzini-gitlab/tags/for-upstream' into staging * qtest documentation improvements (Eduardo, myself) * libqtest event buffering (Maxim) * use RCU for list of children of a bus (Maxim) * move more files to softmmu/ (myself) * meson.build cleanups, qemu-storage-daemon fix (Philippe) # gpg: Signature made Mon 12 Oct 2020 16:55:19 BST # gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83 # gpg: issuer "pbonzini@redhat.com" # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full] # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [full] # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * remotes/bonzini-gitlab/tags/for-upstream: (38 commits) meson: identify more sections of meson.build scsi/scsi_bus: fix races in REPORT LUNS virtio-scsi: use scsi_device_get scsi/scsi_bus: Add scsi_device_get scsi/scsi-bus: scsi_device_find: don't return unrealized devices device-core: use atomic_set on .realized property scsi: switch to bus->check_address device-core: use RCU for list of children of a bus device_core: use drain_call_rcu in in qmp_device_add scsi/scsi_bus: switch search direction in scsi_device_find qdev: add "check if address free" callback for buses qemu-iotests, qtest: rewrite test 067 as a qtest qtest: check that drives are really appearing and disappearing qtest: switch users back to qtest_qmp_receive device-plug-test: use qtest_qmp to send the device_del command qtest: remove qtest_qmp_receive_success qtest: Reintroduce qtest_qmp_receive with QMP event buffering qtest: rename qtest_qmp_receive to qtest_qmp_receive_dict meson.build: Re-enable KVM support for MIPS build-sys: fix git version from -version ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
724c1c8bb3
|
@ -232,7 +232,7 @@ build-tcg-disabled:
|
||||||
- ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048
|
- ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048
|
||||||
052 063 077 086 101 104 106 113 148 150 151 152 157 159 160 163
|
052 063 077 086 101 104 106 113 148 150 151 152 157 159 160 163
|
||||||
170 171 183 184 192 194 197 208 215 221 222 226 227 236 253 277
|
170 171 183 184 192 194 197 208 215 221 222 226 227 236 253 277
|
||||||
- ./check -qcow2 028 051 056 057 058 065 067 068 082 085 091 095 096 102 122
|
- ./check -qcow2 028 051 056 057 058 065 068 082 085 091 095 096 102 122
|
||||||
124 132 139 142 144 145 151 152 155 157 165 194 196 197 200 202
|
124 132 139 142 144 145 151 152 155 157 165 194 196 197 200 202
|
||||||
208 209 215 216 218 222 227 234 246 247 248 250 254 255 257 258
|
208 209 215 216 218 222 227 234 246 247 248 250 254 255 257 258
|
||||||
260 261 262 263 264 270 272 273 277 279
|
260 261 262 263 264 270 272 273 277 279
|
||||||
|
|
16
MAINTAINERS
16
MAINTAINERS
|
@ -117,7 +117,6 @@ R: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: softmmu/cpus.c
|
F: softmmu/cpus.c
|
||||||
F: cpus-common.c
|
F: cpus-common.c
|
||||||
F: exec.c
|
|
||||||
F: accel/tcg/
|
F: accel/tcg/
|
||||||
F: accel/stubs/tcg-stub.c
|
F: accel/stubs/tcg-stub.c
|
||||||
F: scripts/decodetree.py
|
F: scripts/decodetree.py
|
||||||
|
@ -1525,6 +1524,7 @@ Machine core
|
||||||
M: Eduardo Habkost <ehabkost@redhat.com>
|
M: Eduardo Habkost <ehabkost@redhat.com>
|
||||||
M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
|
M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
|
||||||
S: Supported
|
S: Supported
|
||||||
|
F: cpu.c
|
||||||
F: hw/core/cpu.c
|
F: hw/core/cpu.c
|
||||||
F: hw/core/machine-qmp-cmds.c
|
F: hw/core/machine-qmp-cmds.c
|
||||||
F: hw/core/machine.c
|
F: hw/core/machine.c
|
||||||
|
@ -2055,7 +2055,7 @@ R: Laszlo Ersek <lersek@redhat.com>
|
||||||
R: Gerd Hoffmann <kraxel@redhat.com>
|
R: Gerd Hoffmann <kraxel@redhat.com>
|
||||||
S: Supported
|
S: Supported
|
||||||
F: docs/specs/fw_cfg.txt
|
F: docs/specs/fw_cfg.txt
|
||||||
F: hw/nvram/fw_cfg.c
|
F: hw/nvram/fw_cfg*.c
|
||||||
F: stubs/fw_cfg.c
|
F: stubs/fw_cfg.c
|
||||||
F: include/hw/nvram/fw_cfg.h
|
F: include/hw/nvram/fw_cfg.h
|
||||||
F: include/standard-headers/linux/qemu_fw_cfg.h
|
F: include/standard-headers/linux/qemu_fw_cfg.h
|
||||||
|
@ -2235,7 +2235,7 @@ Device Tree
|
||||||
M: Alistair Francis <alistair.francis@wdc.com>
|
M: Alistair Francis <alistair.francis@wdc.com>
|
||||||
R: David Gibson <david@gibson.dropbear.id.au>
|
R: David Gibson <david@gibson.dropbear.id.au>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: device_tree.c
|
F: softmmu/device_tree.c
|
||||||
F: include/sysemu/device_tree.h
|
F: include/sysemu/device_tree.h
|
||||||
|
|
||||||
Dump
|
Dump
|
||||||
|
@ -2281,10 +2281,11 @@ F: include/exec/memop.h
|
||||||
F: include/exec/memory.h
|
F: include/exec/memory.h
|
||||||
F: include/exec/ram_addr.h
|
F: include/exec/ram_addr.h
|
||||||
F: include/exec/ramblock.h
|
F: include/exec/ramblock.h
|
||||||
|
F: softmmu/dma-helpers.c
|
||||||
F: softmmu/ioport.c
|
F: softmmu/ioport.c
|
||||||
F: softmmu/memory.c
|
F: softmmu/memory.c
|
||||||
|
F: softmmu/physmem.c
|
||||||
F: include/exec/memory-internal.h
|
F: include/exec/memory-internal.h
|
||||||
F: exec.c
|
|
||||||
F: scripts/coccinelle/memory-region-housekeeping.cocci
|
F: scripts/coccinelle/memory-region-housekeeping.cocci
|
||||||
|
|
||||||
SPICE
|
SPICE
|
||||||
|
@ -2461,7 +2462,8 @@ F: include/monitor/qdev.h
|
||||||
F: include/qom/
|
F: include/qom/
|
||||||
F: qapi/qom.json
|
F: qapi/qom.json
|
||||||
F: qapi/qdev.json
|
F: qapi/qdev.json
|
||||||
F: qdev-monitor.c
|
F: scripts/coccinelle/qom-parent-type.cocci
|
||||||
|
F: softmmu/qdev-monitor.c
|
||||||
F: qom/
|
F: qom/
|
||||||
F: tests/check-qom-interface.c
|
F: tests/check-qom-interface.c
|
||||||
F: tests/check-qom-proplist.c
|
F: tests/check-qom-proplist.c
|
||||||
|
@ -2591,7 +2593,7 @@ F: docs/interop/dbus-vmstate.rst
|
||||||
Seccomp
|
Seccomp
|
||||||
M: Eduardo Otubo <otubo@redhat.com>
|
M: Eduardo Otubo <otubo@redhat.com>
|
||||||
S: Supported
|
S: Supported
|
||||||
F: qemu-seccomp.c
|
F: softmmu/qemu-seccomp.c
|
||||||
F: include/sysemu/seccomp.h
|
F: include/sysemu/seccomp.h
|
||||||
|
|
||||||
Cryptography
|
Cryptography
|
||||||
|
@ -2957,7 +2959,7 @@ T: git https://github.com/stefanha/qemu.git block
|
||||||
Bootdevice
|
Bootdevice
|
||||||
M: Gonglei <arei.gonglei@huawei.com>
|
M: Gonglei <arei.gonglei@huawei.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: bootdevice.c
|
F: softmmu/bootdevice.c
|
||||||
|
|
||||||
Quorum
|
Quorum
|
||||||
M: Alberto Garcia <berto@igalia.com>
|
M: Alberto Garcia <berto@igalia.com>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
authz_ss = ss.source_set()
|
|
||||||
authz_ss.add(genh)
|
authz_ss.add(genh)
|
||||||
authz_ss.add(files(
|
authz_ss.add(files(
|
||||||
'base.c',
|
'base.c',
|
||||||
|
@ -8,12 +7,3 @@ authz_ss.add(files(
|
||||||
))
|
))
|
||||||
|
|
||||||
authz_ss.add(when: ['CONFIG_AUTH_PAM', pam], if_true: files('pamacct.c'))
|
authz_ss.add(when: ['CONFIG_AUTH_PAM', pam], if_true: files('pamacct.c'))
|
||||||
|
|
||||||
authz_ss = authz_ss.apply(config_host, strict: false)
|
|
||||||
libauthz = static_library('authz', authz_ss.sources() + genh,
|
|
||||||
dependencies: [authz_ss.dependencies()],
|
|
||||||
name_suffix: 'fa',
|
|
||||||
build_by_default: false)
|
|
||||||
|
|
||||||
authz = declare_dependency(link_whole: libauthz,
|
|
||||||
dependencies: qom)
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
chardev_ss = ss.source_set()
|
|
||||||
chardev_ss.add(files(
|
chardev_ss.add(files(
|
||||||
'char-fe.c',
|
'char-fe.c',
|
||||||
'char-file.c',
|
'char-file.c',
|
||||||
|
@ -25,11 +24,6 @@ chardev_ss.add(when: 'CONFIG_WIN32', if_true: files(
|
||||||
))
|
))
|
||||||
|
|
||||||
chardev_ss = chardev_ss.apply(config_host, strict: false)
|
chardev_ss = chardev_ss.apply(config_host, strict: false)
|
||||||
libchardev = static_library('chardev', chardev_ss.sources() + genh,
|
|
||||||
name_suffix: 'fa',
|
|
||||||
build_by_default: false)
|
|
||||||
|
|
||||||
chardev = declare_dependency(link_whole: libchardev)
|
|
||||||
|
|
||||||
softmmu_ss.add(files('chardev-sysemu.c', 'msmouse.c', 'wctablet.c', 'testdev.c'))
|
softmmu_ss.add(files('chardev-sysemu.c', 'msmouse.c', 'wctablet.c', 'testdev.c'))
|
||||||
softmmu_ss.add(when: ['CONFIG_SPICE', spice], if_true: files('spice.c'))
|
softmmu_ss.add(when: ['CONFIG_SPICE', spice], if_true: files('spice.c'))
|
||||||
|
|
|
@ -0,0 +1,452 @@
|
||||||
|
/*
|
||||||
|
* Target-specific parts of the CPU object
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003 Fabrice Bellard
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu-common.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
|
||||||
|
#include "exec/target_page.h"
|
||||||
|
#include "hw/qdev-core.h"
|
||||||
|
#include "hw/qdev-properties.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "migration/vmstate.h"
|
||||||
|
#ifdef CONFIG_USER_ONLY
|
||||||
|
#include "qemu.h"
|
||||||
|
#else
|
||||||
|
#include "exec/address-spaces.h"
|
||||||
|
#endif
|
||||||
|
#include "sysemu/tcg.h"
|
||||||
|
#include "sysemu/kvm.h"
|
||||||
|
#include "sysemu/replay.h"
|
||||||
|
#include "translate-all.h"
|
||||||
|
#include "exec/log.h"
|
||||||
|
|
||||||
|
uintptr_t qemu_host_page_size;
|
||||||
|
intptr_t qemu_host_page_mask;
|
||||||
|
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
static int cpu_common_post_load(void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
CPUState *cpu = opaque;
|
||||||
|
|
||||||
|
/* 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the
|
||||||
|
version_id is increased. */
|
||||||
|
cpu->interrupt_request &= ~0x01;
|
||||||
|
tlb_flush(cpu);
|
||||||
|
|
||||||
|
/* loadvm has just updated the content of RAM, bypassing the
|
||||||
|
* usual mechanisms that ensure we flush TBs for writes to
|
||||||
|
* memory we've translated code from. So we must flush all TBs,
|
||||||
|
* which will now be stale.
|
||||||
|
*/
|
||||||
|
tb_flush(cpu);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cpu_common_pre_load(void *opaque)
|
||||||
|
{
|
||||||
|
CPUState *cpu = opaque;
|
||||||
|
|
||||||
|
cpu->exception_index = -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool cpu_common_exception_index_needed(void *opaque)
|
||||||
|
{
|
||||||
|
CPUState *cpu = opaque;
|
||||||
|
|
||||||
|
return tcg_enabled() && cpu->exception_index != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_cpu_common_exception_index = {
|
||||||
|
.name = "cpu_common/exception_index",
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.needed = cpu_common_exception_index_needed,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_INT32(exception_index, CPUState),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool cpu_common_crash_occurred_needed(void *opaque)
|
||||||
|
{
|
||||||
|
CPUState *cpu = opaque;
|
||||||
|
|
||||||
|
return cpu->crash_occurred;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_cpu_common_crash_occurred = {
|
||||||
|
.name = "cpu_common/crash_occurred",
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.needed = cpu_common_crash_occurred_needed,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_BOOL(crash_occurred, CPUState),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const VMStateDescription vmstate_cpu_common = {
|
||||||
|
.name = "cpu_common",
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.pre_load = cpu_common_pre_load,
|
||||||
|
.post_load = cpu_common_post_load,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32(halted, CPUState),
|
||||||
|
VMSTATE_UINT32(interrupt_request, CPUState),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
},
|
||||||
|
.subsections = (const VMStateDescription*[]) {
|
||||||
|
&vmstate_cpu_common_exception_index,
|
||||||
|
&vmstate_cpu_common_crash_occurred,
|
||||||
|
NULL
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void cpu_exec_unrealizefn(CPUState *cpu)
|
||||||
|
{
|
||||||
|
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||||
|
|
||||||
|
tlb_destroy(cpu);
|
||||||
|
cpu_list_remove(cpu);
|
||||||
|
|
||||||
|
#ifdef CONFIG_USER_ONLY
|
||||||
|
assert(cc->vmsd == NULL);
|
||||||
|
#else
|
||||||
|
if (cc->vmsd != NULL) {
|
||||||
|
vmstate_unregister(NULL, cc->vmsd, cpu);
|
||||||
|
}
|
||||||
|
if (qdev_get_vmsd(DEVICE(cpu)) == NULL) {
|
||||||
|
vmstate_unregister(NULL, &vmstate_cpu_common, cpu);
|
||||||
|
}
|
||||||
|
tcg_iommu_free_notifier_list(cpu);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Property cpu_common_props[] = {
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
/* Create a memory property for softmmu CPU object,
|
||||||
|
* so users can wire up its memory. (This can't go in hw/core/cpu.c
|
||||||
|
* because that file is compiled only once for both user-mode
|
||||||
|
* and system builds.) The default if no link is set up is to use
|
||||||
|
* the system address space.
|
||||||
|
*/
|
||||||
|
DEFINE_PROP_LINK("memory", CPUState, memory, TYPE_MEMORY_REGION,
|
||||||
|
MemoryRegion *),
|
||||||
|
#endif
|
||||||
|
DEFINE_PROP_BOOL("start-powered-off", CPUState, start_powered_off, false),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
|
void cpu_exec_initfn(CPUState *cpu)
|
||||||
|
{
|
||||||
|
cpu->as = NULL;
|
||||||
|
cpu->num_ases = 0;
|
||||||
|
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
cpu->thread_id = qemu_get_thread_id();
|
||||||
|
cpu->memory = get_system_memory();
|
||||||
|
object_ref(OBJECT(cpu->memory));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpu_exec_realizefn(CPUState *cpu, Error **errp)
|
||||||
|
{
|
||||||
|
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||||
|
static bool tcg_target_initialized;
|
||||||
|
|
||||||
|
cpu_list_add(cpu);
|
||||||
|
|
||||||
|
if (tcg_enabled() && !tcg_target_initialized) {
|
||||||
|
tcg_target_initialized = true;
|
||||||
|
cc->tcg_initialize();
|
||||||
|
}
|
||||||
|
tlb_init(cpu);
|
||||||
|
|
||||||
|
qemu_plugin_vcpu_init_hook(cpu);
|
||||||
|
|
||||||
|
#ifdef CONFIG_USER_ONLY
|
||||||
|
assert(cc->vmsd == NULL);
|
||||||
|
#else /* !CONFIG_USER_ONLY */
|
||||||
|
if (qdev_get_vmsd(DEVICE(cpu)) == NULL) {
|
||||||
|
vmstate_register(NULL, cpu->cpu_index, &vmstate_cpu_common, cpu);
|
||||||
|
}
|
||||||
|
if (cc->vmsd != NULL) {
|
||||||
|
vmstate_register(NULL, cpu->cpu_index, cc->vmsd, cpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
tcg_iommu_init_notifier_list(cpu);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *parse_cpu_option(const char *cpu_option)
|
||||||
|
{
|
||||||
|
ObjectClass *oc;
|
||||||
|
CPUClass *cc;
|
||||||
|
gchar **model_pieces;
|
||||||
|
const char *cpu_type;
|
||||||
|
|
||||||
|
model_pieces = g_strsplit(cpu_option, ",", 2);
|
||||||
|
if (!model_pieces[0]) {
|
||||||
|
error_report("-cpu option cannot be empty");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
oc = cpu_class_by_name(CPU_RESOLVING_TYPE, model_pieces[0]);
|
||||||
|
if (oc == NULL) {
|
||||||
|
error_report("unable to find CPU model '%s'", model_pieces[0]);
|
||||||
|
g_strfreev(model_pieces);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_type = object_class_get_name(oc);
|
||||||
|
cc = CPU_CLASS(oc);
|
||||||
|
cc->parse_features(cpu_type, model_pieces[1], &error_fatal);
|
||||||
|
g_strfreev(model_pieces);
|
||||||
|
return cpu_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_USER_ONLY)
|
||||||
|
void tb_invalidate_phys_addr(target_ulong addr)
|
||||||
|
{
|
||||||
|
mmap_lock();
|
||||||
|
tb_invalidate_phys_page_range(addr, addr + 1);
|
||||||
|
mmap_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void breakpoint_invalidate(CPUState *cpu, target_ulong pc)
|
||||||
|
{
|
||||||
|
tb_invalidate_phys_addr(pc);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs)
|
||||||
|
{
|
||||||
|
ram_addr_t ram_addr;
|
||||||
|
MemoryRegion *mr;
|
||||||
|
hwaddr l = 1;
|
||||||
|
|
||||||
|
if (!tcg_enabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RCU_READ_LOCK_GUARD();
|
||||||
|
mr = address_space_translate(as, addr, &addr, &l, false, attrs);
|
||||||
|
if (!(memory_region_is_ram(mr)
|
||||||
|
|| memory_region_is_romd(mr))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ram_addr = memory_region_get_ram_addr(mr) + addr;
|
||||||
|
tb_invalidate_phys_page_range(ram_addr, ram_addr + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void breakpoint_invalidate(CPUState *cpu, target_ulong pc)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* There may not be a virtual to physical translation for the pc
|
||||||
|
* right now, but there may exist cached TB for this pc.
|
||||||
|
* Flush the whole TB cache to force re-translation of such TBs.
|
||||||
|
* This is heavyweight, but we're debugging anyway.
|
||||||
|
*/
|
||||||
|
tb_flush(cpu);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Add a breakpoint. */
|
||||||
|
int cpu_breakpoint_insert(CPUState *cpu, vaddr pc, int flags,
|
||||||
|
CPUBreakpoint **breakpoint)
|
||||||
|
{
|
||||||
|
CPUBreakpoint *bp;
|
||||||
|
|
||||||
|
bp = g_malloc(sizeof(*bp));
|
||||||
|
|
||||||
|
bp->pc = pc;
|
||||||
|
bp->flags = flags;
|
||||||
|
|
||||||
|
/* keep all GDB-injected breakpoints in front */
|
||||||
|
if (flags & BP_GDB) {
|
||||||
|
QTAILQ_INSERT_HEAD(&cpu->breakpoints, bp, entry);
|
||||||
|
} else {
|
||||||
|
QTAILQ_INSERT_TAIL(&cpu->breakpoints, bp, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
breakpoint_invalidate(cpu, pc);
|
||||||
|
|
||||||
|
if (breakpoint) {
|
||||||
|
*breakpoint = bp;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove a specific breakpoint. */
|
||||||
|
int cpu_breakpoint_remove(CPUState *cpu, vaddr pc, int flags)
|
||||||
|
{
|
||||||
|
CPUBreakpoint *bp;
|
||||||
|
|
||||||
|
QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
|
||||||
|
if (bp->pc == pc && bp->flags == flags) {
|
||||||
|
cpu_breakpoint_remove_by_ref(cpu, bp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove a specific breakpoint by reference. */
|
||||||
|
void cpu_breakpoint_remove_by_ref(CPUState *cpu, CPUBreakpoint *breakpoint)
|
||||||
|
{
|
||||||
|
QTAILQ_REMOVE(&cpu->breakpoints, breakpoint, entry);
|
||||||
|
|
||||||
|
breakpoint_invalidate(cpu, breakpoint->pc);
|
||||||
|
|
||||||
|
g_free(breakpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove all matching breakpoints. */
|
||||||
|
void cpu_breakpoint_remove_all(CPUState *cpu, int mask)
|
||||||
|
{
|
||||||
|
CPUBreakpoint *bp, *next;
|
||||||
|
|
||||||
|
QTAILQ_FOREACH_SAFE(bp, &cpu->breakpoints, entry, next) {
|
||||||
|
if (bp->flags & mask) {
|
||||||
|
cpu_breakpoint_remove_by_ref(cpu, bp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enable or disable single step mode. EXCP_DEBUG is returned by the
|
||||||
|
CPU loop after each instruction */
|
||||||
|
void cpu_single_step(CPUState *cpu, int enabled)
|
||||||
|
{
|
||||||
|
if (cpu->singlestep_enabled != enabled) {
|
||||||
|
cpu->singlestep_enabled = enabled;
|
||||||
|
if (kvm_enabled()) {
|
||||||
|
kvm_update_guest_debug(cpu, 0);
|
||||||
|
} else {
|
||||||
|
/* must flush all the translated code to avoid inconsistencies */
|
||||||
|
/* XXX: only flush what is necessary */
|
||||||
|
tb_flush(cpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpu_abort(CPUState *cpu, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_list ap2;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
va_copy(ap2, ap);
|
||||||
|
fprintf(stderr, "qemu: fatal: ");
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
cpu_dump_state(cpu, stderr, CPU_DUMP_FPU | CPU_DUMP_CCOP);
|
||||||
|
if (qemu_log_separate()) {
|
||||||
|
FILE *logfile = qemu_log_lock();
|
||||||
|
qemu_log("qemu: fatal: ");
|
||||||
|
qemu_log_vprintf(fmt, ap2);
|
||||||
|
qemu_log("\n");
|
||||||
|
log_cpu_state(cpu, CPU_DUMP_FPU | CPU_DUMP_CCOP);
|
||||||
|
qemu_log_flush();
|
||||||
|
qemu_log_unlock(logfile);
|
||||||
|
qemu_log_close();
|
||||||
|
}
|
||||||
|
va_end(ap2);
|
||||||
|
va_end(ap);
|
||||||
|
replay_finish();
|
||||||
|
#if defined(CONFIG_USER_ONLY)
|
||||||
|
{
|
||||||
|
struct sigaction act;
|
||||||
|
sigfillset(&act.sa_mask);
|
||||||
|
act.sa_handler = SIG_DFL;
|
||||||
|
act.sa_flags = 0;
|
||||||
|
sigaction(SIGABRT, &act, NULL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* physical memory access (slow version, mainly for debug) */
|
||||||
|
#if defined(CONFIG_USER_ONLY)
|
||||||
|
int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr,
|
||||||
|
void *ptr, target_ulong len, bool is_write)
|
||||||
|
{
|
||||||
|
int flags;
|
||||||
|
target_ulong l, page;
|
||||||
|
void * p;
|
||||||
|
uint8_t *buf = ptr;
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
page = addr & TARGET_PAGE_MASK;
|
||||||
|
l = (page + TARGET_PAGE_SIZE) - addr;
|
||||||
|
if (l > len)
|
||||||
|
l = len;
|
||||||
|
flags = page_get_flags(page);
|
||||||
|
if (!(flags & PAGE_VALID))
|
||||||
|
return -1;
|
||||||
|
if (is_write) {
|
||||||
|
if (!(flags & PAGE_WRITE))
|
||||||
|
return -1;
|
||||||
|
/* XXX: this code should not depend on lock_user */
|
||||||
|
if (!(p = lock_user(VERIFY_WRITE, addr, l, 0)))
|
||||||
|
return -1;
|
||||||
|
memcpy(p, buf, l);
|
||||||
|
unlock_user(p, addr, l);
|
||||||
|
} else {
|
||||||
|
if (!(flags & PAGE_READ))
|
||||||
|
return -1;
|
||||||
|
/* XXX: this code should not depend on lock_user */
|
||||||
|
if (!(p = lock_user(VERIFY_READ, addr, l, 1)))
|
||||||
|
return -1;
|
||||||
|
memcpy(buf, p, l);
|
||||||
|
unlock_user(p, addr, 0);
|
||||||
|
}
|
||||||
|
len -= l;
|
||||||
|
buf += l;
|
||||||
|
addr += l;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool target_words_bigendian(void)
|
||||||
|
{
|
||||||
|
#if defined(TARGET_WORDS_BIGENDIAN)
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void page_size_init(void)
|
||||||
|
{
|
||||||
|
/* NOTE: we can always suppose that qemu_host_page_size >=
|
||||||
|
TARGET_PAGE_SIZE */
|
||||||
|
if (qemu_host_page_size == 0) {
|
||||||
|
qemu_host_page_size = qemu_real_host_page_size;
|
||||||
|
}
|
||||||
|
if (qemu_host_page_size < TARGET_PAGE_SIZE) {
|
||||||
|
qemu_host_page_size = TARGET_PAGE_SIZE;
|
||||||
|
}
|
||||||
|
qemu_host_page_mask = -(intptr_t)qemu_host_page_size;
|
||||||
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
crypto_ss = ss.source_set()
|
|
||||||
crypto_ss.add(genh)
|
crypto_ss.add(genh)
|
||||||
crypto_ss.add(files(
|
crypto_ss.add(files(
|
||||||
'afsplit.c',
|
'afsplit.c',
|
||||||
|
@ -52,15 +51,6 @@ if 'CONFIG_GNUTLS' in config_host
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
crypto_ss = crypto_ss.apply(config_host, strict: false)
|
|
||||||
libcrypto = static_library('crypto', crypto_ss.sources() + genh,
|
|
||||||
dependencies: [crypto_ss.dependencies()],
|
|
||||||
name_suffix: 'fa',
|
|
||||||
build_by_default: false)
|
|
||||||
|
|
||||||
crypto = declare_dependency(link_whole: libcrypto,
|
|
||||||
dependencies: [authz, qom])
|
|
||||||
|
|
||||||
util_ss.add(files('aes.c'))
|
util_ss.add(files('aes.c'))
|
||||||
util_ss.add(files('init.c'))
|
util_ss.add(files('init.c'))
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ Contents:
|
||||||
atomics
|
atomics
|
||||||
stable-process
|
stable-process
|
||||||
testing
|
testing
|
||||||
|
qtest
|
||||||
decodetree
|
decodetree
|
||||||
secure-coding-practices
|
secure-coding-practices
|
||||||
tcg
|
tcg
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
========================================
|
||||||
|
QTest Device Emulation Testing Framework
|
||||||
|
========================================
|
||||||
|
|
||||||
|
QTest is a device emulation testing framework. It can be very useful to test
|
||||||
|
device models; it could also control certain aspects of QEMU (such as virtual
|
||||||
|
clock stepping), with a special purpose "qtest" protocol. Refer to
|
||||||
|
:ref:`qtest-protocol` for more details of the protocol.
|
||||||
|
|
||||||
|
QTest cases can be executed with
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
make check-qtest
|
||||||
|
|
||||||
|
The QTest library is implemented by ``tests/qtest/libqtest.c`` and the API is
|
||||||
|
defined in ``tests/qtest/libqtest.h``.
|
||||||
|
|
||||||
|
Consider adding a new QTest case when you are introducing a new virtual
|
||||||
|
hardware, or extending one if you are adding functionalities to an existing
|
||||||
|
virtual device.
|
||||||
|
|
||||||
|
On top of libqtest, a higher level library, ``libqos``, was created to
|
||||||
|
encapsulate common tasks of device drivers, such as memory management and
|
||||||
|
communicating with system buses or devices. Many virtual device tests use
|
||||||
|
libqos instead of directly calling into libqtest.
|
||||||
|
|
||||||
|
Steps to add a new QTest case are:
|
||||||
|
|
||||||
|
1. Create a new source file for the test. (More than one file can be added as
|
||||||
|
necessary.) For example, ``tests/qtest/foo-test.c``.
|
||||||
|
|
||||||
|
2. Write the test code with the glib and libqtest/libqos API. See also existing
|
||||||
|
tests and the library headers for reference.
|
||||||
|
|
||||||
|
3. Register the new test in ``tests/qtest/meson.build``. Add the test
|
||||||
|
executable name to an appropriate ``qtests_*`` variable. There is
|
||||||
|
one variable per architecture, plus ``qtests_generic`` for tests
|
||||||
|
that can be run for all architectures. For example::
|
||||||
|
|
||||||
|
qtests_generic = [
|
||||||
|
...
|
||||||
|
'foo-test',
|
||||||
|
...
|
||||||
|
]
|
||||||
|
|
||||||
|
4. If the test has more than one source file or needs to be linked with any
|
||||||
|
dependency other than ``qemuutil`` and ``qos``, list them in the ``qtests``
|
||||||
|
dictionary. For example a test that needs to use the ``QIO`` library
|
||||||
|
will have an entry like::
|
||||||
|
|
||||||
|
{
|
||||||
|
...
|
||||||
|
'foo-test': [io],
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
Debugging a QTest failure is slightly harder than the unit test because the
|
||||||
|
tests look up QEMU program names in the environment variables, such as
|
||||||
|
``QTEST_QEMU_BINARY`` and ``QTEST_QEMU_IMG``, and also because it is not easy
|
||||||
|
to attach gdb to the QEMU process spawned from the test. But manual invoking
|
||||||
|
and using gdb on the test is still simple to do: find out the actual command
|
||||||
|
from the output of
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
make check-qtest V=1
|
||||||
|
|
||||||
|
which you can run manually.
|
||||||
|
|
||||||
|
|
||||||
|
.. _qtest-protocol:
|
||||||
|
|
||||||
|
QTest Protocol
|
||||||
|
--------------
|
||||||
|
|
||||||
|
.. kernel-doc:: softmmu/qtest.c
|
||||||
|
:doc: QTest Protocol
|
||||||
|
|
||||||
|
|
||||||
|
libqtest API reference
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
.. kernel-doc:: tests/qtest/libqos/libqtest.h
|
|
@ -41,15 +41,16 @@ add a new unit test:
|
||||||
test. The test code should be organized with the glib testing framework.
|
test. The test code should be organized with the glib testing framework.
|
||||||
Copying and modifying an existing test is usually a good idea.
|
Copying and modifying an existing test is usually a good idea.
|
||||||
|
|
||||||
3. Add the test to ``tests/Makefile.include``. First, name the unit test
|
3. Add the test to ``tests/meson.build``. The unit tests are listed in a
|
||||||
program and add it to ``$(check-unit-y)``; then add a rule to build the
|
dictionary called ``tests``. The values are any additional sources and
|
||||||
executable. For example:
|
dependencies to be linked with the test. For a simple test whose source
|
||||||
|
is in ``tests/foo-test.c``, it is enough to add an entry like::
|
||||||
|
|
||||||
.. code::
|
{
|
||||||
|
...
|
||||||
check-unit-y += tests/foo-test$(EXESUF)
|
'foo-test': [],
|
||||||
tests/foo-test$(EXESUF): tests/foo-test.o $(test-util-obj-y)
|
...
|
||||||
...
|
}
|
||||||
|
|
||||||
Since unit tests don't require environment variables, the simplest way to debug
|
Since unit tests don't require environment variables, the simplest way to debug
|
||||||
a unit test failure is often directly invoking it or even running it under
|
a unit test failure is often directly invoking it or even running it under
|
||||||
|
@ -70,8 +71,8 @@ QTest
|
||||||
|
|
||||||
QTest is a device emulation testing framework. It can be very useful to test
|
QTest is a device emulation testing framework. It can be very useful to test
|
||||||
device models; it could also control certain aspects of QEMU (such as virtual
|
device models; it could also control certain aspects of QEMU (such as virtual
|
||||||
clock stepping), with a special purpose "qtest" protocol. Refer to the
|
clock stepping), with a special purpose "qtest" protocol. Refer to
|
||||||
documentation in ``qtest.c`` for more details of the protocol.
|
:doc:`qtest` for more details.
|
||||||
|
|
||||||
QTest cases can be executed with
|
QTest cases can be executed with
|
||||||
|
|
||||||
|
@ -79,49 +80,6 @@ QTest cases can be executed with
|
||||||
|
|
||||||
make check-qtest
|
make check-qtest
|
||||||
|
|
||||||
The QTest library is implemented by ``tests/qtest/libqtest.c`` and the API is
|
|
||||||
defined in ``tests/qtest/libqtest.h``.
|
|
||||||
|
|
||||||
Consider adding a new QTest case when you are introducing a new virtual
|
|
||||||
hardware, or extending one if you are adding functionalities to an existing
|
|
||||||
virtual device.
|
|
||||||
|
|
||||||
On top of libqtest, a higher level library, ``libqos``, was created to
|
|
||||||
encapsulate common tasks of device drivers, such as memory management and
|
|
||||||
communicating with system buses or devices. Many virtual device tests use
|
|
||||||
libqos instead of directly calling into libqtest.
|
|
||||||
|
|
||||||
Steps to add a new QTest case are:
|
|
||||||
|
|
||||||
1. Create a new source file for the test. (More than one file can be added as
|
|
||||||
necessary.) For example, ``tests/qtest/foo-test.c``.
|
|
||||||
|
|
||||||
2. Write the test code with the glib and libqtest/libqos API. See also existing
|
|
||||||
tests and the library headers for reference.
|
|
||||||
|
|
||||||
3. Register the new test in ``tests/qtest/Makefile.include``. Add the test
|
|
||||||
executable name to an appropriate ``check-qtest-*-y`` variable. For example:
|
|
||||||
|
|
||||||
``check-qtest-generic-y = tests/qtest/foo-test$(EXESUF)``
|
|
||||||
|
|
||||||
4. Add object dependencies of the executable in the Makefile, including the
|
|
||||||
test source file(s) and other interesting objects. For example:
|
|
||||||
|
|
||||||
``tests/qtest/foo-test$(EXESUF): tests/qtest/foo-test.o $(libqos-obj-y)``
|
|
||||||
|
|
||||||
Debugging a QTest failure is slightly harder than the unit test because the
|
|
||||||
tests look up QEMU program names in the environment variables, such as
|
|
||||||
``QTEST_QEMU_BINARY`` and ``QTEST_QEMU_IMG``, and also because it is not easy
|
|
||||||
to attach gdb to the QEMU process spawned from the test. But manual invoking
|
|
||||||
and using gdb on the test is still simple to do: find out the actual command
|
|
||||||
from the output of
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
make check-qtest V=1
|
|
||||||
|
|
||||||
which you can run manually.
|
|
||||||
|
|
||||||
QAPI schema tests
|
QAPI schema tests
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
|
|
@ -49,12 +49,14 @@ int qbus_walk_children(BusState *bus,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
WITH_RCU_READ_LOCK_GUARD() {
|
||||||
err = qdev_walk_children(kid->child,
|
QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) {
|
||||||
pre_devfn, pre_busfn,
|
err = qdev_walk_children(kid->child,
|
||||||
post_devfn, post_busfn, opaque);
|
pre_devfn, pre_busfn,
|
||||||
if (err < 0) {
|
post_devfn, post_busfn, opaque);
|
||||||
return err;
|
if (err < 0) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,8 +92,10 @@ static void bus_reset_child_foreach(Object *obj, ResettableChildCallback cb,
|
||||||
BusState *bus = BUS(obj);
|
BusState *bus = BUS(obj);
|
||||||
BusChild *kid;
|
BusChild *kid;
|
||||||
|
|
||||||
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
WITH_RCU_READ_LOCK_GUARD() {
|
||||||
cb(OBJECT(kid->child), opaque, type);
|
QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) {
|
||||||
|
cb(OBJECT(kid->child), opaque, type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,9 +198,11 @@ static void bus_set_realized(Object *obj, bool value, Error **errp)
|
||||||
|
|
||||||
/* TODO: recursive realization */
|
/* TODO: recursive realization */
|
||||||
} else if (!value && bus->realized) {
|
} else if (!value && bus->realized) {
|
||||||
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
WITH_RCU_READ_LOCK_GUARD() {
|
||||||
DeviceState *dev = kid->child;
|
QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) {
|
||||||
qdev_unrealize(dev);
|
DeviceState *dev = kid->child;
|
||||||
|
qdev_unrealize(dev);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (bc->unrealize) {
|
if (bc->unrealize) {
|
||||||
bc->unrealize(bus);
|
bc->unrealize(bus);
|
||||||
|
|
|
@ -14,12 +14,6 @@ hwcore_files = files(
|
||||||
'qdev-clock.c',
|
'qdev-clock.c',
|
||||||
)
|
)
|
||||||
|
|
||||||
libhwcore = static_library('hwcore', sources: hwcore_files + genh,
|
|
||||||
name_suffix: 'fa',
|
|
||||||
build_by_default: false)
|
|
||||||
hwcore = declare_dependency(link_whole: libhwcore)
|
|
||||||
common_ss.add(hwcore)
|
|
||||||
|
|
||||||
common_ss.add(files('cpu.c'))
|
common_ss.add(files('cpu.c'))
|
||||||
common_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c'))
|
common_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c'))
|
||||||
common_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c'))
|
common_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c'))
|
||||||
|
|
|
@ -51,6 +51,12 @@ const VMStateDescription *qdev_get_vmsd(DeviceState *dev)
|
||||||
return dc->vmsd;
|
return dc->vmsd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void bus_free_bus_child(BusChild *kid)
|
||||||
|
{
|
||||||
|
object_unref(OBJECT(kid->child));
|
||||||
|
g_free(kid);
|
||||||
|
}
|
||||||
|
|
||||||
static void bus_remove_child(BusState *bus, DeviceState *child)
|
static void bus_remove_child(BusState *bus, DeviceState *child)
|
||||||
{
|
{
|
||||||
BusChild *kid;
|
BusChild *kid;
|
||||||
|
@ -60,15 +66,16 @@ static void bus_remove_child(BusState *bus, DeviceState *child)
|
||||||
char name[32];
|
char name[32];
|
||||||
|
|
||||||
snprintf(name, sizeof(name), "child[%d]", kid->index);
|
snprintf(name, sizeof(name), "child[%d]", kid->index);
|
||||||
QTAILQ_REMOVE(&bus->children, kid, sibling);
|
QTAILQ_REMOVE_RCU(&bus->children, kid, sibling);
|
||||||
|
|
||||||
bus->num_children--;
|
bus->num_children--;
|
||||||
|
|
||||||
/* This gives back ownership of kid->child back to us. */
|
/* This gives back ownership of kid->child back to us. */
|
||||||
object_property_del(OBJECT(bus), name);
|
object_property_del(OBJECT(bus), name);
|
||||||
object_unref(OBJECT(kid->child));
|
|
||||||
g_free(kid);
|
/* free the bus kid, when it is safe to do so*/
|
||||||
return;
|
call_rcu(kid, bus_free_bus_child, rcu);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,7 +90,7 @@ static void bus_add_child(BusState *bus, DeviceState *child)
|
||||||
kid->child = child;
|
kid->child = child;
|
||||||
object_ref(OBJECT(kid->child));
|
object_ref(OBJECT(kid->child));
|
||||||
|
|
||||||
QTAILQ_INSERT_HEAD(&bus->children, kid, sibling);
|
QTAILQ_INSERT_HEAD_RCU(&bus->children, kid, sibling);
|
||||||
|
|
||||||
/* This transfers ownership of kid->child to the property. */
|
/* This transfers ownership of kid->child to the property. */
|
||||||
snprintf(name, sizeof(name), "child[%d]", kid->index);
|
snprintf(name, sizeof(name), "child[%d]", kid->index);
|
||||||
|
@ -94,13 +101,23 @@ static void bus_add_child(BusState *bus, DeviceState *child)
|
||||||
0);
|
0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qdev_set_parent_bus(DeviceState *dev, BusState *bus)
|
static bool bus_check_address(BusState *bus, DeviceState *child, Error **errp)
|
||||||
|
{
|
||||||
|
BusClass *bc = BUS_GET_CLASS(bus);
|
||||||
|
return !bc->check_address || bc->check_address(bus, child, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp)
|
||||||
{
|
{
|
||||||
BusState *old_parent_bus = dev->parent_bus;
|
BusState *old_parent_bus = dev->parent_bus;
|
||||||
DeviceClass *dc = DEVICE_GET_CLASS(dev);
|
DeviceClass *dc = DEVICE_GET_CLASS(dev);
|
||||||
|
|
||||||
assert(dc->bus_type && object_dynamic_cast(OBJECT(bus), dc->bus_type));
|
assert(dc->bus_type && object_dynamic_cast(OBJECT(bus), dc->bus_type));
|
||||||
|
|
||||||
|
if (!bus_check_address(bus, dev, errp)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (old_parent_bus) {
|
if (old_parent_bus) {
|
||||||
trace_qdev_update_parent_bus(dev, object_get_typename(OBJECT(dev)),
|
trace_qdev_update_parent_bus(dev, object_get_typename(OBJECT(dev)),
|
||||||
old_parent_bus, object_get_typename(OBJECT(old_parent_bus)),
|
old_parent_bus, object_get_typename(OBJECT(old_parent_bus)),
|
||||||
|
@ -126,6 +143,7 @@ void qdev_set_parent_bus(DeviceState *dev, BusState *bus)
|
||||||
object_unref(OBJECT(old_parent_bus));
|
object_unref(OBJECT(old_parent_bus));
|
||||||
object_unref(OBJECT(dev));
|
object_unref(OBJECT(dev));
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceState *qdev_new(const char *name)
|
DeviceState *qdev_new(const char *name)
|
||||||
|
@ -371,7 +389,9 @@ bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp)
|
||||||
assert(!dev->realized && !dev->parent_bus);
|
assert(!dev->realized && !dev->parent_bus);
|
||||||
|
|
||||||
if (bus) {
|
if (bus) {
|
||||||
qdev_set_parent_bus(dev, bus);
|
if (!qdev_set_parent_bus(dev, bus, errp)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(!DEVICE_GET_CLASS(dev)->bus_type);
|
assert(!DEVICE_GET_CLASS(dev)->bus_type);
|
||||||
}
|
}
|
||||||
|
@ -659,17 +679,19 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id)
|
||||||
DeviceState *ret;
|
DeviceState *ret;
|
||||||
BusState *child;
|
BusState *child;
|
||||||
|
|
||||||
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
WITH_RCU_READ_LOCK_GUARD() {
|
||||||
DeviceState *dev = kid->child;
|
QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) {
|
||||||
|
DeviceState *dev = kid->child;
|
||||||
|
|
||||||
if (dev->id && strcmp(dev->id, id) == 0) {
|
if (dev->id && strcmp(dev->id, id) == 0) {
|
||||||
return dev;
|
return dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
QLIST_FOREACH(child, &dev->child_bus, sibling) {
|
QLIST_FOREACH(child, &dev->child_bus, sibling) {
|
||||||
ret = qdev_find_recursive(child, id);
|
ret = qdev_find_recursive(child, id);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -924,7 +946,25 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qatomic_store_release(&dev->realized, value);
|
||||||
|
|
||||||
} else if (!value && dev->realized) {
|
} else if (!value && dev->realized) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Change the value so that any concurrent users are aware
|
||||||
|
* that the device is going to be unrealized
|
||||||
|
*
|
||||||
|
* TODO: change .realized property to enum that states
|
||||||
|
* each phase of the device realization/unrealization
|
||||||
|
*/
|
||||||
|
|
||||||
|
qatomic_set(&dev->realized, value);
|
||||||
|
/*
|
||||||
|
* Ensure that concurrent users see this update prior to
|
||||||
|
* any other changes done by unrealize.
|
||||||
|
*/
|
||||||
|
smp_wmb();
|
||||||
|
|
||||||
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
|
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
|
||||||
qbus_unrealize(bus);
|
qbus_unrealize(bus);
|
||||||
}
|
}
|
||||||
|
@ -939,7 +979,6 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(local_err == NULL);
|
assert(local_err == NULL);
|
||||||
dev->realized = value;
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
child_realize_fail:
|
child_realize_fail:
|
||||||
|
|
|
@ -3138,7 +3138,7 @@ static bool failover_replug_primary(VirtIONet *n, Error **errp)
|
||||||
error_setg(errp, "virtio_net: couldn't find primary bus");
|
error_setg(errp, "virtio_net: couldn't find primary bus");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
qdev_set_parent_bus(n->primary_dev, n->primary_bus);
|
qdev_set_parent_bus(n->primary_dev, n->primary_bus, &error_abort);
|
||||||
n->primary_should_be_hidden = false;
|
n->primary_should_be_hidden = false;
|
||||||
if (!qemu_opt_set_bool(n->primary_device_opts,
|
if (!qemu_opt_set_bool(n->primary_device_opts,
|
||||||
"partially_hotplugged", true, errp)) {
|
"partially_hotplugged", true, errp)) {
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* QEMU Firmware configuration device emulation (QOM interfaces)
|
||||||
|
*
|
||||||
|
* Copyright 2020 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "hw/nvram/fw_cfg.h"
|
||||||
|
|
||||||
|
static const TypeInfo fw_cfg_data_generator_interface_info = {
|
||||||
|
.parent = TYPE_INTERFACE,
|
||||||
|
.name = TYPE_FW_CFG_DATA_GENERATOR_INTERFACE,
|
||||||
|
.class_size = sizeof(FWCfgDataGeneratorClass),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void fw_cfg_register_interfaces(void)
|
||||||
|
{
|
||||||
|
type_register_static(&fw_cfg_data_generator_interface_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(fw_cfg_register_interfaces)
|
|
@ -1360,18 +1360,11 @@ static const TypeInfo fw_cfg_mem_info = {
|
||||||
.class_init = fw_cfg_mem_class_init,
|
.class_init = fw_cfg_mem_class_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const TypeInfo fw_cfg_data_generator_interface_info = {
|
|
||||||
.parent = TYPE_INTERFACE,
|
|
||||||
.name = TYPE_FW_CFG_DATA_GENERATOR_INTERFACE,
|
|
||||||
.class_size = sizeof(FWCfgDataGeneratorClass),
|
|
||||||
};
|
|
||||||
|
|
||||||
static void fw_cfg_register_types(void)
|
static void fw_cfg_register_types(void)
|
||||||
{
|
{
|
||||||
type_register_static(&fw_cfg_info);
|
type_register_static(&fw_cfg_info);
|
||||||
type_register_static(&fw_cfg_io_info);
|
type_register_static(&fw_cfg_io_info);
|
||||||
type_register_static(&fw_cfg_mem_info);
|
type_register_static(&fw_cfg_mem_info);
|
||||||
type_register_static(&fw_cfg_data_generator_interface_info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type_init(fw_cfg_register_types)
|
type_init(fw_cfg_register_types)
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
# QOM interfaces must be available anytime QOM is used.
|
||||||
|
qom_ss.add(files('fw_cfg-interface.c'))
|
||||||
|
|
||||||
softmmu_ss.add(files('fw_cfg.c'))
|
softmmu_ss.add(files('fw_cfg.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_CHRP_NVRAM', if_true: files('chrp_nvram.c'))
|
softmmu_ss.add(when: 'CONFIG_CHRP_NVRAM', if_true: files('chrp_nvram.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_DS1225Y', if_true: files('ds1225y.c'))
|
softmmu_ss.add(when: 'CONFIG_DS1225Y', if_true: files('ds1225y.c'))
|
||||||
|
|
|
@ -22,34 +22,67 @@ static void scsi_req_dequeue(SCSIRequest *req);
|
||||||
static uint8_t *scsi_target_alloc_buf(SCSIRequest *req, size_t len);
|
static uint8_t *scsi_target_alloc_buf(SCSIRequest *req, size_t len);
|
||||||
static void scsi_target_free_buf(SCSIRequest *req);
|
static void scsi_target_free_buf(SCSIRequest *req);
|
||||||
|
|
||||||
static Property scsi_props[] = {
|
static int next_scsi_bus;
|
||||||
DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0),
|
|
||||||
DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1),
|
|
||||||
DEFINE_PROP_UINT32("lun", SCSIDevice, lun, -1),
|
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
|
||||||
};
|
|
||||||
|
|
||||||
static void scsi_bus_class_init(ObjectClass *klass, void *data)
|
static SCSIDevice *do_scsi_device_find(SCSIBus *bus,
|
||||||
|
int channel, int id, int lun,
|
||||||
|
bool include_unrealized)
|
||||||
{
|
{
|
||||||
BusClass *k = BUS_CLASS(klass);
|
BusChild *kid;
|
||||||
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
|
SCSIDevice *retval = NULL;
|
||||||
|
|
||||||
k->get_dev_path = scsibus_get_dev_path;
|
QTAILQ_FOREACH_RCU(kid, &bus->qbus.children, sibling) {
|
||||||
k->get_fw_dev_path = scsibus_get_fw_dev_path;
|
DeviceState *qdev = kid->child;
|
||||||
hc->unplug = qdev_simple_device_unplug_cb;
|
SCSIDevice *dev = SCSI_DEVICE(qdev);
|
||||||
|
|
||||||
|
if (dev->channel == channel && dev->id == id) {
|
||||||
|
if (dev->lun == lun) {
|
||||||
|
retval = dev;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we don't find exact match (channel/bus/lun),
|
||||||
|
* we will return the first device which matches channel/bus
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!retval) {
|
||||||
|
retval = dev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function might run on the IO thread and we might race against
|
||||||
|
* main thread hot-plugging the device.
|
||||||
|
* We assume that as soon as .realized is set to true we can let
|
||||||
|
* the user access the device.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (retval && !include_unrealized &&
|
||||||
|
!qatomic_load_acquire(&retval->qdev.realized)) {
|
||||||
|
retval = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo scsi_bus_info = {
|
SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int id, int lun)
|
||||||
.name = TYPE_SCSI_BUS,
|
{
|
||||||
.parent = TYPE_BUS,
|
RCU_READ_LOCK_GUARD();
|
||||||
.instance_size = sizeof(SCSIBus),
|
return do_scsi_device_find(bus, channel, id, lun, false);
|
||||||
.class_init = scsi_bus_class_init,
|
}
|
||||||
.interfaces = (InterfaceInfo[]) {
|
|
||||||
{ TYPE_HOTPLUG_HANDLER },
|
SCSIDevice *scsi_device_get(SCSIBus *bus, int channel, int id, int lun)
|
||||||
{ }
|
{
|
||||||
|
SCSIDevice *d;
|
||||||
|
RCU_READ_LOCK_GUARD();
|
||||||
|
d = do_scsi_device_find(bus, channel, id, lun, false);
|
||||||
|
if (d) {
|
||||||
|
object_ref(d);
|
||||||
}
|
}
|
||||||
};
|
return d;
|
||||||
static int next_scsi_bus;
|
}
|
||||||
|
|
||||||
static void scsi_device_realize(SCSIDevice *s, Error **errp)
|
static void scsi_device_realize(SCSIDevice *s, Error **errp)
|
||||||
{
|
{
|
||||||
|
@ -160,35 +193,71 @@ static void scsi_dma_restart_cb(void *opaque, int running, RunState state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool scsi_bus_is_address_free(SCSIBus *bus,
|
||||||
|
int channel, int target, int lun,
|
||||||
|
SCSIDevice **p_dev)
|
||||||
|
{
|
||||||
|
SCSIDevice *d;
|
||||||
|
|
||||||
|
RCU_READ_LOCK_GUARD();
|
||||||
|
d = do_scsi_device_find(bus, channel, target, lun, true);
|
||||||
|
if (d && d->lun == lun) {
|
||||||
|
if (p_dev) {
|
||||||
|
*p_dev = d;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (p_dev) {
|
||||||
|
*p_dev = NULL;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool scsi_bus_check_address(BusState *qbus, DeviceState *qdev, Error **errp)
|
||||||
|
{
|
||||||
|
SCSIDevice *dev = SCSI_DEVICE(qdev);
|
||||||
|
SCSIBus *bus = SCSI_BUS(qbus);
|
||||||
|
|
||||||
|
if (dev->channel > bus->info->max_channel) {
|
||||||
|
error_setg(errp, "bad scsi channel id: %d", dev->channel);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dev->id != -1 && dev->id > bus->info->max_target) {
|
||||||
|
error_setg(errp, "bad scsi device id: %d", dev->id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dev->lun != -1 && dev->lun > bus->info->max_lun) {
|
||||||
|
error_setg(errp, "bad scsi device lun: %d", dev->lun);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dev->id != -1 && dev->lun != -1) {
|
||||||
|
SCSIDevice *d;
|
||||||
|
if (!scsi_bus_is_address_free(bus, dev->channel, dev->id, dev->lun, &d)) {
|
||||||
|
error_setg(errp, "lun already used by '%s'", d->qdev.id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void scsi_qdev_realize(DeviceState *qdev, Error **errp)
|
static void scsi_qdev_realize(DeviceState *qdev, Error **errp)
|
||||||
{
|
{
|
||||||
SCSIDevice *dev = SCSI_DEVICE(qdev);
|
SCSIDevice *dev = SCSI_DEVICE(qdev);
|
||||||
SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
|
SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
|
||||||
SCSIDevice *d;
|
bool is_free;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
if (dev->channel > bus->info->max_channel) {
|
|
||||||
error_setg(errp, "bad scsi channel id: %d", dev->channel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (dev->id != -1 && dev->id > bus->info->max_target) {
|
|
||||||
error_setg(errp, "bad scsi device id: %d", dev->id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (dev->lun != -1 && dev->lun > bus->info->max_lun) {
|
|
||||||
error_setg(errp, "bad scsi device lun: %d", dev->lun);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dev->id == -1) {
|
if (dev->id == -1) {
|
||||||
int id = -1;
|
int id = -1;
|
||||||
if (dev->lun == -1) {
|
if (dev->lun == -1) {
|
||||||
dev->lun = 0;
|
dev->lun = 0;
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
d = scsi_device_find(bus, dev->channel, ++id, dev->lun);
|
is_free = scsi_bus_is_address_free(bus, dev->channel, ++id, dev->lun, NULL);
|
||||||
} while (d && d->lun == dev->lun && id < bus->info->max_target);
|
} while (!is_free && id < bus->info->max_target);
|
||||||
if (d && d->lun == dev->lun) {
|
if (!is_free) {
|
||||||
error_setg(errp, "no free target");
|
error_setg(errp, "no free target");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -196,20 +265,13 @@ static void scsi_qdev_realize(DeviceState *qdev, Error **errp)
|
||||||
} else if (dev->lun == -1) {
|
} else if (dev->lun == -1) {
|
||||||
int lun = -1;
|
int lun = -1;
|
||||||
do {
|
do {
|
||||||
d = scsi_device_find(bus, dev->channel, dev->id, ++lun);
|
is_free = scsi_bus_is_address_free(bus, dev->channel, dev->id, ++lun, NULL);
|
||||||
} while (d && d->lun == lun && lun < bus->info->max_lun);
|
} while (!is_free && lun < bus->info->max_lun);
|
||||||
if (d && d->lun == lun) {
|
if (!is_free) {
|
||||||
error_setg(errp, "no free lun");
|
error_setg(errp, "no free lun");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dev->lun = lun;
|
dev->lun = lun;
|
||||||
} else {
|
|
||||||
d = scsi_device_find(bus, dev->channel, dev->id, dev->lun);
|
|
||||||
assert(d);
|
|
||||||
if (d->lun == dev->lun && dev != d) {
|
|
||||||
error_setg(errp, "lun already used by '%s'", d->qdev.id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QTAILQ_INIT(&dev->requests);
|
QTAILQ_INIT(&dev->requests);
|
||||||
|
@ -376,19 +438,23 @@ struct SCSITargetReq {
|
||||||
static void store_lun(uint8_t *outbuf, int lun)
|
static void store_lun(uint8_t *outbuf, int lun)
|
||||||
{
|
{
|
||||||
if (lun < 256) {
|
if (lun < 256) {
|
||||||
|
/* Simple logical unit addressing method*/
|
||||||
|
outbuf[0] = 0;
|
||||||
outbuf[1] = lun;
|
outbuf[1] = lun;
|
||||||
return;
|
} else {
|
||||||
|
/* Flat space addressing method */
|
||||||
|
outbuf[0] = 0x40 | (lun >> 8);
|
||||||
|
outbuf[1] = (lun & 255);
|
||||||
}
|
}
|
||||||
outbuf[1] = (lun & 255);
|
|
||||||
outbuf[0] = (lun >> 8) | 0x40;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool scsi_target_emulate_report_luns(SCSITargetReq *r)
|
static bool scsi_target_emulate_report_luns(SCSITargetReq *r)
|
||||||
{
|
{
|
||||||
BusChild *kid;
|
BusChild *kid;
|
||||||
int i, len, n;
|
|
||||||
int channel, id;
|
int channel, id;
|
||||||
bool found_lun0;
|
uint8_t tmp[8] = {0};
|
||||||
|
int len = 0;
|
||||||
|
GByteArray *buf;
|
||||||
|
|
||||||
if (r->req.cmd.xfer < 16) {
|
if (r->req.cmd.xfer < 16) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -396,42 +462,40 @@ static bool scsi_target_emulate_report_luns(SCSITargetReq *r)
|
||||||
if (r->req.cmd.buf[2] > 2) {
|
if (r->req.cmd.buf[2] > 2) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* reserve space for 63 LUNs*/
|
||||||
|
buf = g_byte_array_sized_new(512);
|
||||||
|
|
||||||
channel = r->req.dev->channel;
|
channel = r->req.dev->channel;
|
||||||
id = r->req.dev->id;
|
id = r->req.dev->id;
|
||||||
found_lun0 = false;
|
|
||||||
n = 0;
|
|
||||||
QTAILQ_FOREACH(kid, &r->req.bus->qbus.children, sibling) {
|
|
||||||
DeviceState *qdev = kid->child;
|
|
||||||
SCSIDevice *dev = SCSI_DEVICE(qdev);
|
|
||||||
|
|
||||||
if (dev->channel == channel && dev->id == id) {
|
/* add size (will be updated later to correct value */
|
||||||
if (dev->lun == 0) {
|
g_byte_array_append(buf, tmp, 8);
|
||||||
found_lun0 = true;
|
len += 8;
|
||||||
|
|
||||||
|
/* add LUN0 */
|
||||||
|
g_byte_array_append(buf, tmp, 8);
|
||||||
|
len += 8;
|
||||||
|
|
||||||
|
WITH_RCU_READ_LOCK_GUARD() {
|
||||||
|
QTAILQ_FOREACH_RCU(kid, &r->req.bus->qbus.children, sibling) {
|
||||||
|
DeviceState *qdev = kid->child;
|
||||||
|
SCSIDevice *dev = SCSI_DEVICE(qdev);
|
||||||
|
|
||||||
|
if (dev->channel == channel && dev->id == id && dev->lun != 0) {
|
||||||
|
store_lun(tmp, dev->lun);
|
||||||
|
g_byte_array_append(buf, tmp, 8);
|
||||||
|
len += 8;
|
||||||
}
|
}
|
||||||
n += 8;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found_lun0) {
|
|
||||||
n += 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
scsi_target_alloc_buf(&r->req, n + 8);
|
r->buf_len = len;
|
||||||
|
r->buf = g_byte_array_free(buf, FALSE);
|
||||||
|
r->len = MIN(len, r->req.cmd.xfer & ~7);
|
||||||
|
|
||||||
len = MIN(n + 8, r->req.cmd.xfer & ~7);
|
/* store the LUN list length */
|
||||||
memset(r->buf, 0, len);
|
stl_be_p(&r->buf[0], len - 8);
|
||||||
stl_be_p(&r->buf[0], n);
|
|
||||||
i = found_lun0 ? 8 : 16;
|
|
||||||
QTAILQ_FOREACH(kid, &r->req.bus->qbus.children, sibling) {
|
|
||||||
DeviceState *qdev = kid->child;
|
|
||||||
SCSIDevice *dev = SCSI_DEVICE(qdev);
|
|
||||||
|
|
||||||
if (dev->channel == channel && dev->id == id) {
|
|
||||||
store_lun(&r->buf[i], dev->lun);
|
|
||||||
i += 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(i == n + 8);
|
|
||||||
r->len = len;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1567,25 +1631,6 @@ static char *scsibus_get_fw_dev_path(DeviceState *dev)
|
||||||
qdev_fw_name(dev), d->id, d->lun);
|
qdev_fw_name(dev), d->id, d->lun);
|
||||||
}
|
}
|
||||||
|
|
||||||
SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int id, int lun)
|
|
||||||
{
|
|
||||||
BusChild *kid;
|
|
||||||
SCSIDevice *target_dev = NULL;
|
|
||||||
|
|
||||||
QTAILQ_FOREACH_REVERSE(kid, &bus->qbus.children, sibling) {
|
|
||||||
DeviceState *qdev = kid->child;
|
|
||||||
SCSIDevice *dev = SCSI_DEVICE(qdev);
|
|
||||||
|
|
||||||
if (dev->channel == channel && dev->id == id) {
|
|
||||||
if (dev->lun == lun) {
|
|
||||||
return dev;
|
|
||||||
}
|
|
||||||
target_dev = dev;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return target_dev;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SCSI request list. For simplicity, pv points to the whole device */
|
/* SCSI request list. For simplicity, pv points to the whole device */
|
||||||
|
|
||||||
static int put_scsi_requests(QEMUFile *f, void *pv, size_t size,
|
static int put_scsi_requests(QEMUFile *f, void *pv, size_t size,
|
||||||
|
@ -1709,6 +1754,13 @@ const VMStateDescription vmstate_scsi_device = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static Property scsi_props[] = {
|
||||||
|
DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0),
|
||||||
|
DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1),
|
||||||
|
DEFINE_PROP_UINT32("lun", SCSIDevice, lun, -1),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
static void scsi_device_class_init(ObjectClass *klass, void *data)
|
static void scsi_device_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
DeviceClass *k = DEVICE_CLASS(klass);
|
DeviceClass *k = DEVICE_CLASS(klass);
|
||||||
|
@ -1739,6 +1791,28 @@ static const TypeInfo scsi_device_type_info = {
|
||||||
.instance_init = scsi_dev_instance_init,
|
.instance_init = scsi_dev_instance_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void scsi_bus_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
BusClass *k = BUS_CLASS(klass);
|
||||||
|
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
|
||||||
|
|
||||||
|
k->get_dev_path = scsibus_get_dev_path;
|
||||||
|
k->get_fw_dev_path = scsibus_get_fw_dev_path;
|
||||||
|
k->check_address = scsi_bus_check_address;
|
||||||
|
hc->unplug = qdev_simple_device_unplug_cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo scsi_bus_info = {
|
||||||
|
.name = TYPE_SCSI_BUS,
|
||||||
|
.parent = TYPE_BUS,
|
||||||
|
.instance_size = sizeof(SCSIBus),
|
||||||
|
.class_init = scsi_bus_class_init,
|
||||||
|
.interfaces = (InterfaceInfo[]) {
|
||||||
|
{ TYPE_HOTPLUG_HANDLER },
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static void scsi_register_types(void)
|
static void scsi_register_types(void)
|
||||||
{
|
{
|
||||||
type_register_static(&scsi_bus_info);
|
type_register_static(&scsi_bus_info);
|
||||||
|
|
|
@ -33,7 +33,7 @@ static inline int virtio_scsi_get_lun(uint8_t *lun)
|
||||||
return ((lun[2] << 8) | lun[3]) & 0x3FFF;
|
return ((lun[2] << 8) | lun[3]) & 0x3FFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline SCSIDevice *virtio_scsi_device_find(VirtIOSCSI *s, uint8_t *lun)
|
static inline SCSIDevice *virtio_scsi_device_get(VirtIOSCSI *s, uint8_t *lun)
|
||||||
{
|
{
|
||||||
if (lun[0] != 1) {
|
if (lun[0] != 1) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -41,7 +41,7 @@ static inline SCSIDevice *virtio_scsi_device_find(VirtIOSCSI *s, uint8_t *lun)
|
||||||
if (lun[2] != 0 && !(lun[2] >= 0x40 && lun[2] < 0x80)) {
|
if (lun[2] != 0 && !(lun[2] >= 0x40 && lun[2] < 0x80)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return scsi_device_find(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun));
|
return scsi_device_get(&s->bus, 0, lun[1], virtio_scsi_get_lun(lun));
|
||||||
}
|
}
|
||||||
|
|
||||||
void virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq, VirtIOSCSIReq *req)
|
void virtio_scsi_init_req(VirtIOSCSI *s, VirtQueue *vq, VirtIOSCSIReq *req)
|
||||||
|
@ -256,7 +256,7 @@ static inline void virtio_scsi_ctx_check(VirtIOSCSI *s, SCSIDevice *d)
|
||||||
* case of async cancellation. */
|
* case of async cancellation. */
|
||||||
static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||||
{
|
{
|
||||||
SCSIDevice *d = virtio_scsi_device_find(s, req->req.tmf.lun);
|
SCSIDevice *d = virtio_scsi_device_get(s, req->req.tmf.lun);
|
||||||
SCSIRequest *r, *next;
|
SCSIRequest *r, *next;
|
||||||
BusChild *kid;
|
BusChild *kid;
|
||||||
int target;
|
int target;
|
||||||
|
@ -367,12 +367,16 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||||
case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET:
|
case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET:
|
||||||
target = req->req.tmf.lun[1];
|
target = req->req.tmf.lun[1];
|
||||||
s->resetting++;
|
s->resetting++;
|
||||||
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
|
|
||||||
d = SCSI_DEVICE(kid->child);
|
rcu_read_lock();
|
||||||
if (d->channel == 0 && d->id == target) {
|
QTAILQ_FOREACH_RCU(kid, &s->bus.qbus.children, sibling) {
|
||||||
qdev_reset_all(&d->qdev);
|
SCSIDevice *d1 = SCSI_DEVICE(kid->child);
|
||||||
}
|
if (d1->channel == 0 && d1->id == target) {
|
||||||
|
qdev_reset_all(&d1->qdev);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
s->resetting--;
|
s->resetting--;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -382,14 +386,17 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object_unref(OBJECT(d));
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
incorrect_lun:
|
incorrect_lun:
|
||||||
req->resp.tmf.response = VIRTIO_SCSI_S_INCORRECT_LUN;
|
req->resp.tmf.response = VIRTIO_SCSI_S_INCORRECT_LUN;
|
||||||
|
object_unref(OBJECT(d));
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
req->resp.tmf.response = VIRTIO_SCSI_S_BAD_TARGET;
|
req->resp.tmf.response = VIRTIO_SCSI_S_BAD_TARGET;
|
||||||
|
object_unref(OBJECT(d));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -560,7 +567,7 @@ static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d = virtio_scsi_device_find(s, req->req.cmd.lun);
|
d = virtio_scsi_device_get(s, req->req.cmd.lun);
|
||||||
if (!d) {
|
if (!d) {
|
||||||
req->resp.cmd.response = VIRTIO_SCSI_S_BAD_TARGET;
|
req->resp.cmd.response = VIRTIO_SCSI_S_BAD_TARGET;
|
||||||
virtio_scsi_complete_cmd_req(req);
|
virtio_scsi_complete_cmd_req(req);
|
||||||
|
@ -576,10 +583,12 @@ static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
||||||
req->sreq->cmd.xfer > req->qsgl.size)) {
|
req->sreq->cmd.xfer > req->qsgl.size)) {
|
||||||
req->resp.cmd.response = VIRTIO_SCSI_S_OVERRUN;
|
req->resp.cmd.response = VIRTIO_SCSI_S_OVERRUN;
|
||||||
virtio_scsi_complete_cmd_req(req);
|
virtio_scsi_complete_cmd_req(req);
|
||||||
|
object_unref(OBJECT(d));
|
||||||
return -ENOBUFS;
|
return -ENOBUFS;
|
||||||
}
|
}
|
||||||
scsi_req_ref(req->sreq);
|
scsi_req_ref(req->sreq);
|
||||||
blk_io_plug(d->conf.blk);
|
blk_io_plug(d->conf.blk);
|
||||||
|
object_unref(OBJECT(d));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "hw/qdev-core.h"
|
#include "hw/qdev-core.h"
|
||||||
#include "hw/sd/sd.h"
|
#include "hw/sd/sd.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
static inline const char *sdbus_name(SDBus *sdbus)
|
static inline const char *sdbus_name(SDBus *sdbus)
|
||||||
|
@ -240,7 +241,7 @@ void sdbus_reparent_card(SDBus *from, SDBus *to)
|
||||||
readonly = sc->get_readonly(card);
|
readonly = sc->get_readonly(card);
|
||||||
|
|
||||||
sdbus_set_inserted(from, false);
|
sdbus_set_inserted(from, false);
|
||||||
qdev_set_parent_bus(DEVICE(card), &to->qbus);
|
qdev_set_parent_bus(DEVICE(card), &to->qbus, &error_abort);
|
||||||
sdbus_set_inserted(to, true);
|
sdbus_set_inserted(to, true);
|
||||||
sdbus_set_readonly(to, readonly);
|
sdbus_set_readonly(to, readonly);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,9 @@ void cpu_list_unlock(void);
|
||||||
|
|
||||||
void tcg_flush_softmmu_tlb(CPUState *cs);
|
void tcg_flush_softmmu_tlb(CPUState *cs);
|
||||||
|
|
||||||
|
void tcg_iommu_init_notifier_list(CPUState *cpu);
|
||||||
|
void tcg_iommu_free_notifier_list(CPUState *cpu);
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
|
|
||||||
enum device_endian {
|
enum device_endian {
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
OBJECT_DECLARE_SIMPLE_TYPE(VmGenIdState, VMGENID)
|
OBJECT_DECLARE_SIMPLE_TYPE(VmGenIdState, VMGENID)
|
||||||
|
|
||||||
struct VmGenIdState {
|
struct VmGenIdState {
|
||||||
DeviceClass parent_obj;
|
DeviceState parent_obj;
|
||||||
QemuUUID guid; /* The 128-bit GUID seen by the guest */
|
QemuUUID guid; /* The 128-bit GUID seen by the guest */
|
||||||
uint8_t vmgenid_addr_le[8]; /* Address of the GUID (little-endian) */
|
uint8_t vmgenid_addr_le[8]; /* Address of the GUID (little-endian) */
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,7 +24,7 @@ DECLARE_INSTANCE_CHECKER(VMCoreInfoState, VMCOREINFO,
|
||||||
typedef struct fw_cfg_vmcoreinfo FWCfgVMCoreInfo;
|
typedef struct fw_cfg_vmcoreinfo FWCfgVMCoreInfo;
|
||||||
|
|
||||||
struct VMCoreInfoState {
|
struct VMCoreInfoState {
|
||||||
DeviceClass parent_obj;
|
DeviceState parent_obj;
|
||||||
|
|
||||||
bool has_vmcoreinfo;
|
bool has_vmcoreinfo;
|
||||||
FWCfgVMCoreInfo vmcoreinfo;
|
FWCfgVMCoreInfo vmcoreinfo;
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include "qemu/queue.h"
|
#include "qemu/queue.h"
|
||||||
#include "qemu/bitmap.h"
|
#include "qemu/bitmap.h"
|
||||||
|
#include "qemu/rcu.h"
|
||||||
|
#include "qemu/rcu_queue.h"
|
||||||
#include "qom/object.h"
|
#include "qom/object.h"
|
||||||
#include "hw/hotplug.h"
|
#include "hw/hotplug.h"
|
||||||
#include "hw/resettable.h"
|
#include "hw/resettable.h"
|
||||||
|
@ -161,6 +163,8 @@ struct NamedClockList {
|
||||||
/**
|
/**
|
||||||
* DeviceState:
|
* DeviceState:
|
||||||
* @realized: Indicates whether the device has been fully constructed.
|
* @realized: Indicates whether the device has been fully constructed.
|
||||||
|
* When accessed outsize big qemu lock, must be accessed with
|
||||||
|
* atomic_load_acquire()
|
||||||
* @reset: ResettableState for the device; handled by Resettable interface.
|
* @reset: ResettableState for the device; handled by Resettable interface.
|
||||||
*
|
*
|
||||||
* This structure should not be accessed directly. We declare it here
|
* This structure should not be accessed directly. We declare it here
|
||||||
|
@ -210,13 +214,24 @@ struct BusClass {
|
||||||
/* FIXME first arg should be BusState */
|
/* FIXME first arg should be BusState */
|
||||||
void (*print_dev)(Monitor *mon, DeviceState *dev, int indent);
|
void (*print_dev)(Monitor *mon, DeviceState *dev, int indent);
|
||||||
char *(*get_dev_path)(DeviceState *dev);
|
char *(*get_dev_path)(DeviceState *dev);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This callback is used to create Open Firmware device path in accordance
|
* This callback is used to create Open Firmware device path in accordance
|
||||||
* with OF spec http://forthworks.com/standards/of1275.pdf. Individual bus
|
* with OF spec http://forthworks.com/standards/of1275.pdf. Individual bus
|
||||||
* bindings can be found at http://playground.sun.com/1275/bindings/.
|
* bindings can be found at http://playground.sun.com/1275/bindings/.
|
||||||
*/
|
*/
|
||||||
char *(*get_fw_dev_path)(DeviceState *dev);
|
char *(*get_fw_dev_path)(DeviceState *dev);
|
||||||
|
|
||||||
void (*reset)(BusState *bus);
|
void (*reset)(BusState *bus);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return whether the device can be added to @bus,
|
||||||
|
* based on the address that was set (via device properties)
|
||||||
|
* before realize. If not, on return @errp contains the
|
||||||
|
* human-readable error message.
|
||||||
|
*/
|
||||||
|
bool (*check_address)(BusState *bus, DeviceState *dev, Error **errp);
|
||||||
|
|
||||||
BusRealize realize;
|
BusRealize realize;
|
||||||
BusUnrealize unrealize;
|
BusUnrealize unrealize;
|
||||||
|
|
||||||
|
@ -227,6 +242,7 @@ struct BusClass {
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct BusChild {
|
typedef struct BusChild {
|
||||||
|
struct rcu_head rcu;
|
||||||
DeviceState *child;
|
DeviceState *child;
|
||||||
int index;
|
int index;
|
||||||
QTAILQ_ENTRY(BusChild) sibling;
|
QTAILQ_ENTRY(BusChild) sibling;
|
||||||
|
@ -247,6 +263,12 @@ struct BusState {
|
||||||
int max_index;
|
int max_index;
|
||||||
bool realized;
|
bool realized;
|
||||||
int num_children;
|
int num_children;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* children is a RCU QTAILQ, thus readers must use RCU to access it,
|
||||||
|
* and writers must hold the big qemu lock
|
||||||
|
*/
|
||||||
|
|
||||||
QTAILQ_HEAD(, BusChild) children;
|
QTAILQ_HEAD(, BusChild) children;
|
||||||
QLIST_ENTRY(BusState) sibling;
|
QLIST_ENTRY(BusState) sibling;
|
||||||
ResettableState reset;
|
ResettableState reset;
|
||||||
|
@ -788,7 +810,7 @@ const char *qdev_fw_name(DeviceState *dev);
|
||||||
Object *qdev_get_machine(void);
|
Object *qdev_get_machine(void);
|
||||||
|
|
||||||
/* FIXME: make this a link<> */
|
/* FIXME: make this a link<> */
|
||||||
void qdev_set_parent_bus(DeviceState *dev, BusState *bus);
|
bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp);
|
||||||
|
|
||||||
extern bool qdev_hotplug;
|
extern bool qdev_hotplug;
|
||||||
extern bool qdev_hot_removed;
|
extern bool qdev_hot_removed;
|
||||||
|
|
|
@ -190,6 +190,7 @@ int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed);
|
||||||
int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size,
|
int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size,
|
||||||
uint8_t *buf, uint8_t buf_size);
|
uint8_t *buf, uint8_t buf_size);
|
||||||
SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun);
|
SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun);
|
||||||
|
SCSIDevice *scsi_device_get(SCSIBus *bus, int channel, int target, int lun);
|
||||||
|
|
||||||
/* scsi-generic.c. */
|
/* scsi-generic.c. */
|
||||||
extern const SCSIReqOps scsi_generic_req_ops;
|
extern const SCSIReqOps scsi_generic_req_ops;
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
OBJECT_DECLARE_TYPE(CanHostState, CanHostClass, CAN_HOST)
|
OBJECT_DECLARE_TYPE(CanHostState, CanHostClass, CAN_HOST)
|
||||||
|
|
||||||
struct CanHostState {
|
struct CanHostState {
|
||||||
ObjectClass oc;
|
Object oc;
|
||||||
|
|
||||||
CanBusState *bus;
|
CanBusState *bus;
|
||||||
CanBusClientState bus_client;
|
CanBusClientState bus_client;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
io_ss = ss.source_set()
|
|
||||||
io_ss.add(genh)
|
io_ss.add(genh)
|
||||||
io_ss.add(files(
|
io_ss.add(files(
|
||||||
'channel-buffer.c',
|
'channel-buffer.c',
|
||||||
|
@ -14,12 +13,3 @@ io_ss.add(files(
|
||||||
'net-listener.c',
|
'net-listener.c',
|
||||||
'task.c',
|
'task.c',
|
||||||
))
|
))
|
||||||
|
|
||||||
io_ss = io_ss.apply(config_host, strict: false)
|
|
||||||
libio = static_library('io', io_ss.sources() + genh,
|
|
||||||
dependencies: [io_ss.dependencies()],
|
|
||||||
link_with: libqemuutil,
|
|
||||||
name_suffix: 'fa',
|
|
||||||
build_by_default: false)
|
|
||||||
|
|
||||||
io = declare_dependency(link_whole: libio, dependencies: [crypto, qom])
|
|
||||||
|
|
128
meson.build
128
meson.build
|
@ -59,6 +59,8 @@ elif cpu == 's390x'
|
||||||
kvm_targets = ['s390x-softmmu']
|
kvm_targets = ['s390x-softmmu']
|
||||||
elif cpu in ['ppc', 'ppc64']
|
elif cpu in ['ppc', 'ppc64']
|
||||||
kvm_targets = ['ppc-softmmu', 'ppc64-softmmu']
|
kvm_targets = ['ppc-softmmu', 'ppc64-softmmu']
|
||||||
|
elif cpu in ['mips', 'mips64']
|
||||||
|
kvm_targets = ['mips-softmmu', 'mipsel-softmmu', 'mips64-softmmu', 'mips64el-softmmu']
|
||||||
else
|
else
|
||||||
kvm_targets = []
|
kvm_targets = []
|
||||||
endif
|
endif
|
||||||
|
@ -612,7 +614,9 @@ if not has_malloc_trim and get_option('malloc_trim').enabled()
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Create config-host.h
|
#################
|
||||||
|
# config-host.h #
|
||||||
|
#################
|
||||||
|
|
||||||
config_host_data.set('CONFIG_COCOA', cocoa.found())
|
config_host_data.set('CONFIG_COCOA', cocoa.found())
|
||||||
config_host_data.set('CONFIG_LIBUDEV', libudev.found())
|
config_host_data.set('CONFIG_LIBUDEV', libudev.found())
|
||||||
|
@ -658,6 +662,10 @@ foreach k, v: config_host
|
||||||
endif
|
endif
|
||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
|
########################
|
||||||
|
# Target configuration #
|
||||||
|
########################
|
||||||
|
|
||||||
minikconf = find_program('scripts/minikconf.py')
|
minikconf = find_program('scripts/minikconf.py')
|
||||||
config_all = {}
|
config_all = {}
|
||||||
config_all_devices = {}
|
config_all_devices = {}
|
||||||
|
@ -864,7 +872,9 @@ config_all += {
|
||||||
'CONFIG_ALL': true,
|
'CONFIG_ALL': true,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Submodules
|
##############
|
||||||
|
# Submodules #
|
||||||
|
##############
|
||||||
|
|
||||||
capstone = not_found
|
capstone = not_found
|
||||||
capstone_opt = get_option('capstone')
|
capstone_opt = get_option('capstone')
|
||||||
|
@ -1103,9 +1113,11 @@ config_host_data.set('CONFIG_CAPSTONE', capstone.found())
|
||||||
config_host_data.set('CONFIG_FDT', fdt.found())
|
config_host_data.set('CONFIG_FDT', fdt.found())
|
||||||
config_host_data.set('CONFIG_SLIRP', slirp.found())
|
config_host_data.set('CONFIG_SLIRP', slirp.found())
|
||||||
|
|
||||||
genh += configure_file(output: 'config-host.h', configuration: config_host_data)
|
#####################
|
||||||
|
# Generated sources #
|
||||||
|
#####################
|
||||||
|
|
||||||
# Generators
|
genh += configure_file(output: 'config-host.h', configuration: config_host_data)
|
||||||
|
|
||||||
hxtool = find_program('scripts/hxtool')
|
hxtool = find_program('scripts/hxtool')
|
||||||
shaderinclude = find_program('scripts/shaderinclude.pl')
|
shaderinclude = find_program('scripts/shaderinclude.pl')
|
||||||
|
@ -1180,21 +1192,28 @@ sphinx_extn_depends = [ meson.source_root() / 'docs/sphinx/depfile.py',
|
||||||
meson.source_root() / 'docs/sphinx/qmp_lexer.py',
|
meson.source_root() / 'docs/sphinx/qmp_lexer.py',
|
||||||
qapi_gen_depends ]
|
qapi_gen_depends ]
|
||||||
|
|
||||||
# Collect sourcesets.
|
###################
|
||||||
|
# Collect sources #
|
||||||
|
###################
|
||||||
|
|
||||||
util_ss = ss.source_set()
|
authz_ss = ss.source_set()
|
||||||
|
blockdev_ss = ss.source_set()
|
||||||
|
block_ss = ss.source_set()
|
||||||
|
bsd_user_ss = ss.source_set()
|
||||||
|
chardev_ss = ss.source_set()
|
||||||
|
common_ss = ss.source_set()
|
||||||
|
crypto_ss = ss.source_set()
|
||||||
|
io_ss = ss.source_set()
|
||||||
|
linux_user_ss = ss.source_set()
|
||||||
|
qmp_ss = ss.source_set()
|
||||||
|
qom_ss = ss.source_set()
|
||||||
|
softmmu_ss = ss.source_set()
|
||||||
|
specific_fuzz_ss = ss.source_set()
|
||||||
|
specific_ss = ss.source_set()
|
||||||
stub_ss = ss.source_set()
|
stub_ss = ss.source_set()
|
||||||
trace_ss = ss.source_set()
|
trace_ss = ss.source_set()
|
||||||
block_ss = ss.source_set()
|
|
||||||
blockdev_ss = ss.source_set()
|
|
||||||
qmp_ss = ss.source_set()
|
|
||||||
common_ss = ss.source_set()
|
|
||||||
softmmu_ss = ss.source_set()
|
|
||||||
user_ss = ss.source_set()
|
user_ss = ss.source_set()
|
||||||
bsd_user_ss = ss.source_set()
|
util_ss = ss.source_set()
|
||||||
linux_user_ss = ss.source_set()
|
|
||||||
specific_ss = ss.source_set()
|
|
||||||
specific_fuzz_ss = ss.source_set()
|
|
||||||
|
|
||||||
modules = {}
|
modules = {}
|
||||||
hw_arch = {}
|
hw_arch = {}
|
||||||
|
@ -1313,8 +1332,6 @@ if enable_modules
|
||||||
modulecommon = declare_dependency(link_whole: libmodulecommon, compile_args: '-DBUILD_DSO')
|
modulecommon = declare_dependency(link_whole: libmodulecommon, compile_args: '-DBUILD_DSO')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Build targets from sourcesets
|
|
||||||
|
|
||||||
stub_ss = stub_ss.apply(config_all, strict: false)
|
stub_ss = stub_ss.apply(config_all, strict: false)
|
||||||
|
|
||||||
util_ss.add_all(trace_ss)
|
util_ss.add_all(trace_ss)
|
||||||
|
@ -1360,24 +1377,14 @@ blockdev_ss.add(files(
|
||||||
# os-win32.c does not
|
# os-win32.c does not
|
||||||
blockdev_ss.add(when: 'CONFIG_POSIX', if_true: files('os-posix.c'))
|
blockdev_ss.add(when: 'CONFIG_POSIX', if_true: files('os-posix.c'))
|
||||||
softmmu_ss.add(when: 'CONFIG_WIN32', if_true: [files('os-win32.c')])
|
softmmu_ss.add(when: 'CONFIG_WIN32', if_true: [files('os-win32.c')])
|
||||||
|
|
||||||
softmmu_ss.add_all(blockdev_ss)
|
softmmu_ss.add_all(blockdev_ss)
|
||||||
softmmu_ss.add(files(
|
|
||||||
'bootdevice.c',
|
|
||||||
'dma-helpers.c',
|
|
||||||
'qdev-monitor.c',
|
|
||||||
), sdl)
|
|
||||||
|
|
||||||
softmmu_ss.add(when: 'CONFIG_TPM', if_true: files('tpm.c'))
|
|
||||||
softmmu_ss.add(when: 'CONFIG_SECCOMP', if_true: [files('qemu-seccomp.c'), seccomp])
|
|
||||||
softmmu_ss.add(when: fdt, if_true: files('device_tree.c'))
|
|
||||||
|
|
||||||
common_ss.add(files('cpus-common.c'))
|
common_ss.add(files('cpus-common.c'))
|
||||||
|
|
||||||
subdir('softmmu')
|
subdir('softmmu')
|
||||||
|
|
||||||
common_ss.add(capstone)
|
common_ss.add(capstone)
|
||||||
specific_ss.add(files('disas.c', 'exec.c', 'gdbstub.c'), capstone, libpmem, libdaxctl)
|
specific_ss.add(files('cpu.c', 'disas.c', 'gdbstub.c'), capstone)
|
||||||
specific_ss.add(files('exec-vary.c'))
|
specific_ss.add(files('exec-vary.c'))
|
||||||
specific_ss.add(when: 'CONFIG_TCG', if_true: files(
|
specific_ss.add(when: 'CONFIG_TCG', if_true: files(
|
||||||
'fpu/softfloat.c',
|
'fpu/softfloat.c',
|
||||||
|
@ -1412,6 +1419,10 @@ specific_ss.add_all(when: 'CONFIG_LINUX_USER', if_true: linux_user_ss)
|
||||||
subdir('tests/qtest/libqos')
|
subdir('tests/qtest/libqos')
|
||||||
subdir('tests/qtest/fuzz')
|
subdir('tests/qtest/fuzz')
|
||||||
|
|
||||||
|
########################
|
||||||
|
# Library dependencies #
|
||||||
|
########################
|
||||||
|
|
||||||
block_mods = []
|
block_mods = []
|
||||||
softmmu_mods = []
|
softmmu_mods = []
|
||||||
foreach d, list : modules
|
foreach d, list : modules
|
||||||
|
@ -1446,6 +1457,47 @@ qemu_syms = custom_target('qemu.syms', output: 'qemu.syms',
|
||||||
capture: true,
|
capture: true,
|
||||||
command: [undefsym, nm, '@INPUT@'])
|
command: [undefsym, nm, '@INPUT@'])
|
||||||
|
|
||||||
|
qom_ss = qom_ss.apply(config_host, strict: false)
|
||||||
|
libqom = static_library('qom', qom_ss.sources() + genh,
|
||||||
|
dependencies: [qom_ss.dependencies()],
|
||||||
|
name_suffix: 'fa')
|
||||||
|
|
||||||
|
qom = declare_dependency(link_whole: libqom)
|
||||||
|
|
||||||
|
authz_ss = authz_ss.apply(config_host, strict: false)
|
||||||
|
libauthz = static_library('authz', authz_ss.sources() + genh,
|
||||||
|
dependencies: [authz_ss.dependencies()],
|
||||||
|
name_suffix: 'fa',
|
||||||
|
build_by_default: false)
|
||||||
|
|
||||||
|
authz = declare_dependency(link_whole: libauthz,
|
||||||
|
dependencies: qom)
|
||||||
|
|
||||||
|
crypto_ss = crypto_ss.apply(config_host, strict: false)
|
||||||
|
libcrypto = static_library('crypto', crypto_ss.sources() + genh,
|
||||||
|
dependencies: [crypto_ss.dependencies()],
|
||||||
|
name_suffix: 'fa',
|
||||||
|
build_by_default: false)
|
||||||
|
|
||||||
|
crypto = declare_dependency(link_whole: libcrypto,
|
||||||
|
dependencies: [authz, qom])
|
||||||
|
|
||||||
|
io_ss = io_ss.apply(config_host, strict: false)
|
||||||
|
libio = static_library('io', io_ss.sources() + genh,
|
||||||
|
dependencies: [io_ss.dependencies()],
|
||||||
|
link_with: libqemuutil,
|
||||||
|
name_suffix: 'fa',
|
||||||
|
build_by_default: false)
|
||||||
|
|
||||||
|
io = declare_dependency(link_whole: libio, dependencies: [crypto, qom])
|
||||||
|
|
||||||
|
libmigration = static_library('migration', sources: migration_files + genh,
|
||||||
|
name_suffix: 'fa',
|
||||||
|
build_by_default: false)
|
||||||
|
migration = declare_dependency(link_with: libmigration,
|
||||||
|
dependencies: [zlib, qom, io])
|
||||||
|
softmmu_ss.add(migration)
|
||||||
|
|
||||||
block_ss = block_ss.apply(config_host, strict: false)
|
block_ss = block_ss.apply(config_host, strict: false)
|
||||||
libblock = static_library('block', block_ss.sources() + genh,
|
libblock = static_library('block', block_ss.sources() + genh,
|
||||||
dependencies: block_ss.dependencies(),
|
dependencies: block_ss.dependencies(),
|
||||||
|
@ -1465,6 +1517,22 @@ libqmp = static_library('qmp', qmp_ss.sources() + genh,
|
||||||
|
|
||||||
qmp = declare_dependency(link_whole: [libqmp])
|
qmp = declare_dependency(link_whole: [libqmp])
|
||||||
|
|
||||||
|
libchardev = static_library('chardev', chardev_ss.sources() + genh,
|
||||||
|
name_suffix: 'fa',
|
||||||
|
build_by_default: false)
|
||||||
|
|
||||||
|
chardev = declare_dependency(link_whole: libchardev)
|
||||||
|
|
||||||
|
libhwcore = static_library('hwcore', sources: hwcore_files + genh,
|
||||||
|
name_suffix: 'fa',
|
||||||
|
build_by_default: false)
|
||||||
|
hwcore = declare_dependency(link_whole: libhwcore)
|
||||||
|
common_ss.add(hwcore)
|
||||||
|
|
||||||
|
###########
|
||||||
|
# Targets #
|
||||||
|
###########
|
||||||
|
|
||||||
foreach m : block_mods + softmmu_mods
|
foreach m : block_mods + softmmu_mods
|
||||||
shared_module(m.name(),
|
shared_module(m.name(),
|
||||||
name_prefix: '',
|
name_prefix: '',
|
||||||
|
@ -1739,6 +1807,10 @@ if host_machine.system() == 'windows'
|
||||||
alias_target('installer', nsis)
|
alias_target('installer', nsis)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
#########################
|
||||||
|
# Configuration summary #
|
||||||
|
#########################
|
||||||
|
|
||||||
summary_info = {}
|
summary_info = {}
|
||||||
summary_info += {'Install prefix': config_host['prefix']}
|
summary_info += {'Install prefix': config_host['prefix']}
|
||||||
summary_info += {'BIOS directory': config_host['qemu_datadir']}
|
summary_info += {'BIOS directory': config_host['qemu_datadir']}
|
||||||
|
|
|
@ -8,13 +8,7 @@ migration_files = files(
|
||||||
'qemu-file.c',
|
'qemu-file.c',
|
||||||
'qjson.c',
|
'qjson.c',
|
||||||
)
|
)
|
||||||
|
softmmu_ss.add(migration_files)
|
||||||
libmigration = static_library('migration', sources: migration_files + genh,
|
|
||||||
name_suffix: 'fa',
|
|
||||||
build_by_default: false)
|
|
||||||
migration = declare_dependency(link_with: libmigration,
|
|
||||||
dependencies: [zlib, qom, io])
|
|
||||||
softmmu_ss.add(migration)
|
|
||||||
|
|
||||||
softmmu_ss.add(files(
|
softmmu_ss.add(files(
|
||||||
'block-dirty-bitmap.c',
|
'block-dirty-bitmap.c',
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
qom_ss = ss.source_set()
|
|
||||||
qom_ss.add(genh)
|
qom_ss.add(genh)
|
||||||
qom_ss.add(files(
|
qom_ss.add(files(
|
||||||
'container.c',
|
'container.c',
|
||||||
|
@ -9,10 +8,3 @@ qom_ss.add(files(
|
||||||
|
|
||||||
qmp_ss.add(files('qom-qmp-cmds.c'))
|
qmp_ss.add(files('qom-qmp-cmds.c'))
|
||||||
softmmu_ss.add(files('qom-hmp-cmds.c'))
|
softmmu_ss.add(files('qom-hmp-cmds.c'))
|
||||||
|
|
||||||
qom_ss = qom_ss.apply(config_host, strict: false)
|
|
||||||
libqom = static_library('qom', qom_ss.sources() + genh,
|
|
||||||
dependencies: [qom_ss.dependencies()],
|
|
||||||
name_suffix: 'fa')
|
|
||||||
|
|
||||||
qom = declare_dependency(link_whole: libqom)
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Highlight object declarations that don't look like object class but
|
||||||
|
// accidentally inherit from it.
|
||||||
|
|
||||||
|
@match@
|
||||||
|
identifier obj_t, fld;
|
||||||
|
type parent_t =~ ".*Class$";
|
||||||
|
@@
|
||||||
|
struct obj_t {
|
||||||
|
parent_t fld;
|
||||||
|
...
|
||||||
|
};
|
||||||
|
|
||||||
|
@script:python filter depends on match@
|
||||||
|
obj_t << match.obj_t;
|
||||||
|
@@
|
||||||
|
is_class_obj = obj_t.endswith('Class')
|
||||||
|
cocci.include_match(not is_class_obj)
|
||||||
|
|
||||||
|
@replacement depends on filter@
|
||||||
|
identifier match.obj_t, match.fld;
|
||||||
|
type match.parent_t;
|
||||||
|
@@
|
||||||
|
struct obj_t {
|
||||||
|
* parent_t fld;
|
||||||
|
...
|
||||||
|
};
|
|
@ -9,7 +9,7 @@ version="$3"
|
||||||
if [ -z "$pkgversion" ]; then
|
if [ -z "$pkgversion" ]; then
|
||||||
cd "$dir"
|
cd "$dir"
|
||||||
if [ -e .git ]; then
|
if [ -e .git ]; then
|
||||||
pkgversion=$(git describe --match 'v*' --dirty | echo "")
|
pkgversion=$(git describe --match 'v*' --dirty) || :
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files(
|
||||||
'balloon.c',
|
'balloon.c',
|
||||||
'cpus.c',
|
'cpus.c',
|
||||||
'cpu-throttle.c',
|
'cpu-throttle.c',
|
||||||
|
'physmem.c',
|
||||||
'ioport.c',
|
'ioport.c',
|
||||||
'memory.c',
|
'memory.c',
|
||||||
'memory_mapping.c',
|
'memory_mapping.c',
|
||||||
|
@ -14,3 +15,13 @@ specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files(
|
||||||
specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: [files(
|
specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: [files(
|
||||||
'icount.c'
|
'icount.c'
|
||||||
)])
|
)])
|
||||||
|
|
||||||
|
softmmu_ss.add(files(
|
||||||
|
'bootdevice.c',
|
||||||
|
'dma-helpers.c',
|
||||||
|
'qdev-monitor.c',
|
||||||
|
), sdl, libpmem, libdaxctl)
|
||||||
|
|
||||||
|
softmmu_ss.add(when: 'CONFIG_TPM', if_true: files('tpm.c'))
|
||||||
|
softmmu_ss.add(when: 'CONFIG_SECCOMP', if_true: [files('qemu-seccomp.c'), seccomp])
|
||||||
|
softmmu_ss.add(when: fdt, if_true: files('device_tree.c'))
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Virtual page mapping
|
* RAM allocation and memory access
|
||||||
*
|
*
|
||||||
* Copyright (c) 2003 Fabrice Bellard
|
* Copyright (c) 2003 Fabrice Bellard
|
||||||
*
|
*
|
||||||
|
@ -28,10 +28,8 @@
|
||||||
#include "tcg/tcg.h"
|
#include "tcg/tcg.h"
|
||||||
#include "hw/qdev-core.h"
|
#include "hw/qdev-core.h"
|
||||||
#include "hw/qdev-properties.h"
|
#include "hw/qdev-properties.h"
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
|
||||||
#include "hw/boards.h"
|
#include "hw/boards.h"
|
||||||
#include "hw/xen/xen.h"
|
#include "hw/xen/xen.h"
|
||||||
#endif
|
|
||||||
#include "sysemu/kvm.h"
|
#include "sysemu/kvm.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "sysemu/tcg.h"
|
#include "sysemu/tcg.h"
|
||||||
|
@ -40,9 +38,6 @@
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "qemu/qemu-print.h"
|
#include "qemu/qemu-print.h"
|
||||||
#if defined(CONFIG_USER_ONLY)
|
|
||||||
#include "qemu.h"
|
|
||||||
#else /* !CONFIG_USER_ONLY */
|
|
||||||
#include "exec/memory.h"
|
#include "exec/memory.h"
|
||||||
#include "exec/ioport.h"
|
#include "exec/ioport.h"
|
||||||
#include "sysemu/dma.h"
|
#include "sysemu/dma.h"
|
||||||
|
@ -56,7 +51,6 @@
|
||||||
#include <linux/falloc.h>
|
#include <linux/falloc.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
|
||||||
#include "qemu/rcu_queue.h"
|
#include "qemu/rcu_queue.h"
|
||||||
#include "qemu/main-loop.h"
|
#include "qemu/main-loop.h"
|
||||||
#include "translate-all.h"
|
#include "translate-all.h"
|
||||||
|
@ -83,7 +77,6 @@
|
||||||
|
|
||||||
//#define DEBUG_SUBPAGE
|
//#define DEBUG_SUBPAGE
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
|
||||||
/* ram_list is read under rcu_read_lock()/rcu_read_unlock(). Writes
|
/* ram_list is read under rcu_read_lock()/rcu_read_unlock(). Writes
|
||||||
* are protected by the ramlist lock.
|
* are protected by the ramlist lock.
|
||||||
*/
|
*/
|
||||||
|
@ -96,12 +89,6 @@ AddressSpace address_space_io;
|
||||||
AddressSpace address_space_memory;
|
AddressSpace address_space_memory;
|
||||||
|
|
||||||
static MemoryRegion io_mem_unassigned;
|
static MemoryRegion io_mem_unassigned;
|
||||||
#endif
|
|
||||||
|
|
||||||
uintptr_t qemu_host_page_size;
|
|
||||||
intptr_t qemu_host_page_mask;
|
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
|
||||||
|
|
||||||
typedef struct PhysPageEntry PhysPageEntry;
|
typedef struct PhysPageEntry PhysPageEntry;
|
||||||
|
|
||||||
|
@ -179,10 +166,6 @@ struct DirtyBitmapSnapshot {
|
||||||
unsigned long dirty[];
|
unsigned long dirty[];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
|
||||||
|
|
||||||
static void phys_map_node_reserve(PhysPageMap *map, unsigned nodes)
|
static void phys_map_node_reserve(PhysPageMap *map, unsigned nodes)
|
||||||
{
|
{
|
||||||
static unsigned alloc_hint = 16;
|
static unsigned alloc_hint = 16;
|
||||||
|
@ -661,7 +644,7 @@ static void tcg_register_iommu_notifier(CPUState *cpu,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tcg_iommu_free_notifier_list(CPUState *cpu)
|
void tcg_iommu_free_notifier_list(CPUState *cpu)
|
||||||
{
|
{
|
||||||
/* Destroy the CPU's notifier list */
|
/* Destroy the CPU's notifier list */
|
||||||
int i;
|
int i;
|
||||||
|
@ -675,6 +658,11 @@ static void tcg_iommu_free_notifier_list(CPUState *cpu)
|
||||||
g_array_free(cpu->iommu_notifiers, true);
|
g_array_free(cpu->iommu_notifiers, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tcg_iommu_init_notifier_list(CPUState *cpu)
|
||||||
|
{
|
||||||
|
cpu->iommu_notifiers = g_array_new(false, true, sizeof(TCGIOMMUNotifier *));
|
||||||
|
}
|
||||||
|
|
||||||
/* Called from RCU critical section */
|
/* Called from RCU critical section */
|
||||||
MemoryRegionSection *
|
MemoryRegionSection *
|
||||||
address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr,
|
address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr,
|
||||||
|
@ -732,91 +720,6 @@ address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr,
|
||||||
translate_fail:
|
translate_fail:
|
||||||
return &d->map.sections[PHYS_SECTION_UNASSIGNED];
|
return &d->map.sections[PHYS_SECTION_UNASSIGNED];
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
|
||||||
|
|
||||||
static int cpu_common_post_load(void *opaque, int version_id)
|
|
||||||
{
|
|
||||||
CPUState *cpu = opaque;
|
|
||||||
|
|
||||||
/* 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the
|
|
||||||
version_id is increased. */
|
|
||||||
cpu->interrupt_request &= ~0x01;
|
|
||||||
tlb_flush(cpu);
|
|
||||||
|
|
||||||
/* loadvm has just updated the content of RAM, bypassing the
|
|
||||||
* usual mechanisms that ensure we flush TBs for writes to
|
|
||||||
* memory we've translated code from. So we must flush all TBs,
|
|
||||||
* which will now be stale.
|
|
||||||
*/
|
|
||||||
tb_flush(cpu);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cpu_common_pre_load(void *opaque)
|
|
||||||
{
|
|
||||||
CPUState *cpu = opaque;
|
|
||||||
|
|
||||||
cpu->exception_index = -1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool cpu_common_exception_index_needed(void *opaque)
|
|
||||||
{
|
|
||||||
CPUState *cpu = opaque;
|
|
||||||
|
|
||||||
return tcg_enabled() && cpu->exception_index != -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const VMStateDescription vmstate_cpu_common_exception_index = {
|
|
||||||
.name = "cpu_common/exception_index",
|
|
||||||
.version_id = 1,
|
|
||||||
.minimum_version_id = 1,
|
|
||||||
.needed = cpu_common_exception_index_needed,
|
|
||||||
.fields = (VMStateField[]) {
|
|
||||||
VMSTATE_INT32(exception_index, CPUState),
|
|
||||||
VMSTATE_END_OF_LIST()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool cpu_common_crash_occurred_needed(void *opaque)
|
|
||||||
{
|
|
||||||
CPUState *cpu = opaque;
|
|
||||||
|
|
||||||
return cpu->crash_occurred;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const VMStateDescription vmstate_cpu_common_crash_occurred = {
|
|
||||||
.name = "cpu_common/crash_occurred",
|
|
||||||
.version_id = 1,
|
|
||||||
.minimum_version_id = 1,
|
|
||||||
.needed = cpu_common_crash_occurred_needed,
|
|
||||||
.fields = (VMStateField[]) {
|
|
||||||
VMSTATE_BOOL(crash_occurred, CPUState),
|
|
||||||
VMSTATE_END_OF_LIST()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const VMStateDescription vmstate_cpu_common = {
|
|
||||||
.name = "cpu_common",
|
|
||||||
.version_id = 1,
|
|
||||||
.minimum_version_id = 1,
|
|
||||||
.pre_load = cpu_common_pre_load,
|
|
||||||
.post_load = cpu_common_post_load,
|
|
||||||
.fields = (VMStateField[]) {
|
|
||||||
VMSTATE_UINT32(halted, CPUState),
|
|
||||||
VMSTATE_UINT32(interrupt_request, CPUState),
|
|
||||||
VMSTATE_END_OF_LIST()
|
|
||||||
},
|
|
||||||
.subsections = (const VMStateDescription*[]) {
|
|
||||||
&vmstate_cpu_common_exception_index,
|
|
||||||
&vmstate_cpu_common_crash_occurred,
|
|
||||||
NULL
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void cpu_address_space_init(CPUState *cpu, int asidx,
|
void cpu_address_space_init(CPUState *cpu, int asidx,
|
||||||
const char *prefix, MemoryRegion *mr)
|
const char *prefix, MemoryRegion *mr)
|
||||||
|
@ -860,155 +763,7 @@ AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx)
|
||||||
/* Return the AddressSpace corresponding to the specified index */
|
/* Return the AddressSpace corresponding to the specified index */
|
||||||
return cpu->cpu_ases[asidx].as;
|
return cpu->cpu_ases[asidx].as;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
void cpu_exec_unrealizefn(CPUState *cpu)
|
|
||||||
{
|
|
||||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
|
||||||
|
|
||||||
tlb_destroy(cpu);
|
|
||||||
cpu_list_remove(cpu);
|
|
||||||
|
|
||||||
if (cc->vmsd != NULL) {
|
|
||||||
vmstate_unregister(NULL, cc->vmsd, cpu);
|
|
||||||
}
|
|
||||||
if (qdev_get_vmsd(DEVICE(cpu)) == NULL) {
|
|
||||||
vmstate_unregister(NULL, &vmstate_cpu_common, cpu);
|
|
||||||
}
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
|
||||||
tcg_iommu_free_notifier_list(cpu);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
Property cpu_common_props[] = {
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
|
||||||
/* Create a memory property for softmmu CPU object,
|
|
||||||
* so users can wire up its memory. (This can't go in hw/core/cpu.c
|
|
||||||
* because that file is compiled only once for both user-mode
|
|
||||||
* and system builds.) The default if no link is set up is to use
|
|
||||||
* the system address space.
|
|
||||||
*/
|
|
||||||
DEFINE_PROP_LINK("memory", CPUState, memory, TYPE_MEMORY_REGION,
|
|
||||||
MemoryRegion *),
|
|
||||||
#endif
|
|
||||||
DEFINE_PROP_BOOL("start-powered-off", CPUState, start_powered_off, false),
|
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
|
||||||
};
|
|
||||||
|
|
||||||
void cpu_exec_initfn(CPUState *cpu)
|
|
||||||
{
|
|
||||||
cpu->as = NULL;
|
|
||||||
cpu->num_ases = 0;
|
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
|
||||||
cpu->thread_id = qemu_get_thread_id();
|
|
||||||
cpu->memory = system_memory;
|
|
||||||
object_ref(OBJECT(cpu->memory));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void cpu_exec_realizefn(CPUState *cpu, Error **errp)
|
|
||||||
{
|
|
||||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
|
||||||
static bool tcg_target_initialized;
|
|
||||||
|
|
||||||
cpu_list_add(cpu);
|
|
||||||
|
|
||||||
if (tcg_enabled() && !tcg_target_initialized) {
|
|
||||||
tcg_target_initialized = true;
|
|
||||||
cc->tcg_initialize();
|
|
||||||
}
|
|
||||||
tlb_init(cpu);
|
|
||||||
|
|
||||||
qemu_plugin_vcpu_init_hook(cpu);
|
|
||||||
|
|
||||||
#ifdef CONFIG_USER_ONLY
|
|
||||||
assert(cc->vmsd == NULL);
|
|
||||||
#else /* !CONFIG_USER_ONLY */
|
|
||||||
if (qdev_get_vmsd(DEVICE(cpu)) == NULL) {
|
|
||||||
vmstate_register(NULL, cpu->cpu_index, &vmstate_cpu_common, cpu);
|
|
||||||
}
|
|
||||||
if (cc->vmsd != NULL) {
|
|
||||||
vmstate_register(NULL, cpu->cpu_index, cc->vmsd, cpu);
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu->iommu_notifiers = g_array_new(false, true, sizeof(TCGIOMMUNotifier *));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *parse_cpu_option(const char *cpu_option)
|
|
||||||
{
|
|
||||||
ObjectClass *oc;
|
|
||||||
CPUClass *cc;
|
|
||||||
gchar **model_pieces;
|
|
||||||
const char *cpu_type;
|
|
||||||
|
|
||||||
model_pieces = g_strsplit(cpu_option, ",", 2);
|
|
||||||
if (!model_pieces[0]) {
|
|
||||||
error_report("-cpu option cannot be empty");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
oc = cpu_class_by_name(CPU_RESOLVING_TYPE, model_pieces[0]);
|
|
||||||
if (oc == NULL) {
|
|
||||||
error_report("unable to find CPU model '%s'", model_pieces[0]);
|
|
||||||
g_strfreev(model_pieces);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu_type = object_class_get_name(oc);
|
|
||||||
cc = CPU_CLASS(oc);
|
|
||||||
cc->parse_features(cpu_type, model_pieces[1], &error_fatal);
|
|
||||||
g_strfreev(model_pieces);
|
|
||||||
return cpu_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(CONFIG_USER_ONLY)
|
|
||||||
void tb_invalidate_phys_addr(target_ulong addr)
|
|
||||||
{
|
|
||||||
mmap_lock();
|
|
||||||
tb_invalidate_phys_page_range(addr, addr + 1);
|
|
||||||
mmap_unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void breakpoint_invalidate(CPUState *cpu, target_ulong pc)
|
|
||||||
{
|
|
||||||
tb_invalidate_phys_addr(pc);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs)
|
|
||||||
{
|
|
||||||
ram_addr_t ram_addr;
|
|
||||||
MemoryRegion *mr;
|
|
||||||
hwaddr l = 1;
|
|
||||||
|
|
||||||
if (!tcg_enabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RCU_READ_LOCK_GUARD();
|
|
||||||
mr = address_space_translate(as, addr, &addr, &l, false, attrs);
|
|
||||||
if (!(memory_region_is_ram(mr)
|
|
||||||
|| memory_region_is_romd(mr))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ram_addr = memory_region_get_ram_addr(mr) + addr;
|
|
||||||
tb_invalidate_phys_page_range(ram_addr, ram_addr + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void breakpoint_invalidate(CPUState *cpu, target_ulong pc)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* There may not be a virtual to physical translation for the pc
|
|
||||||
* right now, but there may exist cached TB for this pc.
|
|
||||||
* Flush the whole TB cache to force re-translation of such TBs.
|
|
||||||
* This is heavyweight, but we're debugging anyway.
|
|
||||||
*/
|
|
||||||
tb_flush(cpu);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
|
||||||
/* Add a watchpoint. */
|
/* Add a watchpoint. */
|
||||||
int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len,
|
int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len,
|
||||||
int flags, CPUWatchpoint **watchpoint)
|
int flags, CPUWatchpoint **watchpoint)
|
||||||
|
@ -1117,123 +872,7 @@ int cpu_watchpoint_address_matches(CPUState *cpu, vaddr addr, vaddr len)
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
#endif /* !CONFIG_USER_ONLY */
|
|
||||||
|
|
||||||
/* Add a breakpoint. */
|
|
||||||
int cpu_breakpoint_insert(CPUState *cpu, vaddr pc, int flags,
|
|
||||||
CPUBreakpoint **breakpoint)
|
|
||||||
{
|
|
||||||
CPUBreakpoint *bp;
|
|
||||||
|
|
||||||
bp = g_malloc(sizeof(*bp));
|
|
||||||
|
|
||||||
bp->pc = pc;
|
|
||||||
bp->flags = flags;
|
|
||||||
|
|
||||||
/* keep all GDB-injected breakpoints in front */
|
|
||||||
if (flags & BP_GDB) {
|
|
||||||
QTAILQ_INSERT_HEAD(&cpu->breakpoints, bp, entry);
|
|
||||||
} else {
|
|
||||||
QTAILQ_INSERT_TAIL(&cpu->breakpoints, bp, entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
breakpoint_invalidate(cpu, pc);
|
|
||||||
|
|
||||||
if (breakpoint) {
|
|
||||||
*breakpoint = bp;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove a specific breakpoint. */
|
|
||||||
int cpu_breakpoint_remove(CPUState *cpu, vaddr pc, int flags)
|
|
||||||
{
|
|
||||||
CPUBreakpoint *bp;
|
|
||||||
|
|
||||||
QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
|
|
||||||
if (bp->pc == pc && bp->flags == flags) {
|
|
||||||
cpu_breakpoint_remove_by_ref(cpu, bp);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -ENOENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove a specific breakpoint by reference. */
|
|
||||||
void cpu_breakpoint_remove_by_ref(CPUState *cpu, CPUBreakpoint *breakpoint)
|
|
||||||
{
|
|
||||||
QTAILQ_REMOVE(&cpu->breakpoints, breakpoint, entry);
|
|
||||||
|
|
||||||
breakpoint_invalidate(cpu, breakpoint->pc);
|
|
||||||
|
|
||||||
g_free(breakpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove all matching breakpoints. */
|
|
||||||
void cpu_breakpoint_remove_all(CPUState *cpu, int mask)
|
|
||||||
{
|
|
||||||
CPUBreakpoint *bp, *next;
|
|
||||||
|
|
||||||
QTAILQ_FOREACH_SAFE(bp, &cpu->breakpoints, entry, next) {
|
|
||||||
if (bp->flags & mask) {
|
|
||||||
cpu_breakpoint_remove_by_ref(cpu, bp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* enable or disable single step mode. EXCP_DEBUG is returned by the
|
|
||||||
CPU loop after each instruction */
|
|
||||||
void cpu_single_step(CPUState *cpu, int enabled)
|
|
||||||
{
|
|
||||||
if (cpu->singlestep_enabled != enabled) {
|
|
||||||
cpu->singlestep_enabled = enabled;
|
|
||||||
if (kvm_enabled()) {
|
|
||||||
kvm_update_guest_debug(cpu, 0);
|
|
||||||
} else {
|
|
||||||
/* must flush all the translated code to avoid inconsistencies */
|
|
||||||
/* XXX: only flush what is necessary */
|
|
||||||
tb_flush(cpu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void cpu_abort(CPUState *cpu, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_list ap2;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
va_copy(ap2, ap);
|
|
||||||
fprintf(stderr, "qemu: fatal: ");
|
|
||||||
vfprintf(stderr, fmt, ap);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
cpu_dump_state(cpu, stderr, CPU_DUMP_FPU | CPU_DUMP_CCOP);
|
|
||||||
if (qemu_log_separate()) {
|
|
||||||
FILE *logfile = qemu_log_lock();
|
|
||||||
qemu_log("qemu: fatal: ");
|
|
||||||
qemu_log_vprintf(fmt, ap2);
|
|
||||||
qemu_log("\n");
|
|
||||||
log_cpu_state(cpu, CPU_DUMP_FPU | CPU_DUMP_CCOP);
|
|
||||||
qemu_log_flush();
|
|
||||||
qemu_log_unlock(logfile);
|
|
||||||
qemu_log_close();
|
|
||||||
}
|
|
||||||
va_end(ap2);
|
|
||||||
va_end(ap);
|
|
||||||
replay_finish();
|
|
||||||
#if defined(CONFIG_USER_ONLY)
|
|
||||||
{
|
|
||||||
struct sigaction act;
|
|
||||||
sigfillset(&act.sa_mask);
|
|
||||||
act.sa_handler = SIG_DFL;
|
|
||||||
act.sa_flags = 0;
|
|
||||||
sigaction(SIGABRT, &act, NULL);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
|
||||||
/* Called from RCU critical section */
|
/* Called from RCU critical section */
|
||||||
static RAMBlock *qemu_get_ram_block(ram_addr_t addr)
|
static RAMBlock *qemu_get_ram_block(ram_addr_t addr)
|
||||||
{
|
{
|
||||||
|
@ -1420,9 +1059,6 @@ hwaddr memory_region_section_get_iotlb(CPUState *cpu,
|
||||||
AddressSpaceDispatch *d = flatview_to_dispatch(section->fv);
|
AddressSpaceDispatch *d = flatview_to_dispatch(section->fv);
|
||||||
return section - d->map.sections;
|
return section - d->map.sections;
|
||||||
}
|
}
|
||||||
#endif /* defined(CONFIG_USER_ONLY) */
|
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
|
||||||
|
|
||||||
static int subpage_register(subpage_t *mmio, uint32_t start, uint32_t end,
|
static int subpage_register(subpage_t *mmio, uint32_t start, uint32_t end,
|
||||||
uint16_t section);
|
uint16_t section);
|
||||||
|
@ -3023,52 +2659,6 @@ MemoryRegion *get_system_io(void)
|
||||||
return system_io;
|
return system_io;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* !defined(CONFIG_USER_ONLY) */
|
|
||||||
|
|
||||||
/* physical memory access (slow version, mainly for debug) */
|
|
||||||
#if defined(CONFIG_USER_ONLY)
|
|
||||||
int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr,
|
|
||||||
void *ptr, target_ulong len, bool is_write)
|
|
||||||
{
|
|
||||||
int flags;
|
|
||||||
target_ulong l, page;
|
|
||||||
void * p;
|
|
||||||
uint8_t *buf = ptr;
|
|
||||||
|
|
||||||
while (len > 0) {
|
|
||||||
page = addr & TARGET_PAGE_MASK;
|
|
||||||
l = (page + TARGET_PAGE_SIZE) - addr;
|
|
||||||
if (l > len)
|
|
||||||
l = len;
|
|
||||||
flags = page_get_flags(page);
|
|
||||||
if (!(flags & PAGE_VALID))
|
|
||||||
return -1;
|
|
||||||
if (is_write) {
|
|
||||||
if (!(flags & PAGE_WRITE))
|
|
||||||
return -1;
|
|
||||||
/* XXX: this code should not depend on lock_user */
|
|
||||||
if (!(p = lock_user(VERIFY_WRITE, addr, l, 0)))
|
|
||||||
return -1;
|
|
||||||
memcpy(p, buf, l);
|
|
||||||
unlock_user(p, addr, l);
|
|
||||||
} else {
|
|
||||||
if (!(flags & PAGE_READ))
|
|
||||||
return -1;
|
|
||||||
/* XXX: this code should not depend on lock_user */
|
|
||||||
if (!(p = lock_user(VERIFY_READ, addr, l, 1)))
|
|
||||||
return -1;
|
|
||||||
memcpy(buf, p, l);
|
|
||||||
unlock_user(p, addr, 0);
|
|
||||||
}
|
|
||||||
len -= l;
|
|
||||||
buf += l;
|
|
||||||
addr += l;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr,
|
static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr,
|
||||||
hwaddr length)
|
hwaddr length)
|
||||||
{
|
{
|
||||||
|
@ -3857,18 +3447,7 @@ int qemu_target_page_bits_min(void)
|
||||||
{
|
{
|
||||||
return TARGET_PAGE_BITS_MIN;
|
return TARGET_PAGE_BITS_MIN;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
bool target_words_bigendian(void)
|
|
||||||
{
|
|
||||||
#if defined(TARGET_WORDS_BIGENDIAN)
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
|
||||||
bool cpu_physical_memory_is_io(hwaddr phys_addr)
|
bool cpu_physical_memory_is_io(hwaddr phys_addr)
|
||||||
{
|
{
|
||||||
MemoryRegion*mr;
|
MemoryRegion*mr;
|
||||||
|
@ -3998,23 +3577,6 @@ bool ramblock_is_pmem(RAMBlock *rb)
|
||||||
return rb->flags & RAM_PMEM;
|
return rb->flags & RAM_PMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void page_size_init(void)
|
|
||||||
{
|
|
||||||
/* NOTE: we can always suppose that qemu_host_page_size >=
|
|
||||||
TARGET_PAGE_SIZE */
|
|
||||||
if (qemu_host_page_size == 0) {
|
|
||||||
qemu_host_page_size = qemu_real_host_page_size;
|
|
||||||
}
|
|
||||||
if (qemu_host_page_size < TARGET_PAGE_SIZE) {
|
|
||||||
qemu_host_page_size = TARGET_PAGE_SIZE;
|
|
||||||
}
|
|
||||||
qemu_host_page_mask = -(intptr_t)qemu_host_page_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
|
||||||
|
|
||||||
static void mtree_print_phys_entries(int start, int end, int skip, int ptr)
|
static void mtree_print_phys_entries(int start, int end, int skip, int ptr)
|
||||||
{
|
{
|
||||||
if (start == end - 1) {
|
if (start == end - 1) {
|
||||||
|
@ -4147,5 +3709,3 @@ bool ram_block_discard_is_required(void)
|
||||||
{
|
{
|
||||||
return qatomic_read(&ram_block_discard_disabled) < 0;
|
return qatomic_read(&ram_block_discard_disabled) < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
|
@ -803,6 +803,18 @@ void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dev = qdev_device_add(opts, errp);
|
dev = qdev_device_add(opts, errp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Drain all pending RCU callbacks. This is done because
|
||||||
|
* some bus related operations can delay a device removal
|
||||||
|
* (in this case this can happen if device is added and then
|
||||||
|
* removed due to a configuration error)
|
||||||
|
* to a RCU callback, but user might expect that this interface
|
||||||
|
* will finish its job completely once qmp command returns result
|
||||||
|
* to the user
|
||||||
|
*/
|
||||||
|
drain_call_rcu();
|
||||||
|
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
return;
|
return;
|
|
@ -49,92 +49,139 @@ static void *qtest_server_send_opaque;
|
||||||
#define FMT_timeval "%ld.%06ld"
|
#define FMT_timeval "%ld.%06ld"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* QTest Protocol
|
* DOC: QTest Protocol
|
||||||
*
|
*
|
||||||
* Line based protocol, request/response based. Server can send async messages
|
* Line based protocol, request/response based. Server can send async messages
|
||||||
* so clients should always handle many async messages before the response
|
* so clients should always handle many async messages before the response
|
||||||
* comes in.
|
* comes in.
|
||||||
*
|
*
|
||||||
* Valid requests
|
* Valid requests
|
||||||
|
* ^^^^^^^^^^^^^^
|
||||||
*
|
*
|
||||||
* Clock management:
|
* Clock management:
|
||||||
|
* """""""""""""""""
|
||||||
*
|
*
|
||||||
* The qtest client is completely in charge of the QEMU_CLOCK_VIRTUAL. qtest commands
|
* The qtest client is completely in charge of the QEMU_CLOCK_VIRTUAL. qtest commands
|
||||||
* let you adjust the value of the clock (monotonically). All the commands
|
* let you adjust the value of the clock (monotonically). All the commands
|
||||||
* return the current value of the clock in nanoseconds.
|
* return the current value of the clock in nanoseconds.
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > clock_step
|
* > clock_step
|
||||||
* < OK VALUE
|
* < OK VALUE
|
||||||
*
|
*
|
||||||
* Advance the clock to the next deadline. Useful when waiting for
|
* Advance the clock to the next deadline. Useful when waiting for
|
||||||
* asynchronous events.
|
* asynchronous events.
|
||||||
|
*
|
||||||
|
* .. code-block:: none
|
||||||
*
|
*
|
||||||
* > clock_step NS
|
* > clock_step NS
|
||||||
* < OK VALUE
|
* < OK VALUE
|
||||||
*
|
*
|
||||||
* Advance the clock by NS nanoseconds.
|
* Advance the clock by NS nanoseconds.
|
||||||
|
*
|
||||||
|
* .. code-block:: none
|
||||||
*
|
*
|
||||||
* > clock_set NS
|
* > clock_set NS
|
||||||
* < OK VALUE
|
* < OK VALUE
|
||||||
*
|
*
|
||||||
* Advance the clock to NS nanoseconds (do nothing if it's already past).
|
* Advance the clock to NS nanoseconds (do nothing if it's already past).
|
||||||
*
|
*
|
||||||
* PIO and memory access:
|
* PIO and memory access:
|
||||||
|
* """"""""""""""""""""""
|
||||||
|
*
|
||||||
|
* .. code-block:: none
|
||||||
*
|
*
|
||||||
* > outb ADDR VALUE
|
* > outb ADDR VALUE
|
||||||
* < OK
|
* < OK
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > outw ADDR VALUE
|
* > outw ADDR VALUE
|
||||||
* < OK
|
* < OK
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > outl ADDR VALUE
|
* > outl ADDR VALUE
|
||||||
* < OK
|
* < OK
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > inb ADDR
|
* > inb ADDR
|
||||||
* < OK VALUE
|
* < OK VALUE
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > inw ADDR
|
* > inw ADDR
|
||||||
* < OK VALUE
|
* < OK VALUE
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > inl ADDR
|
* > inl ADDR
|
||||||
* < OK VALUE
|
* < OK VALUE
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > writeb ADDR VALUE
|
* > writeb ADDR VALUE
|
||||||
* < OK
|
* < OK
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > writew ADDR VALUE
|
* > writew ADDR VALUE
|
||||||
* < OK
|
* < OK
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > writel ADDR VALUE
|
* > writel ADDR VALUE
|
||||||
* < OK
|
* < OK
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > writeq ADDR VALUE
|
* > writeq ADDR VALUE
|
||||||
* < OK
|
* < OK
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > readb ADDR
|
* > readb ADDR
|
||||||
* < OK VALUE
|
* < OK VALUE
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > readw ADDR
|
* > readw ADDR
|
||||||
* < OK VALUE
|
* < OK VALUE
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > readl ADDR
|
* > readl ADDR
|
||||||
* < OK VALUE
|
* < OK VALUE
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > readq ADDR
|
* > readq ADDR
|
||||||
* < OK VALUE
|
* < OK VALUE
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > read ADDR SIZE
|
* > read ADDR SIZE
|
||||||
* < OK DATA
|
* < OK DATA
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > write ADDR SIZE DATA
|
* > write ADDR SIZE DATA
|
||||||
* < OK
|
* < OK
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > b64read ADDR SIZE
|
* > b64read ADDR SIZE
|
||||||
* < OK B64_DATA
|
* < OK B64_DATA
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > b64write ADDR SIZE B64_DATA
|
* > b64write ADDR SIZE B64_DATA
|
||||||
* < OK
|
* < OK
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > memset ADDR SIZE VALUE
|
* > memset ADDR SIZE VALUE
|
||||||
* < OK
|
* < OK
|
||||||
*
|
*
|
||||||
|
@ -149,16 +196,21 @@ static void *qtest_server_send_opaque;
|
||||||
* If the sizes do not match, the data will be truncated.
|
* If the sizes do not match, the data will be truncated.
|
||||||
*
|
*
|
||||||
* IRQ management:
|
* IRQ management:
|
||||||
|
* """""""""""""""
|
||||||
|
*
|
||||||
|
* .. code-block:: none
|
||||||
*
|
*
|
||||||
* > irq_intercept_in QOM-PATH
|
* > irq_intercept_in QOM-PATH
|
||||||
* < OK
|
* < OK
|
||||||
*
|
*
|
||||||
|
* .. code-block:: none
|
||||||
|
*
|
||||||
* > irq_intercept_out QOM-PATH
|
* > irq_intercept_out QOM-PATH
|
||||||
* < OK
|
* < OK
|
||||||
*
|
*
|
||||||
* Attach to the gpio-in (resp. gpio-out) pins exported by the device at
|
* Attach to the gpio-in (resp. gpio-out) pins exported by the device at
|
||||||
* QOM-PATH. When the pin is triggered, one of the following async messages
|
* QOM-PATH. When the pin is triggered, one of the following async messages
|
||||||
* will be printed to the qtest stream:
|
* will be printed to the qtest stream::
|
||||||
*
|
*
|
||||||
* IRQ raise NUM
|
* IRQ raise NUM
|
||||||
* IRQ lower NUM
|
* IRQ lower NUM
|
||||||
|
@ -168,12 +220,15 @@ static void *qtest_server_send_opaque;
|
||||||
* NUM=0 even though it is remapped to GSI 2).
|
* NUM=0 even though it is remapped to GSI 2).
|
||||||
*
|
*
|
||||||
* Setting interrupt level:
|
* Setting interrupt level:
|
||||||
|
* """"""""""""""""""""""""
|
||||||
|
*
|
||||||
|
* .. code-block:: none
|
||||||
*
|
*
|
||||||
* > set_irq_in QOM-PATH NAME NUM LEVEL
|
* > set_irq_in QOM-PATH NAME NUM LEVEL
|
||||||
* < OK
|
* < OK
|
||||||
*
|
*
|
||||||
* where NAME is the name of the irq/gpio list, NUM is an IRQ number and
|
* where NAME is the name of the irq/gpio list, NUM is an IRQ number and
|
||||||
* LEVEL is an signed integer IRQ level.
|
* LEVEL is an signed integer IRQ level.
|
||||||
*
|
*
|
||||||
* Forcibly set the given interrupt pin to the given level.
|
* Forcibly set the given interrupt pin to the given level.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,157 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# Test automatic deletion of BDSes created by -drive/drive_add
|
|
||||||
#
|
|
||||||
# Copyright (C) 2013 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program 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 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/>.
|
|
||||||
#
|
|
||||||
|
|
||||||
# creator
|
|
||||||
owner=kwolf@redhat.com
|
|
||||||
|
|
||||||
seq=`basename $0`
|
|
||||||
echo "QA output created by $seq"
|
|
||||||
|
|
||||||
status=1 # failure is the default!
|
|
||||||
|
|
||||||
# get standard environment, filters and checks
|
|
||||||
. ./common.rc
|
|
||||||
. ./common.filter
|
|
||||||
|
|
||||||
_supported_fmt qcow2
|
|
||||||
_supported_proto file
|
|
||||||
# Because anything other than 16 would change the output of query-block,
|
|
||||||
# and external data files would change the output of
|
|
||||||
# query-named-block-nodes
|
|
||||||
_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file
|
|
||||||
|
|
||||||
do_run_qemu()
|
|
||||||
{
|
|
||||||
echo Testing: "$@"
|
|
||||||
$QEMU -nographic -qmp-pretty stdio -serial none "$@"
|
|
||||||
echo
|
|
||||||
}
|
|
||||||
|
|
||||||
# Remove QMP events from (pretty-printed) output. Doesn't handle
|
|
||||||
# nested dicts correctly, but we don't get any of those in this test.
|
|
||||||
_filter_qmp_events()
|
|
||||||
{
|
|
||||||
tr '\n' '\t' | sed -e \
|
|
||||||
's/{\s*"timestamp":\s*{[^}]*},\s*"event":[^,}]*\(,\s*"data":\s*{[^}]*}\)\?\s*}\s*//g' \
|
|
||||||
| tr '\t' '\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
run_qemu()
|
|
||||||
{
|
|
||||||
do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | _filter_qemu \
|
|
||||||
| _filter_actual_image_size \
|
|
||||||
| _filter_generated_node_ids | _filter_qmp_events \
|
|
||||||
| _filter_img_info
|
|
||||||
}
|
|
||||||
|
|
||||||
size=128M
|
|
||||||
|
|
||||||
_make_test_img $size
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo === -drive/-device and device_del ===
|
|
||||||
echo
|
|
||||||
|
|
||||||
run_qemu -drive file=$TEST_IMG,format=$IMGFMT,if=none,id=disk -device virtio-blk,drive=disk,id=virtio0 <<EOF
|
|
||||||
{ "execute": "qmp_capabilities" }
|
|
||||||
{ "execute": "query-block" }
|
|
||||||
{ "execute": "device_del", "arguments": { "id": "virtio0" } }
|
|
||||||
{ "execute": "system_reset" }
|
|
||||||
{ "execute": "query-block" }
|
|
||||||
{ "execute": "quit" }
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo === -drive/device_add and device_del ===
|
|
||||||
echo
|
|
||||||
|
|
||||||
run_qemu -drive file=$TEST_IMG,format=$IMGFMT,if=none,id=disk <<EOF
|
|
||||||
{ "execute": "qmp_capabilities" }
|
|
||||||
{ "execute": "query-block" }
|
|
||||||
{ "execute": "device_add",
|
|
||||||
"arguments": { "driver": "virtio-blk", "drive": "disk",
|
|
||||||
"id": "virtio0" } }
|
|
||||||
{ "execute": "device_del", "arguments": { "id": "virtio0" } }
|
|
||||||
{ "execute": "system_reset" }
|
|
||||||
{ "execute": "query-block" }
|
|
||||||
{ "execute": "quit" }
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo === drive_add/device_add and device_del ===
|
|
||||||
echo
|
|
||||||
|
|
||||||
run_qemu <<EOF
|
|
||||||
{ "execute": "qmp_capabilities" }
|
|
||||||
{ "execute": "human-monitor-command",
|
|
||||||
"arguments": { "command-line": "drive_add 0 file=$TEST_IMG,format=$IMGFMT,if=none,id=disk" } }
|
|
||||||
{ "execute": "query-block" }
|
|
||||||
{ "execute": "device_add",
|
|
||||||
"arguments": { "driver": "virtio-blk", "drive": "disk",
|
|
||||||
"id": "virtio0" } }
|
|
||||||
{ "execute": "device_del", "arguments": { "id": "virtio0" } }
|
|
||||||
{ "execute": "system_reset" }
|
|
||||||
{ "execute": "query-block" }
|
|
||||||
{ "execute": "quit" }
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo === blockdev_add/device_add and device_del ===
|
|
||||||
echo
|
|
||||||
|
|
||||||
run_qemu <<EOF
|
|
||||||
{ "execute": "qmp_capabilities" }
|
|
||||||
{ "execute": "blockdev-add",
|
|
||||||
"arguments": {
|
|
||||||
"driver": "$IMGFMT",
|
|
||||||
"node-name": "disk",
|
|
||||||
"file": {
|
|
||||||
"driver": "file",
|
|
||||||
"filename": "$TEST_IMG"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{ "execute": "query-named-block-nodes" }
|
|
||||||
{ "execute": "device_add",
|
|
||||||
"arguments": { "driver": "virtio-blk", "drive": "disk",
|
|
||||||
"id": "virtio0" } }
|
|
||||||
{ "execute": "device_del", "arguments": { "id": "virtio0" } }
|
|
||||||
{ "execute": "system_reset" }
|
|
||||||
{ "execute": "query-named-block-nodes" }
|
|
||||||
{ "execute": "quit" }
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo === Empty drive with -device and device_del ===
|
|
||||||
echo
|
|
||||||
|
|
||||||
run_qemu -device virtio-scsi -device scsi-cd,id=cd0 <<EOF
|
|
||||||
{ "execute": "qmp_capabilities" }
|
|
||||||
{ "execute": "query-block" }
|
|
||||||
{ "execute": "device_del", "arguments": { "id": "cd0" } }
|
|
||||||
{ "execute": "system_reset" }
|
|
||||||
{ "execute": "query-block" }
|
|
||||||
{ "execute": "quit" }
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# success, all done
|
|
||||||
echo "*** done"
|
|
||||||
rm -f $seq.full
|
|
||||||
status=0
|
|
|
@ -1,414 +0,0 @@
|
||||||
QA output created by 067
|
|
||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
|
||||||
|
|
||||||
=== -drive/-device and device_del ===
|
|
||||||
|
|
||||||
Testing: -drive file=TEST_DIR/t.IMGFMT,format=IMGFMT,if=none,id=disk -device virtio-blk,drive=disk,id=virtio0
|
|
||||||
{
|
|
||||||
QMP_VERSION
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": [
|
|
||||||
{
|
|
||||||
"io-status": "ok",
|
|
||||||
"device": "disk",
|
|
||||||
"locked": false,
|
|
||||||
"removable": false,
|
|
||||||
"inserted": {
|
|
||||||
"iops_rd": 0,
|
|
||||||
"detect_zeroes": "off",
|
|
||||||
"image": {
|
|
||||||
"virtual-size": 134217728,
|
|
||||||
"filename": "TEST_DIR/t.IMGFMT",
|
|
||||||
"cluster-size": 65536,
|
|
||||||
"format": "IMGFMT",
|
|
||||||
"actual-size": SIZE,
|
|
||||||
"dirty-flag": false
|
|
||||||
},
|
|
||||||
"iops_wr": 0,
|
|
||||||
"ro": false,
|
|
||||||
"node-name": "NODE_NAME",
|
|
||||||
"backing_file_depth": 0,
|
|
||||||
"drv": "IMGFMT",
|
|
||||||
"iops": 0,
|
|
||||||
"bps_wr": 0,
|
|
||||||
"write_threshold": 0,
|
|
||||||
"encrypted": false,
|
|
||||||
"bps": 0,
|
|
||||||
"bps_rd": 0,
|
|
||||||
"cache": {
|
|
||||||
"no-flush": false,
|
|
||||||
"direct": false,
|
|
||||||
"writeback": true
|
|
||||||
},
|
|
||||||
"file": "TEST_DIR/t.IMGFMT",
|
|
||||||
"encryption_key_missing": false
|
|
||||||
},
|
|
||||||
"qdev": "/machine/peripheral/virtio0/virtio-backend",
|
|
||||||
"type": "unknown"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": [
|
|
||||||
]
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
=== -drive/device_add and device_del ===
|
|
||||||
|
|
||||||
Testing: -drive file=TEST_DIR/t.IMGFMT,format=IMGFMT,if=none,id=disk
|
|
||||||
{
|
|
||||||
QMP_VERSION
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": [
|
|
||||||
{
|
|
||||||
"device": "disk",
|
|
||||||
"locked": false,
|
|
||||||
"removable": true,
|
|
||||||
"inserted": {
|
|
||||||
"iops_rd": 0,
|
|
||||||
"detect_zeroes": "off",
|
|
||||||
"image": {
|
|
||||||
"virtual-size": 134217728,
|
|
||||||
"filename": "TEST_DIR/t.IMGFMT",
|
|
||||||
"cluster-size": 65536,
|
|
||||||
"format": "IMGFMT",
|
|
||||||
"actual-size": SIZE,
|
|
||||||
"dirty-flag": false
|
|
||||||
},
|
|
||||||
"iops_wr": 0,
|
|
||||||
"ro": false,
|
|
||||||
"node-name": "NODE_NAME",
|
|
||||||
"backing_file_depth": 0,
|
|
||||||
"drv": "IMGFMT",
|
|
||||||
"iops": 0,
|
|
||||||
"bps_wr": 0,
|
|
||||||
"write_threshold": 0,
|
|
||||||
"encrypted": false,
|
|
||||||
"bps": 0,
|
|
||||||
"bps_rd": 0,
|
|
||||||
"cache": {
|
|
||||||
"no-flush": false,
|
|
||||||
"direct": false,
|
|
||||||
"writeback": true
|
|
||||||
},
|
|
||||||
"file": "TEST_DIR/t.IMGFMT",
|
|
||||||
"encryption_key_missing": false
|
|
||||||
},
|
|
||||||
"type": "unknown"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": [
|
|
||||||
]
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
=== drive_add/device_add and device_del ===
|
|
||||||
|
|
||||||
Testing:
|
|
||||||
{
|
|
||||||
QMP_VERSION
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": "OK\r\n"
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": [
|
|
||||||
{
|
|
||||||
"device": "disk",
|
|
||||||
"locked": false,
|
|
||||||
"removable": true,
|
|
||||||
"inserted": {
|
|
||||||
"iops_rd": 0,
|
|
||||||
"detect_zeroes": "off",
|
|
||||||
"image": {
|
|
||||||
"virtual-size": 134217728,
|
|
||||||
"filename": "TEST_DIR/t.IMGFMT",
|
|
||||||
"cluster-size": 65536,
|
|
||||||
"format": "IMGFMT",
|
|
||||||
"actual-size": SIZE,
|
|
||||||
"dirty-flag": false
|
|
||||||
},
|
|
||||||
"iops_wr": 0,
|
|
||||||
"ro": false,
|
|
||||||
"node-name": "NODE_NAME",
|
|
||||||
"backing_file_depth": 0,
|
|
||||||
"drv": "IMGFMT",
|
|
||||||
"iops": 0,
|
|
||||||
"bps_wr": 0,
|
|
||||||
"write_threshold": 0,
|
|
||||||
"encrypted": false,
|
|
||||||
"bps": 0,
|
|
||||||
"bps_rd": 0,
|
|
||||||
"cache": {
|
|
||||||
"no-flush": false,
|
|
||||||
"direct": false,
|
|
||||||
"writeback": true
|
|
||||||
},
|
|
||||||
"file": "TEST_DIR/t.IMGFMT",
|
|
||||||
"encryption_key_missing": false
|
|
||||||
},
|
|
||||||
"type": "unknown"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": [
|
|
||||||
]
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
=== blockdev_add/device_add and device_del ===
|
|
||||||
|
|
||||||
Testing:
|
|
||||||
{
|
|
||||||
QMP_VERSION
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": [
|
|
||||||
{
|
|
||||||
"iops_rd": 0,
|
|
||||||
"detect_zeroes": "off",
|
|
||||||
"image": {
|
|
||||||
"virtual-size": 134217728,
|
|
||||||
"filename": "TEST_DIR/t.IMGFMT",
|
|
||||||
"cluster-size": 65536,
|
|
||||||
"format": "IMGFMT",
|
|
||||||
"actual-size": SIZE,
|
|
||||||
"dirty-flag": false
|
|
||||||
},
|
|
||||||
"iops_wr": 0,
|
|
||||||
"ro": false,
|
|
||||||
"node-name": "disk",
|
|
||||||
"backing_file_depth": 0,
|
|
||||||
"drv": "IMGFMT",
|
|
||||||
"iops": 0,
|
|
||||||
"bps_wr": 0,
|
|
||||||
"write_threshold": 0,
|
|
||||||
"encrypted": false,
|
|
||||||
"bps": 0,
|
|
||||||
"bps_rd": 0,
|
|
||||||
"cache": {
|
|
||||||
"no-flush": false,
|
|
||||||
"direct": false,
|
|
||||||
"writeback": true
|
|
||||||
},
|
|
||||||
"file": "TEST_DIR/t.IMGFMT",
|
|
||||||
"encryption_key_missing": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"iops_rd": 0,
|
|
||||||
"detect_zeroes": "off",
|
|
||||||
"image": {
|
|
||||||
"virtual-size": 197120,
|
|
||||||
"filename": "TEST_DIR/t.IMGFMT",
|
|
||||||
"format": "file",
|
|
||||||
"actual-size": SIZE,
|
|
||||||
"dirty-flag": false
|
|
||||||
},
|
|
||||||
"iops_wr": 0,
|
|
||||||
"ro": false,
|
|
||||||
"node-name": "NODE_NAME",
|
|
||||||
"backing_file_depth": 0,
|
|
||||||
"drv": "file",
|
|
||||||
"iops": 0,
|
|
||||||
"bps_wr": 0,
|
|
||||||
"write_threshold": 0,
|
|
||||||
"encrypted": false,
|
|
||||||
"bps": 0,
|
|
||||||
"bps_rd": 0,
|
|
||||||
"cache": {
|
|
||||||
"no-flush": false,
|
|
||||||
"direct": false,
|
|
||||||
"writeback": true
|
|
||||||
},
|
|
||||||
"file": "TEST_DIR/t.IMGFMT",
|
|
||||||
"encryption_key_missing": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": [
|
|
||||||
{
|
|
||||||
"iops_rd": 0,
|
|
||||||
"detect_zeroes": "off",
|
|
||||||
"image": {
|
|
||||||
"virtual-size": 134217728,
|
|
||||||
"filename": "TEST_DIR/t.IMGFMT",
|
|
||||||
"cluster-size": 65536,
|
|
||||||
"format": "IMGFMT",
|
|
||||||
"actual-size": SIZE,
|
|
||||||
"dirty-flag": false
|
|
||||||
},
|
|
||||||
"iops_wr": 0,
|
|
||||||
"ro": false,
|
|
||||||
"node-name": "disk",
|
|
||||||
"backing_file_depth": 0,
|
|
||||||
"drv": "IMGFMT",
|
|
||||||
"iops": 0,
|
|
||||||
"bps_wr": 0,
|
|
||||||
"write_threshold": 0,
|
|
||||||
"encrypted": false,
|
|
||||||
"bps": 0,
|
|
||||||
"bps_rd": 0,
|
|
||||||
"cache": {
|
|
||||||
"no-flush": false,
|
|
||||||
"direct": false,
|
|
||||||
"writeback": true
|
|
||||||
},
|
|
||||||
"file": "TEST_DIR/t.IMGFMT",
|
|
||||||
"encryption_key_missing": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"iops_rd": 0,
|
|
||||||
"detect_zeroes": "off",
|
|
||||||
"image": {
|
|
||||||
"virtual-size": 197120,
|
|
||||||
"filename": "TEST_DIR/t.IMGFMT",
|
|
||||||
"format": "file",
|
|
||||||
"actual-size": SIZE,
|
|
||||||
"dirty-flag": false
|
|
||||||
},
|
|
||||||
"iops_wr": 0,
|
|
||||||
"ro": false,
|
|
||||||
"node-name": "NODE_NAME",
|
|
||||||
"backing_file_depth": 0,
|
|
||||||
"drv": "file",
|
|
||||||
"iops": 0,
|
|
||||||
"bps_wr": 0,
|
|
||||||
"write_threshold": 0,
|
|
||||||
"encrypted": false,
|
|
||||||
"bps": 0,
|
|
||||||
"bps_rd": 0,
|
|
||||||
"cache": {
|
|
||||||
"no-flush": false,
|
|
||||||
"direct": false,
|
|
||||||
"writeback": true
|
|
||||||
},
|
|
||||||
"file": "TEST_DIR/t.IMGFMT",
|
|
||||||
"encryption_key_missing": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
=== Empty drive with -device and device_del ===
|
|
||||||
|
|
||||||
Testing: -device virtio-scsi -device scsi-cd,id=cd0
|
|
||||||
{
|
|
||||||
QMP_VERSION
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": [
|
|
||||||
{
|
|
||||||
"io-status": "ok",
|
|
||||||
"device": "",
|
|
||||||
"locked": false,
|
|
||||||
"removable": true,
|
|
||||||
"qdev": "cd0",
|
|
||||||
"tray_open": false,
|
|
||||||
"type": "unknown"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": [
|
|
||||||
]
|
|
||||||
}
|
|
||||||
{
|
|
||||||
"return": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*** done
|
|
|
@ -88,7 +88,7 @@
|
||||||
064 rw quick
|
064 rw quick
|
||||||
065 rw quick
|
065 rw quick
|
||||||
066 rw auto quick
|
066 rw auto quick
|
||||||
067 rw quick
|
# 067 was removed, do not reuse
|
||||||
068 rw quick
|
068 rw quick
|
||||||
069 rw auto quick
|
069 rw auto quick
|
||||||
070 rw quick
|
070 rw quick
|
||||||
|
|
|
@ -15,26 +15,17 @@
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "qapi/qmp/qstring.h"
|
#include "qapi/qmp/qstring.h"
|
||||||
|
|
||||||
static void device_del_start(QTestState *qtest, const char *id)
|
static void device_del(QTestState *qtest, const char *id)
|
||||||
{
|
{
|
||||||
qtest_qmp_send(qtest,
|
QDict *resp;
|
||||||
"{'execute': 'device_del', 'arguments': { 'id': %s } }", id);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void device_del_finish(QTestState *qtest)
|
resp = qtest_qmp(qtest,
|
||||||
{
|
"{'execute': 'device_del', 'arguments': { 'id': %s } }", id);
|
||||||
QDict *resp = qtest_qmp_receive(qtest);
|
|
||||||
|
|
||||||
g_assert(qdict_haskey(resp, "return"));
|
g_assert(qdict_haskey(resp, "return"));
|
||||||
qobject_unref(resp);
|
qobject_unref(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void device_del_request(QTestState *qtest, const char *id)
|
|
||||||
{
|
|
||||||
device_del_start(qtest, id);
|
|
||||||
device_del_finish(qtest);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void system_reset(QTestState *qtest)
|
static void system_reset(QTestState *qtest)
|
||||||
{
|
{
|
||||||
QDict *resp;
|
QDict *resp;
|
||||||
|
@ -79,7 +70,7 @@ static void test_pci_unplug_request(void)
|
||||||
* be processed. However during system reset, the removal will be
|
* be processed. However during system reset, the removal will be
|
||||||
* handled, removing the device.
|
* handled, removing the device.
|
||||||
*/
|
*/
|
||||||
device_del_request(qtest, "dev0");
|
device_del(qtest, "dev0");
|
||||||
system_reset(qtest);
|
system_reset(qtest);
|
||||||
wait_device_deleted_event(qtest, "dev0");
|
wait_device_deleted_event(qtest, "dev0");
|
||||||
|
|
||||||
|
@ -90,13 +81,8 @@ static void test_ccw_unplug(void)
|
||||||
{
|
{
|
||||||
QTestState *qtest = qtest_initf("-device virtio-balloon-ccw,id=dev0");
|
QTestState *qtest = qtest_initf("-device virtio-balloon-ccw,id=dev0");
|
||||||
|
|
||||||
/*
|
device_del(qtest, "dev0");
|
||||||
* The DEVICE_DELETED events will be sent before the command
|
|
||||||
* completes.
|
|
||||||
*/
|
|
||||||
device_del_start(qtest, "dev0");
|
|
||||||
wait_device_deleted_event(qtest, "dev0");
|
wait_device_deleted_event(qtest, "dev0");
|
||||||
device_del_finish(qtest);
|
|
||||||
|
|
||||||
qtest_quit(qtest);
|
qtest_quit(qtest);
|
||||||
}
|
}
|
||||||
|
@ -109,7 +95,7 @@ static void test_spapr_cpu_unplug_request(void)
|
||||||
"-device power9_v2.0-spapr-cpu-core,core-id=1,id=dev0");
|
"-device power9_v2.0-spapr-cpu-core,core-id=1,id=dev0");
|
||||||
|
|
||||||
/* similar to test_pci_unplug_request */
|
/* similar to test_pci_unplug_request */
|
||||||
device_del_request(qtest, "dev0");
|
device_del(qtest, "dev0");
|
||||||
system_reset(qtest);
|
system_reset(qtest);
|
||||||
wait_device_deleted_event(qtest, "dev0");
|
wait_device_deleted_event(qtest, "dev0");
|
||||||
|
|
||||||
|
@ -125,7 +111,7 @@ static void test_spapr_memory_unplug_request(void)
|
||||||
"-device pc-dimm,id=dev0,memdev=mem0");
|
"-device pc-dimm,id=dev0,memdev=mem0");
|
||||||
|
|
||||||
/* similar to test_pci_unplug_request */
|
/* similar to test_pci_unplug_request */
|
||||||
device_del_request(qtest, "dev0");
|
device_del(qtest, "dev0");
|
||||||
system_reset(qtest);
|
system_reset(qtest);
|
||||||
wait_device_deleted_event(qtest, "dev0");
|
wait_device_deleted_event(qtest, "dev0");
|
||||||
|
|
||||||
|
@ -139,7 +125,7 @@ static void test_spapr_phb_unplug_request(void)
|
||||||
qtest = qtest_initf("-device spapr-pci-host-bridge,index=1,id=dev0");
|
qtest = qtest_initf("-device spapr-pci-host-bridge,index=1,id=dev0");
|
||||||
|
|
||||||
/* similar to test_pci_unplug_request */
|
/* similar to test_pci_unplug_request */
|
||||||
device_del_request(qtest, "dev0");
|
device_del(qtest, "dev0");
|
||||||
system_reset(qtest);
|
system_reset(qtest);
|
||||||
wait_device_deleted_event(qtest, "dev0");
|
wait_device_deleted_event(qtest, "dev0");
|
||||||
|
|
||||||
|
|
|
@ -14,57 +14,95 @@
|
||||||
#include "libqos/libqtest.h"
|
#include "libqos/libqtest.h"
|
||||||
#include "libqos/virtio.h"
|
#include "libqos/virtio.h"
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
|
#include "qapi/qmp/qlist.h"
|
||||||
|
|
||||||
/* TODO actually test the results and get rid of this */
|
static bool look_for_drive0(QTestState *qts, const char *command, const char *key)
|
||||||
#define qmp_discard_response(q, ...) qobject_unref(qtest_qmp(q, __VA_ARGS__))
|
{
|
||||||
|
QDict *response;
|
||||||
|
QList *ret;
|
||||||
|
QListEntry *entry;
|
||||||
|
bool found;
|
||||||
|
|
||||||
|
response = qtest_qmp(qts, "{'execute': %s}", command);
|
||||||
|
g_assert(response && qdict_haskey(response, "return"));
|
||||||
|
ret = qdict_get_qlist(response, "return");
|
||||||
|
|
||||||
|
found = false;
|
||||||
|
QLIST_FOREACH_ENTRY(ret, entry) {
|
||||||
|
QDict *entry_dict = qobject_to(QDict, entry->value);
|
||||||
|
if (!strcmp(qdict_get_str(entry_dict, key), "drive0")) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qobject_unref(response);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool has_drive(QTestState *qts)
|
||||||
|
{
|
||||||
|
return look_for_drive0(qts, "query-block", "device");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool has_blockdev(QTestState *qts)
|
||||||
|
{
|
||||||
|
return look_for_drive0(qts, "query-named-block-nodes", "node-name");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void blockdev_add_with_media(QTestState *qts)
|
||||||
|
{
|
||||||
|
QDict *response;
|
||||||
|
|
||||||
|
response = qtest_qmp(qts,
|
||||||
|
"{ 'execute': 'blockdev-add',"
|
||||||
|
" 'arguments': {"
|
||||||
|
" 'driver': 'raw',"
|
||||||
|
" 'node-name': 'drive0',"
|
||||||
|
" 'file': {"
|
||||||
|
" 'driver': 'null-co',"
|
||||||
|
" 'read-zeroes': true"
|
||||||
|
" }"
|
||||||
|
" }"
|
||||||
|
"}");
|
||||||
|
|
||||||
|
g_assert(response);
|
||||||
|
g_assert(qdict_haskey(response, "return"));
|
||||||
|
qobject_unref(response);
|
||||||
|
g_assert(has_blockdev(qts));
|
||||||
|
}
|
||||||
|
|
||||||
static void drive_add(QTestState *qts)
|
static void drive_add(QTestState *qts)
|
||||||
{
|
{
|
||||||
char *resp = qtest_hmp(qts, "drive_add 0 if=none,id=drive0");
|
char *resp = qtest_hmp(qts, "drive_add 0 if=none,id=drive0");
|
||||||
|
|
||||||
g_assert_cmpstr(resp, ==, "OK\r\n");
|
g_assert_cmpstr(resp, ==, "OK\r\n");
|
||||||
|
g_assert(has_drive(qts));
|
||||||
|
g_free(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drive_add_with_media(QTestState *qts)
|
||||||
|
{
|
||||||
|
char *resp = qtest_hmp(qts,
|
||||||
|
"drive_add 0 if=none,id=drive0,file=null-co://,"
|
||||||
|
"file.read-zeroes=on,format=raw");
|
||||||
|
|
||||||
|
g_assert_cmpstr(resp, ==, "OK\r\n");
|
||||||
|
g_assert(has_drive(qts));
|
||||||
g_free(resp);
|
g_free(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drive_del(QTestState *qts)
|
static void drive_del(QTestState *qts)
|
||||||
{
|
{
|
||||||
char *resp = qtest_hmp(qts, "drive_del drive0");
|
char *resp;
|
||||||
|
|
||||||
|
g_assert(has_drive(qts));
|
||||||
|
resp = qtest_hmp(qts, "drive_del drive0");
|
||||||
g_assert_cmpstr(resp, ==, "");
|
g_assert_cmpstr(resp, ==, "");
|
||||||
|
g_assert(!has_drive(qts));
|
||||||
g_free(resp);
|
g_free(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void device_del(QTestState *qts)
|
|
||||||
{
|
|
||||||
QDict *response;
|
|
||||||
|
|
||||||
/* Complication: ignore DEVICE_DELETED event */
|
|
||||||
qmp_discard_response(qts, "{'execute': 'device_del',"
|
|
||||||
" 'arguments': { 'id': 'dev0' } }");
|
|
||||||
response = qtest_qmp_receive(qts);
|
|
||||||
g_assert(response);
|
|
||||||
g_assert(qdict_haskey(response, "return"));
|
|
||||||
qobject_unref(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_drive_without_dev(void)
|
|
||||||
{
|
|
||||||
QTestState *qts;
|
|
||||||
|
|
||||||
/* Start with an empty drive */
|
|
||||||
qts = qtest_init("-drive if=none,id=drive0");
|
|
||||||
|
|
||||||
/* Delete the drive */
|
|
||||||
drive_del(qts);
|
|
||||||
|
|
||||||
/* Ensure re-adding the drive works - there should be no duplicate ID error
|
|
||||||
* because the old drive must be gone.
|
|
||||||
*/
|
|
||||||
drive_add(qts);
|
|
||||||
|
|
||||||
qtest_quit(qts);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* qvirtio_get_dev_type:
|
* qvirtio_get_dev_type:
|
||||||
* Returns: the preferred virtio bus/device type for the current architecture.
|
* Returns: the preferred virtio bus/device type for the current architecture.
|
||||||
|
@ -83,6 +121,62 @@ static const char *qvirtio_get_dev_type(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void device_add(QTestState *qts)
|
||||||
|
{
|
||||||
|
QDict *response;
|
||||||
|
char driver[32];
|
||||||
|
snprintf(driver, sizeof(driver), "virtio-blk-%s",
|
||||||
|
qvirtio_get_dev_type());
|
||||||
|
|
||||||
|
response = qtest_qmp(qts, "{'execute': 'device_add',"
|
||||||
|
" 'arguments': {"
|
||||||
|
" 'driver': %s,"
|
||||||
|
" 'drive': 'drive0',"
|
||||||
|
" 'id': 'dev0'"
|
||||||
|
"}}", driver);
|
||||||
|
g_assert(response);
|
||||||
|
g_assert(qdict_haskey(response, "return"));
|
||||||
|
qobject_unref(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void device_del(QTestState *qts, bool and_reset)
|
||||||
|
{
|
||||||
|
QDict *response;
|
||||||
|
|
||||||
|
response = qtest_qmp(qts, "{'execute': 'device_del',"
|
||||||
|
" 'arguments': { 'id': 'dev0' } }");
|
||||||
|
g_assert(response);
|
||||||
|
g_assert(qdict_haskey(response, "return"));
|
||||||
|
qobject_unref(response);
|
||||||
|
|
||||||
|
if (and_reset) {
|
||||||
|
response = qtest_qmp(qts, "{'execute': 'system_reset' }");
|
||||||
|
g_assert(response);
|
||||||
|
g_assert(qdict_haskey(response, "return"));
|
||||||
|
qobject_unref(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
qtest_qmp_eventwait(qts, "DEVICE_DELETED");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_drive_without_dev(void)
|
||||||
|
{
|
||||||
|
QTestState *qts;
|
||||||
|
|
||||||
|
/* Start with an empty drive */
|
||||||
|
qts = qtest_init("-drive if=none,id=drive0");
|
||||||
|
|
||||||
|
/* Delete the drive */
|
||||||
|
drive_del(qts);
|
||||||
|
|
||||||
|
/* Ensure re-adding the drive works - there should be no duplicate ID error
|
||||||
|
* because the old drive must be gone.
|
||||||
|
*/
|
||||||
|
drive_add(qts);
|
||||||
|
|
||||||
|
qtest_quit(qts);
|
||||||
|
}
|
||||||
|
|
||||||
static void test_after_failed_device_add(void)
|
static void test_after_failed_device_add(void)
|
||||||
{
|
{
|
||||||
char driver[32];
|
char driver[32];
|
||||||
|
@ -132,7 +226,93 @@ static void test_drive_del_device_del(void)
|
||||||
* Doing it in this order takes notoriously tricky special paths
|
* Doing it in this order takes notoriously tricky special paths
|
||||||
*/
|
*/
|
||||||
drive_del(qts);
|
drive_del(qts);
|
||||||
device_del(qts);
|
device_del(qts, false);
|
||||||
|
g_assert(!has_drive(qts));
|
||||||
|
|
||||||
|
qtest_quit(qts);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_cli_device_del(void)
|
||||||
|
{
|
||||||
|
QTestState *qts;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -drive/-device and device_del. Start with a drive used by a
|
||||||
|
* device that unplugs after reset.
|
||||||
|
*/
|
||||||
|
qts = qtest_initf("-drive if=none,id=drive0,file=null-co://,"
|
||||||
|
"file.read-zeroes=on,format=raw"
|
||||||
|
" -device virtio-blk-%s,drive=drive0,id=dev0",
|
||||||
|
qvirtio_get_dev_type());
|
||||||
|
|
||||||
|
device_del(qts, true);
|
||||||
|
g_assert(!has_drive(qts));
|
||||||
|
|
||||||
|
qtest_quit(qts);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_empty_device_del(void)
|
||||||
|
{
|
||||||
|
QTestState *qts;
|
||||||
|
|
||||||
|
/* device_del with no drive plugged. */
|
||||||
|
qts = qtest_initf("-device virtio-scsi-%s -device scsi-cd,id=dev0",
|
||||||
|
qvirtio_get_dev_type());
|
||||||
|
|
||||||
|
device_del(qts, false);
|
||||||
|
qtest_quit(qts);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_device_add_and_del(void)
|
||||||
|
{
|
||||||
|
QTestState *qts;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -drive/device_add and device_del. Start with a drive used by a
|
||||||
|
* device that unplugs after reset.
|
||||||
|
*/
|
||||||
|
qts = qtest_init("-drive if=none,id=drive0,file=null-co://,"
|
||||||
|
"file.read-zeroes=on,format=raw");
|
||||||
|
|
||||||
|
device_add(qts);
|
||||||
|
device_del(qts, true);
|
||||||
|
g_assert(!has_drive(qts));
|
||||||
|
|
||||||
|
qtest_quit(qts);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_drive_add_device_add_and_del(void)
|
||||||
|
{
|
||||||
|
QTestState *qts;
|
||||||
|
|
||||||
|
qts = qtest_init("");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* drive_add/device_add and device_del. The drive is used by a
|
||||||
|
* device that unplugs after reset.
|
||||||
|
*/
|
||||||
|
drive_add_with_media(qts);
|
||||||
|
device_add(qts);
|
||||||
|
device_del(qts, true);
|
||||||
|
g_assert(!has_drive(qts));
|
||||||
|
|
||||||
|
qtest_quit(qts);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_blockdev_add_device_add_and_del(void)
|
||||||
|
{
|
||||||
|
QTestState *qts;
|
||||||
|
|
||||||
|
qts = qtest_init("");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* blockdev_add/device_add and device_del. The it drive is used by a
|
||||||
|
* device that unplugs after reset, but it doesn't go away.
|
||||||
|
*/
|
||||||
|
blockdev_add_with_media(qts);
|
||||||
|
device_add(qts);
|
||||||
|
device_del(qts, true);
|
||||||
|
g_assert(has_blockdev(qts));
|
||||||
|
|
||||||
qtest_quit(qts);
|
qtest_quit(qts);
|
||||||
}
|
}
|
||||||
|
@ -146,8 +326,18 @@ int main(int argc, char **argv)
|
||||||
if (qvirtio_get_dev_type() != NULL) {
|
if (qvirtio_get_dev_type() != NULL) {
|
||||||
qtest_add_func("/drive_del/after_failed_device_add",
|
qtest_add_func("/drive_del/after_failed_device_add",
|
||||||
test_after_failed_device_add);
|
test_after_failed_device_add);
|
||||||
qtest_add_func("/blockdev/drive_del_device_del",
|
qtest_add_func("/drive_del/drive_del_device_del",
|
||||||
test_drive_del_device_del);
|
test_drive_del_device_del);
|
||||||
|
qtest_add_func("/device_del/drive/cli_device",
|
||||||
|
test_cli_device_del);
|
||||||
|
qtest_add_func("/device_del/drive/device_add",
|
||||||
|
test_device_add_and_del);
|
||||||
|
qtest_add_func("/device_del/drive/drive_add_device_add",
|
||||||
|
test_drive_add_device_add_and_del);
|
||||||
|
qtest_add_func("/device_del/empty",
|
||||||
|
test_empty_device_del);
|
||||||
|
qtest_add_func("/device_del/blockdev",
|
||||||
|
test_blockdev_add_device_add_and_del);
|
||||||
}
|
}
|
||||||
|
|
||||||
return g_test_run();
|
return g_test_run();
|
||||||
|
|
|
@ -24,7 +24,7 @@ typedef struct QTestState QTestState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* qtest_initf:
|
* qtest_initf:
|
||||||
* @fmt...: Format for creating other arguments to pass to QEMU, formatted
|
* @fmt: Format for creating other arguments to pass to QEMU, formatted
|
||||||
* like sprintf().
|
* like sprintf().
|
||||||
*
|
*
|
||||||
* Convenience wrapper around qtest_init().
|
* Convenience wrapper around qtest_init().
|
||||||
|
@ -87,7 +87,7 @@ void qtest_quit(QTestState *s);
|
||||||
* @s: #QTestState instance to operate on.
|
* @s: #QTestState instance to operate on.
|
||||||
* @fds: array of file descriptors
|
* @fds: array of file descriptors
|
||||||
* @fds_num: number of elements in @fds
|
* @fds_num: number of elements in @fds
|
||||||
* @fmt...: QMP message to send to qemu, formatted like
|
* @fmt: QMP message to send to qemu, formatted like
|
||||||
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
||||||
* supported after '%'.
|
* supported after '%'.
|
||||||
*
|
*
|
||||||
|
@ -100,7 +100,7 @@ QDict *qtest_qmp_fds(QTestState *s, int *fds, size_t fds_num,
|
||||||
/**
|
/**
|
||||||
* qtest_qmp:
|
* qtest_qmp:
|
||||||
* @s: #QTestState instance to operate on.
|
* @s: #QTestState instance to operate on.
|
||||||
* @fmt...: QMP message to send to qemu, formatted like
|
* @fmt: QMP message to send to qemu, formatted like
|
||||||
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
||||||
* supported after '%'.
|
* supported after '%'.
|
||||||
*
|
*
|
||||||
|
@ -112,7 +112,7 @@ QDict *qtest_qmp(QTestState *s, const char *fmt, ...)
|
||||||
/**
|
/**
|
||||||
* qtest_qmp_send:
|
* qtest_qmp_send:
|
||||||
* @s: #QTestState instance to operate on.
|
* @s: #QTestState instance to operate on.
|
||||||
* @fmt...: QMP message to send to qemu, formatted like
|
* @fmt: QMP message to send to qemu, formatted like
|
||||||
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
||||||
* supported after '%'.
|
* supported after '%'.
|
||||||
*
|
*
|
||||||
|
@ -124,7 +124,7 @@ void qtest_qmp_send(QTestState *s, const char *fmt, ...)
|
||||||
/**
|
/**
|
||||||
* qtest_qmp_send_raw:
|
* qtest_qmp_send_raw:
|
||||||
* @s: #QTestState instance to operate on.
|
* @s: #QTestState instance to operate on.
|
||||||
* @fmt...: text to send, formatted like sprintf()
|
* @fmt: text to send, formatted like sprintf()
|
||||||
*
|
*
|
||||||
* Sends text to the QMP monitor verbatim. Need not be valid JSON;
|
* Sends text to the QMP monitor verbatim. Need not be valid JSON;
|
||||||
* this is useful for negative tests.
|
* this is useful for negative tests.
|
||||||
|
@ -191,17 +191,27 @@ void qtest_qmp_vsend(QTestState *s, const char *fmt, va_list ap)
|
||||||
GCC_FMT_ATTR(2, 0);
|
GCC_FMT_ATTR(2, 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* qtest_receive:
|
* qtest_qmp_receive_dict:
|
||||||
* @s: #QTestState instance to operate on.
|
* @s: #QTestState instance to operate on.
|
||||||
*
|
*
|
||||||
* Reads a QMP message from QEMU and returns the response.
|
* Reads a QMP message from QEMU and returns the response.
|
||||||
*/
|
*/
|
||||||
|
QDict *qtest_qmp_receive_dict(QTestState *s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qtest_qmp_receive:
|
||||||
|
* @s: #QTestState instance to operate on.
|
||||||
|
*
|
||||||
|
* Reads a QMP message from QEMU and returns the response.
|
||||||
|
* Buffers all the events received meanwhile, until a
|
||||||
|
* call to qtest_qmp_eventwait
|
||||||
|
*/
|
||||||
QDict *qtest_qmp_receive(QTestState *s);
|
QDict *qtest_qmp_receive(QTestState *s);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* qtest_qmp_eventwait:
|
* qtest_qmp_eventwait:
|
||||||
* @s: #QTestState instance to operate on.
|
* @s: #QTestState instance to operate on.
|
||||||
* @s: #event event to wait for.
|
* @event: event to wait for.
|
||||||
*
|
*
|
||||||
* Continuously polls for QMP responses until it receives the desired event.
|
* Continuously polls for QMP responses until it receives the desired event.
|
||||||
*/
|
*/
|
||||||
|
@ -210,7 +220,7 @@ void qtest_qmp_eventwait(QTestState *s, const char *event);
|
||||||
/**
|
/**
|
||||||
* qtest_qmp_eventwait_ref:
|
* qtest_qmp_eventwait_ref:
|
||||||
* @s: #QTestState instance to operate on.
|
* @s: #QTestState instance to operate on.
|
||||||
* @s: #event event to wait for.
|
* @event: event to wait for.
|
||||||
*
|
*
|
||||||
* Continuously polls for QMP responses until it receives the desired event.
|
* Continuously polls for QMP responses until it receives the desired event.
|
||||||
* Returns a copy of the event for further investigation.
|
* Returns a copy of the event for further investigation.
|
||||||
|
@ -218,26 +228,22 @@ void qtest_qmp_eventwait(QTestState *s, const char *event);
|
||||||
QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event);
|
QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* qtest_qmp_receive_success:
|
* qtest_qmp_event_ref:
|
||||||
* @s: #QTestState instance to operate on
|
* @s: #QTestState instance to operate on.
|
||||||
* @event_cb: Event callback
|
* @event: event to return.
|
||||||
* @opaque: Argument for @event_cb
|
*
|
||||||
|
* Removes non-matching events from the buffer that was set by
|
||||||
|
* qtest_qmp_receive, until an event bearing the given name is found,
|
||||||
|
* and returns it.
|
||||||
|
* If no event matches, clears the buffer and returns NULL.
|
||||||
*
|
*
|
||||||
* Poll QMP messages until a command success response is received.
|
|
||||||
* If @event_cb, call it for each event received, passing @opaque,
|
|
||||||
* the event's name and data.
|
|
||||||
* Return the success response's "return" member.
|
|
||||||
*/
|
*/
|
||||||
QDict *qtest_qmp_receive_success(QTestState *s,
|
QDict *qtest_qmp_event_ref(QTestState *s, const char *event);
|
||||||
void (*event_cb)(void *opaque,
|
|
||||||
const char *name,
|
|
||||||
QDict *data),
|
|
||||||
void *opaque);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* qtest_hmp:
|
* qtest_hmp:
|
||||||
* @s: #QTestState instance to operate on.
|
* @s: #QTestState instance to operate on.
|
||||||
* @fmt...: HMP command to send to QEMU, formats arguments like sprintf().
|
* @fmt: HMP command to send to QEMU, formats arguments like sprintf().
|
||||||
*
|
*
|
||||||
* Send HMP command to QEMU via QMP's human-monitor-command.
|
* Send HMP command to QEMU via QMP's human-monitor-command.
|
||||||
* QMP events are discarded.
|
* QMP events are discarded.
|
||||||
|
@ -629,7 +635,7 @@ void qtest_add_abrt_handler(GHookFunc fn, const void *data);
|
||||||
/**
|
/**
|
||||||
* qtest_qmp_assert_success:
|
* qtest_qmp_assert_success:
|
||||||
* @qts: QTestState instance to operate on
|
* @qts: QTestState instance to operate on
|
||||||
* @fmt...: QMP message to send to qemu, formatted like
|
* @fmt: QMP message to send to qemu, formatted like
|
||||||
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
||||||
* supported after '%'.
|
* supported after '%'.
|
||||||
*
|
*
|
||||||
|
@ -676,7 +682,7 @@ void qtest_qmp_device_add_qdict(QTestState *qts, const char *drv,
|
||||||
* @qts: QTestState instance to operate on
|
* @qts: QTestState instance to operate on
|
||||||
* @driver: Name of the device that should be added
|
* @driver: Name of the device that should be added
|
||||||
* @id: Identification string
|
* @id: Identification string
|
||||||
* @fmt...: QMP message to send to qemu, formatted like
|
* @fmt: QMP message to send to qemu, formatted like
|
||||||
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
* qobject_from_jsonf_nofail(). See parse_escape() for what's
|
||||||
* supported after '%'.
|
* supported after '%'.
|
||||||
*
|
*
|
||||||
|
|
|
@ -63,6 +63,7 @@ struct QTestState
|
||||||
bool irq_level[MAX_IRQ];
|
bool irq_level[MAX_IRQ];
|
||||||
GString *rx;
|
GString *rx;
|
||||||
QTestTransportOps ops;
|
QTestTransportOps ops;
|
||||||
|
GList *pending_events;
|
||||||
};
|
};
|
||||||
|
|
||||||
static GHookList abrt_hooks;
|
static GHookList abrt_hooks;
|
||||||
|
@ -279,6 +280,7 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
|
||||||
|
|
||||||
g_test_message("starting QEMU: %s", command);
|
g_test_message("starting QEMU: %s", command);
|
||||||
|
|
||||||
|
s->pending_events = NULL;
|
||||||
s->wstatus = 0;
|
s->wstatus = 0;
|
||||||
s->expected_status = 0;
|
s->expected_status = 0;
|
||||||
s->qemu_pid = fork();
|
s->qemu_pid = fork();
|
||||||
|
@ -386,6 +388,13 @@ void qtest_quit(QTestState *s)
|
||||||
close(s->fd);
|
close(s->fd);
|
||||||
close(s->qmp_fd);
|
close(s->qmp_fd);
|
||||||
g_string_free(s->rx, true);
|
g_string_free(s->rx, true);
|
||||||
|
|
||||||
|
for (GList *it = s->pending_events; it != NULL; it = it->next) {
|
||||||
|
qobject_unref((QDict *)it->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_list_free(s->pending_events);
|
||||||
|
|
||||||
g_free(s);
|
g_free(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -604,6 +613,19 @@ QDict *qmp_fd_receive(int fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
QDict *qtest_qmp_receive(QTestState *s)
|
QDict *qtest_qmp_receive(QTestState *s)
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
QDict *response = qtest_qmp_receive_dict(s);
|
||||||
|
|
||||||
|
if (!qdict_get_try_str(response, "event")) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
/* Stash the event for a later consumption */
|
||||||
|
s->pending_events = g_list_prepend(s->pending_events, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QDict *qtest_qmp_receive_dict(QTestState *s)
|
||||||
{
|
{
|
||||||
return qmp_fd_receive(s->qmp_fd);
|
return qmp_fd_receive(s->qmp_fd);
|
||||||
}
|
}
|
||||||
|
@ -771,12 +793,36 @@ void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...)
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event)
|
QDict *qtest_qmp_event_ref(QTestState *s, const char *event)
|
||||||
{
|
{
|
||||||
|
GList *next = NULL;
|
||||||
QDict *response;
|
QDict *response;
|
||||||
|
|
||||||
|
for (GList *it = s->pending_events; it != NULL; it = next) {
|
||||||
|
|
||||||
|
next = it->next;
|
||||||
|
response = (QDict *)it->data;
|
||||||
|
|
||||||
|
s->pending_events = g_list_remove_link(s->pending_events, it);
|
||||||
|
|
||||||
|
if (!strcmp(qdict_get_str(response, "event"), event)) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
qobject_unref(response);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event)
|
||||||
|
{
|
||||||
|
QDict *response = qtest_qmp_event_ref(s, event);
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
response = qtest_qmp_receive(s);
|
response = qtest_qmp_receive_dict(s);
|
||||||
if ((qdict_haskey(response, "event")) &&
|
if ((qdict_haskey(response, "event")) &&
|
||||||
(strcmp(qdict_get_str(response, "event"), event) == 0)) {
|
(strcmp(qdict_get_str(response, "event"), event) == 0)) {
|
||||||
return response;
|
return response;
|
||||||
|
@ -804,12 +850,6 @@ char *qtest_vhmp(QTestState *s, const char *fmt, va_list ap)
|
||||||
" 'arguments': {'command-line': %s}}",
|
" 'arguments': {'command-line': %s}}",
|
||||||
cmd);
|
cmd);
|
||||||
ret = g_strdup(qdict_get_try_str(resp, "return"));
|
ret = g_strdup(qdict_get_try_str(resp, "return"));
|
||||||
while (ret == NULL && qdict_get_try_str(resp, "event")) {
|
|
||||||
/* Ignore asynchronous QMP events */
|
|
||||||
qobject_unref(resp);
|
|
||||||
resp = qtest_qmp_receive(s);
|
|
||||||
ret = g_strdup(qdict_get_try_str(resp, "return"));
|
|
||||||
}
|
|
||||||
g_assert(ret);
|
g_assert(ret);
|
||||||
qobject_unref(resp);
|
qobject_unref(resp);
|
||||||
g_free(cmd);
|
g_free(cmd);
|
||||||
|
@ -1245,35 +1285,6 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine),
|
||||||
qobject_unref(response);
|
qobject_unref(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
QDict *qtest_qmp_receive_success(QTestState *s,
|
|
||||||
void (*event_cb)(void *opaque,
|
|
||||||
const char *event,
|
|
||||||
QDict *data),
|
|
||||||
void *opaque)
|
|
||||||
{
|
|
||||||
QDict *response, *ret, *data;
|
|
||||||
const char *event;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
response = qtest_qmp_receive(s);
|
|
||||||
g_assert(!qdict_haskey(response, "error"));
|
|
||||||
ret = qdict_get_qdict(response, "return");
|
|
||||||
if (ret) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
event = qdict_get_str(response, "event");
|
|
||||||
data = qdict_get_qdict(response, "data");
|
|
||||||
if (event_cb) {
|
|
||||||
event_cb(opaque, event, data);
|
|
||||||
}
|
|
||||||
qobject_unref(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
qobject_ref(ret);
|
|
||||||
qobject_unref(response);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generic hot-plugging test via the device_add QMP commands.
|
* Generic hot-plugging test via the device_add QMP commands.
|
||||||
*/
|
*/
|
||||||
|
@ -1309,13 +1320,6 @@ void qtest_qmp_device_add(QTestState *qts, const char *driver, const char *id,
|
||||||
qobject_unref(args);
|
qobject_unref(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void device_deleted_cb(void *opaque, const char *name, QDict *data)
|
|
||||||
{
|
|
||||||
bool *got_event = opaque;
|
|
||||||
|
|
||||||
g_assert_cmpstr(name, ==, "DEVICE_DELETED");
|
|
||||||
*got_event = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generic hot-unplugging test via the device_del QMP command.
|
* Generic hot-unplugging test via the device_del QMP command.
|
||||||
|
@ -1332,24 +1336,17 @@ static void device_deleted_cb(void *opaque, const char *name, QDict *data)
|
||||||
* and this one:
|
* and this one:
|
||||||
*
|
*
|
||||||
* {"return": {}}
|
* {"return": {}}
|
||||||
*
|
|
||||||
* But the order of arrival may vary - so we've got to detect both.
|
|
||||||
*/
|
*/
|
||||||
void qtest_qmp_device_del(QTestState *qts, const char *id)
|
void qtest_qmp_device_del(QTestState *qts, const char *id)
|
||||||
{
|
{
|
||||||
bool got_event = false;
|
|
||||||
QDict *rsp;
|
QDict *rsp;
|
||||||
|
|
||||||
qtest_qmp_send(qts, "{'execute': 'device_del', 'arguments': {'id': %s}}",
|
rsp = qtest_qmp(qts, "{'execute': 'device_del', 'arguments': {'id': %s}}",
|
||||||
id);
|
id);
|
||||||
rsp = qtest_qmp_receive_success(qts, device_deleted_cb, &got_event);
|
|
||||||
|
g_assert(qdict_haskey(rsp, "return"));
|
||||||
qobject_unref(rsp);
|
qobject_unref(rsp);
|
||||||
if (!got_event) {
|
qtest_qmp_eventwait(qts, "DEVICE_DELETED");
|
||||||
rsp = qtest_qmp_receive(qts);
|
|
||||||
g_assert_cmpstr(qdict_get_try_str(rsp, "event"),
|
|
||||||
==, "DEVICE_DELETED");
|
|
||||||
qobject_unref(rsp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool qmp_rsp_is_err(QDict *rsp)
|
bool qmp_rsp_is_err(QDict *rsp)
|
||||||
|
@ -1403,6 +1400,7 @@ QTestState *qtest_inproc_init(QTestState **s, bool log, const char* arch,
|
||||||
{
|
{
|
||||||
QTestState *qts;
|
QTestState *qts;
|
||||||
qts = g_new0(QTestState, 1);
|
qts = g_new0(QTestState, 1);
|
||||||
|
qts->pending_events = NULL;
|
||||||
*s = qts; /* Expose qts early on, since the query endianness relies on it */
|
*s = qts; /* Expose qts early on, since the query endianness relies on it */
|
||||||
qts->wstatus = 0;
|
qts->wstatus = 0;
|
||||||
for (int i = 0; i < MAX_IRQ; i++) {
|
for (int i = 0; i < MAX_IRQ; i++) {
|
||||||
|
|
|
@ -111,7 +111,7 @@ qtests_moxie = [ 'boot-serial-test' ]
|
||||||
qtests_ppc = \
|
qtests_ppc = \
|
||||||
(config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) + \
|
(config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) + \
|
||||||
(config_all_devices.has_key('CONFIG_M48T59') ? ['m48t59-test'] : []) + \
|
(config_all_devices.has_key('CONFIG_M48T59') ? ['m48t59-test'] : []) + \
|
||||||
['boot-order-test', 'prom-env-test', 'drive_del-test', 'boot-serial-test'] \
|
['boot-order-test', 'prom-env-test', 'boot-serial-test'] \
|
||||||
|
|
||||||
qtests_ppc64 = \
|
qtests_ppc64 = \
|
||||||
(config_all_devices.has_key('CONFIG_PSERIES') ? ['device-plug-test'] : []) + \
|
(config_all_devices.has_key('CONFIG_PSERIES') ? ['device-plug-test'] : []) + \
|
||||||
|
@ -121,7 +121,7 @@ qtests_ppc64 = \
|
||||||
(config_all_devices.has_key('CONFIG_USB_UHCI') ? ['usb-hcd-uhci-test'] : []) + \
|
(config_all_devices.has_key('CONFIG_USB_UHCI') ? ['usb-hcd-uhci-test'] : []) + \
|
||||||
(config_all_devices.has_key('CONFIG_USB_XHCI_NEC') ? ['usb-hcd-xhci-test'] : []) + \
|
(config_all_devices.has_key('CONFIG_USB_XHCI_NEC') ? ['usb-hcd-xhci-test'] : []) + \
|
||||||
(config_host.has_key('CONFIG_POSIX') ? ['test-filter-mirror'] : []) + \
|
(config_host.has_key('CONFIG_POSIX') ? ['test-filter-mirror'] : []) + \
|
||||||
qtests_pci + ['migration-test', 'numa-test', 'cpu-plug-test']
|
qtests_pci + ['migration-test', 'numa-test', 'cpu-plug-test', 'drive_del-test']
|
||||||
|
|
||||||
qtests_sh4 = (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : [])
|
qtests_sh4 = (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : [])
|
||||||
qtests_sh4eb = (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : [])
|
qtests_sh4eb = (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : [])
|
||||||
|
@ -193,34 +193,24 @@ qos_test_ss.add(
|
||||||
qos_test_ss.add(when: 'CONFIG_VIRTFS', if_true: files('virtio-9p-test.c'))
|
qos_test_ss.add(when: 'CONFIG_VIRTFS', if_true: files('virtio-9p-test.c'))
|
||||||
qos_test_ss.add(when: 'CONFIG_VHOST_USER', if_true: files('vhost-user-test.c'))
|
qos_test_ss.add(when: 'CONFIG_VHOST_USER', if_true: files('vhost-user-test.c'))
|
||||||
|
|
||||||
extra_qtest_deps = {
|
tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c']
|
||||||
'bios-tables-test': [io],
|
|
||||||
'ivshmem-test': [rt],
|
|
||||||
'qos-test': [chardev, io],
|
|
||||||
'tpm-crb-swtpm-test': [io],
|
|
||||||
'tpm-crb-test': [io],
|
|
||||||
'tpm-tis-swtpm-test': [io],
|
|
||||||
'tpm-tis-test': [io],
|
|
||||||
'tpm-tis-device-swtpm-test': [io],
|
|
||||||
'tpm-tis-device-test': [io],
|
|
||||||
}
|
|
||||||
extra_qtest_srcs = {
|
|
||||||
'bios-tables-test': files('boot-sector.c', 'acpi-utils.c', 'tpm-emu.c'),
|
|
||||||
'pxe-test': files('boot-sector.c'),
|
|
||||||
'cdrom-test': files('boot-sector.c'),
|
|
||||||
'migration-test': files('migration-helpers.c'),
|
|
||||||
'ivshmem-test': files('../../contrib/ivshmem-server/ivshmem-server.c'),
|
|
||||||
'dbus-vmstate-test': files('migration-helpers.c') + dbus_vmstate1,
|
|
||||||
'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'),
|
|
||||||
'tpm-crb-swtpm-test': files('tpm-emu.c', 'tpm-util.c', 'tpm-tests.c'),
|
|
||||||
'tpm-crb-test': files('tpm-emu.c', 'tpm-util.c', 'tpm-tests.c'),
|
|
||||||
'tpm-tis-device-swtpm-test': files('tpm-emu.c', 'tpm-util.c', 'tpm-tis-util.c', 'tpm-tests.c'),
|
|
||||||
'tpm-tis-device-test': files('tpm-emu.c', 'tpm-util.c', 'tpm-tis-util.c', 'tpm-tests.c'),
|
|
||||||
'tpm-tis-swtpm-test': files('tpm-emu.c', 'tpm-util.c', 'tpm-tis-util.c', 'tpm-tests.c'),
|
|
||||||
'tpm-tis-test': files('tpm-emu.c', 'tpm-util.c', 'tpm-tis-util.c', 'tpm-tests.c'),
|
|
||||||
'qos-test': qos_test_ss.apply(config_host, strict: false).sources()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
qtests = {
|
||||||
|
'bios-tables-test': [io, 'boot-sector.c', 'acpi-utils.c', 'tpm-emu.c'],
|
||||||
|
'cdrom-test': files('boot-sector.c'),
|
||||||
|
'dbus-vmstate-test': files('migration-helpers.c') + dbus_vmstate1,
|
||||||
|
'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'],
|
||||||
|
'migration-test': files('migration-helpers.c'),
|
||||||
|
'pxe-test': files('boot-sector.c'),
|
||||||
|
'qos-test': [chardev, io, qos_test_ss.apply(config_host, strict: false).sources()],
|
||||||
|
'tpm-crb-swtpm-test': [io, tpmemu_files],
|
||||||
|
'tpm-crb-test': [io, tpmemu_files],
|
||||||
|
'tpm-tis-swtpm-test': [io, tpmemu_files, 'tpm-tis-util.c'],
|
||||||
|
'tpm-tis-test': [io, tpmemu_files, 'tpm-tis-util.c'],
|
||||||
|
'tpm-tis-device-swtpm-test': [io, tpmemu_files, 'tpm-tis-util.c'],
|
||||||
|
'tpm-tis-device-test': [io, tpmemu_files, 'tpm-tis-util.c'],
|
||||||
|
'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'),
|
||||||
|
}
|
||||||
|
|
||||||
qtest_executables = {}
|
qtest_executables = {}
|
||||||
foreach dir : target_dirs
|
foreach dir : target_dirs
|
||||||
|
@ -230,7 +220,7 @@ foreach dir : target_dirs
|
||||||
|
|
||||||
target_base = dir.split('-')[0]
|
target_base = dir.split('-')[0]
|
||||||
qtest_emulator = emulators['qemu-system-' + target_base]
|
qtest_emulator = emulators['qemu-system-' + target_base]
|
||||||
qtests = get_variable('qtests_' + target_base, []) + qtests_generic
|
target_qtests = get_variable('qtests_' + target_base, []) + qtests_generic
|
||||||
|
|
||||||
test_deps = []
|
test_deps = []
|
||||||
qtest_env = environment()
|
qtest_env = environment()
|
||||||
|
@ -241,14 +231,21 @@ foreach dir : target_dirs
|
||||||
qtest_env.set('G_TEST_DBUS_DAEMON', meson.source_root() / 'tests/dbus-vmstate-daemon.sh')
|
qtest_env.set('G_TEST_DBUS_DAEMON', meson.source_root() / 'tests/dbus-vmstate-daemon.sh')
|
||||||
qtest_env.set('QTEST_QEMU_BINARY', './qemu-system-' + target_base)
|
qtest_env.set('QTEST_QEMU_BINARY', './qemu-system-' + target_base)
|
||||||
|
|
||||||
foreach test : qtests
|
foreach test : target_qtests
|
||||||
# Executables are shared across targets, declare them only the first time we
|
# Executables are shared across targets, declare them only the first time we
|
||||||
# encounter them
|
# encounter them
|
||||||
if not qtest_executables.has_key(test)
|
if not qtest_executables.has_key(test)
|
||||||
|
src = [test + '.c']
|
||||||
|
deps = [qemuutil, qos]
|
||||||
|
if test in qtests
|
||||||
|
# use a sourceset to quickly separate sources and deps
|
||||||
|
test_ss = ss.source_set()
|
||||||
|
test_ss.add(qtests[test])
|
||||||
|
src += test_ss.all_sources()
|
||||||
|
deps += test_ss.all_dependencies()
|
||||||
|
endif
|
||||||
qtest_executables += {
|
qtest_executables += {
|
||||||
test: executable(test,
|
test: executable(test, src, dependencies: deps)
|
||||||
files(test + '.c') + extra_qtest_srcs.get(test, []),
|
|
||||||
dependencies: [qemuutil, qos] + extra_qtest_deps.get(test, []))
|
|
||||||
}
|
}
|
||||||
endif
|
endif
|
||||||
# FIXME: missing dependency on the emulator binary and qemu-img
|
# FIXME: missing dependency on the emulator binary and qemu-img
|
||||||
|
|
|
@ -17,10 +17,12 @@
|
||||||
|
|
||||||
bool got_stop;
|
bool got_stop;
|
||||||
|
|
||||||
static void stop_cb(void *opaque, const char *name, QDict *data)
|
static void check_stop_event(QTestState *who)
|
||||||
{
|
{
|
||||||
if (!strcmp(name, "STOP")) {
|
QDict *event = qtest_qmp_event_ref(who, "STOP");
|
||||||
|
if (event) {
|
||||||
got_stop = true;
|
got_stop = true;
|
||||||
|
qobject_unref(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,12 +32,19 @@ static void stop_cb(void *opaque, const char *name, QDict *data)
|
||||||
QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...)
|
QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
QDict *resp;
|
||||||
|
|
||||||
va_start(ap, command);
|
va_start(ap, command);
|
||||||
qtest_qmp_vsend_fds(who, &fd, 1, command, ap);
|
qtest_qmp_vsend_fds(who, &fd, 1, command, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
return qtest_qmp_receive_success(who, stop_cb, NULL);
|
resp = qtest_qmp_receive(who);
|
||||||
|
check_stop_event(who);
|
||||||
|
|
||||||
|
g_assert(!qdict_haskey(resp, "error"));
|
||||||
|
g_assert(qdict_haskey(resp, "return"));
|
||||||
|
|
||||||
|
return qdict_get_qdict(resp, "return");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -44,12 +53,18 @@ QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...)
|
||||||
QDict *wait_command(QTestState *who, const char *command, ...)
|
QDict *wait_command(QTestState *who, const char *command, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
QDict *resp;
|
||||||
|
|
||||||
va_start(ap, command);
|
va_start(ap, command);
|
||||||
qtest_qmp_vsend(who, command, ap);
|
resp = qtest_vqmp(who, command, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
return qtest_qmp_receive_success(who, stop_cb, NULL);
|
check_stop_event(who);
|
||||||
|
|
||||||
|
g_assert(!qdict_haskey(resp, "error"));
|
||||||
|
g_assert(qdict_haskey(resp, "return"));
|
||||||
|
|
||||||
|
return qdict_get_qdict(resp, "return");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -24,9 +24,7 @@ static void test_panic(void)
|
||||||
|
|
||||||
qtest_outb(qts, 0x505, 0x1);
|
qtest_outb(qts, 0x505, 0x1);
|
||||||
|
|
||||||
response = qtest_qmp_receive(qts);
|
response = qtest_qmp_eventwait_ref(qts, "GUEST_PANICKED");
|
||||||
g_assert(qdict_haskey(response, "event"));
|
|
||||||
g_assert_cmpstr(qdict_get_str(response, "event"), ==, "GUEST_PANICKED");
|
|
||||||
g_assert(qdict_haskey(response, "data"));
|
g_assert(qdict_haskey(response, "data"));
|
||||||
data = qdict_get_qdict(response, "data");
|
data = qdict_get_qdict(response, "data");
|
||||||
g_assert(qdict_haskey(data, "action"));
|
g_assert(qdict_haskey(data, "action"));
|
||||||
|
|
|
@ -47,37 +47,37 @@ static void test_malformed(QTestState *qts)
|
||||||
|
|
||||||
/* syntax error */
|
/* syntax error */
|
||||||
qtest_qmp_send_raw(qts, "{]\n");
|
qtest_qmp_send_raw(qts, "{]\n");
|
||||||
resp = qtest_qmp_receive(qts);
|
resp = qtest_qmp_receive_dict(qts);
|
||||||
qmp_expect_error_and_unref(resp, "GenericError");
|
qmp_expect_error_and_unref(resp, "GenericError");
|
||||||
assert_recovered(qts);
|
assert_recovered(qts);
|
||||||
|
|
||||||
/* lexical error: impossible byte outside string */
|
/* lexical error: impossible byte outside string */
|
||||||
qtest_qmp_send_raw(qts, "{\xFF");
|
qtest_qmp_send_raw(qts, "{\xFF");
|
||||||
resp = qtest_qmp_receive(qts);
|
resp = qtest_qmp_receive_dict(qts);
|
||||||
qmp_expect_error_and_unref(resp, "GenericError");
|
qmp_expect_error_and_unref(resp, "GenericError");
|
||||||
assert_recovered(qts);
|
assert_recovered(qts);
|
||||||
|
|
||||||
/* lexical error: funny control character outside string */
|
/* lexical error: funny control character outside string */
|
||||||
qtest_qmp_send_raw(qts, "{\x01");
|
qtest_qmp_send_raw(qts, "{\x01");
|
||||||
resp = qtest_qmp_receive(qts);
|
resp = qtest_qmp_receive_dict(qts);
|
||||||
qmp_expect_error_and_unref(resp, "GenericError");
|
qmp_expect_error_and_unref(resp, "GenericError");
|
||||||
assert_recovered(qts);
|
assert_recovered(qts);
|
||||||
|
|
||||||
/* lexical error: impossible byte in string */
|
/* lexical error: impossible byte in string */
|
||||||
qtest_qmp_send_raw(qts, "{'bad \xFF");
|
qtest_qmp_send_raw(qts, "{'bad \xFF");
|
||||||
resp = qtest_qmp_receive(qts);
|
resp = qtest_qmp_receive_dict(qts);
|
||||||
qmp_expect_error_and_unref(resp, "GenericError");
|
qmp_expect_error_and_unref(resp, "GenericError");
|
||||||
assert_recovered(qts);
|
assert_recovered(qts);
|
||||||
|
|
||||||
/* lexical error: control character in string */
|
/* lexical error: control character in string */
|
||||||
qtest_qmp_send_raw(qts, "{'execute': 'nonexistent', 'id':'\n");
|
qtest_qmp_send_raw(qts, "{'execute': 'nonexistent', 'id':'\n");
|
||||||
resp = qtest_qmp_receive(qts);
|
resp = qtest_qmp_receive_dict(qts);
|
||||||
qmp_expect_error_and_unref(resp, "GenericError");
|
qmp_expect_error_and_unref(resp, "GenericError");
|
||||||
assert_recovered(qts);
|
assert_recovered(qts);
|
||||||
|
|
||||||
/* lexical error: interpolation */
|
/* lexical error: interpolation */
|
||||||
qtest_qmp_send_raw(qts, "%%p");
|
qtest_qmp_send_raw(qts, "%%p");
|
||||||
resp = qtest_qmp_receive(qts);
|
resp = qtest_qmp_receive_dict(qts);
|
||||||
qmp_expect_error_and_unref(resp, "GenericError");
|
qmp_expect_error_and_unref(resp, "GenericError");
|
||||||
assert_recovered(qts);
|
assert_recovered(qts);
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ static void test_qmp_protocol(void)
|
||||||
qts = qtest_init_without_qmp_handshake(common_args);
|
qts = qtest_init_without_qmp_handshake(common_args);
|
||||||
|
|
||||||
/* Test greeting */
|
/* Test greeting */
|
||||||
resp = qtest_qmp_receive(qts);
|
resp = qtest_qmp_receive_dict(qts);
|
||||||
q = qdict_get_qdict(resp, "QMP");
|
q = qdict_get_qdict(resp, "QMP");
|
||||||
g_assert(q);
|
g_assert(q);
|
||||||
test_version(qdict_get(q, "version"));
|
test_version(qdict_get(q, "version"));
|
||||||
|
@ -205,7 +205,7 @@ static void send_oob_cmd_that_fails(QTestState *s, const char *id)
|
||||||
|
|
||||||
static void recv_cmd_id(QTestState *s, const char *id)
|
static void recv_cmd_id(QTestState *s, const char *id)
|
||||||
{
|
{
|
||||||
QDict *resp = qtest_qmp_receive(s);
|
QDict *resp = qtest_qmp_receive_dict(s);
|
||||||
|
|
||||||
g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, id);
|
g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, id);
|
||||||
qobject_unref(resp);
|
qobject_unref(resp);
|
||||||
|
@ -222,7 +222,7 @@ static void test_qmp_oob(void)
|
||||||
qts = qtest_init_without_qmp_handshake(common_args);
|
qts = qtest_init_without_qmp_handshake(common_args);
|
||||||
|
|
||||||
/* Check the greeting message. */
|
/* Check the greeting message. */
|
||||||
resp = qtest_qmp_receive(qts);
|
resp = qtest_qmp_receive_dict(qts);
|
||||||
q = qdict_get_qdict(resp, "QMP");
|
q = qdict_get_qdict(resp, "QMP");
|
||||||
g_assert(q);
|
g_assert(q);
|
||||||
capabilities = qdict_get_qlist(q, "capabilities");
|
capabilities = qdict_get_qlist(q, "capabilities");
|
||||||
|
|
|
@ -237,12 +237,16 @@ void tpm_util_migrate(QTestState *who, const char *uri)
|
||||||
void tpm_util_wait_for_migration_complete(QTestState *who)
|
void tpm_util_wait_for_migration_complete(QTestState *who)
|
||||||
{
|
{
|
||||||
while (true) {
|
while (true) {
|
||||||
|
QDict *rsp;
|
||||||
QDict *rsp_return;
|
QDict *rsp_return;
|
||||||
bool completed;
|
bool completed;
|
||||||
const char *status;
|
const char *status;
|
||||||
|
|
||||||
qtest_qmp_send(who, "{ 'execute': 'query-migrate' }");
|
rsp = qtest_qmp(who, "{ 'execute': 'query-migrate' }");
|
||||||
rsp_return = qtest_qmp_receive_success(who, NULL, NULL);
|
g_assert(qdict_haskey(rsp, "return"));
|
||||||
|
rsp_return = qdict_get_qdict(rsp, "return");
|
||||||
|
|
||||||
|
g_assert(!qdict_haskey(rsp_return, "error"));
|
||||||
status = qdict_get_str(rsp_return, "status");
|
status = qdict_get_str(rsp_return, "status");
|
||||||
completed = strcmp(status, "completed") == 0;
|
completed = strcmp(status, "completed") == 0;
|
||||||
g_assert_cmpstr(status, !=, "failed");
|
g_assert_cmpstr(status, !=, "failed");
|
||||||
|
|
Loading…
Reference in New Issue