Trust zone android что это

Немного о ARM Security Extensions (aka ARM TrustZone)

Это будет обзорная статья, поэтому я не буду вдаваться в глухие технические дебри. Тем не менее технические детали будут присутствовать. Первая часть статьи будет посвящена вопросу зачем это всё вообще нужно, а вторая — как это работает в общих чертах. Если общество заинтересуется — следующая статья будет содержать больше технических деталей. Кому интересно — добро пожаловать под кат.

Хотя в названии и есть слово «security», эти расширения нужны не только реализации функций связанных с безопасностью в привычном понимании этого слова. Когда мы говорим «безопасность» мы обычно думаем о шифровании, ограничении доступа, TPM , безопасных хранилищах и тому подобных вещах. Security extensions позволяют реализовать эти все функции, но так же они используются для таких вещей, как например запуск и остановка процессорных ядер, перезагрузка системы и т.д.

Не совсем security функции

Все мы знаем что есть такая штука как BIOS (а теперь и EFI). Их задачей является первичная инициализация системы, загрузка OS, предоставление OS информации о системе, помощь OS в управлении компьютером.

Исторически сложилось так, что на ARM-based платформах нет (точнее не было) аналога BIOS. Обычно в ROM-коде присутствовал простенький загрузчик который находил и загружал полноценный загрузчик. Часто таким полноценным загрузчиком выступает u-boot. U-boot в свою очередь инициировал минимум периферии (например запускал контроллер DRAM), находил и запускал ядро linux. Ядро linux в свою очередь могло полностью вытереть из памяти u-boot, потому что он больше не нужен. И всё, после загрузки ядро было полностью предоставлено себе. Прямо в него (или в сбоку, в специальную структуру под названием device tree) была зашита вся информация о системе где оно работает. Все операции по управлению системой (например разгон процессора) ядру приходилось выполнять самому.

Это было неудобно по ряду разных причин. Например, производителям не хочется отдавать разработчикам ядра контроль над всякими «нежными» модулями типа кешей и шины. Или, например, процедура перезагрузки чипа требует каких-то хитрых манипуляций которые неудобно производить из кода ядра.

Короче, потребность в чем-то вроде BIOS существует. Только в ARM-системах он называет Secure Monitor и работает благодаря Security Extensions. По моему опыту чаще всего он используется для запуска и остановки дополнительных ядер в многопроцессорной системе, управления шиной, переброски выполняющегося кода между мощными и слабыми ядрами в ахритектуре big.LITTLE, активации режима гипервизора и тому подобных деликатных вещей.

Security функции

Security extensions позволяют запустить сбоку ещё одну OS со своим ядром и пользовательскими приложениями. Её обычно называют Trusted OS. У этой OS будет своя защищенная память и доступ к защищенной же периферии, например крипто-аккселераторам. Двумя примерами таких OS является гугловая Trusty и опенсурсная OP-TEE. Существует консорциум Global Platform который выработал общие требования к таким OS под названием Trusted Execution Environment.

В защищенную OS можно загружать пользовательские приложения (естественно подписанные). Это позволяет например гонять там банковское приложение для платежей через NFC или виртуальную SIM-карту. Правда, скажу честно, на практике я с таким пока не сталкивался.

Так же secure extensions позволяют организовать Secure Boot: корнем доверия является ROM-код, который проверяет подпись следующего загрузчика, загрузчик может проверить подпись ядра обратившись к Secure Monitor. Ядро в свою очередь тоже может проверять подписи исполняемого кода. Таким образом основные компоненты будут защищены от изменения.Технология спорная, но некоторые разработчики android-устройств её очень любят.

О том как это всё работает

Информация о технических деталях открыта. Можно скачать ARM Technical Reference Manual и прочитать всё самому. Кроме того в Сети хватает всяких красивых презентаций. Я не буду вдаваться в глубокие дебри, просто опишу основные идеи. Я буду использовать терминологию из ARMv8, потому что она более последовательна, в сравнении с терминами принятыми в ARMv7.

Режимы процессора

