PAL

Document Sample
PAL Powered By Docstoc
					                                                                                          Page 1 of 11


Some simple PAL code
PAL code is used to implement the parts of the computer architecture that are too complex to
implement in hardware.
In particular, it is used to implement such things as:
•     Entry and exit from architecture level exceptions and interrupts.
•     Implementation of complex instructions.
•     Access to architecturally defined special registers, that may be implemented differently on
      different version of the hardware.
•     Caching of page table entries in registers.
As well as having a notion of user/kernel mode, the Alpha also has a (completely independent)
privileged mode called PAL mode. Basically the processor can be in PAL mode and either user or
kernel mode.
When executing in PAL mode, there are 5 additional instructions that can be executed:
•     Two instructions to move data to or from special registers.
•     Two instructions to load or store data at a physical memory address.
•     An instruction to return from PAL mode back to non-PAL mode.
At the hardware level, there are various special registers.
•     The PAL mode flag, indicating whether in PAL mode or non-PAL mode.
•     The current mode flag, indicating whether in user or kernel mode.
•     The PAL base register, indicating the physical address of the PAL code.
•     The prevPC and prevPAL registers, containing the saved values of the pc and PAL mode flag
      on entry into PAL mode.
•     The page table base register, indicating the address of the page table.
•     Interrupt request and enable flag registers.
•     Instruction and data translation buffer register banks.
•     Registers to control the cache.
•     Registers containing the entry points for kernel exception and interrupt handlers, etc.
•     Registers indicating the cause of the current exception.
•     A bank of temporary registers (in which to save the normal registers, and free up registers for
      use by PAL code). Some of the temporary registers are also used for the above special
      registers.
                                                                                         Page 2 of 11

At the hardware level (below the architecture level), exceptions and interrupt handling is very
simple. They cause entry into PAL mode.
•    The program counter is saved in the prevPC register.
•    The PAL mode flag is saved in the prevPAL register.
•    The pc is set to a physical address computed as the contents of the PAL base register, plus an
     offset that depends on the cause of entry to PAL mode.
     In the simulator, the offsets are held in a table stored at the base of the PAL code, and involve
     a memory access, but on the real machine the offsets are fixed, and defined by the
     implementers of the hardware.
•    The PAL mode register is set to indicate PAL mode.
•    Virtual memory mapping for instruction fetches is disabled.         In other words, the pc is
     interpreted as a physical address.
•    Interrupts are disabled, so execution of PAL code acts like an indivisible operation.
To return from PAL mode, a special instruction is executed. This causes the pc and PAL mode flag
to be restored.
If there is any chance of entry to PAL mode while in PAL mode, it is the responsibility of the writer
of the PAL code to save the values of the prevPC and prevPAL registers somewhere else, then
restore them before return.
Entry to PAL mode can be caused by:
•    A hardware level interrupt.
•    A hardware level exception. For example:
     •     Arithmetic exception, unimplemented instruction, etc.
     •     Memory fault.
     •     Execution of a call_pal instruction, including system call instructions, return from
           system call instruction, swpctx instruction, etc.
     •     Instruction or data translation buffer miss.
Many of these exceptions and interrupts get converted into architecture level exceptions and
interrupts.
This is done by PAL code.
•    The processor changes into kernel mode.
•    The stack pointer is saved in the user stack pointer register, and the kernel stack pointer
     register is copied into the stack pointer register.
•    Various registers are saved on the kernel stack (current mode, interrupt enable flags, pc, gp,
     a0, a1, a2).
•    These registers are set to new values (kernel mode, kernel exception handler, kernel global
     pointer, information on the cause of the exception or interrupt).
The call_pal instruction corresponding to retsys or rti causes the execution of PAL cod that reverses
the effect of this.
                                                                                      Page 3 of 11

ITB and DTB misses also cause entry to PAL code. At the hardware level, the processor has no
understanding of the structure of page tables, and only understands the structure of the ITB and
DTB.
The code for handing ITB/DTB misses has to load the page table entry into the ITB or DTB, then
return back to where it came from. Hence at the architecture level, these exceptions are invisible.

