Вышел Linux 7.2 — очень быстро. Проект Asahi Linux опубликовал очередной отчёт о прогрессе, и в этот раз новостей накопилось действительно много.

Думать иначе… снова

Инфраструктура управления питанием на платформе Apple Silicon устроена сложно. Ответственность распределена между несколькими аппаратными блоками — SMC, PMGR и PMP, о которых уже писали в предыдущих отчётах. Хотя поддержка этих блоков важна для энергопотребления, одним из главных препятствий на пути к улучшению автономности остаются сами вычислительные ядра.

Существует несколько способов «уснуть» для ядра CPU, и каждый применяется в своём контексте. Самый базовый способ погрузить в сон ядро ARM — инструкция Wait For Interrupt (WFI). Она говорит ядру прекратить выполнение до тех пор, пока его не разбудит прерывание. Такой режим экономит энергию за счёт остановки исполнения кода, но ядро остаётся включённым и сохраняет достаточно состояния, чтобы почти мгновенно возобновить работу. Поэтому WFI обычно используют для временной «парковки» ядра в работающей системе. Ядра Apple поддерживают «глубокий» режим WFI, который отключает больше компонентов ядра за счёт потери его состояния. Нижестоящий драйвер cpuidle в Asahi Linux настраивает именно этот режим WFI, предварительно сохраняя состояние ядра и запуская цикл WFI.

Специфичные для конкретного вендора особенности управления питанием — обычное дело. К счастью для мейнтейнеров ядра, есть стандартный способ с ними работать: Power State Coordination Interface (PSCI). PSCI определяет стандартный интерфейс, через который операционная система может вызывать определённый набор функций управления питанием CPU, реализованных прошивкой системы, в том числе для подготовки ядер ко сну.

Чтобы избежать разрастания специфичных для вендоров хаков внутри ядра Linux, мейнтейнеры архитектурного кода arm64 постановили, что всё аппаратное обеспечение, попадающее в апстрим, обязано использовать PSCI для управления питанием. Из-за этого специфичный для Apple драйвер cpuidle нельзя отправить в апстрим. Почему же он до сих пор используется?

PSCI определяет «каналы», через которые вызовы к прошивке диспетчеризуются из ядра. Сейчас в ядре поддерживаются два канала — инструкции SMC (Secure Monitor Call) и HVC (Hypervisor Call), которые передают выполнение более высокому уровню исключений (Exception Level). Ядро Linux рассчитывает работать на уровне EL2, а значит вызовы PSCI должны передавать выполнение прошивке, работающей на EL3. Но ядра Apple не реализуют EL3…

Ядро уже работает на EL2, а прошивки на EL3, к которой можно было бы обратиться, нет — здесь возникает тупик. Linux не может выполнить инструкцию SMC или HVC, поскольку нет EL3, куда передавалось бы выполнение, а значит PSCI недоступен. Возможность корректно управлять питанием ядер CPU критична для автономности и энергоэффективности, так что оставлять всё как есть не вариант. Быстрое и грязное решение — заставить m1n1 загружать ядро в EL1 и разместить реализацию PSCI на EL2. Теоретически это сработало бы, но сломало бы множество архитектурных возможностей, включая виртуализацию. Значит, нужно искать другой путь.

Если задуматься, m1n1 сам по себе почти как собственная прошивка для Apple Silicon. mBoot (в прошлом iBoot) запускает его на EL2, он делает свою работу, а затем передаёт управление любой полезной нагрузке, которая к нему прикреплена. m1n1 не резервирует память под себя и не содержит кода, который должен оставаться постоянно в памяти, так что его полезная нагрузка может свободно освобождать и перезаписывать эту память.

На рабочих системах Asahi Linux m1n1 загружает не ядро напрямую, а U-Boot. Это делается для того, чтобы использовать реализацию UEFI в U-Boot и дать дистрибутивам и пользователям возможность использовать любой стандартный UEFI-загрузчик (GRUB, systemd-boot и так далее). UEFI также предоставляет ещё одну возможность — Runtime Services. Подобно тому, как раньше работали прерывания BIOS, UEFI Runtime Services дают операционной системе способ обращаться к коду, происходящему из прошивки системы.

Если внимательно прочитать спецификацию PSCI, опубликованную Arm, можно заметить, что API там намеренно описан без привязки к конкретному каналу передачи — SMC и HVC приводятся лишь как примеры. При широком толковании из этого можно сделать вывод, что спецификация допускает и другие каналы…

