I was writing a driver here the other day and discovered a bug which
prevents attempts to ioremap() the top of physical space on i386. I was
able to fix it and I am now able to do things like
ioremap(0xFFF00000,0x100000);
which grabs the top 1MB of physical space. (hint: this is where the bios
flash chip might live on a system with an Intel chipset). These accesses
fail on both 2.2.1 and 2.0.36 currently due to little bugs in __ioremap().
The problem is that __ioremap() does this:
101 if (phys_addr >= 0xA0000 && (phys_addr+size) <= 0x100000)
102 return phys_to_virt(phys_addr);
and this:
120 if (!size || size > phys_addr + size)
121 return NULL;
Problem #1:
You can see that first of all, the first test will (incorrectly) think
I'm within the range 0xA0000 - 0x100000 because phys_addr+size will
wrap to 0. This should be phys_addr - 1 + size.
Problem #2:
The second test will fail because phys_addr+size, again, is 0. This
should also be phys_addr - 1 + size to work properly.
Problem #3: (need help here)
The test on line 120 (checking for a range that will go past the end
of memory) should really be first, as the call
ioremap(0xFFF00000, 0x1B0000)
will be seen not as an illegal request, but as a request for the area
between 0xA0000 and 0x100000. I'm thinking that this test should move
to the beginning of the function. Are there any reasons why this would
be a bad idea? (note I've done it in my patch below)
Thanks...
Eric Seppanen / sepp@primenet.com
PS I'd appreciate a CC if anyone replies.
PPS this patch fixes it and I've been running with it for a few days
now, no problems.
--- linux-2.2.1/arch/i386/mm/ioremap.c Fri Nov 27 17:03:14 1998
+++ linux/arch/i386/mm/ioremap.c Thu Feb 18 17:01:45 1999
@@ -96,9 +96,15 @@
unsigned long offset;
/*
+ * Don't allow mappings that wrap..
+ */
+ if (!size || size > phys_addr-1+size)
+ return NULL;
+
+ /*
* Don't remap the low PCI/ISA area, it's always mapped..
*/
- if (phys_addr >= 0xA0000 && (phys_addr+size) <= 0x100000)
+ if (phys_addr >= 0xA0000 && (phys_addr-1+size) <= 0x100000)
return phys_to_virt(phys_addr);
/*
@@ -113,12 +119,6 @@
offset = phys_addr & ~PAGE_MASK;
phys_addr &= PAGE_MASK;
size = PAGE_ALIGN(size + offset);
-
- /*
- * Don't allow mappings that wrap..
- */
- if (!size || size > phys_addr + size)
- return NULL;
/*
* Ok, go for it..
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/