Как написать видеодрайвер

Время на прочтение
9 мин

Количество просмотров 3.4K

Спустя шесть месяцев реверс-инжиниринга новые GPU Arm «Valhall» (Mali-G57, Mali-G78) получили свободные и опенсорсные драйверы Panfrost. Благодаря новому компилятору, патчам драйверов и хакингу ядра эти новые GPU почти готовы к переносу в основную ветку.

В 2021 году не существовало устройств Valhall, способных запускать основную ветвь Linux. Хотя отсутствие устройств являлось очевидным препятствием для разработки драйверов устройств, нет более подходящего времени для написания драйверов, чем момент, когда оборудование ещё не добралось до конечных пользователей. Разработка и распространение драйверов уровня качества продакшена требует времени, а мы не хотели, чтобы пользователям пришлось полагаться на блобы с закрытыми исходниками. Если разработку не начать до того, как устройство попадёт на прилавки магазинов, то к выпуску готовых открытых драйверов это устройство может уже достигнуть конца срока своей жизни. Но имея преимущество по времени, мы можем выпустить драйверы уже к моменту, когда устройства попадут к конечным пользователям.

В статье я расскажу, как нам это удалось.

Реверс-инжиниринг без рута

Летом прошлого года Collabora приобрела телефон с Android на Mali-G78. В телефоне не было рута, поэтому мы не могли заменить графические драйверы на собственные. Мы могли перевести телефон в режим разработчика, чтобы запускать тестовые приложения с проприетарным графическим драйвером и выполнять инъекции нашего кода с помощью LD_PRELOAD, что позволяло нам исследовать подготовленную проприетарным драйвером графическую память и «пассивно» реверс-инжинирить оборудование. Эта память содержала скомпилированные двоичные файлы шейдеров в наборе команд Valhall, а также структуры данных Valhall, контролирующие такое графическое состояние, как текстуры, смешение и усечение.

«Активный» реверс-инжиниринг тоже был возможен. Мы могли модифицировать скомпилированные шейдеры и структуры данных GPU, что позволяло нам экспериментировать с отдельными частями. Мы могли пойти ещё дальше, собирая собственные шейдеры и структуры данных, проверяя их работу на оборудовании.

В качестве примера этой методики рассмотрим реверс-инжиниринг «дескриптора буфера» Valhall. Эта новая структура данных описывает буфер памяти, доступ к которому выполняется новой командой «load buffer» (LD_BUFFER). Угадав структуру дескриптора буфера и кодировку LD_BUFFER, мы можем создать собственный дескриптор буфера и написать шейдер при помощи LD_BUFFER, чтобы подтвердить правильность нашей догадки и исследовать низкоуровневую семантику.

При реверс-инжиниринге новых структур данных Valhall мы могли ориентироваться на легаси. Хотя Valhall реорганизует свои структуры данных, чтобы снизить излишнюю трату ресурсов драйверов Vulkan, содержимое на битовом уровне напоминает старые GPU Mali. Если мы найдём «контуры» новых структур данных, то сможем заполнить недостающую информацию, выполняя сравнение со старым оборудованием.

В процессе исследования структур данных мы документировали свои находки в формальном XML-описании оборудования. Этот файл имеет тот же формат, что и XML для старых архитектур Mali, которые уже поддерживаются драйвером Panfrost. Так как структуры данных Valhall являются потомками этих старых архитектур, мы можем форкнуть старый XML Mali, что сэкономит нам время на ввод и позволит сохранить согласованность наименований.

После достаточного объёма реверс-инжиниринга мы смогли вставить наш XML в Panfrost, автоматически генерирующий код для упаковки и распаковки структур данных. Благодаря неустанному труду сотрудника Collabora Бориса Брезиллона критически важный для производительности код Panfrost специализируется на этапе компиляции под целевую архитектуру, что позволяет нам добавлять новые архитектуры без дополнительных издержек старого оборудования. Итак, получив файл XML, мы были готовы к написанию драйвера Valhall.

Пишем драйвер без оборудования

Ноябрь 2021 года. Я написала компилятор Valhall. Я провела достаточный для написания драйвера объём реверс-инжиниринга. Но у меня всё ещё не было Linux-оборудования для тестирования кода.

Это стало основным препятствием.

К счастью, я знала, как его обойти.

Мы можем разработать драйвер на любой машине с Linux, не тестируя его на реальном оборудовании. Чтобы реализовать это, необходимо юнит-тестирование. Без оборудования мы не можем выполнять комплексное тестирование, однако юнит-тесты можно запускать на любом оборудовании. В случае компилятора Valhall я написала юнит-тесты для всего, от упаковки команд до оптимизации. Хотя покрытие было неполным, тесты ещё на ранних этапах начали выявлять множество багов.