Именно этим и занялся Свен (Sven) — реализацией канала PSCI на основе UEFI Runtime Service. Область памяти m1n1 будет выделена так же, как и у других компонентов прошивки, что позволит ядру обращаться к ней за сервисами PSCI, даже работая на том же уровне исключений. Свен уже модифицировал m1n1, чтобы тот резервировал свою память и оставлял после себя реализацию PSCI, а патчи для ядра, включающие эту возможность, уже отправлены в список рассылки в виде RFC!

Пожалуйста, перестаньте думать иначе

Учитывая, что ситуация с cpuidle долгое время не двигалась с места, можно предположить, что какое-то событие подстегнуло работу в этой области. И это действительно так.

Спецификация ARM требует, чтобы ядра в циклах WFI сохраняли всё своё состояние. На Apple Silicon это не режим по умолчанию. На чипах серий M1–M3 сохранение состояния можно настроить для каждого ядра отдельно с помощью так называемых chicken bits.

По ряду причин, не стоящих отдельного упоминания, начиная с серии M4 Apple теперь сама устанавливает chicken bits каждого ядра в mBoot, а затем блокирует регистры, управляющие ими. Это немного облегчает жизнь проекту — m1n1 теперь требуется чуть меньше работы, — но одновременно означает, что тонко настраивать низкоуровневое поведение CPU невозможно. Особенно остро это ощущается на M4: вызов WFI приводит к потере состояния ядра и краху всего, что на нём выполнялось.

Юрека (Yureka) заметила эту проблему во время работы над поддержкой M4 и добавила параметр командной строки ядра, делающий поведение цикла ожидания настраиваемым. Параметр позволяет указать ядру, как парковать ядра в цикле ожидания, в том числе с помощью простого цикла-заглушки без операций. Это предотвращает крах машин на M4 во время ранней инициализации ядра, до того как загрузится драйвер cpuidle. После того как драйвер вступает в дело, он сохраняет состояние перед вызовом WFI. Патчи для этой возможности уже находятся в linux-next.

Они не перестанут думать иначе

Apple очень серьёзно относится к своей репутации в области платформенной безопасности. Поэтому значительные инженерные усилия направлены на функции, которые делают эксплуатацию уязвимостей в их экосистеме практически невозможной для всех, кроме самых искусных атакующих. Одна из таких функций — Secure Page Table Monitor, или SPTM.

Традиционно ядро операционной системы напрямую отвечало за управление памятью. Это включает обработку выделения памяти, виртуальные отображения на физические адреса и управление MMU/IOMMU. Уязвимость в коде, отвечающем за эти операции, могла дать атакующему доступ ко всему адресному пространству платформы. Иначе говоря — полный провал.

Естественно, это делает код управления памятью очень частой целью атак, а поскольку его задача по своей природе небезопасна (приложениям может понадобиться произвольное выделение памяти буквально для чего угодно), корректно защитить его крайне сложно.

Много лет назад Apple добавила в XNU Page Protection Layer (PPL). PPL использует аппаратные функции безопасности Apple, чтобы изолировать управление таблицами страниц от остального ядра на аппаратном уровне. Это работало очень хорошо, однако со временем атакующие нашли способы обойти PPL и получить полный доступ к системе.

Похожие проблемы у Apple возникали и с IOMobileFramebuffer. Как упоминалось в предыдущих отчётах, Apple по большей части решила проблему IOMFB, поместив его внутрь прошивки DCP, за IOMMU. Ничто в пользовательском пространстве macOS и в самом XNU не может обратиться к нему напрямую, кроме как через определённый набор функций IPC. Взяв этот подход за основу, PPL со временем превратился в SPTM. SPTM берёт PPL и помещает его внутрь Guarded Execution Framework (GXF) Apple — набора уровней исключений, работающих параллельно стандартным уровням исключений ARM64. GXF также включает SPRR — систему прав доступа к таблицам страниц, используемую, когда CPU выполняет код на уровнях GL1 или GL2.

При запуске современного устройства на Apple Silicon iBoot или mBoot определяет, является ли настроенная полезная нагрузка загрузки образом XNU. Если да, сначала загружается SPTM на GL2. Там SPTM настраивает таблицы страниц и управление памятью и блокирует контроль над этими функциями за уровнем GL2. Затем запускается XNU, использующий отдельный протокол IPC для общения с SPTM. Если XNU не может успешно связаться с SPTM, система паникует на самом раннем этапе инициализации и останавливается.

