Page 1 of 1

Kernel triple faults when I dont map the first 261 pages?

Posted: Mon Oct 30, 2023 7:43 am
by tommasopeduzzi
Hi!

I am adding paging to my small kernel, but I am encountering an issue that causes my kernel to triple fault and crash. If I don't identity-map the first 261 blocks (I have found this number through trial and error), the kernel crashes immediately after enabling paging. I have my paging code attached below.

Code: Select all

void PageManager::initialize()
{
    PageManager* page_manager = PageManager::get();
    // Assume we have 4GB of RAM (2^32 bytes), so we need 2^32 / 4096 = 2^21
    uint32_t block_count = 0x100000;
    // Divide by 8 because each byte represents 8 blocks
    page_manager->blocks = (uint8_t *)kmalloc(block_count / 8);
    memset(page_manager->blocks, 0, block_count / 8);

    // This is guaranteed to be physical memory, because we haven't enabled paging and initialized the heap yet.
    page_manager->m_page_directory = (Entry*)kmalloc(PAGE_DIRECTORY_SIZE*sizeof(Entry), true);
    for (int i = 0; i < 1024; i++)
    {
        page_manager->m_page_directory[i] = Entry{
            .present = 0,
            .read_write = 1,
            .user_supervisor = 0,
            .write_through = 0,
            .cache_disabled = 0,
            .accessed = 0,
            .reserved = 0,
            .page_size = 0,
            .global = 0,
            .available = 0,
            .page_table_base_address = 0
        };
    }
    // Map the first 768 blocks to themselves. This is up to the start of the kernel heap.
    // In this first section of section of memory we allocate stuff before we initialize the heap
    // and we have the page VGA text buffer. 
    Entry page_directory_entry = page_manager->create_new_page_table(0);
    Entry *page_table = (Entry *)(page_directory_entry.page_table_base_address << 12);
    uint32_t current = 0;
    while(current <= 0x1000 * 261)
    {
        page_table[current>>12] = Entry{
            .present = 1,
            .read_write = 1,
            .user_supervisor = 1,
            .write_through = 0,
            .cache_disabled = 0,
            .accessed = 0,
            .reserved = 0,
            .page_size = 0,
            .global = 0,
            .available = 0,
            .page_table_base_address = current >> 12
        };
        current += 0x1000;
    }
    page_manager->enable_paging();
    TTY::get()->write("Enabled Paging!");
}

void PageManager::enable_paging()
{
    // Set location of page directory
    asm volatile("movl %0, %%cr3" ::"r"((uint32_t)m_page_directory));
    uint32_t cr0;
    asm volatile("movl %%cr0, %0"
                 : "=r"(cr0));
    cr0 |= 0x80000001; // enable paging, by setting bit 31 and bit 1
    asm volatile("movl %0, %%cr0" ::"r"(cr0));
}

PageManager::Entry PageManager::create_new_page_table(uint32_t index)
{
    // FIXME: Get the physical address of the allocated page table, even when heap
    // is initialized and paging is enabled.
    Entry *page_table_adress = (Entry *)kmalloc(PAGE_TABLE_SIZE * sizeof(Entry), true);
    for (int i = 0; i < 1024; i++)
    {
        page_table_adress[i] = Entry{
            .present = 0,
            .read_write = 1,
            .user_supervisor = 0,
            .write_through = 0,
            .cache_disabled = 0,
            .accessed = 0,
            .reserved = 0,
            .page_size = 0,
            .global = 0,
            .available = 0,
            .page_table_base_address = 0
        };
    }
    Entry page_table = Entry{
        .present = 1,
        .read_write = 1,
        .user_supervisor = 0,
        .write_through = 0,
        .cache_disabled = 0,
        .accessed = 0,
        .reserved = 0,
        .page_size = 0,
        .global = 0,
        .available = 0,
        .page_table_base_address = (uint32_t)page_table_adress >> 12
    };
    m_page_directory[index] = page_table;
    return page_table;
}
Notice the 261, which is the minimum number of pages on which the kernel runs. This is the output I get from QEMU:

Code: Select all

     6: v=08 e=0000 i=0 cpl=0 IP=0008:001018ef pc=001018ef SP=0010:00105f18 env->regs[R_EAX]=80000011
