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