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