1 | // SPDX-License-Identifier: GPL-3.0-or-later |
2 | |
3 | #include "mos/filesystem/sysfs/sysfs.h" |
4 | #include "mos/filesystem/sysfs/sysfs_autoinit.h" |
5 | #include "mos/mm/mm.h" |
6 | #include "mos/mm/paging/table_ops.h" |
7 | #include "mos/mm/physical/pmm.h" |
8 | #include "mos/x86/acpi/acpi_types.h" |
9 | |
10 | #include <mos/mos_global.h> |
11 | #include <mos/syslog/printk.h> |
12 | #include <mos/x86/acpi/acpi.h> |
13 | #include <mos/x86/acpi/madt.h> |
14 | #include <mos/x86/cpu/cpu.h> |
15 | #include <mos/x86/devices/port.h> |
16 | #include <mos/x86/x86_interrupt.h> |
17 | #include <mos/x86/x86_platform.h> |
18 | #include <mos_stdlib.h> |
19 | #include <mos_string.h> |
20 | #include <stddef.h> |
21 | |
22 | ptr_t x86_acpi_dsdt = 0; |
23 | |
24 | static sysfs_item_t acpi_sysfs_items[] = { |
25 | 0, |
26 | }; |
27 | |
28 | SYSFS_AUTOREGISTER(acpi, acpi_sysfs_items); |
29 | |
30 | #define do_verify_checksum(var, header, type) \ |
31 | var = container_of(header, type, sdt_header); \ |
32 | if (!verify_sdt_checksum(&(var)->sdt_header)) \ |
33 | mos_panic(#type " checksum error"); |
34 | |
35 | typedef struct |
36 | { |
37 | sysfs_item_t item; |
38 | size_t size; |
39 | phyframe_t *pages; |
40 | } acpi_sysfs_item_t; |
41 | |
42 | static bool acpi_sysfs_mmap(sysfs_file_t *f, vmap_t *vmap, off_t offset) |
43 | { |
44 | acpi_sysfs_item_t *const item = container_of(sysfs_file_get_item(f), acpi_sysfs_item_t, item); |
45 | const ssize_t item_npages = ALIGN_UP_TO_PAGE(item->size) / MOS_PAGE_SIZE; |
46 | if (offset >= item_npages) |
47 | return false; |
48 | |
49 | const size_t npages = MIN((ssize_t) vmap->npages, item_npages - offset); // limit to the number of pages in the item |
50 | mm_do_map(top: vmap->mmctx->pgd, vaddr: vmap->vaddr, phyframe_pfn(item->pages), n_pages: npages, flags: vmap->vmflags, do_refcount: false); // no need to refcount |
51 | return true; |
52 | } |
53 | |
54 | static bool acpi_sysfs_munmap(sysfs_file_t *f, vmap_t *vmap, bool *unmapped) |
55 | { |
56 | MOS_UNUSED(f); |
57 | mm_do_unmap(top: vmap->mmctx->pgd, vaddr: vmap->vaddr, n_pages: vmap->npages, do_unref: false); |
58 | *unmapped = true; |
59 | return true; |
60 | } |
61 | |
62 | static void register_sysfs_acpi_rsdp(const acpi_rsdp_t *rsdp) |
63 | { |
64 | acpi_sysfs_item_t *const item = kmalloc(sizeof(acpi_sysfs_item_t)); |
65 | item->size = rsdp->length; |
66 | item->item.name = strndup(src: "RSDP" , n: 4); |
67 | item->item.mem.mmap = acpi_sysfs_mmap; |
68 | item->item.mem.munmap = acpi_sysfs_munmap; |
69 | item->item.mem.size = item->size; |
70 | item->item.type = SYSFS_MEM; |
71 | item->pages = mm_get_free_pages(ALIGN_UP_TO_PAGE(item->size) / MOS_PAGE_SIZE); |
72 | if (!item->pages) |
73 | mos_panic("failed to allocate pages for ACPI table" ); |
74 | |
75 | memcpy(dest: (void *) phyframe_va(item->pages), src: rsdp, n: rsdp->length); |
76 | pmm_ref(item->pages, true); // don't free the pages when the item is freed |
77 | |
78 | sysfs_register_file(sysfs_dir: &__sysfs_acpi, item: &item->item); |
79 | } |
80 | |
81 | static void register_sysfs_acpi_node(const char table_name[4], const acpi_sdt_header_t *) |
82 | { |
83 | acpi_sysfs_item_t *const item = kmalloc(sizeof(acpi_sysfs_item_t)); |
84 | item->size = header->length; |
85 | item->item.name = strndup(src: table_name, n: 4); |
86 | item->item.mem.mmap = acpi_sysfs_mmap; |
87 | item->item.mem.munmap = acpi_sysfs_munmap; |
88 | item->item.mem.size = item->size; |
89 | item->item.type = SYSFS_MEM; |
90 | item->pages = mm_get_free_pages(ALIGN_UP_TO_PAGE(item->size) / MOS_PAGE_SIZE); |
91 | if (!item->pages) |
92 | mos_panic("failed to allocate pages for ACPI table" ); |
93 | |
94 | memcpy(dest: (void *) phyframe_va(item->pages), src: header, n: header->length); |
95 | pmm_ref(item->pages, true); // don't free the pages when the item is freed |
96 | |
97 | sysfs_register_file(sysfs_dir: &__sysfs_acpi, item: &item->item); |
98 | } |
99 | |
100 | should_inline bool verify_sdt_checksum(const acpi_sdt_header_t *) |
101 | { |
102 | u8 sum = 0; |
103 | for (u32 i = 0; i < tableHeader->length; i++) |
104 | sum += ((char *) tableHeader)[i]; |
105 | return sum == 0; |
106 | } |
107 | |
108 | static void do_handle_sdt_header(const acpi_sdt_header_t *const ) |
109 | { |
110 | register_sysfs_acpi_node(table_name: header->signature, header); |
111 | pr_dinfo2(x86_acpi, "%.4s at %p, size %u" , header->signature, (void *) header, header->length); |
112 | |
113 | if (strncmp(str1: header->signature, ACPI_SIGNATURE_FADT, n: 4) == 0) |
114 | { |
115 | const acpi_fadt_t *x86_acpi_fadt; |
116 | do_verify_checksum(x86_acpi_fadt, header, acpi_fadt_t); |
117 | |
118 | const acpi_sdt_header_t *const dsdt = (acpi_sdt_header_t *) pa_va(x86_acpi_fadt->dsdt); |
119 | if (!verify_sdt_checksum(tableHeader: dsdt)) |
120 | mos_panic("DSDT checksum error" ); |
121 | pr_dinfo2(x86_acpi, "DSDT at %p, size %u" , (void *) dsdt, dsdt->length); |
122 | x86_acpi_dsdt = (ptr_t) dsdt; |
123 | register_sysfs_acpi_node(table_name: "DSDT" , header: dsdt); |
124 | } |
125 | else if (strncmp(str1: header->signature, ACPI_SIGNATURE_MADT, n: 4) == 0) |
126 | { |
127 | do_verify_checksum(x86_acpi_madt, header, acpi_madt_t); |
128 | } |
129 | } |
130 | |
131 | static void do_iterate_sdts(const acpi_rsdp_t *rsdp) |
132 | { |
133 | if (rsdp->v1.revision == 0) |
134 | { |
135 | const acpi_sdt_header_t * = (acpi_sdt_header_t *) pa_va(rsdp->v1.rsdt_addr); |
136 | if (strncmp(str1: rsdt_header->signature, str2: "RSDT" , n: 4) != 0) |
137 | mos_panic("RSDT signature mismatch" ); |
138 | |
139 | const acpi_rsdt_t *x86_acpi_rsdt; |
140 | do_verify_checksum(x86_acpi_rsdt, rsdt_header, acpi_rsdt_t); |
141 | |
142 | const size_t = (x86_acpi_rsdt->sdt_header.length - sizeof(acpi_sdt_header_t)) / sizeof(ptr32_t); |
143 | for (size_t i = 0; i < num_headers; i++) |
144 | { |
145 | const acpi_sdt_header_t *const = (acpi_sdt_header_t *) pa_va(x86_acpi_rsdt->sdts[i]); |
146 | do_handle_sdt_header(header); |
147 | } |
148 | } |
149 | else if (rsdp->v1.revision == 2) |
150 | { |
151 | const acpi_sdt_header_t * = (acpi_sdt_header_t *) pa_va(rsdp->xsdt_addr); |
152 | if (strncmp(str1: xsdt_header->signature, str2: "XSDT" , n: 4) != 0) |
153 | mos_panic("XSDT signature mismatch" ); |
154 | |
155 | MOS_ASSERT_X(verify_sdt_checksum(xsdt_header), "acpi_xsdt_t checksum error" ); |
156 | |
157 | const acpi_xsdt_t *x86_acpi_xsdt; |
158 | x86_acpi_xsdt = container_of(xsdt_header, acpi_xsdt_t, sdt_header); |
159 | |
160 | const size_t = (x86_acpi_xsdt->sdt_header.length - sizeof(acpi_sdt_header_t)) / sizeof(ptr64_t); |
161 | for (size_t i = 0; i < num_headers; i++) |
162 | { |
163 | const acpi_sdt_header_t *const = (acpi_sdt_header_t *) pa_va(x86_acpi_xsdt->sdts[i]); |
164 | do_handle_sdt_header(header); |
165 | } |
166 | } |
167 | else |
168 | { |
169 | mos_panic("ACPI: RSDP revision %d not supported" , rsdp->v1.revision); |
170 | } |
171 | } |
172 | |
173 | void acpi_parse_rsdt(const acpi_rsdp_t *rsdp) |
174 | { |
175 | pr_dinfo2(x86_acpi, "initializing ACPI with RSDP at %p" , (void *) rsdp); |
176 | register_sysfs_acpi_rsdp(rsdp); |
177 | do_iterate_sdts(rsdp); |
178 | } |
179 | |
180 | const acpi_rsdp_t *acpi_find_rsdp(ptr_t start, size_t size) |
181 | { |
182 | for (ptr_t addr = start; addr < start + size; addr += 0x10) |
183 | { |
184 | if (strncmp(str1: (const char *) addr, ACPI_SIGNATURE_RSDP, n: 8) == 0) |
185 | { |
186 | pr_dinfo2(x86_acpi, "ACPI: RSDP magic at %p" , (void *) addr); |
187 | acpi_rsdp_t *rsdp = (acpi_rsdp_t *) addr; |
188 | |
189 | // check the checksum |
190 | u8 sum = 0; |
191 | for (u32 i = 0; i < sizeof(acpi_rsdp_v1_t); i++) |
192 | sum += ((u8 *) rsdp)[i]; |
193 | |
194 | if (sum != 0) |
195 | { |
196 | pr_info2("ACPI: RSDP checksum failed" ); |
197 | continue; |
198 | } |
199 | pr_dinfo2(x86_acpi, "ACPI: oem: '%.6s', revision: %d" , rsdp->v1.oem_id, rsdp->v1.revision); |
200 | |
201 | if (rsdp->v1.revision != 0 && rsdp->v1.revision != 2) |
202 | mos_panic("ACPI: RSDP revision %d not supported" , rsdp->v1.revision); |
203 | |
204 | return rsdp; |
205 | } |
206 | } |
207 | return NULL; |
208 | } |
209 | |