0
votes

I'm writing a kernel module that needs access to a memory region outside the Kernel and User spaces. I know the physical address and the size of such a memory. I've tried with ioremap(), but the virtual address returned by such a function points to a phisical address different from the one I gave as an argument to ioremap. Then I tried to use phys_to_virt() after ioremap(), this time the virtual address obtained points to the correct physical one, however when I try to access an element inside such memory area I get the following error: Unable to handle kernel paging request at virtual address ffffff801400000f What I'm wrong?

EDIT: Code snippet:

#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);
}

where: -MEMORY_ADDR: is the physical address -BUF_SIZE: number of elements in the buffer -ELEM_SIZE: size of the elements

The flags buffers is an array of chars at the end of such memory region

The full outputs returned are:

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

The processor used is an Armv8 cortex 53

EDIT 2: corrected the previous code snippets, now no kernel panic, however the output is incorrect:

[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

The issue is that I should find the first 'E' at row 0 and row 4 should be instead outside the mapped region. Moreover if now I modify such memory region, the modifications can be seen by this machine, but other machines accessing same memory regions still sees the old values.

1
Can you share code snipped, processor type and relevant kernel log?linkjumper
Sure, I've updated the questionNiccolò Borgioli
Thanks @IanAbbott however, still a question remains? Why the content of the memory area pointed by the virtual address obtained with ioremap() is shifted by 1 Byte respect from what expected? And moreover, can I perform pointers arithmetic on the virtual address returned assuming it is contiguos like the physical ones?Niccolò Borgioli
Problems I can see: (1) res is the virtual address to use, not virt. (2) The ioremap_nocache call is only mapping BUF_SIZE*ELEM_SIZE bytes, but the flags buffer immediately after that area hasn't been mapped. (3) You are not using the special functions intended for accessing ioremapped memory. (4) The printk format specifiers do not match the types of the arguments being printed (see How to get printk format specifiers right but if you are using an older kernel, the details may differ).Ian Abbott

1 Answers

0
votes

The virtual address' returned by ioremap and phys_to_virt are not the same.

ioremap: creates a new mapping page table

phys_to_virt: substract a fixed offset from the address. Check MMU for phy_offset.

How to test?

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);

You will see addr1 and addr2 are different. I think you dont need to use phys_to_virt.

Simply use:

void* virt_addr = ioremap_nocache(MEMORY_ADDR, BUF_SIZE*ELEM_SIZE);