1 // Copyright 2020 The Fuchsia Authors
2 // Copyright 2021 Travis Geiselbrecht
3 //
4 // Use of this source code is governed by a MIT-style
5 // license that can be found in the LICENSE file or at
6 // https://opensource.org/licenses/MIT
7
8 #include <lib/acpi_lite.h>
9
10 #include <inttypes.h>
11 #include <string.h>
12 #include <lk/compiler.h>
13 #include <lk/debug.h>
14 #include <lk/err.h>
15 #include <lk/trace.h>
16 #include <kernel/vm.h>
17
18 #define LOCAL_TRACE 0
19
20 // global state of the acpi lite library
21 struct acpi_lite_state {
22 const acpi_rsdp* rsdp;
23 const acpi_rsdt_xsdt* sdt;
24 size_t num_tables; // number of top level tables
25 bool xsdt; // are the pointers 64 or 32bit?
26 } acpi;
27
28 // given a physical address, return a pointer to it from the VM
phys_to_ptr(uintptr_t pa)29 static const void* phys_to_ptr(uintptr_t pa) {
30 void *ptr = paddr_to_kvaddr(pa);
31 return ptr;
32 }
33
acpi_checksum(const void * _buf,size_t len)34 static uint8_t acpi_checksum(const void* _buf, size_t len) {
35 uint8_t c = 0;
36
37 const uint8_t* buf = static_cast<const uint8_t*>(_buf);
38 for (size_t i = 0; i < len; i++) {
39 c = (uint8_t)(c + buf[i]);
40 }
41
42 return c;
43 }
44
validate_rsdp(const acpi_rsdp * rsdp)45 static bool validate_rsdp(const acpi_rsdp* rsdp) {
46 // check the signature
47 if (memcmp(ACPI_RSDP_SIG, rsdp->sig, 8)) {
48 return false;
49 }
50
51 // validate the v1 checksum on the first 20 bytes of the table
52 uint8_t c = acpi_checksum(rsdp, 20);
53 if (c) {
54 return false;
55 }
56
57 // is it v2?
58 if (rsdp->revision >= 2) {
59 if (rsdp->length < 36 || rsdp->length > 4096) {
60 // keep the table length within reason
61 return false;
62 }
63
64 c = acpi_checksum(rsdp, rsdp->length);
65 if (c) {
66 return false;
67 }
68 }
69
70 // seems okay
71 return true;
72 }
73
find_rsdp_pc()74 static paddr_t find_rsdp_pc() {
75 // search for it in the BIOS EBDA area (0xe0000..0xfffff) on 16 byte boundaries
76 for (paddr_t ptr = 0xe0000; ptr <= 0xfffff; ptr += 16) {
77 const auto rsdp = static_cast<const acpi_rsdp*>(phys_to_ptr(ptr));
78
79 if (validate_rsdp(rsdp)) {
80 return ptr;
81 }
82 }
83
84 return 0;
85 }
86
validate_sdt(const acpi_rsdt_xsdt * sdt,size_t * num_tables,bool * xsdt)87 static bool validate_sdt(const acpi_rsdt_xsdt* sdt, size_t* num_tables, bool* xsdt) {
88 LTRACEF("%p\n", sdt);
89
90 // bad pointer
91 if (!sdt) {
92 return false;
93 }
94
95 // check the signature and see if it's a rsdt or xsdt
96 if (!memcmp(sdt->header.sig, "XSDT", 4)) {
97 *xsdt = true;
98 } else if (!memcmp(sdt->header.sig, "RSDT", 4)) {
99 *xsdt = false;
100 } else {
101 return false;
102 }
103
104 // is the length sane?
105 if (sdt->header.length < 36 || sdt->header.length > 4096) {
106 return false;
107 }
108
109 // is it a revision we understand?
110 if (sdt->header.revision != 1) {
111 return false;
112 }
113
114 // checksum the entire table
115 uint8_t c = acpi_checksum(sdt, sdt->header.length);
116 if (c) {
117 return false;
118 }
119
120 // compute the number of pointers to tables we have
121 *num_tables = (sdt->header.length - 36u) / (*xsdt ? 8u : 4u);
122
123 // looks okay
124 return true;
125 }
126
acpi_get_table_at_index(size_t index)127 const acpi_sdt_header* acpi_get_table_at_index(size_t index) {
128 if (index >= acpi.num_tables) {
129 return nullptr;
130 }
131
132 paddr_t pa;
133 if (acpi.xsdt) {
134 pa = acpi.sdt->addr64[index];
135 } else {
136 pa = acpi.sdt->addr32[index];
137 }
138
139 return static_cast<const acpi_sdt_header*>(phys_to_ptr(pa));
140 }
141
acpi_get_table_by_sig(const char * sig)142 const acpi_sdt_header* acpi_get_table_by_sig(const char* sig) {
143 // walk the list of tables
144 for (size_t i = 0; i < acpi.num_tables; i++) {
145 const auto header = acpi_get_table_at_index(i);
146 if (!header) {
147 continue;
148 }
149
150 if (!memcmp(sig, header->sig, 4)) {
151 // validate the checksum
152 uint8_t c = acpi_checksum(header, header->length);
153 if (c == 0) {
154 return header;
155 }
156 }
157 }
158
159 return nullptr;
160 }
161
acpi_lite_init(paddr_t rsdp_pa)162 status_t acpi_lite_init(paddr_t rsdp_pa) {
163 LTRACEF("passed in rsdp %#" PRIxPTR "\n", rsdp_pa);
164
165 // see if the rsdp pointer is valid
166 if (rsdp_pa == 0) {
167 // search around for it in a platform-specific way
168 #if ARCH_X86
169 rsdp_pa = find_rsdp_pc();
170 if (rsdp_pa == 0) {
171 dprintf(INFO, "ACPI LITE: couldn't find ACPI RSDP in BIOS area\n");
172 }
173 #endif
174
175 if (rsdp_pa == 0) {
176 return ERR_NOT_FOUND;
177 }
178 }
179
180 const void* ptr = phys_to_ptr(rsdp_pa);
181 if (!ptr) {
182 dprintf(INFO, "ACPI LITE: failed to translate RSDP address %#" PRIxPTR " to virtual\n",
183 rsdp_pa);
184 return ERR_NOT_FOUND;
185 }
186
187 // see if the RSDP is there
188 acpi.rsdp = static_cast<const acpi_rsdp*>(ptr);
189 if (!validate_rsdp(acpi.rsdp)) {
190 dprintf(INFO, "ACPI LITE: RSDP structure does not check out\n");
191 return ERR_NOT_FOUND;
192 }
193
194 dprintf(SPEW, "ACPI LITE: RSDP checks out, found at %#lx\n", rsdp_pa);
195
196 // find the pointer to either the RSDT or XSDT
197 acpi.sdt = nullptr;
198 if (acpi.rsdp->revision < 2) {
199 // v1 RSDP, pointing at a RSDT
200 acpi.sdt = static_cast<const acpi_rsdt_xsdt*>(phys_to_ptr(acpi.rsdp->rsdt_address));
201 } else {
202 // v2+ RSDP, pointing at a XSDT
203 // try to use the 64bit address first
204 acpi.sdt = static_cast<const acpi_rsdt_xsdt*>(phys_to_ptr(acpi.rsdp->xsdt_address));
205 if (!acpi.sdt) {
206 acpi.sdt = static_cast<const acpi_rsdt_xsdt*>(phys_to_ptr(acpi.rsdp->rsdt_address));
207 }
208 }
209
210 if (!validate_sdt(acpi.sdt, &acpi.num_tables, &acpi.xsdt)) {
211 dprintf(INFO, "ACPI LITE: RSDT/XSDT structure does not check out\n");
212 return ERR_NOT_FOUND;
213 }
214
215 dprintf(SPEW, "ACPI LITE: RSDT/XSDT checks out, %zu tables\n", acpi.num_tables);
216
217 if (LOCAL_TRACE) {
218 acpi_lite_dump_tables();
219 }
220
221 return NO_ERROR;
222 }
223
acpi_lite_dump_tables()224 void acpi_lite_dump_tables() {
225 if (!acpi.sdt) {
226 return;
227 }
228
229 printf("root table:\n");
230 hexdump(acpi.sdt, acpi.sdt->header.length);
231
232 // walk the table list
233 for (size_t i = 0; i < acpi.num_tables; i++) {
234 const auto header = acpi_get_table_at_index(i);
235 if (!header) {
236 continue;
237 }
238
239 printf("table %zu: '%c%c%c%c' len %u\n", i, header->sig[0], header->sig[1], header->sig[2],
240 header->sig[3], header->length);
241 hexdump(header, header->length);
242 }
243 }
244
acpi_process_madt_entries_etc(const uint8_t search_type,const madt_entry_callback callback)245 status_t acpi_process_madt_entries_etc(const uint8_t search_type, const madt_entry_callback callback) {
246 const acpi_madt_table* madt =
247 reinterpret_cast<const acpi_madt_table*>(acpi_get_table_by_sig(ACPI_MADT_SIG));
248 if (!madt) {
249 return ERR_NOT_FOUND;
250 }
251
252 // bytewise array of the same table
253 const uint8_t* madt_array = reinterpret_cast<const uint8_t*>(madt);
254
255 // walk the table off the end of the header, looking for the requested type
256 size_t off = sizeof(*madt);
257 while (off < madt->header.length) {
258 uint8_t type = madt_array[off];
259 uint8_t length = madt_array[off + 1];
260
261 if (type == search_type) {
262 callback(static_cast<const void*>(&madt_array[off]), length);
263 }
264
265 off += length;
266 }
267
268 return NO_ERROR;
269 }
270