Доступ к физическому адресу из пространства ядра в Linux

Я пишу модуль ядра, которому требуется доступ к области памяти за пределами пространства ядра и пользователя. Я знаю физический адрес и размер такой памяти. Я пробовал использовать ioremap (), но виртуальный адрес, возвращаемый такой функцией, указывает на физический адрес, отличный от того, который я дал в качестве аргумента для ioremap. Затем я попытался использовать Phys_to_virt () после ioremap (), на этот раз полученный виртуальный адрес указывает на правильный физический адрес, однако, когда я пытаюсь получить доступ к элементу внутри такой области памяти, я получаю следующую ошибку: Невозможно обработать запрос подкачки ядра по виртуальному адресу ffffff801400000f Что я не так?

РЕДАКТИРОВАТЬ: фрагмент кода:

#define FLAG_SIZE 1
#define BUF_SIZE 4
#define ELEM_SIZE 5

res = ioremap_nocache(MEMORY_ADDR, BUF_SIZE*ELEM_SIZE);
void *virt = phys_to_virt(MEMORY_ADDR);

        
printk(KERN_INFO "Physical address %px mapped in virtual address %px\n", virt_to_phys(virt), virt);
    
// DEBUG: print buffers content
char *flags = res + BUF_SIZE*(ELEM_SIZE-FLAG_SIZE);
for(i=0; i < BUF_SIZE; i++){
        printk(KERN_INFO "row %d [flag: %c]: %d\n", i, readb(flags + i), virt + i*ELEM_SIZE);
}

где: -MEMORY_ADDR: физический адрес -BUF_SIZE: количество элементов в буфере -ELEM_SIZE: размер элементов

Буферы флагов - это массив символов в конце такой области памяти.

Полные возвращаемые результаты:

Physical address 1409286144 mapped in virtual address 335544320
Unable to handle kernel paging request at virtual address ffffff801400000f
Mem abort info:
  ESR = 0x96000006
  EC = 0x25: DABT (current EL), IL = 32 bits
  SET = 0, FnV = 0
  EA = 0, S1PTW = 0
Data abort info:
  ISV = 0, ISS = 0x00000006
  CM = 0, WnR = 0
