200 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			200 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * QMP commands related to PCI | ||
|  |  * | ||
|  |  * Copyright (c) 2004 Fabrice Bellard | ||
|  |  * | ||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||
|  |  * in the Software without restriction, including without limitation the rights | ||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||
|  |  * furnished to do so, subject to the following conditions: | ||
|  |  * | ||
|  |  * The above copyright notice and this permission notice shall be included in | ||
|  |  * all copies or substantial portions of the Software. | ||
|  |  * | ||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
|  |  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
|  |  * THE SOFTWARE. | ||
|  |  */ | ||
|  | 
 | ||
|  | #include "qemu/osdep.h"
 | ||
|  | #include "hw/pci/pci.h"
 | ||
|  | #include "hw/pci/pci_bridge.h"
 | ||
|  | #include "pci-internal.h"
 | ||
|  | #include "qapi/qapi-commands-pci.h"
 | ||
|  | 
 | ||
|  | static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num); | ||
|  | 
 | ||
|  | static PciMemoryRegionList *qmp_query_pci_regions(const PCIDevice *dev) | ||
|  | { | ||
|  |     PciMemoryRegionList *head = NULL, **tail = &head; | ||
|  |     int i; | ||
|  | 
 | ||
|  |     for (i = 0; i < PCI_NUM_REGIONS; i++) { | ||
|  |         const PCIIORegion *r = &dev->io_regions[i]; | ||
|  |         PciMemoryRegion *region; | ||
|  | 
 | ||
|  |         if (!r->size) { | ||
|  |             continue; | ||
|  |         } | ||
|  | 
 | ||
|  |         region = g_malloc0(sizeof(*region)); | ||
|  | 
 | ||
|  |         if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { | ||
|  |             region->type = g_strdup("io"); | ||
|  |         } else { | ||
|  |             region->type = g_strdup("memory"); | ||
|  |             region->has_prefetch = true; | ||
|  |             region->prefetch = !!(r->type & PCI_BASE_ADDRESS_MEM_PREFETCH); | ||
|  |             region->has_mem_type_64 = true; | ||
|  |             region->mem_type_64 = !!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64); | ||
|  |         } | ||
|  | 
 | ||
|  |         region->bar = i; | ||
|  |         region->address = r->addr; | ||
|  |         region->size = r->size; | ||
|  | 
 | ||
|  |         QAPI_LIST_APPEND(tail, region); | ||
|  |     } | ||
|  | 
 | ||
|  |     return head; | ||
|  | } | ||
|  | 
 | ||
|  | static PciBridgeInfo *qmp_query_pci_bridge(PCIDevice *dev, PCIBus *bus, | ||
|  |                                            int bus_num) | ||
|  | { | ||
|  |     PciBridgeInfo *info; | ||
|  |     PciMemoryRange *range; | ||
|  | 
 | ||
|  |     info = g_new0(PciBridgeInfo, 1); | ||
|  | 
 | ||
|  |     info->bus = g_new0(PciBusInfo, 1); | ||
|  |     info->bus->number = dev->config[PCI_PRIMARY_BUS]; | ||
|  |     info->bus->secondary = dev->config[PCI_SECONDARY_BUS]; | ||
|  |     info->bus->subordinate = dev->config[PCI_SUBORDINATE_BUS]; | ||
|  | 
 | ||
|  |     range = info->bus->io_range = g_new0(PciMemoryRange, 1); | ||
|  |     range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO); | ||
|  |     range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO); | ||
|  | 
 | ||
|  |     range = info->bus->memory_range = g_new0(PciMemoryRange, 1); | ||
|  |     range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); | ||
|  |     range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); | ||
|  | 
 | ||
|  |     range = info->bus->prefetchable_range = g_new0(PciMemoryRange, 1); | ||
|  |     range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); | ||
|  |     range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); | ||
|  | 
 | ||
