diff options
Diffstat (limited to 'tests/libqos')
-rw-r--r-- | tests/libqos/ahci.c | 130 | ||||
-rw-r--r-- | tests/libqos/ahci.h | 49 | ||||
-rw-r--r-- | tests/libqos/libqos-pc.c | 10 | ||||
-rw-r--r-- | tests/libqos/libqos-spapr.c | 34 | ||||
-rw-r--r-- | tests/libqos/libqos-spapr.h | 10 | ||||
-rw-r--r-- | tests/libqos/libqos.c | 35 | ||||
-rw-r--r-- | tests/libqos/libqos.h | 11 | ||||
-rw-r--r-- | tests/libqos/malloc-spapr.c | 38 | ||||
-rw-r--r-- | tests/libqos/malloc-spapr.h | 17 | ||||
-rw-r--r-- | tests/libqos/pci-pc.c | 211 | ||||
-rw-r--r-- | tests/libqos/pci-pc.h | 3 | ||||
-rw-r--r-- | tests/libqos/pci-spapr.c | 212 | ||||
-rw-r--r-- | tests/libqos/pci-spapr.h | 17 | ||||
-rw-r--r-- | tests/libqos/pci.c | 214 | ||||
-rw-r--r-- | tests/libqos/pci.h | 66 | ||||
-rw-r--r-- | tests/libqos/rtas.c | 116 | ||||
-rw-r--r-- | tests/libqos/rtas.h | 15 | ||||
-rw-r--r-- | tests/libqos/usb.c | 6 | ||||
-rw-r--r-- | tests/libqos/usb.h | 2 | ||||
-rw-r--r-- | tests/libqos/virtio-mmio.c | 17 | ||||
-rw-r--r-- | tests/libqos/virtio-pci.c | 146 | ||||
-rw-r--r-- | tests/libqos/virtio-pci.h | 2 | ||||
-rw-r--r-- | tests/libqos/virtio.c | 86 | ||||
-rw-r--r-- | tests/libqos/virtio.h | 57 |
24 files changed, 1094 insertions, 410 deletions
diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c index f3be5500e1..1ca7f456b5 100644 --- a/tests/libqos/ahci.c +++ b/tests/libqos/ahci.c @@ -86,6 +86,7 @@ struct AHCICommand { uint8_t name; uint8_t port; uint8_t slot; + uint8_t errors; uint32_t interrupts; uint64_t xbytes; uint32_t prd_size; @@ -128,7 +129,7 @@ QPCIDevice *get_ahci_device(uint32_t *fingerprint) uint32_t ahci_fingerprint; QPCIBus *pcibus; - pcibus = qpci_init_pc(); + pcibus = qpci_init_pc(NULL); /* Find the AHCI PCI device and verify it's the right one. */ ahci = qpci_device_find(pcibus, QPCI_DEVFN(0x1F, 0x02)); @@ -210,8 +211,7 @@ void ahci_pci_enable(AHCIQState *ahci) void start_ahci_device(AHCIQState *ahci) { /* Map AHCI's ABAR (BAR5) */ - ahci->hba_base = qpci_iomap(ahci->dev, 5, &ahci->barsize); - g_assert(ahci->hba_base); + ahci->hba_bar = qpci_iomap(ahci->dev, 5, &ahci->barsize); /* turns on pci.cmd.iose, pci.cmd.mse and pci.cmd.bme */ qpci_device_enable(ahci->dev); @@ -351,6 +351,7 @@ void ahci_hba_enable(AHCIQState *ahci) reg = ahci_rreg(ahci, AHCI_GHC); ASSERT_BIT_SET(reg, AHCI_GHC_IE); + ahci->enabled = true; /* TODO: The device should now be idling and waiting for commands. * In the future, a small test-case to inspect the Register D2H FIS * and clear the initial interrupts might be good. */ @@ -402,12 +403,14 @@ void ahci_port_clear(AHCIQState *ahci, uint8_t port) /** * Check a port for errors. */ -void ahci_port_check_error(AHCIQState *ahci, uint8_t port) +void ahci_port_check_error(AHCIQState *ahci, uint8_t port, + uint32_t imask, uint8_t emask) { uint32_t reg; /* The upper 9 bits of the IS register all indicate errors. */ reg = ahci_px_rreg(ahci, port, AHCI_PX_IS); + reg &= ~imask; reg >>= 23; g_assert_cmphex(reg, ==, 0); @@ -417,8 +420,13 @@ void ahci_port_check_error(AHCIQState *ahci, uint8_t port) /* The TFD also has two error sections. */ reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD); - ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_ERR); - ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR); + if (!emask) { + ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_ERR); + } else { + ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_ERR); + } + ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR & (~emask << 8)); + ASSERT_BIT_SET(reg, AHCI_PX_TFD_ERR & (emask << 8)); } void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port, @@ -633,7 +641,8 @@ void ahci_exec(AHCIQState *ahci, uint8_t port, /* Command creation */ if (opts->atapi) { - cmd = ahci_atapi_command_create(op); + uint16_t bcl = opts->set_bcl ? opts->bcl : ATAPI_SECTOR_SIZE; + cmd = ahci_atapi_command_create(op, bcl); if (opts->atapi_dma) { ahci_command_enable_atapi_dma(cmd); } @@ -864,19 +873,82 @@ AHCICommand *ahci_command_create(uint8_t command_name) return cmd; } -AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd) +AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl) { AHCICommand *cmd = ahci_command_create(CMD_PACKET); cmd->atapi_cmd = g_malloc0(16); cmd->atapi_cmd[0] = scsi_cmd; - /* ATAPI needs a PIO transfer chunk size set inside of the LBA registers. - * The block/sector size is a natural default. */ - cmd->fis.lba_lo[1] = ATAPI_SECTOR_SIZE >> 8 & 0xFF; - cmd->fis.lba_lo[2] = ATAPI_SECTOR_SIZE & 0xFF; - + stw_le_p(&cmd->fis.lba_lo[1], bcl); return cmd; } +void ahci_atapi_test_ready(AHCIQState *ahci, uint8_t port, + bool ready, uint8_t expected_sense) +{ + AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_TEST_UNIT_READY, 0); + ahci_command_set_size(cmd, 0); + if (!ready) { + cmd->interrupts |= AHCI_PX_IS_TFES; + cmd->errors |= expected_sense << 4; + } + ahci_command_commit(ahci, cmd, port); + ahci_command_issue(ahci, cmd); + ahci_command_verify(ahci, cmd); + ahci_command_free(cmd); +} + +static int copy_buffer(AHCIQState *ahci, AHCICommand *cmd, + const AHCIOpts *opts) +{ + unsigned char *rx = opts->opaque; + bufread(opts->buffer, rx, opts->size); + return 0; +} + +void ahci_atapi_get_sense(AHCIQState *ahci, uint8_t port, + uint8_t *sense, uint8_t *asc) +{ + unsigned char *rx; + AHCIOpts opts = { + .size = 18, + .atapi = true, + .post_cb = copy_buffer, + }; + rx = g_malloc(18); + opts.opaque = rx; + + ahci_exec(ahci, port, CMD_ATAPI_REQUEST_SENSE, &opts); + + *sense = rx[2]; + *asc = rx[12]; + + g_free(rx); +} + +void ahci_atapi_eject(AHCIQState *ahci, uint8_t port) +{ + AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0); + ahci_command_set_size(cmd, 0); + + cmd->atapi_cmd[4] = 0x02; /* loej = true */ + ahci_command_commit(ahci, cmd, port); + ahci_command_issue(ahci, cmd); + ahci_command_verify(ahci, cmd); + ahci_command_free(cmd); +} + +void ahci_atapi_load(AHCIQState *ahci, uint8_t port) +{ + AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0); + ahci_command_set_size(cmd, 0); + + cmd->atapi_cmd[4] = 0x03; /* loej,start = true */ + ahci_command_commit(ahci, cmd, port); + ahci_command_issue(ahci, cmd); + ahci_command_verify(ahci, cmd); + ahci_command_free(cmd); +} + void ahci_command_free(AHCICommand *cmd) { g_free(cmd->atapi_cmd); @@ -901,12 +973,22 @@ static void ahci_atapi_command_set_offset(AHCICommand *cmd, uint64_t lba) switch (cbd[0]) { case CMD_ATAPI_READ_10: + case CMD_ATAPI_READ_CD: g_assert_cmpuint(lba, <=, UINT32_MAX); stl_be_p(&cbd[2], lba); break; + case CMD_ATAPI_REQUEST_SENSE: + case CMD_ATAPI_TEST_UNIT_READY: + case CMD_ATAPI_START_STOP_UNIT: + g_assert_cmpuint(lba, ==, 0x00); + break; default: /* SCSI doesn't have uniform packet formats, * so you have to add support for it manually. Sorry! */ + fprintf(stderr, "The Libqos AHCI driver does not support the " + "set_offset operation for ATAPI command 0x%02x, " + "please add support.\n", + cbd[0]); g_assert_not_reached(); } } @@ -951,6 +1033,7 @@ static void ahci_atapi_set_size(AHCICommand *cmd, uint64_t xbytes) { unsigned char *cbd = cmd->atapi_cmd; uint64_t nsectors = xbytes / 2048; + uint32_t tmp; g_assert(cbd); switch (cbd[0]) { @@ -958,9 +1041,28 @@ static void ahci_atapi_set_size(AHCICommand *cmd, uint64_t xbytes) g_assert_cmpuint(nsectors, <=, UINT16_MAX); stw_be_p(&cbd[7], nsectors); break; + case CMD_ATAPI_READ_CD: + /* 24bit BE store */ + g_assert_cmpuint(nsectors, <, 1ULL << 24); + tmp = nsectors; + cbd[6] = (tmp & 0xFF0000) >> 16; + cbd[7] = (tmp & 0xFF00) >> 8; + cbd[8] = (tmp & 0xFF); + break; + case CMD_ATAPI_REQUEST_SENSE: + g_assert_cmpuint(xbytes, <=, UINT8_MAX); + cbd[4] = (uint8_t)xbytes; + break; + case CMD_ATAPI_TEST_UNIT_READY: + case CMD_ATAPI_START_STOP_UNIT: + g_assert_cmpuint(xbytes, ==, 0); + break; default: /* SCSI doesn't have uniform packet formats, * so you have to add support for it manually. Sorry! */ + fprintf(stderr, "The Libqos AHCI driver does not support the set_size " + "operation for ATAPI command 0x%02x, please add support.\n", + cbd[0]); g_assert_not_reached(); } } @@ -1105,7 +1207,7 @@ void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd) uint8_t slot = cmd->slot; uint8_t port = cmd->port; - ahci_port_check_error(ahci, port); + ahci_port_check_error(ahci, port, cmd->interrupts, cmd->errors); ahci_port_check_interrupts(ahci, port, cmd->interrupts); ahci_port_check_nonbusy(ahci, port, slot); ahci_port_check_cmd_sanity(ahci, cmd); diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h index c69fb5ae90..5f9627bb0f 100644 --- a/tests/libqos/ahci.h +++ b/tests/libqos/ahci.h @@ -287,7 +287,22 @@ enum { /* ATAPI Commands */ enum { - CMD_ATAPI_READ_10 = 0x28, + CMD_ATAPI_TEST_UNIT_READY = 0x00, + CMD_ATAPI_REQUEST_SENSE = 0x03, + CMD_ATAPI_START_STOP_UNIT = 0x1b, + CMD_ATAPI_READ_10 = 0x28, + CMD_ATAPI_READ_CD = 0xbe, +}; + +enum { + SENSE_NO_SENSE = 0x00, + SENSE_NOT_READY = 0x02, + SENSE_UNIT_ATTENTION = 0x06, +}; + +enum { + ASC_MEDIUM_MAY_HAVE_CHANGED = 0x28, + ASC_MEDIUM_NOT_PRESENT = 0x3a, }; /* AHCI Command Header Flags & Masks*/ @@ -321,12 +336,13 @@ typedef struct AHCIPortQState { typedef struct AHCIQState { QOSState *parent; QPCIDevice *dev; - void *hba_base; + QPCIBar hba_bar; uint64_t barsize; uint32_t fingerprint; uint32_t cap; uint32_t cap2; AHCIPortQState port[32]; + bool enabled; } AHCIQState; /** @@ -461,12 +477,14 @@ typedef struct AHCICommand AHCICommand; /* Options to ahci_exec */ typedef struct AHCIOpts { - size_t size; - unsigned prd_size; - uint64_t lba; - uint64_t buffer; - bool atapi; - bool atapi_dma; + size_t size; /* Size of transfer */ + unsigned prd_size; /* Size per-each PRD */ + bool set_bcl; /* Override the default BCL of ATAPI_SECTOR_SIZE */ + unsigned bcl; /* Byte Count Limit, for ATAPI PIO */ + uint64_t lba; /* Starting LBA offset */ + uint64_t buffer; /* Pointer to source or destination guest buffer */ + bool atapi; /* ATAPI command? */ + bool atapi_dma; /* Use DMA for ATAPI? */ bool error; int (*pre_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *); int (*mid_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *); @@ -488,12 +506,12 @@ typedef struct AHCIOpts { static inline uint32_t ahci_mread(AHCIQState *ahci, size_t offset) { - return qpci_io_readl(ahci->dev, ahci->hba_base + offset); + return qpci_io_readl(ahci->dev, ahci->hba_bar, offset); } static inline void ahci_mwrite(AHCIQState *ahci, size_t offset, uint32_t value) { - qpci_io_writel(ahci->dev, ahci->hba_base + offset, value); + qpci_io_writel(ahci->dev, ahci->hba_bar, offset, value); } static inline uint32_t ahci_rreg(AHCIQState *ahci, uint32_t reg_num) @@ -572,7 +590,8 @@ void ahci_set_command_header(AHCIQState *ahci, uint8_t port, void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot); /* AHCI sanity check routines */ -void ahci_port_check_error(AHCIQState *ahci, uint8_t port); +void ahci_port_check_error(AHCIQState *ahci, uint8_t port, + uint32_t imask, uint8_t emask); void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port, uint32_t intr_mask); void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot); @@ -595,10 +614,16 @@ void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, void *buffer, size_t bufsize, uint64_t sector); void ahci_exec(AHCIQState *ahci, uint8_t port, uint8_t op, const AHCIOpts *opts); +void ahci_atapi_test_ready(AHCIQState *ahci, uint8_t port, bool ready, + uint8_t expected_sense); +void ahci_atapi_get_sense(AHCIQState *ahci, uint8_t port, + uint8_t *sense, uint8_t *asc); +void ahci_atapi_eject(AHCIQState *ahci, uint8_t port); +void ahci_atapi_load(AHCIQState *ahci, uint8_t port); /* Command: Fine-grained lifecycle */ AHCICommand *ahci_command_create(uint8_t command_name); -AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd); +AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl); void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port); void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd); void ahci_command_issue_async(AHCIQState *ahci, AHCICommand *cmd); diff --git a/tests/libqos/libqos-pc.c b/tests/libqos/libqos-pc.c index 72b5e3ba09..b554758802 100644 --- a/tests/libqos/libqos-pc.c +++ b/tests/libqos/libqos-pc.c @@ -1,10 +1,14 @@ #include "qemu/osdep.h" #include "libqos/libqos-pc.h" #include "libqos/malloc-pc.h" +#include "libqos/pci-pc.h" static QOSOps qos_ops = { .init_allocator = pc_alloc_init_flags, - .uninit_allocator = pc_alloc_uninit + .uninit_allocator = pc_alloc_uninit, + .qpci_init = qpci_init_pc, + .qpci_free = qpci_free_pc, + .shutdown = qtest_pc_shutdown, }; QOSState *qtest_pc_vboot(const char *cmdline_fmt, va_list ap) @@ -21,10 +25,12 @@ QOSState *qtest_pc_boot(const char *cmdline_fmt, ...) qs = qtest_vboot(&qos_ops, cmdline_fmt, ap); va_end(ap); + qtest_irq_intercept_in(global_qtest, "ioapic"); + return qs; } void qtest_pc_shutdown(QOSState *qs) { - return qtest_shutdown(qs); + return qtest_common_shutdown(qs); } diff --git a/tests/libqos/libqos-spapr.c b/tests/libqos/libqos-spapr.c new file mode 100644 index 0000000000..a37791e5d0 --- /dev/null +++ b/tests/libqos/libqos-spapr.c @@ -0,0 +1,34 @@ +#include "qemu/osdep.h" +#include "libqos/libqos-spapr.h" +#include "libqos/malloc-spapr.h" +#include "libqos/pci-spapr.h" + +static QOSOps qos_ops = { + .init_allocator = spapr_alloc_init_flags, + .uninit_allocator = spapr_alloc_uninit, + .qpci_init = qpci_init_spapr, + .qpci_free = qpci_free_spapr, + .shutdown = qtest_spapr_shutdown, +}; + +QOSState *qtest_spapr_vboot(const char *cmdline_fmt, va_list ap) +{ + return qtest_vboot(&qos_ops, cmdline_fmt, ap); +} + +QOSState *qtest_spapr_boot(const char *cmdline_fmt, ...) +{ + QOSState *qs; + va_list ap; + + va_start(ap, cmdline_fmt); + qs = qtest_vboot(&qos_ops, cmdline_fmt, ap); + va_end(ap); + + return qs; +} + +void qtest_spapr_shutdown(QOSState *qs) +{ + return qtest_common_shutdown(qs); +} diff --git a/tests/libqos/libqos-spapr.h b/tests/libqos/libqos-spapr.h new file mode 100644 index 0000000000..dcb5c43ad3 --- /dev/null +++ b/tests/libqos/libqos-spapr.h @@ -0,0 +1,10 @@ +#ifndef LIBQOS_SPAPR_H +#define LIBQOS_SPAPR_H + +#include "libqos/libqos.h" + +QOSState *qtest_spapr_vboot(const char *cmdline_fmt, va_list ap); +QOSState *qtest_spapr_boot(const char *cmdline_fmt, ...); +void qtest_spapr_shutdown(QOSState *qs); + +#endif diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c index c7ba441d0b..6226546c28 100644 --- a/tests/libqos/libqos.c +++ b/tests/libqos/libqos.c @@ -10,6 +10,8 @@ /** * Launch QEMU with the given command line, * and then set up interrupts and our guest malloc interface. + * Never returns NULL: + * Terminates the application in case an error is encountered. */ QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap) { @@ -20,9 +22,13 @@ QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap) cmdline = g_strdup_vprintf(cmdline_fmt, ap); qs->qts = qtest_start(cmdline); qs->ops = ops; - qtest_irq_intercept_in(global_qtest, "ioapic"); - if (ops && ops->init_allocator) { - qs->alloc = ops->init_allocator(ALLOC_NO_FLAGS); + if (ops) { + if (ops->init_allocator) { + qs->alloc = ops->init_allocator(ALLOC_NO_FLAGS); + } + if (ops->qpci_init && qs->alloc) { + qs->pcibus = ops->qpci_init(qs->alloc); + } } g_free(cmdline); @@ -48,16 +54,31 @@ QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...) /** * Tear down the QEMU instance. */ -void qtest_shutdown(QOSState *qs) +void qtest_common_shutdown(QOSState *qs) { - if (qs->alloc && qs->ops && qs->ops->uninit_allocator) { - qs->ops->uninit_allocator(qs->alloc); - qs->alloc = NULL; + if (qs->ops) { + if (qs->pcibus && qs->ops->qpci_free) { + qs->ops->qpci_free(qs->pcibus); + qs->pcibus = NULL; + } + if (qs->alloc && qs->ops->uninit_allocator) { + qs->ops->uninit_allocator(qs->alloc); + qs->alloc = NULL; + } } qtest_quit(qs->qts); g_free(qs); } +void qtest_shutdown(QOSState *qs) +{ + if (qs->ops && qs->ops->shutdown) { + qs->ops->shutdown(qs); + } else { + qtest_common_shutdown(qs); + } +} + void set_context(QOSState *s) { global_qtest = s->qts; diff --git a/tests/libqos/libqos.h b/tests/libqos/libqos.h index 604980d125..231969766f 100644 --- a/tests/libqos/libqos.h +++ b/tests/libqos/libqos.h @@ -5,19 +5,26 @@ #include "libqos/pci.h" #include "libqos/malloc-pc.h" +typedef struct QOSState QOSState; + typedef struct QOSOps { QGuestAllocator *(*init_allocator)(QAllocOpts); void (*uninit_allocator)(QGuestAllocator *); + QPCIBus *(*qpci_init)(QGuestAllocator *alloc); + void (*qpci_free)(QPCIBus *bus); + void (*shutdown)(QOSState *); } QOSOps; -typedef struct QOSState { +struct QOSState { QTestState *qts; QGuestAllocator *alloc; + QPCIBus *pcibus; QOSOps *ops; -} QOSState; +}; QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap); QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...); +void qtest_common_shutdown(QOSState *qs); void qtest_shutdown(QOSState *qs); bool have_qemu_img(void); void mkimg(const char *file, const char *fmt, unsigned size_mb); diff --git a/tests/libqos/malloc-spapr.c b/tests/libqos/malloc-spapr.c new file mode 100644 index 0000000000..006404af33 --- /dev/null +++ b/tests/libqos/malloc-spapr.c @@ -0,0 +1,38 @@ +/* + * libqos malloc support for SPAPR + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqos/malloc-spapr.h" + +#include "qemu-common.h" + +#define PAGE_SIZE 4096 + +/* Memory must be a multiple of 256 MB, + * so we have at least 256MB + */ +#define SPAPR_MIN_SIZE 0x10000000 + +void spapr_alloc_uninit(QGuestAllocator *allocator) +{ + alloc_uninit(allocator); +} + +QGuestAllocator *spapr_alloc_init_flags(QAllocOpts flags) +{ + QGuestAllocator *s; + + s = alloc_init_flags(flags, 1 << 20, SPAPR_MIN_SIZE); + alloc_set_page_size(s, PAGE_SIZE); + + return s; +} + +QGuestAllocator *spapr_alloc_init(void) +{ + return spapr_alloc_init_flags(ALLOC_NO_FLAGS); +} diff --git a/tests/libqos/malloc-spapr.h b/tests/libqos/malloc-spapr.h new file mode 100644 index 0000000000..64d0e770d1 --- /dev/null +++ b/tests/libqos/malloc-spapr.h @@ -0,0 +1,17 @@ +/* + * libqos malloc support for SPAPR + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef LIBQOS_MALLOC_SPAPR_H +#define LIBQOS_MALLOC_SPAPR_H + +#include "libqos/malloc.h" + +QGuestAllocator *spapr_alloc_init(void); +QGuestAllocator *spapr_alloc_init_flags(QAllocOpts flags); +void spapr_alloc_uninit(QGuestAllocator *allocator); + +#endif diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c index 1ae2d3780f..ded1c54c06 100644 --- a/tests/libqos/pci-pc.c +++ b/tests/libqos/pci-pc.c @@ -17,7 +17,6 @@ #include "hw/pci/pci_regs.h" #include "qemu-common.h" -#include "qemu/host-utils.h" #define ACPI_PCIHP_ADDR 0xae00 @@ -26,89 +25,58 @@ typedef struct QPCIBusPC { QPCIBus bus; - - uint32_t pci_hole_start; - uint32_t pci_hole_size; - uint32_t pci_hole_alloc; - - uint16_t pci_iohole_start; - uint16_t pci_iohole_size; - uint16_t pci_iohole_alloc; } QPCIBusPC; -static uint8_t qpci_pc_io_readb(QPCIBus *bus, void *addr) +static uint8_t qpci_pc_pio_readb(QPCIBus *bus, uint32_t addr) { - uintptr_t port = (uintptr_t)addr; - uint8_t value; - - if (port < 0x10000) { - value = inb(port); - } else { - value = readb(port); - } - - return value; + return inb(addr); } -static uint16_t qpci_pc_io_readw(QPCIBus *bus, void *addr) +static void qpci_pc_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val) { - uintptr_t port = (uintptr_t)addr; - uint16_t value; - - if (port < 0x10000) { - value = inw(port); - } else { - value = readw(port); - } - - return value; + outb(addr, val); } -static uint32_t qpci_pc_io_readl(QPCIBus *bus, void *addr) +static uint16_t qpci_pc_pio_readw(QPCIBus *bus, uint32_t addr) { - uintptr_t port = (uintptr_t)addr; - uint32_t value; - - if (port < 0x10000) { - value = inl(port); - } else { - value = readl(port); - } + return inw(addr); +} - return value; +static void qpci_pc_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val) +{ + outw(addr, val); } -static void qpci_pc_io_writeb(QPCIBus *bus, void *addr, uint8_t value) +static uint32_t qpci_pc_pio_readl(QPCIBus *bus, uint32_t addr) { - uintptr_t port = (uintptr_t)addr; + return inl(addr); +} - if (port < 0x10000) { - outb(port, value); - } else { - writeb(port, value); - } +static void qpci_pc_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val) +{ + outl(addr, val); } -static void qpci_pc_io_writew(QPCIBus *bus, void *addr, uint16_t value) +static uint64_t qpci_pc_pio_readq(QPCIBus *bus, uint32_t addr) { - uintptr_t port = (uintptr_t)addr; + return (uint64_t)inl(addr) + ((uint64_t)inl(addr + 4) << 32); +} - if (port < 0x10000) { - outw(port, value); - } else { - writew(port, value); - } +static void qpci_pc_pio_writeq(QPCIBus *bus, uint32_t addr, uint64_t val) +{ + outl(addr, val & 0xffffffff); + outl(addr + 4, val >> 32); } -static void qpci_pc_io_writel(QPCIBus *bus, void *addr, uint32_t value) +static void qpci_pc_memread(QPCIBus *bus, uint32_t addr, void *buf, size_t len) { - uintptr_t port = (uintptr_t)addr; + memread(addr, buf, len); +} - if (port < 0x10000) { - outl(port, value); - } else { - writel(port, value); - } +static void qpci_pc_memwrite(QPCIBus *bus, uint32_t addr, + const void *buf, size_t len) +{ + memwrite(addr, buf, len); } static uint8_t qpci_pc_config_readb(QPCIBus *bus, int devfn, uint8_t offset) @@ -147,84 +115,24 @@ static void qpci_pc_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint3 outl(0xcfc, value); } -static void *qpci_pc_iomap(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *sizeptr) -{ - QPCIBusPC *s = container_of(bus, QPCIBusPC, bus); - static const int bar_reg_map[] = { - PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2, - PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5, - }; - int bar_reg; - uint32_t addr; - uint64_t size; - uint32_t io_type; - - g_assert(barno >= 0 && barno <= 5); - bar_reg = bar_reg_map[barno]; - - qpci_config_writel(dev, bar_reg, 0xFFFFFFFF); - addr = qpci_config_readl(dev, bar_reg); - - io_type = addr & PCI_BASE_ADDRESS_SPACE; - if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { - addr &= PCI_BASE_ADDRESS_IO_MASK; - } else { - addr &= PCI_BASE_ADDRESS_MEM_MASK; - } - - size = (1ULL << ctzl(addr)); - if (size == 0) { - return NULL; - } - if (sizeptr) { - *sizeptr = size; - } - - if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { - uint16_t loc; - - g_assert(QEMU_ALIGN_UP(s->pci_iohole_alloc, size) + size - <= s->pci_iohole_size); - s->pci_iohole_alloc = QEMU_ALIGN_UP(s->pci_iohole_alloc, size); - loc = s->pci_iohole_start + s->pci_iohole_alloc; - s->pci_iohole_alloc += size; - - qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO); - - return (void *)(intptr_t)loc; - } else { - uint64_t loc; - - g_assert(QEMU_ALIGN_UP(s->pci_hole_alloc, size) + size - <= s->pci_hole_size); - s->pci_hole_alloc = QEMU_ALIGN_UP(s->pci_hole_alloc, size); - loc = s->pci_hole_start + s->pci_hole_alloc; - s->pci_hole_alloc += size; - - qpci_config_writel(dev, bar_reg, loc); - - return (void *)(intptr_t)loc; - } -} - -static void qpci_pc_iounmap(QPCIBus *bus, void *data) -{ - /* FIXME */ -} - -QPCIBus *qpci_init_pc(void) +QPCIBus *qpci_init_pc(QGuestAllocator *alloc) { QPCIBusPC *ret; ret = g_malloc(sizeof(*ret)); - ret->bus.io_readb = qpci_pc_io_readb; - ret->bus.io_readw = qpci_pc_io_readw; - ret->bus.io_readl = qpci_pc_io_readl; + ret->bus.pio_readb = qpci_pc_pio_readb; + ret->bus.pio_readw = qpci_pc_pio_readw; + ret->bus.pio_readl = qpci_pc_pio_readl; + ret->bus.pio_readq = qpci_pc_pio_readq; - ret->bus.io_writeb = qpci_pc_io_writeb; - ret->bus.io_writew = qpci_pc_io_writew; - ret->bus.io_writel = qpci_pc_io_writel; + ret->bus.pio_writeb = qpci_pc_pio_writeb; + ret->bus.pio_writew = qpci_pc_pio_writew; + ret->bus.pio_writel = qpci_pc_pio_writel; + ret->bus.pio_writeq = qpci_pc_pio_writeq; + + ret->bus.memread = qpci_pc_memread; + ret->bus.memwrite = qpci_pc_memwrite; ret->bus.config_readb = qpci_pc_config_readb; ret->bus.config_readw = qpci_pc_config_readw; @@ -234,16 +142,9 @@ QPCIBus *qpci_init_pc(void) ret->bus.config_writew = qpci_pc_config_writew; ret->bus.config_writel = qpci_pc_config_writel; - ret->bus.iomap = qpci_pc_iomap; - ret->bus.iounmap = qpci_pc_iounmap; - - ret->pci_hole_start = 0xE0000000; - ret->pci_hole_size = 0x20000000; - ret->pci_hole_alloc = 0; - - ret->pci_iohole_start = 0xc000; - ret->pci_iohole_size = 0x4000; - ret->pci_iohole_alloc = 0; + ret->bus.pio_alloc_ptr = 0xc000; + ret->bus.mmio_alloc_ptr = 0xE0000000; + ret->bus.mmio_limit = 0x100000000ULL; return &ret->bus; } @@ -255,28 +156,6 @@ void qpci_free_pc(QPCIBus *bus) g_free(s); } -void qpci_plug_device_test(const char *driver, const char *id, - uint8_t slot, const char *opts) -{ - QDict *response; - char *cmd; - - cmd = g_strdup_printf("{'execute': 'device_add'," - " 'arguments': {" - " 'driver': '%s'," - " 'addr': '%d'," - " %s%s" - " 'id': '%s'" - "}}", driver, slot, - opts ? opts : "", opts ? "," : "", - id); - response = qmp(cmd); - g_free(cmd); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - QDECREF(response); -} - void qpci_unplug_acpi_device_test(const char *id, uint8_t slot) { QDict *response; diff --git a/tests/libqos/pci-pc.h b/tests/libqos/pci-pc.h index 26211790cd..9479b51642 100644 --- a/tests/libqos/pci-pc.h +++ b/tests/libqos/pci-pc.h @@ -14,8 +14,9 @@ #define LIBQOS_PCI_PC_H #include "libqos/pci.h" +#include "libqos/malloc.h" -QPCIBus *qpci_init_pc(void); +QPCIBus *qpci_init_pc(QGuestAllocator *alloc); void qpci_free_pc(QPCIBus *bus); #endif diff --git a/tests/libqos/pci-spapr.c b/tests/libqos/pci-spapr.c new file mode 100644 index 0000000000..1e5d015bd4 --- /dev/null +++ b/tests/libqos/pci-spapr.c @@ -0,0 +1,212 @@ +/* + * libqos PCI bindings for SPAPR + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/pci-spapr.h" +#include "libqos/rtas.h" + +#include "hw/pci/pci_regs.h" + +#include "qemu-common.h" +#include "qemu/host-utils.h" + + +/* From include/hw/pci-host/spapr.h */ + +typedef struct QPCIWindow { + uint64_t pci_base; /* window address in PCI space */ + uint64_t size; /* window size */ +} QPCIWindow; + +typedef struct QPCIBusSPAPR { + QPCIBus bus; + QGuestAllocator *alloc; + + uint64_t buid; + + uint64_t pio_cpu_base; + QPCIWindow pio; + + uint64_t mmio32_cpu_base; + QPCIWindow mmio32; +} QPCIBusSPAPR; + +/* + * PCI devices are always little-endian + * SPAPR by default is big-endian + * so PCI accessors need to swap data endianness + */ + +static uint8_t qpci_spapr_pio_readb(QPCIBus *bus, uint32_t addr) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + return readb(s->pio_cpu_base + addr); +} + +static void qpci_spapr_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + writeb(s->pio_cpu_base + addr, val); +} + +static uint16_t qpci_spapr_pio_readw(QPCIBus *bus, uint32_t addr) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + return bswap16(readw(s->pio_cpu_base + addr)); +} + +static void qpci_spapr_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + writew(s->pio_cpu_base + addr, bswap16(val)); +} + +static uint32_t qpci_spapr_pio_readl(QPCIBus *bus, uint32_t addr) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + return bswap32(readl(s->pio_cpu_base + addr)); +} + +static void qpci_spapr_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + writel(s->pio_cpu_base + addr, bswap32(val)); +} + +static uint64_t qpci_spapr_pio_readq(QPCIBus *bus, uint32_t addr) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + return bswap64(readq(s->pio_cpu_base + addr)); +} + +static void qpci_spapr_pio_writeq(QPCIBus *bus, uint32_t addr, uint64_t val) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + writeq(s->pio_cpu_base + addr, bswap64(val)); +} + +static void qpci_spapr_memread(QPCIBus *bus, uint32_t addr, + void *buf, size_t len) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + memread(s->mmio32_cpu_base + addr, buf, len); +} + +static void qpci_spapr_memwrite(QPCIBus *bus, uint32_t addr, + const void *buf, size_t len) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + memwrite(s->mmio32_cpu_base + addr, buf, len); +} + +static uint8_t qpci_spapr_config_readb(QPCIBus *bus, int devfn, uint8_t offset) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + uint32_t config_addr = (devfn << 8) | offset; + return qrtas_ibm_read_pci_config(s->alloc, s->buid, config_addr, 1); +} + +static uint16_t qpci_spapr_config_readw(QPCIBus *bus, int devfn, uint8_t offset) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + uint32_t config_addr = (devfn << 8) | offset; + return qrtas_ibm_read_pci_config(s->alloc, s->buid, config_addr, 2); +} + +static uint32_t qpci_spapr_config_readl(QPCIBus *bus, int devfn, uint8_t offset) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + uint32_t config_addr = (devfn << 8) | offset; + return qrtas_ibm_read_pci_config(s->alloc, s->buid, config_addr, 4); +} + +static void qpci_spapr_config_writeb(QPCIBus *bus, int devfn, uint8_t offset, + uint8_t value) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + uint32_t config_addr = (devfn << 8) | offset; + qrtas_ibm_write_pci_config(s->alloc, s->buid, config_addr, 1, value); +} + +static void qpci_spapr_config_writew(QPCIBus *bus, int devfn, uint8_t offset, + uint16_t value) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + uint32_t config_addr = (devfn << 8) | offset; + qrtas_ibm_write_pci_config(s->alloc, s->buid, config_addr, 2, value); +} + +static void qpci_spapr_config_writel(QPCIBus *bus, int devfn, uint8_t offset, + uint32_t value) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + uint32_t config_addr = (devfn << 8) | offset; + qrtas_ibm_write_pci_config(s->alloc, s->buid, config_addr, 4, value); +} + +#define SPAPR_PCI_BASE (1ULL << 45) + +#define SPAPR_PCI_MMIO32_WIN_SIZE 0x80000000 /* 2 GiB */ +#define SPAPR_PCI_IO_WIN_SIZE 0x10000 + +QPCIBus *qpci_init_spapr(QGuestAllocator *alloc) +{ + QPCIBusSPAPR *ret; + + ret = g_malloc(sizeof(*ret)); + + ret->alloc = alloc; + + ret->bus.pio_readb = qpci_spapr_pio_readb; + ret->bus.pio_readw = qpci_spapr_pio_readw; + ret->bus.pio_readl = qpci_spapr_pio_readl; + ret->bus.pio_readq = qpci_spapr_pio_readq; + + ret->bus.pio_writeb = qpci_spapr_pio_writeb; + ret->bus.pio_writew = qpci_spapr_pio_writew; + ret->bus.pio_writel = qpci_spapr_pio_writel; + ret->bus.pio_writeq = qpci_spapr_pio_writeq; + + ret->bus.memread = qpci_spapr_memread; + ret->bus.memwrite = qpci_spapr_memwrite; + + ret->bus.config_readb = qpci_spapr_config_readb; + ret->bus.config_readw = qpci_spapr_config_readw; + ret->bus.config_readl = qpci_spapr_config_readl; + + ret->bus.config_writeb = qpci_spapr_config_writeb; + ret->bus.config_writew = qpci_spapr_config_writew; + ret->bus.config_writel = qpci_spapr_config_writel; + + /* FIXME: We assume the default location of the PHB for now. + * Ideally we'd parse the device tree deposited in the guest to + * get the window locations */ + ret->buid = 0x800000020000000ULL; + + ret->pio_cpu_base = SPAPR_PCI_BASE; + ret->pio.pci_base = 0; + ret->pio.size = SPAPR_PCI_IO_WIN_SIZE; + + /* 32-bit portion of the MMIO window is at PCI address 2..4 GiB */ + ret->mmio32_cpu_base = SPAPR_PCI_BASE + SPAPR_PCI_MMIO32_WIN_SIZE; + ret->mmio32.pci_base = 0x80000000; /* 2 GiB */ + ret->mmio32.size = SPAPR_PCI_MMIO32_WIN_SIZE; + + ret->bus.pio_alloc_ptr = 0xc000; + ret->bus.mmio_alloc_ptr = ret->mmio32.pci_base; + ret->bus.mmio_limit = ret->mmio32.pci_base + ret->mmio32.size; + + return &ret->bus; +} + +void qpci_free_spapr(QPCIBus *bus) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + + g_free(s); +} diff --git a/tests/libqos/pci-spapr.h b/tests/libqos/pci-spapr.h new file mode 100644 index 0000000000..4192126d86 --- /dev/null +++ b/tests/libqos/pci-spapr.h @@ -0,0 +1,17 @@ +/* + * libqos PCI bindings for SPAPR + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef LIBQOS_PCI_SPAPR_H +#define LIBQOS_PCI_SPAPR_H + +#include "libqos/malloc.h" +#include "libqos/pci.h" + +QPCIBus *qpci_init_spapr(QGuestAllocator *alloc); +void qpci_free_spapr(QPCIBus *bus); + +#endif diff --git a/tests/libqos/pci.c b/tests/libqos/pci.c index ed78d91cea..2dcdeade2a 100644 --- a/tests/libqos/pci.c +++ b/tests/libqos/pci.c @@ -14,6 +14,7 @@ #include "libqos/pci.h" #include "hw/pci/pci_regs.h" +#include "qemu/host-utils.h" void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, void (*func)(QPCIDevice *dev, int devfn, void *data), @@ -103,7 +104,6 @@ void qpci_msix_enable(QPCIDevice *dev) uint32_t table; uint8_t bir_table; uint8_t bir_pba; - void *offset; addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); g_assert_cmphex(addr, !=, 0); @@ -113,18 +113,16 @@ void qpci_msix_enable(QPCIDevice *dev) table = qpci_config_readl(dev, addr + PCI_MSIX_TABLE); bir_table = table & PCI_MSIX_FLAGS_BIRMASK; - offset = qpci_iomap(dev, bir_table, NULL); - dev->msix_table = offset + (table & ~PCI_MSIX_FLAGS_BIRMASK); + dev->msix_table_bar = qpci_iomap(dev, bir_table, NULL); + dev->msix_table_off = table & ~PCI_MSIX_FLAGS_BIRMASK; table = qpci_config_readl(dev, addr + PCI_MSIX_PBA); bir_pba = table & PCI_MSIX_FLAGS_BIRMASK; if (bir_pba != bir_table) { - offset = qpci_iomap(dev, bir_pba, NULL); + dev->msix_pba_bar = qpci_iomap(dev, bir_pba, NULL); } - dev->msix_pba = offset + (table & ~PCI_MSIX_FLAGS_BIRMASK); + dev->msix_pba_off = table & ~PCI_MSIX_FLAGS_BIRMASK; - g_assert(dev->msix_table != NULL); - g_assert(dev->msix_pba != NULL); dev->msix_enabled = true; } @@ -140,22 +138,23 @@ void qpci_msix_disable(QPCIDevice *dev) qpci_config_writew(dev, addr + PCI_MSIX_FLAGS, val & ~PCI_MSIX_FLAGS_ENABLE); - qpci_iounmap(dev, dev->msix_table); - qpci_iounmap(dev, dev->msix_pba); + qpci_iounmap(dev, dev->msix_table_bar); + qpci_iounmap(dev, dev->msix_pba_bar); dev->msix_enabled = 0; - dev->msix_table = NULL; - dev->msix_pba = NULL; + dev->msix_table_off = 0; + dev->msix_pba_off = 0; } bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry) { uint32_t pba_entry; uint8_t bit_n = entry % 32; - void *addr = dev->msix_pba + (entry / 32) * PCI_MSIX_ENTRY_SIZE / 4; + uint64_t off = (entry / 32) * PCI_MSIX_ENTRY_SIZE / 4; g_assert(dev->msix_enabled); - pba_entry = qpci_io_readl(dev, addr); - qpci_io_writel(dev, addr, pba_entry & ~(1 << bit_n)); + pba_entry = qpci_io_readl(dev, dev->msix_pba_bar, dev->msix_pba_off + off); + qpci_io_writel(dev, dev->msix_pba_bar, dev->msix_pba_off + off, + pba_entry & ~(1 << bit_n)); return (pba_entry & (1 << bit_n)) != 0; } @@ -163,7 +162,7 @@ bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry) { uint8_t addr; uint16_t val; - void *vector_addr = dev->msix_table + (entry * PCI_MSIX_ENTRY_SIZE); + uint64_t vector_off = dev->msix_table_off + entry * PCI_MSIX_ENTRY_SIZE; g_assert(dev->msix_enabled); addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); @@ -173,8 +172,9 @@ bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry) if (val & PCI_MSIX_FLAGS_MASKALL) { return true; } else { - return (qpci_io_readl(dev, vector_addr + PCI_MSIX_ENTRY_VECTOR_CTRL) - & PCI_MSIX_ENTRY_CTRL_MASKBIT) != 0; + return (qpci_io_readl(dev, dev->msix_table_bar, + vector_off + PCI_MSIX_ENTRY_VECTOR_CTRL) + & PCI_MSIX_ENTRY_CTRL_MASKBIT) != 0; } } @@ -221,46 +221,194 @@ void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value) dev->bus->config_writel(dev->bus, dev->devfn, offset, value); } +uint8_t qpci_io_readb(QPCIDevice *dev, QPCIBar token, uint64_t off) +{ + if (token.addr < QPCI_PIO_LIMIT) { + return dev->bus->pio_readb(dev->bus, token.addr + off); + } else { + uint8_t val; + dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); + return val; + } +} -uint8_t qpci_io_readb(QPCIDevice *dev, void *data) +uint16_t qpci_io_readw(QPCIDevice *dev, QPCIBar token, uint64_t off) { - return dev->bus->io_readb(dev->bus, data); + if (token.addr < QPCI_PIO_LIMIT) { + return dev->bus->pio_readw(dev->bus, token.addr + off); + } else { + uint16_t val; + dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); + return le16_to_cpu(val); + } } -uint16_t qpci_io_readw(QPCIDevice *dev, void *data) +uint32_t qpci_io_readl(QPCIDevice *dev, QPCIBar token, uint64_t off) { - return dev->bus->io_readw(dev->bus, data); + if (token.addr < QPCI_PIO_LIMIT) { + return dev->bus->pio_readl(dev->bus, token.addr + off); + } else { + uint32_t val; + dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); + return le32_to_cpu(val); + } } -uint32_t qpci_io_readl(QPCIDevice *dev, void *data) +uint64_t qpci_io_readq(QPCIDevice *dev, QPCIBar token, uint64_t off) { - return dev->bus->io_readl(dev->bus, data); + if (token.addr < QPCI_PIO_LIMIT) { + return dev->bus->pio_readq(dev->bus, token.addr + off); + } else { + uint64_t val; + dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); + return le64_to_cpu(val); + } } +void qpci_io_writeb(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint8_t value) +{ + if (token.addr < QPCI_PIO_LIMIT) { + dev->bus->pio_writeb(dev->bus, token.addr + off, value); + } else { + dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); + } +} -void qpci_io_writeb(QPCIDevice *dev, void *data, uint8_t value) +void qpci_io_writew(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint16_t value) { - dev->bus->io_writeb(dev->bus, data, value); + if (token.addr < QPCI_PIO_LIMIT) { + dev->bus->pio_writew(dev->bus, token.addr + off, value); + } else { + value = cpu_to_le16(value); + dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); + } } -void qpci_io_writew(QPCIDevice *dev, void *data, uint16_t value) +void qpci_io_writel(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint32_t value) { - dev->bus->io_writew(dev->bus, data, value); + if (token.addr < QPCI_PIO_LIMIT) { + dev->bus->pio_writel(dev->bus, token.addr + off, value); + } else { + value = cpu_to_le32(value); + dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); + } } -void qpci_io_writel(QPCIDevice *dev, void *data, uint32_t value) +void qpci_io_writeq(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint64_t value) { - dev->bus->io_writel(dev->bus, data, value); + if (token.addr < QPCI_PIO_LIMIT) { + dev->bus->pio_writeq(dev->bus, token.addr + off, value); + } else { + value = cpu_to_le64(value); + dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); + } } -void *qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr) +void qpci_memread(QPCIDevice *dev, QPCIBar token, uint64_t off, + void *buf, size_t len) { - return dev->bus->iomap(dev->bus, dev, barno, sizeptr); + g_assert(token.addr >= QPCI_PIO_LIMIT); + dev->bus->memread(dev->bus, token.addr + off, buf, len); } -void qpci_iounmap(QPCIDevice *dev, void *data) +void qpci_memwrite(QPCIDevice *dev, QPCIBar token, uint64_t off, + const void *buf, size_t len) { - dev->bus->iounmap(dev->bus, data); + g_assert(token.addr >= QPCI_PIO_LIMIT); + dev->bus->memwrite(dev->bus, token.addr + off, buf, len); } +QPCIBar qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr) +{ + QPCIBus *bus = dev->bus; + static const int bar_reg_map[] = { + PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2, + PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5, + }; + QPCIBar bar; + int bar_reg; + uint32_t addr, size; + uint32_t io_type; + uint64_t loc; + + g_assert(barno >= 0 && barno <= 5); + bar_reg = bar_reg_map[barno]; + + qpci_config_writel(dev, bar_reg, 0xFFFFFFFF); + addr = qpci_config_readl(dev, bar_reg); + + io_type = addr & PCI_BASE_ADDRESS_SPACE; + if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { + addr &= PCI_BASE_ADDRESS_IO_MASK; + } else { + addr &= PCI_BASE_ADDRESS_MEM_MASK; + } + g_assert(addr); /* Must have *some* size bits */ + + size = 1U << ctz32(addr); + if (sizeptr) { + *sizeptr = size; + } + + if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { + loc = QEMU_ALIGN_UP(bus->pio_alloc_ptr, size); + + g_assert(loc >= bus->pio_alloc_ptr); + g_assert(loc + size <= QPCI_PIO_LIMIT); /* Keep PIO below 64kiB */ + + bus->pio_alloc_ptr = loc + size; + + qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO); + } else { + loc = QEMU_ALIGN_UP(bus->mmio_alloc_ptr, size); + + /* Check for space */ + g_assert(loc >= bus->mmio_alloc_ptr); + g_assert(loc + size <= bus->mmio_limit); + + bus->mmio_alloc_ptr = loc + size; + + qpci_config_writel(dev, bar_reg, loc); + } + + bar.addr = loc; + return bar; +} + +void qpci_iounmap(QPCIDevice *dev, QPCIBar bar) +{ + /* FIXME */ +} + +QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr) +{ + QPCIBar bar = { .addr = addr }; + return bar; +} + +void qpci_plug_device_test(const char *driver, const char *id, + uint8_t slot, const char *opts) +{ + QDict *response; + char *cmd; + + cmd = g_strdup_printf("{'execute': 'device_add'," + " 'arguments': {" + " 'driver': '%s'," + " 'addr': '%d'," + " %s%s" + " 'id': '%s'" + "}}", driver, slot, + opts ? opts : "", opts ? "," : "", + id); + response = qmp(cmd); + g_free(cmd); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + QDECREF(response); +} diff --git a/tests/libqos/pci.h b/tests/libqos/pci.h index c06add8dbf..ed480614ff 100644 --- a/tests/libqos/pci.h +++ b/tests/libqos/pci.h @@ -15,20 +15,27 @@ #include "libqtest.h" +#define QPCI_PIO_LIMIT 0x10000 + #define QPCI_DEVFN(dev, fn) (((dev) << 3) | (fn)) typedef struct QPCIDevice QPCIDevice; typedef struct QPCIBus QPCIBus; +typedef struct QPCIBar QPCIBar; -struct QPCIBus -{ - uint8_t (*io_readb)(QPCIBus *bus, void *addr); - uint16_t (*io_readw)(QPCIBus *bus, void *addr); - uint32_t (*io_readl)(QPCIBus *bus, void *addr); +struct QPCIBus { + uint8_t (*pio_readb)(QPCIBus *bus, uint32_t addr); + uint16_t (*pio_readw)(QPCIBus *bus, uint32_t addr); + uint32_t (*pio_readl)(QPCIBus *bus, uint32_t addr); + uint64_t (*pio_readq)(QPCIBus *bus, uint32_t addr); - void (*io_writeb)(QPCIBus *bus, void *addr, uint8_t value); - void (*io_writew)(QPCIBus *bus, void *addr, uint16_t value); - void (*io_writel)(QPCIBus *bus, void *addr, uint32_t value); + void (*pio_writeb)(QPCIBus *bus, uint32_t addr, uint8_t value); + void (*pio_writew)(QPCIBus *bus, uint32_t addr, uint16_t value); + void (*pio_writel)(QPCIBus *bus, uint32_t addr, uint32_t value); + void (*pio_writeq)(QPCIBus *bus, uint32_t addr, uint64_t value); + + void (*memread)(QPCIBus *bus, uint32_t addr, void *buf, size_t len); + void (*memwrite)(QPCIBus *bus, uint32_t addr, const void *buf, size_t len); uint8_t (*config_readb)(QPCIBus *bus, int devfn, uint8_t offset); uint16_t (*config_readw)(QPCIBus *bus, int devfn, uint8_t offset); @@ -41,8 +48,12 @@ struct QPCIBus void (*config_writel)(QPCIBus *bus, int devfn, uint8_t offset, uint32_t value); - void *(*iomap)(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *sizeptr); - void (*iounmap)(QPCIBus *bus, void *data); + uint16_t pio_alloc_ptr; + uint64_t mmio_alloc_ptr, mmio_limit; +}; + +struct QPCIBar { + uint64_t addr; }; struct QPCIDevice @@ -50,8 +61,8 @@ struct QPCIDevice QPCIBus *bus; int devfn; bool msix_enabled; - void *msix_table; - void *msix_pba; + QPCIBar msix_table_bar, msix_pba_bar; + uint64_t msix_table_off, msix_pba_off; }; void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, @@ -75,16 +86,27 @@ void qpci_config_writeb(QPCIDevice *dev, uint8_t offset, uint8_t value); void qpci_config_writew(QPCIDevice *dev, uint8_t offset, uint16_t value); void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value); -uint8_t qpci_io_readb(QPCIDevice *dev, void *data); -uint16_t qpci_io_readw(QPCIDevice *dev, void *data); -uint32_t qpci_io_readl(QPCIDevice *dev, void *data); - -void qpci_io_writeb(QPCIDevice *dev, void *data, uint8_t value); -void qpci_io_writew(QPCIDevice *dev, void *data, uint16_t value); -void qpci_io_writel(QPCIDevice *dev, void *data, uint32_t value); - -void *qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr); -void qpci_iounmap(QPCIDevice *dev, void *data); +uint8_t qpci_io_readb(QPCIDevice *dev, QPCIBar token, uint64_t off); +uint16_t qpci_io_readw(QPCIDevice *dev, QPCIBar token, uint64_t off); +uint32_t qpci_io_readl(QPCIDevice *dev, QPCIBar token, uint64_t off); +uint64_t qpci_io_readq(QPCIDevice *dev, QPCIBar token, uint64_t off); + +void qpci_io_writeb(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint8_t value); +void qpci_io_writew(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint16_t value); +void qpci_io_writel(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint32_t value); +void qpci_io_writeq(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint64_t value); + +void qpci_memread(QPCIDevice *bus, QPCIBar token, uint64_t off, + void *buf, size_t len); +void qpci_memwrite(QPCIDevice *bus, QPCIBar token, uint64_t off, + const void *buf, size_t len); +QPCIBar qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr); +void qpci_iounmap(QPCIDevice *dev, QPCIBar addr); +QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr); void qpci_plug_device_test(const char *driver, const char *id, uint8_t slot, const char *opts); diff --git a/tests/libqos/rtas.c b/tests/libqos/rtas.c new file mode 100644 index 0000000000..0269803ce0 --- /dev/null +++ b/tests/libqos/rtas.c @@ -0,0 +1,116 @@ +/* + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/rtas.h" + +static void qrtas_copy_args(uint64_t target_args, uint32_t nargs, + uint32_t *args) +{ + int i; + + for (i = 0; i < nargs; i++) { + writel(target_args + i * sizeof(uint32_t), args[i]); + } +} + +static void qrtas_copy_ret(uint64_t target_ret, uint32_t nret, uint32_t *ret) +{ + int i; + + for (i = 0; i < nret; i++) { + ret[i] = readl(target_ret + i * sizeof(uint32_t)); + } +} + +static uint64_t qrtas_call(QGuestAllocator *alloc, const char *name, + uint32_t nargs, uint32_t *args, + uint32_t nret, uint32_t *ret) +{ + uint64_t res; + uint64_t target_args, target_ret; + + target_args = guest_alloc(alloc, nargs * sizeof(uint32_t)); + target_ret = guest_alloc(alloc, nret * sizeof(uint32_t)); + + qrtas_copy_args(target_args, nargs, args); + res = qtest_rtas_call(global_qtest, name, + nargs, target_args, nret, target_ret); + qrtas_copy_ret(target_ret, nret, ret); + + guest_free(alloc, target_ret); + guest_free(alloc, target_args); + + return res; +} + +int qrtas_get_time_of_day(QGuestAllocator *alloc, struct tm *tm, uint32_t *ns) +{ + int res; + uint32_t ret[8]; + + res = qrtas_call(alloc, "get-time-of-day", 0, NULL, 8, ret); + if (res != 0) { + return res; + } + + res = ret[0]; + memset(tm, 0, sizeof(*tm)); + tm->tm_year = ret[1] - 1900; + tm->tm_mon = ret[2] - 1; + tm->tm_mday = ret[3]; + tm->tm_hour = ret[4]; + tm->tm_min = ret[5]; + tm->tm_sec = ret[6]; + *ns = ret[7]; + + return res; +} + +uint32_t qrtas_ibm_read_pci_config(QGuestAllocator *alloc, uint64_t buid, + uint32_t addr, uint32_t size) +{ + int res; + uint32_t args[4], ret[2]; + + args[0] = addr; + args[1] = buid >> 32; + args[2] = buid & 0xffffffff; + args[3] = size; + res = qrtas_call(alloc, "ibm,read-pci-config", 4, args, 2, ret); + if (res != 0) { + return -1; + } + + if (ret[0] != 0) { + return -1; + } + + return ret[1]; +} + +int qrtas_ibm_write_pci_config(QGuestAllocator *alloc, uint64_t buid, + uint32_t addr, uint32_t size, uint32_t val) +{ + int res; + uint32_t args[5], ret[1]; + + args[0] = addr; + args[1] = buid >> 32; + args[2] = buid & 0xffffffff; + args[3] = size; + args[4] = val; + res = qrtas_call(alloc, "ibm,write-pci-config", 5, args, 1, ret); + if (res != 0) { + return -1; + } + + if (ret[0] != 0) { + return -1; + } + + return 0; +} diff --git a/tests/libqos/rtas.h b/tests/libqos/rtas.h new file mode 100644 index 0000000000..498eb19230 --- /dev/null +++ b/tests/libqos/rtas.h @@ -0,0 +1,15 @@ +/* + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef LIBQOS_RTAS_H +#define LIBQOS_RTAS_H +#include "libqos/malloc.h" + +int qrtas_get_time_of_day(QGuestAllocator *alloc, struct tm *tm, uint32_t *ns); +uint32_t qrtas_ibm_read_pci_config(QGuestAllocator *alloc, uint64_t buid, + uint32_t addr, uint32_t size); +int qrtas_ibm_write_pci_config(QGuestAllocator *alloc, uint64_t buid, + uint32_t addr, uint32_t size, uint32_t val); +#endif /* LIBQOS_RTAS_H */ diff --git a/tests/libqos/usb.c b/tests/libqos/usb.c index f794d92da5..72d7a961fe 100644 --- a/tests/libqos/usb.c +++ b/tests/libqos/usb.c @@ -21,14 +21,12 @@ void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc, uint32_t devfn, int bar) hc->dev = qpci_device_find(pcibus, devfn); g_assert(hc->dev != NULL); qpci_device_enable(hc->dev); - hc->base = qpci_iomap(hc->dev, bar, NULL); - g_assert(hc->base != NULL); + hc->bar = qpci_iomap(hc->dev, bar, NULL); } void uhci_port_test(struct qhc *hc, int port, uint16_t expect) { - void *addr = hc->base + 0x10 + 2 * port; - uint16_t value = qpci_io_readw(hc->dev, addr); + uint16_t value = qpci_io_readw(hc->dev, hc->bar, 0x10 + 2 * port); uint16_t mask = ~(UHCI_PORT_WRITE_CLEAR | UHCI_PORT_RSVD1); g_assert((value & mask) == (expect & mask)); diff --git a/tests/libqos/usb.h b/tests/libqos/usb.h index 8fe56872b7..423dcfd82f 100644 --- a/tests/libqos/usb.h +++ b/tests/libqos/usb.h @@ -5,7 +5,7 @@ struct qhc { QPCIDevice *dev; - void *base; + QPCIBar bar; }; void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc, diff --git a/tests/libqos/virtio-mmio.c b/tests/libqos/virtio-mmio.c index 0cab38f296..7aa8383338 100644 --- a/tests/libqos/virtio-mmio.c +++ b/tests/libqos/virtio-mmio.c @@ -15,28 +15,28 @@ #include "libqos/malloc-generic.h" #include "standard-headers/linux/virtio_ring.h" -static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t addr) +static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t off) { QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readb(dev->addr + addr); + return readb(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } -static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t addr) +static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t off) { QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readw(dev->addr + addr); + return readw(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } -static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t addr) +static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t off) { QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readl(dev->addr + addr); + return readl(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } -static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t addr) +static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t off) { QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readq(dev->addr + addr); + return readq(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } static uint32_t qvirtio_mmio_get_features(QVirtioDevice *d) @@ -199,6 +199,7 @@ QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size) dev->addr = addr; dev->page_size = page_size; dev->vdev.device_type = readl(addr + QVIRTIO_MMIO_DEVICE_ID); + dev->vdev.bus = &qvirtio_mmio; writel(addr + QVIRTIO_MMIO_GUEST_PAGE_SIZE, page_size); diff --git a/tests/libqos/virtio-pci.c b/tests/libqos/virtio-pci.c index 18b92b95dc..d4bf841f23 100644 --- a/tests/libqos/virtio-pci.c +++ b/tests/libqos/virtio-pci.c @@ -62,73 +62,87 @@ static void qvirtio_pci_assign_device(QVirtioDevice *d, void *data) *vpcidev = (QVirtioPCIDevice *)d; } -static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t addr) +#define CONFIG_BASE(dev) (VIRTIO_PCI_CONFIG_OFF((dev)->pdev->msix_enabled)) + +static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readb(dev->pdev, (void *)(uintptr_t)addr); + return qpci_io_readb(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); } -static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t addr) +/* PCI is always read in little-endian order + * but virtio ( < 1.0) is in guest order + * so with a big-endian guest the order has been reversed, + * reverse it again + * virtio-1.0 is always little-endian, like PCI, but this + * case will be managed inside qvirtio_is_big_endian() + */ + +static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t off) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readw(dev->pdev, (void *)(uintptr_t)addr); + uint16_t value; + + value = qpci_io_readw(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); + if (qvirtio_is_big_endian(d)) { + value = bswap16(value); + } + return value; } -static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t addr) +static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t off) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readl(dev->pdev, (void *)(uintptr_t)addr); + uint32_t value; + + value = qpci_io_readl(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); + if (qvirtio_is_big_endian(d)) { + value = bswap32(value); + } + return value; } -static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t addr) +static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t off) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - int i; - uint64_t u64 = 0; + uint64_t val; - if (qtest_big_endian()) { - for (i = 0; i < 8; ++i) { - u64 |= (uint64_t)qpci_io_readb(dev->pdev, - (void *)(uintptr_t)addr + i) << (7 - i) * 8; - } - } else { - for (i = 0; i < 8; ++i) { - u64 |= (uint64_t)qpci_io_readb(dev->pdev, - (void *)(uintptr_t)addr + i) << i * 8; - } + val = qpci_io_readq(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); + if (qvirtio_is_big_endian(d)) { + val = bswap64(val); } - return u64; + return val; } static uint32_t qvirtio_pci_get_features(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readl(dev->pdev, dev->addr + VIRTIO_PCI_HOST_FEATURES); + return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_HOST_FEATURES); } static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writel(dev->pdev, dev->addr + VIRTIO_PCI_GUEST_FEATURES, features); + qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES, features); } static uint32_t qvirtio_pci_get_guest_features(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readl(dev->pdev, dev->addr + VIRTIO_PCI_GUEST_FEATURES); + return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES); } static uint8_t qvirtio_pci_get_status(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readb(dev->pdev, dev->addr + VIRTIO_PCI_STATUS); + return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS); } static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writeb(dev->pdev, dev->addr + VIRTIO_PCI_STATUS, status); + qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS, status); } static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) @@ -152,7 +166,7 @@ static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) } } } else { - return qpci_io_readb(dev->pdev, dev->addr + VIRTIO_PCI_ISR) & 1; + return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_ISR) & 1; } } @@ -176,26 +190,26 @@ static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d) } } } else { - return qpci_io_readb(dev->pdev, dev->addr + VIRTIO_PCI_ISR) & 2; + return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_ISR) & 2; } } static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writeb(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_SEL, index); + qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_SEL, index); } static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readw(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_NUM); + return qpci_io_readw(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NUM); } static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writel(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_PFN, pfn); + qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_PFN, pfn); } static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d, @@ -247,7 +261,7 @@ static void qvirtio_pci_virtqueue_cleanup(QVirtQueue *vq, static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writew(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_NOTIFY, vq->index); + qpci_io_writew(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NOTIFY, vq->index); } const QVirtioBus qvirtio_pci = { @@ -286,20 +300,20 @@ QVirtioPCIDevice *qvirtio_pci_device_find(QPCIBus *bus, uint16_t device_type) QVirtioPCIDevice *dev = NULL; qvirtio_pci_foreach(bus, device_type, qvirtio_pci_assign_device, &dev); + dev->vdev.bus = &qvirtio_pci; + return dev; } void qvirtio_pci_device_enable(QVirtioPCIDevice *d) { qpci_device_enable(d->pdev); - d->addr = qpci_iomap(d->pdev, 0, NULL); - g_assert(d->addr != NULL); + d->bar = qpci_iomap(d->pdev, 0, NULL); } void qvirtio_pci_device_disable(QVirtioPCIDevice *d) { - qpci_iounmap(d->pdev, d->addr); - d->addr = NULL; + qpci_iounmap(d->pdev, d->bar); } void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci, @@ -307,29 +321,33 @@ void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci, { uint16_t vector; uint32_t control; - void *addr; + uint64_t off; g_assert(d->pdev->msix_enabled); - addr = d->pdev->msix_table + (entry * 16); + off = d->pdev->msix_table_off + (entry * 16); g_assert_cmpint(entry, >=, 0); g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev)); vqpci->msix_entry = entry; vqpci->msix_addr = guest_alloc(alloc, 4); - qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_LOWER_ADDR, - vqpci->msix_addr & ~0UL); - qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_UPPER_ADDR, - (vqpci->msix_addr >> 32) & ~0UL); - qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_DATA, vqpci->msix_data); - - control = qpci_io_readl(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL); - qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL, - control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_LOWER_ADDR, vqpci->msix_addr & ~0UL); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_UPPER_ADDR, + (vqpci->msix_addr >> 32) & ~0UL); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_DATA, vqpci->msix_data); + + control = qpci_io_readl(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_VECTOR_CTRL); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_VECTOR_CTRL, + control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); qvirtio_pci_queue_select(&d->vdev, vqpci->vq.index); - qpci_io_writew(d->pdev, d->addr + VIRTIO_MSI_QUEUE_VECTOR, entry); - vector = qpci_io_readw(d->pdev, d->addr + VIRTIO_MSI_QUEUE_VECTOR); + qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR, entry); + vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR); g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); } @@ -338,10 +356,10 @@ void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, { uint16_t vector; uint32_t control; - void *addr; + uint64_t off; g_assert(d->pdev->msix_enabled); - addr = d->pdev->msix_table + (entry * 16); + off = d->pdev->msix_table_off + (entry * 16); g_assert_cmpint(entry, >=, 0); g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev)); @@ -350,17 +368,21 @@ void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, d->config_msix_data = 0x12345678; d->config_msix_addr = guest_alloc(alloc, 4); - qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_LOWER_ADDR, - d->config_msix_addr & ~0UL); - qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_UPPER_ADDR, - (d->config_msix_addr >> 32) & ~0UL); - qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_DATA, d->config_msix_data); - - control = qpci_io_readl(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL); - qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL, - control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); - - qpci_io_writew(d->pdev, d->addr + VIRTIO_MSI_CONFIG_VECTOR, entry); - vector = qpci_io_readw(d->pdev, d->addr + VIRTIO_MSI_CONFIG_VECTOR); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_LOWER_ADDR, d->config_msix_addr & ~0UL); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_UPPER_ADDR, + (d->config_msix_addr >> 32) & ~0UL); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_DATA, d->config_msix_data); + + control = qpci_io_readl(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_VECTOR_CTRL); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_VECTOR_CTRL, + control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); + + qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR, entry); + vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR); g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); } diff --git a/tests/libqos/virtio-pci.h b/tests/libqos/virtio-pci.h index efcac2d3de..38c54c63ea 100644 --- a/tests/libqos/virtio-pci.h +++ b/tests/libqos/virtio-pci.h @@ -16,7 +16,7 @@ typedef struct QVirtioPCIDevice { QVirtioDevice vdev; QPCIDevice *pdev; - void *addr; + QPCIBar bar; uint16_t config_msix_entry; uint64_t config_msix_addr; uint32_t config_msix_data; diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c index d8c2970de7..ec30cb99b2 100644 --- a/tests/libqos/virtio.c +++ b/tests/libqos/virtio.c @@ -13,45 +13,40 @@ #include "standard-headers/linux/virtio_config.h" #include "standard-headers/linux/virtio_ring.h" -uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr) +uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr) { - return bus->config_readb(d, addr); + return d->bus->config_readb(d, addr); } -uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr) +uint16_t qvirtio_config_readw(QVirtioDevice *d, uint64_t addr) { - return bus->config_readw(d, addr); + return d->bus->config_readw(d, addr); } -uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr) +uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr) { - return bus->config_readl(d, addr); + return d->bus->config_readl(d, addr); } -uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr) +uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr) { - return bus->config_readq(d, addr); + return d->bus->config_readq(d, addr); } -uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d) +uint32_t qvirtio_get_features(QVirtioDevice *d) { - return bus->get_features(d); + return d->bus->get_features(d); } -void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d, - uint32_t features) +void qvirtio_set_features(QVirtioDevice *d, uint32_t features) { - bus->set_features(d, features); + d->bus->set_features(d, features); } -QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d, - QGuestAllocator *alloc, uint16_t index) +QVirtQueue *qvirtqueue_setup(QVirtioDevice *d, + QGuestAllocator *alloc, uint16_t index) { - return bus->virtqueue_setup(d, alloc, index); + return d->bus->virtqueue_setup(d, alloc, index); } void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq, @@ -60,40 +55,40 @@ void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq, return bus->virtqueue_cleanup(vq, alloc); } -void qvirtio_reset(const QVirtioBus *bus, QVirtioDevice *d) +void qvirtio_reset(QVirtioDevice *d) { - bus->set_status(d, 0); - g_assert_cmphex(bus->get_status(d), ==, 0); + d->bus->set_status(d, 0); + g_assert_cmphex(d->bus->get_status(d), ==, 0); } -void qvirtio_set_acknowledge(const QVirtioBus *bus, QVirtioDevice *d) +void qvirtio_set_acknowledge(QVirtioDevice *d) { - bus->set_status(d, bus->get_status(d) | VIRTIO_CONFIG_S_ACKNOWLEDGE); - g_assert_cmphex(bus->get_status(d), ==, VIRTIO_CONFIG_S_ACKNOWLEDGE); + d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_ACKNOWLEDGE); + g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_ACKNOWLEDGE); } -void qvirtio_set_driver(const QVirtioBus *bus, QVirtioDevice *d) +void qvirtio_set_driver(QVirtioDevice *d) { - bus->set_status(d, bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER); - g_assert_cmphex(bus->get_status(d), ==, + d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER); + g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE); } -void qvirtio_set_driver_ok(const QVirtioBus *bus, QVirtioDevice *d) +void qvirtio_set_driver_ok(QVirtioDevice *d) { - bus->set_status(d, bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER_OK); - g_assert_cmphex(bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER_OK | + d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER_OK); + g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER_OK | VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE); } -void qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d, +void qvirtio_wait_queue_isr(QVirtioDevice *d, QVirtQueue *vq, gint64 timeout_us) { gint64 start_time = g_get_monotonic_time(); for (;;) { clock_step(100); - if (bus->get_queue_isr_status(d, vq)) { + if (d->bus->get_queue_isr_status(d, vq)) { return; } g_assert(g_get_monotonic_time() - start_time <= timeout_us); @@ -105,8 +100,7 @@ void qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d, * The virtqueue interrupt must not be raised, making this useful for testing * event_index functionality. */ -uint8_t qvirtio_wait_status_byte_no_isr(const QVirtioBus *bus, - QVirtioDevice *d, +uint8_t qvirtio_wait_status_byte_no_isr(QVirtioDevice *d, QVirtQueue *vq, uint64_t addr, gint64 timeout_us) @@ -116,20 +110,19 @@ uint8_t qvirtio_wait_status_byte_no_isr(const QVirtioBus *bus, while ((val = readb(addr)) == 0xff) { clock_step(100); - g_assert(!bus->get_queue_isr_status(d, vq)); + g_assert(!d->bus->get_queue_isr_status(d, vq)); g_assert(g_get_monotonic_time() - start_time <= timeout_us); } return val; } -void qvirtio_wait_config_isr(const QVirtioBus *bus, QVirtioDevice *d, - gint64 timeout_us) +void qvirtio_wait_config_isr(QVirtioDevice *d, gint64 timeout_us) { gint64 start_time = g_get_monotonic_time(); for (;;) { clock_step(100); - if (bus->get_config_isr_status(d)) { + if (d->bus->get_config_isr_status(d)) { return; } g_assert(g_get_monotonic_time() - start_time <= timeout_us); @@ -147,7 +140,7 @@ void qvring_init(const QGuestAllocator *alloc, QVirtQueue *vq, uint64_t addr) for (i = 0; i < vq->size - 1; i++) { /* vq->desc[i].addr */ - writew(vq->desc + (16 * i), 0); + writeq(vq->desc + (16 * i), 0); /* vq->desc[i].next */ writew(vq->desc + (16 * i) + 14, i + 1); } @@ -253,20 +246,19 @@ uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect) return vq->free_head++; /* Return and increase, in this order */ } -void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq, - uint32_t free_head) +void qvirtqueue_kick(QVirtioDevice *d, QVirtQueue *vq, uint32_t free_head) { /* vq->avail->idx */ - uint16_t idx = readl(vq->avail + 2); + uint16_t idx = readw(vq->avail + 2); /* vq->used->flags */ uint16_t flags; /* vq->used->avail_event */ uint16_t avail_event; /* vq->avail->ring[idx % vq->size] */ - writel(vq->avail + 4 + (2 * (idx % vq->size)), free_head); + writew(vq->avail + 4 + (2 * (idx % vq->size)), free_head); /* vq->avail->idx */ - writel(vq->avail + 2, idx + 1); + writew(vq->avail + 2, idx + 1); /* Must read after idx is updated */ flags = readw(vq->avail); @@ -276,7 +268,7 @@ void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq, /* < 1 because we add elements to avail queue one by one */ if ((flags & VRING_USED_F_NO_NOTIFY) == 0 && (!vq->event || (uint16_t)(idx-avail_event) < 1)) { - bus->virtqueue_kick(d, vq); + d->bus->virtqueue_kick(d, vq); } } diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h index 0250842bf2..3397a080e9 100644 --- a/tests/libqos/virtio.h +++ b/tests/libqos/virtio.h @@ -15,7 +15,10 @@ #define QVIRTIO_F_BAD_FEATURE 0x40000000 +typedef struct QVirtioBus QVirtioBus; + typedef struct QVirtioDevice { + const QVirtioBus *bus; /* Device type */ uint16_t device_type; } QVirtioDevice; @@ -39,7 +42,7 @@ typedef struct QVRingIndirectDesc { uint16_t elem; } QVRingIndirectDesc; -typedef struct QVirtioBus { +struct QVirtioBus { uint8_t (*config_readb)(QVirtioDevice *d, uint64_t addr); uint16_t (*config_readw)(QVirtioDevice *d, uint64_t addr); uint32_t (*config_readl)(QVirtioDevice *d, uint64_t addr); @@ -84,7 +87,13 @@ typedef struct QVirtioBus { /* Notify changes in virtqueue */ void (*virtqueue_kick)(QVirtioDevice *d, QVirtQueue *vq); -} QVirtioBus; +}; + +static inline bool qvirtio_is_big_endian(QVirtioDevice *d) +{ + /* FIXME: virtio 1.0 is always little-endian */ + return qtest_big_endian(global_qtest); +} static inline uint32_t qvring_size(uint32_t num, uint32_t align) { @@ -93,34 +102,27 @@ static inline uint32_t qvring_size(uint32_t num, uint32_t align) + sizeof(uint16_t) * 3 + sizeof(struct vring_used_elem) * num; } -uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr); -uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr); -uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr); -uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr); -uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d); -void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d, - uint32_t features); - -void qvirtio_reset(const QVirtioBus *bus, QVirtioDevice *d); -void qvirtio_set_acknowledge(const QVirtioBus *bus, QVirtioDevice *d); -void qvirtio_set_driver(const QVirtioBus *bus, QVirtioDevice *d); -void qvirtio_set_driver_ok(const QVirtioBus *bus, QVirtioDevice *d); - -void qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d, +uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr); +uint16_t qvirtio_config_readw(QVirtioDevice *d, uint64_t addr); +uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr); +uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr); +uint32_t qvirtio_get_features(QVirtioDevice *d); +void qvirtio_set_features(QVirtioDevice *d, uint32_t features); + +void qvirtio_reset(QVirtioDevice *d); +void qvirtio_set_acknowledge(QVirtioDevice *d); +void qvirtio_set_driver(QVirtioDevice *d); +void qvirtio_set_driver_ok(QVirtioDevice *d); + +void qvirtio_wait_queue_isr(QVirtioDevice *d, QVirtQueue *vq, gint64 timeout_us); -uint8_t qvirtio_wait_status_byte_no_isr(const QVirtioBus *bus, - QVirtioDevice *d, +uint8_t qvirtio_wait_status_byte_no_isr(QVirtioDevice *d, QVirtQueue *vq, uint64_t addr, gint64 timeout_us); -void qvirtio_wait_config_isr(const QVirtioBus *bus, QVirtioDevice *d, - gint64 timeout_us); -QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d, - QGuestAllocator *alloc, uint16_t index); +void qvirtio_wait_config_isr(QVirtioDevice *d, gint64 timeout_us); +QVirtQueue *qvirtqueue_setup(QVirtioDevice *d, + QGuestAllocator *alloc, uint16_t index); void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq, QGuestAllocator *alloc); @@ -132,8 +134,7 @@ void qvring_indirect_desc_add(QVRingIndirectDesc *indirect, uint64_t data, uint32_t qvirtqueue_add(QVirtQueue *vq, uint64_t data, uint32_t len, bool write, bool next); uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect); -void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq, - uint32_t free_head); +void qvirtqueue_kick(QVirtioDevice *d, QVirtQueue *vq, uint32_t free_head); void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx); #endif |