zeoneo
Posts: 13
Joined: Sun Sep 30, 2018 6:54 am

RPI 3B UNDEF exception handler is not called.

Sat Dec 01, 2018 7:29 pm

Hi guys,

I am trying (bare metal) exception handling tutorials on RPI 3B.

Here is my code.

---boot.S-------

Code: Select all

// To keep this in the first portion of the binary.
.section ".text.boot"
 
// Make _start global.
.globl _start
.global _get_stack_pointer
.global _exception_table
.global _enable_interrupts

.equ ARM_MODE_USR, 0x10;								;@ CPU in USR mode .. Normal User mode
.equ ARM_MODE_FIQ, 0x11;								;@ CPU in FIQ mode .. FIQ Processing
.equ ARM_MODE_IRQ, 0x12;								;@ CPU in IRQ mode .. IRQ Processing
.equ ARM_MODE_SVC, 0x13;								;@ CPU in SVC mode .. Service mode
.equ ARM_MODE_HYP, 0x1A;								;@ CPU in HYP mode .. Hypervisor mode  (ARM7/ARM8 only)
.equ ARM_MODE_UND, 0x1B;								;@ CPU in UND mode .. Undefined Instructions mode
.equ ARM_MODE_SYS, 0x1F;	

.equ ARM_MODE_MASK, 0x1F;								;@ Mask to clear all but CPU mode bits from cpsr register
.equ ARM_I_BIT,		0x80;								;@ IRQs disabled when set to 1
.equ ARM_F_BIT,		0x40;

.equ    CPSR_MODE_USER,         0x10
.equ    CPSR_MODE_FIQ,          0x11
.equ    CPSR_MODE_IRQ,          0x12
.equ    CPSR_MODE_SVR,          0x13
.equ    CPSR_MODE_ABORT,        0x17
.equ    CPSR_MODE_UNDEFINED,    0x1B
.equ    CPSR_MODE_SYSTEM,       0x1F

// See ARM section A2.5 (Program status registers)
.equ    CPSR_IRQ_INHIBIT,       0x80
.equ    CPSR_FIQ_INHIBIT,       0x40
.equ    CPSR_THUMB,             0x20

.equ	SCTLR_ENABLE_DATA_CACHE,        0x4
.equ	SCTLR_ENABLE_BRANCH_PREDICTION, 0x800
.equ	SCTLR_ENABLE_INSTRUCTION_CACHE, 0x1000


.balign 4

_start:
    ldr pc, _reset_h
    ldr pc, _undefined_instruction_vector_h
    ldr pc, _software_interrupt_vector_h
    ldr pc, _prefetch_abort_vector_h
    ldr pc, _data_abort_vector_h
    ldr pc, _unused_handler_h
    ldr pc, _interrupt_vector_h
    ldr pc, _fast_interrupt_vector_h

	_reset_h:                           .word   _reset_
	_undefined_instruction_vector_h:    .word   undefined_instruction_vector
	_software_interrupt_vector_h:       .word   software_interrupt_vector
	_prefetch_abort_vector_h:           .word   prefetch_abort_vector
	_data_abort_vector_h:               .word   data_abort_vector
	_unused_handler_h:                  .word   _reset_
	_interrupt_vector_h:                .word   irq_handler_asm_wrapper
	_fast_interrupt_vector_h:           .word   fast_interrupt_vector


_reset_:
	mov r12, pc											;@ Hold boot address in high register R12
	mrs r0, CPSR										;@ Fetch the cpsr register
	orr r0, r0, #(ARM_I_BIT | ARM_F_BIT)				;@ Disable Irq/Fiq
	and r11, r0, #ARM_MODE_MASK							;@ Clear all but CPU mode bits in register r11

 	cmp r11, #ARM_MODE_HYP								;@ Check we are in HYP_MODE											
	bne .NotInHypMode									;@ Branch if not equal meaning was not in HYP_MODE  
	bic r0, r0, #ARM_MODE_MASK							;@ Clear the CPU mode bits in register r0							
	orr r0, r0, #ARM_MODE_SVC							;@ ARM_MODE_SVC bits onto register	
    msr spsr_cxsf,r0									;@ Hold value in spsr_cxsf
    add lr,pc,#4										;@ Calculate address of .NotInHypMode label

	msr ELR_hyp, lr
	eret

