| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Accelerated CRC32 implementation with Zbc extension.
- *
- * Copyright (C) 2024 Intel Corporation
- */
- #include <asm/hwcap.h>
- #include <asm/alternative-macros.h>
- #include <asm/byteorder.h>
- #include <linux/types.h>
- #include <linux/minmax.h>
- #include <linux/crc32poly.h>
- #include <linux/crc32.h>
- #include <linux/byteorder/generic.h>
- /*
- * Refer to https://www.corsix.org/content/barrett-reduction-polynomials for
- * better understanding of how this math works.
- *
- * let "+" denotes polynomial add (XOR)
- * let "-" denotes polynomial sub (XOR)
- * let "*" denotes polynomial multiplication
- * let "/" denotes polynomial floor division
- * let "S" denotes source data, XLEN bit wide
- * let "P" denotes CRC32 polynomial
- * let "T" denotes 2^(XLEN+32)
- * let "QT" denotes quotient of T/P, with the bit for 2^XLEN being implicit
- *
- * crc32(S, P)
- * => S * (2^32) - S * (2^32) / P * P
- * => lowest 32 bits of: S * (2^32) / P * P
- * => lowest 32 bits of: S * (2^32) * (T / P) / T * P
- * => lowest 32 bits of: S * (2^32) * quotient / T * P
- * => lowest 32 bits of: S * quotient / 2^XLEN * P
- * => lowest 32 bits of: (clmul_high_part(S, QT) + S) * P
- * => clmul_low_part(clmul_high_part(S, QT) + S, P)
- *
- * In terms of below implementations, the BE case is more intuitive, since the
- * higher order bit sits at more significant position.
- */
- #if __riscv_xlen == 64
- /* Slide by XLEN bits per iteration */
- # define STEP_ORDER 3
- /* Each below polynomial quotient has an implicit bit for 2^XLEN */
- /* Polynomial quotient of (2^(XLEN+32))/CRC32_POLY, in LE format */
- # define CRC32_POLY_QT_LE 0x5a72d812fb808b20
- /* Polynomial quotient of (2^(XLEN+32))/CRC32C_POLY, in LE format */
- # define CRC32C_POLY_QT_LE 0xa434f61c6f5389f8
- /* Polynomial quotient of (2^(XLEN+32))/CRC32_POLY, in BE format, it should be
- * the same as the bit-reversed version of CRC32_POLY_QT_LE
- */
- # define CRC32_POLY_QT_BE 0x04d101df481b4e5a
- static inline u64 crc32_le_prep(u32 crc, unsigned long const *ptr)
- {
- return (u64)crc ^ (__force u64)__cpu_to_le64(*ptr);
- }
- static inline u32 crc32_le_zbc(unsigned long s, u32 poly, unsigned long poly_qt)
- {
- u32 crc;
- /* We don't have a "clmulrh" insn, so use clmul + slli instead. */
- asm volatile (".option push\n"
- ".option arch,+zbc\n"
- "clmul %0, %1, %2\n"
- "slli %0, %0, 1\n"
- "xor %0, %0, %1\n"
- "clmulr %0, %0, %3\n"
- "srli %0, %0, 32\n"
- ".option pop\n"
- : "=&r" (crc)
- : "r" (s),
- "r" (poly_qt),
- "r" ((u64)poly << 32)
- :);
- return crc;
- }
- static inline u64 crc32_be_prep(u32 crc, unsigned long const *ptr)
- {
- return ((u64)crc << 32) ^ (__force u64)__cpu_to_be64(*ptr);
- }
- #elif __riscv_xlen == 32
- # define STEP_ORDER 2
- /* Each quotient should match the upper half of its analog in RV64 */
- # define CRC32_POLY_QT_LE 0xfb808b20
- # define CRC32C_POLY_QT_LE 0x6f5389f8
- # define CRC32_POLY_QT_BE 0x04d101df
- static inline u32 crc32_le_prep(u32 crc, unsigned long const *ptr)
- {
- return crc ^ (__force u32)__cpu_to_le32(*ptr);
- }
- static inline u32 crc32_le_zbc(unsigned long s, u32 poly, unsigned long poly_qt)
- {
- u32 crc;
- /* We don't have a "clmulrh" insn, so use clmul + slli instead. */
- asm volatile (".option push\n"
- ".option arch,+zbc\n"
- "clmul %0, %1, %2\n"
- "slli %0, %0, 1\n"
- "xor %0, %0, %1\n"
- "clmulr %0, %0, %3\n"
- ".option pop\n"
- : "=&r" (crc)
- : "r" (s),
- "r" (poly_qt),
- "r" (poly)
- :);
- return crc;
- }
- static inline u32 crc32_be_prep(u32 crc, unsigned long const *ptr)
- {
- return crc ^ (__force u32)__cpu_to_be32(*ptr);
- }
- #else
- # error "Unexpected __riscv_xlen"
- #endif
- static inline u32 crc32_be_zbc(unsigned long s)
- {
- u32 crc;
- asm volatile (".option push\n"
- ".option arch,+zbc\n"
- "clmulh %0, %1, %2\n"
- "xor %0, %0, %1\n"
- "clmul %0, %0, %3\n"
- ".option pop\n"
- : "=&r" (crc)
- : "r" (s),
- "r" (CRC32_POLY_QT_BE),
- "r" (CRC32_POLY_BE)
- :);
- return crc;
- }
- #define STEP (1 << STEP_ORDER)
- #define OFFSET_MASK (STEP - 1)
- typedef u32 (*fallback)(u32 crc, unsigned char const *p, size_t len);
- static inline u32 crc32_le_unaligned(u32 crc, unsigned char const *p,
- size_t len, u32 poly,
- unsigned long poly_qt)
- {
- size_t bits = len * 8;
- unsigned long s = 0;
- u32 crc_low = 0;
- for (int i = 0; i < len; i++)
- s = ((unsigned long)*p++ << (__riscv_xlen - 8)) | (s >> 8);
- s ^= (unsigned long)crc << (__riscv_xlen - bits);
- if (__riscv_xlen == 32 || len < sizeof(u32))
- crc_low = crc >> bits;
- crc = crc32_le_zbc(s, poly, poly_qt);
- crc ^= crc_low;
- return crc;
- }
- static inline u32 __pure crc32_le_generic(u32 crc, unsigned char const *p,
- size_t len, u32 poly,
- unsigned long poly_qt,
- fallback crc_fb)
- {
- size_t offset, head_len, tail_len;
- unsigned long const *p_ul;
- unsigned long s;
- asm goto(ALTERNATIVE("j %l[legacy]", "nop", 0,
- RISCV_ISA_EXT_ZBC, 1)
- : : : : legacy);
- /* Handle the unaligned head. */
- offset = (unsigned long)p & OFFSET_MASK;
- if (offset && len) {
- head_len = min(STEP - offset, len);
- crc = crc32_le_unaligned(crc, p, head_len, poly, poly_qt);
- p += head_len;
- len -= head_len;
- }
- tail_len = len & OFFSET_MASK;
- len = len >> STEP_ORDER;
- p_ul = (unsigned long const *)p;
- for (int i = 0; i < len; i++) {
- s = crc32_le_prep(crc, p_ul);
- crc = crc32_le_zbc(s, poly, poly_qt);
- p_ul++;
- }
- /* Handle the tail bytes. */
- p = (unsigned char const *)p_ul;
- if (tail_len)
- crc = crc32_le_unaligned(crc, p, tail_len, poly, poly_qt);
- return crc;
- legacy:
- return crc_fb(crc, p, len);
- }
- u32 __pure crc32_le(u32 crc, unsigned char const *p, size_t len)
- {
- return crc32_le_generic(crc, p, len, CRC32_POLY_LE, CRC32_POLY_QT_LE,
- crc32_le_base);
- }
- u32 __pure __crc32c_le(u32 crc, unsigned char const *p, size_t len)
- {
- return crc32_le_generic(crc, p, len, CRC32C_POLY_LE,
- CRC32C_POLY_QT_LE, __crc32c_le_base);
- }
- static inline u32 crc32_be_unaligned(u32 crc, unsigned char const *p,
- size_t len)
- {
- size_t bits = len * 8;
- unsigned long s = 0;
- u32 crc_low = 0;
- s = 0;
- for (int i = 0; i < len; i++)
- s = *p++ | (s << 8);
- if (__riscv_xlen == 32 || len < sizeof(u32)) {
- s ^= crc >> (32 - bits);
- crc_low = crc << bits;
- } else {
- s ^= (unsigned long)crc << (bits - 32);
- }
- crc = crc32_be_zbc(s);
- crc ^= crc_low;
- return crc;
- }
- u32 __pure crc32_be(u32 crc, unsigned char const *p, size_t len)
- {
- size_t offset, head_len, tail_len;
- unsigned long const *p_ul;
- unsigned long s;
- asm goto(ALTERNATIVE("j %l[legacy]", "nop", 0,
- RISCV_ISA_EXT_ZBC, 1)
- : : : : legacy);
- /* Handle the unaligned head. */
- offset = (unsigned long)p & OFFSET_MASK;
- if (offset && len) {
- head_len = min(STEP - offset, len);
- crc = crc32_be_unaligned(crc, p, head_len);
- p += head_len;
- len -= head_len;
- }
- tail_len = len & OFFSET_MASK;
- len = len >> STEP_ORDER;
- p_ul = (unsigned long const *)p;
- for (int i = 0; i < len; i++) {
- s = crc32_be_prep(crc, p_ul);
- crc = crc32_be_zbc(s);
- p_ul++;
- }
- /* Handle the tail bytes. */
- p = (unsigned char const *)p_ul;
- if (tail_len)
- crc = crc32_be_unaligned(crc, p, tail_len);
- return crc;
- legacy:
- return crc32_be_base(crc, p, len);
- }
|