/* * 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 } // extern "C" #include "helper.hpp" #include #include #include #include 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::Handle; } // 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(fmt::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(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(&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 stdplus::span data) { if (data.size() > dataLength) { throw ToolException( fmt::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