Add 'run_gdb.sh' and a GDB script for aarch64

Debugging the EL2 portion of PKVM is hard because ELF addresses
do not match hyp VA. Add a GDB script with helper commands that
make it easier to compute hyp VAs of ELF symbols at runtime.

The script boots the kernel up to a point when memory layout has
been determined and reads the relevant constants from kernel memory.

Vmlinux ELF is loaded twice: once under kernel VA and once under
hyp VA. This way debug info is available when stepping through
hyp code as well, enabling showing C source and dumping the stack.
However, bringing in C symbols means that symbols are displayed
without the '__kvm_nvhe_' ELF prefix.

Add 'run_gdb.sh' wrapper which inserts the ELF path into the GDB
script and invokes GDB with it.

Change-Id: Icd4f648c22285ca0e7a65cba8630b19d2041c2fc
diff --git a/aarch64/aarch64.gdb b/aarch64/aarch64.gdb
new file mode 100644
index 0000000..5ac4d9d
--- /dev/null
+++ b/aarch64/aarch64.gdb
@@ -0,0 +1,117 @@
+# Copyright 2020 The Android KVM Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+##
+## This is a GDB script with commands to easier debug KVM on arm64.
+## Do not use this script directly. The calling script first replaces
+## variables in the form "##VAR##" with real values.
+##
+
+set architecture aarch64
+
+define virt_to_phys
+	set $__addr = $arg0
+	if ($__addr & $kern_va_lm_bit) == 0
+		p/x ($__addr + $physvirt_offset)
+	else
+		p/x ($__addr - $kimage_voffset)
+	end
+end
+document virt_to_phys
+	Convert kernel VA to IPA and print the result.
+	Usage: virt_to_phy <kernel_va>
+end
+
+define phys_to_virt
+	set $__addr = $arg0
+	p/x ($__addr - $physvirt_offset)
+end
+document phys_to_virt
+	Convert kernel IPA to linear-map VA and print the result.
+	Usage: phys_to_virt <kernel_ipa>
+end
+
+define lm_alias
+	virt_to_phys $arg0
+	phys_to_virt $
+end
+document lm_alias
+	Convert kernel VA to linear-map VA and print the result.
+	Usage: lm_alias <kernel_va>
+end
+
+define kern_hyp_va
+	set $__addr = $arg0
+	p/x (($__addr & $hyp_va_mask) | $hyp_va_tag)
+end
+document kern_hyp_va
+	Convert kernel VA to hyp VA and print the result.
+	Usage: kern_hyp_va <kernel_va>
+end
+
+define sym_hyp_va
+	set $__addr = (unsigned long)&$arg0
+	lm_alias $__addr
+	kern_hyp_va $
+end
+document sym_hyp_va
+	Get hyp VA of an ELF symbol and print the result.
+	Usage: sym_hyp_va <sym>
+end
+
+define break_nvhe
+	sym_hyp_va __kvm_nvhe_$arg0
+	b *$
+end
+define bn
+	break_nvhe $arg0
+end
+document break_nvhe
+	Set breakpoint at hyp VA of a given nVHE symbol name.
+	Symbol name is provided without the "__kvm_nvhe_" prefix.
+	Usage: break_nvhe <sym>
+	       bn <sym>
+end
+
+file "##ELF_PATH##"
+
+# Break after memory layout constants have been computed.
+break apply_alternatives_all
+commands
+	# Cache kernel constants so they are available regardless
+	# of CurrentEL.
+	set $hyp_va_tag = (tag_val << tag_lsb)
+	set $hyp_va_mask = va_mask
+	set $kern_va_lm_bit = (1ul << (vabits_actual - 1))
+	set $physvirt_offset = physvirt_offset
+	set $kimage_voffset = kimage_voffset
+
+	# Load the ELF again under hyp VA.
+	# We use '_stext' here because 'add-symbol-file' expects address
+	# of the '.text' section. '_text' corresponds to '.head.text'.
+	sym_hyp_va _stext
+	add-symbol-file "##ELF_PATH##" $
+
+	# Unset this breakpoint.
+	# Note: this deletes *all* breakpoints.
+	delete breakpoint
+
+	echo Protected KVM debug mode initialized\n
+end
+
+# Connect to QEMU emulating the kernel.
+target remote :1234
+
+# Boot the kernel up to the breakpoint above.
+continue
diff --git a/aarch64/run_gdb.sh b/aarch64/run_gdb.sh
new file mode 100755
index 0000000..2555d13
--- /dev/null
+++ b/aarch64/run_gdb.sh
@@ -0,0 +1,68 @@
+#!/usr/bin/env bash
+
+# Copyright 2020 The Android KVM Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source "$(dirname "${BASH_SOURCE[0]}")/../common.inc"
+
+default_var GDB		"${TOOLCHAINS_GDB_BIN}"
+default_var KERNEL_ELF	"${LINUX_OUT_ELF}"
+default_var VERBOSE	0
+
+function usage() {
+	cat <<EOF
+
+Usage: $0 [-h] [-v] [-e GDB] [-k KERNEL_ELF]
+
+    -h    output this help text
+    -v    print invoked command
+    -e    GDB binary
+    -k    kernel ELF file (vmlinux for Linux)
+EOF
+}
+
+while getopts ":e:k:vh" OPT; do
+	case "${OPT}" in
+	e)	GDB="${OPTARG}"			;;
+	k)	KERNEL_ELF="${OPTARG}"		;;
+	v)	VERBOSE=1			;;
+	h)
+		usage
+		exit 0
+		;;
+	\?)
+		echo "Invalid option: ${!OPTIND}" 1>&2
+		usage 1>&2
+		exit 1
+		;;
+	:)
+		echo "Invalid option: -${OPTARG} requires an argument" 1>&2
+		usage 1>&2
+		exit 1
+		;;
+    esac
+done
+shift $((OPTIND -1))
+if [ $# -ne 0 ]; then
+	echo "Unrecognized options: $@" 1>&2
+	usage 1>&2
+	exit 1
+fi
+
+INPUT="$(dirname "${BASH_SOURCE[0]}")/aarch64.gdb"
+OUTPUT=$(mktemp)
+
+[ "${VERBOSE}" -eq 1 ] && set -x
+sed "s|##ELF_PATH##|${KERNEL_ELF}|g" "${INPUT}" > "${OUTPUT}"
+exec "${GDB}" -ex "source ${OUTPUT}"
diff --git a/common.inc b/common.inc
index c2443bb..cb88053 100644
--- a/common.inc
+++ b/common.inc
@@ -30,10 +30,14 @@
 
 PREBUILTS_KUT_ROOTFS="${PREBUILTS_IMG_DIR}/rootfs.ext4"
 
+TOOLCHAINS_DIR="${ROOT_DIR}/toolchains"
+TOOLCHAINS_GDB_BIN="${TOOLCHAINS_DIR}/linux-x86/gdb/bin/gdb"
+
 OUT_DIR="${ROOT_DIR}/out"
 
 LINUX_OUT="${OUT_DIR}/linux"
 LINUX_OUT_IMAGE="${LINUX_OUT}/arch/arm64/boot/Image.gz"
+LINUX_OUT_ELF="${LINUX_OUT}/vmlinux"
 
 KUT_OUT="${OUT_DIR}/kvm-unit-tests"
 KUT_TEST_DIR="${KUT_OUT}/tests"
diff --git a/kvm-unit-tests/gen_makefile.sh b/kvm-unit-tests/gen_makefile.sh
index aafc2db..e484ebb 100755
--- a/kvm-unit-tests/gen_makefile.sh
+++ b/kvm-unit-tests/gen_makefile.sh
@@ -45,6 +45,7 @@
 function group_target_name {
 	local group="$1"
 	local vhe="$2"
+
 	group="$(target_name kut "${group}")"
 	if [ "${vhe}" -eq 1 ]; then
 		group="$(target_name vhe "${group}")"
@@ -82,15 +83,20 @@
 #   1) Target name
 #   2) Test binary path
 #   3) is VHE (0/1)
+#   4) is GDB (0/1)
 function test_target {
 	local target="$1"
 	local binary="$2"
 	local vhe="$3"
+	local gdb="$4"
 
 	local extra_args=()
 	if [ "${vhe}" -eq 1 ]; then
 		extra_args+=(-V)
 	fi
+	if [ "${gdb}" -eq 1 ]; then
+		extra_args+=(-G)
+	fi
 
 	cat <<EOF
 .PHONY: ${target}
@@ -140,9 +146,12 @@
 
 		# Emit a target for this test case.
 		TARGET="$(target_name ${NAME} ${GROUP})"
-		test_target "${TARGET}" "${TEST_PATH}" "${VHE}"
+		GDB_TARGET="$(target_name gdb ${TARGET})"
+		TARGET_LIST+=("${TARGET}" "${GDB_TARGET}")
+
+		test_target "${TARGET}" "${TEST_PATH}" "${VHE}" 0
+		test_target "${GDB_TARGET}" "${TEST_PATH}" "${VHE}" 1
 		dependency_target "${TARGET}" "${GROUP}"
-		TARGET_LIST+=("${TARGET}")
 	done
 done