882 lines
25 KiB
C
882 lines
25 KiB
C
/*
|
|
* asm-e2k/e2k_debug.h
|
|
*/
|
|
#ifndef _E2K_DEBUG_H_
|
|
#define _E2K_DEBUG_H_
|
|
|
|
#ifndef __ASSEMBLY__
|
|
#include <linux/types.h>
|
|
#include <linux/kernel.h>
|
|
|
|
#include <asm/debug_print.h>
|
|
#include <asm/boot_profiling.h>
|
|
#include <asm/mas.h>
|
|
#include <asm/cpu_regs_access.h>
|
|
#include <asm/nmi.h>
|
|
#include <asm/ptrace.h>
|
|
#include <asm/current.h>
|
|
#include <asm/system.h>
|
|
#include <asm/machdep.h>
|
|
#include <asm/e2k_api.h>
|
|
#include <asm/mmu_fault.h>
|
|
#include <asm/io.h>
|
|
#include <asm/e2k.h>
|
|
#include <asm/pgtable_def.h>
|
|
|
|
#define IS_KERNEL_THREAD(task, mm) \
|
|
({ \
|
|
e2k_addr_t ps_base; \
|
|
\
|
|
ps_base = (e2k_addr_t)task_thread_info(task)->u_hw_stack.ps.base; \
|
|
((mm) == NULL || ps_base >= TASK_SIZE); \
|
|
})
|
|
|
|
extern void print_stack_frames(struct task_struct *task,
|
|
const struct pt_regs *pt_regs, int show_reg_window) __cold;
|
|
extern void print_mmap(struct task_struct *task) __cold;
|
|
extern void print_va_tlb(e2k_addr_t addr, int large_page) __cold;
|
|
extern void print_all_TC(const trap_cellar_t *TC, int TC_count) __cold;
|
|
extern void print_tc_record(const trap_cellar_t *tcellar, int num) __cold;
|
|
extern u64 print_all_TIRs(const e2k_tir_t *TIRs, u64 nr_TIRs) __cold;
|
|
extern void print_address_page_tables(unsigned long address,
|
|
int last_level_only) __cold;
|
|
extern void print_pt_regs(const pt_regs_t *regs) __cold;
|
|
|
|
__init extern void setup_stack_print(void);
|
|
|
|
static inline void print_address_tlb(unsigned long address)
|
|
{
|
|
print_va_tlb(address, 0);
|
|
print_va_tlb(pte_virt_offset(round_down(address, PTE_SIZE)), 0);
|
|
print_va_tlb(pmd_virt_offset(round_down(address, PMD_SIZE)), 0);
|
|
print_va_tlb(pud_virt_offset(round_down(address, PUD_SIZE)), 0);
|
|
}
|
|
|
|
/**
|
|
* *parse_chain_fn_t - function to be called on every frame in chain stack
|
|
* @crs - contents of current frame in chain stack
|
|
* @real_frame_addr - real address of current frame, can be used to modify frame
|
|
* @corrected_frame_addr - address of current frame where it would be in stack
|
|
* @flags - PCF_FLUSH_NEEDED if chain stack flush is needed before modifying,
|
|
* PCF_IRQS_CLOSE_NEEDED if irqs should be closed before modifying
|
|
* @arg - passed argument from parse_chain_stack()
|
|
*
|
|
* The distinction between @real_frame_addr and @corrected_frame_addr is
|
|
* important. Normally top of user chain stack is spilled to kernel chain
|
|
* stack, in which case @real_frame_addr points to spilled frame in kernel
|
|
* stack and @corrected_frame_addr holds the address in userspace where
|
|
* the frame _would_ be if it was spilled to userspace. In all other cases
|
|
* these two variables are equal.
|
|
*
|
|
* Generally @corrected_frame_addr is used in comparisons and
|
|
* @real_frame_addr is used for modifying stack in memory.
|
|
*
|
|
* IMPORTANT: if function wants to modify frame contents it must flush
|
|
* chain stack if @flush_needed is set.
|
|
*/
|
|
#define PCF_FLUSH_NEEDED 0x1
|
|
#define PCF_IRQS_CLOSE_NEEDED 0x2
|
|
typedef int (*parse_chain_fn_t)(e2k_mem_crs_t *crs,
|
|
unsigned long real_frame_addr,
|
|
unsigned long corrected_frame_addr,
|
|
int flags, void *arg);
|
|
#define PCS_USER 0x1
|
|
#define PCS_OPEN_IRQS 0x2
|
|
extern notrace long parse_chain_stack(int flags, struct task_struct *p,
|
|
parse_chain_fn_t func, void *arg);
|
|
|
|
extern notrace int ____parse_chain_stack(int flags, struct task_struct *p,
|
|
parse_chain_fn_t func, void *arg, unsigned long delta_user,
|
|
unsigned long top, unsigned long bottom,
|
|
bool *interrupts_enabled, unsigned long *irq_flags);
|
|
|
|
static inline int
|
|
native_do_parse_chain_stack(int flags, struct task_struct *p,
|
|
parse_chain_fn_t func, void *arg, unsigned long delta_user,
|
|
unsigned long top, unsigned long bottom,
|
|
bool *interrupts_enabled, unsigned long *irq_flags)
|
|
{
|
|
return ____parse_chain_stack(flags, p, func, arg, delta_user, top, bottom,
|
|
interrupts_enabled, irq_flags);
|
|
}
|
|
|
|
extern void *kernel_symtab;
|
|
extern long kernel_symtab_size;
|
|
extern void *kernel_strtab;
|
|
extern long kernel_strtab_size;
|
|
|
|
#define boot_kernel_symtab boot_get_vo_value(kernel_symtab)
|
|
#define boot_kernel_symtab_size boot_get_vo_value(kernel_symtab_size)
|
|
#define boot_kernel_strtab boot_get_vo_value(kernel_strtab)
|
|
#define boot_kernel_strtab_size boot_get_vo_value(kernel_strtab_size)
|
|
|
|
#define NATIVE_IS_USER_ADDR(task, addr) \
|
|
(((e2k_addr_t)(addr)) < NATIVE_TASK_SIZE)
|
|
#define NATIVE_GET_PHYS_ADDR(task, addr) \
|
|
({ \
|
|
e2k_addr_t phys; \
|
|
if (NATIVE_IS_USER_ADDR(task, addr)) \
|
|
phys = (unsigned long)user_address_to_pva(task, addr); \
|
|
else \
|
|
phys = (unsigned long)kernel_address_to_pva(addr); \
|
|
phys; \
|
|
})
|
|
|
|
/* Read instruction word (two syllables) from IP address */
|
|
static inline unsigned long
|
|
native_read_instr_on_IP(e2k_addr_t ip, e2k_addr_t phys_ip)
|
|
{
|
|
return NATIVE_READ_MAS_D(phys_ip, MAS_LOAD_PA);
|
|
}
|
|
/* Write modified instruction word at IP address */
|
|
static inline void
|
|
native_modify_instr_on_IP(e2k_addr_t ip, e2k_addr_t phys_ip,
|
|
unsigned long instr_word)
|
|
{
|
|
NATIVE_WRITE_MAS_D(phys_ip, instr_word, MAS_STORE_PA);
|
|
}
|
|
|
|
#define SIZE_PSP_STACK (16 * 4096)
|
|
#define DATA_STACK_PAGES 16
|
|
#define SIZE_DATA_STACK (DATA_STACK_PAGES * PAGE_SIZE)
|
|
|
|
#define SIZE_CHAIN_STACK KERNEL_PC_STACK_SIZE
|
|
|
|
/* Maximum number of user windows where a trap occured
|
|
* for which additional registers will be printed (ctpr's, lsr and ilcr). */
|
|
#define MAX_USER_TRAPS 12
|
|
|
|
/* Maximum number of pt_regs being marked as such
|
|
* when showing kernel data stack */
|
|
#define MAX_PT_REGS_SHOWN 30
|
|
|
|
typedef struct printed_trap_regs {
|
|
bool valid;
|
|
u64 frame;
|
|
e2k_ctpr_t ctpr1;
|
|
e2k_ctpr_t ctpr2;
|
|
e2k_ctpr_t ctpr3;
|
|
e2k_ctpr_hi_t ctpr1_hi;
|
|
e2k_ctpr_hi_t ctpr2_hi;
|
|
e2k_ctpr_hi_t ctpr3_hi;
|
|
u64 lsr;
|
|
u64 ilcr;
|
|
u64 lsr1;
|
|
u64 ilcr1;
|
|
u64 sbbp[SBBP_ENTRIES_NUM];
|
|
} printed_trap_regs_t;
|
|
|
|
typedef struct stack_regs {
|
|
bool used;
|
|
bool valid;
|
|
bool ignore_banner;
|
|
struct task_struct *task;
|
|
e2k_mem_crs_t crs;
|
|
e2k_pcsp_lo_t pcsp_lo;
|
|
e2k_pcsp_hi_t pcsp_hi;
|
|
e2k_psp_lo_t psp_lo;
|
|
e2k_psp_hi_t psp_hi;
|
|
void *base_psp_stack;
|
|
u64 user_size_psp_stack;
|
|
u64 orig_base_psp_stack_u;
|
|
u64 orig_base_psp_stack_k;
|
|
void *psp_stack_cache;
|
|
u64 size_psp_stack;
|
|
bool show_trap_regs;
|
|
bool show_user_regs;
|
|
struct printed_trap_regs trap[MAX_USER_TRAPS];
|
|
#ifdef CONFIG_GREGS_CONTEXT
|
|
struct global_regs gregs;
|
|
bool gregs_valid;
|
|
#endif
|
|
#ifdef CONFIG_DATA_STACK_WINDOW
|
|
bool show_k_data_stack;
|
|
void *base_k_data_stack;
|
|
void *k_data_stack_cache;
|
|
u64 size_k_data_stack;
|
|
void *real_k_data_stack_addr;
|
|
struct {
|
|
unsigned long addr;
|
|
bool valid;
|
|
} pt_regs[MAX_PT_REGS_SHOWN];
|
|
#endif
|
|
u64 size_chain_stack;
|
|
void *base_chain_stack;
|
|
u64 user_size_chain_stack;
|
|
u64 orig_base_chain_stack_u;
|
|
u64 orig_base_chain_stack_k;
|
|
void *chain_stack_cache;
|
|
} stack_regs_t;
|
|
|
|
extern void print_chain_stack(struct stack_regs *regs,
|
|
int show_reg_window);
|
|
extern void copy_stack_regs(struct task_struct *task,
|
|
const struct pt_regs *limit_regs, struct stack_regs *regs);
|
|
|
|
extern struct stack_regs stack_regs_cache[NR_CPUS];
|
|
extern int debug_userstack;
|
|
extern int print_window_regs;
|
|
|
|
#ifdef CONFIG_DATA_STACK_WINDOW
|
|
extern int debug_datastack;
|
|
#endif
|
|
|
|
#ifndef CONFIG_KVM_GUEST_KERNEL
|
|
/* it is native kernel without any virtualization */
|
|
/* or it is native host kernel with virtualization support */
|
|
/* or it is paravirtualized host and guest kernel */
|
|
|
|
static inline int
|
|
do_parse_chain_stack(int flags, struct task_struct *p,
|
|
parse_chain_fn_t func, void *arg, unsigned long delta_user,
|
|
unsigned long top, unsigned long bottom,
|
|
bool *interrupts_enabled, unsigned long *irq_flags)
|
|
{
|
|
return native_do_parse_chain_stack(flags, p, func, arg, delta_user,
|
|
top, bottom,
|
|
interrupts_enabled, irq_flags);
|
|
}
|
|
#endif /* !CONFIG_KVM_GUEST_KERNEL */
|
|
|
|
#ifndef CONFIG_VIRTUALIZATION
|
|
/* it is native kernel without any virtualization */
|
|
#define GET_PHYS_ADDR(task, addr) NATIVE_GET_PHYS_ADDR(task, addr)
|
|
#define print_all_guest_stacks() /* nothing to do */
|
|
#define print_guest_vcpu_stack(vcpu) /* nothing to do */
|
|
#define debug_guest_regs(task) false /* none any guests */
|
|
#define get_cpu_type_name() "CPU" /* real CPU */
|
|
|
|
/* Read instruction word (two syllables) from IP address */
|
|
static inline unsigned long
|
|
read_instr_on_IP(e2k_addr_t ip, e2k_addr_t phys_ip)
|
|
{
|
|
return native_read_instr_on_IP(ip, phys_ip);
|
|
}
|
|
/* Write modified instruction word at IP address */
|
|
static inline void
|
|
modify_instr_on_IP(e2k_addr_t ip, e2k_addr_t phys_ip,
|
|
unsigned long instr_word)
|
|
{
|
|
native_modify_instr_on_IP(ip, phys_ip, instr_word);
|
|
}
|
|
static inline void
|
|
print_guest_stack(struct task_struct *task,
|
|
stack_regs_t *const regs, bool show_reg_window)
|
|
{
|
|
return;
|
|
}
|
|
static inline void
|
|
host_ftrace_stop(void)
|
|
{
|
|
return;
|
|
}
|
|
static inline void
|
|
host_ftrace_dump(void)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static const bool kvm_debug = false;
|
|
#else /* CONFIG_VIRTUALIZATION */
|
|
/* it is native host kernel with virtualization support */
|
|
/* or it is paravirtualized host/guest kernel */
|
|
/* or it is native guest kernel */
|
|
#include <asm/kvm/debug.h>
|
|
#endif /* ! CONFIG_VIRTUALIZATION */
|
|
|
|
/*
|
|
* Print Chain Regs CR0 and CR1
|
|
*/
|
|
#undef DEBUG_CRs_MODE
|
|
#undef DebugCRs
|
|
#define DEBUG_CRs_MODE 0
|
|
#define DebugCRs(POS) if (DEBUG_CRs_MODE) print_chain_stack_regs(POS)
|
|
extern inline void
|
|
print_chain_stack_regs(char *point)
|
|
{
|
|
register e2k_cr0_hi_t cr0_hi = READ_CR0_HI_REG();
|
|
register e2k_cr0_lo_t cr0_lo = READ_CR0_LO_REG();
|
|
register e2k_cr1_hi_t cr1_hi = READ_CR1_HI_REG();
|
|
register e2k_cr1_lo_t cr1_lo = READ_CR1_LO_REG();
|
|
register e2k_psr_t psr;
|
|
|
|
printk("Procedure chain registers state");
|
|
if (point != NULL)
|
|
printk(" at %s :", point);
|
|
printk("\n");
|
|
|
|
printk(" CR0.hi ip 0x%lx\n", (long)AS_STRUCT(cr0_hi).ip << 3);
|
|
printk(" CR0.lo pf 0x%lx\n", (long)AS_STRUCT(cr0_lo).pf);
|
|
printk(" CR1.hi ussz 0x%x br 0x%x\n",
|
|
(int)AS_STRUCT(cr1_hi).ussz << 4, (int)AS_STRUCT(cr1_hi).br);
|
|
AS_WORD(psr) = AS_STRUCT(cr1_lo).psr;
|
|
printk(" CR1.lo: unmie %d nmie %d uie %d lw %d sge %d ie %d "
|
|
"pm %d\n",
|
|
(int)AS_STRUCT(psr).unmie,
|
|
(int)AS_STRUCT(psr).nmie,
|
|
(int)AS_STRUCT(psr).uie,
|
|
(int)AS_STRUCT(psr).lw,
|
|
(int)AS_STRUCT(psr).sge,
|
|
(int)AS_STRUCT(psr).ie,
|
|
(int)AS_STRUCT(psr).pm);
|
|
printk(" cuir 0x%x wbs 0x%x wpsz %d wfx %d ein %d\n",
|
|
(int)AS_STRUCT(cr1_lo).cuir, (int)AS_STRUCT(cr1_lo).wbs,
|
|
(int)AS_STRUCT(cr1_lo).wpsz, (int)AS_STRUCT(cr1_lo).wfx,
|
|
(int)AS_STRUCT(cr1_lo).ein);
|
|
}
|
|
|
|
/*
|
|
* Registers CPU
|
|
*/
|
|
|
|
#define DebugCpuR(str) if (DEBUG_CpuR_MODE) print_cpu_regs(str)
|
|
#define DebugSPRs(POS) if (DEBUG_SPRs_MODE) print_stack_pointers_reg(POS)
|
|
static inline void
|
|
print_cpu_regs(char *str)
|
|
{
|
|
pr_info("%s\n %s", str, "CPU REGS value:\n");
|
|
pr_info("usbr %llx\n", READ_SBR_REG_VALUE());
|
|
pr_info("usd.hi.curptr %llx usd.hi.size %llx\n",
|
|
READ_USD_HI_REG_VALUE() & 0xffffffff,
|
|
(READ_USD_HI_REG_VALUE() >> 32) & 0xffffffff);
|
|
pr_info("usd.lo.base 0x%llx\n",
|
|
READ_USD_LO_REG_VALUE() & 0xffffffffffff);
|
|
pr_info("psp.hi.ind %llx psp.hi.size %llx\n",
|
|
READ_PSP_HI_REG_VALUE() & 0xffffffff,
|
|
(READ_PSP_HI_REG_VALUE() >> 32) & 0xffffffff);
|
|
pr_info("psp.lo %llx\n", READ_PSP_LO_REG_VALUE());
|
|
pr_info("pcsp.hi.ind %llx pcsp.hi.size %llx\n",
|
|
READ_PCSP_HI_REG_VALUE() & 0xffffffff,
|
|
(READ_PCSP_HI_REG_VALUE() >> 32) & 0xffffffff);
|
|
pr_info("pcsp.lo %llx\n", READ_PCSP_LO_REG_VALUE());
|
|
pr_info("cr0.hi.ip %llx\n",
|
|
READ_CR0_HI_REG_VALUE() & ~0x7UL);
|
|
pr_info("cr1.hi.rbs %llx cr1.hi.rsz %llx\ncr1.hi.rcur %llx "
|
|
"cr1.hi.psz %llx cr1.hi.pcur %llx\ncr1.hi.ussz %llx\n",
|
|
READ_CR1_HI_REG_VALUE() & 0x3f,
|
|
READ_CR1_HI_REG_VALUE() >> 6 & 0x3f,
|
|
READ_CR1_HI_REG_VALUE() >> 12 & 0x3f,
|
|
READ_CR1_HI_REG_VALUE() >> 18 & 0x1f,
|
|
READ_CR1_HI_REG_VALUE() >> 23 & 0x1f,
|
|
READ_CR1_HI_REG_VALUE() >> 36 & 0xfffffff);
|
|
pr_info("cr1.lo.wpsz %llx cr1.lo.wbs %llx cr1.lo.psr %llx\n",
|
|
(READ_CR1_LO_REG_VALUE() >> 26) & 0x7f,
|
|
(READ_CR1_LO_REG_VALUE() >> 33) & 0x7f,
|
|
(READ_CR1_LO_REG_VALUE() >> 57) & 0x7);
|
|
pr_info("wd %llx\n", READ_WD_REG_VALUE());
|
|
}
|
|
|
|
extern inline void
|
|
print_stack_pointers_reg(char *point)
|
|
{
|
|
register e2k_psp_hi_t psp_hi = READ_PSP_HI_REG();
|
|
register e2k_psp_lo_t psp_lo = READ_PSP_LO_REG();
|
|
register e2k_pcsp_hi_t pcsp_hi = READ_PCSP_HI_REG();
|
|
register e2k_pcsp_lo_t pcsp_lo = READ_PCSP_LO_REG();
|
|
register long pshtp_reg = READ_PSHTP_REG_VALUE() &
|
|
0xffffUL;
|
|
register long pcshtp_reg = READ_PCSHTP_REG_SVALUE() &
|
|
0xffffUL;
|
|
|
|
pr_info("Stack pointer registers state");
|
|
if (point != NULL)
|
|
pr_info(" at %s :", point);
|
|
pr_info("\n");
|
|
pr_info(" USBR_base 0x%llx\n",
|
|
READ_USBR_REG().USBR_base);
|
|
pr_info(" USD_size 0x%x USD_p %d USD_base 0x%llx\n",
|
|
READ_USD_HI_REG().USD_hi_size,
|
|
READ_USD_LO_REG().USD_lo_p,
|
|
READ_USD_LO_REG().USD_lo_base);
|
|
|
|
pr_info(" PSP_size 0x%x PSP_ind 0x%x PSP_base 0x%lx PSHTP "
|
|
"0x%llx (0x%lx)\n",
|
|
psp_hi.PSP_hi_size,
|
|
psp_hi.PSP_hi_ind, pshtp_reg,
|
|
psp_lo.PSP_lo_base,
|
|
(long)(psp_hi.PSP_hi_ind + pshtp_reg));
|
|
if (psp_hi.PSP_hi_ind + pshtp_reg >= psp_hi.PSP_hi_size) {
|
|
pr_info("PROCEDURE STACK OVERFLOW 0x%lx > size 0x%x\n",
|
|
(long)(psp_hi.PSP_hi_ind + pshtp_reg),
|
|
psp_hi.PSP_hi_size);
|
|
}
|
|
pr_info(" PCSP_size 0x%x PCSP_ind 0x%x PCSP_base 0x%lx "
|
|
"PCSHTP 0x%llx (0x%lx)\n",
|
|
pcsp_hi.PCSP_hi_size,
|
|
pcsp_hi.PCSP_hi_ind, pcshtp_reg,
|
|
pcsp_lo.PCSP_lo_base,
|
|
(long)(pcsp_hi.PCSP_hi_ind + pcshtp_reg));
|
|
|
|
DebugCRs(point);
|
|
|
|
}
|
|
|
|
static inline int print_siginfo(siginfo_t *info, struct pt_regs *regs)
|
|
{
|
|
pr_info("Signal #%d info structure:\n"
|
|
" errno %d code %d pid %d uid %d\n"
|
|
" trap #%d address 0x%px\n",
|
|
info->si_signo, info->si_errno, info->si_code, info->si_pid,
|
|
info->si_uid, info->si_trapno, info->si_addr);
|
|
|
|
print_pt_regs(regs);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Print Switch Regs
|
|
*/
|
|
extern inline void
|
|
print_sw_regs(char *point, sw_regs_t *sw_regs)
|
|
{
|
|
pr_info("%s\n", point);
|
|
pr_info("top: %lx\n", sw_regs->top);
|
|
pr_info("usd_lo: %llx\n", AS_WORD(sw_regs->usd_lo));
|
|
pr_info("usd_hi: %llx\n", AS_WORD(sw_regs->usd_hi));
|
|
pr_info("psp_lo: %llx\n", AS_WORD(sw_regs->psp_lo));
|
|
pr_info("psp_hi: %llx\n", AS_WORD(sw_regs->psp_hi));
|
|
pr_info("pcsp_lo: %llx\n", AS_WORD(sw_regs->pcsp_lo));
|
|
pr_info("pcsp_hi: %llx\n", AS_WORD(sw_regs->pcsp_hi));
|
|
}
|
|
|
|
/*
|
|
* Print PAGE_FAULT (TC TRAP_CELLAR)
|
|
*/
|
|
|
|
#define DebugTC(a, b) \
|
|
if(DEBUG_PAGE_FAULT_MODE) print_tc_state(a, b);
|
|
#include <asm/mmu.h>
|
|
static inline void print_tc_state(const trap_cellar_t *tcellar, int num)
|
|
{
|
|
tc_fault_type_t ftype;
|
|
tc_dst_t dst ;
|
|
tc_opcode_t opcode;
|
|
u64 data;
|
|
u8 data_tag;
|
|
|
|
AW(dst) = AS(tcellar->condition).dst;
|
|
AW(opcode) = AS(tcellar->condition).opcode;
|
|
AW(ftype) = AS(tcellar->condition).fault_type;
|
|
|
|
load_value_and_tagd(&tcellar->data, &data, &data_tag);
|
|
|
|
printk("\n----------------------------"
|
|
"TRAP_CELLAR record #%d:"
|
|
"-----------------------------\n"
|
|
"address = 0x%016lx\n"
|
|
"data = 0x%016llx tag = 0x%x\n"
|
|
"condition = 0x%016llx:\n"
|
|
" dst = 0x%05x: address = 0x%04x, vl = 0x%x, vr = 0x%x\n"
|
|
" opcode = 0x%03x: fmt = 0x%02x, npsp = 0x%x\n\n"
|
|
" store = 0x%x, s_f = 0x%x, mas = 0x%x\n"
|
|
" root = 0x%x, scal = 0x%x, sru = 0x%x\n"
|
|
" chan = 0x%x, se = 0x%x, pm = 0x%x\n\n"
|
|
" fault_type = 0x%x:\n"
|
|
" intl_res_bits = %d MLT_trap = %d\n"
|
|
" ph_pr_page = %d global_sp = %d\n"
|
|
" io_page = %d isys_page = %d\n"
|
|
" prot_page = %d priv_page = %d\n"
|
|
" illegal_page = %d nwrite_page = %d\n"
|
|
" page_miss = %d ph_bound = %d\n"
|
|
" miss_lvl = 0x%x, num_align = 0x%x, empt = 0x%x\n"
|
|
" clw = 0x%x, rcv = 0x%x dst_rcv = 0x%x\n"
|
|
"----------------------------------------------------"
|
|
"---------------------------\n", num,
|
|
tcellar->address,
|
|
data, data_tag,
|
|
AW(tcellar->condition),
|
|
(u32)AW(dst),(u32)(AS(dst).address), (u32)(AS(dst).vl),
|
|
(u32)(AS(dst).vr),
|
|
(u32)AW(opcode), (u32)(AS(opcode).fmt),(u32)(AS(opcode).npsp),
|
|
(u32)AS(tcellar->condition).store,
|
|
(u32)AS(tcellar->condition).s_f,
|
|
(u32)AS(tcellar->condition).mas,
|
|
(u32)AS(tcellar->condition).root,
|
|
(u32)AS(tcellar->condition).scal,
|
|
(u32)AS(tcellar->condition).sru,
|
|
(u32)AS(tcellar->condition).chan,
|
|
(u32)AS(tcellar->condition).spec,
|
|
(u32)AS(tcellar->condition).pm,
|
|
(u32)AS(tcellar->condition).fault_type,
|
|
(u32)AS(ftype).intl_res_bits, (u32)(AS(ftype).exc_mem_lock),
|
|
(u32)AS(ftype).ph_pr_page, (u32)AS(ftype).global_sp,
|
|
(u32)AS(ftype).io_page, (u32)AS(ftype).isys_page,
|
|
(u32)AS(ftype).prot_page, (u32)AS(ftype).priv_page,
|
|
(u32)AS(ftype).illegal_page, (u32)AS(ftype).nwrite_page,
|
|
(u32)AS(ftype).page_miss, (u32)AS(ftype).ph_bound,
|
|
(u32)AS(tcellar->condition).miss_lvl,
|
|
(u32)AS(tcellar->condition).num_align,
|
|
(u32)AS(tcellar->condition).empt,
|
|
(u32)AS(tcellar->condition).clw,
|
|
(u32)AS(tcellar->condition).rcv,
|
|
(u32)AS(tcellar->condition).dst_rcv);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Set instruction data breakpoint at virtual address @addr.
|
|
*
|
|
* NOTE: breakpoint is set only for the current thread!
|
|
* To set it for the whole system, remove restoring of
|
|
* debug registers on a task switch.
|
|
*/
|
|
static inline int set_hardware_instr_breakpoint(u64 addr,
|
|
const int stop, const int cp_num, const int v)
|
|
{
|
|
u64 dibcr, dibsr, dibar = (u64) addr;
|
|
|
|
switch (cp_num) {
|
|
case 0: WRITE_DIBAR0_REG_VALUE(dibar); break;
|
|
case 1: WRITE_DIBAR1_REG_VALUE(dibar); break;
|
|
case 2: WRITE_DIBAR2_REG_VALUE(dibar); break;
|
|
case 3: WRITE_DIBAR3_REG_VALUE(dibar); break;
|
|
default:
|
|
if (__builtin_constant_p((cp_num)))
|
|
BUILD_BUG();
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Rewrite only the requested breakpoint. */
|
|
dibcr = (
|
|
!!(v) /* enable*/
|
|
| (1ULL << 1) /* generate exc_instr_debug */
|
|
) << (cp_num * 2);
|
|
dibcr |= (!!stop << 9);
|
|
dibcr |= READ_DIBCR_REG_VALUE() & ~E2K_DIBCR_MASK(cp_num);
|
|
|
|
dibsr = READ_DIBSR_REG_VALUE() & ~E2K_DIBSR_MASK(cp_num);
|
|
|
|
WRITE_DIBCR_REG_VALUE(dibcr);
|
|
WRITE_DIBSR_REG_VALUE(dibsr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Set hardware data breakpoint at virtual address @addr.
|
|
*
|
|
* NOTE: breakpoint is set only for the current thread!
|
|
* To set it for the whole system, remove restoring of
|
|
* debug registers on a task switch.
|
|
*/
|
|
static inline int set_hardware_data_breakpoint(u64 addr, u64 size,
|
|
const int write, const int read,
|
|
const int stop, const int cp_num, const int v)
|
|
{
|
|
u64 ddbcr, ddbsr, ddbar = (u64) addr;
|
|
|
|
switch (size) {
|
|
case 1:
|
|
size = 1;
|
|
break;
|
|
case 2:
|
|
size = 2;
|
|
break;
|
|
case 4:
|
|
size = 3;
|
|
break;
|
|
case 8:
|
|
size = 4;
|
|
break;
|
|
case 16:
|
|
size = 5;
|
|
break;
|
|
default:
|
|
if (__builtin_constant_p((size)))
|
|
BUILD_BUG();
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (cp_num) {
|
|
case 0:
|
|
WRITE_DDBAR0_REG_VALUE(ddbar);
|
|
break;
|
|
case 1:
|
|
WRITE_DDBAR1_REG_VALUE(ddbar);
|
|
break;
|
|
case 2:
|
|
WRITE_DDBAR2_REG_VALUE(ddbar);
|
|
break;
|
|
case 3:
|
|
WRITE_DDBAR3_REG_VALUE(ddbar);
|
|
break;
|
|
default:
|
|
if (__builtin_constant_p((cp_num)))
|
|
BUILD_BUG();
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Rewrite only the requested breakpoint. */
|
|
ddbcr = (
|
|
!!(v) /* enable*/
|
|
| (0ULL << 1) /* primary space */
|
|
| ((!!write) << 2)
|
|
| ((!!read) << 3)
|
|
| (size << 4)
|
|
| (1ULL << 7) /* sync */
|
|
| (1ULL << 8) /* speculative */
|
|
| (1ULL << 9) /* ap */
|
|
| (1ULL << 10) /* spill/fill */
|
|
| (1ULL << 11) /* hardware */
|
|
| (1ULL << 12) /* generate exc_data_debug */
|
|
) << (cp_num * 14);
|
|
ddbcr |= READ_DDBCR_REG_VALUE() & ~E2K_DDBCR_MASK(cp_num);
|
|
|
|
ddbsr = READ_DDBSR_REG_VALUE() & ~E2K_DDBSR_MASK(cp_num);
|
|
|
|
WRITE_DDBCR_REG_VALUE(ddbcr);
|
|
WRITE_DDBSR_REG_VALUE(ddbsr);
|
|
if (stop) {
|
|
e2k_dibcr_t dibcr;
|
|
|
|
dibcr = READ_DIBCR_REG();
|
|
dibcr.stop = 1;
|
|
WRITE_DIBCR_REG(dibcr);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int reset_hardware_data_breakpoint(void *addr)
|
|
{
|
|
u64 ddbcr, ddbsr, ddbar;
|
|
int cp_num;
|
|
|
|
ddbcr = READ_DDBCR_REG_VALUE();
|
|
for (cp_num = 0; cp_num < 4; cp_num++, ddbcr >>= 14) {
|
|
if (!(ddbcr & 0x1)) /* valid */
|
|
continue;
|
|
switch (cp_num) {
|
|
case 0:
|
|
ddbar = READ_DDBAR0_REG_VALUE();
|
|
break;
|
|
case 1:
|
|
ddbar = READ_DDBAR1_REG_VALUE();
|
|
break;
|
|
case 2:
|
|
ddbar = READ_DDBAR2_REG_VALUE();
|
|
break;
|
|
case 3:
|
|
ddbar = READ_DDBAR3_REG_VALUE();
|
|
break;
|
|
default:
|
|
if (__builtin_constant_p((cp_num)))
|
|
BUILD_BUG();
|
|
return -EINVAL;
|
|
}
|
|
if ((ddbar & E2K_VA_MASK) == ((e2k_addr_t)addr & E2K_VA_MASK))
|
|
break;
|
|
}
|
|
if (cp_num >= 4)
|
|
return cp_num;
|
|
|
|
/* Reset only the requested breakpoint. */
|
|
ddbcr = READ_DDBCR_REG_VALUE() & (~(0x3FFFULL << (cp_num * 14)));
|
|
ddbsr = READ_DDBSR_REG_VALUE() & (~(0x3FFFULL << (cp_num * 14)));
|
|
mb(); /* wait for completion of all load/store in progress */
|
|
WRITE_DDBCR_REG_VALUE(ddbcr);
|
|
WRITE_DDBSR_REG_VALUE(ddbsr);
|
|
|
|
switch (cp_num) {
|
|
case 0:
|
|
WRITE_DDBAR0_REG_VALUE(0);
|
|
break;
|
|
case 1:
|
|
WRITE_DDBAR1_REG_VALUE(0);
|
|
break;
|
|
case 2:
|
|
WRITE_DDBAR2_REG_VALUE(0);
|
|
break;
|
|
case 3:
|
|
WRITE_DDBAR3_REG_VALUE(0);
|
|
break;
|
|
default:
|
|
if (__builtin_constant_p((cp_num)))
|
|
BUILD_BUG();
|
|
return -EINVAL;
|
|
}
|
|
|
|
return cp_num;
|
|
}
|
|
|
|
struct data_breakpoint_params {
|
|
void *address;
|
|
u64 size;
|
|
int write;
|
|
int read;
|
|
int stop;
|
|
int cp_num;
|
|
};
|
|
extern void nmi_set_hardware_data_breakpoint(
|
|
struct data_breakpoint_params *params);
|
|
/**
|
|
* set_hardware_data_breakpoint_on_each_cpu() - set hardware data breakpoint
|
|
* on every online cpu.
|
|
* @addr: virtual address of the breakpoint.
|
|
*
|
|
* This uses non-maskable interrupts to set the breakpoint for the whole
|
|
* system atomically. That is, by the time this function returns the
|
|
* breakpoint will be set everywhere.
|
|
*/
|
|
#define set_hardware_data_breakpoint_on_each_cpu( \
|
|
addr, sz, wr, rd, st, cp) \
|
|
({ \
|
|
struct data_breakpoint_params params; \
|
|
MAYBE_BUILD_BUG_ON((sz) != 1 && (sz) != 2 && (sz) != 4 \
|
|
&& (sz) != 8 && (sz) != 16); \
|
|
MAYBE_BUILD_BUG_ON((cp) != 0 && (cp) != 1 \
|
|
&& (cp) != 2 && (cp) != 3); \
|
|
params.address = (addr); \
|
|
params.size = (sz); \
|
|
params.write = (wr); \
|
|
params.read = (rd); \
|
|
params.stop = (st); \
|
|
params.cp_num = (cp); \
|
|
nmi_on_each_cpu(nmi_set_hardware_data_breakpoint, ¶ms, 1, 0); \
|
|
})
|
|
|
|
|
|
extern int jtag_stop_var;
|
|
static inline void jtag_stop(void)
|
|
{
|
|
set_hardware_data_breakpoint((u64) &jtag_stop_var,
|
|
sizeof(jtag_stop_var), 1, 0, 1, 3, 1);
|
|
|
|
jtag_stop_var = 0;
|
|
|
|
/* Wait for the hardware to stop us */
|
|
wmb();
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_USE_AAU
|
|
#include <asm/aau_regs_types.h>
|
|
|
|
/* print some aux. & AAU registers */
|
|
static inline void
|
|
print_aau_regs(char *str, e2k_aau_t *context, struct pt_regs *regs,
|
|
struct thread_info *ti)
|
|
{
|
|
int i;
|
|
bool old_iset;
|
|
|
|
old_iset = (machine.native_iset_ver < E2K_ISET_V5);
|
|
|
|
if (str)
|
|
pr_info("%s\n", str);
|
|
|
|
pr_info("\naasr register = 0x%x (state: %s, iab: %d, stb: %d)\n"
|
|
"ctpr2 = 0x%llx\n"
|
|
"lsr = 0x%llx\n"
|
|
"ilcr = 0x%llx\n",
|
|
AW(regs->aasr),
|
|
AAU_NULL(regs->aasr) ? "NULL" :
|
|
AAU_READY(regs->aasr) ? "READY" :
|
|
AAU_ACTIVE(regs->aasr) ? "ACTIVE" :
|
|
AAU_STOPPED(regs->aasr) ? "STOPPED" :
|
|
"undefined",
|
|
regs->aasr.iab, regs->aasr.stb,
|
|
AW(regs->ctpr2), regs->lsr, regs->ilcr);
|
|
|
|
if (AAU_STOPPED(regs->aasr)) {
|
|
pr_info("aaldv = 0x%llx\n"
|
|
"aaldm = 0x%llx\n",
|
|
AW(context->aaldv), AW(context->aaldm));
|
|
} else {
|
|
/* AAU can be in active state in kernel - automatic
|
|
* stop by hardware upon trap enter does not work. */
|
|
pr_info("AAU is not in STOPPED or ACTIVE states, AALDV and "
|
|
"AALDM will not be printed\n");
|
|
}
|
|
|
|
if (regs->aasr.iab) {
|
|
for (i = 0; i < 32; i++) {
|
|
pr_info("aad[%d].hi = 0x%llx ", i,
|
|
AW(context->aads[i]).hi);
|
|
pr_info("aad[%d].lo = 0x%llx\n", i,
|
|
AW(context->aads[i]).lo);
|
|
}
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
pr_info("aaincr[%d] = 0x%llx\n", i, (old_iset) ?
|
|
(u32) context->aaincrs[i] :
|
|
context->aaincrs[i]);
|
|
}
|
|
pr_info("aaincr_tags = 0x%x\n", context->aaincr_tags);
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
pr_info("aaind[%d] = 0x%llx\n", i, (old_iset) ?
|
|
(u64) (u32) context->aainds[i] :
|
|
context->aainds[i]);
|
|
}
|
|
pr_info("aaind_tags = 0x%x\n", context->aaind_tags);
|
|
} else {
|
|
pr_info("IAB flag in AASR is not set, following registers "
|
|
"will not be printed: AAD, AAIND, AAIND_TAGS, "
|
|
"AAINCR, AAINCR_TAGS\n");
|
|
}
|
|
|
|
if (regs->aasr.stb) {
|
|
for (i = 0; i < 16; i++) {
|
|
pr_info("aasti[%d] = 0x%llx\n", i, (old_iset) ?
|
|
(u64) (u32) context->aastis[i] :
|
|
context->aastis[i]);
|
|
}
|
|
pr_info("aasti_tags = 0x%x\n", context->aasti_tags);
|
|
} else {
|
|
pr_info("STB flag in AASR is not set, following registers "
|
|
"will not be printed: AASTI, AASTI_TAGS\n");
|
|
}
|
|
|
|
if (ti) {
|
|
for (i = 0; i < 32; i++) {
|
|
pr_info("aaldi[%d] = 0x%llx ", i, (old_iset) ?
|
|
(u64) (u32) context->aaldi[i] :
|
|
context->aaldi[i]);
|
|
pr_info("aaldi[%d] = 0x%llx\n", i+32, (old_iset) ?
|
|
(u64) (u32) context->aaldi[i+32] :
|
|
context->aaldi[i+32]);
|
|
}
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
pr_info("aalda[%d] = 0x%x ", i, AW(ti->aalda[i]));
|
|
pr_info("aalda[%d] = 0x%x\n", i + 32,
|
|
AW(ti->aalda[i+32]));
|
|
}
|
|
}
|
|
|
|
pr_info("aafstr = 0x%x\n", read_aafstr_reg_value());
|
|
pr_info("aafstr = 0x%x\n", context->aafstr);
|
|
}
|
|
#endif /* CONFIG_USE_AAU */
|
|
|
|
extern int debug_signal;
|
|
#define SIGDEBUG_PRINT(format, ...) \
|
|
do { \
|
|
if (debug_signal) \
|
|
pr_info("%s (pid=%d): " format, \
|
|
current->comm, current->pid, ##__VA_ARGS__); \
|
|
} while (0)
|
|
|
|
extern void __debug_signal_print(const char *message,
|
|
struct pt_regs *regs, bool print_stack) __cold;
|
|
|
|
static inline void debug_signal_print(const char *message,
|
|
struct pt_regs *regs, bool print_stack)
|
|
{
|
|
if (likely(!debug_signal))
|
|
return;
|
|
|
|
__debug_signal_print(message, regs, print_stack);
|
|
}
|
|
|
|
extern int debug_trap;
|
|
|
|
#endif /* !(__ASSEMBLY__) */
|
|
|
|
#endif /* _E2K_DEBUG_H_ */
|