Some examples
Callsys
The callsys instruction is really a call_pal instruction. It causes entry to PAL code. The PAL code
changes the current mode to kernel mode, changes the stack used from user stack to kernel stack,
pushes the previous mode, interrupt enable flags, previous program counter and global pointer onto
the stack, and sets them to appropriate values for entry to the kernel, executing the system call
handler.
//________________________________________________________________
// Call system                                                     //
//________________________________________________________________
block Code_Callsys uses register, specialReg, entryReg {
     code {
     public enter:
          hw_mtpr $t0, sav.t0;             //  Save away $t0
          mov      exc.user, $t0;          //  Set $t0 to user mode flag
          hw_mtpr $zero,     currMode;     //  Set currMode to kernel
                                           //  Swap stacks
          hw_mtpr $sp, usp;                //  usp = $sp
          hw_mfpr $sp, ksp;                //  $sp = ksp
          lda      $sp, -exc.max($sp);     //  Allocate kernel stack space

//________________________________________________________________
//   Code may cause DTB miss PAL                                   //
//________________________________________________________________
          stq      $t0, exc.mode($sp);     //  Save user mode flag on stack

           hw_mfpr     $t0, intEnb;
           stq         $t0, exc.intEnb($sp);        //    Save int enable flags on stack
           hw_mtpr     $zero,    intEnb;            //    Clear int enable flags

           hw_mfpr     $t0, prevPC;
           stq         $t0, exc.pc($sp);            //    Save old pc on stack

           stq         $gp, exc.gp($sp);            //    Save old $gp on stack

//________________________________________________________________
//   Code now safe from DTB miss PAL                               //
//________________________________________________________________

           hw_mfpr     $gp, kgp;                    //    $gp = kgp
                                                    //    Make so “return” to kernel.
           hw_mfpr     $t0, kentry+entSys;
           hw_mtpr     $t0, prevPC;                 //    prevPC = system call entry point

           hw_mfpr     $t0, sav.t0;                 //    Restore $t0
           hw_rei;                                  //    “Return” to kernel.
           }
     }
                                                                                           Page 4 of 11

Retsys
The callsys instruction is really a call_pal instruction. It causes entry to PAL code. It reverses the
effect of the callsys instruction. It restores the registers, deallocates the stack space, swaps back to
using the user stack, etc.
//________________________________________________________________
// Return from system call                                         //
//________________________________________________________________
block Code_Retsys uses register, specialReg {
     code {
     public enter:
//________________________________________________________________
//   Code may cause DTB miss PAL                                   //
//________________________________________________________________
          ldq      $gp, exc.gp($sp);       //  Restore $gp

            ldq         $t0, exc.pc($sp);
            hw_mtpr     $t0, prevPC;                   //    Restore saved pc

            ldq         $t0, exc.intEnb($sp);
            hw_mtpr     $t0, intEnb;                   //    Restore int enable flags

            mov         exc.user, $t0;
            hw_mtpr     $t0, currMode;                 //    Restore curr mode

//________________________________________________________________
//   Code now safe from DTB miss PAL                               //
//________________________________________________________________
          lda      $sp, exc.max($sp);      //  Deallocate kernel stack space
                                           //  Swap stacks
          hw_mtpr $sp, ksp;                //  ksp = $sp
          hw_mfpr $sp, usp;                //  $sp = usp
          hw_rei;                          //  “Return” to user.
          }
     }
                                                                                           Page 5 of 11

Return from trap, fault, interrupt
The rti instruction is similar. However, entry to exceptions and interrupts other than system calls
also save the $a0, $a1, $a2 registers on the stack, so this instruction has to restore these as well. It
also has to cope with the fact that entry might have been from kernel mode or user mode. It only
swaps stacks if returning from kernel mode to user mode.
//________________________________________________________________
// Return from trap or interrupt                                   //
//________________________________________________________________
block Code_Rti uses register, specialReg {
     code {
     public enter:
          hw_mtpr $t0, sav.t0;             //  save $t0

//________________________________________________________________
//   Code may cause DTB miss PAL                                   //
//________________________________________________________________
          ldq      $a2, exc.a2($sp);       //  Restore $a2
          ldq      $a1, exc.a1($sp);       //  Restore $a1
          ldq      $a0, exc.a0($sp);       //  Restore $a0
          ldq      $gp, exc.gp($sp);       //  Restore $gp

            ldq         $t0, exc.pc($sp);
            hw_mtpr     $t0, prevPC;                   //    Restore saved pc

            ldq         $t0, exc.intEnb($sp);
            hw_mtpr     $t0, intEnb;                   //    Restore int enable flags

            ldq         $t0, exc.mode($sp);
            hw_mtpr     $t0, currMode;                 //    Restore curr mode

//________________________________________________________________
//   Code now safe from DTB miss PAL                               //
//________________________________________________________________
          lda      $sp, exc.max($sp);      //  Deallocate kernel stack space
          beq      $t0, retKernel;
     retUser:
                                           //  Swap stacks
          hw_mtpr $sp, ksp;                //  ksp = $sp
          hw_mfpr $sp, usp;                //  $sp = usp
     retKernel:
          hw_mfpr $t0, sav.t0;             //  Restore $t0
          hw_rei;                          //  “Return” to previous mode.
          }
     }
                                                                                       Page 6 of 11

