1 #include <stdio.h>
2 #include <stdint.h>
3 #include <inttypes.h>
4 #include "test.h"
5 
6 uint32_t data[64];
7 
8 /* The result of a checksum operation */
9 typedef 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 */
18 static __attribute__((noinline)) cksm_t
cksm_by_insn(const uint32_t * buff,uint64_t len,uint32_t sum)19 cksm_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 */
59 static __attribute__((noinline)) cksm_t
cksm_by_hand(const uint32_t * buff,uint64_t len,uint32_t sum)60 cksm_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. */
120 int
compare_results(cksm_t by_hand,cksm_t by_insn,uint32_t expected_sum)121 compare_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 */
166 void
run_test(const char * name,const uint32_t * buff,uint64_t len,uint32_t sum,uint32_t expected_sum)167 run_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 
main()179 int 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