Skip to content
Open
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
6 changes: 6 additions & 0 deletions py/bitbox02/bitbox02/bitbox02/bootloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,12 @@ def erase(self) -> None:
"""
self._erase(0)

def chip_erase(self) -> None:
"""
Issues a chip erase. Only supported by development bootloaders.
"""
self._query(b"c")

def erased(self) -> bool:
"""
Returns True if the the device contains no firmware.
Expand Down
9 changes: 9 additions & 0 deletions py/send_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -1704,6 +1704,14 @@ def _get_hardware(self) -> None:
def _erase(self) -> None:
self._device.erase()

def _chip_erase(self) -> None:
print("You will need to backup the flash to avoid permanently bricking the device")
if input("Type CHIP ERASE to erase the chip: ") != "CHIP ERASE":
print("Chip erase aborted")
return
self._device.chip_erase()
self._stop = True

def _show_fw_hash(self) -> None:
self._device.set_show_firmware_hash(True)

Expand All @@ -1727,6 +1735,7 @@ def _menu(self) -> None:
("Print versions", self._get_versions),
("Print hardware variant", self._get_hardware),
("Erase firmware", self._erase),
("Chip erase", self._chip_erase),
("Show firmware hash at startup", self._show_fw_hash),
("Don't show firmware hash at startup", self._dont_show_fw_hash),
("Get firmware & sigkey hashes", self._get_hashes),
Expand Down
14 changes: 13 additions & 1 deletion scripts/template-bootloader.jlink
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
loadfile $file
// Reset and halt the device
r

// Temporarily disable bootloader protection before flashing.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? Doesn't the chip erase bootprot too?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the user row is not reset by chip erase.

// NVMCTRL_CTRLB = NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_SBPDIS.
w2 0x41004004 0xA51A

// Flash
loadfile $file 0 noreset

// Reset
r

// Quit
q
48 changes: 46 additions & 2 deletions src/bootloader/bootloader.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@
#define OP_SET_SHOW_FIRMWARE_HASH ((uint8_t)'H') /* 0x4A */
// OP_HARDWARE - Return the secure chip variant.
#define OP_HARDWARE ((uint8_t)'W') /* 0x57 */
// OP_CHIP_ERASE - Issue a chip erase. Only available in non-production dev builds.
#define OP_CHIP_ERASE ((uint8_t)'c') /* 0x63 */

// API return codes
#define OP_STATUS_OK ((uint8_t)0)
Expand Down Expand Up @@ -113,6 +115,9 @@ static_assert(sizeof(((boot_data_t*)0)->fields) <= FLASH_BOOTDATA_LEN, "boot_dat

static bool _loading_ready = false;
static uint8_t _firmware_num_chunks = 0;
#if defined(BOOTLOADER_DEVDEVICE) && !defined(BOOTLOADER_PRODUCTION)
static bool _chip_erase_requested = false;
#endif
// Indicates whether the whole app flash contains only 0xFF.
// This controls bootloader text messages on the screen.
// The value is computed at bootloader enter.
Expand Down Expand Up @@ -317,7 +322,9 @@ static void _render_message(const char* message, int duration)
UG_ClearBuffer();
UG_PutString(0, 0, message);
UG_SendBuffer();
delay_ms(duration);
if (duration > 0) {
delay_ms(duration);
}
}

void bootloader_render_default_screen(void)
Expand Down Expand Up @@ -854,6 +861,21 @@ static size_t _api_hardware(uint8_t* output)
return _report_status(OP_STATUS_OK, output) + 1;
}

#if defined(BOOTLOADER_DEVDEVICE) && !defined(BOOTLOADER_PRODUCTION)
static size_t _api_chip_erase(uint8_t* output)
{
_loading_ready = false;
if (DSU->STATUSB.bit.CELCK) {
return _report_status(OP_STATUS_ERR_LOCK, output);
}
if (periph_unlock(DSU) != ERR_NONE) {
return _report_status(OP_STATUS_ERR_UNLOCK, output);
}
_chip_erase_requested = true;
return _report_status(OP_STATUS_OK, output);
}
#endif

