1 // Copyright 2022 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "commands.hpp"
16 #include "errors.hpp"
17 #include "google_accel_oob.hpp"
18 #include "handler_mock.hpp"
19 
20 #include <ipmid/api.h>
21 
22 #include <gtest/gtest.h>
23 
24 namespace google
25 {
26 namespace ipmi
27 {
28 
29 using ::testing::_;
30 using ::testing::Return;
31 
TEST(GoogleAccelOobTest,DeviceCount_Success)32 TEST(GoogleAccelOobTest, DeviceCount_Success)
33 {
34     ::testing::StrictMock<HandlerMock> h;
35 
36     uint8_t reqBuf[1]; // Could be 0, but zero-length arrays are an extension
37 
38     struct Reply
39     {
40         uint32_t count;
41     } __attribute__((packed));
42 
43     constexpr uint32_t kTestDeviceCount = 2;
44 
45     EXPECT_CALL(h, accelOobDeviceCount()).WillOnce(Return(kTestDeviceCount));
46 
47     Resp r = accelOobDeviceCount(reqBuf, &h);
48 
49     const auto response = std::get<0>(r);
50     EXPECT_EQ(response, IPMI_CC_OK);
51 
52     const auto payload = std::get<1>(r);
53     ASSERT_EQ(payload.has_value(), true);
54     const auto payload_tuple = payload.value();
55     const auto reply_cmd = std::get<0>(payload_tuple);
56     EXPECT_EQ(reply_cmd, SysAccelOobDeviceCount);
57     const auto reply_buff = std::get<1>(payload_tuple);
58     ASSERT_EQ(reply_buff.size(), sizeof(Reply));
59 
60     auto* reply = reinterpret_cast<const Reply*>(reply_buff.data());
61     EXPECT_EQ(reply->count, kTestDeviceCount);
62 }
63 
TEST(GoogleAccelOobTest,DeviceName_Success)64 TEST(GoogleAccelOobTest, DeviceName_Success)
65 {
66     ::testing::StrictMock<HandlerMock> h;
67 
68     struct Request
69     {
70         uint32_t index;
71     } __attribute__((packed));
72 
73     struct Reply
74     {
75         uint32_t index;
76         uint8_t length;
77         char name[1];
78     } __attribute__((packed));
79 
80     constexpr uint32_t kTestDeviceIndex = 0;
81     const std::string kTestDeviceName("testDeviceName");
82 
83     EXPECT_CALL(h, accelOobDeviceName(kTestDeviceIndex))
84         .WillOnce(Return(kTestDeviceName));
85 
86     Request reqBuf{kTestDeviceIndex};
87     Resp r = accelOobDeviceName(
88         std::span(reinterpret_cast<const uint8_t*>(&reqBuf), sizeof(Request)),
89         &h);
90 
91     const auto response = std::get<0>(r);
92     EXPECT_EQ(response, IPMI_CC_OK);
93 
94     const auto payload = std::get<1>(r);
95     ASSERT_EQ(payload.has_value(), true);
96     const auto payload_tuple = payload.value();
97     const auto reply_cmd = std::get<0>(payload_tuple);
98     EXPECT_EQ(reply_cmd, SysAccelOobDeviceName);
99     const auto reply_buff = std::get<1>(payload_tuple);
100     ASSERT_GE(reply_buff.size(), sizeof(Reply));
101 
102     auto* reply = reinterpret_cast<const Reply*>(reply_buff.data());
103     EXPECT_EQ(reply->index, kTestDeviceIndex);
104     EXPECT_EQ(reply->length, kTestDeviceName.length());
105     EXPECT_STREQ(reply->name, kTestDeviceName.c_str());
106 }
107 
TEST(GoogleAccelOobTest,Read_Success)108 TEST(GoogleAccelOobTest, Read_Success)
109 {
110     ::testing::StrictMock<HandlerMock> h;
111 
112     constexpr char kTestDeviceName[] = "testDeviceName";
113     constexpr uint8_t kTestDeviceNameLength =
114         (sizeof(kTestDeviceName) / sizeof(*kTestDeviceName)) - 1;
115     constexpr uint8_t kTestToken = 0xAB;
116     constexpr uint64_t kTestAddress = 0;
117     constexpr uint8_t kTestReadSize = 8;
118     constexpr uint64_t kTestData = 0x12345678;
119 
120     struct Request
121     {
122         uint8_t nameLength;
123         char name[kTestDeviceNameLength];
124         uint8_t token;
125         uint64_t address;
126         uint8_t num_bytes;
127     } __attribute__((packed));
128 
129     struct Reply
130     {
131         uint8_t nameLength;
132         char name[kTestDeviceNameLength];
133         uint8_t token;
134         uint64_t address;
135         uint8_t num_bytes;
136         uint64_t data;
137     } __attribute__((packed));
138 
139     const std::string_view kTestDeviceNameStr(kTestDeviceName,
140                                               kTestDeviceNameLength);
141     EXPECT_CALL(h,
142                 accelOobRead(kTestDeviceNameStr, kTestAddress, kTestReadSize))
143         .WillOnce(Return(kTestData));
144 
145     Request reqBuf{kTestDeviceNameLength, "", kTestToken, kTestAddress,
146                    kTestReadSize};
147     memcpy(reqBuf.name, kTestDeviceName, kTestDeviceNameLength);
148     Resp r = accelOobRead(
149         std::span(reinterpret_cast<const uint8_t*>(&reqBuf), sizeof(Request)),
150         &h);
151 
152     const auto response = std::get<0>(r);
153     EXPECT_EQ(response, IPMI_CC_OK);
154 
155     const auto payload = std::get<1>(r);
156     ASSERT_EQ(payload.has_value(), true);
157     const auto payload_tuple = payload.value();
158     const auto reply_cmd = std::get<0>(payload_tuple);
159     EXPECT_EQ(reply_cmd, SysAccelOobRead);
160     const auto reply_buff = std::get<1>(payload_tuple);
161     ASSERT_GE(reply_buff.size(), sizeof(Reply));
162 
163     auto* reply = reinterpret_cast<const Reply*>(reply_buff.data());
164     EXPECT_EQ(reply->nameLength, kTestDeviceNameLength);
165     EXPECT_EQ(std::string_view(reply->name, reply->nameLength),
166               kTestDeviceNameStr);
167     EXPECT_EQ(reply->token, kTestToken);
168     EXPECT_EQ(reply->address, kTestAddress);
169     EXPECT_EQ(reply->num_bytes, kTestReadSize);
170     EXPECT_EQ(reply->data, kTestData);
171 }
172 
TEST(GoogleAccelOobTest,Write_Success)173 TEST(GoogleAccelOobTest, Write_Success)
174 {
175     ::testing::StrictMock<HandlerMock> h;
176 
177     constexpr char kTestDeviceName[] = "testDeviceName";
178     constexpr uint8_t kTestDeviceNameLength =
179         (sizeof(kTestDeviceName) / sizeof(*kTestDeviceName)) - 1;
180     constexpr uint8_t kTestToken = 0xAB;
181     constexpr uint64_t kTestAddress = 0;
182     constexpr uint8_t kTestWriteSize = 8;
183     constexpr uint64_t kTestData = 0x12345678;
184 
185     struct Request
186     {
187         uint8_t nameLength;
188         char name[kTestDeviceNameLength];
189         uint8_t token;
190         uint64_t address;
191         uint8_t num_bytes;
192         uint64_t data;
193     } __attribute__((packed));
194 
195     struct Reply
196     {
197         uint8_t nameLength;
198         char name[kTestDeviceNameLength];
199         uint8_t token;
200         uint64_t address;
201         uint8_t num_bytes;
202         uint64_t data;
203     } __attribute__((packed));
204 
205     const std::string_view kTestDeviceNameStr(kTestDeviceName,
206                                               kTestDeviceNameLength);
207     EXPECT_CALL(h, accelOobWrite(kTestDeviceNameStr, kTestAddress,
208                                  kTestWriteSize, kTestData))
209         .WillOnce(Return());
210 
211     Request reqBuf{kTestDeviceNameLength, "",       kTestToken, kTestAddress,
212                    kTestWriteSize,        kTestData};
213     memcpy(reqBuf.name, kTestDeviceName, kTestDeviceNameLength);
214     Resp r = accelOobWrite(
215         std::span(reinterpret_cast<const uint8_t*>(&reqBuf), sizeof(Request)),
216         &h);
217 
218     const auto response = std::get<0>(r);
219     EXPECT_EQ(response, IPMI_CC_OK);
220 
221     const auto payload = std::get<1>(r);
222     ASSERT_EQ(payload.has_value(), true);
223     const auto payload_tuple = payload.value();
224     const auto reply_cmd = std::get<0>(payload_tuple);
225     EXPECT_EQ(reply_cmd, SysAccelOobWrite);
226     const auto reply_buff = std::get<1>(payload_tuple);
227     ASSERT_GE(reply_buff.size(), sizeof(Reply));
228 
229     auto* reply = reinterpret_cast<const Reply*>(reply_buff.data());
230     EXPECT_EQ(reply->nameLength, kTestDeviceNameLength);
231     EXPECT_EQ(std::string_view(reply->name, reply->nameLength),
232               kTestDeviceNameStr);
233     EXPECT_EQ(reply->token, kTestToken);
234     EXPECT_EQ(reply->address, kTestAddress);
235     EXPECT_EQ(reply->num_bytes, kTestWriteSize);
236     EXPECT_EQ(reply->data, kTestData);
237 }
238 
TEST(GoogleAccelOobTest,SetVrSettings_Success)239 TEST(GoogleAccelOobTest, SetVrSettings_Success)
240 {
241     ::testing::StrictMock<HandlerMock> h;
242     constexpr uint8_t kChipId = 2;
243     constexpr uint8_t kSettingsId = 1;
244     constexpr uint16_t kTestValue = 0xAABB;
245 
246     std::vector<uint8_t> testData = {kChipId, kSettingsId, 0xBB, 0xAA};
247 
248     EXPECT_CALL(h, accelSetVrSettings(_, kChipId, kSettingsId, kTestValue))
249         .WillOnce(Return());
250 
251     Resp r = accelSetVrSettings(nullptr, testData, &h);
252 
253     const auto response = std::get<0>(r);
254     EXPECT_EQ(response, IPMI_CC_OK);
255 
256     const auto payload = std::get<1>(r);
257     ASSERT_EQ(payload.has_value(), true);
258     const auto payload_tuple = payload.value();
259     const auto reply_cmd = std::get<0>(payload_tuple);
260     EXPECT_EQ(reply_cmd, SysSetAccelVrSettings);
261     const auto reply_buff = std::get<1>(payload_tuple);
262     ASSERT_EQ(reply_buff.size(), 0);
263 }
264 
TEST(GoogleAccelOobTest,SetVrSettings_HandleIncorrectDataSize)265 TEST(GoogleAccelOobTest, SetVrSettings_HandleIncorrectDataSize)
266 {
267     ::testing::StrictMock<HandlerMock> h;
268     constexpr uint8_t kChipId = 2;
269     uint8_t kSettingsId = 1;
270 
271     std::vector<uint8_t> testData = {kChipId, kSettingsId};
272 
273     EXPECT_CALL(h, accelSetVrSettings(_, _, _, _)).Times(0);
274 
275     Resp r = accelSetVrSettings(nullptr, testData, &h);
276 
277     const auto response = std::get<0>(r);
278     EXPECT_EQ(response, IPMI_CC_REQ_DATA_LEN_INVALID);
279 
280     const auto payload = std::get<1>(r);
281     ASSERT_EQ(payload.has_value(), false);
282 }
283 
TEST(GoogleAccelOobTest,GetVrSettings_Success)284 TEST(GoogleAccelOobTest, GetVrSettings_Success)
285 {
286     ::testing::StrictMock<HandlerMock> h;
287     constexpr uint8_t kChipId = 3;
288     constexpr uint8_t kSettingsId = 2;
289 
290     std::vector<uint8_t> testData = {kChipId, kSettingsId};
291 
292     EXPECT_CALL(h, accelGetVrSettings(_, kChipId, kSettingsId))
293         .WillOnce(Return(0xAABB));
294 
295     Resp r = accelGetVrSettings(nullptr, testData, &h);
296 
297     const auto response = std::get<0>(r);
298     EXPECT_EQ(response, IPMI_CC_OK);
299 
300     const auto payload = std::get<1>(r);
301     ASSERT_EQ(payload.has_value(), true);
302     const auto payload_tuple = payload.value();
303     const auto reply_cmd = std::get<0>(payload_tuple);
304     EXPECT_EQ(reply_cmd, SysGetAccelVrSettings);
305     const auto reply_buff = std::get<1>(payload_tuple);
306     ASSERT_EQ(reply_buff.size(), 2);
307 
308     EXPECT_EQ(reply_buff.at(0), 0xBB);
309     EXPECT_EQ(reply_buff.at(1), 0xAA);
310 }
311 
TEST(GoogleAccelOobTest,GetVrSettings_HandleIncorrectDataSize)312 TEST(GoogleAccelOobTest, GetVrSettings_HandleIncorrectDataSize)
313 {
314     ::testing::StrictMock<HandlerMock> h;
315     constexpr uint8_t kChipId = 2;
316     uint8_t kSettingsId = 1;
317 
318     std::vector<uint8_t> testData = {kChipId, kSettingsId, 0xCC};
319 
320     EXPECT_CALL(h, accelGetVrSettings(_, _, _)).Times(0);
321 
322     Resp r = accelGetVrSettings(nullptr, testData, &h);
323 
324     const auto response = std::get<0>(r);
325     EXPECT_EQ(response, IPMI_CC_REQ_DATA_LEN_INVALID);
326 
327     const auto payload = std::get<1>(r);
328     ASSERT_EQ(payload.has_value(), false);
329 }
330 } // namespace ipmi
331 } // namespace google
332