EAX=80000011 EBX=00000010 ECX=00000000 EDX=00000000
ESI=00000000 EDI=00001000 EBP=00105f28 ESP=00105f18
EIP=001018ef EFL=00000286 [--S--P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 000fffff 006f9300 DPL=0 DS   [-WA]
CS =0008 00000000 000fffff 006f9a00 DPL=0 CS64 [-R-]
SS =0010 00000000 000fffff 006f9300 DPL=0 DS   [-WA]
DS =0010 00000000 000fffff 006f9300 DPL=0 DS   [-WA]
FS =0010 00000000 000fffff 006f9300 DPL=0 DS   [-WA]
GS =0010 00000000 000fffff 006f9300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     00000004 00000031
IDT=     0000004a 000007ff
CR0=80000011 CR2=00105f14 CR3=00021000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 
DR6=ffff0ff0 DR7=00000400
CCS=00000010 CCD=80000011 CCO=LOGICL
EFER=0000000000000000
check_exception old: 0x8 new 0xe
It crashes at the leave instruction of the enable_paging method.

Thank you in advance for the help! Let me know if you need more information, I am just really confused why this might be happening, as the encoding seems good.

Re: Kernel triple faults when I dont map the first 261 pages

Posted: Mon Oct 30, 2023 8:21 am
by iansjack
Hint: 261 decimal = 105 hexadecimal.

Look at ESP.

Re: Kernel triple faults when I dont map the first 261 pages

Posted: Mon Oct 30, 2023 9:28 am
by tommasopeduzzi
Ah, so I need to make sure that the stack is identity mapped, such that is preserved?
That seems clunky, there's surely a better solution, right? Also how do I know where the stack starts?

Re: Kernel triple faults when I dont map the first 261 pages

Posted: Mon Oct 30, 2023 10:22 am
by iansjack
You need to ensure that any memory you access is mapped (including memory-mapped devices). The stack doesn’t have to be identity mapped, but it must be mapped. If you map the memory to a different location you will need to adjust esp (and/or copy the stack to its new location). It’s not a good idea to do this within a subroutine so identity mapping (at this stage) is the best idea.

I doubt that you need to map all 261 pages (and it’s a good idea not to map page 0) but you must produce mappings for all code and data locations (including stacks) that your kernel uses.

You know where the stack starts because one of the first things your OS does is to set up the stack.

Re: Kernel triple faults when I dont map the first 261 pages

Posted: Mon Oct 30, 2023 10:55 am
by Octocontrabass
tommasopeduzzi wrote:Ah, so I need to make sure that the stack is identity mapped, such that is preserved?
Pretty much, yeah. If you really wanted to, you could move the stack to a different physical address as long as it stays at the same virtual address.
tommasopeduzzi wrote:That seems clunky, there's surely a better solution, right?
The better solution is to enable paging during a time when you don't need a stack. Usually this is in your bootloader somewhere, in the beginning of your kernel before you start calling C++ code, or in a stub kernel that sets up mappings for the real kernel.
tommasopeduzzi wrote:Also how do I know where the stack starts?
If you set up your own stack, the stack is where you put it. If you're using the stack set up by the bootloader, it's wherever your bootloader put it (if your bootloader promises to set up a valid stack; GRUB will not set up a stack for you).

Also, a few things caught my eye in your first post...

Code: Select all

     6: v=08
That's a double fault. You probably want to look at the previous exception to see what's wrong.

Code: Select all

ES =0010 00000000 000fffff 006f9300 DPL=0 DS   [-WA]
CS =0008 00000000 000fffff 006f9a00 DPL=0 CS64 [-R-]
Your segment limits are too low. It looks like you miscounted and set the wrong bits in the flags byte (0x6F when it should be 0xCF). It only works for you because QEMU ignores segment limits.

Re: Kernel triple faults when I dont map the first 261 pages

Posted: Wed Nov 01, 2023 2:03 am
by tommasopeduzzi
Thank you both for the helpful replies. I completely forgot about having set up the stack, so fixing that issue was easy.

Re: Kernel triple faults when I dont map the first 261 pages

Posted: Wed Nov 01, 2023 2:15 am
by tommasopeduzzi
Octocontrabass wrote: That's a double fault. You probably want to look at the previous exception to see what's wrong.
Thanks for the hint. I will debug this after I fix the previously mentioned issue.
Octocontrabass wrote: Your segment limits are too low. It looks like you miscounted and set the wrong bits in the flags byte (0x6F when it should be 0xCF). It only works for you because QEMU ignores segment limits.
Oh, I thought I checked that. Seems like I need to fix that too! Thanks!