Недавно попросили в двух словах рассказать серьезным людям о загрузке операционной системы на ARM и дать оценку угроз безопасности этого процесса. Вообще ARM-процессоров и вообще ОС. Вы понимаете, все ведь слышали про эти ARM, и что такое ОС тоже все знают. Желательно, на уровне квадратиков со стрелками.

Загрузка ARM в четырех прямоугольниках — под катом.

Сразу ограничим уровень детализации. Нас интересует, что происходит, а не как, то есть конкретные инструкции процессора оставим в стороне. Постараемся найти общее у всех процессоров и всех ОС. Поищем угрозы безопасности.

Разновидности процессоров ARM


Если вы знаете про ARM, то этот раздел можно смело пропустить.

В производстве и эксплуатации сейчас встречаются процессоры ARM пяти архитектур: ARMv4, ARMv5, ARMv6, ARMv7 и ARMv8. Компания ARM дает этим архитектурам коммерческие названия, поэтому ARMv4 называется, например, ARM7, ARMv5 – ARM9, а название Cortex имеют процессоры на архитектурах ARMv6, v7, v8. Следующая таблица перечисляет основные разновидности.
Архитектура Коммерческое название Распространенные виды Запуск Linux
ARMv4 ARM7 ARM7TDMI Нецелесообразно
ARMv5 ARM9 ARM926EJ-S Да
ARMv6 ARM11 ARM1176JZF-S Да
Cortex-M0 Cortex-M0 Нет
ARMv7 Cortex-M Cortex-M3 Нецелесообразно
Cortex-A Cortex-A9 Да
Cortex-R Cortex-R4 Да
ARMv8 Cortex-A Cortex-A53 Да

Например, кнопочные телефоны в основном используют ARM7, а смартфоны – Cortex-A. Современные смартфоны строятся преимущественно на ARMv8, единственных 64-битных. Процессоры ARM7 и ARM9 широко применялись в различных промышленных контроллерах, сетевом оборудовании, а сейчас фокус переходит на использование в них Cortex-A. В различной бытовой технике, мелких электронных приборах, в области безопасности и т.п. применяются микроконтроллеры Cortex-M.

Вообще все устройства ARM можно условно разбить на микроконтроллеры и Application Processor.

  • Микроконтроллеры отличаются наличием на кристалле Flash-памяти и рабочего ОЗУ. Применяются для задач относительно малой автоматизации.
  • Application Processor преимущественно пользуется внешней памятью — DDRAM и Flash. Мы их дальше будем называть просто — процессоры. Масштаб задач у них больше.

Долгое время одни и те же архитектуры ARM7, ARM9 использовались как для построения процессоров, так и микроконтроллеров. С появлением линейки Cortex произошло разделение, и теперь микроконтроллеры называются Cortex-M, а процессоры Cortex-A и Cortex-R.

Виды ОС


Какие есть варианты запуска ОС:

  • на микроконтроллерах обычно запущена маленькая ОС реального времени (RTOS) или просто программа без ОС;
  • на процессорах чаще запущена ОС общего применения (Linux, Android), иногда маленькая RTOS, иногда полнофункциональная RTOS (типа vxWORKS).

Например, в планшетах и смартфонах используется Android, iOS или вариант Linux. В телекоммуникационном оборудовании может быть Linux или один из вариантов RTOS. В более простом оборудовании может применяться RTOS или программа без ОС.

В дальнейшем мы будем говорить только о запуске ОС (Linux, Android) или RTOS на ARM. По способу запуска “большие” RTOS попадают в одну группу с Linux, а “малые” RTOS объединяются с программами без ОС.

Для запуска Linux хорошо подходят процессоры ARM9, ARM11, Cortex-A. Усеченную версию Linux также можно загрузить на ARM7, Cortex-M4 и Cortex-M7, но это нецелесообразно.

Для запуска малых RTOS подходят микроконтроллеры и процессоры ARM7, ARM9, Cortex-M. В некоторых случаях для RTOS используют начальные модели Cortex-A, например, Cortex-A5. Большинство же процессоров Cortex-A столь сложны, что их возможности можно использовать только совместно с поставляемым производителем Linux/Android SDK, что и определяет выбор в пользу Linux.

Загрузчик ОС


С точки зрения разработчика системное ПО устройства делится на загрузчик и ОС. Основную функцию всегда выполняет программа, работающая под управлением ОС или RTOS.

Загрузчик обеспечивает загрузку ОС и сервисные функции, такие, как:

  • проверка целостности образа ОС перед запуском;
  • обновление программ;
  • сервисные функции, функции первоначальной инициализации устройства;
  • самотестирование.

В случае RTOS загрузчик зачастую пишется разработчиком устройства и представляет собой небольшую специализированную программу. В случае с ОС общего применения широко применяются загрузчики с открытым исходным кодом, например, u-boot.

Таким образом, с точки зрения разработчика изделия запуск ОС выглядит следующим образом:


Здесь знаком // отмечен момент подачи питания или сброса процессора. Такой простой способ запуска был у некоторых процессоров ARM7. В последовавших за ними версиях процесс запуска в реальности сложнее, чем на приведенной схеме, но для разработчика конечного решения это обычно не существенно.

