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

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
#if defined(__clang__)
#define GCC_ONLY(x)
#else
#define GCC_ONLY(x) x
#endif
// Function attributes
#define FORMAT_FUNC(TYPE, STR_IDX, FIRST) __attribute__((__format__(TYPE, STR_IDX, FIRST)))
#define FORMAT_PRINTF(STR_IDX, FIRST) FORMAT_FUNC(__printf__, STR_IDX, FIRST)
#define ALWAYS_INLINE __attribute__((__always_inline__)) inline
#define NOINLINE __attribute__((__noinline__))
#define NORETURN __attribute__((__noreturn__)) void
#define NAKED_FUNC __attribute__((__naked__))
#define OPTIMIZE_FUNC(LVL) __attribute__((__optimize__(LVL)))
#define CONST_FUNC __attribute__((__const__))
#define PURE_FUNC __attribute__((__pure__))
// Variable attributes
#define ATTR_CLEANUP(FUNC) __attribute__((__cleanup__(FUNC)))
// Structure attributes
#define PACKED __attribute__((__packed__))
// General attributes
#define USED __attribute__((__used__))
#define UNUSED __attribute__((__unused__))
#define WEAK __attribute__((__weak__))
#define ALIAS(sym) __attribute__((__weak__, __alias__(sym)))
#define SECTION(SEC) GCC_ONLY(__attribute__((__section__(SEC))))
#define EXTERNALLY_VISIBLE GCC_ONLY(__attribute__((__externally_visible__)))

View file