Однако тут был изъян: юнит-тестирование не говорит нам, верны ли наши ожидания относительно оборудования. Однако, оно может подтвердить, что наш код соответствует нашим ожиданиям. Если наш реверс-инжиниринг будет доскональным, то ожидания окажутся верными.

Но даже в этом случае одного юнит-тестирования было бы недостаточно.

На сцене появляется drm-shim.

drm-shim

Драйверы Mesa, в том числе и Panfrost, могут имитировать тестируемое оборудование при помощи drm-shim — небольшой библиотеки, создающей заглушки системных вызовов, используемых графическими драйверами пользовательского пространства для общения с ядром. Благодаря drm-shim немодифицированные драйверы пользовательского пространства считают, что работают на реальном оборудовании, в том числе и на оборудовании Valhall.

Гуру графики Эмма Анхолт спроектировала drm-shim, чтобы запускать компиляторы Mesa как кросс-компиляторы для использования в continuous integration (CI). Кроме CI drm-shim позволяет тестировать компиляторы на наших машинах для разработки, что может быть значительно быстрее, чем на целевых встроенных устройствах. Но дело не ограничивается компиляторами; мы можем выполнять под drm-shim целые наборы тестов, «кросс-тестируя» код для любого оборудования. Тесты не проходятся, поскольку drm-shim не выполняет рендеринга; это прокладка, а не эмулятор. Однако она позволяет нам исполнять кодовые пути новых драйверов без ограничений реального оборудования.

Так как drm-shim работает на любой машине с Linux, я захотела использовать самую быструю машину с Linux, которая у меня есть: Apple M1. Как ни странно, drm-shim не заработала на моём M1 с Linux, хотя работала на всех остальных компьютерах. Требовалась отладка.

Немного изучив код, я наткнулась на виновный в проблеме фрагмент:

bo->addr = util_vma_heap_alloc(&heap, size, 4096);
mmap(NULL, ..., bo->addr);

Этот код распределяет блок памяти, выровненный по странице, и использует его адрес как смещение в вызове mmap. В моей системе вызов mmap завершается неудачей, поэтому я обратилась к странице man mmap:

offset должно быть кратно размеру страницы, возвращаемому sysconf(_SC_PAGE_SIZE).

mmap в drm-shim работает, потому что размер страницы в Linux равен 4096 байтам (4 КБ)…

Но так бывает не всегда.

Блок управления ввода-вывода памяти Apple использует страницы большего размера, 16384 байта (16 КБ). Следовательно, когда мы запускаем Linux bare metal на платформах Apple, чтобы не усложнять жизнь, мы конфигурируем Linux на использование страниц по 16 КБ. Это значит, что на платформах Apple с Linux sysconf(_SC_PAGE_SIZE) возвращает 16384, поэтому mmap завершается неудачей. Исправить это легко:

bo->addr = util_vma_heap_alloc(&heap, size, sysconf(_SC_PAGE_SIZE));
mmap(NULL, ..., bo->addr);

Благодаря этому drm-shim работает в системах с размером страниц больше 4 КБ, в том числе и на моём M1. Это значит, что я могу с помощью компилятора Valhall компилировать тысячи шейдеров в секунду — гораздо больше, чем на любой системе с GPU Mali. Также я могу запускать Khronos OpenGL ES Conformance Test Suite:

PAN_MESA_DEBUG=valhall,trace LIBGL_DRIVERS_PATH=~/lib/dri/ LD_PRELOAD=~/mesa/build/src/panfrost/drm-shim/libpanfrost_noop_drm_shim.so PAN_GPU_ID=9091 EGL_PLATFORM=surfaceless ./deqp-gles31 --deqp-surface-type=pbuffer --deqp-gl-config-name=rgba8888d24s8ms0 --deqp-surface-width=256 --deqp-surface-height=256'

Подобные длинные команды запускают тесты и создают красиво сформатированные дампы памяти GPU, готовые для изучения вручную. Если дампы похожи на дампы из проприетарного драйвера, есть большая вероятность того, что тесты будут пройдены и на реальном оборудовании.

Общий код

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

Например, набор команд Valhall напоминает старый набор команд «Bifrost», поэтому мы можем встроить компилятор Valhall в качестве дополнительного бэкенда в уже имеющийся компилятор Bifrost. Общие проходы компилятора, например, этапы выбора команд и распределения регистров просто работают в Valhall, даже несмотря на то, что разрабатывались и отлаживались они для Bifrost.

После адаптации Panfrost под Valhall мы получили совместимый и производительный готовый драйвер.

… Теоретически.

Реальное оборудование и реальная боль

Я не могла провести тесты на реальном оборудовании Valhall до начала января, когда раздобыла Chromebook с SOC MediaTek MT8192 и соответствующим последовательным кабелем. MT8192 включает в себя GPU Valhall «Mali-G57», совместимый с Mali-G78, реверс-инжинирингом которого я занимаюсь. Поддержка MT8192 в основной ветви ядра минимальна, однако Linux загружается. Благодаря патчам других сотрудников Collabora работает и USB. Этого достаточно для работы на GPU. Да, дисплей не работает, но кому он нужен?