Для проекта это создаёт проблему. Если попытаться запустить XNU под гипервизором m1n1, система рухнет, поскольку SPTM не загружен. Если настроить mBoot так, чтобы он воспринимал m1n1 как бинарник XNU, m1n1 сам рухнет, поскольку не сможет управлять памятью нужным ему способом.

Поскольку на M4 и более новых моделях SPTM обязателен для XNU, гипервизор на этих машинах был полностью неработоспособен. Но сдаваться в проекте не привыкли, поэтому это именно «был», а не «есть»!

Сам SPTM не представляет из себя ничего особенного. Это обычный бинарник Mach-O для ARM64, который лежит в той же директории preboot, что и специфичные для конкретной ОС блобы прошивки для различных сопроцессоров. Это значит, что теоретически m1n1 может загрузить его уже под гипервизором, затем загрузить рядом XNU и наблюдать за обоими! Но SPTM обязан запускаться внутри GL2 с включённым SPRR. Хорошо, что устройство работы обоих этих механизмов уже было изучено и описано ранее.

Благодаря работе Свена по реверс-инжинирингу SPRR и GXF ещё на самых ранних этапах существования Asahi Linux, недавно удалось научить гипервизор m1n1 эмулировать оба этих механизма! Это позволяет загружать блоб SPTM от Apple именно тем способом, которого ожидает XNU, провести небольшую «операцию» над бинарником XNU, загрузить его, а затем отслеживать обращения к MMIO точно так же, как это делалось на M1–M3! Подробности реализации Свеном этого механизма означают, что трассировка на новых машинах работает медленнее, однако не настолько, чтобы это мешало использованию. Это позволит продолжать работу над поддержкой нового оборудования и в дальнейшем!

Больше прогресса по M3!

Работа над портированием Asahi Linux на машины серии M3 также продвигается.

Процессор обработки изображений веб-камеры остался практически без изменений, за исключением одного пропускаемого сообщения инициализации, специфичного для M3 Max. chaos_princess добавила его поддержку в драйвер Linux, что обеспечило полную поддержку веб-камеры на всех устройствах серии M3 со встроенной камерой.

Встроенные микрофоны также немного изменились. На устройствах серии M3 появился новый децимиатор «High Frequency», требующий нового набора коэффициентов и намного более объёмного сообщения инициализации. И снова chaos_princess разобралась с этим в кратчайшие сроки, обеспечив поддержку микрофонов на всех подходящих устройствах серии M3.

ATCPHY — аппаратный блок, отвечающий за согласование подключений USB3, DisplayPort и Thunderbolt через порты USB Type-C — также немного изменился, потребовав новую последовательность настроек (tunables) при инициализации из-за перехода на техпроцесс TSMC N3.

Хотя тезис о том, что Apple будет избегать масштабных архитектурных изменений просто ради изменений, в основном подтвердился, без пары таких изменений всё же не обошлось…

Все устройства от M1 до базового M3 использовали специфичный для Apple контроллер порта USB от Texas Instruments под названием CD3217 (он же ACE2). Он сидит на шине I²C и ведёт переговоры с USB-устройствами при подключении. Начиная с M3 Pro/Max, Apple перешла на ACE3. ACE3 вместо этого использует шину SPMI, что потребовало дополнительного реверс-инжиниринга. Благодаря совместным усилиям mildsunrise и chaos_princess выяснилось, что ACE3 имеет практически тот же набор регистров, что и CD3217, только обёрнутый в интерфейс SPMI вместо адресации по I²C. Теперь и интерфейс SPMI, и сам ACE3 работают в Asahi Linux, обеспечивая поддержку USB 3.0 и Thunderbolt на всех устройствах серии M3.

Одним из ожидаемых крупных изменений был ABI прошивки как для GPU, так и для контроллера дисплея. Поскольку прошивки для AGX и DCP привязаны к конкретной версии macOS, Apple не приходится заботиться о стабильности интерфейса между релизами. Именно поэтому для каждого поколения оборудования выбирается «целевая» версия macOS. Машины серии M3 будут ориентироваться на ABI из macOS 14.8.3, и поддержка DCP теперь почти достигла паритета по функциональности с уже используемым для M1 и M2 ABI из macOS 13.5!

С учётом всего этого прогресса, дополнившего уже достигнутые для M3 рубежи, проект рад сообщить, что почти готов к выпуску официального релиза! Подробности об этом появятся в ближайшие недели, так что новости стоит отслеживать.

Не забыли и про M4 и M5!

