blob: 0fc33b4864b2ec26e8cd4444020db4ad8fb832c5 [file] [log] [blame]
Vijay Khemka11b9c3b2019-08-21 15:21:42 -07001/*
2 * Copyright (c) 2018 Intel Corporation.
3 * Copyright (c) 2018-present Facebook.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <ipmid/api.hpp>
19
20#include <boost/algorithm/string/join.hpp>
21#include <nlohmann/json.hpp>
22#include <iostream>
23#include <sstream>
24#include <fstream>
25#include <phosphor-logging/log.hpp>
26#include <sdbusplus/message/types.hpp>
27#include <sdbusplus/timer.hpp>
28#include <storagecommands.hpp>
29
30//----------------------------------------------------------------------
31// Platform specific functions for storing app data
32//----------------------------------------------------------------------
33
34static void toHexStr(std::vector<uint8_t> &bytes, std::string &hexStr)
35{
36 std::stringstream stream;
37 stream << std::hex << std::uppercase << std::setfill('0');
38 for (const uint8_t byte : bytes)
39 {
40 stream << std::setw(2) << static_cast<int>(byte);
41 }
42 hexStr = stream.str();
43}
44
45static int fromHexStr(const std::string hexStr, std::vector<uint8_t> &data)
46{
47 for (unsigned int i = 0; i < hexStr.size(); i += 2)
48 {
49 try
50 {
51 data.push_back(static_cast<uint8_t>(
52 std::stoul(hexStr.substr(i, 2), nullptr, 16)));
53 }
54 catch (std::invalid_argument &e)
55 {
56 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
57 return -1;
58 }
59 catch (std::out_of_range &e)
60 {
61 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
62 return -1;
63 }
64 }
65 return 0;
66}
67
68namespace fb_oem::ipmi::sel
69{
70
71class SELData
72{
73 private:
74 nlohmann::json selDataObj;
75
76 void flush()
77 {
78 std::ofstream file(SEL_JSON_DATA_FILE);
79 file << selDataObj;
80 file.close();
81 }
82
83 void init()
84 {
85 selDataObj[KEY_SEL_VER] = 0x51;
86 selDataObj[KEY_SEL_COUNT] = 0;
87 selDataObj[KEY_ADD_TIME] = 0xFFFFFFFF;
88 selDataObj[KEY_ERASE_TIME] = 0xFFFFFFFF;
89 selDataObj[KEY_OPER_SUPP] = 0x02;
90 /* Spec indicates that more than 64kB is free */
91 selDataObj[KEY_FREE_SPACE] = 0xFFFF;
92 }
93
94 public:
95 SELData()
96 {
97 /* Get App data stored in json file */
98 std::ifstream file(SEL_JSON_DATA_FILE);
99 if (file)
100 {
101 file >> selDataObj;
102 file.close();
103 }
104
105 /* Initialize SelData object if no entries. */
106 if (selDataObj.find(KEY_SEL_COUNT) == selDataObj.end())
107 {
108 init();
109 }
110 }
111
112 int clear()
113 {
114 /* Clear the complete Sel Json object */
115 selDataObj.clear();
116 /* Reinitialize it with basic data */
117 init();
118 /* Save the erase time */
119 struct timespec selTime = {};
120 if (clock_gettime(CLOCK_REALTIME, &selTime) < 0)
121 {
122 return -1;
123 }
124 selDataObj[KEY_ERASE_TIME] = selTime.tv_sec;
125 flush();
126 return 0;
127 }
128
129 uint32_t getCount()
130 {
131 return selDataObj[KEY_SEL_COUNT];
132 }
133
134 void getInfo(GetSELInfoData &info)
135 {
136 info.selVersion = selDataObj[KEY_SEL_VER];
137 info.entries = selDataObj[KEY_SEL_COUNT];
138 info.freeSpace = selDataObj[KEY_FREE_SPACE];
139 info.addTimeStamp = selDataObj[KEY_ADD_TIME];
140 info.eraseTimeStamp = selDataObj[KEY_ERASE_TIME];
141 info.operationSupport = selDataObj[KEY_OPER_SUPP];
142 }
143
144 int getEntry(uint32_t index, std::string &rawStr)
145 {
146 std::stringstream ss;
147 ss << std::hex;
148 ss << std::setw(2) << std::setfill('0') << index;
149
150 /* Check or the requested SEL Entry, if record is available */
151 if (selDataObj.find(ss.str()) == selDataObj.end())
152 {
153 return -1;
154 }
155
156 rawStr = selDataObj[ss.str()][KEY_SEL_ENTRY_RAW];
157 return 0;
158 }
159
160 int addEntry(std::string keyStr)
161 {
162 struct timespec selTime = {};
163
164 if (clock_gettime(CLOCK_REALTIME, &selTime) < 0)
165 {
166 return -1;
167 }
168
169 selDataObj[KEY_ADD_TIME] = selTime.tv_sec;
170
171 int selCount = selDataObj[KEY_SEL_COUNT];
172 selDataObj[KEY_SEL_COUNT] = ++selCount;
173
174 std::stringstream ss;
175 ss << std::hex;
176 ss << std::setw(2) << std::setfill('0') << selCount;
177
178 selDataObj[ss.str()][KEY_SEL_ENTRY_RAW] = keyStr;
179 flush();
180 return selCount;
181 }
182};
183
184} // namespace fb_oem::ipmi::sel
185
186namespace ipmi
187{
188
189namespace storage
190{
191
192static void registerSELFunctions() __attribute__((constructor));
193static fb_oem::ipmi::sel::SELData selObj __attribute__((init_priority(101)));
194
195ipmi::RspType<uint8_t, // SEL version
196 uint16_t, // SEL entry count
197 uint16_t, // free space
198 uint32_t, // last add timestamp
199 uint32_t, // last erase timestamp
200 uint8_t> // operation support
201 ipmiStorageGetSELInfo()
202{
203
204 fb_oem::ipmi::sel::GetSELInfoData info;
205
206 selObj.getInfo(info);
207 return ipmi::responseSuccess(info.selVersion, info.entries, info.freeSpace,
208 info.addTimeStamp, info.eraseTimeStamp,
209 info.operationSupport);
210}
211
212ipmi::RspType<uint16_t, std::vector<uint8_t>>
213 ipmiStorageGetSELEntry(std::vector<uint8_t> data)
214{
215
216 if (data.size() != sizeof(fb_oem::ipmi::sel::GetSELEntryRequest))
217 {
218 return ipmi::responseReqDataLenInvalid();
219 }
220
221 fb_oem::ipmi::sel::GetSELEntryRequest *reqData =
222 reinterpret_cast<fb_oem::ipmi::sel::GetSELEntryRequest *>(&data[0]);
223
224 if (reqData->reservID != 0)
225 {
226 if (!checkSELReservation(reqData->reservID))
227 {
228 return ipmi::responseInvalidReservationId();
229 }
230 }
231
232 uint16_t selCnt = selObj.getCount();
233 if (selCnt == 0)
234 {
235 return ipmi::responseSensorInvalid();
236 }
237
238 /* If it is asked for first entry */
239 if (reqData->recordID == fb_oem::ipmi::sel::firstEntry)
240 {
241 /* First Entry (0x0000) as per Spec */
242 reqData->recordID = 1;
243 }
244 else if (reqData->recordID == fb_oem::ipmi::sel::lastEntry)
245 {
246 /* Last entry (0xFFFF) as per Spec */
247 reqData->recordID = selCnt;
248 }
249
250 std::string ipmiRaw;
251
252 if (selObj.getEntry(reqData->recordID, ipmiRaw) < 0)
253 {
254 return ipmi::responseSensorInvalid();
255 }
256
257 std::vector<uint8_t> recDataBytes;
258 if (fromHexStr(ipmiRaw, recDataBytes) < 0)
259 {
260 return ipmi::responseUnspecifiedError();
261 }
262
263 /* Identify the next SEL record ID. If recordID is same as
264 * total SeL count then next id should be last entry else
265 * it should be incremented by 1 to current RecordID
266 */
267 uint16_t nextRecord;
268 if (reqData->recordID == selCnt)
269 {
270 nextRecord = fb_oem::ipmi::sel::lastEntry;
271 }
272 else
273 {
274 nextRecord = reqData->recordID + 1;
275 }
276
277 if (reqData->readLen == fb_oem::ipmi::sel::entireRecord)
278 {
279 return ipmi::responseSuccess(nextRecord, recDataBytes);
280 }
281 else
282 {
283 if (reqData->offset >= fb_oem::ipmi::sel::selRecordSize ||
284 reqData->readLen > fb_oem::ipmi::sel::selRecordSize)
285 {
286 return ipmi::responseUnspecifiedError();
287 }
288 std::vector<uint8_t> recPartData;
289
290 auto diff = fb_oem::ipmi::sel::selRecordSize - reqData->offset;
291 auto readLength = std::min(diff, static_cast<int>(reqData->readLen));
292
293 for (int i = 0; i < readLength; i++)
294 {
295 recPartData.push_back(recDataBytes[i + reqData->offset]);
296 }
297 return ipmi::responseSuccess(nextRecord, recPartData);
298 }
299}
300
301ipmi::RspType<uint16_t> ipmiStorageAddSELEntry(std::vector<uint8_t> data)
302{
303 /* Per the IPMI spec, need to cancel any reservation when a
304 * SEL entry is added
305 */
306 cancelSELReservation();
307
308 if (data.size() != fb_oem::ipmi::sel::selRecordSize)
309 {
310 return ipmi::responseReqDataLenInvalid();
311 }
312
313 std::string ipmiRaw, logErr;
314 toHexStr(data, ipmiRaw);
315
316 /* Log the Raw SEL message to the journal */
317 std::string journalMsg = "SEL Entry Added: " + ipmiRaw;
318 phosphor::logging::log<phosphor::logging::level::INFO>(journalMsg.c_str());
319
320 int responseID = selObj.addEntry(ipmiRaw.c_str());
321 if (responseID < 0)
322 {
323 return ipmi::responseUnspecifiedError();
324 }
325 return ipmi::responseSuccess((uint16_t)responseID);
326}
327
328void registerSELFunctions()
329{
330 // <Get SEL Info>
331 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
332 ipmi::storage::cmdGetSelInfo, ipmi::Privilege::User,
333 ipmiStorageGetSELInfo);
334
335 // <Get SEL Entry>
336 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
337 ipmi::storage::cmdGetSelEntry, ipmi::Privilege::User,
338 ipmiStorageGetSELEntry);
339
340 // <Add SEL Entry>
341 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnStorage,
342 ipmi::storage::cmdAddSelEntry,
343 ipmi::Privilege::Operator, ipmiStorageAddSELEntry);
344
345 return;
346}
347
348} // namespace storage
349} // namespace ipmi