blob: 4c5d3f24cc4324bee45f2853f8db50dfff159b10 [file] [log] [blame]
/*
* Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
*/
#include <linux/ptrace.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/fs_struct.h>
#include <linux/proc_fs.h>
#include <linux/file.h>
#include <asm/arcregs.h>
#include <asm/irqflags.h>
#include <asm/board/troubleshoot.h>
#include <asm/cacheflush.h>
#include <linux/kmsg_dump.h>
#include <linux/vmalloc.h>
#include <linux/zlib.h>
#include <linux/slab.h>
/* 3.8 times seems safe for LHost, MuC and/or AuC crash - found by trial and error */
#define CORE_DUMP_COMPRESS_RATIO (380)
static void kmsg_dumper_core_dump(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason);
static unsigned long sram_safe_start;
static unsigned long sram_safe_size;
static arc_troubleshoot_start_hook_cbk troubleshoot_start;
static void *troubleshoot_ctx;
static struct kmsg_dumper dumper_core_dump = {.dump = kmsg_dumper_core_dump, .max_reason = KMSG_DUMP_PANIC};
/*
* Common routine to print scratch regs (r0-r12) or callee regs (r13-r25)
* -Prints 3 regs per line and a CR.
* -To continue, callee regs right after scratch, special handling of CR
*/
static noinline void print_reg_file(long *reg_rev, int start_num)
{
unsigned int i;
char buf[512];
int n = 0, len = sizeof(buf);
for (i = start_num; i < start_num + 13; i++) {
n += scnprintf(buf + n, len - n, "r%02u: 0x%08lx\t",
i, (unsigned long)*reg_rev);
if (((i + 1) % 3) == 0)
n += scnprintf(buf + n, len - n, "\n");
/* because pt_regs has regs reversed: r12..r0, r25..r13 */
if (is_isa_arcv2() && start_num == 0)
reg_rev++;
else
reg_rev--;
}
if (start_num != 0)
n += scnprintf(buf + n, len - n, "\n\n");
/* To continue printing callee regs on same line as scratch regs */
if (start_num == 0)
pr_info("%s", buf);
else
pr_cont("%s\n", buf);
}
static void show_callee_regs(struct callee_regs *cregs)
{
print_reg_file(&(cregs->r13), 13);
}
static void print_task_path_n_nm(struct task_struct *tsk, char *buf)
{
char *path_nm = NULL;
struct mm_struct *mm;
struct file *exe_file;
mm = get_task_mm(tsk);
if (!mm)
goto done;
exe_file = get_mm_exe_file(mm);
mmput(mm);
if (exe_file) {
path_nm = file_path(exe_file, buf, 255);
fput(exe_file);
}
done:
pr_info("Path: %s\n", !IS_ERR(path_nm) ? path_nm : "?");
}
static void show_faulting_vma(unsigned long address, char *buf)
{
struct vm_area_struct *vma;
struct inode *inode;
unsigned long ino = 0;
dev_t dev = 0;
char *nm = buf;
struct mm_struct *active_mm = current->active_mm;
/* can't use print_vma_addr() yet as it doesn't check for
* non-inclusive vma
*/
down_read(&active_mm->mmap_sem);
vma = find_vma(active_mm, address);
/* check against the find_vma( ) behaviour which returns the next VMA
* if the container VMA is not found
*/
if (vma && (vma->vm_start <= address)) {
struct file *file = vma->vm_file;
if (file) {
nm = file_path(file, buf, PAGE_SIZE - 1);
inode = file_inode(vma->vm_file);
dev = inode->i_sb->s_dev;
ino = inode->i_ino;
}
pr_info(" @off 0x%lx in [%s]\n"
" VMA: 0x%08lx to 0x%08lx\n",
vma->vm_start < TASK_UNMAPPED_BASE ?
address : address - vma->vm_start,
nm, vma->vm_start, vma->vm_end);
} else
pr_info(" @No matching VMA found\n");
up_read(&active_mm->mmap_sem);
}
static void show_ecr_verbose(struct pt_regs *regs)
{
unsigned int vec, cause_code;
unsigned long address;
pr_info("\n[ECR ]: 0x%08lx => ", regs->event);
/* For Data fault, this is data address not instruction addr */
address = current->thread.fault_address;
vec = regs->ecr_vec;
cause_code = regs->ecr_cause;
/* For DTLB Miss or ProtV, display the memory involved too */
if (vec == ECR_V_DTLB_MISS) {
pr_cont("Invalid %s @ 0x%08lx by insn @ 0x%08lx\n",
(cause_code == 0x01) ? "Read" :
((cause_code == 0x02) ? "Write" : "EX"),
address, regs->ret);
} else if (vec == ECR_V_ITLB_MISS) {
pr_cont("Insn could not be fetched\n");
} else if (vec == ECR_V_MACH_CHK) {
pr_cont("%s\n", (cause_code == 0x0) ?
"Double Fault" : "Other Fatal Err");
} else if (vec == ECR_V_PROTV) {
if (cause_code == ECR_C_PROTV_INST_FETCH)
pr_cont("Execute from Non-exec Page\n");
else if (cause_code == ECR_C_PROTV_MISALIG_DATA)
pr_cont("Misaligned r/w from 0x%08lx\n", address);
else
pr_cont("%s access not allowed on page\n",
(cause_code == 0x01) ? "Read" :
((cause_code == 0x02) ? "Write" : "EX"));
} else if (vec == ECR_V_INSN_ERR) {
pr_cont("Illegal Insn\n");
#ifdef CONFIG_ISA_ARCV2
} else if (vec == ECR_V_MEM_ERR) {
if (cause_code == 0x00)
pr_cont("Bus Error from Insn Mem\n");
else if (cause_code == 0x10)
pr_cont("Bus Error from Data Mem\n");
else
pr_cont("Bus Error, check PRM\n");
#endif
} else {
pr_cont("Check Programmer's Manual\n");
}
}
static int32_t compress_chunk(z_stream *stream, const bool is_last_stream)
{
int32_t zlib_retval;
if (!stream) {
printk(KERN_ERR "%s: stream is NULL\n", __func__);
return -EINVAL;
}
if (is_last_stream) {
zlib_retval = zlib_deflate(stream, Z_FINISH);
if (zlib_retval != Z_STREAM_END) {
if (zlib_retval != Z_OK) {
printk(KERN_ERR "%s: only chunk; zlib_deflate returned %d\n", __func__,
zlib_retval);
return -1;
}
printk(KERN_WARNING "%s: zlib_deflate (only chunk): some data would be missing\n",
__func__);
}
} else {
zlib_retval = zlib_deflate(stream, Z_SYNC_FLUSH);
if (zlib_retval != Z_OK) {
printk(KERN_ERR "%s: first chunk, zlib_deflate returned %d\n", __func__,
zlib_retval);
return -1;
}
/*
* If stream.avail_in is not 0, some logs could be missing; we should recheck len_max_input in
* arc_save_to_sram_safe_area()
*/
if (stream->avail_in != 0)
printk(KERN_WARNING "%s: stream->avail_in (%ld) is not 0\n", __func__, stream->avail_in);
}
return 0;
}
static int32_t compress_circular_buffer(const char *buf_chunk_one, uint32_t len_chunk_one,
const char *buf_chunk_two, uint32_t len_chunk_two,
char *buf_out, uint32_t len_buf_out,
uint32_t *len_compress_data)
{
int32_t retval = -1;
int32_t zlib_retval;
int32_t workspace_size;
z_stream stream;
if (!len_compress_data) {
printk(KERN_ERR "%s: len_compress_data is NULL\n", __func__);
return -EINVAL;
}
*len_compress_data = 0;
workspace_size = zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL);
printk(KERN_ERR "%s: workspace_size = %d\n", __func__, workspace_size);
stream.workspace = kmalloc(workspace_size, GFP_ATOMIC);
if (!stream.workspace) {
printk(KERN_ERR "%s: Failed to allocate %d bytes for deflate workspace\n", __func__,
workspace_size);
return -ENOMEM;
}
/* Use zlib format because gzip is not available */
zlib_retval = zlib_deflateInit(&stream, Z_DEFAULT_COMPRESSION);
if (zlib_retval != Z_OK) {
printk(KERN_ERR "%s: zlib_deflateInit failed, zlib_retval = %d\n", __func__, zlib_retval);
kfree(stream.workspace);
return retval;
}
stream.next_out = (Byte *) buf_out;
stream.avail_out = len_buf_out;
stream.total_out = 0;
if (len_chunk_one) {
/* Compress the first chunk */
stream.next_in = (Byte *) buf_chunk_one;
stream.avail_in = len_chunk_one;
stream.total_in = 0;
if (compress_chunk(&stream, len_chunk_two ? false : true))
goto deflate_end;
}
if (len_chunk_two) {
/* Compress the second chunk */
stream.next_in = (Byte *) buf_chunk_two;
stream.avail_in = len_chunk_two;
stream.total_in = 0;
if (compress_chunk(&stream, true))
goto deflate_end;
}
*len_compress_data = stream.total_out;
printk(KERN_ERR "%s: compressed data length = %u\n", __func__, *len_compress_data);
retval = 0;
deflate_end:
zlib_retval = zlib_deflateEnd(&stream);
if (zlib_retval != Z_OK) {
printk(KERN_WARNING "%s: zlib_deflateEnd failed, zlib_retval = %d\n", __func__, zlib_retval);
}
kfree(stream.workspace);
return retval;
}
/* Save the printk circular buffer to the SRAM safe area */
void arc_save_to_sram_safe_area(const char *buf_chunk_one, uint32_t len_chunk_one,
const char *buf_chunk_two, uint32_t len_chunk_two,
uint32_t len_max_input)
{
uint32_t extra_margin;
uint32_t len_compress_data;
/* Ensure that there is something in the printk circular buffer */
if (!(len_chunk_one + len_chunk_two)) {
printk(KERN_ERR "%s: len_chunk_one + len_chunk_two is 0\n", __func__);
return;
}
if (len_max_input < len_chunk_one + len_chunk_two) {
if (len_max_input <= len_chunk_two) {
buf_chunk_one = NULL;
len_chunk_one = 0;
buf_chunk_two += (len_chunk_two - len_max_input);
len_chunk_two = len_max_input;
} else {
len_max_input -= len_chunk_two;
buf_chunk_one += (len_chunk_one - len_max_input);
len_chunk_one = len_max_input;
}
}
/* Refer to include/linux/zlib.h */
extra_margin = (len_chunk_one + len_chunk_two) - (((len_chunk_one + len_chunk_two) * 1000) / 1001)
+ 12;
if (extra_margin < len_chunk_one) {
buf_chunk_one += extra_margin;
len_chunk_one -= extra_margin;
} else {
extra_margin -= len_chunk_one;
buf_chunk_one = NULL;
len_chunk_one = 0;
buf_chunk_two += extra_margin;
len_chunk_two -= extra_margin;
}
/*
* Compress the printk circular buffer and store it in SRAM safe aread (so as to retrieve it on the
* next reboot)
*/
compress_circular_buffer(buf_chunk_one, len_chunk_one, buf_chunk_two, len_chunk_two,
(char *) (sram_safe_start + 4), sram_safe_size - 4,
&len_compress_data);
*((uint16_t *) sram_safe_start) = HEADER_CORE_DUMP;
*((uint16_t *) (sram_safe_start + 2)) = (uint16_t) len_compress_data;
flush_cache_all();
}
static void kmsg_dumper_core_dump(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason)
{
char *buf;
uint32_t buf_in_len;
uint32_t buf_out_len = 0;
if (reason < KMSG_DUMP_PANIC)
return;
/*
* Format of buffer:
* 2 bytes: Header (HEADER_CORE_DUMP)
* 2 bytes: Length of the compressed logs (n)
* n bytes: Compressed logs
*/
/* Expecting sram_safe_start to be 2 byte aligned */
if ((sram_safe_start & 0x1) != 0) {
printk(KERN_ERR "%s: sram_safe_start (%lu) is not 2 bytes aligned\n", __func__,
sram_safe_start);
return;
}
/* Needed to inform the owner that its memory is about to be used */
if (!troubleshoot_start) {
printk(KERN_ERR "%s: troubleshoot_start not set\n", __func__);
return;
}
/*
* Find the max input length that could be fed into the compression algo [remove the header (2b)
* and length (2b)]
*/
buf_in_len = (CORE_DUMP_COMPRESS_RATIO * (sram_safe_size - 4)) / 100;
buf = kmalloc(buf_in_len, GFP_ATOMIC);
if (!buf) {
printk(KERN_ERR "%s: Failed to allocate %u bytes for core dump buffer\n", __func__,
buf_in_len);
return;
}
if (false == kmsg_dump_get_buffer(dumper, false, buf, buf_in_len, &buf_out_len)) {
printk(KERN_ERR "%s: kmsg_dump_get_buffer failed\n", __func__);
kfree(buf);
return;
}
(*troubleshoot_start)(troubleshoot_ctx);
arc_save_to_sram_safe_area(buf, buf_out_len, NULL, 0, buf_out_len);
kfree(buf);
}
/************************************************************************
* API called by rest of kernel
***********************************************************************/
void show_regs(struct pt_regs *regs)
{
struct task_struct *tsk = current;
struct callee_regs *cregs;
char *buf;
buf = (char *)__get_free_page(GFP_TEMPORARY);
if (!buf)
return;
print_task_path_n_nm(tsk, buf);
show_regs_print_info(KERN_INFO);
show_ecr_verbose(regs);
pr_info("[EFA ]: 0x%08lx\n[BLINK ]: %pS\n[ERET ]: %pS\n",
current->thread.fault_address,
(void *)regs->blink, (void *)regs->ret);
if (user_mode(regs))
show_faulting_vma(regs->ret, buf); /* faulting code, not data */
pr_info("[STAT32]: 0x%08lx", regs->status32);
#define STS_BIT(r, bit) r->status32 & STATUS_##bit##_MASK ? #bit" " : ""
#ifdef CONFIG_ISA_ARCOMPACT
pr_cont(" : %2s%2s%2s%2s%2s%2s%2s\n",
(regs->status32 & STATUS_U_MASK) ? "U " : "K ",
STS_BIT(regs, DE), STS_BIT(regs, AE),
STS_BIT(regs, A2), STS_BIT(regs, A1),
STS_BIT(regs, E2), STS_BIT(regs, E1));
#else
pr_cont(" : %2s%2s%2s%2s\n",
STS_BIT(regs, IE),
(regs->status32 & STATUS_U_MASK) ? "U " : "K ",
STS_BIT(regs, DE), STS_BIT(regs, AE));
#endif
pr_info("BTA: 0x%08lx\t SP: 0x%08lx\t FP: 0x%08lx\n",
regs->bta, regs->sp, regs->fp);
pr_info("LPS: 0x%08lx\tLPE: 0x%08lx\tLPC: 0x%08lx\n",
regs->lp_start, regs->lp_end, regs->lp_count);
/* print regs->r0 thru regs->r12
* Sequential printing was generating horrible code
*/
print_reg_file(&(regs->r0), 0);
/* If Callee regs were saved, display them too */
cregs = (struct callee_regs *)current->thread.callee_reg;
if (cregs)
show_callee_regs(cregs);
free_page((unsigned long)buf);
}
void show_kernel_fault_diag(const char *str, struct pt_regs *regs,
unsigned long address)
{
current->thread.fault_address = address;
/* Caller and Callee regs */
show_regs(regs);
/* Show stack trace if this Fatality happened in kernel mode */
if (!user_mode(regs))
show_stacktrace(current, regs);
kmsg_dump(KMSG_DUMP_PANIC);
}
void arc_set_sram_safe_area(unsigned long sram_start, unsigned long sram_end)
{
int retval;
sram_safe_start = sram_start;
sram_safe_size = sram_end - sram_safe_start;
printk("%s: sram_safe_start=%p, size=%#x\n", sram_safe_start, sram_safe_size);
/* Register the kmsg dumper - will be called if LHost, MuC and/or AuC crashes */
retval = kmsg_dump_register(&dumper_core_dump);
if (retval)
printk(KERN_ERR "%s: Could not register dumper_core_dump\n", __func__);
}
EXPORT_SYMBOL(arc_set_sram_safe_area);
void arc_set_troubleshoot_start_hook(arc_troubleshoot_start_hook_cbk start, void *ctx)
{
troubleshoot_start = start;
troubleshoot_ctx = ctx;
}
EXPORT_SYMBOL(arc_set_troubleshoot_start_hook);
#ifdef CONFIG_DEBUG_FS
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/mount.h>
#include <linux/pagemap.h>
#include <linux/init.h>
#include <linux/namei.h>
#include <linux/debugfs.h>
static struct dentry *test_dentry;
static struct dentry *test_dir;
static struct dentry *test_u32_dentry;
static u32 clr_on_read = 1;
#ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT
u32 numitlb, numdtlb, num_pte_not_present;
static int fill_display_data(char *kbuf)
{
size_t num = 0;
num += sprintf(kbuf + num, "I-TLB Miss %x\n", numitlb);
num += sprintf(kbuf + num, "D-TLB Miss %x\n", numdtlb);
num += sprintf(kbuf + num, "PTE not present %x\n", num_pte_not_present);
if (clr_on_read)
numitlb = numdtlb = num_pte_not_present = 0;
return num;
}
static int tlb_stats_open(struct inode *inode, struct file *file)
{
file->private_data = (void *)__get_free_page(GFP_KERNEL);
return 0;
}
/* called on user read(): display the counters */
static ssize_t tlb_stats_output(struct file *file, /* file descriptor */
char __user *user_buf, /* user buffer */
size_t len, /* length of buffer */
loff_t *offset) /* offset in the file */
{
size_t num;
char *kbuf = (char *)file->private_data;
/* All of the data can he shoved in one iteration */
if (*offset != 0)
return 0;
num = fill_display_data(kbuf);
/* simple_read_from_buffer() is helper for copy to user space
It copies up to @2 (num) bytes from kernel buffer @4 (kbuf) at offset
@3 (offset) into the user space address starting at @1 (user_buf).
@5 (len) is max size of user buffer
*/
return simple_read_from_buffer(user_buf, num, offset, kbuf, len);
}
/* called on user write : clears the counters */
static ssize_t tlb_stats_clear(struct file *file, const char __user *user_buf,
size_t length, loff_t *offset)
{
numitlb = numdtlb = num_pte_not_present = 0;
return length;
}
static int tlb_stats_close(struct inode *inode, struct file *file)
{
free_page((unsigned long)(file->private_data));
return 0;
}
static const struct file_operations tlb_stats_file_ops = {
.read = tlb_stats_output,
.write = tlb_stats_clear,
.open = tlb_stats_open,
.release = tlb_stats_close
};
#endif
static int __init arc_debugfs_init(void)
{
test_dir = debugfs_create_dir("arc", NULL);
#ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT
test_dentry = debugfs_create_file("tlb_stats", 0444, test_dir, NULL,
&tlb_stats_file_ops);
#endif
test_u32_dentry =
debugfs_create_u32("clr_on_read", 0444, test_dir, &clr_on_read);
return 0;
}
module_init(arc_debugfs_init);
static void __exit arc_debugfs_exit(void)
{
debugfs_remove(test_u32_dentry);
debugfs_remove(test_dentry);
debugfs_remove(test_dir);
}
module_exit(arc_debugfs_exit);
#endif