| #include <stdio.h> |
| #include <stdint.h> |
| #include <inttypes.h> |
| #include "test.h" |
| |
| uint32_t data[64]; |
| |
| /* The result of a checksum operation */ |
| typedef struct { |
| uint64_t addr; |
| uint64_t len; |
| uint32_t sum; |
| char cc; |
| } cksm_t; |
| |
| |
| /* Compute the checksum via the cksm insn */ |
| static __attribute__((noinline)) cksm_t |
| cksm_by_insn(const uint32_t *buff, uint64_t len, uint32_t sum) |
| { |
| const uint32_t *init_addr = buff; |
| uint64_t init_length = len; |
| uint64_t addr; |
| char cc; |
| cksm_t result; |
| register uint64_t reg2 asm("2") = (uint64_t) buff; |
| register uint64_t reg3 asm("3") = len; |
| |
| asm volatile( " lhi 4,42\n\t" |
| " xr 4,4\n\t" /* set cc to != 0 */ |
| "0: cksm %0,%1\n\t" /* do checksum on longs */ |
| " jo 0b\n\t" |
| : "+d" (sum), "+d" (reg2), "+d" (reg3) : : "cc", "memory"); |
| |
| cc = get_cc(); |
| len = reg3; |
| addr = reg2; |
| |
| /* Check the results */ |
| if(addr != (uint64_t)init_addr + init_length) |
| printf("FAIL: address not updated properly\n"); |
| |
| if(len != 0) |
| printf("FAIL: length not zero\n"); |
| |
| if (cc != 0) |
| printf("FAIL: condition code not zero\n"); |
| |
| result.addr = addr; |
| result.len = len; |
| result.cc = cc; |
| result.sum = sum; |
| |
| return result; |
| } |
| |
| |
| /* Compute the checksum via hand-crafted algorithm */ |
| static __attribute__((noinline)) cksm_t |
| cksm_by_hand(const uint32_t *buff, uint64_t len, uint32_t sum) |
| { |
| cksm_t result; |
| unsigned int n; |
| uint64_t v64; |
| uint32_t final; |
| |
| for (n=0; n < len/4; n++) { |
| /* Add 4 bytes to the sum. Do this in 64-bit arithmetic so it's |
| easy to see whether there was a carry-out. */ |
| v64 = sum; |
| v64 += buff[n]; |
| /* If there was a carry-out, add 1 to the sum. */ |
| if (v64 >> 32) |
| sum = sum + buff[n] + 1; |
| else |
| sum = sum + buff[n]; |
| } |
| |
| if (len != 0) { |
| switch (len % 4) { |
| case 0: |
| final = 0; // suppress gcc warning |
| /* done */ |
| break; |
| |
| case 1: |
| final = buff[n] & 0xFF000000; |
| break; |
| |
| case 2: |
| final = buff[n] & 0xFFFF0000; |
| break; |
| |
| case 3: |
| final = buff[n] & 0xFFFFFF00; |
| break; |
| } |
| |
| if (len % 4) { |
| v64 = sum; |
| v64 += final; |
| /* If there was a carry-out, add 1 to the sum. */ |
| if (v64 >> 32) |
| sum = sum + final + 1; |
| else |
| sum = sum + final; |
| } |
| } |
| |
| result.addr = (uint64_t)buff + len; |
| result.len = 0; |
| result.cc = 0; |
| result.sum = sum; |
| |
| return result; |
| } |
| |
| /* The results computed by-insn and by-hand must compare equal and |
| the sum must be identical to EXPECTED_SUM. */ |
| int |
| compare_results(cksm_t by_hand, cksm_t by_insn, uint32_t expected_sum) |
| { |
| int rc = 0; |
| |
| if (by_hand.sum != by_insn.sum) { |
| ++rc; |
| printf("FAIL: sum: by-hand %"PRIx32" by-insn %"PRIx32"\n", |
| by_hand.sum, by_insn.sum); |
| } |
| |
| if (by_hand.addr != by_insn.addr) { |
| ++rc; |
| printf("FAIL: addr: by-hand %"PRIx64" by-insn %"PRIx64"\n", |
| by_hand.addr, by_insn.addr); |
| } |
| |
| if (by_hand.len != by_insn.len) { |
| ++rc; |
| printf("FAIL: len: by-hand %"PRIx64" by-insn %"PRIx64"\n", |
| by_hand.len, by_insn.len); |
| } |
| |
| if (by_hand.cc != by_insn.cc) { |
| ++rc; |
| printf("FAIL: cc: by-hand %d by-insn %d\n", |
| by_hand.cc, by_insn.cc); |
| } |
| |
| if (by_insn.sum != expected_sum) { |
| ++rc; |
| printf("FAIL: sum: by-insn %"PRIx32" expected %"PRIx32"\n", |
| by_insn.sum, expected_sum); |
| } |
| |
| if (by_hand.sum != expected_sum) { |
| ++rc; |
| printf("FAIL: sum: by-hand %"PRIx32" expected %"PRIx32"\n", |
| by_hand.sum, expected_sum); |
| } |
| |
| return rc; |
| } |
| |
| /* Run a testcase. Compute the checksum by-hand and by-insn and compare |
| the results */ |
| void |
| run_test(const char *name, const uint32_t *buff, uint64_t len, uint32_t sum, |
| uint32_t expected_sum) |
| { |
| cksm_t by_hand, by_insn; |
| |
| by_hand = cksm_by_hand(buff, len, sum); |
| by_insn = cksm_by_insn(buff, len, sum); |
| if (compare_results(by_hand, by_insn, expected_sum) != 0) { |
| printf("%s failed\n", name); |
| } |
| } |
| |
| int main () |
| { |
| uint32_t sum, expected_sum; |
| uint64_t len; |
| |
| /* ---------------- test 1 ------------------------------ */ |
| /* Add one word to an initial sum; no carry */ |
| sum = 2; |
| data[0] = 1; |
| len = 4; |
| expected_sum = 3; |
| run_test("test1", data, len, sum, expected_sum); |
| |
| /* ---------------- test 2 ------------------------------ */ |
| /* Add one word to an initial sum; with carry */ |
| sum = 1; |
| data[0] = 0xffffffff; |
| len = 4; |
| expected_sum = 1; |
| run_test("test2", data, len, sum, expected_sum); |
| |
| /* ---------------- test 3 ------------------------------ */ |
| /* Add 15 words to an initial sum; no carry */ |
| sum = 0x1; |
| data[0] = 0x4; |
| data[1] = 0x10; |
| data[2] = 0x40; |
| data[3] = 0x100; |
| data[4] = 0x400; |
| data[5] = 0x1000; |
| data[6] = 0x4000; |
| data[7] = 0x10000; |
| data[8] = 0x40000; |
| data[9] = 0x100000; |
| data[10] = 0x400000; |
| data[11] = 0x1000000; |
| data[12] = 0x4000000; |
| data[13] = 0x10000000; |
| data[14] = 0x40000000; |
| len = 60; |
| expected_sum = 0x55555555; |
| run_test("test3", data, len, sum, expected_sum); |
| |
| /* ---------------- test 4 ------------------------------ */ |
| /* Add some words such that every addition generates a carry. |
| The data is such that the least significant byte is zero, |
| and the carrys from intermediate additions will accumulate |
| in the least significant byte. */ |
| sum = 0xff000000; |
| data[0] = 0x80000000; /* 7f0000001 */ |
| data[1] = 0x85000000; /* 040000002 */ |
| data[2] = 0xff000000; /* 030000003 */ |
| data[3] = 0xff000000; /* 020000004 */ |
| data[4] = 0xff000000; /* 010000005 */ |
| data[5] = 0xff000000; /* 000000006 */ |
| len = 24; |
| expected_sum = 0x00000006; |
| run_test("test4", data, len, sum, expected_sum); |
| |
| /* ---------------- test 5 ------------------------------ */ |
| /* No words are added. Pass a NULL pointer so an attempt to |
| load would raise a SIGSEGV. */ |
| len = 0; |
| sum = 42; |
| expected_sum = sum; |
| run_test("test5", NULL, len, sum, expected_sum); |
| |
| /* ---------------- test 6 ------------------------------ */ |
| /* Add 1 byte; no carry */ |
| sum = 0x02000000; |
| len = 1; |
| data[0] = 0x7fffffff; |
| expected_sum = 0x81000000; |
| run_test("test6", data, len, sum, expected_sum); |
| |
| /* ---------------- test 7 ------------------------------ */ |
| /* Add 1 byte; carry */ |
| sum = 0x02000000; |
| len = 1; |
| data[0] = 0xffffffff; |
| expected_sum = 0x01000001; |
| run_test("test7", data, len, sum, expected_sum); |
| |
| /* ---------------- test 8 ------------------------------ */ |
| /* Add 2 bytes; no carry */ |
| sum = 0x00020000; |
| len = 2; |
| data[0] = 0x7fffffff; |
| expected_sum = 0x80010000; |
| run_test("test8", data, len, sum, expected_sum); |
| |
| /* ---------------- test 9 ------------------------------ */ |
| /* Add 2 bytes; carry */ |
| sum = 0x00020000; |
| len = 2; |
| data[0] = 0xffffffff; |
| expected_sum = 0x00010001; |
| run_test("test9", data, len, sum, expected_sum); |
| |
| /* ---------------- test 10 ------------------------------ */ |
| /* Add 3 bytes; no carry */ |
| sum = 0x00000200; |
| len = 3; |
| data[0] = 0x7fffffff; |
| expected_sum = 0x80000100; |
| run_test("test10", data, len, sum, expected_sum); |
| |
| /* ---------------- test 11 ------------------------------ */ |
| /* Add 3 bytes; carry */ |
| sum = 0x00000200; |
| len = 3; |
| data[0] = 0xffffffff; |
| expected_sum = 0x00000101; |
| run_test("test11", data, len, sum, expected_sum); |
| |
| return 0; |
| } |