Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson 2024-12-12 16:43:03 -08:00 committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions

78
src/fw/system/bootbits.c Normal file
View 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
View 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
View 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
View 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);

View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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);

View 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)

View 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);
}

View 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
View 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);

View 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

View 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
View 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
View 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
View 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
View 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
View 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);