xref: /openbmc/phosphor-ipmi-flash/tools/pci.cpp (revision bcae9002)
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