Serenity Operating System
1/*
2 * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
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/MultiProcessorParser.h>
28#include <Kernel/VM/MemoryManager.h>
29#include <LibBareMetal/StdLib.h>
30
31namespace Kernel {
32
33static MultiProcessorParser* s_parser;
34
35bool MultiProcessorParser::is_initialized()
36{
37 return s_parser != nullptr;
38}
39
40void MultiProcessorParser::initialize()
41{
42 if (!MultiProcessorParser::is_initialized())
43 s_parser = new MultiProcessorParser;
44}
45
46MultiProcessorParser::MultiProcessorParser()
47 : m_floating_pointer(search_floating_pointer())
48 , m_operable((m_floating_pointer != (uintptr_t) nullptr))
49{
50 if (m_floating_pointer != (uintptr_t) nullptr) {
51 kprintf("MultiProcessor: Floating Pointer Structure @ P 0x%x\n", m_floating_pointer);
52 parse_floating_pointer_data();
53 parse_configuration_table();
54 } else {
55 kprintf("MultiProcessor: Can't Locate Floating Pointer Structure, disabled.\n");
56 }
57}
58
59void MultiProcessorParser::parse_floating_pointer_data()
60{
61 auto floating_pointer_region = MM.allocate_kernel_region(PhysicalAddress(page_base_of((u32)m_floating_pointer)), PAGE_SIZE * 2, "MultiProcessor Parser Parsing Floating Pointer Structure", Region::Access::Read, false, true);
62 auto* floating_pointer = (MultiProcessor::FloatingPointer*)floating_pointer_region->vaddr().offset(offset_in_page((u32)m_floating_pointer)).as_ptr();
63 m_configuration_table = floating_pointer->physical_address_ptr;
64 m_specification_revision = floating_pointer->specification_revision;
65}
66
67size_t MultiProcessorParser::get_configuration_table_length()
68{
69 auto config_table_region = MM.allocate_kernel_region(PhysicalAddress(page_base_of((u32)m_configuration_table)), PAGE_SIZE * 2, "MultiProcessor Parser Getting Configuration Table length", Region::Access::Read, false, true);
70 auto* config_table = (MultiProcessor::ConfigurationTableHeader*)config_table_region->vaddr().offset(offset_in_page((u32)m_configuration_table)).as_ptr();
71 return config_table->length;
72}
73
74void MultiProcessorParser::parse_configuration_table()
75{
76 m_configuration_table_length = get_configuration_table_length();
77 auto config_table_region = MM.allocate_kernel_region(PhysicalAddress(page_base_of((u32)m_configuration_table)), PAGE_ROUND_UP(m_configuration_table_length), "MultiProcessor Parser Parsing Configuration Table", Region::Access::Read, false, true);
78 auto* config_table = (MultiProcessor::ConfigurationTableHeader*)config_table_region->vaddr().offset(offset_in_page((u32)m_configuration_table)).as_ptr();
79
80 size_t entry_count = config_table->entry_count;
81 auto* entry = config_table->entries;
82 auto* p_entry = reinterpret_cast<MultiProcessor::ConfigurationTableHeader*>(m_configuration_table)->entries;
83 while (entry_count > 0) {
84 dbg() << "MultiProcessor: Entry Type " << entry->entry_type << " detected.";
85 switch (entry->entry_type) {
86 case ((u8)MultiProcessor::ConfigurationTableEntryType::Processor):
87 entry = (MultiProcessor::EntryHeader*)(u32)entry + (u8)MultiProcessor::ConfigurationTableEntryLength::Processor;
88 p_entry = (MultiProcessor::EntryHeader*)(u32)p_entry + (u8)MultiProcessor::ConfigurationTableEntryLength::Processor;
89 break;
90 case ((u8)MultiProcessor::ConfigurationTableEntryType::Bus):
91 m_bus_entries.append((uintptr_t)p_entry);
92 entry = (MultiProcessor::EntryHeader*)(u32)entry + (u8)MultiProcessor::ConfigurationTableEntryLength::Bus;
93 p_entry = (MultiProcessor::EntryHeader*)(u32)p_entry + (u8)MultiProcessor::ConfigurationTableEntryLength::Bus;
94 break;
95 case ((u8)MultiProcessor::ConfigurationTableEntryType::IOAPIC):
96 entry = (MultiProcessor::EntryHeader*)(u32)entry + (u8)MultiProcessor::ConfigurationTableEntryLength::IOAPIC;
97 p_entry = (MultiProcessor::EntryHeader*)(u32)p_entry + (u8)MultiProcessor::ConfigurationTableEntryLength::IOAPIC;
98 break;
99 case ((u8)MultiProcessor::ConfigurationTableEntryType::IO_Interrupt_Assignment):
100 m_io_interrupt_redirection_entries.append((uintptr_t)p_entry);
101 entry = (MultiProcessor::EntryHeader*)(u32)entry + (u8)MultiProcessor::ConfigurationTableEntryLength::IO_Interrupt_Assignment;
102 p_entry = (MultiProcessor::EntryHeader*)(u32)p_entry + (u8)MultiProcessor::ConfigurationTableEntryLength::IO_Interrupt_Assignment;
103 break;
104 case ((u8)MultiProcessor::ConfigurationTableEntryType::Local_Interrupt_Assignment):
105 entry = (MultiProcessor::EntryHeader*)(u32)entry + (u8)MultiProcessor::ConfigurationTableEntryLength::Local_Interrupt_Assignment;
106 p_entry = (MultiProcessor::EntryHeader*)(u32)p_entry + (u8)MultiProcessor::ConfigurationTableEntryLength::Local_Interrupt_Assignment;
107 break;
108 case ((u8)MultiProcessor::ConfigurationTableEntryType::SystemAddressSpaceMapping):
109 entry = (MultiProcessor::EntryHeader*)(u32)entry + (u8)MultiProcessor::ConfigurationTableEntryLength::SystemAddressSpaceMapping;
110 p_entry = (MultiProcessor::EntryHeader*)(u32)p_entry + (u8)MultiProcessor::ConfigurationTableEntryLength::SystemAddressSpaceMapping;
111 break;
112 case ((u8)MultiProcessor::ConfigurationTableEntryType::BusHierarchyDescriptor):
113 entry = (MultiProcessor::EntryHeader*)(u32)entry + (u8)MultiProcessor::ConfigurationTableEntryLength::BusHierarchyDescriptor;
114 p_entry = (MultiProcessor::EntryHeader*)(u32)p_entry + (u8)MultiProcessor::ConfigurationTableEntryLength::BusHierarchyDescriptor;
115 break;
116 case ((u8)MultiProcessor::ConfigurationTableEntryType::CompatibilityBusAddressSpaceModifier):
117 entry = (MultiProcessor::EntryHeader*)(u32)entry + (u8)MultiProcessor::ConfigurationTableEntryLength::CompatibilityBusAddressSpaceModifier;
118 p_entry = (MultiProcessor::EntryHeader*)(u32)p_entry + (u8)MultiProcessor::ConfigurationTableEntryLength::CompatibilityBusAddressSpaceModifier;
119 break;
120 ASSERT_NOT_REACHED();
121 }
122 entry_count--;
123 }
124}
125
126uintptr_t MultiProcessorParser::search_floating_pointer()
127{
128 uintptr_t mp_floating_pointer = (uintptr_t) nullptr;
129 auto region = MM.allocate_kernel_region(PhysicalAddress(0), PAGE_SIZE, "MultiProcessor Parser Floating Pointer Structure Finding", Region::Access::Read);
130 u16 ebda_seg = (u16) * ((uint16_t*)((region->vaddr().get() & PAGE_MASK) + 0x40e));
131 kprintf("MultiProcessor: Probing EBDA, Segment 0x%x\n", ebda_seg);
132
133 mp_floating_pointer = search_floating_pointer_in_ebda(ebda_seg);
134 if (mp_floating_pointer != (uintptr_t) nullptr)
135 return mp_floating_pointer;
136 return search_floating_pointer_in_bios_area();
137}
138
139uintptr_t MultiProcessorParser::search_floating_pointer_in_ebda(u16 ebda_segment)
140{
141 auto floating_pointer_region = MM.allocate_kernel_region(PhysicalAddress(page_base_of((u32)(ebda_segment << 4))), PAGE_ROUND_UP(1024), "MultiProcessor Parser floating_pointer Finding #1", Region::Access::Read, false, true);
142 char* p_floating_pointer_str = (char*)(PhysicalAddress(ebda_segment << 4).as_ptr());
143 for (char* floating_pointer_str = (char*)floating_pointer_region->vaddr().offset(offset_in_page((u32)(ebda_segment << 4))).as_ptr(); floating_pointer_str < (char*)(floating_pointer_region->vaddr().offset(offset_in_page((u32)(ebda_segment << 4))).get() + 1024); floating_pointer_str += 16) {
144#ifdef MUTLIPROCESSOR_DEBUG
145 dbg() << "MultiProcessor: Looking for floating pointer structure in EBDA @ V0x " << String::format("%x", floating_pointer_str) << ", P0x" << String::format("%x", p_floating_pointer_str);
146#endif
147 if (!strncmp("_MP_", floating_pointer_str, strlen("_MP_")))
148 return (uintptr_t)p_floating_pointer_str;
149 p_floating_pointer_str += 16;
150 }
151 return (uintptr_t) nullptr;
152}
153uintptr_t MultiProcessorParser::search_floating_pointer_in_bios_area()
154{
155 auto floating_pointer_region = MM.allocate_kernel_region(PhysicalAddress(page_base_of((u32)0xE0000)), PAGE_ROUND_UP(0xFFFFF - 0xE0000), "MultiProcessor Parser floating_pointer Finding #2", Region::Access::Read, false, true);
156 char* p_floating_pointer_str = (char*)(PhysicalAddress(0xE0000).as_ptr());
157 for (char* floating_pointer_str = (char*)floating_pointer_region->vaddr().offset(offset_in_page((u32)(0xE0000))).as_ptr(); floating_pointer_str < (char*)(floating_pointer_region->vaddr().offset(offset_in_page((u32)(0xE0000))).get() + (0xFFFFF - 0xE0000)); floating_pointer_str += 16) {
158#ifdef MUTLIPROCESSOR_DEBUG
159 dbg() << "MultiProcessor: Looking for floating pointer structure in BIOS area @ V0x " << String::format("%x", floating_pointer_str) << ", P0x" << String::format("%x", p_floating_pointer_str);
160#endif
161 if (!strncmp("_MP_", floating_pointer_str, strlen("_MP_")))
162 return (uintptr_t)p_floating_pointer_str;
163 p_floating_pointer_str += 16;
164 }
165 return (uintptr_t) nullptr;
166}
167
168Vector<unsigned> MultiProcessorParser::get_pci_bus_ids()
169{
170 Vector<unsigned> pci_bus_ids;
171 for (auto entry : m_bus_entries) {
172 auto entry_region = MM.allocate_kernel_region(PhysicalAddress(page_base_of((u32)entry)), PAGE_ROUND_UP(m_configuration_table_length), "MultiProcessor Parser Parsing Bus Entry", Region::Access::Read, false, true);
173 auto* v_entry_ptr = (MultiProcessor::BusEntry*)entry_region->vaddr().offset(offset_in_page((u32)entry)).as_ptr();
174 if (!strncmp("PCI ", v_entry_ptr->bus_type, strlen("PCI ")))
175 pci_bus_ids.append(v_entry_ptr->bus_id);
176 }
177 return pci_bus_ids;
178}
179
180MultiProcessorParser& MultiProcessorParser::the()
181{
182 ASSERT(!MultiProcessorParser::is_initialized());
183 return *s_parser;
184}
185
186Vector<RefPtr<PCIInterruptOverrideMetadata>> MultiProcessorParser::get_pci_interrupt_redirections()
187{
188 dbg() << "MultiProcessor: Get PCI IOAPIC redirections";
189 Vector<RefPtr<PCIInterruptOverrideMetadata>> overrides;
190 Vector<unsigned> pci_bus_ids = get_pci_bus_ids();
191 for (auto entry : m_io_interrupt_redirection_entries) {
192 auto entry_region = MM.allocate_kernel_region(PhysicalAddress(page_base_of((u32)entry)), PAGE_ROUND_UP(m_configuration_table_length), "MultiProcessor Parser Parsing Bus Entry", Region::Access::Read, false, true);
193 auto* v_entry_ptr = (MultiProcessor::IOInterruptAssignmentEntry*)entry_region->vaddr().offset(offset_in_page((u32)entry)).as_ptr();
194 dbg() << "MultiProcessor: Parsing Entry P 0x" << String::format("%x", entry) << ", V " << v_entry_ptr;
195 for (auto id : pci_bus_ids) {
196 if (id == v_entry_ptr->source_bus_id) {
197
198 kprintf("Interrupts: Bus %d, Polarity 0x%x, Trigger Mode 0x%x, INT %x, IOAPIC %d, IOAPIC INTIN %d\n", v_entry_ptr->source_bus_id,
199 v_entry_ptr->polarity,
200 v_entry_ptr->trigger_mode,
201 v_entry_ptr->source_bus_irq,
202 v_entry_ptr->destination_ioapic_id,
203 v_entry_ptr->destination_ioapic_intin_pin);
204 overrides.append(adopt(*new PCIInterruptOverrideMetadata(
205 v_entry_ptr->source_bus_id,
206 v_entry_ptr->polarity,
207 v_entry_ptr->trigger_mode,
208 v_entry_ptr->source_bus_irq,
209 v_entry_ptr->destination_ioapic_id,
210 v_entry_ptr->destination_ioapic_intin_pin)));
211 }
212 }
213 }
214
215 for (auto override_metadata : overrides) {
216 kprintf("Interrupts: Bus %d, Polarity 0x%x, PCI Device %d, Trigger Mode 0x%x, INT %x, IOAPIC %d, IOAPIC INTIN %d\n",
217 override_metadata->bus(),
218 override_metadata->polarity(),
219 override_metadata->pci_device_number(),
220 override_metadata->trigger_mode(),
221 override_metadata->pci_interrupt_pin(),
222 override_metadata->ioapic_id(),
223 override_metadata->ioapic_interrupt_pin());
224 }
225 return overrides;
226}
227
228PCIInterruptOverrideMetadata::PCIInterruptOverrideMetadata(u8 bus_id, u8 polarity, u8 trigger_mode, u8 source_irq, u32 ioapic_id, u16 ioapic_int_pin)
229 : m_bus_id(bus_id)
230 , m_polarity(polarity)
231 , m_trigger_mode(trigger_mode)
232 , m_pci_interrupt_pin(source_irq & 0b11)
233 , m_pci_device_number((source_irq & 0b11111) >> 2)
234 , m_ioapic_id(ioapic_id)
235 , m_ioapic_interrupt_pin(ioapic_int_pin)
236{
237}
238u8 PCIInterruptOverrideMetadata::bus() const
239{
240 return m_bus_id;
241}
242u8 PCIInterruptOverrideMetadata::polarity() const
243{
244 return m_polarity;
245}
246u8 PCIInterruptOverrideMetadata::trigger_mode() const
247{
248 return m_trigger_mode;
249}
250u8 PCIInterruptOverrideMetadata::pci_interrupt_pin() const
251{
252 return m_pci_interrupt_pin;
253}
254u8 PCIInterruptOverrideMetadata::pci_device_number() const
255{
256 return m_pci_device_number;
257}
258u32 PCIInterruptOverrideMetadata::ioapic_id() const
259{
260 return m_ioapic_id;
261}
262u16 PCIInterruptOverrideMetadata::ioapic_interrupt_pin() const
263{
264 return m_ioapic_interrupt_pin;
265}
266}