RISC OS Documentation - Abort trapping
======================================

Introduction
------------
In order to provide for more extensible memory area handling and to allow
more non-core functions to move out of the kernel, the handlers for trapping
aborts have now been completed. These handlers were planned and designed for
RISC OS 4 but were incomplete and had been deferred until they could be
finished. The SWI OS_AbortTrap allows areas of memory to be associated with
a handler. These handlers may be used to provide a demand page mapping
system of the users choice.

Implementation details
----------------------
The registration system allows contiguous regions of memory to be associated
with abort handlers. Each region is defined by a low and a high address. If
a data abort occurs between these addresses, the handler will be invoked.
The handler is expected to perform the aborted data transfer and return with
the V flag clear, or if this is not possible then return an error by the
normal mechanism. Usually the handler will first map in the relevant page.

The Kernel will have pre-decoded the instruction which caused the abort in
order to decide what operation is being performed upon the memory. The
operation type (whether it is a read, or a write) and user priveledge on
execution will be flagged to the handler. Block data transfers (for example
caused by LDM and STM instructions) will also have been decoded by the
Kernel. The handler must only satisfy the data transfer to a temporary
memory block. For load operations (block and single register), the Kernel
will marshal the temporary block into the relevant registers once the
handler has returned.

If the region of memory handled by a registered AbortTrap is not known to
the Kernel (i.e. it is not a 'aborting' mapped dynamic area) - for example a
sparse dynamic area whose unmapped regions will be mapped on demand - it
will be necessary for the handler to also provide a Service_ValidateAddress
entry point. Refer to the PRMs for more details on this service call, and to
the example before for an implementation.

The Kernel can handle multiple claimants of AbortTrap regions. When a
Dynamic Areas creation requests that the region handle aborts within its
region (see AbortableDAs) it is handled by a veneer registered through the
OS_DynamicArea creation code. It is strongly recommended that clients use
the OS_DynamicArea abort trapping rather than directly implementing an
AbortTrap region.


SWI calls
---------

SWI OS_AbortTrap (&67)
----------------------
On entry:
   R0 = operation :
               0 = register handler
               1 = deregister handler
               other operations are reserved
   R1 = low address (inclusive)
   R2 = high address (exclusive)
   R3 = pointer to address to call to handle aborts
   R4 = value to pass in R12 when calling handler address
On exit:
   All registers preserved

This SWI is used to register or deregister an AbortTrap handler. The SWI
will return an error if the operation requested is invalid. This SWI is not
implemented prior to Kernel version 6.61.

AbortTrap Handler
-----------------
On entry:
   R0 = flags:
         bits 0-3 = operation type:
                  0 if the operation was a 'store'
                    (transfer from block at R1 to memory)
                  1 if the operation was a 'load'
                    (transfer from memory to block at R1)
               others reserved for future expansion
         bit 4 = 0 if the operation was performed in user mode
                 1 if the operation was performed in a privileged mode
                 others reserved
   R1 = address into which the transfer should be performed
   R2 = address of memory required by the abort
   R3 = size of memory required by the abort (this may be 1, 2, 4,
                 or any multiple of 4 up to 64)
On exit:
   All registers preserved if completed successfully
   V set if failed, R0 pointing to an error block

The AbortTrap Handler will be called when an operation is performed on
memory which does not exist. The handler should map in the memory, perform
the transfer and return, or set the V flag and return. The handler may cause
other areas of memory to be mapped out.

Once mapped in, the handler performs a transfer itself by copying from the
address pointed to by R2 to the address pointed to by R1, for R3 bytes.

Handlers may choose to omit the mapping in of a real page of memory. In such
cases, the region will remain unmapped and subsequent operations on it will
result in further calls to the AbortTrap handler.

If the error block contains an error number for a hardware error (ie bit
31), it will be returned through the error generation mechanism rather than
through the abort handler. In other words, it is similar to the instruction
which faulted having been an error returning SWI. The exception registers
and abort stack copies will still take place in this case. However, if the
error is not claimed by ErrorV or the environment handler, an explicit
OS_Exit will be issued.

A SWP instruction will be processed as a load followed by a store operation.

Example code
------------
Because Service_ValidateAddress has no knowledge of the internal construct
used by external AbortTrap handlers, it will be necessary that parties
interested in unmapped regions perform checks in addition to those for just
their region. Assuming a single area is provided by a module, the region
being validated may in one of 4 memory locations:

 Requested region totally outside our area.
 Requested region totally inside our area (including spanning the whole
    area).
 Requested region partially overlaps our area (overlaps the start, or the
    end of our area).
 Requested region totally encloses our area.

The ValidateAddress service does not allow for partitioning of space. It may
require further processing by other such clients once a part of the region
has been validated. In order to achieve this, the regions which are outside
the controlled area must be re-parsed by the OS_ValidateAddress in order to
decide on the behaviour.

The following C code implements such a validation scheme for a single
handled region. Clients with multiple regions may apply a similar method
once they have determined the region within which the validation falls.
Clients with multiple regions may wish to recheck the regions before issuing
the OS_Validate address in case a continuous area spanning multiple regions
has been requested. This optimisation may not be significant in practice,
however.

Within this code, the following is assumed:

- Standard CMHG service entry sequence is used.
- area_start is the inclusive start address of the region being managed by
  this code
- area_end is the exclusive end address of the region being managed by this
  code.
- dprintf is a printf-like function used for debugging.

