LINUX.ORG.RU

Remap PCI memory в user-space


0

1

Здравствуйте!

Подобный вопрос уже поднимался в этой теме: remap PCI memory в user-space Но варианты предложенные там не работают в моем случае. Опишу ситуацию: пишу драйвер для PCI плат, ОС: Ubuntu 11.04 x86_64 Kernel 2.6.38-8-generic.

Для каждой из плат нормальным порядком получаются ресурсы: через pci_resource_start(dev,0) и pci_resource_len (dev, 0) (меня интересует в первую очередь BAR0). Далее необходимо сделать remap в user-space. В первую очередь для проверки правильности получения ресурсов выполняю функцию ioremap_nocache(pci_resource_start(dev,0),pci_resource_len (dev, 0)), на выходе которой получаю указатель, по которому в kernel-space читаются адекватные значения (ресурсы получены правильно). Далее необходимо реализовать mmap. В указанной теме используется такой вариант:

addr = pci_resource_start(dev->pdev, bar);

pfn = virt_to_phys(bus_to_virt(addr)) >> PAGE_SHIFT;

Не работает. Если вывести адреса, получаемые во всех функциях, получается следующее (это для примера):

Resourse start: 0x90104A00

ioremap_nocache: 0xFFFFC90008B9AA00

bus_to_virt: 0xFFFF880090104A00

virt_to_phys(bus_to_virt()): 0x90104A00

Т.е. после конструкции virt_to_phys(bus_to_virt()) получается то же значение. (что не выполняет правильно mmap).

Я пробовал выполнить вариант mmap того адреса, что получается из ioremap_nocache:

pfn = ioremap_nocache >> PAGE_SHIFT;

Тоже на работает. Еще загвоздка в том, что длина получаемых ресурсов равна 0x80, тогда как mmap выполняется минимум для PAGE_SIZE = 0x1000. Вопросы: как выполнить mmap адреса, получаемого после ioremap_nocache и работает ли это для длин не кратных PAGE_SIZE (как я понимаю нет, но существует ли другой способ отправить полученный адрес в user-space)?

Благодарю.


>remap PCI memory в user-space

Не могу сказать, что с радостью смотрю на этот написанный полтора года назад кусок кода, хотя с другой стороны приятно, что ты на него ссылаешься.

По теме. 0x90104A00 - это адрес начала ресурса, идущий с отступом 0xA00 от начала страницы. Когда ты мапишь его в юзерспейс, то мапится вся страница, и, соответственно, выдается адрес ее начала. К возвращаемому значению (в юзерспейсе) нужно добавить этот отступ 0xA00.

ttnl ★★★★★
()

попробуй для начала через /dev/mem

anonymous
()
Ответ на: комментарий от ttnl

Благодарю. Введение отступа помогло - в user-space прочиталось контрольное значение. Но есть вопросы:

1.Как работают virt_to_phys(bus_to_virt()), если на выходе получается тот же адрес? Попробовал без них: все тоже работает, нужны ли они?

2.Про ioremap_nocache можно забыть - в данном случае это неверный путь? (кстати в LDD3 именно про нее и рассказывается и про предварительное выделение request_mem_region() - у меня и без него работает).

3.Что значит во флагах vma->vm_flags |= VM_IO;/* Memory mapped I/O or similar */ ? Работает и без него, и с ним.

4.Когда я читаю pci_resourse_flags(), получаю 0x00040200 (IORESOURCE_MEM | IORESOURCE_SIZEALIGN). Это нужно как-то учитывать при выполнении mmap? Есть ли разница remap_pfn_range и io_remap_pfn_range в данном случае (в LDD3 написано, что для большинства архитектур разницы нет)?

5.На счет минимального размера mmap ( = PAGE_SIZE = 0x1000) при необходимости 0x80. Это никак не влияет на выделяемые ресурсы? Дело в том, что сейчас у меня стоят 2 платы. Одна имеет адрес BAR0: 0x90104A00, другая - 0x90104A80.

попробуй для начала через /dev/mem

Поясните, если не сложно, как. Я новичок в этом деле, у меня в запасе есть только LDD3 и рысканья по драйверам по исходникам ядра.

ksanto
() автор топика
Ответ на: комментарий от ksanto

>1.Как работают virt_to_phys(bus_to_virt()), если на выходе получается тот же адрес? Попробовал без них: все тоже работает, нужны ли они?

Это была попытка конвертирования адреса шины в физический. В целом, так не надо писать.

2.Про ioremap_nocache можно забыть - в данном случае это неверный путь? (кстати в LDD3 именно про нее и рассказывается и про предварительное выделение request_mem_region() - у меня и без него работает).

ioremap - для обращения из ядра, не userspace.

3.Что значит во флагах vma->vm_flags |= VM_IO;/* Memory mapped I/O or similar */ ? Работает и без него, и с ним.

На твоей платформе работает, а на другой не будет. Если речь идет о IO, выставлять обязательно.

4.Когда я читаю pci_resourse_flags(), получаю 0x00040200 (IORESOURCE_MEM | IORESOURCE_SIZEALIGN). Это нужно как-то учитывать при выполнении mmap?

Это значит, что к этим адресам можно обращаться, как к обычной памяти (не через inb/outb).

Есть ли разница remap_pfn_range и io_remap_pfn_range в данном случае (в LDD3 написано, что для большинства архитектур разницы нет)?

Неодинаково только для sparc:

«Remap IO memory, the same way as remap_pfn_range(), but use the obio memory space».

Если ты не знаешь, что такое «obio memory space», то тебе не нужно парится.

