Darwin ABI and its use of stw r0, 8(%r1) using callers %r1 value vs. ofwcall's code

Mark Millard markmi at dsl-only.net
Mon Feb 23 11:06:07 UTC 2015


The compiled BootX code for calling openfirmawre for its _Peer use (as one of many examples) does %r1 related code that looks like the following. This code is here just for illustrating the Darwin ABI usage as it suggests what APple's openfirmware might be expected (or at be least allowed) to do itself when it is called, much liek when _Peer is called.

_Peer:
mfspr   r0,lr
... /* no use of %r1 until... */
stw     r0,0x8(r1) /* Note the 8(%r1) usage on the so far unchanged %r1 */
stwu    r1,-0x90(r1) /* Here %r1 is finally changed */
... /* no %r1 changes */
addi    r3,r1,0x38
... /* no %r1 changes */
mtspr   ctr,r9
or      r12,r9,r9 /* %r9 referencing the openfirmware address to jump to */
bctrl
... /* Just for completness, not essential to my point... */
lwz     r0,0x48(r1) /* the success case's return value extraction */
addi    r1,r1,0x90
or      r3,r0,r0
lwz     r0,0x8(r1)
mtspr   lr,r0
blr

The position 8(%r1) in the stw is based on the caller's %r1 value in this standard Darwin ABI calling sequence.


The following code from ofwcall64.S (-r277335 but the usage is not new) has used that same 8(%r1) position relative to the final value that it gives %r1 just before calling into openfirmware:


        /* Get OF stack pointer */
        ld      %r7,TOC_REF(ofwstk)(%r2)
        addi    %r7,%r7,OFWSTKSZ-32
...
        mr      %r1,%r7
        std     %r5,8(%r1)      /* Save real stack pointer */
... /* no %r1 changes */
        /* Finally, branch to OF */
        mtctr   %r4
        bctrl

At this point OF would be allowed to store the lr to 8(%r1) before it changes or records %r1 if it followed just the Darwin ABI, just like the code above does. That would replace the "std %r5,8(%r1)" value that was assigned in ofwcall64.S.

This may partially explain the corrupted %r1 that I've sometimes observed when openfirmware returns. (But see later notes.)

The only reason that this works as well as it does is that (G5 Quad-Core PowerMac) openfirmware protects itself before potentially executing any normal Darwin ABI code, including special code that in part records %r1 as it came from the caller (x/i notation):

    or      r2,r0,r2,
    addis   r2,r0,-x49
    ori     r2,r2,0xf000 /* so %r2:=0xFFB7F000 */
    std     r1,r2,0x8,   /* %r1 saved to have a special, separate copy */
    std     r0,r2,0x10,
    mfspr   r0,lr
    std     r0,r2,0x120,
    mfmsr   r1
    std     r1,r2,0x108,

(Side comment: It would not be good to call the entry point recursively in any form.)

Openfirmware also carefully saves the 64-bit lr value and the 64-bit msr. My guess is that it has its own transition between potential 64 bit code and the 32 bit environment going in and for returning (usually?). It may even use the msr value to change behavior for being called form 32-bit vs. 64-bit contexts to some degree.

Unfortunately I can not tell (yet?) if openfirmware always restores/uses what it saved for returning or not. It may be that it sometimes does and sometimes does not. So far I've identified only the save side of things.

Should the PowerMac code have this Darwin ABI conflict and depend on Apple's openfirmware having an extra non-ABI layer that translates back and forth from/to 64-bit environments? If it is to depend on such, then why appear to have a full transition to a 32 bit context before calling into openfirmware? (There easily could be evidence that Im not familiar with.)

It is even possible that openfirmware is detecting that it was not called from a 64 bit context and so is doing things that are inappropriate in response.



A different point for the relocatable powerpc64 kernel is that openfirmware might not tolerate the instruction after

        /* Finally, branch to OF */
        mtctr   %r4
        bctrl

being outside the 32 bit addressing range unless openfirmware always has a clean transition back to 64-bit calling contexts.

This could be a reason to either have a 64-bit calling context if it is supported or to disallow such a large relocation on PowerMac G5's.


Yet a different point is the BootX code use of %r12 (which follows the Darwin ABI):

mtspr   ctr,r9
or      r12,r9,r9 /* %r9 referencing the openfirmware address to jump to */
bctrl

Every indirect call to openfirmware sets %r12 to the called address even when it is not necessary to the local jump code to the called routine and so only the called code would potentially use the %r12 value.

The wording I have for the description of Darwin ABI's rule is consistent with the caller possibly caring for some reason:

"... wherein a routine that branches indirectly to another routine must store the target of the call in GPR12. No special purpose for a routine that has been called directly." (Mac OS X Internals, Amit Singh). The code generated for BootX certainly is following that %r12-use rule when it calls openfirmware.

So for PowerMac's it may be that %r12 should be used instead of %r4 in teh following code.


        /* read client interface handler */
        ld      %r4,TOC_REF(openfirmware_entry)(%r2)
        ld      %r4,0(%r4)
...
        /* Finally, branch to OF */
        mtctr   %r4
        bctrl


===
Mark Millard
markmi at dsl-only.net



More information about the freebsd-ppc mailing list