mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'pull-vfio-20231018' of https://github.com/legoater/qemu into staging
vfio queue: * Support for VFIODisplay migration with ramfb * Preliminary work for IOMMUFD support # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmUvlEYACgkQUaNDx8/7 # 7KFlaw//X2053de2eTdo38/UMSzi5ACWWn2j1iGQZf/3+J2LcdlixZarZr/2DN56 # 4axmwF6+GKozt5+EnvWtgodDn6U9iyMNaAB3CGBHFHsH8uqKeZd/Ii754q4Rcmy9 # ZufBOPWm9Ff7s2MMFiAZvso75jP2wuwVEe1YPRjeJnsNSNIJ6WZfemh3Sl96yRBb # r38uqzqetKwl7HziMMWP3yb8v+dU8A9bqI1hf1FZGttfFz3XA+pmjXKA6XxdfiZF # AAotu5x9w86a08sAlr/qVsZFLR37oQykkXM0D840DafJDyr5fbJiq8cwfOjMw9+D # w6+udRm5KoBWPsvb/T3dR88GRMO22PChjH9Vjl51TstMNhdTxuKJTKhhSoUFZbXV # 8CMjwfALk5ggIOyCk1LRd04ed+9qkqgcbw1Guy5pYnyPnY/X6XurxxaxS6Gemgtn # UvgRYhSjio+LgHLO77IVkWJMooTEPzUTty2Zxa7ldbbE+utPUtsmac9+1m2pnpqk # 5VQmB074QnsJuvf+7HPU6vYCzQWoXHsH1UY/A0fF7MPedNUAbVYzKrdGPyqEMqHy # xbilAIaS3oO0pMT6kUpRv5c5vjbwkx94Nf/ii8fQVjWzPfCcaF3yEfaam62jMUku # stySaRpavKIx2oYLlucBqeKaBGaUofk13gGTQlsFs8pKCOAV7r4= # =s0fN # -----END PGP SIGNATURE----- # gpg: Signature made Wed 18 Oct 2023 04:16:06 EDT # gpg: using RSA key A0F66548F04895EBFE6B0B6051A343C7CFFBECA1 # gpg: Good signature from "Cédric Le Goater <clg@redhat.com>" [unknown] # gpg: aka "Cédric Le Goater <clg@kaod.org>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: A0F6 6548 F048 95EB FE6B 0B60 51A3 43C7 CFFB ECA1 * tag 'pull-vfio-20231018' of https://github.com/legoater/qemu: (22 commits) hw/vfio: add ramfb migration support ramfb-standalone: add migration support ramfb: add migration support vfio/pci: Remove vfio_detach_device from vfio_realize error path vfio/ccw: Remove redundant definition of TYPE_VFIO_CCW vfio/ap: Remove pointless apdev variable vfio/pci: Fix a potential memory leak in vfio_listener_region_add vfio/common: Move legacy VFIO backend code into separate container.c vfio/common: Introduce a global VFIODevice list vfio/common: Store the parent container in VFIODevice vfio/common: Introduce a per container device list vfio/common: Move VFIO reset handler registration to a group agnostic function vfio/ccw: Use vfio_[attach/detach]_device vfio/ap: Use vfio_[attach/detach]_device vfio/platform: Use vfio_[attach/detach]_device vfio/pci: Introduce vfio_[attach/detach]_device vfio/common: Extract out vfio_kvm_device_[add/del]_fd vfio/common: Introduce vfio_container_add|del_section_window() vfio/common: Propagate KVM_SET_DEVICE_ATTR error if any vfio/common: Move IOMMU agnostic helpers to a separate file ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
@@ -34,6 +34,8 @@
|
||||
|
||||
GlobalProperty hw_compat_8_1[] = {
|
||||
{ TYPE_PCI_BRIDGE, "x-pci-express-writeable-slt-bug", "true" },
|
||||
{ "ramfb", "x-migrate", "off" },
|
||||
{ "vfio-pci-nohotplug", "x-ramfb-migrate", "off" }
|
||||
};
|
||||
const size_t hw_compat_8_1_len = G_N_ELEMENTS(hw_compat_8_1);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/loader.h"
|
||||
@@ -15,6 +16,7 @@ struct RAMFBStandaloneState {
|
||||
SysBusDevice parent_obj;
|
||||
QemuConsole *con;
|
||||
RAMFBState *state;
|
||||
bool migrate;
|
||||
};
|
||||
|
||||
static void display_update_wrapper(void *dev)
|
||||
@@ -40,14 +42,39 @@ static void ramfb_realizefn(DeviceState *dev, Error **errp)
|
||||
ramfb->state = ramfb_setup(errp);
|
||||
}
|
||||
|
||||
static bool migrate_needed(void *opaque)
|
||||
{
|
||||
RAMFBStandaloneState *ramfb = RAMFB(opaque);
|
||||
|
||||
return ramfb->migrate;
|
||||
}
|
||||
|
||||
static const VMStateDescription ramfb_dev_vmstate = {
|
||||
.name = "ramfb-dev",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = migrate_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_STRUCT_POINTER(state, RAMFBStandaloneState, ramfb_vmstate, RAMFBState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static Property ramfb_properties[] = {
|
||||
DEFINE_PROP_BOOL("x-migrate", RAMFBStandaloneState, migrate, true),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void ramfb_class_initfn(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
|
||||
dc->vmsd = &ramfb_dev_vmstate;
|
||||
dc->realize = ramfb_realizefn;
|
||||
dc->desc = "ram framebuffer standalone device";
|
||||
dc->user_creatable = true;
|
||||
device_class_set_props(dc, ramfb_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo ramfb_info = {
|
||||
|
||||
@@ -28,6 +28,8 @@ struct QEMU_PACKED RAMFBCfg {
|
||||
uint32_t stride;
|
||||
};
|
||||
|
||||
typedef struct RAMFBCfg RAMFBCfg;
|
||||
|
||||
struct RAMFBState {
|
||||
DisplaySurface *ds;
|
||||
uint32_t width, height;
|
||||
@@ -116,6 +118,23 @@ void ramfb_display_update(QemuConsole *con, RAMFBState *s)
|
||||
dpy_gfx_update_full(con);
|
||||
}
|
||||
|
||||
static int ramfb_post_load(void *opaque, int version_id)
|
||||
{
|
||||
ramfb_fw_cfg_write(opaque, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const VMStateDescription ramfb_vmstate = {
|
||||
.name = "ramfb",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.post_load = ramfb_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_BUFFER_UNSAFE(cfg, RAMFBState, 0, sizeof(RAMFBCfg)),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
RAMFBState *ramfb_setup(Error **errp)
|
||||
{
|
||||
FWCfgState *fw_cfg = fw_cfg_find();
|
||||
|
||||
+16
-60
@@ -53,40 +53,6 @@ struct VFIODeviceOps vfio_ap_ops = {
|
||||
.vfio_compute_needs_reset = vfio_ap_compute_needs_reset,
|
||||
};
|
||||
|
||||
static void vfio_ap_put_device(VFIOAPDevice *vapdev)
|
||||
{
|
||||
g_free(vapdev->vdev.name);
|
||||
vfio_put_base_device(&vapdev->vdev);
|
||||
}
|
||||
|
||||
static VFIOGroup *vfio_ap_get_group(VFIOAPDevice *vapdev, Error **errp)
|
||||
{
|
||||
GError *gerror = NULL;
|
||||
char *symlink, *group_path;
|
||||
int groupid;
|
||||
|
||||
symlink = g_strdup_printf("%s/iommu_group", vapdev->vdev.sysfsdev);
|
||||
group_path = g_file_read_link(symlink, &gerror);
|
||||
g_free(symlink);
|
||||
|
||||
if (!group_path) {
|
||||
error_setg(errp, "%s: no iommu_group found for %s: %s",
|
||||
TYPE_VFIO_AP_DEVICE, vapdev->vdev.sysfsdev, gerror->message);
|
||||
g_error_free(gerror);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sscanf(basename(group_path), "%d", &groupid) != 1) {
|
||||
error_setg(errp, "vfio: failed to read %s", group_path);
|
||||
g_free(group_path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_free(group_path);
|
||||
|
||||
return vfio_get_group(groupid, &address_space_memory, errp);
|
||||
}
|
||||
|
||||
static void vfio_ap_req_notifier_handler(void *opaque)
|
||||
{
|
||||
VFIOAPDevice *vapdev = opaque;
|
||||
@@ -189,22 +155,14 @@ static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev,
|
||||
static void vfio_ap_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
char *mdevid;
|
||||
Error *err = NULL;
|
||||
VFIOGroup *vfio_group;
|
||||
APDevice *apdev = AP_DEVICE(dev);
|
||||
VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev);
|
||||
VFIOAPDevice *vapdev = VFIO_AP_DEVICE(dev);
|
||||
VFIODevice *vbasedev = &vapdev->vdev;
|
||||
|
||||
vfio_group = vfio_ap_get_group(vapdev, errp);
|
||||
if (!vfio_group) {
|
||||
return;
|
||||
}
|
||||
|
||||
vapdev->vdev.ops = &vfio_ap_ops;
|
||||
vapdev->vdev.type = VFIO_DEVICE_TYPE_AP;
|
||||
mdevid = basename(vapdev->vdev.sysfsdev);
|
||||
vapdev->vdev.name = g_strdup_printf("%s", mdevid);
|
||||
vapdev->vdev.dev = dev;
|
||||
vbasedev->name = g_path_get_basename(vbasedev->sysfsdev);
|
||||
vbasedev->ops = &vfio_ap_ops;
|
||||
vbasedev->type = VFIO_DEVICE_TYPE_AP;
|
||||
vbasedev->dev = dev;
|
||||
|
||||
/*
|
||||
* vfio-ap devices operate in a way compatible with discarding of
|
||||
@@ -214,9 +172,10 @@ static void vfio_ap_realize(DeviceState *dev, Error **errp)
|
||||
*/
|
||||
vapdev->vdev.ram_block_discard_allowed = true;
|
||||
|
||||
ret = vfio_get_device(vfio_group, mdevid, &vapdev->vdev, errp);
|
||||
ret = vfio_attach_device(vbasedev->name, vbasedev,
|
||||
&address_space_memory, errp);
|
||||
if (ret) {
|
||||
goto out_get_dev_err;
|
||||
goto error;
|
||||
}
|
||||
|
||||
vfio_ap_register_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX, &err);
|
||||
@@ -230,20 +189,18 @@ static void vfio_ap_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
return;
|
||||
|
||||
out_get_dev_err:
|
||||
vfio_ap_put_device(vapdev);
|
||||
vfio_put_group(vfio_group);
|
||||
error:
|
||||
error_prepend(errp, VFIO_MSG_PREFIX, vbasedev->name);
|
||||
g_free(vbasedev->name);
|
||||
}
|
||||
|
||||
static void vfio_ap_unrealize(DeviceState *dev)
|
||||
{
|
||||
APDevice *apdev = AP_DEVICE(dev);
|
||||
VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev);
|
||||
VFIOGroup *group = vapdev->vdev.group;
|
||||
VFIOAPDevice *vapdev = VFIO_AP_DEVICE(dev);
|
||||
|
||||
vfio_ap_unregister_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX);
|
||||
vfio_ap_put_device(vapdev);
|
||||
vfio_put_group(group);
|
||||
vfio_detach_device(&vapdev->vdev);
|
||||
g_free(vapdev->vdev.name);
|
||||
}
|
||||
|
||||
static Property vfio_ap_properties[] = {
|
||||
@@ -254,8 +211,7 @@ static Property vfio_ap_properties[] = {
|
||||
static void vfio_ap_reset(DeviceState *dev)
|
||||
{
|
||||
int ret;
|
||||
APDevice *apdev = AP_DEVICE(dev);
|
||||
VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev);
|
||||
VFIOAPDevice *vapdev = VFIO_AP_DEVICE(dev);
|
||||
|
||||
ret = ioctl(vapdev->vdev.fd, VFIO_DEVICE_RESET);
|
||||
if (ret) {
|
||||
|
||||
+27
-90
@@ -572,88 +572,14 @@ static void vfio_ccw_put_region(VFIOCCWDevice *vcdev)
|
||||
g_free(vcdev->io_region);
|
||||
}
|
||||
|
||||
static void vfio_ccw_put_device(VFIOCCWDevice *vcdev)
|
||||
{
|
||||
g_free(vcdev->vdev.name);
|
||||
vfio_put_base_device(&vcdev->vdev);
|
||||
}
|
||||
|
||||
static void vfio_ccw_get_device(VFIOGroup *group, VFIOCCWDevice *vcdev,
|
||||
Error **errp)
|
||||
{
|
||||
S390CCWDevice *cdev = S390_CCW_DEVICE(vcdev);
|
||||
char *name = g_strdup_printf("%x.%x.%04x", cdev->hostid.cssid,
|
||||
cdev->hostid.ssid,
|
||||
cdev->hostid.devid);
|
||||
VFIODevice *vbasedev;
|
||||
|
||||
QLIST_FOREACH(vbasedev, &group->device_list, next) {
|
||||
if (strcmp(vbasedev->name, name) == 0) {
|
||||
error_setg(errp, "vfio: subchannel %s has already been attached",
|
||||
name);
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* All vfio-ccw devices are believed to operate in a way compatible with
|
||||
* discarding of memory in RAM blocks, ie. pages pinned in the host are
|
||||
* in the current working set of the guest driver and therefore never
|
||||
* overlap e.g., with pages available to the guest balloon driver. This
|
||||
* needs to be set before vfio_get_device() for vfio common to handle
|
||||
* ram_block_discard_disable().
|
||||
*/
|
||||
vcdev->vdev.ram_block_discard_allowed = true;
|
||||
|
||||
if (vfio_get_device(group, cdev->mdevid, &vcdev->vdev, errp)) {
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
vcdev->vdev.ops = &vfio_ccw_ops;
|
||||
vcdev->vdev.type = VFIO_DEVICE_TYPE_CCW;
|
||||
vcdev->vdev.name = name;
|
||||
vcdev->vdev.dev = DEVICE(vcdev);
|
||||
|
||||
return;
|
||||
|
||||
out_err:
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
static VFIOGroup *vfio_ccw_get_group(S390CCWDevice *cdev, Error **errp)
|
||||
{
|
||||
char *tmp, group_path[PATH_MAX];
|
||||
ssize_t len;
|
||||
int groupid;
|
||||
|
||||
tmp = g_strdup_printf("/sys/bus/css/devices/%x.%x.%04x/%s/iommu_group",
|
||||
cdev->hostid.cssid, cdev->hostid.ssid,
|
||||
cdev->hostid.devid, cdev->mdevid);
|
||||
len = readlink(tmp, group_path, sizeof(group_path));
|
||||
g_free(tmp);
|
||||
|
||||
if (len <= 0 || len >= sizeof(group_path)) {
|
||||
error_setg(errp, "vfio: no iommu_group found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
group_path[len] = 0;
|
||||
|
||||
if (sscanf(basename(group_path), "%d", &groupid) != 1) {
|
||||
error_setg(errp, "vfio: failed to read %s", group_path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return vfio_get_group(groupid, &address_space_memory, errp);
|
||||
}
|
||||
|
||||
static void vfio_ccw_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
VFIOGroup *group;
|
||||
S390CCWDevice *cdev = S390_CCW_DEVICE(dev);
|
||||
VFIOCCWDevice *vcdev = VFIO_CCW(cdev);
|
||||
S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(cdev);
|
||||
VFIODevice *vbasedev = &vcdev->vdev;
|
||||
Error *err = NULL;
|
||||
int ret;
|
||||
|
||||
/* Call the class init function for subchannel. */
|
||||
if (cdc->realize) {
|
||||
@@ -663,14 +589,27 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
group = vfio_ccw_get_group(cdev, &err);
|
||||
if (!group) {
|
||||
goto out_group_err;
|
||||
}
|
||||
vbasedev->ops = &vfio_ccw_ops;
|
||||
vbasedev->type = VFIO_DEVICE_TYPE_CCW;
|
||||
vbasedev->name = g_strdup_printf("%x.%x.%04x", vcdev->cdev.hostid.cssid,
|
||||
vcdev->cdev.hostid.ssid,
|
||||
vcdev->cdev.hostid.devid);
|
||||
vbasedev->dev = dev;
|
||||
|
||||
vfio_ccw_get_device(group, vcdev, &err);
|
||||
if (err) {
|
||||
goto out_device_err;
|
||||
/*
|
||||
* All vfio-ccw devices are believed to operate in a way compatible with
|
||||
* discarding of memory in RAM blocks, ie. pages pinned in the host are
|
||||
* in the current working set of the guest driver and therefore never
|
||||
* overlap e.g., with pages available to the guest balloon driver. This
|
||||
* needs to be set before vfio_get_device() for vfio common to handle
|
||||
* ram_block_discard_disable().
|
||||
*/
|
||||
vbasedev->ram_block_discard_allowed = true;
|
||||
|
||||
ret = vfio_attach_device(cdev->mdevid, vbasedev,
|
||||
&address_space_memory, errp);
|
||||
if (ret) {
|
||||
goto out_attach_dev_err;
|
||||
}
|
||||
|
||||
vfio_ccw_get_region(vcdev, &err);
|
||||
@@ -708,10 +647,9 @@ out_irq_notifier_err:
|
||||
out_io_notifier_err:
|
||||
vfio_ccw_put_region(vcdev);
|
||||
out_region_err:
|
||||
vfio_ccw_put_device(vcdev);
|
||||
out_device_err:
|
||||
vfio_put_group(group);
|
||||
out_group_err:
|
||||
vfio_detach_device(vbasedev);
|
||||
out_attach_dev_err:
|
||||
g_free(vbasedev->name);
|
||||
if (cdc->unrealize) {
|
||||
cdc->unrealize(cdev);
|
||||
}
|
||||
@@ -724,14 +662,13 @@ static void vfio_ccw_unrealize(DeviceState *dev)
|
||||
S390CCWDevice *cdev = S390_CCW_DEVICE(dev);
|
||||
VFIOCCWDevice *vcdev = VFIO_CCW(cdev);
|
||||
S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(cdev);
|
||||
VFIOGroup *group = vcdev->vdev.group;
|
||||
|
||||
vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_REQ_IRQ_INDEX);
|
||||
vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX);
|
||||
vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX);
|
||||
vfio_ccw_put_region(vcdev);
|
||||
vfio_ccw_put_device(vcdev);
|
||||
vfio_put_group(group);
|
||||
vfio_detach_device(&vcdev->vdev);
|
||||
g_free(vcdev->vdev.name);
|
||||
|
||||
if (cdc->unrealize) {
|
||||
cdc->unrealize(cdev);
|
||||
|
||||
+122
-1731
File diff suppressed because it is too large
Load Diff
+1161
File diff suppressed because it is too large
Load Diff
@@ -544,3 +544,24 @@ void vfio_display_finalize(VFIOPCIDevice *vdev)
|
||||
vfio_display_edid_exit(vdev->dpy);
|
||||
g_free(vdev->dpy);
|
||||
}
|
||||
|
||||
static bool migrate_needed(void *opaque)
|
||||
{
|
||||
VFIODisplay *dpy = opaque;
|
||||
bool ramfb_exists = dpy->ramfb != NULL;
|
||||
|
||||
/* see vfio_display_migration_needed() */
|
||||
assert(ramfb_exists);
|
||||
return ramfb_exists;
|
||||
}
|
||||
|
||||
const VMStateDescription vfio_display_vmstate = {
|
||||
.name = "VFIODisplay",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = migrate_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_STRUCT_POINTER(ramfb, VFIODisplay, ramfb_vmstate, RAMFBState),
|
||||
VMSTATE_END_OF_LIST(),
|
||||
}
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,8 @@
|
||||
vfio_ss = ss.source_set()
|
||||
vfio_ss.add(files(
|
||||
'helpers.c',
|
||||
'common.c',
|
||||
'container.c',
|
||||
'spapr.c',
|
||||
'migration.c',
|
||||
))
|
||||
|
||||
+52
-45
@@ -2675,6 +2675,33 @@ static bool vfio_msix_present(void *opaque, int version_id)
|
||||
return msix_present(pdev);
|
||||
}
|
||||
|
||||
static bool vfio_display_migration_needed(void *opaque)
|
||||
{
|
||||
VFIOPCIDevice *vdev = opaque;
|
||||
|
||||
/*
|
||||
* We need to migrate the VFIODisplay object if ramfb *migration* was
|
||||
* explicitly requested (in which case we enforced both ramfb=on and
|
||||
* display=on), or ramfb migration was left at the default "auto"
|
||||
* setting, and *ramfb* was explicitly requested (in which case we
|
||||
* enforced display=on).
|
||||
*/
|
||||
return vdev->ramfb_migrate == ON_OFF_AUTO_ON ||
|
||||
(vdev->ramfb_migrate == ON_OFF_AUTO_AUTO && vdev->enable_ramfb);
|
||||
}
|
||||
|
||||
const VMStateDescription vmstate_vfio_display = {
|
||||
.name = "VFIOPCIDevice/VFIODisplay",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = vfio_display_migration_needed,
|
||||
.fields = (VMStateField[]){
|
||||
VMSTATE_STRUCT_POINTER(dpy, VFIOPCIDevice, vfio_display_vmstate,
|
||||
VFIODisplay),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
const VMStateDescription vmstate_vfio_pci_config = {
|
||||
.name = "VFIOPCIDevice",
|
||||
.version_id = 1,
|
||||
@@ -2683,6 +2710,10 @@ const VMStateDescription vmstate_vfio_pci_config = {
|
||||
VMSTATE_PCI_DEVICE(pdev, VFIOPCIDevice),
|
||||
VMSTATE_MSIX_TEST(pdev, VFIOPCIDevice, vfio_msix_present),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (const VMStateDescription * []) {
|
||||
&vmstate_vfio_display,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2895,10 +2926,10 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp)
|
||||
|
||||
static void vfio_pci_put_device(VFIOPCIDevice *vdev)
|
||||
{
|
||||
vfio_detach_device(&vdev->vbasedev);
|
||||
|
||||
g_free(vdev->vbasedev.name);
|
||||
g_free(vdev->msix);
|
||||
|
||||
vfio_put_base_device(&vdev->vbasedev);
|
||||
}
|
||||
|
||||
static void vfio_err_notifier_handler(void *opaque)
|
||||
@@ -3045,13 +3076,9 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
|
||||
{
|
||||
VFIOPCIDevice *vdev = VFIO_PCI(pdev);
|
||||
VFIODevice *vbasedev = &vdev->vbasedev;
|
||||
VFIODevice *vbasedev_iter;
|
||||
VFIOGroup *group;
|
||||
char *tmp, *subsys, group_path[PATH_MAX], *group_name;
|
||||
char *tmp, *subsys;
|
||||
Error *err = NULL;
|
||||
ssize_t len;
|
||||
struct stat st;
|
||||
int groupid;
|
||||
int i, ret;
|
||||
bool is_mdev;
|
||||
char uuid[UUID_FMT_LEN];
|
||||
@@ -3082,39 +3109,6 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
|
||||
vbasedev->type = VFIO_DEVICE_TYPE_PCI;
|
||||
vbasedev->dev = DEVICE(vdev);
|
||||
|
||||
tmp = g_strdup_printf("%s/iommu_group", vbasedev->sysfsdev);
|
||||
len = readlink(tmp, group_path, sizeof(group_path));
|
||||
g_free(tmp);
|
||||
|
||||
if (len <= 0 || len >= sizeof(group_path)) {
|
||||
error_setg_errno(errp, len < 0 ? errno : ENAMETOOLONG,
|
||||
"no iommu_group found");
|
||||
goto error;
|
||||
}
|
||||
|
||||
group_path[len] = 0;
|
||||
|
||||
group_name = basename(group_path);
|
||||
if (sscanf(group_name, "%d", &groupid) != 1) {
|
||||
error_setg_errno(errp, errno, "failed to read %s", group_path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
trace_vfio_realize(vbasedev->name, groupid);
|
||||
|
||||
group = vfio_get_group(groupid, pci_device_iommu_address_space(pdev), errp);
|
||||
if (!group) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
QLIST_FOREACH(vbasedev_iter, &group->device_list, next) {
|
||||
if (strcmp(vbasedev_iter->name, vbasedev->name) == 0) {
|
||||
error_setg(errp, "device is already attached");
|
||||
vfio_put_group(group);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Mediated devices *might* operate compatibly with discarding of RAM, but
|
||||
* we cannot know for certain, it depends on whether the mdev vendor driver
|
||||
@@ -3132,7 +3126,6 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
|
||||
if (vbasedev->ram_block_discard_allowed && !is_mdev) {
|
||||
error_setg(errp, "x-balloon-allowed only potentially compatible "
|
||||
"with mdev devices");
|
||||
vfio_put_group(group);
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -3143,10 +3136,10 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
|
||||
name = g_strdup(vbasedev->name);
|
||||
}
|
||||
|
||||
ret = vfio_get_device(group, name, vbasedev, errp);
|
||||
ret = vfio_attach_device(name, vbasedev,
|
||||
pci_device_iommu_address_space(pdev), errp);
|
||||
g_free(name);
|
||||
if (ret) {
|
||||
vfio_put_group(group);
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -3338,6 +3331,20 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
if (vdev->ramfb_migrate == ON_OFF_AUTO_ON && !vdev->enable_ramfb) {
|
||||
warn_report("x-ramfb-migrate=on but ramfb=off. "
|
||||
"Forcing x-ramfb-migrate to off.");
|
||||
vdev->ramfb_migrate = ON_OFF_AUTO_OFF;
|
||||
}
|
||||
if (vbasedev->enable_migration == ON_OFF_AUTO_OFF) {
|
||||
if (vdev->ramfb_migrate == ON_OFF_AUTO_AUTO) {
|
||||
vdev->ramfb_migrate = ON_OFF_AUTO_OFF;
|
||||
} else if (vdev->ramfb_migrate == ON_OFF_AUTO_ON) {
|
||||
error_setg(errp, "x-ramfb-migrate requires enable-migration");
|
||||
goto out_deregister;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pdev->failover_pair_id) {
|
||||
if (!vfio_migration_realize(vbasedev, errp)) {
|
||||
goto out_deregister;
|
||||
@@ -3371,7 +3378,6 @@ error:
|
||||
static void vfio_instance_finalize(Object *obj)
|
||||
{
|
||||
VFIOPCIDevice *vdev = VFIO_PCI(obj);
|
||||
VFIOGroup *group = vdev->vbasedev.group;
|
||||
|
||||
vfio_display_finalize(vdev);
|
||||
vfio_bars_finalize(vdev);
|
||||
@@ -3385,7 +3391,6 @@ static void vfio_instance_finalize(Object *obj)
|
||||
* g_free(vdev->igd_opregion);
|
||||
*/
|
||||
vfio_pci_put_device(vdev);
|
||||
vfio_put_group(group);
|
||||
}
|
||||
|
||||
static void vfio_exitfn(PCIDevice *pdev)
|
||||
@@ -3551,6 +3556,8 @@ static const TypeInfo vfio_pci_dev_info = {
|
||||
|
||||
static Property vfio_pci_dev_nohotplug_properties[] = {
|
||||
DEFINE_PROP_BOOL("ramfb", VFIOPCIDevice, enable_ramfb, false),
|
||||
DEFINE_PROP_ON_OFF_AUTO("x-ramfb-migrate", VFIOPCIDevice, ramfb_migrate,
|
||||
ON_OFF_AUTO_AUTO),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
||||
@@ -174,6 +174,7 @@ struct VFIOPCIDevice {
|
||||
bool no_kvm_ioeventfd;
|
||||
bool no_vfio_ioeventfd;
|
||||
bool enable_ramfb;
|
||||
OnOffAuto ramfb_migrate;
|
||||
bool defer_kvm_irq_routing;
|
||||
bool clear_parent_atomics_on_exit;
|
||||
VFIODisplay *dpy;
|
||||
@@ -227,4 +228,6 @@ void vfio_display_reset(VFIOPCIDevice *vdev);
|
||||
int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp);
|
||||
void vfio_display_finalize(VFIOPCIDevice *vdev);
|
||||
|
||||
extern const VMStateDescription vfio_display_vmstate;
|
||||
|
||||
#endif /* HW_VFIO_VFIO_PCI_H */
|
||||
|
||||
+3
-40
@@ -529,12 +529,7 @@ static VFIODeviceOps vfio_platform_ops = {
|
||||
*/
|
||||
static int vfio_base_device_init(VFIODevice *vbasedev, Error **errp)
|
||||
{
|
||||
VFIOGroup *group;
|
||||
VFIODevice *vbasedev_iter;
|
||||
char *tmp, group_path[PATH_MAX], *group_name;
|
||||
ssize_t len;
|
||||
struct stat st;
|
||||
int groupid;
|
||||
int ret;
|
||||
|
||||
/* @sysfsdev takes precedence over @host */
|
||||
@@ -557,47 +552,15 @@ static int vfio_base_device_init(VFIODevice *vbasedev, Error **errp)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
tmp = g_strdup_printf("%s/iommu_group", vbasedev->sysfsdev);
|
||||
len = readlink(tmp, group_path, sizeof(group_path));
|
||||
g_free(tmp);
|
||||
|
||||
if (len < 0 || len >= sizeof(group_path)) {
|
||||
ret = len < 0 ? -errno : -ENAMETOOLONG;
|
||||
error_setg_errno(errp, -ret, "no iommu_group found");
|
||||
return ret;
|
||||
}
|
||||
|
||||
group_path[len] = 0;
|
||||
|
||||
group_name = basename(group_path);
|
||||
if (sscanf(group_name, "%d", &groupid) != 1) {
|
||||
error_setg_errno(errp, errno, "failed to read %s", group_path);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
trace_vfio_platform_base_device_init(vbasedev->name, groupid);
|
||||
|
||||
group = vfio_get_group(groupid, &address_space_memory, errp);
|
||||
if (!group) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
QLIST_FOREACH(vbasedev_iter, &group->device_list, next) {
|
||||
if (strcmp(vbasedev_iter->name, vbasedev->name) == 0) {
|
||||
error_setg(errp, "device is already attached");
|
||||
vfio_put_group(group);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
ret = vfio_get_device(group, vbasedev->name, vbasedev, errp);
|
||||
ret = vfio_attach_device(vbasedev->name, vbasedev,
|
||||
&address_space_memory, errp);
|
||||
if (ret) {
|
||||
vfio_put_group(group);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = vfio_populate_device(vbasedev, errp);
|
||||
if (ret) {
|
||||
vfio_put_group(group);
|
||||
vfio_detach_device(vbasedev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -37,7 +37,8 @@ vfio_pci_hot_reset_dep_devices(int domain, int bus, int slot, int function, int
|
||||
vfio_pci_hot_reset_result(const char *name, const char *result) "%s hot reset: %s"
|
||||
vfio_populate_device_config(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device %s config:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx"
|
||||
vfio_populate_device_get_irq_info_failure(const char *errstr) "VFIO_DEVICE_GET_IRQ_INFO failure: %s"
|
||||
vfio_realize(const char *name, int group_id) " (%s) group %d"
|
||||
vfio_attach_device(const char *name, int group_id) " (%s) group %d"
|
||||
vfio_detach_device(const char *name, int group_id) " (%s) group %d"
|
||||
vfio_mdev(const char *name, bool is_mdev) " (%s) is_mdev %d"
|
||||
vfio_add_ext_cap_dropped(const char *name, uint16_t cap, uint16_t offset) "%s 0x%x@0x%x"
|
||||
vfio_pci_reset(const char *name) " (%s)"
|
||||
@@ -120,7 +121,6 @@ vfio_get_dirty_bitmap(int fd, uint64_t iova, uint64_t size, uint64_t bitmap_size
|
||||
vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64
|
||||
|
||||
# platform.c
|
||||
vfio_platform_base_device_init(char *name, int groupid) "%s belongs to group #%d"
|
||||
vfio_platform_realize(char *name, char *compat) "vfio device %s, compat = %s"
|
||||
vfio_platform_eoi(int pin, int fd) "EOI IRQ pin %d (fd=%d)"
|
||||
vfio_platform_intp_mmap_enable(int pin) "IRQ #%d still active, stay in slow path"
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
#ifndef RAMFB_H
|
||||
#define RAMFB_H
|
||||
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
/* ramfb.c */
|
||||
typedef struct RAMFBState RAMFBState;
|
||||
void ramfb_display_update(QemuConsole *con, RAMFBState *s);
|
||||
RAMFBState *ramfb_setup(Error **errp);
|
||||
|
||||
extern const VMStateDescription ramfb_vmstate;
|
||||
|
||||
/* ramfb-standalone.c */
|
||||
#define TYPE_RAMFB_DEVICE "ramfb"
|
||||
|
||||
|
||||
@@ -22,6 +22,4 @@
|
||||
#define TYPE_VFIO_CCW "vfio-ccw"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(VFIOCCWDevice, VFIO_CCW)
|
||||
|
||||
#define TYPE_VFIO_CCW "vfio-ccw"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -98,6 +98,7 @@ typedef struct VFIOContainer {
|
||||
QLIST_HEAD(, VFIOGroup) group_list;
|
||||
QLIST_HEAD(, VFIORamDiscardListener) vrdl_list;
|
||||
QLIST_ENTRY(VFIOContainer) next;
|
||||
QLIST_HEAD(, VFIODevice) device_list;
|
||||
} VFIOContainer;
|
||||
|
||||
typedef struct VFIOGuestIOMMU {
|
||||
@@ -129,7 +130,10 @@ typedef struct VFIODeviceOps VFIODeviceOps;
|
||||
|
||||
typedef struct VFIODevice {
|
||||
QLIST_ENTRY(VFIODevice) next;
|
||||
QLIST_ENTRY(VFIODevice) container_next;
|
||||
QLIST_ENTRY(VFIODevice) global_next;
|
||||
struct VFIOGroup *group;
|
||||
VFIOContainer *container;
|
||||
char *sysfsdev;
|
||||
char *name;
|
||||
DeviceState *dev;
|
||||
@@ -196,7 +200,36 @@ typedef struct VFIODisplay {
|
||||
} dmabuf;
|
||||
} VFIODisplay;
|
||||
|
||||
void vfio_put_base_device(VFIODevice *vbasedev);
|
||||
typedef struct {
|
||||
unsigned long *bitmap;
|
||||
hwaddr size;
|
||||
hwaddr pages;
|
||||
} VFIOBitmap;
|
||||
|
||||
void vfio_host_win_add(VFIOContainer *container,
|
||||
hwaddr min_iova, hwaddr max_iova,
|
||||
uint64_t iova_pgsizes);
|
||||
int vfio_host_win_del(VFIOContainer *container, hwaddr min_iova,
|
||||
hwaddr max_iova);
|
||||
VFIOAddressSpace *vfio_get_address_space(AddressSpace *as);
|
||||
void vfio_put_address_space(VFIOAddressSpace *space);
|
||||
bool vfio_devices_all_running_and_saving(VFIOContainer *container);
|
||||
|
||||
/* container->fd */
|
||||
int vfio_dma_unmap(VFIOContainer *container, hwaddr iova,
|
||||
ram_addr_t size, IOMMUTLBEntry *iotlb);
|
||||
int vfio_dma_map(VFIOContainer *container, hwaddr iova,
|
||||
ram_addr_t size, void *vaddr, bool readonly);
|
||||
int vfio_set_dirty_page_tracking(VFIOContainer *container, bool start);
|
||||
int vfio_query_dirty_bitmap(VFIOContainer *container, VFIOBitmap *vbmap,
|
||||
hwaddr iova, hwaddr size);
|
||||
|
||||
int vfio_container_add_section_window(VFIOContainer *container,
|
||||
MemoryRegionSection *section,
|
||||
Error **errp);
|
||||
void vfio_container_del_section_window(VFIOContainer *container,
|
||||
MemoryRegionSection *section);
|
||||
|
||||
void vfio_disable_irqindex(VFIODevice *vbasedev, int index);
|
||||
void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index);
|
||||
void vfio_mask_single_irqindex(VFIODevice *vbasedev, int index);
|
||||
@@ -214,15 +247,22 @@ void vfio_region_unmap(VFIORegion *region);
|
||||
void vfio_region_exit(VFIORegion *region);
|
||||
void vfio_region_finalize(VFIORegion *region);
|
||||
void vfio_reset_handler(void *opaque);
|
||||
VFIOGroup *vfio_get_group(int groupid, AddressSpace *as, Error **errp);
|
||||
void vfio_put_group(VFIOGroup *group);
|
||||
struct vfio_device_info *vfio_get_device_info(int fd);
|
||||
int vfio_get_device(VFIOGroup *group, const char *name,
|
||||
VFIODevice *vbasedev, Error **errp);
|
||||
int vfio_attach_device(char *name, VFIODevice *vbasedev,
|
||||
AddressSpace *as, Error **errp);
|
||||
void vfio_detach_device(VFIODevice *vbasedev);
|
||||
|
||||
int vfio_kvm_device_add_fd(int fd, Error **errp);
|
||||
int vfio_kvm_device_del_fd(int fd, Error **errp);
|
||||
|
||||
extern const MemoryRegionOps vfio_region_ops;
|
||||
typedef QLIST_HEAD(VFIOGroupList, VFIOGroup) VFIOGroupList;
|
||||
typedef QLIST_HEAD(VFIODeviceList, VFIODevice) VFIODeviceList;
|
||||
extern VFIOGroupList vfio_group_list;
|
||||
extern VFIODeviceList vfio_device_list;
|
||||
|
||||
extern const MemoryListener vfio_memory_listener;
|
||||
extern int vfio_kvm_device_fd;
|
||||
|
||||
bool vfio_mig_active(void);
|
||||
int vfio_block_multiple_devices_migration(VFIODevice *vbasedev, Error **errp);
|
||||
@@ -245,6 +285,8 @@ bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info,
|
||||
unsigned int *avail);
|
||||
struct vfio_info_cap_header *
|
||||
vfio_get_device_info_cap(struct vfio_device_info *info, uint16_t id);
|
||||
struct vfio_info_cap_header *
|
||||
vfio_get_cap(void *ptr, uint32_t cap_offset, uint16_t id);
|
||||
#endif
|
||||
extern const MemoryListener vfio_prereg_listener;
|
||||
|
||||
@@ -257,4 +299,12 @@ int vfio_spapr_remove_window(VFIOContainer *container,
|
||||
bool vfio_migration_realize(VFIODevice *vbasedev, Error **errp);
|
||||
void vfio_migration_exit(VFIODevice *vbasedev);
|
||||
|
||||
int vfio_bitmap_alloc(VFIOBitmap *vbmap, hwaddr size);
|
||||
bool vfio_devices_all_running_and_mig_active(VFIOContainer *container);
|
||||
bool vfio_devices_all_device_dirty_tracking(VFIOContainer *container);
|
||||
int vfio_devices_query_dirty_bitmap(VFIOContainer *container,
|
||||
VFIOBitmap *vbmap, hwaddr iova,
|
||||
hwaddr size);
|
||||
int vfio_get_dirty_bitmap(VFIOContainer *container, uint64_t iova,
|
||||
uint64_t size, ram_addr_t ram_addr);
|
||||
#endif /* HW_VFIO_VFIO_COMMON_H */
|
||||
|
||||
@@ -0,0 +1,444 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES.
|
||||
*/
|
||||
#ifndef _IOMMUFD_H
|
||||
#define _IOMMUFD_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
#define IOMMUFD_TYPE (';')
|
||||
|
||||
/**
|
||||
* DOC: General ioctl format
|
||||
*
|
||||
* The ioctl interface follows a general format to allow for extensibility. Each
|
||||
* ioctl is passed in a structure pointer as the argument providing the size of
|
||||
* the structure in the first u32. The kernel checks that any structure space
|
||||
* beyond what it understands is 0. This allows userspace to use the backward
|
||||
* compatible portion while consistently using the newer, larger, structures.
|
||||
*
|
||||
* ioctls use a standard meaning for common errnos:
|
||||
*
|
||||
* - ENOTTY: The IOCTL number itself is not supported at all
|
||||
* - E2BIG: The IOCTL number is supported, but the provided structure has
|
||||
* non-zero in a part the kernel does not understand.
|
||||
* - EOPNOTSUPP: The IOCTL number is supported, and the structure is
|
||||
* understood, however a known field has a value the kernel does not
|
||||
* understand or support.
|
||||
* - EINVAL: Everything about the IOCTL was understood, but a field is not
|
||||
* correct.
|
||||
* - ENOENT: An ID or IOVA provided does not exist.
|
||||
* - ENOMEM: Out of memory.
|
||||
* - EOVERFLOW: Mathematics overflowed.
|
||||
*
|
||||
* As well as additional errnos, within specific ioctls.
|
||||
*/
|
||||
enum {
|
||||
IOMMUFD_CMD_BASE = 0x80,
|
||||
IOMMUFD_CMD_DESTROY = IOMMUFD_CMD_BASE,
|
||||
IOMMUFD_CMD_IOAS_ALLOC,
|
||||
IOMMUFD_CMD_IOAS_ALLOW_IOVAS,
|
||||
IOMMUFD_CMD_IOAS_COPY,
|
||||
IOMMUFD_CMD_IOAS_IOVA_RANGES,
|
||||
IOMMUFD_CMD_IOAS_MAP,
|
||||
IOMMUFD_CMD_IOAS_UNMAP,
|
||||
IOMMUFD_CMD_OPTION,
|
||||
IOMMUFD_CMD_VFIO_IOAS,
|
||||
IOMMUFD_CMD_HWPT_ALLOC,
|
||||
IOMMUFD_CMD_GET_HW_INFO,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iommu_destroy - ioctl(IOMMU_DESTROY)
|
||||
* @size: sizeof(struct iommu_destroy)
|
||||
* @id: iommufd object ID to destroy. Can be any destroyable object type.
|
||||
*
|
||||
* Destroy any object held within iommufd.
|
||||
*/
|
||||
struct iommu_destroy {
|
||||
__u32 size;
|
||||
__u32 id;
|
||||
};
|
||||
#define IOMMU_DESTROY _IO(IOMMUFD_TYPE, IOMMUFD_CMD_DESTROY)
|
||||
|
||||
/**
|
||||
* struct iommu_ioas_alloc - ioctl(IOMMU_IOAS_ALLOC)
|
||||
* @size: sizeof(struct iommu_ioas_alloc)
|
||||
* @flags: Must be 0
|
||||
* @out_ioas_id: Output IOAS ID for the allocated object
|
||||
*
|
||||
* Allocate an IO Address Space (IOAS) which holds an IO Virtual Address (IOVA)
|
||||
* to memory mapping.
|
||||
*/
|
||||
struct iommu_ioas_alloc {
|
||||
__u32 size;
|
||||
__u32 flags;
|
||||
__u32 out_ioas_id;
|
||||
};
|
||||
#define IOMMU_IOAS_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_ALLOC)
|
||||
|
||||
/**
|
||||
* struct iommu_iova_range - ioctl(IOMMU_IOVA_RANGE)
|
||||
* @start: First IOVA
|
||||
* @last: Inclusive last IOVA
|
||||
*
|
||||
* An interval in IOVA space.
|
||||
*/
|
||||
struct iommu_iova_range {
|
||||
__aligned_u64 start;
|
||||
__aligned_u64 last;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iommu_ioas_iova_ranges - ioctl(IOMMU_IOAS_IOVA_RANGES)
|
||||
* @size: sizeof(struct iommu_ioas_iova_ranges)
|
||||
* @ioas_id: IOAS ID to read ranges from
|
||||
* @num_iovas: Input/Output total number of ranges in the IOAS
|
||||
* @__reserved: Must be 0
|
||||
* @allowed_iovas: Pointer to the output array of struct iommu_iova_range
|
||||
* @out_iova_alignment: Minimum alignment required for mapping IOVA
|
||||
*
|
||||
* Query an IOAS for ranges of allowed IOVAs. Mapping IOVA outside these ranges
|
||||
* is not allowed. num_iovas will be set to the total number of iovas and
|
||||
* the allowed_iovas[] will be filled in as space permits.
|
||||
*
|
||||
* The allowed ranges are dependent on the HW path the DMA operation takes, and
|
||||
* can change during the lifetime of the IOAS. A fresh empty IOAS will have a
|
||||
* full range, and each attached device will narrow the ranges based on that
|
||||
* device's HW restrictions. Detaching a device can widen the ranges. Userspace
|
||||
* should query ranges after every attach/detach to know what IOVAs are valid
|
||||
* for mapping.
|
||||
*
|
||||
* On input num_iovas is the length of the allowed_iovas array. On output it is
|
||||
* the total number of iovas filled in. The ioctl will return -EMSGSIZE and set
|
||||
* num_iovas to the required value if num_iovas is too small. In this case the
|
||||
* caller should allocate a larger output array and re-issue the ioctl.
|
||||
*
|
||||
* out_iova_alignment returns the minimum IOVA alignment that can be given
|
||||
* to IOMMU_IOAS_MAP/COPY. IOVA's must satisfy::
|
||||
*
|
||||
* starting_iova % out_iova_alignment == 0
|
||||
* (starting_iova + length) % out_iova_alignment == 0
|
||||
*
|
||||
* out_iova_alignment can be 1 indicating any IOVA is allowed. It cannot
|
||||
* be higher than the system PAGE_SIZE.
|
||||
*/
|
||||
struct iommu_ioas_iova_ranges {
|
||||
__u32 size;
|
||||
__u32 ioas_id;
|
||||
__u32 num_iovas;
|
||||
__u32 __reserved;
|
||||
__aligned_u64 allowed_iovas;
|
||||
__aligned_u64 out_iova_alignment;
|
||||
};
|
||||
#define IOMMU_IOAS_IOVA_RANGES _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_IOVA_RANGES)
|
||||
|
||||
/**
|
||||
* struct iommu_ioas_allow_iovas - ioctl(IOMMU_IOAS_ALLOW_IOVAS)
|
||||
* @size: sizeof(struct iommu_ioas_allow_iovas)
|
||||
* @ioas_id: IOAS ID to allow IOVAs from
|
||||
* @num_iovas: Input/Output total number of ranges in the IOAS
|
||||
* @__reserved: Must be 0
|
||||
* @allowed_iovas: Pointer to array of struct iommu_iova_range
|
||||
*
|
||||
* Ensure a range of IOVAs are always available for allocation. If this call
|
||||
* succeeds then IOMMU_IOAS_IOVA_RANGES will never return a list of IOVA ranges
|
||||
* that are narrower than the ranges provided here. This call will fail if
|
||||
* IOMMU_IOAS_IOVA_RANGES is currently narrower than the given ranges.
|
||||
*
|
||||
* When an IOAS is first created the IOVA_RANGES will be maximally sized, and as
|
||||
* devices are attached the IOVA will narrow based on the device restrictions.
|
||||
* When an allowed range is specified any narrowing will be refused, ie device
|
||||
* attachment can fail if the device requires limiting within the allowed range.
|
||||
*
|
||||
* Automatic IOVA allocation is also impacted by this call. MAP will only
|
||||
* allocate within the allowed IOVAs if they are present.
|
||||
*
|
||||
* This call replaces the entire allowed list with the given list.
|
||||
*/
|
||||
struct iommu_ioas_allow_iovas {
|
||||
__u32 size;
|
||||
__u32 ioas_id;
|
||||
__u32 num_iovas;
|
||||
__u32 __reserved;
|
||||
__aligned_u64 allowed_iovas;
|
||||
};
|
||||
#define IOMMU_IOAS_ALLOW_IOVAS _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_ALLOW_IOVAS)
|
||||
|
||||
/**
|
||||
* enum iommufd_ioas_map_flags - Flags for map and copy
|
||||
* @IOMMU_IOAS_MAP_FIXED_IOVA: If clear the kernel will compute an appropriate
|
||||
* IOVA to place the mapping at
|
||||
* @IOMMU_IOAS_MAP_WRITEABLE: DMA is allowed to write to this mapping
|
||||
* @IOMMU_IOAS_MAP_READABLE: DMA is allowed to read from this mapping
|
||||
*/
|
||||
enum iommufd_ioas_map_flags {
|
||||
IOMMU_IOAS_MAP_FIXED_IOVA = 1 << 0,
|
||||
IOMMU_IOAS_MAP_WRITEABLE = 1 << 1,
|
||||
IOMMU_IOAS_MAP_READABLE = 1 << 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iommu_ioas_map - ioctl(IOMMU_IOAS_MAP)
|
||||
* @size: sizeof(struct iommu_ioas_map)
|
||||
* @flags: Combination of enum iommufd_ioas_map_flags
|
||||
* @ioas_id: IOAS ID to change the mapping of
|
||||
* @__reserved: Must be 0
|
||||
* @user_va: Userspace pointer to start mapping from
|
||||
* @length: Number of bytes to map
|
||||
* @iova: IOVA the mapping was placed at. If IOMMU_IOAS_MAP_FIXED_IOVA is set
|
||||
* then this must be provided as input.
|
||||
*
|
||||
* Set an IOVA mapping from a user pointer. If FIXED_IOVA is specified then the
|
||||
* mapping will be established at iova, otherwise a suitable location based on
|
||||
* the reserved and allowed lists will be automatically selected and returned in
|
||||
* iova.
|
||||
*
|
||||
* If IOMMU_IOAS_MAP_FIXED_IOVA is specified then the iova range must currently
|
||||
* be unused, existing IOVA cannot be replaced.
|
||||
*/
|
||||
struct iommu_ioas_map {
|
||||
__u32 size;
|
||||
__u32 flags;
|
||||
__u32 ioas_id;
|
||||
__u32 __reserved;
|
||||
__aligned_u64 user_va;
|
||||
__aligned_u64 length;
|
||||
__aligned_u64 iova;
|
||||
};
|
||||
#define IOMMU_IOAS_MAP _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_MAP)
|
||||
|
||||
/**
|
||||
* struct iommu_ioas_copy - ioctl(IOMMU_IOAS_COPY)
|
||||
* @size: sizeof(struct iommu_ioas_copy)
|
||||
* @flags: Combination of enum iommufd_ioas_map_flags
|
||||
* @dst_ioas_id: IOAS ID to change the mapping of
|
||||
* @src_ioas_id: IOAS ID to copy from
|
||||
* @length: Number of bytes to copy and map
|
||||
* @dst_iova: IOVA the mapping was placed at. If IOMMU_IOAS_MAP_FIXED_IOVA is
|
||||
* set then this must be provided as input.
|
||||
* @src_iova: IOVA to start the copy
|
||||
*
|
||||
* Copy an already existing mapping from src_ioas_id and establish it in
|
||||
* dst_ioas_id. The src iova/length must exactly match a range used with
|
||||
* IOMMU_IOAS_MAP.
|
||||
*
|
||||
* This may be used to efficiently clone a subset of an IOAS to another, or as a
|
||||
* kind of 'cache' to speed up mapping. Copy has an efficiency advantage over
|
||||
* establishing equivalent new mappings, as internal resources are shared, and
|
||||
* the kernel will pin the user memory only once.
|
||||
*/
|
||||
struct iommu_ioas_copy {
|
||||
__u32 size;
|
||||
__u32 flags;
|
||||
__u32 dst_ioas_id;
|
||||
__u32 src_ioas_id;
|
||||
__aligned_u64 length;
|
||||
__aligned_u64 dst_iova;
|
||||
__aligned_u64 src_iova;
|
||||
};
|
||||
#define IOMMU_IOAS_COPY _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_COPY)
|
||||
|
||||
/**
|
||||
* struct iommu_ioas_unmap - ioctl(IOMMU_IOAS_UNMAP)
|
||||
* @size: sizeof(struct iommu_ioas_unmap)
|
||||
* @ioas_id: IOAS ID to change the mapping of
|
||||
* @iova: IOVA to start the unmapping at
|
||||
* @length: Number of bytes to unmap, and return back the bytes unmapped
|
||||
*
|
||||
* Unmap an IOVA range. The iova/length must be a superset of a previously
|
||||
* mapped range used with IOMMU_IOAS_MAP or IOMMU_IOAS_COPY. Splitting or
|
||||
* truncating ranges is not allowed. The values 0 to U64_MAX will unmap
|
||||
* everything.
|
||||
*/
|
||||
struct iommu_ioas_unmap {
|
||||
__u32 size;
|
||||
__u32 ioas_id;
|
||||
__aligned_u64 iova;
|
||||
__aligned_u64 length;
|
||||
};
|
||||
#define IOMMU_IOAS_UNMAP _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_UNMAP)
|
||||
|
||||
/**
|
||||
* enum iommufd_option - ioctl(IOMMU_OPTION_RLIMIT_MODE) and
|
||||
* ioctl(IOMMU_OPTION_HUGE_PAGES)
|
||||
* @IOMMU_OPTION_RLIMIT_MODE:
|
||||
* Change how RLIMIT_MEMLOCK accounting works. The caller must have privilege
|
||||
* to invoke this. Value 0 (default) is user based accouting, 1 uses process
|
||||
* based accounting. Global option, object_id must be 0
|
||||
* @IOMMU_OPTION_HUGE_PAGES:
|
||||
* Value 1 (default) allows contiguous pages to be combined when generating
|
||||
* iommu mappings. Value 0 disables combining, everything is mapped to
|
||||
* PAGE_SIZE. This can be useful for benchmarking. This is a per-IOAS
|
||||
* option, the object_id must be the IOAS ID.
|
||||
*/
|
||||
enum iommufd_option {
|
||||
IOMMU_OPTION_RLIMIT_MODE = 0,
|
||||
IOMMU_OPTION_HUGE_PAGES = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum iommufd_option_ops - ioctl(IOMMU_OPTION_OP_SET) and
|
||||
* ioctl(IOMMU_OPTION_OP_GET)
|
||||
* @IOMMU_OPTION_OP_SET: Set the option's value
|
||||
* @IOMMU_OPTION_OP_GET: Get the option's value
|
||||
*/
|
||||
enum iommufd_option_ops {
|
||||
IOMMU_OPTION_OP_SET = 0,
|
||||
IOMMU_OPTION_OP_GET = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iommu_option - iommu option multiplexer
|
||||
* @size: sizeof(struct iommu_option)
|
||||
* @option_id: One of enum iommufd_option
|
||||
* @op: One of enum iommufd_option_ops
|
||||
* @__reserved: Must be 0
|
||||
* @object_id: ID of the object if required
|
||||
* @val64: Option value to set or value returned on get
|
||||
*
|
||||
* Change a simple option value. This multiplexor allows controlling options
|
||||
* on objects. IOMMU_OPTION_OP_SET will load an option and IOMMU_OPTION_OP_GET
|
||||
* will return the current value.
|
||||
*/
|
||||
struct iommu_option {
|
||||
__u32 size;
|
||||
__u32 option_id;
|
||||
__u16 op;
|
||||
__u16 __reserved;
|
||||
__u32 object_id;
|
||||
__aligned_u64 val64;
|
||||
};
|
||||
#define IOMMU_OPTION _IO(IOMMUFD_TYPE, IOMMUFD_CMD_OPTION)
|
||||
|
||||
/**
|
||||
* enum iommufd_vfio_ioas_op - IOMMU_VFIO_IOAS_* ioctls
|
||||
* @IOMMU_VFIO_IOAS_GET: Get the current compatibility IOAS
|
||||
* @IOMMU_VFIO_IOAS_SET: Change the current compatibility IOAS
|
||||
* @IOMMU_VFIO_IOAS_CLEAR: Disable VFIO compatibility
|
||||
*/
|
||||
enum iommufd_vfio_ioas_op {
|
||||
IOMMU_VFIO_IOAS_GET = 0,
|
||||
IOMMU_VFIO_IOAS_SET = 1,
|
||||
IOMMU_VFIO_IOAS_CLEAR = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iommu_vfio_ioas - ioctl(IOMMU_VFIO_IOAS)
|
||||
* @size: sizeof(struct iommu_vfio_ioas)
|
||||
* @ioas_id: For IOMMU_VFIO_IOAS_SET the input IOAS ID to set
|
||||
* For IOMMU_VFIO_IOAS_GET will output the IOAS ID
|
||||
* @op: One of enum iommufd_vfio_ioas_op
|
||||
* @__reserved: Must be 0
|
||||
*
|
||||
* The VFIO compatibility support uses a single ioas because VFIO APIs do not
|
||||
* support the ID field. Set or Get the IOAS that VFIO compatibility will use.
|
||||
* When VFIO_GROUP_SET_CONTAINER is used on an iommufd it will get the
|
||||
* compatibility ioas, either by taking what is already set, or auto creating
|
||||
* one. From then on VFIO will continue to use that ioas and is not effected by
|
||||
* this ioctl. SET or CLEAR does not destroy any auto-created IOAS.
|
||||
*/
|
||||
struct iommu_vfio_ioas {
|
||||
__u32 size;
|
||||
__u32 ioas_id;
|
||||
__u16 op;
|
||||
__u16 __reserved;
|
||||
};
|
||||
#define IOMMU_VFIO_IOAS _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VFIO_IOAS)
|
||||
|
||||
/**
|
||||
* struct iommu_hwpt_alloc - ioctl(IOMMU_HWPT_ALLOC)
|
||||
* @size: sizeof(struct iommu_hwpt_alloc)
|
||||
* @flags: Must be 0
|
||||
* @dev_id: The device to allocate this HWPT for
|
||||
* @pt_id: The IOAS to connect this HWPT to
|
||||
* @out_hwpt_id: The ID of the new HWPT
|
||||
* @__reserved: Must be 0
|
||||
*
|
||||
* Explicitly allocate a hardware page table object. This is the same object
|
||||
* type that is returned by iommufd_device_attach() and represents the
|
||||
* underlying iommu driver's iommu_domain kernel object.
|
||||
*
|
||||
* A HWPT will be created with the IOVA mappings from the given IOAS.
|
||||
*/
|
||||
struct iommu_hwpt_alloc {
|
||||
__u32 size;
|
||||
__u32 flags;
|
||||
__u32 dev_id;
|
||||
__u32 pt_id;
|
||||
__u32 out_hwpt_id;
|
||||
__u32 __reserved;
|
||||
};
|
||||
#define IOMMU_HWPT_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_HWPT_ALLOC)
|
||||
|
||||
/**
|
||||
* struct iommu_hw_info_vtd - Intel VT-d hardware information
|
||||
*
|
||||
* @flags: Must be 0
|
||||
* @__reserved: Must be 0
|
||||
*
|
||||
* @cap_reg: Value of Intel VT-d capability register defined in VT-d spec
|
||||
* section 11.4.2 Capability Register.
|
||||
* @ecap_reg: Value of Intel VT-d capability register defined in VT-d spec
|
||||
* section 11.4.3 Extended Capability Register.
|
||||
*
|
||||
* User needs to understand the Intel VT-d specification to decode the
|
||||
* register value.
|
||||
*/
|
||||
struct iommu_hw_info_vtd {
|
||||
__u32 flags;
|
||||
__u32 __reserved;
|
||||
__aligned_u64 cap_reg;
|
||||
__aligned_u64 ecap_reg;
|
||||
};
|
||||
|
||||
/**
|
||||
* enum iommu_hw_info_type - IOMMU Hardware Info Types
|
||||
* @IOMMU_HW_INFO_TYPE_NONE: Used by the drivers that do not report hardware
|
||||
* info
|
||||
* @IOMMU_HW_INFO_TYPE_INTEL_VTD: Intel VT-d iommu info type
|
||||
*/
|
||||
enum iommu_hw_info_type {
|
||||
IOMMU_HW_INFO_TYPE_NONE,
|
||||
IOMMU_HW_INFO_TYPE_INTEL_VTD,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iommu_hw_info - ioctl(IOMMU_GET_HW_INFO)
|
||||
* @size: sizeof(struct iommu_hw_info)
|
||||
* @flags: Must be 0
|
||||
* @dev_id: The device bound to the iommufd
|
||||
* @data_len: Input the length of a user buffer in bytes. Output the length of
|
||||
* data that kernel supports
|
||||
* @data_uptr: User pointer to a user-space buffer used by the kernel to fill
|
||||
* the iommu type specific hardware information data
|
||||
* @out_data_type: Output the iommu hardware info type as defined in the enum
|
||||
* iommu_hw_info_type.
|
||||
* @__reserved: Must be 0
|
||||
*
|
||||
* Query an iommu type specific hardware information data from an iommu behind
|
||||
* a given device that has been bound to iommufd. This hardware info data will
|
||||
* be used to sync capabilities between the virtual iommu and the physical
|
||||
* iommu, e.g. a nested translation setup needs to check the hardware info, so
|
||||
* a guest stage-1 page table can be compatible with the physical iommu.
|
||||
*
|
||||
* To capture an iommu type specific hardware information data, @data_uptr and
|
||||
* its length @data_len must be provided. Trailing bytes will be zeroed if the
|
||||
* user buffer is larger than the data that kernel has. Otherwise, kernel only
|
||||
* fills the buffer using the given length in @data_len. If the ioctl succeeds,
|
||||
* @data_len will be updated to the length that kernel actually supports,
|
||||
* @out_data_type will be filled to decode the data filled in the buffer
|
||||
* pointed by @data_uptr. Input @data_len == zero is allowed.
|
||||
*/
|
||||
struct iommu_hw_info {
|
||||
__u32 size;
|
||||
__u32 flags;
|
||||
__u32 dev_id;
|
||||
__u32 data_len;
|
||||
__aligned_u64 data_uptr;
|
||||
__u32 out_data_type;
|
||||
__u32 __reserved;
|
||||
};
|
||||
#define IOMMU_GET_HW_INFO _IO(IOMMUFD_TYPE, IOMMUFD_CMD_GET_HW_INFO)
|
||||
#endif
|
||||
@@ -161,7 +161,8 @@ done
|
||||
rm -rf "$output/linux-headers/linux"
|
||||
mkdir -p "$output/linux-headers/linux"
|
||||
for header in const.h stddef.h kvm.h vfio.h vfio_ccw.h vfio_zdev.h vhost.h \
|
||||
psci.h psp-sev.h userfaultfd.h memfd.h mman.h nvme_ioctl.h vduse.h; do
|
||||
psci.h psp-sev.h userfaultfd.h memfd.h mman.h nvme_ioctl.h \
|
||||
vduse.h iommufd.h; do
|
||||
cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux"
|
||||
done
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include "qapi/error.h"
|
||||
#include "hw/display/ramfb.h"
|
||||
|
||||
const VMStateDescription ramfb_vmstate = {};
|
||||
|
||||
void ramfb_display_update(QemuConsole *con, RAMFBState *s)
|
||||
{
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user