diff mbox

[PATCHv4,08/11] tests: Clean up IO handling in ide-test

Message ID 1477012792-3326-9-git-send-email-david@gibson.dropbear.id.au
State New
Headers show

Commit Message

David Gibson Oct. 21, 2016, 1:19 a.m. UTC
ide-test uses many explicit inb() / outb() operations for its IO, which
means it's not portable to non-x86 platforms.  This cleans it up to use
the libqos PCI accessors instead.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
---
 tests/ide-test.c | 179 ++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 118 insertions(+), 61 deletions(-)

Comments

Laurent Vivier Oct. 21, 2016, 8:31 a.m. UTC | #1
On 21/10/2016 03:19, David Gibson wrote:
> ide-test uses many explicit inb() / outb() operations for its IO, which
> means it's not portable to non-x86 platforms.  This cleans it up to use
> the libqos PCI accessors instead.
> 
> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
> ---
>  tests/ide-test.c | 179 ++++++++++++++++++++++++++++++++++++-------------------
>  1 file changed, 118 insertions(+), 61 deletions(-)
> 
> diff --git a/tests/ide-test.c b/tests/ide-test.c
> index a8a4081..86c4373 100644
> --- a/tests/ide-test.c
> +++ b/tests/ide-test.c
...
> @@ -654,7 +700,8 @@ typedef struct Read10CDB {
>      uint16_t padding;
>  } __attribute__((__packed__)) Read10CDB;
>  
> -static void send_scsi_cdb_read10(uint64_t lba, int nblocks)
> +static void send_scsi_cdb_read10(QPCIDevice *dev, void *ide_base,
> +                                 uint64_t lba, int nblocks)
>  {
>      Read10CDB pkt = { .padding = 0 };
>      int i;
> @@ -670,7 +717,8 @@ static void send_scsi_cdb_read10(uint64_t lba, int nblocks)
>  
>      /* Send Packet */
>      for (i = 0; i < sizeof(Read10CDB)/2; i++) {
> -        outw(IDE_BASE + reg_data, cpu_to_le16(((uint16_t *)&pkt)[i]));
> +        qpci_io_writew(dev, ide_base + reg_data,
> +                       le16_to_cpu(((uint16_t *)&pkt)[i]));
>      }
>  }
>  
...
> @@ -780,7 +836,8 @@ static void cdrom_pio_impl(int nblocks)
>  
>          /* HP4: Transfer_Data */
>          for (j = 0; j < MIN((limit / 2), rem); j++) {
> -            rx[offset + j] = le16_to_cpu(inw(IDE_BASE + reg_data));
> +            rx[offset + j] = cpu_to_le16(qpci_io_readw(dev,
> +                                                       ide_base + reg_data));
>          }
>      }

Why do you swap le16_to_cpu() and cpu_to_le16()?

I think this is not semantically correct and the purpose of this patch
is only to replace inX()/outX() functions.

Laurent
David Gibson Oct. 21, 2016, 9:05 a.m. UTC | #2
On Fri, Oct 21, 2016 at 10:31:27AM +0200, Laurent Vivier wrote:
> 
> 
> On 21/10/2016 03:19, David Gibson wrote:
> > ide-test uses many explicit inb() / outb() operations for its IO, which
> > means it's not portable to non-x86 platforms.  This cleans it up to use
> > the libqos PCI accessors instead.
> > 
> > Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
> > ---
> >  tests/ide-test.c | 179 ++++++++++++++++++++++++++++++++++++-------------------
> >  1 file changed, 118 insertions(+), 61 deletions(-)
> > 
> > diff --git a/tests/ide-test.c b/tests/ide-test.c
> > index a8a4081..86c4373 100644
> > --- a/tests/ide-test.c
> > +++ b/tests/ide-test.c
> ...
> > @@ -654,7 +700,8 @@ typedef struct Read10CDB {
> >      uint16_t padding;
> >  } __attribute__((__packed__)) Read10CDB;
> >  
> > -static void send_scsi_cdb_read10(uint64_t lba, int nblocks)
> > +static void send_scsi_cdb_read10(QPCIDevice *dev, void *ide_base,
> > +                                 uint64_t lba, int nblocks)
> >  {
> >      Read10CDB pkt = { .padding = 0 };
> >      int i;
> > @@ -670,7 +717,8 @@ static void send_scsi_cdb_read10(uint64_t lba, int nblocks)
> >  
> >      /* Send Packet */
> >      for (i = 0; i < sizeof(Read10CDB)/2; i++) {
> > -        outw(IDE_BASE + reg_data, cpu_to_le16(((uint16_t *)&pkt)[i]));
> > +        qpci_io_writew(dev, ide_base + reg_data,
> > +                       le16_to_cpu(((uint16_t *)&pkt)[i]));
> >      }
> >  }
> >  
> ...
> > @@ -780,7 +836,8 @@ static void cdrom_pio_impl(int nblocks)
> >  
> >          /* HP4: Transfer_Data */
> >          for (j = 0; j < MIN((limit / 2), rem); j++) {
> > -            rx[offset + j] = le16_to_cpu(inw(IDE_BASE + reg_data));
> > +            rx[offset + j] = cpu_to_le16(qpci_io_readw(dev,
> > +                                                       ide_base + reg_data));
> >          }
> >      }
> 
> Why do you swap le16_to_cpu() and cpu_to_le16()?