Мы начали с того, что стали обучать Linux обнаруживать GPU. На десктопах определять всё подключенное оборудование операционным системам помогают ACPI и UEFI. Хотя эти стандарты существуют и для Arm, на практике системы Arm требуют дерева устройств, описывающего оборудование: какие комплектующие присутствуют, какие регистры и тактовые генераторы они используют, как они подключены. Мы знаем не очень много о MT8192, но его поддерживает ChromeOS, поэтому у ChromeOS есть полное дерево устройств. Адаптировав это дерево устройств под основную ветку, мы вскоре начали наблюдать признаки жизни:

[  1.942843] panfrost 13000000.gpu: unknown id 0x9093 major 0x0 minor 0x0 status 0x0

Ядро не может идентифицировать подключенный GPU Mali, но это было ожидаемо — в конце концов, оно никогда раньше не видео Mali-G57. Нам нужно добавить отображение из аппаратного ID Mali-G57 в его название, список функций и список аппаратных багов. После этого драйвер загружается.

[  1.942843] panfrost 13000000.gpu: mali-g57 id 0x9093 major 0x0 minor 0x0 status 0x0
[  1.982322] [drm] Initialized panfrost 1.2.0 20180908 for 13000000.gpu on minor 0

Благодаря модулю ядра вниз по потоку, выпущенному компанией Arm, мы знали, какие части Valhall, относящиеся к ядру, обратно совместимы с GPU Mali, выпущенными десяток лет назад. Panfrost поддерживает старое оборудование Mali, поэтому теоретически мы могли прямо сейчас протестировать Mali-G57.

Однако когда дело касается оборудования, теория и практика никогда не согласуются.

Давайте попробуем отправить оборудованию «null job» — простую задачу, которая совершенно ничего не делает:

struct mali_job_descriptor_header job = {
    .job_type = MALI_JOB_TYPE_NULL,
    .job_index = 1
};

Во всей структуре данных задано всего 2 бита. Мы даже можем прописать эту задачу в ядре и передавать её сразу после включения питания оборудования. Так как задача корректна, оборудование выполнит её без проблем.

[   2.094748] panfrost 13000000.gpu: js fault, js=1, status=DATA_INVALID_FAULT, head=0x6087000, tail=0x6087000

Что? Оборудование утверждает, что задача недопустима, несмотря на то, что её допустимость очевидна. Наверно, оборудование считывает из памяти не то, что мы написали.

Этот симптом нам пугающе знаком. Когда мы с сотрудником Collabora Томе Визосо два года назад добавили поддержку Mali-G52, то наблюдали те же симптомы у SOC Amlogic. Виновником оказалась специфичная для Amlogic проблема когерентности кэша. Решение той проблемы здесь неприменимо, поэтому настало время охоты за специфичными багами MediaTek.

Пробираясь через код ChromeOS, я выяснила, что MediaTek реализовала необъяснённое изменение в драйвере GPU, задав один бит, относящийся к тактовому генератору MT8192, чтобы «отключить ACP» и устранить проблемы с шиной. Это изменение является воплощением «решающего все проблемы» магического бита, о котором ходили только слухи; это настоящий кошмар для реверс-инженера.

… Однако при задании этого бита в нашем ядре null job выполняется успешно.

… Что-что?

Оказывается, ACP — это «Accelerator Coherency Port», отвечающий за управление когерентностью кэша между CPU и GPU. Очевидно, ACP не должен быть включён в MT8192, но из-за аппаратного бага случайно был включён. Чтобы обойти эту проблему, ядро должно установить этот бит, чтобы отключить ACP.

Что?

Мы можем создать ту же null job из пользовательского пространства. Для оборудования пространство ядра и пользовательское пространство одинаковы, поэтому это должно сработать.

Но не сработало.

Таймаут задачи истекает до завершения. Изучая лог ядра, мы обратили внимание на более ранний таймаут, ожидающий пробуждения GPU после сброса.

Добавив в ядро множество printk, мы всё-таки выяснили, что при запуске Linux GPU отключен и что мы ничего не можем сделать для включения его питания. Неудивительно, что всё завершается таймаутом.

Решить эту проблему нам помог мастер по работе с ядром Хейко Стубнер. Хейко предположил, что Linux может отключать питание GPU. Для экономии энергии Linux отключает неиспользуемые тактовые генераторы и домены питания. Если Linux не знает, что тактовый генератор или домен питания используется GPU, то непреднамеренно отключит GPU.

Для отладки мы можем отключить этот механизм, задав аргументы ядра clk_ignore_unused pd_ignore_unused. Благодаря этому наши тесты пользовательского пространства начинают работать.

