title: ARM Cortex A9 Startup Code and Interrupt Setup
body: <p>I try to program Cortex-A9 in a bare metal fashion. I use the 'hello world' code from:
  <a href="https://github.com/tukl-msd/gem5.bare-metal" rel="nofollow">https://github.com/tukl-msd/gem5.bare-metal</a> which works. However, I'm not able to get interrupts working. When I create an Interrupt with Interrupt e.g. #47 my software doesn't jump in the ISR function. What I am missing? Do I have to do some more initialization?</p>
  
  <p>Startup Code:</p>
  
  <pre><code>.section INTERRUPT_VECTOR, "x"
  .global _Reset
  _Reset:
      B Reset_Handler /* Reset */
      B .             /* Undefined */
      B .             /* SWI */
      B .             /* Prefetch Abort */
      B .             /* Data Abort */
      B .             /* reserved */
      B irq_handler   /* IRQ */
      B irq_handler   /* FIQ */
  
  // Some Definitions for GIC:
  .equ GIC_DIST, 0x10041000
  .equ GIC_CPU , 0x10040000
  
  // GIC Definitions for CPU interface
  .equ ICCICR  , 0x00
  .equ ICCPMR  , 0x04
  .equ ICCEOIR , 0x10
  .equ ICCIAR  , 0x0C
  
  // GIC Definitions for Distributor interface
  .equ ICDDCR  , 0x00
  .equ ICDISER , 0x100
  .equ ICDIPTR , 0x800
  
  // Other Definitions
  .equ USR_MODE , 0x10
  
  GIC_dist_base : .word 0 // address of GIC distributor
  GIC_cpu_base  : .word 0 // address of GIC CPU interface
  
  
  Reset_Handler:
      LDR sp, =stack_top
  
      // Enable Interrupts on CPU Side:
      MRS r1, cpsr        // get the cpsr. 
      BIC r1, r1, #0x80   // enable IRQ (ORR to disable). 
      MSR cpsr_c, r1      // copy it back, control field bit update.
  
      // Configure GIC:
      BL IC_init
  
      // Branch to C code
      BL main
      B .
  
  // Initialize GIC
  .global GIC_init
  IC_init:
      stmfd sp!,{lr}
      // Read GIC base from Configuration Base Address Register
      // And use it to initialize GIC_dist_base and GIC_cpu_base
      //mrc p15, 4, r0, c15, c0, 0
      //add r2, r0, #GIC_DIST // Calculate address
      ldr r2, =GIC_DIST
      ldr r1, =GIC_dist_base
      str r2,[r1] // Store address of GIC distributor
      //add r2, r0, #GIC_CPU // Calculate address
      ldr r2, =GIC_CPU
      ldr r1, =GIC_cpu_base
      str r2,[r1] // Store address of GIC CPU interface
  
      // Register (ICCPMR) to enable interrutps of all priorities
      ldr r1,=0xFFFF
      ldr r2,=GIC_dist_base
      str r1,[r2,#ICCPMR]
  
      // Set the enable bit in the CPU interface control register
      // ICCICR, allowing CPU(s) to receive interrupts
      mov r1,#1
      str r1,[r2,#ICCICR]
  
      // Set the enable bit in the distributor control register
      // ICDDCR, allowing interrpupts to be generated
      ldr r2,=GIC_dist_base
      ldr r2,[r2] // Nase address of distributor
      mov r1, #1
      str r1,[r2,#ICDDCR]
      ldmfd sp!,{pc}
  
  //config_interrupt (int ID , int CPU);
  .global config_interrupt
  config_interrupt:
      stmfd sp!,{r4-r5, lr}
  
      // Cinfigure the distributor interrupt set-enable registers (ICDISERn)
      // enable the intterupt
      // reg_offset = (M/32)*4 (shift and clear some bits)
      // value = 1 &lt;&lt; (N mod 32);
      ldr r2,=GIC_dist_base
      ldr r2,[r2] // Read GIC distributor base address
      add r2,r2,#ICDISER // r2 &lt;- base address of ICDSER regs
      lsr r4,r0,#3 // clculate reg_offset
      bic r4,r4,#3 // r4 &lt;- reg_offset
      add r4,r2,r4 // r4 &lt;- address of ICDISERn
  
      // Create a bit mask
      and r2,r0,#0x1F // r2 &lt;- N mod 32
      mov r5,#1 // need to set one bit
      lsl r2,r5,r2 // r2 &lt;- value
  
      // Using address in r4 and value in r2 to set the correct bit in the GIC register
      ldr r3,[r4] // read ICDISERn
      orr r3, r3, r2 // set the enable bit
      str r3,[r4]  // store the new register value
  
      // Configure the distributor interrupt processor targets register (ICDIPTRn)
      // select target CPU(s)
      // reg_offset = (N/4)*4 (clear 2 bottom bits)
      // index = N mod 4;
      ldr r2,=GIC_dist_base
      ldr r2,[r2] // Read GIC distributor base address
      add r2,r2, #ICDIPTR // base address of ICDIPTR regs
      bic r4,r0,#3 // r4 &lt;- reg_offset
      add r4,r2,r4 // r4 &lt;- address of ICDIPTRn
  
      // Get the address of th ebyte wihtih ICDIPTRn
      and r2,r0,#0x3 // r2 &lt;- index
      add r4,r2,r4 // r4 &lt;- byte address to be set
      strb r1,[r4]
  
      ldmfd sp!, {r4-r5, lr}
  
  // int get_inLerrupt_number();
  // Get the interrupt ID for the current interrupt. This should be called al the
  // beginning of ISR. It also changes the state of the interrupt from pending to
  // active, which helps to prevent other CPUs from trying to handle it.
  .global get_interrupt_number
  get_intterrupt_number:
      // Read the JCCIAR from the CPU Interface
      ldr r0,=GIC_cpu_base
      ldr r0,[r0]
      ldr r0,[r0,#ICCIAR]
      mov pc,lr
  
  // void end_of_interrupt (int ID);
  // Notify the GIC that the interrupt has been processed. The state goes from
  // active to inactive, or it goes from active and pending to pending.
  .global end_of_interrupt
  end_of_interrupt:
      ldr r1,=GIC_cpu_base
      ldr r1,[r1]
      str r0,[r1,#ICCEOIR]
      mov pc, lr
  
  // IRQ Handler that calls the ISR function in C
  .global irq_handler
  irq_handler:
      stmfd sp!,{r0-r7, lr}
      // Call Interrupt Service Routine in C:
      bl ISR
      ldmfd sp!, {r0-r7, lr}
      // Must substract 4 from lr
      subs pc, lr, #4
  </code></pre>
  
  <p>Linker Script:</p>
  
  <pre><code>ENTRY(_Reset)
  SECTIONS
  {
   . = 0x0;
   .text : {
   boot.o (INTERRUPT_VECTOR)
   *(.text)
   }
   .data : { *(.data) }
   .bss : { *(.bss COMMON) }
   . = ALIGN(8);
   . = . + 0x1000; /* 4kB of stack memory */
   stack_top = .;
   PROVIDE (end = .)   ;
  }
  </code></pre>
  
  <p>Main C Program:</p>
  
  <pre><code>#include &lt;stdio.h&gt;
  
  extern "C" void config_interrupt(int, int);
  
  volatile unsigned int * const SHADOW = (unsigned int *)0x1000a000;
  
  void sendShadow(unsigned int s)
  {
          *SHADOW = s;
  }
  
  int main(void)
  {
      config_interrupt(47,0);
  
      unsigned int r = 1337;
  
      while (1)
      {
          printf("Hello World! %d\n", r);
          sendShadow(1);  
      }
  }
  
  void ISR(void)
  {
      printf("ISR");
  }
  </code></pre>