Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Andrea Fioraldi 2021-07-09 15:39:07 +02:00
commit 8e2b2afdc6
75 changed files with 2021 additions and 1941 deletions

View File

@ -1881,6 +1881,7 @@ virtio-9p
M: Greg Kurz <groug@kaod.org> M: Greg Kurz <groug@kaod.org>
M: Christian Schoenebeck <qemu_oss@crudebyte.com> M: Christian Schoenebeck <qemu_oss@crudebyte.com>
S: Odd Fixes S: Odd Fixes
W: https://wiki.qemu.org/Documentation/9p
F: hw/9pfs/ F: hw/9pfs/
X: hw/9pfs/xen-9p* X: hw/9pfs/xen-9p*
F: fsdev/ F: fsdev/

View File

@ -14,7 +14,7 @@ SRC_PATH=.
# we have explicit rules for everything # we have explicit rules for everything
MAKEFLAGS += -rR MAKEFLAGS += -rR
SHELL = /usr/bin/env bash -o pipefail SHELL = bash -o pipefail
# Usage: $(call quiet-command,command and args,"NAME","args to print") # Usage: $(call quiet-command,command and args,"NAME","args to print")
# This will run "command and args", and either: # This will run "command and args", and either:

1023
configure vendored

File diff suppressed because it is too large Load Diff

View File

@ -303,5 +303,5 @@ variable::
host_kconfig = \ host_kconfig = \
('CONFIG_TPM' in config_host ? ['CONFIG_TPM=y'] : []) + \ ('CONFIG_TPM' in config_host ? ['CONFIG_TPM=y'] : []) + \
('CONFIG_SPICE' in config_host ? ['CONFIG_SPICE=y'] : []) + \ ('CONFIG_SPICE' in config_host ? ['CONFIG_SPICE=y'] : []) + \
('CONFIG_IVSHMEM' in config_host ? ['CONFIG_IVSHMEM=y'] : []) + \ (have_ivshmem ? ['CONFIG_IVSHMEM=y'] : []) + \
... ...

View File