So, obviously le16_to_cpu() and cpu_to_le16() are functionally
identical.  But I think my version is conceptually more correct.

This is streaming data via PIO - we're essentially reading a byte
stream in 16-bit chunks.  So, overall we want to preserve byte address
order.  qpci_io_readw() (and inw()) is designed for reading registers
so it preserves byte significance, rather than address order.  So,
since the IDE registers are LE, that means if implicitly contains an
le16_to_cpu().  The cpu_to_le16() undoes that so that rx[] ends up
with the bytestream in the correct order.

> I think this is not semantically correct and the purpose of this patch
> is only to replace inX()/outX() functions.

No, I believe it is correct.  And I think the original was technically
wrong, because inw() reads the register in target endian rather than
LE.  Of course the only common platform with "real" in/out
instructions is x86, which is LE anyway, so it hardly matters.
Laurent Vivier Oct. 21, 2016, 10:07 a.m. UTC | #3
On 21/10/2016 11:05, David Gibson wrote:
> On Fri, Oct 21, 2016 at 10:31:27AM +0200, Laurent Vivier wrote:
>>
>>
>> On 21/10/2016 03:19, David Gibson wrote:
>>> ide-test uses many explicit inb() / outb() operations for its IO, which
>>> means it's not portable to non-x86 platforms.  This cleans it up to use
>>> the libqos PCI accessors instead.
>>>
>>> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
>>> ---
>>>  tests/ide-test.c | 179 ++++++++++++++++++++++++++++++++++++-------------------
>>>  1 file changed, 118 insertions(+), 61 deletions(-)
>>>
>>> diff --git a/tests/ide-test.c b/tests/ide-test.c
>>> index a8a4081..86c4373 100644
>>> --- a/tests/ide-test.c
>>> +++ b/tests/ide-test.c
>> ...
>>> @@ -654,7 +700,8 @@ typedef struct Read10CDB {
>>>      uint16_t padding;
>>>  } __attribute__((__packed__)) Read10CDB;
>>>  
>>> -static void send_scsi_cdb_read10(uint64_t lba, int nblocks)
>>> +static void send_scsi_cdb_read10(QPCIDevice *dev, void *ide_base,
>>> +                                 uint64_t lba, int nblocks)
>>>  {
>>>      Read10CDB pkt = { .padding = 0 };
>>>      int i;
>>> @@ -670,7 +717,8 @@ static void send_scsi_cdb_read10(uint64_t lba, int nblocks)
>>>  
>>>      /* Send Packet */
>>>      for (i = 0; i < sizeof(Read10CDB)/2; i++) {
>>> -        outw(IDE_BASE + reg_data, cpu_to_le16(((uint16_t *)&pkt)[i]));
>>> +        qpci_io_writew(dev, ide_base + reg_data,
>>> +                       le16_to_cpu(((uint16_t *)&pkt)[i]));
>>>      }
>>>  }
>>>  
>> ...
>>> @@ -780,7 +836,8 @@ static void cdrom_pio_impl(int nblocks)
>>>  
>>>          /* HP4: Transfer_Data */
>>>          for (j = 0; j < MIN((limit / 2), rem); j++) {
>>> -            rx[offset + j] = le16_to_cpu(inw(IDE_BASE + reg_data));
>>> +            rx[offset + j] = cpu_to_le16(qpci_io_readw(dev,
>>> +                                                       ide_base + reg_data));
>>>          }
>>>      }
>>
>> Why do you swap le16_to_cpu() and cpu_to_le16()?
> 
> So, obviously le16_to_cpu() and cpu_to_le16() are functionally
> identical.  But I think my version is conceptually more correct.

I agree

> 
> This is streaming data via PIO - we're essentially reading a byte
> stream in 16-bit chunks.  So, overall we want to preserve byte address
> order.  qpci_io_readw() (and inw()) is designed for reading registers
> so it preserves byte significance, rather than address order.  So,
> since the IDE registers are LE, that means if implicitly contains an
> le16_to_cpu().  The cpu_to_le16() undoes that so that rx[] ends up
> with the bytestream in the correct order.

In fact, it has an implicit "le16 to target CPU", so you should not have
a "cpu_to_le16()" but a "qtest_big_endian() != host_big_endian
bswap16()" to swap it.

As this test works only with a little endian guest, the first "LE16 to
target CPU" is a no-op, and the second one does something only on big
endian host so when "qtest_big_endian() != host_big_endian".

