Compare commits

..
97 Commits
Author SHA1 Message Date
Michael Roth ba87166e14 Update version for 2.10.2 release
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-18 10:09:38 -06:00
Laurent Vivier b7d059b91f spapr: don't initialize PATB entry if max-cpu-compat < power9
if KVM is enabled and KVM capabilities MMU radix is available,
the partition table entry (patb_entry) for the radix mode is
initialized by default in ppc_spapr_reset().

It's a problem if we want to migrate the guest to a POWER8 host
while the kernel is not started to set the value to the one
expected for a POWER8 CPU.

The "-machine max-cpu-compat=power8" should allow to migrate
a POWER9 KVM host to a POWER8 KVM host, but because patb_entry
is set, the destination QEMU tries to enable radix mode on the
POWER8 host. This fails and cancels the migration:

    Process table config unsupported by the host
    error while loading state for instance 0x0 of device 'spapr'
    load of migration failed: Invalid argument

This patch doesn't set the PATB entry if the user provides
a CPU compatibility mode that doesn't support radix mode.

Signed-off-by: Laurent Vivier <lvivier@redhat.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
(cherry picked from commit 1481fe5fcf)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-15 09:36:56 -06:00
Suraj Jitindar Singh 2f3e3890c4 target/ppc: Update setting of cpu features to account for compat modes
The device tree nodes ibm,arch-vec-5-platform-support and ibm,pa-features
are used to communicate features of the cpu to the guest operating
system. The properties of each of these are determined based on the
selected cpu model and the availability of hypervisor features.
Currently the compatibility mode of the cpu is not taken into account.

The ibm,arch-vec-5-platform-support node is used to communicate the
level of support for various ISAv3 processor features to the guest
before CAS to inform the guests' request. The available mmu mode should
only be hash unless the cpu is a POWER9 which is not in a prePOWER9
compat mode, in which case the available modes depend on the
accelerator and the hypervisor capabilities.

The ibm,pa-featues node is used to communicate the level of cpu support
for various features to the guest os. This should only contain features
relevant to the operating mode of the processor, that is the selected
cpu model taking into account any compat mode. This means that the
compat mode should be taken into account when choosing the properties of
ibm,pa-features and they should match the compat mode selected, or the
cpu model selected if no compat mode.

Update the setting of these cpu features in the device tree as described
above to properly take into account any compat mode. We use the
ppc_check_compat function which takes into account the current processor
model and the cpu compat mode.

Signed-off-by: Suraj Jitindar Singh <sjitindarsingh@gmail.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
(cherry picked from commit 7abd43baec)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-15 09:36:56 -06:00
Alex Williamson 26c1b49d56 vfio: Fix vfio-kvm group registration
Commit 8c37faa475 ("vfio-pci, ppc64/spapr: Reorder group-to-container
attaching") moved registration of groups with the vfio-kvm device from
vfio_get_group() to vfio_connect_container(), but it missed the case
where a group is attached to an existing container and takes an early
exit.  Perhaps this is a less common case on ppc64/spapr, but on x86
(without viommu) all groups are connected to the same container and
thus only the first group gets registered with the vfio-kvm device.
This becomes a problem if we then hot-unplug the devices associated
with that first group and we end up with KVM being misinformed about
any vfio connections that might remain.  Fix by including the call to
vfio_kvm_device_add_group() in this early exit path.

Fixes: 8c37faa475 ("vfio-pci, ppc64/spapr: Reorder group-to-container attaching")
Cc: qemu-stable@nongnu.org # qemu-2.10+
Reviewed-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Reviewed-by: Peter Xu <peterx@redhat.com>
Tested-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Tested-by: Eric Auger <eric.auger@redhat.com>
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
(cherry picked from commit 2016986aed)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-14 20:18:13 -06:00
David Gibson 5f214279d4 spapr: Include "pre-plugged" DIMMS in ram size calculation at reset
At guest reset time, we allocate a hash page table (HPT) for the guest
based on the guest's RAM size.  If dynamic HPT resizing is not available we
use the maximum RAM size, if it is we use the current RAM size.

But the "current RAM size" calculation is incorrect - we just use the
"base" ram_size from the machine structure.  This doesn't include any
pluggable DIMMs that are already plugged at reset time.

This means that if you try to start a 'pseries' machine with a DIMM
specified on the command line that's much larger than the "base" RAM size,
then the guest will get a woefully inadequate HPT.  This can lead to a
guest freeze during boot as it runs out of HPT space during initial MMU
setup.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Reviewed-by: Greg Kurz <groug@kaod.org>
Tested-by: Greg Kurz <groug@kaod.org>
(cherry picked from commit 768a20f3a4)
*drop dep on 9aa3397f
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 18:12:45 -06:00
Gerd Hoffmann 9c7714afd7 vga: handle cirrus vbe mode wraparounds.
Commit "3d90c62548 vga: stop passing pointers to vga_draw_line*
functions" is incomplete.  It doesn't handle the case that the vga
rendering code tries to create a shared surface, i.e. a pixman image
backed by vga video memory.  That can not work in case the guest display
wraps from end of video memory to the start.  So force shadowing in that
case.  Also adjust the snapshot region calculation.

Can trigger with cirrus only, when programming vbe modes using the bochs
api (stdvga, also qxl and virtio-vga in vga compat mode) wrap arounds
can't happen.

Fixes: CVE-2017-13672
Fixes: 3d90c62548
Cc: P J P <ppandit@redhat.com>
Reported-by: David Buchanan <d@vidbuchanan.co.uk>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Message-id: 20171010141323.14049-3-kraxel@redhat.com
(cherry picked from commit 28f77de26a)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 13:01:53 -06:00
Gerd Hoffmann a0ad811956 vga: drop line_offset variable
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
(cherry picked from commit 362f811793)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 13:01:50 -06:00
Eric Blake b81833fe7d nbd/client: Don't hard-disconnect on ESHUTDOWN from server
The NBD spec says that a server may fail any transmission request
with ESHUTDOWN when it is apparent that no further request from
the client can be successfully honored.  The client is supposed
to then initiate a soft shutdown (wait for all remaining in-flight
requests to be answered, then send NBD_CMD_DISC).  However, since
qemu's server never uses ESHUTDOWN errors, this code was mostly
untested since its introduction in commit b6f5d3b5.

More recently, I learned that nbdkit as the NBD server is able to
send ESHUTDOWN errors, so I finally tested this code, and noticed
that our client was special-casing ESHUTDOWN to cause a hard
shutdown (immediate disconnect, with no NBD_CMD_DISC), but only
if the server sends this error as a simple reply.  Further
investigation found that commit d2febedb introduced a regression
where structured replies behave differently than simple replies -
but that the structured reply behavior is more in line with the
spec (even if we still lack code in nbd-client.c to properly quit
sending further requests).  So this patch reverts the portion of
b6f5d3b5 that introduced an improper hard-disconnect special-case
at the lower level, and leaves the future enhancement of a nicer
soft-disconnect at the higher level for another day.

CC: qemu-stable@nongnu.org
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20171113194857.13933-1-eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
(cherry picked from commit 01b05c66a3)
 Conflicts:
	nbd/client.c
*drop dep on d2febedb
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 12:33:46 -06:00
Eric Blake 0fd80ef569 nbd-client: Refuse read-only client with BDRV_O_RDWR
The NBD spec says that clients should not try to write/trim to
an export advertised as read-only by the server.  But we failed
to check that, and would allow the block layer to use NBD with
BDRV_O_RDWR even when the server is read-only, which meant we
were depending on the server sending a proper EPERM failure for
various commands, and also exposes a leaky abstraction: using
qemu-io in read-write mode would succeed on 'w -z 0 0' because
of local short-circuiting logic, but 'w 0 0' would send a
request over the wire (where it then depends on the server, and
fails at least for qemu-nbd but might pass for other NBD
implementations).

With this patch, a client MUST request read-only mode to access
a server that is doing a read-only export, or else it will get
a message like:

can't open device nbd://localhost:10809/foo: request for write access conflicts with read-only export

It is no longer possible to even attempt writes over the wire
(including the corner case of 0-length writes), because the block
layer enforces the explicit read-only request; this matches the
behavior of qcow2 when backed by a read-only POSIX file.

Fix several iotests to comply with the new behavior (since
qemu-nbd of an internal snapshot, as well as nbd-server-add over QMP,
default to a read-only export, we must tell blockdev-add/qemu-io to
set up a read-only client).

CC: qemu-stable@nongnu.org
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20171108215703.9295-3-eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
(cherry picked from commit 1104d83c72)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 11:54:08 -06:00
Vladimir Sementsov-Ogievskiy b01b1609e6 nbd/server: fix nbd_negotiate_handle_info
namelen should be here, length is unrelated, and always 0 at this
point.  Broken in introduction in commit f37708f6, but mostly
harmless (replying with '' as the name does not violate protocol,
and does not confuse qemu as the nbd client since our implementation
does not ask for the name; but might confuse some other client that
does ask for the name especially if the default export is different
than the export name being queried).

Adding an assert makes it obvious that we are not skipping any bytes
in the client's message, as well as making it obvious that we were
using the wrong variable.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
CC: qemu-stable@nongnu.org
Message-Id: <20171101154204.27146-1-vsementsov@virtuozzo.com>
[eblake: improve commit message, squash in assert addition]
Signed-off-by: Eric Blake <eblake@redhat.com>

(cherry picked from commit 46321d6b5f)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 11:49:26 -06:00
Greg Kurz 82ded5166b vhost: fix error check in vhost_verify_ring_mappings()
Since commit f1f9e6c5 "vhost: adapt vhost_verify_ring_mappings() to
virtio 1 ring layout", we check the mapping of each part (descriptor
table, available ring and used ring) of each virtqueue separately.

The checking of a part is done by the vhost_verify_ring_part_mapping()
function: it returns either 0 on success or a negative errno if the
part cannot be mapped at the same place.

Unfortunately, the vhost_verify_ring_mappings() function checks its
return value the other way round. It means that we either:
- only verify the descriptor table of the first virtqueue, and if it
  is valid we ignore all the other mappings
- or ignore all broken mappings until we reach a valid one

ie, we only raise an error if all mappings are broken, and we consider
all mappings are valid otherwise (false success), which is obviously
wrong.

This patch ensures that vhost_verify_ring_mappings() only returns
success if ALL mappings are okay.

Reported-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Signed-off-by: Greg Kurz <groug@kaod.org>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit 2fe45ec3bf)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 11:47:33 -06:00
Eric Blake 227196c1e7 nbd/server: CVE-2017-15118 Stack smash on large export name
Introduced in commit f37708f6b8 (2.10).  The NBD spec says a client
can request export names up to 4096 bytes in length, even though
they should not expect success on names longer than 256.  However,
qemu hard-codes the limit of 256, and fails to filter out a client
that probes for a longer name; the result is a stack smash that can
potentially give an attacker arbitrary control over the qemu
process.

The smash can be easily demonstrated with this client:
$ qemu-io f raw nbd://localhost:10809/$(printf %3000d 1 | tr ' ' a)

If the qemu NBD server binary (whether the standalone qemu-nbd, or
the builtin server of QMP nbd-server-start) was compiled with
-fstack-protector-strong, the ability to exploit the stack smash
into arbitrary execution is a lot more difficult (but still
theoretically possible to a determined attacker, perhaps in
combination with other CVEs).  Still, crashing a running qemu (and
losing the VM) is bad enough, even if the attacker did not obtain
full execution control.

CC: qemu-stable@nongnu.org
Signed-off-by: Eric Blake <eblake@redhat.com>
(cherry picked from commit 51ae4f8455)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 11:41:27 -06:00
Eric Blake 2ce8993512 nbd/server: CVE-2017-15119 Reject options larger than 32M
The NBD spec gives us permission to abruptly disconnect on clients
that send outrageously large option requests, rather than having
to spend the time reading to the end of the option.  No real
option request requires that much data anyways; and meanwhile, we
already have the practice of abruptly dropping the connection on
any client that sends NBD_CMD_WRITE with a payload larger than 32M.

For comparison, nbdkit drops the connection on any request with
more than 4096 bytes; however, that limit is probably too low
(as the NBD spec states an export name can theoretically be up
to 4096 bytes, which means a valid NBD_OPT_INFO could be even
longer) - even if qemu doesn't permit exports longer than 256
bytes.

It could be argued that a malicious client trying to get us to
read nearly 4G of data on a bad request is a form of denial of
service.  In particular, if the server requires TLS, but a client
that does not know the TLS credentials sends any option (other
than NBD_OPT_STARTTLS or NBD_OPT_EXPORT_NAME) with a stated
payload of nearly 4G, then the server was keeping the connection
alive trying to read all the payload, tying up resources that it
would rather be spending on a client that can get past the TLS
handshake.  Hence, this warranted a CVE.

Present since at least 2.5 when handling known options, and made
worse in 2.6 when fixing support for NBD_FLAG_C_FIXED_NEWSTYLE
to handle unknown options.

CC: qemu-stable@nongnu.org
Signed-off-by: Eric Blake <eblake@redhat.com>
(cherry picked from commit fdad35ef6c)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 11:41:22 -06:00
Jason Wang c2269a0b54 virtio-net: don't touch virtqueue if vm is stopped
Guest state should not be touched if VM is stopped, unfortunately we
didn't check running state and tried to drain tx queue unconditionally
in virtio_net_set_status(). A crash was then noticed as a migration
destination when user type quit after virtqueue state is loaded but
before region cache is initialized. In this case,
virtio_net_drop_tx_queue_data() tries to access the uninitialized
region cache.

Fix this by only dropping tx queue data when vm is running.

Fixes: 283e2c2adc ("net: virtio-net discards TX data after link down")
Cc: Yuri Benditovich <yuri.benditovich@daynix.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Cc: Michael S. Tsirkin <mst@redhat.com>
Cc: qemu-stable@nongnu.org
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Jason Wang <jasowang@redhat.com>
(cherry picked from commit 70e53e6e4d)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 11:40:52 -06:00
Peter Lieven 30e499bdc9 block/nfs: fix nfs_client_open for filesize greater than 1TB
DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE) was overflowing ret (int) if
st.st_size is greater than 1TB.

Cc: qemu-stable@nongnu.org
Signed-off-by: Peter Lieven <pl@kamp.de>
Message-id: 1511798407-31129-1-git-send-email-pl@kamp.de
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit f1a7ff770f)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 11:40:10 -06:00
Michael Roth e1a2a27327 scripts/make-release: ship u-boot source as a tarball
The u-boot sources we ship currently cause problems with unpacking on
a case-insensitive filesystem due to path conflicts. This has been
fixed in upstream u-boot via commit 610eec7f, but since it is not
yet included in an official release we implement this approach as a
temporary workaround.

Once we move to a u-boot containing commit 610eec7f we should revert
this patch.

Cc: qemu-stable@nongnu.org
Cc: Alexander Graf <agraf@suse.de>
Cc: Richard Henderson <richard.henderson@linaro.org>
Cc: Thomas Huth <thuth@redhat.com>
Cc: Peter Maydell <peter.maydell@linaro.org>
Suggested-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Reviewed-by: Thomas Huth <thuth@redhat.com>
Message-id: 20171107205201.10207-1-mdroth@linux.vnet.ibm.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit d0dead3b6d)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 11:34:07 -06:00
Greg Kurz a77c5873fe spapr: reset DRCs after devices
A DRC with a pending unplug request releases its associated device at
machine reset time.

In the case of LMB, when all DRCs for a DIMM device have been reset,
the DIMM gets unplugged, causing guest memory to disappear. This may
be very confusing for anything still using this memory.

This is exactly what happens with vhost backends, and QEMU aborts
with:

qemu-system-ppc64: used ring relocated for ring 2
qemu-system-ppc64: qemu/hw/virtio/vhost.c:649: vhost_commit: Assertion
 `r >= 0' failed.

The issue is that each DRC registers a QEMU reset handler, and we
don't control the order in which these handlers are called (ie,
a LMB DRC will unplug a DIMM before the virtio device using the
memory on this DIMM could stop its vhost backend).

To avoid such situations, let's reset DRCs after all devices
have been reset.

Reported-by: Mallesh N. Koti <mallesh@linux.vnet.ibm.com>
Signed-off-by: Greg Kurz <groug@kaod.org>
Reviewed-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com>
Reviewed-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
(cherry picked from commit 8251248394)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 11:00:58 -06:00
Daniel Henrique Barboza 0a5a2b938a hw/ppc: clear pending_events on machine reset
The sPAPR machine isn't clearing up the pending events QTAILQ on
machine reboot. This allows for unprocessed hotplug/epow events
to persist in the queue after reset and, when reasserting the IRQs in
check_exception later on, these will be being processed by the OS.

This patch implements a new function called 'spapr_clear_pending_events'
that clears up the pending_events QTAILQ. This helper is then called
inside ppc_spapr_reset to clear up the events queue, preventing
old/deprecated events from persisting after a reset.

Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
(cherry picked from commit 5625817423)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 11:00:55 -06:00
Maxime Coquelin 0bc76c8d08 vhost: restore avail index from vring used index on disconnection
vhost_virtqueue_stop() gets avail index value from the backend,
except if the backend is not responding.

It happens when the backend crashes, and in this case, internal
state of the virtio queue is inconsistent, making packets
to corrupt the vring state.

With a Linux guest, it results in following error message on
backend reconnection:

[   22.444905] virtio_net virtio0: output.0:id 0 is not a head!
[   22.446746] net enp0s3: Unexpected TXQ (0) queue failure: -5
[   22.476360] net enp0s3: Unexpected TXQ (0) queue failure: -5

Fixes: 283e2c2adc ("net: virtio-net discards TX data after link down")
Cc: qemu-stable@nongnu.org
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit 2ae39a113a)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 09:55:22 -06:00
Maxime Coquelin 059422ddbc virtio: Add queue interface to restore avail index from vring used index
In case of backend crash, it is not possible to restore internal
avail index from the backend value as vhost_get_vring_base
callback fails.

This patch provides a new interface to restore internal avail index
from the vring used index, as done by some vhost-user backend on
reconnection.

Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit 2d4ba6cc74)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 09:55:16 -06:00
Max Reitz d6c99e8ff5 util/stats64: Fix min/max comparisons
stat64_min_slow() and stat64_max_slow() compare the wrong way.  This
makes iotest 136 fail with clang and -m32.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-Id: <20171114232223.25207-1-mreitz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 26a5db322b)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 09:53:22 -06:00
Eric Blake 56a10ff664 nbd/client: Use error_prepend() correctly
When using error prepend(), it is necessary to end with a space
in the format string; otherwise, messages come out incorrectly,
such as when connecting to a socket that hangs up immediately:

can't open device nbd://localhost:10809/: Failed to read dataUnexpected end-of-file before all bytes were read

Originally botched in commit e44ed99d, then several more instances
added in the meantime.

Pre-existing and not fixed here: we are inconsistent on capitalization;
some of our messages start with lower case, and others start with upper,
although the use of error_prepend() is much nicer to read when all
fragments consistently start with lower.

CC: qemu-stable@nongnu.org
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20171113152424.25381-1-eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
(cherry picked from commit cb6b1a3fc3)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 09:50:49 -06:00
Jens Freimann 69f562ad9e net: fix check for number of parameters to -netdev socket
Since commit 0f8c289ad "net: fix -netdev socket,fd= for UDP sockets"
we allow more than one parameter for -netdev socket. But now
we run into an assert when no parameter at all is specified

> qemu-system-x86_64 -netdev socket
socket.c:729: net_init_socket: Assertion `sock->has_udp' failed.

Fix this by reverting the change of the if condition done in 0f8c289ad.

Cc: Jason Wang <jasowang@redhat.com>
Cc: qemu-stable@nongnu.org
Fixes: 0f8c289ad5
Reported-by: Mao Zhongyi <maozy.fnst@cn.fujitsu.com>
Signed-off-by: Jens Freimann <jfreimann@redhat.com>
Signed-off-by: Jason Wang <jasowang@redhat.com>
(cherry picked from commit ff86d57625)
 Conflicts:
	net/socket.c
* drop context dep on 0522a959
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 09:49:07 -06:00
Jens Freimann 957bd48acf net/socket: fix coverity issue
This fixes coverity issue CID1005339.

Make sure that saddr is not used uninitialized if the
mcast parameter is NULL.

Cc: qemu-stable@nongnu.org
Reported-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Jens Freimann <jfreimann@redhat.com>
Signed-off-by: Jason Wang <jasowang@redhat.com>
(cherry picked from commit bb160b571f)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 09:43:29 -06:00
Eric Auger 3a82a03a2e hw/intc/arm_gicv3_its: Don't abort on table save failure
The ITS is not fully properly reset at the moment. Caches are
not emptied.

After a reset, in case we attempt to save the state before
the bound devices have registered their MSIs and after the
1st level table has been allocated by the ITS driver
(device BASER is valid), the first level entries are still
invalid. If the device cache is not empty (devices registered
before the reset), vgic_its_save_device_tables fails with -EINVAL.
This causes a QEMU abort().

Cc: qemu-stable@nongnu.org
Signed-off-by: Eric Auger <eric.auger@redhat.com>
Reported-by: wanghaibin <wanghaibin.wang@huawei.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 8a7348b5d6)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 09:42:12 -06:00
Peter Maydell b637b865ed translate.c: Fix usermode big-endian AArch32 LDREXD and STREXD
For AArch32 LDREXD and STREXD, architecturally the 32-bit word at the
lowest address is always Rt and the one at addr+4 is Rt2, even if the
CPU is big-endian. Our implementation does these with a single
64-bit store, so if we're big-endian then we need to put the two
32-bit halves together in the opposite order to little-endian,
so that they end up in the right places. We were trying to do
this with the gen_aa32_frob64() function, but that is not correct
for the usermode emulator, because there there is a distinction
between "load a 64 bit value" (which does a BE 64-bit access
and doesn't need swapping) and "load two 32 bit values as one
64 bit access" (where we still need to do the swapping, like
system mode BE32).

Fixes: https://bugs.launchpad.net/qemu/+bug/1725267
Cc: qemu-stable@nongnu.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 1509622400-13351-1-git-send-email-peter.maydell@linaro.org
(cherry picked from commit 3448d47b31)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 09:41:14 -06:00
Greg Kurz 3342fd0286 ppc: fix setting of compat mode
While trying to make KVM PR usable again, commit 5dfaa532ae introduced a
regression: the current compat_pvr value is passed to KVM instead of the
new one. This means that we always pass 0 instead of the max-cpu-compat
PVR during the initial machine reset. And at CAS time, we either pass
the PVR from the command line or even don't call kvmppc_set_compat() at
all, ie, the PCR will not be set as expected.

For example if we start a big endian fedora26 guest in power7 compat
mode on a POWER8 host, we get this in the guest:

$ cat /proc/cpuinfo
processor       : 0
cpu             : POWER7 (architected), altivec supported
clock           : 4024.000000MHz
revision        : 2.0 (pvr 004d 0200)

timebase        : 512000000
platform        : pSeries
model           : IBM pSeries (emulated by qemu)
machine         : CHRP IBM pSeries (emulated by qemu)
MMU             : Hash

but the guest can still execute POWER8 instructions, and the following
program succeeds:

int main()
{
        asm("vncipher 0,0,0"); // ISA 2.07 instruction
}

Let's pass the new compat_pvr to kvmppc_set_compat() and the program fails
with SIGILL as expected.

Reported-by: Nageswara R Sastry <rnsastry@linux.vnet.ibm.com>
Signed-off-by: Greg Kurz <groug@kaod.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
(cherry picked from commit e4f0c6bb1a)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 09:38:59 -06:00
Daniel P. Berrange e0809fcc4b io: monitor encoutput buffer size from websocket GSource
The websocket GSource is monitoring the size of the rawoutput
buffer to determine if the channel can accepts more writes.
The rawoutput buffer, however, is merely a temporary staging
buffer before data is copied into the encoutput buffer. Thus
its size will always be zero when the GSource runs.

This flaw causes the encoutput buffer to grow without bound
if the other end of the underlying data channel doesn't
read data being sent. This can be seen with VNC if a client
is on a slow WAN link and the guest OS is sending many screen
updates. A malicious VNC client can act like it is on a slow
link by playing a video in the guest and then reading data
very slowly, causing QEMU host memory to expand arbitrarily.

This issue is assigned CVE-2017-15268, publically reported in

  https://bugs.launchpad.net/qemu/+bug/1718964

(cherry picked from commit a7b20a8efa)

Reviewed-by: Eric Blake <eblake@redhat.com>

[Dan: Added extra checks to deal with code refactored in master but
 not stable 2.10]

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 09:38:20 -06:00
Paolo Bonzini e31942b486 nios2: define tcg_env
This should be done by all target and, since commit 53f6672bcf
("gen-icount: use tcg_ctx.tcg_env instead of cpu_env", 2017-06-30),
is causing the NIOS2 target to hang.

This is because the test for "should I exit to the main loop"
was being done with the correct offset to the icount decrementer,
but using TCG temporary 0 (the frame pointer) rather than the
env pointer.

Cc: qemu-stable@nongnu.org
Cc: Marek Vasut <marex@denx.de>
Reported-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 17bd9597be)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-06 09:32:04 -06:00
Max Reitz 5aa698ab5f iotests: Add cluster_size=64k to 125
Apparently it would be a good idea to test that, too.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20171009215533.12530-4-mreitz@redhat.com
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Jeff Cody <jcody@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit 4c112a397c)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-05 19:41:16 -06:00
Max Reitz 39475b8805 qcow2: Always execute preallocate() in a coroutine
Some qcow2 functions (at least perform_cow()) expect s->lock to be
taken.  Therefore, if we want to make use of them, we should execute
preallocate() (as "preallocate_co") in a coroutine so that we can use
the qemu_co_mutex_* functions.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20171009215533.12530-3-mreitz@redhat.com
Cc: qemu-stable@nongnu.org
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Jeff Cody <jcody@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit 572b07bea1)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-05 19:40:38 -06:00
Max Reitz a25aca75f8 qcow2: Fix unaligned preallocated truncation
A qcow2 image file's length is not required to have a length that is a
multiple of the cluster size.  However, qcow2_refcount_area() expects an
aligned value for its @start_offset parameter, so we need to round
@old_file_size up to the next cluster boundary.

Reported-by: Ping Li <pingl@redhat.com>
Bug: https://bugzilla.redhat.com/show_bug.cgi?id=1414049
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20171009215533.12530-2-mreitz@redhat.com
Cc: qemu-stable@nongnu.org
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Jeff Cody <jcody@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit e400ad1e1f)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-05 19:40:33 -06:00
Michael Olbrich 64f62e4e90 hw/sd: fix out-of-bounds check for multi block reads
The current code checks if the next block exceeds the size of the card.
This generates an error while reading the last block of the card.
Do the out-of-bounds check when starting to read a new block to fix this.

This issue became visible with increased error checking in Linux 4.13.

Cc: qemu-stable@nongnu.org
Signed-off-by: Michael Olbrich <m.olbrich@pengutronix.de>
Reviewed-by: Alistair Francis <alistair.francis@xilinx.com>
Message-id: 20170916091611.10241-1-m.olbrich@pengutronix.de
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 8573378e62)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-05 19:39:35 -06:00
Maxime Coquelin d765c5e577 memory: fix off-by-one error in memory_region_notify_one()
This patch fixes an off-by-one error that could lead to the
notifyee to receive notifications for ranges it is not
registered to.

The bug has been spotted by code review.

Fixes: bd2bfa4c52 ("memory: introduce memory_region_notify_one()")
Cc: qemu-stable@nongnu.org
Cc: Peter Xu <peterx@redhat.com>
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
Message-Id: <20171010094247.10173-4-maxime.coquelin@redhat.com>
Reviewed-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit b021d1c044)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 22:42:41 -06:00
Peter Xu ae13e2cfa8 exec: simplify address_space_get_iotlb_entry
This patch let address_space_get_iotlb_entry() to use the newly
introduced page_mask parameter in flatview_do_translate(). Then we
will be sure the IOTLB can be aligned to page mask, also we should
nicely support huge pages now when introducing a764040.

Fixes: a764040 ("exec: abstract address_space_do_translate()")
Signed-off-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Message-Id: <20171010094247.10173-3-maxime.coquelin@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 076a93d797)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 22:42:34 -06:00
Peter Xu c9dbe3e0fc exec: add page_mask for flatview_do_translate
The function is originally used for flatview_space_translate() and what
we care about most is (xlat, plen) range. However for iotlb requests, we
don't really care about "plen", but the size of the page that "xlat" is
located on. While, plen cannot really contain this information.

A simple example to show why "plen" is not good for IOTLB translations:

E.g., for huge pages, it is possible that guest mapped 1G huge page on
device side that used this GPA range:

  0x100000000 - 0x13fffffff

Then let's say we want to translate one IOVA that finally mapped to GPA
0x13ffffe00 (which is located on this 1G huge page). Then here we'll
get:

  (xlat, plen) = (0x13fffe00, 0x200)

So the IOTLB would be only covering a very small range since from
"plen" (which is 0x200 bytes) we cannot tell the size of the page.

Actually we can really know that this is a huge page - we just throw the
information away in flatview_do_translate().

This patch introduced "page_mask" optional parameter to capture that
page mask info. Also, I made "plen" an optional parameter as well, with
some comments for the whole function.

No functional change yet.

Signed-off-by: Peter Xu <peterx@redhat.com>
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
Message-Id: <20171010094247.10173-2-maxime.coquelin@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit d5e5fafd11)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 22:42:28 -06:00
Alexey Kardashevskiy 496f97293e memory: Share special empty FlatView
This shares an cached empty FlatView among address spaces. The empty
FV is used every time when a root MR renders into a FV without memory
sections which happens when MR or its children are not enabled or
zero-sized. The empty_view is not NULL to keep the rest of memory
API intact; it also has a dispatch tree for the same reason.

On POWER8 with 255 CPUs, 255 virtio-net, 40 PCI bridges guest this halves
the amount of FlatView's in use (557 -> 260) and dispatch tables
(~800000 -> ~370000).  In an unrelated experiment with 112 non-virtio
devices on x86 ("-M pc"), only 4 FlatViews are alive, and about ~2000
are created at startup.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Message-Id: <20170921085110.25598-16-aik@ozlabs.ru>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 092aa2fc65)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 22:42:02 -06:00
Paolo Bonzini 639701e4f2 memory: seek FlatView sharing candidates among children subregions
A container can be used instead of an alias to allow switching between
multiple subregions.  In this case we cannot directly share the
subregions (since they only belong to a single parent), but if the
subregions are aliases we can in turn walk those.

This is not enough to remove all source of quadratic FlatView creation,
but it enables sharing of the PCI bus master FlatViews (and their
AddressSpaceDispatch structures) across all PCI devices.  For 112
virtio-net-pci devices, boot time is reduced from 25 to 10 seconds and
memory consumption from 1.4 to 1 G.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit e673ba9af9)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 22:41:57 -06:00
Paolo Bonzini 5dbd1f7884 memory: trace FlatView creation and destruction
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 02d9651d6a)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 22:41:52 -06:00
Alexey Kardashevskiy 5b5e49ab5f memory: Create FlatView directly
This avoids usual memory_region_transaction_commit() which rebuilds
all FVs.

On POWER8 with 255 CPUs, 255 virtio-net, 40 PCI bridges guest this brings
down the boot time from 25s to 20s and reduces the amount of temporary FVs
allocated during machine constructon (~800000 -> ~640000) and amount of
temporary dispatch trees (~370000 -> ~300000), the total memory footprint
goes down (18G -> 17G).

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Message-Id: <20170921085110.25598-18-aik@ozlabs.ru>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 202fc01b05)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 22:32:47 -06:00
Alexey Kardashevskiy a7bb94e784 memory: Get rid of address_space_init_shareable
Since FlatViews are shared now and ASes not, this gets rid of
address_space_init_shareable().

This should cause no behavioural change.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Message-Id: <20170921085110.25598-17-aik@ozlabs.ru>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit b516572f31)
 Conflicts:
	target/arm/cpu.c
* drop context deps on 1d2091bc and 1e577cc7
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 22:32:11 -06:00
Alexey Kardashevskiy 7dd7f7ef44 memory: Do not allocate FlatView in address_space_init
This creates a new AS object without any FlatView as
memory_region_transaction_commit() may want to reuse the empty FV.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Message-Id: <20170921085110.25598-14-aik@ozlabs.ru>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 67ace39b25)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 22:04:50 -06:00
Alexey Kardashevskiy e8c7ea3e75 memory: Share FlatView's and dispatch trees between address spaces
This allows sharing flat views between address spaces (AS) when
the same root memory region is used when creating a new address space.
This is done by walking through all ASes and caching one FlatView per
a physical root MR (i.e. not aliased).

This removes search for duplicates from address_space_init_shareable() as
FlatViews are shared elsewhere and keeping as::ref_count correct seems
an unnecessary and useless complication.

This should cause no change and memory use or boot time yet.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Message-Id: <20170921085110.25598-13-aik@ozlabs.ru>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 967dc9b119)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 22:04:44 -06:00
Alexey Kardashevskiy c943efe8b5 memory: Move address_space_update_ioeventfds
So it is called (twice) from the same function. This is to make the next
patches a bit simpler.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Message-Id: <20170921085110.25598-12-aik@ozlabs.ru>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 0221848764)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 22:04:32 -06:00
Alexey Kardashevskiy c14ce078b2 memory: Alloc dispatch tree where topology is generared
This is to make next patches simpler.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Message-Id: <20170921085110.25598-11-aik@ozlabs.ru>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 9bf561e36c)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 22:04:19 -06:00
Alexey Kardashevskiy 260d3646b0 memory: Store physical root MR in FlatView
Address spaces get to keep a root MR (alias or not) but FlatView stores
the actual MR as this is going to be used later on to decide whether to
share a particular FlatView or not.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Message-Id: <20170921085110.25598-10-aik@ozlabs.ru>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 89c177bbdd)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 22:04:14 -06:00
Alexey Kardashevskiy 08101db63b memory: Rename mem_begin/mem_commit/mem_add helpers
This renames some helpers to reflect better what they do.

This should cause no behavioural change.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Message-Id: <20170921085110.25598-9-aik@ozlabs.ru>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 8629d3fcb7)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 22:04:00 -06:00
Alexey Kardashevskiy eff5ed4ae9 memory: Cleanup after switching to FlatView
We store AddressSpaceDispatch* in FlatView anyway so there is no need
to carry it from mem_add() to register_subpage/register_multipage.

This should cause no behavioural change.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Message-Id: <20170921085110.25598-8-aik@ozlabs.ru>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 9950322a59)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 22:03:54 -06:00
Alexey Kardashevskiy f7774e329b memory: Switch memory from using AddressSpace to FlatView
FlatView's will be shared between AddressSpace's and subpage_t
and MemoryRegionSection cannot store AS anymore, hence this change.

In particular, for:

 typedef struct subpage_t {
     MemoryRegion iomem;
-    AddressSpace *as;
+    FlatView *fv;
     hwaddr base;
     uint16_t sub_section[];
 } subpage_t;

  struct MemoryRegionSection {
     MemoryRegion *mr;
-    AddressSpace *address_space;
+    FlatView *fv;
     hwaddr offset_within_region;
     Int128 size;
     hwaddr offset_within_address_space;
     bool readonly;
 };

This should cause no behavioural change.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Message-Id: <20170921085110.25598-7-aik@ozlabs.ru>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 166206845f)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 22:03:46 -06:00
Paolo Bonzini 3568e11940 memory: avoid "resurrection" of dead FlatViews
It's possible for address_space_get_flatview() as it currently stands
to cause a use-after-free for the returned FlatView, if the reference
count is incremented after the FlatView has been replaced by a writer:

   thread 1             thread 2             RCU thread
  -------------------------------------------------------------
   rcu_read_lock
   read as->current_map
                        set as->current_map
                        flatview_unref
                           '--> call_rcu
   flatview_ref
     [ref=1]
   rcu_read_unlock
                                             flatview_destroy
   <badness>

Since FlatViews are not updated very often, we can just detect the
situation using a new atomic op atomic_fetch_inc_nonzero, similar to
Linux's atomic_inc_not_zero, which performs the refcount increment only if
it hasn't already hit zero.  This is similar to Linux commit de09a9771a53
("CRED: Fix get_task_cred() and task_state() to not resurrect dead
credentials", 2010-07-29).

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 447b0d0b9e)
 Conflicts:
	docs/devel/atomics.txt
* drop documentation ref to atomic_fetch_xor
* prereq for 166206845f
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 22:03:33 -06:00
Alexey Kardashevskiy d0136db812 memory: Remove AddressSpace pointer from AddressSpaceDispatch
AS in ASD is only used to pass AS from mem_begin() to register_subpage()
to store it in MemoryRegionSection, we can do this directly now.

This should cause no behavioural change.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Message-Id: <20170921085110.25598-6-aik@ozlabs.ru>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit c775252378)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 21:55:04 -06:00
Alexey Kardashevskiy 4d2f8abb22 memory: Move AddressSpaceDispatch from AddressSpace to FlatView
As we are going to share FlatView's between AddressSpace's,
and AddressSpaceDispatch is a structure to perform quick lookup
in FlatView, this moves ASD to FlatView.

After previosly open coded ASD rendering, we can also remove
as->next_dispatch as the new FlatView pointer is stored
on a stack and set to an AS atomically.

flatview_destroy() is executed under RCU instead of
address_space_dispatch_free() now.

This makes mem_begin/mem_commit to work with ASD and mem_add with FV
as later on mem_add will be taking FV as an argument anyway.

This should cause no behavioural change.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Message-Id: <20170921085110.25598-5-aik@ozlabs.ru>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 66a6df1dc6)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 21:54:59 -06:00
Alexey Kardashevskiy de7e6815b8 memory: Move FlatView allocation to a helper
This moves a FlatView allocation and initialization to a helper.
While we are nere, replace g_new with g_new0 to not to bother if we add
new fields in the future.

This should cause no behavioural change.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Message-Id: <20170921085110.25598-4-aik@ozlabs.ru>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit cc94cd6d36)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 21:54:44 -06:00
Alexey Kardashevskiy 1b04a15809 memory: Open code FlatView rendering
We are going to share FlatView's between AddressSpace's and per-AS
memory listeners won't suit the purpose anymore so open code
the dispatch tree rendering.

Since there is a good chance that dispatch_listener was the only
listener, this avoids address_space_update_topology_pass() if there is
no registered listeners; this should improve starting time.

This should cause no behavioural change.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Message-Id: <20170921085110.25598-3-aik@ozlabs.ru>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 9a62e24f45)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 21:54:37 -06:00
Alexey Kardashevskiy 6424975ce9 exec: Explicitly export target AS from address_space_translate_internal
This adds an AS** parameter to address_space_do_translate()
to make it easier for the next patch to share FlatViews.

This should cause no behavioural change.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Message-Id: <20170921085110.25598-2-aik@ozlabs.ru>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit e76bb18f7e)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 21:54:29 -06:00
Eric Blake 4af42e3cf1 block: Perform copy-on-read in loop
Improve our braindead copy-on-read implementation.  Pre-patch,
we have multiple issues:
- we create a bounce buffer and perform a write for the entire
request, even if the active image already has 99% of the
clusters occupied, and really only needs to copy-on-read the
remaining 1% of the clusters
- our bounce buffer was as large as the read request, and can
needlessly exhaust our memory by using double the memory of
the request size (the original request plus our bounce buffer),
rather than a capped maximum overhead beyond the original
- if a driver has a max_transfer limit, we are bypassing the
normal code in bdrv_aligned_preadv() that fragments to that
limit, and instead attempt to read the entire buffer from the
driver in one go, which some drivers may assert on
- a client can request a large request of nearly 2G such that
rounding the request out to cluster boundaries results in a
byte count larger than 2G.  While this cannot exceed 32 bits,
it DOES have some follow-on problems:
-- the call to bdrv_driver_pread() can assert for exceeding
BDRV_REQUEST_MAX_BYTES, if the driver is old and lacks
.bdrv_co_preadv
-- if the buffer is all zeroes, the subsequent call to
bdrv_co_do_pwrite_zeroes is a no-op due to a negative size,
which means we did not actually copy on read

Fix all of these issues by breaking up the action into a loop,
where each iteration is capped to sane limits.  Also, querying
the allocation status allows us to optimize: when data is
already present in the active layer, we don't need to bounce.

Note that the code has a telling comment that copy-on-read
should probably be a filter driver rather than a bolt-on hack
in io.c; but that remains a task for another day.

CC: qemu-stable@nongnu.org
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit cb2e28780c)
 Conflicts:
	block/io.c
* remove context dep on d855ebcd3
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 21:36:39 -06:00
Jim Somerville 26914ce48d kvmclock: use the updated system_timer_msr
Fixes e2b6c17 (kvmclock: update system_time_msr address forcibly)
which makes a call to get the latest value of the address
stored in system_timer_msr, but then uses the old address anyway.

Signed-off-by: Jim Somerville <Jim.Somerville@windriver.com>
Message-Id: <59b67db0bd15a46ab47c3aa657c81a4c11f168ea.1506702472.git.Jim.Somerville@windriver.com>
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 346b1215b1)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 20:42:20 -06:00
Vladimir Sementsov-Ogievskiy 49958d37e7 block/mirror: check backing in bdrv_mirror_top_flush
Backing may be zero after failed bdrv_append in mirror_start_job,
which leads to SIGSEGV.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Message-id: 20170929152255.5431-1-vsementsov@virtuozzo.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit ce960aa906)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 20:40:23 -06:00
Thomas Huth b234266086 hw/usb/bus: Remove bad object_unparent() from usb_try_create_simple()
Valgrind detects an invalid read operation when hot-plugging of an
USB device fails:

$ valgrind x86_64-softmmu/qemu-system-x86_64 -device usb-ehci -nographic -S
==30598== Memcheck, a memory error detector
==30598== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==30598== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==30598== Command: x86_64-softmmu/qemu-system-x86_64 -device usb-ehci -nographic -S
==30598==
QEMU 2.10.50 monitor - type 'help' for more information
(qemu) device_add usb-tablet
(qemu) device_add usb-tablet
(qemu) device_add usb-tablet
(qemu) device_add usb-tablet
(qemu) device_add usb-tablet
(qemu) device_add usb-tablet
==30598== Invalid read of size 8
==30598==    at 0x60EF50: object_unparent (object.c:445)
==30598==    by 0x580F0D: usb_try_create_simple (bus.c:346)
==30598==    by 0x581BEB: usb_claim_port (bus.c:451)
==30598==    by 0x582310: usb_qdev_realize (bus.c:257)
==30598==    by 0x4CB399: device_set_realized (qdev.c:914)
==30598==    by 0x60E26D: property_set_bool (object.c:1886)
==30598==    by 0x61235E: object_property_set_qobject (qom-qobject.c:27)
==30598==    by 0x61000F: object_property_set_bool (object.c:1162)
==30598==    by 0x4567C3: qdev_device_add (qdev-monitor.c:630)
==30598==    by 0x456D52: qmp_device_add (qdev-monitor.c:807)
==30598==    by 0x470A99: hmp_device_add (hmp.c:1933)
==30598==    by 0x3679C3: handle_hmp_command (monitor.c:3123)

The object_unparent() here is not necessary anymore since commit
69382d8b3e ("qdev: Fix object reference leak in case device.realize()
fails"), so let's remove it now.

Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Thomas Huth <thuth@redhat.com>
Message-id: 1506526106-30971-1-git-send-email-thuth@redhat.com
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
(cherry picked from commit f3b2bea3c7)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-12-04 20:37:19 -06:00
Daniel Henrique Barboza 62695f60c3 hw/ppc: CAS reset on early device hotplug
This patch is a follow up on the discussions made in patch
"hw/ppc: disable hotplug before CAS is completed" that can be
found at [1].

At this moment, we do not support CPU/memory hotplug in early
boot stages, before CAS. When a hotplug occurs, the event is logged
in an internal RTAS event log queue and an IRQ pulse is fired. In
regular conditions, the guest handles the interrupt by executing
check_exception, fetching the generated hotplug event and enabling
the device for use.

In early boot, this IRQ isn't caught (SLOF does not handle hotplug
events), leaving the event in the rtas event log queue. If the guest
executes check_exception due to another hotplug event, the re-assertion
of the IRQ ends up de-queuing the first hotplug event as well. In short,
a device hotplugged before CAS is considered coldplugged by SLOF.
This leads to device misbehavior and, in some cases, guest kernel
Ooops when trying to unplug the device.

A proper fix would be to turn every device hotplugged before CAS
as a colplugged device. This is not trivial to do with the current
code base though - the FDT is written in the guest memory at
ppc_spapr_reset and can't be retrieved without adding extra state
(fdt_size for example) that will need to managed and migrated. Adding
the hotplugged DT in the middle of CAS negotiation via the updated DT
tree works with CPU devs, but panics the guest kernel at boot. Additional
analysis would be necessary for LMBs and PCI devices. There are
questions to be made in QEMU/SLOF/kernel level about how we can make
this change in a sustainable way.

With Linux guests, a fix would be the kernel executing check_exception
at boot time, de-queueing the events that happened in early boot and
processing them. However, even if/when the newer kernels start
fetching these events at boot time, we need to take care of older
kernels that won't be doing that.

This patch works around the situation by issuing a CAS reset if a hotplugged
device is detected during CAS:

- the DRC conditions that warrant a CAS reset is the same as those that
triggers a DRC migration - the DRC must have a device attached and
the DRC state is not equal to its ready_state. With that in mind, this
patch makes use of 'spapr_drc_needed' to determine if a CAS reset
is needed.

- In the middle of CAS negotiations, the function
'spapr_hotplugged_dev_before_cas' goes through all the DRCs to see
if there are any DRC that requires a reset, using spapr_drc_needed. If
that happens, returns '1' in 'spapr_h_cas_compose_response' which will set
spapr->cas_reboot to true, causing the machine to reboot.

No changes are made for coldplug devices.

[1] http://lists.nongnu.org/archive/html/qemu-devel/2017-08/msg02855.html

Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
(cherry picked from commit 10f12e6450)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-10-03 17:40:40 -05:00
Michael Roth 7851197b81 Update version for 2.10.1 release
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-10-02 12:42:58 -05:00
Peter Lieven 547435f550 migration: disable auto-converge during bulk block migration
auto-converge and block migration currently do not play well together.
During block migration the auto-converge logic detects that ram
migration makes no progress and thus throttles down the vm until
it nearly stalls completely. Avoid this by disabling the throttling
logic during the bulk phase of the block migration.

Cc: qemu-stable@nongnu.org
Signed-off-by: Peter Lieven <pl@kamp.de>
Message-Id: <1506421996-12513-1-git-send-email-pl@kamp.de>
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
(cherry picked from commit 9ac78b6171)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-28 16:55:44 -05:00
Christian Borntraeger 17cd46fbdf s390x/cpumodel: remove ais from z14 default model-> also for 2.10.1
We disabled ais for 2.10, so let's also remove it from the z14
default model.

Fixes: 3f2d07b3b0 ("s390x/ais: for 2.10 stable: disable ais facility")
CC: qemu-stable@nongnu.org
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Message-Id: <20170927072030.35737-2-borntraeger@de.ibm.com>
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
(cherry picked from commit 9dacc90846)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-28 16:55:08 -05:00
Anthony PERARD 6a903482b1 Revert "ACPI: don't call acpi_pcihp_device_plug_cb on xen"
This reverts commit 153eba4726.

This patch prevents PCI passthrough hotplug on Xen. Even if the Xen tool
stack prepares its own ACPI tables, we still rely on QEMU for hotplug
ACPI notifications.

The original issue is fixed by the two previous patch:
  hw/acpi: Limit hotplug to root bus on legacy mode
  hw/acpi: Move acpi_set_pci_info to pcihp

Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit 2bed1ba77f)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-28 16:53:20 -05:00
Anthony PERARD 8edf4c6adc hw/acpi: Move acpi_set_pci_info to pcihp
HW part of ACPI PCI hotplug in QEMU depends on ACPI_PCIHP_PROP_BSEL
being set on a PCI bus that supports ACPI hotplug. It should work
regardless of the source of ACPI tables (QEMU generator/legacy SeaBIOS/Xen).
So move ACPI_PCIHP_PROP_BSEL initialization into HW ACPI implementation
part from QEMU's ACPI table generator.

To do PCI passthrough with Xen, the property ACPI_PCIHP_PROP_BSEL needs
to be set, but this was done only when ACPI tables are built which is
not needed for a Xen guest. The need for the property starts with commit
"pc: pcihp: avoid adding ACPI_PCIHP_PROP_BSEL twice"
(f0c9d64a68).

Adding find_i440fx into stubs so that mips-softmmu target can be built.

Reported-by: Sander Eikelenboom <linux@eikelenboom.it>
Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit ab938ae43f)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-28 16:53:11 -05:00
Anthony PERARD 2c3a8cc581 hw/acpi: Limit hotplug to root bus on legacy mode
Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit f5855994fe)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-28 16:53:04 -05:00
Stefan Hajnoczi 0691b70a2a nbd-client: avoid read_reply_co entry if send failed
The following segfault is encountered if the NBD server closes the UNIX
domain socket immediately after negotiation:

  Program terminated with signal SIGSEGV, Segmentation fault.
  #0  aio_co_schedule (ctx=0x0, co=0xd3c0ff2ef0) at util/async.c:441
  441       QSLIST_INSERT_HEAD_ATOMIC(&ctx->scheduled_coroutines,
  (gdb) bt
  #0  0x000000d3c01a50f8 in aio_co_schedule (ctx=0x0, co=0xd3c0ff2ef0) at util/async.c:441
  #1  0x000000d3c012fa90 in nbd_coroutine_end (bs=bs@entry=0xd3c0fec650, request=<optimized out>) at block/nbd-client.c:207
  #2  0x000000d3c012fb58 in nbd_client_co_preadv (bs=0xd3c0fec650, offset=0, bytes=<optimized out>, qiov=0x7ffc10a91b20, flags=0) at block/nbd-client.c:237
  #3  0x000000d3c0128e63 in bdrv_driver_preadv (bs=bs@entry=0xd3c0fec650, offset=offset@entry=0, bytes=bytes@entry=512, qiov=qiov@entry=0x7ffc10a91b20, flags=0) at block/io.c:836
  #4  0x000000d3c012c3e0 in bdrv_aligned_preadv (child=child@entry=0xd3c0ff51d0, req=req@entry=0x7f31885d6e90, offset=offset@entry=0, bytes=bytes@entry=512, align=align@entry=1, qiov=qiov@entry=0x7ffc10a91b20, f
+lags=0) at block/io.c:1086
  #5  0x000000d3c012c6b8 in bdrv_co_preadv (child=0xd3c0ff51d0, offset=offset@entry=0, bytes=bytes@entry=512, qiov=qiov@entry=0x7ffc10a91b20, flags=flags@entry=0) at block/io.c:1182
  #6  0x000000d3c011cc17 in blk_co_preadv (blk=0xd3c0ff4f80, offset=0, bytes=512, qiov=0x7ffc10a91b20, flags=0) at block/block-backend.c:1032
  #7  0x000000d3c011ccec in blk_read_entry (opaque=0x7ffc10a91b40) at block/block-backend.c:1079
  #8  0x000000d3c01bbb96 in coroutine_trampoline (i0=<optimized out>, i1=<optimized out>) at util/coroutine-ucontext.c:79
  #9  0x00007f3196cb8600 in __start_context () at /lib64/libc.so.6

The problem is that nbd_client_init() uses
nbd_client_attach_aio_context() -> aio_co_schedule(new_context,
client->read_reply_co).  Execution of read_reply_co is deferred to a BH
which doesn't run until later.

In the mean time blk_co_preadv() can be called and nbd_coroutine_end()
calls aio_wake() on read_reply_co.  At this point in time
read_reply_co's ctx isn't set because it has never been entered yet.

This patch simplifies the nbd_co_send_request() ->
nbd_co_receive_reply() -> nbd_coroutine_end() lifecycle to just
nbd_co_send_request() -> nbd_co_receive_reply().  The request is "ended"
if an error occurs at any point.  Callers no longer have to invoke
nbd_coroutine_end().

This cleanup also eliminates the segfault because we don't call
aio_co_schedule() to wake up s->read_reply_co if sending the request
failed.  It is only necessary to wake up s->read_reply_co if a reply was
received.

Note this only happens with UNIX domain sockets on Linux.  It doesn't
seem possible to reproduce this with TCP sockets.

Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-Id: <20170829122745.14309-2-stefanha@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>
(cherry picked from commit 3c2d5183f9)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-28 16:52:37 -05:00
Alex Bennée 4d824886c8 accel/tcg/cputlb: avoid recursive BQL (fixes #1706296)
The mmio path (see exec.c:prepare_mmio_access) already protects itself
against recursive locking and it makes sense to do the same for
io_readx/writex. Otherwise any helper running in the BQL context will
assert when it attempts to write to device memory as in the case of
the bug report.

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
CC: Richard Jones <rjones@redhat.com>
CC: Paolo Bonzini <bonzini@gnu.org>
CC: qemu-stable@nongnu.org
Message-Id: <20170921110625.9500-1-alex.bennee@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
(cherry picked from commit 8b81253332)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-28 16:52:09 -05:00
Vladimir Sementsov-Ogievskiy 780fb4ce48 block/qcow2-bitmap: fix use of uninitialized pointer
Without initialization to zero dirty_bitmap field may be not zero
for a bitmap which should not be stored and
qcow2_store_persistent_dirty_bitmaps will erroneously call
store_bitmap for it which leads to SIGSEGV on bdrv_dirty_bitmap_name.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Message-id: 20170922144353.4220-1-vsementsov@virtuozzo.com
Cc: qemu-stable@nongnu.org
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit 5330f32b71)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-28 16:51:42 -05:00
Manos Pitsidianakis 7496699ba6 block/throttle-groups.c: allocate RestartData on the heap
RestartData is the opaque data of the throttle_group_restart_queue_entry
coroutine. By being stack allocated, it isn't available anymore if
aio_co_enter schedules the coroutine with a bottom half and runs after
throttle_group_restart_queue returns.

Cc: qemu-stable@nongnu.org
Signed-off-by: Manos Pitsidianakis <el13635@mail.ntua.gr>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Alberto Garcia <berto@igalia.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 43a5dc02fd)
 Conflicts:
	block/throttle-groups.c
* reworked to avoid functional dep on 022cdc9, since that involves
  refactoring for a feature not present in 2.10
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-28 16:49:39 -05:00
Eric Blake 33a599667a osdep: Fix ROUND_UP(64-bit, 32-bit)
When using bit-wise operations that exploit the power-of-two
nature of the second argument of ROUND_UP(), we still need to
ensure that the mask is as wide as the first argument (done
by using a ternary to force proper arithmetic promotion).
Unpatched, ROUND_UP(2ULL*1024*1024*1024*1024, 512U) produces 0,
instead of the intended 2TiB, because negation of an unsigned
32-bit quantity followed by widening to 64-bits does not
sign-extend the mask.

Broken since its introduction in commit 292c8e50 (v1.5.0).
Callers that passed the same width type to both macro parameters,
or that had other code to ensure the first parameter's maximum
runtime value did not exceed the second parameter's width, are
unaffected, but I did not audit to see which (if any) existing
clients of the macro could trigger incorrect behavior (I found
the bug while adding a new use of the macro).

While preparing the patch, checkpatch complained about poor
spacing, so I also fixed that here and in the nearby DIV_ROUND_UP.

CC: qemu-trivial@nongnu.org
CC: qemu-stable@nongnu.org
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
(cherry picked from commit 2098b073f3)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-28 16:37:36 -05:00
Christian Borntraeger a432f419ab s390x/ais: for 2.10 stable: disable ais facility
The migration interface for ais was introduced with kernel 4.13
but the capability itself had been active since 4.12. As migration
support is considered necessary lets disable ais in the 2.10
stable version. A proper fix and re-enablement will be done
for qemu 2.11.

Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Message-Id: <20170921140834.14233-2-borntraeger@de.ibm.com>
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
(cherry picked from commit 3f2d07b3b0)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-22 18:23:37 -05:00
Jan Dakinevich a83858fdb5 9pfs: check the size of transport buffer before marshaling
v9fs_do_readdir_with_stat() should check for a maximum buffer size
before an attempt to marshal gathered data. Otherwise, buffers assumed
as misconfigured and the transport would be broken.

The patch brings v9fs_do_readdir_with_stat() in conformity with
v9fs_do_readdir() behavior.

Signed-off-by: Jan Dakinevich <jan.dakinevich@gmail.com>
[groug, regression caused my commit 8d37de41ca # 2.10]
Signed-off-by: Greg Kurz <groug@kaod.org>

(cherry picked from commit 772a73692e)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-22 18:23:16 -05:00
Jan Dakinevich d13a0bde83 9pfs: fix name_to_path assertion in v9fs_complete_rename()
The third parameter of v9fs_co_name_to_path() must not contain `/'
character.

The issue is most likely related to 9p2000.u protocol only.

Signed-off-by: Jan Dakinevich <jan.dakinevich@gmail.com>
[groug, regression caused by commit f57f587857 # 2.10]
Signed-off-by: Greg Kurz <groug@kaod.org>

(cherry picked from commit 4d8bc7334b)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-22 18:23:11 -05:00
Jan Dakinevich e90997dc8f 9pfs: fix readdir() for 9p2000.u
If the client is using 9p2000.u, the following occurs:

$ cd ${virtfs_shared_dir}
$ mkdir -p a/b/c
$ ls a/b
ls: cannot access 'a/b/a': No such file or directory
ls: cannot access 'a/b/b': No such file or directory
a  b  c

instead of the expected:

$ ls a/b
c

This is a regression introduced by commit f57f5878578a;
local_name_to_path() now resolves ".." and "." in paths,
and v9fs_do_readdir_with_stat()->stat_to_v9stat() then
copies the basename of the resulting path to the response.
With the example above, this means that "." and ".." are
turned into "b" and "a" respectively...

stat_to_v9stat() currently assumes it is passed a full
canonicalized path and uses it to do two different things:
1) to pass it to v9fs_co_readlink() in case the file is a symbolic
   link
2) to set the name field of the V9fsStat structure to the basename
   part of the given path

It only has two users: v9fs_stat() and v9fs_do_readdir_with_stat().

v9fs_stat() really needs 1) and 2) to be performed since it starts
with the full canonicalized path stored in the fid. It is different
for v9fs_do_readdir_with_stat() though because the name we want to
put into the V9fsStat structure is the d_name field of the dirent
actually (ie, we want to keep the "." and ".." special names). So,
we only need 1) in this case.

This patch hence adds a basename argument to stat_to_v9stat(), to
be used to set the name field of the V9fsStat structure, and moves
the basename logic to v9fs_stat().

Signed-off-by: Jan Dakinevich <jan.dakinevich@gmail.com>
(groug, renamed old name argument to path and updated changelog)
Signed-off-by: Greg Kurz <groug@kaod.org>

(cherry picked from commit 6069537f43)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-22 18:23:05 -05:00
Gerd Hoffmann 7e1288cd0c console: fix dpy_gfx_replace_surface assert
virtio-gpu can trigger the assert added by commit "6905b93447 console:
add same surface replace pre-condition" in multihead setups (where
surface can be NULL for secondary displays).  Allow surface being NULL.

Fixes: 6905b93447
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-id: 20170906142109.2685-1-kraxel@redhat.com
(cherry picked from commit 1540008629)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-22 18:13:37 -05:00
Igor Mammedov 83b23fe55c ide: ahci: unparent children buses before freeing their memory
Fixes read after freeing error reported
  https://lists.gnu.org/archive/html/qemu-devel/2017-08/msg04243.html
  Message-Id: <59a56959-ca12-ea75-33fa-ff07eba1b090@redhat.com>

ich9-ahci device creates ide buses and attaches them as QOM children
at realize time, however it forgets to properly clean them up
at unrealize time and frees memory containing these children,
with following call-chain:

   qdev_device_add()
     object_property_set_bool('realized', true)
       device_set_realized()
          ...
          pci_qdev_realize() -> pci_ich9_ahci_realize() -> ahci_realize()
               ...
               s->dev = g_new0(AHCIDevice, ports);
               ...
                  AHCIDevice *ad = &s->dev[i];
                  ide_bus_new(&ad->port, sizeof(ad->port), qdev, i, 1);
                  ^^^ creates bus in memory allocated by above gnew()
                      and adds it as child propety to ahci device
          ...
          hotplug_handler_plug(); -> goto post_realize_fail;
          pci_qdev_unrealize() -> pci_ich9_uninit() -> ahci_uninit()
              ...
               g_free(s->dev);
               ^^^ free memory that holds children busses

          return with error from device_set_realized()

As result later when qdev_device_add() tries to unparent ich9-ahci
after failed device_set_realized(),
    object_unparent() -> object_property_del_child()
iterates over existing QOM children including buses added by
ide_bus_new() and tries to unparent them, which causes access to
freed memory where they where located.

Reported-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Tested-by: Thomas Huth <thuth@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Message-id: 1503938085-169486-1-git-send-email-imammedo@redhat.com
Signed-off-by: John Snow <jsnow@redhat.com>
(cherry picked from commit 955f5c7ba1)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-22 18:12:41 -05:00
Thomas Huth e96002e0d1 hw/ide/microdrive: Mark the dscm1xxxx device with user_creatable = false
QEMU currently aborts with an assertion message when the user is trying
to remove a dscm1xxxx again:

$ aarch64-softmmu/qemu-system-aarch64 -S -M integratorcp -nographic
QEMU 2.9.93 monitor - type 'help' for more information
(qemu) device_add dscm1xxxx,id=xyz
(qemu) device_del xyz
**
ERROR:qemu/qdev-monitor.c:872:qdev_unplug: assertion failed: (hotplug_ctrl)
Aborted (core dumped)

Looks like this device has to be wired up in code and is not meant
to be hot-pluggable, so let's mark it with user_creatable = false.

Signed-off-by: Thomas Huth <thuth@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Message-id: 1503543783-17192-1-git-send-email-thuth@redhat.com
Signed-off-by: John Snow <jsnow@redhat.com>
(cherry picked from commit 4c93950659)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-22 18:12:35 -05:00
Thomas Huth cc7dd3ad3f hw/arm/aspeed_soc: Mark devices as user_creatable = false
QEMU currently aborts if the user is accidentially trying to
do something like this:

$ aarch64-softmmu/qemu-system-aarch64 -S -M integratorcp -nographic
QEMU 2.9.93 monitor - type 'help' for more information
(qemu) device_add ast2400
Unexpected error in error_set_from_qdev_prop_error()
 at hw/core/qdev-properties.c:1032:
Aborted (core dumped)

The ast2400 SoC devices are clearly not creatable by the user since
they are using the serial_hds and nd_table arrays directly in their
realize function, so mark them with user_creatable = false.

Signed-off-by: Thomas Huth <thuth@redhat.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 469f3da42e)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-22 18:12:31 -05:00
Thomas Huth de4ad17a8e hw/arm/digic: Mark device with user_creatable = false
QEMU currently shows some unexpected behavior when the user trys to
do a "device_add digic" on an unrelated ARM machine like integratorcp
in "-nographic" mode (the device_add command does not immediately
return to the monitor prompt), and trying to "device_del" the device
later results in a "qemu/qdev-monitor.c:872:qdev_unplug: assertion
failed: (hotplug_ctrl)" error condition.
Looking at the realize function of the device, it uses serial_hds
directly and this means that the device can not be added a second
time, so let's simply mark it with "user_creatable = false" now.

Signed-off-by: Thomas Huth <thuth@redhat.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit f58f25599b)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-22 18:12:26 -05:00
Thomas Huth 8a9d7f3063 s390x/ipl: The s390-ipl device is not hot-pluggable
The s390-ipl device can not be created by the user, since it is meant only
to  be instantiated once internally to load the ROMs and kernel. If the user
tries to do a "device_add s390-ipl" via the monitor later, QEMU aborts with
a "ROM images must be loaded at startup" error message.

Signed-off-by: Thomas Huth <thuth@redhat.com>
Message-Id: <1502861458-30270-1-git-send-email-thuth@redhat.com>
Reviewed-by: David Hildenbrand <david@redhat.com>
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
(cherry picked from commit 0d4fa4996f)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-22 18:12:09 -05:00
Thomas Huth d3f05848fc watchdog/wdt_diag288: Mark diag288 watchdog as non-hotpluggable
QEMU currently aborts when the user tries to hot-unplug a diag288
device:

$ qemu-system-s390x -nographic -nodefaults -S -monitor stdio
QEMU 2.9.92 monitor - type 'help' for more information
(qemu) device_add diag288,id=x
(qemu) device_del x
**
ERROR:qemu/qdev-monitor.c:872:qdev_unplug: assertion failed: (hotplug_ctrl)
Aborted (core dumped)

The device is not designed as hot-pluggable (it should only be used
via the "-watchdog" parameter), so let's simply remove the possibility
to hotplug it to prevent that users can run into this ugly situation.

Signed-off-by: Thomas Huth <thuth@redhat.com>
Message-Id: <1502892528-22618-1-git-send-email-thuth@redhat.com>
Reviewed-by: David Hildenbrand <david@redhat.com>
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
(cherry picked from commit 84ebd3e8c7)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-22 18:12:04 -05:00
Prasad J Pandit fca5f37fe9 multiboot: validate multiboot header address values
While loading kernel via multiboot-v1 image, (flags & 0x00010000)
indicates that multiboot header contains valid addresses to load
the kernel image. These addresses are used to compute kernel
size and kernel text offset in the OS image. Validate these
address values to avoid an OOB access issue.

This is CVE-2017-14167.

Reported-by: Thomas Garnier <thgarnie@google.com>
Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
Message-Id: <20170907063256.7418-1-ppandit@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit ed4f86e8b6)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-22 18:11:49 -05:00
Gerd Hoffmann 2965be1f00 vga: stop passing pointers to vga_draw_line* functions
Instead pass around the address (aka offset into vga memory).
Add vga_read_* helper functions which apply vbe_size_mask to
the address, to make sure the address stays within the valid
range, similar to the cirrus blitter fixes (commits ffaf857778
and 026aeffcb4).

Impact:  DoS for privileged guest users.  qemu crashes with
a segfault, when hitting the guard page after vga memory
allocation, while reading vga memory for display updates.

Fixes: CVE-2017-13672
Cc: P J P <ppandit@redhat.com>
Reported-by: David Buchanan <d@vidbuchanan.co.uk>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Message-id: 20170828122906.18993-1-kraxel@redhat.com
(cherry picked from commit 3d90c62548)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-22 18:11:23 -05:00
Gerd Hoffmann d6f7f3b0cf vga: fix display update region calculation (split screen)
vga display update mis-calculated the region for the dirty bitmap
snapshot in case split screen mode is used.  This can trigger an
assert in cpu_physical_memory_snapshot_get_dirty().

Impact:  DoS for privileged guest users.

Fixes: CVE-2017-13673
Fixes: fec5e8c92b
Cc: P J P <ppandit@redhat.com>
Reported-by: David Buchanan <d@vidbuchanan.co.uk>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Message-id: 20170828123307.15392-1-kraxel@redhat.com
(cherry picked from commit e65294157d)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-22 18:11:17 -05:00
Marc-André Lureau 2a2eab6660 vhost-user-bridge: fix resume regression (since 2.9)
Commit e10e798c85 switched to libvhost-user which lacked support
for resuming the avail_idx based on used_idx.

Fixes:
https://bugzilla.redhat.com/show_bug.cgi?id=1485867

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit 672339f7ef)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-19 17:48:37 -05:00
Marc-André Lureau 48f65ce837 libvhost-user: support resuming vq->last_avail_idx based on used_idx
This is the same workaround as commit 523b018dde, which was lost
with libvhost-user transition in commit e10e798c85.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit 35480cbfcb)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-19 17:48:27 -05:00
Hannes Reinecke b95fbe6f12 scsi-bus: correct responses for INQUIRY and REQUEST SENSE
According to SPC-3 INQUIRY and REQUEST SENSE should return GOOD
even on unsupported LUNS.

Signed-off-by: Hannes Reinecke <hare@suse.com>
Message-Id: <1503049022-14749-1-git-send-email-hare@suse.de>
Reported-by: Laszlo Ersek <lersek@redhat.com>
Fixes: ded6ddc5a7
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Hannes Reinecke <hare@suse.de>
(cherry picked from commit b07fbce634)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-19 17:44:14 -05:00
Peter Maydell b8cd978919 mps2-an511: Fix wiring of UART overflow interrupt lines
Fix an error that meant we were wiring every UART's overflow
interrupts into the same inputs 0 and 1 of the OR gate,
rather than giving each its own input.

Cc: qemu-stable@nongnu.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Reviewed-by: Alistair Francis <alistair.francis@xilinx.com>
Message-id: 1505232834-20890-1-git-send-email-peter.maydell@linaro.org
(cherry picked from commit ce3bc112cd)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-17 15:02:04 -05:00
Alex Williamson b24304ca13 vhost: Release memory references on cleanup
vhost registers a MemoryListener where it adds and removes references
to MemoryRegions as the MemoryRegionSections pass through.  The
region_add callback is invoked for each existing section when the
MemoryListener is registered, but unregistering the MemoryListener
performs no reciprocal region_del callback.  It's therefore the
owner of the MemoryListener's responsibility to cleanup any persistent
changes, such as these memory references, after unregistering.

The consequence of this bug is that if we have both a vhost device
and a vfio device, the vhost device will reference any mmap'd MMIO of
the vfio device via this MemoryListener.  If the vhost device is then
removed, those references remain outstanding.  If we then attempt to
remove the vfio device, it never gets finalized and the only way to
release the kernel file descriptors is to terminate the QEMU process.

Fixes: dfde4e6e1a ("memory: add ref/unref calls")
Cc: Michael S. Tsirkin <mst@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: qemu-stable@nongnu.org # v1.6.0+
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit ee4c112846)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-14 19:31:09 -05:00
Pavel Butsykin c6841b112e qcow2: move qcow2_store_persistent_dirty_bitmaps() before cache flushing
After calling qcow2_inactivate(), all qcow2 caches must be flushed, but this
may not happen, because the last call qcow2_store_persistent_dirty_bitmaps()
can lead to marking l2/refcont cache as dirty.

Let's move qcow2_store_persistent_dirty_bitmaps() before the caсhe flushing
to fix it.

Cc: qemu-stable@nongnu.org
Signed-off-by: Pavel Butsykin <pbutsykin@virtuozzo.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 83a8c775a8)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-14 19:29:40 -05:00
Thomas Huth 65a24b5c44 hw/arm/allwinner-a10: Mark the allwinner-a10 device with user_creatable = false
QEMU currently exits unexpectedly when the user accidentially
tries to do something like this:

$ aarch64-softmmu/qemu-system-aarch64 -S -M integratorcp -nographic
QEMU 2.9.93 monitor - type 'help' for more information
(qemu) device_add allwinner-a10
Unsupported NIC model: smc91c111

Exiting just due to a "device_add" should not happen. Looking closer
at the the realize and instance_init function of this device also
reveals that it is using serial_hds and nd_table directly there, so
this device is clearly not creatable by the user and should be marked
accordingly.

Signed-off-by: Thomas Huth <thuth@redhat.com>
Reviewed-by: Eduardo Habkost <ehabkost@redhat.com>
Message-id: 1503416789-32080-1-git-send-email-thuth@redhat.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit dc89a180ca)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-14 19:26:40 -05:00
Pranith Kumar 85cdc23e75 arm_gicv3_kvm: Fix compile warning
Fix the following warning:

/home/pranith/qemu/hw/intc/arm_gicv3_kvm.c:296:17: warning: logical not is only applied to the left hand side of this bitwise operator [-Wlogical-not-parentheses]
            if (!c->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) {
                ^             ~
/home/pranith/qemu/hw/intc/arm_gicv3_kvm.c:296:17: note: add parentheses after the '!' to evaluate the bitwise operator first
            if (!c->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) {
                ^
/home/pranith/qemu/hw/intc/arm_gicv3_kvm.c:296:17: note: add parentheses around left hand side expression to silence this warning
            if (!c->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) {
                ^

This logic error meant we were not setting the PTZ
bit when we should -- luckily as the comment suggests
this wouldn't have had any effects beyond making GIC
initialization take a little longer.

Signed-off-by: Pranith Kumar <bobby.prani@gmail.com>
Message-id: 20170829173226.7625-1-bobby.prani@gmail.com
Cc: qemu-stable@nongnu.org
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 7229ec5825)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-14 19:24:05 -05:00
Greg Kurz 168ff32c5d virtfs: error out gracefully when mandatory suboptions are missing
We internally convert -virtfs to -fsdev/-device. If the user doesn't
provide the path or security_model suboptions, and the fsdev backend
requires them, we hit an assertion when populating the internal -fsdev
option:

util/qemu-option.c:547: opt_set: Assertion `opt->str' failed.
Aborted (core dumped)

Let's test the suboption presence on the command line before trying
to set it in the internal -fsdev option, and let the backend code
error out gracefully (ie, like it already does when the user passes
-fsdev on the command line).

Reported-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Greg Kurz <groug@kaod.org>
Reviewed-by: Thomas Huth <thuth@redhat.com>
(cherry picked from commit 32b6943699)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-12 11:25:01 -05:00
Richard Henderson 728bfa3273 target/arm: Fix aa64 ldp register writeback
For "ldp x0, x1, [x0]", if the second load is on a second page and
the second page is unmapped, the exception would be raised with x0
already modified.  This means the instruction couldn't be restarted.

Cc: qemu-arm@nongnu.org
Cc: qemu-stable@nongnu.org
Reported-by: Andrew <andrew@fubar.geek.nz>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20170825224833.4463-1-richard.henderson@linaro.org
Fixes: https://bugs.launchpad.net/qemu/+bug/1713066
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
[PMM: tweaked comment format]
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

(cherry picked from commit 3e4d91b94c)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-12 11:24:55 -05:00
Farhan Ali e1b4750f06 s390-ccw: Fix alignment for CCW1
The commit 198c0d1f9d s390x/css: check ccw address validity
exposes an alignment issue in ccw bios.

According to PoP the CCW must be doubleword aligned. Let's fix
this in the bios.

Cc: qemu-stable@nongnu.org
Signed-off-by: Farhan Ali <alifm@linux.vnet.ibm.com>
Reviewed-by: Halil Pasic <pasic@linux.vnet.ibm.com>
Reviewed-by: Eric Farman <farman@linux.vnet.ibm.com>
Acked-by: Christian Borntraeger <borntraeger@de.ibm.com>
Message-Id: <3ed8b810b6592daee6a775037ce21f850e40647d.1503667215.git.alifm@linux.vnet.ibm.com>
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
(cherry picked from commit 3a1e4561ad)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-12 11:24:49 -05:00
Samuel Thibault 53d421dd9c slirp: fix clearing ifq_so from pending packets
The if_fastq and if_batchq contain not only packets, but queues of packets
for the same socket. When sofree frees a socket, it thus has to clear ifq_so
from all the packets from the queues, not only the first.

Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Cc: qemu-stable@nongnu.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 1201d30851)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2017-09-12 11:24:43 -05:00
5978 changed files with 253046 additions and 831354 deletions

No files matched your search

-27
View File
@@ -1,27 +0,0 @@
env:
CIRRUS_CLONE_DEPTH: 1
freebsd_12_task:
freebsd_instance:
image: freebsd-12-0-release-amd64
cpu: 8
memory: 8G
install_script: pkg install -y
bash bison curl cyrus-sasl git glib gmake gnutls gsed
nettle perl5 pixman pkgconf png usbredir
script:
- mkdir build
- cd build
- ../configure || { cat config.log; exit 1; }
- gmake -j8
- gmake -j8 V=1 check
macos_task:
osx_instance:
image: mojave-base
install_script:
- brew install pkg-config python gnu-sed glib pixman make sdl2
script:
- ./configure --python=/usr/local/bin/python3 || { cat config.log; exit 1; }
- gmake -j$(sysctl -n hw.ncpu)
- gmake check -j$(sysctl -n hw.ncpu)
+1 -29
View File
@@ -1,10 +1,4 @@
# EditorConfig is a file format and collection of text editor plugins
# for maintaining consistent coding styles between different editors
# and IDEs. Most popular editors support this either natively or via
# plugin.
#
# Check https://editorconfig.org for details.
# http://editorconfig.org
root = true
[*]
@@ -12,32 +6,10 @@ end_of_line = lf
insert_final_newline = true
charset = utf-8
[*.mak]
indent_style = tab
indent_size = 8
file_type_emacs = makefile
[Makefile*]
indent_style = tab
indent_size = 8
file_type_emacs = makefile
[*.{c,h}]
indent_style = space
indent_size = 4
[*.sh]
indent_style = space
indent_size = 4
[*.{s,S}]
indent_style = tab
indent_size = 8
file_type_emacs = asm
[*.{vert,frag}]
file_type_emacs = glsl
[*.json]
indent_style = space
file_type_emacs = python
+8 -32
View File
@@ -1,4 +1,3 @@
/.doctrees
/config-devices.*
/config-all-devices.*
/config-all-disas.*
@@ -6,7 +5,6 @@
/config-target.*
/config.status
/config-temp
/elf2dmp
/trace-events-all
/trace/generated-events.h
/trace/generated-events.c
@@ -16,8 +14,6 @@
/trace/generated-tcg-tracers.h
/ui/shader/texture-blit-frag.h
/ui/shader/texture-blit-vert.h
/ui/shader/texture-blit-flip-vert.h
/ui/input-keymap-*.c
*-timestamp
/*-softmmu
/*-darwin-user
@@ -29,24 +25,16 @@
/libuser
/linux-headers/asm
/qga/qapi-generated
/qapi-gen-timestamp
/qapi/qapi-builtin-types.[ch]
/qapi/qapi-builtin-visit.[ch]
/qapi/qapi-commands-*.[ch]
/qapi/qapi-commands.[ch]
/qapi/qapi-emit-events.[ch]
/qapi/qapi-events-*.[ch]
/qapi/qapi-events.[ch]
/qapi/qapi-introspect.[ch]
/qapi/qapi-types-*.[ch]
/qapi/qapi-types.[ch]
/qapi/qapi-visit-*.[ch]
/qapi/qapi-visit.[ch]
/qapi/qapi-doc.texi
/qapi-generated
/qapi-types.[ch]
/qapi-visit.[ch]
/qapi-event.[ch]
/qmp-commands.h
/qmp-introspect.[ch]
/qmp-marshal.c
/qemu-doc.html
/qemu-doc.info
/qemu-doc.txt
/qemu-edid
/qemu-img
/qemu-nbd
/qemu-options.def
@@ -56,17 +44,14 @@
/qemu-io
/qemu-ga
/qemu-bridge-helper
/qemu-keymap
/qemu-monitor.texi
/qemu-monitor-info.texi
/qemu-version.h
/qemu-version.h.tmp
/module_block.h
/scsi/qemu-pr-helper
/vscclient
/vhost-user-scsi
/vhost-user-blk
/fsdev/virtfs-proxy-helper
*.tmp
*.[1-9]
*.a
*.aux
@@ -95,9 +80,7 @@
.sdk
*.gcda
*.gcno
*.gcov
/pc-bios/bios-pq/status
/pc-bios/edk2-*.fd
/pc-bios/vgabios-pq/status
/pc-bios/optionrom/linuxboot.asm
/pc-bios/optionrom/linuxboot.bin
@@ -107,10 +90,6 @@
/pc-bios/optionrom/linuxboot_dma.bin
/pc-bios/optionrom/linuxboot_dma.raw
/pc-bios/optionrom/linuxboot_dma.img
/pc-bios/optionrom/pvh.asm
/pc-bios/optionrom/pvh.bin
/pc-bios/optionrom/pvh.raw
/pc-bios/optionrom/pvh.img
/pc-bios/optionrom/multiboot.asm
/pc-bios/optionrom/multiboot.bin
/pc-bios/optionrom/multiboot.raw
@@ -121,7 +100,6 @@
/pc-bios/optionrom/kvmvapic.img
/pc-bios/s390-ccw/s390-ccw.elf
/pc-bios/s390-ccw/s390-ccw.img
/docs/built
/docs/interop/qemu-ga-qapi.texi
/docs/interop/qemu-ga-ref.html
/docs/interop/qemu-ga-ref.info*
@@ -133,7 +111,6 @@
/docs/version.texi
*.tps
.stgit-*
.git-submodule-status
cscope.*
tags
TAGS
@@ -157,4 +134,3 @@ trace-dtrace-root.h
trace-dtrace-root.dtrace
trace-ust-all.h
trace-ust-all.c
/target/arm/decode-sve.inc.c
-88
View File
@@ -1,88 +0,0 @@
before_script:
- apt-get update -qq
- apt-get install -y -qq flex bison libglib2.0-dev libpixman-1-dev genisoimage
build-system1:
script:
- apt-get install -y -qq libgtk-3-dev libvte-dev nettle-dev libcacard-dev
libusb-dev libvde-dev libspice-protocol-dev libgl1-mesa-dev
- ./configure --enable-werror --target-list="aarch64-softmmu alpha-softmmu
cris-softmmu hppa-softmmu lm32-softmmu moxie-softmmu microblazeel-softmmu
mips64el-softmmu m68k-softmmu ppc-softmmu riscv64-softmmu sparc-softmmu"
- make -j2
- make -j2 check
build-system2:
script:
- apt-get install -y -qq libsdl2-dev libgcrypt-dev libbrlapi-dev libaio-dev
libfdt-dev liblzo2-dev librdmacm-dev libibverbs-dev libibumad-dev
- ./configure --enable-werror --target-list="tricore-softmmu unicore32-softmmu
microblaze-softmmu mips-softmmu riscv32-softmmu s390x-softmmu sh4-softmmu
sparc64-softmmu x86_64-softmmu xtensa-softmmu nios2-softmmu or1k-softmmu"
- make -j2
- make -j2 check
build-disabled:
script:
- ./configure --enable-werror --disable-rdma --disable-slirp --disable-curl
--disable-capstone --disable-live-block-migration --disable-glusterfs
--disable-replication --disable-coroutine-pool --disable-smartcard
--disable-guest-agent --disable-curses --disable-libxml2 --disable-tpm
--disable-qom-cast-debug --disable-spice --disable-vhost-vsock
--disable-vhost-net --disable-vhost-crypto --disable-vhost-user
--target-list="i386-softmmu ppc64-softmmu mips64-softmmu i386-linux-user"
- make -j2
- make -j2 check-qtest SPEED=slow
build-tcg-disabled:
script:
- apt-get install -y -qq clang libgtk-3-dev libbluetooth-dev libusb-dev
- ./configure --cc=clang --enable-werror --disable-tcg --audio-drv-list=""
- make -j2
- make check-unit
- make check-qapi-schema
- cd tests/qemu-iotests/
- ./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 147 148 150 151 152 157 159 160
163 170 171 183 184 192 194 197 205 208 215 221 222 226 227 236
- ./check -qcow2 001 002 003 004 005 007 008 009 010 011 012 013 017 018 019
020 021 022 024 025 027 028 029 031 032 033 034 035 036 037 038
039 040 042 043 046 047 048 049 050 051 052 053 054 056 057 058
060 061 062 063 065 066 067 068 069 071 072 073 074 079 080 082
085 086 089 090 091 095 096 097 098 099 102 103 104 105 107 108
110 111 114 117 120 122 124 126 127 129 130 132 133 134 137 138
139 140 141 142 143 144 145 147 150 151 152 154 155 156 157 158
161 165 170 172 174 176 177 179 184 186 187 190 192 194 195 196
197 200 202 203 205 208 209 214 215 216 217 218 222 226 227 229 234
build-user:
script:
- ./configure --enable-werror --disable-system --disable-guest-agent
--disable-capstone --disable-slirp --disable-fdt
- make -j2
- make run-tcg-tests-i386-linux-user run-tcg-tests-x86_64-linux-user
build-clang:
script:
- apt-get install -y -qq clang libsdl2-dev
xfslibs-dev libiscsi-dev libnfs-dev libseccomp-dev gnutls-dev librbd-dev
- ./configure --cc=clang --cxx=clang++ --enable-werror
--target-list="alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu
ppc-softmmu s390x-softmmu x86_64-softmmu arm-linux-user"
- make -j2
- make -j2 check
build-tci:
script:
- TARGETS="aarch64 alpha arm hppa m68k microblaze moxie ppc64 s390x x86_64"
- ./configure --enable-tcg-interpreter
--target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)"
- make -j2
- make tests/boot-serial-test tests/cdrom-test tests/pxe-test
- for tg in $TARGETS ; do
export QTEST_QEMU_BINARY="${tg}-softmmu/qemu-system-${tg}" ;
./tests/boot-serial-test || exit 1 ;
./tests/cdrom-test || exit 1 ;
done
- QTEST_QEMU_BINARY="x86_64-softmmu/qemu-system-x86_64" ./tests/pxe-test
- QTEST_QEMU_BINARY="s390x-softmmu/qemu-system-s390x" ./tests/pxe-test -m slow
+17 -38
View File
@@ -1,60 +1,39 @@
[submodule "roms/vgabios"]
path = roms/vgabios
url = git://git.qemu-project.org/vgabios.git/
[submodule "roms/seabios"]
path = roms/seabios
url = https://git.qemu.org/git/seabios.git/
url = git://git.qemu-project.org/seabios.git/
[submodule "roms/SLOF"]
path = roms/SLOF
url = https://git.qemu.org/git/SLOF.git
url = git://git.qemu-project.org/SLOF.git
[submodule "roms/ipxe"]
path = roms/ipxe
url = https://git.qemu.org/git/ipxe.git
url = git://git.qemu-project.org/ipxe.git
[submodule "roms/openbios"]
path = roms/openbios
url = https://git.qemu.org/git/openbios.git
url = git://git.qemu-project.org/openbios.git
[submodule "roms/openhackware"]
path = roms/openhackware
url = https://git.qemu.org/git/openhackware.git
url = git://git.qemu-project.org/openhackware.git
[submodule "roms/qemu-palcode"]
path = roms/qemu-palcode
url = https://git.qemu.org/git/qemu-palcode.git
url = git://github.com/rth7680/qemu-palcode.git
[submodule "roms/sgabios"]
path = roms/sgabios
url = https://git.qemu.org/git/sgabios.git
url = git://git.qemu-project.org/sgabios.git
[submodule "pixman"]
path = pixman
url = git://anongit.freedesktop.org/pixman
[submodule "dtc"]
path = dtc
url = https://git.qemu.org/git/dtc.git
url = git://git.qemu-project.org/dtc.git
[submodule "roms/u-boot"]
path = roms/u-boot
url = https://git.qemu.org/git/u-boot.git
url = git://git.qemu-project.org/u-boot.git
[submodule "roms/skiboot"]
path = roms/skiboot
url = https://git.qemu.org/git/skiboot.git
url = git://git.qemu.org/skiboot.git
[submodule "roms/QemuMacDrivers"]
path = roms/QemuMacDrivers
url = https://git.qemu.org/git/QemuMacDrivers.git
[submodule "ui/keycodemapdb"]
path = ui/keycodemapdb
url = https://git.qemu.org/git/keycodemapdb.git
[submodule "capstone"]
path = capstone
url = https://git.qemu.org/git/capstone.git
[submodule "roms/seabios-hppa"]
path = roms/seabios-hppa
url = https://git.qemu.org/git/seabios-hppa.git
[submodule "roms/u-boot-sam460ex"]
path = roms/u-boot-sam460ex
url = https://git.qemu.org/git/u-boot-sam460ex.git
[submodule "tests/fp/berkeley-testfloat-3"]
path = tests/fp/berkeley-testfloat-3
url = https://git.qemu.org/git/berkeley-testfloat-3.git
[submodule "tests/fp/berkeley-softfloat-3"]
path = tests/fp/berkeley-softfloat-3
url = https://git.qemu.org/git/berkeley-softfloat-3.git
[submodule "roms/edk2"]
path = roms/edk2
url = https://git.qemu.org/git/edk2.git
[submodule "slirp"]
path = slirp
url = https://git.qemu.org/git/libslirp.git
[submodule "roms/opensbi"]
path = roms/opensbi
url = https://git.qemu.org/git/opensbi.git
url = git://git.qemu.org/QemuMacDrivers.git
-51
View File
@@ -1,51 +0,0 @@
#
# Common git-publish profiles that can be used to send patches to QEMU upstream.
#
# See https://github.com/stefanha/git-publish for more information
#
[gitpublishprofile "default"]
base = master
to = qemu-devel@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "rfc"]
base = master
prefix = RFC PATCH
to = qemu-devel@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "stable"]
base = master
to = qemu-devel@nongnu.org
cc = qemu-stable@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "trivial"]
base = master
to = qemu-devel@nongnu.org
cc = qemu-trivial@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "block"]
base = master
to = qemu-devel@nongnu.org
cc = qemu-block@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "arm"]
base = master
to = qemu-devel@nongnu.org
cc = qemu-arm@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "s390"]
base = master
to = qemu-devel@nongnu.org
cc = qemu-s390@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
[gitpublishprofile "ppc"]
base = master
to = qemu-devel@nongnu.org
cc = qemu-ppc@nongnu.org
cccmd = scripts/get_maintainer.pl --noroles --norolestats --nogit --nogit-fallback 2>/dev/null
+3 -25
View File
@@ -1,7 +1,6 @@
# This mailmap fixes up author names/addresses.
# The first section translates weird addresses from the original git import
# into proper addresses so that they are counted properly by git shortlog.
# This mailmap just translates the weird addresses from the original import into git
# into proper addresses so that they are counted properly in git shortlog output.
#
Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
Anthony Liguori <anthony@codemonkey.ws> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162>
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
@@ -9,31 +8,10 @@ Aurelien Jarno <aurelien@aurel32.net> aurel32 <aurel32@c046a42c-6fe2-441c-8c8c-7
Blue Swirl <blauwirbel@gmail.com> blueswir1 <blueswir1@c046a42c-6fe2-441c-8c8c-71466251a162>
Edgar E. Iglesias <edgar.iglesias@gmail.com> edgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162>
Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com>
Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com>
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@mips.com>
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@imgtec.com>
Paul Burton <pburton@wavecomp.com> <paul.burton@mips.com>
Paul Burton <pburton@wavecomp.com> <paul.burton@imgtec.com>
Paul Burton <pburton@wavecomp.com> <paul@archlinuxmips.org>
Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>
# There is also a:
# (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162>
# for the cvs2svn initialization commit e63c3dc74bf.
# Next, translate a few commits where mailman rewrote the From: line due
# to strict SPF, although we prefer to avoid adding more entries like that.
Ed Swierk <eswierk@skyportsystems.com> Ed Swierk via Qemu-devel <qemu-devel@nongnu.org>
Ian McKellar <ianloic@google.com> Ian McKellar via Qemu-devel <qemu-devel@nongnu.org>
Julia Suvorova <jusual@mail.ru> Julia Suvorova via Qemu-devel <qemu-devel@nongnu.org>
Justin Terry (VM) <juterry@microsoft.com> Justin Terry (VM) via Qemu-devel <qemu-devel@nongnu.org>
# Also list preferred name forms where people have changed their
# git author config, or had utf8/latin1 encoding issues.
Daniel P. Berrangé <berrange@redhat.com>
Reimar Döffinger <Reimar.Doeffinger@gmx.de>
-302
View File
@@ -1,302 +0,0 @@
---
# Note: this file is still unused. It serves as a documentation for the
# Patchew configuration in case patchew.org disappears or has to be
# reinstalled.
#
# Patchew configuration is available to project administrators at
# https://patchew.org/api/v1/projects/1/config/ and can be configured
# to YAML using the following Python script:
#
# import json
# import sys
# import ruamel.yaml
#
# json_str = sys.stdin.read()
# yaml = ruamel.yaml.YAML()
# yaml.explicit_start = True
# data = json.loads(json_str, object_pairs_hook=ruamel.yaml.comments.CommentedMap)
# ruamel.yaml.scalarstring.walk_tree(data)
# yaml.dump(data, sys.stdout)
email:
notifications:
timeouts:
event: TestingReport
enabled: true
to_user: false
reply_subject: true
set_reply_to: true
in_reply_to: true
reply_to_all: false
subject_template: none
to: fam@euphon.net
cc: ''
body_template: |
{% if not is_timeout %} {{ cancel }} {% endif %}
Test '{{ test }}' timeout, log:
{{ log }}
ENOSPC:
event: TestingReport
enabled: true
to_user: false
reply_subject: false
set_reply_to: false
in_reply_to: true
reply_to_all: false
subject_template: Out of space error
to: fam@euphon.net
cc: ''
body_template: |
{% if passed %}
{{ cancel }}
{% endif %}
{% if 'No space left on device' in log %}
Tester {{ tester }} out of space when running {{ test }}
{{ log }}
{% else %}
{{ cancel }}
{% endif %}
FailureShort:
event: TestingReport
enabled: true
to_user: false
reply_subject: true
set_reply_to: true
in_reply_to: true
reply_to_all: true
subject_template: Testing failed
to: ''
cc: ''
body_template: |
{% if passed or not obj.message_id or is_timeout %}
{{ cancel }}
{% endif %}
{% if 'No space left on device' in log %}
{{ cancel }}
{% endif %}
Patchew URL: https://patchew.org/QEMU/{{ obj.message_id }}/
{% ansi2text log as logtext %}
{% if test == "checkpatch" %}
Hi,
This series seems to have some coding style problems. See output below for
more information:
{{ logtext }}
{% elif test == "docker-mingw@fedora" or test == "docker-quick@centos7" or test == "asan" %}
Hi,
This series failed the {{ test }} build test. Please find the testing commands and
their output below. If you have Docker installed, you can probably reproduce it
locally.
{% lines_between logtext start="^=== TEST SCRIPT BEGIN ===$" stop="^=== TEST SCRIPT END ===$" %}
{% lines_between logtext start="^=== OUTPUT BEGIN ===$" stop="=== OUTPUT END ===$" as output %}
{% grep_C output regex="\b(FAIL|XPASS|ERROR|WARN|error:|warning:)" n=3 %}
{% elif test == "s390x" or test == "FreeBSD" or test == "ppcle" or test == "ppcbe" %}
Hi,
This series failed build test on {{test}} host. Please find the details below.
{% lines_between logtext start="^=== TEST SCRIPT BEGIN ===$" stop="^=== TEST SCRIPT END ===$" %}
{% lines_between logtext start="^=== OUTPUT BEGIN ===$" stop="=== OUTPUT END ===$" as output %}
{% grep_C output regex="\b(FAIL|XPASS|ERROR|WARN|error:|warning:)" n=3 %}
{% else %}
{{ cancel }}
{% endif %}
The full log is available at
{{ log_url }}.
---
Email generated automatically by Patchew [https://patchew.org/].
Please send your feedback to patchew-devel@redhat.com
testing:
tests:
asan:
enabled: true
requirements: docker
timeout: 3600
script: |
#!/bin/bash
time make docker-test-debug@fedora TARGET_LIST=x86_64-softmmu J=14 NETWORK=1
docker-quick@centos7:
enabled: false
requirements: docker,x86_64
timeout: 3600
script: |
#!/bin/bash
time make docker-test-quick@centos7 SHOW_ENV=1 J=14 NETWORK=1
checkpatch:
enabled: true
requirements: ''
timeout: 600
script: |
#!/bin/bash
git rev-parse base > /dev/null || exit 0
git config --local diff.renamelimit 0
git config --local diff.renames True
git config --local diff.algorithm histogram
./scripts/checkpatch.pl --mailback base..
docker-mingw@fedora:
enabled: true
requirements: docker,x86_64
timeout: 3600
script: |
#! /bin/bash
test "$(uname -m)" = "x86_64"
ppcle:
enabled: false
requirements: ppcle
timeout: 3600
script: |
#!/bin/bash
# Testing script will be invoked under the git checkout with
# HEAD pointing to a commit that has the patches applied on top of "base"
# branch
set -e
CC=$HOME/bin/cc
INSTALL=$PWD/install
BUILD=$PWD/build
mkdir -p $BUILD $INSTALL
SRC=$PWD
cd $BUILD
$SRC/configure --cc=$CC --prefix=$INSTALL
make -j4
# XXX: we need reliable clean up
# make check -j4 V=1
make install
echo
echo "=== ENV ==="
env
echo
echo "=== PACKAGES ==="
rpm -qa
ppcbe:
enabled: false
requirements: ppcbe
timeout: 3600
script: |
#!/bin/bash
# Testing script will be invoked under the git checkout with
# HEAD pointing to a commit that has the patches applied on top of "base"
# branch
set -e
CC=$HOME/bin/cc
INSTALL=$PWD/install
BUILD=$PWD/build
mkdir -p $BUILD $INSTALL
SRC=$PWD
cd $BUILD
$SRC/configure --cc=$CC --prefix=$INSTALL
make -j4
# XXX: we need reliable clean up
# make check -j4 V=1
make install
echo
echo "=== ENV ==="
env
echo
echo "=== PACKAGES ==="
rpm -qa
FreeBSD:
enabled: true
requirements: qemu-x86,x86_64,git
timeout: 3600
script: |
#!/bin/bash
# Testing script will be invoked under the git checkout with
# HEAD pointing to a commit that has the patches applied on top of "base"
# branch
if qemu-system-x86_64 --help >/dev/null 2>&1; then
QEMU=qemu-system-x86_64
elif /usr/libexec/qemu-kvm --help >/dev/null 2>&1; then
QEMU=/usr/libexec/qemu-kvm
else
exit 1
fi
make vm-build-freebsd J=21 QEMU=$QEMU
exit 0
docker-clang@ubuntu:
enabled: true
requirements: docker,x86_64
timeout: 3600
script: |
#!/bin/bash
time make docker-test-clang@ubuntu SHOW_ENV=1 J=14 NETWORK=1
s390x:
enabled: true
requirements: s390x
timeout: 3600
script: |
#!/bin/bash
# Testing script will be invoked under the git checkout with
# HEAD pointing to a commit that has the patches applied on top of "base"
# branch
set -e
CC=$HOME/bin/cc
INSTALL=$PWD/install
BUILD=$PWD/build
mkdir -p $BUILD $INSTALL
SRC=$PWD
cd $BUILD
$SRC/configure --cc=$CC --prefix=$INSTALL
make -j4
# XXX: we need reliable clean up
# make check -j4 V=1
make install
echo
echo "=== ENV ==="
env
echo
echo "=== PACKAGES ==="
rpm -qa
requirements:
x86_64:
script: |
#! /bin/bash
test "$(uname -m)" = "x86_64"
qemu-x86:
script: |
#!/bin/bash
if qemu-system-x86_64 --help >/dev/null 2>&1; then
:
elif /usr/libexec/qemu-kvm --help >/dev/null 2>&1; then
:
else
exit 1
fi
ppcle:
script: |
#!/bin/bash
test "$(uname -m)" = "ppc64le"
ppcbe:
script: |
#!/bin/bash
test "$(uname -m)" = "ppc64"
git:
script: |
#! /bin/bash
git config user.name > /dev/null 2>&1
docker:
script: |
#!/bin/bash
docker ps || sudo -n docker ps
s390x:
script: |
#!/bin/bash
test "$(uname -m)" = "s390x"
git:
push_to: git@github.com:patchew-project/qemu
public_repo: https://github.com/patchew-project/qemu
url_template: https://github.com/patchew-project/qemu/tree/%t
+10
View File
@@ -23,6 +23,8 @@ env:
TARGET_LIST=mips-softmmu,mipsel-linux-user
- IMAGE=debian-mips64el-cross
TARGET_LIST=mips64el-softmmu,mips64el-linux-user
- IMAGE=debian-powerpc-cross
TARGET_LIST=ppc-softmmu,ppcemb-softmmu,ppc-linux-user
- IMAGE=debian-ppc64el-cross
TARGET_LIST=ppc64-softmmu,ppc64-linux-user,ppc64abi32-linux-user
build:
@@ -35,5 +37,13 @@ build:
options: "-e HOME=/root"
ci:
- unset CC
# some targets require newer up to date packages, for example TARGET_LIST matching
# aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu)
# see the configure script:
# error_exit "DTC (libfdt) version >= 1.4.2 not present. Your options:"
# " (1) Preferred: Install the DTC (libfdt) devel package"
# " (2) Fetch the DTC submodule, using:"
# " git submodule update --init dtc"
- dpkg --compare-versions `dpkg-query --showformat='${Version}' --show libfdt-dev` ge 1.4.2 || git submodule update --init dtc
- ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST}
- make -j$(($(getconf _NPROCESSORS_ONLN) + 1))
+108 -208
View File
@@ -1,13 +1,10 @@
# The current Travis default is a VM based 16.04 Xenial on GCE
# Additional builds with specific requirements for a full VM need to
# be added as additional matrix: entries later on
dist: xenial
sudo: false
language: c
python:
- "2.4"
compiler:
- gcc
cache: ccache
addons:
apt:
packages:
@@ -16,13 +13,12 @@ addons:
- libattr1-dev
- libbrlapi-dev
- libcap-ng-dev
- libgcc-4.8-dev
- libgnutls-dev
- libgtk-3-dev
- libiscsi-dev
- liblttng-ust-dev
- libncurses5-dev
- libnfs-dev
- libncurses5-dev
- libnss3-dev
- libpixman-1-dev
- libpng12-dev
@@ -31,20 +27,12 @@ addons:
- libseccomp-dev
- libspice-protocol-dev
- libspice-server-dev
- libssh-dev
- libssh2-1-dev
- liburcu-dev
- libusb-1.0-0-dev
- libvte-2.91-dev
- libvte-2.90-dev
- sparse
- uuid-dev
- gcovr
homebrew:
packages:
- glib
- pixman
- gnu-sed
update: true
# The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu
# to prevent IRC notifications from forks. This was created using:
@@ -55,203 +43,126 @@ notifications:
- secure: "F7GDRgjuOo5IUyRLqSkmDL7kvdU4UcH3Lm/W2db2JnDHTGCqgEdaYEYKciyCLZ57vOTsTsOgesN8iUT7hNHBd1KWKjZe9KDTZWppWRYVwAwQMzVeSOsbbU4tRoJ6Pp+3qhH1Z0eGYR9ZgKYAoTumDFgSAYRp4IscKS8jkoedOqM="
on_success: change
on_failure: always
env:
global:
- SRC_DIR="."
- BUILD_DIR="."
- BASE_CONFIG="--disable-docs --disable-tools"
- TEST_CMD="make check -j3 V=1"
# This is broadly a list of "mainline" softmmu targets which have support across the major distros
- MAIN_SOFTMMU_TARGETS="aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
- TEST_CMD="make check"
- MAKEFLAGS="-j3"
matrix:
- CONFIG=""
- CONFIG="--enable-debug --enable-debug-tcg --enable-trace-backends=log"
- CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-uuid --disable-libusb"
- CONFIG="--enable-modules"
- CONFIG="--with-coroutine=ucontext"
- CONFIG="--with-coroutine=sigaltstack"
git:
# we want to do this ourselves
submodules: false
before_install:
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update ; fi
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install libffi gettext glib pixman ; fi
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
- git submodule update --init --recursive
before_script:
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
- ${SRC_DIR}/configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; }
- ./configure ${CONFIG}
script:
- make -j3 && travis_retry ${TEST_CMD}
- make ${MAKEFLAGS} && ${TEST_CMD}
matrix:
include:
- env:
- CONFIG="--disable-system --static"
# we split the system builds as it takes a while to build them all
- env:
- CONFIG="--disable-user --target-list=${MAIN_SOFTMMU_TARGETS}"
- env:
- CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
# Just build tools and run minimal unit and softfloat checks
- env:
- BASE_CONFIG="--enable-tools"
- CONFIG="--disable-user --disable-system"
- TEST_CMD="make check-unit check-softfloat -j3"
- env:
- CONFIG="--enable-debug --enable-debug-tcg --disable-user"
# TCG debug can be run just on it's own and is mostly agnostic to user/softmmu distinctions
- env:
- CONFIG="--enable-debug-tcg --disable-system"
- env:
- CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-libusb --disable-replication --target-list=${MAIN_SOFTMMU_TARGETS}"
# Module builds are mostly of interest to major distros
- env:
- CONFIG="--enable-modules --target-list=${MAIN_SOFTMMU_TARGETS}"
# Alternate coroutines implementations are only really of interest to KVM users
# However we can't test against KVM on Travis so we can only run unit tests
- env:
- CONFIG="--with-coroutine=ucontext --disable-tcg"
- TEST_CMD="make check-unit -j3 V=1"
- env:
- CONFIG="--with-coroutine=sigaltstack --disable-tcg"
- TEST_CMD="make check-unit -j3 V=1"
# Check we can build docs and tools (out of tree)
- env:
- BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.."
- BASE_CONFIG="--enable-tools --enable-docs"
- CONFIG="--target-list=x86_64-softmmu,aarch64-linux-user"
addons:
apt:
packages:
- python-sphinx
- texinfo
- perl
# Test with Clang for compile portability (Travis uses clang-5.0)
- env:
- CONFIG="--disable-system"
# Test with CLang for compile portability
- env: CONFIG=""
compiler: clang
- env:
- CONFIG="--disable-user --target-list=${MAIN_SOFTMMU_TARGETS}"
compiler: clang
- env:
- CONFIG="--target-list=${MAIN_SOFTMMU_TARGETS} "
compiler: clang
before_script:
- ./configure ${CONFIG} --extra-cflags="-fsanitize=undefined -Werror" || { cat config.log && exit 1; }
- env:
- CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
compiler: clang
# gprof/gcov are GCC features
- env:
- CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=${MAIN_SOFTMMU_TARGETS}"
after_success:
- ${SRC_DIR}/scripts/travis/coverage-summary.sh
- env: CONFIG="--enable-gprof --enable-gcov --disable-pie"
compiler: gcc
# We manually include builds which we disable "make check" for
- env:
- CONFIG="--without-default-devices --disable-user"
- TEST_CMD=""
# We manually include builds which we disable "make check" for
- env:
- CONFIG="--enable-debug --enable-tcg-interpreter"
- TEST_CMD=""
# We don't need to exercise every backend with every front-end
- env:
- CONFIG="--enable-trace-backends=log,simple,syslog --disable-system"
- TEST_CMD=""
- env:
- CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu"
- TEST_CMD=""
- env:
- CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu"
- TEST_CMD=""
# MacOSX builds
- env:
- CONFIG="--target-list=${MAIN_SOFTMMU_TARGETS}"
- env: CONFIG="--enable-debug --enable-tcg-interpreter"
TEST_CMD=""
compiler: gcc
- env: CONFIG="--enable-trace-backends=simple"
TEST_CMD=""
compiler: gcc
- env: CONFIG="--enable-trace-backends=ftrace"
TEST_CMD=""
compiler: gcc
- env: CONFIG="--enable-trace-backends=ust"
TEST_CMD=""
compiler: gcc
- env: CONFIG="--disable-tcg"
TEST_CMD=""
compiler: gcc
- env: CONFIG=""
os: osx
osx_image: xcode9.4
compiler: clang
- env:
- CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu"
os: osx
osx_image: xcode10.2
compiler: clang
# Python builds
- env:
- CONFIG="--target-list=x86_64-softmmu"
language: python
python:
- "3.4"
- env:
- CONFIG="--target-list=x86_64-softmmu"
language: python
python:
- "3.6"
# Acceptance (Functional) tests
- env:
- CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu,mips-softmmu,mips64el-softmmu,aarch64-softmmu,arm-softmmu,s390x-softmmu,alpha-softmmu"
- TEST_CMD="make check-acceptance"
after_failure:
- cat tests/results/latest/job.log
# Plain Trusty System Build
- env: CONFIG="--disable-linux-user"
sudo: required
addons:
apt:
packages:
- python3-pip
- python3.5-venv
dist: trusty
compiler: gcc
before_install:
- sudo apt-get update -qq
- sudo apt-get build-dep -qq qemu
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
- git submodule update --init --recursive
# Plain Trusty Linux User Build
- env: CONFIG="--disable-system"
sudo: required
addons:
dist: trusty
compiler: gcc
before_install:
- sudo apt-get update -qq
- sudo apt-get build-dep -qq qemu
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
- git submodule update --init --recursive
# Trusty System build with latest stable clang
- sudo: required
addons:
dist: trusty
language: generic
compiler: none
env:
- COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9
- CONFIG="--disable-linux-user --cc=clang-3.9 --cxx=clang++-3.9"
before_install:
- wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
- sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main'
- sudo apt-get update -qq
- sudo apt-get install -qq -y clang-3.9
- sudo apt-get build-dep -qq qemu
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
- git submodule update --init --recursive
before_script:
- ./configure ${CONFIG} || cat config.log
# Trusty Linux User build with latest stable clang
- sudo: required
addons:
dist: trusty
language: generic
compiler: none
env:
- COMPILER_NAME=clang CXX=clang++-3.9 CC=clang-3.9
- CONFIG="--disable-system --cc=clang-3.9 --cxx=clang++-3.9"
before_install:
- wget -nv -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
- sudo apt-add-repository -y 'deb http://llvm.org/apt/trusty llvm-toolchain-trusty-3.9 main'
- sudo apt-get update -qq
- sudo apt-get install -qq -y clang-3.9
- sudo apt-get build-dep -qq qemu
- wget -O - http://people.linaro.org/~alex.bennee/qemu-submodule-git-seed.tar.xz | tar -xvJ
- git submodule update --init --recursive
before_script:
- ./configure ${CONFIG} || cat config.log
# Using newer GCC with sanitizers
- addons:
apt:
update: true
sources:
# PPAs for newer toolchains
- ubuntu-toolchain-r-test
packages:
# Extra toolchains
- gcc-9
- g++-9
- gcc-5
- g++-5
# Build dependencies
- libaio-dev
- libattr1-dev
@@ -271,28 +182,17 @@ matrix:
- libseccomp-dev
- libspice-protocol-dev
- libspice-server-dev
- libssh-dev
- libssh2-1-dev
- liburcu-dev
- libusb-1.0-0-dev
- libvte-2.91-dev
- libvte-2.90-dev
- sparse
- uuid-dev
language: generic
compiler: none
env:
- COMPILER_NAME=gcc CXX=g++-9 CC=gcc-9
- CONFIG="--cc=gcc-9 --cxx=g++-9 --disable-pie --disable-linux-user"
- COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5
- CONFIG="--cc=gcc-5 --cxx=g++-5 --disable-pie --disable-linux-user"
- TEST_CMD=""
before_script:
- ./configure ${CONFIG} --extra-cflags="-g3 -O0 -Wno-error=stringop-truncation -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; }
# Run check-tcg against linux-user
- env:
- CONFIG="--disable-system"
- TEST_CMD="make -j3 check-tcg V=1"
# Run check-tcg against softmmu targets
- env:
- CONFIG="--target-list=xtensa-softmmu,arm-softmmu,aarch64-softmmu,alpha-softmmu"
- TEST_CMD="make -j3 check-tcg V=1"
- ./configure ${CONFIG} --extra-cflags="-g3 -O0 -fsanitize=thread -fuse-ld=gold" || cat config.log
+4 -60
View File
@@ -29,45 +29,6 @@ Spaces of course are superior to tabs because:
Do not leave whitespace dangling off the ends of lines.
1.1 Multiline Indent
There are several places where indent is necessary:
- if/else
- while/for
- function definition & call
When breaking up a long line to fit within line width, we need a proper indent
for the following lines.
In case of if/else, while/for, align the secondary lines just after the
opening parenthesis of the first.
For example:
if (a == 1 &&
b == 2) {
while (a == 1 &&
b == 2) {
In case of function, there are several variants:
* 4 spaces indent from the beginning
* align the secondary lines just after the opening parenthesis of the
first
For example:
do_something(x, y,
z);
do_something(x, y,
z);
do_something(x, do_another(y,
z));
2. Line width
Lines should be 80 characters; try not to make them longer.
@@ -147,10 +108,10 @@ block to a separate function altogether.
When comparing a variable for (in)equality with a constant, list the
constant on the right, as in:
if (a == 1) {
/* Reads like: "If a equals 1" */
do_something();
}
if (a == 1) {
/* Reads like: "If a equals 1" */
do_something();
}
Rationale: Yoda conditions (as in 'if (1 == a)') are awkward to read.
Besides, good compilers already warn users when '==' is mis-typed as '=',
@@ -163,23 +124,6 @@ We use traditional C-style /* */ comments and avoid // comments.
Rationale: The // form is valid in C99, so this is purely a matter of
consistency of style. The checkpatch script will warn you about this.
Multiline comment blocks should have a row of stars on the left,
and the initial /* and terminating */ both on their own lines:
/*
* like
* this
*/
This is the same format required by the Linux kernel coding style.
(Some of the existing comments in the codebase use the GNU Coding
Standards form which does not have stars on the left, or other
variations; avoid these when writing new comments, but don't worry
about converting to the preferred form unless you're editing that
comment anyway.)
Rationale: Consistency, and ease of visually picking out a multiline
comment from the surrounding code.
8. trace-events style
8.1 0x prefix
+12 -10
View File
@@ -1,8 +1,8 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -10,7 +10,7 @@
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
@@ -112,7 +112,7 @@ modification follow. Pay close attention to the difference between a
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
@@ -146,7 +146,7 @@ such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
@@ -432,7 +432,7 @@ decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
@@ -455,7 +455,7 @@ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
@@ -476,7 +476,7 @@ convey the exclusion of warranty; and each file should have at least the
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.1 of the License, or (at your option) any later version.
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
@@ -485,7 +485,7 @@ convey the exclusion of warranty; and each file should have at least the
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
@@ -500,3 +500,5 @@ necessary. Here is a sample; alter the names:
Ty Coon, President of Vice
That's all there is to it!
+1 -1
View File
@@ -1,6 +1,6 @@
This file documents changes for QEMU releases 0.12 and earlier.
For changelog information for later releases, see
https://wiki.qemu.org/ChangeLog or look at the git history for
http://wiki.qemu-project.org/ChangeLog or look at the git history for
more detailed information.
-9
View File
@@ -118,15 +118,6 @@ Please note that g_malloc will exit on allocation failure, so there
is no need to test for failure (as you would have to with malloc).
Calling g_malloc with a zero size is valid and will return NULL.
Prefer g_new(T, n) instead of g_malloc(sizeof(T) * n) for the following
reasons:
a. It catches multiplication overflowing size_t;
b. It returns T * instead of void *, letting compiler catch more type
errors.
Declarations like T *v = g_malloc(sizeof(*v)) are acceptable, though.
Memory allocated by qemu_memalign or qemu_blockalign must be freed with
qemu_vfree, since breaking this will cause problems on Win32.
-36
View File
@@ -1,36 +0,0 @@
# These are "proxy" symbols used to pass config-host.mak values
# down to Kconfig. See also MINIKCONF_ARGS in the Makefile:
# these two need to be kept in sync.
config KVM
bool
config LINUX
bool
config OPENGL
bool
config X11
bool
config SPICE
bool
config IVSHMEM
bool
config TPM
bool
config VHOST_USER
bool
config XEN
bool
config VIRTFS
bool
config PVRDMA
bool
+8 -13
View File
@@ -1,18 +1,13 @@
The QEMU distribution includes both the QEMU emulator and
various firmware files. These are separate programs that are
distributed together for our users' convenience, and they have
separate licenses.
The following points clarify the QEMU license:
The following points clarify the license of the QEMU emulator:
1) QEMU as a whole is released under the GNU General Public License,
version 2.
1) The QEMU emulator as a whole is released under the GNU General
Public License, version 2.
2) Parts of the QEMU emulator have specific licenses which are compatible
with the GNU General Public License, version 2. Hence each source file
contains its own licensing information. Source files with no licensing
information are released under the GNU General Public License, version
2 or (at your option) any later version.
2) Parts of QEMU have specific licenses which are compatible with the
GNU General Public License, version 2. Hence each source file contains
its own licensing information. Source files with no licensing information
are released under the GNU General Public License, version 2 or (at your
option) any later version.
As of July 2013, contributions under version 2 of the GNU General Public
License (and no later version) are only accepted for the following files
+271 -1038
View File
File diff suppressed because it is too large. Load diff
+198 -526
View File
File diff suppressed because it is too large. Load diff
+49 -84
View File
@@ -1,21 +1,17 @@
#######################################################################
# Common libraries for tools and emulators
stub-obj-y = stubs/ util/ crypto/
stub-obj-y = stubs/ crypto/
util-obj-y = util/ qobject/ qapi/
util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
chardev-obj-y = chardev/
#######################################################################
# authz-obj-y is code used by both qemu system emulation and qemu-img
authz-obj-y = authz/
#######################################################################
# block-obj-y is code used by both qemu system emulation and qemu-img
block-obj-y = nbd/
block-obj-y += block.o blockjob.o job.o
block-obj-y += block/ scsi/
block-obj-y += nbd/
block-obj-y += block.o blockjob.o
block-obj-y += block/
block-obj-y += qemu-io-cmds.o
block-obj-$(CONFIG_REPLICATION) += replication.o
@@ -25,7 +21,7 @@ block-obj-m = block/
# crypto-obj-y is code used by both qemu system emulation and qemu-img
crypto-obj-y = crypto/
crypto-user-obj-y = crypto/
crypto-aes-obj-y = crypto/
#######################################################################
# qom-obj-y is code used by both qemu system emulation and qemu-img
@@ -45,9 +41,6 @@ io-obj-y = io/
ifeq ($(CONFIG_SOFTMMU),y)
common-obj-y = blockdev.o blockdev-nbd.o block/
common-obj-y += bootdevice.o iothread.o
common-obj-y += dump/
common-obj-y += job-qmp.o
common-obj-y += monitor/
common-obj-y += net/
common-obj-y += qdev-monitor.o device-hotplug.o
common-obj-$(CONFIG_WIN32) += os-win32.o
@@ -58,34 +51,34 @@ common-obj-$(CONFIG_LINUX) += fsdev/
common-obj-y += migration/
common-obj-y += audio/
common-obj-m += audio/
common-obj-y += hw/
common-obj-y += replay/
common-obj-y += ui/
common-obj-m += ui/
common-obj-y += bt-host.o bt-vhci.o
bt-host.o-cflags := $(BLUEZ_CFLAGS)
common-obj-y += dma-helpers.o
common-obj-y += vl.o
vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS)
common-obj-$(CONFIG_TPM) += tpm.o
common-obj-y += tpm.o
common-obj-$(CONFIG_SLIRP) += slirp/
common-obj-y += backends/
common-obj-y += chardev/
common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o
qemu-seccomp.o-cflags := $(SECCOMP_CFLAGS)
qemu-seccomp.o-libs := $(SECCOMP_LIBS)
common-obj-$(CONFIG_FDT) += device_tree.o
######################################################################
# qapi
common-obj-y += qapi/
common-obj-y += qmp-marshal.o
common-obj-y += qmp-introspect.o
common-obj-y += qmp.o hmp.o
endif
#######################################################################
@@ -102,107 +95,79 @@ version-obj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.o
######################################################################
# tracing
util-obj-y += trace/
target-obj-y += trace/
######################################################################
# guest agent
# FIXME: a few definitions from qapi/qapi-types.o and
# qapi/qapi-visit.o are needed by libqemuutil.a. These should be
# extracted into a QAPI schema module, or perhaps a separate schema.
# FIXME: a few definitions from qapi-types.o/qapi-visit.o are needed
# by libqemuutil.a. These should be moved to a separate .json schema.
qga-obj-y = qga/
qga-vss-dll-obj-y = qga/
######################################################################
# contrib
elf2dmp-obj-y = contrib/elf2dmp/
ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/
ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/
libvhost-user-obj-y = contrib/libvhost-user/
vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS)
vhost-user-scsi.o-libs := $(LIBISCSI_LIBS)
vhost-user-scsi-obj-y = contrib/vhost-user-scsi/
vhost-user-blk-obj-y = contrib/vhost-user-blk/
rdmacm-mux-obj-y = contrib/rdmacm-mux/
vhost-user-input-obj-y = contrib/vhost-user-input/
vhost-user-gpu-obj-y = contrib/vhost-user-gpu/
vhost-user-scsi-obj-y += contrib/libvhost-user/libvhost-user.o
######################################################################
trace-events-subdirs =
trace-events-subdirs += accel/kvm
trace-events-subdirs += accel/tcg
trace-events-subdirs += util
trace-events-subdirs += crypto
trace-events-subdirs += monitor
ifeq ($(CONFIG_USER_ONLY),y)
trace-events-subdirs += linux-user
endif
ifeq ($(CONFIG_BLOCK),y)
trace-events-subdirs += authz
trace-events-subdirs += block
trace-events-subdirs += io
trace-events-subdirs += nbd
trace-events-subdirs += scsi
endif
ifeq ($(CONFIG_SOFTMMU),y)
trace-events-subdirs += migration
trace-events-subdirs += block
trace-events-subdirs += chardev
trace-events-subdirs += audio
trace-events-subdirs += hw/9pfs
trace-events-subdirs += hw/acpi
trace-events-subdirs += hw/alpha
trace-events-subdirs += hw/arm
trace-events-subdirs += hw/audio
trace-events-subdirs += hw/block
trace-events-subdirs += hw/block/dataplane
trace-events-subdirs += hw/char
trace-events-subdirs += hw/dma
trace-events-subdirs += hw/hppa
trace-events-subdirs += hw/i2c
trace-events-subdirs += hw/i386
trace-events-subdirs += hw/i386/xen
trace-events-subdirs += hw/ide
trace-events-subdirs += hw/input
trace-events-subdirs += hw/intc
trace-events-subdirs += hw/net
trace-events-subdirs += hw/virtio
trace-events-subdirs += hw/audio
trace-events-subdirs += hw/misc
trace-events-subdirs += hw/usb
trace-events-subdirs += hw/scsi
trace-events-subdirs += hw/nvram
trace-events-subdirs += hw/display
trace-events-subdirs += hw/input
trace-events-subdirs += hw/timer
trace-events-subdirs += hw/dma
trace-events-subdirs += hw/sparc
trace-events-subdirs += hw/sd
trace-events-subdirs += hw/isa
trace-events-subdirs += hw/mem
trace-events-subdirs += hw/mips
trace-events-subdirs += hw/misc
trace-events-subdirs += hw/misc/macio
trace-events-subdirs += hw/net
trace-events-subdirs += hw/nvram
trace-events-subdirs += hw/pci
trace-events-subdirs += hw/pci-host
trace-events-subdirs += hw/i386
trace-events-subdirs += hw/i386/xen
trace-events-subdirs += hw/9pfs
trace-events-subdirs += hw/ppc
trace-events-subdirs += hw/rdma
trace-events-subdirs += hw/rdma/vmw
trace-events-subdirs += hw/pci
trace-events-subdirs += hw/s390x
trace-events-subdirs += hw/scsi
trace-events-subdirs += hw/sd
trace-events-subdirs += hw/sparc
trace-events-subdirs += hw/sparc64
trace-events-subdirs += hw/timer
trace-events-subdirs += hw/tpm
trace-events-subdirs += hw/usb
trace-events-subdirs += hw/vfio
trace-events-subdirs += hw/virtio
trace-events-subdirs += hw/watchdog
trace-events-subdirs += hw/acpi
trace-events-subdirs += hw/arm
trace-events-subdirs += hw/alpha
trace-events-subdirs += hw/xen
trace-events-subdirs += hw/gpio
trace-events-subdirs += hw/riscv
trace-events-subdirs += migration
trace-events-subdirs += net
trace-events-subdirs += ui
endif
trace-events-subdirs += hw/display
trace-events-subdirs += qapi
trace-events-subdirs += qom
trace-events-subdirs += audio
trace-events-subdirs += net
trace-events-subdirs += target/arm
trace-events-subdirs += target/hppa
trace-events-subdirs += target/i386
trace-events-subdirs += target/mips
trace-events-subdirs += target/ppc
trace-events-subdirs += target/riscv
trace-events-subdirs += target/s390x
trace-events-subdirs += target/sparc
trace-events-subdirs += util
trace-events-subdirs += target/s390x
trace-events-subdirs += target/ppc
trace-events-subdirs += qom
trace-events-subdirs += linux-user
trace-events-subdirs += qapi
trace-events-subdirs += accel/tcg
trace-events-subdirs += accel/kvm
trace-events-subdirs += nbd
trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events)
+36 -63
View File
@@ -4,19 +4,16 @@ BUILD_DIR?=$(CURDIR)/..
include ../config-host.mak
include config-target.mak
include $(SRC_PATH)/rules.mak
ifdef CONFIG_SOFTMMU
include config-devices.mak
endif
include $(SRC_PATH)/rules.mak
$(call set-vpath, $(SRC_PATH):$(BUILD_DIR))
ifdef CONFIG_LINUX
QEMU_CFLAGS += -I../linux-headers
endif
QEMU_CFLAGS += -iquote .. -iquote $(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H
QEMU_CFLAGS += -I.. -I$(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H
QEMU_CFLAGS+=-iquote $(SRC_PATH)/include
QEMU_CFLAGS+=-I$(SRC_PATH)/include
ifdef CONFIG_USER_ONLY
# user emulator name
@@ -25,7 +22,7 @@ QEMU_PROG_BUILD = $(QEMU_PROG)
else
# system emulator name
QEMU_PROG=qemu-system-$(TARGET_NAME)$(EXESUF)
ifneq (,$(findstring -mwindows,$(SDL_LIBS)))
ifneq (,$(findstring -mwindows,$(libs_softmmu)))
# Terminate program name with a 'w' because the linker builds a windows executable.
QEMU_PROGW=qemu-system-$(TARGET_NAME)w$(EXESUF)
$(QEMU_PROG): $(QEMU_PROGW)
@@ -39,17 +36,11 @@ endif
PROGS=$(QEMU_PROG) $(QEMU_PROGW)
STPFILES=
# Makefile Tests
include $(SRC_PATH)/tests/tcg/Makefile.include
config-target.h: config-target.h-timestamp
config-target.h-timestamp: config-target.mak
config-devices.h: config-devices.h-timestamp
config-devices.h-timestamp: config-devices.mak
ifdef CONFIG_TRACE_SYSTEMTAP
stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp $(QEMU_PROG)-log.stp
stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp
ifdef CONFIG_USER_ONLY
TARGET_TYPE=user
@@ -88,14 +79,6 @@ $(QEMU_PROG)-simpletrace.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y)
--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \
$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp")
$(QEMU_PROG)-log.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y)
$(call quiet-command,$(TRACETOOL) \
--group=all \
--format=log-stap \
--backends=$(TRACE_BACKENDS) \
--probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \
$< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-log.stp")
else
stap:
endif
@@ -106,21 +89,25 @@ all: $(PROGS) stap
# Dummy command so that make thinks it has done something
@true
obj-y += trace/
#########################################################
# cpu emulator library
obj-y += exec.o
obj-y += accel/
obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o
obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o
obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/optimize.o
obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/tcg-runtime.o
obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o
obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o
obj-$(CONFIG_TCG) += fpu/softfloat.o
obj-y += fpu/softfloat.o
obj-y += target/$(TARGET_BASE_ARCH)/
obj-y += disas.o
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
LIBS := $(libs_cpu) $(LIBS)
obj-$(call lnot,$(CONFIG_HAX)) += hax-stub.o
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/decContext.o
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/decNumber.o
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/dpd/decimal32.o
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/dpd/decimal64.o
obj-$(CONFIG_LIBDECNUMBER) += libdecnumber/dpd/decimal128.o
#########################################################
# Linux user emulator target
@@ -132,7 +119,7 @@ QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) \
-I$(SRC_PATH)/linux-user
obj-y += linux-user/
obj-y += gdbstub.o thunk.o
obj-y += gdbstub.o thunk.o user-exec.o user-exec-stub.o
endif #CONFIG_LINUX_USER
@@ -145,21 +132,19 @@ QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ABI_DIR) \
-I$(SRC_PATH)/bsd-user/$(HOST_VARIANT_DIR)
obj-y += bsd-user/
obj-y += gdbstub.o
obj-y += gdbstub.o user-exec.o user-exec-stub.o
endif #CONFIG_BSD_USER
#########################################################
# System emulator target
ifdef CONFIG_SOFTMMU
obj-y += arch_init.o cpus.o gdbstub.o balloon.o ioport.o
obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o
obj-y += qtest.o
obj-y += dump/
obj-y += hw/
obj-y += monitor/
obj-y += qapi/
obj-y += memory.o
obj-y += memory_mapping.o
obj-y += dump.o
obj-y += migration/ram.o
LIBS := $(libs_softmmu) $(LIBS)
@@ -170,39 +155,45 @@ else
obj-y += hw/$(TARGET_BASE_ARCH)/
endif
generated-files-y += hmp-commands.h hmp-commands-info.h
generated-files-y += config-devices.h
GENERATED_FILES += hmp-commands.h hmp-commands-info.h
endif # CONFIG_SOFTMMU
# Workaround for http://gcc.gnu.org/PR55489, see configure.
%/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS)
dummy := $(call unnest-vars,,obj-y)
all-obj-y := $(obj-y)
target-obj-y :=
block-obj-y :=
common-obj-y :=
chardev-obj-y :=
include $(SRC_PATH)/Makefile.objs
dummy := $(call unnest-vars,,target-obj-y)
target-obj-y-save := $(target-obj-y)
dummy := $(call unnest-vars,.., \
authz-obj-y \
block-obj-y \
block-obj-m \
chardev-obj-y \
crypto-obj-y \
crypto-user-obj-y \
crypto-aes-obj-y \
qom-obj-y \
io-obj-y \
common-obj-y \
common-obj-m)
target-obj-y := $(target-obj-y-save)
all-obj-y += $(common-obj-y)
all-obj-y += $(target-obj-y)
all-obj-y += $(qom-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(authz-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y)
all-obj-$(CONFIG_USER_ONLY) += $(crypto-user-obj-y)
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
ifdef CONFIG_SOFTMMU
$(QEMU_PROG_BUILD): config-devices.mak
endif
COMMON_LDADDS = ../libqemuutil.a
COMMON_LDADDS = ../libqemuutil.a ../libqemustub.a
# build either PROG or PROGW
$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS)
@@ -225,7 +216,6 @@ clean: clean-target
rm -f *.a *~ $(PROGS)
rm -f $(shell find . -name '*.[od]')
rm -f hmp-commands.h gdbstub-xml.c
rm -f trace/generated-helpers.c trace/generated-helpers.c-timestamp
ifdef CONFIG_TRACE_SYSTEMTAP
rm -f *.stp
endif
@@ -238,24 +228,7 @@ ifdef CONFIG_TRACE_SYSTEMTAP
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset"
$(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp"
$(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp"
$(INSTALL_DATA) $(QEMU_PROG)-log.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-log.stp"
endif
generated-files-y += config-target.h
Makefile: $(generated-files-y)
# Reports/Analysis
#
# The target specific coverage report only cares about target specific
# blobs and not the shared code.
#
%/coverage-report.html:
@mkdir -p $*
$(call quiet-command,\
gcovr -r $(SRC_PATH) --object-directory $(CURDIR) \
-p --html --html-details -o $@, \
"GEN", "coverage-report.html")
.PHONY: coverage-report
coverage-report: $(CURDIR)/reports/coverage/coverage-report.html
GENERATED_FILES += config-target.h
Makefile: $(GENERATED_FILES)
+10 -43
View File
@@ -44,9 +44,9 @@ of other UNIX targets. The simple steps to build QEMU are:
Additional information can also be found online via the QEMU website:
https://qemu.org/Hosts/Linux
https://qemu.org/Hosts/Mac
https://qemu.org/Hosts/W32
http://qemu-project.org/Hosts/Linux
http://qemu-project.org/Hosts/Mac
http://qemu-project.org/Hosts/W32
Submitting patches
@@ -54,9 +54,9 @@ Submitting patches
The QEMU source code is maintained under the GIT version control system.
git clone https://git.qemu.org/git/qemu.git
git clone git://git.qemu-project.org/qemu.git
When submitting patches, one common approach is to use 'git
When submitting patches, the preferred approach is to use 'git
format-patch' and/or 'git send-email' to format & send the mail to the
qemu-devel@nongnu.org mailing list. All patches submitted must contain
a 'Signed-off-by' line from the author. Patches should follow the
@@ -65,42 +65,9 @@ guidelines set out in the HACKING and CODING_STYLE files.
Additional information on submitting patches can be found online via
the QEMU website
https://qemu.org/Contribute/SubmitAPatch
https://qemu.org/Contribute/TrivialPatches
http://qemu-project.org/Contribute/SubmitAPatch
http://qemu-project.org/Contribute/TrivialPatches
The QEMU website is also maintained under source control.
git clone https://git.qemu.org/git/qemu-web.git
https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/
A 'git-publish' utility was created to make above process less
cumbersome, and is highly recommended for making regular contributions,
or even just for sending consecutive patch series revisions. It also
requires a working 'git send-email' setup, and by default doesn't
automate everything, so you may want to go through the above steps
manually for once.
For installation instructions, please go to
https://github.com/stefanha/git-publish
The workflow with 'git-publish' is:
$ git checkout master -b my-feature
$ # work on new commits, add your 'Signed-off-by' lines to each
$ git publish
Your patch series will be sent and tagged as my-feature-v1 if you need to refer
back to it in the future.
Sending v2:
$ git checkout my-feature # same topic branch
$ # making changes to the commits (using 'git rebase', for example)
$ git publish
Your patch series will be sent with 'v2' tag in the subject and the git tip
will be tagged as my-feature-v2.
Bug reporting
=============
@@ -118,7 +85,7 @@ reported via launchpad.
For additional information on bug reporting consult:
https://qemu.org/Contribute/ReportABug
http://qemu-project.org/Contribute/ReportABug
Contact
@@ -128,12 +95,12 @@ The QEMU community can be contacted in a number of ways, with the two
main methods being email and IRC
- qemu-devel@nongnu.org
https://lists.nongnu.org/mailman/listinfo/qemu-devel
http://lists.nongnu.org/mailman/listinfo/qemu-devel
- #qemu on irc.oftc.net
Information on additional methods of contacting the community can be
found online via the QEMU website:
https://qemu.org/Contribute/StartHere
http://qemu-project.org/Contribute/StartHere
-- End
+1 -1
View File
@@ -1 +1 @@
4.1.0
2.10.2
+1 -2
View File
@@ -1,5 +1,4 @@
obj-$(CONFIG_SOFTMMU) += accel.o
obj-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_POSIX)) += qtest.o
obj-$(CONFIG_KVM) += kvm/
obj-y += kvm/
obj-$(CONFIG_TCG) += tcg/
obj-y += stubs/
+21 -34
View File
@@ -26,6 +26,7 @@
#include "qemu/osdep.h"
#include "sysemu/accel.h"
#include "hw/boards.h"
#include "qemu-common.h"
#include "sysemu/arch_init.h"
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
@@ -33,8 +34,6 @@
#include "hw/xen/xen.h"
#include "qom/object.h"
#include "qemu/error-report.h"
#include "qemu/option.h"
#include "qapi/error.h"
static const TypeInfo accel_type = {
.name = TYPE_ACCEL,
@@ -65,16 +64,14 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms)
ms->accelerator = NULL;
*(acc->allowed) = false;
object_unref(OBJECT(accel));
} else {
object_set_accelerator_compat_props(acc->compat_props);
}
return ret;
}
void configure_accelerator(MachineState *ms, const char *progname)
void configure_accelerator(MachineState *ms)
{
const char *accel;
char **accel_list, **tmp;
const char *accel, *p;
char buf[10];
int ret;
bool accel_initialised = false;
bool init_failed = false;
@@ -82,31 +79,25 @@ void configure_accelerator(MachineState *ms, const char *progname)
accel = qemu_opt_get(qemu_get_machine_opts(), "accel");
if (accel == NULL) {
/* Select the default accelerator */
int pnlen = strlen(progname);
if (pnlen >= 3 && g_str_equal(&progname[pnlen - 3], "kvm")) {
/* If the program name ends with "kvm", we prefer KVM */
accel = "kvm:tcg";
} else {
#if defined(CONFIG_TCG)
accel = "tcg";
#elif defined(CONFIG_KVM)
accel = "kvm";
#else
error_report("No accelerator selected and"
" no default accelerator available");
exit(1);
#endif
}
/* Use the default "accelerator", tcg */
accel = "tcg";
}
accel_list = g_strsplit(accel, ":", 0);
for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) {
acc = accel_find(*tmp);
p = accel;
while (!accel_initialised && *p != '\0') {
if (*p == ':') {
p++;
}
p = get_opt_name(buf, sizeof(buf), p, ':');
acc = accel_find(buf);
if (!acc) {
continue;
}
if (acc->available && !acc->available()) {
printf("%s not supported for this target\n",
acc->name);
continue;
}
ret = accel_init_machine(acc, ms);
if (ret < 0) {
init_failed = true;
@@ -116,7 +107,6 @@ void configure_accelerator(MachineState *ms, const char *progname)
accel_initialised = true;
}
}
g_strfreev(accel_list);
if (!accel_initialised) {
if (!init_failed) {
@@ -130,13 +120,10 @@ void configure_accelerator(MachineState *ms, const char *progname)
}
}
void accel_setup_post(MachineState *ms)
void accel_register_compat_props(AccelState *accel)
{
AccelState *accel = ms->accelerator;
AccelClass *acc = ACCEL_GET_CLASS(accel);
if (acc->setup_post) {
acc->setup_post(ms, accel);
}
AccelClass *class = ACCEL_GET_CLASS(accel);
register_compat_props_array(class->global_props);
}
static void register_accel_types(void)
+1 -2
View File
@@ -1,2 +1 @@
obj-y += kvm-all.o
obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o
obj-$(CONFIG_KVM) += kvm-all.o
+229 -491
View File
File diff suppressed because it is too large. Load diff
-26
View File
@@ -1,26 +0,0 @@
/*
* QEMU SEV stub
*
* Copyright Advanced Micro Devices 2018
*
* Authors:
* Brijesh Singh <brijesh.singh@amd.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "sysemu/sev.h"
int sev_encrypt_data(void *handle, uint8_t *ptr, uint64_t len)
{
abort();
}
void *sev_guest_init(const char *id)
{
return NULL;
}
+1 -5
View File
@@ -1,4 +1,4 @@
# See docs/devel/tracing.txt for syntax documentation.
# Trace events for debugging and performance instrumentation
# kvm-all.c
kvm_ioctl(int type, void *arg) "type 0x%x, arg %p"
@@ -12,8 +12,4 @@ kvm_irqchip_commit_routes(void) ""
kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d"
kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d"
kvm_irqchip_release_virq(int virq) "virq %d"
kvm_set_ioeventfd_mmio(int fd, uint64_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%" PRIx64 " val=0x%x assign: %d size: %d match: %d"
kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%x val=0x%x assign: %d size: %d match: %d"
kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d"
kvm_clear_dirty_log(uint32_t slot, uint64_t start, uint32_t size) "slot#%"PRId32" start 0x%"PRIx64" size 0x%"PRIx32
-54
View File
@@ -1,54 +0,0 @@
/*
* QTest accelerator code
*
* Copyright IBM, Corp. 2011
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "qemu/config-file.h"
#include "sysemu/accel.h"
#include "sysemu/qtest.h"
#include "sysemu/cpus.h"
static int qtest_init_accel(MachineState *ms)
{
QemuOpts *opts = qemu_opts_create(qemu_find_opts("icount"), NULL, 0,
&error_abort);
qemu_opt_set(opts, "shift", "0", &error_abort);
configure_icount(opts, &error_abort);
qemu_opts_del(opts);
return 0;
}
static void qtest_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "QTest";
ac->init_machine = qtest_init_accel;
ac->allowed = &qtest_allowed;
}
#define TYPE_QTEST_ACCEL ACCEL_CLASS_NAME("qtest")
static const TypeInfo qtest_accel_type = {
.name = TYPE_QTEST_ACCEL,
.parent = TYPE_ACCEL,
.class_init = qtest_accel_class_init,
};
static void qtest_type_init(void)
{
type_register_static(&qtest_accel_type);
}
type_init(qtest_type_init);
+2 -5
View File
@@ -1,5 +1,2 @@
obj-$(call lnot,$(CONFIG_HAX)) += hax-stub.o
obj-$(call lnot,$(CONFIG_HVF)) += hvf-stub.o
obj-$(call lnot,$(CONFIG_WHPX)) += whpx-stub.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o
-30
View File
@@ -1,30 +0,0 @@
/*
* QEMU HVF support
*
* Copyright 2017 Red Hat, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2 or later, as published by the Free Software Foundation,
* and may be copied, distributed, and modified under those terms.
*
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "cpu.h"
#include "sysemu/hvf.h"
int hvf_init_vcpu(CPUState *cpu)
{
return -ENOSYS;
}
int hvf_vcpu_exec(CPUState *cpu)
{
return -ENOSYS;
}
void hvf_vcpu_destroy(CPUState *cpu)
{
}
+3 -12
View File
@@ -11,6 +11,7 @@
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h"
#include "sysemu/kvm.h"
@@ -63,9 +64,9 @@ int kvm_cpu_exec(CPUState *cpu)
abort();
}
bool kvm_has_sync_mmu(void)
int kvm_has_sync_mmu(void)
{
return false;
return 0;
}
int kvm_has_many_ioeventfds(void)
@@ -104,16 +105,6 @@ int kvm_on_sigbus(int code, void *addr)
return 1;
}
bool kvm_memcrypt_enabled(void)
{
return false;
}
int kvm_memcrypt_encrypt_data(uint8_t *ptr, uint64_t len)
{
return 1;
}
#ifndef CONFIG_USER_ONLY
int kvm_irqchip_add_msi_route(KVMState *s, int vector, PCIDevice *dev)
{
-4
View File
@@ -20,7 +20,3 @@
void tb_flush(CPUState *cpu)
{
}
void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
{
}
-47
View File
@@ -1,47 +0,0 @@
/*
* QEMU Windows Hypervisor Platform accelerator (WHPX) stub
*
* Copyright Microsoft Corp. 2017
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "cpu.h"
#include "sysemu/whpx.h"
int whpx_init_vcpu(CPUState *cpu)
{
return -1;
}
int whpx_vcpu_exec(CPUState *cpu)
{
return -1;
}
void whpx_destroy_vcpu(CPUState *cpu)
{
}
void whpx_vcpu_kick(CPUState *cpu)
{
}
void whpx_cpu_synchronize_state(CPUState *cpu)
{
}
void whpx_cpu_synchronize_post_reset(CPUState *cpu)
{
}
void whpx_cpu_synchronize_post_init(CPUState *cpu)
{
}
void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu)
{
}
-5
View File
@@ -1,8 +1,3 @@
obj-$(CONFIG_SOFTMMU) += tcg-all.o
obj-$(CONFIG_SOFTMMU) += cputlb.o
obj-y += tcg-runtime.o tcg-runtime-gvec.o
obj-y += cpu-exec.o cpu-exec-common.o translate-all.o
obj-y += translator.o
obj-$(CONFIG_USER_ONLY) += user-exec.o
obj-$(call lnot,$(CONFIG_SOFTMMU)) += user-exec-stub.o
-368
View File
@@ -1,368 +0,0 @@
/*
* Atomic helper templates
* Included from tcg-runtime.c and cputlb.c.
*
* Copyright (c) 2016 Red Hat, Inc
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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 "trace/mem.h"
#if DATA_SIZE == 16
# define SUFFIX o
# define DATA_TYPE Int128
# define BSWAP bswap128
# define SHIFT 4
#elif DATA_SIZE == 8
# define SUFFIX q
# define DATA_TYPE uint64_t
# define SDATA_TYPE int64_t
# define BSWAP bswap64
# define SHIFT 3
#elif DATA_SIZE == 4
# define SUFFIX l
# define DATA_TYPE uint32_t
# define SDATA_TYPE int32_t
# define BSWAP bswap32
# define SHIFT 2
#elif DATA_SIZE == 2
# define SUFFIX w
# define DATA_TYPE uint16_t
# define SDATA_TYPE int16_t
# define BSWAP bswap16
# define SHIFT 1
#elif DATA_SIZE == 1
# define SUFFIX b
# define DATA_TYPE uint8_t
# define SDATA_TYPE int8_t
# define BSWAP
# define SHIFT 0
#else
# error unsupported data size
#endif
#if DATA_SIZE >= 4
# define ABI_TYPE DATA_TYPE
#else
# define ABI_TYPE uint32_t
#endif
#define ATOMIC_TRACE_RMW do { \
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
\
trace_guest_mem_before_exec(env_cpu(env), addr, info); \
trace_guest_mem_before_exec(env_cpu(env), addr, \
info | TRACE_MEM_ST); \
} while (0)
#define ATOMIC_TRACE_LD do { \
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
\
trace_guest_mem_before_exec(env_cpu(env), addr, info); \
} while (0)
# define ATOMIC_TRACE_ST do { \
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true); \
\
trace_guest_mem_before_exec(env_cpu(env), addr, info); \
} while (0)
/* Define host-endian atomic operations. Note that END is used within
the ATOMIC_NAME macro, and redefined below. */
#if DATA_SIZE == 1
# define END
# define MEND _be /* either le or be would be fine */
#elif defined(HOST_WORDS_BIGENDIAN)
# define END _be
# define MEND _be
#else
# define END _le
# define MEND _le
#endif
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
ABI_TYPE cmpv, ABI_TYPE newv EXTRA_ARGS)
{
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ret;
ATOMIC_TRACE_RMW;
#if DATA_SIZE == 16
ret = atomic16_cmpxchg(haddr, cmpv, newv);
#else
ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
#endif
ATOMIC_MMU_CLEANUP;
return ret;
}
#if DATA_SIZE >= 16
#if HAVE_ATOMIC128
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
{
ATOMIC_MMU_DECLS;
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
ATOMIC_TRACE_LD;
val = atomic16_read(haddr);
ATOMIC_MMU_CLEANUP;
return val;
}
void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
ABI_TYPE val EXTRA_ARGS)
{
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
ATOMIC_TRACE_ST;
atomic16_set(haddr, val);
ATOMIC_MMU_CLEANUP;
}
#endif
#else
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
ABI_TYPE val EXTRA_ARGS)
{
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ret;
ATOMIC_TRACE_RMW;
ret = atomic_xchg__nocheck(haddr, val);
ATOMIC_MMU_CLEANUP;
return ret;
}
#define GEN_ATOMIC_HELPER(X) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE val EXTRA_ARGS) \
{ \
ATOMIC_MMU_DECLS; \
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
DATA_TYPE ret; \
\
ATOMIC_TRACE_RMW; \
ret = atomic_##X(haddr, val); \
ATOMIC_MMU_CLEANUP; \
return ret; \
}
GEN_ATOMIC_HELPER(fetch_add)
GEN_ATOMIC_HELPER(fetch_and)
GEN_ATOMIC_HELPER(fetch_or)
GEN_ATOMIC_HELPER(fetch_xor)
GEN_ATOMIC_HELPER(add_fetch)
GEN_ATOMIC_HELPER(and_fetch)
GEN_ATOMIC_HELPER(or_fetch)
GEN_ATOMIC_HELPER(xor_fetch)
#undef GEN_ATOMIC_HELPER
/* These helpers are, as a whole, full barriers. Within the helper,
* the leading barrier is explicit and the trailing barrier is within
* cmpxchg primitive.
*
* Trace this load + RMW loop as a single RMW op. This way, regardless
* of CF_PARALLEL's value, we'll trace just a read and a write.
*/
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE xval EXTRA_ARGS) \
{ \
ATOMIC_MMU_DECLS; \
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
XDATA_TYPE cmp, old, new, val = xval; \
\
ATOMIC_TRACE_RMW; \
smp_mb(); \
cmp = atomic_read__nocheck(haddr); \
do { \
old = cmp; new = FN(old, val); \
cmp = atomic_cmpxchg__nocheck(haddr, old, new); \
} while (cmp != old); \
ATOMIC_MMU_CLEANUP; \
return RET; \
}
GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(fetch_umin, MIN, DATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(fetch_umax, MAX, DATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
#undef GEN_ATOMIC_HELPER_FN
#endif /* DATA SIZE >= 16 */
#undef END
#undef MEND
#if DATA_SIZE > 1
/* Define reverse-host-endian atomic operations. Note that END is used
within the ATOMIC_NAME macro. */
#ifdef HOST_WORDS_BIGENDIAN
# define END _le
# define MEND _le
#else
# define END _be
# define MEND _be
#endif
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
ABI_TYPE cmpv, ABI_TYPE newv EXTRA_ARGS)
{
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ret;
ATOMIC_TRACE_RMW;
#if DATA_SIZE == 16
ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv));
#else
ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
#endif
ATOMIC_MMU_CLEANUP;
return BSWAP(ret);
}
#if DATA_SIZE >= 16
#if HAVE_ATOMIC128
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
{
ATOMIC_MMU_DECLS;
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
ATOMIC_TRACE_LD;
val = atomic16_read(haddr);
ATOMIC_MMU_CLEANUP;
return BSWAP(val);
}
void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
ABI_TYPE val EXTRA_ARGS)
{
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
ATOMIC_TRACE_ST;
val = BSWAP(val);
atomic16_set(haddr, val);
ATOMIC_MMU_CLEANUP;
}
#endif
#else
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
ABI_TYPE val EXTRA_ARGS)
{
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
ABI_TYPE ret;
ATOMIC_TRACE_RMW;
ret = atomic_xchg__nocheck(haddr, BSWAP(val));
ATOMIC_MMU_CLEANUP;
return BSWAP(ret);
}
#define GEN_ATOMIC_HELPER(X) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE val EXTRA_ARGS) \
{ \
ATOMIC_MMU_DECLS; \
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
DATA_TYPE ret; \
\
ATOMIC_TRACE_RMW; \
ret = atomic_##X(haddr, BSWAP(val)); \
ATOMIC_MMU_CLEANUP; \
return BSWAP(ret); \
}
GEN_ATOMIC_HELPER(fetch_and)
GEN_ATOMIC_HELPER(fetch_or)
GEN_ATOMIC_HELPER(fetch_xor)
GEN_ATOMIC_HELPER(and_fetch)
GEN_ATOMIC_HELPER(or_fetch)
GEN_ATOMIC_HELPER(xor_fetch)
#undef GEN_ATOMIC_HELPER
/* These helpers are, as a whole, full barriers. Within the helper,
* the leading barrier is explicit and the trailing barrier is within
* cmpxchg primitive.
*
* Trace this load + RMW loop as a single RMW op. This way, regardless
* of CF_PARALLEL's value, we'll trace just a read and a write.
*/
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE xval EXTRA_ARGS) \
{ \
ATOMIC_MMU_DECLS; \
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
XDATA_TYPE ldo, ldn, old, new, val = xval; \
\
ATOMIC_TRACE_RMW; \
smp_mb(); \
ldn = atomic_read__nocheck(haddr); \
do { \
ldo = ldn; old = BSWAP(ldo); new = FN(old, val); \
ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \
} while (ldo != ldn); \
ATOMIC_MMU_CLEANUP; \
return RET; \
}
GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(fetch_umin, MIN, DATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(fetch_umax, MAX, DATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
/* Note that for addition, we need to use a separate cmpxchg loop instead
of bswaps for the reverse-host-endian helpers. */
#define ADD(X, Y) (X + Y)
GEN_ATOMIC_HELPER_FN(fetch_add, ADD, DATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
#undef ADD
#undef GEN_ATOMIC_HELPER_FN
#endif /* DATA_SIZE >= 16 */
#undef END
#undef MEND
#endif /* DATA_SIZE > 1 */
#undef ATOMIC_TRACE_ST
#undef ATOMIC_TRACE_LD
#undef ATOMIC_TRACE_RMW
#undef BSWAP
#undef ABI_TYPE
#undef DATA_TYPE
#undef SDATA_TYPE
#undef SUFFIX
#undef DATA_SIZE
#undef SHIFT
+7 -7
View File
@@ -6,7 +6,7 @@
* 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.1 of the License, or (at your option) any later version.
* 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
@@ -20,16 +20,18 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "sysemu/cpus.h"
#include "sysemu/tcg.h"
#include "exec/exec-all.h"
#include "exec/memory-internal.h"
bool tcg_allowed;
/* exit the current TB, but without causing any exception to be raised */
void cpu_loop_exit_noexc(CPUState *cpu)
{
/* XXX: restore cpu registers saved in host registers */
cpu->exception_index = -1;
cpu_loop_exit(cpu);
siglongjmp(cpu->jmp_env, 1);
}
#if defined(CONFIG_SOFTMMU)
@@ -64,17 +66,15 @@ void cpu_reloading_memory_map(void)
void cpu_loop_exit(CPUState *cpu)
{
/* Undo the setting in cpu_tb_exec. */
cpu->can_do_io = 1;
siglongjmp(cpu->jmp_env, 1);
}
void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc)
{
if (pc) {
cpu_restore_state(cpu, pc, true);
cpu_restore_state(cpu, pc);
}
cpu_loop_exit(cpu);
siglongjmp(cpu->jmp_env, 1);
}
void cpu_loop_exit_atomic(CPUState *cpu, uintptr_t pc)
+157 -213
View File
@@ -6,7 +6,7 @@
* 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.1 of the License, or (at your option) any later version.
* 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
@@ -16,9 +16,7 @@
* 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 "cpu.h"
#include "trace.h"
#include "disas/disas.h"
@@ -27,9 +25,9 @@
#include "qemu/atomic.h"
#include "sysemu/qtest.h"
#include "qemu/timer.h"
#include "exec/address-spaces.h"
#include "qemu/rcu.h"
#include "exec/tb-hash.h"
#include "exec/tb-lookup.h"
#include "exec/log.h"
#include "qemu/main-loop.h"
#if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY)
@@ -56,7 +54,7 @@ typedef struct SyncClocks {
#define MAX_DELAY_PRINT_RATE 2000000000LL
#define MAX_NB_PRINTS 100
static void align_clocks(SyncClocks *sc, CPUState *cpu)
static void align_clocks(SyncClocks *sc, const CPUState *cpu)
{
int64_t cpu_icount;
@@ -64,7 +62,7 @@ static void align_clocks(SyncClocks *sc, CPUState *cpu)
return;
}
cpu_icount = cpu->icount_extra + cpu_neg(cpu)->icount_decr.u16.low;
cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low;
sc->diff_clk += cpu_icount_to_ns(sc->last_cpu_icount - cpu_icount);
sc->last_cpu_icount = cpu_icount;
@@ -107,15 +105,15 @@ static void print_delay(const SyncClocks *sc)
}
}
static void init_delay_params(SyncClocks *sc, CPUState *cpu)
static void init_delay_params(SyncClocks *sc,
const CPUState *cpu)
{
if (!icount_align_option) {
return;
}
sc->realtime_clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT);
sc->diff_clk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - sc->realtime_clock;
sc->last_cpu_icount
= cpu->icount_extra + cpu_neg(cpu)->icount_decr.u16.low;
sc->last_cpu_icount = cpu->icount_extra + cpu->icount_decr.u16.low;
if (sc->diff_clk < max_delay) {
max_delay = sc->diff_clk;
}
@@ -144,27 +142,22 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
uintptr_t ret;
TranslationBlock *last_tb;
int tb_exit;
uint8_t *tb_ptr = itb->tc.ptr;
uint8_t *tb_ptr = itb->tc_ptr;
qemu_log_mask_and_addr(CPU_LOG_EXEC, itb->pc,
"Trace %d: %p ["
TARGET_FMT_lx "/" TARGET_FMT_lx "/%#x] %s\n",
cpu->cpu_index, itb->tc.ptr,
itb->cs_base, itb->pc, itb->flags,
"Trace %p [%d: " TARGET_FMT_lx "] %s\n",
itb->tc_ptr, cpu->cpu_index, itb->pc,
lookup_symbol(itb->pc));
#if defined(DEBUG_DISAS)
if (qemu_loglevel_mask(CPU_LOG_TB_CPU)
&& qemu_log_in_addr_range(itb->pc)) {
qemu_log_lock();
int flags = 0;
if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) {
flags |= CPU_DUMP_FPU;
}
#if defined(TARGET_I386)
flags |= CPU_DUMP_CCOP;
log_cpu_state(cpu, CPU_DUMP_CCOP);
#else
log_cpu_state(cpu, 0);
#endif
log_cpu_state(cpu, flags);
qemu_log_unlock();
}
#endif /* DEBUG_DISAS */
@@ -185,7 +178,7 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
qemu_log_mask_and_addr(CPU_LOG_EXEC, last_tb->pc,
"Stopped execution of TB chain before %p ["
TARGET_FMT_lx "] %s\n",
last_tb->tc.ptr, last_tb->pc,
last_tb->tc_ptr, last_tb->pc,
lookup_symbol(last_tb->pc));
if (cc->synchronize_from_tb) {
cc->synchronize_from_tb(cpu, last_tb);
@@ -204,84 +197,81 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles,
TranslationBlock *orig_tb, bool ignore_icount)
{
TranslationBlock *tb;
uint32_t cflags = curr_cflags() | CF_NOCACHE;
if (ignore_icount) {
cflags &= ~CF_USE_ICOUNT;
}
/* Should never happen.
We only end up here when an existing TB is too long. */
cflags |= MIN(max_cycles, CF_COUNT_MASK);
if (max_cycles > CF_COUNT_MASK)
max_cycles = CF_COUNT_MASK;
mmap_lock();
tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base,
orig_tb->flags, cflags);
tb_lock();
tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, orig_tb->flags,
max_cycles | CF_NOCACHE
| (ignore_icount ? CF_IGNORE_ICOUNT : 0));
tb->orig_tb = orig_tb;
mmap_unlock();
tb_unlock();
/* execute the generated code */
trace_exec_tb_nocache(tb, tb->pc);
cpu_tb_exec(cpu, tb);
mmap_lock();
tb_lock();
tb_phys_invalidate(tb, -1);
mmap_unlock();
tcg_tb_remove(tb);
tb_free(tb);
tb_unlock();
}
#endif
void cpu_exec_step_atomic(CPUState *cpu)
static void cpu_exec_step(CPUState *cpu)
{
CPUClass *cc = CPU_GET_CLASS(cpu);
CPUArchState *env = (CPUArchState *)cpu->env_ptr;
TranslationBlock *tb;
target_ulong cs_base, pc;
uint32_t flags;
uint32_t cflags = 1;
uint32_t cf_mask = cflags & CF_HASH_MASK;
/* volatile because we modify it between setjmp and longjmp */
volatile bool in_exclusive_region = false;
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
if (sigsetjmp(cpu->jmp_env, 0) == 0) {
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
if (tb == NULL) {
mmap_lock();
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
mmap_unlock();
}
mmap_lock();
tb_lock();
tb = tb_gen_code(cpu, pc, cs_base, flags,
1 | CF_NOCACHE | CF_IGNORE_ICOUNT);
tb->orig_tb = NULL;
tb_unlock();
mmap_unlock();
start_exclusive();
/* Since we got here, we know that parallel_cpus must be true. */
parallel_cpus = false;
in_exclusive_region = true;
cc->cpu_exec_enter(cpu);
/* execute the generated code */
trace_exec_tb(tb, pc);
trace_exec_tb_nocache(tb, pc);
cpu_tb_exec(cpu, tb);
cc->cpu_exec_exit(cpu);
tb_lock();
tb_phys_invalidate(tb, -1);
tb_free(tb);
tb_unlock();
} else {
/*
/* We may have exited due to another problem here, so we need
* to reset any tb_locks we may have taken but didn't release.
* The mmap_lock is dropped by tb_gen_code if it runs out of
* memory.
*/
#ifndef CONFIG_SOFTMMU
tcg_debug_assert(!have_mmap_lock());
#endif
if (qemu_mutex_iothread_locked()) {
qemu_mutex_unlock_iothread();
}
assert_no_pages_locked();
tb_lock_reset();
}
}
if (in_exclusive_region) {
/* We might longjump out of either the codegen or the
* execution, so must make sure we only end the exclusive
* region if we started it.
*/
parallel_cpus = true;
end_exclusive();
}
void cpu_exec_step_atomic(CPUState *cpu)
{
start_exclusive();
/* Since we got here, we know that parallel_cpus must be true. */
parallel_cpus = false;
cpu_exec_step(cpu);
parallel_cpus = true;
end_exclusive();
}
struct tb_desc {
@@ -290,11 +280,10 @@ struct tb_desc {
CPUArchState *env;
tb_page_addr_t phys_page1;
uint32_t flags;
uint32_t cf_mask;
uint32_t trace_vcpu_dstate;
};
static bool tb_lookup_cmp(const void *p, const void *d)
static bool tb_cmp(const void *p, const void *d)
{
const TranslationBlock *tb = p;
const struct tb_desc *desc = d;
@@ -304,7 +293,7 @@ static bool tb_lookup_cmp(const void *p, const void *d)
tb->cs_base == desc->cs_base &&
tb->flags == desc->flags &&
tb->trace_vcpu_dstate == desc->trace_vcpu_dstate &&
(tb_cflags(tb) & (CF_HASH_MASK | CF_INVALID)) == desc->cf_mask) {
!atomic_read(&tb->invalid)) {
/* check next page if needed */
if (tb->page_addr[1] == -1) {
return true;
@@ -323,8 +312,7 @@ static bool tb_lookup_cmp(const void *p, const void *d)
}
TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
target_ulong cs_base, uint32_t flags,
uint32_t cf_mask)
target_ulong cs_base, uint32_t flags)
{
tb_page_addr_t phys_pc;
struct tb_desc desc;
@@ -333,81 +321,55 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
desc.env = (CPUArchState *)cpu->env_ptr;
desc.cs_base = cs_base;
desc.flags = flags;
desc.cf_mask = cf_mask;
desc.trace_vcpu_dstate = *cpu->trace_dstate;
desc.pc = pc;
phys_pc = get_page_addr_code(desc.env, pc);
if (phys_pc == -1) {
return NULL;
}
desc.phys_page1 = phys_pc & TARGET_PAGE_MASK;
h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate);
return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp);
}
void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr)
{
if (TCG_TARGET_HAS_direct_jump) {
uintptr_t offset = tb->jmp_target_arg[n];
uintptr_t tc_ptr = (uintptr_t)tb->tc.ptr;
tb_target_set_jmp_target(tc_ptr, tc_ptr + offset, addr);
} else {
tb->jmp_target_arg[n] = addr;
}
}
static inline void tb_add_jump(TranslationBlock *tb, int n,
TranslationBlock *tb_next)
{
uintptr_t old;
assert(n < ARRAY_SIZE(tb->jmp_list_next));
qemu_spin_lock(&tb_next->jmp_lock);
/* make sure the destination TB is valid */
if (tb_next->cflags & CF_INVALID) {
goto out_unlock_next;
}
/* Atomically claim the jump destination slot only if it was NULL */
old = atomic_cmpxchg(&tb->jmp_dest[n], (uintptr_t)NULL, (uintptr_t)tb_next);
if (old) {
goto out_unlock_next;
}
/* patch the native jump address */
tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr);
/* add in TB jmp list */
tb->jmp_list_next[n] = tb_next->jmp_list_head;
tb_next->jmp_list_head = (uintptr_t)tb | n;
qemu_spin_unlock(&tb_next->jmp_lock);
qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc,
"Linking TBs %p [" TARGET_FMT_lx
"] index %d -> %p [" TARGET_FMT_lx "]\n",
tb->tc.ptr, tb->pc, n,
tb_next->tc.ptr, tb_next->pc);
return;
out_unlock_next:
qemu_spin_unlock(&tb_next->jmp_lock);
return;
h = tb_hash_func(phys_pc, pc, flags, *cpu->trace_dstate);
return qht_lookup(&tcg_ctx.tb_ctx.htable, tb_cmp, &desc, h);
}
static inline TranslationBlock *tb_find(CPUState *cpu,
TranslationBlock *last_tb,
int tb_exit, uint32_t cf_mask)
int tb_exit)
{
CPUArchState *env = (CPUArchState *)cpu->env_ptr;
TranslationBlock *tb;
target_ulong cs_base, pc;
uint32_t flags;
bool have_tb_lock = false;
/* we record a subset of the CPU state. It will
always be the same before a given translated block
is executed. */
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
tb = atomic_rcu_read(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)]);
if (unlikely(!tb || tb->pc != pc || tb->cs_base != cs_base ||
tb->flags != flags ||
tb->trace_vcpu_dstate != *cpu->trace_dstate)) {
tb = tb_htable_lookup(cpu, pc, cs_base, flags);
if (!tb) {
/* mmap_lock is needed by tb_gen_code, and mmap_lock must be
* taken outside tb_lock. As system emulation is currently
* single threaded the locks are NOPs.
*/
mmap_lock();
tb_lock();
have_tb_lock = true;
/* There's a chance that our desired tb has been translated while
* taking the locks so we check again inside the lock.
*/
tb = tb_htable_lookup(cpu, pc, cs_base, flags);
if (!tb) {
/* if no translated code available, then translate it now */
tb = tb_gen_code(cpu, pc, cs_base, flags, 0);
}
mmap_unlock();
}
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
if (tb == NULL) {
mmap_lock();
tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask);
mmap_unlock();
/* We add the TB in the virtual pc hash table for the fast lookup */
atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb);
}
@@ -421,8 +383,17 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
}
#endif
/* See if we can patch the calling TB. */
if (last_tb) {
tb_add_jump(last_tb, tb_exit, tb);
if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
if (!have_tb_lock) {
tb_lock();
have_tb_lock = true;
}
if (!tb->invalid) {
tb_add_jump(last_tb, tb_exit, tb);
}
}
if (have_tb_lock) {
tb_unlock();
}
return tb;
}
@@ -466,51 +437,48 @@ static inline void cpu_handle_debug_exception(CPUState *cpu)
static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
{
if (cpu->exception_index < 0) {
#ifndef CONFIG_USER_ONLY
if (replay_has_exception()
&& cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra == 0) {
/* try to cause an exception pending in the log */
cpu_exec_nocache(cpu, 1, tb_find(cpu, NULL, 0, curr_cflags()), true);
}
#endif
if (cpu->exception_index < 0) {
return false;
}
}
if (cpu->exception_index >= EXCP_INTERRUPT) {
/* exit request from the cpu execution loop */
*ret = cpu->exception_index;
if (*ret == EXCP_DEBUG) {
cpu_handle_debug_exception(cpu);
}
cpu->exception_index = -1;
return true;
} else {
#if defined(CONFIG_USER_ONLY)
/* if user mode only, we simulate a fake exception
which will be handled outside the cpu execution
loop */
#if defined(TARGET_I386)
CPUClass *cc = CPU_GET_CLASS(cpu);
cc->do_interrupt(cpu);
#endif
*ret = cpu->exception_index;
cpu->exception_index = -1;
return true;
#else
if (replay_exception()) {
CPUClass *cc = CPU_GET_CLASS(cpu);
qemu_mutex_lock_iothread();
cc->do_interrupt(cpu);
qemu_mutex_unlock_iothread();
if (cpu->exception_index >= 0) {
if (cpu->exception_index >= EXCP_INTERRUPT) {
/* exit request from the cpu execution loop */
*ret = cpu->exception_index;
if (*ret == EXCP_DEBUG) {
cpu_handle_debug_exception(cpu);
}
cpu->exception_index = -1;
} else if (!replay_has_interrupt()) {
/* give a chance to iothread in replay mode */
*ret = EXCP_INTERRUPT;
return true;
} else {
#if defined(CONFIG_USER_ONLY)
/* if user mode only, we simulate a fake exception
which will be handled outside the cpu execution
loop */
#if defined(TARGET_I386)
CPUClass *cc = CPU_GET_CLASS(cpu);
cc->do_interrupt(cpu);
#endif
*ret = cpu->exception_index;
cpu->exception_index = -1;
return true;
#else
if (replay_exception()) {
CPUClass *cc = CPU_GET_CLASS(cpu);
qemu_mutex_lock_iothread();
cc->do_interrupt(cpu);
qemu_mutex_unlock_iothread();
cpu->exception_index = -1;
} else if (!replay_has_interrupt()) {
/* give a chance to iothread in replay mode */
*ret = EXCP_INTERRUPT;
return true;
}
#endif
}
#ifndef CONFIG_USER_ONLY
} else if (replay_has_exception()
&& cpu->icount_decr.u16.low + cpu->icount_extra == 0) {
/* try to cause an exception pending in the log */
cpu_exec_nocache(cpu, 1, tb_find(cpu, NULL, 0), true);
*ret = -1;
return true;
#endif
}
@@ -522,13 +490,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
{
CPUClass *cc = CPU_GET_CLASS(cpu);
/* Clear the interrupt flag now since we're processing
* cpu->interrupt_request and cpu->exit_request.
* Ensure zeroing happens before reading cpu->exit_request or
* cpu->interrupt_request (see also smp_wmb in cpu_exit())
*/
atomic_mb_set(&cpu_neg(cpu)->icount_decr.u16.high, 0);
if (unlikely(atomic_read(&cpu->interrupt_request))) {
int interrupt_request;
qemu_mutex_lock_iothread();
@@ -579,7 +540,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
else {
if (cc->cpu_exec_interrupt(cpu, interrupt_request)) {
replay_interrupt();
cpu->exception_index = -1;
*last_tb = NULL;
}
/* The target hook may have updated the 'cpu->interrupt_request';
@@ -598,13 +558,10 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
}
/* Finally, check if we need to exit to the main loop. */
if (unlikely(atomic_read(&cpu->exit_request))
|| (use_icount
&& cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra == 0)) {
if (unlikely(atomic_read(&cpu->exit_request)
|| (use_icount && cpu->icount_decr.u16.low + cpu->icount_extra == 0))) {
atomic_set(&cpu->exit_request, 0);
if (cpu->exception_index == -1) {
cpu->exception_index = EXCP_INTERRUPT;
}
cpu->exception_index = EXCP_INTERRUPT;
return true;
}
@@ -627,15 +584,18 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
}
*last_tb = NULL;
insns_left = atomic_read(&cpu_neg(cpu)->icount_decr.u32);
insns_left = atomic_read(&cpu->icount_decr.u32);
atomic_set(&cpu->icount_decr.u16.high, 0);
if (insns_left < 0) {
/* Something asked us to stop executing chained TBs; just
* continue round the main loop. Whatever requested the exit
* will also have set something else (eg exit_request or
* interrupt_request) which will be handled by
* cpu_handle_interrupt. cpu_handle_interrupt will also
* clear cpu->icount_decr.u16.high.
* interrupt_request) which we will handle next time around
* the loop. But we need to ensure the zeroing of icount_decr
* comes before the next read of cpu->exit_request
* or cpu->interrupt_request.
*/
smp_mb();
return;
}
@@ -646,7 +606,7 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
cpu_update_icount(cpu);
/* Refill decrementer and continue execution. */
insns_left = MIN(0xffff, cpu->icount_budget);
cpu_neg(cpu)->icount_decr.u16.low = insns_left;
cpu->icount_decr.u16.low = insns_left;
cpu->icount_extra = cpu->icount_budget - insns_left;
if (!cpu->icount_extra) {
/* Execute any remaining instructions, then let the main loop
@@ -699,13 +659,11 @@ int cpu_exec(CPUState *cpu)
g_assert(cpu == current_cpu);
g_assert(cc == CPU_GET_CLASS(cpu));
#endif /* buggy compiler */
#ifndef CONFIG_SOFTMMU
tcg_debug_assert(!have_mmap_lock());
#endif
cpu->can_do_io = 1;
tb_lock_reset();
if (qemu_mutex_iothread_locked()) {
qemu_mutex_unlock_iothread();
}
assert_no_pages_locked();
}
/* if an exception is pending, we execute it here */
@@ -714,21 +672,7 @@ int cpu_exec(CPUState *cpu)
int tb_exit = 0;
while (!cpu_handle_interrupt(cpu, &last_tb)) {
uint32_t cflags = cpu->cflags_next_tb;
TranslationBlock *tb;
/* When requested, use an exact setting for cflags for the next
execution. This is used for icount, precise smc, and stop-
after-access watchpoints. Since this request should never
have CF_INVALID set, -1 is a convenient invalid value that
does not require tcg headers for cpu_common_reset. */
if (cflags == -1) {
cflags = curr_cflags();
} else {
cpu->cflags_next_tb = -1;
}
tb = tb_find(cpu, last_tb, tb_exit, cflags);
TranslationBlock *tb = tb_find(cpu, last_tb, tb_exit);
cpu_loop_exec_tb(cpu, tb, &last_tb, &tb_exit);
/* Try to align the host and virtual clocks
if the guest is in advance */
+434 -1188
View File
File diff suppressed because it is too large. Load diff
+5 -3
View File
@@ -26,14 +26,15 @@
#include "qemu/osdep.h"
#include "sysemu/accel.h"
#include "sysemu/sysemu.h"
#include "sysemu/tcg.h"
#include "qom/object.h"
#include "cpu.h"
#include "qemu-common.h"
#include "qom/cpu.h"
#include "sysemu/cpus.h"
#include "qemu/main-loop.h"
unsigned long tcg_tb_size;
#ifndef CONFIG_USER_ONLY
/* mask must never be zero, except for A20 change call */
static void tcg_handle_interrupt(CPUState *cpu, int mask)
{
@@ -50,7 +51,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
if (!qemu_cpu_is_self(cpu)) {
qemu_cpu_kick(cpu);
} else {
atomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1);
cpu->icount_decr.u16.high = -1;
if (use_icount &&
!cpu->can_do_io
&& (mask & ~old_mask) != 0) {
@@ -58,6 +59,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
}
}
}
#endif
static int tcg_init(MachineState *ms)
{
File diff suppressed because it is too large. Load diff
-169
View File
@@ -1,169 +0,0 @@
/*
* Tiny Code Generator for QEMU
*
* Copyright (c) 2008 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qemu/host-utils.h"
#include "cpu.h"
#include "exec/helper-proto.h"
#include "exec/cpu_ldst.h"
#include "exec/exec-all.h"
#include "exec/tb-lookup.h"
#include "disas/disas.h"
#include "exec/log.h"
/* 32-bit helpers */
int32_t HELPER(div_i32)(int32_t arg1, int32_t arg2)
{
return arg1 / arg2;
}
int32_t HELPER(rem_i32)(int32_t arg1, int32_t arg2)
{
return arg1 % arg2;
}
uint32_t HELPER(divu_i32)(uint32_t arg1, uint32_t arg2)
{
return arg1 / arg2;
}
uint32_t HELPER(remu_i32)(uint32_t arg1, uint32_t arg2)
{
return arg1 % arg2;
}
/* 64-bit helpers */
uint64_t HELPER(shl_i64)(uint64_t arg1, uint64_t arg2)
{
return arg1 << arg2;
}
uint64_t HELPER(shr_i64)(uint64_t arg1, uint64_t arg2)
{
return arg1 >> arg2;
}
int64_t HELPER(sar_i64)(int64_t arg1, int64_t arg2)
{
return arg1 >> arg2;
}
int64_t HELPER(div_i64)(int64_t arg1, int64_t arg2)
{
return arg1 / arg2;
}
int64_t HELPER(rem_i64)(int64_t arg1, int64_t arg2)
{
return arg1 % arg2;
}
uint64_t HELPER(divu_i64)(uint64_t arg1, uint64_t arg2)
{
return arg1 / arg2;
}
uint64_t HELPER(remu_i64)(uint64_t arg1, uint64_t arg2)
{
return arg1 % arg2;
}
uint64_t HELPER(muluh_i64)(uint64_t arg1, uint64_t arg2)
{
uint64_t l, h;
mulu64(&l, &h, arg1, arg2);
return h;
}
int64_t HELPER(mulsh_i64)(int64_t arg1, int64_t arg2)
{
uint64_t l, h;
muls64(&l, &h, arg1, arg2);
return h;
}
uint32_t HELPER(clz_i32)(uint32_t arg, uint32_t zero_val)
{
return arg ? clz32(arg) : zero_val;
}
uint32_t HELPER(ctz_i32)(uint32_t arg, uint32_t zero_val)
{
return arg ? ctz32(arg) : zero_val;
}
uint64_t HELPER(clz_i64)(uint64_t arg, uint64_t zero_val)
{
return arg ? clz64(arg) : zero_val;
}
uint64_t HELPER(ctz_i64)(uint64_t arg, uint64_t zero_val)
{
return arg ? ctz64(arg) : zero_val;
}
uint32_t HELPER(clrsb_i32)(uint32_t arg)
{
return clrsb32(arg);
}
uint64_t HELPER(clrsb_i64)(uint64_t arg)
{
return clrsb64(arg);
}
uint32_t HELPER(ctpop_i32)(uint32_t arg)
{
return ctpop32(arg);
}
uint64_t HELPER(ctpop_i64)(uint64_t arg)
{
return ctpop64(arg);
}
void *HELPER(lookup_tb_ptr)(CPUArchState *env)
{
CPUState *cpu = env_cpu(env);
TranslationBlock *tb;
target_ulong cs_base, pc;
uint32_t flags;
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, curr_cflags());
if (tb == NULL) {
return tcg_ctx->code_gen_epilogue;
}
qemu_log_mask_and_addr(CPU_LOG_EXEC, pc,
"Chain %d: %p ["
TARGET_FMT_lx "/" TARGET_FMT_lx "/%#x] %s\n",
cpu->cpu_index, tb->tc.ptr, cs_base, pc, flags,
lookup_symbol(pc));
return tb->tc.ptr;
}
void HELPER(exit_atomic)(CPUArchState *env)
{
cpu_loop_exit_atomic(env_cpu(env), GETPC());
}
-307
View File
@@ -1,307 +0,0 @@
DEF_HELPER_FLAGS_2(div_i32, TCG_CALL_NO_RWG_SE, s32, s32, s32)
DEF_HELPER_FLAGS_2(rem_i32, TCG_CALL_NO_RWG_SE, s32, s32, s32)
DEF_HELPER_FLAGS_2(divu_i32, TCG_CALL_NO_RWG_SE, i32, i32, i32)
DEF_HELPER_FLAGS_2(remu_i32, TCG_CALL_NO_RWG_SE, i32, i32, i32)
DEF_HELPER_FLAGS_2(div_i64, TCG_CALL_NO_RWG_SE, s64, s64, s64)
DEF_HELPER_FLAGS_2(rem_i64, TCG_CALL_NO_RWG_SE, s64, s64, s64)
DEF_HELPER_FLAGS_2(divu_i64, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_2(remu_i64, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_2(shl_i64, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_2(shr_i64, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_2(sar_i64, TCG_CALL_NO_RWG_SE, s64, s64, s64)
DEF_HELPER_FLAGS_2(mulsh_i64, TCG_CALL_NO_RWG_SE, s64, s64, s64)
DEF_HELPER_FLAGS_2(muluh_i64, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_2(clz_i32, TCG_CALL_NO_RWG_SE, i32, i32, i32)
DEF_HELPER_FLAGS_2(ctz_i32, TCG_CALL_NO_RWG_SE, i32, i32, i32)
DEF_HELPER_FLAGS_2(clz_i64, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_2(ctz_i64, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_1(clrsb_i32, TCG_CALL_NO_RWG_SE, i32, i32)
DEF_HELPER_FLAGS_1(clrsb_i64, TCG_CALL_NO_RWG_SE, i64, i64)
DEF_HELPER_FLAGS_1(ctpop_i32, TCG_CALL_NO_RWG_SE, i32, i32)
DEF_HELPER_FLAGS_1(ctpop_i64, TCG_CALL_NO_RWG_SE, i64, i64)
DEF_HELPER_FLAGS_1(lookup_tb_ptr, TCG_CALL_NO_WG_SE, ptr, env)
DEF_HELPER_FLAGS_1(exit_atomic, TCG_CALL_NO_WG, noreturn, env)
#ifdef CONFIG_SOFTMMU
DEF_HELPER_FLAGS_5(atomic_cmpxchgb, TCG_CALL_NO_WG,
i32, env, tl, i32, i32, i32)
DEF_HELPER_FLAGS_5(atomic_cmpxchgw_be, TCG_CALL_NO_WG,
i32, env, tl, i32, i32, i32)
DEF_HELPER_FLAGS_5(atomic_cmpxchgw_le, TCG_CALL_NO_WG,
i32, env, tl, i32, i32, i32)
DEF_HELPER_FLAGS_5(atomic_cmpxchgl_be, TCG_CALL_NO_WG,
i32, env, tl, i32, i32, i32)
DEF_HELPER_FLAGS_5(atomic_cmpxchgl_le, TCG_CALL_NO_WG,
i32, env, tl, i32, i32, i32)
#ifdef CONFIG_ATOMIC64
DEF_HELPER_FLAGS_5(atomic_cmpxchgq_be, TCG_CALL_NO_WG,
i64, env, tl, i64, i64, i32)
DEF_HELPER_FLAGS_5(atomic_cmpxchgq_le, TCG_CALL_NO_WG,
i64, env, tl, i64, i64, i32)
#endif
#ifdef CONFIG_ATOMIC64
#define GEN_ATOMIC_HELPERS(NAME) \
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), b), \
TCG_CALL_NO_WG, i32, env, tl, i32, i32) \
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_le), \
TCG_CALL_NO_WG, i32, env, tl, i32, i32) \
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_be), \
TCG_CALL_NO_WG, i32, env, tl, i32, i32) \
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_le), \
TCG_CALL_NO_WG, i32, env, tl, i32, i32) \
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_be), \
TCG_CALL_NO_WG, i32, env, tl, i32, i32) \
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), q_le), \
TCG_CALL_NO_WG, i64, env, tl, i64, i32) \
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), q_be), \
TCG_CALL_NO_WG, i64, env, tl, i64, i32)
#else
#define GEN_ATOMIC_HELPERS(NAME) \
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), b), \
TCG_CALL_NO_WG, i32, env, tl, i32, i32) \
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_le), \
TCG_CALL_NO_WG, i32, env, tl, i32, i32) \
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), w_be), \
TCG_CALL_NO_WG, i32, env, tl, i32, i32) \
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_le), \
TCG_CALL_NO_WG, i32, env, tl, i32, i32) \
DEF_HELPER_FLAGS_4(glue(glue(atomic_, NAME), l_be), \
TCG_CALL_NO_WG, i32, env, tl, i32, i32)
#endif /* CONFIG_ATOMIC64 */
#else
DEF_HELPER_FLAGS_4(atomic_cmpxchgb, TCG_CALL_NO_WG, i32, env, tl, i32, i32)
DEF_HELPER_FLAGS_4(atomic_cmpxchgw_be, TCG_CALL_NO_WG, i32, env, tl, i32, i32)
DEF_HELPER_FLAGS_4(atomic_cmpxchgw_le, TCG_CALL_NO_WG, i32, env, tl, i32, i32)
DEF_HELPER_FLAGS_4(atomic_cmpxchgl_be, TCG_CALL_NO_WG, i32, env, tl, i32, i32)
DEF_HELPER_FLAGS_4(atomic_cmpxchgl_le, TCG_CALL_NO_WG, i32, env, tl, i32, i32)
#ifdef CONFIG_ATOMIC64
DEF_HELPER_FLAGS_4(atomic_cmpxchgq_be, TCG_CALL_NO_WG, i64, env, tl, i64, i64)
DEF_HELPER_FLAGS_4(atomic_cmpxchgq_le, TCG_CALL_NO_WG, i64, env, tl, i64, i64)
#endif
#ifdef CONFIG_ATOMIC64
#define GEN_ATOMIC_HELPERS(NAME) \
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), b), \
TCG_CALL_NO_WG, i32, env, tl, i32) \
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), w_le), \
TCG_CALL_NO_WG, i32, env, tl, i32) \
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), w_be), \
TCG_CALL_NO_WG, i32, env, tl, i32) \
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), l_le), \
TCG_CALL_NO_WG, i32, env, tl, i32) \
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), l_be), \
TCG_CALL_NO_WG, i32, env, tl, i32) \
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), q_le), \
TCG_CALL_NO_WG, i64, env, tl, i64) \
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), q_be), \
TCG_CALL_NO_WG, i64, env, tl, i64)
#else
#define GEN_ATOMIC_HELPERS(NAME) \
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), b), \
TCG_CALL_NO_WG, i32, env, tl, i32) \
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), w_le), \
TCG_CALL_NO_WG, i32, env, tl, i32) \
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), w_be), \
TCG_CALL_NO_WG, i32, env, tl, i32) \
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), l_le), \
TCG_CALL_NO_WG, i32, env, tl, i32) \
DEF_HELPER_FLAGS_3(glue(glue(atomic_, NAME), l_be), \
TCG_CALL_NO_WG, i32, env, tl, i32)
#endif /* CONFIG_ATOMIC64 */
#endif /* CONFIG_SOFTMMU */
GEN_ATOMIC_HELPERS(fetch_add)
GEN_ATOMIC_HELPERS(fetch_and)
GEN_ATOMIC_HELPERS(fetch_or)
GEN_ATOMIC_HELPERS(fetch_xor)
GEN_ATOMIC_HELPERS(fetch_smin)
GEN_ATOMIC_HELPERS(fetch_umin)
GEN_ATOMIC_HELPERS(fetch_smax)
GEN_ATOMIC_HELPERS(fetch_umax)
GEN_ATOMIC_HELPERS(add_fetch)
GEN_ATOMIC_HELPERS(and_fetch)
GEN_ATOMIC_HELPERS(or_fetch)
GEN_ATOMIC_HELPERS(xor_fetch)
GEN_ATOMIC_HELPERS(smin_fetch)
GEN_ATOMIC_HELPERS(umin_fetch)
GEN_ATOMIC_HELPERS(smax_fetch)
GEN_ATOMIC_HELPERS(umax_fetch)
GEN_ATOMIC_HELPERS(xchg)
#undef GEN_ATOMIC_HELPERS
DEF_HELPER_FLAGS_3(gvec_mov, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_dup8, TCG_CALL_NO_RWG, void, ptr, i32, i32)
DEF_HELPER_FLAGS_3(gvec_dup16, TCG_CALL_NO_RWG, void, ptr, i32, i32)
DEF_HELPER_FLAGS_3(gvec_dup32, TCG_CALL_NO_RWG, void, ptr, i32, i32)
DEF_HELPER_FLAGS_3(gvec_dup64, TCG_CALL_NO_RWG, void, ptr, i32, i64)
DEF_HELPER_FLAGS_4(gvec_add8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_add16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_add32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_add64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_adds8, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
DEF_HELPER_FLAGS_4(gvec_adds16, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
DEF_HELPER_FLAGS_4(gvec_adds32, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
DEF_HELPER_FLAGS_4(gvec_adds64, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
DEF_HELPER_FLAGS_4(gvec_sub8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_sub16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_sub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_sub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_subs8, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
DEF_HELPER_FLAGS_4(gvec_subs16, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
DEF_HELPER_FLAGS_4(gvec_subs32, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
DEF_HELPER_FLAGS_4(gvec_subs64, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
DEF_HELPER_FLAGS_4(gvec_mul8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_mul16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_mul32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_mul64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_muls8, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
DEF_HELPER_FLAGS_4(gvec_muls16, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
DEF_HELPER_FLAGS_4(gvec_muls32, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
DEF_HELPER_FLAGS_4(gvec_muls64, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
DEF_HELPER_FLAGS_4(gvec_ssadd8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ssadd16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ssadd32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ssadd64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_sssub8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_sssub16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_sssub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_sssub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_usadd8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_usadd16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_usadd32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_usadd64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ussub8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ussub16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ussub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ussub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_smin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_smin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_smin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_smin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_smax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_smax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_smax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_smax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_umin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_umin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_umin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_umin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_umax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_umax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_umax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_umax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_neg8, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_neg64, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_abs8, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_abs16, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_abs32, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_abs64, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_not, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_and, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_or, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_xor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_andc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_orc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_nand, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_nor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_eqv, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ands, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
DEF_HELPER_FLAGS_4(gvec_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
DEF_HELPER_FLAGS_4(gvec_ors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
DEF_HELPER_FLAGS_3(gvec_shl8i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_shl16i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_shl32i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_shl64i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_shr8i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_shr16i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_shr32i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_shr64i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_sar8i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_sar16i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_sar32i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(gvec_sar64i, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_shl8v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_shl16v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_shl32v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_shl64v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_shr8v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_shr16v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_shr32v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_shr64v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_sar8v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_sar16v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_sar32v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_sar64v, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_eq8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_eq16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_eq32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_eq64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ne8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ne16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ne32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ne64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_lt8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_lt16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_lt32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_lt64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_le8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_le16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_le32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_le64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ltu8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ltu16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ltu32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ltu64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_leu8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_leu16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_leu32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_leu64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_5(gvec_bitsel, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+1 -1
View File
@@ -1,4 +1,4 @@
# See docs/devel/tracing.txt for syntax documentation.
# Trace events for debugging and performance instrumentation
# TCG related tracing (mostly disabled by default)
# cpu-exec.c
+680 -1145
View File
File diff suppressed because it is too large. Load diff
+3 -6
View File
@@ -6,7 +6,7 @@
* 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.1 of the License, or (at your option) any later version.
* 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
@@ -23,13 +23,10 @@
/* translate-all.c */
struct page_collection *page_collection_lock(tb_page_addr_t start,
tb_page_addr_t end);
void page_collection_unlock(struct page_collection *set);
void tb_invalidate_phys_page_fast(struct page_collection *pages,
tb_page_addr_t start, int len);
void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len);
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
int is_cpu_write_access);
void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end);
void tb_check_watchpoint(CPUState *cpu);
#ifdef CONFIG_USER_ONLY
-129
View File
@@ -1,129 +0,0 @@
/*
* Generic intermediate code generation.
*
* Copyright (C) 2016-2017 Lluís Vilanova <vilanova@ac.upc.edu>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "cpu.h"
#include "tcg/tcg.h"
#include "tcg/tcg-op.h"
#include "exec/exec-all.h"
#include "exec/gen-icount.h"
#include "exec/log.h"
#include "exec/translator.h"
/* Pairs with tcg_clear_temp_count.
To be called by #TranslatorOps.{translate_insn,tb_stop} if
(1) the target is sufficiently clean to support reporting,
(2) as and when all temporaries are known to be consumed.
For most targets, (2) is at the end of translate_insn. */
void translator_loop_temp_check(DisasContextBase *db)
{
if (tcg_check_temp_count()) {
qemu_log("warning: TCG temporary leaks before "
TARGET_FMT_lx "\n", db->pc_next);
}
}
void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
CPUState *cpu, TranslationBlock *tb, int max_insns)
{
int bp_insn = 0;
/* Initialize DisasContext */
db->tb = tb;
db->pc_first = tb->pc;
db->pc_next = db->pc_first;
db->is_jmp = DISAS_NEXT;
db->num_insns = 0;
db->max_insns = max_insns;
db->singlestep_enabled = cpu->singlestep_enabled;
ops->init_disas_context(db, cpu);
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
/* Reset the temp count so that we can identify leaks */
tcg_clear_temp_count();
/* Start translating. */
gen_tb_start(db->tb);
ops->tb_start(db, cpu);
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
while (true) {
db->num_insns++;
ops->insn_start(db, cpu);
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
/* Pass breakpoint hits to target for further processing */
if (!db->singlestep_enabled
&& unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
CPUBreakpoint *bp;
QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
if (bp->pc == db->pc_next) {
if (ops->breakpoint_check(db, cpu, bp)) {
bp_insn = 1;
break;
}
}
}
/* The breakpoint_check hook may use DISAS_TOO_MANY to indicate
that only one more instruction is to be executed. Otherwise
it should use DISAS_NORETURN when generating an exception,
but may use a DISAS_TARGET_* value for Something Else. */
if (db->is_jmp > DISAS_TOO_MANY) {
break;
}
}
/* Disassemble one instruction. The translate_insn hook should
update db->pc_next and db->is_jmp to indicate what should be
done next -- either exiting this loop or locate the start of
the next instruction. */
if (db->num_insns == db->max_insns
&& (tb_cflags(db->tb) & CF_LAST_IO)) {
/* Accept I/O on the last instruction. */
gen_io_start();
ops->translate_insn(db, cpu);
gen_io_end();
} else {
ops->translate_insn(db, cpu);
}
/* Stop translation if translate_insn so indicated. */
if (db->is_jmp != DISAS_NEXT) {
break;
}
/* Stop translation if the output buffer is full,
or we have executed all of the allowed instructions. */
if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
db->is_jmp = DISAS_TOO_MANY;
break;
}
}
/* Emit code to exit the TB, as indicated by db->is_jmp. */
ops->tb_stop(db, cpu);
gen_tb_end(db->tb, db->num_insns - bp_insn);
/* The disas_log hook may use these values rather than recompute. */
db->tb->size = db->pc_next - db->pc_first;
db->tb->icount = db->num_insns;
#ifdef DEBUG_DISAS
if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
&& qemu_log_in_addr_range(db->pc_first)) {
qemu_log_lock();
qemu_log("----------------\n");
ops->disas_log(db, cpu);
qemu_log("\n");
qemu_log_unlock();
}
#endif
}
-756
View File
@@ -1,756 +0,0 @@
/*
* User emulator execution
*
* Copyright (c) 2003-2005 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.1 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 "cpu.h"
#include "disas/disas.h"
#include "exec/exec-all.h"
#include "tcg.h"
#include "qemu/bitops.h"
#include "exec/cpu_ldst.h"
#include "translate-all.h"
#include "exec/helper-proto.h"
#include "qemu/atomic128.h"
#undef EAX
#undef ECX
#undef EDX
#undef EBX
#undef ESP
#undef EBP
#undef ESI
#undef EDI
#undef EIP
#ifdef __linux__
#include <sys/ucontext.h>
#endif
__thread uintptr_t helper_retaddr;
//#define DEBUG_SIGNAL
/* exit the current TB from a signal handler. The host registers are
restored in a state compatible with the CPU emulator
*/
static void cpu_exit_tb_from_sighandler(CPUState *cpu, sigset_t *old_set)
{
/* XXX: use siglongjmp ? */
sigprocmask(SIG_SETMASK, old_set, NULL);
cpu_loop_exit_noexc(cpu);
}
/* 'pc' is the host PC at which the exception was raised. 'address' is
the effective address of the memory exception. 'is_write' is 1 if a
write caused the exception and otherwise 0'. 'old_set' is the
signal set which should be restored */
static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
int is_write, sigset_t *old_set)
{
CPUState *cpu = current_cpu;
CPUClass *cc;
unsigned long address = (unsigned long)info->si_addr;
MMUAccessType access_type = is_write ? MMU_DATA_STORE : MMU_DATA_LOAD;
switch (helper_retaddr) {
default:
/*
* Fault during host memory operation within a helper function.
* The helper's host return address, saved here, gives us a
* pointer into the generated code that will unwind to the
* correct guest pc.
*/
pc = helper_retaddr;
break;
case 0:
/*
* Fault during host memory operation within generated code.
* (Or, a unrelated bug within qemu, but we can't tell from here).
*
* We take the host pc from the signal frame. However, we cannot
* use that value directly. Within cpu_restore_state_from_tb, we
* assume PC comes from GETPC(), as used by the helper functions,
* so we adjust the address by -GETPC_ADJ to form an address that
* is within the call insn, so that the address does not accidentially
* match the beginning of the next guest insn. However, when the
* pc comes from the signal frame it points to the actual faulting
* host memory insn and not the return from a call insn.
*
* Therefore, adjust to compensate for what will be done later
* by cpu_restore_state_from_tb.
*/
pc += GETPC_ADJ;
break;
case 1:
/*
* Fault during host read for translation, or loosely, "execution".
*
* The guest pc is already pointing to the start of the TB for which
* code is being generated. If the guest translator manages the
* page crossings correctly, this is exactly the correct address
* (and if the translator doesn't handle page boundaries correctly
* there's little we can do about that here). Therefore, do not
* trigger the unwinder.
*
* Like tb_gen_code, release the memory lock before cpu_loop_exit.
*/
pc = 0;
access_type = MMU_INST_FETCH;
mmap_unlock();
break;
}
/* For synchronous signals we expect to be coming from the vCPU
* thread (so current_cpu should be valid) and either from running
* code or during translation which can fault as we cross pages.
*
* If neither is true then something has gone wrong and we should
* abort rather than try and restart the vCPU execution.
*/
if (!cpu || !cpu->running) {
printf("qemu:%s received signal outside vCPU context @ pc=0x%"
PRIxPTR "\n", __func__, pc);
abort();
}
#if defined(DEBUG_SIGNAL)
printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
pc, address, is_write, *(unsigned long *)old_set);
#endif
/* XXX: locking issue */
/* Note that it is important that we don't call page_unprotect() unless
* this is really a "write to nonwriteable page" fault, because
* page_unprotect() assumes that if it is called for an access to
* a page that's writeable this means we had two threads racing and
* another thread got there first and already made the page writeable;
* so we will retry the access. If we were to call page_unprotect()
* for some other kind of fault that should really be passed to the
* guest, we'd end up in an infinite loop of retrying the faulting
* access.
*/
if (is_write && info->si_signo == SIGSEGV && info->si_code == SEGV_ACCERR &&
h2g_valid(address)) {
switch (page_unprotect(h2g(address), pc)) {
case 0:
/* Fault not caused by a page marked unwritable to protect
* cached translations, must be the guest binary's problem.
*/
break;
case 1:
/* Fault caused by protection of cached translation; TBs
* invalidated, so resume execution. Retain helper_retaddr
* for a possible second fault.
*/
return 1;
case 2:
/* Fault caused by protection of cached translation, and the
* currently executing TB was modified and must be exited
* immediately. Clear helper_retaddr for next execution.
*/
clear_helper_retaddr();
cpu_exit_tb_from_sighandler(cpu, old_set);
/* NORETURN */
default:
g_assert_not_reached();
}
}
/* Convert forcefully to guest address space, invalid addresses
are still valid segv ones */
address = h2g_nocheck(address);
/*
* There is no way the target can handle this other than raising
* an exception. Undo signal and retaddr state prior to longjmp.
*/
sigprocmask(SIG_SETMASK, old_set, NULL);
clear_helper_retaddr();
cc = CPU_GET_CLASS(cpu);
cc->tlb_fill(cpu, address, 0, access_type, MMU_USER_IDX, false, pc);
g_assert_not_reached();
}
#if defined(__i386__)
#if defined(__NetBSD__)
#include <ucontext.h>
#define EIP_sig(context) ((context)->uc_mcontext.__gregs[_REG_EIP])
#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO])
#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR])
#define MASK_sig(context) ((context)->uc_sigmask)
#elif defined(__FreeBSD__) || defined(__DragonFly__)
#include <ucontext.h>
#define EIP_sig(context) (*((unsigned long *)&(context)->uc_mcontext.mc_eip))
#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno)
#define ERROR_sig(context) ((context)->uc_mcontext.mc_err)
#define MASK_sig(context) ((context)->uc_sigmask)
#elif defined(__OpenBSD__)
#define EIP_sig(context) ((context)->sc_eip)
#define TRAP_sig(context) ((context)->sc_trapno)
#define ERROR_sig(context) ((context)->sc_err)
#define MASK_sig(context) ((context)->sc_mask)
#else
#define EIP_sig(context) ((context)->uc_mcontext.gregs[REG_EIP])
#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO])
#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR])
#define MASK_sig(context) ((context)->uc_sigmask)
#endif
int cpu_signal_handler(int host_signum, void *pinfo,
void *puc)
{
siginfo_t *info = pinfo;
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
ucontext_t *uc = puc;
#elif defined(__OpenBSD__)
struct sigcontext *uc = puc;
#else
ucontext_t *uc = puc;
#endif
unsigned long pc;
int trapno;
#ifndef REG_EIP
/* for glibc 2.1 */
#define REG_EIP EIP
#define REG_ERR ERR
#define REG_TRAPNO TRAPNO
#endif
pc = EIP_sig(uc);
trapno = TRAP_sig(uc);
return handle_cpu_signal(pc, info,
trapno == 0xe ? (ERROR_sig(uc) >> 1) & 1 : 0,
&MASK_sig(uc));
}
#elif defined(__x86_64__)
#ifdef __NetBSD__
#define PC_sig(context) _UC_MACHINE_PC(context)
#define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO])
#define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR])
#define MASK_sig(context) ((context)->uc_sigmask)
#elif defined(__OpenBSD__)
#define PC_sig(context) ((context)->sc_rip)
#define TRAP_sig(context) ((context)->sc_trapno)
#define ERROR_sig(context) ((context)->sc_err)
#define MASK_sig(context) ((context)->sc_mask)
#elif defined(__FreeBSD__) || defined(__DragonFly__)
#include <ucontext.h>
#define PC_sig(context) (*((unsigned long *)&(context)->uc_mcontext.mc_rip))
#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno)
#define ERROR_sig(context) ((context)->uc_mcontext.mc_err)
#define MASK_sig(context) ((context)->uc_sigmask)
#else
#define PC_sig(context) ((context)->uc_mcontext.gregs[REG_RIP])
#define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO])
#define ERROR_sig(context) ((context)->uc_mcontext.gregs[REG_ERR])
#define MASK_sig(context) ((context)->uc_sigmask)
#endif
int cpu_signal_handler(int host_signum, void *pinfo,
void *puc)
{
siginfo_t *info = pinfo;
unsigned long pc;
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
ucontext_t *uc = puc;
#elif defined(__OpenBSD__)
struct sigcontext *uc = puc;
#else
ucontext_t *uc = puc;
#endif
pc = PC_sig(uc);
return handle_cpu_signal(pc, info,
TRAP_sig(uc) == 0xe ? (ERROR_sig(uc) >> 1) & 1 : 0,
&MASK_sig(uc));
}
#elif defined(_ARCH_PPC)
/***********************************************************************
* signal context platform-specific definitions
* From Wine
*/
#ifdef linux
/* All Registers access - only for local access */
#define REG_sig(reg_name, context) \
((context)->uc_mcontext.regs->reg_name)
/* Gpr Registers access */
#define GPR_sig(reg_num, context) REG_sig(gpr[reg_num], context)
/* Program counter */
#define IAR_sig(context) REG_sig(nip, context)
/* Machine State Register (Supervisor) */
#define MSR_sig(context) REG_sig(msr, context)
/* Count register */
#define CTR_sig(context) REG_sig(ctr, context)
/* User's integer exception register */
#define XER_sig(context) REG_sig(xer, context)
/* Link register */
#define LR_sig(context) REG_sig(link, context)
/* Condition register */
#define CR_sig(context) REG_sig(ccr, context)
/* Float Registers access */
#define FLOAT_sig(reg_num, context) \
(((double *)((char *)((context)->uc_mcontext.regs + 48 * 4)))[reg_num])
#define FPSCR_sig(context) \
(*(int *)((char *)((context)->uc_mcontext.regs + (48 + 32 * 2) * 4)))
/* Exception Registers access */
#define DAR_sig(context) REG_sig(dar, context)
#define DSISR_sig(context) REG_sig(dsisr, context)
#define TRAP_sig(context) REG_sig(trap, context)
#endif /* linux */
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#include <ucontext.h>
#define IAR_sig(context) ((context)->uc_mcontext.mc_srr0)
#define MSR_sig(context) ((context)->uc_mcontext.mc_srr1)
#define CTR_sig(context) ((context)->uc_mcontext.mc_ctr)
#define XER_sig(context) ((context)->uc_mcontext.mc_xer)
#define LR_sig(context) ((context)->uc_mcontext.mc_lr)
#define CR_sig(context) ((context)->uc_mcontext.mc_cr)
/* Exception Registers access */
#define DAR_sig(context) ((context)->uc_mcontext.mc_dar)
#define DSISR_sig(context) ((context)->uc_mcontext.mc_dsisr)
#define TRAP_sig(context) ((context)->uc_mcontext.mc_exc)
#endif /* __FreeBSD__|| __FreeBSD_kernel__ */
int cpu_signal_handler(int host_signum, void *pinfo,
void *puc)
{
siginfo_t *info = pinfo;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
ucontext_t *uc = puc;
#else
ucontext_t *uc = puc;
#endif
unsigned long pc;
int is_write;
pc = IAR_sig(uc);
is_write = 0;
#if 0
/* ppc 4xx case */
if (DSISR_sig(uc) & 0x00800000) {
is_write = 1;
}
#else
if (TRAP_sig(uc) != 0x400 && (DSISR_sig(uc) & 0x02000000)) {
is_write = 1;
}
#endif
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
}
#elif defined(__alpha__)
int cpu_signal_handler(int host_signum, void *pinfo,
void *puc)
{
siginfo_t *info = pinfo;
ucontext_t *uc = puc;
uint32_t *pc = uc->uc_mcontext.sc_pc;
uint32_t insn = *pc;
int is_write = 0;
/* XXX: need kernel patch to get write flag faster */
switch (insn >> 26) {
case 0x0d: /* stw */
case 0x0e: /* stb */
case 0x0f: /* stq_u */
case 0x24: /* stf */
case 0x25: /* stg */
case 0x26: /* sts */
case 0x27: /* stt */
case 0x2c: /* stl */
case 0x2d: /* stq */
case 0x2e: /* stl_c */
case 0x2f: /* stq_c */
is_write = 1;
}
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
}
#elif defined(__sparc__)
int cpu_signal_handler(int host_signum, void *pinfo,
void *puc)
{
siginfo_t *info = pinfo;
int is_write;
uint32_t insn;
#if !defined(__arch64__) || defined(CONFIG_SOLARIS)
uint32_t *regs = (uint32_t *)(info + 1);
void *sigmask = (regs + 20);
/* XXX: is there a standard glibc define ? */
unsigned long pc = regs[1];
#else
#ifdef __linux__
struct sigcontext *sc = puc;
unsigned long pc = sc->sigc_regs.tpc;
void *sigmask = (void *)sc->sigc_mask;
#elif defined(__OpenBSD__)
struct sigcontext *uc = puc;
unsigned long pc = uc->sc_pc;
void *sigmask = (void *)(long)uc->sc_mask;
#elif defined(__NetBSD__)
ucontext_t *uc = puc;
unsigned long pc = _UC_MACHINE_PC(uc);
void *sigmask = (void *)&uc->uc_sigmask;
#endif
#endif
/* XXX: need kernel patch to get write flag faster */
is_write = 0;
insn = *(uint32_t *)pc;
if ((insn >> 30) == 3) {
switch ((insn >> 19) & 0x3f) {
case 0x05: /* stb */
case 0x15: /* stba */
case 0x06: /* sth */
case 0x16: /* stha */
case 0x04: /* st */
case 0x14: /* sta */
case 0x07: /* std */
case 0x17: /* stda */
case 0x0e: /* stx */
case 0x1e: /* stxa */
case 0x24: /* stf */
case 0x34: /* stfa */
case 0x27: /* stdf */
case 0x37: /* stdfa */
case 0x26: /* stqf */
case 0x36: /* stqfa */
case 0x25: /* stfsr */
case 0x3c: /* casa */
case 0x3e: /* casxa */
is_write = 1;
break;
}
}
return handle_cpu_signal(pc, info, is_write, sigmask);
}
#elif defined(__arm__)
#if defined(__NetBSD__)
#include <ucontext.h>
#endif
int cpu_signal_handler(int host_signum, void *pinfo,
void *puc)
{
siginfo_t *info = pinfo;
#if defined(__NetBSD__)
ucontext_t *uc = puc;
#else
ucontext_t *uc = puc;
#endif
unsigned long pc;
int is_write;
#if defined(__NetBSD__)
pc = uc->uc_mcontext.__gregs[_REG_R15];
#elif defined(__GLIBC__) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
pc = uc->uc_mcontext.gregs[R15];
#else
pc = uc->uc_mcontext.arm_pc;
#endif
/* error_code is the FSR value, in which bit 11 is WnR (assuming a v6 or
* later processor; on v5 we will always report this as a read).
*/
is_write = extract32(uc->uc_mcontext.error_code, 11, 1);
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
}
#elif defined(__aarch64__)
#ifndef ESR_MAGIC
/* Pre-3.16 kernel headers don't have these, so provide fallback definitions */
#define ESR_MAGIC 0x45535201
struct esr_context {
struct _aarch64_ctx head;
uint64_t esr;
};
#endif
static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc)
{
return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved;
}
static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr)
{
return (struct _aarch64_ctx *)((char *)hdr + hdr->size);
}
int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
{
siginfo_t *info = pinfo;
ucontext_t *uc = puc;
uintptr_t pc = uc->uc_mcontext.pc;
bool is_write;
struct _aarch64_ctx *hdr;
struct esr_context const *esrctx = NULL;
/* Find the esr_context, which has the WnR bit in it */
for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) {
if (hdr->magic == ESR_MAGIC) {
esrctx = (struct esr_context const *)hdr;
break;
}
}
if (esrctx) {
/* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */
uint64_t esr = esrctx->esr;
is_write = extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1;
} else {
/*
* Fall back to parsing instructions; will only be needed
* for really ancient (pre-3.16) kernels.
*/
uint32_t insn = *(uint32_t *)pc;
is_write = ((insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */
|| (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */
|| (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */
|| (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */
|| (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */
|| (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */
|| (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */
/* Ignore bits 10, 11 & 21, controlling indexing. */
|| (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */
|| (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */
/* Ignore bits 23 & 24, controlling indexing. */
|| (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */
}
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
}
#elif defined(__s390__)
int cpu_signal_handler(int host_signum, void *pinfo,
void *puc)
{
siginfo_t *info = pinfo;
ucontext_t *uc = puc;
unsigned long pc;
uint16_t *pinsn;
int is_write = 0;
pc = uc->uc_mcontext.psw.addr;
/* ??? On linux, the non-rt signal handler has 4 (!) arguments instead
of the normal 2 arguments. The 3rd argument contains the "int_code"
from the hardware which does in fact contain the is_write value.
The rt signal handler, as far as I can tell, does not give this value
at all. Not that we could get to it from here even if it were. */
/* ??? This is not even close to complete, since it ignores all
of the read-modify-write instructions. */
pinsn = (uint16_t *)pc;
switch (pinsn[0] >> 8) {
case 0x50: /* ST */
case 0x42: /* STC */
case 0x40: /* STH */
is_write = 1;
break;
case 0xc4: /* RIL format insns */
switch (pinsn[0] & 0xf) {
case 0xf: /* STRL */
case 0xb: /* STGRL */
case 0x7: /* STHRL */
is_write = 1;
}
break;
case 0xe3: /* RXY format insns */
switch (pinsn[2] & 0xff) {
case 0x50: /* STY */
case 0x24: /* STG */
case 0x72: /* STCY */
case 0x70: /* STHY */
case 0x8e: /* STPQ */
case 0x3f: /* STRVH */
case 0x3e: /* STRV */
case 0x2f: /* STRVG */
is_write = 1;
}
break;
}
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
}
#elif defined(__mips__)
int cpu_signal_handler(int host_signum, void *pinfo,
void *puc)
{
siginfo_t *info = pinfo;
ucontext_t *uc = puc;
greg_t pc = uc->uc_mcontext.pc;
int is_write;
/* XXX: compute is_write */
is_write = 0;
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
}
#elif defined(__riscv)
int cpu_signal_handler(int host_signum, void *pinfo,
void *puc)
{
siginfo_t *info = pinfo;
ucontext_t *uc = puc;
greg_t pc = uc->uc_mcontext.__gregs[REG_PC];
uint32_t insn = *(uint32_t *)pc;
int is_write = 0;
/* Detect store by reading the instruction at the program
counter. Note: we currently only generate 32-bit
instructions so we thus only detect 32-bit stores */
switch (((insn >> 0) & 0b11)) {
case 3:
switch (((insn >> 2) & 0b11111)) {
case 8:
switch (((insn >> 12) & 0b111)) {
case 0: /* sb */
case 1: /* sh */
case 2: /* sw */
case 3: /* sd */
case 4: /* sq */
is_write = 1;
break;
default:
break;
}
break;
case 9:
switch (((insn >> 12) & 0b111)) {
case 2: /* fsw */
case 3: /* fsd */
case 4: /* fsq */
is_write = 1;
break;
default:
break;
}
break;
default:
break;
}
}
/* Check for compressed instructions */
switch (((insn >> 13) & 0b111)) {
case 7:
switch (insn & 0b11) {
case 0: /*c.sd */
case 2: /* c.sdsp */
is_write = 1;
break;
default:
break;
}
break;
case 6:
switch (insn & 0b11) {
case 0: /* c.sw */
case 3: /* c.swsp */
is_write = 1;
break;
default:
break;
}
break;
default:
break;
}
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
}
#else
#error host CPU specific signal handler needed
#endif
/* The softmmu versions of these helpers are in cputlb.c. */
/* Do not allow unaligned operations to proceed. Return the host address. */
static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
int size, uintptr_t retaddr)
{
/* Enforce qemu required alignment. */
if (unlikely(addr & (size - 1))) {
cpu_loop_exit_atomic(env_cpu(env), retaddr);
}
void *ret = g2h(addr);
set_helper_retaddr(retaddr);
return ret;
}
/* Macro to call the above, with local variables from the use context. */
#define ATOMIC_MMU_DECLS do {} while (0)
#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, DATA_SIZE, GETPC())
#define ATOMIC_MMU_CLEANUP do { clear_helper_retaddr(); } while (0)
#define ATOMIC_NAME(X) HELPER(glue(glue(atomic_ ## X, SUFFIX), END))
#define EXTRA_ARGS
#define DATA_SIZE 1
#include "atomic_template.h"
#define DATA_SIZE 2
#include "atomic_template.h"
#define DATA_SIZE 4
#include "atomic_template.h"
#ifdef CONFIG_ATOMIC64
#define DATA_SIZE 8
#include "atomic_template.h"
#endif
/* The following is only callable from other helpers, and matches up
with the softmmu version. */
#if HAVE_ATOMIC128 || HAVE_CMPXCHG128
#undef EXTRA_ARGS
#undef ATOMIC_NAME
#undef ATOMIC_MMU_LOOKUP
#define EXTRA_ARGS , TCGMemOpIdx oi, uintptr_t retaddr
#define ATOMIC_NAME(X) \
HELPER(glue(glue(glue(atomic_ ## X, SUFFIX), END), _mmu))
#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, DATA_SIZE, retaddr)
#define DATA_SIZE 16
#include "atomic_template.h"
#endif
+9 -14
View File
@@ -22,15 +22,15 @@
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h"
#include "sysemu/sysemu.h"
#include "sysemu/arch_init.h"
#include "hw/pci/pci.h"
#include "hw/audio/soundhw.h"
#include "qapi/qapi-commands-misc.h"
#include "qapi/error.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
#include "qmp-commands.h"
#include "hw/acpi/acpi.h"
#include "qemu/help_option.h"
@@ -51,14 +51,12 @@ int graphic_depth = 32;
#define QEMU_ARCH QEMU_ARCH_ARM
#elif defined(TARGET_CRIS)
#define QEMU_ARCH QEMU_ARCH_CRIS
#elif defined(TARGET_HPPA)
#define QEMU_ARCH QEMU_ARCH_HPPA
#elif defined(TARGET_I386)
#define QEMU_ARCH QEMU_ARCH_I386
#elif defined(TARGET_LM32)
#define QEMU_ARCH QEMU_ARCH_LM32
#elif defined(TARGET_M68K)
#define QEMU_ARCH QEMU_ARCH_M68K
#elif defined(TARGET_LM32)
#define QEMU_ARCH QEMU_ARCH_LM32
#elif defined(TARGET_MICROBLAZE)
#define QEMU_ARCH QEMU_ARCH_MICROBLAZE
#elif defined(TARGET_MIPS)
@@ -71,20 +69,18 @@ int graphic_depth = 32;
#define QEMU_ARCH QEMU_ARCH_OPENRISC
#elif defined(TARGET_PPC)
#define QEMU_ARCH QEMU_ARCH_PPC
#elif defined(TARGET_RISCV)
#define QEMU_ARCH QEMU_ARCH_RISCV
#elif defined(TARGET_S390X)
#define QEMU_ARCH QEMU_ARCH_S390X
#elif defined(TARGET_SH4)
#define QEMU_ARCH QEMU_ARCH_SH4
#elif defined(TARGET_SPARC)
#define QEMU_ARCH QEMU_ARCH_SPARC
#elif defined(TARGET_TRICORE)
#define QEMU_ARCH QEMU_ARCH_TRICORE
#elif defined(TARGET_UNICORE32)
#define QEMU_ARCH QEMU_ARCH_UNICORE32
#elif defined(TARGET_XTENSA)
#define QEMU_ARCH QEMU_ARCH_XTENSA
#elif defined(TARGET_UNICORE32)
#define QEMU_ARCH QEMU_ARCH_UNICORE32
#elif defined(TARGET_TRICORE)
#define QEMU_ARCH QEMU_ARCH_TRICORE
#endif
const uint32_t arch_type = QEMU_ARCH;
@@ -112,8 +108,7 @@ TargetInfo *qmp_query_target(Error **errp)
{
TargetInfo *info = g_malloc0(sizeof(*info));
info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1,
&error_abort);
info->arch = g_strdup(TARGET_NAME);
return info;
}
+215
View File
@@ -0,0 +1,215 @@
/*
* Atomic helper templates
* Included from tcg-runtime.c and cputlb.c.
*
* Copyright (c) 2016 Red Hat, Inc
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#if DATA_SIZE == 16
# define SUFFIX o
# define DATA_TYPE Int128
# define BSWAP bswap128
#elif DATA_SIZE == 8
# define SUFFIX q
# define DATA_TYPE uint64_t
# define BSWAP bswap64
#elif DATA_SIZE == 4
# define SUFFIX l
# define DATA_TYPE uint32_t
# define BSWAP bswap32
#elif DATA_SIZE == 2
# define SUFFIX w
# define DATA_TYPE uint16_t
# define BSWAP bswap16
#elif DATA_SIZE == 1
# define SUFFIX b
# define DATA_TYPE uint8_t
# define BSWAP
#else
# error unsupported data size
#endif
#if DATA_SIZE >= 4
# define ABI_TYPE DATA_TYPE
#else
# define ABI_TYPE uint32_t
#endif
/* Define host-endian atomic operations. Note that END is used within
the ATOMIC_NAME macro, and redefined below. */
#if DATA_SIZE == 1
# define END
#elif defined(HOST_WORDS_BIGENDIAN)
# define END _be
#else
# define END _le
#endif
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
ABI_TYPE cmpv, ABI_TYPE newv EXTRA_ARGS)
{
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
return atomic_cmpxchg__nocheck(haddr, cmpv, newv);
}
#if DATA_SIZE >= 16
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
{
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
__atomic_load(haddr, &val, __ATOMIC_RELAXED);
return val;
}
void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
ABI_TYPE val EXTRA_ARGS)
{
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
__atomic_store(haddr, &val, __ATOMIC_RELAXED);
}
#else
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
ABI_TYPE val EXTRA_ARGS)
{
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
return atomic_xchg__nocheck(haddr, val);
}
#define GEN_ATOMIC_HELPER(X) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE val EXTRA_ARGS) \
{ \
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
return atomic_##X(haddr, val); \
} \
GEN_ATOMIC_HELPER(fetch_add)
GEN_ATOMIC_HELPER(fetch_and)
GEN_ATOMIC_HELPER(fetch_or)
GEN_ATOMIC_HELPER(fetch_xor)
GEN_ATOMIC_HELPER(add_fetch)
GEN_ATOMIC_HELPER(and_fetch)
GEN_ATOMIC_HELPER(or_fetch)
GEN_ATOMIC_HELPER(xor_fetch)
#undef GEN_ATOMIC_HELPER
#endif /* DATA SIZE >= 16 */
#undef END
#if DATA_SIZE > 1
/* Define reverse-host-endian atomic operations. Note that END is used
within the ATOMIC_NAME macro. */
#ifdef HOST_WORDS_BIGENDIAN
# define END _le
#else
# define END _be
#endif
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
ABI_TYPE cmpv, ABI_TYPE newv EXTRA_ARGS)
{
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
return BSWAP(atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)));
}
#if DATA_SIZE >= 16
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
{
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
__atomic_load(haddr, &val, __ATOMIC_RELAXED);
return BSWAP(val);
}
void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
ABI_TYPE val EXTRA_ARGS)
{
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
val = BSWAP(val);
__atomic_store(haddr, &val, __ATOMIC_RELAXED);
}
#else
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
ABI_TYPE val EXTRA_ARGS)
{
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
return BSWAP(atomic_xchg__nocheck(haddr, BSWAP(val)));
}
#define GEN_ATOMIC_HELPER(X) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE val EXTRA_ARGS) \
{ \
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
return BSWAP(atomic_##X(haddr, BSWAP(val))); \
}
GEN_ATOMIC_HELPER(fetch_and)
GEN_ATOMIC_HELPER(fetch_or)
GEN_ATOMIC_HELPER(fetch_xor)
GEN_ATOMIC_HELPER(and_fetch)
GEN_ATOMIC_HELPER(or_fetch)
GEN_ATOMIC_HELPER(xor_fetch)
#undef GEN_ATOMIC_HELPER
/* Note that for addition, we need to use a separate cmpxchg loop instead
of bswaps for the reverse-host-endian helpers. */
ABI_TYPE ATOMIC_NAME(fetch_add)(CPUArchState *env, target_ulong addr,
ABI_TYPE val EXTRA_ARGS)
{
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ldo, ldn, ret, sto;
ldo = atomic_read__nocheck(haddr);
while (1) {
ret = BSWAP(ldo);
sto = BSWAP(ret + val);
ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto);
if (ldn == ldo) {
return ret;
}
ldo = ldn;
}
}
ABI_TYPE ATOMIC_NAME(add_fetch)(CPUArchState *env, target_ulong addr,
ABI_TYPE val EXTRA_ARGS)
{
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ldo, ldn, ret, sto;
ldo = atomic_read__nocheck(haddr);
while (1) {
ret = BSWAP(ldo) + val;
sto = BSWAP(ret);
ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto);
if (ldn == ldo) {
return ret;
}
ldo = ldn;
}
}
#endif /* DATA_SIZE >= 16 */
#undef END
#endif /* DATA_SIZE > 1 */
#undef BSWAP
#undef ABI_TYPE
#undef DATA_TYPE
#undef SUFFIX
#undef DATA_SIZE
+8 -26
View File
@@ -1,31 +1,13 @@
common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o
common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
common-obj-$(CONFIG_SDL) += sdlaudio.o
common-obj-$(CONFIG_OSS) += ossaudio.o
common-obj-$(CONFIG_SPICE) += spiceaudio.o
common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o
common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o
common-obj-$(CONFIG_COREAUDIO) += coreaudio.o
common-obj-$(CONFIG_ALSA) += alsaaudio.o
common-obj-$(CONFIG_DSOUND) += dsoundaudio.o
common-obj-$(CONFIG_PA) += paaudio.o
common-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o
common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
common-obj-y += wavcapture.o
coreaudio.o-libs := $(COREAUDIO_LIBS)
dsoundaudio.o-libs := $(DSOUND_LIBS)
# alsa module
common-obj-$(CONFIG_AUDIO_ALSA) += alsa.mo
alsa.mo-objs = alsaaudio.o
alsa.mo-libs := $(ALSA_LIBS)
# oss module
common-obj-$(CONFIG_AUDIO_OSS) += oss.mo
oss.mo-objs = ossaudio.o
oss.mo-libs := $(OSS_LIBS)
# pulseaudio module
common-obj-$(CONFIG_AUDIO_PA) += pa.mo
pa.mo-objs = paaudio.o
pa.mo-libs := $(PULSE_LIBS)
# sdl module
common-obj-$(CONFIG_AUDIO_SDL) += sdl.mo
sdl.mo-objs = sdlaudio.o
sdl.mo-cflags := $(SDL_CFLAGS)
sdl.mo-libs := $(SDL_LIBS)
sdlaudio.o-cflags := $(SDL_CFLAGS)
+255 -130
View File
@@ -21,22 +21,42 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include <alsa/asoundlib.h>
#include "qemu-common.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "audio.h"
#include "trace.h"
#if QEMU_GNUC_PREREQ(4, 3)
#pragma GCC diagnostic ignored "-Waddress"
#endif
#define AUDIO_CAP "alsa"
#include "audio_int.h"
typedef struct ALSAConf {
int size_in_usec_in;
int size_in_usec_out;
const char *pcm_name_in;
const char *pcm_name_out;
unsigned int buffer_size_in;
unsigned int period_size_in;
unsigned int buffer_size_out;
unsigned int period_size_out;
unsigned int threshold;
int buffer_size_in_overridden;
int period_size_in_overridden;
int buffer_size_out_overridden;
int period_size_out_overridden;
} ALSAConf;
struct pollhlp {
snd_pcm_t *handle;
struct pollfd *pfds;
ALSAConf *conf;
int count;
int mask;
};
@@ -48,7 +68,6 @@ typedef struct ALSAVoiceOut {
void *pcm_buf;
snd_pcm_t *handle;
struct pollhlp pollhlp;
Audiodev *dev;
} ALSAVoiceOut;
typedef struct ALSAVoiceIn {
@@ -56,18 +75,21 @@ typedef struct ALSAVoiceIn {
snd_pcm_t *handle;
void *pcm_buf;
struct pollhlp pollhlp;
Audiodev *dev;
} ALSAVoiceIn;
struct alsa_params_req {
int freq;
snd_pcm_format_t fmt;
int nchannels;
int size_in_usec;
int override_mask;
unsigned int buffer_size;
unsigned int period_size;
};
struct alsa_params_obt {
int freq;
AudioFormat fmt;
audfmt_e fmt;
int endianness;
int nchannels;
snd_pcm_uframes_t samples;
@@ -274,16 +296,16 @@ static int alsa_write (SWVoiceOut *sw, void *buf, int len)
return audio_pcm_sw_write (sw, buf, len);
}
static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
{
switch (fmt) {
case AUDIO_FORMAT_S8:
case AUD_FMT_S8:
return SND_PCM_FORMAT_S8;
case AUDIO_FORMAT_U8:
case AUD_FMT_U8:
return SND_PCM_FORMAT_U8;
case AUDIO_FORMAT_S16:
case AUD_FMT_S16:
if (endianness) {
return SND_PCM_FORMAT_S16_BE;
}
@@ -291,7 +313,7 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
return SND_PCM_FORMAT_S16_LE;
}
case AUDIO_FORMAT_U16:
case AUD_FMT_U16:
if (endianness) {
return SND_PCM_FORMAT_U16_BE;
}
@@ -299,7 +321,7 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
return SND_PCM_FORMAT_U16_LE;
}
case AUDIO_FORMAT_S32:
case AUD_FMT_S32:
if (endianness) {
return SND_PCM_FORMAT_S32_BE;
}
@@ -307,7 +329,7 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
return SND_PCM_FORMAT_S32_LE;
}
case AUDIO_FORMAT_U32:
case AUD_FMT_U32:
if (endianness) {
return SND_PCM_FORMAT_U32_BE;
}
@@ -324,58 +346,58 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
}
}
static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt,
static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt,
int *endianness)
{
switch (alsafmt) {
case SND_PCM_FORMAT_S8:
*endianness = 0;
*fmt = AUDIO_FORMAT_S8;
*fmt = AUD_FMT_S8;
break;
case SND_PCM_FORMAT_U8:
*endianness = 0;
*fmt = AUDIO_FORMAT_U8;
*fmt = AUD_FMT_U8;
break;
case SND_PCM_FORMAT_S16_LE:
*endianness = 0;
*fmt = AUDIO_FORMAT_S16;
*fmt = AUD_FMT_S16;
break;
case SND_PCM_FORMAT_U16_LE:
*endianness = 0;
*fmt = AUDIO_FORMAT_U16;
*fmt = AUD_FMT_U16;
break;
case SND_PCM_FORMAT_S16_BE:
*endianness = 1;
*fmt = AUDIO_FORMAT_S16;
*fmt = AUD_FMT_S16;
break;
case SND_PCM_FORMAT_U16_BE:
*endianness = 1;
*fmt = AUDIO_FORMAT_U16;
*fmt = AUD_FMT_U16;
break;
case SND_PCM_FORMAT_S32_LE:
*endianness = 0;
*fmt = AUDIO_FORMAT_S32;
*fmt = AUD_FMT_S32;
break;
case SND_PCM_FORMAT_U32_LE:
*endianness = 0;
*fmt = AUDIO_FORMAT_U32;
*fmt = AUD_FMT_U32;
break;
case SND_PCM_FORMAT_S32_BE:
*endianness = 1;
*fmt = AUDIO_FORMAT_S32;
*fmt = AUD_FMT_S32;
break;
case SND_PCM_FORMAT_U32_BE:
*endianness = 1;
*fmt = AUDIO_FORMAT_U32;
*fmt = AUD_FMT_U32;
break;
default:
@@ -388,18 +410,17 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt,
static void alsa_dump_info (struct alsa_params_req *req,
struct alsa_params_obt *obt,
snd_pcm_format_t obtfmt,
AudiodevAlsaPerDirectionOptions *apdo)
snd_pcm_format_t obtfmt)
{
dolog("parameter | requested value | obtained value\n");
dolog("format | %10d | %10d\n", req->fmt, obtfmt);
dolog("channels | %10d | %10d\n",
req->nchannels, obt->nchannels);
dolog("frequency | %10d | %10d\n", req->freq, obt->freq);
dolog("============================================\n");
dolog("requested: buffer len %" PRId32 " period len %" PRId32 "\n",
apdo->buffer_length, apdo->period_length);
dolog("obtained: samples %ld\n", obt->samples);
dolog ("parameter | requested value | obtained value\n");
dolog ("format | %10d | %10d\n", req->fmt, obtfmt);
dolog ("channels | %10d | %10d\n",
req->nchannels, obt->nchannels);
dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
dolog ("============================================\n");
dolog ("requested: buffer size %d period size %d\n",
req->buffer_size, req->period_size);
dolog ("obtained: samples %ld\n", obt->samples);
}
static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
@@ -432,23 +453,23 @@ static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
}
}
static int alsa_open(bool in, struct alsa_params_req *req,
struct alsa_params_obt *obt, snd_pcm_t **handlep,
Audiodev *dev)
static int alsa_open (int in, struct alsa_params_req *req,
struct alsa_params_obt *obt, snd_pcm_t **handlep,
ALSAConf *conf)
{
AudiodevAlsaOptions *aopts = &dev->u.alsa;
AudiodevAlsaPerDirectionOptions *apdo = in ? aopts->in : aopts->out;
snd_pcm_t *handle;
snd_pcm_hw_params_t *hw_params;
int err;
int size_in_usec;
unsigned int freq, nchannels;
const char *pcm_name = apdo->has_dev ? apdo->dev : "default";
const char *pcm_name = in ? conf->pcm_name_in : conf->pcm_name_out;
snd_pcm_uframes_t obt_buffer_size;
const char *typ = in ? "ADC" : "DAC";
snd_pcm_format_t obtfmt;
freq = req->freq;
nchannels = req->nchannels;
size_in_usec = req->size_in_usec;
snd_pcm_hw_params_alloca (&hw_params);
@@ -508,42 +529,79 @@ static int alsa_open(bool in, struct alsa_params_req *req,
goto err;
}
if (apdo->buffer_length) {
int dir = 0;
unsigned int btime = apdo->buffer_length;
if (req->buffer_size) {
unsigned long obt;
err = snd_pcm_hw_params_set_buffer_time_near(
handle, hw_params, &btime, &dir);
if (size_in_usec) {
int dir = 0;
unsigned int btime = req->buffer_size;
err = snd_pcm_hw_params_set_buffer_time_near (
handle,
hw_params,
&btime,
&dir
);
obt = btime;
}
else {
snd_pcm_uframes_t bsize = req->buffer_size;
err = snd_pcm_hw_params_set_buffer_size_near (
handle,
hw_params,
&bsize
);
obt = bsize;
}
if (err < 0) {
alsa_logerr2(err, typ, "Failed to set buffer time to %" PRId32 "\n",
apdo->buffer_length);
alsa_logerr2 (err, typ, "Failed to set buffer %s to %d\n",
size_in_usec ? "time" : "size", req->buffer_size);
goto err;
}
if (apdo->has_buffer_length && btime != apdo->buffer_length) {
dolog("Requested buffer time %" PRId32
" was rejected, using %u\n", apdo->buffer_length, btime);
}
if ((req->override_mask & 2) && (obt - req->buffer_size))
dolog ("Requested buffer %s %u was rejected, using %lu\n",
size_in_usec ? "time" : "size", req->buffer_size, obt);
}
if (apdo->period_length) {
int dir = 0;
unsigned int ptime = apdo->period_length;
if (req->period_size) {
unsigned long obt;
err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &ptime,
&dir);
if (size_in_usec) {
int dir = 0;
unsigned int ptime = req->period_size;
err = snd_pcm_hw_params_set_period_time_near (
handle,
hw_params,
&ptime,
&dir
);
obt = ptime;
}
else {
int dir = 0;
snd_pcm_uframes_t psize = req->period_size;
err = snd_pcm_hw_params_set_period_size_near (
handle,
hw_params,
&psize,
&dir
);
obt = psize;
}
if (err < 0) {
alsa_logerr2(err, typ, "Failed to set period time to %" PRId32 "\n",
apdo->period_length);
alsa_logerr2 (err, typ, "Failed to set period %s to %d\n",
size_in_usec ? "time" : "size", req->period_size);
goto err;
}
if (apdo->has_period_length && ptime != apdo->period_length) {
dolog("Requested period time %" PRId32 " was rejected, using %d\n",
apdo->period_length, ptime);
}
if (((req->override_mask & 1) && (obt - req->period_size)))
dolog ("Requested period %s %u was rejected, using %lu\n",
size_in_usec ? "time" : "size", req->period_size, obt);
}
err = snd_pcm_hw_params (handle, hw_params);
@@ -575,12 +633,30 @@ static int alsa_open(bool in, struct alsa_params_req *req,
goto err;
}
if (!in && aopts->has_threshold && aopts->threshold) {
struct audsettings as = { .freq = freq };
alsa_set_threshold(
handle,
audio_buffer_frames(qapi_AudiodevAlsaPerDirectionOptions_base(apdo),
&as, aopts->threshold));
if (!in && conf->threshold) {
snd_pcm_uframes_t threshold;
int bytes_per_sec;
bytes_per_sec = freq << (nchannels == 2);
switch (obt->fmt) {
case AUD_FMT_S8:
case AUD_FMT_U8:
break;
case AUD_FMT_S16:
case AUD_FMT_U16:
bytes_per_sec <<= 1;
break;
case AUD_FMT_S32:
case AUD_FMT_U32:
bytes_per_sec <<= 2;
break;
}
threshold = (conf->threshold * bytes_per_sec) / 1000;
alsa_set_threshold (handle, threshold);
}
obt->nchannels = nchannels;
@@ -593,11 +669,11 @@ static int alsa_open(bool in, struct alsa_params_req *req,
obt->nchannels != req->nchannels ||
obt->freq != req->freq) {
dolog ("Audio parameters for %s\n", typ);
alsa_dump_info(req, obt, obtfmt, apdo);
alsa_dump_info (req, obt, obtfmt);
}
#ifdef DEBUG
alsa_dump_info(req, obt, obtfmt, pdo);
alsa_dump_info (req, obt, obtfmt);
#endif
return 0;
@@ -723,13 +799,19 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
struct alsa_params_obt obt;
snd_pcm_t *handle;
struct audsettings obt_as;
Audiodev *dev = drv_opaque;
ALSAConf *conf = drv_opaque;
req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
req.freq = as->freq;
req.nchannels = as->nchannels;
req.period_size = conf->period_size_out;
req.buffer_size = conf->buffer_size_out;
req.size_in_usec = conf->size_in_usec_out;
req.override_mask =
(conf->period_size_out_overridden ? 1 : 0) |
(conf->buffer_size_out_overridden ? 2 : 0);
if (alsa_open(0, &req, &obt, &handle, dev)) {
if (alsa_open (0, &req, &obt, &handle, conf)) {
return -1;
}
@@ -741,7 +823,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = obt.samples;
alsa->pcm_buf = audio_calloc(__func__, obt.samples, 1 << hw->info.shift);
alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift);
if (!alsa->pcm_buf) {
dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n",
hw->samples, 1 << hw->info.shift);
@@ -750,7 +832,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
}
alsa->handle = handle;
alsa->dev = dev;
alsa->pollhlp.conf = conf;
return 0;
}
@@ -790,12 +872,16 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.out;
switch (cmd) {
case VOICE_ENABLE:
{
bool poll_mode = apdo->try_poll;
va_list ap;
int poll_mode;
va_start (ap, cmd);
poll_mode = va_arg (ap, int);
va_end (ap);
ldebug ("enabling voice\n");
if (poll_mode && alsa_poll_out (hw)) {
@@ -824,13 +910,19 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
struct alsa_params_obt obt;
snd_pcm_t *handle;
struct audsettings obt_as;
Audiodev *dev = drv_opaque;
ALSAConf *conf = drv_opaque;
req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
req.freq = as->freq;
req.nchannels = as->nchannels;
req.period_size = conf->period_size_in;
req.buffer_size = conf->buffer_size_in;
req.size_in_usec = conf->size_in_usec_in;
req.override_mask =
(conf->period_size_in_overridden ? 1 : 0) |
(conf->buffer_size_in_overridden ? 2 : 0);
if (alsa_open(1, &req, &obt, &handle, dev)) {
if (alsa_open (1, &req, &obt, &handle, conf)) {
return -1;
}
@@ -842,7 +934,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = obt.samples;
alsa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
if (!alsa->pcm_buf) {
dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
hw->samples, 1 << hw->info.shift);
@@ -851,7 +943,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
}
alsa->handle = handle;
alsa->dev = dev;
alsa->pollhlp.conf = conf;
return 0;
}
@@ -993,12 +1085,16 @@ static int alsa_read (SWVoiceIn *sw, void *buf, int size)
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.in;
switch (cmd) {
case VOICE_ENABLE:
{
bool poll_mode = apdo->try_poll;
va_list ap;
int poll_mode;
va_start (ap, cmd);
poll_mode = va_arg (ap, int);
va_end (ap);
ldebug ("enabling voice\n");
if (poll_mode && alsa_poll_in (hw)) {
@@ -1021,54 +1117,88 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
return -1;
}
static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo)
static ALSAConf glob_conf = {
.buffer_size_out = 4096,
.period_size_out = 1024,
.pcm_name_out = "default",
.pcm_name_in = "default",
};
static void *alsa_audio_init (void)
{
if (!apdo->has_try_poll) {
apdo->try_poll = true;
apdo->has_try_poll = true;
}
}
static void *alsa_audio_init(Audiodev *dev)
{
AudiodevAlsaOptions *aopts;
assert(dev->driver == AUDIODEV_DRIVER_ALSA);
aopts = &dev->u.alsa;
alsa_init_per_direction(aopts->in);
alsa_init_per_direction(aopts->out);
/*
* need to define them, as otherwise alsa produces no sound
* doesn't set has_* so alsa_open can identify it wasn't set by the user
*/
if (!dev->u.alsa.out->has_period_length) {
/* 1024 frames assuming 44100Hz */
dev->u.alsa.out->period_length = 1024 * 1000000 / 44100;
}
if (!dev->u.alsa.out->has_buffer_length) {
/* 4096 frames assuming 44100Hz */
dev->u.alsa.out->buffer_length = 4096ll * 1000000 / 44100;
}
/*
* OptsVisitor sets unspecified optional fields to zero, but do not depend
* on it...
*/
if (!dev->u.alsa.in->has_period_length) {
dev->u.alsa.in->period_length = 0;
}
if (!dev->u.alsa.in->has_buffer_length) {
dev->u.alsa.in->buffer_length = 0;
}
return dev;
ALSAConf *conf = g_malloc(sizeof(ALSAConf));
*conf = glob_conf;
return conf;
}
static void alsa_audio_fini (void *opaque)
{
g_free(opaque);
}
static struct audio_option alsa_options[] = {
{
.name = "DAC_SIZE_IN_USEC",
.tag = AUD_OPT_BOOL,
.valp = &glob_conf.size_in_usec_out,
.descr = "DAC period/buffer size in microseconds (otherwise in frames)"
},
{
.name = "DAC_PERIOD_SIZE",
.tag = AUD_OPT_INT,
.valp = &glob_conf.period_size_out,
.descr = "DAC period size (0 to go with system default)",
.overriddenp = &glob_conf.period_size_out_overridden
},
{
.name = "DAC_BUFFER_SIZE",
.tag = AUD_OPT_INT,
.valp = &glob_conf.buffer_size_out,
.descr = "DAC buffer size (0 to go with system default)",
.overriddenp = &glob_conf.buffer_size_out_overridden
},
{
.name = "ADC_SIZE_IN_USEC",
.tag = AUD_OPT_BOOL,
.valp = &glob_conf.size_in_usec_in,
.descr =
"ADC period/buffer size in microseconds (otherwise in frames)"
},
{
.name = "ADC_PERIOD_SIZE",
.tag = AUD_OPT_INT,
.valp = &glob_conf.period_size_in,
.descr = "ADC period size (0 to go with system default)",
.overriddenp = &glob_conf.period_size_in_overridden
},
{
.name = "ADC_BUFFER_SIZE",
.tag = AUD_OPT_INT,
.valp = &glob_conf.buffer_size_in,
.descr = "ADC buffer size (0 to go with system default)",
.overriddenp = &glob_conf.buffer_size_in_overridden
},
{
.name = "THRESHOLD",
.tag = AUD_OPT_INT,
.valp = &glob_conf.threshold,
.descr = "(undocumented)"
},
{
.name = "DAC_DEV",
.tag = AUD_OPT_STR,
.valp = &glob_conf.pcm_name_out,
.descr = "DAC device name (for instance dmix)"
},
{
.name = "ADC_DEV",
.tag = AUD_OPT_STR,
.valp = &glob_conf.pcm_name_in,
.descr = "ADC device name"
},
{ /* End of list */ }
};
static struct audio_pcm_ops alsa_pcm_ops = {
.init_out = alsa_init_out,
.fini_out = alsa_fini_out,
@@ -1083,9 +1213,10 @@ static struct audio_pcm_ops alsa_pcm_ops = {
.ctl_in = alsa_ctl_in,
};
static struct audio_driver alsa_audio_driver = {
struct audio_driver alsa_audio_driver = {
.name = "alsa",
.descr = "ALSA http://www.alsa-project.org",
.options = alsa_options,
.init = alsa_audio_init,
.fini = alsa_audio_fini,
.pcm_ops = &alsa_pcm_ops,
@@ -1095,9 +1226,3 @@ static struct audio_driver alsa_audio_driver = {
.voice_size_out = sizeof (ALSAVoiceOut),
.voice_size_in = sizeof (ALSAVoiceIn)
};
static void register_audio_alsa(void)
{
audio_driver_register(&alsa_audio_driver);
}
type_init(register_audio_alsa);
+633 -377
View File
File diff suppressed because it is too large. Load diff
+13 -17
View File
@@ -26,31 +26,30 @@
#define QEMU_AUDIO_H
#include "qemu/queue.h"
#include "qapi/qapi-types-audio.h"
typedef void (*audio_callback_fn) (void *opaque, int avail);
typedef enum {
AUD_FMT_U8,
AUD_FMT_S8,
AUD_FMT_U16,
AUD_FMT_S16,
AUD_FMT_U32,
AUD_FMT_S32
} audfmt_e;
#ifdef HOST_WORDS_BIGENDIAN
#define AUDIO_HOST_ENDIANNESS 1
#else
#define AUDIO_HOST_ENDIANNESS 0
#endif
typedef struct audsettings {
struct audsettings {
int freq;
int nchannels;
AudioFormat fmt;
audfmt_e fmt;
int endianness;
} audsettings;
audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo);
int audioformat_bytes_per_sample(AudioFormat fmt);
int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
audsettings *as, int def_usecs);
};
typedef enum {
AUD_CNOTIFY_ENABLE,
@@ -90,6 +89,7 @@ typedef struct QEMUAudioTimeStamp {
void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0);
void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
void AUD_help (void);
void AUD_register_card (const char *name, QEMUSoundCard *card);
void AUD_remove_card (QEMUSoundCard *card);
CaptureVoiceOut *AUD_add_capture (
@@ -171,8 +171,4 @@ void audio_sample_to_uint64(void *samples, int pos,
void audio_sample_from_uint64(void *samples, int pos,
uint64_t left, uint64_t right);
void audio_parse_option(const char *opt);
void audio_init_audiodevs(void);
void audio_legacy_help(void);
#endif /* QEMU_AUDIO_H */
+35 -26
View File
@@ -25,7 +25,7 @@
#ifndef QEMU_AUDIO_INT_H
#define QEMU_AUDIO_INT_H
#ifdef CONFIG_AUDIO_COREAUDIO
#ifdef CONFIG_COREAUDIO
#define FLOAT_MIXENG
/* #define RECIPROCAL */
#endif
@@ -33,6 +33,22 @@
struct audio_pcm_ops;
typedef enum {
AUD_OPT_INT,
AUD_OPT_FMT,
AUD_OPT_STR,
AUD_OPT_BOOL
} audio_option_tag_e;
struct audio_option {
const char *name;
audio_option_tag_e tag;
void *valp;
const char *descr;
int *overriddenp;
int overridden;
};
struct audio_callback {
void *opaque;
audio_callback_fn fn;
@@ -125,11 +141,11 @@ struct SWVoiceIn {
QLIST_ENTRY (SWVoiceIn) entries;
};
typedef struct audio_driver audio_driver;
struct audio_driver {
const char *name;
const char *descr;
void *(*init) (Audiodev *);
struct audio_option *options;
void *(*init) (void);
void (*fini) (void *);
struct audio_pcm_ops *pcm_ops;
int can_be_default;
@@ -138,7 +154,6 @@ struct audio_driver {
int voice_size_out;
int voice_size_in;
int ctl_caps;
QLIST_ENTRY(audio_driver) next;
};
struct audio_pcm_ops {
@@ -174,9 +189,8 @@ struct SWVoiceCap {
QLIST_ENTRY (SWVoiceCap) entries;
};
typedef struct AudioState {
struct AudioState {
struct audio_driver *drv;
Audiodev *dev;
void *drv_opaque;
QEMUTimer *ts;
@@ -187,16 +201,19 @@ typedef struct AudioState {
int nb_hw_voices_out;
int nb_hw_voices_in;
int vm_running;
int64_t period_ticks;
} AudioState;
};
extern struct audio_driver no_audio_driver;
extern struct audio_driver oss_audio_driver;
extern struct audio_driver sdl_audio_driver;
extern struct audio_driver wav_audio_driver;
extern struct audio_driver alsa_audio_driver;
extern struct audio_driver coreaudio_audio_driver;
extern struct audio_driver dsound_audio_driver;
extern struct audio_driver pa_audio_driver;
extern struct audio_driver spice_audio_driver;
extern const struct mixeng_volume nominal_volume;
extern const char *audio_prio_list[];
void audio_driver_register(audio_driver *drv);
audio_driver *audio_driver_lookup(const char *name);
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
@@ -235,18 +252,10 @@ static inline int audio_ring_dist (int dst, int src, int len)
#define AUDIO_STRINGIFY_(n) #n
#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n)
typedef struct AudiodevListEntry {
Audiodev *dev;
QSIMPLEQ_ENTRY(AudiodevListEntry) next;
} AudiodevListEntry;
typedef QSIMPLEQ_HEAD(, AudiodevListEntry) AudiodevListHead;
AudiodevListHead audio_handle_legacy_opts(void);
void audio_free_audiodev_list(AudiodevListHead *head);
void audio_create_pdos(Audiodev *dev);
AudiodevPerDirectionOptions *audio_get_pdo_in(Audiodev *dev);
AudiodevPerDirectionOptions *audio_get_pdo_out(Audiodev *dev);
#if defined _MSC_VER || defined __GNUC__
#define AUDIO_FUNC __FUNCTION__
#else
#define AUDIO_FUNC __FILE__ ":" AUDIO_STRINGIFY (__LINE__)
#endif
#endif /* QEMU_AUDIO_INT_H */
-549
View File
@@ -1,549 +0,0 @@
/*
* QEMU Audio subsystem: legacy configuration handling
*
* Copyright (c) 2015-2019 Zoltán Kővágó <DirtY.iCE.hu@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "audio.h"
#include "audio_int.h"
#include "qemu/cutils.h"
#include "qemu/timer.h"
#include "qapi/error.h"
#include "qapi/qapi-visit-audio.h"
#include "qapi/visitor-impl.h"
#define AUDIO_CAP "audio-legacy"
#include "audio_int.h"
static uint32_t toui32(const char *str)
{
unsigned long long ret;
if (parse_uint_full(str, &ret, 10) || ret > UINT32_MAX) {
dolog("Invalid integer value `%s'\n", str);
exit(1);
}
return ret;
}
/* helper functions to convert env variables */
static void get_bool(const char *env, bool *dst, bool *has_dst)
{
const char *val = getenv(env);
if (val) {
*dst = toui32(val) != 0;
*has_dst = true;
}
}
static void get_int(const char *env, uint32_t *dst, bool *has_dst)
{
const char *val = getenv(env);
if (val) {
*dst = toui32(val);
*has_dst = true;
}
}
static void get_str(const char *env, char **dst, bool *has_dst)
{
const char *val = getenv(env);
if (val) {
if (*has_dst) {
g_free(*dst);
}
*dst = g_strdup(val);
*has_dst = true;
}
}
static void get_fmt(const char *env, AudioFormat *dst, bool *has_dst)
{
const char *val = getenv(env);
if (val) {
size_t i;
for (i = 0; AudioFormat_lookup.size; ++i) {
if (strcasecmp(val, AudioFormat_lookup.array[i]) == 0) {
*dst = i;
*has_dst = true;
return;
}
}
dolog("Invalid audio format `%s'\n", val);
exit(1);
}
}
static void get_millis_to_usecs(const char *env, uint32_t *dst, bool *has_dst)
{
const char *val = getenv(env);
if (val) {
*dst = toui32(val) * 1000;
*has_dst = true;
}
}
static uint32_t frames_to_usecs(uint32_t frames,
AudiodevPerDirectionOptions *pdo)
{
uint32_t freq = pdo->has_frequency ? pdo->frequency : 44100;
return (frames * 1000000 + freq / 2) / freq;
}
static void get_frames_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
AudiodevPerDirectionOptions *pdo)
{
const char *val = getenv(env);
if (val) {
*dst = frames_to_usecs(toui32(val), pdo);
*has_dst = true;
}
}
static uint32_t samples_to_usecs(uint32_t samples,
AudiodevPerDirectionOptions *pdo)
{
uint32_t channels = pdo->has_channels ? pdo->channels : 2;
return frames_to_usecs(samples / channels, pdo);
}
static void get_samples_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
AudiodevPerDirectionOptions *pdo)
{
const char *val = getenv(env);
if (val) {
*dst = samples_to_usecs(toui32(val), pdo);
*has_dst = true;
}
}
static uint32_t bytes_to_usecs(uint32_t bytes, AudiodevPerDirectionOptions *pdo)
{
AudioFormat fmt = pdo->has_format ? pdo->format : AUDIO_FORMAT_S16;
uint32_t bytes_per_sample = audioformat_bytes_per_sample(fmt);
return samples_to_usecs(bytes / bytes_per_sample, pdo);
}
static void get_bytes_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
AudiodevPerDirectionOptions *pdo)
{
const char *val = getenv(env);
if (val) {
*dst = bytes_to_usecs(toui32(val), pdo);
*has_dst = true;
}
}
/* backend specific functions */
/* ALSA */
static void handle_alsa_per_direction(
AudiodevAlsaPerDirectionOptions *apdo, const char *prefix)
{
char buf[64];
size_t len = strlen(prefix);
bool size_in_usecs = false;
bool dummy;
memcpy(buf, prefix, len);
strcpy(buf + len, "TRY_POLL");
get_bool(buf, &apdo->try_poll, &apdo->has_try_poll);
strcpy(buf + len, "DEV");
get_str(buf, &apdo->dev, &apdo->has_dev);
strcpy(buf + len, "SIZE_IN_USEC");
get_bool(buf, &size_in_usecs, &dummy);
strcpy(buf + len, "PERIOD_SIZE");
get_int(buf, &apdo->period_length, &apdo->has_period_length);
if (apdo->has_period_length && !size_in_usecs) {
apdo->period_length = frames_to_usecs(
apdo->period_length,
qapi_AudiodevAlsaPerDirectionOptions_base(apdo));
}
strcpy(buf + len, "BUFFER_SIZE");
get_int(buf, &apdo->buffer_length, &apdo->has_buffer_length);
if (apdo->has_buffer_length && !size_in_usecs) {
apdo->buffer_length = frames_to_usecs(
apdo->buffer_length,
qapi_AudiodevAlsaPerDirectionOptions_base(apdo));
}
}
static void handle_alsa(Audiodev *dev)
{
AudiodevAlsaOptions *aopt = &dev->u.alsa;
handle_alsa_per_direction(aopt->in, "QEMU_ALSA_ADC_");
handle_alsa_per_direction(aopt->out, "QEMU_ALSA_DAC_");
get_millis_to_usecs("QEMU_ALSA_THRESHOLD",
&aopt->threshold, &aopt->has_threshold);
}
/* coreaudio */
static void handle_coreaudio(Audiodev *dev)
{
get_frames_to_usecs(
"QEMU_COREAUDIO_BUFFER_SIZE",
&dev->u.coreaudio.out->buffer_length,
&dev->u.coreaudio.out->has_buffer_length,
qapi_AudiodevCoreaudioPerDirectionOptions_base(dev->u.coreaudio.out));
get_int("QEMU_COREAUDIO_BUFFER_COUNT",
&dev->u.coreaudio.out->buffer_count,
&dev->u.coreaudio.out->has_buffer_count);
}
/* dsound */
static void handle_dsound(Audiodev *dev)
{
get_millis_to_usecs("QEMU_DSOUND_LATENCY_MILLIS",
&dev->u.dsound.latency, &dev->u.dsound.has_latency);
get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_OUT",
&dev->u.dsound.out->buffer_length,
&dev->u.dsound.out->has_buffer_length,
dev->u.dsound.out);
get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_IN",
&dev->u.dsound.in->buffer_length,
&dev->u.dsound.in->has_buffer_length,
dev->u.dsound.in);
}
/* OSS */
static void handle_oss_per_direction(
AudiodevOssPerDirectionOptions *opdo, const char *try_poll_env,
const char *dev_env)
{
get_bool(try_poll_env, &opdo->try_poll, &opdo->has_try_poll);
get_str(dev_env, &opdo->dev, &opdo->has_dev);
get_bytes_to_usecs("QEMU_OSS_FRAGSIZE",
&opdo->buffer_length, &opdo->has_buffer_length,
qapi_AudiodevOssPerDirectionOptions_base(opdo));
get_int("QEMU_OSS_NFRAGS", &opdo->buffer_count,
&opdo->has_buffer_count);
}
static void handle_oss(Audiodev *dev)
{
AudiodevOssOptions *oopt = &dev->u.oss;
handle_oss_per_direction(oopt->in, "QEMU_AUDIO_ADC_TRY_POLL",
"QEMU_OSS_ADC_DEV");
handle_oss_per_direction(oopt->out, "QEMU_AUDIO_DAC_TRY_POLL",
"QEMU_OSS_DAC_DEV");
get_bool("QEMU_OSS_MMAP", &oopt->try_mmap, &oopt->has_try_mmap);
get_bool("QEMU_OSS_EXCLUSIVE", &oopt->exclusive, &oopt->has_exclusive);
get_int("QEMU_OSS_POLICY", &oopt->dsp_policy, &oopt->has_dsp_policy);
}
/* pulseaudio */
static void handle_pa_per_direction(
AudiodevPaPerDirectionOptions *ppdo, const char *env)
{
get_str(env, &ppdo->name, &ppdo->has_name);
}
static void handle_pa(Audiodev *dev)
{
handle_pa_per_direction(dev->u.pa.in, "QEMU_PA_SOURCE");
handle_pa_per_direction(dev->u.pa.out, "QEMU_PA_SINK");
get_samples_to_usecs(
"QEMU_PA_SAMPLES", &dev->u.pa.in->buffer_length,
&dev->u.pa.in->has_buffer_length,
qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.in));
get_samples_to_usecs(
"QEMU_PA_SAMPLES", &dev->u.pa.out->buffer_length,
&dev->u.pa.out->has_buffer_length,
qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.out));
get_str("QEMU_PA_SERVER", &dev->u.pa.server, &dev->u.pa.has_server);
}
/* SDL */
static void handle_sdl(Audiodev *dev)
{
/* SDL is output only */
get_samples_to_usecs("QEMU_SDL_SAMPLES", &dev->u.sdl.out->buffer_length,
&dev->u.sdl.out->has_buffer_length, dev->u.sdl.out);
}
/* wav */
static void handle_wav(Audiodev *dev)
{
get_int("QEMU_WAV_FREQUENCY",
&dev->u.wav.out->frequency, &dev->u.wav.out->has_frequency);
get_fmt("QEMU_WAV_FORMAT", &dev->u.wav.out->format,
&dev->u.wav.out->has_format);
get_int("QEMU_WAV_DAC_FIXED_CHANNELS",
&dev->u.wav.out->channels, &dev->u.wav.out->has_channels);
get_str("QEMU_WAV_PATH", &dev->u.wav.path, &dev->u.wav.has_path);
}
/* general */
static void handle_per_direction(
AudiodevPerDirectionOptions *pdo, const char *prefix)
{
char buf[64];
size_t len = strlen(prefix);
memcpy(buf, prefix, len);
strcpy(buf + len, "FIXED_SETTINGS");
get_bool(buf, &pdo->fixed_settings, &pdo->has_fixed_settings);
strcpy(buf + len, "FIXED_FREQ");
get_int(buf, &pdo->frequency, &pdo->has_frequency);
strcpy(buf + len, "FIXED_FMT");
get_fmt(buf, &pdo->format, &pdo->has_format);
strcpy(buf + len, "FIXED_CHANNELS");
get_int(buf, &pdo->channels, &pdo->has_channels);
strcpy(buf + len, "VOICES");
get_int(buf, &pdo->voices, &pdo->has_voices);
}
static AudiodevListEntry *legacy_opt(const char *drvname)
{
AudiodevListEntry *e = g_malloc0(sizeof(AudiodevListEntry));
e->dev = g_malloc0(sizeof(Audiodev));
e->dev->id = g_strdup(drvname);
e->dev->driver = qapi_enum_parse(
&AudiodevDriver_lookup, drvname, -1, &error_abort);
audio_create_pdos(e->dev);
handle_per_direction(audio_get_pdo_in(e->dev), "QEMU_AUDIO_ADC_");
handle_per_direction(audio_get_pdo_out(e->dev), "QEMU_AUDIO_DAC_");
/* Original description: Timer period in HZ (0 - use lowest possible) */
get_int("QEMU_AUDIO_TIMER_PERIOD",
&e->dev->timer_period, &e->dev->has_timer_period);
if (e->dev->has_timer_period && e->dev->timer_period) {
e->dev->timer_period = NANOSECONDS_PER_SECOND / 1000 /
e->dev->timer_period;
}
switch (e->dev->driver) {
case AUDIODEV_DRIVER_ALSA:
handle_alsa(e->dev);
break;
case AUDIODEV_DRIVER_COREAUDIO:
handle_coreaudio(e->dev);
break;
case AUDIODEV_DRIVER_DSOUND:
handle_dsound(e->dev);
break;
case AUDIODEV_DRIVER_OSS:
handle_oss(e->dev);
break;
case AUDIODEV_DRIVER_PA:
handle_pa(e->dev);
break;
case AUDIODEV_DRIVER_SDL:
handle_sdl(e->dev);
break;
case AUDIODEV_DRIVER_WAV:
handle_wav(e->dev);
break;
default:
break;
}
return e;
}
AudiodevListHead audio_handle_legacy_opts(void)
{
const char *drvname = getenv("QEMU_AUDIO_DRV");
AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head);
if (drvname) {
AudiodevListEntry *e;
audio_driver *driver = audio_driver_lookup(drvname);
if (!driver) {
dolog("Unknown audio driver `%s'\n", drvname);
exit(1);
}
e = legacy_opt(drvname);
QSIMPLEQ_INSERT_TAIL(&head, e, next);
} else {
for (int i = 0; audio_prio_list[i]; i++) {
audio_driver *driver = audio_driver_lookup(audio_prio_list[i]);
if (driver && driver->can_be_default) {
AudiodevListEntry *e = legacy_opt(driver->name);
QSIMPLEQ_INSERT_TAIL(&head, e, next);
}
}
if (QSIMPLEQ_EMPTY(&head)) {
dolog("Internal error: no default audio driver available\n");
exit(1);
}
}
return head;
}
/* visitor to print -audiodev option */
typedef struct {
Visitor visitor;
bool comma;
GList *path;
} LegacyPrintVisitor;
static void lv_start_struct(Visitor *v, const char *name, void **obj,
size_t size, Error **errp)
{
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
lv->path = g_list_append(lv->path, g_strdup(name));
}
static void lv_end_struct(Visitor *v, void **obj)
{
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
lv->path = g_list_delete_link(lv->path, g_list_last(lv->path));
}
static void lv_print_key(Visitor *v, const char *name)
{
GList *e;
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
if (lv->comma) {
putchar(',');
} else {
lv->comma = true;
}
for (e = lv->path; e; e = e->next) {
if (e->data) {
printf("%s.", (const char *) e->data);
}
}
printf("%s=", name);
}
static void lv_type_int64(Visitor *v, const char *name, int64_t *obj,
Error **errp)
{
lv_print_key(v, name);
printf("%" PRIi64, *obj);
}
static void lv_type_uint64(Visitor *v, const char *name, uint64_t *obj,
Error **errp)
{
lv_print_key(v, name);
printf("%" PRIu64, *obj);
}
static void lv_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
{
lv_print_key(v, name);
printf("%s", *obj ? "on" : "off");
}
static void lv_type_str(Visitor *v, const char *name, char **obj, Error **errp)
{
const char *str = *obj;
lv_print_key(v, name);
while (*str) {
if (*str == ',') {
putchar(',');
}
putchar(*str++);
}
}
static void lv_complete(Visitor *v, void *opaque)
{
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
assert(lv->path == NULL);
}
static void lv_free(Visitor *v)
{
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
g_list_free_full(lv->path, g_free);
g_free(lv);
}
static Visitor *legacy_visitor_new(void)
{
LegacyPrintVisitor *lv = g_malloc0(sizeof(LegacyPrintVisitor));
lv->visitor.start_struct = lv_start_struct;
lv->visitor.end_struct = lv_end_struct;
/* lists not supported */
lv->visitor.type_int64 = lv_type_int64;
lv->visitor.type_uint64 = lv_type_uint64;
lv->visitor.type_bool = lv_type_bool;
lv->visitor.type_str = lv_type_str;
lv->visitor.type = VISITOR_OUTPUT;
lv->visitor.complete = lv_complete;
lv->visitor.free = lv_free;
return &lv->visitor;
}
void audio_legacy_help(void)
{
AudiodevListHead head;
AudiodevListEntry *e;
printf("Environment variable based configuration deprecated.\n");
printf("Please use the new -audiodev option.\n");
head = audio_handle_legacy_opts();
printf("\nEquivalent -audiodev to your current environment variables:\n");
if (!getenv("QEMU_AUDIO_DRV")) {
printf("(Since you didn't specify QEMU_AUDIO_DRV, I'll list all "
"possibilities)\n");
}
QSIMPLEQ_FOREACH(e, &head, next) {
Visitor *v;
Audiodev *dev = e->dev;
printf("-audiodev ");
v = legacy_visitor_new();
visit_type_Audiodev(v, NULL, &dev, &error_abort);
visit_free(v);
printf("\n");
}
audio_free_audiodev_list(&head);
}
+15 -14
View File
@@ -1,4 +1,5 @@
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "audio.h"
#define AUDIO_CAP "audio-pt"
@@ -30,7 +31,7 @@ int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
err = sigfillset (&set);
if (err) {
logerr(p, errno, "%s(%s): sigfillset failed", cap, __func__);
logerr (p, errno, "%s(%s): sigfillset failed", cap, AUDIO_FUNC);
return -1;
}
@@ -56,8 +57,8 @@ int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
err2 = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
if (err2) {
logerr(p, err2, "%s(%s): pthread_sigmask (restore) failed",
cap, __func__);
logerr (p, err2, "%s(%s): pthread_sigmask (restore) failed",
cap, AUDIO_FUNC);
/* We have failed to restore original signal mask, all bets are off,
so terminate the process */
exit (EXIT_FAILURE);
@@ -73,17 +74,17 @@ int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
err2:
err2 = pthread_cond_destroy (&p->cond);
if (err2) {
logerr(p, err2, "%s(%s): pthread_cond_destroy failed", cap, __func__);
logerr (p, err2, "%s(%s): pthread_cond_destroy failed", cap, AUDIO_FUNC);
}
err1:
err2 = pthread_mutex_destroy (&p->mutex);
if (err2) {
logerr(p, err2, "%s(%s): pthread_mutex_destroy failed", cap, __func__);
logerr (p, err2, "%s(%s): pthread_mutex_destroy failed", cap, AUDIO_FUNC);
}
err0:
logerr(p, err, "%s(%s): %s failed", cap, __func__, efunc);
logerr (p, err, "%s(%s): %s failed", cap, AUDIO_FUNC, efunc);
return -1;
}
@@ -93,13 +94,13 @@ int audio_pt_fini (struct audio_pt *p, const char *cap)
err = pthread_cond_destroy (&p->cond);
if (err) {
logerr(p, err, "%s(%s): pthread_cond_destroy failed", cap, __func__);
logerr (p, err, "%s(%s): pthread_cond_destroy failed", cap, AUDIO_FUNC);
ret = -1;
}
err = pthread_mutex_destroy (&p->mutex);
if (err) {
logerr(p, err, "%s(%s): pthread_mutex_destroy failed", cap, __func__);
logerr (p, err, "%s(%s): pthread_mutex_destroy failed", cap, AUDIO_FUNC);
ret = -1;
}
return ret;
@@ -111,7 +112,7 @@ int audio_pt_lock (struct audio_pt *p, const char *cap)
err = pthread_mutex_lock (&p->mutex);
if (err) {
logerr(p, err, "%s(%s): pthread_mutex_lock failed", cap, __func__);
logerr (p, err, "%s(%s): pthread_mutex_lock failed", cap, AUDIO_FUNC);
return -1;
}
return 0;
@@ -123,7 +124,7 @@ int audio_pt_unlock (struct audio_pt *p, const char *cap)
err = pthread_mutex_unlock (&p->mutex);
if (err) {
logerr(p, err, "%s(%s): pthread_mutex_unlock failed", cap, __func__);
logerr (p, err, "%s(%s): pthread_mutex_unlock failed", cap, AUDIO_FUNC);
return -1;
}
return 0;
@@ -135,7 +136,7 @@ int audio_pt_wait (struct audio_pt *p, const char *cap)
err = pthread_cond_wait (&p->cond, &p->mutex);
if (err) {
logerr(p, err, "%s(%s): pthread_cond_wait failed", cap, __func__);
logerr (p, err, "%s(%s): pthread_cond_wait failed", cap, AUDIO_FUNC);
return -1;
}
return 0;
@@ -147,12 +148,12 @@ int audio_pt_unlock_and_signal (struct audio_pt *p, const char *cap)
err = pthread_mutex_unlock (&p->mutex);
if (err) {
logerr(p, err, "%s(%s): pthread_mutex_unlock failed", cap, __func__);
logerr (p, err, "%s(%s): pthread_mutex_unlock failed", cap, AUDIO_FUNC);
return -1;
}
err = pthread_cond_signal (&p->cond);
if (err) {
logerr(p, err, "%s(%s): pthread_cond_signal failed", cap, __func__);
logerr (p, err, "%s(%s): pthread_cond_signal failed", cap, AUDIO_FUNC);
return -1;
}
return 0;
@@ -165,7 +166,7 @@ int audio_pt_join (struct audio_pt *p, void **arg, const char *cap)
err = pthread_join (p->thread, &ret);
if (err) {
logerr(p, err, "%s(%s): pthread_join failed", cap, __func__);
logerr (p, err, "%s(%s): pthread_join failed", cap, AUDIO_FUNC);
return -1;
}
*arg = ret;
+17 -51
View File
@@ -57,13 +57,13 @@ static void glue (audio_init_nb_voices_, TYPE) (struct audio_driver *drv)
glue (s->nb_hw_voices_, TYPE) = max_voices;
}
if (audio_bug(__func__, !voice_size && max_voices)) {
if (audio_bug (AUDIO_FUNC, !voice_size && max_voices)) {
dolog ("drv=`%s' voice_size=0 max_voices=%d\n",
drv->name, max_voices);
glue (s->nb_hw_voices_, TYPE) = 0;
}
if (audio_bug(__func__, voice_size && !max_voices)) {
if (audio_bug (AUDIO_FUNC, voice_size && !max_voices)) {
dolog ("drv=`%s' voice_size=%d max_voices=0\n",
drv->name, voice_size);
}
@@ -77,7 +77,7 @@ static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw)
{
HWBUF = audio_calloc(__func__, hw->samples, sizeof(struct st_sample));
HWBUF = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (struct st_sample));
if (!HWBUF) {
dolog ("Could not allocate " NAME " buffer (%d samples)\n",
hw->samples);
@@ -105,7 +105,7 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
sw->buf = audio_calloc(__func__, samples, sizeof(struct st_sample));
sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (struct st_sample));
if (!sw->buf) {
dolog ("Could not allocate buffer for `%s' (%d samples)\n",
SW_NAME (sw), samples);
@@ -238,17 +238,17 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
return NULL;
}
if (audio_bug(__func__, !drv)) {
if (audio_bug (AUDIO_FUNC, !drv)) {
dolog ("No host audio driver\n");
return NULL;
}
if (audio_bug(__func__, !drv->pcm_ops)) {
if (audio_bug (AUDIO_FUNC, !drv->pcm_ops)) {
dolog ("Host audio driver without pcm_ops\n");
return NULL;
}
hw = audio_calloc(__func__, 1, glue(drv->voice_size_, TYPE));
hw = audio_calloc (AUDIO_FUNC, 1, glue (drv->voice_size_, TYPE));
if (!hw) {
dolog ("Can not allocate voice `%s' size %d\n",
drv->name, glue (drv->voice_size_, TYPE));
@@ -266,7 +266,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
goto err0;
}
if (audio_bug(__func__, hw->samples <= 0)) {
if (audio_bug (AUDIO_FUNC, hw->samples <= 0)) {
dolog ("hw->samples=%d\n", hw->samples);
goto err1;
}
@@ -299,42 +299,11 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
return NULL;
}
AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
{
switch (dev->driver) {
case AUDIODEV_DRIVER_NONE:
return dev->u.none.TYPE;
case AUDIODEV_DRIVER_ALSA:
return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.TYPE);
case AUDIODEV_DRIVER_COREAUDIO:
return qapi_AudiodevCoreaudioPerDirectionOptions_base(
dev->u.coreaudio.TYPE);
case AUDIODEV_DRIVER_DSOUND:
return dev->u.dsound.TYPE;
case AUDIODEV_DRIVER_OSS:
return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE);
case AUDIODEV_DRIVER_PA:
return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE);
case AUDIODEV_DRIVER_SDL:
return dev->u.sdl.TYPE;
case AUDIODEV_DRIVER_SPICE:
return dev->u.spice.TYPE;
case AUDIODEV_DRIVER_WAV:
return dev->u.wav.TYPE;
case AUDIODEV_DRIVER__MAX:
break;
}
abort();
}
static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
{
HW *hw;
AudioState *s = &glob_audio_state;
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
if (pdo->fixed_settings) {
if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
if (hw) {
return hw;
@@ -362,17 +331,15 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
SW *sw;
HW *hw;
struct audsettings hw_as;
AudioState *s = &glob_audio_state;
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
if (pdo->fixed_settings) {
hw_as = audiodev_to_audsettings(pdo);
if (glue (conf.fixed_, TYPE).enabled) {
hw_as = glue (conf.fixed_, TYPE).settings;
}
else {
hw_as = *as;
}
sw = audio_calloc(__func__, 1, sizeof(*sw));
sw = audio_calloc (AUDIO_FUNC, 1, sizeof (*sw));
if (!sw) {
dolog ("Could not allocate soft voice `%s' (%zu bytes)\n",
sw_name ? sw_name : "unknown", sizeof (*sw));
@@ -412,7 +379,7 @@ static void glue (audio_close_, TYPE) (SW *sw)
void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw)
{
if (sw) {
if (audio_bug(__func__, !card)) {
if (audio_bug (AUDIO_FUNC, !card)) {
dolog ("card=%p\n", card);
return;
}
@@ -431,9 +398,8 @@ SW *glue (AUD_open_, TYPE) (
)
{
AudioState *s = &glob_audio_state;
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
if (audio_bug (AUDIO_FUNC, !card || !name || !callback_fn || !as)) {
dolog ("card=%p name=%p callback_fn=%p as=%p\n",
card, name, callback_fn, as);
goto fail;
@@ -442,12 +408,12 @@ SW *glue (AUD_open_, TYPE) (
ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
name, as->freq, as->nchannels, as->fmt);
if (audio_bug(__func__, audio_validate_settings(as))) {
if (audio_bug (AUDIO_FUNC, audio_validate_settings (as))) {
audio_print_settings (as);
goto fail;
}
if (audio_bug(__func__, !s->drv)) {
if (audio_bug (AUDIO_FUNC, !s->drv)) {
dolog ("Can not open `%s' (no host audio driver)\n", name);
goto fail;
}
@@ -456,7 +422,7 @@ SW *glue (AUD_open_, TYPE) (
return sw;
}
if (!pdo->fixed_settings && sw) {
if (!glue (conf.fixed_, TYPE).enabled && sw) {
glue (AUD_close_, TYPE) (card, sw);
sw = NULL;
}
+9 -9
View File
@@ -24,20 +24,20 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx,
wfx->cbSize = 0;
switch (as->fmt) {
case AUDIO_FORMAT_S8:
case AUDIO_FORMAT_U8:
case AUD_FMT_S8:
case AUD_FMT_U8:
wfx->wBitsPerSample = 8;
break;
case AUDIO_FORMAT_S16:
case AUDIO_FORMAT_U16:
case AUD_FMT_S16:
case AUD_FMT_U16:
wfx->wBitsPerSample = 16;
wfx->nAvgBytesPerSec <<= 1;
wfx->nBlockAlign <<= 1;
break;
case AUDIO_FORMAT_S32:
case AUDIO_FORMAT_U32:
case AUD_FMT_S32:
case AUD_FMT_U32:
wfx->wBitsPerSample = 32;
wfx->nAvgBytesPerSec <<= 2;
wfx->nBlockAlign <<= 2;
@@ -85,15 +85,15 @@ int waveformat_to_audio_settings (WAVEFORMATEX *wfx,
switch (wfx->wBitsPerSample) {
case 8:
as->fmt = AUDIO_FORMAT_U8;
as->fmt = AUD_FMT_U8;
break;
case 16:
as->fmt = AUDIO_FORMAT_S16;
as->fmt = AUD_FMT_S16;
break;
case 32:
as->fmt = AUDIO_FORMAT_S32;
as->fmt = AUD_FMT_S32;
break;
default:
+41 -20
View File
@@ -26,7 +26,7 @@
#include <CoreAudio/CoreAudio.h>
#include <pthread.h> /* pthread_X */
#include "qemu/module.h"
#include "qemu-common.h"
#include "audio.h"
#define AUDIO_CAP "coreaudio"
@@ -36,6 +36,11 @@
#define MAC_OS_X_VERSION_10_6 1060
#endif
typedef struct {
int buffer_frames;
int nbuffers;
} CoreaudioConf;
typedef struct coreaudioVoiceOut {
HWVoiceOut hw;
pthread_mutex_t mutex;
@@ -502,9 +507,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
int err;
const char *typ = "playback";
AudioValueRange frameRange;
Audiodev *dev = drv_opaque;
AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
int frames;
CoreaudioConf *conf = drv_opaque;
/* create mutex */
err = pthread_mutex_init(&core->mutex, NULL);
@@ -535,17 +538,16 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
return -1;
}
frames = audio_buffer_frames(
qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
if (frameRange.mMinimum > frames) {
if (frameRange.mMinimum > conf->buffer_frames) {
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
} else if (frameRange.mMaximum < frames) {
}
else if (frameRange.mMaximum < conf->buffer_frames) {
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
}
else {
core->audioDevicePropertyBufferFrameSize = frames;
core->audioDevicePropertyBufferFrameSize = conf->buffer_frames;
}
/* set Buffer Frame Size */
@@ -566,8 +568,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
"Could not get device buffer frame size\n");
return -1;
}
hw->samples = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) *
core->audioDevicePropertyBufferFrameSize;
hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
/* get StreamFormat */
status = coreaudio_get_streamformat(core->outputDeviceID,
@@ -679,15 +680,40 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
return 0;
}
static void *coreaudio_audio_init(Audiodev *dev)
static CoreaudioConf glob_conf = {
.buffer_frames = 512,
.nbuffers = 4,
};
static void *coreaudio_audio_init (void)
{
return dev;
CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf));
*conf = glob_conf;
return conf;
}
static void coreaudio_audio_fini (void *opaque)
{
g_free(opaque);
}
static struct audio_option coreaudio_options[] = {
{
.name = "BUFFER_SIZE",
.tag = AUD_OPT_INT,
.valp = &glob_conf.buffer_frames,
.descr = "Size of the buffer in frames"
},
{
.name = "BUFFER_COUNT",
.tag = AUD_OPT_INT,
.valp = &glob_conf.nbuffers,
.descr = "Number of buffers"
},
{ /* End of list */ }
};
static struct audio_pcm_ops coreaudio_pcm_ops = {
.init_out = coreaudio_init_out,
.fini_out = coreaudio_fini_out,
@@ -696,9 +722,10 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
.ctl_out = coreaudio_ctl_out
};
static struct audio_driver coreaudio_audio_driver = {
struct audio_driver coreaudio_audio_driver = {
.name = "coreaudio",
.descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
.options = coreaudio_options,
.init = coreaudio_audio_init,
.fini = coreaudio_audio_fini,
.pcm_ops = &coreaudio_pcm_ops,
@@ -708,9 +735,3 @@ static struct audio_driver coreaudio_audio_driver = {
.voice_size_out = sizeof (coreaudioVoiceOut),
.voice_size_in = 0
};
static void register_audio_coreaudio(void)
{
audio_driver_register(&coreaudio_audio_driver);
}
type_init(register_audio_coreaudio);
+3 -3
View File
@@ -167,18 +167,17 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
dsound *s = drv_opaque;
WAVEFORMATEX wfx;
struct audsettings obt_as;
DSoundConf *conf = &s->conf;
#ifdef DSBTYPE_IN
const char *typ = "ADC";
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
DSCBUFFERDESC bd;
DSCBCAPS bc;
AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.in;
#else
const char *typ = "DAC";
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
DSBUFFERDESC bd;
DSBCAPS bc;
AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.out;
#endif
if (!s->FIELD2) {
@@ -194,8 +193,8 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
memset (&bd, 0, sizeof (bd));
bd.dwSize = sizeof (bd);
bd.lpwfxFormat = &wfx;
bd.dwBufferBytes = audio_buffer_bytes(pdo, as, 92880);
#ifdef DSBTYPE_IN
bd.dwBufferBytes = conf->bufsize_in;
hr = IDirectSoundCapture_CreateCaptureBuffer (
s->dsound_capture,
&bd,
@@ -204,6 +203,7 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
);
#else
bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
bd.dwBufferBytes = conf->bufsize_out;
hr = IDirectSound_CreateSoundBuffer (
s->dsound,
&bd,
+46 -27
View File
@@ -27,12 +27,11 @@
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "audio.h"
#define AUDIO_CAP "dsound"
#include "audio_int.h"
#include "qemu/host-utils.h"
#include "qemu/module.h"
#include <windows.h>
#include <mmsystem.h>
@@ -43,11 +42,17 @@
/* #define DEBUG_DSOUND */
typedef struct {
int bufsize_in;
int bufsize_out;
int latency_millis;
} DSoundConf;
typedef struct {
LPDIRECTSOUND dsound;
LPDIRECTSOUNDCAPTURE dsound_capture;
struct audsettings settings;
Audiodev *dev;
DSoundConf conf;
} dsound;
typedef struct {
@@ -243,9 +248,9 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
dsound_log_hresult (hr);
}
static uint64_t usecs_to_bytes(struct audio_pcm_info *info, uint32_t usecs)
static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
{
return muldiv64(usecs, info->bytes_per_second, 1000000);
return (millis * info->bytes_per_second) / 1000;
}
#ifdef DEBUG_DSOUND
@@ -473,7 +478,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
LPVOID p1, p2;
int bufsize;
dsound *s = ds->s;
AudiodevDsoundOptions *dso = &s->dev->u.dsound;
DSoundConf *conf = &s->conf;
if (!dsb) {
dolog ("Attempt to run empty with playback buffer\n");
@@ -496,14 +501,14 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
len = live << hwshift;
if (ds->first_time) {
if (dso->latency) {
if (conf->latency_millis) {
DWORD cur_blat;
cur_blat = audio_ring_dist (wpos, ppos, bufsize);
ds->first_time = 0;
old_pos = wpos;
old_pos +=
usecs_to_bytes(&hw->info, dso->latency) - cur_blat;
millis_to_bytes (&hw->info, conf->latency_millis) - cur_blat;
old_pos %= bufsize;
old_pos &= ~hw->info.align;
}
@@ -538,7 +543,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
}
}
if (audio_bug(__func__, len < 0 || len > bufsize)) {
if (audio_bug (AUDIO_FUNC, len < 0 || len > bufsize)) {
dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n",
len, bufsize, old_pos, ppos);
return 0;
@@ -742,6 +747,12 @@ static int dsound_run_in (HWVoiceIn *hw)
return decr;
}
static DSoundConf glob_conf = {
.bufsize_in = 16384,
.bufsize_out = 16384,
.latency_millis = 10
};
static void dsound_audio_fini (void *opaque)
{
HRESULT hr;
@@ -772,22 +783,13 @@ static void dsound_audio_fini (void *opaque)
g_free(s);
}
static void *dsound_audio_init(Audiodev *dev)
static void *dsound_audio_init (void)
{
int err;
HRESULT hr;
dsound *s = g_malloc0(sizeof(dsound));
AudiodevDsoundOptions *dso;
assert(dev->driver == AUDIODEV_DRIVER_DSOUND);
s->dev = dev;
dso = &dev->u.dsound;
if (!dso->has_latency) {
dso->has_latency = true;
dso->latency = 10000; /* 10 ms */
}
s->conf = glob_conf;
hr = CoInitialize (NULL);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not initialize COM\n");
@@ -852,6 +854,28 @@ static void *dsound_audio_init(Audiodev *dev)
return s;
}
static struct audio_option dsound_options[] = {
{
.name = "LATENCY_MILLIS",
.tag = AUD_OPT_INT,
.valp = &glob_conf.latency_millis,
.descr = "(undocumented)"
},
{
.name = "BUFSIZE_OUT",
.tag = AUD_OPT_INT,
.valp = &glob_conf.bufsize_out,
.descr = "(undocumented)"
},
{
.name = "BUFSIZE_IN",
.tag = AUD_OPT_INT,
.valp = &glob_conf.bufsize_in,
.descr = "(undocumented)"
},
{ /* End of list */ }
};
static struct audio_pcm_ops dsound_pcm_ops = {
.init_out = dsound_init_out,
.fini_out = dsound_fini_out,
@@ -866,9 +890,10 @@ static struct audio_pcm_ops dsound_pcm_ops = {
.ctl_in = dsound_ctl_in
};
static struct audio_driver dsound_audio_driver = {
struct audio_driver dsound_audio_driver = {
.name = "dsound",
.descr = "DirectSound http://wikipedia.org/wiki/DirectSound",
.options = dsound_options,
.init = dsound_audio_init,
.fini = dsound_audio_fini,
.pcm_ops = &dsound_pcm_ops,
@@ -878,9 +903,3 @@ static struct audio_driver dsound_audio_driver = {
.voice_size_out = sizeof (DSoundVoiceOut),
.voice_size_in = sizeof (DSoundVoiceIn)
};
static void register_audio_dsound(void)
{
audio_driver_register(&dsound_audio_driver);
}
type_init(register_audio_dsound);
+2 -1
View File
@@ -23,6 +23,7 @@
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/bswap.h"
#include "qemu/error-report.h"
#include "audio.h"
@@ -343,7 +344,7 @@ struct rate {
*/
void *st_rate_start (int inrate, int outrate)
{
struct rate *rate = audio_calloc(__func__, 1, sizeof(*rate));
struct rate *rate = audio_calloc (AUDIO_FUNC, 1, sizeof (*rate));
if (!rate) {
dolog ("Could not allocate resampler (%zu bytes)\n", sizeof (*rate));
+4 -10
View File
@@ -21,10 +21,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/host-utils.h"
#include "qemu/module.h"
#include "audio.h"
#include "qemu/timer.h"
@@ -137,7 +136,7 @@ static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
return 0;
}
static void *no_audio_init(Audiodev *dev)
static void *no_audio_init (void)
{
return &no_audio_init;
}
@@ -161,9 +160,10 @@ static struct audio_pcm_ops no_pcm_ops = {
.ctl_in = no_ctl_in
};
static struct audio_driver no_audio_driver = {
struct audio_driver no_audio_driver = {
.name = "none",
.descr = "Timer based audio emulation",
.options = NULL,
.init = no_audio_init,
.fini = no_audio_fini,
.pcm_ops = &no_pcm_ops,
@@ -173,9 +173,3 @@ static struct audio_driver no_audio_driver = {
.voice_size_out = sizeof (NoVoiceOut),
.voice_size_in = sizeof (NoVoiceIn)
};
static void register_audio_none(void)
{
audio_driver_register(&no_audio_driver);
}
type_init(register_audio_none);
+134 -80
View File
@@ -21,12 +21,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include "qemu-common.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/host-utils.h"
#include "audio.h"
#include "trace.h"
@@ -38,6 +37,16 @@
#define USE_DSP_POLICY
#endif
typedef struct OSSConf {
int try_mmap;
int nfrags;
int fragsize;
const char *devpath_out;
const char *devpath_in;
int exclusive;
int policy;
} OSSConf;
typedef struct OSSVoiceOut {
HWVoiceOut hw;
void *pcm_buf;
@@ -47,7 +56,7 @@ typedef struct OSSVoiceOut {
int fragsize;
int mmapped;
int pending;
Audiodev *dev;
OSSConf *conf;
} OSSVoiceOut;
typedef struct OSSVoiceIn {
@@ -56,12 +65,12 @@ typedef struct OSSVoiceIn {
int fd;
int nfrags;
int fragsize;
Audiodev *dev;
OSSConf *conf;
} OSSVoiceIn;
struct oss_params {
int freq;
int fmt;
audfmt_e fmt;
int nchannels;
int nfrags;
int fragsize;
@@ -139,16 +148,16 @@ static int oss_write (SWVoiceOut *sw, void *buf, int len)
return audio_pcm_sw_write (sw, buf, len);
}
static int aud_to_ossfmt (AudioFormat fmt, int endianness)
static int aud_to_ossfmt (audfmt_e fmt, int endianness)
{
switch (fmt) {
case AUDIO_FORMAT_S8:
case AUD_FMT_S8:
return AFMT_S8;
case AUDIO_FORMAT_U8:
case AUD_FMT_U8:
return AFMT_U8;
case AUDIO_FORMAT_S16:
case AUD_FMT_S16:
if (endianness) {
return AFMT_S16_BE;
}
@@ -156,7 +165,7 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness)
return AFMT_S16_LE;
}
case AUDIO_FORMAT_U16:
case AUD_FMT_U16:
if (endianness) {
return AFMT_U16_BE;
}
@@ -173,37 +182,37 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness)
}
}
static int oss_to_audfmt (int ossfmt, AudioFormat *fmt, int *endianness)
static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
{
switch (ossfmt) {
case AFMT_S8:
*endianness = 0;
*fmt = AUDIO_FORMAT_S8;
*fmt = AUD_FMT_S8;
break;
case AFMT_U8:
*endianness = 0;
*fmt = AUDIO_FORMAT_U8;
*fmt = AUD_FMT_U8;
break;
case AFMT_S16_LE:
*endianness = 0;
*fmt = AUDIO_FORMAT_S16;
*fmt = AUD_FMT_S16;
break;
case AFMT_U16_LE:
*endianness = 0;
*fmt = AUDIO_FORMAT_U16;
*fmt = AUD_FMT_U16;
break;
case AFMT_S16_BE:
*endianness = 1;
*fmt = AUDIO_FORMAT_S16;
*fmt = AUD_FMT_S16;
break;
case AFMT_U16_BE:
*endianness = 1;
*fmt = AUDIO_FORMAT_U16;
*fmt = AUD_FMT_U16;
break;
default:
@@ -253,25 +262,19 @@ static int oss_get_version (int fd, int *version, const char *typ)
}
#endif
static int oss_open(int in, struct oss_params *req, audsettings *as,
struct oss_params *obt, int *pfd, Audiodev *dev)
static int oss_open (int in, struct oss_params *req,
struct oss_params *obt, int *pfd, OSSConf* conf)
{
AudiodevOssOptions *oopts = &dev->u.oss;
AudiodevOssPerDirectionOptions *opdo = in ? oopts->in : oopts->out;
int fd;
int oflags = (oopts->has_exclusive && oopts->exclusive) ? O_EXCL : 0;
int oflags = conf->exclusive ? O_EXCL : 0;
audio_buf_info abinfo;
int fmt, freq, nchannels;
int setfragment = 1;
const char *dspname = opdo->has_dev ? opdo->dev : "/dev/dsp";
const char *dspname = in ? conf->devpath_in : conf->devpath_out;
const char *typ = in ? "ADC" : "DAC";
#ifdef USE_DSP_POLICY
int policy = oopts->has_dsp_policy ? oopts->dsp_policy : 5;
#endif
/* Kludge needed to have working mmap on Linux */
oflags |= (oopts->has_try_mmap && oopts->try_mmap) ?
O_RDWR : (in ? O_RDONLY : O_WRONLY);
oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
fd = open (dspname, oflags | O_NONBLOCK);
if (-1 == fd) {
@@ -282,9 +285,6 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
freq = req->freq;
nchannels = req->nchannels;
fmt = req->fmt;
req->nfrags = opdo->has_buffer_count ? opdo->buffer_count : 4;
req->fragsize = audio_buffer_bytes(
qapi_AudiodevOssPerDirectionOptions_base(opdo), as, 23220);
if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
@@ -308,18 +308,18 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
}
#ifdef USE_DSP_POLICY
if (policy >= 0) {
if (conf->policy >= 0) {
int version;
if (!oss_get_version (fd, &version, typ)) {
trace_oss_version(version);
if (version >= 0x040000) {
int policy2 = policy;
if (ioctl(fd, SNDCTL_DSP_POLICY, &policy2)) {
int policy = conf->policy;
if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
oss_logerr2 (errno, typ,
"Failed to set timing policy to %d\n",
policy);
conf->policy);
goto err;
}
setfragment = 0;
@@ -500,18 +500,19 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
int endianness;
int err;
int fd;
AudioFormat effective_fmt;
audfmt_e effective_fmt;
struct audsettings obt_as;
Audiodev *dev = drv_opaque;
AudiodevOssOptions *oopts = &dev->u.oss;
OSSConf *conf = drv_opaque;
oss->fd = -1;
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
req.freq = as->freq;
req.nchannels = as->nchannels;
req.fragsize = conf->fragsize;
req.nfrags = conf->nfrags;
if (oss_open(0, &req, as, &obt, &fd, dev)) {
if (oss_open (0, &req, &obt, &fd, conf)) {
return -1;
}
@@ -538,7 +539,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
oss->mmapped = 0;
if (oopts->has_try_mmap && oopts->try_mmap) {
if (conf->try_mmap) {
oss->pcm_buf = mmap (
NULL,
hw->samples << hw->info.shift,
@@ -581,9 +582,11 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
}
if (!oss->mmapped) {
oss->pcm_buf = audio_calloc(__func__,
hw->samples,
1 << hw->info.shift);
oss->pcm_buf = audio_calloc (
AUDIO_FUNC,
hw->samples,
1 << hw->info.shift
);
if (!oss->pcm_buf) {
dolog (
"Could not allocate DAC buffer (%d samples, each %d bytes)\n",
@@ -596,7 +599,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
}
oss->fd = fd;
oss->dev = dev;
oss->conf = conf;
return 0;
}
@@ -604,12 +607,16 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
int trig;
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
switch (cmd) {
case VOICE_ENABLE:
{
bool poll_mode = opdo->try_poll;
va_list ap;
int poll_mode;
va_start (ap, cmd);
poll_mode = va_arg (ap, int);
va_end (ap);
ldebug ("enabling voice\n");
if (poll_mode) {
@@ -662,16 +669,18 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
int endianness;
int err;
int fd;
AudioFormat effective_fmt;
audfmt_e effective_fmt;
struct audsettings obt_as;
Audiodev *dev = drv_opaque;
OSSConf *conf = drv_opaque;
oss->fd = -1;
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
req.freq = as->freq;
req.nchannels = as->nchannels;
if (oss_open(1, &req, as, &obt, &fd, dev)) {
req.fragsize = conf->fragsize;
req.nfrags = conf->nfrags;
if (oss_open (1, &req, &obt, &fd, conf)) {
return -1;
}
@@ -696,7 +705,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
}
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
oss->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
if (!oss->pcm_buf) {
dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
hw->samples, 1 << hw->info.shift);
@@ -705,7 +714,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
}
oss->fd = fd;
oss->dev = dev;
oss->conf = conf;
return 0;
}
@@ -796,12 +805,16 @@ static int oss_read (SWVoiceIn *sw, void *buf, int size)
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
switch (cmd) {
case VOICE_ENABLE:
{
bool poll_mode = opdo->try_poll;
va_list ap;
int poll_mode;
va_start (ap, cmd);
poll_mode = va_arg (ap, int);
va_end (ap);
if (poll_mode) {
oss_poll_in (hw);
@@ -821,36 +834,82 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
return 0;
}
static void oss_init_per_direction(AudiodevOssPerDirectionOptions *opdo)
static OSSConf glob_conf = {
.try_mmap = 0,
.nfrags = 4,
.fragsize = 4096,
.devpath_out = "/dev/dsp",
.devpath_in = "/dev/dsp",
.exclusive = 0,
.policy = 5
};
static void *oss_audio_init (void)
{
if (!opdo->has_try_poll) {
opdo->try_poll = true;
opdo->has_try_poll = true;
}
}
OSSConf *conf = g_malloc(sizeof(OSSConf));
*conf = glob_conf;
static void *oss_audio_init(Audiodev *dev)
{
AudiodevOssOptions *oopts;
assert(dev->driver == AUDIODEV_DRIVER_OSS);
oopts = &dev->u.oss;
oss_init_per_direction(oopts->in);
oss_init_per_direction(oopts->out);
if (access(oopts->in->has_dev ? oopts->in->dev : "/dev/dsp",
R_OK | W_OK) < 0 ||
access(oopts->out->has_dev ? oopts->out->dev : "/dev/dsp",
R_OK | W_OK) < 0) {
if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
access(conf->devpath_out, R_OK | W_OK) < 0) {
g_free(conf);
return NULL;
}
return dev;
return conf;
}
static void oss_audio_fini (void *opaque)
{
g_free(opaque);
}
static struct audio_option oss_options[] = {
{
.name = "FRAGSIZE",
.tag = AUD_OPT_INT,
.valp = &glob_conf.fragsize,
.descr = "Fragment size in bytes"
},
{
.name = "NFRAGS",
.tag = AUD_OPT_INT,
.valp = &glob_conf.nfrags,
.descr = "Number of fragments"
},
{
.name = "MMAP",
.tag = AUD_OPT_BOOL,
.valp = &glob_conf.try_mmap,
.descr = "Try using memory mapped access"
},
{
.name = "DAC_DEV",
.tag = AUD_OPT_STR,
.valp = &glob_conf.devpath_out,
.descr = "Path to DAC device"
},
{
.name = "ADC_DEV",
.tag = AUD_OPT_STR,
.valp = &glob_conf.devpath_in,
.descr = "Path to ADC device"
},
{
.name = "EXCLUSIVE",
.tag = AUD_OPT_BOOL,
.valp = &glob_conf.exclusive,
.descr = "Open device in exclusive mode (vmix won't work)"
},
#ifdef USE_DSP_POLICY
{
.name = "POLICY",
.tag = AUD_OPT_INT,
.valp = &glob_conf.policy,
.descr = "Set the timing policy of the device, -1 to use fragment mode",
},
#endif
{ /* End of list */ }
};
static struct audio_pcm_ops oss_pcm_ops = {
.init_out = oss_init_out,
.fini_out = oss_fini_out,
@@ -865,9 +924,10 @@ static struct audio_pcm_ops oss_pcm_ops = {
.ctl_in = oss_ctl_in
};
static struct audio_driver oss_audio_driver = {
struct audio_driver oss_audio_driver = {
.name = "oss",
.descr = "OSS http://www.opensound.com",
.options = oss_options,
.init = oss_audio_init,
.fini = oss_audio_fini,
.pcm_ops = &oss_pcm_ops,
@@ -877,9 +937,3 @@ static struct audio_driver oss_audio_driver = {
.voice_size_out = sizeof (OSSVoiceOut),
.voice_size_in = sizeof (OSSVoiceIn)
};
static void register_audio_oss(void)
{
audio_driver_register(&oss_audio_driver);
}
type_init(register_audio_oss);
+106 -129
View File
@@ -1,9 +1,7 @@
/* public domain */
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu-common.h"
#include "audio.h"
#include "qapi/opts-visitor.h"
#include <pulse/pulseaudio.h>
@@ -12,7 +10,14 @@
#include "audio_pt_int.h"
typedef struct {
Audiodev *dev;
int samples;
char *server;
char *sink;
char *source;
} PAConf;
typedef struct {
PAConf conf;
pa_threaded_mainloop *mainloop;
pa_context *context;
} paaudio;
@@ -27,7 +32,6 @@ typedef struct {
void *pcm_buf;
struct audio_pt pt;
paaudio *g;
int samples;
} PAVoiceOut;
typedef struct {
@@ -42,7 +46,6 @@ typedef struct {
const void *read_data;
size_t read_index, read_length;
paaudio *g;
int samples;
} PAVoiceIn;
static void qpa_audio_fini(void *opaque);
@@ -86,7 +89,7 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
} \
goto label; \
} \
} while (0)
} while (0);
#define CHECK_DEAD_GOTO(c, stream, rerror, label) \
do { \
@@ -104,7 +107,7 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
} \
goto label; \
} \
} while (0)
} while (0);
static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror)
{
@@ -203,7 +206,7 @@ static void *qpa_thread_out (void *arg)
PAVoiceOut *pa = arg;
HWVoiceOut *hw = &pa->hw;
if (audio_pt_lock(&pa->pt, __func__)) {
if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
return NULL;
}
@@ -219,15 +222,15 @@ static void *qpa_thread_out (void *arg)
break;
}
if (audio_pt_wait(&pa->pt, __func__)) {
if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
goto exit;
}
}
decr = to_mix = audio_MIN(pa->live, pa->samples >> 5);
decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2);
rpos = pa->rpos;
if (audio_pt_unlock(&pa->pt, __func__)) {
if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
return NULL;
}
@@ -248,7 +251,7 @@ static void *qpa_thread_out (void *arg)
to_mix -= chunk;
}
if (audio_pt_lock(&pa->pt, __func__)) {
if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
return NULL;
}
@@ -258,7 +261,7 @@ static void *qpa_thread_out (void *arg)
}
exit:
audio_pt_unlock(&pa->pt, __func__);
audio_pt_unlock (&pa->pt, AUDIO_FUNC);
return NULL;
}
@@ -267,7 +270,7 @@ static int qpa_run_out (HWVoiceOut *hw, int live)
int decr;
PAVoiceOut *pa = (PAVoiceOut *) hw;
if (audio_pt_lock(&pa->pt, __func__)) {
if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
return 0;
}
@@ -276,10 +279,10 @@ static int qpa_run_out (HWVoiceOut *hw, int live)
pa->live = live - decr;
hw->rpos = pa->rpos;
if (pa->live > 0) {
audio_pt_unlock_and_signal(&pa->pt, __func__);
audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
}
else {
audio_pt_unlock(&pa->pt, __func__);
audio_pt_unlock (&pa->pt, AUDIO_FUNC);
}
return decr;
}
@@ -295,7 +298,7 @@ static void *qpa_thread_in (void *arg)
PAVoiceIn *pa = arg;
HWVoiceIn *hw = &pa->hw;
if (audio_pt_lock(&pa->pt, __func__)) {
if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
return NULL;
}
@@ -311,15 +314,15 @@ static void *qpa_thread_in (void *arg)
break;
}
if (audio_pt_wait(&pa->pt, __func__)) {
if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
goto exit;
}
}
incr = to_grab = audio_MIN(pa->dead, pa->samples >> 5);
incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2);
wpos = pa->wpos;
if (audio_pt_unlock(&pa->pt, __func__)) {
if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
return NULL;
}
@@ -339,7 +342,7 @@ static void *qpa_thread_in (void *arg)
to_grab -= chunk;
}
if (audio_pt_lock(&pa->pt, __func__)) {
if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
return NULL;
}
@@ -349,7 +352,7 @@ static void *qpa_thread_in (void *arg)
}
exit:
audio_pt_unlock(&pa->pt, __func__);
audio_pt_unlock (&pa->pt, AUDIO_FUNC);
return NULL;
}
@@ -358,7 +361,7 @@ static int qpa_run_in (HWVoiceIn *hw)
int live, incr, dead;
PAVoiceIn *pa = (PAVoiceIn *) hw;
if (audio_pt_lock(&pa->pt, __func__)) {
if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
return 0;
}
@@ -369,10 +372,10 @@ static int qpa_run_in (HWVoiceIn *hw)
pa->dead = dead - incr;
hw->wpos = pa->wpos;
if (pa->dead > 0) {
audio_pt_unlock_and_signal(&pa->pt, __func__);
audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
}
else {
audio_pt_unlock(&pa->pt, __func__);
audio_pt_unlock (&pa->pt, AUDIO_FUNC);
}
return incr;
}
@@ -382,21 +385,21 @@ static int qpa_read (SWVoiceIn *sw, void *buf, int len)
return audio_pcm_sw_read (sw, buf, len);
}
static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
{
int format;
switch (afmt) {
case AUDIO_FORMAT_S8:
case AUDIO_FORMAT_U8:
case AUD_FMT_S8:
case AUD_FMT_U8:
format = PA_SAMPLE_U8;
break;
case AUDIO_FORMAT_S16:
case AUDIO_FORMAT_U16:
case AUD_FMT_S16:
case AUD_FMT_U16:
format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
break;
case AUDIO_FORMAT_S32:
case AUDIO_FORMAT_U32:
case AUD_FMT_S32:
case AUD_FMT_U32:
format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
break;
default:
@@ -407,26 +410,26 @@ static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
return format;
}
static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
{
switch (fmt) {
case PA_SAMPLE_U8:
return AUDIO_FORMAT_U8;
return AUD_FMT_U8;
case PA_SAMPLE_S16BE:
*endianness = 1;
return AUDIO_FORMAT_S16;
return AUD_FMT_S16;
case PA_SAMPLE_S16LE:
*endianness = 0;
return AUDIO_FORMAT_S16;
return AUD_FMT_S16;
case PA_SAMPLE_S32BE:
*endianness = 1;
return AUDIO_FORMAT_S32;
return AUD_FMT_S32;
case PA_SAMPLE_S32LE:
*endianness = 0;
return AUDIO_FORMAT_S32;
return AUD_FMT_S32;
default:
dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
return AUDIO_FORMAT_U8;
return AUD_FMT_U8;
}
}
@@ -543,15 +546,17 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
struct audsettings obt_as = *as;
PAVoiceOut *pa = (PAVoiceOut *) hw;
paaudio *g = pa->g = drv_opaque;
AudiodevPaOptions *popts = &g->dev->u.pa;
AudiodevPaPerDirectionOptions *ppdo = popts->out;
ss.format = audfmt_to_pa (as->fmt, as->endianness);
ss.channels = as->nchannels;
ss.rate = as->freq;
ba.tlength = pa_usec_to_bytes(ppdo->latency, &ss);
ba.minreq = -1;
/*
* qemu audio tick runs at 100 Hz (by default), so processing
* data chunks worth 10 ms of sound should be a good fit.
*/
ba.tlength = pa_usec_to_bytes (10 * 1000, &ss);
ba.minreq = pa_usec_to_bytes (5 * 1000, &ss);
ba.maxlength = -1;
ba.prebuf = -1;
@@ -561,7 +566,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
g,
"qemu",
PA_STREAM_PLAYBACK,
ppdo->has_name ? ppdo->name : NULL,
g->conf.sink,
&ss,
NULL, /* channel map */
&ba, /* buffering attributes */
@@ -573,10 +578,8 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
}
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = pa->samples = audio_buffer_samples(
qapi_AudiodevPaPerDirectionOptions_base(ppdo),
&obt_as, ppdo->buffer_length);
pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
hw->samples = g->conf.samples;
pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
pa->rpos = hw->rpos;
if (!pa->pcm_buf) {
dolog ("Could not allocate buffer (%d bytes)\n",
@@ -584,7 +587,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
goto fail2;
}
if (audio_pt_init(&pa->pt, qpa_thread_out, hw, AUDIO_CAP, __func__)) {
if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
goto fail3;
}
@@ -606,32 +609,24 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
{
int error;
pa_sample_spec ss;
pa_buffer_attr ba;
struct audsettings obt_as = *as;
PAVoiceIn *pa = (PAVoiceIn *) hw;
paaudio *g = pa->g = drv_opaque;
AudiodevPaOptions *popts = &g->dev->u.pa;
AudiodevPaPerDirectionOptions *ppdo = popts->in;
ss.format = audfmt_to_pa (as->fmt, as->endianness);
ss.channels = as->nchannels;
ss.rate = as->freq;
ba.fragsize = pa_usec_to_bytes(ppdo->latency, &ss);
ba.maxlength = pa_usec_to_bytes(ppdo->latency * 2, &ss);
ba.minreq = -1;
ba.prebuf = -1;
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
pa->stream = qpa_simple_new (
g,
"qemu",
PA_STREAM_RECORD,
ppdo->has_name ? ppdo->name : NULL,
g->conf.source,
&ss,
NULL, /* channel map */
&ba, /* buffering attributes */
NULL, /* buffering attributes */
&error
);
if (!pa->stream) {
@@ -640,10 +635,8 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
}
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = pa->samples = audio_buffer_samples(
qapi_AudiodevPaPerDirectionOptions_base(ppdo),
&obt_as, ppdo->buffer_length);
pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
hw->samples = g->conf.samples;
pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
pa->wpos = hw->wpos;
if (!pa->pcm_buf) {
dolog ("Could not allocate buffer (%d bytes)\n",
@@ -651,7 +644,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
goto fail2;
}
if (audio_pt_init(&pa->pt, qpa_thread_in, hw, AUDIO_CAP, __func__)) {
if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
goto fail3;
}
@@ -674,17 +667,17 @@ static void qpa_fini_out (HWVoiceOut *hw)
void *ret;
PAVoiceOut *pa = (PAVoiceOut *) hw;
audio_pt_lock(&pa->pt, __func__);
audio_pt_lock (&pa->pt, AUDIO_FUNC);
pa->done = 1;
audio_pt_unlock_and_signal(&pa->pt, __func__);
audio_pt_join(&pa->pt, &ret, __func__);
audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
if (pa->stream) {
pa_stream_unref (pa->stream);
pa->stream = NULL;
}
audio_pt_fini(&pa->pt, __func__);
audio_pt_fini (&pa->pt, AUDIO_FUNC);
g_free (pa->pcm_buf);
pa->pcm_buf = NULL;
}
@@ -694,17 +687,17 @@ static void qpa_fini_in (HWVoiceIn *hw)
void *ret;
PAVoiceIn *pa = (PAVoiceIn *) hw;
audio_pt_lock(&pa->pt, __func__);
audio_pt_lock (&pa->pt, AUDIO_FUNC);
pa->done = 1;
audio_pt_unlock_and_signal(&pa->pt, __func__);
audio_pt_join(&pa->pt, &ret, __func__);
audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
if (pa->stream) {
pa_stream_unref (pa->stream);
pa->stream = NULL;
}
audio_pt_fini(&pa->pt, __func__);
audio_pt_fini (&pa->pt, AUDIO_FUNC);
g_free (pa->pcm_buf);
pa->pcm_buf = NULL;
}
@@ -814,54 +807,15 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
return 0;
}
static int qpa_validate_per_direction_opts(Audiodev *dev,
AudiodevPaPerDirectionOptions *pdo)
/* common */
static PAConf glob_conf = {
.samples = 4096,
};
static void *qpa_audio_init (void)
{
if (!pdo->has_buffer_length) {
pdo->has_buffer_length = true;
pdo->buffer_length = 46440;
}
if (!pdo->has_latency) {
pdo->has_latency = true;
pdo->latency = 15000;
}
return 1;
}
static void *qpa_audio_init(Audiodev *dev)
{
paaudio *g;
AudiodevPaOptions *popts = &dev->u.pa;
const char *server;
if (!popts->has_server) {
char pidfile[64];
char *runtime;
struct stat st;
runtime = getenv("XDG_RUNTIME_DIR");
if (!runtime) {
return NULL;
}
snprintf(pidfile, sizeof(pidfile), "%s/pulse/pid", runtime);
if (stat(pidfile, &st) != 0) {
return NULL;
}
}
assert(dev->driver == AUDIODEV_DRIVER_PA);
g = g_malloc(sizeof(paaudio));
server = popts->has_server ? popts->server : NULL;
if (!qpa_validate_per_direction_opts(dev, popts->in)) {
goto fail;
}
if (!qpa_validate_per_direction_opts(dev, popts->out)) {
goto fail;
}
g->dev = dev;
paaudio *g = g_malloc(sizeof(paaudio));
g->conf = glob_conf;
g->mainloop = NULL;
g->context = NULL;
@@ -871,14 +825,14 @@ static void *qpa_audio_init(Audiodev *dev)
}
g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop),
server);
g->conf.server);
if (!g->context) {
goto fail;
}
pa_context_set_state_callback (g->context, context_state_cb, g);
if (pa_context_connect(g->context, server, 0, NULL) < 0) {
if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) {
qpa_logerr (pa_context_errno (g->context),
"pa_context_connect() failed\n");
goto fail;
@@ -941,6 +895,34 @@ static void qpa_audio_fini (void *opaque)
g_free(g);
}
struct audio_option qpa_options[] = {
{
.name = "SAMPLES",
.tag = AUD_OPT_INT,
.valp = &glob_conf.samples,
.descr = "buffer size in samples"
},
{
.name = "SERVER",
.tag = AUD_OPT_STR,
.valp = &glob_conf.server,
.descr = "server address"
},
{
.name = "SINK",
.tag = AUD_OPT_STR,
.valp = &glob_conf.sink,
.descr = "sink device name"
},
{
.name = "SOURCE",
.tag = AUD_OPT_STR,
.valp = &glob_conf.source,
.descr = "source device name"
},
{ /* End of list */ }
};
static struct audio_pcm_ops qpa_pcm_ops = {
.init_out = qpa_init_out,
.fini_out = qpa_fini_out,
@@ -955,9 +937,10 @@ static struct audio_pcm_ops qpa_pcm_ops = {
.ctl_in = qpa_ctl_in
};
static struct audio_driver pa_audio_driver = {
struct audio_driver pa_audio_driver = {
.name = "pa",
.descr = "http://www.pulseaudio.org/",
.options = qpa_options,
.init = qpa_audio_init,
.fini = qpa_audio_fini,
.pcm_ops = &qpa_pcm_ops,
@@ -968,9 +951,3 @@ static struct audio_driver pa_audio_driver = {
.voice_size_in = sizeof (PAVoiceIn),
.ctl_caps = VOICE_VOLUME_CAP
};
static void register_audio_pa(void)
{
audio_driver_register(&pa_audio_driver);
}
type_init(register_audio_pa);
+198 -49
View File
@@ -21,11 +21,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include <SDL.h>
#include <SDL_thread.h>
#include "qemu/module.h"
#include "qemu-common.h"
#include "audio.h"
#ifndef _WIN32
@@ -39,17 +38,31 @@
#define AUDIO_CAP "sdl"
#include "audio_int.h"
#define USE_SEMAPHORE (SDL_MAJOR_VERSION < 2)
typedef struct SDLVoiceOut {
HWVoiceOut hw;
int live;
#if USE_SEMAPHORE
int rpos;
#endif
int decr;
} SDLVoiceOut;
static struct {
int nb_samples;
} conf = {
.nb_samples = 1024
};
static struct SDLAudioState {
int exit;
#if USE_SEMAPHORE
SDL_mutex *mutex;
SDL_sem *sem;
#endif
int initialized;
bool driver_created;
Audiodev *dev;
} glob_sdl;
typedef struct SDLAudioState SDLAudioState;
@@ -64,19 +77,79 @@ static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
}
static int aud_to_sdlfmt (AudioFormat fmt)
static int sdl_lock (SDLAudioState *s, const char *forfn)
{
#if USE_SEMAPHORE
if (SDL_LockMutex (s->mutex)) {
sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
return -1;
}
#else
SDL_LockAudio();
#endif
return 0;
}
static int sdl_unlock (SDLAudioState *s, const char *forfn)
{
#if USE_SEMAPHORE
if (SDL_UnlockMutex (s->mutex)) {
sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn);
return -1;
}
#else
SDL_UnlockAudio();
#endif
return 0;
}
static int sdl_post (SDLAudioState *s, const char *forfn)
{
#if USE_SEMAPHORE
if (SDL_SemPost (s->sem)) {
sdl_logerr ("SDL_SemPost for %s failed\n", forfn);
return -1;
}
#endif
return 0;
}
#if USE_SEMAPHORE
static int sdl_wait (SDLAudioState *s, const char *forfn)
{
if (SDL_SemWait (s->sem)) {
sdl_logerr ("SDL_SemWait for %s failed\n", forfn);
return -1;
}
return 0;
}
#endif
static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
{
if (sdl_unlock (s, forfn)) {
return -1;
}
return sdl_post (s, forfn);
}
static int aud_to_sdlfmt (audfmt_e fmt)
{
switch (fmt) {
case AUDIO_FORMAT_S8:
case AUD_FMT_S8:
return AUDIO_S8;
case AUDIO_FORMAT_U8:
case AUD_FMT_U8:
return AUDIO_U8;
case AUDIO_FORMAT_S16:
case AUD_FMT_S16:
return AUDIO_S16LSB;
case AUDIO_FORMAT_U16:
case AUD_FMT_U16:
return AUDIO_U16LSB;
default:
@@ -88,37 +161,37 @@ static int aud_to_sdlfmt (AudioFormat fmt)
}
}
static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness)
static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness)
{
switch (sdlfmt) {
case AUDIO_S8:
*endianness = 0;
*fmt = AUDIO_FORMAT_S8;
*fmt = AUD_FMT_S8;
break;
case AUDIO_U8:
*endianness = 0;
*fmt = AUDIO_FORMAT_U8;
*fmt = AUD_FMT_U8;
break;
case AUDIO_S16LSB:
*endianness = 0;
*fmt = AUDIO_FORMAT_S16;
*fmt = AUD_FMT_S16;
break;
case AUDIO_U16LSB:
*endianness = 0;
*fmt = AUDIO_FORMAT_U16;
*fmt = AUD_FMT_U16;
break;
case AUDIO_S16MSB:
*endianness = 1;
*fmt = AUDIO_FORMAT_S16;
*fmt = AUD_FMT_S16;
break;
case AUDIO_U16MSB:
*endianness = 1;
*fmt = AUDIO_FORMAT_U16;
*fmt = AUD_FMT_U16;
break;
default:
@@ -170,9 +243,9 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
static void sdl_close (SDLAudioState *s)
{
if (s->initialized) {
SDL_LockAudio();
sdl_lock (s, "sdl_close");
s->exit = 1;
SDL_UnlockAudio();
sdl_unlock_and_post (s, "sdl_close");
SDL_PauseAudio (1);
SDL_CloseAudio ();
s->initialized = 0;
@@ -185,36 +258,76 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
SDLAudioState *s = &glob_sdl;
HWVoiceOut *hw = &sdl->hw;
int samples = len >> hw->info.shift;
int to_mix, decr;
if (s->exit || !sdl->live) {
if (s->exit) {
return;
}
/* dolog ("in callback samples=%d live=%d\n", samples, sdl->live); */
while (samples) {
int to_mix, decr;
to_mix = audio_MIN(samples, sdl->live);
decr = to_mix;
while (to_mix) {
int chunk = audio_MIN(to_mix, hw->samples - hw->rpos);
struct st_sample *src = hw->mix_buf + hw->rpos;
/* dolog ("in callback samples=%d\n", samples); */
#if USE_SEMAPHORE
sdl_wait (s, "sdl_callback");
if (s->exit) {
return;
}
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
hw->clip(buf, src, chunk);
hw->rpos = (hw->rpos + chunk) % hw->samples;
to_mix -= chunk;
buf += chunk << hw->info.shift;
if (sdl_lock (s, "sdl_callback")) {
return;
}
if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) {
dolog ("sdl->live=%d hw->samples=%d\n",
sdl->live, hw->samples);
return;
}
if (!sdl->live) {
goto again;
}
#else
if (s->exit || !sdl->live) {
break;
}
#endif
/* dolog ("in callback live=%d\n", live); */
to_mix = audio_MIN (samples, sdl->live);
decr = to_mix;
while (to_mix) {
int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
struct st_sample *src = hw->mix_buf + hw->rpos;
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
hw->clip (buf, src, chunk);
#if USE_SEMAPHORE
sdl->rpos = (sdl->rpos + chunk) % hw->samples;
#else
hw->rpos = (hw->rpos + chunk) % hw->samples;
#endif
to_mix -= chunk;
buf += chunk << hw->info.shift;
}
samples -= decr;
sdl->live -= decr;
sdl->decr += decr;
#if USE_SEMAPHORE
again:
if (sdl_unlock (s, "sdl_callback")) {
return;
}
#endif
}
samples -= decr;
sdl->live -= decr;
sdl->decr += decr;
/* dolog ("done len=%d\n", len); */
#if (SDL_MAJOR_VERSION >= 2)
/* SDL2 does not clear the remaining buffer for us, so do it on our own */
if (samples) {
memset(buf, 0, samples << hw->info.shift);
}
#endif
}
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
@@ -226,8 +339,11 @@ static int sdl_run_out (HWVoiceOut *hw, int live)
{
int decr;
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
SDLAudioState *s = &glob_sdl;
SDL_LockAudio();
if (sdl_lock (s, "sdl_run_out")) {
return 0;
}
if (sdl->decr > live) {
ldebug ("sdl->decr %d live %d sdl->live %d\n",
@@ -239,10 +355,19 @@ static int sdl_run_out (HWVoiceOut *hw, int live)
decr = audio_MIN (sdl->decr, live);
sdl->decr -= decr;
#if USE_SEMAPHORE
sdl->live = live - decr;
hw->rpos = sdl->rpos;
#else
sdl->live = live;
#endif
SDL_UnlockAudio();
if (sdl->live > 0) {
sdl_unlock_and_post (s, "sdl_run_out");
}
else {
sdl_unlock (s, "sdl_run_out");
}
return decr;
}
@@ -261,13 +386,13 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
SDL_AudioSpec req, obt;
int endianness;
int err;
AudioFormat effective_fmt;
audfmt_e effective_fmt;
struct audsettings obt_as;
req.freq = as->freq;
req.format = aud_to_sdlfmt (as->fmt);
req.channels = as->nchannels;
req.samples = audio_buffer_samples(s->dev->u.sdl.out, as, 11610);
req.samples = conf.nb_samples;
req.callback = sdl_callback;
req.userdata = sdl;
@@ -311,7 +436,7 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
return 0;
}
static void *sdl_audio_init(Audiodev *dev)
static void *sdl_audio_init (void)
{
SDLAudioState *s = &glob_sdl;
if (s->driver_created) {
@@ -324,8 +449,24 @@ static void *sdl_audio_init(Audiodev *dev)
return NULL;
}
#if USE_SEMAPHORE
s->mutex = SDL_CreateMutex ();
if (!s->mutex) {
sdl_logerr ("Failed to create SDL mutex\n");
SDL_QuitSubSystem (SDL_INIT_AUDIO);
return NULL;
}
s->sem = SDL_CreateSemaphore (0);
if (!s->sem) {
sdl_logerr ("Failed to create SDL semaphore\n");
SDL_DestroyMutex (s->mutex);
SDL_QuitSubSystem (SDL_INIT_AUDIO);
return NULL;
}
#endif
s->driver_created = true;
s->dev = dev;
return s;
}
@@ -333,11 +474,24 @@ static void sdl_audio_fini (void *opaque)
{
SDLAudioState *s = opaque;
sdl_close (s);
#if USE_SEMAPHORE
SDL_DestroySemaphore (s->sem);
SDL_DestroyMutex (s->mutex);
#endif
SDL_QuitSubSystem (SDL_INIT_AUDIO);
s->driver_created = false;
s->dev = NULL;
}
static struct audio_option sdl_options[] = {
{
.name = "SAMPLES",
.tag = AUD_OPT_INT,
.valp = &conf.nb_samples,
.descr = "Size of SDL buffer in samples"
},
{ /* End of list */ }
};
static struct audio_pcm_ops sdl_pcm_ops = {
.init_out = sdl_init_out,
.fini_out = sdl_fini_out,
@@ -346,9 +500,10 @@ static struct audio_pcm_ops sdl_pcm_ops = {
.ctl_out = sdl_ctl_out,
};
static struct audio_driver sdl_audio_driver = {
struct audio_driver sdl_audio_driver = {
.name = "sdl",
.descr = "SDL http://www.libsdl.org",
.options = sdl_options,
.init = sdl_audio_init,
.fini = sdl_audio_fini,
.pcm_ops = &sdl_pcm_ops,
@@ -358,9 +513,3 @@ static struct audio_driver sdl_audio_driver = {
.voice_size_out = sizeof (SDLVoiceOut),
.voice_size_in = 0
};
static void register_audio_sdl(void)
{
audio_driver_register(&sdl_audio_driver);
}
type_init(register_audio_sdl);
+9 -11
View File
@@ -20,7 +20,6 @@
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/host-utils.h"
#include "qemu/module.h"
#include "qemu/error-report.h"
#include "qemu/timer.h"
#include "ui/qemu-spice.h"
@@ -78,7 +77,7 @@ static const SpiceRecordInterface record_sif = {
.base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
};
static void *spice_audio_init(Audiodev *dev)
static void *spice_audio_init (void)
{
if (!using_spice) {
return NULL;
@@ -131,7 +130,7 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ;
#endif
settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
settings.fmt = AUDIO_FORMAT_S16;
settings.fmt = AUD_FMT_S16;
settings.endianness = AUDIO_HOST_ENDIANNESS;
audio_pcm_init_info (&hw->info, &settings);
@@ -259,7 +258,7 @@ static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
settings.freq = SPICE_INTERFACE_RECORD_FREQ;
#endif
settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
settings.fmt = AUDIO_FORMAT_S16;
settings.fmt = AUD_FMT_S16;
settings.endianness = AUDIO_HOST_ENDIANNESS;
audio_pcm_init_info (&hw->info, &settings);
@@ -374,6 +373,10 @@ static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
return 0;
}
static struct audio_option audio_options[] = {
{ /* end of list */ },
};
static struct audio_pcm_ops audio_callbacks = {
.init_out = line_out_init,
.fini_out = line_out_fini,
@@ -388,9 +391,10 @@ static struct audio_pcm_ops audio_callbacks = {
.ctl_in = line_in_ctl,
};
static struct audio_driver spice_audio_driver = {
struct audio_driver spice_audio_driver = {
.name = "spice",
.descr = "spice audio driver",
.options = audio_options,
.init = spice_audio_init,
.fini = spice_audio_fini,
.pcm_ops = &audio_callbacks,
@@ -407,9 +411,3 @@ void qemu_spice_audio_init (void)
{
spice_audio_driver.can_be_default = 1;
}
static void register_audio_spice(void)
{
audio_driver_register(&spice_audio_driver);
}
type_init(register_audio_spice);
+2 -7
View File
@@ -1,6 +1,6 @@
# See docs/devel/tracing.txt for syntax documentation.
# alsaaudio.c
# audio/alsaaudio.c
alsa_revents(int revents) "revents = %d"
alsa_pollout(int i, int fd) "i = %d fd = %d"
alsa_set_handler(int events, int index, int fd, int err) "events=0x%x index=%d fd=%d err=%d"
@@ -12,11 +12,6 @@ alsa_resume_out(void) "Resuming suspended output stream"
alsa_resume_in(void) "Resuming suspended input stream"
alsa_no_frames(int state) "No frames available and ALSA state is %d"
# ossaudio.c
# audio/ossaudio.c
oss_version(int version) "OSS version = 0x%x"
oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d"
# audio.c
audio_timer_start(int interval) "interval %d ms"
audio_timer_stop(void) ""
audio_timer_delayed(int interval) "interval %d ms"
+58 -29
View File
@@ -21,12 +21,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qemu/host-utils.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "qapi/opts-visitor.h"
#include "audio.h"
#define AUDIO_CAP "wav"
@@ -40,6 +37,11 @@ typedef struct WAVVoiceOut {
int total_samples;
} WAVVoiceOut;
typedef struct {
struct audsettings settings;
const char *wav_path;
} WAVConf;
static int wav_run_out (HWVoiceOut *hw, int live)
{
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
@@ -110,30 +112,25 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
};
Audiodev *dev = drv_opaque;
AudiodevWavOptions *wopts = &dev->u.wav;
struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out);
const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav";
WAVConf *conf = drv_opaque;
struct audsettings wav_as = conf->settings;
stereo = wav_as.nchannels == 2;
switch (wav_as.fmt) {
case AUDIO_FORMAT_S8:
case AUDIO_FORMAT_U8:
case AUD_FMT_S8:
case AUD_FMT_U8:
bits16 = 0;
break;
case AUDIO_FORMAT_S16:
case AUDIO_FORMAT_U16:
case AUD_FMT_S16:
case AUD_FMT_U16:
bits16 = 1;
break;
case AUDIO_FORMAT_S32:
case AUDIO_FORMAT_U32:
case AUD_FMT_S32:
case AUD_FMT_U32:
dolog ("WAVE files can not handle 32bit formats\n");
return -1;
default:
abort();
}
hdr[34] = bits16 ? 0x10 : 0x08;
@@ -142,7 +139,7 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
audio_pcm_init_info (&hw->info, &wav_as);
hw->samples = 1024;
wav->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
if (!wav->pcm_buf) {
dolog ("Could not allocate buffer (%d bytes)\n",
hw->samples << hw->info.shift);
@@ -154,10 +151,10 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
le_store (hdr + 32, 1 << (bits16 + stereo), 2);
wav->f = fopen(wav_path, "wb");
wav->f = fopen (conf->wav_path, "wb");
if (!wav->f) {
dolog ("Failed to open wave file `%s'\nReason: %s\n",
wav_path, strerror(errno));
conf->wav_path, strerror (errno));
g_free (wav->pcm_buf);
wav->pcm_buf = NULL;
return -1;
@@ -225,17 +222,54 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
return 0;
}
static void *wav_audio_init(Audiodev *dev)
static WAVConf glob_conf = {
.settings.freq = 44100,
.settings.nchannels = 2,
.settings.fmt = AUD_FMT_S16,
.wav_path = "qemu.wav"
};
static void *wav_audio_init (void)
{
assert(dev->driver == AUDIODEV_DRIVER_WAV);
return dev;
WAVConf *conf = g_malloc(sizeof(WAVConf));
*conf = glob_conf;
return conf;
}
static void wav_audio_fini (void *opaque)
{
ldebug ("wav_fini");
g_free(opaque);
}
static struct audio_option wav_options[] = {
{
.name = "FREQUENCY",
.tag = AUD_OPT_INT,
.valp = &glob_conf.settings.freq,
.descr = "Frequency"
},
{
.name = "FORMAT",
.tag = AUD_OPT_FMT,
.valp = &glob_conf.settings.fmt,
.descr = "Format"
},
{
.name = "DAC_FIXED_CHANNELS",
.tag = AUD_OPT_INT,
.valp = &glob_conf.settings.nchannels,
.descr = "Number of channels (1 - mono, 2 - stereo)"
},
{
.name = "PATH",
.tag = AUD_OPT_STR,
.valp = &glob_conf.wav_path,
.descr = "Path to wave file"
},
{ /* End of list */ }
};
static struct audio_pcm_ops wav_pcm_ops = {
.init_out = wav_init_out,
.fini_out = wav_fini_out,
@@ -244,9 +278,10 @@ static struct audio_pcm_ops wav_pcm_ops = {
.ctl_out = wav_ctl_out,
};
static struct audio_driver wav_audio_driver = {
struct audio_driver wav_audio_driver = {
.name = "wav",
.descr = "WAV renderer http://wikipedia.org/wiki/WAV",
.options = wav_options,
.init = wav_audio_init,
.fini = wav_audio_fini,
.pcm_ops = &wav_pcm_ops,
@@ -256,9 +291,3 @@ static struct audio_driver wav_audio_driver = {
.voice_size_out = sizeof (WAVVoiceOut),
.voice_size_in = 0
};
static void register_audio_wav(void)
{
audio_driver_register(&wav_audio_driver);
}
type_init(register_audio_wav);
+23 -19
View File
@@ -1,7 +1,6 @@
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "monitor/monitor.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "audio.h"
@@ -38,29 +37,30 @@ static void wav_destroy (void *opaque)
uint8_t dlen[4];
uint32_t datalen = wav->bytes;
uint32_t rifflen = datalen + 36;
Monitor *mon = cur_mon;
if (wav->f) {
le_store (rlen, rifflen, 4);
le_store (dlen, datalen, 4);
if (fseek (wav->f, 4, SEEK_SET)) {
error_report("wav_destroy: rlen fseek failed: %s",
strerror(errno));
monitor_printf (mon, "wav_destroy: rlen fseek failed\nReason: %s\n",
strerror (errno));
goto doclose;
}
if (fwrite (rlen, 4, 1, wav->f) != 1) {
error_report("wav_destroy: rlen fwrite failed: %s",
strerror(errno));
monitor_printf (mon, "wav_destroy: rlen fwrite failed\nReason %s\n",
strerror (errno));
goto doclose;
}
if (fseek (wav->f, 32, SEEK_CUR)) {
error_report("wav_destroy: dlen fseek failed: %s",
strerror(errno));
monitor_printf (mon, "wav_destroy: dlen fseek failed\nReason %s\n",
strerror (errno));
goto doclose;
}
if (fwrite (dlen, 1, 4, wav->f) != 4) {
error_report("wav_destroy: dlen fwrite failed: %s",
strerror(errno));
monitor_printf (mon, "wav_destroy: dlen fwrite failed\nReason %s\n",
strerror (errno));
goto doclose;
}
doclose:
@@ -77,7 +77,8 @@ static void wav_capture (void *opaque, void *buf, int size)
WAVState *wav = opaque;
if (fwrite (buf, size, 1, wav->f) != 1) {
error_report("wav_capture: fwrite error: %s", strerror(errno));
monitor_printf (cur_mon, "wav_capture: fwrite error\nReason: %s",
strerror (errno));
}
wav->bytes += size;
}
@@ -108,6 +109,7 @@ static struct capture_ops wav_capture_ops = {
int wav_start_capture (CaptureState *s, const char *path, int freq,
int bits, int nchannels)
{
Monitor *mon = cur_mon;
WAVState *wav;
uint8_t hdr[] = {
0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
@@ -121,13 +123,13 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
CaptureVoiceOut *cap;
if (bits != 8 && bits != 16) {
error_report("incorrect bit count %d, must be 8 or 16", bits);
monitor_printf (mon, "incorrect bit count %d, must be 8 or 16\n", bits);
return -1;
}
if (nchannels != 1 && nchannels != 2) {
error_report("incorrect channel count %d, must be 1 or 2",
nchannels);
monitor_printf (mon, "incorrect channel count %d, must be 1 or 2\n",
nchannels);
return -1;
}
@@ -136,7 +138,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
as.freq = freq;
as.nchannels = 1 << stereo;
as.fmt = bits16 ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8;
as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
as.endianness = 0;
ops.notify = wav_notify;
@@ -155,8 +157,8 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
wav->f = fopen (path, "wb");
if (!wav->f) {
error_report("Failed to open wave file `%s': %s",
path, strerror(errno));
monitor_printf (mon, "Failed to open wave file `%s'\nReason: %s\n",
path, strerror (errno));
g_free (wav);
return -1;
}
@@ -167,13 +169,14 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
wav->freq = freq;
if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) {
error_report("Failed to write header: %s", strerror(errno));
monitor_printf (mon, "Failed to write header\nReason: %s\n",
strerror (errno));
goto error_free;
}
cap = AUD_add_capture (&as, &ops, wav);
if (!cap) {
error_report("Failed to add audio capture");
monitor_printf (mon, "Failed to add audio capture\n");
goto error_free;
}
@@ -185,7 +188,8 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
error_free:
g_free (wav->path);
if (fclose (wav->f)) {
error_report("Failed to close wave file: %s", strerror(errno));
monitor_printf (mon, "Failed to close wave file\nReason: %s\n",
strerror (errno));
}
g_free (wav);
return -1;
-7
View File
@@ -1,7 +0,0 @@
authz-obj-y += base.o
authz-obj-y += simple.o
authz-obj-y += list.o
authz-obj-y += listfile.o
authz-obj-$(CONFIG_AUTH_PAM) += pamacct.o
pamacct.o-libs = -lpam
-83
View File
@@ -1,83 +0,0 @@
/*
* QEMU authorization framework base class
*
* Copyright (c) 2018 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include "qemu/osdep.h"
#include "authz/base.h"
#include "qemu/module.h"
#include "trace.h"
bool qauthz_is_allowed(QAuthZ *authz,
const char *identity,
Error **errp)
{
QAuthZClass *cls = QAUTHZ_GET_CLASS(authz);
bool allowed;
allowed = cls->is_allowed(authz, identity, errp);
trace_qauthz_is_allowed(authz, identity, allowed);
return allowed;
}
bool qauthz_is_allowed_by_id(const char *authzid,
const char *identity,
Error **errp)
{
QAuthZ *authz;
Object *obj;
Object *container;
container = object_get_objects_root();
obj = object_resolve_path_component(container,
authzid);
if (!obj) {
error_setg(errp, "Cannot find QAuthZ object ID %s",
authzid);
return false;
}
if (!object_dynamic_cast(obj, TYPE_QAUTHZ)) {
error_setg(errp, "Object '%s' is not a QAuthZ subclass",
authzid);
return false;
}
authz = QAUTHZ(obj);
return qauthz_is_allowed(authz, identity, errp);
}
static const TypeInfo authz_info = {
.parent = TYPE_OBJECT,
.name = TYPE_QAUTHZ,
.instance_size = sizeof(QAuthZ),
.class_size = sizeof(QAuthZClass),
.abstract = true,
};
static void qauthz_register_types(void)
{
type_register_static(&authz_info);
}
type_init(qauthz_register_types)
-272
View File
@@ -1,272 +0,0 @@
/*
* QEMU access control list authorization driver
*
* Copyright (c) 2018 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include "qemu/osdep.h"
#include "authz/list.h"
#include "trace.h"
#include "qom/object_interfaces.h"
#include "qapi/qapi-visit-authz.h"
#include "qemu/module.h"
static bool qauthz_list_is_allowed(QAuthZ *authz,
const char *identity,
Error **errp)
{
QAuthZList *lauthz = QAUTHZ_LIST(authz);
QAuthZListRuleList *rules = lauthz->rules;
while (rules) {
QAuthZListRule *rule = rules->value;
QAuthZListFormat format = rule->has_format ? rule->format :
QAUTHZ_LIST_FORMAT_EXACT;
trace_qauthz_list_check_rule(authz, rule->match, identity,
format, rule->policy);
switch (format) {
case QAUTHZ_LIST_FORMAT_EXACT:
if (g_str_equal(rule->match, identity)) {
return rule->policy == QAUTHZ_LIST_POLICY_ALLOW;
}
break;
case QAUTHZ_LIST_FORMAT_GLOB:
if (g_pattern_match_simple(rule->match, identity)) {
return rule->policy == QAUTHZ_LIST_POLICY_ALLOW;
}
break;
default:
g_warn_if_reached();
return false;
}
rules = rules->next;
}
trace_qauthz_list_default_policy(authz, identity, lauthz->policy);
return lauthz->policy == QAUTHZ_LIST_POLICY_ALLOW;
}
static void
qauthz_list_prop_set_policy(Object *obj,
int value,
Error **errp G_GNUC_UNUSED)
{
QAuthZList *lauthz = QAUTHZ_LIST(obj);
lauthz->policy = value;
}
static int
qauthz_list_prop_get_policy(Object *obj,
Error **errp G_GNUC_UNUSED)
{
QAuthZList *lauthz = QAUTHZ_LIST(obj);
return lauthz->policy;
}
static void
qauthz_list_prop_get_rules(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
QAuthZList *lauthz = QAUTHZ_LIST(obj);
visit_type_QAuthZListRuleList(v, name, &lauthz->rules, errp);
}
static void
qauthz_list_prop_set_rules(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
QAuthZList *lauthz = QAUTHZ_LIST(obj);
QAuthZListRuleList *oldrules;
oldrules = lauthz->rules;
visit_type_QAuthZListRuleList(v, name, &lauthz->rules, errp);
qapi_free_QAuthZListRuleList(oldrules);
}
static void
qauthz_list_finalize(Object *obj)
{
QAuthZList *lauthz = QAUTHZ_LIST(obj);
qapi_free_QAuthZListRuleList(lauthz->rules);
}
static void
qauthz_list_class_init(ObjectClass *oc, void *data)
{
QAuthZClass *authz = QAUTHZ_CLASS(oc);
object_class_property_add_enum(oc, "policy",
"QAuthZListPolicy",
&QAuthZListPolicy_lookup,
qauthz_list_prop_get_policy,
qauthz_list_prop_set_policy,
NULL);
object_class_property_add(oc, "rules", "QAuthZListRule",
qauthz_list_prop_get_rules,
qauthz_list_prop_set_rules,
NULL, NULL, NULL);
authz->is_allowed = qauthz_list_is_allowed;
}
QAuthZList *qauthz_list_new(const char *id,
QAuthZListPolicy policy,
Error **errp)
{
return QAUTHZ_LIST(
object_new_with_props(TYPE_QAUTHZ_LIST,
object_get_objects_root(),
id, errp,
"policy", QAuthZListPolicy_str(policy),
NULL));
}
ssize_t qauthz_list_append_rule(QAuthZList *auth,
const char *match,
QAuthZListPolicy policy,
QAuthZListFormat format,
Error **errp)
{
QAuthZListRule *rule;
QAuthZListRuleList *rules, *tmp;
size_t i = 0;
rule = g_new0(QAuthZListRule, 1);
rule->policy = policy;
rule->match = g_strdup(match);
rule->format = format;
rule->has_format = true;
tmp = g_new0(QAuthZListRuleList, 1);
tmp->value = rule;
rules = auth->rules;
if (rules) {
while (rules->next) {
i++;
rules = rules->next;
}
rules->next = tmp;
return i + 1;
} else {
auth->rules = tmp;
return 0;
}
}
ssize_t qauthz_list_insert_rule(QAuthZList *auth,
const char *match,
QAuthZListPolicy policy,
QAuthZListFormat format,
size_t index,
Error **errp)
{
QAuthZListRule *rule;
QAuthZListRuleList *rules, *tmp;
size_t i = 0;
rule = g_new0(QAuthZListRule, 1);
rule->policy = policy;
rule->match = g_strdup(match);
rule->format = format;
rule->has_format = true;
tmp = g_new0(QAuthZListRuleList, 1);
tmp->value = rule;
rules = auth->rules;
if (rules && index > 0) {
while (rules->next && i < (index - 1)) {
i++;
rules = rules->next;
}
tmp->next = rules->next;
rules->next = tmp;
return i + 1;
} else {
tmp->next = auth->rules;
auth->rules = tmp;
return 0;
}
}
ssize_t qauthz_list_delete_rule(QAuthZList *auth, const char *match)
{
QAuthZListRule *rule;
QAuthZListRuleList *rules, *prev;
size_t i = 0;
prev = NULL;
rules = auth->rules;
while (rules) {
rule = rules->value;
if (g_str_equal(rule->match, match)) {
if (prev) {
prev->next = rules->next;
} else {
auth->rules = rules->next;
}
rules->next = NULL;
qapi_free_QAuthZListRuleList(rules);
return i;
}
prev = rules;
rules = rules->next;
i++;
}
return -1;
}
static const TypeInfo qauthz_list_info = {
.parent = TYPE_QAUTHZ,
.name = TYPE_QAUTHZ_LIST,
.instance_size = sizeof(QAuthZList),
.instance_finalize = qauthz_list_finalize,
.class_size = sizeof(QAuthZListClass),
.class_init = qauthz_list_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_USER_CREATABLE },
{ }
}
};
static void
qauthz_list_register_types(void)
{
type_register_static(&qauthz_list_info);
}
type_init(qauthz_list_register_types);
-284
View File
@@ -1,284 +0,0 @@
/*
* QEMU access control list file authorization driver
*
* Copyright (c) 2018 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include "qemu/osdep.h"
#include "authz/listfile.h"
#include "trace.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qemu/sockets.h"
#include "qemu/filemonitor.h"
#include "qom/object_interfaces.h"
#include "qapi/qapi-visit-authz.h"
#include "qapi/qmp/qjson.h"
#include "qapi/qmp/qobject.h"
#include "qapi/qmp/qerror.h"
#include "qapi/qobject-input-visitor.h"
static bool
qauthz_list_file_is_allowed(QAuthZ *authz,
const char *identity,
Error **errp)
{
QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(authz);
if (fauthz->list) {
return qauthz_is_allowed(fauthz->list, identity, errp);
}
return false;
}
static QAuthZ *
qauthz_list_file_load(QAuthZListFile *fauthz, Error **errp)
{
GError *err = NULL;
gchar *content = NULL;
gsize len;
QObject *obj = NULL;
QDict *pdict;
Visitor *v = NULL;
QAuthZ *ret = NULL;
trace_qauthz_list_file_load(fauthz, fauthz->filename);
if (!g_file_get_contents(fauthz->filename, &content, &len, &err)) {
error_setg(errp, "Unable to read '%s': %s",
fauthz->filename, err->message);
goto cleanup;
}
obj = qobject_from_json(content, errp);
if (!obj) {
goto cleanup;
}
pdict = qobject_to(QDict, obj);
if (!pdict) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "obj", "dict");
goto cleanup;
}
v = qobject_input_visitor_new(obj);
ret = (QAuthZ *)user_creatable_add_type(TYPE_QAUTHZ_LIST,
NULL, pdict, v, errp);
cleanup:
visit_free(v);
qobject_unref(obj);
if (err) {
g_error_free(err);
}
g_free(content);
return ret;
}
static void
qauthz_list_file_event(int64_t wd G_GNUC_UNUSED,
QFileMonitorEvent ev G_GNUC_UNUSED,
const char *name G_GNUC_UNUSED,
void *opaque)
{
QAuthZListFile *fauthz = opaque;
Error *err = NULL;
if (ev != QFILE_MONITOR_EVENT_MODIFIED &&
ev != QFILE_MONITOR_EVENT_CREATED) {
return;
}
object_unref(OBJECT(fauthz->list));
fauthz->list = qauthz_list_file_load(fauthz, &err);
trace_qauthz_list_file_refresh(fauthz,
fauthz->filename, fauthz->list ? 1 : 0);
if (!fauthz->list) {
error_report_err(err);
}
}
static void
qauthz_list_file_complete(UserCreatable *uc, Error **errp)
{
QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(uc);
gchar *dir = NULL, *file = NULL;
fauthz->list = qauthz_list_file_load(fauthz, errp);
if (!fauthz->refresh) {
return;
}
fauthz->file_monitor = qemu_file_monitor_new(errp);
if (!fauthz->file_monitor) {
return;
}
dir = g_path_get_dirname(fauthz->filename);
if (g_str_equal(dir, ".")) {
error_setg(errp, "Filename must be an absolute path");
goto cleanup;
}
file = g_path_get_basename(fauthz->filename);
if (g_str_equal(file, ".")) {
error_setg(errp, "Path has no trailing filename component");
goto cleanup;
}
fauthz->file_watch = qemu_file_monitor_add_watch(
fauthz->file_monitor, dir, file,
qauthz_list_file_event, fauthz, errp);
if (fauthz->file_watch < 0) {
goto cleanup;
}
cleanup:
g_free(file);
g_free(dir);
}
static void
qauthz_list_file_prop_set_filename(Object *obj,
const char *value,
Error **errp G_GNUC_UNUSED)
{
QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
g_free(fauthz->filename);
fauthz->filename = g_strdup(value);
}
static char *
qauthz_list_file_prop_get_filename(Object *obj,
Error **errp G_GNUC_UNUSED)
{
QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
return g_strdup(fauthz->filename);
}
static void
qauthz_list_file_prop_set_refresh(Object *obj,
bool value,
Error **errp G_GNUC_UNUSED)
{
QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
fauthz->refresh = value;
}
static bool
qauthz_list_file_prop_get_refresh(Object *obj,
Error **errp G_GNUC_UNUSED)
{
QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
return fauthz->refresh;
}
static void
qauthz_list_file_finalize(Object *obj)
{
QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj);
object_unref(OBJECT(fauthz->list));
g_free(fauthz->filename);
qemu_file_monitor_free(fauthz->file_monitor);
}
static void
qauthz_list_file_class_init(ObjectClass *oc, void *data)
{
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
QAuthZClass *authz = QAUTHZ_CLASS(oc);
ucc->complete = qauthz_list_file_complete;
object_class_property_add_str(oc, "filename",
qauthz_list_file_prop_get_filename,
qauthz_list_file_prop_set_filename,
NULL);
object_class_property_add_bool(oc, "refresh",
qauthz_list_file_prop_get_refresh,
qauthz_list_file_prop_set_refresh,
NULL);
authz->is_allowed = qauthz_list_file_is_allowed;
}
static void
qauthz_list_file_init(Object *obj)
{
QAuthZListFile *authz = QAUTHZ_LIST_FILE(obj);
authz->file_watch = -1;
#ifdef CONFIG_INOTIFY1
authz->refresh = TRUE;
#endif
}
QAuthZListFile *qauthz_list_file_new(const char *id,
const char *filename,
bool refresh,
Error **errp)
{
return QAUTHZ_LIST_FILE(
object_new_with_props(TYPE_QAUTHZ_LIST_FILE,
object_get_objects_root(),
id, errp,
"filename", filename,
"refresh", refresh ? "yes" : "no",
NULL));
}
static const TypeInfo qauthz_list_file_info = {
.parent = TYPE_QAUTHZ,
.name = TYPE_QAUTHZ_LIST_FILE,
.instance_init = qauthz_list_file_init,
.instance_size = sizeof(QAuthZListFile),
.instance_finalize = qauthz_list_file_finalize,
.class_size = sizeof(QAuthZListFileClass),
.class_init = qauthz_list_file_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_USER_CREATABLE },
{ }
}
};
static void
qauthz_list_file_register_types(void)
{
type_register_static(&qauthz_list_file_info);
}
type_init(qauthz_list_file_register_types);
-149
View File
@@ -1,149 +0,0 @@
/*
* QEMU PAM authorization driver
*
* Copyright (c) 2018 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include "qemu/osdep.h"
#include "authz/pamacct.h"
#include "trace.h"
#include "qemu/module.h"
#include "qom/object_interfaces.h"
#include <security/pam_appl.h>
static bool qauthz_pam_is_allowed(QAuthZ *authz,
const char *identity,
Error **errp)
{
QAuthZPAM *pauthz = QAUTHZ_PAM(authz);
const struct pam_conv pam_conversation = { 0 };
pam_handle_t *pamh = NULL;
int ret;
trace_qauthz_pam_check(authz, identity, pauthz->service);
ret = pam_start(pauthz->service,
identity,
&pam_conversation,
&pamh);
if (ret != PAM_SUCCESS) {
error_setg(errp, "Unable to start PAM transaction: %s",
pam_strerror(NULL, ret));
return false;
}
ret = pam_acct_mgmt(pamh, PAM_SILENT);
pam_end(pamh, ret);
if (ret != PAM_SUCCESS) {
error_setg(errp, "Unable to authorize user '%s': %s",
identity, pam_strerror(pamh, ret));
return false;
}
return true;
}
static void
qauthz_pam_prop_set_service(Object *obj,
const char *service,
Error **errp G_GNUC_UNUSED)
{
QAuthZPAM *pauthz = QAUTHZ_PAM(obj);
g_free(pauthz->service);
pauthz->service = g_strdup(service);
}
static char *
qauthz_pam_prop_get_service(Object *obj,
Error **errp G_GNUC_UNUSED)
{
QAuthZPAM *pauthz = QAUTHZ_PAM(obj);
return g_strdup(pauthz->service);
}
static void
qauthz_pam_complete(UserCreatable *uc, Error **errp)
{
}
static void
qauthz_pam_finalize(Object *obj)
{
QAuthZPAM *pauthz = QAUTHZ_PAM(obj);
g_free(pauthz->service);
}
static void
qauthz_pam_class_init(ObjectClass *oc, void *data)
{
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
QAuthZClass *authz = QAUTHZ_CLASS(oc);
ucc->complete = qauthz_pam_complete;
authz->is_allowed = qauthz_pam_is_allowed;
object_class_property_add_str(oc, "service",
qauthz_pam_prop_get_service,
qauthz_pam_prop_set_service,
NULL);
}
QAuthZPAM *qauthz_pam_new(const char *id,
const char *service,
Error **errp)
{
return QAUTHZ_PAM(
object_new_with_props(TYPE_QAUTHZ_PAM,
object_get_objects_root(),
id, errp,
"service", service,
NULL));
}
static const TypeInfo qauthz_pam_info = {
.parent = TYPE_QAUTHZ,
.name = TYPE_QAUTHZ_PAM,
.instance_size = sizeof(QAuthZPAM),
.instance_finalize = qauthz_pam_finalize,
.class_size = sizeof(QAuthZPAMClass),
.class_init = qauthz_pam_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_USER_CREATABLE },
{ }
}
};
static void
qauthz_pam_register_types(void)
{
type_register_static(&qauthz_pam_info);
}
type_init(qauthz_pam_register_types);
-116
View File
@@ -1,116 +0,0 @@
/*
* QEMU simple authorization driver
*
* Copyright (c) 2018 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
*/
#include "qemu/osdep.h"
#include "authz/simple.h"
#include "trace.h"
#include "qemu/module.h"
#include "qom/object_interfaces.h"
static bool qauthz_simple_is_allowed(QAuthZ *authz,
const char *identity,
Error **errp)
{
QAuthZSimple *sauthz = QAUTHZ_SIMPLE(authz);
trace_qauthz_simple_is_allowed(authz, sauthz->identity, identity);
return g_str_equal(identity, sauthz->identity);
}
static void
qauthz_simple_prop_set_identity(Object *obj,
const char *value,
Error **errp G_GNUC_UNUSED)
{
QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
g_free(sauthz->identity);
sauthz->identity = g_strdup(value);
}
static char *
qauthz_simple_prop_get_identity(Object *obj,
Error **errp G_GNUC_UNUSED)
{
QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
return g_strdup(sauthz->identity);
}
static void
qauthz_simple_finalize(Object *obj)
{
QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj);
g_free(sauthz->identity);
}
static void
qauthz_simple_class_init(ObjectClass *oc, void *data)
{
QAuthZClass *authz = QAUTHZ_CLASS(oc);
authz->is_allowed = qauthz_simple_is_allowed;
object_class_property_add_str(oc, "identity",
qauthz_simple_prop_get_identity,
qauthz_simple_prop_set_identity,
NULL);
}
QAuthZSimple *qauthz_simple_new(const char *id,
const char *identity,
Error **errp)
{
return QAUTHZ_SIMPLE(
object_new_with_props(TYPE_QAUTHZ_SIMPLE,
object_get_objects_root(),
id, errp,
"identity", identity,
NULL));
}
static const TypeInfo qauthz_simple_info = {
.parent = TYPE_QAUTHZ,
.name = TYPE_QAUTHZ_SIMPLE,
.instance_size = sizeof(QAuthZSimple),
.instance_finalize = qauthz_simple_finalize,
.class_size = sizeof(QAuthZSimpleClass),
.class_init = qauthz_simple_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_USER_CREATABLE },
{ }
}
};
static void
qauthz_simple_register_types(void)
{
type_register_static(&qauthz_simple_info);
}
type_init(qauthz_simple_register_types);
-18
View File
@@ -1,18 +0,0 @@
# See docs/devel/tracing.txt for syntax documentation.
# base.c
qauthz_is_allowed(void *authz, const char *identity, bool allowed) "AuthZ %p check identity=%s allowed=%d"
# simple.c
qauthz_simple_is_allowed(void *authz, const char *wantidentity, const char *gotidentity) "AuthZ simple %p check want identity=%s got identity=%s"
# list.c
qauthz_list_check_rule(void *authz, const char *identity, const char *rule, int format, int policy) "AuthZ list %p check rule=%s identity=%s format=%d policy=%d"
qauthz_list_default_policy(void *authz, const char *identity, int policy) "AuthZ list %p default identity=%s policy=%d"
# listfile.c
qauthz_list_file_load(void *authz, const char *filename) "AuthZ file %p load filename=%s"
qauthz_list_file_refresh(void *authz, const char *filename, int success) "AuthZ file %p load filename=%s success=%d"
# pamacct.c
qauthz_pam_check(void *authz, const char *identity, const char *service) "AuthZ PAM %p identity=%s service=%s"
+1 -10
View File
@@ -4,16 +4,7 @@ common-obj-$(CONFIG_POSIX) += rng-random.o
common-obj-$(CONFIG_TPM) += tpm.o
common-obj-y += hostmem.o hostmem-ram.o
common-obj-$(CONFIG_POSIX) += hostmem-file.o
common-obj-$(CONFIG_LINUX) += hostmem-file.o
common-obj-y += cryptodev.o
common-obj-y += cryptodev-builtin.o
ifeq ($(CONFIG_VIRTIO_CRYPTO),y)
common-obj-y += cryptodev-vhost.o
common-obj-$(CONFIG_VHOST_CRYPTO) += cryptodev-vhost-user.o
endif
common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_VIRTIO)) += vhost-user.o
common-obj-$(CONFIG_LINUX) += hostmem-memfd.o
-1
View File
@@ -78,7 +78,6 @@ static void cryptodev_builtin_init(
"cryptodev-builtin", NULL);
cc->info_str = g_strdup_printf("cryptodev-builtin0");
cc->queue_index = 0;
cc->type = CRYPTODEV_BACKEND_TYPE_BUILTIN;
backend->conf.peers.ccs[0] = cc;
backend->conf.crypto_services =
-380
View File
@@ -1,380 +0,0 @@
/*
* QEMU Cryptodev backend for QEMU cipher APIs
*
* Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
*
* Authors:
* Gonglei <arei.gonglei@huawei.com>
*
* 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 "hw/boards.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/error-report.h"
#include "hw/virtio/vhost-user.h"
#include "standard-headers/linux/virtio_crypto.h"
#include "sysemu/cryptodev-vhost.h"
#include "chardev/char-fe.h"
#include "sysemu/cryptodev-vhost-user.h"
/**
* @TYPE_CRYPTODEV_BACKEND_VHOST_USER:
* name of backend that uses vhost user server
*/
#define TYPE_CRYPTODEV_BACKEND_VHOST_USER "cryptodev-vhost-user"
#define CRYPTODEV_BACKEND_VHOST_USER(obj) \
OBJECT_CHECK(CryptoDevBackendVhostUser, \
(obj), TYPE_CRYPTODEV_BACKEND_VHOST_USER)
typedef struct CryptoDevBackendVhostUser {
CryptoDevBackend parent_obj;
VhostUserState vhost_user;
CharBackend chr;
char *chr_name;
bool opened;
CryptoDevBackendVhost *vhost_crypto[MAX_CRYPTO_QUEUE_NUM];
} CryptoDevBackendVhostUser;
static int
cryptodev_vhost_user_running(
CryptoDevBackendVhost *crypto)
{
return crypto ? 1 : 0;
}
CryptoDevBackendVhost *
cryptodev_vhost_user_get_vhost(
CryptoDevBackendClient *cc,
CryptoDevBackend *b,
uint16_t queue)
{
CryptoDevBackendVhostUser *s =
CRYPTODEV_BACKEND_VHOST_USER(b);
assert(cc->type == CRYPTODEV_BACKEND_TYPE_VHOST_USER);
assert(queue < MAX_CRYPTO_QUEUE_NUM);
return s->vhost_crypto[queue];
}
static void cryptodev_vhost_user_stop(int queues,
CryptoDevBackendVhostUser *s)
{
size_t i;
for (i = 0; i < queues; i++) {
if (!cryptodev_vhost_user_running(s->vhost_crypto[i])) {
continue;
}
cryptodev_vhost_cleanup(s->vhost_crypto[i]);
s->vhost_crypto[i] = NULL;
}
}
static int
cryptodev_vhost_user_start(int queues,
CryptoDevBackendVhostUser *s)
{
CryptoDevBackendVhostOptions options;
CryptoDevBackend *b = CRYPTODEV_BACKEND(s);
int max_queues;
size_t i;
for (i = 0; i < queues; i++) {
if (cryptodev_vhost_user_running(s->vhost_crypto[i])) {
continue;
}
options.opaque = &s->vhost_user;
options.backend_type = VHOST_BACKEND_TYPE_USER;
options.cc = b->conf.peers.ccs[i];
s->vhost_crypto[i] = cryptodev_vhost_init(&options);
if (!s->vhost_crypto[i]) {
error_report("failed to init vhost_crypto for queue %zu", i);
goto err;
}
if (i == 0) {
max_queues =
cryptodev_vhost_get_max_queues(s->vhost_crypto[i]);
if (queues > max_queues) {
error_report("you are asking more queues than supported: %d",
max_queues);
goto err;
}
}
}
return 0;
err:
cryptodev_vhost_user_stop(i + 1, s);
return -1;
}
static Chardev *
cryptodev_vhost_claim_chardev(CryptoDevBackendVhostUser *s,
Error **errp)
{
Chardev *chr;
if (s->chr_name == NULL) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
"chardev", "a valid character device");
return NULL;
}
chr = qemu_chr_find(s->chr_name);
if (chr == NULL) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
"Device '%s' not found", s->chr_name);
return NULL;
}
return chr;
}
static void cryptodev_vhost_user_event(void *opaque, int event)
{
CryptoDevBackendVhostUser *s = opaque;
CryptoDevBackend *b = CRYPTODEV_BACKEND(s);
int queues = b->conf.peers.queues;
assert(queues < MAX_CRYPTO_QUEUE_NUM);
switch (event) {
case CHR_EVENT_OPENED:
if (cryptodev_vhost_user_start(queues, s) < 0) {
exit(1);
}
b->ready = true;
break;
case CHR_EVENT_CLOSED:
b->ready = false;
cryptodev_vhost_user_stop(queues, s);
break;
}
}
static void cryptodev_vhost_user_init(
CryptoDevBackend *backend, Error **errp)
{
int queues = backend->conf.peers.queues;
size_t i;
Error *local_err = NULL;
Chardev *chr;
CryptoDevBackendClient *cc;
CryptoDevBackendVhostUser *s =
CRYPTODEV_BACKEND_VHOST_USER(backend);
chr = cryptodev_vhost_claim_chardev(s, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
s->opened = true;
for (i = 0; i < queues; i++) {
cc = cryptodev_backend_new_client(
"cryptodev-vhost-user", NULL);
cc->info_str = g_strdup_printf("cryptodev-vhost-user%zu to %s ",
i, chr->label);
cc->queue_index = i;
cc->type = CRYPTODEV_BACKEND_TYPE_VHOST_USER;
backend->conf.peers.ccs[i] = cc;
if (i == 0) {
if (!qemu_chr_fe_init(&s->chr, chr, &local_err)) {
error_propagate(errp, local_err);
return;
}
}
}
if (!vhost_user_init(&s->vhost_user, &s->chr, errp)) {
return;
}
qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
cryptodev_vhost_user_event, NULL, s, NULL, true);
backend->conf.crypto_services =
1u << VIRTIO_CRYPTO_SERVICE_CIPHER |
1u << VIRTIO_CRYPTO_SERVICE_HASH |
1u << VIRTIO_CRYPTO_SERVICE_MAC;
backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC;
backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1;
backend->conf.max_size = UINT64_MAX;
backend->conf.max_cipher_key_len = VHOST_USER_MAX_CIPHER_KEY_LEN;
backend->conf.max_auth_key_len = VHOST_USER_MAX_AUTH_KEY_LEN;
}
static int64_t cryptodev_vhost_user_sym_create_session(
CryptoDevBackend *backend,
CryptoDevBackendSymSessionInfo *sess_info,
uint32_t queue_index, Error **errp)
{
CryptoDevBackendClient *cc =
backend->conf.peers.ccs[queue_index];
CryptoDevBackendVhost *vhost_crypto;
uint64_t session_id = 0;
int ret;
vhost_crypto = cryptodev_vhost_user_get_vhost(cc, backend, queue_index);
if (vhost_crypto) {
struct vhost_dev *dev = &(vhost_crypto->dev);
ret = dev->vhost_ops->vhost_crypto_create_session(dev,
sess_info,
&session_id);
if (ret < 0) {
return -1;
} else {
return session_id;
}
}
return -1;
}
static int cryptodev_vhost_user_sym_close_session(
CryptoDevBackend *backend,
uint64_t session_id,
uint32_t queue_index, Error **errp)
{
CryptoDevBackendClient *cc =
backend->conf.peers.ccs[queue_index];
CryptoDevBackendVhost *vhost_crypto;
int ret;
vhost_crypto = cryptodev_vhost_user_get_vhost(cc, backend, queue_index);
if (vhost_crypto) {
struct vhost_dev *dev = &(vhost_crypto->dev);
ret = dev->vhost_ops->vhost_crypto_close_session(dev,
session_id);
if (ret < 0) {
return -1;
} else {
return 0;
}
}
return -1;
}
static void cryptodev_vhost_user_cleanup(
CryptoDevBackend *backend,
Error **errp)
{
CryptoDevBackendVhostUser *s =
CRYPTODEV_BACKEND_VHOST_USER(backend);
size_t i;
int queues = backend->conf.peers.queues;
CryptoDevBackendClient *cc;
cryptodev_vhost_user_stop(queues, s);
for (i = 0; i < queues; i++) {
cc = backend->conf.peers.ccs[i];
if (cc) {
cryptodev_backend_free_client(cc);
backend->conf.peers.ccs[i] = NULL;
}
}
vhost_user_cleanup(&s->vhost_user);
}
static void cryptodev_vhost_user_set_chardev(Object *obj,
const char *value, Error **errp)
{
CryptoDevBackendVhostUser *s =
CRYPTODEV_BACKEND_VHOST_USER(obj);
if (s->opened) {
error_setg(errp, QERR_PERMISSION_DENIED);
} else {
g_free(s->chr_name);
s->chr_name = g_strdup(value);
}
}
static char *
cryptodev_vhost_user_get_chardev(Object *obj, Error **errp)
{
CryptoDevBackendVhostUser *s =
CRYPTODEV_BACKEND_VHOST_USER(obj);
Chardev *chr = qemu_chr_fe_get_driver(&s->chr);
if (chr && chr->label) {
return g_strdup(chr->label);
}
return NULL;
}
static void cryptodev_vhost_user_instance_int(Object *obj)
{
object_property_add_str(obj, "chardev",
cryptodev_vhost_user_get_chardev,
cryptodev_vhost_user_set_chardev,
NULL);
}
static void cryptodev_vhost_user_finalize(Object *obj)
{
CryptoDevBackendVhostUser *s =
CRYPTODEV_BACKEND_VHOST_USER(obj);
qemu_chr_fe_deinit(&s->chr, false);
g_free(s->chr_name);
}
static void
cryptodev_vhost_user_class_init(ObjectClass *oc, void *data)
{
CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc);
bc->init = cryptodev_vhost_user_init;
bc->cleanup = cryptodev_vhost_user_cleanup;
bc->create_session = cryptodev_vhost_user_sym_create_session;
bc->close_session = cryptodev_vhost_user_sym_close_session;
bc->do_sym_op = NULL;
}
static const TypeInfo cryptodev_vhost_user_info = {
.name = TYPE_CRYPTODEV_BACKEND_VHOST_USER,
.parent = TYPE_CRYPTODEV_BACKEND,
.class_init = cryptodev_vhost_user_class_init,
.instance_init = cryptodev_vhost_user_instance_int,
.instance_finalize = cryptodev_vhost_user_finalize,
.instance_size = sizeof(CryptoDevBackendVhostUser),
};
static void
cryptodev_vhost_user_register_types(void)
{
type_register_static(&cryptodev_vhost_user_info);
}
type_init(cryptodev_vhost_user_register_types);
-347
View File
@@ -1,347 +0,0 @@
/*
* QEMU Cryptodev backend for QEMU cipher APIs
*
* Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
*
* Authors:
* Gonglei <arei.gonglei@huawei.com>
* Jay Zhou <jianjay.zhou@huawei.com>
*
* 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 "hw/virtio/virtio-bus.h"
#include "sysemu/cryptodev-vhost.h"
#ifdef CONFIG_VHOST_CRYPTO
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/error-report.h"
#include "hw/virtio/virtio-crypto.h"
#include "sysemu/cryptodev-vhost-user.h"
uint64_t
cryptodev_vhost_get_max_queues(
CryptoDevBackendVhost *crypto)
{
return crypto->dev.max_queues;
}
void cryptodev_vhost_cleanup(CryptoDevBackendVhost *crypto)
{
vhost_dev_cleanup(&crypto->dev);
g_free(crypto);
}
struct CryptoDevBackendVhost *
cryptodev_vhost_init(
CryptoDevBackendVhostOptions *options)
{
int r;
CryptoDevBackendVhost *crypto;
crypto = g_new(CryptoDevBackendVhost, 1);
crypto->dev.max_queues = 1;
crypto->dev.nvqs = 1;
crypto->dev.vqs = crypto->vqs;
crypto->cc = options->cc;
crypto->dev.protocol_features = 0;
crypto->backend = -1;
/* vhost-user needs vq_index to initiate a specific queue pair */
crypto->dev.vq_index = crypto->cc->queue_index * crypto->dev.nvqs;
r = vhost_dev_init(&crypto->dev, options->opaque, options->backend_type, 0);
if (r < 0) {
goto fail;
}
return crypto;
fail:
g_free(crypto);
return NULL;
}
static int
cryptodev_vhost_start_one(CryptoDevBackendVhost *crypto,
VirtIODevice *dev)
{
int r;
crypto->dev.nvqs = 1;
crypto->dev.vqs = crypto->vqs;
r = vhost_dev_enable_notifiers(&crypto->dev, dev);
if (r < 0) {
goto fail_notifiers;
}
r = vhost_dev_start(&crypto->dev, dev);
if (r < 0) {
goto fail_start;
}
return 0;
fail_start:
vhost_dev_disable_notifiers(&crypto->dev, dev);
fail_notifiers:
return r;
}
static void
cryptodev_vhost_stop_one(CryptoDevBackendVhost *crypto,
VirtIODevice *dev)
{
vhost_dev_stop(&crypto->dev, dev);
vhost_dev_disable_notifiers(&crypto->dev, dev);
}
CryptoDevBackendVhost *
cryptodev_get_vhost(CryptoDevBackendClient *cc,
CryptoDevBackend *b,
uint16_t queue)
{
CryptoDevBackendVhost *vhost_crypto = NULL;
if (!cc) {
return NULL;
}
switch (cc->type) {
#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX)
case CRYPTODEV_BACKEND_TYPE_VHOST_USER:
vhost_crypto = cryptodev_vhost_user_get_vhost(cc, b, queue);
break;
#endif
default:
break;
}
return vhost_crypto;
}
static void
cryptodev_vhost_set_vq_index(CryptoDevBackendVhost *crypto,
int vq_index)
{
crypto->dev.vq_index = vq_index;
}
static int
vhost_set_vring_enable(CryptoDevBackendClient *cc,
CryptoDevBackend *b,
uint16_t queue, int enable)
{
CryptoDevBackendVhost *crypto =
cryptodev_get_vhost(cc, b, queue);
const VhostOps *vhost_ops;
cc->vring_enable = enable;
if (!crypto) {
return 0;
}
vhost_ops = crypto->dev.vhost_ops;
if (vhost_ops->vhost_set_vring_enable) {
return vhost_ops->vhost_set_vring_enable(&crypto->dev, enable);
}
return 0;
}
int cryptodev_vhost_start(VirtIODevice *dev, int total_queues)
{
VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(dev);
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev)));
VirtioBusState *vbus = VIRTIO_BUS(qbus);
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus);
int r, e;
int i;
CryptoDevBackend *b = vcrypto->cryptodev;
CryptoDevBackendVhost *vhost_crypto;
CryptoDevBackendClient *cc;
if (!k->set_guest_notifiers) {
error_report("binding does not support guest notifiers");
return -ENOSYS;
}
for (i = 0; i < total_queues; i++) {
cc = b->conf.peers.ccs[i];
vhost_crypto = cryptodev_get_vhost(cc, b, i);
cryptodev_vhost_set_vq_index(vhost_crypto, i);
/* Suppress the masking guest notifiers on vhost user
* because vhost user doesn't interrupt masking/unmasking
* properly.
*/
if (cc->type == CRYPTODEV_BACKEND_TYPE_VHOST_USER) {
dev->use_guest_notifier_mask = false;
}
}
r = k->set_guest_notifiers(qbus->parent, total_queues, true);
if (r < 0) {
error_report("error binding guest notifier: %d", -r);
goto err;
}
for (i = 0; i < total_queues; i++) {
cc = b->conf.peers.ccs[i];
vhost_crypto = cryptodev_get_vhost(cc, b, i);
r = cryptodev_vhost_start_one(vhost_crypto, dev);
if (r < 0) {
goto err_start;
}
if (cc->vring_enable) {
/* restore vring enable state */
r = vhost_set_vring_enable(cc, b, i, cc->vring_enable);
if (r < 0) {
goto err_start;
}
}
}
return 0;
err_start:
while (--i >= 0) {
cc = b->conf.peers.ccs[i];
vhost_crypto = cryptodev_get_vhost(cc, b, i);
cryptodev_vhost_stop_one(vhost_crypto, dev);
}
e = k->set_guest_notifiers(qbus->parent, total_queues, false);
if (e < 0) {
error_report("vhost guest notifier cleanup failed: %d", e);
}
err:
return r;
}
void cryptodev_vhost_stop(VirtIODevice *dev, int total_queues)
{
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev)));
VirtioBusState *vbus = VIRTIO_BUS(qbus);
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus);
VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(dev);
CryptoDevBackend *b = vcrypto->cryptodev;
CryptoDevBackendVhost *vhost_crypto;
CryptoDevBackendClient *cc;
size_t i;
int r;
for (i = 0; i < total_queues; i++) {
cc = b->conf.peers.ccs[i];
vhost_crypto = cryptodev_get_vhost(cc, b, i);
cryptodev_vhost_stop_one(vhost_crypto, dev);
}
r = k->set_guest_notifiers(qbus->parent, total_queues, false);
if (r < 0) {
error_report("vhost guest notifier cleanup failed: %d", r);
}
assert(r >= 0);
}
void cryptodev_vhost_virtqueue_mask(VirtIODevice *dev,
int queue,
int idx, bool mask)
{
VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(dev);
CryptoDevBackend *b = vcrypto->cryptodev;
CryptoDevBackendVhost *vhost_crypto;
CryptoDevBackendClient *cc;
assert(queue < MAX_CRYPTO_QUEUE_NUM);
cc = b->conf.peers.ccs[queue];
vhost_crypto = cryptodev_get_vhost(cc, b, queue);
vhost_virtqueue_mask(&vhost_crypto->dev, dev, idx, mask);
}
bool cryptodev_vhost_virtqueue_pending(VirtIODevice *dev,
int queue, int idx)
{
VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(dev);
CryptoDevBackend *b = vcrypto->cryptodev;
CryptoDevBackendVhost *vhost_crypto;
CryptoDevBackendClient *cc;
assert(queue < MAX_CRYPTO_QUEUE_NUM);
cc = b->conf.peers.ccs[queue];
vhost_crypto = cryptodev_get_vhost(cc, b, queue);
return vhost_virtqueue_pending(&vhost_crypto->dev, idx);
}
#else
uint64_t
cryptodev_vhost_get_max_queues(CryptoDevBackendVhost *crypto)
{
return 0;
}
void cryptodev_vhost_cleanup(CryptoDevBackendVhost *crypto)
{
}
struct CryptoDevBackendVhost *
cryptodev_vhost_init(CryptoDevBackendVhostOptions *options)
{
return NULL;
}
CryptoDevBackendVhost *
cryptodev_get_vhost(CryptoDevBackendClient *cc,
CryptoDevBackend *b,
uint16_t queue)
{
return NULL;
}
int cryptodev_vhost_start(VirtIODevice *dev, int total_queues)
{
return -1;
}
void cryptodev_vhost_stop(VirtIODevice *dev, int total_queues)
{
}
void cryptodev_vhost_virtqueue_mask(VirtIODevice *dev,
int queue,
int idx, bool mask)
{
}
bool cryptodev_vhost_virtqueue_pending(VirtIODevice *dev,
int queue, int idx)
{
return false;
}
#endif
+3 -1
View File
@@ -26,6 +26,8 @@
#include "hw/boards.h"
#include "qapi/error.h"
#include "qapi/visitor.h"
#include "qapi-types.h"
#include "qapi-visit.h"
#include "qemu/config-file.h"
#include "qom/object_interfaces.h"
#include "hw/virtio/virtio-crypto.h"
@@ -213,7 +215,7 @@ bool cryptodev_backend_is_ready(CryptoDevBackend *backend)
}
static bool
cryptodev_backend_can_be_deleted(UserCreatable *uc)
cryptodev_backend_can_be_deleted(UserCreatable *uc, Error **errp)
{
return !cryptodev_backend_is_used(CRYPTODEV_BACKEND(uc));
}
+22 -132
View File
@@ -9,11 +9,9 @@
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/module.h"
#include "qemu-common.h"
#include "sysemu/hostmem.h"
#include "sysemu/sysemu.h"
#include "qom/object_interfaces.h"
@@ -33,21 +31,14 @@ typedef struct HostMemoryBackendFile HostMemoryBackendFile;
struct HostMemoryBackendFile {
HostMemoryBackend parent_obj;
bool share;
char *mem_path;
uint64_t align;
bool discard_data;
bool is_pmem;
};
static void
file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
{
#ifndef CONFIG_POSIX
error_setg(errp, "backend '%s' not supported on this host",
object_get_typename(OBJECT(backend)));
#else
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend);
gchar *name;
if (!backend->size) {
error_setg(errp, "can't create backend with size 0");
@@ -57,38 +48,19 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
error_setg(errp, "mem-path property not set");
return;
}
/*
* Verify pmem file size since starting a guest with an incorrect size
* leads to confusing failures inside the guest.
*/
if (fb->is_pmem) {
Error *local_err = NULL;
uint64_t size;
size = qemu_get_pmem_size(fb->mem_path, &local_err);
if (!size) {
error_propagate(errp, local_err);
return;
}
if (backend->size > size) {
error_setg(errp, "size property %" PRIu64 " is larger than "
"pmem file \"%s\" size %" PRIu64, backend->size,
fb->mem_path, size);
return;
}
#ifndef CONFIG_LINUX
error_setg(errp, "-mem-path not supported on this host");
#else
if (!host_memory_backend_mr_inited(backend)) {
gchar *path;
backend->force_prealloc = mem_prealloc;
path = object_get_canonical_path(OBJECT(backend));
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
path,
backend->size, fb->share,
fb->mem_path, errp);
g_free(path);
}
backend->force_prealloc = mem_prealloc;
name = host_memory_backend_get_name(backend);
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
name,
backend->size, fb->align,
(backend->share ? RAM_SHARED : 0) |
(fb->is_pmem ? RAM_PMEM : 0),
fb->mem_path, errp);
g_free(name);
#endif
}
@@ -105,104 +77,30 @@ static void set_mem_path(Object *o, const char *str, Error **errp)
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
if (host_memory_backend_mr_inited(backend)) {
error_setg(errp, "cannot change property 'mem-path' of %s",
object_get_typename(o));
error_setg(errp, "cannot change property value");
return;
}
g_free(fb->mem_path);
fb->mem_path = g_strdup(str);
}
static bool file_memory_backend_get_discard_data(Object *o, Error **errp)
{
return MEMORY_BACKEND_FILE(o)->discard_data;
}
static void file_memory_backend_set_discard_data(Object *o, bool value,
Error **errp)
{
MEMORY_BACKEND_FILE(o)->discard_data = value;
}
static void file_memory_backend_get_align(Object *o, Visitor *v,
const char *name, void *opaque,
Error **errp)
static bool file_memory_backend_get_share(Object *o, Error **errp)
{
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
uint64_t val = fb->align;
visit_type_size(v, name, &val, errp);
return fb->share;
}
static void file_memory_backend_set_align(Object *o, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(o);
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
Error *local_err = NULL;
uint64_t val;
if (host_memory_backend_mr_inited(backend)) {
error_setg(&local_err, "cannot change property '%s' of %s",
name, object_get_typename(o));
goto out;
}
visit_type_size(v, name, &val, &local_err);
if (local_err) {
goto out;
}
fb->align = val;
out:
error_propagate(errp, local_err);
}
static bool file_memory_backend_get_pmem(Object *o, Error **errp)
{
return MEMORY_BACKEND_FILE(o)->is_pmem;
}
static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp)
static void file_memory_backend_set_share(Object *o, bool value, Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(o);
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
if (host_memory_backend_mr_inited(backend)) {
error_setg(errp, "cannot change property 'pmem' of %s.",
object_get_typename(o));
error_setg(errp, "cannot change property value");
return;
}
#ifndef CONFIG_LIBPMEM
if (value) {
Error *local_err = NULL;
error_setg(&local_err,
"Lack of libpmem support while setting the 'pmem=on'"
" of %s. We can't ensure data persistence.",
object_get_typename(o));
error_propagate(errp, local_err);
return;
}
#endif
fb->is_pmem = value;
}
static void file_backend_unparent(Object *obj)
{
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(obj);
if (host_memory_backend_mr_inited(backend) && fb->discard_data) {
void *ptr = memory_region_get_ram_ptr(&backend->mr);
uint64_t sz = memory_region_size(&backend->mr);
qemu_madvise(ptr, sz, QEMU_MADV_REMOVE);
}
fb->share = value;
}
static void
@@ -211,21 +109,13 @@ file_backend_class_init(ObjectClass *oc, void *data)
HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc);
bc->alloc = file_backend_memory_alloc;
oc->unparent = file_backend_unparent;
object_class_property_add_bool(oc, "discard-data",
file_memory_backend_get_discard_data, file_memory_backend_set_discard_data,
object_class_property_add_bool(oc, "share",
file_memory_backend_get_share, file_memory_backend_set_share,
&error_abort);
object_class_property_add_str(oc, "mem-path",
get_mem_path, set_mem_path,
&error_abort);
object_class_property_add(oc, "align", "int",
file_memory_backend_get_align,
file_memory_backend_set_align,
NULL, NULL, &error_abort);
object_class_property_add_bool(oc, "pmem",
file_memory_backend_get_pmem, file_memory_backend_set_pmem,
&error_abort);
}
static void file_backend_instance_finalize(Object *o)
-182
View File
@@ -1,182 +0,0 @@
/*
* QEMU host memfd memory backend
*
* Copyright (C) 2018 Red Hat Inc
*
* Authors:
* Marc-André Lureau <marcandre.lureau@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "sysemu/hostmem.h"
#include "sysemu/sysemu.h"
#include "qom/object_interfaces.h"
#include "qemu/memfd.h"
#include "qemu/module.h"
#include "qapi/error.h"
#define TYPE_MEMORY_BACKEND_MEMFD "memory-backend-memfd"
#define MEMORY_BACKEND_MEMFD(obj) \
OBJECT_CHECK(HostMemoryBackendMemfd, (obj), TYPE_MEMORY_BACKEND_MEMFD)
typedef struct HostMemoryBackendMemfd HostMemoryBackendMemfd;
struct HostMemoryBackendMemfd {
HostMemoryBackend parent_obj;
bool hugetlb;
uint64_t hugetlbsize;
bool seal;
};
static void
memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
{
HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(backend);
char *name;
int fd;
if (!backend->size) {
error_setg(errp, "can't create backend with size 0");
return;
}
backend->force_prealloc = mem_prealloc;
fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size,
m->hugetlb, m->hugetlbsize, m->seal ?
F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL : 0,
errp);
if (fd == -1) {
return;
}
name = host_memory_backend_get_name(backend);
memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend),
name, backend->size,
backend->share, fd, errp);
g_free(name);
}
static bool
memfd_backend_get_hugetlb(Object *o, Error **errp)
{
return MEMORY_BACKEND_MEMFD(o)->hugetlb;
}
static void
memfd_backend_set_hugetlb(Object *o, bool value, Error **errp)
{
MEMORY_BACKEND_MEMFD(o)->hugetlb = value;
}
static void
memfd_backend_set_hugetlbsize(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(obj);
Error *local_err = NULL;
uint64_t value;
if (host_memory_backend_mr_inited(MEMORY_BACKEND(obj))) {
error_setg(&local_err, "cannot change property value");
goto out;
}
visit_type_size(v, name, &value, &local_err);
if (local_err) {
goto out;
}
if (!value) {
error_setg(&local_err, "Property '%s.%s' doesn't take value '%"
PRIu64 "'", object_get_typename(obj), name, value);
goto out;
}
m->hugetlbsize = value;
out:
error_propagate(errp, local_err);
}
static void
memfd_backend_get_hugetlbsize(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(obj);
uint64_t value = m->hugetlbsize;
visit_type_size(v, name, &value, errp);
}
static bool
memfd_backend_get_seal(Object *o, Error **errp)
{
return MEMORY_BACKEND_MEMFD(o)->seal;
}
static void
memfd_backend_set_seal(Object *o, bool value, Error **errp)
{
MEMORY_BACKEND_MEMFD(o)->seal = value;
}
static void
memfd_backend_instance_init(Object *obj)
{
HostMemoryBackendMemfd *m = MEMORY_BACKEND_MEMFD(obj);
/* default to sealed file */
m->seal = true;
MEMORY_BACKEND(m)->share = true;
}
static void
memfd_backend_class_init(ObjectClass *oc, void *data)
{
HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc);
bc->alloc = memfd_backend_memory_alloc;
if (qemu_memfd_check(MFD_HUGETLB)) {
object_class_property_add_bool(oc, "hugetlb",
memfd_backend_get_hugetlb,
memfd_backend_set_hugetlb,
&error_abort);
object_class_property_set_description(oc, "hugetlb",
"Use huge pages",
&error_abort);
object_class_property_add(oc, "hugetlbsize", "int",
memfd_backend_get_hugetlbsize,
memfd_backend_set_hugetlbsize,
NULL, NULL, &error_abort);
object_class_property_set_description(oc, "hugetlbsize",
"Huge pages size (ex: 2M, 1G)",
&error_abort);
}
object_class_property_add_bool(oc, "seal",
memfd_backend_get_seal,
memfd_backend_set_seal,
&error_abort);
object_class_property_set_description(oc, "seal",
"Seal growing & shrinking",
&error_abort);
}
static const TypeInfo memfd_backend_info = {
.name = TYPE_MEMORY_BACKEND_MEMFD,
.parent = TYPE_MEMORY_BACKEND,
.instance_init = memfd_backend_instance_init,
.class_init = memfd_backend_class_init,
.instance_size = sizeof(HostMemoryBackendMemfd),
};
static void register_types(void)
{
if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
type_register_static(&memfd_backend_info);
}
}
type_init(register_types);
+6 -7
View File
@@ -9,29 +9,28 @@
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "sysemu/hostmem.h"
#include "qapi/error.h"
#include "qemu/module.h"
#include "qom/object_interfaces.h"
#define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram"
static void
ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
{
char *name;
char *path;
if (!backend->size) {
error_setg(errp, "can't create backend with size 0");
return;
}
name = host_memory_backend_get_name(backend);
memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), name,
backend->size, backend->share, errp);
g_free(name);
path = object_get_canonical_path_component(OBJECT(backend));
memory_region_init_ram_nomigrate(&backend->mr, OBJECT(backend), path,
backend->size, errp);
g_free(path);
}
static void
+30 -106
View File
@@ -9,16 +9,15 @@
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "sysemu/hostmem.h"
#include "hw/boards.h"
#include "qapi/error.h"
#include "qapi/qapi-builtin-visit.h"
#include "qapi/visitor.h"
#include "qapi-types.h"
#include "qapi-visit.h"
#include "qemu/config-file.h"
#include "qom/object_interfaces.h"
#include "qemu/mmap-alloc.h"
#ifdef CONFIG_NUMA
#include <numaif.h>
@@ -28,16 +27,6 @@ QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_BIND != MPOL_BIND);
QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE);
#endif
char *
host_memory_backend_get_name(HostMemoryBackend *backend)
{
if (!backend->use_canonical_path) {
return object_get_canonical_path_component(OBJECT(backend));
}
return object_get_canonical_path(OBJECT(backend));
}
static void
host_memory_backend_get_size(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
@@ -57,8 +46,7 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name,
uint64_t value;
if (host_memory_backend_mr_inited(backend)) {
error_setg(&local_err, "cannot change property %s of %s ",
name, object_get_typename(obj));
error_setg(&local_err, "cannot change property value");
goto out;
}
@@ -67,9 +55,8 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name,
goto out;
}
if (!value) {
error_setg(&local_err,
"property '%s' of %s doesn't take value '%" PRIu64 "'",
name, object_get_typename(obj), value);
error_setg(&local_err, "Property '%s.%s' doesn't take value '%"
PRIu64 "'", object_get_typename(obj), name, value);
goto out;
}
backend->size = value;
@@ -88,7 +75,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
value = find_first_bit(backend->host_nodes, MAX_NODES);
if (value == MAX_NODES) {
goto ret;
return;
}
*node = g_malloc0(sizeof(**node));
@@ -106,7 +93,6 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name,
node = &(*node)->next;
} while (true);
ret:
visit_type_uint16List(v, name, &host_nodes, errp);
}
@@ -116,23 +102,14 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name,
{
#ifdef CONFIG_NUMA
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
uint16List *l, *host_nodes = NULL;
uint16List *l = NULL;
visit_type_uint16List(v, name, &host_nodes, errp);
visit_type_uint16List(v, name, &l, errp);
for (l = host_nodes; l; l = l->next) {
if (l->value >= MAX_NODES) {
error_setg(errp, "Invalid host-nodes value: %d", l->value);
goto out;
}
}
for (l = host_nodes; l; l = l->next) {
while (l) {
bitmap_set(backend->host_nodes, l->value, 1);
l = l->next;
}
out:
qapi_free_uint16List(host_nodes);
#else
error_setg(errp, "NUMA node binding are not supported by this QEMU");
#endif
@@ -222,7 +199,6 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
{
Error *local_err = NULL;
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
MachineState *ms = MACHINE(qdev_get_machine());
if (backend->force_prealloc) {
if (value) {
@@ -242,7 +218,7 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
void *ptr = memory_region_get_ram_ptr(&backend->mr);
uint64_t sz = memory_region_size(&backend->mr);
os_mem_prealloc(fd, ptr, sz, ms->smp.cpus, &local_err);
os_mem_prealloc(fd, ptr, sz, smp_cpus, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -261,11 +237,6 @@ static void host_memory_backend_init(Object *obj)
backend->prealloc = mem_prealloc;
}
static void host_memory_backend_post_init(Object *obj)
{
object_apply_compat_props(obj);
}
bool host_memory_backend_mr_inited(HostMemoryBackend *backend)
{
/*
@@ -275,7 +246,8 @@ bool host_memory_backend_mr_inited(HostMemoryBackend *backend)
return memory_region_size(&backend->mr) != 0;
}
MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend)
MemoryRegion *
host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp)
{
return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL;
}
@@ -290,29 +262,11 @@ bool host_memory_backend_is_mapped(HostMemoryBackend *backend)
return backend->is_mapped;
}
#ifdef __linux__
size_t host_memory_backend_pagesize(HostMemoryBackend *memdev)
{
Object *obj = OBJECT(memdev);
char *path = object_property_get_str(obj, "mem-path", NULL);
size_t pagesize = qemu_mempath_getpagesize(path);
g_free(path);
return pagesize;
}
#else
size_t host_memory_backend_pagesize(HostMemoryBackend *memdev)
{
return getpagesize();
}
#endif
static void
host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(uc);
HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc);
MachineState *ms = MACHINE(qdev_get_machine());
Error *local_err = NULL;
void *ptr;
uint64_t sz;
@@ -350,7 +304,7 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
return;
} else if (maxnode == 0 && backend->policy != MPOL_DEFAULT) {
error_setg(errp, "host-nodes must be set for policy %s",
HostMemPolicy_str(backend->policy));
HostMemPolicy_lookup[backend->policy]);
return;
}
@@ -377,7 +331,7 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
*/
if (backend->prealloc) {
os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz,
ms->smp.cpus, &local_err);
smp_cpus, &local_err);
if (local_err) {
goto out;
}
@@ -388,7 +342,7 @@ out:
}
static bool
host_memory_backend_can_be_deleted(UserCreatable *uc)
host_memory_backend_can_be_deleted(UserCreatable *uc, Error **errp)
{
if (host_memory_backend_is_mapped(MEMORY_BACKEND(uc))) {
return false;
@@ -397,39 +351,22 @@ host_memory_backend_can_be_deleted(UserCreatable *uc)
}
}
static bool host_memory_backend_get_share(Object *o, Error **errp)
static char *get_id(Object *o, Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(o);
return backend->share;
return g_strdup(backend->id);
}
static void host_memory_backend_set_share(Object *o, bool value, Error **errp)
static void set_id(Object *o, const char *str, Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(o);
if (host_memory_backend_mr_inited(backend)) {
if (backend->id) {
error_setg(errp, "cannot change property value");
return;
}
backend->share = value;
}
static bool
host_memory_backend_get_use_canonical_path(Object *obj, Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
return backend->use_canonical_path;
}
static void
host_memory_backend_set_use_canonical_path(Object *obj, bool value,
Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
backend->use_canonical_path = value;
backend->id = g_strdup(str);
}
static void
@@ -443,44 +380,31 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
object_class_property_add_bool(oc, "merge",
host_memory_backend_get_merge,
host_memory_backend_set_merge, &error_abort);
object_class_property_set_description(oc, "merge",
"Mark memory as mergeable", &error_abort);
object_class_property_add_bool(oc, "dump",
host_memory_backend_get_dump,
host_memory_backend_set_dump, &error_abort);
object_class_property_set_description(oc, "dump",
"Set to 'off' to exclude from core dump", &error_abort);
object_class_property_add_bool(oc, "prealloc",
host_memory_backend_get_prealloc,
host_memory_backend_set_prealloc, &error_abort);
object_class_property_set_description(oc, "prealloc",
"Preallocate memory", &error_abort);
object_class_property_add(oc, "size", "int",
host_memory_backend_get_size,
host_memory_backend_set_size,
NULL, NULL, &error_abort);
object_class_property_set_description(oc, "size",
"Size of the memory region (ex: 500M)", &error_abort);
object_class_property_add(oc, "host-nodes", "int",
host_memory_backend_get_host_nodes,
host_memory_backend_set_host_nodes,
NULL, NULL, &error_abort);
object_class_property_set_description(oc, "host-nodes",
"Binds memory to the list of NUMA host nodes", &error_abort);
object_class_property_add_enum(oc, "policy", "HostMemPolicy",
&HostMemPolicy_lookup,
HostMemPolicy_lookup,
host_memory_backend_get_policy,
host_memory_backend_set_policy, &error_abort);
object_class_property_set_description(oc, "policy",
"Set the NUMA policy", &error_abort);
object_class_property_add_bool(oc, "share",
host_memory_backend_get_share, host_memory_backend_set_share,
&error_abort);
object_class_property_set_description(oc, "share",
"Mark the memory as private to QEMU or shared", &error_abort);
object_class_property_add_bool(oc, "x-use-canonical-path-for-ramblock-id",
host_memory_backend_get_use_canonical_path,
host_memory_backend_set_use_canonical_path, &error_abort);
object_class_property_add_str(oc, "id", get_id, set_id, &error_abort);
}
static void host_memory_backend_finalize(Object *o)
{
HostMemoryBackend *backend = MEMORY_BACKEND(o);
g_free(backend->id);
}
static const TypeInfo host_memory_backend_info = {
@@ -491,7 +415,7 @@ static const TypeInfo host_memory_backend_info = {
.class_init = host_memory_backend_class_init,
.instance_size = sizeof(HostMemoryBackend),
.instance_init = host_memory_backend_init,
.instance_post_init = host_memory_backend_post_init,
.instance_finalize = host_memory_backend_finalize,
.interfaces = (InterfaceInfo[]) {
{ TYPE_USER_CREATABLE },
{ }
-1
View File
@@ -15,7 +15,6 @@
#include "chardev/char-fe.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/module.h"
#define TYPE_RNG_EGD "rng-egd"
#define RNG_EGD(obj) OBJECT_CHECK(RngEgd, (obj), TYPE_RNG_EGD)
+1 -2
View File
@@ -16,7 +16,6 @@
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
struct RngRandom
{
@@ -113,7 +112,7 @@ static void rng_random_init(Object *obj)
rng_random_set_filename,
NULL);
s->filename = g_strdup("/dev/urandom");
s->filename = g_strdup("/dev/random");
s->fd = -1;
}
-1
View File
@@ -14,7 +14,6 @@
#include "sysemu/rng.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/module.h"
#include "qom/object_interfaces.h"
void rng_backend_request_entropy(RngBackend *s, size_t size,
+106 -116
View File
@@ -15,194 +15,184 @@
#include "qemu/osdep.h"
#include "sysemu/tpm_backend.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "sysemu/tpm.h"
#include "qemu/thread.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "block/thread-pool.h"
#include "qemu/error-report.h"
static void tpm_backend_request_completed(void *opaque, int ret)
{
TPMBackend *s = TPM_BACKEND(opaque);
TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif);
tic->request_completed(s->tpmif, ret);
/* no need for atomic, as long the BQL is taken */
s->cmd = NULL;
object_unref(OBJECT(s));
}
static int tpm_backend_worker_thread(gpointer data)
{
TPMBackend *s = TPM_BACKEND(data);
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
Error *err = NULL;
k->handle_request(s, s->cmd, &err);
if (err) {
error_report_err(err);
return -1;
}
return 0;
}
void tpm_backend_finish_sync(TPMBackend *s)
{
while (s->cmd) {
aio_poll(qemu_get_aio_context(), true);
}
}
#include "sysemu/tpm_backend_int.h"
enum TpmType tpm_backend_get_type(TPMBackend *s)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
return k->type;
return k->ops->type;
}
int tpm_backend_init(TPMBackend *s, TPMIf *tpmif, Error **errp)
const char *tpm_backend_get_desc(TPMBackend *s)
{
if (s->tpmif) {
error_setg(errp, "TPM backend '%s' is already initialized", s->id);
return -1;
}
s->tpmif = tpmif;
object_ref(OBJECT(tpmif));
s->had_startup_error = false;
return 0;
}
int tpm_backend_startup_tpm(TPMBackend *s, size_t buffersize)
{
int res = 0;
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
/* terminate a running TPM */
tpm_backend_finish_sync(s);
return k->ops->desc();
}
res = k->startup_tpm ? k->startup_tpm(s, buffersize) : 0;
void tpm_backend_destroy(TPMBackend *s)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
s->had_startup_error = (res != 0);
k->ops->destroy(s);
}
return res;
int tpm_backend_init(TPMBackend *s, TPMState *state,
TPMRecvDataCB *datacb)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
return k->ops->init(s, state, datacb);
}
int tpm_backend_startup_tpm(TPMBackend *s)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
return k->ops->startup_tpm(s);
}
bool tpm_backend_had_startup_error(TPMBackend *s)
{
return s->had_startup_error;
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
return k->ops->had_startup_error(s);
}
void tpm_backend_deliver_request(TPMBackend *s, TPMBackendCmd *cmd)
size_t tpm_backend_realloc_buffer(TPMBackend *s, TPMSizedBuffer *sb)
{
ThreadPool *pool = aio_get_thread_pool(qemu_get_aio_context());
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
if (s->cmd != NULL) {
error_report("There is a TPM request pending");
return;
}
return k->ops->realloc_buffer(sb);
}
s->cmd = cmd;
object_ref(OBJECT(s));
thread_pool_submit_aio(pool, tpm_backend_worker_thread, s,
tpm_backend_request_completed, s);
void tpm_backend_deliver_request(TPMBackend *s)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
k->ops->deliver_request(s);
}
void tpm_backend_reset(TPMBackend *s)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
if (k->reset) {
k->reset(s);
}
tpm_backend_finish_sync(s);
s->had_startup_error = false;
k->ops->reset(s);
}
void tpm_backend_cancel_cmd(TPMBackend *s)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
k->cancel_cmd(s);
k->ops->cancel_cmd(s);
}
bool tpm_backend_get_tpm_established_flag(TPMBackend *s)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
return k->get_tpm_established_flag ?
k->get_tpm_established_flag(s) : false;
return k->ops->get_tpm_established_flag(s);
}
int tpm_backend_reset_tpm_established_flag(TPMBackend *s, uint8_t locty)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
return k->reset_tpm_established_flag ?
k->reset_tpm_established_flag(s, locty) : 0;
return k->ops->reset_tpm_established_flag(s, locty);
}
TPMVersion tpm_backend_get_tpm_version(TPMBackend *s)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
return k->get_tpm_version(s);
return k->ops->get_tpm_version(s);
}
size_t tpm_backend_get_buffer_size(TPMBackend *s)
{
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
return k->get_buffer_size(s);
}
TPMInfo *tpm_backend_query_tpm(TPMBackend *s)
{
TPMInfo *info = g_new0(TPMInfo, 1);
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif);
info->id = g_strdup(s->id);
info->model = tic->model;
info->options = k->get_tpm_options(s);
return info;
}
static void tpm_backend_instance_finalize(Object *obj)
static bool tpm_backend_prop_get_opened(Object *obj, Error **errp)
{
TPMBackend *s = TPM_BACKEND(obj);
object_unref(OBJECT(s->tpmif));
g_free(s->id);
return s->opened;
}
void tpm_backend_open(TPMBackend *s, Error **errp)
{
object_property_set_bool(OBJECT(s), true, "opened", errp);
}
static void tpm_backend_prop_set_opened(Object *obj, bool value, Error **errp)
{
TPMBackend *s = TPM_BACKEND(obj);
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
Error *local_err = NULL;
if (value == s->opened) {
return;
}
if (!value && s->opened) {
error_setg(errp, QERR_PERMISSION_DENIED);
return;
}
if (k->opened) {
k->opened(s, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
}
s->opened = true;
}
static void tpm_backend_instance_init(Object *obj)
{
object_property_add_bool(obj, "opened",
tpm_backend_prop_get_opened,
tpm_backend_prop_set_opened,
NULL);
}
void tpm_backend_thread_deliver_request(TPMBackendThread *tbt)
{
g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD, NULL);
}
void tpm_backend_thread_create(TPMBackendThread *tbt,
GFunc func, gpointer user_data)
{
if (!tbt->pool) {
tbt->pool = g_thread_pool_new(func, user_data, 1, TRUE, NULL);
g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL);
}
}
void tpm_backend_thread_end(TPMBackendThread *tbt)
{
if (tbt->pool) {
g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_END, NULL);
g_thread_pool_free(tbt->pool, FALSE, TRUE);
tbt->pool = NULL;
}
}
static const TypeInfo tpm_backend_info = {
.name = TYPE_TPM_BACKEND,
.parent = TYPE_OBJECT,
.instance_size = sizeof(TPMBackend),
.instance_finalize = tpm_backend_instance_finalize,
.instance_init = tpm_backend_instance_init,
.class_size = sizeof(TPMBackendClass),
.abstract = true,
};
static const TypeInfo tpm_if_info = {
.name = TYPE_TPM_IF,
.parent = TYPE_INTERFACE,
.class_size = sizeof(TPMIfClass),
};
static void register_types(void)
{
type_register_static(&tpm_backend_info);
type_register_static(&tpm_if_info);
}
type_init(register_types);
-209
View File
@@ -1,209 +0,0 @@
/*
* QEMU vhost-user backend
*
* Copyright (C) 2018 Red Hat Inc
*
* Authors:
* Marc-André Lureau <marcandre.lureau@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "hw/qdev.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/error-report.h"
#include "qom/object_interfaces.h"
#include "sysemu/vhost-user-backend.h"
#include "sysemu/kvm.h"
#include "io/channel-command.h"
#include "hw/virtio/virtio-bus.h"
static bool
ioeventfd_enabled(void)
{
return kvm_enabled() && kvm_eventfds_enabled();
}
int
vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev,
unsigned nvqs, Error **errp)
{
int ret;
assert(!b->vdev && vdev);
if (!ioeventfd_enabled()) {
error_setg(errp, "vhost initialization failed: requires kvm");
return -1;
}
if (!vhost_user_init(&b->vhost_user, &b->chr, errp)) {
return -1;
}
b->vdev = vdev;
b->dev.nvqs = nvqs;
b->dev.vqs = g_new(struct vhost_virtqueue, nvqs);
ret = vhost_dev_init(&b->dev, &b->vhost_user, VHOST_BACKEND_TYPE_USER, 0);
if (ret < 0) {
error_setg_errno(errp, -ret, "vhost initialization failed");
return -1;
}
return 0;
}
void
vhost_user_backend_start(VhostUserBackend *b)
{
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(b->vdev)));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
int ret, i ;
if (b->started) {
return;
}
if (!k->set_guest_notifiers) {
error_report("binding does not support guest notifiers");
return;
}
ret = vhost_dev_enable_notifiers(&b->dev, b->vdev);
if (ret < 0) {
return;
}
ret = k->set_guest_notifiers(qbus->parent, b->dev.nvqs, true);
if (ret < 0) {
error_report("Error binding guest notifier");
goto err_host_notifiers;
}
b->dev.acked_features = b->vdev->guest_features;
ret = vhost_dev_start(&b->dev, b->vdev);
if (ret < 0) {
error_report("Error start vhost dev");
goto err_guest_notifiers;
}
/* guest_notifier_mask/pending not used yet, so just unmask
* everything here. virtio-pci will do the right thing by
* enabling/disabling irqfd.
*/
for (i = 0; i < b->dev.nvqs; i++) {
vhost_virtqueue_mask(&b->dev, b->vdev,
b->dev.vq_index + i, false);
}
b->started = true;
return;
err_guest_notifiers:
k->set_guest_notifiers(qbus->parent, b->dev.nvqs, false);
err_host_notifiers:
vhost_dev_disable_notifiers(&b->dev, b->vdev);
}
void
vhost_user_backend_stop(VhostUserBackend *b)
{
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(b->vdev)));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
int ret = 0;
if (!b->started) {
return;
}
vhost_dev_stop(&b->dev, b->vdev);
if (k->set_guest_notifiers) {
ret = k->set_guest_notifiers(qbus->parent,
b->dev.nvqs, false);
if (ret < 0) {
error_report("vhost guest notifier cleanup failed: %d", ret);
}
}
assert(ret >= 0);
vhost_dev_disable_notifiers(&b->dev, b->vdev);
b->started = false;
}
static void set_chardev(Object *obj, const char *value, Error **errp)
{
VhostUserBackend *b = VHOST_USER_BACKEND(obj);
Chardev *chr;
if (b->completed) {
error_setg(errp, QERR_PERMISSION_DENIED);
return;
}
g_free(b->chr_name);
b->chr_name = g_strdup(value);
chr = qemu_chr_find(b->chr_name);
if (chr == NULL) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
"Chardev '%s' not found", b->chr_name);
return;
}
if (!qemu_chr_fe_init(&b->chr, chr, errp)) {
return;
}
b->completed = true;
/* could call vhost_dev_init() so early message can be exchanged */
}
static char *get_chardev(Object *obj, Error **errp)
{
VhostUserBackend *b = VHOST_USER_BACKEND(obj);
Chardev *chr = qemu_chr_fe_get_driver(&b->chr);
if (chr && chr->label) {
return g_strdup(chr->label);
}
return NULL;
}
static void vhost_user_backend_init(Object *obj)
{
object_property_add_str(obj, "chardev", get_chardev, set_chardev, NULL);
}
static void vhost_user_backend_finalize(Object *obj)
{
VhostUserBackend *b = VHOST_USER_BACKEND(obj);
g_free(b->dev.vqs);
g_free(b->chr_name);
vhost_user_cleanup(&b->vhost_user);
qemu_chr_fe_deinit(&b->chr, true);
}
static const TypeInfo vhost_user_backend_info = {
.name = TYPE_VHOST_USER_BACKEND,
.parent = TYPE_OBJECT,
.instance_size = sizeof(VhostUserBackend),
.instance_init = vhost_user_backend_init,
.instance_finalize = vhost_user_backend_finalize,
.class_size = sizeof(VhostUserBackendClass),
};
static void register_types(void)
{
type_register_static(&vhost_user_backend_info);
}
type_init(register_types);
+6 -12
View File
@@ -25,34 +25,28 @@
*/
#include "qemu/osdep.h"
#include "qemu/atomic.h"
#include "qemu-common.h"
#include "exec/cpu-common.h"
#include "sysemu/kvm.h"
#include "sysemu/balloon.h"
#include "trace-root.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-misc.h"
#include "qmp-commands.h"
#include "qapi/qmp/qerror.h"
#include "qapi/qmp/qjson.h"
static QEMUBalloonEvent *balloon_event_fn;
static QEMUBalloonStatus *balloon_stat_fn;
static void *balloon_opaque;
static int balloon_inhibit_count;
static bool balloon_inhibited;
bool qemu_balloon_is_inhibited(void)
{
return atomic_read(&balloon_inhibit_count) > 0;
return balloon_inhibited;
}
void qemu_balloon_inhibit(bool state)
{
if (state) {
atomic_inc(&balloon_inhibit_count);
} else {
atomic_dec(&balloon_inhibit_count);
}
assert(atomic_read(&balloon_inhibit_count) >= 0);
balloon_inhibited = state;
}
static bool have_balloon(Error **errp)
+707 -2308
View File
File diff suppressed because it is too large. Load diff
+11 -28
View File
@@ -1,29 +1,18 @@
block-obj-y += raw-format.o vmdk.o vpc.o
block-obj-$(CONFIG_QCOW1) += qcow.o
block-obj-$(CONFIG_VDI) += vdi.o
block-obj-$(CONFIG_CLOOP) += cloop.o
block-obj-$(CONFIG_BOCHS) += bochs.o
block-obj-$(CONFIG_VVFAT) += vvfat.o
block-obj-$(CONFIG_DMG) += dmg.o
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o qcow2-threads.o
block-obj-$(CONFIG_QED) += qed.o qed-l2-cache.o qed-table.o qed-cluster.o
block-obj-$(CONFIG_QED) += qed-check.o
block-obj-y += raw-format.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o dmg.o
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o
block-obj-y += qed.o qed-l2-cache.o qed-table.o qed-cluster.o
block-obj-y += qed-check.o
block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o
block-obj-y += quorum.o
block-obj-y += blkdebug.o blkverify.o blkreplay.o
block-obj-$(CONFIG_PARALLELS) += parallels.o
block-obj-y += blklogwrites.o
block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o
block-obj-y += block-backend.o snapshot.o qapi.o
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
block-obj-$(CONFIG_POSIX) += file-posix.o
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
block-obj-y += null.o mirror.o commit.o io.o create.o
block-obj-y += null.o mirror.o commit.o io.o
block-obj-y += throttle-groups.o
block-obj-$(CONFIG_LINUX) += nvme.o
block-obj-y += nbd.o
block-obj-$(CONFIG_SHEEPDOG) += sheepdog.o
block-obj-y += nbd.o nbd-client.o sheepdog.o
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o
block-obj-$(CONFIG_LIBNFS) += nfs.o
@@ -31,12 +20,11 @@ block-obj-$(CONFIG_CURL) += curl.o
block-obj-$(CONFIG_RBD) += rbd.o
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
block-obj-$(CONFIG_VXHS) += vxhs.o
block-obj-$(CONFIG_LIBSSH) += ssh.o
block-obj-$(CONFIG_LIBSSH2) += ssh.o
block-obj-y += accounting.o dirty-bitmap.o
block-obj-y += write-threshold.o
block-obj-y += backup.o
block-obj-$(CONFIG_REPLICATION) += replication.o
block-obj-y += throttle.o copy-on-read.o
block-obj-y += crypto.o
@@ -52,14 +40,9 @@ rbd.o-libs := $(RBD_LIBS)
gluster.o-cflags := $(GLUSTERFS_CFLAGS)
gluster.o-libs := $(GLUSTERFS_LIBS)
vxhs.o-libs := $(VXHS_LIBS)
ssh.o-cflags := $(LIBSSH_CFLAGS)
ssh.o-libs := $(LIBSSH_LIBS)
block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o
block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y)
ssh.o-cflags := $(LIBSSH2_CFLAGS)
ssh.o-libs := $(LIBSSH2_LIBS)
block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o
dmg-bz2.o-libs := $(BZIP2_LIBS)
block-obj-$(if $(CONFIG_LZFSE),m,n) += dmg-lzfse.o
dmg-lzfse.o-libs := $(LZFSE_LIBS)
qcow.o-libs := -lz
linux-aio.o-libs := -laio
parallels.o-cflags := $(LIBXML2_CFLAGS)
parallels.o-libs := $(LIBXML2_LIBS)
-91
View File
@@ -94,94 +94,6 @@ void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie,
cookie->type = type;
}
/* block_latency_histogram_compare_func:
* Compare @key with interval [@it[0], @it[1]).
* Return: -1 if @key < @it[0]
* 0 if @key in [@it[0], @it[1])
* +1 if @key >= @it[1]
*/
static int block_latency_histogram_compare_func(const void *key, const void *it)
{
uint64_t k = *(uint64_t *)key;
uint64_t a = ((uint64_t *)it)[0];
uint64_t b = ((uint64_t *)it)[1];
return k < a ? -1 : (k < b ? 0 : 1);
}
static void block_latency_histogram_account(BlockLatencyHistogram *hist,
int64_t latency_ns)
{
uint64_t *pos;
if (hist->bins == NULL) {
/* histogram disabled */
return;
}
if (latency_ns < hist->boundaries[0]) {
hist->bins[0]++;
return;
}
if (latency_ns >= hist->boundaries[hist->nbins - 2]) {
hist->bins[hist->nbins - 1]++;
return;
}
pos = bsearch(&latency_ns, hist->boundaries, hist->nbins - 2,
sizeof(hist->boundaries[0]),
block_latency_histogram_compare_func);
assert(pos != NULL);
hist->bins[pos - hist->boundaries + 1]++;
}
int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type,
uint64List *boundaries)
{
BlockLatencyHistogram *hist = &stats->latency_histogram[type];
uint64List *entry;
uint64_t *ptr;
uint64_t prev = 0;
int new_nbins = 1;
for (entry = boundaries; entry; entry = entry->next) {
if (entry->value <= prev) {
return -EINVAL;
}
new_nbins++;
prev = entry->value;
}
hist->nbins = new_nbins;
g_free(hist->boundaries);
hist->boundaries = g_new(uint64_t, hist->nbins - 1);
for (entry = boundaries, ptr = hist->boundaries; entry;
entry = entry->next, ptr++)
{
*ptr = entry->value;
}
g_free(hist->bins);
hist->bins = g_new0(uint64_t, hist->nbins);
return 0;
}
void block_latency_histograms_clear(BlockAcctStats *stats)
{
int i;
for (i = 0; i < BLOCK_MAX_IOTYPE; i++) {
BlockLatencyHistogram *hist = &stats->latency_histogram[i];
g_free(hist->bins);
g_free(hist->boundaries);
memset(hist, 0, sizeof(*hist));
}
}
static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
bool failed)
{
@@ -204,9 +116,6 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
stats->nr_ops[cookie->type]++;
}
block_latency_histogram_account(&stats->latency_histogram[cookie->type],
latency_ns);
if (!failed || stats->account_failed) {
stats->total_time_ns[cookie->type] += latency_ns;
stats->last_access_time_ns = time_ns;
+320 -327
View File
@@ -27,13 +27,7 @@
#include "qemu/error-report.h"
#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
typedef struct CowRequest {
int64_t start_byte;
int64_t end_byte;
QLIST_ENTRY(CowRequest) list;
CoQueue wait_queue; /* coroutines blocked on this request */
} CowRequest;
#define SLICE_TIME 100000000ULL /* ns */
typedef struct BackupBlockJob {
BlockJob common;
@@ -41,25 +35,18 @@ typedef struct BackupBlockJob {
/* bitmap for sync=incremental */
BdrvDirtyBitmap *sync_bitmap;
MirrorSyncMode sync_mode;
RateLimit limit;
BlockdevOnError on_source_error;
BlockdevOnError on_target_error;
CoRwlock flush_rwlock;
uint64_t len;
uint64_t bytes_read;
unsigned long *done_bitmap;
int64_t cluster_size;
bool compress;
NotifierWithReturn before_write;
QLIST_HEAD(, CowRequest) inflight_reqs;
HBitmap *copy_bitmap;
bool use_copy_range;
int64_t copy_range_size;
bool serialize_target_writes;
} BackupBlockJob;
static const BlockJobDriver backup_job_driver;
/* See if in-flight requests overlap and wait for them to complete */
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
int64_t start,
@@ -97,99 +84,19 @@ static void cow_request_end(CowRequest *req)
qemu_co_queue_restart_all(&req->wait_queue);
}
/* Copy range to target with a bounce buffer and return the bytes copied. If
* error occurred, return a negative error number */
static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
int64_t start,
int64_t end,
bool is_write_notifier,
bool *error_is_read,
void **bounce_buffer)
{
int ret;
BlockBackend *blk = job->common.blk;
int nbytes;
int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0;
assert(QEMU_IS_ALIGNED(start, job->cluster_size));
hbitmap_reset(job->copy_bitmap, start, job->cluster_size);
nbytes = MIN(job->cluster_size, job->len - start);
if (!*bounce_buffer) {
*bounce_buffer = blk_blockalign(blk, job->cluster_size);
}
ret = blk_co_pread(blk, start, nbytes, *bounce_buffer, read_flags);
if (ret < 0) {
trace_backup_do_cow_read_fail(job, start, ret);
if (error_is_read) {
*error_is_read = true;
}
goto fail;
}
if (buffer_is_zero(*bounce_buffer, nbytes)) {
ret = blk_co_pwrite_zeroes(job->target, start,
nbytes, write_flags | BDRV_REQ_MAY_UNMAP);
} else {
ret = blk_co_pwrite(job->target, start,
nbytes, *bounce_buffer, write_flags |
(job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0));
}
if (ret < 0) {
trace_backup_do_cow_write_fail(job, start, ret);
if (error_is_read) {
*error_is_read = false;
}
goto fail;
}
return nbytes;
fail:
hbitmap_set(job->copy_bitmap, start, job->cluster_size);
return ret;
}
/* Copy range to target and return the bytes copied. If error occurred, return a
* negative error number. */
static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job,
int64_t start,
int64_t end,
bool is_write_notifier)
{
int ret;
int nr_clusters;
BlockBackend *blk = job->common.blk;
int nbytes;
int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0;
assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size));
assert(QEMU_IS_ALIGNED(start, job->cluster_size));
nbytes = MIN(job->copy_range_size, end - start);
nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size);
hbitmap_reset(job->copy_bitmap, start, job->cluster_size * nr_clusters);
ret = blk_co_copy_range(blk, start, job->target, start, nbytes,
read_flags, write_flags);
if (ret < 0) {
trace_backup_do_cow_copy_range_fail(job, start, ret);
hbitmap_set(job->copy_bitmap, start, job->cluster_size * nr_clusters);
return ret;
}
return nbytes;
}
static int coroutine_fn backup_do_cow(BackupBlockJob *job,
int64_t offset, uint64_t bytes,
bool *error_is_read,
bool is_write_notifier)
{
BlockBackend *blk = job->common.blk;
CowRequest cow_request;
struct iovec iov;
QEMUIOVector bounce_qiov;
void *bounce_buffer = NULL;
int ret = 0;
int64_t start, end; /* bytes */
void *bounce_buffer = NULL;
int n; /* bytes */
qemu_co_rwlock_rdlock(&job->flush_rwlock);
@@ -201,47 +108,59 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
wait_for_overlapping_requests(job, start, end);
cow_request_begin(&cow_request, job, start, end);
while (start < end) {
int64_t dirty_end;
if (!hbitmap_get(job->copy_bitmap, start)) {
for (; start < end; start += job->cluster_size) {
if (test_bit(start / job->cluster_size, job->done_bitmap)) {
trace_backup_do_cow_skip(job, start);
start += job->cluster_size;
continue; /* already copied */
}
dirty_end = hbitmap_next_zero(job->copy_bitmap, start, (end - start));
if (dirty_end < 0) {
dirty_end = end;
}
trace_backup_do_cow_process(job, start);
if (job->use_copy_range) {
ret = backup_cow_with_offload(job, start, dirty_end,
is_write_notifier);
if (ret < 0) {
job->use_copy_range = false;
}
n = MIN(job->cluster_size, job->common.len - start);
if (!bounce_buffer) {
bounce_buffer = blk_blockalign(blk, job->cluster_size);
}
if (!job->use_copy_range) {
ret = backup_cow_with_bounce_buffer(job, start, dirty_end,
is_write_notifier,
error_is_read, &bounce_buffer);
iov.iov_base = bounce_buffer;
iov.iov_len = n;
qemu_iovec_init_external(&bounce_qiov, &iov, 1);
ret = blk_co_preadv(blk, start, bounce_qiov.size, &bounce_qiov,
is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0);
if (ret < 0) {
trace_backup_do_cow_read_fail(job, start, ret);
if (error_is_read) {
*error_is_read = true;
}
goto out;
}
if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
ret = blk_co_pwrite_zeroes(job->target, start,
bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
} else {
ret = blk_co_pwritev(job->target, start,
bounce_qiov.size, &bounce_qiov,
job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
}
if (ret < 0) {
break;
trace_backup_do_cow_write_fail(job, start, ret);
if (error_is_read) {
*error_is_read = false;
}
goto out;
}
set_bit(start / job->cluster_size, job->done_bitmap);
/* Publish progress, guest I/O counts as progress too. Note that the
* offset field is an opaque progress value, it is not a disk offset.
*/
start += ret;
job->bytes_read += ret;
job_progress_update(&job->common.job, ret);
ret = 0;
job->bytes_read += n;
job->common.offset += n;
}
out:
if (bounce_buffer) {
qemu_vfree(bounce_buffer);
}
@@ -269,12 +188,23 @@ static int coroutine_fn backup_before_write_notify(
return backup_do_cow(job, req->offset, req->bytes, NULL, true);
}
static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
if (speed < 0) {
error_setg(errp, QERR_INVALID_PARAMETER, "speed");
return;
}
ratelimit_set_speed(&s->limit, speed, SLICE_TIME);
}
static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
{
BdrvDirtyBitmap *bm;
BlockDriverState *bs = blk_bs(job->common.blk);
if (ret < 0) {
if (ret < 0 || block_job_is_cancelled(&job->common)) {
/* Merge the successor back into the parent, delete nothing. */
bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
assert(bm);
@@ -285,40 +215,43 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
}
}
static void backup_commit(Job *job)
static void backup_commit(BlockJob *job)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
if (s->sync_bitmap) {
backup_cleanup_sync_bitmap(s, 0);
}
}
static void backup_abort(Job *job)
static void backup_abort(BlockJob *job)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
if (s->sync_bitmap) {
backup_cleanup_sync_bitmap(s, -1);
}
}
static void backup_clean(Job *job)
static void backup_clean(BlockJob *job)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
assert(s->target);
blk_unref(s->target);
s->target = NULL;
}
if (s->copy_bitmap) {
hbitmap_free(s->copy_bitmap);
s->copy_bitmap = NULL;
}
static void backup_attached_aio_context(BlockJob *job, AioContext *aio_context)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
blk_set_aio_context(s->target, aio_context);
}
void backup_do_checkpoint(BlockJob *job, Error **errp)
{
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
int64_t len;
assert(block_job_driver(job) == &backup_job_driver);
assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP);
if (backup_job->sync_mode != MIRROR_SYNC_MODE_NONE) {
error_setg(errp, "The backup job only supports block checkpoint in"
@@ -326,7 +259,39 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
return;
}
hbitmap_set(backup_job->copy_bitmap, 0, backup_job->len);
len = DIV_ROUND_UP(backup_job->common.len, backup_job->cluster_size);
bitmap_zero(backup_job->done_bitmap, len);
}
void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset,
uint64_t bytes)
{
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
int64_t start, end;
assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP);
start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size);
end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
wait_for_overlapping_requests(backup_job, start, end);
}
void backup_cow_request_begin(CowRequest *req, BlockJob *job,
int64_t offset, uint64_t bytes)
{
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
int64_t start, end;
assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP);
start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size);
end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
cow_request_begin(req, backup_job, start, end);
}
void backup_cow_request_end(CowRequest *req)
{
cow_request_end(req);
}
static void backup_drain(BlockJob *job)
@@ -356,192 +321,217 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job,
}
}
typedef struct {
int ret;
} BackupCompleteData;
static void backup_complete(BlockJob *job, void *opaque)
{
BackupCompleteData *data = opaque;
block_job_completed(job, data->ret);
g_free(data);
}
static bool coroutine_fn yield_and_check(BackupBlockJob *job)
{
uint64_t delay_ns;
if (job_is_cancelled(&job->common.job)) {
if (block_job_is_cancelled(&job->common)) {
return true;
}
/* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can
* return. Without a yield, the VM would not reboot. */
delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read);
job->bytes_read = 0;
job_sleep_ns(&job->common.job, delay_ns);
/* we need to yield so that bdrv_drain_all() returns.
* (without, VM does not reboot)
*/
if (job->common.speed) {
uint64_t delay_ns = ratelimit_calculate_delay(&job->limit,
job->bytes_read);
job->bytes_read = 0;
block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, delay_ns);
} else {
block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, 0);
}
if (job_is_cancelled(&job->common.job)) {
if (block_job_is_cancelled(&job->common)) {
return true;
}
return false;
}
static bool bdrv_is_unallocated_range(BlockDriverState *bs,
int64_t offset, int64_t bytes)
static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
{
int64_t end = offset + bytes;
while (offset < end && !bdrv_is_allocated(bs, offset, bytes, &bytes)) {
if (bytes == 0) {
return true;
}
offset += bytes;
bytes = end - offset;
}
return offset >= end;
}
static int coroutine_fn backup_loop(BackupBlockJob *job)
{
int ret;
bool error_is_read;
int64_t offset;
HBitmapIter hbi;
BlockDriverState *bs = blk_bs(job->common.blk);
hbitmap_iter_init(&hbi, job->copy_bitmap, 0);
while ((offset = hbitmap_iter_next(&hbi)) != -1) {
if (job->sync_mode == MIRROR_SYNC_MODE_TOP &&
bdrv_is_unallocated_range(bs, offset, job->cluster_size))
{
hbitmap_reset(job->copy_bitmap, offset, job->cluster_size);
continue;
}
do {
if (yield_and_check(job)) {
return 0;
}
ret = backup_do_cow(job, offset,
job->cluster_size, &error_is_read, false);
if (ret < 0 && backup_error_action(job, error_is_read, -ret) ==
BLOCK_ERROR_ACTION_REPORT)
{
return ret;
}
} while (ret < 0);
}
return 0;
}
/* init copy_bitmap from sync_bitmap */
static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
{
uint64_t offset = 0;
uint64_t bytes = job->len;
while (bdrv_dirty_bitmap_next_dirty_area(job->sync_bitmap,
&offset, &bytes))
{
hbitmap_set(job->copy_bitmap, offset, bytes);
offset += bytes;
if (offset >= job->len) {
break;
}
bytes = job->len - offset;
}
/* TODO job_progress_set_remaining() would make more sense */
job_progress_update(&job->common.job,
job->len - hbitmap_count(job->copy_bitmap));
}
static int coroutine_fn backup_run(Job *job, Error **errp)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
BlockDriverState *bs = blk_bs(s->common.blk);
int ret = 0;
int clusters_per_iter;
uint32_t granularity;
int64_t offset;
int64_t cluster;
int64_t end;
int64_t last_cluster = -1;
BdrvDirtyBitmapIter *dbi;
QLIST_INIT(&s->inflight_reqs);
qemu_co_rwlock_init(&s->flush_rwlock);
granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap);
clusters_per_iter = MAX((granularity / job->cluster_size), 1);
dbi = bdrv_dirty_iter_new(job->sync_bitmap, 0);
job_progress_set_remaining(job, s->len);
/* Find the next dirty sector(s) */
while ((offset = bdrv_dirty_iter_next(dbi) * BDRV_SECTOR_SIZE) >= 0) {
cluster = offset / job->cluster_size;
if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
backup_incremental_init_copy_bitmap(s);
} else {
hbitmap_set(s->copy_bitmap, 0, s->len);
}
s->before_write.notify = backup_before_write_notify;
bdrv_add_before_write_notifier(bs, &s->before_write);
if (s->sync_mode == MIRROR_SYNC_MODE_NONE) {
/* All bits are set in copy_bitmap to allow any cluster to be copied.
* This does not actually require them to be copied. */
while (!job_is_cancelled(job)) {
/* Yield until the job is cancelled. We just let our before_write
* notify callback service CoW requests. */
job_yield(job);
/* Fake progress updates for any clusters we skipped */
if (cluster != last_cluster + 1) {
job->common.offset += ((cluster - last_cluster - 1) *
job->cluster_size);
}
} else {
ret = backup_loop(s);
for (end = cluster + clusters_per_iter; cluster < end; cluster++) {
do {
if (yield_and_check(job)) {
goto out;
}
ret = backup_do_cow(job, cluster * job->cluster_size,
job->cluster_size, &error_is_read,
false);
if ((ret < 0) &&
backup_error_action(job, error_is_read, -ret) ==
BLOCK_ERROR_ACTION_REPORT) {
goto out;
}
} while (ret < 0);
}
/* If the bitmap granularity is smaller than the backup granularity,
* we need to advance the iterator pointer to the next cluster. */
if (granularity < job->cluster_size) {
bdrv_set_dirty_iter(dbi,
cluster * job->cluster_size / BDRV_SECTOR_SIZE);
}
last_cluster = cluster - 1;
}
notifier_with_return_remove(&s->before_write);
/* wait until pending backup_do_cow() calls have completed */
qemu_co_rwlock_wrlock(&s->flush_rwlock);
qemu_co_rwlock_unlock(&s->flush_rwlock);
/* Play some final catchup with the progress meter */
end = DIV_ROUND_UP(job->common.len, job->cluster_size);
if (last_cluster + 1 < end) {
job->common.offset += ((end - last_cluster - 1) * job->cluster_size);
}
out:
bdrv_dirty_iter_free(dbi);
return ret;
}
static const BlockJobDriver backup_job_driver = {
.job_driver = {
.instance_size = sizeof(BackupBlockJob),
.job_type = JOB_TYPE_BACKUP,
.free = block_job_free,
.user_resume = block_job_user_resume,
.drain = block_job_drain,
.run = backup_run,
.commit = backup_commit,
.abort = backup_abort,
.clean = backup_clean,
},
.drain = backup_drain,
};
static int64_t backup_calculate_cluster_size(BlockDriverState *target,
Error **errp)
static void coroutine_fn backup_run(void *opaque)
{
int ret;
BlockDriverInfo bdi;
BackupBlockJob *job = opaque;
BackupCompleteData *data;
BlockDriverState *bs = blk_bs(job->common.blk);
int64_t offset;
int ret = 0;
/*
* If there is no backing file on the target, we cannot rely on COW if our
* backup cluster size is smaller than the target cluster size. Even for
* targets with a backing file, try to avoid COW if possible.
*/
ret = bdrv_get_info(target, &bdi);
if (ret == -ENOTSUP && !target->backing) {
/* Cluster size is not defined */
warn_report("The target block device doesn't provide "
"information about the block size and it doesn't have a "
"backing file. The default block size of %u bytes is "
"used. If the actual block size of the target exceeds "
"this default, the backup may be unusable",
BACKUP_CLUSTER_SIZE_DEFAULT);
return BACKUP_CLUSTER_SIZE_DEFAULT;
} else if (ret < 0 && !target->backing) {
error_setg_errno(errp, -ret,
"Couldn't determine the cluster size of the target image, "
"which has no backing file");
error_append_hint(errp,
"Aborting, since this may create an unusable destination image\n");
return ret;
} else if (ret < 0 && target->backing) {
/* Not fatal; just trudge on ahead. */
return BACKUP_CLUSTER_SIZE_DEFAULT;
QLIST_INIT(&job->inflight_reqs);
qemu_co_rwlock_init(&job->flush_rwlock);
job->done_bitmap = bitmap_new(DIV_ROUND_UP(job->common.len,
job->cluster_size));
job->before_write.notify = backup_before_write_notify;
bdrv_add_before_write_notifier(bs, &job->before_write);
if (job->sync_mode == MIRROR_SYNC_MODE_NONE) {
while (!block_job_is_cancelled(&job->common)) {
/* Yield until the job is cancelled. We just let our before_write
* notify callback service CoW requests. */
block_job_yield(&job->common);
}
} else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
ret = backup_run_incremental(job);
} else {
/* Both FULL and TOP SYNC_MODE's require copying.. */
for (offset = 0; offset < job->common.len;
offset += job->cluster_size) {
bool error_is_read;
int alloced = 0;
if (yield_and_check(job)) {
break;
}
if (job->sync_mode == MIRROR_SYNC_MODE_TOP) {
int i;
int64_t n;
/* Check to see if these blocks are already in the
* backing file. */
for (i = 0; i < job->cluster_size;) {
/* bdrv_is_allocated() only returns true/false based
* on the first set of sectors it comes across that
* are are all in the same state.
* For that reason we must verify each sector in the
* backup cluster length. We end up copying more than
* needed but at some point that is always the case. */
alloced =
bdrv_is_allocated(bs, offset + i,
job->cluster_size - i, &n);
i += n;
if (alloced || n == 0) {
break;
}
}
/* If the above loop never found any sectors that are in
* the topmost image, skip this backup. */
if (alloced == 0) {
continue;
}
}
/* FULL sync mode we copy the whole drive. */
if (alloced < 0) {
ret = alloced;
} else {
ret = backup_do_cow(job, offset, job->cluster_size,
&error_is_read, false);
}
if (ret < 0) {
/* Depending on error action, fail now or retry cluster */
BlockErrorAction action =
backup_error_action(job, error_is_read, -ret);
if (action == BLOCK_ERROR_ACTION_REPORT) {
break;
} else {
offset -= job->cluster_size;
continue;
}
}
}
}
return MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
notifier_with_return_remove(&job->before_write);
/* wait until pending backup_do_cow() calls have completed */
qemu_co_rwlock_wrlock(&job->flush_rwlock);
qemu_co_rwlock_unlock(&job->flush_rwlock);
g_free(job->done_bitmap);
data = g_malloc(sizeof(*data));
data->ret = ret;
block_job_defer_to_main_loop(&job->common, backup_complete, data);
}
static const BlockJobDriver backup_job_driver = {
.instance_size = sizeof(BackupBlockJob),
.job_type = BLOCK_JOB_TYPE_BACKUP,
.start = backup_run,
.set_speed = backup_set_speed,
.commit = backup_commit,
.abort = backup_abort,
.clean = backup_clean,
.attached_aio_context = backup_attached_aio_context,
.drain = backup_drain,
};
BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
BlockDriverState *target, int64_t speed,
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
@@ -550,13 +540,12 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
BlockdevOnError on_target_error,
int creation_flags,
BlockCompletionFunc *cb, void *opaque,
JobTxn *txn, Error **errp)
BlockJobTxn *txn, Error **errp)
{
int64_t len;
BlockDriverInfo bdi;
BackupBlockJob *job = NULL;
int ret;
int64_t cluster_size;
HBitmap *copy_bitmap = NULL;
assert(bs);
assert(target);
@@ -607,7 +596,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
error_setg(errp,
"a sync_bitmap was provided to backup_run, "
"but received an incompatible sync_mode (%s)",
MirrorSyncMode_str(sync_mode));
MirrorSyncMode_lookup[sync_mode]);
return NULL;
}
@@ -618,15 +607,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
goto error;
}
cluster_size = backup_calculate_cluster_size(target, errp);
if (cluster_size < 0) {
goto error;
}
copy_bitmap = hbitmap_alloc(len, ctz32(cluster_size));
/* job->len is fixed, so we can't allow resize */
job = block_job_create(job_id, &backup_job_driver, txn, bs,
/* job->common.len is fixed, so we can't allow resize */
job = block_job_create(job_id, &backup_job_driver, bs,
BLK_PERM_CONSISTENT_READ,
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD,
@@ -636,8 +618,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
}
/* The target must match the source in size, so no resize here either */
job->target = blk_new(job->common.job.aio_context,
BLK_PERM_WRITE,
job->target = blk_new(BLK_PERM_WRITE,
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD);
ret = blk_insert_bs(job->target, target, errp);
@@ -652,36 +633,48 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
sync_bitmap : NULL;
job->compress = compress;
/* Detect image-fleecing (and similar) schemes */
job->serialize_target_writes = bdrv_chain_contains(target, bs);
job->cluster_size = cluster_size;
job->copy_bitmap = copy_bitmap;
copy_bitmap = NULL;
job->use_copy_range = !compress; /* compression isn't supported for it */
job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk),
blk_get_max_transfer(job->target));
job->copy_range_size = MAX(job->cluster_size,
QEMU_ALIGN_UP(job->copy_range_size,
job->cluster_size));
/* If there is no backing file on the target, we cannot rely on COW if our
* backup cluster size is smaller than the target cluster size. Even for
* targets with a backing file, try to avoid COW if possible. */
ret = bdrv_get_info(target, &bdi);
if (ret == -ENOTSUP && !target->backing) {
/* Cluster size is not defined */
warn_report("The target block device doesn't provide "
"information about the block size and it doesn't have a "
"backing file. The default block size of %u bytes is "
"used. If the actual block size of the target exceeds "
"this default, the backup may be unusable",
BACKUP_CLUSTER_SIZE_DEFAULT);
job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
} else if (ret < 0 && !target->backing) {
error_setg_errno(errp, -ret,
"Couldn't determine the cluster size of the target image, "
"which has no backing file");
error_append_hint(errp,
"Aborting, since this may create an unusable destination image\n");
goto error;
} else if (ret < 0 && target->backing) {
/* Not fatal; just trudge on ahead. */
job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
} else {
job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
}
/* Required permissions are already taken with target's blk_new() */
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
&error_abort);
job->len = len;
job->common.len = len;
block_job_txn_add_job(txn, &job->common);
return &job->common;
error:
if (copy_bitmap) {
assert(!job || !job->copy_bitmap);
hbitmap_free(copy_bitmap);
}
if (sync_bitmap) {
bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
}
if (job) {
backup_clean(&job->common.job);
job_early_fail(&job->common.job);
backup_clean(&job->common);
block_job_early_fail(&job->common);
}
return NULL;
+88 -115
View File
@@ -29,7 +29,7 @@
#include "qemu/config-file.h"
#include "block/block_int.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "qapi/qmp/qbool.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "sysemu/qtest.h"
@@ -75,7 +75,6 @@ typedef struct BlkdebugRule {
int state;
union {
struct {
uint64_t iotype_mask;
int error;
int immediately;
int once;
@@ -92,9 +91,6 @@ typedef struct BlkdebugRule {
QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
} BlkdebugRule;
QEMU_BUILD_BUG_MSG(BLKDEBUG_IO_TYPE__MAX > 64,
"BlkdebugIOType mask does not fit into an uint64_t");
static QemuOptsList inject_error_opts = {
.name = "inject-error",
.head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
@@ -107,10 +103,6 @@ static QemuOptsList inject_error_opts = {
.name = "state",
.type = QEMU_OPT_NUMBER,
},
{
.name = "iotype",
.type = QEMU_OPT_STRING,
},
{
.name = "errno",
.type = QEMU_OPT_NUMBER,
@@ -157,6 +149,20 @@ static QemuOptsList *config_groups[] = {
NULL
};
static int get_event_by_name(const char *name, BlkdebugEvent *event)
{
int i;
for (i = 0; i < BLKDBG__MAX; i++) {
if (!strcmp(BlkdebugEvent_lookup[i], name)) {
*event = i;
return 0;
}
}
return -1;
}
struct add_rule_data {
BDRVBlkdebugState *s;
int action;
@@ -167,20 +173,17 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
struct add_rule_data *d = opaque;
BDRVBlkdebugState *s = d->s;
const char* event_name;
int event;
BlkdebugEvent event;
struct BlkdebugRule *rule;
int64_t sector;
BlkdebugIOType iotype;
Error *local_error = NULL;
/* Find the right event for the rule */
event_name = qemu_opt_get(opts, "event");
if (!event_name) {
error_setg(errp, "Missing event name for rule");
return -1;
}
event = qapi_enum_parse(&BlkdebugEvent_lookup, event_name, -1, errp);
if (event < 0) {
} else if (get_event_by_name(event_name, &event) < 0) {
error_setg(errp, "Invalid event name \"%s\"", event_name);
return -1;
}
@@ -202,26 +205,6 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
sector = qemu_opt_get_number(opts, "sector", -1);
rule->options.inject.offset =
sector == -1 ? -1 : sector * BDRV_SECTOR_SIZE;
iotype = qapi_enum_parse(&BlkdebugIOType_lookup,
qemu_opt_get(opts, "iotype"),
BLKDEBUG_IO_TYPE__MAX, &local_error);
if (local_error) {
error_propagate(errp, local_error);
return -1;
}
if (iotype != BLKDEBUG_IO_TYPE__MAX) {
rule->options.inject.iotype_mask = (1ull << iotype);
} else {
/* Apply the default */
rule->options.inject.iotype_mask =
(1ull << BLKDEBUG_IO_TYPE_READ)
| (1ull << BLKDEBUG_IO_TYPE_WRITE)
| (1ull << BLKDEBUG_IO_TYPE_WRITE_ZEROES)
| (1ull << BLKDEBUG_IO_TYPE_DISCARD)
| (1ull << BLKDEBUG_IO_TYPE_FLUSH);
}
break;
case ACTION_SET_STATE:
@@ -274,6 +257,7 @@ static int read_config(BDRVBlkdebugState *s, const char *filename,
ret = qemu_config_parse(f, config_groups, filename);
if (ret < 0) {
error_setg(errp, "Could not parse blkdebug config file");
ret = -EINVAL;
goto fail;
}
}
@@ -335,7 +319,7 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
if (c != filename) {
QString *config_path;
config_path = qstring_from_substr(filename, 0, c - filename);
config_path = qstring_from_substr(filename, 0, c - filename - 1);
qdict_put(options, "config", config_path);
}
@@ -428,11 +412,10 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
goto out;
}
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
(BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
bs->file->bs->supported_zero_flags);
bs->supported_write_flags = BDRV_REQ_FUA &
bs->file->bs->supported_write_flags;
bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
bs->file->bs->supported_zero_flags;
ret = -EINVAL;
/* Set alignment overrides */
@@ -491,8 +474,6 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
goto out;
}
bdrv_debug_event(bs, BLKDBG_NONE);
ret = 0;
out:
if (ret < 0) {
@@ -502,8 +483,7 @@ out:
return ret;
}
static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
BlkdebugIOType iotype)
static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
{
BDRVBlkdebugState *s = bs->opaque;
BlkdebugRule *rule = NULL;
@@ -513,10 +493,9 @@ static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
uint64_t inject_offset = rule->options.inject.offset;
if ((inject_offset == -1 ||
(bytes && inject_offset >= offset &&
inject_offset < offset + bytes)) &&
(rule->options.inject.iotype_mask & (1ull << iotype)))
if (inject_offset == -1 ||
(bytes && inject_offset >= offset &&
inject_offset < offset + bytes))
{
break;
}
@@ -555,7 +534,7 @@ blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
assert(bytes <= bs->bl.max_transfer);
}
err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_READ);
err = rule_check(bs, offset, bytes);
if (err) {
return err;
}
@@ -576,7 +555,7 @@ blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
assert(bytes <= bs->bl.max_transfer);
}
err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE);
err = rule_check(bs, offset, bytes);
if (err) {
return err;
}
@@ -586,7 +565,7 @@ blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
static int blkdebug_co_flush(BlockDriverState *bs)
{
int err = rule_check(bs, 0, 0, BLKDEBUG_IO_TYPE_FLUSH);
int err = rule_check(bs, 0, 0);
if (err) {
return err;
@@ -620,7 +599,7 @@ static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs,
assert(bytes <= bs->bl.max_pwrite_zeroes);
}
err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE_ZEROES);
err = rule_check(bs, offset, bytes);
if (err) {
return err;
}
@@ -654,33 +633,22 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
assert(bytes <= bs->bl.max_pdiscard);
}
err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_DISCARD);
err = rule_check(bs, offset, bytes);
if (err) {
return err;
}
return bdrv_co_pdiscard(bs->file, offset, bytes);
return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
}
static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs,
bool want_zero,
int64_t offset,
int64_t bytes,
int64_t *pnum,
int64_t *map,
BlockDriverState **file)
static int64_t coroutine_fn blkdebug_co_get_block_status(
BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum,
BlockDriverState **file)
{
int err;
assert(QEMU_IS_ALIGNED(offset | bytes, bs->bl.request_alignment));
err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_BLOCK_STATUS);
if (err) {
return err;
}
return bdrv_co_block_status_from_file(bs, want_zero, offset, bytes,
pnum, map, file);
*pnum = nb_sectors;
*file = bs->file->bs;
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
(sector_num << BDRV_SECTOR_BITS);
}
static void blkdebug_close(BlockDriverState *bs)
@@ -775,13 +743,13 @@ static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
{
BDRVBlkdebugState *s = bs->opaque;
struct BlkdebugRule *rule;
int blkdebug_event;
BlkdebugEvent blkdebug_event;
blkdebug_event = qapi_enum_parse(&BlkdebugEvent_lookup, event, -1, NULL);
if (blkdebug_event < 0) {
if (get_event_by_name(event, &blkdebug_event) < 0) {
return -ENOENT;
}
rule = g_malloc(sizeof(*rule));
*rule = (struct BlkdebugRule) {
.event = blkdebug_event,
@@ -853,37 +821,58 @@ static int64_t blkdebug_getlength(BlockDriverState *bs)
return bdrv_getlength(bs->file->bs);
}
static void blkdebug_refresh_filename(BlockDriverState *bs)
static int blkdebug_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp)
{
return bdrv_truncate(bs->file, offset, prealloc, errp);
}
static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
{
BDRVBlkdebugState *s = bs->opaque;
QDict *opts;
const QDictEntry *e;
int ret;
bool force_json = false;
if (!bs->file->bs->exact_filename[0]) {
return;
}
for (e = qdict_first(bs->full_open_options); e;
e = qdict_next(bs->full_open_options, e))
{
/* Real child options are under "image", but "x-image" may
* contain a filename */
for (e = qdict_first(options); e; e = qdict_next(options, e)) {
if (strcmp(qdict_entry_key(e), "config") &&
strcmp(qdict_entry_key(e), "image") &&
strcmp(qdict_entry_key(e), "x-image") &&
strcmp(qdict_entry_key(e), "driver"))
strcmp(qdict_entry_key(e), "x-image"))
{
return;
force_json = true;
break;
}
}
ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
"blkdebug:%s:%s",
s->config_file ?: "", bs->file->bs->exact_filename);
if (ret >= sizeof(bs->exact_filename)) {
/* An overflow makes the filename unusable, so do not report any */
bs->exact_filename[0] = 0;
if (force_json && !bs->file->bs->full_open_options) {
/* The config file cannot be recreated, so creating a plain filename
* is impossible */
return;
}
if (!force_json && bs->file->bs->exact_filename[0]) {
int ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
"blkdebug:%s:%s", s->config_file ?: "",
bs->file->bs->exact_filename);
if (ret >= sizeof(bs->exact_filename)) {
/* An overflow makes the filename unusable, so do not report any */
bs->exact_filename[0] = 0;
}
}
opts = qdict_new();
qdict_put_str(opts, "driver", "blkdebug");
QINCREF(bs->file->bs->full_open_options);
qdict_put(opts, "image", bs->file->bs->full_open_options);
for (e = qdict_first(options); e; e = qdict_next(options, e)) {
if (strcmp(qdict_entry_key(e), "x-image")) {
qobject_incref(qdict_entry_value(e));
qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e));
}
}
bs->full_open_options = opts;
}
static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp)
@@ -916,25 +905,10 @@ static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
return 0;
}
static const char *const blkdebug_strong_runtime_opts[] = {
"config",
"inject-error.",
"set-state.",
"align",
"max-transfer",
"opt-write-zero",
"max-write-zero",
"opt-discard",
"max-discard",
NULL
};
static BlockDriver bdrv_blkdebug = {
.format_name = "blkdebug",
.protocol_name = "blkdebug",
.instance_size = sizeof(BDRVBlkdebugState),
.is_filter = true,
.bdrv_parse_filename = blkdebug_parse_filename,
.bdrv_file_open = blkdebug_open,
@@ -943,6 +917,7 @@ static BlockDriver bdrv_blkdebug = {
.bdrv_child_perm = bdrv_filter_default_perms,
.bdrv_getlength = blkdebug_getlength,
.bdrv_truncate = blkdebug_truncate,
.bdrv_refresh_filename = blkdebug_refresh_filename,
.bdrv_refresh_limits = blkdebug_refresh_limits,
@@ -951,7 +926,7 @@ static BlockDriver bdrv_blkdebug = {
.bdrv_co_flush_to_disk = blkdebug_co_flush,
.bdrv_co_pwrite_zeroes = blkdebug_co_pwrite_zeroes,
.bdrv_co_pdiscard = blkdebug_co_pdiscard,
.bdrv_co_block_status = blkdebug_co_block_status,
.bdrv_co_get_block_status = blkdebug_co_get_block_status,
.bdrv_debug_event = blkdebug_debug_event,
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
@@ -959,8 +934,6 @@ static BlockDriver bdrv_blkdebug = {
= blkdebug_debug_remove_breakpoint,
.bdrv_debug_resume = blkdebug_debug_resume,
.bdrv_debug_is_suspended = blkdebug_debug_is_suspended,
.strong_runtime_opts = blkdebug_strong_runtime_opts,
};
static void bdrv_blkdebug_init(void)
-533
View File
@@ -1,533 +0,0 @@
/*
* Write logging blk driver based on blkverify and blkdebug.
*
* Copyright (c) 2017 Tuomas Tynkkynen <tuomas@tuxera.com>
* Copyright (c) 2018 Aapo Vienamo <aapo@tuxera.com>
* Copyright (c) 2018 Ari Sundholm <ari@tuxera.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu/sockets.h" /* for EINPROGRESS on Windows */
#include "block/block_int.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qemu/cutils.h"
#include "qemu/module.h"
#include "qemu/option.h"
/* Disk format stuff - taken from Linux drivers/md/dm-log-writes.c */
#define LOG_FLUSH_FLAG (1 << 0)
#define LOG_FUA_FLAG (1 << 1)
#define LOG_DISCARD_FLAG (1 << 2)
#define LOG_MARK_FLAG (1 << 3)
#define LOG_FLAG_MASK (LOG_FLUSH_FLAG \
| LOG_FUA_FLAG \
| LOG_DISCARD_FLAG \
| LOG_MARK_FLAG)
#define WRITE_LOG_VERSION 1ULL
#define WRITE_LOG_MAGIC 0x6a736677736872ULL
/* All fields are little-endian. */
struct log_write_super {
uint64_t magic;
uint64_t version;
uint64_t nr_entries;
uint32_t sectorsize;
} QEMU_PACKED;
struct log_write_entry {
uint64_t sector;
uint64_t nr_sectors;
uint64_t flags;
uint64_t data_len;
} QEMU_PACKED;
/* End of disk format structures. */
typedef struct {
BdrvChild *log_file;
uint32_t sectorsize;
uint32_t sectorbits;
uint64_t cur_log_sector;
uint64_t nr_entries;
uint64_t update_interval;
} BDRVBlkLogWritesState;
static QemuOptsList runtime_opts = {
.name = "blklogwrites",
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
.desc = {
{
.name = "log-append",
.type = QEMU_OPT_BOOL,
.help = "Append to an existing log",
},
{
.name = "log-sector-size",
.type = QEMU_OPT_SIZE,
.help = "Log sector size",
},
{
.name = "log-super-update-interval",
.type = QEMU_OPT_NUMBER,
.help = "Log superblock update interval (# of write requests)",
},
{ /* end of list */ }
},
};
static inline uint32_t blk_log_writes_log2(uint32_t value)
{
assert(value > 0);
return 31 - clz32(value);
}
static inline bool blk_log_writes_sector_size_valid(uint32_t sector_size)
{
return is_power_of_2(sector_size) &&
sector_size >= sizeof(struct log_write_super) &&
sector_size >= sizeof(struct log_write_entry) &&
sector_size < (1ull << 24);
}
static uint64_t blk_log_writes_find_cur_log_sector(BdrvChild *log,
uint32_t sector_size,
uint64_t nr_entries,
Error **errp)
{
uint64_t cur_sector = 1;
uint64_t cur_idx = 0;
uint32_t sector_bits = blk_log_writes_log2(sector_size);
struct log_write_entry cur_entry;
while (cur_idx < nr_entries) {
int read_ret = bdrv_pread(log, cur_sector << sector_bits, &cur_entry,
sizeof(cur_entry));
if (read_ret < 0) {
error_setg_errno(errp, -read_ret,
"Failed to read log entry %"PRIu64, cur_idx);
return (uint64_t)-1ull;
}
if (cur_entry.flags & ~cpu_to_le64(LOG_FLAG_MASK)) {
error_setg(errp, "Invalid flags 0x%"PRIx64" in log entry %"PRIu64,
le64_to_cpu(cur_entry.flags), cur_idx);
return (uint64_t)-1ull;
}
/* Account for the sector of the entry itself */
++cur_sector;
/*
* Account for the data of the write.
* For discards, this data is not present.
*/
if (!(cur_entry.flags & cpu_to_le64(LOG_DISCARD_FLAG))) {
cur_sector += le64_to_cpu(cur_entry.nr_sectors);
}
++cur_idx;
}
return cur_sector;
}
static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
BDRVBlkLogWritesState *s = bs->opaque;
QemuOpts *opts;
Error *local_err = NULL;
int ret;
uint64_t log_sector_size;
bool log_append;
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (local_err) {
ret = -EINVAL;
error_propagate(errp, local_err);
goto fail;
}
/* Open the file */
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false,
&local_err);
if (local_err) {
ret = -EINVAL;
error_propagate(errp, local_err);
goto fail;
}
/* Open the log file */
s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_file, false,
&local_err);
if (local_err) {
ret = -EINVAL;
error_propagate(errp, local_err);
goto fail;
}
log_append = qemu_opt_get_bool(opts, "log-append", false);
if (log_append) {
struct log_write_super log_sb = { 0, 0, 0, 0 };
if (qemu_opt_find(opts, "log-sector-size")) {
ret = -EINVAL;
error_setg(errp, "log-append and log-sector-size are mutually "
"exclusive");
goto fail_log;
}
/* Read log superblock or fake one for an empty log */
if (!bdrv_getlength(s->log_file->bs)) {
log_sb.magic = cpu_to_le64(WRITE_LOG_MAGIC);
log_sb.version = cpu_to_le64(WRITE_LOG_VERSION);
log_sb.nr_entries = cpu_to_le64(0);
log_sb.sectorsize = cpu_to_le32(BDRV_SECTOR_SIZE);
} else {
ret = bdrv_pread(s->log_file, 0, &log_sb, sizeof(log_sb));
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read log superblock");
goto fail_log;
}
}
if (log_sb.magic != cpu_to_le64(WRITE_LOG_MAGIC)) {
ret = -EINVAL;
error_setg(errp, "Invalid log superblock magic");
goto fail_log;
}
if (log_sb.version != cpu_to_le64(WRITE_LOG_VERSION)) {
ret = -EINVAL;
error_setg(errp, "Unsupported log version %"PRIu64,
le64_to_cpu(log_sb.version));
goto fail_log;
}
log_sector_size = le32_to_cpu(log_sb.sectorsize);
s->cur_log_sector = 1;
s->nr_entries = 0;
if (blk_log_writes_sector_size_valid(log_sector_size)) {
s->cur_log_sector =
blk_log_writes_find_cur_log_sector(s->log_file, log_sector_size,
le64_to_cpu(log_sb.nr_entries), &local_err);
if (local_err) {
ret = -EINVAL;
error_propagate(errp, local_err);
goto fail_log;
}
s->nr_entries = le64_to_cpu(log_sb.nr_entries);
}
} else {
log_sector_size = qemu_opt_get_size(opts, "log-sector-size",
BDRV_SECTOR_SIZE);
s->cur_log_sector = 1;
s->nr_entries = 0;
}
if (!blk_log_writes_sector_size_valid(log_sector_size)) {
ret = -EINVAL;
error_setg(errp, "Invalid log sector size %"PRIu64, log_sector_size);
goto fail_log;
}
s->sectorsize = log_sector_size;
s->sectorbits = blk_log_writes_log2(log_sector_size);
s->update_interval = qemu_opt_get_number(opts, "log-super-update-interval",
4096);
if (!s->update_interval) {
ret = -EINVAL;
error_setg(errp, "Invalid log superblock update interval %"PRIu64,
s->update_interval);
goto fail_log;
}
ret = 0;
fail_log:
if (ret < 0) {
bdrv_unref_child(bs, s->log_file);
s->log_file = NULL;
}
fail:
if (ret < 0) {
bdrv_unref_child(bs, bs->file);
bs->file = NULL;
}
qemu_opts_del(opts);
return ret;
}
static void blk_log_writes_close(BlockDriverState *bs)
{
BDRVBlkLogWritesState *s = bs->opaque;
bdrv_unref_child(bs, s->log_file);
s->log_file = NULL;
}
static int64_t blk_log_writes_getlength(BlockDriverState *bs)
{
return bdrv_getlength(bs->file->bs);
}
static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c,
const BdrvChildRole *role,
BlockReopenQueue *ro_q,
uint64_t perm, uint64_t shrd,
uint64_t *nperm, uint64_t *nshrd)
{
if (!c) {
*nperm = perm & DEFAULT_PERM_PASSTHROUGH;
*nshrd = (shrd & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED;
return;
}
if (!strcmp(c->name, "log")) {
bdrv_format_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd);
} else {
bdrv_filter_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd);
}
}
static void blk_log_writes_refresh_limits(BlockDriverState *bs, Error **errp)
{
BDRVBlkLogWritesState *s = bs->opaque;
bs->bl.request_alignment = s->sectorsize;
}
static int coroutine_fn
blk_log_writes_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags)
{
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
}
typedef struct BlkLogWritesFileReq {
BlockDriverState *bs;
uint64_t offset;
uint64_t bytes;
int file_flags;
QEMUIOVector *qiov;
int (*func)(struct BlkLogWritesFileReq *r);
int file_ret;
} BlkLogWritesFileReq;
typedef struct {
BlockDriverState *bs;
QEMUIOVector *qiov;
struct log_write_entry entry;
uint64_t zero_size;
int log_ret;
} BlkLogWritesLogReq;
static void coroutine_fn blk_log_writes_co_do_log(BlkLogWritesLogReq *lr)
{
BDRVBlkLogWritesState *s = lr->bs->opaque;
uint64_t cur_log_offset = s->cur_log_sector << s->sectorbits;
s->nr_entries++;
s->cur_log_sector +=
ROUND_UP(lr->qiov->size, s->sectorsize) >> s->sectorbits;
lr->log_ret = bdrv_co_pwritev(s->log_file, cur_log_offset, lr->qiov->size,
lr->qiov, 0);
/* Logging for the "write zeroes" operation */
if (lr->log_ret == 0 && lr->zero_size) {
cur_log_offset = s->cur_log_sector << s->sectorbits;
s->cur_log_sector +=
ROUND_UP(lr->zero_size, s->sectorsize) >> s->sectorbits;
lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, cur_log_offset,
lr->zero_size, 0);
}
/* Update super block on flush or every update interval */
if (lr->log_ret == 0 && ((lr->entry.flags & LOG_FLUSH_FLAG)
|| (s->nr_entries % s->update_interval == 0)))
{
struct log_write_super super = {
.magic = cpu_to_le64(WRITE_LOG_MAGIC),
.version = cpu_to_le64(WRITE_LOG_VERSION),
.nr_entries = cpu_to_le64(s->nr_entries),
.sectorsize = cpu_to_le32(s->sectorsize),
};
void *zeroes = g_malloc0(s->sectorsize - sizeof(super));
QEMUIOVector qiov;
qemu_iovec_init(&qiov, 2);
qemu_iovec_add(&qiov, &super, sizeof(super));
qemu_iovec_add(&qiov, zeroes, s->sectorsize - sizeof(super));
lr->log_ret =
bdrv_co_pwritev(s->log_file, 0, s->sectorsize, &qiov, 0);
if (lr->log_ret == 0) {
lr->log_ret = bdrv_co_flush(s->log_file->bs);
}
qemu_iovec_destroy(&qiov);
g_free(zeroes);
}
}
static void coroutine_fn blk_log_writes_co_do_file(BlkLogWritesFileReq *fr)
{
fr->file_ret = fr->func(fr);
}
static int coroutine_fn
blk_log_writes_co_log(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags,
int (*file_func)(BlkLogWritesFileReq *r),
uint64_t entry_flags, bool is_zero_write)
{
QEMUIOVector log_qiov;
size_t niov = qiov ? qiov->niov : 0;
BDRVBlkLogWritesState *s = bs->opaque;
BlkLogWritesFileReq fr = {
.bs = bs,
.offset = offset,
.bytes = bytes,
.file_flags = flags,
.qiov = qiov,
.func = file_func,
};
BlkLogWritesLogReq lr = {
.bs = bs,
.qiov = &log_qiov,
.entry = {
.sector = cpu_to_le64(offset >> s->sectorbits),
.nr_sectors = cpu_to_le64(bytes >> s->sectorbits),
.flags = cpu_to_le64(entry_flags),
.data_len = 0,
},
.zero_size = is_zero_write ? bytes : 0,
};
void *zeroes = g_malloc0(s->sectorsize - sizeof(lr.entry));
assert((1 << s->sectorbits) == s->sectorsize);
assert(bs->bl.request_alignment == s->sectorsize);
assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
qemu_iovec_init(&log_qiov, niov + 2);
qemu_iovec_add(&log_qiov, &lr.entry, sizeof(lr.entry));
qemu_iovec_add(&log_qiov, zeroes, s->sectorsize - sizeof(lr.entry));
if (qiov) {
qemu_iovec_concat(&log_qiov, qiov, 0, qiov->size);
}
blk_log_writes_co_do_file(&fr);
blk_log_writes_co_do_log(&lr);
qemu_iovec_destroy(&log_qiov);
g_free(zeroes);
if (lr.log_ret < 0) {
return lr.log_ret;
}
return fr.file_ret;
}
static int coroutine_fn
blk_log_writes_co_do_file_pwritev(BlkLogWritesFileReq *fr)
{
return bdrv_co_pwritev(fr->bs->file, fr->offset, fr->bytes,
fr->qiov, fr->file_flags);
}
static int coroutine_fn
blk_log_writes_co_do_file_pwrite_zeroes(BlkLogWritesFileReq *fr)
{
return bdrv_co_pwrite_zeroes(fr->bs->file, fr->offset, fr->bytes,
fr->file_flags);
}
static int coroutine_fn blk_log_writes_co_do_file_flush(BlkLogWritesFileReq *fr)
{
return bdrv_co_flush(fr->bs->file->bs);
}
static int coroutine_fn
blk_log_writes_co_do_file_pdiscard(BlkLogWritesFileReq *fr)
{
return bdrv_co_pdiscard(fr->bs->file, fr->offset, fr->bytes);
}
static int coroutine_fn
blk_log_writes_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags)
{
return blk_log_writes_co_log(bs, offset, bytes, qiov, flags,
blk_log_writes_co_do_file_pwritev, 0, false);
}
static int coroutine_fn
blk_log_writes_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes,
BdrvRequestFlags flags)
{
return blk_log_writes_co_log(bs, offset, bytes, NULL, flags,
blk_log_writes_co_do_file_pwrite_zeroes, 0,
true);
}
static int coroutine_fn blk_log_writes_co_flush_to_disk(BlockDriverState *bs)
{
return blk_log_writes_co_log(bs, 0, 0, NULL, 0,
blk_log_writes_co_do_file_flush,
LOG_FLUSH_FLAG, false);
}
static int coroutine_fn
blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
{
return blk_log_writes_co_log(bs, offset, count, NULL, 0,
blk_log_writes_co_do_file_pdiscard,
LOG_DISCARD_FLAG, false);
}
static const char *const blk_log_writes_strong_runtime_opts[] = {
"log-append",
"log-sector-size",
NULL
};
static BlockDriver bdrv_blk_log_writes = {
.format_name = "blklogwrites",
.instance_size = sizeof(BDRVBlkLogWritesState),
.bdrv_open = blk_log_writes_open,
.bdrv_close = blk_log_writes_close,
.bdrv_getlength = blk_log_writes_getlength,
.bdrv_child_perm = blk_log_writes_child_perm,
.bdrv_refresh_limits = blk_log_writes_refresh_limits,
.bdrv_co_preadv = blk_log_writes_co_preadv,
.bdrv_co_pwritev = blk_log_writes_co_pwritev,
.bdrv_co_pwrite_zeroes = blk_log_writes_co_pwrite_zeroes,
.bdrv_co_flush_to_disk = blk_log_writes_co_flush_to_disk,
.bdrv_co_pdiscard = blk_log_writes_co_pdiscard,
.bdrv_co_block_status = bdrv_co_block_status_from_file,
.is_filter = true,
.strong_runtime_opts = blk_log_writes_strong_runtime_opts,
};
static void bdrv_blk_log_writes_init(void)
{
bdrv_register(&bdrv_blk_log_writes);
}
block_init(bdrv_blk_log_writes_init);
Regular → Executable
+9 -6
View File
@@ -10,7 +10,7 @@
*/
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu-common.h"
#include "block/block_int.h"
#include "sysemu/replay.h"
#include "qapi/error.h"
@@ -35,14 +35,15 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED;
ret = 0;
fail:
return ret;
}
static void blkreplay_close(BlockDriverState *bs)
{
}
static int64_t blkreplay_getlength(BlockDriverState *bs)
{
return bdrv_getlength(bs->file->bs);
@@ -109,7 +110,7 @@ static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs,
int64_t offset, int bytes)
{
uint64_t reqid = blkreplay_next_id();
int ret = bdrv_co_pdiscard(bs->file, offset, bytes);
int ret = bdrv_co_pdiscard(bs->file->bs, offset, bytes);
block_request_create(reqid, bs, qemu_coroutine_self());
qemu_coroutine_yield();
@@ -128,9 +129,11 @@ static int coroutine_fn blkreplay_co_flush(BlockDriverState *bs)
static BlockDriver bdrv_blkreplay = {
.format_name = "blkreplay",
.protocol_name = "blkreplay",
.instance_size = 0,
.bdrv_open = blkreplay_open,
.bdrv_file_open = blkreplay_open,
.bdrv_close = blkreplay_close,
.bdrv_child_perm = bdrv_filter_default_perms,
.bdrv_getlength = blkreplay_getlength,
Loaded 100 of 5978 files, more files were not shown because too many files have changed in this diff. Show more