@ -126,6 +126,18 @@ other options have been processed. This will either have no effect (if
if they were not given. The property is therefore useless and should not be if they were not given. The property is therefore useless and should not be
specified. specified.
``-display sdl,window_close=...`` (since 6.1)
'''''''''''''''''''''''''''''''''''''''''''''
Use ``-display sdl,window-close=...`` instead (i.e. with a minus instead of
an underscore between "window" and "close").
``-no-quit`` (since 6.1)
''''''''''''''''''''''''
The ``-no-quit`` is a synonym for ``-display ...,window-close=off`` which
should be used instead.
QEMU Machine Protocol (QMP) commands QEMU Machine Protocol (QMP) commands
------------------------------------ ------------------------------------

View File

@ -101,6 +101,9 @@ Options
Enable/disable extended attributes (xattr) on files and directories. The Enable/disable extended attributes (xattr) on files and directories. The
default is ``no_xattr``. default is ``no_xattr``.
* posix_acl|no_posix_acl -
Enable/disable posix acl support. Posix ACLs are disabled by default`.
.. option:: --socket-path=PATH .. option:: --socket-path=PATH
Listen on vhost-user UNIX domain socket at PATH. Listen on vhost-user UNIX domain socket at PATH.
@ -127,8 +130,8 @@ Options
timeout. ``always`` sets a long cache lifetime at the expense of coherency. timeout. ``always`` sets a long cache lifetime at the expense of coherency.
The default is ``auto``. The default is ``auto``.
xattr-mapping Extended attribute (xattr) mapping
------------- ----------------------------------
By default the name of xattr's used by the client are passed through to the server By default the name of xattr's used by the client are passed through to the server
file system. This can be a problem where either those xattr names are used file system. This can be a problem where either those xattr names are used
@ -136,6 +139,9 @@ by something on the server (e.g. selinux client/server confusion) or if the
virtiofsd is running in a container with restricted privileges where it cannot virtiofsd is running in a container with restricted privileges where it cannot
access some attributes. access some attributes.
Mapping syntax
~~~~~~~~~~~~~~
A mapping of xattr names can be made using -o xattrmap=mapping where the ``mapping`` A mapping of xattr names can be made using -o xattrmap=mapping where the ``mapping``
string consists of a series of rules. string consists of a series of rules.
@ -232,8 +238,48 @@ Note: When the 'security.capability' xattr is remapped, the daemon has to do
extra work to remove it during many operations, which the host kernel normally extra work to remove it during many operations, which the host kernel normally
does itself. does itself.
xattr-mapping Examples Security considerations
---------------------- ~~~~~~~~~~~~~~~~~~~~~~~
Operating systems typically partition the xattr namespace using
well defined name prefixes. Each partition may have different
access controls applied. For example, on Linux there are multiple
partitions
* ``system.*`` - access varies depending on attribute & filesystem
* ``security.*`` - only processes with CAP_SYS_ADMIN
* ``trusted.*`` - only processes with CAP_SYS_ADMIN
* ``user.*`` - any process granted by file permissions / ownership
While other OS such as FreeBSD have different name prefixes
and access control rules.
When remapping attributes on the host, it is important to
ensure that the remapping does not allow a guest user to
evade the guest access control rules.
Consider if ``trusted.*`` from the guest was remapped to
``user.virtiofs.trusted*`` in the host. An unprivileged
user in a Linux guest has the ability to write to xattrs
under ``user.*``. Thus the user can evade the access
control restriction on ``trusted.*`` by instead writing
to ``user.virtiofs.trusted.*``.
As noted above, the partitions used and access controls
applied, will vary across guest OS, so it is not wise to
try to predict what the guest OS will use.
The simplest way to avoid an insecure configuration is
to remap all xattrs at once, to a given fixed prefix.
This is shown in example (1) below.
If selectively mapping only a subset of xattr prefixes,
then rules must be added to explicitly block direct
access to the target of the remapping. This is shown
in example (2) below.
Mapping examples
~~~~~~~~~~~~~~~~
1) Prefix all attributes with 'user.virtiofs.' 1) Prefix all attributes with 'user.virtiofs.'
@ -271,7 +317,9 @@ stripping of 'user.virtiofs.'.
The second rule hides unprefixed 'trusted.' attributes The second rule hides unprefixed 'trusted.' attributes
on the host. on the host.
The third rule stops a guest from explicitly setting The third rule stops a guest from explicitly setting
the 'user.virtiofs.' path directly. the 'user.virtiofs.' path directly to prevent access
control bypass on the target of the earlier prefix
remapping.
Finally, the fourth rule lets all remaining attributes Finally, the fourth rule lets all remaining attributes
through. through.

View File

@ -10,6 +10,11 @@
* the COPYING file in the top-level directory. * the COPYING file in the top-level directory.
*/ */
/*
* Not so fast! You might want to read the 9p developer docs first:
* https://wiki.qemu.org/Documentation/9p
*/
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "9p.h" #include "9p.h"
#include "9p-local.h" #include "9p-local.h"

View File

@ -11,6 +11,11 @@
* *
*/ */
/*
* Not so fast! You might want to read the 9p developer docs first:
* https://wiki.qemu.org/Documentation/9p
*/
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/xattr.h" #include "qemu/xattr.h"
#include "9p.h" #include "9p.h"

View File

@ -10,6 +10,11 @@
* the COPYING file in the top-level directory. * the COPYING file in the top-level directory.
*/ */
/*
* Not so fast! You might want to read the 9p developer docs first:
* https://wiki.qemu.org/Documentation/9p
*/
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>

View File

@ -12,6 +12,11 @@
* *
*/ */
/*
* Not so fast! You might want to read the 9p developer docs first:
* https://wiki.qemu.org/Documentation/9p
*/
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "9p.h" #include "9p.h"
#include "fsdev/qemu-fsdev.h" #include "fsdev/qemu-fsdev.h"

View File

@ -10,6 +10,11 @@
* See the COPYING file in the top-level directory. * See the COPYING file in the top-level directory.
*/ */
/*
* Not so fast! You might want to read the 9p developer docs first:
* https://wiki.qemu.org/Documentation/9p
*/
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/xattr.h" #include "qemu/xattr.h"
#include "9p-util.h" #include "9p-util.h"

View File

@ -11,6 +11,11 @@
* *
*/ */
/*
* Not so fast! You might want to read the 9p developer docs first:
* https://wiki.qemu.org/Documentation/9p
*/
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "9p.h" #include "9p.h"
#include "fsdev/file-op-9p.h" #include "fsdev/file-op-9p.h"

View File

@ -11,6 +11,11 @@
* *
*/ */
/*
* Not so fast! You might want to read the 9p developer docs first:
* https://wiki.qemu.org/Documentation/9p
*/
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "9p.h" #include "9p.h"
#include "fsdev/file-op-9p.h" #include "fsdev/file-op-9p.h"

View File

@ -11,6 +11,11 @@
* *
*/ */
/*
* Not so fast! You might want to read the 9p developer docs first:
* https://wiki.qemu.org/Documentation/9p
*/
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include <glib/gprintf.h> #include <glib/gprintf.h>
#include "hw/virtio/virtio.h" #include "hw/virtio/virtio.h"
@ -966,23 +971,6 @@ static int stat_to_qid(V9fsPDU *pdu, const struct stat *stbuf, V9fsQID *qidp)
return 0; return 0;
} }
static int coroutine_fn fid_to_qid(V9fsPDU *pdu, V9fsFidState *fidp,
V9fsQID *qidp)
{
struct stat stbuf;
int err;
err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
if (err < 0) {
return err;
}
err = stat_to_qid(pdu, &stbuf, qidp);
if (err < 0) {
return err;
}
return 0;
}
V9fsPDU *pdu_alloc(V9fsState *s) V9fsPDU *pdu_alloc(V9fsState *s)
{ {
V9fsPDU *pdu = NULL; V9fsPDU *pdu = NULL;
@ -1395,6 +1383,7 @@ static void coroutine_fn v9fs_attach(void *opaque)
size_t offset = 7; size_t offset = 7;
V9fsQID qid; V9fsQID qid;
ssize_t err; ssize_t err;
struct stat stbuf;
v9fs_string_init(&uname); v9fs_string_init(&uname);
v9fs_string_init(&aname); v9fs_string_init(&aname);
@ -1417,7 +1406,13 @@ static void coroutine_fn v9fs_attach(void *opaque)
clunk_fid(s, fid); clunk_fid(s, fid);
goto out; goto out;
} }
err = fid_to_qid(pdu, fidp, &qid); err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
if (err < 0) {
err = -EINVAL;
clunk_fid(s, fid);
goto out;
}
err = stat_to_qid(pdu, &stbuf, &qid);
if (err < 0) { if (err < 0) {
err = -EINVAL; err = -EINVAL;
clunk_fid(s, fid); clunk_fid(s, fid);
@ -1449,7 +1444,7 @@ static void coroutine_fn v9fs_attach(void *opaque)
} }
err += offset; err += offset;
memcpy(&s->root_qid, &qid, sizeof(qid)); memcpy(&s->root_st, &stbuf, sizeof(stbuf));
trace_v9fs_attach_return(pdu->tag, pdu->id, trace_v9fs_attach_return(pdu->tag, pdu->id,
qid.type, qid.version, qid.path); qid.type, qid.version, qid.path);
out: out:
@ -1700,12 +1695,9 @@ static bool name_is_illegal(const char *name)
return !*name || strchr(name, '/') != NULL; return !*name || strchr(name, '/') != NULL;
} }
static bool not_same_qid(const V9fsQID *qid1, const V9fsQID *qid2) static bool same_stat_id(const struct stat *a, const struct stat *b)
{ {
return return a->st_dev == b->st_dev && a->st_ino == b->st_ino;
qid1->type != qid2->type ||
qid1->version != qid2->version ||
qid1->path != qid2->path;
} }
static void coroutine_fn v9fs_walk(void *opaque) static void coroutine_fn v9fs_walk(void *opaque)
@ -1713,9 +1705,9 @@ static void coroutine_fn v9fs_walk(void *opaque)
int name_idx; int name_idx;
V9fsQID *qids = NULL; V9fsQID *qids = NULL;
int i, err = 0; int i, err = 0;
V9fsPath dpath, path; V9fsPath dpath, path, *pathes = NULL;
uint16_t nwnames; uint16_t nwnames;
struct stat stbuf; struct stat stbuf, fidst, *stbufs = NULL;
size_t offset = 7; size_t offset = 7;
int32_t fid, newfid; int32_t fid, newfid;
V9fsString *wnames = NULL; V9fsString *wnames = NULL;
@ -1734,9 +1726,15 @@ static void coroutine_fn v9fs_walk(void *opaque)
trace_v9fs_walk(pdu->tag, pdu->id, fid, newfid, nwnames); trace_v9fs_walk(pdu->tag, pdu->id, fid, newfid, nwnames);
if (nwnames && nwnames <= P9_MAXWELEM) { if (nwnames > P9_MAXWELEM) {
err = -EINVAL;
goto out_nofid;
}
if (nwnames) {
wnames = g_new0(V9fsString, nwnames); wnames = g_new0(V9fsString, nwnames);
qids = g_new0(V9fsQID, nwnames); qids = g_new0(V9fsQID, nwnames);
stbufs = g_new0(struct stat, nwnames);
pathes = g_new0(V9fsPath, nwnames);
for (i = 0; i < nwnames; i++) { for (i = 0; i < nwnames; i++) {
err = pdu_unmarshal(pdu, offset, "s", &wnames[i]); err = pdu_unmarshal(pdu, offset, "s", &wnames[i]);
if (err < 0) { if (err < 0) {
@ -1748,9 +1746,6 @@ static void coroutine_fn v9fs_walk(void *opaque)
} }
offset += err; offset += err;
} }
} else if (nwnames > P9_MAXWELEM) {
err = -EINVAL;
goto out_nofid;
} }
fidp = get_fid(pdu, fid); fidp = get_fid(pdu, fid);
if (fidp == NULL) { if (fidp == NULL) {
@ -1760,35 +1755,85 @@ static void coroutine_fn v9fs_walk(void *opaque)
v9fs_path_init(&dpath); v9fs_path_init(&dpath);
v9fs_path_init(&path); v9fs_path_init(&path);
err = fid_to_qid(pdu, fidp, &qid);
if (err < 0) {
goto out;
}
/* /*
* Both dpath and path initially poin to fidp. * Both dpath and path initially point to fidp.
* Needed to handle request with nwnames == 0 * Needed to handle request with nwnames == 0
*/ */
v9fs_path_copy(&dpath, &fidp->path); v9fs_path_copy(&dpath, &fidp->path);
v9fs_path_copy(&path, &fidp->path); v9fs_path_copy(&path, &fidp->path);
for (name_idx = 0; name_idx < nwnames; name_idx++) {
if (not_same_qid(&pdu->s->root_qid, &qid) ||
strcmp("..", wnames[name_idx].data)) {
err = v9fs_co_name_to_path(pdu, &dpath, wnames[name_idx].data,
&path);
if (err < 0) {
goto out;
}
err = v9fs_co_lstat(pdu, &path, &stbuf); /*
if (err < 0) { * To keep latency (i.e. overall execution time for processing this
goto out; * Twalk client request) as small as possible, run all the required fs
* driver code altogether inside the following block.
*/
v9fs_co_run_in_worker({
if (v9fs_request_cancelled(pdu)) {
err = -EINTR;
break;
}
err = s->ops->lstat(&s->ctx, &dpath, &fidst);
if (err < 0) {
err = -errno;
break;
}
stbuf = fidst;
for (name_idx = 0; name_idx < nwnames; name_idx++) {
if (v9fs_request_cancelled(pdu)) {
err = -EINTR;
break;
} }
if (!same_stat_id(&pdu->s->root_st, &stbuf) ||
strcmp("..", wnames[name_idx].data))
{
err = s->ops->name_to_path(&s->ctx, &dpath,
wnames[name_idx].data, &path);
if (err < 0) {
err = -errno;
break;
}
if (v9fs_request_cancelled(pdu)) {
err = -EINTR;
break;
}
err = s->ops->lstat(&s->ctx, &path, &stbuf);
if (err < 0) {
err = -errno;
break;
}
stbufs[name_idx] = stbuf;
v9fs_path_copy(&dpath, &path);
v9fs_path_copy(&pathes[name_idx], &path);
}
}
});
/*
* Handle all the rest of this Twalk request on main thread ...
*/
if (err < 0) {
goto out;
}
err = stat_to_qid(pdu, &fidst, &qid);
if (err < 0) {
goto out;
}
stbuf = fidst;
/* reset dpath and path */
v9fs_path_copy(&dpath, &fidp->path);
v9fs_path_copy(&path, &fidp->path);
for (name_idx = 0; name_idx < nwnames; name_idx++) {
if (!same_stat_id(&pdu->s->root_st, &stbuf) ||
strcmp("..", wnames[name_idx].data))
{
stbuf = stbufs[name_idx];
err = stat_to_qid(pdu, &stbuf, &qid); err = stat_to_qid(pdu, &stbuf, &qid);
if (err < 0) { if (err < 0) {
goto out; goto out;
} }
v9fs_path_copy(&path, &pathes[name_idx]);
v9fs_path_copy(&dpath, &path); v9fs_path_copy(&dpath, &path);
} }
memcpy(&qids[name_idx], &qid, sizeof(qid)); memcpy(&qids[name_idx], &qid, sizeof(qid));
@ -1824,9 +1869,12 @@ out_nofid:
if (nwnames && nwnames <= P9_MAXWELEM) { if (nwnames && nwnames <= P9_MAXWELEM) {
for (name_idx = 0; name_idx < nwnames; name_idx++) { for (name_idx = 0; name_idx < nwnames; name_idx++) {
v9fs_string_free(&wnames[name_idx]); v9fs_string_free(&wnames[name_idx]);
v9fs_path_free(&pathes[name_idx]);
} }
g_free(wnames); g_free(wnames);
g_free(qids); g_free(qids);
g_free(stbufs);
g_free(pathes);
} }
} }

View File

@ -355,7 +355,7 @@ struct V9fsState {
int32_t root_fid; int32_t root_fid;
Error *migration_blocker; Error *migration_blocker;
V9fsConf fsconf; V9fsConf fsconf;
V9fsQID root_qid; struct stat root_st;
dev_t dev_id; dev_t dev_id;
struct qht qpd_table; struct qht qpd_table;
struct qht qpp_table; struct qht qpp_table;

View File

@ -11,6 +11,11 @@
* *
*/ */
/*
* Not so fast! You might want to read the 9p developer docs first:
* https://wiki.qemu.org/Documentation/9p
*/
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "fsdev/qemu-fsdev.h" #include "fsdev/qemu-fsdev.h"
#include "qemu/thread.h" #include "qemu/thread.h"

View File

@ -11,6 +11,11 @@
* *
*/ */
/*
* Not so fast! You might want to read the 9p developer docs first:
* https://wiki.qemu.org/Documentation/9p
*/
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "fsdev/qemu-fsdev.h" #include "fsdev/qemu-fsdev.h"
#include "qemu/thread.h" #include "qemu/thread.h"

View File

@ -11,6 +11,11 @@
* *
*/ */
/*
* Not so fast! You might want to read the 9p developer docs first:
* https://wiki.qemu.org/Documentation/9p
*/
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "fsdev/qemu-fsdev.h" #include "fsdev/qemu-fsdev.h"
#include "qemu/thread.h" #include "qemu/thread.h"

View File

@ -12,6 +12,11 @@
* *
*/ */
/*
* Not so fast! You might want to read the 9p developer docs first:
* https://wiki.qemu.org/Documentation/9p
*/
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "block/thread-pool.h" #include "block/thread-pool.h"
#include "qemu/coroutine.h" #include "qemu/coroutine.h"

View File

@ -11,6 +11,11 @@
* *
*/ */
/*
* Not so fast! You might want to read the 9p developer docs first:
* https://wiki.qemu.org/Documentation/9p
*/
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "fsdev/qemu-fsdev.h" #include "fsdev/qemu-fsdev.h"
#include "qemu/thread.h" #include "qemu/thread.h"

View File

@ -11,6 +11,11 @@
* *
*/ */
/*
* Not so fast! You might want to read the 9p developer docs first:
* https://wiki.qemu.org/Documentation/9p
*/
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "hw/virtio/virtio.h" #include "hw/virtio/virtio.h"
#include "qemu/sockets.h" #include "qemu/sockets.h"

View File

@ -8,6 +8,11 @@
* *
*/ */
/*
* Not so fast! You might want to read the 9p developer docs first:
* https://wiki.qemu.org/Documentation/9p
*/
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "hw/9pfs/9p.h" #include "hw/9pfs/9p.h"

View File

@ -19,6 +19,7 @@
#include "hw/loader.h" #include "hw/loader.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qapi-visit-common.h" #include "qapi/qapi-visit-common.h"
#include "qapi/qapi-visit-machine.h"
#include "qapi/visitor.h" #include "qapi/visitor.h"
#include "hw/sysbus.h" #include "hw/sysbus.h"
#include "sysemu/cpus.h" #include "sysemu/cpus.h"
@ -799,6 +800,57 @@ static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
ms->smp.sockets = sockets; ms->smp.sockets = sockets;
} }
static void machine_get_smp(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
MachineState *ms = MACHINE(obj);
SMPConfiguration *config = &(SMPConfiguration){
.has_cores = true, .cores = ms->smp.cores,
.has_sockets = true, .sockets = ms->smp.sockets,
.has_dies = true, .dies = ms->smp.dies,
.has_threads = true, .threads = ms->smp.threads,
.has_cpus = true, .cpus = ms->smp.cpus,
.has_maxcpus = true, .maxcpus = ms->smp.max_cpus,
};
if (!visit_type_SMPConfiguration(v, name, &config, &error_abort)) {
return;
}
}
static void machine_set_smp(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
MachineClass *mc = MACHINE_GET_CLASS(obj);
MachineState *ms = MACHINE(obj);
SMPConfiguration *config;
ERRP_GUARD();
if (!visit_type_SMPConfiguration(v, name, &config, errp)) {
return;
}
mc->smp_parse(ms, config, errp);
if (errp) {
goto out_free;
}
/* sanity-check smp_cpus and max_cpus against mc */
if (ms->smp.cpus < mc->min_cpus) {
error_setg(errp, "Invalid SMP CPUs %d. The min CPUs "
"supported by machine '%s' is %d",
ms->smp.cpus,
mc->name, mc->min_cpus);
} else if (ms->smp.max_cpus > mc->max_cpus) {
error_setg(errp, "Invalid SMP CPUs %d. The max CPUs "
"supported by machine '%s' is %d",
current_machine->smp.max_cpus,
mc->name, mc->max_cpus);
}
out_free:
qapi_free_SMPConfiguration(config);
}
static void machine_class_init(ObjectClass *oc, void *data) static void machine_class_init(ObjectClass *oc, void *data)
{ {
MachineClass *mc = MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc);
@ -838,6 +890,12 @@ static void machine_class_init(ObjectClass *oc, void *data)
object_class_property_set_description(oc, "dumpdtb", object_class_property_set_description(oc, "dumpdtb",
"Dump current dtb to a file and quit"); "Dump current dtb to a file and quit");
object_class_property_add(oc, "smp", "SMPConfiguration",
machine_get_smp, machine_set_smp,
NULL, NULL);
object_class_property_set_description(oc, "smp",
"CPU topology");
object_class_property_add(oc, "phandle-start", "int", object_class_property_add(oc, "phandle-start", "int",
machine_get_phandle_start, machine_set_phandle_start, machine_get_phandle_start, machine_set_phandle_start,
NULL, NULL); NULL, NULL);
@ -1126,56 +1184,6 @@ MemoryRegion *machine_consume_memdev(MachineState *machine,
return ret; return ret;
} }
bool machine_smp_parse(MachineState *ms, QemuOpts *opts, Error **errp)
{
MachineClass *mc = MACHINE_GET_CLASS(ms);
ERRP_GUARD();
if (opts) {
SMPConfiguration config = {
.has_cpus = !!qemu_opt_get(opts, "cpus"),
.cpus = qemu_opt_get_number(opts, "cpus", 0),
.has_sockets = !!qemu_opt_get(opts, "sockets"),
.sockets = qemu_opt_get_number(opts, "sockets", 0),
.has_dies = !!qemu_opt_get(opts, "dies"),
.dies = qemu_opt_get_number(opts, "dies", 0),
.has_cores = !!qemu_opt_get(opts, "cores"),
.cores = qemu_opt_get_number(opts, "cores", 0),
.has_threads = !!qemu_opt_get(opts, "threads"),
.threads = qemu_opt_get_number(opts, "threads", 0),
.has_maxcpus = !!qemu_opt_get(opts, "maxcpus"),
.maxcpus = qemu_opt_get_number(opts, "maxcpus", 0),
};
mc->smp_parse(ms, &config, errp);
if (*errp) {
return false;
}
}
/* sanity-check smp_cpus and max_cpus against mc */
if (ms->smp.cpus < mc->min_cpus) {
error_setg(errp, "Invalid SMP CPUs %d. The min CPUs "
"supported by machine '%s' is %d",
ms->smp.cpus,
mc->name, mc->min_cpus);
return false;
} else if (ms->smp.max_cpus > mc->max_cpus) {
error_setg(errp, "Invalid SMP CPUs %d. The max CPUs "
"supported by machine '%s' is %d",
current_machine->smp.max_cpus,
mc->name, mc->max_cpus);
return false;
}
if (ms->smp.cpus > 1) {
Error *blocker = NULL;
error_setg(&blocker, QERR_REPLAY_NOT_SUPPORTED, "smp");
replay_add_blocker(blocker);
}
return true;
}
void machine_run_board_init(MachineState *machine) void machine_run_board_init(MachineState *machine)
{ {
MachineClass *machine_class = MACHINE_GET_CLASS(machine); MachineClass *machine_class = MACHINE_GET_CLASS(machine);

View File

@ -61,7 +61,7 @@ if config_all_devices.has_key('CONFIG_VIRTIO_GPU')
hw_display_modules += {'virtio-gpu': virtio_gpu_ss} hw_display_modules += {'virtio-gpu': virtio_gpu_ss}
virtio_gpu_gl_ss = ss.source_set() virtio_gpu_gl_ss = ss.source_set()
virtio_gpu_gl_ss.add(when: ['CONFIG_VIRTIO_GPU', 'CONFIG_VIRGL', opengl], virtio_gpu_gl_ss.add(when: ['CONFIG_VIRTIO_GPU', virgl, opengl],
if_true: [files('virtio-gpu-gl.c', 'virtio-gpu-virgl.c'), pixman, virgl]) if_true: [files('virtio-gpu-gl.c', 'virtio-gpu-virgl.c'), pixman, virgl])
hw_display_modules += {'virtio-gpu-gl': virtio_gpu_gl_ss} hw_display_modules += {'virtio-gpu-gl': virtio_gpu_gl_ss}
endif endif
@ -75,7 +75,7 @@ if config_all_devices.has_key('CONFIG_VIRTIO_PCI')
hw_display_modules += {'virtio-gpu-pci': virtio_gpu_pci_ss} hw_display_modules += {'virtio-gpu-pci': virtio_gpu_pci_ss}
virtio_gpu_pci_gl_ss = ss.source_set() virtio_gpu_pci_gl_ss = ss.source_set()
virtio_gpu_pci_gl_ss.add(when: ['CONFIG_VIRTIO_GPU', 'CONFIG_VIRTIO_PCI', 'CONFIG_VIRGL', opengl], virtio_gpu_pci_gl_ss.add(when: ['CONFIG_VIRTIO_GPU', 'CONFIG_VIRTIO_PCI', virgl, opengl],
if_true: [files('virtio-gpu-pci-gl.c'), pixman]) if_true: [files('virtio-gpu-pci-gl.c'), pixman])
hw_display_modules += {'virtio-gpu-pci-gl': virtio_gpu_pci_gl_ss} hw_display_modules += {'virtio-gpu-pci-gl': virtio_gpu_pci_gl_ss}
endif endif
@ -89,7 +89,7 @@ if config_all_devices.has_key('CONFIG_VIRTIO_VGA')
hw_display_modules += {'virtio-vga': virtio_vga_ss} hw_display_modules += {'virtio-vga': virtio_vga_ss}
virtio_vga_gl_ss = ss.source_set() virtio_vga_gl_ss = ss.source_set()
virtio_vga_gl_ss.add(when: ['CONFIG_VIRTIO_VGA', 'CONFIG_VIRGL', opengl], virtio_vga_gl_ss.add(when: ['CONFIG_VIRTIO_VGA', virgl, opengl],
if_true: [files('virtio-vga-gl.c'), pixman]) if_true: [files('virtio-vga-gl.c'), pixman])
hw_display_modules += {'virtio-vga-gl': virtio_vga_gl_ss} hw_display_modules += {'virtio-vga-gl': virtio_vga_gl_ss}
endif endif

View File

@ -26,7 +26,6 @@ OBJECT_DECLARE_TYPE(MachineState, MachineClass, MACHINE)
extern MachineState *current_machine; extern MachineState *current_machine;
void machine_run_board_init(MachineState *machine); void machine_run_board_init(MachineState *machine);
bool machine_smp_parse(MachineState *ms, QemuOpts *opts, Error **errp);
bool machine_usb(MachineState *machine); bool machine_usb(MachineState *machine);
int machine_phandle_start(MachineState *machine); int machine_phandle_start(MachineState *machine);
bool machine_dump_guest_core(MachineState *machine); bool machine_dump_guest_core(MachineState *machine);

View File

@ -119,7 +119,6 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
int fail_if_exists, Error **errp); int fail_if_exists, Error **errp);
void qemu_opts_reset(QemuOptsList *list); void qemu_opts_reset(QemuOptsList *list);
void qemu_opts_loc_restore(QemuOpts *opts); void qemu_opts_loc_restore(QemuOpts *opts);
bool qemu_opts_set(QemuOptsList *list, const char *name, const char *value, Error **errp);
const char *qemu_opts_id(QemuOpts *opts); const char *qemu_opts_id(QemuOpts *opts);
void qemu_opts_set_id(QemuOpts *opts, char *id); void qemu_opts_set_id(QemuOpts *opts, char *id);
void qemu_opts_del(QemuOpts *opts); void qemu_opts_del(QemuOpts *opts);
@ -130,8 +129,6 @@ QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params,
bool permit_abbrev); bool permit_abbrev);
QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params, QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params,
bool permit_abbrev, Error **errp); bool permit_abbrev, Error **errp);
void qemu_opts_set_defaults(QemuOptsList *list, const char *params,
int permit_abbrev);
QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict, QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict,
Error **errp); Error **errp);
QDict *qemu_opts_to_qdict_filtered(QemuOpts *opts, QDict *qdict, QDict *qemu_opts_to_qdict_filtered(QemuOpts *opts, QDict *qdict,
@ -147,7 +144,10 @@ void qemu_opts_print_help(QemuOptsList *list, bool print_caption);
void qemu_opts_free(QemuOptsList *list); void qemu_opts_free(QemuOptsList *list);
QemuOptsList *qemu_opts_append(QemuOptsList *dst, QemuOptsList *list); QemuOptsList *qemu_opts_append(QemuOptsList *dst, QemuOptsList *list);
QDict *keyval_parse_into(QDict *qdict, const char *params, const char *implied_key,
bool *p_help, Error **errp);
QDict *keyval_parse(const char *params, const char *implied_key, QDict *keyval_parse(const char *params, const char *implied_key,
bool *help, Error **errp); bool *help, Error **errp);
void keyval_merge(QDict *old, const QDict *new, Error **errp);
#endif #endif

View File

@ -861,6 +861,29 @@ static void do_qemu_init_ ## type_array(void) \
} \ } \
type_init(do_qemu_init_ ## type_array) type_init(do_qemu_init_ ## type_array)
/**
* type_print_class_properties:
* @type: a QOM class name
*
* Print the object's class properties to stdout or the monitor.
* Return whether an object was found.
*/
bool type_print_class_properties(const char *type);
/**
* object_set_properties_from_keyval:
* @obj: a QOM object
* @qdict: a dictionary with the properties to be set
* @from_json: true if leaf values of @qdict are typed, false if they
* are strings
* @errp: pointer to error object
*
* For each key in the dictionary, parse the value string if needed,
* then set the corresponding property in @obj.
*/
void object_set_properties_from_keyval(Object *obj, const QDict *qdict,
bool from_json, Error **errp);
/** /**
* object_class_dynamic_cast_assert: * object_class_dynamic_cast_assert:
* @klass: The #ObjectClass to attempt to cast. * @klass: The #ObjectClass to attempt to cast.

View File

@ -44,197 +44,197 @@ struct target_pt_regs {
#define UNAME_MACHINE "alpha" #define UNAME_MACHINE "alpha"
#define UNAME_MINIMUM_RELEASE "2.6.32" #define UNAME_MINIMUM_RELEASE "2.6.32"
#undef TARGET_EWOULDBLOCK
#define TARGET_EWOULDBLOCK TARGET_EAGAIN /* Operation would block */
#undef TARGET_EDEADLK #undef TARGET_EDEADLK
#define TARGET_EDEADLK 11 #define TARGET_EDEADLK 11
#undef TARGET_EAGAIN #undef TARGET_EAGAIN
#define TARGET_EAGAIN 35 #define TARGET_EAGAIN 35
#undef TARGET_EINPROGRESS #undef TARGET_EINPROGRESS
#define TARGET_EINPROGRESS 36 #define TARGET_EINPROGRESS 36
#undef TARGET_EALREADY #undef TARGET_EALREADY
#define TARGET_EALREADY 37 #define TARGET_EALREADY 37
#undef TARGET_ENOTSOCK #undef TARGET_ENOTSOCK
#define TARGET_ENOTSOCK 38 #define TARGET_ENOTSOCK 38
#undef TARGET_EDESTADDRREQ #undef TARGET_EDESTADDRREQ
#define TARGET_EDESTADDRREQ 39 #define TARGET_EDESTADDRREQ 39
#undef TARGET_EMSGSIZE #undef TARGET_EMSGSIZE
#define TARGET_EMSGSIZE 40 #define TARGET_EMSGSIZE 40
#undef TARGET_EPROTOTYPE #undef TARGET_EPROTOTYPE
#define TARGET_EPROTOTYPE 41 #define TARGET_EPROTOTYPE 41
#undef TARGET_ENOPROTOOPT #undef TARGET_ENOPROTOOPT
#define TARGET_ENOPROTOOPT 42 #define TARGET_ENOPROTOOPT 42
#undef TARGET_EPROTONOSUPPORT #undef TARGET_EPROTONOSUPPORT
#define TARGET_EPROTONOSUPPORT 43 #define TARGET_EPROTONOSUPPORT 43
#undef TARGET_ESOCKTNOSUPPORT #undef TARGET_ESOCKTNOSUPPORT
#define TARGET_ESOCKTNOSUPPORT 44 #define TARGET_ESOCKTNOSUPPORT 44
#undef TARGET_EOPNOTSUPP #undef TARGET_EOPNOTSUPP
#define TARGET_EOPNOTSUPP 45 #define TARGET_EOPNOTSUPP 45
#undef TARGET_EPFNOSUPPORT #undef TARGET_EPFNOSUPPORT
#define TARGET_EPFNOSUPPORT 46 #define TARGET_EPFNOSUPPORT 46
#undef TARGET_EAFNOSUPPORT #undef TARGET_EAFNOSUPPORT
#define TARGET_EAFNOSUPPORT 47 #define TARGET_EAFNOSUPPORT 47
#undef TARGET_EADDRINUSE #undef TARGET_EADDRINUSE
#define TARGET_EADDRINUSE 48 #define TARGET_EADDRINUSE 48
#undef TARGET_EADDRNOTAVAIL #undef TARGET_EADDRNOTAVAIL
#define TARGET_EADDRNOTAVAIL 49 #define TARGET_EADDRNOTAVAIL 49
#undef TARGET_ENETDOWN #undef TARGET_ENETDOWN
#define TARGET_ENETDOWN 50 #define TARGET_ENETDOWN 50
#undef TARGET_ENETUNREACH #undef TARGET_ENETUNREACH
#define TARGET_ENETUNREACH 51 #define TARGET_ENETUNREACH 51
#undef TARGET_ENETRESET #undef TARGET_ENETRESET
#define TARGET_ENETRESET 52 #define TARGET_ENETRESET 52
#undef TARGET_ECONNABORTED #undef TARGET_ECONNABORTED
#define TARGET_ECONNABORTED 53 #define TARGET_ECONNABORTED 53
#undef TARGET_ECONNRESET #undef TARGET_ECONNRESET
#define TARGET_ECONNRESET 54 #define TARGET_ECONNRESET 54
#undef TARGET_ENOBUFS #undef TARGET_ENOBUFS
#define TARGET_ENOBUFS 55 #define TARGET_ENOBUFS 55
#undef TARGET_EISCONN #undef TARGET_EISCONN
#define TARGET_EISCONN 56 #define TARGET_EISCONN 56
#undef TARGET_ENOTCONN #undef TARGET_ENOTCONN
#define TARGET_ENOTCONN 57 #define TARGET_ENOTCONN 57
#undef TARGET_ESHUTDOWN #undef TARGET_ESHUTDOWN
#define TARGET_ESHUTDOWN 58 #define TARGET_ESHUTDOWN 58
#undef TARGET_ETOOMANYREFS #undef TARGET_ETOOMANYREFS
#define TARGET_ETOOMANYREFS 59 #define TARGET_ETOOMANYREFS 59
#undef TARGET_ETIMEDOUT #undef TARGET_ETIMEDOUT
#define TARGET_ETIMEDOUT 60 #define TARGET_ETIMEDOUT 60
#undef TARGET_ECONNREFUSED #undef TARGET_ECONNREFUSED
#define TARGET_ECONNREFUSED 61 #define TARGET_ECONNREFUSED 61
#undef TARGET_ELOOP #undef TARGET_ELOOP
#define TARGET_ELOOP 62 #define TARGET_ELOOP 62
#undef TARGET_ENAMETOOLONG #undef TARGET_ENAMETOOLONG
#define TARGET_ENAMETOOLONG 63 #define TARGET_ENAMETOOLONG 63
#undef TARGET_EHOSTDOWN #undef TARGET_EHOSTDOWN
#define TARGET_EHOSTDOWN 64 #define TARGET_EHOSTDOWN 64
#undef TARGET_EHOSTUNREACH #undef TARGET_EHOSTUNREACH
#define TARGET_EHOSTUNREACH 65 #define TARGET_EHOSTUNREACH 65
#undef TARGET_ENOTEMPTY #undef TARGET_ENOTEMPTY
#define TARGET_ENOTEMPTY 66 #define TARGET_ENOTEMPTY 66
// Unused 67 /* Unused 67 */
#undef TARGET_EUSERS #undef TARGET_EUSERS
#define TARGET_EUSERS 68 #define TARGET_EUSERS 68
#undef TARGET_EDQUOT #undef TARGET_EDQUOT
#define TARGET_EDQUOT 69 #define TARGET_EDQUOT 69
#undef TARGET_ESTALE #undef TARGET_ESTALE
#define TARGET_ESTALE 70 #define TARGET_ESTALE 70
#undef TARGET_EREMOTE #undef TARGET_EREMOTE
#define TARGET_EREMOTE 71 #define TARGET_EREMOTE 71
// Unused 72-76 /* Unused 72-76 */
#undef TARGET_ENOLCK #undef TARGET_ENOLCK
#define TARGET_ENOLCK 77 #define TARGET_ENOLCK 77
#undef TARGET_ENOSYS #undef TARGET_ENOSYS
#define TARGET_ENOSYS 78 #define TARGET_ENOSYS 78
// Unused 79 /* Unused 79 */
#undef TARGET_ENOMSG #undef TARGET_ENOMSG
#define TARGET_ENOMSG 80 #define TARGET_ENOMSG 80
#undef TARGET_EIDRM #undef TARGET_EIDRM
#define TARGET_EIDRM 81 #define TARGET_EIDRM 81
#undef TARGET_ENOSR #undef TARGET_ENOSR
#define TARGET_ENOSR 82 #define TARGET_ENOSR 82
#undef TARGET_ETIME #undef TARGET_ETIME
#define TARGET_ETIME 83 #define TARGET_ETIME 83
#undef TARGET_EBADMSG #undef TARGET_EBADMSG
#define TARGET_EBADMSG 84 #define TARGET_EBADMSG 84
#undef TARGET_EPROTO #undef TARGET_EPROTO
#define TARGET_EPROTO 85 #define TARGET_EPROTO 85
#undef TARGET_ENODATA #undef TARGET_ENODATA
#define TARGET_ENODATA 86 #define TARGET_ENODATA 86
#undef TARGET_ENOSTR #undef TARGET_ENOSTR
#define TARGET_ENOSTR 87 #define TARGET_ENOSTR 87
#undef TARGET_ECHRNG #undef TARGET_ECHRNG
#define TARGET_ECHRNG 88 #define TARGET_ECHRNG 88
#undef TARGET_EL2NSYNC #undef TARGET_EL2NSYNC
#define TARGET_EL2NSYNC 89 #define TARGET_EL2NSYNC 89
#undef TARGET_EL3HLT #undef TARGET_EL3HLT
#define TARGET_EL3HLT 90 #define TARGET_EL3HLT 90
#undef TARGET_EL3RST #undef TARGET_EL3RST
#define TARGET_EL3RST 91 #define TARGET_EL3RST 91
#undef TARGET_ENOPKG #undef TARGET_ENOPKG
#define TARGET_ENOPKG 92 #define TARGET_ENOPKG 92
#undef TARGET_ELNRNG #undef TARGET_ELNRNG
#define TARGET_ELNRNG 93 #define TARGET_ELNRNG 93
#undef TARGET_EUNATCH #undef TARGET_EUNATCH
#define TARGET_EUNATCH 94 #define TARGET_EUNATCH 94
#undef TARGET_ENOCSI #undef TARGET_ENOCSI
#define TARGET_ENOCSI 95 #define TARGET_ENOCSI 95
#undef TARGET_EL2HLT #undef TARGET_EL2HLT
#define TARGET_EL2HLT 96 #define TARGET_EL2HLT 96
#undef TARGET_EBADE #undef TARGET_EBADE
#define TARGET_EBADE 97 #define TARGET_EBADE 97
#undef TARGET_EBADR #undef TARGET_EBADR
#define TARGET_EBADR 98 #define TARGET_EBADR 98
#undef TARGET_EXFULL #undef TARGET_EXFULL
#define TARGET_EXFULL 99 #define TARGET_EXFULL 99
#undef TARGET_ENOANO #undef TARGET_ENOANO
#define TARGET_ENOANO 100 #define TARGET_ENOANO 100
#undef TARGET_EBADRQC #undef TARGET_EBADRQC
#define TARGET_EBADRQC 101 #define TARGET_EBADRQC 101
#undef TARGET_EBADSLT #undef TARGET_EBADSLT
#define TARGET_EBADSLT 102 #define TARGET_EBADSLT 102
// Unused 103 /* Unused 103 */
#undef TARGET_EBFONT #undef TARGET_EBFONT
#define TARGET_EBFONT 104 #define TARGET_EBFONT 104
#undef TARGET_ENONET #undef TARGET_ENONET
#define TARGET_ENONET 105 #define TARGET_ENONET 105
#undef TARGET_ENOLINK #undef TARGET_ENOLINK
#define TARGET_ENOLINK 106 #define TARGET_ENOLINK 106
#undef TARGET_EADV #undef TARGET_EADV
#define TARGET_EADV 107 #define TARGET_EADV 107
#undef TARGET_ESRMNT #undef TARGET_ESRMNT
#define TARGET_ESRMNT 108 #define TARGET_ESRMNT 108
#undef TARGET_ECOMM #undef TARGET_ECOMM
#define TARGET_ECOMM 109 #define TARGET_ECOMM 109
#undef TARGET_EMULTIHOP #undef TARGET_EMULTIHOP
#define TARGET_EMULTIHOP 110 #define TARGET_EMULTIHOP 110
#undef TARGET_EDOTDOT #undef TARGET_EDOTDOT
#define TARGET_EDOTDOT 111 #define TARGET_EDOTDOT 111
#undef TARGET_EOVERFLOW #undef TARGET_EOVERFLOW
#define TARGET_EOVERFLOW 112 #define TARGET_EOVERFLOW 112
#undef TARGET_ENOTUNIQ #undef TARGET_ENOTUNIQ
#define TARGET_ENOTUNIQ 113 #define TARGET_ENOTUNIQ 113
#undef TARGET_EBADFD #undef TARGET_EBADFD
#define TARGET_EBADFD 114 #define TARGET_EBADFD 114
#undef TARGET_EREMCHG #undef TARGET_EREMCHG
#define TARGET_EREMCHG 115 #define TARGET_EREMCHG 115
#undef TARGET_EILSEQ #undef TARGET_EILSEQ
#define TARGET_EILSEQ 116 #define TARGET_EILSEQ 116
/* Same as default 117-121 */
// Same as default 117-121
#undef TARGET_ELIBACC #undef TARGET_ELIBACC
#define TARGET_ELIBACC 122 #define TARGET_ELIBACC 122
#undef TARGET_ELIBBAD #undef TARGET_ELIBBAD
#define TARGET_ELIBBAD 123 #define TARGET_ELIBBAD 123
#undef TARGET_ELIBSCN #undef TARGET_ELIBSCN
#define TARGET_ELIBSCN 124 #define TARGET_ELIBSCN 124
#undef TARGET_ELIBMAX #undef TARGET_ELIBMAX
#define TARGET_ELIBMAX 125 #define TARGET_ELIBMAX 125
#undef TARGET_ELIBEXEC #undef TARGET_ELIBEXEC
#define TARGET_ELIBEXEC 126 #define TARGET_ELIBEXEC 126
#undef TARGET_ERESTART #undef TARGET_ERESTART
#define TARGET_ERESTART 127 #define TARGET_ERESTART 127
#undef TARGET_ESTRPIPE #undef TARGET_ESTRPIPE
#define TARGET_ESTRPIPE 128 #define TARGET_ESTRPIPE 128
#undef TARGET_ENOMEDIUM #undef TARGET_ENOMEDIUM
#define TARGET_ENOMEDIUM 129 #define TARGET_ENOMEDIUM 129
#undef TARGET_EMEDIUMTYPE #undef TARGET_EMEDIUMTYPE
#define TARGET_EMEDIUMTYPE 130 #define TARGET_EMEDIUMTYPE 130
#undef TARGET_ECANCELED #undef TARGET_ECANCELED
#define TARGET_ECANCELED 131 #define TARGET_ECANCELED 131
#undef TARGET_ENOKEY #undef TARGET_ENOKEY
#define TARGET_ENOKEY 132 #define TARGET_ENOKEY 132
#undef TARGET_EKEYEXPIRED #undef TARGET_EKEYEXPIRED
#define TARGET_EKEYEXPIRED 133 #define TARGET_EKEYEXPIRED 133
#undef TARGET_EKEYREVOKED #undef TARGET_EKEYREVOKED
#define TARGET_EKEYREVOKED 134 #define TARGET_EKEYREVOKED 134
#undef TARGET_EKEYREJECTED #undef TARGET_EKEYREJECTED
#define TARGET_EKEYREJECTED 135 #define TARGET_EKEYREJECTED 135
#undef TARGET_EOWNERDEAD #undef TARGET_EOWNERDEAD
#define TARGET_EOWNERDEAD 136 #define TARGET_EOWNERDEAD 136
#undef TARGET_ENOTRECOVERABLE #undef TARGET_ENOTRECOVERABLE
#define TARGET_ENOTRECOVERABLE 137 #define TARGET_ENOTRECOVERABLE 137
#undef TARGET_ERFKILL #undef TARGET_ERFKILL
#define TARGET_ERFKILL 138 #define TARGET_ERFKILL 138
#undef TARGET_EHWPOISON #undef TARGET_EHWPOISON
#define TARGET_EHWPOISON 139 #define TARGET_EHWPOISON 139

View File

@ -1434,6 +1434,19 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs,
#define ELF_CLASS ELFCLASS64 #define ELF_CLASS ELFCLASS64
#endif #endif
#define ELF_HWCAP get_elf_hwcap()
static uint32_t get_elf_hwcap(void)
{
#define MISA_BIT(EXT) (1 << (EXT - 'A'))
RISCVCPU *cpu = RISCV_CPU(thread_cpu);
uint32_t mask = MISA_BIT('I') | MISA_BIT('M') | MISA_BIT('A')
| MISA_BIT('F') | MISA_BIT('D') | MISA_BIT('C');
return cpu->env.misa & mask;
#undef MISA_BIT
}
static inline void init_thread(struct target_pt_regs *regs, static inline void init_thread(struct target_pt_regs *regs,
struct image_info *infop) struct image_info *infop)
{ {

View File

@ -27,6 +27,8 @@ struct target_pt_regs {
#define TARGET_MCL_FUTURE 2 #define TARGET_MCL_FUTURE 2
#define TARGET_MCL_ONFAULT 4 #define TARGET_MCL_ONFAULT 4
#undef TARGET_EWOULDBLOCK
#define TARGET_EWOULDBLOCK TARGET_EAGAIN /* Operation would block */
#undef TARGET_ENOMSG #undef TARGET_ENOMSG
#define TARGET_ENOMSG 35 #define TARGET_ENOMSG 35
#undef TARGET_EIDRM #undef TARGET_EIDRM

View File

@ -1,59 +1,57 @@
/* Code for loading Linux executables. Mostly linux kernel code. */ /* Code for loading Linux executables. Mostly linux kernel code. */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu.h" #include "qemu.h"
#define NGROUPS 32 #define NGROUPS 32
/* ??? This should really be somewhere else. */ /* ??? This should really be somewhere else. */
abi_long memcpy_to_target(abi_ulong dest, const void *src, abi_long memcpy_to_target(abi_ulong dest, const void *src, unsigned long len)
unsigned long len)
{ {
void *host_ptr; void *host_ptr;
host_ptr = lock_user(VERIFY_WRITE, dest, len, 0); host_ptr = lock_user(VERIFY_WRITE, dest, len, 0);
if (!host_ptr) if (!host_ptr) {
return -TARGET_EFAULT; return -TARGET_EFAULT;
}
memcpy(host_ptr, src, len); memcpy(host_ptr, src, len);
unlock_user(host_ptr, dest, 1); unlock_user(host_ptr, dest, 1);
return 0; return 0;
} }
static int count(char ** vec) static int count(char **vec)
{ {
int i; int i;
for(i = 0; *vec; i++) { for (i = 0; *vec; i++) {
vec++; vec++;
} }
return i;
return(i);
} }
static int prepare_binprm(struct linux_binprm *bprm) static int prepare_binprm(struct linux_binprm *bprm)
{ {
struct stat st; struct stat st;
int mode; int mode;
int retval; int retval;
if(fstat(bprm->fd, &st) < 0) { if (fstat(bprm->fd, &st) < 0) {
return(-errno); return -errno;
} }
mode = st.st_mode; mode = st.st_mode;
if(!S_ISREG(mode)) { /* Must be regular file */ if (!S_ISREG(mode)) { /* Must be regular file */
return(-EACCES); return -EACCES;
} }
if(!(mode & 0111)) { /* Must have at least one execute bit set */ if (!(mode & 0111)) { /* Must have at least one execute bit set */
return(-EACCES); return -EACCES;
} }
bprm->e_uid = geteuid(); bprm->e_uid = geteuid();
bprm->e_gid = getegid(); bprm->e_gid = getegid();
/* Set-uid? */ /* Set-uid? */
if(mode & S_ISUID) { if (mode & S_ISUID) {
bprm->e_uid = st.st_uid; bprm->e_uid = st.st_uid;
} }
@ -125,8 +123,8 @@ abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp,
} }
int loader_exec(int fdexec, const char *filename, char **argv, char **envp, int loader_exec(int fdexec, const char *filename, char **argv, char **envp,
struct target_pt_regs * regs, struct image_info *infop, struct target_pt_regs *regs, struct image_info *infop,
struct linux_binprm *bprm) struct linux_binprm *bprm)
{ {
int retval; int retval;
@ -139,7 +137,7 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp,
retval = prepare_binprm(bprm); retval = prepare_binprm(bprm);
if(retval>=0) { if (retval >= 0) {
if (bprm->buf[0] == 0x7f if (bprm->buf[0] == 0x7f
&& bprm->buf[1] == 'E' && bprm->buf[1] == 'E'
&& bprm->buf[2] == 'L' && bprm->buf[2] == 'L'
@ -157,11 +155,11 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp,
} }
} }
if(retval>=0) { if (retval >= 0) {
/* success. Initialize important registers */ /* success. Initialize important registers */
do_init_thread(regs, infop); do_init_thread(regs, infop);
return retval; return retval;
} }
return(retval); return retval;
} }

View File

@ -21,6 +21,8 @@ struct target_pt_regs {
}; };
/* Target errno definitions taken from asm-mips/errno.h */ /* Target errno definitions taken from asm-mips/errno.h */
#undef TARGET_EWOULDBLOCK
#define TARGET_EWOULDBLOCK TARGET_EAGAIN /* Operation would block */
#undef TARGET_ENOMSG #undef TARGET_ENOMSG
#define TARGET_ENOMSG 35 /* Identifier removed */ #define TARGET_ENOMSG 35 /* Identifier removed */
#undef TARGET_EIDRM #undef TARGET_EIDRM

View File

@ -18,6 +18,8 @@ struct target_pt_regs {
}; };
/* Target errno definitions taken from asm-mips/errno.h */ /* Target errno definitions taken from asm-mips/errno.h */
#undef TARGET_EWOULDBLOCK
#define TARGET_EWOULDBLOCK TARGET_EAGAIN /* Operation would block */
#undef TARGET_ENOMSG #undef TARGET_ENOMSG
#define TARGET_ENOMSG 35 /* Identifier removed */ #define TARGET_ENOMSG 35 /* Identifier removed */
#undef TARGET_EIDRM #undef TARGET_EIDRM

View File

@ -519,112 +519,112 @@ static uint16_t target_to_host_errno_table[ERRNO_TABLE_SIZE] = {
* minus the errnos that are not actually generic to all archs. * minus the errnos that are not actually generic to all archs.
*/ */
static uint16_t host_to_target_errno_table[ERRNO_TABLE_SIZE] = { static uint16_t host_to_target_errno_table[ERRNO_TABLE_SIZE] = {
[EAGAIN] = TARGET_EAGAIN, [EAGAIN] = TARGET_EAGAIN,
[EIDRM] = TARGET_EIDRM, [EIDRM] = TARGET_EIDRM,
[ECHRNG] = TARGET_ECHRNG, [ECHRNG] = TARGET_ECHRNG,
[EL2NSYNC] = TARGET_EL2NSYNC, [EL2NSYNC] = TARGET_EL2NSYNC,
[EL3HLT] = TARGET_EL3HLT, [EL3HLT] = TARGET_EL3HLT,
[EL3RST] = TARGET_EL3RST, [EL3RST] = TARGET_EL3RST,
[ELNRNG] = TARGET_ELNRNG, [ELNRNG] = TARGET_ELNRNG,
[EUNATCH] = TARGET_EUNATCH, [EUNATCH] = TARGET_EUNATCH,
[ENOCSI] = TARGET_ENOCSI, [ENOCSI] = TARGET_ENOCSI,
[EL2HLT] = TARGET_EL2HLT, [EL2HLT] = TARGET_EL2HLT,
[EDEADLK] = TARGET_EDEADLK, [EDEADLK] = TARGET_EDEADLK,
[ENOLCK] = TARGET_ENOLCK, [ENOLCK] = TARGET_ENOLCK,
[EBADE] = TARGET_EBADE, [EBADE] = TARGET_EBADE,
[EBADR] = TARGET_EBADR, [EBADR] = TARGET_EBADR,
[EXFULL] = TARGET_EXFULL, [EXFULL] = TARGET_EXFULL,
[ENOANO] = TARGET_ENOANO, [ENOANO] = TARGET_ENOANO,
[EBADRQC] = TARGET_EBADRQC, [EBADRQC] = TARGET_EBADRQC,
[EBADSLT] = TARGET_EBADSLT, [EBADSLT] = TARGET_EBADSLT,
[EBFONT] = TARGET_EBFONT, [EBFONT] = TARGET_EBFONT,
[ENOSTR] = TARGET_ENOSTR, [ENOSTR] = TARGET_ENOSTR,
[ENODATA] = TARGET_ENODATA, [ENODATA] = TARGET_ENODATA,
[ETIME] = TARGET_ETIME, [ETIME] = TARGET_ETIME,
[ENOSR] = TARGET_ENOSR, [ENOSR] = TARGET_ENOSR,
[ENONET] = TARGET_ENONET, [ENONET] = TARGET_ENONET,
[ENOPKG] = TARGET_ENOPKG, [ENOPKG] = TARGET_ENOPKG,
[EREMOTE] = TARGET_EREMOTE, [EREMOTE] = TARGET_EREMOTE,
[ENOLINK] = TARGET_ENOLINK, [ENOLINK] = TARGET_ENOLINK,
[EADV] = TARGET_EADV, [EADV] = TARGET_EADV,
[ESRMNT] = TARGET_ESRMNT, [ESRMNT] = TARGET_ESRMNT,
[ECOMM] = TARGET_ECOMM, [ECOMM] = TARGET_ECOMM,
[EPROTO] = TARGET_EPROTO, [EPROTO] = TARGET_EPROTO,
[EDOTDOT] = TARGET_EDOTDOT, [EDOTDOT] = TARGET_EDOTDOT,
[EMULTIHOP] = TARGET_EMULTIHOP, [EMULTIHOP] = TARGET_EMULTIHOP,
[EBADMSG] = TARGET_EBADMSG, [EBADMSG] = TARGET_EBADMSG,
[ENAMETOOLONG] = TARGET_ENAMETOOLONG, [ENAMETOOLONG] = TARGET_ENAMETOOLONG,
[EOVERFLOW] = TARGET_EOVERFLOW, [EOVERFLOW] = TARGET_EOVERFLOW,
[ENOTUNIQ] = TARGET_ENOTUNIQ, [ENOTUNIQ] = TARGET_ENOTUNIQ,
[EBADFD] = TARGET_EBADFD, [EBADFD] = TARGET_EBADFD,
[EREMCHG] = TARGET_EREMCHG, [EREMCHG] = TARGET_EREMCHG,
[ELIBACC] = TARGET_ELIBACC, [ELIBACC] = TARGET_ELIBACC,
[ELIBBAD] = TARGET_ELIBBAD, [ELIBBAD] = TARGET_ELIBBAD,
[ELIBSCN] = TARGET_ELIBSCN, [ELIBSCN] = TARGET_ELIBSCN,
[ELIBMAX] = TARGET_ELIBMAX, [ELIBMAX] = TARGET_ELIBMAX,
[ELIBEXEC] = TARGET_ELIBEXEC, [ELIBEXEC] = TARGET_ELIBEXEC,
[EILSEQ] = TARGET_EILSEQ, [EILSEQ] = TARGET_EILSEQ,
[ENOSYS] = TARGET_ENOSYS, [ENOSYS] = TARGET_ENOSYS,
[ELOOP] = TARGET_ELOOP, [ELOOP] = TARGET_ELOOP,
[ERESTART] = TARGET_ERESTART, [ERESTART] = TARGET_ERESTART,
[ESTRPIPE] = TARGET_ESTRPIPE, [ESTRPIPE] = TARGET_ESTRPIPE,
[ENOTEMPTY] = TARGET_ENOTEMPTY, [ENOTEMPTY] = TARGET_ENOTEMPTY,
[EUSERS] = TARGET_EUSERS, [EUSERS] = TARGET_EUSERS,
[ENOTSOCK] = TARGET_ENOTSOCK, [ENOTSOCK] = TARGET_ENOTSOCK,
[EDESTADDRREQ] = TARGET_EDESTADDRREQ, [EDESTADDRREQ] = TARGET_EDESTADDRREQ,
[EMSGSIZE] = TARGET_EMSGSIZE, [EMSGSIZE] = TARGET_EMSGSIZE,
[EPROTOTYPE] = TARGET_EPROTOTYPE, [EPROTOTYPE] = TARGET_EPROTOTYPE,
[ENOPROTOOPT] = TARGET_ENOPROTOOPT, [ENOPROTOOPT] = TARGET_ENOPROTOOPT,
[EPROTONOSUPPORT] = TARGET_EPROTONOSUPPORT, [EPROTONOSUPPORT] = TARGET_EPROTONOSUPPORT,
[ESOCKTNOSUPPORT] = TARGET_ESOCKTNOSUPPORT, [ESOCKTNOSUPPORT] = TARGET_ESOCKTNOSUPPORT,
[EOPNOTSUPP] = TARGET_EOPNOTSUPP, [EOPNOTSUPP] = TARGET_EOPNOTSUPP,
[EPFNOSUPPORT] = TARGET_EPFNOSUPPORT, [EPFNOSUPPORT] = TARGET_EPFNOSUPPORT,
[EAFNOSUPPORT] = TARGET_EAFNOSUPPORT, [EAFNOSUPPORT] = TARGET_EAFNOSUPPORT,
[EADDRINUSE] = TARGET_EADDRINUSE, [EADDRINUSE] = TARGET_EADDRINUSE,
[EADDRNOTAVAIL] = TARGET_EADDRNOTAVAIL, [EADDRNOTAVAIL] = TARGET_EADDRNOTAVAIL,
[ENETDOWN] = TARGET_ENETDOWN, [ENETDOWN] = TARGET_ENETDOWN,
[ENETUNREACH] = TARGET_ENETUNREACH, [ENETUNREACH] = TARGET_ENETUNREACH,
[ENETRESET] = TARGET_ENETRESET, [ENETRESET] = TARGET_ENETRESET,
[ECONNABORTED] = TARGET_ECONNABORTED, [ECONNABORTED] = TARGET_ECONNABORTED,
[ECONNRESET] = TARGET_ECONNRESET, [ECONNRESET] = TARGET_ECONNRESET,
[ENOBUFS] = TARGET_ENOBUFS, [ENOBUFS] = TARGET_ENOBUFS,
[EISCONN] = TARGET_EISCONN, [EISCONN] = TARGET_EISCONN,
[ENOTCONN] = TARGET_ENOTCONN, [ENOTCONN] = TARGET_ENOTCONN,
[EUCLEAN] = TARGET_EUCLEAN, [EUCLEAN] = TARGET_EUCLEAN,
[ENOTNAM] = TARGET_ENOTNAM, [ENOTNAM] = TARGET_ENOTNAM,
[ENAVAIL] = TARGET_ENAVAIL, [ENAVAIL] = TARGET_ENAVAIL,
[EISNAM] = TARGET_EISNAM, [EISNAM] = TARGET_EISNAM,
[EREMOTEIO] = TARGET_EREMOTEIO, [EREMOTEIO] = TARGET_EREMOTEIO,
[EDQUOT] = TARGET_EDQUOT, [EDQUOT] = TARGET_EDQUOT,
[ESHUTDOWN] = TARGET_ESHUTDOWN, [ESHUTDOWN] = TARGET_ESHUTDOWN,
[ETOOMANYREFS] = TARGET_ETOOMANYREFS, [ETOOMANYREFS] = TARGET_ETOOMANYREFS,
[ETIMEDOUT] = TARGET_ETIMEDOUT, [ETIMEDOUT] = TARGET_ETIMEDOUT,
[ECONNREFUSED] = TARGET_ECONNREFUSED, [ECONNREFUSED] = TARGET_ECONNREFUSED,
[EHOSTDOWN] = TARGET_EHOSTDOWN, [EHOSTDOWN] = TARGET_EHOSTDOWN,
[EHOSTUNREACH] = TARGET_EHOSTUNREACH, [EHOSTUNREACH] = TARGET_EHOSTUNREACH,
[EALREADY] = TARGET_EALREADY, [EALREADY] = TARGET_EALREADY,
[EINPROGRESS] = TARGET_EINPROGRESS, [EINPROGRESS] = TARGET_EINPROGRESS,
[ESTALE] = TARGET_ESTALE, [ESTALE] = TARGET_ESTALE,
[ECANCELED] = TARGET_ECANCELED, [ECANCELED] = TARGET_ECANCELED,
[ENOMEDIUM] = TARGET_ENOMEDIUM, [ENOMEDIUM] = TARGET_ENOMEDIUM,
[EMEDIUMTYPE] = TARGET_EMEDIUMTYPE, [EMEDIUMTYPE] = TARGET_EMEDIUMTYPE,
#ifdef ENOKEY #ifdef ENOKEY
[ENOKEY] = TARGET_ENOKEY, [ENOKEY] = TARGET_ENOKEY,
#endif #endif
#ifdef EKEYEXPIRED #ifdef EKEYEXPIRED
[EKEYEXPIRED] = TARGET_EKEYEXPIRED, [EKEYEXPIRED] = TARGET_EKEYEXPIRED,
#endif #endif
#ifdef EKEYREVOKED #ifdef EKEYREVOKED
[EKEYREVOKED] = TARGET_EKEYREVOKED, [EKEYREVOKED] = TARGET_EKEYREVOKED,
#endif #endif
#ifdef EKEYREJECTED #ifdef EKEYREJECTED
[EKEYREJECTED] = TARGET_EKEYREJECTED, [EKEYREJECTED] = TARGET_EKEYREJECTED,
#endif #endif
#ifdef EOWNERDEAD #ifdef EOWNERDEAD
[EOWNERDEAD] = TARGET_EOWNERDEAD, [EOWNERDEAD] = TARGET_EOWNERDEAD,
#endif #endif
#ifdef ENOTRECOVERABLE #ifdef ENOTRECOVERABLE
[ENOTRECOVERABLE] = TARGET_ENOTRECOVERABLE, [ENOTRECOVERABLE] = TARGET_ENOTRECOVERABLE,
#endif #endif
#ifdef ENOMSG #ifdef ENOMSG
[ENOMSG] = TARGET_ENOMSG, [ENOMSG] = TARGET_ENOMSG,
@ -7956,6 +7956,9 @@ static int open_self_stat(void *cpu_env, int fd)
gchar *bin = g_strrstr(ts->bprm->argv[0], "/"); gchar *bin = g_strrstr(ts->bprm->argv[0], "/");
bin = bin ? bin + 1 : ts->bprm->argv[0]; bin = bin ? bin + 1 : ts->bprm->argv[0];
g_string_printf(buf, "(%.15s) ", bin); g_string_printf(buf, "(%.15s) ", bin);
} else if (i == 3) {
/* ppid */
g_string_printf(buf, FMT_pid " ", getppid());
} else if (i == 27) { } else if (i == 27) {
/* stack bottom */ /* stack bottom */
g_string_printf(buf, TARGET_ABI_FMT_ld " ", ts->info->start_stack); g_string_printf(buf, TARGET_ABI_FMT_ld " ", ts->info->start_stack);

View File

@ -164,6 +164,7 @@ endif
multiprocess_allowed = targetos == 'linux' and not get_option('multiprocess').disabled() multiprocess_allowed = targetos == 'linux' and not get_option('multiprocess').disabled()
libm = cc.find_library('m', required: false) libm = cc.find_library('m', required: false)
threads = dependency('threads')
util = cc.find_library('util', required: false) util = cc.find_library('util', required: false)
winmm = [] winmm = []
socket = [] socket = []
@ -327,15 +328,16 @@ if have_system or have_tools
endif endif
libaio = cc.find_library('aio', required: false) libaio = cc.find_library('aio', required: false)
zlib = dependency('zlib', required: true, kwargs: static_kwargs) zlib = dependency('zlib', required: true, kwargs: static_kwargs)
linux_io_uring = not_found linux_io_uring = not_found
if 'CONFIG_LINUX_IO_URING' in config_host if not get_option('linux_io_uring').auto() or have_block
linux_io_uring = declare_dependency(compile_args: config_host['LINUX_IO_URING_CFLAGS'].split(), linux_io_uring = dependency('liburing', required: get_option('linux_io_uring'),
link_args: config_host['LINUX_IO_URING_LIBS'].split()) method: 'pkg-config', kwargs: static_kwargs)
endif endif
libxml2 = not_found libxml2 = not_found
if 'CONFIG_LIBXML2' in config_host if not get_option('libxml2').auto() or have_block
libxml2 = declare_dependency(compile_args: config_host['LIBXML2_CFLAGS'].split(), libxml2 = dependency('libxml-2.0', required: get_option('libxml2'),
link_args: config_host['LIBXML2_LIBS'].split()) method: 'pkg-config', kwargs: static_kwargs)
endif endif
libnfs = not_found libnfs = not_found
if not get_option('libnfs').auto() or have_block if not get_option('libnfs').auto() or have_block
@ -471,9 +473,11 @@ if 'CONFIG_GBM' in config_host
link_args: config_host['GBM_LIBS'].split()) link_args: config_host['GBM_LIBS'].split())
endif endif
virgl = not_found virgl = not_found
if 'CONFIG_VIRGL' in config_host if not get_option('virglrenderer').auto() or have_system
virgl = declare_dependency(compile_args: config_host['VIRGL_CFLAGS'].split(), virgl = dependency('virglrenderer',
link_args: config_host['VIRGL_LIBS'].split()) method: 'pkg-config',
required: get_option('virglrenderer'),
kwargs: static_kwargs)
endif endif
curl = not_found curl = not_found
if not get_option('curl').auto() or have_block if not get_option('curl').auto() or have_block
@ -860,6 +864,7 @@ endif
gtk = not_found gtk = not_found
gtkx11 = not_found gtkx11 = not_found
vte = not_found
if not get_option('gtk').auto() or (have_system and not cocoa.found()) if not get_option('gtk').auto() or (have_system and not cocoa.found())
gtk = dependency('gtk+-3.0', version: '>=3.22.0', gtk = dependency('gtk+-3.0', version: '>=3.22.0',
method: 'pkg-config', method: 'pkg-config',
@ -871,14 +876,16 @@ if not get_option('gtk').auto() or (have_system and not cocoa.found())
required: false, required: false,
kwargs: static_kwargs) kwargs: static_kwargs)
gtk = declare_dependency(dependencies: [gtk, gtkx11]) gtk = declare_dependency(dependencies: [gtk, gtkx11])
if not get_option('vte').auto() or have_system
vte = dependency('vte-2.91',
method: 'pkg-config',
required: get_option('vte'),
kwargs: static_kwargs)
endif
endif endif
endif endif
vte = not_found
if 'CONFIG_VTE' in config_host
vte = declare_dependency(compile_args: config_host['VTE_CFLAGS'].split(),
link_args: config_host['VTE_LIBS'].split())
endif
x11 = not_found x11 = not_found
if gtkx11.found() if gtkx11.found()
x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found(), x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found(),
@ -1001,13 +1008,15 @@ if not get_option('libusb').auto() or have_system
endif endif
libpmem = not_found libpmem = not_found
if 'CONFIG_LIBPMEM' in config_host if not get_option('libpmem').auto() or have_system
libpmem = declare_dependency(compile_args: config_host['LIBPMEM_CFLAGS'].split(), libpmem = dependency('libpmem', required: get_option('libpmem'),
link_args: config_host['LIBPMEM_LIBS'].split()) method: 'pkg-config', kwargs: static_kwargs)
endif endif
libdaxctl = not_found libdaxctl = not_found
if 'CONFIG_LIBDAXCTL' in config_host if not get_option('libdaxctl').auto() or have_system
libdaxctl = declare_dependency(link_args: config_host['LIBDAXCTL_LIBS'].split()) libdaxctl = dependency('libdaxctl', required: get_option('libdaxctl'),
version: '>=57', method: 'pkg-config',
kwargs: static_kwargs)
endif endif
tasn1 = not_found tasn1 = not_found
if gnutls.found() if gnutls.found()
@ -1042,10 +1051,12 @@ endif
# Check whether the glibc provides statx() # Check whether the glibc provides statx()
statx_test = ''' gnu_source_prefix = '''
#ifndef _GNU_SOURCE #ifndef _GNU_SOURCE
#define _GNU_SOURCE #define _GNU_SOURCE
#endif #endif
'''
statx_test = gnu_source_prefix + '''
#include <sys/stat.h> #include <sys/stat.h>
int main(void) { int main(void) {
struct statx statxbuf; struct statx statxbuf;
@ -1208,6 +1219,7 @@ if glusterfs.found()
config_host_data.set('CONFIG_GLUSTERFS_IOCB_HAS_STAT', glusterfs_iocb_has_stat) config_host_data.set('CONFIG_GLUSTERFS_IOCB_HAS_STAT', glusterfs_iocb_has_stat)
endif endif
config_host_data.set('CONFIG_GTK', gtk.found()) config_host_data.set('CONFIG_GTK', gtk.found())
config_host_data.set('CONFIG_VTE', vte.found())
config_host_data.set('CONFIG_LIBATTR', have_old_libattr) config_host_data.set('CONFIG_LIBATTR', have_old_libattr)
config_host_data.set('CONFIG_LIBCAP_NG', libcap_ng.found()) config_host_data.set('CONFIG_LIBCAP_NG', libcap_ng.found())
config_host_data.set('CONFIG_EBPF', libbpf.found()) config_host_data.set('CONFIG_EBPF', libbpf.found())
@ -1244,16 +1256,150 @@ config_host_data.set('QEMU_VERSION_MAJOR', meson.project_version().split('.')[0]
config_host_data.set('QEMU_VERSION_MINOR', meson.project_version().split('.')[1]) config_host_data.set('QEMU_VERSION_MINOR', meson.project_version().split('.')[1])
config_host_data.set('QEMU_VERSION_MICRO', meson.project_version().split('.')[2]) config_host_data.set('QEMU_VERSION_MICRO', meson.project_version().split('.')[2])
config_host_data.set('HAVE_HOST_BLOCK_DEVICE', have_host_block_device)
# has_header
config_host_data.set('CONFIG_EPOLL', cc.has_header('sys/epoll.h'))
config_host_data.set('CONFIG_LINUX_MAGIC_H', cc.has_header('linux/magic.h'))
config_host_data.set('CONFIG_VALGRIND_H', cc.has_header('valgrind/valgrind.h'))
config_host_data.set('HAVE_BTRFS_H', cc.has_header('linux/btrfs.h')) config_host_data.set('HAVE_BTRFS_H', cc.has_header('linux/btrfs.h'))
config_host_data.set('HAVE_DRM_H', cc.has_header('libdrm/drm.h')) config_host_data.set('HAVE_DRM_H', cc.has_header('libdrm/drm.h'))
config_host_data.set('HAVE_PTY_H', cc.has_header('pty.h')) config_host_data.set('HAVE_PTY_H', cc.has_header('pty.h'))
config_host_data.set('HAVE_SYS_DISK_H', cc.has_header('sys/disk.h'))
config_host_data.set('HAVE_SYS_IOCCOM_H', cc.has_header('sys/ioccom.h')) config_host_data.set('HAVE_SYS_IOCCOM_H', cc.has_header('sys/ioccom.h'))
config_host_data.set('HAVE_SYS_KCOV_H', cc.has_header('sys/kcov.h')) config_host_data.set('HAVE_SYS_KCOV_H', cc.has_header('sys/kcov.h'))
config_host_data.set('HAVE_SYSTEM_FUNCTION', cc.has_function('system', prefix: '#include <stdlib.h>'))
config_host_data.set('HAVE_HOST_BLOCK_DEVICE', have_host_block_device)
config_host_data.set('HAVE_SYS_DISK_H', cc.has_header('sys/disk.h'))
# has_function
config_host_data.set('CONFIG_ACCEPT4', cc.has_function('accept4'))
config_host_data.set('CONFIG_CLOCK_ADJTIME', cc.has_function('clock_adjtime'))
config_host_data.set('CONFIG_DUP3', cc.has_function('dup3'))
config_host_data.set('CONFIG_FALLOCATE', cc.has_function('fallocate'))
config_host_data.set('CONFIG_POSIX_FALLOCATE', cc.has_function('posix_fallocate'))
config_host_data.set('CONFIG_POSIX_MEMALIGN', cc.has_function('posix_memalign'))
config_host_data.set('CONFIG_PPOLL', cc.has_function('ppoll'))
config_host_data.set('CONFIG_PREADV', cc.has_function('preadv', prefix: '#include <sys/uio.h>')) config_host_data.set('CONFIG_PREADV', cc.has_function('preadv', prefix: '#include <sys/uio.h>'))
config_host_data.set('CONFIG_SEM_TIMEDWAIT', cc.has_function('sem_timedwait', dependencies: threads))
config_host_data.set('CONFIG_SENDFILE', cc.has_function('sendfile'))
config_host_data.set('CONFIG_SETNS', cc.has_function('setns') and cc.has_function('unshare'))
config_host_data.set('CONFIG_SYNCFS', cc.has_function('syncfs'))
config_host_data.set('CONFIG_SYNC_FILE_RANGE', cc.has_function('sync_file_range'))
config_host_data.set('CONFIG_TIMERFD', cc.has_function('timerfd_create'))
config_host_data.set('HAVE_COPY_FILE_RANGE', cc.has_function('copy_file_range'))
config_host_data.set('HAVE_OPENPTY', cc.has_function('openpty', dependencies: util))
config_host_data.set('HAVE_STRCHRNUL', cc.has_function('strchrnul'))
config_host_data.set('HAVE_SYSTEM_FUNCTION', cc.has_function('system', prefix: '#include <stdlib.h>'))
# has_header_symbol
config_host_data.set('CONFIG_BYTESWAP_H',
cc.has_header_symbol('byteswap.h', 'bswap_32'))
config_host_data.set('CONFIG_EPOLL_CREATE1',
cc.has_header_symbol('sys/epoll.h', 'epoll_create1'))
config_host_data.set('CONFIG_HAS_ENVIRON',
cc.has_header_symbol('unistd.h', 'environ', prefix: gnu_source_prefix))
config_host_data.set('CONFIG_FALLOCATE_PUNCH_HOLE',
cc.has_header_symbol('linux/falloc.h', 'FALLOC_FL_PUNCH_HOLE') and
cc.has_header_symbol('linux/falloc.h', 'FALLOC_FL_KEEP_SIZE'))
config_host_data.set('CONFIG_FALLOCATE_ZERO_RANGE',
cc.has_header_symbol('linux/falloc.h', 'FALLOC_FL_ZERO_RANGE'))
config_host_data.set('CONFIG_FIEMAP',
cc.has_header('linux/fiemap.h') and
cc.has_header_symbol('linux/fs.h', 'FS_IOC_FIEMAP'))
config_host_data.set('CONFIG_GETRANDOM',
cc.has_function('getrandom') and
cc.has_header_symbol('sys/random.h', 'GRND_NONBLOCK'))
config_host_data.set('CONFIG_INOTIFY',
cc.has_header_symbol('sys/inotify.h', 'inotify_init'))
config_host_data.set('CONFIG_INOTIFY1',
cc.has_header_symbol('sys/inotify.h', 'inotify_init1'))
config_host_data.set('CONFIG_MACHINE_BSWAP_H',
cc.has_header_symbol('machine/bswap.h', 'bswap32',
prefix: '''#include <sys/endian.h>
#include <sys/types.h>'''))
config_host_data.set('CONFIG_PRCTL_PR_SET_TIMERSLACK',
cc.has_header_symbol('sys/prctl.h', 'PR_SET_TIMERSLACK'))
config_host_data.set('CONFIG_RTNETLINK',
cc.has_header_symbol('linux/rtnetlink.h', 'IFLA_PROTO_DOWN'))
config_host_data.set('CONFIG_SYSMACROS',
cc.has_header_symbol('sys/sysmacros.h', 'makedev'))
config_host_data.set('HAVE_OPTRESET',
cc.has_header_symbol('getopt.h', 'optreset'))
config_host_data.set('HAVE_UTMPX',
cc.has_header_symbol('utmpx.h', 'struct utmpx'))
# has_member
config_host_data.set('HAVE_SIGEV_NOTIFY_THREAD_ID',
cc.has_member('struct sigevent', 'sigev_notify_thread_id',
prefix: '#include <signal.h>'))
config_host_data.set('HAVE_STRUCT_STAT_ST_ATIM',
cc.has_member('struct stat', 'st_atim',
prefix: '#include <sys/stat.h>'))
config_host_data.set('CONFIG_EVENTFD', cc.compiles('''
#include <sys/eventfd.h>
int main(void) { return eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); }'''))
config_host_data.set('CONFIG_FDATASYNC', cc.compiles(gnu_source_prefix + '''
#include <unistd.h>
int main(void) {
#if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0
return fdatasync(0);
#else
#error Not supported
#endif
}'''))
config_host_data.set('CONFIG_MADVISE', cc.compiles(gnu_source_prefix + '''
#include <sys/types.h>
#include <sys/mman.h>
#include <stddef.h>
int main(void) { return madvise(NULL, 0, MADV_DONTNEED); }'''))
config_host_data.set('CONFIG_MEMFD', cc.compiles(gnu_source_prefix + '''
#include <sys/mman.h>
int main(void) { return memfd_create("foo", MFD_ALLOW_SEALING); }'''))
config_host_data.set('CONFIG_OPEN_BY_HANDLE', cc.compiles(gnu_source_prefix + '''
#include <fcntl.h>
#if !defined(AT_EMPTY_PATH)
# error missing definition
#else
int main(void) { struct file_handle fh; return open_by_handle_at(0, &fh, 0); }
#endif'''))
config_host_data.set('CONFIG_PIPE2', cc.compiles(gnu_source_prefix + '''
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
int pipefd[2];
return pipe2(pipefd, O_CLOEXEC);
}'''))
config_host_data.set('CONFIG_POSIX_MADVISE', cc.compiles(gnu_source_prefix + '''
#include <sys/mman.h>
#include <stddef.h>
int main(void) { return posix_madvise(NULL, 0, POSIX_MADV_DONTNEED); }'''))
config_host_data.set('CONFIG_SIGNALFD', cc.compiles(gnu_source_prefix + '''
#include <unistd.h>
#include <sys/syscall.h>
#include <signal.h>
int main(void) { return syscall(SYS_signalfd, -1, NULL, _NSIG / 8); }'''))
config_host_data.set('CONFIG_SPLICE', cc.compiles(gnu_source_prefix + '''
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
int main(void)
{
int len, fd = 0;
len = tee(STDIN_FILENO, STDOUT_FILENO, INT_MAX, SPLICE_F_NONBLOCK);
splice(STDIN_FILENO, NULL, fd, NULL, len, SPLICE_F_MOVE);
return 0;
}'''))
# Some versions of Mac OS X incorrectly define SIZE_MAX
config_host_data.set('HAVE_BROKEN_SIZE_MAX', not cc.compiles('''
#include <stdint.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
return printf("%zu", SIZE_MAX);
}''', args: ['-Werror']))
ignored = ['CONFIG_QEMU_INTERP_PREFIX'] # actually per-target ignored = ['CONFIG_QEMU_INTERP_PREFIX'] # actually per-target
arrays = ['CONFIG_AUDIO_DRIVERS', 'CONFIG_BDRV_RW_WHITELIST', 'CONFIG_BDRV_RO_WHITELIST'] arrays = ['CONFIG_AUDIO_DRIVERS', 'CONFIG_BDRV_RW_WHITELIST', 'CONFIG_BDRV_RO_WHITELIST']
@ -1322,10 +1468,11 @@ if link_language == 'cpp'
} }
endif endif
have_ivshmem = config_host_data.get('CONFIG_EVENTFD')
host_kconfig = \ host_kconfig = \
('CONFIG_TPM' in config_host ? ['CONFIG_TPM=y'] : []) + \ ('CONFIG_TPM' in config_host ? ['CONFIG_TPM=y'] : []) + \
('CONFIG_SPICE' in config_host ? ['CONFIG_SPICE=y'] : []) + \ ('CONFIG_SPICE' in config_host ? ['CONFIG_SPICE=y'] : []) + \
('CONFIG_IVSHMEM' in config_host ? ['CONFIG_IVSHMEM=y'] : []) + \ (have_ivshmem ? ['CONFIG_IVSHMEM=y'] : []) + \
('CONFIG_OPENGL' in config_host ? ['CONFIG_OPENGL=y'] : []) + \ ('CONFIG_OPENGL' in config_host ? ['CONFIG_OPENGL=y'] : []) + \
(x11.found() ? ['CONFIG_X11=y'] : []) + \ (x11.found() ? ['CONFIG_X11=y'] : []) + \
('CONFIG_VHOST_USER' in config_host ? ['CONFIG_VHOST_USER=y'] : []) + \ ('CONFIG_VHOST_USER' in config_host ? ['CONFIG_VHOST_USER=y'] : []) + \
@ -1987,7 +2134,7 @@ util_ss.add_all(trace_ss)
util_ss = util_ss.apply(config_all, strict: false) util_ss = util_ss.apply(config_all, strict: false)
libqemuutil = static_library('qemuutil', libqemuutil = static_library('qemuutil',
sources: util_ss.sources() + stub_ss.sources() + genh, sources: util_ss.sources() + stub_ss.sources() + genh,
dependencies: [util_ss.dependencies(), libm, glib, socket, malloc, pixman]) dependencies: [util_ss.dependencies(), libm, threads, glib, socket, malloc, pixman])
qemuutil = declare_dependency(link_with: libqemuutil, qemuutil = declare_dependency(link_with: libqemuutil,
sources: genh + version_res) sources: genh + version_res)
@ -2360,8 +2507,7 @@ foreach target : target_dirs
endif endif
foreach exe: execs foreach exe: execs
exe_name = exe['name'] exe_name = exe['name']
exe_sign = 'CONFIG_HVF' in config_target if targetos == 'darwin'
if exe_sign
exe_name += '-unsigned' exe_name += '-unsigned'
endif endif
@ -2375,7 +2521,13 @@ foreach target : target_dirs
link_args: link_args, link_args: link_args,
gui_app: exe['gui']) gui_app: exe['gui'])
if exe_sign if 'CONFIG_HVF' in config_target
entitlements = meson.current_source_dir() / 'accel/hvf/entitlements.plist'
else
entitlements = '/dev/null'
endif
if targetos == 'darwin'
icon = meson.current_source_dir() / 'pc-bios/qemu.rsrc'
emulators += {exe['name'] : custom_target(exe['name'], emulators += {exe['name'] : custom_target(exe['name'],
depends: emulator, depends: emulator,
output: exe['name'], output: exe['name'],
@ -2383,14 +2535,14 @@ foreach target : target_dirs
meson.current_source_dir() / 'scripts/entitlement.sh', meson.current_source_dir() / 'scripts/entitlement.sh',
meson.current_build_dir() / exe_name, meson.current_build_dir() / exe_name,
meson.current_build_dir() / exe['name'], meson.current_build_dir() / exe['name'],
meson.current_source_dir() / 'accel/hvf/entitlements.plist' entitlements, icon
]) ])
} }
meson.add_install_script('scripts/entitlement.sh', '--install', meson.add_install_script('scripts/entitlement.sh', '--install',
get_option('bindir') / exe_name, get_option('bindir') / exe_name,
get_option('bindir') / exe['name'], get_option('bindir') / exe['name'],
meson.current_source_dir() / 'accel/hvf/entitlements.plist') entitlements, icon)
else else
emulators += {exe['name']: emulator} emulators += {exe['name']: emulator}
endif endif
@ -2476,7 +2628,7 @@ if have_tools
install: true) install: true)
endif endif
if 'CONFIG_IVSHMEM' in config_host if have_ivshmem
subdir('contrib/ivshmem-client') subdir('contrib/ivshmem-client')
subdir('contrib/ivshmem-server') subdir('contrib/ivshmem-server')
endif endif
@ -2613,7 +2765,6 @@ if targetos == 'windows'
summary_info += {'Windows SDK': config_host['WIN_SDK']} summary_info += {'Windows SDK': config_host['WIN_SDK']}
endif endif
endif endif
summary_info += {'ARFLAGS': config_host['ARFLAGS']}
summary_info += {'CFLAGS': ' '.join(get_option('c_args') summary_info += {'CFLAGS': ' '.join(get_option('c_args')
+ ['-O' + get_option('optimization')] + ['-O' + get_option('optimization')]
+ (get_option('debug') ? ['-g'] : []))} + (get_option('debug') ? ['-g'] : []))}
@ -2634,10 +2785,6 @@ summary_info += {'PIE': get_option('b_pie')}
summary_info += {'static build': config_host.has_key('CONFIG_STATIC')} summary_info += {'static build': config_host.has_key('CONFIG_STATIC')}
summary_info += {'malloc trim support': has_malloc_trim} summary_info += {'malloc trim support': has_malloc_trim}
summary_info += {'membarrier': config_host.has_key('CONFIG_MEMBARRIER')} summary_info += {'membarrier': config_host.has_key('CONFIG_MEMBARRIER')}
summary_info += {'fdatasync': config_host.has_key('CONFIG_FDATASYNC')}
summary_info += {'madvise': config_host.has_key('CONFIG_MADVISE')}
summary_info += {'posix_madvise': config_host.has_key('CONFIG_POSIX_MADVISE')}
summary_info += {'posix_memalign': config_host.has_key('CONFIG_POSIX_MEMALIGN')}
summary_info += {'debug stack usage': config_host.has_key('CONFIG_DEBUG_STACK_USAGE')} summary_info += {'debug stack usage': config_host.has_key('CONFIG_DEBUG_STACK_USAGE')}
summary_info += {'mutex debugging': config_host.has_key('CONFIG_DEBUG_MUTEX')} summary_info += {'mutex debugging': config_host.has_key('CONFIG_DEBUG_MUTEX')}
summary_info += {'memory allocator': get_option('malloc')} summary_info += {'memory allocator': get_option('malloc')}
@ -2756,7 +2903,7 @@ summary_info += {'SDL image support': sdl_image.found()}
summary_info += {'GTK support': gtk.found()} summary_info += {'GTK support': gtk.found()}
summary_info += {'pixman': pixman.found()} summary_info += {'pixman': pixman.found()}
# TODO: add back version # TODO: add back version
summary_info += {'VTE support': config_host.has_key('CONFIG_VTE')} summary_info += {'VTE support': vte.found()}
# TODO: add back version # TODO: add back version
summary_info += {'slirp support': slirp_opt == 'disabled' ? false : slirp_opt} summary_info += {'slirp support': slirp_opt == 'disabled' ? false : slirp_opt}
summary_info += {'libtasn1': tasn1.found()} summary_info += {'libtasn1': tasn1.found()}
@ -2764,7 +2911,7 @@ summary_info += {'PAM': pam.found()}
summary_info += {'iconv support': iconv.found()} summary_info += {'iconv support': iconv.found()}
summary_info += {'curses support': curses.found()} summary_info += {'curses support': curses.found()}
# TODO: add back version # TODO: add back version
summary_info += {'virgl support': config_host.has_key('CONFIG_VIRGL')} summary_info += {'virgl support': virgl.found()}
summary_info += {'curl support': curl.found()} summary_info += {'curl support': curl.found()}
summary_info += {'Multipath support': mpathpersist.found()} summary_info += {'Multipath support': mpathpersist.found()}
summary_info += {'VNC support': vnc.found()} summary_info += {'VNC support': vnc.found()}
@ -2777,7 +2924,7 @@ summary_info += {'brlapi support': brlapi.found()}
summary_info += {'vde support': config_host.has_key('CONFIG_VDE')} summary_info += {'vde support': config_host.has_key('CONFIG_VDE')}
summary_info += {'netmap support': config_host.has_key('CONFIG_NETMAP')} summary_info += {'netmap support': config_host.has_key('CONFIG_NETMAP')}
summary_info += {'Linux AIO support': config_host.has_key('CONFIG_LINUX_AIO')} summary_info += {'Linux AIO support': config_host.has_key('CONFIG_LINUX_AIO')}
summary_info += {'Linux io_uring support': config_host.has_key('CONFIG_LINUX_IO_URING')} summary_info += {'Linux io_uring support': linux_io_uring.found()}
summary_info += {'ATTR/XATTR support': libattr.found()} summary_info += {'ATTR/XATTR support': libattr.found()}
summary_info += {'RDMA support': config_host.has_key('CONFIG_RDMA')} summary_info += {'RDMA support': config_host.has_key('CONFIG_RDMA')}
summary_info += {'PVRDMA support': config_host.has_key('CONFIG_PVRDMA')} summary_info += {'PVRDMA support': config_host.has_key('CONFIG_PVRDMA')}
@ -2812,10 +2959,10 @@ summary_info += {'bzip2 support': libbzip2.found()}
summary_info += {'lzfse support': liblzfse.found()} summary_info += {'lzfse support': liblzfse.found()}
summary_info += {'zstd support': zstd.found()} summary_info += {'zstd support': zstd.found()}
summary_info += {'NUMA host support': config_host.has_key('CONFIG_NUMA')} summary_info += {'NUMA host support': config_host.has_key('CONFIG_NUMA')}
summary_info += {'libxml2': config_host.has_key('CONFIG_LIBXML2')} summary_info += {'libxml2': libxml2.found()}
summary_info += {'capstone': capstone_opt == 'disabled' ? false : capstone_opt} summary_info += {'capstone': capstone_opt == 'disabled' ? false : capstone_opt}
summary_info += {'libpmem support': config_host.has_key('CONFIG_LIBPMEM')} summary_info += {'libpmem support': libpmem.found()}
summary_info += {'libdaxctl support': config_host.has_key('CONFIG_LIBDAXCTL')} summary_info += {'libdaxctl support': libdaxctl.found()}
summary_info += {'libudev': libudev.found()} summary_info += {'libudev': libudev.found()}
summary_info += {'FUSE lseek': fuse_lseek.found()} summary_info += {'FUSE lseek': fuse_lseek.found()}
summary(summary_info, bool_yn: true, section: 'Dependencies') summary(summary_info, bool_yn: true, section: 'Dependencies')

