diff mbox

Add qword read/write support.

Message ID 1246905824-12695-2-git-send-email-turkal@google.com (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Warren Turkal July 6, 2009, 6:43 p.m. UTC
Signed-off-by: Warren Turkal <turkal@google.com>
---
 lib/access.c |   15 +++++++++++++++
 lib/pci.h    |    2 ++
 lib/sysdep.h |   18 ++++++++++++++++++
 lib/types.h  |   35 +++++++++++++++++++++++------------
 setpci.c     |   19 ++++++++++++++++---
 setpci.man   |    6 +++---
 6 files changed, 77 insertions(+), 18 deletions(-)

Comments

Martin Mareš July 6, 2009, 7:08 p.m. UTC | #1
Hello!

> * Hopefully fixed ABI issue with u64. Please scrutinize.
>  - I am wondering if we shouldn't just take the ABI hit and just change
>    pciaddr_t to be u64 for simplicity. I can't imagine that there are that many
>    apps that rely on the library that can't be recompiled.

I would really like to keep the ABI of the shared library compatible
as long as possible -- both having to recompile everything and keeping
multiple versions of the same library on a single system are very
annoying.

> diff --git a/lib/types.h b/lib/types.h
> index 3e0e5c3..818872d 100644
> --- a/lib/types.h
> +++ b/lib/types.h
> @@ -15,39 +15,50 @@
>  typedef BYTE u8;
>  typedef WORD u16;
>  typedef DWORD u32;
> +typedef unsigned __int64 u64;
> +
> +#ifdef PCI_HAVE_64BIT_ADDRESS
> +#include <limits.h>
> +#if ULONG_MAX > 0xffffffff
> +#define PRIu64 "l"
> +#else
> +#define PRIu64 "ll"
> +#endif
> +#endif

Does this make sense? Does anyone promise what format sequence should be used
with __int64?

>  #elif defined(PCI_HAVE_STDINT_H) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
> -#include <stdint.h>
> +#include <inttypes.h>

Is there any guarantee that <inttypes.h> is available? Isn't it C99 only?

>  #ifdef PCI_HAVE_64BIT_ADDRESS
>  #include <limits.h>
>  #if ULONG_MAX > 0xffffffff
> -typedef unsigned long u64;
> -#define PCI_U64_FMT "l"
> +typedef unsigned long pciaddr_t;
>  #else
> -typedef unsigned long long u64;
> -#define PCI_U64_FMT "ll"
> -#endif
> +typedef unsigned long long pciaddr_t;
>  #endif
>  
> -#endif	/* PCI_HAVE_Uxx_TYPES */
> +#define PCIADDR_T_FMT "%08" PRIu64 "x"
> +#define PCIADDR_PORT_FMT "%04" PRIu64 "x"

You cannot do this. `unsigned long long' and PRIu64 do not mix.

> --- a/setpci.c
> +++ b/setpci.c
> @@ -12,6 +12,7 @@
>  #include <stdarg.h>
>  #include <unistd.h>
>  #include <errno.h>
> +#include <inttypes.h>

Again, you cannot rely on C99.

> -Finally, you should append a width specifier \fB.B\fP, \fB.W\fP, or \fB.L\fP to choose
> -how many bytes (1, 2, or 4) should be transferred. The width can be omitted if you are
> -accessing a named register whose width is well known.
> +Finally, you should append a width specifier \fB.B\fP, \fB.W\fP, \fB.L\fP, or \fB.Q\fP
> +to choose how many bytes (1, 2, 4, or 8) should be transferred. The width can be
> +omitted if you are accessing a named register whose width is well known.

Please emphasize that the 8-byte accesses are not atomic, unlike their shorter
counterparts.

				Have a nice fortnight
Matthew Wilcox July 6, 2009, 7:22 p.m. UTC | #2
On Mon, Jul 06, 2009 at 09:08:21PM +0200, Martin Mares wrote:
> > +Finally, you should append a width specifier \fB.B\fP, \fB.W\fP, \fB.L\fP, or \fB.Q\fP
> > +to choose how many bytes (1, 2, 4, or 8) should be transferred. The width can be
> > +omitted if you are accessing a named register whose width is well known.
> 
> Please emphasize that the 8-byte accesses are not atomic, unlike their shorter
> counterparts.

Do you want us to start working on an 8-byte atomic access method inside
the kernel?  As I said before, it won't be available on all systems.

Or should we stand firm against this bad habit of putting a 64-bit
register in config space?
Rolf Eike Beer July 6, 2009, 7:33 p.m. UTC | #3
Martin Mares wrote:

> > diff --git a/lib/types.h b/lib/types.h
> > index 3e0e5c3..818872d 100644
> > --- a/lib/types.h
> > +++ b/lib/types.h
> > @@ -15,39 +15,50 @@
> >  typedef BYTE u8;
> >  typedef WORD u16;
> >  typedef DWORD u32;
> > +typedef unsigned __int64 u64;
> > +
> > +#ifdef PCI_HAVE_64BIT_ADDRESS
> > +#include <limits.h>
> > +#if ULONG_MAX > 0xffffffff
> > +#define PRIu64 "l"
> > +#else
> > +#define PRIu64 "ll"
> > +#endif
> > +#endif
>
> Does this make sense? Does anyone promise what format sequence should be
> used with __int64?

No, it doesn't. "I64u" is the correct one. 
http://code.google.com/p/msinttypes/ is a good point to start with that MSVC 
mess.

> >  #elif defined(PCI_HAVE_STDINT_H) || (defined(__STDC_VERSION__) &&
> > __STDC_VERSION__ >= 199901L) -#include <stdint.h>
> > +#include <inttypes.h>
>
> Is there any guarantee that <inttypes.h> is available? Isn't it C99 only?

It is. And MSVC is my favorite example of a compiler that still doesn't 
support it 10 years later.

Greetings,

Eike
Warren Turkal July 6, 2009, 7:53 p.m. UTC | #4
On Mon, Jul 6, 2009 at 12:22, Matthew Wilcox<matthew@wil.cx> wrote:
> Do you want us to start working on an 8-byte atomic access method inside
> the kernel?  As I said before, it won't be available on all systems.
>
> Or should we stand firm against this bad habit of putting a 64-bit
> register in config space?

I'm not sure there is much we can do about it. At least Intel already
has hardware with 64-bit registers in config space on the market. I
imagine there will be others.

Having said that, my current case doesn't require it, but I am sure it
would be nice.

wt
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Warren Turkal July 6, 2009, 7:55 p.m. UTC | #5
On Mon, Jul 6, 2009 at 12:33, Rolf Eike Beer<eike-kernel@sf-tec.de> wrote:
> No, it doesn't. "I64u" is the correct one.
> http://code.google.com/p/msinttypes/ is a good point to start with that MSVC
> mess.

Should we just include the headers from that project? They are under
the BSD license, so I don't think it'd be a problem.

wt
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Warren Turkal July 6, 2009, 8:30 p.m. UTC | #6
On Mon, Jul 6, 2009 at 12:08, Martin Mares<mj@ucw.cz> wrote:
...
> Does this make sense? Does anyone promise what format sequence should be used
> with __int64?

Fixed with Rolf's suggested I64u for take 6. It looks like:
typedef I64u u64;

>>  #elif defined(PCI_HAVE_STDINT_H) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
>> -#include <stdint.h>
>> +#include <inttypes.h>
>
> Is there any guarantee that <inttypes.h> is available? Isn't it C99 only?

Doesn't __STDC_VERSION__ >= 199901L indicate C99+?

That's why I put it within this block. In fact stdint.h, is also a c99
header, so I think this instance of inttypes.h is ok.

...but there is another just below. Please forgive me for assuming if
I am wrong, but were you thinking of the spot a few lines down under
#else of that same if block, where I also included inttypes.h? If
that's the case, I have a question. Are we actually supporting
platforms, other than MS Windows, that don't include the stdint.h and
inttypes.h headers from C99? For instance, this works out of the box
on GCC. It also works with GCC if I add the -c99 flag.

I am basically wondering inttypes.h just exists on all the platforms
we support, other than MS Windows.

For the MS Windows case, could we include the headers from the project
that Rolf mentioned at [1]?

>>  #ifdef PCI_HAVE_64BIT_ADDRESS
>>  #include <limits.h>
>>  #if ULONG_MAX > 0xffffffff
>> -typedef unsigned long u64;
>> -#define PCI_U64_FMT "l"
>> +typedef unsigned long pciaddr_t;
>>  #else
>> -typedef unsigned long long u64;
>> -#define PCI_U64_FMT "ll"
>> -#endif
>> +typedef unsigned long long pciaddr_t;
>>  #endif
>>
>> -#endif       /* PCI_HAVE_Uxx_TYPES */
>> +#define PCIADDR_T_FMT "%08" PRIu64 "x"
>> +#define PCIADDR_PORT_FMT "%04" PRIu64 "x"
>
> You cannot do this. `unsigned long long' and PRIu64 do not mix.

Ok. I'll take another shot at this. I'm really trying, thanks for
being patient. :)

