AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
acpi.c
Go to the documentation of this file.
1
14
15#include <core/system.h>
16#include <core/string.h>
17
18#include <cpu/cpu.h>
19#include <cpu/io.h>
20#include <cpu/sdt.h>
21
22#include <mm/mm.h>
23#include <mm/vm.h>
24#include <mm/kvmem.h>
25
26#include <platform/misc.h>
27
28MALLOC_DEFINE(M_ACPI, "acpi", "ACPI table mapping");
29
30/* where the tables are mapped */
31#define ACPI_WINDOW ARCH_ACPI_BASE
32#define ACPI_WINDOW_SIZE 0x00400000UL
33
35
36/* the offsets of the FADT's fields used here */
37#define FADT_DSDT 40
38#define FADT_SMI_CMD 48
39#define FADT_ACPI_ENABLE 52
40#define FADT_PM1A_CNT 64
41#define FADT_PM1B_CNT 68
42
43#define PM1_SCI_EN 0x0001
44#define PM1_SLP_EN 0x2000
45
46/* AML opcodes */
47#define AML_NAME 0x08
48#define AML_BYTE_PREFIX 0x0A
49#define AML_PACKAGE 0x12
50
52static int checksum(const void *p, size_t size)
53{
54 uint8_t sum = 0;
55
56 for (size_t i = 0; i < size; ++i)
57 sum += ((const uint8_t *) p)[i];
58
59 return sum;
60}
61
65static void *acpi_map(uintptr_t paddr, size_t size)
66{
67 uintptr_t page = PAGE_ALIGN(paddr);
68 size_t len = PAGE_ROUND(paddr + size) - page;
69
71 return NULL;
72
73 struct vm_entry *entry = kmalloc(sizeof(*entry), &M_ACPI, M_ZERO);
74 if (!entry) return NULL;
75
76 entry->base = window_next;
77 entry->paddr = page;
78 entry->size = len;
79 entry->flags = VM_KR;
80
81 if (vm_map(&kvm_space, entry)) {
82 kfree(entry);
83 return NULL;
84 }
85
86 window_next += len;
87 return (void *) (entry->base + (paddr - page));
88}
89
93{
94 struct acpi_sdt_header *hdr = acpi_map(paddr, sizeof(*hdr));
95
96 if (!hdr || hdr->length < sizeof(*hdr) || hdr->length > ACPI_WINDOW_SIZE / 2)
97 return NULL;
98
99 hdr = acpi_map(paddr, hdr->length);
100
101 return hdr && !checksum(hdr, hdr->length) ? hdr : NULL;
102}
103
106static struct acpi_rsdp *acpi_rsdp(void)
107{
108 uintptr_t ebda = (uintptr_t) *(uint16_t *) VMA(0x40E) << 4;
109 uintptr_t areas[][2] = {{ebda, ebda + 0x400}, {0xE0000, 0x100000}};
110
111 for (int i = 0; i < 2; ++i) {
112 for (uintptr_t p = areas[i][0]; p + sizeof(struct acpi_rsdp) <= areas[i][1]; p += 16) {
113 struct acpi_rsdp *rsdp = (struct acpi_rsdp *) VMA(p);
114
115 if (!memcmp(rsdp->signature, "RSD PTR ", 8) && !checksum(rsdp, sizeof(*rsdp)))
116 return rsdp;
117 }
118 }
119
120 return NULL;
121}
122
123#define ACPI_TABLES_MAX 32
124
126static int tables_nr = -1;
127
130static void acpi_tables(void)
131{
132 struct acpi_rsdp *rsdp = acpi_rsdp();
133 struct acpi_rsdp2 *rsdp2 = (struct acpi_rsdp2 *) rsdp;
134
135 tables_nr = 0;
136
137 if (!rsdp) {
138 printk("acpi: no RSDP\n");
139 return;
140 }
141
142 /* the XSDT, if there's one and its address can be reached */
143 if (rsdp->rev >= 2 && !checksum(rsdp2, sizeof(*rsdp2)) && rsdp2->xsdt &&
144 rsdp2->xsdt < ((uint64_t) 1 << (sizeof(uintptr_t) * 8 - 1))) {
145 struct acpi_xsdt *xsdt = (struct acpi_xsdt *) acpi_table((uintptr_t) rsdp2->xsdt);
146
147 if (xsdt) {
148 size_t n = (xsdt->header.length - sizeof(xsdt->header)) / sizeof(uint64_t);
149
150 for (size_t i = 0; i < n && tables_nr < ACPI_TABLES_MAX; ++i) {
151 struct acpi_sdt_header *hdr = acpi_table((uintptr_t) xsdt->sdt[i]);
152
153 if (hdr)
154 tables[tables_nr++] = hdr;
155 }
156
157 return;
158 }
159 }
160
161 struct acpi_rsdt *rsdt = (struct acpi_rsdt *) acpi_table(rsdp->rsdt);
162
163 if (!rsdt)
164 return;
165
166 size_t n = (rsdt->header.length - sizeof(rsdt->header)) / sizeof(uint32_t);
167
168 for (size_t i = 0; i < n && tables_nr < ACPI_TABLES_MAX; ++i) {
169 struct acpi_sdt_header *hdr = acpi_table(rsdt->sdt[i]);
170
171 if (hdr)
172 tables[tables_nr++] = hdr;
173 }
174}
175
183void *x86_acpi_table(const char *signature)
184{
185 if (tables_nr < 0)
186 acpi_tables();
187
188 for (int i = 0; i < tables_nr; ++i)
189 if (!memcmp(tables[i]->signature, signature, 4))
190 return tables[i];
191
192 return NULL;
193}
194
197static int aml_int(const uint8_t **p, const uint8_t *end)
198{
199 if (*p >= end)
200 return -1;
201
202 if (**p == AML_BYTE_PREFIX && *p + 1 < end) {
203 *p += 2;
204 return (*p)[-1];
205 }
206
207 if (**p <= 1)
208 return *(*p)++;
209
210 return -1;
211}
212
218static int acpi_s5(struct acpi_sdt_header *dsdt, int *typb)
219{
220 const uint8_t *aml = (const uint8_t *) dsdt;
221 const uint8_t *end = aml + dsdt->length;
222
223 for (const uint8_t *p = aml + sizeof(*dsdt); p + 8 < end; ++p) {
224 if (memcmp(p, "_S5_", 4))
225 continue;
226
227 /* a Name of a package, \_S5_ or _S5_ */
228 if (!(p[-1] == AML_NAME || (p[-1] == '\\' && p[-2] == AML_NAME)) || p[4] != AML_PACKAGE)
229 continue;
230
231 /* skip the package's length (1 to 4 bytes) and its element count
232 * (1 byte) */
233 const uint8_t *q = p + 5;
234 q += ((*q >> 6) & 3) + 1;
235 q++;
236
237 int typa = aml_int(&q, end);
238 *typb = aml_int(&q, end);
239
240 if (typa >= 0)
241 return typa;
242 }
243
244 return -1;
245}
246
248static void pm1_write(uint32_t port, uint16_t val)
249{
250 struct ioaddr io = {.addr = port, .type = IOADDR_PORT};
251 io_out16(&io, 0, val);
252}
253
256{
257 struct ioaddr io = {.addr = port, .type = IOADDR_PORT};
258 return io_in16(&io, 0);
259}
260
266{
267 uint8_t *fadt = x86_acpi_table("FACP");
268
269 if (!fadt) {
270 printk("acpi: no FADT\n");
271 return;
272 }
273
274 struct acpi_sdt_header *dsdt = acpi_table(*(uint32_t *) (fadt + FADT_DSDT));
275 uint32_t smi_cmd = *(uint32_t *) (fadt + FADT_SMI_CMD);
276 uint8_t enable = fadt[FADT_ACPI_ENABLE];
277 uint32_t pm1a_cnt = *(uint32_t *) (fadt + FADT_PM1A_CNT);
278 uint32_t pm1b_cnt = *(uint32_t *) (fadt + FADT_PM1B_CNT);
279 int typa, typb = -1;
280
281 if (!dsdt || !pm1a_cnt || (typa = acpi_s5(dsdt, &typb)) < 0) {
282 printk("acpi: no S5 sleep state\n");
283 return;
284 }
285
286 /* switch the firmware to ACPI mode, if it isn't in it */
287 if (!(pm1_read(pm1a_cnt) & PM1_SCI_EN) && smi_cmd && enable) {
288 struct ioaddr io = {.addr = smi_cmd, .type = IOADDR_PORT};
289 io_out8(&io, 0, enable);
290
291 for (int i = 0; i < 1000000 && !(pm1_read(pm1a_cnt) & PM1_SCI_EN); ++i)
292 ;
293 }
294
295 pm1_write(pm1a_cnt, typa << 10 | PM1_SLP_EN);
296
297 if (pm1b_cnt && typb >= 0)
298 pm1_write(pm1b_cnt, typb << 10 | PM1_SLP_EN);
299
300 /* the machine takes a moment to power off */
301 for (volatile int i = 0; i < 10000000; ++i)
302 ;
303
304 printk("acpi: S5 sleep state didn't power off\n");
305}
static struct acpi_sdt_header * acpi_table(uintptr_t paddr)
Maps a table, as long as its header says it is.
Definition acpi.c:92
static void pm1_write(uint32_t port, uint16_t val)
Writes a PM1 control register.
Definition acpi.c:248
#define FADT_PM1A_CNT
Definition acpi.c:40
static uint16_t pm1_read(uint32_t port)
Reads a PM1 control register.
Definition acpi.c:255
#define FADT_SMI_CMD
Definition acpi.c:38
#define AML_PACKAGE
Definition acpi.c:49
static void * acpi_map(uintptr_t paddr, size_t size)
Maps a range of physical memory, read-only, in the tables' window.
Definition acpi.c:65
static struct acpi_sdt_header * tables[ACPI_TABLES_MAX]
Definition acpi.c:125
static int aml_int(const uint8_t **p, const uint8_t *end)
Reads an integer element of an AML package: BytePrefix n, or ZeroOp or OneOp.
Definition acpi.c:197
static void acpi_tables(void)
Maps every table that the XSDT (or the RSDT) lists, and records them in tables.
Definition acpi.c:130
#define FADT_PM1B_CNT
Definition acpi.c:41
static int tables_nr
Definition acpi.c:126
static uintptr_t window_next
Definition acpi.c:34
#define FADT_DSDT
Definition acpi.c:37
#define FADT_ACPI_ENABLE
Definition acpi.c:39
#define PM1_SCI_EN
Definition acpi.c:43
#define ACPI_WINDOW
Definition acpi.c:31
#define ACPI_WINDOW_SIZE
Definition acpi.c:32
static int checksum(const void *p, size_t size)
Returns the 8-bit sum of size bytes, which is 0 for a valid table.
Definition acpi.c:52
#define PM1_SLP_EN
Definition acpi.c:44
static int acpi_s5(struct acpi_sdt_header *dsdt, int *typb)
Finds S5's sleep types in the DSDT: Name (_S5, Package () {a, b, ...}).
Definition acpi.c:218
#define AML_NAME
Definition acpi.c:47
#define AML_BYTE_PREFIX
Definition acpi.c:48
#define ACPI_TABLES_MAX
Definition acpi.c:123
static void io_out16(struct ioaddr *io, uintptr_t off, uint16_t val)
Definition io.h:49
static uint16_t io_in16(struct ioaddr *io, uintptr_t off)
Definition io.h:33
static void io_out8(struct ioaddr *io, uintptr_t off, uint8_t val)
Writes an 8-, 16- or 32-bit register.
Definition io.h:44
#define VMA(obj)
Definition mm.h:36
#define IOADDR_PORT
Definition io.h:89
#define PAGE_ALIGN(ptr)
Definition mm.h:9
#define PAGE_ROUND(ptr)
Definition mm.h:10
#define MALLOC_DEFINE(type, _name, _desc)
Definition kvmem.h:30
void * kmalloc(size_t size, struct malloc_type *type, int flags)
Allocates memory for one of the kernel's structures.
Definition kvmem.c:123
#define M_ZERO
Definition kvmem.h:26
void kfree(void *)
Frees an object that kmalloc() gave.
Definition kvmem.c:146
int memcmp(const void *a, const void *b, size_t n)
Compares n bytes.
Definition libc.c:51
void * x86_acpi_table(const char *signature)
Finds one of ACPI's tables, by its signature.
Definition acpi.c:183
void x86_acpi_poweroff(void)
Powers the machine off, by entering ACPI's S5 sleep state.
Definition acpi.c:265
int printk(const char *fmt,...)
Writes a kernel message, to the message buffer and the consoles.
Definition printk.c:249
#define NULL
Definition stddef.h:27
__SIZE_TYPE__ uintptr_t
Definition stddef.h:12
unsigned int uint32_t
Definition stddef.h:22
unsigned char uint8_t
Definition stddef.h:20
unsigned long long int uint64_t
Definition stddef.h:23
unsigned short int uint16_t
Definition stddef.h:21
ACPI 2.0's RSDP (revision 2 or more), which also says where the XSDT is, with 64-bit addresses.
Definition sdt.h:20
uint64_t xsdt
Definition sdt.h:23
The Root System Description Pointer, which says where the RSDT is.
Definition sdt.h:10
uint32_t rsdt
Definition sdt.h:15
char signature[8]
Definition sdt.h:11
uint8_t rev
Definition sdt.h:14
The RSDT: the other tables' 32-bit addresses.
Definition sdt.h:42
uint32_t sdt[0]
Definition sdt.h:44
struct acpi_sdt_header header
Definition sdt.h:43
The header that every table starts with.
Definition sdt.h:29
char signature[4]
Definition sdt.h:30
uint32_t length
Definition sdt.h:31
The XSDT: the other tables' 64-bit addresses.
Definition sdt.h:48
uint64_t sdt[0]
Definition sdt.h:50
struct acpi_sdt_header header
Definition sdt.h:49
Where a device's registers are.
Definition io.h:95
A mapping (map entry): a range of an address space, its permissions, and where its pages come from.
Definition vm.h:66
uint32_t flags
its permissions and flags (VM_*)
Definition vm.h:78
paddr_t paddr
physical memory to map all at once (vm_map()), or 0 for pages that come in on page faults
Definition vm.h:69
vaddr_t base
where the mapping starts in its address space
Definition vm.h:72
size_t size
its size
Definition vm.h:75
#define VM_KR
kernel read
Definition vm.h:38
int vm_map(struct vm_space *vm_space, struct vm_entry *vm_entry)
Maps a map entry's whole range at once: its physical memory (paddr), or new pages of memory if it has...
Definition vmm.c:29
struct vm_space kvm_space
Definition vmm.c:17