diff --git a/scripts/check-uapi.sh b/scripts/check-uapi.sh
new file mode 100755
index 000000000..b9cd3a2d7
--- /dev/null
+++ b/scripts/check-uapi.sh
@@ -0,0 +1,245 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-only
+
+# Script to check a patch for UAPI stability
+set -o errexit
+set -o pipefail
+
+print_usage() {
+	name=$(basename "$0")
+	cat << EOF
+$name - check for UAPI header stability across Git commits
+
+By default, the script will check to make sure the latest commit did
+not introduce ABI changes (HEAD^1). You can check against additional
+commits/tags with the -r option.
+
+Usage: $name [-r GIT_REF]
+
+Options:
+    -r GIT_REF     Compare current version of file to GIT_REF (e.g. -r v6.1)
+
+Environmental Args:
+    ABIDIFF        Custom path to abidiff binary
+    ARCH           Architecture to build with (e.g. ARCH=arm)
+    CC             C compiler (default is "gcc")
+    CROSS_COMPILE  Cross-compiling toochain prefix
+EOF
+}
+
+# Get the file and sanitize it using the headers_install script
+get_header() {
+	local -r ref="$1"
+	local -r file="$2"
+	local -r out="$3"
+
+	if [ ! -x "${KERNEL_SRC}/scripts/unifdef" ]; then
+		if ! make -C "${KERNEL_SRC}/scripts" unifdef; then
+			errlog 'error - failed to build required dependency "scripts/unifdef"'
+			exit 1
+		fi
+	fi
+
+	mkdir -p "$(dirname "$out")"
+	(
+		cd "$KERNEL_SRC"
+		git show "${ref}:${file}" > "${out}.in"
+		scripts/headers_install.sh "${out}.in" "$out"
+	)
+}
+
+# Compile the simple test app
+do_compile() {
+	local -r compiler="$1"
+	local -r inc_dir="$2"
+	local -r header="$3"
+	local -r out="$4"
+	echo "int main(int argc, char **argv) { return 0; }" | \
+		"$compiler" -c \
+		  -o "$out" \
+		  -x c \
+		  -O0 \
+		  -std=c90 \
+		  -fno-eliminate-unused-debug-types \
+		  -g \
+		  "-I$inc_dir" \
+		  -include "$header" \
+		  -
+}
+
+# Print to stderr
+errlog() {
+	echo "$@" >&2
+}
+
+# Grab the list of incompatible headers from the usr/include Makefile
+get_no_header_list() {
+	{
+		cat "${KERNEL_SRC}/usr/include/Makefile"
+		# shellcheck disable=SC2016
+		printf '\nall:\n\t@echo $(no-header-test)\n'
+	} | make -C "${KERNEL_SRC}/usr/include" -f - --just-print \
+	  | grep '^echo' \
+	  | cut -d ' ' -f 2-
+}
+
+# Check any changed files in this commit for UAPI compatibility
+check_changed_files() {
+	refs_to_check=("$@")
+
+	local passed=0;
+	local failed=0;
+
+	while read -r status file; do
+		local -r base=${file/uapi\//}
+
+		# Get the current version of the file and put it in the install dir
+		get_header "HEAD" "$file" "${tmp_dir}/usr/${base}"
+
+		for ref in "${refs_to_check[@]}"; do
+			if ! git rev-parse --verify "$ref" > /dev/null 2>&1; then
+				echo "error - invalid ref \"$ref\""
+				exit 1
+			fi
+
+			if check_uapi_for_file "$status" "$file" "$ref" "$base"; then
+				passed=$((passed + 1))
+			else
+				failed=$((failed + 1))
+			fi
+		done
+	done < <(cd "$KERNEL_SRC" && git show HEAD --name-status --format="" --diff-filter=a -- include/uapi/)
+
+	total=$((passed + failed))
+	if [ "$total" -eq 0 ]; then
+		errlog "No changes to UAPI headers detected in most recent commit"
+	else
+		errlog "${passed}/${total} UAPI header file changes are backwards compatible"
+	fi
+
+	return "$failed"
+}
+
+# Check UAPI compatibility for a given file
+check_uapi_for_file() {
+	local -r status="$1"
+	local -r file="$2"
+	local -r ref="$3"
+	local -r base="$4"
+
+	# shellcheck disable=SC2076
+	if [[ " $(get_no_header_list) " =~ " ${base/include\//} " ]]; then
+		errlog "$file cannot be tested by this script (see usr/include/Makefile)."
+		return 1
+	fi
+
+	if [ "$status" = "D" ]; then
+		errlog "UAPI header $file was incorrectly removed"
+		return 1
+	fi
+
+	if [ "$status" = "R" ]; then
+		errlog "UAPI header $file was incorrectly renamed"
+		return 1
+	fi
+
+	# Get the "previous" verison of the API header and put it in the install dir
+	get_header "$ref" "$file" "${tmp_dir}/usr/${base}.pre"
+
+	compare_abi "${CROSS_COMPILE}${CC:-gcc}" "$file" "$base" "$ref"
+}
+
+# Perform the A/B compilation and compare output ABI
+compare_abi() {
+	local -r compiler="$1"
+	local -r file="$2"
+	local -r base="$3"
+	local -r ref="$4"
+
+	pre_bin="${tmp_dir}/pre.bin"
+	post_bin="${tmp_dir}/post.bin"
+	log="${tmp_dir}/log"
+
+	if ! do_compile "$compiler" "${tmp_dir}/usr/include" "${tmp_dir}/usr/${base}.pre" "$pre_bin" 2> "$log"; then
+		errlog "Couldn't compile current version of UAPI header $file..."
+		cat "$log" >&2
+		return 1
+	fi
+
+	if ! do_compile "$compiler" "${tmp_dir}/usr/include" "${tmp_dir}/usr/${base}" "$post_bin" 2> "$log"; then
+		errlog "Couldn't compile new version of UAPI header $file..."
+		cat "$log" >&2
+		return 1
+	fi
+
+	if "$ABIDIFF" --non-reachable-types "$pre_bin" "$post_bin" > "$log"; then
+		echo "No ABI differences detected in $file (compared to file at $ref)"
+	else
+		errlog "!!! ABI differences detected in $file (compared to file at $ref) !!!"
+		echo >&2
+		sed  -e '/summary:/d' -e '/changed type/d' -e '/^$/d' -e 's/^/  /g' "$log" >&2
+		echo >&2
+		return 1
+	fi
+}
+
+# Make sure we have the tools we need
+check_deps() {
+	export ABIDIFF="${ABIDIFF:-abidiff}"
+
+	if ! command -v "$ABIDIFF" > /dev/null 2>&1; then
+		errlog "error - abidiff not found!"
+		errlog "Please install abigail-tools (version 1.7 or greater)"
+		errlog "See: https://sourceware.org/libabigail/manual/libabigail-overview.html"
+		exit 1
+	fi
+
+	read -r abidiff_maj abidiff_min _ < <("$ABIDIFF" --version | cut -d ' ' -f 2 | tr '.' ' ')
+	if [ "$abidiff_maj" -lt 1 ] || ([ "$abidiff_maj" -eq 1 ] && [ "$abidiff_min" -lt 7 ]); then
+		errlog "error - abidiff version too old: $("$ABIDIFF" --version)"
+		errlog "Please install abigail-tools (version 1.7 or greater)"
+		errlog "See: https://sourceware.org/libabigail/manual/libabigail-overview.html"
+		exit 1
+	fi
+}
+
+main() {
+	refs_to_check=( "HEAD^1" )
+	while getopts "hr:" opt; do
+		case $opt in
+		h)
+			print_usage
+			exit 0
+			;;
+		r)
+			refs_to_check+=( "$OPTARG" )
+			;;
+		esac
+	done
+
+	check_deps
+
+	tmp_dir=$(mktemp -d)
+	trap 'rm -rf $tmp_dir' EXIT
+
+	if [ -z "$KERNEL_SRC" ]; then
+		KERNEL_SRC="$(realpath "$(dirname "$0")"/..)"
+	fi
+	export KERNEL_SRC
+
+	if ! (cd "$KERNEL_SRC" && git rev-parse --is-inside-work-tree > /dev/null 2>&1); then
+		errlog "error - this script requires the kernel tree to be initialized with Git"
+		exit 1
+	fi
+
+	export ARCH
+	export CC
+	export CROSS_COMPILE
+
+	if ! check_changed_files "${refs_to_check[@]}"; then
+		errlog "UAPI header ABI check failed"
+		exit 1
+	fi
+}
+
+main "$@"