>> --- a/setpci.c
>> +++ b/setpci.c
>> @@ -12,6 +12,7 @@
>>  #include <stdarg.h>
>>  #include <unistd.h>
>>  #include <errno.h>
>> +#include <inttypes.h>
>
> Again, you cannot rely on C99.

This file ultimately includes lib/types.h, so it wasn't needed anyway.
Done for take 6.

>> -Finally, you should append a width specifier \fB.B\fP, \fB.W\fP, or \fB.L\fP to choose
>> -how many bytes (1, 2, or 4) should be transferred. The width can be omitted if you are
>> -accessing a named register whose width is well known.
>> +Finally, you should append a width specifier \fB.B\fP, \fB.W\fP, \fB.L\fP, or \fB.Q\fP
>> +to choose how many bytes (1, 2, 4, or 8) should be transferred. The width can be
>> +omitted if you are accessing a named register whose width is well known.
>
> Please emphasize that the 8-byte accesses are not atomic, unlike their shorter
> counterparts.

Done for take 6.

BTW, I am about to post take 6, so please post comments in that part
of the thread.

[1]http://code.google.com/p/msinttypes/

wt
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Matthew Wilcox July 6, 2009, 8:48 p.m. UTC | #7
On Mon, Jul 06, 2009 at 12:53:54PM -0700, Warren Turkal wrote:
> On Mon, Jul 6, 2009 at 12:22, Matthew Wilcox<matthew@wil.cx> wrote:
> > Do you want us to start working on an 8-byte atomic access method inside
> > the kernel? ??As I said before, it won't be available on all systems.
> >
> > Or should we stand firm against this bad habit of putting a 64-bit
> > register in config space?
> 
> I'm not sure there is much we can do about it. At least Intel already
> has hardware with 64-bit registers in config space on the market. I
> imagine there will be others.

