OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 3:38 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 7 posts ] 
Author Message
 Post subject: Triple fault on page enable
PostPosted: Sat Jul 02, 2022 10:31 am 
Offline
Member
Member

Joined: Tue Aug 31, 2021 7:25 am
Posts: 67
For some reason when I try enable paging I am getting a page fault which eventually turns into a triple fault right after enabling paging before returning from the assembly function call.
Is there anything obvious I have missed?
The code should map everything 1:1 to start off with as a test.

Code:
struct PageDirectory {
    unsigned Present : 1;
    unsigned ReadWrite : 1;
    unsigned UserSupervisor : 1;
    unsigned CacheWriteThrough : 1;
    unsigned CacheDisable : 1;
    unsigned Accessed : 1;
    unsigned NotUsed0 : 1;
    unsigned PageSize : 1;
    unsigned NotUsed1 : 4;
    unsigned Address : 20;
} __attribute__((packed));

struct PageEntry {
    unsigned Present : 1;
    unsigned ReadWrite : 1;
    unsigned UserSupervisor : 1;
    unsigned CacheWriteThrough : 1;
    unsigned CacheDisable : 1;
    unsigned Accessed : 1;
    unsigned Dirty : 1;
    unsigned PageAttributeTable : 1;
    unsigned Global : 1;
    unsigned NotUsed0 : 3;
    unsigned Address : 20;
} __attribute__((packed));


Code:
extern "C" __attribute__((fastcall)) void enable_paging(PageDirectory* address);

PageDirectory directories[1024] __attribute__((aligned(4096)));
PageEntry tables[1024 * 1024] __attribute__((aligned(4096)));

void enable() {
    for (uint32_t i = 0; i < 1024; i++) {
        PageDirectory& directory = directories[i];
        directory.UserSupervisor = 1;
        directory.ReadWrite = 1;
        directory.Present = 1;
        directory.Address = reinterpret_cast<uint32_t>(&tables[i * 1024]);
    }

    for (uint32_t i = 0; i < 1024 * 1024; i++) {
        PageEntry& entry = tables[i];
        entry.UserSupervisor = 1;
        entry.Present = 1;
        entry.Address = i * 4096;
    }

    enable_paging(&directories[0]);
}


Code:
global enable_paging
enable_paging:
    mov eax, ecx
    mov cr3, eax
   
    mov eax, cr0
    or eax, 0x80000001
    mov cr0, eax

    ret

Any ideas?

X86 kernel with C++ and NASM - no PAE enabled


Top
 Profile  
 
 Post subject: Re: Triple fault on page enable
PostPosted: Sat Jul 02, 2022 11:04 am 
Offline
Member
Member
User avatar

Joined: Sun Feb 18, 2007 7:28 pm
Posts: 1564
Hi,

"Address" here isn't right. That field is the Page Frame Number (PFN) so multiplying it by the Page Size does not make sense nor does trying to store a 32 bit address in a 20 bit field. It needs to be the PFN of the page tables and pages respectively. I also recommend just using the same structure for the directory and table...you'll simplify a lot of code later on.

For testing paging code, I always found Bochs to be helpful. A simple "info page" is all that is needed to check the page tables and current mapping.

For example, compare with our structure -
Code:
typedef struct _MMPTE_I386 {
   uint32_t valid: 1;
   uint32_t write: 1;
   uint32_t user: 1;
   uint32_t writeThrough: 1;
   uint32_t cacheDisable: 1;
   uint32_t accessed: 1;
   uint32_t dirty: 1;
   uint32_t largePage: 1;
   uint32_t global: 1;
/* following 3 bits we can use for any purpose. */
   uint32_t copyOnWrite: 1;
   uint32_t reserved: 1;
   uint32_t reserved2: 1;
   uint32_t pageFrameNumber: 20;
}MMPTE_I386;
The basic idea is that the physical address this maps is pageFrameNumber * PAGE_SIZE. Or, another way of looking at it, instead of "entry.Address = i * 4096" this would be "entry.pageFrameNumber = i".

_________________
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}


Top
 Profile  
 
 Post subject: Re: Triple fault on page enable
PostPosted: Sat Jul 02, 2022 11:43 am 
Offline
Member
Member

Joined: Tue Aug 31, 2021 7:25 am
Posts: 67
neon wrote:
Hi,

"Address" here isn't right. That field is the Page Frame Number (PFN) so multiplying it by the Page Size does not make sense nor does trying to store a 32 bit address in a 20 bit field. It needs to be the PFN of the page tables and pages respectively. I also recommend just using the same structure for the directory and table...you'll simplify a lot of code later on.

For testing paging code, I always found Bochs to be helpful. A simple "info page" is all that is needed to check the page tables and current mapping.

