// SPDX-License-Identifier: GPL-2.0+ /** @file avm_reboot_status.c * * mbahr: * 1.) hold and get reboot-status after Soft/NMI-Reboot * 2.) handle die-notifier * 3.) handle panic-notifier */ #pragma GCC push_options #pragma GCC diagnostic ignored "-Wunused-parameter" #pragma GCC diagnostic ignored "-Wsign-compare" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if __has_include() #include #else #include #endif #pragma GCC pop_options #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) #include #endif #include #include #include #if defined(CONFIG_AVM_FASTIRQ) #if defined(CONFIG_AVM_FASTIRQ_ARCH_ARM_COMMON) #include #else #include #endif #endif /*--- #if defined(CONFIG_AVM_FASTIRQ) ---*/ #include "avm_sammel.h" #define MAILBOX_SIZE 512U #if defined(CONFIG_SOC_GRX500) #include "arch_avm_reboot_status_grx.h" #endif /*--- #if defined(CONFIG_SOC_GRX500) ---*/ #if defined(CONFIG_VR9) || defined(CONFIG_AR10) #include "arch_avm_reboot_status_ifx.h" #endif /*--- #if defined(CONFIG_VR9) || defined(CONFIG_AR10) ---*/ #if defined(CONFIG_MACH_PUMA6) #include "arch_avm_reboot_status_puma6a.h" #elif defined(CONFIG_X86) && !defined(CONFIG_X86_PUMA7) #include "arch_avm_reboot_status_puma6x.h" #elif defined(CONFIG_MACH_PUMA7) #if defined(CONFIG_X86_PUMA7) #include "arch_avm_reboot_status_puma7x.h" #else #include "arch_avm_reboot_status_puma7.h" #endif #endif #if defined(CONFIG_MACH_BCM963138) || defined(CONFIG_BCM963178) #include "arch_avm_reboot_status_brcma.h" #endif /*--- #if defined(CONFIG_MACH_BCM963138) || defined(CONFIG_BCM963178) ---*/ #if defined(CONFIG_ARCH_IPQ40XX) #include "arch_avm_reboot_status_ipq40xx.h" #endif /*--- #if defined(CONFIG_ARCH_IPQ40XX) ---*/ #if defined(CONFIG_SOC_AR724X) || defined(CONFIG_SOC_AR934X) || \ defined(CONFIG_SOC_QCA955X) || defined(CONFIG_SOC_QCA953X) || \ defined(CONFIG_SOC_QCA956X) #include "arch_avm_reboot_status_scrpn.h" #endif /*--- #if defined(CONFIG_SOC_AR724X) || defined(CONFIG_SOC_AR934X) || defined(CONFIG_SOC_QCA955X) || defined(CONFIG_SOC_QCA953X) || defined(CONFIG_SOC_QCA956X) ---*/ #include #define DEBUG_MAILBOX 0 struct _avm_reboot_info { enum _avm_reset_status status; enum _avm_reset_status next_reboot_status; unsigned long uptime; unsigned long utc; char fw_version[2][64]; // index: 0 prepared before crash for mailbox-write, index: 1 read from mailbox char hw_version[2][32]; char hw_subversion[2][32]; char bootloaderVersion[2][32]; }; static struct _avm_reboot_info avm_reboot_info; #define MB_SUM_PREFIX "SUM(" #define MB_UPTIME_PREFIX "UP(" #define MB_UTC_PREFIX "UTC(" #define MB_FW_PREFIX "FW(" #define MB_HW_PREFIX "HW(" #define MB_HWS_PREFIX "HWS(" #define MB_BV_PREFIX "BV(" #define FW_STRING(strname) .str[0] = avm_reboot_info.strname[0], .str[1] = avm_reboot_info.strname[1], .str_len = sizeof(avm_reboot_info.strname[0]) static const struct _prepare_fw_info { char *env_name; const char *mb_name; // mailbox-name char *str[2]; unsigned int str_len; } prepare_fw_info[] = { { .env_name = "firmware_info", .mb_name = MB_FW_PREFIX, FW_STRING(fw_version) }, { .env_name = "HWRevision", .mb_name = MB_HW_PREFIX, FW_STRING(hw_version) }, { .env_name = "HWSubRevision", .mb_name = MB_HWS_PREFIX, FW_STRING(hw_subversion) }, { .env_name = "bootloaderVersion", .mb_name = MB_BV_PREFIX, FW_STRING(bootloaderVersion) }, }; /** * \brief Deliver last reboot-status */ enum _avm_reset_status avm_reset_status(void) { enum _avm_reset_status status; switch (avm_reboot_info.status) { case RS_PANIC: case RS_OOM: case RS_OOPS: status = RS_REBOOT; break; default: status = avm_reboot_info.status; } return status; } EXPORT_SYMBOL(avm_reset_status); enum _avm_reset_status avm_next_reset_status(void) { return avm_reboot_info.next_reboot_status; } EXPORT_SYMBOL(avm_next_reset_status); struct _reboot_info { const enum _avm_reset_status status; const char *matchtext; int matchlen; const char *printouttext; const char *shortprintouttext; unsigned int reboot_count; }; #define REBOOT_INFO_ENTRY(_status, user_txt, mbox_txt, mbox_counter) { \ .status = (_status), \ .matchtext = (mbox_txt), \ .matchlen = (mbox_txt) ? sizeof(mbox_txt) : 0, \ .printouttext = (user_txt), \ .shortprintouttext = (mbox_counter), \ } // clang-format off static struct _reboot_info reboot_info[] = { REBOOT_INFO_ENTRY(RS_SOFTWATCHDOG, "Softwatchdog-Reboot", SOFTWATCHDOG_REBOOT_STATUS_TEXT, "WD"), REBOOT_INFO_ENTRY(RS_NMIWATCHDOG, "NMI-Watchdog-Reset", NMI_REBOOT_STATUS_TEXT, "NMI"), REBOOT_INFO_ENTRY(RS_FIRMWAREUPDATE, "Fw-Update", UPDATE_REBOOT_STATUS_TEXT, NULL), REBOOT_INFO_ENTRY(RS_SHORTREBOOT, "Short-PowerOff-Reboot", POWERON_REBOOT_STATUS_TEXT, "SHORTPOWERCUT"), REBOOT_INFO_ENTRY(RS_TEMP_REBOOT, "Temperature-Reboot", TEMP_REBOOT_STATUS_TEXT, "TEMPERATURE"), REBOOT_INFO_ENTRY(RS_REBOOT_FOR_UPDATE, "Update-Reboot", SOFT_REBOOT_STATUS_TEXT_UPDATE, "UPDATE"), REBOOT_INFO_ENTRY(RS_PANIC, "Soft-Reboot", SOFT_REBOOT_STATUS_TEXT_PANIC, "PANIC"), REBOOT_INFO_ENTRY(RS_OOM, "Soft-Reboot", SOFT_REBOOT_STATUS_TEXT_OOM, "OOM"), REBOOT_INFO_ENTRY(RS_OOPS, "Soft-Reboot", SOFT_REBOOT_STATUS_TEXT_OOPS, "KCRASH"), REBOOT_INFO_ENTRY(RS_DOCSIS_LOCAL, "Soft-Reboot", SOFT_REBOOT_STATUS_TEXT_DOCSIS_LOCAL, "DOCSIS_LOCAL"), REBOOT_INFO_ENTRY(RS_DOCSIS_OPERATOR, "Soft-Reboot", SOFT_REBOOT_STATUS_TEXT_DOCSIS_OPERATOR, "DOCSIS_OPERATOR"), /*--- als vorletzter Eintrag, da dieser Untermenge von RS_PANIC/RS_OOM/RS_OOPS ---*/ REBOOT_INFO_ENTRY(RS_REBOOT, "Soft-Reboot", SOFT_REBOOT_STATUS_TEXT, NULL), /*--- definition: have to be last entry: ---*/ REBOOT_INFO_ENTRY(RS_POWERON, "Power-On", NULL, NULL), }; // clang-format on /** */ static void read_reboot_counters(char *txt) { char *p; unsigned int i, control, sum = 0; unsigned int counter[ARRAY_SIZE(reboot_info)]; for (i = 0; i < ARRAY_SIZE(reboot_info); i++) { counter[i] = 0; if (reboot_info[i].shortprintouttext && (p = strstr(txt, reboot_info[i].shortprintouttext))) { p += strlen(reboot_info[i].shortprintouttext) + 1; /*--- inklusive ( ---*/ sscanf(p, "%u", &counter[i]); sum += counter[i]; } } p = strstr(txt, MB_SUM_PREFIX); if (p) { p += sizeof(MB_SUM_PREFIX) - 1; sscanf(p, "%u", &control); if (control == sum) { for (i = 0; i < ARRAY_SIZE(reboot_info); i++) { reboot_info[i].reboot_count = counter[i]; } } } } /** * zero-terminate dest on ')' */ static void copy_info_string(char *dest, const char *src, unsigned int len) { strlcpy(dest, src, len); while (*dest) { if (*dest == ')') { *dest = 0; return; } dest++; } } /** * read uptime and fw-info from mailbox * format UP()UTC(