DTB Miss
If the page table entry used by a load or store instruction is not in the Data Translation Buffer, a
DTB miss exception occurs. This saves away various registers, loads the page table entry from
physical memory, checks whether appropriate access will now be possible, restores the saved
registers, then returns.
//________________________________________________________________
// Data Translation Buffer Miss in Non-PAL Mode                    //
//________________________________________________________________
block Code_DTB_Miss_Native uses register, specialReg, entryReg {
     code {
     public enter:

           hw_mtpr     $a0,   sav.a0;                     //    Save   a0
           hw_mtpr     $a1,   sav.a1;                     //    Save   a1
           hw_mtpr     $a2,   sav.a2;                     //    Save   a2
           hw_mfpr     $a0,   intEnb;                     //    Save   intEnb
           hw_mtpr     $a0,   sav.intEnb;
           hw_mfpr     $a0,   prevPC;                     //    Save pc
           hw_mtpr     $a0,   sav.pc;
           hw_mtpr     $ra,   sav.ra;                     //    Save ra

           hw_mfpr     $a0, virtAddr;                     //    Get virtual address
           bsr         GetPTE.enter;                      //    GetPTE( virtAddr )

          hw_mfpr      $a2, dataSum;                      //    Get data summary register
          and          $a2, dataSumFlag.WR;
          beq          $a2, Read;
     Write:
          bsr          CheckWrite.enter;                  //    checkWrite( pte )
          br           end;
     Read:
          bsr          CheckRead.enter;                   //    checkRead( pte )
     end:
          hw_mfpr      $ra,   sav.ra;                     //    Restore ra
          hw_mfpr      $a0,   sav.pc;                     //    Restore pc
          hw_mtpr      $a0,   prevPC;
          hw_mfpr      $a0,   sav.intEnb;                 //    Restore intEnb
          hw_mtpr      $a0,   intEnb;
          hw_mfpr      $a2,   sav.a2;                     //    Restore a2
          hw_mfpr      $a1,   sav.a1;                     //    Restore a1
          hw_mfpr      $a0,   sav.a0;                     //    Restore a0

           hw_rei;
           }
     }
                                                                                        Page 7 of 11

The page table is a multi-level tree. To obtain the page table entry, we have to chain down the
levels, to get to the page table entry at the leaf level. We split the virtual page number into three
fields, and use them to index the different level nodes of the tree.
//________________________________________________________________
// Get page table entry, given virtual address                     //
//   Assumes a0 = virtual address to obtain PTE for.               //
//   Stores PTE in a1.                                             //
//   Uses a2 for temporary memory.                                 //
//________________________________________________________________
block GetPTE uses register, specialReg, entryReg {
     abs {
          high                   =    43;
          level1                      =    33;
          level2                      =    23;
          level3                      =    13;
          left1                       =    64 - high;
          left2                       =    64 - level1;
          left3                       =    64 - level2;
          right                       =    64 - 10;
          pp                          =    32;
          }
     code {
     public enter:
          hw_mfpr $a2, ptbr;                    //   Level 1 page table page
          sll      $a2, 13;                     //   Level 1 page table address

            sll         $a0,   left1,    $a1;
            srl         $a1,   right,    $a1;              //    Level 1 index
            s8addq      $a1,   $a2, $a1;                   //    Level 1 PTE address
            hw_ld       $a1,   ($a1);                      //    Level 1 PTE
            blbc $a1,   end;

            srl         $a1, pp,   $a2;                    //    Level 2 page table page
            sll         $a2, 13;                           //    Level 2 page table address

            sll         $a0,   left2,    $a1;
            srl         $a1,   right,    $a1;              //    Level 2 index
            s8addq      $a1,   $a2, $a1;                   //    Level 2 PTE address
            hw_ld       $a1,   ($a1);                      //    Level 2 PTE
            blbc $a1,   end;

            srl         $a1, pp,   $a2;                    //    Level 3 page table page
            sll         $a2, 13;                           //    Level 3 page table address

            sll         $a0,   left3,    $a1;
            srl         $a1,   right,    $a1;              //    Level 3 index
            s8addq      $a1,   $a2, $a1;                   //    Level 3 PTE address
            hw_ld       $a1,   ($a1);                      //    Level 3 PTE
     end:
            ret;
            }
     }
                                                                                      Page 8 of 11

