Чтение идентификатора поставщика USB-устройства и идентификатора устройства из конфигурационного пространства PCI (EFI)

Я хочу получить идентификатор поставщика и идентификатор устройства для подключенного USB-устройства через программу EFI. Я могу прочитать все пространство конфигурации PCI. Я нахожу хост-контроллер USB, к которому подключено мое USB-устройство. Я также могу прочитать всю память, адресованную этому контроллеру, но я не знаю, что именно я ищу в этой памяти, чтобы получить эти идентификаторы. Кто-нибудь может мне помочь?


person elklepo    schedule 17.01.2016    source источник


Ответы (3)


Протоколы USB определяют пакет настройки, который драйвер xHCI должен создать в xHCI, а оборудование xHCI преобразует его в USB - нет регистров, к которым он обращается напрямую для этой информации, как в случае с PCIe. Во-первых, я выложу всю процедуру перечисления USB-устройств на windows, которая отличается при использовании xHCI или eHCI.

Любые драйверы фильтра нижнего уровня, указанные в значении LowerFilters ключа перечисления устройства.

После аппаратного сброса все порты корневого концентратора будут отключены. Порт будет включен и будет ожидать подключения устройства. Когда оборудование обнаруживает подключение устройства, оно устанавливает флаги текущего состояния подключения и изменения состояния подключения в регистре PORTSC на 1, и это действие приведет к повышению уровня сигнала PSCEG одновременно с логическим ИЛИ в регистре PORTSC. биты. Этот сигнал генерирует событие изменения состояния порта в контроллере, которое заставляет оборудование контроллера xHCI помещать пакет (известный как блок запроса передачи) в кольцо событий. Сегменты и таблица Event Ring выделяются из невыгружаемого пула и инициализируются во время перечисления контроллера xHCI, вероятно, во время процедуры StartDevice драйвера xHCI, который вызывается при его загрузке; он также инициализирует регистры кольца событий в пространстве MMIO устройства.

Постановка события в кольцо заставляет оборудование запускать прерывание по определенному смещению в таблице MSI-X (смещение MSI-X от BAR и номера BAR хранятся в возможности MSI-X в пространстве конфигурации PCIe контроллер xHCI; следовательно, таблица MSI-X находится в пространстве MMIO). Первичное кольцо событий всегда получает все события изменения статуса порта. Первичное кольцо событий всегда отображается на первое прерывание MSI-X. Прерывание MSI-X будет передаваться как стандартный PCIe MSI к LAPIC, и оно будет ставить прерывание в очередь в IRR для вектора, указанного в данных хранилища таблицы MSI-X. Драйвер xHCI ранее зарегистрировал ISR в записи IDT, соответствующей вектору, с использованием API ядра и HAL. ISR драйвера xHCI понимает, что пакет TRB является событием изменения статуса порта; он может оценить идентификатор порта, чтобы определить порт корневого концентратора, который был источником события изменения (например, 5), а затем проверить 5-й регистр PORTSC, чтобы увидеть, какое изменение имело место, к которому он обращается при определенном смещении от операционной базы , который представляет собой смещение от адреса в полосе BAR конфигурационного пространства PCIe контроллера xHCI, которое было выделено во время перечисления PCIe при загрузке (база MMIO); формула Operational base + (400h + (10h*(n-1))), где n - номер порта от 1 до MaxPorts, а Operational base - это значение в регистре CAPLENGTH + база MMIO.

