blob: 691d9a6d0a8b58a57f1fba9ac780b0e6e1caa5c5 [file] [log] [blame]
Patrick Williams691668f2023-11-01 08:19:10 -05001/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
Andrew Jefferyc63f63a2023-02-24 22:29:33 +10302#ifndef PLDM_MSGBUF_H
3#define PLDM_MSGBUF_H
4
Andrew Jeffery66c77232024-04-24 11:42:02 +09305/*
6 * Historically, many of the structs exposed in libpldm's public headers are
7 * defined with __attribute__((packed)). This is unfortunate: it gives the
8 * impression that a wire-format buffer can be cast to the message type to make
9 * the message's fields easily accessible. As it turns out, that's not
10 * that's valid for several reasons:
11 *
12 * 1. Casting the wire-format buffer to a struct of the message type doesn't
13 * abstract the endianness of message field values
14 *
15 * 2. Some messages contain packed tagged union fields which cannot be properly
16 * described in a C struct.
17 *
18 * The msgbuf APIs exist to assist with (un)packing the wire-format in a way
19 * that is type-safe, spatially memory-safe, endian-safe, performant, and
20 * free of undefined-behaviour. Message structs that are added to the public
21 * library API should no-longer be marked __attribute__((packed)), and the
22 * implementation of their encode and decode functions must exploit the msgbuf
23 * API.
24 *
25 * However, we would like to allow implementation of codec functions in terms of
26 * msgbuf APIs even if they're decoding a message into a (historically) packed
27 * struct. Some of the complexity that follows is a consequence of the packed/
28 * unpacked conflict.
29 */
30
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103031#ifdef __cplusplus
Andrew Jeffery37dd6a32023-05-12 16:04:06 +093032/*
33 * Fix up C11's _Static_assert() vs C++'s static_assert().
34 *
35 * Can we please have nice things for once.
36 */
37// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
38#define _Static_assert(...) static_assert(__VA_ARGS__)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103039extern "C" {
40#endif
41
Andrew Jefferyb0c1d202023-11-07 22:08:44 +103042#include <libpldm/base.h>
43#include <libpldm/pldm_types.h>
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103044
Andrew Jeffery66c77232024-04-24 11:42:02 +093045#include "compiler.h"
46
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103047#include <assert.h>
48#include <endian.h>
49#include <limits.h>
50#include <stdbool.h>
Andrew Jeffery66c77232024-04-24 11:42:02 +093051#include <stdint.h>
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103052#include <string.h>
53#include <sys/types.h>
54
55struct pldm_msgbuf {
Thu Nguyen062c8762023-04-22 20:45:04 +070056 uint8_t *cursor;
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103057 ssize_t remaining;
58};
59
60/**
61 * @brief Initialize pldm buf struct for buf extractor
62 *
63 * @param[out] ctx - pldm_msgbuf context for extractor
64 * @param[in] minsize - The minimum required length of buffer `buf`
65 * @param[in] buf - buffer to be extracted
66 * @param[in] len - size of buffer
67 *
68 * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
69 * PLDM_ERROR_INVALID_DATA if pointer parameters are invalid, or
70 * PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
71 */
Andrew Jeffery07febdb2024-05-17 14:17:14 +093072static inline int pldm_msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize,
73 const void *buf, size_t len)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103074{
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103075 if (!ctx || !buf) {
76 return PLDM_ERROR_INVALID_DATA;
77 }
78
79 if ((minsize > len) || (len > SSIZE_MAX)) {
80 return PLDM_ERROR_INVALID_LENGTH;
81 }
82
Andrew Jeffery07febdb2024-05-17 14:17:14 +093083 if ((uintptr_t)buf + len < len) {
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103084 return PLDM_ERROR_INVALID_LENGTH;
85 }
86
87 ctx->cursor = (uint8_t *)buf;
88 ctx->remaining = (ssize_t)len;
89
90 return PLDM_SUCCESS;
91}
92
93/**
94 * @brief Validate buffer overflow state
95 *
96 * @param[in] ctx - pldm_msgbuf context for extractor
97 *
98 * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
99 * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
100 * prior accesses would have occurred beyond the bounds of the buffer, and
101 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
102 * pointer.
103 */
104static inline int pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
105{
106 if (!ctx) {
107 return PLDM_ERROR_INVALID_DATA;
108 }
109
110 return ctx->remaining >= 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
111}
112
113/**
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930114 * @brief Test whether a message buffer has been exactly consumed
115 *
116 * @param[in] ctx - pldm_msgbuf context for extractor
117 *
118 * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
119 * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
120 * indicates that an incorrect sequence of accesses have occurred, and
121 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
122 * pointer.
123 */
124static inline int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
125{
126 if (!ctx) {
127 return PLDM_ERROR_INVALID_DATA;
128 }
129
130 return ctx->remaining == 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
131}
132
133/**
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030134 * @brief Destroy the pldm buf
135 *
136 * @param[in] ctx - pldm_msgbuf context for extractor
137 *
138 * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
139 * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
140 * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
141 * bounds of the buffer.
142 */
143static inline int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
144{
145 int valid;
146
147 if (!ctx) {
148 return PLDM_ERROR_INVALID_DATA;
149 }
150
151 valid = pldm_msgbuf_validate(ctx);
152
153 ctx->cursor = NULL;
154 ctx->remaining = 0;
155
156 return valid;
157}
158
159/**
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930160 * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
161 * has been completely consumed without overflow
162 *
163 * @param[in] ctx - pldm_msgbuf context
164 *
165 * @return PLDM_SUCCESS if all buffer access were in-bounds and completely
166 * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
167 * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
168 * have occurred byond the bounds of the buffer
169 */
170static inline int pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
171{
172 int consumed;
173
174 if (!ctx) {
175 return PLDM_ERROR_INVALID_DATA;
176 }
177
178 consumed = pldm_msgbuf_consumed(ctx);
179
180 ctx->cursor = NULL;
181 ctx->remaining = 0;
182
183 return consumed;
184}
185
Andrew Jeffery66c77232024-04-24 11:42:02 +0930186/*
187 * Exploit the pre-processor to perform type checking by macro substitution.
188 *
189 * A C type is defined by its alignment as well as its object
190 * size, and compilers have a hammer to enforce it in the form of
191 * `-Waddress-of-packed-member`. Due to the unpacked/packed struct conflict in
192 * the libpldm public API this presents a problem: Naively attempting to use the
193 * msgbuf APIs on a member of a packed struct would yield an error.
194 *
195 * The msgbuf APIs are implemented such that data is moved through unaligned
196 * pointers in a safe way, but to mitigate `-Waddress-of-packed-member` we must
197 * make the object pointers take a trip through `void *` at its API boundary.
198 * That presents a bit too much of an opportunity to non-surgically remove your
199 * own foot, so here we set about doing something to mitigate that as well.
200 *
201 * pldm_msgbuf_extract_typecheck() exists to enforce pointer type correctness
202 * only for the purpose of object sizes, disregarding alignment. We have a few
203 * constraints that cause some headaches:
204 *
205 * 1. We have to perform the type-check before a call through a C function,
206 * as the function must take the object pointer argument as `void *`.
207 * Essentially, this constrains us to doing something with macros.
208 *
209 * 2. While libpldm is a C library, its test suite is written in C++ to take
210 * advantage of gtest.
211 *
212 * 3. Ideally we'd do something with C's `static_assert()`, however
213 * `static_assert()` is defined as void, and as we're constrained to macros,
214 * using `static_assert()` would require a statement-expression
215 *
216 * 4. Currently the project is built with `-std=c17`. CPP statement-expressions
217 * are a GNU extension. We prefer to avoid switching to `-std=gnu17` just for
218 * the purpose of enabling statement-expressions in this one instance.
219 *
220 * 5. We can achieve a conditional build error using `pldm_require_obj_type()`,
221 * however it's implemented in terms of `_Generic()`, which is not available
222 * in C++.
223 *
224 * Combined this means we need separate solutions for C and C++.
225 *
226 * For C, as we don't have statement-expressions, we need to exploit some other
227 * language feature to inject a `pldm_require_obj_type()` prior to the msgbuf
228 * API function call. We also have to take care of the fact that the call-sites
229 * may be in the context of a variable assignment for error-handling purposes.
230 * The key observation is that we can use the comma operator as a sequence point
231 * to order the type check before the API call, discarding the "result" value of
232 * the type check and yielding the return value of the API call.
233 *
234 * C++ could be less of a headache than the C as we can leverage template
235 * functions. An advantage of template functions is that while their definition
236 * is driven by instantion, the definition does not appear at the source
237 * location of the instantation, which gives it a great leg-up over the problems
238 * we have in the C path. However, the use of the msgbuf APIs in the test suite
239 * still makes things somewhat tricky, as the call-sites in the test suite are
240 * wrapped up in EXPECT_*() gtest macros. Ideally we'd implement functions that
241 * takes both the object type and the required type as template arguments, and
242 * then define the object pointer parameter as `void *` for a call through to
243 * the appropriate msgbuf API. However, because the msgbuf API call-sites are
244 * encapsulated in gtest macros, use of commas in the template specification
245 * causes pre-processor confusion. In this way we're constrained to only one
246 * template argument per function.
247 *
248 * Implement the C++ path using template functions that take the destination
249 * object type as a template argument, while the name of the function symbols
250 * are derived from the required type. The manual implementations of these
251 * appear at the end of the header. The type safety is actually enforced
252 * by `static_assert()` this time, as we can use statements as we're not
253 * constrained to an expression in the templated function body.
254 *
255 * The invocations of pldm_msgbuf_extract_typecheck() typically result in
256 * double-evaluation of some arguments. We're not yet bothered by this for two
257 * reasons:
258 *
259 * 1. The nature of the current call-sites are such that there are no
260 * argument expressions that result in undesirable side-effects
261 *
262 * 2. It's an API internal to the libpldm implementation, and we can fix things
263 * whenever something crops up the violates the observation in 1.
264 */
265#ifdef __cplusplus
266#define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...) \
267 pldm_msgbuf_typecheck_##ty<decltype(dst)>(__VA_ARGS__)
268#else
269#define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...) \
270 (pldm_require_obj_type(dst, ty), fn(__VA_ARGS__))
271#endif
272
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930273/**
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030274 * @brief pldm_msgbuf extractor for a uint8_t
275 *
276 * @param[inout] ctx - pldm_msgbuf context for extractor
277 * @param[out] dst - destination of extracted value
278 *
279 * @return PLDM_SUCCESS if buffer accesses were in-bounds,
280 * PLDM_ERROR_INVALID_LENGTH otherwise.
281 * PLDM_ERROR_INVALID_DATA if input a invalid ctx
282 */
Andrew Jeffery66c77232024-04-24 11:42:02 +0930283#define pldm_msgbuf_extract_uint8(ctx, dst) \
284 pldm_msgbuf_extract_typecheck(uint8_t, pldm__msgbuf_extract_uint8, \
285 dst, ctx, dst)
286// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
287static inline int pldm__msgbuf_extract_uint8(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030288{
289 if (!ctx || !ctx->cursor || !dst) {
290 return PLDM_ERROR_INVALID_DATA;
291 }
292
Andrew Jeffery66c77232024-04-24 11:42:02 +0930293 ctx->remaining -= sizeof(uint8_t);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030294 assert(ctx->remaining >= 0);
295 if (ctx->remaining < 0) {
296 return PLDM_ERROR_INVALID_LENGTH;
297 }
298
Andrew Jeffery66c77232024-04-24 11:42:02 +0930299 memcpy(dst, ctx->cursor, sizeof(uint8_t));
300
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030301 ctx->cursor++;
302 return PLDM_SUCCESS;
303}
304
Andrew Jeffery66c77232024-04-24 11:42:02 +0930305#define pldm_msgbuf_extract_int8(ctx, dst) \
306 pldm_msgbuf_extract_typecheck(int8_t, pldm__msgbuf_extract_int8, dst, \
307 ctx, dst)
308// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
309static inline int pldm__msgbuf_extract_int8(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030310{
311 if (!ctx || !ctx->cursor || !dst) {
312 return PLDM_ERROR_INVALID_DATA;
313 }
314
Andrew Jeffery66c77232024-04-24 11:42:02 +0930315 ctx->remaining -= sizeof(int8_t);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030316 assert(ctx->remaining >= 0);
317 if (ctx->remaining < 0) {
318 return PLDM_ERROR_INVALID_LENGTH;
319 }
320
Andrew Jeffery66c77232024-04-24 11:42:02 +0930321 memcpy(dst, ctx->cursor, sizeof(int8_t));
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030322 ctx->cursor++;
323 return PLDM_SUCCESS;
324}
325
Andrew Jeffery66c77232024-04-24 11:42:02 +0930326#define pldm_msgbuf_extract_uint16(ctx, dst) \
327 pldm_msgbuf_extract_typecheck(uint16_t, pldm__msgbuf_extract_uint16, \
328 dst, ctx, dst)
329// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
330static inline int pldm__msgbuf_extract_uint16(struct pldm_msgbuf *ctx,
331 void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030332{
333 uint16_t ldst;
334
335 if (!ctx || !ctx->cursor || !dst) {
336 return PLDM_ERROR_INVALID_DATA;
337 }
338
339 // Check for buffer overflow. If we overflow, account for the request as
340 // negative values in ctx->remaining. This way we can debug how far
341 // we've overflowed.
342 ctx->remaining -= sizeof(ldst);
343
344 // Prevent the access if it would overflow. First, assert so we blow up
345 // the test suite right at the point of failure. However, cater to
346 // -DNDEBUG by explicitly testing that the access is valid.
347 assert(ctx->remaining >= 0);
348 if (ctx->remaining < 0) {
349 return PLDM_ERROR_INVALID_LENGTH;
350 }
351
352 // Use memcpy() to have the compiler deal with any alignment
353 // issues on the target architecture
354 memcpy(&ldst, ctx->cursor, sizeof(ldst));
355
356 // Only assign the target value once it's correctly decoded
Andrew Jeffery66c77232024-04-24 11:42:02 +0930357 ldst = le16toh(ldst);
358
359 // Allow storing to unaligned
360 memcpy(dst, &ldst, sizeof(ldst));
361
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030362 ctx->cursor += sizeof(ldst);
363
364 return PLDM_SUCCESS;
365}
366
Andrew Jeffery66c77232024-04-24 11:42:02 +0930367#define pldm_msgbuf_extract_int16(ctx, dst) \
368 pldm_msgbuf_extract_typecheck(int16_t, pldm__msgbuf_extract_int16, \
369 dst, ctx, dst)
370// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
371static inline int pldm__msgbuf_extract_int16(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030372{
373 int16_t ldst;
374
375 if (!ctx || !ctx->cursor || !dst) {
376 return PLDM_ERROR_INVALID_DATA;
377 }
378
379 ctx->remaining -= sizeof(ldst);
380 assert(ctx->remaining >= 0);
381 if (ctx->remaining < 0) {
382 return PLDM_ERROR_INVALID_LENGTH;
383 }
384
385 memcpy(&ldst, ctx->cursor, sizeof(ldst));
386
Andrew Jeffery66c77232024-04-24 11:42:02 +0930387 ldst = le16toh(ldst);
388 memcpy(dst, &ldst, sizeof(ldst));
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030389 ctx->cursor += sizeof(ldst);
390
391 return PLDM_SUCCESS;
392}
393
Andrew Jeffery66c77232024-04-24 11:42:02 +0930394#define pldm_msgbuf_extract_uint32(ctx, dst) \
395 pldm_msgbuf_extract_typecheck(uint32_t, pldm__msgbuf_extract_uint32, \
396 dst, ctx, dst)
397// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
398static inline int pldm__msgbuf_extract_uint32(struct pldm_msgbuf *ctx,
399 void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030400{
401 uint32_t ldst;
402
403 if (!ctx || !ctx->cursor || !dst) {
404 return PLDM_ERROR_INVALID_DATA;
405 }
406
407 ctx->remaining -= sizeof(ldst);
408 assert(ctx->remaining >= 0);
409 if (ctx->remaining < 0) {
410 return PLDM_ERROR_INVALID_LENGTH;
411 }
412
413 memcpy(&ldst, ctx->cursor, sizeof(ldst));
414
Andrew Jeffery66c77232024-04-24 11:42:02 +0930415 ldst = le32toh(ldst);
416 memcpy(dst, &ldst, sizeof(ldst));
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030417 ctx->cursor += sizeof(ldst);
418
419 return PLDM_SUCCESS;
420}
421
Andrew Jeffery66c77232024-04-24 11:42:02 +0930422#define pldm_msgbuf_extract_int32(ctx, dst) \
423 pldm_msgbuf_extract_typecheck(int32_t, pldm__msgbuf_extract_int32, \
424 dst, ctx, dst)
425// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
426static inline int pldm__msgbuf_extract_int32(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030427{
428 int32_t ldst;
429
430 if (!ctx || !ctx->cursor || !dst) {
431 return PLDM_ERROR_INVALID_DATA;
432 }
433
434 ctx->remaining -= sizeof(ldst);
435 assert(ctx->remaining >= 0);
436 if (ctx->remaining < 0) {
437 return PLDM_ERROR_INVALID_LENGTH;
438 }
439
440 memcpy(&ldst, ctx->cursor, sizeof(ldst));
441
Andrew Jeffery66c77232024-04-24 11:42:02 +0930442 ldst = le32toh(ldst);
443 memcpy(dst, &ldst, sizeof(ldst));
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030444 ctx->cursor += sizeof(ldst);
445
446 return PLDM_SUCCESS;
447}
448
Andrew Jeffery66c77232024-04-24 11:42:02 +0930449#define pldm_msgbuf_extract_real32(ctx, dst) \
450 pldm_msgbuf_extract_typecheck(real32_t, pldm__msgbuf_extract_real32, \
451 dst, ctx, dst)
452// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
453static inline int pldm__msgbuf_extract_real32(struct pldm_msgbuf *ctx,
454 void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030455{
456 uint32_t ldst;
457
Andrew Jeffery66c77232024-04-24 11:42:02 +0930458 _Static_assert(sizeof(real32_t) == sizeof(ldst),
459 "Mismatched type sizes for dst and ldst");
460
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030461 if (!ctx || !ctx->cursor || !dst) {
462 return PLDM_ERROR_INVALID_DATA;
463 }
464
465 ctx->remaining -= sizeof(ldst);
466 assert(ctx->remaining >= 0);
467 if (ctx->remaining < 0) {
468 return PLDM_ERROR_INVALID_LENGTH;
469 }
470
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030471 memcpy(&ldst, ctx->cursor, sizeof(ldst));
472 ldst = le32toh(ldst);
Andrew Jeffery66c77232024-04-24 11:42:02 +0930473 memcpy(dst, &ldst, sizeof(ldst));
474 ctx->cursor += sizeof(ldst);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030475
476 return PLDM_SUCCESS;
477}
478
Andrew Jeffery66c77232024-04-24 11:42:02 +0930479/**
480 * Extract the field at the msgbuf cursor into the lvalue named by dst.
481 *
482 * @param ctx The msgbuf context object
483 * @param dst The lvalue into which the field at the msgbuf cursor should be
484 * extracted
485 *
486 * @return PLDM_SUCCESS on success, otherwise another value on error
487 */
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030488#define pldm_msgbuf_extract(ctx, dst) \
Andrew Jeffery66c77232024-04-24 11:42:02 +0930489 _Generic((dst), \
490 uint8_t: pldm__msgbuf_extract_uint8, \
491 int8_t: pldm__msgbuf_extract_int8, \
492 uint16_t: pldm__msgbuf_extract_uint16, \
493 int16_t: pldm__msgbuf_extract_int16, \
494 uint32_t: pldm__msgbuf_extract_uint32, \
495 int32_t: pldm__msgbuf_extract_int32, \
496 real32_t: pldm__msgbuf_extract_real32)(ctx, (void *)&(dst))
497
498/**
499 * Extract the field at the msgbuf cursor into the object pointed-to by dst.
500 *
501 * @param ctx The msgbuf context object
502 * @param dst The pointer to the object into which the field at the msgbuf
503 * cursor should be extracted
504 *
505 * @return PLDM_SUCCESS on success, otherwise another value on error
506 */
507#define pldm_msgbuf_extract_p(ctx, dst) \
508 _Generic((dst), \
509 uint8_t *: pldm__msgbuf_extract_uint8, \
510 int8_t *: pldm__msgbuf_extract_int8, \
511 uint16_t *: pldm__msgbuf_extract_uint16, \
512 int16_t *: pldm__msgbuf_extract_int16, \
513 uint32_t *: pldm__msgbuf_extract_uint32, \
514 int32_t *: pldm__msgbuf_extract_int32, \
515 real32_t *: pldm__msgbuf_extract_real32)(ctx, dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030516
Andrew Jeffery369b1212023-04-20 15:44:48 +0930517static inline int pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx,
518 uint8_t *dst, size_t count)
519{
Andrew Jeffery369b1212023-04-20 15:44:48 +0930520 if (!ctx || !ctx->cursor || !dst) {
521 return PLDM_ERROR_INVALID_DATA;
522 }
523
524 if (!count) {
525 return PLDM_SUCCESS;
526 }
527
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030528 if (count >= SSIZE_MAX) {
Andrew Jeffery369b1212023-04-20 15:44:48 +0930529 return PLDM_ERROR_INVALID_LENGTH;
530 }
531
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030532 ctx->remaining -= (ssize_t)count;
Andrew Jeffery369b1212023-04-20 15:44:48 +0930533 assert(ctx->remaining >= 0);
534 if (ctx->remaining < 0) {
535 return PLDM_ERROR_INVALID_LENGTH;
536 }
537
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030538 memcpy(dst, ctx->cursor, count);
539 ctx->cursor += count;
Andrew Jeffery369b1212023-04-20 15:44:48 +0930540
541 return PLDM_SUCCESS;
542}
543
544#define pldm_msgbuf_extract_array(ctx, dst, count) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930545 _Generic((*(dst)), uint8_t: pldm_msgbuf_extract_array_uint8)(ctx, dst, \
546 count)
Andrew Jeffery369b1212023-04-20 15:44:48 +0930547
Thu Nguyen062c8762023-04-22 20:45:04 +0700548static inline int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx,
549 const uint32_t src)
550{
551 uint32_t val = htole32(src);
552
553 if (!ctx || !ctx->cursor) {
554 return PLDM_ERROR_INVALID_DATA;
555 }
556
557 ctx->remaining -= sizeof(src);
558 assert(ctx->remaining >= 0);
559 if (ctx->remaining < 0) {
560 return PLDM_ERROR_INVALID_LENGTH;
561 }
562
563 memcpy(ctx->cursor, &val, sizeof(val));
564 ctx->cursor += sizeof(src);
565
566 return PLDM_SUCCESS;
567}
568
569static inline int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx,
570 const uint16_t src)
571{
572 uint16_t val = htole16(src);
573
574 if (!ctx || !ctx->cursor) {
575 return PLDM_ERROR_INVALID_DATA;
576 }
577
578 ctx->remaining -= sizeof(src);
579 assert(ctx->remaining >= 0);
580 if (ctx->remaining < 0) {
581 return PLDM_ERROR_INVALID_LENGTH;
582 }
583
584 memcpy(ctx->cursor, &val, sizeof(val));
585 ctx->cursor += sizeof(src);
586
587 return PLDM_SUCCESS;
588}
589
590static inline int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx,
591 const uint8_t src)
592{
593 if (!ctx || !ctx->cursor) {
594 return PLDM_ERROR_INVALID_DATA;
595 }
596
597 ctx->remaining -= sizeof(src);
598 assert(ctx->remaining >= 0);
599 if (ctx->remaining < 0) {
600 return PLDM_ERROR_INVALID_LENGTH;
601 }
602
603 memcpy(ctx->cursor, &src, sizeof(src));
604 ctx->cursor += sizeof(src);
605
606 return PLDM_SUCCESS;
607}
608
609static inline int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx,
610 const int32_t src)
611{
612 int32_t val = htole32(src);
613
614 if (!ctx || !ctx->cursor) {
615 return PLDM_ERROR_INVALID_DATA;
616 }
617
618 ctx->remaining -= sizeof(src);
619 assert(ctx->remaining >= 0);
620 if (ctx->remaining < 0) {
621 return PLDM_ERROR_INVALID_LENGTH;
622 }
623
624 memcpy(ctx->cursor, &val, sizeof(val));
625 ctx->cursor += sizeof(src);
626
627 return PLDM_SUCCESS;
628}
629
630static inline int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx,
631 const int16_t src)
632{
633 int16_t val = htole16(src);
634
635 if (!ctx || !ctx->cursor) {
636 return PLDM_ERROR_INVALID_DATA;
637 }
638
639 ctx->remaining -= sizeof(src);
640 assert(ctx->remaining >= 0);
641 if (ctx->remaining < 0) {
642 return PLDM_ERROR_INVALID_LENGTH;
643 }
644
645 memcpy(ctx->cursor, &val, sizeof(val));
646 ctx->cursor += sizeof(src);
647
648 return PLDM_SUCCESS;
649}
650
651static inline int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx,
652 const int8_t src)
653{
654 if (!ctx || !ctx->cursor) {
655 return PLDM_ERROR_INVALID_DATA;
656 }
657
658 ctx->remaining -= sizeof(src);
659 assert(ctx->remaining >= 0);
660 if (ctx->remaining < 0) {
661 return PLDM_ERROR_INVALID_LENGTH;
662 }
663
664 memcpy(ctx->cursor, &src, sizeof(src));
665 ctx->cursor += sizeof(src);
666
667 return PLDM_SUCCESS;
668}
669
670#define pldm_msgbuf_insert(dst, src) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930671 _Generic((src), \
672 uint8_t: pldm_msgbuf_insert_uint8, \
673 int8_t: pldm_msgbuf_insert_int8, \
674 uint16_t: pldm_msgbuf_insert_uint16, \
675 int16_t: pldm_msgbuf_insert_int16, \
676 uint32_t: pldm_msgbuf_insert_uint32, \
677 int32_t: pldm_msgbuf_insert_int32)(dst, src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700678
679static inline int pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx,
680 const uint8_t *src,
681 size_t count)
682{
Thu Nguyen062c8762023-04-22 20:45:04 +0700683 if (!ctx || !ctx->cursor || !src) {
684 return PLDM_ERROR_INVALID_DATA;
685 }
686
687 if (!count) {
688 return PLDM_SUCCESS;
689 }
690
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030691 if (count >= SSIZE_MAX) {
Thu Nguyen062c8762023-04-22 20:45:04 +0700692 return PLDM_ERROR_INVALID_LENGTH;
693 }
694
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030695 ctx->remaining -= (ssize_t)count;
Thu Nguyen062c8762023-04-22 20:45:04 +0700696 assert(ctx->remaining >= 0);
697 if (ctx->remaining < 0) {
698 return PLDM_ERROR_INVALID_LENGTH;
699 }
700
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030701 memcpy(ctx->cursor, src, count);
702 ctx->cursor += count;
Thu Nguyen062c8762023-04-22 20:45:04 +0700703
704 return PLDM_SUCCESS;
705}
706
707#define pldm_msgbuf_insert_array(dst, src, count) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930708 _Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src, \
709 count)
Thu Nguyen062c8762023-04-22 20:45:04 +0700710
711static inline int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx,
712 size_t required, void **cursor)
713{
714 if (!ctx || !ctx->cursor || !cursor || *cursor) {
715 return PLDM_ERROR_INVALID_DATA;
716 }
717
718 if (required > SSIZE_MAX) {
719 return PLDM_ERROR_INVALID_LENGTH;
720 }
721
722 ctx->remaining -= (ssize_t)required;
723 assert(ctx->remaining >= 0);
724 if (ctx->remaining < 0) {
725 return PLDM_ERROR_INVALID_LENGTH;
726 }
727
728 *cursor = ctx->cursor;
729 ctx->cursor += required;
730
731 return PLDM_SUCCESS;
732}
733
734static inline int pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx,
735 void **cursor, size_t *len)
736{
737 if (!ctx || !ctx->cursor || !cursor || *cursor || !len) {
738 return PLDM_ERROR_INVALID_DATA;
739 }
740
741 assert(ctx->remaining >= 0);
742 if (ctx->remaining < 0) {
743 return PLDM_ERROR_INVALID_LENGTH;
744 }
745
746 *cursor = ctx->cursor;
747 ctx->cursor += ctx->remaining;
748 *len = ctx->remaining;
749 ctx->remaining = 0;
750
751 return PLDM_SUCCESS;
752}
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030753#ifdef __cplusplus
754}
755#endif
756
Andrew Jeffery66c77232024-04-24 11:42:02 +0930757#ifdef __cplusplus
758#include <type_traits>
759
760template <typename T>
761static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx,
762 void *buf)
763{
764 static_assert(std::is_same<uint8_t *, T>::value);
765 return pldm__msgbuf_extract_uint8(ctx, buf);
766}
767
768template <typename T>
769static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx,
770 void *buf)
771{
772 static_assert(std::is_same<int8_t *, T>::value);
773 return pldm__msgbuf_extract_int8(ctx, buf);
774}
775
776template <typename T>
777static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx,
778 void *buf)
779{
780 static_assert(std::is_same<uint16_t *, T>::value);
781 return pldm__msgbuf_extract_uint16(ctx, buf);
782}
783
784template <typename T>
785static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx,
786 void *buf)
787{
788 static_assert(std::is_same<int16_t *, T>::value);
789 return pldm__msgbuf_extract_int16(ctx, buf);
790}
791
792template <typename T>
793static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx,
794 void *buf)
795{
796 static_assert(std::is_same<uint32_t *, T>::value);
797 return pldm__msgbuf_extract_uint32(ctx, buf);
798}
799
800template <typename T>
801static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx,
802 void *buf)
803{
804 static_assert(std::is_same<int32_t *, T>::value);
805 return pldm__msgbuf_extract_int32(ctx, buf);
806}
807
808template <typename T>
809static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx,
810 void *buf)
811{
812 static_assert(std::is_same<real32_t *, T>::value);
813 return pldm__msgbuf_extract_real32(ctx, buf);
814}
815#endif
816
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030817#endif /* BUF_H */