Interesting!  Which device, out of interest?

> Having said that, my current case doesn't require it, but I am sure it
> would be nice.

Actually, we're safe, it can't be done.  Refer to PCI Express 2.1,
section 2.2.7 where it mandates that the data part of a config packet
must contain exactly one DWORD.  Maybe they'll change that for PCIe 3,
but there's no need for a major firedrill yet.
Martin Mareš July 6, 2009, 9:38 p.m. UTC | #8
Hello!

> ...but there is another just below. Please forgive me for assuming if
> I am wrong, but were you thinking of the spot a few lines down under
> #else of that same if block, where I also included inttypes.h? If
> that's the case, I have a question. Are we actually supporting
> platforms, other than MS Windows, that don't include the stdint.h and
> inttypes.h headers from C99?

That's what I would like to know, too... :-)

I unfortunately don't have access to some of the more obscure systems
on which libpci runs, including for example AIX. I do not think that
we can rely on any C99 headers there.

Changing <stdint.h> to <inttypes.h> on C99 systems should be OK.

> For the MS Windows case, could we include the headers from the project
> that Rolf mentioned at [1]?

Isn't it easier to copy the few definitions than importing whole files?

				Have a nice fortnight
diff mbox

Patch

diff --git a/lib/access.c b/lib/access.c
index 691df39..b8addea 100644
--- a/lib/access.c
+++ b/lib/access.c
@@ -98,6 +98,14 @@  pci_read_long(struct pci_dev *d, int pos)
   return le32_to_cpu(buf);
 }
 
+u64
+pci_read_quad(struct pci_dev *d, int pos)
+{
+  u64 buf;
+  pci_read_data(d, &buf, pos, 8);
+  return le64_to_cpu(buf);
+}
+
 int
 pci_read_block(struct pci_dev *d, int pos, byte *buf, int len)
 {
@@ -141,6 +149,13 @@  pci_write_long(struct pci_dev *d, int pos, u32 data)
 }
 
 int