Схема “Загрузчик-ОС” очень удобна из практических соображений, ведь загрузчик берет на себя всю низкоуровневую работу:

  • инициализирует память перед запуском ОС и загружает ядро ОС в память;
  • инициализирует часть периферии;
  • часто реализует хранение двух образов ОС: текущего и резервного, или образа для восстановления;
  • контролирует образ ОС перед загрузкой;
  • дает сервисный режим работы даже при испорченном образе ОС.

Например, для запуска Linux на ARM загрузчик должен инициализировать память, хотя бы один терминал, загрузить образ ядра и Device Tree в память и передать управление на ядро. Все это описано в <https://www.kernel.org/doc/Documentation/arm/Booting>. Код инициализации ядра Linux не будет делать сам то, что должен делать загрузчик.

В то же время, загрузчик зачастую слабо защищен или не защищен вовсе. В большинстве домашних роутеров достаточно открыть крышку и подключиться к разъему UART, чтобы войти в меню управления загрузчиком. В телекоммуникационном оборудовании более высокого класса вход в меню загрузчика зачастую возможен по недокументированной комбинации клавиш или нажатой кнопке в момент включения устройства. Иными словами, зачастую загрузчик не защищен от локального нарушителя.

Рассмотрим работу загрузчика на примере u-boot, загружающего Linux, по шагам.

  1. После включения или сброса процессор загружает образ u-boot, хранимый в Flash-памяти, в ОЗУ и передает управление на первую команду этого образа.
  2. u-boot инициализирует DDRAM.
  3. u-boot инициализирует драйверы загрузочного носителя (ЗН), например, eMMC, NAND Flash.
  4. u-boot читает с ЗН область переменных конфигураций. В конфигурации задан скрипт загрузки, который u-boot далее исполняет.
  5. u-boot выводит в консоль предложение прервать процесс загрузки и сконфигурировать устройство. Если за 2-3 секунды пользователь этого не сделает, запускается скрипт загрузки.
  6. Иногда скрипт начинается с поиска подходящего образа ОС для загрузки на всех доступных носителях. В других случаях ЗН задается в скрипте жестко.
  7. Скрипт загружает с ЗН в DDRAM образ ядра Linux (zImage), файл Device Tree с параметрами ядра (*.dtb).
  8. Дополнительно скрипт может загрузить в DDRAM образ initrd – маленькой файловой системы с необходимыми для старта драйверами устройств. Современные дистрибутивы Linux иногда используют initrd, а иногда – нет.
  9. Разместив загруженные 2 или 3 файла в памяти, скрипт передает управление на первую команду образа zImage (ядро Linux).
  10. zImage состоит из распаковщика и сжатого образа ядра. Распаковщик развертывает ядро в памяти, и загрузка ОС начинается.

Запуск загрузчика – предзагрузчик


Однако в реальности почти никогда не бывает, чтобы команды загрузчика выполнялись первыми после включения или сброса процессора. Это еще было на процессорах ARM7, но почти не встречалось далее.

Любое ядро процессора ARM при сбросе начинает исполнение с адреса 0, где записан вектор “reset”. Старые серии процессоров буквально начинали загружаться с внешней памяти, отображенной по нулевому адресу, и тогда первая команда процессора была командной загрузчика. Однако для такой загрузки подходит только параллельная NOR Flash или ROM. Эти типы памяти работают очень просто – при подаче адреса они выдают данные. Характерный пример параллельной NOR Flash – микросхема BIOS в персональных компьютерах.

В современных системах используются другие виды памяти, потому что они дешевле, а объем больше. Это NAND, eMMC, SPI/QSPI Flash. Эти типы памяти уже не работают по принципу: подал адрес — читаешь данные, а значит, для прямого исполнения команд из них не подходят. Даже для простого чтения тут требуется написать драйвер, и мы имеем проблему «курицы и яйца»: драйвер нужно откуда-то заранее загрузить.

По этой причине в современные процессоры ARM интегрировано ПЗУ с предзагрузчиком. ПЗУ отображено в памяти процессора на адрес 0, и именно с него начинает исполнение команд процессор.

В задачи предзагрузчика входят следующие:

  • определение конфигурации подключенных устройств;
  • определение загрузочного носителя (ЗН);
  • инициализация устройств и ЗН;
  • чтение загрузчика с ЗН;
  • передача управления загрузчику.

Конфигурация предзагрузчика обычно устанавливается одним из двух способов:

  • схемотехнически, подключением определенных выводов процессора к земле или шине питания;
  • записывается в однократно-программируемую память процессора на этапе производства.

В целом почти всегда есть возможность задать единственный вариант загрузки или основной и несколько альтернативных. При этом в числе альтернативных может быть первичная загрузка через USB или последовательный порт, что очень удобно при первоначальной инициализации на производстве.

Подобный предзагрузчик устанавливается как в процессорах ARM, таких, как Cortex-A, так и в микроконтроллерах, даже таких маленьких, как Cortex-M0. Вместе с предзагрузчиком процедура запуска ОС выглядит так:

Анализ угроз на этом этапе


Исходный код предзагрузчика пишется производителем процессора, а не компанией ARM, является частью микросхемы как продукта компании-производителя и защищен авторским правом. Например, в процессорах ARM компаний Atmel и NXP предзагрузчики написаны, соответственно, Atmel и NXP.