Не ограничиваясь только M3, Юрека также занималась портированием на M4 и даже ранние версии M5. Помимо проблемы с WFI на M4, эти SoC страдали от критического изменения в прошивке контроллера NVMe Apple, появившегося в наборе прошивок macOS 15.x. Юрека и Свен совместно разобрали эти изменения и реализовали их как в m1n1, так и в Linux, так что теперь NVMe работает на M4 и M5! Юрека также добилась работы PCIe на уровне, достаточном для того, чтобы Linux мог перечислять устройства на шине, и исправила проблему, вызывавшую крах Linux сразу после загрузки при включении более одного ядра CPU. Пока на M4 и M5 работает не так уж много, так что включать их в Asahi Installer ещё не время, но новости на эту тему появятся, как и всегда, в своё время.

Видео на десктопе — задача не из простых

В прошлый раз было объявлено о предварительной поддержке Apple Video Decoder (AVD) — аппаратного блока, аппаратно ускоряющего декодирование видео в форматах H.264 (AVC), H.265 (HEVC) и VP9 на M1 и M2, а также AV1 на машинах серии M3 и новее. С тех пор sofus доработал поддержку AVD, и теперь AVC, HEVC и VP9 работают достаточно надёжно на всех устройствах, поддерживаемых Asahi Linux! Проект дошёл до этапа, когда встаёт вопрос об интеграции с десктопным окружением — а вот тут всё становится сложнее…

Аппаратный блок AVD фундаментально не хранит состояния: всё, что он делает — берёт закодированный кадр и превращает его в видеобуфер. Оборудование не занимается разбором битового потока, отслеживанием сессии декодирования и прочим управлением конвейером декодирования. Это отлично подходит для V4L2 Stateless API, разработанного именно для таких декодеров. Хотя этот API появился в апстриме ядра более 10 лет назад, пользовательское программное обеспечение медленно его осваивало за пределами специализированного софта для встраиваемых устройств. GStreamer имеет базовую поддержку V4L2 Stateless, однако FFmpeg (и всё, что его использует) — не имеет без сторонних патчей. Десктопное ПО, такое как веб-браузеры, исторически ориентировалось на VA-API, NVDEC и VDPAU. В последнее время фокус усилий на десктопе начал смещаться в сторону Vulkan Video. Из-за этого V4L2 Stateless фактически оказался заброшен для десктопного софта ещё до того, как получил там распространение.

Впрочем, не всё потеряно. VA-API сейчас почти универсален в десктопном ПО благодаря его использованию как AMD, так и Intel для их аппаратуры видеоускорения. Чтобы оборудование с V4L2 Stateless не оказывалось бесполезным для такого софта, компания Bootlin разработала слой трансляции VA-API в V4L2 Stateless. К сожалению, он давно оказался заброшен и уже не собирается без патчей, однако sofus сделал форк и привёл его в рабочее состояние для AVD. С установленным слоем трансляции и заданной переменной окружения для сессии входа программы, поддерживающие VA-API, теперь могут использовать AVD для ускорения декодирования видео! Это пока не поставляется по умолчанию в Fedora Asahi Remix и не работает с песочницей видеодекодирования Firefox, однако проект надеется скоро подготовить готовое к использованию решение.

Путь к прямому выводу (direct scanout)

Традиционное преимущество аппаратного ускорения декодирования видео — снижение нагрузки на CPU. Хотя это само по себе крайне важно, есть и другие места, где расходуются лишние энергия и время во время декодирования видео. Во-первых, CPU должен скопировать декодированные данные кадра в память GPU. Затем GPU должен скомпоновать этот кадр в сцену, как указывает компоситор. Наконец, итоговая отрисованная сцена должна быть скопирована в память контроллера дисплея, а контроллер дисплея настроен на её вывод. И всё это должно происходить с частотой кадров воспроизводимого видео.

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

Кадровые буферы бывают самых разных форматов и размеров. Видеоданные, например, почти всегда хранятся и передаются в полуплоскостном формате Y’CbCr, унаследованном от аналоговых стандартов хранения и вещания видео XX века. Помимо этого, графическое оборудование почти всегда использует какую-либо специализированную адресацию. Пиксели не располагаются в памяти «подряд», а вместо этого выкладываются тайлами для снижения количества промахов кэша и других проблем с производительностью. Более того, кадровые буферы часто ещё и сжимаются, что дополнительно снижает нагрузку на память и шину.

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

