From d6c2c0d70cf38b66de3448436446f1a12f986bf2 Mon Sep 17 00:00:00 2001
From: Wei Shu <weishu@marvell.com>
Date: Thu, 16 Oct 2014 21:28:55 +0800
Subject: [PATCH] gcore: add ARM64 compat mode support

Signed-off-by: Wei Shu <weishu@marvell.com>
---
 extensions/libgcore/gcore_arm64.c      | 119 +++++++++++++++++++++++++++++++++
 extensions/libgcore/gcore_compat_arm.h |  43 ++++++++++++
 extensions/libgcore/gcore_defs.h       |  33 ++++++++-
 3 files changed, 192 insertions(+), 3 deletions(-)
 create mode 100644 extensions/libgcore/gcore_compat_arm.h

diff --git a/extensions/libgcore/gcore_arm64.c b/extensions/libgcore/gcore_arm64.c
index 8ea1994..3257389 100755
--- a/extensions/libgcore/gcore_arm64.c
+++ b/extensions/libgcore/gcore_arm64.c
@@ -102,14 +102,133 @@ static const struct user_regset_view arm64_regset_view = {
 	.e_machine = EM_AARCH64,
 };
 
+#ifdef GCORE_ARCH_COMPAT
+enum compat_regset {
+	REGSET_COMPAT_GPR,
+	REGSET_COMPAT_VFP,
+};
+
+struct pt_regs {
+	struct user_pt_regs user_regs;
+	unsigned long orig_x0;
+	unsigned long syscallno;
+};
+
+static int compat_gpr_get(struct task_context *target,
+			  const struct user_regset *regset,
+			  unsigned int size, void *buf)
+{
+	struct pt_regs pt_regs;
+	struct user_regs_struct32 *regs = (struct user_regs_struct32 *)buf;
+
+	BZERO(&pt_regs, sizeof(pt_regs));
+	BZERO(regs, sizeof(*regs));
+
+	readmem(machdep->get_stacktop(target->task) - 16 - SIZE(pt_regs), KVADDR,
+		&pt_regs, sizeof(struct pt_regs), "compat_gpr_get: pt_regs",
+		gcore_verbose_error_handle());
+
+	regs->r0 = pt_regs.user_regs.regs[0];
+	regs->r1 = pt_regs.user_regs.regs[1];
+	regs->r2 = pt_regs.user_regs.regs[2];
+	regs->r3 = pt_regs.user_regs.regs[3];
+	regs->r4 = pt_regs.user_regs.regs[4];
+	regs->r5 = pt_regs.user_regs.regs[5];
+	regs->r6 = pt_regs.user_regs.regs[6];
+	regs->r7 = pt_regs.user_regs.regs[7];
+	regs->r8 = pt_regs.user_regs.regs[8];
+	regs->r9 = pt_regs.user_regs.regs[9];
+	regs->r10 = pt_regs.user_regs.regs[10];
+	regs->fp = pt_regs.user_regs.regs[11];
+	regs->ip = pt_regs.user_regs.regs[12];
+	regs->sp = pt_regs.user_regs.regs[13];
+	regs->lr = pt_regs.user_regs.regs[14];
+	regs->pc = pt_regs.user_regs.pc;
+	regs->cpsr = pt_regs.user_regs.pstate;
+	regs->ORIG_r0 = pt_regs.orig_x0;
+
+	return 0;
+}
+
+static int compat_vfp_get(struct task_context *target,
+			  const struct user_regset *regset,
+			  unsigned int size, void *buf)
+{
+	/*
+	 * The VFP registers are packed into the fpsimd_state, so they all sit
+	 * nicely together for us. We just need to create the fpscr separately.
+	 */
+	struct user_fpsimd_state *fpr = (struct user_fpsimd_state *)buf;
+	compat_ulong_t fpscr;
+
+	BZERO(fpr, sizeof(*fpr));
+	readmem(target->task + OFFSET(task_struct_thread)
+		+ GCORE_OFFSET(thread_struct_fpsimd_state),
+		KVADDR, fpr, sizeof(struct user_fpsimd_state),
+		"compat_fpr_get: user_fpsimd_state",
+		gcore_verbose_error_handle());
+
+	fpscr = (fpr->fpsr & VFP_FPSCR_STAT_MASK) |
+		(fpr->fpcr & VFP_FPSCR_CTRL_MASK);
+
+	fpr->fpcr = fpscr;
+
+	return 0;
+}
+
+#define NT_ARM_VFP	0x400           /* ARM VFP/NEON registers */
+static const struct user_regset aarch32_regsets[] = {
+	[REGSET_COMPAT_GPR] = {
+		.core_note_type = NT_PRSTATUS,
+		.name = "CORE",
+		.size = sizeof(struct user_regs_struct32),
+		.get = compat_gpr_get,
+	},
+	[REGSET_COMPAT_VFP] = {
+		.core_note_type = NT_ARM_VFP,
+		.name = "CORE",
+		.size = VFP_STATE_SIZE,
+		.get = compat_vfp_get,
+	},
+};
+
+static const struct user_regset_view aarch32_regset_view = {
+	.name = "aarch32",
+	.e_machine = EM_ARM,
+	.regsets = aarch32_regsets,
+	.n = ARRAY_SIZE(aarch32_regsets)
+};
+#endif /* GCORE_ARCH_COMPAT */
+
 const struct user_regset_view *
 task_user_regset_view(void)
 {
+#ifdef GCORE_ARCH_COMPAT
+	if (gcore_is_arch_32bit_emulation(CURRENT_CONTEXT()))
+		return &aarch32_regset_view;
+#endif /* GCORE_ARCH_COMPAT */
 	return &arm64_regset_view;
 }
 