static size_t _api_command(const uint8_t* input, uint8_t* output, const size_t max_out_len)
{
memset(output, 0, max_out_len);
Expand Down Expand Up @@ -907,6 +929,11 @@ static size_t _api_command(const uint8_t* input, uint8_t* output, const size_t m
case OP_HARDWARE:
len = _api_hardware(output);
break;
#if defined(BOOTLOADER_DEVDEVICE) && !defined(BOOTLOADER_PRODUCTION)
case OP_CHIP_ERASE:
len = _api_chip_erase(output);
break;
#endif
default:
len = _report_status(OP_STATUS_ERR_INVALID_CMD, output);
_loading_ready = false;
Expand All @@ -932,9 +959,25 @@ static void _api_setup(void)
usb_processing_register_cmds(usb_processing_hww(), cmd_callbacks, 1);
}

void bootloader_process_pending_action(void)
{
#if defined(BOOTLOADER_DEVDEVICE) && !defined(BOOTLOADER_PRODUCTION)
if (!_chip_erase_requested) {
return;
}
_render_message("Chip erase", 0);
bootloader_close_interfaces();
__disable_irq();
DSU->STATUSA.reg = DSU_STATUSA_DONE | DSU_STATUSA_BERR | DSU_STATUSA_FAIL | DSU_STATUSA_PERR;
DSU->CTRL.reg = DSU_CTRL_CE;
while (DSU->STATUSA.bit.DONE == 0) {
}
_reset_mcu();
#endif
}

static void _check_init(boot_data_t* data)
{
#ifdef BOOTLOADER_PRODUCTION
// Enable boot protection if not already enabled. The
// BOOTPROT fuse is persistent and not erased on chip erase.
while (NVMCTRL->STATUS.bit.READY == 0) {
Expand All @@ -957,6 +1000,7 @@ static void _check_init(boot_data_t* data)
_reset_mcu();
}

#ifdef BOOTLOADER_PRODUCTION
// Hard lock the Device Service Unit, i.e., set the PAC write-protection bit,
// which can only be cleared by a hardware reset. Because the DSU is soft
// locked by default on reset, an unlock is required before a hard lock.
Expand Down
5 changes: 5 additions & 0 deletions src/bootloader/bootloader.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ void bootloader_jump(void);
*/
void bootloader_render_default_screen(void);

/**
* Runs pending bootloader actions that must happen after a USB response was sent.
*/
void bootloader_process_pending_action(void);

#if PLATFORM_BITBOX02PLUS
/**
* Renders a BLE pairing confirmations screen. Use the `confirmed` argument to display the
Expand Down
6 changes: 6 additions & 0 deletions src/bootloader/startup.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ int main(void)
// screen_sprintf_debug(1000, "got frame");
da14531_handler(frame, uart_write_queue);
}
if (!hww_data && rust_bytequeue_num(uart_write_queue) == 0) {
bootloader_process_pending_action();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why all the effort to wait until the response is done and not do it right away when handling OP_CHIP_ERASE? We do it too for the hww reset and reboot API calls in the firmware - the client simply knows to not expect a response in these cases.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, we are pretty unique in having a request/reponse protocol and allow some requests to not have responses, so I totally forgot about it.

}
}
#endif

Expand All @@ -167,6 +170,9 @@ int main(void)
hww_data = NULL;
}
}
if (!hww_data && !hid_hww_write_busy()) {
bootloader_process_pending_action();
}
#if PLATFORM_BITBOX02PLUS == 1
}
#endif
Expand Down
5 changes: 5 additions & 0 deletions src/usb/class/hid/hww/hid_hww.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ bool hid_hww_write_poll(const uint8_t* data)
return false;
}

bool hid_hww_write_busy(void)
{
return _send_busy;
}

/**
* The callback function is called after usb data has been received (endpoint = OUT).
*/
Expand Down
5 changes: 5 additions & 0 deletions src/usb/class/hid/hww/hid_hww.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ void hid_hww_setup(void);
*/
bool hid_hww_write_poll(const uint8_t* data);

/**
* Returns true while an outgoing HID report transfer is in flight.
*/
bool hid_hww_write_busy(void);

/**
* Read data
*
Expand Down
Loading