5.На счет минимального размера mmap ( = PAGE_SIZE = 0x1000) при необходимости 0x80. Это никак не влияет на выделяемые ресурсы? Дело в том, что сейчас у меня стоят 2 платы. Одна имеет адрес BAR0: 0x90104A00, другая - 0x90104A80.

Мапировать можно только страницы

попробуй для начала через /dev/mem

Там тоже можно что-то мапить, но, кажется, в последних версия это принудительно запрещается.

ttnl ★★★★★
()
Ответ на: комментарий от ttnl

Благодарю. Стало более понятно. Может быть, поможете с вопросами не касающимися PCI.

1. Поскольку драйвер должен обслуживать не одну плату одновременно, мне хотелось сделать так, чтобы в функции .probe помимо инициализации PCI'ой части для каждой новой платы создавалось отдельное устройство в /dev/ . Я не совсем понимаю, как это правильно сделать.

Символьные устройства регистрируются с помощью register_chrdev() или register_chrdev_region()+ cdev_add(). Оба пути описаны в LDD3. Но в любом случае в тех примерах сами устройства создаются в отдельном скрипте запуска с помощью mknod (и количество устройств предопределено). Этот вариант не кажется удобным.

Также нашел способ создания устройств в /dev/ с помощью udev. Как я понял, необходимо написать правила и положить этот файл /etc/udev/rules.d/. Правда, примеров реализации драйвера (чтобы было изменяемое количество устройств) вместе с этими правилами не видел.

Если делать устройства в /dev/ в этом случае это не оптимальный вариант (а я к нему обращаюсь из-за наличия mmap), то направьте на другие способы.

2. Как сделать автоматическую загрузку написанного PCI драйвера (помимо вставки в загрузочный скрипт /etc/init.d/ и перекомпиляции ядра с вставкой собственного драйвера)? Есть ли какой-то стандартный способ добавления драйверов?

Благодарю.

ksanto
() автор топика
Ответ на: комментарий от ksanto

> Правда, примеров реализации драйвера (чтобы было изменяемое количество устройств) вместе с этими правилами не видел.

Экспортируй в sysfs какой-нибудь атрибут, с помощью которого можно сделать уникальное имя (например, номер устройства).

Есть ли какой-то стандартный способ добавления драйверов?

Он наверняка есть, но мало кто из разработчиков левых драйверов им пользуется :) Полагаю, если в драйвере указать обслуживаемые им PCI-идентификаторы, положить его в нужное место /lib/modules/, и вызвать depmod, то идентификаторы пропишуьтся в modules.pcimap, и ядро само вызовет загрузчик модулей.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> Он наверняка есть, но мало кто из разработчиков левых драйверов им пользуется :) Полагаю, если в драйвере указать обслуживаемые им PCI-идентификаторы, положить его в нужное место /lib/modules/, и вызвать depmod, то идентификаторы пропишуьтся в modules.pcimap, и ядро само вызовет загрузчик модулей.

Это помогло, я про depmod не подумал (идентификаторы указывал и драйвер копировал в /lib/modules/KERNEL_VERSION/). Спасибо.

ksanto
() автор топика

если нужно из юзерспейса общаться с железкой, почему не взять libpciaccess?

anonymous
()
Ответ на: комментарий от anonymous

Благодарю за подсказку. Но я и не знал о такой библиотеке, да и в данном случае мне важнее написать драйвер со своим API, чем обращаться к чужим библиотекам.

Возникла проблема другого рода. Есть необходимость сделать mmap других ресурсов - портов (если я правильно понимаю).

pci_resource_start(dev,1) = 0x00001000 pci_resourse_flags(dev,1) = 0x00040101 (IORESOURCE_IO | IORESOURCE_SIZEALIGN, 1 - не знаю, что значит).

Mmap() по описанной выше технологии выполняется, но читается 0 по тому адресу, где должно быть адекватное значение.

В LDD3 описан способ обращения к портам через память с помощью ioport_map(), но как я понимаю, это актуально только для kernel-space и у меня это не заработало.

Существует ли метод обращения к этим адресам из user-space? Нужно подравить какие-то флаги, как я понимаю.

ksanto
() автор топика
Ответ на: комментарий от ttnl

Небольшой апдейт.

1.Порты(в моем случае 0x1100-0x117F) я стал читать напрямую из kernel-space с помощью функции inl() (а также пробовал inl_p()). Проблема в том, что из этих портов почему-то читаются значения такие же как и из вышеупомянутой памяти (0x90104A00 - 0x90104A7А), хотя должны быть другие значения. Что я делаю с чтением портов не так?

В случае использования ioport_map() в kernel-space получаю адрес вида 0x00011000, прочитать по-которому ничего невозможно (kernel_panic). Что с этим адресом можно сделать?

2.Как правильно осуществить запись/чтение портов в user-space? Возможно ли сделать mmap()?

Я пробовал прочитать с помощью /dev/ports - получил те же значения, что и при чтении в kernel-space с помощью inl. Вопрос в том, что для того, чтобы прочитать /dev/ports нужно запускать программу из-под root (capable(CAP_SYS_RAWIO)), что не является правильным.

Кроме того, хотелось получить правильные значения, а не те, что при чтении из памяти (0x90104A00 - 0x90104A7А).

ksanto
() автор топика
Ответ на: комментарий от ksanto

>В случае использования ioport_map() в kernel-space получаю адрес вида 0x00011000, прочитать по-которому ничего невозможно (kernel_panic). Что с этим адресом можно сделать?

Какой стек? Читаешь через inb и т.п.?

2.Как правильно осуществить запись/чтение портов в user-space? Возможно ли сделать mmap()?

Сделай, как это реализовано для /dev/ports (обычные файловые операции read/write в ядре преобразуются в inb/outb). Файл drivers/char/mem.c

ttnl ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.