Problems initializing paging

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.
Thpertic
Member
Member
Posts: 56
Joined: Sun Sep 16, 2018 6:46 am

Problems initializing paging

Post by Thpertic »

I'm working on paging. At the moment I only finished the init function while mapping and unmapping is still undone.
This is the init:

Code: Select all

void vmm_init() {
    uint32_t eflags = interrupt_save_disable();
    uint32_t *pd __attribute__((aligned(4096))) = kmalloc();

    /**
     * Using the 'recursive mapping' technique.
     */
    uint32_t self_pde;
    memset(&self_pde, 0, sizeof(self_pde));
    self_pde |= (uint32_t)pd | BIT_PD_PT_PRESENT | BIT_PD_PT_RW;
	pd[PAGE_DIRECTORY_INDEX(PD_VADDR)] = self_pde;

	/**
	 * Mapping the kernel to a 4MB page.
	 */
	uint32_t kernel_pde;
	memset(&kernel_pde, 0, sizeof(kernel_pde));
	kernel_pde |= BIT_PD_PT_PRESENT | BIT_PD_PT_RW | BIT_PD_PAGE_SIZE;
	pd[PAGE_DIRECTORY_INDEX(KERNEL_VIRTUAL_BASE)] = (uint32_t)kernel_pde;

	set_cr3((uint32_t)pd);
	interrupt_restore(eflags);
}
The problem is that I can access pd until I set it as the "official" page directory. In fact when later I try to map a page (or just print it), it Page Faults.

Could you please help me? Am I missing something? Thanks.

This is the mapping code if you need it:

Code: Select all

#define PD_VADDR 0xFFFFF000

#define PAGE_DIRECTORY_ADDR_OFFSET 22
#define PAGE_TABLE_ADDR_OFFSET 12

#define PAGE_DIRECTORY_INDEX(x) (((x) >> PAGE_DIRECTORY_ADDR_OFFSET) & 0x3ff)
#define PAGE_TABLE_INDEX(x) (((x) >> PAGE_TABLE_ADDR_OFFSET) & 0x3ff)

...

uint32_t map_page(uint32_t *phys, uint32_t *virt, uint32_t flags) {
	uint32_t *pd = PD_VADDR;
	uint32_t *pde = pd[(PAGE_DIRECTORY_INDEX((uint32_t)virt))];

	if (!(*pde & BIT_PD_PT_PRESENT)) {
		// Need to create a new page table
		uint32_t pde_phys = kmalloc();
		memset(&pde, 0, sizeof(pde));
		*pde |= pde_phys;
		pd[PAGE_DIRECTORY_INDEX((uint32_t)pde)] = pde;

		memset(PD_INDEX_TO_PT_VADDR(PAGE_DIRECTORY_INDEX((uint32_t)virt)), 0b10, PAGE_SIZE);
		pd[PAGE_DIRECTORY_INDEX((uint32_t)pde)] |= flags;
	}

	uint32_t *pte = pde[PAGE_TABLE_INDEX((uint32_t)virt)];
	memset(&pte, 0, sizeof(pte));

	*pte |= (uint32_t)phys | flags;

	pde[PAGE_TABLE_INDEX((uint32_t)virt)] = pte;
	pd[PAGE_DIRECTORY_INDEX((uint32_t)virt)] = pde;
	return 0;
}
It crashes as soon as pd[something] is accessed.
User avatar
iansjack
Member
Member
Posts: 4705
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Problems initializing paging

Post by iansjack »

Does the page that you have mapped for the kernel include all of the memory used by the page tables?

Edit: Sorry, I missed the fact that you were using recursive mapping. I can never really get my head around that (I find it easier to just map all physical memory to a particular virtual address range). Are you sure you are using the correct virtual addresses to access your page table?
linguofreak
Member
Member
Posts: 510
Joined: Wed Mar 09, 2011 3:55 am

Re: Problems initializing paging

Post by linguofreak »

I suspect you may have a problem with dereferencing a pointer one too many or one too few times, because C pointer syntax is sometimes less clear than it could be.

At first, I thought I'd found the error in:

Code: Select all

uint32_t *pd = PD_VADDR;
But after reviewing pointer syntax that is correct, though it would probably be clearer to do the same thing in two statements:

