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