Skip to content

Commit f35f157

Browse files
authored
Merge pull request #586 from ksmith3036/master
Make bootloader honour the MCU Security Bit
2 parents 738af17 + aafa688 commit f35f157

File tree

4 files changed

+117
-45
lines changed

4 files changed

+117
-45
lines changed

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
# -----------------------------------------------------------------------------

bootloaders/zero/bootloader_samd21x18.ld

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
MEMORY
2828
{
2929
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x2000 /* First 8KB used by bootloader */
30-
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000-0x0004 /* 4 bytes used by bootloader to keep data between resets */
30+
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000-0x0400 /* last 4 bytes used by bootloader to keep data between resets, but reserves 1024 bytes for sketches to have same possibility */
3131
}
3232

3333
/* Linker script to place sections and symbol values. Should be used together

bootloaders/zero/sam_ba_monitor.c

+109-43
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,14 @@ 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) {
237+
return;
238+
}
239+
228240
uint32_t app_start_address;
229241

230242
__disable_irq();
@@ -240,8 +252,10 @@ void call_applet(uint32_t address)
240252
/* Jump to application Reset Handler in the application */
241253
asm("bx %0"::"r"(app_start_address));
242254
}
255+
#endif
243256

244257
uint32_t current_number;
258+
uint32_t erased_from = 0;
245259
uint32_t i, length;
246260
uint8_t command, *ptr_data, *ptr, data[SIZEBUFMAX];
247261
uint8_t j;
@@ -264,6 +278,20 @@ static void put_uint32(uint32_t n)
264278
sam_ba_putdata( ptr_monitor_if, buff, 8);
265279
}
266280

