- PVSM.RU - https://www.pvsm.ru -

Портирование ОС на Aarch64

Портирование ОС на Aarch64 - 1 Aarch64 — это 64-битная архитектура от ARM (иногда её называют arm64). В этой статье я расскажу, чем она отличается от "обычных" (32-битных) ARM и насколько сложно портировать на него свою систему.

Эта статья — не детальный гайд, скорее обзор тех модулей системы, которые придётся переделать, и насколько сильно архитектура в целом отличается от обычных 32-битных ARM-ов; всё это по моему личному опыту портирования Embox [1] на эту архитектуру. Для непосредственного портирования конкретной системы так или иначе придётся разбираться с документацией, в конце статьи я оставил ссылки на некоторые документы, которые могут оказаться полезны.

На самом деле, различий больше, чем сходств, и Aarch64 — это скорее новая архитектура, чем 64-битное расширение привычных ARM. Предшественником Aarch64 во многом является Aarch32 (это расширение обычного 32-битного ARM), но так как у меня не было опыта работы с ним, писать о нём я и не буду :)

Далее в статье, если я пишу о "старом" или "прежнем" ARM, я имею ввиду 32-битный ARM (с набором команд ARM).

Кратко пройдусь по списку изменений по сравнению с 32-битным ARM, а затем разберу их поподробнее.

  • Регистры общего назначения стали в 2 раза шире (теперь они по 64 бита), и количество их удвоилось (т.е. теперь их не 16, а 32).
  • Отказ от концепции сопроцессорных регистров, теперь к ним можно обращаться просто по имени, например msr vbar_el1, x0 (против прежнего mcr p15, 0, %0, c1, c1, 2)
  • Новая модель MMU (со старой никак не связана, придётся писать заново).
  • Раньше было два уровня привилегий: пользовательский (соответствует режиму процессора USR) и системный (соответствует режимам SYS, IRQ, FIQ, ABT, ...), теперь всё одновременно проще и сложнее — режима теперь 4.
  • AdvSIMD пришёл на смену NEON, операции с плавающей точкой делаются через него же.

Теперь подробнее по пунктам.

Регистры и набор команд

Регистры общего назначения — r0-r30, при этом обращаться можно к ним как к 64-битным (x0-x30) или как к 32-битным (w0-w30, доступ к младшим 32 битам).

Набор инструкций для Aarch64 называется A64. Ознакомиться с описанием инструкций можно тут [2]. Базовые арифметические и некоторые другие команды на языке ассемблера остались прежними:

    mov w0, w1          /* Записать значение регистра w1 в w0 */
    add x0, x1, 13      /* Записать в x0 сумму x1 и числа 13 */
    b   label           /* "Прыгнуть" на метку "label"
    bl  label           /* "Прыгнуть" на метку "label", запомнив адрес возврата в x30 */
    ldr x3, [x1, 0]     /* Записать в x3 значение, на которое указывает x1 */
    str x3, [x0, 0]     /* Записать значение x3 по адресу, который лежит в x0 */

Теперь немного о различиях:

  • Появился специальный "zero"-регистр rzr/xzr/wzr, который равен нулю при чтении (можно применять запись в регистр, но результат вычисления не будет никуда записан).

subs xzr, x1, x2 /* Вычесть x1 и x2 и обновить флаги NZCV, сам результат вычитания никуда не записывается */

  • Нельзя складывать в стэк сразу много регистров (stmfd sp!, {r0-r3}), придётся делать это парами:

    stp   x0, x1, [sp, 16]!
    stp   x2, x3, [sp, 16]!

  • Регистр PC (Program counter, указатель на текущую выполняемую инструкцию) теперь не регистр общего назначения (раньше это был R15), следовательно, к нему нельзя обращаться обычными командами (mov, ldr), только через ret, bl и так далее.

  • Состояние программы теперь отображает не CPSR (этого регистра попросту нет), а регистры DAIF (содержит маску IRQ, FIQ и т.д., AIF — те самые биты A, I, F из CPSR), NZCV (биты negative, zero, carry, oVerflow — внезапно, те самые NZCV из CPSR) и System Control Register (SCTLR, для включения кэширования, MMU, endianness и так далее).