View File

@ -84,10 +84,18 @@ option('nettle', type : 'feature', value : 'auto',
description: 'nettle cryptography support') description: 'nettle cryptography support')
option('gcrypt', type : 'feature', value : 'auto', option('gcrypt', type : 'feature', value : 'auto',
description: 'libgcrypt cryptography support') description: 'libgcrypt cryptography support')
option('libdaxctl', type : 'feature', value : 'auto',
description: 'libdaxctl support')
option('libpmem', type : 'feature', value : 'auto',
description: 'libpmem support')
option('libudev', type : 'feature', value : 'auto', option('libudev', type : 'feature', value : 'auto',
description: 'Use libudev to enumerate host devices') description: 'Use libudev to enumerate host devices')
option('libusb', type : 'feature', value : 'auto', option('libusb', type : 'feature', value : 'auto',
description: 'libusb support for USB passthrough') description: 'libusb support for USB passthrough')
option('libxml2', type : 'feature', value : 'auto',
description: 'libxml2 support for Parallels image format')
option('linux_io_uring', type : 'feature', value : 'auto',
description: 'Linux io_uring support')
option('lzfse', type : 'feature', value : 'auto', option('lzfse', type : 'feature', value : 'auto',
description: 'lzfse support for DMG images') description: 'lzfse support for DMG images')
option('lzo', type : 'feature', value : 'auto', option('lzo', type : 'feature', value : 'auto',
@ -110,6 +118,8 @@ option('u2f', type : 'feature', value : 'auto',
description: 'U2F emulation support') description: 'U2F emulation support')
option('usb_redir', type : 'feature', value : 'auto', option('usb_redir', type : 'feature', value : 'auto',
description: 'libusbredir support') description: 'libusbredir support')
option('virglrenderer', type : 'feature', value : 'auto',
description: 'virgl rendering support')
option('vnc', type : 'feature', value : 'enabled', option('vnc', type : 'feature', value : 'enabled',
description: 'VNC server') description: 'VNC server')
option('vnc_jpeg', type : 'feature', value : 'auto', option('vnc_jpeg', type : 'feature', value : 'auto',
@ -118,6 +128,8 @@ option('vnc_png', type : 'feature', value : 'auto',
description: 'PNG compression for VNC server') description: 'PNG compression for VNC server')
option('vnc_sasl', type : 'feature', value : 'auto', option('vnc_sasl', type : 'feature', value : 'auto',
description: 'SASL authentication for VNC server') description: 'SASL authentication for VNC server')
option('vte', type : 'feature', value : 'auto',
description: 'vte support for the gtk UI')
option('xkbcommon', type : 'feature', value : 'auto', option('xkbcommon', type : 'feature', value : 'auto',
description: 'xkbcommon support') description: 'xkbcommon support')
option('zstd', type : 'feature', value : 'auto', option('zstd', type : 'feature', value : 'auto',

View File

@ -456,10 +456,6 @@ static void qemu_start_incoming_migration(const char *uri, Error **errp)
{ {
const char *p = NULL; const char *p = NULL;
if (!yank_register_instance(MIGRATION_YANK_INSTANCE, errp)) {
return;
}
qapi_event_send_migration(MIGRATION_STATUS_SETUP); qapi_event_send_migration(MIGRATION_STATUS_SETUP);
if (strstart(uri, "tcp:", &p) || if (strstart(uri, "tcp:", &p) ||
strstart(uri, "unix:", NULL) || strstart(uri, "unix:", NULL) ||
@ -474,7 +470,6 @@ static void qemu_start_incoming_migration(const char *uri, Error **errp)
} else if (strstart(uri, "fd:", &p)) { } else if (strstart(uri, "fd:", &p)) {
fd_start_incoming_migration(p, errp); fd_start_incoming_migration(p, errp);
} else { } else {
yank_unregister_instance(MIGRATION_YANK_INSTANCE);
error_setg(errp, "unknown migration protocol: %s", uri); error_setg(errp, "unknown migration protocol: %s", uri);
} }
} }
@ -2083,9 +2078,14 @@ void qmp_migrate_incoming(const char *uri, Error **errp)
return; return;
} }
if (!yank_register_instance(MIGRATION_YANK_INSTANCE, errp)) {
return;
}
qemu_start_incoming_migration(uri, &local_err); qemu_start_incoming_migration(uri, &local_err);
if (local_err) { if (local_err) {
yank_unregister_instance(MIGRATION_YANK_INSTANCE);
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; return;
} }
@ -2097,6 +2097,13 @@ void qmp_migrate_recover(const char *uri, Error **errp)
{ {
MigrationIncomingState *mis = migration_incoming_get_current(); MigrationIncomingState *mis = migration_incoming_get_current();
/*
* Don't even bother to use ERRP_GUARD() as it _must_ always be set by
* callers (no one should ignore a recover failure); if there is, it's a
* programming error.
*/
assert(errp);
if (mis->state != MIGRATION_STATUS_POSTCOPY_PAUSED) { if (mis->state != MIGRATION_STATUS_POSTCOPY_PAUSED) {
error_setg(errp, "Migrate recover can only be run " error_setg(errp, "Migrate recover can only be run "
"when postcopy is paused."); "when postcopy is paused.");
@ -2114,8 +2121,13 @@ void qmp_migrate_recover(const char *uri, Error **errp)
* only re-setup the migration stream and poke existing migration * only re-setup the migration stream and poke existing migration
* to continue using that newly established channel. * to continue using that newly established channel.
*/ */
yank_unregister_instance(MIGRATION_YANK_INSTANCE);
qemu_start_incoming_migration(uri, errp); qemu_start_incoming_migration(uri, errp);
/* Safe to dereference with the assert above */
if (*errp) {
/* Reset the flag so user could still retry */
qatomic_set(&mis->postcopy_recover_triggered, false);
}
} }
void qmp_migrate_pause(Error **errp) void qmp_migrate_pause(Error **errp)
@ -3664,6 +3676,39 @@ bool migration_rate_limit(void)
return urgent; return urgent;
} }
/*
* if failover devices are present, wait they are completely
* unplugged
*/
static void qemu_savevm_wait_unplug(MigrationState *s, int old_state,
int new_state)
{
if (qemu_savevm_state_guest_unplug_pending()) {
migrate_set_state(&s->state, old_state, MIGRATION_STATUS_WAIT_UNPLUG);
while (s->state == MIGRATION_STATUS_WAIT_UNPLUG &&
qemu_savevm_state_guest_unplug_pending()) {
qemu_sem_timedwait(&s->wait_unplug_sem, 250);
}
if (s->state != MIGRATION_STATUS_WAIT_UNPLUG) {
int timeout = 120; /* 30 seconds */
/*
* migration has been canceled
* but as we have started an unplug we must wait the end
* to be able to plug back the card
*/
while (timeout-- && qemu_savevm_state_guest_unplug_pending()) {
qemu_sem_timedwait(&s->wait_unplug_sem, 250);
}
}
migrate_set_state(&s->state, MIGRATION_STATUS_WAIT_UNPLUG, new_state);
} else {
migrate_set_state(&s->state, old_state, new_state);
}
}
/* /*
* Master migration thread on the source VM. * Master migration thread on the source VM.
* It drives the migration and pumps the data down the outgoing channel. * It drives the migration and pumps the data down the outgoing channel.
@ -3710,22 +3755,10 @@ static void *migration_thread(void *opaque)
qemu_savevm_state_setup(s->to_dst_file); qemu_savevm_state_setup(s->to_dst_file);
if (qemu_savevm_state_guest_unplug_pending()) { qemu_savevm_wait_unplug(s, MIGRATION_STATUS_SETUP,
migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_ACTIVE);
MIGRATION_STATUS_WAIT_UNPLUG);
while (s->state == MIGRATION_STATUS_WAIT_UNPLUG &&
qemu_savevm_state_guest_unplug_pending()) {
qemu_sem_timedwait(&s->wait_unplug_sem, 250);
}
migrate_set_state(&s->state, MIGRATION_STATUS_WAIT_UNPLUG,
MIGRATION_STATUS_ACTIVE);
}
s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start; s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start;
migrate_set_state(&s->state, MIGRATION_STATUS_SETUP,
MIGRATION_STATUS_ACTIVE);
trace_migration_thread_setup_complete(); trace_migration_thread_setup_complete();
@ -3833,21 +3866,9 @@ static void *bg_migration_thread(void *opaque)
qemu_savevm_state_header(s->to_dst_file); qemu_savevm_state_header(s->to_dst_file);
qemu_savevm_state_setup(s->to_dst_file); qemu_savevm_state_setup(s->to_dst_file);
if (qemu_savevm_state_guest_unplug_pending()) { qemu_savevm_wait_unplug(s, MIGRATION_STATUS_SETUP,
migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_ACTIVE);
MIGRATION_STATUS_WAIT_UNPLUG);
while (s->state == MIGRATION_STATUS_WAIT_UNPLUG &&
qemu_savevm_state_guest_unplug_pending()) {
qemu_sem_timedwait(&s->wait_unplug_sem, 250);
}
migrate_set_state(&s->state, MIGRATION_STATUS_WAIT_UNPLUG,
MIGRATION_STATUS_ACTIVE);
} else {
migrate_set_state(&s->state, MIGRATION_STATUS_SETUP,
MIGRATION_STATUS_ACTIVE);
}
s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start; s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start;
trace_migration_thread_setup_complete(); trace_migration_thread_setup_complete();

View File

@ -416,6 +416,11 @@ static int add_to_iovec(QEMUFile *f, const uint8_t *buf, size_t size,
{ {
f->iov[f->iovcnt - 1].iov_len += size; f->iov[f->iovcnt - 1].iov_len += size;
} else { } else {
if (f->iovcnt >= MAX_IOV_SIZE) {
/* Should only happen if a previous fflush failed */
assert(f->shutdown || !qemu_file_is_writable(f));
return 1;
}
if (may_free) { if (may_free) {
set_bit(f->iovcnt, f->may_free); set_bit(f->iovcnt, f->may_free);
} }

View File

@ -1006,7 +1006,7 @@ route:
if (cm_event->event != RDMA_CM_EVENT_ADDR_RESOLVED) { if (cm_event->event != RDMA_CM_EVENT_ADDR_RESOLVED) {
ERROR(errp, "result not equal to event_addr_resolved %s", ERROR(errp, "result not equal to event_addr_resolved %s",
rdma_event_str(cm_event->event)); rdma_event_str(cm_event->event));
perror("rdma_resolve_addr"); error_report("rdma_resolve_addr");
rdma_ack_cm_event(cm_event); rdma_ack_cm_event(cm_event);
ret = -EINVAL; ret = -EINVAL;
goto err_resolve_get_addr; goto err_resolve_get_addr;
@ -2544,7 +2544,7 @@ static int qemu_rdma_connect(RDMAContext *rdma, Error **errp, bool return_path)
} }
if (cm_event->event != RDMA_CM_EVENT_ESTABLISHED) { if (cm_event->event != RDMA_CM_EVENT_ESTABLISHED) {
perror("rdma_get_cm_event != EVENT_ESTABLISHED after rdma_connect"); error_report("rdma_get_cm_event != EVENT_ESTABLISHED after rdma_connect");
ERROR(errp, "connecting to destination!"); ERROR(errp, "connecting to destination!");
rdma_ack_cm_event(cm_event); rdma_ack_cm_event(cm_event);
goto err_rdma_source_connect; goto err_rdma_source_connect;

View File

@ -1783,11 +1783,12 @@ DEF("display", HAS_ARG, QEMU_OPTION_display,
"-display spice-app[,gl=on|off]\n" "-display spice-app[,gl=on|off]\n"
#endif #endif
#if defined(CONFIG_SDL) #if defined(CONFIG_SDL)
"-display sdl[,alt_grab=on|off][,ctrl_grab=on|off]\n" "-display sdl[,alt_grab=on|off][,ctrl_grab=on|off][,gl=on|core|es|off]\n"
" [,window_close=on|off][,gl=on|core|es|off]\n" " [,show-cursor=on|off][,window-close=on|off]\n"
#endif #endif
#if defined(CONFIG_GTK) #if defined(CONFIG_GTK)
"-display gtk[,grab_on_hover=on|off][,gl=on|off]|\n" "-display gtk[,full-screen=on|off][,gl=on|off][,grab-on-hover=on|off]\n"
" [,show-cursor=on|off][,window-close=on|off]\n"
#endif #endif
#if defined(CONFIG_VNC) #if defined(CONFIG_VNC)
"-display vnc=<display>[,<optargs>]\n" "-display vnc=<display>[,<optargs>]\n"
@ -1824,17 +1825,37 @@ SRST
application. The Spice server will redirect the serial consoles application. The Spice server will redirect the serial consoles
and QEMU monitors. (Since 4.0) and QEMU monitors. (Since 4.0)
``sdl[,window-close=on|off][,gl=on|core|es|off]`` ``sdl``
Display video output via SDL (usually in a separate graphics Display video output via SDL (usually in a separate graphics
window; see the SDL documentation for other possibilities). window; see the SDL documentation for other possibilities).
Valid parameters are:
``gtk[,grab-on-hover=on|off][,gl=on|off]`` ``alt_grab=on|off`` : Use Control+Alt+Shift-g to toggle mouse grabbing
``ctrl_grab=on|off`` : Use Right-Control-g to toggle mouse grabbing
``gl=on|off|core|es`` : Use OpenGL for displaying
``show-cursor=on|off`` : Force showing the mouse cursor
``window-close=on|off`` : Allow to quit qemu with window close button
``gtk``
Display video output in a GTK window. This interface provides Display video output in a GTK window. This interface provides
drop-down menus and other UI elements to configure and control drop-down menus and other UI elements to configure and control
the VM during runtime. the VM during runtime. Valid parameters are:
``curses [,charset=<encoding>]`` ``full-screen=on|off`` : Start in fullscreen mode
``gl=on|off`` : Use OpenGL for displaying
``grab-on-hover=on|off`` : Grab keyboard input on mouse hover
``show-cursor=on|off`` : Force showing the mouse cursor
``window-close=on|off`` : Allow to quit qemu with window close button
``curses[,charset=<encoding>]``
Display video output via curses. For graphics device models Display video output via curses. For graphics device models
which support a text mode, QEMU can display this output using a which support a text mode, QEMU can display this output using a
curses/ncurses interface. Nothing is displayed when the graphics curses/ncurses interface. Nothing is displayed when the graphics
@ -1845,11 +1866,14 @@ SRST
``charset=CP850`` for IBM CP850 encoding. The default is ``charset=CP850`` for IBM CP850 encoding. The default is
``CP437``. ``CP437``.
``egl-headless[,rendernode<file>]`` ``egl-headless[,rendernode=<file>]``
Offload all OpenGL operations to a local DRI device. For any Offload all OpenGL operations to a local DRI device. For any
graphical display, this display needs to be paired with either graphical display, this display needs to be paired with either
VNC or SPICE displays. VNC or SPICE displays.
``vnc=<display>``
Start a VNC server on display <display>
``none`` ``none``
Do not display video output. The guest will still see an Do not display video output. The guest will still see an
emulated graphics card, but its output will not be displayed to emulated graphics card, but its output will not be displayed to
@ -1857,9 +1881,6 @@ SRST
that it only affects what is done with video output; -nographic that it only affects what is done with video output; -nographic
also changes the destination of the serial and parallel port also changes the destination of the serial and parallel port
data. data.
ERST ERST
DEF("nographic", 0, QEMU_OPTION_nographic, DEF("nographic", 0, QEMU_OPTION_nographic,
@ -1910,10 +1931,11 @@ SRST
ERST ERST
DEF("no-quit", 0, QEMU_OPTION_no_quit, DEF("no-quit", 0, QEMU_OPTION_no_quit,
"-no-quit disable SDL window close capability\n", QEMU_ARCH_ALL) "-no-quit disable SDL/GTK window close capability (deprecated)\n", QEMU_ARCH_ALL)
SRST SRST
``-no-quit`` ``-no-quit``
Disable SDL window close capability. Disable window close capability (SDL and GTK only). This option is
deprecated, please use ``-display ...,window-close=off`` instead.
ERST ERST
DEF("sdl", 0, QEMU_OPTION_sdl, DEF("sdl", 0, QEMU_OPTION_sdl,

View File

@ -42,6 +42,44 @@ bool user_creatable_can_be_deleted(UserCreatable *uc)
} }
} }
static void object_set_properties_from_qdict(Object *obj, const QDict *qdict,
Visitor *v, Error **errp)
{
const QDictEntry *e;
Error *local_err = NULL;
if (!visit_start_struct(v, NULL, NULL, 0, &local_err)) {
goto out;
}
for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
if (!object_property_set(obj, e->key, v, &local_err)) {
break;
}
}
if (!local_err) {
visit_check_struct(v, &local_err);
}
visit_end_struct(v, NULL);
out:
if (local_err) {
error_propagate(errp, local_err);
}
}
void object_set_properties_from_keyval(Object *obj, const QDict *qdict,
bool from_json, Error **errp)
{
Visitor *v;
if (from_json) {
v = qobject_input_visitor_new(QOBJECT(qdict));
} else {
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
}
object_set_properties_from_qdict(obj, qdict, v, errp);
visit_free(v);
}
Object *user_creatable_add_type(const char *type, const char *id, Object *user_creatable_add_type(const char *type, const char *id,
const QDict *qdict, const QDict *qdict,
Visitor *v, Error **errp) Visitor *v, Error **errp)
@ -49,7 +87,6 @@ Object *user_creatable_add_type(const char *type, const char *id,
ERRP_GUARD(); ERRP_GUARD();
Object *obj; Object *obj;
ObjectClass *klass; ObjectClass *klass;
const QDictEntry *e;
Error *local_err = NULL; Error *local_err = NULL;
if (id != NULL && !id_wellformed(id)) { if (id != NULL && !id_wellformed(id)) {
@ -78,18 +115,7 @@ Object *user_creatable_add_type(const char *type, const char *id,
assert(qdict); assert(qdict);
obj = object_new(type); obj = object_new(type);
if (!visit_start_struct(v, NULL, NULL, 0, &local_err)) { object_set_properties_from_qdict(obj, qdict, v, &local_err);
goto out;
}
for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
if (!object_property_set(obj, e->key, v, &local_err)) {
break;
}
}
if (!local_err) {
visit_check_struct(v, &local_err);
}
visit_end_struct(v, NULL);
if (local_err) { if (local_err) {
goto out; goto out;
} }
@ -178,7 +204,7 @@ static void user_creatable_print_types(void)
g_slist_free(list); g_slist_free(list);
} }
static bool user_creatable_print_type_properites(const char *type) bool type_print_class_properties(const char *type)
{ {
ObjectClass *klass; ObjectClass *klass;
ObjectPropertyIterator iter; ObjectPropertyIterator iter;
@ -224,7 +250,7 @@ bool user_creatable_print_help(const char *type, QemuOpts *opts)
} }
if (qemu_opt_has_help_opt(opts)) { if (qemu_opt_has_help_opt(opts)) {
return user_creatable_print_type_properites(type); return type_print_class_properties(type);
} }
return false; return false;
@ -234,7 +260,7 @@ static void user_creatable_print_help_from_qdict(QDict *args)
{ {
const char *type = qdict_get_try_str(args, "qom-type"); const char *type = qdict_get_try_str(args, "qom-type");
if (!type || !user_creatable_print_type_properites(type)) { if (!type || !type_print_class_properties(type)) {
user_creatable_print_types(); user_creatable_print_types();
} }
} }

View File

@ -21,9 +21,6 @@ hppa
i386 i386
~ (/qemu)?((/include)?/hw/i386/.*|/target/i386/.*|/hw/intc/[^/]*apic[^/]*\.c) ~ (/qemu)?((/include)?/hw/i386/.*|/target/i386/.*|/hw/intc/[^/]*apic[^/]*\.c)
lm32
~ (/qemu)?((/include)?/hw/lm32/.*|/target/lm32/.*|/hw/.*/(milkymist|lm32).*)
m68k m68k
~ (/qemu)?((/include)?/hw/m68k/.*|/target/m68k/.*|(/include)?/hw(/.*)?/mcf.*) ~ (/qemu)?((/include)?/hw/m68k/.*|/target/m68k/.*|(/include)?/hw(/.*)?/mcf.*)
@ -60,9 +57,6 @@ tilegx
tricore tricore
~ (/qemu)?((/include)?/hw/tricore/.*|/target/tricore/.*) ~ (/qemu)?((/include)?/hw/tricore/.*|/target/tricore/.*)
unicore32
~ (/qemu)?((/include)?/hw/unicore32/.*|/target/unicore32/.*)
9pfs 9pfs
~ (/qemu)?(/hw/9pfs/.*|/fsdev/.*) ~ (/qemu)?(/hw/9pfs/.*|/fsdev/.*)

View File

@ -380,15 +380,17 @@ export PATH="$TOOLBIN:$PATH"
cd "$SRCDIR" cd "$SRCDIR"
echo "Doing make distclean..." echo "Nuking build directory..."
make distclean rm -rf +build
mkdir +build
cd +build
echo "Configuring..." echo "Configuring..."
# We configure with a fixed set of enables here to ensure that we don't # We configure with a fixed set of enables here to ensure that we don't
# accidentally reduce the scope of the analysis by doing the build on # accidentally reduce the scope of the analysis by doing the build on
# the system that's missing a dependency that we need to build part of # the system that's missing a dependency that we need to build part of
# the codebase. # the codebase.
./configure --disable-modules --enable-sdl --enable-gtk \ ../configure --disable-modules --enable-sdl --enable-gtk \
--enable-opengl --enable-vte --enable-gnutls \ --enable-opengl --enable-vte --enable-gnutls \
--enable-nettle --enable-curses --enable-curl \ --enable-nettle --enable-curses --enable-curl \
--audio-drv-list=oss,alsa,sdl,pa --enable-virtfs \ --audio-drv-list=oss,alsa,sdl,pa --enable-virtfs \

View File

@ -11,6 +11,7 @@ fi
SRC="$1" SRC="$1"
DST="$2" DST="$2"
ENTITLEMENT="$3" ENTITLEMENT="$3"
ICON="$4"
if $in_place; then if $in_place; then
trap 'rm "$DST.tmp"' exit trap 'rm "$DST.tmp"' exit
@ -20,6 +21,13 @@ else
cd "$MESON_INSTALL_DESTDIR_PREFIX" cd "$MESON_INSTALL_DESTDIR_PREFIX"
fi fi
codesign --entitlements "$ENTITLEMENT" --force -s - "$SRC" if test "$ENTITLEMENT" != '/dev/null'; then
codesign --entitlements "$ENTITLEMENT" --force -s - "$SRC"
fi
# Add the QEMU icon to the binary on Mac OS
Rez -append "$ICON" -o "$SRC"
SetFile -a C "$SRC"
mv -f "$SRC" "$DST" mv -f "$SRC" "$DST"
trap '' exit trap '' exit

View File

@ -145,6 +145,8 @@ static const char *cpu_option;
static const char *mem_path; static const char *mem_path;
static const char *incoming; static const char *incoming;
static const char *loadvm; static const char *loadvm;
static const char *accelerators;
static QDict *machine_opts_dict;
static QTAILQ_HEAD(, ObjectOption) object_opts = QTAILQ_HEAD_INITIALIZER(object_opts); static QTAILQ_HEAD(, ObjectOption) object_opts = QTAILQ_HEAD_INITIALIZER(object_opts);
static ram_addr_t maxram_size; static ram_addr_t maxram_size;
static uint64_t ram_slots; static uint64_t ram_slots;
@ -235,21 +237,6 @@ static QemuOptsList qemu_option_rom_opts = {
}, },
}; };
static QemuOptsList qemu_machine_opts = {
.name = "machine",
.implied_opt_name = "type",
.merge_lists = true,
.head = QTAILQ_HEAD_INITIALIZER(qemu_machine_opts.head),
.desc = {
/*
* no elements => accept any
* sanity checking will happen later
* when setting machine properties
*/
{ }
},
};
static QemuOptsList qemu_accel_opts = { static QemuOptsList qemu_accel_opts = {
.name = "accel", .name = "accel",
.implied_opt_name = "accel", .implied_opt_name = "accel",
@ -498,16 +485,6 @@ static QemuOptsList qemu_action_opts = {
}, },
}; };
/**
* Get machine options
*
* Returns: machine options (never null).
*/
static QemuOpts *qemu_get_machine_opts(void)
{
return qemu_find_opts_singleton("machine");
}
const char *qemu_get_vm_name(void) const char *qemu_get_vm_name(void)
{ {
return qemu_name; return qemu_name;
@ -815,33 +792,6 @@ static MachineClass *find_default_machine(GSList *machines)
return default_machineclass; return default_machineclass;
} }
static int machine_help_func(QemuOpts *opts, MachineState *machine)
{
ObjectProperty *prop;
ObjectPropertyIterator iter;
if (!qemu_opt_has_help_opt(opts)) {
return 0;
}
object_property_iter_init(&iter, OBJECT(machine));
while ((prop = object_property_iter_next(&iter))) {
if (!prop->set) {
continue;
}
printf("%s.%s=%s", MACHINE_GET_CLASS(machine)->name,
prop->name, prop->type);
if (prop->description) {
printf(" (%s)\n", prop->description);
} else {
printf("\n");
}
}
return 1;
}
static void version(void) static void version(void)
{ {
printf("QEMU emulator version " QEMU_FULL_VERSION "\n" printf("QEMU emulator version " QEMU_FULL_VERSION "\n"
@ -1062,8 +1012,6 @@ static void parse_display(const char *p)
* sdl DisplayType needs hand-crafted parser instead of * sdl DisplayType needs hand-crafted parser instead of
* parse_display_qapi() due to some options not in * parse_display_qapi() due to some options not in
* DisplayOptions, specifically: * DisplayOptions, specifically:
* - frame
* Already deprecated.
* - ctrl_grab + alt_grab * - ctrl_grab + alt_grab
* Not clear yet what happens to them long-term. Should * Not clear yet what happens to them long-term. Should
* replaced by something better or deprecated and dropped. * replaced by something better or deprecated and dropped.
@ -1091,7 +1039,12 @@ static void parse_display(const char *p)
} else { } else {
goto invalid_sdl_args; goto invalid_sdl_args;
} }
} else if (strstart(opts, ",window_close=", &nextopt)) { } else if (strstart(opts, ",window_close=", &nextopt) ||
strstart(opts, ",window-close=", &nextopt)) {
if (strstart(opts, ",window_close=", NULL)) {
warn_report("window_close with an underscore is deprecated,"
" please use window-close instead.");
}
opts = nextopt; opts = nextopt;
dpy.has_window_close = true; dpy.has_window_close = true;
if (strstart(opts, "on", &nextopt)) { if (strstart(opts, "on", &nextopt)) {
@ -1551,33 +1504,50 @@ static gint machine_class_cmp(gconstpointer a, gconstpointer b)
object_class_get_name(OBJECT_CLASS(mc1))); object_class_get_name(OBJECT_CLASS(mc1)));
} }
static MachineClass *machine_parse(const char *name, GSList *machines) static void machine_help_func(const QDict *qdict)
{ {
MachineClass *mc; GSList *machines, *el;
GSList *el; const char *type = qdict_get_try_str(qdict, "type");
if (is_help_option(name)) { machines = object_class_get_list(TYPE_MACHINE, false);
printf("Supported machines are:\n"); if (type) {
machines = g_slist_sort(machines, machine_class_cmp); ObjectClass *machine_class = OBJECT_CLASS(find_machine(type, machines));
for (el = machines; el; el = el->next) { if (machine_class) {
MachineClass *mc = el->data; type_print_class_properties(object_class_get_name(machine_class));
if (mc->alias) { return;
printf("%-20s %s (alias of %s)\n", mc->alias, mc->desc, mc->name);
}
printf("%-20s %s%s%s\n", mc->name, mc->desc,
mc->is_default ? " (default)" : "",
mc->deprecation_reason ? " (deprecated)" : "");
} }
exit(0);
} }
mc = find_machine(name, machines); printf("Supported machines are:\n");
if (!mc) { machines = g_slist_sort(machines, machine_class_cmp);
error_report("unsupported machine type"); for (el = machines; el; el = el->next) {
error_printf("Use -machine help to list supported machines\n"); MachineClass *mc = el->data;
exit(1); if (mc->alias) {
printf("%-20s %s (alias of %s)\n", mc->alias, mc->desc, mc->name);
}
printf("%-20s %s%s%s\n", mc->name, mc->desc,
mc->is_default ? " (default)" : "",
mc->deprecation_reason ? " (deprecated)" : "");
} }
return mc; }
static void
machine_parse_property_opt(QemuOptsList *opts_list, const char *propname,
const char *arg, Error **errp)
{
QDict *opts, *prop;
bool help = false;
ERRP_GUARD();
prop = keyval_parse(arg, opts_list->implied_opt_name, &help, errp);
if (help) {
qemu_opts_print_help(opts_list, true);
return;
}
opts = qdict_new();
qdict_put(opts, propname, prop);
keyval_merge(machine_opts_dict, opts, errp);
qobject_unref(opts);
} }
static const char *pid_file; static const char *pid_file;
@ -1630,32 +1600,31 @@ static const QEMUOption *lookup_opt(int argc, char **argv,
return popt; return popt;
} }
static MachineClass *select_machine(void) static MachineClass *select_machine(QDict *qdict, Error **errp)
{ {
const char *optarg = qdict_get_try_str(qdict, "type");
GSList *machines = object_class_get_list(TYPE_MACHINE, false); GSList *machines = object_class_get_list(TYPE_MACHINE, false);
MachineClass *machine_class = find_default_machine(machines); MachineClass *machine_class;
const char *optarg; Error *local_err = NULL;
QemuOpts *opts;
Location loc;
loc_push_none(&loc);
opts = qemu_get_machine_opts();
qemu_opts_loc_restore(opts);
optarg = qemu_opt_get(opts, "type");
if (optarg) { if (optarg) {
machine_class = machine_parse(optarg, machines); machine_class = find_machine(optarg, machines);
qdict_del(qdict, "type");
if (!machine_class) {
error_setg(&local_err, "unsupported machine type");
}
} else {
machine_class = find_default_machine(machines);
if (!machine_class) {
error_setg(&local_err, "No machine specified, and there is no default");
}
} }
if (!machine_class) {
error_report("No machine specified, and there is no default");
error_printf("Use -machine help to list supported machines\n");
exit(1);
}
loc_pop(&loc);
g_slist_free(machines); g_slist_free(machines);
if (local_err) {
error_append_hint(&local_err, "Use -machine help to list supported machines\n");
error_propagate(errp, local_err);
}
return machine_class; return machine_class;
} }
@ -1674,42 +1643,70 @@ static int object_parse_property_opt(Object *obj,
return 0; return 0;
} }
static int machine_set_property(void *opaque, /* *Non*recursively replace underscores with dashes in QDict keys. */
const char *name, const char *value, static void keyval_dashify(QDict *qdict, Error **errp)
Error **errp)
{ {
g_autofree char *qom_name = g_strdup(name); const QDictEntry *ent, *next;
char *p; char *p;
for (p = qom_name; *p; p++) { for (ent = qdict_first(qdict); ent; ent = next) {
if (*p == '_') { g_autofree char *new_key = NULL;
*p = '-';
next = qdict_next(qdict, ent);
if (!strchr(ent->key, '_')) {
continue;
} }
new_key = g_strdup(ent->key);
for (p = new_key; *p; p++) {
if (*p == '_') {
*p = '-';
}
}
if (qdict_haskey(qdict, new_key)) {
error_setg(errp, "Conflict between '%s' and '%s'", ent->key, new_key);
return;
}
qobject_ref(ent->value);
qdict_put_obj(qdict, new_key, ent->value);
qdict_del(qdict, ent->key);
} }
}
static void qemu_apply_legacy_machine_options(QDict *qdict)
{
const char *value;
keyval_dashify(qdict, &error_fatal);
/* Legacy options do not correspond to MachineState properties. */ /* Legacy options do not correspond to MachineState properties. */
if (g_str_equal(qom_name, "accel")) { value = qdict_get_try_str(qdict, "accel");
return 0; if (value) {
} accelerators = g_strdup(value);
if (g_str_equal(qom_name, "igd-passthru")) { qdict_del(qdict, "accel");
object_register_sugar_prop(ACCEL_CLASS_NAME("xen"), qom_name, value,
false);
return 0;
}
if (g_str_equal(qom_name, "kvm-shadow-mem")) {
object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), qom_name, value,
false);
return 0;
}
if (g_str_equal(qom_name, "kernel-irqchip")) {
object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), qom_name, value,
false);
object_register_sugar_prop(ACCEL_CLASS_NAME("whpx"), qom_name, value,
false);
return 0;
} }
return object_parse_property_opt(opaque, name, value, "type", errp); value = qdict_get_try_str(qdict, "igd-passthru");
if (value) {
object_register_sugar_prop(ACCEL_CLASS_NAME("xen"), "igd-passthru", value,
false);
qdict_del(qdict, "igd-passthru");
}
value = qdict_get_try_str(qdict, "kvm-shadow-mem");
if (value) {
object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), "kvm-shadow-mem", value,
false);
qdict_del(qdict, "kvm-shadow-mem");
}
value = qdict_get_try_str(qdict, "kernel-irqchip");
if (value) {
object_register_sugar_prop(ACCEL_CLASS_NAME("kvm"), "kernel-irqchip", value,
false);
object_register_sugar_prop(ACCEL_CLASS_NAME("whpx"), "kernel-irqchip", value,
false);
qdict_del(qdict, "kernel-irqchip");
}
} }
static void object_option_foreach_add(bool (*type_opt_predicate)(const char *)) static void object_option_foreach_add(bool (*type_opt_predicate)(const char *))
@ -1824,16 +1821,14 @@ static bool object_create_early(const char *type)
return true; return true;
} }
static void qemu_apply_machine_options(void) static void qemu_apply_machine_options(QDict *qdict)
{ {
MachineClass *machine_class = MACHINE_GET_CLASS(current_machine); MachineClass *machine_class = MACHINE_GET_CLASS(current_machine);
QemuOpts *machine_opts = qemu_get_machine_opts();
const char *boot_order = NULL; const char *boot_order = NULL;
const char *boot_once = NULL; const char *boot_once = NULL;
QemuOpts *opts; QemuOpts *opts;
qemu_opt_foreach(machine_opts, machine_set_property, current_machine, object_set_properties_from_keyval(OBJECT(current_machine), qdict, false, &error_fatal);
&error_fatal);
current_machine->ram_size = ram_size; current_machine->ram_size = ram_size;
current_machine->maxram_size = maxram_size; current_machine->maxram_size = maxram_size;
current_machine->ram_slots = ram_slots; current_machine->ram_slots = ram_slots;
@ -1862,10 +1857,14 @@ static void qemu_apply_machine_options(void)
current_machine->boot_once = boot_once; current_machine->boot_once = boot_once;
if (semihosting_enabled() && !semihosting_get_argc()) { if (semihosting_enabled() && !semihosting_get_argc()) {
const char *kernel_filename = qemu_opt_get(machine_opts, "kernel");
const char *kernel_cmdline = qemu_opt_get(machine_opts, "append") ?: "";
/* fall back to the -kernel/-append */ /* fall back to the -kernel/-append */
semihosting_arg_fallback(kernel_filename, kernel_cmdline); semihosting_arg_fallback(current_machine->kernel_filename, current_machine->kernel_cmdline);
}
if (current_machine->smp.cpus > 1) {
Error *blocker = NULL;
error_setg(&blocker, QERR_REPLAY_NOT_SUPPORTED, "smp");
replay_add_blocker(blocker);
} }
} }
@ -1921,8 +1920,7 @@ static void qemu_create_early_backends(void)
/* /*
* Note: we need to create audio and block backends before * Note: we need to create audio and block backends before
* machine_set_property(), so machine properties can refer to * setting machine properties, so they can be referred to.
* them.
*/ */
configure_blockdev(&bdo_queue, machine_class, snapshot); configure_blockdev(&bdo_queue, machine_class, snapshot);
audio_init_audiodevs(); audio_init_audiodevs();
@ -2088,16 +2086,14 @@ static void set_memory_options(MachineClass *mc)
loc_pop(&loc); loc_pop(&loc);
} }
static void qemu_create_machine(MachineClass *machine_class) static void qemu_create_machine(QDict *qdict)
{ {
MachineClass *machine_class = select_machine(qdict, &error_fatal);
object_set_machine_compat_props(machine_class->compat_props); object_set_machine_compat_props(machine_class->compat_props);
set_memory_options(machine_class); set_memory_options(machine_class);
current_machine = MACHINE(object_new_with_class(OBJECT_CLASS(machine_class))); current_machine = MACHINE(object_new_with_class(OBJECT_CLASS(machine_class)));
if (machine_help_func(qemu_get_machine_opts(), current_machine)) {
exit(0);
}
object_property_add_child(object_get_root(), "machine", object_property_add_child(object_get_root(), "machine",
OBJECT(current_machine)); OBJECT(current_machine));
object_property_add_child(container_get(OBJECT(current_machine), object_property_add_child(container_get(OBJECT(current_machine),
@ -2120,16 +2116,17 @@ static void qemu_create_machine(MachineClass *machine_class)
qemu_set_hw_version(machine_class->hw_version); qemu_set_hw_version(machine_class->hw_version);
} }
machine_smp_parse(current_machine,
qemu_opts_find(qemu_find_opts("smp-opts"), NULL), &error_fatal);
/* /*
* Get the default machine options from the machine if it is not already * Get the default machine options from the machine if it is not already
* specified either by the configuration file or by the command line. * specified either by the configuration file or by the command line.
*/ */
if (machine_class->default_machine_opts) { if (machine_class->default_machine_opts) {
qemu_opts_set_defaults(qemu_find_opts("machine"), QDict *default_opts =
machine_class->default_machine_opts, 0); keyval_parse(machine_class->default_machine_opts, NULL, NULL,
&error_abort);
object_set_properties_from_keyval(OBJECT(current_machine), default_opts,
false, &error_abort);
qobject_unref(default_opts);
} }
} }
@ -2151,7 +2148,8 @@ static int global_init_func(void *opaque, QemuOpts *opts, Error **errp)
*/ */
static bool is_qemuopts_group(const char *group) static bool is_qemuopts_group(const char *group)
{ {
if (g_str_equal(group, "object")) { if (g_str_equal(group, "object") ||
g_str_equal(group, "machine")) {
return false; return false;
} }
return true; return true;
@ -2164,6 +2162,13 @@ static void qemu_record_config_group(const char *group, QDict *dict,
Visitor *v = qobject_input_visitor_new_keyval(QOBJECT(dict)); Visitor *v = qobject_input_visitor_new_keyval(QOBJECT(dict));
object_option_add_visitor(v); object_option_add_visitor(v);
visit_free(v); visit_free(v);
} else if (g_str_equal(group, "machine")) {
/*
* Cannot merge string-valued and type-safe dictionaries, so JSON
* is not accepted yet for -M.
*/
assert(!from_json);
keyval_merge(machine_opts_dict, dict, errp);
} else { } else {
abort(); abort();
} }
@ -2294,13 +2299,11 @@ static int do_configure_accelerator(void *opaque, QemuOpts *opts, Error **errp)
static void configure_accelerators(const char *progname) static void configure_accelerators(const char *progname)
{ {
const char *accelerators;
bool init_failed = false; bool init_failed = false;
qemu_opts_foreach(qemu_find_opts("icount"), qemu_opts_foreach(qemu_find_opts("icount"),
do_configure_icount, NULL, &error_fatal); do_configure_icount, NULL, &error_fatal);
accelerators = qemu_opt_get(qemu_get_machine_opts(), "accel");
if (QTAILQ_EMPTY(&qemu_accel_opts.head)) { if (QTAILQ_EMPTY(&qemu_accel_opts.head)) {
char **accel_list, **tmp; char **accel_list, **tmp;
@ -2388,12 +2391,11 @@ static void create_default_memdev(MachineState *ms, const char *path)
&error_fatal); &error_fatal);
} }
static void qemu_validate_options(void) static void qemu_validate_options(const QDict *machine_opts)
{ {
QemuOpts *machine_opts = qemu_get_machine_opts(); const char *kernel_filename = qdict_get_try_str(machine_opts, "kernel");
const char *kernel_filename = qemu_opt_get(machine_opts, "kernel"); const char *initrd_filename = qdict_get_try_str(machine_opts, "initrd");
const char *initrd_filename = qemu_opt_get(machine_opts, "initrd"); const char *kernel_cmdline = qdict_get_try_str(machine_opts, "append");
const char *kernel_cmdline = qemu_opt_get(machine_opts, "append");
if (kernel_filename == NULL) { if (kernel_filename == NULL) {
if (kernel_cmdline != NULL) { if (kernel_cmdline != NULL) {
@ -2733,7 +2735,6 @@ void qemu_init(int argc, char **argv, char **envp)
qemu_add_opts(&qemu_trace_opts); qemu_add_opts(&qemu_trace_opts);
qemu_plugin_add_opts(); qemu_plugin_add_opts();
qemu_add_opts(&qemu_option_rom_opts); qemu_add_opts(&qemu_option_rom_opts);
qemu_add_opts(&qemu_machine_opts);
qemu_add_opts(&qemu_accel_opts); qemu_add_opts(&qemu_accel_opts);
qemu_add_opts(&qemu_mem_opts); qemu_add_opts(&qemu_mem_opts);
qemu_add_opts(&qemu_smp_opts); qemu_add_opts(&qemu_smp_opts);
@ -2774,6 +2775,7 @@ void qemu_init(int argc, char **argv, char **envp)
} }
} }
machine_opts_dict = qdict_new();
if (userconfig) { if (userconfig) {
qemu_read_default_config_file(&error_fatal); qemu_read_default_config_file(&error_fatal);
} }
@ -2863,8 +2865,7 @@ void qemu_init(int argc, char **argv, char **envp)
parse_display(optarg); parse_display(optarg);
break; break;
case QEMU_OPTION_nographic: case QEMU_OPTION_nographic:
olist = qemu_find_opts("machine"); qdict_put_str(machine_opts_dict, "graphics", "off");
qemu_opts_parse_noisily(olist, "graphics=off", false);
nographic = true; nographic = true;
dpy.type = DISPLAY_TYPE_NONE; dpy.type = DISPLAY_TYPE_NONE;
break; break;
@ -2888,16 +2889,16 @@ void qemu_init(int argc, char **argv, char **envp)
} }
break; break;
case QEMU_OPTION_kernel: case QEMU_OPTION_kernel:
qemu_opts_set(qemu_find_opts("machine"), "kernel", optarg, &error_abort); qdict_put_str(machine_opts_dict, "kernel", optarg);
break; break;
case QEMU_OPTION_initrd: case QEMU_OPTION_initrd:
qemu_opts_set(qemu_find_opts("machine"), "initrd", optarg, &error_abort); qdict_put_str(machine_opts_dict, "initrd", optarg);
break; break;
case QEMU_OPTION_append: case QEMU_OPTION_append:
qemu_opts_set(qemu_find_opts("machine"), "append", optarg, &error_abort); qdict_put_str(machine_opts_dict, "append", optarg);
break; break;
case QEMU_OPTION_dtb: case QEMU_OPTION_dtb:
qemu_opts_set(qemu_find_opts("machine"), "dtb", optarg, &error_abort); qdict_put_str(machine_opts_dict, "dtb", optarg);
break; break;
case QEMU_OPTION_cdrom: case QEMU_OPTION_cdrom:
drive_add(IF_DEFAULT, 2, optarg, CDROM_OPTS); drive_add(IF_DEFAULT, 2, optarg, CDROM_OPTS);
@ -3007,7 +3008,7 @@ void qemu_init(int argc, char **argv, char **envp)
} }
break; break;
case QEMU_OPTION_bios: case QEMU_OPTION_bios:
qemu_opts_set(qemu_find_opts("machine"), "firmware", optarg, &error_abort); qdict_put_str(machine_opts_dict, "firmware", optarg);
break; break;
case QEMU_OPTION_singlestep: case QEMU_OPTION_singlestep:
singlestep = 1; singlestep = 1;
@ -3234,6 +3235,8 @@ void qemu_init(int argc, char **argv, char **envp)
case QEMU_OPTION_no_quit: case QEMU_OPTION_no_quit:
dpy.has_window_close = true; dpy.has_window_close = true;
dpy.window_close = false; dpy.window_close = false;
warn_report("-no-quit is deprecated, please use "
"-display ...,window-close=off instead.");
break; break;
case QEMU_OPTION_sdl: case QEMU_OPTION_sdl:
#ifdef CONFIG_SDL #ifdef CONFIG_SDL
@ -3276,17 +3279,20 @@ void qemu_init(int argc, char **argv, char **envp)
preconfig_requested = true; preconfig_requested = true;
break; break;
case QEMU_OPTION_enable_kvm: case QEMU_OPTION_enable_kvm:
olist = qemu_find_opts("machine"); qdict_put_str(machine_opts_dict, "accel", "kvm");
qemu_opts_parse_noisily(olist, "accel=kvm", false);
break; break;
case QEMU_OPTION_M: case QEMU_OPTION_M:
case QEMU_OPTION_machine: case QEMU_OPTION_machine:
olist = qemu_find_opts("machine"); {
opts = qemu_opts_parse_noisily(olist, optarg, true); bool help;
if (!opts) {
exit(1); keyval_parse_into(machine_opts_dict, optarg, "type", &help, &error_fatal);
if (help) {
machine_help_func(machine_opts_dict);
exit(EXIT_SUCCESS);
}
break;
} }
break;
case QEMU_OPTION_accel: case QEMU_OPTION_accel:
accel_opts = qemu_opts_parse_noisily(qemu_find_opts("accel"), accel_opts = qemu_opts_parse_noisily(qemu_find_opts("accel"),
optarg, true); optarg, true);
@ -3313,12 +3319,10 @@ void qemu_init(int argc, char **argv, char **envp)
} }
break; break;
case QEMU_OPTION_usb: case QEMU_OPTION_usb:
olist = qemu_find_opts("machine"); qdict_put_str(machine_opts_dict, "usb", "on");
qemu_opts_parse_noisily(olist, "usb=on", false);
break; break;
case QEMU_OPTION_usbdevice: case QEMU_OPTION_usbdevice:
olist = qemu_find_opts("machine"); qdict_put_str(machine_opts_dict, "usb", "on");
qemu_opts_parse_noisily(olist, "usb=on", false);
add_device_config(DEV_USB, optarg); add_device_config(DEV_USB, optarg);
break; break;
case QEMU_OPTION_device: case QEMU_OPTION_device:
@ -3328,21 +3332,16 @@ void qemu_init(int argc, char **argv, char **envp)
} }
break; break;
case QEMU_OPTION_smp: case QEMU_OPTION_smp:
if (!qemu_opts_parse_noisily(qemu_find_opts("smp-opts"), machine_parse_property_opt(qemu_find_opts("smp-opts"), "smp", optarg, &error_fatal);
optarg, true)) {
exit(1);
}
break; break;
case QEMU_OPTION_vnc: case QEMU_OPTION_vnc:
vnc_parse(optarg); vnc_parse(optarg);
break; break;
case QEMU_OPTION_no_acpi: case QEMU_OPTION_no_acpi:
olist = qemu_find_opts("machine"); qdict_put_str(machine_opts_dict, "acpi", "off");
qemu_opts_parse_noisily(olist, "acpi=off", false);
break; break;
case QEMU_OPTION_no_hpet: case QEMU_OPTION_no_hpet:
olist = qemu_find_opts("machine"); qdict_put_str(machine_opts_dict, "hpet", "off");
qemu_opts_parse_noisily(olist, "hpet=off", false);
break; break;
case QEMU_OPTION_no_reboot: case QEMU_OPTION_no_reboot:
olist = qemu_find_opts("action"); olist = qemu_find_opts("action");
@ -3595,7 +3594,7 @@ void qemu_init(int argc, char **argv, char **envp)
*/ */
loc_set_none(); loc_set_none();
qemu_validate_options(); qemu_validate_options(machine_opts_dict);
qemu_process_sugar_options(); qemu_process_sugar_options();
/* /*
@ -3628,7 +3627,7 @@ void qemu_init(int argc, char **argv, char **envp)
configure_rtc(qemu_find_opts_singleton("rtc")); configure_rtc(qemu_find_opts_singleton("rtc"));
qemu_create_machine(select_machine()); qemu_create_machine(machine_opts_dict);
suspend_mux_open(); suspend_mux_open();
@ -3636,12 +3635,14 @@ void qemu_init(int argc, char **argv, char **envp)
qemu_create_default_devices(); qemu_create_default_devices();
qemu_create_early_backends(); qemu_create_early_backends();
qemu_apply_machine_options(); qemu_apply_legacy_machine_options(machine_opts_dict);
qemu_apply_machine_options(machine_opts_dict);
qobject_unref(machine_opts_dict);
phase_advance(PHASE_MACHINE_CREATED); phase_advance(PHASE_MACHINE_CREATED);
/* /*
* Note: uses machine properties such as kernel-irqchip, must run * Note: uses machine properties such as kernel-irqchip, must run
* after machine_set_property(). * after qemu_apply_machine_options.
*/ */
configure_accelerators(argv[0]); configure_accelerators(argv[0]);
phase_advance(PHASE_ACCEL_CREATED); phase_advance(PHASE_ACCEL_CREATED);

