Most people say, "a person is 10000 times more interested in themselves than you" so here are some things about me.
I am a firmware engineer doing embedded Linux stuff on ARM architecture. I have particular interest in kernel hacking and I've been on and off playing with the kernel code and drivers since 2002. For now, my short-term goal is to become an expert, or at least have an intermediate level of understanding in the Linux Memory Management area.
In the past, I've touched bootup code (from head.S to /bin/init) and developed various device drivers so I'll try to help in any way I can. I so liked the idea of the Open-Source Community and see how everyone helps each other that's why I'm here hoping to be able to lend a hand and also grab one for help.
ARM Page Tables
- ARM Implements a two-fold page implementation of its page tables. Linux uses a three-level organization of the page tables. So, ARM implementation tweaks this Linux requirement a bit.
- There is no bad PGD and there will always be a PMD, thus, some of the typical Linux pgd/pmd functions are trivial for ARM.
- At the 1st level is the PGD. An entry on the PGD points to an address that holds the address of the 1st HW page table.
- The 2nd level (PMD) table then is just another table which holds two addresses. This 2nd level table holds the actual addresses of the 1st and 2nd HW page tables only. SW page tables (Linux version) are easily obtained by offsetting 0x800 (2048) from the HW page table address.
- In this context, a HW or SW page table is referred to as a HW or SW PTE table. There are 512 entries (PTE's) in each PTE table. Each entry in a PTE table is called the PTE (Page Table Entry).
- Each PTE describes one 4K-page that belongs to the process.
pgd -> | ... | | ... | | C09FC000 | | ... | | C09FC008 | C09FC010 | 00000000 | | C09FC010 | C09FC014 | 00000000 | | ... | | C09FC018 | -> C09FC018 | C0FE1011 | -> (C0FE1011 & PAGE_MASK) + 2048 | PT entry 0 | | C09FC020 | C09FC01C | C0FE1411 | | PT entry 1 | | C09FC028 | | ... | | PT entry 2 | | ... | | ... |
get_user(unsigned long val, unsigned long *addr); access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write);
Basically both functions fetch values from user space to kernel space. The difference, in my experience recently, is that get_user increments by sizeof(int) while access_process_vm increments by bytes. In other words, the following are equivalent with respect to decrementing pc:
access_process_vm(tsk, pc - 2*sizeof(unsigned int), &word, sizeof(word), 0); get_user (word, pc - 2);