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