View File

@ -1304,53 +1304,37 @@ static const X86RegisterInfo32 x86_reg_info_32[CPU_NB_REGS32] = {
}; };
#undef REGISTER #undef REGISTER
typedef struct ExtSaveArea { ExtSaveArea x86_ext_save_areas[XSAVE_STATE_AREA_COUNT] = {
uint32_t feature, bits;
uint32_t offset, size;
} ExtSaveArea;
static const ExtSaveArea x86_ext_save_areas[] = {
[XSTATE_FP_BIT] = { [XSTATE_FP_BIT] = {
/* x87 FP state component is always enabled if XSAVE is supported */ /* x87 FP state component is always enabled if XSAVE is supported */
.feature = FEAT_1_ECX, .bits = CPUID_EXT_XSAVE, .feature = FEAT_1_ECX, .bits = CPUID_EXT_XSAVE,
/* x87 state is in the legacy region of the XSAVE area */
.offset = 0,
.size = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader), .size = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader),
}, },
[XSTATE_SSE_BIT] = { [XSTATE_SSE_BIT] = {
/* SSE state component is always enabled if XSAVE is supported */ /* SSE state component is always enabled if XSAVE is supported */
.feature = FEAT_1_ECX, .bits = CPUID_EXT_XSAVE, .feature = FEAT_1_ECX, .bits = CPUID_EXT_XSAVE,
/* SSE state is in the legacy region of the XSAVE area */
.offset = 0,
.size = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader), .size = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader),
}, },
[XSTATE_YMM_BIT] = [XSTATE_YMM_BIT] =
{ .feature = FEAT_1_ECX, .bits = CPUID_EXT_AVX, { .feature = FEAT_1_ECX, .bits = CPUID_EXT_AVX,
.offset = offsetof(X86XSaveArea, avx_state),
.size = sizeof(XSaveAVX) }, .size = sizeof(XSaveAVX) },
[XSTATE_BNDREGS_BIT] = [XSTATE_BNDREGS_BIT] =
{ .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX, { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX,
.offset = offsetof(X86XSaveArea, bndreg_state),
.size = sizeof(XSaveBNDREG) }, .size = sizeof(XSaveBNDREG) },
[XSTATE_BNDCSR_BIT] = [XSTATE_BNDCSR_BIT] =
{ .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX, { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX,
.offset = offsetof(X86XSaveArea, bndcsr_state),
.size = sizeof(XSaveBNDCSR) }, .size = sizeof(XSaveBNDCSR) },
[XSTATE_OPMASK_BIT] = [XSTATE_OPMASK_BIT] =
{ .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F, { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F,
.offset = offsetof(X86XSaveArea, opmask_state),
.size = sizeof(XSaveOpmask) }, .size = sizeof(XSaveOpmask) },
[XSTATE_ZMM_Hi256_BIT] = [XSTATE_ZMM_Hi256_BIT] =
{ .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F, { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F,
.offset = offsetof(X86XSaveArea, zmm_hi256_state),
.size = sizeof(XSaveZMM_Hi256) }, .size = sizeof(XSaveZMM_Hi256) },
[XSTATE_Hi16_ZMM_BIT] = [XSTATE_Hi16_ZMM_BIT] =
{ .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F, { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F,
.offset = offsetof(X86XSaveArea, hi16_zmm_state),
.size = sizeof(XSaveHi16_ZMM) }, .size = sizeof(XSaveHi16_ZMM) },
[XSTATE_PKRU_BIT] = [XSTATE_PKRU_BIT] =
{ .feature = FEAT_7_0_ECX, .bits = CPUID_7_0_ECX_PKU, { .feature = FEAT_7_0_ECX, .bits = CPUID_7_0_ECX_PKU,
.offset = offsetof(X86XSaveArea, pkru_state),
.size = sizeof(XSavePKRU) }, .size = sizeof(XSavePKRU) },
}; };

