M: Alistair Francis <alistair.francis@wdc.com>
R: David Gibson <david@gibson.dropbear.id.au>
S: Maintained
-F: device_tree.c
+F: softmmu/device_tree.c
F: include/sysemu/device_tree.h
Dump
F: include/exec/memory.h
F: include/exec/ram_addr.h
F: include/exec/ramblock.h
+F: softmmu/dma-helpers.c
F: softmmu/ioport.c
F: softmmu/memory.c
F: include/exec/memory-internal.h
F: include/qom/
F: qapi/qom.json
F: qapi/qdev.json
-F: qdev-monitor.c
+F: softmmu/qdev-monitor.c
F: qom/
F: tests/check-qom-interface.c
F: tests/check-qom-proplist.c
Seccomp
M: Eduardo Otubo <otubo@redhat.com>
S: Supported
-F: qemu-seccomp.c
+F: softmmu/qemu-seccomp.c
F: include/sysemu/seccomp.h
Cryptography
Bootdevice
M: Gonglei <arei.gonglei@huawei.com>
S: Maintained
-F: bootdevice.c
+F: softmmu/bootdevice.c
Quorum
M: Alberto Garcia <berto@igalia.com>
+++ /dev/null
-/*
- * QEMU Boot Device Implement
- *
- * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO., LTD.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "qemu/osdep.h"
-#include "qapi/error.h"
-#include "sysemu/sysemu.h"
-#include "qapi/visitor.h"
-#include "qemu/error-report.h"
-#include "sysemu/reset.h"
-#include "hw/qdev-core.h"
-#include "hw/boards.h"
-
-typedef struct FWBootEntry FWBootEntry;
-
-struct FWBootEntry {
- QTAILQ_ENTRY(FWBootEntry) link;
- int32_t bootindex;
- DeviceState *dev;
- char *suffix;
-};
-
-static QTAILQ_HEAD(, FWBootEntry) fw_boot_order =
- QTAILQ_HEAD_INITIALIZER(fw_boot_order);
-static QEMUBootSetHandler *boot_set_handler;
-static void *boot_set_opaque;
-
-void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque)
-{
- boot_set_handler = func;
- boot_set_opaque = opaque;
-}
-
-void qemu_boot_set(const char *boot_order, Error **errp)
-{
- Error *local_err = NULL;
-
- if (!boot_set_handler) {
- error_setg(errp, "no function defined to set boot device list for"
- " this architecture");
- return;
- }
-
- validate_bootdevices(boot_order, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
-
- boot_set_handler(boot_set_opaque, boot_order, errp);
-}
-
-void validate_bootdevices(const char *devices, Error **errp)
-{
- /* We just do some generic consistency checks */
- const char *p;
- int bitmap = 0;
-
- for (p = devices; *p != '\0'; p++) {
- /* Allowed boot devices are:
- * a-b: floppy disk drives
- * c-f: IDE disk drives
- * g-m: machine implementation dependent drives
- * n-p: network devices
- * It's up to each machine implementation to check if the given boot
- * devices match the actual hardware implementation and firmware
- * features.
- */
- if (*p < 'a' || *p > 'p') {
- error_setg(errp, "Invalid boot device '%c'", *p);
- return;
- }
- if (bitmap & (1 << (*p - 'a'))) {
- error_setg(errp, "Boot device '%c' was given twice", *p);
- return;
- }
- bitmap |= 1 << (*p - 'a');
- }
-}
-
-void restore_boot_order(void *opaque)
-{
- char *normal_boot_order = opaque;
- static int first = 1;
-
- /* Restore boot order and remove ourselves after the first boot */
- if (first) {
- first = 0;
- return;
- }
-
- if (boot_set_handler) {
- qemu_boot_set(normal_boot_order, &error_abort);
- }
-
- qemu_unregister_reset(restore_boot_order, normal_boot_order);
- g_free(normal_boot_order);
-}
-
-void check_boot_index(int32_t bootindex, Error **errp)
-{
- FWBootEntry *i;
-
- if (bootindex >= 0) {
- QTAILQ_FOREACH(i, &fw_boot_order, link) {
- if (i->bootindex == bootindex) {
- error_setg(errp, "The bootindex %d has already been used",
- bootindex);
- return;
- }
- }
- }
-}
-
-void del_boot_device_path(DeviceState *dev, const char *suffix)
-{
- FWBootEntry *i;
-
- if (dev == NULL) {
- return;
- }
-
- QTAILQ_FOREACH(i, &fw_boot_order, link) {
- if ((!suffix || !g_strcmp0(i->suffix, suffix)) &&
- i->dev == dev) {
- QTAILQ_REMOVE(&fw_boot_order, i, link);
- g_free(i->suffix);
- g_free(i);
-
- break;
- }
- }
-}
-
-void add_boot_device_path(int32_t bootindex, DeviceState *dev,
- const char *suffix)
-{
- FWBootEntry *node, *i;
-
- if (bootindex < 0) {
- del_boot_device_path(dev, suffix);
- return;
- }
-
- assert(dev != NULL || suffix != NULL);
-
- del_boot_device_path(dev, suffix);
-
- node = g_malloc0(sizeof(FWBootEntry));
- node->bootindex = bootindex;
- node->suffix = g_strdup(suffix);
- node->dev = dev;
-
- QTAILQ_FOREACH(i, &fw_boot_order, link) {
- if (i->bootindex == bootindex) {
- error_report("Two devices with same boot index %d", bootindex);
- exit(1);
- } else if (i->bootindex < bootindex) {
- continue;
- }
- QTAILQ_INSERT_BEFORE(i, node, link);
- return;
- }
- QTAILQ_INSERT_TAIL(&fw_boot_order, node, link);
-}
-
-DeviceState *get_boot_device(uint32_t position)
-{
- uint32_t counter = 0;
- FWBootEntry *i = NULL;
- DeviceState *res = NULL;
-
- if (!QTAILQ_EMPTY(&fw_boot_order)) {
- QTAILQ_FOREACH(i, &fw_boot_order, link) {
- if (counter == position) {
- res = i->dev;
- break;
- }
- counter++;
- }
- }
- return res;
-}
-
-static char *get_boot_device_path(DeviceState *dev, bool ignore_suffixes,
- const char *suffix)
-{
- char *devpath = NULL, *s = NULL, *d, *bootpath;
-
- if (dev) {
- devpath = qdev_get_fw_dev_path(dev);
- assert(devpath);
- }
-
- if (!ignore_suffixes) {
- if (dev) {
- d = qdev_get_own_fw_dev_path_from_handler(dev->parent_bus, dev);
- if (d) {
- assert(!suffix);
- s = d;
- } else {
- s = g_strdup(suffix);
- }
- } else {
- s = g_strdup(suffix);
- }
- }
-
- bootpath = g_strdup_printf("%s%s",
- devpath ? devpath : "",
- s ? s : "");
- g_free(devpath);
- g_free(s);
-
- return bootpath;
-}
-
-/*
- * This function returns null terminated string that consist of new line
- * separated device paths.
- *
- * memory pointed by "size" is assigned total length of the array in bytes
- *
- */
-char *get_boot_devices_list(size_t *size)
-{
- FWBootEntry *i;
- size_t total = 0;
- char *list = NULL;
- MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
- bool ignore_suffixes = mc->ignore_boot_device_suffixes;
-
- QTAILQ_FOREACH(i, &fw_boot_order, link) {
- char *bootpath;
- size_t len;
-
- bootpath = get_boot_device_path(i->dev, ignore_suffixes, i->suffix);
-
- if (total) {
- list[total-1] = '\n';
- }
- len = strlen(bootpath) + 1;
- list = g_realloc(list, total + len);
- memcpy(&list[total], bootpath, len);
- total += len;
- g_free(bootpath);
- }
-
- *size = total;
-
- if (boot_strict && *size > 0) {
- list[total-1] = '\n';
- list = g_realloc(list, total + 5);
- memcpy(&list[total], "HALT", 5);
- *size = total + 5;
- }
- return list;
-}
-
-typedef struct {
- int32_t *bootindex;
- const char *suffix;
- DeviceState *dev;
-} BootIndexProperty;
-
-static void device_get_bootindex(Object *obj, Visitor *v, const char *name,
- void *opaque, Error **errp)
-{
- BootIndexProperty *prop = opaque;
- visit_type_int32(v, name, prop->bootindex, errp);
-}
-
-static void device_set_bootindex(Object *obj, Visitor *v, const char *name,
- void *opaque, Error **errp)
-{
- BootIndexProperty *prop = opaque;
- int32_t boot_index;
- Error *local_err = NULL;
-
- if (!visit_type_int32(v, name, &boot_index, errp)) {
- return;
- }
- /* check whether bootindex is present in fw_boot_order list */
- check_boot_index(boot_index, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
- }
- /* change bootindex to a new one */
- *prop->bootindex = boot_index;
-
- add_boot_device_path(*prop->bootindex, prop->dev, prop->suffix);
-}
-
-static void property_release_bootindex(Object *obj, const char *name,
- void *opaque)
-
-{
- BootIndexProperty *prop = opaque;
-
- del_boot_device_path(prop->dev, prop->suffix);
- g_free(prop);
-}
-
-void device_add_bootindex_property(Object *obj, int32_t *bootindex,
- const char *name, const char *suffix,
- DeviceState *dev)
-{
- BootIndexProperty *prop = g_malloc0(sizeof(*prop));
-
- prop->bootindex = bootindex;
- prop->suffix = suffix;
- prop->dev = dev;
-
- object_property_add(obj, name, "int32",
- device_get_bootindex,
- device_set_bootindex,
- property_release_bootindex,
- prop);
-
- /* initialize devices' bootindex property to -1 */
- object_property_set_int(obj, name, -1, NULL);
-}
-
-typedef struct FWLCHSEntry FWLCHSEntry;
-
-struct FWLCHSEntry {
- QTAILQ_ENTRY(FWLCHSEntry) link;
- DeviceState *dev;
- char *suffix;
- uint32_t lcyls;
- uint32_t lheads;
- uint32_t lsecs;
-};
-
-static QTAILQ_HEAD(, FWLCHSEntry) fw_lchs =
- QTAILQ_HEAD_INITIALIZER(fw_lchs);
-
-void add_boot_device_lchs(DeviceState *dev, const char *suffix,
- uint32_t lcyls, uint32_t lheads, uint32_t lsecs)
-{
- FWLCHSEntry *node;
-
- if (!lcyls && !lheads && !lsecs) {
- return;
- }
-
- assert(dev != NULL || suffix != NULL);
-
- node = g_malloc0(sizeof(FWLCHSEntry));
- node->suffix = g_strdup(suffix);
- node->dev = dev;
- node->lcyls = lcyls;
- node->lheads = lheads;
- node->lsecs = lsecs;
-
- QTAILQ_INSERT_TAIL(&fw_lchs, node, link);
-}
-
-void del_boot_device_lchs(DeviceState *dev, const char *suffix)
-{
- FWLCHSEntry *i;
-
- if (dev == NULL) {
- return;
- }
-
- QTAILQ_FOREACH(i, &fw_lchs, link) {
- if ((!suffix || !g_strcmp0(i->suffix, suffix)) &&
- i->dev == dev) {
- QTAILQ_REMOVE(&fw_lchs, i, link);
- g_free(i->suffix);
- g_free(i);
-
- break;
- }
- }
-}
-
-char *get_boot_devices_lchs_list(size_t *size)
-{
- FWLCHSEntry *i;
- size_t total = 0;
- char *list = NULL;
-
- QTAILQ_FOREACH(i, &fw_lchs, link) {
- char *bootpath;
- char *chs_string;
- size_t len;
-
- bootpath = get_boot_device_path(i->dev, false, i->suffix);
- chs_string = g_strdup_printf("%s %" PRIu32 " %" PRIu32 " %" PRIu32,
- bootpath, i->lcyls, i->lheads, i->lsecs);
-
- if (total) {
- list[total - 1] = '\n';
- }
- len = strlen(chs_string) + 1;
- list = g_realloc(list, total + len);
- memcpy(&list[total], chs_string, len);
- total += len;
- g_free(chs_string);
- g_free(bootpath);
- }
-
- *size = total;
-
- return list;
-}
+++ /dev/null
-/*
- * Functions to help device tree manipulation using libfdt.
- * It also provides functions to read entries from device tree proc
- * interface.
- *
- * Copyright 2008 IBM Corporation.
- * Authors: Jerone Young <jyoung5@us.ibm.com>
- * Hollis Blanchard <hollisb@us.ibm.com>
- *
- * This work is licensed under the GNU GPL license version 2 or later.
- *
- */
-
-#include "qemu/osdep.h"
-
-#ifdef CONFIG_LINUX
-#include <dirent.h>
-#endif
-
-#include "qapi/error.h"
-#include "qemu/error-report.h"
-#include "qemu/option.h"
-#include "qemu/bswap.h"
-#include "sysemu/device_tree.h"
-#include "sysemu/sysemu.h"
-#include "hw/loader.h"
-#include "hw/boards.h"
-#include "qemu/config-file.h"
-
-#include <libfdt.h>
-
-#define FDT_MAX_SIZE 0x100000
-
-void *create_device_tree(int *sizep)
-{
- void *fdt;
- int ret;
-
- *sizep = FDT_MAX_SIZE;
- fdt = g_malloc0(FDT_MAX_SIZE);
- ret = fdt_create(fdt, FDT_MAX_SIZE);
- if (ret < 0) {
- goto fail;
- }
- ret = fdt_finish_reservemap(fdt);
- if (ret < 0) {
- goto fail;
- }
- ret = fdt_begin_node(fdt, "");
- if (ret < 0) {
- goto fail;
- }
- ret = fdt_end_node(fdt);
- if (ret < 0) {
- goto fail;
- }
- ret = fdt_finish(fdt);
- if (ret < 0) {
- goto fail;
- }
- ret = fdt_open_into(fdt, fdt, *sizep);
- if (ret) {
- error_report("Unable to copy device tree in memory");
- exit(1);
- }
-
- return fdt;
-fail:
- error_report("%s Couldn't create dt: %s", __func__, fdt_strerror(ret));
- exit(1);
-}
-
-void *load_device_tree(const char *filename_path, int *sizep)
-{
- int dt_size;
- int dt_file_load_size;
- int ret;
- void *fdt = NULL;
-
- *sizep = 0;
- dt_size = get_image_size(filename_path);
- if (dt_size < 0) {
- error_report("Unable to get size of device tree file '%s'",
- filename_path);
- goto fail;
- }
- if (dt_size > INT_MAX / 2 - 10000) {
- error_report("Device tree file '%s' is too large", filename_path);
- goto fail;
- }
-
- /* Expand to 2x size to give enough room for manipulation. */
- dt_size += 10000;
- dt_size *= 2;
- /* First allocate space in qemu for device tree */
- fdt = g_malloc0(dt_size);
-
- dt_file_load_size = load_image_size(filename_path, fdt, dt_size);
- if (dt_file_load_size < 0) {
- error_report("Unable to open device tree file '%s'",
- filename_path);
- goto fail;
- }
-
- ret = fdt_open_into(fdt, fdt, dt_size);
- if (ret) {
- error_report("Unable to copy device tree in memory");
- goto fail;
- }
-
- /* Check sanity of device tree */
- if (fdt_check_header(fdt)) {
- error_report("Device tree file loaded into memory is invalid: %s",
- filename_path);
- goto fail;
- }
- *sizep = dt_size;
- return fdt;
-
-fail:
- g_free(fdt);
- return NULL;
-}
-
-#ifdef CONFIG_LINUX
-
-#define SYSFS_DT_BASEDIR "/proc/device-tree"
-
-/**
- * read_fstree: this function is inspired from dtc read_fstree
- * @fdt: preallocated fdt blob buffer, to be populated
- * @dirname: directory to scan under SYSFS_DT_BASEDIR
- * the search is recursive and the tree is searched down to the
- * leaves (property files).
- *
- * the function asserts in case of error
- */
-static void read_fstree(void *fdt, const char *dirname)
-{
- DIR *d;
- struct dirent *de;
- struct stat st;
- const char *root_dir = SYSFS_DT_BASEDIR;
- const char *parent_node;
-
- if (strstr(dirname, root_dir) != dirname) {
- error_report("%s: %s must be searched within %s",
- __func__, dirname, root_dir);
- exit(1);
- }
- parent_node = &dirname[strlen(SYSFS_DT_BASEDIR)];
-
- d = opendir(dirname);
- if (!d) {
- error_report("%s cannot open %s", __func__, dirname);
- exit(1);
- }
-
- while ((de = readdir(d)) != NULL) {
- char *tmpnam;
-
- if (!g_strcmp0(de->d_name, ".")
- || !g_strcmp0(de->d_name, "..")) {
- continue;
- }
-
- tmpnam = g_strdup_printf("%s/%s", dirname, de->d_name);
-
- if (lstat(tmpnam, &st) < 0) {
- error_report("%s cannot lstat %s", __func__, tmpnam);
- exit(1);
- }
-
- if (S_ISREG(st.st_mode)) {
- gchar *val;
- gsize len;
-
- if (!g_file_get_contents(tmpnam, &val, &len, NULL)) {
- error_report("%s not able to extract info from %s",
- __func__, tmpnam);
- exit(1);
- }
-
- if (strlen(parent_node) > 0) {
- qemu_fdt_setprop(fdt, parent_node,
- de->d_name, val, len);
- } else {
- qemu_fdt_setprop(fdt, "/", de->d_name, val, len);
- }
- g_free(val);
- } else if (S_ISDIR(st.st_mode)) {
- char *node_name;
-
- node_name = g_strdup_printf("%s/%s",
- parent_node, de->d_name);
- qemu_fdt_add_subnode(fdt, node_name);
- g_free(node_name);
- read_fstree(fdt, tmpnam);
- }
-
- g_free(tmpnam);
- }
-
- closedir(d);
-}
-
-/* load_device_tree_from_sysfs: extract the dt blob from host sysfs */
-void *load_device_tree_from_sysfs(void)
-{
- void *host_fdt;
- int host_fdt_size;
-
- host_fdt = create_device_tree(&host_fdt_size);
- read_fstree(host_fdt, SYSFS_DT_BASEDIR);
- if (fdt_check_header(host_fdt)) {
- error_report("%s host device tree extracted into memory is invalid",
- __func__);
- exit(1);
- }
- return host_fdt;
-}
-
-#endif /* CONFIG_LINUX */
-
-static int findnode_nofail(void *fdt, const char *node_path)
-{
- int offset;
-
- offset = fdt_path_offset(fdt, node_path);
- if (offset < 0) {
- error_report("%s Couldn't find node %s: %s", __func__, node_path,
- fdt_strerror(offset));
- exit(1);
- }
-
- return offset;
-}
-
-char **qemu_fdt_node_unit_path(void *fdt, const char *name, Error **errp)
-{
- char *prefix = g_strdup_printf("%s@", name);
- unsigned int path_len = 16, n = 0;
- GSList *path_list = NULL, *iter;
- const char *iter_name;
- int offset, len, ret;
- char **path_array;
-
- offset = fdt_next_node(fdt, -1, NULL);
-
- while (offset >= 0) {
- iter_name = fdt_get_name(fdt, offset, &len);
- if (!iter_name) {
- offset = len;
- break;
- }
- if (!strcmp(iter_name, name) || g_str_has_prefix(iter_name, prefix)) {
- char *path;
-
- path = g_malloc(path_len);
- while ((ret = fdt_get_path(fdt, offset, path, path_len))
- == -FDT_ERR_NOSPACE) {
- path_len += 16;
- path = g_realloc(path, path_len);
- }
- path_list = g_slist_prepend(path_list, path);
- n++;
- }
- offset = fdt_next_node(fdt, offset, NULL);
- }
- g_free(prefix);
-
- if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {
- error_setg(errp, "%s: abort parsing dt for %s node units: %s",
- __func__, name, fdt_strerror(offset));
- for (iter = path_list; iter; iter = iter->next) {
- g_free(iter->data);
- }
- g_slist_free(path_list);
- return NULL;
- }
-
- path_array = g_new(char *, n + 1);
- path_array[n--] = NULL;
-
- for (iter = path_list; iter; iter = iter->next) {
- path_array[n--] = iter->data;
- }
-
- g_slist_free(path_list);
-
- return path_array;
-}
-
-char **qemu_fdt_node_path(void *fdt, const char *name, const char *compat,
- Error **errp)
-{
- int offset, len, ret;
- const char *iter_name;
- unsigned int path_len = 16, n = 0;
- GSList *path_list = NULL, *iter;
- char **path_array;
-
- offset = fdt_node_offset_by_compatible(fdt, -1, compat);
-
- while (offset >= 0) {
- iter_name = fdt_get_name(fdt, offset, &len);
- if (!iter_name) {
- offset = len;
- break;
- }
- if (!name || !strcmp(iter_name, name)) {
- char *path;
-
- path = g_malloc(path_len);
- while ((ret = fdt_get_path(fdt, offset, path, path_len))
- == -FDT_ERR_NOSPACE) {
- path_len += 16;
- path = g_realloc(path, path_len);
- }
- path_list = g_slist_prepend(path_list, path);
- n++;
- }
- offset = fdt_node_offset_by_compatible(fdt, offset, compat);
- }
-
- if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {
- error_setg(errp, "%s: abort parsing dt for %s/%s: %s",
- __func__, name, compat, fdt_strerror(offset));
- for (iter = path_list; iter; iter = iter->next) {
- g_free(iter->data);
- }
- g_slist_free(path_list);
- return NULL;
- }
-
- path_array = g_new(char *, n + 1);
- path_array[n--] = NULL;
-
- for (iter = path_list; iter; iter = iter->next) {
- path_array[n--] = iter->data;
- }
-
- g_slist_free(path_list);
-
- return path_array;
-}
-
-int qemu_fdt_setprop(void *fdt, const char *node_path,
- const char *property, const void *val, int size)
-{
- int r;
-
- r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val, size);
- if (r < 0) {
- error_report("%s: Couldn't set %s/%s: %s", __func__, node_path,
- property, fdt_strerror(r));
- exit(1);
- }
-
- return r;
-}
-
-int qemu_fdt_setprop_cell(void *fdt, const char *node_path,
- const char *property, uint32_t val)
-{
- int r;
-
- r = fdt_setprop_cell(fdt, findnode_nofail(fdt, node_path), property, val);
- if (r < 0) {
- error_report("%s: Couldn't set %s/%s = %#08x: %s", __func__,
- node_path, property, val, fdt_strerror(r));
- exit(1);
- }
-
- return r;
-}
-
-int qemu_fdt_setprop_u64(void *fdt, const char *node_path,
- const char *property, uint64_t val)
-{
- val = cpu_to_be64(val);
- return qemu_fdt_setprop(fdt, node_path, property, &val, sizeof(val));
-}
-
-int qemu_fdt_setprop_string(void *fdt, const char *node_path,
- const char *property, const char *string)
-{
- int r;
-
- r = fdt_setprop_string(fdt, findnode_nofail(fdt, node_path), property, string);
- if (r < 0) {
- error_report("%s: Couldn't set %s/%s = %s: %s", __func__,
- node_path, property, string, fdt_strerror(r));
- exit(1);
- }
-
- return r;
-}
-
-const void *qemu_fdt_getprop(void *fdt, const char *node_path,
- const char *property, int *lenp, Error **errp)
-{
- int len;
- const void *r;
-
- if (!lenp) {
- lenp = &len;
- }
- r = fdt_getprop(fdt, findnode_nofail(fdt, node_path), property, lenp);
- if (!r) {
- error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__,
- node_path, property, fdt_strerror(*lenp));
- }
- return r;
-}
-
-uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
- const char *property, int *lenp, Error **errp)
-{
- int len;
- const uint32_t *p;
-
- if (!lenp) {
- lenp = &len;
- }
- p = qemu_fdt_getprop(fdt, node_path, property, lenp, errp);
- if (!p) {
- return 0;
- } else if (*lenp != 4) {
- error_setg(errp, "%s: %s/%s not 4 bytes long (not a cell?)",
- __func__, node_path, property);
- *lenp = -EINVAL;
- return 0;
- }
- return be32_to_cpu(*p);
-}
-
-uint32_t qemu_fdt_get_phandle(void *fdt, const char *path)
-{
- uint32_t r;
-
- r = fdt_get_phandle(fdt, findnode_nofail(fdt, path));
- if (r == 0) {
- error_report("%s: Couldn't get phandle for %s: %s", __func__,
- path, fdt_strerror(r));
- exit(1);
- }
-
- return r;
-}
-
-int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
- const char *property,
- const char *target_node_path)
-{
- uint32_t phandle = qemu_fdt_get_phandle(fdt, target_node_path);
- return qemu_fdt_setprop_cell(fdt, node_path, property, phandle);
-}
-
-uint32_t qemu_fdt_alloc_phandle(void *fdt)
-{
- static int phandle = 0x0;
-
- /*
- * We need to find out if the user gave us special instruction at
- * which phandle id to start allocating phandles.
- */
- if (!phandle) {
- phandle = machine_phandle_start(current_machine);
- }
-
- if (!phandle) {
- /*
- * None or invalid phandle given on the command line, so fall back to
- * default starting point.
- */
- phandle = 0x8000;
- }
-
- return phandle++;
-}
-
-int qemu_fdt_nop_node(void *fdt, const char *node_path)
-{
- int r;
-
- r = fdt_nop_node(fdt, findnode_nofail(fdt, node_path));
- if (r < 0) {
- error_report("%s: Couldn't nop node %s: %s", __func__, node_path,
- fdt_strerror(r));
- exit(1);
- }
-
- return r;
-}
-
-int qemu_fdt_add_subnode(void *fdt, const char *name)
-{
- char *dupname = g_strdup(name);
- char *basename = strrchr(dupname, '/');
- int retval;
- int parent = 0;
-
- if (!basename) {
- g_free(dupname);
- return -1;
- }
-
- basename[0] = '\0';
- basename++;
-
- if (dupname[0]) {
- parent = findnode_nofail(fdt, dupname);
- }
-
- retval = fdt_add_subnode(fdt, parent, basename);
- if (retval < 0) {
- error_report("FDT: Failed to create subnode %s: %s", name,
- fdt_strerror(retval));
- exit(1);
- }
-
- g_free(dupname);
- return retval;
-}
-
-void qemu_fdt_dumpdtb(void *fdt, int size)
-{
- const char *dumpdtb = qemu_opt_get(qemu_get_machine_opts(), "dumpdtb");
-
- if (dumpdtb) {
- /* Dump the dtb to a file and quit */
- if (g_file_set_contents(dumpdtb, fdt, size, NULL)) {
- info_report("dtb dumped to %s. Exiting.", dumpdtb);
- exit(0);
- }
- error_report("%s: Failed dumping dtb to %s", __func__, dumpdtb);
- exit(1);
- }
-}
-
-int qemu_fdt_setprop_sized_cells_from_array(void *fdt,
- const char *node_path,
- const char *property,
- int numvalues,
- uint64_t *values)
-{
- uint32_t *propcells;
- uint64_t value;
- int cellnum, vnum, ncells;
- uint32_t hival;
- int ret;
-
- propcells = g_new0(uint32_t, numvalues * 2);
-
- cellnum = 0;
- for (vnum = 0; vnum < numvalues; vnum++) {
- ncells = values[vnum * 2];
- if (ncells != 1 && ncells != 2) {
- ret = -1;
- goto out;
- }
- value = values[vnum * 2 + 1];
- hival = cpu_to_be32(value >> 32);
- if (ncells > 1) {
- propcells[cellnum++] = hival;
- } else if (hival != 0) {
- ret = -1;
- goto out;
- }
- propcells[cellnum++] = cpu_to_be32(value);
- }
-
- ret = qemu_fdt_setprop(fdt, node_path, property, propcells,
- cellnum * sizeof(uint32_t));
-out:
- g_free(propcells);
- return ret;
-}
+++ /dev/null
-/*
- * DMA helper functions
- *
- * Copyright (c) 2009 Red Hat
- *
- * This work is licensed under the terms of the GNU General Public License
- * (GNU GPL), version 2 or later.
- */
-
-#include "qemu/osdep.h"
-#include "sysemu/block-backend.h"
-#include "sysemu/dma.h"
-#include "trace/trace-root.h"
-#include "qemu/thread.h"
-#include "qemu/main-loop.h"
-#include "sysemu/cpu-timers.h"
-#include "qemu/range.h"
-
-/* #define DEBUG_IOMMU */
-
-int dma_memory_set(AddressSpace *as, dma_addr_t addr, uint8_t c, dma_addr_t len)
-{
- dma_barrier(as, DMA_DIRECTION_FROM_DEVICE);
-
-#define FILLBUF_SIZE 512
- uint8_t fillbuf[FILLBUF_SIZE];
- int l;
- bool error = false;
-
- memset(fillbuf, c, FILLBUF_SIZE);
- while (len > 0) {
- l = len < FILLBUF_SIZE ? len : FILLBUF_SIZE;
- error |= address_space_write(as, addr, MEMTXATTRS_UNSPECIFIED,
- fillbuf, l);
- len -= l;
- addr += l;
- }
-
- return error;
-}
-
-void qemu_sglist_init(QEMUSGList *qsg, DeviceState *dev, int alloc_hint,
- AddressSpace *as)
-{
- qsg->sg = g_malloc(alloc_hint * sizeof(ScatterGatherEntry));
- qsg->nsg = 0;
- qsg->nalloc = alloc_hint;
- qsg->size = 0;
- qsg->as = as;
- qsg->dev = dev;
- object_ref(OBJECT(dev));
-}
-
-void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len)
-{
- if (qsg->nsg == qsg->nalloc) {
- qsg->nalloc = 2 * qsg->nalloc + 1;
- qsg->sg = g_realloc(qsg->sg, qsg->nalloc * sizeof(ScatterGatherEntry));
- }
- qsg->sg[qsg->nsg].base = base;
- qsg->sg[qsg->nsg].len = len;
- qsg->size += len;
- ++qsg->nsg;
-}
-
-void qemu_sglist_destroy(QEMUSGList *qsg)
-{
- object_unref(OBJECT(qsg->dev));
- g_free(qsg->sg);
- memset(qsg, 0, sizeof(*qsg));
-}
-
-typedef struct {
- BlockAIOCB common;
- AioContext *ctx;
- BlockAIOCB *acb;
- QEMUSGList *sg;
- uint32_t align;
- uint64_t offset;
- DMADirection dir;
- int sg_cur_index;
- dma_addr_t sg_cur_byte;
- QEMUIOVector iov;
- QEMUBH *bh;
- DMAIOFunc *io_func;
- void *io_func_opaque;
-} DMAAIOCB;
-
-static void dma_blk_cb(void *opaque, int ret);
-
-static void reschedule_dma(void *opaque)
-{
- DMAAIOCB *dbs = (DMAAIOCB *)opaque;
-
- assert(!dbs->acb && dbs->bh);
- qemu_bh_delete(dbs->bh);
- dbs->bh = NULL;
- dma_blk_cb(dbs, 0);
-}
-
-static void dma_blk_unmap(DMAAIOCB *dbs)
-{
- int i;
-
- for (i = 0; i < dbs->iov.niov; ++i) {
- dma_memory_unmap(dbs->sg->as, dbs->iov.iov[i].iov_base,
- dbs->iov.iov[i].iov_len, dbs->dir,
- dbs->iov.iov[i].iov_len);
- }
- qemu_iovec_reset(&dbs->iov);
-}
-
-static void dma_complete(DMAAIOCB *dbs, int ret)
-{
- trace_dma_complete(dbs, ret, dbs->common.cb);
-
- assert(!dbs->acb && !dbs->bh);
- dma_blk_unmap(dbs);
- if (dbs->common.cb) {
- dbs->common.cb(dbs->common.opaque, ret);
- }
- qemu_iovec_destroy(&dbs->iov);
- qemu_aio_unref(dbs);
-}
-
-static void dma_blk_cb(void *opaque, int ret)
-{
- DMAAIOCB *dbs = (DMAAIOCB *)opaque;
- dma_addr_t cur_addr, cur_len;
- void *mem;
-
- trace_dma_blk_cb(dbs, ret);
-
- dbs->acb = NULL;
- dbs->offset += dbs->iov.size;
-
- if (dbs->sg_cur_index == dbs->sg->nsg || ret < 0) {
- dma_complete(dbs, ret);
- return;
- }
- dma_blk_unmap(dbs);
-
- while (dbs->sg_cur_index < dbs->sg->nsg) {
- cur_addr = dbs->sg->sg[dbs->sg_cur_index].base + dbs->sg_cur_byte;
- cur_len = dbs->sg->sg[dbs->sg_cur_index].len - dbs->sg_cur_byte;
- mem = dma_memory_map(dbs->sg->as, cur_addr, &cur_len, dbs->dir);
- /*
- * Make reads deterministic in icount mode. Windows sometimes issues
- * disk read requests with overlapping SGs. It leads
- * to non-determinism, because resulting buffer contents may be mixed
- * from several sectors. This code splits all SGs into several
- * groups. SGs in every group do not overlap.
- */
- if (mem && icount_enabled() && dbs->dir == DMA_DIRECTION_FROM_DEVICE) {
- int i;
- for (i = 0 ; i < dbs->iov.niov ; ++i) {
- if (ranges_overlap((intptr_t)dbs->iov.iov[i].iov_base,
- dbs->iov.iov[i].iov_len, (intptr_t)mem,
- cur_len)) {
- dma_memory_unmap(dbs->sg->as, mem, cur_len,
- dbs->dir, cur_len);
- mem = NULL;
- break;
- }
- }
- }
- if (!mem)
- break;
- qemu_iovec_add(&dbs->iov, mem, cur_len);
- dbs->sg_cur_byte += cur_len;
- if (dbs->sg_cur_byte == dbs->sg->sg[dbs->sg_cur_index].len) {
- dbs->sg_cur_byte = 0;
- ++dbs->sg_cur_index;
- }
- }
-
- if (dbs->iov.size == 0) {
- trace_dma_map_wait(dbs);
- dbs->bh = aio_bh_new(dbs->ctx, reschedule_dma, dbs);
- cpu_register_map_client(dbs->bh);
- return;
- }
-
- if (!QEMU_IS_ALIGNED(dbs->iov.size, dbs->align)) {
- qemu_iovec_discard_back(&dbs->iov,
- QEMU_ALIGN_DOWN(dbs->iov.size, dbs->align));
- }
-
- aio_context_acquire(dbs->ctx);
- dbs->acb = dbs->io_func(dbs->offset, &dbs->iov,
- dma_blk_cb, dbs, dbs->io_func_opaque);
- aio_context_release(dbs->ctx);
- assert(dbs->acb);
-}
-
-static void dma_aio_cancel(BlockAIOCB *acb)
-{
- DMAAIOCB *dbs = container_of(acb, DMAAIOCB, common);
-
- trace_dma_aio_cancel(dbs);
-
- assert(!(dbs->acb && dbs->bh));
- if (dbs->acb) {
- /* This will invoke dma_blk_cb. */
- blk_aio_cancel_async(dbs->acb);
- return;
- }
-
- if (dbs->bh) {
- cpu_unregister_map_client(dbs->bh);
- qemu_bh_delete(dbs->bh);
- dbs->bh = NULL;
- }
- if (dbs->common.cb) {
- dbs->common.cb(dbs->common.opaque, -ECANCELED);
- }
-}
-
-static AioContext *dma_get_aio_context(BlockAIOCB *acb)
-{
- DMAAIOCB *dbs = container_of(acb, DMAAIOCB, common);
-
- return dbs->ctx;
-}
-
-static const AIOCBInfo dma_aiocb_info = {
- .aiocb_size = sizeof(DMAAIOCB),
- .cancel_async = dma_aio_cancel,
- .get_aio_context = dma_get_aio_context,
-};
-
-BlockAIOCB *dma_blk_io(AioContext *ctx,
- QEMUSGList *sg, uint64_t offset, uint32_t align,
- DMAIOFunc *io_func, void *io_func_opaque,
- BlockCompletionFunc *cb,
- void *opaque, DMADirection dir)
-{
- DMAAIOCB *dbs = qemu_aio_get(&dma_aiocb_info, NULL, cb, opaque);
-
- trace_dma_blk_io(dbs, io_func_opaque, offset, (dir == DMA_DIRECTION_TO_DEVICE));
-
- dbs->acb = NULL;
- dbs->sg = sg;
- dbs->ctx = ctx;
- dbs->offset = offset;
- dbs->align = align;
- dbs->sg_cur_index = 0;
- dbs->sg_cur_byte = 0;
- dbs->dir = dir;
- dbs->io_func = io_func;
- dbs->io_func_opaque = io_func_opaque;
- dbs->bh = NULL;
- qemu_iovec_init(&dbs->iov, sg->nsg);
- dma_blk_cb(dbs, 0);
- return &dbs->common;
-}
-
-
-static
-BlockAIOCB *dma_blk_read_io_func(int64_t offset, QEMUIOVector *iov,
- BlockCompletionFunc *cb, void *cb_opaque,
- void *opaque)
-{
- BlockBackend *blk = opaque;
- return blk_aio_preadv(blk, offset, iov, 0, cb, cb_opaque);
-}
-
-BlockAIOCB *dma_blk_read(BlockBackend *blk,
- QEMUSGList *sg, uint64_t offset, uint32_t align,
- void (*cb)(void *opaque, int ret), void *opaque)
-{
- return dma_blk_io(blk_get_aio_context(blk), sg, offset, align,
- dma_blk_read_io_func, blk, cb, opaque,
- DMA_DIRECTION_FROM_DEVICE);
-}
-
-static
-BlockAIOCB *dma_blk_write_io_func(int64_t offset, QEMUIOVector *iov,
- BlockCompletionFunc *cb, void *cb_opaque,
- void *opaque)
-{
- BlockBackend *blk = opaque;
- return blk_aio_pwritev(blk, offset, iov, 0, cb, cb_opaque);
-}
-
-BlockAIOCB *dma_blk_write(BlockBackend *blk,
- QEMUSGList *sg, uint64_t offset, uint32_t align,
- void (*cb)(void *opaque, int ret), void *opaque)
-{
- return dma_blk_io(blk_get_aio_context(blk), sg, offset, align,
- dma_blk_write_io_func, blk, cb, opaque,
- DMA_DIRECTION_TO_DEVICE);
-}
-
-
-static uint64_t dma_buf_rw(uint8_t *ptr, int32_t len, QEMUSGList *sg,
- DMADirection dir)
-{
- uint64_t resid;
- int sg_cur_index;
-
- resid = sg->size;
- sg_cur_index = 0;
- len = MIN(len, resid);
- while (len > 0) {
- ScatterGatherEntry entry = sg->sg[sg_cur_index++];
- int32_t xfer = MIN(len, entry.len);
- dma_memory_rw(sg->as, entry.base, ptr, xfer, dir);
- ptr += xfer;
- len -= xfer;
- resid -= xfer;
- }
-
- return resid;
-}
-
-uint64_t dma_buf_read(uint8_t *ptr, int32_t len, QEMUSGList *sg)
-{
- return dma_buf_rw(ptr, len, sg, DMA_DIRECTION_FROM_DEVICE);
-}
-
-uint64_t dma_buf_write(uint8_t *ptr, int32_t len, QEMUSGList *sg)
-{
- return dma_buf_rw(ptr, len, sg, DMA_DIRECTION_TO_DEVICE);
-}
-
-void dma_acct_start(BlockBackend *blk, BlockAcctCookie *cookie,
- QEMUSGList *sg, enum BlockAcctType type)
-{
- block_acct_start(blk_get_stats(blk), cookie, sg->size, type);
-}
# os-win32.c does not
blockdev_ss.add(when: 'CONFIG_POSIX', if_true: files('os-posix.c'))
softmmu_ss.add(when: 'CONFIG_WIN32', if_true: [files('os-win32.c')])
-
softmmu_ss.add_all(blockdev_ss)
-softmmu_ss.add(files(
- 'bootdevice.c',
- 'dma-helpers.c',
- 'qdev-monitor.c',
-), sdl)
-
-softmmu_ss.add(when: 'CONFIG_TPM', if_true: files('tpm.c'))
-softmmu_ss.add(when: 'CONFIG_SECCOMP', if_true: [files('qemu-seccomp.c'), seccomp])
-softmmu_ss.add(when: fdt, if_true: files('device_tree.c'))
common_ss.add(files('cpus-common.c'))
+++ /dev/null
-/*
- * Dynamic device configuration and creation.
- *
- * Copyright (c) 2009 CodeSourcery
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "qemu/osdep.h"
-#include "hw/sysbus.h"
-#include "monitor/hmp.h"
-#include "monitor/monitor.h"
-#include "monitor/qdev.h"
-#include "sysemu/arch_init.h"
-#include "qapi/error.h"
-#include "qapi/qapi-commands-qdev.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qerror.h"
-#include "qemu/config-file.h"
-#include "qemu/error-report.h"
-#include "qemu/help_option.h"
-#include "qemu/option.h"
-#include "qemu/qemu-print.h"
-#include "qemu/option_int.h"
-#include "sysemu/block-backend.h"
-#include "sysemu/sysemu.h"
-#include "migration/misc.h"
-#include "migration/migration.h"
-#include "qemu/cutils.h"
-#include "hw/clock.h"
-
-/*
- * Aliases were a bad idea from the start. Let's keep them
- * from spreading further.
- */
-typedef struct QDevAlias
-{
- const char *typename;
- const char *alias;
- uint32_t arch_mask;
-} QDevAlias;
-
-/* Please keep this table sorted by typename. */
-static const QDevAlias qdev_alias_table[] = {
- { "AC97", "ac97" }, /* -soundhw name */
- { "e1000", "e1000-82540em" },
- { "ES1370", "es1370" }, /* -soundhw name */
- { "ich9-ahci", "ahci" },
- { "lsi53c895a", "lsi" },
- { "virtio-9p-ccw", "virtio-9p", QEMU_ARCH_S390X },
- { "virtio-9p-pci", "virtio-9p", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-balloon-ccw", "virtio-balloon", QEMU_ARCH_S390X },
- { "virtio-balloon-pci", "virtio-balloon",
- QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-blk-ccw", "virtio-blk", QEMU_ARCH_S390X },
- { "virtio-blk-pci", "virtio-blk", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-gpu-ccw", "virtio-gpu", QEMU_ARCH_S390X },
- { "virtio-gpu-pci", "virtio-gpu", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-input-host-ccw", "virtio-input-host", QEMU_ARCH_S390X },
- { "virtio-input-host-pci", "virtio-input-host",
- QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-iommu-pci", "virtio-iommu", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-keyboard-ccw", "virtio-keyboard", QEMU_ARCH_S390X },
- { "virtio-keyboard-pci", "virtio-keyboard",
- QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-mouse-ccw", "virtio-mouse", QEMU_ARCH_S390X },
- { "virtio-mouse-pci", "virtio-mouse", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-net-ccw", "virtio-net", QEMU_ARCH_S390X },
- { "virtio-net-pci", "virtio-net", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-rng-ccw", "virtio-rng", QEMU_ARCH_S390X },
- { "virtio-rng-pci", "virtio-rng", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-scsi-ccw", "virtio-scsi", QEMU_ARCH_S390X },
- { "virtio-scsi-pci", "virtio-scsi", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-serial-ccw", "virtio-serial", QEMU_ARCH_S390X },
- { "virtio-serial-pci", "virtio-serial", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { "virtio-tablet-ccw", "virtio-tablet", QEMU_ARCH_S390X },
- { "virtio-tablet-pci", "virtio-tablet", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
- { }
-};
-
-static const char *qdev_class_get_alias(DeviceClass *dc)
-{
- const char *typename = object_class_get_name(OBJECT_CLASS(dc));
- int i;
-
- for (i = 0; qdev_alias_table[i].typename; i++) {
- if (qdev_alias_table[i].arch_mask &&
- !(qdev_alias_table[i].arch_mask & arch_type)) {
- continue;
- }
-
- if (strcmp(qdev_alias_table[i].typename, typename) == 0) {
- return qdev_alias_table[i].alias;
- }
- }
-
- return NULL;
-}
-
-static bool qdev_class_has_alias(DeviceClass *dc)
-{
- return (qdev_class_get_alias(dc) != NULL);
-}
-
-static void qdev_print_devinfo(DeviceClass *dc)
-{
- qemu_printf("name \"%s\"", object_class_get_name(OBJECT_CLASS(dc)));
- if (dc->bus_type) {
- qemu_printf(", bus %s", dc->bus_type);
- }
- if (qdev_class_has_alias(dc)) {
- qemu_printf(", alias \"%s\"", qdev_class_get_alias(dc));
- }
- if (dc->desc) {
- qemu_printf(", desc \"%s\"", dc->desc);
- }
- if (!dc->user_creatable) {
- qemu_printf(", no-user");
- }
- qemu_printf("\n");
-}
-
-static void qdev_print_devinfos(bool show_no_user)
-{
- static const char *cat_name[DEVICE_CATEGORY_MAX + 1] = {
- [DEVICE_CATEGORY_BRIDGE] = "Controller/Bridge/Hub",
- [DEVICE_CATEGORY_USB] = "USB",
- [DEVICE_CATEGORY_STORAGE] = "Storage",
- [DEVICE_CATEGORY_NETWORK] = "Network",
- [DEVICE_CATEGORY_INPUT] = "Input",
- [DEVICE_CATEGORY_DISPLAY] = "Display",
- [DEVICE_CATEGORY_SOUND] = "Sound",
- [DEVICE_CATEGORY_MISC] = "Misc",
- [DEVICE_CATEGORY_CPU] = "CPU",
- [DEVICE_CATEGORY_MAX] = "Uncategorized",
- };
- GSList *list, *elt;
- int i;
- bool cat_printed;
-
- module_load_qom_all();
- list = object_class_get_list_sorted(TYPE_DEVICE, false);
-
- for (i = 0; i <= DEVICE_CATEGORY_MAX; i++) {
- cat_printed = false;
- for (elt = list; elt; elt = elt->next) {
- DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, elt->data,
- TYPE_DEVICE);
- if ((i < DEVICE_CATEGORY_MAX
- ? !test_bit(i, dc->categories)
- : !bitmap_empty(dc->categories, DEVICE_CATEGORY_MAX))
- || (!show_no_user
- && !dc->user_creatable)) {
- continue;
- }
- if (!cat_printed) {
- qemu_printf("%s%s devices:\n", i ? "\n" : "", cat_name[i]);
- cat_printed = true;
- }
- qdev_print_devinfo(dc);
- }
- }
-
- g_slist_free(list);
-}
-
-static int set_property(void *opaque, const char *name, const char *value,
- Error **errp)
-{
- Object *obj = opaque;
-
- if (strcmp(name, "driver") == 0)
- return 0;
- if (strcmp(name, "bus") == 0)
- return 0;
-
- if (!object_property_parse(obj, name, value, errp)) {
- return -1;
- }
- return 0;
-}
-
-static const char *find_typename_by_alias(const char *alias)
-{
- int i;
-
- for (i = 0; qdev_alias_table[i].alias; i++) {
- if (qdev_alias_table[i].arch_mask &&
- !(qdev_alias_table[i].arch_mask & arch_type)) {
- continue;
- }
-
- if (strcmp(qdev_alias_table[i].alias, alias) == 0) {
- return qdev_alias_table[i].typename;
- }
- }
-
- return NULL;
-}
-
-static DeviceClass *qdev_get_device_class(const char **driver, Error **errp)
-{
- ObjectClass *oc;
- DeviceClass *dc;
- const char *original_name = *driver;
-
- oc = module_object_class_by_name(*driver);
- if (!oc) {
- const char *typename = find_typename_by_alias(*driver);
-
- if (typename) {
- *driver = typename;
- oc = module_object_class_by_name(*driver);
- }
- }
-
- if (!object_class_dynamic_cast(oc, TYPE_DEVICE)) {
- if (*driver != original_name) {
- error_setg(errp, "'%s' (alias '%s') is not a valid device model"
- " name", original_name, *driver);
- } else {
- error_setg(errp, "'%s' is not a valid device model name", *driver);
- }
- return NULL;
- }
-
- if (object_class_is_abstract(oc)) {
- error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
- "non-abstract device type");
- return NULL;
- }
-
- dc = DEVICE_CLASS(oc);
- if (!dc->user_creatable ||
- (qdev_hotplug && !dc->hotpluggable)) {
- error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
- "pluggable device type");
- return NULL;
- }
-
- return dc;
-}
-
-
-int qdev_device_help(QemuOpts *opts)
-{
- Error *local_err = NULL;
- const char *driver;
- ObjectPropertyInfoList *prop_list;
- ObjectPropertyInfoList *prop;
- GPtrArray *array;
- int i;
-
- driver = qemu_opt_get(opts, "driver");
- if (driver && is_help_option(driver)) {
- qdev_print_devinfos(false);
- return 1;
- }
-
- if (!driver || !qemu_opt_has_help_opt(opts)) {
- return 0;
- }
-
- if (!object_class_by_name(driver)) {
- const char *typename = find_typename_by_alias(driver);
-
- if (typename) {
- driver = typename;
- }
- }
-
- prop_list = qmp_device_list_properties(driver, &local_err);
- if (local_err) {
- goto error;
- }
-
- if (prop_list) {
- qemu_printf("%s options:\n", driver);
- } else {
- qemu_printf("There are no options for %s.\n", driver);
- }
- array = g_ptr_array_new();
- for (prop = prop_list; prop; prop = prop->next) {
- g_ptr_array_add(array,
- object_property_help(prop->value->name,
- prop->value->type,
- prop->value->default_value,
- prop->value->description));
- }
- g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0);
- for (i = 0; i < array->len; i++) {
- qemu_printf("%s\n", (char *)array->pdata[i]);
- }
- g_ptr_array_set_free_func(array, g_free);
- g_ptr_array_free(array, true);
- qapi_free_ObjectPropertyInfoList(prop_list);
- return 1;
-
-error:
- error_report_err(local_err);
- return 1;
-}
-
-static Object *qdev_get_peripheral(void)
-{
- static Object *dev;
-
- if (dev == NULL) {
- dev = container_get(qdev_get_machine(), "/peripheral");
- }
-
- return dev;
-}
-
-static Object *qdev_get_peripheral_anon(void)
-{
- static Object *dev;
-
- if (dev == NULL) {
- dev = container_get(qdev_get_machine(), "/peripheral-anon");
- }
-
- return dev;
-}
-
-static void qbus_error_append_bus_list_hint(DeviceState *dev,
- Error *const *errp)
-{
- BusState *child;
- const char *sep = " ";
-
- error_append_hint(errp, "child buses at \"%s\":",
- dev->id ? dev->id : object_get_typename(OBJECT(dev)));
- QLIST_FOREACH(child, &dev->child_bus, sibling) {
- error_append_hint(errp, "%s\"%s\"", sep, child->name);
- sep = ", ";
- }
- error_append_hint(errp, "\n");
-}
-
-static void qbus_error_append_dev_list_hint(BusState *bus,
- Error *const *errp)
-{
- BusChild *kid;
- const char *sep = " ";
-
- error_append_hint(errp, "devices at \"%s\":", bus->name);
- QTAILQ_FOREACH(kid, &bus->children, sibling) {
- DeviceState *dev = kid->child;
- error_append_hint(errp, "%s\"%s\"", sep,
- object_get_typename(OBJECT(dev)));
- if (dev->id) {
- error_append_hint(errp, "/\"%s\"", dev->id);
- }
- sep = ", ";
- }
- error_append_hint(errp, "\n");
-}
-
-static BusState *qbus_find_bus(DeviceState *dev, char *elem)
-{
- BusState *child;
-
- QLIST_FOREACH(child, &dev->child_bus, sibling) {
- if (strcmp(child->name, elem) == 0) {
- return child;
- }
- }
- return NULL;
-}
-
-static DeviceState *qbus_find_dev(BusState *bus, char *elem)
-{
- BusChild *kid;
-
- /*
- * try to match in order:
- * (1) instance id, if present
- * (2) driver name
- * (3) driver alias, if present
- */
- QTAILQ_FOREACH(kid, &bus->children, sibling) {
- DeviceState *dev = kid->child;
- if (dev->id && strcmp(dev->id, elem) == 0) {
- return dev;
- }
- }
- QTAILQ_FOREACH(kid, &bus->children, sibling) {
- DeviceState *dev = kid->child;
- if (strcmp(object_get_typename(OBJECT(dev)), elem) == 0) {
- return dev;
- }
- }
- QTAILQ_FOREACH(kid, &bus->children, sibling) {
- DeviceState *dev = kid->child;
- DeviceClass *dc = DEVICE_GET_CLASS(dev);
-
- if (qdev_class_has_alias(dc) &&
- strcmp(qdev_class_get_alias(dc), elem) == 0) {
- return dev;
- }
- }
- return NULL;
-}
-
-static inline bool qbus_is_full(BusState *bus)
-{
- BusClass *bus_class = BUS_GET_CLASS(bus);
- return bus_class->max_dev && bus->num_children >= bus_class->max_dev;
-}
-
-/*
- * Search the tree rooted at @bus for a bus.
- * If @name, search for a bus with that name. Note that bus names
- * need not be unique. Yes, that's screwed up.
- * Else search for a bus that is a subtype of @bus_typename.
- * If more than one exists, prefer one that can take another device.
- * Return the bus if found, else %NULL.
- */
-static BusState *qbus_find_recursive(BusState *bus, const char *name,
- const char *bus_typename)
-{
- BusChild *kid;
- BusState *pick, *child, *ret;
- bool match;
-
- assert(name || bus_typename);
- if (name) {
- match = !strcmp(bus->name, name);
- } else {
- match = !!object_dynamic_cast(OBJECT(bus), bus_typename);
- }
-
- if (match && !qbus_is_full(bus)) {
- return bus; /* root matches and isn't full */
- }
-
- pick = match ? bus : NULL;
-
- QTAILQ_FOREACH(kid, &bus->children, sibling) {
- DeviceState *dev = kid->child;
- QLIST_FOREACH(child, &dev->child_bus, sibling) {
- ret = qbus_find_recursive(child, name, bus_typename);
- if (ret && !qbus_is_full(ret)) {
- return ret; /* a descendant matches and isn't full */
- }
- if (ret && !pick) {
- pick = ret;
- }
- }
- }
-
- /* root or a descendant matches, but is full */
- return pick;
-}
-
-static BusState *qbus_find(const char *path, Error **errp)
-{
- DeviceState *dev;
- BusState *bus;
- char elem[128];
- int pos, len;
-
- /* find start element */
- if (path[0] == '/') {
- bus = sysbus_get_default();
- pos = 0;
- } else {
- if (sscanf(path, "%127[^/]%n", elem, &len) != 1) {
- assert(!path[0]);
- elem[0] = len = 0;
- }
- bus = qbus_find_recursive(sysbus_get_default(), elem, NULL);
- if (!bus) {
- error_setg(errp, "Bus '%s' not found", elem);
- return NULL;
- }
- pos = len;
- }
-
- for (;;) {
- assert(path[pos] == '/' || !path[pos]);
- while (path[pos] == '/') {
- pos++;
- }
- if (path[pos] == '\0') {
- break;
- }
-
- /* find device */
- if (sscanf(path+pos, "%127[^/]%n", elem, &len) != 1) {
- g_assert_not_reached();
- elem[0] = len = 0;
- }
- pos += len;
- dev = qbus_find_dev(bus, elem);
- if (!dev) {
- error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
- "Device '%s' not found", elem);
- qbus_error_append_dev_list_hint(bus, errp);
- return NULL;
- }
-
- assert(path[pos] == '/' || !path[pos]);
- while (path[pos] == '/') {
- pos++;
- }
- if (path[pos] == '\0') {
- /* last specified element is a device. If it has exactly
- * one child bus accept it nevertheless */
- if (dev->num_child_bus == 1) {
- bus = QLIST_FIRST(&dev->child_bus);
- break;
- }
- if (dev->num_child_bus) {
- error_setg(errp, "Device '%s' has multiple child buses",
- elem);
- qbus_error_append_bus_list_hint(dev, errp);
- } else {
- error_setg(errp, "Device '%s' has no child bus", elem);
- }
- return NULL;
- }
-
- /* find bus */
- if (sscanf(path+pos, "%127[^/]%n", elem, &len) != 1) {
- g_assert_not_reached();
- elem[0] = len = 0;
- }
- pos += len;
- bus = qbus_find_bus(dev, elem);
- if (!bus) {
- error_setg(errp, "Bus '%s' not found", elem);
- qbus_error_append_bus_list_hint(dev, errp);
- return NULL;
- }
- }
-
- if (qbus_is_full(bus)) {
- error_setg(errp, "Bus '%s' is full", path);
- return NULL;
- }
- return bus;
-}
-
-void qdev_set_id(DeviceState *dev, const char *id)
-{
- if (id) {
- dev->id = id;
- }
-
- if (dev->id) {
- object_property_add_child(qdev_get_peripheral(), dev->id,
- OBJECT(dev));
- } else {
- static int anon_count;
- gchar *name = g_strdup_printf("device[%d]", anon_count++);
- object_property_add_child(qdev_get_peripheral_anon(), name,
- OBJECT(dev));
- g_free(name);
- }
-}
-
-static int is_failover_device(void *opaque, const char *name, const char *value,
- Error **errp)
-{
- if (strcmp(name, "failover_pair_id") == 0) {
- QemuOpts *opts = (QemuOpts *)opaque;
-
- if (qdev_should_hide_device(opts)) {
- return 1;
- }
- }
-
- return 0;
-}
-
-static bool should_hide_device(QemuOpts *opts)
-{
- if (qemu_opt_foreach(opts, is_failover_device, opts, NULL) == 0) {
- return false;
- }
- return true;
-}
-
-DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
-{
- DeviceClass *dc;
- const char *driver, *path;
- DeviceState *dev = NULL;
- BusState *bus = NULL;
- bool hide;
-
- driver = qemu_opt_get(opts, "driver");
- if (!driver) {
- error_setg(errp, QERR_MISSING_PARAMETER, "driver");
- return NULL;
- }
-
- /* find driver */
- dc = qdev_get_device_class(&driver, errp);
- if (!dc) {
- return NULL;
- }
-
- /* find bus */
- path = qemu_opt_get(opts, "bus");
- if (path != NULL) {
- bus = qbus_find(path, errp);
- if (!bus) {
- return NULL;
- }
- if (!object_dynamic_cast(OBJECT(bus), dc->bus_type)) {
- error_setg(errp, "Device '%s' can't go on %s bus",
- driver, object_get_typename(OBJECT(bus)));
- return NULL;
- }
- } else if (dc->bus_type != NULL) {
- bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type);
- if (!bus || qbus_is_full(bus)) {
- error_setg(errp, "No '%s' bus found for device '%s'",
- dc->bus_type, driver);
- return NULL;
- }
- }
- hide = should_hide_device(opts);
-
- if ((hide || qdev_hotplug) && bus && !qbus_is_hotpluggable(bus)) {
- error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name);
- return NULL;
- }
-
- if (hide) {
- return NULL;
- }
-
- if (!migration_is_idle()) {
- error_setg(errp, "device_add not allowed while migrating");
- return NULL;
- }
-
- /* create device */
- dev = qdev_new(driver);
-
- /* Check whether the hotplug is allowed by the machine */
- if (qdev_hotplug && !qdev_hotplug_allowed(dev, errp)) {
- goto err_del_dev;
- }
-
- if (!bus && qdev_hotplug && !qdev_get_machine_hotplug_handler(dev)) {
- /* No bus, no machine hotplug handler --> device is not hotpluggable */
- error_setg(errp, "Device '%s' can not be hotplugged on this machine",
- driver);
- goto err_del_dev;
- }
-
- qdev_set_id(dev, qemu_opts_id(opts));
-
- /* set properties */
- if (qemu_opt_foreach(opts, set_property, dev, errp)) {
- goto err_del_dev;
- }
-
- dev->opts = opts;
- if (!qdev_realize(DEVICE(dev), bus, errp)) {
- dev->opts = NULL;
- goto err_del_dev;
- }
- return dev;
-
-err_del_dev:
- if (dev) {
- object_unparent(OBJECT(dev));
- object_unref(OBJECT(dev));
- }
- return NULL;
-}
-
-
-#define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__)
-static void qbus_print(Monitor *mon, BusState *bus, int indent);
-
-static void qdev_print_props(Monitor *mon, DeviceState *dev, Property *props,
- int indent)
-{
- if (!props)
- return;
- for (; props->name; props++) {
- char *value;
- char *legacy_name = g_strdup_printf("legacy-%s", props->name);
-
- if (object_property_get_type(OBJECT(dev), legacy_name, NULL)) {
- value = object_property_get_str(OBJECT(dev), legacy_name, NULL);
- } else {
- value = object_property_print(OBJECT(dev), props->name, true,
- NULL);
- }
- g_free(legacy_name);
-
- if (!value) {
- continue;
- }
- qdev_printf("%s = %s\n", props->name,
- *value ? value : "<null>");
- g_free(value);
- }
-}
-
-static void bus_print_dev(BusState *bus, Monitor *mon, DeviceState *dev, int indent)
-{
- BusClass *bc = BUS_GET_CLASS(bus);
-
- if (bc->print_dev) {
- bc->print_dev(mon, dev, indent);
- }
-}
-
-static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
-{
- ObjectClass *class;
- BusState *child;
- NamedGPIOList *ngl;
- NamedClockList *ncl;
-
- qdev_printf("dev: %s, id \"%s\"\n", object_get_typename(OBJECT(dev)),
- dev->id ? dev->id : "");
- indent += 2;
- QLIST_FOREACH(ngl, &dev->gpios, node) {
- if (ngl->num_in) {
- qdev_printf("gpio-in \"%s\" %d\n", ngl->name ? ngl->name : "",
- ngl->num_in);
- }
- if (ngl->num_out) {
- qdev_printf("gpio-out \"%s\" %d\n", ngl->name ? ngl->name : "",
- ngl->num_out);
- }
- }
- QLIST_FOREACH(ncl, &dev->clocks, node) {
- qdev_printf("clock-%s%s \"%s\" freq_hz=%e\n",
- ncl->output ? "out" : "in",
- ncl->alias ? " (alias)" : "",
- ncl->name,
- CLOCK_PERIOD_TO_HZ(1.0 * clock_get(ncl->clock)));
- }
- class = object_get_class(OBJECT(dev));
- do {
- qdev_print_props(mon, dev, DEVICE_CLASS(class)->props_, indent);
- class = object_class_get_parent(class);
- } while (class != object_class_by_name(TYPE_DEVICE));
- bus_print_dev(dev->parent_bus, mon, dev, indent);
- QLIST_FOREACH(child, &dev->child_bus, sibling) {
- qbus_print(mon, child, indent);
- }
-}
-
-static void qbus_print(Monitor *mon, BusState *bus, int indent)
-{
- BusChild *kid;
-
- qdev_printf("bus: %s\n", bus->name);
- indent += 2;
- qdev_printf("type %s\n", object_get_typename(OBJECT(bus)));
- QTAILQ_FOREACH(kid, &bus->children, sibling) {
- DeviceState *dev = kid->child;
- qdev_print(mon, dev, indent);
- }
-}
-#undef qdev_printf
-
-void hmp_info_qtree(Monitor *mon, const QDict *qdict)
-{
- if (sysbus_get_default())
- qbus_print(mon, sysbus_get_default(), 0);
-}
-
-void hmp_info_qdm(Monitor *mon, const QDict *qdict)
-{
- qdev_print_devinfos(true);
-}
-
-void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp)
-{
- QemuOpts *opts;
- DeviceState *dev;
-
- opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, errp);
- if (!opts) {
- return;
- }
- if (!monitor_cur_is_qmp() && qdev_device_help(opts)) {
- qemu_opts_del(opts);
- return;
- }
- dev = qdev_device_add(opts, errp);
- if (!dev) {
- qemu_opts_del(opts);
- return;
- }
- object_unref(OBJECT(dev));
-}
-
-static DeviceState *find_device_state(const char *id, Error **errp)
-{
- Object *obj;
-
- if (id[0] == '/') {
- obj = object_resolve_path(id, NULL);
- } else {
- char *root_path = object_get_canonical_path(qdev_get_peripheral());
- char *path = g_strdup_printf("%s/%s", root_path, id);
-
- g_free(root_path);
- obj = object_resolve_path_type(path, TYPE_DEVICE, NULL);
- g_free(path);
- }
-
- if (!obj) {
- error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
- "Device '%s' not found", id);
- return NULL;
- }
-
- if (!object_dynamic_cast(obj, TYPE_DEVICE)) {
- error_setg(errp, "%s is not a hotpluggable device", id);
- return NULL;
- }
-
- return DEVICE(obj);
-}
-
-void qdev_unplug(DeviceState *dev, Error **errp)
-{
- DeviceClass *dc = DEVICE_GET_CLASS(dev);
- HotplugHandler *hotplug_ctrl;
- HotplugHandlerClass *hdc;
- Error *local_err = NULL;
-
- if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) {
- error_setg(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name);
- return;
- }
-
- if (!dc->hotpluggable) {
- error_setg(errp, QERR_DEVICE_NO_HOTPLUG,
- object_get_typename(OBJECT(dev)));
- return;
- }
-
- if (!migration_is_idle() && !dev->allow_unplug_during_migration) {
- error_setg(errp, "device_del not allowed while migrating");
- return;
- }
-
- qdev_hot_removed = true;
-
- hotplug_ctrl = qdev_get_hotplug_handler(dev);
- /* hotpluggable device MUST have HotplugHandler, if it doesn't
- * then something is very wrong with it */
- g_assert(hotplug_ctrl);
-
- /* If device supports async unplug just request it to be done,
- * otherwise just remove it synchronously */
- hdc = HOTPLUG_HANDLER_GET_CLASS(hotplug_ctrl);
- if (hdc->unplug_request) {
- hotplug_handler_unplug_request(hotplug_ctrl, dev, &local_err);
- } else {
- hotplug_handler_unplug(hotplug_ctrl, dev, &local_err);
- if (!local_err) {
- object_unparent(OBJECT(dev));
- }
- }
- error_propagate(errp, local_err);
-}
-
-void qmp_device_del(const char *id, Error **errp)
-{
- DeviceState *dev = find_device_state(id, errp);
- if (dev != NULL) {
- if (dev->pending_deleted_event) {
- error_setg(errp, "Device %s is already in the "
- "process of unplug", id);
- return;
- }
-
- qdev_unplug(dev, errp);
- }
-}
-
-void hmp_device_add(Monitor *mon, const QDict *qdict)
-{
- Error *err = NULL;
-
- qmp_device_add((QDict *)qdict, NULL, &err);
- hmp_handle_error(mon, err);
-}
-
-void hmp_device_del(Monitor *mon, const QDict *qdict)
-{
- const char *id = qdict_get_str(qdict, "id");
- Error *err = NULL;
-
- qmp_device_del(id, &err);
- hmp_handle_error(mon, err);
-}
-
-BlockBackend *blk_by_qdev_id(const char *id, Error **errp)
-{
- DeviceState *dev;
- BlockBackend *blk;
-
- dev = find_device_state(id, errp);
- if (dev == NULL) {
- return NULL;
- }
-
- blk = blk_by_dev(dev);
- if (!blk) {
- error_setg(errp, "Device does not have a block device backend");
- }
- return blk;
-}
-
-void qdev_machine_init(void)
-{
- qdev_get_peripheral_anon();
- qdev_get_peripheral();
-}
-
-QemuOptsList qemu_device_opts = {
- .name = "device",
- .implied_opt_name = "driver",
- .head = QTAILQ_HEAD_INITIALIZER(qemu_device_opts.head),
- .desc = {
- /*
- * no elements => accept any
- * sanity checking will happen later
- * when setting device properties
- */
- { /* end of list */ }
- },
-};
-
-QemuOptsList qemu_global_opts = {
- .name = "global",
- .head = QTAILQ_HEAD_INITIALIZER(qemu_global_opts.head),
- .desc = {
- {
- .name = "driver",
- .type = QEMU_OPT_STRING,
- },{
- .name = "property",
- .type = QEMU_OPT_STRING,
- },{
- .name = "value",
- .type = QEMU_OPT_STRING,
- },
- { /* end of list */ }
- },
-};
-
-int qemu_global_option(const char *str)
-{
- char driver[64], property[64];
- QemuOpts *opts;
- int rc, offset;
-
- rc = sscanf(str, "%63[^.=].%63[^=]%n", driver, property, &offset);
- if (rc == 2 && str[offset] == '=') {
- opts = qemu_opts_create(&qemu_global_opts, NULL, 0, &error_abort);
- qemu_opt_set(opts, "driver", driver, &error_abort);
- qemu_opt_set(opts, "property", property, &error_abort);
- qemu_opt_set(opts, "value", str + offset + 1, &error_abort);
- return 0;
- }
-
- opts = qemu_opts_parse_noisily(&qemu_global_opts, str, false);
- if (!opts) {
- return -1;
- }
-
- return 0;
-}
+++ /dev/null
-/*
- * QEMU seccomp mode 2 support with libseccomp
- *
- * Copyright IBM, Corp. 2012
- *
- * Authors:
- * Eduardo Otubo <eotubo@br.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include "qemu/osdep.h"
-#include "qapi/error.h"
-#include "qemu/config-file.h"
-#include "qemu/option.h"
-#include "qemu/module.h"
-#include <sys/prctl.h>
-#include <seccomp.h>
-#include "sysemu/seccomp.h"
-#include <linux/seccomp.h>
-
-/* For some architectures (notably ARM) cacheflush is not supported until
- * libseccomp 2.2.3, but configure enforces that we are using a more recent
- * version on those hosts, so it is OK for this check to be less strict.
- */
-#if SCMP_VER_MAJOR >= 3
- #define HAVE_CACHEFLUSH
-#elif SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR >= 2
- #define HAVE_CACHEFLUSH
-#endif
-
-struct QemuSeccompSyscall {
- int32_t num;
- uint8_t set;
- uint8_t narg;
- const struct scmp_arg_cmp *arg_cmp;
-};
-
-const struct scmp_arg_cmp sched_setscheduler_arg[] = {
- /* was SCMP_A1(SCMP_CMP_NE, SCHED_IDLE), but expanded due to GCC 4.x bug */
- { .arg = 1, .op = SCMP_CMP_NE, .datum_a = SCHED_IDLE }
-};
-
-static const struct QemuSeccompSyscall blacklist[] = {
- /* default set of syscalls to blacklist */
- { SCMP_SYS(reboot), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(swapon), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(swapoff), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(syslog), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(mount), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(umount), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(kexec_load), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(afs_syscall), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(break), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(ftime), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(getpmsg), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(gtty), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(lock), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(mpx), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(prof), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(profil), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(putpmsg), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(security), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(stty), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(tuxcall), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(ulimit), QEMU_SECCOMP_SET_DEFAULT },
- { SCMP_SYS(vserver), QEMU_SECCOMP_SET_DEFAULT },
- /* obsolete */
- { SCMP_SYS(readdir), QEMU_SECCOMP_SET_OBSOLETE },
- { SCMP_SYS(_sysctl), QEMU_SECCOMP_SET_OBSOLETE },
- { SCMP_SYS(bdflush), QEMU_SECCOMP_SET_OBSOLETE },
- { SCMP_SYS(create_module), QEMU_SECCOMP_SET_OBSOLETE },
- { SCMP_SYS(get_kernel_syms), QEMU_SECCOMP_SET_OBSOLETE },
- { SCMP_SYS(query_module), QEMU_SECCOMP_SET_OBSOLETE },
- { SCMP_SYS(sgetmask), QEMU_SECCOMP_SET_OBSOLETE },
- { SCMP_SYS(ssetmask), QEMU_SECCOMP_SET_OBSOLETE },
- { SCMP_SYS(sysfs), QEMU_SECCOMP_SET_OBSOLETE },
- { SCMP_SYS(uselib), QEMU_SECCOMP_SET_OBSOLETE },
- { SCMP_SYS(ustat), QEMU_SECCOMP_SET_OBSOLETE },
- /* privileged */
- { SCMP_SYS(setuid), QEMU_SECCOMP_SET_PRIVILEGED },
- { SCMP_SYS(setgid), QEMU_SECCOMP_SET_PRIVILEGED },
- { SCMP_SYS(setpgid), QEMU_SECCOMP_SET_PRIVILEGED },
- { SCMP_SYS(setsid), QEMU_SECCOMP_SET_PRIVILEGED },
- { SCMP_SYS(setreuid), QEMU_SECCOMP_SET_PRIVILEGED },
- { SCMP_SYS(setregid), QEMU_SECCOMP_SET_PRIVILEGED },
- { SCMP_SYS(setresuid), QEMU_SECCOMP_SET_PRIVILEGED },
- { SCMP_SYS(setresgid), QEMU_SECCOMP_SET_PRIVILEGED },
- { SCMP_SYS(setfsuid), QEMU_SECCOMP_SET_PRIVILEGED },
- { SCMP_SYS(setfsgid), QEMU_SECCOMP_SET_PRIVILEGED },
- /* spawn */
- { SCMP_SYS(fork), QEMU_SECCOMP_SET_SPAWN },
- { SCMP_SYS(vfork), QEMU_SECCOMP_SET_SPAWN },
- { SCMP_SYS(execve), QEMU_SECCOMP_SET_SPAWN },
- /* resource control */
- { SCMP_SYS(getpriority), QEMU_SECCOMP_SET_RESOURCECTL },
- { SCMP_SYS(setpriority), QEMU_SECCOMP_SET_RESOURCECTL },
- { SCMP_SYS(sched_setparam), QEMU_SECCOMP_SET_RESOURCECTL },
- { SCMP_SYS(sched_getparam), QEMU_SECCOMP_SET_RESOURCECTL },
- { SCMP_SYS(sched_setscheduler), QEMU_SECCOMP_SET_RESOURCECTL,
- ARRAY_SIZE(sched_setscheduler_arg), sched_setscheduler_arg },
- { SCMP_SYS(sched_getscheduler), QEMU_SECCOMP_SET_RESOURCECTL },
- { SCMP_SYS(sched_setaffinity), QEMU_SECCOMP_SET_RESOURCECTL },
- { SCMP_SYS(sched_getaffinity), QEMU_SECCOMP_SET_RESOURCECTL },
- { SCMP_SYS(sched_get_priority_max), QEMU_SECCOMP_SET_RESOURCECTL },
- { SCMP_SYS(sched_get_priority_min), QEMU_SECCOMP_SET_RESOURCECTL },
-};
-
-static inline __attribute__((unused)) int
-qemu_seccomp(unsigned int operation, unsigned int flags, void *args)
-{
-#ifdef __NR_seccomp
- return syscall(__NR_seccomp, operation, flags, args);
-#else
- errno = ENOSYS;
- return -1;
-#endif
-}
-
-static uint32_t qemu_seccomp_get_action(int set)
-{
- switch (set) {
- case QEMU_SECCOMP_SET_DEFAULT:
- case QEMU_SECCOMP_SET_OBSOLETE:
- case QEMU_SECCOMP_SET_PRIVILEGED:
- case QEMU_SECCOMP_SET_SPAWN: {
-#if defined(SECCOMP_GET_ACTION_AVAIL) && defined(SCMP_ACT_KILL_PROCESS) && \
- defined(SECCOMP_RET_KILL_PROCESS)
- static int kill_process = -1;
- if (kill_process == -1) {
- uint32_t action = SECCOMP_RET_KILL_PROCESS;
-
- if (qemu_seccomp(SECCOMP_GET_ACTION_AVAIL, 0, &action) == 0) {
- kill_process = 1;
- } else {
- kill_process = 0;
- }
- }
- if (kill_process == 1) {
- return SCMP_ACT_KILL_PROCESS;
- }
-#endif
- return SCMP_ACT_TRAP;
- }
-
- case QEMU_SECCOMP_SET_RESOURCECTL:
- return SCMP_ACT_ERRNO(EPERM);
-
- default:
- g_assert_not_reached();
- }
-}
-
-
-static int seccomp_start(uint32_t seccomp_opts, Error **errp)
-{
- int rc = -1;
- unsigned int i = 0;
- scmp_filter_ctx ctx;
-
- ctx = seccomp_init(SCMP_ACT_ALLOW);
- if (ctx == NULL) {
- error_setg(errp, "failed to initialize seccomp context");
- goto seccomp_return;
- }
-
- rc = seccomp_attr_set(ctx, SCMP_FLTATR_CTL_TSYNC, 1);
- if (rc != 0) {
- error_setg_errno(errp, -rc,
- "failed to set seccomp thread synchronization");
- goto seccomp_return;
- }
-
- for (i = 0; i < ARRAY_SIZE(blacklist); i++) {
- uint32_t action;
- if (!(seccomp_opts & blacklist[i].set)) {
- continue;
- }
-
- action = qemu_seccomp_get_action(blacklist[i].set);
- rc = seccomp_rule_add_array(ctx, action, blacklist[i].num,
- blacklist[i].narg, blacklist[i].arg_cmp);
- if (rc < 0) {
- error_setg_errno(errp, -rc,
- "failed to add seccomp blacklist rules");
- goto seccomp_return;
- }
- }
-
- rc = seccomp_load(ctx);
- if (rc < 0) {
- error_setg_errno(errp, -rc,
- "failed to load seccomp syscall filter in kernel");
- }
-
- seccomp_return:
- seccomp_release(ctx);
- return rc < 0 ? -1 : 0;
-}
-
-#ifdef CONFIG_SECCOMP
-int parse_sandbox(void *opaque, QemuOpts *opts, Error **errp)
-{
- if (qemu_opt_get_bool(opts, "enable", false)) {
- uint32_t seccomp_opts = QEMU_SECCOMP_SET_DEFAULT
- | QEMU_SECCOMP_SET_OBSOLETE;
- const char *value = NULL;
-
- value = qemu_opt_get(opts, "obsolete");
- if (value) {
- if (g_str_equal(value, "allow")) {
- seccomp_opts &= ~QEMU_SECCOMP_SET_OBSOLETE;
- } else if (g_str_equal(value, "deny")) {
- /* this is the default option, this if is here
- * to provide a little bit of consistency for
- * the command line */
- } else {
- error_setg(errp, "invalid argument for obsolete");
- return -1;
- }
- }
-
- value = qemu_opt_get(opts, "elevateprivileges");
- if (value) {
- if (g_str_equal(value, "deny")) {
- seccomp_opts |= QEMU_SECCOMP_SET_PRIVILEGED;
- } else if (g_str_equal(value, "children")) {
- seccomp_opts |= QEMU_SECCOMP_SET_PRIVILEGED;
-
- /* calling prctl directly because we're
- * not sure if host has CAP_SYS_ADMIN set*/
- if (prctl(PR_SET_NO_NEW_PRIVS, 1)) {
- error_setg(errp, "failed to set no_new_privs aborting");
- return -1;
- }
- } else if (g_str_equal(value, "allow")) {
- /* default value */
- } else {
- error_setg(errp, "invalid argument for elevateprivileges");
- return -1;
- }
- }
-
- value = qemu_opt_get(opts, "spawn");
- if (value) {
- if (g_str_equal(value, "deny")) {
- seccomp_opts |= QEMU_SECCOMP_SET_SPAWN;
- } else if (g_str_equal(value, "allow")) {
- /* default value */
- } else {
- error_setg(errp, "invalid argument for spawn");
- return -1;
- }
- }
-
- value = qemu_opt_get(opts, "resourcecontrol");
- if (value) {
- if (g_str_equal(value, "deny")) {
- seccomp_opts |= QEMU_SECCOMP_SET_RESOURCECTL;
- } else if (g_str_equal(value, "allow")) {
- /* default value */
- } else {
- error_setg(errp, "invalid argument for resourcecontrol");
- return -1;
- }
- }
-
- if (seccomp_start(seccomp_opts, errp) < 0) {
- return -1;
- }
- }
-
- return 0;
-}
-
-static QemuOptsList qemu_sandbox_opts = {
- .name = "sandbox",
- .implied_opt_name = "enable",
- .head = QTAILQ_HEAD_INITIALIZER(qemu_sandbox_opts.head),
- .desc = {
- {
- .name = "enable",
- .type = QEMU_OPT_BOOL,
- },
- {
- .name = "obsolete",
- .type = QEMU_OPT_STRING,
- },
- {
- .name = "elevateprivileges",
- .type = QEMU_OPT_STRING,
- },
- {
- .name = "spawn",
- .type = QEMU_OPT_STRING,
- },
- {
- .name = "resourcecontrol",
- .type = QEMU_OPT_STRING,
- },
- { /* end of list */ }
- },
-};
-
-static void seccomp_register(void)
-{
- bool add = false;
-
- /* FIXME: use seccomp_api_get() >= 2 check when released */
-
-#if defined(SECCOMP_FILTER_FLAG_TSYNC)
- int check;
-
- /* check host TSYNC capability, it returns errno == ENOSYS if unavailable */
- check = qemu_seccomp(SECCOMP_SET_MODE_FILTER,
- SECCOMP_FILTER_FLAG_TSYNC, NULL);
- if (check < 0 && errno == EFAULT) {
- add = true;
- }
-#endif
-
- if (add) {
- qemu_add_opts(&qemu_sandbox_opts);
- }
-}
-opts_init(seccomp_register);
-#endif
--- /dev/null
+/*
+ * QEMU Boot Device Implement
+ *
+ * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO., LTD.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "sysemu/sysemu.h"
+#include "qapi/visitor.h"
+#include "qemu/error-report.h"
+#include "sysemu/reset.h"
+#include "hw/qdev-core.h"
+#include "hw/boards.h"
+
+typedef struct FWBootEntry FWBootEntry;
+
+struct FWBootEntry {
+ QTAILQ_ENTRY(FWBootEntry) link;
+ int32_t bootindex;
+ DeviceState *dev;
+ char *suffix;
+};
+
+static QTAILQ_HEAD(, FWBootEntry) fw_boot_order =
+ QTAILQ_HEAD_INITIALIZER(fw_boot_order);
+static QEMUBootSetHandler *boot_set_handler;
+static void *boot_set_opaque;
+
+void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque)
+{
+ boot_set_handler = func;
+ boot_set_opaque = opaque;
+}
+
+void qemu_boot_set(const char *boot_order, Error **errp)
+{
+ Error *local_err = NULL;
+
+ if (!boot_set_handler) {
+ error_setg(errp, "no function defined to set boot device list for"
+ " this architecture");
+ return;
+ }
+
+ validate_bootdevices(boot_order, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ boot_set_handler(boot_set_opaque, boot_order, errp);
+}
+
+void validate_bootdevices(const char *devices, Error **errp)
+{
+ /* We just do some generic consistency checks */
+ const char *p;
+ int bitmap = 0;
+
+ for (p = devices; *p != '\0'; p++) {
+ /* Allowed boot devices are:
+ * a-b: floppy disk drives
+ * c-f: IDE disk drives
+ * g-m: machine implementation dependent drives
+ * n-p: network devices
+ * It's up to each machine implementation to check if the given boot
+ * devices match the actual hardware implementation and firmware
+ * features.
+ */
+ if (*p < 'a' || *p > 'p') {
+ error_setg(errp, "Invalid boot device '%c'", *p);
+ return;
+ }
+ if (bitmap & (1 << (*p - 'a'))) {
+ error_setg(errp, "Boot device '%c' was given twice", *p);
+ return;
+ }
+ bitmap |= 1 << (*p - 'a');
+ }
+}
+
+void restore_boot_order(void *opaque)
+{
+ char *normal_boot_order = opaque;
+ static int first = 1;
+
+ /* Restore boot order and remove ourselves after the first boot */
+ if (first) {
+ first = 0;
+ return;
+ }
+
+ if (boot_set_handler) {
+ qemu_boot_set(normal_boot_order, &error_abort);
+ }
+
+ qemu_unregister_reset(restore_boot_order, normal_boot_order);
+ g_free(normal_boot_order);
+}
+
+void check_boot_index(int32_t bootindex, Error **errp)
+{
+ FWBootEntry *i;
+
+ if (bootindex >= 0) {
+ QTAILQ_FOREACH(i, &fw_boot_order, link) {
+ if (i->bootindex == bootindex) {
+ error_setg(errp, "The bootindex %d has already been used",
+ bootindex);
+ return;
+ }
+ }
+ }
+}
+
+void del_boot_device_path(DeviceState *dev, const char *suffix)
+{
+ FWBootEntry *i;
+
+ if (dev == NULL) {
+ return;
+ }
+
+ QTAILQ_FOREACH(i, &fw_boot_order, link) {
+ if ((!suffix || !g_strcmp0(i->suffix, suffix)) &&
+ i->dev == dev) {
+ QTAILQ_REMOVE(&fw_boot_order, i, link);
+ g_free(i->suffix);
+ g_free(i);
+
+ break;
+ }
+ }
+}
+
+void add_boot_device_path(int32_t bootindex, DeviceState *dev,
+ const char *suffix)
+{
+ FWBootEntry *node, *i;
+
+ if (bootindex < 0) {
+ del_boot_device_path(dev, suffix);
+ return;
+ }
+
+ assert(dev != NULL || suffix != NULL);
+
+ del_boot_device_path(dev, suffix);
+
+ node = g_malloc0(sizeof(FWBootEntry));
+ node->bootindex = bootindex;
+ node->suffix = g_strdup(suffix);
+ node->dev = dev;
+
+ QTAILQ_FOREACH(i, &fw_boot_order, link) {
+ if (i->bootindex == bootindex) {
+ error_report("Two devices with same boot index %d", bootindex);
+ exit(1);
+ } else if (i->bootindex < bootindex) {
+ continue;
+ }
+ QTAILQ_INSERT_BEFORE(i, node, link);
+ return;
+ }
+ QTAILQ_INSERT_TAIL(&fw_boot_order, node, link);
+}
+
+DeviceState *get_boot_device(uint32_t position)
+{
+ uint32_t counter = 0;
+ FWBootEntry *i = NULL;
+ DeviceState *res = NULL;
+
+ if (!QTAILQ_EMPTY(&fw_boot_order)) {
+ QTAILQ_FOREACH(i, &fw_boot_order, link) {
+ if (counter == position) {
+ res = i->dev;
+ break;
+ }
+ counter++;
+ }
+ }
+ return res;
+}
+
+static char *get_boot_device_path(DeviceState *dev, bool ignore_suffixes,
+ const char *suffix)
+{
+ char *devpath = NULL, *s = NULL, *d, *bootpath;
+
+ if (dev) {
+ devpath = qdev_get_fw_dev_path(dev);
+ assert(devpath);
+ }
+
+ if (!ignore_suffixes) {
+ if (dev) {
+ d = qdev_get_own_fw_dev_path_from_handler(dev->parent_bus, dev);
+ if (d) {
+ assert(!suffix);
+ s = d;
+ } else {
+ s = g_strdup(suffix);
+ }
+ } else {
+ s = g_strdup(suffix);
+ }
+ }
+
+ bootpath = g_strdup_printf("%s%s",
+ devpath ? devpath : "",
+ s ? s : "");
+ g_free(devpath);
+ g_free(s);
+
+ return bootpath;
+}
+
+/*
+ * This function returns null terminated string that consist of new line
+ * separated device paths.
+ *
+ * memory pointed by "size" is assigned total length of the array in bytes
+ *
+ */
+char *get_boot_devices_list(size_t *size)
+{
+ FWBootEntry *i;
+ size_t total = 0;
+ char *list = NULL;
+ MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
+ bool ignore_suffixes = mc->ignore_boot_device_suffixes;
+
+ QTAILQ_FOREACH(i, &fw_boot_order, link) {
+ char *bootpath;
+ size_t len;
+
+ bootpath = get_boot_device_path(i->dev, ignore_suffixes, i->suffix);
+
+ if (total) {
+ list[total-1] = '\n';
+ }
+ len = strlen(bootpath) + 1;
+ list = g_realloc(list, total + len);
+ memcpy(&list[total], bootpath, len);
+ total += len;
+ g_free(bootpath);
+ }
+
+ *size = total;
+
+ if (boot_strict && *size > 0) {
+ list[total-1] = '\n';
+ list = g_realloc(list, total + 5);
+ memcpy(&list[total], "HALT", 5);
+ *size = total + 5;
+ }
+ return list;
+}
+
+typedef struct {
+ int32_t *bootindex;
+ const char *suffix;
+ DeviceState *dev;
+} BootIndexProperty;
+
+static void device_get_bootindex(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ BootIndexProperty *prop = opaque;
+ visit_type_int32(v, name, prop->bootindex, errp);
+}
+
+static void device_set_bootindex(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ BootIndexProperty *prop = opaque;
+ int32_t boot_index;
+ Error *local_err = NULL;
+
+ if (!visit_type_int32(v, name, &boot_index, errp)) {
+ return;
+ }
+ /* check whether bootindex is present in fw_boot_order list */
+ check_boot_index(boot_index, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ /* change bootindex to a new one */
+ *prop->bootindex = boot_index;
+
+ add_boot_device_path(*prop->bootindex, prop->dev, prop->suffix);
+}
+
+static void property_release_bootindex(Object *obj, const char *name,
+ void *opaque)
+
+{
+ BootIndexProperty *prop = opaque;
+
+ del_boot_device_path(prop->dev, prop->suffix);
+ g_free(prop);
+}
+
+void device_add_bootindex_property(Object *obj, int32_t *bootindex,
+ const char *name, const char *suffix,
+ DeviceState *dev)
+{
+ BootIndexProperty *prop = g_malloc0(sizeof(*prop));
+
+ prop->bootindex = bootindex;
+ prop->suffix = suffix;
+ prop->dev = dev;
+
+ object_property_add(obj, name, "int32",
+ device_get_bootindex,
+ device_set_bootindex,
+ property_release_bootindex,
+ prop);
+
+ /* initialize devices' bootindex property to -1 */
+ object_property_set_int(obj, name, -1, NULL);
+}
+
+typedef struct FWLCHSEntry FWLCHSEntry;
+
+struct FWLCHSEntry {
+ QTAILQ_ENTRY(FWLCHSEntry) link;
+ DeviceState *dev;
+ char *suffix;
+ uint32_t lcyls;
+ uint32_t lheads;
+ uint32_t lsecs;
+};
+
+static QTAILQ_HEAD(, FWLCHSEntry) fw_lchs =
+ QTAILQ_HEAD_INITIALIZER(fw_lchs);
+
+void add_boot_device_lchs(DeviceState *dev, const char *suffix,
+ uint32_t lcyls, uint32_t lheads, uint32_t lsecs)
+{
+ FWLCHSEntry *node;
+
+ if (!lcyls && !lheads && !lsecs) {
+ return;
+ }
+
+ assert(dev != NULL || suffix != NULL);
+
+ node = g_malloc0(sizeof(FWLCHSEntry));
+ node->suffix = g_strdup(suffix);
+ node->dev = dev;
+ node->lcyls = lcyls;
+ node->lheads = lheads;
+ node->lsecs = lsecs;
+
+ QTAILQ_INSERT_TAIL(&fw_lchs, node, link);
+}
+
+void del_boot_device_lchs(DeviceState *dev, const char *suffix)
+{
+ FWLCHSEntry *i;
+
+ if (dev == NULL) {
+ return;
+ }
+
+ QTAILQ_FOREACH(i, &fw_lchs, link) {
+ if ((!suffix || !g_strcmp0(i->suffix, suffix)) &&
+ i->dev == dev) {
+ QTAILQ_REMOVE(&fw_lchs, i, link);
+ g_free(i->suffix);
+ g_free(i);
+
+ break;
+ }
+ }
+}
+
+char *get_boot_devices_lchs_list(size_t *size)
+{
+ FWLCHSEntry *i;
+ size_t total = 0;
+ char *list = NULL;
+
+ QTAILQ_FOREACH(i, &fw_lchs, link) {
+ char *bootpath;
+ char *chs_string;
+ size_t len;
+
+ bootpath = get_boot_device_path(i->dev, false, i->suffix);
+ chs_string = g_strdup_printf("%s %" PRIu32 " %" PRIu32 " %" PRIu32,
+ bootpath, i->lcyls, i->lheads, i->lsecs);
+
+ if (total) {
+ list[total - 1] = '\n';
+ }
+ len = strlen(chs_string) + 1;
+ list = g_realloc(list, total + len);
+ memcpy(&list[total], chs_string, len);
+ total += len;
+ g_free(chs_string);
+ g_free(bootpath);
+ }
+
+ *size = total;
+
+ return list;
+}
--- /dev/null
+/*
+ * Functions to help device tree manipulation using libfdt.
+ * It also provides functions to read entries from device tree proc
+ * interface.
+ *
+ * Copyright 2008 IBM Corporation.
+ * Authors: Jerone Young <jyoung5@us.ibm.com>
+ * Hollis Blanchard <hollisb@us.ibm.com>
+ *
+ * This work is licensed under the GNU GPL license version 2 or later.
+ *
+ */
+
+#include "qemu/osdep.h"
+
+#ifdef CONFIG_LINUX
+#include <dirent.h>
+#endif
+
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/option.h"
+#include "qemu/bswap.h"
+#include "sysemu/device_tree.h"
+#include "sysemu/sysemu.h"
+#include "hw/loader.h"
+#include "hw/boards.h"
+#include "qemu/config-file.h"
+
+#include <libfdt.h>
+
+#define FDT_MAX_SIZE 0x100000
+
+void *create_device_tree(int *sizep)
+{
+ void *fdt;
+ int ret;
+
+ *sizep = FDT_MAX_SIZE;
+ fdt = g_malloc0(FDT_MAX_SIZE);
+ ret = fdt_create(fdt, FDT_MAX_SIZE);
+ if (ret < 0) {
+ goto fail;
+ }
+ ret = fdt_finish_reservemap(fdt);
+ if (ret < 0) {
+ goto fail;
+ }
+ ret = fdt_begin_node(fdt, "");
+ if (ret < 0) {
+ goto fail;
+ }
+ ret = fdt_end_node(fdt);
+ if (ret < 0) {
+ goto fail;
+ }
+ ret = fdt_finish(fdt);
+ if (ret < 0) {
+ goto fail;
+ }
+ ret = fdt_open_into(fdt, fdt, *sizep);
+ if (ret) {
+ error_report("Unable to copy device tree in memory");
+ exit(1);
+ }
+
+ return fdt;
+fail:
+ error_report("%s Couldn't create dt: %s", __func__, fdt_strerror(ret));
+ exit(1);
+}
+
+void *load_device_tree(const char *filename_path, int *sizep)
+{
+ int dt_size;
+ int dt_file_load_size;
+ int ret;
+ void *fdt = NULL;
+
+ *sizep = 0;
+ dt_size = get_image_size(filename_path);
+ if (dt_size < 0) {
+ error_report("Unable to get size of device tree file '%s'",
+ filename_path);
+ goto fail;
+ }
+ if (dt_size > INT_MAX / 2 - 10000) {
+ error_report("Device tree file '%s' is too large", filename_path);
+ goto fail;
+ }
+
+ /* Expand to 2x size to give enough room for manipulation. */
+ dt_size += 10000;
+ dt_size *= 2;
+ /* First allocate space in qemu for device tree */
+ fdt = g_malloc0(dt_size);
+
+ dt_file_load_size = load_image_size(filename_path, fdt, dt_size);
+ if (dt_file_load_size < 0) {
+ error_report("Unable to open device tree file '%s'",
+ filename_path);
+ goto fail;
+ }
+
+ ret = fdt_open_into(fdt, fdt, dt_size);
+ if (ret) {
+ error_report("Unable to copy device tree in memory");
+ goto fail;
+ }
+
+ /* Check sanity of device tree */
+ if (fdt_check_header(fdt)) {
+ error_report("Device tree file loaded into memory is invalid: %s",
+ filename_path);
+ goto fail;
+ }
+ *sizep = dt_size;
+ return fdt;
+
+fail:
+ g_free(fdt);
+ return NULL;
+}
+
+#ifdef CONFIG_LINUX
+
+#define SYSFS_DT_BASEDIR "/proc/device-tree"
+
+/**
+ * read_fstree: this function is inspired from dtc read_fstree
+ * @fdt: preallocated fdt blob buffer, to be populated
+ * @dirname: directory to scan under SYSFS_DT_BASEDIR
+ * the search is recursive and the tree is searched down to the
+ * leaves (property files).
+ *
+ * the function asserts in case of error
+ */
+static void read_fstree(void *fdt, const char *dirname)
+{
+ DIR *d;
+ struct dirent *de;
+ struct stat st;
+ const char *root_dir = SYSFS_DT_BASEDIR;
+ const char *parent_node;
+
+ if (strstr(dirname, root_dir) != dirname) {
+ error_report("%s: %s must be searched within %s",
+ __func__, dirname, root_dir);
+ exit(1);
+ }
+ parent_node = &dirname[strlen(SYSFS_DT_BASEDIR)];
+
+ d = opendir(dirname);
+ if (!d) {
+ error_report("%s cannot open %s", __func__, dirname);
+ exit(1);
+ }
+
+ while ((de = readdir(d)) != NULL) {
+ char *tmpnam;
+
+ if (!g_strcmp0(de->d_name, ".")
+ || !g_strcmp0(de->d_name, "..")) {
+ continue;
+ }
+
+ tmpnam = g_strdup_printf("%s/%s", dirname, de->d_name);
+
+ if (lstat(tmpnam, &st) < 0) {
+ error_report("%s cannot lstat %s", __func__, tmpnam);
+ exit(1);
+ }
+
+ if (S_ISREG(st.st_mode)) {
+ gchar *val;
+ gsize len;
+
+ if (!g_file_get_contents(tmpnam, &val, &len, NULL)) {
+ error_report("%s not able to extract info from %s",
+ __func__, tmpnam);
+ exit(1);
+ }
+
+ if (strlen(parent_node) > 0) {
+ qemu_fdt_setprop(fdt, parent_node,
+ de->d_name, val, len);
+ } else {
+ qemu_fdt_setprop(fdt, "/", de->d_name, val, len);
+ }
+ g_free(val);
+ } else if (S_ISDIR(st.st_mode)) {
+ char *node_name;
+
+ node_name = g_strdup_printf("%s/%s",
+ parent_node, de->d_name);
+ qemu_fdt_add_subnode(fdt, node_name);
+ g_free(node_name);
+ read_fstree(fdt, tmpnam);
+ }
+
+ g_free(tmpnam);
+ }
+
+ closedir(d);
+}
+
+/* load_device_tree_from_sysfs: extract the dt blob from host sysfs */
+void *load_device_tree_from_sysfs(void)
+{
+ void *host_fdt;
+ int host_fdt_size;
+
+ host_fdt = create_device_tree(&host_fdt_size);
+ read_fstree(host_fdt, SYSFS_DT_BASEDIR);
+ if (fdt_check_header(host_fdt)) {
+ error_report("%s host device tree extracted into memory is invalid",
+ __func__);
+ exit(1);
+ }
+ return host_fdt;
+}
+
+#endif /* CONFIG_LINUX */
+
+static int findnode_nofail(void *fdt, const char *node_path)
+{
+ int offset;
+
+ offset = fdt_path_offset(fdt, node_path);
+ if (offset < 0) {
+ error_report("%s Couldn't find node %s: %s", __func__, node_path,
+ fdt_strerror(offset));
+ exit(1);
+ }
+
+ return offset;
+}
+
+char **qemu_fdt_node_unit_path(void *fdt, const char *name, Error **errp)
+{
+ char *prefix = g_strdup_printf("%s@", name);
+ unsigned int path_len = 16, n = 0;
+ GSList *path_list = NULL, *iter;
+ const char *iter_name;
+ int offset, len, ret;
+ char **path_array;
+
+ offset = fdt_next_node(fdt, -1, NULL);
+
+ while (offset >= 0) {
+ iter_name = fdt_get_name(fdt, offset, &len);
+ if (!iter_name) {
+ offset = len;
+ break;
+ }
+ if (!strcmp(iter_name, name) || g_str_has_prefix(iter_name, prefix)) {
+ char *path;
+
+ path = g_malloc(path_len);
+ while ((ret = fdt_get_path(fdt, offset, path, path_len))
+ == -FDT_ERR_NOSPACE) {
+ path_len += 16;
+ path = g_realloc(path, path_len);
+ }
+ path_list = g_slist_prepend(path_list, path);
+ n++;
+ }
+ offset = fdt_next_node(fdt, offset, NULL);
+ }
+ g_free(prefix);
+
+ if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {
+ error_setg(errp, "%s: abort parsing dt for %s node units: %s",
+ __func__, name, fdt_strerror(offset));
+ for (iter = path_list; iter; iter = iter->next) {
+ g_free(iter->data);
+ }
+ g_slist_free(path_list);
+ return NULL;
+ }
+
+ path_array = g_new(char *, n + 1);
+ path_array[n--] = NULL;
+
+ for (iter = path_list; iter; iter = iter->next) {
+ path_array[n--] = iter->data;
+ }
+
+ g_slist_free(path_list);
+
+ return path_array;
+}
+
+char **qemu_fdt_node_path(void *fdt, const char *name, const char *compat,
+ Error **errp)
+{
+ int offset, len, ret;
+ const char *iter_name;
+ unsigned int path_len = 16, n = 0;
+ GSList *path_list = NULL, *iter;
+ char **path_array;
+
+ offset = fdt_node_offset_by_compatible(fdt, -1, compat);
+
+ while (offset >= 0) {
+ iter_name = fdt_get_name(fdt, offset, &len);
+ if (!iter_name) {
+ offset = len;
+ break;
+ }
+ if (!name || !strcmp(iter_name, name)) {
+ char *path;
+
+ path = g_malloc(path_len);
+ while ((ret = fdt_get_path(fdt, offset, path, path_len))
+ == -FDT_ERR_NOSPACE) {
+ path_len += 16;
+ path = g_realloc(path, path_len);
+ }
+ path_list = g_slist_prepend(path_list, path);
+ n++;
+ }
+ offset = fdt_node_offset_by_compatible(fdt, offset, compat);
+ }
+
+ if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {
+ error_setg(errp, "%s: abort parsing dt for %s/%s: %s",
+ __func__, name, compat, fdt_strerror(offset));
+ for (iter = path_list; iter; iter = iter->next) {
+ g_free(iter->data);
+ }
+ g_slist_free(path_list);
+ return NULL;
+ }
+
+ path_array = g_new(char *, n + 1);
+ path_array[n--] = NULL;
+
+ for (iter = path_list; iter; iter = iter->next) {
+ path_array[n--] = iter->data;
+ }
+
+ g_slist_free(path_list);
+
+ return path_array;
+}
+
+int qemu_fdt_setprop(void *fdt, const char *node_path,
+ const char *property, const void *val, int size)
+{
+ int r;
+
+ r = fdt_setprop(fdt, findnode_nofail(fdt, node_path), property, val, size);
+ if (r < 0) {
+ error_report("%s: Couldn't set %s/%s: %s", __func__, node_path,
+ property, fdt_strerror(r));
+ exit(1);
+ }
+
+ return r;
+}
+
+int qemu_fdt_setprop_cell(void *fdt, const char *node_path,
+ const char *property, uint32_t val)
+{
+ int r;
+
+ r = fdt_setprop_cell(fdt, findnode_nofail(fdt, node_path), property, val);
+ if (r < 0) {
+ error_report("%s: Couldn't set %s/%s = %#08x: %s", __func__,
+ node_path, property, val, fdt_strerror(r));
+ exit(1);
+ }
+
+ return r;
+}
+
+int qemu_fdt_setprop_u64(void *fdt, const char *node_path,
+ const char *property, uint64_t val)
+{
+ val = cpu_to_be64(val);
+ return qemu_fdt_setprop(fdt, node_path, property, &val, sizeof(val));
+}
+
+int qemu_fdt_setprop_string(void *fdt, const char *node_path,
+ const char *property, const char *string)
+{
+ int r;
+
+ r = fdt_setprop_string(fdt, findnode_nofail(fdt, node_path), property, string);
+ if (r < 0) {
+ error_report("%s: Couldn't set %s/%s = %s: %s", __func__,
+ node_path, property, string, fdt_strerror(r));
+ exit(1);
+ }
+
+ return r;
+}
+
+const void *qemu_fdt_getprop(void *fdt, const char *node_path,
+ const char *property, int *lenp, Error **errp)
+{
+ int len;
+ const void *r;
+
+ if (!lenp) {
+ lenp = &len;
+ }
+ r = fdt_getprop(fdt, findnode_nofail(fdt, node_path), property, lenp);
+ if (!r) {
+ error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__,
+ node_path, property, fdt_strerror(*lenp));
+ }
+ return r;
+}
+
+uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
+ const char *property, int *lenp, Error **errp)
+{
+ int len;
+ const uint32_t *p;
+
+ if (!lenp) {
+ lenp = &len;
+ }
+ p = qemu_fdt_getprop(fdt, node_path, property, lenp, errp);
+ if (!p) {
+ return 0;
+ } else if (*lenp != 4) {
+ error_setg(errp, "%s: %s/%s not 4 bytes long (not a cell?)",
+ __func__, node_path, property);
+ *lenp = -EINVAL;
+ return 0;
+ }
+ return be32_to_cpu(*p);
+}
+
+uint32_t qemu_fdt_get_phandle(void *fdt, const char *path)
+{
+ uint32_t r;
+
+ r = fdt_get_phandle(fdt, findnode_nofail(fdt, path));
+ if (r == 0) {
+ error_report("%s: Couldn't get phandle for %s: %s", __func__,
+ path, fdt_strerror(r));
+ exit(1);
+ }
+
+ return r;
+}
+
+int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
+ const char *property,
+ const char *target_node_path)
+{
+ uint32_t phandle = qemu_fdt_get_phandle(fdt, target_node_path);
+ return qemu_fdt_setprop_cell(fdt, node_path, property, phandle);
+}
+
+uint32_t qemu_fdt_alloc_phandle(void *fdt)
+{
+ static int phandle = 0x0;
+
+ /*
+ * We need to find out if the user gave us special instruction at
+ * which phandle id to start allocating phandles.
+ */
+ if (!phandle) {
+ phandle = machine_phandle_start(current_machine);
+ }
+
+ if (!phandle) {
+ /*
+ * None or invalid phandle given on the command line, so fall back to
+ * default starting point.
+ */
+ phandle = 0x8000;
+ }
+
+ return phandle++;
+}
+
+int qemu_fdt_nop_node(void *fdt, const char *node_path)
+{
+ int r;
+
+ r = fdt_nop_node(fdt, findnode_nofail(fdt, node_path));
+ if (r < 0) {
+ error_report("%s: Couldn't nop node %s: %s", __func__, node_path,
+ fdt_strerror(r));
+ exit(1);
+ }
+
+ return r;
+}
+
+int qemu_fdt_add_subnode(void *fdt, const char *name)
+{
+ char *dupname = g_strdup(name);
+ char *basename = strrchr(dupname, '/');
+ int retval;
+ int parent = 0;
+
+ if (!basename) {
+ g_free(dupname);
+ return -1;
+ }
+
+ basename[0] = '\0';
+ basename++;
+
+ if (dupname[0]) {
+ parent = findnode_nofail(fdt, dupname);
+ }
+
+ retval = fdt_add_subnode(fdt, parent, basename);
+ if (retval < 0) {
+ error_report("FDT: Failed to create subnode %s: %s", name,
+ fdt_strerror(retval));
+ exit(1);
+ }
+
+ g_free(dupname);
+ return retval;
+}
+
+void qemu_fdt_dumpdtb(void *fdt, int size)
+{
+ const char *dumpdtb = qemu_opt_get(qemu_get_machine_opts(), "dumpdtb");
+
+ if (dumpdtb) {
+ /* Dump the dtb to a file and quit */
+ if (g_file_set_contents(dumpdtb, fdt, size, NULL)) {
+ info_report("dtb dumped to %s. Exiting.", dumpdtb);
+ exit(0);
+ }
+ error_report("%s: Failed dumping dtb to %s", __func__, dumpdtb);
+ exit(1);
+ }
+}
+
+int qemu_fdt_setprop_sized_cells_from_array(void *fdt,
+ const char *node_path,
+ const char *property,
+ int numvalues,
+ uint64_t *values)
+{
+ uint32_t *propcells;
+ uint64_t value;
+ int cellnum, vnum, ncells;
+ uint32_t hival;
+ int ret;
+
+ propcells = g_new0(uint32_t, numvalues * 2);
+
+ cellnum = 0;
+ for (vnum = 0; vnum < numvalues; vnum++) {
+ ncells = values[vnum * 2];
+ if (ncells != 1 && ncells != 2) {
+ ret = -1;
+ goto out;
+ }
+ value = values[vnum * 2 + 1];
+ hival = cpu_to_be32(value >> 32);
+ if (ncells > 1) {
+ propcells[cellnum++] = hival;
+ } else if (hival != 0) {
+ ret = -1;
+ goto out;
+ }
+ propcells[cellnum++] = cpu_to_be32(value);
+ }
+
+ ret = qemu_fdt_setprop(fdt, node_path, property, propcells,
+ cellnum * sizeof(uint32_t));
+out:
+ g_free(propcells);
+ return ret;
+}
--- /dev/null
+/*
+ * DMA helper functions
+ *
+ * Copyright (c) 2009 Red Hat
+ *
+ * This work is licensed under the terms of the GNU General Public License
+ * (GNU GPL), version 2 or later.
+ */
+
+#include "qemu/osdep.h"
+#include "sysemu/block-backend.h"
+#include "sysemu/dma.h"
+#include "trace/trace-root.h"
+#include "qemu/thread.h"
+#include "qemu/main-loop.h"
+#include "sysemu/cpu-timers.h"
+#include "qemu/range.h"
+
+/* #define DEBUG_IOMMU */
+
+int dma_memory_set(AddressSpace *as, dma_addr_t addr, uint8_t c, dma_addr_t len)
+{
+ dma_barrier(as, DMA_DIRECTION_FROM_DEVICE);
+
+#define FILLBUF_SIZE 512
+ uint8_t fillbuf[FILLBUF_SIZE];
+ int l;
+ bool error = false;
+
+ memset(fillbuf, c, FILLBUF_SIZE);
+ while (len > 0) {
+ l = len < FILLBUF_SIZE ? len : FILLBUF_SIZE;
+ error |= address_space_write(as, addr, MEMTXATTRS_UNSPECIFIED,
+ fillbuf, l);
+ len -= l;
+ addr += l;
+ }
+
+ return error;
+}
+
+void qemu_sglist_init(QEMUSGList *qsg, DeviceState *dev, int alloc_hint,
+ AddressSpace *as)
+{
+ qsg->sg = g_malloc(alloc_hint * sizeof(ScatterGatherEntry));
+ qsg->nsg = 0;
+ qsg->nalloc = alloc_hint;
+ qsg->size = 0;
+ qsg->as = as;
+ qsg->dev = dev;
+ object_ref(OBJECT(dev));
+}
+
+void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len)
+{
+ if (qsg->nsg == qsg->nalloc) {
+ qsg->nalloc = 2 * qsg->nalloc + 1;
+ qsg->sg = g_realloc(qsg->sg, qsg->nalloc * sizeof(ScatterGatherEntry));
+ }
+ qsg->sg[qsg->nsg].base = base;
+ qsg->sg[qsg->nsg].len = len;
+ qsg->size += len;
+ ++qsg->nsg;
+}
+
+void qemu_sglist_destroy(QEMUSGList *qsg)
+{
+ object_unref(OBJECT(qsg->dev));
+ g_free(qsg->sg);
+ memset(qsg, 0, sizeof(*qsg));
+}
+
+typedef struct {
+ BlockAIOCB common;
+ AioContext *ctx;
+ BlockAIOCB *acb;
+ QEMUSGList *sg;
+ uint32_t align;
+ uint64_t offset;
+ DMADirection dir;
+ int sg_cur_index;
+ dma_addr_t sg_cur_byte;
+ QEMUIOVector iov;
+ QEMUBH *bh;
+ DMAIOFunc *io_func;
+ void *io_func_opaque;
+} DMAAIOCB;
+
+static void dma_blk_cb(void *opaque, int ret);
+
+static void reschedule_dma(void *opaque)
+{
+ DMAAIOCB *dbs = (DMAAIOCB *)opaque;
+
+ assert(!dbs->acb && dbs->bh);
+ qemu_bh_delete(dbs->bh);
+ dbs->bh = NULL;
+ dma_blk_cb(dbs, 0);
+}
+
+static void dma_blk_unmap(DMAAIOCB *dbs)
+{
+ int i;
+
+ for (i = 0; i < dbs->iov.niov; ++i) {
+ dma_memory_unmap(dbs->sg->as, dbs->iov.iov[i].iov_base,
+ dbs->iov.iov[i].iov_len, dbs->dir,
+ dbs->iov.iov[i].iov_len);
+ }
+ qemu_iovec_reset(&dbs->iov);
+}
+
+static void dma_complete(DMAAIOCB *dbs, int ret)
+{
+ trace_dma_complete(dbs, ret, dbs->common.cb);
+
+ assert(!dbs->acb && !dbs->bh);
+ dma_blk_unmap(dbs);
+ if (dbs->common.cb) {
+ dbs->common.cb(dbs->common.opaque, ret);
+ }
+ qemu_iovec_destroy(&dbs->iov);
+ qemu_aio_unref(dbs);
+}
+
+static void dma_blk_cb(void *opaque, int ret)
+{
+ DMAAIOCB *dbs = (DMAAIOCB *)opaque;
+ dma_addr_t cur_addr, cur_len;
+ void *mem;
+
+ trace_dma_blk_cb(dbs, ret);
+
+ dbs->acb = NULL;
+ dbs->offset += dbs->iov.size;
+
+ if (dbs->sg_cur_index == dbs->sg->nsg || ret < 0) {
+ dma_complete(dbs, ret);
+ return;
+ }
+ dma_blk_unmap(dbs);
+
+ while (dbs->sg_cur_index < dbs->sg->nsg) {
+ cur_addr = dbs->sg->sg[dbs->sg_cur_index].base + dbs->sg_cur_byte;
+ cur_len = dbs->sg->sg[dbs->sg_cur_index].len - dbs->sg_cur_byte;
+ mem = dma_memory_map(dbs->sg->as, cur_addr, &cur_len, dbs->dir);
+ /*
+ * Make reads deterministic in icount mode. Windows sometimes issues
+ * disk read requests with overlapping SGs. It leads
+ * to non-determinism, because resulting buffer contents may be mixed
+ * from several sectors. This code splits all SGs into several
+ * groups. SGs in every group do not overlap.
+ */
+ if (mem && icount_enabled() && dbs->dir == DMA_DIRECTION_FROM_DEVICE) {
+ int i;
+ for (i = 0 ; i < dbs->iov.niov ; ++i) {
+ if (ranges_overlap((intptr_t)dbs->iov.iov[i].iov_base,
+ dbs->iov.iov[i].iov_len, (intptr_t)mem,
+ cur_len)) {
+ dma_memory_unmap(dbs->sg->as, mem, cur_len,
+ dbs->dir, cur_len);
+ mem = NULL;
+ break;
+ }
+ }
+ }
+ if (!mem)
+ break;
+ qemu_iovec_add(&dbs->iov, mem, cur_len);
+ dbs->sg_cur_byte += cur_len;
+ if (dbs->sg_cur_byte == dbs->sg->sg[dbs->sg_cur_index].len) {
+ dbs->sg_cur_byte = 0;
+ ++dbs->sg_cur_index;
+ }
+ }
+
+ if (dbs->iov.size == 0) {
+ trace_dma_map_wait(dbs);
+ dbs->bh = aio_bh_new(dbs->ctx, reschedule_dma, dbs);
+ cpu_register_map_client(dbs->bh);
+ return;
+ }
+
+ if (!QEMU_IS_ALIGNED(dbs->iov.size, dbs->align)) {
+ qemu_iovec_discard_back(&dbs->iov,
+ QEMU_ALIGN_DOWN(dbs->iov.size, dbs->align));
+ }
+
+ aio_context_acquire(dbs->ctx);
+ dbs->acb = dbs->io_func(dbs->offset, &dbs->iov,
+ dma_blk_cb, dbs, dbs->io_func_opaque);
+ aio_context_release(dbs->ctx);
+ assert(dbs->acb);
+}
+
+static void dma_aio_cancel(BlockAIOCB *acb)
+{
+ DMAAIOCB *dbs = container_of(acb, DMAAIOCB, common);
+
+ trace_dma_aio_cancel(dbs);
+
+ assert(!(dbs->acb && dbs->bh));
+ if (dbs->acb) {
+ /* This will invoke dma_blk_cb. */
+ blk_aio_cancel_async(dbs->acb);
+ return;
+ }
+
+ if (dbs->bh) {
+ cpu_unregister_map_client(dbs->bh);
+ qemu_bh_delete(dbs->bh);
+ dbs->bh = NULL;
+ }
+ if (dbs->common.cb) {
+ dbs->common.cb(dbs->common.opaque, -ECANCELED);
+ }
+}
+
+static AioContext *dma_get_aio_context(BlockAIOCB *acb)
+{
+ DMAAIOCB *dbs = container_of(acb, DMAAIOCB, common);
+
+ return dbs->ctx;
+}
+
+static const AIOCBInfo dma_aiocb_info = {
+ .aiocb_size = sizeof(DMAAIOCB),
+ .cancel_async = dma_aio_cancel,
+ .get_aio_context = dma_get_aio_context,
+};
+
+BlockAIOCB *dma_blk_io(AioContext *ctx,
+ QEMUSGList *sg, uint64_t offset, uint32_t align,
+ DMAIOFunc *io_func, void *io_func_opaque,
+ BlockCompletionFunc *cb,
+ void *opaque, DMADirection dir)
+{
+ DMAAIOCB *dbs = qemu_aio_get(&dma_aiocb_info, NULL, cb, opaque);
+
+ trace_dma_blk_io(dbs, io_func_opaque, offset, (dir == DMA_DIRECTION_TO_DEVICE));
+
+ dbs->acb = NULL;
+ dbs->sg = sg;
+ dbs->ctx = ctx;
+ dbs->offset = offset;
+ dbs->align = align;
+ dbs->sg_cur_index = 0;
+ dbs->sg_cur_byte = 0;
+ dbs->dir = dir;
+ dbs->io_func = io_func;
+ dbs->io_func_opaque = io_func_opaque;
+ dbs->bh = NULL;
+ qemu_iovec_init(&dbs->iov, sg->nsg);
+ dma_blk_cb(dbs, 0);
+ return &dbs->common;
+}
+
+
+static
+BlockAIOCB *dma_blk_read_io_func(int64_t offset, QEMUIOVector *iov,
+ BlockCompletionFunc *cb, void *cb_opaque,
+ void *opaque)
+{
+ BlockBackend *blk = opaque;
+ return blk_aio_preadv(blk, offset, iov, 0, cb, cb_opaque);
+}
+
+BlockAIOCB *dma_blk_read(BlockBackend *blk,
+ QEMUSGList *sg, uint64_t offset, uint32_t align,
+ void (*cb)(void *opaque, int ret), void *opaque)
+{
+ return dma_blk_io(blk_get_aio_context(blk), sg, offset, align,
+ dma_blk_read_io_func, blk, cb, opaque,
+ DMA_DIRECTION_FROM_DEVICE);
+}
+
+static
+BlockAIOCB *dma_blk_write_io_func(int64_t offset, QEMUIOVector *iov,
+ BlockCompletionFunc *cb, void *cb_opaque,
+ void *opaque)
+{
+ BlockBackend *blk = opaque;
+ return blk_aio_pwritev(blk, offset, iov, 0, cb, cb_opaque);
+}
+
+BlockAIOCB *dma_blk_write(BlockBackend *blk,
+ QEMUSGList *sg, uint64_t offset, uint32_t align,
+ void (*cb)(void *opaque, int ret), void *opaque)
+{
+ return dma_blk_io(blk_get_aio_context(blk), sg, offset, align,
+ dma_blk_write_io_func, blk, cb, opaque,
+ DMA_DIRECTION_TO_DEVICE);
+}
+
+
+static uint64_t dma_buf_rw(uint8_t *ptr, int32_t len, QEMUSGList *sg,
+ DMADirection dir)
+{
+ uint64_t resid;
+ int sg_cur_index;
+
+ resid = sg->size;
+ sg_cur_index = 0;
+ len = MIN(len, resid);
+ while (len > 0) {
+ ScatterGatherEntry entry = sg->sg[sg_cur_index++];
+ int32_t xfer = MIN(len, entry.len);
+ dma_memory_rw(sg->as, entry.base, ptr, xfer, dir);
+ ptr += xfer;
+ len -= xfer;
+ resid -= xfer;
+ }
+
+ return resid;
+}
+
+uint64_t dma_buf_read(uint8_t *ptr, int32_t len, QEMUSGList *sg)
+{
+ return dma_buf_rw(ptr, len, sg, DMA_DIRECTION_FROM_DEVICE);
+}
+
+uint64_t dma_buf_write(uint8_t *ptr, int32_t len, QEMUSGList *sg)
+{
+ return dma_buf_rw(ptr, len, sg, DMA_DIRECTION_TO_DEVICE);
+}
+
+void dma_acct_start(BlockBackend *blk, BlockAcctCookie *cookie,
+ QEMUSGList *sg, enum BlockAcctType type)
+{
+ block_acct_start(blk_get_stats(blk), cookie, sg->size, type);
+}
specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: [files(
'icount.c'
)])
+
+softmmu_ss.add(files(
+ 'bootdevice.c',
+ 'dma-helpers.c',
+ 'qdev-monitor.c',
+), sdl)
+
+softmmu_ss.add(when: 'CONFIG_TPM', if_true: files('tpm.c'))
+softmmu_ss.add(when: 'CONFIG_SECCOMP', if_true: [files('qemu-seccomp.c'), seccomp])
+softmmu_ss.add(when: fdt, if_true: files('device_tree.c'))
--- /dev/null
+/*
+ * Dynamic device configuration and creation.
+ *
+ * Copyright (c) 2009 CodeSourcery
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "monitor/hmp.h"
+#include "monitor/monitor.h"
+#include "monitor/qdev.h"
+#include "sysemu/arch_init.h"
+#include "qapi/error.h"
+#include "qapi/qapi-commands-qdev.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qerror.h"
+#include "qemu/config-file.h"
+#include "qemu/error-report.h"
+#include "qemu/help_option.h"
+#include "qemu/option.h"
+#include "qemu/qemu-print.h"
+#include "qemu/option_int.h"
+#include "sysemu/block-backend.h"
+#include "sysemu/sysemu.h"
+#include "migration/misc.h"
+#include "migration/migration.h"
+#include "qemu/cutils.h"
+#include "hw/clock.h"
+
+/*
+ * Aliases were a bad idea from the start. Let's keep them
+ * from spreading further.
+ */
+typedef struct QDevAlias
+{
+ const char *typename;
+ const char *alias;
+ uint32_t arch_mask;
+} QDevAlias;
+
+/* Please keep this table sorted by typename. */
+static const QDevAlias qdev_alias_table[] = {
+ { "AC97", "ac97" }, /* -soundhw name */
+ { "e1000", "e1000-82540em" },
+ { "ES1370", "es1370" }, /* -soundhw name */
+ { "ich9-ahci", "ahci" },
+ { "lsi53c895a", "lsi" },
+ { "virtio-9p-ccw", "virtio-9p", QEMU_ARCH_S390X },
+ { "virtio-9p-pci", "virtio-9p", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
+ { "virtio-balloon-ccw", "virtio-balloon", QEMU_ARCH_S390X },
+ { "virtio-balloon-pci", "virtio-balloon",
+ QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
+ { "virtio-blk-ccw", "virtio-blk", QEMU_ARCH_S390X },
+ { "virtio-blk-pci", "virtio-blk", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
+ { "virtio-gpu-ccw", "virtio-gpu", QEMU_ARCH_S390X },
+ { "virtio-gpu-pci", "virtio-gpu", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
+ { "virtio-input-host-ccw", "virtio-input-host", QEMU_ARCH_S390X },
+ { "virtio-input-host-pci", "virtio-input-host",
+ QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
+ { "virtio-iommu-pci", "virtio-iommu", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
+ { "virtio-keyboard-ccw", "virtio-keyboard", QEMU_ARCH_S390X },
+ { "virtio-keyboard-pci", "virtio-keyboard",
+ QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
+ { "virtio-mouse-ccw", "virtio-mouse", QEMU_ARCH_S390X },
+ { "virtio-mouse-pci", "virtio-mouse", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
+ { "virtio-net-ccw", "virtio-net", QEMU_ARCH_S390X },
+ { "virtio-net-pci", "virtio-net", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
+ { "virtio-rng-ccw", "virtio-rng", QEMU_ARCH_S390X },
+ { "virtio-rng-pci", "virtio-rng", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
+ { "virtio-scsi-ccw", "virtio-scsi", QEMU_ARCH_S390X },
+ { "virtio-scsi-pci", "virtio-scsi", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
+ { "virtio-serial-ccw", "virtio-serial", QEMU_ARCH_S390X },
+ { "virtio-serial-pci", "virtio-serial", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
+ { "virtio-tablet-ccw", "virtio-tablet", QEMU_ARCH_S390X },
+ { "virtio-tablet-pci", "virtio-tablet", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
+ { }
+};
+
+static const char *qdev_class_get_alias(DeviceClass *dc)
+{
+ const char *typename = object_class_get_name(OBJECT_CLASS(dc));
+ int i;
+
+ for (i = 0; qdev_alias_table[i].typename; i++) {
+ if (qdev_alias_table[i].arch_mask &&
+ !(qdev_alias_table[i].arch_mask & arch_type)) {
+ continue;
+ }
+
+ if (strcmp(qdev_alias_table[i].typename, typename) == 0) {
+ return qdev_alias_table[i].alias;
+ }
+ }
+
+ return NULL;
+}
+
+static bool qdev_class_has_alias(DeviceClass *dc)
+{
+ return (qdev_class_get_alias(dc) != NULL);
+}
+
+static void qdev_print_devinfo(DeviceClass *dc)
+{
+ qemu_printf("name \"%s\"", object_class_get_name(OBJECT_CLASS(dc)));
+ if (dc->bus_type) {
+ qemu_printf(", bus %s", dc->bus_type);
+ }
+ if (qdev_class_has_alias(dc)) {
+ qemu_printf(", alias \"%s\"", qdev_class_get_alias(dc));
+ }
+ if (dc->desc) {
+ qemu_printf(", desc \"%s\"", dc->desc);
+ }
+ if (!dc->user_creatable) {
+ qemu_printf(", no-user");
+ }
+ qemu_printf("\n");
+}
+
+static void qdev_print_devinfos(bool show_no_user)
+{
+ static const char *cat_name[DEVICE_CATEGORY_MAX + 1] = {
+ [DEVICE_CATEGORY_BRIDGE] = "Controller/Bridge/Hub",
+ [DEVICE_CATEGORY_USB] = "USB",
+ [DEVICE_CATEGORY_STORAGE] = "Storage",
+ [DEVICE_CATEGORY_NETWORK] = "Network",
+ [DEVICE_CATEGORY_INPUT] = "Input",
+ [DEVICE_CATEGORY_DISPLAY] = "Display",
+ [DEVICE_CATEGORY_SOUND] = "Sound",
+ [DEVICE_CATEGORY_MISC] = "Misc",
+ [DEVICE_CATEGORY_CPU] = "CPU",
+ [DEVICE_CATEGORY_MAX] = "Uncategorized",
+ };
+ GSList *list, *elt;
+ int i;
+ bool cat_printed;
+
+ module_load_qom_all();
+ list = object_class_get_list_sorted(TYPE_DEVICE, false);
+
+ for (i = 0; i <= DEVICE_CATEGORY_MAX; i++) {
+ cat_printed = false;
+ for (elt = list; elt; elt = elt->next) {
+ DeviceClass *dc = OBJECT_CLASS_CHECK(DeviceClass, elt->data,
+ TYPE_DEVICE);
+ if ((i < DEVICE_CATEGORY_MAX
+ ? !test_bit(i, dc->categories)
+ : !bitmap_empty(dc->categories, DEVICE_CATEGORY_MAX))
+ || (!show_no_user
+ && !dc->user_creatable)) {
+ continue;
+ }
+ if (!cat_printed) {
+ qemu_printf("%s%s devices:\n", i ? "\n" : "", cat_name[i]);
+ cat_printed = true;
+ }
+ qdev_print_devinfo(dc);
+ }
+ }
+
+ g_slist_free(list);
+}
+
+static int set_property(void *opaque, const char *name, const char *value,
+ Error **errp)
+{
+ Object *obj = opaque;
+
+ if (strcmp(name, "driver") == 0)
+ return 0;
+ if (strcmp(name, "bus") == 0)
+ return 0;
+
+ if (!object_property_parse(obj, name, value, errp)) {
+ return -1;
+ }
+ return 0;
+}
+
+static const char *find_typename_by_alias(const char *alias)
+{
+ int i;
+
+ for (i = 0; qdev_alias_table[i].alias; i++) {
+ if (qdev_alias_table[i].arch_mask &&
+ !(qdev_alias_table[i].arch_mask & arch_type)) {
+ continue;
+ }
+
+ if (strcmp(qdev_alias_table[i].alias, alias) == 0) {
+ return qdev_alias_table[i].typename;
+ }
+ }
+
+ return NULL;
+}
+
+static DeviceClass *qdev_get_device_class(const char **driver, Error **errp)
+{
+ ObjectClass *oc;
+ DeviceClass *dc;
+ const char *original_name = *driver;
+
+ oc = module_object_class_by_name(*driver);
+ if (!oc) {
+ const char *typename = find_typename_by_alias(*driver);
+
+ if (typename) {
+ *driver = typename;
+ oc = module_object_class_by_name(*driver);
+ }
+ }
+
+ if (!object_class_dynamic_cast(oc, TYPE_DEVICE)) {
+ if (*driver != original_name) {
+ error_setg(errp, "'%s' (alias '%s') is not a valid device model"
+ " name", original_name, *driver);
+ } else {
+ error_setg(errp, "'%s' is not a valid device model name", *driver);
+ }
+ return NULL;
+ }
+
+ if (object_class_is_abstract(oc)) {
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
+ "non-abstract device type");
+ return NULL;
+ }
+
+ dc = DEVICE_CLASS(oc);
+ if (!dc->user_creatable ||
+ (qdev_hotplug && !dc->hotpluggable)) {
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver",
+ "pluggable device type");
+ return NULL;
+ }
+
+ return dc;
+}
+
+
+int qdev_device_help(QemuOpts *opts)
+{
+ Error *local_err = NULL;
+ const char *driver;
+ ObjectPropertyInfoList *prop_list;
+ ObjectPropertyInfoList *prop;
+ GPtrArray *array;
+ int i;
+
+ driver = qemu_opt_get(opts, "driver");
+ if (driver && is_help_option(driver)) {
+ qdev_print_devinfos(false);
+ return 1;
+ }
+
+ if (!driver || !qemu_opt_has_help_opt(opts)) {
+ return 0;
+ }
+
+ if (!object_class_by_name(driver)) {
+ const char *typename = find_typename_by_alias(driver);
+
+ if (typename) {
+ driver = typename;
+ }
+ }
+
+ prop_list = qmp_device_list_properties(driver, &local_err);
+ if (local_err) {
+ goto error;
+ }
+
+ if (prop_list) {
+ qemu_printf("%s options:\n", driver);
+ } else {
+ qemu_printf("There are no options for %s.\n", driver);
+ }
+ array = g_ptr_array_new();
+ for (prop = prop_list; prop; prop = prop->next) {
+ g_ptr_array_add(array,
+ object_property_help(prop->value->name,
+ prop->value->type,
+ prop->value->default_value,
+ prop->value->description));
+ }
+ g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0);
+ for (i = 0; i < array->len; i++) {
+ qemu_printf("%s\n", (char *)array->pdata[i]);
+ }
+ g_ptr_array_set_free_func(array, g_free);
+ g_ptr_array_free(array, true);
+ qapi_free_ObjectPropertyInfoList(prop_list);
+ return 1;
+
+error:
+ error_report_err(local_err);
+ return 1;
+}
+
+static Object *qdev_get_peripheral(void)
+{
+ static Object *dev;
+
+ if (dev == NULL) {
+ dev = container_get(qdev_get_machine(), "/peripheral");
+ }
+
+ return dev;
+}
+
+static Object *qdev_get_peripheral_anon(void)
+{
+ static Object *dev;
+
+ if (dev == NULL) {
+ dev = container_get(qdev_get_machine(), "/peripheral-anon");
+ }
+
+ return dev;
+}
+
+static void qbus_error_append_bus_list_hint(DeviceState *dev,
+ Error *const *errp)
+{
+ BusState *child;
+ const char *sep = " ";
+
+ error_append_hint(errp, "child buses at \"%s\":",
+ dev->id ? dev->id : object_get_typename(OBJECT(dev)));
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
+ error_append_hint(errp, "%s\"%s\"", sep, child->name);
+ sep = ", ";
+ }
+ error_append_hint(errp, "\n");
+}
+
+static void qbus_error_append_dev_list_hint(BusState *bus,
+ Error *const *errp)
+{
+ BusChild *kid;
+ const char *sep = " ";
+
+ error_append_hint(errp, "devices at \"%s\":", bus->name);
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ DeviceState *dev = kid->child;
+ error_append_hint(errp, "%s\"%s\"", sep,
+ object_get_typename(OBJECT(dev)));
+ if (dev->id) {
+ error_append_hint(errp, "/\"%s\"", dev->id);
+ }
+ sep = ", ";
+ }
+ error_append_hint(errp, "\n");
+}
+
+static BusState *qbus_find_bus(DeviceState *dev, char *elem)
+{
+ BusState *child;
+
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
+ if (strcmp(child->name, elem) == 0) {
+ return child;
+ }
+ }
+ return NULL;
+}
+
+static DeviceState *qbus_find_dev(BusState *bus, char *elem)
+{
+ BusChild *kid;
+
+ /*
+ * try to match in order:
+ * (1) instance id, if present
+ * (2) driver name
+ * (3) driver alias, if present
+ */
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ DeviceState *dev = kid->child;
+ if (dev->id && strcmp(dev->id, elem) == 0) {
+ return dev;
+ }
+ }
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ DeviceState *dev = kid->child;
+ if (strcmp(object_get_typename(OBJECT(dev)), elem) == 0) {
+ return dev;
+ }
+ }
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ DeviceState *dev = kid->child;
+ DeviceClass *dc = DEVICE_GET_CLASS(dev);
+
+ if (qdev_class_has_alias(dc) &&
+ strcmp(qdev_class_get_alias(dc), elem) == 0) {
+ return dev;
+ }
+ }
+ return NULL;
+}
+
+static inline bool qbus_is_full(BusState *bus)
+{
+ BusClass *bus_class = BUS_GET_CLASS(bus);
+ return bus_class->max_dev && bus->num_children >= bus_class->max_dev;
+}
+
+/*
+ * Search the tree rooted at @bus for a bus.
+ * If @name, search for a bus with that name. Note that bus names
+ * need not be unique. Yes, that's screwed up.
+ * Else search for a bus that is a subtype of @bus_typename.
+ * If more than one exists, prefer one that can take another device.
+ * Return the bus if found, else %NULL.
+ */
+static BusState *qbus_find_recursive(BusState *bus, const char *name,
+ const char *bus_typename)
+{
+ BusChild *kid;
+ BusState *pick, *child, *ret;
+ bool match;
+
+ assert(name || bus_typename);
+ if (name) {
+ match = !strcmp(bus->name, name);
+ } else {
+ match = !!object_dynamic_cast(OBJECT(bus), bus_typename);
+ }
+
+ if (match && !qbus_is_full(bus)) {
+ return bus; /* root matches and isn't full */
+ }
+
+ pick = match ? bus : NULL;
+
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ DeviceState *dev = kid->child;
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
+ ret = qbus_find_recursive(child, name, bus_typename);
+ if (ret && !qbus_is_full(ret)) {
+ return ret; /* a descendant matches and isn't full */
+ }
+ if (ret && !pick) {
+ pick = ret;
+ }
+ }
+ }
+
+ /* root or a descendant matches, but is full */
+ return pick;
+}
+
+static BusState *qbus_find(const char *path, Error **errp)
+{
+ DeviceState *dev;
+ BusState *bus;
+ char elem[128];
+ int pos, len;
+
+ /* find start element */
+ if (path[0] == '/') {
+ bus = sysbus_get_default();
+ pos = 0;
+ } else {
+ if (sscanf(path, "%127[^/]%n", elem, &len) != 1) {
+ assert(!path[0]);
+ elem[0] = len = 0;
+ }
+ bus = qbus_find_recursive(sysbus_get_default(), elem, NULL);
+ if (!bus) {
+ error_setg(errp, "Bus '%s' not found", elem);
+ return NULL;
+ }
+ pos = len;
+ }
+
+ for (;;) {
+ assert(path[pos] == '/' || !path[pos]);
+ while (path[pos] == '/') {
+ pos++;
+ }
+ if (path[pos] == '\0') {
+ break;
+ }
+
+ /* find device */
+ if (sscanf(path+pos, "%127[^/]%n", elem, &len) != 1) {
+ g_assert_not_reached();
+ elem[0] = len = 0;
+ }
+ pos += len;
+ dev = qbus_find_dev(bus, elem);
+ if (!dev) {
+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+ "Device '%s' not found", elem);
+ qbus_error_append_dev_list_hint(bus, errp);
+ return NULL;
+ }
+
+ assert(path[pos] == '/' || !path[pos]);
+ while (path[pos] == '/') {
+ pos++;
+ }
+ if (path[pos] == '\0') {
+ /* last specified element is a device. If it has exactly
+ * one child bus accept it nevertheless */
+ if (dev->num_child_bus == 1) {
+ bus = QLIST_FIRST(&dev->child_bus);
+ break;
+ }
+ if (dev->num_child_bus) {
+ error_setg(errp, "Device '%s' has multiple child buses",
+ elem);
+ qbus_error_append_bus_list_hint(dev, errp);
+ } else {
+ error_setg(errp, "Device '%s' has no child bus", elem);
+ }
+ return NULL;
+ }
+
+ /* find bus */
+ if (sscanf(path+pos, "%127[^/]%n", elem, &len) != 1) {
+ g_assert_not_reached();
+ elem[0] = len = 0;
+ }
+ pos += len;
+ bus = qbus_find_bus(dev, elem);
+ if (!bus) {
+ error_setg(errp, "Bus '%s' not found", elem);
+ qbus_error_append_bus_list_hint(dev, errp);
+ return NULL;
+ }
+ }
+
+ if (qbus_is_full(bus)) {
+ error_setg(errp, "Bus '%s' is full", path);
+ return NULL;
+ }
+ return bus;
+}
+
+void qdev_set_id(DeviceState *dev, const char *id)
+{
+ if (id) {
+ dev->id = id;
+ }
+
+ if (dev->id) {
+ object_property_add_child(qdev_get_peripheral(), dev->id,
+ OBJECT(dev));
+ } else {
+ static int anon_count;
+ gchar *name = g_strdup_printf("device[%d]", anon_count++);
+ object_property_add_child(qdev_get_peripheral_anon(), name,
+ OBJECT(dev));
+ g_free(name);
+ }
+}
+
+static int is_failover_device(void *opaque, const char *name, const char *value,
+ Error **errp)
+{
+ if (strcmp(name, "failover_pair_id") == 0) {
+ QemuOpts *opts = (QemuOpts *)opaque;
+
+ if (qdev_should_hide_device(opts)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static bool should_hide_device(QemuOpts *opts)
+{
+ if (qemu_opt_foreach(opts, is_failover_device, opts, NULL) == 0) {
+ return false;
+ }
+ return true;
+}
+
+DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
+{
+ DeviceClass *dc;
+ const char *driver, *path;
+ DeviceState *dev = NULL;
+ BusState *bus = NULL;
+ bool hide;
+
+ driver = qemu_opt_get(opts, "driver");
+ if (!driver) {
+ error_setg(errp, QERR_MISSING_PARAMETER, "driver");
+ return NULL;
+ }
+
+ /* find driver */
+ dc = qdev_get_device_class(&driver, errp);
+ if (!dc) {
+ return NULL;
+ }
+
+ /* find bus */
+ path = qemu_opt_get(opts, "bus");
+ if (path != NULL) {
+ bus = qbus_find(path, errp);
+ if (!bus) {
+ return NULL;
+ }
+ if (!object_dynamic_cast(OBJECT(bus), dc->bus_type)) {
+ error_setg(errp, "Device '%s' can't go on %s bus",
+ driver, object_get_typename(OBJECT(bus)));
+ return NULL;
+ }
+ } else if (dc->bus_type != NULL) {
+ bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type);
+ if (!bus || qbus_is_full(bus)) {
+ error_setg(errp, "No '%s' bus found for device '%s'",
+ dc->bus_type, driver);
+ return NULL;
+ }
+ }
+ hide = should_hide_device(opts);
+
+ if ((hide || qdev_hotplug) && bus && !qbus_is_hotpluggable(bus)) {
+ error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name);
+ return NULL;
+ }
+
+ if (hide) {
+ return NULL;
+ }
+
+ if (!migration_is_idle()) {
+ error_setg(errp, "device_add not allowed while migrating");
+ return NULL;
+ }
+
+ /* create device */
+ dev = qdev_new(driver);
+
+ /* Check whether the hotplug is allowed by the machine */
+ if (qdev_hotplug && !qdev_hotplug_allowed(dev, errp)) {
+ goto err_del_dev;
+ }
+
+ if (!bus && qdev_hotplug && !qdev_get_machine_hotplug_handler(dev)) {
+ /* No bus, no machine hotplug handler --> device is not hotpluggable */
+ error_setg(errp, "Device '%s' can not be hotplugged on this machine",
+ driver);
+ goto err_del_dev;
+ }
+
+ qdev_set_id(dev, qemu_opts_id(opts));
+
+ /* set properties */
+ if (qemu_opt_foreach(opts, set_property, dev, errp)) {
+ goto err_del_dev;
+ }
+
+ dev->opts = opts;
+ if (!qdev_realize(DEVICE(dev), bus, errp)) {
+ dev->opts = NULL;
+ goto err_del_dev;
+ }
+ return dev;
+
+err_del_dev:
+ if (dev) {
+ object_unparent(OBJECT(dev));
+ object_unref(OBJECT(dev));
+ }
+ return NULL;
+}
+
+
+#define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__)
+static void qbus_print(Monitor *mon, BusState *bus, int indent);
+
+static void qdev_print_props(Monitor *mon, DeviceState *dev, Property *props,
+ int indent)
+{
+ if (!props)
+ return;
+ for (; props->name; props++) {
+ char *value;
+ char *legacy_name = g_strdup_printf("legacy-%s", props->name);
+
+ if (object_property_get_type(OBJECT(dev), legacy_name, NULL)) {
+ value = object_property_get_str(OBJECT(dev), legacy_name, NULL);
+ } else {
+ value = object_property_print(OBJECT(dev), props->name, true,
+ NULL);
+ }
+ g_free(legacy_name);
+
+ if (!value) {
+ continue;
+ }
+ qdev_printf("%s = %s\n", props->name,
+ *value ? value : "<null>");
+ g_free(value);
+ }
+}
+
+static void bus_print_dev(BusState *bus, Monitor *mon, DeviceState *dev, int indent)
+{
+ BusClass *bc = BUS_GET_CLASS(bus);
+
+ if (bc->print_dev) {
+ bc->print_dev(mon, dev, indent);
+ }
+}
+
+static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
+{
+ ObjectClass *class;
+ BusState *child;
+ NamedGPIOList *ngl;
+ NamedClockList *ncl;
+
+ qdev_printf("dev: %s, id \"%s\"\n", object_get_typename(OBJECT(dev)),
+ dev->id ? dev->id : "");
+ indent += 2;
+ QLIST_FOREACH(ngl, &dev->gpios, node) {
+ if (ngl->num_in) {
+ qdev_printf("gpio-in \"%s\" %d\n", ngl->name ? ngl->name : "",
+ ngl->num_in);
+ }
+ if (ngl->num_out) {
+ qdev_printf("gpio-out \"%s\" %d\n", ngl->name ? ngl->name : "",
+ ngl->num_out);
+ }
+ }
+ QLIST_FOREACH(ncl, &dev->clocks, node) {
+ qdev_printf("clock-%s%s \"%s\" freq_hz=%e\n",
+ ncl->output ? "out" : "in",
+ ncl->alias ? " (alias)" : "",
+ ncl->name,
+ CLOCK_PERIOD_TO_HZ(1.0 * clock_get(ncl->clock)));
+ }
+ class = object_get_class(OBJECT(dev));
+ do {
+ qdev_print_props(mon, dev, DEVICE_CLASS(class)->props_, indent);
+ class = object_class_get_parent(class);
+ } while (class != object_class_by_name(TYPE_DEVICE));
+ bus_print_dev(dev->parent_bus, mon, dev, indent);
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
+ qbus_print(mon, child, indent);
+ }
+}
+
+static void qbus_print(Monitor *mon, BusState *bus, int indent)
+{
+ BusChild *kid;
+
+ qdev_printf("bus: %s\n", bus->name);
+ indent += 2;
+ qdev_printf("type %s\n", object_get_typename(OBJECT(bus)));
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ DeviceState *dev = kid->child;
+ qdev_print(mon, dev, indent);
+ }
+}
+#undef qdev_printf
+
+void hmp_info_qtree(Monitor *mon, const QDict *qdict)
+{
+ if (sysbus_get_default())
+ qbus_print(mon, sysbus_get_default(), 0);
+}
+
+void hmp_info_qdm(Monitor *mon, const QDict *qdict)
+{
+ qdev_print_devinfos(true);
+}
+
+void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp)
+{
+ QemuOpts *opts;
+ DeviceState *dev;
+
+ opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, errp);
+ if (!opts) {
+ return;
+ }
+ if (!monitor_cur_is_qmp() && qdev_device_help(opts)) {
+ qemu_opts_del(opts);
+ return;
+ }
+ dev = qdev_device_add(opts, errp);
+ if (!dev) {
+ qemu_opts_del(opts);
+ return;
+ }
+ object_unref(OBJECT(dev));
+}
+
+static DeviceState *find_device_state(const char *id, Error **errp)
+{
+ Object *obj;
+
+ if (id[0] == '/') {
+ obj = object_resolve_path(id, NULL);
+ } else {
+ char *root_path = object_get_canonical_path(qdev_get_peripheral());
+ char *path = g_strdup_printf("%s/%s", root_path, id);
+
+ g_free(root_path);
+ obj = object_resolve_path_type(path, TYPE_DEVICE, NULL);
+ g_free(path);
+ }
+
+ if (!obj) {
+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+ "Device '%s' not found", id);
+ return NULL;
+ }
+
+ if (!object_dynamic_cast(obj, TYPE_DEVICE)) {
+ error_setg(errp, "%s is not a hotpluggable device", id);
+ return NULL;
+ }
+
+ return DEVICE(obj);
+}
+
+void qdev_unplug(DeviceState *dev, Error **errp)
+{
+ DeviceClass *dc = DEVICE_GET_CLASS(dev);
+ HotplugHandler *hotplug_ctrl;
+ HotplugHandlerClass *hdc;
+ Error *local_err = NULL;
+
+ if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) {
+ error_setg(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name);
+ return;
+ }
+
+ if (!dc->hotpluggable) {
+ error_setg(errp, QERR_DEVICE_NO_HOTPLUG,
+ object_get_typename(OBJECT(dev)));
+ return;
+ }
+
+ if (!migration_is_idle() && !dev->allow_unplug_during_migration) {
+ error_setg(errp, "device_del not allowed while migrating");
+ return;
+ }
+
+ qdev_hot_removed = true;
+
+ hotplug_ctrl = qdev_get_hotplug_handler(dev);
+ /* hotpluggable device MUST have HotplugHandler, if it doesn't
+ * then something is very wrong with it */
+ g_assert(hotplug_ctrl);
+
+ /* If device supports async unplug just request it to be done,
+ * otherwise just remove it synchronously */
+ hdc = HOTPLUG_HANDLER_GET_CLASS(hotplug_ctrl);
+ if (hdc->unplug_request) {
+ hotplug_handler_unplug_request(hotplug_ctrl, dev, &local_err);
+ } else {
+ hotplug_handler_unplug(hotplug_ctrl, dev, &local_err);
+ if (!local_err) {
+ object_unparent(OBJECT(dev));
+ }
+ }
+ error_propagate(errp, local_err);
+}
+
+void qmp_device_del(const char *id, Error **errp)
+{
+ DeviceState *dev = find_device_state(id, errp);
+ if (dev != NULL) {
+ if (dev->pending_deleted_event) {
+ error_setg(errp, "Device %s is already in the "
+ "process of unplug", id);
+ return;
+ }
+
+ qdev_unplug(dev, errp);
+ }
+}
+
+void hmp_device_add(Monitor *mon, const QDict *qdict)
+{
+ Error *err = NULL;
+
+ qmp_device_add((QDict *)qdict, NULL, &err);
+ hmp_handle_error(mon, err);
+}
+
+void hmp_device_del(Monitor *mon, const QDict *qdict)
+{
+ const char *id = qdict_get_str(qdict, "id");
+ Error *err = NULL;
+
+ qmp_device_del(id, &err);
+ hmp_handle_error(mon, err);
+}
+
+BlockBackend *blk_by_qdev_id(const char *id, Error **errp)
+{
+ DeviceState *dev;
+ BlockBackend *blk;
+
+ dev = find_device_state(id, errp);
+ if (dev == NULL) {
+ return NULL;
+ }
+
+ blk = blk_by_dev(dev);
+ if (!blk) {
+ error_setg(errp, "Device does not have a block device backend");
+ }
+ return blk;
+}
+
+void qdev_machine_init(void)
+{
+ qdev_get_peripheral_anon();
+ qdev_get_peripheral();
+}
+
+QemuOptsList qemu_device_opts = {
+ .name = "device",
+ .implied_opt_name = "driver",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_device_opts.head),
+ .desc = {
+ /*
+ * no elements => accept any
+ * sanity checking will happen later
+ * when setting device properties
+ */
+ { /* end of list */ }
+ },
+};
+
+QemuOptsList qemu_global_opts = {
+ .name = "global",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_global_opts.head),
+ .desc = {
+ {
+ .name = "driver",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "property",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "value",
+ .type = QEMU_OPT_STRING,
+ },
+ { /* end of list */ }
+ },
+};
+
+int qemu_global_option(const char *str)
+{
+ char driver[64], property[64];
+ QemuOpts *opts;
+ int rc, offset;
+
+ rc = sscanf(str, "%63[^.=].%63[^=]%n", driver, property, &offset);
+ if (rc == 2 && str[offset] == '=') {
+ opts = qemu_opts_create(&qemu_global_opts, NULL, 0, &error_abort);
+ qemu_opt_set(opts, "driver", driver, &error_abort);
+ qemu_opt_set(opts, "property", property, &error_abort);
+ qemu_opt_set(opts, "value", str + offset + 1, &error_abort);
+ return 0;
+ }
+
+ opts = qemu_opts_parse_noisily(&qemu_global_opts, str, false);
+ if (!opts) {
+ return -1;
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * QEMU seccomp mode 2 support with libseccomp
+ *
+ * Copyright IBM, Corp. 2012
+ *
+ * Authors:
+ * Eduardo Otubo <eotubo@br.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/config-file.h"
+#include "qemu/option.h"
+#include "qemu/module.h"
+#include <sys/prctl.h>
+#include <seccomp.h>
+#include "sysemu/seccomp.h"
+#include <linux/seccomp.h>
+
+/* For some architectures (notably ARM) cacheflush is not supported until
+ * libseccomp 2.2.3, but configure enforces that we are using a more recent
+ * version on those hosts, so it is OK for this check to be less strict.
+ */
+#if SCMP_VER_MAJOR >= 3
+ #define HAVE_CACHEFLUSH
+#elif SCMP_VER_MAJOR == 2 && SCMP_VER_MINOR >= 2
+ #define HAVE_CACHEFLUSH
+#endif
+
+struct QemuSeccompSyscall {
+ int32_t num;
+ uint8_t set;
+ uint8_t narg;
+ const struct scmp_arg_cmp *arg_cmp;
+};
+
+const struct scmp_arg_cmp sched_setscheduler_arg[] = {
+ /* was SCMP_A1(SCMP_CMP_NE, SCHED_IDLE), but expanded due to GCC 4.x bug */
+ { .arg = 1, .op = SCMP_CMP_NE, .datum_a = SCHED_IDLE }
+};
+
+static const struct QemuSeccompSyscall blacklist[] = {
+ /* default set of syscalls to blacklist */
+ { SCMP_SYS(reboot), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(swapon), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(swapoff), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(syslog), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(mount), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(umount), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(kexec_load), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(afs_syscall), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(break), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(ftime), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(getpmsg), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(gtty), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(lock), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(mpx), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(prof), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(profil), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(putpmsg), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(security), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(stty), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(tuxcall), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(ulimit), QEMU_SECCOMP_SET_DEFAULT },
+ { SCMP_SYS(vserver), QEMU_SECCOMP_SET_DEFAULT },
+ /* obsolete */
+ { SCMP_SYS(readdir), QEMU_SECCOMP_SET_OBSOLETE },
+ { SCMP_SYS(_sysctl), QEMU_SECCOMP_SET_OBSOLETE },
+ { SCMP_SYS(bdflush), QEMU_SECCOMP_SET_OBSOLETE },
+ { SCMP_SYS(create_module), QEMU_SECCOMP_SET_OBSOLETE },
+ { SCMP_SYS(get_kernel_syms), QEMU_SECCOMP_SET_OBSOLETE },
+ { SCMP_SYS(query_module), QEMU_SECCOMP_SET_OBSOLETE },
+ { SCMP_SYS(sgetmask), QEMU_SECCOMP_SET_OBSOLETE },
+ { SCMP_SYS(ssetmask), QEMU_SECCOMP_SET_OBSOLETE },
+ { SCMP_SYS(sysfs), QEMU_SECCOMP_SET_OBSOLETE },
+ { SCMP_SYS(uselib), QEMU_SECCOMP_SET_OBSOLETE },
+ { SCMP_SYS(ustat), QEMU_SECCOMP_SET_OBSOLETE },
+ /* privileged */
+ { SCMP_SYS(setuid), QEMU_SECCOMP_SET_PRIVILEGED },
+ { SCMP_SYS(setgid), QEMU_SECCOMP_SET_PRIVILEGED },
+ { SCMP_SYS(setpgid), QEMU_SECCOMP_SET_PRIVILEGED },
+ { SCMP_SYS(setsid), QEMU_SECCOMP_SET_PRIVILEGED },
+ { SCMP_SYS(setreuid), QEMU_SECCOMP_SET_PRIVILEGED },
+ { SCMP_SYS(setregid), QEMU_SECCOMP_SET_PRIVILEGED },
+ { SCMP_SYS(setresuid), QEMU_SECCOMP_SET_PRIVILEGED },
+ { SCMP_SYS(setresgid), QEMU_SECCOMP_SET_PRIVILEGED },
+ { SCMP_SYS(setfsuid), QEMU_SECCOMP_SET_PRIVILEGED },
+ { SCMP_SYS(setfsgid), QEMU_SECCOMP_SET_PRIVILEGED },
+ /* spawn */
+ { SCMP_SYS(fork), QEMU_SECCOMP_SET_SPAWN },
+ { SCMP_SYS(vfork), QEMU_SECCOMP_SET_SPAWN },
+ { SCMP_SYS(execve), QEMU_SECCOMP_SET_SPAWN },
+ /* resource control */
+ { SCMP_SYS(getpriority), QEMU_SECCOMP_SET_RESOURCECTL },
+ { SCMP_SYS(setpriority), QEMU_SECCOMP_SET_RESOURCECTL },
+ { SCMP_SYS(sched_setparam), QEMU_SECCOMP_SET_RESOURCECTL },
+ { SCMP_SYS(sched_getparam), QEMU_SECCOMP_SET_RESOURCECTL },
+ { SCMP_SYS(sched_setscheduler), QEMU_SECCOMP_SET_RESOURCECTL,
+ ARRAY_SIZE(sched_setscheduler_arg), sched_setscheduler_arg },
+ { SCMP_SYS(sched_getscheduler), QEMU_SECCOMP_SET_RESOURCECTL },
+ { SCMP_SYS(sched_setaffinity), QEMU_SECCOMP_SET_RESOURCECTL },
+ { SCMP_SYS(sched_getaffinity), QEMU_SECCOMP_SET_RESOURCECTL },
+ { SCMP_SYS(sched_get_priority_max), QEMU_SECCOMP_SET_RESOURCECTL },
+ { SCMP_SYS(sched_get_priority_min), QEMU_SECCOMP_SET_RESOURCECTL },
+};
+
+static inline __attribute__((unused)) int
+qemu_seccomp(unsigned int operation, unsigned int flags, void *args)
+{
+#ifdef __NR_seccomp
+ return syscall(__NR_seccomp, operation, flags, args);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+static uint32_t qemu_seccomp_get_action(int set)
+{
+ switch (set) {
+ case QEMU_SECCOMP_SET_DEFAULT:
+ case QEMU_SECCOMP_SET_OBSOLETE:
+ case QEMU_SECCOMP_SET_PRIVILEGED:
+ case QEMU_SECCOMP_SET_SPAWN: {
+#if defined(SECCOMP_GET_ACTION_AVAIL) && defined(SCMP_ACT_KILL_PROCESS) && \
+ defined(SECCOMP_RET_KILL_PROCESS)
+ static int kill_process = -1;
+ if (kill_process == -1) {
+ uint32_t action = SECCOMP_RET_KILL_PROCESS;
+
+ if (qemu_seccomp(SECCOMP_GET_ACTION_AVAIL, 0, &action) == 0) {
+ kill_process = 1;
+ } else {
+ kill_process = 0;
+ }
+ }
+ if (kill_process == 1) {
+ return SCMP_ACT_KILL_PROCESS;
+ }
+#endif
+ return SCMP_ACT_TRAP;
+ }
+
+ case QEMU_SECCOMP_SET_RESOURCECTL:
+ return SCMP_ACT_ERRNO(EPERM);
+
+ default:
+ g_assert_not_reached();
+ }
+}
+
+
+static int seccomp_start(uint32_t seccomp_opts, Error **errp)
+{
+ int rc = -1;
+ unsigned int i = 0;
+ scmp_filter_ctx ctx;
+
+ ctx = seccomp_init(SCMP_ACT_ALLOW);
+ if (ctx == NULL) {
+ error_setg(errp, "failed to initialize seccomp context");
+ goto seccomp_return;
+ }
+
+ rc = seccomp_attr_set(ctx, SCMP_FLTATR_CTL_TSYNC, 1);
+ if (rc != 0) {
+ error_setg_errno(errp, -rc,
+ "failed to set seccomp thread synchronization");
+ goto seccomp_return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(blacklist); i++) {
+ uint32_t action;
+ if (!(seccomp_opts & blacklist[i].set)) {
+ continue;
+ }
+
+ action = qemu_seccomp_get_action(blacklist[i].set);
+ rc = seccomp_rule_add_array(ctx, action, blacklist[i].num,
+ blacklist[i].narg, blacklist[i].arg_cmp);
+ if (rc < 0) {
+ error_setg_errno(errp, -rc,
+ "failed to add seccomp blacklist rules");
+ goto seccomp_return;
+ }
+ }
+
+ rc = seccomp_load(ctx);
+ if (rc < 0) {
+ error_setg_errno(errp, -rc,
+ "failed to load seccomp syscall filter in kernel");
+ }
+
+ seccomp_return:
+ seccomp_release(ctx);
+ return rc < 0 ? -1 : 0;
+}
+
+#ifdef CONFIG_SECCOMP
+int parse_sandbox(void *opaque, QemuOpts *opts, Error **errp)
+{
+ if (qemu_opt_get_bool(opts, "enable", false)) {
+ uint32_t seccomp_opts = QEMU_SECCOMP_SET_DEFAULT
+ | QEMU_SECCOMP_SET_OBSOLETE;
+ const char *value = NULL;
+
+ value = qemu_opt_get(opts, "obsolete");
+ if (value) {
+ if (g_str_equal(value, "allow")) {
+ seccomp_opts &= ~QEMU_SECCOMP_SET_OBSOLETE;
+ } else if (g_str_equal(value, "deny")) {
+ /* this is the default option, this if is here
+ * to provide a little bit of consistency for
+ * the command line */
+ } else {
+ error_setg(errp, "invalid argument for obsolete");
+ return -1;
+ }
+ }
+
+ value = qemu_opt_get(opts, "elevateprivileges");
+ if (value) {
+ if (g_str_equal(value, "deny")) {
+ seccomp_opts |= QEMU_SECCOMP_SET_PRIVILEGED;
+ } else if (g_str_equal(value, "children")) {
+ seccomp_opts |= QEMU_SECCOMP_SET_PRIVILEGED;
+
+ /* calling prctl directly because we're
+ * not sure if host has CAP_SYS_ADMIN set*/
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1)) {
+ error_setg(errp, "failed to set no_new_privs aborting");
+ return -1;
+ }
+ } else if (g_str_equal(value, "allow")) {
+ /* default value */
+ } else {
+ error_setg(errp, "invalid argument for elevateprivileges");
+ return -1;
+ }
+ }
+
+ value = qemu_opt_get(opts, "spawn");
+ if (value) {
+ if (g_str_equal(value, "deny")) {
+ seccomp_opts |= QEMU_SECCOMP_SET_SPAWN;
+ } else if (g_str_equal(value, "allow")) {
+ /* default value */
+ } else {
+ error_setg(errp, "invalid argument for spawn");
+ return -1;
+ }
+ }
+
+ value = qemu_opt_get(opts, "resourcecontrol");
+ if (value) {
+ if (g_str_equal(value, "deny")) {
+ seccomp_opts |= QEMU_SECCOMP_SET_RESOURCECTL;
+ } else if (g_str_equal(value, "allow")) {
+ /* default value */
+ } else {
+ error_setg(errp, "invalid argument for resourcecontrol");
+ return -1;
+ }
+ }
+
+ if (seccomp_start(seccomp_opts, errp) < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static QemuOptsList qemu_sandbox_opts = {
+ .name = "sandbox",
+ .implied_opt_name = "enable",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_sandbox_opts.head),
+ .desc = {
+ {
+ .name = "enable",
+ .type = QEMU_OPT_BOOL,
+ },
+ {
+ .name = "obsolete",
+ .type = QEMU_OPT_STRING,
+ },
+ {
+ .name = "elevateprivileges",
+ .type = QEMU_OPT_STRING,
+ },
+ {
+ .name = "spawn",
+ .type = QEMU_OPT_STRING,
+ },
+ {
+ .name = "resourcecontrol",
+ .type = QEMU_OPT_STRING,
+ },
+ { /* end of list */ }
+ },
+};
+
+static void seccomp_register(void)
+{
+ bool add = false;
+
+ /* FIXME: use seccomp_api_get() >= 2 check when released */
+
+#if defined(SECCOMP_FILTER_FLAG_TSYNC)
+ int check;
+
+ /* check host TSYNC capability, it returns errno == ENOSYS if unavailable */
+ check = qemu_seccomp(SECCOMP_SET_MODE_FILTER,
+ SECCOMP_FILTER_FLAG_TSYNC, NULL);
+ if (check < 0 && errno == EFAULT) {
+ add = true;
+ }
+#endif
+
+ if (add) {
+ qemu_add_opts(&qemu_sandbox_opts);
+ }
+}
+opts_init(seccomp_register);
+#endif
--- /dev/null
+/*
+ * TPM configuration
+ *
+ * Copyright (C) 2011-2013 IBM Corporation
+ *
+ * Authors:
+ * Stefan Berger <stefanb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * Based on net.c
+ */
+
+#include "qemu/osdep.h"
+
+#include "qapi/error.h"
+#include "qapi/qapi-commands-tpm.h"
+#include "qapi/qmp/qerror.h"
+#include "sysemu/tpm_backend.h"
+#include "sysemu/tpm.h"
+#include "qemu/config-file.h"
+#include "qemu/error-report.h"
+
+static QLIST_HEAD(, TPMBackend) tpm_backends =
+ QLIST_HEAD_INITIALIZER(tpm_backends);
+
+static const TPMBackendClass *
+tpm_be_find_by_type(enum TpmType type)
+{
+ ObjectClass *oc;
+ char *typename = g_strdup_printf("tpm-%s", TpmType_str(type));
+
+ oc = object_class_by_name(typename);
+ g_free(typename);
+
+ if (!object_class_dynamic_cast(oc, TYPE_TPM_BACKEND)) {
+ return NULL;
+ }
+
+ return TPM_BACKEND_CLASS(oc);
+}
+
+/*
+ * Walk the list of available TPM backend drivers and display them on the
+ * screen.
+ */
+static void tpm_display_backend_drivers(void)
+{
+ bool got_one = false;
+ int i;
+
+ for (i = 0; i < TPM_TYPE__MAX; i++) {
+ const TPMBackendClass *bc = tpm_be_find_by_type(i);
+ if (!bc) {
+ continue;
+ }
+ if (!got_one) {
+ error_printf("Supported TPM types (choose only one):\n");
+ got_one = true;
+ }
+ error_printf("%12s %s\n", TpmType_str(i), bc->desc);
+ }
+ if (!got_one) {
+ error_printf("No TPM backend types are available\n");
+ }
+}
+
+/*
+ * Find the TPM with the given Id
+ */
+TPMBackend *qemu_find_tpm_be(const char *id)
+{
+ TPMBackend *drv;
+
+ if (id) {
+ QLIST_FOREACH(drv, &tpm_backends, list) {
+ if (!strcmp(drv->id, id)) {
+ return drv;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static int tpm_init_tpmdev(void *dummy, QemuOpts *opts, Error **errp)
+{
+ /*
+ * Use of error_report() in a function with an Error ** parameter
+ * is suspicious. It is okay here. The parameter only exists to
+ * make the function usable with qemu_opts_foreach(). It is not
+ * actually used.
+ */
+ const char *value;
+ const char *id;
+ const TPMBackendClass *be;
+ TPMBackend *drv;
+ Error *local_err = NULL;
+ int i;
+
+ if (!QLIST_EMPTY(&tpm_backends)) {
+ error_report("Only one TPM is allowed.");
+ return 1;
+ }
+
+ id = qemu_opts_id(opts);
+ if (id == NULL) {
+ error_report(QERR_MISSING_PARAMETER, "id");
+ return 1;
+ }
+
+ value = qemu_opt_get(opts, "type");
+ if (!value) {
+ error_report(QERR_MISSING_PARAMETER, "type");
+ tpm_display_backend_drivers();
+ return 1;
+ }
+
+ i = qapi_enum_parse(&TpmType_lookup, value, -1, NULL);
+ be = i >= 0 ? tpm_be_find_by_type(i) : NULL;
+ if (be == NULL) {
+ error_report(QERR_INVALID_PARAMETER_VALUE,
+ "type", "a TPM backend type");
+ tpm_display_backend_drivers();
+ return 1;
+ }
+
+ /* validate backend specific opts */
+ if (!qemu_opts_validate(opts, be->opts, &local_err)) {
+ error_report_err(local_err);
+ return 1;
+ }
+
+ drv = be->create(opts);
+ if (!drv) {
+ return 1;
+ }
+
+ drv->id = g_strdup(id);
+ QLIST_INSERT_HEAD(&tpm_backends, drv, list);
+
+ return 0;
+}
+
+/*
+ * Walk the list of TPM backend drivers that are in use and call their
+ * destroy function to have them cleaned up.
+ */
+void tpm_cleanup(void)
+{
+ TPMBackend *drv, *next;
+
+ QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) {
+ QLIST_REMOVE(drv, list);
+ object_unref(OBJECT(drv));
+ }
+}
+
+/*
+ * Initialize the TPM. Process the tpmdev command line options describing the
+ * TPM backend.
+ */
+int tpm_init(void)
+{
+ if (qemu_opts_foreach(qemu_find_opts("tpmdev"),
+ tpm_init_tpmdev, NULL, NULL)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Parse the TPM configuration options.
+ * To display all available TPM backends the user may use '-tpmdev help'
+ */
+int tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
+{
+ QemuOpts *opts;
+
+ if (!strcmp(optarg, "help")) {
+ tpm_display_backend_drivers();
+ return -1;
+ }
+ opts = qemu_opts_parse_noisily(opts_list, optarg, true);
+ if (!opts) {
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Walk the list of active TPM backends and collect information about them.
+ */
+TPMInfoList *qmp_query_tpm(Error **errp)
+{
+ TPMBackend *drv;
+ TPMInfoList *info, *head = NULL, *cur_item = NULL;
+
+ QLIST_FOREACH(drv, &tpm_backends, list) {
+ if (!drv->tpmif) {
+ continue;
+ }
+
+ info = g_new0(TPMInfoList, 1);
+ info->value = tpm_backend_query_tpm(drv);
+
+ if (!cur_item) {
+ head = cur_item = info;
+ } else {
+ cur_item->next = info;
+ cur_item = info;
+ }
+ }
+
+ return head;
+}
+
+TpmTypeList *qmp_query_tpm_types(Error **errp)
+{
+ unsigned int i = 0;
+ TpmTypeList *head = NULL, *prev = NULL, *cur_item;
+
+ for (i = 0; i < TPM_TYPE__MAX; i++) {
+ if (!tpm_be_find_by_type(i)) {
+ continue;
+ }
+ cur_item = g_new0(TpmTypeList, 1);
+ cur_item->value = i;
+
+ if (prev) {
+ prev->next = cur_item;
+ }
+ if (!head) {
+ head = cur_item;
+ }
+ prev = cur_item;
+ }
+
+ return head;
+}
+TpmModelList *qmp_query_tpm_models(Error **errp)
+{
+ TpmModelList *head = NULL, *prev = NULL, *cur_item;
+ GSList *e, *l = object_class_get_list(TYPE_TPM_IF, false);
+
+ for (e = l; e; e = e->next) {
+ TPMIfClass *c = TPM_IF_CLASS(e->data);
+
+ cur_item = g_new0(TpmModelList, 1);
+ cur_item->value = c->model;
+
+ if (prev) {
+ prev->next = cur_item;
+ }
+ if (!head) {
+ head = cur_item;
+ }
+ prev = cur_item;
+ }
+ g_slist_free(l);
+
+ return head;
+}
+++ /dev/null
-/*
- * TPM configuration
- *
- * Copyright (C) 2011-2013 IBM Corporation
- *
- * Authors:
- * Stefan Berger <stefanb@us.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- *
- * Based on net.c
- */
-
-#include "qemu/osdep.h"
-
-#include "qapi/error.h"
-#include "qapi/qapi-commands-tpm.h"
-#include "qapi/qmp/qerror.h"
-#include "sysemu/tpm_backend.h"
-#include "sysemu/tpm.h"
-#include "qemu/config-file.h"
-#include "qemu/error-report.h"
-
-static QLIST_HEAD(, TPMBackend) tpm_backends =
- QLIST_HEAD_INITIALIZER(tpm_backends);
-
-static const TPMBackendClass *
-tpm_be_find_by_type(enum TpmType type)
-{
- ObjectClass *oc;
- char *typename = g_strdup_printf("tpm-%s", TpmType_str(type));
-
- oc = object_class_by_name(typename);
- g_free(typename);
-
- if (!object_class_dynamic_cast(oc, TYPE_TPM_BACKEND)) {
- return NULL;
- }
-
- return TPM_BACKEND_CLASS(oc);
-}
-
-/*
- * Walk the list of available TPM backend drivers and display them on the
- * screen.
- */
-static void tpm_display_backend_drivers(void)
-{
- bool got_one = false;
- int i;
-
- for (i = 0; i < TPM_TYPE__MAX; i++) {
- const TPMBackendClass *bc = tpm_be_find_by_type(i);
- if (!bc) {
- continue;
- }
- if (!got_one) {
- error_printf("Supported TPM types (choose only one):\n");
- got_one = true;
- }
- error_printf("%12s %s\n", TpmType_str(i), bc->desc);
- }
- if (!got_one) {
- error_printf("No TPM backend types are available\n");
- }
-}
-
-/*
- * Find the TPM with the given Id
- */
-TPMBackend *qemu_find_tpm_be(const char *id)
-{
- TPMBackend *drv;
-
- if (id) {
- QLIST_FOREACH(drv, &tpm_backends, list) {
- if (!strcmp(drv->id, id)) {
- return drv;
- }
- }
- }
-
- return NULL;
-}
-
-static int tpm_init_tpmdev(void *dummy, QemuOpts *opts, Error **errp)
-{
- /*
- * Use of error_report() in a function with an Error ** parameter
- * is suspicious. It is okay here. The parameter only exists to
- * make the function usable with qemu_opts_foreach(). It is not
- * actually used.
- */
- const char *value;
- const char *id;
- const TPMBackendClass *be;
- TPMBackend *drv;
- Error *local_err = NULL;
- int i;
-
- if (!QLIST_EMPTY(&tpm_backends)) {
- error_report("Only one TPM is allowed.");
- return 1;
- }
-
- id = qemu_opts_id(opts);
- if (id == NULL) {
- error_report(QERR_MISSING_PARAMETER, "id");
- return 1;
- }
-
- value = qemu_opt_get(opts, "type");
- if (!value) {
- error_report(QERR_MISSING_PARAMETER, "type");
- tpm_display_backend_drivers();
- return 1;
- }
-
- i = qapi_enum_parse(&TpmType_lookup, value, -1, NULL);
- be = i >= 0 ? tpm_be_find_by_type(i) : NULL;
- if (be == NULL) {
- error_report(QERR_INVALID_PARAMETER_VALUE,
- "type", "a TPM backend type");
- tpm_display_backend_drivers();
- return 1;
- }
-
- /* validate backend specific opts */
- if (!qemu_opts_validate(opts, be->opts, &local_err)) {
- error_report_err(local_err);
- return 1;
- }
-
- drv = be->create(opts);
- if (!drv) {
- return 1;
- }
-
- drv->id = g_strdup(id);
- QLIST_INSERT_HEAD(&tpm_backends, drv, list);
-
- return 0;
-}
-
-/*
- * Walk the list of TPM backend drivers that are in use and call their
- * destroy function to have them cleaned up.
- */
-void tpm_cleanup(void)
-{
- TPMBackend *drv, *next;
-
- QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) {
- QLIST_REMOVE(drv, list);
- object_unref(OBJECT(drv));
- }
-}
-
-/*
- * Initialize the TPM. Process the tpmdev command line options describing the
- * TPM backend.
- */
-int tpm_init(void)
-{
- if (qemu_opts_foreach(qemu_find_opts("tpmdev"),
- tpm_init_tpmdev, NULL, NULL)) {
- return -1;
- }
-
- return 0;
-}
-
-/*
- * Parse the TPM configuration options.
- * To display all available TPM backends the user may use '-tpmdev help'
- */
-int tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
-{
- QemuOpts *opts;
-
- if (!strcmp(optarg, "help")) {
- tpm_display_backend_drivers();
- return -1;
- }
- opts = qemu_opts_parse_noisily(opts_list, optarg, true);
- if (!opts) {
- return -1;
- }
- return 0;
-}
-
-/*
- * Walk the list of active TPM backends and collect information about them.
- */
-TPMInfoList *qmp_query_tpm(Error **errp)
-{
- TPMBackend *drv;
- TPMInfoList *info, *head = NULL, *cur_item = NULL;
-
- QLIST_FOREACH(drv, &tpm_backends, list) {
- if (!drv->tpmif) {
- continue;
- }
-
- info = g_new0(TPMInfoList, 1);
- info->value = tpm_backend_query_tpm(drv);
-
- if (!cur_item) {
- head = cur_item = info;
- } else {
- cur_item->next = info;
- cur_item = info;
- }
- }
-
- return head;
-}
-
-TpmTypeList *qmp_query_tpm_types(Error **errp)
-{
- unsigned int i = 0;
- TpmTypeList *head = NULL, *prev = NULL, *cur_item;
-
- for (i = 0; i < TPM_TYPE__MAX; i++) {
- if (!tpm_be_find_by_type(i)) {
- continue;
- }
- cur_item = g_new0(TpmTypeList, 1);
- cur_item->value = i;
-
- if (prev) {
- prev->next = cur_item;
- }
- if (!head) {
- head = cur_item;
- }
- prev = cur_item;
- }
-
- return head;
-}
-TpmModelList *qmp_query_tpm_models(Error **errp)
-{
- TpmModelList *head = NULL, *prev = NULL, *cur_item;
- GSList *e, *l = object_class_get_list(TYPE_TPM_IF, false);
-
- for (e = l; e; e = e->next) {
- TPMIfClass *c = TPM_IF_CLASS(e->data);
-
- cur_item = g_new0(TpmModelList, 1);
- cur_item->value = c->model;
-
- if (prev) {
- prev->next = cur_item;
- }
- if (!head) {
- head = cur_item;
- }
- prev = cur_item;
- }
- g_slist_free(l);
-
- return head;
-}