mirror of https://gitee.com/openkylin/qemu.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
|
||||
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
|
||||
- ./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
|
||||
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
|
||||
|
|
16
MAINTAINERS
16
MAINTAINERS
|
@ -117,7 +117,6 @@ R: Paolo Bonzini <pbonzini@redhat.com>
|
|||
S: Maintained
|
||||
F: softmmu/cpus.c
|
||||
F: cpus-common.c
|
||||
F: exec.c
|
||||
F: accel/tcg/
|
||||
F: accel/stubs/tcg-stub.c
|
||||
F: scripts/decodetree.py
|
||||
|
@ -1525,6 +1524,7 @@ Machine core
|
|||
M: Eduardo Habkost <ehabkost@redhat.com>
|
||||
M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
|
||||
S: Supported
|
||||
F: cpu.c
|
||||
F: hw/core/cpu.c
|
||||
F: hw/core/machine-qmp-cmds.c
|
||||
F: hw/core/machine.c
|
||||
|
@ -2055,7 +2055,7 @@ R: Laszlo Ersek <lersek@redhat.com>
|
|||
R: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Supported
|
||||
F: docs/specs/fw_cfg.txt
|
||||
F: hw/nvram/fw_cfg.c
|
||||
F: hw/nvram/fw_cfg*.c
|
||||
F: stubs/fw_cfg.c
|
||||
F: include/hw/nvram/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>
|
||||
R: David Gibson <david@gibson.dropbear.id.au>
|
||||
S: Maintained
|
||||
F: device_tree.c
|
||||
F: softmmu/device_tree.c
|
||||
F: include/sysemu/device_tree.h
|
||||
|
||||
Dump
|
||||
|
@ -2281,10 +2281,11 @@ F: include/exec/memop.h
|
|||
F: include/exec/memory.h
|
||||
F: include/exec/ram_addr.h
|
||||
F: include/exec/ramblock.h
|
||||
F: softmmu/dma-helpers.c
|
||||
F: softmmu/ioport.c
|
||||
F: softmmu/memory.c
|
||||
F: softmmu/physmem.c
|
||||
F: include/exec/memory-internal.h
|
||||
F: exec.c
|
||||
F: scripts/coccinelle/memory-region-housekeeping.cocci
|
||||
|
||||
SPICE
|
||||
|
@ -2461,7 +2462,8 @@ F: include/monitor/qdev.h
|
|||
F: include/qom/
|
||||
F: qapi/qom.json
|
||||
F: qapi/qdev.json
|
||||
F: qdev-monitor.c
|
||||
F: scripts/coccinelle/qom-parent-type.cocci
|
||||
F: softmmu/qdev-monitor.c
|
||||
F: qom/
|
||||
F: tests/check-qom-interface.c
|
||||
F: tests/check-qom-proplist.c
|
||||
|
@ -2591,7 +2593,7 @@ F: docs/interop/dbus-vmstate.rst
|
|||
Seccomp
|
||||
M: Eduardo Otubo <otubo@redhat.com>
|
||||
S: Supported
|
||||
F: qemu-seccomp.c
|
||||
F: softmmu/qemu-seccomp.c
|
||||
F: include/sysemu/seccomp.h
|
||||
|
||||
Cryptography
|
||||
|
@ -2957,7 +2959,7 @@ T: git https://github.com/stefanha/qemu.git block
|
|||
Bootdevice
|
||||
M: Gonglei <arei.gonglei@huawei.com>
|
||||
S: Maintained
|
||||
F: bootdevice.c
|
||||
F: softmmu/bootdevice.c
|
||||
|
||||
Quorum
|
||||
M: Alberto Garcia <berto@igalia.com>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
authz_ss = ss.source_set()
|
||||
authz_ss.add(genh)
|
||||
authz_ss.add(files(
|
||||
'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 = 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(
|
||||
'char-fe.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)
|
||||
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(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(files(
|
||||
'afsplit.c',
|
||||
|
@ -52,15 +51,6 @@ if 'CONFIG_GNUTLS' in config_host
|
|||
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('init.c'))
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ Contents:
|
|||
atomics
|
||||
stable-process
|
||||
testing
|
||||
qtest
|
||||
decodetree
|
||||
secure-coding-practices
|
||||
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.
|
||||
Copying and modifying an existing test is usually a good idea.
|
||||
|
||||
3. Add the test to ``tests/Makefile.include``. First, name the unit test
|
||||
program and add it to ``$(check-unit-y)``; then add a rule to build the
|
||||
executable. For example:
|
||||
3. Add the test to ``tests/meson.build``. The unit tests are listed in a
|
||||
dictionary called ``tests``. The values are any additional sources and
|
||||
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)
|
||||
tests/foo-test$(EXESUF): tests/foo-test.o $(test-util-obj-y)
|
||||
{
|
||||
...
|
||||
'foo-test': [],
|
||||
...
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -70,8 +71,8 @@ QTest
|
|||
|
||||
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 the
|
||||
documentation in ``qtest.c`` for more details of the protocol.
|
||||
clock stepping), with a special purpose "qtest" protocol. Refer to
|
||||
:doc:`qtest` for more details.
|
||||
|
||||
QTest cases can be executed with
|
||||
|
||||
|
@ -79,49 +80,6 @@ QTest cases can be executed with
|
|||
|
||||
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
|
||||
-----------------
|
||||
|
||||
|
|
|
@ -49,7 +49,8 @@ int qbus_walk_children(BusState *bus,
|
|||
}
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
||||
WITH_RCU_READ_LOCK_GUARD() {
|
||||
QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) {
|
||||
err = qdev_walk_children(kid->child,
|
||||
pre_devfn, pre_busfn,
|
||||
post_devfn, post_busfn, opaque);
|
||||
|
@ -57,6 +58,7 @@ int qbus_walk_children(BusState *bus,
|
|||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (post_busfn) {
|
||||
err = post_busfn(bus, opaque);
|
||||
|
@ -90,9 +92,11 @@ static void bus_reset_child_foreach(Object *obj, ResettableChildCallback cb,
|
|||
BusState *bus = BUS(obj);
|
||||
BusChild *kid;
|
||||
|
||||
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
||||
WITH_RCU_READ_LOCK_GUARD() {
|
||||
QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) {
|
||||
cb(OBJECT(kid->child), opaque, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void qbus_init(BusState *bus, DeviceState *parent, const char *name)
|
||||
|
@ -194,10 +198,12 @@ static void bus_set_realized(Object *obj, bool value, Error **errp)
|
|||
|
||||
/* TODO: recursive realization */
|
||||
} else if (!value && bus->realized) {
|
||||
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
||||
WITH_RCU_READ_LOCK_GUARD() {
|
||||
QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) {
|
||||
DeviceState *dev = kid->child;
|
||||
qdev_unrealize(dev);
|
||||
}
|
||||
}
|
||||
if (bc->unrealize) {
|
||||
bc->unrealize(bus);
|
||||
}
|
||||
|
|
|
@ -14,12 +14,6 @@ hwcore_files = files(
|
|||
'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(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
BusChild *kid;
|
||||
|
@ -60,15 +66,16 @@ static void bus_remove_child(BusState *bus, DeviceState *child)
|
|||
char name[32];
|
||||
|
||||
snprintf(name, sizeof(name), "child[%d]", kid->index);
|
||||
QTAILQ_REMOVE(&bus->children, kid, sibling);
|
||||
QTAILQ_REMOVE_RCU(&bus->children, kid, sibling);
|
||||
|
||||
bus->num_children--;
|
||||
|
||||
/* This gives back ownership of kid->child back to us. */
|
||||
object_property_del(OBJECT(bus), name);
|
||||
object_unref(OBJECT(kid->child));
|
||||
g_free(kid);
|
||||
return;
|
||||
|
||||
/* free the bus kid, when it is safe to do so*/
|
||||
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;
|
||||
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. */
|
||||
snprintf(name, sizeof(name), "child[%d]", kid->index);
|
||||
|
@ -94,13 +101,23 @@ static void bus_add_child(BusState *bus, DeviceState *child)
|
|||
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;
|
||||
DeviceClass *dc = DEVICE_GET_CLASS(dev);
|
||||
|
||||
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) {
|
||||
trace_qdev_update_parent_bus(dev, object_get_typename(OBJECT(dev)),
|
||||
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(dev));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (bus) {
|
||||
qdev_set_parent_bus(dev, bus);
|
||||
if (!qdev_set_parent_bus(dev, bus, errp)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
assert(!DEVICE_GET_CLASS(dev)->bus_type);
|
||||
}
|
||||
|
@ -659,7 +679,8 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id)
|
|||
DeviceState *ret;
|
||||
BusState *child;
|
||||
|
||||
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
||||
WITH_RCU_READ_LOCK_GUARD() {
|
||||
QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) {
|
||||
DeviceState *dev = kid->child;
|
||||
|
||||
if (dev->id && strcmp(dev->id, id) == 0) {
|
||||
|
@ -673,6 +694,7 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id)
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
qbus_unrealize(bus);
|
||||
}
|
||||
|
@ -939,7 +979,6 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
|
|||
}
|
||||
|
||||
assert(local_err == NULL);
|
||||
dev->realized = value;
|
||||
return;
|
||||
|
||||
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");
|
||||
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;
|
||||
if (!qemu_opt_set_bool(n->primary_device_opts,
|
||||
"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,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
type_register_static(&fw_cfg_info);
|
||||
type_register_static(&fw_cfg_io_info);
|
||||
type_register_static(&fw_cfg_mem_info);
|
||||
type_register_static(&fw_cfg_data_generator_interface_info);
|
||||
}
|
||||
|
||||
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(when: 'CONFIG_CHRP_NVRAM', if_true: files('chrp_nvram.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 void scsi_target_free_buf(SCSIRequest *req);
|
||||
|
||||
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 int next_scsi_bus;
|
||||
|
||||
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);
|
||||
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
|
||||
BusChild *kid;
|
||||
SCSIDevice *retval = NULL;
|
||||
|
||||
k->get_dev_path = scsibus_get_dev_path;
|
||||
k->get_fw_dev_path = scsibus_get_fw_dev_path;
|
||||
hc->unplug = qdev_simple_device_unplug_cb;
|
||||
QTAILQ_FOREACH_RCU(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) {
|
||||
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 = {
|
||||
.name = TYPE_SCSI_BUS,
|
||||
.parent = TYPE_BUS,
|
||||
.instance_size = sizeof(SCSIBus),
|
||||
.class_init = scsi_bus_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_HOTPLUG_HANDLER },
|
||||
{ }
|
||||
SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int id, int lun)
|
||||
{
|
||||
RCU_READ_LOCK_GUARD();
|
||||
return do_scsi_device_find(bus, channel, id, lun, false);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
static int next_scsi_bus;
|
||||
return d;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
SCSIDevice *dev = SCSI_DEVICE(qdev);
|
||||
SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
|
||||
SCSIDevice *d;
|
||||
bool is_free;
|
||||
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) {
|
||||
int id = -1;
|
||||
if (dev->lun == -1) {
|
||||
dev->lun = 0;
|
||||
}
|
||||
do {
|
||||
d = scsi_device_find(bus, dev->channel, ++id, dev->lun);
|
||||
} while (d && d->lun == dev->lun && id < bus->info->max_target);
|
||||
if (d && d->lun == dev->lun) {
|
||||
is_free = scsi_bus_is_address_free(bus, dev->channel, ++id, dev->lun, NULL);
|
||||
} while (!is_free && id < bus->info->max_target);
|
||||
if (!is_free) {
|
||||
error_setg(errp, "no free target");
|
||||
return;
|
||||
}
|
||||
|
@ -196,20 +265,13 @@ static void scsi_qdev_realize(DeviceState *qdev, Error **errp)
|
|||
} else if (dev->lun == -1) {
|
||||
int lun = -1;
|
||||
do {
|
||||
d = scsi_device_find(bus, dev->channel, dev->id, ++lun);
|
||||
} while (d && d->lun == lun && lun < bus->info->max_lun);
|
||||
if (d && d->lun == lun) {
|
||||
is_free = scsi_bus_is_address_free(bus, dev->channel, dev->id, ++lun, NULL);
|
||||
} while (!is_free && lun < bus->info->max_lun);
|
||||
if (!is_free) {
|
||||
error_setg(errp, "no free lun");
|
||||
return;
|
||||
}
|
||||
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);
|
||||
|
@ -376,19 +438,23 @@ struct SCSITargetReq {
|
|||
static void store_lun(uint8_t *outbuf, int lun)
|
||||
{
|
||||
if (lun < 256) {
|
||||
/* Simple logical unit addressing method*/
|
||||
outbuf[0] = 0;
|
||||
outbuf[1] = lun;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* Flat space addressing method */
|
||||
outbuf[0] = 0x40 | (lun >> 8);
|
||||
outbuf[1] = (lun & 255);
|
||||
outbuf[0] = (lun >> 8) | 0x40;
|
||||
}
|
||||
}
|
||||
|
||||
static bool scsi_target_emulate_report_luns(SCSITargetReq *r)
|
||||
{
|
||||
BusChild *kid;
|
||||
int i, len, n;
|
||||
int channel, id;
|
||||
bool found_lun0;
|
||||
uint8_t tmp[8] = {0};
|
||||
int len = 0;
|
||||
GByteArray *buf;
|
||||
|
||||
if (r->req.cmd.xfer < 16) {
|
||||
return false;
|
||||
|
@ -396,42 +462,40 @@ static bool scsi_target_emulate_report_luns(SCSITargetReq *r)
|
|||
if (r->req.cmd.buf[2] > 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* reserve space for 63 LUNs*/
|
||||
buf = g_byte_array_sized_new(512);
|
||||
|
||||
channel = r->req.dev->channel;
|
||||
id = r->req.dev->id;
|
||||
found_lun0 = false;
|
||||
n = 0;
|
||||
QTAILQ_FOREACH(kid, &r->req.bus->qbus.children, sibling) {
|
||||
|
||||
/* add size (will be updated later to correct value */
|
||||
g_byte_array_append(buf, tmp, 8);
|
||||
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) {
|
||||
if (dev->lun == 0) {
|
||||
found_lun0 = true;
|
||||
}
|
||||
n += 8;
|
||||
if (dev->channel == channel && dev->id == id && dev->lun != 0) {
|
||||
store_lun(tmp, dev->lun);
|
||||
g_byte_array_append(buf, tmp, 8);
|
||||
len += 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);
|
||||
memset(r->buf, 0, len);
|
||||
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;
|
||||
/* store the LUN list length */
|
||||
stl_be_p(&r->buf[0], len - 8);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1567,25 +1631,6 @@ static char *scsibus_get_fw_dev_path(DeviceState *dev)
|
|||
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 */
|
||||
|
||||
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)
|
||||
{
|
||||
DeviceClass *k = DEVICE_CLASS(klass);
|
||||
|
@ -1739,6 +1791,28 @@ static const TypeInfo scsi_device_type_info = {
|
|||
.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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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)) {
|
||||
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)
|
||||
|
@ -256,7 +256,7 @@ static inline void virtio_scsi_ctx_check(VirtIOSCSI *s, SCSIDevice *d)
|
|||
* case of async cancellation. */
|
||||
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;
|
||||
BusChild *kid;
|
||||
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:
|
||||
target = req->req.tmf.lun[1];
|
||||
s->resetting++;
|
||||
QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
|
||||
d = SCSI_DEVICE(kid->child);
|
||||
if (d->channel == 0 && d->id == target) {
|
||||
qdev_reset_all(&d->qdev);
|
||||
|
||||
rcu_read_lock();
|
||||
QTAILQ_FOREACH_RCU(kid, &s->bus.qbus.children, sibling) {
|
||||
SCSIDevice *d1 = SCSI_DEVICE(kid->child);
|
||||
if (d1->channel == 0 && d1->id == target) {
|
||||
qdev_reset_all(&d1->qdev);
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
s->resetting--;
|
||||
break;
|
||||
|
||||
|
@ -382,14 +386,17 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
|
|||
break;
|
||||
}
|
||||
|
||||
object_unref(OBJECT(d));
|
||||
return ret;
|
||||
|
||||
incorrect_lun:
|
||||
req->resp.tmf.response = VIRTIO_SCSI_S_INCORRECT_LUN;
|
||||
object_unref(OBJECT(d));
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
req->resp.tmf.response = VIRTIO_SCSI_S_BAD_TARGET;
|
||||
object_unref(OBJECT(d));
|
||||
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) {
|
||||
req->resp.cmd.response = VIRTIO_SCSI_S_BAD_TARGET;
|
||||
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->resp.cmd.response = VIRTIO_SCSI_S_OVERRUN;
|
||||
virtio_scsi_complete_cmd_req(req);
|
||||
object_unref(OBJECT(d));
|
||||
return -ENOBUFS;
|
||||
}
|
||||
scsi_req_ref(req->sreq);
|
||||
blk_io_plug(d->conf.blk);
|
||||
object_unref(OBJECT(d));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "hw/qdev-core.h"
|
||||
#include "hw/sd/sd.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qapi/error.h"
|
||||
#include "trace.h"
|
||||
|
||||
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);
|
||||
|
||||
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_readonly(to, readonly);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,9 @@ void cpu_list_unlock(void);
|
|||
|
||||
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)
|
||||
|
||||
enum device_endian {
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
OBJECT_DECLARE_SIMPLE_TYPE(VmGenIdState, VMGENID)
|
||||
|
||||
struct VmGenIdState {
|
||||
DeviceClass parent_obj;
|
||||
DeviceState parent_obj;
|
||||
QemuUUID guid; /* The 128-bit GUID seen by the guest */
|
||||
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;
|
||||
|
||||
struct VMCoreInfoState {
|
||||
DeviceClass parent_obj;
|
||||
DeviceState parent_obj;
|
||||
|
||||
bool has_vmcoreinfo;
|
||||
FWCfgVMCoreInfo vmcoreinfo;
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/bitmap.h"
|
||||
#include "qemu/rcu.h"
|
||||
#include "qemu/rcu_queue.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/hotplug.h"
|
||||
#include "hw/resettable.h"
|
||||
|
@ -161,6 +163,8 @@ struct NamedClockList {
|
|||
/**
|
||||
* DeviceState:
|
||||
* @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.
|
||||
*
|
||||
* This structure should not be accessed directly. We declare it here
|
||||
|
@ -210,13 +214,24 @@ struct BusClass {
|
|||
/* FIXME first arg should be BusState */
|
||||
void (*print_dev)(Monitor *mon, DeviceState *dev, int indent);
|
||||
char *(*get_dev_path)(DeviceState *dev);
|
||||
|
||||
/*
|
||||
* This callback is used to create Open Firmware device path in accordance
|
||||
* with OF spec http://forthworks.com/standards/of1275.pdf. Individual bus
|
||||
* bindings can be found at http://playground.sun.com/1275/bindings/.
|
||||
*/
|
||||
char *(*get_fw_dev_path)(DeviceState *dev);
|
||||
|
||||
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;
|
||||
BusUnrealize unrealize;
|
||||
|
||||
|
@ -227,6 +242,7 @@ struct BusClass {
|
|||
};
|
||||
|
||||
typedef struct BusChild {
|
||||
struct rcu_head rcu;
|
||||
DeviceState *child;
|
||||
int index;
|
||||
QTAILQ_ENTRY(BusChild) sibling;
|
||||
|
@ -247,6 +263,12 @@ struct BusState {
|
|||
int max_index;
|
||||
bool realized;
|
||||
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;
|
||||
QLIST_ENTRY(BusState) sibling;
|
||||
ResettableState reset;
|
||||
|
@ -788,7 +810,7 @@ const char *qdev_fw_name(DeviceState *dev);
|
|||
Object *qdev_get_machine(void);
|
||||
|
||||
/* 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_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,
|
||||
uint8_t *buf, uint8_t buf_size);
|
||||
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. */
|
||||
extern const SCSIReqOps scsi_generic_req_ops;
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
OBJECT_DECLARE_TYPE(CanHostState, CanHostClass, CAN_HOST)
|
||||
|
||||
struct CanHostState {
|
||||
ObjectClass oc;
|
||||
Object oc;
|
||||
|
||||
CanBusState *bus;
|
||||
CanBusClientState bus_client;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
io_ss = ss.source_set()
|
||||
io_ss.add(genh)
|
||||
io_ss.add(files(
|
||||
'channel-buffer.c',
|
||||
|
@ -14,12 +13,3 @@ io_ss.add(files(
|
|||
'net-listener.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']
|
||||
elif cpu in ['ppc', 'ppc64']
|
||||
kvm_targets = ['ppc-softmmu', 'ppc64-softmmu']
|
||||
elif cpu in ['mips', 'mips64']
|
||||
kvm_targets = ['mips-softmmu', 'mipsel-softmmu', 'mips64-softmmu', 'mips64el-softmmu']
|
||||
else
|
||||
kvm_targets = []
|
||||
endif
|
||||
|
@ -612,7 +614,9 @@ if not has_malloc_trim and get_option('malloc_trim').enabled()
|
|||
endif
|
||||
endif
|
||||
|
||||
# Create config-host.h
|
||||
#################
|
||||
# config-host.h #
|
||||
#################
|
||||
|
||||
config_host_data.set('CONFIG_COCOA', cocoa.found())
|
||||
config_host_data.set('CONFIG_LIBUDEV', libudev.found())
|
||||
|
@ -658,6 +662,10 @@ foreach k, v: config_host
|
|||
endif
|
||||
endforeach
|
||||
|
||||
########################
|
||||
# Target configuration #
|
||||
########################
|
||||
|
||||
minikconf = find_program('scripts/minikconf.py')
|
||||
config_all = {}
|
||||
config_all_devices = {}
|
||||
|
@ -864,7 +872,9 @@ config_all += {
|
|||
'CONFIG_ALL': true,
|
||||
}
|
||||
|
||||
# Submodules
|
||||
##############
|
||||
# Submodules #
|
||||
##############
|
||||
|
||||
capstone = not_found
|
||||
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_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')
|
||||
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',
|
||||
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()
|
||||
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()
|
||||
bsd_user_ss = ss.source_set()
|
||||
linux_user_ss = ss.source_set()
|
||||
specific_ss = ss.source_set()
|
||||
specific_fuzz_ss = ss.source_set()
|
||||
util_ss = ss.source_set()
|
||||
|
||||
modules = {}
|
||||
hw_arch = {}
|
||||
|
@ -1313,8 +1332,6 @@ if enable_modules
|
|||
modulecommon = declare_dependency(link_whole: libmodulecommon, compile_args: '-DBUILD_DSO')
|
||||
endif
|
||||
|
||||
# Build targets from sourcesets
|
||||
|
||||
stub_ss = stub_ss.apply(config_all, strict: false)
|
||||
|
||||
util_ss.add_all(trace_ss)
|
||||
|
@ -1360,24 +1377,14 @@ blockdev_ss.add(files(
|
|||
# os-win32.c does not
|
||||
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_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'))
|
||||
|
||||
subdir('softmmu')
|
||||
|
||||
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(when: 'CONFIG_TCG', if_true: files(
|
||||
'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/fuzz')
|
||||
|
||||
########################
|
||||
# Library dependencies #
|
||||
########################
|
||||
|
||||
block_mods = []
|
||||
softmmu_mods = []
|
||||
foreach d, list : modules
|
||||
|
@ -1446,6 +1457,47 @@ qemu_syms = custom_target('qemu.syms', output: 'qemu.syms',
|
|||
capture: true,
|
||||
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)
|
||||
libblock = static_library('block', block_ss.sources() + genh,
|
||||
dependencies: block_ss.dependencies(),
|
||||
|
@ -1465,6 +1517,22 @@ libqmp = static_library('qmp', qmp_ss.sources() + genh,
|
|||
|
||||
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
|
||||
shared_module(m.name(),
|
||||
name_prefix: '',
|
||||
|
@ -1739,6 +1807,10 @@ if host_machine.system() == 'windows'
|
|||
alias_target('installer', nsis)
|
||||
endif
|
||||
|
||||
#########################
|
||||
# Configuration summary #
|
||||
#########################
|
||||
|
||||
summary_info = {}
|
||||
summary_info += {'Install prefix': config_host['prefix']}
|
||||
summary_info += {'BIOS directory': config_host['qemu_datadir']}
|
||||
|
|
|
@ -8,13 +8,7 @@ migration_files = files(
|
|||
'qemu-file.c',
|
||||
'qjson.c',
|
||||
)
|
||||
|
||||
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(migration_files)
|
||||
|
||||
softmmu_ss.add(files(
|
||||
'block-dirty-bitmap.c',
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
qom_ss = ss.source_set()
|
||||
qom_ss.add(genh)
|
||||
qom_ss.add(files(
|
||||
'container.c',
|
||||
|
@ -9,10 +8,3 @@ qom_ss.add(files(
|
|||
|
||||
qmp_ss.add(files('qom-qmp-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
|
||||
cd "$dir"
|
||||
if [ -e .git ]; then
|
||||
pkgversion=$(git describe --match 'v*' --dirty | echo "")
|
||||
pkgversion=$(git describe --match 'v*' --dirty) || :
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files(
|
|||
'balloon.c',
|
||||
'cpus.c',
|
||||
'cpu-throttle.c',
|
||||
'physmem.c',
|
||||
'ioport.c',
|
||||
'memory.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(
|
||||
'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
|
||||
*
|
||||
|
@ -28,10 +28,8 @@
|
|||
#include "tcg/tcg.h"
|
||||
#include "hw/qdev-core.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
#include "hw/boards.h"
|
||||
#include "hw/xen/xen.h"
|
||||
#endif
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/tcg.h"
|
||||
|
@ -40,9 +38,6 @@
|
|||
#include "qemu/config-file.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/qemu-print.h"
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
#include "qemu.h"
|
||||
#else /* !CONFIG_USER_ONLY */
|
||||
#include "exec/memory.h"
|
||||
#include "exec/ioport.h"
|
||||
#include "sysemu/dma.h"
|
||||
|
@ -56,7 +51,6 @@
|
|||
#include <linux/falloc.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#include "qemu/rcu_queue.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "translate-all.h"
|
||||
|
@ -83,7 +77,6 @@
|
|||
|
||||
//#define DEBUG_SUBPAGE
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
/* ram_list is read under rcu_read_lock()/rcu_read_unlock(). Writes
|
||||
* are protected by the ramlist lock.
|
||||
*/
|
||||
|
@ -96,12 +89,6 @@ AddressSpace address_space_io;
|
|||
AddressSpace address_space_memory;
|
||||
|
||||
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;
|
||||
|
||||
|
@ -179,10 +166,6 @@ struct DirtyBitmapSnapshot {
|
|||
unsigned long dirty[];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
|
||||
static void phys_map_node_reserve(PhysPageMap *map, unsigned nodes)
|
||||
{
|
||||
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 */
|
||||
int i;
|
||||
|
@ -675,6 +658,11 @@ static void tcg_iommu_free_notifier_list(CPUState *cpu)
|
|||
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 */
|
||||
MemoryRegionSection *
|
||||
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:
|
||||
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,
|
||||
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 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. */
|
||||
int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len,
|
||||
int flags, CPUWatchpoint **watchpoint)
|
||||
|
@ -1117,123 +872,7 @@ int cpu_watchpoint_address_matches(CPUState *cpu, vaddr addr, vaddr len)
|
|||
}
|
||||
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 */
|
||||
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);
|
||||
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,
|
||||
uint16_t section);
|
||||
|
@ -3023,52 +2659,6 @@ MemoryRegion *get_system_io(void)
|
|||
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,
|
||||
hwaddr length)
|
||||
{
|
||||
|
@ -3857,18 +3447,7 @@ int qemu_target_page_bits_min(void)
|
|||
{
|
||||
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)
|
||||
{
|
||||
MemoryRegion*mr;
|
||||
|
@ -3998,23 +3577,6 @@ bool ramblock_is_pmem(RAMBlock *rb)
|
|||
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)
|
||||
{
|
||||
if (start == end - 1) {
|
||||
|
@ -4147,5 +3709,3 @@ bool ram_block_discard_is_required(void)
|
|||
{
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
qemu_opts_del(opts);
|
||||
return;
|
|
@ -49,92 +49,139 @@ static void *qtest_server_send_opaque;
|
|||
#define FMT_timeval "%ld.%06ld"
|
||||
|
||||
/**
|
||||
* QTest Protocol
|
||||
* DOC: QTest Protocol
|
||||
*
|
||||
* Line based protocol, request/response based. Server can send async messages
|
||||
* so clients should always handle many async messages before the response
|
||||
* comes in.
|
||||
*
|
||||
* Valid requests
|
||||
* ^^^^^^^^^^^^^^
|
||||
*
|
||||
* Clock management:
|
||||
* """""""""""""""""
|
||||
*
|
||||
* 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
|
||||
* return the current value of the clock in nanoseconds.
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > clock_step
|
||||
* < OK VALUE
|
||||
*
|
||||
* Advance the clock to the next deadline. Useful when waiting for
|
||||
* asynchronous events.
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > clock_step NS
|
||||
* < OK VALUE
|
||||
*
|
||||
* Advance the clock by NS nanoseconds.
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > clock_set NS
|
||||
* < OK VALUE
|
||||
*
|
||||
* Advance the clock to NS nanoseconds (do nothing if it's already past).
|
||||
*
|
||||
* PIO and memory access:
|
||||
* """"""""""""""""""""""
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > outb ADDR VALUE
|
||||
* < OK
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > outw ADDR VALUE
|
||||
* < OK
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > outl ADDR VALUE
|
||||
* < OK
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > inb ADDR
|
||||
* < OK VALUE
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > inw ADDR
|
||||
* < OK VALUE
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > inl ADDR
|
||||
* < OK VALUE
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > writeb ADDR VALUE
|
||||
* < OK
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > writew ADDR VALUE
|
||||
* < OK
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > writel ADDR VALUE
|
||||
* < OK
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > writeq ADDR VALUE
|
||||
* < OK
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > readb ADDR
|
||||
* < OK VALUE
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > readw ADDR
|
||||
* < OK VALUE
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > readl ADDR
|
||||
* < OK VALUE
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > readq ADDR
|
||||
* < OK VALUE
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > read ADDR SIZE
|
||||
* < OK DATA
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > write ADDR SIZE DATA
|
||||
* < OK
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > b64read ADDR SIZE
|
||||
* < OK B64_DATA
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > b64write ADDR SIZE B64_DATA
|
||||
* < OK
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > memset ADDR SIZE VALUE
|
||||
* < OK
|
||||
*
|
||||
|
@ -149,16 +196,21 @@ static void *qtest_server_send_opaque;
|
|||
* If the sizes do not match, the data will be truncated.
|
||||
*
|
||||
* IRQ management:
|
||||
* """""""""""""""
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > irq_intercept_in QOM-PATH
|
||||
* < OK
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > irq_intercept_out QOM-PATH
|
||||
* < OK
|
||||
*
|
||||
* 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
|
||||
* will be printed to the qtest stream:
|
||||
* will be printed to the qtest stream::
|
||||
*
|
||||
* IRQ raise NUM
|
||||
* IRQ lower NUM
|
||||
|
@ -168,6 +220,9 @@ static void *qtest_server_send_opaque;
|
|||
* NUM=0 even though it is remapped to GSI 2).
|
||||
*
|
||||
* Setting interrupt level:
|
||||
* """"""""""""""""""""""""
|
||||
*
|
||||
* .. code-block:: none
|
||||
*
|
||||
* > set_irq_in QOM-PATH NAME NUM LEVEL
|
||||
* < OK
|
||||
|
|
|
@ -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
|
||||
065 rw quick
|
||||
066 rw auto quick
|
||||
067 rw quick
|
||||
# 067 was removed, do not reuse
|
||||
068 rw quick
|
||||
069 rw auto quick
|
||||
070 rw quick
|
||||
|
|
|
@ -15,26 +15,17 @@
|
|||
#include "qapi/qmp/qdict.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,
|
||||
"{'execute': 'device_del', 'arguments': { 'id': %s } }", id);
|
||||
}
|
||||
QDict *resp;
|
||||
|
||||
static void device_del_finish(QTestState *qtest)
|
||||
{
|
||||
QDict *resp = qtest_qmp_receive(qtest);
|
||||
resp = qtest_qmp(qtest,
|
||||
"{'execute': 'device_del', 'arguments': { 'id': %s } }", id);
|
||||
|
||||
g_assert(qdict_haskey(resp, "return"));
|
||||
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)
|
||||
{
|
||||
QDict *resp;
|
||||
|
@ -79,7 +70,7 @@ static void test_pci_unplug_request(void)
|
|||
* be processed. However during system reset, the removal will be
|
||||
* handled, removing the device.
|
||||
*/
|
||||
device_del_request(qtest, "dev0");
|
||||
device_del(qtest, "dev0");
|
||||
system_reset(qtest);
|
||||
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");
|
||||
|
||||
/*
|
||||
* The DEVICE_DELETED events will be sent before the command
|
||||
* completes.
|
||||
*/
|
||||
device_del_start(qtest, "dev0");
|
||||
device_del(qtest, "dev0");
|
||||
wait_device_deleted_event(qtest, "dev0");
|
||||
device_del_finish(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");
|
||||
|
||||
/* similar to test_pci_unplug_request */
|
||||
device_del_request(qtest, "dev0");
|
||||
device_del(qtest, "dev0");
|
||||
system_reset(qtest);
|
||||
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");
|
||||
|
||||
/* similar to test_pci_unplug_request */
|
||||
device_del_request(qtest, "dev0");
|
||||
device_del(qtest, "dev0");
|
||||
system_reset(qtest);
|
||||
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");
|
||||
|
||||
/* similar to test_pci_unplug_request */
|
||||
device_del_request(qtest, "dev0");
|
||||
device_del(qtest, "dev0");
|
||||
system_reset(qtest);
|
||||
wait_device_deleted_event(qtest, "dev0");
|
||||
|
||||
|
|
|
@ -14,57 +14,95 @@
|
|||
#include "libqos/libqtest.h"
|
||||
#include "libqos/virtio.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qlist.h"
|
||||
|
||||
/* TODO actually test the results and get rid of this */
|
||||
#define qmp_discard_response(q, ...) qobject_unref(qtest_qmp(q, __VA_ARGS__))
|
||||
static bool look_for_drive0(QTestState *qts, const char *command, const char *key)
|
||||
{
|
||||
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)
|
||||
{
|
||||
char *resp = qtest_hmp(qts, "drive_add 0 if=none,id=drive0");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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(!has_drive(qts));
|
||||
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:
|
||||
* 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)
|
||||
{
|
||||
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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
@ -146,8 +326,18 @@ int main(int argc, char **argv)
|
|||
if (qvirtio_get_dev_type() != NULL) {
|
||||
qtest_add_func("/drive_del/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);
|
||||
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();
|
||||
|
|
|
@ -24,7 +24,7 @@ typedef struct QTestState QTestState;
|
|||
|
||||
/**
|
||||
* 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().
|
||||
*
|
||||
* Convenience wrapper around qtest_init().
|
||||
|
@ -87,7 +87,7 @@ void qtest_quit(QTestState *s);
|
|||
* @s: #QTestState instance to operate on.
|
||||
* @fds: array of file descriptors
|
||||
* @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
|
||||
* supported after '%'.
|
||||
*
|
||||
|
@ -100,7 +100,7 @@ QDict *qtest_qmp_fds(QTestState *s, int *fds, size_t fds_num,
|
|||
/**
|
||||
* qtest_qmp:
|
||||
* @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
|
||||
* supported after '%'.
|
||||
*
|
||||
|
@ -112,7 +112,7 @@ QDict *qtest_qmp(QTestState *s, const char *fmt, ...)
|
|||
/**
|
||||
* qtest_qmp_send:
|
||||
* @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
|
||||
* supported after '%'.
|
||||
*
|
||||
|
@ -124,7 +124,7 @@ void qtest_qmp_send(QTestState *s, const char *fmt, ...)
|
|||
/**
|
||||
* qtest_qmp_send_raw:
|
||||
* @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;
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* qtest_receive:
|
||||
* qtest_qmp_receive_dict:
|
||||
* @s: #QTestState instance to operate on.
|
||||
*
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* qtest_qmp_eventwait:
|
||||
* @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.
|
||||
*/
|
||||
|
@ -210,7 +220,7 @@ void qtest_qmp_eventwait(QTestState *s, const char *event);
|
|||
/**
|
||||
* qtest_qmp_eventwait_ref:
|
||||
* @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.
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* qtest_qmp_receive_success:
|
||||
* @s: #QTestState instance to operate on
|
||||
* @event_cb: Event callback
|
||||
* @opaque: Argument for @event_cb
|
||||
* qtest_qmp_event_ref:
|
||||
* @s: #QTestState instance to operate on.
|
||||
* @event: event to return.
|
||||
*
|
||||
* 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,
|
||||
void (*event_cb)(void *opaque,
|
||||
const char *name,
|
||||
QDict *data),
|
||||
void *opaque);
|
||||
QDict *qtest_qmp_event_ref(QTestState *s, const char *event);
|
||||
|
||||
/**
|
||||
* qtest_hmp:
|
||||
* @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.
|
||||
* QMP events are discarded.
|
||||
|
@ -629,7 +635,7 @@ void qtest_add_abrt_handler(GHookFunc fn, const void *data);
|
|||
/**
|
||||
* qtest_qmp_assert_success:
|
||||
* @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
|
||||
* supported after '%'.
|
||||
*
|
||||
|
@ -676,7 +682,7 @@ void qtest_qmp_device_add_qdict(QTestState *qts, const char *drv,
|
|||
* @qts: QTestState instance to operate on
|
||||
* @driver: Name of the device that should be added
|
||||
* @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
|
||||
* supported after '%'.
|
||||
*
|
||||
|
|
|
@ -63,6 +63,7 @@ struct QTestState
|
|||
bool irq_level[MAX_IRQ];
|
||||
GString *rx;
|
||||
QTestTransportOps ops;
|
||||
GList *pending_events;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
s->pending_events = NULL;
|
||||
s->wstatus = 0;
|
||||
s->expected_status = 0;
|
||||
s->qemu_pid = fork();
|
||||
|
@ -386,6 +388,13 @@ void qtest_quit(QTestState *s)
|
|||
close(s->fd);
|
||||
close(s->qmp_fd);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -604,6 +613,19 @@ QDict *qmp_fd_receive(int fd)
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -771,12 +793,36 @@ void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...)
|
|||
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;
|
||||
|
||||
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 (;;) {
|
||||
response = qtest_qmp_receive(s);
|
||||
response = qtest_qmp_receive_dict(s);
|
||||
if ((qdict_haskey(response, "event")) &&
|
||||
(strcmp(qdict_get_str(response, "event"), event) == 0)) {
|
||||
return response;
|
||||
|
@ -804,12 +850,6 @@ char *qtest_vhmp(QTestState *s, const char *fmt, va_list ap)
|
|||
" 'arguments': {'command-line': %s}}",
|
||||
cmd);
|
||||
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);
|
||||
qobject_unref(resp);
|
||||
g_free(cmd);
|
||||
|
@ -1245,35 +1285,6 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine),
|
|||
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.
|
||||
*/
|
||||
|
@ -1309,13 +1320,6 @@ void qtest_qmp_device_add(QTestState *qts, const char *driver, const char *id,
|
|||
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.
|
||||
|
@ -1332,24 +1336,17 @@ static void device_deleted_cb(void *opaque, const char *name, QDict *data)
|
|||
* and this one:
|
||||
*
|
||||
* {"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)
|
||||
{
|
||||
bool got_event = false;
|
||||
QDict *rsp;
|
||||
|
||||
qtest_qmp_send(qts, "{'execute': 'device_del', 'arguments': {'id': %s}}",
|
||||
rsp = qtest_qmp(qts, "{'execute': 'device_del', 'arguments': {'id': %s}}",
|
||||
id);
|
||||
rsp = qtest_qmp_receive_success(qts, device_deleted_cb, &got_event);
|
||||
|
||||
g_assert(qdict_haskey(rsp, "return"));
|
||||
qobject_unref(rsp);
|
||||
if (!got_event) {
|
||||
rsp = qtest_qmp_receive(qts);
|
||||
g_assert_cmpstr(qdict_get_try_str(rsp, "event"),
|
||||
==, "DEVICE_DELETED");
|
||||
qobject_unref(rsp);
|
||||
}
|
||||
qtest_qmp_eventwait(qts, "DEVICE_DELETED");
|
||||
}
|
||||
|
||||
bool qmp_rsp_is_err(QDict *rsp)
|
||||
|
@ -1403,6 +1400,7 @@ QTestState *qtest_inproc_init(QTestState **s, bool log, const char* arch,
|
|||
{
|
||||
QTestState *qts;
|
||||
qts = g_new0(QTestState, 1);
|
||||
qts->pending_events = NULL;
|
||||
*s = qts; /* Expose qts early on, since the query endianness relies on it */
|
||||
qts->wstatus = 0;
|
||||
for (int i = 0; i < MAX_IRQ; i++) {
|
||||
|
|
|
@ -111,7 +111,7 @@ qtests_moxie = [ 'boot-serial-test' ]
|
|||
qtests_ppc = \
|
||||
(config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-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 = \
|
||||
(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_XHCI_NEC') ? ['usb-hcd-xhci-test'] : []) + \
|
||||
(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_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_VHOST_USER', if_true: files('vhost-user-test.c'))
|
||||
|
||||
extra_qtest_deps = {
|
||||
'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()
|
||||
}
|
||||
tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c']
|
||||
|
||||
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 = {}
|
||||
foreach dir : target_dirs
|
||||
|
@ -230,7 +220,7 @@ foreach dir : target_dirs
|
|||
|
||||
target_base = dir.split('-')[0]
|
||||
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 = []
|
||||
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('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
|
||||
# encounter them
|
||||
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 += {
|
||||
test: executable(test,
|
||||
files(test + '.c') + extra_qtest_srcs.get(test, []),
|
||||
dependencies: [qemuutil, qos] + extra_qtest_deps.get(test, []))
|
||||
test: executable(test, src, dependencies: deps)
|
||||
}
|
||||
endif
|
||||
# FIXME: missing dependency on the emulator binary and qemu-img
|
||||
|
|
|
@ -17,10 +17,12 @@
|
|||
|
||||
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;
|
||||
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, ...)
|
||||
{
|
||||
va_list ap;
|
||||
QDict *resp;
|
||||
|
||||
va_start(ap, command);
|
||||
qtest_qmp_vsend_fds(who, &fd, 1, command, 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, ...)
|
||||
{
|
||||
va_list ap;
|
||||
QDict *resp;
|
||||
|
||||
va_start(ap, command);
|
||||
qtest_qmp_vsend(who, command, ap);
|
||||
resp = qtest_vqmp(who, command, 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);
|
||||
|
||||
response = qtest_qmp_receive(qts);
|
||||
g_assert(qdict_haskey(response, "event"));
|
||||
g_assert_cmpstr(qdict_get_str(response, "event"), ==, "GUEST_PANICKED");
|
||||
response = qtest_qmp_eventwait_ref(qts, "GUEST_PANICKED");
|
||||
g_assert(qdict_haskey(response, "data"));
|
||||
data = qdict_get_qdict(response, "data");
|
||||
g_assert(qdict_haskey(data, "action"));
|
||||
|
|
|
@ -47,37 +47,37 @@ static void test_malformed(QTestState *qts)
|
|||
|
||||
/* syntax error */
|
||||
qtest_qmp_send_raw(qts, "{]\n");
|
||||
resp = qtest_qmp_receive(qts);
|
||||
resp = qtest_qmp_receive_dict(qts);
|
||||
qmp_expect_error_and_unref(resp, "GenericError");
|
||||
assert_recovered(qts);
|
||||
|
||||
/* lexical error: impossible byte outside string */
|
||||
qtest_qmp_send_raw(qts, "{\xFF");
|
||||
resp = qtest_qmp_receive(qts);
|
||||
resp = qtest_qmp_receive_dict(qts);
|
||||
qmp_expect_error_and_unref(resp, "GenericError");
|
||||
assert_recovered(qts);
|
||||
|
||||
/* lexical error: funny control character outside string */
|
||||
qtest_qmp_send_raw(qts, "{\x01");
|
||||
resp = qtest_qmp_receive(qts);
|
||||
resp = qtest_qmp_receive_dict(qts);
|
||||
qmp_expect_error_and_unref(resp, "GenericError");
|
||||
assert_recovered(qts);
|
||||
|
||||
/* lexical error: impossible byte in string */
|
||||
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");
|
||||
assert_recovered(qts);
|
||||
|
||||
/* lexical error: control character in string */
|
||||
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");
|
||||
assert_recovered(qts);
|
||||
|
||||
/* lexical error: interpolation */
|
||||
qtest_qmp_send_raw(qts, "%%p");
|
||||
resp = qtest_qmp_receive(qts);
|
||||
resp = qtest_qmp_receive_dict(qts);
|
||||
qmp_expect_error_and_unref(resp, "GenericError");
|
||||
assert_recovered(qts);
|
||||
|
||||
|
@ -111,7 +111,7 @@ static void test_qmp_protocol(void)
|
|||
qts = qtest_init_without_qmp_handshake(common_args);
|
||||
|
||||
/* Test greeting */
|
||||
resp = qtest_qmp_receive(qts);
|
||||
resp = qtest_qmp_receive_dict(qts);
|
||||
q = qdict_get_qdict(resp, "QMP");
|
||||
g_assert(q);
|
||||
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)
|
||||
{
|
||||
QDict *resp = qtest_qmp_receive(s);
|
||||
QDict *resp = qtest_qmp_receive_dict(s);
|
||||
|
||||
g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, id);
|
||||
qobject_unref(resp);
|
||||
|
@ -222,7 +222,7 @@ static void test_qmp_oob(void)
|
|||
qts = qtest_init_without_qmp_handshake(common_args);
|
||||
|
||||
/* Check the greeting message. */
|
||||
resp = qtest_qmp_receive(qts);
|
||||
resp = qtest_qmp_receive_dict(qts);
|
||||
q = qdict_get_qdict(resp, "QMP");
|
||||
g_assert(q);
|
||||
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)
|
||||
{
|
||||
while (true) {
|
||||
QDict *rsp;
|
||||
QDict *rsp_return;
|
||||
bool completed;
|
||||
const char *status;
|
||||
|
||||
qtest_qmp_send(who, "{ 'execute': 'query-migrate' }");
|
||||
rsp_return = qtest_qmp_receive_success(who, NULL, NULL);
|
||||
rsp = qtest_qmp(who, "{ 'execute': 'query-migrate' }");
|
||||
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");
|
||||
completed = strcmp(status, "completed") == 0;
|
||||
g_assert_cmpstr(status, !=, "failed");
|
||||
|
|
Loading…
Reference in New Issue