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