Статті та публікації

  1. режим
  2. Режим, а не зона!
  3. сигнал NS
  4. многопроцессорность
  5. периферія
  6. Доступ Bus Master без підтримки TrustZone
  7. Доступ Bus Slave без підтримки TrustZone
  8. помилка доступу
  9. Чому асинхронний?
  10. Чим поганий Asynchronous Abort?
  11. ... і який з цього можна зробити висновок
  12. А пам'ять-то, пам'ять?
  13. Висновок

Інтернет-портал Habrahabr.ru, жовтень 2017
Стаття Андрія Волкова, керівник напрямку довіреної платформи компанії "Аладдін Р.Д."

Сьогодні починаємо досліджувати внутрішній устрій 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
Регістр SCR у Cortex-A5

Незалежно від NS, залишаються на своїх місцях все звичні режими роботи процесора. Найдешевші ходові з них:

  • User - режим виконання команди додатків;
  • Supervisor - режим роботи ядра ОС;
  • IRQ - режим при обробці переривань.

Завдяки NS у нас з'являються Secure User, Non-Secure User, Secure Supervisor, Non-Secure Supervisor і так далі.

Назви User / Supervisor, наведені тут, використовуються для всіх 32-бітних ARM, до ARMv7 включно. У ARMv8 застосовуються інші позначення: EL0 / EL1 і PL0 / PL1. Суті це не змінює.

Біт 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.

Таким чином, від 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 (по крайней мере, ми можемо так це уявити). Топологія чіпа стає простіше.

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

Що відбувається, коли SoC містить кілька процесорних ядер? Кожне ядро ​​(зазвичай саме ядро ​​називається CPU в документації ARM) може працювати в режимах Secure або Non-Secure. У будь-який момент часу може виявитися, що одні ядра - Secure, а інші - ні.

А ще, ядра, які не підтримують TrustZone, можуть бути об'єднані з ядрами, що підтримують її, в одному SoC.

Розглянемо нутрощі роботи сучасного 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-доступу буде безперешкодно читати розшифровані віддання і відображати на екран.

Відеоадаптер з правом 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 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, що виникає при неможливості доступу до якогось пристрою або області пам'яті.

Найчастіше відбуватиметься Asynchronous Data Abort, або по-російськи, асинхронний аборт. Не варто обговорювати це за ланчем.

Data Abort - тому що він стався при читанні / запису даних, а не інструкцій процесора. Асинхронний він тому, що відбувається не відразу в момент помилки, а через деякий час після неї. І ось з цього місця буде ще докладніше. Взагалі, при порушенні доступу може статися як синхронний, так і асинхронний аборт.

Наприклад, коли Linux завантажує додаток, він може завантажити його не цілком, розмістивши при цьому тільки частина сторінок в фізичної пам'яті, а інші налаштувати на генерацію Abort в момент доступу. Додаток запуститься, і коли справа дійде до незавантаженої в фізичну пам'ять сторінки, відбудеться синхронний аборт. Він синхронний тому, що відбудеться рівно на тій інструкції, яка зробила звернення до пам'яті. Коли процесор потрапить в режим Abort, Linux довантажити потрібну сторінку пам'яті і поверне управління на ту ж інструкцію, яка викликала Abort. Результат - програма продовжить працювати "як не бувало".

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

В принципі, процесори ARMv7A, що мають одночасно Security Extensions і Virtualization Extensions, можна налаштувати на генерацію синхронних Abort. Це, наприклад, Cortex-A17, але основна маса ARMv7A (за кількістю вироблених чіпів) віртуалізації не має.

Відповімо собі на два питання:

  • Чому відбувається саме асинхронний аборт?
  • Чим це погано?

Чому асинхронний?

Почнемо з того, що ARMv7A - архітектура з конвеєром команд, де інструкції заздалегідь розбиваються процесором і виконуються не строго послідовно. Виконання частини інструкцій може відбуватися паралельно з іншими. наприклад:

STR r1, [r2] // * r2 = r1; ADD r2, r2, # 16 // r2 = r2 + 16;

Тут перша команда зберігає 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 і, навпаки, для односпрямованого обміну даними.

Більш гнучкі настройки дозволять зробити області Secure Write / Non-Secure Read і, навпаки, для односпрямованого обміну даними

На щастя, виробники дійсно включають в свої SoC подібні контролери.

Шкода тільки, що ARM не потурбувалася про це, і ми маємо найрізноманітніші рішення.

У цій реалізації є мінус: оскільки звичайна пам'ять програм і даних в ARM кешируєтся, а контролер пам'яті - звичайний Bus Slave, ми можемо далеко не відразу дізнатися, що сталася запис по забороненого адресою. Відбудеться асинхронний Abort, і нам залишиться тільки прибирати уламки програми.

Висновок

У цій статті ми розглянули апаратну реалізацію TrustZone в ARMv7A і розвіяли деякі помилки, пов'язані з цією технологією.

Розглянуто:

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

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

До речі, у нас зараз відкрита вакансія технічного письменника . Якщо Ви шукаєте роботу, і Вам цікава тема цієї статті - надсилайте резюме.

Чим поганий Asynchronous Abort?
Як це уявити?
Яка периферія підтримує Secure / NonSecure доступ?
Ще трохи ускладнити?
І тут ми підійшли до ще однієї теми: а що буде, якщо Bus Master доступ почав, а Bus Slave його відмовив?
А що якщо ми використовуємо менше хитрі інтелектуальні пристрої?
Чим це погано?
Чому асинхронний?
Чим поганий Asynchronous Abort?
А пам'ять-то, пам'ять?

Дополнительная информация

rss
Карта