Code: Select all

uint32_t *pd;
pd = PD_VADDR;
Your code is equivalent to the above, but because C pointer syntax can be deceptive, it looked at first like it was equivalent to:

Code: Select all

uint32_t *pd;
*pd = PD_VADDR;
By doing it in two statements, you can make clearer to the reader (including yourself, later) that you mean the first and not the second.

It may well be that your problem is hiding behind a similar statement somewhere else that doesn't do quite what you think it does.
User avatar
iansjack
Member
Member
Posts: 4705
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Problems initializing paging

Post by iansjack »

Looking more closely, in your mapping function you aren't converting the addresses for the page table entries into virtual addresses before accessing the tables. So as soon as you try to access them you are likely to get a page fault. If that's not handled you'll get a triple fault.

Do you have an exception handler for page faults? You should, even if all it does is to halt the processor. Then, running under a debugger, you will be able to see where the page fault is happening, and why. Similarly, you should have a basic handler for GP exceptions.
Thpertic
Member
Member
Posts: 56
Joined: Sun Sep 16, 2018 6:46 am

Re: Problems initializing paging

Post by Thpertic »

linguofreak wrote:By doing it in two statements, you can make clearer to the reader (including yourself, later) that you mean the first and not the second.
Ok, I’ll take your advice!
iansjack wrote:Looking more closely, in your mapping function you aren't converting the addresses for the page table entries into virtual addresses before accessing the tables. So as soon as you try to access them you are likely to get a page fault. If that's not handled you'll get a triple fault.
Sorry, could you explain this further with some examples?
iansjack wrote:Do you have an exception handler for page faults? You should, even if all it does is to halt the processor. Then, running under a debugger, you will be able to see where the page fault is happening, and why. Similarly, you should have a basic handler for GP exceptions.
I have an exception handler, in fact it prints the type of fault and every register. Maybe I can attach the bochs debugger...
User avatar
iansjack
Member
Member
Posts: 4705
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Problems initializing paging

Post by iansjack »

Each level of the page table (except the lowest) contains the physical address of a lower-level table. You are just taking these addresses and using them to access the next level. But you can't do this when paging is enabled. You need to take the physical address and then convert it to the corresponding virtual address, then use that as the address of the next-level table.

What you are doing will only work if all the addresses you are using are identity mapped. This, in general, won't be the case.

PS: One of the clearest explanations of page tables that I've seen is here: https://os.phil-opp.com/paging-introduction/ It's written from the perspective of Rust rather than C but, together with the following article in the series, explains things very clearly.
Octocontrabass
Member
Member
Posts: 5581
Joined: Mon Mar 25, 2013 7:01 pm

Re: Problems initializing paging

Post by Octocontrabass »

iansjack wrote:You need to take the physical address and then convert it to the corresponding virtual address, then use that as the address of the next-level table.
Recursive mapping makes it easier. All of the paging structures are located at fixed virtual addresses, and you can index them directly from the address of the page you want to manipulate.

For example, assuming plain 32-bit paging with recursion in the last PDE, you'd access the PTE and PDE for a particular page something like this:

Code: Select all

uint32_t * pt = (uint32_t *)0xFFC00000;
uint32_t pte = pt[(uint32_t)virt >> 12];

