summaryrefslogtreecommitdiff
path: root/src/acpi.c
blob: 5cee000586ad9519e0501a6c9df10ca66afd78f9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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;
}