diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..3c94e0c4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "kernel-src/io/acpi/uacpi"] + path = kernel-src/io/acpi/uacpi + url = https://github.com/UltraOS/uACPI diff --git a/kernel-src/Makefile b/kernel-src/Makefile index dcf53549..d8ec278d 100644 --- a/kernel-src/Makefile +++ b/kernel-src/Makefile @@ -10,8 +10,10 @@ LDFLAGS=-ffreestanding -nostdlib -lgcc -Wl,-T,"$(ARCHDIR)/kernel.ld" -g PRINTFOPTS=-DPRINTF_DISABLE_SUPPORT_FLOAT # flanterm includes break if the headers are moved into the include dir in the tree FLANTERMINCDIR=$(shell pwd)/flanterm +UACPIINCDIR=$(shell pwd)/io/acpi/uacpi/include +UACPIOPTS=-DUACPI_OVERRIDE_LIBC -DUACPI_KERNEL_INITIALIZATION KERNELCONFIG=#-DX86_64_ENABLE_E9 -DSYSCALL_LOGGING -CFLAGS=-g -ffreestanding -mcmodel=kernel -O2 -mno-red-zone -mgeneral-regs-only -mno-mmx -mno-sse -mno-sse2 -nostdlib -Wall -I "${INCDIR}" -I "$(ARCHINCDIR)" -I "$(FLANTERMINCDIR)" $(PRINTFOPTS) $(KERNELCONFIG) +CFLAGS=-g -ffreestanding -mcmodel=kernel -O2 -mno-red-zone -mgeneral-regs-only -mno-mmx -mno-sse -mno-sse2 -nostdlib -Wall -I "${INCDIR}" -I "$(ARCHINCDIR)" -I "$(FLANTERMINCDIR)" -I "$(UACPIINCDIR)" $(UACPIOPTS) $(PRINTFOPTS) $(KERNELCONFIG) ASFLAGS=-felf64 .PHONY: all diff --git a/kernel-src/arch/x86-64/acpi.c b/kernel-src/arch/x86-64/acpi.c index 596b3196..52685e0f 100644 --- a/kernel-src/arch/x86-64/acpi.c +++ b/kernel-src/arch/x86-64/acpi.c @@ -1,86 +1,12 @@ -#include #include -#include -#include -#include #include -#include - -static size_t headercount; -static sdtheader_t **headers; +#include static volatile struct limine_rsdp_request rsdpreq = { .id = LIMINE_RSDP_REQUEST, .revision = 0 }; -bool arch_acpi_checksumok(void *table) { - sdtheader_t *header = table; - uint8_t *ptr = table; - uint8_t sum = 0; - for (int i = 0; i < header->length; ++i) - sum += *ptr++; - - return sum == 0; -} - -void *arch_acpi_findtable(char *sig, int n) { - for (size_t i = 0; i < headercount; ++i) { - if (headers[i] == NULL) - continue; - - if (strncmp(sig, headers[i]->sig, 4) == 0) { - if (n-- == 0) - return headers[i]; - } - } - return NULL; -} - -static void *map_table(void *physical) { - // TODO map according to header length entry - uintmax_t pageoffset = (uintptr_t)physical % PAGE_SIZE; - void *virt = vmm_map(NULL, PAGE_SIZE * 16, VMM_FLAGS_PHYSICAL, ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_NOEXEC, physical); - __assert(virt); - return (void *)((uintptr_t)virt + pageoffset); -} - -void arch_acpi_init() { - // FIXME search if no response - __assert(rsdpreq.response); - rsdp_t *rsdp = map_table(FROM_HHDM(rsdpreq.response->address)); - - if (rsdp->revision == 0) { - printf("\e[93macpi: version 1.0 -- Using RSDT instead of XSDT.\e[0m\n"); - rsdt_t *rsdt = map_table((void *)(uint64_t)rsdp->rsdt); - __assert(arch_acpi_checksumok(rsdt)); - - headercount = (rsdt->header.length - sizeof(sdtheader_t)) / sizeof(uint32_t); - headers = alloc(sizeof(sdtheader_t *) * headercount); - __assert(headers); - - // TODO map according to the header length entry - for (size_t i = 0; i < headercount; ++i) { - // map it - void *addr = map_table((void *)(uint64_t)rsdt->tables[i]); - if (arch_acpi_checksumok(addr)) - headers[i] = addr; - } - } else { - xsdt_t *xsdt = map_table((void *)rsdp->xsdt); - __assert(arch_acpi_checksumok(xsdt)); - - headercount = (xsdt->header.length - sizeof(sdtheader_t)) / sizeof(uint64_t); - headers = alloc(sizeof(sdtheader_t *) * headercount); - __assert(headers); - - for (size_t i = 0; i < headercount; ++i) { - // map it - void *addr = map_table((void *)xsdt->tables[i]); - if (arch_acpi_checksumok(addr)) - headers[i] = addr; - } - } - - printf("ACPI: %lu tables found\n", headercount); +uintptr_t arch_get_rsdp(void) { + return (uintptr_t)FROM_HHDM(rsdpreq.response->address); } diff --git a/kernel-src/arch/x86-64/apic.c b/kernel-src/arch/x86-64/apic.c index 5c558878..784e2f90 100644 --- a/kernel-src/arch/x86-64/apic.c +++ b/kernel-src/arch/x86-64/apic.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -9,6 +8,9 @@ #include #include +#include +#include + #define IOAPIC_REG_ID 0 #define IOAPIC_REG_ENTRYCOUNT 1 #define IOAPIC_REG_PRIORITY 2 @@ -33,47 +35,6 @@ #define LVT_DELIVERY_NMI (0b100 << 8) #define LVT_MASK (1 << 16) -typedef struct { - uint8_t type; - uint8_t length; -} __attribute__((packed)) listheader_t; - -typedef struct { - listheader_t header; - uint16_t reserved; - uint64_t addr; -} __attribute__((packed)) lapicoverride_t; - -typedef struct { - listheader_t header; - uint8_t id; - uint8_t reserved; - uint32_t addr; - uint32_t intbase; -} __attribute__((packed)) ioentry_t; - -typedef struct { - listheader_t header; - uint8_t acpiid; - uint8_t lapicid; - uint32_t flags; -} __attribute__((packed)) lapicentry_t; - -typedef struct { - listheader_t header; - uint8_t acpiid; - uint16_t flags; - uint8_t lint; -} __attribute__((packed)) lapicnmientry_t; - -typedef struct { - listheader_t header; - uint8_t bus; - uint8_t irq; - uint32_t gsi; - uint16_t flags; -} __attribute__((packed)) ioapicoverride_t; - typedef struct { void *addr; int base; @@ -82,9 +43,9 @@ typedef struct { static ioapicdesc_t *ioapics; -static madt_t *madt; -static listheader_t *liststart; -static listheader_t *listend; +static struct acpi_madt *madt; +static struct acpi_entry_hdr *liststart; +static struct acpi_entry_hdr *listend; static void *lapicaddr; static size_t overridecount; @@ -100,13 +61,13 @@ static ioapicdesc_t *ioapicfromgsi(int gsi) { return NULL; } -static inline listheader_t *getnext(listheader_t *header) { +static inline struct acpi_entry_hdr *getnext(struct acpi_entry_hdr *header) { uintptr_t ptr = (uintptr_t)header; - return (listheader_t *)(ptr + header->length); + return (struct acpi_entry_hdr *)(ptr + header->length); } static inline void *getentry(int type, int n) { - listheader_t *entry = liststart; + struct acpi_entry_hdr *entry = liststart; while (entry < listend) { if (entry->type == type) { @@ -120,7 +81,7 @@ static inline void *getentry(int type, int n) { } static int getcount(int type) { - listheader_t *entry = liststart; + struct acpi_entry_hdr *entry = liststart; int count = 0; while (entry < listend) { @@ -171,8 +132,8 @@ void arch_ioapic_setirq(uint8_t irq, uint8_t vector, uint8_t proc, bool masked) uint8_t polarity = 1; // active high uint8_t trigger = 0; // edge triggered for (uintmax_t i = 0; i < overridecount; ++i) { - ioapicoverride_t *override = getentry(ACPI_MADT_TYPE_OVERRIDE, i); - if (override->irq != irq) + struct acpi_madt_interrupt_source_override *override = getentry(ACPI_MADT_ENTRY_TYPE_INTERRUPT_SOURCE_OVERRIDE, i); + if (override->source != irq) continue; polarity = override->flags & 2 ? 1 : 0; // active low @@ -210,9 +171,9 @@ void arch_apic_initap() { // get the acpi id for the local nmi sources for (size_t i = 0; i < lapiccount; ++i) { - lapicentry_t *current = getentry(ACPI_MADT_TYPE_LAPIC, i); - if (_cpu()->id == current->lapicid) { - _cpu()->acpiid = current->acpiid; + struct acpi_madt_lapic *current = getentry(ACPI_MADT_ENTRY_TYPE_LAPIC, i); + if (_cpu()->id == current->id) { + _cpu()->acpiid = current->uid; break; } } @@ -221,10 +182,10 @@ void arch_apic_initap() { isr_t *nmiisr = interrupt_allocate(nmi, NULL, IPL_MAX); // MAX IPL because NMI __assert(nmiisr); - + for (size_t i = 0; i < lapicnmicount; ++i) { - lapicnmientry_t* current = getentry(ACPI_MADT_TYPE_LANMI, i); - if (current->acpiid == _cpu()->acpiid || current->acpiid == 0xff) + struct acpi_madt_lapic_nmi* current = getentry(ACPI_MADT_ENTRY_TYPE_LAPIC_NMI, i); + if (current->uid == _cpu()->acpiid || current->uid == 0xff) writelapic(APIC_LVT_LINT0 + 0x10 * current->lint, (nmiisr->id & 0xff) | LVT_DELIVERY_NMI | (current->flags << 12)); } } @@ -288,22 +249,25 @@ void arch_apic_sendipi(uint8_t cpu, uint8_t vec, uint8_t dest, uint8_t mode, uin } void arch_apic_init() { - madt = arch_acpi_findtable("APIC", 0); - __assert(madt); + uacpi_table tbl; - liststart = (void *)((uintptr_t)madt + sizeof(madt_t)); - listend = (void *)((uintptr_t)madt + madt->header.length); + uacpi_status ret = uacpi_table_find_by_signature("APIC", &tbl); + __assert(ret == UACPI_STATUS_OK); - overridecount = getcount(ACPI_MADT_TYPE_OVERRIDE); - iocount = getcount(ACPI_MADT_TYPE_IOAPIC); - lapiccount = getcount(ACPI_MADT_TYPE_LAPIC); - lapicnmicount = getcount(ACPI_MADT_TYPE_LANMI); + madt = tbl.ptr; + liststart = (void *)((uintptr_t)madt + sizeof(struct acpi_madt)); + listend = (void *)((uintptr_t)madt + madt->hdr.length); + + overridecount = getcount(ACPI_MADT_ENTRY_TYPE_INTERRUPT_SOURCE_OVERRIDE); + iocount = getcount(ACPI_MADT_ENTRY_TYPE_IOAPIC); + lapiccount = getcount(ACPI_MADT_ENTRY_TYPE_LAPIC); + lapicnmicount = getcount(ACPI_MADT_ENTRY_TYPE_LAPIC_NMI); // map LAPIC to virtual memory - lapicoverride_t *lapic64 = getentry(ACPI_MADT_TYPE_64BITADDR, 0); + struct acpi_madt_lapic_address_override *lapic64 = getentry(ACPI_MADT_ENTRY_TYPE_LAPIC_ADDRESS_OVERRIDE, 0); - void *paddr = lapic64 ? (void *)lapic64->addr : (void *)(uint64_t)madt->addr; + void *paddr = lapic64 ? (void *)lapic64->address : (void *)(uint64_t)madt->local_interrupt_controller_address; if (lapic64) printf("\e[94mUsing 64 bit override for the local APIC address\n\e[0m"); @@ -317,17 +281,17 @@ void arch_apic_init() { __assert(ioapics); for (int i = 0; i < iocount; ++i) { - ioentry_t *entry = getentry(ACPI_MADT_TYPE_IOAPIC, i); + struct acpi_madt_ioapic *entry = getentry(ACPI_MADT_ENTRY_TYPE_IOAPIC, i); - __assert((entry->addr % PAGE_SIZE) == 0); - ioapics[i].addr = vmm_map(NULL, PAGE_SIZE, VMM_FLAGS_PHYSICAL, ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC, (void *)(uint64_t)entry->addr); + __assert((entry->address % PAGE_SIZE) == 0); + ioapics[i].addr = vmm_map(NULL, PAGE_SIZE, VMM_FLAGS_PHYSICAL, ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC, (void *)(uint64_t)entry->address); __assert(ioapics[i].addr); - ioapics[i].base = entry->intbase; + ioapics[i].base = entry->gsi_base; ioapics[i].top = ioapics[i].base + ((readioapic(ioapics[i].addr, IOAPIC_REG_ENTRYCOUNT) >> 16) & 0xff) + 1; - printf("ioapic%lu: addr %p base %lu top %lu\n", i, entry->addr, entry->intbase, ioapics[i].top); + printf("ioapic%lu: addr %p base %lu top %lu\n", i, entry->address, entry->gsi_base, ioapics[i].top); for (int j = ioapics[i].base; j < ioapics[i].top; ++j) writeiored(ioapics[i].addr, j - ioapics[i].base, 0xfe, 0, 0, 0, 0, 1, 0); - + } // FIXME limine does this already but just in case the PIC should be masked diff --git a/kernel-src/arch/x86-64/hpet.c b/kernel-src/arch/x86-64/hpet.c index 9a8c8b4a..802fde82 100644 --- a/kernel-src/arch/x86-64/hpet.c +++ b/kernel-src/arch/x86-64/hpet.c @@ -5,6 +5,9 @@ #include #include +#include +#include + #define HPET_REG_CAPS 0 #define HPET_REG_CONFIG 2 #define HPET_REG_INTSTATUS 4 @@ -62,10 +65,6 @@ void arch_hpet_waitus(time_t us) { arch_hpet_waitticks(us * ticksperus); } -bool arch_hpet_exists() { - return arch_acpi_findtable("HPET", 0) != NULL; -} - static void counterirq(isr_t *isr, context_t *context) { write64(HPET_REG_CONFIG, 0); @@ -75,26 +74,36 @@ static void counterirq(isr_t *isr, context_t *context) { write64(HPET_REG_CONFIG, 1 + ((type == TYPE_LEGACY) ? 2 : 0)); } +bool arch_hpet_exists() { + // This is assumed by arch_hpet_init below + return true; +} time_t arch_hpet_init() { - __assert(arch_hpet_exists()); - hpet_t *table = arch_acpi_findtable("HPET", 0); - printf("hpet%lu: %lu bits %lu comparators\n", table->hpetnum, 32 + table->countersize * 32, table->comparatorcount); - __assert(table->addrid == 0); - __assert((table->addr & PAGE_SIZE) == 0); - hpet = vmm_map(NULL, PAGE_SIZE, VMM_FLAGS_PHYSICAL, ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC, (void *)table->addr); + uacpi_table tbl; + struct acpi_hpet *table; + + __assert(uacpi_table_find_by_signature("HPET", &tbl) == UACPI_STATUS_OK); + table = tbl.ptr; + + printf("hpet%lu: %lu bits %lu comparators\n", table->number, + (table->block_id & ACPI_HPET_COUNT_SIZE_CAP) ? 64 : 32, + (table->block_id >> ACPI_HPET_NUMBER_OF_COMPARATORS_SHIFT) & ACPI_HPET_NUMBER_OF_COMPARATORS_MASK); + __assert(table->address.address_space_id == ACPI_AS_ID_SYS_MEM); + __assert((table->address.address & PAGE_SIZE) == 0); + hpet = vmm_map(NULL, PAGE_SIZE, VMM_FLAGS_PHYSICAL, ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC, (void *)table->address.address); __assert(hpet); uint64_t capabilities = read64(HPET_REG_CAPS); ticksperus = 1000000000 / HPET_CAP_FSPERTICK(capabilities); - printf("hpet%lu: %lu ticks per us (%lu fs per tick)\n", table->hpetnum, ticksperus, HPET_CAP_FSPERTICK(capabilities)); + printf("hpet%lu: %lu ticks per us (%lu fs per tick)\n", table->number, ticksperus, HPET_CAP_FSPERTICK(capabilities)); __assert(ticksperus); write64(HPET_REG_CONFIG, 0); write64(HPET_REG_COUNTER, 0); - if (table->countersize == 0) { + if (!(table->block_id & ACPI_HPET_COUNT_SIZE_CAP)) { int selectedtimer = 2; uint64_t timerconfig = read64(HPET_TIMER_CONFIG(selectedtimer)); // a 32 bit main counter will require more handling on our side. @@ -122,7 +131,7 @@ time_t arch_hpet_init() { write64(HPET_TIMER_CONFIG(selectedtimer), timerconfig); write64(HPET_TIMER_COMPARATOR(selectedtimer), 0x80000000); - printf("hpet%d: timer %d on isr %d using %s\n", table->hpetnum, selectedtimer, isr->id & 0xff, "legacy replacement"); + printf("hpet%d: timer %d on isr %d using %s\n", table->number, selectedtimer, isr->id & 0xff, "legacy replacement"); } else { type = TYPE_64BIT; } diff --git a/kernel-src/arch/x86-64/main.c b/kernel-src/arch/x86-64/main.c index a7ee64f0..5b532e0e 100644 --- a/kernel-src/arch/x86-64/main.c +++ b/kernel-src/arch/x86-64/main.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -57,7 +58,7 @@ void kernel_entry() { vmm_init(); alloc_init(); cmdline_parse(); - arch_acpi_init(); + acpi_early_init(); arch_apic_init(); cpu_initstate(); // XXX fall back to another clock source @@ -68,6 +69,8 @@ void kernel_entry() { arch_smp_wakeup(); pci_init(); + acpi_init(); + vmmcache_init(); vfs_init(); diff --git a/kernel-src/include/kernel/acpi.h b/kernel-src/include/kernel/acpi.h new file mode 100644 index 00000000..892454ef --- /dev/null +++ b/kernel-src/include/kernel/acpi.h @@ -0,0 +1,13 @@ +#ifndef _ACPI_H +#define _ACPI_H + +// Doesn't depend on PCI, only makes table search available +void acpi_early_init(void); + +// Loads the actual ACPI namespace, hooks up the power button +void acpi_init(void); + +int acpi_poweroff(void); +int acpi_reboot(void); + +#endif \ No newline at end of file diff --git a/kernel-src/include/kernel/interrupt.h b/kernel-src/include/kernel/interrupt.h index 3a7405b3..d4fae69f 100644 --- a/kernel-src/include/kernel/interrupt.h +++ b/kernel-src/include/kernel/interrupt.h @@ -19,6 +19,7 @@ #define IPL_DISK 500 #define IPL_KEYBOARD 600 #define IPL_MOUSE 650 +#define IPL_ACPI 700 #define IPL_DPC 900 #define IPL_NORMAL 1000 diff --git a/kernel-src/include/uacpi_libc.h b/kernel-src/include/uacpi_libc.h new file mode 100644 index 00000000..81243afa --- /dev/null +++ b/kernel-src/include/uacpi_libc.h @@ -0,0 +1,11 @@ +#include +#include + +#define uacpi_memcpy memcpy +#define uacpi_memset memset +#define uacpi_memcmp memcmp +#define uacpi_strcmp strcmp +#define uacpi_strlen strlen + +#define uacpi_snprintf snprintf +#define uacpi_vsnprintf vsnprintf diff --git a/kernel-src/include/x86-64/arch/acpi.h b/kernel-src/include/x86-64/arch/acpi.h index e4720cd1..b76c362e 100644 --- a/kernel-src/include/x86-64/arch/acpi.h +++ b/kernel-src/include/x86-64/arch/acpi.h @@ -1,92 +1,8 @@ -#ifndef _ACPI_H -#define _ACPI_H +#ifndef _ARCH_ACPI_H +#define _ARCH_ACPI_H -#include #include -#include -typedef struct { - char sig[8]; - uint8_t checksum; - char oem[6]; - uint8_t revision; - uint32_t rsdt; - uint32_t length; - uint64_t xsdt; - uint8_t xchecksum; - uint8_t reserved[3]; -} __attribute__((packed)) rsdp_t; - -typedef struct { - char sig[4]; - uint32_t length; - uint8_t revision; - uint8_t cechksum; - char oemid[6]; - char oemtableid[8]; - uint32_t oemrevision; - uint32_t creatorid; - uint32_t creatorrevision; -} __attribute__((packed)) sdtheader_t; - -typedef struct { - sdtheader_t header; - uint64_t tables[]; -} __attribute__((packed)) xsdt_t; - -typedef struct { - sdtheader_t header; - uint32_t tables[]; -} __attribute__((packed)) rsdt_t; - -typedef struct { - sdtheader_t header; - uint32_t addr; - uint32_t flags; -} __attribute__((packed)) madt_t; - -typedef struct { - sdtheader_t header; - uint8_t hwrev; - uint8_t comparatorcount:5; - uint8_t countersize:1; - uint8_t reserved:1; - uint8_t legacy_replacement:1; - uint16_t pcivendor; - uint8_t addrid; - uint8_t bitwidth; - uint8_t bitoffset; - uint8_t reserved2; - uint64_t addr; - uint8_t hpetnum; - uint16_t mintick; - uint8_t pageprot; -} __attribute__((packed)) hpet_t; - -typedef struct { - uint64_t address; - uint16_t sgn; - uint8_t startbus; - uint8_t endbus; - uint32_t reserved; -} __attribute__((packed)) mcfgentry_t; - -typedef struct { - sdtheader_t header; - uint64_t reserved; - mcfgentry_t entries[]; -} __attribute__((packed)) mcfg_t; - -#define ACPI_MADT_TYPE_LAPIC 0 -#define ACPI_MADT_TYPE_IOAPIC 1 -#define ACPI_MADT_TYPE_OVERRIDE 2 -#define ACPI_MADT_TYPE_IONMI 3 -#define ACPI_MADT_TYPE_LANMI 4 -#define ACPI_MADT_TYPE_64BITADDR 5 -#define ACPI_MADT_TYPE_X2APIC 9 - -bool arch_acpi_checksumok(void *table); -void *arch_acpi_findtable(char *sig, int n); -void arch_acpi_init(); +uintptr_t arch_get_rsdp(void); #endif diff --git a/kernel-src/include/x86-64/arch/pci.h b/kernel-src/include/x86-64/arch/pci.h index 217ae278..3603c9fd 100644 --- a/kernel-src/include/x86-64/arch/pci.h +++ b/kernel-src/include/x86-64/arch/pci.h @@ -11,8 +11,11 @@ #include #include +#include +#include + static size_t mcfgentrycount; -static mcfgentry_t *mcfgentries; +static struct acpi_mcfg_allocation *mcfgentries; static uint32_t (*pci_archread32)(int bus, int device, int function, uint32_t offset); static void (*pci_archwrite32)(int bus, int device, int function, uint32_t offset, uint32_t value); @@ -31,13 +34,13 @@ static void legacy_write32(int bus, int device, int function, uint32_t offset, u outd(CONFDATA, value); } -static inline mcfgentry_t *getmcfgentry(int bus) { - mcfgentry_t *entry; +static inline struct acpi_mcfg_allocation *getmcfgentry(int bus) { + struct acpi_mcfg_allocation *entry; int i; for (i = 0; i < mcfgentrycount; ++i) { entry = &mcfgentries[i]; - if (bus >= entry->startbus && bus <= entry->endbus) + if (bus >= entry->start_bus && bus <= entry->end_bus) break; } @@ -48,21 +51,21 @@ static inline mcfgentry_t *getmcfgentry(int bus) { } static uint32_t mcfg_read32(int bus, int device, int function, uint32_t offset) { - mcfgentry_t *entry = getmcfgentry(bus); + struct acpi_mcfg_allocation *entry = getmcfgentry(bus); if (entry == NULL) return 0xffffffff; uint64_t base = (uint64_t)entry->address; - volatile uint32_t *address = (uint32_t *)((uintptr_t)base + (((bus - entry->startbus) << 20) | (device << 15) | (function << 12) | (offset & ~0x3))); + volatile uint32_t *address = (uint32_t *)((uintptr_t)base + (((bus - entry->start_bus) << 20) | (device << 15) | (function << 12) | (offset & ~0x3))); return *address; } static void mcfg_write32(int bus, int device, int function, uint32_t offset, uint32_t value) { - mcfgentry_t *entry = getmcfgentry(bus); + struct acpi_mcfg_allocation *entry = getmcfgentry(bus); uint64_t base = (uint64_t)entry->address; - volatile uint32_t *address = (uint32_t *)((uintptr_t)base + (((bus - entry->startbus) << 20) | (device << 15) | (function << 12) | (offset & ~0x3))); + volatile uint32_t *address = (uint32_t *)((uintptr_t)base + (((bus - entry->start_bus) << 20) | (device << 15) | (function << 12) | (offset & ~0x3))); *address = value; } @@ -77,20 +80,23 @@ static uint64_t msiformatmessage(uint32_t *data, int vector, int edgetrigger, in #define MCFG_MAPPING_SIZE(buscount) (4096l * 8l * 32l * (buscount)) static void pci_archinit() { - mcfg_t *mcfg = arch_acpi_findtable("MCFG", 0); + uacpi_table tbl; + struct acpi_mcfg *mcfg; - if (mcfg) { + uacpi_status ret = uacpi_table_find_by_signature("MCFG", &tbl); + if (ret == UACPI_STATUS_OK) { printf("pci: MCFG table found. Using extended configuration space\n"); + mcfg = tbl.ptr; pci_archread32 = mcfg_read32; pci_archwrite32 = mcfg_write32; - mcfgentrycount = (mcfg->header.length - sizeof(sdtheader_t)) / sizeof(mcfgentry_t); - mcfgentries = alloc(sizeof(mcfgentry_t) * mcfgentrycount); + mcfgentrycount = (mcfg->hdr.length - sizeof(struct acpi_sdt_hdr)) / sizeof(struct acpi_mcfg_allocation); + mcfgentries = alloc(sizeof(struct acpi_mcfg_allocation) * mcfgentrycount); __assert(mcfgentries); - memcpy(mcfgentries, mcfg->entries, sizeof(mcfgentry_t) * mcfgentrycount); + memcpy(mcfgentries, mcfg->entries, sizeof(struct acpi_mcfg_allocation) * mcfgentrycount); for (int i = 0; i < mcfgentrycount; ++i) { void *phys = (void *)mcfgentries[i].address; uintmax_t pageoffset = (uintptr_t)phys % PAGE_SIZE; - size_t mappingsize = pageoffset + MCFG_MAPPING_SIZE(mcfgentries[i].endbus - mcfgentries[i].startbus + 1); + size_t mappingsize = pageoffset + MCFG_MAPPING_SIZE(mcfgentries[i].end_bus - mcfgentries[i].start_bus + 1); void *virt = vmm_map(NULL, mappingsize, VMM_FLAGS_PHYSICAL, ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC | ARCH_MMU_FLAGS_UC, phys); __assert(virt); diff --git a/kernel-src/io/acpi/acpi.c b/kernel-src/io/acpi/acpi.c new file mode 100644 index 00000000..220adb46 --- /dev/null +++ b/kernel-src/io/acpi/acpi.c @@ -0,0 +1,86 @@ + +#include "uacpi/kernel_api.h" +#include "uacpi/status.h" +#include "uacpi/types.h" +#include +#include +#include +#include + +#include +#include +#include +#include + + +void acpi_early_init(void) { + struct uacpi_init_params params = { + .rsdp = arch_get_rsdp(), + .log_level = UACPI_LOG_INFO, + }; + + uacpi_status ret = uacpi_initialize(¶ms); + __assert(ret == UACPI_STATUS_OK); +} + +void acpi_dopoweroff(uacpi_handle ctx) { + (void)ctx; + acpi_poweroff(); +} + +static uacpi_interrupt_ret handle_pwrbtn(uacpi_handle ctx) { + uacpi_kernel_schedule_work(UACPI_WORK_GPE_EXECUTION, acpi_dopoweroff, ctx); + return UACPI_INTERRUPT_HANDLED; +} + +void acpi_init(void) { + uacpi_status ret; + + ret = uacpi_namespace_load(); + __assert(ret == UACPI_STATUS_OK); + + uacpi_set_interrupt_model(UACPI_INTERRUPT_MODEL_IOAPIC); + + ret = uacpi_namespace_initialize(); + __assert(ret == UACPI_STATUS_OK); + + uacpi_install_fixed_event_handler( + UACPI_FIXED_EVENT_POWER_BUTTON, + handle_pwrbtn, UACPI_NULL + ); +} + +int acpi_poweroff(void) { + uacpi_status ret = uacpi_prepare_for_sleep_state(UACPI_SLEEP_STATE_S5); + if (uacpi_unlikely_error(ret)) { + printf("acpi: unable to prepare for sleep: %s", uacpi_status_to_string(ret)); + return EIO; + } + + interrupt_set(false); + + ret = uacpi_enter_sleep_state(UACPI_SLEEP_STATE_S5); + if (uacpi_unlikely_error(ret)) { + interrupt_set(true); + printf("acpi: unable to enter S5: %s", uacpi_status_to_string(ret)); + return EIO; + } + + return 0; +} + +int acpi_reboot(void) { + /* + * Windows does reboot via shutdown, so some hardware expects \_PTS(5) + * to be called prior to rebooting, do that. + */ + uacpi_prepare_for_sleep_state(UACPI_SLEEP_STATE_S5); + + uacpi_status ret = uacpi_reboot(); + if (uacpi_unlikely_error(ret)) { + printf("unable to perform system reset: %s\n", uacpi_status_to_string(ret)); + return EIO; + } + + return 0; +} diff --git a/kernel-src/io/acpi/glue.c b/kernel-src/io/acpi/glue.c new file mode 100644 index 00000000..5e1b6e72 --- /dev/null +++ b/kernel-src/io/acpi/glue.c @@ -0,0 +1,551 @@ +#include "arch/context.h" +#include "arch/cpu.h" +#include "arch/mmu.h" +#include "kernel/interrupt.h" +#include "kernel/scheduler.h" +#include "mutex.h" +#include "semaphore.h" +#include "spinlock.h" +#include "uacpi/types.h" +#include "util.h" +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + + +void uacpi_kernel_log(uacpi_log_level lvl, const uacpi_char *str) { + const char *lvlstr; + + switch (lvl) { + case UACPI_LOG_DEBUG: + lvlstr = "debug"; + break; + case UACPI_LOG_TRACE: + lvlstr = "trace"; + break; + case UACPI_LOG_INFO: + lvlstr = "info"; + break; + case UACPI_LOG_WARN: + lvlstr = "warn"; + break; + case UACPI_LOG_ERROR: + lvlstr = "error"; + break; + default: + lvlstr = ""; + } + + printf("acpi/%s %s", lvlstr, str) +} + +void *uacpi_kernel_alloc(uacpi_size size) { + return alloc(size); +} + +void *uacpi_kernel_calloc(uacpi_size count, uacpi_size size) { + return alloc(count * size); +} + +void uacpi_kernel_free(void *ptr) { + if (ptr == NULL) + return; + + return free(ptr); +} + +void *uacpi_kernel_map(uacpi_phys_addr physical, uacpi_size length) { + uintmax_t pageoffset = (uintptr_t)physical % PAGE_SIZE; + void *virt = vmm_map(NULL, ROUND_UP(length, PAGE_SIZE), VMM_FLAGS_PHYSICAL, + ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC, + (void*)ROUND_DOWN(physical, PAGE_SIZE)); + __assert(virt); + return (void *)((uintptr_t)virt + pageoffset); +} + +void uacpi_kernel_unmap(void *ptr, uacpi_size length) { + uintptr_t addr = (uintptr_t)ptr; + vmm_unmap((void*)ROUND_DOWN(addr, PAGE_SIZE), ROUND_UP(length, PAGE_SIZE), 0); +} + +uacpi_status uacpi_kernel_raw_memory_read( + uacpi_phys_addr address, uacpi_u8 width, uacpi_u64 *out +) { + void *ptr = uacpi_kernel_map(address, width); + + switch (width) { + case 1: + *out = *(volatile uint8_t *)ptr; + break; + case 2: + *out = *(volatile uint16_t *)ptr; + break; + case 4: + *out = *(volatile uint32_t *)ptr; + break; + case 8: + *out = *(volatile uint64_t *)ptr; + break; + default: + uacpi_kernel_unmap(ptr, width); + return UACPI_STATUS_INVALID_ARGUMENT; + } + + uacpi_kernel_unmap(ptr, width); + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_kernel_raw_memory_write( + uacpi_phys_addr address, uacpi_u8 width, uacpi_u64 in +) { + void *ptr = uacpi_kernel_map(address, width); + + switch (width) { + case 1: + *(volatile uint8_t *)ptr = in; + break; + case 2: + *(volatile uint16_t *)ptr = in; + break; + case 4: + *(volatile uint32_t *)ptr = in; + break; + case 8: + *(volatile uint64_t *)ptr = in; + break; + default: + uacpi_kernel_unmap(ptr, width); + return UACPI_STATUS_INVALID_ARGUMENT; + } + + uacpi_kernel_unmap(ptr, width); + return UACPI_STATUS_OK; +} + +#ifdef __amd64__ +uacpi_status +uacpi_kernel_raw_io_write(uacpi_io_addr addr, uacpi_u8 width, + uacpi_u64 value) { + switch (width) { + case 1: + outb(addr, value); + break; + case 2: + outw(addr, value); + break; + case 4: + outd(addr, value); + break; + default: + return UACPI_STATUS_INVALID_ARGUMENT; + } + + return UACPI_STATUS_OK; +} + +uacpi_status +uacpi_kernel_raw_io_read(uacpi_io_addr addr, uacpi_u8 width, + uacpi_u64 *out) { + switch (width) { + case 1: + *out = inb(addr); + break; + case 2: + *out = inw(addr); + break; + case 4: + *out = ind(addr); + break; + default: + return UACPI_STATUS_INVALID_ARGUMENT; + } + + return UACPI_STATUS_OK; +} + +// Stolen from https://github.com/osdev0/cc-runtime/blob/dcdf5d82973e77edee597a047a3ef66300903de9/cc-runtime.c#L2229 +int __popcountdi2(int64_t a) { + uint64_t x2 = (uint64_t)a; + x2 = x2 - ((x2 >> 1) & 0x5555555555555555uLL); + // Every 2 bits holds the sum of every pair of bits (32) + x2 = ((x2 >> 2) & 0x3333333333333333uLL) + (x2 & 0x3333333333333333uLL); + // Every 4 bits holds the sum of every 4-set of bits (3 significant bits) (16) + x2 = (x2 + (x2 >> 4)) & 0x0F0F0F0F0F0F0F0FuLL; + // Every 8 bits holds the sum of every 8-set of bits (4 significant bits) (8) + uint32_t x = (uint32_t)(x2 + (x2 >> 32)); + // The lower 32 bits hold four 16 bit sums (5 significant bits). + // Upper 32 bits are garbage + x = x + (x >> 16); + // The lower 16 bits hold two 32 bit sums (6 significant bits). + // Upper 16 bits are garbage + return (x + (x >> 8)) & 0x0000007F; // (7 significant bits) +} +#else +uacpi_status uacpi_kernel_raw_io_read(uacpi_io_addr addr, uacpi_u8 width, + uacpi_u64 *out) { + return UACPI_STATUS_UNIMPLEMENTED; +} +uacpi_status uacpi_kernel_raw_io_write(uacpi_io_addr addr, uacpi_u8 width, + uacpi_u64 value) { + return UACPI_STATUS_UNIMPLEMENTED; +} +#endif + +uacpi_status uacpi_kernel_io_map(uacpi_io_addr base, uacpi_size size, + uacpi_handle *outhandle) { + *outhandle = (uacpi_handle)base; + return UACPI_STATUS_OK; +} + +void uacpi_kernel_io_unmap(uacpi_handle handle) { + (void)handle; +} + +uacpi_status uacpi_kernel_io_read(uacpi_handle handle, uacpi_size offset, + uacpi_u8 width, uacpi_u64 *out) { + return uacpi_kernel_raw_io_read((uacpi_io_addr)handle + offset, width, out); +} + +uacpi_status uacpi_kernel_io_write(uacpi_handle handle, uacpi_size offset, + uacpi_u8 width, uacpi_u64 value) { + return uacpi_kernel_raw_io_write((uacpi_io_addr)handle + offset, width, value); +} + +uacpi_status uacpi_kernel_pci_read( + uacpi_pci_address *address, uacpi_size offset, + uacpi_u8 width, uacpi_u64 *out +) { + if (address->segment != 0) { + printf("reading from PCI segment %u is not supported\n", address->segment); + return UACPI_STATUS_UNIMPLEMENTED; + } + + switch (width) { + case 1: { + *out = pci_read8(address->bus, address->device, address->function, offset); + break; + } + case 2: { + *out = pci_read16(address->bus, address->device, address->function, offset); + break; + } + case 4: { + *out = pci_read32(address->bus, address->device, address->function, offset); + break; + } + default: + return UACPI_STATUS_INVALID_ARGUMENT; + } + + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_kernel_pci_write( + uacpi_pci_address *address, uacpi_size offset, + uacpi_u8 width, uacpi_u64 value +) { + if (address->segment != 0) { + printf("writing to PCI segment %u is not supported\n", address->segment); + return UACPI_STATUS_UNIMPLEMENTED; + } + + switch (width) { + case 1: { + pci_write8(address->bus, address->device, address->function, offset, value); + break; + } + case 2: { + pci_write16(address->bus, address->device, address->function, offset, value); + break; + } + case 4: { + pci_write32(address->bus, address->device, address->function, offset, value); + break; + } + default: + return UACPI_STATUS_INVALID_ARGUMENT; + } + + return UACPI_STATUS_OK; +} + +uacpi_u64 uacpi_kernel_get_ticks(void) { + // uACPI expects ticks in hundreds of nanoseconds + return timespec_ns(timekeeper_timefromboot()) / 100; +} + +void uacpi_kernel_stall(uacpi_u8 usec) { + timespec_t start = timekeeper_timefromboot(); + while (timespec_diffus(start, timekeeper_timefromboot()) < usec); +} + +void uacpi_kernel_sleep(uacpi_u64 msec) { + sched_sleepus(msec * 1000); +} + +struct acpi_irqctx { + uacpi_interrupt_handler handler; + uacpi_handle ctx; +}; + +static void acpi_irq(isr_t *isr, context_t *ctx) { + struct acpi_irqctx *actx = isr->priv; + actx->handler(actx->ctx); +} + +uacpi_status uacpi_kernel_install_interrupt_handler( + uacpi_u32 irq, uacpi_interrupt_handler handler, uacpi_handle ctx, + uacpi_handle *outhandle +) { + struct acpi_irqctx *actx = alloc(sizeof(struct acpi_irqctx)); + __assert(actx); + + isr_t *isr = interrupt_allocate(acpi_irq, arch_apic_eoi, IPL_ACPI); + __assert(isr); + + actx->handler = handler; + actx->ctx = ctx; + isr->priv = actx; + + arch_ioapic_setirq(irq, isr->id & 0xff, _cpu()->id, false); + + *outhandle = isr; + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_kernel_uninstall_interrupt_handler( + uacpi_interrupt_handler handler, uacpi_handle handle) { + (void)handler; (void)handle; + + // We currently don't have an API to unregister interrupts + printf("acpi: interrupt handler uninstallation is a TODO\n"); + return UACPI_STATUS_UNIMPLEMENTED; +} + +struct acpi_work { + uacpi_work_handler handler; + uacpi_handle ctx; + struct acpi_work *next; +}; + +struct acpi_workctx { + semaphore_t sem; + spinlock_t queuelock; + thread_t *thread; + struct acpi_work *head; +}; + +static struct acpi_workctx gpework; +static struct acpi_workctx notifywork; + +static void acpi_initwork(struct acpi_workctx *ctx, void (*proc)(void)) { + SEMAPHORE_INIT(&ctx->sem, 0); + SPINLOCK_INIT(ctx->queuelock); + + ctx->thread = sched_newthread(proc, PAGE_SIZE * 4, 0, NULL, NULL); + __assert(ctx->thread); + sched_queue(ctx->thread); +} + +static void acpi_dowork(struct acpi_workctx *ctx) { + for (;;) { + semaphore_wait(&ctx->sem, true); + + struct acpi_work *work = NULL; + + bool irqstate = spinlock_acquireirqclear(&ctx->queuelock); + if (ctx->head) { + work = ctx->head; + ctx->head = work->next; + } + spinlock_releaseirqrestore(&ctx->queuelock, irqstate); + + if (work == NULL) + continue; + + work->handler(work->ctx); + free(work); + } +} + +static void acpi_dogpework() { + sched_targetcpu(_bsp()); + sched_yield(); + + acpi_dowork(&gpework); +} + +static void acpi_donotifywork() { + acpi_dowork(¬ifywork); +} + +uacpi_status uacpi_kernel_initialize(uacpi_init_level lvl) { + if (lvl != UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED) + return UACPI_STATUS_OK; + + acpi_initwork(&gpework, acpi_dogpework); + acpi_initwork(¬ifywork, acpi_donotifywork); + + return UACPI_STATUS_OK; +} + +void uacpi_kernel_deinitialize() { } + +uacpi_status uacpi_kernel_schedule_work( + uacpi_work_type type, uacpi_work_handler handler, uacpi_handle ctx) { + struct acpi_work *work; + struct acpi_workctx *workctx; + + work = alloc(sizeof(*work)); + if (work == NULL) + return UACPI_STATUS_OUT_OF_MEMORY; + + work->ctx = ctx; + work->handler = handler; + + switch (type) { + case UACPI_WORK_GPE_EXECUTION: + workctx = &gpework; + break; + default: + workctx = ¬ifywork; + break; + } + + bool irqstate = spinlock_acquireirqclear(&workctx->queuelock); + work->next = workctx->head; + workctx->head = work; + spinlock_releaseirqrestore(&workctx->queuelock, irqstate); + + semaphore_signal(&workctx->sem); + return UACPI_STATUS_OK; +} + +static void work_await(struct acpi_workctx *ctx) { + for (;;) { + bool empty; + + bool irqstate = spinlock_acquireirqclear(&ctx->queuelock); + empty = ctx->head == NULL; + spinlock_releaseirqrestore(&ctx->queuelock, irqstate); + + if (empty) + return; + + sched_sleepus(100 * 1000); + } +} + +uacpi_status uacpi_kernel_wait_for_work_completion(void) { + work_await(&gpework); + work_await(¬ifywork); + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_kernel_handle_firmware_request( + uacpi_firmware_request *req) { + switch(req->type) { + case UACPI_FIRMWARE_REQUEST_TYPE_BREAKPOINT: + break; + case UACPI_FIRMWARE_REQUEST_TYPE_FATAL: + printf("acpi: fatal firmware error: type=%d code=%d arg=%d\n", + req->fatal.type, req->fatal.code, req->fatal.arg) + break; + } + + return UACPI_STATUS_OK; +} + +uacpi_handle uacpi_kernel_create_mutex(void) { + mutex_t *mut = alloc(sizeof(mutex_t)); + if (mut == NULL) + return mut; + + MUTEX_INIT(mut); + return mut; +} + +void uacpi_kernel_free_mutex(uacpi_handle mut) { + uacpi_kernel_free(mut); +} + +uacpi_bool uacpi_kernel_acquire_mutex( + uacpi_handle mut, uacpi_u16 timeout) { + if (timeout == 0xFFFF) { + MUTEX_ACQUIRE(mut, true); + return UACPI_TRUE; + } + + return MUTEX_ACQUIRE_TIMED(mut, timeout * 1000, true); +} + +void uacpi_kernel_release_mutex(uacpi_handle mut) { + MUTEX_RELEASE(mut); +} + +uacpi_handle uacpi_kernel_create_event(void) { + semaphore_t *sem = alloc(sizeof(semaphore_t)); + if (sem == NULL) + return sem; + + SEMAPHORE_INIT(sem, 1); + return sem; +} + +void uacpi_kernel_free_event(uacpi_handle sem) { + uacpi_kernel_free(sem); +} + +uacpi_bool uacpi_kernel_wait_for_event( + uacpi_handle sem, uacpi_u16 timeout) { + + if (timeout == 0xFFFF) { + semaphore_wait(sem, true); + return UACPI_TRUE; + } + + return semaphore_timedwait(sem, timeout * 1000, true); +} + +void uacpi_kernel_signal_event(uacpi_handle sem) { + semaphore_signal(sem); +} + +void uacpi_kernel_reset_event(uacpi_handle sem) { + semaphore_reset(sem); +} + +uacpi_thread_id uacpi_kernel_get_thread_id(void) { + return _cpu()->thread; +} + +uacpi_handle uacpi_kernel_create_spinlock(void) { + spinlock_t *lock = alloc(sizeof(spinlock_t)); + if (lock == NULL) + return lock; + + SPINLOCK_INIT(*lock); + return lock; +} + +void uacpi_kernel_free_spinlock(uacpi_handle lock) { + uacpi_kernel_free(lock); +} + +uacpi_cpu_flags uacpi_kernel_spinlock_lock(uacpi_handle lock) { + return spinlock_acquireirqclear(lock); +} + +void uacpi_kernel_spinlock_unlock(uacpi_handle lock, uacpi_cpu_flags intstate) { + spinlock_releaseirqrestore(lock, intstate); +} diff --git a/kernel-src/io/acpi/uacpi b/kernel-src/io/acpi/uacpi new file mode 160000 index 00000000..c137b78d --- /dev/null +++ b/kernel-src/io/acpi/uacpi @@ -0,0 +1 @@ +Subproject commit c137b78de82c8e71ce6bad5582362bf40b2126d5