@ -0,0 +1,103 @@
/*
* 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 util/bitset.h
//!
//! Helper functions for dealing with a bitsets of various widths.
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "system/passert.h"
static inline void bitset8_set(uint8_t* bitset, unsigned int index) {
bitset[index / 8] |= (1 << (index % 8));
}
static inline void bitset8_clear(uint8_t* bitset, unsigned int index) {
bitset[index / 8] &= ~(1 << (index % 8));
}
static inline void bitset8_update(uint8_t* bitset, unsigned int index, bool value) {
if (value) {
bitset8_set(bitset, index);
} else {
bitset8_clear(bitset, index);
}
}
static inline bool bitset8_get(const uint8_t* bitset, unsigned int index) {
return bitset[index / 8] & (1 << (index % 8));
}
static inline void bitset16_set(uint16_t* bitset, unsigned int index) {
bitset[index / 16] |= (1 << (index % 16));
}
static inline void bitset16_clear(uint16_t* bitset, unsigned int index) {
bitset[index / 16] &= ~(1 << (index % 16));
}
static inline void bitset16_update(uint16_t* bitset, unsigned int index, bool value) {
if (value) {
bitset16_set(bitset, index);
} else {
bitset16_clear(bitset, index);
}
}
static inline bool bitset16_get(const uint16_t* bitset, unsigned int index) {
return bitset[index / 16] & (1 << (index % 16));
}
static inline void bitset32_set(uint32_t* bitset, unsigned int index) {
bitset[index / 32] |= (1 << (index % 32));
}
static inline void bitset32_clear(uint32_t* bitset, unsigned int index) {
bitset[index / 32] &= ~(1 << (index % 32));
}
static inline void bitset32_clear_all(uint32_t* bitset, unsigned int width) {
if (width > 32) {
// TODO: revisit
WTF;
}
*bitset &= ~((1 << (width + 1)) - 1);
}
static inline void bitset32_update(uint32_t* bitset, unsigned int index, bool value) {
if (value) {
bitset32_set(bitset, index);
} else {
bitset32_clear(bitset, index);
}
}
static inline bool bitset32_get(const uint32_t* bitset, unsigned int index) {
return bitset[index / 32] & (1 << (index % 32));
}
#ifdef __arm__
#define rotl32(x, shift) \
__asm__ volatile ("ror %0,%0,%1" : "+r" (src) : "r" (32 - (shift)) :);
#else
#define rotl32(x, shift) \
uint32_t s = shift % 32; \
{x = ((x << (s)) | x >> (32 - (s)));}
#endif

View file

@ -0,0 +1,51 @@
/*
* 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 "cobs.h"
size_t cobs_encode(void *dst_ptr, const void *src_ptr, size_t length) {
const char *src = src_ptr;
char *dst = dst_ptr;
uint8_t code = 0x01;
size_t code_idx = 0;
size_t dst_idx = 1;
for (size_t src_idx = 0; src_idx < length; ++src_idx) {
if (src[src_idx] == '\0') {
dst[code_idx] = code;
code_idx = dst_idx++;
code = 0x01;
} else {
dst[dst_idx++] = src[src_idx];
code++;
if (code == 0xff) {
if (src_idx == length - 1) {
// Special case: the final encoded block is 254 bytes long with no
// zero after it. While it's technically a valid encoding if a
// trailing zero is appended, it causes the output to be one byte
// longer than it needs to be. This violates consistent overhead
// contract and could overflow a carefully sized buffer.
break;
}
dst[code_idx] = code;
code_idx = dst_idx++;
code = 0x01;
}
}
}
dst[code_idx] = code;
return dst_idx;
}

View file

@ -0,0 +1,40 @@
/*
* 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 <stddef.h>
#include <stdint.h>
//! An implementation of Consistent Overhead Byte Stuffing
//!
//! http://conferences.sigcomm.org/sigcomm/1997/papers/p062.pdf
//! http://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
//! Evaluates to the offset required when encoding in-place
#define COBS_OVERHEAD(n) (((n) + 253) / 254)
//! Evaluates to the maximum buffer size required to hold n bytes of data
//! after COBS encoding.
#define MAX_SIZE_AFTER_COBS_ENCODING(n) ((n) + COBS_OVERHEAD(n))
//! COBS-encode a buffer out to another buffer.
//!
//! @param [out] dst destination buffer. The buffer must be at least
//! MAX_SIZE_AFTER_COBS_ENCODING(length) bytes long.
//! @param [in] src source buffer
//! @param length length of src
size_t cobs_encode(void * restrict dst, const void * restrict src,
size_t length);

View file

@ -0,0 +1,48 @@
/*
* 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 "util/crc32.h"
// Nybble-wide table driven CRC-32 algorithm
//
// A compromise between speed and size, this algorithm uses a lookup table to
// calculate the CRC four bits at a time with a size cost of only 64 bytes. By
// contrast, a byte-wide algorithm requires a lookup table sixteen times larger!
//
// The lookup table is generated by the crc32_lut.py
static const uint32_t s_lookup_table[] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
};
uint32_t crc32(uint32_t crc, const void * restrict data, size_t length) {
if (data == 0) {
return 0;
}
const uint8_t * restrict bytes = data;
crc ^= 0xffffffff;
while (length--) {
crc = (crc >> 4) ^ s_lookup_table[(crc ^ *bytes) & 0xf];
crc = (crc >> 4) ^ s_lookup_table[(crc ^ (*bytes >> 4)) & 0xf];
bytes++;
}
crc ^= 0xffffffff;
return crc;
}

View file

@ -0,0 +1,67 @@
/*
* 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
//! Calculate the CRC-32 checksum of data.
//!
//! The checksum is the standard CRC-32 used by zlib, PNG and others.
//! The model parameters for the algorithm, as described in A Painless Guide to
//! CRC Error Detection Algorithms (http://www.zlib.net/crc_v3.txt), are:
//! Name: "CRC-32"
//! Width: 32
//! Poly: 04C11DB7
//! Init: FFFFFFFF
//! RefIn: True
//! RefOut: True
//! XorOut: FFFFFFFF
//! Check: CBF43926
#include <stdint.h>
#include <string.h>
//! Update a running CRC-32 checksum with the bytes of data and return the
//! updated CRC-32. If data is NULL, the function returns the required initial
//! value for the CRC.
//!
//! This function is drop-in compatible with zlib's crc32 function.
//!
//! \par Usage
//! \code
//! uint32_t crc = crc32(0, NULL, 0);
//! while (read_buffer(data, length)) {
//! crc = crc32(crc, data, length);
//! }
//! \endcode
uint32_t crc32(uint32_t crc, const void * restrict data, size_t length);
//! The initial CRC register value for a standard CRC-32 checksum.
//!
//! It is the same value as is returned by the `crc32` function when data is
//! NULL.
//!
//! \code
//! assert(CRC32_INIT == crc32(0, NULL, 0));
//! \endcode
#define CRC32_INIT (0)
//! The residue constant of the CRC-32 algorithm.
//!
//! If the CRC-32 value of a message is appended (little-endian) onto the
//! end of the message, the CRC-32 of the concatenated message and CRC will be
//! equal to CRC32_RESIDUE if the message has not been corrupted in transit.
#define CRC32_RESIDUE (0x2144DF1C)

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 "delay.h"
#include <inttypes.h>
void delay_us(uint32_t us) {
// Empirically (measured on a bb2), 1 loop = ~47ns. (sysclk @
// 64MHz, prefetch disabled) Alignment of code will have some impact on how
// long this actually takes
uint32_t delay_loops = us * 22;
__asm volatile (
"spinloop: \n"
" subs %[delay_loops], #1 \n"
" bne spinloop \n"
: [delay_loops] "+r" (delay_loops) // read-write operand
:
: "cc"
);
}
void delay_ms(uint32_t millis) {
// delay_us(millis*1000) is not used because a long delay could easily
// overflow the veriable. Without the outer loop, a delay of even five
// seconds would overflow.
while (millis--) {
delay_us(1000);
}
}

View file

@ -0,0 +1,28 @@
/*
* 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 <inttypes.h>
//! Carefully timed spinloop that allows one to delay at a microsecond
//! granularity.
void delay_us(uint32_t us);
//! Waits for a certain amount of milliseconds by busy-waiting.
//!
//! @param millis The number of milliseconds to wait for
void delay_ms(uint32_t millis);

View file

@ -0,0 +1,201 @@
/*
* 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 "misc.h"
#include "system/logging.h"
#include "system/passert.h"
#include <ctype.h>
#include <stdbool.h>
#include <string.h>
int32_t sign_extend(uint32_t a, int bits) {
if (bits == 32) {
return a;
}
// http://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend
int const m = 1U << (bits - 1); // mask can be pre-computed if b is fixed
a = a & ((1U << bits) - 1); // (Skip this if bits in x above position b are already zero.)
return (a ^ m) - m;
}
int32_t serial_distance32(uint32_t a, uint32_t b) {
return serial_distance(a, b, 32);
}
int32_t serial_distance(uint32_t a, uint32_t b, int bits) {
// See https://en.wikipedia.org/wiki/Serial_Number_Arithmetic
const int64_t a_minus_b = a - b;
const int64_t b_minus_a = b - a;
const bool a_is_earlier_than_b = (a < b && b_minus_a < (1 << (bits - 1))) || (a > b && a_minus_b > (1 << (bits - 1)));
return sign_extend(a_is_earlier_than_b ? -a_minus_b : b_minus_a, bits);
}
int ceil_log_two(uint32_t n) {
// clz stands for Count Leading Zeroes. We use it to find the MSB
int msb = 31 - __builtin_clz(n);
// popcount counts the number of set bits in a word (1's)
bool power_of_two = __builtin_popcount(n) == 1;
// if not exact power of two, use the next power of two
// we want to err on the side of caution and want to
// always round up
return ((power_of_two) ? msb : (msb + 1));
}
uint8_t count_bits_set(uint8_t *bitset_bytes, int num_bits) {
uint8_t num_bits_set = 0;
int num_bytes = (num_bits + 7) / 8;
if ((num_bits % 8) != 0) {
bitset_bytes[num_bytes] &= ((0x1 << (num_bits)) - 1);
}
for (int i = 0; i < num_bytes; i++) {
num_bits_set += __builtin_popcount(bitset_bytes[i]);
}
return (num_bits_set);
}
void itoa(uint32_t num, char *buffer, int buffer_length) {
if (buffer_length < 11) {
PBL_LOG(LOG_LEVEL_WARNING, "ito buffer too small");
return;
}
*buffer++ = '0';
*buffer++ = 'x';
for (int i = 7; i >= 0; --i) {
uint32_t digit = (num & (0xf << (i * 4))) >> (i * 4);
char c;
if (digit < 0xa) {
c = '0' + digit;
} else if (digit < 0x10) {
c = 'a' + (digit - 0xa);
} else {
c = ' ';
}
*buffer++ = c;
}
*buffer = '\0';
}
static int8_t ascii_hex_to_int(const uint8_t c) {
if (isdigit(c)) return c - '0';
if (isupper(c)) return (c - 'A') + 10;
if (islower(c)) return (c - 'a') + 10;
return -1;
}
static uint8_t ascii_hex_to_uint(const uint8_t msb, const uint8_t lsb) {
return 16 * ascii_hex_to_int(msb) + ascii_hex_to_int(lsb);
}
uintptr_t str_to_address(const char *address_str) {
unsigned int address_str_length = strlen(address_str);
if (address_str_length < 3) return -1; // Invalid address, must be at least 0x[0-9a-f]+
if (address_str[0] != '0' || address_str[1] != 'x') return -1; // Incorrect address prefix.
address_str += 2;
uintptr_t address = 0;
for (; *address_str; ++address_str) {
int8_t c = ascii_hex_to_int(*address_str);
if (c == -1) return -1; // Unexpected character
address = (address * 16) + c;
}
return address;
}
bool convert_bt_addr_hex_str_to_bd_addr(const char *hex_str, uint8_t *bd_addr, const unsigned int bd_addr_size) {
const int len = strlen(hex_str);
if (len != 12) {
return false;
}
uint8_t* src = (uint8_t*) hex_str;
uint8_t* dest = bd_addr + bd_addr_size - 1;
for (unsigned int i = 0; i < bd_addr_size; ++i, src += 2, --dest) {
*dest = ascii_hex_to_uint(src[0], src[1]);
}
return true;
}
uint32_t next_exponential_backoff(uint32_t *attempt, uint32_t initial_value, uint32_t max_value) {
if (*attempt > 31) {
return max_value;
}
uint32_t backoff_multiplier = 0x1 << (*attempt)++;
uint32_t next_value = initial_value * backoff_multiplier;
return MIN(next_value, max_value);
}
// Based on DJB2 Hash
uint32_t hash(const uint8_t *bytes, const uint32_t length) {
uint32_t hash = 5381;
if (length == 0) {
return hash;
}
uint8_t c;
const uint8_t *last_byte = bytes + length;
while (bytes++ != last_byte) {
c = *bytes;
hash = ((hash << 5) + hash) + c;
}
return hash;
}
const char *bool_to_str(bool b) {
if (b) {
return "yes";
} else {
return "no";
}
}
// Override libgcc's table-driven __builtin_popcount implementation
#ifdef __arm__
int __popcountsi2(uint32_t val) {
// Adapted from http://www.sciencezero.org/index.php?title=ARM%3a_Count_ones_%28bit_count%29
uint32_t tmp;
__asm("and %[tmp], %[val], #0xaaaaaaaa\n\t"
"sub %[val], %[val], %[tmp], lsr #1\n\t"
"and %[tmp], %[val], #0xcccccccc\n\t"
"and %[val], %[val], #0x33333333\n\t"
"add %[val], %[val], %[tmp], lsr #2\n\t"
"add %[val], %[val], %[val], lsr #4\n\t"
"and %[val], %[val], #0x0f0f0f0f\n\t"
"add %[val], %[val], %[val], lsr #8\n\t"
"add %[val], %[val], %[val], lsr #16\n\t"
"and %[val], %[val], #63\n\t"
: [val] "+l" (val), [tmp] "=&l" (tmp));
return val;
}
#endif // __arm__

View file

@ -0,0 +1,107 @@
/*
* 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 <string.h>
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
#define ABS(a) (((a) > 0) ? (a) : -1 * (a))
#define CLIP(n, min, max) ((n)<(min)?(min):((n)>(max)?(max):(n)))
#define MHZ_TO_HZ(hz) (((uint32_t)(hz)) * 1000000)
#define KiBYTES(k) ((k) * 1024) // Bytes to Kibibytes
#define MiBYTES(m) ((m) * 1024 * 1024) // Bytes to Mebibytes
#define EiBYTES(e) ((e) * 1024 * 1024 * 1024 * 1024 * 1024 * 1024) // Bytes to Exbibytes
// Stolen from http://stackoverflow.com/a/8488201
#define GET_FILE_NAME(file) (strrchr(file, '/') ? (strrchr(file, '/') + 1) : (file))
#define ARRAY_LENGTH(array) (sizeof((array))/sizeof((array)[0]))
#define MEMBER_SIZE(type, member) sizeof(((type *)0)->member)
static inline void swap16(int16_t *a, int16_t *b) {
int16_t t = *a;
*a = *b;
*b = t;
}
int32_t sign_extend(uint32_t a, int bits);
//! Calculates the distance (end - start), taking a roll-over into account as good as it can get.
int32_t serial_distance32(uint32_t start, uint32_t end);
//! Calculates the distance (end - start), taking a roll-over into account as good as it can get.
//! @param bits the number of bits that are valid in start and end.
int32_t serial_distance(uint32_t start, uint32_t end, int bits);
void itoa(uint32_t num, char *buffer, int buffer_length);
/*
* find the log base two of a number rounded up
*/
int ceil_log_two(uint32_t n);
//! Count the number of bits that are set to 1 in a multi-byte bitset.
//! @param bitset_bytes The bytes of the bitset
//! @param num_bits The width of the bitset
//! @note this function zeroes out any bits in the last byte if there
//! are more bits than num_bits.
uint8_t count_bits_set(uint8_t *bitset_bytes, int num_bits);
uintptr_t str_to_address(const char *address_str);
uint32_t hash(const uint8_t *bytes, const uint32_t length);
const char *bool_to_str(bool b);
//! @param hex 12-digit hex string representing a BT address
//! @param addr Points to a SS1 BD_ADDR_t as defined in BTBTypes.h
//! @return True on success
bool convert_bt_addr_hex_str_to_bd_addr(const char *hex_str, uint8_t *bd_addr, const unsigned int bd_addr_size);
/**
* Compute the next backoff interval using a bounded binary expoential backoff formula.
*
* @param[in,out] attempt The number of retries performed so far. This count will be incremented by
* the function.
* @param[in] initial_value The inital backoff interval. Subsequent backoff attempts will be this
* number multiplied by a power of 2.
* @param[in] max_value The maximum backoff interval that returned by the function.
* @return The next backoff interval.
*/
uint32_t next_exponential_backoff(uint32_t *attempt, uint32_t initial_value, uint32_t max_value);
/*
* The -Wtype-limits flag generated an error with the previous IS_SIGNED maco.
* If an unsigned number was passed in the macro would check if the unsigned number was less than 0.
*/
#define IS_SIGNED(var) (__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(var), unsigned char), false, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(var), unsigned short), false, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(var), unsigned int), false, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(var), unsigned long), false, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(var), unsigned long long), false, true))))) \
)

