Page 1 of 2
Paging and INVLPG
Posted: Wed Sep 19, 2007 12:47 am
by pcmattman
Hi everyone,
I've noticed recently that my OS is using a lot of the cached entries in the TLB for paging, even after modifying the entry. I did some research and found the INVLPG instruction but I've had trouble with it.
This is my current method for mapping a physical page:
Code: Select all
/// Flushes a TLB
static inline void pgFlushOneTlb( char* m )
{
asm volatile( "invlpg %0" : : "m" (*m) );
}
/// Sets a physical address mapping
void MapPhysicalAddress( uint_t pdir, uint_t virt_addr, uint_t phys_addr, uint_t flags )
{
// get a pointer to the page directory
uint_t* local_pagedir = (uint_t*) pdir;
// make sure each address is aligned to a page boundary
virt_addr &= ~0xFFF;
phys_addr &= ~0xFFF;
// check that we do actually have a page table, if not, allocate a new one
if( ( local_pagedir[virt_addr >> 22] & ~0xFFF ) == 0 )
{
AllocatePageTable( pdir, virt_addr, flags );
}
// set the mapping in the page table
((uint_t*) (local_pagedir[virt_addr >> 22] & ~0xFFF ))[virt_addr << 10 >> 22] = phys_addr | flags;
// invalidate the old mapping
char* vaddr = (char*) virt_addr;
pgFlushOneTlb( vaddr );
}
However, if (for example) I change the mapping on a page from SUPERVISOR to USER I still get the page fault when I attempt to access that page.
Is there anything I'm missing here?
Posted: Wed Sep 19, 2007 1:36 am
by JamesM
I for one could never get invlpg to work. Eventually I gave up and used a hack:
Which flushes the entire TLB. Note that this was on my old kernel, I haven't noticed page-caching problems yet on my new one. Possibly because I have yet to implement copy-on-write...
JamesM
Posted: Wed Sep 19, 2007 1:38 am
by pcmattman
Won't that make address mapping really slow?
Posted: Wed Sep 19, 2007 2:57 am
by JamesM
Yes, but it'll work, which is what the intention was when I wrote it. I've got a //HACK!!!! comment above it so I think I was intending to change it...
As I say, that was my last (test) kernel, I havent implemented it in my current one yet.
Posted: Wed Sep 19, 2007 4:27 am
by Combuster
compare
the manuals state that invlpg invalidates the tlb entry used when accessing the operand. For that reason, page-invalidating a register does not need to do anything.
Posted: Wed Sep 19, 2007 7:27 am
by matthias
Code: Select all
__asm__ __volatile__("invlpg %0" : : "m" (virtual));
where virtual is the virtual address of the page;
i.e.: unsigned long virtual = 0xa0001000;
for example
Posted: Wed Sep 19, 2007 11:06 am
by Combuster
just checked the suggestion,
Code: Select all
__asm__ __volatile__("invlpg %0" : : "m" (virtual));
23: 0f 01 7d fc invlpg 0xfffffffc(%ebp)
this invalidates the TLB corresponding to the stack
edit: this version generates the correct opcode:
Code: Select all
__asm__ __volatile__("invlpg (%%eax)" : : "a" (address) );
Posted: Wed Sep 19, 2007 11:36 am
by jnc100
My version (to add to the ever growing list here) that works:
Code: Select all
#define invlpg(address) \
__asm volatile ( "invlpg %0" : : "m" (*(char *)(address)) );
Where address is an integer containing the address, rather than a void pointer, e.g. uint32_t address
Regards,
John.
Posted: Thu Sep 20, 2007 5:13 pm
by vhg119
I'm having somewhat of the same problem. I thought I was going crazy.
I've used the WRITE_CR3(READ_CR3()) suggestion as well as the INVLPG suggestion but I'm still getting errors accessing memory through its virtual address.
I've even written a test function that tests whether my physical address mapper was doing the correct thing... and it looks like it is. The corresponding PDE has the right address of the page table. The PTE in the page table has the correct address of the page. But when I try to access the page through its virtual address... 3rd (13) exception with no resolution! The value in CR2 is pointing right to the virtual address I was sure I had just mapped.
These are the macros I'm using to no avail.
Code: Select all
#define READ_CR3() (__extension__({ unsigned int __res; __asm__ ("movl %%cr3, %%eax\n\t" : "=a" (__res) :); __res;}))
#define WRITE_CR3(value) __asm__ ( "movl %%eax, %%cr3\n\t"::"A" (value))
//#define INVLPG(value) __asm__ __volatile__( "invlpg (%%eax)\n\t"::"A" (value))
#define INVLPG(value) __asm__ __volatile__( "invlpg %0\n\t"::"m" (*(char*)(value)))
pcmattman, please let me know if you find a solution.
Posted: Thu Sep 20, 2007 5:51 pm
by Combuster
vhg119 wrote:I've even written a test function that tests whether my physical address mapper was doing the correct thing... and it looks like it is. The corresponding PDE has the right address of the page table. The PTE in the page table has the correct address of the page. But when I try to access the page through its virtual address... 3rd (13) exception with no resolution! The value in CR2 is pointing right to the virtual address I was sure I had just mapped.
Start the bochs debugger, breakpoint to the faulting instruction, then dump all registers and memory locations where the page table entries involved are stored, then copy them here. I have a nagging suspicion that it isn't the macro's that are causing you trouble.
Posted: Thu Sep 20, 2007 6:11 pm
by vhg119
Combuster wrote:vhg119 wrote:I've even written a test function that tests whether my physical address mapper was doing the correct thing... and it looks like it is. The corresponding PDE has the right address of the page table. The PTE in the page table has the correct address of the page. But when I try to access the page through its virtual address... 3rd (13) exception with no resolution! The value in CR2 is pointing right to the virtual address I was sure I had just mapped.
Start the bochs debugger, breakpoint to the faulting instruction, then dump all registers and memory locations where the page table entries involved are stored, then copy them here. I have a nagging suspicion that it isn't the macro's that are causing you trouble.
Crap... I've never used bochs with the debugger and I don't think mine is compiled with the option... (I apt-got it on Ubuntu).
Let me learn how to get the debugger working and I'll get back to you.
Posted: Thu Sep 20, 2007 8:08 pm
by vhg119
OH MAN!!! Thanks for the suggestion Combuster...
Bochs' debugger revealed what was wrong.
I'm mapping 0xA000000 to a newly allocated page. This also requires me to allocate space for a new page table, and create a new entry in the page directory.
so....
I used the last PTE in one of my existing page tables to map the page directory and new page table so that I can write data to them.
When I map the pdir to write the new page table's address, it worked fine.
Then, I used the same PTE to map in the new page table so I can map in a new physical page. This is the problem. I never invalidated the cache so when I thought I was writing to the new page table, I was actually still writing to the page directory.
I had to invalidate the cache between writing to the page dir and writing to the page table.
I hope that makes sense. Please let me know if it doesn't.
Posted: Fri Sep 21, 2007 8:27 pm
by bewing
vhg119 wrote:I had to invalidate the cache between writing to the page dir and writing to the page table.
Combuster wrote:compare
the manuals state that invlpg invalidates the tlb entry used when accessing the operand. For that reason, page-invalidating a register does not need to do anything.
These sound exactly like the sort of desperately needed tips that must go into the wiki.
Posted: Sat Sep 22, 2007 12:09 am
by speal
I'm working with long mode page tables, so it may be different, but I certainly don't have to invalidate the tlb between writing to page directories and page tables. I do call invlpg after completing the mapping process.
If you're mapping in the physical address of the page table so you can write to it, then you'll have to call invlpg simply because you altered the page table. It makes no sense that you'd need to reload the page tables before mapping anything new though.
Correct me if I'm wrong!
Posted: Sat Sep 22, 2007 2:29 am
by urxae
I think he's mapping in the page dir at a certain address, inserts the page table, then remaps that same virtual address address to the page table he just added so he can modify it. His problem was that he forgot to invlpg in-between, so he was writing to the page dir when he thought he was writing to the page table.
He's also doing it in the reversed order I would do it by the way; if I mapped the page dir and page tables in on-demand, I'd make sure to correctly initialize the page table before adding it to the page dir. But since I'm using fractal mapping it's simpler to just map the page table in and write to the correct spot in the upper 4 MB of memory...