Date: Thu, 18 Jul 2002 18:25:37 +1000 From: Peter Grehan <peterg@ptree32.com.au> To: freebsd-ppc@freebsd.org Subject: Long-winded 32-bit syscalls, 64-bit retval explanation Message-ID: <3D367B81.511172C@ptree32.com.au>
index | next in thread | raw e-mail
Given that FreeBSD runs on 32 and 64-bit little-endian, and 64-bit
big-endian systems, I had thought that most endian and word-size related
issues would have been discovered. But, I found a problem that is
peculiar to 32-bit big-endian systems.
There are a number of pseudo-syscalls that actually use __syscall(),
since they have 64-bit parameters. mmap, lseek, ftruncate - basically,
any call that has an off_t parameter.
The issue is that __syscall() is defined as returning a 64-bit value.
Most of the pseudo-syscalls are declared as 32-bit retvals, and either
cast the __syscall return value or implicitly chop it.
e.g. for mmap(),
return((void *)(long)__syscall((quad_t)SYS_mmap, addr, len, prot,
flags, fd, 0, offset));
In the kernel, 32-bit return values are placed into td->td_retval[0].
For 32-bit syscalls on ppc, the value is placed into r3 in the trapframe
and everything works fine. However, for a 64-bit return, the ppc calling
convention uses r3 for the high word, and r4 for the low word. For the
__syscall() clients, this results in the return value coming back with
incorrect
word order, with the 32-bit actual result being discarded by the cast.
Note that on i386, this problem doesn't show up, since the 32-bit value
and the
low word of the 64-bit value are in the same location.
A true 64-bit return, such as lseek(), actually works correctly, since
it's code treats &td->td_retval[0] as a pointer to where the result
should be written, and the word order turns out correct.
*(off_t *)(td->td_retval) = fp->f_offset;
syscall() could fudge the registers, but it doesn't have enough
information to determine if it's calling a 32- or 64-bit retval. I added
the following ugly code which has hard-coded knowledge of __syscall
users:
switch (error) {
case 0:
if ((frame->fixreg[0] == SYS___syscall) &&
(code != SYS_lseek)) {
/*
* 64-bit return, 32-bit syscall. Fixup byte
order
*/
frame->fixreg[FIRSTARG] = 0;
frame->fixreg[FIRSTARG + 1] = td->td_retval[0];
} else {
frame->fixreg[FIRSTARG] = td->td_retval[0];
frame->fixreg[FIRSTARG + 1] = td->td_retval[1];
}
There are two ways to do a cleaner fix for this: either use a constant
to index into the retval array e.g. td->td_retval[LOWORD], or to add a
parameter to syscalls.master to indicate the size of the return value.
Any other opinions ?
Note that for error returns, this isn't an issue, since the libc
.cerror routine can always use r3 to hold the E* value.
later,
Peter.
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-ppc" in the body of the message
home |
help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?3D367B81.511172C>