Иногда простейшее решение находится прямо перед глазами.

Какова первопричина? У MediaTek есть сложная иерархия тактовых генераторов и доменов питания, и мы упустили некоторые из них в нашем дереве устройств. Для правильного решения проблемы нам нужно дополнить код, чтобы Linux узнал о дополнительных тактовых генераторах и доменах питания.

Как бы то ни было, теперь мы можем тестировать наш драйвер на реальном оборудовании. Начало было непростым: первая отправленная задача вернула Data Invalid Fault. Экспериментируя, мы выяснили, что Valhall требуется бОльшая согласованность указателей структур данных, чем в Bifrost. Повышение согласованности распределения устраняет сбои, а повторное снижение позволяет нам определить наименьшую требуемую согласованность. Эта информация доступна только когда мы запускаем код на оборудовании, но недоступна при изучении оборудования in vitro. Реверс-инжиниринг и разработку драйверов лучше выполнять вместе.

Наконец-то успех

После этих исправлений мы наконец получили первый пройденный тест, запущенный на реальном оборудовании, со структурами данных, подготовленными нашим опенсорсным драйвером Mesa и шейдерами, скомпилированными нашим компилятором Valhall. Ура!

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

Похоже, мы наконец вовремя выпустили драйверы Valhall для конечных пользователей.

551

25 октября 2007 года

Pavia

357 / / 22.04.2004

Насчет портов это устаревший режим для VGA/EGA режимов. На этом сайте лежит описание.

В современных видео картах программирование ведеться через регистры отоброженные в памить.

Вот только документы на видео карты в большей части засикречены. Так что не судьба.

Для твоей ОС, хватит VESA’ы (ищи vbe3.pdf). Реализуешь в соей ОС режим VM86 от туда сможешь вызывать прерывания. Что касается прерываний они особо не нужны. Так Они нужны только для выбора и установки видео режима, а вывод осуществляется в видео память. Так что установку видео режима можнео проделвать до перехода в защищенный режим.

PS. За стандар, я бы взял стандартное разрешение монитора, а не то которое у тебя.

Если еще не отпало желание написать драйвер. То диз ассемблер в руки и в перед.
А еще можно отладчиком перехватывать что куда пишеться. А после воспроизводить. После долгого обдумывания ты сможешь написать драйвер.

#1

Jiraff

    Member

  • Posters
  • 120 Сообщений:

Отправлено 27 Июль 2011 — 14:00

Здравствуйте!Вопрос к тем кто шарит: сложно ли написать драйвер для видеокарты (аля Catalyst) , что для этого нужно знать?Бывает что при выходе новых драйверов повышается производительность в играх.За счет чего это достигается?Каким образом задействуется GPU, какие комманды есть для его вызова?

…Пусть Жираф и был не прав,
но тут виновен не Жираф,
а тот, кто крикнул из ветвей:
— Жираф большой, ему видней!

  • Наверх


#2


v.martyanov

v.martyanov

    Guru

  • Virus Analysts
  • 8 308 Сообщений:

Отправлено 27 Июль 2011 — 14:02

Думаю, примерно так же сложно, как и разобрать существующий.

  • Наверх


#3


Jiraff

Jiraff

    Member

  • Posters
  • 120 Сообщений:

Отправлено 27 Июль 2011 — 14:03

а исходники драйверов этих есть?Например уже завтра выйдет Catalyst 11.7

…Пусть Жираф и был не прав,
но тут виновен не Жираф,
а тот, кто крикнул из ветвей:
— Жираф большой, ему видней!

  • Наверх


#4


v.martyanov

v.martyanov

    Guru

  • Virus Analysts
  • 8 308 Сообщений:

Отправлено 27 Июль 2011 — 14:04

а исходники драйверов этих есть?Например уже завтра выйдет Catalyst 11.7

— А Гугл существует?
— Нет, сынок, это фантастика…

  • Наверх


#5


Sc@RpioIIIK@

Sc@RpioIIIK@

    Advanced Member

  • Posters
  • 695 Сообщений:

Отправлено 27 Июль 2011 — 14:04

За счет чего это достигается?Каким образом задействуется GPU, какие комманды есть для его вызова?

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

пыщ-пыщ

  • Наверх


#6


Jiraff

Jiraff

    Member

  • Posters
  • 120 Сообщений:

Отправлено 27 Июль 2011 — 14:06

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

…Пусть Жираф и был не прав,
но тут виновен не Жираф,
а тот, кто крикнул из ветвей:
— Жираф большой, ему видней!

  • Наверх


#7


primarX

primarX

    Newbie

  • Posters
  • 50 Сообщений:

Отправлено 27 Июль 2011 — 14:09

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

вы же хотели ассемблер осваивать (или что-то типа этого) http://forum.drweb.com/public/style_emoticons/default/tongue.png

