xref: /openbmc/phosphor-ipmi-blobs/README.md (revision 35b6c3e6)
1# Phosphor Blob Transfer Interface
2
3This document describes the commands implementing a generic blob transfer
4interface. This document does not specify how blobs are stored; that is left up
5to blob-specific implementations. Introduction This mechanism supports reading
6and writing from a generic blob store. This mechanism can be leveraged to
7support firmware upgrades,
8
9The interface defined in this document supports:
10
11*   Enumerating blobs
12*   Opening a blob for reading or writing
13*   Writing blob content
14*   Committing a blob
15*   Polling until the blob has been committed
16*   Closing a blob
17*   Reading blob content
18*   Deleting a blob
19
20Some blobs will only support a subset of these operations. For example, firmware
21cannot generally be read, as the firmware file is not persisted on the BMC after
22it has been activated.
23
24This specification supports IPMI as a transport layer. This can be quite slow,
25however; IPMI transport speeds range from 23 kbps to 256 kbps. LPC/P2A ("PCI to
26Aspeed") MMIO bridges are currently unsupported. Blob Identifiers Blobs are
27identified by NULL-terminated C strings. This protocol supports
28implementation-specific blob identifiers; some blobs may have single well-known
29names, while others may be defined only by a prefix, with the client specifying
30the rest of the blob name. For example, "/g/bmc_firmware" may represent a single
31well-known blob that supports BMC firmware updates, whereas "/g/skm/[N]" may
32represent an arbitrary number of SKM keys, with the index specified on the host
33when opening the blob.
34
35Blob identifiers are limited based on the maximum size of the IPMI packet. This
36maximum size is platform-dependent; theoretically a packet could be up to 256
37bytes, but some hardware only supports packets up to 64 bytes.
38
39If an identifier is malformed, e.g. does not have a trailing NUL-byte or is
40otherwise unrecognizable by the BMC, an error is returned.
41
42The OEM Number to use with these commands is
43[openbmc oen](https://github.com/openbmc/phosphor-host-ipmid/blob/194375f2676715a0e0697bab63234a4efe39fb96/include/ipmid/iana.hpp#L12)
4449871.
45
46## Commands
47
48The following details each subcommand in the blob spec. In the following, any
49reference to the command body starts after the 3 bytes of OEM header, the 1-byte
50subcommand code, and (in the cases where the command body is non-empty) a
51two-byte CRC over all data that follows in the body.
52
53All non-empty responses should lead with a two-byte CRC. The CRC algorithm is
54CRC-16-CCITT (poly 0x1021).
55
56All multi-byte values are encoded as little-endian. All structs are assumed
57packed. Success codes are returned via the "completion code" field from the IPMI
58spec.
59
60### BmcBlobGetCount (0)
61
62The `BmcBlobGetCount` command expects to receive an empty body. The BMC will
63return the number of enumerable blobs:
64
65```
66struct BmcBlobCountRx {
67    uint16_t crc16;
68    uint32_t blob_count;
69};
70```
71
72### BmcBlobEnumerate (1)
73
74The `BmcBlobEnumerate` command expects to receive a body of:
75
76```
77struct BmcBlobEnumerateTx {
78    uint16_t crc16;
79    uint32_t blob_idx; /* 0-based index of blob to retrieve. */
80};
81```
82
83The BMC will return the corresponding blob identifier:
84
85```
86struct BmcBlobEnumerateRx {
87    uint16_t crc16;
88    char     blob_id[];
89};
90```
91
92Note that the index for a given blob ID is not expected to be stable across a
93long term. Callers are expected to call `BmcBlobGetCount`, followed by N calls
94to `BmcBlobEnumerate`, to collect all blob IDs. Callers can then call
95`BmcBlobStat` with each blob ID. If this process is interleaved with Open or
96Delete calls that modify the number of enumerable blobs, this operation will be
97subject to concurrent modification issues.
98
99### BmcBlobOpen (2)
100
101The BmcBlobOpen command expects to receive a body of:
102
103```
104struct BmcBlobOpenTx {
105    uint16_t crc16;
106    uint16_t flags;
107    char     blob_id[]; /* Must correspond to a valid blob. */
108};
109```
110
111The flags field allows the caller to specify whether the blob is being opened
112for reading or writing:
113
114```
115enum BmcBlobOpenFlagBits {
116    READ = 0,
117    WRITE = 1,
118    <bits 2-7 reserved>
119    <bits 8-15 given blob-specific definitions>
120};
121```
122
123If the `WRITE` flag is specified, the BMC will mark the specified blob as "open
124for writing". Optional blob-specific behavior: if the blob has been open for
125more than one minute without any activity, the existing session will be torn
126down and the open will succeed. Alternatively, blobs may allow multiple write
127sessions to be active at once.
128
129The BMC allocates a unique session identifier, and internally maps it to the
130blob identifier.
131
132```
133struct BmcBlobOpenRx {
134    uint16_t crc16;
135    uint16_t session_id;
136};
137```
138
139### BmcBlobRead (3)
140
141The BmcBlobRead command is used to read blob data. It expects to receive a body
142of:
143
144```
145struct BmcBlobReadTx {
146    uint16_t crc16;
147    uint16_t session_id; /* Returned from BmcBlobOpen. */
148    uint32_t offset; /* The byte sequence start, 0-based. */
149    uint32_t requested_size; /* The number of bytes requested for reading. */
150};
151```
152
153Blob handlers may require the blob’s "state" to equal `OPEN_R` before reading is
154successful.
155
156```
157struct BmcBlobReadRx {
158    uint16_t crc16;
159    uint8_t  data[];
160};
161```
162
163Immediately following this structure are the bytes being read. The number of
164bytes transferred is the size of the response body less the OEN ("OEM number")
165(3 bytes), sub-command (1 byte), and the structure size (4 bytes). If no bytes
166are transferred, the CRC is still sent.
167
168If the BMC cannot return the number of requested bytes, it simply returns the
169number of bytes available for reading. If the host tries to read at an invalid
170offset or if the host tries to read at the end of the blob, an empty successful
171response is returned; e.g., data is empty.
172
173### BmcBlobWrite (4)
174
175The `BmcBlobWrite` command expects to receive a body of:
176
177```
178struct BmcBlobWriteTx {
179    uint16_t crc16;
180    uint16_t session_id; /* Returned from BmcBlobOpen. */
181    uint32_t offset; /* The byte sequence start, 0-based. */
182    uint8_t  data[];
183};
184```
185
186Immediately following this structure are the bytes to write. The length of the
187entire packet is variable and handled at a higher level, therefore the number of
188bytes to write is the size of the command body less the OEN and sub-command (4
189bytes) and less the structure size (10 bytes). It is assumed that all writes are
190sequential, and begin at offset zero.
191
192On success it will return a success completion code.
193
194### BmcBlobCommit (5)
195
196The `BmcBlobCommit` command expects to receive a body of:
197
198```
199struct BmcBlobCommitTx {
200    uint16_t crc16;
201    uint16_t session_id; /* Returned from BmcBlobOpen. */
202    uint8_t  commit_data_len;
203    uint8_t  commit_data[]; /* Optional blob-specific commit data. */
204};
205```
206
207Each blob defines its own commit behavior. A BMC firmware blob may be verified
208and saved to the filesystem. Commit operations may require additional data,
209which would be provided following the structure in the IPMI packet.
210
211The commit operation may exceed the IPMI timeout duration of ~5 seconds
212(implementation dependant). Callers are expected to poll on `BmcBlobSessionStat`
213or `BmcBlobStat` (as appropriate) until committing has finished. To address race
214conditions, blobs should not allow concurrent sessions that modify state.
215
216On success, the BMC returns success completion code.
217
218### BmcBlobClose (6)
219
220The `BmcBlobClose` command must be called after commit-polling has finished,
221regardless of the result. It expects to receive a body of:
222
223```
224struct BmcBlobCloseTx {
225    uint16_t crc16;
226    uint16_t session_id; /* Returned from BmcBlobOpen. */
227};
228```
229
230The BMC marks the specified blob as closed. On success, the BMC returns a
231success completion code.
232
233### BmcBlobDelete (7)
234
235The `BmcBlobDelete` command is used to delete a blob. Not all blobs will support
236deletion. This command expects to receive a body of:
237
238```
239struct BmcBlobDeleteTx {
240    uint16_t crc16;
241    char     blob_id[]; /* Must correspond to a valid blob. */
242};
243```
244
245If the operation is supported, the blob is deleted. On success, the BMC returns
246a success completion code. This command will fail if there are open sessions for
247the blob.
248
249### BmcBlobStat (8)
250
251The `BmcBlobStat` command is used to retrieve statistics about a blob. Not all
252blobs must support this command; this is only useful when blob_id semantics are
253more useful than session IDs. This command expects to receive a body of:
254
255```
256struct BmcBlobStatTx {
257    uint16_t crc16;
258    char     blob_id[]; /* Must correspond to a valid blob. */
259};
260```
261
262The BMC returns the following data:
263
264```
265struct BmcBlobStatRx {
266    uint16_t crc16;
267    uint16_t blob_state;
268    uint32_t size; /* Size in bytes of the blob. */
269    uint8_t  metadata_len;
270    uint8_t  metadata[]; /* Optional blob-specific metadata. */
271};
272```
273
274The blob_state field is a bit field with the following flags:
275
276```
277enum BmcBlobStateFlagBits {
278    OPEN_R = 0,
279    OPEN_W = 1,
280    COMMITTING = 2,
281    COMMITTED = 3,
282    COMMIT_ERROR = 4,
283    <bits 5-7 reserved>
284    <bits 8-15 given blob-specific definitions>
285};
286```
287
288If the state is `COMMITTING`, the blob is not currently available for reading or
289writing. If the state is `COMMITTED`, the blob may be available for reading.
290
291The size field may be zero if the blob does not support reading.
292
293Immediately following this structure are optional blob-specific bytes. The
294number of bytes transferred is the size of the response body less the OEN and
295sub-command and less the structure size. The metadata must fit in a single IPMI
296packet, which has a platform-dependent maximum size. (For reference, Aspeed
297supports 64 bytes max.)
298
299If the blob is open or committed but has been inactive for longer than the
300specified activity timeout, the blob is closed, and blob_status is set to
301`CLOSED` in the response.
302
303### BmcBlobSessionStat (9)
304
305The `BmcBlobSessionStat` command returns the same data as `BmcBlobStat`.
306However, this command operates on sessions, rather than blob IDs. Not all blobs
307must support this command; this is only useful when session semantics are more
308useful than raw blob IDs.
309
310```
311struct BmcBlobSessionStatTx {
312    uint16_t crc16;
313    uint16_t session_id; /* Returned from BmcBlobOpen. */
314};
315```
316
317```
318struct BmcBlobSessionStatRx {
319    uint16_t crc16;
320    uint16_t blob_state;
321    uint32_t size; /* Size in bytes of the blob. */
322    uint8_t  metadata_size;
323    uint8_t  metadata[]; /* Optional blob-specific metadata. */
324};
325```
326
327### BmcBlobWriteMeta (10)
328
329The `BmcBlobWriteMeta` command behaves like `BmcBlobWrite`. Its purpose is
330blob-specific, and not all blobs must support it.
331
332The `BmcBlobWriteMeta` command expects to receive a body of:
333
334```
335struct BmcBlobWriteMetaTx
336{
337    uint16_t crc16;
338    uint16_t session_id; /* Returned from BmcBlobOpen. */
339    uint32_t offset; /* The byte sequence start, 0-based. */
340    uint8_t  data[];
341};
342```
343
344Immediately following this structure are the bytes to write. The length of the
345entire packet is variable and handled at a higher level, therefore the number of
346bytes to write is the size of the command body less the OEN and sub-command (4
347bytes) and less the structure size.
348
349On success it will return a success completion code.
350
351## Idempotent Commands
352
353The IPMI transport layer is somewhat flaky. Client code must rely on a
354"send-with-retries" strategy to ensure that commands make their way from the
355host to the BMC. Commands can fail if the BMC is busy with other commands.
356
357It is possible that an IPMI command successfully invokes the BMC-side handler,
358but that the response does not successfully make its way back to the host. In
359this case, the host may decide to retry the command. Thus, command handlers
360should be idempotent where possible; duplicate calls should return the same
361value if repeated, while avoiding potentially destructive side-effects.
362
363## Stale Sessions
364
365Each blob type will define an operation for cleansing stale sessions. This could
366involve scrubbing secrets or freeing buffers. A function will be provided that
367will scan over each open session, to determine which if any sessions have been
368open for longer than 10 minutes with no activity. For each session, the
369associated blob type’s cleansing routine will be invoked, and the associated
370session ID will be freed. This function will be invoked from the `BmcBlobOpen`
371command handler, though not more than once every minute.
372
373## Handler Calling Contract
374
375The blob manager provides the following calling contract guarantees:
376
377*   The blob manager will only call `open()` on your handler if the handler
378    responds that they can handle the path.
379*   The blob manager will only call a session-based command against your handler
380    if that session is already open (e.g. `stat()` or `commit()`).
381    *   The caveat is the open command where the session is provided to the
382        handler within the call.
383*   The blob manager will only call `delete()` on a blob if there are no open
384    sessions to that blob id.
385