Page 1 of 3

Maybe a Stupid Question

Posted: Tue Feb 15, 2005 6:21 am
by ich_will
I've the following problem:

Currently I'm writing my memory manager and all seems to work fine except for one thing: I can't lock pages again, if they're present one time.

Here is my function for setting page attributes:

unsigned int paging_set_pages(unsigned long from_addr, unsigned long to_addr, unsigned short flags)
{
unsigned long i, limit;
// round addresses
from_addr = MAKE_4K_ALIGN_DOWN(from_addr);
to_addr = MAKE_4K_ALIGN_DOWN(to_addr);

if(from_addr > to_addr || VIRTUAL_ADDRESS_TO_PAGE_INDEX(to_addr) > glb_pages_count)
{
puts("\tpaging_set_pages: invalid address range");
return to_addr;
}

for(i = VIRTUAL_ADDRESS_TO_PAGE_INDEX(from_addr),
limit = VIRTUAL_ADDRESS_TO_PAGE_INDEX(to_addr); i <= limit; i++)
{
PAGE_TABLES = CLR_L12_BITS(PAGE_TABLES);
PAGE_TABLES |= (flags ^ CLR_L12_BITS(flags));
}

PAGE_DIRECTORY[i/PAGES_PER_DIR] = CLR_L12_BITS(PAGE_DIRECTORY[i/PAGES_PER_DIR]);
PAGE_DIRECTORY[i/PAGES_PER_DIR] |= flags|PAGE_FLAG_PRESENT; // <--this is only for this time, later i will check if any page in the directory is present, and if not, i will disable the dir
// <-- is that the mistake?

return 0;
}

I think you haven't to disable the pagedirs for disable one page. So where is the mistake? --> I also have a function which prints a map of present and non present pages. If I can trust this function all should work fine. But it doesn't.

I test it with this loop:

for(i = 0; i > 4096; i++)
{
*((char*)(0x617000 + i)) = '1'; // 0x617000 is the first avaible page after kernel loading and memmanger initializing
}

if i run this loop before i allocate anything a page fault happens, but after i allocate and free some memory it doesn't. Though my print function for the memmap say the page isn't present.

Re:Maybe a Stupid Question

Posted: Tue Feb 15, 2005 6:33 am
by distantvoices
Hm ... what do you mean with "disabling a page". Freeing it?
And what is "locking a page"?

If you wanna free a page - set the corresponging slot in the pagetable/pagedir(if freeing a page table) to zero - or clear the present-flag. Then you should get a pagefault as is due upon access of a not-mapped-in area.

then you mark the page as available in your bookkeeping structures for physical memory.

hth and no, there ain't no stupid questions

Re:Maybe a Stupid Question

Posted: Tue Feb 15, 2005 6:58 am
by ich_will
thx,

disabling a page = set the PRESENT_BIT to 0
locking a page = protect a page --> equal to disabling in this case

thats what I do, i clear the PRESENT_BIT but I get no page fault

Re:Maybe a Stupid Question

Posted: Tue Feb 15, 2005 7:35 am
by distantvoices
If you want to clear a given bit in a bitmask, use this:

Code: Select all

variable&=~BIT_TO_CLEAR;
the tilde causes the BIT_TO_CLEAR come along inverted:

say a given bit 00000010 (0x02)
will become 11111101. AND this with any bitmask and every bit except bit Nr. 2 will remain untouched - this one will be set to Zero. 1 AND 0 = 0. (refer to your average truth tables *gg*)

HtH

Re:Maybe a Stupid Question

Posted: Tue Feb 15, 2005 7:38 am
by ich_will
I don't know why and I think that's not normal but, I only get pagefaults, if the PAGE_PRESENT_BIT of the PAGE_DIR is zero. I can access a PAGE in this PAGE_DIR even though the PAGE_PRESENT_BIT of the page i try to access is zero.

What's going on here?

Re:Maybe a Stupid Question

Posted: Tue Feb 15, 2005 7:42 am
by mystran
Also remember to invalidate TLB entries for pages you remove, unless you know you will be reloading the page directory before you need it to page fault. Otherwise the old entry might be in a TLB and the page tables might not be checked at all.

TLB invalidation problems have the nasty feature that sometimes everything might work fine, while other times the page table modications don't seem to have any effect.

Re:Maybe a Stupid Question

Posted: Tue Feb 15, 2005 7:46 am
by ich_will
srry but what are TLBs, i think you don't mean TBL=Table

