| /* |
| * Copyright 2020 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "pci.hpp" |
| |
| #include "tool_errors.hpp" |
| |
| extern "C" |
| { |
| #include <pciaccess.h> |
| } // extern "C" |
| |
| #include "helper.hpp" |
| |
| #include <stdplus/handle/managed.hpp> |
| |
| #include <cstring> |
| #include <format> |
| #include <span> |
| #include <system_error> |
| |
| namespace host_tool |
| { |
| |
| namespace |
| { |
| |
| /** @brief RAII wrapper and its destructor for creating a pci_device_iterator */ |
| static void closeIt(struct pci_device_iterator*&& it, |
| const PciAccess* const& pci) |
| { |
| pci->pci_iterator_destroy(it); |
| } |
| using It = stdplus::Managed<struct pci_device_iterator*, |
| const PciAccess* const>::Handle<closeIt>; |
| |
| } // namespace |
| |
| PciAccessBridge::PciAccessBridge(const struct pci_id_match* match, int bar, |
| std::size_t dataOffset, std::size_t dataLength, |
| const PciAccess* pci) : |
| dataOffset(dataOffset), |
| dataLength(dataLength), pci(pci) |
| { |
| It it(pci->pci_id_match_iterator_create(match), pci); |
| |
| while ((dev = pci->pci_device_next(*it))) |
| { |
| int ret = pci->pci_device_probe(dev); |
| if (ret) |
| { |
| throw std::system_error(ret, std::generic_category(), |
| "Error probing PCI device"); |
| } |
| |
| /* Verify it's a memory-based bar. */ |
| if (!dev->regions[bar].is_IO) |
| break; |
| } |
| |
| if (!dev) |
| { |
| throw NotFoundException(std::format( |
| "PCI device {:#04x}:{:#04x}", match->vendor_id, match->device_id)); |
| } |
| |
| std::fprintf(stderr, "Find [0x%x 0x%x] \n", match->vendor_id, |
| match->device_id); |
| std::fprintf(stderr, "bar%d[0x%x] \n", bar, |
| static_cast<unsigned int>(dev->regions[bar].base_addr)); |
| |
| size = dev->regions[bar].size; |
| int ret = pci->pci_device_map_range( |
| dev, dev->regions[bar].base_addr, dev->regions[bar].size, |
| PCI_DEV_MAP_FLAG_WRITABLE, reinterpret_cast<void**>(&addr)); |
| if (ret) |
| { |
| throw std::system_error(ret, std::generic_category(), |
| "Error mapping PCI device memory"); |
| } |
| } |
| |
| PciAccessBridge::~PciAccessBridge() |
| { |
| int ret = pci->pci_device_unmap_range(dev, addr, size); |
| |
| if (ret) |
| { |
| std::fprintf(stderr, "Error while unmapping PCI device memory: %s\n", |
| std::strerror(ret)); |
| } |
| } |
| |
| void PciAccessBridge::write(const std::span<const std::uint8_t> data) |
| { |
| if (data.size() > dataLength) |
| { |
| throw ToolException( |
| std::format("Write of {} bytes exceeds maximum of {}", data.size(), |
| dataLength)); |
| } |
| |
| memcpyAligned(addr + dataOffset, data.data(), data.size()); |
| } |
| |
| void NuvotonPciBridge::enableBridge() |
| { |
| std::uint8_t value; |
| int ret; |
| |
| /* TODO: pci_device_disable support is missing in libpciaccess. Add it |
| * to the disableBridge() once it is available. |
| * https://gitlab.freedesktop.org/xorg/lib/libpciaccess/-/merge_requests/17 |
| */ |
| |
| pci->pci_device_enable(dev); |
| |
| /* We need to retain this direct write to config space eventhough |
| * pci_device_enable() should do it. Because currently disabling is done |
| * through write to config space and not done through the proper api. |
| * So libpciaccess ref count does not reset on disable. The |
| * pci_device_enable() above will not do anything the second time. |
| */ |
| ret = pci->pci_device_cfg_read_u8(dev, &value, bridge); |
| if (ret) |
| { |
| throw std::system_error(ret, std::generic_category(), |
| "Error reading bridge status"); |
| } |
| |
| if (value & bridgeEnabled) |
| { |
| std::fprintf(stderr, "Bridge already enabled\n"); |
| return; |
| } |
| |
| value |= bridgeEnabled; |
| |
| ret = pci->pci_device_cfg_write_u8(dev, value, bridge); |
| if (ret) |
| { |
| throw std::system_error(ret, std::generic_category(), |
| "Error enabling bridge"); |
| } |
| } |
| |
| void NuvotonPciBridge::disableBridge() |
| { |
| std::uint8_t value; |
| int ret; |
| |
| ret = pci->pci_device_cfg_read_u8(dev, &value, bridge); |
| if (ret) |
| { |
| std::fprintf(stderr, "Error reading bridge status: %s\n", |
| std::strerror(ret)); |
| return; |
| } |
| value &= ~bridgeEnabled; |
| |
| ret = pci->pci_device_cfg_write_u8(dev, value, bridge); |
| if (ret) |
| { |
| std::fprintf(stderr, "Error disabling bridge: %s\n", |
| std::strerror(ret)); |
| } |
| } |
| |
| void AspeedPciBridge::enableBridge() |
| { |
| /* We sent the open command before this, so the window should be open and |
| * the bridge enabled on the BMC. |
| */ |
| std::uint32_t value; |
| |
| /* TODO: pci_device_disable support is missing in libpciaccess. Add it |
| * to the disableBridge() once it is available. |
| * https://gitlab.freedesktop.org/xorg/lib/libpciaccess/-/merge_requests/17 |
| */ |
| |
| pci->pci_device_enable(dev); |
| |
| /* We need to retain this direct write to config space eventhough |
| * pci_device_enable() should do it. Because currently disabling is done |
| * through write to config space and not done through the proper api. |
| * So libpciaccess ref count does not reset on disable. The |
| * pci_device_enable() above will not do anything the second time. |
| */ |
| |
| std::memcpy(&value, addr + config, sizeof(value)); |
| |
| if (0 == (value & bridgeEnabled)) |
| { |
| std::fprintf(stderr, "Bridge not enabled - Enabling from host\n"); |
| |
| value |= bridgeEnabled; |
| std::memcpy(addr + config, &value, sizeof(value)); |
| } |
| |
| std::fprintf(stderr, "The bridge is enabled!\n"); |
| } |
| |
| void AspeedPciBridge::disableBridge() |
| { |
| /* addr is valid if the constructor completed */ |
| |
| /* Read current value, and just blindly unset the bit. */ |
| std::uint32_t value; |
| std::memcpy(&value, addr + config, sizeof(value)); |
| |
| value &= ~bridgeEnabled; |
| std::memcpy(addr + config, &value, sizeof(value)); |
| } |
| |
| void AspeedPciBridge::configure(const ipmi_flash::PciConfigResponse& configResp) |
| { |
| std::fprintf(stderr, "Received address: 0x%x\n", configResp.address); |
| |
| /* Configure the mmio to point there. */ |
| std::memcpy(addr + bridge, &configResp.address, sizeof(configResp.address)); |
| } |
| |
| } // namespace host_tool |