| /**************************************************************************** |
| * boards/arm/stm32/nucleo-f429zi/src/stm32_bbsram.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you 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. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <sys/ioctl.h> |
| |
| #include <stdint.h> |
| #include <stddef.h> |
| #include <stdlib.h> |
| #include <fcntl.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <debug.h> |
| #include <syslog.h> |
| |
| #include <sys/param.h> |
| |
| #include <nuttx/fs/fs.h> |
| |
| #include "arm_internal.h" |
| #include "stm32_bbsram.h" |
| |
| #include "nucleo-144.h" |
| |
| #ifdef CONFIG_STM32_BBSRAM |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /* Configuration ************************************************************/ |
| |
| #define FREEZE_STR(s) #s |
| #define STRINGIFY(s) FREEZE_STR(s) |
| #define HARDFAULT_FILENO 3 |
| #define HARDFAULT_PATH BBSRAM_PATH""STRINGIFY(HARDFAULT_FILENO) |
| #define HARDFAULT_REBOOT_ FILENO 0 |
| #define HARDFAULT_REBOOT_PATH BBSRAM_PATH""STRINGIFY(HARDFAULT_REBOOT_FILENO) |
| |
| #define BBSRAM_SIZE_FN0 (sizeof(int)) |
| #define BBSRAM_SIZE_FN1 384 |
| #define BBSRAM_SIZE_FN2 384 |
| #define BBSRAM_SIZE_FN3 - 1 |
| |
| /* The following guides in the amount of the user and interrupt stack |
| * data we can save. The amount of storage left will dictate the actual |
| * number of entries of the user stack data saved. If it is too big |
| * It will be truncated by the call to stm32_bbsram_savepanic |
| */ |
| #define BBSRAM_HEADER_SIZE 20 /* This is an assumption */ |
| #define BBSRAM_USED ((4*BBSRAM_HEADER_SIZE)+ \ |
| (BBSRAM_SIZE_FN0+BBSRAM_SIZE_FN1+ \ |
| BBSRAM_SIZE_FN2)) |
| #define BBSRAM_REAMINING (STM32_BBSRAM_SIZE-BBSRAM_USED) |
| #if CONFIG_ARCH_INTERRUPTSTACK <= 3 |
| # define BBSRAM_NUMBER_STACKS 1 |
| #else |
| # define BBSRAM_NUMBER_STACKS 2 |
| #endif |
| #define BBSRAM_FIXED_ELEMENTS_SIZE (sizeof(info_t)) |
| #define BBSRAM_LEFTOVER (BBSRAM_REAMINING-\ |
| BBSRAM_FIXED_ELEMENTS_SIZE) |
| |
| #define CONFIG_ISTACK_SIZE (BBSRAM_LEFTOVER/BBSRAM_NUMBER_STACKS/ \ |
| sizeof(stack_word_t)) |
| #define CONFIG_USTACK_SIZE (BBSRAM_LEFTOVER/BBSRAM_NUMBER_STACKS/ \ |
| sizeof(stack_word_t)) |
| |
| /* The path to the Battery Backed up SRAM */ |
| |
| #define BBSRAM_PATH "/fs/bbr" |
| |
| /* The sizes of the files to create (-1) use rest of BBSRAM memory */ |
| |
| #define BSRAM_FILE_SIZES \ |
| { \ |
| BBSRAM_SIZE_FN0, \ |
| BBSRAM_SIZE_FN1, \ |
| BBSRAM_SIZE_FN2, \ |
| BBSRAM_SIZE_FN3, \ |
| 0 \ |
| } |
| |
| /* For Assert keep this much of the file name */ |
| |
| #define MAX_FILE_PATH_LENGTH 40 |
| |
| #define HEADER_TIME_FMT "%Y-%m-%d-%H:%M:%S" |
| #define HEADER_TIME_FMT_NUM (2+ 0+ 0+ 0+ 0+ 0) |
| #define HEADER_TIME_FMT_LEN (((nitems(HEADER_TIME_FMT)-1) + \ |
| HEADER_TIME_FMT_NUM)) |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /* Used for stack frame storage */ |
| |
| typedef uint32_t stack_word_t; |
| |
| /* Stack related data */ |
| |
| typedef struct |
| { |
| uint32_t sp; |
| uint32_t top; |
| uint32_t size; |
| } _stack_t; |
| |
| typedef struct |
| { |
| _stack_t user; |
| #if CONFIG_ARCH_INTERRUPTSTACK > 3 |
| _stack_t interrupt; |
| #endif |
| } stacks_t; |
| |
| /* Not Used for reference only */ |
| |
| typedef struct |
| { |
| uint32_t r0; |
| uint32_t r1; |
| uint32_t r2; |
| uint32_t r3; |
| 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 r12; |
| uint32_t sp; |
| uint32_t lr; |
| uint32_t pc; |
| uint32_t xpsr; |
| uint32_t d0; |
| uint32_t d1; |
| uint32_t d2; |
| uint32_t d3; |
| uint32_t d4; |
| uint32_t d5; |
| uint32_t d6; |
| uint32_t d7; |
| uint32_t d8; |
| uint32_t d9; |
| uint32_t d10; |
| uint32_t d11; |
| uint32_t d12; |
| uint32_t d13; |
| uint32_t d14; |
| uint32_t d15; |
| uint32_t fpscr; |
| uint32_t sp_main; |
| uint32_t sp_process; |
| uint32_t apsr; |
| uint32_t ipsr; |
| uint32_t epsr; |
| uint32_t primask; |
| uint32_t basepri; |
| uint32_t faultmask; |
| uint32_t control; |
| uint32_t s0; |
| uint32_t s1; |
| uint32_t s2; |
| uint32_t s3; |
| uint32_t s4; |
| uint32_t s5; |
| uint32_t s6; |
| uint32_t s7; |
| uint32_t s8; |
| uint32_t s9; |
| uint32_t s10; |
| uint32_t s11; |
| uint32_t s12; |
| uint32_t s13; |
| uint32_t s14; |
| uint32_t s15; |
| uint32_t s16; |
| uint32_t s17; |
| uint32_t s18; |
| uint32_t s19; |
| uint32_t s20; |
| uint32_t s21; |
| uint32_t s22; |
| uint32_t s23; |
| uint32_t s24; |
| uint32_t s25; |
| uint32_t s26; |
| uint32_t s27; |
| uint32_t s28; |
| uint32_t s29; |
| uint32_t s30; |
| uint32_t s31; |
| } proc_regs_t; |
| |
| /* Flags to identify what is in the dump */ |
| |
| typedef enum |
| { |
| REGS_PRESENT = 0x01, |
| USERSTACK_PRESENT = 0x02, |
| INTSTACK_PRESENT = 0x04, |
| INVALID_USERSTACK_PTR = 0x20, |
| INVALID_INTSTACK_PTR = 0x40, |
| } fault_flags_t; |
| |
| typedef struct |
| { |
| fault_flags_t flags; /* What is in the dump */ |
| uintptr_t current_regs; /* Used to validate the dump */ |
| int lineno; /* __LINE__ to up_assert */ |
| pid_t pid; /* Process ID */ |
| uint32_t regs[XCPTCONTEXT_REGS]; /* Interrupt register save area */ |
| stacks_t stacks; /* Stack info */ |
| #if CONFIG_TASK_NAME_SIZE > 0 |
| char name[CONFIG_TASK_NAME_SIZE + 1]; /* Task name (with NULL |
| * terminator) */ |
| #endif |
| char filename[MAX_FILE_PATH_LENGTH]; /* the Last of chars in |
| * __FILE__ to up_assert */ |
| } info_t; |
| |
| typedef struct |
| { |
| info_t info; /* The info */ |
| #if CONFIG_ARCH_INTERRUPTSTACK > 3 |
| /* The amount of stack data is compile time |
| * sized backed on what is left after the |
| * other BBSRAM files are defined |
| * The order is such that only the |
| * ustack should be truncated |
| */ |
| stack_word_t istack[CONFIG_USTACK_SIZE]; |
| #endif |
| stack_word_t ustack[CONFIG_ISTACK_SIZE]; |
| } fullcontext_t; |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| static uint8_t g_sdata[STM32_BBSRAM_SIZE]; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: hardfault_get_desc |
| ****************************************************************************/ |
| |
| static int hardfault_get_desc(struct bbsramd_s *desc) |
| { |
| struct file filestruct; |
| int ret; |
| |
| ret = file_open(&filestruct, HARDFAULT_PATH, O_RDONLY); |
| if (ret < 0) |
| { |
| syslog(LOG_INFO, "stm32 bbsram: Failed to open Fault Log file [%s] " |
| "(%d)\n", HARDFAULT_PATH, ret); |
| } |
| else |
| { |
| ret = file_ioctl(&filestruct, STM32_BBSRAM_GETDESC_IOCTL, |
| (unsigned long)((uintptr_t)desc)); |
| file_close(&filestruct); |
| |
| if (ret < 0) |
| { |
| syslog(LOG_INFO, "stm32 bbsram:" |
| "Failed to get Fault Log descriptor" "(%d)\n", ret); |
| } |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: copy_reverse |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_STM32_SAVE_CRASHDUMP) |
| static void copy_reverse(stack_word_t *dest, stack_word_t *src, int size) |
| { |
| while (size--) |
| { |
| *dest++ = *src--; |
| } |
| } |
| #endif /* CONFIG_STM32_SAVE_CRASHDUMP */ |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: stm32_bbsram_int |
| ****************************************************************************/ |
| |
| int stm32_bbsram_int(void) |
| { |
| int filesizes[CONFIG_STM32_BBSRAM_FILES + 1] = BSRAM_FILE_SIZES; |
| char buf[HEADER_TIME_FMT_LEN + 1]; |
| struct bbsramd_s desc; |
| int rv; |
| int state; |
| struct tm tt; |
| time_t time_sec; |
| |
| /* Using Battery Backed Up SRAM */ |
| |
| stm32_bbsraminitialize(BBSRAM_PATH, filesizes); |
| |
| #if defined(CONFIG_STM32_SAVE_CRASHDUMP) |
| /* Panic Logging in Battery Backed Up Files |
| * Do we have an hard fault in BBSRAM? |
| */ |
| |
| rv = hardfault_get_desc(&desc); |
| if (rv >= OK) |
| { |
| syslog(LOG_EMERG, "There is a hard fault logged.\n"); |
| state = (desc.lastwrite.tv_sec || desc.lastwrite.tv_nsec) ? OK : 1; |
| |
| syslog(LOG_INFO, "Fault Log info File No %d Length %d flags:0x%02x " |
| "state:%d\n", (unsigned int)desc.fileno, (unsigned int) desc.len, |
| (unsigned int)desc.flags, state); |
| |
| if (state == OK) |
| { |
| time_sec = desc.lastwrite.tv_sec + (desc.lastwrite.tv_nsec / 1e9); |
| gmtime_r(&time_sec, &tt); |
| strftime(buf, HEADER_TIME_FMT_LEN , HEADER_TIME_FMT , &tt); |
| |
| syslog(LOG_INFO, "Fault Logged on %s - Valid\n", buf); |
| } |
| |
| rv = nx_unlink(HARDFAULT_PATH); |
| if (rv < 0) |
| { |
| syslog(LOG_INFO, "stm32 bbsram: Failed to unlink Fault Log file" |
| "[%s] (%d)\n", HARDFAULT_PATH, rv); |
| } |
| } |
| #endif /* CONFIG_STM32_SAVE_CRASHDUMP */ |
| |
| return rv; |
| } |
| |
| /**************************************************************************** |
| * Name: board_crashdump |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_STM32_SAVE_CRASHDUMP) |
| void board_crashdump(uintptr_t sp, struct tcb_s *tcb, |
| const char *filename, int lineno, |
| const char *msg, void *regs) |
| { |
| fullcontext_t *pdump = (fullcontext_t *)&g_sdata; |
| int rv; |
| |
| enter_critical_section(); |
| |
| /* Zero out everything */ |
| |
| memset(pdump, 0, sizeof(fullcontext_t)); |
| |
| /* Save Info */ |
| |
| pdump->info.lineno = lineno; |
| |
| if (filename) |
| { |
| int offset = 0; |
| unsigned int len = strlen((char *)filename) + 1; |
| |
| if (len > sizeof(pdump->info.filename)) |
| { |
| offset = len - sizeof(pdump->info.filename); |
| } |
| |
| strlcpy(pdump->info.filename, (char *)&filename[offset], |
| sizeof(pdump->info.filename)); |
| } |
| |
| /* Save the value of the pointer for current_regs as debugging info. |
| * It should be NULL in case of an ASSERT and will aid in cross |
| * checking the validity of system memory at the time of the |
| * fault. |
| */ |
| |
| pdump->info.current_regs = (uintptr_t) CURRENT_REGS; |
| |
| /* Save Context */ |
| |
| #if CONFIG_TASK_NAME_SIZE > 0 |
| strlcpy(pdump->info.name, tcb->name, sizeof(pdump->info.name)); |
| #endif |
| |
| pdump->info.pid = tcb->pid; |
| |
| /* If current_regs is not NULL then we are in an interrupt context |
| * and the user context is in current_regs else we are running in |
| * the users context |
| */ |
| |
| if (CURRENT_REGS) |
| { |
| pdump->info.stacks.interrupt.sp = sp; |
| pdump->info.flags |= (REGS_PRESENT | USERSTACK_PRESENT | \ |
| INTSTACK_PRESENT); |
| memcpy(pdump->info.regs, (void *)CURRENT_REGS, |
| sizeof(pdump->info.regs)); |
| pdump->info.stacks.user.sp = pdump->info.regs[REG_R13]; |
| } |
| else |
| { |
| /* users context */ |
| |
| pdump->info.flags |= USERSTACK_PRESENT; |
| pdump->info.stacks.user.sp = sp; |
| } |
| |
| pdump->info.stacks.user.top = (uint32_t)tcb->stack_base_ptr + |
| tcb->adj_stack_size; |
| pdump->info.stacks.user.size = (uint32_t)tcb->adj_stack_size; |
| |
| #if CONFIG_ARCH_INTERRUPTSTACK > 3 |
| /* Get the limits on the interrupt stack memory */ |
| |
| pdump->info.stacks.interrupt.top = (uint32_t)g_intstacktop; |
| pdump->info.stacks.interrupt.size = (CONFIG_ARCH_INTERRUPTSTACK & ~3); |
| |
| /* If In interrupt Context save the interrupt stack data centered |
| * about the interrupt stack pointer |
| */ |
| |
| if ((pdump->info.flags & INTSTACK_PRESENT) != 0) |
| { |
| stack_word_t *ps = (stack_word_t *) pdump->info.stacks.interrupt.sp; |
| copy_reverse(pdump->istack, &ps[nitems(pdump->istack) / 2], |
| nitems(pdump->istack)); |
| } |
| |
| /* Is it Invalid? */ |
| |
| if (!(pdump->info.stacks.interrupt.sp |
| <= pdump->info.stacks.interrupt.top && |
| pdump->info.stacks.interrupt.sp > pdump->info.stacks.interrupt.top - |
| pdump->info.stacks.interrupt.size)) |
| { |
| pdump->info.flags |= INVALID_INTSTACK_PTR; |
| } |
| |
| #endif |
| /* If In interrupt context or User save the user stack data centered |
| * about the user stack pointer |
| */ |
| |
| if ((pdump->info.flags & USERSTACK_PRESENT) != 0) |
| { |
| stack_word_t *ps = (stack_word_t *) pdump->info.stacks.user.sp; |
| copy_reverse(pdump->ustack, &ps[nitems(pdump->ustack) / 2], |
| nitems(pdump->ustack)); |
| } |
| |
| /* Is it Invalid? */ |
| |
| if (!(pdump->info.stacks.user.sp <= pdump->info.stacks.user.top && |
| pdump->info.stacks.user.sp > pdump->info.stacks.user.top - |
| pdump->info.stacks.user.size)) |
| { |
| pdump->info.flags |= INVALID_USERSTACK_PTR; |
| } |
| |
| rv = stm32_bbsram_savepanic(HARDFAULT_FILENO, (uint8_t *)pdump, |
| sizeof(fullcontext_t)); |
| |
| /* Test if memory got wiped because of using _sdata */ |
| |
| if (rv == -ENXIO) |
| { |
| char *dead = "Memory wiped - dump not saved!"; |
| |
| while (*dead) |
| { |
| arm_lowputc(*dead++); |
| } |
| } |
| else if (rv == -ENOSPC) |
| { |
| /* hard fault again */ |
| |
| arm_lowputc('!'); |
| } |
| } |
| #endif /* CONFIG_STM32_SAVE_CRASHDUMP */ |
| |
| #endif /* CONFIG_STM32_BBSRAM */ |