mirror of
https://github.com/google/pebble.git
synced 2025-07-13 01:41:52 -04:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
78
src/fw/system/bootbits.c
Normal file
78
src/fw/system/bootbits.c
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "system/bootbits.h"
|
||||
|
||||
#include "drivers/rtc.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/version.h"
|
||||
|
||||
#define STM32F2_COMPATIBLE
|
||||
#define STM32F4_COMPATIBLE
|
||||
#define STM32F7_COMPATIBLE
|
||||
#include <mcu.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void boot_bit_init(void) {
|
||||
rtc_init();
|
||||
|
||||
if (!boot_bit_test(BOOT_BIT_INITIALIZED)) {
|
||||
RTC_WriteBackupRegister(RTC_BKP_BOOTBIT_DR, BOOT_BIT_INITIALIZED);
|
||||
}
|
||||
}
|
||||
|
||||
void boot_bit_set(BootBitValue bit) {
|
||||
uint32_t current_value = RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR);
|
||||
current_value |= bit;
|
||||
RTC_WriteBackupRegister(RTC_BKP_BOOTBIT_DR, current_value);
|
||||
}
|
||||
|
||||
void boot_bit_clear(BootBitValue bit) {
|
||||
uint32_t current_value = RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR);
|
||||
current_value &= ~bit;
|
||||
RTC_WriteBackupRegister(RTC_BKP_BOOTBIT_DR, current_value);
|
||||
}
|
||||
|
||||
bool boot_bit_test(BootBitValue bit) {
|
||||
uint32_t current_value = RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR);
|
||||
return (current_value & bit);
|
||||
}
|
||||
|
||||
void boot_bit_dump(void) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "0x%"PRIx32, RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR));
|
||||
}
|
||||
|
||||
uint32_t boot_bits_get(void) {
|
||||
return RTC_ReadBackupRegister(RTC_BKP_BOOTBIT_DR);
|
||||
}
|
||||
|
||||
void command_boot_bits_get(void) {
|
||||
char buffer[32];
|
||||
dbgserial_putstr_fmt(buffer, sizeof(buffer), "bootbits: 0x%"PRIu32, boot_bits_get());
|
||||
}
|
||||
|
||||
void boot_version_write(void) {
|
||||
if (boot_version_read() == TINTIN_METADATA.version_timestamp) {
|
||||
return;
|
||||
}
|
||||
RTC_WriteBackupRegister(BOOTLOADER_VERSION_REGISTER, TINTIN_METADATA.version_timestamp);
|
||||
}
|
||||
|
||||
uint32_t boot_version_read(void) {
|
||||
return RTC_ReadBackupRegister(BOOTLOADER_VERSION_REGISTER);
|
||||
}
|
59
src/fw/system/bootbits.h
Normal file
59
src/fw/system/bootbits.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "system/rtc_registers.h"
|
||||
|
||||
typedef enum BootBitValue {
|
||||
BOOT_BIT_INITIALIZED = 0x1 << 0,
|
||||
BOOT_BIT_NEW_FW_AVAILABLE = 0x1 << 1,
|
||||
BOOT_BIT_NEW_FW_UPDATE_IN_PROGRESS = 0x1 << 2,
|
||||
BOOT_BIT_FW_START_FAIL_STRIKE_ONE = 0x1 << 3,
|
||||
BOOT_BIT_FW_START_FAIL_STRIKE_TWO = 0x1 << 4,
|
||||
BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_ONE = 0x1 << 5,
|
||||
BOOT_BIT_RECOVERY_LOAD_FAIL_STRIKE_TWO = 0x1 << 6,
|
||||
BOOT_BIT_RECOVERY_START_IN_PROGRESS = 0x1 << 7,
|
||||
BOOT_BIT_STANDBY_MODE_REQUESTED = 0x1 << 8, //!< Bootloader enter standby immediately after reset.
|
||||
BOOT_BIT_SOFTWARE_FAILURE_OCCURRED = 0x1 << 9,
|
||||
BOOT_BIT_NEW_SYSTEM_RESOURCES_AVAILABLE = 0x1 << 10,
|
||||
BOOT_BIT_RESET_LOOP_DETECT_ONE = 0x1 << 11,
|
||||
BOOT_BIT_RESET_LOOP_DETECT_TWO = 0x1 << 12,
|
||||
BOOT_BIT_RESET_LOOP_DETECT_THREE = 0x1 << 13,
|
||||
BOOT_BIT_FW_STABLE = 0x1 << 14,
|
||||
BOOT_BIT_NEW_FW_INSTALLED = 0x1 << 15,
|
||||
BOOT_BIT_STANDBY_MODE_ENTERED = 0x1 << 16,
|
||||
BOOT_BIT_FORCE_PRF = 0x1 << 17,
|
||||
BOOT_BIT_NEW_PRF_AVAILABLE = 0x1 << 18,
|
||||
BOOT_BIT_SHUTDOWN_REQUESTED = 0x1 << 19, //!< Bootloader hard power-off instead of jumping to fw.
|
||||
BOOT_BIT_BOOTLOADER_TEST_A = 0x1 << 30,
|
||||
BOOT_BIT_BOOTLOADER_TEST_B = 0x1 << 31,
|
||||
} BootBitValue;
|
||||
|
||||
void boot_bit_init();
|
||||
void boot_bit_set(BootBitValue bit);
|
||||
void boot_bit_clear(BootBitValue bit);
|
||||
bool boot_bit_test(BootBitValue bit);
|
||||
|
||||
// Dump the contents to PBL_LOG
|
||||
void boot_bit_dump(void);
|
||||
uint32_t boot_bits_get(void);
|
||||
|
||||
void boot_version_write(void);
|
||||
uint32_t boot_version_read(void);
|
55
src/fw/system/die.c
Normal file
55
src/fw/system/die.c
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "drivers/vibe.h"
|
||||
#include "kernel/core_dump.h"
|
||||
#include "kernel/logging_private.h"
|
||||
#include "kernel/pulse_logging.h"
|
||||
#include "system/bootbits.h"
|
||||
#include "system/passert.h"
|
||||
#include "system/reboot_reason.h"
|
||||
#include "system/reset.h"
|
||||
|
||||
#define CMSIS_COMPATIBLE
|
||||
#include <mcu.h>
|
||||
|
||||
#if defined(NO_WATCHDOG)
|
||||
#include "FreeRTOS.h"
|
||||
#include "debug/setup.h"
|
||||
#endif
|
||||
|
||||
NORETURN reset_due_to_software_failure(void) {
|
||||
// Make sure vibration is off
|
||||
vibe_force_off();
|
||||
|
||||
#if PULSE_EVERYWHERE
|
||||
pulse_logging_log_buffer_flush();
|
||||
#endif
|
||||
|
||||
#if defined(NO_WATCHDOG)
|
||||
// Don't reset right away, leave it in a state we can inspect
|
||||
|
||||
enable_mcu_debugging();
|
||||
__disable_irq();
|
||||
while (1) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
PBL_LOG_FROM_FAULT_HANDLER("Resetting!");
|
||||
boot_bit_set(BOOT_BIT_SOFTWARE_FAILURE_OCCURRED);
|
||||
system_reset();
|
||||
}
|
24
src/fw/system/die.h
Normal file
24
src/fw/system/die.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/attributes.h"
|
||||
|
||||
//! Does not call reboot_reason_set, only calls reboot_reason_set_restarted_safely if we were
|
||||
//! able shut everything down nicely before rebooting.
|
||||
NORETURN reset_due_to_software_failure(void);
|
||||
|
54
src/fw/system/firmware_storage.c
Normal file
54
src/fw/system/firmware_storage.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "firmware_storage.h"
|
||||
|
||||
#include "drivers/flash.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
FirmwareDescription firmware_storage_read_firmware_description(uint32_t firmware_start_address) {
|
||||
FirmwareDescription firmware_description;
|
||||
flash_read_bytes((uint8_t*) &firmware_description, firmware_start_address,
|
||||
sizeof(FirmwareDescription));
|
||||
|
||||
|
||||
return firmware_description;
|
||||
}
|
||||
|
||||
bool firmware_storage_check_valid_firmware_description(
|
||||
uint32_t start_address, const FirmwareDescription *firmware_description) {
|
||||
|
||||
if (firmware_description->description_length != sizeof(FirmwareDescription)) {
|
||||
// Corrupted description
|
||||
return false;
|
||||
}
|
||||
|
||||
// Log around this operation, as it can take some time (hundreds of ms)
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "CRCing recovery...");
|
||||
|
||||
start_address += sizeof(FirmwareDescription);
|
||||
#if CAPABILITY_HAS_DEFECTIVE_FW_CRC
|
||||
const uint32_t calculated_crc = flash_calculate_legacy_defective_checksum(
|
||||
start_address, firmware_description->firmware_length);
|
||||
#else
|
||||
const uint32_t calculated_crc = flash_crc32(start_address, firmware_description->firmware_length);
|
||||
#endif
|
||||
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "CRCing recovery... done");
|
||||
|
||||
return calculated_crc == firmware_description->checksum;
|
||||
}
|
||||
|
37
src/fw/system/firmware_storage.h
Normal file
37
src/fw/system/firmware_storage.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
//! @file firmware_storage.h
|
||||
//! Utilities for reading a firmware image stored in flash.
|
||||
|
||||
#include "util/attributes.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct PACKED FirmwareDescription {
|
||||
uint32_t description_length;
|
||||
uint32_t firmware_length;
|
||||
uint32_t checksum;
|
||||
} FirmwareDescription;
|
||||
|
||||
FirmwareDescription firmware_storage_read_firmware_description(uint32_t firmware_start_address);
|
||||
|
||||
bool firmware_storage_check_valid_firmware_description(
|
||||
uint32_t firmware_start_address, const FirmwareDescription* firmware_description);
|
||||
|
44
src/fw/system/hexdump.c
Normal file
44
src/fw/system/hexdump.c
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "system/hexdump.h"
|
||||
#include "system/logging.h"
|
||||
#include "util/hexdump.h"
|
||||
#include "console/prompt.h"
|
||||
|
||||
void hexdump_log(int level, const uint8_t *data, size_t length) {
|
||||
PBL_HEXDUMP_D(LOG_DOMAIN_MISC, level, data, length);
|
||||
}
|
||||
|
||||
void hexdump_using_serial(int level, const char *src_filename, int src_line_number,
|
||||
const char *line_buffer) {
|
||||
dbgserial_putstr(line_buffer);
|
||||
}
|
||||
|
||||
void hexdump_using_prompt(int level, const char *src_filename, int src_line_number,
|
||||
const char *line_buffer) {
|
||||
prompt_send_response(line_buffer);
|
||||
}
|
||||
|
||||
void hexdump_using_pbllog(int level, const char *src_filename, int src_line_number,
|
||||
const char *line_buffer) {
|
||||
pbl_log_sync(level, src_filename, src_line_number, "%s", line_buffer);
|
||||
}
|
||||
|
||||
void hexdump_log_src(const char *src_filename, int src_line_number, int level,
|
||||
const uint8_t *data, size_t length, HexdumpLineCallback cb) {
|
||||
hexdump(src_filename, src_line_number, level, data, length, cb);
|
||||
}
|
50
src/fw/system/hexdump.h
Normal file
50
src/fw/system/hexdump.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/hexdump.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void hexdump_log(int level, const uint8_t *data, size_t length);
|
||||
void hexdump_log_src(const char *src_filename, int src_line_number, int level,
|
||||
const uint8_t *data, size_t length, HexdumpLineCallback cb);
|
||||
|
||||
void hexdump_using_serial(int level, const char *src_filename, int src_line_number,
|
||||
const char *line_buffer);
|
||||
void hexdump_using_prompt(int level, const char *src_filename, int src_line_number,
|
||||
const char *line_buffer);
|
||||
void hexdump_using_pbllog(int level, const char *src_filename, int src_line_number,
|
||||
const char *line_buffer);
|
||||
|
||||
#ifdef PBL_LOG_ENABLED
|
||||
#define PBL_HEXDUMP_D_SERIAL(level, data, length) \
|
||||
hexdump_log_src(__FILE_NAME__, __LINE__, level, data, length, hexdump_using_serial)
|
||||
#define PBL_HEXDUMP_D_PROMPT(level, data, length) \
|
||||
hexdump_log_src(__FILE_NAME__, __LINE__, level, data, length, hexdump_using_prompt)
|
||||
#define PBL_HEXDUMP_D(domain, level, data, length) \
|
||||
if (domain) hexdump_log_src(__FILE_NAME__, __LINE__, level, data, length, hexdump_using_pbllog)
|
||||
#define PBL_HEXDUMP(level, data, length) \
|
||||
hexdump_log_src(__FILE_NAME__, __LINE__, level, data, length, hexdump_using_pbllog);
|
||||
#else
|
||||
#define PBL_HEXDUMP_D_SERIAL(level, data, length)
|
||||
#define PBL_HEXDUMP_D(domain, level, data, length)
|
||||
#define PBL_HEXDUMP(level, data, length)
|
||||
#define PBL_HEXDUMP_D_PROMPT(level, data, length)
|
||||
#endif
|
407
src/fw/system/logging.h
Normal file
407
src/fw/system/logging.h
Normal file
|
@ -0,0 +1,407 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "console/dbgserial.h"
|
||||
#include "system/die.h"
|
||||
#include "system/reboot_reason.h"
|
||||
#include "system/status_codes.h"
|
||||
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef __FILE_NAME__
|
||||
#define __FILE_NAME__ __FILE__
|
||||
#endif
|
||||
|
||||
#define SPLIT_64_BIT_ARG(x) (uint32_t)((x >> 32) & 0xFFFFFFFF), (uint32_t)(x & 0xFFFFFFFF)
|
||||
|
||||
#define LOG_BUFFER_LENGTH 128
|
||||
|
||||
// Minimum amount of stack space required for vsniprintf
|
||||
#define LOGGING_MIN_STACK_FOR_SPRINTF 240
|
||||
#define LOGGING_STACK_FULL_MSG ((const char *)" [STK FULL]")
|
||||
|
||||
void pbl_log_hashed_async(const uint32_t packed_loghash, ...);
|
||||
|
||||
void pbl_log_hashed_sync(const uint32_t packed_loghash, ...);
|
||||
|
||||
// Core Number must be shifted to the correct position.
|
||||
void pbl_log_hashed_core(const uint32_t core_number, const uint32_t packed_loghash, ...);
|
||||
|
||||
// Core Number must be shifted to the correct position.
|
||||
void pbl_log_hashed_vargs(const bool async, const uint32_t core_number,
|
||||
const uint32_t packed_loghash, va_list fmt_args);
|
||||
|
||||
void pbl_log_vargs(uint8_t log_level, const char *src_filename, int src_line_number,
|
||||
const char *fmt, va_list args);
|
||||
|
||||
void pbl_log(uint8_t log_level, const char* src_filename, int src_line_number, const char* fmt, ...)
|
||||
FORMAT_PRINTF(4, 5);
|
||||
|
||||
void pbl_log_sync(uint8_t log_level, const char* src_filename, int src_line_number, const char* fmt, ...)
|
||||
FORMAT_PRINTF(4, 5);
|
||||
|
||||
int pbl_log_binary_format(char* buffer, int buffer_len, const uint8_t log_level,
|
||||
const char* src_filename_path, int src_line_number, const char* fmt, va_list args);
|
||||
|
||||
int pbl_log_get_bin_format(char* buffer, int buffer_len, const uint8_t log_level,
|
||||
const char* src_filename_path, int src_line_number, const char* fmt, ...);
|
||||
|
||||
#define LOG_LEVEL_ALWAYS 0
|
||||
#define LOG_LEVEL_ERROR 1
|
||||
#define LOG_LEVEL_WARNING 50
|
||||
#define LOG_LEVEL_INFO 100
|
||||
#define LOG_LEVEL_DEBUG 200
|
||||
#define LOG_LEVEL_DEBUG_VERBOSE 255
|
||||
|
||||
#ifdef PBL_LOGS_HASHED
|
||||
#include <logging/log_hashing.h>
|
||||
#endif
|
||||
|
||||
#define LOG_COLOR_BLACK "BLACK" // Not so useful in general
|
||||
#define LOG_COLOR_RED "RED"
|
||||
#define LOG_COLOR_GREEN "GREEN"
|
||||
#define LOG_COLOR_YELLOW "YELLOW"
|
||||
#define LOG_COLOR_BLUE "BLUE"
|
||||
#define LOG_COLOR_MAGENTA "MAGENTA"
|
||||
#define LOG_COLOR_CYAN "CYAN"
|
||||
#define LOG_COLOR_GREY "GREY"
|
||||
// Reserved for bold. Use sparingly
|
||||
#define LOG_COLOR_LIGHT_GREY "LIGHT_GREY"
|
||||
#define LOG_COLOR_LIGHT_RED "LIGHT_RED"
|
||||
#define LOG_COLOR_LIGHT_GREEN "LIGHT_GREEN"
|
||||
#define LOG_COLOR_LIGHT_YELLOW "LIGHT_YELLOW"
|
||||
#define LOG_COLOR_LIGHT_BLUE "LIGHT_BLUE"
|
||||
#define LOG_COLOR_LIGHT_MAGENTA "LIGHT_MAGENTA"
|
||||
#define LOG_COLOR_LIGHT_CYAN "LIGHT_CYAN"
|
||||
#define LOG_COLOR_WHITE "WHITE"
|
||||
|
||||
// Allow the default color for a src file to be set by defining FILE_LOG_COLOR
|
||||
// Can be called directly with PBL_LOG_COLOR
|
||||
#ifdef FILE_LOG_COLOR
|
||||
#define DEFAULT_LOG_COLOR FILE_LOG_COLOR
|
||||
#else
|
||||
#define DEFAULT_LOG_COLOR LOG_COLOR_GREY
|
||||
#endif
|
||||
|
||||
#define LOG_DOMAIN_MISC 1
|
||||
#define LOG_DOMAIN_BT_CORE 1
|
||||
#define LOG_DOMAIN_FS 1
|
||||
#define LOG_DOMAIN_COMM 1
|
||||
#define LOG_DOMAIN_ACCEL 0
|
||||
#define LOG_DOMAIN_TEXT 0
|
||||
#define LOG_DOMAIN_QEMU_COMM 0
|
||||
#define LOG_DOMAIN_ANIMATION 0
|
||||
#define LOG_DOMAIN_ANALYTICS 0
|
||||
#define LOG_DOMAIN_ACTIVITY 0
|
||||
#define LOG_DOMAIN_ACTIVITY_INSIGHTS 0
|
||||
#define LOG_DOMAIN_PROTOBUF 0
|
||||
#if defined(VOICE_DEBUG)
|
||||
#define LOG_DOMAIN_VOICE 1
|
||||
#else
|
||||
#define LOG_DOMAIN_VOICE 0
|
||||
#endif
|
||||
|
||||
#define LOG_DOMAIN_BLOBDB 0
|
||||
|
||||
#ifndef LOG_DOMAIN_BT_ISPP
|
||||
#define LOG_DOMAIN_BT_ISPP 0
|
||||
#endif
|
||||
|
||||
#ifndef LOG_DOMAIN_BT_SDP
|
||||
#define LOG_DOMAIN_BT_SDP 0
|
||||
#endif
|
||||
|
||||
#ifndef LOG_DOMAIN_BT_GAP
|
||||
#define LOG_DOMAIN_BT_GAP 0
|
||||
#endif
|
||||
|
||||
#ifndef LOG_DOMAIN_BT_PROFILES
|
||||
#define LOG_DOMAIN_BT_PROFILES 0
|
||||
#endif
|
||||
|
||||
#ifndef LOG_DOMAIN_BT_PAIRING_INFO
|
||||
#if !RELEASE
|
||||
#define LOG_DOMAIN_BT_PAIRING_INFO 1
|
||||
#else
|
||||
#define LOG_DOMAIN_BT_PAIRING_INFO 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef LOG_DOMAIN_BT_SNIFF
|
||||
#define LOG_DOMAIN_BT_SNIFF 0
|
||||
#endif
|
||||
|
||||
#ifndef LOG_DOMAIN_BT_HCI
|
||||
#define LOG_DOMAIN_BT_HCI 0
|
||||
#endif
|
||||
|
||||
#ifndef LOG_DOMAIN_BLE // Not grouped with BT classic
|
||||
#define LOG_DOMAIN_BLE 0
|
||||
#endif
|
||||
|
||||
#ifndef LOG_DOMAIN_DATA_LOGGING
|
||||
#define LOG_DOMAIN_DATA_LOGGING 0
|
||||
#endif
|
||||
|
||||
#ifndef LOG_DOMAIN_BLE_CORE // Not included with BLE
|
||||
#define LOG_DOMAIN_BLE_CORE 0
|
||||
#endif
|
||||
|
||||
#ifndef LOG_DOMAIN_BLE_GAP // Not included with BLE
|
||||
#define LOG_DOMAIN_BLE_GAP 0
|
||||
#endif
|
||||
|
||||
#ifndef LOG_DOMAIN_BLE_SM // Not included with BLE
|
||||
#define LOG_DOMAIN_BLE_SM 0
|
||||
#endif
|
||||
|
||||
#ifdef LOG_DOMAIN_ALL // Turn on all domains that are off by default
|
||||
#define LOG_DOMAIN_BT
|
||||
#endif
|
||||
|
||||
#ifdef LOG_DOMAIN_BT
|
||||
#define LOG_DOMAIN_BT_PROFILES 1
|
||||
#define LOG_DOMAIN_BT_HCI 1
|
||||
#define LOG_DOMAIN_BT_SNIFF 1
|
||||
#else
|
||||
#define LOG_DOMAIN_BT 0
|
||||
#endif
|
||||
|
||||
#if LOG_DOMAIN_BT_PROFILES
|
||||
#define LOG_DOMAIN_BT_ISPP 1
|
||||
#define LOG_DOMAIN_BT_SDP 1
|
||||
#define LOG_DOMAIN_BT_GAP 1
|
||||
#endif
|
||||
|
||||
#ifndef LOG_DOMAIN_TOUCH
|
||||
#define LOG_DOMAIN_TOUCH 0
|
||||
#endif
|
||||
|
||||
#ifndef LOG_DOMAIN_I2C
|
||||
#define LOG_DOMAIN_I2C 0
|
||||
#endif
|
||||
|
||||
#ifndef STRINGIFY
|
||||
#define STRINGIFY_NX(a) #a
|
||||
#define STRINGIFY(a) STRINGIFY_NX(a)
|
||||
#endif // STRINGIFY
|
||||
|
||||
#define STATUS_STRING(s) STRINGIFY(s)
|
||||
|
||||
#ifndef DEFAULT_LOG_DOMAIN
|
||||
#define DEFAULT_LOG_DOMAIN LOG_DOMAIN_MISC
|
||||
#endif // DEFAULT_LOG_DOMAIN
|
||||
|
||||
#if defined(PLATFORM_TINTIN) && !defined(TARGET_QEMU)
|
||||
#define PBL_SHOULD_LOG(level) ((level) < LOG_LEVEL_DEBUG)
|
||||
#else
|
||||
#define PBL_SHOULD_LOG(level) (true)
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef PBL_LOG_ENABLED
|
||||
#ifdef PBL_LOGS_HASHED
|
||||
#define PBL_LOG_D(domain, level, fmt, ...) \
|
||||
do { \
|
||||
if (PBL_SHOULD_LOG(level)) { \
|
||||
if (domain) { \
|
||||
NEW_LOG_HASH(pbl_log_hashed_async, level, DEFAULT_LOG_COLOR, fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PBL_LOG_D_SYNC(domain, level, fmt, ...) \
|
||||
do { \
|
||||
if (PBL_SHOULD_LOG(level)) { \
|
||||
if (domain) { \
|
||||
NEW_LOG_HASH(pbl_log_hashed_sync, level, DEFAULT_LOG_COLOR, fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PBL_LOG_COLOR_D(domain, level, color, fmt, ...) \
|
||||
do { \
|
||||
if (PBL_SHOULD_LOG(level)) { \
|
||||
if (domain) { \
|
||||
NEW_LOG_HASH(pbl_log_hashed_async, level, color, fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PBL_LOG_COLOR_D_SYNC(domain, level, color, fmt, ...) \
|
||||
do { \
|
||||
if (PBL_SHOULD_LOG(level)) { \
|
||||
if (domain) { \
|
||||
NEW_LOG_HASH(pbl_log_hashed_sync, level, color, fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define PBL_LOG_D(domain, level, fmt, ...) \
|
||||
do { \
|
||||
if (domain) { \
|
||||
pbl_log(level, __FILE__, __LINE__, fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PBL_LOG_D_SYNC(domain, level, fmt, ...) \
|
||||
do { \
|
||||
if (domain) { \
|
||||
pbl_log_sync(level, __FILE__, __LINE__, fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PBL_LOG_COLOR_D(domain, level, color, fmt, ...) \
|
||||
do { \
|
||||
if (domain) { \
|
||||
pbl_log(level, __FILE__, __LINE__, fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PBL_LOG_COLOR_D_SYNC(domain, level, color, fmt, ...) \
|
||||
do { \
|
||||
if (domain) { \
|
||||
pbl_log_sync(level, __FILE__, __LINE__, fmt, ## __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define PBL_LOG(level, fmt, ...) \
|
||||
PBL_LOG_D(DEFAULT_LOG_DOMAIN, level, fmt, ## __VA_ARGS__)
|
||||
|
||||
#define PBL_LOG_COLOR(level, color, fmt, ...) \
|
||||
PBL_LOG_COLOR_D(DEFAULT_LOG_DOMAIN, level, color, fmt, ## __VA_ARGS__)
|
||||
|
||||
#define PBL_LOG_SYNC(level, fmt, ...) \
|
||||
PBL_LOG_D_SYNC(DEFAULT_LOG_DOMAIN, level, fmt, ## __VA_ARGS__)
|
||||
|
||||
#define PBL_LOG_COLOR_SYNC(level, color, fmt, ...) \
|
||||
PBL_LOG_COLOR_D_SYNC(DEFAULT_LOG_DOMAIN, level, color, fmt, ## __VA_ARGS__)
|
||||
|
||||
#ifdef VERBOSE_LOGGING
|
||||
|
||||
#define PBL_LOG_VERBOSE(fmt, ...) \
|
||||
PBL_LOG_D(DEFAULT_LOG_DOMAIN, LOG_LEVEL_DEBUG, fmt, ## __VA_ARGS__)
|
||||
|
||||
#define PBL_LOG_D_VERBOSE(domain, fmt, ...) \
|
||||
PBL_LOG_D(domain, LOG_LEVEL_DEBUG, fmt, ## __VA_ARGS__)
|
||||
|
||||
#define PBL_LOG_COLOR_D_VERBOSE(domain, color, fmt, ...) \
|
||||
PBL_LOG_COLOR_D(domain, LOG_LEVEL_DEBUG, color, fmt, ## __VA_ARGS__)
|
||||
|
||||
#define PBL_LOG_COLOR_VERBOSE(color, fmt, ...) \
|
||||
PBL_LOG_COLOR_D(DEFAULT_LOG_DOMAIN, LOG_LEVEL_DEBUG, color, fmt, ## __VA_ARGS__)
|
||||
|
||||
#define RETURN_STATUS_D(d, st) \
|
||||
do { \
|
||||
if (PASSED(st)) \
|
||||
PBL_LOG_D(d, LOG_LEVEL_INFO, "%d", (int)(st)); \
|
||||
else \
|
||||
PBL_LOG_D(d, LOG_LEVEL_WARNING, "%d", (int)(st)); \
|
||||
return st; \
|
||||
} while (0)
|
||||
|
||||
#define RETURN_STATUS_COLOR_D(d, color, st) \
|
||||
do { \
|
||||
if (PASSED(st)) \
|
||||
PBL_LOG_COLOR_D(d, LOG_LEVEL_INFO, color, "%d", (int)(st)); \
|
||||
else \
|
||||
PBL_LOG_COLOR_D(d, LOG_LEVEL_WARNING, color, "%d", (int)(st)); \
|
||||
return st; \
|
||||
} while (0)
|
||||
|
||||
#define RETURN_STATUS_UP_D(d, st) \
|
||||
do { \
|
||||
if ((st) == E_INVALID_ARGUMENT) { \
|
||||
PBL_LOG_D(d, LOG_LEVEL_ERROR, "%d", (int)(st)); \
|
||||
return E_INTERNAL; \
|
||||
} \
|
||||
else { \
|
||||
return (st); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define RETURN_STATUS_UP_COLOR_D(d, color, st) \
|
||||
do { \
|
||||
if ((st) == E_INVALID_ARGUMENT) { \
|
||||
PBL_LOG_COLOR_D(d, LOG_LEVEL_ERROR, color, "%d", (int)(st)); \
|
||||
return E_INTERNAL; \
|
||||
} \
|
||||
else { \
|
||||
return (st); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#else // VERBOSE_LOGGING
|
||||
#define PBL_LOG_VERBOSE(fmt, ...)
|
||||
#define PBL_LOG_COLOR_VERBOSE(color, fmt, ...)
|
||||
#define PBL_LOG_D_VERBOSE(domain, fmt, ...)
|
||||
#define PBL_LOG_COLOR_D_VERBOSE(domain, color, fmt, ...)
|
||||
#define RETURN_STATUS_D(d, st) \
|
||||
do { \
|
||||
if (FAILED(st)) { \
|
||||
PBL_LOG_D(d, LOG_LEVEL_WARNING, "%d", (int)(st)); \
|
||||
} \
|
||||
return st; \
|
||||
} while (0)
|
||||
|
||||
#define RETURN_STATUS_COLOR_D(d, color, st) \
|
||||
do { \
|
||||
if (FAILED(st)) { \
|
||||
PBL_LOG_COLOR_D(d, LOG_LEVEL_WARNING, color, "%d", (int)(st)); \
|
||||
} \
|
||||
return st; \
|
||||
} while (0)
|
||||
|
||||
#define RETURN_STATUS_UP_D(d, st) \
|
||||
return ((st) != E_INVALID_ARGUMENT ? (st) : E_INTERNAL)
|
||||
|
||||
#define RETURN_STATUS_UP_COLOR_D(d, color, st) \
|
||||
return ((st) != E_INVALID_ARGUMENT ? (st) : E_INTERNAL)
|
||||
#endif // VERBOSE_LOGGING
|
||||
|
||||
#else // PBL_LOG_ENABLED
|
||||
#define PBL_LOG(level, fmt, ...)
|
||||
#define PBL_LOG_COLOR(level, color, fmt, ...)
|
||||
#define PBL_LOG_SYNC(level, fmt, ...)
|
||||
#define PBL_LOG_COLOR_SYNC(level, color, fmt, ...)
|
||||
#define PBL_LOG_D(domain, level, fmt, ...)
|
||||
#define PBL_LOG_COLOR_D(domain, level, color, fmt, ...)
|
||||
#define PBL_LOG_D_SYNC(domain, level, fmt, ...)
|
||||
#define PBL_LOG_COLOR_D_SYNC(domain, level, color, fmt, ...)
|
||||
#define PBL_LOG_VERBOSE(fmt, ...)
|
||||
#define PBL_LOG_COLOR_VERBOSE(color, fmt, ...)
|
||||
#define PBL_LOG_D_VERBOSE(domain, fmt, ...)
|
||||
#define PBL_LOG_COLOR_D_VERBOSE(domain, color, fmt, ...)
|
||||
#define RETURN_STATUS_D(d, st) return (st)
|
||||
#define RETURN_STATUS_COLOR_D(d, color, st) return (st)
|
||||
#define RETURN_STATUS_UP_D(d, st) \
|
||||
return ((st) == E_INVALID_ARGUMENT ? E_INTERNAL : (st))
|
||||
#define RETURN_STATUS_UP_COLOR_D(d, color, st) \
|
||||
return ((st) == E_INVALID_ARGUMENT ? E_INTERNAL : (st))
|
||||
#endif // PBL_LOG_ENABLED
|
||||
|
||||
#define RETURN_STATUS(s) RETURN_STATUS_D(DEFAULT_LOG_DOMAIN, s)
|
||||
#define RETURN_STATUS_UP(s) RETURN_STATUS_UP_D(DEFAULT_LOG_DOMAIN, s)
|
||||
|
162
src/fw/system/passert.c
Normal file
162
src/fw/system/passert.c
Normal file
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "passert.h"
|
||||
|
||||
#include "system/die.h"
|
||||
#include "system/reboot_reason.h"
|
||||
#include "kernel/fault_handling.h"
|
||||
|
||||
#include "kernel/pebble_tasks.h"
|
||||
#include "syscall/syscall.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define CORE_NUMBER 0
|
||||
|
||||
static NORETURN handle_passert_failed_vargs(const char* filename, int line_number,
|
||||
uintptr_t lr, const char* expr, const char* fmt, va_list fmt_args) {
|
||||
char buffer[160];
|
||||
|
||||
pbl_log_sync(LOG_LEVEL_ALWAYS, filename, line_number, "*** ASSERTION FAILED: %s", expr);
|
||||
|
||||
if (fmt) {
|
||||
vsniprintf(buffer, sizeof(buffer), fmt, fmt_args);
|
||||
pbl_log_sync(LOG_LEVEL_ALWAYS, filename, line_number, "%s", buffer);
|
||||
}
|
||||
|
||||
trigger_fault(RebootReasonCode_Assert, lr);
|
||||
}
|
||||
|
||||
static NORETURN handle_passert_failed(const char* filename, int line_number,
|
||||
uintptr_t lr, const char *expr, const char* fmt, ...) {
|
||||
va_list fmt_args;
|
||||
va_start(fmt_args, fmt);
|
||||
|
||||
handle_passert_failed_vargs(filename, line_number, lr, expr, fmt, fmt_args);
|
||||
|
||||
va_end(fmt_args);
|
||||
}
|
||||
|
||||
NORETURN passert_failed(const char* filename, int line_number, const char* message, ...) {
|
||||
va_list fmt_args;
|
||||
va_start(fmt_args, message);
|
||||
|
||||
handle_passert_failed_vargs(filename, line_number,
|
||||
(uintptr_t)__builtin_return_address(0), "ASSERT", message, fmt_args);
|
||||
|
||||
va_end(fmt_args);
|
||||
}
|
||||
|
||||
NORETURN passert_failed_hashed(uint32_t packed_loghash, ...) {
|
||||
uintptr_t saved_lr = (uintptr_t) __builtin_return_address(0);
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "ASSERTION at LR 0x%x", saved_lr);
|
||||
|
||||
va_list fmt_args;
|
||||
va_start(fmt_args, packed_loghash);
|
||||
|
||||
pbl_log_hashed_vargs(false, CORE_NUMBER, packed_loghash, fmt_args);
|
||||
|
||||
va_end(fmt_args);
|
||||
|
||||
trigger_fault(RebootReasonCode_Assert, saved_lr);
|
||||
}
|
||||
|
||||
NORETURN passert_failed_hashed_with_lr(uint32_t lr, uint32_t packed_loghash, ...) {
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "ASSERTION at LR 0x%"PRIx32, lr);
|
||||
|
||||
va_list fmt_args;
|
||||
va_start(fmt_args, packed_loghash);
|
||||
|
||||
pbl_log_hashed_vargs(false, CORE_NUMBER, packed_loghash, fmt_args);
|
||||
|
||||
va_end(fmt_args);
|
||||
|
||||
trigger_fault(RebootReasonCode_Assert, lr);
|
||||
}
|
||||
|
||||
NORETURN passert_failed_hashed_no_message_with_lr(uint32_t lr) {
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "ASSERTION at LR 0x%"PRIx32, lr);
|
||||
|
||||
trigger_fault(RebootReasonCode_Assert, lr);
|
||||
}
|
||||
|
||||
NORETURN passert_failed_hashed_no_message(void) {
|
||||
passert_failed_hashed_no_message_with_lr((uint32_t)__builtin_return_address(0));
|
||||
}
|
||||
|
||||
NORETURN passert_failed_no_message_with_lr(const char* filename, int line_number, uint32_t lr) {
|
||||
handle_passert_failed(filename, line_number, lr, "ASSERTN", NULL);
|
||||
}
|
||||
|
||||
NORETURN passert_failed_no_message(const char* filename, int line_number) {
|
||||
handle_passert_failed(filename, line_number,
|
||||
(uintptr_t)__builtin_return_address(0), "ASSERTN", NULL);
|
||||
}
|
||||
|
||||
NORETURN wtf(void) {
|
||||
uintptr_t saved_lr = (uintptr_t) __builtin_return_address(0);
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "*** WTF %p", (void *)saved_lr);
|
||||
trigger_fault(RebootReasonCode_Assert, saved_lr);
|
||||
}
|
||||
|
||||
void passert_check_task(PebbleTask expected_task) {
|
||||
uintptr_t saved_lr = (uintptr_t) __builtin_return_address(0);
|
||||
|
||||
if (pebble_task_get_current() != expected_task) {
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "LR: %p. Incorrect task! Expected <%s> got <%s>",
|
||||
(void*) saved_lr, pebble_task_get_name(expected_task),
|
||||
pebble_task_get_name(pebble_task_get_current()));
|
||||
trigger_fault(RebootReasonCode_Assert, saved_lr);
|
||||
}
|
||||
}
|
||||
|
||||
void passert_check_not_task(PebbleTask unexpected_task) {
|
||||
uintptr_t saved_lr = (uintptr_t) __builtin_return_address(0);
|
||||
|
||||
if (pebble_task_get_current() == unexpected_task) {
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "LR: %p. Incorrect task! Can't be <%s>",
|
||||
(void*) saved_lr, pebble_task_get_name(unexpected_task));
|
||||
trigger_fault(RebootReasonCode_Assert, saved_lr);
|
||||
}
|
||||
}
|
||||
|
||||
//! Assert function called by the STM peripheral library's
|
||||
//! 'assert_param' method. See stm32f2xx_conf.h for more information.
|
||||
void assert_failed(uint8_t* file, uint32_t line) {
|
||||
register uintptr_t lr __asm("lr");
|
||||
uintptr_t saved_lr = lr;
|
||||
|
||||
handle_passert_failed((const char*) file, line, saved_lr, "STM32", "STM32 peripheral library tripped an assert");
|
||||
}
|
||||
|
||||
extern void command_dump_malloc_kernel(void);
|
||||
|
||||
NORETURN croak_oom(size_t bytes, int saved_lr, Heap *heap_ptr) {
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "CROAK OOM: Failed to alloc %d bytes at LR: 0x%x",
|
||||
bytes, saved_lr);
|
||||
|
||||
#ifdef MALLOC_INSTRUMENTATION
|
||||
command_dump_malloc_kernel();
|
||||
#endif
|
||||
|
||||
trigger_oom_fault(bytes, saved_lr, heap_ptr);
|
||||
}
|
146
src/fw/system/passert.h
Normal file
146
src/fw/system/passert.h
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
#include <util/attributes.h>
|
||||
#include <util/likely.h>
|
||||
|
||||
|
||||
#ifdef PBL_LOGS_HASHED
|
||||
#include <logging/log_hashing.h>
|
||||
|
||||
NORETURN passert_failed_hashed(uint32_t packed_loghash, ...);
|
||||
|
||||
NORETURN passert_failed_hashed_with_lr(uint32_t lr,
|
||||
uint32_t packed_loghash, ...);
|
||||
|
||||
NORETURN passert_failed_hashed_no_message(void);
|
||||
|
||||
NORETURN passert_failed_hashed_no_message_with_lr(uint32_t lr);
|
||||
|
||||
#define PBL_ASSERT(expr, msg, ...) \
|
||||
do { \
|
||||
if (UNLIKELY(!(expr))) { \
|
||||
NEW_LOG_HASH(passert_failed_hashed, LOG_LEVEL_ALWAYS, LOG_COLOR_RED, \
|
||||
"*** ASSERTION FAILED: " msg, \
|
||||
## __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PBL_ASSERTN(expr) \
|
||||
do { \
|
||||
if (UNLIKELY(!(expr))) { \
|
||||
passert_failed_hashed_no_message(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PBL_ASSERTN_LR(expr, lr) \
|
||||
do { \
|
||||
if (UNLIKELY(!(expr))) { \
|
||||
passert_failed_hashed_no_message_with_lr(lr); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
NORETURN passert_failed(const char* filename, int line_number, const char* message, ...);
|
||||
|
||||
#define PBL_ASSERT(expr, ...) \
|
||||
do { \
|
||||
if (UNLIKELY(!(expr))) { \
|
||||
passert_failed(__FILE_NAME__, __LINE__, __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PBL_ASSERTN(expr) \
|
||||
do { \
|
||||
if (UNLIKELY(!(expr))) { \
|
||||
passert_failed_no_message(__FILE_NAME__, __LINE__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PBL_ASSERTN_LR(expr, lr) \
|
||||
do { \
|
||||
if (UNLIKELY(!(expr))) { \
|
||||
passert_failed_no_message_with_lr(__FILE_NAME__, __LINE__, lr); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
NORETURN passert_failed_no_message(const char* filename, int line_number);
|
||||
|
||||
NORETURN passert_failed_no_message_with_lr(const char* filename, int line_number, uint32_t lr);
|
||||
|
||||
NORETURN wtf(void);
|
||||
|
||||
#define WTF wtf()
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
#define PBL_ASSERT_TASK(task)
|
||||
#define PBL_ASSERT_NOT_TASK(task)
|
||||
#define PBL_ASSERT_RUNNING_FROM_EXPECTED_TASK(task)
|
||||
#define BREAKPOINT
|
||||
|
||||
#else
|
||||
|
||||
// Insert a compiled-in breakpoint
|
||||
#define BREAKPOINT __asm("bkpt")
|
||||
|
||||
enum PebbleTask;
|
||||
void passert_check_task(enum PebbleTask expected_task);
|
||||
void passert_check_not_task(enum PebbleTask unexpected_task);
|
||||
|
||||
#define PBL_ASSERT_TASK(task) passert_check_task(task);
|
||||
#define PBL_ASSERT_NOT_TASK(task) passert_check_not_task(task);
|
||||
|
||||
// It's useful during development to insert asserts to make sure our callbacks
|
||||
// are being dispatched as expected. It's wasteful (for codespace) to keep them
|
||||
// on after that as it's only when code gets edited that the assert gets hit.
|
||||
#ifdef CHECK_RUNNING_FROM_EXPECTED_TASK
|
||||
#define PBL_ASSERT_RUNNING_FROM_EXPECTED_TASK(task) PBL_ASSERT_TASK(task)
|
||||
#else
|
||||
#define PBL_ASSERT_RUNNING_FROM_EXPECTED_TASK(task)
|
||||
#endif
|
||||
|
||||
#endif // UNITTEST
|
||||
|
||||
// extern void command_dump_malloc(void);
|
||||
|
||||
#ifdef PBL_LOGS_HASHED
|
||||
|
||||
#define PBL_CROAK(msg, ...) \
|
||||
do { \
|
||||
NEW_LOG_HASH(passert_failed_hashed, LOG_LEVEL_ALWAYS, LOG_COLOR_RED, "*** CROAK: " msg, \
|
||||
## __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#else // PBL_LOGS_HASHED
|
||||
|
||||
#define PBL_CROAK(fmt, args...) \
|
||||
passert_failed(__FILE_NAME__, __LINE__, "*** CROAK: " fmt, ## args)
|
||||
|
||||
#endif // PBL_LOGS_HASHED
|
||||
|
||||
typedef struct Heap Heap;
|
||||
|
||||
NORETURN croak_oom(size_t bytes, int saved_lr, Heap *heap_ptr);
|
||||
|
||||
#define PBL_CROAK_OOM(bytes, saved_lr, heap_ptr) \
|
||||
croak_oom(bytes, saved_lr, heap_ptr)
|
211
src/fw/system/profiler.c
Normal file
211
src/fw/system/profiler.c
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "profiler.h"
|
||||
|
||||
#include "system/passert.h"
|
||||
#include "util/size.h"
|
||||
|
||||
#define CMSIS_COMPATIBLE
|
||||
#include <mcu.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#if PULSE_EVERYWHERE
|
||||
#define PROF_LOG(buf, sz, fmt, ...) \
|
||||
do { \
|
||||
snprintf(buf, sz, fmt, ## __VA_ARGS__); \
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "%s", buf); \
|
||||
} while (0)
|
||||
#else
|
||||
#define PROF_LOG(buf, sz, fmt, ...) dbgserial_putstr_fmt(buf, sz, fmt, ## __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
Profiler g_profiler;
|
||||
|
||||
#undef PROFILER_NODE
|
||||
#define PROFILER_NODE(name) ProfilerNode g_profiler_node_##name = {.module_name = #name};
|
||||
#include "profiler_list.h"
|
||||
#undef PROFILER_NODE
|
||||
#if PROFILE_INTERRUPTS
|
||||
#define IRQ_DEF(idx, irq) ProfilerNode g_profiler_node_##irq##_IRQ = {.module_name = #irq"_IRQ"};
|
||||
#include "irq_stm32.def"
|
||||
#undef IRQ_DEF
|
||||
#endif
|
||||
|
||||
static ProfilerNode *s_profiler_nodes[] = {
|
||||
#define PROFILER_NODE(name) &g_profiler_node_##name,
|
||||
#include "profiler_list.h"
|
||||
#undef PROFILER_NODE
|
||||
#if PROFILE_INTERRUPTS
|
||||
#define IRQ_DEF(idx, irq) &g_profiler_node_##irq##_IRQ,
|
||||
#include "irq_stm32.def"
|
||||
#undef IRQ_DEF
|
||||
#endif
|
||||
};
|
||||
|
||||
static void prv_profiler_node_add(ProfilerNode *node) {
|
||||
g_profiler.nodes = list_append(g_profiler.nodes, (ListNode *) node);
|
||||
}
|
||||
|
||||
static int prv_node_compare(void *a, void *b) {
|
||||
return ((ProfilerNode *) b)->total - ((ProfilerNode *) a)->total;
|
||||
}
|
||||
|
||||
void prv_node_reset(ProfilerNode *node) {
|
||||
node->start = 0;
|
||||
node->end = 0;
|
||||
node->total = 0;
|
||||
node->count = 0;
|
||||
list_init(&node->list_node);
|
||||
}
|
||||
|
||||
void profiler_init(void) {
|
||||
g_profiler.end = 0;
|
||||
g_profiler.start = 0;
|
||||
g_profiler.nodes = NULL;
|
||||
for (uint32_t i = 0; i < ARRAY_LENGTH(s_profiler_nodes); i++) {
|
||||
prv_node_reset(s_profiler_nodes[i]);
|
||||
prv_profiler_node_add(s_profiler_nodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void profiler_start(void) {
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
#ifdef MICRO_FAMILY_STM32F7
|
||||
DWT->LAR = 0xC5ACCE55;
|
||||
#endif
|
||||
DWT->CYCCNT = 0;
|
||||
DWT->CTRL |= 0x01;
|
||||
g_profiler.start = DWT->CYCCNT;
|
||||
}
|
||||
|
||||
void profiler_stop(void) {
|
||||
g_profiler.end = DWT->CYCCNT;
|
||||
}
|
||||
|
||||
uint32_t profiler_node_get_last_cycles(ProfilerNode *node) {
|
||||
uint32_t duration = 0;
|
||||
if (node->end > node->start) {
|
||||
duration = node->end - node->start;
|
||||
} else {
|
||||
duration = (UINT32_MAX - node->start) + node->end;
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
void profiler_node_stop(ProfilerNode *node, uint32_t dwt_cyc_cnt) {
|
||||
node->end = dwt_cyc_cnt;
|
||||
++node->count;
|
||||
|
||||
node->total += profiler_node_get_last_cycles(node);
|
||||
}
|
||||
|
||||
uint32_t profiler_cycles_to_us(uint32_t cycles) {
|
||||
RCC_ClocksTypeDef clocks;
|
||||
RCC_GetClocksFreq(&clocks);
|
||||
uint32_t mhz = clocks.HCLK_Frequency / 1000000;
|
||||
return cycles / mhz;
|
||||
}
|
||||
|
||||
uint32_t profiler_node_get_total_us(ProfilerNode *node) {
|
||||
return profiler_cycles_to_us(node->total);
|
||||
}
|
||||
|
||||
uint32_t profiler_node_get_count(ProfilerNode *node) {
|
||||
return node->count;
|
||||
}
|
||||
|
||||
uint32_t profiler_get_total_duration(bool in_us) {
|
||||
uint32_t total;
|
||||
if (g_profiler.end > g_profiler.start) {
|
||||
total = g_profiler.end - g_profiler.start;
|
||||
} else {
|
||||
total = (UINT32_MAX - g_profiler.start) + g_profiler.end;
|
||||
}
|
||||
|
||||
if (in_us) {
|
||||
RCC_ClocksTypeDef clocks;
|
||||
RCC_GetClocksFreq(&clocks);
|
||||
uint32_t mhz = clocks.HCLK_Frequency / 1000000;
|
||||
total /= mhz;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
void profiler_print_stats(void) {
|
||||
PROFILER_STOP; // Make sure the profiler has been stopped.
|
||||
uint32_t total = profiler_get_total_duration(false);
|
||||
|
||||
RCC_ClocksTypeDef clocks;
|
||||
RCC_GetClocksFreq(&clocks);
|
||||
uint32_t mhz = clocks.HCLK_Frequency / 1000000;
|
||||
|
||||
char buf[80];
|
||||
PROF_LOG(buf, sizeof(buf), "CPU Frequency: %"PRIu32"Hz", clocks.HCLK_Frequency);
|
||||
PROF_LOG(buf, sizeof(buf),
|
||||
"Profiler ran for %"PRIu32" ticks (%"PRIu32" us) (start: %"PRIu32"; stop:%"PRIu32")",
|
||||
total, total / mhz, g_profiler.start, g_profiler.end);
|
||||
|
||||
ListNode *sorted = NULL;
|
||||
ListNode *tail = list_get_tail(g_profiler.nodes);
|
||||
while (tail != NULL) {
|
||||
ListNode * new_tail = list_pop_tail(tail);
|
||||
sorted = list_sorted_add(sorted, tail, &prv_node_compare, false);
|
||||
tail = new_tail;
|
||||
}
|
||||
|
||||
if (sorted != NULL) {
|
||||
PROF_LOG(buf, sizeof(buf),
|
||||
"%-24s %-8s %-11s %-15s %-8s %-7s",
|
||||
"Name", "Count", "Cycles", "Time (us)", "Avg (us)", "% CPU");
|
||||
while (sorted != NULL) {
|
||||
ProfilerNode *node = (ProfilerNode *)sorted;
|
||||
uint32_t percent = (((int64_t)node->total) * 100) / total;
|
||||
|
||||
PROF_LOG(buf, sizeof(buf),
|
||||
"%-24s %-8"PRIu32" %-11"PRIu32" %-15"PRIu32" %-8"PRIu32 " %-7"PRIu32,
|
||||
node->module_name, node->count, node->total, node->total / mhz,
|
||||
(node->total/node->count)/mhz, percent);
|
||||
|
||||
sorted = sorted->next;
|
||||
}
|
||||
}
|
||||
|
||||
while (sorted != NULL) {
|
||||
ListNode *new_head = list_pop_head(sorted);
|
||||
list_append(g_profiler.nodes, sorted);
|
||||
sorted = new_head;
|
||||
}
|
||||
}
|
||||
|
||||
void command_profiler_stop(void) {
|
||||
PROFILER_STOP;
|
||||
PROFILER_PRINT_STATS;
|
||||
}
|
||||
|
||||
void command_profiler_start(void) {
|
||||
PROFILER_INIT;
|
||||
PROFILER_START;
|
||||
}
|
||||
|
||||
void command_profiler_stats(void) {
|
||||
PROFILER_PRINT_STATS;
|
||||
}
|
134
src/fw/system/profiler.h
Normal file
134
src/fw/system/profiler.h
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Setting up a profiler node:
|
||||
* 1. Create a new profiler node by adding it to profiler_list.h.
|
||||
* 2. Place PROFILER_NODE_START(<node>) and PROFILER_NODE_STOP(<node>) as desired.
|
||||
* 3. Make sure you are building with the "--profiler" configure option.
|
||||
*
|
||||
* Starting the profiler:
|
||||
* The prompt commands "profiler start" and "profiler stop" can be used to toggle it from the
|
||||
* command line.
|
||||
* Alternatively, one can use the PROFILER_START and PROFILER_STOP macros to start and stop them at
|
||||
* a specific point.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "util/list.h"
|
||||
|
||||
#if PROFILER
|
||||
#define CMSIS_COMPATIBLE
|
||||
#include <mcu.h>
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
ListNode list_node;
|
||||
char *module_name;
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
uint32_t total;
|
||||
uint32_t count;
|
||||
} ProfilerNode;
|
||||
|
||||
typedef struct {
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
ListNode *nodes;
|
||||
} Profiler;
|
||||
|
||||
extern Profiler g_profiler;
|
||||
|
||||
#if !defined(PROFILER)
|
||||
|
||||
#define PROFILER_NODE(name)
|
||||
#define PROFILER_INIT
|
||||
#define PROFILER_START
|
||||
#define PROFILER_STOP
|
||||
#define PROFILER_NODE_START(node)
|
||||
#define PROFILER_NODE_STOP(node)
|
||||
#define SYS_PROFILER_NODE_START(node)
|
||||
#define SYS_PROFILER_NODE_STOP(node)
|
||||
#define PROFILER_PRINT_STATS
|
||||
#define PROFILER_NODE_GET_TOTAL_US(node) (0)
|
||||
#define PROFILER_NODE_GET_TOTAL_CYCLES(node) (0)
|
||||
#define PROFILER_NODE_GET_COUNT(node) (0)
|
||||
#define PROFILER_NODE_GET_LAST_CYCLES(node) (0)
|
||||
|
||||
#else
|
||||
|
||||
#define PROFILER_NODE(name) extern ProfilerNode g_profiler_node_##name;
|
||||
#include "profiler_list.h"
|
||||
#undef PROFILER_NODE
|
||||
|
||||
#define PROFILER_INIT profiler_init()
|
||||
|
||||
#define PROFILER_START profiler_start()
|
||||
|
||||
#define PROFILER_STOP profiler_stop()
|
||||
|
||||
#define PROFILER_NODE_START(node) \
|
||||
g_profiler_node_##node.start = DWT->CYCCNT
|
||||
|
||||
#define PROFILER_NODE_STOP(node) \
|
||||
profiler_node_stop(&g_profiler_node_##node, DWT->CYCCNT)
|
||||
|
||||
#define SYS_PROFILER_NODE_START(node) \
|
||||
sys_profiler_node_start(&g_profiler_node_##node)
|
||||
|
||||
#define SYS_PROFILER_NODE_STOP(node) \
|
||||
sys_profiler_node_stop(&g_profiler_node_##node)
|
||||
|
||||
#define PROFILER_PRINT_STATS \
|
||||
profiler_print_stats()
|
||||
|
||||
#define PROFILER_NODE_GET_TOTAL_US(node) \
|
||||
profiler_node_get_total_us(&g_profiler_node_##node)
|
||||
|
||||
#define PROFILER_NODE_GET_LAST_CYCLES(node) \
|
||||
profiler_node_get_last_cycles(&g_profiler_node_##node)
|
||||
|
||||
#define PROFILER_NODE_GET_TOTAL_CYCLES(node) \
|
||||
g_profiler_node_##node.total
|
||||
|
||||
#define PROFILER_NODE_GET_COUNT(node) \
|
||||
profiler_node_get_count(&g_profiler_node_##node)
|
||||
|
||||
#endif // PROFILER
|
||||
|
||||
void profiler_init(void);
|
||||
void profiler_print_stats(void);
|
||||
void profiler_start(void);
|
||||
void profiler_stop(void);
|
||||
uint32_t profiler_cycles_to_us(uint32_t cycles);
|
||||
void profiler_node_stop(ProfilerNode *node, uint32_t dwt_cyc_cnt);
|
||||
uint32_t profiler_node_get_last_cycles(ProfilerNode *node);
|
||||
uint32_t profiler_node_get_total_us(ProfilerNode *node);
|
||||
uint32_t profiler_node_get_count(ProfilerNode *node);
|
||||
//! returns total time elapsed between a start and stop call
|
||||
//! @param in_us if true result is in us, else it's in cycles
|
||||
uint32_t profiler_get_total_duration(bool in_us);
|
||||
|
||||
void sys_profiler_init(void);
|
||||
void sys_profiler_node_start(ProfilerNode *node);
|
||||
void sys_profiler_node_stop(ProfilerNode *node);
|
||||
void sys_profiler_start(void);
|
||||
void sys_profiler_stop(void);
|
||||
void sys_profiler_print_stats(void);
|
30
src/fw/system/profiler_list.h
Normal file
30
src/fw/system/profiler_list.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// List of specific node timers. These are started and stopped using PROFILER_NODE_START
|
||||
// and PROFILER_NODE_STOP
|
||||
PROFILER_NODE(mic)
|
||||
PROFILER_NODE(framebuffer_dma)
|
||||
PROFILER_NODE(render_modal)
|
||||
PROFILER_NODE(render_app)
|
||||
PROFILER_NODE(dirty_rect)
|
||||
PROFILER_NODE(gfx_test_update_proc)
|
||||
PROFILER_NODE(voice_encode)
|
||||
PROFILER_NODE(compositor)
|
||||
PROFILER_NODE(hrm_handling)
|
||||
PROFILER_NODE(display_transfer)
|
||||
PROFILER_NODE(text_render_flash)
|
||||
PROFILER_NODE(text_render_compress)
|
91
src/fw/system/reboot_reason.c
Normal file
91
src/fw/system/reboot_reason.c
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "reboot_reason.h"
|
||||
|
||||
#include "mcu/interrupts.h"
|
||||
#include "os/tick.h"
|
||||
#include "system/bootbits.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
#define STM32F2_COMPATIBLE
|
||||
#define STM32F4_COMPATIBLE
|
||||
#define STM32F7_COMPATIBLE
|
||||
#include <mcu.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
_Static_assert(sizeof(RebootReason) == sizeof(uint32_t[6]), "RebootReason is a funny size");
|
||||
|
||||
void reboot_reason_set(RebootReason *reason) {
|
||||
uint32_t *raw = (uint32_t*)reason;
|
||||
|
||||
if (RTC_ReadBackupRegister(REBOOT_REASON_REGISTER_1)) {
|
||||
// It's not safe to log if we're called from an ISR or from a FreeRTOS critical section (basepri != 0)
|
||||
if (!mcu_state_is_isr() && __get_BASEPRI() == 0
|
||||
&& xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Reboot reason is already set");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
RTC_WriteBackupRegister(REBOOT_REASON_REGISTER_1, raw[0]);
|
||||
RTC_WriteBackupRegister(REBOOT_REASON_REGISTER_2, raw[1]);
|
||||
RTC_WriteBackupRegister(REBOOT_REASON_STUCK_TASK_PC, raw[2]);
|
||||
RTC_WriteBackupRegister(REBOOT_REASON_STUCK_TASK_LR, raw[3]);
|
||||
RTC_WriteBackupRegister(REBOOT_REASON_STUCK_TASK_CALLBACK, raw[4]);
|
||||
RTC_WriteBackupRegister(REBOOT_REASON_DROPPED_EVENT, raw[5]);
|
||||
}
|
||||
|
||||
void reboot_reason_set_restarted_safely(void) {
|
||||
RebootReason reason;
|
||||
reboot_reason_get(&reason);
|
||||
reason.restarted_safely = true;
|
||||
|
||||
uint32_t* raw = (uint32_t *)&reason;
|
||||
RTC_WriteBackupRegister(REBOOT_REASON_REGISTER_1, *raw);
|
||||
}
|
||||
|
||||
void reboot_reason_get(RebootReason *reason) {
|
||||
uint32_t *raw = (uint32_t *)reason;
|
||||
raw[0] = RTC_ReadBackupRegister(REBOOT_REASON_REGISTER_1);
|
||||
raw[1] = RTC_ReadBackupRegister(REBOOT_REASON_REGISTER_2);
|
||||
raw[2] = RTC_ReadBackupRegister(REBOOT_REASON_STUCK_TASK_PC);
|
||||
raw[3] = RTC_ReadBackupRegister(REBOOT_REASON_STUCK_TASK_LR);
|
||||
raw[4] = RTC_ReadBackupRegister(REBOOT_REASON_STUCK_TASK_CALLBACK);
|
||||
raw[5] = RTC_ReadBackupRegister(REBOOT_REASON_DROPPED_EVENT);
|
||||
}
|
||||
|
||||
void reboot_reason_clear(void) {
|
||||
RTC_WriteBackupRegister(REBOOT_REASON_REGISTER_1, 0);
|
||||
RTC_WriteBackupRegister(REBOOT_REASON_REGISTER_2, 0);
|
||||
RTC_WriteBackupRegister(REBOOT_REASON_STUCK_TASK_PC, 0);
|
||||
RTC_WriteBackupRegister(REBOOT_REASON_STUCK_TASK_LR, 0);
|
||||
RTC_WriteBackupRegister(REBOOT_REASON_STUCK_TASK_CALLBACK, 0);
|
||||
RTC_WriteBackupRegister(REBOOT_REASON_DROPPED_EVENT, 0);
|
||||
}
|
||||
|
||||
uint32_t reboot_get_slot_of_last_launched_app(void) {
|
||||
return RTC_ReadBackupRegister(SLOT_OF_LAST_LAUNCHED_APP);
|
||||
}
|
||||
|
||||
void reboot_set_slot_of_last_launched_app(uint32_t app_slot) {
|
||||
RTC_WriteBackupRegister(SLOT_OF_LAST_LAUNCHED_APP, app_slot);
|
||||
}
|
||||
|
102
src/fw/system/reboot_reason.h
Normal file
102
src/fw/system/reboot_reason.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "system/rtc_registers.h"
|
||||
#include "util/attributes.h"
|
||||
|
||||
// NOTE: We include the reboot reason in analytics and the tools we use to analyze the analytics are
|
||||
// dependent on the position and ordering of these enumerated values. To keep the analysis tools
|
||||
// simpler, it is best to keep these enums in the same order and add new ones to the end.
|
||||
typedef enum {
|
||||
RebootReasonCode_Unknown = 0,
|
||||
// Normal stuff
|
||||
RebootReasonCode_LowBattery,
|
||||
RebootReasonCode_SoftwareUpdate,
|
||||
RebootReasonCode_ResetButtonsHeld,
|
||||
RebootReasonCode_ShutdownMenuItem,
|
||||
RebootReasonCode_FactoryResetReset,
|
||||
RebootReasonCode_FactoryResetShutdown,
|
||||
RebootReasonCode_MfgShutdown,
|
||||
RebootReasonCode_Serial,
|
||||
RebootReasonCode_RemoteReset,
|
||||
RebootReasonCode_PrfReset,
|
||||
RebootReasonCode_ForcedCoreDump,
|
||||
RebootReasonCode_PrfIdle,
|
||||
RebootReasonCode_PrfResetButtonsHeld,
|
||||
|
||||
// Error occurred
|
||||
RebootReasonCode_Watchdog = 16,
|
||||
RebootReasonCode_Assert,
|
||||
RebootReasonCode_StackOverflow,
|
||||
RebootReasonCode_HardFault,
|
||||
RebootReasonCode_LauncherPanic,
|
||||
RebootReasonCode_ClockFailure, // Not used on 3.x
|
||||
RebootReasonCode_AppHardFault, // Not used on 3.x
|
||||
RebootReasonCode_EventQueueFull,
|
||||
RebootReasonCode_WorkerHardFault, // Off by default, compile in with WORKER_CRASH_CAUSES_RESET
|
||||
RebootReasonCode_OutOfMemory,
|
||||
RebootReasonCode_DialogBootFault,
|
||||
RebootReasonCode_BtCoredump,
|
||||
RebootReasonCode_CoreDump, // Core dump initiated without a more specific reason set
|
||||
RebootReasonCode_CoreDumpEntryFailed,
|
||||
} RebootReasonCode;
|
||||
|
||||
typedef struct PACKED {
|
||||
RebootReasonCode code:8;
|
||||
bool restarted_safely:1;
|
||||
uint8_t padding:7;
|
||||
union {
|
||||
uint16_t data16;
|
||||
uint8_t data8[2];
|
||||
};
|
||||
uint32_t extra;
|
||||
union {
|
||||
struct {
|
||||
uint32_t stuck_task_pc;
|
||||
uint32_t stuck_task_lr;
|
||||
uint32_t stuck_task_callback;
|
||||
} watchdog; //!< Valid if code == RebootReasonCode_Watchdog
|
||||
struct {
|
||||
uint32_t destination_task;
|
||||
uint32_t push_lr;
|
||||
uint32_t current_event;
|
||||
uint32_t dropped_event;
|
||||
} event_queue; //!< Valid if code == RebootReasonCode_EventQueueFull
|
||||
struct {
|
||||
uint32_t heap_alloc_lr;
|
||||
uint32_t heap_ptr;
|
||||
} heap_data; //!< Valid if code == RebootReasonCode_OutOfMemory
|
||||
};
|
||||
} RebootReason;
|
||||
|
||||
void reboot_reason_set(RebootReason *reason);
|
||||
|
||||
void reboot_reason_set_restarted_safely(void);
|
||||
|
||||
void reboot_reason_get(RebootReason *reason);
|
||||
|
||||
void reboot_reason_clear(void);
|
||||
|
||||
uint32_t reboot_get_slot_of_last_launched_app(void);
|
||||
|
||||
void reboot_set_slot_of_last_launched_app(uint32_t app_slot);
|
||||
|
||||
RebootReasonCode reboot_reason_get_last_reboot_reason(void);
|
34
src/fw/system/reset.h
Normal file
34
src/fw/system/reset.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/attributes.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
//! Shut down system services but don't actually reset.
|
||||
void system_reset_prepare(bool unsafe_reset);
|
||||
|
||||
//! Reset nicely after shutting down system services. Does not set the reboot_reason other than
|
||||
//! calling reboot_reason_set_restarted_safely just before the reset occurs.
|
||||
NORETURN system_reset(void);
|
||||
|
||||
//! Same as system_reset() but usable as a callback.
|
||||
void system_reset_callback(void *);
|
||||
|
||||
//! The final stage in the reset process.
|
||||
NORETURN system_hard_reset(void);
|
39
src/fw/system/rtc_registers.h
Normal file
39
src/fw/system/rtc_registers.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define RTC_BKP_BOOTBIT_DR RTC_BKP_DR0
|
||||
#define STUCK_BUTTON_REGISTER RTC_BKP_DR1
|
||||
#define BOOTLOADER_VERSION_REGISTER RTC_BKP_DR2
|
||||
#define CURRENT_TIME_REGISTER RTC_BKP_DR3
|
||||
#define CURRENT_INTERVAL_TICKS_REGISTER RTC_BKP_DR4
|
||||
#define REBOOT_REASON_REGISTER_1 RTC_BKP_DR5
|
||||
#define REBOOT_REASON_REGISTER_2 RTC_BKP_DR6
|
||||
#define REBOOT_REASON_STUCK_TASK_PC RTC_BKP_DR7
|
||||
#define REBOOT_REASON_STUCK_TASK_LR RTC_BKP_DR8
|
||||
#define REBOOT_REASON_STUCK_TASK_CALLBACK RTC_BKP_DR9
|
||||
#define REBOOT_REASON_DROPPED_EVENT RTC_BKP_DR10
|
||||
// formerly REBOOT_REASON_MUTEX_PC
|
||||
#define RTC_BKP_FLASH_ERASE_PROGRESS RTC_BKP_DR11
|
||||
#define RTC_TIMEZONE_ABBR_START RTC_BKP_DR12
|
||||
#define RTC_TIMEZONE_ABBR_END_TZID_DSTID RTC_BKP_DR13
|
||||
#define RTC_TIMEZONE_GMTOFFSET RTC_BKP_DR14
|
||||
#define RTC_TIMEZONE_DST_START RTC_BKP_DR15
|
||||
#define RTC_TIMEZONE_DST_END RTC_BKP_DR16
|
||||
#define MAG_XY_CORRECTION_VALS RTC_BKP_DR17
|
||||
#define MAG_Z_CORRECTION_VAL RTC_BKP_DR18
|
||||
#define SLOT_OF_LAST_LAUNCHED_APP RTC_BKP_DR19
|
91
src/fw/system/status_codes.h
Normal file
91
src/fw/system/status_codes.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\file status_codes.h
|
||||
\brief Global status codes and related macros.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//! Status codes. See \ref status_t
|
||||
typedef enum StatusCode {
|
||||
//! Operation completed successfully.
|
||||
S_SUCCESS = 0,
|
||||
|
||||
//! An error occurred (no description).
|
||||
E_ERROR = -1,
|
||||
|
||||
//! No idea what went wrong.
|
||||
E_UNKNOWN = -2,
|
||||
|
||||
//! There was a generic internal logic error.
|
||||
E_INTERNAL = -3,
|
||||
|
||||
//! The function was not called correctly.
|
||||
E_INVALID_ARGUMENT = -4,
|
||||
|
||||
//! Insufficient allocatable memory available.
|
||||
E_OUT_OF_MEMORY = -5,
|
||||
|
||||
//! Insufficient long-term storage available.
|
||||
E_OUT_OF_STORAGE = -6,
|
||||
|
||||
//! Insufficient resources available.
|
||||
E_OUT_OF_RESOURCES = -7,
|
||||
|
||||
//! Argument out of range (may be dynamic).
|
||||
E_RANGE = -8,
|
||||
|
||||
//! Target of operation does not exist.
|
||||
E_DOES_NOT_EXIST = -9,
|
||||
|
||||
//! Operation not allowed (may depend on state).
|
||||
E_INVALID_OPERATION = -10,
|
||||
|
||||
//! Another operation prevented this one.
|
||||
E_BUSY = -11,
|
||||
|
||||
//! Operation not completed; try again.
|
||||
E_AGAIN = -12,
|
||||
|
||||
//! Equivalent of boolean true.
|
||||
S_TRUE = 1,
|
||||
|
||||
//! Equivalent of boolean false.
|
||||
S_FALSE = 0,
|
||||
|
||||
//! For list-style requests. At end of list.
|
||||
S_NO_MORE_ITEMS = 2,
|
||||
|
||||
//! No action was taken as none was required.
|
||||
S_NO_ACTION_REQUIRED = 3,
|
||||
|
||||
} StatusCode;
|
||||
|
||||
// Use the int-sized int from the watch's processor.
|
||||
//! Return value for system operations. See \ref StatusCode for possible values.
|
||||
typedef int32_t status_t;
|
||||
|
||||
|
||||
#define DECLARE_DOMAIN_STATUS(e) ((status_t)(e & (1 << 30)))
|
||||
|
||||
|
||||
#define PASSED(s) ((status_t)(s) >= 0)
|
||||
#define FAILED(s) ((status_t)(s) < 0)
|
||||
|
46
src/fw/system/syscalls.c
Normal file
46
src/fw/system/syscalls.c
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/attributes.h"
|
||||
|
||||
// Newer than 4.7
|
||||
#if (__GNUC__ == 4 && __GNUC_MINOR__ > 7) || (__GNUC__ >= 5) || __clang__
|
||||
void __aeabi_memcpy(void *dest, const void *src, size_t n) {
|
||||
memcpy(dest, src, n);
|
||||
}
|
||||
|
||||
void __aeabi_memmove(void * restrict s1, const void * restrict s2, size_t n) {
|
||||
memmove(s1, s2, n);
|
||||
}
|
||||
|
||||
ALIAS("__aeabi_memcpy") void __aeabi_memcpy4(void *dest, const void *src, size_t n);
|
||||
ALIAS("__aeabi_memcpy") void __aeabi_memcpy8(void *dest, const void *src, size_t n);
|
||||
|
||||
void __aeabi_memset(void *s, size_t n, int c) {
|
||||
memset(s, c, n);
|
||||
}
|
||||
|
||||
void __aeabi_memclr(void *addr, size_t n) {
|
||||
memset(addr, 0, n);
|
||||
}
|
||||
|
||||
ALIAS("__aeabi_memclr") void __aeabi_memclr4(void *s, size_t n);
|
||||
ALIAS("__aeabi_memclr") void __aeabi_memclr8(void *s, size_t n);
|
||||
|
||||
#endif
|
45
src/fw/system/testinfra.c
Normal file
45
src/fw/system/testinfra.c
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "testinfra.h"
|
||||
|
||||
#include "console/pulse_internal.h"
|
||||
#include "kernel/core_dump.h"
|
||||
#include "services/common/new_timer/new_timer.h"
|
||||
#include "system/bootbits.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
void notify_system_ready_for_communication(void) {
|
||||
#if !UNITTEST
|
||||
pbl_log(LOG_LEVEL_DEBUG, __FILE_NAME__, __LINE__, "Ready for communication.");
|
||||
#if PULSE_EVERYWHERE
|
||||
static bool s_pulse_started = false;
|
||||
if (!s_pulse_started) {
|
||||
pulse_start();
|
||||
s_pulse_started = true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#if IS_BIGBOARD
|
||||
NORETURN test_infra_quarantine_board(const char *quarantine_reason) {
|
||||
PBL_LOG(LOG_LEVEL_INFO, "Quarantine Board: %s", quarantine_reason);
|
||||
boot_bit_set(BOOT_BIT_FORCE_PRF);
|
||||
core_dump_reset(true /* is_forced */);
|
||||
}
|
||||
#endif /* IS_BIGBOARD */
|
34
src/fw/system/testinfra.h
Normal file
34
src/fw/system/testinfra.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <util/attributes.h>
|
||||
|
||||
// The automated testing framework shouldn't start operating on the system
|
||||
// after a reset until PebbleOS is ready to handle requests. This function
|
||||
// handles that notification
|
||||
void notify_system_ready_for_communication(void);
|
||||
|
||||
#if IS_BIGBOARD
|
||||
// This sends a notification to infra that we have detected an issue which needs manual
|
||||
// intervention to debug. Infra should disable the board to give the team time to grab the board and
|
||||
// investigate.
|
||||
//
|
||||
// Note: To preserve the current state, this routine sets the FORCE_PRF boot bit & then
|
||||
// forces a coredump
|
||||
NORETURN test_infra_quarantine_board(const char *quarantine_reason);
|
||||
#endif /* IS_BIGBOARD */
|
142
src/fw/system/version.c
Normal file
142
src/fw/system/version.c
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "applib/app_watch_info.h"
|
||||
#include "drivers/flash.h"
|
||||
#include "flash_region/flash_region.h"
|
||||
#include "system/firmware_storage.h"
|
||||
#include "system/passert.h"
|
||||
#include "util/attributes.h"
|
||||
#include "util/build_id.h"
|
||||
#include "util/string.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#include "git_version.auto.h"
|
||||
|
||||
//! This symbol and its contents are provided by the linker script, see the
|
||||
//! .note.gnu.build-id section in src/fw/stm32f2xx_flash_fw.ld
|
||||
extern const ElfExternalNote TINTIN_BUILD_ID;
|
||||
|
||||
const FirmwareMetadata TINTIN_METADATA SECTION(".pbl_fw_version") = {
|
||||
.version_timestamp = GIT_TIMESTAMP,
|
||||
.version_tag = GIT_TAG,
|
||||
.version_short = GIT_REVISION,
|
||||
|
||||
.is_recovery_firmware = FIRMWARE_METADATA_IS_RECOVERY_FIRMWARE,
|
||||
.is_ble_firmware = false,
|
||||
.reserved = 0,
|
||||
|
||||
.hw_platform = FIRMWARE_METADATA_HW_PLATFORM,
|
||||
|
||||
.metadata_version = FW_METADATA_CURRENT_STRUCT_VERSION,
|
||||
};
|
||||
|
||||
bool version_copy_running_fw_metadata(FirmwareMetadata *out_metadata) {
|
||||
if (out_metadata == NULL) {
|
||||
return false;
|
||||
}
|
||||
memcpy(out_metadata, &TINTIN_METADATA, sizeof(FirmwareMetadata));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool prv_version_copy_flash_fw_metadata(FirmwareMetadata *out_metadata,
|
||||
uint32_t flash_address, bool check_crc) {
|
||||
FirmwareDescription firmware_description =
|
||||
firmware_storage_read_firmware_description(flash_address);
|
||||
|
||||
if (check_crc &&
|
||||
!firmware_storage_check_valid_firmware_description(flash_address,
|
||||
&firmware_description)) {
|
||||
*out_metadata = (FirmwareMetadata){};
|
||||
return false;
|
||||
}
|
||||
|
||||
// The FirmwareMetadata is stored at the end of the binary
|
||||
const uint32_t metadata_offset = flash_address +
|
||||
firmware_description.description_length +
|
||||
firmware_description.firmware_length -
|
||||
sizeof(FirmwareMetadata);
|
||||
|
||||
flash_read_bytes((uint8_t*)out_metadata, metadata_offset, sizeof(FirmwareMetadata));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool version_copy_recovery_fw_metadata(FirmwareMetadata *out_metadata) {
|
||||
const bool check_crc = true;
|
||||
return prv_version_copy_flash_fw_metadata(out_metadata, FLASH_REGION_SAFE_FIRMWARE_BEGIN,
|
||||
check_crc);
|
||||
}
|
||||
|
||||
bool version_copy_update_fw_metadata(FirmwareMetadata *out_metadata) {
|
||||
const bool check_crc = false;
|
||||
return prv_version_copy_flash_fw_metadata(out_metadata, FLASH_REGION_FIRMWARE_SCRATCH_BEGIN,
|
||||
check_crc);
|
||||
}
|
||||
|
||||
bool version_copy_recovery_fw_version(char* dest, const int dest_len_bytes) {
|
||||
FirmwareMetadata out_metadata;
|
||||
const bool check_crc = false;
|
||||
bool success = prv_version_copy_flash_fw_metadata(&out_metadata,
|
||||
FLASH_REGION_SAFE_FIRMWARE_BEGIN,
|
||||
check_crc);
|
||||
if (success) {
|
||||
strncpy(dest, out_metadata.version_tag, dest_len_bytes);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool version_is_prf_installed(void) {
|
||||
FirmwareDescription firmware_description =
|
||||
firmware_storage_read_firmware_description(FLASH_REGION_SAFE_FIRMWARE_BEGIN);
|
||||
|
||||
return firmware_storage_check_valid_firmware_description(FLASH_REGION_SAFE_FIRMWARE_BEGIN,
|
||||
&firmware_description);
|
||||
}
|
||||
|
||||
const uint8_t * version_get_build_id(size_t *out_len) {
|
||||
if (out_len) {
|
||||
*out_len = TINTIN_BUILD_ID.data_length;
|
||||
}
|
||||
PBL_ASSERTN(TINTIN_BUILD_ID.data_length == BUILD_ID_EXPECTED_LEN);
|
||||
|
||||
return &TINTIN_BUILD_ID.data[TINTIN_BUILD_ID.name_length];
|
||||
}
|
||||
|
||||
void version_copy_build_id_hex_string(char *buffer, size_t buffer_bytes_left,
|
||||
const ElfExternalNote *elf_build_id) {
|
||||
size_t build_id_bytes_left = elf_build_id->data_length;
|
||||
const uint8_t *build_id = &elf_build_id->data[elf_build_id->name_length];
|
||||
byte_stream_to_hex_string(buffer, buffer_bytes_left, build_id,
|
||||
build_id_bytes_left, false);
|
||||
}
|
||||
|
||||
void version_copy_current_build_id_hex_string(char *buffer, size_t buffer_bytes_left) {
|
||||
version_copy_build_id_hex_string(buffer, buffer_bytes_left, &TINTIN_BUILD_ID);
|
||||
}
|
||||
|
||||
void version_get_major_minor_patch(unsigned int *major, unsigned int *minor,
|
||||
char const **patch_ptr) {
|
||||
*major = GIT_MAJOR_VERSION;
|
||||
*minor = GIT_MINOR_VERSION;
|
||||
*patch_ptr = GIT_PATCH_VERBOSE_STRING;
|
||||
}
|
76
src/fw/system/version.h
Normal file
76
src/fw/system/version.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "pebbleos/firmware_metadata.h"
|
||||
#include "util/attributes.h"
|
||||
#include "util/build_id.h"
|
||||
|
||||
extern const FirmwareMetadata TINTIN_METADATA;
|
||||
|
||||
//! Copies the version metadata of the running firmware in the provided struct.
|
||||
//! @param[out] out_metadata pointer to a FirmwareMetadata to which to copy the version metadata
|
||||
//! @returns true if it successfully copied the version metadata.
|
||||
bool version_copy_running_fw_metadata(FirmwareMetadata *out_metadata);
|
||||
|
||||
//! Copies the version metadata of the recovery firmware in the provided struct.
|
||||
//! If there is no valid metadata available, the struct will be wiped to be all zeroes.
|
||||
//! @param[out] out_metadata pointer to a FirmwareMetadata to which to copy the version metadata
|
||||
//! @returns true if it successfully copied the version metadata.
|
||||
bool version_copy_recovery_fw_metadata(FirmwareMetadata *out_metadata);
|
||||
|
||||
//! Copies the version metadata of the update firmware located in
|
||||
//! FLASH_REGION_FIRMWARE_SCRATCH_BEGIN into the provided struct.
|
||||
//! If there is no valid metadata available, the struct will be wiped to be all zeroes.
|
||||
//! @param[out] out_metadata pointer to a FirmwareMetadata to which to copy the version metadata
|
||||
//! @returns true if it successfully copied the version metadata.
|
||||
bool version_copy_update_fw_metadata(FirmwareMetadata *out_metadata);
|
||||
|
||||
//! Read recovery version_short from flash and copy to dest; copy at most
|
||||
//! dest_len_bytes - 1 before being null-terminated via strncpy()
|
||||
//!
|
||||
//! @param dest: char[dest_len_bytes]
|
||||
//! @returns true on success, false otherwise
|
||||
bool version_copy_recovery_fw_version(char* dest, const int dest_len_bytes);
|
||||
|
||||
//! Checks to see if a valid PRF is installed with a correct checksum.
|
||||
//! @return true if a PRF is installed, false otherwise.
|
||||
bool version_is_prf_installed(void);
|
||||
|
||||
//! @return Pointer to the GNU build id data. This is a hash of the firmware
|
||||
//! that is generated by the linker and uniquely identifies the binary.
|
||||
//! @param[out] out_len The length of the build id in bytes.
|
||||
const uint8_t * version_get_build_id(size_t *out_len);
|
||||
|
||||
//! Copies a hex C-string of the build id into the supplied buffer.
|
||||
//! Get the build id from an elf, using `arm-none-eabi-readelf -n tintin_fw.elf`
|
||||
//! @param[out] buffer The buffer into which the string should be copied.
|
||||
//! @param max_length The length of buffer.
|
||||
void version_copy_current_build_id_hex_string(char *buffer, size_t buffer_bytes_left);
|
||||
|
||||
//! Like version_copy_current_build_id_hex_string, but is copied from the specified Elf section.
|
||||
void version_copy_build_id_hex_string(char *buffer, size_t buffer_bytes_left,
|
||||
const ElfExternalNote *build_id);
|
||||
|
||||
//! Returns the major, minor, and the rest in string form. The return value of patch_ptr is
|
||||
//! read-only
|
||||
void version_get_major_minor_patch(unsigned int *major, unsigned int *minor,
|
||||
char const **patch_ptr);
|
Loading…
Add table
Add a link
Reference in a new issue