| |||
| Home > VFP Support Code > Processing a bounced VFP instruction | |||
The top-level Undefined Instruction handler TLUndef_Handler() saves
the processor state, calls an appropriate second-level handler,
and then restores the saved state. There are separate second-level
handlers for each coprocessor and for each type of undefined instruction.
This section describes example top-level handler code provided
as TLUndef_Handler() in tlundef.s.
After doing some initial state saving, TLUndef_Handler() first
of all checks whether the bounced instruction was a coprocessor
instruction or not.
If the instruction that bounced was not a coprocessor instruction
then one of Undef_ARM_Handler(), Undef_Thumb_Handler(),
or Undef_Java_Handler() is called, depending
on the type of instruction that bounced. In the supplied code these
are simply dummy routines that go in to an infinite loop.
It then checks which coprocessor the instruction belongs to.
The coprocessor number is then used in a table lookup to find the
address of a suitable second-level handler. In the case of VFP the
number will be decimal 10 or 11 and the appropriate function is VFP_Undef_Handler().
By default all other coproc numbers are vectored on to Unknown_Coproc_Handler().
On return from the second-level handler the saved state is restored, and a user-level exception handler may be called. This is described in Section 7.3.3.
When the ARM core signals an Undefined Instruction exception it copies the current execution state to the R14_undef and SPSR_undef registers, disables IRQ interrupts, and starts executing the code at the UNDEF vector in ARM state.
The UNDEF vector contains a jump to the top-level handler code.
The top-level handler immediately re-enables IRQ interrupts.
As a consequence interrupt handlers that wishes to use VFP must save the contents of R14_und and SPSR_und to avoid corrupting return information stored there, and possibly also save the contents of the Undef stack if it is small.
This code effectively relies on the banked register state obeying the following hierarchy:
At any point where an exception corresponding to banked registers can occur the registers must not contain live values. Thus Undef exceptions must not be allowed when in Fiq, Abort, Irq or Undef mode.
After enabling interrupts the top-level handler switches to Supervisor mode. It constructs a complete dump of the application's register state on the Supervisor stack, and copies the return information out of R14_und and SPSR_und, switching modes where necessary.
If the exception occurred in Supervisor mode the R13_svc register is modified to allocated stack space for the register dump, before it is saved in to the register dump. In this case second-level handlers may not read or modify the saved value of R13. As a consequence if VFP instructions are used by Supervisor mode code, then this handler is not suitable for building a software implementation of the VFP architecture, where instructions that copy data to and from the VFP are emulated.
The top-level handler makes temporary use of two words of Undef stack, but mostly runs in Supervisor mode with the Undef stack empty. This reduces the need for a true Undef stack, and so reduces the total amount of stack space required by the system.
The top-level handler should be modified on systems that do not use the Supervisor stack for handling Undef exceptions.
The saved PSR is checked to find out if the processor was
in ARM or Thumb state, and the instruction that caused the exception
is loaded. If Thumb, Unknown_Thumb_Handler() is
called. If ARM, it is checked for a coprocessor instruction encoding,
otherwise Unknown_ARM_Handler() is called.
A function Unknown_Java_Handler() is
provided for exceptions in Java state (on processors conforming
to ARMv5TEJ and above). However, the processor never generates
Undefined Instruction exceptions due to Java execution.
For coprocessor instructions the coprocessor number is used
to index in to a table of second-level handlers, TLUndef_CpTable.
Unused coprocessor entries use Unknown_Coproc_Handler().
The location, initialization and use of this table might be modified for systems that support other coprocessors, or dynamically load coprocessor second-level handlers.
Suitable implementations of Unknown_ARM_Handler(), Unknown_Thumb_Handler() and Unknown_Coproc_Handler() must
be provided by the operating system. These are called for illegal
instructions. They usually signal a fatal exception in the offending
application.
The behavior of TLUndef_Handler when the second-level handler returns is described in Section 7.3.2.
All handlers have this ATPCS-compliant prototype:
__value_in_regs HandlerReturnType handler_function( uint32 instr, ARM_RegDump *regdump)
Where:
instr is the undefined instruction, where
known.
regdump points to the register dump giving
the processor state when the instruction bounced, including the
CPSR. The handler might modify the contents of the register dump.
struct ARM_RegDump {
uint32 reg[16];
uint32 cpsr;
};
HandlerReturnType describes the two return
values. These are returned in registers due to the "__value_in_regs"
qualifier on the function definition.
struct HandlerReturnType {
uint32 skip_instr;
ControlBuffer *controlbuffer;
};
The skip_instr flag (r0) is usually set,
to indicate that the bounced instruction has been processed. If
the skip_instr flag is clear the top-level handler
returns to and retries the bounced instruction. The VFP handler
uses this for handling imprecise exceptions. When an internal exception
condition caused by one coprocessor instruction is signaled imprecisely,
by refusing to respond to a later coprocessor instruction, the handler
takes the action necessary to clear the exception condition, and
then returns to the refused instruction.
The controlbuffer pointer (r1), if valid, points to a ControlBuffer describing a user-level exception. Instead of returning to the application the top-level handler continues application execution in a user-level exception handler as defined in section 7.3.3.
A NULL controlbuffer pointer indicates exception processing is complete, and a standard return to the application code can be carried out.
An example VFP second-level handler is VFP_Undef_Handler in vfpundef.c.
This should need no modification for systems that can process
VFP bounces in a user-level exception handler. For systems that
process all VFP bounces on the kernel stack, the creation of a user-level
exception control buffer can be replaced with a simple call to _VFP_Computation_Engine().
VFP_Undef_Handler() first checks the
VFP is enabled, and if so it calls _VFP_Is_Compute_Exception() to
check if it is a legal VFP instruction. If either check fails the handler
calls Undef_Coproc_Handler() to process the
bounce and VFP_Undef_Handler() will complete
if it returns.
At this point, the Support Code knows the instruction is one
that it needs to handle. The Support Code needs to handle it in
the application's context so that the applications' floating-point
trap handlers can be called when necessary. It is therefore necessary
to create a data structure describing the exception that can be
passed to the computation engine running in the same mode as the
application. The Support Code also needs to arrange for TLUndef_Handler() to
return to VFP_Computation_Engine_Wrapper() rather
than directly back to the application code. The Support Code calls
out to GetControlBuffer to allocate the necessary space. Note that
this space must be thread local.
The Support Code then calls the _VFP_Collect_Trap_Description() to
copy the exception description from the VFP coprocessor into the
allocated space, and to reset the VFP coprocessor's exception mechanism.
The exception description is a list of operations that must be
processed in software.
If any of the following conditions are true when an instruction is passed to the VFP, then the VFP bounces that instruction:
the instruction is illegal, or
the EN bit in the FPEXC is clear, and the instruction is not a privileged access to an exception register, (for example FPEXC or FPSID).
there is a pending exception (the EX bit in the FPEXC is set) and the instruction is not a privileged access to an exception register.
the VFP is signaling a precise exception for the current instruction.The Support Code first checks that the VFP is enabled. If not the faulting instruction is passed on to the system's undefined coprocessor instruction handler.
Then the support code checks if the only reason for the bounce was an illegal instruction. If there is a pending exception and another reason for the bounce, then the pending exception must be dealt with first. Subsequently retrying the bounced instruction then triggers illegal handling.
The EX bit in the FPEXC signals a pending exception. If the EX bit is clear then illegal instruction bounces can be identified by checking the validity of the encoding of the instruction that bounced.
The subarchitecture support library provides a subarchitecture
optimized function _VFP_Is_Compute_Exception() for
recognizing illegal instruction bounces. This is described in Section
7.6.1.
If illegal handling is chosen, then VFP bounce processing is finished, and the faulting instruction is passed on to the operating system's undefined instruction handler.
When the reason for the bounce has been confirmed as a floating-point operation that needs software involvement, then the details of the bounce are collected and stored. These details include both the bounced instruction encoding and, if the EX bit is set, private exception information in the VFP.
The subarchitecture support library provides the _VFP_Collect_Trap_Description() function
to gather and record this information. This is described in Section
7.6.1. This function writes a bounce description to the data block
provided by the caller. The data block describes the VFP operations
that must be emulated before continuing normal execution.
When the bounce details have been saved the support library clears the EX bit in the FPEXC. This function returns a flag that indicates whether or not the return address should be incremented, so that the bounced instruction is not repeated.
The VFP second-level handler returns a ControlBuffer to the
top-level handler, indicating that exception handling must continue
in the application context, transferring control to the VFP_Computation_Engine_Wrapper() user-level
exception handler.
The top-level handler restores the user context before continuing
with VFP bounce processing. This is useful if FP traps are enabled,
and full application-level IEEE Floating-point trap support is required,
because it enables direct calls from the _VFP_Computation_Engine() to
the application's _fp_trap() exception handler.
The second-level handler returns to the top-level handler, where the application's context is restored.
The second-level handler might raise a user-level exception. In this case the top-level handler returns by transferring control and parameters to a user-level exception handler, instead of returning directly to the bounced instruction.
On return, if the controlbuffer pointer is NULL, the top level
handler restores saved registers, (as modified by the second-level
handler), and switches back to the application, continuing execution
either by retrying the refused instruction or from the following
instruction, as specified by skip_instr.
Alternatively the second handler might forward an exception on to an exception handler operating in the application context, that uses the application stack.
The handler can then make calls to user-defined exception handlers, which can in turn use longjmp() to change execution flow. In VFP this is necessary to support user-level trap handlers.
These can return a result for a trapped operation, and because a single instruction can cause multiple exceptions, there might be multiple entries to user-level handlers for a single instruction.
The mechanism used for this is likely to be operating system dependent, so this code and the associated ControlBuffer functions might need to be replaced for your system.
The handler provided calls GetControlBuffer() to get the address of a buffer for data that is to be passed to the user-level handler. This is likely to require modification for use with a multiple process operating system.
This has the prototype:
ControlBuffer *GetControlBuffer( ARM_RegDump *regdump, ReturnAddress target, unsigned size)
Where:
regdump is the register dump pointer, as
passed to the handler.
ReturnAddress target identifies the entry
point of the user-level exception handler.
Unsigned size gives the size of the exception description data that the handler wishes to pass to the user-level handler.
ControlBufferData(controlbuffer) returns
a pointer to a buffer of data_size bytes that is copied somewhere
the user-level handler can read it.
The top-level handler can be modified to copy the ControlBuffer information if appropriate.
The Undef handler passes control to code that receives the description data and resaves user state as necessary, and then calls an appropriate handler in an ATPCS-compliant way.
On return from the handler the wrapper code must completely restore the application state, including the flags in the CPSR, and then return to an address passed in by the top-level Undefined Instruction handler.
The VFP user-level exception handler is _VFP_Computation_Engine_Wrapper() in
"vfpwrapper.s". This code runs in the application's
context.
The user-level VFP handler processes bounced operations in the application's context using the VFP computation engine software. This emulates the instructions described in the bounce description data using software floating-point, and triggers floating-point traps as appropriate.
The VFP_Computation_Engine_Wrapper() first saves some registers and copies the ControlBuffer information on to the application's stack. This enables the use of VFP in application trap handlers, which might cause recursive bounces to the Support Code.
The _VFP_Computation_Engine() function
is called to emulate the bounced instructions, and signal floating-point
traps as appropriate. This is described in detail in Section 7.5.
As floating-point traps are encountered calls can be made to application trap handlers as specified by ANSI / IEEE Std. 754-1985, IEEE Standard for Binary Floating-Point Arithmetic. The application trap handlers can either return substitute results, or abort processing with longjmp. The Support Code uses the floating-point trap handler mechanism in the ARM C library, as described in RVDS Compiler and Libraries Guide.
A single VFP instruction can generate multiple floating-point traps. This is possible only if it is made up of multiple operations, as are vector instructions and multiply-accumulate instructions.