162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2021  Maciej W. Rozycki
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/init.h>
962306a36Sopenharmony_ci#include <linux/ktime.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/printk.h>
1262306a36Sopenharmony_ci#include <linux/time64.h>
1362306a36Sopenharmony_ci#include <linux/types.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <asm/div64.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define TEST_DIV64_N_ITER 1024
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic const u64 test_div64_dividends[] = {
2062306a36Sopenharmony_ci	0x00000000ab275080,
2162306a36Sopenharmony_ci	0x0000000fe73c1959,
2262306a36Sopenharmony_ci	0x000000e54c0a74b1,
2362306a36Sopenharmony_ci	0x00000d4398ff1ef9,
2462306a36Sopenharmony_ci	0x0000a18c2ee1c097,
2562306a36Sopenharmony_ci	0x00079fb80b072e4a,
2662306a36Sopenharmony_ci	0x0072db27380dd689,
2762306a36Sopenharmony_ci	0x0842f488162e2284,
2862306a36Sopenharmony_ci	0xf66745411d8ab063,
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci#define SIZE_DIV64_DIVIDENDS ARRAY_SIZE(test_div64_dividends)
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define TEST_DIV64_DIVISOR_0 0x00000009
3362306a36Sopenharmony_ci#define TEST_DIV64_DIVISOR_1 0x0000007c
3462306a36Sopenharmony_ci#define TEST_DIV64_DIVISOR_2 0x00000204
3562306a36Sopenharmony_ci#define TEST_DIV64_DIVISOR_3 0x0000cb5b
3662306a36Sopenharmony_ci#define TEST_DIV64_DIVISOR_4 0x00010000
3762306a36Sopenharmony_ci#define TEST_DIV64_DIVISOR_5 0x0008a880
3862306a36Sopenharmony_ci#define TEST_DIV64_DIVISOR_6 0x003fd3ae
3962306a36Sopenharmony_ci#define TEST_DIV64_DIVISOR_7 0x0b658fac
4062306a36Sopenharmony_ci#define TEST_DIV64_DIVISOR_8 0xdc08b349
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic const u32 test_div64_divisors[] = {
4362306a36Sopenharmony_ci	TEST_DIV64_DIVISOR_0,
4462306a36Sopenharmony_ci	TEST_DIV64_DIVISOR_1,
4562306a36Sopenharmony_ci	TEST_DIV64_DIVISOR_2,
4662306a36Sopenharmony_ci	TEST_DIV64_DIVISOR_3,
4762306a36Sopenharmony_ci	TEST_DIV64_DIVISOR_4,
4862306a36Sopenharmony_ci	TEST_DIV64_DIVISOR_5,
4962306a36Sopenharmony_ci	TEST_DIV64_DIVISOR_6,
5062306a36Sopenharmony_ci	TEST_DIV64_DIVISOR_7,
5162306a36Sopenharmony_ci	TEST_DIV64_DIVISOR_8,
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci#define SIZE_DIV64_DIVISORS ARRAY_SIZE(test_div64_divisors)
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic const struct {
5662306a36Sopenharmony_ci	u64 quotient;
5762306a36Sopenharmony_ci	u32 remainder;
5862306a36Sopenharmony_ci} test_div64_results[SIZE_DIV64_DIVISORS][SIZE_DIV64_DIVIDENDS] = {
5962306a36Sopenharmony_ci	{
6062306a36Sopenharmony_ci		{ 0x0000000013045e47, 0x00000001 },
6162306a36Sopenharmony_ci		{ 0x000000000161596c, 0x00000030 },
6262306a36Sopenharmony_ci		{ 0x000000000054e9d4, 0x00000130 },
6362306a36Sopenharmony_ci		{ 0x000000000000d776, 0x0000278e },
6462306a36Sopenharmony_ci		{ 0x000000000000ab27, 0x00005080 },
6562306a36Sopenharmony_ci		{ 0x00000000000013c4, 0x0004ce80 },
6662306a36Sopenharmony_ci		{ 0x00000000000002ae, 0x001e143c },
6762306a36Sopenharmony_ci		{ 0x000000000000000f, 0x0033e56c },
6862306a36Sopenharmony_ci		{ 0x0000000000000000, 0xab275080 },
6962306a36Sopenharmony_ci	}, {
7062306a36Sopenharmony_ci		{ 0x00000001c45c02d1, 0x00000000 },
7162306a36Sopenharmony_ci		{ 0x0000000020d5213c, 0x00000049 },
7262306a36Sopenharmony_ci		{ 0x0000000007e3d65f, 0x000001dd },
7362306a36Sopenharmony_ci		{ 0x0000000000140531, 0x000065ee },
7462306a36Sopenharmony_ci		{ 0x00000000000fe73c, 0x00001959 },
7562306a36Sopenharmony_ci		{ 0x000000000001d637, 0x0004e5d9 },
7662306a36Sopenharmony_ci		{ 0x0000000000003fc9, 0x000713bb },
7762306a36Sopenharmony_ci		{ 0x0000000000000165, 0x029abe7d },
7862306a36Sopenharmony_ci		{ 0x0000000000000012, 0x6e9f7e37 },
7962306a36Sopenharmony_ci	}, {
8062306a36Sopenharmony_ci		{ 0x000000197a3a0cf7, 0x00000002 },
8162306a36Sopenharmony_ci		{ 0x00000001d9632e5c, 0x00000021 },
8262306a36Sopenharmony_ci		{ 0x0000000071c28039, 0x000001cd },
8362306a36Sopenharmony_ci		{ 0x000000000120a844, 0x0000b885 },
8462306a36Sopenharmony_ci		{ 0x0000000000e54c0a, 0x000074b1 },
8562306a36Sopenharmony_ci		{ 0x00000000001a7bb3, 0x00072331 },
8662306a36Sopenharmony_ci		{ 0x00000000000397ad, 0x0002c61b },
8762306a36Sopenharmony_ci		{ 0x000000000000141e, 0x06ea2e89 },
8862306a36Sopenharmony_ci		{ 0x000000000000010a, 0xab002ad7 },
8962306a36Sopenharmony_ci	}, {
9062306a36Sopenharmony_ci		{ 0x0000017949e37538, 0x00000001 },
9162306a36Sopenharmony_ci		{ 0x0000001b62441f37, 0x00000055 },
9262306a36Sopenharmony_ci		{ 0x0000000694a3391d, 0x00000085 },
9362306a36Sopenharmony_ci		{ 0x0000000010b2a5d2, 0x0000a753 },
9462306a36Sopenharmony_ci		{ 0x000000000d4398ff, 0x00001ef9 },
9562306a36Sopenharmony_ci		{ 0x0000000001882ec6, 0x0005cbf9 },
9662306a36Sopenharmony_ci		{ 0x000000000035333b, 0x0017abdf },
9762306a36Sopenharmony_ci		{ 0x00000000000129f1, 0x0ab4520d },
9862306a36Sopenharmony_ci		{ 0x0000000000000f6e, 0x8ac0ce9b },
9962306a36Sopenharmony_ci	}, {
10062306a36Sopenharmony_ci		{ 0x000011f321a74e49, 0x00000006 },
10162306a36Sopenharmony_ci		{ 0x0000014d8481d211, 0x0000005b },
10262306a36Sopenharmony_ci		{ 0x0000005025cbd92d, 0x000001e3 },
10362306a36Sopenharmony_ci		{ 0x00000000cb5e71e3, 0x000043e6 },
10462306a36Sopenharmony_ci		{ 0x00000000a18c2ee1, 0x0000c097 },
10562306a36Sopenharmony_ci		{ 0x0000000012a88828, 0x00036c97 },
10662306a36Sopenharmony_ci		{ 0x000000000287f16f, 0x002c2a25 },
10762306a36Sopenharmony_ci		{ 0x00000000000e2cc7, 0x02d581e3 },
10862306a36Sopenharmony_ci		{ 0x000000000000bbf4, 0x1ba08c03 },
10962306a36Sopenharmony_ci	}, {
11062306a36Sopenharmony_ci		{ 0x0000d8db8f72935d, 0x00000005 },
11162306a36Sopenharmony_ci		{ 0x00000fbd5aed7a2e, 0x00000002 },
11262306a36Sopenharmony_ci		{ 0x000003c84b6ea64a, 0x00000122 },
11362306a36Sopenharmony_ci		{ 0x0000000998fa8829, 0x000044b7 },
11462306a36Sopenharmony_ci		{ 0x000000079fb80b07, 0x00002e4a },
11562306a36Sopenharmony_ci		{ 0x00000000e16b20fa, 0x0002a14a },
11662306a36Sopenharmony_ci		{ 0x000000001e940d22, 0x00353b2e },
11762306a36Sopenharmony_ci		{ 0x0000000000ab40ac, 0x06fba6ba },
11862306a36Sopenharmony_ci		{ 0x000000000008debd, 0x72d98365 },
11962306a36Sopenharmony_ci	}, {
12062306a36Sopenharmony_ci		{ 0x000cc3045b8fc281, 0x00000000 },
12162306a36Sopenharmony_ci		{ 0x0000ed1f48b5c9fc, 0x00000079 },
12262306a36Sopenharmony_ci		{ 0x000038fb9c63406a, 0x000000e1 },
12362306a36Sopenharmony_ci		{ 0x000000909705b825, 0x00000a62 },
12462306a36Sopenharmony_ci		{ 0x00000072db27380d, 0x0000d689 },
12562306a36Sopenharmony_ci		{ 0x0000000d43fce827, 0x00082b09 },
12662306a36Sopenharmony_ci		{ 0x00000001ccaba11a, 0x0037e8dd },
12762306a36Sopenharmony_ci		{ 0x000000000a13f729, 0x0566dffd },
12862306a36Sopenharmony_ci		{ 0x000000000085a14b, 0x23d36726 },
12962306a36Sopenharmony_ci	}, {
13062306a36Sopenharmony_ci		{ 0x00eafeb9c993592b, 0x00000001 },
13162306a36Sopenharmony_ci		{ 0x00110e5befa9a991, 0x00000048 },
13262306a36Sopenharmony_ci		{ 0x00041947b4a1d36a, 0x000000dc },
13362306a36Sopenharmony_ci		{ 0x00000a6679327311, 0x0000c079 },
13462306a36Sopenharmony_ci		{ 0x00000842f488162e, 0x00002284 },
13562306a36Sopenharmony_ci		{ 0x000000f4459740fc, 0x00084484 },
13662306a36Sopenharmony_ci		{ 0x0000002122c47bf9, 0x002ca446 },
13762306a36Sopenharmony_ci		{ 0x00000000b9936290, 0x004979c4 },
13862306a36Sopenharmony_ci		{ 0x00000000099ca89d, 0x9db446bf },
13962306a36Sopenharmony_ci	}, {
14062306a36Sopenharmony_ci		{ 0x1b60cece589da1d2, 0x00000001 },
14162306a36Sopenharmony_ci		{ 0x01fcb42be1453f5b, 0x0000004f },
14262306a36Sopenharmony_ci		{ 0x007a3f2457df0749, 0x0000013f },
14362306a36Sopenharmony_ci		{ 0x0001363130e3ec7b, 0x000017aa },
14462306a36Sopenharmony_ci		{ 0x0000f66745411d8a, 0x0000b063 },
14562306a36Sopenharmony_ci		{ 0x00001c757dfab350, 0x00048863 },
14662306a36Sopenharmony_ci		{ 0x000003dc4979c652, 0x00224ea7 },
14762306a36Sopenharmony_ci		{ 0x000000159edc3144, 0x06409ab3 },
14862306a36Sopenharmony_ci		{ 0x000000011eadfee3, 0xa99c48a8 },
14962306a36Sopenharmony_ci	},
15062306a36Sopenharmony_ci};
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic inline bool test_div64_verify(u64 quotient, u32 remainder, int i, int j)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	return (quotient == test_div64_results[i][j].quotient &&
15562306a36Sopenharmony_ci		remainder == test_div64_results[i][j].remainder);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/*
15962306a36Sopenharmony_ci * This needs to be a macro, because we don't want to rely on the compiler
16062306a36Sopenharmony_ci * to do constant propagation, and `do_div' may take a different path for
16162306a36Sopenharmony_ci * constants, so we do want to verify that as well.
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_ci#define test_div64_one(dividend, divisor, i, j) ({			\
16462306a36Sopenharmony_ci	bool result = true;						\
16562306a36Sopenharmony_ci	u64 quotient;							\
16662306a36Sopenharmony_ci	u32 remainder;							\
16762306a36Sopenharmony_ci									\
16862306a36Sopenharmony_ci	quotient = dividend;						\
16962306a36Sopenharmony_ci	remainder = do_div(quotient, divisor);				\
17062306a36Sopenharmony_ci	if (!test_div64_verify(quotient, remainder, i, j)) {		\
17162306a36Sopenharmony_ci		pr_err("ERROR: %016llx / %08x => %016llx,%08x\n",	\
17262306a36Sopenharmony_ci		       dividend, divisor, quotient, remainder);		\
17362306a36Sopenharmony_ci		pr_err("ERROR: expected value              => %016llx,%08x\n",\
17462306a36Sopenharmony_ci		       test_div64_results[i][j].quotient,		\
17562306a36Sopenharmony_ci		       test_div64_results[i][j].remainder);		\
17662306a36Sopenharmony_ci		result = false;						\
17762306a36Sopenharmony_ci	}								\
17862306a36Sopenharmony_ci	result;								\
17962306a36Sopenharmony_ci})
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci/*
18262306a36Sopenharmony_ci * Run calculation for the same divisor value expressed as a constant
18362306a36Sopenharmony_ci * and as a variable, so as to verify the implementation for both cases
18462306a36Sopenharmony_ci * should they be handled by different code execution paths.
18562306a36Sopenharmony_ci */
18662306a36Sopenharmony_cistatic bool __init test_div64(void)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	u64 dividend;
18962306a36Sopenharmony_ci	int i, j;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	for (i = 0; i < SIZE_DIV64_DIVIDENDS; i++) {
19262306a36Sopenharmony_ci		dividend = test_div64_dividends[i];
19362306a36Sopenharmony_ci		if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_0, i, 0))
19462306a36Sopenharmony_ci			return false;
19562306a36Sopenharmony_ci		if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_1, i, 1))
19662306a36Sopenharmony_ci			return false;
19762306a36Sopenharmony_ci		if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_2, i, 2))
19862306a36Sopenharmony_ci			return false;
19962306a36Sopenharmony_ci		if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_3, i, 3))
20062306a36Sopenharmony_ci			return false;
20162306a36Sopenharmony_ci		if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_4, i, 4))
20262306a36Sopenharmony_ci			return false;
20362306a36Sopenharmony_ci		if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_5, i, 5))
20462306a36Sopenharmony_ci			return false;
20562306a36Sopenharmony_ci		if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_6, i, 6))
20662306a36Sopenharmony_ci			return false;
20762306a36Sopenharmony_ci		if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_7, i, 7))
20862306a36Sopenharmony_ci			return false;
20962306a36Sopenharmony_ci		if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_8, i, 8))
21062306a36Sopenharmony_ci			return false;
21162306a36Sopenharmony_ci		for (j = 0; j < SIZE_DIV64_DIVISORS; j++) {
21262306a36Sopenharmony_ci			if (!test_div64_one(dividend, test_div64_divisors[j],
21362306a36Sopenharmony_ci					    i, j))
21462306a36Sopenharmony_ci				return false;
21562306a36Sopenharmony_ci		}
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci	return true;
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic int __init test_div64_init(void)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	struct timespec64 ts, ts0, ts1;
22362306a36Sopenharmony_ci	int i;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	pr_info("Starting 64bit/32bit division and modulo test\n");
22662306a36Sopenharmony_ci	ktime_get_ts64(&ts0);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	for (i = 0; i < TEST_DIV64_N_ITER; i++)
22962306a36Sopenharmony_ci		if (!test_div64())
23062306a36Sopenharmony_ci			break;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	ktime_get_ts64(&ts1);
23362306a36Sopenharmony_ci	ts = timespec64_sub(ts1, ts0);
23462306a36Sopenharmony_ci	pr_info("Completed 64bit/32bit division and modulo test, "
23562306a36Sopenharmony_ci		"%llu.%09lus elapsed\n", ts.tv_sec, ts.tv_nsec);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	return 0;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic void __exit test_div64_exit(void)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cimodule_init(test_div64_init);
24562306a36Sopenharmony_cimodule_exit(test_div64_exit);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ciMODULE_AUTHOR("Maciej W. Rozycki <macro@orcam.me.uk>");
24862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
24962306a36Sopenharmony_ciMODULE_DESCRIPTION("64bit/32bit division and modulo test module");
250