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