Вопрос или проблема
Я пишу модуль ядра низкого уровня, который будет взаимодействовать с параллельным портом через сокет PCIe. Карта использует чип Axis AX99100. Вывод команды lspci -v -s 29:00.2
дает следующий результат:
Параллельный контроллер: Asix Electronics Corporation AX99100 PCIe to Multi I/O Controller (prog-if 03 [IEEE1284])
Подсистема: Asix Electronics Corporation (Неверный ID) Параллельный порт
Флаги: fast devsel, IRQ 39, IOMMU group 26
Порты ввода-вывода на c010 [размер=8]
Порты ввода-вывода на c000 [размер=8]
Память на fc601000 (32-бит, не подверженная предварительной выборке) [размер=4K]
Память на fc600000 (32-бит, не подверженная предварительной выборке) [размер=4K]
Возможности: <доступ запрещен>
Модули ядра: parport_pc
При попытке зарезервировать порты ввода-вывода для моего модуля мне отказали в этом.
Используя sudo lsmod | grep parport
для проверки и удаления всех параллельных драйверов, которые могут использовать этот параллельный ввод-вывод карты. С точки зрения привилегий, мой модуль компилируется и загружается с использованием привилегий root.
После загрузки моего модуля и повторного выполнения lspci -v -s 29:00.2
я дополнительно увижу следующую строку:
Драйвер ядра в использовании: pci-test-driver
Что означает, что мой драйвер теперь связан с этим устройством. Однако ни один из BAR не может быть зарезервирован с помощью функции pci_request_region()
из заголовка <linux/pci.h>
.
Для дополнительного понимания код моего модуля показан ниже.
#include <linux/pci.h> // Основные функции и структуры PCI
#include <linux/pci_regs.h> // Определения регистров конфигурационного пространства PCI
#include <linux/init.h> // Макросы, используемые для инициализации модуля
#include <linux/module.h> // Макросы для управления модулем
#include <linux/kernel.h> // Основная функциональность ядра
#include <linux/io.h> // Функции памяти с отображением ввода-вывода
#include <linux/interrupt.h> // Функции для обработки прерываний
#define PCI_TEST_VENDOR_ID 0x125b //0x144d
#define PCI_TEST_DEVICE_ID 0x9100 //0xa808
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PCIe Драйвер");
MODULE_AUTHOR("BMW");
static struct pci_device_id pci_ids[] = {
{ PCI_DEVICE(PCI_TEST_VENDOR_ID, PCI_TEST_DEVICE_ID), },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, pci_ids);
static int pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
static void pci_remove(struct pci_dev *pdev);
static int read_config_space(struct pci_dev *pdev);
static int read_mmio_space(struct pci_dev *pdev);
static struct pci_driver pci_driver = {
.name = "pci-test-driver",
.id_table = pci_ids,
.probe = pci_probe,
.remove = pci_remove
};
struct parport_dev {
struct pci_dev *pdev;
void __iomem *bar0_addr;
};
static int pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) {
pr_info("Проверка устройства: vendor=0x%X, device=0x%X\n", pdev->vendor, pdev->device);
struct parport_dev *parport;
int err;
// Поддерживает несколько вызовов probe(), структура nvme должна сохраняться до удаления
parport = kzalloc(sizeof(parport), GFP_KERNEL);
if (parport == NULL) {
pci_disable_device(pdev);
pr_err("Не удалось выделить память для структуры parport!\n");
return -ENOMEM;
}
pci_set_drvdata(pdev, parport);
nvme->pdev = pdev;
// Зарезервировать BAR0 для этого драйвера
err = pci_request_region(pdev, 0, "parport_driver");
if (err) {
kfree(parport);
pci_disable_device(pdev);
dev_err(&pdev->dev, "Не удалось запросить BAR0\n");
return err;
}
// Отобразить память параллельного порта в управляемую ядром память
parport->bar0_addr = pci_ioremap_bar(pdev, 0);
if (parport->bar0_addr == NULL) {
pci_release_regions(pdev);
kfree(parport);
pci_disable_device(pdev);
dev_err(&pdev->dev, "Не удалось отобразить BAR0\n");
return -ENOMEM;
}
dev_info(&pdev->dev, "Отображен BAR0 на %p\n", parport->bar0_addr);
return 0;
}
static void pci_remove(struct pci_dev *pdev) {
pr_info("Удаление устройства: vendor=0x%X, device=0x%X\n", pdev->vendor, pdev->device);
pci_disable_device(pdev);
}
static int __init pci_init(void) {
int ret;
pr_info("Инициализация модуля pci-test");
// Регистрация драйвера PCI
ret = pci_register_driver(&pci_driver);
if (ret) {
pr_err("Не удалось зарегистрировать драйвер PCI\n");
return ret;
}
return 0;
}
static void __exit pci_exit(void) {
pr_info("Выход модуля pci-test");
pci_unregister_driver(&pci_driver);
}
module_init(pci_init);
module_exit(pci_exit);
Следующий лог является выводом команды sudo dmesg
и показывает ответ, когда все доступные BAR пытаются быть зарезервированы:
[ 2827.501937] pci-test-driver 0000:29:00.2: BAR 0: нельзя зарезервировать [io 0xc010-0xc017]
[ 2827.501940] pci-test-driver 0000:29:00.2: Не удалось запросить BAR0
[ 2827.501942] pci-test-driver 0000:29:00.2: BAR 1: нельзя зарезервировать [io 0xc000-0xc007]
[ 2827.501944] pci-test-driver 0000:29:00.2: Не удалось запросить BAR1
[ 2827.501946] pci-test-driver 0000:29:00.2: BAR 2: нельзя зарезервировать [mem 0xfc601000-0xfc601fff]
[ 2827.501949] pci-test-driver 0000:29:00.2: Не удалось запросить BAR2
[ 2827.501950] pci-test-driver 0000:29:00.2: BAR 5: нельзя зарезервировать [mem 0xfc600000-0xfc600fff]
[ 2827.501952] pci-test-driver 0000:29:00.2: Не удалось запросить BAR5
Ответ или решение
Не удается зарезервировать BAR в устройстве PCI
Когда вы разрабатываете низкоуровневый модуль ядра, который взаимодействует с параллельным портом через слотовый интерфейс PCIe, и при этом встречаете проблемы с резервированием базовых адресных регистров (BAR), важно внимательно проанализировать, какие факторы могут этому способствовать. В данной ситуации вы пытаетесь работать с картой на основе чипа Axis AX99100. Давайте рассмотрим, что может вызывать проблемы с резервированием BAR.
Причины, по которым резервирование BAR может не удаваться
-
Конфликт с другими драйверами:
Хотя вы упомянули, что проверили наличие других драйверов (например,parport_pc
), которые могут использовать этот параллельный порт, стоит удостовериться, что конфликтов с другими модулями на уровне ядра действительно нет. Необходимо обратить внимание на все загруженные модули и убедиться, что все они отключены перед тестированием вашего драйвера. -
Неверные указанные размеры или адреса:
Убедитесь, что адреса и размеры, которые вы пытаетесь зарезервировать для BAR, не пересекаются с другими устройствами на вашей системе. Ваша система может также иметь наложения адресов для памяти или I/O, которые используются другими устройствами. -
Проблемы с настройками BIOS/UEFI:
В некоторых случаях, настройки BIOS/UEFI могут блокировать доступ к определённым ресурсам PCI. Позаботьтесь о том, чтобы проверить конфигурации относительного PCI/PCIe и, если это возможно, опубликуйте обновления для BIOS/UEFI. -
Несоответствующая конфигурация устройства:
Возможно, что PCI-устройство неправильно инициализировано на уровне аппаратного обеспечения, и оно не предоставляет необходимые ресурсы для работы. Пожалуйста, проверьте, соответствуют ли значения идентификаторов устройства и поставщика тем, которые вы ожидаете. -
Неверное использование функции pci_request_region:
Обратите внимание, что функцияpci_request_region()
используется для запроса ресурсов, которые уже должны быть настроены в конфигурационном пространстве PCI. Убедитесь, что вы указываете правильный индекс BAR для резервирования. Например, в вашем коде вы используетеpci_request_region(pdev, 0, "parport_driver")
для BAR0. Если есть необходимость зарезервировать BAR1 или BAR2, убедитесь, что вы корректно обращаетесь к их индексам и соответствующим адресам.
Анализ вашего кода
Ваш код выглядит обоснованным, однако, есть несколько моментов, которые стоит доработать:
-
В процессе выделения памяти для
parport
используйтеsizeof(struct parport_dev)
вместоsizeof(parport)
. Это позволит избежать ошибок, связанных с неправильным вычислением размера при выделении памяти.parport = kzalloc(sizeof(struct parport_dev), GFP_KERNEL);
-
В функции
pci_remove()
, стоит также освободить регионы и освободить память, выделенную дляparport
, чтобы избежать утечек.static void pci_remove(struct pci_dev *pdev) { struct parport_dev *parport = pci_get_drvdata(pdev); pci_release_region(pdev, 0); kfree(parport); pci_disable_device(pdev); }
Заключение
Резюмируя, проблемы с резервированием BAR в устройстве PCI могут быть вызваны множеством факторов, начиная от конфликтов с другими драйверами и заканчивая аппаратными проблемами. Убедитесь, что все драйвера отключены, что вы правильно настраиваете параметры в BIOS и внимательно проверяете ваш код на наличие ошибок. Если предоставленные выше шаги не помогли, стоит изучить дополнительные логи системы с помощью dmesg
, чтобы выяснить, существуют ли скрытые ошибки или предупреждения.
Следуя этим рекомендациям, вы сможете потенциально устранить проблему и успешно зарезервировать BAR для вашего low-level PCIe драйвера.