diff --git a/bootloader/Makefile b/bootloader/Makefile index 31edbf00e..24270de7a 100644 --- a/bootloader/Makefile +++ b/bootloader/Makefile @@ -37,6 +37,7 @@ bootloader_src += $(addprefix bootloader/interface/menus/, \ warning.cpp \ slot_recovery.cpp \ crash.cpp \ + upsilon_recovery.cpp \ ) bootloader_images = $(addprefix bootloader/, \ diff --git a/bootloader/boot.cpp b/bootloader/boot.cpp index 931b27afc..8aa002603 100644 --- a/bootloader/boot.cpp +++ b/bootloader/boot.cpp @@ -65,7 +65,7 @@ bool Boot::isKernelPatched(const Slot & s) { return *(uint32_t *)(origin_isr + sizeof(uint32_t) * 7) == ((uint32_t)&_fake_isr_function_start) + 1; } -__attribute((section(".fake_isr_function"))) __attribute__((used)) void Boot::flash_interrupt() { +__attribute__((section(".fake_isr_function"))) __attribute__((used)) void Boot::flash_interrupt() { // a simple function Ion::Device::Flash::ClearInternalFlashErrors(); asm("bx lr"); diff --git a/bootloader/boot.h b/bootloader/boot.h index 300fb762c..c771e5678 100644 --- a/bootloader/boot.h +++ b/bootloader/boot.h @@ -47,7 +47,7 @@ public: static void bootSlot(Bootloader::Slot slot); static void bootSelectedSlot(); __attribute__ ((noreturn)) static void jumpToInternalBootloader(); - __attribute((section(".fake_isr_function"))) __attribute__((used)) static void flash_interrupt(); + __attribute__ ((section(".fake_isr_function"))) __attribute__((used)) static void flash_interrupt(); static void bootloader(); static void lockInternal(); diff --git a/bootloader/interface/menus/upsilon_recovery.cpp b/bootloader/interface/menus/upsilon_recovery.cpp new file mode 100644 index 000000000..561a83ec0 --- /dev/null +++ b/bootloader/interface/menus/upsilon_recovery.cpp @@ -0,0 +1,46 @@ +#include "upsilon_recovery.h" +#include +#include +#include +#include +#include + +extern "C" void jump_to_firmware(const uint32_t* stackPtr, const void(*startPtr)(void)); + +Bootloader::UpsilonRecoveryMenu::UpsilonRecoveryMenu() : Menu(KDColorBlack, KDColorWhite, Messages::upsilonRecoveryTitle, Messages::mainTitle) { + setup(); +} + +void Bootloader::UpsilonRecoveryMenu::setup() { + m_defaultColumns[0] = Column(Messages::upsilonRecoveryMessage1, k_small_font, 0, true); + m_defaultColumns[1] = Column(Messages::upsilonRecoveryMessage2, k_small_font, 0, true); + m_defaultColumns[2] = Column(Messages::upsilonRecoveryMessage3, k_small_font, 0, true); + m_defaultColumns[3] = Column(Messages::upsilonRecoveryMessage4, k_small_font, 0, true); + m_defaultColumns[4] = Column(Messages::upsilonRecoveryMessage5, k_small_font, 0, true); + + m_columns[0] = ColumnBinder(&m_defaultColumns[0]); + m_columns[1] = ColumnBinder(&m_defaultColumns[1]); + m_columns[2] = ColumnBinder(&m_defaultColumns[2]); + m_columns[3] = ColumnBinder(&m_defaultColumns[3]); + m_columns[4] = ColumnBinder(&m_defaultColumns[4]); +} + +void Bootloader::UpsilonRecoveryMenu::postOpen() { + // We override the open method + for (;;) { + uint64_t scan = Ion::Keyboard::scan(); + if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Back)) { + while (Ion::Keyboard::scan() == Ion::Keyboard::State(Ion::Keyboard::Key::Back)); + forceExit(); + return; + } else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::OnOff)) { + Ion::Power::standby(); + return; + } else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::OK)) { + Slot slot = Slot::Upsilon(); + Ion::Device::Board::bootloaderMPU(); + jump_to_firmware(slot.kernelHeader()->stackPointer(), slot.userlandHeader()->upsilonRecoveryBootFunction()); + for(;;); + } + } +} diff --git a/bootloader/interface/menus/upsilon_recovery.h b/bootloader/interface/menus/upsilon_recovery.h new file mode 100644 index 000000000..d911f89e8 --- /dev/null +++ b/bootloader/interface/menus/upsilon_recovery.h @@ -0,0 +1,15 @@ +#ifndef _BOOTLOADER_INTERFACE_MENUS_UPSILON_RECOVERY_H_ +#define _BOOTLOADER_INTERFACE_MENUS_UPSILON_RECOVERY_H_ + +#include + +namespace Bootloader { + class UpsilonRecoveryMenu : public Menu { + public: + UpsilonRecoveryMenu(); + void setup() override; + void postOpen() override; + }; +} + +#endif \ No newline at end of file diff --git a/bootloader/interface/src/menu.h b/bootloader/interface/src/menu.h index 0e88ba7c6..ba8bae77c 100644 --- a/bootloader/interface/src/menu.h +++ b/bootloader/interface/src/menu.h @@ -9,10 +9,10 @@ namespace Bootloader { class Menu { public: Menu() : Menu(KDColorBlack, KDColorWhite, Messages::mainTitle) { }; - Menu(KDColor foreground, KDColor background, const char * title) : Menu(foreground, background, title, nullptr) {}; - Menu(KDColor foreground, KDColor background, const char * title, const char * bottom) : Menu(foreground, background, title, bottom, false) {}; - Menu(KDColor foreground, KDColor background, const char * title, const char * bottom, bool centerY) : Menu(foreground, background, title, bottom, centerY, k_columns_margin) {}; - Menu(KDColor foreground, KDColor background, const char * title, const char * bottom, bool centerY, int margin) : m_columns(), m_defaultColumns(), m_slotColumns(), m_background(background), m_title(title), m_foreground(foreground), m_bottom(bottom), m_centerY(centerY), m_forced_exit(false), m_margin(margin) { + Menu(KDColor forground, KDColor background, const char * title) : Menu(forground, background, title, nullptr) {}; + Menu(KDColor forground, KDColor background, const char * title, const char * bottom) : Menu(forground, background, title, bottom, false) {}; + Menu(KDColor forground, KDColor background, const char * title, const char * bottom, bool centerY) : Menu(forground, background, title, bottom, centerY, k_columns_margin) {}; + Menu(KDColor forground, KDColor background, const char * title, const char * bottom, bool centerY, int margin) : m_columns(), m_defaultColumns(), m_slotColumns(), m_background(background), m_title(title), m_foreground(forground), m_bottom(bottom), m_centerY(centerY), m_forced_exit(false), m_margin(margin) { setup(); } static const int k_columns_margin = 5; diff --git a/bootloader/interface/static/messages.h b/bootloader/interface/static/messages.h index e47764ac8..98869dbae 100644 --- a/bootloader/interface/static/messages.h +++ b/bootloader/interface/static/messages.h @@ -53,6 +53,15 @@ public: constexpr static const char * recoveryMessage4 = "Press Back to continue."; constexpr static const char * recoveryMessage5 = "(you will not be able to recover your data !)"; + // Upsilon Recovery menu + constexpr static const char * upsilonRecoveryTitle = "Upsilon Recovery"; + + constexpr static const char * upsilonRecoveryMessage1 = "The bootloader has detected a crash."; + constexpr static const char * upsilonRecoveryMessage2 = "Because you also have an Upsilon slot,"; + constexpr static const char * upsilonRecoveryMessage3 = "you can recover your data by booting"; + constexpr static const char * upsilonRecoveryMessage4 = "the Upsilon slot."; + constexpr static const char * upsilonRecoveryMessage5 = "Press OK to continue, BACK to cancel"; + // Warning menu constexpr static const char * epsilonWarningTitle = "Epsilon Slot"; diff --git a/bootloader/recovery.cpp b/bootloader/recovery.cpp index 8c849f781..1e6174bb1 100644 --- a/bootloader/recovery.cpp +++ b/bootloader/recovery.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -10,6 +11,7 @@ #include #include #include +#include constexpr static uint32_t MagicStorage = 0xEE0BDDBA; @@ -76,7 +78,15 @@ void Bootloader::Recovery::recoverData() { Ion::Display::pushRectUniform(KDRect(0,0,320,240), KDColorWhite); Ion::Backlight::init(); - USBData udata = USBData::Recovery((uint32_t)getSlotConcerned().getStorageAddress(), (uint32_t)getSlotConcerned().getStorageSize()); + CrashedSlot slot = getSlotConcerned(); + + if (Slot::hasUpsilon() && Slot::Upsilon().userlandHeader()->hasUpsilonExtras()) { + Ion::Device::BootloaderSharedData::sharedBootloaderData()->setRecovery((uint32_t)slot.getStorageAddress(), slot.getStorageSize()); + UpsilonRecoveryMenu reco = UpsilonRecoveryMenu(); + reco.open(); + } + + USBData udata = USBData::Recovery((uint32_t)slot.getStorageAddress(), (uint32_t)slot.getStorageSize()); SlotRecoveryMenu menu = SlotRecoveryMenu(&udata); menu.open(); diff --git a/bootloader/slots/slot.cpp b/bootloader/slots/slot.cpp index d8ce93398..34d14e3d6 100644 --- a/bootloader/slots/slot.cpp +++ b/bootloader/slots/slot.cpp @@ -2,6 +2,7 @@ #include #include #include +#include extern "C" void jump_to_firmware(const uint32_t* stackPtr, const void(*startPtr)(void)); @@ -19,6 +20,15 @@ const Slot Slot::Khi() { return Slot(0x90180000); } +const bool Slot::hasUpsilon() { + return (isFullyValid(A()) && A().userlandHeader()->isUpsilon()) || (isFullyValid(B()) && B().userlandHeader()->isUpsilon()); +} + +const Slot Slot::Upsilon() { + assert(hasUpsilon()); + return (isFullyValid(A()) && A().userlandHeader()->isUpsilon()) ? A() : B(); +} + const KernelHeader* Slot::kernelHeader() const { return m_kernelHeader; } diff --git a/bootloader/slots/slot.h b/bootloader/slots/slot.h index 8aa97b4d3..f8ed363bc 100644 --- a/bootloader/slots/slot.h +++ b/bootloader/slots/slot.h @@ -24,6 +24,8 @@ public: static const Slot A(); static const Slot B(); static const Slot Khi(); + static const bool hasUpsilon(); + static const Slot Upsilon(); static bool isFullyValid(const Slot& slot) { return slot.kernelHeader()->isValid() && slot.userlandHeader()->isValid(); diff --git a/bootloader/slots/userland_header.cpp b/bootloader/slots/userland_header.cpp index 3c8a91908..d05c55a5a 100644 --- a/bootloader/slots/userland_header.cpp +++ b/bootloader/slots/userland_header.cpp @@ -38,4 +38,12 @@ const size_t UserlandHeader::storageSize() const { return m_storageSizeRAM; } +const bool UserlandHeader::hasUpsilonExtras() const { + return m_upsilonExtraMagicFooter == UpsilonMagic; +} + +const void (*UserlandHeader::upsilonRecoveryBootFunction() const)() { + return m_recoveryAddress; +} + } diff --git a/bootloader/slots/userland_header.h b/bootloader/slots/userland_header.h index 382e409b0..abead1378 100644 --- a/bootloader/slots/userland_header.h +++ b/bootloader/slots/userland_header.h @@ -17,6 +17,9 @@ public: const char * upsilonVersion() const; const void * storageAddress() const; const size_t storageSize() const; + const bool hasUpsilonExtras() const; + const void (*upsilonRecoveryBootFunction() const)(); + private: UserlandHeader(); @@ -42,6 +45,8 @@ private: const char m_UpsilonVersion[16]; uint32_t m_osType; uint32_t m_upsilonMagicFooter; + const void (*m_recoveryAddress)(); + uint32_t m_upsilonExtraMagicFooter; }; extern const UserlandHeader* s_userlandHeaderA; diff --git a/ion/src/device/bootloader/boot/rt0.cpp b/ion/src/device/bootloader/boot/rt0.cpp index a631356bb..2e242cd8a 100644 --- a/ion/src/device/bootloader/boot/rt0.cpp +++ b/ion/src/device/bootloader/boot/rt0.cpp @@ -1,11 +1,15 @@ #include #include #include +#include +#include +#include #include #include #include #include #include +#include typedef void (*cxx_constructor)(); @@ -14,6 +18,8 @@ extern "C" { extern char _data_section_start_ram; extern char _data_section_end_ram; extern char _bss_section_start_ram; + extern char _static_storage_start; + extern char _static_storage_end; extern char _bss_section_end_ram; extern cxx_constructor _init_array_start; extern cxx_constructor _init_array_end; @@ -129,6 +135,49 @@ void __attribute__((noinline)) start() { abort(); } +void __attribute__((noinline)) __attribute__((section(".recovery_boot"))) __attribute__((used)) recovery_start() { + // Here we are in the recovery boot. + Ion::Device::Board::initFPU(); + + bool is_recoverying = Ion::Device::BootloaderSharedData::sharedBootloaderData()->storageAddress() != 0; + + if (is_recoverying) { + uint32_t address = Ion::Device::BootloaderSharedData::sharedBootloaderData()->storageAddress(); + uint32_t size = Ion::Device::BootloaderSharedData::sharedBootloaderData()->storageSize(); + + size_t storageSize = (&_static_storage_end - &_static_storage_start); + + memcpy(&_static_storage_start, (void*)address, size); + + size_t dataSectionLength = (&_data_section_end_ram - &_data_section_start_ram); + memcpy(&_data_section_start_ram, &_data_section_start_flash, dataSectionLength); + + size_t bssSectionLength = (&_bss_section_end_ram - &_static_storage_end); + memset(&_static_storage_end, 0, bssSectionLength); + } else { + size_t dataSectionLength = (&_data_section_end_ram - &_data_section_start_ram); + memcpy(&_data_section_start_ram, &_data_section_start_flash, dataSectionLength); + size_t bssSectionLength = (&_bss_section_end_ram - &_bss_section_start_ram); + memset(&_bss_section_start_ram, 0, bssSectionLength); + } + + if (&_init_array_start != &_init_array_end) { + abort(); + } + size_t isrSectionLength = (&_isr_vector_table_end_ram - &_isr_vector_table_start_ram); + memcpy(&_isr_vector_table_start_ram, &_isr_vector_table_start_flash, isrSectionLength); + + Ion::Device::Board::init(); + + if (is_recoverying) { + Ion::LED::setColor(KDColorBlue); + } + + jump_to_external_flash(); + + abort(); +} + void __attribute__((interrupt, noinline)) isr_systick() { auto t = Ion::Device::Timing::MillisElapsed; t++; diff --git a/ion/src/device/bootloader/bootloader_common.ld b/ion/src/device/bootloader/bootloader_common.ld index e8f0c9bbc..816e51bcc 100644 --- a/ion/src/device/bootloader/bootloader_common.ld +++ b/ion/src/device/bootloader/bootloader_common.ld @@ -1,3 +1,4 @@ +BOOTLOADER_SHARED_OFFSET = 0x3d0; SECTIONS { .signed_payload_prefix ORIGIN(FLASH) : { @@ -34,7 +35,14 @@ SECTIONS { _isr_vector_table_end_ram = .; } >SRAM - .exam_mode_buffer ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.kernel_header) + SIZEOF(.isr_vector_table) : { + .bootloader_shared ORIGIN(SRAM) + BOOTLOADER_SHARED_OFFSET : AT(ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.kernel_header) + SIZEOF(.isr_vector_table) + SIZEOF(.slot_info)) { + _bootloader_shared_start = .; + KEEP(*(.bootloader_shared)) + KEEP(*(.bootloader_shared.*)) + _bootloader_shared_end = .; + } >SRAM + + .exam_mode_buffer ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.kernel_header) + SIZEOF(.isr_vector_table) + SIZEOF(.bootloader_shared) + SIZEOF(.slot_info) : { . = ALIGN(4K); _exam_mode_buffer_start = .; KEEP(*(.exam_mode_buffer)) @@ -49,6 +57,13 @@ SECTIONS { KEEP(*(.userland_header)); } > FLASH + .recovery_boot : { + . = ALIGN(4); + _recovery_boot_start = .; + KEEP(*(.recovery_boot)); + _recovery_boot_end = .; + } >FLASH + .text : { . = ALIGN(4); *(.text) @@ -88,6 +103,17 @@ SECTIONS { _data_section_end_ram = .; } >SRAM AT> FLASH + /* + * We set the .static_storage right after the .data section, even if it's a bss region, because we need to fix it in memory. + */ + .static_storage : { + . = ALIGN(4); + _bss_section_start_ram = .; + _static_storage_start = .; + KEEP (*(.static_storage)) + _static_storage_end = .; + } > SRAM + .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 @@ -96,7 +122,6 @@ SECTIONS { * 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 diff --git a/ion/src/device/bootloader/internal_flash.ld b/ion/src/device/bootloader/internal_flash.ld index e441c6527..42f6dcf7c 100644 --- a/ion/src/device/bootloader/internal_flash.ld +++ b/ion/src/device/bootloader/internal_flash.ld @@ -8,6 +8,7 @@ MEMORY { STACK_SIZE = 32K; TRAMPOLINES_OFFSET = 0xE000; CUSTOM_TRAMPOLINES_OFFSET = 64K - 64; +BOOTLOADER_SHARED_OFFSET = 0x3d0; SECTIONS { .isr_vector_table ORIGIN(INTERNAL_FLASH) : { @@ -31,7 +32,16 @@ SECTIONS { KEEP(*(.header)) } >INTERNAL_FLASH - .rodata : { + .bootloader_shared ORIGIN(SRAM) + BOOTLOADER_SHARED_OFFSET : AT(ORIGIN(INTERNAL_FLASH) + SIZEOF(.isr_vector_table) + SIZEOF(.header)) { + . = ORIGIN(SRAM) + BOOTLOADER_SHARED_OFFSET; + _bootloader_shared_start = .; + KEEP(*(.bootloader_shared)) + KEEP(*(.bootloader_shared.*)) + _bootloader_shared_end = .; + } >SRAM + + .rodata ORIGIN(INTERNAL_FLASH) + SIZEOF(.isr_vector_table) + SIZEOF(.header) + SIZEOF(.bootloader_shared) : { + . = ALIGN(4); *(.rodata) *(.rodata.*) diff --git a/ion/src/device/bootloader/platform_info.cpp b/ion/src/device/bootloader/platform_info.cpp index 3b08c1860..26d48865e 100644 --- a/ion/src/device/bootloader/platform_info.cpp +++ b/ion/src/device/bootloader/platform_info.cpp @@ -17,6 +17,9 @@ #error This file expects UPSILON_VERSION to be defined #endif +extern "C" { + extern char _recovery_boot_start; +} namespace Ion { extern char staticStorageArea[]; } @@ -72,7 +75,9 @@ public: m_upsilonMagicHeader(UpsilonMagic), m_UpsilonVersion{UPSILON_VERSION}, m_osType(OSType), - m_upsilonMagicFooter(UpsilonMagic) { } + m_upsilonMagicFooter(UpsilonMagic), + m_recoveryAddress(((uint32_t)&_recovery_boot_start) + 1), + m_upsilonExtraMagicFooter(UpsilonMagic) { } const char * omegaVersion() const { assert(m_storageAddressRAM != nullptr); @@ -128,6 +133,8 @@ private: const char m_UpsilonVersion[16]; uint32_t m_osType; uint32_t m_upsilonMagicFooter; + uint32_t m_recoveryAddress; + uint32_t m_upsilonExtraMagicFooter; }; const UserlandHeader __attribute__((section(".userland_header"), used)) k_userlandHeader; diff --git a/ion/src/device/shared/boot/isr.h b/ion/src/device/shared/boot/isr.h index ca5becb13..474522699 100644 --- a/ion/src/device/shared/boot/isr.h +++ b/ion/src/device/shared/boot/isr.h @@ -8,6 +8,7 @@ extern "C" { void start(); void abort(); void isr_systick(); +void recovery_start(); #ifdef __cplusplus } diff --git a/ion/src/device/shared/drivers/Makefile b/ion/src/device/shared/drivers/Makefile index 2db043c5f..0ec24eca3 100644 --- a/ion/src/device/shared/drivers/Makefile +++ b/ion/src/device/shared/drivers/Makefile @@ -26,4 +26,5 @@ ion_device_src += $(addprefix ion/src/device/shared/drivers/, \ timing.cpp \ usb.cpp \ wakeup.cpp \ + bldata.cpp \ ) diff --git a/ion/src/device/shared/drivers/bldata.cpp b/ion/src/device/shared/drivers/bldata.cpp new file mode 100644 index 000000000..dc2ccd474 --- /dev/null +++ b/ion/src/device/shared/drivers/bldata.cpp @@ -0,0 +1,21 @@ +#include "bldata.h" +#include +#include + +namespace Ion { + uint32_t staticSharedData[sizeof(Ion::Device::BootloaderSharedData)/sizeof(uint32_t)] __attribute__((section(".bootloader_shared"))) __attribute__((used)) = {0}; + + Device::BootloaderSharedData * Device::BootloaderSharedData::sharedBootloaderData() { + static BootloaderSharedData * sharedData = new (staticSharedData) BootloaderSharedData(); + return sharedData; + } + + Device::BootloaderSharedData::BootloaderSharedData() { + if (m_header != Magic || m_footer != Magic) { + m_header = Magic; + m_storageAddress = 0; + m_storageSize = 0; + m_footer = Magic; + } + } +} diff --git a/ion/src/device/shared/drivers/bldata.h b/ion/src/device/shared/drivers/bldata.h new file mode 100644 index 000000000..58716a2f6 --- /dev/null +++ b/ion/src/device/shared/drivers/bldata.h @@ -0,0 +1,24 @@ +#include + +namespace Ion +{ +namespace Device +{ + class BootloaderSharedData { + public: + constexpr static uint32_t Magic = 0x626C6461; + static BootloaderSharedData * sharedBootloaderData(); + + BootloaderSharedData(); + + void setRecovery(uint32_t address, uint32_t size) { m_storageAddress = address; m_storageSize = size; } + uint32_t storageAddress() const { return m_storageAddress; } + uint32_t storageSize() const { return m_storageSize; } + private: + uint32_t m_header; + uint32_t m_storageAddress; + uint32_t m_storageSize; + uint32_t m_footer; + }; +} +} \ No newline at end of file diff --git a/ion/src/shared/internal_storage.cpp b/ion/src/shared/internal_storage.cpp index 5fc4a90d1..f8aac4d50 100644 --- a/ion/src/shared/internal_storage.cpp +++ b/ion/src/shared/internal_storage.cpp @@ -336,18 +336,18 @@ void InternalStorage::destroyRecordsWithExtension(const char * extension) { } } -InternalStorage::InternalStorage() : - m_magicHeader(Magic), - m_buffer(), - m_magicFooter(Magic), - m_delegate(nullptr), - m_lastRecordRetrieved(nullptr), - m_lastRecordRetrievedPointer(nullptr) -{ +InternalStorage::InternalStorage() { + m_magicHeader = Magic; + + m_magicFooter = Magic; + m_delegate = nullptr; + m_lastRecordRetrieved = nullptr; + m_lastRecordRetrievedPointer = nullptr; + assert(m_magicHeader == Magic); assert(m_magicFooter == Magic); // Set the size of the first record to 0 - overrideSizeAtPosition(m_buffer, 0); + // overrideSizeAtPosition(m_buffer, 0); } // PRIVATE diff --git a/ion/src/shared/storage.cpp b/ion/src/shared/storage.cpp index 50a8a4c70..0a77cfa39 100644 --- a/ion/src/shared/storage.cpp +++ b/ion/src/shared/storage.cpp @@ -6,7 +6,7 @@ namespace Ion { -uint32_t staticStorageArea[sizeof(Storage)/sizeof(uint32_t)] = {0}; +uint32_t __attribute__((section(".static_storage"))) staticStorageArea[sizeof(Storage)/sizeof(uint32_t)] = {0}; Storage * Storage::sharedStorage() { static Storage * storage = new (staticStorageArea) Storage();