>> I think this is not semantically correct and the purpose of this patch
>> is only to replace inX()/outX() functions.
> 
> No, I believe it is correct.  And I think the original was technically
> wrong, because inw() reads the register in target endian rather than
> LE.  Of course the only common platform with "real" in/out
> instructions is x86, which is LE anyway, so it hardly matters.

I agree, this is why I think a "qtest_big_endian() != host_big_endian
then bswap16()" is better than a "cpu_to_le16()".

But I also think it should be the purpose of another patch...

Thanks,
Laurent
David Gibson Oct. 22, 2016, 4:56 a.m. UTC | #4
On Fri, Oct 21, 2016 at 12:07:28PM +0200, Laurent Vivier wrote:
> 
> 
> On 21/10/2016 11:05, David Gibson wrote:
> > On Fri, Oct 21, 2016 at 10:31:27AM +0200, Laurent Vivier wrote:
> >>
> >>
> >> On 21/10/2016 03:19, David Gibson wrote:
> >>> ide-test uses many explicit inb() / outb() operations for its IO, which
> >>> means it's not portable to non-x86 platforms.  This cleans it up to use
> >>> the libqos PCI accessors instead.
> >>>
> >>> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
> >>> ---
> >>>  tests/ide-test.c | 179 ++++++++++++++++++++++++++++++++++++-------------------
> >>>  1 file changed, 118 insertions(+), 61 deletions(-)
> >>>
> >>> diff --git a/tests/ide-test.c b/tests/ide-test.c
> >>> index a8a4081..86c4373 100644
> >>> --- a/tests/ide-test.c
> >>> +++ b/tests/ide-test.c
> >> ...
> >>> @@ -654,7 +700,8 @@ typedef struct Read10CDB {
> >>>      uint16_t padding;
> >>>  } __attribute__((__packed__)) Read10CDB;
> >>>  
> >>> -static void send_scsi_cdb_read10(uint64_t lba, int nblocks)
> >>> +static void send_scsi_cdb_read10(QPCIDevice *dev, void *ide_base,
> >>> +                                 uint64_t lba, int nblocks)
> >>>  {
> >>>      Read10CDB pkt = { .padding = 0 };
> >>>      int i;
> >>> @@ -670,7 +717,8 @@ static void send_scsi_cdb_read10(uint64_t lba, int nblocks)
> >>>  
> >>>      /* Send Packet */
> >>>      for (i = 0; i < sizeof(Read10CDB)/2; i++) {
> >>> -        outw(IDE_BASE + reg_data, cpu_to_le16(((uint16_t *)&pkt)[i]));
> >>> +        qpci_io_writew(dev, ide_base + reg_data,
> >>> +                       le16_to_cpu(((uint16_t *)&pkt)[i]));
> >>>      }
> >>>  }
> >>>  
> >> ...
> >>> @@ -780,7 +836,8 @@ static void cdrom_pio_impl(int nblocks)
> >>>  
> >>>          /* HP4: Transfer_Data */
> >>>          for (j = 0; j < MIN((limit / 2), rem); j++) {
> >>> -            rx[offset + j] = le16_to_cpu(inw(IDE_BASE + reg_data));
> >>> +            rx[offset + j] = cpu_to_le16(qpci_io_readw(dev,
> >>> +                                                       ide_base + reg_data));
> >>>          }
> >>>      }
> >>
> >> Why do you swap le16_to_cpu() and cpu_to_le16()?
> > 
> > So, obviously le16_to_cpu() and cpu_to_le16() are functionally
> > identical.  But I think my version is conceptually more correct.
> 
> I agree
> 
> > 
> > This is streaming data via PIO - we're essentially reading a byte
> > stream in 16-bit chunks.  So, overall we want to preserve byte address
> > order.  qpci_io_readw() (and inw()) is designed for reading registers
> > so it preserves byte significance, rather than address order.  So,
> > since the IDE registers are LE, that means if implicitly contains an
> > le16_to_cpu().  The cpu_to_le16() undoes that so that rx[] ends up
> > with the bytestream in the correct order.
> 
> In fact, it has an implicit "le16 to target CPU", so you should not have
> a "cpu_to_le16()" but a "qtest_big_endian() != host_big_endian
> bswap16()" to swap it.

No.. it really is le16_to_cpu().  For memory space accesses, there's
an explicit le16_to_cpu in qpci_io_readw() on top of the
order-preserving memread backend.  For IO space accesses, we call the
backend directly.  In all likely cases that will be turned into a (non
PCI) either inw() or readw().

Both inw() and readw() are defined to access in target endianness
(which is stupid, but that argument is in several other threads).  So
they have an implicit "target_to_host16()".  But the PCI versions are
defined to be always LE - on BE targets the backend will add an extra
swap, changing that target_to_host16() into an le16_to_cpu().

