This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Moving or removing the Nordic DFU bootloader seems impossible with approtect set

For various reasons we need to remove the dfu bootloader to use our custom bootloader.  We replace the Nordic bootloader with a temporary one that does the clean-up for us.  On our bench in debug mode this works fine (clear the uicr, clear the mbr, and clear the last words in the softdevice).  HOWEVER, when we try to do this over the air on devices that are with customers and have APPROTECT set, we are never able to clear the uicr, or to clear the first page of the softdevice (the mbr), to remove the address of the bootloader.  If we do it over the air on devices without approtect set, it works fine.  Our procedures, fw etc are identical, the only difference is that in case we set read back protection it just does not work.  Are we doing something wrong?  Is there a protection that gets set in the SoftDevice we are not aware of?

We are seeing this problem on nrf52832 and nrf52840 with the sd332_6.1 and the sd340_7.0 respecitvely.  Both on the pca boards and in our production boards

Parents
  • Hi BasvR, 
    Could you give more information on how you did the task to erase the MBR (?) , and UICR etc ? 

    Are you doing it inside your temporary bootloader code or you doing the erase with a debugger/programmer ? 

    If you are doing it in your code I don't see why APPROTECT would affect the process. APPROTECT only block the access from the external programmer/debugger. When the BPROT protect the flash access from the code in different blocks.

    When you mentioned "we are neer able to clear" , what exact happened ? Did you receive any error code ? 

  • Hi Hung Bui,

    Thanks for your reply.

    How I do it: I program a modified bootloader with nrfutil that does not set any of the BPROT or ACL registers.  Then this new bootloader launches a program that erases the uicr, the last two words of the sd mbr (i.e. copy the page, set the last two words to 0xFFFFFFFF and write the page) and the mbr (at 0xFF000 in the 840).  The bizare thing is that it does not give a hard fault.  I.e. it does perform the erases and then starts normally, but through our communication we can see that in reality nothing got written.  After a reset the (modified) bootloader does not jump to the application anymore.  I think this is because the mbr at 0xff000 got erased and is not the same as the uicr and/or the sd mbr.

    I have also tried to do the erase directly in the bootloader with the same result: fine with approtect not set, fail with approtect set.

    I agree that APPROTECT should have NO effect on the erasing whatsoever, so you can imagine my surprise that when we tried to apply the development code in production devices it did not work.

    I have attached the code below. 


    Thanks

    void TK_FlashHwDepLLErase(uint32_t memAddrStart, uint32_t memAddrStop)
    {
    	// Erases page numbers that contains the memory addresses 
    	// start and stop and every page in between
        uint32_t const page_size = NRF_FICR->CODEPAGESIZE;
    	uint32_t pageStart = memAddrStart / page_size;
    	uint32_t pageStop  = memAddrStop / page_size;
    
    	SEGGER_RTT_printf(0, "Going to erase page from 0x%08lx(0x%08lx) to 0x%08lx(0x%08lx)\n", 
                           pageStart * page_size, memAddrStart, pageStop * page_size, memAddrStop);
        for (uint32_t iPage = pageStart; iPage <= pageStop; ++iPage) {
            nrf_nvmc_page_erase(iPage * page_size);
     	}
    }
    
    uint8_t TK_FlashHwDepLLWriteWords(uint32_t memAddrStart, uint32_t volatile *data, uint32_t lengthInWords)
    {
        nrf_nvmc_write_words(memAddrStart, (uint32_t const *)data, lengthInWords);
    	return 0;
    }
    
    void CNR_RemoveFactoryBootloader()
    {
    	uint32_t checkval = *(uint32_t *)0xff8;
    
    	SEGGER_RTT_printf(0, "Checking: uicr: 0x%08lx && mbr: 0x%08lx\n", NRF_UICR->NRFFW[0], checkval);
    	if (NRF_UICR->NRFFW[0] != CNR_OURBOOTLOADERVALUE || checkval != CNR_OURBOOTLOADERVALUE){
    		SEGGER_RTT_printf(0, "Factory booloader present --> resetting\n");
    		// erase of UICR
    		NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos;
            wait_for_flash_ready();
    
    		NRF_NVMC->ERASEUICR = 1;
            wait_for_flash_ready();
    
    		NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
            wait_for_flash_ready();
    
    		// copy the first page of the flash memory
    		uint32_t volatile *flashDat = (uint32_t *)0x0;
    		uint32_t volatile flashSave[MBR_PAGE_SIZE_IN_WORDS];
    		for (int32_t i=0; i<MBR_PAGE_SIZE_IN_WORDS; i++){
    			flashSave[i] = flashDat[i]; 
    			if (i%32 == 0){
    				CNR_printf("0x%lx: 0x%lx\n", i*sizeof(uint32_t), flashSave[i]);
    			}
    		}
    		// change the bootloader settings and parameters
    		flashSave[MBR_BOOTLOADER_ADDR/sizeof(uint32_t)] = 0xFFFFFFFF;
    		flashSave[MBR_PARAM_PAGE_ADDR/sizeof(uint32_t)] = 0xFFFFFFFF;
    
    		// erase of first flash page (softdevice MBR)
    		TK_FlashHwDepLLErase(0x0, 0xfff);
    
    		// put back the modified flashSave
    		TK_FlashHwDepLLWriteWords((uint32_t)flashDat, flashSave, MBR_PAGE_SIZE_IN_WORDS);
    
    		// also clear the FDS since it has changed
    		TK_FlashHwDepLLErase(0xfd000, 0xfefff);
    
    		// also clear the METADATA (former MBR) since it has changed
    		TK_FlashHwDepLLErase(0xff000, 0xfffff);
    
    	} else {
    		CNR_printf("No factory bootloader present\n");
    	}
    }
    
    int main(void)
    {
        SEGGER_RTT_Init();
        SEGGER_RTT_printf(0, "\n***\nTrueKinetix Cockpit Bootloader\n***\n\n");
    
        NRF_CLOCK->TASKS_HFCLKSTART = 1;    // Start high frequency clock
        while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) {
             // Wait for HFCLK to start.
        }
        NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; // Clear event
        SEGGER_RTT_printf(0, "HFCLK started\n");
    
        CNR_RemoveFactoryBootloader();
        
        // more stuff
        
    }

  • Hi BasvR, 


    Could you explain what you plan to do by erasing the last two words in the MBR ? I guess you want to erase the bootloader address and the PARAM page address ? 

    What exactly you want to achieve ? 
    Could you verify "After a reset the (modified) bootloader does not jump to the application anymore." it's the bootloader couldn't jump to the application or it's the bootloader couldn't run at all ? 
    You can print out some UART log if possible, or blink GPIOs to debug. 

    I would suggest to do a hex dump (nrfjprog --readcode and --readuicr) to check the difference. 


    If you can provide a simple bootloader project here that can reproduce the issue, I can try to test here. 

  • Hi Hung,

    As I wrote in the original question: I need to get rid of the Nordic DFU.  It is very big and buggy, especially the nrfutil part of it.  So I want to erase the bootloader address, so the softdevice jumps directly to the application located after the softdevice.

    Some more info based on a very basic test bootloader I wrote to replace the dfu:  It seems that the nvmc->uicrerase is the culprit. I cannot seem to erase it with approtect set.  It seems I am able to erase the mbr in the softdevice and at 0xff000 as well as the bootloader parameter pages at 0xfe000 (I am using the 52840 hence those locations).

    Now I read this on your forum:

    https://devzone.nordicsemi.com/f/nordic-q-a/61017/how-to-enable-disable-uicr-custom-registers-writes-through-approtect

    this suggests you can erase the uicr if approtect is set, but for some reason I can't.

    HOWEVER, i also read this (VERY SCARY) post that says you cannot erase the uicr if approtect is set !!!

    https://devzone.nordicsemi.com/f/nordic-q-a/46726/erasing-uicr-from-software---do-i-need-to-save-and-copy-non-customer-uicr-registers

    If this is true that means that once you set approtect you are stuck with the dfu and with the dfu in one location.   Because as long as 0x10001014 and 0x10001018 contain anything but 0xffffffff, the sd will try to use that data as the info on the bootloader, as I understand it. 

    Never being able to erase the uicr anymore is also quite problematic for any other info you have stored in uicr and might need to update over time ...

    Really hoping you can help here and that I misunderstand.

  • Hi BasvR,

    I'm sorry I missed the point that when you set APPROTECT the UICR will be protected.

    The reason for that is that if you can change the UICR, you can remove the APPROTECT bit inside the UICR making in a vulnerable to attacking/accidental internal code that can remove APPROTECT. 

    So it's not possible to remove/change the UICR when you set APPROTECT. This UICR page was not made to store dynamic data. It's more of an OTP flash page. In our previous generation of the chip (nRF51) it's not even possible to erase UICR without erasing the whole chip. 

    If you have dynamic data you should store them in an ordinary flash page instead. 

    There still however a solution for your issue, I reviewed the code of the MBR and found that the bootloader address in the MBR page has more priority than the bootloader address in the UICR. The address in UICR is only used if you clear the bootloader address in MBR to 0xFFFFFFFF.

    So, if you can change the start address of the bootloader in MBR_BOOTLOADER_ADDR (0xFF8) location to the new bootloader address, you can overwrite what stored in the UICR. The MBR will then jump to the new bootloader location. It's still not possible to remove the bootloader though but you can make a very small bootloader that does only one task to jump to the application. 

