Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add device ID to prodtest #4735

Merged
merged 4 commits into from
Mar 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/embed/models/otp_layout.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@
#define FLASH_OTP_BLOCK_RANDOMNESS 3
#define FLASH_OTP_BLOCK_DEVICE_VARIANT 4
#define FLASH_OTP_BLOCK_FIRMWARE_VERSION 5
#define FLASH_OTP_BLOCK_DEVICE_ID 6
1 change: 1 addition & 0 deletions core/embed/projects/prodtest/.changelog.d/4735.added
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added device ID write/read commands.
1 change: 1 addition & 0 deletions core/embed/projects/prodtest/.changelog.d/4735.changed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Show device ID in protest QR code.
34 changes: 34 additions & 0 deletions core/embed/projects/prodtest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ If the OTP memory has not been written yet, it returns error code `no-data`.

Example:
```
otp-batch-read
# Reading device OTP memory...
# Bytes read: <hexadecimal string>
ERROR no-data "OTP block is empty."
Expand All @@ -493,6 +494,39 @@ otp-batch-write T2B1-231231 --dry-run
# Locking OTP block...
```


### otp-device-id-read
Retrieves the device ID string from the device's OTP memory. The device ID string is unique for each device.

If the OTP memory has not been written yet, it returns error code `no-data`.

Example:
```
otp-device-id-read
# Reading device OTP memory...
# Bytes read: <hexadecimal string>
ERROR no-data "OTP block is empty."
```

### otp-device-id-write
Writes the device ID string to the device's OTP memory. The device ID string is unique for each device.

The batch string can be up to 31 characters in length.

In non-production firmware, you must include `--execute` as the last parameter to write the data to the OTP memory. Conversely, in production firmware, you can use `--dry-run` as the last parameter to simulate the command without actually writing to the OTP memory.

Example:
```
otp-device-id-write 123456ABCD --dry-run
#
# !!! It's a dry run, OTP will be left unchanged.
# !!! Use '--execute' switch to write to OTP memory.
#
# Writing device batch info into OTP memory...
# Bytes written: 3132333435364142434400000000000000000000000000000000000000000000
# Locking OTP block...
```

### otp-variant-write
Writes up to 31 decimal values, each representing device variant options, to device's OTP memory. Each value must range from 0 to 255.

Expand Down
47 changes: 38 additions & 9 deletions core/embed/projects/prodtest/cmd/prodtest_otp_batch.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#include <rtl/cli.h>
#include <util/flash_otp.h>

