:: limine / common / lib / acpi.c 14.1 KB raw

1
#include <stddef.h>
2
#include <stdint.h>
3
#include <stdbool.h>
4
#include <lib/acpi.h>
5
#include <lib/misc.h>
6
#include <lib/libc.h>
7
#include <lib/print.h>
8
#include <mm/pmm.h>
9
10
// Following function based on https://github.com/managarm/lai/blob/master/helpers/pc-bios.c's function lai_bios_calc_checksum()
11
uint8_t acpi_checksum(void *ptr, size_t size) {
12
    uint8_t sum = 0, *_ptr = ptr;
13
    for (size_t i = 0; i < size; i++)
14
        sum += _ptr[i];
15
    return sum;
16
}
17
18
#if defined (BIOS)
19
20
void *acpi_get_rsdp(void) {
21
    size_t ebda = EBDA;
22
23
    for (size_t i = ebda; i < 0x100000; i += 16) {
24
        if (i == ebda + 1024) {
25
            // We probed the 1st KiB of the EBDA as per spec, move onto 0xe0000
26
            i = 0xe0000;
27
        }
28
        if (!memcmp((char *)i, "RSD PTR ", 8)
29
         && !acpi_checksum((void *)i, 20)) {
30
            printv("acpi: Found RSDP at %p\n", i);
31
            return (void *)i;
32
        }
33
    }
34
35
    return NULL;
36
}
37
38
/// Returns the RSDP v1 pointer if available or else NULL.
39
void *acpi_get_rsdp_v1(void) {
40
    // In BIOS according to the ACPI spec (see ACPI 6.2 section
41
    // 5.2.5.1 'Finding the RSDP on IA-PC Systems') it either contains
42
    // the RSDP or the XSDP and it cannot contain both. So, we directly
43
    // use acpi_get_rsdp function to find the RSDP and if it has the correct
44
    // revision, return it.
45
    struct rsdp *rsdp = acpi_get_rsdp();
46
47
    if (rsdp != NULL && rsdp->rev < 2)
48
        return rsdp;
49
50
    return NULL;
51
}
52
53
void acpi_get_smbios(void **smbios32, void **smbios64) {
54
    *smbios32 = NULL;
55
    *smbios64 = NULL;
56
57
    for (size_t i = 0xf0000; i < 0x100000; i += 16) {
58
        struct smbios_entry_point_32 *ptr = (struct smbios_entry_point_32 *)i;
59
60
        if (!memcmp(ptr->anchor_str, "_SM_", 4) &&
61
            !acpi_checksum((void *)ptr, ptr->length)) {
62
            printv("acpi: Found SMBIOS 32-bit entry point at %p\n", i);
63
            *smbios32 = (void *)ptr;
64
            break;
65
        }
66
    }
67
68
    for (size_t i = 0xf0000; i < 0x100000; i += 16) {
69
        struct smbios_entry_point_64 *ptr = (struct smbios_entry_point_64 *)i;
70
71
        if (!memcmp(ptr->anchor_str, "_SM3_", 5) &&
72
            !acpi_checksum((void *)ptr, ptr->length)) {
73
            printv("acpi: Found SMBIOS 64-bit entry point at %p\n", i);
74
            *smbios64 = (void *)ptr;
75
            break;
76
        }
77
    }
78
}
79
80
#endif
81
82
#if defined (UEFI)
83
84
#include <efi.h>
85
86
void *acpi_get_rsdp(void) {
87
    EFI_GUID acpi_2_guid = ACPI_20_TABLE_GUID;
88
    EFI_GUID acpi_1_guid = ACPI_TABLE_GUID;
89
90
    void *rsdp = NULL;
91
92
    for (size_t i = 0; i < gST->NumberOfTableEntries; i++) {
93
        EFI_CONFIGURATION_TABLE *cur_table = &gST->ConfigurationTable[i];
94
95
        bool is_xsdp = memcmp(&cur_table->VendorGuid, &acpi_2_guid, sizeof(EFI_GUID)) == 0;
96
        bool is_rsdp = memcmp(&cur_table->VendorGuid, &acpi_1_guid, sizeof(EFI_GUID)) == 0;
97
98
        if (!is_xsdp && !is_rsdp)
99
            continue;
100
101
        if ((is_xsdp && acpi_checksum(cur_table->VendorTable, sizeof(struct rsdp)) != 0) || // XSDP is 36 bytes wide
102
            (is_rsdp && acpi_checksum(cur_table->VendorTable, 20) != 0)) // RSDP is 20 bytes wide
103
            continue;
104
105
        printv("acpi: Found %s at %p\n", is_xsdp ? "XSDP" : "RSDP", cur_table->VendorTable);
106
107
        // We want to return the XSDP if it exists rather then returning
108
        // the RSDP. We need to add a check for that since the table entries
109
        // are not in the same order for all EFI systems since it might be the
110
        // case where the RSDP occurs before the XSDP.
111
        if (is_xsdp) {
112
            rsdp = (void *)cur_table->VendorTable;
113
            break; // Found it!.
114
        } else {
115
            // Found the RSDP but we continue to loop since we might
116
            // find the XSDP.
117
            rsdp = (void *)cur_table->VendorTable;
118
        }
119
    }
120
121
    return rsdp;
122
}
123
124
/// Returns the RSDP v1 pointer if available or else NULL.
125
void *acpi_get_rsdp_v1(void) {
126
    // To maintain GRUB compatibility we will need to probe for the RSDP
127
    // again since UEFI can contain both XSDP and RSDP (see ACPI 6.2 section
128
    // 5.2.5.2 'Finding the RSDP on UEFI Enabled Systems') and in the acpi_get_rsdp
129
    // function we look for the RSDP with the latest revision.
130
    EFI_GUID acpi_1_guid = ACPI_TABLE_GUID;
131
132
    for (size_t i = 0; i < gST->NumberOfTableEntries; i++) {
133
        EFI_CONFIGURATION_TABLE *cur_table = &gST->ConfigurationTable[i];
134
135
        if (memcmp(&cur_table->VendorGuid, &acpi_1_guid, sizeof(EFI_GUID)) != 0)
136
            continue;
137
138
        if (acpi_checksum(cur_table->VendorTable, 20) != 0)
139
            continue;
140
141
        return (void *)cur_table->VendorTable;
142
    }
143
144
    return NULL;
145
}
146
147
void acpi_get_smbios(void **smbios32, void **smbios64) {
148
    *smbios32 = NULL;
149
    *smbios64 = NULL;
150
151
    for (size_t i = 0; i < gST->NumberOfTableEntries; i++) {
152
        EFI_CONFIGURATION_TABLE *cur_table = &gST->ConfigurationTable[i];
153
        EFI_GUID smbios_guid = SMBIOS_TABLE_GUID;
154
155
        if (memcmp(&cur_table->VendorGuid, &smbios_guid, sizeof(EFI_GUID)) != 0)
156
            continue;
157
158
        struct smbios_entry_point_32 *ptr = (struct smbios_entry_point_32 *)cur_table->VendorTable;
159
160
        if (acpi_checksum((void *)ptr, ptr->length) != 0)
161
            continue;
162
163
        printv("acpi: Found SMBIOS 32-bit entry point at %p\n", ptr);
164
165
        *smbios32 = (void *)ptr;
166
167
        break;
168
    }
169
170
    for (size_t i = 0; i < gST->NumberOfTableEntries; i++) {
171
        EFI_CONFIGURATION_TABLE *cur_table = &gST->ConfigurationTable[i];
172
        EFI_GUID smbios3_guid = SMBIOS3_TABLE_GUID;
173
174
        if (memcmp(&cur_table->VendorGuid, &smbios3_guid, sizeof(EFI_GUID)) != 0)
175
            continue;
176
177
        struct smbios_entry_point_64 *ptr = (struct smbios_entry_point_64 *)cur_table->VendorTable;
178
179
        if (acpi_checksum((void *)ptr, ptr->length) != 0)
180
            continue;
181
182
        printv("acpi: Found SMBIOS 64-bit entry point at %p\n", ptr);
183
184
        *smbios64 = (void *)ptr;
185
186
        break;
187
    }
188
}
189
190
#endif
191
192
/// Returns the RSDP v2 pointer if available or else NULL.
193
void *acpi_get_rsdp_v2(void) {
194
    // Since the acpi_get_rsdp function already looks for the XSDP we can
195
    // just check if it has the correct revision and return the pointer :^)
196
    struct rsdp *rsdp = acpi_get_rsdp();
197
198
    if (rsdp != NULL && rsdp->rev >= 2)
199
        return rsdp;
200
201
    return NULL;
202
}
203
204
void *acpi_get_table(const char *signature, int index) {
205
    int cnt = 0;
206
207
    struct rsdp *rsdp = acpi_get_rsdp();
208
    if (rsdp == NULL)
209
        return NULL;
210
211
    bool use_xsdt = false;
212
    if (rsdp->rev >= 2 && rsdp->xsdt_addr
213
     && (sizeof(uintptr_t) >= 8 || rsdp->xsdt_addr <= UINT32_MAX))
214
        use_xsdt = true;
215
216
    struct rsdt *rsdt;
217
    if (use_xsdt)
218
        rsdt = (struct rsdt *)(uintptr_t)rsdp->xsdt_addr;
219
    else
220
        rsdt = (struct rsdt *)(uintptr_t)rsdp->rsdt_addr;
221
222
    if (rsdt == NULL) {
223
        return NULL;
224
    }
225
226
    // Validate RSDT/XSDT header length
227
    if (rsdt->header.length < sizeof(struct sdt)) {
228
        printv("acpi: Invalid %s header length\n", use_xsdt ? "XSDT" : "RSDT");
229
        return NULL;
230
    }
231
232
    size_t entry_size = use_xsdt ? 8 : 4;
233
    size_t entry_count = (rsdt->header.length - sizeof(struct sdt)) / entry_size;
234
235
    for (size_t i = 0; i < entry_count; i++) {
236
        struct sdt *ptr;
237
        if (use_xsdt)
238
            ptr = (struct sdt *)(uintptr_t)((uint64_t *)rsdt->ptrs_start)[i];
239
        else
240
            ptr = (struct sdt *)(uintptr_t)((uint32_t *)rsdt->ptrs_start)[i];
241
242
        if (ptr == NULL) {
243
            continue;
244
        }
245
246
        if (!memcmp(ptr->signature, signature, 4)
247
         && !acpi_checksum(ptr, ptr->length)
248
         && cnt++ == index) {
249
            printv("acpi: Found \"%s\" at %p\n", signature, ptr);
250
            return ptr;
251
        }
252
    }
253
254
    printv("acpi: \"%s\" not found\n", signature);
255
    return NULL;
256
}
257
258
static bool acpi_padding_is_safe(uint64_t base, uint64_t length) {
259
    if (length == 0) {
260
        return true;
261
    }
262
263
    uint64_t top = CHECKED_ADD(base, length, return false);
264
265
    for (size_t i = 0; i < memmap_entries; i++) {
266
        uint64_t entry_base = memmap[i].base;
267
        uint64_t entry_top  = CHECKED_ADD(entry_base, memmap[i].length, continue);
268
269
        if (entry_base >= top || entry_top <= base) {
270
            continue;
271
        }
272
273
        if (memmap[i].type != MEMMAP_USABLE && memmap[i].type != MEMMAP_RESERVED) {
274
            return false;
275
        }
276
    }
277
278
    return true;
279
}
280
281
static void map_single_table(uint64_t addr, uint32_t len) {
282
#if defined (__i386__)
283
    if (addr >= 0x100000000) {
284
        print("acpi: warning: Cannot get length of ACPI table above 4GiB\n");
285
        return;
286
    }
287
#endif
288
289
    uint32_t length = len != (uint32_t)-1 ? len : *(uint32_t *)(uintptr_t)(addr + 4);
290
291
    uint64_t aligned_base = ALIGN_DOWN(addr, 4096);
292
    uint64_t aligned_top  = ALIGN_UP(addr + length, 4096, panic(false, "acpi: Alignment overflow"));
293
294
    if (!acpi_padding_is_safe(aligned_base, addr - aligned_base)) {
295
        aligned_base = addr;
296
    }
297
    if (!acpi_padding_is_safe(addr + length, aligned_top - (addr + length))) {
298
        aligned_top = addr + length;
299
    }
300
301
    uint64_t memmap_type = pmm_check_type(addr);
302
303
    if (memmap_type != MEMMAP_ACPI_RECLAIMABLE && memmap_type != MEMMAP_ACPI_NVS) {
304
        memmap_alloc_range(aligned_base, aligned_top - aligned_base, MEMMAP_RESERVED_MAPPED, 0, true, false, true);
305
    }
306
}
307
308
309
void acpi_map_tables(void) {
310
    struct rsdp *rsdp = acpi_get_rsdp();
311
    if (rsdp == NULL)
312
        return;
313
314
    uint64_t rsdp_length;
315
    if (rsdp->rev < 2) {
316
        rsdp_length = 20;
317
    } else {
318
        rsdp_length = rsdp->length;
319
    }
320
321
    map_single_table((uintptr_t)rsdp, rsdp_length);
322
323
    if (!(rsdp->rev >= 2 && rsdp->xsdt_addr)) {
324
        goto no_xsdt;
325
    }
326
327
    struct rsdt *xsdt = (void *)(uintptr_t)rsdp->xsdt_addr;
328
    if (xsdt->header.length < sizeof(struct sdt)) {
329
        goto no_xsdt;
330
    }
331
    size_t xsdt_entry_count = (xsdt->header.length - sizeof(struct sdt)) / 8;
332
333
    map_single_table((uintptr_t)xsdt, (uint32_t)-1);
334
335
    for (size_t i = 0; i < xsdt_entry_count; i++) {
336
        uint64_t entry = ((uint64_t *)xsdt->ptrs_start)[i];
337
        if (entry == 0)
338
            continue;
339
        struct sdt *sdt = (void *)(uintptr_t)entry;
340
341
        map_single_table((uintptr_t)sdt, (uint32_t)-1);
342
    }
343
344
no_xsdt:;
345
    if (rsdp->rsdt_addr == 0) {
346
        goto no_rsdt;
347
    }
348
349
    struct rsdt *rsdt = (void *)(uintptr_t)rsdp->rsdt_addr;
350
    if (rsdt->header.length < sizeof(struct sdt)) {
351
        goto no_rsdt;
352
    }
353
    size_t rsdt_entry_count = (rsdt->header.length - sizeof(struct sdt)) / 4;
354
355
    map_single_table((uintptr_t)rsdt, (uint32_t)-1);
356
357
    for (size_t i = 0; i < rsdt_entry_count; i++) {
358
        uint32_t entry = ((uint32_t *)rsdt->ptrs_start)[i];
359
        if (entry == 0)
360
            continue;
361
        struct sdt *sdt = (void *)(uintptr_t)entry;
362
363
        map_single_table((uintptr_t)sdt, (uint32_t)-1);
364
    }
365
366
no_rsdt:;
367
    uint8_t *fadt = acpi_get_table("FACP", 0);
368
    if (fadt == NULL) {
369
        return;
370
    }
371
    uint32_t fadt_length;
372
    memcpy(&fadt_length, fadt + 4, sizeof(fadt_length));
373
374
    // Read the single fields from the FADT without defining a struct for the whole table
375
    if (fadt_length >= 132 + 8) {
376
        uint64_t x_facs;
377
        memcpy(&x_facs, fadt + 132, sizeof(x_facs));
378
        if (x_facs != 0) {
379
            map_single_table(x_facs, (uint32_t)-1);
380
        }
381
    }
382
    if (fadt_length >= 140 + 8) {
383
        uint64_t x_dsdt;
384
        memcpy(&x_dsdt, fadt + 140, sizeof(x_dsdt));
385
        if (x_dsdt != 0) {
386
            map_single_table(x_dsdt, (uint32_t)-1);
387
        }
388
    }
389
    if (fadt_length >= 36 + 4) {
390
        uint32_t facs;
391
        memcpy(&facs, fadt + 36, sizeof(facs));
392
        if (facs != 0) {
393
            map_single_table(facs, (uint32_t)-1);
394
        }
395
    }
396
    if (fadt_length >= 40 + 4) {
397
        uint32_t dsdt;
398
        memcpy(&dsdt, fadt + 40, sizeof(dsdt));
399
        if (dsdt != 0) {
400
            map_single_table(dsdt, (uint32_t)-1);
401
        }
402
    }
403
}
404
405
void smbios_map_tables(void) {
406
    void *smbios32_ptr = NULL, *smbios64_ptr = NULL;
407
    acpi_get_smbios(&smbios32_ptr, &smbios64_ptr);
408
409
    if (smbios32_ptr != NULL) {
410
        struct smbios_entry_point_32 *smbios32 = smbios32_ptr;
411
        map_single_table((uintptr_t)smbios32, smbios32->length);
412
        if (smbios32->table_address != 0) {
413
            map_single_table(smbios32->table_address, smbios32->table_length);
414
        }
415
    }
416
417
    if (smbios64_ptr != NULL) {
418
        struct smbios_entry_point_64 *smbios64 = smbios64_ptr;
419
        map_single_table((uintptr_t)smbios64, smbios64->length);
420
        if (smbios64->table_address != 0) {
421
            map_single_table(smbios64->table_address, smbios64->table_maximum_size);
422
        }
423
    }
424
}
425
426
#if defined (UEFI)
427
void efi_map_runtime_entries(void) {
428
    size_t entry_count = efi_mmap_size / efi_desc_size;
429
430
    for (size_t i = 0; i < entry_count; i++) {
431
        EFI_MEMORY_DESCRIPTOR *entry = (void *)efi_mmap + i * efi_desc_size;
432
433
        if (entry->Type != EfiRuntimeServicesCode
434
         && entry->Type != EfiRuntimeServicesData) {
435
            continue;
436
        }
437
438
        uint64_t base = entry->PhysicalStart;
439
        uint64_t length = CHECKED_MUL(entry->NumberOfPages, 4096, continue);
440
441
        memmap_alloc_range(base, length, MEMMAP_RESERVED_MAPPED, 0, true, false, true);
442
    }
443
444
    // Explicitly map the EFI system table and the data it references.
445
    // The UEFI spec does not guarantee these reside in EfiRuntimeServicesData,
446
    // so we map them separately to ensure they are always accessible via HHDM.
447
    map_single_table((uintptr_t)gST, sizeof(*gST));
448
449
    if (gST->RuntimeServices != NULL) {
450
        map_single_table((uintptr_t)gST->RuntimeServices,
451
                         sizeof(*gST->RuntimeServices));
452
    }
453
454
    if (gST->ConfigurationTable != NULL && gST->NumberOfTableEntries > 0) {
455
        uint64_t ct_size = CHECKED_MUL(gST->NumberOfTableEntries,
456
                (uint64_t)sizeof(EFI_CONFIGURATION_TABLE), goto skip_ct);
457
        if (ct_size <= UINT32_MAX) {
458
            map_single_table((uintptr_t)gST->ConfigurationTable, (uint32_t)ct_size);
459
        }
460
skip_ct:;
461
    }
462
463
    if (gST->FirmwareVendor != NULL) {
464
        size_t len = 0;
465
        while (gST->FirmwareVendor[len] != 0) {
466
            len++;
467
        }
468
        map_single_table((uintptr_t)gST->FirmwareVendor,
469
                         (len + 1) * sizeof(*gST->FirmwareVendor));
470
    }
471
}
472
#endif
tab: 248 wrap: offon