Драйвер xHCI уведомляет драйвер корневого концентратора о прибытии устройства (я думаю, вызывая Функция обратного вызова корневого концентратора, к которой он, возможно, обращается через PDO корневого концентратора), а драйвер корневого концентратора создает для него PDO и уведомляет диспетчер PnP, что набор дочерних устройств для корневого концентратора изменился. . Либо драйвер xHCI автоматически назначает идентификатор слота и молча выполняет процедуру TRB адресного устройства перед вызовом функции обратного вызова и предоставляет идентификатор слота на месте, либо драйвер концентратора должен инициировать это, отправив URB контроллеру xHCI для запроса Идентификатор слота назначается и возвращается ему, когда он получает информацию об изменении статуса порта для определенного идентификатора порта (я не уверен. И я не уверен, какие структуры данных контролируются драйвером концентратора, а не xHCI драйвер, так что это предположение). Когда драйвер xHCI получает URB, он отправляет разрешающий слот TRB в кольце команд и получает идентификатор слота из TRB завершения команды в кольце событий; он выделит структуру контекста устройства (известную как контекст устройства вывода) и указатель на нее в массиве базы контекста устройства, который контроллер xHCI поддерживает в невыгружаемом пуле. В ответе URB драйвер концентратора получает идентификатор слота; затем драйвер концентратора выделяет в памяти контекст устройства ввода. Флаги добавления контекста для контекста слота и контекста конечной точки 0 в структуре контекста управления вводом в контексте устройства ввода установлены на 1, чтобы указать, что они должны быть добавлены. Затем контексту слота в структуре контекста устройства ввода присваивается номер порта, корневая строка и количество конечных точек. Структура данных контекста конечной точки 0 (также известная как элемент управления по умолчанию) во входном контексте должна быть сконфигурирована с допустимыми значениями для указателя TR Dequeue (указывает на выделяемое им кольцо передачи), типа EP, количества ошибок и полей максимального размера пакета. Значения MaxPStreams, Max Burst Size и EP state должны быть установлены в 0. Указатель структуры Input Context отправляется концентратором в команде устройства адресации, адресованной идентификатору слота нового устройства, которая отправляется через URB на xHCI. Драйвер, и он помещает адресное устройство TRB в кольцо команд, а хост-контроллер копирует входной контекст в выходной контекст, на который указывает запись DCBA для слота.

Затем драйвер концентратора отправляет URB драйверу xHCI, чтобы получить дескриптор устройства в следующей форме:

     Status = SubmitRequestToRootHub(RootHubDeviceObject,
                                 IOCTL_INTERNAL_USB_SUBMIT_URB,
                                 Urb,
                                 NULL);

Все URB, отправленные после того, как идентификатор слота возвращается концентратору, будут содержать идентификатор слота. Он также свяжет ChildDeviceObject->DeviceExtension->UsbDeviceHandle с Urb->UrbHeader.UsbdDeviceHandle, что сделает PDO концентратором, выделенным для нового устройства, доступным через URB. RootHubDeviceObject - это PDO драйвера концентратора, который принадлежит драйверу контроллера xHCI (или паре usbxhci-usbport), который будет использоваться при вызове IoCallDriver внутри этой процедуры. URB будет типа GET_DESCRIPTOR. Затем IRP инициализируется основным кодом IRP_MJ_INTERNAL_DEVICE_CONTROL, а местоположение стека инициализируется с помощью URB в качестве одного из параметров и StackPtr->Parameters.DeviceIoControl.IoControlCode = IoControlCode;. Затем он вызывает IoCallDriver на RootHubDeviceObject, который принадлежит драйверу xHCI.

Затем драйвер xHCI индексирует массив дверных звонков, используя идентификатор слота, и записывает последовательность в регистр дверного звонка по этому индексу, что указывает на то, что указатель постановки в очередь Control EP 0 был обновлен. Затем хост-контроллер начинает действовать и считывает TRB, увеличивая указатель удаления из очереди; и, когда он равен указателю включения в очередь, он останавливается. Для каждого TRB он отправляет соответствующий пакет устройству. Когда он обрабатывает стадию состояния TRB, это вызовет прерывание в кольце событий (я думаю, что кольцо 0), которое вызывает прерывание MSI-x, как указано ранее, для LAPIC процессора на указанный вектор, который будет выбран. вверх с помощью xHCI Contoller ISR и DPC. ISR развернет DPC, который завершит IRP. Дескриптор будет по виртуальному адресу, указанному в IRP URB драйвером концентратора.