Reply
  • Hi BasvR,

    I'm sorry I missed the point that when you set APPROTECT the UICR will be protected.

    The reason for that is that if you can change the UICR, you can remove the APPROTECT bit inside the UICR making in a vulnerable to attacking/accidental internal code that can remove APPROTECT. 

    So it's not possible to remove/change the UICR when you set APPROTECT. This UICR page was not made to store dynamic data. It's more of an OTP flash page. In our previous generation of the chip (nRF51) it's not even possible to erase UICR without erasing the whole chip. 

    If you have dynamic data you should store them in an ordinary flash page instead. 

    There still however a solution for your issue, I reviewed the code of the MBR and found that the bootloader address in the MBR page has more priority than the bootloader address in the UICR. The address in UICR is only used if you clear the bootloader address in MBR to 0xFFFFFFFF.

    So, if you can change the start address of the bootloader in MBR_BOOTLOADER_ADDR (0xFF8) location to the new bootloader address, you can overwrite what stored in the UICR. The MBR will then jump to the new bootloader location. It's still not possible to remove the bootloader though but you can make a very small bootloader that does only one task to jump to the application. 

Children
  • Hi,

    I/we don't understand why APPROTECT would be made write once and/or coupled to the erasing of other parts of the memory.  I think the two are not related and it should be the programmer's responsibility to deal with security.  However, after reading more yesterday I came to the same conclusion that Nordic has implemented it this way, and it is what it is.

    I would however recommend that you add a very big warning note to your manual about UICR, because the way the UICR is explained now it is portrayed as flash memory and not at all as OTP (except in non-secure settings, such as dev).  The same holds for the DFU: once you put it in, it is impossible to remove or move.  So that should be the first thing you should read if you are considering using it, I would think.

    I have been working along the way you have suggested.  I don't understand your last remark: "The MBR will then jump to the new bootloader location. It's still not possible to remove the bootloader though but you can make a very small bootloader that does only one task to jump to the application."  Could you not just have the MBR jump to the application?  So you would not use a new bootloader at all?  Or will that cause problems with interrupt vectors?

    Thanks,

    Bas

  • Hi Bas, 
    I agree that we may need to have better warning about APPROTECT and UICR .  But if you have a look at the nRF52 specification you can find this: 

    So it's pretty clear there that UICR won't be changed if you set the APPROTECT. 

    It's most likely the reason why we move the start address of the bootloader from UICR to the MBR setting page inside the MBR. 

    The reason I mentioned that "it's not possible to remove the bootloader" is that once you have something written in the bootloader start address in the UICR and if you can't remove that, the MBR will always think that there is a bootloader. Even if you can change the start address of the bootloader to the application start address, the process of booting up would no longer the same as you have no bootloader (bootloader start address = 0xFFFFFFFF in both MBR setting and in UICR ). When there is no bootloader the MBR will start the softdevice first and then softdevice jump to application. If there is a bootloader, the MBR will start the bootloader first and then it's the bootloader who decides to start the softdevice or not. 

    So what you can do is to make a very small bootloader , change the start address of this bootloader to the very top of the flash. I guess saving space in flash is what you want to achieve by removing bootloader ? 

Related