static void prodtest_otp_batch_read(cli_t* cli) {
static void prodtest_otp_read(cli_t* cli, uint8_t block_num) {
if (cli_arg_count(cli) > 0) {
cli_error_arg_count(cli);
return;
Expand All @@ -33,9 +33,9 @@ static void prodtest_otp_batch_read(cli_t* cli) {

cli_trace(cli, "Reading device OTP memory...");

if (sectrue !=
flash_otp_read(FLASH_OTP_BLOCK_BATCH, 0, block, sizeof(block))) {
if (sectrue != flash_otp_read(block_num, 0, block, sizeof(block))) {
cli_error(cli, CLI_ERROR, "Failed to read OTP memory.");
return;
}

char block_hex[FLASH_OTP_BLOCK_SIZE * 2 + 1];
Expand Down Expand Up @@ -63,7 +63,7 @@ static void prodtest_otp_batch_read(cli_t* cli) {
}
}

static void prodtest_otp_batch_write(cli_t* cli) {
static void prodtest_otp_write(cli_t* cli, uint8_t block_num) {
const char* data = cli_arg(cli, "text");

if (strlen(data) == 0 || strlen(data) > FLASH_OTP_BLOCK_SIZE - 1) {
Expand Down Expand Up @@ -110,18 +110,17 @@ static void prodtest_otp_batch_write(cli_t* cli) {
return;
}

if (sectrue == flash_otp_is_locked(FLASH_OTP_BLOCK_BATCH)) {
if (sectrue == flash_otp_is_locked(block_num)) {
cli_error(cli, CLI_ERROR_LOCKED,
"OTP block is locked and cannot be written again.");
return;
}

cli_trace(cli, "Writing device batch info into OTP memory...");
cli_trace(cli, "Writing info into OTP memory...");
cli_trace(cli, "Bytes written: %s", block_hex);

if (!dry_run) {
if (sectrue !=
flash_otp_write(FLASH_OTP_BLOCK_BATCH, 0, block, sizeof(block))) {
if (sectrue != flash_otp_write(block_num, 0, block, sizeof(block))) {
cli_error(cli, CLI_ERROR, "Failed to write OTP block.");
return;
}
Expand All @@ -130,7 +129,7 @@ static void prodtest_otp_batch_write(cli_t* cli) {
cli_trace(cli, "Locking OTP block...");

if (!dry_run) {
if (sectrue != flash_otp_lock(FLASH_OTP_BLOCK_BATCH)) {
if (sectrue != flash_otp_lock(block_num)) {
cli_error(cli, CLI_ERROR, "Failed to lock the OTP block.");
return;
}
Expand All @@ -140,6 +139,22 @@ static void prodtest_otp_batch_write(cli_t* cli) {
cli_ok(cli, "");
}

static void prodtest_otp_batch_read(cli_t* cli) {
prodtest_otp_read(cli, FLASH_OTP_BLOCK_BATCH);
}

static void prodtest_otp_batch_write(cli_t* cli) {
prodtest_otp_write(cli, FLASH_OTP_BLOCK_BATCH);
}

static void prodtest_otp_device_id_read(cli_t* cli) {
prodtest_otp_read(cli, FLASH_OTP_BLOCK_DEVICE_ID);
}

static void prodtest_otp_device_id_write(cli_t* cli) {
prodtest_otp_write(cli, FLASH_OTP_BLOCK_DEVICE_ID);
}

// clang-format off

PRODTEST_CLI_CMD(
Expand All @@ -155,3 +170,17 @@ PRODTEST_CLI_CMD(
.info = "Write the device batch info into OTP memory",
.args = "<text> [--execute | --dry-run]"
);

PRODTEST_CLI_CMD(
.name = "otp-device-id-read",
.func = prodtest_otp_device_id_read,
.info = "Read the device ID from OTP memory",
.args = ""
);

PRODTEST_CLI_CMD(
.name = "otp-device-id-write",
.func = prodtest_otp_device_id_write,
.info = "Write the device ID into OTP memory",
.args = "<text> [--execute | --dry-run]"
);
2 changes: 1 addition & 1 deletion core/embed/projects/prodtest/cmd/prodtest_otp_variant.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ static void prodtest_otp_variant_write(cli_t* cli) {
}
#endif

if (sectrue == flash_otp_is_locked(FLASH_OTP_BLOCK_BATCH)) {
if (sectrue == flash_otp_is_locked(FLASH_OTP_BLOCK_DEVICE_VARIANT)) {
cli_error(cli, CLI_ERROR_LOCKED,
"OTP block is locked and cannot be written again.");
return;
Expand Down
13 changes: 6 additions & 7 deletions core/embed/projects/prodtest/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,12 @@ static void usb_init_all(void) {
}

static void show_welcome_screen(void) {
char dom[32] = {0};
// format: {MODEL_IDENTIFIER}YYMMDD
if ((sectrue ==
flash_otp_read(FLASH_OTP_BLOCK_BATCH, 0, (uint8_t *)dom, 32) &&
dom[31] == 0 && cstr_starts_with(dom, MODEL_IDENTIFIER))) {
screen_prodtest_info(dom, strlen(dom), dom + sizeof(MODEL_IDENTIFIER) - 1,
strlen(dom) - sizeof(MODEL_IDENTIFIER) + 1);
char device_id[FLASH_OTP_BLOCK_SIZE];

if (sectrue == flash_otp_read(FLASH_OTP_BLOCK_DEVICE_ID, 0,
(uint8_t *)device_id, sizeof(device_id)) &&
(device_id[0] != 0xFF)) {
screen_prodtest_info(device_id, strnlen(device_id, sizeof(device_id) - 1));
} else {
screen_prodtest_welcome();
}
Expand Down
3 changes: 1 addition & 2 deletions core/embed/rust/rust_ui_prodtest.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include <trezor_types.h>

void screen_prodtest_info(char* id, uint8_t id_len, char* date,
uint8_t date_len);
void screen_prodtest_info(char* id, uint8_t id_len);

void screen_prodtest_welcome(void);

Expand Down
10 changes: 2 additions & 8 deletions core/embed/rust/src/ui/api/prodtest_c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,10 @@ extern "C" fn screen_prodtest_welcome() {
}

#[no_mangle]
extern "C" fn screen_prodtest_info(
id: *const cty::c_char,
id_len: u8,
date: *const cty::c_char,
date_len: u8,
) {
extern "C" fn screen_prodtest_info(id: *const cty::c_char, id_len: u8) {
let id = unwrap!(unsafe { from_c_array(id, id_len as usize) });
let date = unwrap!(unsafe { from_c_array(date, date_len as usize) });

ModelUI::screen_prodtest_info(id, date);
ModelUI::screen_prodtest_info(id);
}

#[no_mangle]
Expand Down
4 changes: 2 additions & 2 deletions core/embed/rust/src/ui/layout_bolt/prodtest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl ProdtestUI for UIBolt {
display::fade_backlight_duration(theme::backlight::get_backlight_normal(), 150);
}

fn screen_prodtest_info(id: &str, date: &str) {
fn screen_prodtest_info(id: &str) {
display::sync();
let qr = Qr::new(id, true);
let mut qr = unwrap!(qr).with_border(4);
Expand All @@ -50,7 +50,7 @@ impl ProdtestUI for UIBolt {

shape::Text::new(
screen().bottom_center() - Offset::y(10),
date,
id,
fonts::FONT_BOLD_UPPER,
)
.with_fg(Color::white())
Expand Down
4 changes: 2 additions & 2 deletions core/embed/rust/src/ui/layout_caesar/prodtest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl ProdtestUI for UICaesar {
display::refresh();
}

fn screen_prodtest_info(id: &str, date: &str) {
fn screen_prodtest_info(id: &str) {
display::sync();
let qr = Qr::new(id, true);
let mut qr = unwrap!(qr).with_border(1);
Expand All @@ -43,7 +43,7 @@ impl ProdtestUI for UICaesar {
render_on_display(None, Some(Color::black()), |target| {
qr.render(target);

shape::Text::new(screen().bottom_center(), date, fonts::FONT_BOLD_UPPER)
shape::Text::new(screen().bottom_center(), id, fonts::FONT_BOLD_UPPER)
.with_fg(Color::white())
.with_align(Alignment::Center)
.render(target);
Expand Down
2 changes: 1 addition & 1 deletion core/embed/rust/src/ui/ui_prodtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use heapless::Vec;
pub trait ProdtestUI {
fn screen_prodtest_welcome();

fn screen_prodtest_info(id: &str, date: &str);
fn screen_prodtest_info(id: &str);

fn screen_prodtest_show_text(text: &str);

Expand Down