xref: /openbmc/libpldm/src/msgbuf.h (revision 66c7723a)
1691668feSPatrick Williams /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2c63f63a2SAndrew Jeffery #ifndef PLDM_MSGBUF_H
3c63f63a2SAndrew Jeffery #define PLDM_MSGBUF_H
4c63f63a2SAndrew Jeffery 
5*66c7723aSAndrew Jeffery /*
6*66c7723aSAndrew Jeffery  * Historically, many of the structs exposed in libpldm's public headers are
7*66c7723aSAndrew Jeffery  * defined with __attribute__((packed)). This is unfortunate: it gives the
8*66c7723aSAndrew Jeffery  * impression that a wire-format buffer can be cast to the message type to make
9*66c7723aSAndrew Jeffery  * the message's fields easily accessible. As it turns out, that's not
10*66c7723aSAndrew Jeffery  * that's valid for several reasons:
11*66c7723aSAndrew Jeffery  *
12*66c7723aSAndrew Jeffery  * 1. Casting the wire-format buffer to a struct of the message type doesn't
13*66c7723aSAndrew Jeffery  *    abstract the endianness of message field values
14*66c7723aSAndrew Jeffery  *
15*66c7723aSAndrew Jeffery  * 2. Some messages contain packed tagged union fields which cannot be properly
16*66c7723aSAndrew Jeffery  *    described in a C struct.
17*66c7723aSAndrew Jeffery  *
18*66c7723aSAndrew Jeffery  * The msgbuf APIs exist to assist with (un)packing the wire-format in a way
19*66c7723aSAndrew Jeffery  * that is type-safe, spatially memory-safe, endian-safe, performant, and
20*66c7723aSAndrew Jeffery  * free of undefined-behaviour. Message structs that are added to the public
21*66c7723aSAndrew Jeffery  * library API should no-longer be marked __attribute__((packed)), and the
22*66c7723aSAndrew Jeffery  * implementation of their encode and decode functions must exploit the msgbuf
23*66c7723aSAndrew Jeffery  * API.
24*66c7723aSAndrew Jeffery  *
25*66c7723aSAndrew Jeffery  * However, we would like to allow implementation of codec functions in terms of
26*66c7723aSAndrew Jeffery  * msgbuf APIs even if they're decoding a message into a (historically) packed
27*66c7723aSAndrew Jeffery  * struct. Some of the complexity that follows is a consequence of the packed/
28*66c7723aSAndrew Jeffery  * unpacked conflict.
29*66c7723aSAndrew Jeffery  */
30*66c7723aSAndrew Jeffery 
31c63f63a2SAndrew Jeffery #ifdef __cplusplus
3237dd6a3dSAndrew Jeffery /*
3337dd6a3dSAndrew Jeffery  * Fix up C11's _Static_assert() vs C++'s static_assert().
3437dd6a3dSAndrew Jeffery  *
3537dd6a3dSAndrew Jeffery  * Can we please have nice things for once.
3637dd6a3dSAndrew Jeffery  */
3737dd6a3dSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
3837dd6a3dSAndrew Jeffery #define _Static_assert(...) static_assert(__VA_ARGS__)
39c63f63a2SAndrew Jeffery extern "C" {
40c63f63a2SAndrew Jeffery #endif
41c63f63a2SAndrew Jeffery 
42b0c1d20aSAndrew Jeffery #include <libpldm/base.h>
43b0c1d20aSAndrew Jeffery #include <libpldm/pldm_types.h>
44c63f63a2SAndrew Jeffery 
45*66c7723aSAndrew Jeffery #include "compiler.h"
46*66c7723aSAndrew Jeffery 
47c63f63a2SAndrew Jeffery #include <assert.h>
48c63f63a2SAndrew Jeffery #include <endian.h>
49c63f63a2SAndrew Jeffery #include <limits.h>
50c63f63a2SAndrew Jeffery #include <stdbool.h>
51*66c7723aSAndrew Jeffery #include <stdint.h>
52c63f63a2SAndrew Jeffery #include <string.h>
53c63f63a2SAndrew Jeffery #include <sys/types.h>
54c63f63a2SAndrew Jeffery 
55c63f63a2SAndrew Jeffery struct pldm_msgbuf {
56062c8762SThu Nguyen 	uint8_t *cursor;
57c63f63a2SAndrew Jeffery 	ssize_t remaining;
58c63f63a2SAndrew Jeffery };
59c63f63a2SAndrew Jeffery 
60c63f63a2SAndrew Jeffery /**
61c63f63a2SAndrew Jeffery  * @brief Initialize pldm buf struct for buf extractor
62c63f63a2SAndrew Jeffery  *
63c63f63a2SAndrew Jeffery  * @param[out] ctx - pldm_msgbuf context for extractor
64c63f63a2SAndrew Jeffery  * @param[in] minsize - The minimum required length of buffer `buf`
65c63f63a2SAndrew Jeffery  * @param[in] buf - buffer to be extracted
66c63f63a2SAndrew Jeffery  * @param[in] len - size of buffer
67c63f63a2SAndrew Jeffery  *
68c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
69c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA if pointer parameters are invalid, or
70c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
71c63f63a2SAndrew Jeffery  */
725646f23bSAndrew Jeffery __attribute__((no_sanitize("pointer-overflow"))) static inline int
735646f23bSAndrew Jeffery pldm_msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
745646f23bSAndrew Jeffery 		 size_t len)
75c63f63a2SAndrew Jeffery {
76c63f63a2SAndrew Jeffery 	uint8_t *end;
77c63f63a2SAndrew Jeffery 
78c63f63a2SAndrew Jeffery 	if (!ctx || !buf) {
79c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
80c63f63a2SAndrew Jeffery 	}
81c63f63a2SAndrew Jeffery 
82c63f63a2SAndrew Jeffery 	if ((minsize > len) || (len > SSIZE_MAX)) {
83c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
84c63f63a2SAndrew Jeffery 	}
85c63f63a2SAndrew Jeffery 
86c63f63a2SAndrew Jeffery 	end = (uint8_t *)buf + len;
87c63f63a2SAndrew Jeffery 	if (end && end < (uint8_t *)buf) {
88c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
89c63f63a2SAndrew Jeffery 	}
90c63f63a2SAndrew Jeffery 
91c63f63a2SAndrew Jeffery 	ctx->cursor = (uint8_t *)buf;
92c63f63a2SAndrew Jeffery 	ctx->remaining = (ssize_t)len;
93c63f63a2SAndrew Jeffery 
94c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
95c63f63a2SAndrew Jeffery }
96c63f63a2SAndrew Jeffery 
97c63f63a2SAndrew Jeffery /**
98c63f63a2SAndrew Jeffery  * @brief Validate buffer overflow state
99c63f63a2SAndrew Jeffery  *
100c63f63a2SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context for extractor
101c63f63a2SAndrew Jeffery  *
102c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if there are zero or more bytes of data that remain
103c63f63a2SAndrew Jeffery  * unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
104c63f63a2SAndrew Jeffery  * prior accesses would have occurred beyond the bounds of the buffer, and
105c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
106c63f63a2SAndrew Jeffery  * pointer.
107c63f63a2SAndrew Jeffery  */
108c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
109c63f63a2SAndrew Jeffery {
110c63f63a2SAndrew Jeffery 	if (!ctx) {
111c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
112c63f63a2SAndrew Jeffery 	}
113c63f63a2SAndrew Jeffery 
114c63f63a2SAndrew Jeffery 	return ctx->remaining >= 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
115c63f63a2SAndrew Jeffery }
116c63f63a2SAndrew Jeffery 
117c63f63a2SAndrew Jeffery /**
118db7b8324SAndrew Jeffery  * @brief Test whether a message buffer has been exactly consumed
119db7b8324SAndrew Jeffery  *
120db7b8324SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context for extractor
121db7b8324SAndrew Jeffery  *
122db7b8324SAndrew Jeffery  * @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
123db7b8324SAndrew Jeffery  * the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
124db7b8324SAndrew Jeffery  * indicates that an incorrect sequence of accesses have occurred, and
125db7b8324SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
126db7b8324SAndrew Jeffery  * pointer.
127db7b8324SAndrew Jeffery  */
128db7b8324SAndrew Jeffery static inline int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
129db7b8324SAndrew Jeffery {
130db7b8324SAndrew Jeffery 	if (!ctx) {
131db7b8324SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
132db7b8324SAndrew Jeffery 	}
133db7b8324SAndrew Jeffery 
134db7b8324SAndrew Jeffery 	return ctx->remaining == 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
135db7b8324SAndrew Jeffery }
136db7b8324SAndrew Jeffery 
137db7b8324SAndrew Jeffery /**
138c63f63a2SAndrew Jeffery  * @brief Destroy the pldm buf
139c63f63a2SAndrew Jeffery  *
140c63f63a2SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context for extractor
141c63f63a2SAndrew Jeffery  *
142c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if all buffer accesses were in-bounds,
143c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
144c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
145c63f63a2SAndrew Jeffery  * bounds of the buffer.
146c63f63a2SAndrew Jeffery  */
147c63f63a2SAndrew Jeffery static inline int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
148c63f63a2SAndrew Jeffery {
149c63f63a2SAndrew Jeffery 	int valid;
150c63f63a2SAndrew Jeffery 
151c63f63a2SAndrew Jeffery 	if (!ctx) {
152c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
153c63f63a2SAndrew Jeffery 	}
154c63f63a2SAndrew Jeffery 
155c63f63a2SAndrew Jeffery 	valid = pldm_msgbuf_validate(ctx);
156c63f63a2SAndrew Jeffery 
157c63f63a2SAndrew Jeffery 	ctx->cursor = NULL;
158c63f63a2SAndrew Jeffery 	ctx->remaining = 0;
159c63f63a2SAndrew Jeffery 
160c63f63a2SAndrew Jeffery 	return valid;
161c63f63a2SAndrew Jeffery }
162c63f63a2SAndrew Jeffery 
163c63f63a2SAndrew Jeffery /**
164db7b8324SAndrew Jeffery  * @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
165db7b8324SAndrew Jeffery  * has been completely consumed without overflow
166db7b8324SAndrew Jeffery  *
167db7b8324SAndrew Jeffery  * @param[in] ctx - pldm_msgbuf context
168db7b8324SAndrew Jeffery  *
169db7b8324SAndrew Jeffery  * @return PLDM_SUCCESS if all buffer access were in-bounds and completely
170db7b8324SAndrew Jeffery  * consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
171db7b8324SAndrew Jeffery  * parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
172db7b8324SAndrew Jeffery  * have occurred byond the bounds of the buffer
173db7b8324SAndrew Jeffery  */
174db7b8324SAndrew Jeffery static inline int pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
175db7b8324SAndrew Jeffery {
176db7b8324SAndrew Jeffery 	int consumed;
177db7b8324SAndrew Jeffery 
178db7b8324SAndrew Jeffery 	if (!ctx) {
179db7b8324SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
180db7b8324SAndrew Jeffery 	}
181db7b8324SAndrew Jeffery 
182db7b8324SAndrew Jeffery 	consumed = pldm_msgbuf_consumed(ctx);
183db7b8324SAndrew Jeffery 
184db7b8324SAndrew Jeffery 	ctx->cursor = NULL;
185db7b8324SAndrew Jeffery 	ctx->remaining = 0;
186db7b8324SAndrew Jeffery 
187db7b8324SAndrew Jeffery 	return consumed;
188db7b8324SAndrew Jeffery }
189db7b8324SAndrew Jeffery 
190*66c7723aSAndrew Jeffery /*
191*66c7723aSAndrew Jeffery  * Exploit the pre-processor to perform type checking by macro substitution.
192*66c7723aSAndrew Jeffery  *
193*66c7723aSAndrew Jeffery  * A C type is defined by its alignment as well as its object
194*66c7723aSAndrew Jeffery  * size, and compilers have a hammer to enforce it in the form of
195*66c7723aSAndrew Jeffery  * `-Waddress-of-packed-member`. Due to the unpacked/packed struct conflict in
196*66c7723aSAndrew Jeffery  * the libpldm public API this presents a problem: Naively attempting to use the
197*66c7723aSAndrew Jeffery  * msgbuf APIs on a member of a packed struct would yield an error.
198*66c7723aSAndrew Jeffery  *
199*66c7723aSAndrew Jeffery  * The msgbuf APIs are implemented such that data is moved through unaligned
200*66c7723aSAndrew Jeffery  * pointers in a safe way, but to mitigate `-Waddress-of-packed-member` we must
201*66c7723aSAndrew Jeffery  * make the object pointers take a trip through `void *` at its API boundary.
202*66c7723aSAndrew Jeffery  * That presents a bit too much of an opportunity to non-surgically remove your
203*66c7723aSAndrew Jeffery  * own foot, so here we set about doing something to mitigate that as well.
204*66c7723aSAndrew Jeffery  *
205*66c7723aSAndrew Jeffery  * pldm_msgbuf_extract_typecheck() exists to enforce pointer type correctness
206*66c7723aSAndrew Jeffery  * only for the purpose of object sizes, disregarding alignment. We have a few
207*66c7723aSAndrew Jeffery  * constraints that cause some headaches:
208*66c7723aSAndrew Jeffery  *
209*66c7723aSAndrew Jeffery  * 1. We have to perform the type-check before a call through a C function,
210*66c7723aSAndrew Jeffery  *    as the function must take the object pointer argument as `void *`.
211*66c7723aSAndrew Jeffery  *    Essentially, this constrains us to doing something with macros.
212*66c7723aSAndrew Jeffery  *
213*66c7723aSAndrew Jeffery  * 2. While libpldm is a C library, its test suite is written in C++ to take
214*66c7723aSAndrew Jeffery  *    advantage of gtest.
215*66c7723aSAndrew Jeffery  *
216*66c7723aSAndrew Jeffery  * 3. Ideally we'd do something with C's `static_assert()`, however
217*66c7723aSAndrew Jeffery  *    `static_assert()` is defined as void, and as we're constrained to macros,
218*66c7723aSAndrew Jeffery  *    using `static_assert()` would require a statement-expression
219*66c7723aSAndrew Jeffery  *
220*66c7723aSAndrew Jeffery  * 4. Currently the project is built with `-std=c17`. CPP statement-expressions
221*66c7723aSAndrew Jeffery  *    are a GNU extension. We prefer to avoid switching to `-std=gnu17` just for
222*66c7723aSAndrew Jeffery  *    the purpose of enabling statement-expressions in this one instance.
223*66c7723aSAndrew Jeffery  *
224*66c7723aSAndrew Jeffery  * 5. We can achieve a conditional build error using `pldm_require_obj_type()`,
225*66c7723aSAndrew Jeffery  *    however it's implemented in terms of `_Generic()`, which is not available
226*66c7723aSAndrew Jeffery  *    in C++.
227*66c7723aSAndrew Jeffery  *
228*66c7723aSAndrew Jeffery  * Combined this means we need separate solutions for C and C++.
229*66c7723aSAndrew Jeffery  *
230*66c7723aSAndrew Jeffery  * For C, as we don't have statement-expressions, we need to exploit some other
231*66c7723aSAndrew Jeffery  * language feature to inject a `pldm_require_obj_type()` prior to the msgbuf
232*66c7723aSAndrew Jeffery  * API function call. We also have to take care of the fact that the call-sites
233*66c7723aSAndrew Jeffery  * may be in the context of a variable assignment for error-handling purposes.
234*66c7723aSAndrew Jeffery  * The key observation is that we can use the comma operator as a sequence point
235*66c7723aSAndrew Jeffery  * to order the type check before the API call, discarding the "result" value of
236*66c7723aSAndrew Jeffery  * the type check and yielding the return value of the API call.
237*66c7723aSAndrew Jeffery  *
238*66c7723aSAndrew Jeffery  * C++ could be less of a headache than the C as we can leverage template
239*66c7723aSAndrew Jeffery  * functions. An advantage of template functions is that while their definition
240*66c7723aSAndrew Jeffery  * is driven by instantion, the definition does not appear at the source
241*66c7723aSAndrew Jeffery  * location of the instantation, which gives it a great leg-up over the problems
242*66c7723aSAndrew Jeffery  * we have in the C path. However, the use of the msgbuf APIs in the test suite
243*66c7723aSAndrew Jeffery  * still makes things somewhat tricky, as the call-sites in the test suite are
244*66c7723aSAndrew Jeffery  * wrapped up in EXPECT_*() gtest macros. Ideally we'd implement functions that
245*66c7723aSAndrew Jeffery  * takes both the object type and the required type as template arguments, and
246*66c7723aSAndrew Jeffery  * then define the object pointer parameter as `void *` for a call through to
247*66c7723aSAndrew Jeffery  * the appropriate msgbuf API. However, because the msgbuf API call-sites are
248*66c7723aSAndrew Jeffery  * encapsulated in gtest macros, use of commas in the template specification
249*66c7723aSAndrew Jeffery  * causes pre-processor confusion. In this way we're constrained to only one
250*66c7723aSAndrew Jeffery  * template argument per function.
251*66c7723aSAndrew Jeffery  *
252*66c7723aSAndrew Jeffery  * Implement the C++ path using template functions that take the destination
253*66c7723aSAndrew Jeffery  * object type as a template argument, while the name of the function symbols
254*66c7723aSAndrew Jeffery  * are derived from the required type. The manual implementations of these
255*66c7723aSAndrew Jeffery  * appear at the end of the header. The type safety is actually enforced
256*66c7723aSAndrew Jeffery  * by `static_assert()` this time, as we can use statements as we're not
257*66c7723aSAndrew Jeffery  * constrained to an expression in the templated function body.
258*66c7723aSAndrew Jeffery  *
259*66c7723aSAndrew Jeffery  * The invocations of pldm_msgbuf_extract_typecheck() typically result in
260*66c7723aSAndrew Jeffery  * double-evaluation of some arguments. We're not yet bothered by this for two
261*66c7723aSAndrew Jeffery  * reasons:
262*66c7723aSAndrew Jeffery  *
263*66c7723aSAndrew Jeffery  * 1. The nature of the current call-sites are such that there are no
264*66c7723aSAndrew Jeffery  *    argument expressions that result in undesirable side-effects
265*66c7723aSAndrew Jeffery  *
266*66c7723aSAndrew Jeffery  * 2. It's an API internal to the libpldm implementation, and we can fix things
267*66c7723aSAndrew Jeffery  *    whenever something crops up the violates the observation in 1.
268*66c7723aSAndrew Jeffery  */
269*66c7723aSAndrew Jeffery #ifdef __cplusplus
270*66c7723aSAndrew Jeffery #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...)                        \
271*66c7723aSAndrew Jeffery 	pldm_msgbuf_typecheck_##ty<decltype(dst)>(__VA_ARGS__)
272*66c7723aSAndrew Jeffery #else
273*66c7723aSAndrew Jeffery #define pldm_msgbuf_extract_typecheck(ty, fn, dst, ...)                        \
274*66c7723aSAndrew Jeffery 	(pldm_require_obj_type(dst, ty), fn(__VA_ARGS__))
275*66c7723aSAndrew Jeffery #endif
276*66c7723aSAndrew Jeffery 
277db7b8324SAndrew Jeffery /**
278c63f63a2SAndrew Jeffery  * @brief pldm_msgbuf extractor for a uint8_t
279c63f63a2SAndrew Jeffery  *
280c63f63a2SAndrew Jeffery  * @param[inout] ctx - pldm_msgbuf context for extractor
281c63f63a2SAndrew Jeffery  * @param[out] dst - destination of extracted value
282c63f63a2SAndrew Jeffery  *
283c63f63a2SAndrew Jeffery  * @return PLDM_SUCCESS if buffer accesses were in-bounds,
284c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_LENGTH otherwise.
285c63f63a2SAndrew Jeffery  * PLDM_ERROR_INVALID_DATA if input a invalid ctx
286c63f63a2SAndrew Jeffery  */
287*66c7723aSAndrew Jeffery #define pldm_msgbuf_extract_uint8(ctx, dst)                                    \
288*66c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(uint8_t, pldm__msgbuf_extract_uint8,     \
289*66c7723aSAndrew Jeffery 				      dst, ctx, dst)
290*66c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
291*66c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_uint8(struct pldm_msgbuf *ctx, void *dst)
292c63f63a2SAndrew Jeffery {
293c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
294c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
295c63f63a2SAndrew Jeffery 	}
296c63f63a2SAndrew Jeffery 
297*66c7723aSAndrew Jeffery 	ctx->remaining -= sizeof(uint8_t);
298c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
299c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
300c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
301c63f63a2SAndrew Jeffery 	}
302c63f63a2SAndrew Jeffery 
303*66c7723aSAndrew Jeffery 	memcpy(dst, ctx->cursor, sizeof(uint8_t));
304*66c7723aSAndrew Jeffery 
305c63f63a2SAndrew Jeffery 	ctx->cursor++;
306c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
307c63f63a2SAndrew Jeffery }
308c63f63a2SAndrew Jeffery 
309*66c7723aSAndrew Jeffery #define pldm_msgbuf_extract_int8(ctx, dst)                                     \
310*66c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(int8_t, pldm__msgbuf_extract_int8, dst,  \
311*66c7723aSAndrew Jeffery 				      ctx, dst)
312*66c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
313*66c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_int8(struct pldm_msgbuf *ctx, void *dst)
314c63f63a2SAndrew Jeffery {
315c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
316c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
317c63f63a2SAndrew Jeffery 	}
318c63f63a2SAndrew Jeffery 
319*66c7723aSAndrew Jeffery 	ctx->remaining -= sizeof(int8_t);
320c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
321c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
322c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
323c63f63a2SAndrew Jeffery 	}
324c63f63a2SAndrew Jeffery 
325*66c7723aSAndrew Jeffery 	memcpy(dst, ctx->cursor, sizeof(int8_t));
326c63f63a2SAndrew Jeffery 	ctx->cursor++;
327c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
328c63f63a2SAndrew Jeffery }
329c63f63a2SAndrew Jeffery 
330*66c7723aSAndrew Jeffery #define pldm_msgbuf_extract_uint16(ctx, dst)                                   \
331*66c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(uint16_t, pldm__msgbuf_extract_uint16,   \
332*66c7723aSAndrew Jeffery 				      dst, ctx, dst)
333*66c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
334*66c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_uint16(struct pldm_msgbuf *ctx,
335*66c7723aSAndrew Jeffery 					      void *dst)
336c63f63a2SAndrew Jeffery {
337c63f63a2SAndrew Jeffery 	uint16_t ldst;
338c63f63a2SAndrew Jeffery 
339c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
340c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
341c63f63a2SAndrew Jeffery 	}
342c63f63a2SAndrew Jeffery 
343c63f63a2SAndrew Jeffery 	// Check for buffer overflow. If we overflow, account for the request as
344c63f63a2SAndrew Jeffery 	// negative values in ctx->remaining. This way we can debug how far
345c63f63a2SAndrew Jeffery 	// we've overflowed.
346c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
347c63f63a2SAndrew Jeffery 
348c63f63a2SAndrew Jeffery 	// Prevent the access if it would overflow. First, assert so we blow up
349c63f63a2SAndrew Jeffery 	// the test suite right at the point of failure. However, cater to
350c63f63a2SAndrew Jeffery 	// -DNDEBUG by explicitly testing that the access is valid.
351c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
352c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
353c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
354c63f63a2SAndrew Jeffery 	}
355c63f63a2SAndrew Jeffery 
356c63f63a2SAndrew Jeffery 	// Use memcpy() to have the compiler deal with any alignment
357c63f63a2SAndrew Jeffery 	// issues on the target architecture
358c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
359c63f63a2SAndrew Jeffery 
360c63f63a2SAndrew Jeffery 	// Only assign the target value once it's correctly decoded
361*66c7723aSAndrew Jeffery 	ldst = le16toh(ldst);
362*66c7723aSAndrew Jeffery 
363*66c7723aSAndrew Jeffery 	// Allow storing to unaligned
364*66c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
365*66c7723aSAndrew Jeffery 
366c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
367c63f63a2SAndrew Jeffery 
368c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
369c63f63a2SAndrew Jeffery }
370c63f63a2SAndrew Jeffery 
371*66c7723aSAndrew Jeffery #define pldm_msgbuf_extract_int16(ctx, dst)                                    \
372*66c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(int16_t, pldm__msgbuf_extract_int16,     \
373*66c7723aSAndrew Jeffery 				      dst, ctx, dst)
374*66c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
375*66c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_int16(struct pldm_msgbuf *ctx, void *dst)
376c63f63a2SAndrew Jeffery {
377c63f63a2SAndrew Jeffery 	int16_t ldst;
378c63f63a2SAndrew Jeffery 
379c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
380c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
381c63f63a2SAndrew Jeffery 	}
382c63f63a2SAndrew Jeffery 
383c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
384c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
385c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
386c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
387c63f63a2SAndrew Jeffery 	}
388c63f63a2SAndrew Jeffery 
389c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
390c63f63a2SAndrew Jeffery 
391*66c7723aSAndrew Jeffery 	ldst = le16toh(ldst);
392*66c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
393c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
394c63f63a2SAndrew Jeffery 
395c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
396c63f63a2SAndrew Jeffery }
397c63f63a2SAndrew Jeffery 
398*66c7723aSAndrew Jeffery #define pldm_msgbuf_extract_uint32(ctx, dst)                                   \
399*66c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(uint32_t, pldm__msgbuf_extract_uint32,   \
400*66c7723aSAndrew Jeffery 				      dst, ctx, dst)
401*66c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
402*66c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_uint32(struct pldm_msgbuf *ctx,
403*66c7723aSAndrew Jeffery 					      void *dst)
404c63f63a2SAndrew Jeffery {
405c63f63a2SAndrew Jeffery 	uint32_t ldst;
406c63f63a2SAndrew Jeffery 
407c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
408c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
409c63f63a2SAndrew Jeffery 	}
410c63f63a2SAndrew Jeffery 
411c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
412c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
413c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
414c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
415c63f63a2SAndrew Jeffery 	}
416c63f63a2SAndrew Jeffery 
417c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
418c63f63a2SAndrew Jeffery 
419*66c7723aSAndrew Jeffery 	ldst = le32toh(ldst);
420*66c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
421c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
422c63f63a2SAndrew Jeffery 
423c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
424c63f63a2SAndrew Jeffery }
425c63f63a2SAndrew Jeffery 
426*66c7723aSAndrew Jeffery #define pldm_msgbuf_extract_int32(ctx, dst)                                    \
427*66c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(int32_t, pldm__msgbuf_extract_int32,     \
428*66c7723aSAndrew Jeffery 				      dst, ctx, dst)
429*66c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
430*66c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_int32(struct pldm_msgbuf *ctx, void *dst)
431c63f63a2SAndrew Jeffery {
432c63f63a2SAndrew Jeffery 	int32_t ldst;
433c63f63a2SAndrew Jeffery 
434c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
435c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
436c63f63a2SAndrew Jeffery 	}
437c63f63a2SAndrew Jeffery 
438c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
439c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
440c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
441c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
442c63f63a2SAndrew Jeffery 	}
443c63f63a2SAndrew Jeffery 
444c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
445c63f63a2SAndrew Jeffery 
446*66c7723aSAndrew Jeffery 	ldst = le32toh(ldst);
447*66c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
448c63f63a2SAndrew Jeffery 	ctx->cursor += sizeof(ldst);
449c63f63a2SAndrew Jeffery 
450c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
451c63f63a2SAndrew Jeffery }
452c63f63a2SAndrew Jeffery 
453*66c7723aSAndrew Jeffery #define pldm_msgbuf_extract_real32(ctx, dst)                                   \
454*66c7723aSAndrew Jeffery 	pldm_msgbuf_extract_typecheck(real32_t, pldm__msgbuf_extract_real32,   \
455*66c7723aSAndrew Jeffery 				      dst, ctx, dst)
456*66c7723aSAndrew Jeffery // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
457*66c7723aSAndrew Jeffery static inline int pldm__msgbuf_extract_real32(struct pldm_msgbuf *ctx,
458*66c7723aSAndrew Jeffery 					      void *dst)
459c63f63a2SAndrew Jeffery {
460c63f63a2SAndrew Jeffery 	uint32_t ldst;
461c63f63a2SAndrew Jeffery 
462*66c7723aSAndrew Jeffery 	_Static_assert(sizeof(real32_t) == sizeof(ldst),
463*66c7723aSAndrew Jeffery 		       "Mismatched type sizes for dst and ldst");
464*66c7723aSAndrew Jeffery 
465c63f63a2SAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
466c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
467c63f63a2SAndrew Jeffery 	}
468c63f63a2SAndrew Jeffery 
469c63f63a2SAndrew Jeffery 	ctx->remaining -= sizeof(ldst);
470c63f63a2SAndrew Jeffery 	assert(ctx->remaining >= 0);
471c63f63a2SAndrew Jeffery 	if (ctx->remaining < 0) {
472c63f63a2SAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
473c63f63a2SAndrew Jeffery 	}
474c63f63a2SAndrew Jeffery 
475c63f63a2SAndrew Jeffery 	memcpy(&ldst, ctx->cursor, sizeof(ldst));
476c63f63a2SAndrew Jeffery 	ldst = le32toh(ldst);
477*66c7723aSAndrew Jeffery 	memcpy(dst, &ldst, sizeof(ldst));
478*66c7723aSAndrew Jeffery 	ctx->cursor += sizeof(ldst);
479c63f63a2SAndrew Jeffery 
480c63f63a2SAndrew Jeffery 	return PLDM_SUCCESS;
481c63f63a2SAndrew Jeffery }
482c63f63a2SAndrew Jeffery 
483*66c7723aSAndrew Jeffery /**
484*66c7723aSAndrew Jeffery  * Extract the field at the msgbuf cursor into the lvalue named by dst.
485*66c7723aSAndrew Jeffery  *
486*66c7723aSAndrew Jeffery  * @param ctx The msgbuf context object
487*66c7723aSAndrew Jeffery  * @param dst The lvalue into which the field at the msgbuf cursor should be
488*66c7723aSAndrew Jeffery  *            extracted
489*66c7723aSAndrew Jeffery  *
490*66c7723aSAndrew Jeffery  * @return PLDM_SUCCESS on success, otherwise another value on error
491*66c7723aSAndrew Jeffery  */
492c63f63a2SAndrew Jeffery #define pldm_msgbuf_extract(ctx, dst)                                          \
493*66c7723aSAndrew Jeffery 	_Generic((dst),                                                        \
494*66c7723aSAndrew Jeffery 		uint8_t: pldm__msgbuf_extract_uint8,                           \
495*66c7723aSAndrew Jeffery 		int8_t: pldm__msgbuf_extract_int8,                             \
496*66c7723aSAndrew Jeffery 		uint16_t: pldm__msgbuf_extract_uint16,                         \
497*66c7723aSAndrew Jeffery 		int16_t: pldm__msgbuf_extract_int16,                           \
498*66c7723aSAndrew Jeffery 		uint32_t: pldm__msgbuf_extract_uint32,                         \
499*66c7723aSAndrew Jeffery 		int32_t: pldm__msgbuf_extract_int32,                           \
500*66c7723aSAndrew Jeffery 		real32_t: pldm__msgbuf_extract_real32)(ctx, (void *)&(dst))
501*66c7723aSAndrew Jeffery 
502*66c7723aSAndrew Jeffery /**
503*66c7723aSAndrew Jeffery  * Extract the field at the msgbuf cursor into the object pointed-to by dst.
504*66c7723aSAndrew Jeffery  *
505*66c7723aSAndrew Jeffery  * @param ctx The msgbuf context object
506*66c7723aSAndrew Jeffery  * @param dst The pointer to the object into which the field at the msgbuf
507*66c7723aSAndrew Jeffery  *            cursor should be extracted
508*66c7723aSAndrew Jeffery  *
509*66c7723aSAndrew Jeffery  * @return PLDM_SUCCESS on success, otherwise another value on error
510*66c7723aSAndrew Jeffery  */
511*66c7723aSAndrew Jeffery #define pldm_msgbuf_extract_p(ctx, dst)                                        \
512*66c7723aSAndrew Jeffery 	_Generic((dst),                                                        \
513*66c7723aSAndrew Jeffery 		uint8_t *: pldm__msgbuf_extract_uint8,                         \
514*66c7723aSAndrew Jeffery 		int8_t *: pldm__msgbuf_extract_int8,                           \
515*66c7723aSAndrew Jeffery 		uint16_t *: pldm__msgbuf_extract_uint16,                       \
516*66c7723aSAndrew Jeffery 		int16_t *: pldm__msgbuf_extract_int16,                         \
517*66c7723aSAndrew Jeffery 		uint32_t *: pldm__msgbuf_extract_uint32,                       \
518*66c7723aSAndrew Jeffery 		int32_t *: pldm__msgbuf_extract_int32,                         \
519*66c7723aSAndrew Jeffery 		real32_t *: pldm__msgbuf_extract_real32)(ctx, dst)
520c63f63a2SAndrew Jeffery 
521369b121aSAndrew Jeffery static inline int pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx,
522369b121aSAndrew Jeffery 						  uint8_t *dst, size_t count)
523369b121aSAndrew Jeffery {
524369b121aSAndrew Jeffery 	if (!ctx || !ctx->cursor || !dst) {
525369b121aSAndrew Jeffery 		return PLDM_ERROR_INVALID_DATA;
526369b121aSAndrew Jeffery 	}
527369b121aSAndrew Jeffery 
528369b121aSAndrew Jeffery 	if (!count) {
529369b121aSAndrew Jeffery 		return PLDM_SUCCESS;
530369b121aSAndrew Jeffery 	}
531369b121aSAndrew Jeffery 
532a065eccbSAndrew Jeffery 	if (count >= SSIZE_MAX) {
533369b121aSAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
534369b121aSAndrew Jeffery 	}
535369b121aSAndrew Jeffery 
536a065eccbSAndrew Jeffery 	ctx->remaining -= (ssize_t)count;
537369b121aSAndrew Jeffery 	assert(ctx->remaining >= 0);
538369b121aSAndrew Jeffery 	if (ctx->remaining < 0) {
539369b121aSAndrew Jeffery 		return PLDM_ERROR_INVALID_LENGTH;
540369b121aSAndrew Jeffery 	}
541369b121aSAndrew Jeffery 
542a065eccbSAndrew Jeffery 	memcpy(dst, ctx->cursor, count);
543a065eccbSAndrew Jeffery 	ctx->cursor += count;
544369b121aSAndrew Jeffery 
545369b121aSAndrew Jeffery 	return PLDM_SUCCESS;
546369b121aSAndrew Jeffery }
547369b121aSAndrew Jeffery 
548369b121aSAndrew Jeffery #define pldm_msgbuf_extract_array(ctx, dst, count)                             \
54937dd6a3dSAndrew Jeffery 	_Generic((*(dst)), uint8_t: pldm_msgbuf_extract_array_uint8)(ctx, dst, \
55037dd6a3dSAndrew Jeffery 								     count)
551369b121aSAndrew Jeffery 
552062c8762SThu Nguyen static inline int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx,
553062c8762SThu Nguyen 					    const uint32_t src)
554062c8762SThu Nguyen {
555062c8762SThu Nguyen 	uint32_t val = htole32(src);
556062c8762SThu Nguyen 
557062c8762SThu Nguyen 	if (!ctx || !ctx->cursor) {
558062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_DATA;
559062c8762SThu Nguyen 	}
560062c8762SThu Nguyen 
561062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
562062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
563062c8762SThu Nguyen 	if (ctx->remaining < 0) {
564062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
565062c8762SThu Nguyen 	}
566062c8762SThu Nguyen 
567062c8762SThu Nguyen 	memcpy(ctx->cursor, &val, sizeof(val));
568062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
569062c8762SThu Nguyen 
570062c8762SThu Nguyen 	return PLDM_SUCCESS;
571062c8762SThu Nguyen }
572062c8762SThu Nguyen 
573062c8762SThu Nguyen static inline int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx,
574062c8762SThu Nguyen 					    const uint16_t src)
575062c8762SThu Nguyen {
576062c8762SThu Nguyen 	uint16_t val = htole16(src);
577062c8762SThu Nguyen 
578062c8762SThu Nguyen 	if (!ctx || !ctx->cursor) {
579062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_DATA;
580062c8762SThu Nguyen 	}
581062c8762SThu Nguyen 
582062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
583062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
584062c8762SThu Nguyen 	if (ctx->remaining < 0) {
585062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
586062c8762SThu Nguyen 	}
587062c8762SThu Nguyen 
588062c8762SThu Nguyen 	memcpy(ctx->cursor, &val, sizeof(val));
589062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
590062c8762SThu Nguyen 
591062c8762SThu Nguyen 	return PLDM_SUCCESS;
592062c8762SThu Nguyen }
593062c8762SThu Nguyen 
594062c8762SThu Nguyen static inline int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx,
595062c8762SThu Nguyen 					   const uint8_t src)
596062c8762SThu Nguyen {
597062c8762SThu Nguyen 	if (!ctx || !ctx->cursor) {
598062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_DATA;
599062c8762SThu Nguyen 	}
600062c8762SThu Nguyen 
601062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
602062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
603062c8762SThu Nguyen 	if (ctx->remaining < 0) {
604062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
605062c8762SThu Nguyen 	}
606062c8762SThu Nguyen 
607062c8762SThu Nguyen 	memcpy(ctx->cursor, &src, sizeof(src));
608062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
609062c8762SThu Nguyen 
610062c8762SThu Nguyen 	return PLDM_SUCCESS;
611062c8762SThu Nguyen }
612062c8762SThu Nguyen 
613062c8762SThu Nguyen static inline int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx,
614062c8762SThu Nguyen 					   const int32_t src)
615062c8762SThu Nguyen {
616062c8762SThu Nguyen 	int32_t val = htole32(src);
617062c8762SThu Nguyen 
618062c8762SThu Nguyen 	if (!ctx || !ctx->cursor) {
619062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_DATA;
620062c8762SThu Nguyen 	}
621062c8762SThu Nguyen 
622062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
623062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
624062c8762SThu Nguyen 	if (ctx->remaining < 0) {
625062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
626062c8762SThu Nguyen 	}
627062c8762SThu Nguyen 
628062c8762SThu Nguyen 	memcpy(ctx->cursor, &val, sizeof(val));
629062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
630062c8762SThu Nguyen 
631062c8762SThu Nguyen 	return PLDM_SUCCESS;
632062c8762SThu Nguyen }
633062c8762SThu Nguyen 
634062c8762SThu Nguyen static inline int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx,
635062c8762SThu Nguyen 					   const int16_t src)
636062c8762SThu Nguyen {
637062c8762SThu Nguyen 	int16_t val = htole16(src);
638062c8762SThu Nguyen 
639062c8762SThu Nguyen 	if (!ctx || !ctx->cursor) {
640062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_DATA;
641062c8762SThu Nguyen 	}
642062c8762SThu Nguyen 
643062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
644062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
645062c8762SThu Nguyen 	if (ctx->remaining < 0) {
646062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
647062c8762SThu Nguyen 	}
648062c8762SThu Nguyen 
649062c8762SThu Nguyen 	memcpy(ctx->cursor, &val, sizeof(val));
650062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
651062c8762SThu Nguyen 
652062c8762SThu Nguyen 	return PLDM_SUCCESS;
653062c8762SThu Nguyen }
654062c8762SThu Nguyen 
655062c8762SThu Nguyen static inline int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx,
656062c8762SThu Nguyen 					  const int8_t src)
657062c8762SThu Nguyen {
658062c8762SThu Nguyen 	if (!ctx || !ctx->cursor) {
659062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_DATA;
660062c8762SThu Nguyen 	}
661062c8762SThu Nguyen 
662062c8762SThu Nguyen 	ctx->remaining -= sizeof(src);
663062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
664062c8762SThu Nguyen 	if (ctx->remaining < 0) {
665062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
666062c8762SThu Nguyen 	}
667062c8762SThu Nguyen 
668062c8762SThu Nguyen 	memcpy(ctx->cursor, &src, sizeof(src));
669062c8762SThu Nguyen 	ctx->cursor += sizeof(src);
670062c8762SThu Nguyen 
671062c8762SThu Nguyen 	return PLDM_SUCCESS;
672062c8762SThu Nguyen }
673062c8762SThu Nguyen 
674062c8762SThu Nguyen #define pldm_msgbuf_insert(dst, src)                                           \
67537dd6a3dSAndrew Jeffery 	_Generic((src),                                                        \
67637dd6a3dSAndrew Jeffery 		uint8_t: pldm_msgbuf_insert_uint8,                             \
67737dd6a3dSAndrew Jeffery 		int8_t: pldm_msgbuf_insert_int8,                               \
67837dd6a3dSAndrew Jeffery 		uint16_t: pldm_msgbuf_insert_uint16,                           \
67937dd6a3dSAndrew Jeffery 		int16_t: pldm_msgbuf_insert_int16,                             \
68037dd6a3dSAndrew Jeffery 		uint32_t: pldm_msgbuf_insert_uint32,                           \
68137dd6a3dSAndrew Jeffery 		int32_t: pldm_msgbuf_insert_int32)(dst, src)
682062c8762SThu Nguyen 
683062c8762SThu Nguyen static inline int pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx,
684062c8762SThu Nguyen 						 const uint8_t *src,
685062c8762SThu Nguyen 						 size_t count)
686062c8762SThu Nguyen {
687062c8762SThu Nguyen 	if (!ctx || !ctx->cursor || !src) {
688062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_DATA;
689062c8762SThu Nguyen 	}
690062c8762SThu Nguyen 
691062c8762SThu Nguyen 	if (!count) {
692062c8762SThu Nguyen 		return PLDM_SUCCESS;
693062c8762SThu Nguyen 	}
694062c8762SThu Nguyen 
695a065eccbSAndrew Jeffery 	if (count >= SSIZE_MAX) {
696062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
697062c8762SThu Nguyen 	}
698062c8762SThu Nguyen 
699a065eccbSAndrew Jeffery 	ctx->remaining -= (ssize_t)count;
700062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
701062c8762SThu Nguyen 	if (ctx->remaining < 0) {
702062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
703062c8762SThu Nguyen 	}
704062c8762SThu Nguyen 
705a065eccbSAndrew Jeffery 	memcpy(ctx->cursor, src, count);
706a065eccbSAndrew Jeffery 	ctx->cursor += count;
707062c8762SThu Nguyen 
708062c8762SThu Nguyen 	return PLDM_SUCCESS;
709062c8762SThu Nguyen }
710062c8762SThu Nguyen 
711062c8762SThu Nguyen #define pldm_msgbuf_insert_array(dst, src, count)                              \
71237dd6a3dSAndrew Jeffery 	_Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src,  \
71337dd6a3dSAndrew Jeffery 								    count)
714062c8762SThu Nguyen 
715062c8762SThu Nguyen static inline int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx,
716062c8762SThu Nguyen 					    size_t required, void **cursor)
717062c8762SThu Nguyen {
718062c8762SThu Nguyen 	if (!ctx || !ctx->cursor || !cursor || *cursor) {
719062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_DATA;
720062c8762SThu Nguyen 	}
721062c8762SThu Nguyen 
722062c8762SThu Nguyen 	if (required > SSIZE_MAX) {
723062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
724062c8762SThu Nguyen 	}
725062c8762SThu Nguyen 
726062c8762SThu Nguyen 	ctx->remaining -= (ssize_t)required;
727062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
728062c8762SThu Nguyen 	if (ctx->remaining < 0) {
729062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
730062c8762SThu Nguyen 	}
731062c8762SThu Nguyen 
732062c8762SThu Nguyen 	*cursor = ctx->cursor;
733062c8762SThu Nguyen 	ctx->cursor += required;
734062c8762SThu Nguyen 
735062c8762SThu Nguyen 	return PLDM_SUCCESS;
736062c8762SThu Nguyen }
737062c8762SThu Nguyen 
738062c8762SThu Nguyen static inline int pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx,
739062c8762SThu Nguyen 					     void **cursor, size_t *len)
740062c8762SThu Nguyen {
741062c8762SThu Nguyen 	if (!ctx || !ctx->cursor || !cursor || *cursor || !len) {
742062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_DATA;
743062c8762SThu Nguyen 	}
744062c8762SThu Nguyen 
745062c8762SThu Nguyen 	assert(ctx->remaining >= 0);
746062c8762SThu Nguyen 	if (ctx->remaining < 0) {
747062c8762SThu Nguyen 		return PLDM_ERROR_INVALID_LENGTH;
748062c8762SThu Nguyen 	}
749062c8762SThu Nguyen 
750062c8762SThu Nguyen 	*cursor = ctx->cursor;
751062c8762SThu Nguyen 	ctx->cursor += ctx->remaining;
752062c8762SThu Nguyen 	*len = ctx->remaining;
753062c8762SThu Nguyen 	ctx->remaining = 0;
754062c8762SThu Nguyen 
755062c8762SThu Nguyen 	return PLDM_SUCCESS;
756062c8762SThu Nguyen }
757c63f63a2SAndrew Jeffery #ifdef __cplusplus
758c63f63a2SAndrew Jeffery }
759c63f63a2SAndrew Jeffery #endif
760c63f63a2SAndrew Jeffery 
761*66c7723aSAndrew Jeffery #ifdef __cplusplus
762*66c7723aSAndrew Jeffery #include <type_traits>
763*66c7723aSAndrew Jeffery 
764*66c7723aSAndrew Jeffery template <typename T>
765*66c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_uint8_t(struct pldm_msgbuf *ctx,
766*66c7723aSAndrew Jeffery 						void *buf)
767*66c7723aSAndrew Jeffery {
768*66c7723aSAndrew Jeffery 	static_assert(std::is_same<uint8_t *, T>::value);
769*66c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_uint8(ctx, buf);
770*66c7723aSAndrew Jeffery }
771*66c7723aSAndrew Jeffery 
772*66c7723aSAndrew Jeffery template <typename T>
773*66c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_int8_t(struct pldm_msgbuf *ctx,
774*66c7723aSAndrew Jeffery 					       void *buf)
775*66c7723aSAndrew Jeffery {
776*66c7723aSAndrew Jeffery 	static_assert(std::is_same<int8_t *, T>::value);
777*66c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_int8(ctx, buf);
778*66c7723aSAndrew Jeffery }
779*66c7723aSAndrew Jeffery 
780*66c7723aSAndrew Jeffery template <typename T>
781*66c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_uint16_t(struct pldm_msgbuf *ctx,
782*66c7723aSAndrew Jeffery 						 void *buf)
783*66c7723aSAndrew Jeffery {
784*66c7723aSAndrew Jeffery 	static_assert(std::is_same<uint16_t *, T>::value);
785*66c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_uint16(ctx, buf);
786*66c7723aSAndrew Jeffery }
787*66c7723aSAndrew Jeffery 
788*66c7723aSAndrew Jeffery template <typename T>
789*66c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_int16_t(struct pldm_msgbuf *ctx,
790*66c7723aSAndrew Jeffery 						void *buf)
791*66c7723aSAndrew Jeffery {
792*66c7723aSAndrew Jeffery 	static_assert(std::is_same<int16_t *, T>::value);
793*66c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_int16(ctx, buf);
794*66c7723aSAndrew Jeffery }
795*66c7723aSAndrew Jeffery 
796*66c7723aSAndrew Jeffery template <typename T>
797*66c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_uint32_t(struct pldm_msgbuf *ctx,
798*66c7723aSAndrew Jeffery 						 void *buf)
799*66c7723aSAndrew Jeffery {
800*66c7723aSAndrew Jeffery 	static_assert(std::is_same<uint32_t *, T>::value);
801*66c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_uint32(ctx, buf);
802*66c7723aSAndrew Jeffery }
803*66c7723aSAndrew Jeffery 
804*66c7723aSAndrew Jeffery template <typename T>
805*66c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_int32_t(struct pldm_msgbuf *ctx,
806*66c7723aSAndrew Jeffery 						void *buf)
807*66c7723aSAndrew Jeffery {
808*66c7723aSAndrew Jeffery 	static_assert(std::is_same<int32_t *, T>::value);
809*66c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_int32(ctx, buf);
810*66c7723aSAndrew Jeffery }
811*66c7723aSAndrew Jeffery 
812*66c7723aSAndrew Jeffery template <typename T>
813*66c7723aSAndrew Jeffery static inline int pldm_msgbuf_typecheck_real32_t(struct pldm_msgbuf *ctx,
814*66c7723aSAndrew Jeffery 						 void *buf)
815*66c7723aSAndrew Jeffery {
816*66c7723aSAndrew Jeffery 	static_assert(std::is_same<real32_t *, T>::value);
817*66c7723aSAndrew Jeffery 	return pldm__msgbuf_extract_real32(ctx, buf);
818*66c7723aSAndrew Jeffery }
819*66c7723aSAndrew Jeffery #endif
820*66c7723aSAndrew Jeffery 
821c63f63a2SAndrew Jeffery #endif /* BUF_H */
822