Каждый слой защиты графики имеет свою цену, и платформенная защита вывода изображения не исключение. AMD Secure Display выносит контроль целостности кадра на уровень PSP, платформенного сопроцессора, который работает собственной микропрограммой рядом с основной системой и доверяет ей ровно настолько, насколько обязан. В Linux функция выглядит скромнее, чем в проприетарных окружениях: драйвер amdgpu открывает к ней отладочный доступ через debugfs, и именно этот доступ превращает вопрос о производительности из теоретического в измеримый. В статье разбирается проверка поддержки на конкретной машине, доступ к доверенному приложению PSP, замер времени полного цикла запроса и поведение плавности вывода при активных обращениях за контрольными суммами. Отдельное место занимает граница шума, потому что без нее любая цифра превращается в суеверие.
Защищенный видеотракт на уровне PSP и его интерфейс в драйвере amdgpu
PSP, Platform Security Processor, это отдельное ядро со своей микропрограммой и памятью, которое драйвер загружает из файлов с префиксом psp в каталоге /lib/firmware/amdgpu. На сопроцессоре работают доверенные приложения, среди них fTPM, защита HDCP и управление шифрованием памяти, и у каждого свой протокол обмена с основной системой. Secure Display вписывается в схему простым ходом: контрольную сумму отображаемой области кадра считает связка контроллера вывода и PSP, а не операционная система. Контроллер вывода оставляет данные региона интереса и его CRC в служебных регистрах класса DIO, сопроцессор читает готовые значения и передает их по шине I2C принимающей стороне тракта, а та сравнивает суммы и вскрывает подмену кадра. Сколько бы процессов ни читали видеопамять, честную сумму они не воспроизведут.
В драйвере amdgpu за разговор с доверенным приложением отвечает отдельный модуль, а наружу торчит единственная ручка: отладочный файл securedisplay_test в каталоге debugfs видеокарты. Постоянных параметров загрузки у функции нет, фоновую работу она не ведет, и активность начинается ровно в момент, когда администратор пишет в файл команду. Для анализа производительности это удобная отправная точка: вся цена защиты сводится к измеримой латентности обмена с PSP и к нагрузке от самих запросов, без скрытых слоев между ними.
Проверка поддержки и доступ к интерфейсу securedisplay_test
Сначала подтверждается, что машине вообще есть о чем говорить. Возраст стека покажет uname -r, и это не формальность: доверенное приложение защищенного вывода появилось в драйвере относительно недавно, и на старых ядрах файла в debugfs может не быть даже при живом сопроцессоре. Три проверки дают полную картину:
uname -r
lspci -nnk | grep -iA3 vga
sudo dmesg | grep -i psp
ls /lib/firmware/amdgpu | grep psp | head
Первая строка показывает версию ядра, вторая ускоритель с загруженным драйвером amdgpu, третья выбирает из журнала сообщения о поднятии PSP с версией микропрограммы, четвертая перечисляет файлы прошивок сопроцессора. Дальше нужен сам каталог debugfs: в дистрибутивных ядрах он смонтирован по умолчанию, но на минималистичных системах его приходится поднимать вручную:
mount | grep debugfs
sudo mount -t debugfs none /sys/kernel/debug
ls /sys/kernel/debug/dri/0/securedisplay_test
Файл securedisplay_test создается драйвером только при инициализированном контексте защищенного вывода, и его отсутствие означает, что связка чипа и прошивки функцию не подняла. Права на запись выданы только суперпользователю, обычные пользователи в интерфейс не пишут, и это осмысленно: через файл идут прямые команды сопроцессору. Если каталога dri/0 в debugfs нет при живой карте, стоит посмотреть номер minor устройства командой ls /dev/dri.
Запрос состояния доверенного приложения и разбор ответов
Первая команда знакомства это ping доверенному приложению, в коде драйвера она носит имя TA_SECUREDISPLAY_COMMAND__QUERY_TA:
echo 1 | sudo tee /sys/kernel/debug/dri/0/securedisplay_test
sudo dmesg | grep SECUREDISPLAY | tail -5
Успешный обмен печатается в журнал строкой вида:
[ 8123.456789] amdgpu 0000:0b:00.0: SECUREDISPLAY: query securedisplay TA ret is 0x0
Код ответа отличается от нуля при внутренних проблемах доверенного приложения, а сбои обмена видны отдельными строками. В драйвере разобраны восемь статусов, от GENERIC_FAILURE и INVALID_PARAMETER до профильных проблем шины, например "Secure display: Failed to write to I2C." или "Secure display: Failed to Read CRC". Вторая команда отправляет регион интереса и контрольную сумму по шине I2C, в терминах доверенного приложения это TA_SECUREDISPLAY_COMMAND__SEND_ROI_CRC:
echo 2 0 | sudo tee /sys/kernel/debug/dri/0/securedisplay_test
Число после кода операции это номер физического канала вывода, нумерация идет с нуля, и по нему доверенное приложение выбирает служебные регистры DIO. Успешная передача тоже попадает в журнал:
[ 8130.123456] amdgpu 0000:0b:00.0: SECUREDISPLAY: I2C buffer out put is: 4c 54 86 01 00 f0
На обычном десктопе без приемного устройства дисплея команда честно ответит ошибкой записи в I2C, и это ожидаемо: канал рассчитан на связку с контроллером монитора или внешней платой. Для замеров латентности это не помеха, полный цикл запроса отрабатывает до стадии записи в шину. Неверный номер канала драйвер отклонит строкой "Invalid input" без отправки команды, так что эксперименты с числами безопасны.
Замер времени полного цикла запроса к сопроцессору
При записи в файл драйвер заполняет общий буфер команды, отправляет ее через кольцо PSP и ждет ответа под мьютексом, а вокруг всей операции стоит обертка управления питанием, которая при простое сначала будит ускоритель. Отсюда два правила измерения: сначала прогрев одним холостым запросом, затем серия без пауз, иначе в цифру попадет пробуждение чипа. Самый честный источник времени это метки журнала: каждый запрос печатает строку, и разность соседних меток равна длительности цикла:
for i in $(seq 1 100); do echo 1 | sudo tee /sys/kernel/debug/dri/0/securedisplay_test > /dev/null; done
sudo dmesg | grep "query securedisplay" | awk '{gsub("]","",$2); if(p){print ($2-p)*1000} p=$2}' | sort -n | awk '{a[NR]=$1} END{print "медиана, мс:", a[int(NR/2)], "максимум, мс:", a[NR]}'
Первая строка гоняет сто запросов подряд, вторая вынимает метки времени, считает разности в миллисекундах и печатает медиану с максимумом. Для примера на типичном десктопном APU медиана укладывается в доли миллисекунды, порядка 0,3-0,5 мс, а максимум в единицы миллисекунд достается первым запросам после простоя. Общая оценка через time тоже работает, но завышает результат, потому что на каждый шаг приходится запуск процессов sudo и tee:
time (for i in $(seq 1 100); do echo 1 | sudo tee /sys/kernel/debug/dri/0/securedisplay_test > /dev/null; done)
Ей доверяют грубую оценку порядка величины, а точные значения берут из меток журнала. Параллельные запросы ситуацию не ускоряют: обмен идет под мьютексом, два фоновых цикла просто встанут в очередь, и медиана вырастет примерно вдвое. Отсюда рабочее правило: частоту обращений наращивают количеством запросов в секунду, а не параллельными потоками, иначе замер начнет измерять очередь, а не цену команды. Холодный запрос после простоя ускорителя стоит на порядки дороже горячего, и при интерпретации цифр это разделение держат в голове постоянно.
Влияние активных запросов CRC на плавность вывода
Замер латентности отвечает на половину вопроса, вторая половина это плавность картинки под нагрузкой от запросов. Протокол сравнения строится жестко:
- снять базовую серию glmark2 без обращений к securedisplay_test, логируя время кадра через MangoHud;
- запустить фоновый цикл запросов CRC с фиксированным интервалом;
- повторить точно такую же серию glmark2 при работающем цикле;
- снять процессорное время композитора через pidstat в обеих сериях;
- сравнить медианы и девяносто пятые процентили времени кадра между сериями.
Инструменты и базовый прогон:
sudo apt install glmark2 mangohud sysstat
MANGOHUD_CONFIG=fps,frametime mangohud glmark2
MangoHud рисует график времени кадра поверх окна и пишет CSV-лог по нажатию Shift_R+F2, файлы падают в каталог mangohud-logs домашней папки. Фоновый генератор нагрузки выглядит так:
for i in $(seq 1 500); do echo 2 0 | sudo tee /sys/kernel/debug/dri/0/securedisplay_test > /dev/null; sleep 0.03; done
Пятьсот итераций с паузой в тридцать миллисекунд растягиваются примерно на полминуты и держат частоту запросов до тридцати в секунду, реальная частота ниже из-за накладных расходов на запуск процессов. Нагрузку композитора снимают двумя отчетами:
pidstat -h -p $(pidof Xorg) 1 60 > /tmp/compositor-base.txt
pidstat -h -p $(pidof Xorg) 1 60 > /tmp/compositor-load.txt
Первый снимают при спокойном столе, второй во время фонового цикла, и строка Average в конце каждого отчета дает средний процент ядра за минуту. На Wayland вместо Xorg подставляется имя композитора, gnome-shell или sway, команда работает одинаково. Ожидание по механике такое: счет контрольных сумм выполняет аппаратура контроллера вывода параллельно с разверткой, PSP читает готовые значения, поэтому умеренная частота запросов не должна сдвигать кадры. Если медианы времени кадра между сериями разъехались заметно, причина почти всегда вне защиты: перегрев, другой профиль питания или посторонние процессы, и их вычеркивают до выводов.
Числовые примеры расчета цены защиты и границы шума
Сырые метрики превращаются в вывод простой арифметикой. Допустим, медиана запроса вышла 0,42 мс, а фоновый цикл держит тридцать запросов в секунду. Доля процессорного времени считается одной строкой:
0.42 * 30 / 1000 * 100 = 1.26
Полтора процента одного ядра на постоянный опрос, десять запросов в секунду уменьшат долю втрое, до 0,42 процента. В пересчете на кадр при 60 Гц с бюджетом 16,7 мс один запрос занимает 2,5 процента бюджета, а при 144 Гц с бюджетом 6,9 мс уже больше шести:
0.42 / 16.7 * 100 = 2.5
0.42 / 6.9 * 100 = 6.1
Балльные тесты интерпретируются через разброс: пять прогонов glmark2 до нагрузки дали 2140, 2128, 2135, 2144 и 2132 очка, медиана 2135, после включения фонового цикла 2122, 2110, 2117, 2126 и 2114, медиана 2117. Разница в процентах:
(2135 - 2117) / 2135 * 100 = 0.8
Меньше одного процента, то есть внутри обычного разброса одиночных прогонов, который у балльных тестов держится около одного процента. Практическое правило простое: расхождение до двух процентов считается шумом, уверенный вывод требует запаса от трех. Потери в кадрах пересчитываются так же наглядно: часовая сессия при 60 кадрах в секунду несет 216000 кадров, и сдвиг половины процента означает 1080 кадров со сдвигом, что глаз замечает как редкие микрорывки. Холодный запрос после простоя стоит десятки миллисекунд против долей миллисекунды у горячего, поэтому в замеры он не попадает, а в интерпретацию попадает всегда: редкие обращения дешевы, циклы из холодных запросов никому не нужны. Арифметика пробуждений не пугает: десять просыпаний за час по 12 мс складываются в 120 мс, или около 0,003 процента времени, и даже грубый расчет показывает, что эпизодические холодные запросы бюджету кадра не угрожают.
Трактовка результатов и границы применения функции
Итог обычно оказывается скучным, и это лучший итог: медиана запроса в долях миллисекунды, плавность вывода не изменилась, композитор не напрягся. Вычислительный конвейер GPU защита не касается вовсе, счет CRC идет аппаратурой вывода, а обмен с PSP не блокирует рендер, поэтому плата за функцию проявляется только при агрессивной частоте обращений. Рабочие сценарии у механизма специфичные: связки с приемными контроллерами дисплея, киоски и витринные машины, где кадр проверяет внешняя сторона, а не операционная система. На обычном десктопе без такой стороны вторая команда честно отработает ошибку I2C, и измеримой остается только латентность ping, чего для оценки цены защиты достаточно.
Отката здесь не требуется по построению: интерфейс отладочный, постоянных настроек он не пишет, после перезагрузки все возвращается в исходное состояние. Закрыть доступ можно отмонтированием debugfs или правами на файл, и это весь цикл обслуживания. Алгоритм переносится на соседние доверенные приложения PSP без изменений: проверка поддержки, прогрев, серия запросов, медианы вместо средних, сравнение с фоном и граница шума в два-три процента. Защита, которая не тратит такты рендера, платит латентностью обмена, и после этой статьи она измерена в миллисекундах, а не в ощущениях.