Если есть два способа, сложный и простой, то выбирай простой, так как он проще сложного способа, который тоже простой, но ещё и сложнее.

  • Наверх


#8


Jiraff

Jiraff

    Member

  • Posters
  • 120 Сообщений:

Отправлено 27 Июль 2011 — 14:09

да хотел, но тяжко что то

…Пусть Жираф и был не прав,
но тут виновен не Жираф,
а тот, кто крикнул из ветвей:
— Жираф большой, ему видней!

  • Наверх


#9


sergeyko

sergeyko

    Guru

  • Dr.Web Staff
  • 3 920 Сообщений:

Отправлено 27 Июль 2011 — 14:22

да хотел, но тяжко что то

Ну вот вы и ответили на свой вопрос. Не сложно, но тяжко. Если осилите, скажете «Драйвер для видяхи? Да, как нечего делать!».

Sergey Komarov
R&D www.drweb.com

  • Наверх


#10


Aleksey Strokin

Aleksey Strokin

    Member

  • Posters
  • 441 Сообщений:

Отправлено 27 Июль 2011 — 14:40

Здравствуйте!Вопрос к тем кто шарит: сложно ли написать драйвер для видеокарты (аля Catalyst)

Вопрос из серии — когда я кину ядерную бомбу она сильно бумкнет?
Если совсем просто, то я бы за такое не взялся.

Что для этого нужно знать?

Архитектуру данного GPU.
Архитектуру API ОС (OpenGL, Direct3D самых разных версий).
Cамо собой разумеется, вы должны быть экспертом в области написания kernel mode кода.
И т.д. и т.п.

Бывает что при выходе новых драйверов повышается производительность в играх.За счет чего это достигается?

В основном оптимизация под конкретные программы, т.е. оптимизируется конкретный порядок работы с видео буферами или определённый порядок команд в шейдере, или ещё что. Из оптимизируемых программ у производителей наиболее популярны тесты(типа 3DMark).

Каким образом задействуется GPU, какие комманды есть для его вызова?

В push буфер(память доступная GPU и CPU одновременно) запихивают команды для GPU. Ситуации связанные с его полным вычитыванием, разруливаются через IO порты и прерывания.

а исходники драйверов этих есть?

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

Сообщение было изменено Aleksey Strokin: 27 Июль 2011 — 14:42

Как идти во мраке ночи? Если нет в руке свечи..?
Как идти ломая стены, и не трогать кирпичи?

  • Наверх


#11


sniper

sniper

    Advanced Member

  • Posters
  • 624 Сообщений:

Отправлено 27 Июль 2011 — 14:48

А нафига вам это?Что аэмдешники перестали продукт поддерживать? http://forum.drweb.com/public/style_emoticons/default/smile.png

У меня богатый словарный запас, в нем присутствуют слова «оксюморон», «клепсидра», «перст указующий» и даже «ибо».
Но некоторые мысли я никак не могу выразить словами.
Хочется просто взять черенок от лопаты и отдубасить всех.

  • Наверх


#12


v.martyanov

v.martyanov

    Guru

  • Virus Analysts
  • 8 308 Сообщений:

Отправлено 27 Июль 2011 — 14:48

Дрова NVidia под линух, насколько я помню, поставляются в бинарном виде, без сорцов. И кастрированы по функциональности.

  • Наверх


#13


Aleksey Strokin

Aleksey Strokin

    Member

  • Posters
  • 441 Сообщений:

Отправлено 27 Июль 2011 — 14:53

Дрова NVidia под линух, насколько я помню, поставляются в бинарном виде, без сорцов. И кастрированы по функциональности.

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

Как идти во мраке ночи? Если нет в руке свечи..?
Как идти ломая стены, и не трогать кирпичи?

  • Наверх


#14


kulikDefense

kulikDefense

    Newbie

  • Posters
  • 25 Сообщений:

Отправлено 27 Июль 2011 — 14:57

проще не юзать богомерзкий линукс

  • Наверх


#15


basid

basid

    Guru

  • Posters
  • 4 391 Сообщений:

Отправлено 27 Июль 2011 — 15:01

сложно ли написать драйвер для видеокарты (аля Catalyst)

AMD, в своё время, выкладывала описание своих видеожелезок.
Найдите, оцените число регистров, их различия по версиям … Дальнейшие вопросы, я думаю, отпадут сами собой.

  • Наверх


#16


Aleksey Strokin

Aleksey Strokin

    Member

  • Posters
  • 441 Сообщений:

Отправлено 27 Июль 2011 — 15:01

проще не юзать богомерзкий линукс

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

Как идти во мраке ночи? Если нет в руке свечи..?
Как идти ломая стены, и не трогать кирпичи?

  • Наверх


#17


Denis Nikolayev

Denis Nikolayev

    Member

  • Posters
  • 461 Сообщений:

Отправлено 28 Июль 2011 — 11:02