> As this test works only with a little endian guest, the first "LE16 to
> target CPU" is a no-op, and the second one does something only on big
> endian host so when "qtest_big_endian() != host_big_endian".
> 
> >> I think this is not semantically correct and the purpose of this patch
> >> is only to replace inX()/outX() functions.
> > 
> > No, I believe it is correct.  And I think the original was technically
> > wrong, because inw() reads the register in target endian rather than
> > LE.  Of course the only common platform with "real" in/out
> > instructions is x86, which is LE anyway, so it hardly matters.
> 
> I agree, this is why I think a "qtest_big_endian() != host_big_endian
> then bswap16()" is better than a "cpu_to_le16()".

No, cpu_to_le16() is correct.  Well.. actually using an
order-preserving read would be correct, but that's a bit awkward for
tne io port case.

> 
> But I also think it should be the purpose of another patch...
> 
> Thanks,
> Laurent
>
diff mbox

Patch

diff --git a/tests/ide-test.c b/tests/ide-test.c
index a8a4081..86c4373 100644
--- a/tests/ide-test.c
+++ b/tests/ide-test.c
@@ -137,7 +137,7 @@  static void ide_test_quit(void)
     qtest_end();
 }
 
-static QPCIDevice *get_pci_device(uint16_t *bmdma_base)
+static QPCIDevice *get_pci_device(void **bmdma_base, void **ide_base)
 {
     QPCIDevice *dev;
     uint16_t vendor_id, device_id;
@@ -156,7 +156,9 @@  static QPCIDevice *get_pci_device(uint16_t *bmdma_base)
     g_assert(device_id == PCI_DEVICE_ID_INTEL_82371SB_1);
 
     /* Map bmdma BAR */
-    *bmdma_base = (uint16_t)(uintptr_t) qpci_iomap(dev, 4, NULL);
+    *bmdma_base = qpci_iomap(dev, 4, NULL);
+
+    *ide_base = qpci_legacy_iomap(dev, IDE_BASE);
 
     qpci_device_enable(dev);
 
@@ -179,17 +181,19 @@  typedef struct PrdtEntry {
 
 static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
                             PrdtEntry *prdt, int prdt_entries,
-                            void(*post_exec)(uint64_t sector, int nb_sectors))
+                            void(*post_exec)(QPCIDevice *dev, void *ide_base,
+                                             uint64_t sector, int nb_sectors))
 {
     QPCIDevice *dev;
-    uint16_t bmdma_base;
+    void *bmdma_base;
+    void *ide_base;
     uintptr_t guest_prdt;
     size_t len;
     bool from_dev;
     uint8_t status;
     int flags;
 
-    dev = get_pci_device(&bmdma_base);
+    dev = get_pci_device(&bmdma_base, &ide_base);
 
     flags = cmd & ~0xff;
     cmd &= 0xff;
@@ -214,59 +218,60 @@  static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
     }
 
     /* Select device 0 */
-    outb(IDE_BASE + reg_device, 0 | LBA);
+    qpci_io_writeb(dev, ide_base + reg_device, 0 | LBA);
 
     /* Stop any running transfer, clear any pending interrupt */
-    outb(bmdma_base + bmreg_cmd, 0);
-    outb(bmdma_base + bmreg_status, BM_STS_INTR);
+    qpci_io_writeb(dev, bmdma_base + bmreg_cmd, 0);
+    qpci_io_writeb(dev, bmdma_base + bmreg_status, BM_STS_INTR);
 
     /* Setup PRDT */
     len = sizeof(*prdt) * prdt_entries;
     guest_prdt = guest_alloc(guest_malloc, len);
     memwrite(guest_prdt, prdt, len);
-    outl(bmdma_base + bmreg_prdt, guest_prdt);
+    qpci_io_writel(dev, bmdma_base + bmreg_prdt, guest_prdt);
 
     /* ATA DMA command */
     if (cmd == CMD_PACKET) {
         /* Enables ATAPI DMA; otherwise PIO is attempted */
-        outb(IDE_BASE + reg_feature, 0x01);
+        qpci_io_writeb(dev, ide_base + reg_feature, 0x01);
     } else {
-        outb(IDE_BASE + reg_nsectors, nb_sectors);
-        outb(IDE_BASE + reg_lba_low,    sector & 0xff);
-        outb(IDE_BASE + reg_lba_middle, (sector >> 8) & 0xff);
-        outb(IDE_BASE + reg_lba_high,   (sector >> 16) & 0xff);
+        qpci_io_writeb(dev, ide_base + reg_nsectors, nb_sectors);
+        qpci_io_writeb(dev, ide_base + reg_lba_low,    sector & 0xff);
+        qpci_io_writeb(dev, ide_base + reg_lba_middle, (sector >> 8) & 0xff);
+        qpci_io_writeb(dev, ide_base + reg_lba_high,   (sector >> 16) & 0xff);
     }
 