Драйвер концентратора вставляет информацию, полученную в IRP URB, в поле PDO->DeviceExtension, которое является указателем на определенную драйвером структуру, с которой он может делать то, что он хочет, что означает, что информация по существу кэшируется и больше не нужно отправлять URB. к драйверу xHCI. Концентратор также отправляет URB GET_CONFIGURATION в драйвер xHCI для каждого номера конфигурации, указанного устройством в дескрипторе устройства. Затем драйвер xHCI передаст это значение конфигурации устройству в TRB GET_CONFIGURATION с правильным номером конфигурации, и вся иерархия конфигурации для этого номера конфигурации будет возвращена драйверу концентратора по адресу, указанному в URB. Затем он вызывает IoInvalidateDeviceRelations() с параметром типа BusRelations и указателем на его PDO (объект физического устройства), который был назначен драйвером xHCI. Менеджер PnP запрашивает у стека устройств PDO текущий список устройств на шине, используя запрос IRP_MN_QUERY_DEVICE_RELATIONS; для этого он инициализирует структуру IRP (в идеале повторно использует одну из альтернативного списка на основе stacksize подсказки в объекте устройства; в противном случае он напрямую выделяет память из невыгружаемого пула для нового). IRP указывает на стек (который примыкает к IRP) через член CurrentStackLocation. Затем он инициализирует первое расположение стека для вызова, который он хочет выполнить (в этом случае основная функция IRP_MJ_PNP и второстепенная функция IRP_MN_QUERY_DEVICE_RELATIONS). Затем он вызывает драйвер наверху стека устройств отправленного PDO, который может быть драйвером верхнего фильтра (который просто не будет реализовывать эту второстепенную функцию, а тело функции будет кодом для ее передачи вниз - мы предположим пока его нет). Таким образом, вершиной стека будет FDO концентратора (который он достигает с помощью IoGetAttachedDevice, который находится на вершине стека). Он вызывает его, используя IoCallDriver(*FDO, *IRP), оболочку IofCallDriver, которая получает следующее местоположение стека путем уменьшения указателя CurrentStackLocation, что заставляет его указывать на следующее местоположение стека по правилам арифметики указателя C (которое является первым местоположением стека как указатель был инициализирован один за ним), а затем он использует основной номер функции IRP_MJ_PNP, указанный в местоположении стека, для индексации в массив MajorFunction объекта драйвера FDO, который был передан в IoCallDriver (драйвер концентратора), и вызывает функцию в этой позиции в массиве.

Код этого вызова выглядит так:

Вы можете видеть, что он проходит IRP. Это позволяет обработчику функций драйвера концентратора USB для IRP_MJ_PNP проверять второстепенную функцию в текущем местоположении стека, а затем вызывать правильную внутреннюю функцию. Для каждого дочернего устройства обработчик ссылается на PDO в структуре DEVICE_RELATIONS, которая представляет собой просто массив указателей PDO. Затем он устанавливает Irp->IoStatus.Information в указатель на массив и возвращается. Затем Plug and Play Manager просматривает массив PDO и сравнивает адреса с адресами PDO в дереве устройств, которые он уже перечислил. Если есть новые адреса, он запрашивает идентификаторы устройства и экземпляра, а также требования к ресурсам; и, если какой-либо из PDO был помечен как неактивный, он также отправляет IRP_MN_SURPRISE_REMOVAL этим PDO, используя тот же процесс инициализации IRP, как описано ранее (FDO устройств не будут реализовывать функцию неожиданного удаления и передавать его драйверу концентратора. ), а драйвер концентратора отключит устройство и освободит назначенные ему аппаратные ресурсы.

return FDO->DriverObject->MajorFunction[StackPtr->MajorFunction](FDO,
                                                             Irp);

Чтобы запросить идентификаторы устройства и экземпляра, PnP Manager отправляет IRP_MN_QUERY_ID (один для идентификатора устройства и отдельный для идентификатора экземпляра) каждому PDO в массиве, указатель которого он получил, который является новым (который будет PDO для нового устройства. который был создан драйвером корневого концентратора). Для IRP, который запрашивает идентификатор устройства (идентификатор устройства представляет собой зависящее от шины соединение Windows, состоящее из идентификатора устройства + идентификатора поставщика + идентификатора подсистемы + версии идентификатора поставщика подсистемы, который он получает от устройства, и префикса шины, также известного как перечислитель, например USB), он отправляет IRP_MN_QUERY_ID, но инициализирует Parameters.QueryId.IdType член расположения стека значением BusQueryDeviceID. В ответ на запрос идентификатора устройства драйвер концентратора должен запросить у устройства информацию, необходимую для создания и объединения идентификатора устройства с помощью префикса шины, но он уже сделал это, как только был создан PDO, поэтому он может просто использовать DeviceExtension, в который была вставлена ​​информация. Идентификатор экземпляра - это строка идентификации устройства, которая отличает устройство от других устройств того же типа, и, вероятно, использует значение iSerialNumber в дескрипторе USB или простое приращение - это зависит от шины. Вместе они образуют DIID (идентификатор экземпляра устройства). InstanceID запрашивается диспетчером PnP в отдельном вызове после использования Parameters.QueryId.IdType = BusQueryInstanceID в IRP.