Здравствуйте!Вопрос к тем кто шарит: сложно ли написать драйвер для видеокарты (аля Catalyst) , что для этого нужно знать?Бывает что при выходе новых драйверов повышается производительность в играх.За счет чего это достигается?Каким образом задействуется GPU, какие комманды есть для его вызова?

А зачем изобретать велосипед? Имхо, официальные дрова пишутся отнюдь не ламерами, поэтому у вас вряд ли получится лучше (при условии, что вы в этом новичок). А если просто хочется поковыряться с кодом, лучше начать с чего-то менее сложного.

  • Наверх


#18


Aleksey Strokin

Aleksey Strokin

    Member

  • Posters
  • 441 Сообщений:

Отправлено 28 Июль 2011 — 11:18

Имхо, официальные дрова пишутся отнюдь не ламерами,

Боянистый отзыв о качестве видео-драйверов, от программиста пишущего игры под эти дрова.

Как идти во мраке ночи? Если нет в руке свечи..?
Как идти ломая стены, и не трогать кирпичи?

  • Наверх


#19


sniper

sniper

    Advanced Member

  • Posters
  • 624 Сообщений:

Отправлено 28 Июль 2011 — 13:19

Боянистый отзыв о качестве видео-драйверов, от программиста пишущего игры под эти дрова.

Да,это так и есть,помню была аэмдешная карта,так вот все новый драйвера для нее были хуже оригинальных(которые вместе с ней шли)приходилось возвращаться к старым. http://forum.drweb.com/public/style_emoticons/default/sad.png

У меня богатый словарный запас, в нем присутствуют слова «оксюморон», «клепсидра», «перст указующий» и даже «ибо».
Но некоторые мысли я никак не могу выразить словами.
Хочется просто взять черенок от лопаты и отдубасить всех.

  • Наверх


#20


Jiraff

Jiraff

    Member

  • Posters
  • 120 Сообщений:

Отправлено 28 Июль 2011 — 14:35

ого ничего себе

…Пусть Жираф и был не прав,
но тут виновен не Жираф,
а тот, кто крикнул из ветвей:
— Жираф большой, ему видней!

  • Наверх


Предыстория

Для очередного проекта возникла необходимость написать простенький софтверный драйвер под Windows, но так как опыта в написании драйверов у меня примерно столько же, сколько и в балете, я начал исследовать данную тему. В таких делах я предпочитаю начинать с основ, ибо если кидаться сразу на сложные вещи, то можно упустить многие базовые понятия и приёмы, что в дальнейшем только усложнит жизнь.

После 20 минут поисков по сети я наткнулся на Github Павла Иосифовича (zodiacon — Overview). Личность легендарная в своих кругах, достаточно посмотреть на его репозиторий, публикации и выступления на именитых конференциях. Помимо этого, Павел является автором/соавтором нескольких книг: «Windows Internals» (книга, имеющаяся у меня на полке, которая принесла немало пользы), и «Windows Kernel Programming» 2019 года выпуска (бегло пролистав 11 Глав или 390 страниц, я понял – это то, что нужно!).
Кстати, книгу вы можете купить прямо на сайте Павла

Ссылка скрыта от гостей

Книгу я приобрёл в бумажной версии, чтобы хоть и немного, но поддержать автора. Безупречное качество, несмотря на то, что она издается в мягком переплете. Хорошие плотные листы формата А4 и качественная краска. (книга без проблем пережила вылитую на нее кружку горячего кофе).
Пока я сидел на балконе и читал четвёртую главу книги, в голову пришла мысль: а почему бы не сделать ряд статей на тему «Программирования драйвера под Windows», так сказать, совместить полезное, с еще более полезным.

И вот я здесь, пишу предысторию.

Как я вижу этот цикл статей и что от него ожидать:
Это будут статьи, которые будут базироваться на вышеупомянутой книге, своеобразный вольный и сокращенный перевод, с дополнениями и примечаниями.

Базовые понятия о внутреннем устройстве Windows (Windows Internals)

Для того, чтобы начать разрабатывать Драйвер под Windows, то есть работать на уровне с ядром ОС, необходимо базовое понимание того, как эта ОС утроена. Так как я хочу сосредоточиться на написании драйвера, а не на теории об операционных системах, подробно описывать понятия я не буду, чтобы не растягивать статью, вместо этого прикреплю ссылки для самостоятельного изучения.

Следовательно, нам стоит ознакомиться с такими базовыми понятиями как:

Ссылка скрыта от гостей

Процесс – это объект, который управляет запущенной инстанцией программы.​

Ссылка скрыта от гостей

Технология, позволяющая создавать закрытые пространства памяти для процессов. В своем роде — это песочница.​

Ссылка скрыта от гостей

Это сущность, которая содержится внутри процесса и использует для работы ресурсы, выделенные процессом — такие, как виртуальная память. По сути, как раз таки потоки и запускают код.​

