Find the Physical Address of Exception Vector Table from Kernel Module

Find the physical address of exception vector table from kernel module

It is also possible to use the MMU directly (registers ATS1Cxx and PAR) to perform V=>P translation. See section B4.2.4 of the ARM ARM for the gory details.

If you use the MMU registers this way, you might need to protect against races with other uses of the registers.

This maybe accessed from any kernel driver with the following code,

 unsigned int pa;
asm("\t mcr p15, 0, %0, c7, c8, 2\n"
"\t isb\n"
"\t mrc p15, 0, %0, c7, c4, 0\n" : "=r" (pa) : "0" (0xffff0000));
printk("Vector is %x\n", pa & 0xfffff000);

The constants are correct.

  • 0xffff0000 is the high vector virtual address.
  • 0xfffff000 is the mask for 4k pages.

This works only on later series ARM processors such as the Cortex series.

Does ARM use physcial address or a virtual address when entering the vector table?

When paging is enabled and an exception occurs does a translation table walk occur to access the exception vector table at address 0x00000000?

Almost all ARM CPUs have a means to configure the exception table address. So in most systems, the exception vector table is not at address 0x00000000. However, the MMU is enabled when exceptions are taken. The TLB (an MMU/page table cache) will contain the vector table physical address.

In some SOCs the boot vector table maybe at 0x0, but this is usually reconfigured by the boot code.

  1. If paging is still enabled then how do user mode processes and the vector table both share address 0x00000000 - the TTBR (translation table base register) does not get updated on exception entry and the TTBR is not a banked register (we are not talking here about switching between secure and non-secure worlds).

If you want the vector table at address 0x00000000, then it is what user space will see unless you prohibit it. Prohibiting access to 0x0 maybe a desired design to prevent NULL pointer use. Many OSes do not have user space run from 0x0, but an address like 0x8000.

Having user space fault based on a parameter can be very useful as you can trap NULL pointer access while a process is being developed. I would recommend always leaving this on, but some people allow NULL access for production code.


  1. If no then we must enter exceptions using physical addressing in which case is paging now disabled?

No paging is enabled as the cache is probably on as well. The load/store unit of the CPU would be more complex if some accesses are physical and others are virtual; especially as caches are populated by a virtual address in traditional ARM CPUs.

How to convert physical address to kernel page structure inside kernel module (x86-64)?

Use pfn_to_page(pfn_num) macro. Pfn num == phys_addr >> 12

How can I access memory at known physical address inside a kernel module?

You are better off doing this the other way around - have the kernel allocate the buffer, then allow the userspace program to map it into its address space using mmap().

How to get the physical address from the logical one in a Linux kernel module?

Well, it might looks as something like that (follow PTE from an virtual address):

void follow_pte(struct mm_struct * mm, unsigned long address, pte_t * entry)
{
pgd_t * pgd = pgd_offset(mm, address);

printk("follow_pte() for %lx\n", address);

entry->pte = 0;
if (!pgd_none(*pgd) && !pgd_bad(*pgd)) {
pud_t * pud = pud_offset(pgd, address);
struct vm_area_struct * vma = find_vma(mm, address);

printk(" pgd = %lx\n", pgd_val(*pgd));

if (pud_none(*pud)) {
printk(" pud = empty\n");
return;
}
if (pud_huge(*pud) && vma->vm_flags & VM_HUGETLB) {
entry->pte = pud_val(*pud);
printk(" pud = huge\n");
return;
}

if (!pud_bad(*pud)) {
pmd_t * pmd = pmd_offset(pud, address);

printk(" pud = %lx\n", pud_val(*pud));

if (pmd_none(*pmd)) {
printk(" pmd = empty\n");
return;
}
if (pmd_huge(*pmd) && vma->vm_flags & VM_HUGETLB) {
entry->pte = pmd_val(*pmd);
printk(" pmd = huge\n");
return;
}
if (pmd_trans_huge(*pmd)) {
entry->pte = pmd_val(*pmd);
printk(" pmd = trans_huge\n");
return;
}
if (!pmd_bad(*pmd)) {
pte_t * pte = pte_offset_map(pmd, address);

printk(" pmd = %lx\n", pmd_val(*pmd));

if (!pte_none(*pte)) {
entry->pte = pte_val(*pte);
printk(" pte = %lx\n", pte_val(*pte));
} else {
printk(" pte = empty\n");
}
pte_unmap(pte);
}
}
}
}


Related Topics



Leave a reply



Submit