Skip to content

Commit 7d69dd6

Browse files
committed
Make bootloaders check for the MCU Security Bit.
If Security Bit is set, it will not let a client read from flash memory, and will always erase the full sketch flash memory, when flashing, to avoid anyone trying to perform a partial flash. If BOOTPROT is set to 2 and Security Bit is set, the bootloader will be fully protected, and it should not be trivial to read the sketch out of the MCU. Without these changes, the bootloader ignores the Security Bit, and let clients like bossac.exe read the entire flash memory.
1 parent 56e13aa commit 7d69dd6

File tree

1 file changed

+94
-30
lines changed

1 file changed

+94
-30
lines changed

bootloaders/zero/sam_ba_monitor.c

+94-30
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
#include <stdlib.h>
3131

3232
const char RomBOOT_Version[] = SAM_BA_VERSION;
33-
const char RomBOOT_ExtendedCapabilities[] = "[Arduino:XYZ]";
33+
// X = Chip Erase, Y = Write Buffer, Z = Checksum Buffer, P = Secure Bit Aware
34+
const char RomBOOT_ExtendedCapabilities[] = "[Arduino:XYZP]";
3435

3536
/* Provides one common interface to handle both USART and USB-CDC */
3637
typedef struct
@@ -83,6 +84,8 @@ const t_monitor_if usbcdc_if =
8384
/* The pointer to the interface object use by the monitor */
8485
t_monitor_if * ptr_monitor_if;
8586

87+
bool b_security_enabled = false;
88+
8689
/* b_terminal_mode mode (ascii) or hex mode */
8790
volatile bool b_terminal_mode = false;
8891
volatile bool b_sam_ba_interface_usart = false;
@@ -225,6 +228,10 @@ void sam_ba_putdata_term(uint8_t* data, uint32_t length)
225228
volatile uint32_t sp;
226229
void call_applet(uint32_t address)
227230
{
231+
if (b_security_enabled) {
232+
return;
233+
}
234+
228235
uint32_t app_start_address;
229236

230237
__disable_irq();
@@ -242,6 +249,7 @@ void call_applet(uint32_t address)
242249
}
243250

244251
uint32_t current_number;
252+
uint32_t erased_from = 0;
245253
uint32_t i, length;
246254
uint8_t command, *ptr_data, *ptr, data[SIZEBUFMAX];
247255
uint8_t j;
@@ -264,6 +272,20 @@ static void put_uint32(uint32_t n)
264272
sam_ba_putdata( ptr_monitor_if, buff, 8);
265273
}
266274

