РАЗБОР
Статья
Сетевой адаптер Virtio-Net[1] - от устройства к драйверу
Режим чтения
[ обложка статьи ]
В статье рассмотрим программный интерфейс паравиртуализации VirtIO от компании RedHat, который поддерживает широкий круг физ.оборудования и используется в таких гипервизорах как: Qemu, Xen, Cloud HV, Proxmox VE, oVirt, OpenStack, и VirtualBox. Основной акцент сделаем на создание драйвера сетевого адаптера Virtio-Net, что позволит взлянуть на сеть с абсолютно нового ракурса. Материал получился большим, поэтому я разделил его на 2 части - в первой само вирт.устройство, а во-второй программное взаимодействие с ним.
Оглавление - часть(1) :
1. Общие сведения
Virtio - это стандарт паравиртуализации для эффективного взаимодействия между гостевой ОС и гипервизором. При обычной вирт гипервизор вынужден полностью эмулировать работу реального железа - это создаёт нагрузку на ЦП, т.к. каждый запрос гостя к устройству перехватывается, декодируется и эмулируется на уровне гипервизора.
Проблему решает паравиртуализация. Здесь вместо эмуляции реального железа, гость получает производительный интерфейс для обмена данными с хостом. Для этого в гостевой ОС устанавливается специальный драйвер Front-End (который мы позже напишем), а на стороне гипервизора qemu работает обслуживающий Back-End. Транспорт между ними построен на разделяемой памяти и кольцевых буферах, что позволяет передавать данные практически без копирования.
Если в обычной схеме драйвер адаптера NIC напрямую управляет физ.устройством, то в паравиртуализации с одним вирт.девайсом работают две согласованные части: Front-End отправляет и принимает запросы через интерфейс без намёков на эмуляцию, а Back-end в qemu обрабатывает эти запросы и при необходимости взаимодействует с реальным оборудованием хоста. Выигрыш достигается за счёт разгрузки кода гипервизора, поскольку обращения к физ.девайсам происходят только, когда это действительно необходимо, например при маршрутизации сетевых пакетов через TAP во внешнюю сеть.
Virtio заточен под Qemu/KVM и способен эмулировать зоопарк из более 40 различных устройств, хотя даже последняя спека v1.4 описывает лишь некоторые из них. Будучи установленной как гость, Linux из коробки имеет полную поддержку в виде гостевых драйверов, а вот для остальных ОС придётся скачивать их отдельно. Чтобы далее не было путаницы, сразу определим понятие "Device" (программный эмуль на хосте) и "Driver" на стороне клиента, от имени которого мы и будем выступать:
Особенностью VirtIO является двусторонний обмен драйвера с устройством через разделяемую память. По сути это хорошо известный механизм проецирования "Memory Mapping", когда 2 разных процесса имеют совместный доступ к одному фрейму физической ОЗУ. Обратите внимание, что для каждой из гостевых VM, хост создаёт отдельные экземпляры устройств Virtio-Net, у которых будет свой МАС и своя разделяемая память. При желании гости могут общаться между собой по внутренней сети Ethernet, однако доступ к чужому кольцевому буферу VirtQueue у них закрыт.
По умолчанию Qemu создаёт сетевой стек TCP/IP с массой ограничений (режим SLiRP). Гость находится как-будто за фаерволом, который полностью блокирует входящий трафик из вне. Если нужен полноценный обмен или выход в Инет, можно сменить конфиг например на мост Bridge. Cервер DHCP (Dynamic Host Configuration Protocol) играет роль шлюза Gateway и имеет постоянную прописку по адресу
2. Установка QEMU на Windows
Если хотите практики, нужно установить на свою основную систему эмулятор Qemu. У меня Win7-x64, а макс.поддерживаемая версия для неё Qemu_v6.2. Для Win скачать бинарные установщики *.exe можно от сюда, а обладатели Linux как правило тянут софт напрямую из официальных репозиториев через встроенный пакетный менеджер. Хэлп для юзера лежит здесь.
После установки, qemu в Win запускается через bat-файл, в котором нужно задать ключи конфигурации, и перечислить типы требуемых устройств (чипсет, диск, сеть и другое). Настроек здесь как звёзд на небе, поэтому начинающие могут утонуть в них. Однако радует, что разработчики зашили в qemu дефолтные установки, и если не указать конкретные опции в *.bat/sh, эмуль запускается в штатном режиме по умолчанию.
Проблема, с которой столкнулся лично я в своей v6.2 - это отсутствие прокрутки экрана во встроенном мониторе аккордом
Если запустить этот батник, получим окно с логами трассировки, отдельный терминал telnet с привязкой к монитору qemu, ну и собственно GUI-окно клиентской ОС, которой пока у нас нет, но позже напишем загрузчик и небольшое ядро для тестов драйвера сети. Именно поэтому мы не подключаем сейчас в батнике образ харда опцией
Зато эмуль возвратил нам дефолтный конфиг сети где видно, что DHCP-сервер и вправду присвоил нам указанный выше клиентский IP
Справку по всем параметрам команды "info" возвращает
3. Программный поиск VirtIO на шине PCI
Qemu полностью эмулирует аппаратную часть системы, а потому гостевая VM в нашем лице получит всё, включая биос и конфиг-пространство PCI для поиска и настройки вирт.оборудования. Каждому из найденных устройств, в пространстве pci выделяется блок размером 256 байт, куда биос сохраняет паспорт девайса и его требования к ОС.
В младенчестве Net работало как Legacy-устройство (спека v0.9.5), но позже появился причёсанный режим Modern v1.1+. Таким образом, сейчас весь зоопарк Virtio-устройств может функционировать в смешанном режиме "Transitional", когда хост qemu сам понимает, что от него хочет драйвер и авто-переключается в нужный режим. Основное их отличие - метод доступа к регистрам устройств: Legacy использует исключительно порты ввода-вывода PIO, а в Modern регистры отображаются в регион системной памяти MMIO (memory mapped input-otput).
Pci-Cfg любого устройства начинается с идентификаторов Vendor/Device. Вендором для Virtio является компания RedHat, которой выделен
Не последнюю роль играют и коды DevID в поле Subsystem по смещению
С приходом Modern разрабы не отправили Legacy торжественно на свалку - режим до сих пор поддерживается qemu, т.к. прост и надёжен как автомат Калашникова. Да, новый имеет много дополнительных плюшек, но для обычной работы сетевого адаптера они попросту не нужны - главное, чтобы пакеты курсировали по линку туда-обратно, с чем вполне справляется и Legacy. Поэтому на практике мы реализуем именно его, а Modern рассмотрим лишь в теории (по сути те-же регистры, только не в порту, а в памяти).
4. Доступ к пространству PCI
Функция
В регистре
Таким образом, код на ассемблере fasm ниже находит устройство Virtio-Net в пространстве PCI, и возвращает нам номер порта, через который позже будем взаимодействовать с регистрами сетевого адаптера. При чтении данных этим прерыванием, смещение внутри 256-байтного блока указывается в регистре
Ниже представлена общая схема работы с устройством Virtio-Net.
Всё, что расположено после
Когда адаптер в режиме Modern, тактика игры меняется. Здесь порт уходит на скамейку запасных, а доступ к отображённым в память регистрам открывает
Всё тем-же прерыванием
Основные регистры адаптера Net в режиме Modern хранит блок COMMON_CFG, поэтому мы должны найти его по полю
Получив таким образом физ.адрес, мы попадаем в структуру COMMON_CFG, которую спека описывает так. Обратите внимание, что аналогичные поля присутсвуют и в регистрах порта PIO режима Legacy, просто в память MMIO были добавляены ещё сдесяток регистров, для описания свойств кольцевых буферов Queue:
В своей демо-ОС я вывел более подробные сведения о возможностях Capability, включая фрагменты структур из MMIO. Напомню, что современный режим рассматривается здесь лишь потому, что адаптер находится сейчас в подвешенном состоянии "Transitional", т.е. мы можем вогнать его хоть в Legacy, хоть в Modern. Просто бородатый Legacy намного проще для понимания, поэтому я выбрал именно его. На скрине ниже конфиги ещё не настроены, а потому отдельные регистры по адресу
5. Механизм CAM/ECAM
Долгое время роль основного транспорта на мат.платах исполняла пар-шина PCI, но сейчас её вытеснила последовательная PCI-Ex с более высокой пропускной способностью. Устройства на этих шинах принципиально отличаются, и чтобы описать характеристики девайса PCI-Ex, стандартных 256 байт в конфиг-пространстве уже не хватает - они требуют до 4КБ памяти, хотя реально не используют и половины.
Но проблема в том, что классический доступ к PCI через порты
В целях совместимости, устройства PCIe поддерживают оба механизма, но в случае САМ доступны только штатные 256 байт из общих 4КБ. В доках на qemu упоминается, что дефолтной базой для САМ является адрес
Адресация устройств на обоих шинах трёхмерная
Если вам понадобится найти базу механизма ECAM (задача довольно распространённая для осдевов), вот реализация, которую я использовал в своём ядре. Расширенную карту памяти можно получить и командой
6. Заключение
Виртуальный адаптер сети Virtio-Net довольно мощная штука, с огромным кол-вом настроек на все случаи жизни. По сути громкое имя RedHat уже говорит само за себя. Это профессионалы своего дела, которые грамотно и с нуля разработали уникальный в своём роде программный интерфейс - лично я ничего подобного больше нигде не встречал.
В следующей части статьи мы рассмотрим логику его работы, и напишем небольшую ОС реального режима для тестов. Для тех, кому мешает языковой барьер, вот спецификация в формате html, чтобы прямо в браузере могли перевести её на русский. Всем удачи, до скорого!
Оглавление - часть(1) :
1. Общие сведения
2. Установка Qemu на Windows
3. Программный поиск Virtio-Net на шине PCI
4. Доступ к пространству конфигурации
5. Механизм ECAM для PCI-Ex
6. Заключение
1. Общие сведения
Virtio - это стандарт паравиртуализации для эффективного взаимодействия между гостевой ОС и гипервизором. При обычной вирт гипервизор вынужден полностью эмулировать работу реального железа - это создаёт нагрузку на ЦП, т.к. каждый запрос гостя к устройству перехватывается, декодируется и эмулируется на уровне гипервизора.
Проблему решает паравиртуализация. Здесь вместо эмуляции реального железа, гость получает производительный интерфейс для обмена данными с хостом. Для этого в гостевой ОС устанавливается специальный драйвер Front-End (который мы позже напишем), а на стороне гипервизора qemu работает обслуживающий Back-End. Транспорт между ними построен на разделяемой памяти и кольцевых буферах, что позволяет передавать данные практически без копирования.
Если в обычной схеме драйвер адаптера NIC напрямую управляет физ.устройством, то в паравиртуализации с одним вирт.девайсом работают две согласованные части: Front-End отправляет и принимает запросы через интерфейс без намёков на эмуляцию, а Back-end в qemu обрабатывает эти запросы и при необходимости взаимодействует с реальным оборудованием хоста. Выигрыш достигается за счёт разгрузки кода гипервизора, поскольку обращения к физ.девайсам происходят только, когда это действительно необходимо, например при маршрутизации сетевых пакетов через TAP во внешнюю сеть.
Virtio заточен под Qemu/KVM и способен эмулировать зоопарк из более 40 различных устройств, хотя даже последняя спека v1.4 описывает лишь некоторые из них. Будучи установленной как гость, Linux из коробки имеет полную поддержку в виде гостевых драйверов, а вот для остальных ОС придётся скачивать их отдельно. Чтобы далее не было путаницы, сразу определим понятие "Device" (программный эмуль на хосте) и "Driver" на стороне клиента, от имени которого мы и будем выступать:
Особенностью VirtIO является двусторонний обмен драйвера с устройством через разделяемую память. По сути это хорошо известный механизм проецирования "Memory Mapping", когда 2 разных процесса имеют совместный доступ к одному фрейму физической ОЗУ. Обратите внимание, что для каждой из гостевых VM, хост создаёт отдельные экземпляры устройств Virtio-Net, у которых будет свой МАС и своя разделяемая память. При желании гости могут общаться между собой по внутренней сети Ethernet, однако доступ к чужому кольцевому буферу VirtQueue у них закрыт.
По умолчанию Qemu создаёт сетевой стек TCP/IP с массой ограничений (режим SLiRP). Гость находится как-будто за фаерволом, который полностью блокирует входящий трафик из вне. Если нужен полноценный обмен или выход в Инет, можно сменить конфиг например на мост Bridge. Cервер DHCP (Dynamic Host Configuration Protocol) играет роль шлюза Gateway и имеет постоянную прописку по адресу
10.0.2.2. Своим гостям он назначает IP начиная с 10.0.2.15 ..16, и т.д. Таким образом, чтобы проверить работоспособность сети, мы можем отправить ARP-запрос серверу DHCP, и он должен вернуть нам МАС маршрутизатора, частью которого и является.
Код:
guest (10.0.2.15) <--+--> DHCP server (10.0.2.2) /Firewall <---> Internet
|
+--> DNS server (10.0.2.3)
|
+--> SMB server (10.0.2.4)
2. Установка QEMU на Windows
Если хотите практики, нужно установить на свою основную систему эмулятор Qemu. У меня Win7-x64, а макс.поддерживаемая версия для неё Qemu_v6.2. Для Win скачать бинарные установщики *.exe можно от сюда, а обладатели Linux как правило тянут софт напрямую из официальных репозиториев через встроенный пакетный менеджер. Хэлп для юзера лежит здесь.
После установки, qemu в Win запускается через bat-файл, в котором нужно задать ключи конфигурации, и перечислить типы требуемых устройств (чипсет, диск, сеть и другое). Настроек здесь как звёзд на небе, поэтому начинающие могут утонуть в них. Однако радует, что разработчики зашили в qemu дефолтные установки, и если не указать конкретные опции в *.bat/sh, эмуль запускается в штатном режиме по умолчанию.
Проблема, с которой столкнулся лично я в своей v6.2 - это отсутствие прокрутки экрана во встроенном мониторе аккордом
Ctrl+Alt+Up/Down, в результате чего на запрос help мне доступны только несколько последних строк. Поэтому я подключился к монитору через внешний telnet-клиент на хосте, и теперь могу не только стандартно прокручивать экран мышью, но и копировать с него информацию для вставки сюда. Вот пример моего батника, который (в порядке следования строк) проделывает следующее..1. Запускает клиента telnet на хосте, сразу указывая адрес и порт сервера qemu
2. Определяет эмуляцию именно cpu х86_64, а не х32 или arm
3. Выбирает современный чипсет Q35 с шиной PCIe вместо древнего i440fx, 2 ядра для ЦП, и 256 МБ озу для клиентской ОС
4. Синхронизирует время с хостом, т.к. у qemu в дефолте стоит UTC
5. Включает логи обращений со стороны хоста к нашему драйверу, и его очередям queue
6. Указывает, что в качестве сетевого адаптера использовать Virtio-Net на шине PCI
7. Активирует доступ к монитору qemu через telnet на хосте
Bash:
start cmd.exe /k telnet 127.0.0.1 4444
qemu-system-x86_64 ^
-machine q35 -smp 2 -m 256M ^
-rtc base=localtime,clock=host ^
-trace "virtio_*" ^
-trace "virtqueue_*" ^
-nic user,model=virtio-net-pci,hostfwd=tcp::5555-:80 ^
-monitor telnet:127.0.0.1:4444,server,nowait
Если запустить этот батник, получим окно с логами трассировки, отдельный терминал telnet с привязкой к монитору qemu, ну и собственно GUI-окно клиентской ОС, которой пока у нас нет, но позже напишем загрузчик и небольшое ядро для тестов драйвера сети. Именно поэтому мы не подключаем сейчас в батнике образ харда опцией
-drive xx, и qemu в последней строке предупреждает нас об этом "No bootable device":Зато эмуль возвратил нам дефолтный конфиг сети где видно, что DHCP-сервер и вправду присвоил нам указанный выше клиентский IP
10.0.2.15, а себе как шлюзу Gateway взял 10.0.2.2. Чтобы собрать более подробную инфу о самом устройстве Virtio-Net, можно ввести в терминале telnet команду info pci:
Код:
(qemu) info pci
........
Bus 0, device 2, function 0:
Ethernet controller:
PCI device 1af4:1000
PCI subsystem 1af4:0001
IRQ 11, pin A
BAR0: I/O port at 0xc040 [size=0x20] <------------ 32 байта
BAR4: prefetch mem at 0xfe000000 [size=0x4000] <--- 16 Kбайт
id ""
(qemu)
Справку по всем параметрам команды "info" возвращает
help info, причём помощь можно запрашивать прямо в аргументах батника. Например если указать model=help, получим список всех поддерживаемых qemu сетевых адаптеров:
Код:
Supported NIC models:
e1000
e1000-82544gc
e1000-82545em
e1000e
i82550
i82551
i82557a,b,c
i82558a,b
i82559a,b,c,er
i82562
i82801
ne2k_pci
pcnet
rtl8139
tulip
virtio-net-pci <---------------- наш пациент
virtio-net-pci-non-transitional
virtio-net-pci-transitional
vmxnet3
3. Программный поиск VirtIO на шине PCI
Qemu полностью эмулирует аппаратную часть системы, а потому гостевая VM в нашем лице получит всё, включая биос и конфиг-пространство PCI для поиска и настройки вирт.оборудования. Каждому из найденных устройств, в пространстве pci выделяется блок размером 256 байт, куда биос сохраняет паспорт девайса и его требования к ОС.
В младенчестве Net работало как Legacy-устройство (спека v0.9.5), но позже появился причёсанный режим Modern v1.1+. Таким образом, сейчас весь зоопарк Virtio-устройств может функционировать в смешанном режиме "Transitional", когда хост qemu сам понимает, что от него хочет драйвер и авто-переключается в нужный режим. Основное их отличие - метод доступа к регистрам устройств: Legacy использует исключительно порты ввода-вывода PIO, а в Modern регистры отображаются в регион системной памяти MMIO (memory mapped input-otput).
Pci-Cfg любого устройства начинается с идентификаторов Vendor/Device. Вендором для Virtio является компания RedHat, которой выделен
ID=0x1AF4. А вот устройств у него может быть вагон и целая тележка, поэтому код DevID плавает в диапазоне от 0x1000 и далее, причём в режиме Modern база меняется на 0x1040. С полным списком DeviceID можно ознакомиться здесь, а табличку ниже я собрал из последней спеки v1.4 от 2026 года (всего 49 устройств):Не последнюю роль играют и коды DevID в поле Subsystem по смещению
0x2E от начала PciCfg - они позволяют однозначно определить тип устройства. Коды в режиме Modern формируются по схеме: Base(0x1040)+SubSys. Обратите внимание, что в Legacy часть оборудования не поддерживается вообще, хотя герой данной статьи Virtio-Net занял в этом списке почётное первое место.С приходом Modern разрабы не отправили Legacy торжественно на свалку - режим до сих пор поддерживается qemu, т.к. прост и надёжен как автомат Калашникова. Да, новый имеет много дополнительных плюшек, но для обычной работы сетевого адаптера они попросту не нужны - главное, чтобы пакеты курсировали по линку туда-обратно, с чем вполне справляется и Legacy. Поэтому на практике мы реализуем именно его, а Modern рассмотрим лишь в теории (по сути те-же регистры, только не в порту, а в памяти).
4. Доступ к пространству PCI
Функция
AH=0xB1 прерывания биос INT-1Ah отвечает за работу с PciConfigSpace.В регистре
AL указывается номер под-функции в сл.порядке:
Код:
AL = 0x02 : Поиск устройства по Ven/Dev = 1AF4:1000 (возвращает адрес BDF в регистре BX)
AL = 0x08 : Прочитать байт из PciCfg по адресу BDF (возвращает в регистр CL)
AL = 0x09 : Прочитать слово 2 байта (возвращает в регистр CX)
AL = 0x0А : Прочитать двойное слово 4 байта (возвращает в регистр ЕCX)
Таким образом, код на ассемблере fasm ниже находит устройство Virtio-Net в пространстве PCI, и возвращает нам номер порта, через который позже будем взаимодействовать с регистрами сетевого адаптера. При чтении данных этим прерыванием, смещение внутри 256-байтного блока указывается в регистре
DI.
Код:
; VirtioNet BDF
mov dx,0x1AF4 ; Vendor
mov cx,0x1000 ; Device
xor si,si ; первое устройство (для много-функциональных)
mov ax,0xB102 ; поиск!
int 1Ah ; в BX получили адрес на шине Bus:Device:Func
; BDF нужен для сл.функции чтения
; Взять порт I/O из BAR0
mov ax,0xB10A ; читать дворд
mov di,0x10 ; смещение = BAR[0]
int 1Ah ; в регистре ECX получили номер порта Virtio-Net
dec cx ; мл.бит это флаг - сбросить его!
mov [BAR0],cx ; сохранить номер порта в переменной
Ниже представлена общая схема работы с устройством Virtio-Net.
Всё, что расположено после
BAR[0] относится к Modern, и если вообще не обращаться к эти полям, хост сам переключит адаптер в Legacy. Порт PIO открывает доступ к регистрам, которыми можно оперировать инструкциями in/out (чтение-запись). Например такой незадачливый код вернёт состояние линка - подключён вирт.сетевой кабель к маршрутизатору на хосте, или нет:
Код:
mov dx,[BAR0] ; DX = номер порта
add dx,0x1A ; выбираем в нём регистр "Net_Status"
in al,dx ; прочитать байт в AL
cmp al,1 ; тест на 1 (статус)
jz @ok ; если равно - кабель подключён,
;.... ; иначе что-то не настроено в конфигах.
Когда адаптер в режиме Modern, тактика игры меняется. Здесь порт уходит на скамейку запасных, а доступ к отображённым в память регистрам открывает
BAR[4] и 1-байтное значение из поля CapPointer. Оно хранит смещение к первой структуре VIRTIO_PCI_CAP, а всего имеется как минимум 5 таких структур (см ID выше). Каждая размером 16-байт и описывает один (конкретно взятый) блок регистров в памяти MMIO. Адрес внешнего блока собирается из 2-х составляющих: база для всех лежит в BAR[4], а смещение - в поле Offset структуры. Размер блока в поле Length как-правило равен 0x1000 или 4КБ (все блоки регистров выровнены в памяти на эту границу).Всё тем-же прерыванием
int-1Ah можно вывести дамп конфиг-пространства Virtio-Net, чтобы продемонстрировать, как это выглядит в реале. Здесь значение CapPointer=84h и сместившись к нему видим первую структуру PCI_CAP в массиве - вот её поля:
Код:
Vndr = 09h (нужный нам маркер, остальные пропускаем)
Next = 70h (смещение к сл.структуре в массиве)
Len = 14h (размер этой структуры, обычно 10 или 14)
Type = 05h (тип внешнего блока: здесь 5=PCI_CFG, а нам нужен основной 1=COMMON_CFG)
Bar = 00h (это порт PIO, значит PCI_CFG настраивается через Legacy - нам не нужно)
Offs = 00000000h, Length = 00000000h
Основные регистры адаптера Net в режиме Modern хранит блок COMMON_CFG, поэтому мы должны найти его по полю
Type=1 в каждой из структур PCI_CAP. В данном случае видно, что сл.структура лежит по Next=70h и Type у неё равен 2, значит идём далее-далее и наконец обнаруживаем Type=01 по смещению 40h, при чём она является последней, т.к. поле Next=00. Здесь видим BAR[4] и Offset=0, т.е. это самый первый блок в памяти MMIO по адресу 0xFE000000.Получив таким образом физ.адрес, мы попадаем в структуру COMMON_CFG, которую спека описывает так. Обратите внимание, что аналогичные поля присутсвуют и в регистрах порта PIO режима Legacy, просто в память MMIO были добавляены ещё сдесяток регистров, для описания свойств кольцевых буферов Queue:
Код:
struct VIRTIO_PCI_COMMON_CFG ;<------ cfg_type(1)
device_feature_select dd 0 ; (00) RW: 0 = только биты[0:31] функций активны, 1 = все биты[0:63]
device_feature dd 0 ; (04) R : (!)--> предлагаемые хостом функции
driver_feature_select dd 0 ; (08) RW: 0 = только биты[0:31] функций активны, 1 = все биты[0:63]
driver_feature dd 0 ; (0C) RW: (!)--> драйвер выбирает функции хоста
msix_config dw 0 ; (10) RW: конфиги MSI-X, 0 = отключён, тогда активен ISR в порту PIO
num_queues dw 0 ; (12) R : общее кол-во очередей для данного девайса Virtio (Net,Block,etc)
device_status db 0 ; (14) RW: (!)--> драйвер записывает статус операции, например "RESET=0" или "OK=4"
config_generation db 0 ; (15) R : счётчик конфигов (хост делает +1, когда заметно меняется конфигурация)
;----> О конкретной "virtqueue"
queue_select dw 0 ; (16) RW: (!)--> драйвер выбирает очередь для конфигурации (отсчёт с нуля),
queue_size dw 0 ; (18) RW: (!)--> ..а хост возвращает её размер в дескрипторах.
queue_msix_vector dw 0 ; (1A) RW: драйвер указывает вектор очереди, если msi-x активны
queue_enable dw 0 ; (1C) RW: драйвер записывает 1, чтобы активировать выбранную очередь (можно временно вкл/выкл)
queue_notify_off dw 0 ; (1E) R : драйвер считывает это значение, чтобы вычислить смещение от начала "структуры уведомлений"
queue_desc dq 0 ; (20) RW: (!)драйвер записывает сюда физ.адрес области дескрипторов
queue_driver dq 0 ; (28) RW: Avail - драйвер записывает сюда физ.адрес кольца доступных буферов
queue_device dq 0 ; (30) RW: Used - драйвер записывает сюда физ.адрес кольца обработанных
ends
В своей демо-ОС я вывел более подробные сведения о возможностях Capability, включая фрагменты структур из MMIO. Напомню, что современный режим рассматривается здесь лишь потому, что адаптер находится сейчас в подвешенном состоянии "Transitional", т.е. мы можем вогнать его хоть в Legacy, хоть в Modern. Просто бородатый Legacy намного проще для понимания, поэтому я выбрал именно его. На скрине ниже конфиги ещё не настроены, а потому отдельные регистры по адресу
0xFE000000 не валидны:5. Механизм CAM/ECAM
Долгое время роль основного транспорта на мат.платах исполняла пар-шина PCI, но сейчас её вытеснила последовательная PCI-Ex с более высокой пропускной способностью. Устройства на этих шинах принципиально отличаются, и чтобы описать характеристики девайса PCI-Ex, стандартных 256 байт в конфиг-пространстве уже не хватает - они требуют до 4КБ памяти, хотя реально не используют и половины.
Но проблема в том, что классический доступ к PCI через порты
0xCF8/0xCFC включая int-1Ah:AH=B1h, способен адресовать лишь первые 256 байт, т.к. для выбора регистра (аля смещение) в этом механизме под названием САМ (config access mechanism) выделяется всего 8 бит. Поэтому для устройств PCI-Ex был введён расширенный ECAM, в котором используются уже не порты, а прямой доступ к памяти MMCFG. Теперь читать и записывать конфиги можно обычной инструкцией mov, но для этого требуется база сегмента pcie-mmcfg.В целях совместимости, устройства PCIe поддерживают оба механизма, но в случае САМ доступны только штатные 256 байт из общих 4КБ. В доках на qemu упоминается, что дефолтной базой для САМ является адрес
0xE0000000, хотя он может съехать в зависимости от объёма установленной ОЗУ. А вот с ECAM дела обстоят иначе - здесь базовый адрес прописывается биосом в лист MCFG системной таблицы ACPI (advanced config & power interface), а потому заранее не известен.Адресация устройств на обоих шинах трёхмерная
Bus:Device:Func, при этом согласно спецификации всего может быть шин(256), девайсов(32) на одной шине, и функций(8) у девайса. Таким образом, если нам известна база mmcfg и BDF устройства в системе (см int-1Ah:AX=B102h), мы сможем найти его пространство используя простую формулу ниже. Обратите внимание на кол-во выделенных бит под регистры - это 8 и 12. Они не явно задают размер блока конфигурации для одного устройства. То-есть мы должны организовать поиск с шагом или 256, или 4096-байт, в зависимости от используемого подхода:Если вам понадобится найти базу механизма ECAM (задача довольно распространённая для осдевов), вот реализация, которую я использовал в своём ядре. Расширенную карту памяти можно получить и командой
info mtree в мониторе qemu (в данном случае терминале telnet):6. Заключение
Виртуальный адаптер сети Virtio-Net довольно мощная штука, с огромным кол-вом настроек на все случаи жизни. По сути громкое имя RedHat уже говорит само за себя. Это профессионалы своего дела, которые грамотно и с нуля разработали уникальный в своём роде программный интерфейс - лично я ничего подобного больше нигде не встречал.
В следующей части статьи мы рассмотрим логику его работы, и напишем небольшую ОС реального режима для тестов. Для тех, кому мешает языковой барьер, вот спецификация в формате html, чтобы прямо в браузере могли перевести её на русский. Всем удачи, до скорого!
AI-выжимка
сгенерировано ИИ
Тезисы статьи скоро
Статья читается полностью. Тезисы со ссылками на разделы появятся позже.
Содержание
Codeby Academy
Практика и мастерство
От основ до продвинутого — программы для практиков Codeby.
Перейти к курсу →
Поиск в обсуждении
Карта ветки
Продолжить чтение
Следующий разбор
Pwn2Own Berlin 2026 итоги: 47 zero-day за три дня
Ещё по теме
- Статья
- Статья
Комментарии
0