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