Драйвер концентратора получает PDO, запрошенный диспетчером PnP через обработчик IRP_MJ_PNP, установленный на DriverEntry, и теперь создает DIID, используя поля в дескрипторе устройства, который ранее был проанализирован и вставлен в DeviceExtension PDO, где usDeviceId выглядит как уже связанный префикс + ProductID + Vendor. Помните, что DIID - это Bus + Device ID + Instance ID, а Device ID - Bus + Vendor + Product ID. Однако он выполняет некоторые фиксированные операции с этими идентификаторами, меняя их все на стандартный формат usbhub, распознаваемый файлами usbstor .inf. DIID будет выглядеть так: USB\VID_<num>&PID_<num>\<InstanceID>. Только PDO имеют DIID или Hardware / Compatible ID.

Затем диспетчер PnP использует возвращенный DIID для индексации в реестре по адресу HKLM\SYSTEM\CurrentControlSet\Enum\Bus\DeviceID\InstanceID.

В моей собственной системе:

В нем значение classguid, которое ведет к подключу класса в HKLM\SYSTEM\CurrentControlSet\Control\Class\<GUID>, который может быть, например, классом клавиатуры. Эти значения заполняются файлами .INF драйвера.

Диспетчер PnP проверяет реестр на наличие соответствующего функционального драйвера, и когда он не находит его, он сообщает диспетчеру PnP пользовательского режима о новом устройстве по его DIID. PnP Manager пользовательского режима сначала пытается выполнить автоматическую установку без вмешательства пользователя. Если процесс установки включает размещение диалоговых окон, требующих взаимодействия с пользователем, и текущий пользователь, вошедший в систему, имеет права администратора, PnP-менеджер пользовательского режима запускает приложение Rundll32.exe (то же приложение, в котором размещены утилиты Панели управления) для выполнения Мастер установки оборудования (% SystemRoot% \ System32 \ Newdev.dll). Если текущий вошедший в систему пользователь не имеет прав администратора (или если пользователь не вошел в систему) и для установки устройства требуется взаимодействие с пользователем, PnP-менеджер пользовательского режима откладывает установку до тех пор, пока не войдет в систему привилегированный пользователь. Мастер установки оборудования использует функции API Setupapi.dll и CfgMgr32.dll (диспетчер конфигурации) для поиска файлов INF, которые соответствуют драйверам, совместимым с обнаруженным устройством.

Он выбирает файл .INF, который наиболее похож на него, присваивая ему рейтинг, выполнив поиск Совместимые идентификаторы в файле .INF, которые, как мы надеемся, соответствуют идентификаторам оборудования / совместимости, сгенерированным из DIID, которые были вставлены в расширение устройства. Если он его находит, он устанавливает драйвер. Установка будет происходить с каждым новым подключенным устройством и, по сути, представляет собой просто заполнение реестра правильной информацией под этим DIID.

Установка выполняется в два этапа. На первом этапе сторонний разработчик драйверов импортирует пакет драйвера в хранилище драйверов, а на втором этапе система выполняет фактическую установку, которая всегда выполняется с помощью процесса% SystemRoot% \ System32 \ Drvinst.exe.

Управление возвращается диспетчеру PnP, и он использует ключи реестра для загрузки драйверов в следующем порядке:

