MOLNAR Ingo writes:
> From: MOLNAR Ingo <[EMAIL PROTECTED]>
> To: Jeffrey Mark Siskind <[EMAIL PROTECTED]>
> cc: [EMAIL PROTECTED], [EMAIL PROTECTED],
> Larry Auton <[EMAIL PROTECTED]>
> Subject: Re: Request for howto
> Date: Sun, 8 Nov 1998 21:24:17 +0100 (CET)
>
>
> On Sat, 7 Nov 1998, Jeffrey Mark Siskind wrote:
>
>> I have a question about __PAGE_OFFSET. I understand that the
>> default setting is 0xC0000000 which means that physical memory is
>> limited to 1G minus epsilon and that per-process address space is
>> limited to 3G minus epsilon. And I have heard that you can change
>> that to 2G minus epsilon physical memory and 2G minus epsilon
>> per-process address space. I presume that the correct setting for
>> that is 0x8000000. Is that true? I also have heard that physical
>> memory
>
> no, take a look at the description in include/asm-i386/page.h.
>
>> plus per-process address space is limited to 4G. Is it trut that
>> one could set __PAGE_OFFSET to any number? Could I set __PAGE_OFFSET
>> to 0x4000000 and access 3G minus epsilon of physical memory while
>> limiting per-process address space to 1G minus epsilon?
>
> 'epsilon' has to be a power of two. (so it can be 3.5+512M, or 3G+1G
> or 2G+2G) Larry Auton has a nice patch that removes this constraint
> without adding any cost.
Thanks for remembering me Ingo. I've been a little slow to reply
because I'm at home recovering from surgery. Take this with a grain
of Percoset, er, salt. Here's an update on the situation as I
understand it. In the file linux/include/asm-i386/io.h at line 104 & 105
you find these macros:
#define __io_virt(x) ((void *)(PAGE_OFFSET | (unsigned long)(x)))
#define __io_phys(x) ((unsigned long)(x) & ~PAGE_OFFSET)
These are the current culprits that break when used with arbitrary values
of PAGE_OFFSET. The more general way to convert between physical and
virtual addresses is to use plus or minus PAGE_OFFSET for the conversion
(why do you think it's called a "offset" ?! :-).
I submitted patches to Linus last May. I see that all of the
address conversion calculations in 2.1.127 have been "fixed"
to use plus or minus except for the two macros in io.h that
still use logical bit manipulation for phys/virt address mapping.
There are two macros defined in linux/include/asm-i386/page.h called
__pa(x) and __va(x) that are IDENTICAL to __io_phys(x) and __io_virt(x)
except that the ones in page.h use plus and minus PAGE_OFFSET.
As I remember it, the crux of the argument for leaving the macros in io.h
alone was that though they only work correctly when PAGE_OFFSET is of
the "right" form, i.e. all binary one's filled from the left, Linus
didn't want to change them because
1) lots of device drivers use __io_phys(x) and __io_virt(x)
and 2) many device driver writers have been sloppy in their use
and are not careful when mapping between virtual and physical
addresses
Since __io_phys(x) and __io_virt(x) still work in the face of that kind
of error it lets the device driver writers get away with being a bit sloppy
at the expense of a more rigid constraint on PAGE_OFFSET.
I suggested (an provided) equivalent functions for the macros __io_phys(x)
and __io_virt(x) that could be used temporarily as a bridge to finding the
problems and fixing the drivers without "breaking" anything. In addtion to
mapping the address paramenter, the functions checked to see whether the
address paramenter was of the proper type. If the caller was misusing the
mapping function the function issued a warning to the kernel log with details
about the caller (source file name and line number).
The function call plus the additional argument checking increased the
overhead somewhat but their use would have identified and eliminated the
badly behaved drivers within a couple of revs of the test kernel; at
which time, the functions could be replaced with macros once again to
eliminate the performance penalty.
I never received any feedback about the patches so I let it drop. I
didn't push the issue because there were so many other real problems
to worry about at that time (2.1.105) and this seemed to be such a
small thing.
If you want to give it a try, this is the smallest patch (vs 2.1.127) I
recommend. This will let you set PAGE_OFFSET to any value you wish and
will report drivers with bad behavior. A quick 'grep "iw_"' in the system
logs will point out the trouble spots. After testing, you could change
the macros in io.h per the comment to be simply __va(x) and __pa(x)
and recompile to eliminate the function call and parameter checking.
Sorry to be so long winded. The arguments seem subtle to me so I felt
it best to include some of the history along with the patch.
-lda
----
diff -u -r v2.1.127/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v2.1.127/arch/i386/mm/init.c Wed Sep 9 11:56:58 1998
+++ linux/arch/i386/mm/init.c Wed Nov 11 20:44:18 1998
@@ -520,3 +520,52 @@
val->sharedram <<= PAGE_SHIFT;
return;
}
+
+unsigned long iw_pa(void *va, char *f, int l)
+{
+ unsigned long pa_old = ((unsigned long ) va) & ~(__PAGE_OFFSET);
+ unsigned long pa_new = ((unsigned long ) va) - (__PAGE_OFFSET);
+ unsigned long ret;
+
+ if(pa_old != pa_new) {
+ printk(KERN_NOTICE "iw_pa(%lx, %s, %d)\n",
+ (unsigned long) va, f, l);
+ }
+
+ /* if the function was called with a physical address instead
+ * of a virtual address, then just return the physical address
+ * without modifying it
+ */
+ if((unsigned long) va < (__PAGE_OFFSET)) {
+ ret = (unsigned long) va;
+ } else {
+ ret = pa_new;
+ }
+
+ return(ret);
+}
+
+void * iw_va(unsigned long pa, char *f, int l)
+{
+ unsigned long va_old = pa | (__PAGE_OFFSET);
+ unsigned long va_new = pa + (__PAGE_OFFSET);
+ unsigned long ret;
+
+ if(va_old != va_new) {
+ printk(KERN_NOTICE "iw_va(%lx, %s, %d)\n",
+ pa, f, l);
+ }
+
+ /* if the function was called with a virtual address instead
+ * of a physical address, then just return the virtual address
+ * without modifying it
+ */
+
+ if((unsigned long) pa >= (__PAGE_OFFSET)) {
+ ret = pa;
+ } else {
+ ret = va_new;
+ }
+
+ return((void *) ret);
+}
diff -u -r v2.1.127/include/asm-i386/io.h linux/include/asm-i386/io.h
--- v2.1.127/include/asm-i386/io.h Sat Nov 7 14:31:11 1998
+++ linux/include/asm-i386/io.h Wed Nov 11 20:53:48 1998
@@ -101,8 +101,12 @@
#include <linux/vmalloc.h>
#include <asm/page.h>
-#define __io_virt(x) ((void *)(PAGE_OFFSET | (unsigned long)(x)))
-#define __io_phys(x) ((unsigned long)(x) & ~PAGE_OFFSET)
+extern unsigned long iw_pa(void *va, char *f, int l);
+extern void *iw_va(unsigned long pa, char *f, int l);
+
+#define __io_virt(x) iw_va((x), __FILE__, __LINE__) /* or just __va(x) */
+#define __io_phys(x) iw_pa((x), __FILE__, __LINE__) /* or just __pa(x) */
+
/*
* Change virtual addresses to physical addresses and vv.
* These are pretty trivial