Page 1 of 1

Finding IOAPIC Base Address with MADT (Causes Triple Fault)

Posted: Thu Mar 09, 2023 10:19 pm
by FunnyGuy9796
I have successfully accessed the RSDT and retrieved it's address. Now I am trying to pass that to the MADT in order to find the address of the IOAPIC. I had taken the function from the MADT article on the Wiki and modified it a bit so that it only retrieved the IOAPIC.

madt.c

Code: Select all

#include "madt.h"
 
uint64_t detect_ioapic(RSDT *rsdt) {
    uint8_t *ptr, *ptr2;
    uint32_t len;
    uint64_t ioapic_ptr = 0;
    for(len = *((uint32_t*)(rsdt + 4)), ptr2 = rsdt + 36; ptr2 < rsdt + rsdt->h.Length; ptr2 += rsdt->h.Signature[0]=='X' ? 8 : 4) {
        ptr = (uint8_t*)(uintptr_t)(rsdt->h.Signature[0]=='X' ? *((uint64_t*)ptr2) : *((uint32_t*)ptr2));
        if(!memcmp(ptr, "APIC", 4)) {
            ptr2 = ptr + *((uint32_t*)(ptr + 4));
            for(ptr += 44; ptr < ptr2; ptr += ptr[1]) {
                if (ptr[0] == 1) {
                    ioapic_ptr = (uint64_t)*((uint32_t*)(ptr+4));
                    return ioapic_ptr;
                }
            }
            break;
        }
    }
}
However, any of the code within the first if statement triggers a triple fault (even only having the 'break;'). I am a little confused as to why this is happening. I know it may be a stupid mistake or error I am missing but help would be much appreciated.

Re: Finding IOAPIC Base Address with MADT (Causes Triple Fau

Posted: Thu Mar 09, 2023 10:33 pm
by Octocontrabass
FunnyGuy9796 wrote:However, any of the code within the first if statement triggers a triple fault (even only having the 'break;').
It triggers some other fault, and your fault-handling (or lack thereof) triggers the triple fault. That "some other fault" will tell you what's going on.

Now would be a good time to set up exception handlers. They don't have to be too fancy - just dump the CPU registers to the screen or a serial port and halt.

If you don't want to do that right now, your virtual machine can give you that information. (For example, if you're using QEMU, add "-d int" to your command line.)

