Ralf Baechle writes:
> In firm assumption that due all the practical problems involved with
> a non-standard execution model (i.e. 32-bit, o32-style ELF, 32/32
> register model and 32-bit gprs) I decieded that in practice nobody will
> use this and dumped all the support for it from later 2.3 kernels. That
> is the scheduler will no longer try to handle context switching for
> the 32/32 fpr model correctly etc.
> If that's desired, how about providing a syscall which allows to manipulate
> this and possibly other bits?
> Btw... Thanks for posting. You pointed my nose at the fact that this bug
> actually exists for the 64-bit kernel - and there is actually a real world
> bug because we can mix 32-bit and 64-bit binaries.
It also applies to -n32 (FR=1 32-bit binaries). Basically, a complete
kernel implementation has to treat the two cases which different from its own
as "compatibility mode" targets. That is, if the kernel is "-32" ("O32"),
it has to turn on FR=1 for those processes currently executing a "-n32" binary
and off for those executing a "-32" binary. That is, exec has to change
the desired FR value, and context switching to and from user mode has to
save and restore it.
One other issue is that UX should always be set, to allow use of
MIPS3 instructions, and that XX (bit 31) should be set on R5000 and
R10000 processors, to enable MIPS4 instructions. This in turn means
that, to avoid various illegal address exceptions, the VM system
should not allow a 32-bit user program to map anything into the top 32
KB of the user address space.
The problem has to do with some compilers using integer
arithmetic to compute a base for some variables in the current stack
frame, and then using negative displacements to address the variables,
for cases where the stack frame exceeds 32 KB, but is located near the
top of memory. The 32-bit unsigned integer add to, say, 0x7fffff00
(64-bit address 0x000000007fffff00) produces a signed 32-bit value
such as 0x80000f00, which is the 64-bit value 0xffffffff80000f00,
since all 32-bit values, signed or unsigned, are stored as 32-bit
signed values sign-extended to 64 bits. When you do a load with a
negative offset of, say, -0x1000, you get an address
0xffffffff7fffff00, not 0x000000007fffff00. With UX=0, this would be
fine, but, with UX=1 (to enable MIPS3 instructions), the above address
is illegal. If the $sp is always at least 32 KB below the top of the
address space, this problem does not arise, since any such intermediate
pointer generated by the compiler will always be below 0x80000000.