Может возникнуть вопрос: почему такие «общие» кадровые буферы не сделать просто с «сырыми» пиксельными данными? Рассмотрим кадровый буфер 1920×1080 с сырыми 8-битными пикселями ARGB. Это около 8 МиБ данных. При разрешении 4K это раздувается до почти 32 МиБ. Даже без дополнительной нагрузки от копирования, чтение и запись такого объёма данных с частотой 30, 60, 165 или даже 240 кадров в секунду создаёт колоссальную нагрузку на шину памяти и, соответственно, огромный расход энергии. Даже если бы существовала бесконечно производительная и эффективная шина памяти, каждому аппаратному блоку в цепочке всё равно потребовался бы достаточный локальный кэш или память вне шины, а также вычислительная мощность для обработки такого объёма данных. Это невозможно реализовать на практике.

К счастью, производители оборудования это понимают, и большинство «семейств» дисплейного оборудования решают эту проблему, реализуя поддержку одинаковых схем адресации и сжатия. Это позволяет их 3D-движкам, контроллерам дисплея и видеоускорителям совместно использовать эффективные по памяти кадровые буферы без необходимости в собственных огромных локальных кэшах или постоянной нагрузки на шину памяти. В Linux каждый драйвер объявляет поддерживаемые им форматы, а различные части программного стека затем согласовывают общий формат для совместно используемых буферов. К счастью, Apple решила не думать слишком уж иначе в этом вопросе. Ну, почти.

Оборудование Apple использует два основных формата адресации и сжатия: AGX, названный по имени GPU, и Interchange. Хотя формат AGX используется исключительно внутри самого AGX, формат Interchange также поддерживается DCP и AVD. В macOS это обеспечивает прямой вывод кадровых буферов как из AVD, так и из AGX, позволяя Quartz (компоситору macOS) в подходящих случаях практически ничего не делать в повседневных сценариях. Если воспроизводится видео и больше ничего не происходит, GPU может полностью выключиться, а компоситору остаётся лишь следить за тем, чтобы DCP отображал новые кадры по мере их генерации AVD. Если игра или приложение работает в полноэкранном режиме, компоситор может просто передать DCP адрес собственного кадрового буфера этого приложения и отключить всю свою отрисовку. Хорошая новость в том, что проект уже близок к тому, чтобы воспроизвести это поведение в Asahi Linux!

Благодаря основе, заложенной Алиссой и Линой, драйвер AGX уже содержал базовую поддержку формата кадровых буферов Interchange от Apple — её просто не подключили. Оливер Бестманн и автор этого отчёта взялись подключить и включить поддержку Interchange в драйвере ядра DCP, а также в драйверах Mesa Asahi и Honeykrisp. В сочетании с уже проделанной работой по добавлению поддержки Y’CbCr и оверлейных плоскостей в DCP это открыло дорогу к прямому выводу кадровых буферов, отрисованных AGX, непосредственно в DCP! Поддержка AVD на подходе — sofus сейчас изучает возможность вывода буферов в формате Interchange из AVD.

Хотя всё это отличные новости, поставлять их пользователям пока рановато. Kwin, компоситор для KDE Plasma, считает такую конфигурацию «мульти-GPU», поскольку AGX и DCP — отдельные аппаратные блоки. Из-за этого прямой вывод через API DMA-BUF пока полностью отключён. Разработчики Kwin активно работают над улучшением этой ситуации, и прямой вывод для конфигураций, подобных этой, может появиться в Kwin уже начиная с Plasma 6.8!

Разное для апстрима

Как обычно, продолжается работа по отправке патчей в апстрим. В последние месяцы прогресс замедлился, и может даже показаться, что произошёл откат назад. Это лишь потому, что большая часть уже отправлена в апстрим! Из оставшегося — либо огромные объёмы работы, которые пока нельзя отправить в апстрим (например, GPU, DCP), либо новые функции, за которые удалось взяться только благодаря разгрузке основного объёма задач по апстримингу. При этом кое-какие мелочи всё ещё остаются в дереве. Среди недавно отправленных в апстрим патчей — ряд изменений периферии I2S, связанных с работой speakersafetyd, патчи Devicetree, включающие драйвер hwmon на основе SMC, и, разумеется, исправления для всех изменений интерфейса прошивки SMC, появившихся в macOS 27.

Спасибо ещё раз!

Постоянные читатели, вероятно, заметили в последних отчётах немало новых имён. Благодаря щедрой поддержке спонсоров через GitHub Sponsors и Open Collective, эти люди получили оборудование, необходимое для той работы, которую они проделали. Поддержка AVD, портирование на M4 и другие готовящиеся улучшения стали возможны именно благодаря этой поддержке.