SPARC traps under SunOS

By: Jim Moore, SunSoft, Sun Microsystems Inc
Email: Jim.Moore@UK.Sun.COM
Version: 1.2
Date: 12 September 1997


Chapter 2

2 WHAT IS A TRAP?

The design of SPARC as a RISC processor means that a lot of the functionality that is normally controlled by complex instructions has to be done by supervisor (kernel) software. Examples of these could be memory exception handling or interrupt handling. When an event occurs that requires special handling in this way, the processor that detected that event will raise a trap so that the supervisor software is notified that it has to handle the event. We'll look at this mechanism in more detail in this section.

2.1 How Traps are Caused

Traps can be generated for a number of reasons and you can see a list of traps under /usr/include/sys/v7/machtrap.h or under /usr/include/sys/v9/machtrap.h for SPARC v7/v8 and v9 respectively. Alternatively, refer to the appendices of this document for listings.

A trap can be caused either by an exception brought about by the execution of an instruction, an explicit trap instruction in running software (see 2.1.4, Software Traps) or triggered by some external interrupt request not directly related to the currently executing instruction.

In order to detect these When the IU (Integer Unit, the part of the CPU that contains the general purpose registers, does integer math and executes the instructions) is about to execute an instruction it first checks to see if there are any exception or interrupt conditions pending and, if so, it selects the highest priority one and causes a trap.

Traps are also used to signal hardware faults and malfunctions, for example a level15 asynchronous memory fault. In some fatal conditions execution cannot continue and the machine will halt or the supervisor software will handle the trap by panicing.

Next, we'll take a generic look at the different trap categories but we'll go into the version specific details later on.

2.1.1 Precise Traps

A precise trap is brought about by an exception directly caused by the executing instruction. This trap occurs before there is any tangible change in the program state of the program that contained the trapped instruction.

2.1.2 Deferred Traps

A deferred trap is similar to a precise trap but in this case the program-visible state may have changed by the time the trap occurs. Such a trap may in theory occur one or more instructions after the trap inducing instruction has executed but it must occur before any subsequent instruction attempts to use any modified register or resource that the trap inducing instruction used. For example, imagine that a floating point operation is being executed. This does not happen synchronously with IU instructions and so it is possible that a floating point exception could occur as a deferred trap.

2.1.3 Disrupt/Interrupt Traps

An interrupt trap, as you have probably guessed, is basically the assertion of an interrupt, either generated externally (from hardware) or internally (via software). The delivery of interrupts is controlled by the PIL (Processor Interrupt Level) field of the PSR (Processor State Register), which specifies the minimum interrupt level to allow, and also by the mask of asserted bits in the IE (Interrupt Enable register...architecture specific).
Under SPARC v9, we have a concept of a disrupt trap. This is very similar to a deferred trap in that it could be related to an earlier instruction but in this case the trap is an unrecoverable error.

2.1.4 Software Traps

Software traps are precise traps but I've decided to give a little more information about them here.

A software trap is basically what happens when we execute a trap instruction. The first half of the trap table is reserved for machine generated traps and so software traps start from 0x80 for SPARC v7/v8 or 0x100 for SPARC v9. When writing code that uses software traps, you simply use trap values from 0x0 to 0x7f for v7/v8 or from 0x0 to 0xff for v9. The specified trap value will automatically used as an offset from the start of the software traps. For example, software trap 0x8 is used to initiate a system call under SunOS 5.x and this maps to "real" trap 0x88 on SPARC v7/v8 or trap 0x108 on SPARC v9. If you attempt to use a trap value in a userland process that would fall off the end of the trap table when added to this software trap offset, an illegal instruction trap will occur. This will result in SIGILL being sent to your process.

2.2 How Traps are Dispatched to the Kernel

In this section we will look at the flow of execution into the kernel when a trap occurs. This is different for SPARC v7/v8 and v9 so we will split this section into two.

2.2.1 SPARC v7/v8

