~aleteoryx/muditaos

202dd9306402a91c309aaf30806559330ee06fba — Artur Śleszyński 4 years ago 606e059
[CP-261] Print crash occurences to log

Make sure we produce log entries when crashes occur.
M CMakeLists.txt => CMakeLists.txt +0 -2
@@ 113,8 113,6 @@ add_subdirectory(third-party)

if (${PROJECT_TARGET} STREQUAL "TARGET_Linux")
    add_subdirectory(board/linux/libiosyscalls)
elseif(${PROJECT_TARGET} STREQUAL "TARGET_RT1051")
    add_subdirectory(board/rt1051)
endif()

add_subdirectory(source)

D board/rt1051/CMakeLists.txt => board/rt1051/CMakeLists.txt +0 -13
@@ 1,13 0,0 @@
add_library(CrashCatcherARM STATIC)

target_sources(CrashCatcherARM
    PRIVATE
        crashdump/CrashCatcher_armv7m.S
)

set_source_files_properties(crashdump/CrashCatcher_armv7m.S PROPERTIES LANGUAGE C)

target_link_libraries(CrashCatcherARM
    PRIVATE
        CrashCatcher::CrashCatcher
)

M board/rt1051/crashdump/consoledump.cpp => board/rt1051/crashdump/consoledump.cpp +23 -13
@@ 120,7 120,7 @@ struct __attribute__((packed)) abfsr_t

struct __attribute__((packed)) syslog_t
{
    syslog_exception_stack_frame_t stackFrame;
    CrashCatcherStackedRegisters stackFrame;
    struct
    {
        hfsr_t hfsr;         /// Hard Fault Fault Status Register


@@ 134,9 134,9 @@ struct __attribute__((packed)) syslog_t

namespace
{
    static void printHardFaultInfo(const syslog_t &syslog)
    void printCrashInfo(const syslog_t &syslog)
    {
        LOG_FATAL("!!! HardFault Detected !!!");
        LOG_FATAL("!!! Crash Occured !!!");
        LOG_FATAL("* Stack Frame:");
        LOG_FATAL("R0  = 0x%0" PRIX32, syslog.stackFrame.r0);
        LOG_FATAL("R1  = 0x%0" PRIX32, syslog.stackFrame.r1);


@@ 225,16 225,26 @@ namespace
        if (abfsr.eppb)
            LOG_FATAL(" - EPPB: Asynchronous fault on EPPB interface");
    }

    syslog_t syslogFrom(const CrashCatcherInfo *info)
    {
        syslog_t syslog;

        syslog.stackFrame          = *info->pSP;
        syslog.registers.hfsr.all  = SCB->HFSR;
        syslog.registers.cfsr.all  = SCB->CFSR;
        syslog.registers.mmfar     = SCB->MMFAR;
        syslog.registers.bfar      = SCB->BFAR;
        syslog.registers.abfsr.all = SCB->ABFSR;

        return syslog;
    }
} // namespace

extern "C" void crashCatcherExtPrintConsoleHF(uint32_t sp)
namespace crashdump
{
    static syslog_t syslog     = {};
    syslog.stackFrame          = *(reinterpret_cast<syslog_exception_stack_frame_t *>(sp));
    syslog.registers.hfsr.all  = SCB->HFSR;
    syslog.registers.cfsr.all  = SCB->CFSR;
    syslog.registers.mmfar     = SCB->MMFAR;
    syslog.registers.bfar      = SCB->BFAR;
    syslog.registers.abfsr.all = SCB->ABFSR;
    printHardFaultInfo(syslog);
}
    void printCrashInfo(const CrashCatcherInfo *info)
    {
        ::printCrashInfo(syslogFrom(info));
    }
} // namespace crashdump

M board/rt1051/crashdump/consoledump.hpp => board/rt1051/crashdump/consoledump.hpp +5 -15
@@ 3,20 3,10 @@

#pragma once

#include <cstdint>
#include <CrashCatcher/CrashCatcher.h>

// Exception Stack Frame
typedef struct __attribute__((packed))
namespace crashdump
{
    uint32_t r0;
    uint32_t r1;
    uint32_t r2;
    uint32_t r3;
    uint32_t r12;
    uint32_t lr;
    uint32_t pc;
    uint32_t psr;
#if defined(__ARM_ARCH_7EM__)
    uint32_t s[16];
#endif
} syslog_exception_stack_frame_t;
    void printCrashInfo(const CrashCatcherInfo *info);

} // namespace crashdump

