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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
#include <panic.h>
#include <string.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <sys.h>
#include "acpi.h"
#include "boot/tag.h"
#include "print.h"
static struct RootSystemDescriptionTable *rsdt;
static struct FixedACPIDescriptionTable *fadt;
static uint16_t SLP_TYPa;
static uint16_t SLP_TYPb;
static uint16_t SLP_EN;
static uint16_t SCI_EN;
static bool is_init = false;
bool checksum(uint8_t *data, size_t len) {
unsigned char sum = 0;
for (size_t i = 0; i < len; i++)
sum += data[i];
return sum == 0;
}
static void *find_fadt(void) {
int entries = (rsdt->header.length - sizeof(rsdt->header)) / 4;
for (int i = 0; i < entries; i++) {
uintptr_t sdt_ptr = rsdt->sdt_table[i];
struct SystemDescriptionTableHeader *h = (void*) sdt_ptr;
if (!strncmp(h->signature, "FACP", 4))
return (void *) h;
}
return NULL;
}
static int read_s5_addr(void) {
uintptr_t ptr = fadt->dsdt;
char *s5_addr = (void*) (ptr + 36);
int dsdt_len = *((int*) (ptr+1)) - 36;
while (0 < dsdt_len--) {
if ( memcmp(s5_addr, "_S5_", 4) == 0)
break;
s5_addr++;
}
if (dsdt_len > 0) {
// check for valid AML structure
if ( ( *(s5_addr-1) == 0x08 || ( *(s5_addr-2) == 0x08 && *(s5_addr-1) == '\\') ) && *(s5_addr+4) == 0x12 ) {
s5_addr += 5;
s5_addr += ((*s5_addr &0xC0)>>6) +2; // calculate PkgLength size
if (*s5_addr == 0x0A)
s5_addr++; // skip byteprefix
SLP_TYPa = *(s5_addr)<<10;
s5_addr++;
if (*s5_addr == 0x0A)
s5_addr++; // skip byteprefix
SLP_TYPb = *(s5_addr)<<10;
SLP_EN = 1<<13;
SCI_EN = 1;
} else {
errork("\\_S5 parse error.");
return 1;
}
} else {
errork("\\_S5 not present.");
return 1;
}
return 0;
}
void acpi_init(void) {
is_init = false;
debugk("Loading ACPI");
struct BootTag *tag;
if(!get_boot_tag(ID_RSDP, &tag)) {
errork("Could not find RSDP");
return;
}
debugk("Loading RSDT");
struct RootSystemDescriptionPointer *rsdp = tag->data.rsdp;
if (!checksum((uint8_t*) rsdp, sizeof(struct RootSystemDescriptionPointer))) {
errork("RSDP checksum failed to validate");
return;
}
uintptr_t rsdt_ptr = rsdp->rsdt_address;
rsdt = (void *) rsdt_ptr;
if (!checksum((uint8_t*) &rsdt->header, rsdt->header.length)) {
errork("RSDT checksum failed to validate");
return;
}
debugk("Loading FADT");
fadt = find_fadt();
if (fadt == NULL) {
errork("Could not find FADT");
return;
}
if (!checksum((uint8_t*) &fadt->header, fadt->header.length)) {
errork("FADT checksum failed to validate");
return;
}
debugk("Reading \\_S5 Addr");
if (read_s5_addr()) {
return;
}
outb(fadt->smi_command_port,fadt->acpi_enable);
succek("ACPI has been loaded");
is_init = true;
}
void acpi_poweroff(void) {
if (is_init) {
outw((unsigned int) fadt->pm1_a_control_block, SLP_TYPb | SLP_EN);
panic("failed to shutdown");
} else {
errork("Cannot shutdown, ACPI not loaded");
}
}
|