xref: /openbmc/smbios-mdr/src/smbios-ipmi-blobs/handler.cpp (revision 1d73dccc89f0bb9d1dce3543e5af6b3e3087d5f4)
1 #include "handler.hpp"
2 
3 #include "mdrv2.hpp"
4 #include "smbios_mdrv2.hpp"
5 
6 #include <sys/stat.h>
7 #include <unistd.h>
8 
9 #include <ipmid/api.hpp>
10 #include <phosphor-logging/log.hpp>
11 #include <sdbusplus/bus.hpp>
12 #include <sdbusplus/exception.hpp>
13 #include <sdbusplus/message.hpp>
14 
15 #include <algorithm>
16 #include <cstdint>
17 #include <ctime>
18 #include <filesystem>
19 #include <fstream>
20 #include <memory>
21 #include <string>
22 #include <vector>
23 
24 namespace blobs
25 {
26 
27 namespace internal
28 {
29 
30 constexpr const char* mdrV2Service = "xyz.openbmc_project.Smbios.MDR_V2";
31 constexpr const char* mdrV2Interface = "xyz.openbmc_project.Smbios.MDR_V2";
32 
syncSmbiosData()33 bool syncSmbiosData()
34 {
35     bool status = false;
36     sdbusplus::bus_t bus = sdbusplus::bus_t(ipmid_get_sd_bus_connection());
37     sdbusplus::message_t method =
38         bus.new_method_call(mdrV2Service, phosphor::smbios::defaultObjectPath,
39                             mdrV2Interface, "AgentSynchronizeData");
40 
41     try
42     {
43         sdbusplus::message_t reply = bus.call(method);
44         reply.read(status);
45     }
46     catch (const sdbusplus::exception_t& e)
47     {
48         phosphor::logging::log<phosphor::logging::level::ERR>(
49             "Error Sync data with service",
50             phosphor::logging::entry("ERROR=%s", e.what()),
51             phosphor::logging::entry("SERVICE=%s", mdrV2Service),
52             phosphor::logging::entry("PATH=%s",
53                                      phosphor::smbios::defaultObjectPath));
54         return false;
55     }
56 
57     if (!status)
58     {
59         phosphor::logging::log<phosphor::logging::level::ERR>(
60             "Sync data with service failure");
61         return false;
62     }
63 
64     return true;
65 }
66 
67 } // namespace internal
68 
canHandleBlob(const std::string & path)69 bool SmbiosBlobHandler::canHandleBlob(const std::string& path)
70 {
71     return path == blobId;
72 }
73 
getBlobIds()74 std::vector<std::string> SmbiosBlobHandler::getBlobIds()
75 {
76     return std::vector<std::string>(1, blobId);
77 }
78 
deleteBlob(const std::string &)79 bool SmbiosBlobHandler::deleteBlob(const std::string& /* path */)
80 {
81     return false;
82 }
83 
stat(const std::string & path,struct BlobMeta * meta)84 bool SmbiosBlobHandler::stat(const std::string& path, struct BlobMeta* meta)
85 {
86     if (!blobPtr || blobPtr->blobId != path)
87     {
88         return false;
89     }
90 
91     meta->size = blobPtr->buffer.size();
92     meta->blobState = blobPtr->state;
93     return true;
94 }
95 
open(uint16_t session,uint16_t flags,const std::string & path)96 bool SmbiosBlobHandler::open(uint16_t session, uint16_t flags,
97                              const std::string& path)
98 {
99     if (flags & blobs::OpenFlags::read)
100     {
101         /* Disable the read operation. */
102         return false;
103     }
104 
105     /* The handler only allows one session. If an open blob exists, return
106      * false directly.
107      */
108     if (blobPtr)
109     {
110         return false;
111     }
112     blobPtr = std::make_unique<SmbiosBlob>(session, path, flags);
113     return true;
114 }
115 
read(uint16_t,uint32_t,uint32_t)116 std::vector<uint8_t> SmbiosBlobHandler::read(
117     uint16_t /* session */, uint32_t /* offset */, uint32_t /* requestedSize */)
118 {
119     /* SMBIOS blob handler does not support read. */
120     return std::vector<uint8_t>();
121 }
122 
write(uint16_t session,uint32_t offset,const std::vector<uint8_t> & data)123 bool SmbiosBlobHandler::write(uint16_t session, uint32_t offset,
124                               const std::vector<uint8_t>& data)
125 {
126     if (!blobPtr || blobPtr->sessionId != session)
127     {
128         return false;
129     }
130 
131     if (!(blobPtr->state & blobs::StateFlags::open_write))
132     {
133         phosphor::logging::log<phosphor::logging::level::ERR>(
134             "No open blob to write");
135         return false;
136     }
137 
138     /* Is the offset beyond the array? */
139     if (offset >= maxBufferSize)
140     {
141         return false;
142     }
143 
144     /* Determine whether all their bytes will fit. */
145     uint32_t remain = maxBufferSize - offset;
146     if (data.size() > remain)
147     {
148         return false;
149     }
150 
151     /* Resize the buffer if what we're writing will go over the size */
152     uint32_t newBufferSize = data.size() + offset;
153     if (newBufferSize > blobPtr->buffer.size())
154     {
155         blobPtr->buffer.resize(newBufferSize);
156     }
157 
158     std::memcpy(blobPtr->buffer.data() + offset, data.data(), data.size());
159     return true;
160 }
161 
writeMeta(uint16_t,uint32_t,const std::vector<uint8_t> &)162 bool SmbiosBlobHandler::writeMeta(uint16_t /* session */, uint32_t /* offset */,
163                                   const std::vector<uint8_t>& /* data */)
164 {
165     return false;
166 }
167 
commit(uint16_t session,const std::vector<uint8_t> & data)168 bool SmbiosBlobHandler::commit(uint16_t session,
169                                const std::vector<uint8_t>& data)
170 {
171     if (!data.empty())
172     {
173         phosphor::logging::log<phosphor::logging::level::ERR>(
174             "Unexpected data provided to commit call");
175         return false;
176     }
177 
178     if (!blobPtr || blobPtr->sessionId != session)
179     {
180         return false;
181     }
182 
183     /* If a blob is committing or committed, return true directly. But if last
184      * commit fails, may try to commit again.
185      */
186     if (blobPtr->state &
187         (blobs::StateFlags::committing | blobs::StateFlags::committed))
188     {
189         return true;
190     }
191 
192     /* Clear the commit_error bit. */
193     blobPtr->state &= ~blobs::StateFlags::commit_error;
194 
195     std::string defaultDir =
196         std::filesystem::path(mdrDefaultFile).parent_path();
197 
198     MDRSMBIOSHeader mdrHdr;
199     mdrHdr.dirVer = mdrDirVersion;
200     mdrHdr.mdrType = mdrTypeII;
201     mdrHdr.timestamp = std::time(nullptr);
202     mdrHdr.dataSize = blobPtr->buffer.size();
203     if (access(defaultDir.c_str(), F_OK) == -1)
204     {
205         int flag = mkdir(defaultDir.c_str(), S_IRWXU);
206         if (flag != 0)
207         {
208             phosphor::logging::log<phosphor::logging::level::ERR>(
209                 "create folder failed for writing smbios file");
210             blobPtr->state |= blobs::StateFlags::commit_error;
211             return false;
212         }
213     }
214 
215     std::ofstream smbiosFile(mdrDefaultFile,
216                              std::ios_base::binary | std::ios_base::trunc);
217     if (!smbiosFile.good())
218     {
219         phosphor::logging::log<phosphor::logging::level::ERR>(
220             "Write data from flash error - Open SMBIOS table file failure");
221         blobPtr->state |= blobs::StateFlags::commit_error;
222         return false;
223     }
224 
225     smbiosFile.exceptions(std::ofstream::badbit | std::ofstream::failbit);
226     try
227     {
228         smbiosFile.write(reinterpret_cast<char*>(&mdrHdr),
229                          sizeof(MDRSMBIOSHeader));
230         smbiosFile.write(reinterpret_cast<char*>(blobPtr->buffer.data()),
231                          mdrHdr.dataSize);
232         smbiosFile.close();
233         blobPtr->state |= blobs::StateFlags::committing;
234     }
235     catch (const std::ofstream::failure& e)
236     {
237         phosphor::logging::log<phosphor::logging::level::ERR>(
238             "Write data from flash error - write data error",
239             phosphor::logging::entry("ERROR=%s", e.what()));
240         blobPtr->state |= blobs::StateFlags::commit_error;
241         return false;
242     }
243 
244     if (!internal::syncSmbiosData())
245     {
246         blobPtr->state &= ~blobs::StateFlags::committing;
247         blobPtr->state |= blobs::StateFlags::commit_error;
248         return false;
249     }
250 
251     // Unset committing state and set committed state
252     blobPtr->state &= ~blobs::StateFlags::committing;
253     blobPtr->state |= blobs::StateFlags::committed;
254 
255     return true;
256 }
257 
close(uint16_t session)258 bool SmbiosBlobHandler::close(uint16_t session)
259 {
260     if (!blobPtr || blobPtr->sessionId != session)
261     {
262         return false;
263     }
264 
265     blobPtr = nullptr;
266     return true;
267 }
268 
stat(uint16_t session,struct BlobMeta * meta)269 bool SmbiosBlobHandler::stat(uint16_t session, struct BlobMeta* meta)
270 {
271     if (!blobPtr || blobPtr->sessionId != session)
272     {
273         return false;
274     }
275 
276     meta->size = blobPtr->buffer.size();
277     meta->blobState = blobPtr->state;
278     return true;
279 }
280 
expire(uint16_t session)281 bool SmbiosBlobHandler::expire(uint16_t session)
282 {
283     return close(session);
284 }
285 
286 } // namespace blobs
287