M board/rt1051/crashdump/crashcatcher_impl.cpp => board/rt1051/crashdump/crashcatcher_impl.cpp +3 -1
@@ 3,11 3,12 @@

#pragma GCC optimize("Og")

#include <CrashCatcher.h>
#include <CrashCatcher/CrashCatcher.h>

#include <log.hpp>
#include <date/date.h>
#include "crashdumpwriter.hpp"
#include "consoledump.hpp"

const CrashCatcherMemoryRegion *CrashCatcher_GetMemoryRegions(void)
{


@@ 33,6 34,7 @@ const CrashCatcherMemoryRegion *CrashCatcher_GetMemoryRegions(void)

void CrashCatcher_DumpStart(const CrashCatcherInfo *pInfo)
{
    crashdump::printCrashInfo(pInfo);
    crashdump::CrashDumpWriter::instance().openDump();
}


M products/PurePhone/CMakeLists.txt => products/PurePhone/CMakeLists.txt +0 -1
@@ 73,7 73,6 @@ target_link_libraries(PurePhone
        ${LWIP_LIBRARIES}
        "$<$<STREQUAL:${PROJECT_TARGET},TARGET_Linux>:iosyscalls>"
        "$<$<STREQUAL:${PROJECT_TARGET},TARGET_RT1051>:CrashCatcher::CrashCatcher>"
        "$<$<STREQUAL:${PROJECT_TARGET},TARGET_RT1051>:CrashCatcherARM>"
    )

target_link_options(PurePhone PUBLIC ${TARGET_LINK_OPTIONS})

M third-party/CrashDebug/CMakeLists.txt => third-party/CrashDebug/CMakeLists.txt +1 -10
@@ 7,13 7,4 @@ ExternalProject_Add(CrashDebug
    INSTALL_COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/bin && ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/src/bins/lin64/CrashDebug ${CMAKE_CURRENT_BINARY_DIR}/bin
)

add_library(CrashCatcher INTERFACE)
add_library(CrashCatcher::CrashCatcher ALIAS CrashCatcher)

target_include_directories(CrashCatcher
    INTERFACE
        src/CrashCatcher/include
        src/CrashCatcher/Core/src
)

target_sources(CrashCatcher INTERFACE src/CrashCatcher/Core/src/CrashCatcher.c)
add_subdirectory(CrashCatcher)

A third-party/CrashDebug/CrashCatcher/CMakeLists.txt => third-party/CrashDebug/CrashCatcher/CMakeLists.txt +32 -0
@@ 0,0 1,32 @@
add_library(CrashCatcher OBJECT)
add_library(CrashCatcher::CrashCatcher ALIAS CrashCatcher)

# These files were copied from 'third-party/CrashDebug/src/CrashCatcher' and modified.
#
# Reasons for modification:
# * CrashCatcher_armv7m.S: need to enter MCU user mode in order to create a file dump.
#
# * CrashCatcherPriv.h
#   CrashCatcher.c
#   CrashCatcher.h    : need to expose MCU registers' state for console dump.

target_sources(
        CrashCatcher
    PRIVATE
        CrashCatcherPriv.h
        CrashCatcher.c
        CrashCatcher_armv7m.S
    PUBLIC
        include/CrashCatcher/CrashCatcher.h
)

set_source_files_properties(
        CrashCatcher_armv7m.S
    PROPERTIES LANGUAGE C
)

target_include_directories(
        CrashCatcher
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)

A third-party/CrashDebug/CrashCatcher/CrashCatcher.c => third-party/CrashDebug/CrashCatcher/CrashCatcher.c +284 -0
@@ 0,0 1,284 @@
/* Copyright (C) 2018  Adam Green (https://github.com/adamgreen)

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
#include <CrashCatcher/CrashCatcher.h>
#include "CrashCatcherPriv.h"
#include <string.h>


/* Test harness will define this value on 64-bit machine to provide upper 32-bits of pointer addresses. */
CRASH_CATCHER_TEST_WRITEABLE uint64_t g_crashCatcherTestBaseAddress;

/* The unit tests can point the core to a fake location for the SCB->CPUID register. */
CRASH_CATCHER_TEST_WRITEABLE uint32_t* g_pCrashCatcherCpuId = (uint32_t*)0xE000ED00;

/* The unit tests can point the core to a fake location for the fault status registers. */
CRASH_CATCHER_TEST_WRITEABLE uint32_t* g_pCrashCatcherFaultStatusRegisters = (uint32_t*)0xE000ED28;

/* The unit tests can point the core to a fake location for the Coprocessor Access Control Register. */
CRASH_CATCHER_TEST_WRITEABLE uint32_t* g_pCrashCatcherCoprocessorAccessControlRegister = (uint32_t*)0xE000ED88;


/* Fault handler will switch MSP to use this area as the stack while CrashCatcher code is running.
   NOTE: If you change the size of this buffer, it also needs to be changed in the HardFault_Handler (in
         FaultHandler_arm*.S) when initializing the stack pointer. */
uint32_t g_crashCatcherStack[CRASH_CATCHER_STACK_WORD_COUNT];


typedef struct
{
    const CrashCatcherExceptionRegisters* pExceptionRegisters;
    uint32_t                              flags;
    CrashCatcherInfo                      info;
} Object;


static Object initStackPointers(const CrashCatcherExceptionRegisters* pExceptionRegisters);
static uint32_t getAddressOfExceptionStack(const CrashCatcherExceptionRegisters* pExceptionRegisters);
static void* uint32AddressToPointer(uint32_t address);
static void advanceStackPointerToValueBeforeException(Object* pObject);
static int areFloatingPointRegistersAutoStacked(const Object* pObject);
static void initFloatingPointFlag(Object* pObject);
static int areFloatingPointCoprocessorsEnabled(void);
static void initIsBKPT(Object* pObject);
static int isBKPT(uint16_t instruction);
static void setStackSentinel(void);
static void dumpSignature(void);
static void dumpFlags(const Object* pObject);
static void dumpR0toR3(const Object* pObject);
static void dumpR4toR11(const Object* pObject);
static void dumpR12(const Object* pObject);
static void dumpSP(const Object* pObject);
static void dumpLR_PC_PSR(const Object* pObject);
static void dumpMSPandPSPandExceptionPSR(const Object* pObject);
static void dumpFloatingPointRegisters(const Object* pObject);
static void dumpMemoryRegions(const CrashCatcherMemoryRegion* pRegion);
static void checkStackSentinelForStackOverflow(void);
static int isARMv6MDevice(void);
static void dumpFaultStatusRegisters(void);
static void advanceProgramCounterPastHardcodedBreakpoint(const Object* pObject);


void CrashCatcher_Entry(const CrashCatcherExceptionRegisters* pExceptionRegisters)
{
    Object object = initStackPointers(pExceptionRegisters);
    advanceStackPointerToValueBeforeException(&object);
    initFloatingPointFlag(&object);
    initIsBKPT(&object);

    do
    {
        setStackSentinel();
        CrashCatcher_DumpStart(&object.info);
        dumpSignature();
        dumpFlags(&object);
        dumpR0toR3(&object);
        dumpR4toR11(&object);
        dumpR12(&object);
        dumpSP(&object);
        dumpLR_PC_PSR(&object);
        dumpMSPandPSPandExceptionPSR(&object);
        if (object.flags & CRASH_CATCHER_FLAGS_FLOATING_POINT)
            dumpFloatingPointRegisters(&object);
        dumpMemoryRegions(CrashCatcher_GetMemoryRegions());
        if (!isARMv6MDevice())
            dumpFaultStatusRegisters();
        checkStackSentinelForStackOverflow();
    }
    while (CrashCatcher_DumpEnd() == CRASH_CATCHER_TRY_AGAIN);

    advanceProgramCounterPastHardcodedBreakpoint(&object);
}

static Object initStackPointers(const CrashCatcherExceptionRegisters* pExceptionRegisters)
{
    Object object;
    object.pExceptionRegisters = pExceptionRegisters;
    object.info.sp = getAddressOfExceptionStack(pExceptionRegisters);
    object.info.pSP            = uint32AddressToPointer(object.info.sp);
    object.flags = 0;
    return object;
}

static uint32_t getAddressOfExceptionStack(const CrashCatcherExceptionRegisters* pExceptionRegisters)
{
    if (pExceptionRegisters->exceptionLR & LR_PSP)
        return pExceptionRegisters->psp;
    else
        return pExceptionRegisters->msp;
}

static void* uint32AddressToPointer(uint32_t address)
{
    if (sizeof(uint32_t*) == 8)
        return (void*)(unsigned long)((uint64_t)address | g_crashCatcherTestBaseAddress);
    else
        return (void*)(unsigned long)address;
}

static void advanceStackPointerToValueBeforeException(Object* pObject)
{
    /* Cortex-M processor always push 8 integer registers on the stack. */
    pObject->info.sp += 8 * sizeof(uint32_t);
    /* ARMv7-M processors can also push 16 single-precision floating point registers, FPSCR and a padding word. */
    if (areFloatingPointRegistersAutoStacked(pObject))
        pObject->info.sp += (16 + 1 + 1) * sizeof(uint32_t);
    /* Cortex-M processor may also have had to force 8-byte alignment before auto stacking registers. */
    pObject->info.sp |= (pObject->info.pSP->psr & PSR_STACK_ALIGN) ? 4 : 0;
}

static int areFloatingPointRegistersAutoStacked(const Object* pObject)
{
    return 0 == (pObject->pExceptionRegisters->exceptionLR & LR_FLOAT);
}

static void initFloatingPointFlag(Object* pObject)
{
    if (areFloatingPointCoprocessorsEnabled())
        pObject->flags |= CRASH_CATCHER_FLAGS_FLOATING_POINT;
}

static int areFloatingPointCoprocessorsEnabled(void)
{
    static const uint32_t coProcessor10and11EnabledBits = 5 << 20;
    uint32_t              coprocessorAccessControl = *g_pCrashCatcherCoprocessorAccessControlRegister;

    return (coprocessorAccessControl & (coProcessor10and11EnabledBits)) == coProcessor10and11EnabledBits;
}

static void initIsBKPT(Object* pObject)
{
    const uint16_t *pInstruction = uint32AddressToPointer(pObject->info.pSP->pc);

    pObject->info.isBKPT = isBKPT(*pInstruction);
}

static int isBKPT(uint16_t instruction)
{
    return (instruction & 0xFF00) == 0xBE00;
}

static void setStackSentinel(void)
{
    g_crashCatcherStack[0] = CRASH_CATCHER_STACK_SENTINEL;
}

static void dumpSignature(void)
{
    static const uint8_t signature[4] = {CRASH_CATCHER_SIGNATURE_BYTE0,
                                         CRASH_CATCHER_SIGNATURE_BYTE1,
                                         CRASH_CATCHER_VERSION_MAJOR,
                                         CRASH_CATCHER_VERSION_MINOR};

    CrashCatcher_DumpMemory(signature, CRASH_CATCHER_BYTE, sizeof(signature));
}

static void dumpFlags(const Object* pObject)
{
    CrashCatcher_DumpMemory(&pObject->flags, CRASH_CATCHER_BYTE, sizeof(pObject->flags));
}

static void dumpR0toR3(const Object* pObject)
{
    CrashCatcher_DumpMemory(&pObject->info.pSP->r0, CRASH_CATCHER_BYTE, 4 * sizeof(uint32_t));
}

static void dumpR4toR11(const Object* pObject)
{
    CrashCatcher_DumpMemory(&pObject->pExceptionRegisters->r4, CRASH_CATCHER_BYTE, (11 - 4 + 1) * sizeof(uint32_t));
}

static void dumpR12(const Object* pObject)
{
    CrashCatcher_DumpMemory(&pObject->info.pSP->r12, CRASH_CATCHER_BYTE, sizeof(uint32_t));
}

static void dumpSP(const Object* pObject)
{
    CrashCatcher_DumpMemory(&pObject->info.sp, CRASH_CATCHER_BYTE, sizeof(uint32_t));
}

static void dumpLR_PC_PSR(const Object* pObject)
{
    CrashCatcher_DumpMemory(&pObject->info.pSP->lr, CRASH_CATCHER_BYTE, 3 * sizeof(uint32_t));
}

static void dumpMSPandPSPandExceptionPSR(const Object* pObject)
{
    CrashCatcher_DumpMemory(&pObject->pExceptionRegisters->msp, CRASH_CATCHER_BYTE, 3 * sizeof(uint32_t));
}

static void dumpFloatingPointRegisters(const Object* pObject)
{
    uint32_t allFloatingPointRegisters[32 + 1];
    if (areFloatingPointRegistersAutoStacked(pObject))
    {
        /* Copy the upper floats first as that will cause a lazy copy of the auto-stacked registers. */
        CrashCatcher_CopyUpperFloatingPointRegisters(&allFloatingPointRegisters[16]);
        memcpy(&allFloatingPointRegisters[0], &pObject->info.pSP->floats, sizeof(pObject->info.pSP->floats));
        allFloatingPointRegisters[32] = pObject->info.pSP->fpscr;
    }
    else
    {
        CrashCatcher_CopyAllFloatingPointRegisters(allFloatingPointRegisters);
    }
    CrashCatcher_DumpMemory(allFloatingPointRegisters, CRASH_CATCHER_BYTE, sizeof(allFloatingPointRegisters));
}

static void dumpMemoryRegions(const CrashCatcherMemoryRegion* pRegion)
{
    while (pRegion && pRegion->startAddress != 0xFFFFFFFF)
    {
        /* Just dump the two addresses in pRegion.  The element size isn't required. */
        CrashCatcher_DumpMemory(pRegion, CRASH_CATCHER_BYTE, 2 * sizeof(uint32_t));
        CrashCatcher_DumpMemory(uint32AddressToPointer(pRegion->startAddress),
                                pRegion->elementSize,
                                (pRegion->endAddress - pRegion->startAddress) / pRegion->elementSize);
        pRegion++;
    }
}

static void checkStackSentinelForStackOverflow(void)
{
    if (g_crashCatcherStack[0] != CRASH_CATCHER_STACK_SENTINEL)
    {
        uint8_t value[4] = {0xAC, 0xCE, 0x55, 0xED};
        CrashCatcher_DumpMemory(value, CRASH_CATCHER_BYTE, sizeof(value));
    }
}

static int isARMv6MDevice(void)
{
    static const uint32_t armv6mArchitecture = 0xC << 16;
    uint32_t              cpuId = *g_pCrashCatcherCpuId;
    uint32_t              architecture = cpuId & (0xF << 16);

    return (architecture == armv6mArchitecture);
}

static void dumpFaultStatusRegisters(void)
{
    uint32_t                 faultStatusRegistersAddress = (uint32_t)(unsigned long)g_pCrashCatcherFaultStatusRegisters;
    CrashCatcherMemoryRegion faultStatusRegion[] = { {faultStatusRegistersAddress,
                                                      faultStatusRegistersAddress + 5 * sizeof(uint32_t),
                                                      CRASH_CATCHER_WORD},
                                                     {0xFFFFFFFF, 0xFFFFFFFF, CRASH_CATCHER_BYTE} };
    dumpMemoryRegions(faultStatusRegion);
}

static void advanceProgramCounterPastHardcodedBreakpoint(const Object* pObject)
{
    if (pObject->info.isBKPT)
        pObject->info.pSP->pc += 2;
}

A third-party/CrashDebug/CrashCatcher/CrashCatcherPriv.h => third-party/CrashDebug/CrashCatcher/CrashCatcherPriv.h +84 -0
@@ 0,0 1,84 @@
/* Copyright (C) 2017  Adam Green (https://github.com/adamgreen)

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
/* Private header file shared with unit tests. */
#ifndef _CRASH_CATCHER_PRIV_H_
#define _CRASH_CATCHER_PRIV_H_


/* Definitions used by assembly language and C code. */
#define CRASH_CATCHER_STACK_WORD_COUNT 125

/* Does this device support THUMB instructions for FPU access? */
#ifdef __ARM_ARCH_7EM__
#define CRASH_CATCHER_WITH_FPU 1
#else
#define CRASH_CATCHER_WITH_FPU 0
#endif


/* Definitions only required from C code. */
#if !defined(__ASSEMBLER__) || (!__ASSEMBLER__)

#include <stdint.h>


/* Bit in LR to indicate whether PSP was used for automatic stacking of registers during exception entry. */
#define LR_PSP (1 << 2)

/* Bit in LR set to 0 when automatic stacking of floating point registers occurs during exception handling. */
#define LR_FLOAT (1 << 4)


/* Bit in auto stacked xPSR which indicates whether stack was force 8-byte aligned. */
#define PSR_STACK_ALIGN (1 << 9)

/* This structure is filled in by the Hard Fault exception handler (or unit test) and then passed in as a parameter to
   CrashCatcher_Entry(). */
typedef struct
{
    uint32_t msp;
    uint32_t psp;
    uint32_t exceptionPSR;
    uint32_t r4;
    uint32_t r5;
    uint32_t r6;
    uint32_t r7;
    uint32_t r8;
    uint32_t r9;
    uint32_t r10;
    uint32_t r11;
    uint32_t exceptionLR;
} CrashCatcherExceptionRegisters;

/* This is the area of memory that would normally be used for the stack when running on an actual Cortex-M
   processor.  Unit tests can write to this buffer to simulate stack overflow. */
extern uint32_t g_crashCatcherStack[CRASH_CATCHER_STACK_WORD_COUNT];


/* The main entry point into CrashCatcher.  Is called from the HardFault exception handler and unit tests. */
void CrashCatcher_Entry(const CrashCatcherExceptionRegisters* pExceptionRegisters);

/* Called from CrashCatcher core to copy all floating point registers to supplied buffer. The supplied buffer must be
   large enough to contain 33 32-bit values (S0-S31 & FPCSR). */
void CrashCatcher_CopyAllFloatingPointRegisters(uint32_t* pBuffer);

/* Called from CrashCatcher core to copy upper 16 floating point registers to supplied buffer. The supplied buffer must be
   large enough to contain 16 32-bit values (S16-S31). */
void CrashCatcher_CopyUpperFloatingPointRegisters(uint32_t* pBuffer);

#endif // #if !defined(__ASSEMBLER__) || (!__ASSEMBLER__)


#endif /* _CRASH_CATCHER_PRIV_H_ */

R board/rt1051/crashdump/CrashCatcher_armv7m.S => third-party/CrashDebug/CrashCatcher/CrashCatcher_armv7m.S +2 -11
@@ 53,21 53,12 @@ HardFault_Handler:
    mrs     r1, msp
    ldr     sp, =(g_crashCatcherStack + 4 * CRASH_CATCHER_STACK_WORD_COUNT)
    push.w  {r1-r11,lr}
    // Call oryginal console dump

    // Call original console dump
    ldr r0,=crash_orig_stack
    str sp, [r0]
    mov sp, r1

    // Call console crash handler
    mrs r0, msp
    mov sp, r0
    tst lr, #4
    ite eq
    mrseq r0,msp
    mrsne r0,psp
    push {lr}
    bl crashCatcherExtPrintConsoleHF
    pop {lr}
    ldr r0,=crash_orig_stack
    ldr sp, [r0]


A third-party/CrashDebug/CrashCatcher/include/CrashCatcher/CrashCatcher.h => third-party/CrashDebug/CrashCatcher/include/CrashCatcher/CrashCatcher.h +176 -0
@@ 0,0 1,176 @@
/* Copyright (C) 2018  Adam Green (https://github.com/adamgreen)

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
#ifndef _CRASH_CATCHER_H_
#define _CRASH_CATCHER_H_


#include <stdint.h>
#include <stdlib.h>


#define TRUE  1
#define FALSE 0


/* The crash dump start with a four byte header.  The first two bytes are "cC", the third byte is the major version
   number, and the fourth bytes is the minor version number. */
#define CRASH_CATCHER_SIGNATURE_BYTE0 'c'
#define CRASH_CATCHER_SIGNATURE_BYTE1 'C'
#define CRASH_CATCHER_VERSION_MAJOR   3
#define CRASH_CATCHER_VERSION_MINOR   0

/* The second word of the dump contains flags.  These are the allowed flags. */
/* Flag to indicate that 32 single-precision floating point registers and FPSCR will follow integer registers. */
#define CRASH_CATCHER_FLAGS_FLOATING_POINT (1 << 0)


/* This magic value will be found as the last word in a crash dump if the fault handler overflowed the stack while
   generating the crash dump. */
#define CRASH_CATCHER_STACK_SENTINEL 0xACCE55ED

/* This structure contains the integer registers that are automatically stacked by Cortex-M processor when it enters
   an exception handler. */
typedef struct
{
    uint32_t r0;
    uint32_t r1;
    uint32_t r2;
    uint32_t r3;
    uint32_t r12;
    uint32_t lr;
    uint32_t pc;
    uint32_t psr;
    /* The following floating point registers are only stacked when the LR_FLOAT bit is set in exceptionLR. */
    uint32_t floats[16];
    uint32_t fpscr;
    uint32_t reserved; /* keeps 8-byte alignment */
} CrashCatcherStackedRegisters;

/* Particulars of crash provided to CrashCatcher_DumpStart(). */
typedef struct
{
    /* The SP, Stack Pointer, contained this value at the time of the fault. */
    uint32_t    sp;
    CrashCatcherStackedRegisters *pSP;
    /* Was this fault actually just a hardcoded breakpoint from which it is safe to continue. */
    int         isBKPT;
} CrashCatcherInfo;


/* The crash dump will have one of these entries for each memory region included in the dump file. */
typedef struct
{
    uint32_t                 startAddress;
    uint32_t                 endAddress;
} CrashCatcherMemoryRegionInfo;

/* Supported element sizes to be used with CrashCatcher_DumpMemory calls. */
typedef enum
{
    CRASH_CATCHER_BYTE = 1,
    CRASH_CATCHER_HALFWORD = 2,
    CRASH_CATCHER_WORD = 4
} CrashCatcherElementSizes;


/* Codes to be returned from an implementation's CrashCathcer_DumpEnd() handler. */
typedef enum
{
    /* Crash Catcher should loop around and try dumping again incase user missed it previous time. */
    CRASH_CATCHER_TRY_AGAIN = 0,
    /* Crash Catcher should exit and return to caller.  This is used during unit testing or if the crash cause was 
       actually a hardcoded breakpoint that should be logged and then execution continued. */
    CRASH_CATCHER_EXIT
} CrashCatcherReturnCodes;


/* An array of these structures is returned from CrashCatcher_GetMemoryRegions() to indicate what regions of memory
   should be dumped as part of the crash dump.  The last entry should contain a starting address of 0xFFFFFFFF to
   indicate that the end of the list has been encountered. */
typedef struct
{
    /* The first address of the element to be dumped for this region of memory. */
    /* The last region in the array return from CrashCatcher_GetMemoryRegions() must set this to 0xFFFFFFFF */
    uint32_t                 startAddress;
    /* Stop dumping the region once this address is encountered.  The dump isn't inclusive of this address. */
    /* It must be greater than startAddress. */
    uint32_t                 endAddress;
    /* This should be set to CRASH_CATCHER_BYTE except for peripheral registers which don't support 8-bit reads. */
    CrashCatcherElementSizes elementSize;
} CrashCatcherMemoryRegion;


#ifdef __cplusplus
extern "C"
{
#endif
/* The following functions must be provided by a specific dumping implementation.  The Core CrashCatcher calls these
   routines to have an implementation dump the bytes associated with the current crash. */

/* Called at the beginning of crash dump. You should provide an implementation which prepares for the dump by opening
   a dump file, prompting the user to begin a crash dump, or whatever makes sense for your scenario. */
void CrashCatcher_DumpStart(const CrashCatcherInfo* pInfo);

/* Called to obtain an array of regions in memory that should be dumped as part of the crash.  This will typically
   be all RAM regions that contain volatile data.  For some crash scenarios, a user may decide to also add peripheral
   registers of interest (ie. dump some ethernet registers when you are encountering crashes in the network stack.)
   If NULL is returned from this function, the core will only dump the registers. */
const CrashCatcherMemoryRegion* CrashCatcher_GetMemoryRegions(void);

/* Called to dump the next chunk of memory to the dump (this memory may point to register contents which has been copied
   to memory by CrashCatcher already.  The element size will be 8-bits, 16-bits, or 32-bits.  The implementation should
   use reads of the specified size since some memory locations may only support the indicated size. */
void CrashCatcher_DumpMemory(const void* pvMemory, CrashCatcherElementSizes elementSize, size_t elementCount);

/* Called at the end of crash dump. You should provide an implementation which cleans up at the end of dump. This could
   include closing a dump file, blinking LEDs, infinite looping, and/or returning CRASH_CATCHER_TRY_AGAIN if
   CrashCatcher should prepare to dump again incase user missed the first attempt. */
CrashCatcherReturnCodes CrashCatcher_DumpEnd(void);


/* The following functions must be provided by a hex dumping implementation. Such implementations will also have to
   implement the core CrashCatcher_GetMemoryRegions() API as well.  The HexDump version of CrashCatcher calls these
   routines to have an implementation query the user when they are ready for the dump to start and actually dump the
   hex data to the user a character at a time. */

/* Called to receive a character of data from the user.  Typically this is in response to a "Press any key" type of
   prompt to the user.  This function should be blocking. */
int CrashCatcher_getc(void);

/* Called to send a character of hex dump data to the user. */
void CrashCatcher_putc(int c);

#ifdef __cplusplus
}
#endif


/* Macros which can generate a few forms of crashes. */
#define CRASH_CATCHER_READ_FAULT()          (*(volatile unsigned int*)0xFFFFFFF0)
#define CRASH_CATCHER_WRITE_FAULT()         (*(volatile unsigned int*)0xFFFFFFF0 = 0x0)
#define CRASH_CATCHER_INVALID_INSTRUCTION() { __asm volatile (".word 0xDE00"); }

/* Macro used to insert hardcoded breakpoint into user's code. */
#define CRASH_CATCHER_BREAKPOINT()          { __asm volatile ("bkpt #0"); }

/* Macro used to make some globals writeable from unit tests but constant when running on ARM processors. */
#ifdef RUNNING_HOST_TESTS
    #define CRASH_CATCHER_TEST_WRITEABLE
#else
    #define CRASH_CATCHER_TEST_WRITEABLE static const
#endif


#endif /* _CRASH_CATCHER_H_ */