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>