-    outb(IDE_BASE + reg_command, cmd);
+    qpci_io_writeb(dev, ide_base + reg_command, cmd);
 
     if (post_exec) {
-        post_exec(sector, nb_sectors);
+        post_exec(dev, ide_base, sector, nb_sectors);
     }
 
     /* Start DMA transfer */
-    outb(bmdma_base + bmreg_cmd, BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0));
+    qpci_io_writeb(dev, bmdma_base + bmreg_cmd,
+                   BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0));
 
     if (flags & CMDF_ABORT) {
-        outb(bmdma_base + bmreg_cmd, 0);
+        qpci_io_writeb(dev, bmdma_base + bmreg_cmd, 0);
     }
 
     /* Wait for the DMA transfer to complete */
     do {
-        status = inb(bmdma_base + bmreg_status);
+        status = qpci_io_readb(dev, bmdma_base + bmreg_status);
     } while ((status & (BM_STS_ACTIVE | BM_STS_INTR)) == BM_STS_ACTIVE);
 
     g_assert_cmpint(get_irq(IDE_PRIMARY_IRQ), ==, !!(status & BM_STS_INTR));
 
     /* Check IDE status code */
-    assert_bit_set(inb(IDE_BASE + reg_status), DRDY);
-    assert_bit_clear(inb(IDE_BASE + reg_status), BSY | DRQ);
+    assert_bit_set(qpci_io_readb(dev, ide_base + reg_status), DRDY);
+    assert_bit_clear(qpci_io_readb(dev, ide_base + reg_status), BSY | DRQ);
 
     /* Reading the status register clears the IRQ */
     g_assert(!get_irq(IDE_PRIMARY_IRQ));
 
     /* Stop DMA transfer if still active */
     if (status & BM_STS_ACTIVE) {
-        outb(bmdma_base + bmreg_cmd, 0);
+        qpci_io_writeb(dev, bmdma_base + bmreg_cmd, 0);
     }
 
     free_pci_device(dev);
@@ -276,6 +281,8 @@  static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
 
 static void test_bmdma_simple_rw(void)
 {
+    QPCIDevice *dev;
+    void *bmdma_base, *ide_base;
     uint8_t status;
     uint8_t *buf;
     uint8_t *cmpbuf;
@@ -289,6 +296,8 @@  static void test_bmdma_simple_rw(void)
         },
     };
 
+    dev = get_pci_device(&bmdma_base, &ide_base);
+
     buf = g_malloc(len);
     cmpbuf = g_malloc(len);
 
@@ -299,7 +308,7 @@  static void test_bmdma_simple_rw(void)
     status = send_dma_request(CMD_WRITE_DMA, 0, 1, prdt,
                               ARRAY_SIZE(prdt), NULL);
     g_assert_cmphex(status, ==, BM_STS_INTR);
-    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+    assert_bit_clear(qpci_io_readb(dev, ide_base + reg_status), DF | ERR);
 
     /* Write 0xaa pattern to sector 1 */
     memset(buf, 0xaa, len);
@@ -308,14 +317,14 @@  static void test_bmdma_simple_rw(void)
     status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt,
                               ARRAY_SIZE(prdt), NULL);
     g_assert_cmphex(status, ==, BM_STS_INTR);
-    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+    assert_bit_clear(qpci_io_readb(dev, ide_base + reg_status), DF | ERR);
 
     /* Read and verify 0x55 pattern in sector 0 */
     memset(cmpbuf, 0x55, len);
 
     status = send_dma_request(CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), NULL);
     g_assert_cmphex(status, ==, BM_STS_INTR);
-    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+    assert_bit_clear(qpci_io_readb(dev, ide_base + reg_status), DF | ERR);
 
     memread(guest_buf, buf, len);
     g_assert(memcmp(buf, cmpbuf, len) == 0);
@@ -325,7 +334,7 @@  static void test_bmdma_simple_rw(void)
 
     status = send_dma_request(CMD_READ_DMA, 1, 1, prdt, ARRAY_SIZE(prdt), NULL);
     g_assert_cmphex(status, ==, BM_STS_INTR);
-    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+    assert_bit_clear(qpci_io_readb(dev, ide_base + reg_status), DF | ERR);
 
     memread(guest_buf, buf, len);
     g_assert(memcmp(buf, cmpbuf, len) == 0);
@@ -337,6 +346,8 @@  static void test_bmdma_simple_rw(void)
 
 static void test_bmdma_short_prdt(void)
 {
+    QPCIDevice *dev;
+    void *bmdma_base, *ide_base;
     uint8_t status;
 
     PrdtEntry prdt[] = {
@@ -346,21 +357,25 @@  static void test_bmdma_short_prdt(void)
         },
     };
 
