Add support for using the Armv8-A CRC32 feature

The Armv8-A architecture specifies a CRC32 hardware feature, it can be
used through an intrinsic specified by ACLE. However, the HW feature is
mandatory only starting from v8.1, so for v8.0 CPUs we have to check its
availability. This commit implements a mechanism to do this check for
both Linux and SP environments, and override the default software CRC32
implementation when the HW feature is present.

Signed-off-by: Balint Dobszay <balint.dobszay@arm.com>
Change-Id: Ia15fe1db9890b8aa076deec44c71639a4382cb35
diff --git a/components/app/fwu-tool/component.cmake b/components/app/fwu-tool/component.cmake
index 73a8524..90a07c2 100644
--- a/components/app/fwu-tool/component.cmake
+++ b/components/app/fwu-tool/component.cmake
@@ -19,7 +19,7 @@
 add_components(TARGET ${TGT}
 	BASE_DIR ${TS_ROOT}
 	COMPONENTS
-		"components/common/crc32/native"
+		"components/common/crc32"
 		"components/common/trace"
 		"components/common/utils"
 )
diff --git a/components/common/crc32/tf-a/component.cmake b/components/common/crc32/component.cmake
similarity index 62%
rename from components/common/crc32/tf-a/component.cmake
rename to components/common/crc32/component.cmake
index 39636d5..4628b7f 100644
--- a/components/common/crc32/tf-a/component.cmake
+++ b/components/common/crc32/component.cmake
@@ -10,5 +10,16 @@
 
 target_sources(${TGT} PRIVATE
 	"${CMAKE_CURRENT_LIST_DIR}/crc32.c"
-	"${TFA_SOURCE_DIR}/common/tf_crc32.c"
+)
+
+if (TS_ENV STREQUAL "arm-linux")
+	target_sources(${TGT} PRIVATE
+		"${CMAKE_CURRENT_LIST_DIR}/crc32_linux.c"
 	)
+endif()
+
+if ((TS_ENV STREQUAL "opteesp") OR (TS_ENV STREQUAL "sp"))
+	target_sources(${TGT} PRIVATE
+		"${CMAKE_CURRENT_LIST_DIR}/crc32_sp.c"
+	)
+endif()
diff --git a/components/common/crc32/native/crc32.c b/components/common/crc32/crc32.c
similarity index 79%
rename from components/common/crc32/native/crc32.c
rename to components/common/crc32/crc32.c
index 705db65..8c34eb1 100644
--- a/components/common/crc32/native/crc32.c
+++ b/components/common/crc32/crc32.c
@@ -8,10 +8,15 @@
  * https://web.mit.edu/freebsd/head/sys/libkern/crc32.c
  */
 
-#include <common/crc32/crc32.h>
+#ifdef __aarch64__
+#include <arm_acle.h>
+#endif
+#include <stddef.h>
+#include <stdint.h>
 
-/* For compatability with TF-A components */
-uint32_t tf_crc32(uint32_t crc, const unsigned char *buf, size_t size);
+#include "crc32.h"
+#include "crc32_discovery.h"
+#include "trace.h"
 
 static const uint32_t crc32_tab[] = {
 	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
@@ -59,18 +64,53 @@
 	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
 };
 
-uint32_t crc32(uint32_t crc, const unsigned char *buf, size_t size)
+static uint32_t crc32_sw(uint32_t crc_prev, const uint8_t *buf, size_t size)
 {
-	uint32_t calc_crc = ~crc;
-	const uint8_t *local_buf = buf;
-	size_t local_size = size;
+	uint32_t crc = ~crc_prev;
 
-	while (local_size--)
-		calc_crc = crc32_tab[(calc_crc ^ *local_buf++) & 0xFF] ^ (calc_crc >> 8);
+	if (!buf)
+		return 0;
 
-	return calc_crc ^ ~0U;
+	while (size > 0) {
+		crc = crc32_tab[(crc ^ *buf) & 0xFF] ^ (crc >> 8);
+		buf++;
+		size--;
+	}
+
+	return ~crc;
 }
 
