1 /* 2 * Copyright 2020 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "pci.hpp" 18 19 #include "tool_errors.hpp" 20 21 extern "C" 22 { 23 #include <pciaccess.h> 24 } // extern "C" 25 26 #include "helper.hpp" 27 28 #include <fmt/format.h> 29 30 #include <stdplus/handle/managed.hpp> 31 32 #include <cstring> 33 #include <span> 34 #include <system_error> 35 36 namespace host_tool 37 { 38 39 namespace 40 { 41 42 /** @brief RAII wrapper and its destructor for creating a pci_device_iterator */ 43 static void closeIt(struct pci_device_iterator*&& it, 44 const PciAccess* const& pci) 45 { 46 pci->pci_iterator_destroy(it); 47 } 48 using It = stdplus::Managed<struct pci_device_iterator*, 49 const PciAccess* const>::Handle<closeIt>; 50 51 } // namespace 52 53 PciAccessBridge::PciAccessBridge(const struct pci_id_match* match, int bar, 54 std::size_t dataOffset, std::size_t dataLength, 55 const PciAccess* pci) : 56 dataOffset(dataOffset), 57 dataLength(dataLength), pci(pci) 58 { 59 It it(pci->pci_id_match_iterator_create(match), pci); 60 61 while ((dev = pci->pci_device_next(*it))) 62 { 63 int ret = pci->pci_device_probe(dev); 64 if (ret) 65 { 66 throw std::system_error(ret, std::generic_category(), 67 "Error probing PCI device"); 68 } 69 70 /* Verify it's a memory-based bar. */ 71 if (!dev->regions[bar].is_IO) 72 break; 73 } 74 75 if (!dev) 76 { 77 throw NotFoundException(fmt::format( 78 "PCI device {:#04x}:{:#04x}", match->vendor_id, match->device_id)); 79 } 80 81 std::fprintf(stderr, "Find [0x%x 0x%x] \n", match->vendor_id, 82 match->device_id); 83 std::fprintf(stderr, "bar%d[0x%x] \n", bar, 84 static_cast<unsigned int>(dev->regions[bar].base_addr)); 85 86 size = dev->regions[bar].size; 87 int ret = pci->pci_device_map_range( 88 dev, dev->regions[bar].base_addr, dev->regions[bar].size, 89 PCI_DEV_MAP_FLAG_WRITABLE, reinterpret_cast<void**>(&addr)); 90 if (ret) 91 { 92 throw std::system_error(ret, std::generic_category(), 93 "Error mapping PCI device memory"); 94 } 95 } 96 97 PciAccessBridge::~PciAccessBridge() 98 { 99 int ret = pci->pci_device_unmap_range(dev, addr, size); 100 101 if (ret) 102 { 103 std::fprintf(stderr, "Error while unmapping PCI device memory: %s\n", 104 std::strerror(ret)); 105 } 106 } 107 108 void PciAccessBridge::write(const std::span<const std::uint8_t> data) 109 { 110 if (data.size() > dataLength) 111 { 112 throw ToolException( 113 fmt::format("Write of {} bytes exceeds maximum of {}", data.size(), 114 dataLength)); 115 } 116 117 memcpyAligned(addr + dataOffset, data.data(), data.size()); 118 } 119 120 void NuvotonPciBridge::enableBridge() 121 { 122 std::uint8_t value; 123 int ret; 124 125 /* TODO: pci_device_disable support is missing in libpciaccess. Add it 126 * to the disableBridge() once it is available. 127 * https://gitlab.freedesktop.org/xorg/lib/libpciaccess/-/merge_requests/17 128 */ 129 130 pci->pci_device_enable(dev); 131 132 /* We need to retain this direct write to config space eventhough 133 * pci_device_enable() should do it. Because currently disabling is done 134 * through write to config space and not done through the proper api. 135 * So libpciaccess ref count does not reset on disable. The 136 * pci_device_enable() above will not do anything the second time. 137 */ 138 ret = pci->pci_device_cfg_read_u8(dev, &value, bridge); 139 if (ret) 140 { 141 throw std::system_error(ret, std::generic_category(), 142 "Error reading bridge status"); 143 } 144 145 if (value & bridgeEnabled) 146 { 147 std::fprintf(stderr, "Bridge already enabled\n"); 148 return; 149 } 150 151 value |= bridgeEnabled; 152 153 ret = pci->pci_device_cfg_write_u8(dev, value, bridge); 154 if (ret) 155 { 156 throw std::system_error(ret, std::generic_category(), 157 "Error enabling bridge"); 158 } 159 } 160 161 void NuvotonPciBridge::disableBridge() 162 { 163 std::uint8_t value; 164 int ret; 165 166 ret = pci->pci_device_cfg_read_u8(dev, &value, bridge); 167 if (ret) 168 { 169 std::fprintf(stderr, "Error reading bridge status: %s\n", 170 std::strerror(ret)); 171 return; 172 } 173 value &= ~bridgeEnabled; 174 175 ret = pci->pci_device_cfg_write_u8(dev, value, bridge); 176 if (ret) 177 { 178 std::fprintf(stderr, "Error disabling bridge: %s\n", 179 std::strerror(ret)); 180 } 181 } 182 183 void AspeedPciBridge::enableBridge() 184 { 185 /* We sent the open command before this, so the window should be open and 186 * the bridge enabled on the BMC. 187 */ 188 std::uint32_t value; 189 190 /* TODO: pci_device_disable support is missing in libpciaccess. Add it 191 * to the disableBridge() once it is available. 192 * https://gitlab.freedesktop.org/xorg/lib/libpciaccess/-/merge_requests/17 193 */ 194 195 pci->pci_device_enable(dev); 196 197 /* We need to retain this direct write to config space eventhough 198 * pci_device_enable() should do it. Because currently disabling is done 199 * through write to config space and not done through the proper api. 200 * So libpciaccess ref count does not reset on disable. The 201 * pci_device_enable() above will not do anything the second time. 202 */ 203 204 std::memcpy(&value, addr + config, sizeof(value)); 205 206 if (0 == (value & bridgeEnabled)) 207 { 208 std::fprintf(stderr, "Bridge not enabled - Enabling from host\n"); 209 210 value |= bridgeEnabled; 211 std::memcpy(addr + config, &value, sizeof(value)); 212 } 213 214 std::fprintf(stderr, "The bridge is enabled!\n"); 215 } 216 217 void AspeedPciBridge::disableBridge() 218 { 219 /* addr is valid if the constructor completed */ 220 221 /* Read current value, and just blindly unset the bit. */ 222 std::uint32_t value; 223 std::memcpy(&value, addr + config, sizeof(value)); 224 225 value &= ~bridgeEnabled; 226 std::memcpy(addr + config, &value, sizeof(value)); 227 } 228 229 void AspeedPciBridge::configure(const ipmi_flash::PciConfigResponse& configResp) 230 { 231 std::fprintf(stderr, "Received address: 0x%x\n", configResp.address); 232 233 /* Configure the mmio to point there. */ 234 std::memcpy(addr + bridge, &configResp.address, sizeof(configResp.address)); 235 } 236 237 } // namespace host_tool 238