В некоторых случаях предзагрузчик можно прочитать из ROM и проанализировать, но иногда доступ к нему ограничен. Например, предзагрузчик процессора серии Psoc4000 компании Cypress был закрыт несколькими слоями защиты (но был взломан талантливым хакером).

Использования предзагрузчика в большинстве сценариев избежать нельзя. Можно рассматривать его как вариант BIOS, которого в ARM-системах нет.

Скрытый текст
Вообще-то, у ARMv8-A есть ARM Trusted Firmware, это системное ПО, отвечающее, например, за управление питанием (PSCI). Вот этот код можно считать BIOS для ARMv8. У ARMv7 и ранее такого стандартного ПО нет.

Сам по себе предзагрузчик в ROM несет в себе угрозу нарушения порядка загрузки и выполнения произвольного кода. Но после того как управление передано загрузчику ОС, предзагрузчик уже безвреден. Мы можем просто не передавать ему управление, перенастроить все обработчики прерываний и так далее.

В некоторые небольшие микроконтроллеры производители интегрируют в ROM-библиотеки для работы с периферийными устройствами, которые требуется вызывать на протяжении всей работы микроконтроллера. В этом случае системное ПО (загрузчик и ОС) само периодически передает управление куда-то в область предзагрузчика, и схема передачи управления получается следующей:

Это в общем случае небезопасно, но встречается только в некоторых микроконтроллерах на архитектуре ARM. На таких микроконтроллерах обычно запускаются программы без ОС или малые RTOS, и дизайнер системы может оценить риски.

Загрузка с TrustZone


В процессоры ARM Cortex-A и Cortex-R встраивается технология TrustZone. Эта технология позволяет на аппаратном уровне выделить два режима исполнения: Secure (Безопасный) и Non-Secure (Гостевой).

Эти процессоры в основном нацелены на рынок смартфонов и планшетных компьютеров, и TrustZone используется для создания в режиме Secure доверенной “песочницы” для исполнения кода, связанного с криптографией, DRM, хранением пользовательских данных.

В режиме Secure при этом запускается специальная ОС, называемая в общем случае TEE (Trusted Execution Environment, доверенная среда исполнения), а нормальная ОС, такая, как Linux, Android, iOS, запускается в режиме Non-Secure. При этом права доступа к некоторым устройствам ограничены для нормальной ОС, поэтому ее еще называют гостевой ОС.

Из-за наложенных ограничений гостевая ОС вынуждена время от времени вызывать функции TEE для исполнения некоторых операций. TEE продолжает существовать параллельно с гостевой ОС все время, и гостевая ОС не может ничего с этим поделать.

Например, гостевая ОС использует функции TEE для:

  • включения и выключения ядер процессора (в ARMv8-A это происходит через PSCI — часть ARM Trusted Firmware, а в ARMv7 — по-разному для каждого производителя процессоров);
  • хранения ключей, данных банковских карт и т.п.;
  • хранения ключей полнодискового шифрования;
  • операций с криптографией;
  • отображения DRM-контента.

При этом, с точки зрения безопасности, на время таких вызовов управление передается в неизвестный нам, непроверенный код. Мы не можем однозначно сказать, что делают Samsung KNOX или QSEE от Qualcomm.

Почему же разработчики систем соглашаются на такой режим функционирования? В процессоры с поддержкой TrustZone встроен и механизм Secure Boot в том или ином виде.

С Secure Boot предзагрузчик проверяет подпись загружаемого образа с помощью прошитого на этапе производства открытого ключа. Таким образом, гарантируется, что загружен будет только подписанный образ. Это функция безопасности.

То есть загрузка ОС становится следующей:

  1. стартует предзагрузчик в ROM. Он загружает ключи для проверки подписи TEE из ROM;
  2. предзагрузчик загружает в память образ TEE, проверяет подпись. Если проверка прошла успешно, запускается TEE;
  3. TEE настраивает режимы Secure и Non-Secure. Далее TEE загружает основной загрузчик ОС и переходит на него в режиме Non-Secure. Сам TEE остается в режиме Secure и ждет;
  4. загрузчик основной ОС загружает ОС как обычно;
  5. ОС вынуждена время от времени вызывать функции TEE для выполнения некоторых задач.

Однако производитель, как правило, поставляет подписанные образы загрузчика и TEE в составе SDK для процессора и поставляет процессоры, уже “зашитые” ключом производителя. В этом случае предзагрузчик из ROM не станет выполнять любой загрузчик, если он не подписан производителем. Все основные процессоры для смартфонов сейчас поставляются уже “прошитыми” под исполнение собственного TEE перед исполнением загрузчика ОС.

Далее действует лень — c TEE все работает, а без TEE даже не запускается. Разработчики используют SDK с TEE, вызывают закрытый бинарный код из ядра Linux и не волнуются.

Как проверить свой проект на обращения к TrustZone


Может даже показаться, что всей этой TrustZone не существует, по крайней мере, в вашей конкретной разработке. Проверить это совсем несложно.

Дело в том, что все процессоры с TrustZone стартуют в режиме Secure, а только потом переключаются в Normal. Если ваша ОС запущена в режиме Normal, то какая-то Secure OS (TEE) существует в системе и перевела ее в этот режим.

