Resending as previous attempt complains attachments are too big.

All of the following applies to the stock linux 2.6 kernel from a fresh
installation of Fedora 10.

I have been looking into the int10 hang when initializing the BIOS of a secondary card. This is what I found: - The function responsible for reading the ROM of the PCI video card is pci_device_linux_sysfs_read_rom() for the Fedora 10 case. - This function pci_device_linux_sysfs_read_rom() is *not* exercised at all when using the primary display, even when an option such as UseBIOS is in effect. So this function might as well be broken and nobody with a single display would notice. - pci_device_linux_sysfs_read_rom() is exercised when initializing a secondary display (using "vesa" in my case)

I introduced a bit of logging in the patch libpciaccess-partial-fix-with-debug.patch that outputs messages to a file in /tmp . The basic problem is that, despite all the sysfs dance to enable the ROM, the kernel terminates the read with 0 bytes when trying to read the ROM:

Reading ROM from /sys/bus/pci/devices/0000:00:09.0/rom into address 0xb7f4a008
ROM size for /sys/bus/pci/devices/0000:00:09.0/rom is 32768 using 32768
Reading ROM from /sys/bus/pci/devices/0000:00:09.0/rom reached 0-sized read (EOF?) at offset 0
Dump of ROM from /sys/bus/pci/devices/0000:00:09.0/rom (0 bytes):
Reading ROM failed with short read, using /dev/mem to read from 0xdffe0000

I introduced an attempt at a fallback that calls pci_device_linux_devmem_read_rom() when the total amount read is less than the expected ROM size. In current git for libpciaccess, the buffer remains uninitialized and hangs the machine. I hoped that the fallback would be enough to read the ROM and fix this problem. However, I ran into another problem. The attempted fallback ends up using pread() on /dev/mem at the offset matching the one reported for the ROM. However, this failed with EINVAL (Invalid argument). By using strace on the stock X server and the modified libpciaccess library, I saw that the pread implementation calls into pread64() with an very big offset of 18446744073172549632 (0xffffffffdffe0000), which is the required offset, sign-extended into 64 bits instead of zero-extended as required. This might point to a bug in glibc headers or code, but I worked around this by replacing the call with a pread64() call, as seen in libpciaccess-partial-fix-without-debug.patch

Now, here comes the third problem: the passed address makes pread64() return EFAULT (Invalid address). I did not have time to find out whether this address is intended or not. However libpciaccess-partial-fix-without-debug.patch is enough to replace the hang with a graceful exit that allows the user to sort-of regain control of the machine. Final strace is attached, search for EFAULT in the text.

Please comment on this. Details already posted at bug #18160

--
perl -e '$x=2.4;print sprintf("%.0f + %.0f = %.0f\n",$x,$x,$x+$x);'

