xref: /openbmc/linux/lib/math/test_div64.c (revision 8be98d2f2a0a262f8bf8a0bc1fdf522b3c7aab17)
15086ea4bSMaciej W. Rozycki // SPDX-License-Identifier: GPL-2.0
25086ea4bSMaciej W. Rozycki /*
35086ea4bSMaciej W. Rozycki  * Copyright (C) 2021  Maciej W. Rozycki
45086ea4bSMaciej W. Rozycki  */
55086ea4bSMaciej W. Rozycki 
65086ea4bSMaciej W. Rozycki #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
75086ea4bSMaciej W. Rozycki 
85086ea4bSMaciej W. Rozycki #include <linux/init.h>
95086ea4bSMaciej W. Rozycki #include <linux/ktime.h>
105086ea4bSMaciej W. Rozycki #include <linux/module.h>
115086ea4bSMaciej W. Rozycki #include <linux/printk.h>
125086ea4bSMaciej W. Rozycki #include <linux/time64.h>
135086ea4bSMaciej W. Rozycki #include <linux/types.h>
145086ea4bSMaciej W. Rozycki 
155086ea4bSMaciej W. Rozycki #include <asm/div64.h>
165086ea4bSMaciej W. Rozycki 
175086ea4bSMaciej W. Rozycki #define TEST_DIV64_N_ITER 1024
185086ea4bSMaciej W. Rozycki 
19*517b322cSMaciej W. Rozycki static const u64 test_div64_dividends[] = {
205086ea4bSMaciej W. Rozycki 	0x00000000ab275080,
215086ea4bSMaciej W. Rozycki 	0x0000000fe73c1959,
225086ea4bSMaciej W. Rozycki 	0x000000e54c0a74b1,
235086ea4bSMaciej W. Rozycki 	0x00000d4398ff1ef9,
245086ea4bSMaciej W. Rozycki 	0x0000a18c2ee1c097,
255086ea4bSMaciej W. Rozycki 	0x00079fb80b072e4a,
265086ea4bSMaciej W. Rozycki 	0x0072db27380dd689,
275086ea4bSMaciej W. Rozycki 	0x0842f488162e2284,
285086ea4bSMaciej W. Rozycki 	0xf66745411d8ab063,
295086ea4bSMaciej W. Rozycki };
30*517b322cSMaciej W. Rozycki #define SIZE_DIV64_DIVIDENDS ARRAY_SIZE(test_div64_dividends)
315086ea4bSMaciej W. Rozycki 
325086ea4bSMaciej W. Rozycki #define TEST_DIV64_DIVISOR_0 0x00000009
335086ea4bSMaciej W. Rozycki #define TEST_DIV64_DIVISOR_1 0x0000007c
345086ea4bSMaciej W. Rozycki #define TEST_DIV64_DIVISOR_2 0x00000204
355086ea4bSMaciej W. Rozycki #define TEST_DIV64_DIVISOR_3 0x0000cb5b
365086ea4bSMaciej W. Rozycki #define TEST_DIV64_DIVISOR_4 0x00010000
375086ea4bSMaciej W. Rozycki #define TEST_DIV64_DIVISOR_5 0x0008a880
385086ea4bSMaciej W. Rozycki #define TEST_DIV64_DIVISOR_6 0x003fd3ae
395086ea4bSMaciej W. Rozycki #define TEST_DIV64_DIVISOR_7 0x0b658fac
405086ea4bSMaciej W. Rozycki #define TEST_DIV64_DIVISOR_8 0xdc08b349
415086ea4bSMaciej W. Rozycki 
425086ea4bSMaciej W. Rozycki static const u32 test_div64_divisors[] = {
435086ea4bSMaciej W. Rozycki 	TEST_DIV64_DIVISOR_0,
445086ea4bSMaciej W. Rozycki 	TEST_DIV64_DIVISOR_1,
455086ea4bSMaciej W. Rozycki 	TEST_DIV64_DIVISOR_2,
465086ea4bSMaciej W. Rozycki 	TEST_DIV64_DIVISOR_3,
475086ea4bSMaciej W. Rozycki 	TEST_DIV64_DIVISOR_4,
485086ea4bSMaciej W. Rozycki 	TEST_DIV64_DIVISOR_5,
495086ea4bSMaciej W. Rozycki 	TEST_DIV64_DIVISOR_6,
505086ea4bSMaciej W. Rozycki 	TEST_DIV64_DIVISOR_7,
515086ea4bSMaciej W. Rozycki 	TEST_DIV64_DIVISOR_8,
525086ea4bSMaciej W. Rozycki };
535086ea4bSMaciej W. Rozycki #define SIZE_DIV64_DIVISORS ARRAY_SIZE(test_div64_divisors)
545086ea4bSMaciej W. Rozycki 
555086ea4bSMaciej W. Rozycki static const struct {
565086ea4bSMaciej W. Rozycki 	u64 quotient;
575086ea4bSMaciej W. Rozycki 	u32 remainder;
58*517b322cSMaciej W. Rozycki } test_div64_results[SIZE_DIV64_DIVISORS][SIZE_DIV64_DIVIDENDS] = {
595086ea4bSMaciej W. Rozycki 	{
605086ea4bSMaciej W. Rozycki 		{ 0x0000000013045e47, 0x00000001 },
615086ea4bSMaciej W. Rozycki 		{ 0x000000000161596c, 0x00000030 },
625086ea4bSMaciej W. Rozycki 		{ 0x000000000054e9d4, 0x00000130 },
635086ea4bSMaciej W. Rozycki 		{ 0x000000000000d776, 0x0000278e },
645086ea4bSMaciej W. Rozycki 		{ 0x000000000000ab27, 0x00005080 },
655086ea4bSMaciej W. Rozycki 		{ 0x00000000000013c4, 0x0004ce80 },
665086ea4bSMaciej W. Rozycki 		{ 0x00000000000002ae, 0x001e143c },
675086ea4bSMaciej W. Rozycki 		{ 0x000000000000000f, 0x0033e56c },
685086ea4bSMaciej W. Rozycki 		{ 0x0000000000000000, 0xab275080 },
695086ea4bSMaciej W. Rozycki 	}, {
705086ea4bSMaciej W. Rozycki 		{ 0x00000001c45c02d1, 0x00000000 },
715086ea4bSMaciej W. Rozycki 		{ 0x0000000020d5213c, 0x00000049 },
725086ea4bSMaciej W. Rozycki 		{ 0x0000000007e3d65f, 0x000001dd },
735086ea4bSMaciej W. Rozycki 		{ 0x0000000000140531, 0x000065ee },
745086ea4bSMaciej W. Rozycki 		{ 0x00000000000fe73c, 0x00001959 },
755086ea4bSMaciej W. Rozycki 		{ 0x000000000001d637, 0x0004e5d9 },
765086ea4bSMaciej W. Rozycki 		{ 0x0000000000003fc9, 0x000713bb },
775086ea4bSMaciej W. Rozycki 		{ 0x0000000000000165, 0x029abe7d },
785086ea4bSMaciej W. Rozycki 		{ 0x0000000000000012, 0x6e9f7e37 },
795086ea4bSMaciej W. Rozycki 	}, {
805086ea4bSMaciej W. Rozycki 		{ 0x000000197a3a0cf7, 0x00000002 },
815086ea4bSMaciej W. Rozycki 		{ 0x00000001d9632e5c, 0x00000021 },
825086ea4bSMaciej W. Rozycki 		{ 0x0000000071c28039, 0x000001cd },
835086ea4bSMaciej W. Rozycki 		{ 0x000000000120a844, 0x0000b885 },
845086ea4bSMaciej W. Rozycki 		{ 0x0000000000e54c0a, 0x000074b1 },
855086ea4bSMaciej W. Rozycki 		{ 0x00000000001a7bb3, 0x00072331 },
865086ea4bSMaciej W. Rozycki 		{ 0x00000000000397ad, 0x0002c61b },
875086ea4bSMaciej W. Rozycki 		{ 0x000000000000141e, 0x06ea2e89 },
885086ea4bSMaciej W. Rozycki 		{ 0x000000000000010a, 0xab002ad7 },
895086ea4bSMaciej W. Rozycki 	}, {
905086ea4bSMaciej W. Rozycki 		{ 0x0000017949e37538, 0x00000001 },
915086ea4bSMaciej W. Rozycki 		{ 0x0000001b62441f37, 0x00000055 },
925086ea4bSMaciej W. Rozycki 		{ 0x0000000694a3391d, 0x00000085 },
935086ea4bSMaciej W. Rozycki 		{ 0x0000000010b2a5d2, 0x0000a753 },
945086ea4bSMaciej W. Rozycki 		{ 0x000000000d4398ff, 0x00001ef9 },
955086ea4bSMaciej W. Rozycki 		{ 0x0000000001882ec6, 0x0005cbf9 },
965086ea4bSMaciej W. Rozycki 		{ 0x000000000035333b, 0x0017abdf },
975086ea4bSMaciej W. Rozycki 		{ 0x00000000000129f1, 0x0ab4520d },
985086ea4bSMaciej W. Rozycki 		{ 0x0000000000000f6e, 0x8ac0ce9b },
995086ea4bSMaciej W. Rozycki 	}, {
1005086ea4bSMaciej W. Rozycki 		{ 0x000011f321a74e49, 0x00000006 },
1015086ea4bSMaciej W. Rozycki 		{ 0x0000014d8481d211, 0x0000005b },
1025086ea4bSMaciej W. Rozycki 		{ 0x0000005025cbd92d, 0x000001e3 },
1035086ea4bSMaciej W. Rozycki 		{ 0x00000000cb5e71e3, 0x000043e6 },
1045086ea4bSMaciej W. Rozycki 		{ 0x00000000a18c2ee1, 0x0000c097 },
1055086ea4bSMaciej W. Rozycki 		{ 0x0000000012a88828, 0x00036c97 },
1065086ea4bSMaciej W. Rozycki 		{ 0x000000000287f16f, 0x002c2a25 },
1075086ea4bSMaciej W. Rozycki 		{ 0x00000000000e2cc7, 0x02d581e3 },
1085086ea4bSMaciej W. Rozycki 		{ 0x000000000000bbf4, 0x1ba08c03 },
1095086ea4bSMaciej W. Rozycki 	}, {
1105086ea4bSMaciej W. Rozycki 		{ 0x0000d8db8f72935d, 0x00000005 },
1115086ea4bSMaciej W. Rozycki 		{ 0x00000fbd5aed7a2e, 0x00000002 },
1125086ea4bSMaciej W. Rozycki 		{ 0x000003c84b6ea64a, 0x00000122 },
1135086ea4bSMaciej W. Rozycki 		{ 0x0000000998fa8829, 0x000044b7 },
1145086ea4bSMaciej W. Rozycki 		{ 0x000000079fb80b07, 0x00002e4a },
1155086ea4bSMaciej W. Rozycki 		{ 0x00000000e16b20fa, 0x0002a14a },
1165086ea4bSMaciej W. Rozycki 		{ 0x000000001e940d22, 0x00353b2e },
1175086ea4bSMaciej W. Rozycki 		{ 0x0000000000ab40ac, 0x06fba6ba },
1185086ea4bSMaciej W. Rozycki 		{ 0x000000000008debd, 0x72d98365 },
1195086ea4bSMaciej W. Rozycki 	}, {
1205086ea4bSMaciej W. Rozycki 		{ 0x000cc3045b8fc281, 0x00000000 },
1215086ea4bSMaciej W. Rozycki 		{ 0x0000ed1f48b5c9fc, 0x00000079 },
1225086ea4bSMaciej W. Rozycki 		{ 0x000038fb9c63406a, 0x000000e1 },
1235086ea4bSMaciej W. Rozycki 		{ 0x000000909705b825, 0x00000a62 },
1245086ea4bSMaciej W. Rozycki 		{ 0x00000072db27380d, 0x0000d689 },
1255086ea4bSMaciej W. Rozycki 		{ 0x0000000d43fce827, 0x00082b09 },
1265086ea4bSMaciej W. Rozycki 		{ 0x00000001ccaba11a, 0x0037e8dd },
1275086ea4bSMaciej W. Rozycki 		{ 0x000000000a13f729, 0x0566dffd },
1285086ea4bSMaciej W. Rozycki 		{ 0x000000000085a14b, 0x23d36726 },
1295086ea4bSMaciej W. Rozycki 	}, {
1305086ea4bSMaciej W. Rozycki 		{ 0x00eafeb9c993592b, 0x00000001 },
1315086ea4bSMaciej W. Rozycki 		{ 0x00110e5befa9a991, 0x00000048 },
1325086ea4bSMaciej W. Rozycki 		{ 0x00041947b4a1d36a, 0x000000dc },
1335086ea4bSMaciej W. Rozycki 		{ 0x00000a6679327311, 0x0000c079 },
1345086ea4bSMaciej W. Rozycki 		{ 0x00000842f488162e, 0x00002284 },
1355086ea4bSMaciej W. Rozycki 		{ 0x000000f4459740fc, 0x00084484 },
1365086ea4bSMaciej W. Rozycki 		{ 0x0000002122c47bf9, 0x002ca446 },
1375086ea4bSMaciej W. Rozycki 		{ 0x00000000b9936290, 0x004979c4 },
1385086ea4bSMaciej W. Rozycki 		{ 0x00000000099ca89d, 0x9db446bf },
1395086ea4bSMaciej W. Rozycki 	}, {
1405086ea4bSMaciej W. Rozycki 		{ 0x1b60cece589da1d2, 0x00000001 },
1415086ea4bSMaciej W. Rozycki 		{ 0x01fcb42be1453f5b, 0x0000004f },
1425086ea4bSMaciej W. Rozycki 		{ 0x007a3f2457df0749, 0x0000013f },
1435086ea4bSMaciej W. Rozycki 		{ 0x0001363130e3ec7b, 0x000017aa },
1445086ea4bSMaciej W. Rozycki 		{ 0x0000f66745411d8a, 0x0000b063 },
1455086ea4bSMaciej W. Rozycki 		{ 0x00001c757dfab350, 0x00048863 },
1465086ea4bSMaciej W. Rozycki 		{ 0x000003dc4979c652, 0x00224ea7 },
1475086ea4bSMaciej W. Rozycki 		{ 0x000000159edc3144, 0x06409ab3 },
1485086ea4bSMaciej W. Rozycki 		{ 0x000000011eadfee3, 0xa99c48a8 },
1495086ea4bSMaciej W. Rozycki 	},
1505086ea4bSMaciej W. Rozycki };
1515086ea4bSMaciej W. Rozycki 
test_div64_verify(u64 quotient,u32 remainder,int i,int j)1525086ea4bSMaciej W. Rozycki static inline bool test_div64_verify(u64 quotient, u32 remainder, int i, int j)
1535086ea4bSMaciej W. Rozycki {
1545086ea4bSMaciej W. Rozycki 	return (quotient == test_div64_results[i][j].quotient &&
1555086ea4bSMaciej W. Rozycki 		remainder == test_div64_results[i][j].remainder);
1565086ea4bSMaciej W. Rozycki }
1575086ea4bSMaciej W. Rozycki 
1585086ea4bSMaciej W. Rozycki /*
1595086ea4bSMaciej W. Rozycki  * This needs to be a macro, because we don't want to rely on the compiler
1605086ea4bSMaciej W. Rozycki  * to do constant propagation, and `do_div' may take a different path for
1615086ea4bSMaciej W. Rozycki  * constants, so we do want to verify that as well.
1625086ea4bSMaciej W. Rozycki  */
163*517b322cSMaciej W. Rozycki #define test_div64_one(dividend, divisor, i, j) ({			\
1645086ea4bSMaciej W. Rozycki 	bool result = true;						\
1655086ea4bSMaciej W. Rozycki 	u64 quotient;							\
1665086ea4bSMaciej W. Rozycki 	u32 remainder;							\
1675086ea4bSMaciej W. Rozycki 									\
168*517b322cSMaciej W. Rozycki 	quotient = dividend;						\
1695086ea4bSMaciej W. Rozycki 	remainder = do_div(quotient, divisor);				\
1705086ea4bSMaciej W. Rozycki 	if (!test_div64_verify(quotient, remainder, i, j)) {		\
1715086ea4bSMaciej W. Rozycki 		pr_err("ERROR: %016llx / %08x => %016llx,%08x\n",	\
172*517b322cSMaciej W. Rozycki 		       dividend, divisor, quotient, remainder);		\
1735086ea4bSMaciej W. Rozycki 		pr_err("ERROR: expected value              => %016llx,%08x\n",\
1745086ea4bSMaciej W. Rozycki 		       test_div64_results[i][j].quotient,		\
1755086ea4bSMaciej W. Rozycki 		       test_div64_results[i][j].remainder);		\
1765086ea4bSMaciej W. Rozycki 		result = false;						\
1775086ea4bSMaciej W. Rozycki 	}								\
1785086ea4bSMaciej W. Rozycki 	result;								\
1795086ea4bSMaciej W. Rozycki })
1805086ea4bSMaciej W. Rozycki 
1815086ea4bSMaciej W. Rozycki /*
1825086ea4bSMaciej W. Rozycki  * Run calculation for the same divisor value expressed as a constant
1835086ea4bSMaciej W. Rozycki  * and as a variable, so as to verify the implementation for both cases
1845086ea4bSMaciej W. Rozycki  * should they be handled by different code execution paths.
1855086ea4bSMaciej W. Rozycki  */
test_div64(void)1865086ea4bSMaciej W. Rozycki static bool __init test_div64(void)
1875086ea4bSMaciej W. Rozycki {
188*517b322cSMaciej W. Rozycki 	u64 dividend;
1895086ea4bSMaciej W. Rozycki 	int i, j;
1905086ea4bSMaciej W. Rozycki 
191*517b322cSMaciej W. Rozycki 	for (i = 0; i < SIZE_DIV64_DIVIDENDS; i++) {
192*517b322cSMaciej W. Rozycki 		dividend = test_div64_dividends[i];
193*517b322cSMaciej W. Rozycki 		if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_0, i, 0))
1945086ea4bSMaciej W. Rozycki 			return false;
195*517b322cSMaciej W. Rozycki 		if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_1, i, 1))
1965086ea4bSMaciej W. Rozycki 			return false;
197*517b322cSMaciej W. Rozycki 		if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_2, i, 2))
1985086ea4bSMaciej W. Rozycki 			return false;
199*517b322cSMaciej W. Rozycki 		if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_3, i, 3))
2005086ea4bSMaciej W. Rozycki 			return false;
201*517b322cSMaciej W. Rozycki 		if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_4, i, 4))
2025086ea4bSMaciej W. Rozycki 			return false;
203*517b322cSMaciej W. Rozycki 		if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_5, i, 5))
2045086ea4bSMaciej W. Rozycki 			return false;
205*517b322cSMaciej W. Rozycki 		if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_6, i, 6))
2065086ea4bSMaciej W. Rozycki 			return false;
207*517b322cSMaciej W. Rozycki 		if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_7, i, 7))
2085086ea4bSMaciej W. Rozycki 			return false;
209*517b322cSMaciej W. Rozycki 		if (!test_div64_one(dividend, TEST_DIV64_DIVISOR_8, i, 8))
2105086ea4bSMaciej W. Rozycki 			return false;
2115086ea4bSMaciej W. Rozycki 		for (j = 0; j < SIZE_DIV64_DIVISORS; j++) {
212*517b322cSMaciej W. Rozycki 			if (!test_div64_one(dividend, test_div64_divisors[j],
2135086ea4bSMaciej W. Rozycki 					    i, j))
2145086ea4bSMaciej W. Rozycki 				return false;
2155086ea4bSMaciej W. Rozycki 		}
2165086ea4bSMaciej W. Rozycki 	}
2175086ea4bSMaciej W. Rozycki 	return true;
2185086ea4bSMaciej W. Rozycki }
2195086ea4bSMaciej W. Rozycki 
test_div64_init(void)2205086ea4bSMaciej W. Rozycki static int __init test_div64_init(void)
2215086ea4bSMaciej W. Rozycki {
2225086ea4bSMaciej W. Rozycki 	struct timespec64 ts, ts0, ts1;
2235086ea4bSMaciej W. Rozycki 	int i;
2245086ea4bSMaciej W. Rozycki 
2255086ea4bSMaciej W. Rozycki 	pr_info("Starting 64bit/32bit division and modulo test\n");
2265086ea4bSMaciej W. Rozycki 	ktime_get_ts64(&ts0);
2275086ea4bSMaciej W. Rozycki 
2285086ea4bSMaciej W. Rozycki 	for (i = 0; i < TEST_DIV64_N_ITER; i++)
2295086ea4bSMaciej W. Rozycki 		if (!test_div64())
2305086ea4bSMaciej W. Rozycki 			break;
2315086ea4bSMaciej W. Rozycki 
2325086ea4bSMaciej W. Rozycki 	ktime_get_ts64(&ts1);
2335086ea4bSMaciej W. Rozycki 	ts = timespec64_sub(ts1, ts0);
2345086ea4bSMaciej W. Rozycki 	pr_info("Completed 64bit/32bit division and modulo test, "
2355086ea4bSMaciej W. Rozycki 		"%llu.%09lus elapsed\n", ts.tv_sec, ts.tv_nsec);
2365086ea4bSMaciej W. Rozycki 
2375086ea4bSMaciej W. Rozycki 	return 0;
2385086ea4bSMaciej W. Rozycki }
2395086ea4bSMaciej W. Rozycki 
test_div64_exit(void)2405086ea4bSMaciej W. Rozycki static void __exit test_div64_exit(void)
2415086ea4bSMaciej W. Rozycki {
2425086ea4bSMaciej W. Rozycki }
2435086ea4bSMaciej W. Rozycki 
2445086ea4bSMaciej W. Rozycki module_init(test_div64_init);
2455086ea4bSMaciej W. Rozycki module_exit(test_div64_exit);
2465086ea4bSMaciej W. Rozycki 
2475086ea4bSMaciej W. Rozycki MODULE_AUTHOR("Maciej W. Rozycki <macro@orcam.me.uk>");
2485086ea4bSMaciej W. Rozycki MODULE_LICENSE("GPL");
2495086ea4bSMaciej W. Rozycki MODULE_DESCRIPTION("64bit/32bit division and modulo test module");
250