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