Page fault on accessing RSDT

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.
Post Reply
User avatar
Schol-R-LEA
Member
Member
Posts: 1925
Joined: Fri Oct 27, 2006 9:42 am
Location: Athens, GA, USA

Page fault on accessing RSDT

Post by Schol-R-LEA »

I am currently working on the ACPI code for my Ordo project. I am able to locate and identify the RSDP structure, and use that to find the RSDT (not the XSDT, as the SeaBIOS used in QEMU only supports ACPI 1.0). However, when I try to access the RSDT structure in order to validate it, I get a page fault.

I have instrumented the code for accessing both the RSDP and the RSDT to show each step, as well as to show the memory map retrieved from the BIOS. Based on this, I have concluded that the RSDT is indeed outside of the free memory, in a section of memory that I don't presently have page mapped (the page definitions in my boot loader can be seen here). This image shows the output with the QEMU memory set to 128M:
Image

I've also checked the same with -m 512M to comfirm that the same behavior applies regardless of the memory map:
Image

I assume that the solution is to add the firmware in question to the paged memory for the kernel, but I am concerned that I might be misreading the situation somehow. I am also not entirely clear how to alter the page mapping in the kernel, given that I am currently using the page map set up in the boot loader. Any advice on this would be appreciated.
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
User avatar
Schol-R-LEA
Member
Member
Posts: 1925
Joined: Fri Oct 27, 2006 9:42 am
Location: Athens, GA, USA

Re: Page fault on accessing RSDT

Post by Schol-R-LEA »

I forgot to post the relevant code:

Code: Select all

bool validate_sdt_checksum(struct ACPI_xSDT_Header *candidate)
{
    int32_t checksum = 0;
    uint8_t i;
    int8_t* c_ptr = (int8_t *) candidate;

    for (i = 0; i < sizeof(struct ACPI_xSDT_Header); i++)
    {
        checksum += c_ptr[i];
    }

    return (checksum == 0);
}


void init_acpi()
{
    kprintf("\nSeeking RSDP... ");

    rsdp = scan_rsdp_signature();
    kprintf("RSDP candidate found\n");

    kprintf("OEM ID: ");
    for (unsigned int i = 0; i < 6; i++)
    {
        kprintc(rsdp->ver_1.OEM_ID[i], BLACK, WHITE);
    }
    kprintc('\n', GRAY, BLACK);


    if (!validate_rsdp_checksum(rsdp))
    {
        kprints("Error: invalid RSDP", LT_RED, BLACK);
        panic();
    }


    // Get the system descriptor table.
    // While the specific link used depends on the ACPI version,
    // the table structure is the same regardless of version.

    kprintf("size of the SDT header ptr: %u\n", sizeof(struct ACPI_xSDT_Header *));

    sdt = (struct ACPI_xSDT_Header *) (rsdp->ver_1.Revision == 0
                                       ? rsdp->ver_1.RsdtAddress
                                       : (uint32_t) rsdp->XsdtAddress);

    kprintf("xSDT candidate found at 0x%p\n", sdt);

    for (uint8_t i = 0; i < 4; i++)
    {
        kprintc(sdt->Signature[i], WHITE, BLACK);
    }

    kprintf(" OEM ID: ");
    for (uint8_t i = 0; i < 6; i++)
    {
        kprintc(sdt->OEM_ID[i], BLACK, WHITE);
    }
    kprintc('\n', GRAY, BLACK);


    if (!validate_sdt_checksum(sdt))
    {
        kprints("Error: invalid SDT", LT_RED, BLACK);
        panic();
    }
    kprintf("SDT checksum valid.\n");
}
And:

Code: Select all

page_directory           equ 0x0040000
page_table_0             equ page_directory + 0x1000
page_table_768           equ page_table_0   + 0x1000

