blob: 00b3eb1d5fe782f29e124ba5d47a0a52fcb9355b [file] [log] [blame]
Benjamin Faire5aafa52020-06-05 21:04:24 -07001/*
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
Benjamin Fair20a18092020-06-08 11:12:21 -070017#include "internal_sys_mock.hpp"
Benjamin Faire5aafa52020-06-05 21:04:24 -070018#include "pci.hpp"
Benjamin Fair20a18092020-06-08 11:12:21 -070019#include "pciaccess_mock.hpp"
Benjamin Faire5aafa52020-06-05 21:04:24 -070020#include "tool_errors.hpp"
21
22#include <stdplus/raw.hpp>
23
24#include <algorithm>
25#include <cstdlib>
26#include <string>
27#include <vector>
Benjamin Fair20a18092020-06-08 11:12:21 -070028
29#include <gtest/gtest.h>
30
31namespace host_tool
32{
33namespace
34{
35
Benjamin Faire5aafa52020-06-05 21:04:24 -070036using namespace std::string_literals;
37
38using ::testing::Assign;
39using ::testing::ContainerEq;
40using ::testing::DoAll;
41using ::testing::Each;
42using ::testing::Eq;
43using ::testing::InSequence;
44using ::testing::NotNull;
45using ::testing::Return;
46using ::testing::SetArgPointee;
47
48// TODO: switch to ConainerEq for C++20
49MATCHER_P(SpanEq, s, "")
50{
51 return arg.size() == s.size() && !memcmp(arg.data(), s.data(), s.size());
52}
53
54MATCHER_P(PciIdMatch, m, "")
55{
56 return (arg->vendor_id == m->vendor_id && arg->device_id == m->device_id &&
57 arg->subvendor_id == m->subvendor_id &&
58 arg->subdevice_id == m->subdevice_id);
59}
60
61pci_device_iterator* mockIter = reinterpret_cast<pci_device_iterator*>(0x42);
62
63constexpr pciaddr_t mockBaseAddr = 0xdeadbeef;
64constexpr pciaddr_t mockRegionSize = 0x20000;
65
66class Device
67{
68 public:
69 virtual const struct pci_id_match* getMatch() const = 0;
70 virtual struct pci_device getDevice() const = 0;
71 virtual std::unique_ptr<PciBridgeIntf> getBridge(PciAccess* pci) const = 0;
72 virtual std::string getName() const = 0;
73};
74
75class NuvotonDevice : public Device
76{
77 public:
78 const struct pci_id_match* getMatch() const override
79 {
80 return &match;
81 }
82
83 struct pci_device getDevice() const override
84 {
85 struct pci_device dev;
86
87 dev.vendor_id = match.vendor_id;
88 dev.device_id = match.device_id;
89
90 dev.regions[0].is_IO = false;
91 dev.regions[0].base_addr = mockBaseAddr;
92 dev.regions[0].size = mockRegionSize;
93
94 return dev;
95 }
96
97 std::unique_ptr<PciBridgeIntf> getBridge(PciAccess* pci) const override
98 {
99 return std::make_unique<NuvotonPciBridge>(pci);
100 }
101
102 std::string getName() const override
103 {
104 return "Nuvoton"s;
105 }
106
107 private:
108 static constexpr struct pci_id_match match
109 {
110 0x1050, 0x0750, PCI_MATCH_ANY, PCI_MATCH_ANY
111 };
112};
113
114class AspeedDevice : public Device
115{
116 public:
117 const struct pci_id_match* getMatch() const override
118 {
119 return &match;
120 }
121
122 struct pci_device getDevice() const override
123 {
124 struct pci_device dev;
125
126 dev.vendor_id = match.vendor_id;
127 dev.device_id = match.device_id;
128
129 dev.regions[1].is_IO = false;
130 dev.regions[1].base_addr = mockBaseAddr;
131 dev.regions[1].size = mockRegionSize;
132
133 return dev;
134 }
135
136 std::unique_ptr<PciBridgeIntf> getBridge(PciAccess* pci) const override
137 {
138 return std::make_unique<AspeedPciBridge>(pci);
139 }
140
141 std::string getName() const override
142 {
143 return "Aspeed"s;
144 }
145
146 /* Offset to the config region */
147 static constexpr int config = 0x0f000;
148 /* Lower bit determines whether bridge is enabled */
149 static constexpr std::uint8_t bridgeEnabled = 0x01;
150 /* Offset to the MMIO address configuration */
151 static constexpr int bridge = 0x0f004;
152
153 private:
154 static constexpr struct pci_id_match match
155 {
156 0x1a03, 0x2000, PCI_MATCH_ANY, PCI_MATCH_ANY
157 };
158};
159
160NuvotonDevice nuvotonDevice;
161AspeedDevice aspeedDevice;
162
163class PciSetupTest : public testing::TestWithParam<Device*>
164{};
165
166/* Handle device not found */
167TEST_P(PciSetupTest, NotFound)
Benjamin Fair20a18092020-06-08 11:12:21 -0700168{
169 PciAccessMock pciMock;
170
Benjamin Faire5aafa52020-06-05 21:04:24 -0700171 InSequence in;
172
173 EXPECT_CALL(pciMock, pci_id_match_iterator_create(
174 PciIdMatch(GetParam()->getMatch())))
175 .WillOnce(Return(mockIter));
176 EXPECT_CALL(pciMock, pci_device_next(Eq(mockIter)))
177 .WillOnce(Return(nullptr));
178 EXPECT_CALL(pciMock, pci_iterator_destroy(Eq(mockIter))).Times(1);
179
180 EXPECT_THROW(GetParam()->getBridge(&pciMock), NotFoundException);
181}
182
183/* Test finding device but probe fails */
184TEST_P(PciSetupTest, ProbeFail)
185{
186 PciAccessMock pciMock;
187 struct pci_device dev;
188
189 EXPECT_CALL(pciMock, pci_id_match_iterator_create(
190 PciIdMatch(GetParam()->getMatch())))
191 .WillOnce(Return(mockIter));
192 EXPECT_CALL(pciMock, pci_device_next(Eq(mockIter)))
193 .WillOnce(Return(&dev))
194 .WillRepeatedly(Return(nullptr));
195
196 EXPECT_CALL(pciMock, pci_device_probe(Eq(&dev))).WillOnce(Return(EFAULT));
197
198 EXPECT_CALL(pciMock, pci_iterator_destroy(Eq(mockIter))).Times(1);
199
200 EXPECT_THROW(GetParam()->getBridge(&pciMock), std::system_error);
201}
202
203/* Test finding device but mapping fails */
204TEST_P(PciSetupTest, MapFail)
205{
206 PciAccessMock pciMock;
207 struct pci_device dev;
208
209 EXPECT_CALL(pciMock, pci_id_match_iterator_create(
210 PciIdMatch(GetParam()->getMatch())))
211 .WillOnce(Return(mockIter));
212 EXPECT_CALL(pciMock, pci_device_next(Eq(mockIter)))
213 .WillOnce(Return(&dev))
214 .WillRepeatedly(Return(nullptr));
215
216 EXPECT_CALL(pciMock, pci_device_probe(Eq(&dev)))
217 .WillOnce(DoAll(Assign(&dev, GetParam()->getDevice()), Return(0)));
218
219 EXPECT_CALL(pciMock,
220 pci_device_map_range(Eq(&dev), mockBaseAddr, mockRegionSize,
221 PCI_DEV_MAP_FLAG_WRITABLE, NotNull()))
222 .WillOnce(Return(EFAULT));
223
224 EXPECT_CALL(pciMock, pci_iterator_destroy(Eq(mockIter))).Times(1);
225
226 EXPECT_THROW(GetParam()->getBridge(&pciMock), std::system_error);
227}
228
229/* Test finding device but unmapping fails */
230TEST_P(PciSetupTest, UnmapFail)
231{
232 PciAccessMock pciMock;
233 struct pci_device dev;
234 std::vector<std::uint8_t> region(mockRegionSize);
235
236 EXPECT_CALL(pciMock, pci_id_match_iterator_create(
237 PciIdMatch(GetParam()->getMatch())))
238 .WillOnce(Return(mockIter));
239 EXPECT_CALL(pciMock, pci_device_next(Eq(mockIter)))
240 .WillOnce(Return(&dev))
241 .WillRepeatedly(Return(nullptr));
242
243 EXPECT_CALL(pciMock, pci_device_probe(Eq(&dev)))
244 .WillOnce(DoAll(Assign(&dev, GetParam()->getDevice()), Return(0)));
245
246 EXPECT_CALL(pciMock,
247 pci_device_map_range(Eq(&dev), mockBaseAddr, mockRegionSize,
248 PCI_DEV_MAP_FLAG_WRITABLE, NotNull()))
249 .WillOnce(DoAll(SetArgPointee<4>(region.data()), Return(0)));
250
251 EXPECT_CALL(pciMock, pci_iterator_destroy(Eq(mockIter))).Times(1);
252 EXPECT_CALL(pciMock, pci_device_unmap_range(Eq(&dev), Eq(region.data()),
253 mockRegionSize))
254 .WillOnce(Return(EFAULT));
255
256 // This will print an error but not throw
257 GetParam()->getBridge(&pciMock);
258}
259
260/* Create expectations on pciMock for finding device and mapping memory region
261 */
262void expectSetup(PciAccessMock& pciMock, struct pci_device& dev, Device* param,
263 std::uint8_t* region)
264{
265 EXPECT_CALL(pciMock,
266 pci_id_match_iterator_create(PciIdMatch(param->getMatch())))
267 .WillOnce(Return(mockIter));
268 EXPECT_CALL(pciMock, pci_device_next(Eq(mockIter)))
269 .WillOnce(Return(&dev))
270 .WillRepeatedly(Return(nullptr));
271
272 EXPECT_CALL(pciMock, pci_device_probe(Eq(&dev)))
273 .WillOnce(DoAll(Assign(&dev, param->getDevice()), Return(0)));
274
275 EXPECT_CALL(pciMock,
276 pci_device_map_range(Eq(&dev), mockBaseAddr, mockRegionSize,
277 PCI_DEV_MAP_FLAG_WRITABLE, NotNull()))
278 .WillOnce(DoAll(SetArgPointee<4>(region), Return(0)));
279
280 EXPECT_CALL(pciMock, pci_iterator_destroy(Eq(mockIter))).Times(1);
281 EXPECT_CALL(pciMock,
282 pci_device_unmap_range(Eq(&dev), Eq(region), mockRegionSize))
283 .WillOnce(Return(0));
284}
285
286/* Test finding device and mapping memory region */
287TEST_P(PciSetupTest, Success)
288{
289 PciAccessMock pciMock;
290 struct pci_device dev;
291 std::vector<std::uint8_t> region(mockRegionSize);
292
293 expectSetup(pciMock, dev, GetParam(), region.data());
294
295 GetParam()->getBridge(&pciMock);
296}
297
298INSTANTIATE_TEST_SUITE_P(Default, PciSetupTest,
299 ::testing::Values(&nuvotonDevice, &aspeedDevice),
300 [](const testing::TestParamInfo<Device*>& info) {
301 return info.param->getName();
302 });
303
304TEST(NuvotonWriteTest, TooLarge)
305{
306 PciAccessMock pciMock;
307 struct pci_device dev;
308 std::vector<std::uint8_t> region(mockRegionSize);
309 std::vector<std::uint8_t> data(0x4001);
310
311 expectSetup(pciMock, dev, &nuvotonDevice, region.data());
312
313 std::unique_ptr<PciBridgeIntf> bridge = nuvotonDevice.getBridge(&pciMock);
314 EXPECT_THROW(bridge->write(stdplus::span<std::uint8_t>(data)),
315 ToolException);
316}
317
318TEST(NuvotonWriteTest, Success)
319{
320 PciAccessMock pciMock;
321 struct pci_device dev;
322 std::vector<std::uint8_t> region(mockRegionSize);
323 std::vector<std::uint8_t> data(0x4000);
324
325 std::generate(data.begin(), data.end(), std::rand);
326
327 expectSetup(pciMock, dev, &nuvotonDevice, region.data());
328
329 std::unique_ptr<PciBridgeIntf> bridge = nuvotonDevice.getBridge(&pciMock);
330 bridge->write(stdplus::span<std::uint8_t>(data));
331
332 EXPECT_THAT(stdplus::span<uint8_t>(&region[0], data.size()),
333 SpanEq(stdplus::span<uint8_t>(data)));
334}
335
336TEST(NuvotonConfigureTest, Success)
337{
338 PciAccessMock pciMock;
339 struct pci_device dev;
340 std::vector<std::uint8_t> region(mockRegionSize);
341 ipmi_flash::PciConfigResponse config{0x123bea51};
342
343 expectSetup(pciMock, dev, &nuvotonDevice, region.data());
344
345 std::unique_ptr<PciBridgeIntf> bridge = nuvotonDevice.getBridge(&pciMock);
346 bridge->configure(config);
347
348 /* No effect from calling configure(), so the whole region should be 0 */
349 EXPECT_THAT(region, Each(0));
350}
351
352TEST(NuvotonDataLengthTest, Success)
353{
354 PciAccessMock pciMock;
355 struct pci_device dev;
356 std::vector<std::uint8_t> region(mockRegionSize);
357
358 expectSetup(pciMock, dev, &nuvotonDevice, region.data());
359
360 std::unique_ptr<PciBridgeIntf> bridge = nuvotonDevice.getBridge(&pciMock);
361 EXPECT_EQ(bridge->getDataLength(), 0x4000);
362}
363
364TEST(AspeedWriteTest, TooLarge)
365{
366 PciAccessMock pciMock;
367 struct pci_device dev;
368 std::vector<std::uint8_t> region(mockRegionSize);
369 std::vector<std::uint8_t> data(0x10001);
370
371 expectSetup(pciMock, dev, &aspeedDevice, region.data());
372
373 std::unique_ptr<PciBridgeIntf> bridge = aspeedDevice.getBridge(&pciMock);
374 EXPECT_THROW(bridge->write(stdplus::span<std::uint8_t>(data)),
375 ToolException);
376}
377
378TEST(AspeedWriteTest, Success)
379{
380 PciAccessMock pciMock;
381 struct pci_device dev;
382 std::vector<std::uint8_t> region(mockRegionSize);
383 std::vector<std::uint8_t> data(0x10000);
384
385 std::generate(data.begin(), data.end(), std::rand);
386
387 expectSetup(pciMock, dev, &aspeedDevice, region.data());
388
389 std::unique_ptr<PciBridgeIntf> bridge = aspeedDevice.getBridge(&pciMock);
390 bridge->write(stdplus::span<std::uint8_t>(data));
391
392 EXPECT_THAT(stdplus::span<uint8_t>(&region[0x10000], data.size()),
393 SpanEq(stdplus::span<uint8_t>(data)));
394}
395
396TEST(AspeedConfigureTest, Success)
397{
398 PciAccessMock pciMock;
399 struct pci_device dev;
400 std::vector<std::uint8_t> region(mockRegionSize);
401 ipmi_flash::PciConfigResponse config{0x123bea51};
402
403 expectSetup(pciMock, dev, &aspeedDevice, region.data());
404
405 std::unique_ptr<PciBridgeIntf> bridge = aspeedDevice.getBridge(&pciMock);
406 bridge->configure(config);
407
408 auto configSpan = stdplus::raw::asSpan<uint8_t>(config);
409 EXPECT_THAT(
410 stdplus::span<uint8_t>(&region[AspeedDevice::bridge], sizeof(config)),
411 SpanEq(configSpan));
412}
413
414TEST(AspeedDataLengthTest, Success)
415{
416 PciAccessMock pciMock;
417 struct pci_device dev;
418 std::vector<std::uint8_t> region(mockRegionSize);
419
420 expectSetup(pciMock, dev, &aspeedDevice, region.data());
421
422 std::unique_ptr<PciBridgeIntf> bridge = aspeedDevice.getBridge(&pciMock);
423 EXPECT_EQ(bridge->getDataLength(), 0x10000);
424}
425
426/* Make sure config region is left alone if the bridge is already enabled */
427TEST(AspeedBridgeTest, AlreadyEnabledSuccess)
428{
429 PciAccessMock pciMock;
430 struct pci_device dev;
431 std::vector<std::uint8_t> region(mockRegionSize);
432
433 constexpr std::uint8_t defaultVal = 0x42;
434
435 region[AspeedDevice::config] = defaultVal | AspeedDevice::bridgeEnabled;
436
437 expectSetup(pciMock, dev, &aspeedDevice, region.data());
438
439 std::unique_ptr<PciBridgeIntf> bridge = aspeedDevice.getBridge(&pciMock);
440
441 {
442 std::vector<std::uint8_t> enabledRegion(mockRegionSize);
443 enabledRegion[AspeedDevice::config] =
444 defaultVal | AspeedDevice::bridgeEnabled;
445 EXPECT_THAT(region, ContainerEq(enabledRegion));
446 }
447
448 bridge.reset();
449
450 {
451 std::vector<std::uint8_t> disabledRegion(mockRegionSize);
452 disabledRegion[AspeedDevice::config] = defaultVal;
453 EXPECT_THAT(region, ContainerEq(disabledRegion));
454 }
455}
456
457/* Make sure the bridge gets enabled when needed */
458TEST(AspeedBridgeTest, NotEnabledSuccess)
459{
460 PciAccessMock pciMock;
461 struct pci_device dev;
462 std::vector<std::uint8_t> region(mockRegionSize);
463
464 constexpr std::uint8_t defaultVal = 0x42;
465
466 region[AspeedDevice::config] = defaultVal;
467
468 expectSetup(pciMock, dev, &aspeedDevice, region.data());
469
470 std::unique_ptr<PciBridgeIntf> bridge = aspeedDevice.getBridge(&pciMock);
471
472 {
473 std::vector<std::uint8_t> enabledRegion(mockRegionSize);
474 enabledRegion[AspeedDevice::config] =
475 defaultVal | AspeedDevice::bridgeEnabled;
476 EXPECT_THAT(region, ContainerEq(enabledRegion));
477 }
478
479 bridge.reset();
480
481 {
482 std::vector<std::uint8_t> disabledRegion(mockRegionSize);
483 disabledRegion[AspeedDevice::config] = defaultVal;
484 EXPECT_THAT(region, ContainerEq(disabledRegion));
485 }
Benjamin Fair20a18092020-06-08 11:12:21 -0700486}
487
488} // namespace
489} // namespace host_tool