Русские Блоги
Разговор о драйвере устройства PCI для Linux (часть 1)
Некоторые студенты предложили написать драйверы PCI. Я нашел один сегодня. Статья очень длинная. По сути, это более подробный блог о PCI во всей сети. Он разделен на две статьи. Это верхняя часть, продолжение следует.
Чтобы понять драйвер устройства Linux PCI, вы должны сначала понять, что так называемый драйвер устройства Linux PCI фактически включает в себя две части: драйвер устройства Linux PCI и само устройство.
Я не знаю, понимает ли читатель это предложение. Я думаю, что это предложение очень важно. Для драйверов, таких как PCI и USB, мы должны понимать эту концепцию, чтобы понять, как обращаться с PCI и USB в Linux и аналогичных типах шин. Привод. Причина также очень проста: драйвер Linux PCI поставляется вместе с ядром или ядро написало его за вас. Что нам нужно завершить, так это драйвер самого устройства, например драйвер сетевой карты.
Конечно, это не означает, что ядро написало для нас драйвер PCI для Linux. Нам не нужно ничего делать. По крайней мере, вы должны понимать, что делает ядро, чтобы понять, что вы должны делать и как управлять самим устройством.
Это то же самое, что мы должны изучить множество интерфейсов системных вызовов, когда мы изучаем операционную систему. Если вы не знаете этих интерфейсов, как вы используете операционную систему или функции, которые операционная система предоставляет вам? Итак, здесь мы изучим, что делает драйвер PCI для Linux, чтобы мы могли доработать драйвер нашего устройства на этой основе.
В http://tldp.org/LDP/tlk/dd/pci.html в этой статье упоминается:
Код инициализации Linux PCI логически разделен на три части:
(1) Драйвер устройства PCI (то есть драйвер устройства PCI для Linux, упомянутый выше)
Этот драйвер псевдоустройства опрашивает систему PCI от шины 0 и определяет местонахождение всех устройств PCI и мостов PCI в системе. Он устанавливает связанный список структур данных, которые можно использовать для описания топологии системы PCI. И пронумеруйте все найденные мосты PCI.
Этот программный уровень предоставляет услуги, описанные в сокращении bib-pci-bios. Хотя Alpha AXP не предоставляет услуги BIOS, соответствующие функции включены в его версию для Linux.
Код исправления инициализации PCI, относящийся к конкретной системе
И здесь, главным образом, для обсуждения драйвера устройства PCI для Linux, образец кода, содержащий драйвер устройства, будет указан в конце только для справки.
1. Обзор и введение
PCI (Periheral Component Interconnect) имеет три адресных пространства: пространство ввода-вывода PCI, адресное пространство памяти PCI и пространство конфигурации PCI. Среди них пространство ввода-вывода PCI и адресное пространство памяти PCI используются драйвером устройства (то есть драйвером самого устройства, упомянутым выше), а пространство конфигурации PCI используется кодом инициализации PCI Linux. Эти коды используются для настройки устройств PCI, таких как номера прерываний. И ввод / вывод или базовый адрес памяти. Итак, драйвер устройства PCI здесь должен грубо описать, что ядро Linux сделало для нас для драйверов устройств PCI (первичные), а затем то, что мы должны выполнить (вторичные).
(1) Что делает ядро Linux
Проще говоря, ядро Linux в основном выполняет перечисление и настройку устройств PCI; все эти задачи выполняются при инициализации ядра Linux.
Перечисление: Для шины PCI существует устройство, называемое мостом PCI, для соединения родительской шины с дочерней шиной. Как особое устройство PCI, мост PCI в основном включает в себя следующие три типа:
(1). Хост / мост PCI: используется для соединения ЦП и корневой шины PCI, первая корневая шина имеет номер 0. В ПК контроллер памяти также обычно интегрирован в микросхему устройства моста Host / PCI, поэтому мост Host / PCI обычно также называют «набором микросхем северного моста (North Bridge Chipset)».
(2). Мост PCI / ISA: используется для подключения старой шины ISA. Обычно такие устройства, как контроллер прерываний i8359A в PCI, также интегрируются в устройство моста PCI / ISA. Поэтому мост PCI / ISA часто называют «набором микросхем южного моста (набор микросхем южного моста)».
(3). Мост PCI-to-PCI (далее именуемый мостом PCI-PCI): используется для соединения первичной шины PCI (Primary Bus) и вторичной шины (Secondary Bus). Шина PCI, на которой расположен мост PCI-PCI, называется первичной шиной, то есть родительской шиной вторичной шины; шина PCI, подключенная к мосту PCI-PCI, называется вторичной шиной, то есть вспомогательной шиной первичной шины.
Рисунок 1, Принципиальная схема системы PCI
Следующий рисунок взят из спецификации PCI Local Bus Specification Revision 2.1 Вы можете видеть, что код класса моста PCI-PCI (см. Рисунок 3) равен 0x060400.
ЦП подключается к шине PCI через мост Host / PCI, и шина PCI в этом положении называется корневой шиной. Обычно на ПК имеется только один мост Host / PCI. На основе шины PCI он может быть подключен к другим шинам более низкого уровня через мост PCI. Например, он может быть подключен к другой шине PCI через мост PCI-PCI. Мост ISA может быть подключен к шине ISA.
Фактически, шина ISA в современных ПК подключена к шине PCI через мост PCI-ISA. Таким образом, с помощью моста PCI-PCI создается иерархическая древовидная структура системы PCI. Для верхней шины мост PCI, подключенный к этой шине, также является устройством. Но это особенное устройство, это не только устройство на верхней шине, но собственно расширение верхней шины.
Так называемое перечисление предназначено для обнаружения и сканирования с моста Host / PCI, а также «перечисления» всех устройств, подключенных к первой шине PCI, одного за другим и их записи. Если одно из устройств является мостом PCI-PCI, оно будет дополнительно обнаруживать и сканировать вторичную шину PCI, подключенную к этому мосту. Это рекурсивно продолжается до тех пор, пока все устройства PCI в системе не будут исчерпаны.
В результате в памяти создается дерево PCI, представляющее эти шины и устройства PCI. Каждое устройство PCI (включая устройство моста PCI) представлено структурой pci_dev, а каждая шина PCI представлена структурой pci_bus. У вас есть дерево аппаратных устройств, построенное через мост PCI, а у меня есть дерево программного обеспечения, построенное через структуры данных в памяти, что очень гармонично.
Конфигурация: устройства PCI обычно имеют несколько пространств ОЗУ и ПЗУ, и обычные регистры управления / состояния и регистры данных часто появляются в виде интервалов ОЗУ, и адреса этих интервалов обычно адресуются с 0 внутри устройства. , Затем, когда к шине подключено несколько устройств, доступ к этим пространствам будет конфликтовать.
Следовательно, эти адреса необходимо сначала сопоставить с системной шиной, а затем сопоставить с виртуальным адресным пространством ядра. Так называемая конфигурация предназначена для завершения сопоставления адресов с помощью регистров в пространстве конфигурации PCI (завершается только отображение внутреннего адреса на адрес шины, а виртуальное адресное пространство, сопоставленное с ядром, является работой, выполняемой самим устройством. ).
(2) Как это делает ядро Linux
Прежде всего, необходимо объяснить, что инициализацию устройства PCI (то есть перечисление и настройку, упомянутые выше) могут выполнять как BIOS ПК, так и ядро Linux. Вообще говоря, если это ПК, который поддерживает шину PCI, его BIOS должен обеспечивать поддержку операций шины PCI, поэтому он называется PCI BIOS.
И самое раннее ядро Linux также получало информацию об устройстве PCI в системе с помощью этого метода вызова BIOS, но не все платформы имеют BIOS (например, некоторые встроенные системы), и на практике также встречаются некоторые материнские платы. У PCI BIOS есть проблемы такого рода, поэтому позже я перешел на ядро Linux, чтобы сделать это сам, и сделал это сам.
Однако ядро Linux по-прежнему очень внимательно относится к предоставлению нам права выбора в параметрах make menuconfig, то есть режима доступа PCI, который предоставляет четыре варианта: BIOS, MMconfig, Direct и Any. Прямой метод означает, что BIOS остается позади, а ядро само завершает работу по инициализации.
2. Начнем наш путь перечисления и настройки.
Примечание. Для большей ясности просто опишите процесс инициализации устройства PCI (поскольку модель драйвера устройства не была представлена в версии 2.4.18, поэтому мы можем сосредоточиться на самом драйвере устройства PCI). Вот анализ ядра Linux-2.4.18. Основная причина в том, что каждый должен уметь разбираться в справочных материалах, многие из которых анализируются на основе информации в справочных материалах [1]. Если вы хотите изучить драйверы устройств PCI, вам следует внимательно изучить раздел о шине PCI в главе 8 [1]. Затем вы можете найти пример кода драйвера и ознакомиться с ним. Его можно рассматривать как введение в драйверы устройств PCI. Конечно, при условии, что вы понимаете их все, ха-ха.
Прекратите говорить чушь, давайте перейдем к теме ниже. Как упоминалось ранее, PCI имеет три адресных пространства: пространство конфигурации PCI используется для кода инициализации PCI в ядре Linux, что мы и используем здесь для перечисления и настройки. Итак, что помещается в это пространство конфигурации PCI, очевидно, это должен быть регистр, называемый группой регистров конфигурации. Когда устройство PCI включено, оборудование остается неактивным. То есть устройство будет отвечать только на транзакции конфигурации. При включении не будет памяти и портов ввода-вывода, сопоставленных с адресным пространством компьютера на устройстве; другие функции, связанные с устройством, такие как создание отчетов о прерываниях, также отключены.
Стандарт PCI предусматривает, что группа регистров конфигурации каждого устройства может иметь непрерывное пространство размером до 256. Назначение и формат первых 64 байтов являются стандартными, что называется заголовком регистра конфигурации. Система предоставляет некоторые аппаратные механизмы, так что код конфигурации PCI может обнаруживать все возможные заголовки регистров конфигурации PCI на данной шине PCI, чтобы знать, в каком слоте PCI в настоящее время установлено устройство, а в каком слоте в настоящее время нет оборудование. Это делается путем чтения поля в заголовке регистра конфигурации PCI (обычно это поле «Vendor ID»). Если слот пуст, вышеуказанная операция вернет некоторые возвращаемые значения ошибок, например 0xFFFFFFFF.
Существует три вида этого типа заголовка (относящиеся к 64-байтовому заголовку), среди которых заголовок «тип 0» (тип 0) используется для общих устройств PCI, а заголовок «тип 1» используется для различных мостов PCI-PCI. Заголовок «Тип 2» используется для мостов PCI-CardBus. CardBus — это шина, используемая в портативных компьютерах. Нам все равно.
16 байтов в 64-байтовом заголовке содержат информацию о типе заголовка, типе устройства, некоторых свойствах устройства и его создателе. В соответствии с информацией, содержащейся в этих 16 байтах, определите, как дальше интерпретировать и обрабатывать 48 байтов в оставшемся заголовке. Для этих 16-байтовых адресов некоторые константы определены в include / linux / pci.h:
Соответствует первым 16 байтам на нашем рисунке 3 (см. Ниже). И мы также видели, что три типа заголовков, упомянутых выше, определены рядом с PCI_HEADER_TYPE (то есть регистром, в котором хранится тип заголовка):
В системе Linux вы можете использовать такие команды, как cat / proc / pci, для просмотра информации о категории, модели и производителе всех устройств PCI в системе, которая поступает из этих регистров. Ниже приведен перехват информации с помощью команды lspci -x на виртуальной машине (команда lspci также использует файл / proc в качестве источника информации):
Первое, что следует отметить, это то, что регистры PCI имеют формат с прямым порядком байтов. Тогда, согласно структуре самой младшей группы регистров конфигурации PCI (рис. 3), очевидно, что идентификатор поставщика этого хост-моста — 0x8086, я не говорю, что вы можете догадаться, что этот поставщик — Intel.
Есть проблема, которую нужно уточнить в первую очередь, то есть адрес этих регистров, иначе не будет. Регистр конфигурации позволяет нам выполнить настройку для завершения доступа к пространству хранения на устройстве PCI, но сами эти регистры конфигурации также расположены в адресном пространстве устройства PCI, способ доступа к этой части пространства стал точкой входа для всей нашей работы по инициализации. Как и у каждой исполняемой программы должна быть точка входа.
Метод, принятый PCI, состоит в том, чтобы заставить наборы регистров конфигурации всех устройств использовать один и тот же адрес, который отличается мостом PCI шины, где во время доступа добавляются другие условия. ЦП отправляет команды «мосту хост-PCI» через унифицированный адрес входа, а соответствующий мост PCI косвенно выполняет определенные операции чтения и записи. Для процессора архитектуры i386 разработчик шины PCI зарезервировал для этой цели 8 байтов в адресном пространстве ввода-вывода, то есть 0xCF8
Эти 8 байтов составляют два 32-битных регистра, первый — это «адресный регистр» 0xCF8, а второй — «регистр данных» 0xCFC. Чтобы получить доступ к определенному регистру конфигурации в определенном устройстве, ЦП сначала записывает целевой адрес в регистр адреса, а затем считывает и записывает данные через регистр данных. Однако целевой адрес, записанный в адресный регистр, представляет собой полный адрес, включающий номер шины, номер устройства, номер функции и адрес регистра устройства. Формат показан на рисунке 2:
Рисунок 2, полный адрес, записанный в адресный регистр 0xCF8
Номер шины, номер устройства и номер функции здесь являются расширением адреса регистра конфигурации, что является дополнительными условиями, упомянутыми выше. Во-первых, каждая шина PCI имеет номер шины, номер шины главной шины равен 0, а остальная часть назначается ЦП каждый раз, когда мост PCI обнаруживается во время фазы перечисления, и увеличивается по очереди. Номер устройства обычно представляет собой интерфейсную карту PCI (точнее, микросхему интерфейса шины PCI) и обычно зависит от расположения слота. На каждой интерфейсной плате PCI может быть несколько функциональных модулей, и эти функциональные модули совместно используют микросхему интерфейса шины PCI, включая электронные схемы для сопоставления адресов, для снижения затрат.
С логической точки зрения каждая «функция» на самом деле является устройством (люди, которые видели драйверы USB-устройств, знакомы с ней, ха-ха), поэтому комбинацию номера устройства и номера функции можно назвать «номером логического устройства». И каждая карта может вместить до 8 устройств.
Очевидно, что комбинация этих полей (относящихся ко всем 32-битным) однозначно определяет логическое устройство PCI в системе. Вначале можно получить доступ только к шине № 0. При сканировании шины № 0, если одно из вышеперечисленных устройств оказывается мостом PCI, назначьте новый номер шины, например 1, чтобы можно было получить доступ к шине № 1. Это Одна из задач переписного этапа.
Теперь читателям предлагается рассмотреть вопрос: когда мы получим сетевую карту PCI, подключим ее к материнской плате ПК и планируем написать драйвер для этой сетевой карты. Итак, что должно быть первым шагом? Читатели могут просмотреть предыдущий контент, поскольку мы сказали, что ядро Linux помогло нам выполнить перечисление и настройку устройства, прежде чем я напишу драйвер сетевой карты, могу ли я взглянуть на перечисление, выполненное ядром Linux для нашего устройства сетевой карты PCI? Результат работы? Или, прямо говоря, воткнул сетевую карту, ядро линукса теперь распознает это устройство? Обратите внимание, что концепция идентификации устройства и устройства, которое можно использовать в обычном режиме, — это разные концепции, которые легко понять.
Любой, кто установил драйвер сетевой карты ПК, знает, что, когда драйвер устройства не установлен, мы можем видеть устройство в диспетчере устройств, но на нем есть большой желтый знак вопроса. В системе Linux мы можем использовать команду lspci для просмотра.
Ниже приведен фрагмент из главы LDD3, посвященной драйверам PCI: вывод lspci (часть pciutils, доступная в большинстве выпусков) и расположение информации в / proc / pci и / porc / bus / pci. Представление sysfs устройств PCI также показывает эту схему адресации, а также информацию домена PCI. При отображении аппаратного адреса он может отображаться как 2 значения (8-битный номер шины и 8-битный номер устройства и функции. ), как 3 значения (шина, устройство и функция) или как 4 значения (домен, шина, устройство и функция); все значения часто отображаются в шестнадцатеричном формате.
Например, / proc / bus / pci / devices использует одно 16-битное поле (для облегчения анализа и сортировки), а / proc / bus / busnumber делит адрес на 3 поля. Ниже показано, как отображаются эти адреса, только Начало выходной строки:
Все три списка устройств расположены в одном порядке, потому что lspci использует файл / proc в качестве источника информации. В качестве примера возьмем видеоконтроллер VGA, 0x00a0 означает 0000: 00: 14.0 при разделении на домен (16 бит), шину (8 бит), устройство (5 бит) и функцию (3 бита). Почему 0x00a0 соответствует 0000 : 00: 14.0, это зависит от содержимого на рисунке 2. Согласно регистру на рисунке 2, регистр, соответствующий 0x00a0, представляет шину (8 бит), устройство (5 бит) и функцию (3 бита). 0x00a0 = 0000000010100000, Легко видеть, что старшие 8 бит — это номер шины, который равен 0. Оставшийся 0xa0 = 10100000, можно увидеть, что если младшие 3 цифры представляют номер функции, то оставшиеся 10100 — это номер устройства, а 8-значное значение — 00010100 или 0x14.
Рисунок 3, группа регистров конфигурации PCI
[1] Сценарный анализ исходного кода ядра Linux (Том 2)
[2] Подробное объяснение разработки драйверов устройств Linux.
[3] Драйвер устройства Linux (третье издание)
[4] pci.txt в документации ядра
[5] Опытный специалист в области разработки драйверов устройств для Linux.
В этой статье около 20 000 слов, поэтому она будет распространена дважды, продолжение следует .
Источник