libqos: add PPC64 PCI support
Signed-off-by: Laurent Vivier <lvivier@redhat.com> [dwg: Fixed build problem on 32-bit hosts] Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
This commit is contained in:
		
							parent
							
								
									2020b67d85
								
							
						
					
					
						commit
						cf716b31cb
					
				| @ -591,6 +591,7 @@ libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o | ||||
| libqos-spapr-obj-y = $(libqos-obj-y) tests/libqos/malloc-spapr.o | ||||
| libqos-spapr-obj-y += tests/libqos/libqos-spapr.o | ||||
| libqos-spapr-obj-y += tests/libqos/rtas.o | ||||
| libqos-spapr-obj-y += tests/libqos/pci-spapr.o | ||||
| libqos-pc-obj-y = $(libqos-obj-y) tests/libqos/pci-pc.o | ||||
| libqos-pc-obj-y += tests/libqos/malloc-pc.o tests/libqos/libqos-pc.o | ||||
| libqos-pc-obj-y += tests/libqos/ahci.o | ||||
|  | ||||
| @ -255,28 +255,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; | ||||
|  | ||||
							
								
								
									
										288
									
								
								tests/libqos/pci-spapr.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										288
									
								
								tests/libqos/pci-spapr.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,288 @@ | ||||
| /*
 | ||||
|  * 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 */ | ||||
| 
 | ||||
| #define SPAPR_PCI_BASE_BUID          0x800000020000000ULL | ||||
| 
 | ||||
| #define SPAPR_PCI_MEM_WIN_BUS_OFFSET 0x80000000ULL | ||||
| 
 | ||||
| #define SPAPR_PCI_WINDOW_BASE        0x10000000000ULL | ||||
| #define SPAPR_PCI_WINDOW_SPACING     0x1000000000ULL | ||||
| #define SPAPR_PCI_MMIO_WIN_OFF       0xA0000000 | ||||
| #define SPAPR_PCI_MMIO_WIN_SIZE      (SPAPR_PCI_WINDOW_SPACING - \ | ||||
|                                      SPAPR_PCI_MEM_WIN_BUS_OFFSET) | ||||
| #define SPAPR_PCI_IO_WIN_OFF         0x80000000 | ||||
| #define SPAPR_PCI_IO_WIN_SIZE        0x10000 | ||||
| 
 | ||||
| /* index is the phb index */ | ||||
| 
 | ||||
| #define BUIDBASE(index)              (SPAPR_PCI_BASE_BUID + (index)) | ||||
| #define PCIBASE(index)               (SPAPR_PCI_WINDOW_BASE + \ | ||||
|                                       (index) * SPAPR_PCI_WINDOW_SPACING) | ||||
| #define IOBASE(index)                (PCIBASE(index) + SPAPR_PCI_IO_WIN_OFF) | ||||
| #define MMIOBASE(index)              (PCIBASE(index) + SPAPR_PCI_MMIO_WIN_OFF) | ||||
| 
 | ||||
| typedef struct QPCIBusSPAPR { | ||||
|     QPCIBus bus; | ||||
|     QGuestAllocator *alloc; | ||||
| 
 | ||||
|     uint64_t pci_hole_start; | ||||
|     uint64_t pci_hole_size; | ||||
|     uint64_t pci_hole_alloc; | ||||
| 
 | ||||
|     uint32_t pci_iohole_start; | ||||
|     uint32_t pci_iohole_size; | ||||
|     uint32_t pci_iohole_alloc; | ||||
| } 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_io_readb(QPCIBus *bus, void *addr) | ||||
| { | ||||
|     uint64_t port = (uintptr_t)addr; | ||||
|     uint8_t v; | ||||
|     if (port < SPAPR_PCI_IO_WIN_SIZE) { | ||||
|         v = readb(IOBASE(0) + port); | ||||
|     } else { | ||||
|         v = readb(MMIOBASE(0) + port); | ||||
|     } | ||||
|     return v; | ||||
| } | ||||
| 
 | ||||
| static uint16_t qpci_spapr_io_readw(QPCIBus *bus, void *addr) | ||||
| { | ||||
|     uint64_t port = (uintptr_t)addr; | ||||
|     uint16_t v; | ||||
|     if (port < SPAPR_PCI_IO_WIN_SIZE) { | ||||
|         v = readw(IOBASE(0) + port); | ||||
|     } else { | ||||
|         v = readw(MMIOBASE(0) + port); | ||||
|     } | ||||
|     return bswap16(v); | ||||
| } | ||||
| 
 | ||||
| static uint32_t qpci_spapr_io_readl(QPCIBus *bus, void *addr) | ||||
| { | ||||
|     uint64_t port = (uintptr_t)addr; | ||||
|     uint32_t v; | ||||
|     if (port < SPAPR_PCI_IO_WIN_SIZE) { | ||||
|         v = readl(IOBASE(0) + port); | ||||
|     } else { | ||||
|         v = readl(MMIOBASE(0) + port); | ||||
|     } | ||||
|     return bswap32(v); | ||||
| } | ||||
| 
 | ||||
