Page 1 of 3

Triple fault when enabling paging

Posted: Sun Feb 14, 2016 6:42 am
by osdever
Sorry if my code is completely wrong, I'm not familiar with Paging.
Code is triple-faulting on setting bit in CR0.
Here's my code:

Code: Select all

//paging.c
#include "../../../include/arch/i686/paging.h"

#include <stdint.h>
#include <stddef.h>

#include "../../../include/stdio.h"

page_directory_t pgdir[1024] __attribute__((aligned(4096)));
page_table_t     pgtbl[1024] __attribute__((aligned(4096)));

void setup_pgdir(page_directory_t pgdir[1024])
{
    for(int i=0; i<1024; i++)
    {
        //Those page directory entries AREN'T USABLE. It just fills all page directory by not present page tables.
        pgdir[i].present    = 0;
        pgdir[i].rw_flag    = 1;
        pgdir[i].access_lvl = 0;
    }
}

void paging_ident(uint32_t* tbl)
{
    size_t size=4096*1024;
    uint32_t from=0x0;
    //Code from OSDev.org.
    from &= 0xfffff000; // discard bits we don't want
    for(;size>0;from+=4096,size-=4096,tbl++)
    {
        *tbl=from|1;     // mark page present.
    }
}

void setup_paging()
{
    void load_pg_dir(uint32_t*);
    void paging_enable(void);
    setup_pgdir(pgdir);
    paging_ident((uint32_t*)pgtbl);
    load_pg_dir((uint32_t*)pgdir);
    paging_enable();
}
//paging.h
#ifndef PAGING_H
#define PAGING_H

typedef struct page_directory_t page_directory_t;
typedef struct page_table_t page_table_t;

struct page_directory_t
{
    int present       : 1;
    int rw_flag       : 1;
    int access_lvl    : 1; //0 is for only ring0. 1 is for anybody.
    int write_through : 1;
    int cache_off     : 1;
    int accessed      : 1;
    int zero          : 1;
    int page_size     : 1;
    int reserved      : 3;
    int tbl_addr      : 21;
};

struct page_table_t
{
    int present       : 1;
    int rw_flag       : 1;
    int access_lvl    : 1; //0 is for only ring0. 1 is for anybody.
    int cache_off     : 1;
    int accessed      : 1;
    int dirty         : 1;
    int zero          : 1;
    int global        : 1;
    int reserved      : 3;
    int tbl_addr      : 21;
};

void load_page_directory(page_directory_t);

#endif // PAGING_H
//paging.asm
global load_pg_dir
load_pg_dir: 
        ; Loaging page directory.
	push ebp
	mov ebp, esp
	mov eax, [esp+8]
	mov cr3, eax
	mov esp, ebp
	pop ebp
	ret
