Merge commit 'mst/pci' into staging
This commit is contained in:
		
						commit
						ad382a1257
					
				
							
								
								
									
										74
									
								
								hw/msix.c
									
									
									
									
									
								
							
							
						
						
									
										74
									
								
								hw/msix.c
									
									
									
									
									
								
							| @ -20,6 +20,7 @@ | ||||
| #define  PCI_MSIX_FLAGS 2     /* Table at lower 11 bits */ | ||||
| #define  PCI_MSIX_FLAGS_QSIZE	0x7FF | ||||
| #define  PCI_MSIX_FLAGS_ENABLE	(1 << 15) | ||||
| #define  PCI_MSIX_FLAGS_MASKALL	(1 << 14) | ||||
| #define  PCI_MSIX_FLAGS_BIRMASK	(7 << 0) | ||||
| 
 | ||||
| /* MSI-X capability structure */ | ||||
| @ -27,9 +28,10 @@ | ||||
| #define MSIX_PBA_OFFSET 8 | ||||
| #define MSIX_CAP_LENGTH 12 | ||||
| 
 | ||||
| /* MSI enable bit is in byte 1 in FLAGS register */ | ||||
| #define MSIX_ENABLE_OFFSET (PCI_MSIX_FLAGS + 1) | ||||
| /* MSI enable bit and maskall bit are in byte 1 in FLAGS register */ | ||||
| #define MSIX_CONTROL_OFFSET (PCI_MSIX_FLAGS + 1) | ||||
| #define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8) | ||||
| #define MSIX_MASKALL_MASK (PCI_MSIX_FLAGS_MASKALL >> 8) | ||||
| 
 | ||||
| /* MSI-X table format */ | ||||
| #define MSIX_MSG_ADDR 0 | ||||
| @ -101,22 +103,11 @@ static int msix_add_config(struct PCIDevice *pdev, unsigned short nentries, | ||||
|                  bar_nr); | ||||
|     pdev->msix_cap = config_offset; | ||||
|     /* Make flags bit writeable. */ | ||||
|     pdev->wmask[config_offset + MSIX_ENABLE_OFFSET] |= MSIX_ENABLE_MASK; | ||||
|     pdev->wmask[config_offset + MSIX_CONTROL_OFFSET] |= MSIX_ENABLE_MASK | | ||||
| 	    MSIX_MASKALL_MASK; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /* Handle MSI-X capability config write. */ | ||||
| void msix_write_config(PCIDevice *dev, uint32_t addr, | ||||
|                        uint32_t val, int len) | ||||
| { | ||||
|     unsigned enable_pos = dev->msix_cap + MSIX_ENABLE_OFFSET; | ||||
|     if (addr + len <= enable_pos || addr > enable_pos) | ||||
|         return; | ||||
| 
 | ||||
|     if (msix_enabled(dev)) | ||||
|         qemu_set_irq(dev->irq[0], 0); | ||||
| } | ||||
| 
 | ||||
| static uint32_t msix_mmio_readl(void *opaque, target_phys_addr_t addr) | ||||
| { | ||||
|     PCIDevice *dev = opaque; | ||||
| @ -157,10 +148,50 @@ static void msix_clr_pending(PCIDevice *dev, int vector) | ||||
|     *msix_pending_byte(dev, vector) &= ~msix_pending_mask(vector); | ||||
| } | ||||
| 
 | ||||
| static int msix_function_masked(PCIDevice *dev) | ||||
| { | ||||
|     return dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & MSIX_MASKALL_MASK; | ||||
| } | ||||
| 
 | ||||
| static int msix_is_masked(PCIDevice *dev, int vector) | ||||
| { | ||||
|     unsigned offset = vector * MSIX_ENTRY_SIZE + MSIX_VECTOR_CTRL; | ||||
|     return dev->msix_table_page[offset] & MSIX_VECTOR_MASK; | ||||
|     return msix_function_masked(dev) || | ||||
| 	   dev->msix_table_page[offset] & MSIX_VECTOR_MASK; | ||||
| } | ||||
| 
 | ||||