View File

@ -1305,41 +1305,23 @@ typedef struct XSavePKRU {
uint32_t padding; uint32_t padding;
} XSavePKRU; } XSavePKRU;
typedef struct X86XSaveArea {
X86LegacyXSaveArea legacy;
X86XSaveHeader header;
/* Extended save areas: */
/* AVX State: */
XSaveAVX avx_state;
uint8_t padding[960 - 576 - sizeof(XSaveAVX)];
/* MPX State: */
XSaveBNDREG bndreg_state;
XSaveBNDCSR bndcsr_state;
/* AVX-512 State: */
XSaveOpmask opmask_state;
XSaveZMM_Hi256 zmm_hi256_state;
XSaveHi16_ZMM hi16_zmm_state;
/* PKRU State: */
XSavePKRU pkru_state;
} X86XSaveArea;
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, avx_state) != 0x240);
QEMU_BUILD_BUG_ON(sizeof(XSaveAVX) != 0x100); QEMU_BUILD_BUG_ON(sizeof(XSaveAVX) != 0x100);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, bndreg_state) != 0x3c0);
QEMU_BUILD_BUG_ON(sizeof(XSaveBNDREG) != 0x40); QEMU_BUILD_BUG_ON(sizeof(XSaveBNDREG) != 0x40);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, bndcsr_state) != 0x400);
QEMU_BUILD_BUG_ON(sizeof(XSaveBNDCSR) != 0x40); QEMU_BUILD_BUG_ON(sizeof(XSaveBNDCSR) != 0x40);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, opmask_state) != 0x440);
QEMU_BUILD_BUG_ON(sizeof(XSaveOpmask) != 0x40); QEMU_BUILD_BUG_ON(sizeof(XSaveOpmask) != 0x40);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, zmm_hi256_state) != 0x480);
QEMU_BUILD_BUG_ON(sizeof(XSaveZMM_Hi256) != 0x200); QEMU_BUILD_BUG_ON(sizeof(XSaveZMM_Hi256) != 0x200);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, hi16_zmm_state) != 0x680);
QEMU_BUILD_BUG_ON(sizeof(XSaveHi16_ZMM) != 0x400); QEMU_BUILD_BUG_ON(sizeof(XSaveHi16_ZMM) != 0x400);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, pkru_state) != 0xA80);
QEMU_BUILD_BUG_ON(sizeof(XSavePKRU) != 0x8); QEMU_BUILD_BUG_ON(sizeof(XSavePKRU) != 0x8);
typedef struct ExtSaveArea {
uint32_t feature, bits;
uint32_t offset, size;
} ExtSaveArea;
#define XSAVE_STATE_AREA_COUNT (XSTATE_PKRU_BIT + 1)
extern ExtSaveArea x86_ext_save_areas[XSAVE_STATE_AREA_COUNT];
typedef enum TPRAccess { typedef enum TPRAccess {
TPR_ACCESS_READ, TPR_ACCESS_READ,
TPR_ACCESS_WRITE, TPR_ACCESS_WRITE,
@ -1637,6 +1619,7 @@ typedef struct CPUX86State {
uint64_t apic_bus_freq; uint64_t apic_bus_freq;
#if defined(CONFIG_KVM) || defined(CONFIG_HVF) #if defined(CONFIG_KVM) || defined(CONFIG_HVF)
void *xsave_buf; void *xsave_buf;
uint32_t xsave_buf_len;
#endif #endif
#if defined(CONFIG_KVM) #if defined(CONFIG_KVM)
struct kvm_nested_state *nested_state; struct kvm_nested_state *nested_state;
@ -2197,8 +2180,8 @@ void x86_cpu_dump_local_apic_state(CPUState *cs, int flags);
/* cpu.c */ /* cpu.c */
bool cpu_is_bsp(X86CPU *cpu); bool cpu_is_bsp(X86CPU *cpu);
void x86_cpu_xrstor_all_areas(X86CPU *cpu, const X86XSaveArea *buf); void x86_cpu_xrstor_all_areas(X86CPU *cpu, const void *buf, uint32_t buflen);
void x86_cpu_xsave_all_areas(X86CPU *cpu, X86XSaveArea *buf); void x86_cpu_xsave_all_areas(X86CPU *cpu, void *buf, uint32_t buflen);
void x86_update_hflags(CPUX86State* env); void x86_update_hflags(CPUX86State* env);
static inline bool hyperv_feat_enabled(X86CPU *cpu, int feat) static inline bool hyperv_feat_enabled(X86CPU *cpu, int feat)

View File

@ -30,6 +30,33 @@ static void hvf_cpu_max_instance_init(X86CPU *cpu)
hvf_get_supported_cpuid(0xC0000000, 0, R_EAX); hvf_get_supported_cpuid(0xC0000000, 0, R_EAX);
} }
static void hvf_cpu_xsave_init(void)
{
static bool first = true;
int i;
if (!first) {
return;
}
first = false;
/* x87 and SSE states are in the legacy region of the XSAVE area. */
x86_ext_save_areas[XSTATE_FP_BIT].offset = 0;
x86_ext_save_areas[XSTATE_SSE_BIT].offset = 0;
for (i = XSTATE_SSE_BIT + 1; i < XSAVE_STATE_AREA_COUNT; i++) {
ExtSaveArea *esa = &x86_ext_save_areas[i];
if (esa->size) {
int sz = hvf_get_supported_cpuid(0xd, i, R_EAX);
if (sz != 0) {
assert(esa->size == sz);
esa->offset = hvf_get_supported_cpuid(0xd, i, R_EBX);
}
}
}
}
static void hvf_cpu_instance_init(CPUState *cs) static void hvf_cpu_instance_init(CPUState *cs)
{ {
X86CPU *cpu = X86_CPU(cs); X86CPU *cpu = X86_CPU(cs);
@ -42,6 +69,8 @@ static void hvf_cpu_instance_init(CPUState *cs)
if (cpu->max_features) { if (cpu->max_features) {
hvf_cpu_max_instance_init(cpu); hvf_cpu_max_instance_init(cpu);
} }
hvf_cpu_xsave_init();
} }
static void hvf_cpu_accel_class_init(ObjectClass *oc, void *data) static void hvf_cpu_accel_class_init(ObjectClass *oc, void *data)

View File

@ -267,7 +267,14 @@ int hvf_arch_init_vcpu(CPUState *cpu)
wvmcs(cpu->hvf->fd, VMCS_TPR_THRESHOLD, 0); wvmcs(cpu->hvf->fd, VMCS_TPR_THRESHOLD, 0);
x86cpu = X86_CPU(cpu); x86cpu = X86_CPU(cpu);
x86cpu->env.xsave_buf = qemu_memalign(4096, 4096); x86cpu->env.xsave_buf_len = 4096;
x86cpu->env.xsave_buf = qemu_memalign(4096, x86cpu->env.xsave_buf_len);
/*
* The allocated storage must be large enough for all of the
* possible XSAVE state components.
*/
assert(hvf_get_supported_cpuid(0xd, 0, R_ECX) <= x86cpu->env.xsave_buf_len);
hv_vcpu_enable_native_msr(cpu->hvf->fd, MSR_STAR, 1); hv_vcpu_enable_native_msr(cpu->hvf->fd, MSR_STAR, 1);
hv_vcpu_enable_native_msr(cpu->hvf->fd, MSR_LSTAR, 1); hv_vcpu_enable_native_msr(cpu->hvf->fd, MSR_LSTAR, 1);

View File

@ -73,14 +73,12 @@ void hvf_get_segment(SegmentCache *qseg, struct vmx_segment *vmx_seg)
void hvf_put_xsave(CPUState *cpu_state) void hvf_put_xsave(CPUState *cpu_state)
{ {
void *xsave = X86_CPU(cpu_state)->env.xsave_buf;
uint32_t xsave_len = X86_CPU(cpu_state)->env.xsave_buf_len;
struct X86XSaveArea *xsave; x86_cpu_xsave_all_areas(X86_CPU(cpu_state), xsave, xsave_len);
xsave = X86_CPU(cpu_state)->env.xsave_buf; if (hv_vcpu_write_fpstate(cpu_state->hvf->fd, xsave, xsave_len)) {
x86_cpu_xsave_all_areas(X86_CPU(cpu_state), xsave);
if (hv_vcpu_write_fpstate(cpu_state->hvf->fd, (void*)xsave, 4096)) {
abort(); abort();
} }
} }
@ -158,15 +156,14 @@ void hvf_put_msrs(CPUState *cpu_state)
void hvf_get_xsave(CPUState *cpu_state) void hvf_get_xsave(CPUState *cpu_state)
{ {
struct X86XSaveArea *xsave; void *xsave = X86_CPU(cpu_state)->env.xsave_buf;
uint32_t xsave_len = X86_CPU(cpu_state)->env.xsave_buf_len;
xsave = X86_CPU(cpu_state)->env.xsave_buf; if (hv_vcpu_read_fpstate(cpu_state->hvf->fd, xsave, xsave_len)) {
if (hv_vcpu_read_fpstate(cpu_state->hvf->fd, (void*)xsave, 4096)) {
abort(); abort();
} }
x86_cpu_xrstor_all_areas(X86_CPU(cpu_state), xsave); x86_cpu_xrstor_all_areas(X86_CPU(cpu_state), xsave, xsave_len);
} }
void hvf_get_segments(CPUState *cpu_state) void hvf_get_segments(CPUState *cpu_state)

View File

@ -122,6 +122,34 @@ static void kvm_cpu_max_instance_init(X86CPU *cpu)
kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX); kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX);
} }
static void kvm_cpu_xsave_init(void)
{
static bool first = true;
KVMState *s = kvm_state;
int i;
if (!first) {
return;
}
first = false;
/* x87 and SSE states are in the legacy region of the XSAVE area. */
x86_ext_save_areas[XSTATE_FP_BIT].offset = 0;
x86_ext_save_areas[XSTATE_SSE_BIT].offset = 0;
for (i = XSTATE_SSE_BIT + 1; i < XSAVE_STATE_AREA_COUNT; i++) {
ExtSaveArea *esa = &x86_ext_save_areas[i];
if (esa->size) {
int sz = kvm_arch_get_supported_cpuid(s, 0xd, i, R_EAX);
if (sz != 0) {
assert(esa->size == sz);
esa->offset = kvm_arch_get_supported_cpuid(s, 0xd, i, R_EBX);
}
}
}
}
static void kvm_cpu_instance_init(CPUState *cs) static void kvm_cpu_instance_init(CPUState *cs)
{ {
X86CPU *cpu = X86_CPU(cs); X86CPU *cpu = X86_CPU(cs);
@ -141,6 +169,8 @@ static void kvm_cpu_instance_init(CPUState *cs)
if (cpu->max_features) { if (cpu->max_features) {
kvm_cpu_max_instance_init(cpu); kvm_cpu_max_instance_init(cpu);
} }
kvm_cpu_xsave_init();
} }
static void kvm_cpu_accel_class_init(ObjectClass *oc, void *data) static void kvm_cpu_accel_class_init(ObjectClass *oc, void *data)