Вроде бы, этих команд достаточно, чтобы написать простенький загрузчик, который сможет передать управление в платформо-независимый код :)

Режимы исполнения и переключение между ними

Про режимы исполнения хорошо написано в Fundamentals of ARMv8-A [3], я здесь кратко перескажу суть этого документа.

В Aarch64 есть 4 уровня привилегий (Execution level, дальше сокращённо EL).

  • EL3 — Secure Monitor (предполагается, что на этом уровне исполняется прошивка)
  • EL2 — Гипервизор
  • EL1 — ОС
  • EL0 — Приложения

На 64-битной ОС можно выполнять и 32-битные, и 64-битные приложения; на 32-битной ОС можно выполнять только 32-битные приложения.

Портирование ОС на Aarch64 - 2

Переходы между EL совершаются либо при помощи исключений (системные вызовы, прерывания, ошибка доступа к памяти), либо при помощи команды возврата из исключения (eret).

Каждый EL имеет свои регистры SPSR, ELR, SP (т.е. это "banked registers").

Многие системные регистры также разделены по EL — например, регистр контекста MMU ttbr0 — есть ttbr0_el2, ttbr0_el1, и на соответствующем EL нужно осуществлять доступ к своему регистру. Это же относится к регистрам состояния программы — DAIF, NZCV, SCTLR, SPSR, ELR...

MMU

Armv8-A поддерживает MMU ARMv8.2 LPA, подробнее про это можно почитать в главе D5 ARM Architecture Reference Manual для Armv8, Armv8-A [4].

Если говорить коротко, то этот MMU поддерживает страницы по 4KiB (4 уровня таблиц виртуальной памяти), 16KiB (4 уровня) и 64KiB (3 уровня). На любом из промежуточных уровней можно задать блок памяти, таким образом указывая не на следующий уровень таблицы, а на целый кусок памяти такого размера, какой должна "покрывать" таблица следующего уровня. У меня есть давнишняя статья [5] про виртуальную память, там можно почитать про таблицы, уровни трансляции и вот это всё.

Из небольших изменений — от доменов (domain) отказались, зато добавили флажки вроде dirty bit.

В целом, кроме "блоков" вместо промежуточных табиц трансляции, особых концептуальных изменений не замечено, MMU как MMU.

Advanced SIMD

Есть существенные AdvSIMD отличия у старого NEON, как при работе с плавающей точкой, так и с векторными операциями (SIMD). Например, если раньше D0 состоял из S0 и S1, а Q0 — из D0 и D1, то теперь это не так: Q0 соответствует D0 и S0, для Q1 — D1 и S0 и так далее. При этом поддержка VFP/SIMD обязательна, по соглашению о вызовах теперь нет никакой программной передачи параметров (то, что раньше называлось "soft float ABI", в GCC — флаг -mfloat-abi=softfp), так что придётся реализовывать аппаратную поддержку плавающей точки.

Было 16 регистров по 128 бит:

Портирование ОС на Aarch64 - 3

Стало 32 регистра по 128 бит:

Портирование ОС на Aarch64 - 4

Подробнее про NEON можно почитать в этой статье [6], перечень доступных команд для Aarch64 можно найти тут [7].

Базовые операции с регистрами с плавающей точкой:

    fadd s0, s1, s2 /* s0 = s1 + s2 */
    fmul d0, d1, d2 /* d0 = d1 * d2 */

Базовые операции SIMD:

    /* Для примера, было: NEON, постфикс у команды */
    /* q0 = q1 + q2, каждый регистр -- вектор из 4 чисел с плавающей точкой */
    vadd.s32 q0, q1, q2

    /* Стало: AdvSIMD, постфиксы у регистров */
    /* v0 = v1 + v2, каждый регистр -- вектор из 4 чисел с плавающей точкой */
    add       v0.4s, v1.4s, v2.4s
    /* Сложить вектор v1 (в нём 2 64-битных числа) и записать в d1 */
    addv      d1, v1.ds
    /* Записать в каждый из 4 элементов вектора 0 */
    movi      v1.4s, 0x0

Платформы

QEMU