global paging_enable
paging_enable: ; Enable the paging by setting bit in CR0.
	push ebp
	mov ebp, esp
	mov eax, cr0
	or eax, 0x80000000
	mov cr0, eax ; Here it crashes, noo :(
	mov esp, ebp
	pop ebp
	ret

Re: Triple fault when enabling paging

Posted: Sun Feb 14, 2016 7:07 am
by iansjack
You should set a breakpoint just before enabling paging. Then use your debugger to examine your Page Table to see that it contains the entries you expect it to.

Re: Triple fault when enabling paging

Posted: Sun Feb 14, 2016 8:00 am
by Nutterts
A page table entry for protected mode is a 32bit value. I just skimmed your code and as far as I can see your code creates entries as a struct that's 40 bytes in size.

You either want to change your structs to use bool and be packed or more reliably use macros to create the entry you want. Below is what I use to create a pml4 entry. It's for 64bit tho but the principle is the same.

Code: Select all

#define MAKE_PML4E(base, avl, pcd, pwt, us, rw, p) \
  (uint64_t)( \
  (base << 12) | \
  (avl << 9) | \
  (pcd << 4) | \
  (pwt << 3) | \
  (us << 2) | \
  (rw << 1) | \
  p)
There are also some other issues so you might want to grab a cup of coffee and read the relevant part of the Intel docs again. The accessed bit is best set to zero on creation for example. Also unless 32bit paging is way different then 64bit the caching bit only selects between write-through and write-back, it doesn't disable caching as a whole.

Re: Triple fault when enabling paging

Posted: Sun Feb 14, 2016 8:14 am
by neon
A page table entry for protected mode is a 32bit value. I just skimmed your code and as far as I can see your code creates entries as a struct that's 40 bytes in size.
Slight correction here - his structures would be 32 bits on any sane compiler that implements bit fields properly.

Re: Triple fault when enabling paging

Posted: Sun Feb 14, 2016 8:18 am
by Nutterts
neon wrote:
A page table entry for protected mode is a 32bit value. I just skimmed your code and as far as I can see your code creates entries as a struct that's 40 bytes in size.
Slight correction here - his structures would be 32 bits on any sane compiler that implements bit fields properly.
At the moment he's defining every member in his structs as an int.

Re: Triple fault when enabling paging

Posted: Sun Feb 14, 2016 8:20 am
by neon
Right, and under the assumption that sizeof(int) = 32 bits, the bit fields he creates would be 32 bits in size. I.e. he is using bit fields - he is not defining a normal structure. If he was not using bit fields, you would be right.

Re: Triple fault when enabling paging

Posted: Sun Feb 14, 2016 8:31 am
by Nutterts
neon wrote:I.e. he is using bit fields - he is not defining a normal structure. If he was not using bit fields, you would be right.
You're absolutely right. Ignore what I said. But that makes me wonder even more whats wrong with it.

Re: Triple fault when enabling paging

Posted: Sun Feb 14, 2016 8:32 am
by neon
catnikita255 - Your code is incomplete. Most importantly, paging_ident doesn't completely identity map the space you want. Think of it from the perspective of the cpu, given only the page directory, how can it find your page table when you marked all of its entries as non-usable? In fact, I don't see anywhere where you set tbl_addr which should store the page frame number.

Re: Triple fault when enabling paging

Posted: Sun Feb 14, 2016 9:54 am
by osdever
neon wrote:catnikita255 - Your code is incomplete. Most importantly, paging_ident doesn't completely identity map the space you want. Think of it from the perspective of the cpu, given only the page directory, how can it find your page table when you marked all of its entries as non-usable? In fact, I don't see anywhere where you set tbl_addr which should store the page frame number.
Oh, I realized it before you. Here's an updated code:

Code: Select all

#include "../../../include/arch/i686/paging.h"

#include <stdint.h>
#include <stddef.h>

#include "../../../include/stdio.h"

// DEPRECATED
page_directory_t pgdir[1024] __attribute__((aligned(4096)));
page_table_t     pgtbl[1024] __attribute__((aligned(4096)));

void setup_pgdir(page_directory_t pgdir[1024])
{
    for(int i=0; i<1024; i++)
    {
        //Those page directory entries AREN'T USABLE. It just fills all page directory by not present page tables.
        pgdir[i].present    = 0;
        pgdir[i].rw_flag    = 1;
        pgdir[i].access_lvl = 0;
    }
}

void paging_ident(page_table_t *tbl)
{
    //Identity map first 4 megabytes of memory.
    size_t size=1024;
    uint32_t mem=0x0;
    for(size_t i=0; i<size; i++, mem+=PAGE_SIZE)
    {
        tbl[i].rw_flag   =1;
        tbl[i].access_lvl=0;
        tbl[i].phys_addr =mem;
        tbl[i].cache_off =1;
        tbl[i].present =1;

    }
}

void setup_paging()
{
    void load_pg_dir(page_directory_t *);
    void paging_enable(void);
    setup_pgdir(pgdir);
    paging_ident(pgtbl);
    pgdir[0].tbl_addr=pgtbl;
    pgdir[0].present    = 1;
    load_pg_dir(pgdir);
    paging_enable();
}

Re: Triple fault when enabling paging

Posted: Sun Feb 14, 2016 10:42 am
by neon
Hello,

I have to assume the code does not work. tbl_addr stores a page frame number not an address. For example, something like pgdir[0].tbl_addr = pgtbl >> 12; should work. I don't quite know what tbl.phys_addr is since its not in the original post, however the same concept applies to the page directory tbl_addr field here. Although in this case you can actually just do "tbl.phys_addr = i" assuming phys_addr = tbl_addr.

Re: Triple fault when enabling paging

Posted: Sun Feb 14, 2016 10:56 am
by osdever
neon wrote:Hello,

I have to assume the code does not work. tbl_addr stores a page frame number not an address. For example, something like pgdir[0].tbl_addr = pgtbl >> 12; should work. I don't quite know what tbl.phys_addr is since its not in the original post, however the same concept applies to the page directory tbl_addr field here. Although in this case you can actually just do "tbl.phys_addr = i" assuming phys_addr = tbl_addr.

phys_addr is a renamed tbl_addr in page_table_t. And it doesn't have anything about table address or index, I forgot to rename it.

Re: Triple fault when enabling paging

Posted: Mon Feb 15, 2016 5:55 am
by osdever
Is anybody here?

Re: Triple fault when enabling paging

Posted: Mon Feb 15, 2016 6:04 am
by iansjack
Have you examined your page table yet to verify it is correct?

Re: Triple fault when enabling paging

Posted: Tue Feb 16, 2016 5:36 am
by osdever
iansjack wrote:Have you examined your page table yet to verify it is correct?
My page directory in 32-bit unsigned integer is 0x9b800003, the page table is 0x00137000. Looks like everything's correct.

Re: Triple fault when enabling paging

Posted: Tue Feb 16, 2016 7:03 am
by iansjack
Your page directory is at a physical address higher than 2GB, and the page table address is much lower in physical memory (just above the 1MB mark)? That just doesn't feel right. I don't know how you are assigning physical pages but I would guess that most people assign the lowest available page first.

Without going in to the full details of how you are managing your pages I just have the gut feeling that your page table is screwed up.