+    dev = get_pci_device(&bmdma_base, &ide_base);
+
     /* Normal request */
     status = send_dma_request(CMD_READ_DMA, 0, 1,
                               prdt, ARRAY_SIZE(prdt), NULL);
     g_assert_cmphex(status, ==, 0);
-    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+    assert_bit_clear(qpci_io_readb(dev, ide_base + reg_status), DF | ERR);
 
     /* Abort the request before it completes */
     status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1,
                               prdt, ARRAY_SIZE(prdt), NULL);
     g_assert_cmphex(status, ==, 0);
-    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+    assert_bit_clear(qpci_io_readb(dev, ide_base + reg_status), DF | ERR);
 }
 
 static void test_bmdma_one_sector_short_prdt(void)
 {
+    QPCIDevice *dev;
+    void *bmdma_base, *ide_base;
     uint8_t status;
 
     /* Read 2 sectors but only give 1 sector in PRDT */
@@ -371,21 +386,25 @@  static void test_bmdma_one_sector_short_prdt(void)
         },
     };
 
+    dev = get_pci_device(&bmdma_base, &ide_base);
+
     /* Normal request */
     status = send_dma_request(CMD_READ_DMA, 0, 2,
                               prdt, ARRAY_SIZE(prdt), NULL);
     g_assert_cmphex(status, ==, 0);
-    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+    assert_bit_clear(qpci_io_readb(dev, ide_base + reg_status), DF | ERR);
 
     /* Abort the request before it completes */
     status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 2,
                               prdt, ARRAY_SIZE(prdt), NULL);
     g_assert_cmphex(status, ==, 0);
-    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+    assert_bit_clear(qpci_io_readb(dev, ide_base + reg_status), DF | ERR);
 }
 
 static void test_bmdma_long_prdt(void)
 {
+    QPCIDevice *dev;
+    void *bmdma_base, *ide_base;
     uint8_t status;
 
     PrdtEntry prdt[] = {
@@ -395,23 +414,29 @@  static void test_bmdma_long_prdt(void)
         },
     };
 
+    dev = get_pci_device(&bmdma_base, &ide_base);
+
     /* Normal request */
     status = send_dma_request(CMD_READ_DMA, 0, 1,
                               prdt, ARRAY_SIZE(prdt), NULL);
     g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR);
-    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+    assert_bit_clear(qpci_io_readb(dev, ide_base + reg_status), DF | ERR);
 
     /* Abort the request before it completes */
     status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1,
                               prdt, ARRAY_SIZE(prdt), NULL);
     g_assert_cmphex(status, ==, BM_STS_INTR);
-    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+    assert_bit_clear(qpci_io_readb(dev, ide_base + reg_status), DF | ERR);
 }
 
 static void test_bmdma_no_busmaster(void)
 {
+    QPCIDevice *dev;
+    void *bmdma_base, *ide_base;
     uint8_t status;
 
+    dev = get_pci_device(&bmdma_base, &ide_base);
+
     /* No PRDT_EOT, each entry addr 0/size 64k, and in theory qemu shouldn't be
      * able to access it anyway because the Bus Master bit in the PCI command
      * register isn't set. This is complete nonsense, but it used to be pretty
@@ -424,7 +449,7 @@  static void test_bmdma_no_busmaster(void)
     /* Not entirely clear what the expected result is, but this is what we get
      * in practice. At least we want to be aware of any changes. */
     g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR);
-    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+    assert_bit_clear(qpci_io_readb(dev, ide_base + reg_status), DF | ERR);
 }
 
 static void test_bmdma_setup(void)
@@ -454,6 +479,8 @@  static void string_cpu_to_be16(uint16_t *s, size_t bytes)
 
 static void test_identify(void)
 {
+    QPCIDevice *dev;
+    void *bmdma_base, *ide_base;
     uint8_t data;
     uint16_t buf[256];
     int i;
@@ -464,23 +491,25 @@  static void test_identify(void)
         "-global ide-hd.ver=%s",
         tmp_path, "testdisk", "version");
 
+    dev = get_pci_device(&bmdma_base, &ide_base);
+
     /* IDENTIFY command on device 0*/
-    outb(IDE_BASE + reg_device, 0);
-    outb(IDE_BASE + reg_command, CMD_IDENTIFY);
+    qpci_io_writeb(dev, ide_base + reg_device, 0);
+    qpci_io_writeb(dev, ide_base + reg_command, CMD_IDENTIFY);
 
     /* Read in the IDENTIFY buffer and check registers */
-    data = inb(IDE_BASE + reg_device);
+    data = qpci_io_readb(dev, ide_base + reg_device);
     g_assert_cmpint(data & DEV, ==, 0);
 
     for (i = 0; i < 256; i++) {
-        data = inb(IDE_BASE + reg_status);
+        data = qpci_io_readb(dev, ide_base + reg_status);
         assert_bit_set(data, DRDY | DRQ);
         assert_bit_clear(data, BSY | DF | ERR);
 
-        ((uint16_t*) buf)[i] = inw(IDE_BASE + reg_data);
+        buf[i] = qpci_io_readw(dev, ide_base + reg_data);
     }
 