275+
static void eraseFlash(uint32_t dst_addr)
276+
{
277+
erased_from = dst_addr;
278+
while (dst_addr < MAX_FLASH)
279+
{
280+
// Execute "ER" Erase Row
281+
NVMCTRL->ADDR.reg = dst_addr / 2;
282+
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
283+
while (NVMCTRL->INTFLAG.bit.READY == 0)
284+
;
285+
dst_addr += PAGE_SIZE * 4; // Skip a ROW
286+
}
287+
}
288+
267289
#ifdef ENABLE_JTAG_LOAD
268290
static uint32_t offset = __UINT32_MAX__;
269291
static bool flashNeeded = false;
@@ -284,7 +306,7 @@ static void sam_ba_monitor_loop(void)
284306
{
285307
sam_ba_putdata(ptr_monitor_if, "\n\r", 2);
286308
}
287-
if (command == 'S')
309+
if (command == 'S') // Write memory (normally RAM, but might be flash, if client handles the Flash MCU commands?)
288310
{
289311
//Check if some data are remaining in the "data" buffer
290312
if(length>i)
@@ -318,37 +340,80 @@ static void sam_ba_monitor_loop(void)
318340

319341
__asm("nop");
320342
}
321-
else if (command == 'R')
343+
else if (command == 'R') // Read memory (flash or RAM)
322344
{
345+
// Flash memory starts at address 0 and runs to flash size 0x40000 (256 KByte)
346+
347+
// Internal RWW section is at adress 0x400000. RWW is flash used for EEPROM emulation. Will not let anyone read that, when in secure mode, either.
348+
// Bootloader ends at 0x1FFF, so user programs start at 0x2000
349+
// RAM starts at 0x20000000, so redirect FLASH reads into RAM reads, when in secure mode
350+
if (b_security_enabled && ((uint32_t)ptr_data >= 0x0000 && (uint32_t)ptr_data < 0x20000000))
351+
{
352+
ptr_data = (uint8_t *)0x20005000;
353+
}
354+
323355
sam_ba_putdata_xmd(ptr_monitor_if, ptr_data, current_number);
324356
}
325-
else if (command == 'O')
357+
else if (command == 'O') // write byte
326358
{
327359
*ptr_data = (char) current_number;
328360
}
329-
else if (command == 'H')
361+
else if (command == 'H') // Write half word
330362
{
331363
*((uint16_t *) ptr_data) = (uint16_t) current_number;
332364
}
333-
else if (command == 'W')
365+
else if (command == 'W') // Write word
334366
{
335367
*((int *) ptr_data) = current_number;
336368
}
337-
else if (command == 'o')
369+
else if (command == 'o') // Read byte
338370
{
371+
// Flash memory starts at address 0 and runs to flash size 0x40000 (256 KByte). RAM starts at 0x20000000.
372+
// Intern RWW section is at adress 0x400000. RWW is flash used for EEPROM emulation. Will not let anyone read that, when in secure mode, either.
373+
// BOSSA reads address 0 to check something, but using read word instead of read byte, but in any case allow reading first byte
374+
// Bootloader ends at 0x1FFF, so user programs start at 0x2000
375+
if (b_security_enabled && ((uint32_t)ptr_data > 0x0003 && (uint32_t)ptr_data < 0x20000000))
376+
{
377+
ptr_data = (uint8_t*) &current_number;
378+
}
379+
339380
sam_ba_putdata_term(ptr_data, 1);
340381
}
341-
else if (command == 'h')
382+
else if (command == 'h') // Read half word
342383
{
343-
current_number = *((uint16_t *) ptr_data);
384+
// Flash memory starts at address 0 and runs to flash size 0x40000 (256 KByte). RAM starts at 0x20000000.
385+
// Intern RWW section is at adress 0x400000. RWW is flash used for EEPROM emulation. Will not let anyone read that, when in secure mode, either.
386+
// BOSSA reads address 0 to check something, but using read word instead of read byte, but in any case allow reading first byte
387+
// Bootloader ends at 0x1FFF, so user programs start at 0x2000
388+
if (b_security_enabled && ((uint32_t)ptr_data > 0x0003 && (uint32_t)ptr_data < 0x20000000))
389+
{
390+
current_number = 0;
391+
}
392+
else
393+
{
394+
current_number = *((uint16_t *) ptr_data);
395+
}
396+
344397
sam_ba_putdata_term((uint8_t*) &current_number, 2);
345398
}
346-
else if (command == 'w')
399+
else if (command == 'w') // Read word
347400
{
348-
current_number = *((uint32_t *) ptr_data);
401+
// Flash memory starts at address 0 and runs to flash size 0x40000 (256 KByte). RAM starts at 0x20000000.
402+
// Intern RWW section is at adress 0x400000. RWW is flash used for EEPROM emulation. Will not let anyone read that, when in secure mode, either.
403+
// BOSSA reads address 0 to check something, but using read word instead of read byte, but in any case allow reading first byte
404+
// Bootloader ends at 0x1FFF, so user programs start at 0x2000
405+
if (b_security_enabled && ((uint32_t)ptr_data > 0x0003 && (uint32_t)ptr_data < 0x20000000))
406+
{
407+
current_number = 0;
408+
}
409+
else
410+
{
411+
current_number = *((uint32_t *) ptr_data);
412+
}
413+
349414
sam_ba_putdata_term((uint8_t*) &current_number, 4);
350415
}
351-
else if (command == 'G')
416+
else if (!b_security_enabled && command == 'G') // Execute code. Will not allow when security is enabled.
352417
{
353418
call_applet(current_number);
354419
/* Rebase the Stack Pointer */
@@ -358,20 +423,20 @@ static void sam_ba_monitor_loop(void)
358423
ptr_monitor_if->put_c(0x6);
359424
}
360425
}
361-
else if (command == 'T')
426+
else if (command == 'T') // Turn on terminal mode
362427
{
363428
b_terminal_mode = 1;
364429
sam_ba_putdata(ptr_monitor_if, "\n\r", 2);
365430
}
366-
else if (command == 'N')
431+
else if (command == 'N') // Turn off terminal mode
367432
{
368433
if (b_terminal_mode == 0)
369434
{
370435
sam_ba_putdata( ptr_monitor_if, "\n\r", 2);
371436
}
372437
b_terminal_mode = 0;
373438
}
374-
else if (command == 'V')
439+
else if (command == 'V') // Read version information
375440
{
376441
sam_ba_putdata( ptr_monitor_if, "v", 1);
377442
sam_ba_putdata( ptr_monitor_if, (uint8_t *) RomBOOT_Version, strlen(RomBOOT_Version));
@@ -391,7 +456,7 @@ static void sam_ba_monitor_loop(void)
391456
sam_ba_putdata( ptr_monitor_if, (uint8_t *) &(__TIME__), i);
392457
sam_ba_putdata( ptr_monitor_if, "\n\r", 2);
393458
}
394-
else if (command == 'X')
459+
else if (command == 'X') // Erase flash
395460
{
396461
// Syntax: X[ADDR]#
397462
// Erase the flash memory starting from ADDR to the end of flash.
@@ -400,22 +465,13 @@ static void sam_ba_monitor_loop(void)
400465
// Even if the starting address is the last byte of a ROW the entire
401466
// ROW is erased anyway.
402467

403-
uint32_t dst_addr = current_number; // starting address
404-
405-
while (dst_addr < MAX_FLASH)
406-
{
407-
// Execute "ER" Erase Row
408-
NVMCTRL->ADDR.reg = dst_addr / 2;
409-
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
410-
while (NVMCTRL->INTFLAG.bit.READY == 0)
411-
;
412-
dst_addr += PAGE_SIZE * 4; // Skip a ROW
413-
}
414-
468+
// BOSSAC.exe always erase with 0x2000 as argument, but an attacker might try to erase just parts of the flash, to be able to copy or analyze the untouched parts.
469+
// To mitigate this, always erase all sketch flash, that is, starting from address 0x2000. This butloader always assume 8 KByte for itself, and sketch starting at 0x2000.
470+
eraseFlash(b_security_enabled ? 0x2000 : current_number);
415471
// Notify command completed
416472
sam_ba_putdata( ptr_monitor_if, "X\n\r", 3);
417473
}
418-
else if (command == 'Y')
474+
else if (command == 'Y') // Write buffer to flash
419475
{
420476
// This command writes the content of a buffer in SRAM into flash memory.
421477

@@ -435,6 +491,13 @@ static void sam_ba_monitor_loop(void)
435491
}
436492
else
437493
{
494+
if (b_security_enabled && erased_from != 0x2000)
495+
{
496+
// To mitigate that an attacker might not use the ordinary BOSSA method of erasing flash before programming,
497+
// always erase flash, if it hasn't been done already.
498+
eraseFlash(0x2000);
499+
}
500+
438501
// Write to flash
439502
uint32_t size = current_number/4;
440503
uint32_t *src_addr = src_buff_addr;
@@ -546,7 +609,7 @@ static void sam_ba_monitor_loop(void)
546609
// Notify command completed
547610
sam_ba_putdata( ptr_monitor_if, "Y\n\r", 3);
548611
}
549-
else if (command == 'Z')
612+
else if (command == 'Z') // Calculate CRC16
550613
{
551614
// This command calculate CRC for a given area of memory.
552615
// It's useful to quickly check if a transfer has been done
@@ -648,6 +711,7 @@ void sam_ba_monitor_run(void)
648711
PAGES = NVMCTRL->PARAM.bit.NVMP;
649712
MAX_FLASH = PAGE_SIZE * PAGES;
650713

714+
b_security_enabled = NVMCTRL->STATUS.bit.SB != 0;
651715
ptr_data = NULL;
652716
command = 'z';
653717
while (1)

0 commit comments

Comments
 (0)