Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <Kernel/ACPI/DMIDecoder.h>
28#include <Kernel/VM/MemoryManager.h>
29#include <LibBareMetal/StdLib.h>
30
31namespace Kernel {
32
33static DMIDecoder* s_dmi_decoder;
34
35//#define SMBIOS_DEBUG
36
37#define SMBIOS_BASE_SEARCH_ADDR 0xf0000
38#define SMBIOS_END_SEARCH_ADDR 0xfffff
39#define SMBIOS_SEARCH_AREA_SIZE (SMBIOS_END_SEARCH_ADDR - SMBIOS_BASE_SEARCH_ADDR)
40
41DMIDecoder& DMIDecoder::the()
42{
43 if (s_dmi_decoder == nullptr) {
44 s_dmi_decoder = new DMIDecoder(true);
45 }
46 return *s_dmi_decoder;
47}
48
49void DMIDecoder::initialize()
50{
51 if (s_dmi_decoder == nullptr) {
52 s_dmi_decoder = new DMIDecoder(true);
53 }
54}
55
56void DMIDecoder::initialize_untrusted()
57{
58 if (s_dmi_decoder == nullptr) {
59 s_dmi_decoder = new DMIDecoder(false);
60 }
61}
62
63void DMIDecoder::set_64_bit_entry_initialization_values(SMBIOS::EntryPoint64bit& entry)
64{
65 kprintf("DMIDecoder: SMBIOS 64bit Entry point @ P 0x%x\n", m_entry64bit_point);
66 m_use_64bit_entry = true;
67
68 auto region = MM.allocate_kernel_region(PhysicalAddress(page_base_of((u32)&entry)), PAGE_ROUND_UP(SMBIOS_SEARCH_AREA_SIZE), "DMI Decoder 64 bit Initialization", Region::Access::Read, false, false);
69 auto& entry_ptr = *(SMBIOS::EntryPoint64bit*)region->vaddr().offset(offset_in_page((u32)&entry)).as_ptr();
70 m_structure_table = (SMBIOS::TableHeader*)entry_ptr.table_ptr;
71 m_structures_count = entry_ptr.table_maximum_size;
72 m_table_length = entry_ptr.table_maximum_size;
73}
74
75void DMIDecoder::set_32_bit_entry_initialization_values(SMBIOS::EntryPoint32bit& entry)
76{
77 kprintf("DMIDecoder: SMBIOS 32bit Entry point @ P 0x%x\n", m_entry32bit_point);
78 m_use_64bit_entry = false;
79
80 auto region = MM.allocate_kernel_region(PhysicalAddress(page_base_of((u32)&entry)), PAGE_ROUND_UP(SMBIOS_SEARCH_AREA_SIZE), "DMI Decoder 32 bit Initialization", Region::Access::Read, false, false);
81 auto& entry_ptr = *(SMBIOS::EntryPoint32bit*)region->vaddr().offset(offset_in_page((u32)&entry)).as_ptr();
82
83 m_structure_table = (SMBIOS::TableHeader*)entry_ptr.legacy_structure.smbios_table_ptr;
84 m_structures_count = entry_ptr.legacy_structure.smbios_tables_count;
85 m_table_length = entry_ptr.legacy_structure.smboios_table_length;
86}
87
88void DMIDecoder::initialize_parser()
89{
90 if (m_entry32bit_point != nullptr || m_entry64bit_point != nullptr) {
91 m_operable = true;
92 kprintf("DMI Decoder is enabled\n");
93 if (m_entry64bit_point != nullptr) {
94 set_64_bit_entry_initialization_values(*m_entry64bit_point);
95 } else if (m_entry32bit_point != nullptr) {
96 set_32_bit_entry_initialization_values(*m_entry32bit_point);
97 }
98 kprintf("DMIDecoder: Data table @ P 0x%x\n", m_structure_table);
99 enumerate_smbios_tables();
100 } else {
101 m_operable = false;
102 kprintf("DMI Decoder is disabled. Cannot find SMBIOS tables.\n");
103 }
104}
105
106void DMIDecoder::enumerate_smbios_tables()
107{
108
109 u32 table_length = m_table_length;
110 SMBIOS::TableHeader* p_table_ptr = m_structure_table;
111
112 PhysicalAddress paddr = PhysicalAddress(page_base_of((uintptr_t)p_table_ptr));
113 auto region = MM.allocate_kernel_region(paddr, PAGE_ROUND_UP(table_length), "DMI Decoder Enumerating SMBIOS", Region::Access::Read, false, false);
114
115 volatile SMBIOS::TableHeader* v_table_ptr = (SMBIOS::TableHeader*)region->vaddr().offset(offset_in_page((uintptr_t)p_table_ptr)).as_ptr();
116#ifdef SMBIOS_DEBUG
117 dbgprintf("DMIDecoder: Total Table length %d\n", m_table_length);
118#endif
119
120 u32 structures_count = 0;
121 while (table_length > 0) {
122#ifdef SMBIOS_DEBUG
123 dbgprintf("DMIDecoder: Examining table @ P 0x%x V 0x%x\n", p_table_ptr, v_table_ptr);
124#endif
125 structures_count++;
126 if (v_table_ptr->type == (u8)SMBIOS::TableType::EndOfTable) {
127 kprintf("DMIDecoder: Detected table with type 127, End of SMBIOS data.\n");
128 break;
129 }
130 kprintf("DMIDecoder: Detected table with type %d\n", v_table_ptr->type);
131 m_smbios_tables.append(p_table_ptr);
132 table_length -= v_table_ptr->length;
133
134 size_t table_size = get_table_size(*p_table_ptr);
135 p_table_ptr = (SMBIOS::TableHeader*)((uintptr_t)p_table_ptr + table_size);
136 v_table_ptr = (SMBIOS::TableHeader*)((uintptr_t)v_table_ptr + table_size);
137#ifdef SMBIOS_DEBUG
138 dbgprintf("DMIDecoder: Next table @ P 0x%x\n", p_table_ptr);
139#endif
140 if (p_table_ptr == nullptr)
141 break;
142 }
143 m_structures_count = structures_count;
144}
145
146size_t DMIDecoder::get_table_size(SMBIOS::TableHeader& table)
147{
148 auto region = MM.allocate_kernel_region(PhysicalAddress(page_base_of((u32)&table)), PAGE_ROUND_UP(m_table_length), "DMI Decoder Determining table size", Region::Access::Read, false, false);
149 auto& table_v_ptr = (SMBIOS::TableHeader&)*region->vaddr().offset(offset_in_page((u32)&table)).as_ptr();
150#ifdef SMBIOS_DEBUG
151 dbgprintf("DMIDecoder: table legnth - 0x%x\n", table_v_ptr.length);
152#endif
153 const char* strtab = (char*)&table_v_ptr + table_v_ptr.length;
154 size_t index = 1;
155 while (strtab[index - 1] != '\0' || strtab[index] != '\0') {
156 if (index > m_table_length) {
157 ASSERT_NOT_REACHED(); // FIXME: Instead of halting, find a better solution (Hint: use m_operable to disallow further use of DMIDecoder)
158 }
159 index++;
160 }
161#ifdef SMBIOS_DEBUG
162 dbgprintf("DMIDecoder: table size - 0x%x\n", table_v_ptr.length + index + 1);
163#endif
164 return table_v_ptr.length + index + 1;
165}
166
167SMBIOS::TableHeader* DMIDecoder::get_next_physical_table(SMBIOS::TableHeader& p_table)
168{
169 return (SMBIOS::TableHeader*)((uintptr_t)&p_table + get_table_size(p_table));
170}
171
172SMBIOS::TableHeader* DMIDecoder::get_smbios_physical_table_by_handle(u16 handle)
173{
174
175 for (auto* table : m_smbios_tables) {
176 if (!table)
177 continue;
178 auto region = MM.allocate_kernel_region(PhysicalAddress(page_base_of((uintptr_t)table)), PAGE_SIZE * 2, "DMI Decoder Finding Table", Region::Access::Read, false, false);
179 SMBIOS::TableHeader* table_v_ptr = (SMBIOS::TableHeader*)region->vaddr().offset(offset_in_page((uintptr_t)table)).as_ptr();
180
181 if (table_v_ptr->handle == handle) {
182 return table;
183 }
184 }
185 return nullptr;
186}
187SMBIOS::TableHeader* DMIDecoder::get_smbios_physical_table_by_type(u8 table_type)
188{
189
190 for (auto* table : m_smbios_tables) {
191 if (!table)
192 continue;
193 auto region = MM.allocate_kernel_region(PhysicalAddress(page_base_of((uintptr_t)table)), PAGE_ROUND_UP(PAGE_SIZE * 2), "DMI Decoder Finding Table", Region::Access::Read, false, false);
194 SMBIOS::TableHeader* table_v_ptr = (SMBIOS::TableHeader*)region->vaddr().offset(offset_in_page((uintptr_t)table)).as_ptr();
195 if (table_v_ptr->type == table_type) {
196 return table;
197 }
198 }
199 return nullptr;
200}
201
202DMIDecoder::DMIDecoder(bool trusted)
203 : m_entry32bit_point(find_entry32bit_point())
204 , m_entry64bit_point(find_entry64bit_point())
205 , m_structure_table(nullptr)
206 , m_untrusted(!trusted)
207{
208 if (!trusted) {
209 kprintf("DMI Decoder initialized as untrusted due to user request.\n");
210 }
211 initialize_parser();
212}
213
214SMBIOS::EntryPoint64bit* DMIDecoder::find_entry64bit_point()
215{
216 PhysicalAddress paddr = PhysicalAddress(SMBIOS_BASE_SEARCH_ADDR);
217 auto region = MM.allocate_kernel_region(paddr, PAGE_ROUND_UP(SMBIOS_SEARCH_AREA_SIZE), "DMI Decoder Entry Point 64 bit Finding", Region::Access::Read, false, false);
218
219 char* tested_physical_ptr = (char*)paddr.get();
220 for (char* entry_str = (char*)(region->vaddr().get()); entry_str < (char*)(region->vaddr().get() + (SMBIOS_SEARCH_AREA_SIZE)); entry_str += 16) {
221#ifdef SMBIOS_DEBUG
222 dbgprintf("DMI Decoder: Looking for 64 bit Entry point @ V 0x%x P 0x%x\n", entry_str, tested_physical_ptr);
223#endif
224 if (!strncmp("_SM3_", entry_str, strlen("_SM3_")))
225 return (SMBIOS::EntryPoint64bit*)tested_physical_ptr;
226
227 tested_physical_ptr += 16;
228 }
229 return nullptr;
230}
231
232SMBIOS::EntryPoint32bit* DMIDecoder::find_entry32bit_point()
233{
234 PhysicalAddress paddr = PhysicalAddress(SMBIOS_BASE_SEARCH_ADDR);
235 auto region = MM.allocate_kernel_region(paddr, PAGE_ROUND_UP(SMBIOS_SEARCH_AREA_SIZE), "DMI Decoder Entry Point 32 bit Finding", Region::Access::Read, false, false);
236
237 char* tested_physical_ptr = (char*)paddr.get();
238 for (char* entry_str = (char*)(region->vaddr().get()); entry_str < (char*)(region->vaddr().get() + (SMBIOS_SEARCH_AREA_SIZE)); entry_str += 16) {
239#ifdef SMBIOS_DEBUG
240 dbgprintf("DMI Decoder: Looking for 32 bit Entry point @ V 0x%x P 0x%x\n", entry_str, tested_physical_ptr);
241#endif
242 if (!strncmp("_SM_", entry_str, strlen("_SM_")))
243 return (SMBIOS::EntryPoint32bit*)tested_physical_ptr;
244
245 tested_physical_ptr += 16;
246 }
247 return nullptr;
248}
249
250Vector<SMBIOS::PhysicalMemoryArray*>& DMIDecoder::get_physical_memory_areas()
251{
252 // FIXME: Implement it...
253 kprintf("DMIDecoder::get_physical_memory_areas() is not implemented.\n");
254 ASSERT_NOT_REACHED();
255}
256bool DMIDecoder::is_reliable()
257{
258 return !m_untrusted;
259}
260u64 DMIDecoder::get_bios_characteristics()
261{
262 // FIXME: Make sure we have some mapping here so we don't rely on existing identity mapping...
263 ASSERT(m_operable == true);
264 SMBIOS::BIOSInfo* bios_info = (SMBIOS::BIOSInfo*)get_smbios_physical_table_by_type(0);
265 ASSERT(bios_info != nullptr);
266
267 kprintf("DMIDecoder: BIOS info @ P 0x%x\n", bios_info);
268 return bios_info->bios_characteristics;
269}
270
271char* DMIDecoder::get_smbios_string(SMBIOS::TableHeader&, u8)
272{
273 // FIXME: Implement it...
274 // FIXME: Make sure we have some mapping here so we don't rely on existing identity mapping...
275 kprintf("DMIDecoder::get_smbios_string() is not implemented.\n");
276 ASSERT_NOT_REACHED();
277 return nullptr;
278}
279
280}