-    data = inb(IDE_BASE + reg_status);
+    data = qpci_io_readb(dev, ide_base + reg_status);
     assert_bit_set(data, DRDY);
     assert_bit_clear(data, BSY | DF | ERR | DRQ);
 
@@ -505,11 +534,15 @@  static void test_identify(void)
  */
 static void make_dirty(uint8_t device)
 {
+    QPCIDevice *dev;
+    void *bmdma_base, *ide_base;
     uint8_t status;
     size_t len = 512;
     uintptr_t guest_buf;
     void* buf;
 
+    dev = get_pci_device(&bmdma_base, &ide_base);
+
     guest_buf = guest_alloc(guest_malloc, len);
     buf = g_malloc(len);
     g_assert(guest_buf);
@@ -527,19 +560,23 @@  static void make_dirty(uint8_t device)
     status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt,
                               ARRAY_SIZE(prdt), NULL);
     g_assert_cmphex(status, ==, BM_STS_INTR);
-    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+    assert_bit_clear(qpci_io_readb(dev, ide_base + reg_status), DF | ERR);
 
     g_free(buf);
 }
 
 static void test_flush(void)
 {
+    QPCIDevice *dev;
+    void *bmdma_base, *ide_base;
     uint8_t data;
 
     ide_test_start(
         "-drive file=blkdebug::%s,if=ide,cache=writeback,format=raw",
         tmp_path);
 
+    dev = get_pci_device(&bmdma_base, &ide_base);
+
     qtest_irq_intercept_in(global_qtest, "ioapic");
 
     /* Dirty media so that CMD_FLUSH_CACHE will actually go to disk */
@@ -549,11 +586,11 @@  static void test_flush(void)
     g_free(hmp("qemu-io ide0-hd0 \"break flush_to_os A\""));
 
     /* FLUSH CACHE command on device 0*/
-    outb(IDE_BASE + reg_device, 0);
-    outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE);
+    qpci_io_writeb(dev, ide_base + reg_device, 0);
+    qpci_io_writeb(dev, ide_base + reg_command, CMD_FLUSH_CACHE);
 
     /* Check status while request is in flight*/
-    data = inb(IDE_BASE + reg_status);
+    data = qpci_io_readb(dev, ide_base + reg_status);
     assert_bit_set(data, BSY | DRDY);
     assert_bit_clear(data, DF | ERR | DRQ);
 
@@ -561,11 +598,11 @@  static void test_flush(void)
     g_free(hmp("qemu-io ide0-hd0 \"resume A\""));
 
     /* Check registers */
-    data = inb(IDE_BASE + reg_device);
+    data = qpci_io_readb(dev, ide_base + reg_device);
     g_assert_cmpint(data & DEV, ==, 0);
 
     do {
-        data = inb(IDE_BASE + reg_status);
+        data = qpci_io_readb(dev, ide_base + reg_status);
     } while (data & BSY);
 
     assert_bit_set(data, DRDY);
@@ -576,6 +613,8 @@  static void test_flush(void)
 
 static void test_retry_flush(const char *machine)
 {
+    QPCIDevice *dev;
+    void *bmdma_base, *ide_base;
     uint8_t data;
     const char *s;
 
@@ -587,17 +626,19 @@  static void test_retry_flush(const char *machine)
         "rerror=stop,werror=stop",
         debug_path, tmp_path);
 
+    dev = get_pci_device(&bmdma_base, &ide_base);
+
     qtest_irq_intercept_in(global_qtest, "ioapic");
 
     /* Dirty media so that CMD_FLUSH_CACHE will actually go to disk */
     make_dirty(0);
 
     /* FLUSH CACHE command on device 0*/
-    outb(IDE_BASE + reg_device, 0);
-    outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE);
+    qpci_io_writeb(dev, ide_base + reg_device, 0);
+    qpci_io_writeb(dev, ide_base + reg_command, CMD_FLUSH_CACHE);
 
     /* Check status while request is in flight*/
-    data = inb(IDE_BASE + reg_status);
+    data = qpci_io_readb(dev, ide_base + reg_status);
     assert_bit_set(data, BSY | DRDY);
     assert_bit_clear(data, DF | ERR | DRQ);
 
@@ -608,11 +649,11 @@  static void test_retry_flush(const char *machine)
     qmp_discard_response(s);
 
     /* Check registers */
-    data = inb(IDE_BASE + reg_device);
+    data = qpci_io_readb(dev, ide_base + reg_device);
     g_assert_cmpint(data & DEV, ==, 0);
 
     do {
-        data = inb(IDE_BASE + reg_status);
+        data = qpci_io_readb(dev, ide_base + reg_status);
     } while (data & BSY);
 
     assert_bit_set(data, DRDY);