+#if __ARM_FEATURE_CRC32
+static uint32_t crc32_armv8a(uint32_t crc_prev, const uint8_t *buf, size_t size)
+{
+	uint32_t crc = ~crc_prev;
+
+	if (!buf)
+		return 0;
+
+	while (size > 0) {
+		crc = __crc32b(crc, *buf);
+		buf++;
+		size--;
+	}
+
+	return ~crc;
+}
+#endif
+
+uint32_t (*crc32)(uint32_t crc_prev, const uint8_t *buf, size_t size) = crc32_sw;
+
+void crc32_init(void)
+{
+#if __ARM_FEATURE_CRC32
+	if (crc32_armv8a_hw_available()) {
+		DMSG("Using crc32_armv8a CRC32 implementation");
+		crc32 = crc32_armv8a;
+	}
+#endif
+}
+
+/* For compatability with TF-A components */
 uint32_t tf_crc32(uint32_t crc, const unsigned char *buf, size_t size)
 {
 	return crc32(crc, buf, size);
diff --git a/components/common/crc32/crc32.h b/components/common/crc32/crc32.h
index a04e380..ae185cc 100644
--- a/components/common/crc32/crc32.h
+++ b/components/common/crc32/crc32.h
@@ -15,15 +15,20 @@
 #endif
 
 /**
+ * \brief Initialize CRC32 to use HW acceleration if available
+ */
+void crc32_init(void);
+
+/**
  * \brief Calculate a CRC32 over the provided data
  *
- * \param[in]  	crc		    The starting CRC for previous data
- * \param[in]  	buf  		The buffer to calculate the CRC over
- * \param[in]  	size		Number of bytes in the buffer
+ * \param[in]	crc_prev	The starting CRC for previous data
+ * \param[in]	buf		The buffer to calculate the CRC over
+ * \param[in]	size		Number of bytes in the buffer
  *
  * \return	The calculated CRC32
  */
-uint32_t crc32(uint32_t crc, const unsigned char *buf, size_t size);
+extern uint32_t (*crc32)(uint32_t crc_prev, const uint8_t *buf, size_t size);
 
 #ifdef __cplusplus
 }
diff --git a/components/common/crc32/crc32_discovery.h b/components/common/crc32/crc32_discovery.h
new file mode 100644
index 0000000..77a22d1
--- /dev/null
+++ b/components/common/crc32/crc32_discovery.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef CRC32_DISCOVERY_H
+#define CRC32_DISCOVERY_H
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bool crc32_armv8a_hw_available(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CRC32_DISCOVERY_H */
diff --git a/components/common/crc32/crc32_linux.c b/components/common/crc32/crc32_linux.c
new file mode 100644
index 0000000..d6dd8b6
--- /dev/null
+++ b/components/common/crc32/crc32_linux.c
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdbool.h>
+#include <sys/auxv.h>
+
+#include "crc32_discovery.h"
+
+bool crc32_armv8a_hw_available(void)
+{
+	unsigned long hwcaps = getauxval(AT_HWCAP);
+	return (hwcaps & HWCAP_CRC32) == HWCAP_CRC32;
+}
diff --git a/components/common/crc32/crc32_sp.c b/components/common/crc32/crc32_sp.c
new file mode 100644
index 0000000..9e2db8e
--- /dev/null
+++ b/components/common/crc32/crc32_sp.c
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "config/interface/config_store.h"
+#include "crc32_discovery.h"
+
+bool crc32_armv8a_hw_available(void)
+{
+	uint32_t value = 0;
+
+	if (!config_store_query(CONFIG_CLASSIFIER_HW_FEATURE, "crc32", 0, &value, sizeof(value)))
+		return false;
+
+	return value != 0;
+}
diff --git a/components/common/crc32/native/component.cmake b/components/common/crc32/native/component.cmake
deleted file mode 100644
index 9bbf298..0000000
--- a/components/common/crc32/native/component.cmake
+++ /dev/null
@@ -1,13 +0,0 @@
-#-------------------------------------------------------------------------------
-# Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
-#
-# SPDX-License-Identifier: BSD-3-Clause
-#
-#-------------------------------------------------------------------------------
-if (NOT DEFINED TGT)
-	message(FATAL_ERROR "mandatory parameter TGT is not defined.")
-endif()
-
-target_sources(${TGT} PRIVATE
-	"${CMAKE_CURRENT_LIST_DIR}/crc32.c"
-	)
diff --git a/components/common/crc32/test/crc32_test.cpp b/components/common/crc32/test/crc32_test.cpp
index a21286f..5e1ea8d 100644
--- a/components/common/crc32/test/crc32_test.cpp
+++ b/components/common/crc32/test/crc32_test.cpp
@@ -9,7 +9,10 @@
 
 TEST_GROUP(Crc32Tests)
 {
-
+	TEST_SETUP()
+	{
+		crc32_init();
+	}
 };
 
 /*
diff --git a/components/common/crc32/tf-a/crc32.c b/components/common/crc32/tf-a/crc32.c
deleted file mode 100644
index abc0f8a..0000000
--- a/components/common/crc32/tf-a/crc32.c
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright (c) 2022, Arm Limited. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- *
- */
-
-#include <common/crc32/crc32.h>
-#include <common/tf_crc32.h>
-
-
-uint32_t crc32(uint32_t crc, const unsigned char *buf, size_t size)
-{
-	return tf_crc32(crc, buf, size);
-}
diff --git a/deployments/block-storage/env/commonsp/block_storage_sp.c b/deployments/block-storage/env/commonsp/block_storage_sp.c
index ccefaaa..012250c 100644
--- a/deployments/block-storage/env/commonsp/block_storage_sp.c
+++ b/deployments/block-storage/env/commonsp/block_storage_sp.c
@@ -3,6 +3,7 @@
  * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
  */
 
+#include "common/crc32/crc32.h"
 #include "rpc/ffarpc/endpoint/ffarpc_call_ep.h"
 #include "protocols/rpc/common/packed-c/status.h"
 #include "config/ramstore/config_ramstore.h"
@@ -43,6 +44,8 @@
 		goto fatal_error;
 	}
 
