Mailing List Archive

Enable stack trace on Xen BUG (by Isaku Yamahata)
# HG changeset patch
# User djm@kirby.fc.hp.com
# Node ID 45c4e735fc8c5dc39a19cb2e52a48585bbf96b02
# Parent f89906acd9f6f58b28d673403d4a592e18cacb09
Enable stack trace on Xen BUG (by Isaku Yamahata)

diff -r f89906acd9f6 -r 45c4e735fc8c xen/arch/ia64/Makefile
--- a/xen/arch/ia64/Makefile Fri Dec 30 22:11:08 2005
+++ b/xen/arch/ia64/Makefile Sat Dec 31 05:40:13 2005
@@ -23,6 +23,13 @@
__divsi3.o __udivsi3.o __modsi3.o __umodsi3.o \
__divdi3.o __udivdi3.o __moddi3.o __umoddi3.o

+# xen stack unwinder
+# unwind_decoder.c is included in unwind.c
+OBJS += unwind.o
+#unwind.o: CFLAGS += -DUNW_DEBUG=4
+
+OBJS += process-linux-xen.o
+
# perfmon.o
# unwind.o needed for kernel unwinding (rare)

@@ -31,11 +38,26 @@
# remove following line if not privifying in memory
# OBJS += privify.o

-default: $(OBJS) head.o xen.lds.s
- $(LD) -r -o arch.o $(OBJS)
+default: $(TARGET)
+
+$(CURDIR)/arch.o: $(OBJS)
+ $(LD) -r -o $@ $(OBJS)
+
+$(TARGET)-syms: $(ALL_OBJS) head.o xen.lds.s
$(LD) $(LDFLAGS) -T $(BASEDIR)/arch/$(TARGET_ARCH)/xen.lds.s -N \
- -Map map.out head.o $(ALL_OBJS) -o $(TARGET)-syms
- $(OBJCOPY) -R .note -R .comment -S $(TARGET)-syms $(TARGET)
+ -Map map.out head.o $(ALL_OBJS) -o $@
+ $(NM) -n $@ | $(BASEDIR)/tools/symbols > $(BASEDIR)/xen-syms.S
+ $(MAKE) $(BASEDIR)/xen-syms.o
+ $(LD) $(LDFLAGS) -T $(BASEDIR)/arch/$(TARGET_ARCH)/xen.lds.s -N \
+ -Map map.out head.o $(ALL_OBJS) $(BASEDIR)/xen-syms.o -o $@
+ $(NM) -n $@ | $(BASEDIR)/tools/symbols >$(BASEDIR)/xen-syms.S
+ $(MAKE) $(BASEDIR)/xen-syms.o
+ $(LD) $(LDFLAGS) -T $(BASEDIR)/arch/$(TARGET_ARCH)/xen.lds.s -N \
+ -Map map.out head.o $(ALL_OBJS) $(BASEDIR)/xen-syms.o -o $@
+ rm -f $(BASEDIR)/xen-syms.S $(BASEDIR)/xen-syms.o
+
+$(TARGET): $(TARGET)-syms
+ $(OBJCOPY) -R .note -R .comment -S $(TARGET)-syms $@
$(NM) -n $(TARGET)-syms | grep -v '\( [aUw] \)\|\(__crc_\)\|\( \$[adt]\)'\
> $(BASEDIR)/System.map

diff -r f89906acd9f6 -r 45c4e735fc8c xen/arch/ia64/linux-xen/entry.S
--- a/xen/arch/ia64/linux-xen/entry.S Fri Dec 30 22:11:08 2005
+++ b/xen/arch/ia64/linux-xen/entry.S Sat Dec 31 05:40:13 2005
@@ -1417,7 +1417,6 @@
br.cond.sptk.many rp // goes to ia64_leave_kernel
END(ia64_prepare_handle_unaligned)

-#ifndef XEN
//
// unw_init_running(void (*callback)(info, arg), void *arg)
//
@@ -1463,6 +1462,7 @@
br.ret.sptk.many rp
END(unw_init_running)

+#ifndef XEN
.rodata
.align 8
.globl sys_call_table
diff -r f89906acd9f6 -r 45c4e735fc8c xen/arch/ia64/vmx/vmx_process.c
--- a/xen/arch/ia64/vmx/vmx_process.c Fri Dec 30 22:11:08 2005
+++ b/xen/arch/ia64/vmx/vmx_process.c Sat Dec 31 05:40:13 2005
@@ -53,6 +53,7 @@
#define INITIAL_PSR_VALUE_AT_INTERRUPTION 0x0000001808028034


+extern void die_if_kernel(char *str, struct pt_regs *regs, long err);
extern void rnat_consumption (VCPU *vcpu);
#define DOMN_PAL_REQUEST 0x110000

@@ -185,8 +186,11 @@
}else if(iim == DOMN_PAL_REQUEST){
pal_emul(current);
vmx_vcpu_increment_iip(current);
- } else
+ } else {
+ if (iim == 0)
+ die_if_kernel("bug check", regs, iim);
vmx_reflect_interruption(ifa,isr,iim,11,regs);
+ }
}


diff -r f89906acd9f6 -r 45c4e735fc8c xen/arch/ia64/xen/hyperprivop.S
--- a/xen/arch/ia64/xen/hyperprivop.S Fri Dec 30 22:11:08 2005
+++ b/xen/arch/ia64/xen/hyperprivop.S Sat Dec 31 05:40:13 2005
@@ -543,6 +543,13 @@
extr.u r21=r30,IA64_PSR_PP_BIT,1 ;;
cmp.ne p7,p0=r21,r0 ;;
(p7) br.spnt.few dispatch_break_fault ;;
+ movl r20=IA64_PSR_CPL ;;
+ and r22=r20,r30 ;;
+ cmp.ne p7,p0=r22,r0
+(p7) br.spnt.many 1f ;;
+ cmp.eq p7,p0=r17,r0
+(p7) br.spnt.few dispatch_break_fault ;;
+1:
#if 1 /* special handling in case running on simulator */
movl r20=first_break;;
ld4 r23=[r20];;
diff -r f89906acd9f6 -r 45c4e735fc8c xen/arch/ia64/xen/ivt.S
--- a/xen/arch/ia64/xen/ivt.S Fri Dec 30 22:11:08 2005
+++ b/xen/arch/ia64/xen/ivt.S Sat Dec 31 05:40:13 2005
@@ -839,6 +839,8 @@
mov r17=cr.iim
mov r31=pr
;;
+ cmp.eq p7,p0=r17,r0
+(p7) br.spnt.few dispatch_break_fault ;;
movl r18=XSI_PSR_IC
;;
ld8 r19=[r18]
diff -r f89906acd9f6 -r 45c4e735fc8c xen/arch/ia64/xen/process.c
--- a/xen/arch/ia64/xen/process.c Fri Dec 30 22:11:08 2005
+++ b/xen/arch/ia64/xen/process.c Sat Dec 31 05:40:13 2005
@@ -33,6 +33,7 @@
#include <xen/multicall.h>

extern unsigned long vcpu_get_itir_on_fault(struct vcpu *, UINT64);
+extern void die_if_kernel(char *str, struct pt_regs *regs, long err);

extern unsigned long dom0_start, dom0_size;

@@ -686,6 +687,8 @@
vcpu_increment_iip(current);
}
else {
+ if (iim == 0)
+ die_if_kernel("bug check", regs, iim);
PSCB(v,iim) = iim;
reflect_interruption(isr,regs,IA64_BREAK_VECTOR);
}
diff -r f89906acd9f6 -r 45c4e735fc8c xen/arch/ia64/xen/xenmisc.c
--- a/xen/arch/ia64/xen/xenmisc.c Fri Dec 30 22:11:08 2005
+++ b/xen/arch/ia64/xen/xenmisc.c Sat Dec 31 05:40:13 2005
@@ -25,7 +25,6 @@
int phys_proc_id[NR_CPUS];
unsigned long loops_per_jiffy = (1<<12); // from linux/init/main.c

-void unw_init(void) { printf("unw_init() skipped (NEED FOR KERNEL UNWIND)\n"); }
void ia64_mca_init(void) { printf("ia64_mca_init() skipped (Machine check abort handling)\n"); }
void ia64_mca_cpu_init(void *x) { }
void ia64_patch_mckinley_e9(unsigned long a, unsigned long b) { }
@@ -180,11 +179,6 @@
// from arch/ia64/traps.c
///////////////////////////////

