PCI bus support
Subsystems (2)
v4 cover PCI bus support
Hi all,
This series should be applied on top of "Cleanup low-level arch code" series which is still not included. Yet, it is ready for review as all previous comments and suggestions are addressed.
There might be some confusion about version numbering as I posted the previous version as RFC with no version number at all. In fact it was 3rd version so I am labelling this series as v4. Unlike the RFC it does not have gaps in implementation.
There are quite a lot of changes since the previous version.
I tried pci-testdev against ARM and got the device semi-operational. It is still to investigate, but that could be addressed separately.
Most interesting - writing to IO BAR on ARM does not seem working as a written value does not read back. Probably, ARM64 is also affected, but again - I have not investigated it yet.
Alexander Gordeev (12):
pci: Fix coding style in generic PCI files
pci: x86: Rename pci_config_read() to pci_config_readl()
pci: x86: Add remaining PCI configuration space accessors
pci: Rework pci_bar_addr()
pci: Factor out pci_bar_get()
pci: Add pci_bar_set_addr()
pci: Add pci_dev_exists()
pci: Add pci_print()
pci: Add generic ECAM host support
arm/arm64: pci: Add PCI bus operation test
pci: Add pci-testdev PCI bus test device arm/arm64: pci: Add pci-testdev PCI device operation test
arm/Makefile.common | 7 +- arm/pci-test.c | 31 ++++++ arm/run | 7 +- lib/arm/asm/pci.h | 26 +++++ lib/arm64/asm/pci.h | 1 + lib/pci-host-generic.c | 294 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/pci-host-generic.h | 46 ++++++++ lib/pci-testdev.c | 184 +++++++++++++++++++++++++++++++ lib/pci.c | 194 ++++++++++++++++++++++++++++---- lib/pci.h | 33 +++++- lib/x86/asm/pci.h | 31 +++++- x86/vmexit.c | 4 +- 12 files changed, 830 insertions(+), 28 deletions(-) create mode 100644 arm/pci-test.c create mode 100644 lib/arm/asm/pci.h create mode 100644 lib/arm64/asm/pci.h create mode 100644 lib/pci-host-generic.c create mode 100644 lib/pci-host-generic.h create mode 100644 lib/pci-testdev.c
Signature · 1 line
1.8.3.1
PATCH v4 1/12 pci: Fix coding style in generic PCI files
--- lib/pci.c | 38 ++++++++++++++++++++------------------ lib/pci.h | 2 +- 2 files changed, 21 insertions(+), 19 deletions(-)
lib/pci.c +20 -18
@@ -10,34 +10,36 @@/* Scan bus look for a specific device. Only bus 0 scanned for now. */ pcidevaddr_t pci_find_dev (uint16_t vendor_id,uint16_t device_id) { -unsigned dev; -for (dev =0 ; dev <256 ; ++dev) { -uint32_t id =pci_config_read (dev,0 ); -if ((id &0xFFFF ) == vendor_id && (id >>16 ) == device_id) { -return dev; - } - } -return PCIDEVADDR_INVALID ; +pcidevaddr_t dev; + +for (dev =0 ; dev <256 ; ++dev) { +uint32_t id =pci_config_read (dev,0 ); + +if ((id &0xFFFF ) == vendor_id && (id >>16 ) == device_id) +return dev; + } + +return PCIDEVADDR_INVALID ; }unsigned long pci_bar_addr (pcidevaddr_t dev,int bar_num) { -uint32_t bar =pci_config_read (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); -if (bar &PCI_BASE_ADDRESS_SPACE_IO ) { -return bar &PCI_BASE_ADDRESS_IO_MASK ; - }else { -return bar &PCI_BASE_ADDRESS_MEM_MASK ; - } +uint32_t bar =pci_config_read (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); + +if (bar &PCI_BASE_ADDRESS_SPACE_IO ) +return bar &PCI_BASE_ADDRESS_IO_MASK ; +else +return bar &PCI_BASE_ADDRESS_MEM_MASK ; }bool pci_bar_is_memory (pcidevaddr_t dev,int bar_num) { -uint32_t bar =pci_config_read (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); -return !(bar &PCI_BASE_ADDRESS_SPACE_IO ); +uint32_t bar =pci_config_read (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +return !(bar &PCI_BASE_ADDRESS_SPACE_IO ); }bool pci_bar_is_valid (pcidevaddr_t dev,int bar_num) { -uint32_t bar =pci_config_read (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); -return bar; +uint32_t bar =pci_config_read (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +return bar; }
lib/pci.h +1 -1
@@ -12,7 +12,7 @@typedef uint16_t pcidevaddr_t ;enum { -PCIDEVADDR_INVALID =0xffff , +PCIDEVADDR_INVALID =0xffff , };pcidevaddr_t pci_find_dev (uint16_t vendor_id,uint16_t device_id);unsigned long pci_bar_addr (pcidevaddr_t dev,int bar_num);
Signature · 1 line
1.8.3.1
Re: [kvm-unit-tests PATCH v4 01/12] pci: Fix coding style in generic PCI files
Quoted 47 lines · Alexander Gordeev
Cc Thomas Huth <[email protected]>Cc Andrew Jones <[email protected]>Signed-off-by Alexander Gordeev <[email protected]>--- lib/pci.c | 38 ++++++++++++++++++++------------------ lib/pci.h | 2 +- 2 files changed, 21 insertions(+), 19 deletions(-)lib/pci.c +13
-14@@ -10,34 +10,36 @@/* Scan bus look for a specific device. Only bus 0 scanned for now. */ pcidevaddr_t pci_find_dev (uint16_t vendor_id,uint16_t device_id) { -unsigned dev; -for (dev =0 ; dev <256 ; ++dev) { -uint32_t id =pci_config_read (dev,0 ); -if ((id &0xFFFF ) == vendor_id && (id >>16 ) == device_id) { -return dev; - } - } -return PCIDEVADDR_INVALID ; +pcidevaddr_t dev; + +for (dev =0 ; dev <256 ; ++dev) { +uint32_t id =pci_config_read (dev,0 ); + +if ((id &0xFFFF ) == vendor_id && (id >>16 ) == device_id) +return dev; + } + +return PCIDEVADDR_INVALID ; }unsigned long pci_bar_addr (pcidevaddr_t dev,int bar_num) { -uint32_t bar =pci_config_read (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); -if (bar &PCI_BASE_ADDRESS_SPACE_IO ) { -return bar &PCI_BASE_ADDRESS_IO_MASK ; - }else { -return bar &PCI_BASE_ADDRESS_MEM_MASK ; - } +uint32_t bar =pci_config_read (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); + +if (bar &PCI_BASE_ADDRESS_SPACE_IO )
lib/pci.c line 37
Quoted 10 lines
lib/pci.c +4
-2+return bar &PCI_BASE_ADDRESS_IO_MASK ; + else + return bar & PCI_BASE_ADDRESS_MEM_MASK; }bool pci_bar_is_memory (pcidevaddr_t dev,int bar_num) { -uint32_t bar =pci_config_read (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); -return !(bar &PCI_BASE_ADDRESS_SPACE_IO ); +uint32_t bar =pci_config_read (dev,PCI_BASE_ADDRESS_0 + bar_num *4 );
no blank line here?
lib/pci.c line 44
Quoted 9 lines
lib/pci.c +3
-2+return !(bar &PCI_BASE_ADDRESS_SPACE_IO ); }bool pci_bar_is_valid (pcidevaddr_t dev,int bar_num) { -uint32_t bar =pci_config_read (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); -return bar; +uint32_t bar =pci_config_read (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +return bar;
can just do return pci_config_read(...)
Quoted 17 lines
lib/pci.c +0
-0}lib/pci.h +1
-1@@ -12,7 +12,7 @@typedef uint16_t pcidevaddr_t ;enum { -PCIDEVADDR_INVALID =0xffff , +PCIDEVADDR_INVALID =0xffff , };pcidevaddr_t pci_find_dev (uint16_t vendor_id,uint16_t device_id);unsigned long pci_bar_addr (pcidevaddr_t dev,int bar_num);Signature · 1 line
1.8.3.1
PATCH v4 2/12 pci: x86: Rename pci_config_read() to pci_config_readl()
In reply to Alexander Gordeev--- lib/pci.c | 8 ++++---- lib/x86/asm/pci.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-)
lib/pci.c +4 -4
@@ -13,7 +13,7 @@ pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id)pcidevaddr_t dev;for (dev =0 ; dev <256 ; ++dev) { -uint32_t id =pci_config_read (dev,0 ); +uint32_t id =pci_config_readl (dev,0 );if ((id &0xFFFF ) == vendor_id && (id >>16 ) == device_id)return dev;
@@ -24,7 +24,7 @@ pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id)unsigned long pci_bar_addr (pcidevaddr_t dev,int bar_num) { -uint32_t bar =pci_config_read (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 );if (bar &PCI_BASE_ADDRESS_SPACE_IO )return bar &PCI_BASE_ADDRESS_IO_MASK ;
@@ -34,12 +34,12 @@ unsigned long pci_bar_addr(pcidevaddr_t dev, int bar_num)bool pci_bar_is_memory (pcidevaddr_t dev,int bar_num) { -uint32_t bar =pci_config_read (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 );return !(bar &PCI_BASE_ADDRESS_SPACE_IO ); }bool pci_bar_is_valid (pcidevaddr_t dev,int bar_num) { -uint32_t bar =pci_config_read (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 );return bar; }
lib/x86/asm/pci.h +1 -1
@@ -9,7 +9,7 @@#include "pci.h" #include "x86/asm/io.h" -static inline uint32_t pci_config_read (pcidevaddr_t dev,uint8_t reg) +static inline uint32_t pci_config_readl (pcidevaddr_t dev,uint8_t reg) {uint32_t index = reg | (dev <<8 ) | (0x1 <<31 );outl (index,0xCF8 );
Signature · 1 line
1.8.3.1
PATCH v4 3/12 pci: x86: Add remaining PCI configuration space accessors
In reply to Alexander Gordeev--- lib/pci.c | 5 ++--- lib/x86/asm/pci.h | 23 +++++++++++++++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-)
lib/pci.c +2 -3
@@ -13,9 +13,8 @@ pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id)pcidevaddr_t dev;for (dev =0 ; dev <256 ; ++dev) { -uint32_t id =pci_config_readl (dev,0 ); - -if ((id &0xFFFF ) == vendor_id && (id >>16 ) == device_id) +if (pci_config_readw (dev,PCI_VENDOR_ID ) == vendor_id && +pci_config_readw (dev,PCI_DEVICE_ID ) == device_id)return dev; }
lib/x86/asm/pci.h +21 -2
@@ -9,11 +9,30 @@#include "pci.h" #include "x86/asm/io.h" +#define PCI_CONF1_ADDRESS (dev, reg) ((0x1 << 31) | (dev << 8) | reg) + +static inline uint8_t pci_config_readb (pcidevaddr_t dev,uint8_t reg) +{ +outl (PCI_CONF1_ADDRESS (dev, reg),0xCF8 ); +return inb (0xCFC ); +} + +static inline uint16_t pci_config_readw (pcidevaddr_t dev,uint8_t reg) +{ +outl (PCI_CONF1_ADDRESS (dev, reg),0xCF8 ); +return inw (0xCFC ); +} +static inline uint32_t pci_config_readl (pcidevaddr_t dev,uint8_t reg) { -uint32_t index = reg | (dev <<8 ) | (0x1 <<31 ); -outl (index,0xCF8 ); +outl (PCI_CONF1_ADDRESS (dev, reg),0xCF8 );return inl (0xCFC ); } +static inline void pci_config_writel (pcidevaddr_t dev,uint8_t reg,uint32_t val) +{ +outl (PCI_CONF1_ADDRESS (dev, reg),0xCF8 ); +outl (val,0xCFC ); +} +#endif
Signature · 1 line
1.8.3.1
Re: [kvm-unit-tests PATCH v4 03/12] pci: x86: Add remaining PCI configuration space accessors
Quoted 52 lines · Alexander Gordeev
Cc Thomas Huth <[email protected]>Cc Andrew Jones <[email protected]>Reviewed-by Andrew Jones <[email protected]>Signed-off-by Alexander Gordeev <[email protected]>--- lib/pci.c | 5 ++--- lib/x86/asm/pci.h | 23 +++++++++++++++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-)lib/pci.c +2
-3@@ -13,9 +13,8 @@ pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id)pcidevaddr_t dev;for (dev =0 ; dev <256 ; ++dev) { -uint32_t id =pci_config_readl (dev,0 ); - -if ((id &0xFFFF ) == vendor_id && (id >>16 ) == device_id) +if (pci_config_readw (dev,PCI_VENDOR_ID ) == vendor_id && +pci_config_readw (dev,PCI_DEVICE_ID ) == device_id)return dev; }lib/x86/asm/pci.h +15
-2@@ -9,11 +9,30 @@#include "pci.h" #include "x86/asm/io.h" +#define PCI_CONF1_ADDRESS (dev, reg) ((0x1 << 31) | (dev << 8) | reg) + +static inline uint8_t pci_config_readb (pcidevaddr_t dev,uint8_t reg) +{ +outl (PCI_CONF1_ADDRESS (dev, reg),0xCF8 ); +return inb (0xCFC ); +} + +static inline uint16_t pci_config_readw (pcidevaddr_t dev,uint8_t reg) +{ +outl (PCI_CONF1_ADDRESS (dev, reg),0xCF8 ); +return inw (0xCFC ); +} +static inline uint32_t pci_config_readl (pcidevaddr_t dev,uint8_t reg) { -uint32_t index = reg | (dev <<8 ) | (0x1 <<31 ); -outl (index,0xCF8 ); +outl (PCI_CONF1_ADDRESS (dev, reg),0xCF8 );
lib/x86/asm/pci.h line 38
Quoted 10 lines
lib/x86/asm/pci.h +6
-0return inl (0xCFC ); } +static inline void pci_config_writel (pcidevaddr_t dev,uint8_t reg,uint32_t val) +{ +outl (PCI_CONF1_ADDRESS (dev, reg),0xCF8 ); +outl (val,0xCFC ); +} +#endif
Maybe add a #define for that magic value 0xCF8, now that you use it multiple times?
Anyway,
Re: [kvm-unit-tests PATCH v4 03/12] pci: x86: Add remaining PCI configuration space accessors
Quoted 56 lines · Thomas Huth
Quoted 55 lines · Alexander Gordeev
Cc Thomas Huth <[email protected]>Cc Andrew Jones <[email protected]>Reviewed-by Andrew Jones <[email protected]>Signed-off-by Alexander Gordeev <[email protected]>--- lib/pci.c | 5 ++--- lib/x86/asm/pci.h | 23 +++++++++++++++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-)lib/pci.c +2
-3@@ -13,9 +13,8 @@ pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id)pcidevaddr_t dev;for (dev =0 ; dev <256 ; ++dev) { -uint32_t id =pci_config_readl (dev,0 ); - -if ((id &0xFFFF ) == vendor_id && (id >>16 ) == device_id) +if (pci_config_readw (dev,PCI_VENDOR_ID ) == vendor_id && +pci_config_readw (dev,PCI_DEVICE_ID ) == device_id)return dev; }lib/x86/asm/pci.h +15
-2@@ -9,11 +9,30 @@#include "pci.h" #include "x86/asm/io.h" +#define PCI_CONF1_ADDRESS (dev, reg) ((0x1 << 31) | (dev << 8) | reg) + +static inline uint8_t pci_config_readb (pcidevaddr_t dev,uint8_t reg) +{ +outl (PCI_CONF1_ADDRESS (dev, reg),0xCF8 ); +return inb (0xCFC ); +} + +static inline uint16_t pci_config_readw (pcidevaddr_t dev,uint8_t reg) +{ +outl (PCI_CONF1_ADDRESS (dev, reg),0xCF8 ); +return inw (0xCFC ); +} +static inline uint32_t pci_config_readl (pcidevaddr_t dev,uint8_t reg) { -uint32_t index = reg | (dev <<8 ) | (0x1 <<31 ); -outl (index,0xCF8 ); +outl (PCI_CONF1_ADDRESS (dev, reg),0xCF8 );return inl (0xCFC ); }
Quoted 10 lines
lib/x86/asm/pci.h line 38
Quoted 7 lines
lib/x86/asm/pci.h +6
-0+static inline void pci_config_writel (pcidevaddr_t dev,uint8_t reg,uint32_t val) +{ +outl (PCI_CONF1_ADDRESS (dev, reg),0xCF8 ); +outl (val,0xCFC ); +} +#endif Maybe add a #define for that magic value
0xCF8, now that you use it multiple times?
I used to an idea 0xCF8/0xCFC in x86 world are one of those well- known constant that people perceive better their values rather than any names :)
Andrew?
Quoted 4 lines
Anyway,
Reviewed-by Thomas Huth <[email protected]>
Re: [kvm-unit-tests PATCH v4 03/12] pci: x86: Add remaining PCI configuration space accessors
Quoted 68 lines · Alexander Gordeev
Quoted 56 lines · Thomas Huth
Quoted 55 lines · Alexander Gordeev
Cc Thomas Huth <[email protected]>Cc Andrew Jones <[email protected]>Reviewed-by Andrew Jones <[email protected]>Signed-off-by Alexander Gordeev <[email protected]>--- lib/pci.c | 5 ++--- lib/x86/asm/pci.h | 23 +++++++++++++++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-)lib/pci.c +2
-3@@ -13,9 +13,8 @@ pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id)pcidevaddr_t dev;for (dev =0 ; dev <256 ; ++dev) { -uint32_t id =pci_config_readl (dev,0 ); - -if ((id &0xFFFF ) == vendor_id && (id >>16 ) == device_id) +if (pci_config_readw (dev,PCI_VENDOR_ID ) == vendor_id && +pci_config_readw (dev,PCI_DEVICE_ID ) == device_id)return dev; }lib/x86/asm/pci.h +15
-2@@ -9,11 +9,30 @@#include "pci.h" #include "x86/asm/io.h" +#define PCI_CONF1_ADDRESS (dev, reg) ((0x1 << 31) | (dev << 8) | reg) + +static inline uint8_t pci_config_readb (pcidevaddr_t dev,uint8_t reg) +{ +outl (PCI_CONF1_ADDRESS (dev, reg),0xCF8 ); +return inb (0xCFC ); +} + +static inline uint16_t pci_config_readw (pcidevaddr_t dev,uint8_t reg) +{ +outl (PCI_CONF1_ADDRESS (dev, reg),0xCF8 ); +return inw (0xCFC ); +} +static inline uint32_t pci_config_readl (pcidevaddr_t dev,uint8_t reg) { -uint32_t index = reg | (dev <<8 ) | (0x1 <<31 ); -outl (index,0xCF8 ); +outl (PCI_CONF1_ADDRESS (dev, reg),0xCF8 );return inl (0xCFC ); }Quoted 10 lines
lib/x86/asm/pci.h line 38
Quoted 7 lines
lib/x86/asm/pci.h +6
-0+static inline void pci_config_writel (pcidevaddr_t dev,uint8_t reg,uint32_t val) +{ +outl (PCI_CONF1_ADDRESS (dev, reg),0xCF8 ); +outl (val,0xCFC ); +} +#endif Maybe add a #define for that magic value
0xCF8, now that you use it multiple times?
Quoted 5 lines
I used to an idea 0xCF8/0xCFC in x86 world are one of those well- known constant that people perceive better their values rather than any names :)
Andrew?
I agree with Alex. I think CF8 and CFC are effectively the names now in the PC world :-)
Quoted 9 lines
Quoted 5 lines
Anyway,
Reviewed-by Thomas Huth <[email protected]>--
To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v4 4/12 pci: Rework pci_bar_addr()
In reply to Alexander GordeevThis update makes pci_bar_addr() interface 64 bit BARs aware and introduces a concept of PCI address translation.
An architecutre should implement pci_translate_addr() interface in order to provide mapping between PCI bus address and CPU physical address.
--- lib/pci.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++----- lib/pci.h | 16 +++++++++++++- lib/x86/asm/pci.h | 6 +++++ 3 files changed, 82 insertions(+), 6 deletions(-)
lib/pci.c +61 -5
@@ -21,14 +21,59 @@ pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id)return PCIDEVADDR_INVALID ; } -unsigned long pci_bar_addr (pcidevaddr_t dev,int bar_num) +static uint32_t pci_bar_mask (uint32_t bar) +{ +return (bar &PCI_BASE_ADDRESS_SPACE_IO ) ? +PCI_BASE_ADDRESS_IO_MASK :PCI_BASE_ADDRESS_MEM_MASK ; +} + +phys_addr_t pci_bar_addr (pcidevaddr_t dev,int bar_num) +{ +int off =PCI_BASE_ADDRESS_0 + bar_num *4 ; +phys_addr_t bar =pci_config_readl (dev, off); +phys_addr_t mask = (int32_t )pci_bar_mask (bar); + +if (pci_bar_is64 (dev, bar_num)) + bar |= (phys_addr_t )pci_config_readl (dev, off +4 ) <<32 ; + +return pci_translate_addr (dev, bar & mask); +} + +/* +* To determine the amount of address space needed by a PCI device, +* one must save the original value of the BAR, write a value of +* all 1's to the register, then read it back. The amount of memory +* can then be then determined by masking the information bits, +* performing a bitwise NOT and incrementing the value by 1. +* +* The following pci_bar_size32() and pci_bar_size() functions do +* the described algorithm. +*/ +static uint32_t pci_bar_size32 (pcidevaddr_t dev,int bar_num) +{ +int off =PCI_BASE_ADDRESS_0 + bar_num *4 ; +uint32_t bar, size; + + bar =pci_config_readl (dev, off); +pci_config_writel (dev, off, ~0u ); + size =pci_config_readl (dev, off); +pci_config_writel (dev, off, bar); + +return size; +} + +phys_addr_t pci_bar_size (pcidevaddr_t dev,int bar_num) {uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +phys_addr_t size = (int32_t )pci_bar_size32 (dev, bar_num); +phys_addr_t mask = (int32_t )pci_bar_mask (bar); -if (bar &PCI_BASE_ADDRESS_SPACE_IO ) -return bar &PCI_BASE_ADDRESS_IO_MASK ; -else -return bar &PCI_BASE_ADDRESS_MEM_MASK ; +if (pci_bar_is64 (dev, bar_num)) { +uint32_t size_high =pci_bar_size32 (dev, bar_num +1 ); + size = ((phys_addr_t )size_high <<32 ) | (uint32_t )size; + } + +return (~(size & mask)) +1 ; }bool pci_bar_is_memory (pcidevaddr_t dev,int bar_num)
@@ -42,3 +87,14 @@ bool pci_bar_is_valid(pcidevaddr_t dev, int bar_num)uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 );return bar; } + +bool pci_bar_is64 (pcidevaddr_t dev,int bar_num) +{ +uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); + +if (bar &PCI_BASE_ADDRESS_SPACE_IO ) +return false; + +return (bar &PCI_BASE_ADDRESS_MEM_TYPE_MASK ) == +PCI_BASE_ADDRESS_MEM_TYPE_64 ; +}
lib/pci.h +15 -1
@@ -15,7 +15,21 @@ enum { PCIDEVADDR_INVALID = 0xffff, };pcidevaddr_t pci_find_dev (uint16_t vendor_id,uint16_t device_id); -unsigned long pci_bar_addr (pcidevaddr_t dev,int bar_num); + +/* +* BAR number in all BAR access functions below is a number of 32-bit +* register starting from PCI_BASE_ADDRESS_0 offset. +* +* In cases BAR size is 64-bit a caller should still provide BAR number +* in terms of 32-bit words. For example, if a device has 64-bit BAR#0 +* and 32-bit BAR#1 the caller should provide 2 to address BAR#1, not 1. +* +* It is expected the caller is aware of the device BAR layout and never +* tries to address in the middle of a 64-bit register. +*/ +phys_addr_t pci_bar_addr (pcidevaddr_t dev,int bar_num); +phys_addr_t pci_bar_size (pcidevaddr_t dev,int bar_num); +bool pci_bar_is64 (pcidevaddr_t dev,int bar_num);bool pci_bar_is_memory (pcidevaddr_t dev,int bar_num);bool pci_bar_is_valid (pcidevaddr_t dev,int bar_num);
lib/x86/asm/pci.h +6 -0
@@ -35,4 +35,10 @@ static inline void pci_config_writel(pcidevaddr_t dev, uint8_t reg, uint32_t valoutl (val,0xCFC ); } +static inline +phys_addr_t pci_translate_addr(pcidevaddr_t __unused dev, uint64_t addr) +{ +return addr; +} +#endif
Signature · 1 line
1.8.3.1
Re: [kvm-unit-tests PATCH v4 04/12] pci: Rework pci_bar_addr()
Quoted 26 lines · Alexander Gordeev
This update makes
pci_bar_addr()interface 64 bit BARs aware and introduces a concept of PCI address translation.An architecutre should implement
pci_translate_addr()interface in order to provide mapping between PCI bus address and CPU physical address.Cc Thomas Huth <[email protected]>Cc Andrew Jones <[email protected]>Signed-off-by Alexander Gordeev <[email protected]>--- lib/pci.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++----- lib/pci.h | 16 +++++++++++++- lib/x86/asm/pci.h | 6 +++++ 3 files changed, 82 insertions(+), 6 deletions(-)lib/pci.c +1
-1@@ -21,14 +21,59 @@ pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id)return PCIDEVADDR_INVALID ; } -unsigned long pci_bar_addr (pcidevaddr_t dev,int bar_num) +static uint32_t pci_bar_mask (uint32_t bar)
lib/pci.c pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id) · line 34
Quoted 10 lines
lib/pci.c +10
-0+{ +return (bar &PCI_BASE_ADDRESS_SPACE_IO ) ? +PCI_BASE_ADDRESS_IO_MASK :PCI_BASE_ADDRESS_MEM_MASK ; +} + +phys_addr_t pci_bar_addr (pcidevaddr_t dev,int bar_num) +{ +int off =PCI_BASE_ADDRESS_0 + bar_num *4 ; +phys_addr_t bar =pci_config_readl (dev, off); +phys_addr_t mask = (int32_t )pci_bar_mask (bar);
This sign-extension trick is too subtle, IMO, and not really necessary. We only need to apply the mask to the lower half of a 64-bit bar, i.e.
phys_addr_t bar = pci_config_readl(dev, off);
uint32_t mask = pci_bar_mask(bar);
bar &= mask;
if (pci_bar_is64(dev, bar_num))
bar |= (phys_addr_t)pci_config_readl(dev, off + 4) << 32;
return pci_translate_addr(dev, bar);
Quoted 36 lines
lib/pci.c +33
-0+ +if (pci_bar_is64 (dev, bar_num)) + bar |= (phys_addr_t )pci_config_readl (dev, off +4 ) <<32 ; + +return pci_translate_addr (dev, bar & mask); +} + +/* +* To determine the amount of address space needed by a PCI device, +* one must save the original value of the BAR, write a value of +* all 1's to the register, then read it back. The amount of memory +* can then be then determined by masking the information bits, +* performing a bitwise NOT and incrementing the value by 1. +* +* The following pci_bar_size32() and pci_bar_size() functions do +* the described algorithm. +*/ +static uint32_t pci_bar_size32 (pcidevaddr_t dev,int bar_num) +{ +int off =PCI_BASE_ADDRESS_0 + bar_num *4 ; +uint32_t bar, size; + + bar =pci_config_readl (dev, off); +pci_config_writel (dev, off, ~0u ); + size =pci_config_readl (dev, off); +pci_config_writel (dev, off, bar); + +return size; +} + +phys_addr_t pci_bar_size (pcidevaddr_t dev,int bar_num) {uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +phys_addr_t size = (int32_t )pci_bar_size32 (dev, bar_num); +phys_addr_t mask = (int32_t )pci_bar_mask (bar);
lib/pci.c pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id) · line 76
Quoted 10 lines
lib/pci.c +6
-4-if (bar &PCI_BASE_ADDRESS_SPACE_IO ) -return bar &PCI_BASE_ADDRESS_IO_MASK ; -else -return bar &PCI_BASE_ADDRESS_MEM_MASK ; +if (pci_bar_is64 (dev, bar_num)) { +uint32_t size_high =pci_bar_size32 (dev, bar_num +1 ); + size = ((phys_addr_t )size_high <<32 ) | (uint32_t )size; + } + +return (~(size & mask)) +1 ;
All this casting of size and mask is pointless. Please rework it similar to what I did above.
Quoted 25 lines
lib/pci.c +11
-0}bool pci_bar_is_memory (pcidevaddr_t dev,int bar_num)@@ -42,3 +87,14 @@ bool pci_bar_is_valid(pcidevaddr_t dev, int bar_num)uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 );return bar; } + +bool pci_bar_is64 (pcidevaddr_t dev,int bar_num) +{ +uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); + +if (bar &PCI_BASE_ADDRESS_SPACE_IO ) +return false; + +return (bar &PCI_BASE_ADDRESS_MEM_TYPE_MASK ) == +PCI_BASE_ADDRESS_MEM_TYPE_64 ; +}lib/pci.h +0
-0@@ -15,7 +15,21 @@ enum { PCIDEVADDR_INVALID = 0xffff, };
lib/pci.h enum { · line 25
Quoted 10 lines
lib/pci.h +8
-1pcidevaddr_t pci_find_dev (uint16_t vendor_id,uint16_t device_id); -unsigned long pci_bar_addr (pcidevaddr_t dev,int bar_num); + +/* +* BAR number in all BAR access functions below is a number of 32-bit +* register starting from PCI_BASE_ADDRESS_0 offset. +* +* In cases BAR size is 64-bit a caller should still provide BAR number +* in terms of 32-bit words. For example, if a device has 64-bit BAR#0 +* and 32-bit BAR#1 the caller should provide 2 to address BAR#1, not 1.
Is this how people label bars? I.e. they call a bar following a 64-bit bar BAR#N+1? Or do they skip numbers with the labels to match the offsets? I.e. in this example you'd say BAR#0 (64-bit), there is no BAR#1, and then BAR#2 (32-bit) when labeling?
Quoted 15 lines
lib/pci.h +7
-0+* +* It is expected the caller is aware of the device BAR layout and never +* tries to address in the middle of a 64-bit register. +*/ +phys_addr_t pci_bar_addr (pcidevaddr_t dev,int bar_num); +phys_addr_t pci_bar_size (pcidevaddr_t dev,int bar_num); +bool pci_bar_is64 (pcidevaddr_t dev,int bar_num);bool pci_bar_is_memory (pcidevaddr_t dev,int bar_num);bool pci_bar_is_valid (pcidevaddr_t dev,int bar_num);lib/x86/asm/pci.h +0
-0
lib/x86/asm/pci.h static inline void pci_config_writel(pcidevaddr_t dev, uint8_t reg, uint32_t val · line 39
Quoted 5 lines
lib/x86/asm/pci.h +2
-0@@ -35,4 +35,10 @@ static inline void pci_config_writel(pcidevaddr_t dev, uint8_t reg, uint32_t valoutl (val,0xCFC ); } +static inline +phys_addr_t pci_translate_addr(pcidevaddr_t __unused dev, uint64_t addr)
I put the __unused after the argument name.
lib/x86/asm/pci.h static inline void pci_config_writel(pcidevaddr_t dev, uint8_t reg, uint32_t val · line 41
Quoted 2 lines
lib/x86/asm/pci.h +2
-0+{ +return addr;
Need tab here.
Quoted 6 lines
lib/x86/asm/pci.h +2
-0+} +#endif Signature · 1 line
1.8.3.1
Thanks,
drew
Re: [kvm-unit-tests PATCH v4 04/12] pci: Rework pci_bar_addr()
Quoted 6 lines · Andrew Jones
Quoted 6 lines
lib/pci.c +3
-0+phys_addr_t pci_bar_size (pcidevaddr_t dev,int bar_num) {uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +phys_addr_t size = (int32_t )pci_bar_size32 (dev, bar_num); +phys_addr_t mask = (int32_t )pci_bar_mask (bar);
Quoted 13 lines
lib/pci.c pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id) · line 76
Quoted 10 lines
lib/pci.c +6
-4-if (bar &PCI_BASE_ADDRESS_SPACE_IO ) -return bar &PCI_BASE_ADDRESS_IO_MASK ; -else -return bar &PCI_BASE_ADDRESS_MEM_MASK ; +if (pci_bar_is64 (dev, bar_num)) { +uint32_t size_high =pci_bar_size32 (dev, bar_num +1 ); + size = ((phys_addr_t )size_high <<32 ) | (uint32_t )size; + } + +return (~(size & mask)) +1 ;All this casting of size and mask is pointless. Please rework it similar to what I did above.
This is the most compact and straight variant I was able to come up with:
uint32_t bar = pci_bar_get(dev, bar_num); phys_addr_t size = (int32_t)pci_bar_size32(dev, bar_num); phys_addr_t mask = (int32_t)pci_bar_mask(bar); if (pci_bar_is64(dev, bar_num)) size |= (phys_addr_t)pci_bar_size32(dev, bar_num + 1) << 32; return (~(size & mask)) + 1;
The casting is needed to avoid putting explicitly all 1s into higher bits of size and/or mask. Otherwise (~(size & mask)) + 1 expression would not bring correct results. I really struggle to make something better readable.
Quoted 2 lines
Thanks,
drew
Re: [kvm-unit-tests PATCH v4 04/12] pci: Rework pci_bar_addr()
Quoted 21 lines · Alexander Gordeev
Quoted 6 lines · Andrew Jones
Quoted 6 lines
lib/pci.c +3
-0+phys_addr_t pci_bar_size (pcidevaddr_t dev,int bar_num) {uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +phys_addr_t size = (int32_t )pci_bar_size32 (dev, bar_num); +phys_addr_t mask = (int32_t )pci_bar_mask (bar);Quoted 13 lines
lib/pci.c pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id) · line 76
Quoted 10 lines
lib/pci.c +6
-4-if (bar &PCI_BASE_ADDRESS_SPACE_IO ) -return bar &PCI_BASE_ADDRESS_IO_MASK ; -else -return bar &PCI_BASE_ADDRESS_MEM_MASK ; +if (pci_bar_is64 (dev, bar_num)) { +uint32_t size_high =pci_bar_size32 (dev, bar_num +1 ); + size = ((phys_addr_t )size_high <<32 ) | (uint32_t )size; + } + +return (~(size & mask)) +1 ;All this casting of size and mask is pointless. Please rework it similar to what I did above.
Quoted 4 lines
This is the most compact and straight variant I was able to come up with:
uint32_t bar = pci_bar_get(dev, bar_num); phys_addr_t size = (int32_t)pci_bar_size32(dev, bar_num);
But this is wrong. If your 32-bit size was 0x80000000, then you now say it's 0xffffffff80000000.
Quoted 1 line
phys_addr_t mask = (int32_t)pci_bar_mask(bar);
It might be OK to do that here, on a mask, but even if it is, then I don't like it, because it's too subtle (like I said for the casting in pci_bar_addr in my last reply). I don't like that it requires us to know that masking bit 31 in a 32-bit mask means we also want to mask 63..32. That should at least be in a comment somewhere.
Quoted 10 lines
if (pci_bar_is64(dev, bar_num)) size |= (phys_addr_t)pci_bar_size32(dev, bar_num + 1) << 32; return (~(size & mask)) + 1;The casting is needed to avoid putting explicitly all 1s into higher bits of size and/or mask. Otherwise (~(size & mask)) + 1 expression would not bring correct results. I really struggle to make something better readable.
It's not just about readability, it's about correctness. You shouldn't use 32-bit sizes/masks with 64-bit data this way. Like I said in the last reply, you should rework it like I showed you, operate on the 32-bit data with the 32-bit mask/size, and then eventually construct 64-bit data.
Thanks,
drew
Re: [kvm-unit-tests PATCH v4 04/12] pci: Rework pci_bar_addr()
Quoted 22 lines · Andrew Jones
Quoted 21 lines · Alexander Gordeev
Quoted 19 lines · Andrew Jones
Quoted 6 lines
lib/pci.c +3
-0+phys_addr_t pci_bar_size (pcidevaddr_t dev,int bar_num) {uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +phys_addr_t size = (int32_t )pci_bar_size32 (dev, bar_num); +phys_addr_t mask = (int32_t )pci_bar_mask (bar);lib/pci.c pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id) · line 76
Quoted 10 lines
lib/pci.c +6
-4-if (bar &PCI_BASE_ADDRESS_SPACE_IO ) -return bar &PCI_BASE_ADDRESS_IO_MASK ; -else -return bar &PCI_BASE_ADDRESS_MEM_MASK ; +if (pci_bar_is64 (dev, bar_num)) { +uint32_t size_high =pci_bar_size32 (dev, bar_num +1 ); + size = ((phys_addr_t )size_high <<32 ) | (uint32_t )size; + } + +return (~(size & mask)) +1 ;All this casting of size and mask is pointless. Please rework it similar to what I did above.
Quoted 7 lines
Quoted 4 lines
This is the most compact and straight variant I was able to come up with:
uint32_t bar = pci_bar_get(dev, bar_num); phys_addr_t size = (int32_t)pci_bar_size32(dev, bar_num);But this is wrong. If your 32-bit size was
0x80000000, then you now say it's0xffffffff80000000.
Hmm.. I am either terribly missing the point or we are on different pages.
So if pci_bar_size32() returned 0x80000000 the size sign-extension gives 0xffffffff80000000 and the mask sign extension gives (i.e. mmio) 0xfffffffffffffff0. The AND gives 0xffffffff80000000 and the NOT gives 0x7fffffff. Finally, 0x7fffffff + 1 gives 0x80000000.
If we do not sign-extend (or explicitly OR with 0xffffffff00000000) size and/or mask then the return(~(size & mask)) + 1 gives a wrong 0xffffffff80000000.
Quoted 7 lines
Quoted 1 line
phys_addr_t mask = (int32_t)pci_bar_mask(bar);It might be OK to do that here, on a mask, but even if it is, then I don't like it, because it's too subtle (like I said for the casting in
pci_bar_addrin my last reply). I don't like that it requires us to know that masking bit 31 in a 32-bit mask means we also want to mask 63..32. That should at least be in a comment somewhere.
Yes, but it is not an arbitrary mask, it is an alignment mask. We unconditionally want to mask 63..32, 31th and even lower.
Quoted 10 lines
Quoted 9 lines
if (pci_bar_is64(dev, bar_num)) size |= (phys_addr_t)pci_bar_size32(dev, bar_num + 1) << 32; return (~(size & mask)) + 1;The casting is needed to avoid putting explicitly all 1s into higher bits of size and/or mask. Otherwise (~(size & mask)) + 1 expression would not bring correct results. I really struggle to make something better readable.
Quoted 5 lines
It's not just about readability, it's about correctness. You shouldn't use 32-bit sizes/masks with 64-bit data this way. Like I said in the last reply, you should rework it like I showed you, operate on the 32-bit data with the 32-bit mask/size, and then eventually construct 64-bit data.
It is all about those last +1 and shifting bits from lower to upper word of 64-bit value. My hope was to off-load it to the compiler :)
Quoted 2 lines
Thanks,
drew
Re: [kvm-unit-tests PATCH v4 04/12] pci: Rework pci_bar_addr()
Quoted 10 lines · Alexander Gordeev
Quoted 7 lines
Quoted 1 line
phys_addr_t mask = (int32_t)pci_bar_mask(bar);It might be OK to do that here, on a mask, but even if it is, then I don't like it, because it's too subtle (like I said for the casting in
pci_bar_addrin my last reply). I don't like that it requires us to know that masking bit 31 in a 32-bit mask means we also want to mask 63..32. That should at least be in a comment somewhere.Yes, but it is not an arbitrary mask, it is an alignment mask. We unconditionally want to mask 63..32, 31th and even lower.
Seems like making pci_bar_mask() return phys_addr_t rather than uint32_t would make more sense.
Quoted 21 lines
Quoted 10 lines
Quoted 9 lines
if (pci_bar_is64(dev, bar_num)) size |= (phys_addr_t)pci_bar_size32(dev, bar_num + 1) << 32; return (~(size & mask)) + 1;The casting is needed to avoid putting explicitly all 1s into higher bits of size and/or mask. Otherwise (~(size & mask)) + 1 expression would not bring correct results. I really struggle to make something better readable.
Quoted 5 lines
It's not just about readability, it's about correctness. You shouldn't use 32-bit sizes/masks with 64-bit data this way. Like I said in the last reply, you should rework it like I showed you, operate on the 32-bit data with the 32-bit mask/size, and then eventually construct 64-bit data.
It is all about those last +1 and shifting bits from lower to upper word of 64-bit value. My hope was to off-load it to the compiler :)
Quoted 2 lines
Thanks,
drew
Re: [kvm-unit-tests PATCH v4 04/12] pci: Rework pci_bar_addr()
Quoted 9 lines · Alexander Gordeev
Quoted 8 lines · Alexander Gordeev
Quoted 7 lines
Quoted 1 line
phys_addr_t mask = (int32_t)pci_bar_mask(bar);It might be OK to do that here, on a mask, but even if it is, then I don't like it, because it's too subtle (like I said for the casting in
pci_bar_addrin my last reply). I don't like that it requires us to know that masking bit 31 in a 32-bit mask means we also want to mask 63..32. That should at least be in a comment somewhere.
Quoted 5 lines
Quoted 2 lines
Yes, but it is not an arbitrary mask, it is an alignment mask. We unconditionally want to mask 63..32, 31th and even lower.
Seems like making
pci_bar_mask()returnphys_addr_trather thanuint32_twould make more sense.
See last reply. If a BAR mask is suppose to operate on a BAR (4 bytes) then it should be 32-bits and not applied to a 64-bit operation. And, as the upper 32-bits of a 64-bit bar don't need any masking anyway, then there's no point in complicating things by attempting to mask it.
Thanks,
drew
Re: [kvm-unit-tests PATCH v4 04/12] pci: Rework pci_bar_addr()
In reply to Alexander GordeevQuoted 25 lines · Alexander Gordeev
Quoted 24 lines · Andrew Jones
Quoted 23 lines · Alexander Gordeev
Quoted 6 lines · Andrew Jones
Quoted 6 lines
lib/pci.c +3
-0+phys_addr_t pci_bar_size (pcidevaddr_t dev,int bar_num) {uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +phys_addr_t size = (int32_t )pci_bar_size32 (dev, bar_num); +phys_addr_t mask = (int32_t )pci_bar_mask (bar);Quoted 13 lines
lib/pci.c pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id) · line 76
Quoted 10 lines
lib/pci.c +6
-4-if (bar &PCI_BASE_ADDRESS_SPACE_IO ) -return bar &PCI_BASE_ADDRESS_IO_MASK ; -else -return bar &PCI_BASE_ADDRESS_MEM_MASK ; +if (pci_bar_is64 (dev, bar_num)) { +uint32_t size_high =pci_bar_size32 (dev, bar_num +1 ); + size = ((phys_addr_t )size_high <<32 ) | (uint32_t )size; + } + +return (~(size & mask)) +1 ;All this casting of size and mask is pointless. Please rework it similar to what I did above.
This is the most compact and straight variant I was able to come up with:
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
uint32_t bar = pci_bar_get(dev, bar_num); phys_addr_t size = (int32_t)pci_bar_size32(dev, bar_num);But this is wrong. If your 32-bit size was
0x80000000, then you now say it's0xffffffff80000000.Hmm.. I am either terribly missing the point or we are on different pages.
Let's make sure I know what the function is supposed to do.
Based on your comment explaining how to get the PCI bar size, it sounds like we should do these steps; let's assume a device needs size=0x200000, and is 32-bit
read-write1s-dance barsz=0x00000000
readl barsz=0xffe0000c
mask barsz=0xffe00000
barsz = ~barsz + 1 barsz=0x00200000
Now let's say it's 64-bit
read-write1s-dance barsz_lo=0x00000000
readl barsz_lo=0xffe0000c
mask barsz_lo=0xffe00000
read-write1s-dance barsz_hi=0x00000000
readl barsz_hi=0xffffffff
<upper 32-bit mask is 0xffffffff, so don't bother masking at all>
barsz = ~((phys_addr_t)barsz_hi << 32 | barsz_lo) + 1 (barsz=0x200000)
So you need 5 functions
uint32_t get_mask(int bar)
{
...
}
uint32_t read_dance(int bar)
{
read-write1s-dance
return readl
}
uint32_t get_size32(int bar)
{
uint32_t size = read_dance(bar) & get_mask(bar); return ~size + 1;
}
uint64_t get_size64(int bar)
{
uint64_t size = read_dance(bar) & get_mask(bar); uint64_t size_hi = read_dance(bar + 1);
size |= size_hi << 32;
return ~size + 1;
}
uint64_t get_size(int bar)
{
return bar_size(bar) == BAR64 ? get_size64(bar) : get_size32(bar);
}
Quoted 5 lines
So if
pci_bar_size32()returned0x80000000the size sign-extension gives0xffffffff80000000and the mask sign extension gives (i.e. mmio)0xfffffffffffffff0. The AND gives0xffffffff80000000and the NOT gives0x7fffffff. Finally,0x7fffffff+ 1 gives0x80000000.
I don't see the point of using the upper 32-bits to calculate a 32-bit value from 32-bit inputs.
Quoted 7 lines
If we do not sign-extend (or explicitly OR with
0xffffffff00000000) size and/or mask then thereturn(~(size & mask))+ 1 gives a wrong0xffffffff80000000.Quoted 2 lines
Quoted 1 line
phys_addr_t mask = (int32_t)pci_bar_mask(bar);
Quoted 8 lines
Quoted 5 lines
It might be OK to do that here, on a mask, but even if it is, then I don't like it, because it's too subtle (like I said for the casting in
pci_bar_addrin my last reply). I don't like that it requires us to know that masking bit 31 in a 32-bit mask means we also want to mask 63..32. That should at least be in a comment somewhere.Yes, but it is not an arbitrary mask, it is an alignment mask. We unconditionally want to mask 63..32, 31th and even lower.
If you use sign-extension to generate the alignment mask, then it needs a comment. But if we can avoid the headache of 64-bit data computing 32-bit results then let's avoid it. (Even though you described to me how it's supposed to work, I'm still not sure it's safe for all cases :-)
Quoted 11 lines
Quoted 11 lines
Quoted 10 lines
if (pci_bar_is64(dev, bar_num)) size |= (phys_addr_t)pci_bar_size32(dev, bar_num + 1) << 32; return (~(size & mask)) + 1;The casting is needed to avoid putting explicitly all 1s into higher bits of size and/or mask. Otherwise (~(size & mask)) + 1 expression would not bring correct results. I really struggle to make something better readable.
Quoted 8 lines
Quoted 5 lines
It's not just about readability, it's about correctness. You shouldn't use 32-bit sizes/masks with 64-bit data this way. Like I said in the last reply, you should rework it like I showed you, operate on the 32-bit data with the 32-bit mask/size, and then eventually construct 64-bit data.
It is all about those last +1 and shifting bits from lower to upper word of 64-bit value. My hope was to off-load it to the compiler :)
I think the problem comes from trying to apply a mask to the upper bits, even though it doesn't need one. Off-loading to the compiler is a good idea, but we can avoid being compilers ourselves by writing short, simple to understand functions, and then letting the compiler inline and optimize them for us :-)
Thanks,
drew
Re: [kvm-unit-tests PATCH v4 04/12] pci: Rework pci_bar_addr()
Quoted 32 lines · Andrew Jones
Quoted 31 lines · Alexander Gordeev
Quoted 29 lines · Andrew Jones
Quoted 21 lines · Alexander Gordeev
Quoted 6 lines · Andrew Jones
Quoted 6 lines
lib/pci.c +3
-0+phys_addr_t pci_bar_size (pcidevaddr_t dev,int bar_num) {uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +phys_addr_t size = (int32_t )pci_bar_size32 (dev, bar_num); +phys_addr_t mask = (int32_t )pci_bar_mask (bar);Quoted 13 lines
lib/pci.c pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id) · line 76
Quoted 10 lines
lib/pci.c +6
-4-if (bar &PCI_BASE_ADDRESS_SPACE_IO ) -return bar &PCI_BASE_ADDRESS_IO_MASK ; -else -return bar &PCI_BASE_ADDRESS_MEM_MASK ; +if (pci_bar_is64 (dev, bar_num)) { +uint32_t size_high =pci_bar_size32 (dev, bar_num +1 ); + size = ((phys_addr_t )size_high <<32 ) | (uint32_t )size; + } + +return (~(size & mask)) +1 ;All this casting of size and mask is pointless. Please rework it similar to what I did above.
Quoted 4 lines
This is the most compact and straight variant I was able to come up with:
uint32_t bar = pci_bar_get(dev, bar_num); phys_addr_t size = (int32_t)pci_bar_size32(dev, bar_num);But this is wrong. If your 32-bit size was
0x80000000, then you now say it's0xffffffff80000000.
Quoted 8 lines
Quoted 2 lines
Hmm.. I am either terribly missing the point or we are on different pages.
Let's make sure I know what the function is supposed to do.
Based on your comment explaining how to get the PCI bar size, it sounds like we should do these steps; let's assume a device needs size=0x200000, and is 32-bit
I am not quite sure what barsz=0x00000000 at read-write1s-dance phase is, but otherwise it seems correct to me (well may be a nit - the 32-bit variant denoted 64-bit mmio).
Quoted 41 lines
read-write1s-dance barsz=0x00000000
readl barsz=0xffe0000c
mask barsz=0xffe00000barsz = ~barsz + 1 barsz=0x00200000
Now let's say it's 64-bit
read-write1s-dance barsz_lo=0x00000000
readl barsz_lo=0xffe0000c
mask barsz_lo=0xffe00000
read-write1s-dance barsz_hi=0x00000000
readl barsz_hi=0xffffffff
<upper 32-bit mask is0xffffffff, so don't bother masking at all>barsz = ~((phys_addr_t)barsz_hi << 32 |
barsz_lo) + 1 (barsz=0x200000)So you need 5 functions
uint32_tget_mask(int bar)
{
...
}
uint32_tread_dance(int bar)
{
read-write1s-dance
return readl
}
uint32_tget_size32(int bar)
{
uint32_tsize =read_dance(bar)&get_mask(bar); return ~size + 1;
}
uint64_tget_size64(int bar)
{
uint64_tsize =read_dance(bar)&get_mask(bar);uint64_tsize_hi=read_dance(bar + 1);
Quoted 8 lines
size |=
size_hi<< 32;
return ~size + 1;
}
uint64_tget_size(int bar)
{
returnbar_size(bar)== BAR64 ?get_size64(bar):get_size32(bar);
}
If the above is a pseudo code or you want me to rework using these functions? If not, below is how it could look like.
Also, looking at your get_size() I noticed a bug in my version :-) when read_dance() is followed by readl() returns zero - that means no (more) BAR(s).
phys_addr_t pci_bar_size(pcidevaddr_t dev, int bar_num)
{
uint32_t size;
uint32_t bar;
size = pci_bar_size32(dev, bar_num);
if (!size)
return 0;
bar = pci_bar_get(dev, bar_num);
size &= pci_bar_mask(bar);
if (pci_bar_is64(dev, bar_num)) {
phys_addr_t size64 = pci_bar_size32(dev, bar_num + 1);
size64 = (size64 << 32) | size;
return ~size64 + 1;
} else {
return ~size + 1;
}
}Quoted 7 lines
Quoted 4 lines
So if
pci_bar_size32()returned0x80000000the size sign-extension gives0xffffffff80000000and the mask sign extension gives (i.e. mmio)0xfffffffffffffff0. The AND gives0xffffffff80000000and the NOT gives0x7fffffff. Finally,0x7fffffff+ 1 gives0x80000000.I don't see the point of using the upper 32-bits to calculate a 32-bit value from 32-bit inputs.
It is indeed too complicated. My point has faded away :)
Quoted 49 lines
Quoted 6 lines
If we do not sign-extend (or explicitly OR with
0xffffffff00000000) size and/or mask then thereturn(~(size & mask))+ 1 gives a wrong0xffffffff80000000.Quoted 2 lines
Quoted 1 line
phys_addr_t mask = (int32_t)pci_bar_mask(bar);Quoted 8 lines
Quoted 5 lines
It might be OK to do that here, on a mask, but even if it is, then I don't like it, because it's too subtle (like I said for the casting in
pci_bar_addrin my last reply). I don't like that it requires us to know that masking bit 31 in a 32-bit mask means we also want to mask 63..32. That should at least be in a comment somewhere.Yes, but it is not an arbitrary mask, it is an alignment mask. We unconditionally want to mask 63..32, 31th and even lower.
If you use sign-extension to generate the alignment mask, then it needs a comment. But if we can avoid the headache of 64-bit data computing 32-bit results then let's avoid it. (Even though you described to me how it's supposed to work, I'm still not sure it's safe for all cases :-)
Quoted 11 lines
Quoted 11 lines
Quoted 10 lines
if (pci_bar_is64(dev, bar_num)) size |= (phys_addr_t)pci_bar_size32(dev, bar_num + 1) << 32; return (~(size & mask)) + 1;The casting is needed to avoid putting explicitly all 1s into higher bits of size and/or mask. Otherwise (~(size & mask)) + 1 expression would not bring correct results. I really struggle to make something better readable.
Quoted 8 lines
Quoted 5 lines
It's not just about readability, it's about correctness. You shouldn't use 32-bit sizes/masks with 64-bit data this way. Like I said in the last reply, you should rework it like I showed you, operate on the 32-bit data with the 32-bit mask/size, and then eventually construct 64-bit data.
It is all about those last +1 and shifting bits from lower to upper word of 64-bit value. My hope was to off-load it to the compiler :)
I think the problem comes from trying to apply a mask to the upper bits, even though it doesn't need one. Off-loading to the compiler is a good idea, but we can avoid being compilers ourselves by writing short, simple to understand functions, and then letting the compiler inline and optimize them for us :-)
Thanks,
drew
Re: [kvm-unit-tests PATCH v4 04/12] pci: Rework pci_bar_addr()
Quoted 36 lines · Alexander Gordeev
Quoted 35 lines · Andrew Jones
Quoted 33 lines · Alexander Gordeev
Quoted 22 lines · Andrew Jones
Quoted 21 lines · Alexander Gordeev
Quoted 19 lines · Andrew Jones
Quoted 6 lines
(quoted diff) +3
-0+phys_addr_t pci_bar_size(pcidevaddr_t dev, int bar_num) { uint32_t bar = pci_config_readl(dev, PCI_BASE_ADDRESS_0 + bar_num * 4); + phys_addr_t size = (int32_t)pci_bar_size32(dev, bar_num); + phys_addr_t mask = (int32_t)pci_bar_mask(bar);Review
Quoted 10 lines
(quoted diff) +6
-4- if (bar & PCI_BASE_ADDRESS_SPACE_IO) - return bar & PCI_BASE_ADDRESS_IO_MASK; - else - return bar & PCI_BASE_ADDRESS_MEM_MASK; + if (pci_bar_is64(dev, bar_num)) { + uint32_t size_high = pci_bar_size32(dev, bar_num + 1); + size = ((phys_addr_t)size_high << 32) | (uint32_t)size; + } + + return (~(size & mask)) + 1;All this casting of size and mask is pointless. Please rework it similar to what I did above.
Quoted 7 lines
Quoted 4 lines
This is the most compact and straight variant I was able to come up with:
uint32_t bar = pci_bar_get(dev, bar_num); phys_addr_t size = (int32_t)pci_bar_size32(dev, bar_num);But this is wrong. If your 32-bit size was
0x80000000, then you now say it's0xffffffff80000000.Hmm.. I am either terribly missing the point or we are on different pages.
Quoted 7 lines
Quoted 5 lines
Let's make sure I know what the function is supposed to do.
Based on your comment explaining how to get the PCI bar size, it sounds like we should do these steps; let's assume a device needs size=0x200000, and is 32-bit
I am not quite sure what barsz=0x00000000 at read-write1s-dance
barsz is initialized to zero in the sequence in order to start the variable descriptions with something, but it's not necessary to do so with real code.
Quoted 2 lines
phase is, but otherwise it seems correct to me (well may be a nit - the 32-bit variant denoted 64-bit mmio).
Not sure what you mean, but it doesn't matter for this :-)
Quoted 46 lines
Quoted 46 lines
read-write1s-dance barsz=0x00000000
readl barsz=0xffe0000c
mask barsz=0xffe00000barsz = ~barsz + 1 barsz=0x00200000
Now let's say it's 64-bit
read-write1s-dance barsz_lo=0x00000000
readl barsz_lo=0xffe0000c
mask barsz_lo=0xffe00000
read-write1s-dance barsz_hi=0x00000000
readl barsz_hi=0xffffffff
<upper 32-bit mask is0xffffffff, so don't bother masking at all>barsz = ~((phys_addr_t)barsz_hi << 32 |
barsz_lo) + 1 (barsz=0x200000)So you need 5 functions
uint32_tget_mask(int bar)
{
...
}
uint32_tread_dance(int bar)
{
read-write1s-dance
return readl
}
uint32_tget_size32(int bar)
{
uint32_tsize =read_dance(bar)&get_mask(bar); return ~size + 1;
}
uint64_tget_size64(int bar)
{
uint64_tsize =read_dance(bar)&get_mask(bar);uint64_tsize_hi=read_dance(bar + 1);size |=
size_hi<< 32;
return ~size + 1;
}
Quoted 7 lines
Quoted 4 lines
uint64_tget_size(int bar)
{
returnbar_size(bar)== BAR64 ?get_size64(bar):get_size32(bar);
}If the above is a pseudo code or you want me to rework using these functions?
Yes and no. You can do what you like, but pci_bar_size32 should return a size (like my get_size32), not a 2's complement of size, otherwise the name is wrong.
Quoted 6 lines
If not, below is how it could look like.
Also, looking at your
get_size()I noticed a bug in my version :-) whenread_dance()is followed byreadl()returns zero - that means no (more) BAR(s).
Quoted 8 lines
phys_addr_tpci_bar_size(pcidevaddr_t dev, int bar_num){ uint32_t size; uint32_t bar; size = pci_bar_size32(dev, bar_num); if (!size) return 0;
Should callers ever call pci_bar_size on a bar_num that will result in zero, "no more BARs"? If not, then you can assert here. Otherwise will all callers know what zero means, and check for it?
Quoted 14 lines
bar = pci_bar_get(dev, bar_num);
size &= pci_bar_mask(bar);
if (pci_bar_is64(dev, bar_num)) {
phys_addr_t size64 = pci_bar_size32(dev, bar_num + 1);
size64 = (size64 << 32) | size;
return ~size64 + 1;
} else {
return ~size + 1;
}
}Otherwise I think it's right.
Thanks,
drew
Re: [kvm-unit-tests PATCH v4 04/12] pci: Rework pci_bar_addr()
Quoted 33 lines · Andrew Jones
Quoted 33 lines
Quoted 32 lines
So you need 5 functions
uint32_tget_mask(int bar)
{
...
}
uint32_tread_dance(int bar)
{
read-write1s-dance
return readl
}
uint32_tget_size32(int bar)
{
uint32_tsize =read_dance(bar)&get_mask(bar); return ~size + 1;
}
uint64_tget_size64(int bar)
{
uint64_tsize =read_dance(bar)&get_mask(bar);uint64_tsize_hi=read_dance(bar + 1);size |=
size_hi<< 32;
return ~size + 1;
}
uint64_tget_size(int bar)
{
returnbar_size(bar)== BAR64 ?get_size64(bar):get_size32(bar);
}
Quoted 6 lines
Quoted 2 lines
If the above is a pseudo code or you want me to rework using these functions?
Yes and no. You can do what you like, but
pci_bar_size32should return a size (like myget_size32), not a 2's complement of size, otherwise the name is wrong.
Right. So my pci_bar_size32 is your read_dance. Want to use that name or i.e. pci_bar_size_complement?
Quoted 11 lines
Quoted 11 lines
If not, below is how it could look like.
Also, looking at your
get_size()I noticed a bug in my version :-) whenread_dance()is followed byreadl()returns zero - that means no (more) BAR(s).
phys_addr_tpci_bar_size(pcidevaddr_t dev, int bar_num){ uint32_t size; uint32_t bar;
Quoted 7 lines
Quoted 3 lines
size = pci_bar_size32(dev, bar_num); if (!size) return 0;Should callers ever call
pci_bar_sizeon abar_numthat will result in zero, "no more BARs"? If not, then you can assert here. Otherwise will all callers know what zero means, and check for it?
Yes, it is an indication a BAR is implemented in HW. But I misled you :/ I remember some code stopped on a zero-sized BAR, but I do not recall what code it was. There is nothing in specs about stopping and both Seabios and Linux just continue probing. So I will update the breaks to continues.
Quoted 2 lines
Thanks,
drew
Re: [kvm-unit-tests PATCH v4 04/12] pci: Rework pci_bar_addr()
Quoted 37 lines · Alexander Gordeev
Quoted 36 lines · Andrew Jones
Quoted 35 lines
Quoted 24 lines
So you need 5 functions
uint32_tget_mask(int bar)
{
...
}
uint32_tread_dance(int bar)
{
read-write1s-dance
return readl
}
uint32_tget_size32(int bar)
{
uint32_tsize =read_dance(bar)&get_mask(bar); return ~size + 1;
}
uint64_tget_size64(int bar)
{
uint64_tsize =read_dance(bar)&get_mask(bar);uint64_tsize_hi=read_dance(bar + 1);Quoted 8 lines
size |=
size_hi<< 32;
return ~size + 1;
}
uint64_tget_size(int bar)
{
returnbar_size(bar)== BAR64 ?get_size64(bar):get_size32(bar);
}If the above is a pseudo code or you want me to rework using these functions?
Quoted 6 lines
Quoted 3 lines
Yes and no. You can do what you like, but
pci_bar_size32should return a size (like myget_size32), not a 2's complement of size, otherwise the name is wrong.Right. So my
pci_bar_size32is yourread_dance. Want to use that name or i.e.pci_bar_size_complement?
read-dance was more of joke name, but I am starting to like it :-)
pci_bar_size_complement isn't right, as it returns an unmasked value, not the real complement.
pci_bar_size_helper()? pci_bar_size_read_dance()?
Whatever you like :-)
Quoted 32 lines
Quoted 12 lines
Quoted 12 lines
If not, below is how it could look like.
Also, looking at your
get_size()I noticed a bug in my version :-) whenread_dance()is followed byreadl()returns zero - that means no (more) BAR(s).
phys_addr_tpci_bar_size(pcidevaddr_t dev, int bar_num){ uint32_t size; uint32_t bar;Quoted 7 lines
Quoted 3 lines
size = pci_bar_size32(dev, bar_num); if (!size) return 0;Should callers ever call
pci_bar_sizeon abar_numthat will result in zero, "no more BARs"? If not, then you can assert here. Otherwise will all callers know what zero means, and check for it?Yes, it is an indication a BAR is implemented in HW. But I misled you :/ I remember some code stopped on a zero-sized BAR, but I do not recall what code it was. There is nothing in specs about stopping and both Seabios and Linux just continue probing. So I will update the breaks to continues.
Quoted 2 lines
Thanks,
drew--
To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [kvm-unit-tests PATCH v4 04/12] pci: Rework pci_bar_addr()
In reply to Andrew JonesQuoted 8 lines · Andrew Jones
Quoted 7 lines
+/*
+ * BAR number in all BAR access functions below is a number of 32-bit
+ * register starting fromPCI_BASE_ADDRESS_0offset.
+ *
+ * In cases BAR size is 64-bit a caller should still provide BAR number
+ * in terms of 32-bit words. For example, if a device has 64-bit BAR#0
+ * and 32-bit BAR#1 the caller should provide 2 to address BAR#1, not 1.
Quoted 4 lines
Is this how people label bars? I.e. they call a bar following a 64-bit bar BAR#N+1? Or do they skip numbers with the labels to match the offsets? I.e. in this example you'd say BAR#0 (64-bit), there is no BAR#1, and then BAR#2 (32-bit) when labeling?
I do not think there is an agreed notation here. I would guess, when it comes to a particular device people label registers as described in the device specification. But I can easily imagine specifications using both ways.
Quoted 2 lines
Thanks,
drew
Re: [kvm-unit-tests PATCH v4 04/12] pci: Rework pci_bar_addr()
In reply to Andrew JonesQuoted 13 lines · Andrew Jones
lib/x86/asm/pci.h static inline void pci_config_writel(pcidevaddr_t dev, uint8_t reg, uint32_t val · line 39
Quoted 10 lines
lib/x86/asm/pci.h +2
-0@@ -35,4 +35,10 @@ static inline void pci_config_writel(pcidevaddr_t dev, uint8_t reg, uint32_t valoutl (val,0xCFC ); } +static inline +phys_addr_t pci_translate_addr(pcidevaddr_t __unused dev, uint64_t addr)I put the
__unusedafter the argument name.
Quoted 4 lines
lib/x86/asm/pci.h static inline void pci_config_writel(pcidevaddr_t dev, uint8_t reg, uint32_t val · line 41
Quoted 2 lines
lib/x86/asm/pci.h +2
-0+{ +return addr;Need tab here.
It will differ then from the coding style of the other functions in this header (and elsewhere in x86).
Quoted 9 lines
Quoted 6 lines
lib/x86/asm/pci.h +2
-0+} +#endif Signature · 1 line
1.8.3.1Thanks,
drew
Re: [kvm-unit-tests PATCH v4 04/12] pci: Rework pci_bar_addr()
Quoted 14 lines · Alexander Gordeev
Quoted 13 lines · Andrew Jones
lib/x86/asm/pci.h static inline void pci_config_writel(pcidevaddr_t dev, uint8_t reg, uint32_t val · line 39
Quoted 10 lines
lib/x86/asm/pci.h +2
-0@@ -35,4 +35,10 @@ static inline void pci_config_writel(pcidevaddr_t dev, uint8_t reg, uint32_t valoutl (val,0xCFC ); } +static inline +phys_addr_t pci_translate_addr(pcidevaddr_t __unused dev, uint64_t addr)I put the
__unusedafter the argument name.
Quoted 7 lines
Quoted 4 lines
lib/x86/asm/pci.h static inline void pci_config_writel(pcidevaddr_t dev, uint8_t reg, uint32_t val · line 41
Quoted 2 lines
lib/x86/asm/pci.h +2
-0+{ +return addr;Need tab here.
It will differ then from the coding style of the other functions in this header (and elsewhere in x86).
ah, nevermind then
Quoted 14 lines
Quoted 10 lines
Quoted 7 lines
lib/x86/asm/pci.h +2
-0+} +#endif Signature · 1 line
1.8.3.1Thanks,
drew--
To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v4 5/12 pci: Factor out pci_bar_get()
In reply to Alexander Gordeev--- lib/pci.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-)
lib/pci.c +11 -7
@@ -27,14 +27,18 @@ static uint32_t pci_bar_mask(uint32_t bar) PCI_BASE_ADDRESS_IO_MASK :PCI_BASE_ADDRESS_MEM_MASK ; } +static uint32_t pci_bar_get (pcidevaddr_t dev,int bar_num) +{ +return pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +} +phys_addr_t pci_bar_addr (pcidevaddr_t dev,int bar_num) { -int off =PCI_BASE_ADDRESS_0 + bar_num *4 ; -phys_addr_t bar =pci_config_readl (dev, off); +phys_addr_t bar =pci_bar_get (dev, bar_num);phys_addr_t mask = (int32_t )pci_bar_mask (bar);if (pci_bar_is64 (dev, bar_num)) - bar |= (phys_addr_t )pci_config_readl (dev, off +4 ) <<32 ; + bar |= (phys_addr_t )pci_bar_get (dev, bar_num +1 ) <<32 ;return pci_translate_addr (dev, bar & mask); }
@@ -64,7 +68,7 @@ static uint32_t pci_bar_size32(pcidevaddr_t dev, int bar_num)phys_addr_t pci_bar_size (pcidevaddr_t dev,int bar_num) { -uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +uint32_t bar =pci_bar_get (dev, bar_num);phys_addr_t size = (int32_t )pci_bar_size32 (dev, bar_num);phys_addr_t mask = (int32_t )pci_bar_mask (bar);
@@ -78,19 +82,19 @@ phys_addr_t pci_bar_size(pcidevaddr_t dev, int bar_num)bool pci_bar_is_memory (pcidevaddr_t dev,int bar_num) { -uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +uint32_t bar =pci_bar_get (dev, bar_num);return !(bar &PCI_BASE_ADDRESS_SPACE_IO ); }bool pci_bar_is_valid (pcidevaddr_t dev,int bar_num) { -uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +uint32_t bar =pci_bar_get (dev, bar_num);return bar; }bool pci_bar_is64 (pcidevaddr_t dev,int bar_num) { -uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +uint32_t bar =pci_bar_get (dev, (bar_num));if (bar &PCI_BASE_ADDRESS_SPACE_IO )return false;
Signature · 1 line
1.8.3.1
Re: [kvm-unit-tests PATCH v4 05/12] pci: Factor out pci_bar_get()
Quoted 52 lines · Alexander Gordeev
Cc Thomas Huth <[email protected]>Cc Andrew Jones <[email protected]>Signed-off-by Alexander Gordeev <[email protected]>--- lib/pci.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-)lib/pci.c +9
-5@@ -27,14 +27,18 @@ static uint32_t pci_bar_mask(uint32_t bar) PCI_BASE_ADDRESS_IO_MASK :PCI_BASE_ADDRESS_MEM_MASK ; } +static uint32_t pci_bar_get (pcidevaddr_t dev,int bar_num) +{ +return pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +} +phys_addr_t pci_bar_addr (pcidevaddr_t dev,int bar_num) { -int off =PCI_BASE_ADDRESS_0 + bar_num *4 ; -phys_addr_t bar =pci_config_readl (dev, off); +phys_addr_t bar =pci_bar_get (dev, bar_num);phys_addr_t mask = (int32_t )pci_bar_mask (bar);if (pci_bar_is64 (dev, bar_num)) - bar |= (phys_addr_t )pci_config_readl (dev, off +4 ) <<32 ; + bar |= (phys_addr_t )pci_bar_get (dev, bar_num +1 ) <<32 ;return pci_translate_addr (dev, bar & mask); }@@ -64,7 +68,7 @@ static uint32_t pci_bar_size32(pcidevaddr_t dev, int bar_num)phys_addr_t pci_bar_size (pcidevaddr_t dev,int bar_num) { -uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +uint32_t bar =pci_bar_get (dev, bar_num);phys_addr_t size = (int32_t )pci_bar_size32 (dev, bar_num);phys_addr_t mask = (int32_t )pci_bar_mask (bar);@@ -78,19 +82,19 @@ phys_addr_t pci_bar_size(pcidevaddr_t dev, int bar_num)bool pci_bar_is_memory (pcidevaddr_t dev,int bar_num) { -uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +uint32_t bar =pci_bar_get (dev, bar_num);return !(bar &PCI_BASE_ADDRESS_SPACE_IO ); }bool pci_bar_is_valid (pcidevaddr_t dev,int bar_num)
lib/pci.c phys_addr_t pci_bar_size(pcidevaddr_t dev, int bar_num) · line 97
Quoted 10 lines
lib/pci.c +2
-2{ -uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +uint32_t bar =pci_bar_get (dev, bar_num);return bar; }bool pci_bar_is64 (pcidevaddr_t dev,int bar_num) { -uint32_t bar =pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); +uint32_t bar =pci_bar_get (dev, (bar_num));
Useless () around bar_num here
Quoted 6 lines
lib/pci.c +0
-0if (bar &PCI_BASE_ADDRESS_SPACE_IO )return false;Signature · 1 line
1.8.3.1
You probably could have introduced this function earlier in the series, allowing you to immediately use it on new functions, but it doesn't really matter.
Besides the nit above,
PATCH v4 6/12 pci: Add pci_bar_set_addr()
In reply to Alexander GordeevBecause the counterpart to pci_bar_set_addr() setter is pci_bar_addr() getter, these names become inconsistent. Rename pci_bar_addr() to pci_bar_get_addr() also to make the resulting names conform to each other.
--- lib/pci.c | 12 +++++++++++- lib/pci.h | 3 ++- x86/vmexit.c | 4 ++-- 3 files changed, 15 insertions(+), 4 deletions(-)
lib/pci.c +11 -1
@@ -32,7 +32,7 @@ static uint32_t pci_bar_get(pcidevaddr_t dev, int bar_num)return pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); } -phys_addr_t pci_bar_addr (pcidevaddr_t dev,int bar_num) +phys_addr_t pci_bar_get_addr (pcidevaddr_t dev,int bar_num) {phys_addr_t bar =pci_bar_get (dev, bar_num);phys_addr_t mask = (int32_t )pci_bar_mask (bar);
@@ -43,6 +43,16 @@ phys_addr_t pci_bar_addr(pcidevaddr_t dev, int bar_num)return pci_translate_addr (dev, bar & mask); } +void pci_bar_set_addr (pcidevaddr_t dev,int bar_num,phys_addr_t addr) +{ +int off =PCI_BASE_ADDRESS_0 + bar_num *4 ; + +pci_config_writel (dev, off, (uint32_t )addr); + +if (pci_bar_is64 (dev, bar_num)) +pci_config_writel (dev, off +4 , (uint32_t )(addr >>32 )); +} +/* * To determine the amount of address space needed by a PCI device, * one must save the original value of the BAR, write a value of
lib/pci.h +2 -1
@@ -27,7 +27,8 @@ pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id);* It is expected the caller is aware of the device BAR layout and never * tries to address in the middle of a 64-bit register. */ -phys_addr_t pci_bar_addr (pcidevaddr_t dev,int bar_num); +phys_addr_t pci_bar_get_addr (pcidevaddr_t dev,int bar_num); +void pci_bar_set_addr (pcidevaddr_t dev,int bar_num,phys_addr_t addr);phys_addr_t pci_bar_size (pcidevaddr_t dev,int bar_num);bool pci_bar_is64 (pcidevaddr_t dev,int bar_num);bool pci_bar_is_memory (pcidevaddr_t dev,int bar_num);
x86/vmexit.c +2 -2
@@ -392,10 +392,10 @@ int main(int ac, char **av)continue ; }if (pci_bar_is_memory (pcidev, i)) { - membar =pci_bar_addr (pcidev, i); + membar =pci_bar_get_addr (pcidev, i); pci_test.memaddr =ioremap (membar,PAGE_SIZE ); }else { - pci_test.iobar =pci_bar_addr (pcidev, i); + pci_test.iobar =pci_bar_get_addr (pcidev, i); } } printf("pci-testdev at 0x%x membar %lx iobar %x\n",
Signature · 1 line
1.8.3.1
Re: [kvm-unit-tests PATCH v4 06/12] pci: Add pci_bar_set_addr()
Quoted 78 lines · Alexander Gordeev
Because the counterpart to
pci_bar_set_addr()setter ispci_bar_addr()getter, these names become inconsistent. Renamepci_bar_addr()topci_bar_get_addr()also to make the resulting names conform to each other.Cc Thomas Huth <[email protected]>Cc Andrew Jones <[email protected]>Signed-off-by Alexander Gordeev <[email protected]>--- lib/pci.c | 12 +++++++++++- lib/pci.h | 3 ++- x86/vmexit.c | 4 ++-- 3 files changed, 15 insertions(+), 4 deletions(-)lib/pci.c +11
-1@@ -32,7 +32,7 @@ static uint32_t pci_bar_get(pcidevaddr_t dev, int bar_num)return pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); } -phys_addr_t pci_bar_addr (pcidevaddr_t dev,int bar_num) +phys_addr_t pci_bar_get_addr (pcidevaddr_t dev,int bar_num) {phys_addr_t bar =pci_bar_get (dev, bar_num);phys_addr_t mask = (int32_t )pci_bar_mask (bar);@@ -43,6 +43,16 @@ phys_addr_t pci_bar_addr(pcidevaddr_t dev, int bar_num)return pci_translate_addr (dev, bar & mask); } +void pci_bar_set_addr (pcidevaddr_t dev,int bar_num,phys_addr_t addr) +{ +int off =PCI_BASE_ADDRESS_0 + bar_num *4 ; + +pci_config_writel (dev, off, (uint32_t )addr); + +if (pci_bar_is64 (dev, bar_num)) +pci_config_writel (dev, off +4 , (uint32_t )(addr >>32 )); +} +/* * To determine the amount of address space needed by a PCI device, * one must save the original value of the BAR, write a value of lib/pci.h +2
-1@@ -27,7 +27,8 @@ pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id);* It is expected the caller is aware of the device BAR layout and never * tries to address in the middle of a 64-bit register. */ -phys_addr_t pci_bar_addr (pcidevaddr_t dev,int bar_num); +phys_addr_t pci_bar_get_addr (pcidevaddr_t dev,int bar_num); +void pci_bar_set_addr (pcidevaddr_t dev,int bar_num,phys_addr_t addr);phys_addr_t pci_bar_size (pcidevaddr_t dev,int bar_num);bool pci_bar_is64 (pcidevaddr_t dev,int bar_num);bool pci_bar_is_memory (pcidevaddr_t dev,int bar_num);x86/vmexit.c +2
-2@@ -392,10 +392,10 @@ int main(int ac, char **av)continue ; }if (pci_bar_is_memory (pcidev, i)) { - membar =pci_bar_addr (pcidev, i); + membar =pci_bar_get_addr (pcidev, i); pci_test.memaddr =ioremap (membar,PAGE_SIZE ); }else { - pci_test.iobar =pci_bar_addr (pcidev, i); + pci_test.iobar =pci_bar_get_addr (pcidev, i); } } printf("pci-testdev at 0x%x membar %lx iobar %x\n",Signature · 1 line
1.8.3.1
PATCH v4 7/12 pci: Add pci_dev_exists()
In reply to Alexander Gordeev--- lib/pci.c | 6 ++++++ lib/pci.h | 1 + 2 files changed, 7 insertions(+)
lib/pci.c +6 -0
@@ -7,6 +7,12 @@#include "pci.h" #include "asm/pci.h" +bool pci_dev_exists (pcidevaddr_t dev) +{ +return (pci_config_readw (dev,PCI_VENDOR_ID ) != (uint16_t )~0 && +pci_config_readw (dev,PCI_DEVICE_ID ) != (uint16_t )~0 ); +} +/* Scan bus look for a specific device. Only bus 0 scanned for now. */ pcidevaddr_t pci_find_dev (uint16_t vendor_id,uint16_t device_id) {
lib/pci.h +1 -0
@@ -14,6 +14,7 @@ typedef uint16_t pcidevaddr_t;enum {PCIDEVADDR_INVALID =0xffff , }; +bool pci_dev_exists (pcidevaddr_t dev);pcidevaddr_t pci_find_dev (uint16_t vendor_id,uint16_t device_id);/*
Signature · 1 line
1.8.3.1
Re: [kvm-unit-tests PATCH v4 07/12] pci: Add pci_dev_exists()
Quoted 13 lines · Alexander Gordeev
Cc Thomas Huth <[email protected]>Cc Andrew Jones <[email protected]>Signed-off-by Alexander Gordeev <[email protected]>--- lib/pci.c | 6 ++++++ lib/pci.h | 1 + 2 files changed, 7 insertions(+)lib/pci.c +0
-0
lib/pci.c line 13
Quoted 7 lines
lib/pci.c +4
-0@@ -7,6 +7,12 @@#include "pci.h" #include "asm/pci.h" +bool pci_dev_exists (pcidevaddr_t dev) +{ +return (pci_config_readw (dev,PCI_VENDOR_ID ) != (uint16_t )~0 && +pci_config_readw (dev,PCI_DEVICE_ID ) != (uint16_t )~0 );
I'd prefer 0xffff, it's even 6 less characters :-)
lib/pci.h typedef uint16_t pcidevaddr_t; · line 16
Quoted 13 lines
lib/pci.c +2
-0+} +/* Scan bus look for a specific device. Only bus 0 scanned for now. */ pcidevaddr_t pci_find_dev (uint16_t vendor_id,uint16_t device_id) {lib/pci.h +0
-0@@ -14,6 +14,7 @@ typedef uint16_t pcidevaddr_t;enum {PCIDEVADDR_INVALID =0xffff , };
a blank line here would be nice
Quoted 6 lines
lib/pci.h +1
-0+bool pci_dev_exists (pcidevaddr_t dev);pcidevaddr_t pci_find_dev (uint16_t vendor_id,uint16_t device_id);/* Signature · 1 line
1.8.3.1
Otherwise,
PATCH v4 8/12 pci: Add pci_print()
In reply to Alexander Gordeev--- lib/pci.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/pci.h | 3 +++ 2 files changed, 82 insertions(+)
lib/pci.c +79 -0
@@ -118,3 +118,82 @@ bool pci_bar_is64(pcidevaddr_t dev, int bar_num)return (bar &PCI_BASE_ADDRESS_MEM_TYPE_MASK ) ==PCI_BASE_ADDRESS_MEM_TYPE_64 ; } + +static void pci_dev_print (pcidevaddr_t dev) +{ +uint16_t vendor_id =pci_config_readw (dev,PCI_VENDOR_ID ); +uint16_t device_id =pci_config_readw (dev,PCI_DEVICE_ID ); +uint8_t header =pci_config_readb (dev,PCI_HEADER_TYPE ); +uint8_t progif =pci_config_readb (dev,PCI_CLASS_PROG ); +uint8_t subclass =pci_config_readb (dev,PCI_CLASS_DEVICE ); +uint8_t class =pci_config_readb (dev,PCI_CLASS_DEVICE +1 ); +int i; + +printf ("dev %2d fn %d vendor_id %04x device_id %04x type %02x " +"progif %02x class %02x subclass %02x\n" , + dev /8 , dev %8 , vendor_id, device_id, header, + progif, class, subclass); + +if ((header &PCI_HEADER_TYPE_MASK ) !=PCI_HEADER_TYPE_NORMAL ) +return ; + +for (i =0 ; i <6 ; i++) { +phys_addr_t start, end; +uint32_t bar; + +if (!pci_bar_is_valid (dev, i)) +break ; + + start =pci_bar_get_addr (dev, i); + end = start +pci_bar_size (dev, i) -1 ; + +if (pci_bar_is64 (dev, i)) { +printf ("\tBAR#%d,%d [%" PRIx64"-%" PRIx64" " , + i, i +1 , start, end); + i++; + }else { +printf ("\tBAR#%d [%02x-%02x " , + i, (uint32_t )start, (uint32_t )end); + } + + bar =pci_bar_get (dev, i); + +if (bar &PCI_BASE_ADDRESS_SPACE_IO ) { +printf ("PIO]\n" ); +continue ; + } + +printf ("MEM" ); + +switch (bar &PCI_BASE_ADDRESS_MEM_TYPE_MASK ) { +case PCI_BASE_ADDRESS_MEM_TYPE_32 : +printf ("32" ); +break ; +case PCI_BASE_ADDRESS_MEM_TYPE_1M : +printf ("1M" ); +break ; +case PCI_BASE_ADDRESS_MEM_TYPE_64 : +printf ("64" ); +break ; +default : +assert (0 ); + } + +if (bar &PCI_BASE_ADDRESS_MEM_PREFETCH ) +printf ("/p" ); + +printf ("]\n" ); + } +} + +void pci_print (void ) +{ +pcidevaddr_t dev; + +for (dev =0 ; dev <256 ; ++dev) { +if (pci_config_readw (dev,PCI_VENDOR_ID ) != (uint16_t )~0 && +pci_config_readw (dev,PCI_DEVICE_ID ) != (uint16_t )~0 ) { +pci_dev_print (dev); + } + } +}
lib/pci.h +3 -0
@@ -14,6 +14,7 @@ typedef uint16_t pcidevaddr_t;enum {PCIDEVADDR_INVALID =0xffff , }; +void pci_print (void );bool pci_dev_exists (pcidevaddr_t dev);pcidevaddr_t pci_find_dev (uint16_t vendor_id,uint16_t device_id);
@@ -55,4 +56,6 @@ struct pci_test_dev_hdr { uint8_t name[]; }; +#define PCI_HEADER_TYPE_MASK 0x7f +#endif /* PCI_H */
Signature · 1 line
1.8.3.1
Re: [kvm-unit-tests PATCH v4 08/12] pci: Add pci_print()
Quoted 81 lines · Alexander Gordeev
Cc Thomas Huth <[email protected]>Cc Andrew Jones <[email protected]>Signed-off-by Alexander Gordeev <[email protected]>--- lib/pci.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/pci.h | 3 +++ 2 files changed, 82 insertions(+)lib/pci.c +65
-0@@ -118,3 +118,82 @@ bool pci_bar_is64(pcidevaddr_t dev, int bar_num)return (bar &PCI_BASE_ADDRESS_MEM_TYPE_MASK ) ==PCI_BASE_ADDRESS_MEM_TYPE_64 ; } + +static void pci_dev_print (pcidevaddr_t dev) +{ +uint16_t vendor_id =pci_config_readw (dev,PCI_VENDOR_ID ); +uint16_t device_id =pci_config_readw (dev,PCI_DEVICE_ID ); +uint8_t header =pci_config_readb (dev,PCI_HEADER_TYPE ); +uint8_t progif =pci_config_readb (dev,PCI_CLASS_PROG ); +uint8_t subclass =pci_config_readb (dev,PCI_CLASS_DEVICE ); +uint8_t class =pci_config_readb (dev,PCI_CLASS_DEVICE +1 ); +int i; + +printf ("dev %2d fn %d vendor_id %04x device_id %04x type %02x " +"progif %02x class %02x subclass %02x\n" , + dev /8 , dev %8 , vendor_id, device_id, header, + progif, class, subclass); + +if ((header &PCI_HEADER_TYPE_MASK ) !=PCI_HEADER_TYPE_NORMAL ) +return ; + +for (i =0 ; i <6 ; i++) { +phys_addr_t start, end; +uint32_t bar; + +if (!pci_bar_is_valid (dev, i)) +break ; + + start =pci_bar_get_addr (dev, i); + end = start +pci_bar_size (dev, i) -1 ; + +if (pci_bar_is64 (dev, i)) { +printf ("\tBAR#%d,%d [%" PRIx64"-%" PRIx64" " , + i, i +1 , start, end); + i++; + }else { +printf ("\tBAR#%d [%02x-%02x " , + i, (uint32_t )start, (uint32_t )end); + } + + bar =pci_bar_get (dev, i); + +if (bar &PCI_BASE_ADDRESS_SPACE_IO ) { +printf ("PIO]\n" ); +continue ; + } + +printf ("MEM" ); + +switch (bar &PCI_BASE_ADDRESS_MEM_TYPE_MASK ) { +case PCI_BASE_ADDRESS_MEM_TYPE_32 : +printf ("32" ); +break ; +case PCI_BASE_ADDRESS_MEM_TYPE_1M : +printf ("1M" ); +break ; +case PCI_BASE_ADDRESS_MEM_TYPE_64 : +printf ("64" ); +break ; +default : +assert (0 ); + } + +if (bar &PCI_BASE_ADDRESS_MEM_PREFETCH ) +printf ("/p" ); + +printf ("]\n" );
lib/pci.c bool pci_bar_is64(pcidevaddr_t dev, int bar_num) · line 195
Quoted 10 lines
lib/pci.c +10
-0+ } +} + +void pci_print (void ) +{ +pcidevaddr_t dev; + +for (dev =0 ; dev <256 ; ++dev) { +if (pci_config_readw (dev,PCI_VENDOR_ID ) != (uint16_t )~0 && +pci_config_readw (dev,PCI_DEVICE_ID ) != (uint16_t )~0 ) {
Should use pci_dev_exists() here. Or, could also just unconditionally call pci_dev_print here with a "if (!pci_dev_exists(dev)) return" in pci_dev_print.
Quoted 26 lines
lib/pci.c +4
-0+pci_dev_print (dev); + } + } +}lib/pci.h +3
-0@@ -14,6 +14,7 @@ typedef uint16_t pcidevaddr_t;enum {PCIDEVADDR_INVALID =0xffff , }; +void pci_print (void );bool pci_dev_exists (pcidevaddr_t dev);pcidevaddr_t pci_find_dev (uint16_t vendor_id,uint16_t device_id);@@ -55,4 +56,6 @@ struct pci_test_dev_hdr { uint8_t name[]; }; +#define PCI_HEADER_TYPE_MASK 0x7f +#endif /* PCI_H */ Signature · 1 line
1.8.3.1
PATCH v4 9/12 pci: Add generic ECAM host support
In reply to Alexander GordeevUnlike x86, other architectures using generic ECAM PCI host do not have a luxury of PCI bus initialized by a BIOS and ready to use at start. Thus, we need allocate and assign resources to all devices, much like an architecture's firmware would do.
There is no any sort of resource management for memory and io spaces, since only ones-per-BAR allocations are expected and no deallocations at all.
--- lib/pci-host-generic.c | 294 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/pci-host-generic.h | 46 ++++++++ lib/pci.c | 4 +- lib/pci.h | 3 + 4 files changed, 345 insertions(+), 2 deletions(-) create mode 100644 lib/pci-host-generic.c create mode 100644 lib/pci-host-generic.h
lib/pci-host-generic.c new file +294 -0
@@ -0,0 +1,294 @@ +/* +* Generic PCI host controller as described in PCI Bus Binding to Open Firmware +* +* Copyright (C) 2016, Red Hat Inc, Alexander Gordeev <[email protected]> +* +* This work is licensed under the terms of the GNU LGPL, version 2. +*/ +#include "libcflat.h" +#include "devicetree.h" +#include "alloc.h" +#include "pci.h" +#include "asm/pci.h" +#include "asm/io.h" +#include "pci-host-generic.h" +#include <linux/pci_regs.h> + +static struct pci_host_bridge *pci_host_bridge; + +static int of_flags_to_pci_type (u32 of_flags) +{ +static int type_map[] = { + [1 ] =PCI_BASE_ADDRESS_SPACE_IO , + [2 ] =PCI_BASE_ADDRESS_MEM_TYPE_32 , + [3 ] =PCI_BASE_ADDRESS_MEM_TYPE_64 + }; +int idx = (of_flags >>24 ) &0x03 ; +int res; + +assert (idx >0 ); + res = type_map[idx]; + +if (of_flags &0x40000000 ) + res |=PCI_BASE_ADDRESS_MEM_PREFETCH ; + +return res; +} + +static int pci_bar_type (u32 bar) +{ +if (bar &PCI_BASE_ADDRESS_SPACE ) +return PCI_BASE_ADDRESS_SPACE_IO ; +else +return bar & (PCI_BASE_ADDRESS_MEM_TYPE_MASK | +PCI_BASE_ADDRESS_MEM_PREFETCH ); +} + +/* +* Probe DT for a generic PCI host controller +* See kernel Documentation/devicetree/bindings/pci/host-generic-pci.txt +* and function gen_pci_probe() in drivers/pci/host/pci-host-generic.c +*/ +static struct pci_host_bridge *pci_dt_probe (void ) +{ +struct pci_host_bridge *host; +const void *fdt =dt_fdt (); +const struct fdt_property *prop; +struct dt_pbus_reg base; +struct dt_device dt_dev; +struct dt_bus dt_bus; +struct pci_addr_space *as; +fdt32_t *data; +u32 bus, bus_max; +u32 nac, nsc, nac_root, nsc_root; +int nr_range_cells, nr_addr_spaces; +int ret, node, len, i; + +if (!dt_available ()) { +printf ("No device tree found\n" ); +return NULL ; + } + +dt_bus_init_defaults (&dt_bus); +dt_device_init (&dt_dev, &dt_bus,NULL ); + + node =fdt_path_offset (fdt,"/" ); +assert (node >=0 ); + + ret =dt_get_nr_cells (node, &nac_root, &nsc_root); +assert (ret ==0 ); +assert (nac_root ==1 || nac_root ==2 ); + + node =fdt_node_offset_by_compatible (fdt, node, +"pci-host-ecam-generic" ); +if (node == -FDT_ERR_NOTFOUND ) { +printf ("No PCIe ECAM compatible controller found\n" ); +return NULL ; + } +assert (node >=0 ); + + prop =fdt_get_property (fdt, node,"device_type" , &len); +assert (prop && len ==4 && !strcmp ((char *)prop->data,"pci" )); + +dt_device_bind_node (&dt_dev, node); + ret =dt_pbus_get_base (&dt_dev, &base); +assert (ret ==0 ); + + prop =fdt_get_property (fdt, node,"bus-range" , &len); +if (prop ==NULL ) { +assert (len == -FDT_ERR_NOTFOUND ); + bus =0x00 ; + bus_max =0xff ; + }else { + data = (fdt32_t *)prop->data; + bus =fdt32_to_cpu (data[0 ]); + bus_max =fdt32_to_cpu (data[1 ]); +assert (bus <= bus_max); + } +assert (bus_max < base.size / (1 <<PCI_ECAM_BUS_SHIFT )); + + ret =dt_get_nr_cells (node, &nac, &nsc); +assert (ret ==0 ); +assert (nac ==3 && nsc ==2 ); + + prop =fdt_get_property (fdt, node,"ranges" , &len); +assert (prop !=NULL ); + + nr_range_cells = nac + nsc + nac_root; + nr_addr_spaces = (len /4 ) / nr_range_cells; +assert (nr_addr_spaces); + + host =malloc (sizeof (*host) + +sizeof (host->addr_space[0 ]) * nr_addr_spaces); +assert (host !=NULL ); + + host->start = base.addr; + host->size = base.size; + host->bus = bus; + host->bus_max = bus_max; + host->nr_addr_spaces = nr_addr_spaces; + + data = (fdt32_t *)prop->data; + as = &host->addr_space[0 ]; + +for (i =0 ; i < nr_addr_spaces; i++) { +/* +* The PCI binding claims the numerical representation of a PCI +* address consists of three cells, encoded as follows: +* +* phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr +* phys.mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh +* phys.lo cell: llllllll llllllll llllllll llllllll +* +* PCI device bus address and flags are encoded into phys.high +* PCI 64 bit address is encoded into phys.mid and phys.low +*/ + as->type =of_flags_to_pci_type (fdt32_to_cpu (data[0 ])); + as->pci_start = ((u64 )fdt32_to_cpu (data[1 ]) <<32 ) | +fdt32_to_cpu (data[2 ]); + +if (nr_range_cells ==6 ) { + as->start =fdt32_to_cpu (data[3 ]); + as->size = ((u64 )fdt32_to_cpu (data[4 ]) <<32 ) | +fdt32_to_cpu (data[5 ]); + }else { + as->start = ((u64 )fdt32_to_cpu (data[3 ]) <<32 ) | +fdt32_to_cpu (data[4 ]); + as->size = ((u64 )fdt32_to_cpu (data[5 ]) <<32 ) | +fdt32_to_cpu (data[6 ]); + } + + data += nr_range_cells; + as++; + } + +return host; +} + +static u64 pci_alloc_resource (u32 bar,u64 size) +{ +struct pci_host_bridge *host = pci_host_bridge; +struct pci_addr_space *as = &host->addr_space[0 ]; +phys_addr_t addr; +u32 mask; +int i; + +for (i =0 ; i < host->nr_addr_spaces; i++) { +if (as->type ==pci_bar_type (bar)) +break ; + as++; + } +if (i >= host->nr_addr_spaces) { +printf ("No PCI resource found for a device\n" ); +assert (0 ); + } + + mask =pci_bar_mask (bar); + size =ALIGN (size, ~mask +1 ); +assert (as->allocated + size <= as->size); + + addr = as->pci_start + as->allocated; + as->allocated += size; + +return addr; +} + +bool pci_probe (void ) +{ +pcidevaddr_t dev; +u8 header; +u32 cmd; +int i; + +assert (!pci_host_bridge); + pci_host_bridge =pci_dt_probe (); +if (!pci_host_bridge) +return false; + +for (dev =0 ; dev <256 ; dev++) { +if (!pci_dev_exists (dev)) +continue ; + +/* We are only interested in normal PCI devices */ + header =pci_config_readb (dev,PCI_HEADER_TYPE ); +if ((header &PCI_HEADER_TYPE_MASK ) !=PCI_HEADER_TYPE_NORMAL ) +continue ; + + cmd =PCI_COMMAND_SERR ; + +for (i =0 ; i <6 ; i++) { +u64 addr, size; +u32 bar; + + size =pci_bar_size (dev, i); +if (!size) +break ; + + bar =pci_bar_get (dev, i); + addr =pci_alloc_resource (bar, size); +pci_bar_set_addr (dev, i, addr); + +if (pci_bar_is_memory (dev, i)) + cmd |=PCI_COMMAND_MEMORY ; +else + cmd |=PCI_COMMAND_IO ; + +if (pci_bar_is64 (dev, i)) + i++; + } + +pci_config_writel (dev,PCI_COMMAND , cmd); + } + +return true; +} + +/* +* This function is to be called from pci_translate_addr() to provide +* mapping between this host bridge's PCI busses address and CPU physical +* address. +*/ +phys_addr_t pci_host_bridge_get_paddr (u64 pci_addr) +{ +struct pci_host_bridge *host = pci_host_bridge; +struct pci_addr_space *as = &host->addr_space[0 ]; +int i; + +for (i =0 ; i < host->nr_addr_spaces; i++, as++) { +if (pci_addr >= as->pci_start && + pci_addr < as->pci_start + as->size) +return as->start + (pci_addr - as->pci_start); + } + +return 0 ; +} + +static void __iomem *pci_get_dev_conf (struct pci_host_bridge *host,int devfn) +{ +return (void __iomem *)(unsigned long ) + host->start + (devfn <<PCI_ECAM_DEVFN_SHIFT ); +} + +u8 pci_config_readb (pcidevaddr_t dev,u8 off) +{ +void __iomem *conf =pci_get_dev_conf (pci_host_bridge, dev); +return readb (conf + off); +} + +u16 pci_config_readw (pcidevaddr_t dev,u8 off) +{ +void __iomem *conf =pci_get_dev_conf (pci_host_bridge, dev); +return readw (conf + off); +} + +u32 pci_config_readl (pcidevaddr_t dev,u8 off) +{ +void __iomem *conf =pci_get_dev_conf (pci_host_bridge, dev); +return readl (conf + off); +} + +void pci_config_writel (pcidevaddr_t dev,u8 off,u32 val) +{ +void __iomem *conf =pci_get_dev_conf (pci_host_bridge, dev); +writel (val, conf + off); +}
lib/pci-host-generic.h new file +46 -0
46 lines not on this page · raw message
lib/pci.c +2 -2
@@ -27,13 +27,13 @@ pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id)return PCIDEVADDR_INVALID ; } -static uint32_t pci_bar_mask (uint32_t bar) +uint32_t pci_bar_mask (uint32_t bar) {return (bar &PCI_BASE_ADDRESS_SPACE_IO ) ?PCI_BASE_ADDRESS_IO_MASK :PCI_BASE_ADDRESS_MEM_MASK ; } -static uint32_t pci_bar_get (pcidevaddr_t dev,int bar_num) +uint32_t pci_bar_get (pcidevaddr_t dev,int bar_num) {return pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); }
lib/pci.h +3 -0
@@ -15,6 +15,7 @@ enum { PCIDEVADDR_INVALID = 0xffff, };void pci_print (void ); +bool pci_probe (void );bool pci_dev_exists (pcidevaddr_t dev);pcidevaddr_t pci_find_dev (uint16_t vendor_id,uint16_t device_id);
@@ -32,6 +33,8 @@ pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id);phys_addr_t pci_bar_get_addr (pcidevaddr_t dev,int bar_num);void pci_bar_set_addr (pcidevaddr_t dev,int bar_num,phys_addr_t addr);phys_addr_t pci_bar_size (pcidevaddr_t dev,int bar_num); +uint32_t pci_bar_get (pcidevaddr_t dev,int bar_num); +uint32_t pci_bar_mask (uint32_t bar);bool pci_bar_is64 (pcidevaddr_t dev,int bar_num);bool pci_bar_is_memory (pcidevaddr_t dev,int bar_num);bool pci_bar_is_valid (pcidevaddr_t dev,int bar_num);
Signature · 1 line
1.8.3.1
Re: [kvm-unit-tests PATCH v4 09/12] pci: Add generic ECAM host support
Quoted 126 lines · Alexander Gordeev
Unlike x86, other architectures using generic ECAM PCI host do not have a luxury of PCI bus initialized by a BIOS and ready to use at start. Thus, we need allocate and assign resources to all devices, much like an architecture's firmware would do.
There is no any sort of resource management for memory and io spaces, since only ones-per-BAR allocations are expected and no deallocations at all.
Cc Thomas Huth <[email protected]>Cc Andrew Jones <[email protected]>Signed-off-by Alexander Gordeev <[email protected]>--- lib/pci-host-generic.c | 294 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/pci-host-generic.h | 46 ++++++++ lib/pci.c | 4 +- lib/pci.h | 3 + 4 files changed, 345 insertions(+), 2 deletions(-) create mode 100644 lib/pci-host-generic.c create mode 100644 lib/pci-host-generic.hlib/pci-host-generic.c new file +98
-0@@ -0,0 +1,294 @@ +/* +* Generic PCI host controller as described in PCI Bus Binding to Open Firmware +* +* Copyright (C) 2016, Red Hat Inc, Alexander Gordeev <[email protected]> +* +* This work is licensed under the terms of the GNU LGPL, version 2. +*/ +#include "libcflat.h" +#include "devicetree.h" +#include "alloc.h" +#include "pci.h" +#include "asm/pci.h" +#include "asm/io.h" +#include "pci-host-generic.h" +#include <linux/pci_regs.h> + +static struct pci_host_bridge *pci_host_bridge; + +static int of_flags_to_pci_type (u32 of_flags) +{ +static int type_map[] = { + [1 ] =PCI_BASE_ADDRESS_SPACE_IO , + [2 ] =PCI_BASE_ADDRESS_MEM_TYPE_32 , + [3 ] =PCI_BASE_ADDRESS_MEM_TYPE_64 + }; +int idx = (of_flags >>24 ) &0x03 ; +int res; + +assert (idx >0 ); + res = type_map[idx]; + +if (of_flags &0x40000000 ) + res |=PCI_BASE_ADDRESS_MEM_PREFETCH ; + +return res; +} + +static int pci_bar_type (u32 bar) +{ +if (bar &PCI_BASE_ADDRESS_SPACE ) +return PCI_BASE_ADDRESS_SPACE_IO ; +else +return bar & (PCI_BASE_ADDRESS_MEM_TYPE_MASK | +PCI_BASE_ADDRESS_MEM_PREFETCH ); +} + +/* +* Probe DT for a generic PCI host controller +* See kernel Documentation/devicetree/bindings/pci/host-generic-pci.txt +* and function gen_pci_probe() in drivers/pci/host/pci-host-generic.c +*/ +static struct pci_host_bridge *pci_dt_probe (void ) +{ +struct pci_host_bridge *host; +const void *fdt =dt_fdt (); +const struct fdt_property *prop; +struct dt_pbus_reg base; +struct dt_device dt_dev; +struct dt_bus dt_bus; +struct pci_addr_space *as; +fdt32_t *data; +u32 bus, bus_max; +u32 nac, nsc, nac_root, nsc_root; +int nr_range_cells, nr_addr_spaces; +int ret, node, len, i; + +if (!dt_available ()) { +printf ("No device tree found\n" ); +return NULL ; + } + +dt_bus_init_defaults (&dt_bus); +dt_device_init (&dt_dev, &dt_bus,NULL ); + + node =fdt_path_offset (fdt,"/" ); +assert (node >=0 ); + + ret =dt_get_nr_cells (node, &nac_root, &nsc_root); +assert (ret ==0 ); +assert (nac_root ==1 || nac_root ==2 ); + + node =fdt_node_offset_by_compatible (fdt, node, +"pci-host-ecam-generic" ); +if (node == -FDT_ERR_NOTFOUND ) { +printf ("No PCIe ECAM compatible controller found\n" ); +return NULL ; + } +assert (node >=0 ); + + prop =fdt_get_property (fdt, node,"device_type" , &len); +assert (prop && len ==4 && !strcmp ((char *)prop->data,"pci" )); + +dt_device_bind_node (&dt_dev, node); + ret =dt_pbus_get_base (&dt_dev, &base); +assert (ret ==0 ); + + prop =fdt_get_property (fdt, node,"bus-range" , &len); +if (prop ==NULL ) {
lib/pci-host-generic.c line 108
Quoted 10 lines
lib/pci-host-generic.c new file +10
-0+assert (len == -FDT_ERR_NOTFOUND ); + bus =0x00 ; + bus_max =0xff ; + }else { + data = (fdt32_t *)prop->data; + bus =fdt32_to_cpu (data[0 ]); + bus_max =fdt32_to_cpu (data[1 ]); +assert (bus <= bus_max); + } +assert (bus_max < base.size / (1 <<PCI_ECAM_BUS_SHIFT ));
Maybe assert(bus_max + 1 == base.size / (1 << PCI_ECAM_BUS_SHIFT)) ? Or is there a reason to have a larger address space than necessary?
Quoted 19 lines
lib/pci-host-generic.c new file +19
-0+ + ret =dt_get_nr_cells (node, &nac, &nsc); +assert (ret ==0 ); +assert (nac ==3 && nsc ==2 ); + + prop =fdt_get_property (fdt, node,"ranges" , &len); +assert (prop !=NULL ); + + nr_range_cells = nac + nsc + nac_root; + nr_addr_spaces = (len /4 ) / nr_range_cells; +assert (nr_addr_spaces); + + host =malloc (sizeof (*host) + +sizeof (host->addr_space[0 ]) * nr_addr_spaces); +assert (host !=NULL ); + + host->start = base.addr; + host->size = base.size; + host->bus = bus;
lib/pci-host-generic.c line 137
Quoted 10 lines
lib/pci-host-generic.c new file +10
-0+ host->bus_max = bus_max; + host->nr_addr_spaces = nr_addr_spaces; + + data = (fdt32_t *)prop->data; + as = &host->addr_space[0 ]; + +for (i =0 ; i < nr_addr_spaces; i++) { +/* +* The PCI binding claims the numerical representation of a PCI +* address consists of three cells, encoded as follows:
nit: No need to say 'claims', "The PCI binding encodes the PCI address with three cells as follows:"
Quoted 32 lines
lib/pci-host-generic.c new file +32
-0+* +* phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr +* phys.mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh +* phys.lo cell: llllllll llllllll llllllll llllllll +* +* PCI device bus address and flags are encoded into phys.high +* PCI 64 bit address is encoded into phys.mid and phys.low +*/ + as->type =of_flags_to_pci_type (fdt32_to_cpu (data[0 ])); + as->pci_start = ((u64 )fdt32_to_cpu (data[1 ]) <<32 ) | +fdt32_to_cpu (data[2 ]); + +if (nr_range_cells ==6 ) { + as->start =fdt32_to_cpu (data[3 ]); + as->size = ((u64 )fdt32_to_cpu (data[4 ]) <<32 ) | +fdt32_to_cpu (data[5 ]); + }else { + as->start = ((u64 )fdt32_to_cpu (data[3 ]) <<32 ) | +fdt32_to_cpu (data[4 ]); + as->size = ((u64 )fdt32_to_cpu (data[5 ]) <<32 ) | +fdt32_to_cpu (data[6 ]); + } + + data += nr_range_cells; + as++; + } + +return host; +} + +static u64 pci_alloc_resource (u32 bar,u64 size) +{
lib/pci-host-generic.c line 179
Quoted 10 lines
lib/pci-host-generic.c new file +10
-0+struct pci_host_bridge *host = pci_host_bridge; +struct pci_addr_space *as = &host->addr_space[0 ]; +phys_addr_t addr; +u32 mask; +int i; + +for (i =0 ; i < host->nr_addr_spaces; i++) { +if (as->type ==pci_bar_type (bar)) +break ; + as++;
nit: could have put this as++ up with the i++ like you do below
Quoted 211 lines
lib/pci-host-generic.c new file +115
-0+ } +if (i >= host->nr_addr_spaces) { +printf ("No PCI resource found for a device\n" ); +assert (0 ); + } + + mask =pci_bar_mask (bar); + size =ALIGN (size, ~mask +1 ); +assert (as->allocated + size <= as->size); + + addr = as->pci_start + as->allocated; + as->allocated += size; + +return addr; +} + +bool pci_probe (void ) +{ +pcidevaddr_t dev; +u8 header; +u32 cmd; +int i; + +assert (!pci_host_bridge); + pci_host_bridge =pci_dt_probe (); +if (!pci_host_bridge) +return false; + +for (dev =0 ; dev <256 ; dev++) { +if (!pci_dev_exists (dev)) +continue ; + +/* We are only interested in normal PCI devices */ + header =pci_config_readb (dev,PCI_HEADER_TYPE ); +if ((header &PCI_HEADER_TYPE_MASK ) !=PCI_HEADER_TYPE_NORMAL ) +continue ; + + cmd =PCI_COMMAND_SERR ; + +for (i =0 ; i <6 ; i++) { +u64 addr, size; +u32 bar; + + size =pci_bar_size (dev, i); +if (!size) +break ; + + bar =pci_bar_get (dev, i); + addr =pci_alloc_resource (bar, size); +pci_bar_set_addr (dev, i, addr); + +if (pci_bar_is_memory (dev, i)) + cmd |=PCI_COMMAND_MEMORY ; +else + cmd |=PCI_COMMAND_IO ; + +if (pci_bar_is64 (dev, i)) + i++; + } + +pci_config_writel (dev,PCI_COMMAND , cmd); + } + +return true; +} + +/* +* This function is to be called from pci_translate_addr() to provide +* mapping between this host bridge's PCI busses address and CPU physical +* address. +*/ +phys_addr_t pci_host_bridge_get_paddr (u64 pci_addr) +{ +struct pci_host_bridge *host = pci_host_bridge; +struct pci_addr_space *as = &host->addr_space[0 ]; +int i; + +for (i =0 ; i < host->nr_addr_spaces; i++, as++) { +if (pci_addr >= as->pci_start && + pci_addr < as->pci_start + as->size) +return as->start + (pci_addr - as->pci_start); + } + +return 0 ; +} + +static void __iomem *pci_get_dev_conf (struct pci_host_bridge *host,int devfn) +{ +return (void __iomem *)(unsigned long ) + host->start + (devfn <<PCI_ECAM_DEVFN_SHIFT ); +} + +u8 pci_config_readb (pcidevaddr_t dev,u8 off) +{ +void __iomem *conf =pci_get_dev_conf (pci_host_bridge, dev); +return readb (conf + off); +} + +u16 pci_config_readw (pcidevaddr_t dev,u8 off) +{ +void __iomem *conf =pci_get_dev_conf (pci_host_bridge, dev); +return readw (conf + off); +} + +u32 pci_config_readl (pcidevaddr_t dev,u8 off) +{ +void __iomem *conf =pci_get_dev_conf (pci_host_bridge, dev); +return readl (conf + off); +} + +void pci_config_writel (pcidevaddr_t dev,u8 off,u32 val) +{ +void __iomem *conf =pci_get_dev_conf (pci_host_bridge, dev); +writel (val, conf + off); +}lib/pci-host-generic.h new file +46
-0@@ -0,0 +1,46 @@ +#ifndef PCI_HOST_GENERIC_H +#define PCI_HOST_GENERIC_H +/* +* PCI host bridge supporting structures and constants +* +* Copyright (C) 2016, Red Hat Inc, Alexander Gordeev <[email protected]> +* +* This work is licensed under the terms of the GNU LGPL, version 2. +*/ +#include "libcflat.h" + +struct pci_addr_space { +phys_addr_t pci_start; +phys_addr_t start; +phys_addr_t size; +phys_addr_t allocated; +int type; +}; + +struct pci_host_bridge { +phys_addr_t start; +phys_addr_t size; +int bus; +int bus_max; +int nr_addr_spaces; +struct pci_addr_space addr_space[]; +}; + +/* +* The following constants are derived from Linux, see this source: +* +* drivers/pci/host/pci-host-generic.c +* struct gen_pci_cfg_bus_ops::bus_shift +* int gen_pci_parse_map_cfg_windows(struct gen_pci *pci) +* +* Documentation/devicetree/bindings/pci/host-generic-pci.txt describes +* ECAM Configuration Space is be memory-mapped by concatenating the various +* components to form an offset: +* +* cfg_offset(bus, device, function, register) = +* bus << 20 | device << 15 | function << 12 | register +*/ +#define PCI_ECAM_BUS_SHIFT 20 +#define PCI_ECAM_DEVFN_SHIFT 12 + +#endif lib/pci.c +2
-2@@ -27,13 +27,13 @@ pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id)return PCIDEVADDR_INVALID ; } -static uint32_t pci_bar_mask (uint32_t bar) +uint32_t pci_bar_mask (uint32_t bar) {return (bar &PCI_BASE_ADDRESS_SPACE_IO ) ?PCI_BASE_ADDRESS_IO_MASK :PCI_BASE_ADDRESS_MEM_MASK ; } -static uint32_t pci_bar_get (pcidevaddr_t dev,int bar_num) +uint32_t pci_bar_get (pcidevaddr_t dev,int bar_num) {return pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); }lib/pci.h +3
-0@@ -15,6 +15,7 @@ enum { PCIDEVADDR_INVALID = 0xffff, };void pci_print (void ); +bool pci_probe (void );bool pci_dev_exists (pcidevaddr_t dev);pcidevaddr_t pci_find_dev (uint16_t vendor_id,uint16_t device_id);@@ -32,6 +33,8 @@ pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id);phys_addr_t pci_bar_get_addr (pcidevaddr_t dev,int bar_num);void pci_bar_set_addr (pcidevaddr_t dev,int bar_num,phys_addr_t addr);phys_addr_t pci_bar_size (pcidevaddr_t dev,int bar_num); +uint32_t pci_bar_get (pcidevaddr_t dev,int bar_num); +uint32_t pci_bar_mask (uint32_t bar);bool pci_bar_is64 (pcidevaddr_t dev,int bar_num);bool pci_bar_is_memory (pcidevaddr_t dev,int bar_num);bool pci_bar_is_valid (pcidevaddr_t dev,int bar_num);Signature · 1 line
1.8.3.1
Looks great! Only a couple nits, so
Re: [kvm-unit-tests PATCH v4 09/12] pci: Add generic ECAM host support
Quoted 132 lines · Andrew Jones
Quoted 131 lines · Alexander Gordeev
Unlike x86, other architectures using generic ECAM PCI host do not have a luxury of PCI bus initialized by a BIOS and ready to use at start. Thus, we need allocate and assign resources to all devices, much like an architecture's firmware would do.
There is no any sort of resource management for memory and io spaces, since only ones-per-BAR allocations are expected and no deallocations at all.
Cc Thomas Huth <[email protected]>Cc Andrew Jones <[email protected]>Signed-off-by Alexander Gordeev <[email protected]>--- lib/pci-host-generic.c | 294 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/pci-host-generic.h | 46 ++++++++ lib/pci.c | 4 +- lib/pci.h | 3 + 4 files changed, 345 insertions(+), 2 deletions(-) create mode 100644 lib/pci-host-generic.c create mode 100644 lib/pci-host-generic.hlib/pci-host-generic.c new file +103
-0@@ -0,0 +1,294 @@ +/* +* Generic PCI host controller as described in PCI Bus Binding to Open Firmware +* +* Copyright (C) 2016, Red Hat Inc, Alexander Gordeev <[email protected]> +* +* This work is licensed under the terms of the GNU LGPL, version 2. +*/ +#include "libcflat.h" +#include "devicetree.h" +#include "alloc.h" +#include "pci.h" +#include "asm/pci.h" +#include "asm/io.h" +#include "pci-host-generic.h" +#include <linux/pci_regs.h> + +static struct pci_host_bridge *pci_host_bridge; + +static int of_flags_to_pci_type (u32 of_flags) +{ +static int type_map[] = { + [1 ] =PCI_BASE_ADDRESS_SPACE_IO , + [2 ] =PCI_BASE_ADDRESS_MEM_TYPE_32 , + [3 ] =PCI_BASE_ADDRESS_MEM_TYPE_64 + }; +int idx = (of_flags >>24 ) &0x03 ; +int res; + +assert (idx >0 ); + res = type_map[idx]; + +if (of_flags &0x40000000 ) + res |=PCI_BASE_ADDRESS_MEM_PREFETCH ; + +return res; +} + +static int pci_bar_type (u32 bar) +{ +if (bar &PCI_BASE_ADDRESS_SPACE ) +return PCI_BASE_ADDRESS_SPACE_IO ; +else +return bar & (PCI_BASE_ADDRESS_MEM_TYPE_MASK | +PCI_BASE_ADDRESS_MEM_PREFETCH ); +} + +/* +* Probe DT for a generic PCI host controller +* See kernel Documentation/devicetree/bindings/pci/host-generic-pci.txt +* and function gen_pci_probe() in drivers/pci/host/pci-host-generic.c +*/ +static struct pci_host_bridge *pci_dt_probe (void ) +{ +struct pci_host_bridge *host; +const void *fdt =dt_fdt (); +const struct fdt_property *prop; +struct dt_pbus_reg base; +struct dt_device dt_dev; +struct dt_bus dt_bus; +struct pci_addr_space *as; +fdt32_t *data; +u32 bus, bus_max; +u32 nac, nsc, nac_root, nsc_root; +int nr_range_cells, nr_addr_spaces; +int ret, node, len, i; + +if (!dt_available ()) { +printf ("No device tree found\n" ); +return NULL ; + } + +dt_bus_init_defaults (&dt_bus); +dt_device_init (&dt_dev, &dt_bus,NULL ); + + node =fdt_path_offset (fdt,"/" ); +assert (node >=0 ); + + ret =dt_get_nr_cells (node, &nac_root, &nsc_root); +assert (ret ==0 ); +assert (nac_root ==1 || nac_root ==2 ); + + node =fdt_node_offset_by_compatible (fdt, node, +"pci-host-ecam-generic" ); +if (node == -FDT_ERR_NOTFOUND ) { +printf ("No PCIe ECAM compatible controller found\n" ); +return NULL ; + } +assert (node >=0 ); + + prop =fdt_get_property (fdt, node,"device_type" , &len); +assert (prop && len ==4 && !strcmp ((char *)prop->data,"pci" )); + +dt_device_bind_node (&dt_dev, node); + ret =dt_pbus_get_base (&dt_dev, &base); +assert (ret ==0 ); + + prop =fdt_get_property (fdt, node,"bus-range" , &len); +if (prop ==NULL ) { +assert (len == -FDT_ERR_NOTFOUND ); + bus =0x00 ; + bus_max =0xff ; + }else { + data = (fdt32_t *)prop->data;
Quoted 8 lines
lib/pci-host-generic.c line 108
Quoted 5 lines
lib/pci-host-generic.c new file +5
-0+ bus =fdt32_to_cpu (data[0 ]); + bus_max =fdt32_to_cpu (data[1 ]); +assert (bus <= bus_max); + } +assert (bus_max < base.size / (1 <<PCI_ECAM_BUS_SHIFT ));Maybe
assert(bus_max + 1 == base.size / (1 << PCI_ECAM_BUS_SHIFT))? Or is there a reason to have a larger address space than necessary?
I can easily imagine a FW that does not bind number of busses vs address space size, for whatever reason. But if we align with the current QEMU implementation then == is better.
Quoted 292 lines
Quoted 18 lines
lib/pci-host-generic.c new file +18
-0+ ret =dt_get_nr_cells (node, &nac, &nsc); +assert (ret ==0 ); +assert (nac ==3 && nsc ==2 ); + + prop =fdt_get_property (fdt, node,"ranges" , &len); +assert (prop !=NULL ); + + nr_range_cells = nac + nsc + nac_root; + nr_addr_spaces = (len /4 ) / nr_range_cells; +assert (nr_addr_spaces); + + host =malloc (sizeof (*host) + +sizeof (host->addr_space[0 ]) * nr_addr_spaces); +assert (host !=NULL ); + + host->start = base.addr; + host->size = base.size; + host->bus = bus;lib/pci-host-generic.c line 137
Quoted 10 lines
lib/pci-host-generic.c new file +10
-0+ host->bus_max = bus_max; + host->nr_addr_spaces = nr_addr_spaces; + + data = (fdt32_t *)prop->data; + as = &host->addr_space[0 ]; + +for (i =0 ; i < nr_addr_spaces; i++) { +/* +* The PCI binding claims the numerical representation of a PCI +* address consists of three cells, encoded as follows: nit: No need to say 'claims', "The PCI binding encodes the PCI address with three cells as follows:"
Quoted 32 lines
lib/pci-host-generic.c new file +32
-0+* +* phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr +* phys.mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh +* phys.lo cell: llllllll llllllll llllllll llllllll +* +* PCI device bus address and flags are encoded into phys.high +* PCI 64 bit address is encoded into phys.mid and phys.low +*/ + as->type =of_flags_to_pci_type (fdt32_to_cpu (data[0 ])); + as->pci_start = ((u64 )fdt32_to_cpu (data[1 ]) <<32 ) | +fdt32_to_cpu (data[2 ]); + +if (nr_range_cells ==6 ) { + as->start =fdt32_to_cpu (data[3 ]); + as->size = ((u64 )fdt32_to_cpu (data[4 ]) <<32 ) | +fdt32_to_cpu (data[5 ]); + }else { + as->start = ((u64 )fdt32_to_cpu (data[3 ]) <<32 ) | +fdt32_to_cpu (data[4 ]); + as->size = ((u64 )fdt32_to_cpu (data[5 ]) <<32 ) | +fdt32_to_cpu (data[6 ]); + } + + data += nr_range_cells; + as++; + } + +return host; +} + +static u64 pci_alloc_resource (u32 bar,u64 size) +{lib/pci-host-generic.c line 179
Quoted 10 lines
lib/pci-host-generic.c new file +10
-0+struct pci_host_bridge *host = pci_host_bridge; +struct pci_addr_space *as = &host->addr_space[0 ]; +phys_addr_t addr; +u32 mask; +int i; + +for (i =0 ; i < host->nr_addr_spaces; i++) { +if (as->type ==pci_bar_type (bar)) +break ; + as++;nit: could have put this as++ up with the i++ like you do below
Quoted 211 lines
lib/pci-host-generic.c new file +115
-0+ } +if (i >= host->nr_addr_spaces) { +printf ("No PCI resource found for a device\n" ); +assert (0 ); + } + + mask =pci_bar_mask (bar); + size =ALIGN (size, ~mask +1 ); +assert (as->allocated + size <= as->size); + + addr = as->pci_start + as->allocated; + as->allocated += size; + +return addr; +} + +bool pci_probe (void ) +{ +pcidevaddr_t dev; +u8 header; +u32 cmd; +int i; + +assert (!pci_host_bridge); + pci_host_bridge =pci_dt_probe (); +if (!pci_host_bridge) +return false; + +for (dev =0 ; dev <256 ; dev++) { +if (!pci_dev_exists (dev)) +continue ; + +/* We are only interested in normal PCI devices */ + header =pci_config_readb (dev,PCI_HEADER_TYPE ); +if ((header &PCI_HEADER_TYPE_MASK ) !=PCI_HEADER_TYPE_NORMAL ) +continue ; + + cmd =PCI_COMMAND_SERR ; + +for (i =0 ; i <6 ; i++) { +u64 addr, size; +u32 bar; + + size =pci_bar_size (dev, i); +if (!size) +break ; + + bar =pci_bar_get (dev, i); + addr =pci_alloc_resource (bar, size); +pci_bar_set_addr (dev, i, addr); + +if (pci_bar_is_memory (dev, i)) + cmd |=PCI_COMMAND_MEMORY ; +else + cmd |=PCI_COMMAND_IO ; + +if (pci_bar_is64 (dev, i)) + i++; + } + +pci_config_writel (dev,PCI_COMMAND , cmd); + } + +return true; +} + +/* +* This function is to be called from pci_translate_addr() to provide +* mapping between this host bridge's PCI busses address and CPU physical +* address. +*/ +phys_addr_t pci_host_bridge_get_paddr (u64 pci_addr) +{ +struct pci_host_bridge *host = pci_host_bridge; +struct pci_addr_space *as = &host->addr_space[0 ]; +int i; + +for (i =0 ; i < host->nr_addr_spaces; i++, as++) { +if (pci_addr >= as->pci_start && + pci_addr < as->pci_start + as->size) +return as->start + (pci_addr - as->pci_start); + } + +return 0 ; +} + +static void __iomem *pci_get_dev_conf (struct pci_host_bridge *host,int devfn) +{ +return (void __iomem *)(unsigned long ) + host->start + (devfn <<PCI_ECAM_DEVFN_SHIFT ); +} + +u8 pci_config_readb (pcidevaddr_t dev,u8 off) +{ +void __iomem *conf =pci_get_dev_conf (pci_host_bridge, dev); +return readb (conf + off); +} + +u16 pci_config_readw (pcidevaddr_t dev,u8 off) +{ +void __iomem *conf =pci_get_dev_conf (pci_host_bridge, dev); +return readw (conf + off); +} + +u32 pci_config_readl (pcidevaddr_t dev,u8 off) +{ +void __iomem *conf =pci_get_dev_conf (pci_host_bridge, dev); +return readl (conf + off); +} + +void pci_config_writel (pcidevaddr_t dev,u8 off,u32 val) +{ +void __iomem *conf =pci_get_dev_conf (pci_host_bridge, dev); +writel (val, conf + off); +}lib/pci-host-generic.h new file +46
-0@@ -0,0 +1,46 @@ +#ifndef PCI_HOST_GENERIC_H +#define PCI_HOST_GENERIC_H +/* +* PCI host bridge supporting structures and constants +* +* Copyright (C) 2016, Red Hat Inc, Alexander Gordeev <[email protected]> +* +* This work is licensed under the terms of the GNU LGPL, version 2. +*/ +#include "libcflat.h" + +struct pci_addr_space { +phys_addr_t pci_start; +phys_addr_t start; +phys_addr_t size; +phys_addr_t allocated; +int type; +}; + +struct pci_host_bridge { +phys_addr_t start; +phys_addr_t size; +int bus; +int bus_max; +int nr_addr_spaces; +struct pci_addr_space addr_space[]; +}; + +/* +* The following constants are derived from Linux, see this source: +* +* drivers/pci/host/pci-host-generic.c +* struct gen_pci_cfg_bus_ops::bus_shift +* int gen_pci_parse_map_cfg_windows(struct gen_pci *pci) +* +* Documentation/devicetree/bindings/pci/host-generic-pci.txt describes +* ECAM Configuration Space is be memory-mapped by concatenating the various +* components to form an offset: +* +* cfg_offset(bus, device, function, register) = +* bus << 20 | device << 15 | function << 12 | register +*/ +#define PCI_ECAM_BUS_SHIFT 20 +#define PCI_ECAM_DEVFN_SHIFT 12 + +#endif lib/pci.c +2
-2@@ -27,13 +27,13 @@ pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id)return PCIDEVADDR_INVALID ; } -static uint32_t pci_bar_mask (uint32_t bar) +uint32_t pci_bar_mask (uint32_t bar) {return (bar &PCI_BASE_ADDRESS_SPACE_IO ) ?PCI_BASE_ADDRESS_IO_MASK :PCI_BASE_ADDRESS_MEM_MASK ; } -static uint32_t pci_bar_get (pcidevaddr_t dev,int bar_num) +uint32_t pci_bar_get (pcidevaddr_t dev,int bar_num) {return pci_config_readl (dev,PCI_BASE_ADDRESS_0 + bar_num *4 ); }lib/pci.h +3
-0@@ -15,6 +15,7 @@ enum { PCIDEVADDR_INVALID = 0xffff, };void pci_print (void ); +bool pci_probe (void );bool pci_dev_exists (pcidevaddr_t dev);pcidevaddr_t pci_find_dev (uint16_t vendor_id,uint16_t device_id);@@ -32,6 +33,8 @@ pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id);phys_addr_t pci_bar_get_addr (pcidevaddr_t dev,int bar_num);void pci_bar_set_addr (pcidevaddr_t dev,int bar_num,phys_addr_t addr);phys_addr_t pci_bar_size (pcidevaddr_t dev,int bar_num); +uint32_t pci_bar_get (pcidevaddr_t dev,int bar_num); +uint32_t pci_bar_mask (uint32_t bar);bool pci_bar_is64 (pcidevaddr_t dev,int bar_num);bool pci_bar_is_memory (pcidevaddr_t dev,int bar_num);bool pci_bar_is_valid (pcidevaddr_t dev,int bar_num);Signature · 1 line
1.8.3.1Looks great! Only a couple nits, so
Reviewed-by Andrew Jones <[email protected]>
Re: [kvm-unit-tests PATCH v4 09/12] pci: Add generic ECAM host support
In reply to Andrew JonesQuoted 26 lines · Andrew Jones
lib/pci-host-generic.c line 137
Quoted 4 lines · Alexander Gordeev
lib/pci-host-generic.c new file +4
-0+for (i =0 ; i < nr_addr_spaces; i++) { +/* +* The PCI binding claims the numerical representation of a PCI +* address consists of three cells, encoded as follows: nit: No need to say 'claims', "The PCI binding encodes the PCI address with three cells as follows:"
Quoted 17 lines
lib/pci-host-generic.c new file +17
-0+* +* phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr +* phys.mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh +* phys.lo cell: llllllll llllllll llllllll llllllll +* +* PCI device bus address and flags are encoded into phys.high +* PCI 64 bit address is encoded into phys.mid and phys.low +*/ + as->type =of_flags_to_pci_type (fdt32_to_cpu (data[0 ])); + as->pci_start = ((u64 )fdt32_to_cpu (data[1 ]) <<32 ) | +fdt32_to_cpu (data[2 ]); + +if (nr_range_cells ==6 ) { + as->start =fdt32_to_cpu (data[3 ]); + as->size = ((u64 )fdt32_to_cpu (data[4 ]) <<32 ) | +fdt32_to_cpu (data[5 ]); + }else {
Quoted 8 lines
lib/pci-host-generic.c line 162
Quoted 8 lines
lib/pci-host-generic.c new file +8
-0+ as->start = ((u64 )fdt32_to_cpu (data[3 ]) <<32 ) | +fdt32_to_cpu (data[4 ]); + as->size = ((u64 )fdt32_to_cpu (data[5 ]) <<32 ) | +fdt32_to_cpu (data[6 ]); + } + + data += nr_range_cells; + as++;
(2)
Quoted 11 lines
Quoted 11 lines
lib/pci-host-generic.c new file +11
-0+ } + +return host; +} + +static u64 pci_alloc_resource (u32 bar,u64 size) +{ +struct pci_host_bridge *host = pci_host_bridge; +struct pci_addr_space *as = &host->addr_space[0 ]; +phys_addr_t addr; +u32 mask;
Quoted 8 lines
lib/pci-host-generic.c line 179
Quoted 6 lines
lib/pci-host-generic.c new file +6
-0+int i; + +for (i =0 ; i < host->nr_addr_spaces; i++) { +if (as->type ==pci_bar_type (bar)) +break ; + as++;nit: could have put this as++ up with the i++ like you do below
Actually, I forgot to make it below (1) the same way as here :-) The reason is there are three cycles looping over address spaces and I wanted to make them alike. I chose (2) as the sample since these two assignents read better:
data += nr_range_cells; as++;
Will repost whatever you like, IOW ;)
Quoted 70 lines
Quoted 70 lines
lib/pci-host-generic.c new file +70
-0+ } +if (i >= host->nr_addr_spaces) { +printf ("No PCI resource found for a device\n" ); +assert (0 ); + } + + mask =pci_bar_mask (bar); + size =ALIGN (size, ~mask +1 ); +assert (as->allocated + size <= as->size); + + addr = as->pci_start + as->allocated; + as->allocated += size; + +return addr; +} + +bool pci_probe (void ) +{ +pcidevaddr_t dev; +u8 header; +u32 cmd; +int i; + +assert (!pci_host_bridge); + pci_host_bridge =pci_dt_probe (); +if (!pci_host_bridge) +return false; + +for (dev =0 ; dev <256 ; dev++) { +if (!pci_dev_exists (dev)) +continue ; + +/* We are only interested in normal PCI devices */ + header =pci_config_readb (dev,PCI_HEADER_TYPE ); +if ((header &PCI_HEADER_TYPE_MASK ) !=PCI_HEADER_TYPE_NORMAL ) +continue ; + + cmd =PCI_COMMAND_SERR ; + +for (i =0 ; i <6 ; i++) { +u64 addr, size; +u32 bar; + + size =pci_bar_size (dev, i); +if (!size) +break ; + + bar =pci_bar_get (dev, i); + addr =pci_alloc_resource (bar, size); +pci_bar_set_addr (dev, i, addr); + +if (pci_bar_is_memory (dev, i)) + cmd |=PCI_COMMAND_MEMORY ; +else + cmd |=PCI_COMMAND_IO ; + +if (pci_bar_is64 (dev, i)) + i++; + } + +pci_config_writel (dev,PCI_COMMAND , cmd); + } + +return true; +} + +/* +* This function is to be called from pci_translate_addr() to provide +* mapping between this host bridge's PCI busses address and CPU physical +* address.
Quoted 8 lines
lib/pci-host-generic.c line 257
Quoted 8 lines
+ */
lib/pci-host-generic.c new file +7
-0+phys_addr_t pci_host_bridge_get_paddr (u64 pci_addr) +{ +struct pci_host_bridge *host = pci_host_bridge; +struct pci_addr_space *as = &host->addr_space[0 ]; +int i; + +for (i =0 ; i < host->nr_addr_spaces; i++, as++) {
(1)
Quoted 7 lines
Quoted 7 lines
lib/pci-host-generic.c new file +7
-0+if (pci_addr >= as->pci_start && + pci_addr < as->pci_start + as->size) +return as->start + (pci_addr - as->pci_start); + } + +return 0 ; +}
Re: [kvm-unit-tests PATCH v4 09/12] pci: Add generic ECAM host support
Quoted 63 lines · Alexander Gordeev
Quoted 26 lines · Andrew Jones
lib/pci-host-generic.c line 137
Quoted 4 lines · Alexander Gordeev
lib/pci-host-generic.c new file +4
-0+for (i =0 ; i < nr_addr_spaces; i++) { +/* +* The PCI binding claims the numerical representation of a PCI +* address consists of three cells, encoded as follows: nit: No need to say 'claims', "The PCI binding encodes the PCI address with three cells as follows:"
Quoted 17 lines
lib/pci-host-generic.c new file +17
-0+* +* phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr +* phys.mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh +* phys.lo cell: llllllll llllllll llllllll llllllll +* +* PCI device bus address and flags are encoded into phys.high +* PCI 64 bit address is encoded into phys.mid and phys.low +*/ + as->type =of_flags_to_pci_type (fdt32_to_cpu (data[0 ])); + as->pci_start = ((u64 )fdt32_to_cpu (data[1 ]) <<32 ) | +fdt32_to_cpu (data[2 ]); + +if (nr_range_cells ==6 ) { + as->start =fdt32_to_cpu (data[3 ]); + as->size = ((u64 )fdt32_to_cpu (data[4 ]) <<32 ) | +fdt32_to_cpu (data[5 ]); + }else {Quoted 8 lines
lib/pci-host-generic.c line 162
Quoted 8 lines
lib/pci-host-generic.c new file +8
-0+ as->start = ((u64 )fdt32_to_cpu (data[3 ]) <<32 ) | +fdt32_to_cpu (data[4 ]); + as->size = ((u64 )fdt32_to_cpu (data[5 ]) <<32 ) | +fdt32_to_cpu (data[6 ]); + } + + data += nr_range_cells; + as++;(2)Quoted 11 lines
Quoted 11 lines
lib/pci-host-generic.c new file +11
-0+ } + +return host; +} + +static u64 pci_alloc_resource (u32 bar,u64 size) +{ +struct pci_host_bridge *host = pci_host_bridge; +struct pci_addr_space *as = &host->addr_space[0 ]; +phys_addr_t addr; +u32 mask;Quoted 8 lines
lib/pci-host-generic.c line 179
Quoted 6 lines
lib/pci-host-generic.c new file +6
-0+int i; + +for (i =0 ; i < host->nr_addr_spaces; i++) { +if (as->type ==pci_bar_type (bar)) +break ; + as++;nit: could have put this as++ up with the i++ like you do below
Actually, I forgot to make it below (1) the same way as here :-) The reason is there are three cycles looping over address spaces and I wanted to make them alike. I chose (2) as the sample since these two assignents read better:
Quoted 4 lines
data += nr_range_cells; as++;Will repost whatever you like, IOW ;)
whatever you like. it was just a nit.
Quoted 93 lines
Quoted 71 lines
Quoted 71 lines
lib/pci-host-generic.c new file +70
-0+ } +if (i >= host->nr_addr_spaces) { +printf ("No PCI resource found for a device\n" ); +assert (0 ); + } + + mask =pci_bar_mask (bar); + size =ALIGN (size, ~mask +1 ); +assert (as->allocated + size <= as->size); + + addr = as->pci_start + as->allocated; + as->allocated += size; + +return addr; +} + +bool pci_probe (void ) +{ +pcidevaddr_t dev; +u8 header; +u32 cmd; +int i; + +assert (!pci_host_bridge); + pci_host_bridge =pci_dt_probe (); +if (!pci_host_bridge) +return false; + +for (dev =0 ; dev <256 ; dev++) { +if (!pci_dev_exists (dev)) +continue ; + +/* We are only interested in normal PCI devices */ + header =pci_config_readb (dev,PCI_HEADER_TYPE ); +if ((header &PCI_HEADER_TYPE_MASK ) !=PCI_HEADER_TYPE_NORMAL ) +continue ; + + cmd =PCI_COMMAND_SERR ; + +for (i =0 ; i <6 ; i++) { +u64 addr, size; +u32 bar; + + size =pci_bar_size (dev, i); +if (!size) +break ; + + bar =pci_bar_get (dev, i); + addr =pci_alloc_resource (bar, size); +pci_bar_set_addr (dev, i, addr); + +if (pci_bar_is_memory (dev, i)) + cmd |=PCI_COMMAND_MEMORY ; +else + cmd |=PCI_COMMAND_IO ; + +if (pci_bar_is64 (dev, i)) + i++; + } + +pci_config_writel (dev,PCI_COMMAND , cmd); + } + +return true; +} + +/* +* This function is to be called from pci_translate_addr() to provide +* mapping between this host bridge's PCI busses address and CPU physical +* address. Quoted 8 lines
lib/pci-host-generic.c line 257
Quoted 8 lines
+ */
lib/pci-host-generic.c new file +7
-0+phys_addr_t pci_host_bridge_get_paddr (u64 pci_addr) +{ +struct pci_host_bridge *host = pci_host_bridge; +struct pci_addr_space *as = &host->addr_space[0 ]; +int i; + +for (i =0 ; i < host->nr_addr_spaces; i++, as++) {(1)Quoted 7 lines
lib/pci-host-generic.c line 264
Quoted 7 lines
lib/pci-host-generic.c new file +7
-0+if (pci_addr >= as->pci_start && + pci_addr < as->pci_start + as->size) +return as->start + (pci_addr - as->pci_start); + } + +return 0 ; +}--
To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v4 10/12 arm/arm64: pci: Add PCI bus operation test
In reply to Alexander Gordeev--- arm/Makefile.common | 6 +++++- arm/pci-test.c | 21 +++++++++++++++++++++ lib/arm/asm/pci.h | 26 ++++++++++++++++++++++++++ lib/arm64/asm/pci.h | 1 + 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 arm/pci-test.c create mode 100644 lib/arm/asm/pci.h create mode 100644 lib/arm64/asm/pci.h
arm/Makefile.common +5 -1
@@ -11,7 +11,8 @@ endif tests-common = \ $(TEST_DIR)/selftest.flat \ - $(TEST_DIR)/spinlock-test.flat + $(TEST_DIR)/spinlock-test.flat \ + $(TEST_DIR)/pci-test.flat all: test_cases
@@ -33,6 +34,8 @@ include scripts/asm-offsets.mak cflatobjs += lib/util.o cflatobjs += lib/alloc.o cflatobjs += lib/devicetree.o +cflatobjs += lib/pci.o +cflatobjs += lib/pci-host-generic.o cflatobjs += lib/virtio.o cflatobjs += lib/virtio-mmio.o cflatobjs += lib/chr-testdev.o
@@ -73,3 +76,4 @@ generated_files = $(asm-offsets) test_cases: $(generated_files) $(tests-common) $(tests) $(TEST_DIR)/selftest.o $(cstart.o): $(asm-offsets) +$(TEST_DIR)/pci-test.elf: $(cstart.o) $(TEST_DIR)/pci-test.o
arm/pci-test.c new file +21 -0
@@ -0,0 +1,21 @@ +/* +* PCI bus operation test +* +* Copyright (C) 2016, Red Hat Inc, Alexander Gordeev <[email protected]> +* +* This work is licensed under the terms of the GNU LGPL, version 2. +*/ +#include <libcflat.h> +#include <pci.h> + +int main (void ) +{ +int ret =pci_probe (); + +report ("PCI bus probing" , ret); + +if (ret) +pci_print (); + +return report_summary (); +}
lib/arm/asm/pci.h new file +26 -0
@@ -0,0 +1,26 @@ +#ifndef _ASMARM_PCI_H_ +#define _ASMARM_PCI_H_ +/* +* Copyright (C) 2016, Red Hat Inc, Alexander Gordeev <[email protected]> +* +* This work is licensed under the terms of the GNU LGPL, version 2. +*/ +#include "libcflat.h" + +phys_addr_t pci_host_bridge_get_paddr (uint64_t addr); + +static inline +phys_addr_t pci_translate_addr(pcidevaddr_t __unused dev, uint64_t addr) +{ +/* +* Assume we only have single PCI host bridge in a system. +*/ +return pci_host_bridge_get_paddr (addr); +} + +uint8_t pci_config_readb (pcidevaddr_t dev,uint8_t reg); +uint16_t pci_config_readw (pcidevaddr_t dev,uint8_t reg); +uint32_t pci_config_readl (pcidevaddr_t dev,uint8_t reg); +void pci_config_writel (pcidevaddr_t dev,uint8_t reg,uint32_t val); + +#endif
lib/arm64/asm/pci.h new file +1 -0
@@ -0,0 +1 @@ +#include "../../arm/asm/pci.h"
Signature · 1 line
1.8.3.1
Re: [kvm-unit-tests PATCH v4 10/12] arm/arm64: pci: Add PCI bus operation test
Quoted 37 lines · Alexander Gordeev
Cc Thomas Huth <[email protected]>Cc Andrew Jones <[email protected]>Signed-off-by Alexander Gordeev <[email protected]>--- arm/Makefile.common | 6 +++++- arm/pci-test.c | 21 +++++++++++++++++++++ lib/arm/asm/pci.h | 26 ++++++++++++++++++++++++++ lib/arm64/asm/pci.h | 1 + 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 arm/pci-test.c create mode 100644 lib/arm/asm/pci.h create mode 100644 lib/arm64/asm/pci.harm/Makefile.common +4
-1@@ -11,7 +11,8 @@ endif tests-common = \ $(TEST_DIR)/selftest.flat \ - $(TEST_DIR)/spinlock-test.flat + $(TEST_DIR)/spinlock-test.flat \ + $(TEST_DIR)/pci-test.flat all: test_cases@@ -33,6 +34,8 @@ include scripts/asm-offsets.mak cflatobjs += lib/util.o cflatobjs += lib/alloc.o cflatobjs += lib/devicetree.o +cflatobjs += lib/pci.o +cflatobjs += lib/pci-host-generic.o cflatobjs += lib/virtio.o cflatobjs += lib/virtio-mmio.o cflatobjs += lib/chr-testdev.o
arm/Makefile.common generated_files = $(asm-offsets) · line 79
Quoted 4 lines
arm/Makefile.common +1
-0@@ -73,3 +76,4 @@ generated_files = $(asm-offsets) test_cases: $(generated_files) $(tests-common) $(tests) $(TEST_DIR)/selftest.o $(cstart.o): $(asm-offsets) +$(TEST_DIR)/pci-test.elf: $(cstart.o) $(TEST_DIR)/pci-test.o
You don't need this line anymore. See 4c6b5d0b1c "arm/arm64:Makefile cleanup"
Quoted 36 lines
arm/pci-test.c new file +21
-0@@ -0,0 +1,21 @@ +/* +* PCI bus operation test +* +* Copyright (C) 2016, Red Hat Inc, Alexander Gordeev <[email protected]> +* +* This work is licensed under the terms of the GNU LGPL, version 2. +*/ +#include <libcflat.h> +#include <pci.h> + +int main (void ) +{ +int ret =pci_probe (); + +report ("PCI bus probing" , ret); + +if (ret) +pci_print (); + +return report_summary (); +}lib/arm/asm/pci.h new file +3
-0@@ -0,0 +1,26 @@ +#ifndef _ASMARM_PCI_H_ +#define _ASMARM_PCI_H_ +/*
lib/arm/asm/pci.h line 13
Quoted 10 lines
lib/arm/asm/pci.h new file +10
-0+* Copyright (C) 2016, Red Hat Inc, Alexander Gordeev <[email protected]> +* +* This work is licensed under the terms of the GNU LGPL, version 2. +*/ +#include "libcflat.h" + +phys_addr_t pci_host_bridge_get_paddr (uint64_t addr); + +static inline +phys_addr_t pci_translate_addr(pcidevaddr_t __unused dev, uint64_t addr)
__unused after dev
lib/arm/asm/pci.h line 26
Quoted 13 lines
lib/arm/asm/pci.h new file +13
-0+{ +/* +* Assume we only have single PCI host bridge in a system. +*/ +return pci_host_bridge_get_paddr (addr); +} + +uint8_t pci_config_readb (pcidevaddr_t dev,uint8_t reg); +uint16_t pci_config_readw (pcidevaddr_t dev,uint8_t reg); +uint32_t pci_config_readl (pcidevaddr_t dev,uint8_t reg); +void pci_config_writel (pcidevaddr_t dev,uint8_t reg,uint32_t val); + +#endif
The code in lib/arm/asm/pci.h could probably be in a generic include instead, and then included by lib/arm/asm/pci.h, but I'm OK with this until we add ppc64 support.
Quoted 10 lines
lib/arm64/asm/pci.h new file +1
-0@@ -0,0 +1 @@ +#include "../../arm/asm/pci.h" Signature · 1 line
1.8.3.1
Thanks,
drew
Re: [kvm-unit-tests PATCH v4 10/12] arm/arm64: pci: Add PCI bus operation test
Quoted 31 lines · Andrew Jones
Quoted 9 lines
lib/arm/asm/pci.h new file +3
-0@@ -0,0 +1,26 @@ +#ifndef _ASMARM_PCI_H_ +#define _ASMARM_PCI_H_ +/* lib/arm/asm/pci.h line 13
Quoted 10 lines
lib/arm/asm/pci.h new file +10
-0+* Copyright (C) 2016, Red Hat Inc, Alexander Gordeev <[email protected]> +* +* This work is licensed under the terms of the GNU LGPL, version 2. +*/ +#include "libcflat.h" + +phys_addr_t pci_host_bridge_get_paddr (uint64_t addr); + +static inline +phys_addr_t pci_translate_addr(pcidevaddr_t __unused dev, uint64_t addr)
__unusedafter devQuoted 9 lines
lib/arm/asm/pci.h new file +9
-0+{ +/* +* Assume we only have single PCI host bridge in a system. +*/ +return pci_host_bridge_get_paddr (addr); +} + +uint8_t pci_config_readb (pcidevaddr_t dev,uint8_t reg); +uint16_t pci_config_readw (pcidevaddr_t dev,uint8_t reg);
Quoted 8 lines
lib/arm/asm/pci.h line 26
Quoted 4 lines
lib/arm/asm/pci.h new file +4
-0+uint32_t pci_config_readl (pcidevaddr_t dev,uint8_t reg); +void pci_config_writel (pcidevaddr_t dev,uint8_t reg,uint32_t val); + +#endif The code in
lib/arm/asm/pci.hcould probably be in a generic include instead, and then included bylib/arm/asm/pci.h, but I'm OK with this until we add ppc64 support.
You mean moving the PCI accessor declarations to asm-generic/pci.h? But a generic include is rather for default implementations, not just declarations that still whould have be defined somewhere?
Quoted 13 lines
Quoted 10 lines
lib/arm64/asm/pci.h new file +1
-0@@ -0,0 +1 @@ +#include "../../arm/asm/pci.h" Signature · 1 line
1.8.3.1Thanks,
drew
Re: [kvm-unit-tests PATCH v4 10/12] arm/arm64: pci: Add PCI bus operation test
Quoted 41 lines · Alexander Gordeev
Quoted 31 lines · Andrew Jones
Quoted 9 lines
lib/arm/asm/pci.h new file +3
-0@@ -0,0 +1,26 @@ +#ifndef _ASMARM_PCI_H_ +#define _ASMARM_PCI_H_ +/* lib/arm/asm/pci.h line 13
Quoted 10 lines
lib/arm/asm/pci.h new file +10
-0+* Copyright (C) 2016, Red Hat Inc, Alexander Gordeev <[email protected]> +* +* This work is licensed under the terms of the GNU LGPL, version 2. +*/ +#include "libcflat.h" + +phys_addr_t pci_host_bridge_get_paddr (uint64_t addr); + +static inline +phys_addr_t pci_translate_addr(pcidevaddr_t __unused dev, uint64_t addr)
__unusedafter devQuoted 9 lines
lib/arm/asm/pci.h new file +9
-0+{ +/* +* Assume we only have single PCI host bridge in a system. +*/ +return pci_host_bridge_get_paddr (addr); +} + +uint8_t pci_config_readb (pcidevaddr_t dev,uint8_t reg); +uint16_t pci_config_readw (pcidevaddr_t dev,uint8_t reg);Quoted 8 lines
lib/arm/asm/pci.h line 26
Quoted 4 lines
lib/arm/asm/pci.h new file +4
-0+uint32_t pci_config_readl (pcidevaddr_t dev,uint8_t reg); +void pci_config_writel (pcidevaddr_t dev,uint8_t reg,uint32_t val); + +#endif The code in
lib/arm/asm/pci.hcould probably be in a generic include instead, and then included bylib/arm/asm/pci.h, but I'm OK with this until we add ppc64 support.
Quoted 3 lines
You mean moving the PCI accessor declarations to
asm-generic/pci.h? But a generic include is rather for default implementations, not just declarations that still whould have be defined somewhere?
Right, but the implementation of pci_translate_addr above looks pretty generic :-) Also putting the declarations somewhere common avoids every arch that needs them from adding them. Arches that don't want them, x86, simply don't include the common header. The common header doesn't need to be named asm-generic/pci.h. It can be asm/pci-host-bridge.h (or whatever). Since arm and ppc need the host bridge code for pci, then it makes sense for them to include host bridge headers from their asm/pci.h header.
Thanks,
drew
PATCH v4 11/12 pci: Add pci-testdev PCI bus test device
In reply to Alexander Gordeev--- lib/pci-testdev.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/pci.h | 7 +++ 2 files changed, 191 insertions(+) create mode 100644 lib/pci-testdev.c
lib/pci-testdev.c new file +184 -0
@@ -0,0 +1,184 @@ +/* +* QEMU "pci-testdev" PCI test device +* +* Copyright (C) 2016, Red Hat Inc, Alexander Gordeev <[email protected]> +* +* This work is licensed under the terms of the GNU LGPL, version 2. +*/ +#include "pci.h" +#include "asm/io.h" + +struct pci_testdev_ops { +u8 (*io_readb)(const volatile void *addr); +u16 (*io_readw)(const volatile void *addr); +u32 (*io_readl)(const volatile void *addr); +void (*io_writeb)(u8 value,volatile void *addr); +void (*io_writew)(u16 value,volatile void *addr); +void (*io_writel)(u32 value,volatile void *addr); +}; + +static u8 pio_readb (const volatile void *addr) +{ +return inb ((unsigned long )addr); +} + +static u16 pio_readw (const volatile void *addr) +{ +return inw ((unsigned long )addr); +} + +static u32 pio_readl (const volatile void *addr) +{ +return inl ((unsigned long )addr); +} + +static void pio_writeb (u8 value,volatile void *addr) +{ +outb (value, (unsigned long )addr); +} + +static void pio_writew (u16 value,volatile void *addr) +{ +outw (value, (unsigned long )addr); +} + +static void pio_writel (u32 value,volatile void *addr) +{ +outl (value, (unsigned long )addr); +} + +static struct pci_testdev_ops pci_testdev_io_ops = { + .io_readb = pio_readb, + .io_readw = pio_readw, + .io_readl = pio_readl, + .io_writeb = pio_writeb, + .io_writew = pio_writew, + .io_writel = pio_writel +}; + +static u8 mmio_readb (const volatile void *addr) +{ +return *(const volatile u8 __force *)addr; +} + +static u16 mmio_readw (const volatile void *addr) +{ +return *(const volatile u16 __force *)addr; +} + +static u32 mmio_readl (const volatile void *addr) +{ +return *(const volatile u32 __force *)addr; +} + +static void mmio_writeb (u8 value,volatile void *addr) +{ + *(volatile u8 __force *)addr = value; +} + +static void mmio_writew (u16 value,volatile void *addr) +{ + *(volatile u16 __force *)addr = value; +} + +static void mmio_writel (u32 value,volatile void *addr) +{ + *(volatile u32 __force *)addr = value; +} + +static struct pci_testdev_ops pci_testdev_mem_ops = { + .io_readb = mmio_readb, + .io_readw = mmio_readw, + .io_readl = mmio_readl, + .io_writeb = mmio_writeb, + .io_writew = mmio_writew, + .io_writel = mmio_writel +}; + +static bool pci_testdev_one (struct pci_test_dev_hdr *test, +int test_nr, +struct pci_testdev_ops *ops) +{ +u8 width; +u32 count, sig, off; +const int nr_writes =16 ; +int i; + + ops->io_writeb (test_nr, &test->test); + count = ops->io_readl (&test->count); +if (count !=0 ) +return false; + + width = ops->io_readb (&test->width); +if ((width !=1 ) && (width !=2 ) && (width !=4 )) +return false; + + sig = ops->io_readl (&test->data); + off = ops->io_readl (&test->offset); + +for (i =0 ; i < nr_writes; i++) { +switch (width) { +case 1 : ops->io_writeb (sig, (void *)test + off);break ; +case 2 : ops->io_writew (sig, (void *)test + off);break ; +case 4 : ops->io_writel (sig, (void *)test + off);break ; + } + } + +return (int )ops->io_readl (&test->count) == nr_writes; +} + +void pci_testdev_print (struct pci_test_dev_hdr *test, +struct pci_testdev_ops *ops) +{ +bool io = (ops == &pci_testdev_io_ops); +int i; + +printf ("pci-testdev %3s: " , io ?"io" :"mem" ); +for (i =0 ;; ++i) { +char c = ops->io_readb (&test->name[i]); +if (!c) +break ; +printf ("%c" , c); + } +printf ("\n" ); +} + +static int pci_testdev_all (struct pci_test_dev_hdr *test, +struct pci_testdev_ops *ops) +{ +int i; + +for (i =0 ;; i++) { +if (!pci_testdev_one (test, i, ops)) +break ; +pci_testdev_print (test, ops); + } + +return i; +} + +int pci_testdev (void ) +{ +phys_addr_t addr; +void __iomem *mem, *io; +pcidevaddr_t dev; +int nr_tests =0 ; + + dev =pci_find_dev (PCI_VENDOR_ID_REDHAT ,PCI_DEVICE_ID_REDHAT_TEST ); +if (dev ==PCIDEVADDR_INVALID ) +return -1 ; + +if (!pci_bar_is_valid (dev,0 ) || !pci_bar_is_valid (dev,1 )) +return -1 ; + + addr =pci_bar_get_addr (dev,0 ); + mem =ioremap (addr,0 ); + + addr =pci_bar_get_addr (dev,1 ); + io = (void *)(unsigned long )addr; + + nr_tests +=pci_testdev_all (mem, &pci_testdev_mem_ops); + nr_tests +=pci_testdev_all (io, &pci_testdev_io_ops); + +return nr_tests; +}
lib/pci.h +7 -0
@@ -39,6 +39,8 @@ bool pci_bar_is64(pcidevaddr_t dev, int bar_num);bool pci_bar_is_memory (pcidevaddr_t dev,int bar_num);bool pci_bar_is_valid (pcidevaddr_t dev,int bar_num); +int pci_testdev (void ); +/* * pci-testdev is a driver for the pci-testdev qemu pci device. The * device enables testing mmio and portio exits, and measuring their
@@ -47,7 +49,12 @@ bool pci_bar_is_valid(pcidevaddr_t dev, int bar_num);#define PCI_VENDOR_ID_REDHAT 0x1b36#define PCI_DEVICE_ID_REDHAT_TEST 0x0005 +/* +* pci-testdev supports at least three types of tests (via mmio and +* portio BARs): no-eventfd, wildcard-eventfd and datamatch-eventfd +*/ #define PCI_TESTDEV_NUM_BARS 2 +#define PCI_TESTDEV_NUM_TESTS 3struct pci_test_dev_hdr {uint8_t test;
Signature · 1 line
1.8.3.1
Re: [kvm-unit-tests PATCH v4 11/12] pci: Add pci-testdev PCI bus test device
Quoted 118 lines · Alexander Gordeev
Cc Thomas Huth <[email protected]>Cc Andrew Jones <[email protected]>Signed-off-by Alexander Gordeev <[email protected]>--- lib/pci-testdev.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/pci.h | 7 +++ 2 files changed, 191 insertions(+) create mode 100644 lib/pci-testdev.clib/pci-testdev.c new file +103
-0@@ -0,0 +1,184 @@ +/* +* QEMU "pci-testdev" PCI test device +* +* Copyright (C) 2016, Red Hat Inc, Alexander Gordeev <[email protected]> +* +* This work is licensed under the terms of the GNU LGPL, version 2. +*/ +#include "pci.h" +#include "asm/io.h" + +struct pci_testdev_ops { +u8 (*io_readb)(const volatile void *addr); +u16 (*io_readw)(const volatile void *addr); +u32 (*io_readl)(const volatile void *addr); +void (*io_writeb)(u8 value,volatile void *addr); +void (*io_writew)(u16 value,volatile void *addr); +void (*io_writel)(u32 value,volatile void *addr); +}; + +static u8 pio_readb (const volatile void *addr) +{ +return inb ((unsigned long )addr); +} + +static u16 pio_readw (const volatile void *addr) +{ +return inw ((unsigned long )addr); +} + +static u32 pio_readl (const volatile void *addr) +{ +return inl ((unsigned long )addr); +} + +static void pio_writeb (u8 value,volatile void *addr) +{ +outb (value, (unsigned long )addr); +} + +static void pio_writew (u16 value,volatile void *addr) +{ +outw (value, (unsigned long )addr); +} + +static void pio_writel (u32 value,volatile void *addr) +{ +outl (value, (unsigned long )addr); +} + +static struct pci_testdev_ops pci_testdev_io_ops = { + .io_readb = pio_readb, + .io_readw = pio_readw, + .io_readl = pio_readl, + .io_writeb = pio_writeb, + .io_writew = pio_writew, + .io_writel = pio_writel +}; + +static u8 mmio_readb (const volatile void *addr) +{ +return *(const volatile u8 __force *)addr; +} + +static u16 mmio_readw (const volatile void *addr) +{ +return *(const volatile u16 __force *)addr; +} + +static u32 mmio_readl (const volatile void *addr) +{ +return *(const volatile u32 __force *)addr; +} + +static void mmio_writeb (u8 value,volatile void *addr) +{ + *(volatile u8 __force *)addr = value; +} + +static void mmio_writew (u16 value,volatile void *addr) +{ + *(volatile u16 __force *)addr = value; +} + +static void mmio_writel (u32 value,volatile void *addr) +{ + *(volatile u32 __force *)addr = value; +} + +static struct pci_testdev_ops pci_testdev_mem_ops = { + .io_readb = mmio_readb, + .io_readw = mmio_readw, + .io_readl = mmio_readl, + .io_writeb = mmio_writeb, + .io_writew = mmio_writew, + .io_writel = mmio_writel +}; + +static bool pci_testdev_one (struct pci_test_dev_hdr *test, +int test_nr, +struct pci_testdev_ops *ops) +{ +u8 width; +u32 count, sig, off;
lib/pci-testdev.c line 113
Quoted 10 lines
lib/pci-testdev.c new file +10
-0+const int nr_writes =16 ; +int i; + + ops->io_writeb (test_nr, &test->test); + count = ops->io_readl (&test->count); +if (count !=0 ) +return false; + + width = ops->io_readb (&test->width); +if ((width !=1 ) && (width !=2 ) && (width !=4 ))
nit: no need for all the ()'s
Quoted 99 lines
lib/pci-testdev.c new file +71
-0+return false; + + sig = ops->io_readl (&test->data); + off = ops->io_readl (&test->offset); + +for (i =0 ; i < nr_writes; i++) { +switch (width) { +case 1 : ops->io_writeb (sig, (void *)test + off);break ; +case 2 : ops->io_writew (sig, (void *)test + off);break ; +case 4 : ops->io_writel (sig, (void *)test + off);break ; + } + } + +return (int )ops->io_readl (&test->count) == nr_writes; +} + +void pci_testdev_print (struct pci_test_dev_hdr *test, +struct pci_testdev_ops *ops) +{ +bool io = (ops == &pci_testdev_io_ops); +int i; + +printf ("pci-testdev %3s: " , io ?"io" :"mem" ); +for (i =0 ;; ++i) { +char c = ops->io_readb (&test->name[i]); +if (!c) +break ; +printf ("%c" , c); + } +printf ("\n" ); +} + +static int pci_testdev_all (struct pci_test_dev_hdr *test, +struct pci_testdev_ops *ops) +{ +int i; + +for (i =0 ;; i++) { +if (!pci_testdev_one (test, i, ops)) +break ; +pci_testdev_print (test, ops); + } + +return i; +} + +int pci_testdev (void ) +{ +phys_addr_t addr; +void __iomem *mem, *io; +pcidevaddr_t dev; +int nr_tests =0 ; + + dev =pci_find_dev (PCI_VENDOR_ID_REDHAT ,PCI_DEVICE_ID_REDHAT_TEST ); +if (dev ==PCIDEVADDR_INVALID ) +return -1 ; + +if (!pci_bar_is_valid (dev,0 ) || !pci_bar_is_valid (dev,1 )) +return -1 ; + + addr =pci_bar_get_addr (dev,0 ); + mem =ioremap (addr,0 ); + + addr =pci_bar_get_addr (dev,1 ); + io = (void *)(unsigned long )addr; + + nr_tests +=pci_testdev_all (mem, &pci_testdev_mem_ops); + nr_tests +=pci_testdev_all (io, &pci_testdev_io_ops); + +return nr_tests; +}lib/pci.h +7
-0@@ -39,6 +39,8 @@ bool pci_bar_is64(pcidevaddr_t dev, int bar_num);bool pci_bar_is_memory (pcidevaddr_t dev,int bar_num);bool pci_bar_is_valid (pcidevaddr_t dev,int bar_num); +int pci_testdev (void ); +/* * pci-testdev is a driver for the pci-testdev qemu pci device. The * device enables testing mmio and portio exits, and measuring their @@ -47,7 +49,12 @@ bool pci_bar_is_valid(pcidevaddr_t dev, int bar_num);#define PCI_VENDOR_ID_REDHAT 0x1b36#define PCI_DEVICE_ID_REDHAT_TEST 0x0005 +/* +* pci-testdev supports at least three types of tests (via mmio and +* portio BARs): no-eventfd, wildcard-eventfd and datamatch-eventfd +*/ #define PCI_TESTDEV_NUM_BARS 2 +#define PCI_TESTDEV_NUM_TESTS 3struct pci_test_dev_hdr {uint8_t test;Signature · 1 line
1.8.3.1
Just one nit. Looks great.
PATCH v4 12/12 arm/arm64: pci: Add pci-testdev PCI device operation test
In reply to Alexander Gordeev--- arm/Makefile.common | 1 + arm/pci-test.c | 14 ++++++++++++-- arm/run | 7 ++++++- 3 files changed, 19 insertions(+), 3 deletions(-)
arm/Makefile.common +1 -0
@@ -36,6 +36,7 @@ cflatobjs += lib/alloc.o cflatobjs += lib/devicetree.o cflatobjs += lib/pci.o cflatobjs += lib/pci-host-generic.o +cflatobjs += lib/pci-testdev.o cflatobjs += lib/virtio.o cflatobjs += lib/virtio-mmio.o cflatobjs += lib/chr-testdev.o
arm/pci-test.c +12 -2
@@ -13,9 +13,19 @@ int main(void)int ret =pci_probe ();report ("PCI bus probing" , ret); +if (!ret) + goto done; -if (ret) -pci_print (); +pci_print (); +if (pci_find_dev (PCI_VENDOR_ID_REDHAT , +PCI_DEVICE_ID_REDHAT_TEST ) ==PCIDEVADDR_INVALID ) + goto done; + + ret =pci_testdev (); +report ("PCI test device passed %d tests" , + ret >=PCI_TESTDEV_NUM_BARS *PCI_TESTDEV_NUM_TESTS , ret); + +done:return report_summary (); }
arm/run +6 -1
@@ -67,8 +67,13 @@ fi chr_testdev='-device virtio-serial-device' chr_testdev+=' -device virtconsole,chardev=ctd -chardev testdev,id=ctd' +pci_testdev= +if $qemu $M -device '?' 2>&1 | grep pci-testdev > /dev/null; then + pci_testdev="-device pci-testdev" +fi + M+=",accel=$ACCEL" -command="$qemu $M -cpu $processor $chr_testdev" +command="$qemu $M -cpu $processor $chr_testdev $pci_testdev" command+=" -display none -serial stdio -kernel" command="$(timeout_cmd) $command" echo $command "$@"
Signature · 1 line
1.8.3.1
Re: [kvm-unit-tests PATCH v4 12/12] arm/arm64: pci: Add pci-testdev PCI device operation test
Quoted 69 lines · Alexander Gordeev
Cc Thomas Huth <[email protected]>Cc Andrew Jones <[email protected]>Signed-off-by Alexander Gordeev <[email protected]>--- arm/Makefile.common | 1 + arm/pci-test.c | 14 ++++++++++++-- arm/run | 7 ++++++- 3 files changed, 19 insertions(+), 3 deletions(-)arm/Makefile.common +1
-0@@ -36,6 +36,7 @@ cflatobjs += lib/alloc.o cflatobjs += lib/devicetree.o cflatobjs += lib/pci.o cflatobjs += lib/pci-host-generic.o +cflatobjs += lib/pci-testdev.o cflatobjs += lib/virtio.o cflatobjs += lib/virtio-mmio.o cflatobjs += lib/chr-testdev.oarm/pci-test.c +12
-2@@ -13,9 +13,19 @@ int main(void)int ret =pci_probe ();report ("PCI bus probing" , ret); +if (!ret) + goto done; -if (ret) -pci_print (); +pci_print (); +if (pci_find_dev (PCI_VENDOR_ID_REDHAT , +PCI_DEVICE_ID_REDHAT_TEST ) ==PCIDEVADDR_INVALID ) + goto done; + + ret =pci_testdev (); +report ("PCI test device passed %d tests" , + ret >=PCI_TESTDEV_NUM_BARS *PCI_TESTDEV_NUM_TESTS , ret); + +done:return report_summary (); }arm/run +6
-1@@ -67,8 +67,13 @@ fi chr_testdev='-device virtio-serial-device' chr_testdev+=' -device virtconsole,chardev=ctd -chardev testdev,id=ctd' +pci_testdev= +if $qemu $M -device '?' 2>&1 | grep pci-testdev > /dev/null; then + pci_testdev="-device pci-testdev" +fi + M+=",accel=$ACCEL" -command="$qemu $M -cpu $processor $chr_testdev" +command="$qemu $M -cpu $processor $chr_testdev $pci_testdev" command+=" -display none -serial stdio -kernel" command="$(timeout_cmd) $command" echo $command "$@"Signature · 1 line
1.8.3.1
Quoted 11 lines · Alexander Gordeev
Hi all,
This series should be applied on top of "Cleanup low-level arch code" series which is still not included. Yet, it is ready for review as all previous comments and suggestions are addressed.
There might be some confusion about version numbering as I posted the previous version as RFC with no version number at all. In fact it was 3rd version so I am labelling this series as v4. Unlike the RFC it does not have gaps in implementation.
Quoted 4 lines
There are quite a lot of changes since the previous version.
I tried pci-testdev against ARM and got the device semi-operational. It is still to investigate, but that could be addressed separately.
I'd like to either have this resolved, or at least understood it before we accept the series. If you drop your pci-testdev implementation (patch 11/12) into x86/vmexit.c, in order to replace the implementation there, does pci-testdev still work? I.e. have you checked if x86 works both with its current implementation and yours?
Quoted 4 lines
Most interesting - writing to IO BAR on ARM does not seem working as a written value does not read back. Probably, ARM64 is also affected, but again - I have not investigated it yet.
Maybe add some tracing to QEMU to see if the writes are making there.
Anyway, this series has really improved! I'm glad to see it coming together!
Thanks,
drew
Quoted 41 lines
Cc Thomas Huth <[email protected]>Cc Andrew Jones <[email protected]>Alexander Gordeev (12):
pci: Fix coding style in generic PCI files
pci: x86: Renamepci_config_read()topci_config_readl()
pci: x86: Add remaining PCI configuration space accessors
pci: Reworkpci_bar_addr()
pci: Factor outpci_bar_get()
pci: Addpci_bar_set_addr()
pci: Addpci_dev_exists()
pci: Addpci_print()
pci: Add generic ECAM host support
arm/arm64: pci: Add PCI bus operation test
pci: Add pci-testdev PCI bus test device arm/arm64: pci: Add pci-testdev PCI device operation testarm/Makefile.common | 7 +- arm/pci-test.c | 31 ++++++ arm/run | 7 +- lib/arm/asm/pci.h | 26 +++++ lib/arm64/asm/pci.h | 1 + lib/pci-host-generic.c | 294 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/pci-host-generic.h | 46 ++++++++ lib/pci-testdev.c | 184 +++++++++++++++++++++++++++++++ lib/pci.c | 194 ++++++++++++++++++++++++++++---- lib/pci.h | 33 +++++- lib/x86/asm/pci.h | 31 +++++- x86/vmexit.c | 4 +- 12 files changed, 830 insertions(+), 28 deletions(-) create mode 100644 arm/pci-test.c create mode 100644 lib/arm/asm/pci.h create mode 100644 lib/arm64/asm/pci.h create mode 100644 lib/pci-host-generic.c create mode 100644 lib/pci-host-generic.h create mode 100644 lib/pci-testdev.cSignature · 1 line
1.8.3.1
Quoted 17 lines · Andrew Jones
Quoted 11 lines · Alexander Gordeev
Hi all,
This series should be applied on top of "Cleanup low-level arch code" series which is still not included. Yet, it is ready for review as all previous comments and suggestions are addressed.
There might be some confusion about version numbering as I posted the previous version as RFC with no version number at all. In fact it was 3rd version so I am labelling this series as v4. Unlike the RFC it does not have gaps in implementation.
Quoted 4 lines
There are quite a lot of changes since the previous version.
I tried pci-testdev against ARM and got the device semi-operational. It is still to investigate, but that could be addressed separately.
Quoted 6 lines
I'd like to either have this resolved, or at least understood it before we accept the series. If you drop your pci-testdev implementation (patch 11/12) into
x86/vmexit.c, in order to replace the implementation there, does pci-testdev still work? I.e. have you checked if x86 works both with its current implementation and yours?
pci-testdev does not work. It does not work for x86/vmexit.c as well, but unlike my implementation x86/vmexit.c does not check status of device writes and therefore fails to notice that the writes do not take place for "wildcard-eventfd" and "datamatch-eventfd" cases. CONFIG_EVENTFD is set, so I am not sure what is happening here.
pci-testdev also does not implement 16 and 32 bit accesses, although x86/vmexit.c and pci/pci-testdev.c do have code to test these two. It is not a bug though.
Quoted 54 lines
Quoted 3 lines
Most interesting - writing to IO BAR on ARM does not seem working as a written value does not read back. Probably, ARM64 is also affected, but again - I have not investigated it yet.
Maybe add some tracing to QEMU to see if the writes are making there.
Anyway, this series has really improved! I'm glad to see it coming together!
Thanks,
drewQuoted 41 lines
Cc Thomas Huth <[email protected]>Cc Andrew Jones <[email protected]>Alexander Gordeev (12):
pci: Fix coding style in generic PCI files
pci: x86: Renamepci_config_read()topci_config_readl()
pci: x86: Add remaining PCI configuration space accessors
pci: Reworkpci_bar_addr()
pci: Factor outpci_bar_get()
pci: Addpci_bar_set_addr()
pci: Addpci_dev_exists()
pci: Addpci_print()
pci: Add generic ECAM host support
arm/arm64: pci: Add PCI bus operation test
pci: Add pci-testdev PCI bus test device arm/arm64: pci: Add pci-testdev PCI device operation testarm/Makefile.common | 7 +- arm/pci-test.c | 31 ++++++ arm/run | 7 +- lib/arm/asm/pci.h | 26 +++++ lib/arm64/asm/pci.h | 1 + lib/pci-host-generic.c | 294 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/pci-host-generic.h | 46 ++++++++ lib/pci-testdev.c | 184 +++++++++++++++++++++++++++++++ lib/pci.c | 194 ++++++++++++++++++++++++++++---- lib/pci.h | 33 +++++- lib/x86/asm/pci.h | 31 +++++- x86/vmexit.c | 4 +- 12 files changed, 830 insertions(+), 28 deletions(-) create mode 100644 arm/pci-test.c create mode 100644 lib/arm/asm/pci.h create mode 100644 lib/arm64/asm/pci.h create mode 100644 lib/pci-host-generic.c create mode 100644 lib/pci-host-generic.h create mode 100644 lib/pci-testdev.cSignature · 1 line
1.8.3.1
Quoted 10 lines · Alexander Gordeev
Quoted 9 lines
Quoted 2 lines
I tried pci-testdev against ARM and got the device semi-operational. It is still to investigate, but that could be addressed separately.
I'd like to either have this resolved, or at least understood it before we accept the series. If you drop your pci-testdev implementation (patch 11/12) into
x86/vmexit.c, in order to replace the implementation there, does pci-testdev still work? I.e. have you checked if x86 works both with its current implementation and yours?
Quoted 5 lines
pci-testdev does not work. It does not work for
x86/vmexit.cas well, but unlike my implementationx86/vmexit.cdoes not check status of device writes and therefore fails to notice that the writes do not take place for "wildcard-eventfd" and "datamatch-eventfd" cases.CONFIG_EVENTFDis set, so I am not sure what is happening here.
So I could define PCI_TESTDEV_NUM_TESTS to 1 and hence skip failing tests. But on the other hand these failures are exposing a problem in QEMU and therefore fulfilling kvm-unit-tests purpose :)
Quoted 3 lines
pci-testdev also does not implement 16 and 32 bit accesses, although
x86/vmexit.candpci/pci-testdev.cdo have code to test these two. It is not a bug though.
Quoted 17 lines · Alexander Gordeev
Quoted 10 lines · Alexander Gordeev
Quoted 9 lines
Quoted 2 lines
I tried pci-testdev against ARM and got the device semi-operational. It is still to investigate, but that could be addressed separately.
I'd like to either have this resolved, or at least understood it before we accept the series. If you drop your pci-testdev implementation (patch 11/12) into
x86/vmexit.c, in order to replace the implementation there, does pci-testdev still work? I.e. have you checked if x86 works both with its current implementation and yours?Quoted 5 lines
pci-testdev does not work. It does not work for
x86/vmexit.cas well, but unlike my implementationx86/vmexit.cdoes not check status of device writes and therefore fails to notice that the writes do not take place for "wildcard-eventfd" and "datamatch-eventfd" cases.CONFIG_EVENTFDis set, so I am not sure what is happening here.
Quoted 3 lines
So I could define
PCI_TESTDEV_NUM_TESTSto 1 and hence skip failing tests. But on the other hand these failures are exposing a problem in QEMU and therefore fulfilling kvm-unit-tests purpose :)
Can you collect enough details to report it on qemu-devel?
Thanks,
drew
Quoted 8 lines
Quoted 4 lines
pci-testdev also does not implement 16 and 32 bit accesses, although
x86/vmexit.candpci/pci-testdev.cdo have code to test these two. It is not a bug though.--
To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 6 lines · Andrew Jones
I'd like to either have this resolved, or at least understood it before we accept the series. If you drop your pci-testdev implementation (patch 11/12) into
x86/vmexit.c, in order to replace the implementation there, does pci-testdev still work? I.e. have you checked if x86 works both with its current implementation and yours?
What if I post for inclusion updated patches 1-10/12 and continue working on barriers/io/pci-testdev?
Quoted 2 lines
Thanks,
drew
Quoted 10 lines · Alexander Gordeev
Quoted 6 lines · Andrew Jones
I'd like to either have this resolved, or at least understood it before we accept the series. If you drop your pci-testdev implementation (patch 11/12) into
x86/vmexit.c, in order to replace the implementation there, does pci-testdev still work? I.e. have you checked if x86 works both with its current implementation and yours?What if I post for inclusion updated patches 1-10/12 and continue working on barriers/io/pci-testdev?
Hmm.. the only way I can test the early patches without pci-testdev is to dump the data structures, but that doesn't really test anything other than the DT parsing. I'd rather we wait.
Thanks,
drew
Quoted 9 lines · Andrew Jones
Quoted 8 lines · Alexander Gordeev
Quoted 6 lines · Andrew Jones
I'd like to either have this resolved, or at least understood it before we accept the series. If you drop your pci-testdev implementation (patch 11/12) into
x86/vmexit.c, in order to replace the implementation there, does pci-testdev still work? I.e. have you checked if x86 works both with its current implementation and yours?
Quoted 6 lines
Quoted 2 lines
What if I post for inclusion updated patches 1-10/12 and continue working on barriers/io/pci-testdev?
Hmm.. the only way I can test the early patches without pci-testdev is to dump the data structures, but that doesn't really test anything other than the DT parsing. I'd rather we wait.
It also involves what would be PCI configuration space cycles: PCI bus scanning and PCI devices inquiring and parsing. Not only DT.
Quoted 2 lines
Thanks,
drew
Quoted 13 lines · Alexander Gordeev
Quoted 12 lines · Andrew Jones
Quoted 10 lines · Alexander Gordeev
Quoted 6 lines · Andrew Jones
I'd like to either have this resolved, or at least understood it before we accept the series. If you drop your pci-testdev implementation (patch 11/12) into
x86/vmexit.c, in order to replace the implementation there, does pci-testdev still work? I.e. have you checked if x86 works both with its current implementation and yours?What if I post for inclusion updated patches 1-10/12 and continue working on barriers/io/pci-testdev?
Quoted 6 lines
Quoted 3 lines
Hmm.. the only way I can test the early patches without pci-testdev is to dump the data structures, but that doesn't really test anything other than the DT parsing. I'd rather we wait.
It also involves what would be PCI configuration space cycles: PCI bus scanning and PCI devices inquiring and parsing. Not only DT.
But how do I know any of that stuff is working?
Quoted 7 lines
Quoted 3 lines
Thanks,
drew--
To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 8 lines · Andrew Jones
Quoted 6 lines
Quoted 3 lines
Hmm.. the only way I can test the early patches without pci-testdev is to dump the data structures, but that doesn't really test anything other than the DT parsing. I'd rather we wait.
It also involves what would be PCI configuration space cycles: PCI bus scanning and PCI devices inquiring and parsing. Not only DT.
But how do I know any of that stuff is working?
Patch 10/12:
int main(void)
{
int ret = pci_probe();
report("PCI bus probing", ret);
if (ret)
pci_print();
return report_summary();
}Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Thanks,
drew
Quoted 13 lines · Alexander Gordeev
Quoted 8 lines · Andrew Jones
Quoted 6 lines
Quoted 3 lines
Hmm.. the only way I can test the early patches without pci-testdev is to dump the data structures, but that doesn't really test anything other than the DT parsing. I'd rather we wait.
It also involves what would be PCI configuration space cycles: PCI bus scanning and PCI devices inquiring and parsing. Not only DT.
But how do I know any of that stuff is working?
Patch 10/12:
int main(void)
Quoted 10 lines
{
int ret = pci_probe();
report("PCI bus probing", ret);
if (ret)
pci_print();
return report_summary();
}Well, if you plug some random PCI devices into kvm-unit-tests, and do the pci_print, and then compare with an lspci in a guest with the same devices, and get the equivalent output, then I'd be pretty convinced, but, even then, what's the point? Without the pci-testdev (the only pci device we're currently targeting) then none of this series is really necessary.
Thanks,
drew
Quoted 8 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Thanks,
drew--
To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 25 lines · Andrew Jones
Quoted 13 lines · Alexander Gordeev
Quoted 8 lines · Andrew Jones
Quoted 6 lines
Quoted 3 lines
Hmm.. the only way I can test the early patches without pci-testdev is to dump the data structures, but that doesn't really test anything other than the DT parsing. I'd rather we wait.
It also involves what would be PCI configuration space cycles: PCI bus scanning and PCI devices inquiring and parsing. Not only DT.
But how do I know any of that stuff is working?
Patch 10/12:
int main(void)
Quoted 10 lines
{ int ret = pci_probe(); report("PCI bus probing", ret); if (ret) pci_print(); return report_summary(); }
Quoted 6 lines
Well, if you plug some random PCI devices into kvm-unit-tests, and do the
pci_print, and then compare with an lspci in a guest with the same devices, and get the equivalent output, then I'd be pretty convinced, but, even then, what's the point? Without the pci-testdev (the only pci device we're currently targeting) then none of this series is really necessary.
My point is even without pci-testdev the above is of some value, because it only misses MMIO/PIO test and (a custom) pci-testdev protocol - which is not a PCI test per se.
So even without pci-testdev PCI configuration space is checked, reading/writing to devices' BARs etc. All of those is part of QEMU PCI framwork, not pci-testdev implementation.
But surely, I am not sure how valuable this test alone is.
Quoted 11 lines
Thanks,
drewQuoted 8 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Thanks,
drew--
To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html