+pci_write_quad(struct pci_dev *d, int pos, u64 data)
+{
+  u64 buf = cpu_to_le64(data);
+  return pci_write_data(d, &buf, pos, 8);
+}
+
+int
 pci_write_block(struct pci_dev *d, int pos, byte *buf, int len)
 {
   if (pos < d->cache_len)
diff --git a/lib/pci.h b/lib/pci.h
index 7a5a6b8..71d673f 100644
--- a/lib/pci.h
+++ b/lib/pci.h
@@ -147,11 +147,13 @@  struct pci_dev {
 u8 pci_read_byte(struct pci_dev *, int pos) PCI_ABI; /* Access to configuration space */
 u16 pci_read_word(struct pci_dev *, int pos) PCI_ABI;
 u32 pci_read_long(struct pci_dev *, int pos) PCI_ABI;
+u64 pci_read_quad(struct pci_dev *, int pos) PCI_ABI;
 int pci_read_block(struct pci_dev *, int pos, u8 *buf, int len) PCI_ABI;
 int pci_read_vpd(struct pci_dev *d, int pos, u8 *buf, int len) PCI_ABI;
 int pci_write_byte(struct pci_dev *, int pos, u8 data) PCI_ABI;
 int pci_write_word(struct pci_dev *, int pos, u16 data) PCI_ABI;
 int pci_write_long(struct pci_dev *, int pos, u32 data) PCI_ABI;
+int pci_write_quad(struct pci_dev *, int pos, u64 data) PCI_ABI;
 int pci_write_block(struct pci_dev *, int pos, u8 *buf, int len) PCI_ABI;
 
 int pci_fill_info(struct pci_dev *, int flags) PCI_ABI; /* Fill in device information */
diff --git a/lib/sysdep.h b/lib/sysdep.h
index 2a25c93..4701884 100644
--- a/lib/sysdep.h
+++ b/lib/sysdep.h
@@ -27,8 +27,10 @@  typedef u16 word;
 #include <asm/byteorder.h>
 #define cpu_to_le16 __cpu_to_le16
 #define cpu_to_le32 __cpu_to_le32
+#define cpu_to_le64 __cpu_to_le64
 #define le16_to_cpu __le16_to_cpu
 #define le32_to_cpu __le32_to_cpu
+#define le64_to_cpu __le64_to_cpu
 
 #else
 
@@ -63,8 +65,10 @@  typedef u16 word;
 #if BYTE_ORDER == BIG_ENDIAN
 #define cpu_to_le16 swab16
 #define cpu_to_le32 swab32
+#define cpu_to_le64 swab64
 #define le16_to_cpu swab16
 #define le32_to_cpu swab32
+#define le64_to_cpu swab64
 
 static inline word swab16(word w)
 {
@@ -78,11 +82,25 @@  static inline u32 swab32(u32 w)
          ((w & 0x0000ff00) << 8)  |
          ((w & 0x000000ff) << 24);
 }
+
+static inline u64 swab64(u64 w)
+{
+  return ((w & 0xff00000000000000) >> 56) |
+         ((w & 0x00ff000000000000) >> 40) |
+         ((w & 0x0000ff0000000000) >> 24) |
+         ((w & 0x000000ff00000000) >> 8) |
+         ((w & 0x00000000ff000000) << 8) |
+         ((w & 0x0000000000ff0000) << 24) |
+         ((w & 0x000000000000ff00) << 40) |
+         ((w & 0x00000000000000ff) << 56);
+}
 #else
 #define cpu_to_le16(x) (x)
 #define cpu_to_le32(x) (x)
+#define cpu_to_le64(x) (x)
 #define le16_to_cpu(x) (x)
 #define le32_to_cpu(x) (x)
+#define le64_to_cpu(x) (x)
 #endif
 
 #endif
diff --git a/lib/types.h b/lib/types.h
index 3e0e5c3..818872d 100644
--- a/lib/types.h
+++ b/lib/types.h
@@ -15,39 +15,50 @@ 
 typedef BYTE u8;
 typedef WORD u16;
 typedef DWORD u32;
+typedef unsigned __int64 u64;
+
+#ifdef PCI_HAVE_64BIT_ADDRESS
+#include <limits.h>
+#if ULONG_MAX > 0xffffffff
+#define PRIu64 "l"
+#else
+#define PRIu64 "ll"
+#endif
+#endif
+
 #elif defined(PCI_HAVE_STDINT_H) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
-#include <stdint.h>
+#include <inttypes.h>
 typedef uint8_t u8;
 typedef uint16_t u16;
 typedef uint32_t u32;
+typedef uint64_t u64;
 #else
+#include <inttypes.h>
 typedef u_int8_t u8;
 typedef u_int16_t u16;
 typedef u_int32_t u32;
+typedef u_int64_t u64;
 #endif
 
+#endif	/* PCI_HAVE_Uxx_TYPES */
+
 #ifdef PCI_HAVE_64BIT_ADDRESS
 #include <limits.h>
 #if ULONG_MAX > 0xffffffff
-typedef unsigned long u64;
-#define PCI_U64_FMT "l"
+typedef unsigned long pciaddr_t;
 #else
-typedef unsigned long long u64;
-#define PCI_U64_FMT "ll"
-#endif
+typedef unsigned long long pciaddr_t;
 #endif
 
-#endif	/* PCI_HAVE_Uxx_TYPES */
+#define PCIADDR_T_FMT "%08" PRIu64 "x"
+#define PCIADDR_PORT_FMT "%04" PRIu64 "x"
 
-#ifdef PCI_HAVE_64BIT_ADDRESS
-typedef u64 pciaddr_t;
-#define PCIADDR_T_FMT "%08" PCI_U64_FMT "x"
-#define PCIADDR_PORT_FMT "%04" PCI_U64_FMT "x"
 #else
 typedef u32 pciaddr_t;
 #define PCIADDR_T_FMT "%08x"
 #define PCIADDR_PORT_FMT "%04x"
-#endif
+
+#endif /* PCI_HAVE_64BIT_ADDRESS */
 
 #ifdef PCI_ARCH_SPARC64
 /* On sparc64 Linux the kernel reports remapped port addresses and IRQ numbers */
diff --git a/setpci.c b/setpci.c
index 97740e8..30195b0 100644
--- a/setpci.c
+++ b/setpci.c
@@ -12,6 +12,7 @@ 
 #include <stdarg.h>
 #include <unistd.h>
 #include <errno.h>
+#include <inttypes.h>
 
 #define PCIUTILS_SETPCI
 #include "pciutils.h"
@@ -73,9 +74,10 @@  trace(const char *fmt, ...)
 static void
 exec_op(struct op *op, struct pci_dev *dev)
 {
-  const char * const formats[] = { NULL, " %02x", " %04x", NULL, " %08x" };
-  const char * const mask_formats[] = { NULL, " %02x->(%02x:%02x)->%02x", " %04x->(%04x:%04x)->%04x", NULL, " %08x->(%08x:%08x)->%08x" };
-  unsigned int i, x, y;
+  const char * const formats[] = { NULL, " %02x", " %04x", NULL, " %08x", NULL, NULL, NULL, " %016" PRIu64 "x"};
+  const char * const mask_formats[] = { NULL, " %02x->(%02x:%02x)->%02x", " %04x->(%04x:%04x)->%04x", NULL, " %08x->(%08x:%08x)->%08x", NULL, NULL, NULL, " %016" PRIu64 "x->(%016" PRIu64 "x:%016" PRIu64 "x)->%016" PRIu64 "x"};
+  unsigned int i;
+  u64 x, y;
   int addr = 0;
   int width = op->width;
   char slot[16];
@@ -120,6 +122,9 @@  exec_op(struct op *op, struct pci_dev *dev)
 		case 2:
 		  y = pci_read_word(dev, addr);
 		  break;
+		case 8:
+		  y = pci_read_quad(dev, addr);
+		  break;
 		default:
 		  y = pci_read_long(dev, addr);
 		  break;
@@ -137,6 +142,9 @@  exec_op(struct op *op, struct pci_dev *dev)
 		case 2:
 		  pci_write_word(dev, addr, x);
 		  break;
+		case 8:
+		  pci_write_quad(dev, addr, x);
+		  break;
 		default:
 		  pci_write_long(dev, addr, x);
 		  break;
@@ -157,6 +165,9 @@  exec_op(struct op *op, struct pci_dev *dev)
 	case 2:
 	  x = pci_read_word(dev, addr);
 	  break;
+	case 8:
+	  x = pci_read_quad(dev, addr);
+	  break;
 	default:
 	  x = pci_read_long(dev, addr);
 	  break;
@@ -600,6 +611,8 @@  static void parse_op(char *c, struct pci_dev **selected_devices)
 	  op->width = 2; break;
 	case 'L':
 	  op->width = 4; break;
+	case 'Q':
+	  op->width = 8; break;
 	default:
 	  parse_err("Invalid width \"%c\"", *width);
 	}
diff --git a/setpci.man b/setpci.man
index 6c1ad8c..21ecf35 100644
--- a/setpci.man
+++ b/setpci.man
@@ -149,9 +149,9 @@  Each of the previous formats can be followed by \fB+offset\fP to add an offset
 (a hex number) to the address. This feature can be useful for addressing of registers
 living within a capability, or to modify parts of standard registers.
 \IP \(bu
-Finally, you should append a width specifier \fB.B\fP, \fB.W\fP, or \fB.L\fP to choose
-how many bytes (1, 2, or 4) should be transferred. The width can be omitted if you are
-accessing a named register whose width is well known.
+Finally, you should append a width specifier \fB.B\fP, \fB.W\fP, \fB.L\fP, or \fB.Q\fP
+to choose how many bytes (1, 2, 4, or 8) should be transferred. The width can be
+omitted if you are accessing a named register whose width is well known.
 
 .PP
 All names of registers and width specifiers are case-insensitive.