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