* 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:
Peter Maydell 2020-10-12 22:48:45 +01:00
commit 724c1c8bb3
52 changed files with 1491 additions and 1517 deletions

View File

@ -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

View File

@ -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>

View File

@ -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)

View File

@ -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'))

452
cpu.c Normal file
View File

@ -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;
}

View File

@ -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'))

View File

@ -21,6 +21,7 @@ Contents:
atomics
stable-process
testing
qtest
decodetree
secure-coding-practices
tcg

84
docs/devel/qtest.rst Normal file
View File

@ -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

View File

@ -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
-----------------

View File

@ -49,12 +49,14 @@ int qbus_walk_children(BusState *bus,
}
}
QTAILQ_FOREACH(kid, &bus->children, sibling) {
err = qdev_walk_children(kid->child,
pre_devfn, pre_busfn,
post_devfn, post_busfn, opaque);
if (err < 0) {
return err;
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);
if (err < 0) {
return err;
}
}
}
@ -90,8 +92,10 @@ static void bus_reset_child_foreach(Object *obj, ResettableChildCallback cb,
BusState *bus = BUS(obj);
BusChild *kid;
QTAILQ_FOREACH(kid, &bus->children, sibling) {
cb(OBJECT(kid->child), opaque, type);
WITH_RCU_READ_LOCK_GUARD() {
QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) {
cb(OBJECT(kid->child), opaque, type);
}
}
}
@ -194,9 +198,11 @@ static void bus_set_realized(Object *obj, bool value, Error **errp)
/* TODO: recursive realization */
} else if (!value && bus->realized) {
QTAILQ_FOREACH(kid, &bus->children, sibling) {
DeviceState *dev = kid->child;
qdev_unrealize(dev);
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);

View File

@ -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'))

View File

@ -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,17 +679,19 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id)
DeviceState *ret;
BusState *child;
QTAILQ_FOREACH(kid, &bus->children, sibling) {
DeviceState *dev = kid->child;
WITH_RCU_READ_LOCK_GUARD() {
QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) {
DeviceState *dev = kid->child;
if (dev->id && strcmp(dev->id, id) == 0) {
return dev;
}
if (dev->id && strcmp(dev->id, id) == 0) {
return dev;
}
QLIST_FOREACH(child, &dev->child_bus, sibling) {
ret = qdev_find_recursive(child, id);
if (ret) {
return ret;
QLIST_FOREACH(child, &dev->child_bus, sibling) {
ret = qdev_find_recursive(child, id);
if (ret) {
return ret;
}
}
}
}
@ -924,7 +946,25 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
}
}
qatomic_store_release(&dev->realized, value);
} else if (!value && dev->realized) {
/*
* 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:

View File

@ -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)) {

View File

@ -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)

View File

@ -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)

View File

@ -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'))

View File

@ -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[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) {
DeviceState *qdev = kid->child;
SCSIDevice *dev = SCSI_DEVICE(qdev);
if (dev->channel == channel && dev->id == id) {
if (dev->lun == 0) {
found_lun0 = true;
/* 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 && dev->lun != 0) {
store_lun(tmp, dev->lun);
g_byte_array_append(buf, tmp, 8);
len += 8;
}
n += 8;
}
}
if (!found_lun0) {
n += 8;
}
scsi_target_alloc_buf(&r->req, n + 8);
r->buf_len = len;
r->buf = g_byte_array_free(buf, FALSE);
r->len = MIN(len, r->req.cmd.xfer & ~7);
len = MIN(n + 8, r->req.cmd.xfer & ~7);
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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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 {

View File

@ -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) */
};

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -35,7 +35,7 @@
OBJECT_DECLARE_TYPE(CanHostState, CanHostClass, CAN_HOST)
struct CanHostState {
ObjectClass oc;
Object oc;
CanBusState *bus;
CanBusClientState bus_client;

View File

@ -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])

View File

@ -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']}

View File

@ -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',

View File

@ -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)

View File

@ -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;
...
};

View File

@ -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

View File

@ -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'))

View File

@ -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

View File

@ -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;

View File

@ -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.
* 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.
* 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).
* 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,12 +220,15 @@ 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
*
* where NAME is the name of the irq/gpio list, NUM is an IRQ number and
* LEVEL is an signed integer IRQ level.
* where NAME is the name of the irq/gpio list, NUM is an IRQ number and
* LEVEL is an signed integer IRQ level.
*
* Forcibly set the given interrupt pin to the given level.
*

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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();

View File

@ -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 '%'.
*

View File

@ -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}}",
id);
rsp = qtest_qmp_receive_success(qts, device_deleted_cb, &got_event);
rsp = qtest_qmp(qts, "{'execute': 'device_del', 'arguments': {'id': %s}}",
id);
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++) {

View File

@ -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

View File

@ -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");
}
/*

View File

@ -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"));

View File

@ -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");

View File

@ -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");