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