mirror of
https://github.com/google/pebble.git
synced 2025-05-05 17:31:40 -04:00
178 lines
6 KiB
C
178 lines
6 KiB
C
/*
|
|
* 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 <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "drivers/crc.h"
|
|
#include "drivers/flash.h"
|
|
#include "flash_region.h"
|
|
#include "git_version.auto.h"
|
|
#include "system/firmware_storage.h"
|
|
#include "system/passert.h"
|
|
#include "version.h"
|
|
|
|
//! The linker inserts the build id as an "elf external note" structure:
|
|
struct ElfExternalNote {
|
|
uint32_t name_length;
|
|
uint32_t data_length;
|
|
uint32_t type; // NT_GNU_BUILD_ID = 3
|
|
uint8_t data[]; // concatenated name ('GNU') + data (build id)
|
|
};
|
|
|
|
//! 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 struct ElfExternalNote TINTIN_BUILD_ID;
|
|
|
|
const FirmwareMetadata TINTIN_METADATA __attribute__ ((section (".pbl_fw_version"))) = {
|
|
.version_timestamp = GIT_TIMESTAMP,
|
|
.version_tag = GIT_TAG,
|
|
.version_short = GIT_REVISION,
|
|
|
|
#ifdef RECOVERY_FW
|
|
.is_recovery_firmware = true,
|
|
#else
|
|
.is_recovery_firmware = false,
|
|
#endif
|
|
|
|
#if defined(BOARD_BIGBOARD)
|
|
.hw_platform = FirmwareMetadataPlatformPebbleOneBigboard,
|
|
#elif defined(BOARD_BB2)
|
|
.hw_platform = FirmwareMetadataPlatformPebbleOneBigboard2,
|
|
#elif defined(BOARD_V2_0)
|
|
.hw_platform = FirmwareMetadataPlatformPebbleTwoPointZero,
|
|
#elif defined(BOARD_V1_5)
|
|
.hw_platform = FirmwareMetadataPlatformPebbleOnePointFive,
|
|
#elif defined(BOARD_EV2_4)
|
|
.hw_platform = FirmwareMetadataPlatformPebbleOneEV2_4,
|
|
#else
|
|
.hw_platform = FirmwareMetadataPlatformUnknown,
|
|
#endif
|
|
|
|
.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) {
|
|
if (out_metadata == NULL) {
|
|
return false;
|
|
}
|
|
|
|
FirmwareDescription firmware_description = firmware_storage_read_firmware_description(flash_address);
|
|
if (!firmware_storage_check_valid_firmware_description(&firmware_description)) {
|
|
memset(out_metadata, 0, sizeof(FirmwareMetadata));
|
|
return false;
|
|
}
|
|
// The FirmwareMetadata is stored at the end of the binary:
|
|
uint32_t offset = firmware_description.description_length + firmware_description.firmware_length - sizeof(FirmwareMetadata);
|
|
flash_read_bytes((uint8_t*)out_metadata, flash_address + offset, sizeof(FirmwareMetadata));
|
|
return true;
|
|
}
|
|
|
|
bool version_copy_recovery_fw_metadata(FirmwareMetadata *out_metadata) {
|
|
return prv_version_copy_flash_fw_metadata(out_metadata, FLASH_REGION_SAFE_FIRMWARE_BEGIN);
|
|
}
|
|
|
|
bool version_copy_update_fw_metadata(FirmwareMetadata *out_metadata) {
|
|
return prv_version_copy_flash_fw_metadata(out_metadata, FLASH_REGION_FIRMWARE_SCRATCH_BEGIN);
|
|
}
|
|
|
|
bool version_copy_recovery_fw_version(char* dest, const int dest_len_bytes) {
|
|
FirmwareMetadata out_metadata;
|
|
bool success = version_copy_recovery_fw_metadata(&out_metadata);
|
|
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);
|
|
if (!firmware_storage_check_valid_firmware_description(&firmware_description)) {
|
|
return false;
|
|
}
|
|
|
|
uint32_t flash_address = FLASH_REGION_SAFE_FIRMWARE_BEGIN + firmware_description.description_length;
|
|
uint32_t crc = crc_calculate_flash(flash_address, firmware_description.firmware_length);
|
|
return crc == firmware_description.checksum;
|
|
}
|
|
|
|
const uint8_t * version_get_build_id(size_t *out_len) {
|
|
if (out_len) {
|
|
*out_len = TINTIN_BUILD_ID.data_length;
|
|
}
|
|
return &TINTIN_BUILD_ID.data[TINTIN_BUILD_ID.name_length];
|
|
}
|
|
|
|
void version_copy_build_id_hex_string(char *buffer, size_t buffer_bytes_left) {
|
|
size_t build_id_bytes_left;
|
|
const uint8_t *build_id = version_get_build_id(&build_id_bytes_left);
|
|
while (buffer_bytes_left >= 3 /* 2 hex digits, plus zero terminator */
|
|
&& build_id_bytes_left > 0) {
|
|
snprintf(buffer, buffer_bytes_left, "%02x", *build_id);
|
|
|
|
buffer += 2;
|
|
buffer_bytes_left -= 2;
|
|
|
|
build_id += 1;
|
|
build_id_bytes_left -= 1;
|
|
}
|
|
}
|
|
|
|
static void version_fw_version_to_major_minor(unsigned int *major, unsigned int *minor,
|
|
char *version_str) {
|
|
// read in the two X's (vX.X)
|
|
sscanf(version_str, "v%u.%u", major, minor);
|
|
}
|
|
|
|
//! Compares its two arguments for order. Returns a negative integer, zero, or a positive integer
|
|
//! if the first argument is less than, equal to, or greater than the second.
|
|
static int8_t prv_version_compare_fw_version_tags(char *fw1_version, char *fw2_version) {
|
|
unsigned int major1, minor1, major2, minor2;
|
|
version_fw_version_to_major_minor(&major1, &minor1, fw1_version);
|
|
version_fw_version_to_major_minor(&major2, &minor2, fw2_version);
|
|
|
|
if (major1 != major2) { // do the major versions differ?
|
|
return (major1 - major2);
|
|
}
|
|
|
|
if (minor1 != minor2) { // do the minor versions differ?
|
|
return (minor1 - minor2);
|
|
}
|
|
|
|
return (0); // versions are the same
|
|
}
|
|
|
|
bool version_fw_downgrade_detected(void) {
|
|
FirmwareMetadata running_meta_data, update_meta_data;
|
|
version_copy_running_fw_metadata(&running_meta_data);
|
|
version_copy_update_fw_metadata(&update_meta_data);
|
|
|
|
int rv = prv_version_compare_fw_version_tags(update_meta_data.version_tag,
|
|
running_meta_data.version_tag);
|
|
|
|
// return true is the new firmware to be updated to is a version less than the old one.
|
|
return (rv < 0);
|
|
}
|