summaryrefslogtreecommitdiff
path: root/src/acpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/acpi.c')
-rw-r--r--src/acpi.c89
1 files changed, 89 insertions, 0 deletions
diff --git a/src/acpi.c b/src/acpi.c
new file mode 100644
index 0000000..5cee000
--- /dev/null
+++ b/src/acpi.c
@@ -0,0 +1,89 @@
+#include "aplos.h"
+
+enum {
+ SDT_HEADER_SIZE = 36,
+ SDT_LENGTH_OFFSET = 4,
+
+ RSDT_VERSION_OFFSET = 15,
+ RSDT_VENDOR_OFFSET = 9,
+ RSDT_VENDOR_SIZE = 6,
+ RSDT_RSDP_OFFSET = 16
+};
+
+static uint8_t *rsdp;
+static uint8_t *rsdt;
+static uint8_t *madt;
+
+static void parse_rsdt(uint64_t);
+static void parse_madt(struct ioapic_info *);
+static uint8_t *map_sdt(uint64_t);
+
+void
+setup_acpi(uint64_t rsdp_physical, struct ioapic_info *ioapic)
+{
+ rsdp = (uint8_t *)map_page(rsdp_physical, 1, true);
+ uint8_t version = rsdp[RSDT_VERSION_OFFSET];
+ char8_t vendor[RSDT_VENDOR_SIZE+1];
+ memcpy(vendor, rsdp+RSDT_VENDOR_OFFSET, RSDT_VENDOR_SIZE);
+ vendor[RSDT_VENDOR_SIZE] = 0;
+
+ switch(version){
+ case 0:
+ parse_rsdt(read_uint32_le(rsdp+RSDT_RSDP_OFFSET));
+ break;
+ default:
+ print(u8"Unknown RSDP version :(\n");
+ panic();
+ }
+
+ parse_madt(ioapic);
+}
+
+void
+parse_rsdt(uint64_t rsdt_physical)
+{
+ rsdt = map_sdt(rsdt_physical);
+ uint32_t length = read_uint32_le(rsdt+SDT_LENGTH_OFFSET);
+ uint32_t entries = (length - SDT_HEADER_SIZE) / 4;
+
+ uint32_t *ptrs = (uint32_t *)(((uint64_t)rsdt)+SDT_HEADER_SIZE);
+ for(uint32_t i = 0; i < entries; i++){
+ uint8_t *ptr = map_sdt(ptrs[i]);
+ if(utf8_cmp_n(ptr, u8"APIC", 4) == 0)
+ madt = ptr;
+ }
+}
+
+void
+parse_madt(struct ioapic_info *ioapic)
+{
+ assert(madt != NULL);
+
+ uint32_t length = read_uint32_le(madt+SDT_LENGTH_OFFSET);
+ uint32_t offset = SDT_HEADER_SIZE + 8;
+ while(offset < length){
+ uint8_t type = madt[offset];
+ uint8_t record_length = madt[offset+1];
+ switch(type){
+ case 1:
+ /* I/O APIC */
+ ioapic->base = read_uint32_le(madt+offset+4);
+ ioapic->interrupt_base = read_uint32_le(madt+offset+8);
+ break;
+ }
+ offset += record_length;
+ }
+}
+
+static uint8_t *
+map_sdt(uint64_t sdt_physical)
+{
+ uint8_t *p = (uint8_t *)map_page(sdt_physical, 1, true);
+ uint32_t length = read_uint32_le(p+4);
+ uint32_t pages = (length + PAGE_SIZE - 1) / PAGE_SIZE;
+ if(pages > 1){
+ /* Remap the thing */
+ p = (uint8_t *)map_page(sdt_physical, pages, true);
+ }
+ return p;
+}