/*++

Copyright (c) 2012 Minoca Corp. All Rights Reserved

Module Name:

    trap.S

Abstract:

    This module implements interrupt and exception trap management, such as
    saving and restoring registers.

Author:

    Evan Green 11-Aug-2012

Environment:

    Kernel mode

--*/

##
## ------------------------------------------------------------------- Includes
##

#include <minoca/kernel/arm.inc>

##
## ---------------------------------------------------------------- Definitions
##

##
## ---------------------------------------------------------------------- Code
##

ASSEMBLY_FILE_HEADER

##
## VOID
## ArpInitializeExceptionStacks (
##     PVOID ExceptionStacksBase,
##     ULONG ExceptionStackSize
##     )
##

/*++

Routine Description:

    This routine initializes the stack pointer for all privileged ARM modes. It
    switches into each mode and initializes the banked r13. This function
    should be called with interrupts disabled and returns with interrupts
    disabled.

Arguments:

    ExceptionStacksBase - Supplies a pointer to the lowest address that should
        be used for exception stacks. Each stack takes up 16 bytes and there are
        4 modes, so at least 64 bytes are needed.

    ExceptionStackSize - Supplies the size of each exception stack.

Return Value:

    None.

--*/

FUNCTION ArpInitializeExceptionStacks

    ##
    ## Load R1 with an individual stack size.
    ##

    add     %r0, %r0, %r1

    ##
    ## Disable interrupts and switch into IRQ mode. Note that this also
    ## clobbers the flags register.
    ##

    mov     %r2, #(PSR_FLAG_IRQ | ARM_MODE_IRQ)
    msr     CPSR_cxsf, %r2
    mov     %sp, %r0
    add     %r0, %r0, %r1

    ##
    ## Initialize the FIQ stack.
    ##

    mov     %r2, #(PSR_FLAG_IRQ | ARM_MODE_FIQ)
    msr     CPSR_cxsf, %r2
    mov     %sp, %r0
    add     %r0, %r0, %r1

    ##
    ## Initialize the undefined instruction stack.
    ##

    mov     %r2, #(PSR_FLAG_IRQ | ARM_MODE_UNDEF)
    msr     CPSR_cxsf, %r2
    mov     %sp, %r0
    add     %r0, %r0, %r1

    ##
    ## Initialize the data fetch abort stack.
    ##

    mov     %r2, #(PSR_FLAG_IRQ | ARM_MODE_ABORT)
    msr     CPSR_cxsf, %r2
    mov     %sp, %r0

    ##
    ## Switch back to SVC mode and return.
    ##

    mov     %r2, #(PSR_FLAG_IRQ | ARM_MODE_SVC)
    msr     CPSR_cxsf, %r2
    bx      %lr

END_FUNCTION ArpInitializeExceptionStacks

##
## VOID
## ArpUndefinedInstructionEntry (
##     VOID
##     )
##

/*++

Routine Description:

    This routine directly handles an exception generated by an undefined
    instruction. It uses a largely separate code path from normal exceptions
    to avoid recursively breaking into the debugger.

Arguments:

    None.

Return Value:

    None.

--*/

FUNCTION ArpUndefinedInstructionEntry

    ##
    ## Save state and create a trap frame.
    ##

    ARM_ENTER_INTERRUPT

    ##
    ## Call the main dispatch routine routine with a pointer to the trap frame
    ## as the only parameter. Align the stack down even though it shouldn't be
    ## strictly necessary in case something bad happened.
    ##

    mov     %r0, %sp
    mov     %r4, %sp
    and     %r1, %r4, #0xFFFFFFF0       @ Align stack down for fear of badness.
    mov     %sp, %r1
    bl      KeDispatchUndefinedInstructionException
    mov     %sp, %r4

    ##
    ## Restore state and return.
    ##

    ARM_EXIT_INTERRUPT

END_FUNCTION ArpUndefinedInstructionEntry

##
## VOID
## ArpSoftwareInterruptEntry (
##     VOID
##     )
##

/*++

Routine Description:

    This routine directly handles an exception generated by a software
    interrupt (a system call).

Arguments:

    None.

Return Value:

    None.

--*/

