Commit | Line | Data |
---|---|---|
ccbc1c30 MP |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * KUnit test for the FPGA Manager | |
4 | * | |
5 | * Copyright (C) 2023 Red Hat, Inc. | |
6 | * | |
7 | * Author: Marco Pagani <marpagan@redhat.com> | |
8 | */ | |
9 | ||
4d2bc3f7 | 10 | #include <kunit/device.h> |
ccbc1c30 | 11 | #include <kunit/test.h> |
ccbc1c30 MP |
12 | #include <linux/fpga/fpga-mgr.h> |
13 | #include <linux/module.h> | |
14 | #include <linux/scatterlist.h> | |
15 | #include <linux/types.h> | |
16 | ||
17 | #define HEADER_FILL 'H' | |
18 | #define IMAGE_FILL 'P' | |
19 | #define IMAGE_BLOCK 1024 | |
20 | ||
21 | #define HEADER_SIZE IMAGE_BLOCK | |
22 | #define IMAGE_SIZE (IMAGE_BLOCK * 4) | |
23 | ||
24 | struct mgr_stats { | |
25 | bool header_match; | |
26 | bool image_match; | |
27 | u32 seq_num; | |
28 | u32 op_parse_header_seq; | |
29 | u32 op_write_init_seq; | |
30 | u32 op_write_seq; | |
31 | u32 op_write_sg_seq; | |
32 | u32 op_write_complete_seq; | |
33 | enum fpga_mgr_states op_parse_header_state; | |
34 | enum fpga_mgr_states op_write_init_state; | |
35 | enum fpga_mgr_states op_write_state; | |
36 | enum fpga_mgr_states op_write_sg_state; | |
37 | enum fpga_mgr_states op_write_complete_state; | |
38 | }; | |
39 | ||
40 | struct mgr_ctx { | |
41 | struct fpga_image_info *img_info; | |
42 | struct fpga_manager *mgr; | |
4d2bc3f7 | 43 | struct device *dev; |
ccbc1c30 MP |
44 | struct mgr_stats stats; |
45 | }; | |
46 | ||
47 | /** | |
48 | * init_test_buffer() - Allocate and initialize a test image in a buffer. | |
49 | * @test: KUnit test context object. | |
50 | * @count: image size in bytes. | |
51 | * | |
52 | * Return: pointer to the newly allocated image. | |
53 | */ | |
54 | static char *init_test_buffer(struct kunit *test, size_t count) | |
55 | { | |
56 | char *buf; | |
57 | ||
58 | KUNIT_ASSERT_GE(test, count, HEADER_SIZE); | |
59 | ||
60 | buf = kunit_kzalloc(test, count, GFP_KERNEL); | |
61 | KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); | |
62 | ||
63 | memset(buf, HEADER_FILL, HEADER_SIZE); | |
64 | memset(buf + HEADER_SIZE, IMAGE_FILL, count - HEADER_SIZE); | |
65 | ||
66 | return buf; | |
67 | } | |
68 | ||
69 | /* | |
70 | * Check the image header. Do not return an error code if the image check fails | |
71 | * since, in this case, it is a failure of the FPGA manager itself, not this | |
72 | * op that tests it. | |
73 | */ | |
74 | static int op_parse_header(struct fpga_manager *mgr, struct fpga_image_info *info, | |
75 | const char *buf, size_t count) | |
76 | { | |
77 | struct mgr_stats *stats = mgr->priv; | |
78 | size_t i; | |
79 | ||
80 | stats->op_parse_header_state = mgr->state; | |
81 | stats->op_parse_header_seq = stats->seq_num++; | |
82 | ||
83 | /* Set header_size and data_size for later */ | |
84 | info->header_size = HEADER_SIZE; | |
85 | info->data_size = info->count - HEADER_SIZE; | |
86 | ||
87 | stats->header_match = true; | |
88 | for (i = 0; i < info->header_size; i++) { | |
89 | if (buf[i] != HEADER_FILL) { | |
90 | stats->header_match = false; | |
91 | break; | |
92 | } | |
93 | } | |
94 | ||
95 | return 0; | |
96 | } | |
97 | ||
98 | static int op_write_init(struct fpga_manager *mgr, struct fpga_image_info *info, | |
99 | const char *buf, size_t count) | |
100 | { | |
101 | struct mgr_stats *stats = mgr->priv; | |
102 | ||
103 | stats->op_write_init_state = mgr->state; | |
104 | stats->op_write_init_seq = stats->seq_num++; | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
109 | /* | |
110 | * Check the image data. As with op_parse_header, do not return an error code | |
111 | * if the image check fails. | |
112 | */ | |
113 | static int op_write(struct fpga_manager *mgr, const char *buf, size_t count) | |
114 | { | |
115 | struct mgr_stats *stats = mgr->priv; | |
116 | size_t i; | |
117 | ||
118 | stats->op_write_state = mgr->state; | |
119 | stats->op_write_seq = stats->seq_num++; | |
120 | ||
121 | stats->image_match = true; | |
122 | for (i = 0; i < count; i++) { | |
123 | if (buf[i] != IMAGE_FILL) { | |
124 | stats->image_match = false; | |
125 | break; | |
126 | } | |
127 | } | |
128 | ||
129 | return 0; | |
130 | } | |
131 | ||
132 | /* | |
133 | * Check the image data, but first skip the header since write_sg will get | |
134 | * the whole image in sg_table. As with op_parse_header, do not return an | |
135 | * error code if the image check fails. | |
136 | */ | |
137 | static int op_write_sg(struct fpga_manager *mgr, struct sg_table *sgt) | |
138 | { | |
139 | struct mgr_stats *stats = mgr->priv; | |
140 | struct sg_mapping_iter miter; | |
141 | char *img; | |
142 | size_t i; | |
143 | ||
144 | stats->op_write_sg_state = mgr->state; | |
145 | stats->op_write_sg_seq = stats->seq_num++; | |
146 | ||
147 | stats->image_match = true; | |
148 | sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG); | |
149 | ||
150 | if (!sg_miter_skip(&miter, HEADER_SIZE)) { | |
151 | stats->image_match = false; | |
152 | goto out; | |
153 | } | |
154 | ||
155 | while (sg_miter_next(&miter)) { | |
156 | img = miter.addr; | |
157 | for (i = 0; i < miter.length; i++) { | |
158 | if (img[i] != IMAGE_FILL) { | |
159 | stats->image_match = false; | |
160 | goto out; | |
161 | } | |
162 | } | |
163 | } | |
164 | out: | |
165 | sg_miter_stop(&miter); | |
166 | return 0; | |
167 | } | |
168 | ||
169 | static int op_write_complete(struct fpga_manager *mgr, struct fpga_image_info *info) | |
170 | { | |
171 | struct mgr_stats *stats = mgr->priv; | |
172 | ||
173 | stats->op_write_complete_state = mgr->state; | |
174 | stats->op_write_complete_seq = stats->seq_num++; | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
179 | /* | |
180 | * Fake FPGA manager that implements all ops required to check the programming | |
181 | * sequence using a single contiguous buffer and a scatter gather table. | |
182 | */ | |
183 | static const struct fpga_manager_ops fake_mgr_ops = { | |
184 | .skip_header = true, | |
185 | .parse_header = op_parse_header, | |
186 | .write_init = op_write_init, | |
187 | .write = op_write, | |
188 | .write_sg = op_write_sg, | |
189 | .write_complete = op_write_complete, | |
190 | }; | |
191 | ||
192 | static void fpga_mgr_test_get(struct kunit *test) | |
193 | { | |
194 | struct mgr_ctx *ctx = test->priv; | |
195 | struct fpga_manager *mgr; | |
196 | ||
4d2bc3f7 | 197 | mgr = fpga_mgr_get(ctx->dev); |
ccbc1c30 MP |
198 | KUNIT_EXPECT_PTR_EQ(test, mgr, ctx->mgr); |
199 | ||
200 | fpga_mgr_put(ctx->mgr); | |
201 | } | |
202 | ||
203 | static void fpga_mgr_test_lock(struct kunit *test) | |
204 | { | |
205 | struct mgr_ctx *ctx = test->priv; | |
206 | int ret; | |
207 | ||
208 | ret = fpga_mgr_lock(ctx->mgr); | |
209 | KUNIT_EXPECT_EQ(test, ret, 0); | |
210 | ||
211 | ret = fpga_mgr_lock(ctx->mgr); | |
212 | KUNIT_EXPECT_EQ(test, ret, -EBUSY); | |
213 | ||
214 | fpga_mgr_unlock(ctx->mgr); | |
215 | } | |
216 | ||
217 | /* Check the programming sequence using an image in a buffer */ | |
218 | static void fpga_mgr_test_img_load_buf(struct kunit *test) | |
219 | { | |
220 | struct mgr_ctx *ctx = test->priv; | |
221 | char *img_buf; | |
222 | int ret; | |
223 | ||
224 | img_buf = init_test_buffer(test, IMAGE_SIZE); | |
225 | ||
226 | ctx->img_info->count = IMAGE_SIZE; | |
227 | ctx->img_info->buf = img_buf; | |
228 | ||
229 | ret = fpga_mgr_load(ctx->mgr, ctx->img_info); | |
230 | KUNIT_EXPECT_EQ(test, ret, 0); | |
231 | ||
232 | KUNIT_EXPECT_TRUE(test, ctx->stats.header_match); | |
233 | KUNIT_EXPECT_TRUE(test, ctx->stats.image_match); | |
234 | ||
235 | KUNIT_EXPECT_EQ(test, ctx->stats.op_parse_header_state, FPGA_MGR_STATE_PARSE_HEADER); | |
236 | KUNIT_EXPECT_EQ(test, ctx->stats.op_write_init_state, FPGA_MGR_STATE_WRITE_INIT); | |
237 | KUNIT_EXPECT_EQ(test, ctx->stats.op_write_state, FPGA_MGR_STATE_WRITE); | |
238 | KUNIT_EXPECT_EQ(test, ctx->stats.op_write_complete_state, FPGA_MGR_STATE_WRITE_COMPLETE); | |
239 | ||
240 | KUNIT_EXPECT_EQ(test, ctx->stats.op_write_init_seq, ctx->stats.op_parse_header_seq + 1); | |
241 | KUNIT_EXPECT_EQ(test, ctx->stats.op_write_seq, ctx->stats.op_parse_header_seq + 2); | |
242 | KUNIT_EXPECT_EQ(test, ctx->stats.op_write_complete_seq, ctx->stats.op_parse_header_seq + 3); | |
243 | } | |
244 | ||
245 | /* Check the programming sequence using an image in a scatter gather table */ | |
246 | static void fpga_mgr_test_img_load_sgt(struct kunit *test) | |
247 | { | |
248 | struct mgr_ctx *ctx = test->priv; | |
249 | struct sg_table *sgt; | |
250 | char *img_buf; | |
251 | int ret; | |
252 | ||
253 | img_buf = init_test_buffer(test, IMAGE_SIZE); | |
254 | ||
255 | sgt = kunit_kzalloc(test, sizeof(*sgt), GFP_KERNEL); | |
256 | ret = sg_alloc_table(sgt, 1, GFP_KERNEL); | |
257 | KUNIT_ASSERT_EQ(test, ret, 0); | |
258 | sg_init_one(sgt->sgl, img_buf, IMAGE_SIZE); | |
259 | ||
260 | ctx->img_info->sgt = sgt; | |
261 | ||
262 | ret = fpga_mgr_load(ctx->mgr, ctx->img_info); | |
263 | KUNIT_EXPECT_EQ(test, ret, 0); | |
264 | ||
265 | KUNIT_EXPECT_TRUE(test, ctx->stats.header_match); | |
266 | KUNIT_EXPECT_TRUE(test, ctx->stats.image_match); | |
267 | ||
268 | KUNIT_EXPECT_EQ(test, ctx->stats.op_parse_header_state, FPGA_MGR_STATE_PARSE_HEADER); | |
269 | KUNIT_EXPECT_EQ(test, ctx->stats.op_write_init_state, FPGA_MGR_STATE_WRITE_INIT); | |
270 | KUNIT_EXPECT_EQ(test, ctx->stats.op_write_sg_state, FPGA_MGR_STATE_WRITE); | |
271 | KUNIT_EXPECT_EQ(test, ctx->stats.op_write_complete_state, FPGA_MGR_STATE_WRITE_COMPLETE); | |
272 | ||
273 | KUNIT_EXPECT_EQ(test, ctx->stats.op_write_init_seq, ctx->stats.op_parse_header_seq + 1); | |
274 | KUNIT_EXPECT_EQ(test, ctx->stats.op_write_sg_seq, ctx->stats.op_parse_header_seq + 2); | |
275 | KUNIT_EXPECT_EQ(test, ctx->stats.op_write_complete_seq, ctx->stats.op_parse_header_seq + 3); | |
276 | ||
277 | sg_free_table(ctx->img_info->sgt); | |
278 | } | |
279 | ||
280 | static int fpga_mgr_test_init(struct kunit *test) | |
281 | { | |
282 | struct mgr_ctx *ctx; | |
283 | ||
284 | ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); | |
285 | KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); | |
286 | ||
4d2bc3f7 MP |
287 | ctx->dev = kunit_device_register(test, "fpga-manager-test-dev"); |
288 | KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->dev); | |
ccbc1c30 | 289 | |
4d2bc3f7 | 290 | ctx->mgr = devm_fpga_mgr_register(ctx->dev, "Fake FPGA Manager", &fake_mgr_ops, |
ccbc1c30 MP |
291 | &ctx->stats); |
292 | KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->mgr)); | |
293 | ||
4d2bc3f7 | 294 | ctx->img_info = fpga_image_info_alloc(ctx->dev); |
ccbc1c30 MP |
295 | KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->img_info); |
296 | ||
297 | test->priv = ctx; | |
298 | ||
299 | return 0; | |
300 | } | |
301 | ||
302 | static void fpga_mgr_test_exit(struct kunit *test) | |
303 | { | |
304 | struct mgr_ctx *ctx = test->priv; | |
305 | ||
306 | fpga_image_info_free(ctx->img_info); | |
4d2bc3f7 | 307 | kunit_device_unregister(test, ctx->dev); |
ccbc1c30 MP |
308 | } |
309 | ||
310 | static struct kunit_case fpga_mgr_test_cases[] = { | |
311 | KUNIT_CASE(fpga_mgr_test_get), | |
312 | KUNIT_CASE(fpga_mgr_test_lock), | |
313 | KUNIT_CASE(fpga_mgr_test_img_load_buf), | |
314 | KUNIT_CASE(fpga_mgr_test_img_load_sgt), | |
315 | {} | |
316 | }; | |
317 | ||
318 | static struct kunit_suite fpga_mgr_suite = { | |
319 | .name = "fpga_mgr", | |
320 | .init = fpga_mgr_test_init, | |
321 | .exit = fpga_mgr_test_exit, | |
322 | .test_cases = fpga_mgr_test_cases, | |
323 | }; | |
324 | ||
325 | kunit_test_suite(fpga_mgr_suite); | |
326 | ||
327 | MODULE_LICENSE("GPL"); |