Parse ACPI MADT table
This commit is contained in:
@ -58,7 +58,7 @@ int liballoc_free(void *ptr1, int pages) {
|
||||
sl_lock(&pd->sl);
|
||||
|
||||
for (uptr_t page = ptr; page < ptr + PAGE_SIZE * pages; page += PAGE_SIZE) {
|
||||
uptr_t phys = mm_translate(pd, page, 0);
|
||||
uptr_t phys = mm_translate_v2p(pd, page, 0);
|
||||
mm_unmap_page(pd, page, 0);
|
||||
pmm_free(phys);
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ void mregmap_init(void) {
|
||||
}
|
||||
|
||||
bool_t mregmap_add_region(struct mreg *mreg) {
|
||||
bool_t ret = true;
|
||||
bool_t ret = 1;
|
||||
|
||||
for (usize_t i = 0; i < MREGMAP_MAX; i++) {
|
||||
if (!bitmap_test(&mregmap.bm, i)) {
|
||||
@ -52,7 +52,7 @@ bool_t mregmap_add_region(struct mreg *mreg) {
|
||||
}
|
||||
}
|
||||
|
||||
ret = false;
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
return ret;
|
||||
|
||||
1
kernel/platform/i386_pc/acpi/.gitignore
vendored
Normal file
1
kernel/platform/i386_pc/acpi/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.o
|
||||
231
kernel/platform/i386_pc/acpi/acpi.c
Normal file
231
kernel/platform/i386_pc/acpi/acpi.c
Normal file
@ -0,0 +1,231 @@
|
||||
#include <libk/types.h>
|
||||
#include <libk/compiler.h>
|
||||
#include <libk/bitmap.h>
|
||||
#include <libk/util.h>
|
||||
#include <libk/string.h>
|
||||
#include <sys/halt.h>
|
||||
#include <sys/mm.h>
|
||||
#include <acpi/acpi.h>
|
||||
#include <mm/mregmap.h>
|
||||
#include <sync/spinlock.h>
|
||||
#include <config.h>
|
||||
|
||||
#define ACPI_RSDP_SCAN_START 0x000E0000
|
||||
#define ACPI_RSDP_SCAN_END 0x000FFFFF
|
||||
|
||||
#define ACPI_PARSER_MATCHES_MAX 32
|
||||
|
||||
typedef bool_t (*acpi_struct_parser_func_t)(struct acpi_header *h);
|
||||
|
||||
struct acpi_parser_match {
|
||||
char string[4];
|
||||
acpi_struct_parser_func_t func;
|
||||
};
|
||||
|
||||
static uptr_t acpi_rsdp;
|
||||
static uptr_t acpi_phys_base;
|
||||
|
||||
/* forward declarations */
|
||||
static bool_t acpi_parse_madt(struct acpi_header *h);
|
||||
|
||||
static struct acpi_parser_match acpi_parser_matches[ACPI_PARSER_MATCHES_MAX] = {
|
||||
{ .string = "APIC", .func = &acpi_parse_madt },
|
||||
};
|
||||
|
||||
static bool_t acpi_calc_checksum(struct acpi_header *h) {
|
||||
uint8_t sum = 0;
|
||||
uint32_t length = h->length;
|
||||
|
||||
for (uint32_t i = 0; i < length; i++)
|
||||
sum += ((uint8_t *)h)[i];
|
||||
|
||||
return (sum == 0);
|
||||
}
|
||||
|
||||
static void acpi_map_memory(uptr_t ptr, usize_t size) {
|
||||
uptr_t aligned_ptr = ALIGN_DOWN(ptr, PAGE_SIZE);
|
||||
usize_t aligned_size = ALIGN_UP(size, PAGE_SIZE);
|
||||
|
||||
struct page_dir *pd = mm_get_kernel_pd();
|
||||
|
||||
sl_lock(&pd->sl);
|
||||
|
||||
for (uptr_t page = ACPI_MAP_START, paddr = aligned_ptr;
|
||||
page < ACPI_MAP_START + aligned_size;
|
||||
page += PAGE_SIZE, paddr += PAGE_SIZE) {
|
||||
mm_map_page(pd, page, paddr, PF_PRESENT);
|
||||
}
|
||||
|
||||
sl_unlock(&pd->sl);
|
||||
}
|
||||
|
||||
static bool_t acpi_find_rsdp(void) {
|
||||
bool_t ret = 0;
|
||||
|
||||
uptr_t ptr = ACPI_RSDP_SCAN_START;
|
||||
uptr_t end = ACPI_RSDP_SCAN_END;
|
||||
|
||||
while (ptr < end) {
|
||||
if (memcmp((void *)(ptr + VIRT_BASE), "RSD PTR ", 8) == 0) {
|
||||
acpi_rsdp = ptr + VIRT_BASE;
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
ptr += 16;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool_t acpi_parse_rsdt(struct acpi_rsdt *rsdt) {
|
||||
bool_t ret = 0;
|
||||
|
||||
dbgf("rsdt=%p\n", rsdt);
|
||||
usize_t entries = (rsdt->h.length - sizeof(rsdt->h)) / 4;
|
||||
dbgf("ACPI rsdt entries=%zu\n", entries);
|
||||
|
||||
struct page_dir *pd = mm_get_kernel_pd();
|
||||
|
||||
sl_lock(&pd->sl);
|
||||
|
||||
for (usize_t i = 0; i < entries; i++) {
|
||||
struct acpi_header *h = (struct acpi_header *)mm_translate_p2v(pd, rsdt->ptrs[i], 0);
|
||||
|
||||
if (h == NULL)
|
||||
continue;
|
||||
|
||||
bool_t checksum_ok = acpi_calc_checksum(h);
|
||||
if (!checksum_ok)
|
||||
continue;
|
||||
|
||||
for (usize_t j = 0; j < LEN(acpi_parser_matches); j++) {
|
||||
struct acpi_parser_match *match = &acpi_parser_matches[j];
|
||||
|
||||
if (memcmp(h->signature, match->string, 4) == 0) {
|
||||
dbgf("ACPI parsing %.*s (%p)\n", 4, match->string, h);
|
||||
ret = match->func(h);
|
||||
dbgf("ACPI %.*s %s\n", 4, match->string, ret ? "OK" : "FAIL");
|
||||
if (!ret) {
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
sl_unlock(&pd->sl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool_t acpi_parse_rsdp(void) {
|
||||
bool_t ret = 1;
|
||||
|
||||
uint8_t checksum = 0;
|
||||
for (usize_t i = 0; i < 20; i++) {
|
||||
checksum += ((uint8_t *)acpi_rsdp)[i];
|
||||
}
|
||||
|
||||
if (checksum) {
|
||||
dbgf("ACPI rsdp checksum failed: %u\n", (uint32_t)checksum);
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
dbgf("ACPI rsdp checksum OK\n");
|
||||
|
||||
struct acpi_rsdp *rsdp = (struct acpi_rsdp *)acpi_rsdp;
|
||||
dbgf("ACPI rsdp OEM=%.*s\n", 6, rsdp->oem);
|
||||
|
||||
if (!(rsdp->revision == 0 || rsdp->revision == 2)) {
|
||||
dbgf("ACPI version %u is not supported\n", rsdp->revision);
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* we've mapped the rsdt contents into virtual memory, so we're good to go */
|
||||
struct acpi_rsdt *rsdt = (struct acpi_rsdt *)ACPI_MAP_START;
|
||||
ret = acpi_parse_rsdt(rsdt);
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool_t acpi_parse_madt(struct acpi_header *h) {
|
||||
struct acpi_madt *madt = (struct acpi_madt *)h;
|
||||
dbgf("madt=%p\n", madt);
|
||||
|
||||
uptr_t ptr = (uptr_t)madt + 0x2C;
|
||||
uptr_t end = (uptr_t)madt + madt->h.length;
|
||||
|
||||
while (ptr < end) {
|
||||
struct acpi_apic_header *header = (struct acpi_apic_header *)ptr;
|
||||
|
||||
if (header->length < sizeof(struct acpi_apic_header))
|
||||
break;
|
||||
|
||||
switch (header->type) {
|
||||
case ACPI_LAPIC: {
|
||||
struct acpi_lapic *lapic = (struct acpi_lapic *)ptr;
|
||||
dbgf("LAPIC: cpu=%u, apic id=%u, flags=%08x\n",
|
||||
lapic->acpi_cpu_id, lapic->apic_id, lapic->flags);
|
||||
} break;
|
||||
case ACPI_IOAPIC: {
|
||||
struct acpi_ioapic *ioapic = (struct acpi_ioapic *)ptr;
|
||||
dbgf("IOAPIC: id=%u, addr=%08x, GSI base=%u\n",
|
||||
ioapic->ioapic_id, ioapic->ioapic_addr, ioapic->global_intr_base);
|
||||
} break;
|
||||
case ACPI_INTR_OVERRIDE: {
|
||||
struct acpi_intr_override *override = (struct acpi_intr_override *)ptr;
|
||||
dbgf("INTR OVERRIDE: bus=%u, source=%u, intr=%u, flags=%08x\n",
|
||||
override->bus, override->source, override->interrupt, override->flags);
|
||||
} break;
|
||||
default:
|
||||
dbgf("unknown APIC structure\n");
|
||||
break;
|
||||
}
|
||||
|
||||
ptr += header->length;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void acpi_init(void) {
|
||||
struct mregmap *mregmap = mregmap_get();
|
||||
|
||||
struct mreg *mreg = NULL;
|
||||
|
||||
for (usize_t i = 0; i < MREGMAP_MAX; i++) {
|
||||
if (!bitmap_test(&mregmap->bm, i))
|
||||
continue;
|
||||
|
||||
mreg = &mregmap->mregs[i];
|
||||
|
||||
if (mreg->type == MREG_ACPI) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mreg == NULL) {
|
||||
/* We can't go further */
|
||||
dbgf("Could not find ACPI memory region!\n");
|
||||
halt();
|
||||
}
|
||||
|
||||
dbgf("ACPI memory found at %p (%x)\n", mreg->start, mreg->size);
|
||||
|
||||
acpi_phys_base = mreg->start;
|
||||
acpi_map_memory(mreg->start, mreg->size);
|
||||
|
||||
if (!acpi_find_rsdp()) {
|
||||
dbgf("ACPI rsdp not found\n");
|
||||
halt();
|
||||
}
|
||||
|
||||
dbgf("ACPI rsdp found at %p\n", acpi_rsdp - VIRT_BASE);
|
||||
|
||||
if (!acpi_parse_rsdp()) {
|
||||
halt();
|
||||
}
|
||||
}
|
||||
173
kernel/platform/i386_pc/acpi/acpi.h
Normal file
173
kernel/platform/i386_pc/acpi/acpi.h
Normal file
@ -0,0 +1,173 @@
|
||||
#ifndef _ACPI_ACPI_H
|
||||
#define _ACPI_ACPI_H
|
||||
|
||||
#include <libk/types.h>
|
||||
#include <libk/compiler.h>
|
||||
|
||||
struct acpi_header {
|
||||
byte_t signature[4];
|
||||
uint32_t length;
|
||||
uint8_t revision;
|
||||
uint8_t checksum;
|
||||
byte_t oem[6];
|
||||
byte_t oemtable[8];
|
||||
uint32_t oem_revision;
|
||||
uint32_t creator;
|
||||
uint32_t creator_revision;
|
||||
} packed;
|
||||
|
||||
struct acpi_rsdp {
|
||||
byte_t signature[8];
|
||||
uint8_t checksum;
|
||||
byte_t oem[6];
|
||||
uint8_t revision;
|
||||
uint32_t rsdt_addr;
|
||||
} packed;
|
||||
|
||||
struct acpi_rsdt {
|
||||
struct acpi_header h;
|
||||
uint32_t ptrs[];
|
||||
};
|
||||
|
||||
/* Address space */
|
||||
enum {
|
||||
ACPI_GAS_SYSMEMORY = 0,
|
||||
ACPI_GAS_SYSIO = 1,
|
||||
ACPI_GAS_PCI_CONFIG = 2,
|
||||
ACPI_GAS_MBED_CTL = 3,
|
||||
ACPI_GAS_SYSMGMT_BUS = 4,
|
||||
ACPI_GAS_CMOS = 5,
|
||||
ACPI_GAS_PCI_BAR_TGT = 6,
|
||||
ACPI_GAS_IPMI = 7,
|
||||
ACPI_GAS_GPIO = 8,
|
||||
ACPI_GAS_SERIAL_BUS = 9,
|
||||
ACPI_GAS_PLAT_COM_CHL = 10,
|
||||
};
|
||||
|
||||
/* Access size */
|
||||
enum {
|
||||
ACPI_GAS_UNDEFINED = 0,
|
||||
ACPI_GAS_BYTE = 1,
|
||||
ACPI_GAS_WORD = 2,
|
||||
ACPI_GAS_DWORD = 3,
|
||||
ACPI_GAS_QWORD = 4,
|
||||
};
|
||||
|
||||
struct acpi_gas {
|
||||
uint8_t addr_space;
|
||||
uint8_t bit_width;
|
||||
uint8_t bit_offset;
|
||||
uint8_t access_size;
|
||||
uint64_t addr;
|
||||
} packed;
|
||||
|
||||
struct acpi_fadt {
|
||||
struct acpi_header h;
|
||||
|
||||
uint32_t firmware_ctl;
|
||||
uint32_t dsdt;
|
||||
|
||||
uint8_t reserved;
|
||||
|
||||
uint8_t preferred_power_mgmt_profile;
|
||||
uint16_t sci_interrupt;
|
||||
uint32_t smi_cmd_port;
|
||||
uint8_t acpi_enable;
|
||||
uint8_t acpi_disable;
|
||||
uint8_t s4bios_req;
|
||||
uint8_t pstate_ctl;
|
||||
uint32_t pm1a_evt_blk;
|
||||
uint32_t pm1b_evt_blk;
|
||||
uint32_t pm1a_ctl_blk;
|
||||
uint32_t pm1b_ctl_blk;
|
||||
uint32_t pm2_ctl_blk;
|
||||
uint32_t pm_timer_blk;
|
||||
uint32_t gpe0_blk;
|
||||
uint32_t gpe1_blk;
|
||||
uint8_t pm1_evt_length;
|
||||
uint8_t pm1_ctl_length;
|
||||
uint8_t pm2_ctl_length;
|
||||
uint8_t pm_timer_length;
|
||||
uint8_t gpe0_length;
|
||||
uint8_t gpe1_length;
|
||||
uint8_t gpe1_base;
|
||||
uint8_t cstate_ctl;
|
||||
uint16_t worst_c2_latency;
|
||||
uint16_t worst_c3_latency;
|
||||
uint16_t flush_size;
|
||||
uint16_t flush_stride;
|
||||
uint8_t duty_offset;
|
||||
uint8_t duty_width;
|
||||
uint8_t day_alarm;
|
||||
uint8_t month_alarm;
|
||||
uint8_t century;
|
||||
|
||||
uint16_t boot_arch_flags;
|
||||
|
||||
uint8_t reserved2;
|
||||
uint32_t flags;
|
||||
|
||||
struct acpi_gas reset_reg;
|
||||
|
||||
uint8_t reset_value;
|
||||
uint8_t reserved3[3];
|
||||
|
||||
uint64_t x_firmware_ctl;
|
||||
uint64_t x_dsdt;
|
||||
|
||||
struct acpi_gas x_pm1a_evt_blk;
|
||||
struct acpi_gas x_pm1b_evt_blk;
|
||||
struct acpi_gas x_pm1a_ctl_blk;
|
||||
struct acpi_gas x_pm1b_ctl_blk;
|
||||
struct acpi_gas x_pm2_ctl_blk;
|
||||
struct acpi_gas x_pm_timer_blk;
|
||||
struct acpi_gas x_gpe0_blk;
|
||||
struct acpi_gas x_gpe1_blk;
|
||||
} packed;
|
||||
|
||||
struct acpi_madt {
|
||||
struct acpi_header h;
|
||||
|
||||
uint32_t lapic_addr;
|
||||
uint32_t flags;
|
||||
} packed;
|
||||
|
||||
enum {
|
||||
ACPI_LAPIC = 0,
|
||||
ACPI_IOAPIC = 1,
|
||||
ACPI_INTR_OVERRIDE = 2,
|
||||
};
|
||||
|
||||
struct acpi_apic_header {
|
||||
uint8_t type;
|
||||
uint8_t length;
|
||||
} packed;
|
||||
|
||||
struct acpi_lapic {
|
||||
struct acpi_apic_header h;
|
||||
uint8_t acpi_cpu_id;
|
||||
uint8_t apic_id;
|
||||
uint32_t flags;
|
||||
} packed;
|
||||
|
||||
struct acpi_ioapic {
|
||||
struct acpi_apic_header h;
|
||||
uint8_t ioapic_id;
|
||||
uint8_t reserved;
|
||||
uint32_t ioapic_addr;
|
||||
uint32_t global_intr_base;
|
||||
};
|
||||
|
||||
struct acpi_intr_override {
|
||||
struct acpi_apic_header h;
|
||||
uint8_t bus;
|
||||
uint8_t source;
|
||||
uint32_t interrupt;
|
||||
uint16_t flags;
|
||||
};
|
||||
|
||||
#define ACPI_MAP_START 0xC5000000
|
||||
|
||||
void acpi_init(void);
|
||||
|
||||
#endif // _ACPI_ACPI_H
|
||||
5
kernel/platform/i386_pc/acpi/make.src
Normal file
5
kernel/platform/i386_pc/acpi/make.src
Normal file
@ -0,0 +1,5 @@
|
||||
dir_acpi := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
|
||||
|
||||
c_files += $(dir_acpi)/acpi.c
|
||||
|
||||
o_files += $(dir_acpi)/acpi.o
|
||||
@ -39,6 +39,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <sys/pic.h>
|
||||
#include <sys/pit.h>
|
||||
#include <sys/isr.h>
|
||||
#include <acpi/acpi.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <mm/bba.h>
|
||||
#include <mm/liballoc.h>
|
||||
@ -136,16 +137,18 @@ void bootmain(void *mbootptr) {
|
||||
|
||||
#if CFG_I386_PC_DEBUG == CFG_I386_PC_DEBUG_SERIAL
|
||||
serialdbg_init();
|
||||
dbgf("dbgf() test...\n");
|
||||
#endif
|
||||
dbgf("dbgf() test...\n");
|
||||
dbgf("-------------------------------------------------------------\n");
|
||||
|
||||
dump_multiboot_header();
|
||||
mregmap_init1();
|
||||
pmm_init1();
|
||||
bba_init();
|
||||
mm_init();
|
||||
ramdisk_init();
|
||||
pit_init();
|
||||
acpi_init();
|
||||
/* ramdisk_init(); */
|
||||
/* pit_init(); */
|
||||
|
||||
__asm__ volatile("sti");
|
||||
|
||||
|
||||
@ -6,3 +6,4 @@ kernel_cflags += -isystem $(dir_i386_pc)
|
||||
include $(dir_i386_pc)/boot/make.src
|
||||
include $(dir_i386_pc)/libk/make.src
|
||||
include $(dir_i386_pc)/sys/make.src
|
||||
include $(dir_i386_pc)/acpi/make.src
|
||||
|
||||
@ -40,6 +40,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#define PD_INDEX(va) (((va) >> 22) & 0x3FF)
|
||||
#define PT_INDEX(va) (((va) >> 12) & 0x3FF)
|
||||
#define PD_SHIFT 22
|
||||
#define PT_SHIFT 12
|
||||
|
||||
extern char __init_pd;
|
||||
|
||||
@ -137,7 +139,7 @@ done:
|
||||
sl_unlock(&pd->sl);
|
||||
}
|
||||
|
||||
uptr_t mm_translate(struct page_dir *pd, uptr_t vaddr, uint32_t flags) {
|
||||
uptr_t mm_translate_v2p(struct page_dir *pd, uptr_t vaddr, uint32_t flags) {
|
||||
if (flags & PF_LOCK)
|
||||
sl_lock(&pd->sl);
|
||||
|
||||
@ -162,6 +164,42 @@ done:
|
||||
return paddr;
|
||||
}
|
||||
|
||||
uptr_t mm_translate_p2v(struct page_dir *pd, uptr_t paddr, uint32_t flags) {
|
||||
uptr_t vaddr;
|
||||
bool_t found = 0;
|
||||
|
||||
if (flags & PF_LOCK)
|
||||
sl_lock(&pd->sl);
|
||||
|
||||
for (uint32_t pd_idx = 0; pd_idx < 1024; pd_idx++) {
|
||||
if (!(pd->pd[pd_idx] & PF_PRESENT))
|
||||
continue;
|
||||
|
||||
uptr_t pt_phys = pd->pd[pd_idx] & ~(PAGE_SIZE - 1);
|
||||
volatile uint32_t *pt = (volatile uint32_t *)(pt_phys + VIRT_BASE);
|
||||
|
||||
for (uint32_t pt_idx = 0; pt_idx < 1024; pt_idx++) {
|
||||
if (!(pt[pt_idx] & PF_PRESENT))
|
||||
continue;
|
||||
|
||||
uptr_t pt_addr = pt[pt_idx] & ~(PAGE_SIZE - 1);
|
||||
if (pt_addr == (paddr & ~(PAGE_SIZE - 1))) {
|
||||
vaddr = (pd_idx << PD_SHIFT) | (pt_idx << PT_SHIFT) | (paddr & (PAGE_SIZE - 1));
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
|
||||
if (flags & PF_LOCK)
|
||||
sl_unlock(&pd->sl);
|
||||
|
||||
return found ? vaddr : 0;
|
||||
}
|
||||
|
||||
void mm_load_pd(struct page_dir *pd) {
|
||||
uptr_t phys = (uptr_t)pd->pd - VIRT_BASE;
|
||||
__asm__ volatile ("movl %0, %%cr3" :: "r"(phys));
|
||||
|
||||
@ -60,7 +60,8 @@ void mm_init(void);
|
||||
|
||||
void mm_map_page(struct page_dir *pd, uptr_t vaddr, uptr_t paddr, uint32_t flags);
|
||||
void mm_unmap_page(struct page_dir *pd, uptr_t vaddr, uint32_t flags);
|
||||
uptr_t mm_translate(struct page_dir *pd, uptr_t vaddr, uint32_t flags);
|
||||
uptr_t mm_translate_v2p(struct page_dir *pd, uptr_t vaddr, uint32_t flags);
|
||||
uptr_t mm_translate_p2v(struct page_dir *pd, uptr_t paddr, uint32_t flags);
|
||||
void mm_load_pd(struct page_dir *pd);
|
||||
struct page_dir *mm_get_kernel_pd(void);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user