Драйверы USB будут иметь промежуточный узел разработки - usbccgp, если это составное устройство, и usbstor, если это запоминающее устройство большой емкости, что можно увидеть здесь. Когда драйвер концентратора отправляет DIID, диспетчер PnP загружает именно usbstor, как показано на изображении выше. (Нам нужен промежуточный USB-узел хранения, чтобы транслировать общие IRP disk.sys в URB и обрабатывать конфигурацию USB-накопителя, а не заполнять все функции в usbhub.sys).

  1. Любые драйверы фильтра нижнего уровня, указанные в значении LowerFilters ключа класса устройства.
  2. Функциональный драйвер, указанный значением Service в ключе перечисления устройства. Это значение интерпретируется как ключ драйвера в HKLM \ SYSTEM \ CurrentControlSet \ Services.
  3. Любые драйверы фильтров верхнего уровня, указанные в значении UpperFilters ключа перечисления устройства.
  4. Любые драйверы фильтров верхнего уровня, указанные в значении UpperFilters ключа класса устройства.
  5. В пространстве конфигурации PCI отображаются устройства PCI и PCI Express, а не USB-устройства.

Он загружает драйверы и вызывает DriverEntry функцию каждого, а затем AddDevice процедуру, если они еще не запущены (для другого USB-устройства, использующего тот же драйвер); в противном случае он просто вызывает процедуру AddDevice. Подпрограмма AddDevice создает FDO для переданного PDO. В своей AddDevice процедуре драйвер фильтра создает объект устройства фильтра (FiDO) и присоединяет его к стеку устройств (IoAttachDeviceToDeviceStack). Затем PnP Manager создает узел устройства и связывает его с PDO. PnP Manager уже ранее получил мнение шинного устройства (драйвера концентратора) о ресурсах устройства, используя IRP_MN_QUERY_RESOURCE_REQUIREMENTS одновременно с отправкой пакетов IRP для идентификатора устройства. Диспетчер PnP теперь отправляет IRP_MN_FILTER_RESOURCE_REQUIREMENTS, используя IoCallDriver в верхней части нового узла устройства с указанным FDO. Только драйвер FDO обрабатывает это, и он изменит любые требования к объекту устройства, который ему нужен, который драйвер концентратора не смог предсказать. Запоминающее устройство USB не требует прерывания, так как оно будет использовать только первичное кольцо событий. Если да, то в ответе на IRP он должен указать количество требуемых сообщений MSI-x), и как только диспетчер PnP назначает ресурсы устройству, он отправляет IRP_MN_START_DEVICE IRP в стек устройств. Хотя каждое USB-устройство может иметь отдельное прерывание и соответствующее кольцо событий, на самом деле это не имеет значения, потому что на прерывания всегда будет реагировать один и тот же драйвер самого низкого уровня: драйвер xHCI; USB-устройства не имеют регистров ISR для регистрации. Следовательно, все USB-устройства могут использовать одно кольцо событий и одно прерывание.

В подпрограмме usbstor, которая обрабатывает IRP запуска устройства, IRP передается на устройство шины (usbhub) после того, как процедура устанавливает IoCompletionRoutine. IoCompletionRoutine, когда он в конечном итоге вызывается, отправит URB GET_CONFIGURATION, который будет передан его PDO (принадлежащему usbhub), а usbhub представит конфигурацию, которую он кэшировал в этом расширении устройства PDO ранее. Usbstor в конечном итоге решает, какую конфигурацию использовать, и отправляет SET_CONFIGURATION URB. Usbhub заполнит входной контекст из кэшированной конфигурации конечными точками, то есть ISOCH IN, INTERRUPT IN, и добавит указатель входного контекста в URB. Затем Usbhub добавляет дополнительную информацию, такую ​​как идентификатор слота, и отправляет URB контроллеру xHCI, а тот забирает его и вставляет TRB в кольцо передачи конечной точки 0 по умолчанию для устройства, на которое хост-контроллер будет заполнять контекст устройства вывода слота с помощью контекст устройства ввода в соответствии с контекстом управления вводом и информирует устройство о выбранной конфигурации.

Затем Usbstor выделит PDO и вызовет IoInvalidateBusRelations. Когда он доберется до него, usbstor запросит информацию об устройстве, в котором ранее хранился его PDO, принадлежащий usbhub, и он переведет DIID в стандартный формат для usbstor, распознаваемый драйверами disk.sys .inf (формат \ Disk & Ven_xxx & Prod_xxx & Rev_x.xx) и добавит префикс USBSTOR \, и это позволит реестру загружать disk.sys и partmgr.sys в качестве фильтра, хранящегося в подразделе класса disk.sys. Usbstor теперь является устройством шины для устройства.