281+
static void eraseFlash(uint32_t dst_addr)
282+
{
283+
erased_from = dst_addr;
284+
while (dst_addr < MAX_FLASH)
285+
{
286+
// Execute "ER" Erase Row
287+
NVMCTRL->ADDR.reg = dst_addr / 2;
288+
NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER;
289+
while (NVMCTRL->INTFLAG.bit.READY == 0)
290+
;
291+
dst_addr += PAGE_SIZE * 4; // Skip a ROW
292+
}
293+
}
294+
267295
#ifdef ENABLE_JTAG_LOAD
268296
static uint32_t offset = __UINT32_MAX__;
269297
static bool flashNeeded = false;
@@ -284,7 +312,7 @@ static void sam_ba_monitor_loop(void)
284312
{
285313
sam_ba_putdata(ptr_monitor_if, "\n\r", 2);
286314
}
287-
if (command == 'S')
315+
if (command == 'S') // Write memory (normally RAM, but might be flash, if client handles the Flash MCU commands?)
288316
{
289317
//Check if some data are remaining in the "data" buffer
290318
if(length>i)
@@ -318,37 +346,81 @@ static void sam_ba_monitor_loop(void)
318346

319347
__asm("nop");
320348
}
321-
else if (command == 'R')
349+
else if (command == 'R') // Read memory (flash or RAM)
322350
{
351+
// Flash memory starts at address 0 and runs to flash size 0x40000 (256 KByte)
352+
353+
// 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.
354+
// Bootloader ends at 0x1FFF, so user programs start at 0x2000
355+
// RAM starts at 0x20000000, so redirect FLASH reads into RAM reads, when in secure mode
356+
if (b_security_enabled && ((uint32_t)ptr_data >= 0x0000 && (uint32_t)ptr_data < 0x20000000))
357+
{
358+
ptr_data = (uint8_t *)0x20005000;
359+
}
360+
323361
sam_ba_putdata_xmd(ptr_monitor_if, ptr_data, current_number);
324362
}
325-
else if (command == 'O')
363+
else if (command == 'O') // write byte
326364
{
327365
*ptr_data = (char) current_number;
328366
}
329-
else if (command == 'H')
367+
else if (command == 'H') // Write half word
330368
{
331369
*((uint16_t *) ptr_data) = (uint16_t) current_number;
332370
}
333-
else if (command == 'W')
371+
else if (command == 'W') // Write word
334372
{
335373
*((int *) ptr_data) = current_number;
336374
}
337-
else if (command == 'o')
375+
else if (command == 'o') // Read byte
338376
{
377+
// Flash memory starts at address 0 and runs to flash size 0x40000 (256 KByte). RAM starts at 0x20000000.
378+
// 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.
379+
// BOSSA reads address 0 to check something, but using read word instead of read byte, but in any case allow reading first byte
380+
// Bootloader ends at 0x1FFF, so user programs start at 0x2000
381+
if (b_security_enabled && ((uint32_t)ptr_data > 0x0003 && (uint32_t)ptr_data < 0x20000000))
382+
{
383+
ptr_data = (uint8_t*) &current_number;
384+
}
385+
339386
sam_ba_putdata_term(ptr_data, 1);
340387
}
341-
else if (command == 'h')
388+
else if (command == 'h') // Read half word
342389
{
343-
current_number = *((uint16_t *) ptr_data);
390+
// Flash memory starts at address 0 and runs to flash size 0x40000 (256 KByte). RAM starts at 0x20000000.
391+
// 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.
392+
// BOSSA reads address 0 to check something, but using read word instead of read byte, but in any case allow reading first byte
393+
// Bootloader ends at 0x1FFF, so user programs start at 0x2000
394+
if (b_security_enabled && ((uint32_t)ptr_data > 0x0003 && (uint32_t)ptr_data < 0x20000000))
395+
{
396+
current_number = 0;
397+
}
398+
else
399+
{
400+
current_number = *((uint16_t *) ptr_data);
401+
}
402+
344403
sam_ba_putdata_term((uint8_t*) &current_number, 2);
345404
}
346-
else if (command == 'w')
405+
else if (command == 'w') // Read word
347406
{
348-
current_number = *((uint32_t *) ptr_data);
407+
// Flash memory starts at address 0 and runs to flash size 0x40000 (256 KByte). RAM starts at 0x20000000.
408+
// 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.
409+
// BOSSA reads address 0 to check something, but using read word instead of read byte, but in any case allow reading first byte
410+
// Bootloader ends at 0x1FFF, so user programs start at 0x2000
411+
if (b_security_enabled && ((uint32_t)ptr_data > 0x0003 && (uint32_t)ptr_data < 0x20000000))
412+
{
413+
current_number = 0;
414+
}
415+
else
416+
{
417+
current_number = *((uint32_t *) ptr_data);
418+
}
419+
349420
sam_ba_putdata_term((uint8_t*) &current_number, 4);
350421
}
351-
else if (command == 'G')
422+
#ifndef SECURE_BY_DEFAULT
423+
else if (!b_security_enabled && command == 'G') // Execute code. Will not allow when security is enabled.
352424
{
353425
call_applet(current_number);
354426
/* Rebase the Stack Pointer */
@@ -358,40 +430,30 @@ static void sam_ba_monitor_loop(void)
358430
ptr_monitor_if->put_c(0x6);
359431
}
360432
}
361-
else if (command == 'T')
433+
#endif
434+
else if (command == 'T') // Turn on terminal mode
362435
{
363436
b_terminal_mode = 1;
364437
sam_ba_putdata(ptr_monitor_if, "\n\r", 2);
365438
}
366-
else if (command == 'N')
439+
else if (command == 'N') // Turn off terminal mode
367440
{
368441
if (b_terminal_mode == 0)
369442
{
370443
sam_ba_putdata( ptr_monitor_if, "\n\r", 2);
371444
}
372445
b_terminal_mode = 0;
373446
}
374-
else if (command == 'V')
447+
else if (command == 'V') // Read version information
375448
{
376449
sam_ba_putdata( ptr_monitor_if, "v", 1);
377450
sam_ba_putdata( ptr_monitor_if, (uint8_t *) RomBOOT_Version, strlen(RomBOOT_Version));
378451
sam_ba_putdata( ptr_monitor_if, " ", 1);
379452
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);
453+
ptr = (uint8_t*) &(" " __DATE__ " " __TIME__ "\n\r");
454+
sam_ba_putdata( ptr_monitor_if, ptr, strlen(ptr));
393455
}
394-
else if (command == 'X')
456+
else if (command == 'X') // Erase flash
395457
{
396458
// Syntax: X[ADDR]#
397459
// Erase the flash memory starting from ADDR to the end of flash.
@@ -400,22 +462,13 @@ static void sam_ba_monitor_loop(void)
400462
// Even if the starting address is the last byte of a ROW the entire
401463
// ROW is erased anyway.
402464

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-
465+
// 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.
466+
// 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.
467+
eraseFlash(b_security_enabled ? 0x2000 : current_number);
415468
// Notify command completed
416469
sam_ba_putdata( ptr_monitor_if, "X\n\r", 3);
417470
}
418-
else if (command == 'Y')
471+
else if (command == 'Y') // Write buffer to flash
419472
{
420473
// This command writes the content of a buffer in SRAM into flash memory.
421474

@@ -435,6 +488,13 @@ static void sam_ba_monitor_loop(void)
435488
}
436489
else
437490
{
491+
if (b_security_enabled && erased_from != 0x2000)
492+
{
493+
// To mitigate that an attacker might not use the ordinary BOSSA method of erasing flash before programming,
494+
// always erase flash, if it hasn't been done already.
495+
eraseFlash(0x2000);
496+
}
497+
438498
// Write to flash
439499
uint32_t size = current_number/4;
440500
uint32_t *src_addr = src_buff_addr;
@@ -546,7 +606,7 @@ static void sam_ba_monitor_loop(void)
546606
// Notify command completed
547607
sam_ba_putdata( ptr_monitor_if, "Y\n\r", 3);
548608
}
549-
else if (command == 'Z')
609+
else if (command == 'Z') // Calculate CRC16
550610
{
551611
// This command calculate CRC for a given area of memory.
552612
// It's useful to quickly check if a transfer has been done
@@ -648,6 +708,12 @@ void sam_ba_monitor_run(void)
648708
PAGES = NVMCTRL->PARAM.bit.NVMP;
649709
MAX_FLASH = PAGE_SIZE * PAGES;
650710

711+
#ifdef SECURE_BY_DEFAULT
712+
b_security_enabled = true;
713+
#else
714+
b_security_enabled = NVMCTRL->STATUS.bit.SB != 0;
715+
#endif
716+
651717
ptr_data = NULL;
652718
command = 'z';
653719
while (1)

bootloaders/zero/sam_ba_monitor.h

+3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
/* Selects USB as the communication interface of the monitor */
3737
#define SIZEBUFMAX 64
3838

39+
// Set this flag to let the bootloader enforce read restrictions of flash memory, even if security bit is not set
40+
//#define SECURE_BY_DEFAULT
41+
3942
/**
4043
* \brief Initialize the monitor
4144
*

0 commit comments

Comments
 (0)