]> cvs.zerfleddert.de Git - proxmark3-svn/blobdiff - bootrom/bootrom.c
Make the bootrom correctly nack in case of flash write error (e.g. due to lock bits)
[proxmark3-svn] / bootrom / bootrom.c
index 3dba041e67757d79396b5cd1f595448dc9fe622c..83794d9f8c65fb1163f683bf99b24f7c104afc3c 100644 (file)
@@ -1,58 +1,66 @@
 #include <proxmark3.h>\r
 \r
+struct common_area common_area __attribute__((section(".commonarea")));\r
+unsigned int start_addr, end_addr, bootrom_unlocked;\r
+extern char _bootrom_start, _bootrom_end, _flash_start, _flash_end;\r
+\r
 static void ConfigClocks(void)\r
 {\r
     // we are using a 16 MHz crystal as the basis for everything\r
     // slow clock runs at 32Khz typical regardless of crystal\r
 \r
     // enable system clock and USB clock\r
-    PMC_SYS_CLK_ENABLE = PMC_SYS_CLK_PROCESSOR_CLK | PMC_SYS_CLK_UDP_CLK;\r
+    AT91C_BASE_PMC->PMC_SCER = AT91C_PMC_PCK | AT91C_PMC_UDP;\r
 \r
        // enable the clock to the following peripherals\r
-    PMC_PERIPHERAL_CLK_ENABLE =\r
-        (1<<PERIPH_PIOA) |\r
-        (1<<PERIPH_ADC) |\r
-        (1<<PERIPH_SPI) |\r
-        (1<<PERIPH_SSC) |\r
-        (1<<PERIPH_PWMC) |\r
-        (1<<PERIPH_UDP);\r
+    AT91C_BASE_PMC->PMC_PCER =\r
+               (1<<AT91C_ID_PIOA)      |\r
+               (1<<AT91C_ID_ADC)       |\r
+               (1<<AT91C_ID_SPI)       |\r
+               (1<<AT91C_ID_SSC)       |\r
+               (1<<AT91C_ID_PWMC)      |\r
+               (1<<AT91C_ID_UDP);\r
 \r
        // worst case scenario, with 16Mhz xtal startup delay is 14.5ms\r
        // with a slow clock running at it worst case (max) frequency of 42khz\r
        // max startup delay = (14.5ms*42k)/8 = 76 = 0x4C round up to 0x50\r
 \r
        // enable main oscillator and set startup delay\r
-    PMC_MAIN_OSCILLATOR = PMC_MAIN_OSCILLATOR_ENABLE |\r
-        PMC_MAIN_OSCILLATOR_STARTUP_DELAY(0x50);\r
+    AT91C_BASE_PMC->PMC_MOR =\r
+       PMC_MAIN_OSC_ENABLE |\r
+        PMC_MAIN_OSC_STARTUP_DELAY(0x50);\r
 \r
        // wait for main oscillator to stabilize\r
-       while ( !(PMC_INTERRUPT_STATUS & PMC_MAIN_OSCILLATOR_STABILIZED) )\r
+       while ( !(AT91C_BASE_PMC->PMC_SR & PMC_MAIN_OSC_STABILIZED) )\r
                ;\r
 \r
     // minimum PLL clock frequency is 80 MHz in range 00 (96 here so okay)\r
     // frequency is crystal * multiplier / divisor = 16Mhz * 12 / 2 = 96Mhz\r
-    PMC_PLL = PMC_PLL_DIVISOR(2) | PMC_PLL_COUNT_BEFORE_LOCK(0x50) |\r
-        PMC_PLL_FREQUENCY_RANGE(0) | PMC_PLL_MULTIPLIER(12) |\r
-        PMC_PLL_USB_DIVISOR(1);\r
+    AT91C_BASE_PMC->PMC_PLLR =\r
+       PMC_PLL_DIVISOR(2) |\r
+               PMC_PLL_COUNT_BEFORE_LOCK(0x50) |\r
+               PMC_PLL_FREQUENCY_RANGE(0) |\r
+               PMC_PLL_MULTIPLIER(12) |\r
+               PMC_PLL_USB_DIVISOR(1);\r
 \r
        // wait for PLL to lock\r
-       while ( !(PMC_INTERRUPT_STATUS & PMC_MAIN_OSCILLATOR_PLL_LOCK) )\r
+       while ( !(AT91C_BASE_PMC->PMC_SR & PMC_MAIN_OSC_PLL_LOCK) )\r
                ;\r
 \r
        // we want a master clock (MCK) to be PLL clock / 2 = 96Mhz / 2 = 48Mhz\r
        // as per datasheet, this register must be programmed in two operations\r
        // when changing to PLL, program the prescaler first then the source\r
-    PMC_MASTER_CLK = PMC_CLK_PRESCALE_DIV_2;\r
+    AT91C_BASE_PMC->PMC_MCKR = PMC_CLK_PRESCALE_DIV_2;\r
 \r
        // wait for main clock ready signal\r
-       while ( !(PMC_INTERRUPT_STATUS & PMC_MAIN_OSCILLATOR_MCK_READY) )\r
+       while ( !(AT91C_BASE_PMC->PMC_SR & PMC_MAIN_OSC_MCK_READY) )\r
                ;\r
 \r
        // set the source to PLL\r
-    PMC_MASTER_CLK = PMC_CLK_SELECTION_PLL_CLOCK | PMC_CLK_PRESCALE_DIV_2;\r
+    AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_CSS_PLL_CLK | PMC_CLK_PRESCALE_DIV_2;\r
 \r
        // wait for main clock ready signal\r
-       while ( !(PMC_INTERRUPT_STATUS & PMC_MAIN_OSCILLATOR_MCK_READY) )\r
+       while ( !(AT91C_BASE_PMC->PMC_SR & PMC_MAIN_OSC_MCK_READY) )\r
                ;\r
 }\r
 \r