Re:Maybe a Stupid Question

Posted: Tue Feb 15, 2005 7:50 am
by distantvoices
tlb = transaction lookaside buffer

that's an area in the cpu/mmu where already resolved pages are stored for quick lookup.

So to say: first resolving a virtual address to the physical page -> store the page in the TLB.

having to fiddle with a virtual address area again - first look it up in the TLB and only resolve via pagedir->pagetable->offset if not found there.

invalidating an entry in the TLB means nothing else than throwing it away.

Re:Maybe a Stupid Question

Posted: Tue Feb 15, 2005 8:06 am
by ich_will
ok, should i simply do the following after every change?:

mov cr3, page_dir ; this flushes the complete tlb or? --> also if its the same value than before?

or how can I invalidate a single entry of the tlb?

Re:Maybe a Stupid Question

Posted: Tue Feb 15, 2005 8:17 am
by distantvoices
Have to correct myself: tlb = translation lookaside buffer. stupid me.

Have googled for it: invlpg ebx f. ex. where in ebx you store the address of the physical page you wanna drop outta tlb.

Maybe our wiki has more indepth information about this command.

Concerning your question about reloading cr3 upon each change: I wouldna do this as it trashes the complete TLB and is a kinda ritus in itself: loading the register and doing an absolute jump to flush the tlb. invlpg is rather quick at hand it seems, so, I'd do it the invlpg way as long as the address space remains the same.

Pype? Mystran? D'ya have some input about this?

Re:Maybe a Stupid Question

Posted: Tue Feb 15, 2005 8:24 am
by ich_will
thanks, i'll try it --> but what should i do on a 386 (or have is there cpu no tlb?) because this instruction exists only for 468+

Re:Maybe a Stupid Question

Posted: Tue Feb 15, 2005 8:31 am
by distantvoices
on an i386, I think, you reload cr3 and make that jump in order to flush tlb. Hm ... gonna look it up myself. I don't know it for sure.

Re:Maybe a Stupid Question

Posted: Tue Feb 15, 2005 8:33 am
by Pype.Clicker
page mappings that have been resolved once by the CPU are kept in the Translation Lookaside Buffer ... If you change the mapping or "disable" the translation, you should clear the corresponding TLB entry with "invlpg" instruction, otherwise the CPU simply continues using the (now obsolete) cache content...

Re:Maybe a Stupid Question

Posted: Tue Feb 15, 2005 8:57 am
by Brendan
Hi,

For an 80386 you must reload CR3 (which does trash the entire TLB, but this doesn't matter too much because the difference in speed between the TLB and RAM isn't that high for these old computers). For 80486 or higher you should use INVLPG.

For Pentium (I think) and above there's a "global page" flag in the page table entries which should be used. This flag (when set) prevents the page from being flushed from the TLB when CR3 is reloaded. By enabling global pages and setting all of the kernel's pages to global you can improve the cost of context switches (changing address spaces) a fair bit. Support for global pages is detected via. CPUID and it's enabled using the PGE flag in CR4 (bit 7).

If you do use global pages then you must use INVLPG (at least for the kernel's pages), as reloading CR3 won't flush the TLB's anymore.

IMHO using global pages and INVLPG is very important for performance now, especially for Pentium IV where accessing RAM is incredibly slow compared to the CPU's TLB.

To make things easier I don't bother supporting 80386's anymore - I searched for a working second hand 80386 for about 6 months but couldn't find one.


Cheers,

Brendan

Re:Maybe a Stupid Question

Posted: Tue Feb 15, 2005 9:05 am
by ich_will
thx

srry, but how does this invplg instruction work?

i tried the following:

__invlpg
push ebp
mov ebp, esp
push ebx

mov ebx, [ebp + 8]???; page_dir
invlpg [ebx] ; Invalidate Translation Look-Aside Buffer Entry: WARNING 486+ only

pop ebx
pop ebp
ret

and call it from C in the loop from the function at the top at that thread:

for(i = VIRTUAL_ADDRESS_TO_PAGE_INDEX(from_addr),
limit = VIRTUAL_ADDRESS_TO_PAGE_INDEX(to_addr); i <= limit; i++)
{
PAGE_TABLES = CLR_L12_BITS(PAGE_TABLES);
PAGE_TABLES |= (flags ^ CLR_L12_BITS(flags));
_invlpg(PAGE_TABLES);
}

but the cpu only resets with an page fault --> shouldn't i call invlpg if the cpu has paging disabled?