diff --git a/bootloaders/zero/Makefile b/bootloaders/zero/Makefile index 65243c52b..61fca6eb5 100644 --- a/bootloaders/zero/Makefile +++ b/bootloaders/zero/Makefile @@ -71,11 +71,14 @@ else CFLAGS+=-Os -DDEBUG=0 -flto endif +ifdef SECURE_BY_DEFAULT + CFLAGS+=-DSECURE_BY_DEFAULT=1 +endif + ELF=$(NAME).elf BIN=$(NAME).bin HEX=$(NAME).hex - INCLUDES=-I"$(MODULE_PATH)/tools/CMSIS/4.5.0/CMSIS/Include/" -I"$(MODULE_PATH)/tools/CMSIS-Atmel/1.2.0/CMSIS/Device/ATMEL/" # ----------------------------------------------------------------------------- diff --git a/bootloaders/zero/bootloader_samd21x18.ld b/bootloaders/zero/bootloader_samd21x18.ld index 2a8b056d3..91d308d47 100644 --- a/bootloaders/zero/bootloader_samd21x18.ld +++ b/bootloaders/zero/bootloader_samd21x18.ld @@ -27,7 +27,7 @@ MEMORY { FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x2000 /* First 8KB used by bootloader */ - RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000-0x0004 /* 4 bytes used by bootloader to keep data between resets */ + 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 */ } /* Linker script to place sections and symbol values. Should be used together diff --git a/bootloaders/zero/sam_ba_monitor.c b/bootloaders/zero/sam_ba_monitor.c index 4d1620c1b..619d1334b 100644 --- a/bootloaders/zero/sam_ba_monitor.c +++ b/bootloaders/zero/sam_ba_monitor.c @@ -30,7 +30,8 @@ #include const char RomBOOT_Version[] = SAM_BA_VERSION; -const char RomBOOT_ExtendedCapabilities[] = "[Arduino:XYZ]"; +// X = Chip Erase, Y = Write Buffer, Z = Checksum Buffer, P = Secure Bit Aware +const char RomBOOT_ExtendedCapabilities[] = "[Arduino:XYZP]"; /* Provides one common interface to handle both USART and USB-CDC */ typedef struct @@ -83,6 +84,12 @@ const t_monitor_if usbcdc_if = /* The pointer to the interface object use by the monitor */ t_monitor_if * ptr_monitor_if; +#ifdef SECURE_BY_DEFAULT +bool b_security_enabled = true; +#else +bool b_security_enabled = false; +#endif + /* b_terminal_mode mode (ascii) or hex mode */ volatile bool b_terminal_mode = false; volatile bool b_sam_ba_interface_usart = false; @@ -222,9 +229,14 @@ void sam_ba_putdata_term(uint8_t* data, uint32_t length) return; } +#ifndef SECURE_BY_DEFAULT volatile uint32_t sp; void call_applet(uint32_t address) { + if (b_security_enabled) { + return; + } + uint32_t app_start_address; __disable_irq(); @@ -240,8 +252,10 @@ void call_applet(uint32_t address) /* Jump to application Reset Handler in the application */ asm("bx %0"::"r"(app_start_address)); } +#endif uint32_t current_number; +uint32_t erased_from = 0; uint32_t i, length; uint8_t command, *ptr_data, *ptr, data[SIZEBUFMAX]; uint8_t j; @@ -264,6 +278,20 @@ static void put_uint32(uint32_t n) sam_ba_putdata( ptr_monitor_if, buff, 8); } +static void eraseFlash(uint32_t dst_addr) +{ + erased_from = dst_addr; + while (dst_addr < MAX_FLASH) + { + // Execute "ER" Erase Row + NVMCTRL->ADDR.reg = dst_addr / 2; + NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER; + while (NVMCTRL->INTFLAG.bit.READY == 0) + ; + dst_addr += PAGE_SIZE * 4; // Skip a ROW + } +} + #ifdef ENABLE_JTAG_LOAD static uint32_t offset = __UINT32_MAX__; static bool flashNeeded = false; @@ -284,7 +312,7 @@ static void sam_ba_monitor_loop(void) { sam_ba_putdata(ptr_monitor_if, "\n\r", 2); } - if (command == 'S') + if (command == 'S') // Write memory (normally RAM, but might be flash, if client handles the Flash MCU commands?) { //Check if some data are remaining in the "data" buffer if(length>i) @@ -318,37 +346,81 @@ static void sam_ba_monitor_loop(void) __asm("nop"); } - else if (command == 'R') + else if (command == 'R') // Read memory (flash or RAM) { + // Flash memory starts at address 0 and runs to flash size 0x40000 (256 KByte) + + // 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. + // Bootloader ends at 0x1FFF, so user programs start at 0x2000 + // RAM starts at 0x20000000, so redirect FLASH reads into RAM reads, when in secure mode + if (b_security_enabled && ((uint32_t)ptr_data >= 0x0000 && (uint32_t)ptr_data < 0x20000000)) + { + ptr_data = (uint8_t *)0x20005000; + } + sam_ba_putdata_xmd(ptr_monitor_if, ptr_data, current_number); } - else if (command == 'O') + else if (command == 'O') // write byte { *ptr_data = (char) current_number; } - else if (command == 'H') + else if (command == 'H') // Write half word { *((uint16_t *) ptr_data) = (uint16_t) current_number; } - else if (command == 'W') + else if (command == 'W') // Write word { *((int *) ptr_data) = current_number; } - else if (command == 'o') + else if (command == 'o') // Read byte { + // Flash memory starts at address 0 and runs to flash size 0x40000 (256 KByte). RAM starts at 0x20000000. + // 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. + // BOSSA reads address 0 to check something, but using read word instead of read byte, but in any case allow reading first byte + // Bootloader ends at 0x1FFF, so user programs start at 0x2000 + if (b_security_enabled && ((uint32_t)ptr_data > 0x0003 && (uint32_t)ptr_data < 0x20000000)) + { + ptr_data = (uint8_t*) ¤t_number; + } + sam_ba_putdata_term(ptr_data, 1); } - else if (command == 'h') + else if (command == 'h') // Read half word { - current_number = *((uint16_t *) ptr_data); + // Flash memory starts at address 0 and runs to flash size 0x40000 (256 KByte). RAM starts at 0x20000000. + // 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. + // BOSSA reads address 0 to check something, but using read word instead of read byte, but in any case allow reading first byte + // Bootloader ends at 0x1FFF, so user programs start at 0x2000 + if (b_security_enabled && ((uint32_t)ptr_data > 0x0003 && (uint32_t)ptr_data < 0x20000000)) + { + current_number = 0; + } + else + { + current_number = *((uint16_t *) ptr_data); + } + sam_ba_putdata_term((uint8_t*) ¤t_number, 2); } - else if (command == 'w') + else if (command == 'w') // Read word { - current_number = *((uint32_t *) ptr_data); + // Flash memory starts at address 0 and runs to flash size 0x40000 (256 KByte). RAM starts at 0x20000000. + // 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. + // BOSSA reads address 0 to check something, but using read word instead of read byte, but in any case allow reading first byte + // Bootloader ends at 0x1FFF, so user programs start at 0x2000 + if (b_security_enabled && ((uint32_t)ptr_data > 0x0003 && (uint32_t)ptr_data < 0x20000000)) + { + current_number = 0; + } + else + { + current_number = *((uint32_t *) ptr_data); + } + sam_ba_putdata_term((uint8_t*) ¤t_number, 4); } - else if (command == 'G') +#ifndef SECURE_BY_DEFAULT + else if (!b_security_enabled && command == 'G') // Execute code. Will not allow when security is enabled. { call_applet(current_number); /* Rebase the Stack Pointer */ @@ -358,12 +430,13 @@ static void sam_ba_monitor_loop(void) ptr_monitor_if->put_c(0x6); } } - else if (command == 'T') +#endif + else if (command == 'T') // Turn on terminal mode { b_terminal_mode = 1; sam_ba_putdata(ptr_monitor_if, "\n\r", 2); } - else if (command == 'N') + else if (command == 'N') // Turn off terminal mode { if (b_terminal_mode == 0) { @@ -371,27 +444,16 @@ static void sam_ba_monitor_loop(void) } b_terminal_mode = 0; } - else if (command == 'V') + else if (command == 'V') // Read version information { sam_ba_putdata( ptr_monitor_if, "v", 1); sam_ba_putdata( ptr_monitor_if, (uint8_t *) RomBOOT_Version, strlen(RomBOOT_Version)); sam_ba_putdata( ptr_monitor_if, " ", 1); sam_ba_putdata( ptr_monitor_if, (uint8_t *) RomBOOT_ExtendedCapabilities, strlen(RomBOOT_ExtendedCapabilities)); - sam_ba_putdata( ptr_monitor_if, " ", 1); - ptr = (uint8_t*) &(__DATE__); - i = 0; - while (*ptr++ != '\0') - i++; - sam_ba_putdata( ptr_monitor_if, (uint8_t *) &(__DATE__), i); - sam_ba_putdata( ptr_monitor_if, " ", 1); - i = 0; - ptr = (uint8_t*) &(__TIME__); - while (*ptr++ != '\0') - i++; - sam_ba_putdata( ptr_monitor_if, (uint8_t *) &(__TIME__), i); - sam_ba_putdata( ptr_monitor_if, "\n\r", 2); + ptr = (uint8_t*) &(" " __DATE__ " " __TIME__ "\n\r"); + sam_ba_putdata( ptr_monitor_if, ptr, strlen(ptr)); } - else if (command == 'X') + else if (command == 'X') // Erase flash { // Syntax: X[ADDR]# // Erase the flash memory starting from ADDR to the end of flash. @@ -400,22 +462,13 @@ static void sam_ba_monitor_loop(void) // Even if the starting address is the last byte of a ROW the entire // ROW is erased anyway. - uint32_t dst_addr = current_number; // starting address - - while (dst_addr < MAX_FLASH) - { - // Execute "ER" Erase Row - NVMCTRL->ADDR.reg = dst_addr / 2; - NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER; - while (NVMCTRL->INTFLAG.bit.READY == 0) - ; - dst_addr += PAGE_SIZE * 4; // Skip a ROW - } - + // 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. + // 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. + eraseFlash(b_security_enabled ? 0x2000 : current_number); // Notify command completed sam_ba_putdata( ptr_monitor_if, "X\n\r", 3); } - else if (command == 'Y') + else if (command == 'Y') // Write buffer to flash { // This command writes the content of a buffer in SRAM into flash memory. @@ -435,6 +488,13 @@ static void sam_ba_monitor_loop(void) } else { + if (b_security_enabled && erased_from != 0x2000) + { + // To mitigate that an attacker might not use the ordinary BOSSA method of erasing flash before programming, + // always erase flash, if it hasn't been done already. + eraseFlash(0x2000); + } + // Write to flash uint32_t size = current_number/4; uint32_t *src_addr = src_buff_addr; @@ -546,7 +606,7 @@ static void sam_ba_monitor_loop(void) // Notify command completed sam_ba_putdata( ptr_monitor_if, "Y\n\r", 3); } - else if (command == 'Z') + else if (command == 'Z') // Calculate CRC16 { // This command calculate CRC for a given area of memory. // It's useful to quickly check if a transfer has been done @@ -648,6 +708,12 @@ void sam_ba_monitor_run(void) PAGES = NVMCTRL->PARAM.bit.NVMP; MAX_FLASH = PAGE_SIZE * PAGES; +#ifdef SECURE_BY_DEFAULT + b_security_enabled = true; +#else + b_security_enabled = NVMCTRL->STATUS.bit.SB != 0; +#endif + ptr_data = NULL; command = 'z'; while (1) diff --git a/bootloaders/zero/sam_ba_monitor.h b/bootloaders/zero/sam_ba_monitor.h index 9ba72343c..122b22a6c 100644 --- a/bootloaders/zero/sam_ba_monitor.h +++ b/bootloaders/zero/sam_ba_monitor.h @@ -36,6 +36,9 @@ /* Selects USB as the communication interface of the monitor */ #define SIZEBUFMAX 64 +// Set this flag to let the bootloader enforce read restrictions of flash memory, even if security bit is not set +//#define SECURE_BY_DEFAULT + /** * \brief Initialize the monitor *