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