+	crc32_init();
+
 	/* Initialise the service provider and backend block store */
 	backend = block_store_factory_create();
 	if (!backend) {
diff --git a/deployments/block-storage/env/commonsp/block_storage_sp.cmake b/deployments/block-storage/env/commonsp/block_storage_sp.cmake
index 0dea2a8..d55474c 100644
--- a/deployments/block-storage/env/commonsp/block_storage_sp.cmake
+++ b/deployments/block-storage/env/commonsp/block_storage_sp.cmake
@@ -14,6 +14,7 @@
 add_components(TARGET "block-storage"
 	BASE_DIR ${TS_ROOT}
 	COMPONENTS
+		"components/common/crc32"
 		"components/common/fdt"
 		"components/common/trace"
 		"components/common/utils"
diff --git a/deployments/block-storage/infra/semihosted.cmake b/deployments/block-storage/infra/semihosted.cmake
index ec10a0e..e9637ad 100644
--- a/deployments/block-storage/infra/semihosted.cmake
+++ b/deployments/block-storage/infra/semihosted.cmake
@@ -25,11 +25,10 @@
 		"components/media/volume/index"
 		"components/media/volume/base_io_dev"
 		"components/media/volume/block_volume"
-		"components/common/crc32/native"
 )
 
 #-------------------------------------------------------------------------------
 #  This infrastructure depends on platform specific drivers
 #
 #-------------------------------------------------------------------------------
-add_platform(TARGET "block-storage")
\ No newline at end of file
+add_platform(TARGET "block-storage")
diff --git a/deployments/component-test/component-test.cmake b/deployments/component-test/component-test.cmake
index 88e28b1..2724ccf 100644
--- a/deployments/component-test/component-test.cmake
+++ b/deployments/component-test/component-test.cmake
@@ -36,7 +36,7 @@
 		"components/common/trace"
 		"components/common/endian"
 		"components/common/endian/test"
-		"components/common/crc32/native"
+		"components/common/crc32"
 		"components/common/crc32/test"
 		"components/config/ramstore"
 		"components/config/ramstore/test"
diff --git a/deployments/fwu/env/commonsp/fwu_sp.c b/deployments/fwu/env/commonsp/fwu_sp.c
index bf7cbe8..0dce92e 100644
--- a/deployments/fwu/env/commonsp/fwu_sp.c
+++ b/deployments/fwu/env/commonsp/fwu_sp.c
@@ -5,6 +5,7 @@
 
 #include <stddef.h>
 
+#include "common/crc32/crc32.h"
 #include "config/loader/sp/sp_config_loader.h"
 #include "config/ramstore/config_ramstore.h"
 #include "media/volume/factory/volume_factory.h"
@@ -65,6 +66,8 @@
 		goto fatal_error;
 	}
 