| static void qpci_spapr_io_writeb(QPCIBus *bus, void *addr, uint8_t value) | ||||
| { | ||||
|     uint64_t port = (uintptr_t)addr; | ||||
|     if (port < SPAPR_PCI_IO_WIN_SIZE) { | ||||
|         writeb(IOBASE(0) + port, value); | ||||
|     } else { | ||||
|         writeb(MMIOBASE(0) + port, value); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void qpci_spapr_io_writew(QPCIBus *bus, void *addr, uint16_t value) | ||||
| { | ||||
|     uint64_t port = (uintptr_t)addr; | ||||
|     value = bswap16(value); | ||||
|     if (port < SPAPR_PCI_IO_WIN_SIZE) { | ||||
|         writew(IOBASE(0) + port, value); | ||||
|     } else { | ||||
|         writew(MMIOBASE(0) + port, value); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void qpci_spapr_io_writel(QPCIBus *bus, void *addr, uint32_t value) | ||||
| { | ||||
|     uint64_t port = (uintptr_t)addr; | ||||
|     value = bswap32(value); | ||||
|     if (port < SPAPR_PCI_IO_WIN_SIZE) { | ||||
|         writel(IOBASE(0) + port, value); | ||||
|     } else { | ||||
|         writel(MMIOBASE(0) + port, value); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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, BUIDBASE(0), | ||||
|                                      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, BUIDBASE(0), | ||||
|                                      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, BUIDBASE(0), | ||||
|                                      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, BUIDBASE(0), | ||||
|                                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, BUIDBASE(0), | ||||
|                                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, BUIDBASE(0), | ||||
|                                config_addr, 4, value); | ||||
| } | ||||
| 
 | ||||
| static void *qpci_spapr_iomap(QPCIBus *bus, QPCIDevice *dev, int barno, | ||||
|                               uint64_t *sizeptr) | ||||
| { | ||||
|     QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, 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 *)(unsigned long)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 *)(unsigned long)loc; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void qpci_spapr_iounmap(QPCIBus *bus, void *data) | ||||
| { | ||||
|     /* FIXME */ | ||||
| } | ||||
| 
 | ||||
| QPCIBus *qpci_init_spapr(QGuestAllocator *alloc) | ||||
| { | ||||
|     QPCIBusSPAPR *ret; | ||||
| 
 | ||||
|     ret = g_malloc(sizeof(*ret)); | ||||
| 
 | ||||
|     ret->alloc = alloc; | ||||
| 
 | ||||
|     ret->bus.io_readb = qpci_spapr_io_readb; | ||||
|     ret->bus.io_readw = qpci_spapr_io_readw; | ||||
|     ret->bus.io_readl = qpci_spapr_io_readl; | ||||
| 
 | ||||
|     ret->bus.io_writeb = qpci_spapr_io_writeb; | ||||
|     ret->bus.io_writew = qpci_spapr_io_writew; | ||||
|     ret->bus.io_writel = qpci_spapr_io_writel; | ||||
| 
 | ||||
|     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; | ||||
| 
 | ||||
|     ret->bus.iomap = qpci_spapr_iomap; | ||||
|     ret->bus.iounmap = qpci_spapr_iounmap; | ||||
| 
 | ||||
|     ret->pci_hole_start = 0xC0000000; | ||||
|     ret->pci_hole_size = SPAPR_PCI_MMIO_WIN_SIZE; | ||||
|     ret->pci_hole_alloc = 0; | ||||
| 
 | ||||
|     ret->pci_iohole_start = 0xc000; | ||||
|     ret->pci_iohole_size = SPAPR_PCI_IO_WIN_SIZE; | ||||
|     ret->pci_iohole_alloc = 0; | ||||
| 
 | ||||
|     return &ret->bus; | ||||
| } | ||||
| 
 | ||||
| void qpci_free_spapr(QPCIBus *bus) | ||||
| { | ||||
|     QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); | ||||
| 
 | ||||
|     g_free(s); | ||||
| } | ||||
							
								
								
									
										17
									
								
								tests/libqos/pci-spapr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/libqos/pci-spapr.h
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||
| @ -263,4 +263,24 @@ void qpci_iounmap(QPCIDevice *dev, void *data) | ||||
|     dev->bus->iounmap(dev->bus, data); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
|  | ||||
| @ -69,3 +69,48 @@ int qrtas_get_time_of_day(QGuestAllocator *alloc, struct tm *tm, uint32_t *ns) | ||||
| 
 | ||||
|     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; | ||||
| } | ||||
|  | ||||
| @ -8,4 +8,8 @@ | ||||
| #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 */ | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Laurent Vivier
						Laurent Vivier