refactor(tools/cppcheck): introduce build target for MISRA check

The CPPCheck was integrated to the build system but it was not inline
with other checkers in the system. This patch refactors the cppcheck
integration with the Cmake build system.

1. Corrected the issue that Cmake .dump files are created along with
   source files
2. Enabled the MISRA checks without have to copy the rules file at
   a particular location. The limitation being that the rule description
   will not be present in the error output.
3. Introduced separate build rules for regular CPPCheck and MISRA check.
4. Cleaned up the CPPCheck cmake file to remove unneeded sequences.
5. The output of CPPCheck is now aligned to gcc output format which
   makes it more readable for developers familiar with GCC output.
6. Added MISRA rule suppressions to match project expectations.
7. The cppcheck platform file `GNU.xml` is now renamed to a generic name as the file is not specific to GNU but common for aarch64 platforms.

The patch also moves static check build targets to `tools/` cmake file.

Signed-off-by: Soby Mathew <soby.mathew@arm.com>
Change-Id: I793e86c8238e7a5d39ef720ba3e0b22f51db648d
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bddf801..6c51e17 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -151,69 +151,3 @@
 #
 
 add_subdirectory("tools")
-
-#
-# Rules for checkpatch
-#
-
-add_custom_target(checkcodebase
-  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
-  COMMAND ${CMAKE_COMMAND} -DCHECKCODEBASE_RUN=1 -P ${CMAKE_SOURCE_DIR}/tools/checkpatch/CheckPatch.cmake
-  )
-
-add_custom_target(checkpatch
-  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
-  COMMAND ${CMAKE_COMMAND} -DCHECKPATCH_RUN=1 -P ${CMAKE_SOURCE_DIR}/tools/checkpatch/CheckPatch.cmake
-  )
-
-#
-# Rules for checking license and copyright headers
-#
-add_custom_target(checkspdx-codebase
-  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
-  COMMAND ${CMAKE_COMMAND} -DCHECKSPDX_CODEBASE=1 -P ${CMAKE_SOURCE_DIR}/tools/checkspdx/CheckSPDX.cmake
-  )
-
-add_custom_target(checkspdx-patch
-  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
-  COMMAND ${CMAKE_COMMAND} -DCHECKSPDX_PATCH=1 -P ${CMAKE_SOURCE_DIR}/tools/checkspdx/CheckSPDX.cmake
-  )
-
-#
-# Rules for checking header files include order
-#
-add_custom_target(checkincludes-codebase
-  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
-  COMMAND ${CMAKE_COMMAND} -DCHECKINCLUDES_CODEBASE=1 -P ${CMAKE_SOURCE_DIR}/tools/checkincludes/CheckIncludes.cmake
-  )
-
-add_custom_target(checkincludes-patch
-  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
-  COMMAND ${CMAKE_COMMAND} -DCHECKINCLUDES_PATCH=1 -P ${CMAKE_SOURCE_DIR}/tools/checkincludes/CheckIncludes.cmake
-  )
-
-#
-# Rules for running clang-tidy checks
-#
-# Pass through the value of RMM_TOOLCHAIN as this must be verified before
-# clang-tidy can be run.
-#
-# Also pass through the build directory as this cannot be accessed when the
-# clang-tidy target is built.
-#
-add_custom_target(clang-tidy-codebase
-  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
-  COMMAND ${CMAKE_COMMAND} -DCLANG-TIDY_CODEBASE=1
-    -DRMM_TOOLCHAIN=${RMM_TOOLCHAIN}
-    -DBUILD_DIR=${CMAKE_BINARY_DIR}
-    -P ${CMAKE_SOURCE_DIR}/tools/clang-tidy/clang-tidy.cmake
-  )
-
-add_custom_target(clang-tidy-patch
-  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
-  COMMAND ${CMAKE_COMMAND} -DCLANG-TIDY_PATCH=1
-    -DRMM_TOOLCHAIN=${RMM_TOOLCHAIN}
-    -DBUILD_DIR=${CMAKE_BINARY_DIR}
-    -P ${CMAKE_SOURCE_DIR}/tools/clang-tidy/clang-tidy.cmake
-  )
-
diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst
index 58edeb7..6dab78d 100644
--- a/docs/getting_started/build-options.rst
+++ b/docs/getting_started/build-options.rst
@@ -102,17 +102,17 @@
 
 .. code-block:: bash
 
