Skip site navigation (1)Skip section navigation (2)
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>

next in thread | raw e-mail | index | archive | help
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




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?3D367B81.511172C>