SWI

From ARMwiki
Revision as of 20:51, 11 December 2011 by Admin (Talk | contribs)

Jump to: navigation, search
SWI
Instruction SWI
Function SoftWare Interrupt
Category Interrupt
ARM family All
Notes -

Contents

SWI

SWI calls a software interrupt. This is akin to INT on the x86. The 6502 has no equivalent.

The SWI value field is an immediate 24 bit value, permitting SWIs from &0 to &FFFFFF.

The SWI value is ignored by the processor. The SWI interrupt is taken, at which point the SWI handler code of the operating system should sort out the SWI and then take the appropriate action.

Upon calling a SWI, the return address is placed in R14. This won't be of much concern to you unless you are writing kernel level code, but be aware that calling SWIs will corrupt R14.

Syntax

  SWI   <number>

Function

  Call operating system software interrupt handler

Example

  SWI  &12345        ; call SWI &12345

RISC OS specific

Calling SWIs by name in BASIC, such as:

  SYS "Wimp_PollIdle", %1, hdl%, (time% + 100)

is very inefficient as the BASIC interpreter is required to translate the SWI name ("Wimp_PollIdle") into a number (&400E1) every time the SWI is called. It is best to use SWI names in your source code, for clarity, and 'compress' the code prior to release. There are numerous compressors available, most of them will convert SWIs to numbers. The following is the same:

  SYS 262369, 1, hdl%, (time%+100)

Convention provides for SWIs to have an "X" form, which instructs the operating system to suppress any error that might have been generated, and instead return with the V bit set to indicate an error. This permits you to handle the error in your own way, which is usually going to be nicer than the usual OS method (namely "bang!").
To use this, simply prefix any SWI name with an X (XOS_Write0, XFileCore_DiscOp, XWimp_Poll, etc), or if calling by number, set bit 17 (OR with &10000).

Linux specific

For reasons that are explained below, later (EABI) versions of Linux have deprecated the use of SWI numbers, and instead recommend calling SWI &0 with the desired SWI in R7.

Technical

The instruction bit pattern is as follows:

31 - 28 27 - 24 23 - 0
condition 1 1 1 1 24 bit immediate

Example SWI dispatch

Here is how the SWI dispatch works on RISC OS 5:

  • Registers R10-R12 are preserved
  • The SWI instruction is loaded into R11 using LDR r11, [r14 #-4], using the fact that R14 is set to point to the instruction after the SWI call.
  • The upper byte is cleared, leaving the OS with a number to dispatch as necessary.

By passing the number in a register, none of the above is required. We can skip straight to the SWI dispatch. This is the method used by the Linux EABI kernel which specifies the SWI number in R7 (although, for compatibility, there is an option to support the older method as well).

Why the SWI number matters

The behaviour of the SWI call was logical on the ARM2 - with no cache it would just backtrack and load the SWI instruction. The behaviour was even more logical on the ARM3 - with a von Neuman architecture, the SWI instruction would already be present in the combined instruction/data cache, so the LDR would just pluck it from the cache.

However modern ARM processors use Harvard architecture with separate instruction and data caches. While the instruction will be in the instruction cache, there is no mechanism for loading it from there. Instead, it is read anew into the data cache, thus polluting the data cache with code.

For this reason, it is a more preferable action to provide the SWI number in a register, rather than specify them on the command line. This does, however, carry its own baggage - registers may need to be preserved, the desired register will need to be set up prior to calling the SWI.

There are about 178 defined Linux system calls [1]. Linux has switched to the "armel" release to call SWIs with the number passed in a register.

The RISC OS kernel offers several hundred system calls, most system modules offer between five and thirty SWIs [2] (<- the list linked is woefully incomplete and misses many things - printer support, various hardware, internationalisation, most filing systems...). There are about 80 such modules in a standard ROM release, not to mention a plethora of third-party modules. As such, RISC OS depends heavily on SWI numbers being included in the SWI instruction. To do it any other way would be unthinkable, and would likely break everything ever written for RISC OS.

What happens when a SWI is issued

When the processor encounters a SWI instruction, it takes the SWI vector as follows:

  • Switch to SVC mode
  • R14_svc is set to SWI instruction address + 4
  • SPSR_svc is set to CPSR (not on legacy processors)
  • IRQs are disabled
  • Processor branches to address &8

Very simple SWI dispatch

The operating system may have a simple SWI handler for a core set of system services, or it may be like RISC OS and provide a complex and overwhelmingly powerful use of SWIs.

Here is an example of a very simple SWI dispatch routine to give you a feel for how it works. At address &8 is a branch to this routine:

  .swi_handler
  STMFD   sp!, {r12}
  LDR     r12, [r14, #-4]       ; load the SWI instruction
  BIC     r12, r12, #&FF000000  ; keep only the SWI number
  CMP     r12, #SWIcount        ; SWI in range?
  LDRLE   pc, [pc, r12, LSL #2] ; if so, load address from jumptable
  B       swi_unknown           ; else handle unknown SWIs
  
  DCD     SwiHandler0           ; pointer to SWI &0 handler code
  DCD     SwiHandler1           ; pointer to SWI &1 handler code
  DCD     SwiHandler2           ; pointer to SWI &2 handler code
  DCD     SwiHandler3           ; pointer to SWI &3 handler code
  ...etc...
  
  .swihandler0
  ; code to handle SWI &0
  ...do stuff...
  
  LDMFD   sp!, {r12}            ; restore previously saved R12
  MOVS    pc, r14               ; restore CPSR and PC to return from SWI
Personal tools
Namespaces

Variants
Actions
Navigation
Contents
Toolbox