View File

@ -1888,8 +1888,16 @@ int kvm_arch_init_vcpu(CPUState *cs)
} }
if (has_xsave) { if (has_xsave) {
env->xsave_buf = qemu_memalign(4096, sizeof(struct kvm_xsave)); env->xsave_buf_len = sizeof(struct kvm_xsave);
memset(env->xsave_buf, 0, sizeof(struct kvm_xsave)); env->xsave_buf = qemu_memalign(4096, env->xsave_buf_len);
memset(env->xsave_buf, 0, env->xsave_buf_len);
/*
* The allocated storage must be large enough for all of the
* possible XSAVE state components.
*/
assert(kvm_arch_get_supported_cpuid(kvm_state, 0xd, 0, R_ECX)
<= env->xsave_buf_len);
} }
max_nested_state_len = kvm_max_nested_state_length(); max_nested_state_len = kvm_max_nested_state_length();
@ -2466,54 +2474,15 @@ static int kvm_put_fpu(X86CPU *cpu)
return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_FPU, &fpu); return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_FPU, &fpu);
} }
#define XSAVE_FCW_FSW 0
#define XSAVE_FTW_FOP 1
#define XSAVE_CWD_RIP 2
#define XSAVE_CWD_RDP 4
#define XSAVE_MXCSR 6
#define XSAVE_ST_SPACE 8
#define XSAVE_XMM_SPACE 40
#define XSAVE_XSTATE_BV 128
#define XSAVE_YMMH_SPACE 144
#define XSAVE_BNDREGS 240
#define XSAVE_BNDCSR 256
#define XSAVE_OPMASK 272
#define XSAVE_ZMM_Hi256 288
#define XSAVE_Hi16_ZMM 416
#define XSAVE_PKRU 672
#define XSAVE_BYTE_OFFSET(word_offset) \
((word_offset) * sizeof_field(struct kvm_xsave, region[0]))
#define ASSERT_OFFSET(word_offset, field) \
QEMU_BUILD_BUG_ON(XSAVE_BYTE_OFFSET(word_offset) != \
offsetof(X86XSaveArea, field))
ASSERT_OFFSET(XSAVE_FCW_FSW, legacy.fcw);
ASSERT_OFFSET(XSAVE_FTW_FOP, legacy.ftw);
ASSERT_OFFSET(XSAVE_CWD_RIP, legacy.fpip);
ASSERT_OFFSET(XSAVE_CWD_RDP, legacy.fpdp);
ASSERT_OFFSET(XSAVE_MXCSR, legacy.mxcsr);
ASSERT_OFFSET(XSAVE_ST_SPACE, legacy.fpregs);
ASSERT_OFFSET(XSAVE_XMM_SPACE, legacy.xmm_regs);
ASSERT_OFFSET(XSAVE_XSTATE_BV, header.xstate_bv);
ASSERT_OFFSET(XSAVE_YMMH_SPACE, avx_state);
ASSERT_OFFSET(XSAVE_BNDREGS, bndreg_state);
ASSERT_OFFSET(XSAVE_BNDCSR, bndcsr_state);
ASSERT_OFFSET(XSAVE_OPMASK, opmask_state);
ASSERT_OFFSET(XSAVE_ZMM_Hi256, zmm_hi256_state);
ASSERT_OFFSET(XSAVE_Hi16_ZMM, hi16_zmm_state);
ASSERT_OFFSET(XSAVE_PKRU, pkru_state);
static int kvm_put_xsave(X86CPU *cpu) static int kvm_put_xsave(X86CPU *cpu)
{ {
CPUX86State *env = &cpu->env; CPUX86State *env = &cpu->env;
X86XSaveArea *xsave = env->xsave_buf; void *xsave = env->xsave_buf;
if (!has_xsave) { if (!has_xsave) {
return kvm_put_fpu(cpu); return kvm_put_fpu(cpu);
} }
x86_cpu_xsave_all_areas(cpu, xsave); x86_cpu_xsave_all_areas(cpu, xsave, env->xsave_buf_len);
return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_XSAVE, xsave); return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_XSAVE, xsave);
} }
@ -3158,7 +3127,7 @@ static int kvm_get_fpu(X86CPU *cpu)
static int kvm_get_xsave(X86CPU *cpu) static int kvm_get_xsave(X86CPU *cpu)
{ {
CPUX86State *env = &cpu->env; CPUX86State *env = &cpu->env;
X86XSaveArea *xsave = env->xsave_buf; void *xsave = env->xsave_buf;
int ret; int ret;
if (!has_xsave) { if (!has_xsave) {
@ -3169,7 +3138,7 @@ static int kvm_get_xsave(X86CPU *cpu)
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
x86_cpu_xrstor_all_areas(cpu, xsave); x86_cpu_xrstor_all_areas(cpu, xsave, env->xsave_buf_len);
return 0; return 0;
} }

View File

@ -20,6 +20,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include <math.h> #include <math.h>
#include "cpu.h" #include "cpu.h"
#include "tcg-cpu.h"
#include "exec/helper-proto.h" #include "exec/helper-proto.h"
#include "fpu/softfloat.h" #include "fpu/softfloat.h"
#include "fpu/softfloat-macros.h" #include "fpu/softfloat-macros.h"

View File

@ -80,6 +80,24 @@ static void tcg_cpu_class_init(CPUClass *cc)
cc->init_accel_cpu = tcg_cpu_init_ops; cc->init_accel_cpu = tcg_cpu_init_ops;
} }
static void tcg_cpu_xsave_init(void)
{
#define XO(bit, field) \
x86_ext_save_areas[bit].offset = offsetof(X86XSaveArea, field);
XO(XSTATE_FP_BIT, legacy);
XO(XSTATE_SSE_BIT, legacy);
XO(XSTATE_YMM_BIT, avx_state);
XO(XSTATE_BNDREGS_BIT, bndreg_state);
XO(XSTATE_BNDCSR_BIT, bndcsr_state);
XO(XSTATE_OPMASK_BIT, opmask_state);
XO(XSTATE_ZMM_Hi256_BIT, zmm_hi256_state);
XO(XSTATE_Hi16_ZMM_BIT, hi16_zmm_state);
XO(XSTATE_PKRU_BIT, pkru_state);
#undef XO
}
/* /*
* TCG-specific defaults that override all CPU models when using TCG * TCG-specific defaults that override all CPU models when using TCG
*/ */
@ -93,6 +111,8 @@ static void tcg_cpu_instance_init(CPUState *cs)
X86CPU *cpu = X86_CPU(cs); X86CPU *cpu = X86_CPU(cs);
/* Special cases not set in the X86CPUDefinition structs: */ /* Special cases not set in the X86CPUDefinition structs: */
x86_cpu_apply_props(cpu, tcg_default_props); x86_cpu_apply_props(cpu, tcg_default_props);
tcg_cpu_xsave_init();
} }
static void tcg_cpu_accel_class_init(ObjectClass *oc, void *data) static void tcg_cpu_accel_class_init(ObjectClass *oc, void *data)

View File

@ -19,6 +19,63 @@
#ifndef TCG_CPU_H #ifndef TCG_CPU_H
#define TCG_CPU_H #define TCG_CPU_H
#define XSAVE_FCW_FSW_OFFSET 0x000
#define XSAVE_FTW_FOP_OFFSET 0x004
#define XSAVE_CWD_RIP_OFFSET 0x008
#define XSAVE_CWD_RDP_OFFSET 0x010
#define XSAVE_MXCSR_OFFSET 0x018
#define XSAVE_ST_SPACE_OFFSET 0x020
#define XSAVE_XMM_SPACE_OFFSET 0x0a0
#define XSAVE_XSTATE_BV_OFFSET 0x200
#define XSAVE_AVX_OFFSET 0x240
#define XSAVE_BNDREG_OFFSET 0x3c0
#define XSAVE_BNDCSR_OFFSET 0x400
#define XSAVE_OPMASK_OFFSET 0x440
#define XSAVE_ZMM_HI256_OFFSET 0x480
#define XSAVE_HI16_ZMM_OFFSET 0x680
#define XSAVE_PKRU_OFFSET 0xa80
typedef struct X86XSaveArea {
X86LegacyXSaveArea legacy;
X86XSaveHeader header;
/* Extended save areas: */
/* AVX State: */
XSaveAVX avx_state;
/* Ensure that XSaveBNDREG is properly aligned. */
uint8_t padding[XSAVE_BNDREG_OFFSET
- sizeof(X86LegacyXSaveArea)
- sizeof(X86XSaveHeader)
- sizeof(XSaveAVX)];
/* MPX State: */
XSaveBNDREG bndreg_state;
XSaveBNDCSR bndcsr_state;
/* AVX-512 State: */
XSaveOpmask opmask_state;
XSaveZMM_Hi256 zmm_hi256_state;
XSaveHi16_ZMM hi16_zmm_state;
/* PKRU State: */
XSavePKRU pkru_state;
} X86XSaveArea;
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, legacy.fcw) != XSAVE_FCW_FSW_OFFSET);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, legacy.ftw) != XSAVE_FTW_FOP_OFFSET);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, legacy.fpip) != XSAVE_CWD_RIP_OFFSET);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, legacy.fpdp) != XSAVE_CWD_RDP_OFFSET);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, legacy.mxcsr) != XSAVE_MXCSR_OFFSET);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, legacy.fpregs) != XSAVE_ST_SPACE_OFFSET);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, legacy.xmm_regs) != XSAVE_XMM_SPACE_OFFSET);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, avx_state) != XSAVE_AVX_OFFSET);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, bndreg_state) != XSAVE_BNDREG_OFFSET);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, bndcsr_state) != XSAVE_BNDCSR_OFFSET);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, opmask_state) != XSAVE_OPMASK_OFFSET);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, zmm_hi256_state) != XSAVE_ZMM_HI256_OFFSET);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, hi16_zmm_state) != XSAVE_HI16_ZMM_OFFSET);
QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, pkru_state) != XSAVE_PKRU_OFFSET);
bool tcg_cpu_realizefn(CPUState *cs, Error **errp); bool tcg_cpu_realizefn(CPUState *cs, Error **errp);
#endif /* TCG_CPU_H */ #endif /* TCG_CPU_H */

View File

@ -6,14 +6,23 @@
#include "cpu.h" #include "cpu.h"
void x86_cpu_xsave_all_areas(X86CPU *cpu, X86XSaveArea *buf) void x86_cpu_xsave_all_areas(X86CPU *cpu, void *buf, uint32_t buflen)
{ {
CPUX86State *env = &cpu->env; CPUX86State *env = &cpu->env;
X86XSaveArea *xsave = buf; const ExtSaveArea *e, *f;
uint16_t cwd, swd, twd;
int i; int i;
memset(xsave, 0, sizeof(X86XSaveArea));
X86LegacyXSaveArea *legacy;
X86XSaveHeader *header;
uint16_t cwd, swd, twd;
memset(buf, 0, buflen);
e = &x86_ext_save_areas[XSTATE_FP_BIT];
legacy = buf + e->offset;
header = buf + e->offset + sizeof(*legacy);
twd = 0; twd = 0;
swd = env->fpus & ~(7 << 11); swd = env->fpus & ~(7 << 11);
swd |= (env->fpstt & 7) << 11; swd |= (env->fpstt & 7) << 11;
@ -21,92 +30,222 @@ void x86_cpu_xsave_all_areas(X86CPU *cpu, X86XSaveArea *buf)
for (i = 0; i < 8; ++i) { for (i = 0; i < 8; ++i) {
twd |= (!env->fptags[i]) << i; twd |= (!env->fptags[i]) << i;
} }
xsave->legacy.fcw = cwd; legacy->fcw = cwd;
xsave->legacy.fsw = swd; legacy->fsw = swd;
xsave->legacy.ftw = twd; legacy->ftw = twd;
xsave->legacy.fpop = env->fpop; legacy->fpop = env->fpop;
xsave->legacy.fpip = env->fpip; legacy->fpip = env->fpip;
xsave->legacy.fpdp = env->fpdp; legacy->fpdp = env->fpdp;
memcpy(&xsave->legacy.fpregs, env->fpregs, memcpy(&legacy->fpregs, env->fpregs,
sizeof env->fpregs); sizeof(env->fpregs));
xsave->legacy.mxcsr = env->mxcsr; legacy->mxcsr = env->mxcsr;
xsave->header.xstate_bv = env->xstate_bv;
memcpy(&xsave->bndreg_state.bnd_regs, env->bnd_regs,
sizeof env->bnd_regs);
xsave->bndcsr_state.bndcsr = env->bndcs_regs;
memcpy(&xsave->opmask_state.opmask_regs, env->opmask_regs,
sizeof env->opmask_regs);
for (i = 0; i < CPU_NB_REGS; i++) { for (i = 0; i < CPU_NB_REGS; i++) {
uint8_t *xmm = xsave->legacy.xmm_regs[i]; uint8_t *xmm = legacy->xmm_regs[i];
uint8_t *ymmh = xsave->avx_state.ymmh[i];
uint8_t *zmmh = xsave->zmm_hi256_state.zmm_hi256[i];
stq_p(xmm, env->xmm_regs[i].ZMM_Q(0)); stq_p(xmm, env->xmm_regs[i].ZMM_Q(0));
stq_p(xmm+8, env->xmm_regs[i].ZMM_Q(1)); stq_p(xmm + 8, env->xmm_regs[i].ZMM_Q(1));
stq_p(ymmh, env->xmm_regs[i].ZMM_Q(2)); }
stq_p(ymmh+8, env->xmm_regs[i].ZMM_Q(3));
stq_p(zmmh, env->xmm_regs[i].ZMM_Q(4)); header->xstate_bv = env->xstate_bv;
stq_p(zmmh+8, env->xmm_regs[i].ZMM_Q(5));
stq_p(zmmh+16, env->xmm_regs[i].ZMM_Q(6)); e = &x86_ext_save_areas[XSTATE_YMM_BIT];
stq_p(zmmh+24, env->xmm_regs[i].ZMM_Q(7)); if (e->size && e->offset) {
XSaveAVX *avx;
avx = buf + e->offset;
for (i = 0; i < CPU_NB_REGS; i++) {
uint8_t *ymmh = avx->ymmh[i];
stq_p(ymmh, env->xmm_regs[i].ZMM_Q(2));
stq_p(ymmh + 8, env->xmm_regs[i].ZMM_Q(3));
}
}
e = &x86_ext_save_areas[XSTATE_BNDREGS_BIT];
if (e->size && e->offset) {
XSaveBNDREG *bndreg;
XSaveBNDCSR *bndcsr;
f = &x86_ext_save_areas[XSTATE_BNDCSR_BIT];
assert(f->size);
assert(f->offset);
bndreg = buf + e->offset;
bndcsr = buf + f->offset;
memcpy(&bndreg->bnd_regs, env->bnd_regs,
sizeof(env->bnd_regs));
bndcsr->bndcsr = env->bndcs_regs;
}
e = &x86_ext_save_areas[XSTATE_OPMASK_BIT];
if (e->size && e->offset) {
XSaveOpmask *opmask;
XSaveZMM_Hi256 *zmm_hi256;
#ifdef TARGET_X86_64
XSaveHi16_ZMM *hi16_zmm;
#endif
f = &x86_ext_save_areas[XSTATE_ZMM_Hi256_BIT];
assert(f->size);
assert(f->offset);
opmask = buf + e->offset;
zmm_hi256 = buf + f->offset;
memcpy(&opmask->opmask_regs, env->opmask_regs,
sizeof(env->opmask_regs));
for (i = 0; i < CPU_NB_REGS; i++) {
uint8_t *zmmh = zmm_hi256->zmm_hi256[i];
stq_p(zmmh, env->xmm_regs[i].ZMM_Q(4));
stq_p(zmmh + 8, env->xmm_regs[i].ZMM_Q(5));
stq_p(zmmh + 16, env->xmm_regs[i].ZMM_Q(6));
stq_p(zmmh + 24, env->xmm_regs[i].ZMM_Q(7));
}
#ifdef TARGET_X86_64
f = &x86_ext_save_areas[XSTATE_Hi16_ZMM_BIT];
assert(f->size);
assert(f->offset);
hi16_zmm = buf + f->offset;
memcpy(&hi16_zmm->hi16_zmm, &env->xmm_regs[16],
16 * sizeof(env->xmm_regs[16]));
#endif
} }
#ifdef TARGET_X86_64 #ifdef TARGET_X86_64
memcpy(&xsave->hi16_zmm_state.hi16_zmm, &env->xmm_regs[16], e = &x86_ext_save_areas[XSTATE_PKRU_BIT];
16 * sizeof env->xmm_regs[16]); if (e->size && e->offset) {
memcpy(&xsave->pkru_state, &env->pkru, sizeof env->pkru); XSavePKRU *pkru = buf + e->offset;
#endif
memcpy(pkru, &env->pkru, sizeof(env->pkru));
}
#endif
} }
void x86_cpu_xrstor_all_areas(X86CPU *cpu, const X86XSaveArea *buf) void x86_cpu_xrstor_all_areas(X86CPU *cpu, const void *buf, uint32_t buflen)
{ {
CPUX86State *env = &cpu->env; CPUX86State *env = &cpu->env;
const X86XSaveArea *xsave = buf; const ExtSaveArea *e, *f, *g;
int i; int i;
const X86LegacyXSaveArea *legacy;
const X86XSaveHeader *header;
uint16_t cwd, swd, twd; uint16_t cwd, swd, twd;
cwd = xsave->legacy.fcw;
swd = xsave->legacy.fsw; e = &x86_ext_save_areas[XSTATE_FP_BIT];
twd = xsave->legacy.ftw;
env->fpop = xsave->legacy.fpop; legacy = buf + e->offset;
header = buf + e->offset + sizeof(*legacy);
cwd = legacy->fcw;
swd = legacy->fsw;
twd = legacy->ftw;
env->fpop = legacy->fpop;
env->fpstt = (swd >> 11) & 7; env->fpstt = (swd >> 11) & 7;
env->fpus = swd; env->fpus = swd;
env->fpuc = cwd; env->fpuc = cwd;
for (i = 0; i < 8; ++i) { for (i = 0; i < 8; ++i) {
env->fptags[i] = !((twd >> i) & 1); env->fptags[i] = !((twd >> i) & 1);
} }
env->fpip = xsave->legacy.fpip; env->fpip = legacy->fpip;
env->fpdp = xsave->legacy.fpdp; env->fpdp = legacy->fpdp;
env->mxcsr = xsave->legacy.mxcsr; env->mxcsr = legacy->mxcsr;
memcpy(env->fpregs, &xsave->legacy.fpregs, memcpy(env->fpregs, &legacy->fpregs,
sizeof env->fpregs); sizeof(env->fpregs));
env->xstate_bv = xsave->header.xstate_bv;
memcpy(env->bnd_regs, &xsave->bndreg_state.bnd_regs,
sizeof env->bnd_regs);
env->bndcs_regs = xsave->bndcsr_state.bndcsr;
memcpy(env->opmask_regs, &xsave->opmask_state.opmask_regs,
sizeof env->opmask_regs);
for (i = 0; i < CPU_NB_REGS; i++) { for (i = 0; i < CPU_NB_REGS; i++) {
const uint8_t *xmm = xsave->legacy.xmm_regs[i]; const uint8_t *xmm = legacy->xmm_regs[i];
const uint8_t *ymmh = xsave->avx_state.ymmh[i];
const uint8_t *zmmh = xsave->zmm_hi256_state.zmm_hi256[i];
env->xmm_regs[i].ZMM_Q(0) = ldq_p(xmm); env->xmm_regs[i].ZMM_Q(0) = ldq_p(xmm);
env->xmm_regs[i].ZMM_Q(1) = ldq_p(xmm+8); env->xmm_regs[i].ZMM_Q(1) = ldq_p(xmm + 8);
env->xmm_regs[i].ZMM_Q(2) = ldq_p(ymmh); }
env->xmm_regs[i].ZMM_Q(3) = ldq_p(ymmh+8);
env->xmm_regs[i].ZMM_Q(4) = ldq_p(zmmh); env->xstate_bv = header->xstate_bv;
env->xmm_regs[i].ZMM_Q(5) = ldq_p(zmmh+8);
env->xmm_regs[i].ZMM_Q(6) = ldq_p(zmmh+16); e = &x86_ext_save_areas[XSTATE_YMM_BIT];
env->xmm_regs[i].ZMM_Q(7) = ldq_p(zmmh+24); if (e->size && e->offset) {
const XSaveAVX *avx;
avx = buf + e->offset;
for (i = 0; i < CPU_NB_REGS; i++) {
const uint8_t *ymmh = avx->ymmh[i];
env->xmm_regs[i].ZMM_Q(2) = ldq_p(ymmh);
env->xmm_regs[i].ZMM_Q(3) = ldq_p(ymmh + 8);
}
}
e = &x86_ext_save_areas[XSTATE_BNDREGS_BIT];
if (e->size && e->offset) {
const XSaveBNDREG *bndreg;
const XSaveBNDCSR *bndcsr;
f = &x86_ext_save_areas[XSTATE_BNDCSR_BIT];
assert(f->size);
assert(f->offset);
bndreg = buf + e->offset;
bndcsr = buf + f->offset;
memcpy(env->bnd_regs, &bndreg->bnd_regs,
sizeof(env->bnd_regs));
env->bndcs_regs = bndcsr->bndcsr;
}
e = &x86_ext_save_areas[XSTATE_OPMASK_BIT];
if (e->size && e->offset) {
const XSaveOpmask *opmask;
const XSaveZMM_Hi256 *zmm_hi256;
#ifdef TARGET_X86_64
const XSaveHi16_ZMM *hi16_zmm;
#endif
f = &x86_ext_save_areas[XSTATE_ZMM_Hi256_BIT];
assert(f->size);
assert(f->offset);
g = &x86_ext_save_areas[XSTATE_Hi16_ZMM_BIT];
assert(g->size);
assert(g->offset);
opmask = buf + e->offset;
zmm_hi256 = buf + f->offset;
#ifdef TARGET_X86_64
hi16_zmm = buf + g->offset;
#endif
memcpy(env->opmask_regs, &opmask->opmask_regs,
sizeof(env->opmask_regs));
for (i = 0; i < CPU_NB_REGS; i++) {
const uint8_t *zmmh = zmm_hi256->zmm_hi256[i];
env->xmm_regs[i].ZMM_Q(4) = ldq_p(zmmh);
env->xmm_regs[i].ZMM_Q(5) = ldq_p(zmmh + 8);
env->xmm_regs[i].ZMM_Q(6) = ldq_p(zmmh + 16);
env->xmm_regs[i].ZMM_Q(7) = ldq_p(zmmh + 24);
}
#ifdef TARGET_X86_64
memcpy(&env->xmm_regs[16], &hi16_zmm->hi16_zmm,
16 * sizeof(env->xmm_regs[16]));
#endif
} }
#ifdef TARGET_X86_64 #ifdef TARGET_X86_64
memcpy(&env->xmm_regs[16], &xsave->hi16_zmm_state.hi16_zmm, e = &x86_ext_save_areas[XSTATE_PKRU_BIT];
16 * sizeof env->xmm_regs[16]); if (e->size && e->offset) {
memcpy(&env->pkru, &xsave->pkru_state, sizeof env->pkru); const XSavePKRU *pkru;
#endif
pkru = buf + e->offset;
memcpy(&env->pkru, pkru, sizeof(env->pkru));
}
#endif
} }