+#ifdef GCORE_ARCH_COMPAT
+enum gcore_arm64_thread_info_flag
+{
+	TIF_32BIT = 22		/* 32bit process */
+};
+#endif /* GCORE_ARCH_COMPAT */
+
 int gcore_is_arch_32bit_emulation(struct task_context *tc)
 {
+#ifdef GCORE_ARCH_COMPAT
+	uint32_t flags;
+	char *thread_info_buf;
+
+	thread_info_buf = fill_thread_info(tc->thread_info);
+	flags = ULONG(thread_info_buf + OFFSET(thread_info_flags));
+
+	if (flags & (1UL << TIF_32BIT))
+		return TRUE;
+#endif /* GCORE_ARCH_COMPAT */
 	return FALSE;
 }
 
diff --git a/extensions/libgcore/gcore_compat_arm.h b/extensions/libgcore/gcore_compat_arm.h
new file mode 100644
index 0000000..bec9c56
--- /dev/null
+++ b/extensions/libgcore/gcore_compat_arm.h
@@ -0,0 +1,43 @@
+/* gcore_compat_arm.h -- core analysis suite
+ *
+ * Copyright (C) 2014 Marvell. Inc
+ * author: Wei Shu <weishu@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef GCORE_COMPAT_ARM_H_
+#define GCORE_COMPAT_ARM_H_
+#include <stdint.h>
+
+typedef int32_t compat_time_t;
+typedef int32_t compat_pid_t;
+typedef uint32_t __compat_uid_t;
+typedef uint32_t __compat_gid_t;
+typedef int32_t compat_int_t;
+typedef uint32_t compat_ulong_t;
+
+struct compat_timeval {
+	compat_time_t	tv_sec;
+	int32_t	tv_usec;
+};
+
+typedef struct user_regs_struct32 compat_elf_gregset_t;
+
+/* Masks for extracting the FPSR and FPCR from the FPSCR */
+#define VFP_FPSCR_STAT_MASK	0xf800009f
+#define VFP_FPSCR_CTRL_MASK	0x07f79f00
+/*
+ * The VFP state has 32x64-bit registers and a single 32-bit
+ * control/status register.
+ */
+#define VFP_STATE_SIZE		((32 * 8) + 4)
+
+#endif /* GCORE_COMPAT_ARM_H_ */
diff --git a/extensions/libgcore/gcore_defs.h b/extensions/libgcore/gcore_defs.h
index 95b256d..058340e 100755
--- a/extensions/libgcore/gcore_defs.h
+++ b/extensions/libgcore/gcore_defs.h
@@ -18,7 +18,7 @@
 #include <stdio.h>
 #include <elf.h>
 
-#ifdef X86_64
+#if defined(X86_64) || defined(ARM64)
 #define GCORE_ARCH_COMPAT 1
 #endif
 
@@ -26,6 +26,10 @@
 #include <gcore_compat_x86.h>
 #endif
 
+#ifdef ARM64
+#include <gcore_compat_arm.h>
+#endif
+
 #define PN_XNUM 0xffff
 
 #define ELF_CORE_EFLAGS 0
@@ -109,7 +113,7 @@
 #define Elf_Shdr Elf64_Shdr
 #define Elf_Nhdr Elf64_Nhdr
 
-#ifndef NT_ARM_TLS 
+#ifndef NT_ARM_TLS
 #define NT_ARM_TLS      0x401           /* ARM TLS register */
 #endif
 #endif
@@ -410,7 +414,6 @@ struct user_regs_struct {
 	unsigned long	fs;
 	unsigned long	gs;
 };
-#endif
 
 struct user_regs_struct32 {
 	uint32_t ebx, ecx, edx, esi, edi, ebp, eax;
@@ -421,6 +424,7 @@ struct user_regs_struct32 {
 	uint32_t eflags, esp;
 	unsigned short ss, __ss;
 };
+#endif
 
 #ifdef X86
 struct user_regs_struct {
@@ -532,6 +536,29 @@ typedef elf_greg_t elf_gregset_t[ELF_NGREG];
 /* Register set for the floating-point registers.  */
 typedef struct user_fpsimd_state elf_fpregset_t;
 
+#ifdef GCORE_ARCH_COMPAT
+/* AArch32 registers. */
+struct user_regs_struct32{
+	uint32_t r0;
+	uint32_t r1;
+	uint32_t r2;
+	uint32_t r3;
+	uint32_t r4;
+	uint32_t r5;
+	uint32_t r6;
+	uint32_t r7;
+	uint32_t r8;
+	uint32_t r9;
+	uint32_t r10;
+	uint32_t fp;
+	uint32_t ip;
+	uint32_t sp;
+	uint32_t lr;
+	uint32_t pc;
+	uint32_t cpsr;
+	uint32_t ORIG_r0;
+};
+#endif /* GCORE_ARCH_COMPAT */
 #endif
 
 #if defined(X86) || defined(X86_64) || defined(ARM)
-- 
1.9.1