When a trap occurs, the flow of execution jumps to an address which is calculated from the Trap Base Register (TBR) and the Trap Type (TT). The sequence is as follows:

  1. An exception/interrupt has been detected as pending by the IU.
  2. The IU multiplies the TT by 16 (TT << 0x4) as there are 4 instructions per trap table entry.
  3. The IU loads the address of the trap table (from the TBR) and adds the offset calculated in (2).
  4. The CWP (Current Window Pointer) is decremented, so that we are in a new register window. This happens for EVERY trap that occurs (refer to section 3.2 on Register Windows for more information).
  5. The trapped instruction (%pc) and the next instruction to be executed (%npc) are written into local registers %l1 and %l2
  6. Traps are disabled and the current processor mode is set to "supervisor". This is done by setting the ET bit to zero and the supervisor mode bit to one in the PSR (refer to the PSR description in /usr/include/v7/sys/psr.h).
  7. Execution resumes at [TBR + (TT<<4)], as calculated in (3)
Part of the SunOS kernel code is a trap table, which contains 255 4-instruction entries, each entry corresponding to a trap type from 0 to 0xff. This structure is defined by the SPARC implementation.
Each trap table entry basically contains a branch to a trap handling routine and may also load the PSR into a local register for use later. Here's an example of a trap table entry:
                sethi   %hi(trap_handler), %l3          ! Load trap handler
                jmp     [%l3 + %lo(trap_handler)]       ! address and jump
                mov     %psr, %l0                       ! Delay: load %psr
                nop
It is the address of the trap table that is written into the TBR when the kernel is ready to handle traps. The kernel contains a table of trap vectors like the above example and it writes the address of this trap table into the TBR in it's startup code. You can look at the trap table on a running SunOS system by running adb against the kernel and looking at the instructions from "scb" onwards:
	# adb -k
	physmem        1dec
	scb,8/ia
	_start:         rd      %psr, %l0
	scb+4:          ld      [%l0], %f27
	scb+8:          jmp     %l3 + 0x280
	scb+0xc:        clr     %l4
	scb+0x10:       rd      %psr, %l0
	scb+0x14:       sethi   %hi(0xf0041000), %l3
	scb+0x18:       jmp     %l3 + 0x280
	scb+0x1c:       mov     0x201, %l4
Note that the trap table should be aligned on a 4096 byte boundary.

2.2.2 SPARC v9

The SPARC v9 case is quite different from the v7/v8 case mainly due to the concept of processor states and trap nesting.

2.2.2.1 Trap Nesting

With the UltraSPARC architecture there is the concept of trap nesting. This defines a set of trap levels and a trap stack which the machine can use for more efficient trap processing. At the time of writing, there are five trap levels defined in the UltraSPARC architecture although the number of trap levels above four is implementation dependent.

The trap level register (TL) contains the current trap level of the processor. A processor normally executes at trap level zero (execute state). In SPARC v7/v8, the CPU enters trap state and system (supervisor) software (in other words, the kernel) has to save enough information about the processor state so that it can return from the trap successfully and also to prevent another error condition occurring whilst handling the trap. If an error occurs that would cause a trap when traps are already disabled (ie. we are in trap state), the v7/v8 CPU will watchdog reset. Upon completion of handling the trap, we have to restore the processor state before returning to the offending instruction (or terminating the process) and this time consuming operation is necessary because SPARC v7/v8 doesn't support trap nesting.

SPARC v9 handles things more efficiently. When a trap occurs, the CPU simply moves into the next trap level up (ie. TL is incremented). The most important processor states (PC, NPC and PSTATE) are saved on the trap stack. There is one set of trap state registers for each trap level and so entering a higher trap level is very fast and efficient. Here is an example of how we use trap levels to improve OS performance:

	LEVEL 0: Normal program execution
	LEVEL 1: System calls, interrupt handlers
	LEVEL 2: Exceptions in OS routines
	LEVEL 3: Page fault handling
	LEVEL 4: RED state (see below)

We still use a trap table concept under v9 but the destination address for the transfer of execution is calculated differently. Also, trap table entries for v9 are 8 instructions in size, except for spill/fill traps, in which case the entries are 32 instructions in size. Also, in a special state called the RED state (more on that later) we could use a different trap table!

The trap table is divided into three parts. The first half of the table is used for machine generated traps. The next quarter is reserved for software initiated traps and the final quarter is reserved for future use. The displacement into the trap table is defined by the trap level (TL) and the Trap Type (TT) together.

Let's take a look at this in some more detail. I strongly advise that you obtain a copy of the version 9 SPARC architecture manual if you want to follow this in detail.

When a trap occurs, the action taken depends on the TT, the current level of trap nesting (contained in the TL) and the processor state at that time. Let's look at processor states and what we mean by normal and special traps so that the rest of this section has more chance of making sense!

