Не удалось зарезервировать BAR в устройстве PCI.

Вопрос или проблема

Я пишу модуль ядра низкого уровня, который будет взаимодействовать с параллельным портом через сокет 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 может не удаваться

  1. Конфликт с другими драйверами:
    Хотя вы упомянули, что проверили наличие других драйверов (например, parport_pc), которые могут использовать этот параллельный порт, стоит удостовериться, что конфликтов с другими модулями на уровне ядра действительно нет. Необходимо обратить внимание на все загруженные модули и убедиться, что все они отключены перед тестированием вашего драйвера.

  2. Неверные указанные размеры или адреса:
    Убедитесь, что адреса и размеры, которые вы пытаетесь зарезервировать для BAR, не пересекаются с другими устройствами на вашей системе. Ваша система может также иметь наложения адресов для памяти или I/O, которые используются другими устройствами.

  3. Проблемы с настройками BIOS/UEFI:
    В некоторых случаях, настройки BIOS/UEFI могут блокировать доступ к определённым ресурсам PCI. Позаботьтесь о том, чтобы проверить конфигурации относительного PCI/PCIe и, если это возможно, опубликуйте обновления для BIOS/UEFI.

  4. Несоответствующая конфигурация устройства:
    Возможно, что PCI-устройство неправильно инициализировано на уровне аппаратного обеспечения, и оно не предоставляет необходимые ресурсы для работы. Пожалуйста, проверьте, соответствуют ли значения идентификаторов устройства и поставщика тем, которые вы ожидаете.

  5. Неверное использование функции 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 драйвера.

Оцените материал
Добавить комментарий

Капча загружается...