В этом разделе будет приведено немного технических деталей. Если системное программирование для вас абсолютно темная тема — этот раздел можно и пропустить.

Собственно, все наверное слышали про кольца защиты в x86. В ARM-ах есть точно такая же штука, только тут они называются Exception Levels (сокращенно EL). Их может быть от двух до четырех (или шести, смотря как считать). Это режимы работы процессора. Чем выше Exception Level, тем большими привилегиями обладает код исполняющийся в нем.

Все ядра ARMv7 или ARMv8 поддерживают минимум два из них: EL0 и EL1.

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

На EL1 работают ядро и драйвера. У них, соответственно, есть возможность работать с периферией, программировать MMU и далее по списку. Еще лет 10 назад этого было бы вполне достаточно. Но технологии не стоят на месте.

Ещё два EL могут появится если процессорное ядро включает дополнительные расширения: virtualization extensions и security extensions. При чем оба эти расширения опциональны (хотя и присутствуют во всех современных чипах) и процессор может иметь или любое из них, либо оба сразу. Немалая часть ARM Technical Reference Manual посвящена взаимодействию этих расширений.

Так, если если в ядре есть virtualization extensions, то появляется EL2. На этом уровне работает гипервизор. Так же MMU становится двухстадийным и появляется виртуальный контроллер прерываний. Но это совсем другая история которую нужно рассказывать отдельно.

Если в процессоре присутствуют security extensions, то появляется режим EL3 и режимы S-EL0 и S-EL1. Кроме того появляется понятие secure mode (и соответственно non-secure mode) — это режимы процессора ортогональные exception levels. EL3 обладает теми же привилегиями что и EL2, плюс ещё одной особенностью. Только в режиме EL3 код может переключить процессор между secure и non-secure mode. В чем же между ними разница? А разница только в значении одного бита — NS .

Дело в том, что security extensions — это не только расширения для вычислительного ядра. Эти расширения так же затрагивают MMU, контроллер прерываний и контроллер шины. Например, контролер шины проверяет значение этого самого бита NS при доступе к памяти и периферии. Если участок памяти помечен как secure, то доступ к нему можно получить только из secure mode. Это значит, что ни ядро обычной OS, ни гипервизор не смогут прочитать/изменить «безопасную» память. То же самое и с периферией. Если приходит прерывание которое помечено как secure, то это прерывание будет обрабатывать не обычная OS, а trusted OS из режима secure mode.

Получается, будто в одном процессоре живут два мира: normal world и secure world (это термины из официальной документации, если что). При чем secure world может вмешиваться в дела normal world, но не наоборот. В режиме EL3 работает secure monitor, который служит эдаким Хароном — позволяет попасть из одного мира в другой.

Читайте также:  Универсальный мультимедийный блок для потоковой передачи android q link pro

Как вы уже наверное догадались, режимы S-EL0 и S-EL1 — это аналоги EL0 и EL1 из normal world. В S-EL1 бегает ядро «безопасной» OS, а в S-EL0 — приложения (например та же виртуальная SIM-карта). На картинке выше используется терминология из ARMv7, но понять что к чему довольно легко (я надеюсь).

Взаимодействие Normal World и Secure World

Процессор всегда запускается в secure-mode (потому что иначе он туда никак не попадет). Загружается и инициализируется Trusted OS, после чего процессор переходит в non-secure mode и загружает обычную OS.

Казалось бы при всех своих возможностях secure world должен занимать доминирующее положение в системе. Но на самом деле всё наоборот. Большую часть времени он неактивен. Только когда обычной OS нужны какие-то услуги, она обращается к secure monitor и управление переходит в secure world. Таким образом именно normal world решает когда будет работать secure world. Это сделано для того, что бы не мешать пользователю работать с устройством, смотреть видео и слушать музыку. Ведь если secure world заберет управление в неподходящий момент, то это может нарушить работу normal world.

На самом деле у secure world есть возможность получить управление в обход normal world, используя прерывания. Например можно завести таймер который будет периодически вызывать secure world. Но так обычно не делают по причинам обозначенным выше. User experience превыше всего.