|  |     if (dev->config[PCI_SECONDARY_BUS] != 0) { | ||
|  |         PCIBus *child_bus = pci_find_bus_nr(bus, | ||
|  |                                             dev->config[PCI_SECONDARY_BUS]); | ||
|  |         if (child_bus) { | ||
|  |             info->has_devices = true; | ||
|  |             info->devices = qmp_query_pci_devices(child_bus, | ||
|  |                                             dev->config[PCI_SECONDARY_BUS]); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     return info; | ||
|  | } | ||
|  | 
 | ||
|  | static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus, | ||
|  |                                            int bus_num) | ||
|  | { | ||
|  |     const pci_class_desc *desc; | ||
|  |     PciDeviceInfo *info; | ||
|  |     uint8_t type; | ||
|  |     int class; | ||
|  | 
 | ||
|  |     info = g_new0(PciDeviceInfo, 1); | ||
|  |     info->bus = bus_num; | ||
|  |     info->slot = PCI_SLOT(dev->devfn); | ||
|  |     info->function = PCI_FUNC(dev->devfn); | ||
|  | 
 | ||
|  |     info->class_info = g_new0(PciDeviceClass, 1); | ||
|  |     class = pci_get_word(dev->config + PCI_CLASS_DEVICE); | ||
|  |     info->class_info->q_class = class; | ||
|  |     desc = get_class_desc(class); | ||
|  |     if (desc->desc) { | ||
|  |         info->class_info->desc = g_strdup(desc->desc); | ||
|  |     } | ||
|  | 
 | ||
|  |     info->id = g_new0(PciDeviceId, 1); | ||
|  |     info->id->vendor = pci_get_word(dev->config + PCI_VENDOR_ID); | ||
|  |     info->id->device = pci_get_word(dev->config + PCI_DEVICE_ID); | ||
|  |     info->regions = qmp_query_pci_regions(dev); | ||
|  |     info->qdev_id = g_strdup(dev->qdev.id ? dev->qdev.id : ""); | ||
|  | 
 | ||
|  |     info->irq_pin = dev->config[PCI_INTERRUPT_PIN]; | ||
|  |     if (dev->config[PCI_INTERRUPT_PIN] != 0) { | ||
|  |         info->has_irq = true; | ||
|  |         info->irq = dev->config[PCI_INTERRUPT_LINE]; | ||
|  |     } | ||
|  | 
 | ||
|  |     type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION; | ||
|  |     if (type == PCI_HEADER_TYPE_BRIDGE) { | ||
|  |         info->pci_bridge = qmp_query_pci_bridge(dev, bus, bus_num); | ||
|  |     } else if (type == PCI_HEADER_TYPE_NORMAL) { | ||
|  |         info->id->has_subsystem = info->id->has_subsystem_vendor = true; | ||
|  |         info->id->subsystem = pci_get_word(dev->config + PCI_SUBSYSTEM_ID); | ||
|  |         info->id->subsystem_vendor = | ||
|  |             pci_get_word(dev->config + PCI_SUBSYSTEM_VENDOR_ID); | ||
|  |     } else if (type == PCI_HEADER_TYPE_CARDBUS) { | ||
|  |         info->id->has_subsystem = info->id->has_subsystem_vendor = true; | ||
|  |         info->id->subsystem = pci_get_word(dev->config + PCI_CB_SUBSYSTEM_ID); | ||
|  |         info->id->subsystem_vendor = | ||
|  |             pci_get_word(dev->config + PCI_CB_SUBSYSTEM_VENDOR_ID); | ||
|  |     } | ||
|  | 
 | ||
|  |     return info; | ||
|  | } | ||
|  | 
 | ||
|  | static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num) | ||
|  | { | ||
|  |     PciDeviceInfoList *head = NULL, **tail = &head; | ||
|  |     PCIDevice *dev; | ||
|  |     int devfn; | ||
|  | 
 | ||
|  |     for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { | ||
|  |         dev = bus->devices[devfn]; | ||
|  |         if (dev) { | ||
|  |             QAPI_LIST_APPEND(tail, qmp_query_pci_device(dev, bus, bus_num)); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     return head; | ||
|  | } | ||
|  | 
 | ||
|  | static PciInfo *qmp_query_pci_bus(PCIBus *bus, int bus_num) | ||
|  | { | ||
|  |     PciInfo *info = NULL; | ||
|  | 
 | ||
|  |     bus = pci_find_bus_nr(bus, bus_num); | ||
|  |     if (bus) { | ||
|  |         info = g_malloc0(sizeof(*info)); | ||
|  |         info->bus = bus_num; | ||
|  |         info->devices = qmp_query_pci_devices(bus, bus_num); | ||
|  |     } | ||
|  | 
 | ||
|  |     return info; | ||
|  | } | ||
|  | 
 | ||
|  | PciInfoList *qmp_query_pci(Error **errp) | ||
|  | { | ||
|  |     PciInfoList *head = NULL, **tail = &head; | ||
|  |     PCIHostState *host_bridge; | ||
|  | 
 | ||
|  |     QLIST_FOREACH(host_bridge, &pci_host_bridges, next) { | ||
|  |         QAPI_LIST_APPEND(tail, | ||
|  |                          qmp_query_pci_bus(host_bridge->bus, | ||
|  |                                            pci_bus_num(host_bridge->bus))); | ||
|  |     } | ||
|  | 
 | ||
|  |     return head; | ||
|  | } |