Disk.sys FDO проверит таблицу поддерживаемых драйверов, чтобы узнать, сколько дисков (N) было пронумеровано в системе, и назовет FDO \Device\HarddiskN\DRN. Partmgr.sys создает символическую ссылку \Device\HarddiskN\Partiton0 на \Device\HarddiskN\DRN. Затем Partmgr вызовет IoReadPartitionTableEx и создаст PDO разделов для каждого раздела, назвав их \Device\HarddiskN\PartitonM и так далее. Для каждого раздела он отправляет IOCTL_INTERNAL_VOLMGR_PARTITION_ARRIVED IRP в volmgr и предоставляет подпись диска и смещение раздела. Volmgr создает PDO тома для каждого тома и создает символические ссылки между \Device\HarddiskN\PartitonM и именем PDO тома \Device\HarddiskvolumeX X >= 1, которое он назначает PDO тома (фактически из-за этой символической ссылки PDO раздела с таким именем никогда не будет доступен, а сам PDO не имеет devnode и внутренне управляется partmgr) и отправляет MntMgr запрос IRP_MJ_DEVICE_CONTROL, указывая IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION путем вызова IoBuildDeviceIoControlRequest для каждого PDO тома (Harddiskvolume1 всегда является первым томом на disk.sys \ Device \ Harddisk0 \ DR0). Диспетчер монтирования отвечает, запрашивая у volmgr непостоянное имя объекта устройства тома (отправляя 3 IOCTL (контрольные пакеты IRP)), расположенные в каталоге устройства дерева системных объектов (например: \ Device \ HarddiskVolume1), уникальный идентификатор, сгенерированный для том и предлагаемое постоянное имя символической ссылки, например \DosDevices\D:.

Постоянная буква диска и точки подключения хранятся с идентичными полями данных. Данные значений называются уникальным идентификатором, который volmgr предоставляет mntmgr с IOCTL_MOUNTDEV_QUERY_UNIQUE_ID. Уникальный идентификатор базовых дисков - это подпись и смещение раздела. Диспетчер монтирования использует предложенное имя, если база данных диспетчера монтирования еще не содержит постоянного буквенного имени диска для тома, сопряженного с уникальным идентификатором. В противном случае он игнорирует предложение и использует имя буквы диска в своей постоянной базе данных имен. Он связывает \DosDevices\D: с уникальным идентификатором и разрабатывает идентификатор GUID и связывает идентификатор GUID в форме \??\Volume{GUID} с уникальным идентификатором в своем пространстве имен, а затем создает между ними символические связи пространства имен диспетчера объектов. Более поздние точки монтирования, то есть \DosDevices\D:\mymount, также будут связаны с уникальным идентификатором. На фотографии выше диск MBR содержит разделы A: (зарезервировано системой) и C:, и ясно, что у них одинаковая подпись MBR и другое смещение. Диск D: является диском GPT и имеет подпись и смещение GPT. E: это USB-накопитель MBR со специальной подписью и смещением USBSTOR.

Когда файл на C: открывается в первый раз, файловая система не будет смонтирована, поэтому, когда строка пути к файлу анализируется в IopParseDevice, который вызывает IopCheckVpbMounted на томе (C: соответствует имени устройства \Device\HarddiskVolume2 из-за символического ссылка, созданная менеджером монтирования), он вызовет IopMountDevice потому что VPB->DeviceObject == NULL, который отправляет IRP_MJ_FILE_SYSTEM_CONTROL/IRP_MN_MOUNT_VOLUME каждой зарегистрированной файловой системе, зарегистрированной с помощью IoRegisterFileSystem. Вызываемые FS обрабатывают IRP_MN_MOUNT_VOLUME и определяют, находится ли их файловая система на носителе, и, если это так, файловая система создает объект тома файловой системы (VDO) и помещает его в VPB. C: указывает устройство, а \file - объект файла. \ - объект корневого каталога. IoGetRelatedDeviceObject получает вершину стека VDO из файлового объекта (выполняет IoGetAttachedDevice на FileObject->Vpb->DeviceObject).

