Serenity Operating System
at hosted 256 lines 11 kB view raw
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 <AK/Optional.h> 28#include <AK/StringView.h> 29#include <Kernel/PCI/MMIOAccess.h> 30#include <Kernel/VM/MemoryManager.h> 31 32namespace Kernel { 33namespace PCI { 34 35class MMIOSegment { 36public: 37 MMIOSegment(PhysicalAddress, u8, u8); 38 u8 get_start_bus(); 39 u8 get_end_bus(); 40 size_t get_size(); 41 PhysicalAddress get_paddr(); 42 43private: 44 PhysicalAddress m_base_addr; 45 u8 m_start_bus; 46 u8 m_end_bus; 47}; 48 49#define PCI_MMIO_CONFIG_SPACE_SIZE 4096 50 51uint32_t MMIOAccess::segment_count() const 52{ 53 return m_segments.size(); 54} 55 56uint8_t MMIOAccess::segment_start_bus(u32 seg) const 57{ 58 auto segment = m_segments.get(seg); 59 ASSERT(segment.has_value()); 60 return segment.value().get_start_bus(); 61} 62 63uint8_t MMIOAccess::segment_end_bus(u32 seg) const 64{ 65 auto segment = m_segments.get(seg); 66 ASSERT(segment.has_value()); 67 return segment.value().get_end_bus(); 68} 69 70void MMIOAccess::initialize(PhysicalAddress mcfg) 71{ 72 if (!Access::is_initialized()) 73 new MMIOAccess(mcfg); 74} 75 76MMIOAccess::MMIOAccess(PhysicalAddress p_mcfg) 77 : m_mcfg(p_mcfg) 78 , m_mapped_address(ChangeableAddress(0xFFFF, 0xFF, 0xFF, 0xFF)) 79{ 80 klog() << "PCI: Using MMIO for PCI configuration space access"; 81 m_mmio_window_region = MM.allocate_kernel_region(PAGE_ROUND_UP(PCI_MMIO_CONFIG_SPACE_SIZE), "PCI MMIO", Region::Access::Read | Region::Access::Write); 82 83 auto checkup_region = MM.allocate_kernel_region(p_mcfg.page_base(), (PAGE_SIZE * 2), "PCI MCFG Checkup", Region::Access::Read | Region::Access::Write); 84#ifdef PCI_DEBUG 85 dbg() << "PCI: Checking MCFG Table length to choose the correct mapping size"; 86#endif 87 88 auto* sdt = (ACPI::Structures::SDTHeader*)checkup_region->vaddr().offset(p_mcfg.offset_in_page()).as_ptr(); 89 u32 length = sdt->length; 90 u8 revision = sdt->revision; 91 92 klog() << "PCI: MCFG, length - " << length << ", revision " << revision; 93 checkup_region->unmap(); 94 95 auto mcfg_region = MM.allocate_kernel_region(p_mcfg.page_base(), PAGE_ROUND_UP(length) + PAGE_SIZE, "PCI Parsing MCFG", Region::Access::Read | Region::Access::Write); 96 97 auto& mcfg = *(ACPI::Structures::MCFG*)mcfg_region->vaddr().offset(p_mcfg.offset_in_page()).as_ptr(); 98#ifdef PCI_DEBUG 99 dbg() << "PCI: Checking MCFG @ V " << &mcfg << ", P 0x" << String::format("%x", p_mcfg.get()); 100#endif 101 102 for (u32 index = 0; index < ((mcfg.header.length - sizeof(ACPI::Structures::MCFG)) / sizeof(ACPI::Structures::PCI_MMIO_Descriptor)); index++) { 103 u8 start_bus = mcfg.descriptors[index].start_pci_bus; 104 u8 end_bus = mcfg.descriptors[index].end_pci_bus; 105 u32 lower_addr = mcfg.descriptors[index].base_addr; 106 107 m_segments.set(index, { PhysicalAddress(lower_addr), start_bus, end_bus }); 108 klog() << "PCI: New PCI segment @ " << PhysicalAddress(lower_addr) << ", PCI buses (" << start_bus << "-" << end_bus << ")"; 109 } 110 mcfg_region->unmap(); 111 klog() << "PCI: MMIO segments - " << m_segments.size(); 112 InterruptDisabler disabler; 113#ifdef PCI_DEBUG 114 dbg() << "PCI: mapped address (" << String::format("%w", m_mapped_address.seg()) << ":" << String::format("%b", m_mapped_address.bus()) << ":" << String::format("%b", m_mapped_address.slot()) << "." << String::format("%b", m_mapped_address.function()) << ")"; 115#endif 116 map_device(Address(0, 0, 0, 0)); 117#ifdef PCI_DEBUG 118 dbg() << "PCI: Default mapped address (" << String::format("%w", m_mapped_address.seg()) << ":" << String::format("%b", m_mapped_address.bus()) << ":" << String::format("%b", m_mapped_address.slot()) << "." << String::format("%b", m_mapped_address.function()) << ")"; 119#endif 120} 121 122void MMIOAccess::map_device(Address address) 123{ 124 if (m_mapped_address == address) 125 return; 126 // FIXME: Map and put some lock! 127 ASSERT_INTERRUPTS_DISABLED(); 128 auto segment = m_segments.get(address.seg()); 129 ASSERT(segment.has_value()); 130 PhysicalAddress segment_lower_addr = segment.value().get_paddr(); 131 PhysicalAddress device_physical_mmio_space = segment_lower_addr.offset( 132 PCI_MMIO_CONFIG_SPACE_SIZE * address.function() + (PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE) * address.slot() + (PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE * PCI_MAX_DEVICES_PER_BUS) * (address.bus() - segment.value().get_start_bus())); 133 134#ifdef PCI_DEBUG 135 dbg() << "PCI: Mapping device @ pci (" << String::format("%w", address.seg()) << ":" << String::format("%b", address.bus()) << ":" << String::format("%b", address.slot()) << "." << String::format("%b", address.function()) << ")" 136 << " V 0x" << String::format("%x", m_mmio_window_region->vaddr().get()) << " P 0x" << String::format("%x", device_physical_mmio_space.get()); 137#endif 138 m_mmio_window_region->vmobject().physical_pages()[0] = PhysicalPage::create(device_physical_mmio_space, false, false); 139 m_mmio_window_region->remap(); 140 m_mapped_address = address; 141} 142 143u8 MMIOAccess::read8_field(Address address, u32 field) 144{ 145 InterruptDisabler disabler; 146 ASSERT(field <= 0xfff); 147#ifdef PCI_DEBUG 148 dbg() << "PCI: Reading field " << field << ", Address(" << String::format("%w", address.seg()) << ":" << String::format("%b", address.bus()) << ":" << String::format("%b", address.slot()) << "." << String::format("%b", address.function()) << ")"; 149#endif 150 map_device(address); 151 return *((u8*)(m_mmio_window_region->vaddr().get() + (field & 0xfff))); 152} 153 154u16 MMIOAccess::read16_field(Address address, u32 field) 155{ 156 InterruptDisabler disabler; 157 ASSERT(field < 0xfff); 158#ifdef PCI_DEBUG 159 dbg() << "PCI: Reading field " << field << ", Address(" << String::format("%w", address.seg()) << ":" << String::format("%b", address.bus()) << ":" << String::format("%b", address.slot()) << "." << String::format("%b", address.function()) << ")"; 160#endif 161 map_device(address); 162 return *((u16*)(m_mmio_window_region->vaddr().get() + (field & 0xfff))); 163} 164 165u32 MMIOAccess::read32_field(Address address, u32 field) 166{ 167 InterruptDisabler disabler; 168 ASSERT(field <= 0xffc); 169#ifdef PCI_DEBUG 170 dbg() << "PCI: Reading field " << field << ", Address(" << String::format("%w", address.seg()) << ":" << String::format("%b", address.bus()) << ":" << String::format("%b", address.slot()) << "." << String::format("%b", address.function()) << ")"; 171#endif 172 map_device(address); 173 return *((u32*)(m_mmio_window_region->vaddr().get() + (field & 0xfff))); 174} 175 176void MMIOAccess::write8_field(Address address, u32 field, u8 value) 177{ 178 InterruptDisabler disabler; 179 ASSERT(field <= 0xfff); 180#ifdef PCI_DEBUG 181 dbg() << "PCI: Writing to field " << field << ", Address(" << String::format("%w", address.seg()) << ":" << String::format("%b", address.bus()) << ":" << String::format("%b", address.slot()) << "." << String::format("%b", address.function()) << ") value 0x" << String::format("%x", value); 182#endif 183 map_device(address); 184 *((u8*)(m_mmio_window_region->vaddr().get() + (field & 0xfff))) = value; 185} 186void MMIOAccess::write16_field(Address address, u32 field, u16 value) 187{ 188 InterruptDisabler disabler; 189 ASSERT(field < 0xfff); 190#ifdef PCI_DEBUG 191 dbg() << "PCI: Writing to field " << field << ", Address(" << String::format("%w", address.seg()) << ":" << String::format("%b", address.bus()) << ":" << String::format("%b", address.slot()) << "." << String::format("%b", address.function()) << ") value 0x" << String::format("%x", value); 192#endif 193 map_device(address); 194 *((u16*)(m_mmio_window_region->vaddr().get() + (field & 0xfff))) = value; 195} 196void MMIOAccess::write32_field(Address address, u32 field, u32 value) 197{ 198 InterruptDisabler disabler; 199 ASSERT(field <= 0xffc); 200#ifdef PCI_DEBUG 201 dbg() << "PCI: Writing to field " << field << ", Address(" << String::format("%w", address.seg()) << ":" << String::format("%b", address.bus()) << ":" << String::format("%b", address.slot()) << "." << String::format("%b", address.function()) << ") value 0x" << String::format("%x", value); 202#endif 203 map_device(address); 204 *((u32*)(m_mmio_window_region->vaddr().get() + (field & 0xfff))) = value; 205} 206 207void MMIOAccess::enumerate_all(Function<void(Address, ID)>& callback) 208{ 209 for (u16 seg = 0; seg < m_segments.size(); seg++) { 210#ifdef PCI_DEBUG 211 dbg() << "PCI: Enumerating Memory mapped IO segment " << seg; 212#endif 213 // Single PCI host controller. 214 if ((read8_field(Address(seg), PCI_HEADER_TYPE) & 0x80) == 0) { 215 enumerate_bus(-1, 0, callback); 216 return; 217 } 218 219 // Multiple PCI host controllers. 220 for (u8 function = 0; function < 8; ++function) { 221 if (read16_field(Address(seg, 0, 0, function), PCI_VENDOR_ID) == PCI_NONE) 222 break; 223 enumerate_bus(-1, function, callback); 224 } 225 } 226} 227 228MMIOSegment::MMIOSegment(PhysicalAddress segment_base_addr, u8 start_bus, u8 end_bus) 229 : m_base_addr(segment_base_addr) 230 , m_start_bus(start_bus) 231 , m_end_bus(end_bus) 232{ 233} 234 235u8 MMIOSegment::get_start_bus() 236{ 237 return m_start_bus; 238} 239 240u8 MMIOSegment::get_end_bus() 241{ 242 return m_end_bus; 243} 244 245size_t MMIOSegment::get_size() 246{ 247 return (PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE * PCI_MAX_DEVICES_PER_BUS * (get_end_bus() - get_start_bus())); 248} 249 250PhysicalAddress MMIOSegment::get_paddr() 251{ 252 return m_base_addr; 253} 254 255} 256}