summaryrefslogtreecommitdiffstats
path: root/src/common/crc32c_ppc.c
blob: 52fd1c4eef2e2259929cae892d4d125ab8800d18 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/* Copyright (C) 2017 International Business Machines Corp.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */
#define CRC_TABLE
#define FAST_ZERO_TABLE

#include "acconfig.h"
#include "include/int_types.h"
#include "crc32c_ppc_constants.h"
#include "reverse.h"

#include <stdlib.h>
#include <strings.h>

#define VMX_ALIGN	16
#define VMX_ALIGN_MASK	(VMX_ALIGN-1)

#ifdef HAVE_PPC64LE
#ifdef REFLECT
static unsigned int crc32_align(unsigned int crc, unsigned char const *p,
                                unsigned long len)
{
  while (len--)
    crc = crc_table[(crc ^ *p++) & 0xff] ^ (crc >> 8);
  return crc;
}
#else
static unsigned int crc32_align(unsigned int crc, unsigned char const *p,
                                unsigned long len)
{
  while (len--)
    crc = crc_table[((crc >> 24) ^ *p++) & 0xff] ^ (crc << 8);
  return crc;
}
#endif

static inline unsigned long polynomial_multiply(unsigned int a, unsigned int b) {
        vector unsigned int va = {a, 0, 0, 0};
        vector unsigned int vb = {b, 0, 0, 0};
        vector unsigned long vt;

        __asm__("vpmsumw %0,%1,%2" : "=v"(vt) : "v"(va), "v"(vb));

        return vt[0];
}

unsigned int barrett_reduction(unsigned long val);

static inline unsigned int gf_multiply(unsigned int a, unsigned int b) {
        return barrett_reduction(polynomial_multiply(a, b));
}

unsigned int append_zeros(unsigned int crc, unsigned long length) {
        unsigned long i = 0;

        while (length) {
                if (length & 1) {
                        crc = gf_multiply(crc, crc_zero[i]);
                }
                i++;
                length /= 2;
        }

        return crc;
}


unsigned int __crc32_vpmsum(unsigned int crc, unsigned char const *p,
                            unsigned long len);

static uint32_t crc32_vpmsum(uint32_t crc, unsigned char const *data,
                             unsigned len)
{
  unsigned int prealign;
  unsigned int tail;

#ifdef CRC_XOR
  crc ^= 0xffffffff;
#endif

  if (len < VMX_ALIGN + VMX_ALIGN_MASK) {
    crc = crc32_align(crc, data, (unsigned long)len);
    goto out;
  }

  if ((unsigned long)data & VMX_ALIGN_MASK) {
    prealign = VMX_ALIGN - ((unsigned long)data & VMX_ALIGN_MASK);
    crc = crc32_align(crc, data, prealign);
    len -= prealign;
    data += prealign;
  }

  crc = __crc32_vpmsum(crc, data, (unsigned long)len & ~VMX_ALIGN_MASK);

  tail = len & VMX_ALIGN_MASK;
  if (tail) {
    data += len & ~VMX_ALIGN_MASK;
    crc = crc32_align(crc, data, tail);
  }

out:
#ifdef CRC_XOR
  crc ^= 0xffffffff;
#endif

  return crc;
}

/* This wrapper function works around the fact that crc32_vpmsum 
 * does not gracefully handle the case where the data pointer is NULL.
 */
uint32_t ceph_crc32c_ppc(uint32_t crc, unsigned char const *data, unsigned len)
{
  if (!data) {
    /* Handle the NULL buffer case. */
#ifdef REFLECT
    crc = reverse_bits(crc);
#endif

    crc = append_zeros(crc, len);

#ifdef REFLECT
    crc = reverse_bits(crc);
#endif
  } else {
    /* Handle the valid buffer case. */
    crc = crc32_vpmsum(crc, data, (unsigned long)len);
  }
  return crc;
}

#else /* HAVE_PPC64LE */

/* This symbol has to exist on non-ppc architectures (and on legacy
 * ppc systems using power7 or below) in order to compile properly
 * there, even though it won't be called.
 */
uint32_t ceph_crc32c_ppc(uint32_t crc, unsigned char const *data, unsigned len)
{
  return 0;
}

#endif /* HAVE_PPC64LE */