Reading ROM from /sys/bus/pci/devices/0000:00:09.0/rom into address 0xb7f4a008
ROM size for /sys/bus/pci/devices/0000:00:09.0/rom is 32768 using 32768
Reading ROM from /sys/bus/pci/devices/0000:00:09.0/rom reached 0-sized read (EOF?) at offset 0
Dump of ROM from /sys/bus/pci/devices/0000:00:09.0/rom (0 bytes):
Reading ROM failed with short read, using /dev/mem to read from 0xdffe0000
diff -ur ../xorg-build/lib/libpciaccess/src/linux_devmem.c libpciaccess/src/linux_devmem.c
--- ../xorg-build/lib/libpciaccess/src/linux_devmem.c	2008-11-25 06:42:15.000000000 -0500
+++ libpciaccess/src/linux_devmem.c	2008-12-05 00:34:34.000000000 -0500
@@ -125,7 +125,7 @@
 	size_t bytes;
 
 	for (bytes = 0; bytes < rom_size; /* empty */) {
-	    const ssize_t got = pread(fd, buffer, rom_size - bytes, 
+	    const ssize_t got = pread64(fd, buffer, rom_size - bytes, 
 				      rom_base + bytes);
 	    if (got == -1) {
 		err = errno;
diff -ur ../xorg-build/lib/libpciaccess/src/linux_sysfs.c libpciaccess/src/linux_sysfs.c
--- ../xorg-build/lib/libpciaccess/src/linux_sysfs.c	2008-11-25 06:42:15.000000000 -0500
+++ libpciaccess/src/linux_sysfs.c	2008-12-05 00:42:08.000000000 -0500
@@ -301,6 +301,7 @@
     return err;
 }
 
+#define DEBUG_READROM 1
 
 static int
 pci_device_linux_sysfs_read_rom( struct pci_device * dev, void * buffer )
@@ -312,6 +313,13 @@
     size_t rom_size;
     size_t total_bytes;
 
+#if defined DEBUG_READROM
+    FILE * dbglog = fopen("/tmp/debug-read-rom.log", "a"); 
+    setvbuf(dbglog, NULL, _IONBF, 0);
+#define COND_CLOSE() if (dbglog != NULL) fclose(dbglog);
+#else
+#define COND_CLOSE()
+#endif
 
     snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/rom",
 	      SYS_BUS_PCI,
@@ -319,18 +327,28 @@
 	      dev->bus,
 	      dev->dev,
 	      dev->func );
-    
+#if defined DEBUG_READROM
+fprintf(dbglog, "Reading ROM from %s into address %p\n", name, buffer);
+#endif    
     fd = open( name, O_RDWR );
     if ( fd == -1 ) {
 	/* If reading the ROM using sysfs fails, fall back to the old
 	 * /dev/mem based interface.
 	 */
+#if defined DEBUG_READROM
+fprintf(dbglog, "Reading ROM failed, using /dev/mem\n");
+#endif    
+COND_CLOSE();
 	return pci_device_linux_devmem_read_rom(dev, buffer);
     }
 
 
     if ( fstat( fd, & st ) == -1 ) {
 	close( fd );
+#if defined DEBUG_READROM
+fprintf(dbglog, "fstat(%s) failed with errno %d\n", name, errno);
+#endif    
+COND_CLOSE();
 	return errno;
     }
 
@@ -338,6 +356,11 @@
     if ( rom_size == 0 )
 	rom_size = 0x10000;
 
+#if defined DEBUG_READROM
+fprintf(dbglog, "ROM size for %s is %ld using %u\n", name, st.st_size, rom_size);
+#endif    
+
+
     /* This is a quirky thing on Linux.  Even though the ROM and the file
      * for the ROM in sysfs are read-only, the string "1" must be written to
      * the file to enable the ROM.  After the data has been read, "0" must be
@@ -351,9 +374,15 @@
 				rom_size - total_bytes );
 	if ( bytes == -1 ) {
 	    err = errno;
+#if defined DEBUG_READROM
+fprintf(dbglog, "Reading ROM from %s failed at offset %u with errno %d\n", name, total_bytes, errno);
+#endif    
 	    break;
 	}
 	else if ( bytes == 0 ) {
+#if defined DEBUG_READROM
+fprintf(dbglog, "Reading ROM from %s reached 0-sized read (EOF?) at offset %u\n", name, total_bytes);
+#endif    
 	    break;
 	}
 
@@ -365,6 +394,27 @@
     write( fd, "0", 1 );
 
     close( fd );
+#if defined DEBUG_READROM
+	{
+		unsigned int a, b, c;
+		fprintf(dbglog, "Dump of ROM from %s (%u bytes):\n", name, total_bytes);
+		for (a = 0; a < total_bytes; a += 16) {
+			b = total_bytes - a;
+			if (b > 16) b = 16;
+			for (c = 0; c < b; c++) fprintf(dbglog, "%02x ", ((unsigned char *)buffer)[a + c]);
+			fprintf(dbglog, "\n");
+		}
+	}
+#endif    
+    if (total_bytes < rom_size) {
+#if defined DEBUG_READROM
+struct pci_device_private *priv = (struct pci_device_private *) dev;
+fprintf(dbglog, "Reading ROM failed with short read, using /dev/mem to read from 0x%Lx\n", priv->rom_base);
+#endif
+        /* Failed to read entire ROM, falling back to /dev/mem  */
+        err = pci_device_linux_devmem_read_rom(dev, buffer);
+    }
+COND_CLOSE();
     return err;
 }
 
diff -ur ../xorg-build/lib/libpciaccess/src/linux_devmem.c libpciaccess/src/linux_devmem.c
--- ../xorg-build/lib/libpciaccess/src/linux_devmem.c	2008-11-25 06:42:15.000000000 -0500
+++ libpciaccess/src/linux_devmem.c	2008-12-05 00:34:34.000000000 -0500
@@ -125,7 +125,7 @@
 	size_t bytes;
 
 	for (bytes = 0; bytes < rom_size; /* empty */) {
-	    const ssize_t got = pread(fd, buffer, rom_size - bytes, 
+	    const ssize_t got = pread64(fd, buffer, rom_size - bytes, 
 				      rom_base + bytes);
 	    if (got == -1) {
 		err = errno;
diff -ur ../xorg-build/lib/libpciaccess/src/linux_sysfs.c libpciaccess/src/linux_sysfs.c
--- ../xorg-build/lib/libpciaccess/src/linux_sysfs.c	2008-11-25 06:42:15.000000000 -0500
+++ libpciaccess/src/linux_sysfs.c	2008-12-05 00:51:38.000000000 -0500
@@ -365,6 +365,10 @@
     write( fd, "0", 1 );
 
     close( fd );
+    if (total_bytes < rom_size) {
+        /* Failed to read entire ROM, falling back to /dev/mem  */
+        err = pci_device_linux_devmem_read_rom(dev, buffer);
+    }
     return err;
 }
 

Attachment: salida-strace-x.txt.bz2
Description: BZip2 compressed data

_______________________________________________
xorg mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/xorg

Reply via email to