Для ядра linux есть набор патчей который позволяет обычным приложениям взаимодействовать с приложениями работающими в Trusted OS, согласно спецификациям GlobalPlatform TEE, о которых я уже упоминал выше. Таким разработчики могут писать приложения которые (с незначительными изменениями) смогут работать на любой совместимой Trusted OS.

Уголок параноика

Может ли secure world следить за вами? Теоретически — да. На практике я имел доступ к проприетарным trusted OS, которые затем устанавливались на пользовательские устройства. В их коде я не видел таких функций. Что, конечно же, ни о чем не говорит.

Если на вашем устройстве активирован secure boot, то у вас проблемы. В том смысле, что вы ничего не сможете сделать с кодом бегающим в secure world. Правда, вы так же ничего не сможете сделать и с кодом бегающим в режиме ядра.

К счастью, SoC’и с включенным secure boot довольно редки и их стараются не продавать всем подряд. Поэтому у вас скорее есть возможность заменить trusted OS на свою. Правда, ещё остается ROM-код с которым вы не сможете сделать почти ничего. И если secure monitor зашит в ROM-код, то опять же у вас проблемы. Но, есть SoC’и, на которых вы сможете устанавливать свой secure monitor. Например — Renesas RCAR H3. Так что ещё не всё потеряно.

Для желающих поэкспериментировать — есть возможность поднять Trusted OS на Raspberry PI. Как это сделать — написано в документации к OP-TEE.

Источник

TrustZone: аппаратная реализация в ARMv7A

Сегодня начинаем исследовать внутреннее устройство TrustZone (это торговая марка компании ARM).

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

Поэтому я начну с того, что TrustZone — это никакое не «место» в процессоре. Ее нельзя найти на чипе, как кеш или АЛУ. И доверенные программы, на самом деле, не исполняются в какой-то физически выделенной зоне процессора.

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

В этой статье мы рассмотрим, как TrustZone реализуется на аппаратном уровне процессоров ARM Cortex-A (ARMv7A).
В ARMv8A будет примерно то же самое, а вот в ARMv7M все совсем по-другому. В угоду маркетингу, там тоже есть TrustZone, но другая.

Режим

Первый компонент TrustZone — это режим процессора. Он задается битом NS (Non-Secure) в регистре SCR (Secure Configuration Register). Если NS=1, мы в режиме Non-Secure, если NS=0, мы в доверенном, то есть Secure-режиме.

Регистр SCR у Cortex-A5

Независимо от NS, остаются на своих местах все привычные режимы работы процессора. Cамые ходовые из них:

  • User — режим исполнения команды приложений;
  • Supervisor — режим работы ядра ОС;
  • IRQ — режим при обработке прерываний.

Благодаря NS у нас появляются Secure User, Non-Secure User, Secure Supervisor, Non-Secure Supervisor и так далее.

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

Более того, оказывается, что в нельзя взять и поменять значение бита NS ни в одном из обычных режимов работы процессора — это запрещено. Для смены значения NS предусмотрен церемониал с заходом процессора в отдельный режим Secure Monitor, который не принадлежит однозначно ни к Secure, ни к Non-Secure. Но об этом мы поговорим в следующей статье.

Получается, что NS раздваивает процессор, создает два неравноправных режима работы: Secure и Non-Secure. В каждом режиме, впрочем, есть все, что нужно для исполнения ОС и программ, просто привилегии по доступу к части функций CPU и периферии отличаются.

Режим, а не зона!

Продолжаем снимать завесы.

Доверенный режим исполнения программ — тот, где NS=0, все!

Нет никакого дополнительного конвейера команд, АЛУ, отдельной памяти программ, — ничего такого, что можно представить, услышав название TrustZone. Нет никакой границы этой зоны, команды нарушителей не стремятся «переползти» в доверенную зону, как вирусы сквозь клеточную мембрану.

В общем случае конвейер исполнял команды недоверенной программы (NS=1), а потом (бац!) произошло прерывание, процессор перешел в доверенный режим (NS=0) и тут же исполняет доверенный код.

