[FL-1430] New OTP format #595

This commit is contained in:
あく 2021-07-22 05:49:23 +03:00 committed by GitHub
parent 769ab2aef2
commit 7ca89256eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 287 additions and 83 deletions

View File

@ -1,58 +1,170 @@
#include <api-hal-version.h> #include <api-hal-version.h>
#include <furi.h>
#include <stm32wbxx.h> #include <stm32wbxx.h>
#include <stm32wbxx_ll_rtc.h> #include <stm32wbxx_ll_rtc.h>
#include <stdio.h> #include <stdio.h>
#include "ble.h" #include "ble.h"
#define FLIPPER_NAME_LENGTH 8 #define API_HAL_VERSION_OTP_HEADER_MAGIC 0xBABE
#define API_HAL_VERSION_NAME_LENGTH 8
#define API_HAL_VERSION_ARRAY_NAME_LENGTH (API_HAL_VERSION_NAME_LENGTH + 1)
/** BLE symbol + "Flipper " + name */
#define API_HAL_VERSION_DEVICE_NAME_LENGTH (1 + 8 + API_HAL_VERSION_ARRAY_NAME_LENGTH)
#define API_HAL_VERSION_OTP_ADDRESS OTP_AREA_BASE
/** OTP Versions enum */
typedef enum {
ApiHalVersionOtpVersion0=0x00,
ApiHalVersionOtpVersion1=0x01,
ApiHalVersionOtpVersionEmpty=0xFFFFFFFE,
ApiHalVersionOtpVersionUnknown=0xFFFFFFFF,
} ApiHalVersionOtpVersion;
/** OTP V0 Structure: prototypes and early EVT */
typedef struct { typedef struct {
uint8_t version; uint8_t board_version;
uint8_t target; uint8_t board_target;
uint8_t body; uint8_t board_body;
uint8_t connect; uint8_t board_connect;
uint32_t header_timestamp;
char name[API_HAL_VERSION_NAME_LENGTH];
} ApiHalVersionOTPv0;
/** OTP V1 Structure: late EVT, DVT, PVT, Production */
typedef struct {
/* First 64 bits: header */
uint16_t header_magic;
uint8_t header_version;
uint8_t header_reserved;
uint32_t header_timestamp;
/* Second 64 bits: board info */
uint8_t board_version; /** Board version */
uint8_t board_target; /** Board target firmware */
uint8_t board_body; /** Board body */
uint8_t board_connect; /** Board interconnect */
uint8_t board_color; /** Board color */
uint8_t board_region; /** Board region */
uint16_t board_reserved; /** Reserved for future use, 0x0000 */
/* Third 64 bits: Unique Device Name */
char name[API_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
} ApiHalVersionOTPv1;
/** Represenation Model: */
typedef struct {
ApiHalVersionOtpVersion otp_version;
uint32_t timestamp; uint32_t timestamp;
char name[FLIPPER_NAME_LENGTH];
} ApiHalVersionOTP;
#define FLIPPER_ARRAY_NAME_LENGTH (FLIPPER_NAME_LENGTH + 1) uint8_t board_version; /** Board version */
// BLE symbol + "Flipper " + name uint8_t board_target; /** Board target firmware */
#define FLIPPER_DEVICE_NAME_LENGTH (1 + 8 + FLIPPER_ARRAY_NAME_LENGTH) uint8_t board_body; /** Board body */
uint8_t board_connect; /** Board interconnect */
uint8_t board_color; /** Board color */
uint8_t board_region; /** Board region */
// Initialiazed from OTP, used to guarantee zero terminated C string char name[API_HAL_VERSION_ARRAY_NAME_LENGTH]; /** \0 terminated name */
static char flipper_name[FLIPPER_ARRAY_NAME_LENGTH]; char device_name[API_HAL_VERSION_DEVICE_NAME_LENGTH]; /** device name for special needs */
static char flipper_device_name[FLIPPER_DEVICE_NAME_LENGTH]; uint8_t ble_mac[6];
static uint8_t api_hal_version_ble_mac[6]; } ApiHalVersion;
void api_hal_version_init() { static ApiHalVersion api_hal_version = {0};
char* name = ((ApiHalVersionOTP*)OTP_AREA_BASE)->name;
strlcpy(flipper_name, name, FLIPPER_ARRAY_NAME_LENGTH);
if(api_hal_version_get_name_ptr() != NULL) { static ApiHalVersionOtpVersion api_hal_version_get_otp_version() {
if (*(uint64_t*)API_HAL_VERSION_OTP_ADDRESS == 0xFFFFFFFF) {
return ApiHalVersionOtpVersionEmpty;
} else {
if (((ApiHalVersionOTPv1*)API_HAL_VERSION_OTP_ADDRESS)->header_magic == API_HAL_VERSION_OTP_HEADER_MAGIC) {
return ApiHalVersionOtpVersion1;
} else if (((ApiHalVersionOTPv0*)API_HAL_VERSION_OTP_ADDRESS)->board_version <= 10) {
return ApiHalVersionOtpVersion0;
} else {
return ApiHalVersionOtpVersionUnknown;
}
}
}
static void api_hal_version_set_name(const char* name) {
if(name != NULL) {
strlcpy(api_hal_version.name, name, API_HAL_VERSION_ARRAY_NAME_LENGTH);
snprintf( snprintf(
flipper_device_name, api_hal_version.device_name,
FLIPPER_DEVICE_NAME_LENGTH, API_HAL_VERSION_DEVICE_NAME_LENGTH,
"xFlipper %s", "xFlipper %s",
flipper_name); api_hal_version.name);
} else { } else {
snprintf( snprintf(
flipper_device_name, api_hal_version.device_name,
FLIPPER_DEVICE_NAME_LENGTH, API_HAL_VERSION_DEVICE_NAME_LENGTH,
"xFlipper"); "xFlipper");
} }
flipper_device_name[0] = AD_TYPE_COMPLETE_LOCAL_NAME; api_hal_version.device_name[0] = AD_TYPE_COMPLETE_LOCAL_NAME;
// BLE Mac address // BLE Mac address
uint32_t udn = LL_FLASH_GetUDN(); uint32_t udn = LL_FLASH_GetUDN();
uint32_t company_id = LL_FLASH_GetSTCompanyID(); uint32_t company_id = LL_FLASH_GetSTCompanyID();
uint32_t device_id = LL_FLASH_GetDeviceID(); uint32_t device_id = LL_FLASH_GetDeviceID();
api_hal_version_ble_mac[0] = (uint8_t)(udn & 0x000000FF); api_hal_version.ble_mac[0] = (uint8_t)(udn & 0x000000FF);
api_hal_version_ble_mac[1] = (uint8_t)( (udn & 0x0000FF00) >> 8 ); api_hal_version.ble_mac[1] = (uint8_t)( (udn & 0x0000FF00) >> 8 );
api_hal_version_ble_mac[2] = (uint8_t)( (udn & 0x00FF0000) >> 16 ); api_hal_version.ble_mac[2] = (uint8_t)( (udn & 0x00FF0000) >> 16 );
api_hal_version_ble_mac[3] = (uint8_t)device_id; api_hal_version.ble_mac[3] = (uint8_t)device_id;
api_hal_version_ble_mac[4] = (uint8_t)(company_id & 0x000000FF);; api_hal_version.ble_mac[4] = (uint8_t)(company_id & 0x000000FF);
api_hal_version_ble_mac[5] = (uint8_t)( (company_id & 0x0000FF00) >> 8 ); api_hal_version.ble_mac[5] = (uint8_t)( (company_id & 0x0000FF00) >> 8 );
}
static void api_hal_version_load_otp_default() {
api_hal_version_set_name(NULL);
}
static void api_hal_version_load_otp_v0() {
const ApiHalVersionOTPv0* otp = (ApiHalVersionOTPv0*)API_HAL_VERSION_OTP_ADDRESS;
api_hal_version.timestamp = otp->header_timestamp;
api_hal_version.board_version = otp->board_version;
api_hal_version.board_target = otp->board_target;
api_hal_version.board_body = otp->board_body;
api_hal_version.board_connect = otp->board_connect;
api_hal_version.board_color = 0;
api_hal_version.board_region = 0;
api_hal_version_set_name(otp->name);
}
static void api_hal_version_load_otp_v1() {
const ApiHalVersionOTPv1* otp = (ApiHalVersionOTPv1*)API_HAL_VERSION_OTP_ADDRESS;
api_hal_version.timestamp = otp->header_timestamp;
api_hal_version.board_version = otp->board_version;
api_hal_version.board_target = otp->board_target;
api_hal_version.board_body = otp->board_body;
api_hal_version.board_connect = otp->board_connect;
api_hal_version.board_color = otp->board_color;
api_hal_version.board_region = otp->board_region;
api_hal_version_set_name(otp->name);
}
void api_hal_version_init() {
api_hal_version.otp_version = api_hal_version_get_otp_version();
switch(api_hal_version.otp_version) {
case ApiHalVersionOtpVersionUnknown:
api_hal_version_load_otp_default();
break;
case ApiHalVersionOtpVersionEmpty:
api_hal_version_load_otp_default();
break;
case ApiHalVersionOtpVersion0:
api_hal_version_load_otp_v0();
break;
case ApiHalVersionOtpVersion1:
api_hal_version_load_otp_v1();
break;
default: furi_check(0);
}
} }
bool api_hal_version_do_i_belong_here() { bool api_hal_version_do_i_belong_here() {
@ -64,47 +176,47 @@ const char* api_hal_version_get_model_name() {
} }
const uint8_t api_hal_version_get_hw_version() { const uint8_t api_hal_version_get_hw_version() {
return ((ApiHalVersionOTP*)OTP_AREA_BASE)->version; return api_hal_version.board_version;
} }
const uint8_t api_hal_version_get_hw_target() { const uint8_t api_hal_version_get_hw_target() {
return ((ApiHalVersionOTP*)OTP_AREA_BASE)->target; return api_hal_version.board_target;
} }
const uint8_t api_hal_version_get_hw_body() { const uint8_t api_hal_version_get_hw_body() {
return ((ApiHalVersionOTP*)OTP_AREA_BASE)->body; return api_hal_version.board_body;
} }
const uint8_t api_hal_version_get_hw_color() { const uint8_t api_hal_version_get_hw_color() {
return 0; return api_hal_version.board_color;
} }
const uint8_t api_hal_version_get_hw_connect() { const uint8_t api_hal_version_get_hw_connect() {
return ((ApiHalVersionOTP*)OTP_AREA_BASE)->connect; return api_hal_version.board_connect;
} }
const uint8_t api_hal_version_get_hw_region() { const uint8_t api_hal_version_get_hw_region() {
return 0; return api_hal_version.board_region;
} }
const uint32_t api_hal_version_get_hw_timestamp() { const uint32_t api_hal_version_get_hw_timestamp() {
return ((ApiHalVersionOTP*)OTP_AREA_BASE)->timestamp; return api_hal_version.timestamp;
} }
const char* api_hal_version_get_name_ptr() { const char* api_hal_version_get_name_ptr() {
return *flipper_name == 0xFFU ? NULL : flipper_name; return *api_hal_version.name == 0x00 ? NULL : api_hal_version.name;
} }
const char* api_hal_version_get_device_name_ptr() { const char* api_hal_version_get_device_name_ptr() {
return flipper_device_name + 1; return api_hal_version.device_name + 1;
} }
const char* api_hal_version_get_ble_local_device_name_ptr() { const char* api_hal_version_get_ble_local_device_name_ptr() {
return flipper_device_name; return api_hal_version.device_name;
} }
const uint8_t* api_hal_version_get_ble_mac() { const uint8_t* api_hal_version_get_ble_mac() {
return api_hal_version_ble_mac; return api_hal_version.ble_mac;
} }
const struct Version* api_hal_version_get_firmware_version(void) { const struct Version* api_hal_version_get_firmware_version(void) {

View File

@ -16,8 +16,14 @@ class Main:
self.parser_check = self.subparsers.add_parser( self.parser_check = self.subparsers.add_parser(
"check", help="Check Option Bytes" "check", help="Check Option Bytes"
) )
self.parser_check.add_argument(
"--port", type=str, help="Port to connect: swd or usb1", default="swd"
)
self.parser_check.set_defaults(func=self.check) self.parser_check.set_defaults(func=self.check)
self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes") self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes")
self.parser_set.add_argument(
"--port", type=str, help="Port to connect: swd or usb1", default="swd"
)
self.parser_set.set_defaults(func=self.set) self.parser_set.set_defaults(func=self.set)
# logging # logging
self.logger = logging.getLogger() self.logger = logging.getLogger()
@ -52,9 +58,19 @@ class Main:
self.logger.info(f"Checking Option Bytes") self.logger.info(f"Checking Option Bytes")
try: try:
output = subprocess.check_output( output = subprocess.check_output(
["STM32_Programmer_CLI", "-q", "-c port=swd", "-ob displ"] [
"STM32_Programmer_CLI",
"-q",
"-c",
f"port={self.args.port}",
"-ob displ",
]
) )
assert output assert output
except subprocess.CalledProcessError as e:
self.logger.error(e.output.decode())
self.logger.error(f"Failed to call STM32_Programmer_CLI")
return
except Exception as e: except Exception as e:
self.logger.error(f"Failed to call STM32_Programmer_CLI") self.logger.error(f"Failed to call STM32_Programmer_CLI")
self.logger.exception(e) self.logger.exception(e)
@ -97,12 +113,18 @@ class Main:
[ [
"STM32_Programmer_CLI", "STM32_Programmer_CLI",
"-q", "-q",
"-c port=swd", "-c",
f"-ob {' '.join(options)}", f"port={self.args.port}",
"-ob",
*options,
] ]
) )
assert output assert output
self.logger.info(f"Success") self.logger.info(f"Success")
except subprocess.CalledProcessError as e:
self.logger.error(e.output.decode())
self.logger.error(f"Failed to call STM32_Programmer_CLI")
return
except Exception as e: except Exception as e:
self.logger.error(f"Failed to call STM32_Programmer_CLI") self.logger.error(f"Failed to call STM32_Programmer_CLI")
self.logger.exception(e) self.logger.exception(e)

View File

@ -2,12 +2,30 @@
import logging import logging
import argparse import argparse
import subprocess
import os import os
import sys import sys
import re import re
import struct import struct
import datetime import datetime
OTP_MAGIC = 0xBABE
OTP_VERSION = 0x01
OTP_RESERVED = 0x00
OTP_COLORS = {
"unknown": 0x00,
"black": 0x01,
"white": 0x02,
}
OTP_REGIONS = {
"unknown": 0x00,
"europe": 0x01,
"usa": 0x02,
}
BOARD_RESERVED = 0x0000
class Main: class Main:
def __init__(self): def __init__(self):
@ -15,28 +33,25 @@ class Main:
self.parser = argparse.ArgumentParser() self.parser = argparse.ArgumentParser()
self.parser.add_argument("-d", "--debug", action="store_true", help="Debug") self.parser.add_argument("-d", "--debug", action="store_true", help="Debug")
self.subparsers = self.parser.add_subparsers(help="sub-command help") self.subparsers = self.parser.add_subparsers(help="sub-command help")
# Generate
self.parser_generate = self.subparsers.add_parser( self.parser_generate = self.subparsers.add_parser(
"generate", help="OTP HW version generator" "generate", help="Generate OTP binary"
)
self.parser_generate.add_argument(
"--version", type=int, help="Version", required=True
)
self.parser_generate.add_argument(
"--firmware", type=int, help="Firmware", required=True
)
self.parser_generate.add_argument(
"--body", type=int, help="Body", required=True
)
self.parser_generate.add_argument(
"--connect", type=int, help="Connect", required=True
)
self.parser_generate.add_argument(
"--name", type=str, help="Name", required=True
) )
self._add_args(self.parser_generate)
self.parser_generate.add_argument("file", help="Output file") self.parser_generate.add_argument("file", help="Output file")
self.parser_generate.set_defaults(func=self.generate) self.parser_generate.set_defaults(func=self.generate)
# Flash
self.parser_flash = self.subparsers.add_parser(
"flash", help="Flash OTP to device"
)
self._add_args(self.parser_flash)
self.parser_flash.add_argument(
"--port", type=str, help="Port to connect: swd or usb1", default="swd"
)
self.parser_flash.set_defaults(func=self.flash)
# logging # logging
self.logger = logging.getLogger() self.logger = logging.getLogger()
self.timestamp = datetime.datetime.now().timestamp()
def __call__(self): def __call__(self):
self.args = self.parser.parse_args() self.args = self.parser.parse_args()
@ -53,39 +68,94 @@ class Main:
# execute requested function # execute requested function
self.args.func() self.args.func()
def generate(self): def _add_args(self, parser):
self.logger.debug(f"Generating OTP") parser.add_argument("--version", type=int, help="Version", default=10)
parser.add_argument("--firmware", type=int, help="Firmware", default=6)
parser.add_argument("--body", type=int, help="Body", default=8)
parser.add_argument("--connect", type=int, help="Connect", default=5)
parser.add_argument("--color", type=str, help="Color", default="unknown")
parser.add_argument("--region", type=str, help="Region", default="unknown")
parser.add_argument("--name", type=str, help="Name", required=True)
if self.args.name: def _process_args(self):
name = re.sub( if self.args.color not in OTP_COLORS:
"[^a-zA-Z0-9.]", "", self.args.name self.parser.error(f"Invalid color. Use one of {OTP_COLORS.keys()}")
) # Filter all special characters self.args.color = OTP_COLORS[self.args.color]
name = list(
map(str, map(ord, name[0:8]))
) # Strip to 8 chars and map to ascii codes
while len(name) < 8:
name.append("0")
n1, n2, n3, n4, n5, n6, n7, n8 = map(int, name) if self.args.region not in OTP_REGIONS:
self.parser.error(f"Invalid region. Use one of {OTP_REGIONS.keys()}")
self.args.region = OTP_REGIONS[self.args.region]
data = struct.pack( if len(self.args.name) > 8:
"<BBBBLBBBBBBBB", self.parser.error("Name is too long. Max 8 symbols.")
if re.match(r"[a-zA-Z0-9]+", self.args.name) is None:
self.parser.error(
"Name contains incorrect symbols. Only a-zA-Z0-9 allowed."
)
def _pack_struct(self):
return struct.pack(
"<" "HBBL" "BBBBBBH" "8s",
OTP_MAGIC,
OTP_VERSION,
OTP_RESERVED,
int(self.timestamp),
self.args.version, self.args.version,
self.args.firmware, self.args.firmware,
self.args.body, self.args.body,
self.args.connect, self.args.connect,
int(datetime.datetime.now().timestamp()), self.args.color,
n1, self.args.region,
n2, BOARD_RESERVED,
n3, self.args.name.encode("ascii"),
n4,
n5,
n6,
n7,
n8,
) )
def generate(self):
self.logger.debug(f"Generating OTP")
self._process_args()
data = self._pack_struct()
open(self.args.file, "wb").write(data) open(self.args.file, "wb").write(data)
def flash(self):
self.logger.debug(f"Flashing OTP")
self._process_args()
data = self._pack_struct()
filename = f"otp_{self.args.name}_{self.timestamp}.bin"
open(filename, "wb").write(data)
try:
output = subprocess.check_output(
[
"STM32_Programmer_CLI",
"-q",
"-c",
f"port={self.args.port}",
"-d",
filename,
"0x1FFF7000",
]
)
assert output
self.logger.info(f"Success")
except subprocess.CalledProcessError as e:
self.logger.error(e.output.decode())
self.logger.error(f"Failed to call STM32_Programmer_CLI")
return
except Exception as e:
self.logger.error(f"Failed to call STM32_Programmer_CLI")
self.logger.exception(e)
return
# reboot
subprocess.check_output(
[
"STM32_Programmer_CLI",
"-q",
"-c",
f"port={self.args.port}",
]
)
os.remove(filename)
if __name__ == "__main__": if __name__ == "__main__":
Main()() Main()()