@@ -63,7 +71,7 @@ static void Fatal(void)
 \r
 void UsbPacketReceived(BYTE *packet, int len)\r
 {\r
-    int i;\r
+    int i, dont_ack=0;\r
     UsbCommand *c = (UsbCommand *)packet;\r
     volatile DWORD *p;\r
 \r
@@ -73,29 +81,84 @@ void UsbPacketReceived(BYTE *packet, int len)
 \r
     switch(c->cmd) {\r
         case CMD_DEVICE_INFO:\r
+            dont_ack = 1;\r
+            c->cmd = CMD_DEVICE_INFO;\r
+            c->arg[0] = DEVICE_INFO_FLAG_BOOTROM_PRESENT | DEVICE_INFO_FLAG_CURRENT_MODE_BOOTROM |\r
+                DEVICE_INFO_FLAG_UNDERSTANDS_START_FLASH;\r
+            if(common_area.flags.osimage_present) c->arg[0] |= DEVICE_INFO_FLAG_OSIMAGE_PRESENT;\r
+            UsbSendPacket(packet, len);\r
             break;\r
 \r
         case CMD_SETUP_WRITE:\r
-            p = (volatile DWORD *)0;\r
+            /* The temporary write buffer of the embedded flash controller is mapped to the\r
+             * whole memory region, only the last 8 bits are decoded.\r
+             */\r
+            p = (volatile DWORD *)&_flash_start;\r
             for(i = 0; i < 12; i++) {\r
-                p[i+c->ext1] = c->d.asDwords[i];\r
+                p[i+c->arg[0]] = c->d.asDwords[i];\r
             }\r
             break;\r
 \r
         case CMD_FINISH_WRITE:\r
-            p = (volatile DWORD *)0;\r
+            p = (volatile DWORD *)&_flash_start;\r
             for(i = 0; i < 4; i++) {\r
                 p[i+60] = c->d.asDwords[i];\r
             }\r
 \r
-            MC_FLASH_COMMAND = MC_FLASH_COMMAND_KEY |\r
-                MC_FLASH_COMMAND_PAGEN(c->ext1/FLASH_PAGE_SIZE_BYTES) |\r
-                FCMD_WRITE_PAGE;\r
-            while(!(MC_FLASH_STATUS & MC_FLASH_STATUS_READY))\r
+            /* Check that the address that we are supposed to write to is within our allowed region */\r
+            if( ((c->arg[0]+AT91C_IFLASH_PAGE_SIZE-1) >= end_addr) || (c->arg[0] < start_addr) ) {\r
+                /* Disallow write */\r
+                dont_ack = 1;\r
+                c->cmd = CMD_NACK;\r
+                UsbSendPacket(packet, len);\r
+            } else {\r
+                /* Translate address to flash page and do flash, update here for the 512k part */\r
+                AT91C_BASE_EFC0->EFC_FCR = MC_FLASH_COMMAND_KEY |\r
+                    MC_FLASH_COMMAND_PAGEN((c->arg[0]-(int)&_flash_start)/AT91C_IFLASH_PAGE_SIZE) |\r
+                    AT91C_MC_FCMD_START_PROG;\r
+            }\r
+            \r
+            uint32_t sr;\r
+            \r
+            while(!((sr = AT91C_BASE_EFC0->EFC_FSR) & MC_FLASH_STATUS_READY))\r
                 ;\r
+            if(sr & (MC_FLASH_STATUS_LOCKE | MC_FLASH_STATUS_PROGE)) { \r
+                   dont_ack = 1;\r
+                    c->cmd = CMD_NACK;\r
+                    UsbSendPacket(packet, len);\r
+            }\r
             break;\r
 \r
         case CMD_HARDWARE_RESET:\r
+            USB_D_PLUS_PULLUP_OFF();\r
+            AT91C_BASE_RSTC->RSTC_RCR = RST_CONTROL_KEY | AT91C_RSTC_PROCRST;\r
+            break;\r
+\r
+        case CMD_START_FLASH:\r
+            if(c->arg[2] == START_FLASH_MAGIC) bootrom_unlocked = 1;\r
+            else bootrom_unlocked = 0;\r
+            {\r
+                int prot_start = (int)&_bootrom_start;\r
+                int prot_end = (int)&_bootrom_end;\r
+                int allow_start = (int)&_flash_start;\r
+                int allow_end = (int)&_flash_end;\r
+                int cmd_start = c->arg[0];\r
+                int cmd_end = c->arg[1];\r
+\r
+                /* Only allow command if the bootrom is unlocked, or the parameters are outside of the protected\r
+                 * bootrom area. In any case they must be within the flash area.\r
+                 */\r
+                if( (bootrom_unlocked || ((cmd_start >= prot_end) || (cmd_end < prot_start)))\r
+                    && (cmd_start >= allow_start) && (cmd_end <= allow_end) ) {\r
+                    start_addr = cmd_start;\r
+                    end_addr = cmd_end;\r
+                } else {\r
+                    start_addr = end_addr = 0;\r
+                    dont_ack = 1;\r
+                    c->cmd = CMD_NACK;\r
+                    UsbSendPacket(packet, len);\r
+                }\r
+            }\r
             break;\r
 \r
         default:\r
@@ -103,10 +166,39 @@ void UsbPacketReceived(BYTE *packet, int len)
             break;\r
     }\r
 \r
-    c->cmd = CMD_ACK;\r
-    UsbSendPacket(packet, len);\r
+    if(!dont_ack) {\r
+        c->cmd = CMD_ACK;\r
+        UsbSendPacket(packet, len);\r
+    }\r
+}\r
+\r
+static void flash_mode(int externally_entered)\r
+{\r
+       start_addr = 0;\r
+       end_addr = 0;\r
+       bootrom_unlocked = 0;\r
+\r
+       UsbStart();\r
+       for(;;) {\r
+               WDT_HIT();\r
+\r
+               UsbPoll(TRUE);\r
+\r
+               if(!externally_entered && !BUTTON_PRESS()) {\r
+                       /* Perform a reset to leave flash mode */\r
+                       USB_D_PLUS_PULLUP_OFF();\r
+                       LED_B_ON();\r
+                       AT91C_BASE_RSTC->RSTC_RCR = RST_CONTROL_KEY | AT91C_RSTC_PROCRST;\r
+                       for(;;);\r
+               }\r
+               if(externally_entered && BUTTON_PRESS()) {\r
+                       /* Let the user's button press override the automatic leave */\r
+                       externally_entered = 0;\r
+               }\r
+       }\r
 }\r
 \r