Лакмусовой бумажкой является обращение к TEE для включения кэш-памяти 2-го уровня. По какой-то причине архитектура ARM не позволяет этого делать из Normal World. Поэтому для включения кэша ядру ОС потребуется сделать хоть один вызов к TrustZone. Делается это единственной командой: smc #0, и вы можете поискать ее сами в ядре Linux или Android.

Разумеется, мы и сами поискали, и нашли такие вызовы в коде поддержки ряда процессоров Qualcomm, Samsung, Mediatek, Rockchip, Spreadtrum, HiSilicon, Broadcom, Cavium.

Загрузка ARM Cortex-A и анализ угроз


Итак, обещанный процесс загрузки ОС на ARM (здесь — Cortex-A) в четыре блока:

На схеме пунктиром обозначен путь обращения из ядра ОС в TEE.

В двух блоках — неизвестный нам код. Посмотрим, чем это грозит.

Технически, любой из компонентов системного ПО может содержать ошибки, намеренные закладки и так далее. Однако в большинстве случаев загрузчик, ОС и системное ПО можно проверить, изучив исходные коды. Сконцентрируемся на возможных угрозах, исходящих от предзагрузчика и TEE, исходные коды к которым закрыты.

Предзагрузчик работает на самом раннем этапе, когда схема подключения к процессору различных периферийных устройств еще не известна, никакие коммуникационные устройства (WiFi, 3G и т.д.) не настроены, коммуникационные протоколы не работают. При этом предзагрузчик – небольшая программа, с размером кода порядка нескольких десятков килобайт, и сложно представить размещение в нем полных стеков протоколов или серьезной эвристики по определению подключенных устройств. Поэтому предзагрузчик вряд ли таит в себе серьезные закладки, связанные со слежкой, передачей данных и т.п.

Гораздо более интересной точкой атаки является TEE, так как его функции вызываются в процессе работы ОС, когда все периферийные устройства работают, а коммуникационные протоколы настроены. Создание шпионской закладки в коде TEE позволяет практически неограниченно следить за пользователем СВТ.

В небольшом исследовании мы показали реализуемость закладки в TEE, незаметно перехватывающей системные вызовы ОС Linux. Для активации закладки нужно только одно обращение из ядра Linux в TEE (например, то самое, для кэша второго уровня), после чего система становится полностью управляемой. Это позволяет:

  • контролировать чтение и запись файлов, модифицировать данные «на лету»;
  • перехватывать пользовательский ввод, причем введенные символы перехватываются даже с экранной клавиатуры;
  • незаметно внедрять свои данные при коммуникации с удаленными серверами, в том числе по протоколу https, маскируя передачу шпионской информации под обычный зашифрованный Web-трафик.

Несомненно, выявленные возможности – только вершина айсберга, и создание закладок не было целью исследования.

Выводы


Мы рассмотрели процесс загрузки различных микроконтроллеров и процессоров ARM.

У микроконтроллеров наиболее уязвимым местом в процессе загрузки является загрузчик ОС.

Современные процессоры ARM Cortex-A включают в себя TrustZone — и от этого никуда не уйти. TrustZone предполагает запуск перед ОС доверенной среды исполнения TEE.

TEE является самой уязвимой точкой в процессе загрузки ОС на ARM Cortex-A, потому что обращения к TEE приводят к выполнению закрытого системного кода, известного производителю, но скрытого от нас.

Без контроля над TEE невозможно обеспечить безопасность и доверенность исполнения любой ОС на ARM Cortex-A.