View File

@ -113,7 +113,7 @@ class Engine(object):
vcpus = src.command("query-cpus-fast") vcpus = src.command("query-cpus-fast")
src_threads = [] src_threads = []
for vcpu in vcpus: for vcpu in vcpus:
src_threads.append(vcpu["thread_id"]) src_threads.append(vcpu["thread-id"])
# XXX how to get dst timings on remote host ? # XXX how to get dst timings on remote host ?
@ -153,7 +153,7 @@ class Engine(object):
max_bandwidth=scenario._bandwidth * 1024 * 1024) max_bandwidth=scenario._bandwidth * 1024 * 1024)
resp = src.command("migrate-set-parameters", resp = src.command("migrate-set-parameters",
downtime_limit=scenario._downtime / 1024.0) downtime_limit=scenario._downtime)
if scenario._compression_mt: if scenario._compression_mt:
resp = src.command("migrate-set-capabilities", resp = src.command("migrate-set-capabilities",

View File

@ -16,6 +16,11 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/> * License along with this library; if not, see <http://www.gnu.org/licenses/>
*/ */
/*
* Not so fast! You might want to read the 9p developer docs first:
* https://wiki.qemu.org/Documentation/9p
*/
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "libqtest.h" #include "libqtest.h"
#include "qemu/module.h" #include "qemu/module.h"

View File

@ -27,6 +27,10 @@
#include "migration-helpers.h" #include "migration-helpers.h"
#include "tests/migration/migration-test.h" #include "tests/migration/migration-test.h"
#if defined(__linux__)
#include "linux/kvm.h"
#endif
/* TODO actually test the results and get rid of this */ /* TODO actually test the results and get rid of this */
#define qtest_qmp_discard_response(...) qobject_unref(qtest_qmp(__VA_ARGS__)) #define qtest_qmp_discard_response(...) qobject_unref(qtest_qmp(__VA_ARGS__))
@ -467,6 +471,8 @@ typedef struct {
bool use_shmem; bool use_shmem;
/* only launch the target process */ /* only launch the target process */
bool only_target; bool only_target;
/* Use dirty ring if true; dirty logging otherwise */
bool use_dirty_ring;
char *opts_source; char *opts_source;
char *opts_target; char *opts_target;
} MigrateStart; } MigrateStart;
@ -573,11 +579,13 @@ static int test_migrate_start(QTestState **from, QTestState **to,
shmem_opts = g_strdup(""); shmem_opts = g_strdup("");
} }
cmd_source = g_strdup_printf("-accel kvm -accel tcg%s%s " cmd_source = g_strdup_printf("-accel kvm%s -accel tcg%s%s "
"-name source,debug-threads=on " "-name source,debug-threads=on "
"-m %s " "-m %s "
"-serial file:%s/src_serial " "-serial file:%s/src_serial "
"%s %s %s %s", "%s %s %s %s",
args->use_dirty_ring ?
",dirty-ring-size=4096" : "",
machine_opts ? " -machine " : "", machine_opts ? " -machine " : "",
machine_opts ? machine_opts : "", machine_opts ? machine_opts : "",
memory_size, tmpfs, memory_size, tmpfs,
@ -587,12 +595,14 @@ static int test_migrate_start(QTestState **from, QTestState **to,
*from = qtest_init(cmd_source); *from = qtest_init(cmd_source);
} }
cmd_target = g_strdup_printf("-accel kvm -accel tcg%s%s " cmd_target = g_strdup_printf("-accel kvm%s -accel tcg%s%s "
"-name target,debug-threads=on " "-name target,debug-threads=on "
"-m %s " "-m %s "
"-serial file:%s/dest_serial " "-serial file:%s/dest_serial "
"-incoming %s " "-incoming %s "
"%s %s %s %s", "%s %s %s %s",
args->use_dirty_ring ?
",dirty-ring-size=4096" : "",
machine_opts ? " -machine " : "", machine_opts ? " -machine " : "",
machine_opts ? machine_opts : "", machine_opts ? machine_opts : "",
memory_size, tmpfs, uri, memory_size, tmpfs, uri,
@ -785,12 +795,14 @@ static void test_baddest(void)
test_migrate_end(from, to, false); test_migrate_end(from, to, false);
} }
static void test_precopy_unix(void) static void test_precopy_unix_common(bool dirty_ring)
{ {
g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
MigrateStart *args = migrate_start_new(); MigrateStart *args = migrate_start_new();
QTestState *from, *to; QTestState *from, *to;
args->use_dirty_ring = dirty_ring;
if (test_migrate_start(&from, &to, uri, args)) { if (test_migrate_start(&from, &to, uri, args)) {
return; return;
} }
@ -825,6 +837,18 @@ static void test_precopy_unix(void)
test_migrate_end(from, to, true); test_migrate_end(from, to, true);
} }
static void test_precopy_unix(void)
{
/* Using default dirty logging */
test_precopy_unix_common(false);
}
static void test_precopy_unix_dirty_ring(void)
{
/* Using dirty ring tracking */
test_precopy_unix_common(true);
}
#if 0 #if 0
/* Currently upset on aarch64 TCG */ /* Currently upset on aarch64 TCG */
static void test_ignore_shared(void) static void test_ignore_shared(void)
@ -1369,6 +1393,29 @@ static void test_multifd_tcp_cancel(void)
test_migrate_end(from, to2, true); test_migrate_end(from, to2, true);
} }
static bool kvm_dirty_ring_supported(void)
{
#if defined(__linux__)
int ret, kvm_fd = open("/dev/kvm", O_RDONLY);
if (kvm_fd < 0) {
return false;
}
ret = ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_DIRTY_LOG_RING);
close(kvm_fd);
/* We test with 4096 slots */
if (ret < 4096) {
return false;
}
return true;
#else
return false;
#endif
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
char template[] = "/tmp/migration-test-XXXXXX"; char template[] = "/tmp/migration-test-XXXXXX";
@ -1439,6 +1486,11 @@ int main(int argc, char **argv)
qtest_add_func("/migration/multifd/tcp/zstd", test_multifd_tcp_zstd); qtest_add_func("/migration/multifd/tcp/zstd", test_multifd_tcp_zstd);
#endif #endif
if (kvm_dirty_ring_supported()) {
qtest_add_func("/migration/dirty_ring",
test_precopy_unix_dirty_ring);
}
ret = g_test_run(); ret = g_test_run();
g_assert_cmpint(ret, ==, 0); g_assert_cmpint(ret, ==, 0);

View File

@ -25,7 +25,7 @@ static void test_mon_explicit(const void *data)
g_autofree char *s = NULL; g_autofree char *s = NULL;
g_autofree char *cli = NULL; g_autofree char *cli = NULL;
cli = make_cli(data, "-smp 8 -numa node,nodeid=0,memdev=ram,cpus=0-3 " cli = make_cli(data, "-machine smp.cpus=8 -numa node,nodeid=0,memdev=ram,cpus=0-3 "
"-numa node,nodeid=1,cpus=4-7"); "-numa node,nodeid=1,cpus=4-7");
qts = qtest_init(cli); qts = qtest_init(cli);
@ -42,7 +42,7 @@ static void test_def_cpu_split(const void *data)
g_autofree char *s = NULL; g_autofree char *s = NULL;
g_autofree char *cli = NULL; g_autofree char *cli = NULL;
cli = make_cli(data, "-smp 8 -numa node,memdev=ram -numa node"); cli = make_cli(data, "-machine smp.cpus=8 -numa node,memdev=ram -numa node");
qts = qtest_init(cli); qts = qtest_init(cli);
s = qtest_hmp(qts, "info numa"); s = qtest_hmp(qts, "info numa");
@ -58,7 +58,7 @@ static void test_mon_partial(const void *data)
g_autofree char *s = NULL; g_autofree char *s = NULL;
g_autofree char *cli = NULL; g_autofree char *cli = NULL;
cli = make_cli(data, "-smp 8 " cli = make_cli(data, "-machine smp.cpus=8 "
"-numa node,nodeid=0,memdev=ram,cpus=0-1 " "-numa node,nodeid=0,memdev=ram,cpus=0-1 "
"-numa node,nodeid=1,cpus=4-5 "); "-numa node,nodeid=1,cpus=4-5 ");
qts = qtest_init(cli); qts = qtest_init(cli);
@ -86,7 +86,7 @@ static void test_query_cpus(const void *data)
QTestState *qts; QTestState *qts;
g_autofree char *cli = NULL; g_autofree char *cli = NULL;
cli = make_cli(data, "-smp 8 -numa node,memdev=ram,cpus=0-3 " cli = make_cli(data, "-machine smp.cpus=8 -numa node,memdev=ram,cpus=0-3 "
"-numa node,cpus=4-7"); "-numa node,cpus=4-7");
qts = qtest_init(cli); qts = qtest_init(cli);
cpus = get_cpus(qts, &resp); cpus = get_cpus(qts, &resp);
@ -124,7 +124,7 @@ static void pc_numa_cpu(const void *data)
QTestState *qts; QTestState *qts;
g_autofree char *cli = NULL; g_autofree char *cli = NULL;
cli = make_cli(data, "-cpu pentium -smp 8,sockets=2,cores=2,threads=2 " cli = make_cli(data, "-cpu pentium -machine smp.cpus=8,smp.sockets=2,smp.cores=2,smp.threads=2 "
"-numa node,nodeid=0,memdev=ram -numa node,nodeid=1 " "-numa node,nodeid=0,memdev=ram -numa node,nodeid=1 "
"-numa cpu,node-id=1,socket-id=0 " "-numa cpu,node-id=1,socket-id=0 "
"-numa cpu,node-id=0,socket-id=1,core-id=0 " "-numa cpu,node-id=0,socket-id=1,core-id=0 "
@ -177,7 +177,7 @@ static void spapr_numa_cpu(const void *data)
QTestState *qts; QTestState *qts;
g_autofree char *cli = NULL; g_autofree char *cli = NULL;
cli = make_cli(data, "-smp 4,cores=4 " cli = make_cli(data, "-machine smp.cpus=4,smp.cores=4 "
"-numa node,nodeid=0,memdev=ram -numa node,nodeid=1 " "-numa node,nodeid=0,memdev=ram -numa node,nodeid=1 "
"-numa cpu,node-id=0,core-id=0 " "-numa cpu,node-id=0,core-id=0 "
"-numa cpu,node-id=0,core-id=1 " "-numa cpu,node-id=0,core-id=1 "
@ -222,7 +222,7 @@ static void aarch64_numa_cpu(const void *data)
QTestState *qts; QTestState *qts;
g_autofree char *cli = NULL; g_autofree char *cli = NULL;
cli = make_cli(data, "-smp 2 " cli = make_cli(data, "-machine smp.cpus=2 "
"-numa node,nodeid=0,memdev=ram -numa node,nodeid=1 " "-numa node,nodeid=0,memdev=ram -numa node,nodeid=1 "
"-numa cpu,node-id=1,thread-id=0 " "-numa cpu,node-id=1,thread-id=0 "
"-numa cpu,node-id=0,thread-id=1"); "-numa cpu,node-id=0,thread-id=1");
@ -265,7 +265,7 @@ static void pc_dynamic_cpu_cfg(const void *data)
QTestState *qs; QTestState *qs;
g_autofree char *cli = NULL; g_autofree char *cli = NULL;
cli = make_cli(data, "-nodefaults --preconfig -smp 2"); cli = make_cli(data, "-nodefaults --preconfig -machine smp.cpus=2");
qs = qtest_init(cli); qs = qtest_init(cli);
/* create 2 numa nodes */ /* create 2 numa nodes */
@ -324,7 +324,7 @@ static void pc_hmat_build_cfg(const void *data)
g_autofree char *cli = NULL; g_autofree char *cli = NULL;
cli = make_cli(data, "-nodefaults --preconfig -machine hmat=on " cli = make_cli(data, "-nodefaults --preconfig -machine hmat=on "
"-smp 2,sockets=2 " "-machine smp.cpus=2,smp.sockets=2 "
"-m 128M,slots=2,maxmem=1G " "-m 128M,slots=2,maxmem=1G "
"-object memory-backend-ram,size=64M,id=m0 " "-object memory-backend-ram,size=64M,id=m0 "
"-object memory-backend-ram,size=64M,id=m1 " "-object memory-backend-ram,size=64M,id=m1 "
@ -453,7 +453,7 @@ static void pc_hmat_off_cfg(const void *data)
g_autofree char *cli = NULL; g_autofree char *cli = NULL;
cli = make_cli(data, "-nodefaults --preconfig " cli = make_cli(data, "-nodefaults --preconfig "
"-smp 2,sockets=2 " "-machine smp.cpus=2,smp.sockets=2 "
"-m 128M,slots=2,maxmem=1G " "-m 128M,slots=2,maxmem=1G "
"-object memory-backend-ram,size=64M,id=m0,prealloc=y " "-object memory-backend-ram,size=64M,id=m0,prealloc=y "
"-object memory-backend-ram,size=64M,id=m1 " "-object memory-backend-ram,size=64M,id=m1 "
@ -492,7 +492,7 @@ static void pc_hmat_erange_cfg(const void *data)
g_autofree char *cli = NULL; g_autofree char *cli = NULL;
cli = make_cli(data, "-nodefaults --preconfig -machine hmat=on " cli = make_cli(data, "-nodefaults --preconfig -machine hmat=on "
"-smp 2,sockets=2 " "-machine smp.cpus=2,smp.sockets=2 "
"-m 128M,slots=2,maxmem=1G " "-m 128M,slots=2,maxmem=1G "
"-object memory-backend-ram,size=64M,id=m0 " "-object memory-backend-ram,size=64M,id=m0 "
"-object memory-backend-ram,size=64M,id=m1 " "-object memory-backend-ram,size=64M,id=m1 "

View File

@ -7,6 +7,11 @@
* See the COPYING file in the top-level directory. * See the COPYING file in the top-level directory.
*/ */
/*
* Not so fast! You might want to read the 9p developer docs first:
* https://wiki.qemu.org/Documentation/9p
*/
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "libqtest-single.h" #include "libqtest-single.h"
#include "qemu/module.h" #include "qemu/module.h"

View File

@ -5,6 +5,3 @@
# On parisc Linux supports 4K/16K/64K (but currently only 4k works) # On parisc Linux supports 4K/16K/64K (but currently only 4k works)
EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-16384 run-test-mmap-65536 EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-16384 run-test-mmap-65536
# There is a race that causes this to fail about 1% of the time
run-signals: signals
$(call skip-test, $<, "BROKEN awaiting vdso support")

View File

@ -32,6 +32,12 @@ threadcount: LDFLAGS+=-lpthread
signals: LDFLAGS+=-lrt -lpthread signals: LDFLAGS+=-lrt -lpthread
# This triggers failures on s390x hosts about 4% of the time
# This triggers failures for hppa-linux about 1% of the time
run-signals: signals
$(call skip-test, $<, "BROKEN awaiting sigframe clean-ups and vdso support")
# We define the runner for test-mmap after the individual # We define the runner for test-mmap after the individual
# architectures have defined their supported pages sizes. If no # architectures have defined their supported pages sizes. If no
# additional page sizes are defined we only run the default test. # additional page sizes are defined we only run the default test.

View File

@ -9,6 +9,3 @@ TESTS+=pack
TESTS+=mvo TESTS+=mvo
TESTS+=mvc TESTS+=mvc
# This triggers failures on s390x hosts about 4% of the time
run-signals: signals
$(call skip-test, $<, "BROKEN awaiting sigframe clean-ups")

View File

@ -747,6 +747,61 @@ static void test_keyval_visit_any(void)
visit_free(v); visit_free(v);
} }
static void test_keyval_merge_dict(void)
{
QDict *first = keyval_parse("opt1=abc,opt2.sub1=def,opt2.sub2=ghi,opt3=xyz",
NULL, NULL, &error_abort);
QDict *second = keyval_parse("opt1=ABC,opt2.sub2=GHI,opt2.sub3=JKL",
NULL, NULL, &error_abort);
QDict *combined = keyval_parse("opt1=ABC,opt2.sub1=def,opt2.sub2=GHI,opt2.sub3=JKL,opt3=xyz",
NULL, NULL, &error_abort);
Error *err = NULL;
keyval_merge(first, second, &err);
g_assert(!err);
g_assert(qobject_is_equal(QOBJECT(combined), QOBJECT(first)));
qobject_unref(first);
qobject_unref(second);
qobject_unref(combined);
}
static void test_keyval_merge_list(void)
{
QDict *first = keyval_parse("opt1.0=abc,opt2.0=xyz",
NULL, NULL, &error_abort);
QDict *second = keyval_parse("opt1.0=def",
NULL, NULL, &error_abort);
QDict *combined = keyval_parse("opt1.0=abc,opt1.1=def,opt2.0=xyz",
NULL, NULL, &error_abort);
Error *err = NULL;
keyval_merge(first, second, &err);
g_assert(!err);
g_assert(qobject_is_equal(QOBJECT(combined), QOBJECT(first)));
qobject_unref(first);
qobject_unref(second);
qobject_unref(combined);
}
static void test_keyval_merge_conflict(void)
{
QDict *first = keyval_parse("opt2=ABC",
NULL, NULL, &error_abort);
QDict *second = keyval_parse("opt2.sub1=def,opt2.sub2=ghi",
NULL, NULL, &error_abort);
QDict *third = qdict_clone_shallow(first);
Error *err = NULL;
keyval_merge(first, second, &err);
error_free_or_abort(&err);
keyval_merge(second, third, &err);
error_free_or_abort(&err);
qobject_unref(first);
qobject_unref(second);
qobject_unref(third);
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
g_test_init(&argc, &argv, NULL); g_test_init(&argc, &argv, NULL);
@ -760,6 +815,9 @@ int main(int argc, char *argv[])
g_test_add_func("/keyval/visit/optional", test_keyval_visit_optional); g_test_add_func("/keyval/visit/optional", test_keyval_visit_optional);
g_test_add_func("/keyval/visit/alternate", test_keyval_visit_alternate); g_test_add_func("/keyval/visit/alternate", test_keyval_visit_alternate);
g_test_add_func("/keyval/visit/any", test_keyval_visit_any); g_test_add_func("/keyval/visit/any", test_keyval_visit_any);
g_test_add_func("/keyval/merge/dict", test_keyval_merge_dict);
g_test_add_func("/keyval/merge/list", test_keyval_merge_list);
g_test_add_func("/keyval/merge/conflict", test_keyval_merge_conflict);
g_test_run(); g_test_run();
return 0; return 0;
} }

View File

@ -410,40 +410,6 @@ static void test_qemu_opts_reset(void)
g_assert(opts == NULL); g_assert(opts == NULL);
} }
static void test_qemu_opts_set(void)
{
QemuOptsList *list;
QemuOpts *opts;
const char *opt;
list = qemu_find_opts("opts_list_04");
g_assert(list != NULL);
g_assert(QTAILQ_EMPTY(&list->head));
g_assert_cmpstr(list->name, ==, "opts_list_04");
/* should not find anything at this point */
opts = qemu_opts_find(list, NULL);
g_assert(opts == NULL);
/* implicitly create opts and set str3 value */
qemu_opts_set(list, "str3", "value", &error_abort);
g_assert(!QTAILQ_EMPTY(&list->head));
/* get the just created opts */
opts = qemu_opts_find(list, NULL);
g_assert(opts != NULL);
/* check the str3 value */
opt = qemu_opt_get(opts, "str3");
g_assert_cmpstr(opt, ==, "value");
qemu_opts_del(opts);
/* should not find anything at this point */
opts = qemu_opts_find(list, NULL);
g_assert(opts == NULL);
}
static int opts_count_iter(void *opaque, const char *name, const char *value, static int opts_count_iter(void *opaque, const char *name, const char *value,
Error **errp) Error **errp)
{ {
@ -1041,7 +1007,6 @@ int main(int argc, char *argv[])
g_test_add_func("/qemu-opts/opt_get_size", test_qemu_opt_get_size); g_test_add_func("/qemu-opts/opt_get_size", test_qemu_opt_get_size);
g_test_add_func("/qemu-opts/opt_unset", test_qemu_opt_unset); g_test_add_func("/qemu-opts/opt_unset", test_qemu_opt_unset);
g_test_add_func("/qemu-opts/opts_reset", test_qemu_opts_reset); g_test_add_func("/qemu-opts/opts_reset", test_qemu_opts_reset);
g_test_add_func("/qemu-opts/opts_set", test_qemu_opts_set);
g_test_add_func("/qemu-opts/opts_parse/general", test_opts_parse); g_test_add_func("/qemu-opts/opts_parse/general", test_opts_parse);
g_test_add_func("/qemu-opts/opts_parse/bool", test_opts_parse_bool); g_test_add_func("/qemu-opts/opts_parse/bool", test_opts_parse_bool);
g_test_add_func("/qemu-opts/opts_parse/number", test_opts_parse_number); g_test_add_func("/qemu-opts/opts_parse/number", test_opts_parse_number);

View File

@ -372,6 +372,11 @@ struct fuse_file_info {
*/ */
#define FUSE_CAP_HANDLE_KILLPRIV_V2 (1 << 28) #define FUSE_CAP_HANDLE_KILLPRIV_V2 (1 << 28)
/**
* Indicates that file server supports extended struct fuse_setxattr_in
*/
#define FUSE_CAP_SETXATTR_EXT (1 << 29)
/** /**
* Ioctl flags * Ioctl flags
* *

View File

@ -1084,6 +1084,12 @@ static void do_open(fuse_req_t req, fuse_ino_t nodeid,
return; return;
} }
/* File creation is handled by do_create() or do_mknod() */
if (arg->flags & (O_CREAT | O_TMPFILE)) {
fuse_reply_err(req, EINVAL);
return;
}
memset(&fi, 0, sizeof(fi)); memset(&fi, 0, sizeof(fi));
fi.flags = arg->flags; fi.flags = arg->flags;
fi.kill_priv = arg->open_flags & FUSE_OPEN_KILL_SUIDGID; fi.kill_priv = arg->open_flags & FUSE_OPEN_KILL_SUIDGID;
@ -1419,8 +1425,13 @@ static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid,
struct fuse_setxattr_in *arg; struct fuse_setxattr_in *arg;
const char *name; const char *name;
const char *value; const char *value;
bool setxattr_ext = req->se->conn.want & FUSE_CAP_SETXATTR_EXT;
arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); if (setxattr_ext) {
arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
} else {
arg = fuse_mbuf_iter_advance(iter, FUSE_COMPAT_SETXATTR_IN_SIZE);
}
name = fuse_mbuf_iter_advance_str(iter); name = fuse_mbuf_iter_advance_str(iter);
if (!arg || !name) { if (!arg || !name) {
fuse_reply_err(req, EINVAL); fuse_reply_err(req, EINVAL);
@ -1434,7 +1445,9 @@ static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid,
} }
if (req->se->op.setxattr) { if (req->se->op.setxattr) {
req->se->op.setxattr(req, nodeid, name, value, arg->size, arg->flags); uint32_t setxattr_flags = setxattr_ext ? arg->setxattr_flags : 0;
req->se->op.setxattr(req, nodeid, name, value, arg->size, arg->flags,
setxattr_flags);
} else { } else {
fuse_reply_err(req, ENOSYS); fuse_reply_err(req, ENOSYS);
} }
@ -1981,6 +1994,9 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid,
if (arg->flags & FUSE_HANDLE_KILLPRIV_V2) { if (arg->flags & FUSE_HANDLE_KILLPRIV_V2) {
se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV_V2; se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV_V2;
} }
if (arg->flags & FUSE_SETXATTR_EXT) {
se->conn.capable |= FUSE_CAP_SETXATTR_EXT;
}
#ifdef HAVE_SPLICE #ifdef HAVE_SPLICE
#ifdef HAVE_VMSPLICE #ifdef HAVE_VMSPLICE
se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE; se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
@ -2116,6 +2132,10 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid,
outarg.flags |= FUSE_HANDLE_KILLPRIV_V2; outarg.flags |= FUSE_HANDLE_KILLPRIV_V2;
} }
if (se->conn.want & FUSE_CAP_SETXATTR_EXT) {
outarg.flags |= FUSE_SETXATTR_EXT;
}
fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor); fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor);
fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags); fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags);
fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n", outarg.max_readahead); fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n", outarg.max_readahead);

View File

