root/sys/arch/powerpc/oea/ofw_subr.S
/*      $NetBSD: ofw_subr.S,v 1.21 2026/06/30 23:59:47 rkujawa Exp $    */

/*
 * Copyright (C) 1995, 1996 Wolfgang Solfrank.
 * Copyright (C) 1995, 1996 TooLs GmbH.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by TooLs GmbH.
 * 4. The name of TooLs GmbH may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifdef _KERNEL_OPT
#include "opt_ppcarch.h"
#include "opt_efika.h"
#endif

        /* Stack used to call into OpenFirmware. */
        .lcomm  firmstk,NBPG,16

        /* Buffer used to pass data to/from OpenFirmware. */
        .lcomm  OF_buffer,NBPG + 36,4

        /* The OpenFirmware entry point, provided by OpenFirmware. */
        .lcomm  ofentry,4,4

        /* Entry trampoline used by openfirmware(). */
        .lcomm  oftramp,4,4

        /* Client SR save area */
        .lcomm  clsrsave,64,4

        /* MSR used in OpenFirmware */
        .globl  ofwmsr
        .comm   ofwmsr,4,4

#ifdef FIRMWORKSBUGS
        .lcomm  ofwreal_incharge,4,4
#endif

/*
 * Called by start to save the initial OFW state so we can restore it
 * when call back to OFW.
 *
 * We expect the registers to be as for the entry point into the kernel:
 *
 *      %r1     Stack provided by OpenFirmware / boot loader
 *      %r5     OpenFirmware client entry point
 *
 * (others -- don't care)
 */
ENTRY_NOPROFILE(ofwinit)
        /* Save return address, Push a stack frame. */
        mflr    %r0
        stw     %r0,4(%r1)
        stwu    %r1,-16(%r1)

#ifdef FIRMWORKSBUGS
        mfmsr   %r0
        andi.   %r0,%r0,PSL_IR|PSL_DR
        beq     1f

        li      %r8,1
        lis     %r9,ofwreal_incharge@ha
        stw     %r8,ofwreal_incharge@l(%r9)

        bl      _C_LABEL(ofwr_init)
1:
#endif

        lis     %r8,ofentry@ha
        stw     %r5,ofentry@l(%r8)      /* save client interface handler */

        /*
         * Call directly into OpenFirmware until we're ready to use
         * the trampoline.
         */
        lis     %r8,oftramp@ha
        stw     %r5,oftramp@l(%r8)

        /* Save the MSR that OpenFirmware is using. */
        mfmsr   %r0
        lis     %r9,ofwmsr@ha
        stw     %r0,ofwmsr@l(%r9)

        lis     %r8,OF_buffer@ha
        addi    %r8,%r8,OF_buffer@l
        lis     %r9,_C_LABEL(OF_buf)@ha
        stw     %r8,_C_LABEL(OF_buf)@l(%r9)

        /*
         * Call into C code to perform additional early initialization.
         */
        lis     %r8,_C_LABEL(ofw_bootstrap)@ha
        addi    %r8,%r8,_C_LABEL(ofw_bootstrap)@l
        mtctr   %r8
        bctrl

        /*
         * Jump off the trampoline for all subsequent calls
         * into OpenFirmware.
         */
        lis     %r5,_C_LABEL(openfirmware_trampoline)@ha
        addi    %r5,%r5,_C_LABEL(openfirmware_trampoline)@l
        lis     %r8,oftramp@ha
        stw     %r5,oftramp@l(%r8)

        /* Retrieve LR, pop stack frame. */
        addi    %r1,%r1,16
        lwz     %r0,4(%r1)
        mtlr    %r0

        blr

/*
 * OpenFirmware trampoline.  We are already on the OpenFirmware stack.
 */
ENTRY_NOPROFILE(openfirmware_trampoline)
        mflr    %r0
        stw     %r0,4(%r1)              /* save return address */

        /*
         * Push stack frame and save area:
         *
         * [SP+8 save area]
         * [SP+4 slot for saved LR in callee]
         * [SP+0 saved SP]
         */
        stwu    %r1,-48(%r1)

#ifdef FIRMWORKSBUGS
        lis     %r4,ofwreal_incharge@ha
        lwz     %r4,ofwreal_incharge@l(%r4)
        cmpwi   %r4,1
        bne     1f
        blrl
        b       4f
1:
#endif
        mfmsr   %r4                     /* save msr */
        stw     %r4,8(%r1)

        li      %r0,0                   /* disable MMU */
        mtmsr   %r0
        isync

#if defined (PPC_OEA) || defined (PPC_OEA64_BRIDGE)
#ifdef EFIKA_OFW_WORKAROUNDS
        /*
         * EFIKA 5200B / CodeGen SmartFirmware call workarounds.
         *
         * Save SDR1.
         */
        mfsdr1  %r4
        stw     %r4,12(%r1)
