1########################################################################
2# Implement fast CRC-T10DIF computation with SSE and PCLMULQDQ instructions
3#
4# Copyright (c) 2013, Intel Corporation
5#
6# Authors:
7#     Erdinc Ozturk <erdinc.ozturk@intel.com>
8#     Vinodh Gopal <vinodh.gopal@intel.com>
9#     James Guilford <james.guilford@intel.com>
10#     Tim Chen <tim.c.chen@linux.intel.com>
11#
12# This software is available to you under a choice of one of two
13# licenses.  You may choose to be licensed under the terms of the GNU
14# General Public License (GPL) Version 2, available from the file
15# COPYING in the main directory of this source tree, or the
16# OpenIB.org BSD license below:
17#
18# Redistribution and use in source and binary forms, with or without
19# modification, are permitted provided that the following conditions are
20# met:
21#
22# * Redistributions of source code must retain the above copyright
23#   notice, this list of conditions and the following disclaimer.
24#
25# * Redistributions in binary form must reproduce the above copyright
26#   notice, this list of conditions and the following disclaimer in the
27#   documentation and/or other materials provided with the
28#   distribution.
29#
30# * Neither the name of the Intel Corporation nor the names of its
31#   contributors may be used to endorse or promote products derived from
32#   this software without specific prior written permission.
33#
34#
35# THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION ""AS IS"" AND ANY
36# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
38# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR
39# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
40# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
41# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
42# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
43# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
44# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
45# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46########################################################################
47#       Function API:
48#       UINT16 crc_t10dif_pcl(
49#               UINT16 init_crc, //initial CRC value, 16 bits
50#               const unsigned char *buf, //buffer pointer to calculate CRC on
51#               UINT64 len //buffer length in bytes (64-bit data)
52#       );
53#
54#       Reference paper titled "Fast CRC Computation for Generic
55#	Polynomials Using PCLMULQDQ Instruction"
56#       URL: http://www.intel.com/content/dam/www/public/us/en/documents
57#  /white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf
58#
59#
60
61#include <linux/linkage.h>
62
63.text
64
65#define        arg1 %rdi
66#define        arg2 %rsi
67#define        arg3 %rdx
68
69#define        arg1_low32 %edi
70
71ENTRY(crc_t10dif_pcl)
72.align 16
73
74	# adjust the 16-bit initial_crc value, scale it to 32 bits
75	shl	$16, arg1_low32
76
77	# Allocate Stack Space
78	mov     %rsp, %rcx
79	sub	$16*2, %rsp
80	# align stack to 16 byte boundary
81	and     $~(0x10 - 1), %rsp
82
83	# check if smaller than 256
84	cmp	$256, arg3
85
86	# for sizes less than 128, we can't fold 64B at a time...
87	jl	_less_than_128
88
89
90	# load the initial crc value
91	movd	arg1_low32, %xmm10	# initial crc
92
93	# crc value does not need to be byte-reflected, but it needs
94	# to be moved to the high part of the register.
95	# because data will be byte-reflected and will align with
96	# initial crc at correct place.
97	pslldq	$12, %xmm10
98
99	movdqa  SHUF_MASK(%rip), %xmm11
100	# receive the initial 64B data, xor the initial crc value
101	movdqu	16*0(arg2), %xmm0
102	movdqu	16*1(arg2), %xmm1
103	movdqu	16*2(arg2), %xmm2
104	movdqu	16*3(arg2), %xmm3
105	movdqu	16*4(arg2), %xmm4
106	movdqu	16*5(arg2), %xmm5
107	movdqu	16*6(arg2), %xmm6
108	movdqu	16*7(arg2), %xmm7
109
110	pshufb	%xmm11, %xmm0
111	# XOR the initial_crc value
112	pxor	%xmm10, %xmm0
113	pshufb	%xmm11, %xmm1
114	pshufb	%xmm11, %xmm2
115	pshufb	%xmm11, %xmm3
116	pshufb	%xmm11, %xmm4
117	pshufb	%xmm11, %xmm5
118	pshufb	%xmm11, %xmm6
119	pshufb	%xmm11, %xmm7
120
121	movdqa	rk3(%rip), %xmm10	#xmm10 has rk3 and rk4
122					#imm value of pclmulqdq instruction
123					#will determine which constant to use
124
125	#################################################################
126	# we subtract 256 instead of 128 to save one instruction from the loop
127	sub	$256, arg3
128
129	# at this section of the code, there is 64*x+y (0<=y<64) bytes of
130	# buffer. The _fold_64_B_loop will fold 64B at a time
131	# until we have 64+y Bytes of buffer
132
133
134	# fold 64B at a time. This section of the code folds 4 xmm
135	# registers in parallel
136_fold_64_B_loop:
137
138	# update the buffer pointer
139	add	$128, arg2		#    buf += 64#
140
141	movdqu	16*0(arg2), %xmm9
142	movdqu	16*1(arg2), %xmm12
143	pshufb	%xmm11, %xmm9
144	pshufb	%xmm11, %xmm12
145	movdqa	%xmm0, %xmm8
146	movdqa	%xmm1, %xmm13
147	pclmulqdq	$0x0 , %xmm10, %xmm0
148	pclmulqdq	$0x11, %xmm10, %xmm8
149	pclmulqdq	$0x0 , %xmm10, %xmm1
150	pclmulqdq	$0x11, %xmm10, %xmm13
151	pxor	%xmm9 , %xmm0
152	xorps	%xmm8 , %xmm0
153	pxor	%xmm12, %xmm1
154	xorps	%xmm13, %xmm1
155
156	movdqu	16*2(arg2), %xmm9
157	movdqu	16*3(arg2), %xmm12
158	pshufb	%xmm11, %xmm9
159	pshufb	%xmm11, %xmm12
160	movdqa	%xmm2, %xmm8
161	movdqa	%xmm3, %xmm13
162	pclmulqdq	$0x0, %xmm10, %xmm2
163	pclmulqdq	$0x11, %xmm10, %xmm8
164	pclmulqdq	$0x0, %xmm10, %xmm3
165	pclmulqdq	$0x11, %xmm10, %xmm13
166	pxor	%xmm9 , %xmm2
167	xorps	%xmm8 , %xmm2
168	pxor	%xmm12, %xmm3
169	xorps	%xmm13, %xmm3
170
171	movdqu	16*4(arg2), %xmm9
172	movdqu	16*5(arg2), %xmm12
173	pshufb	%xmm11, %xmm9
174	pshufb	%xmm11, %xmm12
175	movdqa	%xmm4, %xmm8
176	movdqa	%xmm5, %xmm13
177	pclmulqdq	$0x0,  %xmm10, %xmm4
178	pclmulqdq	$0x11, %xmm10, %xmm8
179	pclmulqdq	$0x0,  %xmm10, %xmm5
180	pclmulqdq	$0x11, %xmm10, %xmm13
181	pxor	%xmm9 ,  %xmm4
182	xorps	%xmm8 ,  %xmm4
183	pxor	%xmm12,  %xmm5
184	xorps	%xmm13,  %xmm5
185
186	movdqu	16*6(arg2), %xmm9
187	movdqu	16*7(arg2), %xmm12
188	pshufb	%xmm11, %xmm9
189	pshufb	%xmm11, %xmm12
190	movdqa	%xmm6 , %xmm8
191	movdqa	%xmm7 , %xmm13
192	pclmulqdq	$0x0 , %xmm10, %xmm6
193	pclmulqdq	$0x11, %xmm10, %xmm8
194	pclmulqdq	$0x0 , %xmm10, %xmm7
195	pclmulqdq	$0x11, %xmm10, %xmm13
196	pxor	%xmm9 , %xmm6
197	xorps	%xmm8 , %xmm6
198	pxor	%xmm12, %xmm7
199	xorps	%xmm13, %xmm7
200
201	sub	$128, arg3
202
203	# check if there is another 64B in the buffer to be able to fold
204	jge	_fold_64_B_loop
205	##################################################################
206
207
208	add	$128, arg2
209	# at this point, the buffer pointer is pointing at the last y Bytes
210	# of the buffer the 64B of folded data is in 4 of the xmm
211	# registers: xmm0, xmm1, xmm2, xmm3
212
213
214	# fold the 8 xmm registers to 1 xmm register with different constants
215
216	movdqa	rk9(%rip), %xmm10
217	movdqa	%xmm0, %xmm8
218	pclmulqdq	$0x11, %xmm10, %xmm0
219	pclmulqdq	$0x0 , %xmm10, %xmm8
220	pxor	%xmm8, %xmm7
221	xorps	%xmm0, %xmm7
222
223	movdqa	rk11(%rip), %xmm10
224	movdqa	%xmm1, %xmm8
225	pclmulqdq	 $0x11, %xmm10, %xmm1
226	pclmulqdq	 $0x0 , %xmm10, %xmm8
227	pxor	%xmm8, %xmm7
228	xorps	%xmm1, %xmm7
229
230	movdqa	rk13(%rip), %xmm10
231	movdqa	%xmm2, %xmm8
232	pclmulqdq	 $0x11, %xmm10, %xmm2
233	pclmulqdq	 $0x0 , %xmm10, %xmm8
234	pxor	%xmm8, %xmm7
235	pxor	%xmm2, %xmm7
236
237	movdqa	rk15(%rip), %xmm10
238	movdqa	%xmm3, %xmm8
239	pclmulqdq	$0x11, %xmm10, %xmm3
240	pclmulqdq	$0x0 , %xmm10, %xmm8
241	pxor	%xmm8, %xmm7
242	xorps	%xmm3, %xmm7
243
244	movdqa	rk17(%rip), %xmm10
245	movdqa	%xmm4, %xmm8
246	pclmulqdq	$0x11, %xmm10, %xmm4
247	pclmulqdq	$0x0 , %xmm10, %xmm8
248	pxor	%xmm8, %xmm7
249	pxor	%xmm4, %xmm7
250
251	movdqa	rk19(%rip), %xmm10
252	movdqa	%xmm5, %xmm8
253	pclmulqdq	$0x11, %xmm10, %xmm5
254	pclmulqdq	$0x0 , %xmm10, %xmm8
255	pxor	%xmm8, %xmm7
256	xorps	%xmm5, %xmm7
257
258	movdqa	rk1(%rip), %xmm10	#xmm10 has rk1 and rk2
259					#imm value of pclmulqdq instruction
260					#will determine which constant to use
261	movdqa	%xmm6, %xmm8
262	pclmulqdq	$0x11, %xmm10, %xmm6
263	pclmulqdq	$0x0 , %xmm10, %xmm8
264	pxor	%xmm8, %xmm7
265	pxor	%xmm6, %xmm7
266
267
268	# instead of 64, we add 48 to the loop counter to save 1 instruction
269	# from the loop instead of a cmp instruction, we use the negative
270	# flag with the jl instruction
271	add	$128-16, arg3
272	jl	_final_reduction_for_128
273
274	# now we have 16+y bytes left to reduce. 16 Bytes is in register xmm7
275	# and the rest is in memory. We can fold 16 bytes at a time if y>=16
276	# continue folding 16B at a time
277
278_16B_reduction_loop:
279	movdqa	%xmm7, %xmm8
280	pclmulqdq	$0x11, %xmm10, %xmm7
281	pclmulqdq	$0x0 , %xmm10, %xmm8
282	pxor	%xmm8, %xmm7
283	movdqu	(arg2), %xmm0
284	pshufb	%xmm11, %xmm0
285	pxor	%xmm0 , %xmm7
286	add	$16, arg2
287	sub	$16, arg3
288	# instead of a cmp instruction, we utilize the flags with the
289	# jge instruction equivalent of: cmp arg3, 16-16
290	# check if there is any more 16B in the buffer to be able to fold
291	jge	_16B_reduction_loop
292
293	#now we have 16+z bytes left to reduce, where 0<= z < 16.
294	#first, we reduce the data in the xmm7 register
295
296
297_final_reduction_for_128:
298	# check if any more data to fold. If not, compute the CRC of
299	# the final 128 bits
300	add	$16, arg3
301	je	_128_done
302
303	# here we are getting data that is less than 16 bytes.
304	# since we know that there was data before the pointer, we can
305	# offset the input pointer before the actual point, to receive
306	# exactly 16 bytes. after that the registers need to be adjusted.
307_get_last_two_xmms:
308	movdqa	%xmm7, %xmm2
309
310	movdqu	-16(arg2, arg3), %xmm1
311	pshufb	%xmm11, %xmm1
312
313	# get rid of the extra data that was loaded before
314	# load the shift constant
315	lea	pshufb_shf_table+16(%rip), %rax
316	sub	arg3, %rax
317	movdqu	(%rax), %xmm0
318
319	# shift xmm2 to the left by arg3 bytes
320	pshufb	%xmm0, %xmm2
321
322	# shift xmm7 to the right by 16-arg3 bytes
323	pxor	mask1(%rip), %xmm0
324	pshufb	%xmm0, %xmm7
325	pblendvb	%xmm2, %xmm1	#xmm0 is implicit
326
327	# fold 16 Bytes
328	movdqa	%xmm1, %xmm2
329	movdqa	%xmm7, %xmm8
330	pclmulqdq	$0x11, %xmm10, %xmm7
331	pclmulqdq	$0x0 , %xmm10, %xmm8
332	pxor	%xmm8, %xmm7
333	pxor	%xmm2, %xmm7
334
335_128_done:
336	# compute crc of a 128-bit value
337	movdqa	rk5(%rip), %xmm10	# rk5 and rk6 in xmm10
338	movdqa	%xmm7, %xmm0
339
340	#64b fold
341	pclmulqdq	$0x1, %xmm10, %xmm7
342	pslldq	$8   ,  %xmm0
343	pxor	%xmm0,  %xmm7
344
345	#32b fold
346	movdqa	%xmm7, %xmm0
347
348	pand	mask2(%rip), %xmm0
349
350	psrldq	$12, %xmm7
351	pclmulqdq	$0x10, %xmm10, %xmm7
352	pxor	%xmm0, %xmm7
353
354	#barrett reduction
355_barrett:
356	movdqa	rk7(%rip), %xmm10	# rk7 and rk8 in xmm10
357	movdqa	%xmm7, %xmm0
358	pclmulqdq	$0x01, %xmm10, %xmm7
359	pslldq	$4, %xmm7
360	pclmulqdq	$0x11, %xmm10, %xmm7
361
362	pslldq	$4, %xmm7
363	pxor	%xmm0, %xmm7
364	pextrd	$1, %xmm7, %eax
365
366_cleanup:
367	# scale the result back to 16 bits
368	shr	$16, %eax
369	mov     %rcx, %rsp
370	ret
371
372########################################################################
373
374.align 16
375_less_than_128:
376
377	# check if there is enough buffer to be able to fold 16B at a time
378	cmp	$32, arg3
379	jl	_less_than_32
380	movdqa  SHUF_MASK(%rip), %xmm11
381
382	# now if there is, load the constants
383	movdqa	rk1(%rip), %xmm10	# rk1 and rk2 in xmm10
384
385	movd	arg1_low32, %xmm0	# get the initial crc value
386	pslldq	$12, %xmm0	# align it to its correct place
387	movdqu	(arg2), %xmm7	# load the plaintext
388	pshufb	%xmm11, %xmm7	# byte-reflect the plaintext
389	pxor	%xmm0, %xmm7
390
391
392	# update the buffer pointer
393	add	$16, arg2
394
395	# update the counter. subtract 32 instead of 16 to save one
396	# instruction from the loop
397	sub	$32, arg3
398
399	jmp	_16B_reduction_loop
400
401
402.align 16
403_less_than_32:
404	# mov initial crc to the return value. this is necessary for
405	# zero-length buffers.
406	mov	arg1_low32, %eax
407	test	arg3, arg3
408	je	_cleanup
409
410	movdqa  SHUF_MASK(%rip), %xmm11
411
412	movd	arg1_low32, %xmm0	# get the initial crc value
413	pslldq	$12, %xmm0	# align it to its correct place
414
415	cmp	$16, arg3
416	je	_exact_16_left
417	jl	_less_than_16_left
418
419	movdqu	(arg2), %xmm7	# load the plaintext
420	pshufb	%xmm11, %xmm7	# byte-reflect the plaintext
421	pxor	%xmm0 , %xmm7	# xor the initial crc value
422	add	$16, arg2
423	sub	$16, arg3
424	movdqa	rk1(%rip), %xmm10	# rk1 and rk2 in xmm10
425	jmp	_get_last_two_xmms
426
427
428.align 16
429_less_than_16_left:
430	# use stack space to load data less than 16 bytes, zero-out
431	# the 16B in memory first.
432
433	pxor	%xmm1, %xmm1
434	mov	%rsp, %r11
435	movdqa	%xmm1, (%r11)
436
437	cmp	$4, arg3
438	jl	_only_less_than_4
439
440	# backup the counter value
441	mov	arg3, %r9
442	cmp	$8, arg3
443	jl	_less_than_8_left
444
445	# load 8 Bytes
446	mov	(arg2), %rax
447	mov	%rax, (%r11)
448	add	$8, %r11
449	sub	$8, arg3
450	add	$8, arg2
451_less_than_8_left:
452
453	cmp	$4, arg3
454	jl	_less_than_4_left
455
456	# load 4 Bytes
457	mov	(arg2), %eax
458	mov	%eax, (%r11)
459	add	$4, %r11
460	sub	$4, arg3
461	add	$4, arg2
462_less_than_4_left:
463
464	cmp	$2, arg3
465	jl	_less_than_2_left
466
467	# load 2 Bytes
468	mov	(arg2), %ax
469	mov	%ax, (%r11)
470	add	$2, %r11
471	sub	$2, arg3
472	add	$2, arg2
473_less_than_2_left:
474	cmp     $1, arg3
475        jl      _zero_left
476
477	# load 1 Byte
478	mov	(arg2), %al
479	mov	%al, (%r11)
480_zero_left:
481	movdqa	(%rsp), %xmm7
482	pshufb	%xmm11, %xmm7
483	pxor	%xmm0 , %xmm7	# xor the initial crc value
484
485	# shl r9, 4
486	lea	pshufb_shf_table+16(%rip), %rax
487	sub	%r9, %rax
488	movdqu	(%rax), %xmm0
489	pxor	mask1(%rip), %xmm0
490
491	pshufb	%xmm0, %xmm7
492	jmp	_128_done
493
494.align 16
495_exact_16_left:
496	movdqu	(arg2), %xmm7
497	pshufb	%xmm11, %xmm7
498	pxor	%xmm0 , %xmm7   # xor the initial crc value
499
500	jmp	_128_done
501
502_only_less_than_4:
503	cmp	$3, arg3
504	jl	_only_less_than_3
505
506	# load 3 Bytes
507	mov	(arg2), %al
508	mov	%al, (%r11)
509
510	mov	1(arg2), %al
511	mov	%al, 1(%r11)
512
513	mov	2(arg2), %al
514	mov	%al, 2(%r11)
515
516	movdqa	 (%rsp), %xmm7
517	pshufb	 %xmm11, %xmm7
518	pxor	 %xmm0 , %xmm7  # xor the initial crc value
519
520	psrldq	$5, %xmm7
521
522	jmp	_barrett
523_only_less_than_3:
524	cmp	$2, arg3
525	jl	_only_less_than_2
526
527	# load 2 Bytes
528	mov	(arg2), %al
529	mov	%al, (%r11)
530
531	mov	1(arg2), %al
532	mov	%al, 1(%r11)
533
534	movdqa	(%rsp), %xmm7
535	pshufb	%xmm11, %xmm7
536	pxor	%xmm0 , %xmm7   # xor the initial crc value
537
538	psrldq	$6, %xmm7
539
540	jmp	_barrett
541_only_less_than_2:
542
543	# load 1 Byte
544	mov	(arg2), %al
545	mov	%al, (%r11)
546
547	movdqa	(%rsp), %xmm7
548	pshufb	%xmm11, %xmm7
549	pxor	%xmm0 , %xmm7   # xor the initial crc value
550
551	psrldq	$7, %xmm7
552
553	jmp	_barrett
554
555ENDPROC(crc_t10dif_pcl)
556
557.data
558
559# precomputed constants
560# these constants are precomputed from the poly:
561# 0x8bb70000 (0x8bb7 scaled to 32 bits)
562.align 16
563# Q = 0x18BB70000
564# rk1 = 2^(32*3) mod Q << 32
565# rk2 = 2^(32*5) mod Q << 32
566# rk3 = 2^(32*15) mod Q << 32
567# rk4 = 2^(32*17) mod Q << 32
568# rk5 = 2^(32*3) mod Q << 32
569# rk6 = 2^(32*2) mod Q << 32
570# rk7 = floor(2^64/Q)
571# rk8 = Q
572rk1:
573.quad 0x2d56000000000000
574rk2:
575.quad 0x06df000000000000
576rk3:
577.quad 0x9d9d000000000000
578rk4:
579.quad 0x7cf5000000000000
580rk5:
581.quad 0x2d56000000000000
582rk6:
583.quad 0x1368000000000000
584rk7:
585.quad 0x00000001f65a57f8
586rk8:
587.quad 0x000000018bb70000
588
589rk9:
590.quad 0xceae000000000000
591rk10:
592.quad 0xbfd6000000000000
593rk11:
594.quad 0x1e16000000000000
595rk12:
596.quad 0x713c000000000000
597rk13:
598.quad 0xf7f9000000000000
599rk14:
600.quad 0x80a6000000000000
601rk15:
602.quad 0x044c000000000000
603rk16:
604.quad 0xe658000000000000
605rk17:
606.quad 0xad18000000000000
607rk18:
608.quad 0xa497000000000000
609rk19:
610.quad 0x6ee3000000000000
611rk20:
612.quad 0xe7b5000000000000
613
614
615
616mask1:
617.octa 0x80808080808080808080808080808080
618mask2:
619.octa 0x00000000FFFFFFFFFFFFFFFFFFFFFFFF
620
621SHUF_MASK:
622.octa 0x000102030405060708090A0B0C0D0E0F
623
624pshufb_shf_table:
625# use these values for shift constants for the pshufb instruction
626# different alignments result in values as shown:
627#	DDQ 0x008f8e8d8c8b8a898887868584838281 # shl 15 (16-1) / shr1
628#	DDQ 0x01008f8e8d8c8b8a8988878685848382 # shl 14 (16-3) / shr2
629#	DDQ 0x0201008f8e8d8c8b8a89888786858483 # shl 13 (16-4) / shr3
630#	DDQ 0x030201008f8e8d8c8b8a898887868584 # shl 12 (16-4) / shr4
631#	DDQ 0x04030201008f8e8d8c8b8a8988878685 # shl 11 (16-5) / shr5
632#	DDQ 0x0504030201008f8e8d8c8b8a89888786 # shl 10 (16-6) / shr6
633#	DDQ 0x060504030201008f8e8d8c8b8a898887 # shl 9  (16-7) / shr7
634#	DDQ 0x07060504030201008f8e8d8c8b8a8988 # shl 8  (16-8) / shr8
635#	DDQ 0x0807060504030201008f8e8d8c8b8a89 # shl 7  (16-9) / shr9
636#	DDQ 0x090807060504030201008f8e8d8c8b8a # shl 6  (16-10) / shr10
637#	DDQ 0x0a090807060504030201008f8e8d8c8b # shl 5  (16-11) / shr11
638#	DDQ 0x0b0a090807060504030201008f8e8d8c # shl 4  (16-12) / shr12
639#	DDQ 0x0c0b0a090807060504030201008f8e8d # shl 3  (16-13) / shr13
640#	DDQ 0x0d0c0b0a090807060504030201008f8e # shl 2  (16-14) / shr14
641#	DDQ 0x0e0d0c0b0a090807060504030201008f # shl 1  (16-15) / shr15
642.octa 0x8f8e8d8c8b8a89888786858483828100
643.octa 0x000e0d0c0b0a09080706050403020100
644