xref: /openbmc/libpldm/src/transport/test.c (revision 2b440d4c)
1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2 /* NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) */
3 #include "array.h"
4 #include "container-of.h"
5 #include "transport.h"
6 #include "test.h"
7 
8 #include <errno.h>
9 #include <poll.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 
14 struct pldm_transport_test {
15 	struct pldm_transport transport;
16 	const struct pldm_transport_test_descriptor *seq;
17 	size_t count;
18 	size_t cursor;
19 	int timerfd;
20 };
21 
22 #define transport_to_test(ptr)                                                 \
23 	container_of(ptr, struct pldm_transport_test, transport)
24 
25 LIBPLDM_ABI_TESTING
pldm_transport_test_core(struct pldm_transport_test * ctx)26 struct pldm_transport *pldm_transport_test_core(struct pldm_transport_test *ctx)
27 {
28 	return &ctx->transport;
29 }
30 
31 #ifdef PLDM_HAS_POLL
32 #include <poll.h>
33 LIBPLDM_ABI_TESTING
pldm_transport_test_init_pollfd(struct pldm_transport * ctx,struct pollfd * pollfd)34 int pldm_transport_test_init_pollfd(struct pldm_transport *ctx,
35 				    struct pollfd *pollfd)
36 {
37 	static const struct itimerspec disable = {
38 		.it_value = { 0, 0 },
39 		.it_interval = { 0, 0 },
40 	};
41 	struct pldm_transport_test *test = transport_to_test(ctx);
42 	const struct pldm_transport_test_descriptor *desc;
43 	int rc;
44 
45 	rc = timerfd_settime(test->timerfd, 0, &disable, NULL);
46 	if (rc < 0) {
47 		return PLDM_REQUESTER_POLL_FAIL;
48 	}
49 
50 	if (test->cursor >= test->count) {
51 		return PLDM_REQUESTER_POLL_FAIL;
52 	}
53 
54 	desc = &test->seq[test->cursor];
55 
56 	if (desc->type == PLDM_TRANSPORT_TEST_ELEMENT_LATENCY) {
57 		rc = timerfd_settime(test->timerfd, 0, &desc->latency, NULL);
58 		if (rc < 0) {
59 			return PLDM_REQUESTER_POLL_FAIL;
60 		}
61 
62 		/* This was an explicit latency element, so now move beyond it for recv */
63 		test->cursor++;
64 	} else if (desc->type == PLDM_TRANSPORT_TEST_ELEMENT_MSG_RECV) {
65 		/* Expire the timer immediately so it appears ready */
66 		static const struct timespec ensure_ready = {
67 			.tv_sec = 0,
68 			.tv_nsec = 2,
69 		};
70 		static const struct itimerspec ready = {
71 			.it_value = { 0, 1 },
72 			.it_interval = { 0, 0 },
73 		};
74 		struct pollfd pfds[] = {
75 			{ .fd = test->timerfd, .events = POLLIN },
76 		};
77 
78 		rc = timerfd_settime(test->timerfd, 0, &ready, NULL);
79 		if (rc < 0) {
80 			return PLDM_REQUESTER_POLL_FAIL;
81 		}
82 
83 		rc = ppoll(pfds, ARRAY_SIZE(pfds), &ensure_ready, NULL);
84 		if (rc < 1) {
85 			return PLDM_REQUESTER_POLL_FAIL;
86 		}
87 
88 		/* Don't increment test->cursor as recv needs to consume the current test element */
89 	} else {
90 		return PLDM_REQUESTER_POLL_FAIL;
91 	}
92 
93 	pollfd->fd = test->timerfd;
94 	pollfd->events = POLLIN;
95 
96 	return 0;
97 }
98 #endif
99 
pldm_transport_test_recv(struct pldm_transport * ctx,pldm_tid_t * tid,void ** pldm_resp_msg,size_t * resp_msg_len)100 static pldm_requester_rc_t pldm_transport_test_recv(struct pldm_transport *ctx,
101 						    pldm_tid_t *tid,
102 						    void **pldm_resp_msg,
103 						    size_t *resp_msg_len)
104 {
105 	struct pldm_transport_test *test = transport_to_test(ctx);
106 	const struct pldm_transport_test_descriptor *desc;
107 	void *msg;
108 
109 	if (test->cursor >= test->count) {
110 		return PLDM_REQUESTER_RECV_FAIL;
111 	}
112 
113 	desc = &test->seq[test->cursor];
114 
115 	if (desc->type != PLDM_TRANSPORT_TEST_ELEMENT_MSG_RECV) {
116 		return PLDM_REQUESTER_RECV_FAIL;
117 	}
118 
119 	msg = malloc(desc->recv_msg.len);
120 	if (!msg) {
121 		return PLDM_REQUESTER_RECV_FAIL;
122 	}
123 
124 	memcpy(msg, desc->recv_msg.msg, desc->recv_msg.len);
125 	*pldm_resp_msg = msg;
126 	*resp_msg_len = desc->recv_msg.len;
127 	*tid = desc->recv_msg.src;
128 
129 	test->cursor++;
130 
131 	return PLDM_REQUESTER_SUCCESS;
132 }
133 
pldm_transport_test_send(struct pldm_transport * ctx,pldm_tid_t tid,const void * pldm_req_msg,size_t req_msg_len)134 static pldm_requester_rc_t pldm_transport_test_send(struct pldm_transport *ctx,
135 						    pldm_tid_t tid,
136 						    const void *pldm_req_msg,
137 						    size_t req_msg_len)
138 {
139 	struct pldm_transport_test *test = transport_to_test(ctx);
140 	const struct pldm_transport_test_descriptor *desc;
141 
142 	if (test->cursor > test->count) {
143 		return PLDM_REQUESTER_SEND_FAIL;
144 	}
145 
146 	desc = &test->seq[test->cursor];
147 
148 	if (desc->type != PLDM_TRANSPORT_TEST_ELEMENT_MSG_SEND) {
149 		return PLDM_REQUESTER_SEND_FAIL;
150 	}
151 
152 	if (desc->send_msg.dst != tid) {
153 		return PLDM_REQUESTER_SEND_FAIL;
154 	}
155 
156 	if (desc->send_msg.len != req_msg_len) {
157 		return PLDM_REQUESTER_SEND_FAIL;
158 	}
159 
160 	if (memcmp(desc->send_msg.msg, pldm_req_msg, req_msg_len) != 0) {
161 		return PLDM_REQUESTER_SEND_FAIL;
162 	}
163 
164 	test->cursor++;
165 
166 	return PLDM_REQUESTER_SUCCESS;
167 }
168 
169 LIBPLDM_ABI_TESTING
pldm_transport_test_init(struct pldm_transport_test ** ctx,const struct pldm_transport_test_descriptor * seq,size_t count)170 int pldm_transport_test_init(struct pldm_transport_test **ctx,
171 			     const struct pldm_transport_test_descriptor *seq,
172 			     size_t count)
173 {
174 	int rc;
175 
176 	if (!ctx || *ctx) {
177 		return -EINVAL;
178 	}
179 
180 	struct pldm_transport_test *test = malloc(sizeof(*test));
181 	if (!test) {
182 		return -ENOMEM;
183 	}
184 
185 	test->transport.name = "TEST";
186 	test->transport.version = 1;
187 	test->transport.recv = pldm_transport_test_recv;
188 	test->transport.send = pldm_transport_test_send;
189 	test->transport.init_pollfd = pldm_transport_test_init_pollfd;
190 	test->seq = seq;
191 	test->count = count;
192 	test->cursor = 0;
193 	test->timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
194 	if (test->timerfd < 0) {
195 		rc = -errno;
196 		goto cleanup_test;
197 	}
198 
199 	*ctx = test;
200 
201 	return 0;
202 
203 cleanup_test:
204 	free(test);
205 	return rc;
206 }
207 
208 LIBPLDM_ABI_TESTING
pldm_transport_test_destroy(struct pldm_transport_test * ctx)209 void pldm_transport_test_destroy(struct pldm_transport_test *ctx)
210 {
211 	close(ctx->timerfd);
212 	free(ctx);
213 }
214