+	crc32_init();
+
 	/* Configuration - discovers required volumes and installers */
 	if (!configure_for_platform()) {
 		EMSG("Failed to configure for platform");
diff --git a/deployments/fwu/env/commonsp/fwu_sp.cmake b/deployments/fwu/env/commonsp/fwu_sp.cmake
index 20d62b8..875af62 100644
--- a/deployments/fwu/env/commonsp/fwu_sp.cmake
+++ b/deployments/fwu/env/commonsp/fwu_sp.cmake
@@ -17,7 +17,7 @@
 		"components/common/fdt"
 		"components/common/trace"
 		"components/common/utils"
-		"components/common/crc32/native"
+		"components/common/crc32"
 		"components/config/ramstore"
 		"components/config/loader/sp"
 		"components/messaging/ffa/libsp"
diff --git a/deployments/libts/linux-pc/CMakeLists.txt b/deployments/libts/linux-pc/CMakeLists.txt
index a9dd75c..3db5585 100644
--- a/deployments/libts/linux-pc/CMakeLists.txt
+++ b/deployments/libts/linux-pc/CMakeLists.txt
@@ -54,7 +54,7 @@
 		"components/common/endian"
 		"components/common/utils"
 		"components/common/trace"
-		"components/common/crc32/native"
+		"components/common/crc32"
 		"components/config/ramstore"
 		"components/service/common/include"
 		"components/service/common/client"
diff --git a/deployments/ts-service-test/linux-pc/CMakeLists.txt b/deployments/ts-service-test/linux-pc/CMakeLists.txt
index c69437f..60db229 100644
--- a/deployments/ts-service-test/linux-pc/CMakeLists.txt
+++ b/deployments/ts-service-test/linux-pc/CMakeLists.txt
@@ -84,8 +84,8 @@
 add_components(
 	TARGET "ts-service-test"
 	BASE_DIR ${TS_ROOT}
-    COMPONENTS
-		"components/common/crc32/native"
+	COMPONENTS
+		"components/common/crc32"
 		"components/service/test_runner/client/cpp"
 		"components/service/test_runner/test/service"
 		"components/service/smm_variable/client/cpp"
diff --git a/environments/arm-linux/default_toolchain_file.cmake b/environments/arm-linux/default_toolchain_file.cmake
index e77d355..6909db6 100644
--- a/environments/arm-linux/default_toolchain_file.cmake
+++ b/environments/arm-linux/default_toolchain_file.cmake
@@ -18,7 +18,7 @@
 set(CMAKE_SYSTEM_PROCESSOR arm)
 
 set(TS_DEBUG_INFO_FLAGS "-fdiagnostics-show-option -gdwarf-2" CACHE STRING "Compiler flags to add debug information.")
-set(TS_MANDATORY_AARCH_FLAGS "-mstrict-align -DARM64=1" CACHE STRING "Compiler flags configuring architecture specific ")
+set(TS_MANDATORY_AARCH_FLAGS "-mstrict-align -march=armv8-a+crc -DARM64=1" CACHE STRING "Compiler flags configuring architecture specific ")
 set(TS_WARNING_FLAGS "-Wall" CACHE STRING "Compiler flags affecting generating warning messages.")
 set(TS_MANDATORY_LINKER_FLAGS "" CACHE STRING "Linker flags needed for correct builds.")
 
diff --git a/environments/opteesp/default_toolchain_file.cmake b/environments/opteesp/default_toolchain_file.cmake
index ec5b4d7..90a9418 100644
--- a/environments/opteesp/default_toolchain_file.cmake
+++ b/environments/opteesp/default_toolchain_file.cmake
@@ -20,7 +20,7 @@
 set(CMAKE_POSITION_INDEPENDENT_CODE True)
 
 set(TS_DEBUG_INFO_FLAGS "-fdiagnostics-show-option -gdwarf-2" CACHE STRING "Compiler flags to add debug information.")
-set(TS_MANDATORY_AARCH_FLAGS "-fpic -mstrict-align" CACHE STRING "Compiler flags configuring architecture specific ")
+set(TS_MANDATORY_AARCH_FLAGS "-fpic -mstrict-align -march=armv8-a+crc" CACHE STRING "Compiler flags configuring architecture specific ")
 set(TS_WARNING_FLAGS "-Wall" CACHE STRING "Compiler flags affecting generating warning messages.")
 set(TS_MANDATORY_LINKER_FLAGS "-pie -Wl,--as-needed -Wl,--sort-section=alignment -zmax-page-size=4096"
 	CACHE STRING "Linker flags needed for correct builds.")
diff --git a/environments/sp/default_toolchain_file.cmake b/environments/sp/default_toolchain_file.cmake
index e43a00e..31febe5 100644
--- a/environments/sp/default_toolchain_file.cmake
+++ b/environments/sp/default_toolchain_file.cmake
@@ -20,7 +20,7 @@
 set(CMAKE_POSITION_INDEPENDENT_CODE True)
 
 set(TS_DEBUG_INFO_FLAGS "-fdiagnostics-show-option -gdwarf-2" CACHE STRING "Compiler flags to add debug information.")
-set(TS_MANDATORY_AARCH_FLAGS "-fpie -mstrict-align" CACHE STRING "Compiler flags configuring architecture specific ")
+set(TS_MANDATORY_AARCH_FLAGS "-fpie -mstrict-align -march=armv8-a+crc" CACHE STRING "Compiler flags configuring architecture specific ")
 set(TS_WARNING_FLAGS "-Wall" CACHE STRING "Compiler flags affecting generating warning messages.")
 set(TS_MANDATORY_LINKER_FLAGS "-Wl,-pie -Wl,--no-dynamic-linker -Wl,--sort-section=alignment -zmax-page-size=4096" CACHE STRING "Linker flags needed for correct builds.")