/* * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved. * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "los_backtrace.h" #include "los_task.h" #include "los_debug.h" #include "los_arch.h" #if (LOSCFG_BACKTRACE_TYPE == 4) #include "los_arch_regs.h" #endif #if (LOSCFG_BACKTRACE_TYPE != 0) /* This function is used to judge whether the data in the stack is a code section address. The default code section is only one, but there may be more than one. Modify the judgment condition to support multiple code sections. */ WEAK BOOL OsStackDataIsCodeAddr(UINTPTR value) { if ((value > CODE_START_ADDR) && (value < CODE_END_ADDR)) { return TRUE; } return FALSE; } #if (LOSCFG_BACKTRACE_TYPE == 1) #define OS_BACKTRACE_START 2 /* Thumb instruction, so the pc must be an odd number */ #define OS_IS_THUMB_INSTRUCTION(pc) ((pc & 0x1) == 1) /* BL or BLX instruction flag. */ #define OS_BL_INS_MASK 0xF800 #define OS_BL_INS_HIGH 0xF800 #define OS_BL_INS_LOW 0xF000 #define OS_BLX_INX_MASK 0xFF00 #define OS_BLX_INX 0x4700 STATIC INLINE BOOL OsInsIsBlOrBlx(UINTPTR addr) { UINT16 ins1 = *((UINT16 *)addr); UINT16 ins2 = *((UINT16 *)(addr + 2)); /* 2: Thumb instruction is two bytes. */ if (((ins2 & OS_BL_INS_MASK) == OS_BL_INS_HIGH) && ((ins1 & OS_BL_INS_MASK) == OS_BL_INS_LOW)) { return TRUE; } else if ((ins2 & OS_BLX_INX_MASK) == OS_BLX_INX) { return TRUE; } else { return FALSE; } } STATIC INLINE UINT32 OsStackAddrGet(UINTPTR *stackStart, UINTPTR *stackEnd, UINTPTR SP) { if (SP != 0) { *stackStart = SP; if ((SP >= CSTACK_START_ADDR) && (SP < CSTACK_END_ADDR)) { *stackEnd = CSTACK_END_ADDR; } else { UINT32 taskID = LOS_CurTaskIDGet(); LosTaskCB *taskCB = OS_TCB_FROM_TID(taskID); *stackEnd = (UINTPTR)taskCB->topOfStack + taskCB->stackSize; if ((SP < (UINTPTR)taskCB->topOfStack) || (SP >= *stackEnd)) { PRINT_ERR("msp statck [0x%x, 0x%x], cur task stack [0x%x, 0x%x], cur sp(0x%x) is overflow!\n", CSTACK_START_ADDR, CSTACK_END_ADDR, (UINTPTR)taskCB->topOfStack, *stackEnd, SP); return LOS_NOK; } } } else { if (ArchSpGet() != ArchPspGet()) { *stackStart = ArchMspGet(); *stackEnd = CSTACK_END_ADDR; if ((*stackStart < CSTACK_START_ADDR) || (*stackStart >= CSTACK_END_ADDR)) { PRINT_ERR("msp stack [0x%x, 0x%x], cur sp(0x%x) is overflow!\n", CSTACK_START_ADDR, CSTACK_END_ADDR, *stackStart); return LOS_NOK; } PRINTK("msp, start = %x, end = %x\n", *stackStart, *stackEnd); } else { *stackStart = ArchPspGet(); UINT32 taskID = LOS_CurTaskIDGet(); LosTaskCB *taskCB = OS_TCB_FROM_TID(taskID); *stackEnd = (UINTPTR)taskCB->topOfStack + taskCB->stackSize; if ((*stackStart < (UINTPTR)taskCB->topOfStack) || (*stackStart >= *stackEnd)) { PRINT_ERR("psp stack [0x%x, 0x%x], cur sp(0x%x) is overflow, cur task id is %d!\n", taskCB->topOfStack, *stackEnd, *stackStart, taskID); return LOS_NOK; } PRINTK("psp, start = %x, end = %x\n", *stackStart, *stackEnd); } } return LOS_OK; } STATIC INLINE UINTPTR OsAddrIsValid(UINTPTR sp) { UINTPTR pc; BOOL ret; /* The stack space pointed to by the current SP may store the LR, so need decrease a word to PC. */ pc = *((UINTPTR *)sp) - sizeof(UINTPTR); if (!OS_IS_THUMB_INSTRUCTION(pc)) { return 0; } /* PC in thumb mode is an odd number, fix the PC address by decreasing one byte. */ pc = *((UINTPTR *)sp) - 1; ret = OsStackDataIsCodeAddr(pc); if (ret == FALSE) { return 0; } ret = OsInsIsBlOrBlx(pc - sizeof(UINTPTR)); if (ret == FALSE) { return 0; } return pc; } #elif (LOSCFG_BACKTRACE_TYPE == 2) #define OS_BACKTRACE_START 1 #define OS_RA_OFFSET 4 #define OS_FP_OFFSET 8 #define OS_FP_ALIGN(value) (((UINT32)(value) & (UINT32)(LOSCFG_STACK_POINT_ALIGN_SIZE - 1)) == 0) STATIC INLINE UINTPTR OsFpGet(VOID) { UINTPTR fp = 0; __asm volatile("mv %0, s0" : "=r"(fp)); dsb(); return fp; } WEAK BOOL IsValidFP(UINTPTR fp) { LosTaskCB *taskCB = NULL; UINTPTR stackTop, stackBottom; UINTPTR irqStackTop, irqStackBottom; if ((fp == FP_INIT_VALUE) || !OS_FP_ALIGN(fp)) { return FALSE; } if (LOS_TaskIsRunning()) { taskCB = OS_TCB_FROM_TID(LOS_CurTaskIDGet()); stackTop = taskCB->topOfStack; stackBottom = taskCB->topOfStack + taskCB->stackSize; irqStackTop = (UINTPTR)CSTACK_START_ADDR; irqStackBottom = (UINTPTR)CSTACK_SECTION_END; } else { stackTop = 0; stackBottom = 0; irqStackTop = (UINTPTR)CSTACK_START_ADDR; irqStackBottom = (UINTPTR)CSTACK_SECTION_END; } if (((fp > stackTop) && (fp <= stackBottom)) || ((fp > irqStackTop) && (fp <= irqStackBottom))) { return TRUE; } return FALSE; } VOID LOS_RecordLR(UINTPTR *LR, UINT32 LRSize, UINT32 jumpCount, UINTPTR SP) { UINTPTR backFp; UINTPTR tmpFp; UINTPTR backRa; UINT32 count = 0; UINT32 index = 0; if (LR == NULL) { return; } if (SP != 0) { backFp = SP; } else { backFp = OsFpGet(); } if (!IsValidFP(backFp)) { PRINT_ERR("BackTrace failed! Invalid fp 0x%x\n", backFp); return; } do { tmpFp = backFp; backRa = *((UINTPTR *)(UINTPTR)(tmpFp - OS_RA_OFFSET)); backFp = *((UINTPTR *)(UINTPTR)(tmpFp - OS_FP_OFFSET)); if (index++ < jumpCount) { continue; } LR[count] = backRa; count++; if ((count == LRSize) || (backFp == tmpFp)) { break; } } while (IsValidFP(backFp)); if (count < LRSize) { LR[count] = 0; } } #elif (LOSCFG_BACKTRACE_TYPE == 3) #define OS_BACKTRACE_START 1 #define OS_JALX_INS_MASK 0x7F #define OS_JAL_INS_LOW 0x6F #define OS_JAL_16_INS_MASK 0x2001 #define OS_JALR_INS_LOW 0x67 #define OS_JALR_16_INS_MASK 0x9002 #define OS_JR_16_INS_MASK 0x8002 #define OS_J_16_INS_MASK 0xA001 STATIC INLINE BOOL OsInsIsJump(UINTPTR addr) { UINT16 ins1 = *((UINT16 *)addr); UINT16 ins2 = *((UINT16 *)(addr + 2)); // 2, for the mask /* Jal ins */ if (((ins1 & OS_JALX_INS_MASK) == OS_JAL_INS_LOW) || ((ins1 & OS_JAL_16_INS_MASK) == OS_JAL_16_INS_MASK) || ((ins2 & OS_JAL_16_INS_MASK) == OS_JAL_16_INS_MASK)) { return TRUE; } /* Jalr ins */ if (((ins1 & OS_JALX_INS_MASK) == OS_JALR_INS_LOW) || ((ins1 & OS_JALR_16_INS_MASK) == OS_JALR_16_INS_MASK) || ((ins2 & OS_JALR_16_INS_MASK) == OS_JALR_16_INS_MASK)) { return TRUE; } /* Jr ins */ if (((ins1 & OS_JR_16_INS_MASK) == OS_JR_16_INS_MASK) || ((ins2 & OS_JR_16_INS_MASK) == OS_JR_16_INS_MASK)) { return TRUE; } /* J ins */ if (((ins1 & OS_J_16_INS_MASK) == OS_J_16_INS_MASK) || ((ins2 & OS_J_16_INS_MASK) == OS_J_16_INS_MASK)) { return TRUE; } return FALSE; } STATIC INLINE UINTPTR OsSpGet(VOID) { UINTPTR sp = 0; __asm volatile("mv %0, sp" : "=r"(sp)); dsb(); return sp; } STATIC INLINE UINT32 OsStackAddrGet(UINTPTR *stackStart, UINTPTR *stackEnd, UINTPTR SP) { if (SP != 0) { *stackStart = SP; if ((SP >= CSTACK_START_ADDR) && (SP < CSTACK_END_ADDR)) { *stackEnd = CSTACK_END_ADDR; } else { UINT32 taskID = LOS_CurTaskIDGet(); LosTaskCB *taskCB = OS_TCB_FROM_TID(taskID); *stackEnd = (UINTPTR)taskCB->topOfStack + taskCB->stackSize; if ((SP < (UINTPTR)taskCB->topOfStack) || (SP >= *stackEnd)) { PRINT_ERR("msp statck [0x%x, 0x%x], cur task stack [0x%x, 0x%x], cur sp(0x%x) is overflow!\n", CSTACK_START_ADDR, CSTACK_END_ADDR, (UINTPTR)taskCB->topOfStack, *stackEnd, SP); return LOS_NOK; } } } else { if (!LOS_TaskIsRunning()) { *stackStart = OsSpGet(); *stackEnd = CSTACK_END_ADDR; if ((*stackStart < CSTACK_START_ADDR) || (*stackStart >= CSTACK_END_ADDR)) { PRINT_ERR("msp stack [0x%x, 0x%x], cur sp(0x%x) is overflow!\n", CSTACK_START_ADDR, CSTACK_END_ADDR, *stackStart); return LOS_NOK; } } else { *stackStart = OsSpGet(); UINT32 taskID = LOS_CurTaskIDGet(); LosTaskCB *taskCB = OS_TCB_FROM_TID(taskID); *stackEnd = (UINTPTR)taskCB->topOfStack + taskCB->stackSize; if ((*stackStart < (UINTPTR)taskCB->topOfStack) || (*stackStart >= *stackEnd)) { PRINT_ERR("psp stack [0x%x, 0x%x], cur sp(0x%x) is overflow, cur task id is %d!\n", taskCB->topOfStack, *stackEnd, *stackStart, taskID); return LOS_NOK; } } } return LOS_OK; } STATIC INLINE UINTPTR OsAddrIsValid(UINTPTR sp) { UINTPTR pc; BOOL ret; pc = *((UINTPTR *)sp); ret = OsStackDataIsCodeAddr(pc); if (ret == FALSE) { return 0; } ret = OsInsIsJump(pc - sizeof(UINTPTR)); if (ret == FALSE) { return 0; } return pc; } #elif (LOSCFG_BACKTRACE_TYPE == 4) #define OS_BACKTRACE_START 0 #define ALIGN_MASK (4 - 1) #define OS_REG_LR_OFFSET (CONTEXT_SIZE - 8) UINT32 IsSpAligned(UINT32 value) { return (value & (UINT32)(ALIGN_MASK)) == 0; } STATIC INLINE UINTPTR HalGetLr(VOID) { UINTPTR regLr; __asm__ __volatile__("mov %0, a0" : "=r"(regLr)); return regLr; } /* This function is used to check sp address. */ BOOL IsValidSP(UINTPTR regSP, UINTPTR start, UINTPTR end) { return (regSP >= start) && (regSP <= end) && IsSpAligned(regSP); } /* This function is used to check return address. */ BOOL IsValidRa(UINTPTR regRA) { regRA &= ~VIR_TEXT_ADDR_MASK; regRA |= TEXT_ADDR_MASK; return OsStackDataIsCodeAddr(regRA); } BOOL FindSuitableStack(UINTPTR regSP, UINTPTR *start, UINTPTR *end) { UINT32 stackStart; UINT32 stackEnd; BOOL found = FALSE; if (LOS_TaskIsRunning()) { UINT32 taskID = LOS_CurTaskIDGet(); LosTaskCB *taskCB = OS_TCB_FROM_TID(taskID); stackStart = taskCB->topOfStack; stackEnd = taskCB->topOfStack + taskCB->stackSize; if (IsValidSP(regSP, stackStart, stackEnd)) { found = TRUE; goto FOUND; } } if (IsValidSP(regSP, CSTACK_START_ADDR, CSTACK_END_ADDR)) { stackStart = CSTACK_START_ADDR; stackEnd = CSTACK_END_ADDR; found = TRUE; goto FOUND; } FOUND: if (found == TRUE) { *start = stackStart; *end = stackEnd; } return found; } UINT32 HalBackTraceGet(UINTPTR sp, UINT32 retAddr, UINTPTR *callChain, UINT32 maxDepth, UINT32 jumpCount) { UINTPTR tmpSp; UINT32 tmpRa; UINTPTR backRa = retAddr; UINTPTR backSp = sp; UINTPTR stackStart; UINT32 stackEnd; UINT32 count = 0; UINT32 index = 0; if (FindSuitableStack(sp, &stackStart, &stackEnd) == FALSE) { PRINTK("sp:0x%x error, backtrace failed!\n", sp); return 0; } while (IsValidSP(backSp, stackStart, stackEnd)) { if (callChain == NULL) { PRINTK("trace%u ra:0x%x sp:0x%x\n", count, (backRa << WINDOW_INCREMENT_SHIFT) >> WINDOW_INCREMENT_SHIFT, backSp); } else { if (index++ < jumpCount) { continue; } backRa &= ~VIR_TEXT_ADDR_MASK; backRa |= TEXT_ADDR_MASK; callChain[count++] = backRa; } tmpRa = backRa; tmpSp = backSp; backRa = *((UINT32 *)(UINTPTR)(tmpSp - RA_OFFSET)); backSp = *((UINT32 *)(UINTPTR)(tmpSp - SP_OFFSET)); if ((tmpRa == backRa) || (backSp == tmpSp) || (count == maxDepth) || !IsValidRa(backRa)) { break; } } return count; } VOID LOS_RecordLR(UINTPTR *LR, UINT32 LRSize, UINT32 jumpCount, UINTPTR SP) { UINTPTR reglr; if (LR == NULL) { return; } if (SP == 0) { __asm__ __volatile__("mov %0, sp" : "=a"(SP) : :); __asm__ __volatile__("mov %0, a0" : "=a"(reglr) : :); } else { reglr = *(UINT32 *)(SP - OS_REG_LR_OFFSET); } HakSpillWindow(); HalBackTraceGet(SP, reglr, LR, LRSize, jumpCount); } #elif (LOSCFG_BACKTRACE_TYPE == 5) #define OS_BACKTRACE_START 0 UINT32 IsAligned(UINT32 val, UINT32 align) { return ((val & (align - 1)) == 0); } STATIC INLINE UINTPTR OsSpGet(VOID) { UINTPTR regSp; __asm__ __volatile__("mov %0, sp" : "=r"(regSp)); return regSp; } /* This function is used to check sp. */ BOOL IsValidSP(UINTPTR regSP, UINTPTR start, UINTPTR end) { return (regSP >= start) && (regSP <= end); } BOOL FindSuitableStack(UINTPTR regSP, UINTPTR *start, UINTPTR *end) { UINT32 stackStart; UINT32 stackEnd; BOOL found = FALSE; if (LOS_TaskIsRunning()) { UINT32 taskID = LOS_CurTaskIDGet(); LosTaskCB *taskCB = OS_TCB_FROM_TID(taskID); stackStart = taskCB->topOfStack; stackEnd = taskCB->topOfStack + taskCB->stackSize; if (IsValidSP(regSP, stackStart, stackEnd)) { found = TRUE; goto FOUND; } } if (IsValidSP(regSP, CSTACK_START_ADDR, CSTACK_END_ADDR)) { stackStart = CSTACK_START_ADDR; stackEnd = CSTACK_END_ADDR; found = TRUE; goto FOUND; } FOUND: if (found == TRUE) { *start = stackStart; *end = stackEnd; } return found; } VOID LOS_RecordLR(UINTPTR *LR, UINT32 LRSize, UINT32 jumpCount, UINTPTR SP) { UINTPTR stackPointer; UINTPTR topOfStack; UINTPTR tmpStack = 0; UINTPTR stackBottom; UINTPTR checkBL; UINT32 count = 0; UINT32 index = 0; if (LR == NULL) { return; } if (SP == 0) { SP = OsSpGet(); } stackPointer = SP; if (FindSuitableStack(stackPointer, &topOfStack, &stackBottom) == FALSE) { return; } while ((stackPointer < stackBottom) && (count < LRSize)) { if (IsValidSP(*(UINT32 *)stackPointer, topOfStack, stackBottom) && OsStackDataIsCodeAddr(*(UINT32 *)(stackPointer + STACK_OFFSET)) && IsAligned(*(UINT32 *)stackPointer, ALGIN_CODE)) { if (tmpStack == *(UINT32 *)stackPointer) { break; } tmpStack = *(UINT32 *)stackPointer; checkBL = *(UINT32 *)(stackPointer + STACK_OFFSET); if (count++ < jumpCount) { continue; } stackPointer = tmpStack; LR[index++] = checkBL; continue; } stackPointer += STACK_OFFSET; } if (index < LRSize) { LR[index] = 0; } } #elif (LOSCFG_BACKTRACE_TYPE == 6) #define OS_BACKTRACE_START 1 #define STACK_OFFSET 4 #define THUMB_OFFSET 2 #define THUMB_BIT 16 #define ARM_ALIGN_CODE 4 #define THUMB_ALIGN_CODE 2 #define BL_CMD_OFFSET 4 #define ARM_BL_MASK 0xEB000000 #define THUMB_BL_MASK 0xF000F000 #define CLEAR_LOW_BIT_MASK 0xFFFFFFFE STATIC INLINE BOOL IsAligned(UINT32 val, UINT32 align) { return ((val & (align - 1)) == 0); } STATIC INLINE UINTPTR OsSpGet(VOID) { UINTPTR SP; __asm volatile("mov %0, sp" : "=r"(SP)); return SP; } STATIC INLINE BOOL IsArmValidLr(UINTPTR lr) { return ((*(UINT32 *)(lr - BL_CMD_OFFSET) & ARM_BL_MASK) == ARM_BL_MASK); } STATIC INLINE BOOL IsThumbValidLr(UINTPTR lr) { lr = (*(UINT16 *)(lr - BL_CMD_OFFSET) << THUMB_BIT) + *(UINT16 *)(lr - THUMB_OFFSET); return ((lr & THUMB_BL_MASK) == THUMB_BL_MASK); } VOID LOS_RecordLR(UINTPTR *LR, UINT32 LRSize, UINT32 jumpCount, UINTPTR SP) { UINT32 count = 0; UINT32 index = 0; LosTaskCB *taskCB = NULL; UINT32 taskID; UINT32 stackStart, stackEnd; UINTPTR framePtr, tmpFramePtr, linkReg; if (LR == NULL) { return; } if (SP == 0) { SP = OsSpGet(); } if (LOS_TaskIsRunning()) { taskID = LOS_CurTaskIDGet(); taskCB = OS_TCB_FROM_TID(taskID); stackStart = taskCB->topOfStack; stackEnd = stackStart + taskCB->stackSize; } else { stackStart = CSTACK_START_ADDR; stackEnd = CSTACK_END_ADDR; } while ((SP > stackStart) && (SP < stackEnd)) { linkReg = *(UINTPTR *)SP; if (!OsStackDataIsCodeAddr(linkReg)) { SP += STACK_OFFSET; continue; } if (((!IsAligned(linkReg, ARM_ALIGN_CODE)) || !IsArmValidLr(linkReg)) && ((!IsAligned(linkReg - 1, THUMB_ALIGN_CODE)) || !IsThumbValidLr(linkReg - 1))) { SP += STACK_OFFSET; continue; } if (index >= jumpCount) { LR[count++] = linkReg & CLEAR_LOW_BIT_MASK; if (count == LRSize) { break; } } ++index; SP += STACK_OFFSET; } /* if linkReg is not enough,clean up the last of the effective LR as the end. */ if (count < LRSize) { LR[count] = 0; } } #else #error Unknown backtrace type. #endif #if (LOSCFG_BACKTRACE_TYPE == 1) || (LOSCFG_BACKTRACE_TYPE == 3) VOID LOS_RecordLR(UINTPTR *LR, UINT32 LRSize, UINT32 jumpCount, UINTPTR SP) { if (LR == NULL) { return; } UINTPTR stackStart; UINTPTR stackEnd; UINT32 count = 0; UINT32 index = 0; UINTPTR sp; UINTPTR pc; UINT32 ret; ret = OsStackAddrGet(&stackStart, &stackEnd, SP); if (ret != LOS_OK) { return; } /* Traverse the stack space and find the LR address. */ for (sp = stackStart; sp < stackEnd; sp += sizeof(UINTPTR)) { pc = OsAddrIsValid(sp); if ((pc != 0) && (count < LRSize)) { if (index++ < jumpCount) { continue; } LR[count] = pc; count++; if (count == LRSize) { break; } } } if (count < LRSize) { LR[count] = 0; } } #endif VOID LOS_BackTrace(VOID) { UINTPTR LR[BACKTRACE_MAX_DEPTH] = {0}; UINT32 index; LOS_RecordLR(LR, BACKTRACE_MAX_DEPTH, OS_BACKTRACE_START, 0); if (LOS_TaskIsRunning()) { PRINTK("taskName = %s\n", g_losTask.runTask->taskName); PRINTK("taskID = %u\n", g_losTask.runTask->taskID); } PRINTK("----- traceback start -----\r\n"); for (index = 0; index < BACKTRACE_MAX_DEPTH; index++) { if (LR[index] == 0) { break; } PRINTK("traceback %d -- lr = 0x%x\r\n", index, LR[index]); } PRINTK("----- traceback end -----\r\n"); } VOID OsBackTraceInit(VOID) { OsBackTraceHookSet(LOS_RecordLR); } #endif