Здесь проблема в том, что вторичный загрузчик собирается под некий начальный адрес. Т.е. нужно будет либо выяснять, скажем, из .map файла первичного загрузчика адрес этого массива и выставлять в linker script вторичного, либо собирать вторичный как position-independent executable, что увеличит размер (ну и всё равно потребует некоторых настроек).
"Подкрутить" linker script на самом деле просто - находите в нём объявление доступных диапазонов памяти memory { ... }, внутри находите объявление RAM с началом/длиной, в первичном загрузчике, к примеру, уменьшаете длину до 1к (вообще нужно посмотреть его .map файл, понять, сколько +- ему нужно и округлить в большую сторону), во вторичном прибавляете те же самые 1к к начальному адресу и вычитаете из длины - всё, первичный ограничен в первом кб RAM, у вторичного тот же самый первый кб выведен из доступных, они больше не пересекаются.
Всё бы так, но этот гипотетический "опытный проектировщик" настолько опытен, что уже не первый раз чудесным образом "по описанию в каталоге" полностью угадывает топологию аналога от конкретной одной и той же тайваньской фирмы.
Если "в лоб" - да, перетрёт, нужно спланировать размещение и того и другого (в linker script). Учтите, что работа загрузчиков разделена во времени и в общем случае вторичный спокойно может при старте затереть секции данных первичного своими неинициализированными данными (.bss)/стеком и вообще не вспоминать, что до него исполнялось что-то ещё.
В случае же с переиспользованием (вызовом) функций первичного из вторичного какие-то данные первичного (относящиеся к вызываемым функциям) нужно будет оставить нетронутыми, и тут несколько вариантов:
полностью исключить секции .data/.bss первичного из доступной памяти вторичного (технически проще, но может быть затратнее, если помимо действительно нужных данных сохранится много лишнего)
вынести переиспользуемые данные в отдельные секции (в linker script можно указать размещение таких-то секций из таких-то файлов в такие-то области, либо самим переменным указать атрибут section("name") (см. gcc section attribute)) и оставить нетронутыми только их
в функциях первичного, требующих использования глобальных данных, не ссылаться на те жёстко, а передавать в них указатель на "контекст", который во время работы вторичного загрузчика сможет находиться уже в другом месте (но его нужно будет инициализировать).
Смотрел диагностической утилитой от производителя (IBM ITDT. Сам привод брендирован как Dell, но внутри железо от IBM). Универсальных команд, возможно, и нет.
Вдогонку: вообще странно, что оптимизатор не смог установить факт незименности UART. Доводилось смотреть бинарники на основе STM32 HAL, где вовсю практикуется подобный подход (структуры с контекстами периферии), и там оптимизатор распутывал вызовы наподобие HAL_GPIO_WritePin(&Pin123Handle, 0) вообще до замены вызова на inline запись нужного значения в нужный регистр.
Сам UartInstance, часом, не как volatile объявлен? (вот этой информации в elf не сохраняется, не вижу). Если нет, можно попробовать перенести инициализацию базового адреса UART из uart_mcal_init (node->UARTx=&UART0) непосредственно в статический инициализатор UartHandle_t UartInstance = { ..., .UARTx=&UART0, ... } (вижу, что он у вас присутствует, некоторые поля UartInstance инициализированы статически). Для пущего эффекта можно даже полю UARTx навесить const.
Вполне реально, на самом деле, ценой меньшей переносимости.
Насколько вижу по https://github.com/aabzel/Artifacts/blob/main/start_mik32_v1_eeprom_bootloader_m/start_mik32_v1_eeprom_bootloader_m.elf, у вас высокие требования к переносимости кода, всё, что можно, абстрагировано в драйверы с интерфейсами в виде структур. Это в общем случае здорово (если приходится менять чипы итд), но ощутимо ограничивает компилятор в оптимизациях. Простой пример: ваш загрузчик общается через единственный и жёстко заданный UART, но адрес данного UART заносится в структуру UartHandle_t во время исполнения (в uart_mcal_init), оптимизатор не может докопаться до факта, что UART - один и тот же такой-то, в результате совсем низкоуровневая функция отправки массива в UART вынуждена постоянно жонглировать полями структуры (она вообще скомпилировалась так, что готова даже к изменению адреса UART посреди отправки массива). Остальной код выглядит примерно так же - львиная доля объёма (и времени выполнения!) приходится на манипуляции с полями структур драйверов, а не сам функционал загрузчика. Это, в целом, вопрос стиля кода, данный уровень абстракции вполне достижим и с учётом интересов оптимизатора (помнится, на Хабре был цикл статей по программированию микроконтроллеров на С++ с грамотным использованием шаблонов, позволявшим компилятору "разматывать" зависимости до удивительных глубин).
Но если вам нужно как-то уместить туда CRC8 и забыть, достаточно выкинуть оптимизированные на скорость в сильный ущерб объёму библиотечные memcpy и memset, заменив их своими побайтовыми версиями, и простой CRC8 (без таблицы, побитовый цикл с XOR с полиномом) уж точно влезет в освободившееся место.
Странновато, что 8к оказалось впритык под такое, но, глядя на характеристики данного чипа, упоминающие 16к RAM, напрашивается другой классический путь - первичный загрузчик из ROM принимает более объёмистый вторичный загрузчик в RAM, прыгает в него, а тот уже творит что хочет. Дополнительные плюсы: можно тем же способом загружать и гонять любые сервисные вещи - тестер платы, «читальщик» flash для диагностики сбоев итд. Для уменьшения объёма вторичного загрузчика можно даже переиспользовать фкнкции из ROM (код вашего протокола, к примеру).
Контроллеры SSD всё-таки далеко не ПК с Windows, тремя уровнями кешей процессора, кешами ФС итд, их памяти/регистры вполне обозримы и управляемы настолько, что можно не гадать, где конкретно мог осесть ключ, а просто перезагрузиться (аппаратным сбросом) в некий «режим тревоги», в котором целиком забить мусором RAM, кеш (если он там вообще есть), хранилище ключа.
Более актуален скорее вопрос, поднятый ниже - насколько доказуема корректная реализация, не слит ли ключ к производителю итд. Прожжённая дырка на месте кристаллов NAND была бы куда убедительнее. Однако в статье ноль деталей реализации, что и как уничтожается физически, а без них точно так же хватает пространства для скепсиса.
В качестве метода физического разрушения чипов без использования батареек вспомнились "брелки для разбивания стёкол при авариях" с твердосплавным бойком и мощной пружиной, взведённой на производстве - пусть такое бъёт в центр чипа NAND.
Пару лет назад от дружеского сервиса достался, по-видимому, далёкий предок (возраст - около 20 лет) того AmScope SE400 из статьи, произведённый компанией Motic и когда-то стоивший серьёзных денег. Выглядит один в один, за исключением лампы подсветки, установленной жёстко за объективом, работает идеально, глаза не устают, голова не болит, глубина изображения - присутствует. Шея - да, протестует иногда.
А вот в новые красивые тринокуляры (бинокуляр+камера, сервисники с камеры процесс ремонта клиентам в зону ожидания транслируют "для красоты") с Али, которые тот сервис купил на замену этому Motic'у, смотреть двумя глазами невозможно - ощущение косоглазия, против которого не помогли никакие настройки. Говорят, дело в несовпадении оптических осей левого/правого трактов.
Прошу прощения, но "контрольный модуль", "задачи базовой полосы" - попахивает переводом на малознакомую тему, а утверждения о функциях чипов Ericsson - фантазии.
VP27266 ARM на "контрольном модуле" - вспомогательный процессор для режима модема и IrDA. Была и более дешёвая модель телефона (без IrDA/модема), в которой данная плата не устанавливалась. Возможно, и этот телефон без неё заведётся.
VP27317 - основной процессор (ядро AVR, как в Arduino, верхний уровень GSM протокола, пользовательский интерфейс, флеш M29W008 хранит его прошивку)
RYS105627 - DSP (сигнальный процессор, нижний уровень GSM протокола, управление радиотрактом, аудио)
Выше небеспочвенно удивлялись двухпроцессорной архитектуре - да, вот так странно у старых Ericsson делились функции, "наследственный" 8-битный AVR выполнял основную работу, а "новомодный" на тот момент ARM - вспомогательные "новомодные" функции.
Поддержу, не вижу особого смысла писать такого рода утилиты на С. Python проще, а главное, оброс массой простых в использовании библиотек на все случаи жизни. Просто приведу примеры: у нас в ходу загрузчики, общающиеся через целый зоопарк интерфейсов: UART, USB, BLE, CAN, UDS-DoIP-Ethernet. Для всех этих интерфейсов нашлись достаточно удобные кросс-платформенные Python библиотеки, благодаря которым написание загрузчиков ощущалось как отдых от основной деятельности, какой-нибудь CAN на приём поднялся вообще тремя строчками итд. Сколько времени/сил отняло бы завести сами эти интерфейсы под две ОС на С даже думать не хочется, невозможность переиспользовать Сшную реализацию своих протоколов - мелочи в сравнении с этим. А уж со всякой «косметикой» вроде именованных параметров командной строки, которые автор статьи аж в отдельный пункт TODO вынес, или разноцветным выводом в консоль, на Python вопросов вообще не возникает.
В embedded вообще хватает хардкора, так пусть он будет только там, где оправдан. Поверхностные знания Python, которых хватит для написания подобного рода утилит, достаются довольно «дёшево».
КМК, существенное отличие между озоновой проблемой и потеплением - масштабы требуемых изменений. Фреоны были всё-таки довольно нишевым (в масштабах цивилизации) продуктом, изменения коснулись самых «нижних» уровней технологий - замена материалов/оборудования с теми же функциями в рамках тех же сценариев использования. Уход же от ископаемого топлива требует несравнимо больших изменений, финансово бьёт по целым странам итд
А я в них периодически езжу. А ещё их РЖД закупала до 2022г ("Стриж", урезанная до 200км/ч версия, больше "высокая" инж.культура проложенных путей не позволяет). А так же летаю самолётами, "собратья" которых наверняка когда-то терпели крушения, и ничего, ибо статистика. По мне, одна за такой срок серьёзная авария у техники со многими десятками рейсов каждый день - очень даже хороший показатель качества.
Навскидку: по Иберийскому полуострову, с его инженерной культурой, уже лет 20 ездят собственные поезда со скоростями 300+ (найдите историю Talgo, интересное чтиво), городские сети убраны со столбов под землю, водопроводы без железных труб итд. Они раздолбаи ровно дотуда, докуда «можно», электрик приходит на два дня позже обещанного, но педантично отмеряет высоту розетки над полом и суёт провода в гофротрубу в подсобке, где в жизни никто этого не увидит - «так положено».
Здесь проблема в том, что вторичный загрузчик собирается под некий начальный адрес. Т.е. нужно будет либо выяснять, скажем, из .map файла первичного загрузчика адрес этого массива и выставлять в linker script вторичного, либо собирать вторичный как position-independent executable, что увеличит размер (ну и всё равно потребует некоторых настроек).
"Подкрутить" linker script на самом деле просто - находите в нём объявление доступных диапазонов памяти memory { ... }, внутри находите объявление RAM с началом/длиной, в первичном загрузчике, к примеру, уменьшаете длину до 1к (вообще нужно посмотреть его .map файл, понять, сколько +- ему нужно и округлить в большую сторону), во вторичном прибавляете те же самые 1к к начальному адресу и вычитаете из длины - всё, первичный ограничен в первом кб RAM, у вторичного тот же самый первый кб выведен из доступных, они больше не пересекаются.
Первичный загрузчик:
MEMORY
{
RAM (rwx) : ORIGIN = 0x02000000, LENGTH = 0x400 /* первые 1К */
}
Вторичный:
MEMORY
{
RAM (rwx) : ORIGIN = 0x02000400, LENGTH = 0x3C00 /* оставшиеся 15К */
}
Всё бы так, но этот гипотетический "опытный проектировщик" настолько опытен, что уже не первый раз чудесным образом "по описанию в каталоге" полностью угадывает топологию аналога от конкретной одной и той же тайваньской фирмы.
Если "в лоб" - да, перетрёт, нужно спланировать размещение и того и другого (в linker script). Учтите, что работа загрузчиков разделена во времени и в общем случае вторичный спокойно может при старте затереть секции данных первичного своими неинициализированными данными (.bss)/стеком и вообще не вспоминать, что до него исполнялось что-то ещё.
В случае же с переиспользованием (вызовом) функций первичного из вторичного какие-то данные первичного (относящиеся к вызываемым функциям) нужно будет оставить нетронутыми, и тут несколько вариантов:
полностью исключить секции .data/.bss первичного из доступной памяти вторичного (технически проще, но может быть затратнее, если помимо действительно нужных данных сохранится много лишнего)
вынести переиспользуемые данные в отдельные секции (в linker script можно указать размещение таких-то секций из таких-то файлов в такие-то области, либо самим переменным указать атрибут section("name") (см. gcc section attribute)) и оставить нетронутыми только их
в функциях первичного, требующих использования глобальных данных, не ссылаться на те жёстко, а передавать в них указатель на "контекст", который во время работы вторичного загрузчика сможет находиться уже в другом месте (но его нужно будет инициализировать).
Смотрел диагностической утилитой от производителя (IBM ITDT. Сам привод брендирован как Dell, но внутри железо от IBM). Универсальных команд, возможно, и нет.
Вдогонку: вообще странно, что оптимизатор не смог установить факт незименности UART. Доводилось смотреть бинарники на основе STM32 HAL, где вовсю практикуется подобный подход (структуры с контекстами периферии), и там оптимизатор распутывал вызовы наподобие HAL_GPIO_WritePin(&Pin123Handle, 0) вообще до замены вызова на inline запись нужного значения в нужный регистр.
Сам UartInstance, часом, не как volatile объявлен? (вот этой информации в elf не сохраняется, не вижу). Если нет, можно попробовать перенести инициализацию базового адреса UART из uart_mcal_init (node->UARTx=&UART0) непосредственно в статический инициализатор UartHandle_t UartInstance = { ..., .UARTx=&UART0, ... } (вижу, что он у вас присутствует, некоторые поля UartInstance инициализированы статически). Для пущего эффекта можно даже полю UARTx навесить const.
Вполне реально, на самом деле, ценой меньшей переносимости.
Насколько вижу по https://github.com/aabzel/Artifacts/blob/main/start_mik32_v1_eeprom_bootloader_m/start_mik32_v1_eeprom_bootloader_m.elf, у вас высокие требования к переносимости кода, всё, что можно, абстрагировано в драйверы с интерфейсами в виде структур. Это в общем случае здорово (если приходится менять чипы итд), но ощутимо ограничивает компилятор в оптимизациях. Простой пример: ваш загрузчик общается через единственный и жёстко заданный UART, но адрес данного UART заносится в структуру UartHandle_t во время исполнения (в uart_mcal_init), оптимизатор не может докопаться до факта, что UART - один и тот же такой-то, в результате совсем низкоуровневая функция отправки массива в UART вынуждена постоянно жонглировать полями структуры (она вообще скомпилировалась так, что готова даже к изменению адреса UART посреди отправки массива). Остальной код выглядит примерно так же - львиная доля объёма (и времени выполнения!) приходится на манипуляции с полями структур драйверов, а не сам функционал загрузчика. Это, в целом, вопрос стиля кода, данный уровень абстракции вполне достижим и с учётом интересов оптимизатора (помнится, на Хабре был цикл статей по программированию микроконтроллеров на С++ с грамотным использованием шаблонов, позволявшим компилятору "разматывать" зависимости до удивительных глубин).
Но если вам нужно как-то уместить туда CRC8 и забыть, достаточно выкинуть оптимизированные на скорость в сильный ущерб объёму библиотечные memcpy и memset, заменив их своими побайтовыми версиями, и простой CRC8 (без таблицы, побитовый цикл с XOR с полиномом) уж точно влезет в освободившееся место.
Странновато, что 8к оказалось впритык под такое, но, глядя на характеристики данного чипа, упоминающие 16к RAM, напрашивается другой классический путь - первичный загрузчик из ROM принимает более объёмистый вторичный загрузчик в RAM, прыгает в него, а тот уже творит что хочет. Дополнительные плюсы: можно тем же способом загружать и гонять любые сервисные вещи - тестер платы, «читальщик» flash для диагностики сбоев итд. Для уменьшения объёма вторичного загрузчика можно даже переиспользовать фкнкции из ROM (код вашего протокола, к примеру).
Контроллеры SSD всё-таки далеко не ПК с Windows, тремя уровнями кешей процессора, кешами ФС итд, их памяти/регистры вполне обозримы и управляемы настолько, что можно не гадать, где конкретно мог осесть ключ, а просто перезагрузиться (аппаратным сбросом) в некий «режим тревоги», в котором целиком забить мусором RAM, кеш (если он там вообще есть), хранилище ключа.
Более актуален скорее вопрос, поднятый ниже - насколько доказуема корректная реализация, не слит ли ключ к производителю итд. Прожжённая дырка на месте кристаллов NAND была бы куда убедительнее. Однако в статье ноль деталей реализации, что и как уничтожается физически, а без них точно так же хватает пространства для скепсиса.
В качестве метода физического разрушения чипов без использования батареек вспомнились "брелки для разбивания стёкол при авариях" с твердосплавным бойком и мощной пружиной, взведённой на производстве - пусть такое бъёт в центр чипа NAND.
Они и с российскими симками отлично ходят на свои серверы за обновлениями. Прилетит «нехорошее» обновление да и всё.
Пару лет назад от дружеского сервиса достался, по-видимому, далёкий предок (возраст - около 20 лет) того AmScope SE400 из статьи, произведённый компанией Motic и когда-то стоивший серьёзных денег. Выглядит один в один, за исключением лампы подсветки, установленной жёстко за объективом, работает идеально, глаза не устают, голова не болит, глубина изображения - присутствует. Шея - да, протестует иногда.
А вот в новые красивые тринокуляры (бинокуляр+камера, сервисники с камеры процесс ремонта клиентам в зону ожидания транслируют "для красоты") с Али, которые тот сервис купил на замену этому Motic'у, смотреть двумя глазами невозможно - ощущение косоглазия, против которого не помогли никакие настройки. Говорят, дело в несовпадении оптических осей левого/правого трактов.
Прошу прощения, но "контрольный модуль", "задачи базовой полосы" - попахивает переводом на малознакомую тему, а утверждения о функциях чипов Ericsson - фантазии.
VP27266 ARM на "контрольном модуле" - вспомогательный процессор для режима модема и IrDA. Была и более дешёвая модель телефона (без IrDA/модема), в которой данная плата не устанавливалась. Возможно, и этот телефон без неё заведётся.
VP27317 - основной процессор (ядро AVR, как в Arduino, верхний уровень GSM протокола, пользовательский интерфейс, флеш M29W008 хранит его прошивку)
RYS105627 - DSP (сигнальный процессор, нижний уровень GSM протокола, управление радиотрактом, аудио)
Выше небеспочвенно удивлялись двухпроцессорной архитектуре - да, вот так странно у старых Ericsson делились функции, "наследственный" 8-битный AVR выполнял основную работу, а "новомодный" на тот момент ARM - вспомогательные "новомодные" функции.
В Германии лихачество в прошлом, нынче у самокатов лимит 20км/ч (полиция реально проверяет на мобильных тестовых стендах) и номера/страховка.
Поддержу, не вижу особого смысла писать такого рода утилиты на С. Python проще, а главное, оброс массой простых в использовании библиотек на все случаи жизни. Просто приведу примеры: у нас в ходу загрузчики, общающиеся через целый зоопарк интерфейсов: UART, USB, BLE, CAN, UDS-DoIP-Ethernet. Для всех этих интерфейсов нашлись достаточно удобные кросс-платформенные Python библиотеки, благодаря которым написание загрузчиков ощущалось как отдых от основной деятельности, какой-нибудь CAN на приём поднялся вообще тремя строчками итд. Сколько времени/сил отняло бы завести сами эти интерфейсы под две ОС на С даже думать не хочется, невозможность переиспользовать Сшную реализацию своих протоколов - мелочи в сравнении с этим. А уж со всякой «косметикой» вроде именованных параметров командной строки, которые автор статьи аж в отдельный пункт TODO вынес, или разноцветным выводом в консоль, на Python вопросов вообще не возникает.
В embedded вообще хватает хардкора, так пусть он будет только там, где оправдан. Поверхностные знания Python, которых хватит для написания подобного рода утилит, достаются довольно «дёшево».
Внимательно перечитал статью ещё раз, не нашёл ни «Запада», ни «Рашки». ЧЯНТД?
Так всё равно ж интернет работать не будет</s>
КМК, существенное отличие между озоновой проблемой и потеплением - масштабы требуемых изменений. Фреоны были всё-таки довольно нишевым (в масштабах цивилизации) продуктом, изменения коснулись самых «нижних» уровней технологий - замена материалов/оборудования с теми же функциями в рамках тех же сценариев использования. Уход же от ископаемого топлива требует несравнимо больших изменений, финансово бьёт по целым странам итд
Ради большей прочности на отрыв. Для «радиоконструктора», да ещё без корпуса, должно быть прямо критично важно.
А я в них периодически езжу. А ещё их РЖД закупала до 2022г ("Стриж", урезанная до 200км/ч версия, больше "высокая" инж.культура проложенных путей не позволяет). А так же летаю самолётами, "собратья" которых наверняка когда-то терпели крушения, и ничего, ибо статистика. По мне, одна за такой срок серьёзная авария у техники со многими десятками рейсов каждый день - очень даже хороший показатель качества.
Навскидку: по Иберийскому полуострову, с его инженерной культурой, уже лет 20 ездят собственные поезда со скоростями 300+ (найдите историю Talgo, интересное чтиво), городские сети убраны со столбов под землю, водопроводы без железных труб итд. Они раздолбаи ровно дотуда, докуда «можно», электрик приходит на два дня позже обещанного, но педантично отмеряет высоту розетки над полом и суёт провода в гофротрубу в подсобке, где в жизни никто этого не увидит - «так положено».
Так столбы ж есть (и рядом место, куда вбить ещё).