uint32_t * pd = (uint32_t *)0xFFFFF000;
uint32_t pde = pd[(uint32_t)virt >> 22];
(Note: this is for illustration purposes only; I haven't verified this code works correctly, and it may invoke undefined behavior.)
Thpertic
Member
Member
Posts: 56
Joined: Sun Sep 16, 2018 6:46 am

Re: Problems initializing paging

Post by Thpertic »

Octocontrabass wrote:For example, assuming plain 32-bit paging with recursion in the last PDE, you'd access the PTE and PDE for a particular page something like this:

Code: Select all

uint32_t * pt = (uint32_t *)0xFFC00000;
uint32_t pte = pt[(uint32_t)virt >> 12];

uint32_t * pd = (uint32_t *)0xFFFFF000;
uint32_t pde = pd[(uint32_t)virt >> 22];
(Note: this is for illustration purposes only; I haven't verified this code works correctly, and it may invoke undefined behavior.)
I'm doing this for the PD only, should I do it for every PT too? Isn't that too much?

Currently the init function works like this:

Code: Select all

void init_vmm() {
    uint32_t eflags = interrupt_save_disable();
    uint32_t *pd __attribute__((aligned(4096))) = kmalloc();
    uint32_t self_pde;
    memset(&self_pde, 0, sizeof(self_pde));
    self_pde |= (uint32_t)pd | BIT_PD_PT_PRESENT | BIT_PD_PT_RW;
    pd[PAGE_DIRECTORY_INDEX(PD_VADDR)] = self_pde;

    // repeat for kernel (4MB page), set CR3 and restore registers
}
Maybe self_pde/other pdes should point to a physical address obtained by the pmm? Sorry for the dumb questions, still a noob :lol:
User avatar
iansjack
Member
Member
Posts: 4705
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Problems initializing paging

Post by iansjack »

All addresses in all page tables are physical addresses. To access them you have to use virtual addresses mapped to the corresponding physical address. The articles that I linked to explain this with diagrams.
Octocontrabass
Member
Member
Posts: 5581
Joined: Mon Mar 25, 2013 7:01 pm

Re: Problems initializing paging

Post by Octocontrabass »

Thpertic wrote:I'm doing this for the PD only, should I do it for every PT too? Isn't that too much?
This is the easiest way to access your page structures when you're using recursive mapping, why wouldn't you want to do it this way?
Thpertic wrote:Maybe self_pde/other pdes should point to a physical address obtained by the pmm? Sorry for the dumb questions, still a noob :lol:
All entries in your page tables must be physical addresses.
Thpertic
Member
Member
Posts: 56
Joined: Sun Sep 16, 2018 6:46 am

Re: Problems initializing paging

Post by Thpertic »

Octocontrabass wrote:
Thpertic wrote:I'm doing this for the PD only, should I do it for every PT too? Isn't that too much?
This is the easiest way to access your page structures when you're using recursive mapping, why wouldn't you want to do it this way?
Aren't one for each Page Table too many addresses to "remember"?

When mapping the single frame, it has to be physical, all right.

Now, I need the physical address of the Page Directory (to set in CR3), but I need to map it otherwise a PF comes when I try to access it. But how do I map it if I don't have a PD yet? Or better, I have one but I don't want to use it as that was just to boot. Then the same thing happens for the Page Tables, however, I don't need to access them now (probably this problem will come later while mapping the page).

For example:

Code: Select all

void init() {
    int *pd;
    pd = get_physical_addr();

    int *self_pde
    self_pde = get_physical_addr() | flags;
    pd[0xFFFFFF000 >> 22] = self_pde;  // <-- here the os Page Faults because pd is a physical address not mapped

    // Other things...
}
User avatar
iansjack
Member
Member
Posts: 4705
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Problems initializing paging

Post by iansjack »

The follow up article to the one I linked you to explains this problem and the various options to deal with it.

https://os.phil-opp.com/paging-implementation/
Thpertic
Member
Member
Posts: 56
Joined: Sun Sep 16, 2018 6:46 am

Re: Problems initializing paging

Post by Thpertic »

Thank you! Just for curiosity, what approach do you think is the best?

I think i’m going with the recursive paging technique because the others seems too “expensive”.
For example, mapping the entire memory with a fixed offset at boot doesn’t sound very well? I mean, it will work without a doubt but I won’t map 1024 page tables :lol:
User avatar
iansjack
Member
Member
Posts: 4705
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Problems initializing paging

Post by iansjack »

I use the "mapping all memory" technique, but I am working in 64-bit mode where there is loads of virtual memory address space.
User avatar
JAAman
Member
Member
Posts: 879
Joined: Wed Oct 27, 2004 11:00 pm
Location: WA

Re: Problems initializing paging

Post by JAAman »

personally, I consider mapping all physical memory to be a basic security violation -- you are giving access to memory that belongs to another context


there is never a reason to map physical memory, never (there is nothing that mapping physical memory will make possible)
physical memory should never be mapped (violation of both separation and isolation)
physical memory must never be mapped (security violation)


just my opinion
Post Reply