В QEMU есть поддержка Aarch64. Одна из платформ — virt, для того, чтобы она запускалась в 64-битном режиме, нужно дополнительно передать флаг -cpu cortex-a53, примерно так:

qemu-system-aarch64 -M virt -cpu cortex-a53 -kernel ./embox -m 1024 -nographic # ./embox -- ELF-образ ядра

Что приятно, для этой платформы используется куча периферии, драйвера для которой уже были в Embox — например PL011 для консоли, ARM Generic Interrupt Controller и т. д. Само собой, у этих устройств другие базовые адреса регистров и другие номера прерываний, но главное — код драйверов без изменений работает на новой архитектуре. При старте системы управление находится в EL1.

i.MX8

Из-за этой железки и было затеяно портирование на Aarch64 — i.MX8MQ Nitrogen8M.

Портирование ОС на Aarch64 - 5

В отличие от QEMU, u-boot передаёт управление образу в EL2, и, более того, зачем-то включает MMU (вся память мэпируется 1 к 1), что создаёт некоторые дополнительные проблемы при инициализации.

Embox уже поддерживал i.MX6, и, что хорошо, в i.MX8 часть периферии та же самая — например, UART и Ethernet, которые также заработали (пришлось подправить пару мест, где была жёсткая привязка к 32-битным адресам). С другой стороны, контроллер прерываний там другой — ARM GICv3, который достаточно сильно отличается от первой версии.

Заключение

На данный момент поддержка Aarch64 в Embox не полная, но минимальный функционал уже есть — прерывания, MMU, ввод-вывод через UART. Многое ещё предстоит доработать, но первые шаги было сделать проще, чем казалось с самого начала. Документации и статей заметно меньше, чем по ARM, но информации больше, чем достаточно, чтобы со всем разобраться.

В целом, если у вас есть опыт работы с ARM, портирование на Aarch64 — посильная задача. Хотя, как обычно, можно споткнуться на какой-нибудь мелочи :)

Скачать проект, чтобы потыркать его в QEMU, можно из нашего репозитория [1], если есть какие-то вопросы — пишите в комментах, или в рассылку [8], или в чат в Телеграме [9] (есть ещё канал [10]).

Полезные ссылки

P.S.

24-25 августа мы будем выступать на TechTrain [13], слушайте наши выступления раз [14] два [15] три [16], приходите к стенду — ответим на ваши вопросы :)

Автор: Денис Дерюгин

Источник [17]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/programmirovanie/326934

Ссылки в тексте:

[1] Embox: https://github.com/embox/embox/

[2] тут: https://developer.arm.com/architectures/learn-the-architecture/armv8-a-instruction-set-architecture

[3] Fundamentals of ARMv8-A: https://static.docs.arm.com/100878/0100/fundamentals_of_armv8_a_100878_0100_en.pdf

[4] ARM Architecture Reference Manual для Armv8, Armv8-A: https://static.docs.arm.com/ddi0487/ea/DDI0487E_a_armv8_arm.pdf

[5] давнишняя статья: https://habr.com/ru/company/embox/blog/256191/

[6] этой статье: https://habr.com/ru/company/embox/blog/418295/

[7] тут: https://www.element14.com/community/servlet/JiveServlet/previewBody/41836-102-1-229511/ARM.Reference_Manual.pdf

[8] рассылку: mailto:embox-devel@googlegroups.com

[9] в чат в Телеграме: https://t.me/embox_chat

[10] канал: https://t.me/embox_news

[11] Aarch64 ABI (соглашение о вызовах): http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf

[12] Migrating code from ARM to ARM64: https://blog.linuxplumbersconf.org/2014/ocw/system/presentations/2343/original/08%20-%20Migrating%20code%20from%20ARM%20to%20ARM64.pdf

[13] TechTrain: https://techtrain.ru/

[14] раз: https://techtrain.ru/2019/talks/3ugmbweakxi2tgfjvtfd1o/

[15] два: https://techtrain.ru/2019/talks/cvcndzoq1n46u0uucqfrz/

[16] три: https://techtrain.ru/2019/talks/1apgapgmijesokza2iallz/

[17] Источник: https://habr.com/ru/post/463417/?utm_source=habrahabr&utm_medium=rss&utm_campaign=463417