blob: a6bc74dc9657ba07a3a41b6efe4609ceb286d738 [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 <stdplus/handle/managed.hpp>
Benjamin Fairc04c2c52020-06-05 09:14:44 -070029
Patrick Venture24141612019-05-03 17:59:18 -070030#include <cstring>
Patrick Williams14c78ed2023-07-16 18:02:26 -050031#include <format>
Patrick Williams28c00d62022-04-27 08:37:13 -050032#include <span>
Benjamin Faire5aafa52020-06-05 21:04:24 -070033#include <system_error>
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070034
35namespace host_tool
36{
37
Benjamin Faire5aafa52020-06-05 21:04:24 -070038namespace
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070039{
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070040
Benjamin Faire5aafa52020-06-05 21:04:24 -070041/** @brief RAII wrapper and its destructor for creating a pci_device_iterator */
42static void closeIt(struct pci_device_iterator*&& it,
43 const PciAccess* const& pci)
44{
45 pci->pci_iterator_destroy(it);
46}
47using It = stdplus::Managed<struct pci_device_iterator*,
48 const PciAccess* const>::Handle<closeIt>;
49
50} // namespace
51
52PciAccessBridge::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)))
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070061 {
Benjamin Faire5aafa52020-06-05 21:04:24 -070062 int ret = pci->pci_device_probe(dev);
63 if (ret)
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070064 {
Benjamin Faire5aafa52020-06-05 21:04:24 -070065 throw std::system_error(ret, std::generic_category(),
66 "Error probing PCI device");
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070067 }
Benjamin Fairc04c2c52020-06-05 09:14:44 -070068
Benjamin Faire5aafa52020-06-05 21:04:24 -070069 /* Verify it's a memory-based bar. */
70 if (!dev->regions[bar].is_IO)
71 break;
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070072 }
73
Benjamin Faire5aafa52020-06-05 21:04:24 -070074 if (!dev)
75 {
Patrick Williams14c78ed2023-07-16 18:02:26 -050076 throw NotFoundException(std::format(
Benjamin Faire5aafa52020-06-05 21:04:24 -070077 "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
96PciAccessBridge::~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
Patrick Williams28c00d62022-04-27 08:37:13 -0500107void PciAccessBridge::write(const std::span<const std::uint8_t> data)
Benjamin Faire5aafa52020-06-05 21:04:24 -0700108{
109 if (data.size() > dataLength)
110 {
111 throw ToolException(
Patrick Williams14c78ed2023-07-16 18:02:26 -0500112 std::format("Write of {} bytes exceeds maximum of {}", data.size(),
Benjamin Faire5aafa52020-06-05 21:04:24 -0700113 dataLength));
114 }
115
Vivekanand Veeracholanc7fa2c22021-02-18 18:05:41 -0800116 memcpyAligned(addr + dataOffset, data.data(), data.size());
Benjamin Faire5aafa52020-06-05 21:04:24 -0700117}
118
Benjamin Fairc1a30c02020-06-09 11:46:34 -0700119void NuvotonPciBridge::enableBridge()
120{
121 std::uint8_t value;
122 int ret;
123
Vivekanand Veeracholan55b1a712021-02-18 12:06:37 -0800124 /* 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
Manojkiran Eda166b4f12024-06-17 10:35:24 +0530131 /* We need to retain this direct write to config space even though
Vivekanand Veeracholan55b1a712021-02-18 12:06:37 -0800132 * 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 */
Benjamin Fairc1a30c02020-06-09 11:46:34 -0700137 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
160void 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
Benjamin Faire5aafa52020-06-05 21:04:24 -0700182void 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;
Vivekanand Veeracholan55b1a712021-02-18 12:06:37 -0800188
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
Manojkiran Eda166b4f12024-06-17 10:35:24 +0530196 /* We need to retain this direct write to config space even though
Vivekanand Veeracholan55b1a712021-02-18 12:06:37 -0800197 * 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
Benjamin Faire5aafa52020-06-05 21:04:24 -0700203 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
216void 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
228void 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));
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -0700234}
235
236} // namespace host_tool