|
| 1 | +//! @file |
| 2 | +//! |
| 3 | +//! Copyright (c) Memfault, Inc. |
| 4 | +//! See License.txt for details |
| 5 | +//! |
| 6 | +//! @brief |
| 7 | +//! Memfault SDK self test functions to verify SDK integration |
| 8 | +#include <ctype.h> |
| 9 | +#include <stdbool.h> |
| 10 | +#include <string.h> |
| 11 | + |
| 12 | +#include "memfault/core/build_info.h" |
| 13 | +#include "memfault/core/debug_log.h" |
| 14 | +#include "memfault/core/math.h" |
| 15 | +#include "memfault/core/platform/device_info.h" |
| 16 | +#include "memfault/core/self_test.h" |
| 17 | +#include "memfault_reboot_tracking_private.h" |
| 18 | +#include "memfault_self_test_private.h" |
| 19 | + |
| 20 | +typedef enum { |
| 21 | + kDeviceInfoField_DeviceSerial = 0, |
| 22 | + kDeviceInfoField_SoftwareType, |
| 23 | + kDeviceInfoField_SoftwareVersion, |
| 24 | + kDeviceInfoField_HardwareVersion, |
| 25 | + kDeviceInfoField_BuildId, |
| 26 | + kDeviceInfoField_Max, |
| 27 | +} eDeviceInfoField; |
| 28 | + |
| 29 | +static const char *s_device_info_field_names[] = { |
| 30 | + [kDeviceInfoField_DeviceSerial] = "Device Serial", |
| 31 | + [kDeviceInfoField_SoftwareType] = "Software Type", |
| 32 | + [kDeviceInfoField_SoftwareVersion] = "Software Version", |
| 33 | + [kDeviceInfoField_HardwareVersion] = "Hardware Version", |
| 34 | + [kDeviceInfoField_BuildId] = "Build ID", |
| 35 | +}; |
| 36 | + |
| 37 | +typedef bool (*FieldCharValidator)(unsigned char c); |
| 38 | + |
| 39 | +static const FieldCharValidator s_device_info_str_validators[] = { |
| 40 | + [kDeviceInfoField_DeviceSerial] = memfault_self_test_valid_device_serial, |
| 41 | + [kDeviceInfoField_SoftwareType] = memfault_self_test_valid_hw_version_sw_type, |
| 42 | + [kDeviceInfoField_SoftwareVersion] = memfault_self_test_valid_sw_version, |
| 43 | + [kDeviceInfoField_HardwareVersion] = memfault_self_test_valid_hw_version_sw_type, |
| 44 | + NULL, |
| 45 | +}; |
| 46 | + |
| 47 | +MEMFAULT_STATIC_ASSERT(MEMFAULT_ARRAY_SIZE(s_device_info_field_names) == kDeviceInfoField_Max, |
| 48 | + "Mismatch in field name table, must be equal"); |
| 49 | +MEMFAULT_STATIC_ASSERT(MEMFAULT_ARRAY_SIZE(s_device_info_str_validators) == kDeviceInfoField_Max, |
| 50 | + "Mismatch in string validator table, must be equal"); |
| 51 | + |
| 52 | +static bool prv_validate_string(const char *str, size_t len, FieldCharValidator is_valid) { |
| 53 | + for (size_t idx = 0; idx < len; ++idx) { |
| 54 | + unsigned char c = (unsigned char)str[idx]; |
| 55 | + if (!is_valid(c)) { |
| 56 | + MEMFAULT_LOG_ERROR("Invalid char %c, found in %s", c, str); |
| 57 | + return false; |
| 58 | + } |
| 59 | + } |
| 60 | + return true; |
| 61 | +} |
| 62 | + |
| 63 | +static bool is_field_valid(const char *str, eDeviceInfoField field) { |
| 64 | + if (str == NULL) { |
| 65 | + return false; |
| 66 | + } |
| 67 | + // First validate min and max length |
| 68 | + // Max + 1 needed determine if we exceeded bounds before NULL found |
| 69 | + size_t len = strnlen(str, MEMFAULT_DEVICE_INFO_MAX_STRING_SIZE + 1); |
| 70 | + if ((len < 1) || (len > MEMFAULT_DEVICE_INFO_MAX_STRING_SIZE)) { |
| 71 | + MEMFAULT_LOG_ERROR("Invalid length %zu for %s", len, s_device_info_field_names[field]); |
| 72 | + return false; |
| 73 | + } |
| 74 | + |
| 75 | + if (field < kDeviceInfoField_Max) { |
| 76 | + FieldCharValidator validator = s_device_info_str_validators[field]; |
| 77 | + return (validator != NULL) ? prv_validate_string(str, len, validator) : false; |
| 78 | + } else { |
| 79 | + MEMFAULT_LOG_ERROR("Invalid device info string type %u for %s", field, str); |
| 80 | + return false; |
| 81 | + } |
| 82 | +} |
| 83 | + |
| 84 | +static uint32_t prv_validate_device_info_field(const char *str, eDeviceInfoField field) { |
| 85 | + uint32_t result = 0; |
| 86 | + if (!is_field_valid(str, field)) { |
| 87 | + result = (uint32_t)(1 << field); |
| 88 | + } |
| 89 | + |
| 90 | + return result; |
| 91 | +} |
| 92 | + |
| 93 | +static uint32_t prv_validate_device_info(void) { |
| 94 | + sMemfaultDeviceInfo device_info = { 0 }; |
| 95 | + memfault_platform_get_device_info(&device_info); |
| 96 | + uint32_t results = 0; |
| 97 | + |
| 98 | + // Validate each field in device_info |
| 99 | + results |= |
| 100 | + prv_validate_device_info_field(device_info.device_serial, kDeviceInfoField_DeviceSerial); |
| 101 | + |
| 102 | + results |= |
| 103 | + prv_validate_device_info_field(device_info.hardware_version, kDeviceInfoField_HardwareVersion); |
| 104 | + |
| 105 | + results |= |
| 106 | + prv_validate_device_info_field(device_info.software_version, kDeviceInfoField_SoftwareVersion); |
| 107 | + |
| 108 | + results |= |
| 109 | + prv_validate_device_info_field(device_info.software_type, kDeviceInfoField_SoftwareType); |
| 110 | + |
| 111 | + return results; |
| 112 | +} |
| 113 | + |
| 114 | +static uint32_t prv_validate_build_id(void) { |
| 115 | + char build_id_str[(MEMFAULT_BUILD_ID_LEN * 2) + 1] = {0}; |
| 116 | + uint32_t result = 0; |
| 117 | + |
| 118 | + if (!memfault_build_id_get_string(build_id_str, MEMFAULT_ARRAY_SIZE(build_id_str))) { |
| 119 | + result = (uint32_t)(1 << kDeviceInfoField_BuildId); |
| 120 | + } |
| 121 | + |
| 122 | + return result; |
| 123 | +} |
| 124 | + |
| 125 | +static void prv_device_info_test_describe(uint32_t results) { |
| 126 | + MEMFAULT_LOG_INFO("Device Info Test Results"); |
| 127 | + MEMFAULT_LOG_INFO("------------------------"); |
| 128 | + |
| 129 | + if (results == 0) { |
| 130 | + MEMFAULT_LOG_INFO("All fields valid"); |
| 131 | + return; |
| 132 | + } |
| 133 | + |
| 134 | + MEMFAULT_LOG_ERROR("One or more fields is invalid. Check for correct length and contents"); |
| 135 | + for (uint8_t i = 0; i < kDeviceInfoField_Max; i++) { |
| 136 | + // Check if bit cleared, cleared bits indicate an invalid field |
| 137 | + if ((results & (1 << i))) { |
| 138 | + MEMFAULT_LOG_ERROR("%s invalid", s_device_info_field_names[i]); |
| 139 | + } |
| 140 | + } |
| 141 | +} |
| 142 | + |
| 143 | +uint32_t memfault_self_test_device_info_test(void) { |
| 144 | + uint32_t results = 0; |
| 145 | + // Validate the build ID |
| 146 | + results |= prv_validate_build_id(); |
| 147 | + // Valid device info fields |
| 148 | + results |= prv_validate_device_info(); |
| 149 | + |
| 150 | + prv_device_info_test_describe(results); |
| 151 | + return results; |
| 152 | +} |
| 153 | + |
| 154 | +int memfault_self_test_run(void) { |
| 155 | + // Run each test one at a time and return result of all runs OR'd together |
| 156 | + return (memfault_self_test_device_info_test() != 0); |
| 157 | +} |
0 commit comments