| static void msix_handle_mask_update(PCIDevice *dev, int vector) | ||||
| { | ||||
|     if (!msix_is_masked(dev, vector) && msix_is_pending(dev, vector)) { | ||||
|         msix_clr_pending(dev, vector); | ||||
|         msix_notify(dev, vector); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Handle MSI-X capability config write. */ | ||||
| void msix_write_config(PCIDevice *dev, uint32_t addr, | ||||
|                        uint32_t val, int len) | ||||
| { | ||||
|     unsigned enable_pos = dev->msix_cap + MSIX_CONTROL_OFFSET; | ||||
|     int vector; | ||||
| 
 | ||||
|     if (addr + len <= enable_pos || addr > enable_pos) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (!msix_enabled(dev)) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     qemu_set_irq(dev->irq[0], 0); | ||||
| 
 | ||||
|     if (msix_function_masked(dev)) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     for (vector = 0; vector < dev->msix_entries_nr; ++vector) { | ||||
|         msix_handle_mask_update(dev, vector); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void msix_mmio_writel(void *opaque, target_phys_addr_t addr, | ||||
| @ -170,10 +201,7 @@ static void msix_mmio_writel(void *opaque, target_phys_addr_t addr, | ||||
|     unsigned int offset = addr & (MSIX_PAGE_SIZE - 1) & ~0x3; | ||||
|     int vector = offset / MSIX_ENTRY_SIZE; | ||||
|     pci_set_long(dev->msix_table_page + offset, val); | ||||
|     if (!msix_is_masked(dev, vector) && msix_is_pending(dev, vector)) { | ||||
|         msix_clr_pending(dev, vector); | ||||
|         msix_notify(dev, vector); | ||||
|     } | ||||
|     msix_handle_mask_update(dev, vector); | ||||
| } | ||||
| 
 | ||||
| static void msix_mmio_write_unallowed(void *opaque, target_phys_addr_t addr, | ||||
| @ -327,7 +355,7 @@ int msix_present(PCIDevice *dev) | ||||
| int msix_enabled(PCIDevice *dev) | ||||
| { | ||||
|     return (dev->cap_present & QEMU_PCI_CAP_MSIX) && | ||||
|         (dev->config[dev->msix_cap + MSIX_ENABLE_OFFSET] & | ||||
|         (dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & | ||||
|          MSIX_ENABLE_MASK); | ||||
| } | ||||
| 
 | ||||
| @ -363,8 +391,8 @@ void msix_reset(PCIDevice *dev) | ||||
|     if (!(dev->cap_present & QEMU_PCI_CAP_MSIX)) | ||||
|         return; | ||||
|     msix_free_irq_entries(dev); | ||||
|     dev->config[dev->msix_cap + MSIX_ENABLE_OFFSET] &= | ||||
| 	    ~dev->wmask[dev->msix_cap + MSIX_ENABLE_OFFSET]; | ||||
|     dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &= | ||||
| 	    ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET]; | ||||
|     memset(dev->msix_table_page, 0, MSIX_PAGE_SIZE); | ||||
|     msix_mask_all(dev, dev->msix_entries_nr); | ||||
| } | ||||
|  | ||||
							
								
								
									
										144
									
								
								hw/pci.c
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								hw/pci.c
									
									
									
									
									
								
							| @ -103,11 +103,48 @@ static int pci_bar(PCIDevice *d, int reg) | ||||
|     return type == PCI_HEADER_TYPE_BRIDGE ? PCI_ROM_ADDRESS1 : PCI_ROM_ADDRESS; | ||||
| } | ||||
| 
 | ||||
| static inline int pci_irq_state(PCIDevice *d, int irq_num) | ||||
| { | ||||
| 	return (d->irq_state >> irq_num) & 0x1; | ||||
| } | ||||
| 
 | ||||
| static inline void pci_set_irq_state(PCIDevice *d, int irq_num, int level) | ||||
| { | ||||
| 	d->irq_state &= ~(0x1 << irq_num); | ||||
| 	d->irq_state |= level << irq_num; | ||||
| } | ||||
| 
 | ||||
| static void pci_change_irq_level(PCIDevice *pci_dev, int irq_num, int change) | ||||
| { | ||||
|     PCIBus *bus; | ||||
|     for (;;) { | ||||
|         bus = pci_dev->bus; | ||||
|         irq_num = bus->map_irq(pci_dev, irq_num); | ||||
|         if (bus->set_irq) | ||||
|             break; | ||||
|         pci_dev = bus->parent_dev; | ||||
|     } | ||||
|     bus->irq_count[irq_num] += change; | ||||
|     bus->set_irq(bus->irq_opaque, irq_num, bus->irq_count[irq_num] != 0); | ||||
| } | ||||
| 
 | ||||
| /* Update interrupt status bit in config space on interrupt
 | ||||
|  * state change. */ | ||||
| static void pci_update_irq_status(PCIDevice *dev) | ||||
| { | ||||
|     if (dev->irq_state) { | ||||
|         dev->config[PCI_STATUS] |= PCI_STATUS_INTERRUPT; | ||||
|     } else { | ||||
|         dev->config[PCI_STATUS] &= ~PCI_STATUS_INTERRUPT; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void pci_device_reset(PCIDevice *dev) | ||||
| { | ||||
|     int r; | ||||
| 
 | ||||
|     memset(dev->irq_state, 0, sizeof dev->irq_state); | ||||
|     dev->irq_state = 0; | ||||
|     pci_update_irq_status(dev); | ||||
|     dev->config[PCI_COMMAND] &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | | ||||
|                                   PCI_COMMAND_MASTER); | ||||
|     dev->config[PCI_CACHE_LINE_SIZE] = 0x0; | ||||
| @ -274,6 +311,43 @@ static VMStateInfo vmstate_info_pci_config = { | ||||
|     .put  = put_pci_config_device, | ||||
| }; | ||||
| 
 | ||||
| static int get_pci_irq_state(QEMUFile *f, void *pv, size_t size) | ||||
| { | ||||
|     PCIDevice *s = container_of(pv, PCIDevice, config); | ||||
|     uint32_t irq_state[PCI_NUM_PINS]; | ||||
|     int i; | ||||
|     for (i = 0; i < PCI_NUM_PINS; ++i) { | ||||
|         irq_state[i] = qemu_get_be32(f); | ||||
|         if (irq_state[i] != 0x1 && irq_state[i] != 0) { | ||||
|             fprintf(stderr, "irq state %d: must be 0 or 1.\n", | ||||
|                     irq_state[i]); | ||||
|             return -EINVAL; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     for (i = 0; i < PCI_NUM_PINS; ++i) { | ||||
|         pci_set_irq_state(s, i, irq_state[i]); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void put_pci_irq_state(QEMUFile *f, void *pv, size_t size) | ||||
| { | ||||
|     int i; | ||||
|     PCIDevice *s = container_of(pv, PCIDevice, config); | ||||
| 
 | ||||
|     for (i = 0; i < PCI_NUM_PINS; ++i) { | ||||
|         qemu_put_be32(f, pci_irq_state(s, i)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static VMStateInfo vmstate_info_pci_irq_state = { | ||||
|     .name = "pci irq state", | ||||
|     .get  = get_pci_irq_state, | ||||
|     .put  = put_pci_irq_state, | ||||
| }; | ||||
| 
 | ||||
| const VMStateDescription vmstate_pci_device = { | ||||
|     .name = "PCIDevice", | ||||
|     .version_id = 2, | ||||
| @ -284,7 +358,9 @@ const VMStateDescription vmstate_pci_device = { | ||||
|         VMSTATE_BUFFER_UNSAFE_INFO(config, PCIDevice, 0, | ||||
|                                    vmstate_info_pci_config, | ||||
|                                    PCI_CONFIG_SPACE_SIZE), | ||||
|         VMSTATE_INT32_ARRAY_V(irq_state, PCIDevice, PCI_NUM_PINS, 2), | ||||
|         VMSTATE_BUFFER_UNSAFE_INFO(irq_state, PCIDevice, 2, | ||||
| 				   vmstate_info_pci_irq_state, | ||||
| 				   PCI_NUM_PINS * sizeof(int32_t)), | ||||
|         VMSTATE_END_OF_LIST() | ||||
|     } | ||||
| }; | ||||
| @ -299,7 +375,9 @@ const VMStateDescription vmstate_pcie_device = { | ||||
|         VMSTATE_BUFFER_UNSAFE_INFO(config, PCIDevice, 0, | ||||
|                                    vmstate_info_pci_config, | ||||
|                                    PCIE_CONFIG_SPACE_SIZE), | ||||
|         VMSTATE_INT32_ARRAY_V(irq_state, PCIDevice, PCI_NUM_PINS, 2), | ||||
|         VMSTATE_BUFFER_UNSAFE_INFO(irq_state, PCIDevice, 2, | ||||
| 				   vmstate_info_pci_irq_state, | ||||
| 				   PCI_NUM_PINS * sizeof(int32_t)), | ||||
|         VMSTATE_END_OF_LIST() | ||||
|     } | ||||
| }; | ||||
| @ -311,12 +389,23 @@ static inline const VMStateDescription *pci_get_vmstate(PCIDevice *s) | ||||
| 
 | ||||
| void pci_device_save(PCIDevice *s, QEMUFile *f) | ||||
| { | ||||
|     /* Clear interrupt status bit: it is implicit
 | ||||
|      * in irq_state which we are saving. | ||||
|      * This makes us compatible with old devices | ||||
|      * which never set or clear this bit. */ | ||||
|     s->config[PCI_STATUS] &= ~PCI_STATUS_INTERRUPT; | ||||
|     vmstate_save_state(f, pci_get_vmstate(s), s); | ||||
|     /* Restore the interrupt status bit. */ | ||||
|     pci_update_irq_status(s); | ||||
| } | ||||
| 
 | ||||
| int pci_device_load(PCIDevice *s, QEMUFile *f) | ||||
| { | ||||
|     return vmstate_load_state(f, pci_get_vmstate(s), s, s->version_id); | ||||
|     int ret; | ||||
|     ret = vmstate_load_state(f, pci_get_vmstate(s), s, s->version_id); | ||||
|     /* Restore the interrupt status bit. */ | ||||
|     pci_update_irq_status(s); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static int pci_set_default_subsystem_id(PCIDevice *pci_dev) | ||||
| @ -429,7 +518,8 @@ static void pci_init_wmask(PCIDevice *dev) | ||||
|     dev->wmask[PCI_CACHE_LINE_SIZE] = 0xff; | ||||
|     dev->wmask[PCI_INTERRUPT_LINE] = 0xff; | ||||
|     pci_set_word(dev->wmask + PCI_COMMAND, | ||||
|                  PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); | ||||
|                  PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | | ||||
|                  PCI_COMMAND_INTX_DISABLE); | ||||
| 
 | ||||
|     memset(dev->wmask + PCI_CONFIG_HEADER_SIZE, 0xff, | ||||
|            config_size - PCI_CONFIG_HEADER_SIZE); | ||||
| @ -499,7 +589,7 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, | ||||
|     pci_dev->bus = bus; | ||||
|     pci_dev->devfn = devfn; | ||||
|     pstrcpy(pci_dev->name, sizeof(pci_dev->name), name); | ||||
|     memset(pci_dev->irq_state, 0, sizeof(pci_dev->irq_state)); | ||||
|     pci_dev->irq_state = 0; | ||||
|     pci_config_alloc(pci_dev); | ||||
| 
 | ||||
|     header_type &= ~PCI_HEADER_TYPE_MULTI_FUNCTION; | ||||
| @ -849,6 +939,25 @@ static void pci_update_mappings(PCIDevice *d) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static inline int pci_irq_disabled(PCIDevice *d) | ||||
| { | ||||
|     return pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_INTX_DISABLE; | ||||
| } | ||||
| 
 | ||||
| /* Called after interrupt disabled field update in config space,
 | ||||
|  * assert/deassert interrupts if necessary. | ||||
|  * Gets original interrupt disable bit value (before update). */ | ||||
| static void pci_update_irq_disabled(PCIDevice *d, int was_irq_disabled) | ||||
| { | ||||
|     int i, disabled = pci_irq_disabled(d); | ||||
|     if (disabled == was_irq_disabled) | ||||
|         return; | ||||
|     for (i = 0; i < PCI_NUM_PINS; ++i) { | ||||
|         int state = pci_irq_state(d, i); | ||||
|         pci_change_irq_level(d, i, disabled ? -state : state); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| uint32_t pci_default_read_config(PCIDevice *d, | ||||
|                                  uint32_t address, int len) | ||||
| { | ||||
| @ -861,7 +970,7 @@ uint32_t pci_default_read_config(PCIDevice *d, | ||||
| 
 | ||||
| void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l) | ||||
| { | ||||
|     int i; | ||||
|     int i, was_irq_disabled = pci_irq_disabled(d); | ||||
|     uint32_t config_size = pci_config_size(d); | ||||
| 
 | ||||
|     for (i = 0; i < l && addr + i < config_size; val >>= 8, ++i) { | ||||
| @ -873,6 +982,9 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l) | ||||
|         ranges_overlap(addr, l, PCI_ROM_ADDRESS1, 4) || | ||||
|         range_covers_byte(addr, l, PCI_COMMAND)) | ||||
|         pci_update_mappings(d); | ||||
| 
 | ||||
|     if (range_covers_byte(addr, l, PCI_COMMAND)) | ||||
|         pci_update_irq_disabled(d, was_irq_disabled); | ||||
| } | ||||
| 
 | ||||
| /***********************************************************/ | ||||
| @ -882,23 +994,17 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l) | ||||
| static void pci_set_irq(void *opaque, int irq_num, int level) | ||||
| { | ||||
|     PCIDevice *pci_dev = opaque; | ||||
|     PCIBus *bus; | ||||
|     int change; | ||||
| 
 | ||||
|     change = level - pci_dev->irq_state[irq_num]; | ||||
|     change = level - pci_irq_state(pci_dev, irq_num); | ||||
|     if (!change) | ||||
|         return; | ||||
| 
 | ||||
|     pci_dev->irq_state[irq_num] = level; | ||||
|     for (;;) { | ||||
|         bus = pci_dev->bus; | ||||
|         irq_num = bus->map_irq(pci_dev, irq_num); | ||||
|         if (bus->set_irq) | ||||
|             break; | ||||
|         pci_dev = bus->parent_dev; | ||||
|     } | ||||
|     bus->irq_count[irq_num] += change; | ||||
|     bus->set_irq(bus->irq_opaque, irq_num, bus->irq_count[irq_num] != 0); | ||||
|     pci_set_irq_state(pci_dev, irq_num, level); | ||||
|     pci_update_irq_status(pci_dev); | ||||
|     if (pci_irq_disabled(pci_dev)) | ||||
|         return; | ||||
|     pci_change_irq_level(pci_dev, irq_num, change); | ||||
| } | ||||
| 
 | ||||
| /***********************************************************/ | ||||
|  | ||||
							
								
								
									
										4
									
								
								hw/pci.h
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								hw/pci.h
									
									
									
									
									
								
							| @ -101,7 +101,9 @@ typedef struct PCIIORegion { | ||||
| #define  PCI_COMMAND_IO		0x1	/* Enable response in I/O space */ | ||||
| #define  PCI_COMMAND_MEMORY	0x2	/* Enable response in Memory space */ | ||||
| #define  PCI_COMMAND_MASTER	0x4	/* Enable bus master */ | ||||
| #define  PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */ | ||||
| #define PCI_STATUS              0x06    /* 16 bits */ | ||||
| #define  PCI_STATUS_INTERRUPT   0x08 | ||||
| #define PCI_REVISION_ID         0x08    /* 8 bits  */ | ||||
| #define PCI_CLASS_PROG		0x09	/* Reg. Level Programming Interface */ | ||||
| #define PCI_CLASS_DEVICE        0x0a    /* Device class */ | ||||
| @ -220,7 +222,7 @@ struct PCIDevice { | ||||
|     qemu_irq *irq; | ||||
| 
 | ||||
|     /* Current IRQ levels.  Used internally by the generic PCI code.  */ | ||||
|     int irq_state[PCI_NUM_PINS]; | ||||
|     uint8_t irq_state; | ||||
| 
 | ||||
|     /* Capability bits */ | ||||
|     uint32_t cap_present; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Anthony Liguori
						Anthony Liguori