Ссылка скрыта от гостей

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

Ссылка скрыта от гостей

Это сложно описать словами коротко, проще один раз увидеть картинку.​

В упрощённом виде это выглядит так:​

image1.png

Ссылка скрыта от гостей

Дескрипторы и объекты необходимы для регулирования доступа к системным ресурсам.​

Объект — это структура данных, представляющая системный ресурс, например файл, поток или графическое изображение.​

Дескриптор – это некая абстракция, которая позволяет скрыть реальный адрес памяти от Программы в пользовательском режиме.​

Для более глубокого понимания Операционных систем могу посоветовать следующие материалы:
Книги:

  • Таненбаум, Бос: Современные операционные системы
  • Windows Internals 7th edition (Part 1)

Видео:

Настройка рабочего пространства

Для разработки драйвера, как и любого другого софта необходима подходящая среда.
Так как мы работаем в операционной системе Windows, её средствами мы и будем пользоваться.

Что нам понадобится:

1. Visual Studio 2017 и старше.​

(Community Version хватает с головой) Также во вкладке „Individual components” необходимо установить

Код:

MSVC v142 - VS 2019 C++ ARM build tools (Latest)
MSVC v142 - VS 2019 C++ ARM Spectre-mitigated libs (Latest)
MSVC v142 - VS 2019 C++ ARM64 build tools (Latest)
MSVC v142 - VS 2019 C++ ARM64 Spectre-mitigated libs (Latest)
MSVC v142 - VS 2019 C++ ARM64EC build tools (Latest - experimental)
MSVC v142 - VS 2019 C++ ARM64EC Spectre-mitigated libs (Latest - experimental)
MSVC v142 - VS 2019 C++ x64/x86 build tools (Latest)
MSVC v142 - VS 2019 C++ x64/x86 Spectre-mitigated libs (Latest)

image10.png

и далее по списку.

2. Windows 10/11 SDK (последней версии)​

Ссылка скрыта от гостей

image3.png

Тут все просто. Качаем iso файл, монтируем и запускаем установщик.

3. Windows 10/11 Driver Kit (WDK)​

Ссылка скрыта от гостей

image4.png

В конце установки вам будет предложено установить расширение для Visual Studio. Обязательно установите его!

image11.png

После закрытия окна установки WDK появится установщик Расширения VisualStudio

image2.png

4. Sysinternals Suite

Ссылка скрыта от гостей

Скачайте и распакуйте в удобное для вас место. Это набор полезных утилит, которые пригодятся для исследования Windows, дебага драйвера и прочего.

image8.png

5. Виртуальная Машина с Windows для тестов.​

Выбор ПО для виртуализации на ваше усмотрение. Я буду использовать «VMware Workstation 16 pro».
Написанные драйверы лучше тестировать именно в виртуальной машине, так как Ядро — ошибок не прощает, и вы будете часто улетать в синий экран смерти.

После того, как все было установлено, пора запускать Visual Studio и начинать писать драйвер.

Создание проекта

Запускаем Visual Studio и создаем новый проект. Создадим пустой проект „Empty WDM Driver“

image21.png

Называем его как душе угодно.

image24.png

И вот он, наш свеженький чистенький проект для нашего первого драйвера.

image27.png

Теперь необходимо создать cpp файл, в котором мы будем писать сам драйвер.

image7.png

image14.png

Вот и все. Настройку системы и среды мы закончили.

image22.png

Первый драйвер

Сначала импортируем ntddk.h эта одна из базовых библиотек для работы с ядром. Больше информации

Ссылка скрыта от гостей

. Как и у любой программы, у драйвера должна быть точка входа DriverEntry, как функция Main в обычной программе. Готовый прототип этой функции выглядит так

C++:

#include <ntddk.h>
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
/*
    In_ это часть SAL(Source Code Ananotation Language) Аннотации не видимы для компилятора,
    но содержат метаданные которые, улучшают анализ и чтение кода.
*/
    return STATUS_SUCCESS;
}

Если мы попробуем собрать наш проект, то получим следующие ошибки и предупреждения.

image15.png

В данном случае пункт 1 является следствием пунктов 2 и 3. Дело в том, что по дефолту в Visual Studio некоторые “предупреждения” расцениваются как ошибки.
Чтобы решить эту проблему есть 2 пути.

  1. Отключить эту фичу в Visual Studio, что делать не рекомендуется. Так как сообщения об ошибках могут быть полезны и сэкономят вам время и нервы в дальнейшем.
  2. Более правильный и классический метод это использовать макросы в c++. Как видно из сообщения с кодом C4100 объекты RegistryPath и DriverObject не упомянуты в теле функции. Подробнее

    Ссылка скрыта от гостей

    .

Для того, чтобы избавиться от предупреждений, и заставить наш код работать, стоит поместить объекты в макрос UNREFERENCED_PARAMETER(ObjectName)