swapper pgtable: 4k pages, 39-bit VAs, pgdp=00000000407c8000
[ffffff801400000f] pgd=000000004fffb003, pud=000000004fffb003, pmd=0000000000000000
Internal error: Oops: 96000006 [#1] SMP
Modules linked in: source(O)
CPU: 1 PID: 107 Comm: ifconfig Tainted: G           O      5.4.88 #1
Hardware name: linux,dummy-virt (DT)
pstate: 20000005 (nzCv daif -PAN -UAO)
pc : getFlag+0x24/0x40 [source]
lr : shmem_net_open+0x168/0x2c0 [source]
sp : ffffffc010b4b9d0
x29: ffffffc010b4b9d0 x28: 0000000000001043 
x27: ffffff800e2b4c00 x26: ffffffc01077c000 
x25: 0000000000000005 x24: ffffffc008661158 
x23: 0000000000000004 x22: 0000000000000000 
x21: ffffff800e24e200 x20: ffffff800e255000 
x19: ffffff800e255858 x18: 0000000000020000 
x17: 000000004a0b804e x16: 0000000031b76cb0 
x15: ffffffc010859c40 x14: 0720072007200720 
x13: 0720073007320733 x12: 0734073407350735 
x11: 0733073307200773 x10: 0773076507720764 
x9 : 076407610720076c x8 : 000000000000006f 
x7 : 076907760720076e x6 : 0000000000000001 
x5 : ffffff800e255840 x4 : 0000000000000005 
x3 : 0000008040000000 x2 : 0000000000000000 
x1 : 000000000000000f x0 : ffffff801400000f 
Call trace:
 getFlag+0x24/0x40 [source]
 __dev_open+0xe4/0x160
 __dev_change_flags+0x160/0x1c0
 dev_change_flags+0x20/0x60
 devinet_ioctl+0x63c/0x700
 inet_ioctl+0x2f4/0x360
 sock_do_ioctl+0x44/0x2b0
 sock_ioctl+0x1c8/0x510
 do_vfs_ioctl+0x984/0xb70
 ksys_ioctl+0x44/0x90
 __arm64_sys_ioctl+0x1c/0xc0
 el0_svc_common.constprop.0+0x68/0x160
 el0_svc_handler+0x6c/0x90
 el0_svc+0x8/0x1fc
Code: 1b047c21 8b21c041 8b010000 cb030000 (39400000) 
---[ end trace 5a219c0b95978c47 ]---
Segmentation fault

Используемый процессор - Armv8 Cortex 53

РЕДАКТИРОВАТЬ 2: исправлены предыдущие фрагменты кода, теперь нет паники ядра, однако вывод неверен:

[SHMEM_NET] Physical address 0000000054000000 mapped in virtual address ffffffc01000d000
row 0 [flag: ]: 268488704
row 1 [flag: E]: 268488707
row 2 [flag: E]: 268488710
row 3 [flag: E]: 268488713
row 4 [flag: E]: 268488716

Проблема в том, что я должен найти первую букву «E» в строке 0, а строка 4 должна находиться за пределами отображаемой области. Более того, если теперь я изменяю такую ​​область памяти, эта машина будет видеть изменения, но другие машины, обращающиеся к тем же областям памяти, по-прежнему видят старые значения.


person Niccolò Borgioli    schedule 27.01.2021    source источник
comment
Можете ли вы поделиться фрагментированным кодом, типом процессора и соответствующим журналом ядра?   -  person linkjumper    schedule 28.01.2021
comment
Конечно, я обновил вопрос   -  person Niccolò Borgioli    schedule 28.01.2021
comment
См. stackoverflow.com/a/38761757/5264491   -  person Ian Abbott    schedule 28.01.2021
comment
Спасибо @IanAbbott, но все еще остается вопрос? Почему содержимое области памяти, указанной виртуальным адресом, полученным с помощью ioremap (), сдвинуто на 1 байт относительно ожидаемого? И, кроме того, могу ли я выполнить арифметику указателей на возвращенном виртуальном адресе, предполагая, что он является смежным, как и физический?   -  person Niccolò Borgioli    schedule 28.01.2021
comment
Какое значение имеет MEMORY_ADDR?   -  person Ian Abbott    schedule 28.01.2021
comment
Проблемы, которые я вижу: (1) res - это виртуальный адрес, который нужно использовать, а не virt. (2) Вызов ioremap_nocache отображает только BUF_SIZE*ELEM_SIZE байта, но буфер флагов сразу после этой области не был отображен. (3) Вы не используете специальные функции, предназначенные для доступа к оперативной памяти. (4) Спецификаторы формата printk не соответствуют типам печатаемых аргументов (см. Как правильно настроить спецификаторы формата printk, но если вы используете более старое ядро, детали могут отличаться).   -  person Ian Abbott    schedule 28.01.2021
comment
Спасибо за ответ: (1) Я исправил, и больше не вылетает. (2) вы правы, предоставленный мной фрагмент кода был упрощением реального кода (я удалил доступ к структурам, содержащим информацию о размере такой памяти), я соответствующим образом исправил фрагмент (3) Я попытался использовать readb ( ) и работает нормально (4) Спасибо, исправил. Однако проблема все еще остается: теперь у меня не возникает паники ядра при доступе к этой памяти, однако считываемые данные смещаются на 1 байт (см. Обновленный вывод)   -  person Niccolò Borgioli    schedule 28.01.2021
comment
где мне найти значение MEMORY_ADDR?   -  person Niccolò Borgioli    schedule 28.01.2021


Ответы (1)


Виртуальный адрес, возвращаемый ioremap и phys_to_virt, не совпадает.

ioremap: создает новую таблицу страниц сопоставления

phys_to_virt: вычесть фиксированное смещение из адреса. Проверьте MMU на наличие phy_offset.

Как протестировать?

void* addr1 = phys_to_virt(MEMORY_ADDR);
void* addr2 = ioremap_nocache(MEMORY_ADDR, BUF_SIZE*ELEM_SIZE);

printk(KERN_INFO "virt_addr1: %x\n", (unsigned int volatile *) addr1);
printk(KERN_INFO "virt_addr2: %x\n", (unsigned int volatile *) addr2);

Вы увидите, что addr1 и addr2 разные. Я думаю, вам не нужно использовать phys_to_virt.

Просто используйте:

void* virt_addr = ioremap_nocache(MEMORY_ADDR, BUF_SIZE*ELEM_SIZE);
person linkjumper    schedule 28.01.2021
comment
Спасибо @linkjumper, однако проблема в том, что кажется, что физический адрес, связанный с виртуальным адресом, возвращаемым ioremap_nocache (), отличается от того, который я дал ему в качестве входных данных. Я могу заметить это, поскольку значения, которые я читаю, отличаются от ожидаемых (такая область памяти была инициализирована известными значениями), и с помощью virt_to_phys () я получаю физический адрес, отличный от того, который указан в качестве аргумента для ioremap. Что случилось? - person Niccolò Borgioli; 28.01.2021