qemu/pci: make default_write_config use mask table
Subsystems (3)
A message that is not in the archive (sent off-list or not yet received): [email protected]
Michael S. Tsirkin
maintainerPATCH v6 1/12 qemu/pci: make default_write_config use mask table
Change much of hw/pci to use symbolic constants and a table-driven
design: add a mask table with writable bits set and readonly bits unset. Detect change by comparing original and new registers.
This makes it easy to support capabilities where read-only/writeable bit layout differs between devices, depending on capabilities present.
As a result, writing a single byte in BAR registers now works as it should. Writing to upper limit registers in the bridge also works as it should. Code is also shorter.
--- hw/pci.c | 145 ++++++++++++------------------------------------------------- hw/pci.h | 18 +++++++- 2 files changed, 46 insertions(+), 117 deletions(-)
hw/pci.c +29 -116
@@ -238,6 +238,17 @@ int pci_assign_devaddr(const char *addr, int *domp, int *busp, unsigned *slotp)return pci_parse_devaddr (devaddr, domp, busp, slotp); } +static void pci_init_wmask (PCIDevice *dev) +{ +int i; + dev->wmask[PCI_CACHE_LINE_SIZE ] =0xff ; + dev->wmask[PCI_INTERRUPT_LINE ] =0xff ; + dev->wmask[PCI_COMMAND ] =PCI_COMMAND_IO |PCI_COMMAND_MEMORY + |PCI_COMMAND_MASTER ; +for (i =PCI_CONFIG_HEADER_SIZE ; i <PCI_CONFIG_SPACE_SIZE ; ++i) + dev->wmask[i] =0xff ; +} +/* -1 for devfn means auto assign */ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, const char *name, int devfn,
@@ -257,6 +268,7 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,pstrcpy (pci_dev->name,sizeof (pci_dev->name), name);memset (pci_dev->irq_state,0 ,sizeof (pci_dev->irq_state));pci_set_default_subsystem_id (pci_dev); +pci_init_wmask (pci_dev);if (!config_read) config_read = pci_default_read_config;
@@ -328,6 +340,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, {PCIIORegion *r;uint32_t addr; +uint32_t wmask;if ((unsigned int )region_num >=PCI_NUM_REGIONS )return ;
@@ -343,12 +356,17 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, r->size = size; r->type = type; r->map_func = map_func; + + wmask = ~(size -1 );if (region_num ==PCI_ROM_SLOT ) { addr =0x30 ; +/* ROM enable bit is writeable */ + wmask |=1 ; }else { addr =0x10 + region_num *4 ; } *(uint32_t *)(pci_dev->config + addr) =cpu_to_le32 (type); + *(uint32_t *)(pci_dev->wmask + addr) =cpu_to_le32 (wmask); }static void pci_update_mappings (PCIDevice *d)
@@ -457,118 +475,21 @@ uint32_t pci_default_read_config(PCIDevice *d,return val; } -void pci_default_write_config (PCIDevice *d, -uint32_t address,uint32_t val,int len) +void pci_default_write_config (PCIDevice *d,uint32_t addr,uint32_t val,int l) { -int can_write, i; -uint32_t end, addr; - -if (len ==4 && ((address >=0x10 && address <0x10 +4 *6 ) || - (address >=0x30 && address <0x34 ))) { -PCIIORegion *r; -int reg; +uint8_t orig[PCI_CONFIG_SPACE_SIZE ]; +int i; -if ( address >=0x30 ) { - reg =PCI_ROM_SLOT ; - }else { - reg = (address -0x10 ) >>2 ; - } - r = &d->io_regions[reg]; -if (r->size ==0 ) - goto default_config; -/* compute the stored value */ -if (reg ==PCI_ROM_SLOT ) { -/* keep ROM enable bit */ - val &= (~(r->size -1 )) |1 ; - }else { - val &= ~(r->size -1 ); - val |= r->type; - } - *(uint32_t *)(d->config + address) =cpu_to_le32 (val); -pci_update_mappings (d); -return ; - } - default_config:/* not efficient, but simple */ - addr = address; -for (i =0 ; i < len; i++) { -/* default read/write accesses */ -switch (d->config[0x0e ]) { -case 0x00 : -case 0x80 : -switch (addr) { -case 0x00 : -case 0x01 : -case 0x02 : -case 0x03 : -case 0x06 : -case 0x07 : -case 0x08 : -case 0x09 : -case 0x0a : -case 0x0b : -case 0x0e : - case 0x10 ... 0x27: /* base */ - case 0x2c ... 0x2f: /* read-only subsystem ID & vendor ID */ - case 0x30 ... 0x33: /* rom */ -case 0x3d : - can_write =0 ; -break ; -default : - can_write =1 ; -break ; - } -break ; -default : -case 0x01 : -switch (addr) { -case 0x00 : -case 0x01 : -case 0x02 : -case 0x03 : -case 0x06 : -case 0x07 : -case 0x08 : -case 0x09 : -case 0x0a : -case 0x0b : -case 0x0e : - case 0x2c ... 0x2f: /* read-only subsystem ID & vendor ID */ - case 0x38 ... 0x3b: /* rom */ -case 0x3d : - can_write =0 ; -break ; -default : - can_write =1 ; -break ; - } -break ; - } -if (can_write) { -/* Mask out writes to reserved bits in registers */ -switch (addr) { -case 0x05 : - val &= ~PCI_COMMAND_RESERVED_MASK_HI ; -break ; -case 0x06 : - val &= ~PCI_STATUS_RESERVED_MASK_LO ; -break ; -case 0x07 : - val &= ~PCI_STATUS_RESERVED_MASK_HI ; -break ; - } - d->config[addr] = val; - } -if (++addr >0xff ) -break ; - val >>=8 ; +memcpy (orig, d->config,PCI_CONFIG_SPACE_SIZE ); +for (i =0 ; i < l && addr <PCI_CONFIG_SPACE_SIZE ; val >>=8 , ++i, ++addr) { +uint8_t wmask = d->wmask[addr]; + d->config[addr] = (d->config[addr] & ~wmask) | (val & wmask); } - - end = address + len; -if (end >PCI_COMMAND && address < (PCI_COMMAND +2 )) { -/* if the command register is modified, we must modify the mappings */ +if (memcmp (orig +PCI_BASE_ADDRESS_0 , d->config +PCI_BASE_ADDRESS_0 ,24 ) + || ((orig[PCI_COMMAND ] ^ d->config[PCI_COMMAND ]) + & (PCI_COMMAND_MEMORY |PCI_COMMAND_IO )))pci_update_mappings (d); - } }void pci_data_write (void *opaque,uint32_t addr,uint32_t val,int len)
@@ -841,16 +762,8 @@ static void pci_bridge_write_config(PCIDevice *d, {PCIBridge *s = (PCIBridge *)d; -if (address ==0x19 || (address ==0x18 && len >1 )) { -if (address ==0x19 ) - s->bus->bus_num = val &0xff ; -else - s->bus->bus_num = (val >>8 ) &0xff ; -#if defined(DEBUG_PCI ) -printf ("pci-bridge: %s: Assigned bus %d\n" , d->name, s->bus->bus_num); -#endif - }pci_default_write_config (d, address, val, len); + s->bus->bus_num = d->config[PCI_SECONDARY_BUS ]; }PCIBus *pci_find_bus (int bus_num)
hw/pci.h +17 -1
@@ -98,16 +98,24 @@ typedef struct PCIIORegion {#define PCI_COMMAND 0x04/* 16 bits */ #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_STATUS 0x06/* 16 bits */ #define PCI_REVISION_ID 0x08/* 8 bits */ #define PCI_CLASS_DEVICE 0x0a/* Device class */ +#define PCI_CACHE_LINE_SIZE 0x0c/* 8 bits */ +#define PCI_LATENCY_TIMER 0x0d/* 8 bits */ #define PCI_HEADER_TYPE 0x0e/* 8 bits */ #define PCI_HEADER_TYPE_NORMAL 0#define PCI_HEADER_TYPE_BRIDGE 1#define PCI_HEADER_TYPE_CARDBUS 2#define PCI_HEADER_TYPE_MULTI_FUNCTION 0x80 +#define PCI_BASE_ADDRESS_0 0x10/* 32 bits */ +#define PCI_PRIMARY_BUS 0x18/* Primary bus number */ +#define PCI_SECONDARY_BUS 0x19/* Secondary bus number */ +#define PCI_SEC_STATUS 0x1e/* Secondary status register, only bit 14 used */ #define PCI_SUBSYSTEM_VENDOR_ID 0x2c/* 16 bits */ #define PCI_SUBSYSTEM_ID 0x2e/* 16 bits */ +#define PCI_CAPABILITY_LIST 0x34/* Offset of first capability list entry */ #define PCI_INTERRUPT_LINE 0x3c/* 8 bits */ #define PCI_INTERRUPT_PIN 0x3d/* 8 bits */ #define PCI_MIN_GNT 0x3e/* 8 bits */
@@ -137,10 +145,18 @@ typedef struct PCIIORegion {#define PCI_COMMAND_RESERVED_MASK_HI (PCI_COMMAND_RESERVED >> 8) +/* Size of the standard PCI config header */ +#define PCI_CONFIG_HEADER_SIZE 0x40 +/* Size of the standard PCI config space */ +#define PCI_CONFIG_SPACE_SIZE 0x100 +struct PCIDevice {DeviceState qdev;/* PCI config space */ -uint8_t config[256 ]; +uint8_t config[PCI_CONFIG_SPACE_SIZE ]; + +/* Used to implement R/W bytes */ +uint8_t wmask[PCI_CONFIG_SPACE_SIZE ];/* the following fields are read only */ PCIBus *bus;
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 1/12 qemu/pci: make default_write_config use mask table
In reply to a message not in the archiveChange much of hw/pci to use symbolic constants and a table-driven
design: add a mask table with writable bits set and readonly bits unset. Detect change by comparing original and new registers.
This makes it easy to support capabilities where read-only/writeable bit layout differs between devices, depending on capabilities present.
As a result, writing a single byte in BAR registers now works as it should. Writing to upper limit registers in the bridge also works as it should. Code is also shorter.
--- hw/pci.c | 145 ++++++++++++------------------------------------------------- hw/pci.h | 18 +++++++- 2 files changed, 46 insertions(+), 117 deletions(-)
hw/pci.c +29 -116
@@ -238,6 +238,17 @@ int pci_assign_devaddr(const char *addr, int *domp, int *busp, unsigned *slotp)return pci_parse_devaddr (devaddr, domp, busp, slotp); } +static void pci_init_wmask (PCIDevice *dev) +{ +int i; + dev->wmask[PCI_CACHE_LINE_SIZE ] =0xff ; + dev->wmask[PCI_INTERRUPT_LINE ] =0xff ; + dev->wmask[PCI_COMMAND ] =PCI_COMMAND_IO |PCI_COMMAND_MEMORY + |PCI_COMMAND_MASTER ; +for (i =PCI_CONFIG_HEADER_SIZE ; i <PCI_CONFIG_SPACE_SIZE ; ++i) + dev->wmask[i] =0xff ; +} +/* -1 for devfn means auto assign */ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, const char *name, int devfn,
@@ -257,6 +268,7 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,pstrcpy (pci_dev->name,sizeof (pci_dev->name), name);memset (pci_dev->irq_state,0 ,sizeof (pci_dev->irq_state));pci_set_default_subsystem_id (pci_dev); +pci_init_wmask (pci_dev);if (!config_read) config_read = pci_default_read_config;
@@ -328,6 +340,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, {PCIIORegion *r;uint32_t addr; +uint32_t wmask;if ((unsigned int )region_num >=PCI_NUM_REGIONS )return ;
@@ -343,12 +356,17 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, r->size = size; r->type = type; r->map_func = map_func; + + wmask = ~(size -1 );if (region_num ==PCI_ROM_SLOT ) { addr =0x30 ; +/* ROM enable bit is writeable */ + wmask |=1 ; }else { addr =0x10 + region_num *4 ; } *(uint32_t *)(pci_dev->config + addr) =cpu_to_le32 (type); + *(uint32_t *)(pci_dev->wmask + addr) =cpu_to_le32 (wmask); }static void pci_update_mappings (PCIDevice *d)
@@ -457,118 +475,21 @@ uint32_t pci_default_read_config(PCIDevice *d,return val; } -void pci_default_write_config (PCIDevice *d, -uint32_t address,uint32_t val,int len) +void pci_default_write_config (PCIDevice *d,uint32_t addr,uint32_t val,int l) { -int can_write, i; -uint32_t end, addr; - -if (len ==4 && ((address >=0x10 && address <0x10 +4 *6 ) || - (address >=0x30 && address <0x34 ))) { -PCIIORegion *r; -int reg; +uint8_t orig[PCI_CONFIG_SPACE_SIZE ]; +int i; -if ( address >=0x30 ) { - reg =PCI_ROM_SLOT ; - }else { - reg = (address -0x10 ) >>2 ; - } - r = &d->io_regions[reg]; -if (r->size ==0 ) - goto default_config; -/* compute the stored value */ -if (reg ==PCI_ROM_SLOT ) { -/* keep ROM enable bit */ - val &= (~(r->size -1 )) |1 ; - }else { - val &= ~(r->size -1 ); - val |= r->type; - } - *(uint32_t *)(d->config + address) =cpu_to_le32 (val); -pci_update_mappings (d); -return ; - } - default_config:/* not efficient, but simple */ - addr = address; -for (i =0 ; i < len; i++) { -/* default read/write accesses */ -switch (d->config[0x0e ]) { -case 0x00 : -case 0x80 : -switch (addr) { -case 0x00 : -case 0x01 : -case 0x02 : -case 0x03 : -case 0x06 : -case 0x07 : -case 0x08 : -case 0x09 : -case 0x0a : -case 0x0b : -case 0x0e : - case 0x10 ... 0x27: /* base */ - case 0x2c ... 0x2f: /* read-only subsystem ID & vendor ID */ - case 0x30 ... 0x33: /* rom */ -case 0x3d : - can_write =0 ; -break ; -default : - can_write =1 ; -break ; - } -break ; -default : -case 0x01 : -switch (addr) { -case 0x00 : -case 0x01 : -case 0x02 : -case 0x03 : -case 0x06 : -case 0x07 : -case 0x08 : -case 0x09 : -case 0x0a : -case 0x0b : -case 0x0e : - case 0x2c ... 0x2f: /* read-only subsystem ID & vendor ID */ - case 0x38 ... 0x3b: /* rom */ -case 0x3d : - can_write =0 ; -break ; -default : - can_write =1 ; -break ; - } -break ; - } -if (can_write) { -/* Mask out writes to reserved bits in registers */ -switch (addr) { -case 0x05 : - val &= ~PCI_COMMAND_RESERVED_MASK_HI ; -break ; -case 0x06 : - val &= ~PCI_STATUS_RESERVED_MASK_LO ; -break ; -case 0x07 : - val &= ~PCI_STATUS_RESERVED_MASK_HI ; -break ; - } - d->config[addr] = val; - } -if (++addr >0xff ) -break ; - val >>=8 ; +memcpy (orig, d->config,PCI_CONFIG_SPACE_SIZE ); +for (i =0 ; i < l && addr <PCI_CONFIG_SPACE_SIZE ; val >>=8 , ++i, ++addr) { +uint8_t wmask = d->wmask[addr]; + d->config[addr] = (d->config[addr] & ~wmask) | (val & wmask); } - - end = address + len; -if (end >PCI_COMMAND && address < (PCI_COMMAND +2 )) { -/* if the command register is modified, we must modify the mappings */ +if (memcmp (orig +PCI_BASE_ADDRESS_0 , d->config +PCI_BASE_ADDRESS_0 ,24 ) + || ((orig[PCI_COMMAND ] ^ d->config[PCI_COMMAND ]) + & (PCI_COMMAND_MEMORY |PCI_COMMAND_IO )))pci_update_mappings (d); - } }void pci_data_write (void *opaque,uint32_t addr,uint32_t val,int len)
@@ -841,16 +762,8 @@ static void pci_bridge_write_config(PCIDevice *d, {PCIBridge *s = (PCIBridge *)d; -if (address ==0x19 || (address ==0x18 && len >1 )) { -if (address ==0x19 ) - s->bus->bus_num = val &0xff ; -else - s->bus->bus_num = (val >>8 ) &0xff ; -#if defined(DEBUG_PCI ) -printf ("pci-bridge: %s: Assigned bus %d\n" , d->name, s->bus->bus_num); -#endif - }pci_default_write_config (d, address, val, len); + s->bus->bus_num = d->config[PCI_SECONDARY_BUS ]; }PCIBus *pci_find_bus (int bus_num)
hw/pci.h +17 -1
@@ -98,16 +98,24 @@ typedef struct PCIIORegion {#define PCI_COMMAND 0x04/* 16 bits */ #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_STATUS 0x06/* 16 bits */ #define PCI_REVISION_ID 0x08/* 8 bits */ #define PCI_CLASS_DEVICE 0x0a/* Device class */ +#define PCI_CACHE_LINE_SIZE 0x0c/* 8 bits */ +#define PCI_LATENCY_TIMER 0x0d/* 8 bits */ #define PCI_HEADER_TYPE 0x0e/* 8 bits */ #define PCI_HEADER_TYPE_NORMAL 0#define PCI_HEADER_TYPE_BRIDGE 1#define PCI_HEADER_TYPE_CARDBUS 2#define PCI_HEADER_TYPE_MULTI_FUNCTION 0x80 +#define PCI_BASE_ADDRESS_0 0x10/* 32 bits */ +#define PCI_PRIMARY_BUS 0x18/* Primary bus number */ +#define PCI_SECONDARY_BUS 0x19/* Secondary bus number */ +#define PCI_SEC_STATUS 0x1e/* Secondary status register, only bit 14 used */ #define PCI_SUBSYSTEM_VENDOR_ID 0x2c/* 16 bits */ #define PCI_SUBSYSTEM_ID 0x2e/* 16 bits */ +#define PCI_CAPABILITY_LIST 0x34/* Offset of first capability list entry */ #define PCI_INTERRUPT_LINE 0x3c/* 8 bits */ #define PCI_INTERRUPT_PIN 0x3d/* 8 bits */ #define PCI_MIN_GNT 0x3e/* 8 bits */
@@ -137,10 +145,18 @@ typedef struct PCIIORegion {#define PCI_COMMAND_RESERVED_MASK_HI (PCI_COMMAND_RESERVED >> 8) +/* Size of the standard PCI config header */ +#define PCI_CONFIG_HEADER_SIZE 0x40 +/* Size of the standard PCI config space */ +#define PCI_CONFIG_SPACE_SIZE 0x100 +struct PCIDevice {DeviceState qdev;/* PCI config space */ -uint8_t config[256 ]; +uint8_t config[PCI_CONFIG_SPACE_SIZE ]; + +/* Used to implement R/W bytes */ +uint8_t wmask[PCI_CONFIG_SPACE_SIZE ];/* the following fields are read only */ PCIBus *bus;
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 2/12 qemu/pci: helper routines for pci access
In reply to a message not in the archiveAdd inline routines for convenient access to pci devices with correct (little) endianness. Will be used by MSI-X support.
--- hw/pci.h | 30 +++++++++++++++++++++++++++--- 1 files changed, 27 insertions(+), 3 deletions(-)
hw/pci.h +27 -3
@@ -216,21 +216,45 @@ PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did, pci_map_irq_fn map_irq, const char *name);static inline void +pci_set_word (uint8_t *config,uint16_t val) +{ +cpu_to_le16wu ((uint16_t *)config, val); +} + +static inline uint16_t +pci_get_word (uint8_t *config) +{ +return le16_to_cpupu ((uint16_t *)config); +} + +static inline void +pci_set_long (uint8_t *config,uint32_t val) +{ +cpu_to_le32wu ((uint32_t *)config, val); +} + +static inline uint32_t +pci_get_long (uint8_t *config) +{ +return le32_to_cpupu ((uint32_t *)config); +} + +static inline void pci_config_set_vendor_id (uint8_t *pci_config,uint16_t val) { -cpu_to_le16wu ((uint16_t *)&pci_config[PCI_VENDOR_ID ], val); +pci_set_word (&pci_config[PCI_VENDOR_ID ], val); }static inline void pci_config_set_device_id (uint8_t *pci_config,uint16_t val) { -cpu_to_le16wu ((uint16_t *)&pci_config[PCI_DEVICE_ID ], val); +pci_set_word (&pci_config[PCI_DEVICE_ID ], val); }static inline void pci_config_set_class (uint8_t *pci_config,uint16_t val) { -cpu_to_le16wu ((uint16_t *)&pci_config[PCI_CLASS_DEVICE ], val); +pci_set_word (&pci_config[PCI_CLASS_DEVICE ], val); }typedef void (*pci_qdev_initfn )(PCIDevice *dev);
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 2/12 qemu/pci: helper routines for pci access
In reply to a message not in the archiveAdd inline routines for convenient access to pci devices with correct (little) endianness. Will be used by MSI-X support.
--- hw/pci.h | 30 +++++++++++++++++++++++++++--- 1 files changed, 27 insertions(+), 3 deletions(-)
hw/pci.h +27 -3
@@ -216,21 +216,45 @@ PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did, pci_map_irq_fn map_irq, const char *name);static inline void +pci_set_word (uint8_t *config,uint16_t val) +{ +cpu_to_le16wu ((uint16_t *)config, val); +} + +static inline uint16_t +pci_get_word (uint8_t *config) +{ +return le16_to_cpupu ((uint16_t *)config); +} + +static inline void +pci_set_long (uint8_t *config,uint32_t val) +{ +cpu_to_le32wu ((uint32_t *)config, val); +} + +static inline uint32_t +pci_get_long (uint8_t *config) +{ +return le32_to_cpupu ((uint32_t *)config); +} + +static inline void pci_config_set_vendor_id (uint8_t *pci_config,uint16_t val) { -cpu_to_le16wu ((uint16_t *)&pci_config[PCI_VENDOR_ID ], val); +pci_set_word (&pci_config[PCI_VENDOR_ID ], val); }static inline void pci_config_set_device_id (uint8_t *pci_config,uint16_t val) { -cpu_to_le16wu ((uint16_t *)&pci_config[PCI_DEVICE_ID ], val); +pci_set_word (&pci_config[PCI_DEVICE_ID ], val); }static inline void pci_config_set_class (uint8_t *pci_config,uint16_t val) { -cpu_to_le16wu ((uint16_t *)&pci_config[PCI_CLASS_DEVICE ], val); +pci_set_word (&pci_config[PCI_CLASS_DEVICE ], val); }typedef void (*pci_qdev_initfn )(PCIDevice *dev);
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 3/12 qemu/pci: add routines to manage PCI capabilities
In reply to a message not in the archiveAdd routines to manage PCI capability list. First user will be MSI-X.
--- hw/pci.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- hw/pci.h | 18 ++++++++++++++- 2 files changed, 90 insertions(+), 2 deletions(-)
hw/pci.c +73 -1
@@ -148,7 +148,6 @@ int pci_device_load(PCIDevice *s, QEMUFile *f)if (version_id >=2 )for (i =0 ; i <4 ; i ++) s->irq_state[i] =qemu_get_be32 (f); -return 0 ; }
@@ -856,3 +855,76 @@ PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name)return (PCIDevice *)dev; } + +static int pci_find_space (PCIDevice *pdev,uint8_t size) +{ +int offset =PCI_CONFIG_HEADER_SIZE ; +int i; +for (i =PCI_CONFIG_HEADER_SIZE ; i <PCI_CONFIG_SPACE_SIZE ; ++i) +if (pdev->used[i]) + offset = i +1 ; +else if (i - offset +1 == size) +return offset; +return 0 ; +} + +static uint8_t pci_find_capability_list (PCIDevice *pdev,uint8_t cap_id, +uint8_t *prev_p) +{ +uint8_t next, prev; + +if (!(pdev->config[PCI_STATUS ] &PCI_STATUS_CAP_LIST )) +return 0 ; + +for (prev =PCI_CAPABILITY_LIST ; (next = pdev->config[prev]); + prev = next +PCI_CAP_LIST_NEXT ) +if (pdev->config[next +PCI_CAP_LIST_ID ] == cap_id) +break ; + +if (prev_p) + *prev_p = prev; +return next; +} + +/* Reserve space and add capability to the linked list in pci config space */ +int pci_add_capability (PCIDevice *pdev,uint8_t cap_id,uint8_t size) +{ +uint8_t offset =pci_find_space (pdev, size); +uint8_t *config = pdev->config + offset; +if (!offset) +return -ENOSPC ; + config[PCI_CAP_LIST_ID ] = cap_id; + config[PCI_CAP_LIST_NEXT ] = pdev->config[PCI_CAPABILITY_LIST ]; + pdev->config[PCI_CAPABILITY_LIST ] = offset; + pdev->config[PCI_STATUS ] |=PCI_STATUS_CAP_LIST ; +memset (pdev->used + offset,0xFF , size); +/* Make capability read-only by default */ +memset (pdev->wmask + offset,0 , size); +return offset; +} + +/* Unlink capability from the pci config space. */ +void pci_del_capability (PCIDevice *pdev,uint8_t cap_id,uint8_t size) +{ +uint8_t prev, offset =pci_find_capability_list (pdev, cap_id, &prev); +if (!offset) +return ; + pdev->config[prev] = pdev->config[offset +PCI_CAP_LIST_NEXT ]; +/* Make capability writeable again */ +memset (pdev->wmask + offset,0xff , size); +memset (pdev->used + offset,0 , size); + +if (!pdev->config[PCI_CAPABILITY_LIST ]) + pdev->config[PCI_STATUS ] &= ~PCI_STATUS_CAP_LIST ; +} + +/* Reserve space for capability at a known offset (to call after load). */ +void pci_reserve_capability (PCIDevice *pdev,uint8_t offset,uint8_t size) +{ +memset (pdev->used + offset,0xff , size); +} + +uint8_t pci_find_capability (PCIDevice *pdev,uint8_t cap_id) +{ +return pci_find_capability_list (pdev, cap_id,NULL ); +}
hw/pci.h +17 -1
@@ -121,6 +121,10 @@ typedef struct PCIIORegion {#define PCI_MIN_GNT 0x3e/* 8 bits */ #define PCI_MAX_LAT 0x3f/* 8 bits */ +/* Capability lists */ +#define PCI_CAP_LIST_ID 0/* Capability ID */ +#define PCI_CAP_LIST_NEXT 1/* Next capability in the list */ +#define PCI_REVISION 0x08/* obsolete, use PCI_REVISION_ID */ #define PCI_SUBVENDOR_ID 0x2c/* obsolete, use PCI_SUBSYSTEM_VENDOR_ID */ #define PCI_SUBDEVICE_ID 0x2e/* obsolete, use PCI_SUBSYSTEM_ID */
@@ -128,7 +132,7 @@ typedef struct PCIIORegion {/* Bits in the PCI Status Register (PCI 2.3 spec) */ #define PCI_STATUS_RESERVED1 0x007#define PCI_STATUS_INT_STATUS 0x008 -#define PCI_STATUS_CAPABILITIES 0x010 +#define PCI_STATUS_CAP_LIST 0x010#define PCI_STATUS_66MHZ 0x020#define PCI_STATUS_RESERVED2 0x040#define PCI_STATUS_FAST_BACK 0x080
@@ -158,6 +162,9 @@ struct PCIDevice {/* Used to implement R/W bytes */ uint8_t wmask[PCI_CONFIG_SPACE_SIZE ]; +/* Used to allocate config space for capabilities. */ +uint8_t used[PCI_CONFIG_SPACE_SIZE ]; +/* the following fields are read only */ PCIBus *bus;int devfn;
@@ -186,6 +193,15 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, uint32_t size, int type, PCIMapIORegionFunc *map_func); +int pci_add_capability (PCIDevice *pci_dev,uint8_t cap_id,uint8_t cap_size); + +void pci_del_capability (PCIDevice *pci_dev,uint8_t cap_id,uint8_t cap_size); + +void pci_reserve_capability (PCIDevice *pci_dev,uint8_t offset,uint8_t size); + +uint8_t pci_find_capability (PCIDevice *pci_dev,uint8_t cap_id); + +uint32_t pci_default_read_config (PCIDevice *d,uint32_t address,int len); void pci_default_write_config(PCIDevice *d,
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 3/12 qemu/pci: add routines to manage PCI capabilities
In reply to a message not in the archiveAdd routines to manage PCI capability list. First user will be MSI-X.
--- hw/pci.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- hw/pci.h | 18 ++++++++++++++- 2 files changed, 90 insertions(+), 2 deletions(-)
hw/pci.c +73 -1
@@ -148,7 +148,6 @@ int pci_device_load(PCIDevice *s, QEMUFile *f)if (version_id >=2 )for (i =0 ; i <4 ; i ++) s->irq_state[i] =qemu_get_be32 (f); -return 0 ; }
@@ -856,3 +855,76 @@ PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name)return (PCIDevice *)dev; } + +static int pci_find_space (PCIDevice *pdev,uint8_t size) +{ +int offset =PCI_CONFIG_HEADER_SIZE ; +int i; +for (i =PCI_CONFIG_HEADER_SIZE ; i <PCI_CONFIG_SPACE_SIZE ; ++i) +if (pdev->used[i]) + offset = i +1 ; +else if (i - offset +1 == size) +return offset; +return 0 ; +} + +static uint8_t pci_find_capability_list (PCIDevice *pdev,uint8_t cap_id, +uint8_t *prev_p) +{ +uint8_t next, prev; + +if (!(pdev->config[PCI_STATUS ] &PCI_STATUS_CAP_LIST )) +return 0 ; + +for (prev =PCI_CAPABILITY_LIST ; (next = pdev->config[prev]); + prev = next +PCI_CAP_LIST_NEXT ) +if (pdev->config[next +PCI_CAP_LIST_ID ] == cap_id) +break ; + +if (prev_p) + *prev_p = prev; +return next; +} + +/* Reserve space and add capability to the linked list in pci config space */ +int pci_add_capability (PCIDevice *pdev,uint8_t cap_id,uint8_t size) +{ +uint8_t offset =pci_find_space (pdev, size); +uint8_t *config = pdev->config + offset; +if (!offset) +return -ENOSPC ; + config[PCI_CAP_LIST_ID ] = cap_id; + config[PCI_CAP_LIST_NEXT ] = pdev->config[PCI_CAPABILITY_LIST ]; + pdev->config[PCI_CAPABILITY_LIST ] = offset; + pdev->config[PCI_STATUS ] |=PCI_STATUS_CAP_LIST ; +memset (pdev->used + offset,0xFF , size); +/* Make capability read-only by default */ +memset (pdev->wmask + offset,0 , size); +return offset; +} + +/* Unlink capability from the pci config space. */ +void pci_del_capability (PCIDevice *pdev,uint8_t cap_id,uint8_t size) +{ +uint8_t prev, offset =pci_find_capability_list (pdev, cap_id, &prev); +if (!offset) +return ; + pdev->config[prev] = pdev->config[offset +PCI_CAP_LIST_NEXT ]; +/* Make capability writeable again */ +memset (pdev->wmask + offset,0xff , size); +memset (pdev->used + offset,0 , size); + +if (!pdev->config[PCI_CAPABILITY_LIST ]) + pdev->config[PCI_STATUS ] &= ~PCI_STATUS_CAP_LIST ; +} + +/* Reserve space for capability at a known offset (to call after load). */ +void pci_reserve_capability (PCIDevice *pdev,uint8_t offset,uint8_t size) +{ +memset (pdev->used + offset,0xff , size); +} + +uint8_t pci_find_capability (PCIDevice *pdev,uint8_t cap_id) +{ +return pci_find_capability_list (pdev, cap_id,NULL ); +}
hw/pci.h +17 -1
@@ -121,6 +121,10 @@ typedef struct PCIIORegion {#define PCI_MIN_GNT 0x3e/* 8 bits */ #define PCI_MAX_LAT 0x3f/* 8 bits */ +/* Capability lists */ +#define PCI_CAP_LIST_ID 0/* Capability ID */ +#define PCI_CAP_LIST_NEXT 1/* Next capability in the list */ +#define PCI_REVISION 0x08/* obsolete, use PCI_REVISION_ID */ #define PCI_SUBVENDOR_ID 0x2c/* obsolete, use PCI_SUBSYSTEM_VENDOR_ID */ #define PCI_SUBDEVICE_ID 0x2e/* obsolete, use PCI_SUBSYSTEM_ID */
@@ -128,7 +132,7 @@ typedef struct PCIIORegion {/* Bits in the PCI Status Register (PCI 2.3 spec) */ #define PCI_STATUS_RESERVED1 0x007#define PCI_STATUS_INT_STATUS 0x008 -#define PCI_STATUS_CAPABILITIES 0x010 +#define PCI_STATUS_CAP_LIST 0x010#define PCI_STATUS_66MHZ 0x020#define PCI_STATUS_RESERVED2 0x040#define PCI_STATUS_FAST_BACK 0x080
@@ -158,6 +162,9 @@ struct PCIDevice {/* Used to implement R/W bytes */ uint8_t wmask[PCI_CONFIG_SPACE_SIZE ]; +/* Used to allocate config space for capabilities. */ +uint8_t used[PCI_CONFIG_SPACE_SIZE ]; +/* the following fields are read only */ PCIBus *bus;int devfn;
@@ -186,6 +193,15 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, uint32_t size, int type, PCIMapIORegionFunc *map_func); +int pci_add_capability (PCIDevice *pci_dev,uint8_t cap_id,uint8_t cap_size); + +void pci_del_capability (PCIDevice *pci_dev,uint8_t cap_id,uint8_t cap_size); + +void pci_reserve_capability (PCIDevice *pci_dev,uint8_t offset,uint8_t size); + +uint8_t pci_find_capability (PCIDevice *pci_dev,uint8_t cap_id); + +uint32_t pci_default_read_config (PCIDevice *d,uint32_t address,int len); void pci_default_write_config(PCIDevice *d,
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 4/12 qemu/pci: check constant registers on load
In reply to a message not in the archiveAdd "cmask" table of constant register masks: if a bit is not writeable and is set in cmask table, this bit is checked on load. An attempt to load an image that would change such a register causes load to fail. Use this table to make sure that load does not modify registers that guest can not change (directly or indirectly).
Note: we can't just assume that read-only registers never change, because the guest could change a register indirectly.
--- hw/pci.c | 26 +++++++++++++++++++++++++- hw/pci.h | 5 +++++ 2 files changed, 30 insertions(+), 1 deletions(-)
hw/pci.c +25 -1
@@ -136,13 +136,19 @@ void pci_device_save(PCIDevice *s, QEMUFile *f)int pci_device_load (PCIDevice *s,QEMUFile *f) { +uint8_t config[PCI_CONFIG_SPACE_SIZE ];uint32_t version_id;int i; version_id =qemu_get_be32 (f);if (version_id >2 )return -EINVAL ; -qemu_get_buffer (f, s->config,256 ); +qemu_get_buffer (f, config,sizeof config); +for (i =0 ; i <sizeof config; ++i) +if ((config[i] ^ s->config[i]) & s->cmask[i] & ~s->wmask[i]) +return -EINVAL ; +memcpy (s->config, config,sizeof config); +pci_update_mappings (s);if (version_id >=2 )
@@ -237,6 +243,18 @@ int pci_assign_devaddr(const char *addr, int *domp, int *busp, unsigned *slotp)return pci_parse_devaddr (devaddr, domp, busp, slotp); } +static void pci_init_cmask (PCIDevice *dev) +{ +pci_set_word (dev->cmask +PCI_VENDOR_ID ,0xffff ); +pci_set_word (dev->cmask +PCI_DEVICE_ID ,0xffff ); + dev->cmask[PCI_STATUS ] =PCI_STATUS_CAP_LIST ; + dev->cmask[PCI_REVISION_ID ] =0xff ; + dev->cmask[PCI_CLASS_PROG ] =0xff ; +pci_set_word (dev->cmask +PCI_CLASS_DEVICE ,0xffff ); + dev->cmask[PCI_HEADER_TYPE ] =0xff ; + dev->cmask[PCI_CAPABILITY_LIST ] =0xff ; +} +static void pci_init_wmask (PCIDevice *dev) {int i;
@@ -267,6 +285,7 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,pstrcpy (pci_dev->name,sizeof (pci_dev->name), name);memset (pci_dev->irq_state,0 ,sizeof (pci_dev->irq_state));pci_set_default_subsystem_id (pci_dev); +pci_init_cmask (pci_dev);pci_init_wmask (pci_dev);if (!config_read)
@@ -366,6 +385,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, } *(uint32_t *)(pci_dev->config + addr) =cpu_to_le32 (type); *(uint32_t *)(pci_dev->wmask + addr) =cpu_to_le32 (wmask); + *(uint32_t *)(pci_dev->cmask + addr) =0xffffffff ; }static void pci_update_mappings (PCIDevice *d)
@@ -900,6 +920,8 @@ int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size)memset (pdev->used + offset,0xFF , size);/* Make capability read-only by default */ memset (pdev->wmask + offset,0 , size); +/* Check capability by default */ +memset (pdev->cmask + offset,0xFF , size);return offset; }
@@ -912,6 +934,8 @@ void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size) pdev->config[prev] = pdev->config[offset +PCI_CAP_LIST_NEXT ];/* Make capability writeable again */ memset (pdev->wmask + offset,0xff , size); +/* Clear cmask as device-specific registers can't be checked */ +memset (pdev->cmask + offset,0 , size);memset (pdev->used + offset,0 , size);if (!pdev->config[PCI_CAPABILITY_LIST ])
hw/pci.h +5 -0
@@ -101,6 +101,7 @@ typedef struct PCIIORegion {#define PCI_COMMAND_MASTER 0x4/* Enable bus master */ #define PCI_STATUS 0x06/* 16 bits */ #define PCI_REVISION_ID 0x08/* 8 bits */ +#define PCI_CLASS_PROG 0x09/* Reg. Level Programming Interface */ #define PCI_CLASS_DEVICE 0x0a/* Device class */ #define PCI_CACHE_LINE_SIZE 0x0c/* 8 bits */ #define PCI_LATENCY_TIMER 0x0d/* 8 bits */
@@ -159,6 +160,10 @@ struct PCIDevice {/* PCI config space */ uint8_t config[PCI_CONFIG_SPACE_SIZE ]; +/* Used to enable config checks on load. Note that writeable bits are +* never checked even if set in cmask. */ +uint8_t cmask[PCI_CONFIG_SPACE_SIZE ]; +/* Used to implement R/W bytes */ uint8_t wmask[PCI_CONFIG_SPACE_SIZE ];
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 4/12 qemu/pci: check constant registers on load
In reply to a message not in the archiveAdd "cmask" table of constant register masks: if a bit is not writeable and is set in cmask table, this bit is checked on load. An attempt to load an image that would change such a register causes load to fail. Use this table to make sure that load does not modify registers that guest can not change (directly or indirectly).
Note: we can't just assume that read-only registers never change, because the guest could change a register indirectly.
--- hw/pci.c | 26 +++++++++++++++++++++++++- hw/pci.h | 5 +++++ 2 files changed, 30 insertions(+), 1 deletions(-)
hw/pci.c +25 -1
@@ -136,13 +136,19 @@ void pci_device_save(PCIDevice *s, QEMUFile *f)int pci_device_load (PCIDevice *s,QEMUFile *f) { +uint8_t config[PCI_CONFIG_SPACE_SIZE ];uint32_t version_id;int i; version_id =qemu_get_be32 (f);if (version_id >2 )return -EINVAL ; -qemu_get_buffer (f, s->config,256 ); +qemu_get_buffer (f, config,sizeof config); +for (i =0 ; i <sizeof config; ++i) +if ((config[i] ^ s->config[i]) & s->cmask[i] & ~s->wmask[i]) +return -EINVAL ; +memcpy (s->config, config,sizeof config); +pci_update_mappings (s);if (version_id >=2 )
@@ -237,6 +243,18 @@ int pci_assign_devaddr(const char *addr, int *domp, int *busp, unsigned *slotp)return pci_parse_devaddr (devaddr, domp, busp, slotp); } +static void pci_init_cmask (PCIDevice *dev) +{ +pci_set_word (dev->cmask +PCI_VENDOR_ID ,0xffff ); +pci_set_word (dev->cmask +PCI_DEVICE_ID ,0xffff ); + dev->cmask[PCI_STATUS ] =PCI_STATUS_CAP_LIST ; + dev->cmask[PCI_REVISION_ID ] =0xff ; + dev->cmask[PCI_CLASS_PROG ] =0xff ; +pci_set_word (dev->cmask +PCI_CLASS_DEVICE ,0xffff ); + dev->cmask[PCI_HEADER_TYPE ] =0xff ; + dev->cmask[PCI_CAPABILITY_LIST ] =0xff ; +} +static void pci_init_wmask (PCIDevice *dev) {int i;
@@ -267,6 +285,7 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,pstrcpy (pci_dev->name,sizeof (pci_dev->name), name);memset (pci_dev->irq_state,0 ,sizeof (pci_dev->irq_state));pci_set_default_subsystem_id (pci_dev); +pci_init_cmask (pci_dev);pci_init_wmask (pci_dev);if (!config_read)
@@ -366,6 +385,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, } *(uint32_t *)(pci_dev->config + addr) =cpu_to_le32 (type); *(uint32_t *)(pci_dev->wmask + addr) =cpu_to_le32 (wmask); + *(uint32_t *)(pci_dev->cmask + addr) =0xffffffff ; }static void pci_update_mappings (PCIDevice *d)
@@ -900,6 +920,8 @@ int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size)memset (pdev->used + offset,0xFF , size);/* Make capability read-only by default */ memset (pdev->wmask + offset,0 , size); +/* Check capability by default */ +memset (pdev->cmask + offset,0xFF , size);return offset; }
@@ -912,6 +934,8 @@ void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size) pdev->config[prev] = pdev->config[offset +PCI_CAP_LIST_NEXT ];/* Make capability writeable again */ memset (pdev->wmask + offset,0xff , size); +/* Clear cmask as device-specific registers can't be checked */ +memset (pdev->cmask + offset,0 , size);memset (pdev->used + offset,0 , size);if (!pdev->config[PCI_CAPABILITY_LIST ])
hw/pci.h +5 -0
@@ -101,6 +101,7 @@ typedef struct PCIIORegion {#define PCI_COMMAND_MASTER 0x4/* Enable bus master */ #define PCI_STATUS 0x06/* 16 bits */ #define PCI_REVISION_ID 0x08/* 8 bits */ +#define PCI_CLASS_PROG 0x09/* Reg. Level Programming Interface */ #define PCI_CLASS_DEVICE 0x0a/* Device class */ #define PCI_CACHE_LINE_SIZE 0x0c/* 8 bits */ #define PCI_LATENCY_TIMER 0x0d/* 8 bits */
@@ -159,6 +160,10 @@ struct PCIDevice {/* PCI config space */ uint8_t config[PCI_CONFIG_SPACE_SIZE ]; +/* Used to enable config checks on load. Note that writeable bits are +* never checked even if set in cmask. */ +uint8_t cmask[PCI_CONFIG_SPACE_SIZE ]; +/* Used to implement R/W bytes */ uint8_t wmask[PCI_CONFIG_SPACE_SIZE ];
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 5/12 qemu/pci: MSI-X support functions
In reply to a message not in the archiveAdd functions implementing MSI-X support. First user will be virtio-pci. Note that platform must set a flag to declare MSI supported: this is a safety measure to avoid breaking platforms which should support MSI-X but currently lack this in the interrupt controller emulation. For PC this will be set by APIC.
--- Makefile.target | 2 +- hw/msix.c | 378 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/msix.h | 34 +++++ hw/pci.h | 23 ++++ 4 files changed, 436 insertions(+), 1 deletions(-) create mode 100644 hw/msix.c create mode 100644 hw/msix.h
Makefile.target +1 -1
@@ -494,7 +494,7 @@ endif #CONFIG_BSD_USER ifndef CONFIG_USER_ONLY OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o \ - gdbstub.o gdbstub-xml.o + gdbstub.o gdbstub-xml.o msix.o # virtio has to be here due to weird dependency between PCI and virtio-net. # need to fix this properly OBJS+=virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o
hw/msix.c new file +378 -0
378 lines not on this page · raw message
hw/msix.h new file +34 -0
@@ -0,0 +1,34 @@ +#ifndef QEMU_MSIX_H +#define QEMU_MSIX_H + +#include "qemu-common.h" + +int msix_init (PCIDevice *pdev,unsigned short nentries, +unsigned bar_nr,unsigned bar_size); + +void msix_write_config (PCIDevice *pci_dev,uint32_t address, +uint32_t val,int len); + +void msix_mmio_map (PCIDevice *pci_dev,int region_num, +uint32_t addr,uint32_t size,int type); + +int msix_uninit (PCIDevice *d); + +void msix_save (PCIDevice *dev,QEMUFile *f); +void msix_load (PCIDevice *dev,QEMUFile *f); + +int msix_enabled (PCIDevice *dev); +int msix_present (PCIDevice *dev); + +uint32_t msix_bar_size (PCIDevice *dev); + +int msix_vector_use (PCIDevice *dev,unsigned vector); +void msix_vector_unuse (PCIDevice *dev,unsigned vector); + +void msix_notify (PCIDevice *dev,unsigned vector); + +void msix_reset (PCIDevice *dev); + +extern int msix_supported; + +#endif
hw/pci.h +23 -0
@@ -155,6 +155,11 @@ typedef struct PCIIORegion {/* Size of the standard PCI config space */ #define PCI_CONFIG_SPACE_SIZE 0x100 +/* Bits in cap_present field. */ +enum { +QEMU_PCI_CAP_MSIX =0x1 , +}; +struct PCIDevice {DeviceState qdev;/* PCI config space */
@@ -186,6 +191,24 @@ struct PCIDevice {/* Current IRQ levels. Used internally by the generic PCI code. */ int irq_state[4 ]; + +/* Capability bits */ +uint32_t cap_present; + +/* Offset of MSI-X capability in config space */ +uint8_t msix_cap; + +/* MSI-X entries */ +int msix_entries_nr; + +/* Space to store MSIX table */ +uint8_t *msix_table_page; +/* MMIO index used to map MSIX table and pending bit entries. */ +int msix_mmio_index; +/* Reference-count for entries actually in use by driver. */ +unsigned *msix_entry_used; +/* Region including the MSI-X table */ +uint32_t msix_bar_size; }; PCIDevice *pci_register_device(PCIBus *bus, const char *name,
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 5/12 qemu/pci: MSI-X support functions
In reply to a message not in the archiveAdd functions implementing MSI-X support. First user will be virtio-pci. Note that platform must set a flag to declare MSI supported: this is a safety measure to avoid breaking platforms which should support MSI-X but currently lack this in the interrupt controller emulation. For PC this will be set by APIC.
--- Makefile.target | 2 +- hw/msix.c | 378 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/msix.h | 34 +++++ hw/pci.h | 23 ++++ 4 files changed, 436 insertions(+), 1 deletions(-) create mode 100644 hw/msix.c create mode 100644 hw/msix.h
Makefile.target +1 -1
@@ -494,7 +494,7 @@ endif #CONFIG_BSD_USER ifndef CONFIG_USER_ONLY OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o \ - gdbstub.o gdbstub-xml.o + gdbstub.o gdbstub-xml.o msix.o # virtio has to be here due to weird dependency between PCI and virtio-net. # need to fix this properly OBJS+=virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o
hw/msix.c new file +378 -0
378 lines not on this page · raw message
hw/msix.h new file +34 -0
@@ -0,0 +1,34 @@ +#ifndef QEMU_MSIX_H +#define QEMU_MSIX_H + +#include "qemu-common.h" + +int msix_init (PCIDevice *pdev,unsigned short nentries, +unsigned bar_nr,unsigned bar_size); + +void msix_write_config (PCIDevice *pci_dev,uint32_t address, +uint32_t val,int len); + +void msix_mmio_map (PCIDevice *pci_dev,int region_num, +uint32_t addr,uint32_t size,int type); + +int msix_uninit (PCIDevice *d); + +void msix_save (PCIDevice *dev,QEMUFile *f); +void msix_load (PCIDevice *dev,QEMUFile *f); + +int msix_enabled (PCIDevice *dev); +int msix_present (PCIDevice *dev); + +uint32_t msix_bar_size (PCIDevice *dev); + +int msix_vector_use (PCIDevice *dev,unsigned vector); +void msix_vector_unuse (PCIDevice *dev,unsigned vector); + +void msix_notify (PCIDevice *dev,unsigned vector); + +void msix_reset (PCIDevice *dev); + +extern int msix_supported; + +#endif
hw/pci.h +23 -0
@@ -155,6 +155,11 @@ typedef struct PCIIORegion {/* Size of the standard PCI config space */ #define PCI_CONFIG_SPACE_SIZE 0x100 +/* Bits in cap_present field. */ +enum { +QEMU_PCI_CAP_MSIX =0x1 , +}; +struct PCIDevice {DeviceState qdev;/* PCI config space */
@@ -186,6 +191,24 @@ struct PCIDevice {/* Current IRQ levels. Used internally by the generic PCI code. */ int irq_state[4 ]; + +/* Capability bits */ +uint32_t cap_present; + +/* Offset of MSI-X capability in config space */ +uint8_t msix_cap; + +/* MSI-X entries */ +int msix_entries_nr; + +/* Space to store MSIX table */ +uint8_t *msix_table_page; +/* MMIO index used to map MSIX table and pending bit entries. */ +int msix_mmio_index; +/* Reference-count for entries actually in use by driver. */ +unsigned *msix_entry_used; +/* Region including the MSI-X table */ +uint32_t msix_bar_size; }; PCIDevice *pci_register_device(PCIBus *bus, const char *name,
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerRe: [PATCHv6 05/12] qemu/pci: MSI-X support functions
hw/msix.c line 243
Quoted 2 lines · Michael S. Tsirkin
hw/msix.c new file +2
-0+ dev->msix_mmio_index =cpu_register_io_memory (0 , msix_mmio_read, + msix_mmio_write, dev);
The signature for cpu_register_io_memory changed meanwhile so this now needs to be:
+ dev->msix_mmio_index = cpu_register_io_memory(msix_mmio_read,
+ msix_mmio_write, dev);
Anthony, I guess you figured this out, or do you want me to respin the series with this minor fix?
Signature · 1 line
MST
Michael S. Tsirkin
maintainerRe: [PATCHv6 05/12] qemu/pci: MSI-X support functions
In reply to Michael S. Tsirkinhw/msix.c line 243
Quoted 2 lines · Michael S. Tsirkin
hw/msix.c new file +2
-0+ dev->msix_mmio_index =cpu_register_io_memory (0 , msix_mmio_read, + msix_mmio_write, dev);
The signature for cpu_register_io_memory changed meanwhile so this now needs to be:
+ dev->msix_mmio_index = cpu_register_io_memory(msix_mmio_read,
+ msix_mmio_write, dev);
Anthony, I guess you figured this out, or do you want me to respin the series with this minor fix?
Signature · 1 line
MST
Re: [PATCHv6 05/12] qemu/pci: MSI-X support functions
Quoted 13 lines · Michael S. Tsirkin
hw/msix.c line 243
Quoted 3 lines · Michael S. Tsirkin
hw/msix.c new file +2
-0+ dev->msix_mmio_index =cpu_register_io_memory (0 , msix_mmio_read, + msix_mmio_write, dev);The signature for
cpu_register_io_memorychanged meanwhile so this now needs to be:(quoted diff) +2
-0+ dev->msix_mmio_index = cpu_register_io_memory(msix_mmio_read, + msix_mmio_write, dev);
Quoted 3 lines
Anthony, I guess you figured this out, or do you want me to respin the series with this minor fix?
It's already changed in staging.
Regards,
Anthony Liguori
Re: [PATCHv6 05/12] qemu/pci: MSI-X support functions
In reply to Michael S. TsirkinQuoted 13 lines · Michael S. Tsirkin
hw/msix.c line 243
Quoted 3 lines · Michael S. Tsirkin
hw/msix.c new file +2
-0+ dev->msix_mmio_index =cpu_register_io_memory (0 , msix_mmio_read, + msix_mmio_write, dev);The signature for
cpu_register_io_memorychanged meanwhile so this now needs to be:(quoted diff) +2
-0+ dev->msix_mmio_index = cpu_register_io_memory(msix_mmio_read, + msix_mmio_write, dev);
Quoted 3 lines
Anthony, I guess you figured this out, or do you want me to respin the series with this minor fix?
It's already changed in staging.
Regards,
Anthony Liguori
Michael S. Tsirkin
maintainerPATCH v6 6/12 qemu/apic: minimal MSI/MSI-X implementation for PC
In reply to a message not in the archiveImplement MSI support in APIC. Note that MSI and MMIO APIC registers are at the same memory location, but actually not on the global bus: MSI is on PCI bus, APIC is connected directly to the CPU. We map them on the global bus at the same address which happens to work because MSI registers are reserved in APIC MMIO and vice versa.
--- hw/apic.c | 43 +++++++++++++++++++++++++++++++++++++++---- 1 files changed, 39 insertions(+), 4 deletions(-)
hw/apic.c +39 -4
@@ -19,6 +19,8 @@*/ #include "hw.h" #include "pc.h" +#include "pci.h" +#include "msix.h" #include "qemu-timer.h" #include "host-utils.h"
@@ -63,6 +65,19 @@#define MAX_APICS 255#define MAX_APIC_WORDS 8 +/* Intel APIC constants: from include/asm/msidef.h */ +#define MSI_DATA_VECTOR_SHIFT 0 +#define MSI_DATA_VECTOR_MASK 0x000000ff +#define MSI_DATA_DELIVERY_MODE_SHIFT 8 +#define MSI_DATA_TRIGGER_SHIFT 15 +#define MSI_DATA_LEVEL_SHIFT 14 +#define MSI_ADDR_DEST_MODE_SHIFT 2 +#define MSI_ADDR_DEST_ID_SHIFT 12 +#define MSI_ADDR_DEST_ID_MASK 0x00ffff0 + +#define MSI_ADDR_BASE 0xfee00000 +#define MSI_ADDR_SIZE 0x100000 + typedef struct APICState { CPUState *cpu_env; uint32_t apicbase;
@@ -731,11 +746,31 @@ static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr)return val; } +static void apic_send_msi (target_phys_addr_t addr,uint32 data) +{ +uint8_t dest = (addr &MSI_ADDR_DEST_ID_MASK ) >>MSI_ADDR_DEST_ID_SHIFT ; +uint8_t vector = (data &MSI_DATA_VECTOR_MASK ) >>MSI_DATA_VECTOR_SHIFT ; +uint8_t dest_mode = (addr >>MSI_ADDR_DEST_MODE_SHIFT ) &0x1 ; +uint8_t trigger_mode = (data >>MSI_DATA_TRIGGER_SHIFT ) &0x1 ; +uint8_t delivery = (data >>MSI_DATA_DELIVERY_MODE_SHIFT ) &0x7 ; +/* XXX: Ignore redirection hint. */ +apic_deliver_irq (dest, dest_mode, delivery, vector,0 , trigger_mode); +} +static void apic_mem_writel (void *opaque,target_phys_addr_t addr,uint32_t val) {CPUState *env;APICState *s; -int index; +int index = (addr >>4 ) &0xff ; +if (addr >0xfff || !index) { +/* MSI and MMIO APIC are at the same memory location, +* but actually not on the global bus: MSI is on PCI bus +* APIC is connected directly to the CPU. +* Mapping them on the global bus happens to work because +* MSI registers are reserved in APIC MMIO and vice versa. */ +apic_send_msi (addr, val); +return ; + } env = cpu_single_env;if (!env)
@@ -746,7 +781,6 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)printf ("APIC write: %08x = %08x\n" , (uint32_t )addr, val);#endif - index = (addr >>4 ) &0xff ;switch (index) {case 0x02 : s->id = (val >>24 );
@@ -931,6 +965,7 @@ int apic_init(CPUState *env) s->cpu_env = env;apic_reset (s); + msix_supported =1 ;/* XXX: mapping more APICs at the same memory location */ if (apic_io_memory ==0 ) {
@@ -938,7 +973,8 @@ int apic_init(CPUState *env)on the global memory bus. */ apic_io_memory =cpu_register_io_memory (apic_mem_read, apic_mem_write,NULL ); -cpu_register_physical_memory (s->apicbase & ~0xfff ,0x1000 , +/* XXX: what if the base changes? */ +cpu_register_physical_memory (MSI_ADDR_BASE ,MSI_ADDR_SIZE , apic_io_memory); } s->timer =qemu_new_timer (vm_clock, apic_timer, s);
@@ -949,4 +985,3 @@ int apic_init(CPUState *env) local_apics[s->idx] = s;return 0 ; } -
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 6/12 qemu/apic: minimal MSI/MSI-X implementation for PC
In reply to a message not in the archiveImplement MSI support in APIC. Note that MSI and MMIO APIC registers are at the same memory location, but actually not on the global bus: MSI is on PCI bus, APIC is connected directly to the CPU. We map them on the global bus at the same address which happens to work because MSI registers are reserved in APIC MMIO and vice versa.
--- hw/apic.c | 43 +++++++++++++++++++++++++++++++++++++++---- 1 files changed, 39 insertions(+), 4 deletions(-)
hw/apic.c +39 -4
@@ -19,6 +19,8 @@*/ #include "hw.h" #include "pc.h" +#include "pci.h" +#include "msix.h" #include "qemu-timer.h" #include "host-utils.h"
@@ -63,6 +65,19 @@#define MAX_APICS 255#define MAX_APIC_WORDS 8 +/* Intel APIC constants: from include/asm/msidef.h */ +#define MSI_DATA_VECTOR_SHIFT 0 +#define MSI_DATA_VECTOR_MASK 0x000000ff +#define MSI_DATA_DELIVERY_MODE_SHIFT 8 +#define MSI_DATA_TRIGGER_SHIFT 15 +#define MSI_DATA_LEVEL_SHIFT 14 +#define MSI_ADDR_DEST_MODE_SHIFT 2 +#define MSI_ADDR_DEST_ID_SHIFT 12 +#define MSI_ADDR_DEST_ID_MASK 0x00ffff0 + +#define MSI_ADDR_BASE 0xfee00000 +#define MSI_ADDR_SIZE 0x100000 + typedef struct APICState { CPUState *cpu_env; uint32_t apicbase;
@@ -731,11 +746,31 @@ static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr)return val; } +static void apic_send_msi (target_phys_addr_t addr,uint32 data) +{ +uint8_t dest = (addr &MSI_ADDR_DEST_ID_MASK ) >>MSI_ADDR_DEST_ID_SHIFT ; +uint8_t vector = (data &MSI_DATA_VECTOR_MASK ) >>MSI_DATA_VECTOR_SHIFT ; +uint8_t dest_mode = (addr >>MSI_ADDR_DEST_MODE_SHIFT ) &0x1 ; +uint8_t trigger_mode = (data >>MSI_DATA_TRIGGER_SHIFT ) &0x1 ; +uint8_t delivery = (data >>MSI_DATA_DELIVERY_MODE_SHIFT ) &0x7 ; +/* XXX: Ignore redirection hint. */ +apic_deliver_irq (dest, dest_mode, delivery, vector,0 , trigger_mode); +} +static void apic_mem_writel (void *opaque,target_phys_addr_t addr,uint32_t val) {CPUState *env;APICState *s; -int index; +int index = (addr >>4 ) &0xff ; +if (addr >0xfff || !index) { +/* MSI and MMIO APIC are at the same memory location, +* but actually not on the global bus: MSI is on PCI bus +* APIC is connected directly to the CPU. +* Mapping them on the global bus happens to work because +* MSI registers are reserved in APIC MMIO and vice versa. */ +apic_send_msi (addr, val); +return ; + } env = cpu_single_env;if (!env)
@@ -746,7 +781,6 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)printf ("APIC write: %08x = %08x\n" , (uint32_t )addr, val);#endif - index = (addr >>4 ) &0xff ;switch (index) {case 0x02 : s->id = (val >>24 );
@@ -931,6 +965,7 @@ int apic_init(CPUState *env) s->cpu_env = env;apic_reset (s); + msix_supported =1 ;/* XXX: mapping more APICs at the same memory location */ if (apic_io_memory ==0 ) {
@@ -938,7 +973,8 @@ int apic_init(CPUState *env)on the global memory bus. */ apic_io_memory =cpu_register_io_memory (apic_mem_read, apic_mem_write,NULL ); -cpu_register_physical_memory (s->apicbase & ~0xfff ,0x1000 , +/* XXX: what if the base changes? */ +cpu_register_physical_memory (MSI_ADDR_BASE ,MSI_ADDR_SIZE , apic_io_memory); } s->timer =qemu_new_timer (vm_clock, apic_timer, s);
@@ -949,4 +985,3 @@ int apic_init(CPUState *env) local_apics[s->idx] = s;return 0 ; } -
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 7/12 qemu/virtio: virtio support for many interrupt vectors
In reply to a message not in the archiveExtend virtio to support many interrupt vectors, and rearrange code in preparation for multi-vector support (mostly move reset out to bindings, because we will have to reset the vectors in transport-specific code). Actual bindings in pci, and use in net, to follow.
Load and save are not connected to bindings yet, so they are left stubbed out for now.
--- hw/syborg_virtio.c | 13 ++++++++-- hw/virtio-pci.c | 24 ++++++++++++++++---- hw/virtio.c | 59 ++++++++++++++++++++++++++++++++++++++------------- hw/virtio.h | 10 +++++++- 4 files changed, 81 insertions(+), 25 deletions(-)
hw/syborg_virtio.c +10 -3
@@ -134,7 +134,10 @@ static void syborg_virtio_writel(void *opaque, target_phys_addr_t offset, vdev->features = value;break ;case SYBORG_VIRTIO_QUEUE_BASE : -virtio_queue_set_addr (vdev, vdev->queue_sel, value); +if (value ==0 ) +virtio_reset (vdev); +else +virtio_queue_set_addr (vdev, vdev->queue_sel, value);break ;case SYBORG_VIRTIO_QUEUE_SEL :if (value <VIRTIO_PCI_QUEUE_MAX )
@@ -228,7 +231,7 @@ static CPUWriteMemoryFunc *syborg_virtio_writefn[] = { syborg_virtio_writel }; -static void syborg_virtio_update_irq (void *opaque) +static void syborg_virtio_update_irq (void *opaque,uint16_t vector) {SyborgVirtIOProxy *proxy = opaque;int level;
@@ -239,7 +242,7 @@ static void syborg_virtio_update_irq(void *opaque) }static VirtIOBindings syborg_virtio_bindings = { - .update_irq = syborg_virtio_update_irq + .notify = syborg_virtio_update_irq };static void syborg_virtio_init (SyborgVirtIOProxy *proxy,VirtIODevice *vdev)
@@ -248,6 +251,8 @@ static void syborg_virtio_init(SyborgVirtIOProxy *proxy, VirtIODevice *vdev) proxy->vdev = vdev; +/* Don't support multiple vectors */ + proxy->vdev->nvectors =0 ;sysbus_init_irq (&proxy->busdev, &proxy->irq); iomemtype =cpu_register_io_memory (syborg_virtio_readfn, syborg_virtio_writefn, proxy);
@@ -255,6 +260,8 @@ static void syborg_virtio_init(SyborgVirtIOProxy *proxy, VirtIODevice *vdev) proxy->id = ((uint32_t )0x1af4 <<16 ) | vdev->device_id; +qemu_register_reset (virtio_reset,0 , vdev); +virtio_bind_device (vdev, &syborg_virtio_bindings, proxy); }
hw/virtio-pci.c +19 -5
@@ -78,13 +78,19 @@ typedef struct {/* virtio device */ -static void virtio_pci_update_irq (void *opaque) +static void virtio_pci_notify (void *opaque,uint16_t vector) {VirtIOPCIProxy *proxy = opaque;qemu_set_irq (proxy->pci_dev.irq[0 ], proxy->vdev->isr &1 ); } +static void virtio_pci_reset (void *opaque) +{ +VirtIOPCIProxy *proxy = opaque; +virtio_reset (proxy->vdev); +} +static void virtio_ioport_write (void *opaque,uint32_t addr,uint32_t val) {VirtIOPCIProxy *proxy = opaque;
@@ -108,7 +114,10 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)break ;case VIRTIO_PCI_QUEUE_PFN : pa = (target_phys_addr_t )val <<VIRTIO_PCI_QUEUE_ADDR_SHIFT ; -virtio_queue_set_addr (vdev, vdev->queue_sel, pa); +if (pa ==0 ) +virtio_pci_reset (proxy); +else +virtio_queue_set_addr (vdev, vdev->queue_sel, pa);break ;case VIRTIO_PCI_QUEUE_SEL :if (val <VIRTIO_PCI_QUEUE_MAX )
@@ -120,7 +129,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)case VIRTIO_PCI_STATUS : vdev->status = val &0xFF ;if (vdev->status ==0 ) -virtio_reset (vdev); +virtio_pci_reset (proxy);break ; } }
@@ -158,7 +167,7 @@ static uint32_t virtio_ioport_read(void *opaque, uint32_t addr)/* reading from the ISR also clears it. */ ret = vdev->isr; vdev->isr =0 ; -virtio_update_irq (vdev); +qemu_set_irq (proxy->pci_dev.irq[0 ],0 );break ;default :break ;
@@ -243,7 +252,7 @@ static void virtio_map(PCIDevice *pci_dev, int region_num, }static const VirtIOBindings virtio_pci_bindings = { - .update_irq = virtio_pci_update_irq + .notify = virtio_pci_notify }; static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,
@@ -255,6 +264,9 @@ static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, proxy->vdev = vdev; +/* No support for multiple vectors yet. */ + proxy->vdev->nvectors =0 ; + config = proxy->pci_dev.config;pci_config_set_vendor_id (config, vendor);pci_config_set_device_id (config, device);
@@ -279,6 +291,8 @@ static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,pci_register_bar (&proxy->pci_dev,0 , size,PCI_ADDRESS_SPACE_IO , virtio_map); +qemu_register_reset (virtio_pci_reset,0 , proxy); +virtio_bind_device (vdev, &virtio_pci_bindings, proxy); }
hw/virtio.c +44 -15
160 lines not on this page · raw message
hw/virtio.h +8 -2
@@ -71,11 +71,13 @@ typedef struct VirtQueueElement } VirtQueueElement;typedef struct { -void (*update_irq)(void * opaque); +void (*notify)(void * opaque,uint16_t vector); }VirtIOBindings ;#define VIRTIO_PCI_QUEUE_MAX 16 +#define VIRTIO_NO_VECTOR 0xffff +struct VirtIODevice {const char *name;
@@ -85,6 +87,8 @@ struct VirtIODeviceuint32_t features;size_t config_len;void *config; +uint16_t config_vector; +int nvectors;uint32_t (*get_features)(VirtIODevice *vdev);uint32_t (*bad_features)(VirtIODevice *vdev);void (*set_features)(VirtIODevice *vdev,uint32_t val);
@@ -114,7 +118,7 @@ void virtio_notify(VirtIODevice *vdev, VirtQueue *vq);void virtio_save (VirtIODevice *vdev,QEMUFile *f); -void virtio_load (VirtIODevice *vdev,QEMUFile *f); +int virtio_load (VirtIODevice *vdev,QEMUFile *f);void virtio_cleanup (VirtIODevice *vdev);
@@ -140,6 +144,8 @@ void virtio_queue_set_addr(VirtIODevice *vdev, int n, target_phys_addr_t addr);target_phys_addr_t virtio_queue_get_addr (VirtIODevice *vdev,int n);int virtio_queue_get_num (VirtIODevice *vdev,int n);void virtio_queue_notify (VirtIODevice *vdev,int n); +uint16_t virtio_queue_vector (VirtIODevice *vdev,int n); +void virtio_queue_set_vector (VirtIODevice *vdev,int n,uint16_t vector);void virtio_reset (void *opaque);void virtio_update_irq (VirtIODevice *vdev);
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 7/12 qemu/virtio: virtio support for many interrupt vectors
In reply to a message not in the archiveExtend virtio to support many interrupt vectors, and rearrange code in preparation for multi-vector support (mostly move reset out to bindings, because we will have to reset the vectors in transport-specific code). Actual bindings in pci, and use in net, to follow.
Load and save are not connected to bindings yet, so they are left stubbed out for now.
--- hw/syborg_virtio.c | 13 ++++++++-- hw/virtio-pci.c | 24 ++++++++++++++++---- hw/virtio.c | 59 ++++++++++++++++++++++++++++++++++++++------------- hw/virtio.h | 10 +++++++- 4 files changed, 81 insertions(+), 25 deletions(-)
hw/syborg_virtio.c +10 -3
@@ -134,7 +134,10 @@ static void syborg_virtio_writel(void *opaque, target_phys_addr_t offset, vdev->features = value;break ;case SYBORG_VIRTIO_QUEUE_BASE : -virtio_queue_set_addr (vdev, vdev->queue_sel, value); +if (value ==0 ) +virtio_reset (vdev); +else +virtio_queue_set_addr (vdev, vdev->queue_sel, value);break ;case SYBORG_VIRTIO_QUEUE_SEL :if (value <VIRTIO_PCI_QUEUE_MAX )
@@ -228,7 +231,7 @@ static CPUWriteMemoryFunc *syborg_virtio_writefn[] = { syborg_virtio_writel }; -static void syborg_virtio_update_irq (void *opaque) +static void syborg_virtio_update_irq (void *opaque,uint16_t vector) {SyborgVirtIOProxy *proxy = opaque;int level;
@@ -239,7 +242,7 @@ static void syborg_virtio_update_irq(void *opaque) }static VirtIOBindings syborg_virtio_bindings = { - .update_irq = syborg_virtio_update_irq + .notify = syborg_virtio_update_irq };static void syborg_virtio_init (SyborgVirtIOProxy *proxy,VirtIODevice *vdev)
@@ -248,6 +251,8 @@ static void syborg_virtio_init(SyborgVirtIOProxy *proxy, VirtIODevice *vdev) proxy->vdev = vdev; +/* Don't support multiple vectors */ + proxy->vdev->nvectors =0 ;sysbus_init_irq (&proxy->busdev, &proxy->irq); iomemtype =cpu_register_io_memory (syborg_virtio_readfn, syborg_virtio_writefn, proxy);
@@ -255,6 +260,8 @@ static void syborg_virtio_init(SyborgVirtIOProxy *proxy, VirtIODevice *vdev) proxy->id = ((uint32_t )0x1af4 <<16 ) | vdev->device_id; +qemu_register_reset (virtio_reset,0 , vdev); +virtio_bind_device (vdev, &syborg_virtio_bindings, proxy); }
hw/virtio-pci.c +19 -5
@@ -78,13 +78,19 @@ typedef struct {/* virtio device */ -static void virtio_pci_update_irq (void *opaque) +static void virtio_pci_notify (void *opaque,uint16_t vector) {VirtIOPCIProxy *proxy = opaque;qemu_set_irq (proxy->pci_dev.irq[0 ], proxy->vdev->isr &1 ); } +static void virtio_pci_reset (void *opaque) +{ +VirtIOPCIProxy *proxy = opaque; +virtio_reset (proxy->vdev); +} +static void virtio_ioport_write (void *opaque,uint32_t addr,uint32_t val) {VirtIOPCIProxy *proxy = opaque;
@@ -108,7 +114,10 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)break ;case VIRTIO_PCI_QUEUE_PFN : pa = (target_phys_addr_t )val <<VIRTIO_PCI_QUEUE_ADDR_SHIFT ; -virtio_queue_set_addr (vdev, vdev->queue_sel, pa); +if (pa ==0 ) +virtio_pci_reset (proxy); +else +virtio_queue_set_addr (vdev, vdev->queue_sel, pa);break ;case VIRTIO_PCI_QUEUE_SEL :if (val <VIRTIO_PCI_QUEUE_MAX )
@@ -120,7 +129,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)case VIRTIO_PCI_STATUS : vdev->status = val &0xFF ;if (vdev->status ==0 ) -virtio_reset (vdev); +virtio_pci_reset (proxy);break ; } }
@@ -158,7 +167,7 @@ static uint32_t virtio_ioport_read(void *opaque, uint32_t addr)/* reading from the ISR also clears it. */ ret = vdev->isr; vdev->isr =0 ; -virtio_update_irq (vdev); +qemu_set_irq (proxy->pci_dev.irq[0 ],0 );break ;default :break ;
@@ -243,7 +252,7 @@ static void virtio_map(PCIDevice *pci_dev, int region_num, }static const VirtIOBindings virtio_pci_bindings = { - .update_irq = virtio_pci_update_irq + .notify = virtio_pci_notify }; static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,
@@ -255,6 +264,9 @@ static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, proxy->vdev = vdev; +/* No support for multiple vectors yet. */ + proxy->vdev->nvectors =0 ; + config = proxy->pci_dev.config;pci_config_set_vendor_id (config, vendor);pci_config_set_device_id (config, device);
@@ -279,6 +291,8 @@ static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,pci_register_bar (&proxy->pci_dev,0 , size,PCI_ADDRESS_SPACE_IO , virtio_map); +qemu_register_reset (virtio_pci_reset,0 , proxy); +virtio_bind_device (vdev, &virtio_pci_bindings, proxy); }
hw/virtio.c +44 -15
160 lines not on this page · raw message
hw/virtio.h +8 -2
@@ -71,11 +71,13 @@ typedef struct VirtQueueElement } VirtQueueElement;typedef struct { -void (*update_irq)(void * opaque); +void (*notify)(void * opaque,uint16_t vector); }VirtIOBindings ;#define VIRTIO_PCI_QUEUE_MAX 16 +#define VIRTIO_NO_VECTOR 0xffff +struct VirtIODevice {const char *name;
@@ -85,6 +87,8 @@ struct VirtIODeviceuint32_t features;size_t config_len;void *config; +uint16_t config_vector; +int nvectors;uint32_t (*get_features)(VirtIODevice *vdev);uint32_t (*bad_features)(VirtIODevice *vdev);void (*set_features)(VirtIODevice *vdev,uint32_t val);
@@ -114,7 +118,7 @@ void virtio_notify(VirtIODevice *vdev, VirtQueue *vq);void virtio_save (VirtIODevice *vdev,QEMUFile *f); -void virtio_load (VirtIODevice *vdev,QEMUFile *f); +int virtio_load (VirtIODevice *vdev,QEMUFile *f);void virtio_cleanup (VirtIODevice *vdev);
@@ -140,6 +144,8 @@ void virtio_queue_set_addr(VirtIODevice *vdev, int n, target_phys_addr_t addr);target_phys_addr_t virtio_queue_get_addr (VirtIODevice *vdev,int n);int virtio_queue_get_num (VirtIODevice *vdev,int n);void virtio_queue_notify (VirtIODevice *vdev,int n); +uint16_t virtio_queue_vector (VirtIODevice *vdev,int n); +void virtio_queue_set_vector (VirtIODevice *vdev,int n,uint16_t vector);void virtio_reset (void *opaque);void virtio_update_irq (VirtIODevice *vdev);
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 8/12 qemu/virtio: MSI-X support in virtio PCI
In reply to a message not in the archiveThis enables actual support for MSI-X in virtio PCI. First user will be virtio-net.
--- hw/virtio-pci.c | 152 ++++++++++++++++++++++++++++++++++++++++-------------- rules.mak | 2 +- 2 files changed, 113 insertions(+), 41 deletions(-)
hw/virtio-pci.c +112 -40
@@ -18,6 +18,7 @@#include "virtio.h" #include "pci.h" //#include "sysemu.h" +#include "msix.h" /* from Linux's linux/virtio_pci.h */
@@ -47,7 +48,24 @@* a read-and-acknowledge. */ #define VIRTIO_PCI_ISR 19 -#define VIRTIO_PCI_CONFIG 20 +/* MSI-X registers: only enabled if MSI-X is enabled. */ +/* A 16-bit vector for configuration changes. */ +#define VIRTIO_MSI_CONFIG_VECTOR 20 +/* A 16-bit vector for selected queue notifications. */ +#define VIRTIO_MSI_QUEUE_VECTOR 22 + +/* Config space size */ +#define VIRTIO_PCI_CONFIG_NOMSI 20 +#define VIRTIO_PCI_CONFIG_MSI 24 +#define VIRTIO_PCI_REGION_SIZE (dev) (msix_present(dev) ? \ + VIRTIO_PCI_CONFIG_MSI : \ + VIRTIO_PCI_CONFIG_NOMSI) + +/* The remaining space is defined by each driver as the per-driver +* configuration space */ +#define VIRTIO_PCI_CONFIG (dev) (msix_enabled(dev) ? \ + VIRTIO_PCI_CONFIG_MSI : \ + VIRTIO_PCI_CONFIG_NOMSI)/* Virtio ABI version, if we increment this, we break the guest driver. */ #define VIRTIO_PCI_ABI_VERSION 0
@@ -81,14 +99,17 @@ typedef struct {static void virtio_pci_notify (void *opaque,uint16_t vector) {VirtIOPCIProxy *proxy = opaque; - -qemu_set_irq (proxy->pci_dev.irq[0 ], proxy->vdev->isr &1 ); +if (msix_enabled (&proxy->pci_dev)) +msix_notify (&proxy->pci_dev, vector); +else +qemu_set_irq (proxy->pci_dev.irq[0 ], proxy->vdev->isr &1 ); }static void virtio_pci_reset (void *opaque) {VirtIOPCIProxy *proxy = opaque;virtio_reset (proxy->vdev); +msix_reset (&proxy->pci_dev); }static void virtio_ioport_write (void *opaque,uint32_t addr,uint32_t val)
@@ -97,8 +118,6 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)VirtIODevice *vdev = proxy->vdev;target_phys_addr_t pa; - addr -= proxy->addr; -switch (addr) {case VIRTIO_PCI_GUEST_FEATURES :/* Guest does not negotiate properly? We have to assume nothing. */
@@ -131,17 +150,33 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)if (vdev->status ==0 )virtio_pci_reset (proxy);break ; +case VIRTIO_MSI_CONFIG_VECTOR : +msix_vector_unuse (&proxy->pci_dev, vdev->config_vector); +/* Make it possible for guest to discover an error took place. */ +if (msix_vector_use (&proxy->pci_dev, val) <0 ) + val =VIRTIO_NO_VECTOR ; + vdev->config_vector = val; +break ; +case VIRTIO_MSI_QUEUE_VECTOR : +msix_vector_unuse (&proxy->pci_dev, +virtio_queue_vector (vdev, vdev->queue_sel)); +/* Make it possible for guest to discover an error took place. */ +if (msix_vector_use (&proxy->pci_dev, val) <0 ) + val =VIRTIO_NO_VECTOR ; +virtio_queue_set_vector (vdev, vdev->queue_sel, val); +break ; +default : +fprintf (stderr,"%s: unexpected address 0x%x value 0x%x\n" , + __func__, addr, val); +break ; } } -static uint32_t virtio_ioport_read (void *opaque,uint32_t addr) +static uint32_t virtio_ioport_read (VirtIOPCIProxy *proxy,uint32_t addr) { -VirtIOPCIProxy *proxy = opaque;VirtIODevice *vdev = proxy->vdev;uint32_t ret =0xFFFFFFFF ; - addr -= proxy->addr; -switch (addr) {case VIRTIO_PCI_HOST_FEATURES : ret = vdev->get_features (vdev);
@@ -169,6 +204,12 @@ static uint32_t virtio_ioport_read(void *opaque, uint32_t addr) vdev->isr =0 ;qemu_set_irq (proxy->pci_dev.irq[0 ],0 );break ; +case VIRTIO_MSI_CONFIG_VECTOR : + ret = vdev->config_vector; +break ; +case VIRTIO_MSI_QUEUE_VECTOR : + ret =virtio_queue_vector (vdev, vdev->queue_sel); +break ;default :break ; }
@@ -179,42 +220,72 @@ static uint32_t virtio_ioport_read(void *opaque, uint32_t addr)static uint32_t virtio_pci_config_readb (void *opaque,uint32_t addr) {VirtIOPCIProxy *proxy = opaque; - addr -= proxy->addr +VIRTIO_PCI_CONFIG ; +uint32_t config =VIRTIO_PCI_CONFIG (&proxy->pci_dev); + addr -= proxy->addr; +if (addr < config) +return virtio_ioport_read (proxy, addr); + addr -= config;return virtio_config_readb (proxy->vdev, addr); }static uint32_t virtio_pci_config_readw (void *opaque,uint32_t addr) {VirtIOPCIProxy *proxy = opaque; - addr -= proxy->addr +VIRTIO_PCI_CONFIG ; +uint32_t config =VIRTIO_PCI_CONFIG (&proxy->pci_dev); + addr -= proxy->addr; +if (addr < config) +return virtio_ioport_read (proxy, addr); + addr -= config;return virtio_config_readw (proxy->vdev, addr); }static uint32_t virtio_pci_config_readl (void *opaque,uint32_t addr) {VirtIOPCIProxy *proxy = opaque; - addr -= proxy->addr +VIRTIO_PCI_CONFIG ; +uint32_t config =VIRTIO_PCI_CONFIG (&proxy->pci_dev); + addr -= proxy->addr; +if (addr < config) +return virtio_ioport_read (proxy, addr); + addr -= config;return virtio_config_readl (proxy->vdev, addr); }static void virtio_pci_config_writeb (void *opaque,uint32_t addr,uint32_t val) {VirtIOPCIProxy *proxy = opaque; - addr -= proxy->addr +VIRTIO_PCI_CONFIG ; +uint32_t config =VIRTIO_PCI_CONFIG (&proxy->pci_dev); + addr -= proxy->addr; +if (addr < config) { +virtio_ioport_write (proxy, addr, val); +return ; + } + addr -= config;virtio_config_writeb (proxy->vdev, addr, val); }static void virtio_pci_config_writew (void *opaque,uint32_t addr,uint32_t val) {VirtIOPCIProxy *proxy = opaque; - addr -= proxy->addr +VIRTIO_PCI_CONFIG ; +uint32_t config =VIRTIO_PCI_CONFIG (&proxy->pci_dev); + addr -= proxy->addr; +if (addr < config) { +virtio_ioport_write (proxy, addr, val); +return ; + } + addr -= config;virtio_config_writew (proxy->vdev, addr, val); }static void virtio_pci_config_writel (void *opaque,uint32_t addr,uint32_t val) {VirtIOPCIProxy *proxy = opaque; - addr -= proxy->addr +VIRTIO_PCI_CONFIG ; +uint32_t config =VIRTIO_PCI_CONFIG (&proxy->pci_dev); + addr -= proxy->addr; +if (addr < config) { +virtio_ioport_write (proxy, addr, val); +return ; + } + addr -= config;virtio_config_writel (proxy->vdev, addr, val); }
@@ -223,32 +294,26 @@ static void virtio_map(PCIDevice *pci_dev, int region_num, {VirtIOPCIProxy *proxy =container_of (pci_dev, VirtIOPCIProxy, pci_dev);VirtIODevice *vdev = proxy->vdev; -int i; +unsigned config_len =VIRTIO_PCI_REGION_SIZE (pci_dev) + vdev->config_len; proxy->addr = addr; -for (i =0 ; i <3 ; i++) { -register_ioport_write (addr,VIRTIO_PCI_CONFIG ,1 << i, - virtio_ioport_write, proxy); -register_ioport_read (addr,VIRTIO_PCI_CONFIG ,1 << i, - virtio_ioport_read, proxy); - } -if (vdev->config_len) { -register_ioport_write (addr +VIRTIO_PCI_CONFIG , vdev->config_len,1 , - virtio_pci_config_writeb, proxy); -register_ioport_write (addr +VIRTIO_PCI_CONFIG , vdev->config_len,2 , - virtio_pci_config_writew, proxy); -register_ioport_write (addr +VIRTIO_PCI_CONFIG , vdev->config_len,4 , - virtio_pci_config_writel, proxy); -register_ioport_read (addr +VIRTIO_PCI_CONFIG , vdev->config_len,1 , - virtio_pci_config_readb, proxy); -register_ioport_read (addr +VIRTIO_PCI_CONFIG , vdev->config_len,2 , - virtio_pci_config_readw, proxy); -register_ioport_read (addr +VIRTIO_PCI_CONFIG , vdev->config_len,4 , - virtio_pci_config_readl, proxy); +register_ioport_write (addr, config_len,1 , virtio_pci_config_writeb, proxy); +register_ioport_write (addr, config_len,2 , virtio_pci_config_writew, proxy); +register_ioport_write (addr, config_len,4 , virtio_pci_config_writel, proxy); +register_ioport_read (addr, config_len,1 , virtio_pci_config_readb, proxy); +register_ioport_read (addr, config_len,2 , virtio_pci_config_readw, proxy); +register_ioport_read (addr, config_len,4 , virtio_pci_config_readl, proxy); +if (vdev->config_len) vdev->get_config (vdev, vdev->config); - } +} + +static void virtio_write_config (PCIDevice *pci_dev,uint32_t address, +uint32_t val,int len) +{ +pci_default_write_config (pci_dev, address, val, len); +msix_write_config (pci_dev, address, val, len); }static const VirtIOBindings virtio_pci_bindings = {
@@ -264,9 +329,6 @@ static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, proxy->vdev = vdev; -/* No support for multiple vectors yet. */ - proxy->vdev->nvectors =0 ; - config = proxy->pci_dev.config;pci_config_set_vendor_id (config, vendor);pci_config_set_device_id (config, device);
@@ -284,7 +346,17 @@ static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, config[0x3d ] =1 ; - size =20 + vdev->config_len; +if (vdev->nvectors && !msix_init (&proxy->pci_dev, vdev->nvectors,1 ,0 )) { +pci_register_bar (&proxy->pci_dev,1 , +msix_bar_size (&proxy->pci_dev), +PCI_ADDRESS_SPACE_MEM , + msix_mmio_map); + proxy->pci_dev.config_write = virtio_write_config; + proxy->pci_dev.unregister = msix_uninit; + }else + vdev->nvectors =0 ; + + size =VIRTIO_PCI_REGION_SIZE (&proxy->pci_dev) + vdev->config_len;if (size & (size-1 )) size =1 <<qemu_fls (size);
rules.mak +1 -1
@@ -1,6 +1,6 @@ %.o: %.c - $(call quiet-command,$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<," CC $(TARGET_DIR)$@") + $(call quiet-command,$(CC) $(CPPFLAGS) $(CFLAGS) -Werror -Wno-error=uninitialized -c -o $@ $<," CC $(TARGET_DIR)$@") %.o: %.S $(call quiet-command,$(CC) $(CPPFLAGS) -c -o $@ $<," AS $(TARGET_DIR)$@")
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 8/12 qemu/virtio: MSI-X support in virtio PCI
In reply to a message not in the archiveThis enables actual support for MSI-X in virtio PCI. First user will be virtio-net.
--- hw/virtio-pci.c | 152 ++++++++++++++++++++++++++++++++++++++++-------------- rules.mak | 2 +- 2 files changed, 113 insertions(+), 41 deletions(-)
hw/virtio-pci.c +112 -40
@@ -18,6 +18,7 @@#include "virtio.h" #include "pci.h" //#include "sysemu.h" +#include "msix.h" /* from Linux's linux/virtio_pci.h */
@@ -47,7 +48,24 @@* a read-and-acknowledge. */ #define VIRTIO_PCI_ISR 19 -#define VIRTIO_PCI_CONFIG 20 +/* MSI-X registers: only enabled if MSI-X is enabled. */ +/* A 16-bit vector for configuration changes. */ +#define VIRTIO_MSI_CONFIG_VECTOR 20 +/* A 16-bit vector for selected queue notifications. */ +#define VIRTIO_MSI_QUEUE_VECTOR 22 + +/* Config space size */ +#define VIRTIO_PCI_CONFIG_NOMSI 20 +#define VIRTIO_PCI_CONFIG_MSI 24 +#define VIRTIO_PCI_REGION_SIZE (dev) (msix_present(dev) ? \ + VIRTIO_PCI_CONFIG_MSI : \ + VIRTIO_PCI_CONFIG_NOMSI) + +/* The remaining space is defined by each driver as the per-driver +* configuration space */ +#define VIRTIO_PCI_CONFIG (dev) (msix_enabled(dev) ? \ + VIRTIO_PCI_CONFIG_MSI : \ + VIRTIO_PCI_CONFIG_NOMSI)/* Virtio ABI version, if we increment this, we break the guest driver. */ #define VIRTIO_PCI_ABI_VERSION 0
@@ -81,14 +99,17 @@ typedef struct {static void virtio_pci_notify (void *opaque,uint16_t vector) {VirtIOPCIProxy *proxy = opaque; - -qemu_set_irq (proxy->pci_dev.irq[0 ], proxy->vdev->isr &1 ); +if (msix_enabled (&proxy->pci_dev)) +msix_notify (&proxy->pci_dev, vector); +else +qemu_set_irq (proxy->pci_dev.irq[0 ], proxy->vdev->isr &1 ); }static void virtio_pci_reset (void *opaque) {VirtIOPCIProxy *proxy = opaque;virtio_reset (proxy->vdev); +msix_reset (&proxy->pci_dev); }static void virtio_ioport_write (void *opaque,uint32_t addr,uint32_t val)
@@ -97,8 +118,6 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)VirtIODevice *vdev = proxy->vdev;target_phys_addr_t pa; - addr -= proxy->addr; -switch (addr) {case VIRTIO_PCI_GUEST_FEATURES :/* Guest does not negotiate properly? We have to assume nothing. */
@@ -131,17 +150,33 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)if (vdev->status ==0 )virtio_pci_reset (proxy);break ; +case VIRTIO_MSI_CONFIG_VECTOR : +msix_vector_unuse (&proxy->pci_dev, vdev->config_vector); +/* Make it possible for guest to discover an error took place. */ +if (msix_vector_use (&proxy->pci_dev, val) <0 ) + val =VIRTIO_NO_VECTOR ; + vdev->config_vector = val; +break ; +case VIRTIO_MSI_QUEUE_VECTOR : +msix_vector_unuse (&proxy->pci_dev, +virtio_queue_vector (vdev, vdev->queue_sel)); +/* Make it possible for guest to discover an error took place. */ +if (msix_vector_use (&proxy->pci_dev, val) <0 ) + val =VIRTIO_NO_VECTOR ; +virtio_queue_set_vector (vdev, vdev->queue_sel, val); +break ; +default : +fprintf (stderr,"%s: unexpected address 0x%x value 0x%x\n" , + __func__, addr, val); +break ; } } -static uint32_t virtio_ioport_read (void *opaque,uint32_t addr) +static uint32_t virtio_ioport_read (VirtIOPCIProxy *proxy,uint32_t addr) { -VirtIOPCIProxy *proxy = opaque;VirtIODevice *vdev = proxy->vdev;uint32_t ret =0xFFFFFFFF ; - addr -= proxy->addr; -switch (addr) {case VIRTIO_PCI_HOST_FEATURES : ret = vdev->get_features (vdev);
@@ -169,6 +204,12 @@ static uint32_t virtio_ioport_read(void *opaque, uint32_t addr) vdev->isr =0 ;qemu_set_irq (proxy->pci_dev.irq[0 ],0 );break ; +case VIRTIO_MSI_CONFIG_VECTOR : + ret = vdev->config_vector; +break ; +case VIRTIO_MSI_QUEUE_VECTOR : + ret =virtio_queue_vector (vdev, vdev->queue_sel); +break ;default :break ; }
@@ -179,42 +220,72 @@ static uint32_t virtio_ioport_read(void *opaque, uint32_t addr)static uint32_t virtio_pci_config_readb (void *opaque,uint32_t addr) {VirtIOPCIProxy *proxy = opaque; - addr -= proxy->addr +VIRTIO_PCI_CONFIG ; +uint32_t config =VIRTIO_PCI_CONFIG (&proxy->pci_dev); + addr -= proxy->addr; +if (addr < config) +return virtio_ioport_read (proxy, addr); + addr -= config;return virtio_config_readb (proxy->vdev, addr); }static uint32_t virtio_pci_config_readw (void *opaque,uint32_t addr) {VirtIOPCIProxy *proxy = opaque; - addr -= proxy->addr +VIRTIO_PCI_CONFIG ; +uint32_t config =VIRTIO_PCI_CONFIG (&proxy->pci_dev); + addr -= proxy->addr; +if (addr < config) +return virtio_ioport_read (proxy, addr); + addr -= config;return virtio_config_readw (proxy->vdev, addr); }static uint32_t virtio_pci_config_readl (void *opaque,uint32_t addr) {VirtIOPCIProxy *proxy = opaque; - addr -= proxy->addr +VIRTIO_PCI_CONFIG ; +uint32_t config =VIRTIO_PCI_CONFIG (&proxy->pci_dev); + addr -= proxy->addr; +if (addr < config) +return virtio_ioport_read (proxy, addr); + addr -= config;return virtio_config_readl (proxy->vdev, addr); }static void virtio_pci_config_writeb (void *opaque,uint32_t addr,uint32_t val) {VirtIOPCIProxy *proxy = opaque; - addr -= proxy->addr +VIRTIO_PCI_CONFIG ; +uint32_t config =VIRTIO_PCI_CONFIG (&proxy->pci_dev); + addr -= proxy->addr; +if (addr < config) { +virtio_ioport_write (proxy, addr, val); +return ; + } + addr -= config;virtio_config_writeb (proxy->vdev, addr, val); }static void virtio_pci_config_writew (void *opaque,uint32_t addr,uint32_t val) {VirtIOPCIProxy *proxy = opaque; - addr -= proxy->addr +VIRTIO_PCI_CONFIG ; +uint32_t config =VIRTIO_PCI_CONFIG (&proxy->pci_dev); + addr -= proxy->addr; +if (addr < config) { +virtio_ioport_write (proxy, addr, val); +return ; + } + addr -= config;virtio_config_writew (proxy->vdev, addr, val); }static void virtio_pci_config_writel (void *opaque,uint32_t addr,uint32_t val) {VirtIOPCIProxy *proxy = opaque; - addr -= proxy->addr +VIRTIO_PCI_CONFIG ; +uint32_t config =VIRTIO_PCI_CONFIG (&proxy->pci_dev); + addr -= proxy->addr; +if (addr < config) { +virtio_ioport_write (proxy, addr, val); +return ; + } + addr -= config;virtio_config_writel (proxy->vdev, addr, val); }
@@ -223,32 +294,26 @@ static void virtio_map(PCIDevice *pci_dev, int region_num, {VirtIOPCIProxy *proxy =container_of (pci_dev, VirtIOPCIProxy, pci_dev);VirtIODevice *vdev = proxy->vdev; -int i; +unsigned config_len =VIRTIO_PCI_REGION_SIZE (pci_dev) + vdev->config_len; proxy->addr = addr; -for (i =0 ; i <3 ; i++) { -register_ioport_write (addr,VIRTIO_PCI_CONFIG ,1 << i, - virtio_ioport_write, proxy); -register_ioport_read (addr,VIRTIO_PCI_CONFIG ,1 << i, - virtio_ioport_read, proxy); - } -if (vdev->config_len) { -register_ioport_write (addr +VIRTIO_PCI_CONFIG , vdev->config_len,1 , - virtio_pci_config_writeb, proxy); -register_ioport_write (addr +VIRTIO_PCI_CONFIG , vdev->config_len,2 , - virtio_pci_config_writew, proxy); -register_ioport_write (addr +VIRTIO_PCI_CONFIG , vdev->config_len,4 , - virtio_pci_config_writel, proxy); -register_ioport_read (addr +VIRTIO_PCI_CONFIG , vdev->config_len,1 , - virtio_pci_config_readb, proxy); -register_ioport_read (addr +VIRTIO_PCI_CONFIG , vdev->config_len,2 , - virtio_pci_config_readw, proxy); -register_ioport_read (addr +VIRTIO_PCI_CONFIG , vdev->config_len,4 , - virtio_pci_config_readl, proxy); +register_ioport_write (addr, config_len,1 , virtio_pci_config_writeb, proxy); +register_ioport_write (addr, config_len,2 , virtio_pci_config_writew, proxy); +register_ioport_write (addr, config_len,4 , virtio_pci_config_writel, proxy); +register_ioport_read (addr, config_len,1 , virtio_pci_config_readb, proxy); +register_ioport_read (addr, config_len,2 , virtio_pci_config_readw, proxy); +register_ioport_read (addr, config_len,4 , virtio_pci_config_readl, proxy); +if (vdev->config_len) vdev->get_config (vdev, vdev->config); - } +} + +static void virtio_write_config (PCIDevice *pci_dev,uint32_t address, +uint32_t val,int len) +{ +pci_default_write_config (pci_dev, address, val, len); +msix_write_config (pci_dev, address, val, len); }static const VirtIOBindings virtio_pci_bindings = {
@@ -264,9 +329,6 @@ static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, proxy->vdev = vdev; -/* No support for multiple vectors yet. */ - proxy->vdev->nvectors =0 ; - config = proxy->pci_dev.config;pci_config_set_vendor_id (config, vendor);pci_config_set_device_id (config, device);
@@ -284,7 +346,17 @@ static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, config[0x3d ] =1 ; - size =20 + vdev->config_len; +if (vdev->nvectors && !msix_init (&proxy->pci_dev, vdev->nvectors,1 ,0 )) { +pci_register_bar (&proxy->pci_dev,1 , +msix_bar_size (&proxy->pci_dev), +PCI_ADDRESS_SPACE_MEM , + msix_mmio_map); + proxy->pci_dev.config_write = virtio_write_config; + proxy->pci_dev.unregister = msix_uninit; + }else + vdev->nvectors =0 ; + + size =VIRTIO_PCI_REGION_SIZE (&proxy->pci_dev) + vdev->config_len;if (size & (size-1 )) size =1 <<qemu_fls (size);
rules.mak +1 -1
@@ -1,6 +1,6 @@ %.o: %.c - $(call quiet-command,$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<," CC $(TARGET_DIR)$@") + $(call quiet-command,$(CC) $(CPPFLAGS) $(CFLAGS) -Werror -Wno-error=uninitialized -c -o $@ $<," CC $(TARGET_DIR)$@") %.o: %.S $(call quiet-command,$(CC) $(CPPFLAGS) -c -o $@ $<," AS $(TARGET_DIR)$@")
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 9/12 qemu/virtio: virtio save/load bindings
In reply to a message not in the archiveImplement bindings for virtio save/load. Use them in virtio pci.
--- hw/virtio-pci.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- hw/virtio.c | 33 ++++++++++++++++----------------- hw/virtio.h | 4 ++++ 3 files changed, 65 insertions(+), 18 deletions(-)
hw/virtio-pci.c +45 -1
@@ -105,6 +105,46 @@ static void virtio_pci_notify(void *opaque, uint16_t vector)qemu_set_irq (proxy->pci_dev.irq[0 ], proxy->vdev->isr &1 ); } +static void virtio_pci_save_config (void * opaque,QEMUFile *f) +{ +VirtIOPCIProxy *proxy = opaque; +pci_device_save (&proxy->pci_dev, f); +msix_save (&proxy->pci_dev, f); +if (msix_present (&proxy->pci_dev)) +qemu_put_be16 (f, proxy->vdev->config_vector); +} + +static void virtio_pci_save_queue (void * opaque,int n,QEMUFile *f) +{ +VirtIOPCIProxy *proxy = opaque; +if (msix_present (&proxy->pci_dev)) +qemu_put_be16 (f,virtio_queue_vector (proxy->vdev, n)); +} + +static int virtio_pci_load_config (void * opaque,QEMUFile *f) +{ +VirtIOPCIProxy *proxy = opaque; +int ret; + ret =pci_device_load (&proxy->pci_dev, f); +if (ret) +return ret; +msix_load (&proxy->pci_dev, f); +if (msix_present (&proxy->pci_dev)) +qemu_get_be16s (f, &proxy->vdev->config_vector); +return 0 ; +} + +static int virtio_pci_load_queue (void * opaque,int n,QEMUFile *f) +{ +VirtIOPCIProxy *proxy = opaque; +uint16_t vector; +if (!msix_present (&proxy->pci_dev)) +return 0 ; +qemu_get_be16s (f, &vector); +virtio_queue_set_vector (proxy->vdev, n, vector); +return 0 ; +} +static void virtio_pci_reset (void *opaque) {VirtIOPCIProxy *proxy = opaque;
@@ -317,7 +357,11 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, }static const VirtIOBindings virtio_pci_bindings = { - .notify = virtio_pci_notify + .notify = virtio_pci_notify, + .save_config = virtio_pci_save_config, + .load_config = virtio_pci_load_config, + .save_queue = virtio_pci_save_queue, + .load_queue = virtio_pci_load_queue, }; static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,
hw/virtio.c +16 -17
@@ -568,9 +568,8 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f) {int i; -/* FIXME: load/save binding. */ -//pci_device_save(&vdev->pci_dev, f); -//msix_save(&vdev->pci_dev, f); +if (vdev->binding->save_config) + vdev->binding->save_config (vdev->binding_opaque, f);qemu_put_8s (f, &vdev->status);qemu_put_8s (f, &vdev->isr);
@@ -596,18 +595,20 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f)qemu_put_be32 (f, vdev->vq[i].vring.num);qemu_put_be64 (f, vdev->vq[i].pa);qemu_put_be16s (f, &vdev->vq[i].last_avail_idx); -if (vdev->nvectors) -qemu_put_be16s (f, &vdev->vq[i].vector); +if (vdev->binding->save_queue) + vdev->binding->save_queue (vdev->binding_opaque, i, f); } } -void virtio_load (VirtIODevice *vdev,QEMUFile *f) +int virtio_load (VirtIODevice *vdev,QEMUFile *f) { -int num, i; +int num, i, ret; -/* FIXME: load/save binding. */ -//pci_device_load(&vdev->pci_dev, f); -//r = msix_load(&vdev->pci_dev, f); +if (vdev->binding->load_config) { + ret = vdev->binding->load_config (vdev->binding_opaque, f); +if (ret) +return ret; + }qemu_get_8s (f, &vdev->status);qemu_get_8s (f, &vdev->isr);
@@ -616,10 +617,6 @@ void virtio_load(VirtIODevice *vdev, QEMUFile *f) vdev->config_len =qemu_get_be32 (f);qemu_get_buffer (f, vdev->config, vdev->config_len); -if (vdev->nvectors) { -qemu_get_be16s (f, &vdev->config_vector); -//msix_vector_use(&vdev->pci_dev, vdev->config_vector); - } num =qemu_get_be32 (f);for (i =0 ; i < num; i++) {
@@ -630,13 +627,15 @@ void virtio_load(VirtIODevice *vdev, QEMUFile *f)if (vdev->vq[i].pa) {virtqueue_init (&vdev->vq[i]); } -if (vdev->nvectors) { -qemu_get_be16s (f, &vdev->vq[i].vector); -//msix_vector_use(&vdev->pci_dev, vdev->config_vector); +if (vdev->binding->load_queue) { + ret = vdev->binding->load_queue (vdev->binding_opaque, i, f); +if (ret) +return ret; } }virtio_notify_vector (vdev,VIRTIO_NO_VECTOR ); +return 0 ; }void virtio_cleanup (VirtIODevice *vdev)
hw/virtio.h +4 -0
@@ -72,6 +72,10 @@ typedef struct VirtQueueElementtypedef struct {void (*notify)(void * opaque,uint16_t vector); +void (*save_config)(void * opaque,QEMUFile *f); +void (*save_queue)(void * opaque,int n,QEMUFile *f); +int (*load_config)(void * opaque,QEMUFile *f); +int (*load_queue)(void * opaque,int n,QEMUFile *f); }VirtIOBindings ;#define VIRTIO_PCI_QUEUE_MAX 16
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 9/12 qemu/virtio: virtio save/load bindings
In reply to a message not in the archiveImplement bindings for virtio save/load. Use them in virtio pci.
--- hw/virtio-pci.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- hw/virtio.c | 33 ++++++++++++++++----------------- hw/virtio.h | 4 ++++ 3 files changed, 65 insertions(+), 18 deletions(-)
hw/virtio-pci.c +45 -1
@@ -105,6 +105,46 @@ static void virtio_pci_notify(void *opaque, uint16_t vector)qemu_set_irq (proxy->pci_dev.irq[0 ], proxy->vdev->isr &1 ); } +static void virtio_pci_save_config (void * opaque,QEMUFile *f) +{ +VirtIOPCIProxy *proxy = opaque; +pci_device_save (&proxy->pci_dev, f); +msix_save (&proxy->pci_dev, f); +if (msix_present (&proxy->pci_dev)) +qemu_put_be16 (f, proxy->vdev->config_vector); +} + +static void virtio_pci_save_queue (void * opaque,int n,QEMUFile *f) +{ +VirtIOPCIProxy *proxy = opaque; +if (msix_present (&proxy->pci_dev)) +qemu_put_be16 (f,virtio_queue_vector (proxy->vdev, n)); +} + +static int virtio_pci_load_config (void * opaque,QEMUFile *f) +{ +VirtIOPCIProxy *proxy = opaque; +int ret; + ret =pci_device_load (&proxy->pci_dev, f); +if (ret) +return ret; +msix_load (&proxy->pci_dev, f); +if (msix_present (&proxy->pci_dev)) +qemu_get_be16s (f, &proxy->vdev->config_vector); +return 0 ; +} + +static int virtio_pci_load_queue (void * opaque,int n,QEMUFile *f) +{ +VirtIOPCIProxy *proxy = opaque; +uint16_t vector; +if (!msix_present (&proxy->pci_dev)) +return 0 ; +qemu_get_be16s (f, &vector); +virtio_queue_set_vector (proxy->vdev, n, vector); +return 0 ; +} +static void virtio_pci_reset (void *opaque) {VirtIOPCIProxy *proxy = opaque;
@@ -317,7 +357,11 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, }static const VirtIOBindings virtio_pci_bindings = { - .notify = virtio_pci_notify + .notify = virtio_pci_notify, + .save_config = virtio_pci_save_config, + .load_config = virtio_pci_load_config, + .save_queue = virtio_pci_save_queue, + .load_queue = virtio_pci_load_queue, }; static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,
hw/virtio.c +16 -17
@@ -568,9 +568,8 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f) {int i; -/* FIXME: load/save binding. */ -//pci_device_save(&vdev->pci_dev, f); -//msix_save(&vdev->pci_dev, f); +if (vdev->binding->save_config) + vdev->binding->save_config (vdev->binding_opaque, f);qemu_put_8s (f, &vdev->status);qemu_put_8s (f, &vdev->isr);
@@ -596,18 +595,20 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f)qemu_put_be32 (f, vdev->vq[i].vring.num);qemu_put_be64 (f, vdev->vq[i].pa);qemu_put_be16s (f, &vdev->vq[i].last_avail_idx); -if (vdev->nvectors) -qemu_put_be16s (f, &vdev->vq[i].vector); +if (vdev->binding->save_queue) + vdev->binding->save_queue (vdev->binding_opaque, i, f); } } -void virtio_load (VirtIODevice *vdev,QEMUFile *f) +int virtio_load (VirtIODevice *vdev,QEMUFile *f) { -int num, i; +int num, i, ret; -/* FIXME: load/save binding. */ -//pci_device_load(&vdev->pci_dev, f); -//r = msix_load(&vdev->pci_dev, f); +if (vdev->binding->load_config) { + ret = vdev->binding->load_config (vdev->binding_opaque, f); +if (ret) +return ret; + }qemu_get_8s (f, &vdev->status);qemu_get_8s (f, &vdev->isr);
@@ -616,10 +617,6 @@ void virtio_load(VirtIODevice *vdev, QEMUFile *f) vdev->config_len =qemu_get_be32 (f);qemu_get_buffer (f, vdev->config, vdev->config_len); -if (vdev->nvectors) { -qemu_get_be16s (f, &vdev->config_vector); -//msix_vector_use(&vdev->pci_dev, vdev->config_vector); - } num =qemu_get_be32 (f);for (i =0 ; i < num; i++) {
@@ -630,13 +627,15 @@ void virtio_load(VirtIODevice *vdev, QEMUFile *f)if (vdev->vq[i].pa) {virtqueue_init (&vdev->vq[i]); } -if (vdev->nvectors) { -qemu_get_be16s (f, &vdev->vq[i].vector); -//msix_vector_use(&vdev->pci_dev, vdev->config_vector); +if (vdev->binding->load_queue) { + ret = vdev->binding->load_queue (vdev->binding_opaque, i, f); +if (ret) +return ret; } }virtio_notify_vector (vdev,VIRTIO_NO_VECTOR ); +return 0 ; }void virtio_cleanup (VirtIODevice *vdev)
hw/virtio.h +4 -0
@@ -72,6 +72,10 @@ typedef struct VirtQueueElementtypedef struct {void (*notify)(void * opaque,uint16_t vector); +void (*save_config)(void * opaque,QEMUFile *f); +void (*save_queue)(void * opaque,int n,QEMUFile *f); +int (*load_config)(void * opaque,QEMUFile *f); +int (*load_queue)(void * opaque,int n,QEMUFile *f); }VirtIOBindings ;#define VIRTIO_PCI_QUEUE_MAX 16
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 10/12 qemu/pci: add pci_get/set_byte
In reply to a message not in the archiveAdd pci_get/set_byte to keep *_word and *_long access functions company. They are unused for now.
--- hw/pci.h | 12 ++++++++++++ 1 files changed, 12 insertions(+), 0 deletions(-)
hw/pci.h +12 -0
@@ -260,6 +260,18 @@ PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did, pci_map_irq_fn map_irq, const char *name);static inline void +pci_set_byte (uint8_t *config,uint8_t val) +{ + *config = val; +} + +static inline uint8_t +pci_get_byte (uint8_t *config) +{ +return *config; +} + +static inline void pci_set_word (uint8_t *config,uint16_t val) {cpu_to_le16wu ((uint16_t *)config, val);
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 10/12 qemu/pci: add pci_get/set_byte
In reply to a message not in the archiveAdd pci_get/set_byte to keep *_word and *_long access functions company. They are unused for now.
--- hw/pci.h | 12 ++++++++++++ 1 files changed, 12 insertions(+), 0 deletions(-)
hw/pci.h +12 -0
@@ -260,6 +260,18 @@ PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did, pci_map_irq_fn map_irq, const char *name);static inline void +pci_set_byte (uint8_t *config,uint8_t val) +{ + *config = val; +} + +static inline uint8_t +pci_get_byte (uint8_t *config) +{ +return *config; +} + +static inline void pci_set_word (uint8_t *config,uint16_t val) {cpu_to_le16wu ((uint16_t *)config, val);
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 11/12 qemu/net: request 3 vectors in virtio-net
In reply to a message not in the archiveRequest up to 3 vectors in virtio-net. Actual bindings might supply less.
--- hw/virtio-net.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
hw/virtio-net.c +1 -0
@@ -709,6 +709,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev) n->mac_table.macs =qemu_mallocz (MAC_TABLE_ENTRIES *ETH_ALEN ); n->vlans =qemu_mallocz (MAX_VLAN >>3 ); + n->vdev.nvectors =3 ;register_savevm ("virtio-net" , virtio_net_id++,VIRTIO_NET_VM_VERSION , virtio_net_save, virtio_net_load, n);
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 11/12 qemu/net: request 3 vectors in virtio-net
In reply to a message not in the archiveRequest up to 3 vectors in virtio-net. Actual bindings might supply less.
--- hw/virtio-net.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
hw/virtio-net.c +1 -0
@@ -709,6 +709,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev) n->mac_table.macs =qemu_mallocz (MAC_TABLE_ENTRIES *ETH_ALEN ); n->vlans =qemu_mallocz (MAX_VLAN >>3 ); + n->vdev.nvectors =3 ;register_savevm ("virtio-net" , virtio_net_id++,VIRTIO_NET_VM_VERSION , virtio_net_save, virtio_net_load, n);
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 12/12 qemu/net: flag to control the number of vectors a nic has
In reply to a message not in the archiveAdd an option to specify the number of MSI-X vectors for PCI NIC cards. This can also be used to disable MSI-X, for compatibility with old qemu. This option currently only affects virtio cards.
--- hw/virtio-net.c | 5 ++++- net.c | 18 +++++++++++++++++- net.h | 4 ++++ qemu-options.hx | 14 ++++++++------ 4 files changed, 33 insertions(+), 8 deletions(-)
hw/virtio-net.c +4 -1
@@ -709,7 +709,10 @@ VirtIODevice *virtio_net_init(DeviceState *dev) n->mac_table.macs =qemu_mallocz (MAC_TABLE_ENTRIES *ETH_ALEN ); n->vlans =qemu_mallocz (MAX_VLAN >>3 ); - n->vdev.nvectors =3 ; +if (dev->nd->nvectors ==NIC_NVECTORS_UNSPECIFIED ) + n->vdev.nvectors =3 ; +else + n->vdev.nvectors = dev->nd->nvectors;register_savevm ("virtio-net" , virtio_net_id++,VIRTIO_NET_VM_VERSION , virtio_net_save, virtio_net_load, n);
net.c +17 -1
@@ -2103,7 +2103,7 @@ int net_client_init(Monitor *mon, const char *device, const char *p) }if (!strcmp (device,"nic" )) {static const char *const nic_params[] = { -"vlan" ,"name" ,"macaddr" ,"model" ,NULL +"vlan" ,"name" ,"macaddr" ,"model" ,"vectors" ,NULL };NICInfo *nd;uint8_t *macaddr;
@@ -2138,6 +2138,22 @@ int net_client_init(Monitor *mon, const char *device, const char *p)if (get_param_value (buf,sizeof (buf),"model" , p)) { nd->model =strdup (buf); } + nd->nvectors =NIC_NVECTORS_UNSPECIFIED ; +if (get_param_value (buf,sizeof (buf),"vectors" , p)) { +char *endptr; +long vectors =strtol (buf, &endptr,0 ); +if (*endptr) { +config_error (mon,"invalid syntax for # of vectors\n" ); + ret =-1 ; + goto out; + } +if (vectors <0 || vectors >0x7ffffff ) { +config_error (mon,"invalid # of vectors\n" ); + ret =-1 ; + goto out; + } + nd->nvectors = vectors; + } nd->vlan = vlan; nd->name = name; nd->used =1 ;
net.h +4 -0
@@ -83,6 +83,9 @@ int do_set_link(Monitor *mon, const char *name, const char *up_or_down);/* NIC info */ #define MAX_NICS 8 +enum { +NIC_NVECTORS_UNSPECIFIED =-1 +};struct NICInfo {uint8_t macaddr[6 ];
@@ -91,6 +94,7 @@ struct NICInfo {VLANState *vlan;void *private;int used; +int nvectors; };extern int nb_nics;
qemu-options.hx +8 -6
@@ -733,7 +733,7 @@ STEXI ETEXI DEF("net", HAS_ARG, QEMU_OPTION_net, - "-net nic[,vlan=n][,macaddr=addr][,model=type][,name=str]\n" + "-net nic[,vlan=n][,macaddr=addr][,model=type][,name=str][,vectors=v]\n" " create a new Network Interface Card and connect it to VLAN 'n'\n" #ifdef CONFIG_SLIRP "-net user[,vlan=n][,name=str][,hostname=host]\n"
@@ -767,15 +767,17 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, "-net none use it alone to have zero network devices; if no -net option\n" " is provided, the default is '-net nic -net user'\n") STEXI -@item -net nic[,vlan=@var{n}][,macaddr=@var{addr}][,model=@var{type}][,name=@var{name}] +@item -net nic[,vlan=@var{n}][,macaddr=@var{addr}][,model=@var{type}][,name=@var{name}][,vectors=@var{v}] Create a new Network Interface Card and connect it to VLAN @var{n} (@var{n} = 0 is the default). The NIC is an ne2k_pci by default on the PC target. Optionally, the MAC address can be changed to @var{addr} -and a @var{name} can be assigned for use in monitor commands. If no -@option{-net} option is specified, a single NIC is created. -Qemu can emulate several different models of network card. +and a @var{name} can be assigned for use in monitor commands. +Optionally, for PCI cards, you can specify the number @var{v} of MSI-X vectors +that the card should have; this option currently only affects virtio cards; set +@var{v} = 0 to disable MSI-X. If no @option{-net} option is specified, a single +NIC is created. Qemu can emulate several different models of network card. Valid values for @var{type} are -@code{i82551}, @code{i82557b}, @code{i82559er}, +@code{virtio}, @code{i82551}, @code{i82557b}, @code{i82559er}, @code{ne2k_pci}, @code{ne2k_isa}, @code{pcnet}, @code{rtl8139}, @code{e1000}, @code{smc91c111}, @code{lance} and @code{mcf_fec}. Not all devices are supported on all targets. Use -net nic,model=?
Signature · 1 line
1.6.2.2
Michael S. Tsirkin
maintainerPATCH v6 12/12 qemu/net: flag to control the number of vectors a nic has
In reply to a message not in the archiveAdd an option to specify the number of MSI-X vectors for PCI NIC cards. This can also be used to disable MSI-X, for compatibility with old qemu. This option currently only affects virtio cards.
--- hw/virtio-net.c | 5 ++++- net.c | 18 +++++++++++++++++- net.h | 4 ++++ qemu-options.hx | 14 ++++++++------ 4 files changed, 33 insertions(+), 8 deletions(-)
hw/virtio-net.c +4 -1
@@ -709,7 +709,10 @@ VirtIODevice *virtio_net_init(DeviceState *dev) n->mac_table.macs =qemu_mallocz (MAC_TABLE_ENTRIES *ETH_ALEN ); n->vlans =qemu_mallocz (MAX_VLAN >>3 ); - n->vdev.nvectors =3 ; +if (dev->nd->nvectors ==NIC_NVECTORS_UNSPECIFIED ) + n->vdev.nvectors =3 ; +else + n->vdev.nvectors = dev->nd->nvectors;register_savevm ("virtio-net" , virtio_net_id++,VIRTIO_NET_VM_VERSION , virtio_net_save, virtio_net_load, n);
net.c +17 -1
@@ -2103,7 +2103,7 @@ int net_client_init(Monitor *mon, const char *device, const char *p) }if (!strcmp (device,"nic" )) {static const char *const nic_params[] = { -"vlan" ,"name" ,"macaddr" ,"model" ,NULL +"vlan" ,"name" ,"macaddr" ,"model" ,"vectors" ,NULL };NICInfo *nd;uint8_t *macaddr;
@@ -2138,6 +2138,22 @@ int net_client_init(Monitor *mon, const char *device, const char *p)if (get_param_value (buf,sizeof (buf),"model" , p)) { nd->model =strdup (buf); } + nd->nvectors =NIC_NVECTORS_UNSPECIFIED ; +if (get_param_value (buf,sizeof (buf),"vectors" , p)) { +char *endptr; +long vectors =strtol (buf, &endptr,0 ); +if (*endptr) { +config_error (mon,"invalid syntax for # of vectors\n" ); + ret =-1 ; + goto out; + } +if (vectors <0 || vectors >0x7ffffff ) { +config_error (mon,"invalid # of vectors\n" ); + ret =-1 ; + goto out; + } + nd->nvectors = vectors; + } nd->vlan = vlan; nd->name = name; nd->used =1 ;
net.h +4 -0
@@ -83,6 +83,9 @@ int do_set_link(Monitor *mon, const char *name, const char *up_or_down);/* NIC info */ #define MAX_NICS 8 +enum { +NIC_NVECTORS_UNSPECIFIED =-1 +};struct NICInfo {uint8_t macaddr[6 ];
@@ -91,6 +94,7 @@ struct NICInfo {VLANState *vlan;void *private;int used; +int nvectors; };extern int nb_nics;
qemu-options.hx +8 -6
@@ -733,7 +733,7 @@ STEXI ETEXI DEF("net", HAS_ARG, QEMU_OPTION_net, - "-net nic[,vlan=n][,macaddr=addr][,model=type][,name=str]\n" + "-net nic[,vlan=n][,macaddr=addr][,model=type][,name=str][,vectors=v]\n" " create a new Network Interface Card and connect it to VLAN 'n'\n" #ifdef CONFIG_SLIRP "-net user[,vlan=n][,name=str][,hostname=host]\n"
@@ -767,15 +767,17 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, "-net none use it alone to have zero network devices; if no -net option\n" " is provided, the default is '-net nic -net user'\n") STEXI -@item -net nic[,vlan=@var{n}][,macaddr=@var{addr}][,model=@var{type}][,name=@var{name}] +@item -net nic[,vlan=@var{n}][,macaddr=@var{addr}][,model=@var{type}][,name=@var{name}][,vectors=@var{v}] Create a new Network Interface Card and connect it to VLAN @var{n} (@var{n} = 0 is the default). The NIC is an ne2k_pci by default on the PC target. Optionally, the MAC address can be changed to @var{addr} -and a @var{name} can be assigned for use in monitor commands. If no -@option{-net} option is specified, a single NIC is created. -Qemu can emulate several different models of network card. +and a @var{name} can be assigned for use in monitor commands. +Optionally, for PCI cards, you can specify the number @var{v} of MSI-X vectors +that the card should have; this option currently only affects virtio cards; set +@var{v} = 0 to disable MSI-X. If no @option{-net} option is specified, a single +NIC is created. Qemu can emulate several different models of network card. Valid values for @var{type} are -@code{i82551}, @code{i82557b}, @code{i82559er}, +@code{virtio}, @code{i82551}, @code{i82557b}, @code{i82559er}, @code{ne2k_pci}, @code{ne2k_isa}, @code{pcnet}, @code{rtl8139}, @code{e1000}, @code{smc91c111}, @code{lance} and @code{mcf_fec}. Not all devices are supported on all targets. Use -net nic,model=?
Signature · 1 line
1.6.2.2