На самом деле, технология TrustZone дает нам инструментарий, позволяющий предпринять ряд мер (разделить память доверенных и недоверенных программ, разделить доступ к периферии) для создания надежного барьера между Secure и Non-Secure. Но надежность этого барьера будет зависеть и от качества, и полноты реализации доверенного ПО.

Конец снятия завес.

Сигнал NS

Бит NS не просто указывает процессорному ядру, в каком режиме ему работать. Это еще и внешний сигнал, подключенный от процессора почти ко всей периферии.
Как это представить? В общем случае, мы представляем, что периферия к CPU подключена шинами адреса, данных и управления. NS входит в состав сигналов управления для тех процессоров, где TrustZone реализована. Таким образом, от CPU к устройству идут не просто команды Read, Write, а Secure Read, NonSecure Read, Secure Write, NonSecure Write.

Cortex-A чуть чаще, чем всегда поставляется как System On Chip (SoC), поэтому все эти шины скрыты от нас внутри чипа. Однако ряд SoC позволяют вывести сигнал NS наружу, на случай подключения внешней периферии, поддерживающей безопасный режим.

Какая периферия поддерживает Secure/NonSecure доступ? Например, это контроллер прерываний GIC — в ARM это периферийное устройство в составе SoC. В Secure-режиме он позволяет настроить доставку некоторых прерываний в режим Secure FIQ и запретить изменять эту настройку ПО из NonSecure-режима.

Вот что происходит при работе CPU с GIC: при записи регистра GIC в режиме Secure от CPU вместе с адресом регистра и данными идет сигнал NS=0. GIC понимает, что запись доверенная, и дает полный доступ. Если же NS=1, GIC ограничивает доступ к части регистров, как на запись, так и на чтение.

Читайте также:  Тока скул для андроид

Другие блоки процессора, поддерживающие сигнал NS: контроллеры памяти, часы реального времени (RTC), хранилище ключей, контроллер сброса и управления питанием.
Заметим, что в ARMv7A поддержка TrustZone опциональна, и при создании SoC опция Secure Extensions (читаем: TrustZone) может быть отключена. При этом из чипа удаляются ненужные блоки и связи, в частности, отпадает необходимость в трассировке линии NS по всему чипу. При этом входы NS периферийных устройств подключаются к 0 (по крайней мере, мы можем так это представить). Топология чипа становится проще.

Многопроцессорность

Рассмотрим внутренности работы современного ARM, чтобы понять, как будет работать TrustZone в этом случае.

