Skip to content

[manuf] Backport #25388 #27666

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

Merged
merged 1 commit into from
Jul 24, 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
29 changes: 26 additions & 3 deletions sw/device/lib/testing/json/provisioning_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@ extern "C" {
#define MODULE_ID MAKE_MODULE_ID('j', 'p', 'd')

/**
* Provisioning data imported onto the device in CP.
* Provisioning data imported onto the device during CP.
*/
// clang-format off
#define STRUCT_MANUF_CP_PROVISIONING_DATA(field, string) \
field(device_id, uint32_t, 8) \
field(manuf_state, uint32_t, 8) \
field(wafer_auth_secret, uint32_t, 8) \
field(test_unlock_token_hash, uint64_t, 2) \
field(test_exit_token_hash, uint64_t, 2)
Expand All @@ -28,6 +26,31 @@ UJSON_SERDE_STRUCT(ManufCpProvisioningData, \
STRUCT_MANUF_CP_PROVISIONING_DATA);
// clang-format on

/**
* Provisioning data exported off the device during CP.
*/
// clang-format off
#define STRUCT_MANUF_CP_PROVISIONING_DATA_OUT(field, string) \
field(cp_device_id, uint32_t, 4)
UJSON_SERDE_STRUCT(ManufCpProvisioningDataOut, \
manuf_cp_provisioning_data_out_t, \
STRUCT_MANUF_CP_PROVISIONING_DATA_OUT);
// clang-format on

/**
* Test factory data imported onto the device prior to CP initialize stage.
*/
// clang-format off
#define STRUCT_MANUF_CP_TEST_DATA(field, string) \
field(lot_name, uint32_t) \
field(wafer_number, uint32_t) \
field(wafer_x_coord, uint32_t) \
field(wafer_y_coord, uint32_t)
UJSON_SERDE_STRUCT(ManufCpTestData, \
manuf_cp_test_data_t, \
STRUCT_MANUF_CP_TEST_DATA);
// clang-format on

/**
* Provisioning data imported onto the device in FT during individualization.
*/
Expand Down
3 changes: 1 addition & 2 deletions sw/device/silicon_creator/manuf/base/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ opentitan_binary(
"//sw/device/lib/testing/test_framework:ottf_test_config",
"//sw/device/lib/testing/test_framework:status",
"//sw/device/lib/testing/test_framework:ujson_ottf",
"//sw/device/silicon_creator/manuf/data/ast:fake",
"//sw/device/silicon_creator/manuf/lib:flash_info_fields",
"//sw/device/silicon_creator/manuf/lib:individualize",
"//sw/device/silicon_creator/manuf/lib:otp_fields",
Expand Down Expand Up @@ -169,7 +168,7 @@ opentitan_test(
test_cmd = """
--provisioning-sram-elf={sram_cp_provision}
--test-sram-elf={sram_cp_provision_functest}
""" + CP_PROVISIONING_INPUTS,
""",
test_harness = "//sw/host/tests/manuf/cp_provision_functest",
),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,17 @@ EARLGREY_SKUS = {
},
} | EXT_EARLGREY_SKUS

_DEVICE_ID_AND_TEST_TOKENS = """
--device-id="0x11111111_22222222_33333333_44444444_55555555_66666666_77777777_88888888"
_TEST_TOKENS = """
--test-unlock-token="0x11111111_11111111_11111111_11111111"
--test-exit-token="0x11111111_11111111_11111111_11111111"
"""

CP_PROVISIONING_INPUTS = _DEVICE_ID_AND_TEST_TOKENS + """
--manuf-state="0x00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000"
CP_PROVISIONING_INPUTS = _TEST_TOKENS + """
--wafer-auth-secret="0x00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000"
"""

FT_PROVISIONING_INPUTS = _DEVICE_ID_AND_TEST_TOKENS + """
FT_PROVISIONING_INPUTS = _TEST_TOKENS + """
--device-id="0x11111111_22222222_33333333_44444444_55555555_66666666_77777777_88888888"
--target-mission-mode-lc-state="prod"
--rma-unlock-token="0x01234567_89abcdef_01234567_89abcdef"
--rom-ext-measurement="0x11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111"
Expand Down
189 changes: 105 additions & 84 deletions sw/device/silicon_creator/manuf/base/sram_cp_provision.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
#include "sw/device/lib/testing/test_framework/ottf_test_config.h"
#include "sw/device/lib/testing/test_framework/ujson_ottf.h"
#include "sw/device/silicon_creator/manuf/base/flash_info_permissions.h"
#include "sw/device/silicon_creator/manuf/data/ast/calibration_values.h"
#include "sw/device/silicon_creator/manuf/lib/flash_info_fields.h"
#include "sw/device/silicon_creator/manuf/lib/individualize.h"
#include "sw/device/silicon_creator/manuf/lib/otp_fields.h"
Expand All @@ -38,6 +37,8 @@ static dif_pinmux_t pinmux;
static dif_flash_ctrl_state_t flash_ctrl_state;
static dif_lc_ctrl_t lc_ctrl;

static uint32_t ast_cfg_data[kFlashInfoAstCalibrationDataSizeIn32BitWords];

/**
* Initializes all DIF handles used in this SRAM program.
*/
Expand All @@ -61,99 +62,106 @@ static void manually_init_ast(uint32_t *data) {
}
}

static status_t flash_info_page_0_erase(void) {
uint32_t byte_address = 0;
// DeviceId and ManufState are located on the same flash info page.
TRY(flash_ctrl_testutils_info_region_setup_properties(
&flash_ctrl_state, kFlashInfoFieldDeviceId.page,
kFlashInfoFieldDeviceId.bank, kFlashInfoFieldDeviceId.partition,
kFlashInfoPage0Permissions, &byte_address));
TRY(flash_ctrl_testutils_erase_page(&flash_ctrl_state, byte_address,
kFlashInfoFieldDeviceId.partition,
kDifFlashCtrlPartitionTypeInfo));
return OK_STATUS();
}

static status_t flash_info_page_0_write(
manuf_cp_provisioning_data_t *provisioning_data) {
uint32_t byte_address = 0;
static status_t flash_info_page_0_read_and_validate(
manuf_cp_provisioning_data_out_t *console_out) {
TRY(flash_ctrl_testutils_info_region_setup_properties(
&flash_ctrl_state, kFlashInfoFieldDeviceId.page,
kFlashInfoFieldDeviceId.bank, kFlashInfoFieldDeviceId.partition,
kFlashInfoPage0Permissions, &byte_address));

// Write DeviceId.
TRY(flash_ctrl_testutils_write(
&flash_ctrl_state, byte_address, kFlashInfoFieldDeviceId.partition,
provisioning_data->device_id, kDifFlashCtrlPartitionTypeInfo,
kHwCfgDeviceIdSizeIn32BitWords));

// Write ManufState (on same page as DeviceId).
TRY(flash_ctrl_testutils_write(
&flash_ctrl_state, byte_address + kFlashInfoFieldManufState.byte_offset,
kFlashInfoFieldManufState.partition, provisioning_data->manuf_state,
kDifFlashCtrlPartitionTypeInfo, kHwCfgManufStateSizeIn32BitWords));

// Write AST calibration values (on same page as DeviceId).
TRY(flash_ctrl_testutils_write(
&flash_ctrl_state,
byte_address + kFlashInfoFieldAstCalibrationData.byte_offset,
kFlashInfoFieldAstCalibrationData.partition, ast_cfg_data,
kDifFlashCtrlPartitionTypeInfo,
&flash_ctrl_state, kFlashInfoFieldCpDeviceId.page,
kFlashInfoFieldCpDeviceId.bank, kFlashInfoFieldCpDeviceId.partition,
kFlashInfoPage0Permissions, /*offset=*/NULL));

// Read (wafer) lot name.
uint32_t lot_name = 0;
static_assert(kFlashInfoFieldLotNameSizeIn32BitWords == 1,
"Lot name should fit in <32bits.");
TRY(manuf_flash_info_field_read(&flash_ctrl_state, kFlashInfoFieldLotName,
&lot_name,
kFlashInfoFieldLotNameSizeIn32BitWords));

// Read wafer number.
uint32_t wafer_number = 0;
static_assert(kFlashInfoFieldWaferNumberSizeIn32BitWords == 1,
"Wafer number should fit in <32bits.");
TRY(manuf_flash_info_field_read(&flash_ctrl_state, kFlashInfoFieldWaferNumber,
&wafer_number,
kFlashInfoFieldWaferNumberSizeIn32BitWords));

// Read wafer X coord.
uint32_t wafer_x_coord = 0;
static_assert(kFlashInfoFieldWaferXCoordSizeIn32BitWords == 1,
"Wafer X coordinate value should fit in <32bits.");
TRY(manuf_flash_info_field_read(&flash_ctrl_state, kFlashInfoFieldWaferXCoord,
&wafer_x_coord,
kFlashInfoFieldWaferXCoordSizeIn32BitWords));

// Read wafer Y coord.
uint32_t wafer_y_coord = 0;
static_assert(kFlashInfoFieldWaferYCoordSizeIn32BitWords == 1,
"Wafer Y coordinate value should fit in <32bits.");
TRY(manuf_flash_info_field_read(&flash_ctrl_state, kFlashInfoFieldWaferYCoord,
&wafer_y_coord,
kFlashInfoFieldWaferYCoordSizeIn32BitWords));

// Read AST calibration values into RAM.
TRY(manuf_flash_info_field_read(
&flash_ctrl_state, kFlashInfoFieldAstCalibrationData, ast_cfg_data,
kFlashInfoAstCalibrationDataSizeIn32BitWords));

return OK_STATUS();
}
// Encode CP device ID.
// HW origin portion of CP device.
// "0x00024001" encodes:
// - a SiliconCreator ID of "0x4001" for "Nuvoton", and
// - a Product ID of "0x0002" for Earlgrey A1 silicon.
console_out->cp_device_id[0] = 0x00024001u;
// Device Identification Number portion of CP device ID.
uint32_t year = (lot_name >> 24) & 0xf;
uint32_t week = (lot_name >> 16) & 0xff;
uint32_t lot_number = lot_name & 0xfff;
console_out->cp_device_id[1] =
(wafer_number << 24) | (lot_number << 12) | (week << 4) | year;
console_out->cp_device_id[2] = (wafer_y_coord << 12) | wafer_x_coord;
// Reserved word; set to 0.
console_out->cp_device_id[3] = 0;

// Write CP device ID.
TRY(manuf_flash_info_field_write(&flash_ctrl_state, kFlashInfoFieldCpDeviceId,
console_out->cp_device_id,
kFlashInfoFieldCpDeviceIdSizeIn32BitWords,
/*erase_page_before_write=*/false));

static status_t wafer_auth_secret_flash_info_page_erase(void) {
uint32_t byte_address = 0;
TRY(flash_ctrl_testutils_info_region_setup_properties(
&flash_ctrl_state, kFlashInfoFieldWaferAuthSecret.page,
kFlashInfoFieldWaferAuthSecret.bank,
kFlashInfoFieldWaferAuthSecret.partition, kFlashInfoPage3WritePermissions,
&byte_address));
TRY(flash_ctrl_testutils_erase_page(&flash_ctrl_state, byte_address,
kFlashInfoFieldWaferAuthSecret.partition,
kDifFlashCtrlPartitionTypeInfo));
return OK_STATUS();
}

static status_t wafer_auth_secret_flash_info_page_write(
manuf_cp_provisioning_data_t *provisioning_data) {
manuf_cp_provisioning_data_t *console_in) {
uint32_t byte_address = 0;
TRY(flash_ctrl_testutils_info_region_setup_properties(
&flash_ctrl_state, kFlashInfoFieldWaferAuthSecret.page,
kFlashInfoFieldWaferAuthSecret.bank,
kFlashInfoFieldWaferAuthSecret.partition, kFlashInfoPage3WritePermissions,
&byte_address));
TRY(flash_ctrl_testutils_write(
&flash_ctrl_state, byte_address, kFlashInfoFieldWaferAuthSecret.partition,
provisioning_data->manuf_state, kDifFlashCtrlPartitionTypeInfo,
kFlashInfoWaferAuthSecretSizeIn32BitWords));
TRY(manuf_flash_info_field_write(
&flash_ctrl_state, kFlashInfoFieldWaferAuthSecret,
console_in->wafer_auth_secret,
kFlashInfoFieldWaferAuthSecretSizeIn32BitWords,
/*erase_page_before_write=*/true));
return OK_STATUS();
}

static status_t print_inputs_to_console(
manuf_cp_provisioning_data_t *provisioning_data) {
manuf_cp_provisioning_data_t *console_in) {
uint32_t high;
uint32_t low;

LOG_INFO("Device ID:");
for (size_t i = 0; i < kHwCfgDeviceIdSizeIn32BitWords; ++i) {
LOG_INFO("0x%08x", provisioning_data->device_id[i]);
}
LOG_INFO("Test Unlock Token Hash:");
for (size_t i = 0; i < ARRAYSIZE(provisioning_data->test_unlock_token_hash);
++i) {
high = provisioning_data->test_unlock_token_hash[i] >> 32;
low = provisioning_data->test_unlock_token_hash[i] & 0xffffffff;
for (size_t i = 0; i < ARRAYSIZE(console_in->test_unlock_token_hash); ++i) {
high = console_in->test_unlock_token_hash[i] >> 32;
low = console_in->test_unlock_token_hash[i] & 0xffffffff;
LOG_INFO("0x%08x%08x", high, low);
}
LOG_INFO("Test Exit Token Hash:");
for (size_t i = 0; i < ARRAYSIZE(provisioning_data->test_exit_token_hash);
++i) {
high = provisioning_data->test_exit_token_hash[i] >> 32;
low = provisioning_data->test_exit_token_hash[i] & 0xffffffff;
for (size_t i = 0; i < ARRAYSIZE(console_in->test_exit_token_hash); ++i) {
high = console_in->test_exit_token_hash[i] >> 32;
low = console_in->test_exit_token_hash[i] & 0xffffffff;
LOG_INFO("0x%08x%08x", high, low);
}
return OK_STATUS();
Expand All @@ -162,33 +170,46 @@ static status_t print_inputs_to_console(
/**
* Provision flash info pages 0 and 3, and OTP Secret0 partition.
*/
static status_t provision(ujson_t *uj) {
static status_t provision(ujson_t *uj,
manuf_cp_provisioning_data_out_t *console_out) {
// Wait for input console data.
LOG_INFO("Waiting for CP provisioning data ...");
manuf_cp_provisioning_data_t provisioning_data;
TRY(ujson_deserialize_manuf_cp_provisioning_data_t(uj, &provisioning_data));
TRY(print_inputs_to_console(&provisioning_data));
TRY(flash_ctrl_testutils_wait_for_init(&flash_ctrl_state));
TRY(flash_info_page_0_erase());
TRY(wafer_auth_secret_flash_info_page_erase());
TRY(flash_info_page_0_write(&provisioning_data));
TRY(wafer_auth_secret_flash_info_page_write(&provisioning_data));
TRY(manuf_individualize_device_secret0(&lc_ctrl, &otp_ctrl,
&provisioning_data));
LOG_INFO("CP provisioning done.");
manuf_cp_provisioning_data_t console_in;
TRY(ujson_deserialize_manuf_cp_provisioning_data_t(uj, &console_in));
TRY(print_inputs_to_console(&console_in));

// Provision flash info page 3 (wafer authentication secret).
TRY(wafer_auth_secret_flash_info_page_write(&console_in));

// Burn test tokens into OTP.
TRY(manuf_individualize_device_secret0(&lc_ctrl, &otp_ctrl, &console_in));

// Send data back to host.
LOG_INFO("Exporting CP device ID ...");
RESP_OK(ujson_serialize_manuf_cp_provisioning_data_out_t, uj, console_out);

return OK_STATUS();
}

bool test_main(void) {
// Initialize AST, DIF handles, pinmux, and UART.
manually_init_ast(ast_cfg_data);
// Initialize DIF handles, pinmux, and console.
CHECK_STATUS_OK(peripheral_handles_init());
pinmux_testutils_init(&pinmux);
ottf_console_init();
ujson_t uj = ujson_ottf_console();

// Extract factory data from flash info page 0.
manuf_cp_provisioning_data_out_t console_out;
CHECK_STATUS_OK(flash_ctrl_testutils_wait_for_init(&flash_ctrl_state));
CHECK_STATUS_OK(flash_info_page_0_read_and_validate(&console_out));

// Initialize AST.
manually_init_ast(ast_cfg_data);
LOG_INFO("AST manually configured.");

// Perform CP provisioning operations.
CHECK_STATUS_OK(provision(&uj));
CHECK_STATUS_OK(provision(&uj, &console_out));

LOG_INFO("CP provisioning done.");
return true;
}
Loading
Loading