FUNCTION ArpSoftwareInterruptEntry
    srsdb   %sp!, #ARM_MODE_SVC                  @ Push lr and spsr.
    mov     %lr, #0                              @ Zero SVC link register.
    stmdb   %sp!, {%r1-%r12, %lr}                @ Push general registers.
    mrs     %r2, cpsr                            @ Get the "exception" CPSR.
    stmdb   %sp!, {%r0, %r2}                     @ Push exception CPSR and R0.
    mov     %r4, %sp                             @ Get stack.
    sub     %sp, #8                              @ Account for pushes.
    cps     #ARM_MODE_SYSTEM                     @ Switch to system mode.
    str     %lr, [%r4, #-4]                      @ Save usermode SP.
    str     %sp, [%r4, #-8]                      @ Save usermode LR.
    cpsie   i, #ARM_MODE_SVC                     @ Enable interrupts, svc mode.
    sub     %sp, #4                              @ Allocate room for sp.
    str     %sp, [%sp]                           @ Save SP.

    ##
    ## The system call routine takes three parameters: the system call number,
    ## system call parameter, and a pointer to the trap frame. User-mode already
    ## set up the first two parameters in R0 and R1, and they were preserved
    ## throughout the context save process here. So all that's left is to put a
    ## pointer to the trap frame in R2.
    ##

    mov     %r2, %sp
    bl      KeSystemCallHandler

    ##
    ## Restore state and return.
    ##

    ARM_EXIT_INTERRUPT

END_FUNCTION ArpSoftwareInterruptEntry

##
## VOID
## ArpPrefetchAbortEntry (
##     VOID
##     )
##

/*++

Routine Description:

    This routine directly handles an exception generated by a prefetch abort
    (page fault).

Arguments:

    None.

Return Value:

    None.

--*/

FUNCTION ArpPrefetchAbortEntry

    ##
    ## In ARM mode, prefect aborts save the PC-4 in LR. For Thumb mode, data
    ## aborts save the PC in LR. Thus, the PC is always 4 bytes head of the
    ## faulting address.
    ##

    sub     %lr, %lr, #4                @ Prefetches go too far by 4.

    ##
    ## Save state and create a trap frame.
    ##

    ARM_ENTER_INTERRUPT

    ##
    ## Call the main dispatch routine routine with a pointer to the trap frame
    ## and 1 to indicate a prefetch abort.
    ##

    mov     %r0, %sp
    mov     %r1, #1
    blx     KeDispatchException

    ##
    ## Restore state and return.
    ##

    ARM_EXIT_INTERRUPT

END_FUNCTION ArpPrefetchAbortEntry

##
## VOID
## ArpDataAbortEntry (
##     VOID
##     )
##

/*++

Routine Description:

    This routine directly handles an exception generated by a data abort (page
    fault).

Arguments:

    None.

Return Value:

    None.

--*/

FUNCTION ArpDataAbortEntry

    ##
    ## In ARM mode, data aborts save the PC in LR. For Thumb mode, data aborts
    ## save the PC+4 in LR. Thus, the PC is always 8 bytes head of the faulting
    ## address.
    ##

    sub     %lr, %lr, #8

    ##
    ## Save state and create a trap frame.
    ##

    ARM_ENTER_INTERRUPT

    ##
    ## Call the main dispatch routine routine with a pointer to the trap frame
    ## and 0 to indicate a prefetch abort.
    ##

    mov     %r0, %sp
    mov     %r1, #0
    blx     KeDispatchException

    ##
    ## Restore state and return.
    ##

    ARM_EXIT_INTERRUPT

END_FUNCTION ArpDataAbortEntry

##
## VOID
## ArpIrqEntry (
##     VOID
##     )
##

/*++

Routine Description:

    This routine directly handles an exception generated by an external
    interrupt on the IRQ pin.

Arguments:

    None.

Return Value:

    None.

--*/

FUNCTION ArpIrqEntry
    b       ArpCommonInterruptEntry

END_FUNCTION ArpIrqEntry

##
## VOID
## ArpFiqEntry (
##     VOID
##     )
##

/*++

Routine Description:

    This routine directly handles an exception generated by an external
    interrupt on the FIQ pin.

Arguments:

    None.

Return Value:

    None.

--*/

FUNCTION ArpFiqEntry
    b       ArpCommonInterruptEntry

END_FUNCTION ArpFiqEntry

##
## --------------------------------------------------------- Internal Functions
##

##
## This code is entered as the result of any interrupt or exception. Its job is
## to transition back to the SVC stack and then call the real interrupt
## dispatch routine.
##

ArpCommonInterruptEntry:

    ##
    ## Save state and create a trap frame.
    ##

    ARM_ENTER_INTERRUPT

    ##
    ## Call the main dispatch routine routine with a pointer to the trap frame
    ## as the only parameter. Align the stack down in case the exception
    ## interrupted something unaligned.
    ##

    mov     %r0, %sp
    mov     %r4, %sp
    and     %r5, %r4, #0xFFFFFFF0
    mov     %sp, %r5
    bl      KeDispatchException
    mov     %sp, %r4

    ##
    ## Restore state and return.
    ##

    ARM_EXIT_INTERRUPT