@ -798,7 +798,8 @@ struct fuse_lowlevel_ops {
* fuse_reply_err * fuse_reply_err
*/ */
void (*setxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, void (*setxattr)(fuse_req_t req, fuse_ino_t ino, const char *name,
const char *value, size_t size, int flags); const char *value, size_t size, int flags,
uint32_t setxattr_flags);
/** /**
* Get an extended attribute * Get an extended attribute

View File

@ -186,6 +186,7 @@ void fuse_cmdline_help(void)
" to virtiofsd from guest applications.\n" " to virtiofsd from guest applications.\n"
" default: no_allow_direct_io\n" " default: no_allow_direct_io\n"
" -o announce_submounts Announce sub-mount points to the guest\n" " -o announce_submounts Announce sub-mount points to the guest\n"
" -o posix_acl/no_posix_acl Enable/Disable posix_acl. (default: disabled)\n"
); );
} }

View File

@ -122,6 +122,7 @@ struct lo_inode {
struct lo_cred { struct lo_cred {
uid_t euid; uid_t euid;
gid_t egid; gid_t egid;
mode_t umask;
}; };
enum { enum {
@ -172,6 +173,9 @@ struct lo_data {
/* An O_PATH file descriptor to /proc/self/fd/ */ /* An O_PATH file descriptor to /proc/self/fd/ */
int proc_self_fd; int proc_self_fd;
int user_killpriv_v2, killpriv_v2; int user_killpriv_v2, killpriv_v2;
/* If set, virtiofsd is responsible for setting umask during creation */
bool change_umask;
int user_posix_acl, posix_acl;
}; };
static const struct fuse_opt lo_opts[] = { static const struct fuse_opt lo_opts[] = {
@ -204,6 +208,8 @@ static const struct fuse_opt lo_opts[] = {
{ "announce_submounts", offsetof(struct lo_data, announce_submounts), 1 }, { "announce_submounts", offsetof(struct lo_data, announce_submounts), 1 },
{ "killpriv_v2", offsetof(struct lo_data, user_killpriv_v2), 1 }, { "killpriv_v2", offsetof(struct lo_data, user_killpriv_v2), 1 },
{ "no_killpriv_v2", offsetof(struct lo_data, user_killpriv_v2), 0 }, { "no_killpriv_v2", offsetof(struct lo_data, user_killpriv_v2), 0 },
{ "posix_acl", offsetof(struct lo_data, user_posix_acl), 1 },
{ "no_posix_acl", offsetof(struct lo_data, user_posix_acl), 0 },
FUSE_OPT_END FUSE_OPT_END
}; };
static bool use_syslog = false; static bool use_syslog = false;
@ -702,6 +708,32 @@ static void lo_init(void *userdata, struct fuse_conn_info *conn)
conn->want &= ~FUSE_CAP_HANDLE_KILLPRIV_V2; conn->want &= ~FUSE_CAP_HANDLE_KILLPRIV_V2;
lo->killpriv_v2 = 0; lo->killpriv_v2 = 0;
} }
if (lo->user_posix_acl == 1) {
/*
* User explicitly asked for this option. Enable it unconditionally.
* If connection does not have this capability, print error message
* now. It will fail later in fuse_lowlevel.c
*/
if (!(conn->capable & FUSE_CAP_POSIX_ACL) ||
!(conn->capable & FUSE_CAP_DONT_MASK) ||
!(conn->capable & FUSE_CAP_SETXATTR_EXT)) {
fuse_log(FUSE_LOG_ERR, "lo_init: Can not enable posix acl."
" kernel does not support FUSE_POSIX_ACL, FUSE_DONT_MASK"
" or FUSE_SETXATTR_EXT capability.\n");
} else {
fuse_log(FUSE_LOG_DEBUG, "lo_init: enabling posix acl\n");
}
conn->want |= FUSE_CAP_POSIX_ACL | FUSE_CAP_DONT_MASK |
FUSE_CAP_SETXATTR_EXT;
lo->change_umask = true;
lo->posix_acl = true;
} else {
/* User either did not specify anything or wants it disabled */
fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling posix_acl\n");
conn->want &= ~FUSE_CAP_POSIX_ACL;
}
} }
static void lo_getattr(fuse_req_t req, fuse_ino_t ino, static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
@ -1134,7 +1166,8 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
* ownership of caller. * ownership of caller.
* TODO: What about selinux context? * TODO: What about selinux context?
*/ */
static int lo_change_cred(fuse_req_t req, struct lo_cred *old) static int lo_change_cred(fuse_req_t req, struct lo_cred *old,
bool change_umask)
{ {
int res; int res;
@ -1154,11 +1187,14 @@ static int lo_change_cred(fuse_req_t req, struct lo_cred *old)
return errno_save; return errno_save;
} }
if (change_umask) {
old->umask = umask(req->ctx.umask);
}
return 0; return 0;
} }
/* Regain Privileges */ /* Regain Privileges */
static void lo_restore_cred(struct lo_cred *old) static void lo_restore_cred(struct lo_cred *old, bool restore_umask)
{ {
int res; int res;
@ -1173,6 +1209,54 @@ static void lo_restore_cred(struct lo_cred *old)
fuse_log(FUSE_LOG_ERR, "setegid(%u): %m\n", old->egid); fuse_log(FUSE_LOG_ERR, "setegid(%u): %m\n", old->egid);
exit(1); exit(1);
} }
if (restore_umask)
umask(old->umask);
}
/*
* A helper to change cred and drop capability. Returns 0 on success and
* errno on error
*/
static int lo_drop_cap_change_cred(fuse_req_t req, struct lo_cred *old,
bool change_umask, const char *cap_name,
bool *cap_dropped)
{
int ret;
bool __cap_dropped;
assert(cap_name);
ret = drop_effective_cap(cap_name, &__cap_dropped);
if (ret) {
return ret;
}
ret = lo_change_cred(req, old, change_umask);
if (ret) {
if (__cap_dropped) {
if (gain_effective_cap(cap_name)) {
fuse_log(FUSE_LOG_ERR, "Failed to gain CAP_%s\n", cap_name);
}
}
}
if (cap_dropped) {
*cap_dropped = __cap_dropped;
}
return ret;
}
static void lo_restore_cred_gain_cap(struct lo_cred *old, bool restore_umask,
const char *cap_name)
{
assert(cap_name);
lo_restore_cred(old, restore_umask);
if (gain_effective_cap(cap_name)) {
fuse_log(FUSE_LOG_ERR, "Failed to gain CAP_%s\n", cap_name);
}
} }
static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
@ -1202,7 +1286,7 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
return; return;
} }
saverr = lo_change_cred(req, &old); saverr = lo_change_cred(req, &old, lo->change_umask && !S_ISLNK(mode));
if (saverr) { if (saverr) {
goto out; goto out;
} }
@ -1211,7 +1295,7 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
saverr = errno; saverr = errno;
lo_restore_cred(&old); lo_restore_cred(&old, lo->change_umask && !S_ISLNK(mode));
if (res == -1) { if (res == -1) {
goto out; goto out;
@ -1917,7 +2001,7 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
return; return;
} }
err = lo_change_cred(req, &old); err = lo_change_cred(req, &old, lo->change_umask);
if (err) { if (err) {
goto out; goto out;
} }
@ -1928,7 +2012,7 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
fd = openat(parent_inode->fd, name, fi->flags | O_CREAT | O_EXCL, mode); fd = openat(parent_inode->fd, name, fi->flags | O_CREAT | O_EXCL, mode);
err = fd == -1 ? errno : 0; err = fd == -1 ? errno : 0;
lo_restore_cred(&old); lo_restore_cred(&old, lo->change_umask);
/* Ignore the error if file exists and O_EXCL was not given */ /* Ignore the error if file exists and O_EXCL was not given */
if (err && (err != EEXIST || (fi->flags & O_EXCL))) { if (err && (err != EEXIST || (fi->flags & O_EXCL))) {
@ -2727,6 +2811,63 @@ static int xattr_map_server(const struct lo_data *lo, const char *server_name,
assert(fchdir_res == 0); \ assert(fchdir_res == 0); \
} while (0) } while (0)
static bool block_xattr(struct lo_data *lo, const char *name)
{
/*
* If user explicitly enabled posix_acl or did not provide any option,
* do not block acl. Otherwise block system.posix_acl_access and
* system.posix_acl_default xattrs.
*/
if (lo->user_posix_acl) {
return false;
}
if (!strcmp(name, "system.posix_acl_access") ||
!strcmp(name, "system.posix_acl_default"))
return true;
return false;
}
/*
* Returns number of bytes in xattr_list after filtering on success. This
* could be zero as well if nothing is left after filtering.
*
* Returns negative error code on failure.
* xattr_list is modified in place.
*/
static int remove_blocked_xattrs(struct lo_data *lo, char *xattr_list,
unsigned in_size)
{
size_t out_index, in_index;
/*
* As of now we only filter out acl xattrs. If acls are enabled or
* they have not been explicitly disabled, there is nothing to
* filter.
*/
if (lo->user_posix_acl) {
return in_size;
}
out_index = 0;
in_index = 0;
while (in_index < in_size) {
char *in_ptr = xattr_list + in_index;
/* Length of current attribute name */
size_t in_len = strlen(xattr_list + in_index) + 1;
if (!block_xattr(lo, in_ptr)) {
if (in_index != out_index) {
memmove(xattr_list + out_index, xattr_list + in_index, in_len);
}
out_index += in_len;
}
in_index += in_len;
}
return out_index;
}
static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name, static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name,
size_t size) size_t size)
{ {
@ -2740,6 +2881,11 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name,
int saverr; int saverr;
int fd = -1; int fd = -1;
if (block_xattr(lo, in_name)) {
fuse_reply_err(req, EOPNOTSUPP);
return;
}
mapped_name = NULL; mapped_name = NULL;
name = in_name; name = in_name;
if (lo->xattrmap) { if (lo->xattrmap) {
@ -2791,15 +2937,17 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name,
goto out_err; goto out_err;
} }
ret = fgetxattr(fd, name, value, size); ret = fgetxattr(fd, name, value, size);
saverr = ret == -1 ? errno : 0;
} else { } else {
/* fchdir should not fail here */ /* fchdir should not fail here */
FCHDIR_NOFAIL(lo->proc_self_fd); FCHDIR_NOFAIL(lo->proc_self_fd);
ret = getxattr(procname, name, value, size); ret = getxattr(procname, name, value, size);
saverr = ret == -1 ? errno : 0;
FCHDIR_NOFAIL(lo->root.fd); FCHDIR_NOFAIL(lo->root.fd);
} }
if (ret == -1) { if (ret == -1) {
goto out_err; goto out;
} }
if (size) { if (size) {
saverr = 0; saverr = 0;
@ -2864,15 +3012,17 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
goto out_err; goto out_err;
} }
ret = flistxattr(fd, value, size); ret = flistxattr(fd, value, size);
saverr = ret == -1 ? errno : 0;
} else { } else {
/* fchdir should not fail here */ /* fchdir should not fail here */
FCHDIR_NOFAIL(lo->proc_self_fd); FCHDIR_NOFAIL(lo->proc_self_fd);
ret = listxattr(procname, value, size); ret = listxattr(procname, value, size);
saverr = ret == -1 ? errno : 0;
FCHDIR_NOFAIL(lo->root.fd); FCHDIR_NOFAIL(lo->root.fd);
} }
if (ret == -1) { if (ret == -1) {
goto out_err; goto out;
} }
if (size) { if (size) {
saverr = 0; saverr = 0;
@ -2926,6 +3076,12 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
goto out; goto out;
} }
} }
ret = remove_blocked_xattrs(lo, value, ret);
if (ret <= 0) {
saverr = -ret;
goto out;
}
fuse_reply_buf(req, value, ret); fuse_reply_buf(req, value, ret);
} else { } else {
/* /*
@ -2951,7 +3107,8 @@ out:
} }
static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name, static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name,
const char *value, size_t size, int flags) const char *value, size_t size, int flags,
uint32_t extra_flags)
{ {
char procname[64]; char procname[64];
const char *name; const char *name;
@ -2961,6 +3118,14 @@ static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name,
ssize_t ret; ssize_t ret;
int saverr; int saverr;
int fd = -1; int fd = -1;
bool switched_creds = false;
bool cap_fsetid_dropped = false;
struct lo_cred old = {};
if (block_xattr(lo, in_name)) {
fuse_reply_err(req, EOPNOTSUPP);
return;
}
mapped_name = NULL; mapped_name = NULL;
name = in_name; name = in_name;
@ -2991,6 +3156,26 @@ static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name,
", name=%s value=%s size=%zd)\n", ino, name, value, size); ", name=%s value=%s size=%zd)\n", ino, name, value, size);
sprintf(procname, "%i", inode->fd); sprintf(procname, "%i", inode->fd);
/*
* If we are setting posix access acl and if SGID needs to be
* cleared, then switch to caller's gid and drop CAP_FSETID
* and that should make sure host kernel clears SGID.
*
* This probably will not work when we support idmapped mounts.
* In that case we will need to find a non-root gid and switch
* to it. (Instead of gid in request). Fix it when we support
* idmapped mounts.
*/
if (lo->posix_acl && !strcmp(name, "system.posix_acl_access")
&& (extra_flags & FUSE_SETXATTR_ACL_KILL_SGID)) {
ret = lo_drop_cap_change_cred(req, &old, false, "FSETID",
&cap_fsetid_dropped);
if (ret) {
saverr = ret;
goto out;
}
switched_creds = true;
}
if (S_ISREG(inode->filetype) || S_ISDIR(inode->filetype)) { if (S_ISREG(inode->filetype) || S_ISDIR(inode->filetype)) {
fd = openat(lo->proc_self_fd, procname, O_RDONLY); fd = openat(lo->proc_self_fd, procname, O_RDONLY);
if (fd < 0) { if (fd < 0) {
@ -2998,14 +3183,20 @@ static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *in_name,
goto out; goto out;
} }
ret = fsetxattr(fd, name, value, size, flags); ret = fsetxattr(fd, name, value, size, flags);
saverr = ret == -1 ? errno : 0;
} else { } else {
/* fchdir should not fail here */ /* fchdir should not fail here */
FCHDIR_NOFAIL(lo->proc_self_fd); FCHDIR_NOFAIL(lo->proc_self_fd);
ret = setxattr(procname, name, value, size, flags); ret = setxattr(procname, name, value, size, flags);
saverr = ret == -1 ? errno : 0;
FCHDIR_NOFAIL(lo->root.fd); FCHDIR_NOFAIL(lo->root.fd);
} }
if (switched_creds) {
saverr = ret == -1 ? errno : 0; if (cap_fsetid_dropped)
lo_restore_cred_gain_cap(&old, false, "FSETID");
else
lo_restore_cred(&old, false);
}
out: out:
if (fd >= 0) { if (fd >= 0) {
@ -3028,6 +3219,11 @@ static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *in_name)
int saverr; int saverr;
int fd = -1; int fd = -1;
if (block_xattr(lo, in_name)) {
fuse_reply_err(req, EOPNOTSUPP);
return;
}
mapped_name = NULL; mapped_name = NULL;
name = in_name; name = in_name;
if (lo->xattrmap) { if (lo->xattrmap) {
@ -3064,15 +3260,15 @@ static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *in_name)
goto out; goto out;
} }
ret = fremovexattr(fd, name); ret = fremovexattr(fd, name);
saverr = ret == -1 ? errno : 0;
} else { } else {
/* fchdir should not fail here */ /* fchdir should not fail here */
FCHDIR_NOFAIL(lo->proc_self_fd); FCHDIR_NOFAIL(lo->proc_self_fd);
ret = removexattr(procname, name); ret = removexattr(procname, name);
saverr = ret == -1 ? errno : 0;
FCHDIR_NOFAIL(lo->root.fd); FCHDIR_NOFAIL(lo->root.fd);
} }
saverr = ret == -1 ? errno : 0;
out: out:
if (fd >= 0) { if (fd >= 0) {
close(fd); close(fd);
@ -3559,10 +3755,6 @@ static void setup_nofile_rlimit(unsigned long rlimit_nofile)
static void log_func(enum fuse_log_level level, const char *fmt, va_list ap) static void log_func(enum fuse_log_level level, const char *fmt, va_list ap)
{ {
g_autofree char *localfmt = NULL; g_autofree char *localfmt = NULL;
struct timespec ts;
struct tm tm;
char sec_fmt[sizeof "2020-12-07 18:17:54"];
char zone_fmt[sizeof "+0100"];
if (current_log_level < level) { if (current_log_level < level) {
return; return;
@ -3574,23 +3766,10 @@ static void log_func(enum fuse_log_level level, const char *fmt, va_list ap)
localfmt = g_strdup_printf("[ID: %08ld] %s", syscall(__NR_gettid), localfmt = g_strdup_printf("[ID: %08ld] %s", syscall(__NR_gettid),
fmt); fmt);
} else { } else {
/* try formatting a broken-down timestamp */ g_autoptr(GDateTime) now = g_date_time_new_now_utc();
if (clock_gettime(CLOCK_REALTIME, &ts) != -1 && g_autofree char *nowstr = g_date_time_format(now, "%Y-%m-%d %H:%M:%S.%f%z");
localtime_r(&ts.tv_sec, &tm) != NULL && localfmt = g_strdup_printf("[%s] [ID: %08ld] %s",
strftime(sec_fmt, sizeof sec_fmt, "%Y-%m-%d %H:%M:%S", nowstr, syscall(__NR_gettid), fmt);
&tm) != 0 &&
strftime(zone_fmt, sizeof zone_fmt, "%z", &tm) != 0) {
localfmt = g_strdup_printf("[%s.%02ld%s] [ID: %08ld] %s",
sec_fmt,
ts.tv_nsec / (10L * 1000 * 1000),
zone_fmt, syscall(__NR_gettid),
fmt);
} else {
/* fall back to a flat timestamp */
localfmt = g_strdup_printf("[%" PRId64 "] [ID: %08ld] %s",
get_clock(), syscall(__NR_gettid),
fmt);
}
} }
fmt = localfmt; fmt = localfmt;
} }
@ -3722,6 +3901,7 @@ int main(int argc, char *argv[])
.allow_direct_io = 0, .allow_direct_io = 0,
.proc_self_fd = -1, .proc_self_fd = -1,
.user_killpriv_v2 = -1, .user_killpriv_v2 = -1,
.user_posix_acl = -1,
}; };
struct lo_map_elem *root_elem; struct lo_map_elem *root_elem;
struct lo_map_elem *reserve_elem; struct lo_map_elem *reserve_elem;
@ -3850,6 +4030,12 @@ int main(int argc, char *argv[])
exit(1); exit(1);
} }
if (lo.user_posix_acl == 1 && !lo.xattr) {
fuse_log(FUSE_LOG_ERR, "Can't enable posix ACLs. xattrs are disabled."
"\n");
exit(1);
}
lo.use_statx = true; lo.use_statx = true;
se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo); se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo);

View File

@ -114,6 +114,7 @@ static const int syscall_allowlist[] = {
SCMP_SYS(utimensat), SCMP_SYS(utimensat),
SCMP_SYS(write), SCMP_SYS(write),
SCMP_SYS(writev), SCMP_SYS(writev),
SCMP_SYS(umask),
}; };
/* Syscalls used when --syslog is enabled */ /* Syscalls used when --syslog is enabled */

View File

@ -310,6 +310,86 @@ static char *reassemble_key(GSList *key)
return g_string_free(s, FALSE); return g_string_free(s, FALSE);
} }
/*
* Recursive worker for keyval_merge.
*
* @str is the path that led to the * current dictionary (to be used for
* error messages). It is modified internally but restored before the
* function returns.
*/
static void keyval_do_merge(QDict *dest, const QDict *merged, GString *str, Error **errp)
{
size_t save_len = str->len;
const QDictEntry *ent;
QObject *old_value;
for (ent = qdict_first(merged); ent; ent = qdict_next(merged, ent)) {
old_value = qdict_get(dest, ent->key);
if (old_value) {
if (qobject_type(old_value) != qobject_type(ent->value)) {
error_setg(errp, "Parameter '%s%s' used inconsistently",
str->str, ent->key);
return;
} else if (qobject_type(ent->value) == QTYPE_QDICT) {
/* Merge sub-dictionaries. */
g_string_append(str, ent->key);
g_string_append_c(str, '.');
keyval_do_merge(qobject_to(QDict, old_value),
qobject_to(QDict, ent->value),
str, errp);
g_string_truncate(str, save_len);
continue;
} else if (qobject_type(ent->value) == QTYPE_QLIST) {
/* Append to old list. */
QList *old = qobject_to(QList, old_value);
QList *new = qobject_to(QList, ent->value);
const QListEntry *item;
QLIST_FOREACH_ENTRY(new, item) {
qobject_ref(item->value);
qlist_append_obj(old, item->value);
}
continue;
} else {
assert(qobject_type(ent->value) == QTYPE_QSTRING);
}
}
qobject_ref(ent->value);
qdict_put_obj(dest, ent->key, ent->value);
}
}
/* Merge the @merged dictionary into @dest.
*
* The dictionaries are expected to be returned by the keyval parser, and
* therefore the only expected scalar type is the string. In case the same
* path is present in both @dest and @merged, the semantics are as follows:
*
* - lists are concatenated
*
* - dictionaries are merged recursively
*
* - for scalar values, @merged wins
*
* In case an error is reported, @dest may already have been modified.
*
* This function can be used to implement semantics analogous to QemuOpts's
* .merge_lists = true case, or to implement -set for options backed by QDicts.
*
* Note: while QemuOpts is commonly used so that repeated keys overwrite
* ("last one wins"), it can also be used so that repeated keys build up
* a list. keyval_merge() can only be used when the options' semantics are
* the former, not the latter.
*/
void keyval_merge(QDict *dest, const QDict *merged, Error **errp)
{
GString *str;
str = g_string_new("");
keyval_do_merge(dest, merged, str, errp);
g_string_free(str, TRUE);
}
/* /*
* Listify @cur recursively. * Listify @cur recursively.
* Replace QDicts whose keys are all valid list indexes by QLists. * Replace QDicts whose keys are all valid list indexes by QLists.
@ -431,13 +511,14 @@ static QObject *keyval_listify(QDict *cur, GSList *key_of_cur, Error **errp)
* If @p_help is not NULL, store whether help is requested there. * If @p_help is not NULL, store whether help is requested there.
* If @p_help is NULL and help is requested, fail. * If @p_help is NULL and help is requested, fail.
* *
* On success, return a dictionary of the parsed keys and values. * On success, return @dict, now filled with the parsed keys and values.
* On failure, store an error through @errp and return NULL. *
* On failure, store an error through @errp and return NULL. Any keys
* and values parsed so far will be in @dict nevertheless.
*/ */
QDict *keyval_parse(const char *params, const char *implied_key, QDict *keyval_parse_into(QDict *qdict, const char *params, const char *implied_key,
bool *p_help, Error **errp) bool *p_help, Error **errp)
{ {
QDict *qdict = qdict_new();
QObject *listified; QObject *listified;
const char *s; const char *s;
bool help = false; bool help = false;
@ -446,7 +527,6 @@ QDict *keyval_parse(const char *params, const char *implied_key,
while (*s) { while (*s) {
s = keyval_parse_one(qdict, s, implied_key, &help, errp); s = keyval_parse_one(qdict, s, implied_key, &help, errp);
if (!s) { if (!s) {
qobject_unref(qdict);
return NULL; return NULL;
} }
implied_key = NULL; implied_key = NULL;
@ -456,15 +536,42 @@ QDict *keyval_parse(const char *params, const char *implied_key,
*p_help = help; *p_help = help;
} else if (help) { } else if (help) {
error_setg(errp, "Help is not available for this option"); error_setg(errp, "Help is not available for this option");
qobject_unref(qdict);
return NULL; return NULL;
} }
listified = keyval_listify(qdict, NULL, errp); listified = keyval_listify(qdict, NULL, errp);
if (!listified) { if (!listified) {
qobject_unref(qdict);
return NULL; return NULL;
} }
assert(listified == QOBJECT(qdict)); assert(listified == QOBJECT(qdict));
return qdict; return qdict;
} }
/*
* Parse @params in QEMU's traditional KEY=VALUE,... syntax.
*
* If @implied_key, the first KEY= can be omitted. @implied_key is
* implied then, and VALUE can't be empty or contain ',' or '='.
*
* A parameter "help" or "?" without a value isn't added to the
* resulting dictionary, but instead is interpreted as help request.
* All other options are parsed and returned normally so that context
* specific help can be printed.
*
* If @p_help is not NULL, store whether help is requested there.
* If @p_help is NULL and help is requested, fail.
*
* On success, return a dictionary of the parsed keys and values.
* On failure, store an error through @errp and return NULL.
*/
QDict *keyval_parse(const char *params, const char *implied_key,
bool *p_help, Error **errp)
{
QDict *qdict = qdict_new();
QDict *ret = keyval_parse_into(qdict, params, implied_key, p_help, errp);
if (!ret) {
qobject_unref(qdict);
}
return ret;
}

View File

@ -1,9 +1,10 @@
util_ss.add(dependency('threads'))
util_ss.add(files('osdep.c', 'cutils.c', 'unicode.c', 'qemu-timer-common.c')) util_ss.add(files('osdep.c', 'cutils.c', 'unicode.c', 'qemu-timer-common.c'))
util_ss.add(when: 'CONFIG_ATOMIC64', if_false: files('atomic64.c')) util_ss.add(when: 'CONFIG_ATOMIC64', if_false: files('atomic64.c'))
util_ss.add(when: 'CONFIG_POSIX', if_true: files('aio-posix.c')) util_ss.add(when: 'CONFIG_POSIX', if_true: files('aio-posix.c'))
util_ss.add(when: 'CONFIG_POSIX', if_true: files('fdmon-poll.c')) util_ss.add(when: 'CONFIG_POSIX', if_true: files('fdmon-poll.c'))
util_ss.add(when: 'CONFIG_EPOLL_CREATE1', if_true: files('fdmon-epoll.c')) if config_host_data.get('CONFIG_EPOLL_CREATE1')
util_ss.add(files('fdmon-epoll.c'))
endif
util_ss.add(when: ['CONFIG_LINUX_IO_URING', linux_io_uring], if_true: files('fdmon-io_uring.c')) util_ss.add(when: ['CONFIG_LINUX_IO_URING', linux_io_uring], if_true: files('fdmon-io_uring.c'))
util_ss.add(when: 'CONFIG_POSIX', if_true: files('compatfd.c')) util_ss.add(when: 'CONFIG_POSIX', if_true: files('compatfd.c'))
util_ss.add(when: 'CONFIG_POSIX', if_true: files('event_notifier-posix.c')) util_ss.add(when: 'CONFIG_POSIX', if_true: files('event_notifier-posix.c'))

View File

@ -479,19 +479,14 @@ int qemu_opt_unset(QemuOpts *opts, const char *name)
} }
} }
static QemuOpt *opt_create(QemuOpts *opts, const char *name, char *value, static QemuOpt *opt_create(QemuOpts *opts, const char *name, char *value)
bool prepend)
{ {
QemuOpt *opt = g_malloc0(sizeof(*opt)); QemuOpt *opt = g_malloc0(sizeof(*opt));
opt->name = g_strdup(name); opt->name = g_strdup(name);
opt->str = value; opt->str = value;
opt->opts = opts; opt->opts = opts;
if (prepend) { QTAILQ_INSERT_TAIL(&opts->head, opt, next);
QTAILQ_INSERT_HEAD(&opts->head, opt, next);
} else {
QTAILQ_INSERT_TAIL(&opts->head, opt, next);
}
return opt; return opt;
} }
@ -518,7 +513,7 @@ static bool opt_validate(QemuOpt *opt, Error **errp)
bool qemu_opt_set(QemuOpts *opts, const char *name, const char *value, bool qemu_opt_set(QemuOpts *opts, const char *name, const char *value,
Error **errp) Error **errp)
{ {
QemuOpt *opt = opt_create(opts, name, g_strdup(value), false); QemuOpt *opt = opt_create(opts, name, g_strdup(value));
if (!opt_validate(opt, errp)) { if (!opt_validate(opt, errp)) {
qemu_opt_del(opt); qemu_opt_del(opt);
@ -662,15 +657,6 @@ void qemu_opts_loc_restore(QemuOpts *opts)
loc_restore(&opts->loc); loc_restore(&opts->loc);
} }
bool qemu_opts_set(QemuOptsList *list, const char *name, const char *value, Error **errp)
{
QemuOpts *opts;
assert(list->merge_lists);
opts = qemu_opts_create(list, NULL, 0, &error_abort);
return qemu_opt_set(opts, name, value, errp);
}
const char *qemu_opts_id(QemuOpts *opts) const char *qemu_opts_id(QemuOpts *opts)
{ {
return opts->id; return opts->id;
@ -811,7 +797,7 @@ static const char *get_opt_name_value(const char *params,
} }
static bool opts_do_parse(QemuOpts *opts, const char *params, static bool opts_do_parse(QemuOpts *opts, const char *params,
const char *firstname, bool prepend, const char *firstname,
bool warn_on_flag, bool *help_wanted, Error **errp) bool warn_on_flag, bool *help_wanted, Error **errp)
{ {
char *option, *value; char *option, *value;
@ -833,7 +819,7 @@ static bool opts_do_parse(QemuOpts *opts, const char *params,
continue; continue;
} }
opt = opt_create(opts, option, value, prepend); opt = opt_create(opts, option, value);
g_free(option); g_free(option);
if (!opt_validate(opt, errp)) { if (!opt_validate(opt, errp)) {
qemu_opt_del(opt); qemu_opt_del(opt);
@ -889,11 +875,11 @@ bool has_help_option(const char *params)
bool qemu_opts_do_parse(QemuOpts *opts, const char *params, bool qemu_opts_do_parse(QemuOpts *opts, const char *params,
const char *firstname, Error **errp) const char *firstname, Error **errp)
{ {
return opts_do_parse(opts, params, firstname, false, false, NULL, errp); return opts_do_parse(opts, params, firstname, false, NULL, errp);
} }
static QemuOpts *opts_parse(QemuOptsList *list, const char *params, static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
bool permit_abbrev, bool defaults, bool permit_abbrev,
bool warn_on_flag, bool *help_wanted, Error **errp) bool warn_on_flag, bool *help_wanted, Error **errp)
{ {
const char *firstname; const char *firstname;
@ -903,21 +889,13 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
assert(!permit_abbrev || list->implied_opt_name); assert(!permit_abbrev || list->implied_opt_name);
firstname = permit_abbrev ? list->implied_opt_name : NULL; firstname = permit_abbrev ? list->implied_opt_name : NULL;
/*
* This code doesn't work for defaults && !list->merge_lists: when
* params has no id=, and list has an element with !opts->id, it
* appends a new element instead of returning the existing opts.
* However, we got no use for this case. Guard against possible
* (if unlikely) future misuse:
*/
assert(!defaults || list->merge_lists);
opts = qemu_opts_create(list, id, !list->merge_lists, errp); opts = qemu_opts_create(list, id, !list->merge_lists, errp);
g_free(id); g_free(id);
if (opts == NULL) { if (opts == NULL) {
return NULL; return NULL;
} }
if (!opts_do_parse(opts, params, firstname, defaults, if (!opts_do_parse(opts, params, firstname,
warn_on_flag, help_wanted, errp)) { warn_on_flag, help_wanted, errp)) {
qemu_opts_del(opts); qemu_opts_del(opts);
return NULL; return NULL;
@ -936,7 +914,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params, QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params,
bool permit_abbrev, Error **errp) bool permit_abbrev, Error **errp)
{ {
return opts_parse(list, params, permit_abbrev, false, false, NULL, errp); return opts_parse(list, params, permit_abbrev, false, NULL, errp);
} }
/** /**
@ -954,7 +932,7 @@ QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params,
QemuOpts *opts; QemuOpts *opts;
bool help_wanted = false; bool help_wanted = false;
opts = opts_parse(list, params, permit_abbrev, false, true, opts = opts_parse(list, params, permit_abbrev, true,
opts_accepts_any(list) ? NULL : &help_wanted, opts_accepts_any(list) ? NULL : &help_wanted,
&err); &err);
if (!opts) { if (!opts) {
@ -968,15 +946,6 @@ QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params,
return opts; return opts;
} }
void qemu_opts_set_defaults(QemuOptsList *list, const char *params,
int permit_abbrev)
{
QemuOpts *opts;
opts = opts_parse(list, params, permit_abbrev, true, false, NULL, NULL);
assert(opts);
}
static bool qemu_opts_from_qdict_entry(QemuOpts *opts, static bool qemu_opts_from_qdict_entry(QemuOpts *opts,
const QDictEntry *entry, const QDictEntry *entry,
Error **errp) Error **errp)