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