В процессорах ARM все процессорные ядра, память и периферию соединяет внутренняя шина, которая называется AMBA (https://en.wikipedia.org/wiki/Advanced_Microcontroller_Bus_Architecture). Начиная примерно с ARMv4, в cоставе шины AMBA существует блок коммутации, он подключает блоки, называемые Bus Master, к различным Slave-устройствам.

Только реально крепкий орешек разберется в деталях работы AXI и AMBA, а ведь для полной картины нужно добавить AHB, APB и учесть детали реализации в разных архитектурах. Но общая идея улавливается очень быстро.

Например, процессорное ядро (а точнее, D-cache и I-cache этого процессора) — это Bus Master, а какой-нибудь I2C контроллер — это Slave. Bus Master начинает транзакцию по шине, то есть чтение или запись. Slave — это тот блок, куда пишут или откуда читают. Отсюда, кстати, следует и сам набор мастеров: ядра процессора, контроллеры DMA и периферия со встроенным DMA (как, например, USB host).

Блок коммутации Master Slave мы рассмотрим подробнее. В ARMv7A он называется Interconnect и является элементом реализации AXI (Advanced eXtensible Interface). В ARM926 этот блок имел говорящее название Bus Matrix и был частью реализации интерфейса внутренней шины AHB (Advanced High-Perfomance Bus). По сути — это то же самое.
У нас есть M×Master и N×Slave, и есть матрица коммутации, соединяющая первых со вторыми. В каждый момент времени каждый Master может быть подключен к одному Slave или отключен совсем. Но несколько Master могут быть одновременно активны, если подключены к разным устройствам.

В общем случае не все связи возможны. В частности, дизайнер системы может исключить ненужные связи — например, если нет причин для Ethernet-контроллера (Master), можно писать напрямую в I2C-контроллер (Slave).

Кроме того, некоторые устройства могут быть как Master, так и Slave. Например, USB Host, когда сохраняет данные через DMA в память — Master, а когда мы настраиваем его регистры— Slave.

При этом каждый Master является и источником сигнала NS, а Slave — реципиентом этого сигнала. AXI транслирует через Interconnect сигналы NS от Master к соответствующему Slave, и благодаря этому в SoC могут одновременно происходить как Secure-, так и NonSecure-транзакции.

Периферия

Теперь мы видим, как в ARM Cortex-A поддерживается одновременная работа на внутренней шине нескольких процессорных ядер и множества периферийных устройств, одновременно в режимах Secure и Non-Secure. Еще немного усложним?

При создании SoC разработчик берет блоки от ARM, блоки от сторонних производителей и блоки собственной разработки, соединяет их в единую систему.

От ARM берутся, в том числе

  • ядра процессоров, например, Cortex-A, Cortex-M4, или мультипроцессорная система целиком, например, Cortex-A9 MPCore;
  • контроллер прерываний GIC, например, PL390;
  • контроллер кеша, например, L2C-310.

Все они имеют поддержку TrustZone и внутри себя разделяют доступ по NS на доверенный и недоверенный.

Например, контроллер кеша знает, какие линейки были сохранены в доверенном режиме, а какие — в недоверенном, и будет производить соответствующие транзакции по AXI для сброса данных в физическую память.

Далее, многие блоки процессора покупаются у сторонних (надежных и известных) разработчиков, они одни и те же даже в процессорах разных производителей. Это, например, USB host, SDHC host. Другие блоки разработчик SoC использует во всех своих процессорах, почти не меняя. Это, например, Ethernet MAC, контроллеры I2C, UART, SPI.

Вот эти покупные и свои блоки могут не иметь поддержки TrustZone совсем. Это объяснимо — мы не можем представить, зачем нужно разделять доступ к UART между Secure и Non-Secure. Но вопрос интеграции таких устройств в TrustZone повисает в воздухе.

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

  • для Bus Master без поддержки TrustZone подставить верный NS-бит;
  • для Bus Slave обеспечить настройку и проверку прав доступа.

Доступ Bus Master без поддержки TrustZone

Посмотрим, что это значит для Bus Master на примере с видеоконтроллером, берущим данные из памяти и передающим их прямо в HDMI.

Мы хотим обеспечить пресловутый DRM: зашифрованный видеопоток будет поступать из Linux в Secure OS, там расшифровываться и отображаться на экране. Расшифрованные данные будут размещаться в области памяти, доступной только для Secure Read/Write, чтение этой области из Linux (Non-Secure) даст ошибку доступа. Таким образом, мы не дадим Linux скопировать расшифрованный поток. Видеоадаптер с правом Secure-доступа будет беспрепятственно читать расшифрованные видеоданные и отображать на экран.

Чтобы видеоадаптер мог через AXI получать данные из Secure-памяти, он должен осуществлять доступ с NS=0. Однако если DRM нам противен не нужен, предоставлять видеоконтроллеру привилегированный доступ мы можем и не захотеть.

Чтобы контроллер работал так и этак, в системе вводится настройка: тип доступа для каждого Bus Master, не поддерживающего TrustZone. То есть минимум 1 бит на каждый Bus Master. Возможно, это просто один регистр — но это работа для создателя SoC, его ответственность. И это, конечно, источник несовместимости между процессорами разных производителей.

Доступ Bus Slave без поддержки TrustZone

Для каждого устройства Slave разумно будет определить следующие права доступа при работе с AXI:

  • разрешен ли доступ Secure Read;
  • разрешен ли доступ Secure Write;
  • разрешен ли доступ Non-Secure Read;
  • разрешен ли доступ Non-Secure Write.

Этот набор вытекает из суперпозиции операций Read/Write и режимов Secure/Non-Secure.
На самом деле, как делить в данном случае права, решает производитель SoC самостоятельно. Например, можно уменьшить количество настроек, разрешив Secure-доступ всегда. А можно и увеличить, добавив разбиение по типам доступа User/Supervisor.

Для такого контроля доступа можно под каждый Bus Slave предусмотреть регистр с 2-4-8 битами, разрешающими или запрещающими доступ к устройству в зависимости от режима доступа.

И тут мы подошли к еще одной теме: а что будет, если Bus Master доступ начал, а Bus Slave его не разрешил?

Ошибка доступа

Если есть ограничение, то будет и нарушение. Если какой-то тип доступа к устройству запрещен, что-то должно произойти, если его осуществить.

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

А что если мы используем менее хитрые интеллектуальные устройства? Тогда, например, при Non-Secure записи в Secure область памяти произойдет Abort. Abort — это тип исключения ARM, возникающий при невозможности доступа к какому-то устройству или области памяти.

Читайте также:  Zte nubia z17 mini как обновить андроид

Чаще всего будет происходить Asynchronous Data Abort, или по-русски, асинхронный аборт. Не стоит обсуждать это за ланчем.

Data Abort — потому что он произошел при чтении/записи данных, а не инструкций процессора. Асинхронный он потому, что происходит не сразу в момент ошибки, а через некоторое время после нее. И вот с этого места будет еще подробнее.
Вообще, при нарушении доступа может произойти как синхронный, так и асинхронный аборт.

Например, когда Linux загружает приложение, он может загрузить его не целиком, разместив при этом только часть страниц в физической памяти, а остальные настроить на генерацию Abort в момент доступа. Приложение запустится, и когда дело дойдет до незагруженной в физическую память страницы, произойдет синхронный аборт. Он синхронный потому, что произойдет ровно на той инструкции, которая совершила обращение к памяти. Когда процессор попадет в режим Abort, Linux подгрузит нужную страницу памяти и вернет управление на ту же инструкцию, которая вызвала Abort. Результат — программа продолжит работать «как не бывало».

Но в случае с TrustZone все не так гладко. Некоторые процессоры будут генерировать синхронные исключения, но большинство — будут генерировать для большинства ошибок доступа асинхронный Abort.

Ответим себе на два вопроса:

  • Почему происходит именно асинхронный аборт?
  • Чем это плохо?

Почему асинхронный?

Начнем с того, что ARMv7A — архитектура с конвейером команд, где инструкции заранее разбиваются процессором и выполняются не строго последовательно. Исполнение части инструкций может происходить параллельно с другими. Например:

Здесь первая команда сохраняет r1 по адресу r2, а вторая увеличивает r2. После исполнения первой команды, в общем случае, сохранение в память только начнется, и возможно, еще не закончится, когда будет выполнена полностью вторая инструкция.

Далее, у процессора есть cache, в котором записанная ячейка застрянет на неопределенное количество времени, и ошибка доступа потенциально произойдет только в момент синхронизации кеша с памятью.

Потом, даже если область памяти не кешируется: память в ARM делится на Normal, Strongly Ordered и Device Memory, допуская разные вольности со стороны процессора по изменению порядка реальных обращений к памяти и устройствам через AXI. В результате, транзакция через AXI может произойти не сразу из-за того, что доступ к устройству занят другим обращением.

Ну и наконец, если доступ к обычному Bus Slave вызвал Abort, то это будет внешний по отношению к процессорному ядру логический сигнал. Ядро никак не ожидает, что этот сигнал синхронизирован с тем, что происходит сейчас в конвейере команд, и это абсолютно справедливо: ядро даже не может на 100% определить причину такого аборта.

При любом из этих обстоятельств ARM сгенерирует Asynchronous Abort, сообщая нам, что попытка запрещенного доступа была, но, сколько тактов или инструкций назад — он не знает.

Чем плох Asynchronous Abort?

Да тем, что мы не можем определить точку сбоя и не можем ничего исправить. Программа после ошибочного доступа может выполняться не один десяток тактов и за это время удалиться настолько далеко от правильного функционирования, что ее останется только остановить и перезапустить. Возможно, и с полным сбросом процессора, если от работы программы после Abort пострадает какая-нибудь периферия или внутренние структуры ОС.

… и какой из этого можно сделать вывод

При работе с TrustZone поначалу возникает соблазн использовать эту технологию как технологию аппаратной виртуализации. Но из-за Asynchronous Abort это не получится сделать.

Действительно, есть два режима: Secure и Non-Secure. Режим Secure может создать для Non-Secure аналог песочницы, ограничить доступ к периферии.

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

Как бы хотелось:

  • гостевая ОС обращается к устройству, происходит Abort (синхронный);
  • гипервизор понимает, что произошло;
  • гипервизор эмулирует ожидаемую гостевой ОС работу устройства;
  • гипервизор возвращает управление гостевой ОС, та продолжает работать, как ни в чем ни бывало.

А вот, как получится:

  • гостевая ОС обращается к устройству, создаются условия для Асинхронного аборта;
  • гостевая ОС продолжает работать, не подозревая об этом;
  • внезапно для всех Abort генерируется системой;
  • гипервизор понимает, что Abort — асинхронный, и он не может вычислить, из-за какой инструкции это произошло, по какому адресу и к какому устройству был доступ;
  • гипервизор прекращает работу гостевой ОС.

Вывод: технологию TrustZone нельзя саму по себе использовать для аппаратной виртуализации.

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

А память-то, память?

А что обстоит с доступом к обычной памяти? Можно ли выделить для Secure-доступа часть системной DDRAM?

ARM позаботилась об этом меньше, чем можно ожидать!

Контроллеры памяти бывают разные, например,

  • контроллер статической памяти, SRAM, часто это внутренняя память SoC;
  • контроллер динамической памяти, например, DDR3;
  • универсальный контроллер доступа к параллельной памяти, может использоваться для SRAM, NOR Flash.

Все эти контроллеры — типичные Bus Slave. ARM их не разрабатывает, поэтому разграничение доступа Secure/Non-Secure ложится на плечи разработчика SoC, по приведенной выше схеме.

Самый базовый вариант есть почти всегда — доступ к встроенной SRAM настраивается как Secure, а к DDR — как Non-Secure.

Это довольно безопасный способ, потому что все Secure-данные хранятся внутри чипа, не покидают его периметр. Но встроенная SRAM — это жалкие десятки или сотни килобайт, и этого может не хватить для полноценной Secure OS и защищаемых данных.

Более гибкий способ появляется, если производитель SoC по своему усмотрению реализовал контроллер DDR с поддержкой зонирования памяти по критерию NS=0/1. На самом деле, вариантов реализации может быть много, но это не меняет сути.

В целом, такая память предлагает минимум следующее:

  • Есть зоны с разными правами доступа, числом от 3.
  • Одну зону можно настроить как Non-Secure, там будет работать Linux или другая гостевая ОС. Это самая большая часть памяти.
  • Другую зону можно настроить как Secure, там будут данные Secure OS. Эта зона значительно меньше по размеру.
  • Третью зону настраиваем с доступом как Secure, так и Non-Secure. Она используется для обмена большими объемами данных между Linux и Secure OS, это всего несколько Мб.
  • Более гибкие настройки позволят сделать области Secure Write/Non-Secure Read и, наоборот, для однонаправленного обмена данными.

По счастью, производители действительно включают в свои SoC подобные контроллеры.

Жаль только, что ARM не позаботилась об этом, и мы имеем самые разные решения.

У этой реализации есть минус: поскольку обычная память программ и данных в ARM кешируется, а контроллер памяти — обычный Bus Slave, мы можем далеко не сразу узнать, что произошла запись по запрещенному адресу. Произойдет асинхронный Abort, и нам останется только убирать обломки программы.

Заключение

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

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

Можно сказать, что мы разобрались под капотом, но зажигание еще не включали. В следующей статье мы запустим процессор, рассмотрим его работу в режимах Secure, Non-Secure и переключение между ними через режим Secure Monitor.

Источник

Оцените статью