-void show_registers(struct pt_regs *regs)
-{
- printf("*** ADD REGISTER DUMP HERE FOR DEBUGGING\n");
-}
-
int is_kernel_text(unsigned long addr)
{
extern char _stext[], _etext[];
@@ -236,7 +230,13 @@

void die_if_kernel(char *str, struct pt_regs *regs, long err) /* __attribute__ ((noreturn)) */
{
- printk("die_if_kernel: called, not implemented\n");
+ if (user_mode(regs))
+ return;
+
+ printk("%s: %s %ld\n", __func__, str, err);
+ debugtrace_dump();
+ show_registers(regs);
+ domain_crash_synchronous();
}

long
diff -r f89906acd9f6 -r 45c4e735fc8c xen/arch/ia64/linux-xen/process-linux-xen.c
--- /dev/null Fri Dec 30 22:11:08 2005
+++ b/xen/arch/ia64/linux-xen/process-linux-xen.c Sat Dec 31 05:40:13 2005
@@ -0,0 +1,848 @@
+/*
+ * Architecture-specific setup.
+ *
+ * Copyright (C) 1998-2003 Hewlett-Packard Co
+ * David Mosberger-Tang <davidm@hpl.hp.com>
+ * 04/11/17 Ashok Raj <ashok.raj@intel.com> Added CPU Hotplug Support
+ */
+#ifdef XEN
+#include <xen/types.h>
+#include <xen/lib.h>
+#include <xen/symbols.h>
+#include <xen/smp.h>
+#include <asm/uaccess.h>
+#include <asm/processor.h>
+#include <asm/ptrace.h>
+#include <asm/unwind.h>
+#else
+#define __KERNEL_SYSCALLS__ /* see <asm/unistd.h> */
+#include <linux/config.h>
+
+#include <linux/cpu.h>
+#include <linux/pm.h>
+#include <linux/elf.h>
+#include <linux/errno.h>
+#include <linux/kallsyms.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/personality.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/stddef.h>
+#include <linux/thread_info.h>
+#include <linux/unistd.h>
+#include <linux/efi.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kprobes.h>
+
+#include <asm/cpu.h>
+#include <asm/delay.h>
+#include <asm/elf.h>
+#include <asm/ia32.h>
+#include <asm/irq.h>
+#include <asm/pgalloc.h>
+#include <asm/processor.h>
+#include <asm/sal.h>
+#include <asm/tlbflush.h>
+#include <asm/uaccess.h>
+#include <asm/unwind.h>
+#include <asm/user.h>
+
+#include "entry.h"
+
+#ifdef CONFIG_PERFMON
+# include <asm/perfmon.h>
+#endif
+
+#include "sigframe.h"
+
+void (*ia64_mark_idle)(int);
+static DEFINE_PER_CPU(unsigned int, cpu_idle_state);
+
+unsigned long boot_option_idle_override = 0;
+EXPORT_SYMBOL(boot_option_idle_override);
+#endif
+
+void
+ia64_do_show_stack (struct unw_frame_info *info, void *arg)
+{
+ unsigned long ip, sp, bsp;
+ char buf[128]; /* don't make it so big that it overflows the stack! */
+
+ printk("\nCall Trace:\n");
+ do {
+ unw_get_ip(info, &ip);
+ if (ip == 0)
+ break;
+
+ unw_get_sp(info, &sp);
+ unw_get_bsp(info, &bsp);
+ snprintf(buf, sizeof(buf),
+ " [<%016lx>] %%s\n"
+ " sp=%016lx bsp=%016lx\n",
+ ip, sp, bsp);
+ print_symbol(buf, ip);
+ } while (unw_unwind(info) >= 0);
+}
+
+void
+show_stack (struct task_struct *task, unsigned long *sp)
+{
+ if (!task)
+ unw_init_running(ia64_do_show_stack, NULL);
+ else {
+ struct unw_frame_info info;
+
+ unw_init_from_blocked_task(&info, task);
+ ia64_do_show_stack(&info, NULL);
+ }
+}
+
+#ifndef XEN
+void
+dump_stack (void)
+{
+ show_stack(NULL, NULL);
+}
+
+EXPORT_SYMBOL(dump_stack);
+#endif
+
+#ifdef XEN
+void
+show_registers(struct pt_regs *regs)
+#else
+void
+show_regs (struct pt_regs *regs)
+#endif
+{
+ unsigned long ip = regs->cr_iip + ia64_psr(regs)->ri;
+
+#ifndef XEN
+ print_modules();
+ printk("\nPid: %d, CPU %d, comm: %20s\n", current->pid, smp_processor_id(), current->comm);
+ printk("psr : %016lx ifs : %016lx ip : [<%016lx>] %s\n",
+ regs->cr_ipsr, regs->cr_ifs, ip, print_tainted());
+#else
+ printk("\nCPU %d\n", smp_processor_id());
+ printk("psr : %016lx ifs : %016lx ip : [<%016lx>]\n",
+ regs->cr_ipsr, regs->cr_ifs, ip);
+#endif
+ print_symbol("ip is at %s\n", ip);
+ printk("unat: %016lx pfs : %016lx rsc : %016lx\n",
+ regs->ar_unat, regs->ar_pfs, regs->ar_rsc);
+ printk("rnat: %016lx bsps: %016lx pr : %016lx\n",
+ regs->ar_rnat, regs->ar_bspstore, regs->pr);
+ printk("ldrs: %016lx ccv : %016lx fpsr: %016lx\n",
+ regs->loadrs, regs->ar_ccv, regs->ar_fpsr);
+ printk("csd : %016lx ssd : %016lx\n", regs->ar_csd, regs->ar_ssd);
+ printk("b0 : %016lx b6 : %016lx b7 : %016lx\n", regs->b0, regs->b6, regs->b7);
+ printk("f6 : %05lx%016lx f7 : %05lx%016lx\n",
+ regs->f6.u.bits[1], regs->f6.u.bits[0],
+ regs->f7.u.bits[1], regs->f7.u.bits[0]);
+ printk("f8 : %05lx%016lx f9 : %05lx%016lx\n",
+ regs->f8.u.bits[1], regs->f8.u.bits[0],
+ regs->f9.u.bits[1], regs->f9.u.bits[0]);
+ printk("f10 : %05lx%016lx f11 : %05lx%016lx\n",
+ regs->f10.u.bits[1], regs->f10.u.bits[0],
+ regs->f11.u.bits[1], regs->f11.u.bits[0]);
+
+ printk("r1 : %016lx r2 : %016lx r3 : %016lx\n", regs->r1, regs->r2, regs->r3);
+ printk("r8 : %016lx r9 : %016lx r10 : %016lx\n", regs->r8, regs->r9, regs->r10);
+ printk("r11 : %016lx r12 : %016lx r13 : %016lx\n", regs->r11, regs->r12, regs->r13);
+ printk("r14 : %016lx r15 : %016lx r16 : %016lx\n", regs->r14, regs->r15, regs->r16);
+ printk("r17 : %016lx r18 : %016lx r19 : %016lx\n", regs->r17, regs->r18, regs->r19);
+ printk("r20 : %016lx r21 : %016lx r22 : %016lx\n", regs->r20, regs->r21, regs->r22);
+ printk("r23 : %016lx r24 : %016lx r25 : %016lx\n", regs->r23, regs->r24, regs->r25);
+ printk("r26 : %016lx r27 : %016lx r28 : %016lx\n", regs->r26, regs->r27, regs->r28);
+ printk("r29 : %016lx r30 : %016lx r31 : %016lx\n", regs->r29, regs->r30, regs->r31);
+
+ if (user_mode(regs)) {
+ /* print the stacked registers */
+ unsigned long val, *bsp, ndirty;
+ int i, sof, is_nat = 0;
+
+ sof = regs->cr_ifs & 0x7f; /* size of frame */
+ ndirty = (regs->loadrs >> 19);
+ bsp = ia64_rse_skip_regs((unsigned long *) regs->ar_bspstore, ndirty);
+ for (i = 0; i < sof; ++i) {
+ get_user(val, (unsigned long __user *) ia64_rse_skip_regs(bsp, i));
+ printk("r%-3u:%c%016lx%s", 32 + i, is_nat ? '*' : ' ', val,
+ ((i == sof - 1) || (i % 3) == 2) ? "\n" : " ");
+ }
+ } else
+ show_stack(NULL, NULL);
+}
+
+#ifndef XEN
+void
+do_notify_resume_user (sigset_t *oldset, struct sigscratch *scr, long in_syscall)
+{
+ if (fsys_mode(current, &scr->pt)) {
+ /* defer signal-handling etc. until we return to privilege-level 0. */
+ if (!ia64_psr(&scr->pt)->lp)
+ ia64_psr(&scr->pt)->lp = 1;
+ return;
+ }
+
+#ifdef CONFIG_PERFMON
+ if (current->thread.pfm_needs_checking)
+ pfm_handle_work();
+#endif
+
+ /* deal with pending signal delivery */
+ if (test_thread_flag(TIF_SIGPENDING))
+ ia64_do_signal(oldset, scr, in_syscall);
+}
+
+static int pal_halt = 1;
+static int can_do_pal_halt = 1;
+
+static int __init nohalt_setup(char * str)
+{
+ pal_halt = can_do_pal_halt = 0;
+ return 1;
+}
+__setup("nohalt", nohalt_setup);
+
+void
+update_pal_halt_status(int status)
+{
+ can_do_pal_halt = pal_halt && status;
+}
+
+/*
+ * We use this if we don't have any better idle routine..
+ */
+void
+default_idle (void)
+{
+ local_irq_enable();
+ while (!need_resched())
+ if (can_do_pal_halt)
+ safe_halt();
+ else
+ cpu_relax();
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+/* We don't actually take CPU down, just spin without interrupts. */
+static inline void play_dead(void)
+{
+ extern void ia64_cpu_local_tick (void);
+ unsigned int this_cpu = smp_processor_id();
+
+ /* Ack it */
+ __get_cpu_var(cpu_state) = CPU_DEAD;
+
+ max_xtp();
+ local_irq_disable();
+ idle_task_exit();
+ ia64_jump_to_sal(&sal_boot_rendez_state[this_cpu]);
+ /*
+ * The above is a point of no-return, the processor is
+ * expected to be in SAL loop now.
+ */
+ BUG();
+}
+#else
+static inline void play_dead(void)
+{
+ BUG();
+}
+#endif /* CONFIG_HOTPLUG_CPU */
+
+void cpu_idle_wait(void)
+{
+ unsigned int cpu, this_cpu = get_cpu();
+ cpumask_t map;
+
+ set_cpus_allowed(current, cpumask_of_cpu(this_cpu));
+ put_cpu();
+
+ cpus_clear(map);
+ for_each_online_cpu(cpu) {
+ per_cpu(cpu_idle_state, cpu) = 1;
+ cpu_set(cpu, map);
+ }
+
+ __get_cpu_var(cpu_idle_state) = 0;
+
+ wmb();
+ do {
+ ssleep(1);
+ for_each_online_cpu(cpu) {
+ if (cpu_isset(cpu, map) && !per_cpu(cpu_idle_state, cpu))
+ cpu_clear(cpu, map);
+ }
+ cpus_and(map, map, cpu_online_map);
+ } while (!cpus_empty(map));
+}
+EXPORT_SYMBOL_GPL(cpu_idle_wait);
+
+void __attribute__((noreturn))
+cpu_idle (void)
+{
+ void (*mark_idle)(int) = ia64_mark_idle;
+
+ /* endless idle loop with no priority at all */
+ while (1) {
+#ifdef CONFIG_SMP
+ if (!need_resched())
+ min_xtp();
+#endif
+ while (!need_resched()) {
+ void (*idle)(void);
+
+ if (__get_cpu_var(cpu_idle_state))
+ __get_cpu_var(cpu_idle_state) = 0;
+
+ rmb();
+ if (mark_idle)
+ (*mark_idle)(1);
+
+ idle = pm_idle;
+ if (!idle)
+ idle = default_idle;
+ (*idle)();
+ }
+
+ if (mark_idle)
+ (*mark_idle)(0);
+
+#ifdef CONFIG_SMP
+ normal_xtp();
+#endif
+ schedule();
+ check_pgt_cache();
+ if (cpu_is_offline(smp_processor_id()))
+ play_dead();
+ }
+}
+
+void
+ia64_save_extra (struct task_struct *task)
+{
+#ifdef CONFIG_PERFMON
+ unsigned long info;
+#endif
+
+ if ((task->thread.flags & IA64_THREAD_DBG_VALID) != 0)
+ ia64_save_debug_regs(&task->thread.dbr[0]);
+
+#ifdef CONFIG_PERFMON
+ if ((task->thread.flags & IA64_THREAD_PM_VALID) != 0)
+ pfm_save_regs(task);
+
+ info = __get_cpu_var(pfm_syst_info);
+ if (info & PFM_CPUINFO_SYST_WIDE)
+ pfm_syst_wide_update_task(task, info, 0);
+#endif
+
+#ifdef CONFIG_IA32_SUPPORT
+ if (IS_IA32_PROCESS(ia64_task_regs(task)))
+ ia32_save_state(task);
+#endif
+}
+
+void
+ia64_load_extra (struct task_struct *task)
+{
+#ifdef CONFIG_PERFMON
+ unsigned long info;
+#endif
+
+ if ((task->thread.flags & IA64_THREAD_DBG_VALID) != 0)
+ ia64_load_debug_regs(&task->thread.dbr[0]);
+
+#ifdef CONFIG_PERFMON
+ if ((task->thread.flags & IA64_THREAD_PM_VALID) != 0)
+ pfm_load_regs(task);
+
+ info = __get_cpu_var(pfm_syst_info);
+ if (info & PFM_CPUINFO_SYST_WIDE)
+ pfm_syst_wide_update_task(task, info, 1);
+#endif
+
+#ifdef CONFIG_IA32_SUPPORT
+ if (IS_IA32_PROCESS(ia64_task_regs(task)))
+ ia32_load_state(task);
+#endif
+}
+
+/*
+ * Copy the state of an ia-64 thread.
+ *
+ * We get here through the following call chain:
+ *
+ * from user-level: from kernel:
+ *
+ * <clone syscall> <some kernel call frames>
+ * sys_clone :
+ * do_fork do_fork
+ * copy_thread copy_thread
+ *
+ * This means that the stack layout is as follows:
+ *
+ * +---------------------+ (highest addr)
+ * | struct pt_regs |
+ * +---------------------+
+ * | struct switch_stack |
+ * +---------------------+
+ * | |
+ * | memory stack |
+ * | | <-- sp (lowest addr)
+ * +---------------------+
+ *
+ * Observe that we copy the unat values that are in pt_regs and switch_stack. Spilling an
+ * integer to address X causes bit N in ar.unat to be set to the NaT bit of the register,
+ * with N=(X & 0x1ff)/8. Thus, copying the unat value preserves the NaT bits ONLY if the
+ * pt_regs structure in the parent is congruent to that of the child, modulo 512. Since
+ * the stack is page aligned and the page size is at least 4KB, this is always the case,
+ * so there is nothing to worry about.
+ */
+int
+copy_thread (int nr, unsigned long clone_flags,
+ unsigned long user_stack_base, unsigned long user_stack_size,
+ struct task_struct *p, struct pt_regs *regs)
+{
+ extern char ia64_ret_from_clone, ia32_ret_from_clone;
+ struct switch_stack *child_stack, *stack;
+ unsigned long rbs, child_rbs, rbs_size;
+ struct pt_regs *child_ptregs;
+ int retval = 0;
+
+#ifdef CONFIG_SMP
+ /*
+ * For SMP idle threads, fork_by_hand() calls do_fork with
+ * NULL regs.
+ */
+ if (!regs)
+ return 0;
+#endif
+
+ stack = ((struct switch_stack *) regs) - 1;
+
+ child_ptregs = (struct pt_regs *) ((unsigned long) p + IA64_STK_OFFSET) - 1;
+ child_stack = (struct switch_stack *) child_ptregs - 1;
+
+ /* copy parent's switch_stack & pt_regs to child: */
+ memcpy(child_stack, stack, sizeof(*child_ptregs) + sizeof(*child_stack));
+
+ rbs = (unsigned long) current + IA64_RBS_OFFSET;
+ child_rbs = (unsigned long) p + IA64_RBS_OFFSET;
+ rbs_size = stack->ar_bspstore - rbs;
+
+ /* copy the parent's register backing store to the child: */
+ memcpy((void *) child_rbs, (void *) rbs, rbs_size);
+
+ if (likely(user_mode(child_ptregs))) {
+ if ((clone_flags & CLONE_SETTLS) && !IS_IA32_PROCESS(regs))
+ child_ptregs->r13 = regs->r16; /* see sys_clone2() in entry.S */
+ if (user_stack_base) {
+ child_ptregs->r12 = user_stack_base + user_stack_size - 16;
+ child_ptregs->ar_bspstore = user_stack_base;
+ child_ptregs->ar_rnat = 0;
+ child_ptregs->loadrs = 0;
+ }
+ } else {
+ /*
+ * Note: we simply preserve the relative position of
+ * the stack pointer here. There is no need to
+ * allocate a scratch area here, since that will have
+ * been taken care of by the caller of sys_clone()
+ * already.
+ */
+ child_ptregs->r12 = (unsigned long) child_ptregs - 16; /* kernel sp */
+ child_ptregs->r13 = (unsigned long) p; /* set `current' pointer */
+ }
+ child_stack->ar_bspstore = child_rbs + rbs_size;
+ if (IS_IA32_PROCESS(regs))
+ child_stack->b0 = (unsigned long) &ia32_ret_from_clone;
+ else
+ child_stack->b0 = (unsigned long) &ia64_ret_from_clone;
+
+ /* copy parts of thread_struct: */
+ p->thread.ksp = (unsigned long) child_stack - 16;
+
+ /* stop some PSR bits from being inherited.
+ * the psr.up/psr.pp bits must be cleared on fork but inherited on execve()
+ * therefore we must specify them explicitly here and not include them in
+ * IA64_PSR_BITS_TO_CLEAR.
+ */
+ child_ptregs->cr_ipsr = ((child_ptregs->cr_ipsr | IA64_PSR_BITS_TO_SET)
+ & ~(IA64_PSR_BITS_TO_CLEAR | IA64_PSR_PP | IA64_PSR_UP));
+
+ /*
+ * NOTE: The calling convention considers all floating point
+ * registers in the high partition (fph) to be scratch. Since
+ * the only way to get to this point is through a system call,
+ * we know that the values in fph are all dead. Hence, there
+ * is no need to inherit the fph state from the parent to the
+ * child and all we have to do is to make sure that
+ * IA64_THREAD_FPH_VALID is cleared in the child.
+ *
+ * XXX We could push this optimization a bit further by
+ * clearing IA64_THREAD_FPH_VALID on ANY system call.
+ * However, it's not clear this is worth doing. Also, it
+ * would be a slight deviation from the normal Linux system
+ * call behavior where scratch registers are preserved across
+ * system calls (unless used by the system call itself).
+ */
+# define THREAD_FLAGS_TO_CLEAR (IA64_THREAD_FPH_VALID | IA64_THREAD_DBG_VALID \
+ | IA64_THREAD_PM_VALID)
+# define THREAD_FLAGS_TO_SET 0
+ p->thread.flags = ((current->thread.flags & ~THREAD_FLAGS_TO_CLEAR)
+ | THREAD_FLAGS_TO_SET);
+ ia64_drop_fpu(p); /* don't pick up stale state from a CPU's fph */
+#ifdef CONFIG_IA32_SUPPORT
+ /*
+ * If we're cloning an IA32 task then save the IA32 extra
+ * state from the current task to the new task
+ */
+ if (IS_IA32_PROCESS(ia64_task_regs(current))) {
+ ia32_save_state(p);
+ if (clone_flags & CLONE_SETTLS)
+ retval = ia32_clone_tls(p, child_ptregs);
+
+ /* Copy partially mapped page list */
+ if (!retval)
+ retval = ia32_copy_partial_page_list(p, clone_flags);
+ }
+#endif
+
+#ifdef CONFIG_PERFMON
+ if (current->thread.pfm_context)
+ pfm_inherit(p, child_ptregs);
+#endif
+ return retval;
+}
+
+static void
+do_copy_task_regs (struct task_struct *task, struct unw_frame_info *info, void *arg)
+{
+ unsigned long mask, sp, nat_bits = 0, ip, ar_rnat, urbs_end, cfm;
+ elf_greg_t *dst = arg;
+ struct pt_regs *pt;
+ char nat;
+ int i;
+
+ memset(dst, 0, sizeof(elf_gregset_t)); /* don't leak any kernel bits to user-level */
+
+ if (unw_unwind_to_user(info) < 0)
+ return;
+
+ unw_get_sp(info, &sp);
+ pt = (struct pt_regs *) (sp + 16);
+
+ urbs_end = ia64_get_user_rbs_end(task, pt, &cfm);
+
+ if (ia64_sync_user_rbs(task, info->sw, pt->ar_bspstore, urbs_end) < 0)
+ return;
+
+ ia64_peek(task, info->sw, urbs_end, (long) ia64_rse_rnat_addr((long *) urbs_end),
+ &ar_rnat);
+
+ /*
+ * coredump format:
+ * r0-r31
+ * NaT bits (for r0-r31; bit N == 1 iff rN is a NaT)
+ * predicate registers (p0-p63)
+ * b0-b7
+ * ip cfm user-mask
+ * ar.rsc ar.bsp ar.bspstore ar.rnat
+ * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec
+ */
+
+ /* r0 is zero */
+ for (i = 1, mask = (1UL << i); i < 32; ++i) {
+ unw_get_gr(info, i, &dst[i], &nat);
+ if (nat)
+ nat_bits |= mask;
+ mask <<= 1;
+ }
+ dst[32] = nat_bits;
+ unw_get_pr(info, &dst[33]);
+
+ for (i = 0; i < 8; ++i)
+ unw_get_br(info, i, &dst[34 + i]);
+
+ unw_get_rp(info, &ip);
+ dst[42] = ip + ia64_psr(pt)->ri;
+ dst[43] = cfm;
+ dst[44] = pt->cr_ipsr & IA64_PSR_UM;
+
+ unw_get_ar(info, UNW_AR_RSC, &dst[45]);
+ /*
+ * For bsp and bspstore, unw_get_ar() would return the kernel
+ * addresses, but we need the user-level addresses instead:
+ */
+ dst[46] = urbs_end; /* note: by convention PT_AR_BSP points to the end of the urbs! */
+ dst[47] = pt->ar_bspstore;
+ dst[48] = ar_rnat;
+ unw_get_ar(info, UNW_AR_CCV, &dst[49]);
+ unw_get_ar(info, UNW_AR_UNAT, &dst[50]);
+ unw_get_ar(info, UNW_AR_FPSR, &dst[51]);
+ dst[52] = pt->ar_pfs; /* UNW_AR_PFS is == to pt->cr_ifs for interrupt frames */
+ unw_get_ar(info, UNW_AR_LC, &dst[53]);
+ unw_get_ar(info, UNW_AR_EC, &dst[54]);
+ unw_get_ar(info, UNW_AR_CSD, &dst[55]);
+ unw_get_ar(info, UNW_AR_SSD, &dst[56]);
+}
+
+void
+do_dump_task_fpu (struct task_struct *task, struct unw_frame_info *info, void *arg)
+{
+ elf_fpreg_t *dst = arg;
+ int i;
+
+ memset(dst, 0, sizeof(elf_fpregset_t)); /* don't leak any "random" bits */
+
+ if (unw_unwind_to_user(info) < 0)
+ return;
+
+ /* f0 is 0.0, f1 is 1.0 */
+
+ for (i = 2; i < 32; ++i)
+ unw_get_fr(info, i, dst + i);
+
+ ia64_flush_fph(task);
+ if ((task->thread.flags & IA64_THREAD_FPH_VALID) != 0)
+ memcpy(dst + 32, task->thread.fph, 96*16);
+}
+
+void
+do_copy_regs (struct unw_frame_info *info, void *arg)
+{
+ do_copy_task_regs(current, info, arg);
+}
+
+void
+do_dump_fpu (struct unw_frame_info *info, void *arg)
+{
+ do_dump_task_fpu(current, info, arg);
+}
+
+int
+dump_task_regs(struct task_struct *task, elf_gregset_t *regs)
+{
+ struct unw_frame_info tcore_info;
+
+ if (current == task) {
+ unw_init_running(do_copy_regs, regs);
+ } else {
+ memset(&tcore_info, 0, sizeof(tcore_info));
+ unw_init_from_blocked_task(&tcore_info, task);
+ do_copy_task_regs(task, &tcore_info, regs);
+ }
+ return 1;
+}
+
+void
+ia64_elf_core_copy_regs (struct pt_regs *pt, elf_gregset_t dst)
+{
+ unw_init_running(do_copy_regs, dst);
+}
+
+int
+dump_task_fpu (struct task_struct *task, elf_fpregset_t *dst)
+{
+ struct unw_frame_info tcore_info;
+
+ if (current == task) {
+ unw_init_running(do_dump_fpu, dst);
+ } else {
+ memset(&tcore_info, 0, sizeof(tcore_info));
+ unw_init_from_blocked_task(&tcore_info, task);
+ do_dump_task_fpu(task, &tcore_info, dst);
+ }
+ return 1;
+}
+
+int
+dump_fpu (struct pt_regs *pt, elf_fpregset_t dst)
+{
+ unw_init_running(do_dump_fpu, dst);
+ return 1; /* f0-f31 are always valid so we always return 1 */
+}
+
+long
+sys_execve (char __user *filename, char __user * __user *argv, char __user * __user *envp,
+ struct pt_regs *regs)
+{
+ char *fname;
+ int error;
+
+ fname = getname(filename);
+ error = PTR_ERR(fname);
+ if (IS_ERR(fname))
+ goto out;
+ error = do_execve(fname, argv, envp, regs);
+ putname(fname);
+out:
+ return error;
+}
+
+pid_t
+kernel_thread (int (*fn)(void *), void *arg, unsigned long flags)
+{
+ extern void start_kernel_thread (void);
+ unsigned long *helper_fptr = (unsigned long *) &start_kernel_thread;
+ struct {
+ struct switch_stack sw;
+ struct pt_regs pt;
+ } regs;
+
+ memset(&regs, 0, sizeof(regs));
+ regs.pt.cr_iip = helper_fptr[0]; /* set entry point (IP) */
+ regs.pt.r1 = helper_fptr[1]; /* set GP */
+ regs.pt.r9 = (unsigned long) fn; /* 1st argument */
+ regs.pt.r11 = (unsigned long) arg; /* 2nd argument */
+ /* Preserve PSR bits, except for bits 32-34 and 37-45, which we can't read. */
+ regs.pt.cr_ipsr = ia64_getreg(_IA64_REG_PSR) | IA64_PSR_BN;
+ regs.pt.cr_ifs = 1UL << 63; /* mark as valid, empty frame */
+ regs.sw.ar_fpsr = regs.pt.ar_fpsr = ia64_getreg(_IA64_REG_AR_FPSR);
+ regs.sw.ar_bspstore = (unsigned long) current + IA64_RBS_OFFSET;
+ regs.sw.pr = (1 << PRED_KERNEL_STACK);
+ return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs.pt, 0, NULL, NULL);
+}
+EXPORT_SYMBOL(kernel_thread);
+
+/* This gets called from kernel_thread() via ia64_invoke_thread_helper(). */
+int
+kernel_thread_helper (int (*fn)(void *), void *arg)
+{
+#ifdef CONFIG_IA32_SUPPORT
+ if (IS_IA32_PROCESS(ia64_task_regs(current))) {
+ /* A kernel thread is always a 64-bit process. */
+ current->thread.map_base = DEFAULT_MAP_BASE;
+ current->thread.task_size = DEFAULT_TASK_SIZE;
+ ia64_set_kr(IA64_KR_IO_BASE, current->thread.old_iob);
+ ia64_set_kr(IA64_KR_TSSD, current->thread.old_k1);
+ }
+#endif
+ return (*fn)(arg);
+}
+
+/*
+ * Flush thread state. This is called when a thread does an execve().
+ */
+void
+flush_thread (void)
+{
+ /*
+ * Remove function-return probe instances associated with this task
+ * and put them back on the free list. Do not insert an exit probe for
+ * this function, it will be disabled by kprobe_flush_task if you do.
+ */
+ kprobe_flush_task(current);
+
+ /* drop floating-point and debug-register state if it exists: */
+ current->thread.flags &= ~(IA64_THREAD_FPH_VALID | IA64_THREAD_DBG_VALID);
+ ia64_drop_fpu(current);
+ if (IS_IA32_PROCESS(ia64_task_regs(current)))
+ ia32_drop_partial_page_list(current);
+}
+
+/*
+ * Clean up state associated with current thread. This is called when
+ * the thread calls exit().
+ */
+void
+exit_thread (void)
+{
+
+ /*
+ * Remove function-return probe instances associated with this task
+ * and put them back on the free list. Do not insert an exit probe for
+ * this function, it will be disabled by kprobe_flush_task if you do.
+ */
+ kprobe_flush_task(current);
+
+ ia64_drop_fpu(current);
+#ifdef CONFIG_PERFMON
+ /* if needed, stop monitoring and flush state to perfmon context */
+ if (current->thread.pfm_context)
+ pfm_exit_thread(current);
+
+ /* free debug register resources */
+ if (current->thread.flags & IA64_THREAD_DBG_VALID)
+ pfm_release_debug_registers(current);
+#endif
+ if (IS_IA32_PROCESS(ia64_task_regs(current)))
+ ia32_drop_partial_page_list(current);
+}
+
+unsigned long
+get_wchan (struct task_struct *p)
+{
+ struct unw_frame_info info;
+ unsigned long ip;
+ int count = 0;
+
+ /*
+ * Note: p may not be a blocked task (it could be current or
+ * another process running on some other CPU. Rather than
+ * trying to determine if p is really blocked, we just assume
+ * it's blocked and rely on the unwind routines to fail
+ * gracefully if the process wasn't really blocked after all.
+ * --davidm 99/12/15
+ */
+ unw_init_from_blocked_task(&info, p);
+ do {
+ if (unw_unwind(&info) < 0)
+ return 0;
+ unw_get_ip(&info, &ip);
+ if (!in_sched_functions(ip))
+ return ip;
+ } while (count++ < 16);
+ return 0;
+}
+
+void
+cpu_halt (void)
+{
+ pal_power_mgmt_info_u_t power_info[8];
+ unsigned long min_power;
+ int i, min_power_state;
+
+ if (ia64_pal_halt_info(power_info) != 0)
+ return;
+
+ min_power_state = 0;
+ min_power = power_info[0].pal_power_mgmt_info_s.power_consumption;
+ for (i = 1; i < 8; ++i)
+ if (power_info[i].pal_power_mgmt_info_s.im
+ && power_info[i].pal_power_mgmt_info_s.power_consumption < min_power) {
+ min_power = power_info[i].pal_power_mgmt_info_s.power_consumption;
+ min_power_state = i;
+ }
+
+ while (1)
+ ia64_pal_halt(min_power_state);
+}
+
+void
+machine_restart (char *restart_cmd)
+{
+ (*efi.reset_system)(EFI_RESET_WARM, 0, 0, NULL);
+}
+
+void
+machine_halt (void)
+{
+ cpu_halt();
+}
+
+void
+machine_power_off (void)
+{
+ if (pm_power_off)
+ pm_power_off();
+ machine_halt();
+}
+#endif // !XEN
diff -r f89906acd9f6 -r 45c4e735fc8c xen/arch/ia64/linux-xen/unwind.c
--- /dev/null Fri Dec 30 22:11:08 2005
+++ b/xen/arch/ia64/linux-xen/unwind.c Sat Dec 31 05:40:13 2005
@@ -0,0 +1,2332 @@
+/*
+ * Copyright (C) 1999-2004 Hewlett-Packard Co
+ * David Mosberger-Tang <davidm@hpl.hp.com>
+ * Copyright (C) 2003 Fenghua Yu <fenghua.yu@intel.com>
+ * - Change pt_regs_off() to make it less dependant on pt_regs structure.
+ */
+/*
+ * This file implements call frame unwind support for the Linux
+ * kernel. Parsing and processing the unwind information is
+ * time-consuming, so this implementation translates the unwind
+ * descriptors into unwind scripts. These scripts are very simple
+ * (basically a sequence of assignments) and efficient to execute.
+ * They are cached for later re-use. Each script is specific for a
+ * given instruction pointer address and the set of predicate values
+ * that the script depends on (most unwind descriptors are
+ * unconditional and scripts often do not depend on predicates at
+ * all). This code is based on the unwind conventions described in
+ * the "IA-64 Software Conventions and Runtime Architecture" manual.
+ *
+ * SMP conventions:
+ * o updates to the global unwind data (in structure "unw") are serialized
+ * by the unw.lock spinlock
+ * o each unwind script has its own read-write lock; a thread must acquire
+ * a read lock before executing a script and must acquire a write lock
+ * before modifying a script
+ * o if both the unw.lock spinlock and a script's read-write lock must be
+ * acquired, then the read-write lock must be acquired first.
+ */
+#ifdef XEN
+#include <xen/types.h>
+#include <xen/elf.h>
+#include <xen/kernel.h>
+#include <xen/sched.h>
+#include <xen/xmalloc.h>
+#include <xen/spinlock.h>
+
+// work around
+#ifdef CONFIG_SMP
+#define write_trylock(lock) _raw_write_trylock(lock)
+#else
+#define write_trylock(lock) ({1;})
+#endif
+
+#else
+#include <linux/module.h>
+#include <linux/bootmem.h>
+#include <linux/elf.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#endif
+
+#include <asm/unwind.h>
+
+#include <asm/delay.h>
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/ptrace_offsets.h>
+#include <asm/rse.h>
+#include <asm/sections.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "entry.h"
+#include "unwind_i.h"
+
+#define UNW_LOG_CACHE_SIZE 7 /* each unw_script is ~256 bytes in size */
+#define UNW_CACHE_SIZE (1 << UNW_LOG_CACHE_SIZE)
+
+#define UNW_LOG_HASH_SIZE (UNW_LOG_CACHE_SIZE + 1)
+#define UNW_HASH_SIZE (1 << UNW_LOG_HASH_SIZE)
+
+#define UNW_STATS 0 /* WARNING: this disabled interrupts for long time-spans!! */
+
+#ifdef UNW_DEBUG
+ static unsigned int unw_debug_level = UNW_DEBUG;
+# define UNW_DEBUG_ON(n) unw_debug_level >= n
+ /* Do not code a printk level, not all debug lines end in newline */
+# define UNW_DPRINT(n, ...) if (UNW_DEBUG_ON(n)) printk(__VA_ARGS__)
+# define inline
+#else /* !UNW_DEBUG */
+# define UNW_DEBUG_ON(n) 0
+# define UNW_DPRINT(n, ...)
+#endif /* UNW_DEBUG */
+
+#if UNW_STATS
+# define STAT(x...) x
+#else
+# define STAT(x...)
+#endif
+
+#ifdef XEN
+#define alloc_reg_state() xmalloc(struct unw_reg_state)
+#define free_reg_state(usr) xfree(usr)
+#define alloc_labeled_state() xmalloc(struct unw_labeled_state)
+#define free_labeled_state(usr) xfree(usr)
+#else
+#define alloc_reg_state() kmalloc(sizeof(struct unw_reg_state), GFP_ATOMIC)
+#define free_reg_state(usr) kfree(usr)
+#define alloc_labeled_state() kmalloc(sizeof(struct unw_labeled_state), GFP_ATOMIC)
+#define free_labeled_state(usr) kfree(usr)
+#endif
+
+typedef unsigned long unw_word;
+typedef unsigned char unw_hash_index_t;
+
+static struct {
+ spinlock_t lock; /* spinlock for unwind data */
+
+ /* list of unwind tables (one per load-module) */
+ struct unw_table *tables;
+
+ unsigned long r0; /* constant 0 for r0 */
+
+ /* table of registers that prologues can save (and order in which they're saved): */
+ const unsigned char save_order[8];
+
+ /* maps a preserved register index (preg_index) to corresponding switch_stack offset: */
+ unsigned short sw_off[sizeof(struct unw_frame_info) / 8];
+
+ unsigned short lru_head; /* index of lead-recently used script */
+ unsigned short lru_tail; /* index of most-recently used script */
+
+ /* index into unw_frame_info for preserved register i */
+ unsigned short preg_index[UNW_NUM_REGS];
+
+ short pt_regs_offsets[32];
+
+ /* unwind table for the kernel: */
+ struct unw_table kernel_table;
+
+ /* unwind table describing the gate page (kernel code that is mapped into user space): */
+ size_t gate_table_size;
+ unsigned long *gate_table;
+
+ /* hash table that maps instruction pointer to script index: */
+ unsigned short hash[UNW_HASH_SIZE];
+
+ /* script cache: */
+ struct unw_script cache[UNW_CACHE_SIZE];
+
+# ifdef UNW_DEBUG
+ const char *preg_name[UNW_NUM_REGS];
+# endif
+# if UNW_STATS
+ struct {
+ struct {
+ int lookups;
+ int hinted_hits;
+ int normal_hits;
+ int collision_chain_traversals;
+ } cache;
+ struct {
+ unsigned long build_time;
+ unsigned long run_time;
+ unsigned long parse_time;
+ int builds;
+ int news;
+ int collisions;
+ int runs;
+ } script;
+ struct {
+ unsigned long init_time;
+ unsigned long unwind_time;
+ int inits;
+ int unwinds;
+ } api;
+ } stat;
+# endif
+} unw = {
+ .tables = &unw.kernel_table,
+ .lock = SPIN_LOCK_UNLOCKED,
+ .save_order = {
+ UNW_REG_RP, UNW_REG_PFS, UNW_REG_PSP, UNW_REG_PR,
+ UNW_REG_UNAT, UNW_REG_LC, UNW_REG_FPSR, UNW_REG_PRI_UNAT_GR
+ },
+ .preg_index = {
+ offsetof(struct unw_frame_info, pri_unat_loc)/8, /* PRI_UNAT_GR */
+ offsetof(struct unw_frame_info, pri_unat_loc)/8, /* PRI_UNAT_MEM */
+ offsetof(struct unw_frame_info, bsp_loc)/8,
+ offsetof(struct unw_frame_info, bspstore_loc)/8,
+ offsetof(struct unw_frame_info, pfs_loc)/8,
+ offsetof(struct unw_frame_info, rnat_loc)/8,
+ offsetof(struct unw_frame_info, psp)/8,
+ offsetof(struct unw_frame_info, rp_loc)/8,
+ offsetof(struct unw_frame_info, r4)/8,
+ offsetof(struct unw_frame_info, r5)/8,
+ offsetof(struct unw_frame_info, r6)/8,
+ offsetof(struct unw_frame_info, r7)/8,
+ offsetof(struct unw_frame_info, unat_loc)/8,
+ offsetof(struct unw_frame_info, pr_loc)/8,
+ offsetof(struct unw_frame_info, lc_loc)/8,
+ offsetof(struct unw_frame_info, fpsr_loc)/8,
+ offsetof(struct unw_frame_info, b1_loc)/8,
+ offsetof(struct unw_frame_info, b2_loc)/8,
+ offsetof(struct unw_frame_info, b3_loc)/8,
+ offsetof(struct unw_frame_info, b4_loc)/8,
+ offsetof(struct unw_frame_info, b5_loc)/8,
+ offsetof(struct unw_frame_info, f2_loc)/8,
+ offsetof(struct unw_frame_info, f3_loc)/8,
+ offsetof(struct unw_frame_info, f4_loc)/8,
+ offsetof(struct unw_frame_info, f5_loc)/8,
+ offsetof(struct unw_frame_info, fr_loc[16 - 16])/8,
+ offsetof(struct unw_frame_info, fr_loc[17 - 16])/8,
+ offsetof(struct unw_frame_info, fr_loc[18 - 16])/8,
+ offsetof(struct unw_frame_info, fr_loc[19 - 16])/8,
+ offsetof(struct unw_frame_info, fr_loc[20 - 16])/8,
+ offsetof(struct unw_frame_info, fr_loc[21 - 16])/8,
+ offsetof(struct unw_frame_info, fr_loc[22 - 16])/8,
+ offsetof(struct unw_frame_info, fr_loc[23 - 16])/8,
+ offsetof(struct unw_frame_info, fr_loc[24 - 16])/8,
+ offsetof(struct unw_frame_info, fr_loc[25 - 16])/8,
+ offsetof(struct unw_frame_info, fr_loc[26 - 16])/8,
+ offsetof(struct unw_frame_info, fr_loc[27 - 16])/8,
+ offsetof(struct unw_frame_info, fr_loc[28 - 16])/8,
+ offsetof(struct unw_frame_info, fr_loc[29 - 16])/8,
+ offsetof(struct unw_frame_info, fr_loc[30 - 16])/8,
+ offsetof(struct unw_frame_info, fr_loc[31 - 16])/8,
+ },
+ .pt_regs_offsets = {
+ [0] = -1,
+ offsetof(struct pt_regs, r1),
+ offsetof(struct pt_regs, r2),
+ offsetof(struct pt_regs, r3),
+ [4] = -1, [5] = -1, [6] = -1, [7] = -1,
+ offsetof(struct pt_regs, r8),
+ offsetof(struct pt_regs, r9),
+ offsetof(struct pt_regs, r10),
+ offsetof(struct pt_regs, r11),
+ offsetof(struct pt_regs, r12),
+ offsetof(struct pt_regs, r13),
+ offsetof(struct pt_regs, r14),
+ offsetof(struct pt_regs, r15),
+ offsetof(struct pt_regs, r16),
+ offsetof(struct pt_regs, r17),
+ offsetof(struct pt_regs, r18),
+ offsetof(struct pt_regs, r19),
+ offsetof(struct pt_regs, r20),
+ offsetof(struct pt_regs, r21),
+ offsetof(struct pt_regs, r22),
+ offsetof(struct pt_regs, r23),
+ offsetof(struct pt_regs, r24),
+ offsetof(struct pt_regs, r25),
+ offsetof(struct pt_regs, r26),
+ offsetof(struct pt_regs, r27),
+ offsetof(struct pt_regs, r28),
+ offsetof(struct pt_regs, r29),
+ offsetof(struct pt_regs, r30),
+ offsetof(struct pt_regs, r31),
+ },
+ .hash = { [0 ... UNW_HASH_SIZE - 1] = -1 },
+#ifdef UNW_DEBUG
+ .preg_name = {
+ "pri_unat_gr", "pri_unat_mem", "bsp", "bspstore", "ar.pfs", "ar.rnat", "psp", "rp",
+ "r4", "r5", "r6", "r7",
+ "ar.unat", "pr", "ar.lc", "ar.fpsr",
+ "b1", "b2", "b3", "b4", "b5",
+ "f2", "f3", "f4", "f5",
+ "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
+ "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31"
+ }
+#endif
+};
+
+static inline int
+read_only (void *addr)
+{
+ return (unsigned long) ((char *) addr - (char *) &unw.r0) < sizeof(unw.r0);
+}
+
+/*
+ * Returns offset of rREG in struct pt_regs.
+ */
+static inline unsigned long
+pt_regs_off (unsigned long reg)
+{
+ short off = -1;
+
+ if (reg < ARRAY_SIZE(unw.pt_regs_offsets))
+ off = unw.pt_regs_offsets[reg];
+
+ if (off < 0) {
+ UNW_DPRINT(0, "unwind.%s: bad scratch reg r%lu\n", __FUNCTION__, reg);
+ off = 0;
+ }
+ return (unsigned long) off;
+}
+
+static inline struct pt_regs *
+get_scratch_regs (struct unw_frame_info *info)
+{
+ if (!info->pt) {
+ /* This should not happen with valid unwind info. */
+ UNW_DPRINT(0, "unwind.%s: bad unwind info: resetting info->pt\n", __FUNCTION__);
+ if (info->flags & UNW_FLAG_INTERRUPT_FRAME)
+ info->pt = (unsigned long) ((struct pt_regs *) info->psp - 1);
+ else
+ info->pt = info->sp - 16;
+ }
+ UNW_DPRINT(3, "unwind.%s: sp 0x%lx pt 0x%lx\n", __FUNCTION__, info->sp, info->pt);
+ return (struct pt_regs *) info->pt;
+}
+
+/* Unwind accessors. */
+
+int
+unw_access_gr (struct unw_frame_info *info, int regnum, unsigned long *val, char *nat, int write)
+{
+ unsigned long *addr, *nat_addr, nat_mask = 0, dummy_nat;
+ struct unw_ireg *ireg;
+ struct pt_regs *pt;
+
+ if ((unsigned) regnum - 1 >= 127) {
+ if (regnum == 0 && !write) {
+ *val = 0; /* read r0 always returns 0 */
+ *nat = 0;
+ return 0;
+ }
+ UNW_DPRINT(0, "unwind.%s: trying to access non-existent r%u\n",
+ __FUNCTION__, regnum);
+ return -1;
+ }
+
+ if (regnum < 32) {
+ if (regnum >= 4 && regnum <= 7) {
+ /* access a preserved register */
+ ireg = &info->r4 + (regnum - 4);
+ addr = ireg->loc;
+ if (addr) {
+ nat_addr = addr + ireg->nat.off;
+ switch (ireg->nat.type) {
+ case UNW_NAT_VAL:
+ /* simulate getf.sig/setf.sig */
+ if (write) {
+ if (*nat) {
+ /* write NaTVal and be done with it */
+ addr[0] = 0;
+ addr[1] = 0x1fffe;
+ return 0;
+ }
+ addr[1] = 0x1003e;
+ } else {
+ if (addr[0] == 0 && addr[1] == 0x1ffe) {
+ /* return NaT and be done with it */
+ *val = 0;
+ *nat = 1;
+ return 0;
+ }
+ }
+ /* fall through */
+ case UNW_NAT_NONE:
+ dummy_nat = 0;
+ nat_addr = &dummy_nat;
+ break;
+
+ case UNW_NAT_MEMSTK:
+ nat_mask = (1UL << ((long) addr & 0x1f8)/8);
+ break;
+
+ case UNW_NAT_REGSTK:
+ nat_addr = ia64_rse_rnat_addr(addr);
+ if ((unsigned long) addr < info->regstk.limit
+ || (unsigned long) addr >= info->regstk.top)
+ {
+ UNW_DPRINT(0, "unwind.%s: %p outside of regstk "
+ "[.0x%lx-0x%lx)\n",
+ __FUNCTION__, (void *) addr,
+ info->regstk.limit,
+ info->regstk.top);
+ return -1;
+ }
+ if ((unsigned long) nat_addr >= info->regstk.top)
+ nat_addr = &info->sw->ar_rnat;
+ nat_mask = (1UL << ia64_rse_slot_num(addr));
+ break;
+ }
+ } else {
+ addr = &info->sw->r4 + (regnum - 4);
+ nat_addr = &info->sw->ar_unat;
+ nat_mask = (1UL << ((long) addr & 0x1f8)/8);
+ }
+ } else {
+ /* access a scratch register */
+ pt = get_scratch_regs(info);
+ addr = (unsigned long *) ((unsigned long)pt + pt_regs_off(regnum));
+ if (info->pri_unat_loc)
+ nat_addr = info->pri_unat_loc;
+ else
+ nat_addr = &info->sw->caller_unat;
+ nat_mask = (1UL << ((long) addr & 0x1f8)/8);
+ }
+ } else {
+ /* access a stacked register */
+ addr = ia64_rse_skip_regs((unsigned long *) info->bsp, regnum - 32);
+ nat_addr = ia64_rse_rnat_addr(addr);
+ if ((unsigned long) addr < info->regstk.limit
+ || (unsigned long) addr >= info->regstk.top)
+ {
+ UNW_DPRINT(0, "unwind.%s: ignoring attempt to access register outside "
+ "of rbs\n", __FUNCTION__);
+ return -1;
+ }
+ if ((unsigned long) nat_addr >= info->regstk.top)
+ nat_addr = &info->sw->ar_rnat;
+ nat_mask = (1UL << ia64_rse_slot_num(addr));
+ }
+
+ if (write) {
+ if (read_only(addr)) {
+ UNW_DPRINT(0, "unwind.%s: ignoring attempt to write read-only location\n",
+ __FUNCTION__);
+ } else {
+ *addr = *val;
+ if (*nat)
+ *nat_addr |= nat_mask;
+ else
+ *nat_addr &= ~nat_mask;
+ }
+ } else {
+ if ((*nat_addr & nat_mask) == 0) {
+ *val = *addr;
+ *nat = 0;
+ } else {
+ *val = 0; /* if register is a NaT, *addr may contain kernel data! */
+ *nat = 1;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(unw_access_gr);
+
+int
+unw_access_br (struct unw_frame_info *info, int regnum, unsigned long *val, int write)
+{
+ unsigned long *addr;
+ struct pt_regs *pt;
+
+ switch (regnum) {
+ /* scratch: */
+ case 0: pt = get_scratch_regs(info); addr = &pt->b0; break;
+ case 6: pt = get_scratch_regs(info); addr = &pt->b6; break;
+ case 7: pt = get_scratch_regs(info); addr = &pt->b7; break;
+
+ /* preserved: */
+ case 1: case 2: case 3: case 4: case 5:
+ addr = *(&info->b1_loc + (regnum - 1));
+ if (!addr)
+ addr = &info->sw->b1 + (regnum - 1);
+ break;
+
+ default:
+ UNW_DPRINT(0, "unwind.%s: trying to access non-existent b%u\n",
+ __FUNCTION__, regnum);
+ return -1;
+ }
+ if (write)
+ if (read_only(addr)) {
+ UNW_DPRINT(0, "unwind.%s: ignoring attempt to write read-only location\n",
+ __FUNCTION__);
+ } else
+ *addr = *val;
+ else
+ *val = *addr;
+ return 0;
+}
+EXPORT_SYMBOL(unw_access_br);
+
+int
+unw_access_fr (struct unw_frame_info *info, int regnum, struct ia64_fpreg *val, int write)
+{
+ struct ia64_fpreg *addr = NULL;
+ struct pt_regs *pt;
+
+ if ((unsigned) (regnum - 2) >= 126) {
+ UNW_DPRINT(0, "unwind.%s: trying to access non-existent f%u\n",
+ __FUNCTION__, regnum);
+ return -1;
+ }
+
+ if (regnum <= 5) {
+ addr = *(&info->f2_loc + (regnum - 2));
+ if (!addr)
+ addr = &info->sw->f2 + (regnum - 2);
+ } else if (regnum <= 15) {
+ if (regnum <= 11) {
+ pt = get_scratch_regs(info);
+ addr = &pt->f6 + (regnum - 6);
+ }
+ else
+ addr = &info->sw->f12 + (regnum - 12);
+ } else if (regnum <= 31) {
+ addr = info->fr_loc[regnum - 16];
+ if (!addr)
+ addr = &info->sw->f16 + (regnum - 16);
+ } else {
+ struct task_struct *t = info->task;
+
+ if (write)
+ ia64_sync_fph(t);
+ else
+ ia64_flush_fph(t);
+#ifdef XEN
+ addr = t->arch._thread.fph + (regnum - 32);
+#else
+ addr = t->thread.fph + (regnum - 32);
+#endif
+ }
+
+ if (write)
+ if (read_only(addr)) {
+ UNW_DPRINT(0, "unwind.%s: ignoring attempt to write read-only location\n",
+ __FUNCTION__);
+ } else
+ *addr = *val;
+ else
+ *val = *addr;
+ return 0;
+}
+EXPORT_SYMBOL(unw_access_fr);
+
+int
+unw_access_ar (struct unw_frame_info *info, int regnum, unsigned long *val, int write)
+{
+ unsigned long *addr;
+ struct pt_regs *pt;
+
+ switch (regnum) {
+ case UNW_AR_BSP:
+ addr = info->bsp_loc;
+ if (!addr)
+ addr = &info->sw->ar_bspstore;
+ break;
+
+ case UNW_AR_BSPSTORE:
+ addr = info->bspstore_loc;
+ if (!addr)
+ addr = &info->sw->ar_bspstore;
+ break;
+
+ case UNW_AR_PFS:
+ addr = info->pfs_loc;
+ if (!addr)
+ addr = &info->sw->ar_pfs;
+ break;
+
+ case UNW_AR_RNAT:
+ addr = info->rnat_loc;
+ if (!addr)
+ addr = &info->sw->ar_rnat;
+ break;
+
+ case UNW_AR_UNAT:
+ addr = info->unat_loc;
+ if (!addr)
+ addr = &info->sw->caller_unat;
+ break;
+
+ case UNW_AR_LC:
+ addr = info->lc_loc;
+ if (!addr)
+ addr = &info->sw->ar_lc;
+ break;
+
+ case UNW_AR_EC:
+ if (!info->cfm_loc)
+ return -1;
+ if (write)
+ *info->cfm_loc =
+ (*info->cfm_loc & ~(0x3fUL << 52)) | ((*val & 0x3f) << 52);
+ else
+ *val = (*info->cfm_loc >> 52) & 0x3f;
+ return 0;
+
+ case UNW_AR_FPSR:
+ addr = info->fpsr_loc;
+ if (!addr)
+ addr = &info->sw->ar_fpsr;
+ break;
+
+ case UNW_AR_RSC:
+ pt = get_scratch_regs(info);
+ addr = &pt->ar_rsc;
+ break;
+
+ case UNW_AR_CCV:
+ pt = get_scratch_regs(info);
+ addr = &pt->ar_ccv;
+ break;
+
+ case UNW_AR_CSD:
+ pt = get_scratch_regs(info);
+ addr = &pt->ar_csd;
+ break;
+
+ case UNW_AR_SSD:
+ pt = get_scratch_regs(info);
+ addr = &pt->ar_ssd;
+ break;
+
+ default:
+ UNW_DPRINT(0, "unwind.%s: trying to access non-existent ar%u\n",
+ __FUNCTION__, regnum);
+ return -1;
+ }
+
+ if (write) {
+ if (read_only(addr)) {
+ UNW_DPRINT(0, "unwind.%s: ignoring attempt to write read-only location\n",
+ __FUNCTION__);
+ } else
+ *addr = *val;
+ } else
+ *val = *addr;
+ return 0;
+}
+EXPORT_SYMBOL(unw_access_ar);
+
+int
+unw_access_pr (struct unw_frame_info *info, unsigned long *val, int write)
+{
+ unsigned long *addr;
+
+ addr = info->pr_loc;
+ if (!addr)
+ addr = &info->sw->pr;
+
+ if (write) {
+ if (read_only(addr)) {
+ UNW_DPRINT(0, "unwind.%s: ignoring attempt to write read-only location\n",
+ __FUNCTION__);
+ } else
+ *addr = *val;
+ } else
+ *val = *addr;
+ return 0;
+}
+EXPORT_SYMBOL(unw_access_pr);
+
+
+/* Routines to manipulate the state stack. */
+
+static inline void
+push (struct unw_state_record *sr)
+{
+ struct unw_reg_state *rs;
+
+ rs = alloc_reg_state();
+ if (!rs) {
+ printk(KERN_ERR "unwind: cannot stack reg state!\n");
+ return;
+ }
+ memcpy(rs, &sr->curr, sizeof(*rs));
+ sr->curr.next = rs;
+}
+
+static void
+pop (struct unw_state_record *sr)
+{
+ struct unw_reg_state *rs = sr->curr.next;
+
+ if (!rs) {
+ printk(KERN_ERR "unwind: stack underflow!\n");
+ return;
+ }
+ memcpy(&sr->curr, rs, sizeof(*rs));
+ free_reg_state(rs);
+}
+
+/* Make a copy of the state stack. Non-recursive to avoid stack overflows. */
+static struct unw_reg_state *
+dup_state_stack (struct unw_reg_state *rs)
+{
+ struct unw_reg_state *copy, *prev = NULL, *first = NULL;
+
+ while (rs) {
+ copy = alloc_reg_state();
+ if (!copy) {
+ printk(KERN_ERR "unwind.dup_state_stack: out of memory\n");
+ return NULL;
+ }
+ memcpy(copy, rs, sizeof(*copy));
+ if (first)
+ prev->next = copy;
+ else
+ first = copy;
+ rs = rs->next;
+ prev = copy;
+ }
+ return first;
+}
+
+/* Free all stacked register states (but not RS itself). */
+static void
+free_state_stack (struct unw_reg_state *rs)
+{
+ struct unw_reg_state *p, *next;
+
+ for (p = rs->next; p != NULL; p = next) {
+ next = p->next;
+ free_reg_state(p);
+ }
+ rs->next = NULL;
+}
+
+/* Unwind decoder routines */
+
+static enum unw_register_index __attribute_const__
+decode_abreg (unsigned char abreg, int memory)
+{
+ switch (abreg) {
+ case 0x04 ... 0x07: return UNW_REG_R4 + (abreg - 0x04);
+ case 0x22 ... 0x25: return UNW_REG_F2 + (abreg - 0x22);
+ case 0x30 ... 0x3f: return UNW_REG_F16 + (abreg - 0x30);
+ case 0x41 ... 0x45: return UNW_REG_B1 + (abreg - 0x41);
+ case 0x60: return UNW_REG_PR;
+ case 0x61: return UNW_REG_PSP;
+ case 0x62: return memory ? UNW_REG_PRI_UNAT_MEM : UNW_REG_PRI_UNAT_GR;
+ case 0x63: return UNW_REG_RP;
+ case 0x64: return UNW_REG_BSP;
+ case 0x65: return UNW_REG_BSPSTORE;
+ case 0x66: return UNW_REG_RNAT;
+ case 0x67: return UNW_REG_UNAT;
+ case 0x68: return UNW_REG_FPSR;
+ case 0x69: return UNW_REG_PFS;
+ case 0x6a: return UNW_REG_LC;
+ default:
+ break;
+ }
+ UNW_DPRINT(0, "unwind.%s: bad abreg=0x%x\n", __FUNCTION__, abreg);
+ return UNW_REG_LC;
+}
+
+static void
+set_reg (struct unw_reg_info *reg, enum unw_where where, int when, unsigned long val)
+{
+ reg->val = val;
+ reg->where = where;
+ if (reg->when == UNW_WHEN_NEVER)
+ reg->when = when;
+}
+
+static void
+alloc_spill_area (unsigned long *offp, unsigned long regsize,
+ struct unw_reg_info *lo, struct unw_reg_info *hi)
+{
+ struct unw_reg_info *reg;
+
+ for (reg = hi; reg >= lo; --reg) {
+ if (reg->where == UNW_WHERE_SPILL_HOME) {
+ reg->where = UNW_WHERE_PSPREL;
+ *offp -= regsize;
+ reg->val = *offp;
+ }
+ }
+}
+
+static inline void
+spill_next_when (struct unw_reg_info **regp, struct unw_reg_info *lim, unw_word t)
+{
+ struct unw_reg_info *reg;
+
+ for (reg = *regp; reg <= lim; ++reg) {
+ if (reg->where == UNW_WHERE_SPILL_HOME) {
+ reg->when = t;
+ *regp = reg + 1;
+ return;
+ }
+ }
+ UNW_DPRINT(0, "unwind.%s: excess spill!\n", __FUNCTION__);
+}
+
+static inline void
+finish_prologue (struct unw_state_record *sr)
+{
+ struct unw_reg_info *reg;
+ unsigned long off;
+ int i;
+
+ /*
+ * First, resolve implicit register save locations (see Section "11.4.2.3 Rules
+ * for Using Unwind Descriptors", rule 3):
+ */
+ for (i = 0; i < (int) ARRAY_SIZE(unw.save_order); ++i) {
+ reg = sr->curr.reg + unw.save_order[i];
+ if (reg->where == UNW_WHERE_GR_SAVE) {
+ reg->where = UNW_WHERE_GR;
+ reg->val = sr->gr_save_loc++;
+ }
+ }
+
+ /*
+ * Next, compute when the fp, general, and branch registers get
+ * saved. This must come before alloc_spill_area() because
+ * we need to know which registers are spilled to their home
+ * locations.
+ */
+ if (sr->imask) {
+ unsigned char kind, mask = 0, *cp = sr->imask;
+ int t;
+ static const unsigned char limit[3] = {
+ UNW_REG_F31, UNW_REG_R7, UNW_REG_B5
+ };
+ struct unw_reg_info *(regs[3]);
+
+ regs[0] = sr->curr.reg + UNW_REG_F2;
+ regs[1] = sr->curr.reg + UNW_REG_R4;
+ regs[2] = sr->curr.reg + UNW_REG_B1;
+
+ for (t = 0; t < sr->region_len; ++t) {
+ if ((t & 3) == 0)
+ mask = *cp++;
+ kind = (mask >> 2*(3-(t & 3))) & 3;
+ if (kind > 0)
+ spill_next_when(&regs[kind - 1], sr->curr.reg + limit[kind - 1],
+ sr->region_start + t);
+ }
+ }
+ /*
+ * Next, lay out the memory stack spill area:
+ */
+ if (sr->any_spills) {
+ off = sr->spill_offset;
+ alloc_spill_area(&off, 16, sr->curr.reg + UNW_REG_F2, sr->curr.reg + UNW_REG_F31);
+ alloc_spill_area(&off, 8, sr->curr.reg + UNW_REG_B1, sr->curr.reg + UNW_REG_B5);
+ alloc_spill_area(&off, 8, sr->curr.reg + UNW_REG_R4, sr->curr.reg + UNW_REG_R7);
+ }
+}
+
+/*
+ * Region header descriptors.
+ */
+
+static void
+desc_prologue (int body, unw_word rlen, unsigned char mask, unsigned char grsave,
+ struct unw_state_record *sr)
+{
+ int i, region_start;
+
+ if (!(sr->in_body || sr->first_region))
+ finish_prologue(sr);
+ sr->first_region = 0;
+
+ /* check if we're done: */
+ if (sr->when_target < sr->region_start + sr->region_len) {
+ sr->done = 1;
+ return;
+ }
+
+ region_start = sr->region_start + sr->region_len;
+
+ for (i = 0; i < sr->epilogue_count; ++i)
+ pop(sr);
+ sr->epilogue_count = 0;
+ sr->epilogue_start = UNW_WHEN_NEVER;
+
+ sr->region_start = region_start;
+ sr->region_len = rlen;
+ sr->in_body = body;
+
+ if (!body) {
+ push(sr);
+
+ for (i = 0; i < 4; ++i) {
+ if (mask & 0x8)
+ set_reg(sr->curr.reg + unw.save_order[i], UNW_WHERE_GR,
+ sr->region_start + sr->region_len - 1, grsave++);
+ mask <<= 1;
+ }
+ sr->gr_save_loc = grsave;
+ sr->any_spills = 0;
+ sr->imask = NULL;
+ sr->spill_offset = 0x10; /* default to psp+16 */
+ }
+}
+
+/*
+ * Prologue descriptors.
+ */
+
+static inline void
+desc_abi (unsigned char abi, unsigned char context, struct unw_state_record *sr)
+{
+ if (abi == 3 && context == 'i') {
+ sr->flags |= UNW_FLAG_INTERRUPT_FRAME;
+ UNW_DPRINT(3, "unwind.%s: interrupt frame\n", __FUNCTION__);
+ }
+ else
+ UNW_DPRINT(0, "unwind%s: ignoring unwabi(abi=0x%x,context=0x%x)\n",
+ __FUNCTION__, abi, context);
+}
+
+static inline void
+desc_br_gr (unsigned char brmask, unsigned char gr, struct unw_state_record *sr)
+{
+ int i;
+
+ for (i = 0; i < 5; ++i) {
+ if (brmask & 1)
+ set_reg(sr->curr.reg + UNW_REG_B1 + i, UNW_WHERE_GR,
+ sr->region_start + sr->region_len - 1, gr++);
+ brmask >>= 1;
+ }
+}
+
+static inline void
+desc_br_mem (unsigned char brmask, struct unw_state_record *sr)
+{
+ int i;
+
+ for (i = 0; i < 5; ++i) {
+ if (brmask & 1) {
+ set_reg(sr->curr.reg + UNW_REG_B1 + i, UNW_WHERE_SPILL_HOME,
+ sr->region_start + sr->region_len - 1, 0);
+ sr->any_spills = 1;
+ }
+ brmask >>= 1;
+ }
+}
+
+static inline void
+desc_frgr_mem (unsigned char grmask, unw_word frmask, struct unw_state_record *sr)
+{
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ if ((grmask & 1) != 0) {
+ set_reg(sr->curr.reg + UNW_REG_R4 + i, UNW_WHERE_SPILL_HOME,
+ sr->region_start + sr->region_len - 1, 0);
+ sr->any_spills = 1;
+ }
+ grmask >>= 1;
+ }
+ for (i = 0; i < 20; ++i) {
+ if ((frmask & 1) != 0) {
+ int base = (i < 4) ? UNW_REG_F2 : UNW_REG_F16 - 4;
+ set_reg(sr->curr.reg + base + i, UNW_WHERE_SPILL_HOME,
+ sr->region_start + sr->region_len - 1, 0);
+ sr->any_spills = 1;
+ }
+ frmask >>= 1;
+ }
+}
+
+static inline void
+desc_fr_mem (unsigned char frmask, struct unw_state_record *sr)
+{
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ if ((frmask & 1) != 0) {
+ set_reg(sr->curr.reg + UNW_REG_F2 + i, UNW_WHERE_SPILL_HOME,
+ sr->region_start + sr->region_len - 1, 0);
+ sr->any_spills = 1;
+ }
+ frmask >>= 1;
+ }
+}
+
+static inline void
+desc_gr_gr (unsigned char grmask, unsigned char gr, struct unw_state_record *sr)
+{
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ if ((grmask & 1) != 0)
+ set_reg(sr->curr.reg + UNW_REG_R4 + i, UNW_WHERE_GR,
+ sr->region_start + sr->region_len - 1, gr++);
+ grmask >>= 1;
+ }
+}
+
+static inline void
+desc_gr_mem (unsigned char grmask, struct unw_state_record *sr)
+{
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ if ((grmask & 1) != 0) {
+ set_reg(sr->curr.reg + UNW_REG_R4 + i, UNW_WHERE_SPILL_HOME,
+ sr->region_start + sr->region_len - 1, 0);
+ sr->any_spills = 1;
+ }
+ grmask >>= 1;
+ }
+}
+
+static inline void
+desc_mem_stack_f (unw_word t, unw_word size, struct unw_state_record *sr)
+{
+ set_reg(sr->curr.reg + UNW_REG_PSP, UNW_WHERE_NONE,
+ sr->region_start + min_t(int, t, sr->region_len - 1), 16*size);
+}
+
+static inline void
+desc_mem_stack_v (unw_word t, struct unw_state_record *sr)
+{
+ sr->curr.reg[UNW_REG_PSP].when = sr->region_start + min_t(int, t, sr->region_len - 1);
+}
+
+static inline void
+desc_reg_gr (unsigned char reg, unsigned char dst, struct unw_state_record *sr)
+{
+ set_reg(sr->curr.reg + reg, UNW_WHERE_GR, sr->region_start + sr->region_len - 1, dst);
+}
+
+static inline void
+desc_reg_psprel (unsigned char reg, unw_word pspoff, struct unw_state_record *sr)
+{
+ set_reg(sr->curr.reg + reg, UNW_WHERE_PSPREL, sr->region_start + sr->region_len - 1,
+ 0x10 - 4*pspoff);
+}
+
+static inline void
+desc_reg_sprel (unsigned char reg, unw_word spoff, struct unw_state_record *sr)
+{
+ set_reg(sr->curr.reg + reg, UNW_WHERE_SPREL, sr->region_start + sr->region_len - 1,
+ 4*spoff);
+}
+
+static inline void
+desc_rp_br (unsigned char dst, struct unw_state_record *sr)
+{
+ sr->return_link_reg = dst;
+}
+
+static inline void
+desc_reg_when (unsigned char regnum, unw_word t, struct unw_state_record *sr)
+{
+ struct unw_reg_info *reg = sr->curr.reg + regnum;
+
+ if (reg->where == UNW_WHERE_NONE)
+ reg->where = UNW_WHERE_GR_SAVE;
+ reg->when = sr->region_start + min_t(int, t, sr->region_len - 1);
+}
+
+static inline void
+desc_spill_base (unw_word pspoff, struct unw_state_record *sr)
+{
+ sr->spill_offset = 0x10 - 4*pspoff;
+}
+
+static inline unsigned char *
+desc_spill_mask (unsigned char *imaskp, struct unw_state_record *sr)
+{
+ sr->imask = imaskp;
+ return imaskp + (2*sr->region_len + 7)/8;
+}
+
+/*
+ * Body descriptors.
+ */
+static inline void
+desc_epilogue (unw_word t, unw_word ecount, struct unw_state_record *sr)
+{
+ sr->epilogue_start = sr->region_start + sr->region_len - 1 - t;
+ sr->epilogue_count = ecount + 1;
+}
+
+static inline void
+desc_copy_state (unw_word label, struct unw_state_record *sr)
+{
+ struct unw_labeled_state *ls;
+
+ for (ls = sr->labeled_states; ls; ls = ls->next) {
+ if (ls->label == label) {
+ free_state_stack(&sr->curr);
+ memcpy(&sr->curr, &ls->saved_state, sizeof(sr->curr));
+ sr->curr.next = dup_state_stack(ls->saved_state.next);
+ return;
+ }
+ }
+ printk(KERN_ERR "unwind: failed to find state labeled 0x%lx\n", label);
+}
+
+static inline void
+desc_label_state (unw_word label, struct unw_state_record *sr)
+{
+ struct unw_labeled_state *ls;
+
+ ls = alloc_labeled_state();
+ if (!ls) {
+ printk(KERN_ERR "unwind.desc_label_state(): out of memory\n");
+ return;
+ }
+ ls->label = label;
+ memcpy(&ls->saved_state, &sr->curr, sizeof(ls->saved_state));
+ ls->saved_state.next = dup_state_stack(sr->curr.next);
+
+ /* insert into list of labeled states: */
+ ls->next = sr->labeled_states;
+ sr->labeled_states = ls;
+}
+
+/*
+ * General descriptors.
+ */
+
+static inline int
+desc_is_active (unsigned char qp, unw_word t, struct unw_state_record *sr)
+{
+ if (sr->when_target <= sr->region_start + min_t(int, t, sr->region_len - 1))
+ return 0;
+ if (qp > 0) {
+ if ((sr->pr_val & (1UL << qp)) == 0)
+ return 0;
+ sr->pr_mask |= (1UL << qp);
+ }
+ return 1;
+}
+
+static inline void
+desc_restore_p (unsigned char qp, unw_word t, unsigned char abreg, struct unw_state_record *sr)
+{
+ struct unw_reg_info *r;
+
+ if (!desc_is_active(qp, t, sr))
+ return;
+
+ r = sr->curr.reg + decode_abreg(abreg, 0);
+ r->where = UNW_WHERE_NONE;
+ r->when = UNW_WHEN_NEVER;
+ r->val = 0;
+}
+
+static inline void
+desc_spill_reg_p (unsigned char qp, unw_word t, unsigned char abreg, unsigned char x,
+ unsigned char ytreg, struct unw_state_record *sr)
+{
+ enum unw_where where = UNW_WHERE_GR;
+ struct unw_reg_info *r;
+
+ if (!desc_is_active(qp, t, sr))
+ return;
+
+ if (x)
+ where = UNW_WHERE_BR;
+ else if (ytreg & 0x80)
+ where = UNW_WHERE_FR;
+
+ r = sr->curr.reg + decode_abreg(abreg, 0);
+ r->where = where;
+ r->when = sr->region_start + min_t(int, t, sr->region_len - 1);
+ r->val = (ytreg & 0x7f);
+}
+
+static inline void
+desc_spill_psprel_p (unsigned char qp, unw_word t, unsigned char abreg, unw_word pspoff,
+ struct unw_state_record *sr)
+{
+ struct unw_reg_info *r;
+
+ if (!desc_is_active(qp, t, sr))
+ return;
+
+ r = sr->curr.reg + decode_abreg(abreg, 1);
+ r->where = UNW_WHERE_PSPREL;
+ r->when = sr->region_start + min_t(int, t, sr->region_len - 1);
+ r->val = 0x10 - 4*pspoff;
+}
+
+static inline void
+desc_spill_sprel_p (unsigned char qp, unw_word t, unsigned char abreg, unw_word spoff,
+ struct unw_state_record *sr)
+{
+ struct unw_reg_info *r;
+
+ if (!desc_is_active(qp, t, sr))
+ return;
+
+ r = sr->curr.reg + decode_abreg(abreg, 1);
+ r->where = UNW_WHERE_SPREL;
+ r->when = sr->region_start + min_t(int, t, sr->region_len - 1);
+ r->val = 4*spoff;
+}
+
+#define UNW_DEC_BAD_CODE(code) printk(KERN_ERR "unwind: unknown code 0x%02x\n", \
+ code);
+
+/*
+ * region headers:
+ */
+#define UNW_DEC_PROLOGUE_GR(fmt,r,m,gr,arg) desc_prologue(0,r,m,gr,arg)
+#define UNW_DEC_PROLOGUE(fmt,b,r,arg) desc_prologue(b,r,0,32,arg)
+/*
+ * prologue descriptors:
+ */
+#define UNW_DEC_ABI(fmt,a,c,arg) desc_abi(a,c,arg)
+#define UNW_DEC_BR_GR(fmt,b,g,arg) desc_br_gr(b,g,arg)
+#define UNW_DEC_BR_MEM(fmt,b,arg) desc_br_mem(b,arg)
+#define UNW_DEC_FRGR_MEM(fmt,g,f,arg) desc_frgr_mem(g,f,arg)
+#define UNW_DEC_FR_MEM(fmt,f,arg) desc_fr_mem(f,arg)
+#define UNW_DEC_GR_GR(fmt,m,g,arg) desc_gr_gr(m,g,arg)
+#define UNW_DEC_GR_MEM(fmt,m,arg) desc_gr_mem(m,arg)
+#define UNW_DEC_MEM_STACK_F(fmt,t,s,arg) desc_mem_stack_f(t,s,arg)
+#define UNW_DEC_MEM_STACK_V(fmt,t,arg) desc_mem_stack_v(t,arg)
+#define UNW_DEC_REG_GR(fmt,r,d,arg) desc_reg_gr(r,d,arg)
+#define UNW_DEC_REG_PSPREL(fmt,r,o,arg) desc_reg_psprel(r,o,arg)
+#define UNW_DEC_REG_SPREL(fmt,r,o,arg) desc_reg_sprel(r,o,arg)
+#define UNW_DEC_REG_WHEN(fmt,r,t,arg) desc_reg_when(r,t,arg)
+#define UNW_DEC_PRIUNAT_WHEN_GR(fmt,t,arg) desc_reg_when(UNW_REG_PRI_UNAT_GR,t,arg)
+#define UNW_DEC_PRIUNAT_WHEN_MEM(fmt,t,arg) desc_reg_when(UNW_REG_PRI_UNAT_MEM,t,arg)
+#define UNW_DEC_PRIUNAT_GR(fmt,r,arg) desc_reg_gr(UNW_REG_PRI_UNAT_GR,r,arg)
+#define UNW_DEC_PRIUNAT_PSPREL(fmt,o,arg) desc_reg_psprel(UNW_REG_PRI_UNAT_MEM,o,arg)
+#define UNW_DEC_PRIUNAT_SPREL(fmt,o,arg) desc_reg_sprel(UNW_REG_PRI_UNAT_MEM,o,arg)
+#define UNW_DEC_RP_BR(fmt,d,arg) desc_rp_br(d,arg)
+#define UNW_DEC_SPILL_BASE(fmt,o,arg) desc_spill_base(o,arg)
+#define UNW_DEC_SPILL_MASK(fmt,m,arg) (m = desc_spill_mask(m,arg))
+/*
+ * body descriptors:
+ */
+#define UNW_DEC_EPILOGUE(fmt,t,c,arg) desc_epilogue(t,c,arg)
+#define UNW_DEC_COPY_STATE(fmt,l,arg) desc_copy_state(l,arg)
+#define UNW_DEC_LABEL_STATE(fmt,l,arg) desc_label_state(l,arg)
+/*
+ * general unwind descriptors:
+ */
+#define UNW_DEC_SPILL_REG_P(f,p,t,a,x,y,arg) desc_spill_reg_p(p,t,a,x,y,arg)
+#define UNW_DEC_SPILL_REG(f,t,a,x,y,arg) desc_spill_reg_p(0,t,a,x,y,arg)
+#define UNW_DEC_SPILL_PSPREL_P(f,p,t,a,o,arg) desc_spill_psprel_p(p,t,a,o,arg)
+#define UNW_DEC_SPILL_PSPREL(f,t,a,o,arg) desc_spill_psprel_p(0,t,a,o,arg)
+#define UNW_DEC_SPILL_SPREL_P(f,p,t,a,o,arg) desc_spill_sprel_p(p,t,a,o,arg)
+#define UNW_DEC_SPILL_SPREL(f,t,a,o,arg) desc_spill_sprel_p(0,t,a,o,arg)
+#define UNW_DEC_RESTORE_P(f,p,t,a,arg) desc_restore_p(p,t,a,arg)
+#define UNW_DEC_RESTORE(f,t,a,arg) desc_restore_p(0,t,a,arg)
+
+#include "unwind_decoder.c"
+
+
+/* Unwind scripts. */
+
+static inline unw_hash_index_t
+hash (unsigned long ip)
+{
+# define hashmagic 0x9e3779b97f4a7c16UL /* based on (sqrt(5)/2-1)*2^64 */
+
+ return (ip >> 4)*hashmagic >> (64 - UNW_LOG_HASH_SIZE);
+#undef hashmagic
+}
+
+static inline long
+cache_match (struct unw_script *script, unsigned long ip, unsigned long pr)
+{
+ read_lock(&script->lock);
+ if (ip == script->ip && ((pr ^ script->pr_val) & script->pr_mask) == 0)
+ /* keep the read lock... */
+ return 1;
+ read_unlock(&script->lock);
+ return 0;
+}
+
+static inline struct unw_script *
+script_lookup (struct unw_frame_info *info)
+{
+ struct unw_script *script = unw.cache + info->hint;
+ unsigned short index;
+ unsigned long ip, pr;
+
+ if (UNW_DEBUG_ON(0))
+ return NULL; /* Always regenerate scripts in debug mode */
+
+ STAT(++unw.stat.cache.lookups);
+
+ ip = info->ip;
+ pr = info->pr;
+
+ if (cache_match(script, ip, pr)) {
+ STAT(++unw.stat.cache.hinted_hits);
+ return script;
+ }
+
+ index = unw.hash[hash(ip)];
+ if (index >= UNW_CACHE_SIZE)
+ return NULL;
+
+ script = unw.cache + index;
+ while (1) {
+ if (cache_match(script, ip, pr)) {
+ /* update hint; no locking required as single-word writes are atomic */
+ STAT(++unw.stat.cache.normal_hits);
+ unw.cache[info->prev_script].hint = script - unw.cache;
+ return script;
+ }
+ if (script->coll_chain >= UNW_HASH_SIZE)
+ return NULL;
+ script = unw.cache + script->coll_chain;
+ STAT(++unw.stat.cache.collision_chain_traversals);
+ }
+}
+
+/*
+ * On returning, a write lock for the SCRIPT is still being held.
+ */
+static inline struct unw_script *
+script_new (unsigned long ip)
+{
+ struct unw_script *script, *prev, *tmp;
+ unw_hash_index_t index;
+ unsigned short head;
+
+ STAT(++unw.stat.script.news);
+
+ /*
+ * Can't (easily) use cmpxchg() here because of ABA problem
+ * that is intrinsic in cmpxchg()...
+ */
+ head = unw.lru_head;
+ script = unw.cache + head;
+ unw.lru_head = script->lru_chain;
+
+ /*
+ * We'd deadlock here if we interrupted a thread that is holding a read lock on
+ * script->lock. Thus, if the write_trylock() fails, we simply bail out. The
+ * alternative would be to disable interrupts whenever we hold a read-lock, but
+ * that seems silly.
+ */
+ if (!write_trylock(&script->lock))
+ return NULL;
+
+ /* re-insert script at the tail of the LRU chain: */
+ unw.cache[unw.lru_tail].lru_chain = head;
+ unw.lru_tail = head;
+
+ /* remove the old script from the hash table (if it's there): */
+ if (script->ip) {
+ index = hash(script->ip);
+ tmp = unw.cache + unw.hash[index];
+ prev = NULL;
+ while (1) {
+ if (tmp == script) {
+ if (prev)
+ prev->coll_chain = tmp->coll_chain;
+ else
+ unw.hash[index] = tmp->coll_chain;
+ break;
+ } else
+ prev = tmp;
+ if (tmp->coll_chain >= UNW_CACHE_SIZE)
+ /* old script wasn't in the hash-table */
+ break;
+ tmp = unw.cache + tmp->coll_chain;
+ }
+ }
+
+ /* enter new script in the hash table */
+ index = hash(ip);
+ script->coll_chain = unw.hash[index];
+ unw.hash[index] = script - unw.cache;
+
+ script->ip = ip; /* set new IP while we're holding the locks */
+
+ STAT(if (script->coll_chain < UNW_CACHE_SIZE) ++unw.stat.script.collisions);
+
+ script->flags = 0;
+ script->hint = 0;
+ script->count = 0;
+ return script;
+}
+
+static void
+script_finalize (struct unw_script *script, struct unw_state_record *sr)
+{
+ script->pr_mask = sr->pr_mask;
+ script->pr_val = sr->pr_val;
+ /*
+ * We could down-grade our write-lock on script->lock here but
+ * the rwlock API doesn't offer atomic lock downgrading, so
+ * we'll just keep the write-lock and release it later when
+ * we're done using the script.
+ */
+}
+
+static inline void
+script_emit (struct unw_script *script, struct unw_insn insn)
+{
+ if (script->count >= UNW_MAX_SCRIPT_LEN) {
+ UNW_DPRINT(0, "unwind.%s: script exceeds maximum size of %u instructions!\n",
+ __FUNCTION__, UNW_MAX_SCRIPT_LEN);
+ return;
+ }
+ script->insn[script->count++] = insn;
+}
+
+static inline void
+emit_nat_info (struct unw_state_record *sr, int i, struct unw_script *script)
+{
+ struct unw_reg_info *r = sr->curr.reg + i;
+ enum unw_insn_opcode opc;
+ struct unw_insn insn;
+ unsigned long val = 0;
+
+ switch (r->where) {
+ case UNW_WHERE_GR:
+ if (r->val >= 32) {
+ /* register got spilled to a stacked register */
+ opc = UNW_INSN_SETNAT_TYPE;
+ val = UNW_NAT_REGSTK;
+ } else
+ /* register got spilled to a scratch register */
+ opc = UNW_INSN_SETNAT_MEMSTK;
+ break;
+
+ case UNW_WHERE_FR:
+ opc = UNW_INSN_SETNAT_TYPE;
+ val = UNW_NAT_VAL;
+ break;
+
+ case UNW_WHERE_BR:
+ opc = UNW_INSN_SETNAT_TYPE;
+ val = UNW_NAT_NONE;
+ break;
+
+ case UNW_WHERE_PSPREL:
+ case UNW_WHERE_SPREL:
+ opc = UNW_INSN_SETNAT_MEMSTK;
+ break;
+
+ default:
+ UNW_DPRINT(0, "unwind.%s: don't know how to emit nat info for where = %u\n",
+ __FUNCTION__, r->where);
+ return;
+ }
+ insn.opc = opc;
+ insn.dst = unw.preg_index[i];
+ insn.val = val;
+ script_emit(script, insn);
+}
+
+static void
+compile_reg (struct unw_state_record *sr, int i, struct unw_script *script)
+{
+ struct unw_reg_info *r = sr->curr.reg + i;
+ enum unw_insn_opcode opc;
+ unsigned long val, rval;
+ struct unw_insn insn;
+ long need_nat_info;
+
+ if (r->where == UNW_WHERE_NONE || r->when >= sr->when_target)
+ return;
+
+ opc = UNW_INSN_MOVE;
+ val = rval = r->val;
+ need_nat_info = (i >= UNW_REG_R4 && i <= UNW_REG_R7);
+
+ switch (r->where) {
+ case UNW_WHERE_GR:
+ if (rval >= 32) {
+ opc = UNW_INSN_MOVE_STACKED;
+ val = rval - 32;
+ } else if (rval >= 4 && rval <= 7) {
+ if (need_nat_info) {
+ opc = UNW_INSN_MOVE2;
+ need_nat_info = 0;
+ }
+ val = unw.preg_index[UNW_REG_R4 + (rval - 4)];
+ } else if (rval == 0) {
+ opc = UNW_INSN_MOVE_CONST;
+ val = 0;
+ } else {
+ /* register got spilled to a scratch register */
+ opc = UNW_INSN_MOVE_SCRATCH;
+ val = pt_regs_off(rval);
+ }
+ break;
+
+ case UNW_WHERE_FR:
+ if (rval <= 5)
+ val = unw.preg_index[UNW_REG_F2 + (rval - 2)];
+ else if (rval >= 16 && rval <= 31)
+ val = unw.preg_index[UNW_REG_F16 + (rval - 16)];
+ else {
+ opc = UNW_INSN_MOVE_SCRATCH;
+ if (rval <= 11)
+ val = offsetof(struct pt_regs, f6) + 16*(rval - 6);
+ else
+ UNW_DPRINT(0, "unwind.%s: kernel may not touch f%lu\n",
+ __FUNCTION__, rval);
+ }
+ break;
+
+ case UNW_WHERE_BR:
+ if (rval >= 1 && rval <= 5)
+ val = unw.preg_index[UNW_REG_B1 + (rval - 1)];
+ else {
+ opc = UNW_INSN_MOVE_SCRATCH;
+ if (rval == 0)
+ val = offsetof(struct pt_regs, b0);
+ else if (rval == 6)
+ val = offsetof(struct pt_regs, b6);
+ else
+ val = offsetof(struct pt_regs, b7);
+ }
+ break;
+
+ case UNW_WHERE_SPREL:
+ opc = UNW_INSN_ADD_SP;
+ break;
+
+ case UNW_WHERE_PSPREL:
+ opc = UNW_INSN_ADD_PSP;
+ break;
+
+ default:
+ UNW_DPRINT(0, "unwind%s: register %u has unexpected `where' value of %u\n",
+ __FUNCTION__, i, r->where);
+ break;
+ }
+ insn.opc = opc;
+ insn.dst = unw.preg_index[i];
+ insn.val = val;
+ script_emit(script, insn);
+ if (need_nat_info)
+ emit_nat_info(sr, i, script);
+
+ if (i == UNW_REG_PSP) {
+ /*
+ * info->psp must contain the _value_ of the previous
+ * sp, not it's save location. We get this by
+ * dereferencing the value we just stored in
+ * info->psp:
+ */
+ insn.opc = UNW_INSN_LOAD;
+ insn.dst = insn.val = unw.preg_index[UNW_REG_PSP];
+ script_emit(script, insn);
+ }
+}
+
+static inline const struct unw_table_entry *
+lookup (struct unw_table *table, unsigned long rel_ip)
+{
+ const struct unw_table_entry *e = NULL;
+ unsigned long lo, hi, mid;
+
+ /* do a binary search for right entry: */
+ for (lo = 0, hi = table->length; lo < hi; ) {
+ mid = (lo + hi) / 2;
+ e = &table->array[mid];
+ if (rel_ip < e->start_offset)
+ hi = mid;
+ else if (rel_ip >= e->end_offset)
+ lo = mid + 1;
+ else
+ break;
+ }
+ if (rel_ip < e->start_offset || rel_ip >= e->end_offset)
+ return NULL;
+ return e;
+}
+
+/*
+ * Build an unwind script that unwinds from state OLD_STATE to the
+ * entrypoint of the function that called OLD_STATE.
+ */
+static inline struct unw_script *
+build_script (struct unw_frame_info *info)
+{
+ const struct unw_table_entry *e = NULL;
+ struct unw_script *script = NULL;
+ struct unw_labeled_state *ls, *next;
+ unsigned long ip = info->ip;
+ struct unw_state_record sr;
+ struct unw_table *table;
+ struct unw_reg_info *r;
+ struct unw_insn insn;
+ u8 *dp, *desc_end;
+ u64 hdr;
+ int i;
+ STAT(unsigned long start, parse_start;)
+
+ STAT(++unw.stat.script.builds; start = ia64_get_itc());
+
+ /* build state record */
+ memset(&sr, 0, sizeof(sr));
+ for (r = sr.curr.reg; r < sr.curr.reg + UNW_NUM_REGS; ++r)
+ r->when = UNW_WHEN_NEVER;
+ sr.pr_val = info->pr;
+
+ UNW_DPRINT(3, "unwind.%s: ip 0x%lx\n", __FUNCTION__, ip);
+ script = script_new(ip);
+ if (!script) {
+ UNW_DPRINT(0, "unwind.%s: failed to create unwind script\n", __FUNCTION__);
+ STAT(unw.stat.script.build_time += ia64_get_itc() - start);
+ return NULL;
+ }
+ unw.cache[info->prev_script].hint = script - unw.cache;
+
+ /* search the kernels and the modules' unwind tables for IP: */
+
+ STAT(parse_start = ia64_get_itc());
+
+ for (table = unw.tables; table; table = table->next) {
+ if (ip >= table->start && ip < table->end) {
+ e = lookup(table, ip - table->segment_base);
+ break;
+ }
+ }
+ if (!e) {
+ /* no info, return default unwinder (leaf proc, no mem stack, no saved regs) */
+ UNW_DPRINT(1, "unwind.%s: no unwind info for ip=0x%lx (prev ip=0x%lx)\n",
+ __FUNCTION__, ip, unw.cache[info->prev_script].ip);
+ sr.curr.reg[UNW_REG_RP].where = UNW_WHERE_BR;
+ sr.curr.reg[UNW_REG_RP].when = -1;
+ sr.curr.reg[UNW_REG_RP].val = 0;
+ compile_reg(&sr, UNW_REG_RP, script);
+ script_finalize(script, &sr);
+ STAT(unw.stat.script.parse_time += ia64_get_itc() - parse_start);
+ STAT(unw.stat.script.build_time += ia64_get_itc() - start);
+ return script;
+ }
+
+ sr.when_target = (3*((ip & ~0xfUL) - (table->segment_base + e->start_offset))/16
+ + (ip & 0xfUL));
+ hdr = *(u64 *) (table->segment_base + e->info_offset);
+ dp = (u8 *) (table->segment_base + e->info_offset + 8);
+ desc_end = dp + 8*UNW_LENGTH(hdr);
+
+ while (!sr.done && dp < desc_end)
+ dp = unw_decode(dp, sr.in_body, &sr);
+
+ if (sr.when_target > sr.epilogue_start) {
+ /*
+ * sp has been restored and all values on the memory stack below
+ * psp also have been restored.
+ */
+ sr.curr.reg[UNW_REG_PSP].val = 0;
+ sr.curr.reg[UNW_REG_PSP].where = UNW_WHERE_NONE;
+ sr.curr.reg[UNW_REG_PSP].when = UNW_WHEN_NEVER;
+ for (r = sr.curr.reg; r < sr.curr.reg + UNW_NUM_REGS; ++r)
+ if ((r->where == UNW_WHERE_PSPREL && r->val <= 0x10)
+ || r->where == UNW_WHERE_SPREL)
+ {
+ r->val = 0;
+ r->where = UNW_WHERE_NONE;
+ r->when = UNW_WHEN_NEVER;
+ }
+ }
+
+ script->flags = sr.flags;
+
+ /*
+ * If RP did't get saved, generate entry for the return link
+ * register.
+ */
+ if (sr.curr.reg[UNW_REG_RP].when >= sr.when_target) {
+ sr.curr.reg[UNW_REG_RP].where = UNW_WHERE_BR;
+ sr.curr.reg[UNW_REG_RP].when = -1;
+ sr.curr.reg[UNW_REG_RP].val = sr.return_link_reg;
+ UNW_DPRINT(1, "unwind.%s: using default for rp at ip=0x%lx where=%d val=0x%lx\n",
+ __FUNCTION__, ip, sr.curr.reg[UNW_REG_RP].where,
+ sr.curr.reg[UNW_REG_RP].val);
+ }
+
+#ifdef UNW_DEBUG
+ UNW_DPRINT(1, "unwind.%s: state record for func 0x%lx, t=%u:\n",
+ __FUNCTION__, table->segment_base + e->start_offset, sr.when_target);
+ for (r = sr.curr.reg; r < sr.curr.reg + UNW_NUM_REGS; ++r) {
+ if (r->where != UNW_WHERE_NONE || r->when != UNW_WHEN_NEVER) {
+ UNW_DPRINT(1, " %s <- ", unw.preg_name[r - sr.curr.reg]);
+ switch (r->where) {
+ case UNW_WHERE_GR: UNW_DPRINT(1, "r%lu", r->val); break;
+ case UNW_WHERE_FR: UNW_DPRINT(1, "f%lu", r->val); break;
+ case UNW_WHERE_BR: UNW_DPRINT(1, "b%lu", r->val); break;
+ case UNW_WHERE_SPREL: UNW_DPRINT(1, "[sp+0x%lx]", r->val); break;
+ case UNW_WHERE_PSPREL: UNW_DPRINT(1, "[psp+0x%lx]", r->val); break;
+ case UNW_WHERE_NONE:
+ UNW_DPRINT(1, "%s+0x%lx", unw.preg_name[r - sr.curr.reg], r->val);
+ break;
+
+ default:
+ UNW_DPRINT(1, "BADWHERE(%d)", r->where);
+ break;
+ }
+ UNW_DPRINT(1, "\t\t%d\n", r->when);
+ }
+ }
+#endif
+
+ STAT(unw.stat.script.parse_time += ia64_get_itc() - parse_start);
+
+ /* translate state record into unwinder instructions: */
+
+ /*
+ * First, set psp if we're dealing with a fixed-size frame;
+ * subsequent instructions may depend on this value.
+ */
+ if (sr.when_target > sr.curr.reg[UNW_REG_PSP].when
+ && (sr.curr.reg[UNW_REG_PSP].where == UNW_WHERE_NONE)
+ && sr.curr.reg[UNW_REG_PSP].val != 0) {
+ /* new psp is sp plus frame size */
+ insn.opc = UNW_INSN_ADD;
+ insn.dst = offsetof(struct unw_frame_info, psp)/8;
+ insn.val = sr.curr.reg[UNW_REG_PSP].val; /* frame size */
+ script_emit(script, insn);
+ }
+
+ /* determine where the primary UNaT is: */
+ if (sr.when_target < sr.curr.reg[UNW_REG_PRI_UNAT_GR].when)
+ i = UNW_REG_PRI_UNAT_MEM;
+ else if (sr.when_target < sr.curr.reg[UNW_REG_PRI_UNAT_MEM].when)
+ i = UNW_REG_PRI_UNAT_GR;
+ else if (sr.curr.reg[UNW_REG_PRI_UNAT_MEM].when > sr.curr.reg[UNW_REG_PRI_UNAT_GR].when)
+ i = UNW_REG_PRI_UNAT_MEM;
+ else
+ i = UNW_REG_PRI_UNAT_GR;
+
+ compile_reg(&sr, i, script);
+
+ for (i = UNW_REG_BSP; i < UNW_NUM_REGS; ++i)
+ compile_reg(&sr, i, script);
+
+ /* free labeled register states & stack: */
+
+ STAT(parse_start = ia64_get_itc());
+ for (ls = sr.labeled_states; ls; ls = next) {
+ next = ls->next;
+ free_state_stack(&ls->saved_state);
+ free_labeled_state(ls);
+ }
+ free_state_stack(&sr.curr);
+ STAT(unw.stat.script.parse_time += ia64_get_itc() - parse_start);
+
+ script_finalize(script, &sr);
+ STAT(unw.stat.script.build_time += ia64_get_itc() - start);
+ return script;
+}
+
+/*
+ * Apply the unwinding actions represented by OPS and update SR to
+ * reflect the state that existed upon entry to the function that this
+ * unwinder represents.
+ */
+static inline void
+run_script (struct unw_script *script, struct unw_frame_info *state)
+{
+ struct unw_insn *ip, *limit, next_insn;
+ unsigned long opc, dst, val, off;
+ unsigned long *s = (unsigned long *) state;
+ STAT(unsigned long start;)
+
+ STAT(++unw.stat.script.runs; start = ia64_get_itc());
+ state->flags = script->flags;
+ ip = script->insn;
+ limit = script->insn + script->count;
+ next_insn = *ip;
+
+ while (ip++ < limit) {
+ opc = next_insn.opc;
+ dst = next_insn.dst;
+ val = next_insn.val;
+ next_insn = *ip;
+
+ redo:
+ switch (opc) {
+ case UNW_INSN_ADD:
+ s[dst] += val;
+ break;
+
+ case UNW_INSN_MOVE2:
+ if (!s[val])
+ goto lazy_init;
+ s[dst+1] = s[val+1];
+ s[dst] = s[val];
+ break;
+
+ case UNW_INSN_MOVE:
+ if (!s[val])
+ goto lazy_init;
+ s[dst] = s[val];
+ break;
+
+ case UNW_INSN_MOVE_SCRATCH:
+ if (state->pt) {
+ s[dst] = (unsigned long) get_scratch_regs(state) + val;
+ } else {
+ s[dst] = 0;
+ UNW_DPRINT(0, "unwind.%s: no state->pt, dst=%ld, val=%ld\n",
+ __FUNCTION__, dst, val);
+ }
+ break;
+
+ case UNW_INSN_MOVE_CONST:
+ if (val == 0)
+ s[dst] = (unsigned long) &unw.r0;
+ else {
+ s[dst] = 0;
+ UNW_DPRINT(0, "unwind.%s: UNW_INSN_MOVE_CONST bad val=%ld\n",
+ __FUNCTION__, val);
+ }
+ break;
+
+
+ case UNW_INSN_MOVE_STACKED:
+ s[dst] = (unsigned long) ia64_rse_skip_regs((unsigned long *)state->bsp,
+ val);
+ break;
+
+ case UNW_INSN_ADD_PSP:
+ s[dst] = state->psp + val;
+ break;
+
+ case UNW_INSN_ADD_SP:
+ s[dst] = state->sp + val;
+ break;
+
+ case UNW_INSN_SETNAT_MEMSTK:
+ if (!state->pri_unat_loc)
+ state->pri_unat_loc = &state->sw->caller_unat;
+ /* register off. is a multiple of 8, so the least 3 bits (type) are 0 */
+ s[dst+1] = ((unsigned long) state->pri_unat_loc - s[dst]) | UNW_NAT_MEMSTK;
+ break;
+
+ case UNW_INSN_SETNAT_TYPE:
+ s[dst+1] = val;
+ break;
+
+ case UNW_INSN_LOAD:
+#ifdef UNW_DEBUG
+ if ((s[val] & (local_cpu_data->unimpl_va_mask | 0x7)) != 0
+#ifndef XEN
+ || s[val] < TASK_SIZE
+#endif
+ )
+ {
+ UNW_DPRINT(0, "unwind.%s: rejecting bad psp=0x%lx\n",
+ __FUNCTION__, s[val]);
+ break;
+ }
+#endif
+ s[dst] = *(unsigned long *) s[val];
+ break;
+ }
+ }
+ STAT(unw.stat.script.run_time += ia64_get_itc() - start);
+ return;
+
+ lazy_init:
+ off = unw.sw_off[val];
+ s[val] = (unsigned long) state->sw + off;
+ if (off >= offsetof(struct switch_stack, r4) && off <= offsetof(struct switch_stack, r7))
+ /*
+ * We're initializing a general register: init NaT info, too. Note that
+ * the offset is a multiple of 8 which gives us the 3 bits needed for
+ * the type field.
+ */
+ s[val+1] = (offsetof(struct switch_stack, ar_unat) - off) | UNW_NAT_MEMSTK;
+ goto redo;
+}
+
+static int
+find_save_locs (struct unw_frame_info *info)
+{
+ int have_write_lock = 0;
+ struct unw_script *scr;
+ unsigned long flags = 0;
+
+ if ((info->ip & (local_cpu_data->unimpl_va_mask | 0xf))
+#ifndef XEN
+ || info->ip < TASK_SIZE
+#endif
+ ) {
+ /* don't let obviously bad addresses pollute the cache */
+ /* FIXME: should really be level 0 but it occurs too often. KAO */
+ UNW_DPRINT(1, "unwind.%s: rejecting bad ip=0x%lx\n", __FUNCTION__, info->ip);
+ info->rp_loc = NULL;
+ return -1;
+ }
+
+ scr = script_lookup(info);
+ if (!scr) {
+ spin_lock_irqsave(&unw.lock, flags);
+ scr = build_script(info);
+ if (!scr) {
+ spin_unlock_irqrestore(&unw.lock, flags);
+ UNW_DPRINT(0,
+ "unwind.%s: failed to locate/build unwind script for ip %lx\n",
+ __FUNCTION__, info->ip);
+ return -1;
+ }
+ have_write_lock = 1;
+ }
+ info->hint = scr->hint;
+ info->prev_script = scr - unw.cache;
+
+ run_script(scr, info);
+
+ if (have_write_lock) {
+ write_unlock(&scr->lock);
+ spin_unlock_irqrestore(&unw.lock, flags);
+ } else
+ read_unlock(&scr->lock);
+ return 0;
+}
+
+int
+unw_unwind (struct unw_frame_info *info)
+{
+ unsigned long prev_ip, prev_sp, prev_bsp;
+ unsigned long ip, pr, num_regs;
+ STAT(unsigned long start, flags;)
+ int retval;
+
+ STAT(local_irq_save(flags); ++unw.stat.api.unwinds; start = ia64_get_itc());
+
+ prev_ip = info->ip;
+ prev_sp = info->sp;
+ prev_bsp = info->bsp;
+
+ /* restore the ip */
+ if (!info->rp_loc) {
+ /* FIXME: should really be level 0 but it occurs too often. KAO */
+ UNW_DPRINT(1, "unwind.%s: failed to locate return link (ip=0x%lx)!\n",
+ __FUNCTION__, info->ip);
+ STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags));
+ return -1;
+ }
+ ip = info->ip = *info->rp_loc;
+ if (ip < GATE_ADDR) {
+ UNW_DPRINT(2, "unwind.%s: reached user-space (ip=0x%lx)\n", __FUNCTION__, ip);
+ STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags));
+ return -1;
+ }
+
+ /* restore the cfm: */
+ if (!info->pfs_loc) {
+ UNW_DPRINT(0, "unwind.%s: failed to locate ar.pfs!\n", __FUNCTION__);
+ STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags));
+ return -1;
+ }
+ info->cfm_loc = info->pfs_loc;
+
+ /* restore the bsp: */
+ pr = info->pr;
+ num_regs = 0;
+ if ((info->flags & UNW_FLAG_INTERRUPT_FRAME)) {
+ info->pt = info->sp + 16;
+ if ((pr & (1UL << PRED_NON_SYSCALL)) != 0)
+ num_regs = *info->cfm_loc & 0x7f; /* size of frame */
+ info->pfs_loc =
+ (unsigned long *) (info->pt + offsetof(struct pt_regs, ar_pfs));
+ UNW_DPRINT(3, "unwind.%s: interrupt_frame pt 0x%lx\n", __FUNCTION__, info->pt);
+ } else
+ num_regs = (*info->cfm_loc >> 7) & 0x7f; /* size of locals */
+ info->bsp = (unsigned long) ia64_rse_skip_regs((unsigned long *) info->bsp, -num_regs);
+ if (info->bsp < info->regstk.limit || info->bsp > info->regstk.top) {
+ UNW_DPRINT(0, "unwind.%s: bsp (0x%lx) out of range [0x%lx-0x%lx]\n",
+ __FUNCTION__, info->bsp, info->regstk.limit, info->regstk.top);
+ STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags));
+ return -1;
+ }
+
+ /* restore the sp: */
+ info->sp = info->psp;
+ if (info->sp < info->memstk.top || info->sp > info->memstk.limit) {
+ UNW_DPRINT(0, "unwind.%s: sp (0x%lx) out of range [0x%lx-0x%lx]\n",
+ __FUNCTION__, info->sp, info->memstk.top, info->memstk.limit);
+ STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags));
+ return -1;
+ }
+
+ if (info->ip == prev_ip && info->sp == prev_sp && info->bsp == prev_bsp) {
+ UNW_DPRINT(0, "unwind.%s: ip, sp, bsp unchanged; stopping here (ip=0x%lx)\n",
+ __FUNCTION__, ip);
+ STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags));
+ return -1;
+ }
+
+ /* as we unwind, the saved ar.unat becomes the primary unat: */
+ info->pri_unat_loc = info->unat_loc;
+
+ /* finally, restore the predicates: */
+ unw_get_pr(info, &info->pr);
+
+ retval = find_save_locs(info);
+ STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags));
+ return retval;
+}
+EXPORT_SYMBOL(unw_unwind);
+
+int
+unw_unwind_to_user (struct unw_frame_info *info)
+{
+ unsigned long ip, sp, pr = 0;
+
+ while (unw_unwind(info) >= 0) {
+ unw_get_sp(info, &sp);
+ if ((long)((unsigned long)info->task + IA64_STK_OFFSET - sp)
+ < IA64_PT_REGS_SIZE) {
+ UNW_DPRINT(0, "unwind.%s: ran off the top of the kernel stack\n",
+ __FUNCTION__);
+ break;
+ }
+ if (unw_is_intr_frame(info) &&
+ (pr & (1UL << PRED_USER_STACK)))
+ return 0;
+ if (unw_get_pr (info, &pr) < 0) {
+ unw_get_rp(info, &ip);
+ UNW_DPRINT(0, "unwind.%s: failed to read "
+ "predicate register (ip=0x%lx)\n",
+ __FUNCTION__, ip);
+ return -1;
+ }
+ }
+ unw_get_ip(info, &ip);
+ UNW_DPRINT(0, "unwind.%s: failed to unwind to user-level (ip=0x%lx)\n",
+ __FUNCTION__, ip);
+ return -1;
+}
+EXPORT_SYMBOL(unw_unwind_to_user);
+
+static void
+init_frame_info (struct unw_frame_info *info, struct task_struct *t,
+ struct switch_stack *sw, unsigned long stktop)
+{
+ unsigned long rbslimit, rbstop, stklimit;
+ STAT(unsigned long start, flags;)
+
+ STAT(local_irq_save(flags); ++unw.stat.api.inits; start = ia64_get_itc());
+
+ /*
+ * Subtle stuff here: we _could_ unwind through the switch_stack frame but we
+ * don't want to do that because it would be slow as each preserved register would
+ * have to be processed. Instead, what we do here is zero out the frame info and
+ * start the unwind process at the function that created the switch_stack frame.
+ * When a preserved value in switch_stack needs to be accessed, run_script() will
+ * initialize the appropriate pointer on demand.
+ */
+ memset(info, 0, sizeof(*info));
+
+ rbslimit = (unsigned long) t + IA64_RBS_OFFSET;
+ rbstop = sw->ar_bspstore;
+ if (rbstop - (unsigned long) t >= IA64_STK_OFFSET)
+ rbstop = rbslimit;
+
+ stklimit = (unsigned long) t + IA64_STK_OFFSET;
+ if (stktop <= rbstop)
+ stktop = rbstop;
+
+ info->regstk.limit = rbslimit;
+ info->regstk.top = rbstop;
+ info->memstk.limit = stklimit;
+ info->memstk.top = stktop;
+ info->task = t;
+ info->sw = sw;
+ info->sp = info->psp = stktop;
+ info->pr = sw->pr;
+ UNW_DPRINT(3, "unwind.%s:\n"
+ " task 0x%lx\n"
+ " rbs = [.0x%lx-0x%lx)\n"
+ " stk = [.0x%lx-0x%lx)\n"
+ " pr 0x%lx\n"
+ " sw 0x%lx\n"
+ " sp 0x%lx\n",
+ __FUNCTION__, (unsigned long) t, rbslimit, rbstop, stktop, stklimit,
+ info->pr, (unsigned long) info->sw, info->sp);
+ STAT(unw.stat.api.init_time += ia64_get_itc() - start; local_irq_restore(flags));
+}
+
+void
+unw_init_frame_info (struct unw_frame_info *info, struct task_struct *t, struct switch_stack *sw)
+{
+ unsigned long sol;
+
+ init_frame_info(info, t, sw, (unsigned long) (sw + 1) - 16);
+ info->cfm_loc = &sw->ar_pfs;
+ sol = (*info->cfm_loc >> 7) & 0x7f;
+ info->bsp = (unsigned long) ia64_rse_skip_regs((unsigned long *) info->regstk.top, -sol);
+ info->ip = sw->b0;
+ UNW_DPRINT(3, "unwind.%s:\n"
+ " bsp 0x%lx\n"
+ " sol 0x%lx\n"
+ " ip 0x%lx\n",
+ __FUNCTION__, info->bsp, sol, info->ip);
+ find_save_locs(info);
+}
+
+EXPORT_SYMBOL(unw_init_frame_info);
+
+void
+unw_init_from_blocked_task (struct unw_frame_info *info, struct task_struct *t)
+{
+#ifdef XEN
+ struct switch_stack *sw = (struct switch_stack *) (t->arch._thread.ksp + 16);
+#else
+ struct switch_stack *sw = (struct switch_stack *) (t->thread.ksp + 16);
+#endif
+
+ UNW_DPRINT(1, "unwind.%s\n", __FUNCTION__);
+ unw_init_frame_info(info, t, sw);
+}
+EXPORT_SYMBOL(unw_init_from_blocked_task);
+
+static void
+init_unwind_table (struct unw_table *table, const char *name, unsigned long segment_base,
+ unsigned long gp, const void *table_start, const void *table_end)
+{
+ const struct unw_table_entry *start = table_start, *end = table_end;
+
+ table->name = name;
+ table->segment_base = segment_base;
+ table->gp = gp;
+ table->start = segment_base + start[0].start_offset;
+ table->end = segment_base + end[-1].end_offset;
+ table->array = start;
+ table->length = end - start;
+}
+
+#ifndef XEN
+void *
+unw_add_unwind_table (const char *name, unsigned long segment_base, unsigned long gp,
+ const void *table_start, const void *table_end)
+{
+ const struct unw_table_entry *start = table_start, *end = table_end;
+ struct unw_table *table;
+ unsigned long flags;
+
+ if (end - start <= 0) {
+ UNW_DPRINT(0, "unwind.%s: ignoring attempt to insert empty unwind table\n",
+ __FUNCTION__);
+ return NULL;
+ }
+
+ table = kmalloc(sizeof(*table), GFP_USER);
+ if (!table)
+ return NULL;
+
+ init_unwind_table(table, name, segment_base, gp, table_start, table_end);
+
+ spin_lock_irqsave(&unw.lock, flags);
+ {
+ /* keep kernel unwind table at the front (it's searched most commonly): */
+ table->next = unw.tables->next;
+ unw.tables->next = table;
+ }
+ spin_unlock_irqrestore(&unw.lock, flags);
+
+ return table;
+}
+
+void
+unw_remove_unwind_table (void *handle)
+{
+ struct unw_table *table, *prev;
+ struct unw_script *tmp;
+ unsigned long flags;
+ long index;
+
+ if (!handle) {
+ UNW_DPRINT(0, "unwind.%s: ignoring attempt to remove non-existent unwind table\n",
+ __FUNCTION__);
+ return;
+ }
+
+ table = handle;
+ if (table == &unw.kernel_table) {
+ UNW_DPRINT(0, "unwind.%s: sorry, freeing the kernel's unwind table is a "
+ "no-can-do!\n", __FUNCTION__);
+ return;
+ }
+
+ spin_lock_irqsave(&unw.lock, flags);
+ {
+ /* first, delete the table: */
+
+ for (prev = (struct unw_table *) &unw.tables; prev; prev = prev->next)
+ if (prev->next == table)
+ break;
+ if (!prev) {
+ UNW_DPRINT(0, "unwind.%s: failed to find unwind table %p\n",
+ __FUNCTION__, (void *) table);
+ spin_unlock_irqrestore(&unw.lock, flags);
+ return;
+ }
+ prev->next = table->next;
+ }
+ spin_unlock_irqrestore(&unw.lock, flags);
+
+ /* next, remove hash table entries for this table */
+
+ for (index = 0; index <= UNW_HASH_SIZE; ++index) {
+ tmp = unw.cache + unw.hash[index];
+ if (unw.hash[index] >= UNW_CACHE_SIZE
+ || tmp->ip < table->start || tmp->ip >= table->end)
+ continue;
+
+ write_lock(&tmp->lock);
+ {
+ if (tmp->ip >= table->start && tmp->ip < table->end) {
+ unw.hash[index] = tmp->coll_chain;
+ tmp->ip = 0;
+ }
+ }
+ write_unlock(&tmp->lock);
+ }
+
+ kfree(table);
+}
+
+static int __init
+create_gate_table (void)
+{
+ const struct unw_table_entry *entry, *start, *end;
+ unsigned long *lp, segbase = GATE_ADDR;
+ size_t info_size, size;
+ char *info;
+ Elf64_Phdr *punw = NULL, *phdr = (Elf64_Phdr *) (GATE_ADDR + GATE_EHDR->e_phoff);
+ int i;
+
+ for (i = 0; i < GATE_EHDR->e_phnum; ++i, ++phdr)
+ if (phdr->p_type == PT_IA_64_UNWIND) {
+ punw = phdr;
+ break;
+ }
+
+ if (!punw) {
+ printk("%s: failed to find gate DSO's unwind table!\n", __FUNCTION__);
+ return 0;
+ }
+
+ start = (const struct unw_table_entry *) punw->p_vaddr;
+ end = (struct unw_table_entry *) ((char *) start + punw->p_memsz);
+ size = 0;
+
+ unw_add_unwind_table("linux-gate.so", segbase, 0, start, end);
+
+ for (entry = start; entry < end; ++entry)
+ size += 3*8 + 8 + 8*UNW_LENGTH(*(u64 *) (segbase + entry->info_offset));
+ size += 8; /* reserve space for "end of table" marker */
+
+ unw.gate_table = kmalloc(size, GFP_KERNEL);
+ if (!unw.gate_table) {
+ unw.gate_table_size = 0;
+ printk(KERN_ERR "%s: unable to create unwind data for gate page!\n", __FUNCTION__);
+ return 0;
+ }
+ unw.gate_table_size = size;
+
+ lp = unw.gate_table;
+ info = (char *) unw.gate_table + size;
+
+ for (entry = start; entry < end; ++entry, lp += 3) {
+ info_size = 8 + 8*UNW_LENGTH(*(u64 *) (segbase + entry->info_offset));
+ info -= info_size;
+ memcpy(info, (char *) segbase + entry->info_offset, info_size);
+
+ lp[0] = segbase + entry->start_offset; /* start */
+ lp[1] = segbase + entry->end_offset; /* end */
+ lp[2] = info - (char *) unw.gate_table; /* info */
+ }
+ *lp = 0; /* end-of-table marker */
+ return 0;
+}
+
+__initcall(create_gate_table);
+#endif // !XEN
+
+void __init
+unw_init (void)
+{
+ extern char __gp[];
+ extern void unw_hash_index_t_is_too_narrow (void);
+ long i, off;
+
+ if (8*sizeof(unw_hash_index_t) < UNW_LOG_HASH_SIZE)
+ unw_hash_index_t_is_too_narrow();
+
+ unw.sw_off[unw.preg_index[UNW_REG_PRI_UNAT_GR]] = SW(CALLER_UNAT);
+ unw.sw_off[unw.preg_index[UNW_REG_BSPSTORE]] = SW(AR_BSPSTORE);
+ unw.sw_off[unw.preg_index[UNW_REG_PFS]] = SW(AR_PFS);
+ unw.sw_off[unw.preg_index[UNW_REG_RP]] = SW(B0);
+ unw.sw_off[unw.preg_index[UNW_REG_UNAT]] = SW(CALLER_UNAT);
+ unw.sw_off[unw.preg_index[UNW_REG_PR]] = SW(PR);
+ unw.sw_off[unw.preg_index[UNW_REG_LC]] = SW(AR_LC);
+ unw.sw_off[unw.preg_index[UNW_REG_FPSR]] = SW(AR_FPSR);
+ for (i = UNW_REG_R4, off = SW(R4); i <= UNW_REG_R7; ++i, off += 8)
+ unw.sw_off[unw.preg_index[i]] = off;
+ for (i = UNW_REG_B1, off = SW(B1); i <= UNW_REG_B5; ++i, off += 8)
+ unw.sw_off[unw.preg_index[i]] = off;
+ for (i = UNW_REG_F2, off = SW(F2); i <= UNW_REG_F5; ++i, off += 16)
+ unw.sw_off[unw.preg_index[i]] = off;
+ for (i = UNW_REG_F16, off = SW(F16); i <= UNW_REG_F31; ++i, off += 16)
+ unw.sw_off[unw.preg_index[i]] = off;
+
+ for (i = 0; i < UNW_CACHE_SIZE; ++i) {
+ if (i > 0)
+ unw.cache[i].lru_chain = (i - 1);
+ unw.cache[i].coll_chain = -1;
+ rwlock_init(&unw.cache[i].lock);
+ }
+ unw.lru_head = UNW_CACHE_SIZE - 1;
+ unw.lru_tail = 0;
+
+ init_unwind_table(&unw.kernel_table, "kernel", KERNEL_START, (unsigned long) __gp,
+ __start_unwind, __end_unwind);
+}
+
+/*
+ * DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED
+ *
+ * This system call has been deprecated. The new and improved way to get
+ * at the kernel's unwind info is via the gate DSO. The address of the
+ * ELF header for this DSO is passed to user-level via AT_SYSINFO_EHDR.
+ *
+ * DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED DEPRECATED
+ *
+ * This system call copies the unwind data into the buffer pointed to by BUF and returns
+ * the size of the unwind data. If BUF_SIZE is smaller than the size of the unwind data
+ * or if BUF is NULL, nothing is copied, but the system call still returns the size of the
+ * unwind data.
+ *
+ * The first portion of the unwind data contains an unwind table and rest contains the
+ * associated unwind info (in no particular order). The unwind table consists of a table
+ * of entries of the form:
+ *
+ * u64 start; (64-bit address of start of function)
+ * u64 end; (64-bit address of start of function)
+ * u64 info; (BUF-relative offset to unwind info)
+ *
+ * The end of the unwind table is indicated by an entry with a START address of zero.
+ *
+ * Please see the IA-64 Software Conventions and Runtime Architecture manual for details
+ * on the format of the unwind info.
+ *
+ * ERRORS
+ * EFAULT BUF points outside your accessible address space.
+ */
+asmlinkage long
+sys_getunwind (void __user *buf, size_t buf_size)
+{
+ if (buf && buf_size >= unw.gate_table_size)
+ if (copy_to_user(buf, unw.gate_table, unw.gate_table_size) != 0)
+ return -EFAULT;
+ return unw.gate_table_size;
+}
diff -r f89906acd9f6 -r 45c4e735fc8c xen/arch/ia64/linux-xen/unwind_decoder.c
--- /dev/null Fri Dec 30 22:11:08 2005
+++ b/xen/arch/ia64/linux-xen/unwind_decoder.c Sat Dec 31 05:40:13 2005
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2000 Hewlett-Packard Co
+ * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com>
+ *
+ * Generic IA-64 unwind info decoder.
+ *
+ * This file is used both by the Linux kernel and objdump. Please keep
+ * the two copies of this file in sync.
+ *
+ * You need to customize the decoder by defining the following
+ * macros/constants before including this file:
+ *
+ * Types:
+ * unw_word Unsigned integer type with at least 64 bits
+ *
+ * Register names:
+ * UNW_REG_BSP
+ * UNW_REG_BSPSTORE
+ * UNW_REG_FPSR
+ * UNW_REG_LC
+ * UNW_REG_PFS
+ * UNW_REG_PR
+ * UNW_REG_RNAT
+ * UNW_REG_PSP
+ * UNW_REG_RP
+ * UNW_REG_UNAT
+ *
+ * Decoder action macros:
+ * UNW_DEC_BAD_CODE(code)
+ * UNW_DEC_ABI(fmt,abi,context,arg)
+ * UNW_DEC_BR_GR(fmt,brmask,gr,arg)
+ * UNW_DEC_BR_MEM(fmt,brmask,arg)
+ * UNW_DEC_COPY_STATE(fmt,label,arg)
+ * UNW_DEC_EPILOGUE(fmt,t,ecount,arg)
+ * UNW_DEC_FRGR_MEM(fmt,grmask,frmask,arg)
+ * UNW_DEC_FR_MEM(fmt,frmask,arg)
+ * UNW_DEC_GR_GR(fmt,grmask,gr,arg)
+ * UNW_DEC_GR_MEM(fmt,grmask,arg)
+ * UNW_DEC_LABEL_STATE(fmt,label,arg)
+ * UNW_DEC_MEM_STACK_F(fmt,t,size,arg)
+ * UNW_DEC_MEM_STACK_V(fmt,t,arg)
+ * UNW_DEC_PRIUNAT_GR(fmt,r,arg)
+ * UNW_DEC_PRIUNAT_WHEN_GR(fmt,t,arg)
+ * UNW_DEC_PRIUNAT_WHEN_MEM(fmt,t,arg)
+ * UNW_DEC_PRIUNAT_WHEN_PSPREL(fmt,pspoff,arg)
+ * UNW_DEC_PRIUNAT_WHEN_SPREL(fmt,spoff,arg)
+ * UNW_DEC_PROLOGUE(fmt,body,rlen,arg)
+ * UNW_DEC_PROLOGUE_GR(fmt,rlen,mask,grsave,arg)
+ * UNW_DEC_REG_PSPREL(fmt,reg,pspoff,arg)
+ * UNW_DEC_REG_REG(fmt,src,dst,arg)
+ * UNW_DEC_REG_SPREL(fmt,reg,spoff,arg)
+ * UNW_DEC_REG_WHEN(fmt,reg,t,arg)
+ * UNW_DEC_RESTORE(fmt,t,abreg,arg)
+ * UNW_DEC_RESTORE_P(fmt,qp,t,abreg,arg)
+ * UNW_DEC_SPILL_BASE(fmt,pspoff,arg)
+ * UNW_DEC_SPILL_MASK(fmt,imaskp,arg)
+ * UNW_DEC_SPILL_PSPREL(fmt,t,abreg,pspoff,arg)
+ * UNW_DEC_SPILL_PSPREL_P(fmt,qp,t,abreg,pspoff,arg)
+ * UNW_DEC_SPILL_REG(fmt,t,abreg,x,ytreg,arg)
+ * UNW_DEC_SPILL_REG_P(fmt,qp,t,abreg,x,ytreg,arg)
+ * UNW_DEC_SPILL_SPREL(fmt,t,abreg,spoff,arg)
+ * UNW_DEC_SPILL_SPREL_P(fmt,qp,t,abreg,pspoff,arg)
+ */
+
+static unw_word
+unw_decode_uleb128 (unsigned char **dpp)
+{
+ unsigned shift = 0;
+ unw_word byte, result = 0;
+ unsigned char *bp = *dpp;
+
+ while (1)
+ {
+ byte = *bp++;
+ result |= (byte & 0x7f) << shift;
+ if ((byte & 0x80) == 0)
+ break;
+ shift += 7;
+ }
+ *dpp = bp;
+ return result;
+}
+
+static unsigned char *
+unw_decode_x1 (unsigned char *dp, unsigned char code, void *arg)
+{
+ unsigned char byte1, abreg;
+ unw_word t, off;
+
+ byte1 = *dp++;
+ t = unw_decode_uleb128 (&dp);
+ off = unw_decode_uleb128 (&dp);
+ abreg = (byte1 & 0x7f);
+ if (byte1 & 0x80)
+ UNW_DEC_SPILL_SPREL(X1, t, abreg, off, arg);
+ else
+ UNW_DEC_SPILL_PSPREL(X1, t, abreg, off, arg);
+ return dp;
+}
+
+static unsigned char *
+unw_decode_x2 (unsigned char *dp, unsigned char code, void *arg)
+{
+ unsigned char byte1, byte2, abreg, x, ytreg;
+ unw_word t;
+
+ byte1 = *dp++; byte2 = *dp++;
+ t = unw_decode_uleb128 (&dp);
+ abreg = (byte1 & 0x7f);
+ ytreg = byte2;
+ x = (byte1 >> 7) & 1;
+ if ((byte1 & 0x80) == 0 && ytreg == 0)
+ UNW_DEC_RESTORE(X2, t, abreg, arg);
+ else
+ UNW_DEC_SPILL_REG(X2, t, abreg, x, ytreg, arg);
+ return dp;
+}
+
+static unsigned char *
+unw_decode_x3 (unsigned char *dp, unsigned char code, void *arg)
+{
+ unsigned char byte1, byte2, abreg, qp;
+ unw_word t, off;
+
+ byte1 = *dp++; byte2 = *dp++;
+ t = unw_decode_uleb128 (&dp);
+ off = unw_decode_uleb128 (&dp);
+
+ qp = (byte1 & 0x3f);
+ abreg = (byte2 & 0x7f);
+
+ if (byte1 & 0x80)
+ UNW_DEC_SPILL_SPREL_P(X3, qp, t, abreg, off, arg);
+ else
+ UNW_DEC_SPILL_PSPREL_P(X3, qp, t, abreg, off, arg);
+ return dp;
+}
+
+static unsigned char *
+unw_decode_x4 (unsigned char *dp, unsigned char code, void *arg)
+{
+ unsigned char byte1, byte2, byte3, qp, abreg, x, ytreg;
+ unw_word t;
+
+ byte1 = *dp++; byte2 = *dp++; byte3 = *dp++;
+ t = unw_decode_uleb128 (&dp);
+
+ qp = (byte1 & 0x3f);
+ abreg = (byte2 & 0x7f);
+ x = (byte2 >> 7) & 1;
+ ytreg = byte3;
+
+ if ((byte2 & 0x80) == 0 && byte3 == 0)
+ UNW_DEC_RESTORE_P(X4, qp, t, abreg, arg);
+ else
+ UNW_DEC_SPILL_REG_P(X4, qp, t, abreg, x, ytreg, arg);
+ return dp;
+}
+
+static unsigned char *
+unw_decode_r1 (unsigned char *dp, unsigned char code, void *arg)
+{
+ int body = (code & 0x20) != 0;
+ unw_word rlen;
+
+ rlen = (code & 0x1f);
+ UNW_DEC_PROLOGUE(R1, body, rlen, arg);
+ return dp;
+}
+
+static unsigned char *
+unw_decode_r2 (unsigned char *dp, unsigned char code, void *arg)
+{
+ unsigned char byte1, mask, grsave;
+ unw_word rlen;
+
+ byte1 = *dp++;
+
+ mask = ((code & 0x7) << 1) | ((byte1 >> 7) & 1);
+ grsave = (byte1 & 0x7f);
+ rlen = unw_decode_uleb128 (&dp);
+ UNW_DEC_PROLOGUE_GR(R2, rlen, mask, grsave, arg);
+ return dp;
+}
+
+static unsigned char *
+unw_decode_r3 (unsigned char *dp, unsigned char code, void *arg)
+{
+ unw_word rlen;
+
+ rlen = unw_decode_uleb128 (&dp);
+ UNW_DEC_PROLOGUE(R3, ((code & 0x3) == 1), rlen, arg);
+ return dp;
+}
+
+static unsigned char *
+unw_decode_p1 (unsigned char *dp, unsigned char code, void *arg)
+{
+ unsigned char brmask = (code & 0x1f);
+
+ UNW_DEC_BR_MEM(P1, brmask, arg);
+ return dp;
+}
+
+static unsigned char *
+unw_decode_p2_p5 (unsigned char *dp, unsigned char code, void *arg)
+{
+ if ((code & 0x10) == 0)
+ {
+ unsigned char byte1 = *dp++;
+
+ UNW_DEC_BR_GR(P2, ((code & 0xf) << 1) | ((byte1 >> 7) & 1),
+ (byte1 & 0x7f), arg);
+ }
+ else if ((code & 0x08) == 0)
+ {
+ unsigned char byte1 = *dp++, r, dst;
+
+ r = ((code & 0x7) << 1) | ((byte1 >> 7) & 1);
+ dst = (byte1 & 0x7f);
+ switch (r)
+ {
+ case 0: UNW_DEC_REG_GR(P3, UNW_REG_PSP, dst, arg); break;
+ case 1: UNW_DEC_REG_GR(P3, UNW_REG_RP, dst, arg); break;
+ case 2: UNW_DEC_REG_GR(P3, UNW_REG_PFS, dst, arg); break;
+ case 3: UNW_DEC_REG_GR(P3, UNW_REG_PR, dst, arg); break;
+ case 4: UNW_DEC_REG_GR(P3, UNW_REG_UNAT, dst, arg); break;
+ case 5: UNW_DEC_REG_GR(P3, UNW_REG_LC, dst, arg); break;
+ case 6: UNW_DEC_RP_BR(P3, dst, arg); break;
+ case 7: UNW_DEC_REG_GR(P3, UNW_REG_RNAT, dst, arg); break;
+ case 8: UNW_DEC_REG_GR(P3, UNW_REG_BSP, dst, arg); break;
+ case 9: UNW_DEC_REG_GR(P3, UNW_REG_BSPSTORE, dst, arg); break;
+ case 10: UNW_DEC_REG_GR(P3, UNW_REG_FPSR, dst, arg); break;
+ case 11: UNW_DEC_PRIUNAT_GR(P3, dst, arg); break;
+ default: UNW_DEC_BAD_CODE(r); break;
+ }
+ }
+ else if ((code & 0x7) == 0)
+ UNW_DEC_SPILL_MASK(P4, dp, arg);
+ else if ((code & 0x7) == 1)
+ {
+ unw_word grmask, frmask, byte1, byte2, byte3;
+
+ byte1 = *dp++; byte2 = *dp++; byte3 = *dp++;
+ grmask = ((byte1 >> 4) & 0xf);
+ frmask = ((byte1 & 0xf) << 16) | (byte2 << 8) | byte3;
+ UNW_DEC_FRGR_MEM(P5, grmask, frmask, arg);
+ }
+ else
+ UNW_DEC_BAD_CODE(code);
+ return dp;
+}
+
+static unsigned char *
+unw_decode_p6 (unsigned char *dp, unsigned char code, void *arg)
+{
+ int gregs = (code & 0x10) != 0;
+ unsigned char mask = (code & 0x0f);
+
+ if (gregs)
+ UNW_DEC_GR_MEM(P6, mask, arg);
+ else
+ UNW_DEC_FR_MEM(P6, mask, arg);
+ return dp;
+}
+
+static unsigned char *
+unw_decode_p7_p10 (unsigned char *dp, unsigned char code, void *arg)
+{
+ unsigned char r, byte1, byte2;
+ unw_word t, size;
+
+ if ((code & 0x10) == 0)
+ {
+ r = (code & 0xf);
+ t = unw_decode_uleb128 (&dp);
+ switch (r)
+ {
+ case 0:
+ size = unw_decode_uleb128 (&dp);
+ UNW_DEC_MEM_STACK_F(P7, t, size, arg);
+ break;
+
+ case 1: UNW_DEC_MEM_STACK_V(P7, t, arg); break;
+ case 2: UNW_DEC_SPILL_BASE(P7, t, arg); break;
+ case 3: UNW_DEC_REG_SPREL(P7, UNW_REG_PSP, t, arg); break;
+ case 4: UNW_DEC_REG_WHEN(P7, UNW_REG_RP, t, arg); break;
+ case 5: UNW_DEC_REG_PSPREL(P7, UNW_REG_RP, t, arg); break;
+ case 6: UNW_DEC_REG_WHEN(P7, UNW_REG_PFS, t, arg); break;
+ case 7: UNW_DEC_REG_PSPREL(P7, UNW_REG_PFS, t, arg); break;
+ case 8: UNW_DEC_REG_WHEN(P7, UNW_REG_PR, t, arg); break;
+ case 9: UNW_DEC_REG_PSPREL(P7, UNW_REG_PR, t, arg); break;
+ case 10: UNW_DEC_REG_WHEN(P7, UNW_REG_LC, t, arg); break;
+ case 11: UNW_DEC_REG_PSPREL(P7, UNW_REG_LC, t, arg); break;
+ case 12: UNW_DEC_REG_WHEN(P7, UNW_REG_UNAT, t, arg); break;
+ case 13: UNW_DEC_REG_PSPREL(P7, UNW_REG_UNAT, t, arg); break;
+ case 14: UNW_DEC_REG_WHEN(P7, UNW_REG_FPSR, t, arg); break;
+ case 15: UNW_DEC_REG_PSPREL(P7, UNW_REG_FPSR, t, arg); break;
+ default: UNW_DEC_BAD_CODE(r); break;
+ }
+ }
+ else
+ {
+ switch (code & 0xf)
+ {
+ case 0x0: /* p8 */
+ {
+ r = *dp++;
+ t = unw_decode_uleb128 (&dp);
+ switch (r)
+ {
+ case 1: UNW_DEC_REG_SPREL(P8, UNW_REG_RP, t, arg); break;
+ case 2: UNW_DEC_REG_SPREL(P8, UNW_REG_PFS, t, arg); break;
+ case 3: UNW_DEC_REG_SPREL(P8, UNW_REG_PR, t, arg); break;
+ case 4: UNW_DEC_REG_SPREL(P8, UNW_REG_LC, t, arg); break;
+ case 5: UNW_DEC_REG_SPREL(P8, UNW_REG_UNAT, t, arg); break;
+ case 6: UNW_DEC_REG_SPREL(P8, UNW_REG_FPSR, t, arg); break;
+ case 7: UNW_DEC_REG_WHEN(P8, UNW_REG_BSP, t, arg); break;
+ case 8: UNW_DEC_REG_PSPREL(P8, UNW_REG_BSP, t, arg); break;
+ case 9: UNW_DEC_REG_SPREL(P8, UNW_REG_BSP, t, arg); break;
+ case 10: UNW_DEC_REG_WHEN(P8, UNW_REG_BSPSTORE, t, arg); break;
+ case 11: UNW_DEC_REG_PSPREL(P8, UNW_REG_BSPSTORE, t, arg); break;
+ case 12: UNW_DEC_REG_SPREL(P8, UNW_REG_BSPSTORE, t, arg); break;
+ case 13: UNW_DEC_REG_WHEN(P8, UNW_REG_RNAT, t, arg); break;
+ case 14: UNW_DEC_REG_PSPREL(P8, UNW_REG_RNAT, t, arg); break;
+ case 15: UNW_DEC_REG_SPREL(P8, UNW_REG_RNAT, t, arg); break;
+ case 16: UNW_DEC_PRIUNAT_WHEN_GR(P8, t, arg); break;
+ case 17: UNW_DEC_PRIUNAT_PSPREL(P8, t, arg); break;
+ case 18: UNW_DEC_PRIUNAT_SPREL(P8, t, arg); break;
+ case 19: UNW_DEC_PRIUNAT_WHEN_MEM(P8, t, arg); break;
+ default: UNW_DEC_BAD_CODE(r); break;
+ }
+ }
+ break;
+
+ case 0x1:
+ byte1 = *dp++; byte2 = *dp++;
+ UNW_DEC_GR_GR(P9, (byte1 & 0xf), (byte2 & 0x7f), arg);
+ break;
+
+ case 0xf: /* p10 */
+ byte1 = *dp++; byte2 = *dp++;
+ UNW_DEC_ABI(P10, byte1, byte2, arg);
+ break;
+
+ case 0x9:
+ return unw_decode_x1 (dp, code, arg);
+
+ case 0xa:
+ return unw_decode_x2 (dp, code, arg);
+
+ case 0xb:
+ return unw_decode_x3 (dp, code, arg);
+
+ case 0xc:
+ return unw_decode_x4 (dp, code, arg);
+
+ default:
+ UNW_DEC_BAD_CODE(code);
+ break;
+ }
+ }
+ return dp;
+}
+
+static unsigned char *
+unw_decode_b1 (unsigned char *dp, unsigned char code, void *arg)
+{
+ unw_word label = (code & 0x1f);
+
+ if ((code & 0x20) != 0)
+ UNW_DEC_COPY_STATE(B1, label, arg);
+ else
+ UNW_DEC_LABEL_STATE(B1, label, arg);
+ return dp;
+}
+
+static unsigned char *
+unw_decode_b2 (unsigned char *dp, unsigned char code, void *arg)
+{
+ unw_word t;
+
+ t = unw_decode_uleb128 (&dp);
+ UNW_DEC_EPILOGUE(B2, t, (code & 0x1f), arg);
+ return dp;
+}
+
+static unsigned char *
+unw_decode_b3_x4 (unsigned char *dp, unsigned char code, void *arg)
+{
+ unw_word t, ecount, label;
+
+ if ((code & 0x10) == 0)
+ {
+ t = unw_decode_uleb128 (&dp);
+ ecount = unw_decode_uleb128 (&dp);
+ UNW_DEC_EPILOGUE(B3, t, ecount, arg);
+ }
+ else if ((code & 0x07) == 0)
+ {
+ label = unw_decode_uleb128 (&dp);
+ if ((code & 0x08) != 0)
+ UNW_DEC_COPY_STATE(B4, label, arg);
+ else
+ UNW_DEC_LABEL_STATE(B4, label, arg);
+ }
+ else
+ switch (code & 0x7)
+ {
+ case 1: return unw_decode_x1 (dp, code, arg);
+ case 2: return unw_decode_x2 (dp, code, arg);
+ case 3: return unw_decode_x3 (dp, code, arg);
+ case 4: return unw_decode_x4 (dp, code, arg);
+ default: UNW_DEC_BAD_CODE(code); break;
+ }
+ return dp;
+}
+
+typedef unsigned char *(*unw_decoder) (unsigned char *, unsigned char, void *);
+
+static unw_decoder unw_decode_table[2][8] =
+{
+ /* prologue table: */
+ {
+ unw_decode_r1, /* 0 */
+ unw_decode_r1,
+ unw_decode_r2,
+ unw_decode_r3,
+ unw_decode_p1, /* 4 */
+ unw_decode_p2_p5,
+ unw_decode_p6,
+ unw_decode_p7_p10
+ },
+ {
+ unw_decode_r1, /* 0 */
+ unw_decode_r1,
+ unw_decode_r2,
+ unw_decode_r3,
+ unw_decode_b1, /* 4 */
+ unw_decode_b1,
+ unw_decode_b2,
+ unw_decode_b3_x4
+ }
+};
+
+/*
+ * Decode one descriptor and return address of next descriptor.
+ */
+static inline unsigned char *
+unw_decode (unsigned char *dp, int inside_body, void *arg)
+{
+ unw_decoder decoder;
+ unsigned char code;
+
+ code = *dp++;
+ decoder = unw_decode_table[inside_body][code >> 5];
+ dp = (*decoder) (dp, code, arg);
+ return dp;
+}
diff -r f89906acd9f6 -r 45c4e735fc8c xen/arch/ia64/linux-xen/unwind_i.h
--- /dev/null Fri Dec 30 22:11:08 2005
+++ b/xen/arch/ia64/linux-xen/unwind_i.h Sat Dec 31 05:40:13 2005
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2000, 2002-2003 Hewlett-Packard Co
+ * David Mosberger-Tang <davidm@hpl.hp.com>
+ *
+ * Kernel unwind support.
+ */
+
+#define UNW_VER(x) ((x) >> 48)
+#define UNW_FLAG_MASK 0x0000ffff00000000
+#define UNW_FLAG_OSMASK 0x0000f00000000000
+#define UNW_FLAG_EHANDLER(x) ((x) & 0x0000000100000000L)
+#define UNW_FLAG_UHANDLER(x) ((x) & 0x0000000200000000L)
+#define UNW_LENGTH(x) ((x) & 0x00000000ffffffffL)
+
+enum unw_register_index {
+ /* primary unat: */
+ UNW_REG_PRI_UNAT_GR,
+ UNW_REG_PRI_UNAT_MEM,
+
+ /* register stack */
+ UNW_REG_BSP, /* register stack pointer */
+ UNW_REG_BSPSTORE,
+ UNW_REG_PFS, /* previous function state */
+ UNW_REG_RNAT,
+ /* memory stack */
+ UNW_REG_PSP, /* previous memory stack pointer */
+ /* return pointer: */
+ UNW_REG_RP,
+
+ /* preserved registers: */
+ UNW_REG_R4, UNW_REG_R5, UNW_REG_R6, UNW_REG_R7,
+ UNW_REG_UNAT, UNW_REG_PR, UNW_REG_LC, UNW_REG_FPSR,
+ UNW_REG_B1, UNW_REG_B2, UNW_REG_B3, UNW_REG_B4, UNW_REG_B5,
+ UNW_REG_F2, UNW_REG_F3, UNW_REG_F4, UNW_REG_F5,
+ UNW_REG_F16, UNW_REG_F17, UNW_REG_F18, UNW_REG_F19,
+ UNW_REG_F20, UNW_REG_F21, UNW_REG_F22, UNW_REG_F23,
+ UNW_REG_F24, UNW_REG_F25, UNW_REG_F26, UNW_REG_F27,
+ UNW_REG_F28, UNW_REG_F29, UNW_REG_F30, UNW_REG_F31,
+ UNW_NUM_REGS
+};
+
+struct unw_info_block {
+ u64 header;
+ u64 desc[0]; /* unwind descriptors */
+ /* personality routine and language-specific data follow behind descriptors */
+};
+
+struct unw_table {
+ struct unw_table *next; /* must be first member! */
+ const char *name;
+ unsigned long gp; /* global pointer for this load-module */
+ unsigned long segment_base; /* base for offsets in the unwind table entries */
+ unsigned long start;
+ unsigned long end;
+ const struct unw_table_entry *array;
+ unsigned long length;
+};
+
+enum unw_where {
+ UNW_WHERE_NONE, /* register isn't saved at all */
+ UNW_WHERE_GR, /* register is saved in a general register */
+ UNW_WHERE_FR, /* register is saved in a floating-point register */
+ UNW_WHERE_BR, /* register is saved in a branch register */
+ UNW_WHERE_SPREL, /* register is saved on memstack (sp-relative) */
+ UNW_WHERE_PSPREL, /* register is saved on memstack (psp-relative) */
+ /*
+ * At the end of each prologue these locations get resolved to
+ * UNW_WHERE_PSPREL and UNW_WHERE_GR, respectively:
+ */
+ UNW_WHERE_SPILL_HOME, /* register is saved in its spill home */
+ UNW_WHERE_GR_SAVE /* register is saved in next general register */
+};
+
+#define UNW_WHEN_NEVER 0x7fffffff
+
+struct unw_reg_info {
+ unsigned long val; /* save location: register number or offset */
+ enum unw_where where; /* where the register gets saved */
+ int when; /* when the register gets saved */
+};
+
+struct unw_reg_state {
+ struct unw_reg_state *next; /* next (outer) element on state stack */
+ struct unw_reg_info reg[UNW_NUM_REGS]; /* register save locations */
+};
+
+struct unw_labeled_state {
+ struct unw_labeled_state *next; /* next labeled state (or NULL) */
+ unsigned long label; /* label for this state */
+ struct unw_reg_state saved_state;
+};
+
+struct unw_state_record {
+ unsigned int first_region : 1; /* is this the first region? */
+ unsigned int done : 1; /* are we done scanning descriptors? */
+ unsigned int any_spills : 1; /* got any register spills? */
+ unsigned int in_body : 1; /* are we inside a body (as opposed to a prologue)? */
+ unsigned long flags; /* see UNW_FLAG_* in unwind.h */
+
+ u8 *imask; /* imask of spill_mask record or NULL */
+ unsigned long pr_val; /* predicate values */
+ unsigned long pr_mask; /* predicate mask */
+ long spill_offset; /* psp-relative offset for spill base */
+ int region_start;
+ int region_len;
+ int epilogue_start;
+ int epilogue_count;
+ int when_target;
+
+ u8 gr_save_loc; /* next general register to use for saving a register */
+ u8 return_link_reg; /* branch register in which the return link is passed */
+
+ struct unw_labeled_state *labeled_states; /* list of all labeled states */
+ struct unw_reg_state curr; /* current state */
+};
+
+enum unw_nat_type {
+ UNW_NAT_NONE, /* NaT not represented */
+ UNW_NAT_VAL, /* NaT represented by NaT value (fp reg) */
+ UNW_NAT_MEMSTK, /* NaT value is in unat word at offset OFF */
+ UNW_NAT_REGSTK /* NaT is in rnat */
+};
+
+enum unw_insn_opcode {
+ UNW_INSN_ADD, /* s[dst] += val */
+ UNW_INSN_ADD_PSP, /* s[dst] = (s.psp + val) */
+ UNW_INSN_ADD_SP, /* s[dst] = (s.sp + val) */
+ UNW_INSN_MOVE, /* s[dst] = s[val] */
+ UNW_INSN_MOVE2, /* s[dst] = s[val]; s[dst+1] = s[val+1] */
+ UNW_INSN_MOVE_STACKED, /* s[dst] = ia64_rse_skip(*s.bsp, val) */
+ UNW_INSN_SETNAT_MEMSTK, /* s[dst+1].nat.type = MEMSTK;
+ s[dst+1].nat.off = *s.pri_unat - s[dst] */
+ UNW_INSN_SETNAT_TYPE, /* s[dst+1].nat.type = val */
+ UNW_INSN_LOAD, /* s[dst] = *s[val] */
+ UNW_INSN_MOVE_SCRATCH, /* s[dst] = scratch reg "val" */
+ UNW_INSN_MOVE_CONST, /* s[dst] = constant reg "val" */
+};
+
+struct unw_insn {
+ unsigned int opc : 4;
+ unsigned int dst : 9;
+ signed int val : 19;
+};
+
+/*
+ * Preserved general static registers (r4-r7) give rise to two script
+ * instructions; everything else yields at most one instruction; at
+ * the end of the script, the psp gets popped, accounting for one more
+ * instruction.
+ */
+#define UNW_MAX_SCRIPT_LEN (UNW_NUM_REGS + 5)
+
+struct unw_script {
+ unsigned long ip; /* ip this script is for */
+ unsigned long pr_mask; /* mask of predicates script depends on */
+ unsigned long pr_val; /* predicate values this script is for */
+ rwlock_t lock;
+ unsigned int flags; /* see UNW_FLAG_* in unwind.h */
+ unsigned short lru_chain; /* used for least-recently-used chain */
+ unsigned short coll_chain; /* used for hash collisions */
+ unsigned short hint; /* hint for next script to try (or -1) */
+ unsigned short count; /* number of instructions in script */
+ struct unw_insn insn[UNW_MAX_SCRIPT_LEN];
+};

_______________________________________________
Xen-changelog mailing list
Xen-changelog@lists.xensource.com
http://lists.xensource.com/xen-changelog