// 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 */ #include #include #include #include #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0) #include #endif #include #include #include #include #include #include #include #include #include #include #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_grx.h" #endif /*--- #if defined(CONFIG_SOC_GRX500) ---*/ #if defined(CONFIG_MACH_PUMA7) #if defined(CONFIG_X86_PUMA7) #include "arch_puma7x.h" #else #include "arch_puma7.h" #endif #endif #if defined(CONFIG_MACH_BCM963138) || defined(CONFIG_BCM963178) #include "arch_brcma.h" #endif /*--- #if defined(CONFIG_MACH_BCM963138) || defined(CONFIG_BCM963178) ---*/ #if defined(CONFIG_ARCH_IPQ9574) || defined(CONFIG_ARCH_IPQ5018) \ || defined(CONFIG_ARCH_IPQ40XX) || defined(CONFIG_ARCH_IPQ5332) \ || defined(CONFIG_ARCH_IPQ8074) #include "arch_ipq.h" #endif #if defined(CONFIG_SOC_AR724X) || defined(CONFIG_SOC_AR934X) || \ defined(CONFIG_SOC_QCA955X) || defined(CONFIG_SOC_QCA953X) || \ defined(CONFIG_SOC_QCA956X) #include "arch_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 static struct avm_reboot_info current_reboot_info __ro_after_init; static struct avm_reboot_info next_reboot_info; static struct avm_firmware_build_info reboot_buildinfo; /* parsed from mailbox */ /** * \brief Deliver last reboot-status */ enum _avm_reset_status avm_reset_status(void) { return current_reboot_info.reboot_status; } EXPORT_SYMBOL(avm_reset_status); enum _avm_reset_status avm_next_reset_status(void) { return next_reboot_info.reboot_status; } EXPORT_SYMBOL(avm_next_reset_status); /* * Mailbox string helpers */ #define __repeat3x(s) s s s /* * "Simple" mailbox strings are zero-terminated. The AVM Urlader knows about * these, so we cannot change these strings. */ #define __mbx_string_simple(s) __repeat3x(s) /* * "Extended" mailbox strings extend beyond the first NUL character. * The Urlader stops parsing at the first NUL; so, everything after that is * internal to the kernel. */ #define __mbx_string_ext(s, x) \ __mbx_string_simple(s) "\0(" x ")" struct mailbox_strings { const u8 *str; size_t strlen; const char *cntr; }; #define _mbx_str_init(s, c) { s, sizeof(s), c } /* Header file for target box must define MAILBOX_STRING_YEAR */ #ifndef MAILBOX_STRING_YEAR #error MAILBOX_STRING_YEAR not defined for target box! #endif #define mbx_str_init(_mbs, _mbcntr) _mbx_str_init( \ __mbx_string_simple("(c) AVM " MAILBOX_STRING_YEAR ", Reboot Status is: " _mbs), \ _mbcntr) #define mbx_str_init_ext(_mbs, _mbs_ext, _mbcntr) _mbx_str_init( \ __mbx_string_ext("(c) AVM " MAILBOX_STRING_YEAR ", Reboot Status is: " _mbs, _mbs_ext), \ _mbcntr) struct _reboot_info { const enum _avm_reset_status status; const char *status_name; const struct mailbox_strings mbs; /* Historical sysctl status names for reads and writes can differ. */ const char *sysctl_name_read; const char *sysctl_name_write; /* NULL if status is r/o */ unsigned int reboot_count; unsigned int live_count; /* for other events, reset on each boot */ #define RI_FOLLOWS_PANIC BIT(0) #define RI_CLEARALL_ON_BOOTUP BIT(1) #define RI_PROVISIONAL BIT(2) #define RI_CNT_ON_BOOTUP BIT(3) #define RI_OTHER_EVENT BIT(4) #define RI_RESET_ON_BOOTUP BIT(5) unsigned int flags; }; enum reboot_array_format { mailbox_format, irregularreboot_format, /* format for "Irregular-Reboots" */ line_format /* format for "Line by Line", the keyword is sysctl_name_read */ }; #define REBOOT_INFO(_status, _status_name, _sysctl_rd, _sysctl_wr, \ _mbs, _mbcntr, _flags) { \ .status = _status, \ .status_name = _status_name, \ .sysctl_name_read = _sysctl_rd, \ .sysctl_name_write = _sysctl_wr, \ .mbs = mbx_str_init(_mbs, _mbcntr), \ .flags = (_flags), \ } #define REBOOT_INFO_EXT(_status, _status_name, _sysctl_rd, _sysctl_wr, \ _mbs, _mbs_ext, _mbcntr, _flags) { \ .status = _status, \ .status_name = _status_name, \ .sysctl_name_read = _sysctl_rd, \ .sysctl_name_write = _sysctl_wr, \ .mbs = mbx_str_init_ext(_mbs, _mbs_ext, _mbcntr), \ .flags = (_flags), \ } #define REBOOT_INFO_NOMBX(_status, _status_name, _sysctl_rd, _sysctl_wr, _flags) { \ .status = _status, \ .status_name = _status_name, \ .sysctl_name_read = _sysctl_rd, \ .sysctl_name_write = _sysctl_wr, \ .flags = (_flags), \ } // clang-format off static struct _reboot_info reboot_info[] = { /* status status_name sysctl_name_read, sysctl_name_write, mbx_status_name, mbx_status_ext, mbx_cntr, flags */ REBOOT_INFO_EXT(RS_PANIC, "Soft-Reboot", "panic", NULL, "Software-Reboot", "PANIC", "PANIC", RI_FOLLOWS_PANIC), REBOOT_INFO_EXT(RS_OOM, "Soft-Reboot", "oom", NULL, "Software-Reboot", "OOM", "OOM", RI_FOLLOWS_PANIC), REBOOT_INFO_EXT(RS_OOPS, "Soft-Reboot", "oops", NULL, "Software-Reboot", "OOPS", "KCRASH", RI_FOLLOWS_PANIC), REBOOT_INFO_EXT(RS_BUSERROR, "Soft-Reboot", "buserror-reboot", "buserror", "Software-Reboot", "BE", "BE", 0), REBOOT_INFO_EXT(RS_DOCSIS_LOCAL, "Soft-Reboot", "docsis-local", "docsis-local", "Software-Reboot", "DOCSIS_LOCAL", "DOCSIS_LOCAL", 0), REBOOT_INFO_EXT(RS_DOCSIS_OPERATOR, "Soft-Reboot", "docsis-operator", "docsis-operator", "Software-Reboot", "DOCSIS_OPERATOR", "DOCSIS_OPERATOR", 0), REBOOT_INFO_EXT(RS_BOXCHANGE, "Soft-Reboot", "boxchange", "boxchange", "Software-Reboot", "BOXCHANGE", "BOXCHANGE", 0), REBOOT_INFO_EXT(RS_OPERATOR, "Soft-Reboot", "operator", "operator", "Software-Reboot", "OPERATOR", "OPERATOR", 0), /* status status_name sysctl_name_read, sysctl_name_write, mbx_status_name, mbx_cntr, flags */ REBOOT_INFO (RS_SOFTWATCHDOG, "Softwatchdog-Reboot", "software", NULL, "Software-Watchdog", "WD", RI_FOLLOWS_PANIC), REBOOT_INFO (RS_NMIWATCHDOG, "NMI-Watchdog-Reset", "watchdog", NULL, "NMI-Watchdog", "NMI", RI_FOLLOWS_PANIC), REBOOT_INFO (RS_SHORTREBOOT, "Short-PowerOff-Reboot", "short-reboot", NULL, "Power-On-Reboot", "SHORTPOWERCUT", RI_PROVISIONAL | RI_CNT_ON_BOOTUP), REBOOT_INFO (RS_TEMP_REBOOT, "Temperature-Reboot", "temperature-reboot", "temperature", "Temperature-Reboot", "TEMPERATURE", 0), REBOOT_INFO (RS_FIRMWAREUPDATE, "Fw-Update", "firmware_update", "FW-Update", "Firmware-Update", NULL, RI_CLEARALL_ON_BOOTUP), /* status status_name sysctl_name_read, sysctl_name_write, mbx_status_name, mbx_cntr, flags */ REBOOT_INFO (RS_DYING_GASP, "Soft-Reboot", "dying-gasp", NULL, "Software-Reboot", "DG", RI_PROVISIONAL | RI_OTHER_EVENT | RI_RESET_ON_BOOTUP), /*--- als vorletzter Eintrag, da dieser Untermenge von RS_PANIC/RS_OOM/RS_OOPS ---*/ REBOOT_INFO (RS_REBOOT, "Soft-Reboot", "reboot", "reboot", "Software-Reboot", NULL, RI_CLEARALL_ON_BOOTUP), /* status status_name sysctl_name_read, sysctl_name_write, flags */ /*--- definition: have to be last entry: ---*/ REBOOT_INFO_NOMBX(RS_POWERON, "Power-On", "poweron", NULL, RI_PROVISIONAL), }; // clang-format on int avm_sysctl_set_reset_status(const char *status) { const struct _reboot_info *ri; int i; for (i = 0; i < ARRAY_SIZE(reboot_info); ++i) { ri = &reboot_info[i]; if (!ri->sysctl_name_write) continue; if (!strcmp(status, ri->sysctl_name_write)) goto found; } return -EINVAL; found: avm_set_reset_status(ri->status); return 0; } const char *avm_sysctl_reset_status(void) { const struct _reboot_info *ri; enum _avm_reset_status status; int i; status = avm_reset_status(); for (i = 0; i < ARRAY_SIZE(reboot_info); ++i) { ri = &reboot_info[i]; if (status == ri->status) goto found; } BUG(); found: return ri->sysctl_name_read; } static int mailbox_get_value(const char *mbox, const char *mbkey, char *buf, size_t bufsiz) { const char *s, *e; size_t len; if (!bufsiz) return 0; s = mbox; while ((s = strstr(s, mbkey))) { s += strlen(mbkey); if (*s++ == '(') break; } if (!s) return -ENOENT; e = strnchr(s, bufsiz, ')'); len = e ? e-s : bufsiz-1; strlcpy(buf, s, len+1); return 0; } /** */ static void read_reboot_counters(const char *mbox) { unsigned int i; char val[32]; int err; for (i = 0; i < ARRAY_SIZE(reboot_info); i++) { unsigned int counter = 0; const char *mbkey; mbkey = reboot_info[i].mbs.cntr; if (!mbkey) continue; err = mailbox_get_value(mbox, mbkey, val, sizeof(val)); if (err) continue; sscanf(val, "%u", &counter); reboot_info[i].reboot_count = counter; if ((reboot_info[i].flags & RI_OTHER_EVENT) && (reboot_info[i].flags & RI_RESET_ON_BOOTUP) == 0) reboot_info[i].live_count = counter; } } /** * read uptime and fw-info from mailbox * format UP()UTC(