@@ -623,11 +664,16 @@  static void test_retry_flush(const char *machine)
 
 static void test_flush_nodev(void)
 {
+    QPCIDevice *dev;
+    void *bmdma_base, *ide_base;
+
     ide_test_start("");
 
+    dev = get_pci_device(&bmdma_base, &ide_base);
+
     /* FLUSH CACHE command on device 0*/
-    outb(IDE_BASE + reg_device, 0);
-    outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE);
+    qpci_io_writeb(dev, ide_base + reg_device, 0);
+    qpci_io_writeb(dev, ide_base + reg_command, CMD_FLUSH_CACHE);
 
     /* Just testing that qemu doesn't crash... */
 
@@ -654,7 +700,8 @@  typedef struct Read10CDB {
     uint16_t padding;
 } __attribute__((__packed__)) Read10CDB;
 
-static void send_scsi_cdb_read10(uint64_t lba, int nblocks)
+static void send_scsi_cdb_read10(QPCIDevice *dev, void *ide_base,
+                                 uint64_t lba, int nblocks)
 {
     Read10CDB pkt = { .padding = 0 };
     int i;
@@ -670,7 +717,8 @@  static void send_scsi_cdb_read10(uint64_t lba, int nblocks)
 
     /* Send Packet */
     for (i = 0; i < sizeof(Read10CDB)/2; i++) {
-        outw(IDE_BASE + reg_data, cpu_to_le16(((uint16_t *)&pkt)[i]));
+        qpci_io_writew(dev, ide_base + reg_data,
+                       le16_to_cpu(((uint16_t *)&pkt)[i]));
     }
 }
 
@@ -683,13 +731,17 @@  static void nsleep(int64_t nsecs)
 
 static uint8_t ide_wait_clear(uint8_t flag)
 {
+    QPCIDevice *dev;
+    void *bmdma_base, *ide_base;
     uint8_t data;
     time_t st;
 
+    dev = get_pci_device(&bmdma_base, &ide_base);
+
     /* Wait with a 5 second timeout */
     time(&st);
     while (true) {
-        data = inb(IDE_BASE + reg_status);
+        data = qpci_io_readb(dev, ide_base + reg_status);
         if (!(data & flag)) {
             return data;
         }
@@ -723,6 +775,8 @@  static void ide_wait_intr(int irq)
 
 static void cdrom_pio_impl(int nblocks)
 {
+    QPCIDevice *dev;
+    void *bmdma_base, *ide_base;
     FILE *fh;
     int patt_blocks = MAX(16, nblocks);
     size_t patt_len = ATAPI_BLOCK_SIZE * patt_blocks;
@@ -741,13 +795,15 @@  static void cdrom_pio_impl(int nblocks)
 
     ide_test_start("-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 "
                    "-device ide-cd,drive=sr0,bus=ide.0", tmp_path);
+    dev = get_pci_device(&bmdma_base, &ide_base);
     qtest_irq_intercept_in(global_qtest, "ioapic");
 
     /* PACKET command on device 0 */
-    outb(IDE_BASE + reg_device, 0);
-    outb(IDE_BASE + reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF);
-    outb(IDE_BASE + reg_lba_high, (BYTE_COUNT_LIMIT >> 8 & 0xFF));
-    outb(IDE_BASE + reg_command, CMD_PACKET);
+    qpci_io_writeb(dev, ide_base + reg_device, 0);
+    qpci_io_writeb(dev, ide_base + reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF);
+    qpci_io_writeb(dev, ide_base + reg_lba_high,
+                   (BYTE_COUNT_LIMIT >> 8 & 0xFF));
+    qpci_io_writeb(dev, ide_base + reg_command, CMD_PACKET);
     /* HP0: Check_Status_A State */
     nsleep(400);
     data = ide_wait_clear(BSY);
@@ -756,7 +812,7 @@  static void cdrom_pio_impl(int nblocks)
     assert_bit_clear(data, ERR | DF | BSY);
 
     /* SCSI CDB (READ10) -- read n*2048 bytes from block 0 */
-    send_scsi_cdb_read10(0, nblocks);
+    send_scsi_cdb_read10(dev, ide_base, 0, nblocks);
 
     /* Read data back: occurs in bursts of 'BYTE_COUNT_LIMIT' bytes.
      * If BYTE_COUNT_LIMIT is odd, we transfer BYTE_COUNT_LIMIT - 1 bytes.
@@ -780,7 +836,8 @@  static void cdrom_pio_impl(int nblocks)
 
         /* HP4: Transfer_Data */
         for (j = 0; j < MIN((limit / 2), rem); j++) {
-            rx[offset + j] = le16_to_cpu(inw(IDE_BASE + reg_data));
+            rx[offset + j] = cpu_to_le16(qpci_io_readw(dev,
+                                                       ide_base + reg_data));
         }
     }