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