.NotInHypMode:
    mov r0,#0x8000
    mov r1,#0x0000
    ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
    stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
    ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
    stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}

    ;@ (PSR_IRQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
    mov r0,#0xD2
    msr cpsr_c,r0
    mov sp,#0x8000

    ;@ (PSR_FIQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
    mov r0,#0xD1
    msr cpsr_c,r0
    mov sp,#0x4000

    ;@ (PSR_SVC_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
    mov r0,#0xD3
    msr cpsr_c,r0
    mov sp,#0x8000000

	// Call clear-bss.c
	bl _clear_bss

 
	// halt
_inf_loop:
    b       _inf_loop

.globl PUT32
PUT32:
    str r1,[r0]
    bx lr

.globl GET32
GET32:
    ldr r0,[r0]
    bx lr

.globl dummy
dummy:
    mov     pc, lr

.globl enable_irq
enable_irq:
    mrs r0,cpsr
    bic r0,r0,#0x80
    msr cpsr_c,r0
    bx lr

_get_stack_pointer:
    // Return the stack pointer value
    str     sp, [sp]
    ldr     r0, [sp]

    // Return from the function
    mov     pc, lr


_enable_interrupts:
    mrs     r0, cpsr
    bic     r0, r0, #0x80
    msr     cpsr_c, r0
    mov     pc, lr

irq_handler_asm_wrapper:
    sub     lr, lr, #4      // Adjsut return address
    srsdb   sp!, #0x13      // Save irq lr and irq spsp to supervisor stack, and save the resulting stack pointer as the current stack pointer
    cpsid   if, #0x13       // Switch to supervisor mode with interrupts disabled
    push    {r0-r3, r12, lr}// Save the caller save registers
    and     r1, sp, #4      // Make sure stack is 8 byte aligned
    sub     sp, sp, r1
    push    {r1}            // Save the stack adjustment
    bl      c_irq_handler
    pop     {r1}            // Get the stack adjustment
    add     sp, sp, r1
    pop     {r0-r3, r12, lr}// Revert the caller save registers
    rfeia   sp!             // Load the saved return address and program state register from before the interrupt from the stack and return 

--------------interrupt.c------------------------

Code: Select all

#include <stdint.h>

#include <kernel/rpi-base.h>
#include <kernel/rpi-armtimer.h>
#include <kernel/rpi-interrupts.h>
#include <stdio.h>

/** @brief The BCM2835 Interupt controller peripheral at it's base address */
static rpi_irq_controller_t *rpiIRQController =
    (rpi_irq_controller_t *)RPI_INTERRUPT_CONTROLLER_BASE;

volatile int count_irqs = 0;

/**
    @brief Return the IRQ Controller register set
*/
rpi_irq_controller_t *RPI_GetIrqController(void)
{
    return rpiIRQController;
}

/**
    @brief The Reset vector interrupt handler

    This can never be called, since an ARM core reset would also reset the
    GPU and therefore cause the GPU to start running code again until
    the ARM is handed control at the end of boot loading
*/
void __attribute__((interrupt("ABORT"))) reset_vector(void)
{
    printf("ABORT INTERRUPT OCCURRED");
    while (1)
        ;
}

/**
    @brief The undefined instruction interrupt handler

    If an undefined intstruction is encountered, the CPU will start
    executing this function. Just trap here as a debug solution.
*/
void __attribute__((interrupt("UNDEF"))) undefined_instruction_vector(void)
{
    printf("Undef Interrupt Occurred");
    while (1)
        ;
}

void __attribute__((interrupt("SWI"))) software_interrupt_vector(void)
{
    printf("Software Interrupt Occurred");
}

void __attribute__((interrupt("ABORT"))) prefetch_abort_vector(void)
{
    printf("prefetch_abort_vector Interrupt Occurred");
    while (1)
        ;
}

void __attribute__((interrupt("ABORT"))) data_abort_vector(void)
{
    printf("data_abort_vector Interrupt Occurred");
    while (1)
        ;
}

void c_irq_handler(void)
{
    RPI_GetArmTimer()->IRQClear = 1;
    count_irqs++;
    printf("\n irq_count: %d", count_irqs);
    if (count_irqs >= 13)
    {
        printf("\n irq count is greater than 13: %d", count_irqs);
        __asm__ __volatile__(".long 0x0ffffffffffff");
    }
}

void __attribute__((interrupt("FIQ"))) fast_interrupt_vector(void)
{
    printf("fast_interrupt_vector Interrupt Occurred");
}
I get following output

Code: Select all

rq_count: 1
 irq_count: 2
 irq_count: 3
 irq_count: 4
 irq_count: 5
 irq_count: 6
 irq_count: 7
 irq_count: 8
 irq_count: 9
 irq_count: 10
 irq_count: 11
 irq_count: 12
 irq_count: 13
It just stucks after 13 as expected but it doesn't call UNDEF exception handler.

I tried setting stack for UNDEF mode, it didn't work. What is that I am missing?

Return to “Bare metal, Assembly language”