• AVR Freaks

Calling code from different compiler versions

Author
GoEk
Senior Member
  • Total Posts : 120
  • Reward points : 0
  • Joined: 2012/09/27 00:32:28
  • Location: Sweden
  • Status: offline
2018/11/01 10:23:05 (permalink)
0

Calling code from different compiler versions

Hi,
I have a Project with a code self-updater function that uses an external SPI FLASH device as intermediate storage. The self-updater code resides in a special section of the PIC flash which is later protected, i.e. not updated, when the main code is updated. This means that the updater code can have been compiled by an older XC16 version than the current one.
 
Are there any immediate problems with this method that I haven't thought of? The self-updater only uses stack variables to prevent memory map collisions and is a do-or-die function, i.e. if it fails the target is dead, if it works the target resets after update.
#1

9 Replies Related Threads

    NKurzman
    A Guy on the Net
    • Total Posts : 18061
    • Reward points : 0
    • Joined: 2008/01/16 19:33:48
    • Location: 0
    • Status: offline
    Re: Calling code from different compiler versions 2018/11/01 10:28:44 (permalink)
    0
    If the self updated is a bootloader built as a seperate project it will work. If it is a function in a project then yes you have ignored a lot.
    The startup code.
    The c common libraries.
    The compilers libraries.
    And yes the fact that a newer compiler may interface differently.
    #2
    GoEk
    Senior Member
    • Total Posts : 120
    • Reward points : 0
    • Joined: 2012/09/27 00:32:28
    • Location: Sweden
    • Status: offline
    Re: Calling code from different compiler versions 2018/11/01 10:41:57 (permalink)
    0
    NKurzman
    If the self updated is a bootloader built as a seperate project it will work.

    No.
     
    NKurzman
     The startup code.

    The updater is coded as a "self contained function", i.e. it only accesses the SPI port, on-stack variables and uses the code FLASH read/write registers, all accesses outside the function should be to known chip addresses.

    NKurzmanThe c common libraries.

    Don't uses them or any other call outside this protected section.

    NKurzmanThe compilers libraries.

    What libaries are you refering to?
     
    NKurzmanAnd yes the fact that a newer compiler may interface differently.

    That is what I am currently suspecting but the self-updater code function has no arguments so I assumed that "a call is a call", regardless of compiler version.
     
    #3
    GoEk
    Senior Member
    • Total Posts : 120
    • Reward points : 0
    • Joined: 2012/09/27 00:32:28
    • Location: Sweden
    • Status: offline
    Re: Calling code from different compiler versions 2018/11/01 10:47:30 (permalink)
    0
    This is the updater code:
    //*******************************************************************
    void __attribute__((__section__("MainSoftwareUpdater"))) DoFLASHTransfer(uint8_t *TXData, uint16_t TXCount, uint8_t *RXData, int16_t RXCount)
    {
    uint16_t TransferCount = 0;

    while((TXCount > 0) || (TransferCount > 0) || (RXCount > 0))
        {
        if (TransferCount < SPI1_FIFO_FILL_LIMIT)
            {
            if (TXCount > 0)
                {
                TXCount--;
                
                SPI1BUF = *TXData++;
                TransferCount++;
                }
            else
                {
                if (RXCount > 0)
                    {
                    RXCount--;
                    TransferCount++;
                    SPI1BUF = 0;
                    }
                }
            }

        if (SPI1STATbits.SRXMPT == false)
            {
            if (RXData)
                *RXData++ = SPI1BUF;
            else
                SPI1BUF;

            TransferCount--;
            }
        }
    }

    //*******************************************************************
    void __attribute__((__section__("MainSoftwareUpdater"))) UpdateMainSoftware(void)
    {
    uint8_t ReadCommand[4] = { EXTFLASH_READ_DATA_BYTES_COMMAND, 0, 0, 0 };
    uint32_t ReadBuffer[_FLASH_PAGE * 2];
    uint32_t *SourceFLASHReadBuffer;
    uint16_t MasterPageCounter;
    uint16_t RowCounter, InstructionCounter;
    uint16_t StatusIndicatorCounterInWords, Offset;
    uint32_t PageAddress, VerifyDataHold;

    lcdSelectPage(6);
    lcdSelectColumnHighNibble(0);
    lcdSelectColumnLowNibble(0);

    // Disable all interrupts
    SET_CPU_IPL(7);

    // Prepare FLASH for reading
    LATBbits.LATB11 = 0;
    DoFLASHTransfer((uint8_t *)ReadCommand, sizeof(ReadCommand), 0, 0);

    // PICFLASHPROGRAMSPACESIZE is the total number of Words (16 bit) of PIC FLASH memory
    // _FLASH_PAGE is the number of Instructions per FLASH memory page, i.e. size for each erase operation
    // _FLASH_ROW is the maximum number of Instructions per programming operation
    // An "Instruction" is 24 bits or two Words including one "Phantom" byte

    MasterPageCounter = 0;
    StatusIndicatorCounterInWords = 0;
    PageAddress = 0;

    while(MasterPageCounter < ((PICFLASHPROGRAMSPACESIZE / 2) / _FLASH_PAGE))
        {
        // Read next page data
        DoFLASHTransfer(0, 0, (uint8_t *)ReadBuffer, _FLASH_PAGE * 4);
            
        // Handle each page but skip our own page
        // NOTE! Page number for our own page here must match linker script!
        if (MasterPageCounter != 1)
            {
            // Erase current page
            TBLPAG = PageAddress >> 16;
            __builtin_tblwtl(PageAddress, 0x0000);
            NVMCON = 0x4042;
            __builtin_write_NVM();
            
            // Wait for erase to complete
            while(NVMCONbits.WR);
            
            // Set Phantom byte to 0 and check if we need to program
            bool DoProgram = false;
            for (InstructionCounter = 0; InstructionCounter < _FLASH_PAGE; InstructionCounter++)
                {
                if (ReadBuffer[InstructionCounter] != 0xFFFFFFFF)
                    DoProgram = true;
                
                // The Phantom Byte in the PIC FLASH reads as 0, set our source Phantom byte to 0
                ReadBuffer[InstructionCounter] &= 0x00FFFFFF;
                }
            
            if (DoProgram)
                {
                // Program full page
                Offset = PageAddress;
                SourceFLASHReadBuffer = ReadBuffer;
                
                for (RowCounter = 0; RowCounter < (_FLASH_PAGE / _FLASH_ROW); RowCounter++)
                    {
                    // Program current row
                    NVMCON = 0x4001;
                    
                    for (InstructionCounter = 0; InstructionCounter < _FLASH_ROW; InstructionCounter++)
                        {
                        __builtin_tblwtl(Offset, *SourceFLASHReadBuffer);
                        __builtin_tblwth(Offset, *SourceFLASHReadBuffer >> 16);
                        
                        SourceFLASHReadBuffer++;
                        Offset += 2;
                        }

                    __builtin_write_NVM();

                    // Wait for program to complete
                    while(NVMCONbits.WR);
                    }
                
                // Verify full page
                SourceFLASHReadBuffer = ReadBuffer;
                Offset = PageAddress;
                
                for (RowCounter = 0; RowCounter < (_FLASH_PAGE / _FLASH_ROW); RowCounter++)
                    {
                    for (InstructionCounter = 0; InstructionCounter < _FLASH_ROW; InstructionCounter++)
                        {
                        VerifyDataHold = ((uint32_t)__builtin_tblrdh(Offset)) << 16;
                        VerifyDataHold += __builtin_tblrdl(Offset);
                        
                        if (*SourceFLASHReadBuffer != VerifyDataHold)
                            break;

                        SourceFLASHReadBuffer++;
                        Offset += 2;
                        }

                    if (InstructionCounter < _FLASH_ROW)
                        break; // Verify error!
                    }

                if (RowCounter < (_FLASH_PAGE / _FLASH_ROW))
                    {
                    // FLASH write error, fatal! Flash display backlight forever.
                    while(true)
                        {
                        DISP_BACKLIGHT_Toggle();
                        PageAddress = 1000000;
                        while(PageAddress--);
                        }
                    }
                }
            }
        
        MasterPageCounter++;
        PageAddress += (_FLASH_PAGE * 2);
        
        // Update status display
        StatusIndicatorCounterInWords += (_FLASH_PAGE * 2);
        
        while(StatusIndicatorCounterInWords >= (PICFLASHPROGRAMSPACESIZE / DISPLAY_PIXELSPERLINE))
            {
            lcdDataByte(0);
            lcdDataByte(0);
            StatusIndicatorCounterInWords -= (PICFLASHPROGRAMSPACESIZE / DISPLAY_PIXELSPERLINE);
            }
        }

    asm ("NOP");
    asm ("NOP");
    asm ("NOP");
    asm ("RESET");
    }

     
     
    #4
    NKurzman
    A Guy on the Net
    • Total Posts : 18061
    • Reward points : 0
    • Joined: 2008/01/16 19:33:48
    • Location: 0
    • Status: offline
    Re: Calling code from different compiler versions 2018/11/01 11:42:08 (permalink)
    0
    The compilers libraries.
    What libraries are you referring to?
    The Compiler may have internal functions for commonly code.  Just like all programmers have.
    Your plan has only one chance to work. if it fails you are bricked.
    I hope you __section__ is on a Flash Erase Page Boundary and ends on a Flash Erase Page Boundary. 
    #5
    GoEk
    Senior Member
    • Total Posts : 120
    • Reward points : 0
    • Joined: 2012/09/27 00:32:28
    • Location: Sweden
    • Status: offline
    Re: Calling code from different compiler versions 2018/11/01 12:11:06 (permalink)
    0
    NKurzman
    I hope you __section__ is on a Flash Erase Page Boundary and ends on a Flash Erase Page Boundary. 


    Yes it is.
    #6
    du00000001
    Just Some Member
    • Total Posts : 3248
    • Reward points : 0
    • Joined: 2016/05/03 13:52:42
    • Location: Germany
    • Status: online
    Re: Calling code from different compiler versions 2018/11/01 13:28:03 (permalink)
    0
    What a vulnerable way to implement somerhing that is essentially a bootloader.
     
    You'll have to assert regularly that all code required for the updater is within the protected section. Beyond that, special care has to be taken that the function's address won't change.
     
    But yes - basically it should work this way.

    PEBKAC / EBKAC / POBCAK / PICNIC (eventually see en.wikipedia.org)
    #7
    malaugh
    Super Member
    • Total Posts : 400
    • Reward points : 0
    • Joined: 2011/03/31 14:04:42
    • Location: San Diego
    • Status: offline
    Re: Calling code from different compiler versions 2018/11/01 13:33:06 (permalink)
    0
    This is exactly what we do on our products.  We make two entirely separate programs, and combine the hex files for each program using hexmate.  We have linker files for each program so each resides in separate non-overlapping memory.  We also locate the start of the bootloader at a know address so we can jump to it from the main program.  Also, we do not use interrupts in the bootloader, so the main program is free to change the vector table.  We do have a test to make sure the reset vector is not overwritten.
     
    We also use he same technique as you, storing the main program image in SPI flash, so the bootloader just needs to move the code form flash to program memory, so it can be a very simple program,
     
     
    #8
    GoEk
    Senior Member
    • Total Posts : 120
    • Reward points : 0
    • Joined: 2012/09/27 00:32:28
    • Location: Sweden
    • Status: offline
    Re: Calling code from different compiler versions 2018/11/01 13:47:01 (permalink)
    0
    du00000001What a vulnerable way to implement somerhing that is essentially a bootloader.

    It is very code space efficient.
     
    du00000001
    ... Beyond that, special care has to be taken that the function's address won't change….



    Thanks for the gotcha! That is most likely the problem, i.e. if the loader code is compiled with 1.24 it will optimize the same way every time but when I compile with 1.35 the optimization is different and the function address is different. I will have to reverse the order of the DoFLASHTransfer and UpdateMainSoftware functions so that UpdateMainSoftware is always first in the protected section, i.e. always a known address.
    post edited by GoEk - 2018/11/01 13:53:08
    #9
    du00000001
    Just Some Member
    • Total Posts : 3248
    • Reward points : 0
    • Joined: 2016/05/03 13:52:42
    • Location: Germany
    • Status: online
    Re: Calling code from different compiler versions 2018/11/01 13:58:58 (permalink)
    0
    Re: function address
    A somewhat less vulnerable approach is to have some "table" at the very start of the block - including the address of the entry function.
    This way you are somewhat less dependent on code generation - at the "cost" of entering via the table entry instead of a simple function call.

    PEBKAC / EBKAC / POBCAK / PICNIC (eventually see en.wikipedia.org)
    #10
    Jump to:
    © 2019 APG vNext Commercial Version 4.5