[Select]

Core


Index

 

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 :

  1. Requested region totally outside our area.

  2. Requested region totally inside our area (including spanning the whole area).

  3. Requested region partially overlaps our area (overlaps the start, or the end of our area).

  4. 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 3QD Developments Ltd 2013 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 3QD Developments Ltd in good faith and is believed to be correct at the time of publication E&OE. 3QD Developments 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.03 3rd November 2015