summaryrefslogtreecommitdiffstats
path: root/arch/ia64/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ia64/kernel')
-rw-r--r--arch/ia64/kernel/Makefile20
-rw-r--r--arch/ia64/kernel/acpi.c10
-rw-r--r--arch/ia64/kernel/brl_emu.c220
-rw-r--r--arch/ia64/kernel/efi.c73
-rw-r--r--arch/ia64/kernel/efi_stub.S73
-rw-r--r--arch/ia64/kernel/entry.S573
-rw-r--r--arch/ia64/kernel/entry.h59
-rw-r--r--arch/ia64/kernel/fw-emu.c20
-rw-r--r--arch/ia64/kernel/gate.S26
-rw-r--r--arch/ia64/kernel/head.S122
-rw-r--r--arch/ia64/kernel/ia64_ksyms.c72
-rw-r--r--arch/ia64/kernel/irq.c17
-rw-r--r--arch/ia64/kernel/irq_ia64.c133
-rw-r--r--arch/ia64/kernel/ivt.S196
-rw-r--r--arch/ia64/kernel/mca.c5
-rw-r--r--arch/ia64/kernel/mca_asm.S1
-rw-r--r--arch/ia64/kernel/minstate.h1
-rw-r--r--arch/ia64/kernel/pal.S192
-rw-r--r--arch/ia64/kernel/palinfo.c780
-rw-r--r--arch/ia64/kernel/pci-dma.c4
-rw-r--r--arch/ia64/kernel/pci.c6
-rw-r--r--arch/ia64/kernel/process.c193
-rw-r--r--arch/ia64/kernel/ptrace.c755
-rw-r--r--arch/ia64/kernel/sal_stub.S118
-rw-r--r--arch/ia64/kernel/setup.c94
-rw-r--r--arch/ia64/kernel/signal.c279
-rw-r--r--arch/ia64/kernel/smp.c198
-rw-r--r--arch/ia64/kernel/sys_ia64.c173
-rw-r--r--arch/ia64/kernel/time.c120
-rw-r--r--arch/ia64/kernel/traps.c56
-rw-r--r--arch/ia64/kernel/unaligned.c57
-rw-r--r--arch/ia64/kernel/unwind.c2015
-rw-r--r--arch/ia64/kernel/unwind_decoder.c459
-rw-r--r--arch/ia64/kernel/unwind_i.h160
34 files changed, 6027 insertions, 1253 deletions
diff --git a/arch/ia64/kernel/Makefile b/arch/ia64/kernel/Makefile
index 225cbec5d..3fb62560d 100644
--- a/arch/ia64/kernel/Makefile
+++ b/arch/ia64/kernel/Makefile
@@ -1,11 +1,6 @@
#
# Makefile for the linux kernel.
#
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definitions are now in the main makefile...
.S.s:
$(CPP) $(AFLAGS) -o $*.s $<
@@ -15,16 +10,19 @@
all: kernel.o head.o init_task.o
O_TARGET := kernel.o
-O_OBJS := acpi.o entry.o gate.o efi.o efi_stub.o irq.o irq_ia64.o irq_sapic.o ivt.o \
- pal.o pci-dma.o process.o perfmon.o ptrace.o sal.o sal_stub.o semaphore.o setup.o \
+O_OBJS := acpi.o entry.o gate.o efi.o efi_stub.o irq.o irq_ia64.o irq_sapic.o ivt.o \
+ pal.o pci-dma.o process.o perfmon.o ptrace.o sal.o semaphore.o setup.o \
signal.o sys_ia64.o traps.o time.o unaligned.o unwind.o
-#O_OBJS := fpreg.o
-#OX_OBJS := ia64_ksyms.o
+OX_OBJS := ia64_ksyms.o
ifdef CONFIG_IA64_GENERIC
O_OBJS += machvec.o
endif
+ifdef CONFIG_IA64_PALINFO
+O_OBJS += palinfo.o
+endif
+
ifdef CONFIG_PCI
O_OBJS += pci.o
endif
@@ -37,6 +35,10 @@ ifdef CONFIG_IA64_MCA
O_OBJS += mca.o mca_asm.o
endif
+ifdef CONFIG_IA64_BRL_EMU
+O_OBJS += brl_emu.o
+endif
+
clean::
include $(TOPDIR)/Rules.make
diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c
index 72e10a683..20521da36 100644
--- a/arch/ia64/kernel/acpi.c
+++ b/arch/ia64/kernel/acpi.c
@@ -89,16 +89,16 @@ acpi_iosapic(char *p)
#ifdef CONFIG_IA64_DIG
acpi_entry_iosapic_t *iosapic = (acpi_entry_iosapic_t *) p;
unsigned int ver, v;
- int l, pins;
+ int l, max_pin;
ver = iosapic_version(iosapic->address);
- pins = (ver >> 16) & 0xff;
+ max_pin = (ver >> 16) & 0xff;
printk("IOSAPIC Version %x.%x: address 0x%lx IRQs 0x%x - 0x%x\n",
(ver & 0xf0) >> 4, (ver & 0x0f), iosapic->address,
- iosapic->irq_base, iosapic->irq_base + pins);
+ iosapic->irq_base, iosapic->irq_base + max_pin);
- for (l = 0; l < pins; l++) {
+ for (l = 0; l <= max_pin; l++) {
v = iosapic->irq_base + l;
if (v < 16)
v = isa_irq_to_vector(v);
@@ -110,7 +110,7 @@ acpi_iosapic(char *p)
iosapic_addr(v) = (unsigned long) ioremap(iosapic->address, 0);
iosapic_baseirq(v) = iosapic->irq_base;
}
- iosapic_init(iosapic->address);
+ iosapic_init(iosapic->address, iosapic->irq_base);
#endif
}
diff --git a/arch/ia64/kernel/brl_emu.c b/arch/ia64/kernel/brl_emu.c
new file mode 100644
index 000000000..8948b7bb2
--- /dev/null
+++ b/arch/ia64/kernel/brl_emu.c
@@ -0,0 +1,220 @@
+/*
+ * Emulation of the "brl" instruction for IA64 processors that
+ * don't support it in hardware.
+ * Author: Stephan Zeisset, Intel Corp. <Stephan.Zeisset@intel.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <asm/processor.h>
+
+extern char ia64_set_b1, ia64_set_b2, ia64_set_b3, ia64_set_b4, ia64_set_b5;
+
+struct illegal_op_return {
+ unsigned long fkt, arg1, arg2, arg3;
+};
+
+/*
+ * The unimplemented bits of a virtual address must be set
+ * to the value of the most significant implemented bit.
+ * unimpl_va_mask includes all unimplemented bits and
+ * the most significant implemented bit, so the result
+ * of an and operation with the mask must be all 0's
+ * or all 1's for the address to be valid.
+ */
+#define unimplemented_virtual_address(va) ( \
+ ((va) & my_cpu_data.unimpl_va_mask) != 0 && \
+ ((va) & my_cpu_data.unimpl_va_mask) != my_cpu_data.unimpl_va_mask \
+)
+
+/*
+ * The unimplemented bits of a physical address must be 0.
+ * unimpl_pa_mask includes all unimplemented bits, so the result
+ * of an and operation with the mask must be all 0's for the
+ * address to be valid.
+ */
+#define unimplemented_physical_address(pa) ( \
+ ((pa) & my_cpu_data.unimpl_pa_mask) != 0 \
+)
+
+/*
+ * Handle an illegal operation fault that was caused by an
+ * unimplemented "brl" instruction.
+ * If we are not successful (e.g because the illegal operation
+ * wasn't caused by a "brl" after all), we return -1.
+ * If we are successful, we return either 0 or the address
+ * of a "fixup" function for manipulating preserved register
+ * state.
+ */
+
+struct illegal_op_return
+ia64_emulate_brl (struct pt_regs *regs, unsigned long ar_ec)
+{
+ unsigned long bundle[2];
+ unsigned long opcode, btype, qp, offset;
+ unsigned long next_ip;
+ struct siginfo siginfo;
+ struct illegal_op_return rv;
+ int tmp_taken, unimplemented_address;
+
+ rv.fkt = (unsigned long) -1;
+
+ /*
+ * Decode the instruction bundle.
+ */
+
+ if (copy_from_user(bundle, (void *) (regs->cr_iip), sizeof(bundle)))
+ return rv;
+
+ next_ip = (unsigned long) regs->cr_iip + 16;
+
+ /* "brl" must be in slot 2. */
+ if (ia64_psr(regs)->ri != 1) return rv;
+
+ /* Must be "mlx" template */
+ if ((bundle[0] & 0x1e) != 0x4) return rv;
+
+ opcode = (bundle[1] >> 60);
+ btype = ((bundle[1] >> 29) & 0x7);
+ qp = ((bundle[1] >> 23) & 0x3f);
+ offset = ((bundle[1] & 0x0800000000000000L) << 4)
+ | ((bundle[1] & 0x00fffff000000000L) >> 32)
+ | ((bundle[1] & 0x00000000007fffffL) << 40)
+ | ((bundle[0] & 0xffff000000000000L) >> 24);
+
+ tmp_taken = regs->pr & (1L << qp);
+
+ switch(opcode) {
+
+ case 0xC:
+ /*
+ * Long Branch.
+ */
+ if (btype != 0) return rv;
+ rv.fkt = 0;
+ if (!(tmp_taken)) {
+ /*
+ * Qualifying predicate is 0.
+ * Skip instruction.
+ */
+ regs->cr_iip = next_ip;
+ ia64_psr(regs)->ri = 0;
+ return rv;
+ }
+ break;
+
+ case 0xD:
+ /*
+ * Long Call.
+ */
+ rv.fkt = 0;
+ if (!(tmp_taken)) {
+ /*
+ * Qualifying predicate is 0.
+ * Skip instruction.
+ */
+ regs->cr_iip = next_ip;
+ ia64_psr(regs)->ri = 0;
+ return rv;
+ }
+
+ /*
+ * BR[btype] = IP+16
+ */
+ switch(btype) {
+ case 0:
+ regs->b0 = next_ip;
+ break;
+ case 1:
+ rv.fkt = (unsigned long) &ia64_set_b1;
+ break;
+ case 2:
+ rv.fkt = (unsigned long) &ia64_set_b2;
+ break;
+ case 3:
+ rv.fkt = (unsigned long) &ia64_set_b3;
+ break;
+ case 4:
+ rv.fkt = (unsigned long) &ia64_set_b4;
+ break;
+ case 5:
+ rv.fkt = (unsigned long) &ia64_set_b5;
+ break;
+ case 6:
+ regs->b6 = next_ip;
+ break;
+ case 7:
+ regs->b7 = next_ip;
+ break;
+ }
+ rv.arg1 = next_ip;
+
+ /*
+ * AR[PFS].pfm = CFM
+ * AR[PFS].pec = AR[EC]
+ * AR[PFS].ppl = PSR.cpl
+ */
+ regs->ar_pfs = ((regs->cr_ifs & 0x3fffffffff)
+ | (ar_ec << 52)
+ | ((unsigned long) ia64_psr(regs)->cpl << 62));
+
+ /*
+ * CFM.sof -= CFM.sol
+ * CFM.sol = 0
+ * CFM.sor = 0
+ * CFM.rrb.gr = 0
+ * CFM.rrb.fr = 0
+ * CFM.rrb.pr = 0
+ */
+ regs->cr_ifs = ((regs->cr_ifs & 0xffffffc00000007f)
+ - ((regs->cr_ifs >> 7) & 0x7f));
+
+ break;
+
+ default:
+ /*
+ * Unknown opcode.
+ */
+ return rv;
+
+ }
+
+ regs->cr_iip += offset;
+ ia64_psr(regs)->ri = 0;
+
+ if (ia64_psr(regs)->it == 0)
+ unimplemented_address = unimplemented_physical_address(regs->cr_iip);
+ else
+ unimplemented_address = unimplemented_virtual_address(regs->cr_iip);
+
+ if (unimplemented_address) {
+ /*
+ * The target address contains unimplemented bits.
+ */
+ printk("Woah! Unimplemented Instruction Address Trap!\n");
+ siginfo.si_signo = SIGILL;
+ siginfo.si_errno = 0;
+ siginfo.si_code = ILL_BADIADDR;
+ force_sig_info(SIGILL, &siginfo, current);
+ } else if (ia64_psr(regs)->tb) {
+ /*
+ * Branch Tracing is enabled.
+ * Force a taken branch signal.
+ */
+ siginfo.si_signo = SIGTRAP;
+ siginfo.si_errno = 0;
+ siginfo.si_code = TRAP_BRANCH;
+ force_sig_info(SIGTRAP, &siginfo, current);
+ } else if (ia64_psr(regs)->ss) {
+ /*
+ * Single Step is enabled.
+ * Force a trace signal.
+ */
+ siginfo.si_signo = SIGTRAP;
+ siginfo.si_errno = 0;
+ siginfo.si_code = TRAP_TRACE;
+ force_sig_info(SIGTRAP, &siginfo, current);
+ }
+ return rv;
+}
diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c
index 0ce1db504..c4383b97f 100644
--- a/arch/ia64/kernel/efi.c
+++ b/arch/ia64/kernel/efi.c
@@ -5,15 +5,18 @@
*
* Copyright (C) 1999 VA Linux Systems
* Copyright (C) 1999 Walt Drummond <drummond@valinux.com>
- * Copyright (C) 1999 Hewlett-Packard Co.
+ * Copyright (C) 1999-2000 Hewlett-Packard Co.
* Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com>
- * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com>
+ * Copyright (C) 1999-2000 Stephane Eranian <eranian@hpl.hp.com>
*
* All EFI Runtime Services are not implemented yet as EFI only
* supports physical mode addressing on SoftSDV. This is to be fixed
* in a future version. --drummond 1999-07-20
*
* Implemented EFI runtime services and virtual mode calls. --davidm
+ *
+ * Goutham Rao: <goutham.rao@intel.com>
+ * Skip non-WB memory and ignore empty memory ranges.
*/
#include <linux/kernel.h>
#include <linux/init.h>
@@ -22,6 +25,7 @@
#include <asm/efi.h>
#include <asm/io.h>
+#include <asm/pgtable.h>
#include <asm/processor.h>
#define EFI_DEBUG 0
@@ -172,6 +176,14 @@ efi_memmap_walk (efi_freemem_callback_t callback, void *arg)
continue;
}
+ if (!(md->attribute & EFI_MEMORY_WB))
+ continue;
+ if (md->num_pages == 0) {
+ printk("efi_memmap_walk: ignoring empty region at 0x%lx",
+ md->phys_addr);
+ continue;
+ }
+
curr.start = PAGE_OFFSET + md->phys_addr;
curr.end = curr.start + (md->num_pages << 12);
@@ -207,6 +219,61 @@ efi_memmap_walk (efi_freemem_callback_t callback, void *arg)
}
}
+/*
+ * Look for the PAL_CODE region reported by EFI and maps it using an
+ * ITR to enable safe PAL calls in virtual mode. See IA-64 Processor
+ * Abstraction Layer chapter 11 in ADAG
+ */
+static void
+map_pal_code (void)
+{
+ void *efi_map_start, *efi_map_end, *p;
+ efi_memory_desc_t *md;
+ u64 efi_desc_size;
+ int pal_code_count=0;
+ u64 mask, flags;
+ u64 vaddr;
+
+ efi_map_start = __va(ia64_boot_param.efi_memmap);
+ efi_map_end = efi_map_start + ia64_boot_param.efi_memmap_size;
+ efi_desc_size = ia64_boot_param.efi_memdesc_size;
+
+ for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) {
+ md = p;
+ if (md->type != EFI_PAL_CODE) continue;
+
+ if (++pal_code_count > 1) {
+ printk(KERN_ERR "Too many EFI Pal Code memory ranges, dropped @ %lx\n",
+ md->phys_addr);
+ continue;
+ }
+ mask = ~((1 << _PAGE_SIZE_4M)-1); /* XXX should be dynamic? */
+ vaddr = PAGE_OFFSET + md->phys_addr;
+
+ printk(__FUNCTION__": mapping PAL code [0x%lx-0x%lx) into [0x%lx-0x%lx)\n",
+ md->phys_addr, md->phys_addr + (md->num_pages << 12),
+ vaddr & mask, (vaddr & mask) + 4*1024*1024);
+
+ /*
+ * Cannot write to CRx with PSR.ic=1
+ */
+ ia64_clear_ic(flags);
+
+ /*
+ * ITR0/DTR0: used for kernel code/data
+ * ITR1/DTR1: used by HP simulator
+ * ITR2/DTR2: map PAL code
+ * ITR3/DTR3: used to map PAL calls buffer
+ */
+ ia64_itr(0x1, 2, vaddr & mask,
+ pte_val(mk_pte_phys(md->phys_addr,
+ __pgprot(__DIRTY_BITS|_PAGE_PL_0|_PAGE_AR_RX))),
+ _PAGE_SIZE_4M);
+ local_irq_restore(flags);
+ ia64_srlz_i ();
+ }
+}
+
void __init
efi_init (void)
{
@@ -291,6 +358,8 @@ efi_init (void)
}
}
#endif
+
+ map_pal_code();
}
void
diff --git a/arch/ia64/kernel/efi_stub.S b/arch/ia64/kernel/efi_stub.S
index 4e6f1fc63..2bb45c790 100644
--- a/arch/ia64/kernel/efi_stub.S
+++ b/arch/ia64/kernel/efi_stub.S
@@ -1,7 +1,8 @@
/*
* EFI call stub.
*
- * Copyright (C) 1999 David Mosberger <davidm@hpl.hp.com>
+ * Copyright (C) 1999-2000 Hewlett-Packard Co
+ * Copyright (C) 1999-2000 David Mosberger <davidm@hpl.hp.com>
*
* This stub allows us to make EFI calls in physical mode with interrupts
* turned off. We need this because we can't call SetVirtualMap() until
@@ -30,6 +31,7 @@
(IA64_PSR_BN)
#include <asm/processor.h>
+#include <asm/asmmacro.h>
.text
.psr abi64
@@ -39,53 +41,6 @@
.text
/*
- * Switch execution mode from virtual to physical or vice versa.
- *
- * Inputs:
- * r16 = new psr to establish
- */
- .proc switch_mode
-switch_mode:
- {
- alloc r2=ar.pfs,0,0,0,0
- rsm psr.i | psr.ic // disable interrupts and interrupt collection
- mov r15=ip
- }
- ;;
- {
- flushrs // must be first insn in group
- srlz.i
- shr.u r19=r15,61 // r19 <- top 3 bits of current IP
- }
- ;;
- mov cr.ipsr=r16 // set new PSR
- add r3=1f-switch_mode,r15
- xor r15=0x7,r19 // flip the region bits
-
- mov r17=ar.bsp
- mov r14=rp // get return address into a general register
-
- // switch RSE backing store:
- ;;
- dep r17=r15,r17,61,3 // make ar.bsp physical or virtual
- mov r18=ar.rnat // save ar.rnat
- ;;
- mov ar.bspstore=r17 // this steps on ar.rnat
- dep r3=r15,r3,61,3 // make rfi return address physical or virtual
- ;;
- mov cr.iip=r3
- mov cr.ifs=r0
- dep sp=r15,sp,61,3 // make stack pointer physical or virtual
- ;;
- mov ar.rnat=r18 // restore ar.rnat
- dep r14=r15,r14,61,3 // make function return address physical or virtual
- rfi // must be last insn in group
- ;;
-1: mov rp=r14
- br.ret.sptk.few rp
- .endp switch_mode
-
-/*
* Inputs:
* in0 = address of function descriptor of EFI routine to call
* in1..in7 = arguments to routine
@@ -94,13 +49,12 @@ switch_mode:
* r8 = EFI_STATUS returned by called function
*/
- .global efi_call_phys
- .proc efi_call_phys
-efi_call_phys:
-
- alloc loc0=ar.pfs,8,5,7,0
+GLOBAL_ENTRY(efi_call_phys)
+ UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8))
+ alloc loc1=ar.pfs,8,5,7,0
ld8 r2=[in0],8 // load EFI function's entry point
- mov loc1=rp
+ mov loc0=rp
+ UNW(.body)
;;
mov loc2=gp // save global pointer
mov loc4=ar.rsc // save RSE configuration
@@ -121,7 +75,7 @@ efi_call_phys:
;;
andcm r16=loc3,r16 // get psr with IT, DT, and RT bits cleared
mov out3=in4
- br.call.sptk.few rp=switch_mode
+ br.call.sptk.few rp=ia64_switch_mode
.ret0:
mov out4=in5
mov out5=in6
@@ -130,12 +84,11 @@ efi_call_phys:
.ret1:
mov ar.rsc=r0 // put RSE in enforced lazy, LE mode
mov r16=loc3
- br.call.sptk.few rp=switch_mode // return to virtual mode
+ br.call.sptk.few rp=ia64_switch_mode // return to virtual mode
.ret2:
mov ar.rsc=loc4 // restore RSE configuration
- mov ar.pfs=loc0
- mov rp=loc1
+ mov ar.pfs=loc1
+ mov rp=loc0
mov gp=loc2
br.ret.sptk.few rp
-
- .endp efi_call_phys
+END(efi_call_phys)
diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S
index 755e3a0c1..e56e3fc8e 100644
--- a/arch/ia64/kernel/entry.S
+++ b/arch/ia64/kernel/entry.S
@@ -13,8 +13,6 @@
/*
* Global (preserved) predicate usage on syscall entry/exit path:
*
- *
- * pEOI: See entry.h.
* pKern: See entry.h.
* pSys: See entry.h.
* pNonSys: !pSys
@@ -30,6 +28,7 @@
#include <asm/offsets.h>
#include <asm/processor.h>
#include <asm/unistd.h>
+#include <asm/asmmacro.h>
#include "entry.h"
@@ -42,11 +41,11 @@
* execve() is special because in case of success, we need to
* setup a null register window frame.
*/
- .align 16
- .proc ia64_execve
-ia64_execve:
- alloc loc0=ar.pfs,3,2,4,0
- mov loc1=rp
+ENTRY(ia64_execve)
+ UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(3))
+ alloc loc1=ar.pfs,3,2,4,0
+ mov loc0=rp
+ UNW(.body)
mov out0=in0 // filename
;; // stop bit between alloc and call
mov out1=in1 // argv
@@ -54,25 +53,22 @@ ia64_execve:
add out3=16,sp // regs
br.call.sptk.few rp=sys_execve
.ret0: cmp4.ge p6,p0=r8,r0
- mov ar.pfs=loc0 // restore ar.pfs
+ mov ar.pfs=loc1 // restore ar.pfs
;;
(p6) mov ar.pfs=r0 // clear ar.pfs in case of success
sxt4 r8=r8 // return 64-bit result
- mov rp=loc1
+ mov rp=loc0
br.ret.sptk.few rp
- .endp ia64_execve
+END(ia64_execve)
- .align 16
- .global sys_clone
- .proc sys_clone
-sys_clone:
+GLOBAL_ENTRY(sys_clone)
+ UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2))
alloc r16=ar.pfs,2,2,3,0;;
- movl r28=1f
- mov loc1=rp
- br.cond.sptk.many save_switch_stack
-1:
- mov loc0=r16 // save ar.pfs across do_fork
+ mov loc0=rp
+ DO_SAVE_SWITCH_STACK
+ mov loc1=r16 // save ar.pfs across do_fork
+ UNW(.body)
adds out2=IA64_SWITCH_STACK_SIZE+16,sp
adds r2=IA64_SWITCH_STACK_SIZE+IA64_PT_REGS_R12_OFFSET+16,sp
cmp.eq p8,p9=in1,r0 // usp == 0?
@@ -82,24 +78,22 @@ sys_clone:
(p9) mov out1=in1
br.call.sptk.few rp=do_fork
.ret1:
- mov ar.pfs=loc0
+ mov ar.pfs=loc1
+ UNW(.restore sp)
adds sp=IA64_SWITCH_STACK_SIZE,sp // pop the switch stack
- mov rp=loc1
+ mov rp=loc0
;;
br.ret.sptk.many rp
- .endp sys_clone
+END(sys_clone)
/*
- * prev_task <- switch_to(struct task_struct *next)
+ * prev_task <- ia64_switch_to(struct task_struct *next)
*/
- .align 16
- .global ia64_switch_to
- .proc ia64_switch_to
-ia64_switch_to:
+GLOBAL_ENTRY(ia64_switch_to)
+ UNW(.prologue)
alloc r16=ar.pfs,1,0,0,0
- movl r28=1f
- br.cond.sptk.many save_switch_stack
-1:
+ DO_SAVE_SWITCH_STACK
+ UNW(.body)
// disable interrupts to ensure atomicity for next few instructions:
mov r17=psr // M-unit
;;
@@ -123,66 +117,60 @@ ia64_switch_to:
mov psr.l=r17
;;
srlz.d
-
- movl r28=1f
- br.cond.sptk.many load_switch_stack
-1:
+ DO_LOAD_SWITCH_STACK( )
br.ret.sptk.few rp
- .endp ia64_switch_to
+END(ia64_switch_to)
+#ifndef CONFIG_IA64_NEW_UNWIND
/*
* Like save_switch_stack, but also save the stack frame that is active
* at the time this function is called.
*/
- .align 16
- .proc save_switch_stack_with_current_frame
-save_switch_stack_with_current_frame:
-1: {
- alloc r16=ar.pfs,0,0,0,0 // pass ar.pfs to save_switch_stack
- mov r28=ip
- }
- ;;
- adds r28=1f-1b,r28
- br.cond.sptk.many save_switch_stack
-1: br.ret.sptk.few rp
- .endp save_switch_stack_with_current_frame
+ENTRY(save_switch_stack_with_current_frame)
+ UNW(.prologue)
+ alloc r16=ar.pfs,0,0,0,0 // pass ar.pfs to save_switch_stack
+ DO_SAVE_SWITCH_STACK
+ br.ret.sptk.few rp
+END(save_switch_stack_with_current_frame)
+#endif /* !CONFIG_IA64_NEW_UNWIND */
+
/*
* Note that interrupts are enabled during save_switch_stack and
* load_switch_stack. This means that we may get an interrupt with
* "sp" pointing to the new kernel stack while ar.bspstore is still
* pointing to the old kernel backing store area. Since ar.rsc,
* ar.rnat, ar.bsp, and ar.bspstore are all preserved by interrupts,
- * this is not a problem.
+ * this is not a problem. Also, we don't need to specify unwind
+ * information for preserved registers that are not modified in
+ * save_switch_stack as the right unwind information is already
+ * specified at the call-site of save_switch_stack.
*/
/*
* save_switch_stack:
* - r16 holds ar.pfs
- * - r28 holds address to return to
+ * - b7 holds address to return to
* - rp (b0) holds return address to save
*/
- .align 16
- .global save_switch_stack
- .proc save_switch_stack
-save_switch_stack:
+GLOBAL_ENTRY(save_switch_stack)
+ UNW(.prologue)
+ UNW(.altrp b7)
flushrs // flush dirty regs to backing store (must be first in insn group)
mov r17=ar.unat // preserve caller's
- adds r2=-IA64_SWITCH_STACK_SIZE+16,sp // r2 = &sw->caller_unat
+ adds r2=16,sp // r2 = &sw->caller_unat
;;
mov r18=ar.fpsr // preserve fpsr
mov ar.rsc=r0 // put RSE in mode: enforced lazy, little endian, pl 0
;;
mov r19=ar.rnat
- adds r3=-IA64_SWITCH_STACK_SIZE+24,sp // r3 = &sw->ar_fpsr
-
- // Note: the instruction ordering is important here: we can't
- // store anything to the switch stack before sp is updated
- // as otherwise an interrupt might overwrite the memory!
- adds sp=-IA64_SWITCH_STACK_SIZE,sp
+ adds r3=24,sp // r3 = &sw->ar_fpsr
;;
+ .savesp ar.unat,SW(CALLER_UNAT)
st8 [r2]=r17,16
+ .savesp ar.fpsr,SW(AR_FPSR)
st8 [r3]=r18,24
;;
+ UNW(.body)
stf.spill [r2]=f2,32
stf.spill [r3]=f3,32
mov r21=b0
@@ -259,16 +247,17 @@ save_switch_stack:
st8 [r3]=r21 // save predicate registers
mov ar.rsc=3 // put RSE back into eager mode, pl 0
br.cond.sptk.few b7
- .endp save_switch_stack
+END(save_switch_stack)
/*
* load_switch_stack:
- * - r28 holds address to return to
+ * - b7 holds address to return to
*/
- .align 16
- .proc load_switch_stack
-load_switch_stack:
+ENTRY(load_switch_stack)
+ UNW(.prologue)
+ UNW(.altrp b7)
invala // invalidate ALAT
+ UNW(.body)
adds r2=IA64_SWITCH_STACK_B0_OFFSET+16,sp // get pointer to switch_stack.b0
mov ar.rsc=r0 // put RSE into enforced lazy mode
adds r3=IA64_SWITCH_STACK_B0_OFFSET+24,sp // get pointer to switch_stack.b1
@@ -353,21 +342,16 @@ load_switch_stack:
;;
ld8.fill r4=[r2],16
ld8.fill r5=[r3],16
- mov b7=r28
;;
ld8.fill r6=[r2],16
ld8.fill r7=[r3],16
mov ar.unat=r18 // restore caller's unat
mov ar.fpsr=r19 // restore fpsr
mov ar.rsc=3 // put RSE back into eager mode, pl 0
- adds sp=IA64_SWITCH_STACK_SIZE,sp // pop switch_stack
br.cond.sptk.few b7
- .endp load_switch_stack
+END(load_switch_stack)
- .align 16
- .global __ia64_syscall
- .proc __ia64_syscall
-__ia64_syscall:
+GLOBAL_ENTRY(__ia64_syscall)
.regstk 6,0,0,0
mov r15=in5 // put syscall number in place
break __BREAK_SYSCALL
@@ -377,30 +361,42 @@ __ia64_syscall:
(p6) st4 [r2]=r8
(p6) mov r8=-1
br.ret.sptk.few rp
- .endp __ia64_syscall
+END(__ia64_syscall)
//
// We invoke syscall_trace through this intermediate function to
// ensure that the syscall input arguments are not clobbered. We
// also use it to preserve b6, which contains the syscall entry point.
//
- .align 16
- .global invoke_syscall_trace
- .proc invoke_syscall_trace
-invoke_syscall_trace:
- alloc loc0=ar.pfs,8,3,0,0
+GLOBAL_ENTRY(invoke_syscall_trace)
+#ifdef CONFIG_IA64_NEW_UNWIND
+ UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8))
+ alloc loc1=ar.pfs,8,3,0,0
+ mov loc0=rp
+ UNW(.body)
+ mov loc2=b6
+ ;;
+ br.call.sptk.few rp=syscall_trace
+.ret3: mov rp=loc0
+ mov ar.pfs=loc1
+ mov b6=loc2
+ br.ret.sptk.few rp
+#else /* !CONFIG_IA64_NEW_SYSCALL */
+ UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8))
+ alloc loc1=ar.pfs,8,3,0,0
;; // WAW on CFM at the br.call
- mov loc1=rp
+ mov loc0=rp
br.call.sptk.many rp=save_switch_stack_with_current_frame // must preserve b6!!
.ret2: mov loc2=b6
br.call.sptk.few rp=syscall_trace
.ret3: adds sp=IA64_SWITCH_STACK_SIZE,sp // drop switch_stack frame
- mov rp=loc1
- mov ar.pfs=loc0
+ mov rp=loc0
+ mov ar.pfs=loc1
mov b6=loc2
;;
br.ret.sptk.few rp
- .endp invoke_syscall_trace
+#endif /* !CONFIG_IA64_NEW_SYSCALL */
+END(invoke_syscall_trace)
//
// Invoke a system call, but do some tracing before and after the call.
@@ -414,19 +410,19 @@ invoke_syscall_trace:
//
.global ia64_trace_syscall
.global ia64_strace_leave_kernel
- .global ia64_strace_clear_r8
- .proc ia64_strace_clear_r8
-ia64_strace_clear_r8: // this is where we return after cloning when PF_TRACESYS is on
+GLOBAL_ENTRY(ia64_strace_clear_r8)
+ // this is where we return after cloning when PF_TRACESYS is on
+ PT_REGS_UNWIND_INFO(0)
# ifdef CONFIG_SMP
br.call.sptk.few rp=invoke_schedule_tail
# endif
mov r8=0
br strace_check_retval
- .endp ia64_strace_clear_r8
+END(ia64_strace_clear_r8)
- .proc ia64_trace_syscall
-ia64_trace_syscall:
+ENTRY(ia64_trace_syscall)
+ PT_REGS_UNWIND_INFO(0)
br.call.sptk.few rp=invoke_syscall_trace // give parent a chance to catch syscall args
.ret4: br.call.sptk.few rp=b6 // do the syscall
strace_check_retval:
@@ -454,7 +450,7 @@ strace_error:
(p6) mov r10=-1
(p6) mov r8=r9
br.cond.sptk.few strace_save_retval
- .endp ia64_trace_syscall
+END(ia64_trace_syscall)
/*
* A couple of convenience macros to help implement/understand the state
@@ -472,12 +468,8 @@ strace_error:
#define rKRBS r22
#define rB6 r21
- .align 16
- .global ia64_ret_from_syscall
- .global ia64_ret_from_syscall_clear_r8
- .global ia64_leave_kernel
- .proc ia64_ret_from_syscall
-ia64_ret_from_syscall_clear_r8:
+GLOBAL_ENTRY(ia64_ret_from_syscall_clear_r8)
+ PT_REGS_UNWIND_INFO(0)
#ifdef CONFIG_SMP
// In SMP mode, we need to call schedule_tail to complete the scheduling process.
// Called by ia64_switch_to after do_fork()->copy_thread(). r8 contains the
@@ -487,7 +479,10 @@ ia64_ret_from_syscall_clear_r8:
#endif
mov r8=0
;; // added stop bits to prevent r8 dependency
-ia64_ret_from_syscall:
+END(ia64_ret_from_syscall_clear_r8)
+ // fall through
+GLOBAL_ENTRY(ia64_ret_from_syscall)
+ PT_REGS_UNWIND_INFO(0)
cmp.ge p6,p7=r8,r0 // syscall executed successfully?
adds r2=IA64_PT_REGS_R8_OFFSET+16,sp // r2 = &pt_regs.r8
adds r3=IA64_PT_REGS_R8_OFFSET+32,sp // r3 = &pt_regs.r10
@@ -497,19 +492,21 @@ ia64_ret_from_syscall:
.mem.offset 8,0
(p6) st8.spill [r3]=r0 // clear error indication in slot for r10 and set unat bit
(p7) br.cond.spnt.few handle_syscall_error // handle potential syscall failure
-
-ia64_leave_kernel:
+END(ia64_ret_from_syscall)
+ // fall through
+GLOBAL_ENTRY(ia64_leave_kernel)
// check & deliver software interrupts:
+ PT_REGS_UNWIND_INFO(0)
#ifdef CONFIG_SMP
- adds r2=IA64_TASK_PROCESSOR_OFFSET,r13
- movl r3=softirq_state
+ adds r2=IA64_TASK_PROCESSOR_OFFSET,r13
+ movl r3=softirq_state
;;
- ld4 r2=[r2]
+ ld4 r2=[r2]
;;
- shl r2=r2,SMP_LOG_CACHE_BYTES // can't use shladd here...
+ shl r2=r2,SMP_LOG_CACHE_BYTES // can't use shladd here...
;;
- add r3=r2,r3
+ add r3=r2,r3
#else
movl r3=softirq_state
#endif
@@ -538,32 +535,28 @@ back_from_resched:
ld4 r14=[r14]
mov rp=r3 // arrange for schedule() to return to back_from_resched
;;
- /*
- * If pEOI is set, we need to write the cr.eoi now and then
- * clear pEOI because both invoke_schedule() and
- * handle_signal_delivery() may call the scheduler. Since
- * we're returning to user-level, we get at most one nested
- * interrupt of the same priority level, which doesn't tax the
- * kernel stack too much.
- */
-(pEOI) mov cr.eoi=r0
cmp.ne p6,p0=r2,r0
cmp.ne p2,p0=r14,r0 // NOTE: pKern is an alias for p2!!
-(pEOI) cmp.ne pEOI,p0=r0,r0 // clear pEOI before calling schedule()
srlz.d
(p6) br.call.spnt.many b6=invoke_schedule // ignore return value
2:
// check & deliver pending signals:
(p2) br.call.spnt.few rp=handle_signal_delivery
-#if defined(CONFIG_SMP) || defined(CONFIG_IA64_SOFTSDV_HACKS)
+#if defined(CONFIG_ITANIUM_ASTEP_SPECIFIC) || defined(CONFIG_IA64_SOFTSDV_HACKS)
// Check for lost ticks
+ rsm psr.i
mov r2 = ar.itc
+ movl r14 = 1000 // latency tolerance
mov r3 = cr.itm
;;
sub r2 = r2, r3
;;
+ sub r2 = r2, r14
+ ;;
cmp.ge p6,p7 = r2, r0
(p6) br.call.spnt.few rp=invoke_ia64_reset_itm
+ ;;
+ ssm psr.i
#endif
restore_all:
@@ -692,18 +685,6 @@ restore_all:
;;
add r18=r16,r18 // adjust the loadrs value
;;
-#ifdef CONFIG_IA64_SOFTSDV_HACKS
- // Reset ITM if we've missed a timer tick. Workaround for SoftSDV bug
- mov r16 = r2
- mov r2 = ar.itc
- mov r17 = cr.itm
- ;;
- cmp.gt p6,p7 = r2, r17
-(p6) addl r17 = 100, r2
- ;;
- mov cr.itm = r17
- mov r2 = r16
-#endif
dont_preserve_current_frame:
alloc r16=ar.pfs,0,0,0,0 // drop the current call frame (noop for syscalls)
;;
@@ -724,14 +705,14 @@ skip_rbs_switch:
mov ar.rsc=rARRSC
mov ar.unat=rARUNAT
mov cr.ifs=rCRIFS // restore cr.ifs only if not a (synchronous) syscall
-(pEOI) mov cr.eoi=r0
mov pr=rARPR,-1
mov cr.iip=rCRIIP
mov cr.ipsr=rCRIPSR
;;
rfi;; // must be last instruction in an insn group
+END(ia64_leave_kernel)
-handle_syscall_error:
+ENTRY(handle_syscall_error)
/*
* Some system calls (e.g., ptrace, mmap) can return arbitrary
* values which could lead us to mistake a negative return
@@ -740,6 +721,7 @@ handle_syscall_error:
* If pt_regs.r8 is zero, we assume that the call completed
* successfully.
*/
+ PT_REGS_UNWIND_INFO(0)
ld8 r3=[r2] // load pt_regs.r8
sub r9=0,r8 // negate return value to get errno
;;
@@ -753,205 +735,283 @@ handle_syscall_error:
.mem.offset 0,0; st8.spill [r2]=r9 // store errno in pt_regs.r8 and set unat bit
.mem.offset 8,0; st8.spill [r3]=r10 // store error indication in pt_regs.r10 and set unat bit
br.cond.sptk.many ia64_leave_kernel
- .endp handle_syscall_error
+END(handle_syscall_error)
#ifdef CONFIG_SMP
/*
* Invoke schedule_tail(task) while preserving in0-in7, which may be needed
* in case a system call gets restarted.
*/
- .proc invoke_schedule_tail
-invoke_schedule_tail:
- alloc loc0=ar.pfs,8,2,1,0
- mov loc1=rp
+ENTRY(invoke_schedule_tail)
+ UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8))
+ alloc loc1=ar.pfs,8,2,1,0
+ mov loc0=rp
mov out0=r8 // Address of previous task
;;
br.call.sptk.few rp=schedule_tail
.ret8:
- mov ar.pfs=loc0
- mov rp=loc1
+ mov ar.pfs=loc1
+ mov rp=loc0
br.ret.sptk.many rp
- .endp invoke_schedule_tail
+END(invoke_schedule_tail)
+
+#endif /* CONFIG_SMP */
+
+#if defined(CONFIG_ITANIUM_ASTEP_SPECIFIC) || defined(CONFIG_IA64_SOFTSDV_HACKS)
- .proc invoke_ia64_reset_itm
-invoke_ia64_reset_itm:
- alloc loc0=ar.pfs,8,2,0,0
- mov loc1=rp
+ENTRY(invoke_ia64_reset_itm)
+ UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8))
+ alloc loc1=ar.pfs,8,2,0,0
+ mov loc0=rp
;;
+ UNW(.body)
br.call.sptk.many rp=ia64_reset_itm
;;
- mov ar.pfs=loc0
- mov rp=loc1
+ mov ar.pfs=loc1
+ mov rp=loc0
br.ret.sptk.many rp
- .endp invoke_ia64_reset_itm
+END(invoke_ia64_reset_itm)
-#endif /* CONFIG_SMP */
+#endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC || CONFIG_IA64_SOFTSDV_HACKS */
/*
* Invoke do_softirq() while preserving in0-in7, which may be needed
* in case a system call gets restarted.
*/
- .proc invoke_do_softirq
-invoke_do_softirq:
- alloc loc0=ar.pfs,8,2,0,0
- mov loc1=rp
-(pEOI) mov cr.eoi=r0
+ENTRY(invoke_do_softirq)
+ UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8))
+ alloc loc1=ar.pfs,8,2,0,0
+ mov loc0=rp
;;
-(pEOI) cmp.ne pEOI,p0=r0,r0
+ UNW(.body)
br.call.sptk.few rp=do_softirq
.ret9:
- mov ar.pfs=loc0
- mov rp=loc1
+ mov ar.pfs=loc1
+ mov rp=loc0
br.ret.sptk.many rp
- .endp invoke_do_softirq
+END(invoke_do_softirq)
/*
* Invoke schedule() while preserving in0-in7, which may be needed
* in case a system call gets restarted.
*/
- .proc invoke_schedule
-invoke_schedule:
- alloc loc0=ar.pfs,8,2,0,0
- mov loc1=rp
+ENTRY(invoke_schedule)
+ UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8))
+ alloc loc1=ar.pfs,8,2,0,0
+ mov loc0=rp
;;
+ UNW(.body)
br.call.sptk.few rp=schedule
.ret10:
- mov ar.pfs=loc0
- mov rp=loc1
+ mov ar.pfs=loc1
+ mov rp=loc0
br.ret.sptk.many rp
- .endp invoke_schedule
+END(invoke_schedule)
//
// Setup stack and call ia64_do_signal. Note that pSys and pNonSys need to
// be set up by the caller. We declare 8 input registers so the system call
// args get preserved, in case we need to restart a system call.
//
- .align 16
- .proc handle_signal_delivery
-handle_signal_delivery:
- alloc loc0=ar.pfs,8,2,3,0 // preserve all eight input regs in case of syscall restart!
+ENTRY(handle_signal_delivery)
+#ifdef CONFIG_IA64_NEW_UNWIND
+ .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8)
+ alloc loc1=ar.pfs,8,2,3,0 // preserve all eight input regs in case of syscall restart!
mov r9=ar.unat
-
- // If the process is being ptraced, the signal may not actually be delivered to
- // the process. Instead, SIGCHLD will be sent to the parent. We need to
- // setup a switch_stack so ptrace can inspect the processes state if necessary.
- adds r2=IA64_TASK_FLAGS_OFFSET,r13
- ;;
- ld8 r2=[r2]
+ mov loc0=rp // save return address
mov out0=0 // there is no "oldset"
- adds out1=16,sp // out1=&pt_regs
- ;;
+ adds out1=0,sp // out1=&sigscratch
(pSys) mov out2=1 // out2==1 => we're in a syscall
- tbit.nz p16,p17=r2,PF_PTRACED_BIT
-(p16) br.cond.spnt.many setup_switch_stack
;;
-back_from_setup_switch_stack:
(pNonSys) mov out2=0 // out2==0 => not a syscall
- adds r3=-IA64_SWITCH_STACK_SIZE+IA64_SWITCH_STACK_CALLER_UNAT_OFFSET+16,sp
-(p17) adds sp=-IA64_SWITCH_STACK_SIZE,sp // make space for (dummy) switch_stack
- ;;
-(p17) st8 [r3]=r9 // save ar.unat in sw->caller_unat
- mov loc1=rp // save return address
+ .fframe 16
+ .spillpsp ar.unat, 16 // (note that offset is relative to psp+0x10!)
+ st8 [sp]=r9,-16 // allocate space for ar.unat and save it
+ .body
br.call.sptk.few rp=ia64_do_signal
.ret11:
- adds r3=IA64_SWITCH_STACK_CALLER_UNAT_OFFSET+16,sp
+ .restore sp
+ adds sp=16,sp // pop scratch stack space
;;
- ld8 r9=[r3] // load new unat from sw->caller_unat
- mov rp=loc1
+ ld8 r9=[sp] // load new unat from sw->caller_unat
+ mov rp=loc0
;;
-(p17) adds sp=IA64_SWITCH_STACK_SIZE,sp // drop (dummy) switch_stack
-(p17) mov ar.unat=r9
-(p17) mov ar.pfs=loc0
-(p17) br.ret.sptk.many rp
-
- // restore the switch stack (ptrace may have modified it):
- movl r28=1f
- br.cond.sptk.many load_switch_stack
-1: br.ret.sptk.many rp
- // NOT REACHED
-
-setup_switch_stack:
- movl r28=back_from_setup_switch_stack
- mov r16=loc0
- br.cond.sptk.many save_switch_stack
- // NOT REACHED
-
- .endp handle_signal_delivery
+ mov ar.unat=r9
+ mov ar.pfs=loc1
+ br.ret.sptk.many rp
+#else /* !CONFIG_IA64_NEW_UNWIND */
+ .prologue
+ alloc r16=ar.pfs,8,0,3,0 // preserve all eight input regs in case of syscall restart!
+ DO_SAVE_SWITCH_STACK
+ UNW(.body)
- .align 16
- .proc sys_rt_sigsuspend
- .global sys_rt_sigsuspend
-sys_rt_sigsuspend:
- alloc loc0=ar.pfs,2,2,3,0
+ mov out0=0 // there is no "oldset"
+ adds out1=16,sp // out1=&sigscratch
+ .pred.rel.mutex pSys, pNonSys
+(pSys) mov out2=1 // out2==1 => we're in a syscall
+(pNonSys) mov out2=0 // out2==0 => not a syscall
+ br.call.sptk.few rp=ia64_do_signal
+.ret11:
+ // restore the switch stack (ptrace may have modified it)
+ DO_LOAD_SWITCH_STACK( )
+ br.ret.sptk.many rp
+#endif /* !CONFIG_IA64_NEW_UNWIND */
+END(handle_signal_delivery)
- // If the process is being ptraced, the signal may not actually be delivered to
- // the process. Instead, SIGCHLD will be sent to the parent. We need to
- // setup a switch_stack so ptrace can inspect the processes state if necessary.
- // Also, the process might not ptraced until stopped in sigsuspend, so this
- // isn't something that we can do conditionally based upon the value of
- // PF_PTRACED_BIT.
+GLOBAL_ENTRY(sys_rt_sigsuspend)
+#ifdef CONFIG_IA64_NEW_UNWIND
+ .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8)
+ alloc loc1=ar.pfs,8,2,3,0 // preserve all eight input regs in case of syscall restart!
+ mov r9=ar.unat
+ mov loc0=rp // save return address
mov out0=in0 // mask
mov out1=in1 // sigsetsize
+ adds out2=0,sp // out2=&sigscratch
;;
- adds out2=16,sp // out1=&pt_regs
- movl r28=back_from_sigsuspend_setup_switch_stack
- mov r16=loc0
- br.cond.sptk.many save_switch_stack
- ;;
-back_from_sigsuspend_setup_switch_stack:
- mov loc1=rp // save return address
- br.call.sptk.many rp=ia64_rt_sigsuspend
+ .fframe 16
+ .spillpsp ar.unat, 16 // (note that offset is relative to psp+0x10!)
+ st8 [sp]=r9,-16 // allocate space for ar.unat and save it
+ .body
+ br.call.sptk.few rp=ia64_rt_sigsuspend
.ret12:
- adds r3=IA64_SWITCH_STACK_CALLER_UNAT_OFFSET+16,sp
+ .restore sp
+ adds sp=16,sp // pop scratch stack space
;;
- ld8 r9=[r3] // load new unat from sw->caller_unat
- mov rp=loc1
+ ld8 r9=[sp] // load new unat from sw->caller_unat
+ mov rp=loc0
;;
+ mov ar.unat=r9
+ mov ar.pfs=loc1
+ br.ret.sptk.many rp
+#else /* !CONFIG_IA64_NEW_UNWIND */
+ UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2))
+ alloc r16=ar.pfs,2,0,3,0
+ DO_SAVE_SWITCH_STACK
+ UNW(.body)
- // restore the switch stack (ptrace may have modified it):
- movl r28=1f
- br.cond.sptk.many load_switch_stack
-1: br.ret.sptk.many rp
- // NOT REACHED
- .endp sys_rt_sigsuspend
+ mov out0=in0 // mask
+ mov out1=in1 // sigsetsize
+ adds out2=16,sp // out1=&sigscratch
+ br.call.sptk.many rp=ia64_rt_sigsuspend
+.ret12:
+ // restore the switch stack (ptrace may have modified it)
+ DO_LOAD_SWITCH_STACK( )
+ br.ret.sptk.many rp
+#endif /* !CONFIG_IA64_NEW_UNWIND */
+END(sys_rt_sigsuspend)
- .align 16
- .proc sys_rt_sigreturn
-sys_rt_sigreturn:
+ENTRY(sys_rt_sigreturn)
+#ifdef CONFIG_IA64_NEW_UNWIND
.regstk 0,0,3,0 // inherited from gate.s:invoke_sighandler()
- adds out0=16,sp // out0 = &pt_regs
- adds sp=-IA64_SWITCH_STACK_SIZE,sp // make space for unat and padding
+ PT_REGS_UNWIND_INFO(0)
+ .prologue
+ PT_REGS_SAVES(16)
+ adds sp=-16,sp
+ .body
+ cmp.eq pNonSys,p0=r0,r0 // sigreturn isn't a normal syscall...
+ ;;
+ adds out0=16,sp // out0 = &sigscratch
+ br.call.sptk.few rp=ia64_rt_sigreturn
+.ret13:
+ adds sp=16,sp // doesn't drop pt_regs, so don't mark it as restoring sp!
+ PT_REGS_UNWIND_INFO(0) // instead, create a new body section with the smaller frame
;;
+ ld8 r9=[sp] // load new ar.unat
+ mov b7=r8
+ ;;
+ mov ar.unat=r9
+ br b7
+#else /* !CONFIG_IA64_NEW_UNWIND */
+ .regstk 0,0,3,0 // inherited from gate.s:invoke_sighandler()
+ PT_REGS_UNWIND_INFO(0)
+ UNW(.prologue)
+ UNW(.fframe IA64_PT_REGS_SIZE+IA64_SWITCH_STACK_SIZE)
+ UNW(.spillsp rp, PT(CR_IIP)+IA64_SWITCH_STACK_SIZE)
+ UNW(.spillsp ar.pfs, PT(CR_IFS)+IA64_SWITCH_STACK_SIZE)
+ UNW(.spillsp ar.unat, PT(AR_UNAT)+IA64_SWITCH_STACK_SIZE)
+ UNW(.spillsp pr, PT(PR)+IA64_SWITCH_STACK_SIZE)
+ adds sp=-IA64_SWITCH_STACK_SIZE,sp
cmp.eq pNonSys,p0=r0,r0 // sigreturn isn't a normal syscall...
+ ;;
+ UNW(.body)
+
+ adds out0=16,sp // out0 = &sigscratch
br.call.sptk.few rp=ia64_rt_sigreturn
.ret13:
adds r3=IA64_SWITCH_STACK_CALLER_UNAT_OFFSET+16,sp
;;
ld8 r9=[r3] // load new ar.unat
- mov rp=r8
+ mov b7=r8
;;
+ PT_REGS_UNWIND_INFO(0)
adds sp=IA64_SWITCH_STACK_SIZE,sp // drop (dummy) switch-stack frame
mov ar.unat=r9
- br rp
- .endp sys_rt_sigreturn
+ br b7
+#endif /* !CONFIG_IA64_NEW_UNWIND */
+END(sys_rt_sigreturn)
- .align 16
- .global ia64_prepare_handle_unaligned
- .proc ia64_prepare_handle_unaligned
-ia64_prepare_handle_unaligned:
- movl r28=1f
+GLOBAL_ENTRY(ia64_prepare_handle_unaligned)
//
// r16 = fake ar.pfs, we simply need to make sure
// privilege is still 0
//
+ PT_REGS_UNWIND_INFO(0)
mov r16=r0
- br.cond.sptk.few save_switch_stack
-1: br.call.sptk.few rp=ia64_handle_unaligned // stack frame setup in ivt
+ DO_SAVE_SWITCH_STACK
+ br.call.sptk.few rp=ia64_handle_unaligned // stack frame setup in ivt
.ret14:
- movl r28=2f
- br.cond.sptk.many load_switch_stack
-2: br.cond.sptk.many rp // goes to ia64_leave_kernel
- .endp ia64_prepare_handle_unaligned
+ DO_LOAD_SWITCH_STACK(PT_REGS_UNWIND_INFO(0))
+ br.cond.sptk.many rp // goes to ia64_leave_kernel
+END(ia64_prepare_handle_unaligned)
+
+#ifdef CONFIG_IA64_NEW_UNWIND
+
+ //
+ // unw_init_running(void (*callback)(info, arg), void *arg)
+ //
+# define EXTRA_FRAME_SIZE ((UNW_FRAME_INFO_SIZE+15)&~15)
+
+GLOBAL_ENTRY(unw_init_running)
+ .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2)
+ alloc loc1=ar.pfs,2,3,3,0
+ ;;
+ ld8 loc2=[in0],8
+ mov loc0=rp
+ mov r16=loc1
+ DO_SAVE_SWITCH_STACK
+ .body
+
+ .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2)
+ .fframe IA64_SWITCH_STACK_SIZE+EXTRA_FRAME_SIZE
+ SWITCH_STACK_SAVES(EXTRA_FRAME_SIZE)
+ adds sp=-EXTRA_FRAME_SIZE,sp
+ .body
+ ;;
+ adds out0=16,sp // &info
+ mov out1=r13 // current
+ adds out2=16+EXTRA_FRAME_SIZE,sp // &switch_stack
+ br.call.sptk.few rp=unw_init_frame_info
+1: adds out0=16,sp // &info
+ mov b6=loc2
+ mov loc2=gp // save gp across indirect function call
+ ;;
+ ld8 gp=[in0]
+ mov out1=in1 // arg
+ br.call.sptk.few rp=b6 // invoke the callback function
+1: mov gp=loc2 // restore gp
+
+ // For now, we don't allow changing registers from within
+ // unw_init_running; if we ever want to allow that, we'd
+ // have to do a load_switch_stack here:
+ .restore sp
+ adds sp=IA64_SWITCH_STACK_SIZE+EXTRA_FRAME_SIZE,sp
+
+ mov ar.pfs=loc1
+ mov rp=loc0
+ br.ret.sptk.many rp
+END(unw_init_running)
+
+#endif
.rodata
.align 8
@@ -1053,9 +1113,9 @@ sys_call_table:
data8 sys_syslog
data8 sys_setitimer
data8 sys_getitimer
- data8 sys_newstat // 1120
- data8 sys_newlstat
- data8 sys_newfstat
+ data8 ia64_oldstat // 1120
+ data8 ia64_oldlstat
+ data8 ia64_oldfstat
data8 sys_vhangup
data8 sys_lchown
data8 sys_vm86 // 1125
@@ -1065,7 +1125,7 @@ sys_call_table:
data8 sys_setdomainname
data8 sys_newuname // 1130
data8 sys_adjtimex
- data8 sys_create_module
+ data8 ia64_create_module
data8 sys_init_module
data8 sys_delete_module
data8 sys_get_kernel_syms // 1135
@@ -1143,9 +1203,9 @@ sys_call_table:
data8 sys_pivot_root
data8 sys_mincore
data8 sys_madvise
- data8 ia64_ni_syscall // 1210
- data8 ia64_ni_syscall
- data8 ia64_ni_syscall
+ data8 sys_newstat // 1210
+ data8 sys_newlstat
+ data8 sys_newfstat
data8 ia64_ni_syscall
data8 ia64_ni_syscall
data8 ia64_ni_syscall // 1215
@@ -1212,4 +1272,3 @@ sys_call_table:
data8 ia64_ni_syscall
data8 ia64_ni_syscall
data8 ia64_ni_syscall
-
diff --git a/arch/ia64/kernel/entry.h b/arch/ia64/kernel/entry.h
index ecef44f60..41307f1b0 100644
--- a/arch/ia64/kernel/entry.h
+++ b/arch/ia64/kernel/entry.h
@@ -2,7 +2,64 @@
* Preserved registers that are shared between code in ivt.S and entry.S. Be
* careful not to step on these!
*/
-#define pEOI p1 /* should leave_kernel write EOI? */
#define pKern p2 /* will leave_kernel return to kernel-mode? */
#define pSys p4 /* are we processing a (synchronous) system call? */
#define pNonSys p5 /* complement of pSys */
+
+#define PT(f) (IA64_PT_REGS_##f##_OFFSET + 16)
+#define SW(f) (IA64_SWITCH_STACK_##f##_OFFSET + 16)
+
+#define PT_REGS_SAVES(off) \
+ UNW(.unwabi @svr4, 'i'); \
+ UNW(.fframe IA64_PT_REGS_SIZE+16+(off)); \
+ UNW(.spillsp rp, PT(CR_IIP)+(off)); \
+ UNW(.spillsp ar.pfs, PT(CR_IFS)+(off)); \
+ UNW(.spillsp ar.unat, PT(AR_UNAT)+(off)); \
+ UNW(.spillsp ar.fpsr, PT(AR_FPSR)+(off)); \
+ UNW(.spillsp pr, PT(PR)+(off));
+
+#define PT_REGS_UNWIND_INFO(off) \
+ UNW(.prologue); \
+ PT_REGS_SAVES(off); \
+ UNW(.body)
+
+#define SWITCH_STACK_SAVES(off) \
+ UNW(.savesp ar.unat,SW(CALLER_UNAT)+(off)); UNW(.savesp ar.fpsr,SW(AR_FPSR)+(off)); \
+ UNW(.spillsp f2,SW(F2)+(off)); UNW(.spillsp f3,SW(F3)+(off)); \
+ UNW(.spillsp f4,SW(F4)+(off)); UNW(.spillsp f5,SW(F5)+(off)); \
+ UNW(.spillsp f16,SW(F16)+(off)); UNW(.spillsp f17,SW(F17)+(off)); \
+ UNW(.spillsp f18,SW(F18)+(off)); UNW(.spillsp f19,SW(F19)+(off)); \
+ UNW(.spillsp f20,SW(F20)+(off)); UNW(.spillsp f21,SW(F21)+(off)); \
+ UNW(.spillsp f22,SW(F22)+(off)); UNW(.spillsp f23,SW(F23)+(off)); \
+ UNW(.spillsp f24,SW(F24)+(off)); UNW(.spillsp f25,SW(F25)+(off)); \
+ UNW(.spillsp f26,SW(F26)+(off)); UNW(.spillsp f27,SW(F27)+(off)); \
+ UNW(.spillsp f28,SW(F28)+(off)); UNW(.spillsp f29,SW(F29)+(off)); \
+ UNW(.spillsp f30,SW(F30)+(off)); UNW(.spillsp f31,SW(F31)+(off)); \
+ UNW(.spillsp r4,SW(R4)+(off)); UNW(.spillsp r5,SW(R5)+(off)); \
+ UNW(.spillsp r6,SW(R6)+(off)); UNW(.spillsp r7,SW(R7)+(off)); \
+ UNW(.spillsp b0,SW(B0)+(off)); UNW(.spillsp b1,SW(B1)+(off)); \
+ UNW(.spillsp b2,SW(B2)+(off)); UNW(.spillsp b3,SW(B3)+(off)); \
+ UNW(.spillsp b4,SW(B4)+(off)); UNW(.spillsp b5,SW(B5)+(off)); \
+ UNW(.spillsp ar.pfs,SW(AR_PFS)+(off)); UNW(.spillsp ar.lc,SW(AR_LC)+(off)); \
+ UNW(.spillsp @priunat,SW(AR_UNAT)+(off)); \
+ UNW(.spillsp ar.rnat,SW(AR_RNAT)+(off)); UNW(.spillsp ar.bspstore,SW(AR_BSPSTORE)+(off)); \
+ UNW(.spillsp pr,SW(PR)+(off))
+
+#define DO_SAVE_SWITCH_STACK \
+ movl r28=1f; \
+ ;; \
+ .fframe IA64_SWITCH_STACK_SIZE; \
+ adds sp=-IA64_SWITCH_STACK_SIZE,sp; \
+ mov b7=r28; \
+ SWITCH_STACK_SAVES(0); \
+ br.cond.sptk.many save_switch_stack; \
+1:
+
+#define DO_LOAD_SWITCH_STACK(extra) \
+ movl r28=1f; \
+ ;; \
+ mov b7=r28; \
+ br.cond.sptk.many load_switch_stack; \
+1: UNW(.restore sp); \
+ extra; \
+ adds sp=IA64_SWITCH_STACK_SIZE,sp
diff --git a/arch/ia64/kernel/fw-emu.c b/arch/ia64/kernel/fw-emu.c
index 23ded0730..9e5ec1668 100644
--- a/arch/ia64/kernel/fw-emu.c
+++ b/arch/ia64/kernel/fw-emu.c
@@ -124,7 +124,18 @@ asm ("
.proc pal_emulator_static
pal_emulator_static:
mov r8=-1
- cmp.eq p6,p7=6,r28 /* PAL_PTCE_INFO */
+
+ mov r9=256
+ ;;
+ cmp.gtu p6,p7=r9,r28 /* r28 <= 255? */
+(p6) br.cond.sptk.few static
+ ;;
+ mov r9=512
+ ;;
+ cmp.gtu p6,p7=r9,r28
+(p6) br.cond.sptk.few stacked
+ ;;
+static: cmp.eq p6,p7=6,r28 /* PAL_PTCE_INFO */
(p7) br.cond.sptk.few 1f
;;
mov r8=0 /* status = 0 */
@@ -157,7 +168,12 @@ pal_emulator_static:
;;
mov ar.lc=r9
mov r8=r0
-1: br.cond.sptk.few rp
+1:
+ br.cond.sptk.few rp
+
+stacked:
+ br.ret.sptk.few rp
+
.endp pal_emulator_static\n");
/* Macro to emulate SAL call using legacy IN and OUT calls to CF8, CFC etc.. */
diff --git a/arch/ia64/kernel/gate.S b/arch/ia64/kernel/gate.S
index 8eabe53d1..f7f8d02ae 100644
--- a/arch/ia64/kernel/gate.S
+++ b/arch/ia64/kernel/gate.S
@@ -3,10 +3,11 @@
* each task's text region. For now, it contains the signal
* trampoline code only.
*
- * Copyright (C) 1999 Hewlett-Packard Co
- * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com>
+ * Copyright (C) 1999-2000 Hewlett-Packard Co
+ * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com>
*/
+#include <asm/asmmacro.h>
#include <asm/offsets.h>
#include <asm/sigcontext.h>
#include <asm/system.h>
@@ -75,15 +76,12 @@
* [sp+16] = sigframe
*/
- .global ia64_sigtramp
- .proc ia64_sigtramp
-ia64_sigtramp:
+GLOBAL_ENTRY(ia64_sigtramp)
ld8 r10=[r3],8 // get signal handler entry point
br.call.sptk.many rp=invoke_sighandler
- .endp ia64_sigtramp
+END(ia64_sigtramp)
- .proc invoke_sighandler
-invoke_sighandler:
+ENTRY(invoke_sighandler)
ld8 gp=[r3] // get signal handler's global pointer
mov b6=r10
cover // push args in interrupted frame onto backing store
@@ -152,10 +150,9 @@ back_from_restore_rbs:
ldf.fill f15=[base1],32
mov r15=__NR_rt_sigreturn
break __BREAK_SYSCALL
- .endp invoke_sighandler
+END(invoke_sighandler)
- .proc setup_rbs
-setup_rbs:
+ENTRY(setup_rbs)
flushrs // must be first in insn
mov ar.rsc=r0 // put RSE into enforced lazy mode
adds r16=(RNAT_OFF+SIGCONTEXT_OFF),sp
@@ -167,9 +164,9 @@ setup_rbs:
mov ar.rsc=0xf // set RSE into eager mode, pl 3
invala // invalidate ALAT
br.cond.sptk.many back_from_setup_rbs
+END(setup_rbs)
- .proc restore_rbs
-restore_rbs:
+ENTRY(restore_rbs)
flushrs
mov ar.rsc=r0 // put RSE into enforced lazy mode
adds r16=(RNAT_OFF+SIGCONTEXT_OFF),sp
@@ -181,5 +178,4 @@ restore_rbs:
mov ar.rsc=0xf // (will be restored later on from sc_ar_rsc)
// invala not necessary as that will happen when returning to user-mode
br.cond.sptk.many back_from_restore_rbs
-
- .endp restore_rbs
+END(restore_rbs)
diff --git a/arch/ia64/kernel/head.S b/arch/ia64/kernel/head.S
index 35a52628a..d0bc7687f 100644
--- a/arch/ia64/kernel/head.S
+++ b/arch/ia64/kernel/head.S
@@ -16,6 +16,7 @@
#include <linux/config.h>
+#include <asm/asmmacro.h>
#include <asm/fpu.h>
#include <asm/pal.h>
#include <asm/offsets.h>
@@ -54,10 +55,12 @@ halt_msg:
stringz "Halting kernel\n"
.text
- .align 16
- .global _start
- .proc _start
-_start:
+
+GLOBAL_ENTRY(_start)
+ UNW(.prologue)
+ UNW(.save rp, r4) // terminate unwind chain with a NULL rp
+ UNW(mov r4=r0)
+ UNW(.body)
// set IVT entry point---can't access I/O ports without it
movl r3=ia64_ivt
;;
@@ -156,12 +159,9 @@ alive_msg:
ld8 out0=[r2]
br.call.sptk.few b0=console_print
self: br.sptk.few self // endless loop
- .endp _start
+END(_start)
- .align 16
- .global ia64_save_debug_regs
- .proc ia64_save_debug_regs
-ia64_save_debug_regs:
+GLOBAL_ENTRY(ia64_save_debug_regs)
alloc r16=ar.pfs,1,0,0,0
mov r20=ar.lc // preserve ar.lc
mov ar.lc=IA64_NUM_DBG_REGS-1
@@ -177,13 +177,10 @@ ia64_save_debug_regs:
br.cloop.sptk.few 1b
;;
mov ar.lc=r20 // restore ar.lc
- br.ret.sptk.few b0
- .endp ia64_save_debug_regs
+ br.ret.sptk.few rp
+END(ia64_save_debug_regs)
- .align 16
- .global ia64_load_debug_regs
- .proc ia64_load_debug_regs
-ia64_load_debug_regs:
+GLOBAL_ENTRY(ia64_load_debug_regs)
alloc r16=ar.pfs,1,0,0,0
lfetch.nta [in0]
mov r20=ar.lc // preserve ar.lc
@@ -200,13 +197,10 @@ ia64_load_debug_regs:
br.cloop.sptk.few 1b
;;
mov ar.lc=r20 // restore ar.lc
- br.ret.sptk.few b0
- .endp ia64_load_debug_regs
+ br.ret.sptk.few rp
+END(ia64_load_debug_regs)
- .align 16
- .global __ia64_save_fpu
- .proc __ia64_save_fpu
-__ia64_save_fpu:
+GLOBAL_ENTRY(__ia64_save_fpu)
alloc r2=ar.pfs,1,0,0,0
adds r3=16,in0
;;
@@ -354,12 +348,9 @@ __ia64_save_fpu:
stf.spill.nta [in0]=f126,32
stf.spill.nta [ r3]=f127,32
br.ret.sptk.few rp
- .endp __ia64_save_fpu
+END(__ia64_save_fpu)
- .align 16
- .global __ia64_load_fpu
- .proc __ia64_load_fpu
-__ia64_load_fpu:
+GLOBAL_ENTRY(__ia64_load_fpu)
alloc r2=ar.pfs,1,0,0,0
adds r3=16,in0
;;
@@ -507,12 +498,9 @@ __ia64_load_fpu:
ldf.fill.nta f126=[in0],32
ldf.fill.nta f127=[ r3],32
br.ret.sptk.few rp
- .endp __ia64_load_fpu
+END(__ia64_load_fpu)
- .align 16
- .global __ia64_init_fpu
- .proc __ia64_init_fpu
-__ia64_init_fpu:
+GLOBAL_ENTRY(__ia64_init_fpu)
alloc r2=ar.pfs,0,0,0,0
stf.spill [sp]=f0
mov f32=f0
@@ -644,4 +632,74 @@ __ia64_init_fpu:
ldf.fill f126=[sp]
mov f127=f0
br.ret.sptk.few rp
- .endp __ia64_init_fpu
+END(__ia64_init_fpu)
+
+/*
+ * Switch execution mode from virtual to physical or vice versa.
+ *
+ * Inputs:
+ * r16 = new psr to establish
+ *
+ * Note: RSE must already be in enforced lazy mode
+ */
+GLOBAL_ENTRY(ia64_switch_mode)
+ {
+ alloc r2=ar.pfs,0,0,0,0
+ rsm psr.i | psr.ic // disable interrupts and interrupt collection
+ mov r15=ip
+ }
+ ;;
+ {
+ flushrs // must be first insn in group
+ srlz.i
+ shr.u r19=r15,61 // r19 <- top 3 bits of current IP
+ }
+ ;;
+ mov cr.ipsr=r16 // set new PSR
+ add r3=1f-ia64_switch_mode,r15
+ xor r15=0x7,r19 // flip the region bits
+
+ mov r17=ar.bsp
+ mov r14=rp // get return address into a general register
+
+ // switch RSE backing store:
+ ;;
+ dep r17=r15,r17,61,3 // make ar.bsp physical or virtual
+ mov r18=ar.rnat // save ar.rnat
+ ;;
+ mov ar.bspstore=r17 // this steps on ar.rnat
+ dep r3=r15,r3,61,3 // make rfi return address physical or virtual
+ ;;
+ mov cr.iip=r3
+ mov cr.ifs=r0
+ dep sp=r15,sp,61,3 // make stack pointer physical or virtual
+ ;;
+ mov ar.rnat=r18 // restore ar.rnat
+ dep r14=r15,r14,61,3 // make function return address physical or virtual
+ rfi // must be last insn in group
+ ;;
+1: mov rp=r14
+ br.ret.sptk.few rp
+END(ia64_switch_mode)
+
+#ifdef CONFIG_IA64_BRL_EMU
+
+/*
+ * Assembly routines used by brl_emu.c to set preserved register state.
+ */
+
+#define SET_REG(reg) \
+ GLOBAL_ENTRY(ia64_set_##reg); \
+ alloc r16=ar.pfs,1,0,0,0; \
+ mov reg=r32; \
+ ;; \
+ br.ret.sptk rp; \
+ END(ia64_set_##reg)
+
+SET_REG(b1);
+SET_REG(b2);
+SET_REG(b3);
+SET_REG(b4);
+SET_REG(b5);
+
+#endif /* CONFIG_IA64_BRL_EMU */
diff --git a/arch/ia64/kernel/ia64_ksyms.c b/arch/ia64/kernel/ia64_ksyms.c
new file mode 100644
index 000000000..7f01b667c
--- /dev/null
+++ b/arch/ia64/kernel/ia64_ksyms.c
@@ -0,0 +1,72 @@
+/*
+ * Architecture-specific kernel symbols
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/string.h>
+EXPORT_SYMBOL_NOVERS(memset);
+EXPORT_SYMBOL(memcmp);
+EXPORT_SYMBOL_NOVERS(memcpy);
+EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(strcat);
+EXPORT_SYMBOL(strchr);
+EXPORT_SYMBOL(strcmp);
+EXPORT_SYMBOL(strcpy);
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(strncat);
+EXPORT_SYMBOL(strncmp);
+EXPORT_SYMBOL(strncpy);
+EXPORT_SYMBOL(strtok);
+
+#include <linux/pci.h>
+EXPORT_SYMBOL(pci_alloc_consistent);
+EXPORT_SYMBOL(pci_free_consistent);
+
+#include <linux/in6.h>
+#include <asm/checksum.h>
+EXPORT_SYMBOL(csum_partial_copy_nocheck);
+
+#include <asm/irq.h>
+EXPORT_SYMBOL(enable_irq);
+EXPORT_SYMBOL(disable_irq);
+
+#include <asm/current.h>
+#include <asm/hardirq.h>
+EXPORT_SYMBOL(irq_stat);
+
+#include <asm/processor.h>
+EXPORT_SYMBOL(cpu_data);
+EXPORT_SYMBOL(kernel_thread);
+
+#ifdef CONFIG_SMP
+EXPORT_SYMBOL(synchronize_irq);
+
+#include <asm/smplock.h>
+EXPORT_SYMBOL(kernel_flag);
+
+#include <asm/system.h>
+EXPORT_SYMBOL(__global_sti);
+EXPORT_SYMBOL(__global_cli);
+EXPORT_SYMBOL(__global_save_flags);
+EXPORT_SYMBOL(__global_restore_flags);
+
+#endif
+
+#include <asm/uaccess.h>
+EXPORT_SYMBOL(__copy_user);
+
+#include <asm/unistd.h>
+EXPORT_SYMBOL(__ia64_syscall);
+
+/* from arch/ia64/lib */
+extern void __divdi3(void);
+extern void __udivdi3(void);
+extern void __moddi3(void);
+extern void __umoddi3(void);
+
+EXPORT_SYMBOL_NOVERS(__divdi3);
+EXPORT_SYMBOL_NOVERS(__udivdi3);
+EXPORT_SYMBOL_NOVERS(__moddi3);
+EXPORT_SYMBOL_NOVERS(__umoddi3);
diff --git a/arch/ia64/kernel/irq.c b/arch/ia64/kernel/irq.c
index a01432a60..279befd3b 100644
--- a/arch/ia64/kernel/irq.c
+++ b/arch/ia64/kernel/irq.c
@@ -201,10 +201,14 @@ static void show(char * str)
printk(" %d",local_bh_count(i));
printk(" ]\nStack dumps:");
-#ifdef __ia64__
- printk(" ]\nStack dumps: <unimplemented on IA-64---please fix me>");
- /* for now we don't have stack dumping support... */
-#elif __i386__
+#if defined(__ia64__)
+ /*
+ * We can't unwind the stack of another CPU without access to
+ * the registers of that CPU. And sending an IPI when we're
+ * in a potentially wedged state doesn't sound like a smart
+ * idea.
+ */
+#elif defined(__i386__)
for(i=0;i< smp_num_cpus;i++) {
unsigned long esp;
if(i==cpu)
@@ -227,9 +231,7 @@ static void show(char * str)
You lose...
#endif
printk("\nCPU %d:",cpu);
-#ifdef __i386__
show_stack(NULL);
-#endif
printk("\n");
}
@@ -582,7 +584,8 @@ unsigned int do_IRQ(unsigned long irq, struct pt_regs *regs)
if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
action = desc->action;
status &= ~IRQ_PENDING; /* we commit to handling */
- status |= IRQ_INPROGRESS; /* we are handling it */
+ if (!(status & IRQ_PER_CPU))
+ status |= IRQ_INPROGRESS; /* we are handling it */
}
desc->status = status;
diff --git a/arch/ia64/kernel/irq_ia64.c b/arch/ia64/kernel/irq_ia64.c
index 1a8398f85..1ee2974b5 100644
--- a/arch/ia64/kernel/irq_ia64.c
+++ b/arch/ia64/kernel/irq_ia64.c
@@ -33,7 +33,9 @@
#include <asm/pgtable.h>
#include <asm/system.h>
-#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC
+#define IRQ_DEBUG 0
+
+#ifdef CONFIG_ITANIUM_A1_SPECIFIC
spinlock_t ivr_read_lock;
#endif
@@ -49,7 +51,7 @@ __u8 isa_irq_to_vector_map[16] = {
0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x40, 0x41
};
-#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC
+#ifdef CONFIG_ITANIUM_A1_SPECIFIC
int usbfix;
@@ -63,7 +65,7 @@ usbfix_option (char *str)
__setup("usbfix", usbfix_option);
-#endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC */
+#endif /* CONFIG_ITANIUM_A1_SPECIFIC */
/*
* That's where the IVT branches when we get an external
@@ -73,13 +75,8 @@ __setup("usbfix", usbfix_option);
void
ia64_handle_irq (unsigned long vector, struct pt_regs *regs)
{
- unsigned long bsp, sp, saved_tpr;
-
-#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC
-# ifndef CONFIG_SMP
- static unsigned int max_prio = 0;
- unsigned int prev_prio;
-# endif
+ unsigned long saved_tpr;
+#ifdef CONFIG_ITANIUM_A1_SPECIFIC
unsigned long eoi_ptr;
# ifdef CONFIG_USB
@@ -95,18 +92,14 @@ ia64_handle_irq (unsigned long vector, struct pt_regs *regs)
spin_lock(&ivr_read_lock);
{
unsigned int tmp;
-
/*
* Disable PCI writes
*/
outl(0x80ff81c0, 0xcf8);
tmp = inl(0xcfc);
outl(tmp | 0x400, 0xcfc);
-
eoi_ptr = inl(0xcfc);
-
vector = ia64_get_ivr();
-
/*
* Enable PCI writes
*/
@@ -118,75 +111,61 @@ ia64_handle_irq (unsigned long vector, struct pt_regs *regs)
if (usbfix)
reenable_usb();
# endif
+#endif /* CONFIG_ITANIUM_A1_SPECIFIC */
-# ifndef CONFIG_SMP
- prev_prio = max_prio;
- if (vector < max_prio) {
- printk ("ia64_handle_irq: got vector %lu while %u was in progress!\n",
- vector, max_prio);
-
- } else
- max_prio = vector;
-# endif /* !CONFIG_SMP */
-#endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC */
+#if IRQ_DEBUG
+ {
+ unsigned long bsp, sp;
+
+ asm ("mov %0=ar.bsp" : "=r"(bsp));
+ asm ("mov %0=sp" : "=r"(sp));
+
+ if ((sp - bsp) < 1024) {
+ static unsigned char count;
+ static long last_time;
+
+ if (count > 5 && jiffies - last_time > 5*HZ)
+ count = 0;
+ if (++count < 5) {
+ last_time = jiffies;
+ printk("ia64_handle_irq: DANGER: less than "
+ "1KB of free stack space!!\n"
+ "(bsp=0x%lx, sp=%lx)\n", bsp, sp);
+ }
+ }
+ }
+#endif /* IRQ_DEBUG */
/*
* Always set TPR to limit maximum interrupt nesting depth to
* 16 (without this, it would be ~240, which could easily lead
- * to kernel stack overflows.
+ * to kernel stack overflows).
*/
saved_tpr = ia64_get_tpr();
ia64_srlz_d();
- ia64_set_tpr(vector);
- ia64_srlz_d();
-
- asm ("mov %0=ar.bsp" : "=r"(bsp));
- asm ("mov %0=sp" : "=r"(sp));
-
- if ((sp - bsp) < 1024) {
- static long last_time;
- static unsigned char count;
-
- if (count > 5 && jiffies - last_time > 5*HZ)
- count = 0;
- if (++count < 5) {
- last_time = jiffies;
- printk("ia64_handle_irq: DANGER: less than 1KB of free stack space!!\n"
- "(bsp=0x%lx, sp=%lx)\n", bsp, sp);
+ do {
+ if (vector >= NR_IRQS) {
+ printk("handle_irq: invalid vector %lu\n", vector);
+ ia64_set_tpr(saved_tpr);
+ ia64_srlz_d();
+ return;
}
- }
+ ia64_set_tpr(vector);
+ ia64_srlz_d();
- /*
- * The interrupt is now said to be in service
- */
- if (vector >= NR_IRQS) {
- printk("handle_irq: invalid vector %lu\n", vector);
- goto out;
- }
-
- do_IRQ(vector, regs);
- out:
-#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC
- {
- long pEOI;
-
- asm ("mov %0=0;; (p1) mov %0=1" : "=r"(pEOI));
- if (!pEOI) {
- printk("Yikes: ia64_handle_irq() without pEOI!!\n");
- asm volatile ("cmp.eq p1,p0=r0,r0" : "=r"(pEOI));
- }
- }
-
- local_irq_disable();
-# ifndef CONFIG_SMP
- if (max_prio == vector)
- max_prio = prev_prio;
-# endif /* !CONFIG_SMP */
-#endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC */
+ do_IRQ(vector, regs);
- ia64_srlz_d();
- ia64_set_tpr(saved_tpr);
- ia64_srlz_d();
+ /*
+ * Disable interrupts and send EOI:
+ */
+ local_irq_disable();
+ ia64_set_tpr(saved_tpr);
+ ia64_eoi();
+#ifdef CONFIG_ITANIUM_A1_SPECIFIC
+ break;
+#endif
+ vector = ia64_get_ivr();
+ } while (vector != IA64_SPURIOUS_INT);
}
#ifdef CONFIG_SMP
@@ -210,12 +189,12 @@ init_IRQ (void)
ia64_set_lrr0(0, 1);
ia64_set_lrr1(0, 1);
- irq_desc[TIMER_IRQ].handler = &irq_type_ia64_sapic;
irq_desc[IA64_SPURIOUS_INT].handler = &irq_type_ia64_sapic;
#ifdef CONFIG_SMP
/*
* Configure the IPI vector and handler
*/
+ irq_desc[IPI_IRQ].status |= IRQ_PER_CPU;
irq_desc[IPI_IRQ].handler = &irq_type_ia64_sapic;
setup_irq(IPI_IRQ, &ipi_irqaction);
#endif
@@ -234,7 +213,7 @@ ipi_send (int cpu, int vector, int delivery_mode, int redirect)
{
unsigned long ipi_addr;
unsigned long ipi_data;
-#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC
+#ifdef CONFIG_ITANIUM_A1_SPECIFIC
unsigned long flags;
#endif
# define EID 0
@@ -242,13 +221,13 @@ ipi_send (int cpu, int vector, int delivery_mode, int redirect)
ipi_data = (delivery_mode << 8) | (vector & 0xff);
ipi_addr = ipi_base_addr | ((cpu << 8 | EID) << 4) | ((redirect & 1) << 3);
-#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC
+#ifdef CONFIG_ITANIUM_A1_SPECIFIC
spin_lock_irqsave(&ivr_read_lock, flags);
-#endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC */
+#endif
writeq(ipi_data, ipi_addr);
-#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC
+#ifdef CONFIG_ITANIUM_A1_SPECIFIC
spin_unlock_irqrestore(&ivr_read_lock, flags);
#endif
}
diff --git a/arch/ia64/kernel/ivt.S b/arch/ia64/kernel/ivt.S
index 56dd2a333..d58cd494e 100644
--- a/arch/ia64/kernel/ivt.S
+++ b/arch/ia64/kernel/ivt.S
@@ -170,9 +170,31 @@ ia64_ivt:
* The ITLB basically does the same as the VHPT handler except
* that we always insert exactly one instruction TLB entry.
*/
+#if 0
+ /*
+ * This code works, but I don't want to enable it until I have numbers
+ * that prove this to be a win.
+ */
+ mov r31=pr // save predicates
+ ;;
+ thash r17=r16 // compute virtual address of L3 PTE
+ ;;
+ ld8.s r18=[r17] // try to read L3 PTE
+ ;;
+ tnat.nz p6,p0=r18 // did read succeed?
+(p6) br.cond.spnt.many 1f
+ ;;
+ itc.i r18
+ ;;
+ mov pr=r31,-1
+ rfi
+
+1: rsm psr.dt // use physical addressing for data
+#else
mov r16=cr.ifa // get address that caused the TLB miss
;;
rsm psr.dt // use physical addressing for data
+#endif
mov r31=pr // save the predicate registers
mov r19=ar.k7 // get page table base address
shl r21=r16,3 // shift bit 60 into sign bit
@@ -222,9 +244,31 @@ ia64_ivt:
* that we always insert exactly one data TLB entry.
*/
mov r16=cr.ifa // get address that caused the TLB miss
+#if 0
+ /*
+ * This code works, but I don't want to enable it until I have numbers
+ * that prove this to be a win.
+ */
+ mov r31=pr // save predicates
+ ;;
+ thash r17=r16 // compute virtual address of L3 PTE
+ ;;
+ ld8.s r18=[r17] // try to read L3 PTE
+ ;;
+ tnat.nz p6,p0=r18 // did read succeed?
+(p6) br.cond.spnt.many 1f
+ ;;
+ itc.d r18
;;
+ mov pr=r31,-1
+ rfi
+
+1: rsm psr.dt // use physical addressing for data
+#else
rsm psr.dt // use physical addressing for data
mov r31=pr // save the predicate registers
+ ;;
+#endif
mov r19=ar.k7 // get page table base address
shl r21=r16,3 // shift bit 60 into sign bit
shr.u r17=r16,61 // get the region number into r17
@@ -265,37 +309,6 @@ ia64_ivt:
mov pr=r31,-1 // restore predicate registers
rfi
- //-----------------------------------------------------------------------------------
- // call do_page_fault (predicates are in r31, psr.dt is off, r16 is faulting address)
-page_fault:
- SAVE_MIN_WITH_COVER
- //
- // Copy control registers to temporary registers, then turn on psr bits,
- // then copy the temporary regs to the output regs. We have to do this
- // because the "alloc" can cause a mandatory store which could lead to
- // an "Alt DTLB" fault which we can handle only if psr.ic is on.
- //
- mov r8=cr.ifa
- mov r9=cr.isr
- adds r3=8,r2 // set up second base pointer
- ;;
- ssm psr.ic | psr.dt
- ;;
- srlz.i // guarantee that interrupt collection is enabled
- ;;
-(p15) ssm psr.i // restore psr.i
- movl r14=ia64_leave_kernel
- ;;
- alloc r15=ar.pfs,0,0,3,0 // must be first in insn group
- mov out0=r8
- mov out1=r9
- ;;
- SAVE_REST
- mov rp=r14
- ;;
- adds out2=16,r12 // out2 = pointer to pt_regs
- br.call.sptk.few b6=ia64_do_page_fault // ignore return address
-
.align 1024
/////////////////////////////////////////////////////////////////////////////////////////
// 0x0c00 Entry 3 (size 64 bundles) Alt ITLB (19)
@@ -303,7 +316,7 @@ page_fault:
movl r17=__DIRTY_BITS|_PAGE_PL_0|_PAGE_AR_RX
;;
shr.u r18=r16,57 // move address bit 61 to bit 4
- dep r16=0,r16,IA64_PHYS_BITS,(64-IA64_PHYS_BITS) // clear ed, resvd, and unimpl. phys bits
+ dep r16=0,r16,IA64_MAX_PHYS_BITS,(64-IA64_MAX_PHYS_BITS) // clear ed & reserved bits
;;
andcm r18=0x10,r18 // bit 4=~address-bit(61)
dep r16=r17,r16,0,12 // insert PTE control bits into r16
@@ -318,18 +331,58 @@ page_fault:
// 0x1000 Entry 4 (size 64 bundles) Alt DTLB (7,46)
mov r16=cr.ifa // get address that caused the TLB miss
movl r17=__DIRTY_BITS|_PAGE_PL_0|_PAGE_AR_RW
+ mov r20=cr.isr
+ mov r21=cr.ipsr
+ mov r19=pr
;;
+ tbit.nz p6,p7=r20,IA64_ISR_SP_BIT // is speculation bit on?
shr.u r18=r16,57 // move address bit 61 to bit 4
- dep r16=0,r16,IA64_PHYS_BITS,(64-IA64_PHYS_BITS) // clear ed, resvd, and unimpl. phys bits
+ dep r16=0,r16,IA64_MAX_PHYS_BITS,(64-IA64_MAX_PHYS_BITS) // clear ed & reserved bits
;;
+ dep r21=-1,r21,IA64_PSR_ED_BIT,1
andcm r18=0x10,r18 // bit 4=~address-bit(61)
dep r16=r17,r16,0,12 // insert PTE control bits into r16
;;
or r16=r16,r18 // set bit 4 (uncached) if the access was to region 6
+(p6) mov cr.ipsr=r21
;;
- itc.d r16 // insert the TLB entry
+(p7) itc.d r16 // insert the TLB entry
+ mov pr=r19,-1
rfi
+ ;;
+
+ //-----------------------------------------------------------------------------------
+ // call do_page_fault (predicates are in r31, psr.dt is off, r16 is faulting address)
+page_fault:
+ SAVE_MIN_WITH_COVER
+ //
+ // Copy control registers to temporary registers, then turn on psr bits,
+ // then copy the temporary regs to the output regs. We have to do this
+ // because the "alloc" can cause a mandatory store which could lead to
+ // an "Alt DTLB" fault which we can handle only if psr.ic is on.
+ //
+ mov r8=cr.ifa
+ mov r9=cr.isr
+ adds r3=8,r2 // set up second base pointer
+ ;;
+ ssm psr.ic | psr.dt
+ ;;
+ srlz.i // guarantee that interrupt collection is enabled
+ ;;
+(p15) ssm psr.i // restore psr.i
+ movl r14=ia64_leave_kernel
+ ;;
+ alloc r15=ar.pfs,0,0,3,0 // must be first in insn group
+ mov out0=r8
+ mov out1=r9
+ ;;
+ SAVE_REST
+ mov rp=r14
+ ;;
+ adds out2=16,r12 // out2 = pointer to pt_regs
+ br.call.sptk.few b6=ia64_do_page_fault // ignore return address
+
.align 1024
/////////////////////////////////////////////////////////////////////////////////////////
// 0x1400 Entry 5 (size 64 bundles) Data nested TLB (6,45)
@@ -338,7 +391,7 @@ page_fault:
// Access-bit, or Data Access-bit faults cause a nested fault because the
// dTLB entry for the virtual page table isn't present. In such a case,
// we lookup the pte for the faulting address by walking the page table
- // and return to the contination point passed in register r30.
+ // and return to the continuation point passed in register r30.
// In accessing the page tables, we don't need to check for NULL entries
// because if the page tables didn't map the faulting address, it would not
// be possible to receive one of the above faults.
@@ -441,9 +494,6 @@ page_fault:
tbit.z p6,p0=r17,IA64_PSR_IS_BIT // IA64 instruction set?
;;
(p6) mov r16=r18 // if so, use cr.iip instead of cr.ifa
-#if 0
- ;;
-#endif
mov pr=r31,-1
#endif /* CONFIG_ITANIUM */
movl r30=1f // load continuation point in case of nested fault
@@ -489,7 +539,6 @@ page_fault:
;;
srlz.d // ensure everyone knows psr.dt is off...
cmp.eq p0,p7=r16,r17 // is this a system call? (p7 <- false, if so)
-
#if 1
// Allow syscalls via the old system call number for the time being. This is
// so we can transition to the new syscall number in a relatively smooth
@@ -498,7 +547,6 @@ page_fault:
;;
(p7) cmp.eq.or.andcm p0,p7=r16,r17 // is this the old syscall number?
#endif
-
(p7) br.cond.spnt.many non_syscall
SAVE_MIN // uses r31; defines r2:
@@ -575,13 +623,12 @@ page_fault:
ssm psr.ic | psr.dt // turn interrupt collection and data translation back on
;;
adds r3=8,r2 // set up second base pointer for SAVE_REST
- cmp.eq pEOI,p0=r0,r0 // set pEOI flag so that ia64_leave_kernel writes cr.eoi
srlz.i // ensure everybody knows psr.ic and psr.dt are back on
;;
SAVE_REST
;;
alloc r14=ar.pfs,0,0,2,0 // must be first in an insn group
-#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC
+#ifdef CONFIG_ITANIUM_A1_SPECIFIC
mov out0=r0 // defer reading of cr.ivr to handle_irq...
#else
mov out0=cr.ivr // pass cr.ivr as first arg
@@ -609,6 +656,50 @@ page_fault:
// 0x3c00 Entry 15 (size 64 bundles) Reserved
FAULT(15)
+//
+// Squatting in this space ...
+//
+// This special case dispatcher for illegal operation faults
+// allows preserved registers to be modified through a
+// callback function (asm only) that is handed back from
+// the fault handler in r8. Up to three arguments can be
+// passed to the callback function by returning an aggregate
+// with the callback as its first element, followed by the
+// arguments.
+//
+dispatch_illegal_op_fault:
+ SAVE_MIN_WITH_COVER
+ //
+ // The "alloc" can cause a mandatory store which could lead to
+ // an "Alt DTLB" fault which we can handle only if psr.ic is on.
+ //
+ ssm psr.ic | psr.dt
+ ;;
+ srlz.i // guarantee that interrupt collection is enabled
+ ;;
+(p15) ssm psr.i // restore psr.i
+ adds r3=8,r2 // set up second base pointer for SAVE_REST
+ ;;
+ alloc r14=ar.pfs,0,0,1,0 // must be first in insn group
+ mov out0=ar.ec
+ ;;
+ SAVE_REST
+ ;;
+ br.call.sptk.few rp=ia64_illegal_op_fault
+ ;;
+ alloc r14=ar.pfs,0,0,3,0 // must be first in insn group
+ mov out0=r9
+ mov out1=r10
+ mov out2=r11
+ movl r15=ia64_leave_kernel
+ ;;
+ mov rp=r15
+ mov b6=r8
+ ;;
+ cmp.ne p6,p0=0,r8
+(p6) br.call.dpnt b6=b6 // call returns to ia64_leave_kernel
+ br.sptk ia64_leave_kernel
+
.align 1024
/////////////////////////////////////////////////////////////////////////////////////////
// 0x4000 Entry 16 (size 64 bundles) Reserved
@@ -643,14 +734,17 @@ dispatch_to_ia32_handler:
(p6) br.call.dpnt.few b6=non_ia32_syscall
adds r14=IA64_PT_REGS_R8_OFFSET + 16,sp // 16 byte hole per SW conventions
-
+ adds r15=IA64_PT_REGS_R1_OFFSET + 16,sp
+ ;;
+ cmp.eq pSys,pNonSys=r0,r0 // set pSys=1, pNonSys=0
+ st8 [r15]=r8 // save orignal EAX in r1 (IA32 procs don't use the GP)
;;
alloc r15=ar.pfs,0,0,6,0 // must first in an insn group
;;
ld4 r8=[r14],8 // r8 == EAX (syscall number)
- mov r15=0xff
+ mov r15=190 // sys_vfork - last implemented system call
;;
- cmp.ltu.unc p6,p7=r8,r15
+ cmp.leu.unc p6,p7=r8,r15
ld4 out1=[r14],8 // r9 == ecx
;;
ld4 out2=[r14],8 // r10 == edx
@@ -868,7 +962,16 @@ dispatch_to_fault_handler:
.align 256
/////////////////////////////////////////////////////////////////////////////////////////
// 0x5400 Entry 24 (size 16 bundles) General Exception (5,32,34,36,38,39)
- FAULT(24)
+ mov r16=cr.isr
+ mov r31=pr
+ rsm psr.dt // avoid nested faults due to TLB misses...
+ ;;
+ srlz.d // ensure everyone knows psr.dt is off...
+ cmp4.eq p6,p0=0,r16
+(p6) br.sptk dispatch_illegal_op_fault
+ ;;
+ mov r19=24 // fault number
+ br.cond.sptk.many dispatch_to_fault_handler
.align 256
/////////////////////////////////////////////////////////////////////////////////////////
@@ -939,7 +1042,6 @@ dispatch_to_fault_handler:
mov r31=pr // prepare to save predicates
;;
srlz.d // ensure everyone knows psr.dt is off
- mov r19=30 // error vector for fault_handler (when kernel)
br.cond.sptk.many dispatch_unaligned_handler
.align 256
diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c
index 150feac03..003b8dd69 100644
--- a/arch/ia64/kernel/mca.c
+++ b/arch/ia64/kernel/mca.c
@@ -9,15 +9,16 @@
* Copyright (C) 1999 Silicon Graphics, Inc.
* Copyright (C) Vijay Chander(vijay@engr.sgi.com)
*
- * 00/03/29 C. Fleckenstein Fixed PAL/SAL update issues, began MCA bug fixes, logging issues,
+ * 00/03/29 C. Fleckenstein Fixed PAL/SAL update issues, began MCA bug fixes,
+ * logging issues,
* added min save state dump, added INIT handler.
*/
+#include <linux/config.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/irq.h>
#include <linux/smp_lock.h>
-#include <linux/config.h>
#include <asm/page.h>
#include <asm/ptrace.h>
diff --git a/arch/ia64/kernel/mca_asm.S b/arch/ia64/kernel/mca_asm.S
index 81966bb99..e4a9f0530 100644
--- a/arch/ia64/kernel/mca_asm.S
+++ b/arch/ia64/kernel/mca_asm.S
@@ -6,7 +6,6 @@
// 00/03/29 cfleck Added code to save INIT handoff state in pt_regs format, switch to temp kstack,
// switch modes, jump to C INIT handler
//
-#include <asm/offsets.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/mca_asm.h>
diff --git a/arch/ia64/kernel/minstate.h b/arch/ia64/kernel/minstate.h
index bcfe1659c..24be2f53d 100644
--- a/arch/ia64/kernel/minstate.h
+++ b/arch/ia64/kernel/minstate.h
@@ -101,7 +101,6 @@
;; \
st8 [r16]=r18,16; /* save ar.rsc value for "loadrs" */ \
st8.spill [r17]=rR1,16; /* save original r1 */ \
- cmp.ne pEOI,p0=r0,r0 /* clear pEOI by default */ \
;; \
.mem.offset 0,0; st8.spill [r16]=r2,16; \
.mem.offset 8,0; st8.spill [r17]=r3,16; \
diff --git a/arch/ia64/kernel/pal.S b/arch/ia64/kernel/pal.S
index 1506bacc2..e6f44cfb6 100644
--- a/arch/ia64/kernel/pal.S
+++ b/arch/ia64/kernel/pal.S
@@ -4,9 +4,16 @@
*
* Copyright (C) 1999 Don Dugger <don.dugger@intel.com>
* Copyright (C) 1999 Walt Drummond <drummond@valinux.com>
- * Copyright (C) 1999 David Mosberger <davidm@hpl.hp.com>
+ * Copyright (C) 1999-2000 David Mosberger <davidm@hpl.hp.com>
+ * Copyright (C) 2000 Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * 05/22/2000 eranian Added support for stacked register calls
+ * 05/24/2000 eranian Added support for physical mode static calls
*/
+#include <asm/asmmacro.h>
+#include <asm/processor.h>
+
.text
.psr abi64
.psr lsb
@@ -24,29 +31,23 @@ pal_entry_point:
*
* in0 Address of the PAL entry point (text address, NOT a function descriptor).
*/
- .align 16
- .global ia64_pal_handler_init
- .proc ia64_pal_handler_init
-ia64_pal_handler_init:
+GLOBAL_ENTRY(ia64_pal_handler_init)
alloc r3=ar.pfs,1,0,0,0
movl r2=pal_entry_point
;;
st8 [r2]=in0
br.ret.sptk.few rp
-
- .endp ia64_pal_handler_init
+END(ia64_pal_handler_init)
/*
* Default PAL call handler. This needs to be coded in assembly because it uses
* the static calling convention, i.e., the RSE may not be used and calls are
* done via "br.cond" (not "br.call").
*/
- .align 16
- .global ia64_pal_default_handler
- .proc ia64_pal_default_handler
-ia64_pal_default_handler:
+GLOBAL_ENTRY(ia64_pal_default_handler)
mov r8=-1
br.cond.sptk.few rp
+END(ia64_pal_default_handler)
/*
* Make a PAL call using the static calling convention.
@@ -56,64 +57,139 @@ ia64_pal_default_handler:
* in2 - in4 Remaning PAL arguments
*
*/
+GLOBAL_ENTRY(ia64_pal_call_static)
+ UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(6))
+ alloc loc1 = ar.pfs,6,90,0,0
+ movl loc2 = pal_entry_point
+1: {
+ mov r28 = in0
+ mov r29 = in1
+ mov r8 = ip
+ }
+ ;;
+ ld8 loc2 = [loc2] // loc2 <- entry point
+ mov r30 = in2
+ mov r31 = in3
+ ;;
+ mov loc3 = psr
+ mov loc0 = rp
+ UNW(.body)
+ adds r8 = .ret0-1b,r8
+ ;;
+ rsm psr.i
+ mov b7 = loc2
+ mov rp = r8
+ ;;
+ br.cond.sptk.few b7
+.ret0: mov psr.l = loc3
+ mov ar.pfs = loc1
+ mov rp = loc0
+ ;;
+ srlz.d // seralize restoration of psr.l
+ br.ret.sptk.few b0
+END(ia64_pal_call_static)
-#ifdef __GCC_MULTIREG_RETVALS__
-# define arg0 in0
-# define arg1 in1
-# define arg2 in2
-# define arg3 in3
-# define arg4 in4
-#else
-# define arg0 in1
-# define arg1 in2
-# define arg2 in3
-# define arg3 in4
-# define arg4 in5
-#endif
+/*
+ * Make a PAL call using the stacked registers calling convention.
+ *
+ * Inputs:
+ * in0 Index of PAL service
+ * in2 - in3 Remaning PAL arguments
+ */
+GLOBAL_ENTRY(ia64_pal_call_stacked)
+ UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(5))
+ alloc loc1 = ar.pfs,5,4,87,0
+ movl loc2 = pal_entry_point
- .text
- .psr abi64
- .psr lsb
- .lsb
+ mov r28 = in0 // Index MUST be copied to r28
+ mov out0 = in0 // AND in0 of PAL function
+ mov loc0 = rp
+ UNW(.body)
+ ;;
+ ld8 loc2 = [loc2] // loc2 <- entry point
+ mov out1 = in1
+ mov out2 = in2
+ mov out3 = in3
+ mov loc3 = psr
+ ;;
+ rsm psr.i
+ mov b7 = loc2
+ ;;
+ br.call.sptk.many rp=b7 // now make the call
+.ret2:
+ mov psr.l = loc3
+ mov ar.pfs = loc1
+ mov rp = loc0
+ ;;
+ srlz.d // serialize restoration of psr.l
+ br.ret.sptk.few b0
+END(ia64_pal_call_stacked)
+
+/*
+ * Make a physical mode PAL call using the static registers calling convention.
+ *
+ * Inputs:
+ * in0 Index of PAL service
+ * in2 - in3 Remaning PAL arguments
+ *
+ * PSR_DB, PSR_LP, PSR_TB, PSR_ID, PSR_DA are never set by the kernel.
+ * So we don't need to clear them.
+ */
+#define PAL_PSR_BITS_TO_CLEAR \
+ (IA64_PSR_I | IA64_PSR_IT | IA64_PSR_DT | IA64_PSR_RT | \
+ IA64_PSR_DD | IA64_PSR_SS | IA64_PSR_RI | IA64_PSR_ED | \
+ IA64_PSR_DFL | IA64_PSR_DFH)
+
+#define PAL_PSR_BITS_TO_SET \
+ (IA64_PSR_BN)
- .align 16
- .global ia64_pal_call_static
- .proc ia64_pal_call_static
-ia64_pal_call_static:
- alloc loc0 = ar.pfs,6,90,0,0
- movl loc2 = pal_entry_point
+
+GLOBAL_ENTRY(ia64_pal_call_phys_static)
+ UNW(.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(6))
+ alloc loc1 = ar.pfs,6,90,0,0
+ movl loc2 = pal_entry_point
1: {
- mov r28 = arg0
- mov r29 = arg1
- mov r8 = ip
+ mov r28 = in0 // copy procedure index
+ mov r8 = ip // save ip to compute branch
+ mov loc0 = rp // save rp
}
+ UNW(.body)
;;
- ld8 loc2 = [loc2] // loc2 <- entry point
- mov r30 = arg2
- mov r31 = arg3
+ ld8 loc2 = [loc2] // loc2 <- entry point
+ mov r29 = in1 // first argument
+ mov r30 = in2 // copy arg2
+ mov r31 = in3 // copy arg3
;;
- mov loc3 = psr
- mov loc1 = rp
- adds r8 = .ret0-1b,r8
- ;;
- rsm psr.i
- mov b7 = loc2
- mov rp = r8
+ mov loc3 = psr // save psr
+ adds r8 = .ret4-1b,r8 // calculate return address for call
;;
- br.cond.sptk.few b7
-.ret0: mov psr.l = loc3
-#ifndef __GCC_MULTIREG_RETVALS__
- st8 [in0] = r8, 8
+ mov loc4=ar.rsc // save RSE configuration
+ dep.z loc2=loc2,0,61 // convert pal entry point to physical
+ dep.z r8=r8,0,61 // convert rp to physical
;;
- st8 [in0] = r9, 8
+ mov b7 = loc2 // install target to branch reg
+ mov ar.rsc=r0 // put RSE in enforced lazy, LE mode
+ movl r16=PAL_PSR_BITS_TO_CLEAR
+ movl r17=PAL_PSR_BITS_TO_SET
;;
- st8 [in0] = r10, 8
+ or loc3=loc3,r17 // add in psr the bits to set
;;
- st8 [in0] = r11, 8
-#endif
- mov ar.pfs = loc0
- mov rp = loc1
+ andcm r16=loc3,r16 // removes bits to clear from psr
+ br.call.sptk.few rp=ia64_switch_mode
+.ret3:
+ mov rp = r8 // install return address (physical)
+ br.cond.sptk.few b7
+.ret4:
+ mov ar.rsc=r0 // put RSE in enforced lazy, LE mode
+ mov r16=loc3 // r16= original psr
+ br.call.sptk.few rp=ia64_switch_mode // return to virtual mode
+
+.ret5: mov psr.l = loc3 // restore init PSR
+
+ mov ar.pfs = loc1
+ mov rp = loc0
;;
+ mov ar.rsc=loc4 // restore RSE configuration
srlz.d // seralize restoration of psr.l
br.ret.sptk.few b0
- .endp ia64_pal_call_static
+END(ia64_pal_call_phys_static)
diff --git a/arch/ia64/kernel/palinfo.c b/arch/ia64/kernel/palinfo.c
new file mode 100644
index 000000000..ad40e911e
--- /dev/null
+++ b/arch/ia64/kernel/palinfo.c
@@ -0,0 +1,780 @@
+/*
+ * palinfo.c
+ *
+ * Prints processor specific information reported by PAL.
+ * This code is based on specification of PAL as of the
+ * Intel IA-64 Architecture Software Developer's Manual v1.0.
+ *
+ *
+ * Copyright (C) 2000 Hewlett-Packard Co
+ * Copyright (C) 2000 Stephane Eranian <eranian@hpl.hp.com>
+ *
+ * 05/26/2000 S.Eranian initial release
+ *
+ * ISSUES:
+ * - because of some PAL bugs, some calls return invalid results or
+ * are empty for now.
+ * - remove hack to avoid problem with <= 256M RAM for itr.
+ */
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+
+#include <asm/pal.h>
+#include <asm/sal.h>
+#include <asm/efi.h>
+#include <asm/page.h>
+#include <asm/processor.h>
+
+/*
+ * Hope to get rid of these in a near future
+*/
+#define IA64_PAL_VERSION_BUG 1
+
+#define PALINFO_VERSION "0.1"
+
+typedef int (*palinfo_func_t)(char*);
+
+typedef struct {
+ const char *name; /* name of the proc entry */
+ palinfo_func_t proc_read; /* function to call for reading */
+ struct proc_dir_entry *entry; /* registered entry (removal) */
+} palinfo_entry_t;
+
+static struct proc_dir_entry *palinfo_dir;
+
+/*
+ * A bunch of string array to get pretty printing
+ */
+
+static char *cache_types[] = {
+ "", /* not used */
+ "Instruction",
+ "Data",
+ "Data/Instruction" /* unified */
+};
+
+static const char *cache_mattrib[]={
+ "WriteThrough",
+ "WriteBack",
+ "", /* reserved */
+ "" /* reserved */
+};
+
+static const char *cache_st_hints[]={
+ "Temporal, level 1",
+ "Reserved",
+ "Reserved",
+ "Non-temporal, all levels",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved"
+};
+
+static const char *cache_ld_hints[]={
+ "Temporal, level 1",
+ "Non-temporal, level 1",
+ "Reserved",
+ "Non-temporal, all levels",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved"
+};
+
+static const char *rse_hints[]={
+ "enforced lazy",
+ "eager stores",
+ "eager loads",
+ "eager loads and stores"
+};
+
+#define RSE_HINTS_COUNT (sizeof(rse_hints)/sizeof(const char *))
+
+/*
+ * The current resvision of the Volume 2 of
+ * IA-64 Architecture Software Developer's Manual is wrong.
+ * Table 4-10 has invalid information concerning the ma field:
+ * Correct table is:
+ * bit 0 - 001 - UC
+ * bit 4 - 100 - UC
+ * bit 5 - 101 - UCE
+ * bit 6 - 110 - WC
+ * bit 7 - 111 - NatPage
+ */
+static const char *mem_attrib[]={
+ "Write Back (WB)", /* 000 */
+ "Uncacheable (UC)", /* 001 */
+ "Reserved", /* 010 */
+ "Reserved", /* 011 */
+ "Uncacheable (UC)", /* 100 */
+ "Uncacheable Exported (UCE)", /* 101 */
+ "Write Coalescing (WC)", /* 110 */
+ "NaTPage" /* 111 */
+};
+
+
+
+/*
+ * Allocate a buffer suitable for calling PAL code in Virtual mode
+ *
+ * The documentation (PAL2.6) requires thius buffer to have a pinned
+ * translation to avoid any DTLB faults. For this reason we allocate
+ * a page (large enough to hold any possible reply) and use a DTC
+ * to hold the translation during the call. A call the free_palbuffer()
+ * is required to release ALL resources (page + translation).
+ *
+ * The size of the page allocated is based on the PAGE_SIZE defined
+ * at compile time for the kernel, i.e. >= 4Kb.
+ *
+ * Return: a pointer to the newly allocated page (virtual address)
+ */
+static void *
+get_palcall_buffer(void)
+{
+ void *tmp;
+
+ tmp = (void *)__get_free_page(GFP_KERNEL);
+ if (tmp == 0) {
+ printk(KERN_ERR "%s: can't get a buffer page\n", __FUNCTION__);
+ } else if ( ((u64)tmp - PAGE_OFFSET) > (1<<_PAGE_SIZE_256M) ) { /* XXX: temporary hack */
+ unsigned long flags;
+
+ /* PSR.ic must be zero to insert new DTR */
+ ia64_clear_ic(flags);
+
+ /*
+ * we only insert of DTR
+ *
+ * XXX: we need to figure out a way to "allocate" TR(s) to avoid
+ * conflicts. Maybe something in an include file like pgtable.h
+ * page.h or processor.h
+ *
+ * ITR0/DTR0: used for kernel code/data
+ * ITR1/DTR1: used by HP simulator
+ * ITR2/DTR2: used to map PAL code
+ */
+ ia64_itr(0x2, 3, (u64)tmp,
+ pte_val(mk_pte_phys(__pa(tmp), __pgprot(__DIRTY_BITS|_PAGE_PL_0|_PAGE_AR_RW))), PAGE_SHIFT);
+
+ ia64_srlz_d ();
+
+ __restore_flags(flags);
+ }
+
+ return tmp;
+}
+
+/*
+ * Free a palcall buffer allocated with the previous call
+ *
+ * The translation is also purged.
+ */
+static void
+free_palcall_buffer(void *addr)
+{
+ __free_page(addr);
+ ia64_ptr(0x2, (u64)addr, PAGE_SHIFT);
+ ia64_srlz_d ();
+}
+
+/*
+ * Take a 64bit vector and produces a string such that
+ * if bit n is set then 2^n in clear text is generated. The adjustment
+ * to the right unit is also done.
+ *
+ * Input:
+ * - a pointer to a buffer to hold the string
+ * - a 64-bit vector
+ * Ouput:
+ * - a pointer to the end of the buffer
+ *
+ */
+static char *
+bitvector_process(char *p, u64 vector)
+{
+ int i,j;
+ const char *units[]={ "", "K", "M", "G", "T" };
+
+ for (i=0, j=0; i < 64; i++ , j=i/10) {
+ if (vector & 0x1) {
+ p += sprintf(p, "%d%s ", 1 << (i-j*10), units[j]);
+ }
+ vector >>= 1;
+ }
+ return p;
+}
+
+/*
+ * Take a 64bit vector and produces a string such that
+ * if bit n is set then register n is present. The function
+ * takes into account consecutive registers and prints out ranges.
+ *
+ * Input:
+ * - a pointer to a buffer to hold the string
+ * - a 64-bit vector
+ * Ouput:
+ * - a pointer to the end of the buffer
+ *
+ */
+static char *
+bitregister_process(char *p, u64 *reg_info, int max)
+{
+ int i, begin, skip = 0;
+ u64 value = reg_info[0];
+
+ value >>= i = begin = ffs(value) - 1;
+
+ for(; i < max; i++ ) {
+
+ if (i != 0 && (i%64) == 0) value = *++reg_info;
+
+ if ((value & 0x1) == 0 && skip == 0) {
+ if (begin <= i - 2)
+ p += sprintf(p, "%d-%d ", begin, i-1);
+ else
+ p += sprintf(p, "%d ", i-1);
+ skip = 1;
+ begin = -1;
+ } else if ((value & 0x1) && skip == 1) {
+ skip = 0;
+ begin = i;
+ }
+ value >>=1;
+ }
+ if (begin > -1) {
+ if (begin < 127)
+ p += sprintf(p, "%d-127", begin);
+ else
+ p += sprintf(p, "127");
+ }
+
+ return p;
+}
+
+static int
+power_info(char *page)
+{
+ s64 status;
+ char *p = page;
+ pal_power_mgmt_info_u_t *halt_info;
+ int i;
+
+ halt_info = get_palcall_buffer();
+ if (halt_info == 0) return 0;
+
+ status = ia64_pal_halt_info(halt_info);
+ if (status != 0) {
+ free_palcall_buffer(halt_info);
+ return 0;
+ }
+
+ for (i=0; i < 8 ; i++ ) {
+ if (halt_info[i].pal_power_mgmt_info_s.im == 1) {
+ p += sprintf(p, "Power level %d:\n" \
+ "\tentry_latency : %d cycles\n" \
+ "\texit_latency : %d cycles\n" \
+ "\tpower consumption : %d mW\n" \
+ "\tCache+TLB coherency : %s\n", i,
+ halt_info[i].pal_power_mgmt_info_s.entry_latency,
+ halt_info[i].pal_power_mgmt_info_s.exit_latency,
+ halt_info[i].pal_power_mgmt_info_s.power_consumption,
+ halt_info[i].pal_power_mgmt_info_s.co ? "Yes" : "No");
+ } else {
+ p += sprintf(p,"Power level %d: not implemented\n",i);
+ }
+ }
+
+ free_palcall_buffer(halt_info);
+
+ return p - page;
+}
+
+static int
+cache_info(char *page)
+{
+ char *p = page;
+ u64 levels, unique_caches;
+ pal_cache_config_info_t cci;
+ int i,j, k;
+ s64 status;
+
+ if ((status=ia64_pal_cache_summary(&levels, &unique_caches)) != 0) {
+ printk("ia64_pal_cache_summary=%ld\n", status);
+ return 0;
+ }
+
+ p += sprintf(p, "Cache levels : %ld\n" \
+ "Unique caches : %ld\n\n",
+ levels,
+ unique_caches);
+
+ for (i=0; i < levels; i++) {
+
+ for (j=2; j >0 ; j--) {
+
+ /* even without unification some level may not be present */
+ if ((status=ia64_pal_cache_config_info(i,j, &cci)) != 0) {
+ continue;
+ }
+ p += sprintf(p, "%s Cache level %d:\n" \
+ "\tSize : %ld bytes\n" \
+ "\tAttributes : ",
+ cache_types[j+cci.pcci_unified], i+1,
+ cci.pcci_cache_size);
+
+ if (cci.pcci_unified) p += sprintf(p, "Unified ");
+
+ p += sprintf(p, "%s\n", cache_mattrib[cci.pcci_cache_attr]);
+
+ p += sprintf(p, "\tAssociativity : %d\n" \
+ "\tLine size : %d bytes\n" \
+ "\tStride : %d bytes\n",
+ cci.pcci_assoc,
+ 1<<cci.pcci_line_size,
+ 1<<cci.pcci_stride);
+ if (j == 1)
+ p += sprintf(p, "\tStore latency : N/A\n");
+ else
+ p += sprintf(p, "\tStore latency : %d cycle(s)\n",
+ cci.pcci_st_latency);
+
+ p += sprintf(p, "\tLoad latency : %d cycle(s)\n" \
+ "\tStore hints : ",
+ cci.pcci_ld_latency);
+
+ for(k=0; k < 8; k++ ) {
+ if ( cci.pcci_st_hints & 0x1) p += sprintf(p, "[%s]", cache_st_hints[k]);
+ cci.pcci_st_hints >>=1;
+ }
+ p += sprintf(p, "\n\tLoad hints : ");
+
+ for(k=0; k < 8; k++ ) {
+ if ( cci.pcci_ld_hints & 0x1) p += sprintf(p, "[%s]", cache_ld_hints[k]);
+ cci.pcci_ld_hints >>=1;
+ }
+ p += sprintf(p, "\n\tAlias boundary : %d byte(s)\n" \
+ "\tTag LSB : %d\n" \
+ "\tTag MSB : %d\n",
+ 1<<cci.pcci_alias_boundary,
+ cci.pcci_tag_lsb,
+ cci.pcci_tag_msb);
+
+ /* when unified, data(j=2) is enough */
+ if (cci.pcci_unified) break;
+ }
+ }
+ return p - page;
+}
+
+
+static int
+vm_info(char *page)
+{
+ char *p = page;
+ u64 tr_pages =0, vw_pages=0, tc_pages;
+ u64 attrib;
+ pal_vm_info_1_u_t vm_info_1;
+ pal_vm_info_2_u_t vm_info_2;
+ pal_tc_info_u_t tc_info;
+ ia64_ptce_info_t ptce;
+ int i, j;
+ s64 status;
+
+ if ((status=ia64_pal_vm_summary(&vm_info_1, &vm_info_2)) !=0) {
+ printk("ia64_pal_vm_summary=%ld\n", status);
+ return 0;
+ }
+
+
+ p += sprintf(p, "Physical Address Space : %d bits\n" \
+ "Virtual Address Space : %d bits\n" \
+ "Protection Key Registers(PKR) : %d\n" \
+ "Implemented bits in PKR.key : %d\n" \
+ "Hash Tag ID : 0x%x\n" \
+ "Size of RR.rid : %d\n",
+ vm_info_1.pal_vm_info_1_s.phys_add_size,
+ vm_info_2.pal_vm_info_2_s.impl_va_msb+1,
+ vm_info_1.pal_vm_info_1_s.max_pkr+1,
+ vm_info_1.pal_vm_info_1_s.key_size,
+ vm_info_1.pal_vm_info_1_s.hash_tag_id,
+ vm_info_2.pal_vm_info_2_s.rid_size);
+
+ if (ia64_pal_mem_attrib(&attrib) != 0) return 0;
+
+ p += sprintf(p, "Supported memory attributes : %s\n", mem_attrib[attrib&0x7]);
+
+ if ((status=ia64_pal_vm_page_size(&tr_pages, &vw_pages)) !=0) {
+ printk("ia64_pal_vm_page_size=%ld\n", status);
+ return 0;
+ }
+
+ p += sprintf(p, "\nTLB walker : %s implemented\n" \
+ "Number of DTR : %d\n" \
+ "Number of ITR : %d\n" \
+ "TLB insertable page sizes : ",
+ vm_info_1.pal_vm_info_1_s.vw ? "\b":"not",
+ vm_info_1.pal_vm_info_1_s.max_dtr_entry+1,
+ vm_info_1.pal_vm_info_1_s.max_itr_entry+1);
+
+
+ p = bitvector_process(p, tr_pages);
+
+ p += sprintf(p, "\nTLB purgeable page sizes : ");
+
+ p = bitvector_process(p, vw_pages);
+
+ if ((status=ia64_get_ptce(&ptce)) != 0) {
+ printk("ia64_get_ptce=%ld\n",status);
+ return 0;
+ }
+
+ p += sprintf(p, "\nPurge base address : 0x%016lx\n" \
+ "Purge outer loop count : %d\n" \
+ "Purge inner loop count : %d\n" \
+ "Purge outer loop stride : %d\n" \
+ "Purge inner loop stride : %d\n",
+ ptce.base,
+ ptce.count[0],
+ ptce.count[1],
+ ptce.stride[0],
+ ptce.stride[1]);
+
+ p += sprintf(p, "TC Levels : %d\n" \
+ "Unique TC(s) : %d\n",
+ vm_info_1.pal_vm_info_1_s.num_tc_levels,
+ vm_info_1.pal_vm_info_1_s.max_unique_tcs);
+
+ for(i=0; i < vm_info_1.pal_vm_info_1_s.num_tc_levels; i++) {
+ for (j=2; j>0 ; j--) {
+ tc_pages = 0; /* just in case */
+
+
+ /* even without unification, some levels may not be present */
+ if ((status=ia64_pal_vm_info(i,j, &tc_info, &tc_pages)) != 0) {
+ continue;
+ }
+
+ p += sprintf(p, "\n%s Translation Cache Level %d:\n" \
+ "\tHash sets : %d\n" \
+ "\tAssociativity : %d\n" \
+ "\tNumber of entries : %d\n" \
+ "\tFlags : ",
+ cache_types[j+tc_info.tc_unified], i+1,
+ tc_info.tc_num_sets,
+ tc_info.tc_associativity,
+ tc_info.tc_num_entries);
+
+ if (tc_info.tc_pf) p += sprintf(p, "PreferredPageSizeOptimized ");
+ if (tc_info.tc_unified) p += sprintf(p, "Unified ");
+ if (tc_info.tc_reduce_tr) p += sprintf(p, "TCReduction");
+
+ p += sprintf(p, "\n\tSupported page sizes: ");
+
+ p = bitvector_process(p, tc_pages);
+
+ /* when unified date (j=2) is enough */
+ if (tc_info.tc_unified) break;
+ }
+ }
+ p += sprintf(p, "\n");
+
+ return p - page;
+}
+
+
+static int
+register_info(char *page)
+{
+ char *p = page;
+ u64 reg_info[2];
+ u64 info;
+ u64 phys_stacked;
+ pal_hints_u_t hints;
+ u64 iregs, dregs;
+ char *info_type[]={
+ "Implemented AR(s)",
+ "AR(s) with read side-effects",
+ "Implemented CR(s)",
+ "CR(s) with read side-effects",
+ };
+
+ for(info=0; info < 4; info++) {
+
+ if (ia64_pal_register_info(info, &reg_info[0], &reg_info[1]) != 0) return 0;
+
+ p += sprintf(p, "%-32s : ", info_type[info]);
+
+ p = bitregister_process(p, reg_info, 128);
+
+ p += sprintf(p, "\n");
+ }
+
+ if (ia64_pal_rse_info(&phys_stacked, &hints) != 0) return 0;
+
+ p += sprintf(p, "RSE stacked physical registers : %ld\n" \
+ "RSE load/store hints : %ld (%s)\n",
+ phys_stacked,
+ hints.ph_data,
+ hints.ph_data < RSE_HINTS_COUNT ? rse_hints[hints.ph_data]: "(??)");
+
+ if (ia64_pal_debug_info(&iregs, &dregs)) return 0;
+
+ p += sprintf(p, "Instruction debug register pairs : %ld\n" \
+ "Data debug register pairs : %ld\n",
+ iregs, dregs);
+
+ return p - page;
+}
+
+static const char *proc_features[]={
+ NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+ NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,
+ NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
+ NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,
+ NULL,NULL,NULL,NULL,NULL,
+ "XIP,XPSR,XFS implemented",
+ "XR1-XR3 implemented",
+ "Disable dynamic predicate prediction",
+ "Disable processor physical number",
+ "Disable dynamic data cache prefetch",
+ "Disable dynamic inst cache prefetch",
+ "Disable dynamic branch prediction",
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ "Disable BINIT on processor time-out",
+ "Disable dynamic power management (DPM)",
+ "Disable coherency",
+ "Disable cache",
+ "Enable CMCI promotion",
+ "Enable MCA to BINIT promotion",
+ "Enable MCA promotion",
+ "Enable BEER promotion"
+};
+
+
+static int
+processor_info(char *page)
+{
+ char *p = page;
+ const char **v = proc_features;
+ u64 avail=1, status=1, control=1;
+ int i;
+ s64 ret;
+
+ /* must be in physical mode */
+ if ((ret=ia64_pal_proc_get_features(&avail, &status, &control)) != 0) return 0;
+
+ for(i=0; i < 64; i++, v++,avail >>=1, status >>=1, control >>=1) {
+ if ( ! *v ) continue;
+ p += sprintf(p, "%-40s : %s%s %s\n", *v,
+ avail & 0x1 ? "" : "NotImpl",
+ avail & 0x1 ? (status & 0x1 ? "On" : "Off"): "",
+ avail & 0x1 ? (control & 0x1 ? "Ctrl" : "NoCtrl"): "");
+ }
+ return p - page;
+}
+
+/*
+ * physical mode call for PAL_VERSION is working fine.
+ * This function is meant to go away once PAL get fixed.
+ */
+static inline s64
+ia64_pal_version_phys(pal_version_u_t *pal_min_version, pal_version_u_t *pal_cur_version)
+{
+ struct ia64_pal_retval iprv;
+ PAL_CALL_PHYS(iprv, PAL_VERSION, 0, 0, 0);
+ if (pal_min_version)
+ pal_min_version->pal_version_val = iprv.v0;
+ if (pal_cur_version)
+ pal_cur_version->pal_version_val = iprv.v1;
+ return iprv.status;
+}
+
+static int
+version_info(char *page)
+{
+ s64 status;
+ pal_version_u_t min_ver, cur_ver;
+ char *p = page;
+
+#ifdef IA64_PAL_VERSION_BUG
+ /* The virtual mode call is buggy. But the physical mode call seems
+ * to be ok. Until they fix virtual mode, we do physical.
+ */
+ status = ia64_pal_version_phys(&min_ver, &cur_ver);
+#else
+ /* The system crashes if you enable this code with the wrong PAL
+ * code
+ */
+ status = ia64_pal_version(&min_ver, &cur_ver);
+#endif
+ if (status != 0) return 0;
+
+ p += sprintf(p, "PAL_vendor : 0x%x (min=0x%x)\n" \
+ "PAL_A revision : 0x%x (min=0x%x)\n" \
+ "PAL_A model : 0x%x (min=0x%x)\n" \
+ "PAL_B mode : 0x%x (min=0x%x)\n" \
+ "PAL_B revision : 0x%x (min=0x%x)\n",
+ cur_ver.pal_version_s.pv_pal_vendor,
+ min_ver.pal_version_s.pv_pal_vendor,
+ cur_ver.pal_version_s.pv_pal_a_rev,
+ cur_ver.pal_version_s.pv_pal_a_rev,
+ cur_ver.pal_version_s.pv_pal_a_model,
+ min_ver.pal_version_s.pv_pal_a_model,
+ cur_ver.pal_version_s.pv_pal_b_rev,
+ min_ver.pal_version_s.pv_pal_b_rev,
+ cur_ver.pal_version_s.pv_pal_b_model,
+ min_ver.pal_version_s.pv_pal_b_model);
+
+ return p - page;
+}
+
+static int
+perfmon_info(char *page)
+{
+ char *p = page;
+ u64 *pm_buffer;
+ pal_perf_mon_info_u_t pm_info;
+
+ pm_buffer = (u64 *)get_palcall_buffer();
+ if (pm_buffer == 0) return 0;
+
+ if (ia64_pal_perf_mon_info(pm_buffer, &pm_info) != 0) {
+ free_palcall_buffer(pm_buffer);
+ return 0;
+ }
+
+#ifdef IA64_PAL_PERF_MON_INFO_BUG
+ pm_buffer[5]=0x3;
+ pm_info.pal_perf_mon_info_s.cycles = 0x12;
+ pm_info.pal_perf_mon_info_s.retired = 0x08;
+#endif
+
+ p += sprintf(p, "PMC/PMD pairs : %d\n" \
+ "Counter width : %d bits\n" \
+ "Cycle event number : %d\n" \
+ "Retired event number : %d\n" \
+ "Implemented PMC : ",
+ pm_info.pal_perf_mon_info_s.generic,
+ pm_info.pal_perf_mon_info_s.width,
+ pm_info.pal_perf_mon_info_s.cycles,
+ pm_info.pal_perf_mon_info_s.retired);
+
+ p = bitregister_process(p, pm_buffer, 256);
+
+ p += sprintf(p, "\nImplemented PMD : ");
+
+ p = bitregister_process(p, pm_buffer+4, 256);
+
+ p += sprintf(p, "\nCycles count capable : ");
+
+ p = bitregister_process(p, pm_buffer+8, 256);
+
+ p += sprintf(p, "\nRetired bundles count capable : ");
+
+ p = bitregister_process(p, pm_buffer+12, 256);
+
+ p += sprintf(p, "\n");
+
+ free_palcall_buffer(pm_buffer);
+
+ return p - page;
+}
+
+static int
+frequency_info(char *page)
+{
+ char *p = page;
+ struct pal_freq_ratio proc, itc, bus;
+ u64 base;
+
+ if (ia64_pal_freq_base(&base) == -1)
+ p += sprintf(p, "Output clock : not implemented\n");
+ else
+ p += sprintf(p, "Output clock : %ld ticks/s\n", base);
+
+ if (ia64_pal_freq_ratios(&proc, &bus, &itc) != 0) return 0;
+
+ p += sprintf(p, "Processor/Clock ratio : %ld/%ld\n" \
+ "Bus/Clock ratio : %ld/%ld\n" \
+ "ITC/Clock ratio : %ld/%ld\n",
+ proc.num, proc.den,
+ bus.num, bus.den,
+ itc.num, itc.den);
+
+ return p - page;
+}
+
+
+/*
+ * Entry point routine: all calls go trhough this function
+ */
+static int
+palinfo_read_entry(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ palinfo_func_t info = (palinfo_func_t)data;
+ int len = info(page);
+
+ if (len <= off+count) *eof = 1;
+
+ *start = page + off;
+ len -= off;
+
+ if (len>count) len = count;
+ if (len<0) len = 0;
+
+ return len;
+}
+
+/*
+ * List names,function pairs for every entry in /proc/palinfo
+ * Must be terminated with the NULL,NULL entry.
+ */
+static palinfo_entry_t palinfo_entries[]={
+ { "version_info", version_info, },
+ { "vm_info", vm_info, },
+ { "cache_info", cache_info, },
+ { "power_info", power_info, },
+ { "register_info", register_info, },
+ { "processor_info", processor_info, },
+ { "perfmon_info", perfmon_info, },
+ { "frequency_info", frequency_info, },
+ { NULL, NULL,}
+};
+
+
+static int __init
+palinfo_init(void)
+{
+ palinfo_entry_t *p;
+
+ printk(KERN_INFO "PAL Information Facility v%s\n", PALINFO_VERSION);
+
+ palinfo_dir = create_proc_entry("palinfo", S_IFDIR | S_IRUGO | S_IXUGO, NULL);
+
+ for (p = palinfo_entries; p->name ; p++){
+ p->entry = create_proc_read_entry (p->name, 0, palinfo_dir,
+ palinfo_read_entry, p->proc_read);
+ }
+
+ return 0;
+}
+
+static int __exit
+palinfo_exit(void)
+{
+ palinfo_entry_t *p;
+
+ for (p = palinfo_entries; p->name ; p++){
+ remove_proc_entry (p->name, palinfo_dir);
+ }
+ remove_proc_entry ("palinfo", 0);
+
+ return 0;
+}
+
+module_init(palinfo_init);
+module_exit(palinfo_exit);
diff --git a/arch/ia64/kernel/pci-dma.c b/arch/ia64/kernel/pci-dma.c
index 0bc110510..ab86e69b3 100644
--- a/arch/ia64/kernel/pci-dma.c
+++ b/arch/ia64/kernel/pci-dma.c
@@ -23,8 +23,8 @@ pci_alloc_consistent (struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle
void *ret;
int gfp = GFP_ATOMIC;
- if (!hwdev || hwdev->dma_mask != 0xffffffff)
- gfp |= GFP_DMA;
+ if (!hwdev || hwdev->dma_mask == 0xffffffff)
+ gfp |= GFP_DMA; /* XXX fix me: should change this to GFP_32BIT or ZONE_32BIT */
ret = (void *)__get_free_pages(gfp, get_order(size));
if (ret) {
diff --git a/arch/ia64/kernel/pci.c b/arch/ia64/kernel/pci.c
index 767cfa5ce..2d814b443 100644
--- a/arch/ia64/kernel/pci.c
+++ b/arch/ia64/kernel/pci.c
@@ -133,7 +133,7 @@ pci_find_bios(void)
* Initialization. Uses the SAL interface
*/
-#define PCI_BUSSES_TO_SCAN 2 /* On "real" ;) hardware this will be 255 */
+#define PCI_BUSES_TO_SCAN 255
void __init
pcibios_init(void)
@@ -147,7 +147,7 @@ pcibios_init(void)
}
printk("PCI: Probing PCI hardware\n");
- for (i = 0; i < PCI_BUSSES_TO_SCAN; i++)
+ for (i = 0; i < PCI_BUSES_TO_SCAN; i++)
pci_scan_bus(i, ops, NULL);
platform_pci_fixup();
return;
@@ -197,7 +197,7 @@ pcibios_fixup_pbus_ranges (struct pci_bus * bus, struct pbus_set_ranges_data * r
ranges->mem_end -= bus->resource[1]->start;
}
-int __init
+int
pcibios_enable_device (struct pci_dev *dev)
{
/* Not needed, since we enable all devices at startup. */
diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c
index a8c217b9a..58ad3c21c 100644
--- a/arch/ia64/kernel/process.c
+++ b/arch/ia64/kernel/process.c
@@ -23,8 +23,40 @@
#include <asm/processor.h>
#include <asm/sal.h>
#include <asm/uaccess.h>
+#include <asm/unwind.h>
#include <asm/user.h>
+static void
+do_show_stack (struct unw_frame_info *info, void *arg)
+{
+ unsigned long ip, sp, bsp;
+
+ printk("\nCall Trace: ");
+ do {
+ unw_get_ip(info, &ip);
+ if (ip == 0)
+ break;
+
+ unw_get_sp(info, &sp);
+ unw_get_bsp(info, &bsp);
+ printk("[<%016lx>] sp=0x%016lx bsp=0x%016lx\n", ip, sp, bsp);
+ } while (unw_unwind(info) >= 0);
+}
+
+void
+show_stack (struct task_struct *task)
+{
+#ifdef CONFIG_IA64_NEW_UNWIND
+ if (!task)
+ unw_init_running(do_show_stack, 0);
+ else {
+ struct unw_frame_info info;
+
+ unw_init_from_blocked_task(&info, task);
+ do_show_stack(&info, 0);
+ }
+#endif
+}
void
show_regs (struct pt_regs *regs)
@@ -71,6 +103,10 @@ show_regs (struct pt_regs *regs)
((i == sof - 1) || (i % 3) == 2) ? "\n" : " ");
}
}
+#ifdef CONFIG_IA64_NEW_UNWIND
+ if (!user_mode(regs))
+ show_stack(0);
+#endif
}
void __attribute__((noreturn))
@@ -98,16 +134,49 @@ cpu_idle (void *unused)
if (pm_idle)
(*pm_idle)();
#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC
- if (ia64_get_itm() < ia64_get_itc()) {
- extern void ia64_reset_itm (void);
-
- printk("cpu_idle: ITM in past, resetting it...\n");
- ia64_reset_itm();
+ local_irq_disable();
+ {
+ u64 itc, itm;
+
+ itc = ia64_get_itc();
+ itm = ia64_get_itm();
+ if (time_after(itc, itm + 1000)) {
+ extern void ia64_reset_itm (void);
+
+ printk("cpu_idle: ITM in past (itc=%lx,itm=%lx:%lums)\n",
+ itc, itm, (itc - itm)/500000);
+ ia64_reset_itm();
+ }
}
+ local_irq_enable();
#endif
}
}
+void
+ia64_save_extra (struct task_struct *task)
+{
+ extern void ia64_save_debug_regs (unsigned long *save_area);
+ extern void ia32_save_state (struct thread_struct *thread);
+
+ if ((task->thread.flags & IA64_THREAD_DBG_VALID) != 0)
+ ia64_save_debug_regs(&task->thread.dbr[0]);
+ if (IS_IA32_PROCESS(ia64_task_regs(task)))
+ ia32_save_state(&task->thread);
+}
+
+void
+ia64_load_extra (struct task_struct *task)
+{
+ extern void ia64_load_debug_regs (unsigned long *save_area);
+ extern void ia32_load_state (struct thread_struct *thread);
+
+ if ((task->thread.flags & IA64_THREAD_DBG_VALID) != 0)
+ ia64_load_debug_regs(&task->thread.dbr[0]);
+ if (IS_IA32_PROCESS(ia64_task_regs(task)))
+ ia32_load_state(&task->thread);
+}
+
/*
* Copy the state of an ia-64 thread.
*
@@ -234,9 +303,103 @@ copy_thread (int nr, unsigned long clone_flags, unsigned long usp,
return 0;
}
+#ifdef CONFIG_IA64_NEW_UNWIND
+
+void
+do_copy_regs (struct unw_frame_info *info, void *arg)
+{
+ unsigned long ar_bsp, ndirty, *krbs, addr, mask, sp, nat_bits = 0, ip;
+ elf_greg_t *dst = arg;
+ struct pt_regs *pt;
+ char nat;
+ long val;
+ 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);
+
+ krbs = (unsigned long *) current + IA64_RBS_OFFSET/8;
+ ndirty = ia64_rse_num_regs(krbs, krbs + (pt->loadrs >> 19));
+ ar_bsp = (unsigned long) ia64_rse_skip_regs((long *) pt->ar_bspstore, ndirty);
+
+ /*
+ * Write portion of RSE backing store living on the kernel
+ * stack to the VM of the process.
+ */
+ for (addr = pt->ar_bspstore; addr < ar_bsp; addr += 8)
+ if (ia64_peek(pt, current, addr, &val) == 0)
+ access_process_vm(current, addr, &val, sizeof(val), 1);
+
+ /* 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] = pt->cr_ifs & 0x3fffffffff;
+ 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] = ar_bsp;
+ dst[47] = pt->ar_bspstore;
+ unw_get_ar(info, UNW_AR_RNAT, &dst[48]);
+ 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]);
+}
+
+void
+do_dump_fpu (struct unw_frame_info *info, void *arg)
+{
+ struct task_struct *fpu_owner = ia64_get_fpu_owner();
+ 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);
+
+ if ((fpu_owner == current) || (current->thread.flags & IA64_THREAD_FPH_VALID)) {
+ ia64_sync_fph(current);
+ memcpy(dst + 32, current->thread.fph, 96*16);
+ }
+}
+
+#endif /* CONFIG_IA64_NEW_UNWIND */
+
void
ia64_elf_core_copy_regs (struct pt_regs *pt, elf_gregset_t dst)
{
+#ifdef CONFIG_IA64_NEW_UNWIND
+ unw_init_running(do_copy_regs, dst);
+#else
struct switch_stack *sw = ((struct switch_stack *) pt) - 1;
unsigned long ar_ec, cfm, ar_bsp, ndirty, *krbs, addr;
@@ -270,7 +433,7 @@ ia64_elf_core_copy_regs (struct pt_regs *pt, elf_gregset_t dst)
* ar.rsc ar.bsp ar.bspstore ar.rnat
* ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec
*/
- memset(dst, 0, sizeof (dst)); /* don't leak any "random" bits */
+ memset(dst, 0, sizeof(dst)); /* don't leak any "random" bits */
/* r0 is zero */ dst[ 1] = pt->r1; dst[ 2] = pt->r2; dst[ 3] = pt->r3;
dst[ 4] = sw->r4; dst[ 5] = sw->r5; dst[ 6] = sw->r6; dst[ 7] = sw->r7;
@@ -285,17 +448,22 @@ ia64_elf_core_copy_regs (struct pt_regs *pt, elf_gregset_t dst)
dst[34] = pt->b0; dst[35] = sw->b1; dst[36] = sw->b2; dst[37] = sw->b3;
dst[38] = sw->b4; dst[39] = sw->b5; dst[40] = pt->b6; dst[41] = pt->b7;
- dst[42] = pt->cr_iip; dst[43] = pt->cr_ifs;
- dst[44] = pt->cr_ipsr; /* XXX perhaps we should filter out some bits here? --davidm */
+ dst[42] = pt->cr_iip + ia64_psr(pt)->ri;
+ dst[43] = pt->cr_ifs;
+ dst[44] = pt->cr_ipsr & IA64_PSR_UM;
dst[45] = pt->ar_rsc; dst[46] = ar_bsp; dst[47] = pt->ar_bspstore; dst[48] = pt->ar_rnat;
dst[49] = pt->ar_ccv; dst[50] = pt->ar_unat; dst[51] = sw->ar_fpsr; dst[52] = pt->ar_pfs;
dst[53] = sw->ar_lc; dst[54] = (sw->ar_pfs >> 52) & 0x3f;
+#endif /* !CONFIG_IA64_NEW_UNWIND */
}
int
dump_fpu (struct pt_regs *pt, elf_fpregset_t dst)
{
+#ifdef CONFIG_IA64_NEW_UNWIND
+ unw_init_running(do_dump_fpu, dst);
+#else
struct switch_stack *sw = ((struct switch_stack *) pt) - 1;
struct task_struct *fpu_owner = ia64_get_fpu_owner();
@@ -312,6 +480,7 @@ dump_fpu (struct pt_regs *pt, elf_fpregset_t dst)
}
memcpy(dst + 32, current->thread.fph, 96*16);
}
+#endif
return 1; /* f0-f31 are always valid so we always return 1 */
}
@@ -384,7 +553,7 @@ release_thread (struct task_struct *dead_task)
unsigned long
get_wchan (struct task_struct *p)
{
- struct ia64_frame_info info;
+ struct unw_frame_info info;
unsigned long ip;
int count = 0;
/*
@@ -403,11 +572,11 @@ get_wchan (struct task_struct *p)
* gracefully if the process wasn't really blocked after all.
* --davidm 99/12/15
*/
- ia64_unwind_init_from_blocked_task(&info, p);
+ unw_init_from_blocked_task(&info, p);
do {
- if (ia64_unwind_to_previous_frame(&info) < 0)
+ if (unw_unwind(&info) < 0)
return 0;
- ip = ia64_unwind_get_ip(&info);
+ unw_get_ip(&info, &ip);
if (ip < first_sched || ip >= last_sched)
return ip;
} while (count++ < 16);
diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c
index 22ed4f569..0efd42bb8 100644
--- a/arch/ia64/kernel/ptrace.c
+++ b/arch/ia64/kernel/ptrace.c
@@ -7,6 +7,7 @@
* Derived from the x86 and Alpha versions. Most of the code in here
* could actually be factored into a common set of routines.
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
@@ -29,8 +30,74 @@
* id (instruction debug fault disable; one bit)
* dd (data debug fault disable; one bit)
* ri (restart instruction; two bits)
+ * is (instruction set; one bit)
*/
-#define CR_IPSR_CHANGE_MASK 0x06a00100003eUL
+#define IPSR_WRITE_MASK \
+ (IA64_PSR_UM | IA64_PSR_DB | IA64_PSR_IS | IA64_PSR_ID | IA64_PSR_DD | IA64_PSR_RI)
+#define IPSR_READ_MASK IPSR_WRITE_MASK
+
+#ifdef CONFIG_IA64_NEW_UNWIND
+
+#define PTRACE_DEBUG 1
+
+#if PTRACE_DEBUG
+# define dprintk(format...) printk(format)
+# define inline
+#else
+# define dprintk(format...)
+#endif
+
+/*
+ * Collect the NaT bits for r1-r31 from scratch_unat and return a NaT
+ * bitset where bit i is set iff the NaT bit of register i is set.
+ */
+unsigned long
+ia64_get_scratch_nat_bits (struct pt_regs *pt, unsigned long scratch_unat)
+{
+# define GET_BITS(first, last, unat) \
+ ({ \
+ unsigned long bit = ia64_unat_pos(&pt->r##first); \
+ unsigned long mask = ((1UL << (last - first + 1)) - 1) << first; \
+ (ia64_rotl(unat, first) >> bit) & mask; \
+ })
+ unsigned long val;
+
+ val = GET_BITS( 1, 3, scratch_unat);
+ val |= GET_BITS(12, 15, scratch_unat);
+ val |= GET_BITS( 8, 11, scratch_unat);
+ val |= GET_BITS(16, 31, scratch_unat);
+ return val;
+
+# undef GET_BITS
+}
+
+/*
+ * Set the NaT bits for the scratch registers according to NAT and
+ * return the resulting unat (assuming the scratch registers are
+ * stored in PT).
+ */
+unsigned long
+ia64_put_scratch_nat_bits (struct pt_regs *pt, unsigned long nat)
+{
+ unsigned long scratch_unat;
+
+# define PUT_BITS(first, last, nat) \
+ ({ \
+ unsigned long bit = ia64_unat_pos(&pt->r##first); \
+ unsigned long mask = ((1UL << (last - first + 1)) - 1) << bit; \
+ (ia64_rotr(nat, first) << bit) & mask; \
+ })
+ scratch_unat = PUT_BITS( 1, 3, nat);
+ scratch_unat |= PUT_BITS(12, 15, nat);
+ scratch_unat |= PUT_BITS( 8, 11, nat);
+ scratch_unat |= PUT_BITS(16, 31, nat);
+
+ return scratch_unat;
+
+# undef PUT_BITS
+}
+
+#else /* !CONFIG_IA64_NEW_UNWIND */
/*
* Collect the NaT bits for r1-r31 from sw->caller_unat and
@@ -79,28 +146,26 @@ ia64_put_nat_bits (struct pt_regs *pt, struct switch_stack *sw, unsigned long na
# undef PUT_BITS
}
-#define IA64_MLI_TEMPLATE 0x2
+#endif /* !CONFIG_IA64_NEW_UNWIND */
+
+#define IA64_MLX_TEMPLATE 0x2
#define IA64_MOVL_OPCODE 6
void
ia64_increment_ip (struct pt_regs *regs)
{
- unsigned long w0, w1, ri = ia64_psr(regs)->ri + 1;
+ unsigned long w0, ri = ia64_psr(regs)->ri + 1;
if (ri > 2) {
ri = 0;
regs->cr_iip += 16;
} else if (ri == 2) {
get_user(w0, (char *) regs->cr_iip + 0);
- get_user(w1, (char *) regs->cr_iip + 8);
- if (((w0 >> 1) & 0xf) == IA64_MLI_TEMPLATE && (w1 >> 60) == IA64_MOVL_OPCODE) {
+ if (((w0 >> 1) & 0xf) == IA64_MLX_TEMPLATE) {
/*
- * rfi'ing to slot 2 of an MLI bundle causes
+ * rfi'ing to slot 2 of an MLX bundle causes
* an illegal operation fault. We don't want
- * that to happen... Note that we check the
- * opcode only. "movl" has a vc bit of 0, but
- * since a vc bit of 1 is currently reserved,
- * we might just as well treat it like a movl.
+ * that to happen...
*/
ri = 0;
regs->cr_iip += 16;
@@ -112,21 +177,17 @@ ia64_increment_ip (struct pt_regs *regs)
void
ia64_decrement_ip (struct pt_regs *regs)
{
- unsigned long w0, w1, ri = ia64_psr(regs)->ri - 1;
+ unsigned long w0, ri = ia64_psr(regs)->ri - 1;
if (ia64_psr(regs)->ri == 0) {
regs->cr_iip -= 16;
ri = 2;
get_user(w0, (char *) regs->cr_iip + 0);
- get_user(w1, (char *) regs->cr_iip + 8);
- if (((w0 >> 1) & 0xf) == IA64_MLI_TEMPLATE && (w1 >> 60) == IA64_MOVL_OPCODE) {
+ if (((w0 >> 1) & 0xf) == IA64_MLX_TEMPLATE) {
/*
- * rfi'ing to slot 2 of an MLI bundle causes
+ * rfi'ing to slot 2 of an MLX bundle causes
* an illegal operation fault. We don't want
- * that to happen... Note that we check the
- * opcode only. "movl" has a vc bit of 0, but
- * since a vc bit of 1 is currently reserved,
- * we might just as well treat it like a movl.
+ * that to happen...
*/
ri = 1;
}
@@ -291,7 +352,11 @@ ia64_peek (struct pt_regs *regs, struct task_struct *child, unsigned long addr,
laddr = (unsigned long *) addr;
child_regs = ia64_task_regs(child);
+#ifdef CONFIG_IA64_NEW_UNWIND
+ child_stack = (struct switch_stack *) (child->thread.ksp + 16);
+#else
child_stack = (struct switch_stack *) child_regs - 1;
+#endif
bspstore = (unsigned long *) child_regs->ar_bspstore;
krbs = (unsigned long *) child + IA64_RBS_OFFSET/8;
krbs_num_regs = ia64_rse_num_regs(krbs, (unsigned long *) child_stack->ar_bspstore);
@@ -335,7 +400,11 @@ ia64_poke (struct pt_regs *regs, struct task_struct *child, unsigned long addr,
laddr = (unsigned long *) addr;
child_regs = ia64_task_regs(child);
+#ifdef CONFIG_IA64_NEW_UNWIND
+ child_stack = (struct switch_stack *) (child->thread.ksp + 16);
+#else
child_stack = (struct switch_stack *) child_regs - 1;
+#endif
bspstore = (unsigned long *) child_regs->ar_bspstore;
krbs = (unsigned long *) child + IA64_RBS_OFFSET/8;
krbs_num_regs = ia64_rse_num_regs(krbs, (unsigned long *) child_stack->ar_bspstore);
@@ -394,21 +463,43 @@ sync_kernel_register_backing_store (struct task_struct *child,
long new_bsp,
int force_loadrs_to_zero)
{
- unsigned long *krbs, bspstore, bsp, krbs_num_regs, rbs_end, addr, val;
- long ndirty, ret;
- struct pt_regs *child_regs;
+ unsigned long *krbs, bspstore, *kbspstore, bsp, rbs_end, addr, val;
+ long ndirty, ret = 0;
+ struct pt_regs *child_regs = ia64_task_regs(child);
+
+#ifdef CONFIG_IA64_NEW_UNWIND
+ struct unw_frame_info info;
+ unsigned long cfm, sof;
+
+ unw_init_from_blocked_task(&info, child);
+ if (unw_unwind_to_user(&info) < 0)
+ return -1;
+
+ unw_get_bsp(&info, (unsigned long *) &kbspstore);
+
+ krbs = (unsigned long *) child + IA64_RBS_OFFSET/8;
+ ndirty = ia64_rse_num_regs(krbs, krbs + (child_regs->loadrs >> 19));
+ bspstore = child_regs->ar_bspstore;
+ bsp = (long) ia64_rse_skip_regs((long *)bspstore, ndirty);
+
+ cfm = child_regs->cr_ifs;
+ if (!(cfm & (1UL << 63)))
+ unw_get_cfm(&info, &cfm);
+ sof = (cfm & 0x7f);
+ rbs_end = (long) ia64_rse_skip_regs((long *)bspstore, sof);
+#else
struct switch_stack *child_stack;
+ unsigned long krbs_num_regs;
- ret = 0;
- child_regs = ia64_task_regs(child);
child_stack = (struct switch_stack *) child_regs - 1;
-
+ kbspstore = (unsigned long *) child_stack->ar_bspstore;
krbs = (unsigned long *) child + IA64_RBS_OFFSET/8;
ndirty = ia64_rse_num_regs(krbs, krbs + (child_regs->loadrs >> 19));
bspstore = child_regs->ar_bspstore;
bsp = (long) ia64_rse_skip_regs((long *)bspstore, ndirty);
- krbs_num_regs = ia64_rse_num_regs(krbs, (unsigned long *) child_stack->ar_bspstore);
+ krbs_num_regs = ia64_rse_num_regs(krbs, kbspstore);
rbs_end = (long) ia64_rse_skip_regs((long *)bspstore, krbs_num_regs);
+#endif
/* Return early if nothing to do */
if (bsp == new_bsp)
@@ -437,13 +528,15 @@ sync_kernel_register_backing_store (struct task_struct *child,
}
static void
-sync_thread_rbs (struct task_struct *child, int make_writable)
+sync_thread_rbs (struct task_struct *child, struct mm_struct *mm, int make_writable)
{
struct task_struct *p;
read_lock(&tasklist_lock);
- for_each_task(p) {
- if (p->mm == child->mm && p->state != TASK_RUNNING)
- sync_kernel_register_backing_store(p, 0, make_writable);
+ {
+ for_each_task(p) {
+ if (p->mm == mm && p->state != TASK_RUNNING)
+ sync_kernel_register_backing_store(p, 0, make_writable);
+ }
}
read_unlock(&tasklist_lock);
child->thread.flags |= IA64_THREAD_KRBS_SYNCED;
@@ -452,10 +545,11 @@ sync_thread_rbs (struct task_struct *child, int make_writable)
/*
* Ensure the state in child->thread.fph is up-to-date.
*/
-static void
-sync_fph (struct task_struct *child)
+void
+ia64_sync_fph (struct task_struct *child)
{
if (ia64_psr(ia64_task_regs(child))->mfh && ia64_get_fpu_owner() == child) {
+ ia64_set_fpu_owner(0);
ia64_save_fpu(&child->thread.fph[0]);
child->thread.flags |= IA64_THREAD_FPH_VALID;
}
@@ -465,15 +559,383 @@ sync_fph (struct task_struct *child)
}
}
+#ifdef CONFIG_IA64_NEW_UNWIND
+
+#include <asm/unwind.h>
+
+static int
+access_fr (struct unw_frame_info *info, int regnum, int hi, unsigned long *data, int write_access)
+{
+ struct ia64_fpreg fpval;
+ int ret;
+
+ ret = unw_get_fr(info, regnum, &fpval);
+ if (ret < 0)
+ return ret;
+
+ if (write_access) {
+ fpval.u.bits[hi] = *data;
+ ret = unw_set_fr(info, regnum, fpval);
+ } else
+ *data = fpval.u.bits[hi];
+ return ret;
+}
+
+static int
+access_uarea (struct task_struct *child, unsigned long addr, unsigned long *data, int write_access)
+{
+ unsigned long *ptr, *rbs, *bspstore, ndirty, regnum;
+ struct switch_stack *sw;
+ struct unw_frame_info info;
+ struct pt_regs *pt;
+
+ pt = ia64_task_regs(child);
+ sw = (struct switch_stack *) (child->thread.ksp + 16);
+
+ if ((addr & 0x7) != 0) {
+ dprintk("ptrace: unaligned register address 0x%lx\n", addr);
+ return -1;
+ }
+
+ if (addr < PT_F127 + 16) {
+ /* accessing fph */
+ ia64_sync_fph(child);
+ ptr = (unsigned long *) ((unsigned long) &child->thread.fph + addr);
+ } else if (addr >= PT_F10 && addr < PT_F15 + 16) {
+ /* scratch registers untouched by kernel (saved in switch_stack) */
+ ptr = (unsigned long *) ((long) sw + addr - PT_NAT_BITS);
+ } else if (addr < PT_AR_LC + 8) {
+ /* preserved state: */
+ unsigned long nat_bits, scratch_unat, dummy = 0;
+ struct unw_frame_info info;
+ char nat = 0;
+ int ret;
+
+ unw_init_from_blocked_task(&info, child);
+ if (unw_unwind_to_user(&info) < 0)
+ return -1;
+
+ switch (addr) {
+ case PT_NAT_BITS:
+ if (write_access) {
+ nat_bits = *data;
+ scratch_unat = ia64_put_scratch_nat_bits(pt, nat_bits);
+ if (unw_set_ar(&info, UNW_AR_UNAT, scratch_unat) < 0) {
+ dprintk("ptrace: failed to set ar.unat\n");
+ return -1;
+ }
+ for (regnum = 4; regnum <= 7; ++regnum) {
+ unw_get_gr(&info, regnum, &dummy, &nat);
+ unw_set_gr(&info, regnum, dummy, (nat_bits >> regnum) & 1);
+ }
+ } else {
+ if (unw_get_ar(&info, UNW_AR_UNAT, &scratch_unat) < 0) {
+ dprintk("ptrace: failed to read ar.unat\n");
+ return -1;
+ }
+ nat_bits = ia64_get_scratch_nat_bits(pt, scratch_unat);
+ for (regnum = 4; regnum <= 7; ++regnum) {
+ unw_get_gr(&info, regnum, &dummy, &nat);
+ nat_bits |= (nat != 0) << regnum;
+ }
+ *data = nat_bits;
+ }
+ return 0;
+
+ case PT_R4: case PT_R5: case PT_R6: case PT_R7:
+ if (write_access) {
+ /* read NaT bit first: */
+ ret = unw_get_gr(&info, (addr - PT_R4)/8 + 4, data, &nat);
+ if (ret < 0)
+ return ret;
+ }
+ return unw_access_gr(&info, (addr - PT_R4)/8 + 4, data, &nat,
+ write_access);
+
+ case PT_B1: case PT_B2: case PT_B3: case PT_B4: case PT_B5:
+ return unw_access_br(&info, (addr - PT_B1)/8 + 1, data, write_access);
+
+ case PT_AR_LC:
+ return unw_access_ar(&info, UNW_AR_LC, data, write_access);
+
+ default:
+ if (addr >= PT_F2 && addr < PT_F5 + 16)
+ return access_fr(&info, (addr - PT_F2)/16 + 2, (addr & 8) != 0,
+ data, write_access);
+ else if (addr >= PT_F16 && addr < PT_F31 + 16)
+ return access_fr(&info, (addr - PT_F16)/16 + 16, (addr & 8) != 0,
+ data, write_access);
+ else {
+ dprintk("ptrace: rejecting access to register address 0x%lx\n",
+ addr);
+ return -1;
+ }
+ }
+ } else if (addr < PT_F9+16) {
+ /* scratch state */
+ switch (addr) {
+ case PT_AR_BSP:
+ if (write_access)
+ /* FIXME? Account for lack of ``cover'' in the syscall case */
+ return sync_kernel_register_backing_store(child, *data, 1);
+ else {
+ rbs = (unsigned long *) child + IA64_RBS_OFFSET/8;
+ bspstore = (unsigned long *) pt->ar_bspstore;
+ ndirty = ia64_rse_num_regs(rbs, rbs + (pt->loadrs >> 19));
+
+ /*
+ * If we're in a system call, no ``cover'' was done. So to
+ * make things uniform, we'll add the appropriate displacement
+ * onto bsp if we're in a system call.
+ */
+ if (!(pt->cr_ifs & (1UL << 63))) {
+ struct unw_frame_info info;
+ unsigned long cfm;
+
+ unw_init_from_blocked_task(&info, child);
+ if (unw_unwind_to_user(&info) < 0)
+ return -1;
+
+ unw_get_cfm(&info, &cfm);
+ ndirty += cfm & 0x7f;
+ }
+ *data = (unsigned long) ia64_rse_skip_regs(bspstore, ndirty);
+ return 0;
+ }
+
+ case PT_CFM:
+ if (pt->cr_ifs & (1UL << 63)) {
+ if (write_access)
+ pt->cr_ifs = ((pt->cr_ifs & ~0x3fffffffffUL)
+ | (*data & 0x3fffffffffUL));
+ else
+ *data = pt->cr_ifs & 0x3fffffffffUL;
+ } else {
+ /* kernel was entered through a system call */
+ unsigned long cfm;
+
+ unw_init_from_blocked_task(&info, child);
+ if (unw_unwind_to_user(&info) < 0)
+ return -1;
+
+ unw_get_cfm(&info, &cfm);
+ if (write_access)
+ unw_set_cfm(&info, ((cfm & ~0x3fffffffffU)
+ | (*data & 0x3fffffffffUL)));
+ else
+ *data = cfm;
+ }
+ return 0;
+
+ case PT_CR_IPSR:
+ if (write_access)
+ pt->cr_ipsr = ((*data & IPSR_WRITE_MASK)
+ | (pt->cr_ipsr & ~IPSR_WRITE_MASK));
+ else
+ *data = (pt->cr_ipsr & IPSR_READ_MASK);
+ return 0;
+
+ case PT_R1: case PT_R2: case PT_R3:
+ case PT_R8: case PT_R9: case PT_R10: case PT_R11:
+ case PT_R12: case PT_R13: case PT_R14: case PT_R15:
+ case PT_R16: case PT_R17: case PT_R18: case PT_R19:
+ case PT_R20: case PT_R21: case PT_R22: case PT_R23:
+ case PT_R24: case PT_R25: case PT_R26: case PT_R27:
+ case PT_R28: case PT_R29: case PT_R30: case PT_R31:
+ case PT_B0: case PT_B6: case PT_B7:
+ case PT_F6: case PT_F6+8: case PT_F7: case PT_F7+8:
+ case PT_F8: case PT_F8+8: case PT_F9: case PT_F9+8:
+ case PT_AR_BSPSTORE:
+ case PT_AR_RSC: case PT_AR_UNAT: case PT_AR_PFS: case PT_AR_RNAT:
+ case PT_AR_CCV: case PT_AR_FPSR: case PT_CR_IIP: case PT_PR:
+ /* scratch register */
+ ptr = (unsigned long *) ((long) pt + addr - PT_CR_IPSR);
+ break;
+
+ default:
+ /* disallow accessing anything else... */
+ dprintk("ptrace: rejecting access to register address 0x%lx\n",
+ addr);
+ return -1;
+ }
+ } else {
+ /* access debug registers */
+
+ if (!(child->thread.flags & IA64_THREAD_DBG_VALID)) {
+ child->thread.flags |= IA64_THREAD_DBG_VALID;
+ memset(child->thread.dbr, 0, sizeof(child->thread.dbr));
+ memset(child->thread.ibr, 0, sizeof( child->thread.ibr));
+ }
+ if (addr >= PT_IBR) {
+ regnum = (addr - PT_IBR) >> 3;
+ ptr = &child->thread.ibr[0];
+ } else {
+ regnum = (addr - PT_DBR) >> 3;
+ ptr = &child->thread.dbr[0];
+ }
+
+ if (regnum >= 8) {
+ dprintk("ptrace: rejecting access to register address 0x%lx\n", addr);
+ return -1;
+ }
+
+ ptr += regnum;
+ }
+ if (write_access)
+ *ptr = *data;
+ else
+ *data = *ptr;
+ return 0;
+}
+
+#else /* !CONFIG_IA64_NEW_UNWIND */
+
+static int
+access_uarea (struct task_struct *child, unsigned long addr, unsigned long *data, int write_access)
+{
+ unsigned long *ptr, *rbs, *bspstore, ndirty, regnum;
+ struct switch_stack *sw;
+ struct pt_regs *pt;
+
+ if ((addr & 0x7) != 0)
+ return -1;
+
+ if (addr < PT_F127+16) {
+ /* accessing fph */
+ ia64_sync_fph(child);
+ ptr = (unsigned long *) ((unsigned long) &child->thread.fph + addr);
+ } else if (addr < PT_F9+16) {
+ /* accessing switch_stack or pt_regs: */
+ pt = ia64_task_regs(child);
+ sw = (struct switch_stack *) pt - 1;
+
+ switch (addr) {
+ case PT_NAT_BITS:
+ if (write_access)
+ ia64_put_nat_bits(pt, sw, *data);
+ else
+ *data = ia64_get_nat_bits(pt, sw);
+ return 0;
+
+ case PT_AR_BSP:
+ if (write_access)
+ /* FIXME? Account for lack of ``cover'' in the syscall case */
+ return sync_kernel_register_backing_store(child, *data, 1);
+ else {
+ rbs = (unsigned long *) child + IA64_RBS_OFFSET/8;
+ bspstore = (unsigned long *) pt->ar_bspstore;
+ ndirty = ia64_rse_num_regs(rbs, rbs + (pt->loadrs >> 19));
+
+ /*
+ * If we're in a system call, no ``cover'' was done. So to
+ * make things uniform, we'll add the appropriate displacement
+ * onto bsp if we're in a system call.
+ */
+ if (!(pt->cr_ifs & (1UL << 63)))
+ ndirty += sw->ar_pfs & 0x7f;
+ *data = (unsigned long) ia64_rse_skip_regs(bspstore, ndirty);
+ return 0;
+ }
+
+ case PT_CFM:
+ if (write_access) {
+ if (pt->cr_ifs & (1UL << 63))
+ pt->cr_ifs = ((pt->cr_ifs & ~0x3fffffffffUL)
+ | (*data & 0x3fffffffffUL));
+ else
+ sw->ar_pfs = ((sw->ar_pfs & ~0x3fffffffffUL)
+ | (*data & 0x3fffffffffUL));
+ return 0;
+ } else {
+ if ((pt->cr_ifs & (1UL << 63)) == 0)
+ *data = sw->ar_pfs;
+ else
+ /* return only the CFM */
+ *data = pt->cr_ifs & 0x3fffffffffUL;
+ return 0;
+ }
+
+ case PT_CR_IPSR:
+ if (write_access)
+ pt->cr_ipsr = ((*data & IPSR_WRITE_MASK)
+ | (pt->cr_ipsr & ~IPSR_WRITE_MASK));
+ else
+ *data = (pt->cr_ipsr & IPSR_READ_MASK);
+ return 0;
+
+ case PT_R1: case PT_R2: case PT_R3:
+ case PT_R4: case PT_R5: case PT_R6: case PT_R7:
+ case PT_R8: case PT_R9: case PT_R10: case PT_R11:
+ case PT_R12: case PT_R13: case PT_R14: case PT_R15:
+ case PT_R16: case PT_R17: case PT_R18: case PT_R19:
+ case PT_R20: case PT_R21: case PT_R22: case PT_R23:
+ case PT_R24: case PT_R25: case PT_R26: case PT_R27:
+ case PT_R28: case PT_R29: case PT_R30: case PT_R31:
+ case PT_B0: case PT_B1: case PT_B2: case PT_B3:
+ case PT_B4: case PT_B5: case PT_B6: case PT_B7:
+ case PT_F2: case PT_F2+8: case PT_F3: case PT_F3+8:
+ case PT_F4: case PT_F4+8: case PT_F5: case PT_F5+8:
+ case PT_F6: case PT_F6+8: case PT_F7: case PT_F7+8:
+ case PT_F8: case PT_F8+8: case PT_F9: case PT_F9+8:
+ case PT_F10: case PT_F10+8: case PT_F11: case PT_F11+8:
+ case PT_F12: case PT_F12+8: case PT_F13: case PT_F13+8:
+ case PT_F14: case PT_F14+8: case PT_F15: case PT_F15+8:
+ case PT_F16: case PT_F16+8: case PT_F17: case PT_F17+8:
+ case PT_F18: case PT_F18+8: case PT_F19: case PT_F19+8:
+ case PT_F20: case PT_F20+8: case PT_F21: case PT_F21+8:
+ case PT_F22: case PT_F22+8: case PT_F23: case PT_F23+8:
+ case PT_F24: case PT_F24+8: case PT_F25: case PT_F25+8:
+ case PT_F26: case PT_F26+8: case PT_F27: case PT_F27+8:
+ case PT_F28: case PT_F28+8: case PT_F29: case PT_F29+8:
+ case PT_F30: case PT_F30+8: case PT_F31: case PT_F31+8:
+ case PT_AR_BSPSTORE:
+ case PT_AR_RSC: case PT_AR_UNAT: case PT_AR_PFS: case PT_AR_RNAT:
+ case PT_AR_CCV: case PT_AR_FPSR: case PT_CR_IIP: case PT_PR:
+ case PT_AR_LC:
+ ptr = (unsigned long *) ((long) sw + addr - PT_NAT_BITS);
+ break;
+
+ default:
+ /* disallow accessing anything else... */
+ return -1;
+ }
+ } else {
+ /* access debug registers */
+
+ if (!(child->thread.flags & IA64_THREAD_DBG_VALID)) {
+ child->thread.flags |= IA64_THREAD_DBG_VALID;
+ memset(child->thread.dbr, 0, sizeof child->thread.dbr);
+ memset(child->thread.ibr, 0, sizeof child->thread.ibr);
+ }
+ if (addr >= PT_IBR) {
+ regnum = (addr - PT_IBR) >> 3;
+ ptr = &child->thread.ibr[0];
+ } else {
+ regnum = (addr - PT_DBR) >> 3;
+ ptr = &child->thread.dbr[0];
+ }
+
+ if (regnum >= 8)
+ return -1;
+
+ ptr += regnum;
+ }
+ if (write_access)
+ *ptr = *data;
+ else
+ *data = *ptr;
+ return 0;
+}
+
+#endif /* !CONFIG_IA64_NEW_UNWIND */
+
asmlinkage long
sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data,
long arg4, long arg5, long arg6, long arg7, long stack)
{
struct pt_regs *regs = (struct pt_regs *) &stack;
- struct switch_stack *child_stack;
- struct pt_regs *child_regs;
struct task_struct *child;
- unsigned long flags, regnum, *base;
+ unsigned long flags;
long ret;
lock_kernel();
@@ -489,17 +951,21 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data,
ret = -ESRCH;
read_lock(&tasklist_lock);
- child = find_task_by_pid(pid);
+ {
+ child = find_task_by_pid(pid);
+ if (child)
+ get_task_struct(child);
+ }
read_unlock(&tasklist_lock);
if (!child)
goto out;
ret = -EPERM;
if (pid == 1) /* no messing around with init! */
- goto out;
+ goto out_tsk;
if (request == PTRACE_ATTACH) {
if (child == current)
- goto out;
+ goto out_tsk;
if ((!child->dumpable ||
(current->uid != child->euid) ||
(current->uid != child->suid) ||
@@ -508,10 +974,10 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data,
(current->gid != child->sgid) ||
(!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
(current->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
- goto out;
+ goto out_tsk;
/* the same process cannot be attached many times */
if (child->flags & PF_PTRACED)
- goto out;
+ goto out_tsk;
child->flags |= PF_PTRACED;
if (child->p_pptr != current) {
unsigned long flags;
@@ -524,199 +990,98 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data,
}
send_sig(SIGSTOP, child, 1);
ret = 0;
- goto out;
+ goto out_tsk;
}
ret = -ESRCH;
if (!(child->flags & PF_PTRACED))
- goto out;
+ goto out_tsk;
if (child->state != TASK_STOPPED) {
if (request != PTRACE_KILL)
- goto out;
+ goto out_tsk;
}
if (child->p_pptr != current)
- goto out;
+ goto out_tsk;
switch (request) {
case PTRACE_PEEKTEXT:
case PTRACE_PEEKDATA: /* read word at location addr */
- if (!(child->thread.flags & IA64_THREAD_KRBS_SYNCED)
- && atomic_read(&child->mm->mm_users) > 1)
- sync_thread_rbs(child, 0);
+ if (!(child->thread.flags & IA64_THREAD_KRBS_SYNCED)) {
+ struct mm_struct *mm;
+ long do_sync;
+
+ task_lock(child);
+ {
+ mm = child->mm;
+ do_sync = mm && (atomic_read(&mm->mm_users) > 1);
+ }
+ task_unlock(child);
+ if (do_sync)
+ sync_thread_rbs(child, mm, 0);
+ }
ret = ia64_peek(regs, child, addr, &data);
if (ret == 0) {
ret = data;
regs->r8 = 0; /* ensure "ret" is not mistaken as an error code */
}
- goto out;
+ goto out_tsk;
case PTRACE_POKETEXT:
case PTRACE_POKEDATA: /* write the word at location addr */
- if (!(child->thread.flags & IA64_THREAD_KRBS_SYNCED)
- && atomic_read(&child->mm->mm_users) > 1)
- sync_thread_rbs(child, 1);
+ if (!(child->thread.flags & IA64_THREAD_KRBS_SYNCED)) {
+ struct mm_struct *mm;
+ long do_sync;
+
+ task_lock(child);
+ {
+ mm = child->mm;
+ do_sync = mm && (atomic_read(&child->mm->mm_users) > 1);
+ }
+ task_unlock(child);
+ if (do_sync)
+ sync_thread_rbs(child, mm, 1);
+ }
ret = ia64_poke(regs, child, addr, data);
- goto out;
+ goto out_tsk;
case PTRACE_PEEKUSR: /* read the word at addr in the USER area */
- ret = -EIO;
- if ((addr & 0x7) != 0)
- goto out;
-
- if (addr < PT_CALLER_UNAT) {
- /* accessing fph */
- sync_fph(child);
- addr += (unsigned long) &child->thread.fph;
- ret = *(unsigned long *) addr;
- } else if (addr < PT_F9+16) {
- /* accessing switch_stack or pt_regs: */
- child_regs = ia64_task_regs(child);
- child_stack = (struct switch_stack *) child_regs - 1;
- ret = *(unsigned long *) ((long) child_stack + addr - PT_CALLER_UNAT);
-
- if (addr == PT_AR_BSP) {
- /* ret currently contains pt_regs.loadrs */
- unsigned long *rbs, *bspstore, ndirty;
-
- rbs = (unsigned long *) child + IA64_RBS_OFFSET/8;
- bspstore = (unsigned long *) child_regs->ar_bspstore;
- ndirty = ia64_rse_num_regs(rbs, rbs + (ret >> 19));
- ret = (unsigned long) ia64_rse_skip_regs(bspstore, ndirty);
-
- /*
- * If we're in a system call, no ``cover'' was done. So
- * to make things uniform, we'll add the appropriate
- * displacement onto bsp if we're in a system call.
- *
- * Note: It may be better to leave the system call case
- * alone and subtract the amount of the cover for the
- * non-syscall case. That way the reported bsp value
- * would actually be the correct bsp for the child
- * process.
- */
- if (!(child_regs->cr_ifs & (1UL << 63))) {
- ret = (unsigned long)
- ia64_rse_skip_regs((unsigned long *) ret,
- child_stack->ar_pfs & 0x7f);
- }
- } else if (addr == PT_CFM) {
- /* ret currently contains pt_regs.cr_ifs */
- if ((ret & (1UL << 63)) == 0)
- ret = child_stack->ar_pfs;
- ret &= 0x3fffffffffUL; /* return only the CFM */
- }
- } else {
- if (!(child->thread.flags & IA64_THREAD_DBG_VALID)) {
- child->thread.flags |= IA64_THREAD_DBG_VALID;
- memset(child->thread.dbr, 0, sizeof child->thread.dbr);
- memset(child->thread.ibr, 0, sizeof child->thread.ibr);
- }
- if (addr >= PT_IBR) {
- regnum = (addr - PT_IBR) >> 3;
- base = &child->thread.ibr[0];
- } else {
- regnum = (addr - PT_DBR) >> 3;
- base = &child->thread.dbr[0];
- }
- if (regnum >= 8)
- goto out;
- ret = base[regnum];
+ if (access_uarea(child, addr, &data, 0) < 0) {
+ ret = -EIO;
+ goto out_tsk;
}
+ ret = data;
regs->r8 = 0; /* ensure "ret" is not mistaken as an error code */
- goto out;
+ goto out_tsk;
case PTRACE_POKEUSR: /* write the word at addr in the USER area */
- ret = -EIO;
- if ((addr & 0x7) != 0)
- goto out;
-
- if (addr < PT_CALLER_UNAT) {
- /* accessing fph */
- sync_fph(child);
- addr += (unsigned long) &child->thread.fph;
- *(unsigned long *) addr = data;
- } else if (addr == PT_AR_BSPSTORE || addr == PT_CALLER_UNAT
- || addr == PT_KERNEL_FPSR || addr == PT_K_B0 || addr == PT_K_AR_PFS
- || (PT_K_AR_UNAT <= addr && addr <= PT_K_PR)) {
- /*
- * Don't permit changes to certain registers.
- *
- * We don't allow bspstore to be modified because doing
- * so would mess up any modifications to bsp. (See
- * sync_kernel_register_backing_store for the details.)
- */
- goto out;
- } else if (addr == PT_AR_BSP) {
- /* FIXME? Account for lack of ``cover'' in the syscall case */
- ret = sync_kernel_register_backing_store(child, data, 1);
- goto out;
- } else if (addr == PT_CFM) {
- child_regs = ia64_task_regs(child);
- child_stack = (struct switch_stack *) child_regs - 1;
-
- if (child_regs->cr_ifs & (1UL << 63)) {
- child_regs->cr_ifs = (child_regs->cr_ifs & ~0x3fffffffffUL)
- | (data & 0x3fffffffffUL);
- } else {
- child_stack->ar_pfs = (child_stack->ar_pfs & ~0x3fffffffffUL)
- | (data & 0x3fffffffffUL);
- }
- } else if (addr < PT_F9+16) {
- /* accessing switch_stack or pt_regs */
- child_regs = ia64_task_regs(child);
- child_stack = (struct switch_stack *) child_regs - 1;
-
- if (addr == PT_CR_IPSR)
- data = (data & CR_IPSR_CHANGE_MASK)
- | (child_regs->cr_ipsr & ~CR_IPSR_CHANGE_MASK);
-
- *(unsigned long *) ((long) child_stack + addr - PT_CALLER_UNAT) = data;
- } else {
- if (!(child->thread.flags & IA64_THREAD_DBG_VALID)) {
- child->thread.flags |= IA64_THREAD_DBG_VALID;
- memset(child->thread.dbr, 0, sizeof child->thread.dbr);
- memset(child->thread.ibr, 0, sizeof child->thread.ibr);
- }
-
- if (addr >= PT_IBR) {
- regnum = (addr - PT_IBR) >> 3;
- base = &child->thread.ibr[0];
- } else {
- regnum = (addr - PT_DBR) >> 3;
- base = &child->thread.dbr[0];
- }
- if (regnum >= 8)
- goto out;
- if (regnum & 1) {
- /* force breakpoint to be effective only for user-level: */
- data &= ~(0x7UL << 56);
- }
- base[regnum] = data;
+ if (access_uarea(child, addr, &data, 1) < 0) {
+ ret = -EIO;
+ goto out_tsk;
}
ret = 0;
- goto out;
+ goto out_tsk;
case PTRACE_GETSIGINFO:
ret = -EIO;
if (!access_ok(VERIFY_WRITE, data, sizeof (siginfo_t))
|| child->thread.siginfo == 0)
- goto out;
+ goto out_tsk;
copy_to_user((siginfo_t *) data, child->thread.siginfo, sizeof (siginfo_t));
ret = 0;
- goto out;
+ goto out_tsk;
break;
case PTRACE_SETSIGINFO:
ret = -EIO;
if (!access_ok(VERIFY_READ, data, sizeof (siginfo_t))
|| child->thread.siginfo == 0)
- goto out;
+ goto out_tsk;
copy_from_user(child->thread.siginfo, (siginfo_t *) data, sizeof (siginfo_t));
ret = 0;
- goto out;
+ goto out_tsk;
case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
case PTRACE_CONT: /* restart after signal. */
ret = -EIO;
if (data > _NSIG)
- goto out;
+ goto out_tsk;
if (request == PTRACE_SYSCALL)
child->flags |= PF_TRACESYS;
else
@@ -732,7 +1097,7 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data,
wake_up_process(child);
ret = 0;
- goto out;
+ goto out_tsk;
case PTRACE_KILL:
/*
@@ -741,7 +1106,7 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data,
* that it wants to exit.
*/
if (child->state == TASK_ZOMBIE) /* already dead */
- goto out;
+ goto out_tsk;
child->exit_code = SIGKILL;
/* make sure the single step/take-branch tra bits are not set: */
@@ -753,13 +1118,13 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data,
wake_up_process(child);
ret = 0;
- goto out;
+ goto out_tsk;
case PTRACE_SINGLESTEP: /* let child execute for one instruction */
case PTRACE_SINGLEBLOCK:
ret = -EIO;
if (data > _NSIG)
- goto out;
+ goto out_tsk;
child->flags &= ~PF_TRACESYS;
if (request == PTRACE_SINGLESTEP) {
@@ -775,12 +1140,12 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data,
/* give it a chance to run. */
wake_up_process(child);
ret = 0;
- goto out;
+ goto out_tsk;
case PTRACE_DETACH: /* detach a process that was attached. */
ret = -EIO;
if (data > _NSIG)
- goto out;
+ goto out_tsk;
child->flags &= ~(PF_PTRACED|PF_TRACESYS);
child->exit_code = data;
@@ -799,12 +1164,14 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data,
wake_up_process(child);
ret = 0;
- goto out;
+ goto out_tsk;
default:
ret = -EIO;
- goto out;
+ goto out_tsk;
}
+ out_tsk:
+ free_task_struct(child);
out:
unlock_kernel();
return ret;
diff --git a/arch/ia64/kernel/sal_stub.S b/arch/ia64/kernel/sal_stub.S
deleted file mode 100644
index d73851810..000000000
--- a/arch/ia64/kernel/sal_stub.S
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 1998-2000 Hewlett-Packard Co
- * Copyright (C) 1998-2000 David Mosberger-Tang <davidm@hpl.hp.com>
- */
-#ifndef __GCC_MULTIREG_RETVALS__
- /*
- * gcc currently does not conform to the ia-64 calling
- * convention as far as returning function values are
- * concerned. Instead of returning values up to 32 bytes in
- * size in r8-r11, gcc returns any value bigger than a
- * doubleword via a structure that's allocated by the caller
- * and whose address is passed into the function. Since
- * SAL_PROC returns values according to the calling
- * convention, this stub takes care of copying r8-r11 to the
- * place where gcc expects them.
- */
- .text
- .psr abi64
- .psr lsb
- .lsb
-
- .align 16
- .global ia64_sal_stub
-ia64_sal_stub:
- /*
- * Sheesh, the Cygnus backend passes the pointer to a return value structure in
- * in0 whereas the HP backend passes it in r8. Don't you hate those little
- * differences...
- */
-#ifdef GCC_RETVAL_POINTER_IN_R8
- adds r2=-24,sp
- adds sp=-48,sp
- mov r14=rp
- ;;
- st8 [r2]=r8,8 // save pointer to return value
- addl r3=@ltoff(ia64_sal),gp
- ;;
- ld8 r3=[r3]
- st8 [r2]=gp,8 // save global pointer
- ;;
- ld8 r3=[r3] // fetch the value of ia64_sal
- st8 [r2]=r14 // save return pointer
- ;;
- ld8 r2=[r3],8 // load function's entry point
- ;;
- ld8 gp=[r3] // load function's global pointer
- ;;
- mov b6=r2
- br.call.sptk.few rp=b6
-.ret0: adds r2=24,sp
- ;;
- ld8 r3=[r2],8 // restore pointer to return value
- ;;
- ld8 gp=[r2],8 // restore global pointer
- st8 [r3]=r8,8
- ;;
- ld8 r14=[r2] // restore return pointer
- st8 [r3]=r9,8
- ;;
- mov rp=r14
- st8 [r3]=r10,8
- ;;
- st8 [r3]=r11,8
- adds sp=48,sp
- br.sptk.few rp
-#else
- /*
- * On input:
- * in0 = pointer to return value structure
- * in1 = index of SAL function to call
- * in2..inN = remaining args to SAL call
- */
- /*
- * We allocate one input and eight output register such that the br.call instruction
- * will rename in1-in7 to in0-in6---exactly what we want because SAL doesn't want to
- * see the pointer to the return value structure.
- */
- alloc r15=ar.pfs,1,0,8,0
-
- adds r2=-24,sp
- adds sp=-48,sp
- mov r14=rp
- ;;
- st8 [r2]=r15,8 // save ar.pfs
- addl r3=@ltoff(ia64_sal),gp
- ;;
- ld8 r3=[r3] // get address of ia64_sal
- st8 [r2]=gp,8 // save global pointer
- ;;
- ld8 r3=[r3] // get value of ia64_sal
- st8 [r2]=r14,8 // save return address (rp)
- ;;
- ld8 r2=[r3],8 // load function's entry point
- ;;
- ld8 gp=[r3] // load function's global pointer
- mov b6=r2
- br.call.sptk.few rp=b6 // make SAL call
-.ret0: adds r2=24,sp
- ;;
- ld8 r15=[r2],8 // restore ar.pfs
- ;;
- ld8 gp=[r2],8 // restore global pointer
- st8 [in0]=r8,8 // store 1. dword of return value
- ;;
- ld8 r14=[r2] // restore return address (rp)
- st8 [in0]=r9,8 // store 2. dword of return value
- ;;
- mov rp=r14
- st8 [in0]=r10,8 // store 3. dword of return value
- ;;
- st8 [in0]=r11,8
- adds sp=48,sp // pop stack frame
- mov ar.pfs=r15
- br.ret.sptk.few rp
-#endif
-
- .endp ia64_sal_stub
-#endif /* __GCC_MULTIREG_RETVALS__ */
diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c
index 80838f990..09850fdd8 100644
--- a/arch/ia64/kernel/setup.c
+++ b/arch/ia64/kernel/setup.c
@@ -28,6 +28,7 @@
#include <linux/console.h>
#include <asm/acpi-ext.h>
+#include <asm/ia32.h>
#include <asm/page.h>
#include <asm/machvec.h>
#include <asm/processor.h>
@@ -36,6 +37,10 @@
#include <asm/efi.h>
#include <asm/mca.h>
+#ifdef CONFIG_BLK_DEV_RAM
+# include <linux/blk.h>
+#endif
+
extern char _end;
/* cpu_data[bootstrap_processor] is data for the bootstrap processor: */
@@ -108,6 +113,8 @@ setup_arch (char **cmdline_p)
{
unsigned long max_pfn, bootmap_start, bootmap_size;
+ unw_init();
+
/*
* The secondary bootstrap loader passes us the boot
* parameters at the beginning of the ZERO_PAGE, so let's
@@ -125,11 +132,22 @@ setup_arch (char **cmdline_p)
* change APIs, they'd do things for the better. Grumble...
*/
bootmap_start = PAGE_ALIGN(__pa(&_end));
+ if (ia64_boot_param.initrd_size)
+ bootmap_start = PAGE_ALIGN(bootmap_start + ia64_boot_param.initrd_size);
bootmap_size = init_bootmem(bootmap_start >> PAGE_SHIFT, max_pfn);
efi_memmap_walk(free_available_memory, 0);
reserve_bootmem(bootmap_start, bootmap_size);
+#ifdef CONFIG_BLK_DEV_INITRD
+ initrd_start = ia64_boot_param.initrd_start;
+ if (initrd_start) {
+ initrd_end = initrd_start+ia64_boot_param.initrd_size;
+ printk("Initial ramdisk at: 0x%p (%lu bytes)\n",
+ (void *) initrd_start, ia64_boot_param.initrd_size);
+ reserve_bootmem(virt_to_phys(initrd_start), ia64_boot_param.initrd_size);
+ }
+#endif
#if 0
/* XXX fix me */
init_mm.start_code = (unsigned long) &_stext;
@@ -155,10 +173,8 @@ setup_arch (char **cmdline_p)
#ifdef CONFIG_SMP
bootstrap_processor = hard_smp_processor_id();
current->processor = bootstrap_processor;
-#else
- cpu_init();
- identify_cpu(&cpu_data[0]);
#endif
+ cpu_init(); /* initialize the bootstrap CPU */
if (efi.acpi) {
/* Parse the ACPI tables */
@@ -270,35 +286,18 @@ identify_cpu (struct cpuinfo_ia64 *c)
u64 features;
} field;
} cpuid;
+ pal_vm_info_1_u_t vm1;
+ pal_vm_info_2_u_t vm2;
+ pal_status_t status;
+ unsigned long impl_va_msb = 50, phys_addr_size = 44; /* Itanium defaults */
int i;
- for (i = 0; i < 5; ++i) {
+ for (i = 0; i < 5; ++i)
cpuid.bits[i] = ia64_get_cpuid(i);
- }
-#ifdef CONFIG_SMP
- /*
- * XXX Instead of copying the ITC info from the bootstrap
- * processor, ia64_init_itm() should be done per CPU. That
- * should get you the right info. --davidm 1/24/00
- */
- if (c != &cpu_data[bootstrap_processor]) {
- memset(c, 0, sizeof(struct cpuinfo_ia64));
- c->proc_freq = cpu_data[bootstrap_processor].proc_freq;
- c->itc_freq = cpu_data[bootstrap_processor].itc_freq;
- c->cyc_per_usec = cpu_data[bootstrap_processor].cyc_per_usec;
- c->usec_per_cyc = cpu_data[bootstrap_processor].usec_per_cyc;
- }
-#else
memset(c, 0, sizeof(struct cpuinfo_ia64));
-#endif
memcpy(c->vendor, cpuid.field.vendor, 16);
-#ifdef CONFIG_IA64_SOFTSDV_HACKS
- /* BUG: SoftSDV doesn't support the cpuid registers. */
- if (c->vendor[0] == '\0')
- memcpy(c->vendor, "Intel", 6);
-#endif
c->ppn = cpuid.field.ppn;
c->number = cpuid.field.number;
c->revision = cpuid.field.revision;
@@ -306,8 +305,29 @@ identify_cpu (struct cpuinfo_ia64 *c)
c->family = cpuid.field.family;
c->archrev = cpuid.field.archrev;
c->features = cpuid.field.features;
-#ifdef CONFIG_SMP
- c->loops_per_sec = loops_per_sec;
+
+ status = ia64_pal_vm_summary(&vm1, &vm2);
+ if (status == PAL_STATUS_SUCCESS) {
+#if 1
+ /*
+ * XXX the current PAL code returns IMPL_VA_MSB==60, which is dead-wrong.
+ * --davidm 00/05/26
+ s*/
+ impl_va_msb = 50;
+#else
+ impl_va_msb = vm2.pal_vm_info_2_s.impl_va_msb;
+#endif
+ phys_addr_size = vm1.pal_vm_info_1_s.phys_add_size;
+ }
+ printk("processor implements %lu virtual and %lu physical address bits\n",
+ impl_va_msb + 1, phys_addr_size);
+ c->unimpl_va_mask = ~((7L<<61) | ((1L << (impl_va_msb + 1)) - 1));
+ c->unimpl_pa_mask = ~((1L<<63) | ((1L << phys_addr_size) - 1));
+
+#ifdef CONFIG_IA64_SOFTSDV_HACKS
+ /* BUG: SoftSDV doesn't support the cpuid registers. */
+ if (c->vendor[0] == '\0')
+ memcpy(c->vendor, "Intel", 6);
#endif
}
@@ -318,6 +338,11 @@ identify_cpu (struct cpuinfo_ia64 *c)
void
cpu_init (void)
{
+ extern void __init ia64_rid_init (void);
+ extern void __init ia64_tlb_init (void);
+
+ identify_cpu(&my_cpu_data);
+
/* Clear the stack memory reserved for pt_regs: */
memset(ia64_task_regs(current), 0, sizeof(struct pt_regs));
@@ -331,6 +356,21 @@ cpu_init (void)
*/
ia64_set_dcr(IA64_DCR_DR | IA64_DCR_DK | IA64_DCR_DX | IA64_DCR_PP);
ia64_set_fpu_owner(0); /* initialize ar.k5 */
+
atomic_inc(&init_mm.mm_count);
current->active_mm = &init_mm;
+
+ ia64_rid_init();
+ ia64_tlb_init();
+
+#ifdef CONFIG_IA32_SUPPORT
+ /* initialize global ia32 state - CR0 and CR4 */
+ __asm__("mov ar.cflg = %0"
+ : /* no outputs */
+ : "r" (((ulong) IA32_CR4 << 32) | IA32_CR0));
+#endif
+
+#ifdef CONFIG_SMP
+ normal_xtp();
+#endif
}
diff --git a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c
index 25197c1d4..8a46377c9 100644
--- a/arch/ia64/kernel/signal.c
+++ b/arch/ia64/kernel/signal.c
@@ -37,16 +37,26 @@
# define GET_SIGSET(k,u) __get_user((k)->sig[0], &(u)->sig[0])
#endif
+struct sigscratch {
+#ifdef CONFIG_IA64_NEW_UNWIND
+ unsigned long scratch_unat; /* ar.unat for the general registers saved in pt */
+ unsigned long pad;
+#else
+ struct switch_stack sw;
+#endif
+ struct pt_regs pt;
+};
+
struct sigframe {
struct siginfo info;
struct sigcontext sc;
};
extern long sys_wait4 (int, int *, int, struct rusage *);
-extern long ia64_do_signal (sigset_t *, struct pt_regs *, long); /* forward decl */
+extern long ia64_do_signal (sigset_t *, struct sigscratch *, long); /* forward decl */
long
-ia64_rt_sigsuspend (sigset_t *uset, size_t sigsetsize, struct pt_regs *pt)
+ia64_rt_sigsuspend (sigset_t *uset, size_t sigsetsize, struct sigscratch *scr)
{
sigset_t oldset, set;
@@ -71,12 +81,19 @@ ia64_rt_sigsuspend (sigset_t *uset, size_t sigsetsize, struct pt_regs *pt)
* pre-set the correct error code here to ensure that the right values
* get saved in sigcontext by ia64_do_signal.
*/
- pt->r8 = EINTR;
- pt->r10 = -1;
+#ifdef CONFIG_IA32_SUPPORT
+ if (IS_IA32_PROCESS(&scr->pt)) {
+ scr->pt.r8 = -EINTR;
+ } else
+#endif
+ {
+ scr->pt.r8 = EINTR;
+ scr->pt.r10 = -1;
+ }
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
schedule();
- if (ia64_do_signal(&oldset, pt, 1))
+ if (ia64_do_signal(&oldset, scr, 1))
return -EINTR;
}
}
@@ -91,9 +108,8 @@ sys_sigaltstack (const stack_t *uss, stack_t *uoss, long arg2, long arg3, long a
}
static long
-restore_sigcontext (struct sigcontext *sc, struct pt_regs *pt)
+restore_sigcontext (struct sigcontext *sc, struct sigscratch *scr)
{
- struct switch_stack *sw = (struct switch_stack *) pt - 1;
unsigned long ip, flags, nat, um, cfm;
long err;
@@ -104,28 +120,32 @@ restore_sigcontext (struct sigcontext *sc, struct pt_regs *pt)
err |= __get_user(ip, &sc->sc_ip); /* instruction pointer */
err |= __get_user(cfm, &sc->sc_cfm);
err |= __get_user(um, &sc->sc_um); /* user mask */
- err |= __get_user(pt->ar_rsc, &sc->sc_ar_rsc);
- err |= __get_user(pt->ar_ccv, &sc->sc_ar_ccv);
- err |= __get_user(pt->ar_unat, &sc->sc_ar_unat);
- err |= __get_user(pt->ar_fpsr, &sc->sc_ar_fpsr);
- err |= __get_user(pt->ar_pfs, &sc->sc_ar_pfs);
- err |= __get_user(pt->pr, &sc->sc_pr); /* predicates */
- err |= __get_user(pt->b0, &sc->sc_br[0]); /* b0 (rp) */
- err |= __get_user(pt->b6, &sc->sc_br[6]); /* b6 */
- err |= __get_user(pt->b7, &sc->sc_br[7]); /* b7 */
- err |= __copy_from_user(&pt->r1, &sc->sc_gr[1], 3*8); /* r1-r3 */
- err |= __copy_from_user(&pt->r8, &sc->sc_gr[8], 4*8); /* r8-r11 */
- err |= __copy_from_user(&pt->r12, &sc->sc_gr[12], 4*8); /* r12-r15 */
- err |= __copy_from_user(&pt->r16, &sc->sc_gr[16], 16*8); /* r16-r31 */
-
- pt->cr_ifs = cfm | (1UL << 63);
+ err |= __get_user(scr->pt.ar_rsc, &sc->sc_ar_rsc);
+ err |= __get_user(scr->pt.ar_ccv, &sc->sc_ar_ccv);
+ err |= __get_user(scr->pt.ar_unat, &sc->sc_ar_unat);
+ err |= __get_user(scr->pt.ar_fpsr, &sc->sc_ar_fpsr);
+ err |= __get_user(scr->pt.ar_pfs, &sc->sc_ar_pfs);
+ err |= __get_user(scr->pt.pr, &sc->sc_pr); /* predicates */
+ err |= __get_user(scr->pt.b0, &sc->sc_br[0]); /* b0 (rp) */
+ err |= __get_user(scr->pt.b6, &sc->sc_br[6]); /* b6 */
+ err |= __get_user(scr->pt.b7, &sc->sc_br[7]); /* b7 */
+ err |= __copy_from_user(&scr->pt.r1, &sc->sc_gr[1], 3*8); /* r1-r3 */
+ err |= __copy_from_user(&scr->pt.r8, &sc->sc_gr[8], 4*8); /* r8-r11 */
+ err |= __copy_from_user(&scr->pt.r12, &sc->sc_gr[12], 4*8); /* r12-r15 */
+ err |= __copy_from_user(&scr->pt.r16, &sc->sc_gr[16], 16*8); /* r16-r31 */
+
+ scr->pt.cr_ifs = cfm | (1UL << 63);
/* establish new instruction pointer: */
- pt->cr_iip = ip & ~0x3UL;
- ia64_psr(pt)->ri = ip & 0x3;
- pt->cr_ipsr = (pt->cr_ipsr & ~IA64_PSR_UM) | (um & IA64_PSR_UM);
+ scr->pt.cr_iip = ip & ~0x3UL;
+ ia64_psr(&scr->pt)->ri = ip & 0x3;
+ scr->pt.cr_ipsr = (scr->pt.cr_ipsr & ~IA64_PSR_UM) | (um & IA64_PSR_UM);
- ia64_put_nat_bits (pt, sw, nat); /* restore the original scratch NaT bits */
+#ifdef CONFIG_IA64_NEW_UNWIND
+ scr->scratch_unat = ia64_put_scratch_nat_bits(&scr->pt, nat);
+#else
+ ia64_put_nat_bits(&scr->pt, &scr->sw, nat); /* restore the original scratch NaT bits */
+#endif
if (flags & IA64_SC_FLAG_FPH_VALID) {
struct task_struct *fpu_owner = ia64_get_fpu_owner();
@@ -138,7 +158,8 @@ restore_sigcontext (struct sigcontext *sc, struct pt_regs *pt)
return err;
}
-int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from)
+int
+copy_siginfo_to_user (siginfo_t *to, siginfo_t *from)
{
if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t)))
return -EFAULT;
@@ -147,43 +168,39 @@ int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from)
else {
int err;
- /* If you change siginfo_t structure, please be sure
- this code is fixed accordingly.
- It should never copy any pad contained in the structure
- to avoid security leaks, but must copy the generic
- 3 ints plus the relevant union member. */
+ /*
+ * If you change siginfo_t structure, please be sure
+ * this code is fixed accordingly. It should never
+ * copy any pad contained in the structure to avoid
+ * security leaks, but must copy the generic 3 ints
+ * plus the relevant union member.
+ */
err = __put_user(from->si_signo, &to->si_signo);
err |= __put_user(from->si_errno, &to->si_errno);
err |= __put_user((short)from->si_code, &to->si_code);
switch (from->si_code >> 16) {
- case __SI_FAULT >> 16:
- case __SI_POLL >> 16:
+ case __SI_FAULT >> 16:
+ err |= __put_user(from->si_isr, &to->si_isr);
+ case __SI_POLL >> 16:
err |= __put_user(from->si_addr, &to->si_addr);
err |= __put_user(from->si_imm, &to->si_imm);
break;
- case __SI_CHLD >> 16:
+ case __SI_CHLD >> 16:
err |= __put_user(from->si_utime, &to->si_utime);
err |= __put_user(from->si_stime, &to->si_stime);
err |= __put_user(from->si_status, &to->si_status);
- default:
+ default:
err |= __put_user(from->si_uid, &to->si_uid);
err |= __put_user(from->si_pid, &to->si_pid);
break;
- /* case __SI_RT: This is not generated by the kernel as of now. */
+ /* case __SI_RT: This is not generated by the kernel as of now. */
}
return err;
}
}
-/*
- * When we get here, ((struct switch_stack *) pt - 1) is a
- * switch_stack frame that has no defined value. Upon return, we
- * expect sw->caller_unat to contain the new unat value. The reason
- * we use a full switch_stack frame is so everything is symmetric
- * with ia64_do_signal().
- */
long
-ia64_rt_sigreturn (struct pt_regs *pt)
+ia64_rt_sigreturn (struct sigscratch *scr)
{
extern char ia64_strace_leave_kernel, ia64_leave_kernel;
struct sigcontext *sc;
@@ -191,7 +208,7 @@ ia64_rt_sigreturn (struct pt_regs *pt)
sigset_t set;
long retval;
- sc = &((struct sigframe *) (pt->r12 + 16))->sc;
+ sc = &((struct sigframe *) (scr->pt.r12 + 16))->sc;
/*
* When we return to the previously executing context, r8 and
@@ -200,9 +217,15 @@ ia64_rt_sigreturn (struct pt_regs *pt)
* must not touch r8 or r10 as otherwise user-level stat could
* be corrupted.
*/
- retval = (long) &ia64_leave_kernel | 1;
- if ((current->flags & PF_TRACESYS)
- && (sc->sc_flags & IA64_SC_FLAG_IN_SYSCALL))
+ retval = (long) &ia64_leave_kernel;
+ if (current->flags & PF_TRACESYS)
+ /*
+ * strace expects to be notified after sigreturn
+ * returns even though the context to which we return
+ * may not be in the middle of a syscall. Thus, the
+ * return-value that strace displays for sigreturn is
+ * meaningless.
+ */
retval = (long) &ia64_strace_leave_kernel;
if (!access_ok(VERIFY_READ, sc, sizeof(*sc)))
@@ -217,18 +240,18 @@ ia64_rt_sigreturn (struct pt_regs *pt)
recalc_sigpending(current);
spin_unlock_irq(&current->sigmask_lock);
- if (restore_sigcontext(sc, pt))
+ if (restore_sigcontext(sc, scr))
goto give_sigsegv;
#if DEBUG_SIG
printk("SIG return (%s:%d): sp=%lx ip=%lx\n",
- current->comm, current->pid, pt->r12, pt->cr_iip);
+ current->comm, current->pid, scr->pt.r12, scr->pt.cr_iip);
#endif
/*
* It is more difficult to avoid calling this function than to
* call it and ignore errors.
*/
- do_sigaltstack(&sc->sc_stack, 0, pt->r12);
+ do_sigaltstack(&sc->sc_stack, 0, scr->pt.r12);
return retval;
give_sigsegv:
@@ -249,14 +272,13 @@ ia64_rt_sigreturn (struct pt_regs *pt)
* trampoline starts. Everything else is done at the user-level.
*/
static long
-setup_sigcontext (struct sigcontext *sc, sigset_t *mask, struct pt_regs *pt)
+setup_sigcontext (struct sigcontext *sc, sigset_t *mask, struct sigscratch *scr)
{
- struct switch_stack *sw = (struct switch_stack *) pt - 1;
struct task_struct *fpu_owner = ia64_get_fpu_owner();
unsigned long flags = 0, ifs, nat;
long err;
- ifs = pt->cr_ifs;
+ ifs = scr->pt.cr_ifs;
if (on_sig_stack((unsigned long) sc))
flags |= IA64_SC_FLAG_ONSTACK;
@@ -276,46 +298,49 @@ setup_sigcontext (struct sigcontext *sc, sigset_t *mask, struct pt_regs *pt)
* Note: sw->ar_unat is UNDEFINED unless the process is being
* PTRACED. However, this is OK because the NaT bits of the
* preserved registers (r4-r7) are never being looked at by
- * the signal handler (register r4-r7 are used instead).
+ * the signal handler (registers r4-r7 are used instead).
*/
- nat = ia64_get_nat_bits(pt, sw);
+#ifdef CONFIG_IA64_NEW_UNWIND
+ nat = ia64_get_scratch_nat_bits(&scr->pt, scr->scratch_unat);
+#else
+ nat = ia64_get_nat_bits(&scr->pt, &scr->sw);
+#endif
err = __put_user(flags, &sc->sc_flags);
err |= __put_user(nat, &sc->sc_nat);
err |= PUT_SIGSET(mask, &sc->sc_mask);
- err |= __put_user(pt->cr_ipsr & IA64_PSR_UM, &sc->sc_um);
- err |= __put_user(pt->ar_rsc, &sc->sc_ar_rsc);
- err |= __put_user(pt->ar_ccv, &sc->sc_ar_ccv);
- err |= __put_user(pt->ar_unat, &sc->sc_ar_unat); /* ar.unat */
- err |= __put_user(pt->ar_fpsr, &sc->sc_ar_fpsr); /* ar.fpsr */
- err |= __put_user(pt->ar_pfs, &sc->sc_ar_pfs);
- err |= __put_user(pt->pr, &sc->sc_pr); /* predicates */
- err |= __put_user(pt->b0, &sc->sc_br[0]); /* b0 (rp) */
- err |= __put_user(pt->b6, &sc->sc_br[6]); /* b6 */
- err |= __put_user(pt->b7, &sc->sc_br[7]); /* b7 */
-
- err |= __copy_to_user(&sc->sc_gr[1], &pt->r1, 3*8); /* r1-r3 */
- err |= __copy_to_user(&sc->sc_gr[8], &pt->r8, 4*8); /* r8-r11 */
- err |= __copy_to_user(&sc->sc_gr[12], &pt->r12, 4*8); /* r12-r15 */
- err |= __copy_to_user(&sc->sc_gr[16], &pt->r16, 16*8); /* r16-r31 */
-
- err |= __put_user(pt->cr_iip + ia64_psr(pt)->ri, &sc->sc_ip);
- err |= __put_user(pt->r12, &sc->sc_gr[12]); /* r12 */
+ err |= __put_user(scr->pt.cr_ipsr & IA64_PSR_UM, &sc->sc_um);
+ err |= __put_user(scr->pt.ar_rsc, &sc->sc_ar_rsc);
+ err |= __put_user(scr->pt.ar_ccv, &sc->sc_ar_ccv);
+ err |= __put_user(scr->pt.ar_unat, &sc->sc_ar_unat); /* ar.unat */
+ err |= __put_user(scr->pt.ar_fpsr, &sc->sc_ar_fpsr); /* ar.fpsr */
+ err |= __put_user(scr->pt.ar_pfs, &sc->sc_ar_pfs);
+ err |= __put_user(scr->pt.pr, &sc->sc_pr); /* predicates */
+ err |= __put_user(scr->pt.b0, &sc->sc_br[0]); /* b0 (rp) */
+ err |= __put_user(scr->pt.b6, &sc->sc_br[6]); /* b6 */
+ err |= __put_user(scr->pt.b7, &sc->sc_br[7]); /* b7 */
+
+ err |= __copy_to_user(&sc->sc_gr[1], &scr->pt.r1, 3*8); /* r1-r3 */
+ err |= __copy_to_user(&sc->sc_gr[8], &scr->pt.r8, 4*8); /* r8-r11 */
+ err |= __copy_to_user(&sc->sc_gr[12], &scr->pt.r12, 4*8); /* r12-r15 */
+ err |= __copy_to_user(&sc->sc_gr[16], &scr->pt.r16, 16*8); /* r16-r31 */
+
+ err |= __put_user(scr->pt.cr_iip + ia64_psr(&scr->pt)->ri, &sc->sc_ip);
return err;
}
static long
-setup_frame (int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs *pt)
+setup_frame (int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set,
+ struct sigscratch *scr)
{
- struct switch_stack *sw = (struct switch_stack *) pt - 1;
extern char ia64_sigtramp[], __start_gate_section[];
unsigned long tramp_addr, new_rbs = 0;
struct sigframe *frame;
struct siginfo si;
long err;
- frame = (void *) pt->r12;
+ frame = (void *) scr->pt.r12;
tramp_addr = GATE_ADDR + (ia64_sigtramp - __start_gate_section);
if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && !on_sig_stack((unsigned long) frame)) {
new_rbs = (current->sas_ss_sp + sizeof(long) - 1) & ~(sizeof(long) - 1);
@@ -331,31 +356,39 @@ setup_frame (int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, st
err |= __put_user(current->sas_ss_sp, &frame->sc.sc_stack.ss_sp);
err |= __put_user(current->sas_ss_size, &frame->sc.sc_stack.ss_size);
- err |= __put_user(sas_ss_flags(pt->r12), &frame->sc.sc_stack.ss_flags);
- err |= setup_sigcontext(&frame->sc, set, pt);
+ err |= __put_user(sas_ss_flags(scr->pt.r12), &frame->sc.sc_stack.ss_flags);
+ err |= setup_sigcontext(&frame->sc, set, scr);
if (err)
goto give_sigsegv;
- pt->r12 = (unsigned long) frame - 16; /* new stack pointer */
- pt->r2 = sig; /* signal number */
- pt->r3 = (unsigned long) ka->sa.sa_handler; /* addr. of handler's proc. descriptor */
- pt->r15 = new_rbs;
- pt->ar_fpsr = FPSR_DEFAULT; /* reset fpsr for signal handler */
- pt->cr_iip = tramp_addr;
- ia64_psr(pt)->ri = 0; /* start executing in first slot */
+ scr->pt.r12 = (unsigned long) frame - 16; /* new stack pointer */
+ scr->pt.r2 = sig; /* signal number */
+ scr->pt.r3 = (unsigned long) ka->sa.sa_handler; /* addr. of handler's proc desc */
+ scr->pt.r15 = new_rbs;
+ scr->pt.ar_fpsr = FPSR_DEFAULT; /* reset fpsr for signal handler */
+ scr->pt.cr_iip = tramp_addr;
+ ia64_psr(&scr->pt)->ri = 0; /* start executing in first slot */
+#ifdef CONFIG_IA64_NEW_UNWIND
+ /*
+ * Note: this affects only the NaT bits of the scratch regs
+ * (the ones saved in pt_regs), which is exactly what we want.
+ */
+ scr->scratch_unat = 0; /* ensure NaT bits of at least r2, r3, r12, and r15 are clear */
+#else
/*
* Note: this affects only the NaT bits of the scratch regs
- * (the ones saved in pt_regs, which is exactly what we want.
+ * (the ones saved in pt_regs), which is exactly what we want.
* The NaT bits for the preserved regs (r4-r7) are in
* sw->ar_unat iff this process is being PTRACED.
*/
- sw->caller_unat = 0; /* ensure NaT bits of at least r2, r3, r12, and r15 are clear */
+ scr->sw.caller_unat = 0; /* ensure NaT bits of at least r2, r3, r12, and r15 are clear */
+#endif
#if DEBUG_SIG
printk("SIG deliver (%s:%d): sig=%d sp=%lx ip=%lx handler=%lx\n",
- current->comm, current->pid, sig, pt->r12, pt->cr_iip, pt->r3);
+ current->comm, current->pid, sig, scr->pt.r12, scr->pt.cr_iip, scr->pt.r3);
#endif
return 1;
@@ -374,17 +407,17 @@ setup_frame (int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, st
static long
handle_signal (unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset,
- struct pt_regs *pt)
+ struct sigscratch *scr)
{
#ifdef CONFIG_IA32_SUPPORT
- if (IS_IA32_PROCESS(pt)) {
+ if (IS_IA32_PROCESS(&scr->pt)) {
/* send signal to IA-32 process */
- if (!ia32_setup_frame1(sig, ka, info, oldset, pt))
+ if (!ia32_setup_frame1(sig, ka, info, oldset, &scr->pt))
return 0;
} else
#endif
/* send signal to IA-64 process */
- if (!setup_frame(sig, ka, info, oldset, pt))
+ if (!setup_frame(sig, ka, info, oldset, scr))
return 0;
if (ka->sa.sa_flags & SA_ONESHOT)
@@ -401,12 +434,6 @@ handle_signal (unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigse
}
/*
- * When we get here, `pt' points to struct pt_regs and ((struct
- * switch_stack *) pt - 1) points to a switch stack structure.
- * HOWEVER, in the normal case, the ONLY value valid in the
- * switch_stack is the caller_unat field. The entire switch_stack is
- * valid ONLY if current->flags has PF_PTRACED set.
- *
* Note that `init' is a special process: it doesn't get signals it
* doesn't want to handle. Thus you cannot kill init even with a
* SIGKILL even by mistake.
@@ -416,24 +443,35 @@ handle_signal (unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigse
* user-level signal handling stack-frames in one go after that.
*/
long
-ia64_do_signal (sigset_t *oldset, struct pt_regs *pt, long in_syscall)
+ia64_do_signal (sigset_t *oldset, struct sigscratch *scr, long in_syscall)
{
struct k_sigaction *ka;
siginfo_t info;
long restart = in_syscall;
+ long errno = scr->pt.r8;
/*
* In the ia64_leave_kernel code path, we want the common case
* to go fast, which is why we may in certain cases get here
* from kernel mode. Just return without doing anything if so.
*/
- if (!user_mode(pt))
+ if (!user_mode(&scr->pt))
return 0;
if (!oldset)
oldset = &current->blocked;
- if (pt->r10 != -1) {
+#ifdef CONFIG_IA32_SUPPORT
+ if (IS_IA32_PROCESS(&scr->pt)) {
+ if (in_syscall) {
+ if (errno >= 0)
+ restart = 0;
+ else
+ errno = -errno;
+ }
+ } else
+#endif
+ if (scr->pt.r10 != -1) {
/*
* A system calls has to be restarted only if one of
* the error codes ERESTARTNOHAND, ERESTARTSYS, or
@@ -527,7 +565,7 @@ ia64_do_signal (sigset_t *oldset, struct pt_regs *pt, long in_syscall)
case SIGQUIT: case SIGILL: case SIGTRAP:
case SIGABRT: case SIGFPE: case SIGSEGV:
case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ:
- if (do_coredump(signr, pt))
+ if (do_coredump(signr, &scr->pt))
exit_code |= 0x80;
/* FALLTHRU */
@@ -542,39 +580,54 @@ ia64_do_signal (sigset_t *oldset, struct pt_regs *pt, long in_syscall)
}
if (restart) {
- switch (pt->r8) {
+ switch (errno) {
case ERESTARTSYS:
if ((ka->sa.sa_flags & SA_RESTART) == 0) {
case ERESTARTNOHAND:
- pt->r8 = EINTR;
- /* note: pt->r10 is already -1 */
+#ifdef CONFIG_IA32_SUPPORT
+ if (IS_IA32_PROCESS(&scr->pt))
+ scr->pt.r8 = -EINTR;
+ else
+#endif
+ scr->pt.r8 = EINTR;
+ /* note: scr->pt.r10 is already -1 */
break;
}
case ERESTARTNOINTR:
- ia64_decrement_ip(pt);
+#ifdef CONFIG_IA32_SUPPORT
+ if (IS_IA32_PROCESS(&scr->pt)) {
+ scr->pt.r8 = scr->pt.r1;
+ scr->pt.cr_iip -= 2;
+ } else
+#endif
+ ia64_decrement_ip(&scr->pt);
}
}
/* Whee! Actually deliver the signal. If the
delivery failed, we need to continue to iterate in
this loop so we can deliver the SIGSEGV... */
- if (handle_signal(signr, ka, &info, oldset, pt))
+ if (handle_signal(signr, ka, &info, oldset, scr))
return 1;
}
/* Did we come from a system call? */
if (restart) {
/* Restart the system call - no handlers present */
- if (pt->r8 == ERESTARTNOHAND ||
- pt->r8 == ERESTARTSYS ||
- pt->r8 == ERESTARTNOINTR) {
+ if (errno == ERESTARTNOHAND || errno == ERESTARTSYS || errno == ERESTARTNOINTR) {
+#ifdef CONFIG_IA32_SUPPORT
+ if (IS_IA32_PROCESS(&scr->pt)) {
+ scr->pt.r8 = scr->pt.r1;
+ scr->pt.cr_iip -= 2;
+ } else
+#endif
/*
* Note: the syscall number is in r15 which is
* saved in pt_regs so all we need to do here
* is adjust ip so that the "break"
* instruction gets re-executed.
*/
- ia64_decrement_ip(pt);
+ ia64_decrement_ip(&scr->pt);
}
}
return 0;
diff --git a/arch/ia64/kernel/smp.c b/arch/ia64/kernel/smp.c
index 43d9f2dde..e6f0f36fe 100644
--- a/arch/ia64/kernel/smp.c
+++ b/arch/ia64/kernel/smp.c
@@ -21,11 +21,13 @@
#include <linux/smp.h>
#include <linux/kernel_stat.h>
#include <linux/mm.h>
+#include <linux/delay.h>
#include <asm/atomic.h>
#include <asm/bitops.h>
#include <asm/current.h>
#include <asm/delay.h>
+
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/page.h>
@@ -39,6 +41,7 @@
extern int cpu_idle(void * unused);
extern void _start(void);
+extern void machine_halt(void);
extern int cpu_now_booting; /* Used by head.S to find idle task */
extern volatile unsigned long cpu_online_map; /* Bitmap of available cpu's */
@@ -66,15 +69,18 @@ struct smp_call_struct {
atomic_t unstarted_count;
atomic_t unfinished_count;
};
-static struct smp_call_struct *smp_call_function_data;
+static volatile struct smp_call_struct *smp_call_function_data;
-#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC
+#ifdef CONFIG_ITANIUM_A1_SPECIFIC
extern spinlock_t ivr_read_lock;
#endif
#define IPI_RESCHEDULE 0
#define IPI_CALL_FUNC 1
#define IPI_CPU_STOP 2
+#ifndef CONFIG_ITANIUM_PTCG
+# define IPI_FLUSH_TLB 3
+#endif /*!CONFIG_ITANIUM_PTCG */
/*
* Setup routine for controlling SMP activation
@@ -126,6 +132,22 @@ halt_processor(void)
}
+static inline int
+pointer_lock(void *lock, void *data, int retry)
+{
+ again:
+ if (cmpxchg_acq((void **) lock, 0, data) == 0)
+ return 0;
+
+ if (!retry)
+ return -EBUSY;
+
+ while (*(void **) lock)
+ ;
+
+ goto again;
+}
+
void
handle_IPI(int irq, void *dev_id, struct pt_regs *regs)
{
@@ -160,13 +182,14 @@ handle_IPI(int irq, void *dev_id, struct pt_regs *regs)
void *info;
int wait;
+ /* release the 'pointer lock' */
data = smp_call_function_data;
func = data->func;
info = data->info;
wait = data->wait;
mb();
- atomic_dec (&data->unstarted_count);
+ atomic_dec(&data->unstarted_count);
/* At this point the structure may be gone unless wait is true. */
(*func)(info);
@@ -174,7 +197,7 @@ handle_IPI(int irq, void *dev_id, struct pt_regs *regs)
/* Notify the sending CPU that the task is done. */
mb();
if (wait)
- atomic_dec (&data->unfinished_count);
+ atomic_dec(&data->unfinished_count);
}
break;
@@ -182,6 +205,51 @@ handle_IPI(int irq, void *dev_id, struct pt_regs *regs)
halt_processor();
break;
+#ifndef CONFIG_ITANIUM_PTCG
+ case IPI_FLUSH_TLB:
+ {
+ extern unsigned long flush_start, flush_end, flush_nbits, flush_rid;
+ extern atomic_t flush_cpu_count;
+ unsigned long saved_rid = ia64_get_rr(flush_start);
+ unsigned long end = flush_end;
+ unsigned long start = flush_start;
+ unsigned long nbits = flush_nbits;
+
+ /*
+ * Current CPU may be running with different
+ * RID so we need to reload the RID of flushed
+ * address. Purging the translation also
+ * needs ALAT invalidation; we do not need
+ * "invala" here since it is done in
+ * ia64_leave_kernel.
+ */
+ ia64_srlz_d();
+ if (saved_rid != flush_rid) {
+ ia64_set_rr(flush_start, flush_rid);
+ ia64_srlz_d();
+ }
+
+ do {
+ /*
+ * Purge local TLB entries.
+ */
+ __asm__ __volatile__ ("ptc.l %0,%1" ::
+ "r"(start), "r"(nbits<<2) : "memory");
+ start += (1UL << nbits);
+ } while (start < end);
+
+ ia64_insn_group_barrier();
+ ia64_srlz_i(); /* srlz.i implies srlz.d */
+
+ if (saved_rid != flush_rid) {
+ ia64_set_rr(flush_start, saved_rid);
+ ia64_srlz_d();
+ }
+ atomic_dec(&flush_cpu_count);
+ break;
+ }
+#endif /* !CONFIG_ITANIUM_PTCG */
+
default:
printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n", this_cpu, which);
break;
@@ -199,7 +267,7 @@ send_IPI_single(int dest_cpu, int op)
if (dest_cpu == -1)
return;
- ipi_op[dest_cpu] |= (1 << op);
+ set_bit(op, &ipi_op[dest_cpu]);
ipi_send(dest_cpu, IPI_IRQ, IA64_IPI_DM_INT, 0);
}
@@ -243,6 +311,14 @@ smp_send_stop(void)
send_IPI_allbutself(IPI_CPU_STOP);
}
+#ifndef CONFIG_ITANIUM_PTCG
+void
+smp_send_flush_tlb(void)
+{
+ send_IPI_allbutself(IPI_FLUSH_TLB);
+}
+#endif /* !CONFIG_ITANIUM_PTCG */
+
/*
* Run a function on all other CPUs.
* <func> The function to run. This must be fast and non-blocking.
@@ -260,63 +336,35 @@ smp_call_function (void (*func) (void *info), void *info, int retry, int wait)
{
struct smp_call_struct data;
long timeout;
- static spinlock_t lock = SPIN_LOCK_UNLOCKED;
+ int cpus = smp_num_cpus - 1;
+
+ if (cpus == 0)
+ return 0;
data.func = func;
data.info = info;
data.wait = wait;
- atomic_set(&data.unstarted_count, smp_num_cpus - 1);
- atomic_set(&data.unfinished_count, smp_num_cpus - 1);
+ atomic_set(&data.unstarted_count, cpus);
+ atomic_set(&data.unfinished_count, cpus);
- if (retry) {
- while (1) {
- if (smp_call_function_data) {
- schedule (); /* Give a mate a go */
- continue;
- }
- spin_lock (&lock);
- if (smp_call_function_data) {
- spin_unlock (&lock); /* Bad luck */
- continue;
- }
- /* Mine, all mine! */
- break;
- }
- }
- else {
- if (smp_call_function_data)
- return -EBUSY;
- spin_lock (&lock);
- if (smp_call_function_data) {
- spin_unlock (&lock);
- return -EBUSY;
- }
- }
+ if (pointer_lock(&smp_call_function_data, &data, retry))
+ return -EBUSY;
- smp_call_function_data = &data;
- spin_unlock (&lock);
- data.func = func;
- data.info = info;
- atomic_set (&data.unstarted_count, smp_num_cpus - 1);
- data.wait = wait;
- if (wait)
- atomic_set (&data.unfinished_count, smp_num_cpus - 1);
-
/* Send a message to all other CPUs and wait for them to respond */
send_IPI_allbutself(IPI_CALL_FUNC);
/* Wait for response */
timeout = jiffies + HZ;
- while ( (atomic_read (&data.unstarted_count) > 0) &&
- time_before (jiffies, timeout) )
- barrier ();
- if (atomic_read (&data.unstarted_count) > 0) {
+ while ((atomic_read(&data.unstarted_count) > 0) && time_before(jiffies, timeout))
+ barrier();
+ if (atomic_read(&data.unstarted_count) > 0) {
smp_call_function_data = NULL;
return -ETIMEDOUT;
}
if (wait)
- while (atomic_read (&data.unfinished_count) > 0)
- barrier ();
+ while (atomic_read(&data.unfinished_count) > 0)
+ barrier();
+ /* unlock pointer */
smp_call_function_data = NULL;
return 0;
}
@@ -382,17 +430,21 @@ smp_do_timer(struct pt_regs *regs)
}
}
-
-/*
- * Called by both boot and secondaries to move global data into
- * per-processor storage.
- */
static inline void __init
-smp_store_cpu_info(int cpuid)
+smp_calibrate_delay(int cpuid)
{
struct cpuinfo_ia64 *c = &cpu_data[cpuid];
-
- identify_cpu(c);
+#if 0
+ unsigned long old = loops_per_sec;
+ extern void calibrate_delay(void);
+
+ loops_per_sec = 0;
+ calibrate_delay();
+ c->loops_per_sec = loops_per_sec;
+ loops_per_sec = old;
+#else
+ c->loops_per_sec = loops_per_sec;
+#endif
}
/*
@@ -446,34 +498,26 @@ smp_callin(void)
extern void ia64_init_itm(void);
extern void ia64_cpu_local_tick(void);
- ia64_set_dcr(IA64_DCR_DR | IA64_DCR_DK | IA64_DCR_DX | IA64_DCR_PP);
- ia64_set_fpu_owner(0);
- ia64_rid_init(); /* initialize region ids */
-
cpu_init();
- __flush_tlb_all();
- smp_store_cpu_info(smp_processor_id());
smp_setup_percpu_timer(smp_processor_id());
- if (test_and_set_bit(smp_processor_id(), &cpu_online_map)) {
- printk("CPU#%d already initialized!\n", smp_processor_id());
- machine_halt();
- }
- while (!smp_threads_ready)
- mb();
-
- normal_xtp();
-
/* setup the CPU local timer tick */
- ia64_cpu_local_tick();
+ ia64_init_itm();
/* Disable all local interrupts */
ia64_set_lrr0(0, 1);
ia64_set_lrr1(0, 1);
- __sti(); /* Interrupts have been off till now. */
+ if (test_and_set_bit(smp_processor_id(), &cpu_online_map)) {
+ printk("CPU#%d already initialized!\n", smp_processor_id());
+ machine_halt();
+ }
+ while (!smp_threads_ready)
+ mb();
+ local_irq_enable(); /* Interrupts have been off until now */
+ smp_calibrate_delay(smp_processor_id());
printk("SMP: CPU %d starting idle loop\n", smp_processor_id());
cpu_idle(NULL);
@@ -583,16 +627,8 @@ smp_boot_cpus(void)
/* Setup BSP mappings */
__cpu_number_map[bootstrap_processor] = 0;
__cpu_logical_map[0] = bootstrap_processor;
- current->processor = bootstrap_processor;
-
- /* Mark BSP booted and get active_mm context */
- cpu_init();
-
- /* reset XTP for interrupt routing */
- normal_xtp();
- /* And generate an entry in cpu_data */
- smp_store_cpu_info(bootstrap_processor);
+ smp_calibrate_delay(smp_processor_id());
#if 0
smp_tune_scheduling();
#endif
diff --git a/arch/ia64/kernel/sys_ia64.c b/arch/ia64/kernel/sys_ia64.c
index f06d3bea8..cd9d64fce 100644
--- a/arch/ia64/kernel/sys_ia64.c
+++ b/arch/ia64/kernel/sys_ia64.c
@@ -14,6 +14,9 @@
#include <linux/file.h> /* doh, must come after sched.h... */
#include <linux/smp.h>
#include <linux/smp_lock.h>
+#include <linux/highuid.h>
+
+#include <asm/uaccess.h>
asmlinkage long
ia64_getpriority (int which, int who, long arg2, long arg3, long arg4, long arg5, long arg6,
@@ -94,7 +97,11 @@ sys_pipe (long arg0, long arg1, long arg2, long arg3,
static inline unsigned long
do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, unsigned long pgoff)
{
+ long start_low, end_low, starting_region, ending_region;
+ unsigned long loff, hoff;
struct file *file = 0;
+ /* the virtual address space that is mappable in each region: */
+# define OCTANT_SIZE ((PTRS_PER_PGD<<PGDIR_SHIFT)/8)
/*
* A zero mmap always succeeds in Linux, independent of
@@ -103,15 +110,19 @@ do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, un
if (PAGE_ALIGN(len) == 0)
return addr;
-#ifdef notyet
- /* Don't permit mappings that would cross a region boundary: */
- region_start = IA64_GET_REGION(addr);
- region_end = IA64_GET_REGION(addr + len);
- if (region_start != region_end)
+ /* Don't permit mappings into or across the address hole in a region: */
+ loff = REGION_OFFSET(addr);
+ hoff = loff - (REGION_SIZE - OCTANT_SIZE/2);
+ if ((len | loff | (loff + len)) >= OCTANT_SIZE/2
+ && (len | hoff | (hoff + len)) >= OCTANT_SIZE/2)
return -EINVAL;
- <<x??x>>
-#endif
+ /* Don't permit mappings that would cross a region boundary: */
+
+ starting_region = REGION_NUMBER(addr);
+ ending_region = REGION_NUMBER(addr + len);
+ if (starting_region != ending_region)
+ return -EINVAL;
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
if (!(flags & MAP_ANONYMOUS)) {
@@ -156,6 +167,9 @@ sys_mmap (unsigned long addr, unsigned long len, int prot, int flags,
{
struct pt_regs *regs = (struct pt_regs *) &stack;
+ if ((off & ~PAGE_MASK) != 0)
+ return -EINVAL;
+
addr = do_mmap2(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
if (!IS_ERR(addr))
regs->r8 = 0; /* ensure large addresses are not mistaken as failures... */
@@ -196,6 +210,150 @@ sys_modify_ldt (long arg0, long arg1, long arg2, long arg3)
return -ENOSYS;
}
+asmlinkage unsigned long
+ia64_create_module (const char *name_user, size_t size, long arg2, long arg3,
+ long arg4, long arg5, long arg6, long arg7, long stack)
+{
+ extern unsigned long sys_create_module (const char *, size_t);
+ struct pt_regs *regs = (struct pt_regs *) &stack;
+ unsigned long addr;
+
+ addr = sys_create_module (name_user, size);
+ if (!IS_ERR(addr))
+ regs->r8 = 0; /* ensure large addresses are not mistaken as failures... */
+ return addr;
+}
+
+#if 1
+/*
+ * This is here for a while to keep compatibillity with the old stat()
+ * call - it will be removed later once everybody migrates to the new
+ * kernel stat structure that matches the glibc one - Jes
+ */
+static __inline__ int
+do_revalidate (struct dentry *dentry)
+{
+ struct inode * inode = dentry->d_inode;
+ if (inode->i_op && inode->i_op->revalidate)
+ return inode->i_op->revalidate(dentry);
+ return 0;
+}
+
+static int
+cp_ia64_old_stat (struct inode *inode, struct ia64_oldstat *statbuf)
+{
+ struct ia64_oldstat tmp;
+ unsigned int blocks, indirect;
+
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.st_dev = kdev_t_to_nr(inode->i_dev);
+ tmp.st_ino = inode->i_ino;
+ tmp.st_mode = inode->i_mode;
+ tmp.st_nlink = inode->i_nlink;
+ SET_STAT_UID(tmp, inode->i_uid);
+ SET_STAT_GID(tmp, inode->i_gid);
+ tmp.st_rdev = kdev_t_to_nr(inode->i_rdev);
+ tmp.st_size = inode->i_size;
+ tmp.st_atime = inode->i_atime;
+ tmp.st_mtime = inode->i_mtime;
+ tmp.st_ctime = inode->i_ctime;
+/*
+ * st_blocks and st_blksize are approximated with a simple algorithm if
+ * they aren't supported directly by the filesystem. The minix and msdos
+ * filesystems don't keep track of blocks, so they would either have to
+ * be counted explicitly (by delving into the file itself), or by using
+ * this simple algorithm to get a reasonable (although not 100% accurate)
+ * value.
+ */
+
+/*
+ * Use minix fs values for the number of direct and indirect blocks. The
+ * count is now exact for the minix fs except that it counts zero blocks.
+ * Everything is in units of BLOCK_SIZE until the assignment to
+ * tmp.st_blksize.
+ */
+#define D_B 7
+#define I_B (BLOCK_SIZE / sizeof(unsigned short))
+
+ if (!inode->i_blksize) {
+ blocks = (tmp.st_size + BLOCK_SIZE - 1) / BLOCK_SIZE;
+ if (blocks > D_B) {
+ indirect = (blocks - D_B + I_B - 1) / I_B;
+ blocks += indirect;
+ if (indirect > 1) {
+ indirect = (indirect - 1 + I_B - 1) / I_B;
+ blocks += indirect;
+ if (indirect > 1)
+ blocks++;
+ }
+ }
+ tmp.st_blocks = (BLOCK_SIZE / 512) * blocks;
+ tmp.st_blksize = BLOCK_SIZE;
+ } else {
+ tmp.st_blocks = inode->i_blocks;
+ tmp.st_blksize = inode->i_blksize;
+ }
+ return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
+}
+
+asmlinkage long
+ia64_oldstat (char *filename, struct ia64_oldstat *statbuf)
+{
+ struct nameidata nd;
+ int error;
+
+ lock_kernel();
+ error = user_path_walk(filename, &nd);
+ if (!error) {
+ error = do_revalidate(nd.dentry);
+ if (!error)
+ error = cp_ia64_old_stat(nd.dentry->d_inode, statbuf);
+ path_release(&nd);
+ }
+ unlock_kernel();
+ return error;
+}
+
+
+asmlinkage long
+ia64_oldlstat (char *filename, struct ia64_oldstat *statbuf) {
+ struct nameidata nd;
+ int error;
+
+ lock_kernel();
+ error = user_path_walk_link(filename, &nd);
+ if (!error) {
+ error = do_revalidate(nd.dentry);
+ if (!error)
+ error = cp_ia64_old_stat(nd.dentry->d_inode, statbuf);
+ path_release(&nd);
+ }
+ unlock_kernel();
+ return error;
+}
+
+asmlinkage long
+ia64_oldfstat (unsigned int fd, struct ia64_oldstat *statbuf)
+{
+ struct file * f;
+ int err = -EBADF;
+
+ lock_kernel();
+ f = fget(fd);
+ if (f) {
+ struct dentry * dentry = f->f_dentry;
+
+ err = do_revalidate(dentry);
+ if (!err)
+ err = cp_ia64_old_stat(dentry->d_inode, statbuf);
+ fput(f);
+ }
+ unlock_kernel();
+ return err;
+}
+
+#endif
+
#ifndef CONFIG_PCI
asmlinkage long
@@ -212,5 +370,4 @@ sys_pciconfig_write (unsigned long bus, unsigned long dfn, unsigned long off, un
return -ENOSYS;
}
-
#endif /* CONFIG_PCI */
diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
index b88855ce4..d14ba0031 100644
--- a/arch/ia64/kernel/time.c
+++ b/arch/ia64/kernel/time.c
@@ -34,7 +34,10 @@ unsigned long last_cli_ip;
static struct {
unsigned long delta;
- unsigned long next[NR_CPUS];
+ union {
+ unsigned long count;
+ unsigned char pad[SMP_CACHE_BYTES];
+ } next[NR_CPUS];
} itm;
static void
@@ -69,16 +72,27 @@ do_profile (unsigned long ip)
static inline unsigned long
gettimeoffset (void)
{
- unsigned long now = ia64_get_itc();
- unsigned long elapsed_cycles, lost;
-
- elapsed_cycles = now - (itm.next[smp_processor_id()] - itm.delta);
-
- lost = lost_ticks;
- if (lost)
- elapsed_cycles += lost*itm.delta;
-
+#ifdef CONFIG_SMP
+ /*
+ * The code below doesn't work for SMP because only CPU 0
+ * keeps track of the time.
+ */
+ return 0;
+#else
+ unsigned long now = ia64_get_itc(), last_tick;
+ unsigned long elapsed_cycles, lost = lost_ticks;
+
+ last_tick = (itm.next[smp_processor_id()].count - (lost+1)*itm.delta);
+# if 1
+ if ((long) (now - last_tick) < 0) {
+ printk("Yikes: now < last_tick (now=0x%lx,last_tick=%lx)! No can do.\n",
+ now, last_tick);
+ return 0;
+ }
+# endif
+ elapsed_cycles = now - last_tick;
return (elapsed_cycles*my_cpu_data.usec_per_cyc) >> IA64_USEC_PER_CYC_SHIFT;
+#endif
}
void
@@ -137,6 +151,7 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
static unsigned long last_time;
static unsigned char count;
int cpu = smp_processor_id();
+ unsigned long new_itm;
int printed = 0;
/*
@@ -146,6 +161,12 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
* xtime_lock.
*/
write_lock(&xtime_lock);
+ new_itm = itm.next[cpu].count;
+
+ if (!time_after(ia64_get_itc(), new_itm))
+ printk("Oops: timer tick before it's due (itc=%lx,itm=%lx)\n",
+ ia64_get_itc(), new_itm);
+
while (1) {
/*
* Do kernel PC profiling here. We multiply the
@@ -164,18 +185,10 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
do_timer(regs);
#endif
- itm.next[cpu] += itm.delta;
- /*
- * There is a race condition here: to be on the "safe"
- * side, we process timer ticks until itm.next is
- * ahead of the itc by at least half the timer
- * interval. This should give us enough time to set
- * the new itm value without losing a timer tick.
- */
- if (time_after(itm.next[cpu], ia64_get_itc() + itm.delta/2)) {
- ia64_set_itm(itm.next[cpu]);
+ new_itm += itm.delta;
+ itm.next[cpu].count = new_itm;
+ if (time_after(new_itm, ia64_get_itc()))
break;
- }
#if !(defined(CONFIG_IA64_SOFTSDV_HACKS) && defined(CONFIG_SMP))
/*
@@ -188,28 +201,39 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
last_time = jiffies;
if (!printed) {
printk("Lost clock tick on CPU %d (now=%lx, next=%lx)!!\n",
- cpu, ia64_get_itc(), itm.next[cpu]);
+ cpu, ia64_get_itc(), itm.next[cpu].count);
printed = 1;
- }
# ifdef CONFIG_IA64_DEBUG_IRQ
- printk("last_cli_ip=%lx\n", last_cli_ip);
+ printk("last_cli_ip=%lx\n", last_cli_ip);
# endif
+ }
}
#endif
}
write_unlock(&xtime_lock);
+
+ /*
+ * If we're too close to the next clock tick for comfort, we
+ * increase the saftey margin by intentionally dropping the
+ * next tick(s). We do NOT update itm.next accordingly
+ * because that would force us to call do_timer() which in
+ * turn would let our clock run too fast (with the potentially
+ * devastating effect of losing monotony of time).
+ */
+ while (!time_after(new_itm, ia64_get_itc() + itm.delta/2))
+ new_itm += itm.delta;
+ ia64_set_itm(new_itm);
}
-#ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC
+#if defined(CONFIG_ITANIUM_ASTEP_SPECIFIC) || defined(CONFIG_IA64_SOFTSDV_HACKS)
-void
+/*
+ * Interrupts must be disabled before calling this routine.
+ */
+void
ia64_reset_itm (void)
{
- unsigned long flags;
-
- local_irq_save(flags);
timer_interrupt(0, 0, ia64_task_regs(current));
- local_irq_restore(flags);
}
#endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC */
@@ -220,11 +244,14 @@ ia64_reset_itm (void)
void __init
ia64_cpu_local_tick(void)
{
+#ifdef CONFIG_IA64_SOFTSDV_HACKS
+ ia64_set_itc(0);
+#endif
+
/* arrange for the cycle counter to generate a timer interrupt: */
ia64_set_itv(TIMER_IRQ, 0);
- ia64_set_itc(0);
- itm.next[smp_processor_id()] = ia64_get_itc() + itm.delta;
- ia64_set_itm(itm.next[smp_processor_id()]);
+ itm.next[smp_processor_id()].count = ia64_get_itc() + itm.delta;
+ ia64_set_itm(itm.next[smp_processor_id()].count);
}
void __init
@@ -254,25 +281,7 @@ ia64_init_itm (void)
itc_ratio.num = 3;
itc_ratio.den = 1;
}
-#if defined(CONFIG_IA64_LION_HACKS)
- /* Our Lion currently returns base freq 104.857MHz, which
- ain't right (it really is 100MHz). */
- printk("SAL/PAL returned: base-freq=%lu, itc-ratio=%lu/%lu, proc-ratio=%lu/%lu\n",
- platform_base_freq, itc_ratio.num, itc_ratio.den,
- proc_ratio.num, proc_ratio.den);
- platform_base_freq = 100000000;
-#elif 0 && defined(CONFIG_IA64_BIGSUR_HACKS)
- /* BigSur with 991020 firmware returned itc-ratio=9/2 and base
- freq 75MHz, which wasn't right. The 991119 firmware seems
- to return the right values, so this isn't necessary
- anymore... */
- printk("SAL/PAL returned: base-freq=%lu, itc-ratio=%lu/%lu, proc-ratio=%lu/%lu\n",
- platform_base_freq, itc_ratio.num, itc_ratio.den,
- proc_ratio.num, proc_ratio.den);
- platform_base_freq = 100000000;
- proc_ratio.num = 5; proc_ratio.den = 1;
- itc_ratio.num = 5; itc_ratio.den = 1;
-#elif defined(CONFIG_IA64_SOFTSDV_HACKS)
+#ifdef CONFIG_IA64_SOFTSDV_HACKS
platform_base_freq = 10000000;
proc_ratio.num = 4; proc_ratio.den = 1;
itc_ratio.num = 4; itc_ratio.den = 1;
@@ -290,8 +299,9 @@ ia64_init_itm (void)
itc_freq = (platform_base_freq*itc_ratio.num)/itc_ratio.den;
itm.delta = itc_freq / HZ;
- printk("timer: base freq=%lu.%03luMHz, ITC ratio=%lu/%lu, ITC freq=%lu.%03luMHz\n",
- platform_base_freq / 1000000, (platform_base_freq / 1000) % 1000,
+ printk("timer: CPU %d base freq=%lu.%03luMHz, ITC ratio=%lu/%lu, ITC freq=%lu.%03luMHz\n",
+ smp_processor_id(),
+ platform_base_freq / 1000000, (platform_base_freq / 1000) % 1000,
itc_ratio.num, itc_ratio.den, itc_freq / 1000000, (itc_freq / 1000) % 1000);
my_cpu_data.proc_freq = (platform_base_freq*proc_ratio.num)/proc_ratio.den;
@@ -313,6 +323,8 @@ void __init
time_init (void)
{
/* we can't do request_irq() here because the kmalloc() would fail... */
+ irq_desc[TIMER_IRQ].status |= IRQ_PER_CPU;
+ irq_desc[TIMER_IRQ].handler = &irq_type_ia64_sapic;
setup_irq(TIMER_IRQ, &timer_irqaction);
efi_gettimeofday(&xtime);
diff --git a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c
index 3a7706a27..4003b20f1 100644
--- a/arch/ia64/kernel/traps.c
+++ b/arch/ia64/kernel/traps.c
@@ -3,8 +3,12 @@
*
* Copyright (C) 1998-2000 Hewlett-Packard Co
* Copyright (C) 1998-2000 David Mosberger-Tang <davidm@hpl.hp.com>
+ *
+ * 05/12/00 grao <goutham.rao@intel.com> : added isr in siginfo for SIGFPE
*/
+#define FPSWA_DEBUG 1
+
/*
* The fpu_fault() handler needs to be able to access and update all
* floating point registers. Those saved in pt_regs can be accessed
@@ -168,7 +172,7 @@ ia64_bad_break (unsigned long break_num, struct pt_regs *regs)
siginfo.si_signo = sig;
siginfo.si_errno = 0;
siginfo.si_code = code;
- send_sig_info(sig, &siginfo, current);
+ force_sig_info(sig, &siginfo, current);
}
/*
@@ -300,6 +304,7 @@ handle_fpu_swa (int fp_fault, struct pt_regs *regs, unsigned long isr)
if (copy_from_user(bundle, (void *) fault_ip, sizeof(bundle)))
return -1;
+#ifdef FPSWA_DEBUG
if (fpu_swa_count > 5 && jiffies - last_time > 5*HZ)
fpu_swa_count = 0;
if (++fpu_swa_count < 5) {
@@ -307,7 +312,7 @@ handle_fpu_swa (int fp_fault, struct pt_regs *regs, unsigned long isr)
printk("%s(%d): floating-point assist fault at ip %016lx\n",
current->comm, current->pid, regs->cr_iip + ia64_psr(regs)->ri);
}
-
+#endif
exception = fp_emulate(fp_fault, bundle, &regs->cr_ipsr, &regs->ar_fpsr, &isr, &regs->pr,
&regs->cr_ifs, regs);
if (fp_fault) {
@@ -331,7 +336,8 @@ handle_fpu_swa (int fp_fault, struct pt_regs *regs, unsigned long isr)
} else if (isr & 0x44) {
siginfo.si_code = FPE_FLTDIV;
}
- send_sig_info(SIGFPE, &siginfo, current);
+ siginfo.si_isr = isr;
+ force_sig_info(SIGFPE, &siginfo, current);
}
} else {
if (exception == -1) {
@@ -350,12 +356,49 @@ handle_fpu_swa (int fp_fault, struct pt_regs *regs, unsigned long isr)
} else if (isr & 0x2200) {
siginfo.si_code = FPE_FLTRES;
}
- send_sig_info(SIGFPE, &siginfo, current);
+ siginfo.si_isr = isr;
+ force_sig_info(SIGFPE, &siginfo, current);
}
}
return 0;
}
+struct illegal_op_return {
+ unsigned long fkt, arg1, arg2, arg3;
+};
+
+struct illegal_op_return
+ia64_illegal_op_fault (unsigned long ec, unsigned long arg1, unsigned long arg2,
+ unsigned long arg3, unsigned long arg4, unsigned long arg5,
+ unsigned long arg6, unsigned long arg7, unsigned long stack)
+{
+ struct pt_regs *regs = (struct pt_regs *) &stack;
+ struct illegal_op_return rv;
+ struct siginfo si;
+ char buf[128];
+
+#ifdef CONFIG_IA64_BRL_EMU
+ {
+ extern struct illegal_op_return ia64_emulate_brl (struct pt_regs *, unsigned long);
+
+ rv = ia64_emulate_brl(regs, ec);
+ if (rv.fkt != (unsigned long) -1)
+ return rv;
+ }
+#endif
+
+ sprintf(buf, "IA-64 Illegal operation fault");
+ die_if_kernel(buf, regs, 0);
+
+ memset(&si, 0, sizeof(si));
+ si.si_signo = SIGILL;
+ si.si_code = ILL_ILLOPC;
+ si.si_addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri);
+ force_sig_info(SIGILL, &si, current);
+ rv.fkt = 0;
+ return rv;
+}
+
void
ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
unsigned long iim, unsigned long itir, unsigned long arg5,
@@ -450,11 +493,6 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
force_sig_info(SIGTRAP, &siginfo, current);
return;
- case 30: /* Unaligned fault */
- sprintf(buf, "Kernel unaligned trap accessing %016lx (ip=%016lx)!",
- ifa, regs->cr_iip + ia64_psr(regs)->ri);
- break;
-
case 32: /* fp fault */
case 33: /* fp trap */
result = handle_fpu_swa((vector == 32) ? 1 : 0, regs, isr);
diff --git a/arch/ia64/kernel/unaligned.c b/arch/ia64/kernel/unaligned.c
index 35e8cb846..a95b78f64 100644
--- a/arch/ia64/kernel/unaligned.c
+++ b/arch/ia64/kernel/unaligned.c
@@ -1,8 +1,8 @@
/*
* Architecture-specific unaligned trap handling.
*
- * Copyright (C) 1999 Hewlett-Packard Co
- * Copyright (C) 1999 Stephane Eranian <eranian@hpl.hp.com>
+ * Copyright (C) 1999-2000 Hewlett-Packard Co
+ * Copyright (C) 1999-2000 Stephane Eranian <eranian@hpl.hp.com>
*/
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -460,32 +460,15 @@ setfpreg(unsigned long regnum, struct ia64_fpreg *fpval, struct pt_regs *regs)
* enabled.
*
* The registers [32-127] are ususally saved in the tss. When get here,
- * they are NECESSARY live because they are only saved explicitely.
+ * they are NECESSARILY live because they are only saved explicitely.
* We have 3 ways of updating the values: force a save of the range
* in tss, use a gigantic switch/case statement or generate code on the
* fly to store to the right register.
* For now, we are using the (slow) save/restore way.
*/
if (regnum >= IA64_FIRST_ROTATING_FR) {
- /*
- * force a save of [32-127] to tss
- * we use the __() form to avoid fiddling with the dfh bit
- */
- __ia64_save_fpu(&current->thread.fph[0]);
-
+ ia64_sync_fph(current);
current->thread.fph[IA64_FPH_OFFS(regnum)] = *fpval;
-
- __ia64_load_fpu(&current->thread.fph[0]);
-
- /*
- * mark the high partition as being used now
- *
- * This is REQUIRED because the disabled_fph_fault() does
- * not set it, it's relying on the faulting instruction to
- * do it. In our case the faulty instruction never gets executed
- * completely, so we need to toggle the bit.
- */
- regs->cr_ipsr |= IA64_PSR_MFH;
} else {
/*
* pt_regs or switch_stack ?
@@ -544,15 +527,8 @@ getfpreg(unsigned long regnum, struct ia64_fpreg *fpval, struct pt_regs *regs)
* See discussion in setfpreg() for reasons and other ways of doing this.
*/
if (regnum >= IA64_FIRST_ROTATING_FR) {
-
- /*
- * force a save of [32-127] to tss
- * we use the__ia64_save_fpu() form to avoid fiddling with
- * the dfh bit.
- */
- __ia64_save_fpu(&current->thread.fph[0]);
-
- *fpval = current->thread.fph[IA64_FPH_OFFS(regnum)];
+ ia64_sync_fph(current);
+ *fpval = current->thread.fph[IA64_FPH_OFFS(regnum)];
} else {
/*
* f0 = 0.0, f1= 1.0. Those registers are constant and are thus
@@ -1410,6 +1386,25 @@ ia64_handle_unaligned(unsigned long ifa, struct pt_regs *regs)
die_if_kernel("Unaligned reference while in kernel\n", regs, 30);
/* NOT_REACHED */
}
+ /*
+ * For now, we don't support user processes running big-endian
+ * which do unaligned accesses
+ */
+ if (ia64_psr(regs)->be) {
+ struct siginfo si;
+
+ printk(KERN_ERR "%s(%d): big-endian unaligned access %016lx (ip=%016lx) not "
+ "yet supported\n",
+ current->comm, current->pid, ifa, regs->cr_iip + ipsr->ri);
+
+ si.si_signo = SIGBUS;
+ si.si_errno = 0;
+ si.si_code = BUS_ADRALN;
+ si.si_addr = (void *) ifa;
+ force_sig_info(SIGBUS, &si, current);
+ return;
+ }
+
if (current->thread.flags & IA64_THREAD_UAC_SIGBUS) {
struct siginfo si;
@@ -1417,7 +1412,7 @@ ia64_handle_unaligned(unsigned long ifa, struct pt_regs *regs)
si.si_errno = 0;
si.si_code = BUS_ADRALN;
si.si_addr = (void *) ifa;
- send_sig_info (SIGBUS, &si, current);
+ force_sig_info(SIGBUS, &si, current);
return;
}
diff --git a/arch/ia64/kernel/unwind.c b/arch/ia64/kernel/unwind.c
index c2b772e68..7f3c203ad 100644
--- a/arch/ia64/kernel/unwind.c
+++ b/arch/ia64/kernel/unwind.c
@@ -1,16 +1,1796 @@
/*
- * Copyright (C) 1999 Hewlett-Packard Co
- * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com>
+ * Copyright (C) 1999-2000 Hewlett-Packard Co
+ * Copyright (C) 1999-2000 David Mosberger-Tang <davidm@hpl.hp.com>
*/
+/*
+ * 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 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.
+ */
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/slab.h>
#include <asm/unwind.h>
+#ifdef CONFIG_IA64_NEW_UNWIND
+
+#include <asm/delay.h>
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/ptrace_offsets.h>
+#include <asm/rse.h>
+#include <asm/system.h>
+
+#include "entry.h"
+#include "unwind_i.h"
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#define p5 5
+
+/*
+ * The unwind tables are supposed to be sorted, but the GNU toolchain
+ * currently fails to produce a sorted table in the presence of
+ * functions that go into sections other than .text. For example, the
+ * kernel likes to put initialization code into .text.init, which
+ * messes up the sort order. Hopefully, this will get fixed sometime
+ * soon. --davidm 00/05/23
+ */
+#define UNWIND_TABLE_SORT_BUG
+
+#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_DEBUG 1
+#define UNW_STATS 0 /* WARNING: this disabled interrupts for long time-spans!! */
+
+#if UNW_DEBUG
+# define dprintk(format...) printk(format)
+# define inline
+#else
+# define dprintk(format...)
+#endif
+
+#if UNW_STATS
+# define STAT(x...) x
+#else
+# define STAT(x...)
+#endif
+
+#define alloc_reg_state() kmalloc(sizeof(struct unw_state_record), GFP_ATOMIC)
+#define free_reg_state(usr) kfree(usr)
+
+typedef unsigned long unw_word;
+typedef unsigned char unw_hash_index_t;
+
+#define struct_offset(str,fld) ((char *)&((str *)NULL)->fld - (char *) 0)
+
+static struct {
+ spinlock_t lock; /* spinlock for unwind data */
+
+ /* list of unwind tables (one per load-module) */
+ struct unw_table *tables;
+
+ /* 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];
+
+ /* unwind table for the kernel: */
+ struct unw_table kernel_table;
+
+ /* hash table that maps instruction pointer to script index: */
+ unw_hash_index_t hash[UNW_HASH_SIZE];
+
+ /* script cache: */
+ struct unw_script cache[UNW_CACHE_SIZE];
+
+# if 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: {
+ struct_offset(struct unw_frame_info, pri_unat)/8, /* PRI_UNAT_GR */
+ struct_offset(struct unw_frame_info, pri_unat)/8, /* PRI_UNAT_MEM */
+ struct_offset(struct unw_frame_info, pbsp)/8,
+ struct_offset(struct unw_frame_info, bspstore)/8,
+ struct_offset(struct unw_frame_info, pfs)/8,
+ struct_offset(struct unw_frame_info, rnat)/8,
+ struct_offset(struct unw_frame_info, psp)/8,
+ struct_offset(struct unw_frame_info, rp)/8,
+ struct_offset(struct unw_frame_info, r4)/8,
+ struct_offset(struct unw_frame_info, r5)/8,
+ struct_offset(struct unw_frame_info, r6)/8,
+ struct_offset(struct unw_frame_info, r7)/8,
+ struct_offset(struct unw_frame_info, unat)/8,
+ struct_offset(struct unw_frame_info, pr)/8,
+ struct_offset(struct unw_frame_info, lc)/8,
+ struct_offset(struct unw_frame_info, fpsr)/8,
+ struct_offset(struct unw_frame_info, b1)/8,
+ struct_offset(struct unw_frame_info, b2)/8,
+ struct_offset(struct unw_frame_info, b3)/8,
+ struct_offset(struct unw_frame_info, b4)/8,
+ struct_offset(struct unw_frame_info, b5)/8,
+ struct_offset(struct unw_frame_info, f2)/8,
+ struct_offset(struct unw_frame_info, f3)/8,
+ struct_offset(struct unw_frame_info, f4)/8,
+ struct_offset(struct unw_frame_info, f5)/8,
+ struct_offset(struct unw_frame_info, fr[16 - 16])/8,
+ struct_offset(struct unw_frame_info, fr[17 - 16])/8,
+ struct_offset(struct unw_frame_info, fr[18 - 16])/8,
+ struct_offset(struct unw_frame_info, fr[19 - 16])/8,
+ struct_offset(struct unw_frame_info, fr[20 - 16])/8,
+ struct_offset(struct unw_frame_info, fr[21 - 16])/8,
+ struct_offset(struct unw_frame_info, fr[22 - 16])/8,
+ struct_offset(struct unw_frame_info, fr[23 - 16])/8,
+ struct_offset(struct unw_frame_info, fr[24 - 16])/8,
+ struct_offset(struct unw_frame_info, fr[25 - 16])/8,
+ struct_offset(struct unw_frame_info, fr[26 - 16])/8,
+ struct_offset(struct unw_frame_info, fr[27 - 16])/8,
+ struct_offset(struct unw_frame_info, fr[28 - 16])/8,
+ struct_offset(struct unw_frame_info, fr[29 - 16])/8,
+ struct_offset(struct unw_frame_info, fr[30 - 16])/8,
+ struct_offset(struct unw_frame_info, fr[31 - 16])/8,
+ },
+ hash : { [0 ... UNW_HASH_SIZE - 1] = -1 },
+#if 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
+};
+
+
+/* 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) {
+ dprintk("unwind: trying to access non-existent r%u\n", 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:
+ nat_addr = &dummy_nat;
+ break;
+
+ case UNW_NAT_SCRATCH:
+ if (info->pri_unat)
+ nat_addr = info->pri_unat;
+ else
+ nat_addr = &info->sw->caller_unat;
+ case UNW_NAT_PRI_UNAT:
+ nat_mask = (1UL << ((long) addr & 0x1f8)/8);
+ break;
+
+ case UNW_NAT_STACKED:
+ nat_addr = ia64_rse_rnat_addr(addr);
+ if ((unsigned long) addr < info->regstk.limit
+ || (unsigned long) addr >= info->regstk.top)
+ {
+ dprintk("unwind: 0x%p outside of regstk "
+ "[0x%lx-0x%lx)\n", 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 */
+ if (info->flags & UNW_FLAG_INTERRUPT_FRAME)
+ pt = (struct pt_regs *) info->psp - 1;
+ else
+ pt = (struct pt_regs *) info->sp - 1;
+ if (regnum <= 3)
+ addr = &pt->r1 + (regnum - 1);
+ else if (regnum <= 11)
+ addr = &pt->r8 + (regnum - 8);
+ else if (regnum <= 15)
+ addr = &pt->r12 + (regnum - 12);
+ else
+ addr = &pt->r16 + (regnum - 16);
+ if (info->pri_unat)
+ nat_addr = info->pri_unat;
+ 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);
+ nat_addr = ia64_rse_rnat_addr(addr);
+ if ((unsigned long) addr < info->regstk.limit
+ || (unsigned long) addr >= info->regstk.top)
+ {
+ dprintk("unwind: ignoring attempt to access register outside of rbs\n");
+ 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) {
+ *addr = *val;
+ *nat_addr = (*nat_addr & ~nat_mask) | nat_mask;
+ } else {
+ *val = *addr;
+ *nat = (*nat_addr & nat_mask) != 0;
+ }
+ return 0;
+}
+
+int
+unw_access_br (struct unw_frame_info *info, int regnum, unsigned long *val, int write)
+{
+ unsigned long *addr;
+ struct pt_regs *pt;
+
+ if (info->flags & UNW_FLAG_INTERRUPT_FRAME)
+ pt = (struct pt_regs *) info->psp - 1;
+ else
+ pt = (struct pt_regs *) info->sp - 1;
+ switch (regnum) {
+ /* scratch: */
+ case 0: addr = &pt->b0; break;
+ case 6: addr = &pt->b6; break;
+ case 7: addr = &pt->b7; break;
+
+ /* preserved: */
+ case 1: case 2: case 3: case 4: case 5:
+ addr = *(&info->b1 + (regnum - 1));
+ if (!addr)
+ addr = &info->sw->b1 + (regnum - 1);
+ break;
+
+ default:
+ dprintk("unwind: trying to access non-existent b%u\n", regnum);
+ return -1;
+ }
+ if (write)
+ *addr = *val;
+ else
+ *val = *addr;
+ return 0;
+}
+
+int
+unw_access_fr (struct unw_frame_info *info, int regnum, struct ia64_fpreg *val, int write)
+{
+ struct ia64_fpreg *addr = 0;
+ struct pt_regs *pt;
+
+ if ((unsigned) (regnum - 2) >= 126) {
+ dprintk("unwind: trying to access non-existent f%u\n", regnum);
+ return -1;
+ }
+
+ if (info->flags & UNW_FLAG_INTERRUPT_FRAME)
+ pt = (struct pt_regs *) info->psp - 1;
+ else
+ pt = (struct pt_regs *) info->sp - 1;
+
+ if (regnum <= 5) {
+ addr = *(&info->f2 + (regnum - 2));
+ if (!addr)
+ addr = &info->sw->f2 + (regnum - 2);
+ } else if (regnum <= 15) {
+ if (regnum <= 9)
+ addr = &pt->f6 + (regnum - 6);
+ else
+ addr = &info->sw->f10 + (regnum - 10);
+ } else if (regnum <= 31) {
+ addr = info->fr[regnum - 16];
+ if (!addr)
+ addr = &info->sw->f16 + (regnum - 16);
+ } else {
+ struct task_struct *t = info->task;
+
+ ia64_sync_fph(t);
+ addr = t->thread.fph + (regnum - 32);
+ }
+
+ if (write)
+ *addr = *val;
+ else
+ *val = *addr;
+ return 0;
+}
+
+int
+unw_access_ar (struct unw_frame_info *info, int regnum, unsigned long *val, int write)
+{
+ unsigned long *addr;
+ struct pt_regs *pt;
+
+ if (info->flags & UNW_FLAG_INTERRUPT_FRAME)
+ pt = (struct pt_regs *) info->psp - 1;
+ else
+ pt = (struct pt_regs *) info->sp - 1;
+
+ switch (regnum) {
+ case UNW_AR_BSP:
+ addr = info->pbsp;
+ if (!addr)
+ addr = &info->sw->ar_bspstore;
+ break;
+
+ case UNW_AR_BSPSTORE:
+ addr = info->bspstore;
+ if (!addr)
+ addr = &info->sw->ar_bspstore;
+ break;
+
+ case UNW_AR_PFS:
+ addr = info->pfs;
+ if (!addr)
+ addr = &info->sw->ar_pfs;
+ break;
+
+ case UNW_AR_RNAT:
+ addr = info->rnat;
+ if (!addr)
+ addr = &info->sw->ar_rnat;
+ break;
+
+ case UNW_AR_UNAT:
+ addr = info->unat;
+ if (!addr)
+ addr = &info->sw->ar_unat;
+ break;
+
+ case UNW_AR_LC:
+ addr = info->lc;
+ if (!addr)
+ addr = &info->sw->ar_lc;
+ break;
+
+ case UNW_AR_EC:
+ if (!info->cfm)
+ return -1;
+ if (write)
+ *info->cfm = (*info->cfm & ~(0x3fUL << 52)) | ((*val & 0x3f) << 52);
+ else
+ *val = (*info->cfm >> 52) & 0x3f;
+ return 0;
+
+ case UNW_AR_FPSR:
+ addr = info->fpsr;
+ if (!addr)
+ addr = &info->sw->ar_fpsr;
+ break;
+
+ case UNW_AR_RSC:
+ addr = &pt->ar_rsc;
+ break;
+
+ case UNW_AR_CCV:
+ addr = &pt->ar_ccv;
+ break;
+
+ default:
+ dprintk("unwind: trying to access non-existent ar%u\n", regnum);
+ return -1;
+ }
+
+ if (write)
+ *addr = *val;
+ else
+ *val = *addr;
+ return 0;
+}
+
+inline int
+unw_access_pr (struct unw_frame_info *info, unsigned long *val, int write)
+{
+ unsigned long *addr;
+
+ addr = info->pr;
+ if (!addr)
+ addr = &info->sw->pr;
+
+ if (write)
+ *addr = *val;
+ else
+ *val = *addr;
+ return 0;
+}
+
+
+/* Unwind decoder routines */
+
+static inline void
+push (struct unw_state_record *sr)
+{
+ struct unw_reg_state *rs;
+
+ rs = alloc_reg_state();
+ memcpy(rs, &sr->curr, sizeof(*rs));
+ rs->next = sr->stack;
+ sr->stack = rs;
+}
+
+static void
+pop (struct unw_state_record *sr)
+{
+ struct unw_reg_state *rs;
+
+ if (!sr->stack) {
+ printk ("unwind: stack underflow!\n");
+ return;
+ }
+ rs = sr->stack;
+ sr->stack = rs->next;
+ free_reg_state(rs);
+}
+
+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;
+ }
+ dprintk("unwind: bad abreg=0x%x\n", 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;
+ reg->val = 0x10 - *offp;
+ *offp += regsize;
+ }
+ }
+}
+
+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;
+ }
+ }
+ dprintk("unwind: excess spill!\n");
+}
+
+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) sizeof(unw.save_order)/sizeof(unw.save_order[0]); ++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;
+ unsigned long 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;
+
+ if (!(sr->in_body || sr->first_region))
+ finish_prologue(sr);
+ sr->first_region = 0;
+
+ /* check if we're done: */
+ if (body && sr->when_target < sr->region_start + sr->region_len) {
+ sr->done = 1;
+ return;
+ }
+
+ for (i = 0; i < sr->epilogue_count; ++i)
+ pop(sr);
+ sr->epilogue_count = 0;
+ sr->epilogue_start = UNW_WHEN_NEVER;
+
+ if (!body)
+ push(sr);
+
+ sr->region_start += sr->region_len;
+ sr->region_len = rlen;
+ sr->in_body = body;
+
+ if (!body) {
+ 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 = 0;
+ 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 == 0 && context == 'i')
+ sr->flags |= UNW_FLAG_INTERRUPT_FRAME;
+ else
+ dprintk("unwind: ignoring unwabi(abi=0x%x,context=0x%x)\n", 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) {
+ 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_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((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((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((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_reg_state *rs;
+
+ for (rs = sr->reg_state_list; rs; rs = rs->next) {
+ if (rs->label == label) {
+ memcpy (&sr->curr, rs, sizeof(sr->curr));
+ return;
+ }
+ }
+ printk("unwind: failed to find state labelled 0x%lx\n", label);
+}
+
+static inline void
+desc_label_state (unw_word label, struct unw_state_record *sr)
+{
+ struct unw_reg_state *rs;
+
+ rs = alloc_reg_state();
+ memcpy(rs, &sr->curr, sizeof(*rs));
+ rs->label = label;
+ rs->next = sr->reg_state_list;
+ sr->reg_state_list = rs;
+}
+
+/*
+ * 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((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 = sr->region_start + MIN((int)t, sr->region_len - 1);
+ 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((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((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((int)t, sr->region_len - 1);
+ r->val = 4*spoff;
+}
+
+#define UNW_DEC_BAD_CODE(code) printk("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 magic 0x9e3779b97f4a7c16 /* (sqrt(5)/2-1)*2^64 */
+
+ return (ip >> 4)*magic >> (64 - UNW_LOG_HASH_SIZE);
+}
+
+static inline long
+cache_match (struct unw_script *script, unsigned long ip, unsigned long pr_val)
+{
+ read_lock(&script->lock);
+ if ((ip) == (script)->ip && (((pr_val) ^ (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 long ip, pr_val;
+
+ STAT(++unw.stat.cache.lookups);
+
+ ip = info->ip;
+ pr_val = info->pr_val;
+
+ if (cache_match(script, ip, pr_val)) {
+ STAT(++unw.stat.cache.hinted_hits);
+ return script;
+ }
+
+ script = unw.cache + unw.hash[hash(ip)];
+ while (1) {
+ if (cache_match(script, ip, pr_val)) {
+ /* 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 0;
+ 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;
+ unsigned long flags;
+ unsigned char index;
+ unsigned short head;
+
+ STAT(++unw.stat.script.news);
+
+ /*
+ * Can't (easily) use cmpxchg() here because of ABA problem
+ * that is intrinsic in cmpxchg()...
+ */
+ spin_lock_irqsave(&unw.lock, flags);
+ {
+ head = unw.lru_head;
+ script = unw.cache + head;
+ unw.lru_head = script->lru_chain;
+ }
+ spin_unlock(&unw.lock);
+
+ /*
+ * XXX We'll deadlock here if we interrupt a thread that is
+ * holding a read lock on script->lock. A try_write_lock()
+ * might be mighty handy here... Alternatively, we could
+ * disable interrupts whenever we hold a read-lock, but that
+ * seems silly.
+ */
+ write_lock(&script->lock);
+
+ spin_lock(&unw.lock);
+ {
+ /* 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): */
+ index = hash(script->ip);
+ tmp = unw.cache + unw.hash[index];
+ prev = 0;
+ 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);
+ }
+ spin_unlock_irqrestore(&unw.lock, flags);
+
+ 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) {
+ dprintk("unwind: script exceeds maximum size of %u instructions!\n",
+ 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;
+
+ 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_STACKED;
+ } else {
+ /* register got spilled to a scratch register */
+ opc = UNW_INSN_SETNAT_TYPE;
+ val = UNW_NAT_SCRATCH;
+ }
+ 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_PRI_UNAT;
+ val = 0;
+ break;
+
+ default:
+ dprintk("unwind: don't know how to emit nat info for where = %u\n", 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 {
+ opc = UNW_INSN_LOAD_SPREL;
+ val = -sizeof(struct pt_regs);
+ if (rval >= 1 && rval <= 3)
+ val += struct_offset(struct pt_regs, r1) + 8*(rval - 1);
+ else if (rval <= 11)
+ val += struct_offset(struct pt_regs, r8) + 8*(rval - 8);
+ else if (rval <= 15)
+ val += struct_offset(struct pt_regs, r12) + 8*(rval - 12);
+ else if (rval <= 31)
+ val += struct_offset(struct pt_regs, r16) + 8*(rval - 16);
+ else
+ dprintk("unwind: bad scratch reg r%lu\n", rval);
+ }
+ break;
+
+ case UNW_WHERE_FR:
+ if (rval <= 5)
+ val = unw.preg_index[UNW_REG_F2 + (rval - 1)];
+ else if (rval >= 16 && rval <= 31)
+ val = unw.preg_index[UNW_REG_F16 + (rval - 16)];
+ else {
+ opc = UNW_INSN_LOAD_SPREL;
+ val = -sizeof(struct pt_regs);
+ if (rval <= 9)
+ val += struct_offset(struct pt_regs, f6) + 16*(rval - 6);
+ else
+ dprintk("unwind: kernel may not touch f%lu\n", rval);
+ }
+ break;
+
+ case UNW_WHERE_BR:
+ if (rval >= 1 && rval <= 5)
+ val = unw.preg_index[UNW_REG_B1 + (rval - 1)];
+ else {
+ opc = UNW_INSN_LOAD_SPREL;
+ val = -sizeof(struct pt_regs);
+ if (rval == 0)
+ val += struct_offset(struct pt_regs, b0);
+ else if (rval == 6)
+ val += struct_offset(struct pt_regs, b6);
+ else
+ val += struct_offset(struct pt_regs, b7);
+ }
+ break;
+
+ case UNW_WHERE_SPREL:
+ opc = UNW_INSN_LOAD_SPREL;
+ break;
+
+ case UNW_WHERE_PSPREL:
+ opc = UNW_INSN_LOAD_PSPREL;
+ break;
+
+ default:
+ dprintk("unwind: register %u has unexpected `where' value of %u\n", 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);
+}
+
+static inline struct unw_table_entry *
+lookup (struct unw_table *table, unsigned long rel_ip)
+{
+ struct unw_table_entry *e = 0;
+ 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;
+ }
+ 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)
+{
+ struct unw_reg_state *rs, *next;
+ struct unw_table_entry *e = 0;
+ struct unw_script *script = 0;
+ 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_val;
+
+ script = script_new(ip);
+ if (!script) {
+ dprintk("unwind: failed to create unwind script\n");
+ STAT(unw.stat.script.build_time += ia64_get_itc() - start);
+ return 0;
+ }
+ 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) */
+ dprintk("unwind: no unwind info for ip=0x%lx (prev ip=0x%lx)\n", 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].where = UNW_WHERE_NONE;
+ sr.curr.reg[UNW_REG_PSP].val = 0;
+ 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->where = UNW_WHERE_NONE;
+ }
+
+ 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;
+ }
+
+#if UNW_DEBUG
+ printk ("unwind: state record for func 0x%lx, t=%u:\n",
+ 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) {
+ printk(" %s <- ", unw.preg_name[r - sr.curr.reg]);
+ switch (r->where) {
+ case UNW_WHERE_GR: printk("r%lu", r->val); break;
+ case UNW_WHERE_FR: printk("f%lu", r->val); break;
+ case UNW_WHERE_BR: printk("b%lu", r->val); break;
+ case UNW_WHERE_SPREL: printk("[sp+0x%lx]", r->val); break;
+ case UNW_WHERE_PSPREL: printk("[psp+0x%lx]", r->val); break;
+ case UNW_WHERE_NONE:
+ printk("%s+0x%lx", unw.preg_name[r - sr.curr.reg], r->val);
+ break;
+ default: printk("BADWHERE(%d)", r->where); break;
+ }
+ printk ("\t\t%d\n", r->when);
+ }
+ }
+#endif
+
+ STAT(unw.stat.script.parse_time += ia64_get_itc() - parse_start);
+
+ /* translate state record into unwinder instructions: */
+
+ if (sr.curr.reg[UNW_REG_PSP].where == UNW_WHERE_NONE
+ && sr.when_target > sr.curr.reg[UNW_REG_PSP].when && sr.curr.reg[UNW_REG_PSP].val != 0)
+ {
+ /* new psp is sp plus frame size */
+ insn.opc = UNW_INSN_ADD;
+ insn.dst = unw.preg_index[UNW_REG_PSP];
+ insn.val = sr.curr.reg[UNW_REG_PSP].val;
+ 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 labelled register states & stack: */
+
+ STAT(parse_start = ia64_get_itc());
+ for (rs = sr.reg_state_list; rs; rs = next) {
+ next = rs->next;
+ free_reg_state(rs);
+ }
+ while (sr.stack)
+ pop(&sr);
+ 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_STACKED:
+ s[dst] = (unsigned long) ia64_rse_skip_regs((unsigned long *)state->bsp,
+ val);
+ break;
+
+ case UNW_INSN_LOAD_PSPREL:
+ s[dst] = state->psp + val;
+ break;
+
+ case UNW_INSN_LOAD_SPREL:
+ s[dst] = state->sp + val;
+ break;
+
+ case UNW_INSN_SETNAT_PRI_UNAT:
+ if (!state->pri_unat)
+ state->pri_unat = &state->sw->caller_unat;
+ s[dst+1] = ((*state->pri_unat - s[dst]) << 32) | UNW_NAT_PRI_UNAT;
+ break;
+
+ case UNW_INSN_SETNAT_TYPE:
+ s[dst+1] = 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 >= struct_offset (struct unw_frame_info, r4)
+ && off <= struct_offset (struct unw_frame_info, r7))
+ /*
+ * We're initializing a general register: init NaT info, too. Note that we
+ * rely on the fact that call_unat is the first field in struct switch_stack:
+ */
+ s[val+1] = (-off << 32) | UNW_NAT_PRI_UNAT;
+ goto redo;
+}
+
+static int
+find_save_locs (struct unw_frame_info *info)
+{
+ int have_write_lock = 0;
+ struct unw_script *scr;
+
+ if ((info->ip & (my_cpu_data.unimpl_va_mask | 0xf))
+ || REGION_NUMBER(info->ip) != REGION_KERNEL)
+ {
+ /* don't let obviously bad addresses pollute the cache */
+ dprintk("unwind: rejecting bad ip=0x%lx\n", info->ip);
+ info->rp = 0;
+ return -1;
+ }
+
+ scr = script_lookup(info);
+ if (!scr) {
+ scr = build_script(info);
+ if (!scr) {
+ dprintk("unwind: failed to locate/build unwind script for ip %lx\n",
+ 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);
+ 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) {
+ dprintk("unwind: failed to locate return link (ip=0x%lx)!\n", info->ip);
+ STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags));
+ return -1;
+ }
+ ip = info->ip = *info->rp;
+ if (ip < GATE_ADDR + PAGE_SIZE) {
+ /*
+ * We don't have unwind info for the gate page, so we consider that part
+ * of user-space for the purpose of unwinding.
+ */
+ dprintk("unwind: reached user-space (ip=0x%lx)\n", ip);
+ STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags));
+ return -1;
+ }
+
+ /* restore the cfm: */
+ if (!info->pfs) {
+ dprintk("unwind: failed to locate ar.pfs!\n");
+ STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags));
+ return -1;
+ }
+ info->cfm = info->pfs;
+
+ /* restore the bsp: */
+ pr = info->pr_val;
+ num_regs = 0;
+ if ((info->flags & UNW_FLAG_INTERRUPT_FRAME)) {
+ if ((pr & (1UL << pNonSys)) != 0)
+ num_regs = *info->cfm & 0x7f; /* size of frame */
+ info->pfs =
+ (unsigned long *) (info->sp + 16 + struct_offset(struct pt_regs, ar_pfs));
+ } else
+ num_regs = (*info->cfm >> 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) {
+ dprintk("unwind: bsp (0x%lx) out of range [0x%lx-0x%lx]\n",
+ 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) {
+ dprintk("unwind: sp (0x%lx) out of range [0x%lx-0x%lx]\n",
+ info->sp, info->regstk.top, info->regstk.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) {
+ dprintk("unwind: ip, sp, bsp remain unchanged; stopping here (ip=0x%lx)\n", ip);
+ STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags));
+ return -1;
+ }
+
+ /* finally, restore the predicates: */
+ unw_get_pr(info, &info->pr_val);
+
+ retval = find_save_locs(info);
+ STAT(unw.stat.api.unwind_time += ia64_get_itc() - start; local_irq_restore(flags));
+ return retval;
+}
+
+int
+unw_unwind_to_user (struct unw_frame_info *info)
+{
+ unsigned long ip;
+
+ while (unw_unwind(info) >= 0) {
+ if (unw_get_rp(info, &ip) < 0) {
+ unw_get_ip(info, &ip);
+ dprintk("unwind: failed to read return pointer (ip=0x%lx)\n", ip);
+ return -1;
+ }
+ /*
+ * We don't have unwind info for the gate page, so we consider that part
+ * of user-space for the purpose of unwinding.
+ */
+ if (ip < GATE_ADDR + PAGE_SIZE)
+ return 0;
+ }
+ unw_get_ip(info, &ip);
+ dprintk("unwind: failed to unwind to user-level (ip=0x%lx)\n", ip);
+ return -1;
+}
+
+void
+unw_init_frame_info (struct unw_frame_info *info, struct task_struct *t, struct switch_stack *sw)
+{
+ unsigned long rbslimit, rbstop, stklimit, stktop, sol;
+ 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;
+ stktop = (unsigned long) sw - 16;
+ 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 = (unsigned long) (sw + 1) - 16;
+ info->cfm = &sw->ar_pfs;
+ sol = (*info->cfm >> 7) & 0x7f;
+ info->bsp = (unsigned long) ia64_rse_skip_regs((unsigned long *) info->regstk.top, -sol);
+ info->ip = sw->b0;
+ info->pr_val = sw->pr;
+
+ find_save_locs(info);
+ STAT(unw.stat.api.init_time += ia64_get_itc() - start; local_irq_restore(flags));
+}
+
+#endif /* CONFIG_IA64_NEW_UNWIND */
+
void
-ia64_unwind_init_from_blocked_task (struct ia64_frame_info *info, struct task_struct *t)
+unw_init_from_blocked_task (struct unw_frame_info *info, struct task_struct *t)
{
struct switch_stack *sw = (struct switch_stack *) (t->thread.ksp + 16);
+
+#ifdef CONFIG_IA64_NEW_UNWIND
+ unw_init_frame_info(info, t, sw);
+#else
unsigned long sol, limit, top;
memset(info, 0, sizeof(*info));
@@ -22,17 +1802,25 @@ ia64_unwind_init_from_blocked_task (struct ia64_frame_info *info, struct task_st
if (top - (unsigned long) t >= IA64_STK_OFFSET)
top = limit;
- info->regstk.limit = (unsigned long *) limit;
- info->regstk.top = (unsigned long *) top;
- info->bsp = ia64_rse_skip_regs(info->regstk.top, -sol);
- info->top_rnat = sw->ar_rnat;
- info->cfm = sw->ar_pfs;
- info->ip = sw->b0;
+ info->regstk.limit = limit;
+ info->regstk.top = top;
+ info->sw = sw;
+ info->bsp = (unsigned long) ia64_rse_skip_regs((unsigned long *) info->regstk.top, -sol);
+ info->cfm = &sw->ar_pfs;
+ info->ip = sw->b0;
+#endif
}
void
-ia64_unwind_init_from_current (struct ia64_frame_info *info, struct pt_regs *regs)
+unw_init_from_current (struct unw_frame_info *info, struct pt_regs *regs)
{
+#ifdef CONFIG_IA64_NEW_UNWIND
+ struct switch_stack *sw = (struct switch_stack *) regs - 1;
+
+ unw_init_frame_info(info, current, sw);
+ /* skip over interrupt frame: */
+ unw_unwind(info);
+#else
struct switch_stack *sw = (struct switch_stack *) regs - 1;
unsigned long sol, sof, *bsp, limit, top;
@@ -44,34 +1832,40 @@ ia64_unwind_init_from_current (struct ia64_frame_info *info, struct pt_regs *reg
memset(info, 0, sizeof(*info));
sol = (sw->ar_pfs >> 7) & 0x7f; /* size of frame */
- info->regstk.limit = (unsigned long *) limit;
- info->regstk.top = (unsigned long *) top;
- info->top_rnat = sw->ar_rnat;
/* this gives us the bsp top level frame (kdb interrupt frame): */
bsp = ia64_rse_skip_regs((unsigned long *) top, -sol);
/* now skip past the interrupt frame: */
sof = regs->cr_ifs & 0x7f; /* size of frame */
- info->cfm = regs->cr_ifs;
- info->bsp = ia64_rse_skip_regs(bsp, -sof);
+
+ info->regstk.limit = limit;
+ info->regstk.top = top;
+ info->sw = sw;
+ info->bsp = (unsigned long) ia64_rse_skip_regs(bsp, -sof);
+ info->cfm = &regs->cr_ifs;
info->ip = regs->cr_iip;
+#endif
}
+#ifndef CONFIG_IA64_NEW_UNWIND
+
static unsigned long
-read_reg (struct ia64_frame_info *info, int regnum, int *is_nat)
+read_reg (struct unw_frame_info *info, int regnum, int *is_nat)
{
unsigned long *addr, *rnat_addr, rnat;
- addr = ia64_rse_skip_regs(info->bsp, regnum);
- if (addr < info->regstk.limit || addr >= info->regstk.top || ((long) addr & 0x7) != 0) {
+ addr = ia64_rse_skip_regs((unsigned long *) info->bsp, regnum);
+ if ((unsigned long) addr < info->regstk.limit
+ || (unsigned long) addr >= info->regstk.top || ((long) addr & 0x7) != 0)
+ {
*is_nat = 1;
return 0xdeadbeefdeadbeef;
}
rnat_addr = ia64_rse_rnat_addr(addr);
- if (rnat_addr >= info->regstk.top)
- rnat = info->top_rnat;
+ if ((unsigned long) rnat_addr >= info->regstk.top)
+ rnat = info->sw->ar_rnat;
else
rnat = *rnat_addr;
*is_nat = (rnat & (1UL << ia64_rse_slot_num(addr))) != 0;
@@ -83,9 +1877,9 @@ read_reg (struct ia64_frame_info *info, int regnum, int *is_nat)
* store for r32.
*/
int
-ia64_unwind_to_previous_frame (struct ia64_frame_info *info)
+unw_unwind (struct unw_frame_info *info)
{
- unsigned long sol, cfm = info->cfm;
+ unsigned long sol, cfm = *info->cfm;
int is_nat;
sol = (cfm >> 7) & 0x7f; /* size of locals */
@@ -103,16 +1897,187 @@ ia64_unwind_to_previous_frame (struct ia64_frame_info *info)
return -1;
info->ip = read_reg(info, sol - 2, &is_nat);
- if (is_nat)
+ if (is_nat || (info->ip & (my_cpu_data.unimpl_va_mask | 0xf)))
+ /* reject let obviously bad addresses */
return -1;
+ info->cfm = ia64_rse_skip_regs((unsigned long *) info->bsp, sol - 1);
cfm = read_reg(info, sol - 1, &is_nat);
if (is_nat)
return -1;
sol = (cfm >> 7) & 0x7f;
- info->cfm = cfm;
- info->bsp = ia64_rse_skip_regs(info->bsp, -sol);
+ info->bsp = (unsigned long) ia64_rse_skip_regs((unsigned long *) info->bsp, -sol);
return 0;
}
+#endif /* !CONFIG_IA64_NEW_UNWIND */
+
+#ifdef CONFIG_IA64_NEW_UNWIND
+
+static void
+init_unwind_table (struct unw_table *table, const char *name, unsigned long segment_base,
+ unsigned long gp, void *table_start, void *table_end)
+{
+ struct unw_table_entry *start = table_start, *end = table_end;
+
+#ifdef UNWIND_TABLE_SORT_BUG
+ {
+ struct unw_table_entry *e1, *e2, tmp;
+
+ /* stupid bubble sort... */
+
+ for (e1 = start; e1 < end; ++e1) {
+ for (e2 = e1 + 1; e2 < end; ++e2) {
+ if (e2->start_offset < e1->start_offset) {
+ tmp = *e1;
+ *e1 = *e2;
+ *e2 = tmp;
+ }
+ }
+ }
+ }
+#endif
+ 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;
+}
+
+void *
+unw_add_unwind_table (const char *name, unsigned long segment_base, unsigned long gp,
+ void *table_start, void *table_end)
+{
+ struct unw_table_entry *start = table_start, *end = table_end;
+ struct unw_table *table;
+ unsigned long flags;
+
+ if (end - start <= 0) {
+ dprintk("unwind: ignoring attempt to insert empty unwind table\n");
+ return 0;
+ }
+
+ table = kmalloc(sizeof(*table), GFP_USER);
+ if (!table)
+ return 0;
+
+ 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, *prevt;
+ struct unw_script *tmp, *prev;
+ unsigned long flags;
+ long index;
+
+ if (!handle) {
+ dprintk("unwind: ignoring attempt to remove non-existent unwind table\n");
+ return;
+ }
+
+ table = handle;
+ if (table == &unw.kernel_table) {
+ dprintk("unwind: sorry, freeing the kernel's unwind table is a no-can-do!\n");
+ return;
+ }
+
+ spin_lock_irqsave(&unw.lock, flags);
+ {
+ /* first, delete the table: */
+
+ for (prevt = (struct unw_table *) &unw.tables; prevt; prevt = prevt->next)
+ if (prevt->next == table)
+ break;
+ if (!prevt) {
+ dprintk("unwind: failed to find unwind table %p\n", table);
+ spin_unlock_irqrestore(&unw.lock, flags);
+ return;
+ }
+ prevt->next = table->next;
+
+ /* next, remove hash table entries for this table */
+
+ for (index = 0; index <= UNW_HASH_SIZE; ++index) {
+ if (unw.hash[index] >= UNW_CACHE_SIZE)
+ continue;
+
+ tmp = unw.cache + unw.hash[index];
+ prev = 0;
+ while (1) {
+ write_lock(&tmp->lock);
+ {
+ if (tmp->ip >= table->start && tmp->ip < table->end) {
+ if (prev)
+ prev->coll_chain = tmp->coll_chain;
+ else
+ unw.hash[index] = -1;
+ tmp->ip = 0;
+ } else
+ prev = tmp;
+ }
+ write_unlock(&tmp->lock);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&unw.lock, flags);
+
+ kfree(table);
+}
+#endif /* CONFIG_IA64_NEW_UNWIND */
+
+void
+unw_init (void)
+{
+#ifdef CONFIG_IA64_NEW_UNWIND
+ extern int ia64_unw_start, ia64_unw_end, __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(AR_UNAT);
+ unw.sw_off[unw.preg_index[UNW_REG_BSPSTORE]] = SW(AR_BSPSTORE);
+ unw.sw_off[unw.preg_index[UNW_REG_PFS]] = SW(AR_UNAT);
+ unw.sw_off[unw.preg_index[UNW_REG_RP]] = SW(B0);
+ unw.sw_off[unw.preg_index[UNW_REG_UNAT]] = SW(AR_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;
+
+ unw.cache[0].coll_chain = -1;
+ for (i = 1; i < UNW_CACHE_SIZE; ++i) {
+ unw.cache[i].lru_chain = (i - 1);
+ unw.cache[i].coll_chain = -1;
+ unw.cache[i].lock = RW_LOCK_UNLOCKED;
+ }
+ unw.lru_head = UNW_CACHE_SIZE - 1;
+ unw.lru_tail = 0;
+
+ init_unwind_table(&unw.kernel_table, "kernel", KERNEL_START, (unsigned long) &__gp,
+ &ia64_unw_start, &ia64_unw_end);
+#endif /* CONFIG_IA64_NEW_UNWIND */
+}
diff --git a/arch/ia64/kernel/unwind_decoder.c b/arch/ia64/kernel/unwind_decoder.c
new file mode 100644
index 000000000..50ac2d82f
--- /dev/null
+++ b/arch/ia64/kernel/unwind_decoder.c
@@ -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 --git a/arch/ia64/kernel/unwind_i.h b/arch/ia64/kernel/unwind_i.h
new file mode 100644
index 000000000..fea655efd
--- /dev/null
+++ b/arch/ia64/kernel/unwind_i.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2000 Hewlett-Packard Co
+ * Copyright (C) 2000 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_entry {
+ u64 start_offset;
+ u64 end_offset;
+ u64 info_offset;
+};
+
+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;
+ 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_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 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_reg_state {
+ struct unw_reg_state *next;
+ unsigned long label; /* label of this state record */
+ struct unw_reg_info reg[UNW_NUM_REGS];
+ } curr, *stack, *reg_state_list;
+};
+
+enum unw_nat_type {
+ UNW_NAT_NONE, /* NaT not represented */
+ UNW_NAT_VAL, /* NaT represented by NaT value (fp reg) */
+ UNW_NAT_PRI_UNAT, /* NaT value is in unat word at offset OFF */
+ UNW_NAT_SCRATCH, /* NaT value is in scratch.pri_unat */
+ UNW_NAT_STACKED /* NaT is in rnat */
+};
+
+enum unw_insn_opcode {
+ UNW_INSN_ADD, /* s[dst] += 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_LOAD_PSPREL, /* s[dst] = *(*s.psp + 8*val) */
+ UNW_INSN_LOAD_SPREL, /* s[dst] = *(*s.sp + 8*val) */
+ UNW_INSN_SETNAT_PRI_UNAT, /* s[dst+1].nat.type = PRI_UNAT;
+ s[dst+1].nat.off = *s.pri_unat - s[dst] */
+ UNW_INSN_SETNAT_TYPE /* s[dst+1].nat.type = val */
+};
+
+struct unw_insn {
+ unsigned int opc : 4;
+ unsigned int dst : 9;
+ signed int val : 19;
+};
+
+/*
+ * Preserved general static registers (r2-r5) 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];
+};