diff options
author | Yifei Wan <ywan@nvidia.com> | 2014-04-24 10:51:19 -0500 |
---|---|---|
committer | Mandar Padmawar <mpadmawar@nvidia.com> | 2014-04-29 22:47:12 -0700 |
commit | fe58eed2a3a88559370b7a2694178c6ecd049ec4 (patch) | |
tree | 3b279d13ae274454aea2d97b7df3b46e8dcb2f93 | |
parent | d8b41004f38dda7a16ba972ef0852d850b264927 (diff) |
ARM: tegra: add armv8 cpu registers to nvdumper
Bug 1498965
Change-Id: I353b383acd5ae32338ef38468787644fc9eafe48
Signed-off-by: Yifei Wan <ywan@nvidia.com>
Reviewed-on: http://git-master/r/400929
Reviewed-by: Mandar Padmawar <mpadmawar@nvidia.com>
Tested-by: Mandar Padmawar <mpadmawar@nvidia.com>
-rw-r--r-- | arch/arm/mach-tegra/include/mach/nvdumper.h | 93 | ||||
-rw-r--r-- | arch/arm64/configs/tegra13_android_defconfig | 2 | ||||
-rw-r--r-- | arch/arm64/mach-tegra/Makefile | 1 | ||||
-rw-r--r-- | arch/arm64/mach-tegra/nvdumper.c | 64 | ||||
-rw-r--r-- | arch/arm64/mach-tegra/nvdumper_regdump.c | 298 | ||||
-rw-r--r-- | kernel/panic.c | 15 |
6 files changed, 468 insertions, 5 deletions
diff --git a/arch/arm/mach-tegra/include/mach/nvdumper.h b/arch/arm/mach-tegra/include/mach/nvdumper.h new file mode 100644 index 000000000000..9f4b592019b5 --- /dev/null +++ b/arch/arm/mach-tegra/include/mach/nvdumper.h @@ -0,0 +1,93 @@ +/* + * arch/arm64/mach-tegra/include/mach/nvdumper.h + * + * Copyright (c) 2011-2014, NVIDIA CORPORATION. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 __MACH_TEGRA_NVDUMPER_H +#define __MACH_TEGRA_NVDUMPER_H + +/* + * This register list is from table Table D1-81 of + * ARMv8 architecture reference manual + * The table also includes the mapping of AArch64 + * to AArch32 registers. + */ +struct aar64_system_regs_t { + u64 ACTLR_EL1; + u64 AFSR0_EL1; + u64 AFSR1_EL1; + u64 AMAIR_EL1; + u64 CONTEXTIDR_EL1; + u64 CPACR_EL1; + u64 CSSELR_EL1; + u64 FAR_EL1; + u64 ESR_EL1; + u64 PAR_EL1; + u64 MAIR_EL1; + u64 RMR_EL1; + u64 SCTLR_EL1; + u64 TEECR32_EL1; + u64 TEEHBR32_EL1; + u64 TPIDR_EL1; + u64 TCR_EL1; + u64 TTBR0_EL1; + u64 TTBR1_EL1; + u64 VBAR_EL1; + + u64 TPIDRRO_EL0; + u64 TPIDR_EL0; + + u64 DACR32_EL2; + u64 HACR_EL2; + u64 ACTLR_EL2; + u64 AFSR0_EL2; + u64 AFSR1_EL2; + u64 AMAIR_EL2; + u64 CPTR_EL2; + u64 HCR_EL2; + u64 MDCR_EL2; + u64 FAR_EL2; + u64 MAIR_EL2; + u64 HPFAR_EL2; + u64 SCTLR_EL2; + u64 ESR_EL2; + u64 HSTR_EL2; + u64 TCR_EL2; + u64 TPIDR_EL2; + u64 TTBR0_EL2; + u64 VBAR_EL2; + u64 IFSR32_EL2; + u64 RMR_EL2; + u64 VMPIDR_EL2; + u64 VPIDR_EL2; + u64 VTCR_EL2; + u64 VTTBR_EL2; + + u64 RMR_EL3; + u64 SDER32_EL3; +}; + +struct nvdumper_cpu_data_t { + bool is_online; + struct pt_regs pt_regs; + struct aar64_system_regs_t aar64_sys_regs; + struct task_struct *current_task; +}; + +int nvdumper_regdump_init(void); +void nvdumper_regdump_exit(void); +void nvdumper_crash_setup_regs(void); +void nvdumper_print_data(void); + +#endif diff --git a/arch/arm64/configs/tegra13_android_defconfig b/arch/arm64/configs/tegra13_android_defconfig index c776f969052d..283cae9e87c1 100644 --- a/arch/arm64/configs/tegra13_android_defconfig +++ b/arch/arm64/configs/tegra13_android_defconfig @@ -564,7 +564,7 @@ CONFIG_FAULT_INJECTION=y CONFIG_FAILSLAB=y CONFIG_ENABLE_DEFAULT_TRACERS=y CONFIG_DYNAMIC_DEBUG=y -CONFIG_TEGRA_NVDUMPER=n +CONFIG_TEGRA_NVDUMPER=y CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y CONFIG_SECURITY_SELINUX=y diff --git a/arch/arm64/mach-tegra/Makefile b/arch/arm64/mach-tegra/Makefile index c820141a6262..751466473355 100644 --- a/arch/arm64/mach-tegra/Makefile +++ b/arch/arm64/mach-tegra/Makefile @@ -85,6 +85,7 @@ endif obj-$(CONFIG_TEGRA_DYNAMIC_PWRDET) += powerdetect.o obj-${CONFIG_TEGRA_NVDUMPER} += nvdumper.o +obj-${CONFIG_TEGRA_NVDUMPER} += nvdumper_regdump.o # To override the "-march=armv7-xxx" in arm/mach-tegra/Makefile AFLAGS_sleep.o := diff --git a/arch/arm64/mach-tegra/nvdumper.c b/arch/arm64/mach-tegra/nvdumper.c index 0d57f4228140..2404af2202b8 100644 --- a/arch/arm64/mach-tegra/nvdumper.c +++ b/arch/arm64/mach-tegra/nvdumper.c @@ -19,11 +19,20 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/reboot.h> +#include <linux/debugfs.h> +#include <linux/slab.h> #include "board.h" +#include <mach/nvdumper.h> + +#ifdef CONFIG_TEGRA_USE_NCT +#include <mach/nct.h> +#endif #define NVDUMPER_CLEAN 0xf000caf3U #define NVDUMPER_DIRTY 0xdeadbeefU +#define RW_MODE (S_IWUSR | S_IRUGO) + static uint32_t *nvdumper_ptr; static int get_dirty_state(void) @@ -63,6 +72,10 @@ static int __init nvdumper_init(void) { int ret, dirty; +#ifdef CONFIG_TEGRA_USE_NCT + union nct_item_type *item; +#endif + if (!nvdumper_reserved) { pr_info("nvdumper: not configured\n"); return -ENOTSUPP; @@ -70,13 +83,18 @@ static int __init nvdumper_init(void) nvdumper_ptr = ioremap_nocache(nvdumper_reserved, NVDUMPER_RESERVED_SIZE); if (!nvdumper_ptr) { - pr_info("nvdumper: failed to ioremap memory " - "at 0x%08lx\n", nvdumper_reserved); + pr_info("nvdumper: failed to ioremap memory at 0x%08lx\n", + nvdumper_reserved); return -EIO; } ret = register_reboot_notifier(&nvdumper_reboot_notifier); if (ret) - return ret; + goto err_out1; + + ret = nvdumper_regdump_init(); + if (ret) + goto err_out2; + dirty = get_dirty_state(); switch (dirty) { case 0: @@ -89,18 +107,56 @@ static int __init nvdumper_init(void) pr_info("nvdumper: last reboot was unknown\n"); break; } +#ifdef CONFIG_TEGRA_USE_NCT + item = kzalloc(sizeof(*item), GFP_KERNEL); + if (!item) { + pr_err("failed to allocate memory\n"); + goto err_out3; + } + + ret = tegra_nct_read_item(NCT_ID_RAMDUMP, item); + if (ret < 0) { + pr_err("%s: NCT read failure\n", __func__); + kfree(item); + goto err_out3; + } + + pr_info("%s: RAMDUMP flag(%d) from NCT\n", + __func__, item->ramdump.flag); + if (item->ramdump.flag == 1) + set_dirty_state(1); + else + set_dirty_state(0); + + kfree(item); + + return 0; + +err_out3: + +#else set_dirty_state(1); return 0; +#endif + +err_out2: + unregister_reboot_notifier(&nvdumper_reboot_notifier); +err_out1: + iounmap(nvdumper_ptr); + + return ret; + } static void __exit nvdumper_exit(void) { + nvdumper_regdump_exit(); unregister_reboot_notifier(&nvdumper_reboot_notifier); set_dirty_state(0); iounmap(nvdumper_ptr); } -module_init(nvdumper_init); +arch_initcall(nvdumper_init); module_exit(nvdumper_exit); MODULE_LICENSE("GPL"); diff --git a/arch/arm64/mach-tegra/nvdumper_regdump.c b/arch/arm64/mach-tegra/nvdumper_regdump.c new file mode 100644 index 000000000000..bd4efb8caa7a --- /dev/null +++ b/arch/arm64/mach-tegra/nvdumper_regdump.c @@ -0,0 +1,298 @@ +/* + * arch/arm64/mach-tegra/nvdumper_regdump.c + * + * Copyright (c) 2011-2014, NVIDIA CORPORATION. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/kernel.h> +#include <linux/kdebug.h> +#include <linux/notifier.h> +#include <linux/sched.h> +#include <linux/dma-mapping.h> +#include <asm/cacheflush.h> +#include <mach/nvdumper.h> + +#define THREAD_INFO(sp) ((struct thread_info *) \ + ((unsigned long)(sp) & ~(THREAD_SIZE - 1))) + +#define DEBUG_REGDUMP 1 + +static int max_cpus; +struct nvdumper_cpu_data_t *nvdumper_cpu_data; +static dma_addr_t nvdumper_p; + +void save_aar64_sys_regs(struct aar64_system_regs_t *aar64_sys_regs) +{ + asm("mrs x1, ACTLR_EL1\n\t" + "str x1, [%0, #8 * 0]\n\t" + "mrs x1, AFSR0_EL1\n\t" + "str x1, [%0, #8 * 1]\n\t" + "mrs x1, AFSR1_EL1\n\t" + "str x1, [%0, #8 * 2]\n\t" + "mrs x1, AMAIR_EL1\n\t" + "str x1, [%0, #8 * 3]\n\t" + "mrs x1, CONTEXTIDR_EL1\n\t" + "str x1, [%0, #8 * 4]\n\t" + "mrs x1, CPACR_EL1\n\t" + "str x1, [%0, #8 * 5]\n\t" + "mrs x1, CSSELR_EL1\n\t" + "str x1, [%0, #8 * 6]\n\t" + "mrs x1, FAR_EL1\n\t" + "str x1, [%0, #8 * 7]\n\t" + "mrs x1, ESR_EL1\n\t" + "str x1, [%0, #8 * 8]\n\t" + "mrs x1, PAR_EL1\n\t" + "str x1, [%0, #8 * 9]\n\t" + : /* output */ + : "r"(aar64_sys_regs) /* input */ + : "%x1", "memory" /* clobbered register */ + ); +} + +void nvdumper_save_regs(void *data) +{ + int id = smp_processor_id(); + nvdumper_cpu_data[id].current_task = current_thread_info()->task; + nvdumper_cpu_data[id].is_online = true; + + __asm__ __volatile__ ( + "mov %[_arm_sp], sp\n\t" + "adr %[_arm_pc], 1f\n\t" + "mov %[regx0], x0\n\t" + "mov %[regx1], x1\n\t" + "mov %[regx2], x2\n\t" + "mov %[regx3], x3\n\t" + "mov %[regx4], x4\n\t" + "mov %[regx5], x5\n\t" + "mov %[regx6], x6\n\t" + "mov %[regx7], x7\n\t" + "mov %[regx8], x8\n\t" + "mov %[regx9], x9\n\t" + "mov %[regx10], x10\n\t" + "mov %[regx11], x11\n\t" + "mov %[regx12], x12\n\t" + "mov %[regx13], x13\n\t" + "mov %[regx14], x14\n\t" + "mov %[regx15], x15\n\t" + "1:" : + [_arm_pc] "=r" (nvdumper_cpu_data[id].pt_regs.pc), + [_arm_sp] "=r" (nvdumper_cpu_data[id].pt_regs.sp), + [regx0] "=r" (nvdumper_cpu_data[id].pt_regs.regs[0]), + [regx1] "=r" (nvdumper_cpu_data[id].pt_regs.regs[1]), + [regx2] "=r" (nvdumper_cpu_data[id].pt_regs.regs[2]), + [regx3] "=r" (nvdumper_cpu_data[id].pt_regs.regs[3]), + [regx4] "=r" (nvdumper_cpu_data[id].pt_regs.regs[4]), + [regx5] "=r" (nvdumper_cpu_data[id].pt_regs.regs[5]), + [regx6] "=r" (nvdumper_cpu_data[id].pt_regs.regs[6]), + [regx7] "=r" (nvdumper_cpu_data[id].pt_regs.regs[7]), + [regx8] "=r" (nvdumper_cpu_data[id].pt_regs.regs[8]), + [regx9] "=r" (nvdumper_cpu_data[id].pt_regs.regs[9]), + [regx10] "=r" (nvdumper_cpu_data[id].pt_regs.regs[10]), + [regx11] "=r" (nvdumper_cpu_data[id].pt_regs.regs[11]), + [regx12] "=r" (nvdumper_cpu_data[id].pt_regs.regs[12]), + [regx13] "=r" (nvdumper_cpu_data[id].pt_regs.regs[13]), + [regx14] "=r" (nvdumper_cpu_data[id].pt_regs.regs[14]), + [regx15] "=r" (nvdumper_cpu_data[id].pt_regs.regs[15]) + ); + + __asm__ __volatile__ ( + "mov %[regx16], x16\n\t" + "mov %[regx17], x17\n\t" + "mov %[regx18], x18\n\t" + "mov %[regx19], x19\n\t" + "mov %[regx20], x20\n\t" + "mov %[regx21], x21\n\t" + "mov %[regx22], x22\n\t" + "mov %[regx23], x23\n\t" + "mov %[regx24], x24\n\t" + "mov %[regx25], x25\n\t" + "mov %[regx26], x26\n\t" + "mov %[regx27], x27\n\t" + "mov %[regx28], x28\n\t" + "mov %[regx29], x29\n\t" + "mov %[regx30], x30\n\t" + "1:" : + [regx16] "=r" (nvdumper_cpu_data[id].pt_regs.regs[16]), + [regx17] "=r" (nvdumper_cpu_data[id].pt_regs.regs[17]), + [regx18] "=r" (nvdumper_cpu_data[id].pt_regs.regs[18]), + [regx19] "=r" (nvdumper_cpu_data[id].pt_regs.regs[19]), + [regx20] "=r" (nvdumper_cpu_data[id].pt_regs.regs[20]), + [regx21] "=r" (nvdumper_cpu_data[id].pt_regs.regs[21]), + [regx22] "=r" (nvdumper_cpu_data[id].pt_regs.regs[22]), + [regx23] "=r" (nvdumper_cpu_data[id].pt_regs.regs[23]), + [regx24] "=r" (nvdumper_cpu_data[id].pt_regs.regs[24]), + [regx25] "=r" (nvdumper_cpu_data[id].pt_regs.regs[25]), + [regx26] "=r" (nvdumper_cpu_data[id].pt_regs.regs[26]), + [regx27] "=r" (nvdumper_cpu_data[id].pt_regs.regs[27]), + [regx28] "=r" (nvdumper_cpu_data[id].pt_regs.regs[28]), + [regx29] "=r" (nvdumper_cpu_data[id].pt_regs.regs[29]), + [regx30] "=r" (nvdumper_cpu_data[id].pt_regs.regs[30]) + ); + + save_aar64_sys_regs(&nvdumper_cpu_data[id].aar64_sys_regs); +} + +void nvdumper_crash_setup_regs(void) +{ + pr_info("Enter nvdumper_crash_setup_regs\n"); + on_each_cpu(nvdumper_save_regs, NULL, 1); +} + +void nvdumper_copy_regs(unsigned int id, struct pt_regs *regs, void *svc_sp) +{ + struct thread_info *thread_info = THREAD_INFO(svc_sp); + nvdumper_cpu_data[id].current_task = thread_info->task; + nvdumper_cpu_data[id].is_online = true; + memcpy(&nvdumper_cpu_data[id].pt_regs, regs, sizeof(struct pt_regs)); + save_aar64_sys_regs(&nvdumper_cpu_data[id].aar64_sys_regs); +} + +void print_cpu_data(int id) +{ + struct pt_regs *pt_regs = &nvdumper_cpu_data[id].pt_regs; + struct aar64_system_regs_t *s_regs = + &nvdumper_cpu_data[id].aar64_sys_regs; + + pr_info("------------------------------------------------\n"); + pr_info("CPU%d Status: %s\n", id, + nvdumper_cpu_data[id].is_online ? "online" : "offline"); + pr_info("current task: %p\n", nvdumper_cpu_data[id].current_task); + + if (nvdumper_cpu_data[id].is_online) { + int i; + for (i = 0; i < 31; i++) { + if (i < 10) /* Keep "=" align */ + pr_info("ARM_x%d = 0x%016llx\n", + i, pt_regs->regs[i]); + else + pr_info("ARM_x%d = 0x%016llx\n", + i, pt_regs->regs[i]); + } + pr_info("ARM_sp = 0x%016llx\n", pt_regs->sp); + pr_info("ARM_pc = 0x%016llx\n", pt_regs->pc); + + /* Print system register */ + pr_info("ACTLR_EL1 = 0x%016llx\n", s_regs->ACTLR_EL1); + pr_info("AFSR0_EL1 = 0x%016llx\n", s_regs->AFSR0_EL1); + pr_info("AFSR1_EL1 = 0x%016llx\n", s_regs->AFSR1_EL1); + pr_info("AMAIR_EL1 = 0x%016llx\n", s_regs->AMAIR_EL1); + pr_info("CONTEXTIDR_EL1 = 0x%016llx\n", + s_regs->CONTEXTIDR_EL1); + pr_info("CPACR_EL1 = 0x%016llx\n", s_regs->CPACR_EL1); + pr_info("CSSELR_EL1 = 0x%016llx\n", s_regs->CSSELR_EL1); + pr_info("FAR_EL1 = 0x%016llx\n", s_regs->FAR_EL1); + pr_info("ESR_EL1 = 0x%016llx\n", s_regs->ESR_EL1); + pr_info("PAR_EL1 = 0x%016llx\n", s_regs->PAR_EL1); + pr_info("MAIR_EL1 = 0x%016llx\n", s_regs->MAIR_EL1); + pr_info("RMR_EL1 = 0x%016llx\n", s_regs->RMR_EL1); + pr_info("SCTLR_EL1 = 0x%016llx\n", s_regs->SCTLR_EL1); + pr_info("TEECR32_EL1 = 0x%016llx\n", s_regs->TEECR32_EL1); + pr_info("TEEHBR32_EL1 = 0x%016llx\n", s_regs->TEEHBR32_EL1); + pr_info("TPIDR_EL1 = 0x%016llx\n", s_regs->TPIDR_EL1); + pr_info("TCR_EL1 = 0x%016llx\n", s_regs->TCR_EL1); + pr_info("TTBR0_EL1 = 0x%016llx\n", s_regs->TTBR0_EL1); + pr_info("TTBR1_EL1 = 0x%016llx\n", s_regs->TTBR1_EL1); + pr_info("VBAR_EL1 = 0x%016llx\n", s_regs->VBAR_EL1); + pr_info("TPIDRRO_EL0 = 0x%016llx\n", s_regs->TPIDRRO_EL0); + pr_info("TPIDR_EL0 = 0x%016llx\n", s_regs->TPIDR_EL0); + } +} + +void nvdumper_print_data(void) +{ + int id; + + for_each_present_cpu(id) + print_cpu_data(id); +} + +int nvdumper_die_handler(struct notifier_block *nb, unsigned long reason, + void *data) +{ + nvdumper_crash_setup_regs(); + return NOTIFY_OK; +} + +static int nvdumper_panic_handler(struct notifier_block *this, + unsigned long event, void *unused) +{ +#if DEBUG_REGDUMP + nvdumper_print_data(); +#endif + flush_cache_all(); + + return NOTIFY_OK; +} + +struct notifier_block nvdumper_die_notifier = { + .notifier_call = nvdumper_die_handler, + .priority = INT_MAX-1, /* priority: INT_MAX >= x >= 0 */ +}; + +static struct notifier_block nvdumper_panic_notifier = { + .notifier_call = nvdumper_panic_handler, + .priority = INT_MAX-1, /* priority: INT_MAX >= x >= 0 */ +}; + +int nvdumper_regdump_init(void) +{ + int ret; + + max_cpus = num_possible_cpus(); + + nvdumper_cpu_data = dma_alloc_coherent(NULL, + sizeof(struct nvdumper_cpu_data_t) * max_cpus, + &nvdumper_p, 0); + if (!nvdumper_cpu_data) { + pr_err("%s: can not allocate bounce buffer\n", __func__); + + return -ENOMEM; + } + + ret = register_die_notifier(&nvdumper_die_notifier); + if (ret != 0) { + pr_err("%s: registering die notifier failed with err=%d\n", + __func__, ret); + goto err_out1; + } + + ret = atomic_notifier_chain_register(&panic_notifier_list, + &nvdumper_panic_notifier); + if (ret != 0) { + pr_err("%s: unable to register a panic notifier (err=%d)\n", + __func__, ret); + goto err_out2; + } + + return ret; + +err_out2: + unregister_die_notifier(&nvdumper_die_notifier); + +err_out1: + if (nvdumper_cpu_data) + dma_free_coherent(NULL, + sizeof(struct nvdumper_cpu_data_t) * max_cpus, + nvdumper_cpu_data, nvdumper_p); + + return ret; +} + +void nvdumper_regdump_exit(void) +{ + dma_free_coherent(NULL, sizeof(struct nvdumper_cpu_data_t) * max_cpus, + nvdumper_cpu_data, nvdumper_p); + unregister_die_notifier(&nvdumper_die_notifier); + atomic_notifier_chain_unregister(&panic_notifier_list, + &nvdumper_panic_notifier); +} diff --git a/kernel/panic.c b/kernel/panic.c index 126b2ef2eb61..df3a06a32496 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -63,6 +63,11 @@ void __weak panic_smp_self_stop(void) cpu_relax(); } +#ifdef CONFIG_TEGRA_NVDUMPER +#include <mach/nvdumper.h> +static int is_oops_called; +#endif /* CONFIG_TEGRA_NVDUMPER */ + /** * panic - halt the system * @fmt: The text string to print @@ -79,6 +84,12 @@ void panic(const char *fmt, ...) long i, i_next = 0; int state = 0; +#ifdef CONFIG_TEGRA_NVDUMPER + /* if panic is called directly */ + if (!is_oops_called) + nvdumper_crash_setup_regs(); +#endif + /* * Disable local interrupts. This will prevent panic_smp_self_stop * from deadlocking the first cpu that invokes the panic, since @@ -356,6 +367,10 @@ int oops_may_print(void) */ void oops_enter(void) { +#ifdef CONFIG_TEGRA_NVDUMPER + is_oops_called = 1; +#endif + tracing_off(); /* can't trust the integrity of the kernel anymore: */ debug_locks_off(); |