mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'pull-vfio-20250509' of https://github.com/legoater/qemu into staging
vfio queue: * Preparatory changes for the introduction of CPR support * Automatic enablement of OpRegion for IGD device passthrough * Linux headers update * Preparatory changes for the introduction of vfio-user # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmgd/0kACgkQUaNDx8/7 # 7KHRmRAArw1PXMCmoVBBeLcZ8BZPGjBZHtsvRzwS1yhVnNQadlpDlq4wd9HrfDFK # BTr7//Ag2Q1dKgibesh0A8hSjorXHUGQCmdkcCuGGTFnEwC86q5jCH1lUxgI0cs5 # 3bVwc43zhXGoKqmo07g4f2UFbjDYHe89LgWz2c7TFFGz7Tda/LCOdhnmXlXcIwz+ # v1ocutXd7VbDWvUzN7uZbf0SIH3Zj3p96dwmpLDtdzdliDA0JidNvS27+Z5gtvWe # O+1NW9MDzNfd6zLXCxL3GLeT61WZCe1dRCHEPX4cBo+DhnrifsC25DtJwYlDFvi2 # NMFfGzdKcEVSpeDp7WeM6MJgCZsGHC7ytmAKOKgN2M2kFSj3SI3sTFNlE1rzUhe6 # yjjCa59HzNLIi7L7xYCrVtCLGC/VXOp9kh67Sjs7FY7v778QUEdiudFBdBki7Bwh # bpRhdFJgCLHuKc6XrM7hsMnsRyM28MywyfHDo3M/pRSFNKfeImW6zSMXnyncZztK # W8e8OIz2DBMfH8pIu8hPw9Gsm5VAAs4aVmVFNa0CLl0oBko0Ew2YXcA5pTK5gGqv # x24uc/BhbLcfFUtK0OnP4N/B4rcoADebPV2u4eWoUK3aF5u4+7BY235bFuoTj+sb # 55DPDyWm5cmkX58Tdq46tD39dbD1hlUYkcydPbANH51wYx/lPpc= # =OqYP # -----END PGP SIGNATURE----- # gpg: Signature made Fri 09 May 2025 09:12:41 EDT # gpg: using RSA key A0F66548F04895EBFE6B0B6051A343C7CFFBECA1 # gpg: Good signature from "Cédric Le Goater <clg@redhat.com>" [full] # gpg: aka "Cédric Le Goater <clg@kaod.org>" [full] # Primary key fingerprint: A0F6 6548 F048 95EB FE6B 0B60 51A3 43C7 CFFB ECA1 * tag 'pull-vfio-20250509' of https://github.com/legoater/qemu: (28 commits) vfio/container: pass listener_begin/commit callbacks vfio: add vfio-pci-base class vfio: add read/write to device IO ops vector vfio: add region info cache vfio: add device IO ops vector vfio: implement unmap all for DMA unmap callbacks vfio: add unmap_all flag to DMA unmap callback vfio: add vfio_pci_config_space_read/write() vfio: add strread/writeerror() vfio: consistently handle return value for helpers vfio: add vfio_device_get_irq_info() helper vfio: add vfio_attach_device_by_iommu_type() vfio: add vfio_device_unprepare() vfio: add vfio_device_prepare() linux-headers: Update to Linux v6.15-rc3 linux-header: update-linux-header script changes vfio/igd: Remove generation limitation for IGD passthrough vfio/igd: Only emulate GGC register when x-igd-gms is set vfio/igd: Allow overriding GMS with 0xf0 to 0xfe on Gen9+ vfio/igd: Enable OpRegion by default ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
+9
-2
@@ -47,6 +47,7 @@ Intel document [1] shows how to dump VBIOS to file. For UEFI Option ROM, see
|
||||
|
||||
QEMU also provides a "Legacy" mode that implicitly enables full functionality
|
||||
on IGD, it is automatically enabled when
|
||||
* IGD generation is 6 to 9 (Sandy Bridge to Comet Lake)
|
||||
* Machine type is i440fx
|
||||
* IGD is assigned to guest BDF 00:02.0
|
||||
* ROM BAR or romfile is present
|
||||
@@ -101,7 +102,7 @@ digital formats work well.
|
||||
|
||||
Options
|
||||
=======
|
||||
* x-igd-opregion=[on|*off*]
|
||||
* x-igd-opregion=[*on*|off]
|
||||
Copy host IGD OpRegion and expose it to guest with fw_cfg
|
||||
|
||||
* x-igd-lpc=[on|*off*]
|
||||
@@ -123,7 +124,7 @@ Examples
|
||||
|
||||
* Adding IGD with OpRegion and LPC ID hack, but without VGA ranges
|
||||
(For UEFI guests)
|
||||
-device vfio-pci,host=00:02.0,id=hostdev0,addr=2.0,x-igd-legacy-mode=off,x-igd-opregion=on,x-igd-lpc=on,romfile=efi_oprom.rom
|
||||
-device vfio-pci,host=00:02.0,id=hostdev0,addr=2.0,x-igd-legacy-mode=off,x-igd-lpc=on,romfile=efi_oprom.rom
|
||||
|
||||
|
||||
Guest firmware
|
||||
@@ -156,6 +157,12 @@ fw_cfg requirements on the VM firmware:
|
||||
it's expected that this fw_cfg file is only relevant to a single PCI
|
||||
class VGA device with Intel vendor ID, appearing at PCI bus address 00:02.0.
|
||||
|
||||
Starting from Meteor Lake, IGD devices access stolen memory via its MMIO
|
||||
BAR2 (LMEMBAR) and removed the BDSM register in config space. There is
|
||||
no need for guest firmware to allocate data stolen memory in guest address
|
||||
space and write it to BDSM register. Value of this fw_cfg file is 0 in
|
||||
such case.
|
||||
|
||||
Upstream Seabios has OpRegion and BDSM (pre-Gen11 device only) support.
|
||||
However, the support is not accepted by upstream EDK2/OVMF. A recommended
|
||||
solution is to create a virtual OpRom with following DXE drivers:
|
||||
|
||||
+10
-9
@@ -74,10 +74,10 @@ static bool vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev,
|
||||
unsigned int irq, Error **errp)
|
||||
{
|
||||
int fd;
|
||||
size_t argsz;
|
||||
int ret;
|
||||
IOHandler *fd_read;
|
||||
EventNotifier *notifier;
|
||||
g_autofree struct vfio_irq_info *irq_info = NULL;
|
||||
struct vfio_irq_info irq_info;
|
||||
VFIODevice *vdev = &vapdev->vdev;
|
||||
|
||||
switch (irq) {
|
||||
@@ -96,14 +96,15 @@ static bool vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev,
|
||||
return false;
|
||||
}
|
||||
|
||||
argsz = sizeof(*irq_info);
|
||||
irq_info = g_malloc0(argsz);
|
||||
irq_info->index = irq;
|
||||
irq_info->argsz = argsz;
|
||||
ret = vfio_device_get_irq_info(vdev, irq, &irq_info);
|
||||
|
||||
if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO,
|
||||
irq_info) < 0 || irq_info->count < 1) {
|
||||
error_setg_errno(errp, errno, "vfio: Error getting irq info");
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "vfio: Error getting irq info");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (irq_info.count < 1) {
|
||||
error_setg(errp, "vfio: Error getting irq info, count=0");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+11
-14
@@ -376,8 +376,8 @@ static bool vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev,
|
||||
Error **errp)
|
||||
{
|
||||
VFIODevice *vdev = &vcdev->vdev;
|
||||
g_autofree struct vfio_irq_info *irq_info = NULL;
|
||||
size_t argsz;
|
||||
struct vfio_irq_info irq_info;
|
||||
int ret;
|
||||
int fd;
|
||||
EventNotifier *notifier;
|
||||
IOHandler *fd_read;
|
||||
@@ -406,13 +406,15 @@ static bool vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev,
|
||||
return false;
|
||||
}
|
||||
|
||||
argsz = sizeof(*irq_info);
|
||||
irq_info = g_malloc0(argsz);
|
||||
irq_info->index = irq;
|
||||
irq_info->argsz = argsz;
|
||||
if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO,
|
||||
irq_info) < 0 || irq_info->count < 1) {
|
||||
error_setg_errno(errp, errno, "vfio: Error getting irq info");
|
||||
ret = vfio_device_get_irq_info(vdev, irq, &irq_info);
|
||||
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "vfio: Error getting irq info");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (irq_info.count < 1) {
|
||||
error_setg(errp, "vfio: Error getting irq info, count=0");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -502,7 +504,6 @@ static bool vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
|
||||
|
||||
vcdev->io_region_offset = info->offset;
|
||||
vcdev->io_region = g_malloc0(info->size);
|
||||
g_free(info);
|
||||
|
||||
/* check for the optional async command region */
|
||||
ret = vfio_device_get_region_info_type(vdev, VFIO_REGION_TYPE_CCW,
|
||||
@@ -515,7 +516,6 @@ static bool vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
|
||||
}
|
||||
vcdev->async_cmd_region_offset = info->offset;
|
||||
vcdev->async_cmd_region = g_malloc0(info->size);
|
||||
g_free(info);
|
||||
}
|
||||
|
||||
ret = vfio_device_get_region_info_type(vdev, VFIO_REGION_TYPE_CCW,
|
||||
@@ -528,7 +528,6 @@ static bool vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
|
||||
}
|
||||
vcdev->schib_region_offset = info->offset;
|
||||
vcdev->schib_region = g_malloc(info->size);
|
||||
g_free(info);
|
||||
}
|
||||
|
||||
ret = vfio_device_get_region_info_type(vdev, VFIO_REGION_TYPE_CCW,
|
||||
@@ -542,7 +541,6 @@ static bool vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
|
||||
}
|
||||
vcdev->crw_region_offset = info->offset;
|
||||
vcdev->crw_region = g_malloc(info->size);
|
||||
g_free(info);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -552,7 +550,6 @@ out_err:
|
||||
g_free(vcdev->schib_region);
|
||||
g_free(vcdev->async_cmd_region);
|
||||
g_free(vcdev->io_region);
|
||||
g_free(info);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,12 +85,12 @@ int vfio_container_dma_map(VFIOContainerBase *bcontainer,
|
||||
|
||||
int vfio_container_dma_unmap(VFIOContainerBase *bcontainer,
|
||||
hwaddr iova, ram_addr_t size,
|
||||
IOMMUTLBEntry *iotlb)
|
||||
IOMMUTLBEntry *iotlb, bool unmap_all)
|
||||
{
|
||||
VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
|
||||
|
||||
g_assert(vioc->dma_unmap);
|
||||
return vioc->dma_unmap(bcontainer, iova, size, iotlb);
|
||||
return vioc->dma_unmap(bcontainer, iova, size, iotlb, unmap_all);
|
||||
}
|
||||
|
||||
bool vfio_container_add_section_window(VFIOContainerBase *bcontainer,
|
||||
@@ -198,11 +198,7 @@ static int vfio_device_dma_logging_report(VFIODevice *vbasedev, hwaddr iova,
|
||||
feature->flags = VFIO_DEVICE_FEATURE_GET |
|
||||
VFIO_DEVICE_FEATURE_DMA_LOGGING_REPORT;
|
||||
|
||||
if (ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature)) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return vbasedev->io_ops->device_feature(vbasedev, feature);
|
||||
}
|
||||
|
||||
static int vfio_container_iommu_query_dirty_bitmap(const VFIOContainerBase *bcontainer,
|
||||
|
||||
+110
-80
@@ -119,12 +119,9 @@ unmap_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* DMA - Mapping and unmapping for the "type1" IOMMU interface used on x86
|
||||
*/
|
||||
static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer,
|
||||
hwaddr iova, ram_addr_t size,
|
||||
IOMMUTLBEntry *iotlb)
|
||||
static int vfio_legacy_dma_unmap_one(const VFIOContainerBase *bcontainer,
|
||||
hwaddr iova, ram_addr_t size,
|
||||
IOMMUTLBEntry *iotlb)
|
||||
{
|
||||
const VFIOContainer *container = container_of(bcontainer, VFIOContainer,
|
||||
bcontainer);
|
||||
@@ -181,6 +178,34 @@ static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* DMA - Mapping and unmapping for the "type1" IOMMU interface used on x86
|
||||
*/
|
||||
static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer,
|
||||
hwaddr iova, ram_addr_t size,
|
||||
IOMMUTLBEntry *iotlb, bool unmap_all)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (unmap_all) {
|
||||
/* The unmap ioctl doesn't accept a full 64-bit span. */
|
||||
Int128 llsize = int128_rshift(int128_2_64(), 1);
|
||||
|
||||
ret = vfio_legacy_dma_unmap_one(bcontainer, 0, int128_get64(llsize),
|
||||
iotlb);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = vfio_legacy_dma_unmap_one(bcontainer, int128_get64(llsize),
|
||||
int128_get64(llsize), iotlb);
|
||||
}
|
||||
|
||||
} else {
|
||||
ret = vfio_legacy_dma_unmap_one(bcontainer, iova, size, iotlb);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vfio_legacy_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova,
|
||||
ram_addr_t size, void *vaddr, bool readonly)
|
||||
{
|
||||
@@ -205,7 +230,7 @@ static int vfio_legacy_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova,
|
||||
*/
|
||||
if (ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map) == 0 ||
|
||||
(errno == EBUSY &&
|
||||
vfio_legacy_dma_unmap(bcontainer, iova, size, NULL) == 0 &&
|
||||
vfio_legacy_dma_unmap(bcontainer, iova, size, NULL, false) == 0 &&
|
||||
ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map) == 0)) {
|
||||
return 0;
|
||||
}
|
||||
@@ -511,16 +536,10 @@ static bool vfio_legacy_setup(VFIOContainerBase *bcontainer, Error **errp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as,
|
||||
Error **errp)
|
||||
static bool vfio_container_attach_discard_disable(VFIOContainer *container,
|
||||
VFIOGroup *group, Error **errp)
|
||||
{
|
||||
VFIOContainer *container;
|
||||
VFIOContainerBase *bcontainer;
|
||||
int ret, fd;
|
||||
VFIOAddressSpace *space;
|
||||
VFIOIOMMUClass *vioc;
|
||||
|
||||
space = vfio_address_space_get(as);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* VFIO is currently incompatible with discarding of RAM insofar as the
|
||||
@@ -553,97 +572,118 @@ static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as,
|
||||
* details once we know which type of IOMMU we are using.
|
||||
*/
|
||||
|
||||
ret = vfio_ram_block_discard_disable(container, true);
|
||||
if (ret) {
|
||||
error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken");
|
||||
if (ioctl(group->fd, VFIO_GROUP_UNSET_CONTAINER, &container->fd)) {
|
||||
error_report("vfio: error disconnecting group %d from"
|
||||
" container", group->groupid);
|
||||
}
|
||||
}
|
||||
return !ret;
|
||||
}
|
||||
|
||||
static bool vfio_container_group_add(VFIOContainer *container, VFIOGroup *group,
|
||||
Error **errp)
|
||||
{
|
||||
if (!vfio_container_attach_discard_disable(container, group, errp)) {
|
||||
return false;
|
||||
}
|
||||
group->container = container;
|
||||
QLIST_INSERT_HEAD(&container->group_list, group, container_next);
|
||||
vfio_group_add_kvm_device(group);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void vfio_container_group_del(VFIOContainer *container, VFIOGroup *group)
|
||||
{
|
||||
QLIST_REMOVE(group, container_next);
|
||||
group->container = NULL;
|
||||
vfio_group_del_kvm_device(group);
|
||||
vfio_ram_block_discard_disable(container, false);
|
||||
}
|
||||
|
||||
static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as,
|
||||
Error **errp)
|
||||
{
|
||||
VFIOContainer *container;
|
||||
VFIOContainerBase *bcontainer;
|
||||
int ret, fd = -1;
|
||||
VFIOAddressSpace *space;
|
||||
VFIOIOMMUClass *vioc = NULL;
|
||||
bool new_container = false;
|
||||
bool group_was_added = false;
|
||||
|
||||
space = vfio_address_space_get(as);
|
||||
|
||||
QLIST_FOREACH(bcontainer, &space->containers, next) {
|
||||
container = container_of(bcontainer, VFIOContainer, bcontainer);
|
||||
if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) {
|
||||
ret = vfio_ram_block_discard_disable(container, true);
|
||||
if (ret) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Cannot set discarding of RAM broken");
|
||||
if (ioctl(group->fd, VFIO_GROUP_UNSET_CONTAINER,
|
||||
&container->fd)) {
|
||||
error_report("vfio: error disconnecting group %d from"
|
||||
" container", group->groupid);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
group->container = container;
|
||||
QLIST_INSERT_HEAD(&container->group_list, group, container_next);
|
||||
vfio_group_add_kvm_device(group);
|
||||
return true;
|
||||
return vfio_container_group_add(container, group, errp);
|
||||
}
|
||||
}
|
||||
|
||||
fd = qemu_open("/dev/vfio/vfio", O_RDWR, errp);
|
||||
if (fd < 0) {
|
||||
goto put_space_exit;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = ioctl(fd, VFIO_GET_API_VERSION);
|
||||
if (ret != VFIO_API_VERSION) {
|
||||
error_setg(errp, "supported vfio version: %d, "
|
||||
"reported version: %d", VFIO_API_VERSION, ret);
|
||||
goto close_fd_exit;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
container = vfio_create_container(fd, group, errp);
|
||||
if (!container) {
|
||||
goto close_fd_exit;
|
||||
goto fail;
|
||||
}
|
||||
new_container = true;
|
||||
bcontainer = &container->bcontainer;
|
||||
|
||||
if (!vfio_cpr_register_container(bcontainer, errp)) {
|
||||
goto free_container_exit;
|
||||
}
|
||||
|
||||
ret = vfio_ram_block_discard_disable(container, true);
|
||||
if (ret) {
|
||||
error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken");
|
||||
goto unregister_container_exit;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vioc = VFIO_IOMMU_GET_CLASS(bcontainer);
|
||||
assert(vioc->setup);
|
||||
|
||||
if (!vioc->setup(bcontainer, errp)) {
|
||||
goto enable_discards_exit;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vfio_group_add_kvm_device(group);
|
||||
|
||||
vfio_address_space_insert(space, bcontainer);
|
||||
|
||||
group->container = container;
|
||||
QLIST_INSERT_HEAD(&container->group_list, group, container_next);
|
||||
if (!vfio_container_group_add(container, group, errp)) {
|
||||
goto fail;
|
||||
}
|
||||
group_was_added = true;
|
||||
|
||||
if (!vfio_listener_register(bcontainer, errp)) {
|
||||
goto listener_release_exit;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bcontainer->initialized = true;
|
||||
|
||||
return true;
|
||||
listener_release_exit:
|
||||
QLIST_REMOVE(group, container_next);
|
||||
vfio_group_del_kvm_device(group);
|
||||
|
||||
fail:
|
||||
vfio_listener_unregister(bcontainer);
|
||||
if (vioc->release) {
|
||||
|
||||
if (group_was_added) {
|
||||
vfio_container_group_del(container, group);
|
||||
}
|
||||
if (vioc && vioc->release) {
|
||||
vioc->release(bcontainer);
|
||||
}
|
||||
|
||||
enable_discards_exit:
|
||||
vfio_ram_block_discard_disable(container, false);
|
||||
|
||||
unregister_container_exit:
|
||||
vfio_cpr_unregister_container(bcontainer);
|
||||
|
||||
free_container_exit:
|
||||
object_unref(container);
|
||||
|
||||
close_fd_exit:
|
||||
close(fd);
|
||||
|
||||
put_space_exit:
|
||||
if (new_container) {
|
||||
vfio_cpr_unregister_container(bcontainer);
|
||||
object_unref(container);
|
||||
}
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
}
|
||||
vfio_address_space_put(space);
|
||||
|
||||
return false;
|
||||
@@ -811,18 +851,14 @@ static bool vfio_device_get(VFIOGroup *group, const char *name,
|
||||
}
|
||||
}
|
||||
|
||||
vfio_device_prepare(vbasedev, &group->container->bcontainer, info);
|
||||
|
||||
vbasedev->fd = fd;
|
||||
vbasedev->group = group;
|
||||
QLIST_INSERT_HEAD(&group->device_list, vbasedev, next);
|
||||
|
||||
vbasedev->num_irqs = info->num_irqs;
|
||||
vbasedev->num_regions = info->num_regions;
|
||||
vbasedev->flags = info->flags;
|
||||
|
||||
trace_vfio_device_get(name, info->flags, info->num_regions, info->num_irqs);
|
||||
|
||||
vbasedev->reset_works = !!(info->flags & VFIO_DEVICE_FLAGS_RESET);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -875,7 +911,6 @@ static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev,
|
||||
int groupid = vfio_device_get_groupid(vbasedev, errp);
|
||||
VFIODevice *vbasedev_iter;
|
||||
VFIOGroup *group;
|
||||
VFIOContainerBase *bcontainer;
|
||||
|
||||
if (groupid < 0) {
|
||||
return false;
|
||||
@@ -904,11 +939,6 @@ static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev,
|
||||
goto device_put_exit;
|
||||
}
|
||||
|
||||
bcontainer = &group->container->bcontainer;
|
||||
vbasedev->bcontainer = bcontainer;
|
||||
QLIST_INSERT_HEAD(&bcontainer->device_list, vbasedev, container_next);
|
||||
QLIST_INSERT_HEAD(&vfio_device_list, vbasedev, global_next);
|
||||
|
||||
return true;
|
||||
|
||||
device_put_exit:
|
||||
@@ -922,10 +952,10 @@ static void vfio_legacy_detach_device(VFIODevice *vbasedev)
|
||||
{
|
||||
VFIOGroup *group = vbasedev->group;
|
||||
|
||||
QLIST_REMOVE(vbasedev, global_next);
|
||||
QLIST_REMOVE(vbasedev, container_next);
|
||||
vbasedev->bcontainer = NULL;
|
||||
trace_vfio_device_detach(vbasedev->name, group->groupid);
|
||||
|
||||
vfio_device_unprepare(vbasedev);
|
||||
|
||||
object_unref(vbasedev->hiod);
|
||||
vfio_device_put(vbasedev);
|
||||
vfio_group_put(group);
|
||||
|
||||
+165
-18
@@ -82,7 +82,7 @@ void vfio_device_irq_disable(VFIODevice *vbasedev, int index)
|
||||
.count = 0,
|
||||
};
|
||||
|
||||
ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set);
|
||||
vbasedev->io_ops->set_irqs(vbasedev, &irq_set);
|
||||
}
|
||||
|
||||
void vfio_device_irq_unmask(VFIODevice *vbasedev, int index)
|
||||
@@ -95,7 +95,7 @@ void vfio_device_irq_unmask(VFIODevice *vbasedev, int index)
|
||||
.count = 1,
|
||||
};
|
||||
|
||||
ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set);
|
||||
vbasedev->io_ops->set_irqs(vbasedev, &irq_set);
|
||||
}
|
||||
|
||||
void vfio_device_irq_mask(VFIODevice *vbasedev, int index)
|
||||
@@ -108,7 +108,7 @@ void vfio_device_irq_mask(VFIODevice *vbasedev, int index)
|
||||
.count = 1,
|
||||
};
|
||||
|
||||
ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set);
|
||||
vbasedev->io_ops->set_irqs(vbasedev, &irq_set);
|
||||
}
|
||||
|
||||
static inline const char *action_to_str(int action)
|
||||
@@ -167,7 +167,7 @@ bool vfio_device_irq_set_signaling(VFIODevice *vbasedev, int index, int subindex
|
||||
pfd = (int32_t *)&irq_set->data;
|
||||
*pfd = fd;
|
||||
|
||||
if (!ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set)) {
|
||||
if (!vbasedev->io_ops->set_irqs(vbasedev, irq_set)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -185,10 +185,28 @@ bool vfio_device_irq_set_signaling(VFIODevice *vbasedev, int index, int subindex
|
||||
return false;
|
||||
}
|
||||
|
||||
int vfio_device_get_irq_info(VFIODevice *vbasedev, int index,
|
||||
struct vfio_irq_info *info)
|
||||
{
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
info->argsz = sizeof(*info);
|
||||
info->index = index;
|
||||
|
||||
return vbasedev->io_ops->get_irq_info(vbasedev, info);
|
||||
}
|
||||
|
||||
int vfio_device_get_region_info(VFIODevice *vbasedev, int index,
|
||||
struct vfio_region_info **info)
|
||||
{
|
||||
size_t argsz = sizeof(struct vfio_region_info);
|
||||
int ret;
|
||||
|
||||
/* check cache */
|
||||
if (vbasedev->reginfo[index] != NULL) {
|
||||
*info = vbasedev->reginfo[index];
|
||||
return 0;
|
||||
}
|
||||
|
||||
*info = g_malloc0(argsz);
|
||||
|
||||
@@ -196,10 +214,11 @@ int vfio_device_get_region_info(VFIODevice *vbasedev, int index,
|
||||
retry:
|
||||
(*info)->argsz = argsz;
|
||||
|
||||
if (ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, *info)) {
|
||||
ret = vbasedev->io_ops->get_region_info(vbasedev, *info);
|
||||
if (ret != 0) {
|
||||
g_free(*info);
|
||||
*info = NULL;
|
||||
return -errno;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((*info)->argsz > argsz) {
|
||||
@@ -209,6 +228,9 @@ retry:
|
||||
goto retry;
|
||||
}
|
||||
|
||||
/* fill cache */
|
||||
vbasedev->reginfo[index] = *info;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -227,7 +249,6 @@ int vfio_device_get_region_info_type(VFIODevice *vbasedev, uint32_t type,
|
||||
|
||||
hdr = vfio_get_region_info_cap(*info, VFIO_REGION_INFO_CAP_TYPE);
|
||||
if (!hdr) {
|
||||
g_free(*info);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -239,8 +260,6 @@ int vfio_device_get_region_info_type(VFIODevice *vbasedev, uint32_t type,
|
||||
if (cap_type->type == type && cap_type->subtype == subtype) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_free(*info);
|
||||
}
|
||||
|
||||
*info = NULL;
|
||||
@@ -249,7 +268,7 @@ int vfio_device_get_region_info_type(VFIODevice *vbasedev, uint32_t type,
|
||||
|
||||
bool vfio_device_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type)
|
||||
{
|
||||
g_autofree struct vfio_region_info *info = NULL;
|
||||
struct vfio_region_info *info = NULL;
|
||||
bool ret = false;
|
||||
|
||||
if (!vfio_device_get_region_info(vbasedev, region, &info)) {
|
||||
@@ -305,11 +324,14 @@ void vfio_device_set_fd(VFIODevice *vbasedev, const char *str, Error **errp)
|
||||
vbasedev->fd = fd;
|
||||
}
|
||||
|
||||
static VFIODeviceIOOps vfio_device_io_ops_ioctl;
|
||||
|
||||
void vfio_device_init(VFIODevice *vbasedev, int type, VFIODeviceOps *ops,
|
||||
DeviceState *dev, bool ram_discard)
|
||||
{
|
||||
vbasedev->type = type;
|
||||
vbasedev->ops = ops;
|
||||
vbasedev->io_ops = &vfio_device_io_ops_ioctl;
|
||||
vbasedev->dev = dev;
|
||||
vbasedev->fd = -1;
|
||||
|
||||
@@ -370,27 +392,35 @@ bool vfio_device_hiod_create_and_realize(VFIODevice *vbasedev,
|
||||
VFIODevice *vfio_get_vfio_device(Object *obj)
|
||||
{
|
||||
if (object_dynamic_cast(obj, TYPE_VFIO_PCI)) {
|
||||
return &VFIO_PCI(obj)->vbasedev;
|
||||
return &VFIO_PCI_BASE(obj)->vbasedev;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool vfio_device_attach(char *name, VFIODevice *vbasedev,
|
||||
AddressSpace *as, Error **errp)
|
||||
bool vfio_device_attach_by_iommu_type(const char *iommu_type, char *name,
|
||||
VFIODevice *vbasedev, AddressSpace *as,
|
||||
Error **errp)
|
||||
{
|
||||
const VFIOIOMMUClass *ops =
|
||||
VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_LEGACY));
|
||||
|
||||
if (vbasedev->iommufd) {
|
||||
ops = VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_IOMMUFD));
|
||||
}
|
||||
VFIO_IOMMU_CLASS(object_class_by_name(iommu_type));
|
||||
|
||||
assert(ops);
|
||||
|
||||
return ops->attach_device(name, vbasedev, as, errp);
|
||||
}
|
||||
|
||||
bool vfio_device_attach(char *name, VFIODevice *vbasedev,
|
||||
AddressSpace *as, Error **errp)
|
||||
{
|
||||
const char *iommu_type = vbasedev->iommufd ?
|
||||
TYPE_VFIO_IOMMU_IOMMUFD :
|
||||
TYPE_VFIO_IOMMU_LEGACY;
|
||||
|
||||
return vfio_device_attach_by_iommu_type(iommu_type, name, vbasedev,
|
||||
as, errp);
|
||||
}
|
||||
|
||||
void vfio_device_detach(VFIODevice *vbasedev)
|
||||
{
|
||||
if (!vbasedev->bcontainer) {
|
||||
@@ -398,3 +428,120 @@ void vfio_device_detach(VFIODevice *vbasedev)
|
||||
}
|
||||
VFIO_IOMMU_GET_CLASS(vbasedev->bcontainer)->detach_device(vbasedev);
|
||||
}
|
||||
|
||||
void vfio_device_prepare(VFIODevice *vbasedev, VFIOContainerBase *bcontainer,
|
||||
struct vfio_device_info *info)
|
||||
{
|
||||
vbasedev->num_irqs = info->num_irqs;
|
||||
vbasedev->num_regions = info->num_regions;
|
||||
vbasedev->flags = info->flags;
|
||||
vbasedev->reset_works = !!(info->flags & VFIO_DEVICE_FLAGS_RESET);
|
||||
|
||||
vbasedev->bcontainer = bcontainer;
|
||||
QLIST_INSERT_HEAD(&bcontainer->device_list, vbasedev, container_next);
|
||||
|
||||
QLIST_INSERT_HEAD(&vfio_device_list, vbasedev, global_next);
|
||||
|
||||
vbasedev->reginfo = g_new0(struct vfio_region_info *,
|
||||
vbasedev->num_regions);
|
||||
}
|
||||
|
||||
void vfio_device_unprepare(VFIODevice *vbasedev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < vbasedev->num_regions; i++) {
|
||||
g_free(vbasedev->reginfo[i]);
|
||||
}
|
||||
g_free(vbasedev->reginfo);
|
||||
vbasedev->reginfo = NULL;
|
||||
|
||||
QLIST_REMOVE(vbasedev, container_next);
|
||||
QLIST_REMOVE(vbasedev, global_next);
|
||||
vbasedev->bcontainer = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Traditional ioctl() based io
|
||||
*/
|
||||
|
||||
static int vfio_device_io_device_feature(VFIODevice *vbasedev,
|
||||
struct vfio_device_feature *feature)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature);
|
||||
|
||||
return ret < 0 ? -errno : ret;
|
||||
}
|
||||
|
||||
static int vfio_device_io_get_region_info(VFIODevice *vbasedev,
|
||||
struct vfio_region_info *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, info);
|
||||
|
||||
return ret < 0 ? -errno : ret;
|
||||
}
|
||||
|
||||
static int vfio_device_io_get_irq_info(VFIODevice *vbasedev,
|
||||
struct vfio_irq_info *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_IRQ_INFO, info);
|
||||
|
||||
return ret < 0 ? -errno : ret;
|
||||
}
|
||||
|
||||
static int vfio_device_io_set_irqs(VFIODevice *vbasedev,
|
||||
struct vfio_irq_set *irqs)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irqs);
|
||||
|
||||
return ret < 0 ? -errno : ret;
|
||||
}
|
||||
|
||||
static int vfio_device_io_region_read(VFIODevice *vbasedev, uint8_t index,
|
||||
off_t off, uint32_t size, void *data)
|
||||
{
|
||||
struct vfio_region_info *info;
|
||||
int ret;
|
||||
|
||||
ret = vfio_device_get_region_info(vbasedev, index, &info);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = pread(vbasedev->fd, data, size, info->offset + off);
|
||||
|
||||
return ret < 0 ? -errno : ret;
|
||||
}
|
||||
|
||||
static int vfio_device_io_region_write(VFIODevice *vbasedev, uint8_t index,
|
||||
off_t off, uint32_t size, void *data)
|
||||
{
|
||||
struct vfio_region_info *info;
|
||||
int ret;
|
||||
|
||||
ret = vfio_device_get_region_info(vbasedev, index, &info);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = pwrite(vbasedev->fd, data, size, info->offset + off);
|
||||
|
||||
return ret < 0 ? -errno : ret;
|
||||
}
|
||||
|
||||
static VFIODeviceIOOps vfio_device_io_ops_ioctl = {
|
||||
.device_feature = vfio_device_io_device_feature,
|
||||
.get_region_info = vfio_device_io_get_region_info,
|
||||
.get_irq_info = vfio_device_io_get_irq_info,
|
||||
.set_irqs = vfio_device_io_set_irqs,
|
||||
.region_read = vfio_device_io_region_read,
|
||||
.region_write = vfio_device_io_region_write,
|
||||
};
|
||||
|
||||
+129
-93
@@ -103,6 +103,7 @@ static int igd_gen(VFIOPCIDevice *vdev)
|
||||
/*
|
||||
* Unfortunately, Intel changes it's specification quite often. This makes
|
||||
* it impossible to use a suitable default value for unknown devices.
|
||||
* Return -1 for not applying any generation-specific quirks.
|
||||
*/
|
||||
return -1;
|
||||
}
|
||||
@@ -182,16 +183,13 @@ static bool vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev,
|
||||
|
||||
trace_vfio_pci_igd_opregion_enabled(vdev->vbasedev.name);
|
||||
|
||||
pci_set_long(vdev->pdev.config + IGD_ASLS, 0);
|
||||
pci_set_long(vdev->pdev.wmask + IGD_ASLS, ~0);
|
||||
pci_set_long(vdev->emulated_config_bits + IGD_ASLS, ~0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool vfio_pci_igd_setup_opregion(VFIOPCIDevice *vdev, Error **errp)
|
||||
static bool vfio_pci_igd_opregion_detect(VFIOPCIDevice *vdev,
|
||||
struct vfio_region_info **opregion,
|
||||
Error **errp)
|
||||
{
|
||||
g_autofree struct vfio_region_info *opregion = NULL;
|
||||
int ret;
|
||||
|
||||
/* Hotplugging is not supported for opregion access */
|
||||
@@ -202,17 +200,13 @@ static bool vfio_pci_igd_setup_opregion(VFIOPCIDevice *vdev, Error **errp)
|
||||
|
||||
ret = vfio_device_get_region_info_type(&vdev->vbasedev,
|
||||
VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL,
|
||||
VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, &opregion);
|
||||
VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, opregion);
|
||||
if (ret) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Device does not supports IGD OpRegion feature");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!vfio_pci_igd_opregion_init(vdev, opregion, errp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -355,8 +349,8 @@ static int vfio_pci_igd_lpc_init(VFIOPCIDevice *vdev,
|
||||
|
||||
static bool vfio_pci_igd_setup_lpc_bridge(VFIOPCIDevice *vdev, Error **errp)
|
||||
{
|
||||
g_autofree struct vfio_region_info *host = NULL;
|
||||
g_autofree struct vfio_region_info *lpc = NULL;
|
||||
struct vfio_region_info *host = NULL;
|
||||
struct vfio_region_info *lpc = NULL;
|
||||
PCIDevice *lpc_bridge;
|
||||
int ret;
|
||||
|
||||
@@ -419,6 +413,44 @@ static bool vfio_pci_igd_setup_lpc_bridge(VFIOPCIDevice *vdev, Error **errp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool vfio_pci_igd_override_gms(int gen, uint32_t gms, uint32_t *gmch)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if (gen == -1) {
|
||||
error_report("x-igd-gms is not supported on this device");
|
||||
} else if (gen < 8) {
|
||||
if (gms <= 0x10) {
|
||||
*gmch &= ~(IGD_GMCH_GEN6_GMS_MASK << IGD_GMCH_GEN6_GMS_SHIFT);
|
||||
*gmch |= gms << IGD_GMCH_GEN6_GMS_SHIFT;
|
||||
ret = true;
|
||||
} else {
|
||||
error_report(QERR_INVALID_PARAMETER_VALUE, "x-igd-gms", "0~0x10");
|
||||
}
|
||||
} else if (gen == 8) {
|
||||
if (gms <= 0x40) {
|
||||
*gmch &= ~(IGD_GMCH_GEN8_GMS_MASK << IGD_GMCH_GEN8_GMS_SHIFT);
|
||||
*gmch |= gms << IGD_GMCH_GEN8_GMS_SHIFT;
|
||||
ret = true;
|
||||
} else {
|
||||
error_report(QERR_INVALID_PARAMETER_VALUE, "x-igd-gms", "0~0x40");
|
||||
}
|
||||
} else {
|
||||
/* 0x0 to 0x40: 32MB increments starting at 0MB */
|
||||
/* 0xf0 to 0xfe: 4MB increments starting at 4MB */
|
||||
if ((gms <= 0x40) || (gms >= 0xf0 && gms <= 0xfe)) {
|
||||
*gmch &= ~(IGD_GMCH_GEN8_GMS_MASK << IGD_GMCH_GEN8_GMS_SHIFT);
|
||||
*gmch |= gms << IGD_GMCH_GEN8_GMS_SHIFT;
|
||||
ret = true;
|
||||
} else {
|
||||
error_report(QERR_INVALID_PARAMETER_VALUE,
|
||||
"x-igd-gms", "0~0x40 or 0xf0~0xfe");
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define IGD_GGC_MMIO_OFFSET 0x108040
|
||||
#define IGD_BDSM_MMIO_OFFSET 0x1080C0
|
||||
|
||||
@@ -428,41 +460,35 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr)
|
||||
VFIOConfigMirrorQuirk *ggc_mirror, *bdsm_mirror;
|
||||
int gen;
|
||||
|
||||
/*
|
||||
* This must be an Intel VGA device at address 00:02.0 for us to even
|
||||
* consider enabling legacy mode. Some driver have dependencies on the PCI
|
||||
* bus address.
|
||||
*/
|
||||
if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) ||
|
||||
!vfio_is_vga(vdev) || nr != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only on IGD devices of gen 11 and above, the BDSM register is mirrored
|
||||
* into MMIO space and read from MMIO space by the Windows driver.
|
||||
*/
|
||||
/* Only on IGD Gen6-12 device needs quirks in BAR 0 */
|
||||
gen = igd_gen(vdev);
|
||||
if (gen < 6) {
|
||||
return;
|
||||
}
|
||||
|
||||
ggc_quirk = vfio_quirk_alloc(1);
|
||||
ggc_mirror = ggc_quirk->data = g_malloc0(sizeof(*ggc_mirror));
|
||||
ggc_mirror->mem = ggc_quirk->mem;
|
||||
ggc_mirror->vdev = vdev;
|
||||
ggc_mirror->bar = nr;
|
||||
ggc_mirror->offset = IGD_GGC_MMIO_OFFSET;
|
||||
ggc_mirror->config_offset = IGD_GMCH;
|
||||
if (vdev->igd_gms) {
|
||||
ggc_quirk = vfio_quirk_alloc(1);
|
||||
ggc_mirror = ggc_quirk->data = g_malloc0(sizeof(*ggc_mirror));
|
||||
ggc_mirror->mem = ggc_quirk->mem;
|
||||
ggc_mirror->vdev = vdev;
|
||||
ggc_mirror->bar = nr;
|
||||
ggc_mirror->offset = IGD_GGC_MMIO_OFFSET;
|
||||
ggc_mirror->config_offset = IGD_GMCH;
|
||||
|
||||
memory_region_init_io(ggc_mirror->mem, OBJECT(vdev),
|
||||
&vfio_generic_mirror_quirk, ggc_mirror,
|
||||
"vfio-igd-ggc-quirk", 2);
|
||||
memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
|
||||
ggc_mirror->offset, ggc_mirror->mem,
|
||||
1);
|
||||
memory_region_init_io(ggc_mirror->mem, OBJECT(vdev),
|
||||
&vfio_generic_mirror_quirk, ggc_mirror,
|
||||
"vfio-igd-ggc-quirk", 2);
|
||||
memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
|
||||
ggc_mirror->offset, ggc_mirror->mem,
|
||||
1);
|
||||
|
||||
QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, ggc_quirk, next);
|
||||
QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, ggc_quirk, next);
|
||||
}
|
||||
|
||||
bdsm_quirk = vfio_quirk_alloc(1);
|
||||
bdsm_mirror = bdsm_quirk->data = g_malloc0(sizeof(*bdsm_mirror));
|
||||
@@ -484,44 +510,37 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr)
|
||||
|
||||
static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp)
|
||||
{
|
||||
struct vfio_region_info *opregion = NULL;
|
||||
int ret, gen;
|
||||
uint64_t gms_size;
|
||||
uint64_t gms_size = 0;
|
||||
uint64_t *bdsm_size;
|
||||
uint32_t gmch;
|
||||
bool legacy_mode_enabled = false;
|
||||
Error *err = NULL;
|
||||
|
||||
/*
|
||||
* This must be an Intel VGA device at address 00:02.0 for us to even
|
||||
* consider enabling legacy mode. The vBIOS has dependencies on the
|
||||
* PCI bus address.
|
||||
*/
|
||||
if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) ||
|
||||
!vfio_is_vga(vdev)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* IGD is not a standard, they like to change their specs often. We
|
||||
* only attempt to support back to SandBridge and we hope that newer
|
||||
* devices maintain compatibility with generation 8.
|
||||
*/
|
||||
gen = igd_gen(vdev);
|
||||
if (gen == -1) {
|
||||
error_report("IGD device %s is unsupported in legacy mode, "
|
||||
"try SandyBridge or newer", vdev->vbasedev.name);
|
||||
/* IGD device always comes with OpRegion */
|
||||
if (!vfio_pci_igd_opregion_detect(vdev, &opregion, errp)) {
|
||||
return true;
|
||||
}
|
||||
info_report("OpRegion detected on Intel display %x.", vdev->device_id);
|
||||
|
||||
gen = igd_gen(vdev);
|
||||
gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4);
|
||||
|
||||
/*
|
||||
* For backward compatibility, enable legacy mode when
|
||||
* - Device geneation is 6 to 9 (including both)
|
||||
* - Machine type is i440fx (pc_piix)
|
||||
* - IGD device is at guest BDF 00:02.0
|
||||
* - Not manually disabled by x-igd-legacy-mode=off
|
||||
*/
|
||||
if ((vdev->igd_legacy_mode != ON_OFF_AUTO_OFF) &&
|
||||
(gen >= 6 && gen <= 9) &&
|
||||
!strcmp(MACHINE_GET_CLASS(qdev_get_machine())->family, "pc_piix") &&
|
||||
(&vdev->pdev == pci_find_device(pci_device_root_bus(&vdev->pdev),
|
||||
0, PCI_DEVFN(0x2, 0)))) {
|
||||
@@ -532,7 +551,7 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp)
|
||||
* - OpRegion
|
||||
* - Same LPC bridge and Host bridge VID/DID/SVID/SSID as host
|
||||
*/
|
||||
g_autofree struct vfio_region_info *rom = NULL;
|
||||
struct vfio_region_info *rom = NULL;
|
||||
|
||||
legacy_mode_enabled = true;
|
||||
info_report("IGD legacy mode enabled, "
|
||||
@@ -566,13 +585,15 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp)
|
||||
vdev->features |= VFIO_FEATURE_ENABLE_IGD_LPC;
|
||||
} else if (vdev->igd_legacy_mode == ON_OFF_AUTO_ON) {
|
||||
error_setg(&err,
|
||||
"Machine is not i440fx or assigned BDF is not 00:02.0");
|
||||
"Machine is not i440fx, assigned BDF is not 00:02.0, "
|
||||
"or device %04x (gen %d) doesn't support legacy mode",
|
||||
vdev->device_id, gen);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Setup OpRegion access */
|
||||
if ((vdev->features & VFIO_FEATURE_ENABLE_IGD_OPREGION) &&
|
||||
!vfio_pci_igd_setup_opregion(vdev, errp)) {
|
||||
!vfio_pci_igd_opregion_init(vdev, opregion, errp)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -580,7 +601,15 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp)
|
||||
if ((vdev->features & VFIO_FEATURE_ENABLE_IGD_LPC) &&
|
||||
!vfio_pci_igd_setup_lpc_bridge(vdev, errp)) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ASLS (OpRegion address) is read-only, emulated
|
||||
* It contains HPA, guest firmware need to reprogram it with GPA.
|
||||
*/
|
||||
pci_set_long(vdev->pdev.config + IGD_ASLS, 0);
|
||||
pci_set_long(vdev->pdev.wmask + IGD_ASLS, ~0);
|
||||
pci_set_long(vdev->emulated_config_bits + IGD_ASLS, ~0);
|
||||
|
||||
/*
|
||||
* Allow user to override dsm size using x-igd-gms option, in multiples of
|
||||
@@ -588,56 +617,44 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp)
|
||||
* set from DVMT Pre-Allocated option in host BIOS.
|
||||
*/
|
||||
if (vdev->igd_gms) {
|
||||
if (gen < 8) {
|
||||
if (vdev->igd_gms <= 0x10) {
|
||||
gmch &= ~(IGD_GMCH_GEN6_GMS_MASK << IGD_GMCH_GEN6_GMS_SHIFT);
|
||||
gmch |= vdev->igd_gms << IGD_GMCH_GEN6_GMS_SHIFT;
|
||||
} else {
|
||||
error_report(QERR_INVALID_PARAMETER_VALUE,
|
||||
"x-igd-gms", "0~0x10");
|
||||
}
|
||||
} else {
|
||||
if (vdev->igd_gms <= 0x40) {
|
||||
gmch &= ~(IGD_GMCH_GEN8_GMS_MASK << IGD_GMCH_GEN8_GMS_SHIFT);
|
||||
gmch |= vdev->igd_gms << IGD_GMCH_GEN8_GMS_SHIFT;
|
||||
} else {
|
||||
error_report(QERR_INVALID_PARAMETER_VALUE,
|
||||
"x-igd-gms", "0~0x40");
|
||||
}
|
||||
if (!vfio_pci_igd_override_gms(gen, vdev->igd_gms, &gmch)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* GMCH is read-only, emulated */
|
||||
pci_set_long(vdev->pdev.config + IGD_GMCH, gmch);
|
||||
pci_set_long(vdev->pdev.wmask + IGD_GMCH, 0);
|
||||
pci_set_long(vdev->emulated_config_bits + IGD_GMCH, ~0);
|
||||
}
|
||||
|
||||
gms_size = igd_stolen_memory_size(gen, gmch);
|
||||
if (gen > 0) {
|
||||
gms_size = igd_stolen_memory_size(gen, gmch);
|
||||
|
||||
/* BDSM is read-write, emulated. BIOS needs to be able to write it */
|
||||
if (gen < 11) {
|
||||
pci_set_long(vdev->pdev.config + IGD_BDSM, 0);
|
||||
pci_set_long(vdev->pdev.wmask + IGD_BDSM, ~0);
|
||||
pci_set_long(vdev->emulated_config_bits + IGD_BDSM, ~0);
|
||||
} else {
|
||||
pci_set_quad(vdev->pdev.config + IGD_BDSM_GEN11, 0);
|
||||
pci_set_quad(vdev->pdev.wmask + IGD_BDSM_GEN11, ~0);
|
||||
pci_set_quad(vdev->emulated_config_bits + IGD_BDSM_GEN11, ~0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Request reserved memory for stolen memory via fw_cfg. VM firmware
|
||||
* must allocate a 1MB aligned reserved memory region below 4GB with
|
||||
* the requested size (in bytes) for use by the Intel PCI class VGA
|
||||
* device at VM address 00:02.0. The base address of this reserved
|
||||
* memory region must be written to the device BDSM register at PCI
|
||||
* config offset 0x5C.
|
||||
* the requested size (in bytes) for use by the IGD device. The base
|
||||
* address of this reserved memory region must be written to the
|
||||
* device BDSM register.
|
||||
* For newer device without BDSM register, this fw_cfg item is 0.
|
||||
*/
|
||||
bdsm_size = g_malloc(sizeof(*bdsm_size));
|
||||
*bdsm_size = cpu_to_le64(gms_size);
|
||||
fw_cfg_add_file(fw_cfg_find(), "etc/igd-bdsm-size",
|
||||
bdsm_size, sizeof(*bdsm_size));
|
||||
|
||||
/* GMCH is read-only, emulated */
|
||||
pci_set_long(vdev->pdev.config + IGD_GMCH, gmch);
|
||||
pci_set_long(vdev->pdev.wmask + IGD_GMCH, 0);
|
||||
pci_set_long(vdev->emulated_config_bits + IGD_GMCH, ~0);
|
||||
|
||||
/* BDSM is read-write, emulated. The BIOS needs to be able to write it */
|
||||
if (gen < 11) {
|
||||
pci_set_long(vdev->pdev.config + IGD_BDSM, 0);
|
||||
pci_set_long(vdev->pdev.wmask + IGD_BDSM, ~0);
|
||||
pci_set_long(vdev->emulated_config_bits + IGD_BDSM, ~0);
|
||||
} else {
|
||||
pci_set_quad(vdev->pdev.config + IGD_BDSM_GEN11, 0);
|
||||
pci_set_quad(vdev->pdev.wmask + IGD_BDSM_GEN11, ~0);
|
||||
pci_set_quad(vdev->emulated_config_bits + IGD_BDSM_GEN11, ~0);
|
||||
}
|
||||
|
||||
trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, (gms_size / MiB));
|
||||
|
||||
return true;
|
||||
@@ -664,8 +681,27 @@ error:
|
||||
*/
|
||||
static bool vfio_pci_kvmgt_config_quirk(VFIOPCIDevice *vdev, Error **errp)
|
||||
{
|
||||
struct vfio_region_info *opregion = NULL;
|
||||
int gen;
|
||||
|
||||
if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) ||
|
||||
!vfio_is_vga(vdev)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* FIXME: Cherryview is Gen8, but don't support GVT-g */
|
||||
gen = igd_gen(vdev);
|
||||
if (gen != 8 && gen != 9) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!vfio_pci_igd_opregion_detect(vdev, &opregion, errp)) {
|
||||
/* Should never reach here, KVMGT always emulates OpRegion */
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((vdev->features & VFIO_FEATURE_ENABLE_IGD_OPREGION) &&
|
||||
!vfio_pci_igd_setup_opregion(vdev, errp)) {
|
||||
!vfio_pci_igd_opregion_init(vdev, opregion, errp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+20
-12
@@ -46,11 +46,28 @@ static int iommufd_cdev_map(const VFIOContainerBase *bcontainer, hwaddr iova,
|
||||
|
||||
static int iommufd_cdev_unmap(const VFIOContainerBase *bcontainer,
|
||||
hwaddr iova, ram_addr_t size,
|
||||
IOMMUTLBEntry *iotlb)
|
||||
IOMMUTLBEntry *iotlb, bool unmap_all)
|
||||
{
|
||||
const VFIOIOMMUFDContainer *container =
|
||||
container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer);
|
||||
|
||||
/* unmap in halves */
|
||||
if (unmap_all) {
|
||||
Int128 llsize = int128_rshift(int128_2_64(), 1);
|
||||
int ret;
|
||||
|
||||
ret = iommufd_backend_unmap_dma(container->be, container->ioas_id,
|
||||
0, int128_get64(llsize));
|
||||
|
||||
if (ret == 0) {
|
||||
ret = iommufd_backend_unmap_dma(container->be, container->ioas_id,
|
||||
int128_get64(llsize),
|
||||
int128_get64(llsize));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* TODO: Handle dma_unmap_bitmap with iotlb args (migration) */
|
||||
return iommufd_backend_unmap_dma(container->be,
|
||||
container->ioas_id, iova, size);
|
||||
@@ -588,14 +605,7 @@ found_container:
|
||||
iommufd_cdev_ram_block_discard_disable(false);
|
||||
}
|
||||
|
||||
vbasedev->group = 0;
|
||||
vbasedev->num_irqs = dev_info.num_irqs;
|
||||
vbasedev->num_regions = dev_info.num_regions;
|
||||
vbasedev->flags = dev_info.flags;
|
||||
vbasedev->reset_works = !!(dev_info.flags & VFIO_DEVICE_FLAGS_RESET);
|
||||
vbasedev->bcontainer = bcontainer;
|
||||
QLIST_INSERT_HEAD(&bcontainer->device_list, vbasedev, container_next);
|
||||
QLIST_INSERT_HEAD(&vfio_device_list, vbasedev, global_next);
|
||||
vfio_device_prepare(vbasedev, bcontainer, &dev_info);
|
||||
|
||||
trace_iommufd_cdev_device_info(vbasedev->name, devfd, vbasedev->num_irqs,
|
||||
vbasedev->num_regions, vbasedev->flags);
|
||||
@@ -622,9 +632,7 @@ static void iommufd_cdev_detach(VFIODevice *vbasedev)
|
||||
VFIOIOMMUFDContainer *container = container_of(bcontainer,
|
||||
VFIOIOMMUFDContainer,
|
||||
bcontainer);
|
||||
QLIST_REMOVE(vbasedev, global_next);
|
||||
QLIST_REMOVE(vbasedev, container_next);
|
||||
vbasedev->bcontainer = NULL;
|
||||
vfio_device_unprepare(vbasedev);
|
||||
|
||||
if (!vbasedev->ram_block_discard_allowed) {
|
||||
iommufd_cdev_ram_block_discard_disable(false);
|
||||
|
||||
+44
-20
@@ -172,7 +172,7 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb)
|
||||
}
|
||||
} else {
|
||||
ret = vfio_container_dma_unmap(bcontainer, iova,
|
||||
iotlb->addr_mask + 1, iotlb);
|
||||
iotlb->addr_mask + 1, iotlb, false);
|
||||
if (ret) {
|
||||
error_setg(&local_err,
|
||||
"vfio_container_dma_unmap(%p, 0x%"HWADDR_PRIx", "
|
||||
@@ -201,7 +201,7 @@ static void vfio_ram_discard_notify_discard(RamDiscardListener *rdl,
|
||||
int ret;
|
||||
|
||||
/* Unmap with a single call. */
|
||||
ret = vfio_container_dma_unmap(bcontainer, iova, size , NULL);
|
||||
ret = vfio_container_dma_unmap(bcontainer, iova, size , NULL, false);
|
||||
if (ret) {
|
||||
error_report("%s: vfio_container_dma_unmap() failed: %s", __func__,
|
||||
strerror(-ret));
|
||||
@@ -411,6 +411,32 @@ static bool vfio_get_section_iova_range(VFIOContainerBase *bcontainer,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void vfio_listener_begin(MemoryListener *listener)
|
||||
{
|
||||
VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase,
|
||||
listener);
|
||||
void (*listener_begin)(VFIOContainerBase *bcontainer);
|
||||
|
||||
listener_begin = VFIO_IOMMU_GET_CLASS(bcontainer)->listener_begin;
|
||||
|
||||
if (listener_begin) {
|
||||
listener_begin(bcontainer);
|
||||
}
|
||||
}
|
||||
|
||||
static void vfio_listener_commit(MemoryListener *listener)
|
||||
{
|
||||
VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase,
|
||||
listener);
|
||||
void (*listener_commit)(VFIOContainerBase *bcontainer);
|
||||
|
||||
listener_commit = VFIO_IOMMU_GET_CLASS(bcontainer)->listener_begin;
|
||||
|
||||
if (listener_commit) {
|
||||
listener_commit(bcontainer);
|
||||
}
|
||||
}
|
||||
|
||||
static void vfio_device_error_append(VFIODevice *vbasedev, Error **errp)
|
||||
{
|
||||
/*
|
||||
@@ -634,21 +660,14 @@ static void vfio_listener_region_del(MemoryListener *listener,
|
||||
}
|
||||
|
||||
if (try_unmap) {
|
||||
bool unmap_all = false;
|
||||
|
||||
if (int128_eq(llsize, int128_2_64())) {
|
||||
/* The unmap ioctl doesn't accept a full 64-bit span. */
|
||||
llsize = int128_rshift(llsize, 1);
|
||||
ret = vfio_container_dma_unmap(bcontainer, iova,
|
||||
int128_get64(llsize), NULL);
|
||||
if (ret) {
|
||||
error_report("vfio_container_dma_unmap(%p, 0x%"HWADDR_PRIx", "
|
||||
"0x%"HWADDR_PRIx") = %d (%s)",
|
||||
bcontainer, iova, int128_get64(llsize), ret,
|
||||
strerror(-ret));
|
||||
}
|
||||
iova += int128_get64(llsize);
|
||||
unmap_all = true;
|
||||
llsize = int128_zero();
|
||||
}
|
||||
ret = vfio_container_dma_unmap(bcontainer, iova,
|
||||
int128_get64(llsize), NULL);
|
||||
ret = vfio_container_dma_unmap(bcontainer, iova, int128_get64(llsize),
|
||||
NULL, unmap_all);
|
||||
if (ret) {
|
||||
error_report("vfio_container_dma_unmap(%p, 0x%"HWADDR_PRIx", "
|
||||
"0x%"HWADDR_PRIx") = %d (%s)",
|
||||
@@ -801,13 +820,17 @@ static void vfio_devices_dma_logging_stop(VFIOContainerBase *bcontainer)
|
||||
VFIO_DEVICE_FEATURE_DMA_LOGGING_STOP;
|
||||
|
||||
QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) {
|
||||
int ret;
|
||||
|
||||
if (!vbasedev->dirty_tracking) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature)) {
|
||||
ret = vbasedev->io_ops->device_feature(vbasedev, feature);
|
||||
|
||||
if (ret != 0) {
|
||||
warn_report("%s: Failed to stop DMA logging, err %d (%s)",
|
||||
vbasedev->name, -errno, strerror(errno));
|
||||
vbasedev->name, -ret, strerror(-ret));
|
||||
}
|
||||
vbasedev->dirty_tracking = false;
|
||||
}
|
||||
@@ -908,10 +931,9 @@ static bool vfio_devices_dma_logging_start(VFIOContainerBase *bcontainer,
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature);
|
||||
ret = vbasedev->io_ops->device_feature(vbasedev, feature);
|
||||
if (ret) {
|
||||
ret = -errno;
|
||||
error_setg_errno(errp, errno, "%s: Failed to start DMA logging",
|
||||
error_setg_errno(errp, -ret, "%s: Failed to start DMA logging",
|
||||
vbasedev->name);
|
||||
goto out;
|
||||
}
|
||||
@@ -1165,6 +1187,8 @@ static void vfio_listener_log_sync(MemoryListener *listener,
|
||||
|
||||
static const MemoryListener vfio_memory_listener = {
|
||||
.name = "vfio",
|
||||
.begin = vfio_listener_begin,
|
||||
.commit = vfio_listener_commit,
|
||||
.region_add = vfio_listener_region_add,
|
||||
.region_del = vfio_listener_region_del,
|
||||
.log_global_start = vfio_listener_log_global_start,
|
||||
|
||||
+159
-100
File diff suppressed because it is too large
Load Diff
+9
-1
@@ -118,8 +118,16 @@ typedef struct VFIOMSIXInfo {
|
||||
bool noresize;
|
||||
} VFIOMSIXInfo;
|
||||
|
||||
/*
|
||||
* TYPE_VFIO_PCI_BASE is an abstract type used to share code
|
||||
* between VFIO implementations that use a kernel driver
|
||||
* with those that use user sockets.
|
||||
*/
|
||||
#define TYPE_VFIO_PCI_BASE "vfio-pci-base"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(VFIOPCIDevice, VFIO_PCI_BASE)
|
||||
|
||||
#define TYPE_VFIO_PCI "vfio-pci"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(VFIOPCIDevice, VFIO_PCI)
|
||||
/* TYPE_VFIO_PCI shares struct VFIOPCIDevice. */
|
||||
|
||||
struct VFIOPCIDevice {
|
||||
PCIDevice pdev;
|
||||
|
||||
+3
-3
@@ -474,10 +474,10 @@ static bool vfio_populate_device(VFIODevice *vbasedev, Error **errp)
|
||||
QSIMPLEQ_INIT(&vdev->pending_intp_queue);
|
||||
|
||||
for (i = 0; i < vbasedev->num_irqs; i++) {
|
||||
struct vfio_irq_info irq = { .argsz = sizeof(irq) };
|
||||
struct vfio_irq_info irq;
|
||||
|
||||
ret = vfio_device_get_irq_info(vbasedev, i, &irq);
|
||||
|
||||
irq.index = i;
|
||||
ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq);
|
||||
if (ret) {
|
||||
error_setg_errno(errp, -ret, "failed to get device irq info");
|
||||
goto irq_err;
|
||||
|
||||
+12
-7
@@ -45,6 +45,7 @@ void vfio_region_write(void *opaque, hwaddr addr,
|
||||
uint32_t dword;
|
||||
uint64_t qword;
|
||||
} buf;
|
||||
int ret;
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
@@ -64,11 +65,13 @@ void vfio_region_write(void *opaque, hwaddr addr,
|
||||
break;
|
||||
}
|
||||
|
||||
if (pwrite(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) {
|
||||
ret = vbasedev->io_ops->region_write(vbasedev, region->nr,
|
||||
addr, size, &buf);
|
||||
if (ret != size) {
|
||||
error_report("%s(%s:region%d+0x%"HWADDR_PRIx", 0x%"PRIx64
|
||||
",%d) failed: %m",
|
||||
",%d) failed: %s",
|
||||
__func__, vbasedev->name, region->nr,
|
||||
addr, data, size);
|
||||
addr, data, size, strwriteerror(ret));
|
||||
}
|
||||
|
||||
trace_vfio_region_write(vbasedev->name, region->nr, addr, data, size);
|
||||
@@ -96,11 +99,13 @@ uint64_t vfio_region_read(void *opaque,
|
||||
uint64_t qword;
|
||||
} buf;
|
||||
uint64_t data = 0;
|
||||
int ret;
|
||||
|
||||
if (pread(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) {
|
||||
error_report("%s(%s:region%d+0x%"HWADDR_PRIx", %d) failed: %m",
|
||||
ret = vbasedev->io_ops->region_read(vbasedev, region->nr, addr, size, &buf);
|
||||
if (ret != size) {
|
||||
error_report("%s(%s:region%d+0x%"HWADDR_PRIx", %d) failed: %s",
|
||||
__func__, vbasedev->name, region->nr,
|
||||
addr, size);
|
||||
addr, size, strreaderror(ret));
|
||||
return (uint64_t)-1;
|
||||
}
|
||||
switch (size) {
|
||||
@@ -182,7 +187,7 @@ static int vfio_setup_region_sparse_mmaps(VFIORegion *region,
|
||||
int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region,
|
||||
int index, const char *name)
|
||||
{
|
||||
g_autofree struct vfio_region_info *info = NULL;
|
||||
struct vfio_region_info *info = NULL;
|
||||
int ret;
|
||||
|
||||
ret = vfio_device_get_region_info(vbasedev, index, &info);
|
||||
|
||||
@@ -81,7 +81,7 @@ int vfio_container_dma_map(VFIOContainerBase *bcontainer,
|
||||
void *vaddr, bool readonly);
|
||||
int vfio_container_dma_unmap(VFIOContainerBase *bcontainer,
|
||||
hwaddr iova, ram_addr_t size,
|
||||
IOMMUTLBEntry *iotlb);
|
||||
IOMMUTLBEntry *iotlb, bool unmap_all);
|
||||
bool vfio_container_add_section_window(VFIOContainerBase *bcontainer,
|
||||
MemoryRegionSection *section,
|
||||
Error **errp);
|
||||
@@ -117,12 +117,25 @@ struct VFIOIOMMUClass {
|
||||
|
||||
/* basic feature */
|
||||
bool (*setup)(VFIOContainerBase *bcontainer, Error **errp);
|
||||
void (*listener_begin)(VFIOContainerBase *bcontainer);
|
||||
void (*listener_commit)(VFIOContainerBase *bcontainer);
|
||||
int (*dma_map)(const VFIOContainerBase *bcontainer,
|
||||
hwaddr iova, ram_addr_t size,
|
||||
void *vaddr, bool readonly);
|
||||
/**
|
||||
* @dma_unmap
|
||||
*
|
||||
* Unmap an address range from the container.
|
||||
*
|
||||
* @bcontainer: #VFIOContainerBase to use for unmap
|
||||
* @iova: start address to unmap
|
||||
* @size: size of the range to unmap
|
||||
* @iotlb: The IOMMU TLB mapping entry (or NULL)
|
||||
* @unmap_all: if set, unmap the entire address space
|
||||
*/
|
||||
int (*dma_unmap)(const VFIOContainerBase *bcontainer,
|
||||
hwaddr iova, ram_addr_t size,
|
||||
IOMMUTLBEntry *iotlb);
|
||||
IOMMUTLBEntry *iotlb, bool unmap_all);
|
||||
bool (*attach_device)(const char *name, VFIODevice *vbasedev,
|
||||
AddressSpace *as, Error **errp);
|
||||
void (*detach_device)(VFIODevice *vbasedev);
|
||||
|
||||
@@ -41,6 +41,7 @@ enum {
|
||||
};
|
||||
|
||||
typedef struct VFIODeviceOps VFIODeviceOps;
|
||||
typedef struct VFIODeviceIOOps VFIODeviceIOOps;
|
||||
typedef struct VFIOMigration VFIOMigration;
|
||||
|
||||
typedef struct IOMMUFDBackend IOMMUFDBackend;
|
||||
@@ -66,6 +67,7 @@ typedef struct VFIODevice {
|
||||
OnOffAuto migration_multifd_transfer;
|
||||
bool migration_events;
|
||||
VFIODeviceOps *ops;
|
||||
VFIODeviceIOOps *io_ops;
|
||||
unsigned int num_irqs;
|
||||
unsigned int num_regions;
|
||||
unsigned int flags;
|
||||
@@ -81,6 +83,7 @@ typedef struct VFIODevice {
|
||||
IOMMUFDBackend *iommufd;
|
||||
VFIOIOASHwpt *hwpt;
|
||||
QLIST_ENTRY(VFIODevice) hwpt_next;
|
||||
struct vfio_region_info **reginfo;
|
||||
} VFIODevice;
|
||||
|
||||
struct VFIODeviceOps {
|
||||
@@ -115,6 +118,20 @@ struct VFIODeviceOps {
|
||||
int (*vfio_load_config)(VFIODevice *vdev, QEMUFile *f);
|
||||
};
|
||||
|
||||
/*
|
||||
* Given a return value of either a short number of bytes read or -errno,
|
||||
* construct a meaningful error message.
|
||||
*/
|
||||
#define strreaderror(ret) \
|
||||
(ret < 0 ? strerror(-ret) : "short read")
|
||||
|
||||
/*
|
||||
* Given a return value of either a short number of bytes written or -errno,
|
||||
* construct a meaningful error message.
|
||||
*/
|
||||
#define strwriteerror(ret) \
|
||||
(ret < 0 ? strerror(-ret) : "short write")
|
||||
|
||||
void vfio_device_irq_disable(VFIODevice *vbasedev, int index);
|
||||
void vfio_device_irq_unmask(VFIODevice *vbasedev, int index);
|
||||
void vfio_device_irq_mask(VFIODevice *vbasedev, int index);
|
||||
@@ -127,6 +144,9 @@ bool vfio_device_hiod_create_and_realize(VFIODevice *vbasedev,
|
||||
const char *typename, Error **errp);
|
||||
bool vfio_device_attach(char *name, VFIODevice *vbasedev,
|
||||
AddressSpace *as, Error **errp);
|
||||
bool vfio_device_attach_by_iommu_type(const char *iommu_type, char *name,
|
||||
VFIODevice *vbasedev, AddressSpace *as,
|
||||
Error **errp);
|
||||
void vfio_device_detach(VFIODevice *vbasedev);
|
||||
VFIODevice *vfio_get_vfio_device(Object *obj);
|
||||
|
||||
@@ -134,11 +154,73 @@ typedef QLIST_HEAD(VFIODeviceList, VFIODevice) VFIODeviceList;
|
||||
extern VFIODeviceList vfio_device_list;
|
||||
|
||||
#ifdef CONFIG_LINUX
|
||||
/*
|
||||
* How devices communicate with the server. The default option is through
|
||||
* ioctl() to the kernel VFIO driver, but vfio-user can use a socket to a remote
|
||||
* process.
|
||||
*/
|
||||
struct VFIODeviceIOOps {
|
||||
/**
|
||||
* @device_feature
|
||||
*
|
||||
* Fill in feature info for the given device.
|
||||
*/
|
||||
int (*device_feature)(VFIODevice *vdev, struct vfio_device_feature *);
|
||||
|
||||
/**
|
||||
* @get_region_info
|
||||
*
|
||||
* Fill in @info with information on the region given by @info->index.
|
||||
*/
|
||||
int (*get_region_info)(VFIODevice *vdev,
|
||||
struct vfio_region_info *info);
|
||||
|
||||
/**
|
||||
* @get_irq_info
|
||||
*
|
||||
* Fill in @irq with information on the IRQ given by @info->index.
|
||||
*/
|
||||
int (*get_irq_info)(VFIODevice *vdev, struct vfio_irq_info *irq);
|
||||
|
||||
/**
|
||||
* @set_irqs
|
||||
*
|
||||
* Configure IRQs as defined by @irqs.
|
||||
*/
|
||||
int (*set_irqs)(VFIODevice *vdev, struct vfio_irq_set *irqs);
|
||||
|
||||
/**
|
||||
* @region_read
|
||||
*
|
||||
* Read @size bytes from the region @nr at offset @off into the buffer
|
||||
* @data.
|
||||
*/
|
||||
int (*region_read)(VFIODevice *vdev, uint8_t nr, off_t off, uint32_t size,
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* @region_write
|
||||
*
|
||||
* Write @size bytes to the region @nr at offset @off from the buffer
|
||||
* @data.
|
||||
*/
|
||||
int (*region_write)(VFIODevice *vdev, uint8_t nr, off_t off, uint32_t size,
|
||||
void *data);
|
||||
};
|
||||
|
||||
void vfio_device_prepare(VFIODevice *vbasedev, VFIOContainerBase *bcontainer,
|
||||
struct vfio_device_info *info);
|
||||
|
||||
void vfio_device_unprepare(VFIODevice *vbasedev);
|
||||
|
||||
int vfio_device_get_region_info(VFIODevice *vbasedev, int index,
|
||||
struct vfio_region_info **info);
|
||||
int vfio_device_get_region_info_type(VFIODevice *vbasedev, uint32_t type,
|
||||
uint32_t subtype, struct vfio_region_info **info);
|
||||
bool vfio_device_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type);
|
||||
|
||||
int vfio_device_get_irq_info(VFIODevice *vbasedev, int index,
|
||||
struct vfio_irq_info *info);
|
||||
#endif
|
||||
|
||||
/* Returns 0 on success, or a negative errno. */
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#define SETUP_INDIRECT (1<<31)
|
||||
#define SETUP_TYPE_MAX (SETUP_ENUM_MAX | SETUP_INDIRECT)
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include "standard-headers/linux/types.h"
|
||||
|
||||
@@ -78,6 +78,6 @@ struct ima_setup_data {
|
||||
uint64_t size;
|
||||
} QEMU_PACKED;
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* __ASSEMBLER__ */
|
||||
|
||||
#endif /* _ASM_X86_SETUP_DATA_H */
|
||||
|
||||
@@ -420,6 +420,7 @@ extern "C" {
|
||||
#define DRM_FORMAT_MOD_VENDOR_ARM 0x08
|
||||
#define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09
|
||||
#define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a
|
||||
#define DRM_FORMAT_MOD_VENDOR_MTK 0x0b
|
||||
|
||||
/* add more to the end as needed */
|
||||
|
||||
@@ -1452,6 +1453,46 @@ drm_fourcc_canonicalize_nvidia_format_mod(uint64_t modifier)
|
||||
*/
|
||||
#define AMLOGIC_FBC_OPTION_MEM_SAVING (1ULL << 0)
|
||||
|
||||
/* MediaTek modifiers
|
||||
* Bits Parameter Notes
|
||||
* ----- ------------------------ ---------------------------------------------
|
||||
* 7: 0 TILE LAYOUT Values are MTK_FMT_MOD_TILE_*
|
||||
* 15: 8 COMPRESSION Values are MTK_FMT_MOD_COMPRESS_*
|
||||
* 23:16 10 BIT LAYOUT Values are MTK_FMT_MOD_10BIT_LAYOUT_*
|
||||
*
|
||||
*/
|
||||
|
||||
#define DRM_FORMAT_MOD_MTK(__flags) fourcc_mod_code(MTK, __flags)
|
||||
|
||||
/*
|
||||
* MediaTek Tiled Modifier
|
||||
* The lowest 8 bits of the modifier is used to specify the tiling
|
||||
* layout. Only the 16L_32S tiling is used for now, but we define an
|
||||
* "untiled" version and leave room for future expansion.
|
||||
*/
|
||||
#define MTK_FMT_MOD_TILE_MASK 0xf
|
||||
#define MTK_FMT_MOD_TILE_NONE 0x0
|
||||
#define MTK_FMT_MOD_TILE_16L32S 0x1
|
||||
|
||||
/*
|
||||
* Bits 8-15 specify compression options
|
||||
*/
|
||||
#define MTK_FMT_MOD_COMPRESS_MASK (0xf << 8)
|
||||
#define MTK_FMT_MOD_COMPRESS_NONE (0x0 << 8)
|
||||
#define MTK_FMT_MOD_COMPRESS_V1 (0x1 << 8)
|
||||
|
||||
/*
|
||||
* Bits 16-23 specify how the bits of 10 bit formats are
|
||||
* stored out in memory
|
||||
*/
|
||||
#define MTK_FMT_MOD_10BIT_LAYOUT_MASK (0xf << 16)
|
||||
#define MTK_FMT_MOD_10BIT_LAYOUT_PACKED (0x0 << 16)
|
||||
#define MTK_FMT_MOD_10BIT_LAYOUT_LSBTILED (0x1 << 16)
|
||||
#define MTK_FMT_MOD_10BIT_LAYOUT_LSBRASTER (0x2 << 16)
|
||||
|
||||
/* alias for the most common tiling format */
|
||||
#define DRM_FORMAT_MOD_MTK_16L_32S_TILE DRM_FORMAT_MOD_MTK(MTK_FMT_MOD_TILE_16L32S)
|
||||
|
||||
/*
|
||||
* AMD modifiers
|
||||
*
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
* Missing __asm__ support
|
||||
*
|
||||
* __BIT128() would not work in the __asm__ code, as it shifts an
|
||||
* 'unsigned __init128' data type as direct representation of
|
||||
* 'unsigned __int128' data type as direct representation of
|
||||
* 128 bit constants is not supported in the gcc compiler, as
|
||||
* they get silently truncated.
|
||||
*
|
||||
|
||||
@@ -2059,6 +2059,24 @@ enum ethtool_link_mode_bit_indices {
|
||||
ETHTOOL_LINK_MODE_10baseT1S_Half_BIT = 100,
|
||||
ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT = 101,
|
||||
ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT = 102,
|
||||
ETHTOOL_LINK_MODE_200000baseCR_Full_BIT = 103,
|
||||
ETHTOOL_LINK_MODE_200000baseKR_Full_BIT = 104,
|
||||
ETHTOOL_LINK_MODE_200000baseDR_Full_BIT = 105,
|
||||
ETHTOOL_LINK_MODE_200000baseDR_2_Full_BIT = 106,
|
||||
ETHTOOL_LINK_MODE_200000baseSR_Full_BIT = 107,
|
||||
ETHTOOL_LINK_MODE_200000baseVR_Full_BIT = 108,
|
||||
ETHTOOL_LINK_MODE_400000baseCR2_Full_BIT = 109,
|
||||
ETHTOOL_LINK_MODE_400000baseKR2_Full_BIT = 110,
|
||||
ETHTOOL_LINK_MODE_400000baseDR2_Full_BIT = 111,
|
||||
ETHTOOL_LINK_MODE_400000baseDR2_2_Full_BIT = 112,
|
||||
ETHTOOL_LINK_MODE_400000baseSR2_Full_BIT = 113,
|
||||
ETHTOOL_LINK_MODE_400000baseVR2_Full_BIT = 114,
|
||||
ETHTOOL_LINK_MODE_800000baseCR4_Full_BIT = 115,
|
||||
ETHTOOL_LINK_MODE_800000baseKR4_Full_BIT = 116,
|
||||
ETHTOOL_LINK_MODE_800000baseDR4_Full_BIT = 117,
|
||||
ETHTOOL_LINK_MODE_800000baseDR4_2_Full_BIT = 118,
|
||||
ETHTOOL_LINK_MODE_800000baseSR4_Full_BIT = 119,
|
||||
ETHTOOL_LINK_MODE_800000baseVR4_Full_BIT = 120,
|
||||
|
||||
/* must be last entry */
|
||||
__ETHTOOL_LINK_MODE_MASK_NBITS
|
||||
@@ -2271,6 +2289,10 @@ static inline int ethtool_validate_duplex(uint8_t duplex)
|
||||
* be exploited to reduce the RSS queue spread.
|
||||
*/
|
||||
#define RXH_XFRM_SYM_XOR (1 << 0)
|
||||
/* Similar to SYM_XOR, except that one copy of the XOR'ed fields is replaced by
|
||||
* an OR of the same fields
|
||||
*/
|
||||
#define RXH_XFRM_SYM_OR_XOR (1 << 1)
|
||||
#define RXH_XFRM_NO_CHANGE 0xff
|
||||
|
||||
/* L2-L4 network traffic flow types */
|
||||
|
||||
@@ -229,6 +229,9 @@
|
||||
* - FUSE_URING_IN_OUT_HEADER_SZ
|
||||
* - FUSE_URING_OP_IN_OUT_SZ
|
||||
* - enum fuse_uring_cmd
|
||||
*
|
||||
* 7.43
|
||||
* - add FUSE_REQUEST_TIMEOUT
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FUSE_H
|
||||
@@ -260,7 +263,7 @@
|
||||
#define FUSE_KERNEL_VERSION 7
|
||||
|
||||
/** Minor version number of this interface */
|
||||
#define FUSE_KERNEL_MINOR_VERSION 42
|
||||
#define FUSE_KERNEL_MINOR_VERSION 43
|
||||
|
||||
/** The node ID of the root inode */
|
||||
#define FUSE_ROOT_ID 1
|
||||
@@ -431,6 +434,8 @@ struct fuse_file_lock {
|
||||
* of the request ID indicates resend requests
|
||||
* FUSE_ALLOW_IDMAP: allow creation of idmapped mounts
|
||||
* FUSE_OVER_IO_URING: Indicate that client supports io-uring
|
||||
* FUSE_REQUEST_TIMEOUT: kernel supports timing out requests.
|
||||
* init_out.request_timeout contains the timeout (in secs)
|
||||
*/
|
||||
#define FUSE_ASYNC_READ (1 << 0)
|
||||
#define FUSE_POSIX_LOCKS (1 << 1)
|
||||
@@ -473,11 +478,11 @@ struct fuse_file_lock {
|
||||
#define FUSE_PASSTHROUGH (1ULL << 37)
|
||||
#define FUSE_NO_EXPORT_SUPPORT (1ULL << 38)
|
||||
#define FUSE_HAS_RESEND (1ULL << 39)
|
||||
|
||||
/* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */
|
||||
#define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP
|
||||
#define FUSE_ALLOW_IDMAP (1ULL << 40)
|
||||
#define FUSE_OVER_IO_URING (1ULL << 41)
|
||||
#define FUSE_REQUEST_TIMEOUT (1ULL << 42)
|
||||
|
||||
/**
|
||||
* CUSE INIT request/reply flags
|
||||
@@ -905,7 +910,8 @@ struct fuse_init_out {
|
||||
uint16_t map_alignment;
|
||||
uint32_t flags2;
|
||||
uint32_t max_stack_depth;
|
||||
uint32_t unused[6];
|
||||
uint16_t request_timeout;
|
||||
uint16_t unused[11];
|
||||
};
|
||||
|
||||
#define CUSE_INIT_INFO_MAX 4096
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user