Memfault Firmware SDK 1.10.0 (Build 8899)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 04b31fa..8b80ea4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,34 @@
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.10.0] - 2024-07-12
+
+### :chart_with_upwards_trend: Improvements
+
+- Zephyr:
+
+ - Add support for Memfault Logging when `CONFIG_LOG_MODE_MINIMAL=y`, in
+ addition to the previously supported `LOG_MODE_DEFERRED` (Zephyr default)
+ and `LOG_MODE_IMMEDIATE`.
+ [Zephyr minimal log mode](https://docs.zephyrproject.org/3.6.0/kconfig.html#CONFIG_LOG_MODE_MINIMAL)
+ disables all backends- logs are simply sent to `printk`.
+
+ - For ESP32 chips, place the reboot reason tracking into a dedicated section,
+ `.rtc_noinit`, to ensure the reboot reason is preserved across reboots. This
+ was previously only supported on Zephyr ESP32-S3/S2 devices (all ESP32
+ devices on ESP-IDF already support this).
+
+ - Fix a bug where the default `memfault_platform_get_device_info()`, which
+ uses the hwinfo subsystem, when available, was incorrectly disabled when
+ `CONFIG_MEMFAULT_REBOOT_REASON_GET_CUSTOM=y`.
+
+- General:
+
+ - Improve the `memfault_demo_cli_cmd_busfault()` function (accessed via
+ `test_busfault`/`mflt test busfault` demo CLI commands) to produce BusFaults
+ on more devices. Previously, certain chips would either not produce a fault,
+ or would produce a MemManage exception instead.
+
## [1.9.4] - 2024-07-01
### :chart_with_upwards_trend: Improvements
diff --git a/VERSION b/VERSION
index bc6021f..cd7a1ab 100644
--- a/VERSION
+++ b/VERSION
@@ -1,3 +1,3 @@
-BUILD ID: 8676
-GIT COMMIT: 69ac2e1558
-VERSION: 1.9.4
+BUILD ID: 8899
+GIT COMMIT: 7e534eec9c
+VERSION: 1.10.0
diff --git a/components/demo/src/panics/memfault_demo_panics.c b/components/demo/src/panics/memfault_demo_panics.c
index fa60953..2fb0ca9 100644
--- a/components/demo/src/panics/memfault_demo_panics.c
+++ b/components/demo/src/panics/memfault_demo_panics.c
@@ -172,8 +172,13 @@
}
int memfault_demo_cli_cmd_busfault(MEMFAULT_UNUSED int argc, MEMFAULT_UNUSED char *argv[]) {
- void (*unaligned_func)(void) = (void (*)(void))0x50000001;
- unaligned_func();
+ // Trigger a BusFault by attempting to load and jump to an address from the "RAM" (0x60000000 -
+ // 0x7FFFFFFF) region, described as: "Memory with write-back, write allocate cache attribute for
+ // L2/L3 cache support." in the ARMv7-M architecture reference manual. This is not guaranteed to
+ // trigger a BusFault, if the address is both loadable and executable, but on many devices it
+ // will.
+ void (*busfault_func)(void) = (void (*)(void))0x60000001;
+ busfault_func();
// We should never get here -- platforms BusFault or HardFault handler should be tripped
// with a precise error due to unaligned execution
diff --git a/components/include/memfault/version.h b/components/include/memfault/version.h
index b104c82..2c30904 100644
--- a/components/include/memfault/version.h
+++ b/components/include/memfault/version.h
@@ -19,8 +19,8 @@
uint8_t patch;
} sMfltSdkVersion;
-#define MEMFAULT_SDK_VERSION { .major = 1, .minor = 9, .patch = 4 }
-#define MEMFAULT_SDK_VERSION_STR "1.9.4"
+#define MEMFAULT_SDK_VERSION { .major = 1, .minor = 10, .patch = 0 }
+#define MEMFAULT_SDK_VERSION_STR "1.10.0"
#ifdef __cplusplus
}
diff --git a/examples/freertos/boards/qemu_mps2_an385/startup.c b/examples/freertos/boards/qemu_mps2_an385/startup.c
index 8b4d4a3..33bb706 100644
--- a/examples/freertos/boards/qemu_mps2_an385/startup.c
+++ b/examples/freertos/boards/qemu_mps2_an385/startup.c
@@ -102,6 +102,10 @@
}
__attribute__((noreturn)) void Reset_Handler(void) {
+// enable mem/bus/usage faults
+#define SCBSHCSR_ (*(uint32_t *)0xE000ED24)
+ SCBSHCSR_ |= (1UL << 16U) | (1UL << 17U) | (1UL << 18U);
+
prv_cinit();
uart_init();
diff --git a/examples/freertos/boards/qemu_mps2_an386/startup.c b/examples/freertos/boards/qemu_mps2_an386/startup.c
index 8b4d4a3..6605472 100644
--- a/examples/freertos/boards/qemu_mps2_an386/startup.c
+++ b/examples/freertos/boards/qemu_mps2_an386/startup.c
@@ -102,6 +102,10 @@
}
__attribute__((noreturn)) void Reset_Handler(void) {
+ // enable mem/bus/usage faults
+#define SCBSHCSR_ (*(uint32_t *)0xE000ED24)
+ SCBSHCSR_ |= (1UL << 16U) | (1UL << 17U) | (1UL << 18U);
+
prv_cinit();
uart_init();
diff --git a/ports/zephyr/common/CMakeLists.txt b/ports/zephyr/common/CMakeLists.txt
index 866c94c..9207a3b 100644
--- a/ports/zephyr/common/CMakeLists.txt
+++ b/ports/zephyr/common/CMakeLists.txt
@@ -36,7 +36,10 @@
# https://github.com/zephyrproject-rtos/zephyr/pull/31535
# It fully replaced version 1 after the 3.1 release: https://github.com/zephyrproject-rtos/zephyr/issues/46500
# and the option to enable logging v1 via CONFIG_LOG1 was entirely removed
- if(${KERNEL_VERSION_MAJOR}.${KERNEL_VERSION_MINOR}.${KERNEL_PATCHLEVEL} VERSION_GREATER_EQUAL "3.1.99" OR CONFIG_LOG2)
+ if(CONFIG_LOG_MODE_MINIMAL)
+ target_link_libraries(app INTERFACE "-Wl,--wrap=z_log_minimal_printk")
+ zephyr_library_sources(memfault_logging_minimal.c)
+ elseif(${KERNEL_VERSION_MAJOR}.${KERNEL_VERSION_MINOR}.${KERNEL_PATCHLEVEL} VERSION_GREATER_EQUAL "3.1.99" OR CONFIG_LOG2)
zephyr_library_sources(memfault_logging.c)
else()
zephyr_library_sources(memfault_logging_legacy.c)
@@ -73,4 +76,8 @@
zephyr_ld_options(-Wl,--wrap=k_free)
endif()
+if(CONFIG_SOC_FAMILY_ESP32)
+ zephyr_linker_sources(SECTIONS memfault-rtc-noinit-region.ld)
+endif()
+
zephyr_include_directories(.)
diff --git a/ports/zephyr/common/memfault-rtc-noinit-region.ld b/ports/zephyr/common/memfault-rtc-noinit-region.ld
new file mode 100644
index 0000000..26d6309
--- /dev/null
+++ b/ports/zephyr/common/memfault-rtc-noinit-region.ld
@@ -0,0 +1,19 @@
+/**
+* For ESP32 variants other than ESP32-S3/S2, provide a region of noinit placed
+in * RTC memory, for holding reboot reason information through OTA updates. *
+The normal Zephyr noinit region is not suitable for this, because its offset *
+can change through an OTA update, invalidating the reboot reason data.
+ *
+ * Unfortunately this doesn't place the section in the same order in RTC as in
+ * the ESP32-S3/S2, but it puts it in a usable location.
+ */
+#if !defined(CONFIG_SOC_SERIES_ESP32S3) && !defined(CONFIG_SOC_SERIES_ESP32S2)
+SECTION_PROLOGUE(.rtc_noinit,(NOLOAD),)
+{
+ . = ALIGN(4);
+ _rtc_noinit_start = ABSOLUTE(.);
+ *(.rtc_noinit .rtc_noinit.*)
+ . = ALIGN(4) ;
+ _rtc_noinit_end = ABSOLUTE(.);
+} GROUP_LINK_IN(rtc_slow_seg)
+#endif
diff --git a/ports/zephyr/common/memfault_logging_minimal.c b/ports/zephyr/common/memfault_logging_minimal.c
new file mode 100644
index 0000000..7f84ac5
--- /dev/null
+++ b/ports/zephyr/common/memfault_logging_minimal.c
@@ -0,0 +1,73 @@
+//! @file
+//!
+//! Copyright (c) Memfault, Inc.
+//! See License.txt for details
+//!
+//! @brief
+//! Hooks the Memfault log collection to the Zephyr LOG_MODE_MINIMAL logging
+//! system.
+
+// clang-format off
+#include MEMFAULT_ZEPHYR_INCLUDE(kernel.h)
+#include MEMFAULT_ZEPHYR_INCLUDE(sys/atomic.h)
+#include MEMFAULT_ZEPHYR_INCLUDE(logging/log.h)
+
+#include <stdarg.h>
+
+#include "memfault/components.h"
+// clang-format on
+
+enum {
+ MFLT_LOG_BUFFER_BUSY,
+};
+static atomic_t s_log_output_busy_flag;
+
+static void prv_log_init(void) {
+ // Static RAM storage where logs will be stored.
+ static uint8_t s_mflt_log_buf_storage[CONFIG_MEMFAULT_LOGGING_RAM_SIZE];
+ memfault_log_boot(s_mflt_log_buf_storage, sizeof(s_mflt_log_buf_storage));
+}
+
+static void prv_memfault_log_vprintf(const char *fmt, va_list args) {
+ // Force synchronization at this level.
+ if (atomic_test_and_set_bit(&s_log_output_busy_flag, MFLT_LOG_BUFFER_BUSY)) {
+ return;
+ }
+
+ // This is idempotent; but sensitive to concurrent access. The atomic flag
+ // that protects this function should be good enough, since it only has to
+ // succeed once. The vulnerability is only if a user is accessing
+ // memfault_log_save_preformatted_nolock() directly (should not be done).
+ prv_log_init();
+
+ // Render the log message into a buffer. Static storage class, so it doesn't
+ // contribute to stack usage which could be problematic in this call site.
+ static uint8_t s_zephyr_render_buf[128];
+ int save_length = vsnprintk(s_zephyr_render_buf, sizeof(s_zephyr_render_buf), fmt, args);
+ if (save_length > 0) {
+ // Always set to info, since we can't reliably determine the log level
+ memfault_log_save_preformatted_nolock(kMemfaultPlatformLogLevel_Info, s_zephyr_render_buf,
+ save_length);
+ }
+
+ atomic_clear_bit(&s_log_output_busy_flag, MFLT_LOG_BUFFER_BUSY);
+}
+
+void __wrap_z_log_minimal_printk(const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+
+ prv_memfault_log_vprintf(fmt, args);
+
+ va_start(args, fmt);
+
+ // Now call zephyr printk
+ z_log_minimal_vprintk(fmt, args);
+
+ va_end(args);
+}
+
+// Ensure the substituted function signature matches the original function
+_Static_assert(__builtin_types_compatible_p(__typeof__(&z_log_minimal_printk),
+ __typeof__(&__wrap_z_log_minimal_printk)),
+ "Error: check z_log_minimal_printk function signature");
diff --git a/ports/zephyr/common/memfault_platform_core.c b/ports/zephyr/common/memfault_platform_core.c
index cdd35b3..dcda2f1 100644
--- a/ports/zephyr/common/memfault_platform_core.c
+++ b/ports/zephyr/common/memfault_platform_core.c
@@ -61,7 +61,9 @@
// register capture disable is a very small size optimization, and logs are
// likely not used on devices with space constraints.
LOG_PANIC();
+ #if !defined(CONFIG_LOG_MODE_MINIMAL)
memfault_zephyr_log_backend_disable();
+ #endif
#endif
memfault_coredump_cache_fault_regs();
@@ -149,47 +151,6 @@
}
#endif // CONFIG_HWINFO
-// This can be overridden by the application to set a custom device ID
-MEMFAULT_WEAK const char *memfault_zephyr_get_device_id(void) {
- uint8_t dev_id[16] = { 0 };
- static char dev_id_str[sizeof(dev_id) * 2 + 1];
- static const char *dev_str = "UNKNOWN";
-
- // Obtain the device id
- #if defined(CONFIG_HWINFO)
- ssize_t length = hwinfo_get_device_id(dev_id, sizeof(dev_id));
- #else
- ssize_t length = 0;
- #endif
-
- // If hwinfo_get_device_id() fails or isn't enabled, use a fallback string
- if (length <= 0) {
- #if defined(CONFIG_SOC)
- dev_str = CONFIG_SOC "-testserial";
- #else
- dev_str = "testserial";
- #endif
- length = strlen(dev_str);
- } else {
- // Render the obtained serial number in hexadecimal representation
- for (size_t i = 0; i < length; i++) {
- (void)snprintf(&dev_id_str[i * 2], sizeof(dev_id_str), "%02x", dev_id[i]);
- }
- dev_str = dev_id_str;
- }
-
- return dev_str;
-}
-
-MEMFAULT_WEAK void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) {
- *info = (sMemfaultDeviceInfo){
- .device_serial = memfault_zephyr_get_device_id(),
- .software_type = CONFIG_MEMFAULT_BUILTIN_DEVICE_INFO_SOFTWARE_TYPE,
- .software_version = CONFIG_MEMFAULT_BUILTIN_DEVICE_INFO_SOFTWARE_VERSION,
- .hardware_version = CONFIG_MEMFAULT_BUILTIN_DEVICE_INFO_HARDWARE_VERSION,
- };
-}
-
MEMFAULT_WEAK void memfault_reboot_reason_get(sResetBootupInfo *info) {
eMemfaultRebootReason reset_reason = kMfltRebootReason_Unknown;
uint32_t reset_reason_reg = 0;
@@ -213,6 +174,47 @@
memfault_reboot_tracking_collect_reset_info(s_memfault_event_storage);
}
+// This can be overridden by the application to set a custom device ID
+MEMFAULT_WEAK const char *memfault_zephyr_get_device_id(void) {
+ uint8_t dev_id[16] = { 0 };
+ static char dev_id_str[sizeof(dev_id) * 2 + 1];
+ static const char *dev_str = "UNKNOWN";
+
+// Obtain the device id
+#if defined(CONFIG_HWINFO)
+ ssize_t length = hwinfo_get_device_id(dev_id, sizeof(dev_id));
+#else
+ ssize_t length = 0;
+#endif
+
+ // If hwinfo_get_device_id() fails or isn't enabled, use a fallback string
+ if (length <= 0) {
+#if defined(CONFIG_SOC)
+ dev_str = CONFIG_SOC "-testserial";
+#else
+ dev_str = "testserial";
+#endif
+ length = strlen(dev_str);
+ } else {
+ // Render the obtained serial number in hexadecimal representation
+ for (size_t i = 0; i < length; i++) {
+ (void)snprintf(&dev_id_str[i * 2], sizeof(dev_id_str), "%02x", dev_id[i]);
+ }
+ dev_str = dev_id_str;
+ }
+
+ return dev_str;
+}
+
+MEMFAULT_WEAK void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) {
+ *info = (sMemfaultDeviceInfo){
+ .device_serial = memfault_zephyr_get_device_id(),
+ .software_type = CONFIG_MEMFAULT_BUILTIN_DEVICE_INFO_SOFTWARE_TYPE,
+ .software_version = CONFIG_MEMFAULT_BUILTIN_DEVICE_INFO_SOFTWARE_VERSION,
+ .hardware_version = CONFIG_MEMFAULT_BUILTIN_DEVICE_INFO_HARDWARE_VERSION,
+ };
+}
+
// Note: the function signature has changed here across zephyr releases
// "struct device *dev" -> "const struct device *dev"
//
diff --git a/scripts/memfault_gdb.py b/scripts/memfault_gdb.py
index 4b0fbb7..0d76ea6 100644
--- a/scripts/memfault_gdb.py
+++ b/scripts/memfault_gdb.py
@@ -286,7 +286,7 @@
# Memory map:
# https://github.com/espressif/esp-idf/blob/v3.3.1/components/soc/esp32/include/soc/soc.h#L286-L304
- regions = (
+ return (
# SOC_DRAM
(0x3FFAE000, 0x52000),
# SOC_RTC_DATA
@@ -294,7 +294,6 @@
# SOC_RTC_DRAM
(0x3FF80000, 0x2000),
)
- return regions
def _read_registers(self, core, gdb_thread, analytics_props):
# NOTE: The only way I could figure out to read raw registers for CPU1 was
@@ -734,7 +733,7 @@
self._write(out_f.write, total_size["size"])
-class Section(object):
+class Section(object): # noqa: PLW1641
def __init__(self, addr, size, name, read_only=True):
self.addr = addr
self.size = size