init_page_directory:
        mov ebx, dword page_directory
        memset32 0, 0x0400, ebx                ; clear the page dir table

        ; start by setting up the base page table
        mov ebx, dword page_table_0            ; get index into the base page table
        memset32 0, 0x0400, ebx                ; clear the table entries
        ; entries 0-1024 - identity mapping the first 1 MiB of memory
        mov ecx, 0x0100                        ; 256 entries * 4KiB = 1 MiB
        mov eax, 0
    .pt_0_fill:
        mov edx, eax
        or edx, PTE_Present
        mov [ebx], dword edx
        add eax, 0x1000
        add ebx, 4
        loop .pt_0_fill

        ; set up the kernel code table
        mov ebx, page_table_768                ; get index into the kernel code page table
        memset32 0, 0x0400, ebx                ; clear the table entries
        ; entries 0-4096 - mapping the start of higher half
        mov ecx, 0x1000                        ; 4096 entries * 4KiB = 4 MiB
        mov eax, 0x00100000                    ; the 1MiB entry point
    .pt_768_fill:
        mov edx, eax
        or edx, PTE_Present
        mov [ebx], dword edx
        add eax, 0x1000
        add ebx, 4
        loop .pt_768_fill


    .setup_directory:
        mov ebx, page_directory
    .pd_fill:
        mov eax, page_table_0
        or eax, PDE_Present
        mov [ebx], eax
        add ebx, 768 * 4
        mov eax, page_table_768
        or eax, PDE_Present
        mov [ebx], eax


        ; set the page directory
        mov eax, page_directory
        mov cr3, eax
        ret
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Octocontrabass
Member
Member
Posts: 5562
Joined: Mon Mar 25, 2013 7:01 pm

Re: Page fault on accessing RSDT

Post by Octocontrabass »

Schol-R-LEA wrote:I assume that the solution is to add the firmware in question to the paged memory for the kernel, but I am concerned that I might be misreading the situation somehow.
You're correct. Important data (like ACPI tables) will be placed outside "free" memory so you don't accidentally overwrite it while allocating memory.
Schol-R-LEA wrote:I am also not entirely clear how to alter the page mapping in the kernel, given that I am currently using the page map set up in the boot loader. Any advice on this would be appreciated.
This is one of those things where it's up to you, but you have two options: you can either create entirely new page tables or you can update the bootloader page tables.

Either way, you need to know the addresses of the memory containing your kernel (to avoid overwriting it and ensure it remains mapped correctly) and the memory containing the bootloader's page tables (same except you can overwrite it once you're done using it). If you've chosen to identity map all usable memory, you can read CR3 and walk the current page tables yourself. Otherwise, you'll need some other way for your kernel to get that information.

Keep in mind that your kernel's virtual address may conflict with identity mapping. It's pretty common for there to be MMIO near physical address 0xC0000000, and there are some (rare?) chipsets that can even place RAM at physical address 0xC0000000. If you want, this could be your excuse to stop relying on identity mapping.

Also, this is completely unrelated, but your inb8 and outb8 inline assembly has an invalid "i" constraint. You should either use the "N" constraint by itself or delete those functions and use only your inb16 and outb16 functions.
User avatar
Schol-R-LEA
Member
Member
Posts: 1925
Joined: Fri Oct 27, 2006 9:42 am
Location: Athens, GA, USA

Re: Page fault on accessing RSDT

Post by Schol-R-LEA »

Octocontrabass wrote:
Schol-R-LEA wrote:I am also not entirely clear how to alter the page mapping in the kernel, given that I am currently using the page map set up in the boot loader. Any advice on this would be appreciated.
This is one of those things where it's up to you, but you have two options: you can either create entirely new page tables or you can update the bootloader page tables.[...] If you want, this could be your excuse to stop relying on identity mapping.
I think it would make more sense at this stage to replace the existing page tables with ones that are in the kernel proper. I wasn't planning on tackling it quite this early, but as you said, this is a good point at which to start moving to a more flexible paging model.
Octocontrabass wrote:Also, this is completely unrelated, but your inb8 and outb8 inline assembly has an invalid "i" constraint. You should either use the "N" constraint by itself or delete those functions and use only your inb16 and outb16 functions.
sigh OK, I wasn't certain if the i was needed there or not; I was overthinking things again. I will fix that shortly. I am aware that the byte-wise functions are strictly speaking redundant, so I will probably just drop them entirely.
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Post Reply