162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * KUnit test for the FPGA Manager 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2023 Red Hat, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Marco Pagani <marpagan@redhat.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <kunit/test.h> 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/fpga/fpga-mgr.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/scatterlist.h> 1562306a36Sopenharmony_ci#include <linux/types.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define HEADER_FILL 'H' 1862306a36Sopenharmony_ci#define IMAGE_FILL 'P' 1962306a36Sopenharmony_ci#define IMAGE_BLOCK 1024 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define HEADER_SIZE IMAGE_BLOCK 2262306a36Sopenharmony_ci#define IMAGE_SIZE (IMAGE_BLOCK * 4) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct mgr_stats { 2562306a36Sopenharmony_ci bool header_match; 2662306a36Sopenharmony_ci bool image_match; 2762306a36Sopenharmony_ci u32 seq_num; 2862306a36Sopenharmony_ci u32 op_parse_header_seq; 2962306a36Sopenharmony_ci u32 op_write_init_seq; 3062306a36Sopenharmony_ci u32 op_write_seq; 3162306a36Sopenharmony_ci u32 op_write_sg_seq; 3262306a36Sopenharmony_ci u32 op_write_complete_seq; 3362306a36Sopenharmony_ci enum fpga_mgr_states op_parse_header_state; 3462306a36Sopenharmony_ci enum fpga_mgr_states op_write_init_state; 3562306a36Sopenharmony_ci enum fpga_mgr_states op_write_state; 3662306a36Sopenharmony_ci enum fpga_mgr_states op_write_sg_state; 3762306a36Sopenharmony_ci enum fpga_mgr_states op_write_complete_state; 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct mgr_ctx { 4162306a36Sopenharmony_ci struct fpga_image_info *img_info; 4262306a36Sopenharmony_ci struct fpga_manager *mgr; 4362306a36Sopenharmony_ci struct platform_device *pdev; 4462306a36Sopenharmony_ci struct mgr_stats stats; 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/** 4862306a36Sopenharmony_ci * init_test_buffer() - Allocate and initialize a test image in a buffer. 4962306a36Sopenharmony_ci * @test: KUnit test context object. 5062306a36Sopenharmony_ci * @count: image size in bytes. 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * Return: pointer to the newly allocated image. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_cistatic char *init_test_buffer(struct kunit *test, size_t count) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci char *buf; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci KUNIT_ASSERT_GE(test, count, HEADER_SIZE); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci buf = kunit_kzalloc(test, count, GFP_KERNEL); 6162306a36Sopenharmony_ci KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci memset(buf, HEADER_FILL, HEADER_SIZE); 6462306a36Sopenharmony_ci memset(buf + HEADER_SIZE, IMAGE_FILL, count - HEADER_SIZE); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return buf; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * Check the image header. Do not return an error code if the image check fails 7162306a36Sopenharmony_ci * since, in this case, it is a failure of the FPGA manager itself, not this 7262306a36Sopenharmony_ci * op that tests it. 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_cistatic int op_parse_header(struct fpga_manager *mgr, struct fpga_image_info *info, 7562306a36Sopenharmony_ci const char *buf, size_t count) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct mgr_stats *stats = mgr->priv; 7862306a36Sopenharmony_ci size_t i; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci stats->op_parse_header_state = mgr->state; 8162306a36Sopenharmony_ci stats->op_parse_header_seq = stats->seq_num++; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* Set header_size and data_size for later */ 8462306a36Sopenharmony_ci info->header_size = HEADER_SIZE; 8562306a36Sopenharmony_ci info->data_size = info->count - HEADER_SIZE; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci stats->header_match = true; 8862306a36Sopenharmony_ci for (i = 0; i < info->header_size; i++) { 8962306a36Sopenharmony_ci if (buf[i] != HEADER_FILL) { 9062306a36Sopenharmony_ci stats->header_match = false; 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int op_write_init(struct fpga_manager *mgr, struct fpga_image_info *info, 9962306a36Sopenharmony_ci const char *buf, size_t count) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct mgr_stats *stats = mgr->priv; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci stats->op_write_init_state = mgr->state; 10462306a36Sopenharmony_ci stats->op_write_init_seq = stats->seq_num++; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* 11062306a36Sopenharmony_ci * Check the image data. As with op_parse_header, do not return an error code 11162306a36Sopenharmony_ci * if the image check fails. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_cistatic int op_write(struct fpga_manager *mgr, const char *buf, size_t count) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct mgr_stats *stats = mgr->priv; 11662306a36Sopenharmony_ci size_t i; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci stats->op_write_state = mgr->state; 11962306a36Sopenharmony_ci stats->op_write_seq = stats->seq_num++; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci stats->image_match = true; 12262306a36Sopenharmony_ci for (i = 0; i < count; i++) { 12362306a36Sopenharmony_ci if (buf[i] != IMAGE_FILL) { 12462306a36Sopenharmony_ci stats->image_match = false; 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* 13362306a36Sopenharmony_ci * Check the image data, but first skip the header since write_sg will get 13462306a36Sopenharmony_ci * the whole image in sg_table. As with op_parse_header, do not return an 13562306a36Sopenharmony_ci * error code if the image check fails. 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_cistatic int op_write_sg(struct fpga_manager *mgr, struct sg_table *sgt) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct mgr_stats *stats = mgr->priv; 14062306a36Sopenharmony_ci struct sg_mapping_iter miter; 14162306a36Sopenharmony_ci char *img; 14262306a36Sopenharmony_ci size_t i; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci stats->op_write_sg_state = mgr->state; 14562306a36Sopenharmony_ci stats->op_write_sg_seq = stats->seq_num++; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci stats->image_match = true; 14862306a36Sopenharmony_ci sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (!sg_miter_skip(&miter, HEADER_SIZE)) { 15162306a36Sopenharmony_ci stats->image_match = false; 15262306a36Sopenharmony_ci goto out; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci while (sg_miter_next(&miter)) { 15662306a36Sopenharmony_ci img = miter.addr; 15762306a36Sopenharmony_ci for (i = 0; i < miter.length; i++) { 15862306a36Sopenharmony_ci if (img[i] != IMAGE_FILL) { 15962306a36Sopenharmony_ci stats->image_match = false; 16062306a36Sopenharmony_ci goto out; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ciout: 16562306a36Sopenharmony_ci sg_miter_stop(&miter); 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int op_write_complete(struct fpga_manager *mgr, struct fpga_image_info *info) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct mgr_stats *stats = mgr->priv; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci stats->op_write_complete_state = mgr->state; 17462306a36Sopenharmony_ci stats->op_write_complete_seq = stats->seq_num++; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* 18062306a36Sopenharmony_ci * Fake FPGA manager that implements all ops required to check the programming 18162306a36Sopenharmony_ci * sequence using a single contiguous buffer and a scatter gather table. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_cistatic const struct fpga_manager_ops fake_mgr_ops = { 18462306a36Sopenharmony_ci .skip_header = true, 18562306a36Sopenharmony_ci .parse_header = op_parse_header, 18662306a36Sopenharmony_ci .write_init = op_write_init, 18762306a36Sopenharmony_ci .write = op_write, 18862306a36Sopenharmony_ci .write_sg = op_write_sg, 18962306a36Sopenharmony_ci .write_complete = op_write_complete, 19062306a36Sopenharmony_ci}; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void fpga_mgr_test_get(struct kunit *test) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct mgr_ctx *ctx = test->priv; 19562306a36Sopenharmony_ci struct fpga_manager *mgr; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci mgr = fpga_mgr_get(&ctx->pdev->dev); 19862306a36Sopenharmony_ci KUNIT_EXPECT_PTR_EQ(test, mgr, ctx->mgr); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci fpga_mgr_put(ctx->mgr); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic void fpga_mgr_test_lock(struct kunit *test) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct mgr_ctx *ctx = test->priv; 20662306a36Sopenharmony_ci int ret; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci ret = fpga_mgr_lock(ctx->mgr); 20962306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, ret, 0); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci ret = fpga_mgr_lock(ctx->mgr); 21262306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, ret, -EBUSY); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci fpga_mgr_unlock(ctx->mgr); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/* Check the programming sequence using an image in a buffer */ 21862306a36Sopenharmony_cistatic void fpga_mgr_test_img_load_buf(struct kunit *test) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct mgr_ctx *ctx = test->priv; 22162306a36Sopenharmony_ci char *img_buf; 22262306a36Sopenharmony_ci int ret; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci img_buf = init_test_buffer(test, IMAGE_SIZE); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci ctx->img_info->count = IMAGE_SIZE; 22762306a36Sopenharmony_ci ctx->img_info->buf = img_buf; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci ret = fpga_mgr_load(ctx->mgr, ctx->img_info); 23062306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, ret, 0); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, ctx->stats.header_match); 23362306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, ctx->stats.image_match); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, ctx->stats.op_parse_header_state, FPGA_MGR_STATE_PARSE_HEADER); 23662306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, ctx->stats.op_write_init_state, FPGA_MGR_STATE_WRITE_INIT); 23762306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, ctx->stats.op_write_state, FPGA_MGR_STATE_WRITE); 23862306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, ctx->stats.op_write_complete_state, FPGA_MGR_STATE_WRITE_COMPLETE); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, ctx->stats.op_write_init_seq, ctx->stats.op_parse_header_seq + 1); 24162306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, ctx->stats.op_write_seq, ctx->stats.op_parse_header_seq + 2); 24262306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, ctx->stats.op_write_complete_seq, ctx->stats.op_parse_header_seq + 3); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/* Check the programming sequence using an image in a scatter gather table */ 24662306a36Sopenharmony_cistatic void fpga_mgr_test_img_load_sgt(struct kunit *test) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct mgr_ctx *ctx = test->priv; 24962306a36Sopenharmony_ci struct sg_table *sgt; 25062306a36Sopenharmony_ci char *img_buf; 25162306a36Sopenharmony_ci int ret; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci img_buf = init_test_buffer(test, IMAGE_SIZE); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci sgt = kunit_kzalloc(test, sizeof(*sgt), GFP_KERNEL); 25662306a36Sopenharmony_ci ret = sg_alloc_table(sgt, 1, GFP_KERNEL); 25762306a36Sopenharmony_ci KUNIT_ASSERT_EQ(test, ret, 0); 25862306a36Sopenharmony_ci sg_init_one(sgt->sgl, img_buf, IMAGE_SIZE); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci ctx->img_info->sgt = sgt; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci ret = fpga_mgr_load(ctx->mgr, ctx->img_info); 26362306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, ret, 0); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, ctx->stats.header_match); 26662306a36Sopenharmony_ci KUNIT_EXPECT_TRUE(test, ctx->stats.image_match); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, ctx->stats.op_parse_header_state, FPGA_MGR_STATE_PARSE_HEADER); 26962306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, ctx->stats.op_write_init_state, FPGA_MGR_STATE_WRITE_INIT); 27062306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, ctx->stats.op_write_sg_state, FPGA_MGR_STATE_WRITE); 27162306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, ctx->stats.op_write_complete_state, FPGA_MGR_STATE_WRITE_COMPLETE); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, ctx->stats.op_write_init_seq, ctx->stats.op_parse_header_seq + 1); 27462306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, ctx->stats.op_write_sg_seq, ctx->stats.op_parse_header_seq + 2); 27562306a36Sopenharmony_ci KUNIT_EXPECT_EQ(test, ctx->stats.op_write_complete_seq, ctx->stats.op_parse_header_seq + 3); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci sg_free_table(ctx->img_info->sgt); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int fpga_mgr_test_init(struct kunit *test) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct mgr_ctx *ctx; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); 28562306a36Sopenharmony_ci KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ctx->pdev = platform_device_register_simple("mgr_pdev", PLATFORM_DEVID_AUTO, NULL, 0); 28862306a36Sopenharmony_ci KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->pdev); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci ctx->mgr = devm_fpga_mgr_register(&ctx->pdev->dev, "Fake FPGA Manager", &fake_mgr_ops, 29162306a36Sopenharmony_ci &ctx->stats); 29262306a36Sopenharmony_ci KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->mgr)); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci ctx->img_info = fpga_image_info_alloc(&ctx->pdev->dev); 29562306a36Sopenharmony_ci KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->img_info); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci test->priv = ctx; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic void fpga_mgr_test_exit(struct kunit *test) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct mgr_ctx *ctx = test->priv; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci fpga_image_info_free(ctx->img_info); 30762306a36Sopenharmony_ci platform_device_unregister(ctx->pdev); 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic struct kunit_case fpga_mgr_test_cases[] = { 31162306a36Sopenharmony_ci KUNIT_CASE(fpga_mgr_test_get), 31262306a36Sopenharmony_ci KUNIT_CASE(fpga_mgr_test_lock), 31362306a36Sopenharmony_ci KUNIT_CASE(fpga_mgr_test_img_load_buf), 31462306a36Sopenharmony_ci KUNIT_CASE(fpga_mgr_test_img_load_sgt), 31562306a36Sopenharmony_ci {} 31662306a36Sopenharmony_ci}; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic struct kunit_suite fpga_mgr_suite = { 31962306a36Sopenharmony_ci .name = "fpga_mgr", 32062306a36Sopenharmony_ci .init = fpga_mgr_test_init, 32162306a36Sopenharmony_ci .exit = fpga_mgr_test_exit, 32262306a36Sopenharmony_ci .test_cases = fpga_mgr_test_cases, 32362306a36Sopenharmony_ci}; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cikunit_test_suite(fpga_mgr_suite); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 328