-    cmake -DRMM_CONFIG=fvp_defcfg -DRMM_STATIC_ANALYSIS_CPPCHECK=ON -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
+    cmake -DRMM_CONFIG=fvp_defcfg -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
     cmake --build ${RMM_BUILD_DIR} -- cppcheck
     cat ${BUILD_DIR}/tools/cppcheck/cppcheck.xml
 
-9. Perform a Cppcheck static analysis with CERT_C/MISRA/THREAD SAFETY (example with MISRA):
+9. Perform a Cppcheck static analysis with MISRA:
 
 .. code-block:: bash
 
-    cmake -DRMM_CONFIG=fvp_defcfg -DRMM_STATIC_ANALYSIS_CPPCHECK=ON -DRMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_MISRA=ON -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
-    cmake --build ${RMM_BUILD_DIR} -- cppcheck
-    cat ${BUILD_DIR}/tools/cppcheck/cppcheck.xml
+    cmake -DRMM_CONFIG=fvp_defcfg -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
+    cmake --build ${RMM_BUILD_DIR} -- cppcheck-misra
+    cat ${BUILD_DIR}/tools/cppcheck/cppcheck_misra.xml
 
 10. Perform a checkpatch analysis:
 
@@ -270,10 +270,6 @@
    RMM_TOOLCHAIN		,gnu | llvm		,			,"Toolchain name"
    LOG_LEVEL			,			,40			,"Log level to apply for RMM (0 - 50)"
    RMM_STATIC_ANALYSIS		,			,			,"Enable static analysis checkers"
-   RMM_STATIC_ANALYSIS_CPPCHECK				,ON | OFF	,ON	,"Enable Cppcheck static analysis"
-   RMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_CERT_C		,ON | OFF	,ON	,"Enable Cppcheck's SEI CERT C checker"
-   RMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_MISRA		,ON | OFF	,ON	,"Enable Cppcheck's MISRA C:2012 checker"
-   RMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_THREAD_SAFETY	,ON | OFF	,ON	,"Enable Cppcheck's thread safety checker"
    RMM_UART_ADDR		,			,0x0			,"Base addr of UART to be used for RMM logs"
    PLAT_CMN_CTX_MAX_XLAT_TABLES ,			,0			,"Maximum number of translation tables used by the runtime context"
    PLAT_CMN_EXTRA_MMAP_REGIONS	,			,0			,"Extra platform mmap regions that need to be mapped in S1 xlat tables"
diff --git a/docs/getting_started/getting-started.rst b/docs/getting_started/getting-started.rst
index bbf744e..87c003d 100644
--- a/docs/getting_started/getting-started.rst
+++ b/docs/getting_started/getting-started.rst
@@ -51,6 +51,7 @@
    "docutils",">v2.38.0","Documentation"
    "gcovr",">=v4.2","Tools(Coverage analysis)"
    "CBMC",">=5.84.0","Tools(CBMC analysis)"
+   "CPPcheck",">=1.90","Tools(CPPcheck)"
 
 .. _getting_started_toolchain:
 
@@ -216,15 +217,14 @@
     if using the Cppcheck static analysis.
 
 Follow the public documentation to install Cppcheck either from the official
-website https://cppcheck.sourceforge.io/#download or from the official github
+website https://cppcheck.sourceforge.io/ or from the official github
 https://github.com/danmar/cppcheck/
 
 If you own a valid copy of a MISRA rules file:
 
 .. code-block:: bash
 
-    sudo mkdir /usr/local/share/Cppcheck/misra
-    sudo cp -a <path to the misra rules file>/<file name> /usr/local/share/Cppcheck/misra/misra.rules
+    cp -a <path to the misra rules file>/<file name> ${RMM_SOURCE_DIR}/tools/cppcheck/misra.rules
 
 ############
 Install CBMC
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 9811bf3..bee9500 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -7,5 +7,92 @@
     NAME RMM_STATIC_ANALYSIS
     HELP "Enable static analysis checkers.")
 
-add_subdirectory("cppcheck")
 add_subdirectory("cbmc")
