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 "internal_sys_mock.hpp"
18 #include "pci.hpp"
19 #include "pciaccess_mock.hpp"
20 #include "tool_errors.hpp"
21 
22 #include <stdplus/raw.hpp>
23 
24 #include <algorithm>
25 #include <cstdlib>
26 #include <span>
27 #include <string>
28 #include <vector>
29 
30 #include <gtest/gtest.h>
31 
32 namespace host_tool
33 {
34 namespace
35 {
36 
37 using namespace std::string_literals;
38 
39 using ::testing::Assign;
40 using ::testing::ContainerEq;
41 using ::testing::DoAll;
42 using ::testing::Each;
43 using ::testing::Eq;
44 using ::testing::InSequence;
45 using ::testing::NotNull;
46 using ::testing::Return;
47 using ::testing::SetArgPointee;
48 
49 // TODO: switch to ConainerEq for C++20
50 MATCHER_P(SpanEq, s, "")
51 {
52     return arg.size() == s.size() && !memcmp(arg.data(), s.data(), s.size());
53 }
54 
55 MATCHER_P(PciIdMatch, m, "")
56 {
57     return (arg->vendor_id == m->vendor_id && arg->device_id == m->device_id &&
58             arg->subvendor_id == m->subvendor_id &&
59             arg->subdevice_id == m->subdevice_id);
60 }
61 
62 pci_device_iterator* mockIter = reinterpret_cast<pci_device_iterator*>(0x42);
63 
64 constexpr pciaddr_t mockBaseAddr = 0xdeadbeef;
65 constexpr pciaddr_t mockRegionSize = 0x20000;
66 
67 class Device
68 {
69   public:
70     virtual ~Device() = default;
71     virtual const struct pci_id_match* getMatch() const = 0;
72     virtual struct pci_device getDevice() const = 0;
expectSetup(PciAccessMock &,const struct pci_device &) const73     virtual void expectSetup(PciAccessMock&, const struct pci_device&) const {};
74     virtual std::unique_ptr<PciBridgeIntf>
75         getBridge(PciAccess* pci, bool skipBridgeDisable = false) const = 0;
76     virtual std::string getName() const = 0;
77 };
78 
79 class NuvotonDevice : public Device
80 {
81   public:
getMatch() const82     const struct pci_id_match* getMatch() const override
83     {
84         return &match;
85     }
86 
getDevice() const87     struct pci_device getDevice() const override
88     {
89         struct pci_device dev;
90 
91         dev.vendor_id = match.vendor_id;
92         dev.device_id = match.device_id;
93 
94         dev.regions[0].is_IO = false;
95         dev.regions[0].base_addr = mockBaseAddr;
96         dev.regions[0].size = mockRegionSize;
97 
98         return dev;
99     }
100 
expectSetup(PciAccessMock & pciMock,const struct pci_device & dev) const101     void expectSetup(PciAccessMock& pciMock,
102                      const struct pci_device& dev) const override
103     {
104         static constexpr std::uint8_t defaultVal = 0x40;
105 
106         InSequence in;
107 
108         EXPECT_CALL(pciMock,
109                     pci_device_cfg_read_u8(Eq(&dev), NotNull(), config))
110             .WillOnce(DoAll(SetArgPointee<1>(defaultVal), Return(0)));
111         EXPECT_CALL(pciMock, pci_device_cfg_write_u8(
112                                  Eq(&dev), defaultVal | bridgeEnabled, config))
113             .WillOnce(Return(0));
114 
115         EXPECT_CALL(pciMock,
116                     pci_device_cfg_read_u8(Eq(&dev), NotNull(), config))
117             .WillOnce(
118                 DoAll(SetArgPointee<1>(defaultVal | bridgeEnabled), Return(0)));
119         EXPECT_CALL(pciMock,
120                     pci_device_cfg_write_u8(Eq(&dev), defaultVal, config))
121             .WillOnce(Return(0));
122     }
123 
124     std::unique_ptr<PciBridgeIntf>
getBridge(PciAccess * pci,bool skipBridgeDisable=false) const125         getBridge(PciAccess* pci, bool skipBridgeDisable = false) const override
126     {
127         return std::make_unique<NuvotonPciBridge>(pci, skipBridgeDisable);
128     }
129 
getName() const130     std::string getName() const override
131     {
132         return "Nuvoton"s;
133     }
134 
135     /* Offset to the config register */
136     static constexpr int config = 0x04;
137     /* Second bit determines whether bridge is enabled */
138     static constexpr std::uint8_t bridgeEnabled = 0x02;
139 
140   private:
141     static constexpr struct pci_id_match match
142     {
143         0x1050, 0x0750, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0, 0
144     };
145 };
146 
147 class AspeedDevice : public Device
148 {
149   public:
getMatch() const150     const struct pci_id_match* getMatch() const override
151     {
152         return &match;
153     }
154 
getDevice() const155     struct pci_device getDevice() const override
156     {
157         struct pci_device dev;
158 
159         dev.vendor_id = match.vendor_id;
160         dev.device_id = match.device_id;
161 
162         dev.regions[1].is_IO = false;
163         dev.regions[1].base_addr = mockBaseAddr;
164         dev.regions[1].size = mockRegionSize;
165 
166         return dev;
167     }
168 
169     std::unique_ptr<PciBridgeIntf>
getBridge(PciAccess * pci,bool skipBridgeDisable=false) const170         getBridge(PciAccess* pci, bool skipBridgeDisable = false) const override
171     {
172         return std::make_unique<AspeedPciBridge>(pci, skipBridgeDisable);
173     }
174 
getName() const175     std::string getName() const override
176     {
177         return "Aspeed"s;
178     }
179 
180     /* Offset to the config region */
181     static constexpr int config = 0x0f000;
182     /* Lower bit determines whether bridge is enabled */
183     static constexpr std::uint8_t bridgeEnabled = 0x01;
184     /* Offset to the MMIO address configuration */
185     static constexpr int bridge = 0x0f004;
186 
187   private:
188     static constexpr struct pci_id_match match
189     {
190         0x1a03, 0x2000, PCI_MATCH_ANY, PCI_MATCH_ANY, 0, 0, 0
191     };
192 };
193 
194 NuvotonDevice nuvotonDevice;
195 AspeedDevice aspeedDevice;
196 
197 class PciSetupTest : public testing::TestWithParam<Device*>
198 {};
199 
200 /* Handle device not found */
TEST_P(PciSetupTest,NotFound)201 TEST_P(PciSetupTest, NotFound)
202 {
203     PciAccessMock pciMock;
204 
205     InSequence in;
206 
207     EXPECT_CALL(pciMock, pci_id_match_iterator_create(
208                              PciIdMatch(GetParam()->getMatch())))
209         .WillOnce(Return(mockIter));
210     EXPECT_CALL(pciMock, pci_device_next(Eq(mockIter)))
211         .WillOnce(Return(nullptr));
212     EXPECT_CALL(pciMock, pci_iterator_destroy(Eq(mockIter))).Times(1);
213 
214     EXPECT_THROW(GetParam()->getBridge(&pciMock), NotFoundException);
215 }
216 
217 /* Test finding device but probe fails */
TEST_P(PciSetupTest,ProbeFail)218 TEST_P(PciSetupTest, ProbeFail)
219 {
220     PciAccessMock pciMock;
221     struct pci_device dev;
222 
223     EXPECT_CALL(pciMock, pci_id_match_iterator_create(
224                              PciIdMatch(GetParam()->getMatch())))
225         .WillOnce(Return(mockIter));
226     EXPECT_CALL(pciMock, pci_device_next(Eq(mockIter)))
227         .WillOnce(Return(&dev))
228         .WillRepeatedly(Return(nullptr));
229 
230     EXPECT_CALL(pciMock, pci_device_probe(Eq(&dev))).WillOnce(Return(EFAULT));
231 
232     EXPECT_CALL(pciMock, pci_iterator_destroy(Eq(mockIter))).Times(1);
233 
234     EXPECT_THROW(GetParam()->getBridge(&pciMock), std::system_error);
235 }
236 
237 /* Test finding device but mapping fails */
TEST_P(PciSetupTest,MapFail)238 TEST_P(PciSetupTest, MapFail)
239 {
240     PciAccessMock pciMock;
241     struct pci_device dev;
242 
243     EXPECT_CALL(pciMock, pci_id_match_iterator_create(
244                              PciIdMatch(GetParam()->getMatch())))
245         .WillOnce(Return(mockIter));
246     EXPECT_CALL(pciMock, pci_device_next(Eq(mockIter)))
247         .WillOnce(Return(&dev))
248         .WillRepeatedly(Return(nullptr));
249 
250     EXPECT_CALL(pciMock, pci_device_probe(Eq(&dev)))
251         .WillOnce(DoAll(Assign(&dev, GetParam()->getDevice()), Return(0)));
252 
253     EXPECT_CALL(pciMock,
254                 pci_device_map_range(Eq(&dev), mockBaseAddr, mockRegionSize,
255                                      PCI_DEV_MAP_FLAG_WRITABLE, NotNull()))
256         .WillOnce(Return(EFAULT));
257 
258     EXPECT_CALL(pciMock, pci_iterator_destroy(Eq(mockIter))).Times(1);
259 
260     EXPECT_THROW(GetParam()->getBridge(&pciMock), std::system_error);
261 }
262 
263 /* Test finding device but unmapping fails */
TEST_P(PciSetupTest,UnmapFail)264 TEST_P(PciSetupTest, UnmapFail)
265 {
266     PciAccessMock pciMock;
267     struct pci_device dev;
268     std::vector<std::uint8_t> region(mockRegionSize);
269 
270     EXPECT_CALL(pciMock, pci_id_match_iterator_create(
271                              PciIdMatch(GetParam()->getMatch())))
272         .WillOnce(Return(mockIter));
273     EXPECT_CALL(pciMock, pci_device_next(Eq(mockIter)))
274         .WillOnce(Return(&dev))
275         .WillRepeatedly(Return(nullptr));
276 
277     EXPECT_CALL(pciMock, pci_device_probe(Eq(&dev)))
278         .WillOnce(DoAll(Assign(&dev, GetParam()->getDevice()), Return(0)));
279 
280     EXPECT_CALL(pciMock,
281                 pci_device_map_range(Eq(&dev), mockBaseAddr, mockRegionSize,
282                                      PCI_DEV_MAP_FLAG_WRITABLE, NotNull()))
283         .WillOnce(DoAll(SetArgPointee<4>(region.data()), Return(0)));
284 
285     EXPECT_CALL(pciMock, pci_iterator_destroy(Eq(mockIter))).Times(1);
286     EXPECT_CALL(pciMock, pci_device_unmap_range(Eq(&dev), Eq(region.data()),
287                                                 mockRegionSize))
288         .WillOnce(Return(EFAULT));
289 
290     GetParam()->expectSetup(pciMock, dev);
291     // This will print an error but not throw
292     GetParam()->getBridge(&pciMock);
293 }
294 
295 /* Create expectations on pciMock for finding device and mapping memory region
296  */
expectSetup(PciAccessMock & pciMock,struct pci_device & dev,Device * param,std::uint8_t * region,bool deviceExpectations=true)297 void expectSetup(PciAccessMock& pciMock, struct pci_device& dev, Device* param,
298                  std::uint8_t* region, bool deviceExpectations = true)
299 {
300     EXPECT_CALL(pciMock,
301                 pci_id_match_iterator_create(PciIdMatch(param->getMatch())))
302         .WillOnce(Return(mockIter));
303     EXPECT_CALL(pciMock, pci_device_next(Eq(mockIter)))
304         .WillOnce(Return(&dev))
305         .WillRepeatedly(Return(nullptr));
306 
307     EXPECT_CALL(pciMock, pci_device_probe(Eq(&dev)))
308         .WillOnce(DoAll(Assign(&dev, param->getDevice()), Return(0)));
309 
310     EXPECT_CALL(pciMock,
311                 pci_device_map_range(Eq(&dev), mockBaseAddr, mockRegionSize,
312                                      PCI_DEV_MAP_FLAG_WRITABLE, NotNull()))
313         .WillOnce(DoAll(SetArgPointee<4>(region), Return(0)));
314 
315     EXPECT_CALL(pciMock, pci_iterator_destroy(Eq(mockIter))).Times(1);
316     EXPECT_CALL(pciMock,
317                 pci_device_unmap_range(Eq(&dev), Eq(region), mockRegionSize))
318         .WillOnce(Return(0));
319 
320     EXPECT_CALL(pciMock, pci_device_enable(Eq(&dev))).Times(1);
321 
322     if (deviceExpectations)
323         param->expectSetup(pciMock, dev);
324 }
325 
326 /* Test finding device and mapping memory region */
TEST_P(PciSetupTest,Success)327 TEST_P(PciSetupTest, Success)
328 {
329     PciAccessMock pciMock;
330     struct pci_device dev;
331     std::vector<std::uint8_t> region(mockRegionSize);
332 
333     expectSetup(pciMock, dev, GetParam(), region.data());
334 
335     GetParam()->getBridge(&pciMock);
336 }
337 
338 INSTANTIATE_TEST_SUITE_P(Default, PciSetupTest,
339                          ::testing::Values(&nuvotonDevice, &aspeedDevice),
__anond86d8cb60202(const testing::TestParamInfo<Device*>& info) 340                          [](const testing::TestParamInfo<Device*>& info) {
341     return info.param->getName();
342 });
343 
TEST(NuvotonWriteTest,TooLarge)344 TEST(NuvotonWriteTest, TooLarge)
345 {
346     PciAccessMock pciMock;
347     struct pci_device dev;
348     std::vector<std::uint8_t> region(mockRegionSize);
349     std::vector<std::uint8_t> data(0x4001);
350 
351     expectSetup(pciMock, dev, &nuvotonDevice, region.data());
352 
353     std::unique_ptr<PciBridgeIntf> bridge = nuvotonDevice.getBridge(&pciMock);
354     EXPECT_THROW(bridge->write(std::span<std::uint8_t>(data)), ToolException);
355 }
356 
TEST(NuvotonWriteTest,Success)357 TEST(NuvotonWriteTest, Success)
358 {
359     PciAccessMock pciMock;
360     struct pci_device dev;
361     std::vector<std::uint8_t> region(mockRegionSize);
362     std::vector<std::uint8_t> data(0x4000);
363 
364     std::generate(data.begin(), data.end(), std::rand);
365 
366     expectSetup(pciMock, dev, &nuvotonDevice, region.data());
367 
368     std::unique_ptr<PciBridgeIntf> bridge = nuvotonDevice.getBridge(&pciMock);
369     bridge->write(std::span<std::uint8_t>(data));
370 
371     EXPECT_THAT(std::span<uint8_t>(&region[0], data.size()),
372                 SpanEq(std::span<uint8_t>(data)));
373 }
374 
TEST(NuvotonConfigureTest,Success)375 TEST(NuvotonConfigureTest, Success)
376 {
377     PciAccessMock pciMock;
378     struct pci_device dev;
379     std::vector<std::uint8_t> region(mockRegionSize);
380     ipmi_flash::PciConfigResponse config{0x123bea51};
381 
382     expectSetup(pciMock, dev, &nuvotonDevice, region.data());
383 
384     std::unique_ptr<PciBridgeIntf> bridge = nuvotonDevice.getBridge(&pciMock);
385     bridge->configure(config);
386 
387     /* No effect from calling configure(), so the whole region should be 0 */
388     EXPECT_THAT(region, Each(0));
389 }
390 
TEST(NuvotonDataLengthTest,Success)391 TEST(NuvotonDataLengthTest, Success)
392 {
393     PciAccessMock pciMock;
394     struct pci_device dev;
395     std::vector<std::uint8_t> region(mockRegionSize);
396 
397     expectSetup(pciMock, dev, &nuvotonDevice, region.data());
398 
399     std::unique_ptr<PciBridgeIntf> bridge = nuvotonDevice.getBridge(&pciMock);
400     EXPECT_EQ(bridge->getDataLength(), 0x4000);
401 }
402 
403 /* Make sure config register is left alone if the bridge is already enabled */
TEST(NuvotonBridgeTest,AlreadyEnabledSuccess)404 TEST(NuvotonBridgeTest, AlreadyEnabledSuccess)
405 {
406     PciAccessMock pciMock;
407     struct pci_device dev;
408     std::vector<std::uint8_t> region(mockRegionSize);
409 
410     constexpr std::uint8_t defaultVal = 0x40;
411 
412     /* Only set standard expectations; not those from nuvotonDevice */
413     expectSetup(pciMock, dev, &nuvotonDevice, region.data(), false);
414 
415     {
416         InSequence in;
417 
418         EXPECT_CALL(pciMock, pci_device_cfg_read_u8(Eq(&dev), NotNull(),
419                                                     NuvotonDevice::config))
420             .WillOnce(DoAll(
421                 SetArgPointee<1>(defaultVal | NuvotonDevice::bridgeEnabled),
422                 Return(0)));
423 
424         EXPECT_CALL(pciMock, pci_device_cfg_read_u8(Eq(&dev), NotNull(),
425                                                     NuvotonDevice::config))
426             .WillOnce(DoAll(
427                 SetArgPointee<1>(defaultVal | NuvotonDevice::bridgeEnabled),
428                 Return(0)));
429         EXPECT_CALL(pciMock, pci_device_cfg_write_u8(Eq(&dev), defaultVal,
430                                                      NuvotonDevice::config))
431             .WillOnce(Return(0));
432     }
433 
434     nuvotonDevice.getBridge(&pciMock);
435 }
436 
437 /* Read fails when attempting to setup the bridge */
TEST(NuvotonBridgeTest,ReadSetupFail)438 TEST(NuvotonBridgeTest, ReadSetupFail)
439 {
440     PciAccessMock pciMock;
441     struct pci_device dev;
442     std::vector<std::uint8_t> region(mockRegionSize);
443 
444     /* Only set standard expectations; not those from nuvotonDevice */
445     expectSetup(pciMock, dev, &nuvotonDevice, region.data(), false);
446 
447     EXPECT_CALL(pciMock, pci_device_cfg_read_u8(Eq(&dev), NotNull(),
448                                                 NuvotonDevice::config))
449         .WillOnce(Return(EFAULT));
450 
451     EXPECT_THROW(nuvotonDevice.getBridge(&pciMock), std::system_error);
452 }
453 
454 /* Write fails when attempting to setup the bridge */
TEST(NuvotonBridgeTest,WriteSetupFail)455 TEST(NuvotonBridgeTest, WriteSetupFail)
456 {
457     PciAccessMock pciMock;
458     struct pci_device dev;
459     std::vector<std::uint8_t> region(mockRegionSize);
460 
461     constexpr std::uint8_t defaultVal = 0x40;
462 
463     /* Only set standard expectations; not those from nuvotonDevice */
464     expectSetup(pciMock, dev, &nuvotonDevice, region.data(), false);
465 
466     EXPECT_CALL(pciMock, pci_device_cfg_read_u8(Eq(&dev), NotNull(),
467                                                 NuvotonDevice::config))
468         .WillOnce(DoAll(SetArgPointee<1>(defaultVal), Return(0)));
469     EXPECT_CALL(pciMock,
470                 pci_device_cfg_write_u8(
471                     Eq(&dev), defaultVal | NuvotonDevice::bridgeEnabled,
472                     NuvotonDevice::config))
473         .WillOnce(Return(EFAULT));
474 
475     EXPECT_THROW(nuvotonDevice.getBridge(&pciMock), std::system_error);
476 }
477 
478 /* Read fails when attempting to disable the bridge */
TEST(NuvotonBridgeTest,ReadDisableFail)479 TEST(NuvotonBridgeTest, ReadDisableFail)
480 {
481     PciAccessMock pciMock;
482     struct pci_device dev;
483     std::vector<std::uint8_t> region(mockRegionSize);
484 
485     constexpr std::uint8_t defaultVal = 0x40;
486 
487     /* Only set standard expectations; not those from nuvotonDevice */
488     expectSetup(pciMock, dev, &nuvotonDevice, region.data(), false);
489 
490     {
491         InSequence in;
492 
493         EXPECT_CALL(pciMock, pci_device_cfg_read_u8(Eq(&dev), NotNull(),
494                                                     NuvotonDevice::config))
495             .WillOnce(DoAll(SetArgPointee<1>(defaultVal), Return(0)));
496         EXPECT_CALL(pciMock,
497                     pci_device_cfg_write_u8(
498                         Eq(&dev), defaultVal | NuvotonDevice::bridgeEnabled,
499                         NuvotonDevice::config))
500             .WillOnce(Return(0));
501 
502         EXPECT_CALL(pciMock, pci_device_cfg_read_u8(Eq(&dev), NotNull(),
503                                                     NuvotonDevice::config))
504             .WillOnce(Return(EFAULT));
505     }
506 
507     nuvotonDevice.getBridge(&pciMock);
508 }
509 
510 /* Write fails when attempting to disable the bridge */
TEST(NuvotonBridgeTest,WriteDisableFail)511 TEST(NuvotonBridgeTest, WriteDisableFail)
512 {
513     PciAccessMock pciMock;
514     struct pci_device dev;
515     std::vector<std::uint8_t> region(mockRegionSize);
516 
517     constexpr std::uint8_t defaultVal = 0x40;
518 
519     /* Only set standard expectations; not those from nuvotonDevice */
520     expectSetup(pciMock, dev, &nuvotonDevice, region.data(), false);
521 
522     {
523         InSequence in;
524 
525         EXPECT_CALL(pciMock, pci_device_cfg_read_u8(Eq(&dev), NotNull(),
526                                                     NuvotonDevice::config))
527             .WillOnce(DoAll(SetArgPointee<1>(defaultVal), Return(0)));
528         EXPECT_CALL(pciMock,
529                     pci_device_cfg_write_u8(
530                         Eq(&dev), defaultVal | NuvotonDevice::bridgeEnabled,
531                         NuvotonDevice::config))
532             .WillOnce(Return(0));
533 
534         EXPECT_CALL(pciMock, pci_device_cfg_read_u8(Eq(&dev), NotNull(),
535                                                     NuvotonDevice::config))
536             .WillOnce(DoAll(
537                 SetArgPointee<1>(defaultVal | NuvotonDevice::bridgeEnabled),
538                 Return(0)));
539         EXPECT_CALL(pciMock, pci_device_cfg_write_u8(Eq(&dev), defaultVal,
540                                                      NuvotonDevice::config))
541             .WillOnce(Return(EFAULT));
542     }
543 
544     nuvotonDevice.getBridge(&pciMock);
545 }
546 
547 /* Make sure the bridge gets enabled when needed */
TEST(NuvotonBridgeTest,NotEnabledSuccess)548 TEST(NuvotonBridgeTest, NotEnabledSuccess)
549 {
550     PciAccessMock pciMock;
551     struct pci_device dev;
552     std::vector<std::uint8_t> region(mockRegionSize);
553 
554     expectSetup(pciMock, dev, &nuvotonDevice, region.data());
555     nuvotonDevice.getBridge(&pciMock);
556 }
557 
558 /* Make sure it skips the disable bridge call when skipBridgeDisable is true */
TEST(NuvotonBridgeTest,SkipDisable)559 TEST(NuvotonBridgeTest, SkipDisable)
560 {
561     PciAccessMock pciMock;
562     struct pci_device dev;
563     std::vector<std::uint8_t> region(mockRegionSize);
564 
565     constexpr std::uint8_t defaultVal = 0x40;
566 
567     /* Only set standard expectations; not those from nuvotonDevice */
568     expectSetup(pciMock, dev, &nuvotonDevice, region.data(), false);
569 
570     {
571         InSequence in;
572 
573         /* Only expect call for enableBridge() */
574         EXPECT_CALL(pciMock, pci_device_cfg_read_u8(Eq(&dev), NotNull(),
575                                                     NuvotonDevice::config))
576             .WillOnce(DoAll(
577                 SetArgPointee<1>(defaultVal | NuvotonDevice::bridgeEnabled),
578                 Return(0)));
579     }
580 
581     /* Setting skipBridgeDisable to true */
582     nuvotonDevice.getBridge(&pciMock, true);
583 }
584 
TEST(AspeedWriteTest,TooLarge)585 TEST(AspeedWriteTest, TooLarge)
586 {
587     PciAccessMock pciMock;
588     struct pci_device dev;
589     std::vector<std::uint8_t> region(mockRegionSize);
590     std::vector<std::uint8_t> data(0x10001);
591 
592     expectSetup(pciMock, dev, &aspeedDevice, region.data());
593 
594     std::unique_ptr<PciBridgeIntf> bridge = aspeedDevice.getBridge(&pciMock);
595     EXPECT_THROW(bridge->write(std::span<std::uint8_t>(data)), ToolException);
596 }
597 
TEST(AspeedWriteTest,Success)598 TEST(AspeedWriteTest, Success)
599 {
600     PciAccessMock pciMock;
601     struct pci_device dev;
602     std::vector<std::uint8_t> region(mockRegionSize);
603     std::vector<std::uint8_t> data(0x10000);
604 
605     std::generate(data.begin(), data.end(), std::rand);
606 
607     expectSetup(pciMock, dev, &aspeedDevice, region.data());
608 
609     std::unique_ptr<PciBridgeIntf> bridge = aspeedDevice.getBridge(&pciMock);
610     bridge->write(std::span<std::uint8_t>(data));
611 
612     EXPECT_THAT(std::span<uint8_t>(&region[0x10000], data.size()),
613                 SpanEq(std::span<uint8_t>(data)));
614 }
615 
TEST(AspeedConfigureTest,Success)616 TEST(AspeedConfigureTest, Success)
617 {
618     PciAccessMock pciMock;
619     struct pci_device dev;
620     std::vector<std::uint8_t> region(mockRegionSize);
621     ipmi_flash::PciConfigResponse config{0x123bea51};
622 
623     expectSetup(pciMock, dev, &aspeedDevice, region.data());
624 
625     std::unique_ptr<PciBridgeIntf> bridge = aspeedDevice.getBridge(&pciMock);
626     bridge->configure(config);
627 
628     auto configSpan = stdplus::raw::asSpan<uint8_t>(config);
629     EXPECT_THAT(
630         std::span<uint8_t>(&region[AspeedDevice::bridge], sizeof(config)),
631         SpanEq(configSpan));
632 }
633 
TEST(AspeedDataLengthTest,Success)634 TEST(AspeedDataLengthTest, Success)
635 {
636     PciAccessMock pciMock;
637     struct pci_device dev;
638     std::vector<std::uint8_t> region(mockRegionSize);
639 
640     expectSetup(pciMock, dev, &aspeedDevice, region.data());
641 
642     std::unique_ptr<PciBridgeIntf> bridge = aspeedDevice.getBridge(&pciMock);
643     EXPECT_EQ(bridge->getDataLength(), 0x10000);
644 }
645 
646 /* Make sure config region is left alone if the bridge is already enabled */
TEST(AspeedBridgeTest,AlreadyEnabledSuccess)647 TEST(AspeedBridgeTest, AlreadyEnabledSuccess)
648 {
649     PciAccessMock pciMock;
650     struct pci_device dev;
651     std::vector<std::uint8_t> region(mockRegionSize);
652 
653     constexpr std::uint8_t defaultVal = 0x42;
654 
655     region[AspeedDevice::config] = defaultVal | AspeedDevice::bridgeEnabled;
656 
657     expectSetup(pciMock, dev, &aspeedDevice, region.data());
658 
659     std::unique_ptr<PciBridgeIntf> bridge = aspeedDevice.getBridge(&pciMock);
660 
661     {
662         std::vector<std::uint8_t> enabledRegion(mockRegionSize);
663         enabledRegion[AspeedDevice::config] = defaultVal |
664                                               AspeedDevice::bridgeEnabled;
665         EXPECT_THAT(region, ContainerEq(enabledRegion));
666     }
667 
668     bridge.reset();
669 
670     {
671         std::vector<std::uint8_t> disabledRegion(mockRegionSize);
672         disabledRegion[AspeedDevice::config] = defaultVal;
673         EXPECT_THAT(region, ContainerEq(disabledRegion));
674     }
675 }
676 
677 /* Make sure the config region remains the same even after cleanup if
678  * skipBridgeDisable is true */
TEST(AspeedBridgeTest,SkipDisable)679 TEST(AspeedBridgeTest, SkipDisable)
680 {
681     PciAccessMock pciMock;
682     struct pci_device dev;
683     std::vector<std::uint8_t> region(mockRegionSize);
684 
685     constexpr std::uint8_t defaultVal = 0x42;
686 
687     region[AspeedDevice::config] = defaultVal | AspeedDevice::bridgeEnabled;
688 
689     expectSetup(pciMock, dev, &aspeedDevice, region.data());
690 
691     /* Setting skipBridgeDisable to true */
692     std::unique_ptr<PciBridgeIntf> bridge = aspeedDevice.getBridge(&pciMock,
693                                                                    true);
694 
695     {
696         std::vector<std::uint8_t> enabledRegion(mockRegionSize);
697         enabledRegion[AspeedDevice::config] = defaultVal |
698                                               AspeedDevice::bridgeEnabled;
699         EXPECT_THAT(region, ContainerEq(enabledRegion));
700     }
701 
702     bridge.reset();
703 
704     {
705         std::vector<std::uint8_t> disabledRegion(mockRegionSize);
706         disabledRegion[AspeedDevice::config] = defaultVal |
707                                                AspeedDevice::bridgeEnabled;
708         EXPECT_THAT(region, ContainerEq(disabledRegion));
709     }
710 }
711 
712 /* Make sure the bridge gets enabled when needed */
TEST(AspeedBridgeTest,NotEnabledSuccess)713 TEST(AspeedBridgeTest, NotEnabledSuccess)
714 {
715     PciAccessMock pciMock;
716     struct pci_device dev;
717     std::vector<std::uint8_t> region(mockRegionSize);
718 
719     constexpr std::uint8_t defaultVal = 0x42;
720 
721     region[AspeedDevice::config] = defaultVal;
722 
723     expectSetup(pciMock, dev, &aspeedDevice, region.data());
724 
725     std::unique_ptr<PciBridgeIntf> bridge = aspeedDevice.getBridge(&pciMock);
726 
727     {
728         std::vector<std::uint8_t> enabledRegion(mockRegionSize);
729         enabledRegion[AspeedDevice::config] = defaultVal |
730                                               AspeedDevice::bridgeEnabled;
731         EXPECT_THAT(region, ContainerEq(enabledRegion));
732     }
733 
734     bridge.reset();
735 
736     {
737         std::vector<std::uint8_t> disabledRegion(mockRegionSize);
738         disabledRegion[AspeedDevice::config] = defaultVal;
739         EXPECT_THAT(region, ContainerEq(disabledRegion));
740     }
741 }
742 
743 } // namespace
744 } // namespace host_tool
745