/********************************************************************
 * Function:     Mod_Service
 * Description:  Service call handler routine. All services which are being
 *               received by the module will be passed to this routine.
 * Parameters:   service = service call number
 *               r       = pointer to register block on entry
 *               pw      = private word for module
 * On exit:      Update register values on return by updating r.
 ********************************************************************/
   void Mod_Service(int service, _kernel_swi_regs *r, void *pw)
   {
   switch (service)
    {
     case Service_ValidateAddress: /* PRM1-362 */
      {
        long a = (unsigned long)r->r[2];
        unsigned long b = (unsigned long)r->r[3];
        dprintf("ValidateAddress: %08lx - %08lx\n", a, b);
        /*
                   area_start          area_end
               _____________|__________|_________________
              |             |   OURS   |                 |
              +------------------------------------------+
                   |    |_____|     |______|   |
                   |   a   1   b   a    2   b  |
                   |___________________________|
                  a             3               b
              ALL:   a < area_end
                     b > area_start
           1:   1a < area_start, 1b > area_start
                Extra region: 1a to area_start
           2:   2a < area_end,   2b > area_end
                Extra region: area_end to 2b
           3:   3a < area_start, 3b > area_end
                Extra region: 3a to area_start
                                area_end to 3b
       We split this into a check for enclosing the region (ALL),then
       checking whether 1 (or 3) applies and validating that region,then
       checking whether 2 (or 3) applies and validating that region. If 
       the tests all pass, the memory is valid.
     */
      if (a < area_end &&
         b > area_start)
      {
       /*'ALL' applies - ie the region requested encloses (possibly
           partially) our area.
        */
        _kernel_oserror *err;
        int flags;
        dprintf("  encloses our area\n");
         /* Check extra region 1 (applies to 3 as well) */
        if (a < area_start)
        {
          dprintf("  need to validate %08lx - %08lx (LEFT)\n",a,area_start);
          err = _swix(OS_ValidateAddress, _INR(0,1) | _OUT(_FLAGS),
                                              a, area_start, &flags);
          if (err || (flags & _C))
        {it didn't pass this before region test, so the region is not valid.
                 */
          dprintf("*Failed LEFT*\n");
          return;
        }
      }
       /* Check extra region 2 (applies to 3 as well) */
      if (b > area_end)
      {need to validate %08lx - %08lx (RIGHT)\n", area_end, b);
        err = _swix(OS_ValidateAddress, _INR(0,1) | _OUT(_FLAGS),
                                        area_end, b, &flags);
        if (err || (flags & _C))
        {it didn't pass this after region test, so the region is not valid.
           */
          dprintf("*Failed RIGHT*\n");
          return;
        }
      }
       /* The region covers our area AND the left and right region checks
         have come up as valid so the whole region is valid.*/
      r->r[1] = 0; /* Region is valid */
      dprintf("*Region is valid*\n");
      return;
         }
       }
       break;
    } }


The AbortTrap handler code should perform the copy operations necessary to
satisfy the transfer request. This can be performed using a single 'memcpy'
instruction for each direction. As above, the dprintf function is used to
provide debugging on the abort. In addition, the 'page_in' function is
expected to perform the paging operation and either return NULL or an error
pointer. Because the error pointer will be discarded on return by the Kernel
the error block is not relevant. The CMHG veneer ensures that V is set if a
non-NULL valid is returned.

/*******************************************************************
 * Function:     Abort_Handler
 * Description:  Generic handler function
 * Parameters:   r  = pointer to register block on entry
 *               pw = private word for module
 * On exit:      Update r to alter return values
 *               Return NULL to return with V clear
 *               Return an error pointer to set V and r0
 *******************************************************************/
   _kernel_oserror *Abort_Handler(_kernel_swi_regs *r, void *pw) {*err ;
    #define ABORT_TYPE_MASK   (15<<0)
    #define ABORT_TYPE_STORE  (0<<0)
    #define ABORT_TYPE_LOAD   (1<<0)
    #define ABORT_SVC    (1<<4)
    unsigned long flags = (unsigned long)r->r[0];
    unsigned long start = (unsigned long)r->r[2];
    unsigned long size = (unsigned long)r->r[3];
    unsigned long block = (unsigned long)r->r[1];
     dprintf("\n\nABORT: flags = %08lx\n", flags);
    dprintf(    "       start = %08lx\n", start);
    dprintf(    "       size  = %08lx\n", size);
    dprintf(    "       block = %08lx\n", block);
     err = page_in(start, size);
    if (err)
    {page in failed: %s\n", err->errmess);
      return err;
    }
     switch (flags & ABORT_TYPE_MASK)
    {abort_type_load: memcpy((void *)block, (void *)start, size);
        break;
       case ABORT_TYPE_STORE:
        memcpy((void *)start, (void *)block, size);
        break;
       default:
        dprintf("Unsupported operation type %i\n", flags & ABORT_TYPE_MASK);
        return err_Unsupported;
    }
     dprintf("AbortTrap satisfied\n");
     return NULL; }
   
Caveats
-------
The following caveats should be noted when using the Abort trapping
interfaces:

- Hardware FPU operations not supported. Software FPE will function
correctly because all the transfers degrade to standard LDR/STR or LDM/STM
operations, with the exception of the LDRT/STRT operations.

- Half-word operations (LDR[S]H/STR[S]H) are not supported

This documentation is copyright RISCOS Ltd 2006 and may not be reproduced or
published in any form without the copyright holders permission.  RISC OS is
subject to continuous development and improvement as such all information is
reproduced by RISCOS Ltd in good faith and is believed to be correct at the
time of publication E&OE. RISCOS Ltd cannot accept any liability for any
loss or damage arising from the use of any information provided as part of
the RISC OS Documentation.

HTML document version 1.01  16th October 2006