View file

@ -0,0 +1,80 @@
/*
* 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>
// When compiling test, the host OS might have conflicting defines for this:
#undef ntohs
#undef htons
#undef ntohl
#undef htonl
#undef ltohs
#undef ltohl
static inline uint16_t ntohs(uint16_t v) {
// return ((v & 0x00ff) << 8) | ((v & 0xff00) >> 8);
return __builtin_bswap16(v);
}
static inline uint16_t htons(uint16_t v) {
return ntohs(v);
}
static inline uint32_t ntohl(uint32_t v) {
// return ((v & 0x000000ff) << 24) |
// ((v & 0x0000ff00) << 8) |
// ((v & 0x00ff0000) >> 8) |
// ((v & 0xff000000) >> 24);
return __builtin_bswap32(v);
}
static inline uint32_t htonl(uint32_t v) {
return ntohl(v);
}
#define ltohs(v) (v)
#define ltohl(v) (v)
// Types for values in network byte-order. They are wrapped in structs so that
// the compiler will disallow implicit casting of these types to or from
// integral types. This way it is a compile error to try using variables of
// these types without first performing a byte-order conversion.
// There is no overhead for wrapping the values in structs.
typedef struct net16 {
uint16_t v;
} net16;
typedef struct net32 {
uint32_t v;
} net32;
static inline uint16_t ntoh16(net16 net) {
return ntohs(net.v);
}
static inline net16 hton16(uint16_t v) {
return (net16){ htons(v) };
}
static inline uint32_t ntoh32(net32 net) {
return ntohl(net.v);
}
static inline net32 hton32(uint32_t v) {
return (net32){ htonl(v) };
}

View file

@ -0,0 +1,178 @@
/*
* 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);
}

View file

@ -0,0 +1,97 @@
/*
* 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>
#define FW_METADATA_CURRENT_STRUCT_VERSION 0x1
#define FW_METADATA_VERSION_SHORT_BYTES 8
#define FW_METADATA_VERSION_TAG_BYTES 32
typedef enum {
FirmwareMetadataPlatformUnknown = 0,
FirmwareMetadataPlatformPebbleOneEV1 = 1,
FirmwareMetadataPlatformPebbleOneEV2 = 2,
FirmwareMetadataPlatformPebbleOneEV2_3 = 3,
FirmwareMetadataPlatformPebbleOneEV2_4 = 4,
FirmwareMetadataPlatformPebbleOnePointFive = 5,
FirmwareMetadataPlatformPebbleTwoPointZero = 6,
FirmwareMetadataPlatformPebbleOneBigboard = 0xff,
FirmwareMetadataPlatformPebbleOneBigboard2 = 0xfe,
} FirmwareMetadataPlatform;
// WARNING: changes in this struct must be reflected in:
// - iOS/PebblePrivateKit/PebblePrivateKit/PBBundle.m
struct FirmwareMetadata {
uint32_t version_timestamp;
char version_tag[FW_METADATA_VERSION_TAG_BYTES];
char version_short[FW_METADATA_VERSION_SHORT_BYTES];
const bool is_recovery_firmware;
const uint8_t hw_platform;
const uint8_t metadata_version; //!< This should be the last field, since we put the meta data struct at the end of the fw binary.
} __attribute__((__packed__));
typedef struct FirmwareMetadata FirmwareMetadata;
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_build_id_hex_string(char *buffer, size_t max_length);
//! Checks the firmware stored in FLASH_REGION_FIRMWARE_SCRATCH_BEGIN and compares it to the
//! currently running firmware.
//! @returns true if a downgrade is detected.
bool version_fw_downgrade_detected(void);