blob: 85cfb3946e35fec73444017f29588e2303729e2d [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 Jeffery5646f232023-04-12 22:40:19 +093072__attribute__((no_sanitize("pointer-overflow"))) static inline int
73pldm_msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
74 size_t len)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +103075{
76 uint8_t *end;
77
78 if (!ctx || !buf) {
79 return PLDM_ERROR_INVALID_DATA;
80 }
81
82 if ((minsize > len) || (len > SSIZE_MAX)) {
83 return PLDM_ERROR_INVALID_LENGTH;
84 }
85
86 end = (uint8_t *)buf + len;
87 if (end && end < (uint8_t *)buf) {
88 return PLDM_ERROR_INVALID_LENGTH;
89 }
90
91 ctx->cursor = (uint8_t *)buf;
92 ctx->remaining = (ssize_t)len;
93
94 return PLDM_SUCCESS;
95}
96
97/**
98 * @brief Validate buffer overflow state
99 *
100 * @param[in] ctx - pldm_msgbuf context for extractor
101 *
102 * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
103 * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
104 * prior accesses would have occurred beyond the bounds of the buffer, and
105 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
106 * pointer.
107 */
108static inline int pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
109{
110 if (!ctx) {
111 return PLDM_ERROR_INVALID_DATA;
112 }
113
114 return ctx->remaining >= 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
115}
116
117/**
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930118 * @brief Test whether a message buffer has been exactly consumed
119 *
120 * @param[in] ctx - pldm_msgbuf context for extractor
121 *
122 * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
123 * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
124 * indicates that an incorrect sequence of accesses have occurred, and
125 * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
126 * pointer.
127 */
128static inline int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
129{
130 if (!ctx) {
131 return PLDM_ERROR_INVALID_DATA;
132 }
133
134 return ctx->remaining == 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
135}
136
137/**
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030138 * @brief Destroy the pldm buf
139 *
140 * @param[in] ctx - pldm_msgbuf context for extractor
141 *
142 * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
143 * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
144 * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
145 * bounds of the buffer.
146 */
147static inline int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
148{
149 int valid;
150
151 if (!ctx) {
152 return PLDM_ERROR_INVALID_DATA;
153 }
154
155 valid = pldm_msgbuf_validate(ctx);
156
157 ctx->cursor = NULL;
158 ctx->remaining = 0;
159
160 return valid;
161}
162
163/**
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930164 * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
165 * has been completely consumed without overflow
166 *
167 * @param[in] ctx - pldm_msgbuf context
168 *
169 * @return PLDM_SUCCESS if all buffer access were in-bounds and completely
170 * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
171 * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
172 * have occurred byond the bounds of the buffer
173 */
174static inline int pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
175{
176 int consumed;
177
178 if (!ctx) {
179 return PLDM_ERROR_INVALID_DATA;
180 }
181
182 consumed = pldm_msgbuf_consumed(ctx);
183
184 ctx->cursor = NULL;
185 ctx->remaining = 0;
186
187 return consumed;
188}
189
Andrew Jeffery66c77232024-04-24 11:42:02 +0930190/*
191 * Exploit the pre-processor to perform type checking by macro substitution.
192 *
193 * A C type is defined by its alignment as well as its object
194 * size, and compilers have a hammer to enforce it in the form of
195 * `-Waddress-of-packed-member`. Due to the unpacked/packed struct conflict in
196 * the libpldm public API this presents a problem: Naively attempting to use the
197 * msgbuf APIs on a member of a packed struct would yield an error.
198 *
199 * The msgbuf APIs are implemented such that data is moved through unaligned
200 * pointers in a safe way, but to mitigate `-Waddress-of-packed-member` we must
201 * make the object pointers take a trip through `void *` at its API boundary.
202 * That presents a bit too much of an opportunity to non-surgically remove your
203 * own foot, so here we set about doing something to mitigate that as well.
204 *
205 * pldm_msgbuf_extract_typecheck() exists to enforce pointer type correctness
206 * only for the purpose of object sizes, disregarding alignment. We have a few
207 * constraints that cause some headaches:
208 *
209 * 1. We have to perform the type-check before a call through a C function,
210 * as the function must take the object pointer argument as `void *`.
211 * Essentially, this constrains us to doing something with macros.
212 *
213 * 2. While libpldm is a C library, its test suite is written in C++ to take
214 * advantage of gtest.
215 *
216 * 3. Ideally we'd do something with C's `static_assert()`, however
217 * `static_assert()` is defined as void, and as we're constrained to macros,
218 * using `static_assert()` would require a statement-expression
219 *
220 * 4. Currently the project is built with `-std=c17`. CPP statement-expressions
221 * are a GNU extension. We prefer to avoid switching to `-std=gnu17` just for
222 * the purpose of enabling statement-expressions in this one instance.
223 *
224 * 5. We can achieve a conditional build error using `pldm_require_obj_type()`,
225 * however it's implemented in terms of `_Generic()`, which is not available
226 * in C++.
227 *
228 * Combined this means we need separate solutions for C and C++.
229 *
230 * For C, as we don't have statement-expressions, we need to exploit some other
231 * language feature to inject a `pldm_require_obj_type()` prior to the msgbuf
232 * API function call. We also have to take care of the fact that the call-sites
233 * may be in the context of a variable assignment for error-handling purposes.
234 * The key observation is that we can use the comma operator as a sequence point
235 * to order the type check before the API call, discarding the "result" value of
236 * the type check and yielding the return value of the API call.
237 *
238 * C++ could be less of a headache than the C as we can leverage template
239 * functions. An advantage of template functions is that while their definition
240 * is driven by instantion, the definition does not appear at the source
241 * location of the instantation, which gives it a great leg-up over the problems
242 * we have in the C path. However, the use of the msgbuf APIs in the test suite
243 * still makes things somewhat tricky, as the call-sites in the test suite are
244 * wrapped up in EXPECT_*() gtest macros. Ideally we'd implement functions that
245 * takes both the object type and the required type as template arguments, and
246 * then define the object pointer parameter as `void *` for a call through to
247 * the appropriate msgbuf API. However, because the msgbuf API call-sites are
248 * encapsulated in gtest macros, use of commas in the template specification
249 * causes pre-processor confusion. In this way we're constrained to only one
250 * template argument per function.
251 *
252 * Implement the C++ path using template functions that take the destination
253 * object type as a template argument, while the name of the function symbols
254 * are derived from the required type. The manual implementations of these
255 * appear at the end of the header. The type safety is actually enforced
256 * by `static_assert()` this time, as we can use statements as we're not
257 * constrained to an expression in the templated function body.
258 *
259 * The invocations of pldm_msgbuf_extract_typecheck() typically result in
260 * double-evaluation of some arguments. We're not yet bothered by this for two
261 * reasons:
262 *
263 * 1. The nature of the current call-sites are such that there are no
264 * argument expressions that result in undesirable side-effects
265 *
266 * 2. It's an API internal to the libpldm implementation, and we can fix things
267 * whenever something crops up the violates the observation in 1.
268 */
269#ifdef __cplusplus
270#define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...) \
271 pldm_msgbuf_typecheck_##ty<decltype(dst)>(__VA_ARGS__)
272#else
273#define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...) \
274 (pldm_require_obj_type(dst, ty), fn(__VA_ARGS__))
275#endif
276
Andrew Jefferydb7b8322023-04-12 23:05:21 +0930277/**
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030278 * @brief pldm_msgbuf extractor for a uint8_t
279 *
280 * @param[inout] ctx - pldm_msgbuf context for extractor
281 * @param[out] dst - destination of extracted value
282 *
283 * @return PLDM_SUCCESS if buffer accesses were in-bounds,
284 * PLDM_ERROR_INVALID_LENGTH otherwise.
285 * PLDM_ERROR_INVALID_DATA if input a invalid ctx
286 */
Andrew Jeffery66c77232024-04-24 11:42:02 +0930287#define pldm_msgbuf_extract_uint8(ctx, dst) \
288 pldm_msgbuf_extract_typecheck(uint8_t, pldm__msgbuf_extract_uint8, \
289 dst, ctx, dst)
290// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
291static inline int pldm__msgbuf_extract_uint8(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030292{
293 if (!ctx || !ctx->cursor || !dst) {
294 return PLDM_ERROR_INVALID_DATA;
295 }
296
Andrew Jeffery66c77232024-04-24 11:42:02 +0930297 ctx->remaining -= sizeof(uint8_t);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030298 assert(ctx->remaining >= 0);
299 if (ctx->remaining < 0) {
300 return PLDM_ERROR_INVALID_LENGTH;
301 }
302
Andrew Jeffery66c77232024-04-24 11:42:02 +0930303 memcpy(dst, ctx->cursor, sizeof(uint8_t));
304
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030305 ctx->cursor++;
306 return PLDM_SUCCESS;
307}
308
Andrew Jeffery66c77232024-04-24 11:42:02 +0930309#define pldm_msgbuf_extract_int8(ctx, dst) \
310 pldm_msgbuf_extract_typecheck(int8_t, pldm__msgbuf_extract_int8, dst, \
311 ctx, dst)
312// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
313static inline int pldm__msgbuf_extract_int8(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030314{
315 if (!ctx || !ctx->cursor || !dst) {
316 return PLDM_ERROR_INVALID_DATA;
317 }
318
Andrew Jeffery66c77232024-04-24 11:42:02 +0930319 ctx->remaining -= sizeof(int8_t);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030320 assert(ctx->remaining >= 0);
321 if (ctx->remaining < 0) {
322 return PLDM_ERROR_INVALID_LENGTH;
323 }
324
Andrew Jeffery66c77232024-04-24 11:42:02 +0930325 memcpy(dst, ctx->cursor, sizeof(int8_t));
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030326 ctx->cursor++;
327 return PLDM_SUCCESS;
328}
329
Andrew Jeffery66c77232024-04-24 11:42:02 +0930330#define pldm_msgbuf_extract_uint16(ctx, dst) \
331 pldm_msgbuf_extract_typecheck(uint16_t, pldm__msgbuf_extract_uint16, \
332 dst, ctx, dst)
333// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
334static inline int pldm__msgbuf_extract_uint16(struct pldm_msgbuf *ctx,
335 void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030336{
337 uint16_t ldst;
338
339 if (!ctx || !ctx->cursor || !dst) {
340 return PLDM_ERROR_INVALID_DATA;
341 }
342
343 // Check for buffer overflow. If we overflow, account for the request as
344 // negative values in ctx->remaining. This way we can debug how far
345 // we've overflowed.
346 ctx->remaining -= sizeof(ldst);
347
348 // Prevent the access if it would overflow. First, assert so we blow up
349 // the test suite right at the point of failure. However, cater to
350 // -DNDEBUG by explicitly testing that the access is valid.
351 assert(ctx->remaining >= 0);
352 if (ctx->remaining < 0) {
353 return PLDM_ERROR_INVALID_LENGTH;
354 }
355
356 // Use memcpy() to have the compiler deal with any alignment
357 // issues on the target architecture
358 memcpy(&ldst, ctx->cursor, sizeof(ldst));
359
360 // Only assign the target value once it's correctly decoded
Andrew Jeffery66c77232024-04-24 11:42:02 +0930361 ldst = le16toh(ldst);
362
363 // Allow storing to unaligned
364 memcpy(dst, &ldst, sizeof(ldst));
365
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030366 ctx->cursor += sizeof(ldst);
367
368 return PLDM_SUCCESS;
369}
370
Andrew Jeffery66c77232024-04-24 11:42:02 +0930371#define pldm_msgbuf_extract_int16(ctx, dst) \
372 pldm_msgbuf_extract_typecheck(int16_t, pldm__msgbuf_extract_int16, \
373 dst, ctx, dst)
374// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
375static inline int pldm__msgbuf_extract_int16(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030376{
377 int16_t ldst;
378
379 if (!ctx || !ctx->cursor || !dst) {
380 return PLDM_ERROR_INVALID_DATA;
381 }
382
383 ctx->remaining -= sizeof(ldst);
384 assert(ctx->remaining >= 0);
385 if (ctx->remaining < 0) {
386 return PLDM_ERROR_INVALID_LENGTH;
387 }
388
389 memcpy(&ldst, ctx->cursor, sizeof(ldst));
390
Andrew Jeffery66c77232024-04-24 11:42:02 +0930391 ldst = le16toh(ldst);
392 memcpy(dst, &ldst, sizeof(ldst));
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030393 ctx->cursor += sizeof(ldst);
394
395 return PLDM_SUCCESS;
396}
397
Andrew Jeffery66c77232024-04-24 11:42:02 +0930398#define pldm_msgbuf_extract_uint32(ctx, dst) \
399 pldm_msgbuf_extract_typecheck(uint32_t, pldm__msgbuf_extract_uint32, \
400 dst, ctx, dst)
401// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
402static inline int pldm__msgbuf_extract_uint32(struct pldm_msgbuf *ctx,
403 void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030404{
405 uint32_t ldst;
406
407 if (!ctx || !ctx->cursor || !dst) {
408 return PLDM_ERROR_INVALID_DATA;
409 }
410
411 ctx->remaining -= sizeof(ldst);
412 assert(ctx->remaining >= 0);
413 if (ctx->remaining < 0) {
414 return PLDM_ERROR_INVALID_LENGTH;
415 }
416
417 memcpy(&ldst, ctx->cursor, sizeof(ldst));
418
Andrew Jeffery66c77232024-04-24 11:42:02 +0930419 ldst = le32toh(ldst);
420 memcpy(dst, &ldst, sizeof(ldst));
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030421 ctx->cursor += sizeof(ldst);
422
423 return PLDM_SUCCESS;
424}
425
Andrew Jeffery66c77232024-04-24 11:42:02 +0930426#define pldm_msgbuf_extract_int32(ctx, dst) \
427 pldm_msgbuf_extract_typecheck(int32_t, pldm__msgbuf_extract_int32, \
428 dst, ctx, dst)
429// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
430static inline int pldm__msgbuf_extract_int32(struct pldm_msgbuf *ctx, void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030431{
432 int32_t ldst;
433
434 if (!ctx || !ctx->cursor || !dst) {
435 return PLDM_ERROR_INVALID_DATA;
436 }
437
438 ctx->remaining -= sizeof(ldst);
439 assert(ctx->remaining >= 0);
440 if (ctx->remaining < 0) {
441 return PLDM_ERROR_INVALID_LENGTH;
442 }
443
444 memcpy(&ldst, ctx->cursor, sizeof(ldst));
445
Andrew Jeffery66c77232024-04-24 11:42:02 +0930446 ldst = le32toh(ldst);
447 memcpy(dst, &ldst, sizeof(ldst));
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030448 ctx->cursor += sizeof(ldst);
449
450 return PLDM_SUCCESS;
451}
452
Andrew Jeffery66c77232024-04-24 11:42:02 +0930453#define pldm_msgbuf_extract_real32(ctx, dst) \
454 pldm_msgbuf_extract_typecheck(real32_t, pldm__msgbuf_extract_real32, \
455 dst, ctx, dst)
456// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
457static inline int pldm__msgbuf_extract_real32(struct pldm_msgbuf *ctx,
458 void *dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030459{
460 uint32_t ldst;
461
Andrew Jeffery66c77232024-04-24 11:42:02 +0930462 _Static_assert(sizeof(real32_t) == sizeof(ldst),
463 "Mismatched type sizes for dst and ldst");
464
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030465 if (!ctx || !ctx->cursor || !dst) {
466 return PLDM_ERROR_INVALID_DATA;
467 }
468
469 ctx->remaining -= sizeof(ldst);
470 assert(ctx->remaining >= 0);
471 if (ctx->remaining < 0) {
472 return PLDM_ERROR_INVALID_LENGTH;
473 }
474
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030475 memcpy(&ldst, ctx->cursor, sizeof(ldst));
476 ldst = le32toh(ldst);
Andrew Jeffery66c77232024-04-24 11:42:02 +0930477 memcpy(dst, &ldst, sizeof(ldst));
478 ctx->cursor += sizeof(ldst);
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030479
480 return PLDM_SUCCESS;
481}
482
Andrew Jeffery66c77232024-04-24 11:42:02 +0930483/**
484 * Extract the field at the msgbuf cursor into the lvalue named by dst.
485 *
486 * @param ctx The msgbuf context object
487 * @param dst The lvalue into which the field at the msgbuf cursor should be
488 * extracted
489 *
490 * @return PLDM_SUCCESS on success, otherwise another value on error
491 */
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030492#define pldm_msgbuf_extract(ctx, dst) \
Andrew Jeffery66c77232024-04-24 11:42:02 +0930493 _Generic((dst), \
494 uint8_t: pldm__msgbuf_extract_uint8, \
495 int8_t: pldm__msgbuf_extract_int8, \
496 uint16_t: pldm__msgbuf_extract_uint16, \
497 int16_t: pldm__msgbuf_extract_int16, \
498 uint32_t: pldm__msgbuf_extract_uint32, \
499 int32_t: pldm__msgbuf_extract_int32, \
500 real32_t: pldm__msgbuf_extract_real32)(ctx, (void *)&(dst))
501
502/**
503 * Extract the field at the msgbuf cursor into the object pointed-to by dst.
504 *
505 * @param ctx The msgbuf context object
506 * @param dst The pointer to the object into which the field at the msgbuf
507 * cursor should be extracted
508 *
509 * @return PLDM_SUCCESS on success, otherwise another value on error
510 */
511#define pldm_msgbuf_extract_p(ctx, dst) \
512 _Generic((dst), \
513 uint8_t *: pldm__msgbuf_extract_uint8, \
514 int8_t *: pldm__msgbuf_extract_int8, \
515 uint16_t *: pldm__msgbuf_extract_uint16, \
516 int16_t *: pldm__msgbuf_extract_int16, \
517 uint32_t *: pldm__msgbuf_extract_uint32, \
518 int32_t *: pldm__msgbuf_extract_int32, \
519 real32_t *: pldm__msgbuf_extract_real32)(ctx, dst)
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030520
Andrew Jeffery369b1212023-04-20 15:44:48 +0930521static inline int pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx,
522 uint8_t *dst, size_t count)
523{
Andrew Jeffery369b1212023-04-20 15:44:48 +0930524 if (!ctx || !ctx->cursor || !dst) {
525 return PLDM_ERROR_INVALID_DATA;
526 }
527
528 if (!count) {
529 return PLDM_SUCCESS;
530 }
531
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030532 if (count >= SSIZE_MAX) {
Andrew Jeffery369b1212023-04-20 15:44:48 +0930533 return PLDM_ERROR_INVALID_LENGTH;
534 }
535
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030536 ctx->remaining -= (ssize_t)count;
Andrew Jeffery369b1212023-04-20 15:44:48 +0930537 assert(ctx->remaining >= 0);
538 if (ctx->remaining < 0) {
539 return PLDM_ERROR_INVALID_LENGTH;
540 }
541
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030542 memcpy(dst, ctx->cursor, count);
543 ctx->cursor += count;
Andrew Jeffery369b1212023-04-20 15:44:48 +0930544
545 return PLDM_SUCCESS;
546}
547
548#define pldm_msgbuf_extract_array(ctx, dst, count) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930549 _Generic((*(dst)), uint8_t: pldm_msgbuf_extract_array_uint8)(ctx, dst, \
550 count)
Andrew Jeffery369b1212023-04-20 15:44:48 +0930551
Thu Nguyen062c8762023-04-22 20:45:04 +0700552static inline int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx,
553 const uint32_t src)
554{
555 uint32_t val = htole32(src);
556
557 if (!ctx || !ctx->cursor) {
558 return PLDM_ERROR_INVALID_DATA;
559 }
560
561 ctx->remaining -= sizeof(src);
562 assert(ctx->remaining >= 0);
563 if (ctx->remaining < 0) {
564 return PLDM_ERROR_INVALID_LENGTH;
565 }
566
567 memcpy(ctx->cursor, &val, sizeof(val));
568 ctx->cursor += sizeof(src);
569
570 return PLDM_SUCCESS;
571}
572
573static inline int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx,
574 const uint16_t src)
575{
576 uint16_t val = htole16(src);
577
578 if (!ctx || !ctx->cursor) {
579 return PLDM_ERROR_INVALID_DATA;
580 }
581
582 ctx->remaining -= sizeof(src);
583 assert(ctx->remaining >= 0);
584 if (ctx->remaining < 0) {
585 return PLDM_ERROR_INVALID_LENGTH;
586 }
587
588 memcpy(ctx->cursor, &val, sizeof(val));
589 ctx->cursor += sizeof(src);
590
591 return PLDM_SUCCESS;
592}
593
594static inline int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx,
595 const uint8_t src)
596{
597 if (!ctx || !ctx->cursor) {
598 return PLDM_ERROR_INVALID_DATA;
599 }
600
601 ctx->remaining -= sizeof(src);
602 assert(ctx->remaining >= 0);
603 if (ctx->remaining < 0) {
604 return PLDM_ERROR_INVALID_LENGTH;
605 }
606
607 memcpy(ctx->cursor, &src, sizeof(src));
608 ctx->cursor += sizeof(src);
609
610 return PLDM_SUCCESS;
611}
612
613static inline int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx,
614 const int32_t src)
615{
616 int32_t val = htole32(src);
617
618 if (!ctx || !ctx->cursor) {
619 return PLDM_ERROR_INVALID_DATA;
620 }
621
622 ctx->remaining -= sizeof(src);
623 assert(ctx->remaining >= 0);
624 if (ctx->remaining < 0) {
625 return PLDM_ERROR_INVALID_LENGTH;
626 }
627
628 memcpy(ctx->cursor, &val, sizeof(val));
629 ctx->cursor += sizeof(src);
630
631 return PLDM_SUCCESS;
632}
633
634static inline int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx,
635 const int16_t src)
636{
637 int16_t val = htole16(src);
638
639 if (!ctx || !ctx->cursor) {
640 return PLDM_ERROR_INVALID_DATA;
641 }
642
643 ctx->remaining -= sizeof(src);
644 assert(ctx->remaining >= 0);
645 if (ctx->remaining < 0) {
646 return PLDM_ERROR_INVALID_LENGTH;
647 }
648
649 memcpy(ctx->cursor, &val, sizeof(val));
650 ctx->cursor += sizeof(src);
651
652 return PLDM_SUCCESS;
653}
654
655static inline int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx,
656 const int8_t src)
657{
658 if (!ctx || !ctx->cursor) {
659 return PLDM_ERROR_INVALID_DATA;
660 }
661
662 ctx->remaining -= sizeof(src);
663 assert(ctx->remaining >= 0);
664 if (ctx->remaining < 0) {
665 return PLDM_ERROR_INVALID_LENGTH;
666 }
667
668 memcpy(ctx->cursor, &src, sizeof(src));
669 ctx->cursor += sizeof(src);
670
671 return PLDM_SUCCESS;
672}
673
674#define pldm_msgbuf_insert(dst, src) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930675 _Generic((src), \
676 uint8_t: pldm_msgbuf_insert_uint8, \
677 int8_t: pldm_msgbuf_insert_int8, \
678 uint16_t: pldm_msgbuf_insert_uint16, \
679 int16_t: pldm_msgbuf_insert_int16, \
680 uint32_t: pldm_msgbuf_insert_uint32, \
681 int32_t: pldm_msgbuf_insert_int32)(dst, src)
Thu Nguyen062c8762023-04-22 20:45:04 +0700682
683static inline int pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx,
684 const uint8_t *src,
685 size_t count)
686{
Thu Nguyen062c8762023-04-22 20:45:04 +0700687 if (!ctx || !ctx->cursor || !src) {
688 return PLDM_ERROR_INVALID_DATA;
689 }
690
691 if (!count) {
692 return PLDM_SUCCESS;
693 }
694
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030695 if (count >= SSIZE_MAX) {
Thu Nguyen062c8762023-04-22 20:45:04 +0700696 return PLDM_ERROR_INVALID_LENGTH;
697 }
698
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030699 ctx->remaining -= (ssize_t)count;
Thu Nguyen062c8762023-04-22 20:45:04 +0700700 assert(ctx->remaining >= 0);
701 if (ctx->remaining < 0) {
702 return PLDM_ERROR_INVALID_LENGTH;
703 }
704
Andrew Jefferya065ecc2023-10-27 15:02:11 +1030705 memcpy(ctx->cursor, src, count);
706 ctx->cursor += count;
Thu Nguyen062c8762023-04-22 20:45:04 +0700707
708 return PLDM_SUCCESS;
709}
710
711#define pldm_msgbuf_insert_array(dst, src, count) \
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930712 _Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src, \
713 count)
Thu Nguyen062c8762023-04-22 20:45:04 +0700714
715static inline int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx,
716 size_t required, void **cursor)
717{
718 if (!ctx || !ctx->cursor || !cursor || *cursor) {
719 return PLDM_ERROR_INVALID_DATA;
720 }
721
722 if (required > SSIZE_MAX) {
723 return PLDM_ERROR_INVALID_LENGTH;
724 }
725
726 ctx->remaining -= (ssize_t)required;
727 assert(ctx->remaining >= 0);
728 if (ctx->remaining < 0) {
729 return PLDM_ERROR_INVALID_LENGTH;
730 }
731
732 *cursor = ctx->cursor;
733 ctx->cursor += required;
734
735 return PLDM_SUCCESS;
736}
737
738static inline int pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx,
739 void **cursor, size_t *len)
740{
741 if (!ctx || !ctx->cursor || !cursor || *cursor || !len) {
742 return PLDM_ERROR_INVALID_DATA;
743 }
744
745 assert(ctx->remaining >= 0);
746 if (ctx->remaining < 0) {
747 return PLDM_ERROR_INVALID_LENGTH;
748 }
749
750 *cursor = ctx->cursor;
751 ctx->cursor += ctx->remaining;
752 *len = ctx->remaining;
753 ctx->remaining = 0;
754
755 return PLDM_SUCCESS;
756}
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030757#ifdef __cplusplus
758}
759#endif
760
Andrew Jeffery66c77232024-04-24 11:42:02 +0930761#ifdef __cplusplus
762#include <type_traits>
763
764template <typename T>
765static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx,
766 void *buf)
767{
768 static_assert(std::is_same<uint8_t *, T>::value);
769 return pldm__msgbuf_extract_uint8(ctx, buf);
770}
771
772template <typename T>
773static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx,
774 void *buf)
775{
776 static_assert(std::is_same<int8_t *, T>::value);
777 return pldm__msgbuf_extract_int8(ctx, buf);
778}
779
780template <typename T>
781static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx,
782 void *buf)
783{
784 static_assert(std::is_same<uint16_t *, T>::value);
785 return pldm__msgbuf_extract_uint16(ctx, buf);
786}
787
788template <typename T>
789static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx,
790 void *buf)
791{
792 static_assert(std::is_same<int16_t *, T>::value);
793 return pldm__msgbuf_extract_int16(ctx, buf);
794}
795
796template <typename T>
797static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx,
798 void *buf)
799{
800 static_assert(std::is_same<uint32_t *, T>::value);
801 return pldm__msgbuf_extract_uint32(ctx, buf);
802}
803
804template <typename T>
805static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx,
806 void *buf)
807{
808 static_assert(std::is_same<int32_t *, T>::value);
809 return pldm__msgbuf_extract_int32(ctx, buf);
810}
811
812template <typename T>
813static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx,
814 void *buf)
815{
816 static_assert(std::is_same<real32_t *, T>::value);
817 return pldm__msgbuf_extract_real32(ctx, buf);
818}
819#endif
820
Andrew Jefferyc63f63a2023-02-24 22:29:33 +1030821#endif /* BUF_H */