blob: 0efb75d8afce2cc55fca8b4b4528539b9ff33039 [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
Benjamin Faire5aafa52020-06-05 21:04:24 -070026#include <fmt/format.h>
27
28#include <stdplus/handle/managed.hpp>
Benjamin Fairc04c2c52020-06-05 09:14:44 -070029
Patrick Venture24141612019-05-03 17:59:18 -070030#include <cstring>
Benjamin Faire5aafa52020-06-05 21:04:24 -070031#include <system_error>
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070032
33namespace host_tool
34{
35
Benjamin Faire5aafa52020-06-05 21:04:24 -070036namespace
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070037{
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070038
Benjamin Faire5aafa52020-06-05 21:04:24 -070039/** @brief RAII wrapper and its destructor for creating a pci_device_iterator */
40static void closeIt(struct pci_device_iterator*&& it,
41 const PciAccess* const& pci)
42{
43 pci->pci_iterator_destroy(it);
44}
45using It = stdplus::Managed<struct pci_device_iterator*,
46 const PciAccess* const>::Handle<closeIt>;
47
48} // namespace
49
50PciAccessBridge::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)))
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070059 {
Benjamin Faire5aafa52020-06-05 21:04:24 -070060 int ret = pci->pci_device_probe(dev);
61 if (ret)
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070062 {
Benjamin Faire5aafa52020-06-05 21:04:24 -070063 throw std::system_error(ret, std::generic_category(),
64 "Error probing PCI device");
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070065 }
Benjamin Fairc04c2c52020-06-05 09:14:44 -070066
Benjamin Faire5aafa52020-06-05 21:04:24 -070067 /* Verify it's a memory-based bar. */
68 if (!dev->regions[bar].is_IO)
69 break;
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -070070 }
71
Benjamin Faire5aafa52020-06-05 21:04:24 -070072 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
94PciAccessBridge::~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
105void 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
Benjamin Fairc1a30c02020-06-09 11:46:34 -0700117void NuvotonPciBridge::enableBridge()
118{
119 std::uint8_t value;
120 int ret;
121
122 ret = pci->pci_device_cfg_read_u8(dev, &value, bridge);
123 if (ret)
124 {
125 throw std::system_error(ret, std::generic_category(),
126 "Error reading bridge status");
127 }
128
129 if (value & bridgeEnabled)
130 {
131 std::fprintf(stderr, "Bridge already enabled\n");
132 return;
133 }
134
135 value |= bridgeEnabled;
136
137 ret = pci->pci_device_cfg_write_u8(dev, value, bridge);
138 if (ret)
139 {
140 throw std::system_error(ret, std::generic_category(),
141 "Error enabling bridge");
142 }
143}
144
145void NuvotonPciBridge::disableBridge()
146{
147 std::uint8_t value;
148 int ret;
149
150 ret = pci->pci_device_cfg_read_u8(dev, &value, bridge);
151 if (ret)
152 {
153 std::fprintf(stderr, "Error reading bridge status: %s\n",
154 std::strerror(ret));
155 return;
156 }
157 value &= ~bridgeEnabled;
158
159 ret = pci->pci_device_cfg_write_u8(dev, value, bridge);
160 if (ret)
161 {
162 std::fprintf(stderr, "Error disabling bridge: %s\n",
163 std::strerror(ret));
164 }
165}
166
Benjamin Faire5aafa52020-06-05 21:04:24 -0700167void AspeedPciBridge::enableBridge()
168{
169 /* We sent the open command before this, so the window should be open and
170 * the bridge enabled on the BMC.
171 */
172 std::uint32_t value;
173 std::memcpy(&value, addr + config, sizeof(value));
174
175 if (0 == (value & bridgeEnabled))
176 {
177 std::fprintf(stderr, "Bridge not enabled - Enabling from host\n");
178
179 value |= bridgeEnabled;
180 std::memcpy(addr + config, &value, sizeof(value));
181 }
182
183 std::fprintf(stderr, "The bridge is enabled!\n");
184}
185
186void AspeedPciBridge::disableBridge()
187{
188 /* addr is valid if the constructor completed */
189
190 /* Read current value, and just blindly unset the bit. */
191 std::uint32_t value;
192 std::memcpy(&value, addr + config, sizeof(value));
193
194 value &= ~bridgeEnabled;
195 std::memcpy(addr + config, &value, sizeof(value));
196}
197
198void AspeedPciBridge::configure(const ipmi_flash::PciConfigResponse& configResp)
199{
200 std::fprintf(stderr, "Received address: 0x%x\n", configResp.address);
201
202 /* Configure the mmio to point there. */
203 std::memcpy(addr + bridge, &configResp.address, sizeof(configResp.address));
Patrick Ventureb5bf0fc2019-05-03 14:33:49 -0700204}
205
206} // namespace host_tool