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