Search code examples
linuxlinux-kerneloperating-systemarmpage-tables

Does virt_to_pfn checks whether the page table exists or not


Does virt_to_pfn checks whether the page table is present or not. Below is the code which is failing with page fault.

#include <linux/module.h>

#define address 0xf0000000
int init_module(void)
{
    struct page *page;
    int32_t reserved;
    int32_t private;
    uint32_t test  = 0xAAAAAAAA;
    uint32_t saveValue;
    unsigned long pfn = 0;
    static uint32_t *testAddr = (uint *)address;

    pfn = virt_to_pfn(address);
    printk("Test!:%x, page frame number:%lu\n", address, pfn);
    if ( !pfn_valid(pfn) ) {
           printk("Page frame number not valid\n");
    } else {
           printk("Page Frame Number valid\n");
    }

     page = ( struct page * )virt_to_page(address);
         if ( page == NULL )
     {
         printk("NO page exist\n");
         return -1;
     }
     reserved = PageReserved( page );
     private = PagePrivate( page );
     if (reserved)
         printk("Page reserved\n");
     if (private)
         printk("Page Private\n");

     saveValue = ( uint32_t )*testAddr;
     printk("Save value:%u\n", saveValue);
    return 0;
}

void cleanup_module(void)
{
    printk("Goodbye Cruel World!\n");
}

MODULE_LICENSE("GPL");

Fails with the below error:

root@qemuarm:~# modprobe hello
[   33.283359] hello: loading out-of-tree module taints kernel.
[   33.290515] Test!:f0000000, page frame number:458752
[   33.290708] Page Frame Number valid
[   33.291169] 8<--- cut here ---
[   33.291280] Unable to handle kernel paging request at virtual address f0000000
[   33.291704] pgd = afcbaf1a
[   33.291832] [f0000000] *pgd=80000040007003, *pmd=00000000
[   33.292313] Internal error: Oops: 206 [#1] PREEMPT SMP ARM
[   33.292589] Modules linked in: hello(O+)
[   33.293078] CPU: 0 PID: 323 Comm: modprobe Tainted: G           O      5.4.172-yocto-standard #1
[   33.293368] Hardware name: Generic DT based system
[   33.293926] PC is at init_module+0x94/0xc8 [hello]
[   33.294078] LR is at init_module+0x40/0xc8 [hello]
[   33.294243] pc : [<bf000094>]    lr : [<bf000040>]    psr: 600f0013
[   33.294549] sp : ed879dd0  ip : 2e85d000  fp : bf002000
[   33.294712] r10: ed879f40  r9 : f0815df0  r8 : 00000002
[   33.294838] r7 : 00000000  r6 : c10a78c0  r5 : bf000000  r4 : 00000000
[   33.295019] r3 : f0000000  r2 : 40000000  r1 : 00000007  r0 : bf0010c3
[   33.295255] Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
[   33.295510] Control: 30c5387d  Table: 6e13d300  DAC: fffffffd
[   33.295656] Process modprobe (pid: 323, stack limit = 0x04adcafd)

RAM Size is 1024MB, the below code works with kernel virtual addresses from 0xc000 0000 - 0xefff ffff. 0xffff 0000 is where the high mem region starts.

Will the logic changes for high mem region access


Solution

  • The results of virt_to_pfn(kaddr) and virt_to_page(kaddr) are valid if and only if virt_addr_valid(kaddr) is true. That is only true if kaddr is in the linear-mapped (lowmem) region of kernel virtual address space.

    pfn_valid(virt_to_pfn(kaddr)) is not equivalent to virt_addr_valid(kaddr). virt_addr_valid(kaddr) implies pfn_valid(virt_to_pfn(kaddr)) but pfn_valid(virt_to_pfn(kaddr)) does not imply virt_addr_valid(kaddr).