+extern char _osimage_entry;\r
 void BootROM(void)\r
 {\r
     //------------\r
@@ -115,37 +207,40 @@ void BootROM(void)
 \r
     // Kill all the pullups, especially the one on USB D+; leave them for\r
     // the unused pins, though.\r
-    PIO_NO_PULL_UP_ENABLE =     (1 << GPIO_USB_PU)          |\r
-                                (1 << GPIO_LED_A)           |\r
-                                (1 << GPIO_LED_B)           |\r
-                                (1 << GPIO_LED_C)           |\r
-                                (1 << GPIO_LED_D)           |\r
-                                (1 << GPIO_FPGA_DIN)        |\r
-                                (1 << GPIO_FPGA_DOUT)       |\r
-                                (1 << GPIO_FPGA_CCLK)       |\r
-                                (1 << GPIO_FPGA_NINIT)      |\r
-                                (1 << GPIO_FPGA_NPROGRAM)   |\r
-                                (1 << GPIO_FPGA_DONE)       |\r
-                                (1 << GPIO_MUXSEL_HIPKD)    |\r
-                                (1 << GPIO_MUXSEL_HIRAW)    |\r
-                                (1 << GPIO_MUXSEL_LOPKD)    |\r
-                                (1 << GPIO_MUXSEL_LORAW)    |\r
-                                (1 << GPIO_RELAY)              |\r
-                                (1 << GPIO_NVDD_ON);\r
-                                // (and add GPIO_FPGA_ON)\r
+    AT91C_BASE_PIOA->PIO_PPUDR =\r
+       GPIO_USB_PU                     |\r
+               GPIO_LED_A                      |\r
+               GPIO_LED_B                      |\r
+               GPIO_LED_C                      |\r
+               GPIO_LED_D                      |\r
+               GPIO_FPGA_DIN           |\r
+               GPIO_FPGA_DOUT          |\r
+               GPIO_FPGA_CCLK          |\r
+               GPIO_FPGA_NINIT         |\r
+               GPIO_FPGA_NPROGRAM      |\r
+               GPIO_FPGA_DONE          |\r
+               GPIO_MUXSEL_HIPKD       |\r
+               GPIO_MUXSEL_HIRAW       |\r
+               GPIO_MUXSEL_LOPKD       |\r
+               GPIO_MUXSEL_LORAW       |\r
+               GPIO_RELAY                      |\r
+               GPIO_NVDD_ON;\r
+               // (and add GPIO_FPGA_ON)\r
        // These pins are outputs\r
-    PIO_OUTPUT_ENABLE =         (1 << GPIO_LED_A)           |\r
-                                (1 << GPIO_LED_B)           |\r
-                                (1 << GPIO_LED_C)           |\r
-                                (1 << GPIO_LED_D)           |\r
-                                (1 << GPIO_RELAY)              |\r
-                                (1 << GPIO_NVDD_ON);\r
+    AT91C_BASE_PIOA->PIO_OER =\r
+       GPIO_LED_A                      |\r
+               GPIO_LED_B                      |\r
+               GPIO_LED_C                      |\r
+               GPIO_LED_D                      |\r
+               GPIO_RELAY                      |\r
+               GPIO_NVDD_ON;\r
        // PIO controls the following pins\r
-    PIO_ENABLE =                (1 << GPIO_USB_PU)          |\r
-                                (1 << GPIO_LED_A)           |\r
-                                (1 << GPIO_LED_B)           |\r
-                                (1 << GPIO_LED_C)           |\r
-                                (1 << GPIO_LED_D);\r
+    AT91C_BASE_PIOA->PIO_PER =\r
+       GPIO_USB_PU                     |\r
+               GPIO_LED_A                      |\r
+               GPIO_LED_B                      |\r
+               GPIO_LED_C                      |\r
+               GPIO_LED_D;\r
 \r
     USB_D_PLUS_PULLUP_OFF();\r
     LED_D_OFF();\r
@@ -153,38 +248,57 @@ void BootROM(void)
     LED_B_OFF();\r
     LED_A_OFF();\r
 \r
-       // if 512K FLASH part - TODO make some defines :)\r
-       if ((DBGU_CIDR | 0xf00) == 0xa00) {\r
-               MC_FLASH_MODE0 = MC_FLASH_MODE_FLASH_WAIT_STATES(1) |\r
+    // if 512K FLASH part - TODO make some defines :)\r
+    if ((AT91C_BASE_DBGU->DBGU_CIDR | 0xf00) == 0xa00) {\r
+           AT91C_BASE_EFC0->EFC_FMR =\r
+               MC_FLASH_MODE_FLASH_WAIT_STATES(1) |\r
                        MC_FLASH_MODE_MASTER_CLK_IN_MHZ(0x48);\r
-               MC_FLASH_MODE1 = MC_FLASH_MODE_FLASH_WAIT_STATES(1) |\r
+           AT91C_BASE_EFC1->EFC_FMR =\r
+               MC_FLASH_MODE_FLASH_WAIT_STATES(1) |\r
                        MC_FLASH_MODE_MASTER_CLK_IN_MHZ(0x48);\r
-       } else {\r
-               MC_FLASH_MODE0 = MC_FLASH_MODE_FLASH_WAIT_STATES(0) |\r
+    } else {\r
+           AT91C_BASE_EFC0->EFC_FMR =\r
+               MC_FLASH_MODE_FLASH_WAIT_STATES(0) |\r
                        MC_FLASH_MODE_MASTER_CLK_IN_MHZ(48);\r
-       }\r
+    }\r
 \r
-       // Initialize all system clocks\r
+    // Initialize all system clocks\r
     ConfigClocks();\r
 \r
     LED_A_ON();\r
 \r
-       if(BUTTON_PRESS()) {\r
-       UsbStart();\r
-       }\r
-\r
-    for(;;) {\r
-        WDT_HIT();\r
-\r
-        UsbPoll(TRUE);\r
+    int common_area_present = 0;\r
+    switch(AT91C_BASE_RSTC->RSTC_RSR & AT91C_RSTC_RSTTYP) {\r
+    case AT91C_RSTC_RSTTYP_WATCHDOG:\r
+    case AT91C_RSTC_RSTTYP_SOFTWARE:\r
+    case AT91C_RSTC_RSTTYP_USER:\r
+           /* In these cases the common_area in RAM should be ok, retain it if it's there */\r
+           if(common_area.magic == COMMON_AREA_MAGIC && common_area.version == 1) {\r
+                   common_area_present = 1;\r
+           }\r
+           break;\r
+    default: /* Otherwise, initialize it from scratch */\r
+           break;\r
+    }\r
 \r
-               if(!BUTTON_PRESS()) {\r
-            USB_D_PLUS_PULLUP_OFF();\r
-            LED_B_ON();\r
+    if(!common_area_present){\r
+           /* Common area not ok, initialize it */\r
+           int i; for(i=0; i<sizeof(common_area); i++) { /* Makeshift memset, no need to drag util.c into this */\r
+                   ((char*)&common_area)[i] = 0;\r
+           }\r
+           common_area.magic = COMMON_AREA_MAGIC;\r
+           common_area.version = 1;\r
+           common_area.flags.bootrom_present = 1;\r
+    }\r
 \r
-                       // jump to Flash address 0x10000 (LSBit set for thumb mode, 0x100000 added for Flash base address)\r
-            asm("ldr r3, = 0x00110001\n");\r
-            asm("bx r3\n");\r
-        }\r
+    common_area.flags.bootrom_present = 1;\r
+    if(common_area.command == COMMON_AREA_COMMAND_ENTER_FLASH_MODE) {\r
+           common_area.command = COMMON_AREA_COMMAND_NONE;\r
+           flash_mode(1);\r
+    } else if(BUTTON_PRESS()) {\r
+           flash_mode(0);\r
+    } else {\r
+           // jump to Flash address of the osimage entry point (LSBit set for thumb mode)\r
+           asm("bx %0\n" : : "r" ( ((int)&_osimage_entry) | 0x1 ) );\r
     }\r
 }\r
Impressum, Datenschutz