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