diff --git a/ports/nuttx/include/flash_map_backend.h b/ports/nuttx/include/flash_map_backend.h new file mode 100644 index 000000000..a2d4292ad --- /dev/null +++ b/ports/nuttx/include/flash_map_backend.h @@ -0,0 +1,432 @@ +/**************************************************************************** + * boot/nuttx/include/flash_map_backend/flash_map_backend.h + * + * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd. + * + * Licensed 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_FLASH_MAP_BACKEND_FLASH_MAP_BACKEND_H +#define __INCLUDE_FLASH_MAP_BACKEND_FLASH_MAP_BACKEND_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "memfault_platform_log_config.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define COREDUMP_STORAGE_FLASH 0 + +#define CONFIG_COREDUMP_STORAGE_FLASH_SLOT_PATH "/dev/coredump" + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Structure describing a flash area. */ + +struct flash_area +{ + /* MCUboot-API fields */ + + uint8_t fa_id; /* The slot/scratch ID */ + uint8_t fa_device_id; /* The device ID (usually there's only one) */ + uint16_t pad16; /* Padding */ + uint32_t fa_off; /* The flash offset from the beginning */ + uint32_t fa_size; /* The size of this sector */ + + /* NuttX implementation-specific fields */ + + const char *fa_mtd_path; /* Path for the MTD partition */ +}; + +/* Structure describing a sector within a flash area. */ + +struct flash_sector +{ + /* Offset of this sector, from the start of its flash area (not device). */ + + uint32_t fs_off; + + /* Size of this sector, in bytes. */ + + uint32_t fs_size; +}; + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mem_flash_area_get_id + * + * Description: + * Obtain the ID of a given flash area. + * + * Input Parameters: + * fa - Flash area. + * + * Returned Value: + * The ID of the requested flash area. + * + ****************************************************************************/ + +static inline uint8_t mem_flash_area_get_id(const struct flash_area *fa) +{ + return fa->fa_id; +} + +/**************************************************************************** + * Name: mem_flash_area_get_device_id + * + * Description: + * Obtain the ID of the device in which a given flash area resides on. + * + * Input Parameters: + * fa - Flash area. + * + * Returned Value: + * The device ID of the requested flash area. + * + ****************************************************************************/ + +static inline uint8_t mem_flash_area_get_device_id(const struct flash_area *fa) +{ + return fa->fa_device_id; +} + +/**************************************************************************** + * Name: mem_flash_area_get_off + * + * Description: + * Obtain the offset, from the beginning of a device, where a given flash + * area starts at. + * + * Input Parameters: + * fa - Flash area. + * + * Returned Value: + * The offset value of the requested flash area. + * + ****************************************************************************/ + +static inline uint32_t mem_flash_area_get_off(const struct flash_area *fa) +{ + return fa->fa_off; +} + +/**************************************************************************** + * Name: mem_flash_area_get_size + * + * Description: + * Obtain the size, from the offset, of a given flash area. + * + * Input Parameters: + * fa - Flash area. + * + * Returned Value: + * The size value of the requested flash area. + * + ****************************************************************************/ + +static inline uint32_t mem_flash_area_get_size(const struct flash_area *fa) +{ + return fa->fa_size; +} + +/**************************************************************************** + * Name: mem_flash_sector_get_off + * + * Description: + * Obtain the offset, from the beginning of its flash area, where a given + * flash sector starts at. + * + * Input Parameters: + * fs - Flash sector. + * + * Returned Value: + * The offset value of the requested flash sector. + * + ****************************************************************************/ + +static inline uint32_t mem_flash_sector_get_off(const struct flash_sector *fs) +{ + return fs->fs_off; +} + +/**************************************************************************** + * Name: mem_flash_sector_get_size + * + * Description: + * Obtain the size, from the offset, of a given flash sector. + * + * Input Parameters: + * fs - Flash sector. + * + * Returned Value: + * The size in bytes of the requested flash sector. + * + ****************************************************************************/ + +static inline uint32_t mem_flash_sector_get_size(const struct flash_sector *fs) +{ + return fs->fs_size; +} + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: mem_flash_area_open + * + * Description: + * Retrieve flash area from the flash map for a given partition. + * + * Input Parameters: + * fa_id - ID of the flash partition. + * + * Output Parameters: + * fa - Pointer which will contain the reference to flash_area. + * If ID is unknown, it will be NULL on output. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int mem_flash_area_open(uint8_t id, const struct flash_area **fa); + +/**************************************************************************** + * Name: mem_flash_area_close + * + * Description: + * Close a given flash area. + * + * Input Parameters: + * fa - Flash area to be closed. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void mem_flash_area_close(const struct flash_area *fa); + +/**************************************************************************** + * Name: mem_flash_area_read + * + * Description: + * Read data from flash area. + * Area readout boundaries are asserted before read request. API has the + * same limitation regarding read-block alignment and size as the + * underlying flash driver. + * + * Input Parameters: + * fa - Flash area to be read. + * off - Offset relative from beginning of flash area to be read. + * len - Number of bytes to read. + * + * Output Parameters: + * dst - Buffer to store read data. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int mem_flash_area_read(const struct flash_area *fa, uint32_t off, + void *dst, uint32_t len); + +/**************************************************************************** + * Name: mem_flash_area_write + * + * Description: + * Write data to flash area. + * Area write boundaries are asserted before write request. API has the + * same limitation regarding write-block alignment and size as the + * underlying flash driver. + * + * Input Parameters: + * fa - Flash area to be written. + * off - Offset relative from beginning of flash area to be written. + * src - Buffer with data to be written. + * len - Number of bytes to write. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int mem_flash_area_write(const struct flash_area *fa, uint32_t off, + const void *src, uint32_t len); + +/**************************************************************************** + * Name: mem_flash_area_erase + * + * Description: + * Erase a given flash area range. + * Area boundaries are asserted before erase request. API has the same + * limitation regarding erase-block alignment and size as the underlying + * flash driver. + * + * Input Parameters: + * fa - Flash area to be erased. + * off - Offset relative from beginning of flash area to be erased. + * len - Number of bytes to be erase. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int mem_flash_area_erase(const struct flash_area *fa, uint32_t off, + uint32_t len); + +/**************************************************************************** + * Name: mem_flash_area_align + * + * Description: + * Get write block size of the flash area. + * Write block size might be treated as read block size, although most + * drivers support unaligned readout. + * + * Input Parameters: + * fa - Flash area. + * + * Returned Value: + * Alignment restriction for flash writes in the given flash area. + * + ****************************************************************************/ + +uint8_t mem_flash_area_align(const struct flash_area *fa); + +/**************************************************************************** + * Name: mem_flash_area_erased_val + * + * Description: + * Get the value expected to be read when accessing any erased flash byte. + * This API is compatible with the MCUboot's porting layer. + * + * Input Parameters: + * fa - Flash area. + * + * Returned Value: + * Byte value of erased memory. + * + ****************************************************************************/ + +uint8_t mem_flash_area_erased_val(const struct flash_area *fa); + +/**************************************************************************** + * Name: mem_flash_area_get_sectors + * + * Description: + * Retrieve info about sectors within the area. + * + * Input Parameters: + * fa_id - ID of the flash area whose info will be retrieved. + * count - On input, represents the capacity of the sectors buffer. + * + * Output Parameters: + * count - On output, it shall contain the number of retrieved sectors. + * sectors - Buffer for sectors data. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int mem_flash_area_get_sectors(int fa_id, uint32_t *count, + struct flash_sector *sectors); + +/**************************************************************************** + * Name: mem_flash_area_id_from_multi_image_slot + * + * Description: + * Return the flash area ID for a given slot and a given image index + * (in case of a multi-image setup). + * + * Input Parameters: + * image_index - Index of the image. + * slot - Image slot, which may be 0 (primary) or 1 (secondary). + * + * Returned Value: + * Flash area ID (0 or 1), or negative value in case the requested slot + * is invalid. + * + ****************************************************************************/ + +int mem_flash_area_id_from_multi_image_slot(int image_index, int slot); + +/**************************************************************************** + * Name: mem_flash_area_id_from_image_slot + * + * Description: + * Return the flash area ID for a given slot. + * + * Input Parameters: + * slot - Image slot, which may be 0 (primary) or 1 (secondary). + * + * Returned Value: + * Flash area ID (0 or 1), or negative value in case the requested slot + * is invalid. + * + ****************************************************************************/ + +int mem_flash_area_id_from_image_slot(int slot); + +/**************************************************************************** + * Name: mem_flash_area_id_to_multi_image_slot + * + * Description: + * Convert the specified flash area ID and image index (in case of a + * multi-image setup) to an image slot index. + * + * Input Parameters: + * image_index - Index of the image. + * fa_id - Image slot, which may be 0 (primary) or 1 (secondary). + * + * Returned Value: + * Image slot index (0 or 1), or negative value in case ID doesn't + * correspond to an image slot. + * + ****************************************************************************/ + +int mem_flash_area_id_to_multi_image_slot(int image_index, int fa_id); + +/**************************************************************************** + * Name: mem_flash_area_id_from_image_offset + * + * Description: + * Return the flash area ID for a given image offset. + * + * Input Parameters: + * offset - Image offset. + * + * Returned Value: + * Flash area ID (0 or 1), or negative value in case the requested slot + * is invalid. + * + ****************************************************************************/ + +int mem_flash_area_id_from_image_offset(uint32_t offset); + +#endif /* __INCLUDE_FLASH_MAP_BACKEND_FLASH_MAP_BACKEND_H */ diff --git a/ports/nuttx/include/memfault_platform_config.h b/ports/nuttx/include/memfault_platform_config.h new file mode 100644 index 000000000..6861e042c --- /dev/null +++ b/ports/nuttx/include/memfault_platform_config.h @@ -0,0 +1,63 @@ +#pragma once + +//! @file memfault_platform_config.h +//! +//! Copyright 2022 Memfault, Inc +//! +//! Licensed 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. +//! +//! @brief +//! Platform overrides for the default configuration settings in the memfault-firmware-sdk. +//! Default configuration settings can be found in "memfault/config.h" + +/***************************************************************************** + * Included Files + *****************************************************************************/ + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// For example, decide if you want to use the Gnu Build ID. +// #define MEMFAULT_USE_GNU_BUILD_ID 1 + +#define MEMFAULT_PLATFORM_HAS_LOG_CONFIG 1 + +#define MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS 600 + +#define MEMFAULT_PLATFORM_COREDUMP_STORAGE_USE_FLASH 1 + +#ifdef __cplusplus +} +#endif + + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ \ No newline at end of file diff --git a/ports/nuttx/include/memfault_platform_log_config.h b/ports/nuttx/include/memfault_platform_log_config.h new file mode 100644 index 000000000..1336cf3bb --- /dev/null +++ b/ports/nuttx/include/memfault_platform_log_config.h @@ -0,0 +1,63 @@ +//! @file +//! +//! Copyright 2022 Memfault, Inc +//! +//! Licensed 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. +//! +//! @brief +//! Logging depends on how your configuration does logging. See +//! https://docs.memfault.com/docs/mcu/self-serve/#logging-dependency + + +/***************************************************************************** + * Included Files + *****************************************************************************/ + +#include + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +#define MEMFAULT_LOG_DEBUG(fmt, ...) syslog(LOG_DEBUG, "%s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__) +#define MEMFAULT_LOG_INFO(fmt, ...) syslog(LOG_INFO, "%s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__) +#define MEMFAULT_LOG_WARN(fmt, ...) syslog(LOG_WARNING, "%s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__) +#define MEMFAULT_LOG_ERROR(fmt, ...) syslog(LOG_ERR, "%s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__) + +#ifdef __cplusplus +} +#endif + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ \ No newline at end of file diff --git a/ports/nuttx/src/flash_map_backend.c b/ports/nuttx/src/flash_map_backend.c new file mode 100644 index 000000000..2c5963309 --- /dev/null +++ b/ports/nuttx/src/flash_map_backend.c @@ -0,0 +1,744 @@ +//! @file file_map_backend.c +//! +//! Copyright 2022 Memfault, Inc +//! +//! Licensed 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. +//! +//! Glue layer between the Memfault SDK and the Nuttx platform + +/***************************************************************************** + * Included Files + *****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "flash_map_backend.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ARRAYSIZE(x) (sizeof((x)) / sizeof((x)[0])) + +#define CONFIG_DEFAULT_FLASH_ERASE_STATE 0xff + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct flash_device_s +{ + /* Reference to the flash area configuration parameters */ + + struct flash_area *fa_cfg; + + /* Geometry characteristics of the underlying MTD device */ + + struct mtd_geometry_s mtdgeo; + + /* Partition information */ + + struct partition_info_s partinfo; + + int fd; /* File descriptor for an open flash area */ + int32_t refs; /* Reference counter */ + uint8_t erase_state; /* Byte value of the flash erased state */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct flash_area g_coredump_storage_area = +{ + .fa_id = COREDUMP_STORAGE_FLASH, + .fa_device_id = 0, + .fa_off = 0, + .fa_size = 0, + .fa_mtd_path = CONFIG_COREDUMP_STORAGE_FLASH_SLOT_PATH +}; + +static struct flash_device_s g_coredump_storage_priv = +{ + .fa_cfg = &g_coredump_storage_area, + .mtdgeo = + { + 0 + }, + .partinfo = + { + 0 + }, + .fd = -1, + .refs = 0, + .erase_state = CONFIG_DEFAULT_FLASH_ERASE_STATE +}; + + +static struct flash_device_s *g_flash_devices[] = +{ + &g_coredump_storage_priv, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: lookup_flash_device_by_id + * + * Description: + * Retrieve flash device from a given flash area ID. + * + * Input Parameters: + * fa_id - ID of the flash area. + * + * Returned Value: + * Reference to the found flash device, or NULL in case it does not exist. + * + ****************************************************************************/ + +static struct flash_device_s *lookup_flash_device_by_id(uint8_t fa_id) +{ + size_t i; + + for (i = 0; i < ARRAYSIZE(g_flash_devices); i++) + { + struct flash_device_s *dev = g_flash_devices[i]; + + if (fa_id == dev->fa_cfg->fa_id) + { + return dev; + } + } + + return NULL; +} + +/**************************************************************************** + * Name: lookup_flash_device_by_offset + * + * Description: + * Retrieve flash device from a given flash area offset. + * + * Input Parameters: + * offset - Offset of the flash area. + * + * Returned Value: + * Reference to the found flash device, or NULL in case it does not exist. + * + ****************************************************************************/ + +static struct flash_device_s *lookup_flash_device_by_offset(uint32_t offset) +{ + size_t i; + + for (i = 0; i < ARRAYSIZE(g_flash_devices); i++) + { + struct flash_device_s *dev = g_flash_devices[i]; + + if (offset == dev->fa_cfg->fa_off) + { + return dev; + } + } + + return NULL; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mem_flash_area_open + * + * Description: + * Retrieve flash area from the flash map for a given ID. + * + * Input Parameters: + * fa_id - ID of the flash area. + * + * Output Parameters: + * fa - Pointer which will contain the reference to flash_area. + * If ID is unknown, it will be NULL on output. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int mem_flash_area_open(uint8_t id, const struct flash_area **fa) +{ + struct flash_device_s *dev; + int fd; + int ret; + + MEMFAULT_LOG_ERROR("ID:%" PRIu8, id); + + dev = lookup_flash_device_by_id(id); + if (dev == NULL) + { + MEMFAULT_LOG_ERROR("Undefined flash area: %d", (int)id); + + return ERROR; + } + + *fa = dev->fa_cfg; + + if (dev->refs++ > 0) + { + MEMFAULT_LOG_ERROR("Flash area ID %d already open, count: %d (+)", + (int)id, (int)dev->refs); + + return OK; + } + + fd = open(dev->fa_cfg->fa_mtd_path, O_RDWR); + if (fd < 0) + { + int errcode = errno; + + MEMFAULT_LOG_ERROR("Error opening MTD device: %d", errcode); + + goto errout; + } + + ret = ioctl(fd, MTDIOC_GEOMETRY, (unsigned long)((uintptr_t)&dev->mtdgeo)); + if (ret < 0) + { + int errcode = errno; + + MEMFAULT_LOG_ERROR("Error retrieving MTD device geometry: %d", errcode); + + goto errout_with_fd; + } + + ret = ioctl(fd, BIOC_PARTINFO, (unsigned long)((uintptr_t)&dev->partinfo)); + if (ret < 0) + { + int errcode = errno; + + MEMFAULT_LOG_ERROR("Error retrieving MTD partition info: %d", errcode); + + goto errout_with_fd; + } + + ret = ioctl(fd, MTDIOC_ERASESTATE, + (unsigned long)((uintptr_t)&dev->erase_state)); + if (ret < 0) + { + int errcode = errno; + + MEMFAULT_LOG_ERROR("Error retrieving MTD device erase state: %d", errcode); + + goto errout_with_fd; + } + + dev->fa_cfg->fa_off = dev->partinfo.startsector * dev->partinfo.sectorsize; + dev->fa_cfg->fa_size = dev->partinfo.numsectors * dev->partinfo.sectorsize; + + MEMFAULT_LOG_ERROR("Flash area offset: 0x%" PRIx32, dev->fa_cfg->fa_off); + MEMFAULT_LOG_ERROR("Flash area size: %" PRIu32, dev->fa_cfg->fa_size); + MEMFAULT_LOG_ERROR("MTD erase state: 0x%" PRIx8, dev->erase_state); + + dev->fd = fd; + + MEMFAULT_LOG_ERROR("Flash area %d open, count: %d (+)", id, (int)dev->refs); + + return OK; + +errout_with_fd: + close(fd); + +errout: + --dev->refs; + + return ERROR; +} + +/**************************************************************************** + * Name: flash_area_close + * + * Description: + * Close a given flash area. + * + * Input Parameters: + * fa - Flash area to be closed. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void mem_flash_area_close(const struct flash_area *fa) +{ + MEMFAULT_LOG_ERROR("ID:%" PRIu8, fa->fa_id); + + struct flash_device_s *dev = lookup_flash_device_by_id(fa->fa_id); + + DEBUGASSERT(dev != NULL); + + if (dev->refs == 0) + { + /* No need to close an unopened flash area, avoid an overflow of the + * counter. + */ + + return; + } + + MEMFAULT_LOG_ERROR("Close request for flash area %" PRIu8 ", count: %d (-)", + fa->fa_id, (int)dev->refs); + + if (--dev->refs == 0) + { + close(dev->fd); + dev->fd = -1; + + MEMFAULT_LOG_ERROR("Flash area %" PRIu8 " closed", fa->fa_id); + } +} + +/**************************************************************************** + * Name: mem_flash_area_read + * + * Description: + * Read data from flash area. + * Area readout boundaries are asserted before read request. API has the + * same limitation regarding read-block alignment and size as the + * underlying flash driver. + * + * Input Parameters: + * fa - Flash area to be read. + * off - Offset relative from beginning of flash area to be read. + * len - Number of bytes to read. + * + * Output Parameters: + * dst - Buffer to store read data. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int mem_flash_area_read(const struct flash_area *fa, uint32_t off, + void *dst, uint32_t len) +{ + struct flash_device_s *dev; + off_t seekpos; + ssize_t nbytes; + + MEMFAULT_LOG_ERROR("ID:%" PRIu8 " offset:%" PRIu32 " length:%" PRIu32, + fa->fa_id, off, len); + + dev = lookup_flash_device_by_id(fa->fa_id); + + DEBUGASSERT(dev != NULL); + + if (off + len > fa->fa_size) + { + MEMFAULT_LOG_ERROR("Attempt to read out of flash area bounds"); + + return ERROR; + } + + /* Reposition the file offset from the beginning of the flash area */ + + seekpos = lseek(dev->fd, (off_t)off, SEEK_SET); + if (seekpos != (off_t)off) + { + int errcode = errno; + + MEMFAULT_LOG_ERROR("Seek to offset %" PRIu32 " failed: %d", off, errcode); + + return ERROR; + } + + /* Read the flash block into memory */ + + nbytes = read(dev->fd, dst, len); + if (nbytes < 0) + { + int errcode = errno; + + MEMFAULT_LOG_ERROR("Read from %s failed: %d", fa->fa_mtd_path, errcode); + + return ERROR; + } + + return OK; +} + +/**************************************************************************** + * Name: mem_flash_area_write + * + * Description: + * Write data to flash area. + * Area write boundaries are asserted before write request. API has the + * same limitation regarding write-block alignment and size as the + * underlying flash driver. + * + * Input Parameters: + * fa - Flash area to be written. + * off - Offset relative from beginning of flash area to be written. + * src - Buffer with data to be written. + * len - Number of bytes to write. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int mem_flash_area_write(const struct flash_area *fa, uint32_t off, + const void *src, uint32_t len) +{ + struct flash_device_s *dev; + off_t seekpos; + ssize_t nbytes; + + MEMFAULT_LOG_ERROR("ID:%" PRIu8 " offset:%" PRIu32 " length:%" PRIu32, + fa->fa_id, off, len); + + dev = lookup_flash_device_by_id(fa->fa_id); + + DEBUGASSERT(dev != NULL); + + if (off + len > fa->fa_size) + { + MEMFAULT_LOG_ERROR("Attempt to write out of flash area bounds"); + + return ERROR; + } + + /* Reposition the file offset from the beginning of the flash area */ + + seekpos = lseek(dev->fd, (off_t)off, SEEK_SET); + if (seekpos != (off_t)off) + { + int errcode = errno; + + MEMFAULT_LOG_ERROR("Seek to offset %" PRIu32 " failed: %d", off, errcode); + + return ERROR; + } + + /* Write the buffer to the flash block */ + + nbytes = write(dev->fd, src, len); + if (nbytes < 0) + { + int errcode = errno; + + MEMFAULT_LOG_ERROR("Write to %s failed: %d", fa->fa_mtd_path, errcode); + + return ERROR; + } + + return OK; +} + +/**************************************************************************** + * Name: mem_flash_area_erase + * + * Description: + * Erase a given flash area range. + * Area boundaries are asserted before erase request. API has the same + * limitation regarding erase-block alignment and size as the underlying + * flash driver. + * + * Input Parameters: + * fa - Flash area to be erased. + * off - Offset relative from beginning of flash area to be erased. + * len - Number of bytes to be erase. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int mem_flash_area_erase(const struct flash_area *fa, uint32_t off, uint32_t len) +{ + int ret; + void *buffer; + size_t i; + struct flash_device_s *dev = lookup_flash_device_by_id(fa->fa_id); + const size_t sector_size = dev->mtdgeo.erasesize; + const uint8_t erase_val = dev->erase_state; + + MEMFAULT_LOG_ERROR("ID:%" PRIu8 " offset:%" PRIu32 " length:%" PRIu32, + fa->fa_id, off, len); + + buffer = malloc(sector_size); + if (buffer == NULL) + { + MEMFAULT_LOG_ERROR("Failed to allocate erase buffer"); + + return ERROR; + } + + memset(buffer, erase_val, sector_size); + + i = 0; + + do + { + MEMFAULT_LOG_DEBUG("Erasing %lu bytes at offset %" PRIu32, + (unsigned long)sector_size, off + i); + + ret = mem_flash_area_write(fa, off + i, buffer, sector_size); + i += sector_size; + } + while (ret == OK && i < (len - sector_size)); + + if (ret == OK) + { + MEMFAULT_LOG_DEBUG("Erasing %lu bytes at offset %" PRIu32, + (unsigned long)(len - i), off + i); + + ret = mem_flash_area_write(fa, off + i, buffer, len - i); + } + + free(buffer); + + return ret; +} + +/**************************************************************************** + * Name: mem_flash_area_align + * + * Description: + * Get write block size of the flash area. + * Write block size might be treated as read block size, although most + * drivers support unaligned readout. + * + * Input Parameters: + * fa - Flash area. + * + * Returned Value: + * Alignment restriction for flash writes in the given flash area. + * + ****************************************************************************/ + +uint8_t mem_flash_area_align(const struct flash_area *fa) +{ + /* MTD access alignment is handled by the character and block device + * drivers. + */ + + const uint8_t minimum_write_length = 1; + + MEMFAULT_LOG_ERROR("ID:%" PRIu8 " align:%" PRIu8, + fa->fa_id, minimum_write_length); + + return minimum_write_length; +} + +/**************************************************************************** + * Name: mem_flash_area_erased_val + * + * Description: + * Get the value expected to be read when accessing any erased flash byte. + * This API is compatible with the MCUboot's porting layer. + * + * Input Parameters: + * fa - Flash area. + * + * Returned Value: + * Byte value of erased memory. + * + ****************************************************************************/ + +uint8_t mem_flash_area_erased_val(const struct flash_area *fa) +{ + struct flash_device_s *dev; + uint8_t erased_val; + + dev = lookup_flash_device_by_id(fa->fa_id); + + DEBUGASSERT(dev != NULL); + + erased_val = dev->erase_state; + + MEMFAULT_LOG_ERROR("ID:%" PRIu8 " erased_val:0x%" PRIx8, fa->fa_id, erased_val); + + return erased_val; +} + +/**************************************************************************** + * Name: mem_flash_area_get_sectors + * + * Description: + * Retrieve info about sectors within the area. + * + * Input Parameters: + * fa_id - ID of the flash area whose info will be retrieved. + * count - On input, represents the capacity of the sectors buffer. + * + * Output Parameters: + * count - On output, it shall contain the number of retrieved sectors. + * sectors - Buffer for sectors data. + * + * Returned Value: + * Zero on success, or negative value in case of error. + * + ****************************************************************************/ + +int mem_flash_area_get_sectors(int fa_id, uint32_t *count, + struct flash_sector *sectors) +{ + size_t off; + uint32_t total_count = 0; + struct flash_device_s *dev = lookup_flash_device_by_id(fa_id); + const size_t sector_size = dev->mtdgeo.erasesize; + const struct flash_area *fa = fa = dev->fa_cfg; + + for (off = 0; off < fa->fa_size; off += sector_size) + { + /* Note: Offset here is relative to flash area, not device */ + + sectors[total_count].fs_off = off; + sectors[total_count].fs_size = sector_size; + total_count++; + } + + *count = total_count; + + DEBUGASSERT(total_count == dev->mtdgeo.neraseblocks); + + MEMFAULT_LOG_ERROR("ID:%d count:%" PRIu32, fa_id, *count); + + return OK; +} + +/**************************************************************************** + * Name: mem_flash_area_id_from_multi_image_slot + * + * Description: + * Return the flash area ID for a given slot and a given image index + * (in case of a multi-image setup). + * + * Input Parameters: + * image_index - Index of the image. + * slot - Image slot, which may be 0 (primary) or 1 (secondary). + * + * Returned Value: + * Flash area ID (0 or 1), or negative value in case the requested slot + * is invalid. + * + ****************************************************************************/ + +int mem_flash_area_id_from_multi_image_slot(int image_index, int slot) +{ + MEMFAULT_LOG_ERROR("image_index:%d slot:%d", image_index, slot); + + switch (slot) + { + case 0: + return COREDUMP_STORAGE_FLASH; + } + + MEMFAULT_LOG_ERROR("Unexpected Request: image_index:%d, slot:%d", + image_index, slot); + + return ERROR; /* flash_area_open will fail on that */ +} + +/**************************************************************************** + * Name: mem_flash_area_id_from_image_slot + * + * Description: + * Return the flash area ID for a given slot. + * + * Input Parameters: + * slot - Image slot, which may be 0 (primary) or 1 (secondary). + * + * Returned Value: + * Flash area ID (0 or 1), or negative value in case the requested slot + * is invalid. + * + ****************************************************************************/ + +int mem_flash_area_id_from_image_slot(int slot) +{ + MEMFAULT_LOG_ERROR("slot:%d", slot); + + return mem_flash_area_id_from_multi_image_slot(0, slot); +} + +/**************************************************************************** + * Name: mem_flash_area_id_to_multi_image_slot + * + * Description: + * Convert the specified flash area ID and image index (in case of a + * multi-image setup) to an image slot index. + * + * Input Parameters: + * image_index - Index of the image. + * fa_id - Image slot, which may be 0 (primary) or 1 (secondary). + * + * Returned Value: + * Image slot index (0 or 1), or negative value in case ID doesn't + * correspond to an image slot. + * + ****************************************************************************/ + +int mem_flash_area_id_to_multi_image_slot(int image_index, int fa_id) +{ + MEMFAULT_LOG_ERROR("image_index:%d fa_id:%d", image_index, fa_id); + + if (fa_id == COREDUMP_STORAGE_FLASH) + { + return 0; + } + + MEMFAULT_LOG_ERROR("Unexpected Request: image_index:%d, fa_id:%d", + image_index, fa_id); + + return ERROR; /* flash_area_open will fail on that */ +} + +/**************************************************************************** + * Name: mem_flash_area_id_from_image_offset + * + * Description: + * Return the flash area ID for a given image offset. + * + * Input Parameters: + * offset - Image offset. + * + * Returned Value: + * Flash area ID (0 or 1), or negative value in case the requested slot + * is invalid. + * + ****************************************************************************/ + +int mem_flash_area_id_from_image_offset(uint32_t offset) +{ + struct flash_device_s *dev = lookup_flash_device_by_offset(offset); + + MEMFAULT_LOG_ERROR("offset:%" PRIu32, offset); + + return dev->fa_cfg->fa_id; +} diff --git a/ports/nuttx/src/memfault_platform_boot.c b/ports/nuttx/src/memfault_platform_boot.c new file mode 100644 index 000000000..3d672b2a0 --- /dev/null +++ b/ports/nuttx/src/memfault_platform_boot.c @@ -0,0 +1,93 @@ +//! @file memfault_platform_boot.c +//! +//! Copyright 2022 Memfault, Inc +//! +//! Licensed 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. +//! +//! Glue layer between the Memfault SDK and the Nuttx platform + +/***************************************************************************** + * Included Files + *****************************************************************************/ + +#include "memfault/components.h" +#include "memfault/ports/reboot_reason.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: memfault_platform_boot + ****************************************************************************/ + +/** + * @brief Main memfault platform function. Should be called as soon as + * posible at OS initialization phase. + * + * @return int + */ + +int memfault_platform_boot(void) { + + memfault_build_info_dump(); + memfault_device_info_dump(); + memfault_platform_reboot_tracking_boot(); + + // initialize the event storage buffer + static uint8_t s_event_storage[1024]; + const sMemfaultEventStorageImpl *evt_storage = + memfault_events_storage_boot(s_event_storage, sizeof(s_event_storage)); + + // configure trace events to store into the buffer + memfault_trace_event_boot(evt_storage); + + // record the current reboot reason + memfault_reboot_tracking_collect_reset_info(evt_storage); + + // configure the metrics component to store into the buffer + sMemfaultMetricBootInfo boot_info = { + .unexpected_reboot_count = memfault_reboot_tracking_get_crash_count(), + }; + memfault_metrics_boot(evt_storage, &boot_info); + + MEMFAULT_LOG_INFO("Memfault Initialized!"); + + return 0; +} diff --git a/ports/nuttx/src/memfault_platform_flash_coredump_storage.c b/ports/nuttx/src/memfault_platform_flash_coredump_storage.c new file mode 100644 index 000000000..98d33e198 --- /dev/null +++ b/ports/nuttx/src/memfault_platform_flash_coredump_storage.c @@ -0,0 +1,192 @@ +//! @file +//! +//! Copyright (c) Memfault, Inc. +//! See License.txt for details +//! +//! Reference implementation of platform dependency functions to use a sector of internal flash +//! for coredump capture +//! +//! To use, update your linker script (.ld file) to expose information about the location to use. +//! For example, using the last sector of the STM32F429I (2MB dual banked flash) would look +//! something like this: +//! +//! MEMORY +//! { +//! /* ... other regions ... */ +//! COREDUMP_STORAGE_FLASH (rx) : ORIGIN = 0x81E0000, LENGTH = 128K +//! } +//! __MemfaultCoreStorageStart = ORIGIN(COREDUMP_STORAGE_FLASH); +//! __MemfaultCoreStorageEnd = ORIGIN(COREDUMP_STORAGE_FLASH) + LENGTH(COREDUMP_STORAGE_FLASH); +//! __MemfaultCoreStorageSectorNumber = 23; + +#include "memfault/panics/coredump.h" + +#include + +#include +#include + +#include "memfault/config.h" +#include "memfault/core/compiler.h" +#include "memfault/core/math.h" +#include "memfault/core/platform/core.h" +#include "memfault/panics/platform/coredump.h" + +#include "flash_map_backend.h" + +#ifndef MEMFAULT_COREDUMP_STORAGE_START_ADDR +extern uint32_t __MemfaultCoreStorageStart[]; +#define MEMFAULT_COREDUMP_STORAGE_START_ADDR ((uint32_t)__MemfaultCoreStorageStart) +#endif + +#ifndef MEMFAULT_COREDUMP_STORAGE_END_ADDR +extern uint32_t __MemfaultCoreStorageEnd[]; +#define MEMFAULT_COREDUMP_STORAGE_END_ADDR ((uint32_t)__MemfaultCoreStorageEnd) +#endif + +#ifndef MEMFAULT_COREDUMP_STORAGE_FLASH_SECTOR_NUMBER +//! The sector number to write to. This ID can be found in the "Embedded Flash memory" +//! section of the reference manual for the STM32 family +extern uint32_t __MemfaultCoreStorageSectorNumber[]; +#define MEMFAULT_COREDUMP_STORAGE_FLASH_SECTOR_NUMBER ((uint32_t)__MemfaultCoreStorageSectorNumber) +#endif + +// Error writing to flash - should never happen & likely detects a configuration error +// Call the reboot handler which will halt the device if a debugger is attached and then reboot +MEMFAULT_NO_OPT +static void prv_coredump_writer_assert_and_reboot(int error_code) { + memfault_platform_halt_if_debugging(); + memfault_platform_reboot(); +} + +static bool prv_op_within_flash_bounds(uint32_t offset, size_t data_len) { + sMfltCoredumpStorageInfo info = { 0 }; + memfault_platform_coredump_storage_get_info(&info); + return (offset + data_len) <= info.size; +} + +void memfault_platform_coredump_storage_clear(void) { + const uint32_t data = 0x0; + memfault_platform_coredump_storage_write(0, &data, sizeof(data)); +} + +void memfault_platform_coredump_storage_get_info(sMfltCoredumpStorageInfo *info) { + const size_t size = + MEMFAULT_COREDUMP_STORAGE_END_ADDR - MEMFAULT_COREDUMP_STORAGE_START_ADDR; + + *info = (sMfltCoredumpStorageInfo) { + .size = size, + .sector_size = size, + }; +} + +bool memfault_platform_coredump_storage_write(uint32_t offset, const void *data, + size_t data_len) { + /*if + (!prv_op_within_flash_bounds(offset, data_len)) { + return false; + } + + const uint32_t start_addr = MEMFAULT_COREDUMP_STORAGE_START_ADDR + offset; + HAL_FLASH_Unlock(); + { + const uint8_t *datap = data; + for (size_t i = 0; i < data_len; i++) { + const uint32_t res = HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, start_addr + i, datap[i]); + if (res != HAL_OK) { + prv_coredump_writer_assert_and_reboot(res); + } + } + } + HAL_FLASH_Lock(); + return true; + */ + return true; +} + +bool memfault_platform_coredump_storage_read(uint32_t offset, void *data, + size_t read_len) { + + int ret; + static const struct flash_area *fa; + + ret = mem_flash_area_open(COREDUMP_STORAGE_FLASH, &fa); + if(OK != ret) { + return false; + } + + ret = mem_flash_area_read(fa, offset, data, read_len); + if(OK != ret) { + return false; + } + + mem_flash_area_close(fa); + if(OK != ret) { + return false; + } + + /* + if (!prv_op_within_flash_bounds(offset, read_len)) { + return false; + } + + // The internal flash is memory mapped so we can just use memcpy! + const uint32_t start_addr = MEMFAULT_COREDUMP_STORAGE_START_ADDR; + memcpy(data, (void *)(start_addr + offset), read_len); + return true; + */ + return true; +} + +bool memfault_platform_coredump_storage_erase(uint32_t offset, size_t erase_size) { + + int ret; + static const struct flash_area *fa; + + ret = mem_flash_area_open(COREDUMP_STORAGE_FLASH, &fa); + if(OK != ret) { + return false; + } + + ret = mem_flash_area_erase(fa, offset, erase_size); + if(OK != ret) { + return false; + } + + mem_flash_area_close(fa); + if(OK != ret) { + return false; + } + + /* + if (!prv_op_within_flash_bounds(offset, erase_size)) { + return false; + } + + FLASH_EraseInitTypeDef s_erase_cfg = { + .TypeErase = FLASH_TYPEERASE_SECTORS, + // Only needs to be provided for Mass Erase + .Banks = 0, + .Sector = MEMFAULT_COREDUMP_STORAGE_FLASH_SECTOR_NUMBER, + .NbSectors = 1, + + // See "Program/erase parallelism" in TRM. By using the lowest parallelism + // the driver will work over the entire voltage range supported by the MCU + // (1.8 - 3.6V). Exact time ranges for sector erases can be found in the + // "Flash memory programming" section of the device datasheet + .VoltageRange = FLASH_VOLTAGE_RANGE_1 + }; + uint32_t SectorError = 0; + HAL_FLASH_Unlock(); + { + int res = HAL_FLASHEx_Erase(&s_erase_cfg, &SectorError); + if (res != HAL_OK) { + prv_coredump_writer_assert_and_reboot(res); + } + } + HAL_FLASH_Lock(); + + return true; + */ + return true; +} \ No newline at end of file diff --git a/ports/nuttx/src/memfault_platform_info.c b/ports/nuttx/src/memfault_platform_info.c new file mode 100644 index 000000000..0595ebc35 --- /dev/null +++ b/ports/nuttx/src/memfault_platform_info.c @@ -0,0 +1,134 @@ +//! @file memfault_platform_info.c +//! +//! Copyright 2022 Memfault, Inc +//! +//! Licensed 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. +//! +//! Glue layer between the Memfault SDK and the Nuttx platform + +/***************************************************************************** + * Included Files + *****************************************************************************/ + +#include "memfault/components.h" +#include "memfault/ports/reboot_reason.h" + +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +#ifndef CONFIG_MEMFAULT_DEVICE_SERIAL +#define CONFIG_MEMFAULT_DEVICE_SERIAL "DEMOSERIAL" +#endif + +#ifndef CONFIG_MEMFAULT_HARDWARE_VERSION +#define CONFIG_MEMFAULT_HARDWARE_VERSION "app-fw" +#endif + +#ifndef CONFIG_MEMFAULT_SOFTWARE_TYPE +#define CONFIG_MEMFAULT_SOFTWARE_TYPE "1.0.0" +#endif + +#ifndef CONFIG_MEMFAULT_SOFTWARE_VERSION +#define CONFIG_MEMFAULT_SOFTWARE_VERSION "dvt1" +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: prv_int_to_ascii_hex + ****************************************************************************/ + +/** + * @brief Convert integers into ascii hex + * + * @param val + * @return char + */ +#ifdef CONFIG_BOARDCTL_UNIQUEID +static char prv_int_to_ascii_hex(uint8_t val) { + return val < 10 ? (char)val + '0' : (char)(val - 10) + 'A'; +} +#endif +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: memfault_platform_get_device_info + ****************************************************************************/ + +/** + * @brief Get device info form Nuttx platform + * + * @param info + */ + +void memfault_platform_get_device_info(sMemfaultDeviceInfo *info) { + +#ifdef CONFIG_BOARDCTL_UNIQUEID + + static char s_device_serial[CONFIG_BOARDCTL_UNIQUEID_SIZE * 2 + 1]; + uint8_t uniqueid[CONFIG_BOARDCTL_UNIQUEID_SIZE]; + + if (boardctl(BOARDIOC_UNIQUEID, (uintptr_t)&uniqueid) != OK) + { + MEMFAULT_LOG_ERROR("Board unique id failed\n"); + } + + for (size_t i = 0; i < CONFIG_BOARDCTL_UNIQUEID_SIZE ; i++) + { + const uint8_t val = uniqueid[i]; + const int curr_idx = i * 2; + s_device_serial[curr_idx] = prv_int_to_ascii_hex((val >> 4) & 0xf); + s_device_serial[curr_idx + 1] = prv_int_to_ascii_hex(val & 0xf); + } + + *info = (sMemfaultDeviceInfo) + { + .device_serial = s_device_serial, + .software_type = CONFIG_MEMFAULT_SOFTWARE_TYPE, + .software_version = CONFIG_MEMFAULT_SOFTWARE_VERSION, + .hardware_version = CONFIG_MEMFAULT_HARDWARE_VERSION, + }; + +#else + + *info = (sMemfaultDeviceInfo) + { + .device_serial = CONFIG_MEMFAULT_DEVICE_SERIAL, + .software_type = CONFIG_MEMFAULT_SOFTWARE_TYPE, + .software_version = CONFIG_MEMFAULT_SOFTWARE_VERSION, + .hardware_version = CONFIG_MEMFAULT_HARDWARE_VERSION, + }; + +#endif +} + diff --git a/ports/nuttx/src/memfault_platform_metrics.c b/ports/nuttx/src/memfault_platform_metrics.c new file mode 100644 index 000000000..31e27a73d --- /dev/null +++ b/ports/nuttx/src/memfault_platform_metrics.c @@ -0,0 +1,168 @@ +//! @file memfault_platform_metrics +//! +//! Copyright 2022 Memfault, Inc +//! +//! Licensed 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. +//! +//! Glue layer between the Memfault SDK and the Nuttx platform + + +/***************************************************************************** + * Included Files + *****************************************************************************/ + +#include "memfault/components.h" +#include "memfault/ports/reboot_reason.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/** + * @brief Static variable used to store the memfault passed callback + * + */ +static MemfaultPlatformTimerCallback *metric_timer_handler; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: memfault_platform_metric_timer_dispatch + ****************************************************************************/ + +/** + * @brief Memfault callback function dispatcher + * + * @param handler + */ + +void memfault_platform_metric_timer_dispatch(MemfaultPlatformTimerCallback handler) { + if (handler == NULL) { + return; + } + handler(); +} + +/**************************************************************************** + * Name: platform_metrics_timer_handler + ****************************************************************************/ + +/** + * @brief Timer handler + * + * @param signo + */ + +static void platform_metrics_timer_handler(int signo) { + + // Check signo + if (signo == SIGALRM) + { + memfault_reboot_tracking_reset_crash_count(); + memfault_platform_metric_timer_dispatch(metric_timer_handler); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: memfault_platform_metrics_timer_boot + ****************************************************************************/ + +/** + * @brief Creates a timer that executes each period_sec seconds and calls + * the callback function. + * + * @param period_sec Period in seconds for each execution of the timer + * @param callback Called function on timer expiration. + * @return true + * @return false + */ + +bool memfault_platform_metrics_timer_boot(uint32_t period_sec, + MemfaultPlatformTimerCallback callback) { + struct sigaction act; + struct sigaction oact; + struct sigevent notify; + struct itimerspec timer; + timer_t timerid; + int status; + + metric_timer_handler = (MemfaultPlatformTimerCallback *)callback; + + // Set timer timeout action + act.sa_handler = &platform_metrics_timer_handler; + act.sa_flags = SA_SIGINFO; + (void)sigfillset(&act.sa_mask); + (void)sigdelset(&act.sa_mask, SIGALRM); + status = sigaction(SIGALRM, &act, &oact); + if (status != OK) + { + MEMFAULT_LOG_ERROR("Memfault metrics timer sigaction failed, status=%d\n", status); + return ERROR; + } + + // Create the POSIX timer + notify.sigev_notify = SIGEV_SIGNAL; + notify.sigev_signo = SIGALRM; + notify.sigev_value.sival_int = 0; + +#ifdef CONFIG_SIG_EVTHREAD + + notify.sigev_notify_function = NULL; + notify.sigev_notify_attributes = NULL; + +#endif + + status = timer_create(CLOCK_MONOTONIC, ¬ify, &timerid); + if (status != OK) { + MEMFAULT_LOG_ERROR("Memfault timer creation failed, errno=%d\n", errno); + return ERROR; + } + + // Start the POSIX timer + timer.it_value.tv_sec = (10); + timer.it_value.tv_nsec = (0); + timer.it_interval.tv_sec = (period_sec); + timer.it_interval.tv_nsec = (0); + status = timer_settime(timerid, 0, &timer, NULL); + if (status != OK) + { + MEMFAULT_LOG_ERROR("Memfault timer start failed, errno=%d\n", errno); + return ERROR; + } + + return true; +} diff --git a/ports/nuttx/src/memfault_platform_reboot_tracking.c b/ports/nuttx/src/memfault_platform_reboot_tracking.c new file mode 100644 index 000000000..14b49568e --- /dev/null +++ b/ports/nuttx/src/memfault_platform_reboot_tracking.c @@ -0,0 +1,153 @@ +//! @file memfault_platform_reboot_tracking.c +//! +//! Copyright 2022 Memfault, Inc +//! +//! Licensed 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. +//! +//! Glue layer between the Memfault SDK and the Nuttx platform + +/***************************************************************************** + * Included Files + *****************************************************************************/ + +#include "memfault/ports/reboot_reason.h" + +#include + +#include "memfault/config.h" +#include "memfault/core/debug_log.h" +#include "memfault/core/reboot_reason_types.h" +#include "memfault/core/sdk_assert.h" + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +#if MEMFAULT_ENABLE_REBOOT_DIAG_DUMP +#define MEMFAULT_PRINT_RESET_INFO(...) MEMFAULT_LOG_INFO(__VA_ARGS__) +#else +#define MEMFAULT_PRINT_RESET_INFO(...) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +MEMFAULT_PUT_IN_SECTION(".noinit.mflt_reboot_tracking") +static uint8_t s_reboot_tracking[MEMFAULT_REBOOT_TRACKING_REGION_SIZE]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: memfault_reboot_reason_get + ****************************************************************************/ + +/** + * @brief Transform Nuttx provided reset reasons into memfault + * compatible ones. + * + * @param info + */ + +void memfault_reboot_reason_get(sResetBootupInfo *info) { + + uint32_t reset_reason_reg = 0x0; + eMemfaultRebootReason reset_reason = kMfltRebootReason_UnknownError; + +#ifdef CONFIG_BOARDCTL_RESET_CAUSE + + struct boardioc_reset_cause_s cause; + if (boardctl(BOARDIOC_RESET_CAUSE, (uintptr_t)&cause) != OK) + { + syslog(LOG_ERR, "Failed to get reset cause!\n"); + } + + reset_reason_reg = (uint32_t)cause.cause; + + MEMFAULT_LOG_INFO("Reset Reason, GetResetReason=0x%" PRIx32, reset_reason_reg); + MEMFAULT_PRINT_RESET_INFO("Reset Causes: "); + + switch (reset_reason_reg) { + case BOARDIOC_RESETCAUSE_SYS_CHIPPOR: + MEMFAULT_PRINT_RESET_INFO(" Power on Reset"); + reset_reason = kMfltRebootReason_PowerOnReset; + break; + case BOARDIOC_RESETCAUSE_PIN: + MEMFAULT_PRINT_RESET_INFO(" User Reset"); + reset_reason = kMfltRebootReason_UserReset; + break; + case BOARDIOC_RESETCAUSE_SYS_BOR: + MEMFAULT_PRINT_RESET_INFO(" Brown out"); + reset_reason = kMfltRebootReason_BrownOutReset; + break; + case BOARDIOC_RESETCAUSE_CPU_SOFT: + case BOARDIOC_RESETCAUSE_CORE_SOFT: + MEMFAULT_PRINT_RESET_INFO(" Software"); + reset_reason = kMfltRebootReason_SoftwareReset; + break; + case BOARDIOC_RESETCAUSE_SYS_RWDT: + case BOARDIOC_RESETCAUSE_CORE_MWDT: + case BOARDIOC_RESETCAUSE_CPU_MWDT: + case BOARDIOC_RESETCAUSE_CORE_RWDT: + case BOARDIOC_RESETCAUSE_CPU_RWDT: + MEMFAULT_PRINT_RESET_INFO(" Watchdog"); + reset_reason = kMfltRebootReason_HardwareWatchdog; + break; + case BOARDIOC_RESETCAUSE_LOWPOWER: + MEMFAULT_PRINT_RESET_INFO(" Low Power Exit"); + reset_reason = kMfltRebootReason_LowPower; + break; + case BOARDIOC_RESETCAUSE_CORE_DPSP: + MEMFAULT_PRINT_RESET_INFO(" Deep Sleep"); + reset_reason = kMfltRebootReason_DeepSleep; + break; + case BOARDIOC_RESETCAUSE_NONE: + case BOARDIOC_RESETCAUSE_UNKOWN: + default: + MEMFAULT_PRINT_RESET_INFO(" Unknown"); + reset_reason = kMfltRebootReason_Unknown; + break; + } + +#endif + + *info = (sResetBootupInfo) { + .reset_reason_reg = reset_reason_reg, + .reset_reason = reset_reason, + }; +} + +/**************************************************************************** + * Name: memfault_platform_reboot_tracking_boot + ****************************************************************************/ + +/** + * @brief Tracking of reoboot causes. + * + */ + +void memfault_platform_reboot_tracking_boot(void) { + sResetBootupInfo reset_info = { 0 }; + memfault_reboot_reason_get(&reset_info); + memfault_reboot_tracking_boot(s_reboot_tracking, &reset_info); +} \ No newline at end of file diff --git a/ports/nuttx/src/memfault_platform_time.c b/ports/nuttx/src/memfault_platform_time.c new file mode 100644 index 000000000..204a246e2 --- /dev/null +++ b/ports/nuttx/src/memfault_platform_time.c @@ -0,0 +1,113 @@ +//! @file memfault_platform_time.c +//! +//! Copyright 2022 Memfault, Inc +//! +//! Licensed 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. +//! +//! Glue layer between the Memfault SDK and the Nuttx platform + +/***************************************************************************** + * Included Files + *****************************************************************************/ + +#include "memfault/components.h" +#include "memfault/ports/reboot_reason.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: memfault_platform_time_get_current + ****************************************************************************/ + +/** + * @brief Get time since epoch. + * + * @param time_val + * @return true + * @return false + */ + +bool memfault_platform_time_get_current(sMemfaultCurrentTime *time_val) { + + time_t seconds_since_epoch; + + if(time(&seconds_since_epoch) == (time_t)ERROR) + { + MEMFAULT_LOG_ERROR("Current calendar time is not available."); + return false; + } + + *time_val = (sMemfaultCurrentTime) { + .type = kMemfaultCurrentTimeType_UnixEpochTimeSec, + .info = { + .unix_timestamp_secs = seconds_since_epoch + }, + }; + + return true; +} + +/**************************************************************************** + * Name: memfault_platform_time_get_current + ****************************************************************************/ + +/** + * @brief Get time since boot. + * + * @return uint64_t + */ + +uint64_t memfault_platform_get_time_since_boot_ms(void) { + + uint64_t time; + struct timespec tp; + + if(clock_gettime(CLOCK_MONOTONIC, &tp) != OK) + { + MEMFAULT_LOG_ERROR("Time since boot not available."); + return time = 0; + } + + time = (((uint64_t) tp.tv_sec) << 32) | ((uint64_t) tp.tv_nsec / NSEC_PER_MSEC); + + return time; +} + diff --git a/ports/nuttx/src/memfault_platform_unimplemented.c b/ports/nuttx/src/memfault_platform_unimplemented.c new file mode 100644 index 000000000..1d2bc9e92 --- /dev/null +++ b/ports/nuttx/src/memfault_platform_unimplemented.c @@ -0,0 +1,133 @@ +//! @file memfault_platform_uninmplemented.c +//! +//! Copyright 2022 Memfault, Inc +//! +//! Licensed 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. +//! +//! Glue layer between the Memfault SDK and the Nuttx platform + +/***************************************************************************** + * Included Files + *****************************************************************************/ + +#include "memfault/components.h" +#include "memfault/ports/reboot_reason.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Preprocessor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: memfault_platform_halt_if_debugging + ****************************************************************************/ + +/** + * @brief memfault_platform_halt_if_debugging + * TODO: Not yet implemented + * + * @return MEMFAULT_WEAK + */ + +MEMFAULT_WEAK +void memfault_platform_halt_if_debugging(void) { + // volatile uint32_t *dhcsr = (volatile uint32_t *)0xE000EDF0; + // const uint32_t c_debugen_mask = 0x1; + + // if ((*dhcsr & c_debugen_mask) == 0) { + // // no debugger is attached so return since issuing a breakpoint instruction would trigger a + // // fault + // return; + // } + + // // NB: A breakpoint with value 'M' (77) for easy disambiguation from other breakpoints that may + // // be used by the system. + // MEMFAULT_BREAKPOINT(77); +} + +/**************************************************************************** + * Name: memfault_platform_sanitize_address_range + ****************************************************************************/ + +/** + * @brief memfault_platform_sanitize_address_range + * TODO: Not yet implemented + * + * @param start_addr + * @param desired_size + * @return size_t + */ + +size_t memfault_platform_sanitize_address_range(void *start_addr, size_t desired_size) { + // static const struct { + // uint32_t start_addr; + // size_t length; + // } s_mcu_mem_regions[] = { + // // !FIXME: Update with list of valid memory banks to collect in a coredump + // {.start_addr = 0x00000000, .length = 0xFFFFFFFF}, + // }; + + // for (size_t i = 0; i < MEMFAULT_ARRAY_SIZE(s_mcu_mem_regions); i++) { + // const uint32_t lower_addr = s_mcu_mem_regions[i].start_addr; + // const uint32_t upper_addr = lower_addr + s_mcu_mem_regions[i].length; + // if ((uint32_t)start_addr >= lower_addr && ((uint32_t)start_addr < upper_addr)) { + // return MEMFAULT_MIN(desired_size, upper_addr - (uint32_t)start_addr); + // } + // } + + return 0; +} + +/**************************************************************************** + * Name: memfault_platform_reboot + ****************************************************************************/ + +/** + * @brief memfault_platform_reboot + * TODO: Not yet implemented + */ + +//! Last function called after a coredump is saved. Should perform +//! any final cleanup and then reset the device +void memfault_platform_reboot(void) { + // !FIXME: Perform any final system cleanup here + + // !FIXME: Reset System + // NVIC_SystemReset() + while (1) { } // unreachable +} \ No newline at end of file