diff --git a/build/targets.device.bootloader.mak b/build/targets.device.bootloader.mak index d3644ca0f..3101a49ea 100644 --- a/build/targets.device.bootloader.mak +++ b/build/targets.device.bootloader.mak @@ -34,7 +34,7 @@ HANDY_TARGETS += epsilon.A epsilon.B .PHONY: %.two_binaries %.two_binaries: %.elf @echo "Building an external binary for $<" - $(Q) $(OBJCOPY) -O binary $< $(basename $<).external.bin + $(Q) $(OBJCOPY) -O binary -R .slot_info $< $(basename $<).external.bin @echo "Padding $(basename $<).external.bin" $(Q) printf "\xFF\xFF\xFF\xFF" >> $(basename $<).external.bin diff --git a/ion/include/ion.h b/ion/include/ion.h index 854c3c539..ce44f84a3 100644 --- a/ion/include/ion.h +++ b/ion/include/ion.h @@ -39,6 +39,7 @@ const char * omegaVersion(); const char * patchLevel(); const char * fccId(); const char * pcbVersion(); +void updateSlotInfo(); // CRC32 : non xor-ed, non reversed, direct, polynomial 4C11DB7 uint32_t crc32Word(const uint32_t * data, size_t length); // Only accepts whole 32bit values diff --git a/ion/src/device/bootloader/bootloader.A.ld b/ion/src/device/bootloader/bootloader.A.ld index df8ad59f8..3e1188476 100644 --- a/ion/src/device/bootloader/bootloader.A.ld +++ b/ion/src/device/bootloader/bootloader.A.ld @@ -23,122 +23,6 @@ MEMORY { STACK_SIZE = 32K; FIRST_FLASH_SECTOR_SIZE = 4K; SIGNED_PAYLOAD_LENGTH = 8; +USERLAND_OFFSET = 64K; -SECTIONS { - .signed_payload_prefix ORIGIN(FLASH) : { - FILL(0xFF); - BYTE(0xFF) - . = ORIGIN(FLASH) + SIGNED_PAYLOAD_LENGTH; - } >FLASH - - .header : { - KEEP(*(.header)) - } >FLASH - - .isr_vector_table : AT(ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.header)) { - /* When booting, the STM32F412 fetches the content of address 0x0, and - * extracts from it various key infos: the initial value of the PC register - * (program counter), the initial value of the stack pointer, and various - * entry points to interrupt service routines. This data is called the ISR - * vector table. - * - * Note that address 0x0 is always an alias. It points to the beginning of - * Flash, SRAM, or integrated bootloader depending on the boot mode chosen. - * (This mode is chosen by setting the BOOTn pins on the chip). - * - * We're generating the ISR vector table in code because it's very - * convenient: using function pointers, we can easily point to the service - * routine for each interrupt. */ - _isr_vector_table_start_flash = LOADADDR(.isr_vector_table); - _isr_vector_table_start_ram = .; - KEEP(*(.isr_vector_table)) - _isr_vector_table_end_ram = .; - } >SRAM - - .exam_mode_buffer ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.header) + SIZEOF(.isr_vector_table) : { - . = ALIGN(4K); - _exam_mode_buffer_start = .; - KEEP(*(.exam_mode_buffer)) - /* Note: We don't increment "." here, we set it. */ - . = . + FIRST_FLASH_SECTOR_SIZE; - _exam_mode_buffer_end = .; - } >FLASH - - /* External flash memory */ - .text ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.header) + SIZEOF(.isr_vector_table) + SIZEOF(.exam_mode_buffer) : { - . = ALIGN(4); - *(.text) - *(.text.*) - } >FLASH - - .rodata : { - *(.rodata) - *(.rodata.*) - } >FLASH - - .init_array : { - . = ALIGN(4); - _init_array_start = .; - KEEP (*(.init_array*)) - _init_array_end = .; - } >FLASH - - .data : { - /* The data section is written to Flash but linked as if it were in RAM. - * - * This is required because its initial value matters (so it has to be in - * persistant memory in the first place), but it is a R/W area of memory - * so it will have to live in RAM upon execution (in linker lingo, that - * translates to the data section having a LMA in Flash and a VMA in RAM). - * - * This means we'll have to copy it from Flash to RAM on initialization. - * To do this, we'll need to know the source location of the data section - * (in Flash), the target location (in RAM), and the size of the section. - * That's why we're defining three symbols that we'll use in the initial- - * -ization routine. */ - . = ALIGN(4); - _data_section_start_flash = LOADADDR(.data); - _data_section_start_ram = .; - *(.data) - *(.data.*) - _data_section_end_ram = .; - } >SRAM AT> FLASH - - .bss : { - /* The bss section contains data for all uninitialized variables - * So like the .data section, it will go in RAM, but unlike the data section - * we don't care at all about an initial value. - * - * Before execution, crt0 will erase that section of memory though, so we'll - * need pointers to the beginning and end of this section. */ - . = ALIGN(4); - _bss_section_start_ram = .; - *(.bss) - *(.bss.*) - /* The compiler may choose to allocate uninitialized global variables as - * COMMON blocks. This can be disabled with -fno-common if needed. */ - *(COMMON) - _bss_section_end_ram = .; - } >SRAM - - .heap : { - _heap_start = .; - /* Note: We don't increment "." here, we set it. */ - . = (ORIGIN(SRAM) + LENGTH(SRAM) - STACK_SIZE); - _heap_end = .; - } >SRAM - - .stack : { - . = ALIGN(8); - _stack_end = .; - . += (STACK_SIZE - 8); - . = ALIGN(8); - _stack_start = .; - } >SRAM - - /DISCARD/ : { - /* exidx and extab are needed for unwinding, which we don't use */ - *(.ARM.exidx*) - *(.ARM.extab*) - } -} +INCLUDE ion/src/device/bootloader/bootloader_common.ld; diff --git a/ion/src/device/bootloader/bootloader.B.ld b/ion/src/device/bootloader/bootloader.B.ld index 25cbd2b22..f89ebca2f 100644 --- a/ion/src/device/bootloader/bootloader.B.ld +++ b/ion/src/device/bootloader/bootloader.B.ld @@ -23,122 +23,6 @@ MEMORY { STACK_SIZE = 32K; FIRST_FLASH_SECTOR_SIZE = 4K; SIGNED_PAYLOAD_LENGTH = 8; +USERLAND_OFFSET = 64K; -SECTIONS { - .signed_payload_prefix ORIGIN(FLASH) : { - FILL(0xFF); - BYTE(0xFF) - . = ORIGIN(FLASH) + SIGNED_PAYLOAD_LENGTH; - } >FLASH - - .header : { - KEEP(*(.header)) - } >FLASH - - .isr_vector_table : AT(ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.header)) { - /* When booting, the STM32F412 fetches the content of address 0x0, and - * extracts from it various key infos: the initial value of the PC register - * (program counter), the initial value of the stack pointer, and various - * entry points to interrupt service routines. This data is called the ISR - * vector table. - * - * Note that address 0x0 is always an alias. It points to the beginning of - * Flash, SRAM, or integrated bootloader depending on the boot mode chosen. - * (This mode is chosen by setting the BOOTn pins on the chip). - * - * We're generating the ISR vector table in code because it's very - * convenient: using function pointers, we can easily point to the service - * routine for each interrupt. */ - _isr_vector_table_start_flash = LOADADDR(.isr_vector_table); - _isr_vector_table_start_ram = .; - KEEP(*(.isr_vector_table)) - _isr_vector_table_end_ram = .; - } >SRAM - - .exam_mode_buffer ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.header) + SIZEOF(.isr_vector_table) : { - . = ALIGN(4K); - _exam_mode_buffer_start = .; - KEEP(*(.exam_mode_buffer)) - /* Note: We don't increment "." here, we set it. */ - . = . + FIRST_FLASH_SECTOR_SIZE; - _exam_mode_buffer_end = .; - } >FLASH - - /* External flash memory */ - .text ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.header) + SIZEOF(.isr_vector_table) + SIZEOF(.exam_mode_buffer) : { - . = ALIGN(4); - *(.text) - *(.text.*) - } >FLASH - - .rodata : { - *(.rodata) - *(.rodata.*) - } >FLASH - - .init_array : { - . = ALIGN(4); - _init_array_start = .; - KEEP (*(.init_array*)) - _init_array_end = .; - } >FLASH - - .data : { - /* The data section is written to Flash but linked as if it were in RAM. - * - * This is required because its initial value matters (so it has to be in - * persistant memory in the first place), but it is a R/W area of memory - * so it will have to live in RAM upon execution (in linker lingo, that - * translates to the data section having a LMA in Flash and a VMA in RAM). - * - * This means we'll have to copy it from Flash to RAM on initialization. - * To do this, we'll need to know the source location of the data section - * (in Flash), the target location (in RAM), and the size of the section. - * That's why we're defining three symbols that we'll use in the initial- - * -ization routine. */ - . = ALIGN(4); - _data_section_start_flash = LOADADDR(.data); - _data_section_start_ram = .; - *(.data) - *(.data.*) - _data_section_end_ram = .; - } >SRAM AT> FLASH - - .bss : { - /* The bss section contains data for all uninitialized variables - * So like the .data section, it will go in RAM, but unlike the data section - * we don't care at all about an initial value. - * - * Before execution, crt0 will erase that section of memory though, so we'll - * need pointers to the beginning and end of this section. */ - . = ALIGN(4); - _bss_section_start_ram = .; - *(.bss) - *(.bss.*) - /* The compiler may choose to allocate uninitialized global variables as - * COMMON blocks. This can be disabled with -fno-common if needed. */ - *(COMMON) - _bss_section_end_ram = .; - } >SRAM - - .heap : { - _heap_start = .; - /* Note: We don't increment "." here, we set it. */ - . = (ORIGIN(SRAM) + LENGTH(SRAM) - STACK_SIZE); - _heap_end = .; - } >SRAM - - .stack : { - . = ALIGN(8); - _stack_end = .; - . += (STACK_SIZE - 8); - . = ALIGN(8); - _stack_start = .; - } >SRAM - - /DISCARD/ : { - /* exidx and extab are needed for unwinding, which we don't use */ - *(.ARM.exidx*) - *(.ARM.extab*) - } -} +INCLUDE ion/src/device/bootloader/bootloader_common.ld; diff --git a/ion/src/device/bootloader/bootloader_common.ld b/ion/src/device/bootloader/bootloader_common.ld new file mode 100644 index 000000000..e8f0c9bbc --- /dev/null +++ b/ion/src/device/bootloader/bootloader_common.ld @@ -0,0 +1,128 @@ + +SECTIONS { + .signed_payload_prefix ORIGIN(FLASH) : { + FILL(0xFF); + BYTE(0xFF) + . = ORIGIN(FLASH) + SIGNED_PAYLOAD_LENGTH; + } >FLASH + + .kernel_header : { + KEEP(*(.kernel_header)) + } >FLASH + + .slot_info : { + *(.slot_info*) + } >SRAM + + .isr_vector_table ORIGIN(SRAM) + 512 : AT(ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.kernel_header)) { + /* When booting, the STM32F412 fetches the content of address 0x0, and + * extracts from it various key infos: the initial value of the PC register + * (program counter), the initial value of the stack pointer, and various + * entry points to interrupt service routines. This data is called the ISR + * vector table. + * + * Note that address 0x0 is always an alias. It points to the beginning of + * Flash, SRAM, or integrated bootloader depending on the boot mode chosen. + * (This mode is chosen by setting the BOOTn pins on the chip). + * + * We're generating the ISR vector table in code because it's very + * convenient: using function pointers, we can easily point to the service + * routine for each interrupt. */ + _isr_vector_table_start_flash = LOADADDR(.isr_vector_table); + _isr_vector_table_start_ram = .; + KEEP(*(.isr_vector_table)) + _isr_vector_table_end_ram = .; + } >SRAM + + .exam_mode_buffer ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.kernel_header) + SIZEOF(.isr_vector_table) : { + . = ALIGN(4K); + _exam_mode_buffer_start = .; + KEEP(*(.exam_mode_buffer)) + /* Note: We don't increment "." here, we set it. */ + . = . + FIRST_FLASH_SECTOR_SIZE; + _exam_mode_buffer_end = .; + } >FLASH + + /* External flash memory */ + .userland_header : { + . = ORIGIN(FLASH) + USERLAND_OFFSET; + KEEP(*(.userland_header)); + } > FLASH + + .text : { + . = ALIGN(4); + *(.text) + *(.text.*) + } >FLASH + + .rodata : { + *(.rodata) + *(.rodata.*) + } >FLASH + + .init_array : { + . = ALIGN(4); + _init_array_start = .; + KEEP (*(.init_array*)) + _init_array_end = .; + } >FLASH + + .data : { + /* The data section is written to Flash but linked as if it were in RAM. + * + * This is required because its initial value matters (so it has to be in + * persistant memory in the first place), but it is a R/W area of memory + * so it will have to live in RAM upon execution (in linker lingo, that + * translates to the data section having a LMA in Flash and a VMA in RAM). + * + * This means we'll have to copy it from Flash to RAM on initialization. + * To do this, we'll need to know the source location of the data section + * (in Flash), the target location (in RAM), and the size of the section. + * That's why we're defining three symbols that we'll use in the initial- + * -ization routine. */ + . = ALIGN(4); + _data_section_start_flash = LOADADDR(.data); + _data_section_start_ram = .; + *(.data) + *(.data.*) + _data_section_end_ram = .; + } >SRAM AT> FLASH + + .bss : { + /* The bss section contains data for all uninitialized variables + * So like the .data section, it will go in RAM, but unlike the data section + * we don't care at all about an initial value. + * + * Before execution, crt0 will erase that section of memory though, so we'll + * need pointers to the beginning and end of this section. */ + . = ALIGN(4); + _bss_section_start_ram = .; + *(.bss) + *(.bss.*) + /* The compiler may choose to allocate uninitialized global variables as + * COMMON blocks. This can be disabled with -fno-common if needed. */ + *(COMMON) + _bss_section_end_ram = .; + } >SRAM + + .heap : { + _heap_start = .; + /* Note: We don't increment "." here, we set it. */ + . = (ORIGIN(SRAM) + LENGTH(SRAM) - STACK_SIZE); + _heap_end = .; + } >SRAM + + .stack : { + . = ALIGN(8); + _stack_end = .; + . += (STACK_SIZE - 8); + . = ALIGN(8); + _stack_start = .; + } >SRAM + + /DISCARD/ : { + /* exidx and extab are needed for unwinding, which we don't use */ + *(.ARM.exidx*) + *(.ARM.extab*) + } +} diff --git a/ion/src/device/bootloader/drivers/board.cpp b/ion/src/device/bootloader/drivers/board.cpp index 3db56ee2a..03e47bc2c 100644 --- a/ion/src/device/bootloader/drivers/board.cpp +++ b/ion/src/device/bootloader/drivers/board.cpp @@ -106,7 +106,20 @@ void initMPU() { MPU.RASR()->setB(0); MPU.RASR()->setENABLE(true); - // 2.3 Enable MPU + /* 2.3 Empty sector + * We have to override the sectors configured by the bootloader. */ + while(sector < 8) { + MPU.RNR()->setREGION(sector++); + MPU.RBAR()->setADDR(0); + MPU.RASR()->setENABLE(0); + } + + /* We assert that all sectors have been initialized. Otherwise, the bootloader + * configuration is still active on the last sectors when their configuration + * should be reset. */ + assert(sector == 8); + + // 2.4 Enable MPU MPU.CTRL()->setPRIVDEFENA(true); MPU.CTRL()->setENABLE(true); diff --git a/ion/src/device/bootloader/platform_info.cpp b/ion/src/device/bootloader/platform_info.cpp index 4ca5052cc..c1f83e36a 100644 --- a/ion/src/device/bootloader/platform_info.cpp +++ b/ion/src/device/bootloader/platform_info.cpp @@ -13,18 +13,14 @@ #error This file expects OMEGA_VERSION to be defined #endif -#ifndef HEADER_SECTION -#define HEADER_SECTION -#endif - namespace Ion { extern char staticStorageArea[]; } constexpr void * storageAddress = &(Ion::staticStorageArea); -class PlatformInfo { +class KernelHeader { public: - constexpr PlatformInfo() : + constexpr KernelHeader() : m_header(Magic), m_version{EPSILON_VERSION}, m_patchLevel{PATCH_LEVEL}, @@ -41,34 +37,120 @@ public: } private: constexpr static uint32_t Magic = 0xDEC00DF0; - constexpr static uint32_t OmegaMagic = 0xEFBEADDE; uint32_t m_header; const char m_version[8]; const char m_patchLevel[8]; uint32_t m_footer; }; -const PlatformInfo HEADER_SECTION platform_infos; +const KernelHeader __attribute__((section(".kernel_header"), used)) k_kernelHeader; -const char * Ion::softwareVersion() { - return platform_infos.version(); -} - -static const char s_omegaVersion[16] = {OMEGA_VERSION}; +class UserlandHeader { +public: + constexpr UserlandHeader(): + m_header(Magic), + m_expectedEpsilonVersion{EPSILON_VERSION}, + m_storageAddressRAM(storageAddress), + m_storageSizeRAM(Ion::Storage::k_storageSize), + m_externalAppsFlashStart(0xFFFFFFFF), + m_externalAppsFlashEnd(0xFFFFFFFF), + m_externalAppsRAMStart(0xFFFFFFFF), + m_externalAppsRAMEnd(0xFFFFFFFF), + m_footer(Magic), + m_ohm_header(OmegaMagic), + m_omegaVersion{OMEGA_VERSION}, #ifdef OMEGA_USERNAME - static const char s_username[16] = {OMEGA_USERNAME}; + m_username{OMEGA_USERNAME}, #else - static const char s_username[16] = {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}; + m_username{"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, #endif + m_ohm_footer(OmegaMagic) { } + + const char * omegaVersion() const { + assert(m_storageAddressRAM != nullptr); + assert(m_storageSizeRAM != 0); + assert(m_header == Magic); + assert(m_footer == Magic); + assert(m_ohm_header == OmegaMagic); + assert(m_ohm_footer == OmegaMagic); + return m_omegaVersion; + } + const volatile char * username() const volatile { + assert(m_storageAddressRAM != nullptr); + assert(m_storageSizeRAM != 0); + assert(m_header == Magic); + assert(m_footer == Magic); + assert(m_ohm_header == OmegaMagic); + assert(m_ohm_footer == OmegaMagic); + return m_username; + } + +private: + constexpr static uint32_t Magic = 0xDEC0EDFE; + constexpr static uint32_t OmegaMagic = 0xEFBEADDE; + uint32_t m_header; + const char m_expectedEpsilonVersion[8]; + void * m_storageAddressRAM; + size_t m_storageSizeRAM; + /* We store the range addresses of external apps memory because storing the + * size is complicated due to c++11 constexpr. */ + uint32_t m_externalAppsFlashStart; + uint32_t m_externalAppsFlashEnd; + uint32_t m_externalAppsRAMStart; + uint32_t m_externalAppsRAMEnd; + uint32_t m_footer; + uint32_t m_ohm_header; + const char m_omegaVersion[16]; + const volatile char m_username[16]; + uint32_t m_ohm_footer; +}; + +const UserlandHeader __attribute__((section(".userland_header"), used)) k_userlandHeader; + +class SlotInfo { + +public: + SlotInfo() : + m_header(Magic), + m_footer(Magic) {} + + void update() { + m_header = Magic; + m_kernelHeaderAddress = &k_kernelHeader; + m_userlandHeaderAddress = &k_userlandHeader; + m_footer = Magic; + } + +private: + constexpr static uint32_t Magic = 0xEFEEDBBA; + uint32_t m_header; + const KernelHeader * m_kernelHeaderAddress; + const UserlandHeader * m_userlandHeaderAddress; + uint32_t m_footer; + +}; const char * Ion::omegaVersion() { - return s_omegaVersion; + return k_userlandHeader.omegaVersion(); } const volatile char * Ion::username() { - return s_username; + return k_userlandHeader.username(); +} + +const char * Ion::softwareVersion() { + return k_kernelHeader.version(); } const char * Ion::patchLevel() { - return platform_infos.patchLevel(); + return k_kernelHeader.patchLevel(); +} + +SlotInfo * slotInfo() { + static SlotInfo __attribute__((used)) __attribute__((section(".slot_info"))) slotInformation; + return &slotInformation; +} + +void Ion::updateSlotInfo() { + slotInfo()->update(); } diff --git a/ion/src/device/n0100/platform_info.cpp b/ion/src/device/n0100/platform_info.cpp index cdf676a0c..90d205352 100644 --- a/ion/src/device/n0100/platform_info.cpp +++ b/ion/src/device/n0100/platform_info.cpp @@ -107,3 +107,7 @@ const volatile char * Ion::username() { const char * Ion::patchLevel() { return platform_infos.patchLevel(); } + +void Ion::updateSlotInfo() { + +} \ No newline at end of file diff --git a/ion/src/device/n0110/platform_info.cpp b/ion/src/device/n0110/platform_info.cpp index cdf676a0c..90d205352 100644 --- a/ion/src/device/n0110/platform_info.cpp +++ b/ion/src/device/n0110/platform_info.cpp @@ -107,3 +107,7 @@ const volatile char * Ion::username() { const char * Ion::patchLevel() { return platform_infos.patchLevel(); } + +void Ion::updateSlotInfo() { + +} \ No newline at end of file diff --git a/ion/src/device/shared/usb/dfu_relocated.cpp b/ion/src/device/shared/usb/dfu_relocated.cpp index 82868a88c..fb001cef1 100644 --- a/ion/src/device/shared/usb/dfu_relocated.cpp +++ b/ion/src/device/shared/usb/dfu_relocated.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -14,6 +15,7 @@ namespace USB { typedef void (*PollFunctionPointer)(bool exitWithKeyboard); void DFU(bool exitWithKeyboard) { + Ion::updateSlotInfo(); /* DFU transfers can serve two purposes: * - Transfering RAM data between the machine and a host, e.g. Python scripts diff --git a/ion/src/device/shared/usb/dfu_xip.cpp b/ion/src/device/shared/usb/dfu_xip.cpp index 753948f73..fd0447c32 100644 --- a/ion/src/device/shared/usb/dfu_xip.cpp +++ b/ion/src/device/shared/usb/dfu_xip.cpp @@ -1,9 +1,11 @@ +#include #include "calculator.h" namespace Ion { namespace USB { void DFU(bool exitWithKeyboard) { + Ion::updateSlotInfo(); Ion::Device::USB::Calculator::PollAndReset(exitWithKeyboard); }