C++:

include <ntddk.h>

NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
    UNREFERENCED_PARAMETER(DriverObject);
    UNREFERENCED_PARAMETER(RegistryPath);
    return STATUS_SUCCESS;
}

Теперь, если пересобрать проект, то мы увидим, что ошибка С220 и предупреждение C4100 пропали, но к ним на смену пришли LNK2019 и LNK1120. Однако это уже не ошибки компиляции — это ошибки линкера. А кто говорил что будет легко?
О том, что такое линкер можно почитать

Ссылка скрыта от гостей

.

Дело в том, что наша функция не представлена в стандартном линкере С++ и вообще она девушка капризная и хочет Си-линкер. Удовлетворим желание дамы и дадим ей то, чего она хочет.

Делается это просто. Перед функцией надо добавить extern "C" так наш линкер будет понимать, что эта функция должна линковаться С-линкером.

Собираем проект заново и вуаля — Драйвер собрался.

image17.png

Что на данный момент умеет наш драйвер? Сейчас это по сути пустышка, которая после загрузки, в случае успеха, вернет нам сообщения об удачном запуске. Давайте заставим его нас поприветствовать и проверим его работоспособность. Выводить сообщения мы будем при помощи функции KdPrint(()); да именно в двойных кавычках.

Итоговый код драйвера будет выглядеть так:

C++:

#include <ntddk.h>

//Указываем линкеру, что DriverEntry должна линковаться С-линкером
extern "C"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
    //Убираем варнинг C4100 и связанную с ним ошибку C220
    UNREFERENCED_PARAMETER(DriverObject);
    UNREFERENCED_PARAMETER(RegistryPath);
    //Выводим сообщение
    KdPrint(("Hi Codeby, this is our first driver! Yuhu!n"));
   
    return STATUS_SUCCESS;
}

Собираем или пересобираем драйвер.

image6.png

Важно! Сборка драйвера должна происходить в режиме Debug!!!

image12.png

После чего в папке нашего проекта мы сможем найти результаты нашего труда. Вы только посмотрите на него, какой маленький и хорошенький.

image5.png

Но что делать дальше? Как проверить его работоспособность?
Для этого нам и понадобится наша виртуальная машина с Windows, но перед запуском на ней драйвера, нам придется проделать пару манипуляций. Дело в том, что в Windows есть встроенная защита, и если драйвер не подписан «нужной» подписью ака сертификатом, то драйвер просто не загрузится.

Дальнейшие действия нужно проделать в Windows на виртуальной машине.
Чтобы отключить эту проверку подписи, а точенее перевести Windows в тестовый режим, запустите cmd.exe от имени администратора и введите следующую команду bcdedit /set testsigning on.

image13.png

Перезагрузите виртуальную машину.
Если все прошло удачно, в правом нижнем углу вы увидите следующую надпись (2 нижнее строчки могут отличиться в зависимости от версии Windows)

image9.png

Возвращаемся в папку с драйвером и копируем его в виртуальную машину. Теперь нам надо создать службу для запуска драйвер. Открываем консоль от имени администратора и вводим следующую команду:
sc create Name type= kernel binPaht= PATH_TO_DRIVER

в моем случае это выглядит так:

image19.png

Также проверить успешность создания можно через реестр.

image16.png

В той же консоли мы можем попробовать запустить нашу службу.
sc start CodebyDriver

image18.png

Отлично, драйвер запустился и мы даже не улетели в синьку, а это всегда приятно. Теперь давайте проверим, выводится ли сообщение от драйвера.
Для этого нам необходимо провести подготовительные работы.

Создадим новый ключ в реестре и назовем его Debug Print Filter.

image23.png

В качестве значения задаем DWORD с именем DEFAULT и определяем данные для значения как 8.

image25.png

Перезагружаем виртуальную машину.

После перезапуска запускаем DebugView данный инструмент находится в архиве Sysinternals, который мы ранее скачали. Ее можно смело скопировать в виртуальную машину.

Запускаем DebugView от имени Администратора и ставим галочку “Capture Kerner”

image20.png

Capture Win32 и Capture Global Win32 можно снять, если летит много сообщений.

Затем запускаем консоль от имени администратора и запускаем службу загрузки драйвера.

image26.png

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

Спасибо за чтение!

P.S: Я сам только начал изучать тему работы с драйверами. Так что если у вас есть предложения или правки по технической части статьи, прошу отписать в комментарии, чтобы я мог внести изменения в статью.
P.P.S: Как вы могли заметить, писать мы будем преимущественно на С++, посему могу посоветовать отличный канал с уроками по С++ — The Cherno.

Понравилась статья? Поделить с друзьями:
  • Как написать взаимное поздравление
  • Как написать взаимно на английском
  • Как написать взаимно вежливы
  • Как написать веять
  • Как написать вешалка