Skip to content

Commit 88d02e8

Browse files
committed
The bootloaders checks 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. Protection may also be turned on by setting the SECURE_BY_DEFAULT compile flag. To allow the MKR VIDOR 4000 loader to build to less than 8 KByte, the string handling of compile time DATE and TIME had to be concatinated in source code.
1 parent 56e13aa commit 88d02e8

File tree

3 files changed

+101
-46
lines changed

3 files changed

+101
-46
lines changed

Diff for: bootloaders/zero/Makefile

+4-1
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,14 @@ else
7171
CFLAGS+=-Os -DDEBUG=0 -flto
7272
endif
7373

74+
ifdef SECURE_BY_DEFAULT
75+
CFLAGS+=-DSECURE_BY_DEFAULT=1
76+
endif
77+
7478
ELF=$(NAME).elf
7579
BIN=$(NAME).bin
7680
HEX=$(NAME).hex
7781

78-
7982
INCLUDES=-I"$(MODULE_PATH)/tools/CMSIS/4.5.0/CMSIS/Include/" -I"$(MODULE_PATH)/tools/CMSIS-Atmel/1.2.0/CMSIS/Device/ATMEL/"
8083

8184
# -----------------------------------------------------------------------------

Diff for: bootloaders/zero/board_definitions.h

+3
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,6 @@
4848

4949
#define BOOT_PIN_MASK (1U << (BOOT_LOAD_PIN & 0x1f))
5050

51+
// Set this flag to let the bootloader enforce read restrictions of flash memory, even if security bit is not set
52+
//#define SECURE_BY_DEFAULT
53+

Diff for: bootloaders/zero/sam_ba_monitor.c

+94-45
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,12 @@ 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+
#ifdef SECURE_BY_DEFAULT
88+
bool b_security_enabled = true;
89+
#else
90+
bool b_security_enabled = false;
91+
#endif
92+
8693
/* b_terminal_mode mode (ascii) or hex mode */
8794
volatile bool b_terminal_mode = false;
8895
volatile bool b_sam_ba_interface_usart = false;
@@ -222,9 +229,12 @@ void sam_ba_putdata_term(uint8_t* data, uint32_t length)
222229
return;
223230
}
224231

232+
#ifndef SECURE_BY_DEFAULT
225233
volatile uint32_t sp;
226234
void call_applet(uint32_t address)
227235
{
236+
if (b_security_enabled) return;
237+
228238
uint32_t app_start_address;
229239

230240
__disable_irq();
@@ -240,8 +250,10 @@ void call_applet(uint32_t address)
240250
/* Jump to application Reset Handler in the application */
241251
asm("bx %0"::"r"(app_start_address));
242252
}
253+
#endif
243254

244255
uint32_t current_number;
256+
uint32_t erased_from = 0;
245257
uint32_t i, length;
246258
uint8_t command, *ptr_data, *ptr, data[SIZEBUFMAX];
247259
uint8_t j;
@@ -264,11 +276,26 @@ static void put_uint32(uint32_t n)
264276
sam_ba_putdata( ptr_monitor_if, buff, 8);
265277
}
266278

279+
static void eraseFlash(uint32_t dst_addr)
280+
{
281+
erased_from = dst_addr;
282+
while (dst_addr < MAX_FLASH)
283+
{
284+
// Execute "ER" Erase Row
285+
NVMCTRL->ADDR.reg = dst_addr / 2;
286+
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
287+
while (NVMCTRL->INTFLAG.bit.READY == 0)
288+
;
289+
dst_addr += PAGE_SIZE * 4; // Skip a ROW
290+
}
291+
}
292+
267293
#ifdef ENABLE_JTAG_LOAD
268294
static uint32_t offset = __UINT32_MAX__;
269295
static bool flashNeeded = false;
270296
#endif
271297

