blob: 8403771648e2254da7195697d7dce9b321fab600 [file] [log] [blame]
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -07001/*
Benjamin Faire5aafa52020-06-05 21:04:24 -07002 * Copyright 2020 Google Inc.
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -07003 *
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
Benjamin Faire5aafa52020-06-05 21:04:24 -070019#include "tool_errors.hpp"
20
Patrick Venture9b37b092020-05-28 20:58:57 -070021extern "C"
22{
Benjamin Fairc04c2c52020-06-05 09:14:44 -070023#include <pciaccess.h>
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070024} // extern "C"
25
Vivekanand Veeracholanc7fa2c22021-02-18 18:05:41 -080026#include "helper.hpp"
27
Benjamin Faire5aafa52020-06-05 21:04:24 -070028#include <fmt/format.h>
29
30#include <stdplus/handle/managed.hpp>
Benjamin Fairc04c2c52020-06-05 09:14:44 -070031
Patrick Venture24141612019-05-03 17:59:18 -070032#include <cstring>
Patrick Williams28c00d62022-04-27 08:37:13 -050033#include <span>
Benjamin Faire5aafa52020-06-05 21:04:24 -070034#include <system_error>
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070035
36namespace host_tool
37{
38
Benjamin Faire5aafa52020-06-05 21:04:24 -070039namespace
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070040{
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070041
Benjamin Faire5aafa52020-06-05 21:04:24 -070042/** @brief RAII wrapper and its destructor for creating a pci_device_iterator */
43static void closeIt(struct pci_device_iterator*&& it,
44 const PciAccess* const& pci)
45{
46 pci->pci_iterator_destroy(it);
47}
48using It = stdplus::Managed<struct pci_device_iterator*,
49 const PciAccess* const>::Handle<closeIt>;
50
51} // namespace
52
53PciAccessBridge::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)))
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070062 {
Benjamin Faire5aafa52020-06-05 21:04:24 -070063 int ret = pci->pci_device_probe(dev);
64 if (ret)
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070065 {
Benjamin Faire5aafa52020-06-05 21:04:24 -070066 throw std::system_error(ret, std::generic_category(),
67 "Error probing PCI device");
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070068 }
Benjamin Fairc04c2c52020-06-05 09:14:44 -070069
Benjamin Faire5aafa52020-06-05 21:04:24 -070070 /* Verify it's a memory-based bar. */
71 if (!dev->regions[bar].is_IO)
72 break;
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070073 }
74
Benjamin Faire5aafa52020-06-05 21:04:24 -070075 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
97PciAccessBridge::~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
Patrick Williams28c00d62022-04-27 08:37:13 -0500108void PciAccessBridge::write(const std::span<const std::uint8_t> data)
Benjamin Faire5aafa52020-06-05 21:04:24 -0700109{
110 if (data.size() > dataLength)
111 {
112 throw ToolException(
113 fmt::format("Write of {} bytes exceeds maximum of {}", data.size(),
114 dataLength));
115 }
116
Vivekanand Veeracholanc7fa2c22021-02-18 18:05:41 -0800117 memcpyAligned(addr + dataOffset, data.data(), data.size());
Benjamin Faire5aafa52020-06-05 21:04:24 -0700118}
119
Benjamin Fairc1a30c02020-06-09 11:46:34 -0700120void NuvotonPciBridge::enableBridge()
121{
122 std::uint8_t value;
123 int ret;
124
Vivekanand Veeracholan55b1a712021-02-18 12:06:37 -0800125 /* 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 */
Benjamin Fairc1a30c02020-06-09 11:46:34 -0700138 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
161void 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
Benjamin Faire5aafa52020-06-05 21:04:24 -0700183void 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;
Vivekanand Veeracholan55b1a712021-02-18 12:06:37 -0800189
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
Benjamin Faire5aafa52020-06-05 21:04:24 -0700204 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
217void 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
229void 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));
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -0700235}
236
237} // namespace host_tool