Комментарии (52)


  1. marsianin
    27.09.2017 11:25
    +1

    Странно, что Cortex серии R отнесены к application-процессорам. Они всё-таки realtime-процессоры. У них нет mmu, вместо него используется memory protection unit. Ну, кроме процессоров архитектуры ARMv8-R, в которых ожидается и mmu и mpu (но на них ещё и документация-то не опубликована). Поэтому запуск linux на них хотя и возможен, мне не представляется целесообразным. Эти процессоры скорее подходят для RT OS, собственно для этого они и предназначены.


    1. vlk77 Автор
      27.09.2017 11:32
      -2

      Да, абсолютно верно, просто на заданном уровне изложения это не сильно принципиально. Cortex-R достаточно редкий и на нем все равно запускаются довольно тяжелые ОС. Поэтому разбить на два класса проще.


      1. marsianin
        27.09.2017 11:40

        Статья мне в целом нравится, но это вопросы терминологии. Для тех, кто с этим работает, статья ничего нового и не расскажет. А для тех, кто не в теме, но интересуется, может внести некоторую путаницу. Поэтому, на мой взгляд, осторожней надо с терминами.


        1. nckma
          27.09.2017 16:34

          По вопросу терминологии, хорошо было бы в статью добавить какие-то жизненные примеры: в статье используются только термины «предзагрузчик», «загрузчик», а в реальной жизни мы знаем про u-boot, xloader, preloader или какой нибудь isw_handoff.
          Вот что из этого — что.


          1. vlk77 Автор
            27.09.2017 16:47
            -1

            Про u-boot там написано. u-boot — это в данной схеме загрузчик ОС. Туда же идет, например, barebox.
            Пример предзагрузчика: ну например, SAM-BA у Atmel Microchip. Это типичный ROM-код, который запускает внешнюю программу.


            1. mcu_by
              28.09.2017 12:04

              Может конечно я ошибаюсь, но когда sam-ba от atmel(microchip), стала предзагрузчиком? Для меня это по для прошивки, вы берете бинарь и льете его в ту область памяти которая Вам необходима (так же выбора интерфейса по которому льется бинарь), по поводу терминологии, вы сначала выставляете boot mode для камня, а уже по boot mode происходит переключение интерфейсов и после загрузчик первого уровня начинает, работать с тем boot mode который вы указали. Плюс sam-ba это спец пакет для заливки прошивок, так же все это можно делать и без sam-bы, можно же залить bare metal по через jtag. Такое по как sam-ba есть у многих производителей железа.


              1. vlk77 Автор
                28.09.2017 12:13

                Точно, ошибка вышла. SAM-BA называется только программа для PC. А загрузчик называется Boot Program (например, на AT91SAM9G20). Не очень выразительно, вот я и перепутал.
                Вот Boot Program — это как раз ROM предзагрузчик и есть.


      1. Wicron
        27.09.2017 16:23

        Запуск Linux без MMU? Это как?


        1. vlk77 Автор
          27.09.2017 16:52

          Есть uCLinux. Другой вопрос — зачем? Но используется, например, в промавтоматике. Например, этот конвертер RS-Ethernet.



        1. gresolio
          27.09.2017 18:20

          Или так :)
          Linux on an 8-bit micro?


  1. marsianin
    27.09.2017 11:34
    +1

    По поводу того, что архитектура ARM не позволяет включить L2 cache из Non-secure State. Давайте всё-таки будем точны в терминах: ARM-архитектура вообще не описывает кэш в терминах L1, L2 и тому подобных. Соответственно, архитектура и не может запрещать или разрешать включать или отключать кэш определённого уровня, это делается в документе на конкретную реализацию процессора. Например, в Cortex-A75 L2 кэш может включаться совсем не так, как это происходит в Cortex-A35, к примеру. А возможны и процессоры совсем без кэша.


    1. vlk77 Автор
      27.09.2017 11:48

      В данном случае я говорил о процессорах Cortex-A, и эти процессоры без кеша представить сложно. Кеш-контроллеры, насколько я это видел на практике, предоставляются компанией ARM как компаньоны к их же ядрам. Вот, например, описание кеш-контроллера L2C-310, с которым довелось поработать: <infocenter.arm.com/help/topic/com.arm.doc.ddi0246h/DDI0246H_l2c310_r3p3_trm.pdf>. Вы можете ясно видеть, что он называется Level 2 Cache Controller. Вообще, архитектура кеша прописана в ARM, и в Cortex-A она сложная и красивая. Я бы сказал, сделана как надо. В приведенном документе Вы можете посмотреть раздел 2.3.5, там написано: «You can only write to the L2 Control Register with an access tagged as secure, to enable or disable the L2 cache». Это именно то, о чем я писал в статье.


      1. marsianin
        27.09.2017 11:51

        Ещё раз: это документ на реализацию конкретного IP, а не на архитектуру. Согласен, архитектура кэша в ARM красивая. Только описывается она в терминах Instruction cache и Data cache, а так же Point of Unification, Point of Coherency и Point of Persistency (для ARMv8.1-A), но ни как не L1 и L2.


        1. vlk77 Автор
          27.09.2017 12:04
          -1

          Ну давайте про архитектуру.

          Возьмем, например, Cortex-A9 Technical Reference Manual. Открываем пункт 1.1 (About the Cortex-A9 processor) и читаем, что там написано.

          Начинается абзац словами: «The Cortex-A9 processor is a high-performance, low-power, ARM macrocell with an L1 cache subsystem».

          Дальше по тексту: «The following figure shows a Cortex-A9 uniprocessor in a design with a PL390 Interrupt Controller and an L2C-310 L2 Cache Controller.»
          Вот вам и L1 cache и L2 cache и даже ссылка на контроллер кеша в описании архитектуры.

          Если этого мало, то посмотрите ARMv7-A and ARMv7-R Architecture reference manual (ARM DDI 0406C.c), раздел A.3.9.2.


          1. marsianin
            27.09.2017 12:09

            Cortex-A9 technical reference manual — это документ *на конкретную реализацию* процессора архитектуры ARMv7-A. Архитектурный документ называется «ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition». И если вы говорите об архитектуре, значит говорите именно в терминах этого документа.


            1. vlk77 Автор
              27.09.2017 12:13
              -1

              Ну давайте тогда вспомним, что статья обзорная.
              А что касается указанного вами документа, так посмотрите там раздел A.3.9.2.


      1. marsianin
        27.09.2017 12:05

        Если по существу, то Non-secure OS даже не должна специально дёргать TEE. TEE может на старте правильно настроить таймер и контроллер прерываний (он в ARM тоже няшка) и периодически получать управление. Non-secure OS даже не заметит (-:


        1. vlk77 Автор
          27.09.2017 12:15

          Согласен! Это может сработать, если ОС не будет полагаться на этот таймер.


          1. marsianin
            27.09.2017 12:17

            В ARMv8 есть Secure Timer, который недоступен для OS (-:


  1. mcu_by
    27.09.2017 12:19

    Спасибо за статью, но Вы не совсем корректно описали этапы загрузки MPU, см. free-electrons.com/doc/training/embedded-linux/embedded-linux-slides.pdf (стр. 95-97), процесс запуска может отличатся от камня к камня, плюс доверенная загрузка может быть когда у Вас на борту soc(cortex A + M), тогда у Вас 2 камня, MCU общается MPU с через внутр. адреса и шины, тогда cortex M проверяет легальность данных и после разрешает запуск, чем не вариант?


    1. vlk77 Автор
      27.09.2017 12:26

      Да, отличный вариант, если есть два ядра. При этом доверенная загрузка все равно должна опираться на аппаратный блок контроля загружаемого образа. Если MPU (я так понял, вы имеете в виду Cortex-M сопроцессор) будет загружен недоверенно, то и MCU будет загружен так же.

      Что касается этапов загрузки в этой презентации — вы имеете в виду этап с bootstrap? Да, он чаще всего присутствует в том или ином виде. Но собирается он обычно в составе основного загрузчика, не так ли? Например, для Sitara u-boot собирает MLO. То есть, это просто деление на два бинарных образа. Я просто не хочу загружать статью такими подробностями.

      Если это действительно интересно, напишу отдельно.


      1. mcu_by
        27.09.2017 12:42

        Да, будет не одновременный запуск, MCU-микроконтроллеры cortex M, MPU-микропроцессоры cortex A(для данного контекста). Да именно не было упоминания bootstrap (mlo, at91bootstrap и т.п.), bootstrap можно отдельно собирать, если производитель предоставляет исходники. Плюс с помощью soc можно контролировать работу cortex-A(linux) на каждом шаге работы, уйдя например от smp в сторону amp(openamp, mcapi и т.д.) можно гарантировать безопасность системы, тогда сложно будет нарушить работу системы в целом.


  1. Legion21
    27.09.2017 13:06
    +1

    Супер! Как раз недавно думал что тема ОС умерла, а тут неплохая статья! Спасибо автору!


  1. izzholtik
    27.09.2017 13:38
    +1

    А как телефоны показывают картинку «зарядка батареи» при подключении зарядного в отключенном состоянии? Что при этом загружается?


    1. vlk77 Автор
      27.09.2017 13:48

      Это скорее всего делает загрузчик ОС.
      То, что не предзагрузчик — понятно, потому что на этом этапе еще неизвестна подключенная периферия.
      А вот загрузчик уже может быть кастомизирован. Запускается, опрашивает статус батареи (по I2C, например) и подключено ли внешнее питание. Если батарея села и питание не подключено, то выключается или засыпает. Просыпается, когда включаем зарядное устройство, инициализирует видеоподсистему, рисует картинку и спит дальше. По таймеру через некоторое время гасит экран. В этом режиме он почти не потребляет энергии и не мешает заряду аккумулятора. То есть, такая простая программа.


      1. selenite
        27.09.2017 15:34
        +2

        Запускается boot-образ, в котором лежит ядро и init-скрипт. Ядро получает состояние телефона в режиме «не грузимся, только usb, только зарядка» и рисует картинку. на мтк, как вариант, стартует boot_logo_updater


        1. vlk77 Автор
          27.09.2017 16:42

          Можно и так и так, конечно. И сложно спорить с наличием той или иной реализации, в том числе в MTK.

          Если бы я реализовывал такую функцию, я бы делал на уровне загрузчика. Поясню.

          Если мы уже загрузили ядро Linux/android, оно спит с гораздо большими затратами энергии, потому что запускает разную периферию, потом частично отключается. Может банально не догрузиться. Потребует времени для старта. Я не слышал о таком варианте загрузки ядра, как «не грузимся, только зарядка», но наверное все можно сделать, адаптируя ядро под себя. Но это уже будет ближе к грязному хаку.

          В то же время, в загрузчике это сделать совсем не сложно. Если устраивает u-boot, то тут у вас есть и графика, и много драйверов, в т.ч. драйвер батареи, питания. Можно дописать свой драйвер контроля батареи и это не будет хаком, все в ложится в архитектуру. Далее, на уровне boot-скрипта можно запустить маленькую программу, которая покажет экран зарядки или внедрить этот код в свой вариант u-boot. В любом случае, это менее травматичный способ.


          1. saw_tooth
            27.09.2017 19:35

            Ну возможно вы правы, в плане сложностей оно может того не стоить.
            Просто мне нужен по суть MK, как числодробилка, а лучше с DSP модулем, внешней периферии посути минимум, несколько i2, i2s и дисплей не большой. Может Вы посоветуете что-то более верное?
            На данный момент ковыряю stm32F407, но уже вижу что его производительности совсем впритык.
            Но халявные чипы MT 6xxx конечно очень прельщают)


            1. vlk77 Автор
              27.09.2017 20:05

              Если stm32F407 впритык, то посоветовать сложно. Типовой ARM926 может будет быстрее вашей STM-ки всего в пару раз. На перспективу развития проекта может хватить или не хватить.
              С другой стороны, есть большой выбор из Cortex-A с частотой 0.5-1ГГц и даже с несколькими ядрами, но без Linux там будет сложно запускаться.
              Даже ARM926 — это уже ненужное усложнение проекта, если он живет нормально на STM32. Сейчас есть выбор из MCU на Cortex-M7, может туда посмотреть?


              1. saw_tooth
                27.09.2017 21:01

                В принципе могу, но мне показалось они не слишком выше M4 (168 против 216мгц, возможно сама архитектура более шустрая)
                Периферии там правда… можно 5 прибором моих на одном МК сделать.


                1. hardegor
                  28.09.2017 00:53

                  Тогда только ждать STM32H7xx или i.MX RT1050



  1. sashz
    27.09.2017 17:02

    Усеченную версию Linux также можно загрузить на ARM7

    Полноценный линукс вполне себе комфортно жил на strongarmах.


  1. vlk77 Автор
    27.09.2017 17:12

    Linux можно запустить и на m68k. Жаль, нет у меня такого железа :)


  1. saw_tooth
    27.09.2017 18:36
    +1

    Очень интересная статья, спасибо, и назрел такой вот вопрос, возможно косвенный, но все же.
    А насколько возможно использовать в bare-metal, процессоры ARM которые скажем стояли в средствах коммуникации? Ну вот чисто потенциально…
    Ведь сами по себе они достаточно мощные, плюс aliexpress как кладезь в плане их доступности.
    Вот на данный момент имею:
    MT6235, MT6575, есть документация на само железо, есть u-boot — с чего так сказать начать, имею ввиду стратегически, ковырял u-boot но пока ничего интересного не нашел (по крайней мере то что вы описали)


    1. vlk77 Автор
      27.09.2017 18:53

      Спасибо за добрые слова!
      Под bare-metal вы подразумеваете использование без большой операционки? Нет ничего невозможного.
      Можно начать с u-boot, посмотреть, как он работает. Далее, с u-boot можно запустить свою маленькую RTOS или программу. Только, если запускаете RTOS, то надо пускать ее как Linux, командой bootz, а не bootm, в этом случае u-boot выключит MMU, cache и т.п.
      Потом, посмотрев на u-boot, можно загрузить свой софт и без него. Если повезет, бывают и bare-metal примеры программ, и порты RTOS на конкретный процессор.

      Если выбирать между перечисленными вами, я бы начал с MT6235, потому что он ARM926EJ-S. По своему опыту могу сказать, что эта архитектура для таких применений удобнее, там все значительно проще.
      Вообще Cortex-A не очень удобен для работы на голом железе, хотя мы и работаем на нем. Суммарный объем документации раз в 10-20 больше, чем у ARM926. Усилий по включению разных блоков, шин, настройке PLL, частот, MMU, кешей, питания, сброса будет больше.


  1. Disasm
    27.09.2017 18:57

    При этом предзагрузчик – небольшая программа, с размером кода порядка нескольких десятков килобайт, и сложно представить размещение в нем полных стеков протоколов или серьезной эвристики по определению подключенных устройств

    Это вы зря так. Raspberry Pi 3, например, позволяет загружаться по сети с помощью Ethernet-адаптера. Подозреваю, что это как раз средствами предзагрузчика и делается.


    1. vlk77 Автор
      27.09.2017 19:11

      Да, в Raspberry Pi 3 это делается средствами предзагрузчика. Там загрузка по Ethernet, протоколы TFTP и DHCP, оба не слишком тяжелые. Вполне можно написать код под ARM, вмещающий эти протоколы и LwIP или uIP, который влезет и в 64k.

      Вот когда Raspberry Pi будет из предзагрузчика грузиться по WiFi — это будет сложный стек протоколов…


      1. Disasm
        27.09.2017 19:19

        Для большинства простых задач вовсе не нужно реализовывать весь стек протоколов, достаточно научиться парсить пакеты нужного типа и отправлять/принимать их с помощью устройства. Так что, подозреваю, что и загрузку по WiFi можно было бы сделать. И вообще, 64к это очень много. Нужно потратить довольно много часов своей жизни чтобы написать столько низкоуровневого кода.


        1. vlk77 Автор
          27.09.2017 19:27

          То, что вы говорите про парсинг пакетов — это как раз работает хорошо с Ethernet.

          С WiFi нет однозначности. Разные сети, логин, пароль, частоты в разных странах. С Ethernet/DHCP/TFTP этого нет. Плюс криптография и довольно сложный протокол, все же.

          Насчет низкоуровневого кода — если не на ассемблере, то 64к довольно быстро забиваются.


          1. Disasm
            27.09.2017 20:09

            Ужасно, и как только людям удаётся умещать это всё в ESP8266 и всякие WiFi-шилды для Arduino? Нет там ничего сложного, ну будет у вас кода условно в два раза больше, чем для Ethernet, ну и всё. Та же крипта вообще уместилась в загрузчик на ESP, там даже поддержка SSL есть в каком-то виде.


            1. vlk77 Автор
              27.09.2017 22:42

              Ну что спорить… Я же и говорил, что предзагрузчик — простая программа.
              Кстати, минимальный размер Flash, с которой ESP8266 будет работать — 512к…

              А по поводу WiFi — в Raspberry разве процессор со встроенным радиомодулем? Нет? Тогда велкам поддерживать все варианты подключения внешнего радиомодуля и все варианты самих модулей. То есть — и варианты подключения по SDIO к разным портам, и всякие SPI. Посмотрите на вполне классический bcm4330, сколько там нужно подключений.
              Речь идет ведь о включении в масочный ROM, который должен подходить максимально для всех. Если это ESP8266, то там все понятно, из чипа торчит уже антенна. А если мы говорим о MPU/MCU общего применения, то это далеко не так.


              1. Disasm
                28.09.2017 10:46

                А по поводу WiFi — в Raspberry разве процессор со встроенным радиомодулем? Нет? Тогда велкам поддерживать все варианты подключения внешнего радиомодуля и все варианты самих модулей.

                И встроенного Ethernet-модуля на Raspberry Pi 3 тоже нет. Он подключен по USB, хоть и распаян на плате. Но это же не помешало реализовать поддержку этого модуля?


                Речь идет ведь о включении в масочный ROM, который должен подходить максимально для всех.

                Помимо масочного ROM есть и другие технологии ROM (EPROM тот же). И даже масочный ROM позволяет без особых затрат сделать вариант чипа с нужным содержимым ROM.


                1. vlk77 Автор
                  28.09.2017 11:14

                  Да, создатели малинки молодцы. Я не хочу даже с этим спорить. И не хочу провоцировать вас на спор.
                  Просто скажите, сколько примерно, по вашему опыту, стоит заказать у Broadcom чип типа BCM2837 с нужным содержимым масочного ROM? Просто у меня есть свой опыт в этом вопросе, хочу сравнить.


                  1. Disasm
                    28.09.2017 11:26

                    Не люблю вот этих смелых предположений, основанных ни на чём, так что не могу ответить на этот вопрос. Могу только сказать, что профит масочного ROM в том, что для изготовления вариации чипа нужно переделать одну (или несколько, для сложных тех.процессов) маску, что значительно дешевле переделывания всех масок. А для вариаций EPROM так вообще ничего переделывать не нужно (ну, кроме прошивки).


                    И да, про масочный ROM в BCM2837 сказали вы, а не я. Есть ли какие-нибудь подтверждения этому?


  1. yshurik
    27.09.2017 22:12

    Raspberry Pi оч интересно грузится:
    Сначала все выполняется на GPU (дадада)


    1. Stage 1 boot is in the on-chip ROM. Loads stage2 in the L2 cache!
    2. Stage 2 is bootcode.bin. Enables SDRAM and loads stage3
    3. Stage 3 is loader.bin. Knows about elf format and load start.elf
    4. start.elf loads kernel.img (also start.elf is the main gpu code).

    и вот только уже потом:


    5 kernel.img, is the first! thing that runs on the ARM processor. (у меня kernel.img это был еще и u-boot, а уже после него inferno/plan9)


    1. vlk77 Автор
      27.09.2017 22:45

      Спасибо, вот это ценно! Малинка огонь, рвет шаблоны…


    1. beeruser
      27.09.2017 23:45

      У вас информация уже устарела на 5 лет

      Prior to 19th October 2012, there was previously also a third stage bootloader (loader.bin) but this is no longer required.


  1. beeruser
    27.09.2017 23:51

    Эксплойт TrustZone

    USENIX Security '17 — CLKSCREW: Exposing the Perils of Security-Oblivious Energy Management www.youtube.com/watch?v=vI3GRCgThxE

    www.usenix.org/conference/usenixsecurity17/technical-sessions/presentation/tang

    CLKSCREW: Exposing the perils of security-oblivious energy management blog.acolyer.org/2017/09/21/clkscrew-exposing-the-perils-of-security-oblivious-energy-management

    Example attack: loading self-signed apps
    In section 5 of the paper the authors show how CLKSCREW can subvert RSA signature chain verification used in loading firmware images into Trustzone.


    1. vlk77 Автор
      28.09.2017 12:05

      Эту атаку можно использовать не только для TrustZone. Просто TrustZone в действительности довольно классно защищает доверенную зону и поэтому атаковать TrustZone — это очень эффектно. Еще уместно вспомнить Row Hammer.

      Вообще, это вопрос модели угроз и дефект дизайна устройства, а не TrustZone, как технологии. Как раз TZ позволяет ограничить доступ почти к любым устройствам из Normal World. Поэтому, будь в модели угроз устройства предположение о манипуляции рабочей точкой CPU (напряжение, частота), эта атака легко была бы предотвращена. То есть, закрываем от Normal World эту функциональность и делаем вызов TEE для этой операции. Linux/Android будет вызывать TEE для изменения режима, что при нормальном функционировании не будет влиять на производительность. А на момент, когда TEE что-то вычисляет, вызовы по изменению рабочей точки будут задерживаться или блокироваться. Можно еще ограничить частоту вызовов или выполнять их со случайной задержкой.

      Вот манипуляции с напряжением питания давно известны, и аппаратный блок хранения ключей, который есть в процессорах с TrustZone, скорее всего, их обнаружит и обнулит мастер-ключ, спасая остальные хранимые ключи, зашифрованные на мастер-ключе.

      Но в любом случае, все тайное становится явным, и в TrustZone не стоит хранить, конечно, самый главный ключ, который нельзя знать никому. Рано или поздно девайс подключат к внешним источникам питания, генераторам частоты, нагреют, охладят, просверлят отверстия, придумают еще одну атаку и достанут ключик. Поэтому наши регуляторы очень мудро ограничивают сроки жизни ключей в устройствах 1-3 годами, в зависимости от применения.


  1. ubobrov
    29.09.2017 12:24

    Андрей!
    Спасибо за статью! Наконец-то я осмыслил что собой являет TrustZone.