Внутренняя структура драйвера NTFS:

Дерево устройств USB-накопитель Windows

Обзор пространства MMIO

Как перечисляется контроллер xHCI

При загрузке системы pci.sys загружается apci.sys, и одна из вещей, которые он выполняет, - это вызов IoInvalidateDeviceRelations, который запускает диспетчер PnP для отправки IRP_MN_QUERY_DEVICE_RELATIONS, на который он отвечает списком PDO. Он создает список на месте, используя поле Base address of enhanced configuration mechanism таблицы MCFG ACPI для получения основы пространства конфигурации PCIe (PCIEXBAR), а затем выполняет итерацию по нему на границах 4096 байт и для любых идентификаторов поставщика / устройства, которые он находит на границах, он создает PDO и связывает его с номером конфигурации. Одним из устройств будет контроллер xHCI. Он проходит точно такой же процесс, как описано выше, в конечном итоге создает DIID, проверяет реестр и т. Д., Что приводит к загрузке драйвера xHCI; и диспетчер PnP также запрашивает у шины ресурсы, которые потребуются его дочернему элементу, с IRP_MN_QUERY_RESOURCE_REQUIREMENTS в половину стека PDO (который также будет обрабатываться фильтром шины ACPI). После загрузки драйвера xHCI он отправляет IRP_MN_FILTER_RESOURCE_REQUIREMENTS драйверу xHCI, чтобы он мог внести изменения в требования к ресурсам. Devnode PDO контроллера xHCI получает IRP_MN_START_DEVICE, и контроллер xHCI замечает, что это для его собственного PDO, устанавливает IoCompletionRoutine и передает его pci.sys, который увидит, что переданный PDO является дочерним, и выделит физический диапазон MMIO, определенный менеджером PnP, который он получил в списке ресурсов в параметрах IRP начального устройства в BAR, а также устанавливает прерывание MSI-x, определенное в IRP_MN_QUERY_RESOURCE_REQUIREMENTS и IRP_QUERY_FILTER_RESOURCE_REQUIREMENTS, и вызывает IoCompleteRequest, который вызывает IoCompletionRoutine xHCI Набор драйверов, который будет вызывать MmMapIoSpace дескрипторы CmResourceTypeMemory в CM_RESOURCE_LIST в Parameters.StartDevice.AllocatedResourcesTranslated. Он создаст кольцо событий, кольцо команд и DBCA в виртуальном адресном пространстве, полученном от MmMapIoSpace, и установит регистры в пространстве MMIO, чтобы они указывали на них. Затем он связывает кольцо событий с вектором MSI-x, полученным в IRP_MN_START_DEVICE. Затем он настроит ISR с использованием IoConnectInterrupt и регистрация DPC. Я не уверен, как загружается драйвер USB-концентратора, но потенциально это делается в StartDevice драйвера xHCI; он может вызвать IoInvalidateDeviceRelations и сказать, что у него есть только один дочерний элемент, Hub. Он предоставляет DIID, прикрепленный к IUSB3\.

xHCI