Rather than just put the page table entry into the DTB, and retry, and see whether we get an access
control violation, the PAL code checks that access will be allowed, by checking the permission bits
in the page table entry.
If access is not allowed, it has to generate an architecture level exception. It puts appropriate
information into the argument registers, and calls code to push information onto the kernel stack.
//________________________________________________________________
// Check Write permissions                                         //
//   Assumes a0 = virtual address.                                 //
//   Assumes a1 = PTE.                                             //
//   If generates an error then sets up a0, a1, a2, prevPC,        //
//        and branches to exception code.                          //
//________________________________________________________________
block CheckWrite uses register, specialReg, entryReg, PTEFlag {
     abs {
          virtAddr =    a0;
          pte           =    a1;
          }
     code {
     public enter:
          hw_mfpr $a2, currMode;
          beq      $a2, kernel;

     user:
          lda          $a2, UWE;
          br           both;
     kernel:
          lda          $a2, KWE;

     both:
          and          $pte,     $a2, $a2;
          beq          $a2, CauseACV;

           and         $pte,     VALID,       $a2;
           beq         $a2, CauseTNV;

           and         $pte,     FOW, $a2;
           bne         $a2, CauseFOW;

           mov         $a0, $a2;
           srl         $a2, 13;
           hw_mtpr     $a2, TLBTag;
           hw_mtpr     $pte,     dtb;
           ret;
                                                                 Page 9 of 11

CauseACV:
     mov       MMFault.ACV,    $a1;    //   New a1 = MMFault.ACV
     br        Error;

CauseTNV:
     mov       MMFault.TNV,    $a1;    //   New a1 = MMFault.TNV
     br        Error;

CauseFOW:
     mov       MMFault.FOW,    $a1;    //   New a1 = MMFault.FOW

Error:
     hw_mfpr   $a2, entMM;             //   New prevPC = entMM
     hw_mtpr   $a2, prevPC;

    hw_mfpr    $ra, sav.ra;            //   Restore ra

    mov        1,       $a2;           //   New a2 = indication caused
                                       //   by write (1)
    br         CauseException.enter;
    }
}
                                                                                        Page 10 of 11

The code for causing an exception is very similar to the code for callsys.
However, it a bit more complex, because it has to push the old values of the argument registers onto
the kernel stack as well. It also has to deal with the fact that the previous mode might have been
either user or kernel mode.
An additional complexity with all PAL code for entry and exit from the kernel is that it attempts to
push information on or pop information off the kernel stack. When doing this, it is using the virtual
memory mapping indicated by the DTB registers.
The trouble is that the page table entry for the kernel stack may not be in the DTB, and so handling
the exception may cause another exception from within PAL code. There is a different exception
handler for DTB misses that occur when in PAL mode.
The main difference is that this DTB miss PAL exception handler has to save registers in different
temporary registers. If it gets an exception when in this exception handler, it probably gives up, and
the machine more or less terminates. Such events could only happen if the kernel stack is
inaccessible - perhaps the kernel stack overflows, or some such thing. You can’t swap the kernel
stack out to disk!
//________________________________________________________________
// Cause Exception                                                 //
//   Assume:                                                       //
//        New values for a0, a1, a2, intEnb, prevPC                //
//             are already in registers                            //
//        Old values for a0, a1, a2, intEnb, prevPC                //
//             are in pal temp registers                           //
//                                                                 //
//________________________________________________________________
block CauseException uses register, specialReg, entryReg {
     code {
     public enter:

            hw_mtpr     $t0, sav.t0;                        //    Save t0

           hw_mfpr      $t0,   prevPAL;
           bne          $t0,   fromPAL;
           hw_mfpr      $t0,   currMode;                    //    obtain old currMode
           beq          $t0,   fromKernel;
      fromUser:
           hw_mtpr      $zero,    currMode;                 //    Swap stacks
           hw_mtpr      $sp, usp;
           hw_mfpr      $sp, ksp;
      fromKernel:
           lda          $sp, -exc.max($sp);                 //    Allocate new stack
                                                                        Page 11 of 11

//________________________________________________________________
//   Code may cause DTB miss PAL                                   //
//________________________________________________________________

         stq       $t0, exc.mode($sp);         //   Save old currMode

         hw_mfpr   $t0, sav.intEnb;            //   Save old intEnb
         stq       $t0, exc.intEnb($sp);

         hw_mfpr   $t0, sav.pc;                //   Save prevPC
         stq       $t0, exc.pc($sp);

         stq       $gp, exc.gp($sp);           //   Save gp
         hw_mfpr   $gp, kgp;

         hw_mfpr   $t0, sav.a0;                //   Save a0
         stq       $t0, exc.a0($sp);

         hw_mfpr   $t0, sav.a1;                //   Save a1
         stq       $t0, exc.a1($sp);

         hw_mfpr   $t0, sav.a2;                //   Save a2
         stq       $t0, exc.a2($sp);

//________________________________________________________________
//   Code now safe from DTB miss PAL                               //
//________________________________________________________________

         hw_mfpr   $t0, sav.t0;                //   Restore t0
         hw_rei;

    fromPAL:                                   //   Should never get here!!
         call_xfc      XFC.STOP;
         br       fromPAL;

         }
    }

Bruce Hutton

				
DOCUMENT INFO
Shared By:
Categories:
Tags:
Stats:
views:8
posted:11/18/2011
language:English
pages:11