@@ -212,10 +212,41 @@ static bool avx2_usable(void)
static void sha1_apply_transform_avx2(u32 *digest, const char *data,
unsigned int rounds)
{
+ const char *last;
+ unsigned int rounds_avx2;
+
/* Select the optimal transform based on data block size */
- if (rounds >= SHA1_AVX2_BLOCK_OPTSIZE)
- sha1_transform_avx2(digest, data, rounds);
- else
+ if (rounds < SHA1_AVX2_BLOCK_OPTSIZE)
+ goto avx;
+
+ /*
+ * sha1_transform_avx2() can read ahead couple blocks, which
+ * can cause problems if it crosses page boundary and next
+ * page doesn't exist. It operates on even number of blocks.
+ * Code below checks for worst case, where it can access
+ * up to 3 consecutive blocks after data end. In that case
+ * sha1_transform_avx2() is passed 3 blocks less and rest
+ * of data is handled by sha1_transform_avx().
+ *
+ * +----------+---------+---------+---------+
+ * 2x SHA1_BLOCK_SIZE | 2*SHA1_BLOCK_SIZE
+ * +----------+---------+---------+---------+
+ * ^ data end
+ */
+ last = data + (rounds + 3) * SHA1_BLOCK_SIZE - 1;
+ if (offset_in_page(last) >= 3 * SHA1_BLOCK_SIZE) {
+ rounds_avx2 = rounds;
+ } else {
+ rounds_avx2 = rounds - 3;
+ if (rounds_avx2 < SHA1_AVX2_BLOCK_OPTSIZE)
+ goto avx;
+ }
+
+ sha1_transform_avx2(digest, data, rounds_avx2);
+ data += SHA1_BLOCK_SIZE * rounds_avx2;
+ rounds -= rounds_avx2;
+avx:
+ if (rounds)
sha1_transform_avx(digest, data, rounds);
}