device-assignment: Use PCI I/O port sysfs resource file when available
PATCH 1/1 device-assignment: Use PCI I/O port sysfs resource file when available
When supported by the host kernel, we can use read/write on the PCI sysfs resource file for I/O port regions. This allows us to avoid raw in/out commands and works with deprivileged guests via libvirt. For uid 0 callers, we use in/out directly to avoid any compatibility issues.
---
Required kernel patch pending here:
http://www.spinics.net/lists/linux-pci/msg09389.html
hw/device-assignment.c | 131 ++++++++++++++++++++++++++++++++++++------------ hw/device-assignment.h | 1 2 files changed, 99 insertions(+), 33 deletions(-)
hw/device-assignment.c +98 -33
@@ -67,6 +67,28 @@ static uint32_t guest_to_host_ioport(AssignedDevRegion *region, uint32_t addr)return region->u.r_baseport + (addr - region->e_physbase); } +static int assigned_dev_ioport_rw (AssignedDevRegion *dev_region, +uint32_t addr,int len,uint32_t *val, +int write) +{ +if (dev_region->region->resource_fd ==-1 ) +return -1 ; + +if (write) { +if (pwrite (dev_region->region->resource_fd, val, len, + (addr - dev_region->e_physbase)) != len) { +return -1 ; + } + }else { +if (pread (dev_region->region->resource_fd, val, len, + (addr - dev_region->e_physbase)) != len) { +return -1 ; + } + } + +return 0 ; +} +static void assigned_dev_ioport_writeb (void *opaque,uint32_t addr,uint32_t value) {
@@ -77,7 +99,9 @@ static void assigned_dev_ioport_writeb(void *opaque, uint32_t addr, r_pio, (int )r_access->e_physbase, (unsigned long)r_access->u.r_baseport, value); -outb (value, r_pio); +if (assigned_dev_ioport_rw (r_access, addr,1 , &value,1 ) !=0 ) { +outb (value, r_pio); + } } static void assigned_dev_ioport_writew(void *opaque, uint32_t addr,
@@ -90,7 +114,9 @@ static void assigned_dev_ioport_writew(void *opaque, uint32_t addr, r_pio, (int )r_access->e_physbase, (unsigned long)r_access->u.r_baseport, value); -outw (value, r_pio); +if (assigned_dev_ioport_rw (r_access, addr,2 , &value,1 ) !=0 ) { +outw (value, r_pio); + } } static void assigned_dev_ioport_writel(void *opaque, uint32_t addr,
@@ -103,7 +129,9 @@ static void assigned_dev_ioport_writel(void *opaque, uint32_t addr, r_pio, (int )r_access->e_physbase, (unsigned long)r_access->u.r_baseport, value); -outl (value, r_pio); +if (assigned_dev_ioport_rw (r_access, addr,4 , &value,1 ) !=0 ) { +outl (value, r_pio); + } }static uint32_t assigned_dev_ioport_readb (void *opaque,uint32_t addr)
@@ -112,7 +140,9 @@ static uint32_t assigned_dev_ioport_readb(void *opaque, uint32_t addr)uint32_t r_pio =guest_to_host_ioport (r_access, addr);uint32_t value; - value =inb (r_pio); +if (assigned_dev_ioport_rw (r_access, addr,1 , &value,0 ) !=0 ) { + value =inb (r_pio); + } DEBUG("r_pio=%08x e_physbase=%08x r_=%08lx value=%08x\n", r_pio, (int)r_access->e_physbase,
@@ -127,7 +157,9 @@ static uint32_t assigned_dev_ioport_readw(void *opaque, uint32_t addr)uint32_t r_pio =guest_to_host_ioport (r_access, addr);uint32_t value; - value =inw (r_pio); +if (assigned_dev_ioport_rw (r_access, addr,2 , &value,0 ) !=0 ) { + value =inw (r_pio); + } DEBUG("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n", r_pio, (int)r_access->e_physbase,
@@ -142,7 +174,9 @@ static uint32_t assigned_dev_ioport_readl(void *opaque, uint32_t addr)uint32_t r_pio =guest_to_host_ioport (r_access, addr);uint32_t value; - value =inl (r_pio); +if (assigned_dev_ioport_rw (r_access, addr,4 , &value,0 ) !=0 ) { + value =inl (r_pio); + } DEBUG("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n", r_pio, (int)r_access->e_physbase,
@@ -305,7 +339,7 @@ static void assigned_dev_ioport_map(PCIDevice *pci_dev, int region_num,DEBUG ("e_phys=0x%" FMT_PCIBUS " r_baseport=%x type=0x%x len=%" FMT_PCIBUS " region_num=%d \n" , addr, region->u.r_baseport, type, size, region_num); -if (first_map) { +if (first_map && region->region->resource_fd <0 ) {struct ioperm_data *data; data =qemu_mallocz (sizeof (struct ioperm_data ));
@@ -586,19 +620,46 @@ static int assigned_dev_register_regions(PCIRegion *io_regions, slow_map ? assigned_dev_iomem_map_slow : assigned_dev_iomem_map);continue ; + }else { +/* handle port io regions */ +uint32_t val; +int ret; + +/* Test kernel support for ioport resource read/write. Old +* kernels return EIO. New kernels only allow 1/2/4 byte reads +* so should return EINVAL for a 3 byte read */ + ret =pread (pci_dev->v_addrs[i].region->resource_fd, &val,3 ,0 ); +if (ret ==3 ) { +fprintf (stderr,"I/O port resource supports 3 byte read?!\n" ); +abort (); + }else if (errno ==EIO ) { +fprintf (stderr, +"pcisysfs does not support rw ioport resource\n" ); +close (pci_dev->v_addrs[i].region->resource_fd); + pci_dev->v_addrs[i].region->resource_fd =-1 ; + }else if (errno !=EINVAL ) { +fprintf (stderr,"Unexpected return from ioport pread (%d) %s\n" , + errno,strerror (errno)); +abort (); + } + +/* Root user can use direct access for compatibility */ +if (getuid () ==0 ) { +close (pci_dev->v_addrs[i].region->resource_fd); + pci_dev->v_addrs[i].region->resource_fd =-1 ; + } + pci_dev->v_addrs[i].e_physbase = cur_region->base_addr; + pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr; + pci_dev->v_addrs[i].r_size = cur_region->size; + pci_dev->v_addrs[i].e_size =0 ; + +pci_register_bar ((PCIDevice *) pci_dev, i, + cur_region->size,PCI_BASE_ADDRESS_SPACE_IO , + assigned_dev_ioport_map); + +/* not relevant for port io */ + pci_dev->v_addrs[i].memory_index =0 ; } -/* handle port io regions */ - pci_dev->v_addrs[i].e_physbase = cur_region->base_addr; - pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr; - pci_dev->v_addrs[i].r_size = cur_region->size; - pci_dev->v_addrs[i].e_size =0 ; - -pci_register_bar ((PCIDevice *) pci_dev, i, - cur_region->size,PCI_BASE_ADDRESS_SPACE_IO , - assigned_dev_ioport_map); - -/* not relevant for port io */ - pci_dev->v_addrs[i].memory_index =0 ; }/* success */
@@ -705,20 +766,22 @@ again:continue ;if (flags &IORESOURCE_MEM ) { flags &= ~IORESOURCE_IO ; -if (r !=PCI_ROM_SLOT ) { -snprintf (name,sizeof (name),"%sresource%d" , dir, r); - fd =open (name,O_RDWR ); -if (fd ==-1 ) -continue ; - rp->resource_fd = fd; - } - }else + }else { flags &= ~IORESOURCE_PREFETCH ; + } +if (r !=PCI_ROM_SLOT ) { +snprintf (name,sizeof (name),"%sresource%d" , dir, r); + fd =open (name,O_RDWR ); +if (fd ==-1 ) +continue ; + rp->resource_fd = fd; + } rp->type = flags; rp->valid =1 ; rp->base_addr = start; rp->size = size; + pci_dev->v_addrs[r].region = rp;DEBUG ("region %d size %d start 0x%llx type %d resource_fd %d\n" , r, rp->size, start, rp->type, rp->resource_fd); }
@@ -780,8 +843,10 @@ static void free_assigned_device(AssignedDevice *dev)continue ;if (pci_region->type &IORESOURCE_IO ) { -kvm_remove_ioperm_data (region->u.r_baseport, region->r_size); -continue ; +if (pci_region->resource_fd <0 ) { +kvm_remove_ioperm_data (region->u.r_baseport, + region->r_size); + } }else if (pci_region->type &IORESOURCE_MEM ) {if (region->u.r_virtbase) {if (region->memory_index) {
@@ -795,11 +860,11 @@ static void free_assigned_device(AssignedDevice *dev)fprintf (stderr,"Failed to unmap assigned device region: %s\n" ,strerror (errno)); -if (pci_region->resource_fd >=0 ) { -close (pci_region->resource_fd); - } } - } + } +if (pci_region->resource_fd >=0 ) { +close (pci_region->resource_fd); + } }if (dev->cap.available &ASSIGNED_DEVICE_CAP_MSIX )
hw/device-assignment.h +1 -0
@@ -71,6 +71,7 @@ typedef struct {int num;/* our index within v_addrs[] */ pcibus_t e_size;/* emulated size of region in bytes */ pcibus_t r_size;/* real size of region in bytes */ +PCIRegion *region; } AssignedDevRegion; typedef struct AssignedDevice {
Quoted 5 lines · * Alex Williamson ([email protected]) wrote:
When supported by the host kernel, we can use read/write on the PCI sysfs resource file for I/O port regions. This allows us to avoid raw in/out commands and works with deprivileged guests via libvirt. For uid 0 callers, we use in/out directly to avoid any compatibility issues.
won't uid 0 test will fail if libvirt launches qemu with user set to root (capabilities still get dropped)?
thanks,
-chris
Quoted 9 lines · Chris Wright
Quoted 5 lines · * Alex Williamson ([email protected]) wrote:
When supported by the host kernel, we can use read/write on the PCI sysfs resource file for I/O port regions. This allows us to avoid raw in/out commands and works with deprivileged guests via libvirt. For uid 0 callers, we use in/out directly to avoid any compatibility issues.
won't uid 0 test will fail if libvirt launches qemu with user set to root (capabilities still get dropped)?
Yes, if the kernel is doing a CAP_SYS_ADMIN check (or similar), then testing uid==0 is definitely wrong. You'd need to test have(CAP_SYS_ADMIN) instead.
REgards,
Daniel
Signature · 4 lines
|: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
PATCH v2 1/1 device-assignment: Use PCI I/O port sysfs resource file when available
In reply to Alex WilliamsonWhen supported by the host kernel, we can use read/write on the PCI sysfs resource file for I/O port regions. This allows us to avoid raw in/out commands and works with deprivileged guests via libvirt. For uid 0 callers, we use in/out directly to avoid any compatibility issues.
---
Required kernel patch pending here:
http://www.spinics.net/lists/linux-pci/msg09389.html
v2: Drop getuid() since it doesn't guarantee permissions
Don't use in/out as a fallback since we don't have permissions Consolidate ioport read/write functions
hw/device-assignment.c | 205 ++++++++++++++++++++++++++++-------------------- hw/device-assignment.h | 1 2 files changed, 120 insertions(+), 86 deletions(-)
hw/device-assignment.c +119 -86
@@ -62,93 +62,100 @@ static void assigned_dev_load_option_rom(AssignedDevice *dev);static void assigned_dev_unregister_msix_mmio (AssignedDevice *dev); -static uint32_t guest_to_host_ioport (AssignedDevRegion *region,uint32_t addr) +static uint32_t assigned_dev_ioport_rw (AssignedDevRegion *dev_region, +uint32_t addr,int len,uint32_t *val) { -return region->u.r_baseport + (addr - region->e_physbase); +uint32_t ret =0 ; +uint32_t offset = addr - dev_region->e_physbase; +int fd = dev_region->region->resource_fd; + +if (fd >=0 ) { +if (val) { +DEBUG ("pwrite val=%x, len=%d, e_phys=%x, offset=%x\n" , + *val, len, addr, offset); +if (pwrite (fd, val, len, offset) != len) { +fprintf (stderr,"%s - pwrite failed %s\n" , + __func__,strerror (errno)); + } + }else { +if (pread (fd, &ret, len, offset) != len) { +fprintf (stderr,"%s - pread failed %s\n" , + __func__,strerror (errno)); + ret = (1UL << (len *8 )) -1 ; + } +DEBUG ("pread ret=%x, len=%d, e_phys=%x, offset=%x\n" , + ret, len, addr, offset); + } + }else { +uint32_t port = offset + dev_region->u.r_baseport; + +if (val) { +DEBUG ("out val=%x, len=%d, e_phys=%x, host=%x\n" , + *val, len, addr, port); +switch (len) { +case 1 : +outb (*val, port); +break ; +case 2 : +outw (*val, port); +break ; +case 4 : +outl (*val, port); +break ; + } + }else { +switch (len) { +case 1 : + ret =inb (port); +break ; +case 2 : + ret =inw (port); +break ; +case 4 : + ret =inl (port); +break ; + } +DEBUG ("in val=%x, len=%d, e_phys=%x, host=%x\n" , + ret, len, addr, port); + } + } +return ret; }static void assigned_dev_ioport_writeb (void *opaque,uint32_t addr,uint32_t value) { -AssignedDevRegion *r_access = opaque; -uint32_t r_pio =guest_to_host_ioport (r_access, addr); - -DEBUG ("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n" , - r_pio, (int )r_access->e_physbase, - (unsigned long )r_access->u.r_baseport, value); - -outb (value, r_pio); +assigned_dev_ioport_rw (opaque, addr,1 , &value); +return ; }static void assigned_dev_ioport_writew (void *opaque,uint32_t addr,uint32_t value) { -AssignedDevRegion *r_access = opaque; -uint32_t r_pio =guest_to_host_ioport (r_access, addr); - -DEBUG ("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n" , - r_pio, (int )r_access->e_physbase, - (unsigned long )r_access->u.r_baseport, value); - -outw (value, r_pio); +assigned_dev_ioport_rw (opaque, addr,2 , &value); +return ; }static void assigned_dev_ioport_writel (void *opaque,uint32_t addr,uint32_t value) { -AssignedDevRegion *r_access = opaque; -uint32_t r_pio =guest_to_host_ioport (r_access, addr); - -DEBUG ("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n" , - r_pio, (int )r_access->e_physbase, - (unsigned long )r_access->u.r_baseport, value); - -outl (value, r_pio); +assigned_dev_ioport_rw (opaque, addr,4 , &value); +return ; }static uint32_t assigned_dev_ioport_readb (void *opaque,uint32_t addr) { -AssignedDevRegion *r_access = opaque; -uint32_t r_pio =guest_to_host_ioport (r_access, addr); -uint32_t value; - - value =inb (r_pio); - -DEBUG ("r_pio=%08x e_physbase=%08x r_=%08lx value=%08x\n" , - r_pio, (int )r_access->e_physbase, - (unsigned long )r_access->u.r_baseport, value); - -return value; +return assigned_dev_ioport_rw (opaque, addr,1 ,NULL ); }static uint32_t assigned_dev_ioport_readw (void *opaque,uint32_t addr) { -AssignedDevRegion *r_access = opaque; -uint32_t r_pio =guest_to_host_ioport (r_access, addr); -uint32_t value; - - value =inw (r_pio); - -DEBUG ("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n" , - r_pio, (int )r_access->e_physbase, - (unsigned long )r_access->u.r_baseport, value); - -return value; +return assigned_dev_ioport_rw (opaque, addr,2 ,NULL ); }static uint32_t assigned_dev_ioport_readl (void *opaque,uint32_t addr) { -AssignedDevRegion *r_access = opaque; -uint32_t r_pio =guest_to_host_ioport (r_access, addr); -uint32_t value; - - value =inl (r_pio); - -DEBUG ("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n" , - r_pio, (int )r_access->e_physbase, - (unsigned long )r_access->u.r_baseport, value); - -return value; +return assigned_dev_ioport_rw (opaque, addr,4 ,NULL ); }static uint32_t slow_bar_readb (void *opaque,target_phys_addr_t addr)
@@ -305,7 +312,7 @@ static void assigned_dev_ioport_map(PCIDevice *pci_dev, int region_num,DEBUG ("e_phys=0x%" FMT_PCIBUS " r_baseport=%x type=0x%x len=%" FMT_PCIBUS " region_num=%d \n" , addr, region->u.r_baseport, type, size, region_num); -if (first_map) { +if (first_map && region->region->resource_fd <0 ) {struct ioperm_data *data; data =qemu_mallocz (sizeof (struct ioperm_data ));
@@ -586,19 +593,41 @@ static int assigned_dev_register_regions(PCIRegion *io_regions, slow_map ? assigned_dev_iomem_map_slow : assigned_dev_iomem_map);continue ; + }else { +/* handle port io regions */ +uint32_t val; +int ret; + +/* Test kernel support for ioport resource read/write. Old +* kernels return EIO. New kernels only allow 1/2/4 byte reads +* so should return EINVAL for a 3 byte read */ + ret =pread (pci_dev->v_addrs[i].region->resource_fd, &val,3 ,0 ); +if (ret ==3 ) { +fprintf (stderr,"I/O port resource supports 3 byte read?!\n" ); +abort (); + }else if (errno ==EIO ) { +fprintf (stderr, +"pcisysfs does not support rw ioport resource\n" ); +close (pci_dev->v_addrs[i].region->resource_fd); + pci_dev->v_addrs[i].region->resource_fd =-1 ; + }else if (errno !=EINVAL ) { +fprintf (stderr,"Unexpected return from ioport pread (%d) %s\n" , + errno,strerror (errno)); +abort (); + } + + pci_dev->v_addrs[i].e_physbase = cur_region->base_addr; + pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr; + pci_dev->v_addrs[i].r_size = cur_region->size; + pci_dev->v_addrs[i].e_size =0 ; + +pci_register_bar ((PCIDevice *) pci_dev, i, + cur_region->size,PCI_BASE_ADDRESS_SPACE_IO , + assigned_dev_ioport_map); + +/* not relevant for port io */ + pci_dev->v_addrs[i].memory_index =0 ; } -/* handle port io regions */ - pci_dev->v_addrs[i].e_physbase = cur_region->base_addr; - pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr; - pci_dev->v_addrs[i].r_size = cur_region->size; - pci_dev->v_addrs[i].e_size =0 ; - -pci_register_bar ((PCIDevice *) pci_dev, i, - cur_region->size,PCI_BASE_ADDRESS_SPACE_IO , - assigned_dev_ioport_map); - -/* not relevant for port io */ - pci_dev->v_addrs[i].memory_index =0 ; }/* success */
@@ -705,20 +734,22 @@ again:continue ;if (flags &IORESOURCE_MEM ) { flags &= ~IORESOURCE_IO ; -if (r !=PCI_ROM_SLOT ) { -snprintf (name,sizeof (name),"%sresource%d" , dir, r); - fd =open (name,O_RDWR ); -if (fd ==-1 ) -continue ; - rp->resource_fd = fd; - } - }else + }else { flags &= ~IORESOURCE_PREFETCH ; + } +if (r !=PCI_ROM_SLOT ) { +snprintf (name,sizeof (name),"%sresource%d" , dir, r); + fd =open (name,O_RDWR ); +if (fd ==-1 ) +continue ; + rp->resource_fd = fd; + } rp->type = flags; rp->valid =1 ; rp->base_addr = start; rp->size = size; + pci_dev->v_addrs[r].region = rp;DEBUG ("region %d size %d start 0x%llx type %d resource_fd %d\n" , r, rp->size, start, rp->type, rp->resource_fd); }
@@ -780,8 +811,10 @@ static void free_assigned_device(AssignedDevice *dev)continue ;if (pci_region->type &IORESOURCE_IO ) { -kvm_remove_ioperm_data (region->u.r_baseport, region->r_size); -continue ; +if (pci_region->resource_fd <0 ) { +kvm_remove_ioperm_data (region->u.r_baseport, + region->r_size); + } }else if (pci_region->type &IORESOURCE_MEM ) {if (region->u.r_virtbase) {if (region->memory_index) {
@@ -795,11 +828,11 @@ static void free_assigned_device(AssignedDevice *dev)fprintf (stderr,"Failed to unmap assigned device region: %s\n" ,strerror (errno)); -if (pci_region->resource_fd >=0 ) { -close (pci_region->resource_fd); - } } - } + } +if (pci_region->resource_fd >=0 ) { +close (pci_region->resource_fd); + } }if (dev->cap.available &ASSIGNED_DEVICE_CAP_MSIX )
hw/device-assignment.h +1 -0
@@ -71,6 +71,7 @@ typedef struct {int num;/* our index within v_addrs[] */ pcibus_t e_size;/* emulated size of region in bytes */ pcibus_t r_size;/* real size of region in bytes */ +PCIRegion *region; } AssignedDevRegion; typedef struct AssignedDevice {
PATCH v3 1/1 device-assignment: Use PCI I/O port sysfs resource file when available
When supported by the host kernel, we can use read/write on the PCI sysfs resource file for I/O port regions. This allows us to avoid raw in/out commands and works with deprivileged guests via libvirt. For uid 0 callers, we use in/out directly to avoid any compatibility issues.
---
Required kernel patch pending here:
http://www.spinics.net/lists/linux-pci/msg09389.html
v3: Fixup sysfs errno checking, anything except EINVAL should fall
back to in/out. This covers things like EPERM on the resource file too.
v2: Drop getuid() since it doesn't guarantee permissions
Don't use in/out as a fallback since we don't have permissions Consolidate ioport read/write functions
hw/device-assignment.c | 201 +++++++++++++++++++++++++++--------------------- hw/device-assignment.h | 1 2 files changed, 116 insertions(+), 86 deletions(-)
hw/device-assignment.c +115 -86
@@ -62,93 +62,100 @@ static void assigned_dev_load_option_rom(AssignedDevice *dev);static void assigned_dev_unregister_msix_mmio (AssignedDevice *dev); -static uint32_t guest_to_host_ioport (AssignedDevRegion *region,uint32_t addr) +static uint32_t assigned_dev_ioport_rw (AssignedDevRegion *dev_region, +uint32_t addr,int len,uint32_t *val) { -return region->u.r_baseport + (addr - region->e_physbase); +uint32_t ret =0 ; +uint32_t offset = addr - dev_region->e_physbase; +int fd = dev_region->region->resource_fd; + +if (fd >=0 ) { +if (val) { +DEBUG ("pwrite val=%x, len=%d, e_phys=%x, offset=%x\n" , + *val, len, addr, offset); +if (pwrite (fd, val, len, offset) != len) { +fprintf (stderr,"%s - pwrite failed %s\n" , + __func__,strerror (errno)); + } + }else { +if (pread (fd, &ret, len, offset) != len) { +fprintf (stderr,"%s - pread failed %s\n" , + __func__,strerror (errno)); + ret = (1UL << (len *8 )) -1 ; + } +DEBUG ("pread ret=%x, len=%d, e_phys=%x, offset=%x\n" , + ret, len, addr, offset); + } + }else { +uint32_t port = offset + dev_region->u.r_baseport; + +if (val) { +DEBUG ("out val=%x, len=%d, e_phys=%x, host=%x\n" , + *val, len, addr, port); +switch (len) { +case 1 : +outb (*val, port); +break ; +case 2 : +outw (*val, port); +break ; +case 4 : +outl (*val, port); +break ; + } + }else { +switch (len) { +case 1 : + ret =inb (port); +break ; +case 2 : + ret =inw (port); +break ; +case 4 : + ret =inl (port); +break ; + } +DEBUG ("in val=%x, len=%d, e_phys=%x, host=%x\n" , + ret, len, addr, port); + } + } +return ret; }static void assigned_dev_ioport_writeb (void *opaque,uint32_t addr,uint32_t value) { -AssignedDevRegion *r_access = opaque; -uint32_t r_pio =guest_to_host_ioport (r_access, addr); - -DEBUG ("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n" , - r_pio, (int )r_access->e_physbase, - (unsigned long )r_access->u.r_baseport, value); - -outb (value, r_pio); +assigned_dev_ioport_rw (opaque, addr,1 , &value); +return ; }static void assigned_dev_ioport_writew (void *opaque,uint32_t addr,uint32_t value) { -AssignedDevRegion *r_access = opaque; -uint32_t r_pio =guest_to_host_ioport (r_access, addr); - -DEBUG ("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n" , - r_pio, (int )r_access->e_physbase, - (unsigned long )r_access->u.r_baseport, value); - -outw (value, r_pio); +assigned_dev_ioport_rw (opaque, addr,2 , &value); +return ; }static void assigned_dev_ioport_writel (void *opaque,uint32_t addr,uint32_t value) { -AssignedDevRegion *r_access = opaque; -uint32_t r_pio =guest_to_host_ioport (r_access, addr); - -DEBUG ("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n" , - r_pio, (int )r_access->e_physbase, - (unsigned long )r_access->u.r_baseport, value); - -outl (value, r_pio); +assigned_dev_ioport_rw (opaque, addr,4 , &value); +return ; }static uint32_t assigned_dev_ioport_readb (void *opaque,uint32_t addr) { -AssignedDevRegion *r_access = opaque; -uint32_t r_pio =guest_to_host_ioport (r_access, addr); -uint32_t value; - - value =inb (r_pio); - -DEBUG ("r_pio=%08x e_physbase=%08x r_=%08lx value=%08x\n" , - r_pio, (int )r_access->e_physbase, - (unsigned long )r_access->u.r_baseport, value); - -return value; +return assigned_dev_ioport_rw (opaque, addr,1 ,NULL ); }static uint32_t assigned_dev_ioport_readw (void *opaque,uint32_t addr) { -AssignedDevRegion *r_access = opaque; -uint32_t r_pio =guest_to_host_ioport (r_access, addr); -uint32_t value; - - value =inw (r_pio); - -DEBUG ("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n" , - r_pio, (int )r_access->e_physbase, - (unsigned long )r_access->u.r_baseport, value); - -return value; +return assigned_dev_ioport_rw (opaque, addr,2 ,NULL ); }static uint32_t assigned_dev_ioport_readl (void *opaque,uint32_t addr) { -AssignedDevRegion *r_access = opaque; -uint32_t r_pio =guest_to_host_ioport (r_access, addr); -uint32_t value; - - value =inl (r_pio); - -DEBUG ("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n" , - r_pio, (int )r_access->e_physbase, - (unsigned long )r_access->u.r_baseport, value); - -return value; +return assigned_dev_ioport_rw (opaque, addr,4 ,NULL ); }static uint32_t slow_bar_readb (void *opaque,target_phys_addr_t addr)
@@ -305,7 +312,7 @@ static void assigned_dev_ioport_map(PCIDevice *pci_dev, int region_num,DEBUG ("e_phys=0x%" FMT_PCIBUS " r_baseport=%x type=0x%x len=%" FMT_PCIBUS " region_num=%d \n" , addr, region->u.r_baseport, type, size, region_num); -if (first_map) { +if (first_map && region->region->resource_fd <0 ) {struct ioperm_data *data; data =qemu_mallocz (sizeof (struct ioperm_data ));
@@ -586,19 +593,37 @@ static int assigned_dev_register_regions(PCIRegion *io_regions, slow_map ? assigned_dev_iomem_map_slow : assigned_dev_iomem_map);continue ; + }else { +/* handle port io regions */ +uint32_t val; +int ret; + +/* Test kernel support for ioport resource read/write. Old +* kernels return EIO. New kernels only allow 1/2/4 byte reads +* so should return EINVAL for a 3 byte read */ + ret =pread (pci_dev->v_addrs[i].region->resource_fd, &val,3 ,0 ); +if (ret ==3 ) { +fprintf (stderr,"I/O port resource supports 3 byte read?!\n" ); +abort (); + }else if (errno !=EINVAL ) { +fprintf (stderr,"Using raw in/out ioport access (sysfs - %s)\n" , +strerror (errno)); +close (pci_dev->v_addrs[i].region->resource_fd); + pci_dev->v_addrs[i].region->resource_fd =-1 ; + } + + pci_dev->v_addrs[i].e_physbase = cur_region->base_addr; + pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr; + pci_dev->v_addrs[i].r_size = cur_region->size; + pci_dev->v_addrs[i].e_size =0 ; + +pci_register_bar ((PCIDevice *) pci_dev, i, + cur_region->size,PCI_BASE_ADDRESS_SPACE_IO , + assigned_dev_ioport_map); + +/* not relevant for port io */ + pci_dev->v_addrs[i].memory_index =0 ; } -/* handle port io regions */ - pci_dev->v_addrs[i].e_physbase = cur_region->base_addr; - pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr; - pci_dev->v_addrs[i].r_size = cur_region->size; - pci_dev->v_addrs[i].e_size =0 ; - -pci_register_bar ((PCIDevice *) pci_dev, i, - cur_region->size,PCI_BASE_ADDRESS_SPACE_IO , - assigned_dev_ioport_map); - -/* not relevant for port io */ - pci_dev->v_addrs[i].memory_index =0 ; }/* success */
@@ -705,20 +730,22 @@ again:continue ;if (flags &IORESOURCE_MEM ) { flags &= ~IORESOURCE_IO ; -if (r !=PCI_ROM_SLOT ) { -snprintf (name,sizeof (name),"%sresource%d" , dir, r); - fd =open (name,O_RDWR ); -if (fd ==-1 ) -continue ; - rp->resource_fd = fd; - } - }else + }else { flags &= ~IORESOURCE_PREFETCH ; + } +if (r !=PCI_ROM_SLOT ) { +snprintf (name,sizeof (name),"%sresource%d" , dir, r); + fd =open (name,O_RDWR ); +if (fd ==-1 ) +continue ; + rp->resource_fd = fd; + } rp->type = flags; rp->valid =1 ; rp->base_addr = start; rp->size = size; + pci_dev->v_addrs[r].region = rp;DEBUG ("region %d size %d start 0x%llx type %d resource_fd %d\n" , r, rp->size, start, rp->type, rp->resource_fd); }
@@ -780,8 +807,10 @@ static void free_assigned_device(AssignedDevice *dev)continue ;if (pci_region->type &IORESOURCE_IO ) { -kvm_remove_ioperm_data (region->u.r_baseport, region->r_size); -continue ; +if (pci_region->resource_fd <0 ) { +kvm_remove_ioperm_data (region->u.r_baseport, + region->r_size); + } }else if (pci_region->type &IORESOURCE_MEM ) {if (region->u.r_virtbase) {if (region->memory_index) {
@@ -795,11 +824,11 @@ static void free_assigned_device(AssignedDevice *dev)fprintf (stderr,"Failed to unmap assigned device region: %s\n" ,strerror (errno)); -if (pci_region->resource_fd >=0 ) { -close (pci_region->resource_fd); - } } - } + } +if (pci_region->resource_fd >=0 ) { +close (pci_region->resource_fd); + } }if (dev->cap.available &ASSIGNED_DEVICE_CAP_MSIX )
hw/device-assignment.h +1 -0
@@ -71,6 +71,7 @@ typedef struct {int num;/* our index within v_addrs[] */ pcibus_t e_size;/* emulated size of region in bytes */ pcibus_t r_size;/* real size of region in bytes */ +PCIRegion *region; } AssignedDevRegion; typedef struct AssignedDevice {
PATCH v4 1/1 device-assignment: Use PCI I/O port sysfs resource file when available
When supported by the host kernel, we can use read/write on the PCI sysfs resource file for I/O port regions. This allows us to avoid raw in/out commands and works with deprivileged guests via libvirt.
---
Required kernel patch pending here:
http://www.spinics.net/lists/linux-pci/msg09389.html
v4: Remove commit sentence about uid since we removed the code
in v2. Identical patch to v3. Also note preliminary agreement to include kerenl patch:
http://www.spinics.net/lists/linux-pci/msg09412.html
v3: Fixup sysfs errno checking, anything except EINVAL should fall
back to in/out. This covers things like EPERM on the resource file too.
v2: Drop getuid() since it doesn't guarantee permissions
Don't use in/out as a fallback since we don't have permissions Consolidate ioport read/write functions
hw/device-assignment.c | 201 +++++++++++++++++++++++++++--------------------- hw/device-assignment.h | 1 2 files changed, 116 insertions(+), 86 deletions(-)
hw/device-assignment.c +115 -86
@@ -62,93 +62,100 @@ static void assigned_dev_load_option_rom(AssignedDevice *dev);static void assigned_dev_unregister_msix_mmio (AssignedDevice *dev); -static uint32_t guest_to_host_ioport (AssignedDevRegion *region,uint32_t addr) +static uint32_t assigned_dev_ioport_rw (AssignedDevRegion *dev_region, +uint32_t addr,int len,uint32_t *val) { -return region->u.r_baseport + (addr - region->e_physbase); +uint32_t ret =0 ; +uint32_t offset = addr - dev_region->e_physbase; +int fd = dev_region->region->resource_fd; + +if (fd >=0 ) { +if (val) { +DEBUG ("pwrite val=%x, len=%d, e_phys=%x, offset=%x\n" , + *val, len, addr, offset); +if (pwrite (fd, val, len, offset) != len) { +fprintf (stderr,"%s - pwrite failed %s\n" , + __func__,strerror (errno)); + } + }else { +if (pread (fd, &ret, len, offset) != len) { +fprintf (stderr,"%s - pread failed %s\n" , + __func__,strerror (errno)); + ret = (1UL << (len *8 )) -1 ; + } +DEBUG ("pread ret=%x, len=%d, e_phys=%x, offset=%x\n" , + ret, len, addr, offset); + } + }else { +uint32_t port = offset + dev_region->u.r_baseport; + +if (val) { +DEBUG ("out val=%x, len=%d, e_phys=%x, host=%x\n" , + *val, len, addr, port); +switch (len) { +case 1 : +outb (*val, port); +break ; +case 2 : +outw (*val, port); +break ; +case 4 : +outl (*val, port); +break ; + } + }else { +switch (len) { +case 1 : + ret =inb (port); +break ; +case 2 : + ret =inw (port); +break ; +case 4 : + ret =inl (port); +break ; + } +DEBUG ("in val=%x, len=%d, e_phys=%x, host=%x\n" , + ret, len, addr, port); + } + } +return ret; }static void assigned_dev_ioport_writeb (void *opaque,uint32_t addr,uint32_t value) { -AssignedDevRegion *r_access = opaque; -uint32_t r_pio =guest_to_host_ioport (r_access, addr); - -DEBUG ("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n" , - r_pio, (int )r_access->e_physbase, - (unsigned long )r_access->u.r_baseport, value); - -outb (value, r_pio); +assigned_dev_ioport_rw (opaque, addr,1 , &value); +return ; }static void assigned_dev_ioport_writew (void *opaque,uint32_t addr,uint32_t value) { -AssignedDevRegion *r_access = opaque; -uint32_t r_pio =guest_to_host_ioport (r_access, addr); - -DEBUG ("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n" , - r_pio, (int )r_access->e_physbase, - (unsigned long )r_access->u.r_baseport, value); - -outw (value, r_pio); +assigned_dev_ioport_rw (opaque, addr,2 , &value); +return ; }static void assigned_dev_ioport_writel (void *opaque,uint32_t addr,uint32_t value) { -AssignedDevRegion *r_access = opaque; -uint32_t r_pio =guest_to_host_ioport (r_access, addr); - -DEBUG ("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n" , - r_pio, (int )r_access->e_physbase, - (unsigned long )r_access->u.r_baseport, value); - -outl (value, r_pio); +assigned_dev_ioport_rw (opaque, addr,4 , &value); +return ; }static uint32_t assigned_dev_ioport_readb (void *opaque,uint32_t addr) { -AssignedDevRegion *r_access = opaque; -uint32_t r_pio =guest_to_host_ioport (r_access, addr); -uint32_t value; - - value =inb (r_pio); - -DEBUG ("r_pio=%08x e_physbase=%08x r_=%08lx value=%08x\n" , - r_pio, (int )r_access->e_physbase, - (unsigned long )r_access->u.r_baseport, value); - -return value; +return assigned_dev_ioport_rw (opaque, addr,1 ,NULL ); }static uint32_t assigned_dev_ioport_readw (void *opaque,uint32_t addr) { -AssignedDevRegion *r_access = opaque; -uint32_t r_pio =guest_to_host_ioport (r_access, addr); -uint32_t value; - - value =inw (r_pio); - -DEBUG ("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n" , - r_pio, (int )r_access->e_physbase, - (unsigned long )r_access->u.r_baseport, value); - -return value; +return assigned_dev_ioport_rw (opaque, addr,2 ,NULL ); }static uint32_t assigned_dev_ioport_readl (void *opaque,uint32_t addr) { -AssignedDevRegion *r_access = opaque; -uint32_t r_pio =guest_to_host_ioport (r_access, addr); -uint32_t value; - - value =inl (r_pio); - -DEBUG ("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n" , - r_pio, (int )r_access->e_physbase, - (unsigned long )r_access->u.r_baseport, value); - -return value; +return assigned_dev_ioport_rw (opaque, addr,4 ,NULL ); }static uint32_t slow_bar_readb (void *opaque,target_phys_addr_t addr)
@@ -305,7 +312,7 @@ static void assigned_dev_ioport_map(PCIDevice *pci_dev, int region_num,DEBUG ("e_phys=0x%" FMT_PCIBUS " r_baseport=%x type=0x%x len=%" FMT_PCIBUS " region_num=%d \n" , addr, region->u.r_baseport, type, size, region_num); -if (first_map) { +if (first_map && region->region->resource_fd <0 ) {struct ioperm_data *data; data =qemu_mallocz (sizeof (struct ioperm_data ));
@@ -586,19 +593,37 @@ static int assigned_dev_register_regions(PCIRegion *io_regions, slow_map ? assigned_dev_iomem_map_slow : assigned_dev_iomem_map);continue ; + }else { +/* handle port io regions */ +uint32_t val; +int ret; + +/* Test kernel support for ioport resource read/write. Old +* kernels return EIO. New kernels only allow 1/2/4 byte reads +* so should return EINVAL for a 3 byte read */ + ret =pread (pci_dev->v_addrs[i].region->resource_fd, &val,3 ,0 ); +if (ret ==3 ) { +fprintf (stderr,"I/O port resource supports 3 byte read?!\n" ); +abort (); + }else if (errno !=EINVAL ) { +fprintf (stderr,"Using raw in/out ioport access (sysfs - %s)\n" , +strerror (errno)); +close (pci_dev->v_addrs[i].region->resource_fd); + pci_dev->v_addrs[i].region->resource_fd =-1 ; + } + + pci_dev->v_addrs[i].e_physbase = cur_region->base_addr; + pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr; + pci_dev->v_addrs[i].r_size = cur_region->size; + pci_dev->v_addrs[i].e_size =0 ; + +pci_register_bar ((PCIDevice *) pci_dev, i, + cur_region->size,PCI_BASE_ADDRESS_SPACE_IO , + assigned_dev_ioport_map); + +/* not relevant for port io */ + pci_dev->v_addrs[i].memory_index =0 ; } -/* handle port io regions */ - pci_dev->v_addrs[i].e_physbase = cur_region->base_addr; - pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr; - pci_dev->v_addrs[i].r_size = cur_region->size; - pci_dev->v_addrs[i].e_size =0 ; - -pci_register_bar ((PCIDevice *) pci_dev, i, - cur_region->size,PCI_BASE_ADDRESS_SPACE_IO , - assigned_dev_ioport_map); - -/* not relevant for port io */ - pci_dev->v_addrs[i].memory_index =0 ; }/* success */
@@ -705,20 +730,22 @@ again:continue ;if (flags &IORESOURCE_MEM ) { flags &= ~IORESOURCE_IO ; -if (r !=PCI_ROM_SLOT ) { -snprintf (name,sizeof (name),"%sresource%d" , dir, r); - fd =open (name,O_RDWR ); -if (fd ==-1 ) -continue ; - rp->resource_fd = fd; - } - }else + }else { flags &= ~IORESOURCE_PREFETCH ; + } +if (r !=PCI_ROM_SLOT ) { +snprintf (name,sizeof (name),"%sresource%d" , dir, r); + fd =open (name,O_RDWR ); +if (fd ==-1 ) +continue ; + rp->resource_fd = fd; + } rp->type = flags; rp->valid =1 ; rp->base_addr = start; rp->size = size; + pci_dev->v_addrs[r].region = rp;DEBUG ("region %d size %d start 0x%llx type %d resource_fd %d\n" , r, rp->size, start, rp->type, rp->resource_fd); }
@@ -780,8 +807,10 @@ static void free_assigned_device(AssignedDevice *dev)continue ;if (pci_region->type &IORESOURCE_IO ) { -kvm_remove_ioperm_data (region->u.r_baseport, region->r_size); -continue ; +if (pci_region->resource_fd <0 ) { +kvm_remove_ioperm_data (region->u.r_baseport, + region->r_size); + } }else if (pci_region->type &IORESOURCE_MEM ) {if (region->u.r_virtbase) {if (region->memory_index) {
@@ -795,11 +824,11 @@ static void free_assigned_device(AssignedDevice *dev)fprintf (stderr,"Failed to unmap assigned device region: %s\n" ,strerror (errno)); -if (pci_region->resource_fd >=0 ) { -close (pci_region->resource_fd); - } } - } + } +if (pci_region->resource_fd >=0 ) { +close (pci_region->resource_fd); + } }if (dev->cap.available &ASSIGNED_DEVICE_CAP_MSIX )
hw/device-assignment.h +1 -0
@@ -71,6 +71,7 @@ typedef struct {int num;/* our index within v_addrs[] */ pcibus_t e_size;/* emulated size of region in bytes */ pcibus_t r_size;/* real size of region in bytes */ +PCIRegion *region; } AssignedDevRegion; typedef struct AssignedDevice {
Quoted 6 lines · * Alex Williamson ([email protected]) wrote:
When supported by the host kernel, we can use read/write on the PCI sysfs resource file for I/O port regions. This allows us to avoid raw in/out commands and works with deprivileged guests via libvirt.
Signed-off-by Alex Williamson <[email protected]>
Marcelo Tosatti
AppliedQuoted 20 lines · Alex Williamson
When supported by the host kernel, we can use read/write on the PCI sysfs resource file for I/O port regions. This allows us to avoid raw in/out commands and works with deprivileged guests via libvirt.
Signed-off-by Alex Williamson <[email protected]>---
Required kernel patch pending here:
http://www.spinics.net/lists/linux-pci/msg09389.htmlv4: Remove commit sentence about uid since we removed the code
in v2. Identical patch to v3. Also note preliminary agreement to include kerenl patch:
http://www.spinics.net/lists/linux-pci/msg09412.htmlv3: Fixup sysfs errno checking, anything except EINVAL should fall
back to in/out. This covers things like EPERM on the resource file too.
Quoted 7 lines
v2: Drop
getuid()since it doesn't guarantee permissions
Don't use in/out as a fallback since we don't have permissions Consolidate ioport read/write functionshw/device-assignment.c | 201 +++++++++++++++++++++++++++--------------------- hw/device-assignment.h | 1 2 files changed, 116 insertions(+), 86 deletions(-)
Applied, thanks.