298+
272299
static void sam_ba_monitor_loop(void)
273300
{
274301
length = sam_ba_getdata(ptr_monitor_if, data, SIZEBUFMAX);
@@ -284,7 +311,7 @@ static void sam_ba_monitor_loop(void)
284311
{
285312
sam_ba_putdata(ptr_monitor_if, "\n\r", 2);
286313
}
287-
if (command == 'S')
314+
if (command == 'S') // Write memory (normally RAM, but might be flash, if client handles the Flash MCU commands?)
288315
{
289316
//Check if some data are remaining in the "data" buffer
290317
if(length>i)
@@ -318,37 +345,65 @@ static void sam_ba_monitor_loop(void)
318345

319346
__asm("nop");
320347
}
321-
else if (command == 'R')
348+
else if (command == 'R') // Read memory (flash or RAM)
322349
{
323-
sam_ba_putdata_xmd(ptr_monitor_if, ptr_data, current_number);
350+
// Flash memory starts at address 0 and runs to flash size 0x40000 (256 KByte)
351+
352+
// 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.
353+
// Bootloader ends at 0x1FFF, so user programs start at 0x2000
354+
// RAM starts at 0x20000000, so redirect FLASH reads into RAM reads, when in secure mode
355+
if (b_security_enabled && ((uint32_t)ptr_data >= 0x0000 && (uint32_t)ptr_data < 0x20000000))
356+
ptr_data = (uint8_t *)0x20005000;
357+
sam_ba_putdata_xmd(ptr_monitor_if, ptr_data, current_number);
324358
}
325-
else if (command == 'O')
359+
else if (command == 'O') // write byte
326360
{
327361
*ptr_data = (char) current_number;
328362
}
329-
else if (command == 'H')
363+
else if (command == 'H') // Write half word
330364
{
331365
*((uint16_t *) ptr_data) = (uint16_t) current_number;
332366
}
333-
else if (command == 'W')
367+
else if (command == 'W') // Write word
334368
{
335369
*((int *) ptr_data) = current_number;
336370
}
337-
else if (command == 'o')
371+
else if (command == 'o') // Read byte
338372
{
339-
sam_ba_putdata_term(ptr_data, 1);
373+
// Flash memory starts at address 0 and runs to flash size 0x40000 (256 KByte). RAM starts at 0x20000000.
374+
// 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.
375+
// BOSSA reads address 0 to check something, but using read word instead of read byte, but in any case allow reading first byte
376+
// Bootloader ends at 0x1FFF, so user programs start at 0x2000
377+
if (b_security_enabled && ((uint32_t)ptr_data > 0x0003 && (uint32_t)ptr_data < 0x20000000))
378+
ptr_data = (uint8_t*) &current_number;
379+
sam_ba_putdata_term(ptr_data, 1);
340380
}
341-
else if (command == 'h')
381+
else if (command == 'h') // Read half word
342382
{
343-
current_number = *((uint16_t *) ptr_data);
383+
// Flash memory starts at address 0 and runs to flash size 0x40000 (256 KByte). RAM starts at 0x20000000.
384+
// 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.
385+
// BOSSA reads address 0 to check something, but using read word instead of read byte, but in any case allow reading first byte
386+
// Bootloader ends at 0x1FFF, so user programs start at 0x2000
387+
if (b_security_enabled && ((uint32_t)ptr_data > 0x0003 && (uint32_t)ptr_data < 0x20000000))
388+
current_number = 0;
389+
else
390+
current_number = *((uint16_t *) ptr_data);
344391
sam_ba_putdata_term((uint8_t*) &current_number, 2);
345392
}
346-
else if (command == 'w')
393+
else if (command == 'w') // Read word
347394
{
348-
current_number = *((uint32_t *) ptr_data);
395+
// Flash memory starts at address 0 and runs to flash size 0x40000 (256 KByte). RAM starts at 0x20000000.
396+
// 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.
397+
// BOSSA reads address 0 to check something, but using read word instead of read byte, but in any case allow reading first byte
398+
// Bootloader ends at 0x1FFF, so user programs start at 0x2000
399+
if (b_security_enabled && ((uint32_t)ptr_data > 0x0003 && (uint32_t)ptr_data < 0x20000000))
400+
current_number = 0;
401+
else
402+
current_number = *((uint32_t *) ptr_data);
349403
sam_ba_putdata_term((uint8_t*) &current_number, 4);
350404
}
351-
else if (command == 'G')
405+
#ifndef SECURE_BY_DEFAULT
406+
else if (!b_security_enabled && command == 'G') // Execute code. Will not allow when security is enabled.
352407
{
353408
call_applet(current_number);
354409
/* Rebase the Stack Pointer */
@@ -358,40 +413,30 @@ static void sam_ba_monitor_loop(void)
358413
ptr_monitor_if->put_c(0x6);
359414
}
360415
}
361-
else if (command == 'T')
416+
#endif
417+
else if (command == 'T') // Turn on terminal mode
362418
{
363419
b_terminal_mode = 1;
364420
sam_ba_putdata(ptr_monitor_if, "\n\r", 2);
365421
}
366-
else if (command == 'N')
422+
else if (command == 'N') // Turn off terminal mode
367423
{
368424
if (b_terminal_mode == 0)
369425
{
370426
sam_ba_putdata( ptr_monitor_if, "\n\r", 2);
371427
}
372428
b_terminal_mode = 0;
373429
}
374-
else if (command == 'V')
430+
else if (command == 'V') // Read version information
375431
{
376432
sam_ba_putdata( ptr_monitor_if, "v", 1);
377433
sam_ba_putdata( ptr_monitor_if, (uint8_t *) RomBOOT_Version, strlen(RomBOOT_Version));
378434
sam_ba_putdata( ptr_monitor_if, " ", 1);
379435
sam_ba_putdata( ptr_monitor_if, (uint8_t *) RomBOOT_ExtendedCapabilities, strlen(RomBOOT_ExtendedCapabilities));
380-
sam_ba_putdata( ptr_monitor_if, " ", 1);
381-
ptr = (uint8_t*) &(__DATE__);
382-
i = 0;
383-
while (*ptr++ != '\0')
384-
i++;
385-
sam_ba_putdata( ptr_monitor_if, (uint8_t *) &(__DATE__), i);
386-
sam_ba_putdata( ptr_monitor_if, " ", 1);
387-
i = 0;
388-
ptr = (uint8_t*) &(__TIME__);
389-
while (*ptr++ != '\0')
390-
i++;
391-
sam_ba_putdata( ptr_monitor_if, (uint8_t *) &(__TIME__), i);
392-
sam_ba_putdata( ptr_monitor_if, "\n\r", 2);
436+
ptr = (uint8_t*) &(" " __DATE__ " " __TIME__ "\n\r");
437+
sam_ba_putdata( ptr_monitor_if, ptr, strlen(ptr));
393438
}
394-
else if (command == 'X')
439+
else if (command == 'X') // Erase flash
395440
{
396441
// Syntax: X[ADDR]#
397442
// Erase the flash memory starting from ADDR to the end of flash.
@@ -400,22 +445,13 @@ static void sam_ba_monitor_loop(void)
400445
// Even if the starting address is the last byte of a ROW the entire
401446
// ROW is erased anyway.
402447

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-
448+
// 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.
449+
// 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.
450+
eraseFlash(b_security_enabled ? 0x2000 : current_number);
415451
// Notify command completed
416452
sam_ba_putdata( ptr_monitor_if, "X\n\r", 3);
417453
}
418-
else if (command == 'Y')
454+
else if (command == 'Y') // Write buffer to flash
419455
{
420456
// This command writes the content of a buffer in SRAM into flash memory.
421457

@@ -435,6 +471,13 @@ static void sam_ba_monitor_loop(void)
435471
}
436472
else
437473
{
474+
if (b_security_enabled) {
475+
// To mitigate that an attacker might not use the ordinary BOSSA method of erasing flash before programming,
476+
// always erase flash, if it hasn't been done already.
477+
if (erased_from != 0x2000)
478+
eraseFlash(0x2000);
479+
}
480+
438481
// Write to flash
439482
uint32_t size = current_number/4;
440483
uint32_t *src_addr = src_buff_addr;
@@ -546,7 +589,7 @@ static void sam_ba_monitor_loop(void)
546589
// Notify command completed
547590
sam_ba_putdata( ptr_monitor_if, "Y\n\r", 3);
548591
}
549-
else if (command == 'Z')
592+
else if (command == 'Z') // Calculate CRC16
550593
{
551594
// This command calculate CRC for a given area of memory.
552595
// It's useful to quickly check if a transfer has been done
@@ -648,6 +691,12 @@ void sam_ba_monitor_run(void)
648691
PAGES = NVMCTRL->PARAM.bit.NVMP;
649692
MAX_FLASH = PAGE_SIZE * PAGES;
650693

694+
#ifdef SECURE_BY_DEFAULT
695+
b_security_enabled = true;
696+
#else
697+
b_security_enabled = NVMCTRL->STATUS.bit.SB != 0;
698+
#endif
699+
651700
ptr_data = NULL;
652701
command = 'z';
653702
while (1)

0 commit comments

Comments
 (0)