Re: Finding IOAPIC Base Address with MADT (Causes Triple Fau

Posted: Thu Mar 09, 2023 10:44 pm
by FunnyGuy9796
Alright, thanks! I will definitely do that. I kind of just skipped implementing an exception handler mainly because I was being lazy :/

Thank you for the advice!

Re: Finding IOAPIC Base Address with MADT (Causes Triple Fau

Posted: Fri Mar 10, 2023 3:17 am
by rdos
I think the IOAPIC is at a fixed address, so no reason to search ACPI tables for it. :-)

Re: Finding IOAPIC Base Address with MADT (Causes Triple Fau

Posted: Fri Mar 10, 2023 6:13 am
by iansjack
rdos wrote:I think the IOAPIC is at a fixed address, so no reason to search ACPI tables for it. :-)
Whilst that may be true in practice for current computers, I don't think it's a given. It's probably safer to future proof by specifically finding the address.

Re: Finding IOAPIC Base Address with MADT (Causes Triple Fau

Posted: Fri Mar 10, 2023 6:17 am
by iansjack
FunnyGuy9796 wrote:I kind of just skipped implementing an exception handler mainly because I was being lazy :/
I'm afraid that's false laziness. It is so easy to implement trivial exception handlers (e.g. just "jmp ." which then allows you to investigate the machine state in your debugger), and the consequences of not doing so can be so baffling, that it should be the first thing that you implement.

Re: Finding IOAPIC Base Address with MADT (Causes Triple Fau

Posted: Fri Mar 10, 2023 9:03 am
by rdos
iansjack wrote:
rdos wrote:I think the IOAPIC is at a fixed address, so no reason to search ACPI tables for it. :-)
Whilst that may be true in practice for current computers, I don't think it's a given. It's probably safer to future proof by specifically finding the address.
I think the chance of a buggy BIOS providing the wrong address actually is higher than the chance this will ever be changed. It's part of Intel's & AMD's chipsets, and not the motherboard build.

Re: Finding IOAPIC Base Address with MADT (Causes Triple Fau

Posted: Fri Mar 10, 2023 10:08 am
by FunnyGuy9796
Ok, after some debugging I found that the reason the triple fault occurred was because the MADT could not be found. I am unsure as to why that is the case but it is at least a step in the right direction. Does anyone have any idea why the MADT could not be found for the APIC?

Here's the current code:

Code: Select all

uint64_t detect_ioapic(uint8_t *rsdt) {
    if (rsdt == NULL) {
        terminal_write("RSDT not found\n");
    }
    uint8_t *ptr, *ptr2;
    uint32_t len;
    for(len = *((uint32_t*)(rsdt + 4)), ptr2 = rsdt + 36; ptr2 < rsdt + len; ptr2 += rsdt[0]=='X' ? 8 : 4) {
        ptr = (uint8_t*)(uintptr_t)(rsdt[0]=='X' ? *((uint64_t*)ptr2) : *((uint32_t*)ptr2));
        if(!memcmp(ptr, "APIC", 4)) {
            uint32_t *ptr32 = (uint32_t*)((uint8_t*)ptr + 0x24);
            uint32_t val32 = *ptr32;
            uint64_t lapic_ptr = (uint64_t)val32;
            ptr2 = ptr + *((uint32_t*)(ptr + 4));
            for(ptr += 44; ptr < ptr2; ptr += ptr[1]) {
                if (ptr[0] == 1) {
                    uint64_t ioapic_ptr = (uint64_t)*((uint32_t*)(ptr+4));
                    return ioapic_ptr;
                }
            }
            break;
        } else {
            terminal_write("Could not locate IOAPIC\n");
            return;
        }
    }
}
It is able to retrieve the RSDT but gets trapped in an infinite loop looking for the MADT.

Re: Finding IOAPIC Base Address with MADT (Causes Triple Fau

Posted: Fri Mar 10, 2023 10:25 am
by nullplan
You know, that doesn't work unless the MADT is the first table found. If the memcmp() returns nonzero, you will immediately return from the function. Without a value, so the caller will have no defined value telling them the MADT was not found. Also, if you do manage to find the MADT there, you overwrite "ptr2", so now the outer loop will no longer work. Might I suggest adding a few structures to make that mess more readable?

Re: Finding IOAPIC Base Address with MADT (Causes Triple Fau

Posted: Fri Mar 10, 2023 11:34 am
by FunnyGuy9796
Thank you for the advice. I added the return instruction because without it the loop is infinite and therefore triggers a fault. I am really only using this function to locate the IOAPIC so exiting once it is found isn't really an issue in this situation. I did fix the rewriting of the variable. However, I am still stuck trying to figure out how to find the IOAPIC because, as I said, the loop seems to trigger a fault which makes me think I am searching for the MADT incorrectly or something along those lines.

Re: Finding IOAPIC Base Address with MADT (Causes Triple Fau

Posted: Fri Mar 10, 2023 11:56 am
by iansjack
rdos wrote:
iansjack wrote:
rdos wrote:I think the IOAPIC is at a fixed address, so no reason to search ACPI tables for it. :-)
Whilst that may be true in practice for current computers, I don't think it's a given. It's probably safer to future proof by specifically finding the address.
I think the chance of a buggy BIOS providing the wrong address actually is higher than the chance this will ever be changed. It's part of Intel's & AMD's chipsets, and not the motherboard build.
If you don’t search, how do know there is only one?

Re: Finding IOAPIC Base Address with MADT (Causes Triple Fau

Posted: Fri Mar 10, 2023 3:18 pm
by FunnyGuy9796
Update: I found the address it is usually at and set that as a constant and now everything is working fine.

ioapic.c

Code: Select all

#include "ioapic.h"

static inline uint32_t ioapic_read(uint32_t reg) {
    volatile uint32_t* ioapic = (uint32_t*)IOAPIC_BASE;
    *ioapic = reg;
    return *(ioapic + 4);
}

static inline void ioapic_write(uint32_t reg, uint32_t val) {
    volatile uint32_t* ioapic = (uint32_t*)IOAPIC_BASE;
    *ioapic = reg;
    *(ioapic + 4) = val;
}

void ioapic_init() {
    uint32_t id = ioapic_read(IOAPIC_REG_ID);
    uint32_t ver = ioapic_read(IOAPIC_REG_VER);
    for (int i = 0; i < 24; i++) {
        ioapic_write(IOAPIC_REG_REDTBL + i * 2, 0x10000);
        ioapic_write(IOAPIC_REG_REDTBL + i * 2 + 1, 0);
    }
    terminal_write("IOAPIC: Initialized\n");
}
ioapic.h

Code: Select all

#ifndef IOAPIC_H_INCLUDED
#define IOAPIC_H_INCLUDED

#define IOAPICID          0x00
#define IOAPICVER         0x01
#define IOAPICARB         0x02
#define IOAPICREDTBL(n)   (0x10 + 2 * n)

#include <stdint.h>
#include "../terminal.h"

#define IOAPIC_BASE 0xFEC00000

#define IOAPIC_REG_ID     0x00
#define IOAPIC_REG_VER    0x01
#define IOAPIC_REG_ARB    0x02
#define IOAPIC_REG_REDTBL 0x10

typedef struct {
    uint32_t low;
    uint32_t high;
} ioapic_redtbl_entry_t;

void ioapic_init();

#endif
I know it's not the cleanest implementation and probably not the most efficient but at least it works.