2.2.2.2 Processor States, Normal and Special Traps

The SPARC v9 processor is always in one of three states and these are:

  1. Execute state. This is the normal execution state.
  2. RED state. RED = Reset, Error and Debug. This is a state that is reserved for handling traps when we are at the penultimate level of trap nesting (TL = MAXTL - 1).
  3. Error state. This is a state that is entered when we have a trap occur at a point in time when we are at our maximum level of trap nesting (TL = MAXTL) or an unrecoverable fatal error has occurred.
Normal traps are traps that are processed when we are in the nice cosy execute state. If we trap in RED state, then this is a special trap. There is an implementation dependent address called the RED State Trap Vector Address (RSTVaddr) which contains the vector to the RED state trap table. This vector could be set to overlay the same one in the TBR. Under SunOS, the RSTVaddr table is at the same address as the normal trap table, so that the six RED state traps below overlay the first six entries in the trap table. The definition of those six RED state traps are:
        TT      Vector          Reason

        0       RSTVaddr|0x0    SPARC v8 style reset
        1       RSTVaddr|0x20   Power On Reset (POR)
        2       RSTVaddr|0x40   Watchdog Reset (WDR)
        3       RSTVaddr|0x60   Externally Initiated Reset (XIR)
        4       RSTVaddr|0x80   Software Initiated Reset (SIR)
        Others  RSTVaddr|0xa0   All other traps in RED state
A fatal exception that causes us to drop into error state will cause the processor to note the exception and either halt, reset or watchdog reset. After the reset, the processor enters RED state with a TL appropriate to the type of reset (usually maximum). Also, the TT is set to the value of the original trap that caused the reset and not the TT value for the reset itself (ie. WDR - Watchdog reset or XIR - Externally Indicated Reset). When we reboot, if we detect that this RED state condition exists, we can interrogate the ECC (Error Check and Control) registers
Now that we have a concept of the different traps and processor states, let's look at the sequence of execution when a trap occurs to deliver the trap to the supervisor (kernel).

2.2.2.3 Normal Trap (Processor in Execute State)

  1. If TL = MAXTL-1, the processor enters RED state (Goto 2.2.2.3).
  2. TL = TL + 1
  3. Processor state, %pc, %npc, CWP, CCR (Condition Code Register), TT and ASI (Address Space Identifier register) are saved.
  4. The PSTATE (Processor State) register is updated as follows:
    1. RED field set to zero
    2. AM (Address Masking) disabled
    3. PRIV (Privileged Mode) enabled
    4. IE cleared, disabling interrupts
    5. AG set (Alternate Global Registers enabled)
    6. Endian mode set for traps (TLE)
    Refer to the architecture manual for a description of PSTATE
  5. If TT is a register window trap, CWP is set to point to the register window to be accessed by the trap handler code.
    Possibilities are:
    1. TT = 0x24 (Clean Window), CWP = CWP + 1
    2. TT <= 0x80 AND TT <= 0xbf (Window Spill),
      CWP = CWP + CANSAVE + 2. CANSAVE is a register that contains the number of register windows following the CWP that are NOT in use.
    3. TT <= 0xc0 AND TT <= 0xff (Window fill),
      CWP = CWP - 1
    Unlike SPARC v7/v7, non-register window traps do NOT result in a change in the CWP, as the nested trap concept provides some hardware support to preserve the processor state.
  6. Control is transferred to the trap table at an address calculated as follows:
                    New %pc =  TBA | (TL>0 ? 1: 0) | TL
                    New %npc = TBA | (TL>0 ? 1: 0) | TL | 0x4
    
    Remember, TBA = Traptable Base Address, similar to the TBR in v8 Execution then resumes at the new %pc and %npc

2.2.2.4 Special Trap (Processor in RED State)

  1. TL = MAXTL
  2. The existing state is preserved as in 2.2.2.2, step 3.
  3. The PSTATE is modified as per 2.2.2.2, step 4 except that the RED field is asserted.
  4. If TT is a register window trap, CWP processing occurs as in 2.2.2.2, step 5.
  5. Implementation specific state changed may occur. For example, the MMU may be disabled.
  6. Control is transferred to the RED state trap table subject to the trap type. Look back to 2.2.2.1 for the RSTVaddr information to see how this vector is made.

This may seem rather complicated but once you have the picture built clearly it will all fall into place. Post or email if you need clarification.


Go to chapter 3 Back to table of contents