1#include <stdio.h>
2#include <stdint.h>
3#include <inttypes.h>
4#include "test.h"
5
6uint32_t data[64];
7
8/* The result of a checksum operation */
9typedef struct {
10  uint64_t addr;
11  uint64_t len;
12  uint32_t sum;
13  char     cc;
14} cksm_t;
15
16
17/* Compute the checksum via the cksm insn */
18static __attribute__((noinline)) cksm_t
19cksm_by_insn(const uint32_t *buff, uint64_t len, uint32_t sum)
20{
21  const uint32_t *init_addr = buff;
22  uint64_t init_length = len;
23  uint64_t addr;
24  char cc;
25  cksm_t result;
26  register uint64_t reg2 asm("2") = (uint64_t) buff;
27  register uint64_t reg3 asm("3") = len;
28
29  asm volatile( "       lhi     4,42\n\t"
30                "       xr      4,4\n\t"        /* set cc to != 0 */
31                "0:	cksm	%0,%1\n\t"	/* do checksum on longs */
32		"	jo	0b\n\t"
33		: "+d" (sum), "+d" (reg2), "+d" (reg3) : : "cc", "memory");
34
35  cc   = get_cc();
36  len  = reg3;
37  addr = reg2;
38
39  /* Check the results */
40  if(addr != (uint64_t)init_addr + init_length)
41    printf("FAIL: address not updated properly\n");
42
43  if(len != 0)
44    printf("FAIL: length not zero\n");
45
46  if (cc != 0)
47    printf("FAIL: condition code not zero\n");
48
49  result.addr = addr;
50  result.len  = len;
51  result.cc   = cc;
52  result.sum  = sum;
53
54  return result;
55}
56
57
58/* Compute the checksum via hand-crafted algorithm */
59static __attribute__((noinline)) cksm_t
60cksm_by_hand(const uint32_t *buff, uint64_t len, uint32_t sum)
61{
62  cksm_t result;
63  unsigned int n;
64  uint64_t v64;
65  uint32_t final;
66
67  for (n=0; n < len/4; n++) {
68    /* Add 4 bytes to the sum. Do this in 64-bit arithmetic so it's
69       easy to see whether there was a carry-out. */
70    v64 = sum;
71    v64 += buff[n];
72    /* If there was a carry-out, add 1 to the sum. */
73    if (v64 >> 32)
74      sum = sum + buff[n] + 1;
75    else
76      sum = sum + buff[n];
77  }
78
79  if (len != 0) {
80    switch (len % 4) {
81    case 0:
82      final = 0;  // suppress gcc warning
83      /* done */
84      break;
85
86    case 1:
87      final = buff[n] & 0xFF000000;
88      break;
89
90    case 2:
91      final = buff[n] & 0xFFFF0000;
92      break;
93
94    case 3:
95      final = buff[n] & 0xFFFFFF00;
96      break;
97    }
98
99    if (len % 4) {
100      v64 = sum;
101      v64 += final;
102      /* If there was a carry-out, add 1 to the sum. */
103      if (v64 >> 32)
104        sum = sum + final + 1;
105      else
106        sum = sum + final;
107    }
108  }
109
110  result.addr = (uint64_t)buff + len;
111  result.len  = 0;
112  result.cc   = 0;
113  result.sum  = sum;
114
115  return result;
116}
117
118/* The results computed by-insn and by-hand must compare equal and
119   the sum must be identical to EXPECTED_SUM. */
120int
121compare_results(cksm_t by_hand, cksm_t by_insn, uint32_t expected_sum)
122{
123  int rc = 0;
124
125  if (by_hand.sum != by_insn.sum) {
126    ++rc;
127    printf("FAIL: sum:   by-hand %"PRIx32"  by-insn %"PRIx32"\n",
128           by_hand.sum, by_insn.sum);
129  }
130
131  if (by_hand.addr != by_insn.addr) {
132    ++rc;
133    printf("FAIL: addr:  by-hand %"PRIx64"  by-insn %"PRIx64"\n",
134           by_hand.addr, by_insn.addr);
135  }
136
137  if (by_hand.len != by_insn.len) {
138    ++rc;
139    printf("FAIL: len:   by-hand %"PRIx64"  by-insn %"PRIx64"\n",
140           by_hand.len, by_insn.len);
141  }
142
143  if (by_hand.cc != by_insn.cc) {
144    ++rc;
145    printf("FAIL: cc:    by-hand %d  by-insn %d\n",
146           by_hand.cc, by_insn.cc);
147  }
148
149  if (by_insn.sum != expected_sum) {
150    ++rc;
151    printf("FAIL: sum:   by-insn %"PRIx32"  expected %"PRIx32"\n",
152           by_insn.sum, expected_sum);
153  }
154
155  if (by_hand.sum != expected_sum) {
156    ++rc;
157    printf("FAIL: sum:   by-hand %"PRIx32"  expected %"PRIx32"\n",
158           by_hand.sum, expected_sum);
159  }
160
161  return rc;
162}
163
164/* Run a testcase. Compute the checksum by-hand and by-insn and compare
165   the results */
166void
167run_test(const char *name, const uint32_t *buff, uint64_t len, uint32_t sum,
168         uint32_t expected_sum)
169{
170  cksm_t by_hand, by_insn;
171
172  by_hand = cksm_by_hand(buff, len, sum);
173  by_insn = cksm_by_insn(buff, len, sum);
174  if (compare_results(by_hand, by_insn, expected_sum) != 0) {
175    printf("%s failed\n", name);
176  }
177}
178
179int main ()
180{
181  uint32_t sum, expected_sum;
182  uint64_t len;
183
184  /* ---------------- test 1 ------------------------------ */
185  /* Add one word to an initial sum; no carry */
186  sum = 2;
187  data[0] = 1;
188  len = 4;
189  expected_sum = 3;
190  run_test("test1", data, len, sum, expected_sum);
191
192  /* ---------------- test 2 ------------------------------ */
193  /* Add one word to an initial sum; with carry */
194  sum = 1;
195  data[0] = 0xffffffff;
196  len = 4;
197  expected_sum = 1;
198  run_test("test2", data, len, sum, expected_sum);
199
200  /* ---------------- test 3 ------------------------------ */
201  /* Add 15 words to an initial sum; no carry */
202  sum      = 0x1;
203  data[0]  = 0x4;
204  data[1]  = 0x10;
205  data[2]  = 0x40;
206  data[3]  = 0x100;
207  data[4]  = 0x400;
208  data[5]  = 0x1000;
209  data[6]  = 0x4000;
210  data[7]  = 0x10000;
211  data[8]  = 0x40000;
212  data[9]  = 0x100000;
213  data[10] = 0x400000;
214  data[11] = 0x1000000;
215  data[12] = 0x4000000;
216  data[13] = 0x10000000;
217  data[14] = 0x40000000;
218  len = 60;
219  expected_sum = 0x55555555;
220  run_test("test3", data, len, sum, expected_sum);
221
222  /* ---------------- test 4 ------------------------------ */
223  /* Add some words such that every addition generates a carry.
224     The data is such that the least significant byte is zero,
225     and the carrys from intermediate additions will accumulate
226     in the least significant byte. */
227  sum      = 0xff000000;
228  data[0]  = 0x80000000;   /* 7f0000001 */
229  data[1]  = 0x85000000;   /* 040000002 */
230  data[2]  = 0xff000000;   /* 030000003 */
231  data[3]  = 0xff000000;   /* 020000004 */
232  data[4]  = 0xff000000;   /* 010000005 */
233  data[5]  = 0xff000000;   /* 000000006 */
234  len = 24;
235  expected_sum = 0x00000006;
236  run_test("test4", data, len, sum, expected_sum);
237
238  /* ---------------- test 5 ------------------------------ */
239  /* No words are added. Pass a NULL pointer so an attempt to
240     load would raise a SIGSEGV. */
241  len = 0;
242  sum = 42;
243  expected_sum = sum;
244  run_test("test5", NULL, len, sum, expected_sum);
245
246  /* ---------------- test 6 ------------------------------ */
247  /* Add 1 byte; no carry */
248  sum = 0x02000000;
249  len = 1;
250  data[0] = 0x7fffffff;
251  expected_sum = 0x81000000;
252  run_test("test6", data, len, sum, expected_sum);
253
254  /* ---------------- test 7 ------------------------------ */
255  /* Add 1 byte; carry */
256  sum = 0x02000000;
257  len = 1;
258  data[0] = 0xffffffff;
259  expected_sum = 0x01000001;
260  run_test("test7", data, len, sum, expected_sum);
261
262  /* ---------------- test 8 ------------------------------ */
263  /* Add 2 bytes; no carry */
264  sum = 0x00020000;
265  len = 2;
266  data[0] = 0x7fffffff;
267  expected_sum = 0x80010000;
268  run_test("test8", data, len, sum, expected_sum);
269
270  /* ---------------- test 9 ------------------------------ */
271  /* Add 2 bytes; carry */
272  sum = 0x00020000;
273  len = 2;
274  data[0] = 0xffffffff;
275  expected_sum = 0x00010001;
276  run_test("test9", data, len, sum, expected_sum);
277
278  /* ---------------- test 10 ------------------------------ */
279  /* Add 3 bytes; no carry */
280  sum = 0x00000200;
281  len = 3;
282  data[0] = 0x7fffffff;
283  expected_sum = 0x80000100;
284  run_test("test10", data, len, sum, expected_sum);
285
286  /* ---------------- test 11 ------------------------------ */
287  /* Add 3 bytes; carry */
288  sum = 0x00000200;
289  len = 3;
290  data[0] = 0xffffffff;
291  expected_sum = 0x00000101;
292  run_test("test11", data, len, sum, expected_sum);
293
294  return 0;
295}
296