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