#else
        /* clear BAT translations */
        mtdbatu 2,%r0
        mtdbatu 3,%r0
        mtibatu 2,%r0
        mtibatu 3,%r0
#endif /* EFIKA_OFW_WORKAROUNDS */
#endif /* PPC_OEA */

        lis     %r4,clsrsave@ha         /* save current SRs */
        addi    %r4,%r4,clsrsave@l
        li      %r5,0
1:      mfsrin  %r0,%r5
        stw     %r0,0(%r4)
        addi    %r4,%r4,4
        addis   %r5,%r5,0x10000000@h
        cmpwi   %r5,0
        bne     1b

        lis     %r4,_C_LABEL(ofw_pmap)@ha       /* load OFW SR */
        addi    %r4,%r4,_C_LABEL(ofw_pmap)@l
        lwz     %r0,PM_KERNELSR(%r4)
        cmpwi   %r0,0                   /* pm_sr[KERNEL_SR] == 0? */
        beq     2f                      /* then skip (not initialized yet) */
        li      %r5,0
1:      lwz     %r0,0(%r4)
        mtsrin  %r0,%r5
        addi    %r4,%r4,4
        addis   %r5,%r5,0x10000000@h
        cmpwi   %r5,0
        bne     1b
2:
        /* curcpu()->ci_battable = &ofw_battable */
        GET_CPUINFO(%r4)
        lis     %r5,_C_LABEL(ofw_battable)@ha
        addi    %r5,%r5,_C_LABEL(ofw_battable)@l
        stw     %r5,CI_BATTABLE(%r4)

        lis     %r4,ofentry@ha          /* get firmware entry point */
        lwz     %r4,ofentry@l(%r4)
        mtlr    %r4

        lis     %r4,ofwmsr@ha           /* load Open Firmware MSR */
        lwz     %r5,ofwmsr@l(%r4)
        mtmsr   %r5
        isync

        blrl                            /* call Open Firmware */

        li      %r0,0                   /* ensure disable MMU is disabled */
        mtmsr   %r0
        isync

        /* curcpu()->ci_battable = &battable */
        GET_CPUINFO(%r4)
        lis     %r5,_C_LABEL(battable)@ha
        addi    %r5,%r5,_C_LABEL(battable)@l
        stw     %r5,CI_BATTABLE(%r4)

        lis     %r4,clsrsave@ha         /* restore saved SRs */
        addi    %r4,%r4,clsrsave@l
        li      %r5,0
1:      lwz     %r0,0(%r4)
        mtsrin  %r0,%r5
        addi    %r4,%r4,4
        addis   %r5,%r5,0x10000000@h
        cmpwi   %r5,0
        bne     1b

#if defined (PPC_OEA) || defined (PPC_OEA64_BRIDGE)
#ifdef EFIKA_OFW_WORKAROUNDS
        /*
         * EFIKA 5200B / SmartFirmware: undo the firmware's leftover SDR1 zero
         * and user-valid BATs after the call.
         */
        lwz     %r4,12(%r1)             /* restore kernel SDR1 (see save above) */
        sync
        mtsdr1  %r4
        isync

        mfibatu %r4,0
        clrrwi  %r4,%r4,1               /* clear Vp (BAT_Vu, bit 31) */
        mtibatu 0,%r4
        li      %r4,0
        mtibatu 1,%r4
        mtibatu 2,%r4
        mtibatu 3,%r4
        isync

        mfdbatu %r4,0
        clrrwi  %r4,%r4,1
        mtdbatu 0,%r4
        mfdbatu %r4,1
        clrrwi  %r4,%r4,1
        mtdbatu 1,%r4
        mfdbatu %r4,2
        clrrwi  %r4,%r4,1
        mtdbatu 2,%r4
        mfdbatu %r4,3
        clrrwi  %r4,%r4,1
        mtdbatu 3,%r4
        isync
#endif /* EFIKA_OFW_WORKAROUNDS */
#endif /* PPC_OEA */

        lwz     %r4,8(%r1)              /* restore msr */
        mtmsr   %r4
        isync
4:      
        addi    %r1,%r1,48              /* pop stack frame and save area */
        lwz     %r0,4(%r1)              /* return address */
        mtlr    %r0
        blr

/*
 * Call into OpenFirmware.
 */
ENTRY_NOPROFILE(openfirmware)
        mflr    %r0
        stw     %r0,4(%r1)              /* save return address */

        /* Switch to OpenFirmware stack. */
        lis     %r7,firmstk+NBPG-16@ha
        addi    %r7,%r7,firmstk+NBPG-16@l
        stw     %r1,0(%r7)              /* stash away prev stack pointer */
        mr      %r1,%r7

        lis     %r4,oftramp@ha
        lwz     %r4,oftramp@l(%r4)

        mtctr   %r4
        bctrl

        lwz     %r1,0(%r1)              /* restore previous stack pointer */
        lwz     %r0,4(%r1)              /* return address */
        mtlr    %r0
        blr