person Lewis Kelsey    schedule 27.04.2019
comment
@ Kubahasn'tforgottenMonica, спасибо. Тебе понравились все мои ответы? Если бы это было так, то я мог бы объяснить дальше (в телеграмме?), Почему мне нужно было писать эти ответы, потому что в сети не было ни одного источника, в котором в одном месте было бы все, что мне нужно, чтобы он щелкнул, поэтому на исследование просто ушло много времени. снова все разные источники, когда я все это забыл, и мне нужен пошаговый процесс до мельчайших деталей того, как что-то работает. Также не смотрите на ответ IRQL, который я еще не переписывал в 2021 году, поэтому он неточен, также как и ответ гипервизора. - person Kuba hasn't forgotten Monica; 08.07.2021
comment
Не уверен, что я сделал что-то массово, но у вас есть несколько удивительных ответов - даже если некоторые детали не точны, они являются очень хорошими указателями для дальнейшего изучения. Я уверен, что на это ушло много работы. Продолжай - это круто. - person Lewis Kelsey; 09.07.2021
comment
Драйвер xHCI будет использовать идентификатор слота, указанный в URB, для индексации в массив DCBA и массив дверных звонков. Он переходит к дескриптору конечной точки Control (по умолчанию, 0), который находится в индексе 1 в массиве контекста устройства слота, на который указывает DCBA [slotID] (контекст слота имеет индекс 0), и он запишет этап установки TD (который состоит из одного TRB установки) на указатель постановки в очередь, указанный в дескрипторе конечной точки по умолчанию (управления) (который, как я полагаю, автоматически устанавливается на тот же адрес, что и указатель удаления из очереди изначально в контексте устройства ввода, когда контроллер xHCI обрабатывает команду устройства адреса), который является физическим адресом в ОЗУ, который контроллер xHCI считывает с помощью транзакций PCIe TLP. В TRB он указывает тип TRB (Setup); Тип передачи (IN); Длина передачи (размер дескриптора устройства = 18); Немедленные данные = 0 (не уверен, что это, но только на этапе настройки, кажется, переключено на 1); Прерывание по завершении (нет); bmRequestType = 80h и bRequest = 6 вместе определяют тип запроса GET_DESCRIPTOR; wValue имеет тип: Дескриптор устройства, который равен 0100h; а затем wLength равняется 18 (длина дескриптора устройства). Затем он продвигает указатель постановки в очередь кольца передачи конечной точки 0 (добавляет к нему размер предыдущего TD). Затем он записывает TD этапа данных в месте написания нового указателя включения в очередь; но на самом деле он использует виртуальный адрес, который драйвер xHCI определил в пространстве MMIO в перечислении xHCI (он использовал _13_ в CM_RESOURCE_LIST в Parameters.StartDevice.AllocatedResourcesTranslated в подпрограмме устройства запуска) для записи в место в ОЗУ, поскольку системное программное обеспечение не может использовать физические адреса в отличие от устройств PCIe (хост-контроллер). Data Stage TD состоит из одного TRB с типом TRB, установленным на Data Stage TRB; Направление = 1; Длина передачи TRB = 18; Цепной бит = 0; IOC = 0 (не прерывается, потому что только стадия состояния вызывает прерывание, т.е. когда это сделано); Немедленные данные = 0; Указатель буфера данных (64-битный физический адрес, по которому контроллер xHCI будет писать ответ, на который транслируется виртуальный адрес, предоставленный драйвером концентратора); и бит цикла (текущее состояние цикла производителя (переключается на 1 или 0 в зависимости от указателя очереди, охватывающего кольцо. Производитель переключает бит цикла с 0 на 1, если он встречает ссылку TRB (он считывает перед записью в местоположение в убедитесь, что там уже нет Link TRB, указывающего на начало кольца)). Затем он снова перемещает указатель Enqueue. Наконец, он записывает TD этапа состояния, который состоит из одного TRB состояния с типом TRB = TRB этапа состояния; Направление = '0'; Длина передачи TRB = 0; Цепной бит = 0; Прерывание по завершении = 1; Немедленные данные = 0; Указатель буфера данных = 0 (его нет, это просто стадия состояния); и бит цикла = (текущее состояние цикла производителя). - person Kuba hasn't forgotten Monica; 12.07.2021

Пространство конфигурации PCI покажет вам производителя и идентификатор устройства USB-контроллера, но не подключенные устройства. Вам нужно будет пронумеровать шину USB, читая / записывая для этого регистры USB.

Обратите внимание, что захват USB-контроллера приведет к поломке работающего в данный момент USB-стека и отключению вашей USB-клавиатуры и загрузочных устройств.

Если вы работаете в оболочке UEFI, возможно, вы найдете то, что вам нужно, в выводе devtree.

Если вы пишете свой собственный код UEFI DXE, он должен будет запросить драйвер USB.

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

person myron-semack    schedule 21.01.2016

Таким образом, ваше приложение будет переносимым между всеми совместимыми платформами UEFI.

  • EFI_USB_IO_PROTOCOL для взаимодействия с USB-устройствами независимо от того, к какой шине подключен хост-контроллер.
  • Этот ответ - демонстрация силы. Высоко оценен!

Пользователь @fpmurphy, который публикует здесь ответы, иногда имеет примеры обоих в своей области github..

EFI_PCI_IO_PROTOCOL для операций PCI

person unixsmurf    schedule 07.05.2019