For example, compare with our structure -
Code:
typedef struct _MMPTE_I386 {
   uint32_t valid: 1;
   uint32_t write: 1;
   uint32_t user: 1;
   uint32_t writeThrough: 1;
   uint32_t cacheDisable: 1;
   uint32_t accessed: 1;
   uint32_t dirty: 1;
   uint32_t largePage: 1;
   uint32_t global: 1;
/* following 3 bits we can use for any purpose. */
   uint32_t copyOnWrite: 1;
   uint32_t reserved: 1;
   uint32_t reserved2: 1;
   uint32_t pageFrameNumber: 20;
}MMPTE_I386;
The basic idea is that the physical address this maps is pageFrameNumber * PAGE_SIZE. Or, another way of looking at it, instead of "entry.Address = i * 4096" this would be "entry.pageFrameNumber = i".

I know you can't store a 32 bit address in 20 bits but the compiler implicitly takes the biggest 20 bits which does the job.
And huh? I must've gotten confused by the documentation, it is my first time hearing about page frames.
Just to reiterate, what should the "address" / 20 bit field contain for the directory table and page table respectively?


Top
 Profile  
 
 Post subject: Re: Triple fault on page enable
PostPosted: Sat Jul 02, 2022 11:59 am 
Offline
Member
Member
User avatar

Joined: Sun Feb 18, 2007 7:28 pm
Posts: 1564
Hi,

This shouldnt be the first time you heard about page frames. Have you not written a page frame allocator ("physical memory manager")?

I.e. PFN of 1 refers to address 0x1000. A PFN of 2 refers to address 0x2000 and so on. Think of it as PFN = address / PAGE_SIZE. This is why those structures must be paged aligned. PFN's only become necessary here because you are using bit fields as the underlaying structure does not store addresses.

In other words... you are storing an address in a field not meant to hold an address.

If you want to think in terms of addresses rather then PFN's, do what most here do and don't use bit fields. Then you can mask off the bits you don't want (and your idea applies.) But if you want to stick with bit fields (its what I use as well) then learn to use page frame numbers.

_________________
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}


Top
 Profile  
 
 Post subject: Re: Triple fault on page enable
PostPosted: Sat Jul 02, 2022 12:15 pm 
Offline
Member
Member

Joined: Tue Aug 31, 2021 7:25 am
Posts: 67
neon wrote:
Hi,

This shouldnt be the first time you heard about page frames. Have you not written a page frame allocator ("physical memory manager")?

I.e. PFN of 1 refers to address 0x1000. A PFN of 2 refers to address 0x2000 and so on. Think of it as PFN = address / PAGE_SIZE. This is why those structures must be paged aligned. PFN's only become necessary here because you are using bit fields as the underlaying structure does not store addresses.

In other words... you are storing an address in a field not meant to hold an address.

If you want to think in terms of addresses rather then PFN's, do what most here do and don't use bit fields. Then you can mask off the bits you don't want (and your idea applies.) But if you want to stick with bit fields (its what I use as well) then learn to use page frame numbers.

Ah okay that makes sense why I need to align the structures.
Instead of referring to addresses as 32 bit memory locations I should refer to them in page frames?

Quote:
Have you not written a page frame allocator ("physical memory manager")?

I would have thought I would need to write one after I get paging working.


Top
 Profile  
 
 Post subject: Re: Triple fault on page enable
PostPosted: Sat Jul 02, 2022 12:31 pm 
Offline
Member
Member

Joined: Tue Aug 31, 2021 7:25 am
Posts: 67
That did work! Thank you for informing me. The wiki and some of the examples gave me the impression that I should refer to addresses in actual addresses instead of the number of pages.


Top
 Profile  
 
 Post subject: Re: Triple fault on page enable
PostPosted: Sat Jul 02, 2022 12:39 pm 
Offline
Member
Member
User avatar

Joined: Sun Feb 18, 2007 7:28 pm
Posts: 1564
Hi,

Another way of looking at it. Here is code from our Executive that sets up the initial Identity Space. Notice all we do is use the index? This refers to physical pages 0 - 1024 which is the first 4MB of the physical address space. This specific code was selected as it is the simplest example of how this is used. In more general cases, this "Index" comes from the Physical Frame allocator as it refers to a free frame.
Code:
PageTableEntry = (MMPTE*) MM_FROM_PFN(IdentitySpacePageTable);

NrlZeroMemory (PageTableEntry, MM_PAGE_SIZE);

for (Index = 0; Index < 1024; Index++) {
   PageTableEntry[Index].u.device.valid = 1;
   PageTableEntry[Index].u.device.pageFrameNumber = Index;
}

HalWritePdbr (PageDirectoryEntry);
Most tutorials and examples don't use this as a bit field but rather as an uint32_t which you can just set and mask off the bits you dont want. It is unfortunate, as looking at it in terms of page frames like above makes the code far simpler.

_________________
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot] and 64 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group