Paging and INVLPG

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Paging and INVLPG

Post 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?
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

I for one could never get invlpg to work. Eventually I gave up and used a hack:

Code: Select all

write_cr3(read_cr3());
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
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

Won't that make address mapping really slow?
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post 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.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Post by Combuster »

compare

Code: Select all

invlpg eax
invlpg [eax]
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.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
matthias
Member
Member
Posts: 158
Joined: Fri Oct 22, 2004 11:00 pm
Location: Vlaardingen, Holland
Contact:

Post 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
The source of my problems is in the source.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Post 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) );
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
jnc100
Member
Member
Posts: 775
Joined: Mon Apr 09, 2007 12:10 pm
Location: London, UK
Contact:

Post 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.
vhg119
Member
Member
Posts: 71
Joined: Fri Aug 24, 2007 5:56 pm
Location: CA, USA

Post 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.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Post 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.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
vhg119
Member
Member
Posts: 71
Joined: Fri Aug 24, 2007 5:56 pm
Location: CA, USA

Post 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.
vhg119
Member
Member
Posts: 71
Joined: Fri Aug 24, 2007 5:56 pm
Location: CA, USA

Post 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.
User avatar
bewing
Member
Member
Posts: 1401
Joined: Wed Feb 07, 2007 1:45 pm
Location: Eugene, OR, US

Post 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

Code: Select all

invlpg eax 
invlpg [eax] 
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.
speal
Member
Member
Posts: 43
Joined: Wed Mar 07, 2007 10:09 am
Location: Minneapolis, Minnesota
Contact:

Post 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!
urxae
Member
Member
Posts: 149
Joined: Sun Jul 30, 2006 8:16 am
Location: The Netherlands

Post 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...
Post Reply