+
+#
+# Additional static checks build targets
+#
+
+#
+# Rules for checkpatch
+#
+add_custom_target(checkcodebase
+  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+  COMMAND ${CMAKE_COMMAND} -DCHECKCODEBASE_RUN=1 -P ${CMAKE_SOURCE_DIR}/tools/checkpatch/CheckPatch.cmake
+  )
+
+add_custom_target(checkpatch
+  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+  COMMAND ${CMAKE_COMMAND} -DCHECKPATCH_RUN=1 -P ${CMAKE_SOURCE_DIR}/tools/checkpatch/CheckPatch.cmake
+  )
+
+#
+# Rules for checking license and copyright headers
+#
+add_custom_target(checkspdx-codebase
+  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+  COMMAND ${CMAKE_COMMAND} -DCHECKSPDX_CODEBASE=1 -P ${CMAKE_SOURCE_DIR}/tools/checkspdx/CheckSPDX.cmake
+  )
+
+add_custom_target(checkspdx-patch
+  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+  COMMAND ${CMAKE_COMMAND} -DCHECKSPDX_PATCH=1 -P ${CMAKE_SOURCE_DIR}/tools/checkspdx/CheckSPDX.cmake
+  )
+
+#
+# Rules for checking header files include order
+#
+add_custom_target(checkincludes-codebase
+  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+  COMMAND ${CMAKE_COMMAND} -DCHECKINCLUDES_CODEBASE=1 -P ${CMAKE_SOURCE_DIR}/tools/checkincludes/CheckIncludes.cmake
+  )
+
+add_custom_target(checkincludes-patch
+  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+  COMMAND ${CMAKE_COMMAND} -DCHECKINCLUDES_PATCH=1 -P ${CMAKE_SOURCE_DIR}/tools/checkincludes/CheckIncludes.cmake
+  )
+
+#
+# Rules for running clang-tidy checks
+#
+# Pass through the value of RMM_TOOLCHAIN as this must be verified before
+# clang-tidy can be run.
+#
+# Also pass through the build directory as this cannot be accessed when the
+# clang-tidy target is built.
+#
+add_custom_target(clang-tidy-codebase
+  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+  COMMAND ${CMAKE_COMMAND} -DCLANG-TIDY_CODEBASE=1
+    -DRMM_TOOLCHAIN=${RMM_TOOLCHAIN}
+    -DBUILD_DIR=${CMAKE_BINARY_DIR}
+    -P ${CMAKE_SOURCE_DIR}/tools/clang-tidy/clang-tidy.cmake
+  )
+
+add_custom_target(clang-tidy-patch
+  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+  COMMAND ${CMAKE_COMMAND} -DCLANG-TIDY_PATCH=1
+    -DRMM_TOOLCHAIN=${RMM_TOOLCHAIN}
+    -DBUILD_DIR=${CMAKE_BINARY_DIR}
+    -P ${CMAKE_SOURCE_DIR}/tools/clang-tidy/clang-tidy.cmake
+  )
+
+#
+# Rules for running CPPCheck
+#
+add_custom_target(cppcheck
+  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+  COMMAND ${CMAKE_COMMAND}
+    -DSOURCE_DIR=${CMAKE_SOURCE_DIR}
+    -DBUILD_DIR=${CMAKE_BINARY_DIR}
+    -P ${CMAKE_SOURCE_DIR}/tools/cppcheck/CPPCheck.cmake
+  )
+
+add_custom_target(cppcheck-misra
+  WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+  COMMAND ${CMAKE_COMMAND}
+    -DSOURCE_DIR=${CMAKE_SOURCE_DIR}
+    -DBUILD_DIR=${CMAKE_BINARY_DIR}
+    -DCPPCHECK_MISRA=1
+    -P ${CMAKE_SOURCE_DIR}/tools/cppcheck/CPPCheck.cmake
+  )
diff --git a/tools/cppcheck/compilers/GNU.xml b/tools/cppcheck-aarch64-platform.xml
similarity index 100%
rename from tools/cppcheck/compilers/GNU.xml
rename to tools/cppcheck-aarch64-platform.xml
diff --git a/tools/cppcheck/CMakeLists.txt b/tools/cppcheck/CMakeLists.txt
deleted file mode 100644
index bbc503d..0000000
--- a/tools/cppcheck/CMakeLists.txt
+++ /dev/null
@@ -1,164 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-3-Clause
-# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
-#
-
-if(RMM_STATIC_ANALYSIS)
-    find_program(RMM_STATIC_ANALYSIS_CPPCHECK_PATH "cppcheck"
-        DOC "Path to Cppcheck.")
-endif()
-
-if(EXISTS "${RMM_STATIC_ANALYSIS}")
-    mark_as_advanced(FORCE RMM_STATIC_ANALYSIS_CPPCHECK_PATH)
-else()
-    mark_as_advanced(CLEAR RMM_STATIC_ANALYSIS_CPPCHECK_PATH)
-endif()
-
-arm_config_option(
-    NAME RMM_STATIC_ANALYSIS_CPPCHECK
-    HELP "Enable Cppcheck static analysis."
-    DEFAULT TRUE
-    DEPENDS (RMM_STATIC_ANALYSIS) AND
-            (EXISTS "${RMM_STATIC_ANALYSIS_CPPCHECK_PATH}")
-    ELSE FALSE)
-
-arm_config_option(
-    NAME RMM_STATIC_ANALYSIS_CPPCHECK_FLAGS
-    HELP "Cppcheck command line options."
-    TYPE STRING
-    DEFAULT ""
-    DEPENDS RMM_STATIC_ANALYSIS_CPPCHECK
-    ADVANCED)
-
-arm_config_option(
-    NAME RMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_CERT_C
-    HELP "Enable Cppcheck's SEI CERT C checker."
-    DEFAULT TRUE
-    DEPENDS RMM_STATIC_ANALYSIS_CPPCHECK
-    ELSE FALSE)
-
-arm_config_option(
-    NAME RMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_MISRA
-    HELP "Enable Cppcheck's MISRA C:2012 checker."
-    DEFAULT TRUE
-    DEPENDS RMM_STATIC_ANALYSIS_CPPCHECK
-    ELSE FALSE)
-
-arm_config_option(
-    NAME RMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_THREAD_SAFETY
-    HELP "Enable Cppcheck's thread safety checker."
-    DEFAULT TRUE
-    DEPENDS RMM_STATIC_ANALYSIS_CPPCHECK
-    ELSE FALSE)
-
-if(RMM_STATIC_ANALYSIS_CPPCHECK)
-    #
-    # Set up checkers.
-    #
-
-    set(cppcheck-flags)
-
-    list(APPEND cppcheck-flags "--enable=all")
-    list(APPEND cppcheck-flags "--xml")
-    list(APPEND cppcheck-flags "--xml-version=2")
-    list(APPEND cppcheck-flags "--output-file=${CMAKE_CURRENT_BINARY_DIR}/cppcheck.xml")
-
-    if(RMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_CERT_C)
-        list(APPEND cppcheck-flags "--addon=cert")
-    endif()
-
-    if(RMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_MISRA)
-	list(APPEND cppcheck-flags "--addon=${CMAKE_CURRENT_SOURCE_DIR}/misra.json")
-    endif()
-
-    if(RMM_STATIC_ANALYSIS_CPPCHECK_CHECKER_THREAD_SAFETY)
-        list(APPEND cppcheck-flags "--addon=threadsafety")
-    endif()
-
-    #
-    # Pass CHAR_BIT to Mbed TLS to supress error:
-    # "mbed TLS requires a platform with 8-bit chars"
-    #
-
-    list(APPEND cppcheck-flags "-DCHAR_BIT=8")
-
-    #
-    # Exclude files or directories we don't want to receive warnings about.
-    #
-
-    list(APPEND cppcheck-flags "-i${CMAKE_SOURCE_DIR}/ext/")
-    list(APPEND cppcheck-flags "-i${CMAKE_SOURCE_DIR}/lib/libc")
-
-    #
-    # If you want to suppress specific files without using an inline suppression,
-    # do it in `suppressions.txt`.
-    #
-
-    list(APPEND cppcheck-flags
-        "--inline-suppr" # Allow inline suppressions
-        "--suppressions-list=${CMAKE_CURRENT_SOURCE_DIR}/suppressions.txt")
-
-    #
-    # Determine implicit C compiler definitions by pulling them from the
-    # compiler and dumping them into a header file. This is for those situations
-    # where we're relying on compiler implementation details, but Cppcheck
-    # doesn't expose them.
-    #
-
-    file(TOUCH "${CMAKE_CURRENT_BINARY_DIR}/null.txt")
-
-    separate_arguments(cflags NATIVE_COMMAND "${CMAKE_C_FLAGS}")
-
-    execute_process(
-        COMMAND ${CMAKE_C_COMPILER} ${cflags} -dM -E -
-        INPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/null.txt"
-        OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/implicit-defines.h")
-
-    list(APPEND cppcheck-flags
-        "--include=${CMAKE_CURRENT_BINARY_DIR}/implicit-defines.h"
-        "--suppress=*:${CMAKE_CURRENT_BINARY_DIR}/implicit-defines.h")
-
-    #
-    # Traditionally we would let Cppcheck use its own standard library headers,
-    # but it appears to be lacking some critical symbols like `CHAR_BIT` from
-    # `<limits.h>`. Luckily, CMake makes this relatively easy for us to do. We
-    # don't analyze these headers, we just make them available for inclusion.
-    #
-
-    foreach(include IN LISTS CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES)
-        list(APPEND cppcheck-flags "-I${include}")
-        list(APPEND cppcheck-flags "--suppress=*:${include}/*")
-    endforeach()
-
-    #
-    # Configure the platform file. This is done based on the current compiler,
-    # if possible, so that we can communicate certain implementation details to
-    # Cppcheck and avoid false positives.
-    #
-
-    set(platform-xml
-        "${CMAKE_CURRENT_SOURCE_DIR}/compilers/${CMAKE_C_COMPILER_ID}.xml")
-
-    if(EXISTS "${platform-xml}")
-        list(APPEND cppcheck-flags "--platform=${platform-xml}")
-    else()
-        message(WARNING
-            "No Cppcheck platform file is available for this compiler. Static "
-            "analysis results may be inaccurate.")
-    endif()
-
-    separate_arguments(cppcheck-flags-user
-        NATIVE_COMMAND "${RMM_STATIC_ANALYSIS_CPPCHECK_FLAGS}")
-
-
-    set(COMPILE_COMMANDS_FILE "${CMAKE_BINARY_DIR}/compile_commands.json")
-
-    add_custom_target(cppcheck
-      COMMAND ${RMM_STATIC_ANALYSIS_CPPCHECK_PATH}
-              --project=${COMPILE_COMMANDS_FILE} ${cppcheck-flags} ||
-              (test ! -e ${COMPILE_COMMANDS_FILE} &&
-              echo 'please generate with -DRMM_STATIC_ANALYSIS_CPPCHECK=ON')
-    )
-else()
-    unset(CMAKE_C_CPPCHECK CACHE)
-endif()
diff --git a/tools/cppcheck/CPPCheck.cmake b/tools/cppcheck/CPPCheck.cmake
new file mode 100644
index 0000000..81411a1
--- /dev/null
+++ b/tools/cppcheck/CPPCheck.cmake
@@ -0,0 +1,89 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+find_program(RMM_CPPCHECK_EXE "cppcheck" DOC "Path to Cppcheck")
+
+if(NOT RMM_CPPCHECK_EXE)
+  message(FATAL_ERROR "Could not find cppcheck executable.")
+endif()
+
+#
+# Set up checkers.
+#
+set(cppcheck-flags)
+
+list(APPEND cppcheck-flags "--enable=all")
+list(APPEND cppcheck-flags "--xml")
+list(APPEND cppcheck-flags "--xml-version=2")
+list(APPEND cppcheck-flags "--template=gcc")
+
+
+if(CPPCHECK_MISRA)
+    list(APPEND cppcheck-flags "--addon=${SOURCE_DIR}/tools/cppcheck/misra.json")
+    set(CPPCHECK_OUTPUT "${BUILD_DIR}/tools/cppcheck/cppcheck_misra.xml")
+    set(CPPCHECK_BUILD_DIR "${BUILD_DIR}/tools/cppcheck/dump_misra")
+else()
+    set(CPPCHECK_OUTPUT "${BUILD_DIR}/tools/cppcheck/cppcheck.xml")
+    set(CPPCHECK_BUILD_DIR "${BUILD_DIR}/tools/cppcheck/dump")
+endif()
+
+list(APPEND cppcheck-flags "--output-file=${CPPCHECK_OUTPUT}")
+list(APPEND cppcheck-flags "--cppcheck-build-dir=${CPPCHECK_BUILD_DIR}")
+
+#
+# Exclude files or directories we don't want to receive warnings about.
+#
+list(APPEND cppcheck-flags "-i${SOURCE_DIR}/ext/")
+list(APPEND cppcheck-flags "-i${SOURCE_DIR}/lib/libc")
+
+#
+# If you want to suppress specific files without using an inline suppression,
+# do it in `suppressions.txt`.
+#
+list(APPEND cppcheck-flags
+    "--inline-suppr" # Allow inline suppressions
+    "--suppressions-list=${SOURCE_DIR}/tools/cppcheck/suppressions.txt")
+
+#
+# Configure the platform file. This communicates certain implementation details to
+# Cppcheck and avoid false positives.
+#
+set(toolchain-xml
+    "${SOURCE_DIR}/tools/cppcheck-aarch64-platform.xml")
+
+list(APPEND cppcheck-flags "--platform=${toolchain-xml}")
+set(COMPILE_COMMANDS_FILE "${BUILD_DIR}/compile_commands.json")
+if(NOT EXISTS "${COMPILE_COMMANDS_FILE}")
+    message(FATAL_ERROR "Please configure with -DCMAKE_EXPORT_COMPILE_COMMANDS=ON.")
+endif()
+
+#
+# Create the output directory
+#
+file(MAKE_DIRECTORY "${CPPCHECK_BUILD_DIR}")
+
+set(EXE_CPPCHECK_TWICE)
+
+#
+# Workaround for "internal errors" reported for cppcheck-misra.
+# Run CPPCheck 2nd time if the output file is not created.
+#
+if(CPPCHECK_MISRA AND (NOT EXISTS "${CPPCHECK_OUTPUT}"))
+    set(EXE_CPPCHECK_TWICE 1)
+endif()
+
+execute_process(
+    WORKING_DIRECTORY ${SOURCE_DIR}
+    COMMAND ${RMM_CPPCHECK_EXE}
+      --project=${COMPILE_COMMANDS_FILE} ${cppcheck-flags}
+    )
+
+if(EXE_CPPCHECK_TWICE)
+    execute_process(
+        WORKING_DIRECTORY ${SOURCE_DIR}
+        COMMAND ${RMM_CPPCHECK_EXE}
+          --project=${COMPILE_COMMANDS_FILE} ${cppcheck-flags}
+    )
+endif()
diff --git a/tools/cppcheck/misra.json b/tools/cppcheck/misra.json
index 2932aa2..e4b3624 100644
--- a/tools/cppcheck/misra.json
+++ b/tools/cppcheck/misra.json
@@ -1,7 +1,7 @@
 {
 "script": "misra.py",
     "args": [
-	"--suppress-rules=1.2,2.3,2.4,2.5,5.7,8.7,8.9,8.13,9.1,11.5,15.1,15.5,16.1,16.3,21.6",
-	"--rule-texts=/usr/local/share/Cppcheck/misra/misra.rules"
+	"--suppress-rules=1.2,2.5,3.1,5.7,8.5,8.6,8.9,8.12,8.13,9.5,10.5,10.8,11.3,11.4,11.5,11.6,13.3,15.1,15.4,15.5,15.7,16.1,16.2,16.3,16.5,16.6,17.1,17.8,17.12,19.2,20.6,20.10,20.12,21.1,21.2,21.6",
+	"--rule-texts=./tools/cppcheck/misra.rules"
     ]
 }
diff --git a/tools/cppcheck/misra.rules b/tools/cppcheck/misra.rules
new file mode 100644
index 0000000..8591d4d
--- /dev/null
+++ b/tools/cppcheck/misra.rules
@@ -0,0 +1,2 @@
+This is a place holder file for MISRA rules text.
+Refer to https://forum.misra.org.uk/thread-884.html for more info.
diff --git a/tools/cppcheck/suppressions.txt b/tools/cppcheck/suppressions.txt
index 9e74662..56848ed 100644
--- a/tools/cppcheck/suppressions.txt
+++ b/tools/cppcheck/suppressions.txt
@@ -18,3 +18,6 @@
 
 // Ignore unusedLabel which triggers false positives for COMPILER_ASSERT_ZERO
 unusedLabel
+
+// Ignore errors in ext folder
+*:ext/