Ia64 kdump patch

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The ia64 kdump patch is in 2 parts.

the kexec-kdump-ia64-2.6.16.patch should apply on top of the previous 
kexec patch by Khalid in Tony's test tree.

the kexec-tools-kdump-ia64.patch should apply to kexec-tools-1.101
with kexec-tools-1.101-kdump.patch


To test it.
Build first SMP kernel with KEXEC and KDUMP enabled.

Boot it with kernel parameter "crashkernel=XXX@YYY"
means reserver XXX from YYY for crashdumping.
Build an UP kernel with KEXEC KDUMP VMCORE enabled.
load this kernel as a crashdumping kernel
kexec -p vmlinux.gz --initrd=initrd --append="...."

trigger a crash,
maybe "echo c > /proc/sysrq-trigger"
after the crash kernel boots,
cp /proc/vmcore core

gdb first_kernel_vmlinux core

please test and review.

Signed-off-by: Khalid Aziz <khalid_aziz@xxxxxx>
Signed-off-by: Zou Nan hai <nanhai.zou@xxxxxxxxx>



diff -Nraup linux-2.6.16/arch/ia64/hp/common/sba_iommu.c b/arch/ia64/hp/common/sba_iommu.c
--- linux-2.6.16/arch/ia64/hp/common/sba_iommu.c	2006-03-20 13:53:29.000000000 +0800
+++ b/arch/ia64/hp/common/sba_iommu.c	2006-06-08 12:23:31.000000000 +0800
@@ -1624,6 +1624,28 @@ ioc_iova_init(struct ioc *ioc)
 	READ_REG(ioc->ioc_hpa + IOC_IBASE);
 }
 
+#ifdef CONFIG_KEXEC
+void
+ioc_iova_disable(void)
+{
+	struct ioc *ioc;
+
+	ioc = ioc_list;
+
+	while (ioc != NULL) {
+		/* Disable IOVA translation */
+		WRITE_REG(ioc->ibase & 0xfffffffffffffffe, ioc->ioc_hpa + IOC_IBASE);
+		READ_REG(ioc->ioc_hpa + IOC_IBASE);
+
+		/* Clear I/O TLB of any possible entries */
+		WRITE_REG(ioc->ibase | (get_iovp_order(ioc->iov_size) + iovp_shift), ioc->ioc_hpa + IOC_PCOM);
+		READ_REG(ioc->ioc_hpa + IOC_PCOM);
+
+		ioc = ioc->next;
+	}
+}
+#endif
+
 static void __init
 ioc_resource_init(struct ioc *ioc)
 {
diff -Nraup linux-2.6.16/arch/ia64/Kconfig b/arch/ia64/Kconfig
--- linux-2.6.16/arch/ia64/Kconfig	2006-03-20 13:53:29.000000000 +0800
+++ b/arch/ia64/Kconfig	2006-06-08 12:23:39.000000000 +0800
@@ -376,6 +376,29 @@ config IA64_PALINFO
 config SGI_SN
 	def_bool y if (IA64_SGI_SN2 || IA64_GENERIC)
 
+config KEXEC
+	bool "kexec system call (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	help
+	  kexec is a system call that implements the ability to shutdown your
+	  current kernel, and to start another kernel.  It is like a reboot
+	  but it is indepedent of the system firmware.   And like a reboot
+	  you can start any kernel with it, not just Linux.
+
+	  The name comes from the similiarity to the exec system call.
+
+	  It is an ongoing process to be certain the hardware in a machine
+	  is properly shutdown, so do not be surprised if this code does not
+	  initially work for you.  It may help to enable device hotplugging
+	  support.  As of this writing the exact hardware interface is
+	  strongly in flux, so no good recommendation can be made.
+
+config CRASH_DUMP
+	  bool "kernel crash dumps (EXPERIMENTAL)"
+	  depends on EXPERIMENTAL
+	  help
+	    Generate crash dump after being started by kexec.
+
 source "drivers/firmware/Kconfig"
 
 source "fs/Kconfig.binfmt"
diff -Nraup linux-2.6.16/arch/ia64/kernel/crash.c b/arch/ia64/kernel/crash.c
--- linux-2.6.16/arch/ia64/kernel/crash.c	1970-01-01 08:00:00.000000000 +0800
+++ b/arch/ia64/kernel/crash.c	2006-06-08 12:23:39.000000000 +0800
@@ -0,0 +1,152 @@
+/*
+ * arch/ia64/kernel/crash.c
+ *
+ * Architecture specific (ia64) functions for kexec based crash dumps.
+ *
+ * Created by: Khalid Aziz <khalid.aziz@xxxxxx>
+ * Copyright (C) 2005 Hewlett-Packard Development Company, L.P.
+ * Copyright (C) 2005 Intel Corp	Zou Nan hai <nanhai.zou@xxxxxxxxx>
+ *
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/irq.h>
+#include <linux/pci.h>
+#include <linux/reboot.h>
+#include <linux/kexec.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/elf.h>
+#include <linux/elfcore.h>
+#include <linux/device.h>
+#include <asm/uaccess.h>
+
+size_t copy_oldmem_page(unsigned long pfn, char *buf,
+                               size_t csize, unsigned long offset, int userbuf)
+{
+        void  *vaddr;
+
+        if (!csize)
+                return 0;
+        vaddr = page_address(pfn_to_page(pfn));
+
+        if (userbuf) {
+                if (copy_to_user(buf, (vaddr + offset), csize)) {
+                        return -EFAULT;
+                }
+        } else
+                memcpy(buf, (vaddr + offset), csize);
+        return csize;
+}
+
+static void device_shootdown(void)
+{
+       struct pci_dev *dev;
+       irq_desc_t *desc;
+       u16 pci_command;
+
+       list_for_each_entry(dev, &pci_devices, global_list) {
+               desc = irq_descp(dev->irq);
+               if (!desc->action)
+                       continue;
+               pci_read_config_word(dev, PCI_COMMAND, &pci_command);
+               if (pci_command & PCI_COMMAND_MASTER) {
+                       pci_command &= ~PCI_COMMAND_MASTER;
+                       pci_write_config_word(dev, PCI_COMMAND, pci_command);
+               }
+               disable_irq_nosync(dev->irq);
+               desc->handler->end(dev->irq);
+       }
+}
+
+static Elf64_Word
+*append_elf_note(Elf64_Word *buf, char *name, unsigned type, void *data,
+		size_t data_len)
+{
+	struct elf_note *note = (struct elf_note *)buf;
+	note->n_namesz = strlen(name) + 1;
+	note->n_descsz = data_len;
+	note->n_type   = type;
+	buf += (sizeof(*note) + 3)/4;
+	memcpy(buf, name, note->n_namesz);
+	buf += (note->n_namesz + 3)/4;
+	memcpy(buf, data, data_len);
+	buf += (data_len + 3)/4;
+	return buf;
+}
+
+static void
+final_note(void *buf)
+{
+	memset(buf, 0, sizeof(struct elf_note));
+}
+
+static void
+crash_save_this_cpu(void)
+{
+	void *buf;
+	struct elf_prstatus prstatus;
+	int cpu = smp_processor_id();
+	elf_greg_t *dst = (elf_greg_t *)&prstatus.pr_reg;
+
+	memset(&prstatus, 0, sizeof(prstatus));
+	prstatus.pr_pid = current->pid;
+
+    	dst[1] = ia64_getreg(_IA64_REG_GP);
+    	dst[12] = ia64_getreg(_IA64_REG_SP);
+    	dst[13] = ia64_getreg(_IA64_REG_TP);
+
+    	dst[42] = ia64_getreg(_IA64_REG_IP);
+    	dst[45] = ia64_getreg(_IA64_REG_AR_RSC);
+
+	ia64_setreg(_IA64_REG_AR_RSC, 0);
+	ia64_srlz_i();
+
+    	dst[46] = ia64_getreg(_IA64_REG_AR_BSP);
+    	dst[47] = ia64_getreg(_IA64_REG_AR_BSPSTORE);
+
+    	dst[48] = ia64_getreg(_IA64_REG_AR_RNAT);
+    	dst[49] = ia64_getreg(_IA64_REG_AR_CCV);
+    	dst[50] = ia64_getreg(_IA64_REG_AR_UNAT);
+
+    	dst[51] = ia64_getreg(_IA64_REG_AR_FPSR);
+    	dst[52] = ia64_getreg(_IA64_REG_AR_PFS);
+    	dst[53] = ia64_getreg(_IA64_REG_AR_LC);
+
+    	dst[54] = ia64_getreg(_IA64_REG_AR_LC);
+    	dst[55] = ia64_getreg(_IA64_REG_AR_CSD);
+    	dst[56] = ia64_getreg(_IA64_REG_AR_SSD);
+
+        buf = (u64 *) per_cpu_ptr(crash_notes, cpu);
+	if (!buf)
+		return;
+	buf = append_elf_note(buf, "CORE", NT_PRSTATUS, &prstatus,
+		sizeof(prstatus));
+	final_note(buf);
+}
+
+void
+machine_crash_shutdown(struct pt_regs *pt)
+{
+	/* This function is only called after the system
+	 * has paniced or is otherwise in a critical state.
+	 * The minimum amount of code to allow a kexec'd kernel
+	 * to run successfully needs to happen here.
+	 *
+	 * In practice this means shooting down the other cpus in
+	 * an SMP system.
+	 */
+	if (in_interrupt()) {
+		ia64_eoi();
+	}
+	crash_save_this_cpu();
+	device_shootdown();
+#ifdef CONFIG_SMP
+	smp_send_stop();
+#endif
+#ifdef CONFIG_IA64_HP_ZX1
+	ioc_iova_disable();
+#endif
+}
diff -Nraup linux-2.6.16/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c
--- linux-2.6.16/arch/ia64/kernel/efi.c	2006-03-20 13:53:29.000000000 +0800
+++ b/arch/ia64/kernel/efi.c	2006-06-08 12:26:11.000000000 +0800
@@ -25,6 +25,7 @@
 #include <linux/types.h>
 #include <linux/time.h>
 #include <linux/efi.h>
+#include <linux/kexec.h>
 
 #include <asm/io.h>
 #include <asm/kregs.h>
@@ -40,7 +41,7 @@ extern efi_status_t efi_call_phys (void 
 struct efi efi;
 EXPORT_SYMBOL(efi);
 static efi_runtime_services_t *runtime;
-static unsigned long mem_limit = ~0UL, max_addr = ~0UL;
+static unsigned long mem_limit = ~0UL, max_addr = ~0UL, min_addr = 0UL;
 
 #define efi_call_virt(f, args...)	(*(f))(args)
 
@@ -420,6 +421,8 @@ efi_init (void)
 			mem_limit = memparse(cp + 4, &cp);
 		} else if (memcmp(cp, "max_addr=", 9) == 0) {
 			max_addr = GRANULEROUNDDOWN(memparse(cp + 9, &cp));
+		} else if (memcmp(cp, "min_addr=", 9) == 0) {
+			min_addr = GRANULEROUNDDOWN(memparse(cp + 9, &cp));
 		} else {
 			while (*cp != ' ' && *cp)
 				++cp;
@@ -427,6 +430,8 @@ efi_init (void)
 				++cp;
 		}
 	}
+	if (min_addr != 0UL)
+		printk(KERN_INFO "Ignoring memory below %luMB\n", min_addr >> 20);
 	if (max_addr != ~0UL)
 		printk(KERN_INFO "Ignoring memory above %luMB\n", max_addr >> 20);
 
@@ -839,7 +844,8 @@ find_memmap_space (void)
 		as = max(contig_low, md->phys_addr);
 		ae = min(contig_high, efi_md_end(md));
 
-		/* keep within max_addr= command line arg */
+		/* keep within max_addr= and min_addr= command line arg */
+		as = max(as, min_addr);
 		ae = min(ae, max_addr);
 		if (ae <= as)
 			continue;
@@ -949,7 +955,8 @@ efi_memmap_init(unsigned long *s, unsign
 		} else
 			ae = efi_md_end(md);
 
-		/* keep within max_addr= command line arg */
+		/* keep within max_addr= and min_addr= command line arg */
+		as = max(as, min_addr);
 		ae = min(ae, max_addr);
 		if (ae <= as)
 			continue;
@@ -1061,6 +1068,10 @@ efi_initialize_iomem_resources(struct re
 			 */
 			insert_resource(res, code_resource);
 			insert_resource(res, data_resource);
+#ifdef CONFIG_KEXEC
+			if (crashk_res.end > crashk_res.start)
+				insert_resource(res, &crashk_res);
+#endif
 		}
 	}
 }
diff -Nraup linux-2.6.16/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S
--- linux-2.6.16/arch/ia64/kernel/entry.S	2006-03-20 13:53:29.000000000 +0800
+++ b/arch/ia64/kernel/entry.S	2006-06-08 12:23:31.000000000 +0800
@@ -1590,7 +1590,7 @@ sys_call_table:
 	data8 sys_mq_timedreceive		// 1265
 	data8 sys_mq_notify
 	data8 sys_mq_getsetattr
-	data8 sys_ni_syscall			// reserved for kexec_load
+	data8 sys_kexec_load
 	data8 sys_ni_syscall			// reserved for vserver
 	data8 sys_waitid			// 1270
 	data8 sys_add_key
diff -Nraup linux-2.6.16/arch/ia64/kernel/machine_kexec.c b/arch/ia64/kernel/machine_kexec.c
--- linux-2.6.16/arch/ia64/kernel/machine_kexec.c	1970-01-01 08:00:00.000000000 +0800
+++ b/arch/ia64/kernel/machine_kexec.c	2006-06-08 12:55:57.000000000 +0800
@@ -0,0 +1,115 @@
+/*
+ * arch/ia64/kernel/machine_kexec.c 
+ *
+ * Handle transition of Linux booting another kernel
+ * Copyright (C) 2005 Hewlett-Packard Development Comapny, L.P.
+ * Copyright (C) 2005 Khalid Aziz <khalid.aziz@xxxxxx>
+ * Copyright (C) 2006 Intel Corp, Zou Nan hai <nanhai.zou@xxxxxxxxx>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2.  See the file COPYING for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/kexec.h>
+#include <linux/pci.h>
+#include <linux/cpu.h>
+#include <asm/mmu_context.h>
+#include <asm/setup.h>
+#include <asm/mca.h>
+#include <asm/page.h>
+#include <asm/bitops.h>
+#include <asm/tlbflush.h>
+#include <asm/delay.h>
+#include <asm/meminit.h>
+
+typedef void (*relocate_new_kernel_t)(unsigned long, unsigned long,
+		struct ia64_boot_param *, unsigned long);
+
+/*
+ * Do what every setup is needed on image and the
+ * reboot code buffer to allow us to avoid allocations
+ * later.
+ */
+int machine_kexec_prepare(struct kimage *image)
+{
+	void *control_code_buffer;
+	const unsigned long *func;
+
+	func = (unsigned long *)&relocate_new_kernel;
+	/* Pre-load control code buffer to minimize work in kexec path */
+	control_code_buffer = page_address(image->control_code_page);
+	memcpy((void *)control_code_buffer, (const void *)func[0], 
+			relocate_new_kernel_size);
+	flush_icache_range((unsigned long)control_code_buffer, 
+			(unsigned long)control_code_buffer + relocate_new_kernel_size);
+
+	return 0;
+}
+
+void machine_kexec_cleanup(struct kimage *image)
+{
+}
+
+void machine_shutdown(void)
+{
+#ifdef CONFIG_PCI
+	struct pci_dev *dev = NULL;
+	irq_desc_t *idesc;
+	cpumask_t mask = CPU_MASK_NONE;
+	/* Disable all PCI devices */
+	while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
+		if (!(dev->is_enabled))
+			continue;
+		idesc = irq_descp(dev->irq);
+		if (!idesc)
+			continue;
+		cpu_set(0, mask);
+		disable_irq_nosync(dev->irq);
+		idesc->handler->end(dev->irq);
+		idesc->handler->set_affinity(dev->irq, mask);
+		idesc->action = NULL;
+		pci_disable_device(dev);
+	}
+#endif
+
+#ifdef CONFIG_HOTPLUG_CPU
+	{
+		int cpu;
+
+		for_each_online_cpu(cpu) {
+			if (cpu != smp_processor_id())
+				cpu_down(cpu);
+		}
+	}
+#elif defined(CONFIG_SMP)
+	smp_call_function(kexec_stop_this_cpu, (void *)image->start, 0, 0);
+#endif
+
+
+#ifdef CONFIG_IA64_HP_ZX1
+	ioc_iova_disable();
+#endif
+}
+
+/*
+ * Do not allocate memory (or fail in any way) in machine_kexec().
+ * We are past the point of no return, committed to rebooting now. 
+ */
+extern void *efi_get_pal_addr(void);
+void machine_kexec(struct kimage *image)
+{
+	relocate_new_kernel_t rnk;
+	void *pal_addr = efi_get_pal_addr();
+	unsigned long code_addr = (unsigned long)page_address(image->control_code_page);
+	/* Interrupts aren't acceptable while we reboot */
+	ia64_set_itv(1<<16);
+	local_irq_disable();
+	rnk = (relocate_new_kernel_t)&code_addr;
+	(*rnk)(image->head, image->start, ia64_boot_param,
+		     GRANULEROUNDDOWN((unsigned long) pal_addr));
+	BUG();
+	for (;;);
+}
diff -Nraup linux-2.6.16/arch/ia64/kernel/Makefile b/arch/ia64/kernel/Makefile
--- linux-2.6.16/arch/ia64/kernel/Makefile	2006-03-20 13:53:29.000000000 +0800
+++ b/arch/ia64/kernel/Makefile	2006-06-08 12:23:31.000000000 +0800
@@ -28,6 +28,7 @@ obj-$(CONFIG_IA64_CYCLONE)	+= cyclone.o
 obj-$(CONFIG_CPU_FREQ)		+= cpufreq/
 obj-$(CONFIG_IA64_MCA_RECOVERY)	+= mca_recovery.o
 obj-$(CONFIG_KPROBES)		+= kprobes.o jprobes.o
+obj-$(CONFIG_KEXEC)		+= machine_kexec.o relocate_kernel.o crash.o
 obj-$(CONFIG_IA64_UNCACHED_ALLOCATOR)	+= uncached.o
 mca_recovery-y			+= mca_drv.o mca_drv_asm.o
 
diff -Nraup linux-2.6.16/arch/ia64/kernel/relocate_kernel.S b/arch/ia64/kernel/relocate_kernel.S
--- linux-2.6.16/arch/ia64/kernel/relocate_kernel.S	1970-01-01 08:00:00.000000000 +0800
+++ b/arch/ia64/kernel/relocate_kernel.S	2006-06-08 12:23:39.000000000 +0800
@@ -0,0 +1,353 @@
+/*
+ * arch/ia64/kernel/relocate_kernel.S 
+ *
+ * Relocate kexec'able kernel and start it
+ *
+ * Copyright (C) 2005 Hewlett-Packard Development Company, L.P.
+ * Copyright (C) 2005 Khalid Aziz  <khalid.aziz@xxxxxx>
+ * Copyright (C) 2005 Intel Corp,  Zou Nan hai <nanhai.zou@xxxxxxxxx>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2.  See the file COPYING for more details.
+ */
+#include <linux/config.h>
+#include <asm/asmmacro.h>
+#include <asm/kregs.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/mca_asm.h>
+
+       /* Must be relocatable PIC code callable as a C function
+        */
+GLOBAL_ENTRY(relocate_new_kernel)
+	.prologue
+	alloc r31=ar.pfs,4,0,0,0
+        .body
+.reloc_entry:
+{
+	rsm psr.i| psr.ic
+	mov r2=ip
+}
+	;;
+{
+        flushrs                         // must be first insn in group
+        srlz.i
+}
+	;;
+	dep r2=0,r2,61,3		//to physical address
+	;;
+	//first switch to physical mode
+	add r3=1f-.reloc_entry, r2
+	movl r16 = IA64_PSR_AC|IA64_PSR_BN|IA64_PSR_IC
+	mov ar.rsc=0	          	// put RSE in enforced lazy mode
+	;;
+	add sp=(memory_stack_end - 16 - .reloc_entry),r2
+	add r8=(register_stack - .reloc_entry),r2
+	;;
+	mov r18=ar.rnat
+	mov ar.bspstore=r8
+	;;
+        mov cr.ipsr=r16
+        mov cr.iip=r3
+        mov cr.ifs=r0
+	srlz.i
+	;;
+	mov ar.rnat=r18
+	rfi
+	;;
+1:
+	//physical mode code begin
+	mov b6=in1
+	dep r28=0,in2,61,3	//to physical address
+
+	// purge all TC entries
+#define O(member)       IA64_CPUINFO_##member##_OFFSET
+        GET_THIS_PADDR(r2, cpu_info)    // load phys addr of cpu_info into r2
+        ;;
+        addl r17=O(PTCE_STRIDE),r2
+        addl r2=O(PTCE_BASE),r2
+        ;;
+        ld8 r18=[r2],(O(PTCE_COUNT)-O(PTCE_BASE));;    	// r18=ptce_base
+        ld4 r19=[r2],4                                  // r19=ptce_count[0]
+        ld4 r21=[r17],4                                 // r21=ptce_stride[0]
+        ;;
+        ld4 r20=[r2]                                    // r20=ptce_count[1]
+        ld4 r22=[r17]                                   // r22=ptce_stride[1]
+        mov r24=r0
+        ;;
+        adds r20=-1,r20
+        ;;
+#undef O
+2:
+        cmp.ltu p6,p7=r24,r19
+(p7)    br.cond.dpnt.few 4f
+        mov ar.lc=r20
+3:
+        ptc.e r18
+        ;;
+        add r18=r22,r18
+        br.cloop.sptk.few 3b
+        ;;
+        add r18=r21,r18
+        add r24=1,r24
+        ;;
+        br.sptk.few 2b
+4:
+        srlz.i
+        ;;
+	//purge TR entry for kernel text and data
+        movl r16=KERNEL_START
+        mov r18=KERNEL_TR_PAGE_SHIFT<<2
+        ;;
+        ptr.i r16, r18
+        ptr.d r16, r18
+        ;;
+        srlz.i
+        ;;
+
+	// purge TR entry for percpu data
+        movl r16=PERCPU_ADDR
+        mov r18=PERCPU_PAGE_SHIFT<<2
+        ;;
+        ptr.d r16,r18
+        ;;
+        srlz.d
+	;;
+
+        // purge TR entry for pal code
+        mov r16=in3
+        mov r18=IA64_GRANULE_SHIFT<<2
+        ;;
+        ptr.i r16,r18
+        ;;
+        srlz.i
+	;;
+
+        // purge TR entry for stack
+        mov r16=IA64_KR(CURRENT_STACK)
+        ;;
+        shl r16=r16,IA64_GRANULE_SHIFT
+        movl r19=PAGE_OFFSET
+        ;;
+        add r16=r19,r16
+        mov r18=IA64_GRANULE_SHIFT<<2
+        ;;
+        ptr.d r16,r18
+        ;;
+        srlz.i
+	;;
+
+	//copy segments
+	movl r16=PAGE_MASK
+        mov  r30=in0                    // in0 is page_list
+        br.sptk.few .dest_page
+	;;
+.loop:
+	ld8  r30=[in0], 8;;
+.dest_page:
+	tbit.z p0, p6=r30, 0;;    	// 0x1 dest page
+(p6)	and r17=r30, r16
+(p6)	br.cond.sptk.few .loop;;
+
+	tbit.z p0, p6=r30, 1;;		// 0x2 indirect page
+(p6)	and in0=r30, r16
+(p6)	br.cond.sptk.few .loop;;
+
+	tbit.z p0, p6=r30, 2;;		// 0x4 end flag
+(p6)	br.cond.sptk.few .end_loop;;
+
+	tbit.z p6, p0=r30, 3;;		// 0x8 source page
+(p6)	br.cond.sptk.few .loop
+
+	and r18=r30, r16
+
+	// simple copy page, may optimize later
+	movl r14=PAGE_SIZE/8 - 1;;
+	mov ar.lc=r14;;
+1:
+	ld8 r14=[r18], 8;;
+	st8 [r17]=r14, 8;;
+	fc.i r17
+	br.ctop.sptk.few 1b
+	br.sptk.few .loop
+	;;
+
+.end_loop:
+	sync.i			// for fc.i
+	;;
+	srlz.i
+	;;
+	srlz.d
+	;;
+	br.call.sptk.many b0=b6;;
+
+.align  32
+memory_stack:
+	.fill           8192, 1, 0
+memory_stack_end:
+register_stack:
+	.fill           8192, 1, 0
+register_stack_end:
+relocate_new_kernel_end:
+END(relocate_new_kernel)
+
+GLOBAL_ENTRY(kexec_fake_sal_rendez)
+	.prologue
+	alloc r31=ar.pfs,3,0,0,0
+	.body
+.rendez_entry:
+	rsm	psr.i | psr.ic
+	mov r25=ip
+	;;
+	{
+		flushrs
+		srlz.i
+	}
+	;;
+       /* See where I am running, and compute gp */
+	{
+		mov     ar.rsc = 0      /* Put RSE in enforce lacy, LE mode */
+		mov     gp = ip         /* gp == relocate_new_kernel */
+	}
+
+	movl r8=0x00000100000000
+	;;
+	mov cr.iva=r8
+	/* Transition from virtual to physical mode */
+	srlz.i
+	;;
+	add	r17=5f-.rendez_entry, r25
+	movl	r16=(IA64_PSR_AC | IA64_PSR_BN | IA64_PSR_IC | IA64_PSR_MFL)
+	;;
+	tpa	r17=r17
+	mov	cr.ipsr=r16
+	;;
+	mov	cr.iip=r17
+	mov	cr.ifs=r0
+	;;
+	rfi
+	;;
+5:
+	mov     b6=in0			/* _start addr */
+	mov	r8=in1			/* ap_wakeup_vector */
+	mov	r26=in2			/* PAL addr */
+	;;
+	/* Purge kernel TRs */
+	movl	r16=KERNEL_START
+	mov	r18=KERNEL_TR_PAGE_SHIFT<<2
+	;;
+	ptr.i	r16,r18
+	ptr.d	r16,r18
+	;;
+	srlz.i
+	;;
+	srlz.d
+	;;
+	/* Purge percpu TR */
+	movl	r16=PERCPU_ADDR
+	mov	r18=PERCPU_PAGE_SHIFT<<2
+	;;
+	ptr.d	r16,r18
+	;;
+	srlz.d
+	;;
+	/* Purge PAL TR */
+	mov	r18=IA64_GRANULE_SHIFT<<2
+	;;
+	ptr.i	r26,r18
+	;;
+	srlz.i
+	;;
+	/* Purge stack TR */
+	mov	r16=IA64_KR(CURRENT_STACK)
+	;;
+	shl	r16=r16,IA64_GRANULE_SHIFT
+	movl	r19=PAGE_OFFSET
+	;;
+	add	r16=r19,r16
+	mov	r18=IA64_GRANULE_SHIFT<<2
+	;;
+	ptr.d	r16,r18
+	;;
+	srlz.i
+	;;
+
+	/* Ensure we can read and clear external interrupts */
+	mov	cr.tpr=r0
+	srlz.d
+
+	shr.u	r9=r8,6			/* which irr */
+	;;
+	and	r8=63,r8		/* bit offset into irr */
+	;;
+	mov	r10=1;;
+	;;
+	shl	r10=r10,r8		/* bit mask off irr we want */
+	cmp.eq	p6,p0=0,r9
+	;;
+(p6)	br.cond.sptk.few        check_irr0
+	cmp.eq	p7,p0=1,r9
+	;;
+(p7)	br.cond.sptk.few        check_irr1
+	cmp.eq	p8,p0=2,r9
+	;;
+(p8)	br.cond.sptk.few        check_irr2
+	cmp.eq	p9,p0=3,r9
+	;;
+(p9)	br.cond.sptk.few        check_irr3
+
+check_irr0:
+	mov	r8=cr.irr0
+	;;
+	and	r8=r8,r10
+	;;
+	cmp.eq	p6,p0=0,r8
+(p6)	br.cond.sptk.few	check_irr0
+	br.few	call_start
+	
+check_irr1:
+	mov	r8=cr.irr1
+	;;
+	and	r8=r8,r10
+	;;
+	cmp.eq	p6,p0=0,r8
+(p6)	br.cond.sptk.few	check_irr1
+	br.few	call_start
+	
+check_irr2:
+	mov	r8=cr.irr2
+	;;
+	and	r8=r8,r10
+	;;
+	cmp.eq	p6,p0=0,r8
+(p6)	br.cond.sptk.few	check_irr2
+	br.few	call_start
+	
+check_irr3:
+	mov	r8=cr.irr3
+	;;
+	and	r8=r8,r10
+	;;
+	cmp.eq	p6,p0=0,r8
+(p6)	br.cond.sptk.few	check_irr3
+	br.few	call_start
+	
+call_start:
+	mov	cr.eoi=r0
+	;;
+	srlz.d
+	;;
+	mov	r8=cr.ivr
+	;;
+	srlz.d
+	;;
+	cmp.eq	p0,p6=15,r8
+(p6)	br.cond.sptk.few	call_start
+	br.sptk.few		b6
+kexec_fake_sal_rendez_end:
+END(kexec_fake_sal_rendez)
+
+	.global relocate_new_kernel_size
+relocate_new_kernel_size:
+	data8	kexec_fake_sal_rendez_end - relocate_new_kernel
+
diff -Nraup linux-2.6.16/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c
--- linux-2.6.16/arch/ia64/kernel/setup.c	2006-03-20 13:53:29.000000000 +0800
+++ b/arch/ia64/kernel/setup.c	2006-06-08 12:24:46.000000000 +0800
@@ -44,6 +44,8 @@
 #include <linux/platform.h>
 #include <linux/pm.h>
 #include <linux/cpufreq.h>
+#include <linux/kexec.h>
+#include <linux/crash_dump.h>
 
 #include <asm/ia32.h>
 #include <asm/machvec.h>
@@ -251,6 +253,32 @@ reserve_memory (void)
 	}
 #endif
 
+#ifdef CONFIG_KEXEC
+	/* crashkernel=size@addr specifies the location to reserve for
+	 * a crash kernel.  By reserving this memory we guarantee
+	 * that linux never set's it up as a DMA target.
+	 * Useful for holding code to do something appropriate
+	 * after a kernel panic.
+	 */
+	{
+		char *from = strstr(saved_command_line, "crashkernel=");
+		if (from) {
+			unsigned long size, base;
+			size = memparse(from + 12, &from);
+			if (*from == '@') {
+				base = memparse(from + 1, &from);
+				rsvd_region[n].start =
+					(unsigned long)__va(base);
+				rsvd_region[n].end =
+					(unsigned long)__va(base + size);
+				crashk_res.start = base;
+				crashk_res.end = base + size - 1;
+				n++;
+			}
+		}
+	}
+#endif
+
 	efi_memmap_init(&rsvd_region[n].start, &rsvd_region[n].end);
 	n++;
 
@@ -496,6 +524,16 @@ setup_arch (char **cmdline_p)
 	if (!strstr(saved_command_line, "nomca"))
 		ia64_mca_init();
 
+#ifdef CONFIG_CRASH_DUMP
+	{
+		char *from = strstr(saved_command_line, "elfcorehdr=");
+
+		if (from)
+			elfcorehdr_addr = memparse(from+11, &from);
+		saved_max_pfn = (unsigned long) -1;
+	}
+#endif
+
 	platform_setup(cmdline_p);
 	paging_init();
 }
diff -Nraup linux-2.6.16/arch/ia64/kernel/smp.c b/arch/ia64/kernel/smp.c
--- linux-2.6.16/arch/ia64/kernel/smp.c	2006-03-20 13:53:29.000000000 +0800
+++ b/arch/ia64/kernel/smp.c	2006-06-08 12:23:31.000000000 +0800
@@ -30,6 +30,7 @@
 #include <linux/delay.h>
 #include <linux/efi.h>
 #include <linux/bitops.h>
+#include <linux/kexec.h>
 
 #include <asm/atomic.h>
 #include <asm/current.h>
@@ -84,6 +85,34 @@ unlock_ipi_calllock(void)
 	spin_unlock_irq(&call_lock);
 }
 
+#ifdef CONFIG_KEXEC
+/*
+ * Stop the CPU and put it in fake SAL rendezvous. This allows CPU to wake
+ * up with IPI from boot processor
+ */
+void
+kexec_stop_this_cpu (void *func)
+{
+	unsigned long pta, impl_va_bits, pal_base;
+
+	/*
+	 * Remove this CPU by putting it into fake SAL rendezvous
+	 */
+	cpu_clear(smp_processor_id(), cpu_online_map);
+	max_xtp();
+	ia64_eoi();
+
+	/* Disable VHPT */
+	impl_va_bits = ffz(~(local_cpu_data->unimpl_va_mask | (7UL << 61)));
+	pta = POW2(61) - POW2(vmlpt_bits);
+	ia64_set_pta(pta | (0 << 8) | (vmlpt_bits << 2) | 0);
+
+	local_irq_disable();
+	pal_base = __get_cpu_var(ia64_mca_pal_base);
+	kexec_fake_sal_rendez(func, ap_wakeup_vector, pal_base);
+}
+#endif
+
 static void
 stop_this_cpu (void)
 {
diff -Nraup linux-2.6.16/include/asm-ia64/kexec.h b/include/asm-ia64/kexec.h
--- linux-2.6.16/include/asm-ia64/kexec.h	1970-01-01 08:00:00.000000000 +0800
+++ b/include/asm-ia64/kexec.h	2006-06-08 12:24:04.000000000 +0800
@@ -0,0 +1,34 @@
+#ifndef _ASM_IA64_KEXEC_H
+#define _ASM_IA64_KEXEC_H
+
+
+/* Maximum physical address we can use pages from */
+#define KEXEC_SOURCE_MEMORY_LIMIT (-1UL)
+/* Maximum address we can reach in physical address mode */
+#define KEXEC_DESTINATION_MEMORY_LIMIT (-1UL)
+/* Maximum address we can use for the control code buffer */
+#define KEXEC_CONTROL_MEMORY_LIMIT TASK_SIZE
+
+#define KEXEC_CONTROL_CODE_SIZE (8192 + 8192 + 4096)
+
+/* The native architecture */
+#define KEXEC_ARCH KEXEC_ARCH_IA_64
+
+#define MAX_NOTE_BYTES 1024
+
+#define pte_bits	3
+#define vmlpt_bits	(impl_va_bits - PAGE_SHIFT + pte_bits)
+#define POW2(n)		(1ULL << (n))
+
+DECLARE_PER_CPU(u64, ia64_mca_pal_base);
+const extern unsigned int relocate_new_kernel_size;
+volatile extern long kexec_rendez;
+extern void relocate_new_kernel(unsigned long, unsigned long, 
+		struct ia64_boot_param *, unsigned long);
+extern void kexec_fake_sal_rendez(void *start, unsigned long wake_up,
+		unsigned long pal_base);
+static inline void
+crash_setup_regs(struct pt_regs *newregs, struct pt_regs *oldregs)
+{
+}
+#endif /* _ASM_IA64_KEXEC_H */
diff -Nraup linux-2.6.16/include/asm-ia64/machvec_hpzx1.h b/include/asm-ia64/machvec_hpzx1.h
--- linux-2.6.16/include/asm-ia64/machvec_hpzx1.h	2006-03-20 13:53:29.000000000 +0800
+++ b/include/asm-ia64/machvec_hpzx1.h	2006-06-08 12:23:31.000000000 +0800
@@ -34,4 +34,6 @@ extern ia64_mv_dma_mapping_error	sba_dma
 #define platform_dma_supported			sba_dma_supported
 #define platform_dma_mapping_error		sba_dma_mapping_error
 
+extern void ioc_iova_disable(void);
+
 #endif /* _ASM_IA64_MACHVEC_HPZX1_h */
diff -Nraup linux-2.6.16/include/asm-ia64/meminit.h b/include/asm-ia64/meminit.h
--- linux-2.6.16/include/asm-ia64/meminit.h	2006-03-20 13:53:29.000000000 +0800
+++ b/include/asm-ia64/meminit.h	2006-06-08 12:24:04.000000000 +0800
@@ -16,11 +16,12 @@
  * 	- initrd (optional)
  * 	- command line string
  * 	- kernel code & data
+ * 	- crash dumping code reserved region
  * 	- Kernel memory map built from EFI memory map
  *
  * More could be added if necessary
  */
-#define IA64_MAX_RSVD_REGIONS 6
+#define IA64_MAX_RSVD_REGIONS 7
 
 struct rsvd_region {
 	unsigned long start;	/* virtual address of beginning of element */
diff -Nraup linux-2.6.16/include/asm-ia64/smp.h b/include/asm-ia64/smp.h
--- linux-2.6.16/include/asm-ia64/smp.h	2006-03-20 13:53:29.000000000 +0800
+++ b/include/asm-ia64/smp.h	2006-06-08 12:23:31.000000000 +0800
@@ -129,6 +129,9 @@ extern void smp_send_reschedule (int cpu
 extern void lock_ipi_calllock(void);
 extern void unlock_ipi_calllock(void);
 extern void identify_siblings (struct cpuinfo_ia64 *);
+#ifdef CONFIG_KEXEC
+extern void kexec_stop_this_cpu(void *);
+#endif
 
 #else
 
diff -Nraup linux-2.6.16/include/linux/irq.h b/include/linux/irq.h
--- linux-2.6.16/include/linux/irq.h	2006-03-20 13:53:29.000000000 +0800
+++ b/include/linux/irq.h	2006-06-08 12:23:31.000000000 +0800
@@ -94,6 +94,7 @@ irq_descp (int irq)
 #include <asm/hw_irq.h> /* the arch dependent stuff */
 
 extern int setup_irq(unsigned int irq, struct irqaction * new);
+extern void terminate_irqs(void);
 
 #ifdef CONFIG_GENERIC_HARDIRQS
 extern cpumask_t irq_affinity[NR_IRQS];
diff -Nraup linux-2.6.16/kernel/irq/manage.c b/kernel/irq/manage.c
--- linux-2.6.16/kernel/irq/manage.c	2006-03-20 13:53:29.000000000 +0800
+++ b/kernel/irq/manage.c	2006-06-08 12:23:31.000000000 +0800
@@ -377,3 +377,22 @@ int request_irq(unsigned int irq,
 
 EXPORT_SYMBOL(request_irq);
 
+/*
+ * Terminate any outstanding interrupts
+ */
+void terminate_irqs(void)
+{
+	struct irqaction * action;
+	irq_desc_t *idesc;
+	int i;
+
+	for (i=0; i < NR_IRQS; i++) {
+		idesc = irq_descp(i);
+		action = idesc->action;
+		if (!action)
+			continue;
+		if (idesc->handler->end)
+			idesc->handler->end(i);
+	}
+}
+
diff -Nraup a/kexec/arch/ia64/crashdump-ia64.c b/kexec/arch/ia64/crashdump-ia64.c
--- a/kexec/arch/ia64/crashdump-ia64.c	1970-01-01 08:00:00.000000000 +0800
+++ b/kexec/arch/ia64/crashdump-ia64.c	2006-06-03 11:56:58.000000000 +0800
@@ -0,0 +1,351 @@
+/*
+ * kexec: crashdum support
+ * Copyright (C) 2005-2006 Zou Nan hai <nanhai.zou@xxxxxxxxx> Intel Corp
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <elf.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-syscall.h"
+#include "kexec-ia64.h"
+#include "crashdump-ia64.h"
+
+int memory_ranges = 0;
+#define LOAD_OFFSET 	(0xa000000000000000UL + 0x100000000UL - (1UL<<26))
+#define MAX_LINE        160
+/* Stores a sorted list of RAM memory ranges for which to create elf headers.
+ * A separate program header is created for backup region */
+static struct memory_range crash_memory_range[CRASH_MAX_MEMORY_RANGES];
+/* Memory region reserved for storing panic kernel and other data. */
+static struct memory_range crash_reserved_mem;
+unsigned long elfcorehdr;
+static unsigned long kernel_code_start;
+struct loaded_segment {
+        unsigned long start;
+        unsigned long end;
+	unsigned long reserved;
+};
+
+#define MAX_LOAD_SEGMENTS	128
+struct loaded_segment loaded_segments[MAX_LOAD_SEGMENTS];
+
+unsigned long loaded_segments_num, loaded_segments_base;
+static int seg_comp(const void *a, const void *b)
+{
+        const struct loaded_segment *x = a, *y = b;
+        /* avoid overflow */
+        if (x->start > y->start) return 1;
+	if (x->start < y->start) return -1;
+	return 0;
+}
+
+/* purgatory code need this info to patch the EFI memmap
+ */
+static void add_loaded_segments_info(struct kexec_info *info, 
+	struct mem_ehdr *ehdr, unsigned long max_addr)
+{
+	 int i;
+         for(i = 0; i < ehdr->e_phnum; i++) {
+                unsigned long start, end;
+                struct mem_phdr *phdr;
+                phdr = &ehdr->e_phdr[i];
+                if (phdr->p_type != PT_LOAD)
+                        continue;
+                start = phdr->p_paddr;
+                end = phdr->p_paddr + phdr->p_memsz;
+
+		loaded_segments[loaded_segments_num].start =
+                        start&~(ELF_PAGE_SIZE-1);
+                loaded_segments[loaded_segments_num].end =
+                        (end + ELF_PAGE_SIZE - 1)&~(ELF_PAGE_SIZE - 1);
+		loaded_segments[loaded_segments_num].reserved = 0;
+		loaded_segments_num++;
+	}
+}
+
+static int get_crash_notes_section_addr(unsigned long *addr, int cpu)
+{
+        char crash_notes[128];
+        char line[MAX_LINE];
+        FILE *fp;
+	sprintf(crash_notes, "/sys/devices/system/cpu/cpu%d/crash_notes", cpu);
+        fp = fopen(crash_notes, "r");
+        if (!fp) {
+                fprintf(stderr, "Cannot open %s: %s\n",
+                        crash_notes, strerror(errno));
+                fprintf(stderr, "Try mounting sysfs\n");
+                return -1;
+        }
+	if (fscanf(fp, "%lx", addr) != 1) {
+		*addr = 0;
+		return -1;
+	}
+        return 0;
+}
+
+/* Removes crash reserve region from list of memory chunks for whom elf program
+ * headers have to be created. Assuming crash reserve region to be a single
+ * continuous area fully contained inside one of the memory chunks */
+static int exclude_crash_reserve_region(int *nr_ranges)
+{
+	int i, j, tidx = -1;
+	unsigned long cstart, cend;
+	struct memory_range temp_region;
+
+	/* Crash reserved region. */
+	cstart = crash_reserved_mem.start;
+	cend = crash_reserved_mem.end;
+
+	for (i = 0; i < (*nr_ranges); i++) {
+		unsigned long mstart, mend;
+		mstart = crash_memory_range[i].start;
+		mend = crash_memory_range[i].end;
+		if (cstart < mend && cend > mstart) {
+			if (cstart != mstart && cend != mend) {
+				/* Split memory region */
+				crash_memory_range[i].end = cstart - 1;
+				temp_region.start = cend + 1;
+				temp_region.end = mend;
+				temp_region.type = RANGE_RAM;
+				tidx = i+1;
+			} else if (cstart != mstart)
+				crash_memory_range[i].end = cstart - 1;
+			else
+				crash_memory_range[i].start = cend + 1;
+		}
+	}
+	/* Insert split memory region, if any. */
+	if (tidx >= 0) {
+		if (*nr_ranges == CRASH_MAX_MEMORY_RANGES) {
+			/* No space to insert another element. */
+			fprintf(stderr, "Error: Number of crash memory ranges"
+					" excedeed the max limit\n");
+			return -1;
+		}
+		for (j = (*nr_ranges - 1); j >= tidx; j--)
+			crash_memory_range[j+1] = crash_memory_range[j];
+		crash_memory_range[tidx].start = temp_region.start;
+		crash_memory_range[tidx].end = temp_region.end;
+		crash_memory_range[tidx].type = temp_region.type;
+		(*nr_ranges)++;
+	}
+	return 0;
+}
+
+static int prepare_crash_memory_elf64_headers(struct kexec_info *info,
+                                                void *buf, unsigned long size)
+{
+	Elf64_Ehdr *elf;
+	Elf64_Phdr *phdr;
+	int i;
+	long int nr_cpus = 0;
+	char *bufp = buf;
+	unsigned long notes_addr, notes_offset;
+
+	/* Setup ELF Header*/
+	elf = (Elf64_Ehdr *) bufp;
+	bufp += sizeof(Elf64_Ehdr);
+	memcpy(elf->e_ident, ELFMAG, SELFMAG);
+	elf->e_ident[EI_CLASS]  = ELFCLASS64;
+	elf->e_ident[EI_DATA]   = ELFDATA2LSB;
+	elf->e_ident[EI_VERSION]= EV_CURRENT;
+	elf->e_ident[EI_OSABI] = ELFOSABI_NONE;
+	memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
+	elf->e_type     = ET_CORE;
+	elf->e_machine  = EM_IA_64;
+	elf->e_version  = EV_CURRENT;
+	elf->e_entry    = 0;
+	elf->e_phoff    = sizeof(Elf64_Ehdr);
+	elf->e_shoff    = 0;
+	elf->e_flags    = 0;
+	elf->e_ehsize   = sizeof(Elf64_Ehdr);
+	elf->e_phentsize= sizeof(Elf64_Phdr);
+	elf->e_phnum    = 0;
+	elf->e_shentsize= 0;
+	elf->e_shnum    = 0;
+	elf->e_shstrndx = 0;
+
+	/* PT_NOTE program headers. One per cpu*/
+	nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
+	if (nr_cpus < 0) {
+		return -1;
+	}
+
+        /* Need to find a better way to determine per cpu notes section size. */
+#define MAX_NOTE_BYTES  1024
+
+        for (i = 0; i < nr_cpus; i++) {
+        	if (get_crash_notes_section_addr (&notes_addr, i) < 0) 
+                	break;	
+		notes_offset = notes_addr;
+		phdr = (Elf64_Phdr *) bufp;
+                bufp += sizeof(Elf64_Phdr);
+                phdr->p_type    = PT_NOTE;
+                phdr->p_flags   = 0;
+                phdr->p_offset  = notes_offset;
+                phdr->p_vaddr   = phdr->p_paddr = notes_offset;
+                phdr->p_filesz  = phdr->p_memsz = MAX_NOTE_BYTES;
+                /* Do we need any alignment of segments? */
+                phdr->p_align   = 0;
+
+                /* Increment number of program headers. */
+                (elf->e_phnum)++;
+        }
+
+	for (i = 0; i < memory_ranges; i++) {
+		unsigned long mstart, mend;
+		mstart = crash_memory_range[i].start;
+		mend = crash_memory_range[i].end;
+		if (!mstart && !mend)
+			break;
+		phdr = (Elf64_Phdr *) bufp;
+		bufp += sizeof(Elf64_Phdr);
+		phdr->p_type    = PT_LOAD;
+		phdr->p_flags   = PF_R|PF_W|PF_X;
+		phdr->p_offset  = mstart;
+		/*add region 5 mapping for kernel*/
+		if (kernel_code_start >= mstart && kernel_code_start < mend) {
+			phdr->p_vaddr = mstart + LOAD_OFFSET;
+			phdr->p_paddr = mstart;
+			phdr->p_filesz  = phdr->p_memsz = mend - mstart + 1;
+			phdr->p_align   = 0;
+			(elf->e_phnum)++;
+
+			phdr = (Elf64_Phdr *) bufp;
+			bufp += sizeof(Elf64_Phdr);
+			phdr->p_type    = PT_LOAD;
+			phdr->p_flags   = PF_R|PF_W|PF_X;
+			phdr->p_offset  = mstart;
+		}
+		phdr->p_vaddr = mstart + PAGE_OFFSET;
+		phdr->p_paddr = mstart;
+		phdr->p_filesz  = phdr->p_memsz = mend - mstart + 1;
+		phdr->p_align   = 0;
+		(elf->e_phnum)++;
+	}
+	return 0;
+}
+
+static int get_crash_memory_ranges(struct memory_range **range, int *ranges)
+{
+	const char iomem[]= "/proc/iomem";
+        char line[MAX_LINE];
+        FILE *fp;
+        unsigned long start, end;
+
+        fp = fopen(iomem, "r");
+        if (!fp) {
+                fprintf(stderr, "Cannot open %s: %s\n",
+                        iomem, strerror(errno));
+                return -1;
+        }
+	while(fgets(line, sizeof(line), fp) != 0) {
+		char *str;
+		int type, consumed, count;
+		if (memory_ranges >= CRASH_MAX_MEMORY_RANGES)
+			break;
+		count = sscanf(line, "%lx-%lx : %n",
+				&start, &end, &consumed);
+		str = line + consumed;
+		if (count != 2)
+			continue;
+
+		if (memcmp(str, "System RAM\n", 11) == 0) {
+			type = RANGE_RAM;
+		} else if (memcmp(str, "Crash kernel\n", 13) == 0) {
+			/* Reserved memory region. New kernel can
+			 * use this region to boot into. */
+			crash_reserved_mem.start = start;
+			crash_reserved_mem.end = end;
+			crash_reserved_mem.type = RANGE_RAM;
+			continue;
+		}
+		else if (memcmp(str, "Kernel code\n", 12) == 0) {
+			kernel_code_start = start;
+			continue;
+		}else
+			continue;
+		crash_memory_range[memory_ranges].start = start;
+		crash_memory_range[memory_ranges].end = end;
+		crash_memory_range[memory_ranges].type = type;
+		memory_ranges++;
+	}
+        fclose(fp);
+	if (exclude_crash_reserve_region(&memory_ranges) < 0)
+		return -1;
+	*ranges = memory_ranges;
+	return 0;
+}
+
+static void 
+cmdline_add_elfcorehdr(char **cmdline, unsigned long addr)
+{
+	char *str = *cmdline;
+	char buf[64];
+	size_t len;
+	sprintf(buf, " elfcorehdr=%ldK", addr/1024);
+	len = strlen(str) + strlen(buf) + 1;
+	str = xmalloc(len);
+	sprintf(str, "%s%s", *cmdline, buf);
+	*cmdline = str;
+}
+
+int load_crashdump_segments(struct kexec_info *info, struct mem_ehdr *ehdr,
+                            unsigned long max_addr, unsigned long min_base, 
+			    char **cmdline)
+{
+	//struct memory_range *mem_range, *memmap_p;
+	struct memory_range *mem_range;
+	int nr_ranges;
+	size_t size;	
+	void *tmp;
+	if (info->kexec_flags & KEXEC_ON_CRASH ) {
+		if (get_crash_memory_ranges(&mem_range, &nr_ranges) == 0) {
+			size =  sizeof(Elf64_Ehdr) +
+				(nr_ranges + 1) * sizeof(Elf64_Phdr);
+			size = (size + EFI_PAGE_SIZE - 1) & ~(EFI_PAGE_SIZE - 1);
+			tmp = xmalloc(size);
+			memset(tmp, 0, size);
+			if (prepare_crash_memory_elf64_headers(info, tmp, size) < 0)
+				return -1;
+			elfcorehdr = add_buffer(info, tmp, size, size, EFI_PAGE_SIZE, min_base,
+					max_addr, -1);
+			loaded_segments[loaded_segments_num].start = elfcorehdr;
+			loaded_segments[loaded_segments_num].end = elfcorehdr + size;
+			loaded_segments[loaded_segments_num].reserved = 1;
+			loaded_segments_num++;
+			cmdline_add_elfcorehdr(cmdline, elfcorehdr);
+		}
+	}
+	add_loaded_segments_info(info, ehdr, max_addr);
+	size = sizeof(struct loaded_segment) * loaded_segments_num;
+	qsort(loaded_segments, loaded_segments_num,
+                        sizeof(struct loaded_segment), seg_comp);
+        loaded_segments_base = add_buffer(info, loaded_segments,
+                        size, size, 16, 0, max_addr, -1);
+
+        elf_rel_set_symbol(&info->rhdr, "__loaded_segments",
+                        &loaded_segments_base, sizeof(long));
+        elf_rel_set_symbol(&info->rhdr, "__loaded_segments_num",
+                         &loaded_segments_num, sizeof(long));
+	return 0;
+}
+
+
diff -Nraup a/kexec/arch/ia64/crashdump-ia64.h b/kexec/arch/ia64/crashdump-ia64.h
--- a/kexec/arch/ia64/crashdump-ia64.h	1970-01-01 08:00:00.000000000 +0800
+++ b/kexec/arch/ia64/crashdump-ia64.h	2006-06-03 11:56:58.000000000 +0800
@@ -0,0 +1,13 @@
+#ifndef CRASHDUMP_IA64_H
+#define CRASHDUMP_IA64_H
+
+#define PAGE_OFFSET             0xe000000000000000UL
+#define __pa(x)			((unsigned long)(x)-PAGE_OFFSET)
+extern int load_crashdump_segments(struct kexec_info *info, 
+		struct mem_ehdr *ehdr, unsigned long max_addr, 
+		unsigned long min_base, char **cmdline);
+
+#define CRASH_MAX_MEMMAP_NR     (KEXEC_MAX_SEGMENTS + 1)
+#define CRASH_MAX_MEMORY_RANGES (MAX_MEMORY_RANGES + 2)
+
+#endif
diff -Nraup a/kexec/arch/ia64/kexec-elf-ia64.c b/kexec/arch/ia64/kexec-elf-ia64.c
--- a/kexec/arch/ia64/kexec-elf-ia64.c	2004-12-22 04:01:37.000000000 +0800
+++ b/kexec/arch/ia64/kexec-elf-ia64.c	2006-06-03 11:58:45.000000000 +0800
@@ -6,6 +6,7 @@
  * Copyright (C) 2004 Silicon Graphics, Inc.
  *   Jesse Barnes <jbarnes@xxxxxxx>
  * Copyright (C) 2004 Khalid Aziz <khalid.aziz@xxxxxx> Hewlett Packard Co
+ * Copyright (C) 2005 Zou Nan hai <nanhai.zou@xxxxxxxxx> Intel Corp
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -34,11 +35,14 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <getopt.h>
+#include <limits.h>
 #include <elf.h>
 #include <boot/elf_boot.h>
 #include <ip_checksum.h>
 #include "../../kexec.h"
+#include "../../kexec-syscall.h"
 #include "../../kexec-elf.h"
+#include "crashdump-ia64.h"
 #include <arch/options.h>
 
 static const int probe_debug = 0;
@@ -74,48 +78,79 @@ void elf_ia64_usage(void)
 {
 	printf(
 		"    --command-line=STRING Set the kernel command line to STRING.\n"
-		"    --append=STRING       Set the kernel command line to STRING.\n");
+		"    --append=STRING       Set the kernel command line to STRING.\n"
+		"    --initrd=STRING       Set the kernel initrd to STRING.\n");
+}
+
+/* Move the crash kerenl physical offset to reserved region
+ */
+static void move_loaded_segments(struct kexec_info *info, struct mem_ehdr *ehdr)
+{
+	 int i;
+	 long offset;
+	 struct mem_phdr *phdr;
+	 for(i = 0; i < ehdr->e_phnum; i++) {
+		 phdr = &ehdr->e_phdr[i];
+		 if (phdr->p_type == PT_LOAD) {
+		 	offset = mem_min - phdr->p_paddr;
+			break;
+		}
+	 }
+	 ehdr->e_entry += offset;
+	 for(i = 0; i < ehdr->e_phnum; i++) {
+		 phdr = &ehdr->e_phdr[i];
+		 if (phdr->p_type == PT_LOAD) 
+			phdr->p_paddr += offset;
+	 }
 }
 
 int elf_ia64_load(int argc, char **argv, const char *buf, off_t len,
 	struct kexec_info *info)
 {
 	struct mem_ehdr ehdr;
-	const char *command_line;
-	int command_line_len;
-	unsigned long entry, max_addr;
+	char *command_line = 0, *ramdisk=0;
+	char *ramdisk_buf;
+	off_t ramdisk_size = 0;
+	unsigned long command_line_len;
+	unsigned long entry, max_addr, gp_value;
+	unsigned long command_line_base, ramdisk_base;
+	unsigned long efi_memmap_base, efi_memmap_size;
 	int result;
 	int opt;
+	char *efi_memmap_buf;
 #define OPT_APPEND	(OPT_ARCH_MAX+0)
+#define OPT_RAMDISK	(OPT_ARCH_MAX+1)
 	static const struct option options[] = {
 		KEXEC_ARCH_OPTIONS
 		{"command-line", 1, 0, OPT_APPEND},
 		{"append",       1, 0, OPT_APPEND},
+		{"initrd",       1, 0, OPT_RAMDISK},
 		{0, 0, 0, 0},
 	};
 
 	static const char short_options[] = KEXEC_ARCH_OPT_STR "";
 
 	command_line = 0;
-	while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
+	while ((opt = getopt_long(argc, argv, short_options, options, 0)) 
+			!= -1) {
 		switch (opt) {
-		default:
-			/* Ignore core options */
-			if (opt < OPT_ARCH_MAX) {
+			default:
+				/* Ignore core options */
+				if (opt < OPT_ARCH_MAX) {
+					break;
+				}
+			case '?':
+				usage();
+				return -1;
+			case OPT_APPEND:
+				command_line = optarg;
+				break;
+			case OPT_RAMDISK:
+				ramdisk = optarg;
 				break;
-			}
-		case '?':
-			usage();
-			return -1;
-		case OPT_APPEND:
-			command_line = optarg;
-			break;
 		}
 	}
 	command_line_len = 0;
-	if (command_line) {
-		command_line_len = strlen(command_line) + 1;
-	}
 
 	/* Parse the Elf file */
 	result = build_elf_exec_info(buf, len, &ehdr);
@@ -124,18 +159,85 @@ int elf_ia64_load(int argc, char **argv,
 		free_elf_info(&ehdr);
 		return result;
 	}
+	if (info->kexec_flags & KEXEC_ON_CRASH ) {
+		if ((mem_min == 0x00) && (mem_max = ULONG_MAX)) {
+                       fprintf(stderr, "Failed to find crash kernel region in /proc/iomem\n");
+                       return -1;
+               }
+               move_loaded_segments(info, &ehdr);
+       }
+
 	entry = ehdr.e_entry;
 	max_addr = elf_max_addr(&ehdr);
-
 	/* Load the Elf data */
 	result = elf_exec_load(&ehdr, info);
-	free_elf_info(&ehdr);
 	if (result < 0) {
 		fprintf(stderr, "ELF load failed\n");
 		return result;
 	}
-	
-	/* For now we don't have arguments to pass :( */
-	info->entry = (void *)entry;
+	/* Load the setup code */
+	elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
+			0, ULONG_MAX, -1);
+
+	if (load_crashdump_segments(info, &ehdr, max_addr, 0, 
+				&command_line) < 0)
+		return -1;
+
+	// reserve 8k for efi_memmap
+	efi_memmap_size = 1UL<<14; 
+	efi_memmap_buf = xmalloc(efi_memmap_size);
+	efi_memmap_base = add_buffer(info, efi_memmap_buf,
+			efi_memmap_size, efi_memmap_size, 4096, 0, 
+			max_addr, -1);
+
+	elf_rel_set_symbol(&info->rhdr, "__efi_memmap_base", 
+			&efi_memmap_base, sizeof(long));
+
+	elf_rel_set_symbol(&info->rhdr, "__efi_memmap_size", 
+			&efi_memmap_size, sizeof(long));
+	if (command_line) {
+		command_line_len = strlen(command_line) + 1;
+	}
+	if (command_line_len || (info->kexec_flags & KEXEC_ON_CRASH )) {
+		char *cmdline = xmalloc(command_line_len);
+		strcpy(cmdline, command_line);
+
+		if (info->kexec_flags & KEXEC_ON_CRASH) {
+			char buf[128];
+			sprintf(buf," max_addr=%lluM min_addr=%lluM", 
+					mem_max>>20, mem_min>>20);
+			command_line_len = strlen(cmdline) + strlen(buf) + 1;
+			cmdline = xrealloc(cmdline, command_line_len);  
+			strcat(cmdline, buf);
+		}
+
+		command_line_len = (command_line_len + 15)&(~15);
+		command_line_base = add_buffer(info, cmdline,
+				command_line_len, command_line_len,
+				getpagesize(), 0UL, 
+				max_addr, -1);
+		elf_rel_set_symbol(&info->rhdr, "__command_line_len",
+				&command_line_len, sizeof(long));
+		elf_rel_set_symbol(&info->rhdr, "__command_line",
+				&command_line_base, sizeof(long));
+	}
+	if (ramdisk) {
+		ramdisk_buf = slurp_file(ramdisk, &ramdisk_size);
+		ramdisk_base = add_buffer(info, ramdisk_buf, ramdisk_size,
+				ramdisk_size,
+				getpagesize(), 0, max_addr, -1);
+		elf_rel_set_symbol(&info->rhdr, "__ramdisk_base",
+				&ramdisk_base, sizeof(long));
+		elf_rel_set_symbol(&info->rhdr, "__ramdisk_size",
+				&ramdisk_size, sizeof(long));
+	}
+
+	gp_value = info->rhdr.rel_addr + 0x200000;
+	elf_rel_set_symbol(&info->rhdr, "__gp_value", &gp_value,
+			sizeof(gp_value));
+
+	elf_rel_set_symbol(&info->rhdr, "__kernel_entry", &entry, sizeof(entry));
+
+	free_elf_info(&ehdr);
 	return 0;
 }
diff -Nraup a/kexec/arch/ia64/kexec-elf-rel-ia64.c b/kexec/arch/ia64/kexec-elf-rel-ia64.c
--- a/kexec/arch/ia64/kexec-elf-rel-ia64.c	2004-12-21 06:43:23.000000000 +0800
+++ b/kexec/arch/ia64/kexec-elf-rel-ia64.c	2006-06-03 11:56:58.000000000 +0800
@@ -1,8 +1,32 @@
+/*
+ * Copyright (C) 2005-2006  Zou Nan hai (nanhai.zou@xxxxxxxxx)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*  pugatory relocation code
+ *  Most of the code in this file is
+ *  based on arch/ia64/kernel/module.c in Linux kernel
+ */
+
 #include <stdio.h>
 #include <elf.h>
 #include "../../kexec.h"
 #include "../../kexec-elf.h"
 
+#define MAX_LTOFF       ((uint64_t) (1 << 22))
+
 int machine_verify_elf_rel(struct mem_ehdr *ehdr)
 {
 	if (ehdr->ei_data != ELFDATA2LSB) {
@@ -17,12 +41,49 @@ int machine_verify_elf_rel(struct mem_eh
 	return 1;
 }
 
+static void
+ia64_patch (uint64_t insn_addr, uint64_t mask, uint64_t val)
+{
+        uint64_t m0, m1, v0, v1, b0, b1, *b = (uint64_t *) (insn_addr & -16);
+#       define insn_mask ((1UL << 41) - 1)
+        unsigned long shift;
+
+        b0 = b[0]; b1 = b[1];
+        shift = 5 + 41 * (insn_addr % 16); /* 5 bits of template, then 3 x 41-bit instructions */
+        if (shift >= 64) {
+                m1 = mask << (shift - 64);
+                v1 = val << (shift - 64);
+        } else {
+                m0 = mask << shift; m1 = mask >> (64 - shift);
+                v0 = val  << shift; v1 = val >> (64 - shift);
+                b[0] = (b0 & ~m0) | (v0 & m0);
+        }
+        b[1] = (b1 & ~m1) | (v1 & m1);
+}
+
+static void 
+put_unaligned64(unsigned long val, unsigned char *location) 
+{
+	unsigned char *src = (unsigned char *)&val;
+	int i;
+	for (i = 0; i < sizeof(long); i++)
+		*location++ = *src++;
+}
+
+static inline uint64_t
+bundle (const uint64_t insn)
+{
+        return insn & ~0xfUL;
+}
+
 void machine_apply_elf_rel(struct mem_ehdr *ehdr, unsigned long r_type,
 	void *location, unsigned long address, unsigned long value)
 {
+	uint64_t gp_value = ehdr->rel_addr + 0x200000;
 	switch(r_type) {
 	case R_IA64_NONE:
 		break;
+	case R_IA64_SEGREL64LSB:
 	case R_IA64_DIR64LSB:
 		*((uint64_t *)location) = value;
 		break;
@@ -31,15 +92,72 @@ void machine_apply_elf_rel(struct mem_eh
 		if (value != *((uint32_t *)location))
 			goto overflow;
 		break;
-	case R_IA64_PCREL21B:
+	case R_IA64_IMM64:
+		ia64_patch((uint64_t)location, 0x01fffefe000UL, 
+				/* bit 63 -> 36 */
+				(((value & 0x8000000000000000UL) >> 27) 
+				/* bit 21 -> 21 */
+				  | ((value & 0x0000000000200000UL) <<  0) 
+				/* bit 16 -> 22 */
+				  | ((value & 0x00000000001f0000UL) <<  6) 
+				/* bit 7 -> 27 */
+				  | ((value & 0x000000000000ff80UL) << 20) 
+				/* bit 0 -> 13 */
+				  | ((value & 0x000000000000007fUL) << 13)));
+		ia64_patch((uint64_t)location - 1, 0x1ffffffffffUL, value>>22);
+		break;
+	case R_IA64_IMM22:
+		if (value + (1 << 21) >= (1 << 22))
+                	die("value out of IMM22 range\n");
+		ia64_patch((uint64_t)location, 0x01fffcfe000UL,
+				/* bit 21 -> 36 */
+				(((value & 0x200000UL) << 15)
+				/* bit 16 -> 22 */
+				 | ((value & 0x1f0000UL) <<  6)
+				/* bit  7 -> 27 */
+				 | ((value & 0x00ff80UL) << 20)
+				/* bit  0 -> 13 */
+				 | ((value & 0x00007fUL) << 13) ));
+		break;
+	case R_IA64_PCREL21B: {
+		uint64_t delta = ((int64_t)value - (int64_t)address)/16;
+		if (delta + (1 << 20) >= (1 << 21))
+			die("value out of IMM21B range\n");
+		value = ((int64_t)(value - bundle(address)))/16;
+		ia64_patch((uint64_t)location, 0x11ffffe000UL,
+				(((value & 0x100000UL) << 16) /* bit 20 -> 36 */
+				 | ((value & 0x0fffffUL) << 13) /* bit  0 -> 13 */));
+		}
+		break;
+	case R_IA64_PCREL64LSB: {
+		value = value - address;
+		put_unaligned64(value, location);	
+	} break;
+	case R_IA64_GPREL22:
+	case R_IA64_LTOFF22X:
+		if (value - gp_value + MAX_LTOFF/2 >= MAX_LTOFF)
+			die("value out of gp relative range");
+		value -= gp_value;
+		ia64_patch((uint64_t)location, 0x01fffcfe000UL,
+				(((value & 0x200000UL) << 15) /* bit 21 -> 36 */
+				   |((value & 0x1f0000UL) <<  6) /* bit 16 -> 22 */
+				   |((value & 0x00ff80UL) << 20) /* bit  7 -> 27 */
+				   |((value & 0x00007fUL) << 13) /* bit  0 -> 13 */));
+		break;
+	case R_IA64_LDXMOV:
+		if (value - gp_value + MAX_LTOFF/2 >= MAX_LTOFF)
+			die("value out of gp relative range");
+		ia64_patch((uint64_t)location, 0x1fff80fe000UL, 0x10000000000UL);
+	        break;
 	case R_IA64_LTOFF22:
-	case R_IA64_SEGREL64LSB:
+
 	default:
-		die("Unknown rela relocation: %lu\n", r_type);
+		die("Unknown rela relocation: 0x%lx 0x%lx\n",
+				r_type, address);
 		break;
 	}
 	return;
- overflow:
+overflow:
 	die("overflow in relocation type %lu val %Lx\n", 
-		r_type, value);
+			r_type, value);
 }
diff -Nraup a/kexec/arch/ia64/kexec-ia64.c b/kexec/arch/ia64/kexec-ia64.c
--- a/kexec/arch/ia64/kexec-ia64.c	2006-06-03 11:56:06.000000000 +0800
+++ b/kexec/arch/ia64/kexec-ia64.c	2006-06-03 12:00:38.000000000 +0800
@@ -6,6 +6,8 @@
  * Copyright (C) 2004 Silicon Graphics, Inc.
  *   Jesse Barnes <jbarnes@xxxxxxx>
  *
+ * Copyright (C) 2005-2006 Zou Nan hai <nanhai.zou@xxxxxxxxx> Intel Corp
+
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation (version 2 of the License).
@@ -27,40 +29,84 @@
 #include <stdint.h>
 #include <string.h>
 #include <getopt.h>
+#include <sched.h>
 #include <sys/utsname.h>
 #include "../../kexec.h"
 #include "../../kexec-syscall.h"
 #include "kexec-ia64.h"
 #include <arch/options.h>
 
-#define MAX_MEMORY_RANGES 64
 #define MAX_LINE 160
 static struct memory_range memory_range[MAX_MEMORY_RANGES];
 
 /* Return a sorted list of available memory ranges. */
 int get_memory_ranges(struct memory_range **range, int *ranges,
-				unsigned long kexec_flags)
+	   			unsigned long kexec_flags)
 {
-	int memory_ranges;
-	/*
-	 * /proc/iomem on ia64 does not show where all memory is. If
-	 * that is fixed up, we can make use of that to validate
-	 * the memory range kernel will be loade din. Until then.....
-	 * -- Khalid Aziz
-	 */
-	
-	/* Note that the ia64 architecture mandates all systems will
-	 * have at least 64MB at 0-64M.  The SGI altix does not follow
-	 * that restriction, but a reasonable guess is better than nothing
-	 * at all.
-	 * -- Eric Biederman
-	 */
-	fprintf(stderr, "Warning assuming memory at 0-64MB is present\n");
-	memory_ranges = 0;
-	memory_range[memory_ranges].start = 0x00010000;
-	memory_range[memory_ranges].end   = 0x10000000;
-	memory_range[memory_ranges].type  = RANGE_RAM;
-	memory_ranges++;
+	const char iomem[]= "/proc/iomem";
+	int memory_ranges = 0;
+	char line[MAX_LINE];
+	FILE *fp;
+	fp = fopen(iomem, "r");
+	if (!fp) {
+		fprintf(stderr, "Cannot open %s: %s\n", 
+			iomem, strerror(errno));
+		return -1;
+	}
+
+	while(fgets(line, sizeof(line), fp) != 0) {
+		unsigned long start, end;
+		char *str;
+		int type;
+		int consumed;
+		int count;
+		if (memory_ranges >= MAX_MEMORY_RANGES)
+			break;
+		count = sscanf(line, "%lx-%lx : %n",
+				&start, &end, &consumed);
+		if (count != 2) 
+			continue;
+		str = line + consumed;
+		end = end + 1;
+		if (memcmp(str, "System RAM\n", 11) == 0) {
+			type = RANGE_RAM;
+		} 
+		else if (memcmp(str, "reserved\n", 9) == 0) {
+			type = RANGE_RESERVED;
+		}
+		else if (memcmp(str, "Crash kernel\n", 13) == 0) {
+			/* Redefine the memory region boundaries if kernel
+			 * exports the limits and if it is panic kernel.
+			 * Override user values only if kernel exported values are
+			 * subset of user defined values.
+			 */
+
+			if (kexec_flags & KEXEC_ON_CRASH) {
+				if (start > mem_min)
+					mem_min = start;
+				if (end < mem_max)
+					mem_max = end;
+			}
+			continue;
+		} else
+			continue;
+		/*
+		 * Check if this memory range can be coalesced with
+		 * the previous range
+		 */
+		if ((memory_ranges > 0) &&
+				(start == memory_range[memory_ranges-1].end) &&
+				(type == memory_range[memory_ranges-1].type)) {
+			memory_range[memory_ranges-1].end = end;
+		}
+		else {
+			memory_range[memory_ranges].start = start;
+			memory_range[memory_ranges].end = end;
+			memory_range[memory_ranges].type = type;
+			memory_ranges++;
+		}
+	}
+	fclose(fp);
 	*range = memory_range;
 	*ranges = memory_ranges;
 	return 0;
@@ -77,9 +123,6 @@ void arch_usage(void)
 {
 }
 
-static struct {
-} arch_options = {
-};
 int arch_process_options(int argc, char **argv)
 {
 	static const struct option options[] = {
@@ -88,8 +131,11 @@ int arch_process_options(int argc, char 
 	};
 	static const char short_options[] = KEXEC_ARCH_OPT_STR;
 	int opt;
-	unsigned long value;
-	char *end;
+	/* execute from BP */
+        cpu_set_t affinity;
+	CPU_ZERO(&affinity);
+	CPU_SET(0, &affinity);
+        sched_setaffinity(0, sizeof(affinity), &affinity);
 
 	opterr = 0; /* Don't complain about unrecognized options here */
 	while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
@@ -116,32 +162,7 @@ int arch_compat_trampoline(struct kexec_
 	}
 	if (strcmp(utsname.machine, "ia64") == 0)
 	{
-		info->kexec_flags |= KEXEC_ARCH_X86_64;
-	}
-	else {
-		fprintf(stderr, "Unsupported machine type: %s\n",
-			utsname.machine);
-		return -1;
-	}
-	return 0;
-}
-
-int arch_compat_trampoline(struct kexec_info *info)
-{
-	int result;
-	struct utsname utsname;
-	result = uname(&utsname);
-	if (result < 0) {
-		fprintf(stderr, "uname failed: %s\n",
-			strerror(errno));
-		return -1;
-	}
-	if (strcmp(utsname.machine, "ia64") == 0)
-	{
-		/* For compatibility with older patches 
-		 * use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_IA64 here.
-		 */
-		info->kexec_flags |= KEXEC_ARCH_DEFAULT;
+		info->kexec_flags |= KEXEC_ARCH_IA_64;
 	}
 	else {
 		fprintf(stderr, "Unsupported machine type: %s\n",
diff -Nraup a/kexec/arch/ia64/kexec-ia64.h b/kexec/arch/ia64/kexec-ia64.h
--- a/kexec/arch/ia64/kexec-ia64.h	2004-12-20 07:52:38.000000000 +0800
+++ b/kexec/arch/ia64/kexec-ia64.h	2006-06-03 11:56:58.000000000 +0800
@@ -5,5 +5,7 @@ int elf_ia64_probe(const char *buf, off_
 int elf_ia64_load(int argc, char **argv, const char *buf, off_t len,
 	struct kexec_info *info);
 void elf_ia64_usage(void);
-
+#define MAX_MEMORY_RANGES 1024
+#define EFI_PAGE_SIZE	  (1UL<<12)
+#define ELF_PAGE_SIZE	  (1UL<<16)
 #endif /* KEXEC_IA64_H */
diff -Nraup a/kexec/arch/ia64/Makefile b/kexec/arch/ia64/Makefile
--- a/kexec/arch/ia64/Makefile	2004-12-20 08:11:13.000000000 +0800
+++ b/kexec/arch/ia64/Makefile	2006-06-03 11:56:58.000000000 +0800
@@ -4,3 +4,5 @@
 KEXEC_C_SRCS+= kexec/arch/ia64/kexec-ia64.c 
 KEXEC_C_SRCS+= kexec/arch/ia64/kexec-elf-ia64.c 
 KEXEC_C_SRCS+= kexec/arch/ia64/kexec-elf-rel-ia64.c
+KEXEC_C_SRCS+= kexec/arch/ia64/crashdump-ia64.c
+
diff -Nraup a/kexec/kexec.c b/kexec/kexec.c
--- a/kexec/kexec.c	2006-06-03 11:56:06.000000000 +0800
+++ b/kexec/kexec.c	2006-06-03 11:56:58.000000000 +0800
@@ -96,6 +96,9 @@ int valid_memory_range(unsigned long sst
 			continue;
 		mstart = memory_range[i].start;
 		mend = memory_range[i].end;
+		if (i < memory_ranges - 1 && mend == memory_range[i+1].start)
+			mend = memory_range[i+1].end;
+
 		/* Check to see if we are fully contained */
 		if ((mstart <= sstart) && (mend >= send)) {
 			return 1;
@@ -187,7 +190,7 @@ unsigned long locate_hole(struct kexec_i
 	}
 
 	/* Compute the free memory ranges */
-	max_mem_ranges = memory_ranges + (info->nr_segments -1);
+	max_mem_ranges = memory_ranges + (info->nr_segments);
 	mem_range = malloc(max_mem_ranges *sizeof(struct memory_range));
 	mem_ranges = 0;
 		
diff -Nraup a/kexec/kexec-syscall.h b/kexec/kexec-syscall.h
--- a/kexec/kexec-syscall.h	2005-01-06 14:59:50.000000000 +0800
+++ b/kexec/kexec-syscall.h	2006-06-03 11:56:58.000000000 +0800
@@ -68,6 +68,6 @@ static inline long kexec_reboot(void)
 #define KEXEC_ARCH_PPC64   (21 << 16)
 #define KEXEC_ARCH_IA_64   (50 << 16)
 
-#define KEXEC_MAX_SEGMENTS 8
+#define KEXEC_MAX_SEGMENTS 16
 
 #endif /* KEXEC_SYSCALL_H */
diff -Nraup a/purgatory/arch/ia64/console-ia64.c b/purgatory/arch/ia64/console-ia64.c
--- a/purgatory/arch/ia64/console-ia64.c	2004-12-21 06:41:26.000000000 +0800
+++ b/purgatory/arch/ia64/console-ia64.c	2006-06-03 11:56:58.000000000 +0800
@@ -1,5 +1,47 @@
 #include <purgatory.h>
+#include "io.h"
+
+#define VGABASE         UNCACHED(0xb8000)
+
+/* code based on i386 console code
+ * TODO add serial support
+ */
+#define MAX_YPOS        25
+#define MAX_XPOS        80
+
+unsigned long current_ypos = 1, current_xpos = 0;
+
+static void putchar_vga(int ch)
+{
+	int  i, k, j;
+
+	if (current_ypos >= MAX_YPOS) {
+		/* scroll 1 line up */
+		for (k = 1, j = 0; k < MAX_YPOS; k++, j++) {
+			for (i = 0; i < MAX_XPOS; i++) {
+				writew(readw(VGABASE + 2*(MAX_XPOS*k + i)),
+						VGABASE + 2*(MAX_XPOS*j + i));
+			}
+		}
+		for (i = 0; i < MAX_XPOS; i++)
+			writew(0x720, VGABASE + 2*(MAX_XPOS*j + i));
+		current_ypos = MAX_YPOS-1;
+	}
+	if (ch == '\n') {
+		current_xpos = 0;
+		current_ypos++;
+	} else if (ch != '\r')  {
+		writew(((0x7 << 8) | (unsigned short) ch),
+				VGABASE + 2*(MAX_XPOS*current_ypos +
+					current_xpos++));
+		if (current_xpos >= MAX_XPOS) {
+			current_xpos = 0;
+			current_ypos++;
+		}
+	}
+}
+
 void putchar(int ch)
 {
-	/* Nothing for now */
+	putchar_vga(ch);
 }
diff -Nraup a/purgatory/arch/ia64/entry.S b/purgatory/arch/ia64/entry.S
--- a/purgatory/arch/ia64/entry.S	1970-01-01 08:00:00.000000000 +0800
+++ b/purgatory/arch/ia64/entry.S	2006-06-03 11:56:58.000000000 +0800
@@ -0,0 +1,67 @@
+/*
+ * purgatory:  setup code
+ *
+ * Copyright (C) 2005-2006  Zou Nan hai (nanhai.zou@xxxxxxxxx)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#define DECLARE_DATA8(name)	\
+.global name;	\
+.size	name, 8;	\
+name:	data8 0x0
+
+.global __dummy_efi_function
+.align  32
+.proc  __dummy_efi_function
+__dummy_efi_function:
+	mov r8=r0;;
+	br.ret.sptk.many rp;;
+.global __dummy_efi_function_end
+__dummy_efi_function_end:
+.endp 	__dummy_efi_function
+
+.global purgatory_start
+.align  32
+.proc   purgatory_start
+purgatory_start:
+	movl r2=__gp_value;;
+	ld8 gp=[r2];;
+	br.call.sptk.many b0=purgatory
+	;;
+	alloc r2 = ar.pfs, 0, 0, 2, 0
+	;;
+	mov out0=r28
+	movl out1=__ramdisk_base;;
+	br.call.sptk.many b0=ia64_env_setup
+	movl r10=__kernel_entry;;
+	ld8 r14=[r10];;
+	mov b6=r14;;
+	mov ar.lc=r0
+	mov ar.ec=r0
+	cover;;
+	invala;;
+	br.call.sptk.many  b0=b6
+.endp   purgatory_start
+
+DECLARE_DATA8(__kernel_entry)
+DECLARE_DATA8(__ramdisk_base)
+DECLARE_DATA8(__ramdisk_size)
+DECLARE_DATA8(__command_line)
+DECLARE_DATA8(__command_line_len)
+DECLARE_DATA8(__efi_memmap_base)
+DECLARE_DATA8(__efi_memmap_size)
+DECLARE_DATA8(__loaded_segments)
+DECLARE_DATA8(__loaded_segments_num)
+
+DECLARE_DATA8(__gp_value)
diff -Nraup a/purgatory/arch/ia64/io.h b/purgatory/arch/ia64/io.h
--- a/purgatory/arch/ia64/io.h	1970-01-01 08:00:00.000000000 +0800
+++ b/purgatory/arch/ia64/io.h	2006-06-03 11:56:58.000000000 +0800
@@ -0,0 +1,94 @@
+#ifndef IO_H
+#define IO_H
+#define UNCACHED(x) (void *)((x)|(1UL<<63))
+#define MF()	asm volatile ("mf.a" ::: "memory")
+#define IO_SPACE_ENCODING(p)     ((((p) >> 2) << 12) | (p & 0xfff))
+
+static inline void *io_addr (unsigned long port)
+{
+        unsigned long offset;
+	unsigned long io_base;
+	asm volatile ("mov %0=ar.k0":"=r"(io_base));
+	offset = IO_SPACE_ENCODING(port);
+        return UNCACHED(io_base | offset);
+}
+
+static inline unsigned int inb (unsigned long port)
+{
+        volatile unsigned char *addr = io_addr(port);
+        unsigned char ret;
+        ret = *addr;
+        MF();
+        return ret;
+}
+
+static inline unsigned int inw (unsigned long port)
+{
+        volatile unsigned short *addr = io_addr(port);
+        unsigned short ret;
+
+        ret = *addr;
+	MF();
+        return ret;
+}
+
+static inline unsigned int ia64_inl (unsigned long port)
+{
+        volatile unsigned int *addr = __ia64_mk_io_addr(port);
+        unsigned int ret;
+        ret = *addr;
+	MF();
+        return ret;
+}
+
+static inline void outb (unsigned char val, unsigned long port)
+{
+        volatile unsigned char *addr = io_addr(port);
+
+        *addr = val;
+	MF();
+}
+
+static inline void outw (unsigned short val, unsigned long port)
+{
+        volatile unsigned short *addr = io_addr(port);
+
+        *addr = val;
+	MF();
+}
+
+static inline void outl (unsigned int val, unsigned long port)
+{
+        volatile unsigned int *addr = io_addr(port);
+
+        *addr = val;
+	MF();
+}
+
+
+static inline unsigned char readb(const volatile void  *addr)
+{
+        return *(volatile unsigned char *) addr;
+}
+static inline unsigned short readw(const volatile void  *addr)
+{
+        return *(volatile unsigned short *) addr;
+}
+static inline unsigned int readl(const volatile void  *addr)
+{
+        return *(volatile unsigned int *) addr;
+}
+
+static inline void writeb(unsigned char b, volatile void  *addr)
+{
+        *(volatile unsigned char *) addr = b;
+}
+static inline void writew(unsigned short b, volatile void  *addr)
+{
+        *(volatile unsigned short *) addr = b;
+}
+static inline void writel(unsigned int b, volatile void  *addr)
+{
+        *(volatile unsigned int *) addr = b;
+}
+#endif
diff -Nraup a/purgatory/arch/ia64/Makefile b/purgatory/arch/ia64/Makefile
--- a/purgatory/arch/ia64/Makefile	2004-12-21 06:44:22.000000000 +0800
+++ b/purgatory/arch/ia64/Makefile	2006-06-03 11:56:58.000000000 +0800
@@ -1,9 +1,9 @@
 #
 # Purgatory ia64
 #
-
-PURGATORY_S_SRCS+=
+PCFLAGS		+= -ffixed-r28
+PURGATORY_S_SRCS+= purgatory/arch/ia64/entry.S
 PURGATORY_C_SRCS+= purgatory/arch/ia64/purgatory-ia64.c
 PURGATORY_C_SRCS+= purgatory/arch/ia64/console-ia64.c
-PURGATORY_C_SRCS+=
+PURGATORY_C_SRCS+= purgatory/arch/ia64/vga.c
 
diff -Nraup a/purgatory/arch/ia64/purgatory-ia64.c b/purgatory/arch/ia64/purgatory-ia64.c
--- a/purgatory/arch/ia64/purgatory-ia64.c	2006-06-03 11:56:06.000000000 +0800
+++ b/purgatory/arch/ia64/purgatory-ia64.c	2006-06-03 11:56:58.000000000 +0800
@@ -1,9 +1,271 @@
+/*
+ * purgatory:  setup code
+ *
+ * Copyright (C) 2005-2006  Zou Nan hai (nanhai.zou@xxxxxxxxx)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
 #include <purgatory.h>
+#include <stdint.h>
+#include <string.h>
 #include "purgatory-ia64.h"
 
-void setup_arch(void)
+#define PAGE_OFFSET             0xe000000000000000UL
+
+#define EFI_PAGE_SHIFT          12
+#define EFI_PAGE_SIZE		(1UL<<EFI_PAGE_SHIFT)
+#define EFI_PAGE_ALIGN(x)	((x + EFI_PAGE_SIZE - 1)&~(EFI_PAGE_SIZE-1))
+/* Memory types: */
+#define EFI_RESERVED_TYPE                0
+#define EFI_LOADER_CODE                  1
+#define EFI_LOADER_DATA                  2
+#define EFI_BOOT_SERVICES_CODE           3
+#define EFI_BOOT_SERVICES_DATA           4
+#define EFI_RUNTIME_SERVICES_CODE        5
+#define EFI_RUNTIME_SERVICES_DATA        6
+#define EFI_CONVENTIONAL_MEMORY          7
+#define EFI_UNUSABLE_MEMORY              8
+#define EFI_ACPI_RECLAIM_MEMORY          9
+#define EFI_ACPI_MEMORY_NVS             10
+#define EFI_MEMORY_MAPPED_IO            11
+#define EFI_MEMORY_MAPPED_IO_PORT_SPACE 12
+#define EFI_PAL_CODE                    13
+#define EFI_MAX_MEMORY_TYPE             14
+
+typedef struct {
+        uint64_t signature;
+        uint32_t revision;
+        uint32_t headersize;
+        uint32_t crc32;
+        uint32_t reserved;
+} efi_table_hdr_t;
+
+typedef struct {
+        efi_table_hdr_t hdr;
+        unsigned long get_time;
+        unsigned long set_time;
+        unsigned long get_wakeup_time;
+        unsigned long set_wakeup_time;
+        unsigned long set_virtual_address_map;
+        unsigned long convert_pointer;
+        unsigned long get_variable;
+        unsigned long get_next_variable;
+        unsigned long set_variable;
+        unsigned long get_next_high_mono_count;
+        unsigned long reset_system;
+} efi_runtime_services_t;
+
+typedef struct {
+        efi_table_hdr_t hdr;
+        unsigned long fw_vendor;        /* physical addr of CHAR16 vendor string
+ */
+        uint32_t fw_revision;
+        unsigned long con_in_handle;
+        unsigned long con_in;
+        unsigned long con_out_handle;
+        unsigned long con_out;
+        unsigned long stderr_handle;
+        unsigned long stderr;
+        unsigned long runtime;
+        unsigned long boottime;
+        unsigned long nr_tables;
+        unsigned long tables;
+} efi_system_table_t;
+
+struct ia64_boot_param {
+        uint64_t command_line;             /* physical address of command line arguments */
+        uint64_t efi_systab;               /* physical address of EFI system table */
+        uint64_t efi_memmap;               /* physical address of EFI memory map */
+        uint64_t efi_memmap_size;          /* size of EFI memory map */
+        uint64_t efi_memdesc_size;         /* size of an EFI memory map descriptor */
+        uint32_t efi_memdesc_version;      /* memory descriptor version */
+        struct {
+                uint16_t num_cols; /* number of columns on console output device */
+                uint16_t num_rows; /* number of rows on console output device */
+                uint16_t orig_x;   /* cursor's x position */
+                uint16_t orig_y;   /* cursor's y position */
+        } console_info;
+        uint64_t fpswa;            /* physical address of the fpswa interface */
+        uint64_t initrd_start;
+        uint64_t initrd_size;
+};
+
+typedef struct {
+        uint32_t type;
+        uint32_t pad;
+        uint64_t phys_addr;
+        uint64_t virt_addr;
+        uint64_t num_pages;
+        uint64_t attribute;
+} efi_memory_desc_t;
+
+struct loaded_segment {
+        unsigned long start;
+        unsigned long end;
+        unsigned long reserved;
+};
+
+struct kexec_boot_params {
+	uint64_t ramdisk_base;
+	uint64_t ramdisk_size;
+	uint64_t command_line;
+	uint64_t command_line_len;
+	uint64_t efi_memmap_base;
+	uint64_t efi_memmap_size;
+	struct loaded_segment *loaded_segments;
+	unsigned long loaded_segments_num;
+};
+
+void 
+setup_arch(void)
 {
-	/* Nothing for now */
+	reset_vga();
+}
+
+inline unsigned long PA(unsigned long addr)
+{
+	return addr - PAGE_OFFSET;
+}
+
+void 
+patch_efi_memmap(struct kexec_boot_params *params, 
+		struct ia64_boot_param *boot_param)
+{
+	void *dest = (void *)params->efi_memmap_base; 
+	void *src  = (void *)boot_param->efi_memmap;
+	unsigned long len = boot_param->efi_memmap_size;
+	unsigned long memdesc_size = boot_param->efi_memdesc_size;
+	uint64_t orig_type;
+	efi_memory_desc_t *md1, *md2;
+	void *p1, *p2, *src_end = src + len;
+	int i;
+	for (p1 = src, p2 = dest; p1 < src_end; 
+			p1 += memdesc_size, p2 += memdesc_size) {
+		unsigned long mstart, mend;
+		md1 = p1;
+		md2 = p2;
+		if (md1->num_pages == 0) 
+			continue;
+		mstart = md1->phys_addr;
+		mend = md1->phys_addr + (md1->num_pages 
+				<< EFI_PAGE_SHIFT);
+		switch (md1->type) {
+			case EFI_LOADER_DATA:
+				*md2 = *md1;
+				md2->type = EFI_CONVENTIONAL_MEMORY;
+				break;
+			default:
+				*md2 = *md1;
+		}
+		// segments are already sorted and aligned to 4K 
+		orig_type = md2->type;
+		for (i = 0; i < params->loaded_segments_num; i++) {
+			struct loaded_segment *seg;
+			seg = &params->loaded_segments[i];
+			if (seg->start >= mstart && seg->start < mend) {
+				unsigned long start_pages, mid_pages, end_pages;
+				if (seg->end > mend) {
+					p1 += memdesc_size;
+					for(; p1 < src_end; 
+							p1 += memdesc_size) {
+						md1 = p1;
+						/* TODO check contig and attribute here */
+						mend = md1->phys_addr 
+							+ (md1->num_pages << EFI_PAGE_SHIFT);
+						if (seg->end < mend)
+							break;
+					}
+				}
+				start_pages = (seg->start - mstart) 
+					>> EFI_PAGE_SHIFT;
+				mid_pages = (seg->end - seg->start)
+					>> EFI_PAGE_SHIFT;
+				end_pages  = (mend - seg->end) 
+					>> EFI_PAGE_SHIFT;
+				if (start_pages) {
+					md2->num_pages = start_pages;
+					p2 += memdesc_size; 
+					md2 = p2;
+					*md2 = *md1;
+				}
+				md2->phys_addr = seg->start;
+				md2->num_pages = mid_pages;
+				md2->type = seg->reserved ? 
+					EFI_UNUSABLE_MEMORY:EFI_LOADER_DATA;
+				if (end_pages) {
+					p2 += memdesc_size; 
+					md2 = p2;
+					*md2 = *md1;
+					md2->phys_addr = seg->end;
+					md2->num_pages = end_pages;
+					md2->type = orig_type;
+					mstart = seg->end;
+				} else 
+					break;
+			}
+		}
+	}
+	
+	boot_param->efi_memmap_size = p2 - dest;
+}
+
+void 
+flush_icache_range(char *start, unsigned long len)
+{
+	unsigned long i;
+	for (i = 0;i < len; i += 32)
+	  asm volatile("fc.i %0"::"r"(start + i):"memory");
+	asm volatile (";;sync.i;;":::"memory");
+	asm volatile ("srlz.i":::"memory");
+}
+
+extern char __dummy_efi_function[], __dummy_efi_function_end[];
+
+
+void 
+ia64_env_setup(struct ia64_boot_param *boot_param,
+	struct kexec_boot_params *params)
+{
+	unsigned long len;
+        efi_system_table_t *systab;
+        efi_runtime_services_t *runtime;
+	unsigned long *set_virtual_address_map;
+	char *command_line = (char *)params->command_line;
+	uint64_t command_line_len = params->command_line_len;
+
+	// patch efi_runtime->set_virtual_address_map to a
+	// dummy function
+	len = __dummy_efi_function_end - __dummy_efi_function;
+	memcpy(command_line + command_line_len, 
+		__dummy_efi_function, len);
+	systab = (efi_system_table_t *)boot_param->efi_systab;
+	runtime = (efi_runtime_services_t *)PA(systab->runtime);
+	set_virtual_address_map =
+		(unsigned long *)PA(runtime->set_virtual_address_map);
+	*(set_virtual_address_map) =
+		(unsigned long)(command_line + command_line_len);
+	flush_icache_range(command_line + command_line_len, len);
+
+	patch_efi_memmap(params, boot_param);
+
+	boot_param->efi_memmap = params->efi_memmap_base;
+
+	boot_param->command_line = params->command_line;
+	boot_param->console_info.orig_x = 0;
+	boot_param->console_info.orig_y = 0;
+	boot_param->initrd_start = params->ramdisk_base;
+	boot_param->initrd_size =  params->ramdisk_size;
 }
 
 /* This function can be used to execute after the SHA256 verification. */
diff -Nraup a/purgatory/arch/ia64/purgatory-ia64.h b/purgatory/arch/ia64/purgatory-ia64.h
--- a/purgatory/arch/ia64/purgatory-ia64.h	2004-12-21 06:44:55.000000000 +0800
+++ b/purgatory/arch/ia64/purgatory-ia64.h	2006-06-03 11:56:58.000000000 +0800
@@ -1,6 +1,5 @@
 #ifndef PURGATORY_IA64_H
 #define PURGATORY_IA64_H
 
-/* nothing yet */
-
+void reset_vga(void);
 #endif /* PURGATORY_IA64_H */
diff -Nraup a/purgatory/arch/ia64/vga.c b/purgatory/arch/ia64/vga.c
--- a/purgatory/arch/ia64/vga.c	1970-01-01 08:00:00.000000000 +0800
+++ b/purgatory/arch/ia64/vga.c	2006-06-03 11:56:58.000000000 +0800
@@ -0,0 +1,143 @@
+#include "io.h"
+void reset_vga(void)
+{
+	/* Hello */
+	inb(0x3da);
+	outb(0, 0x3c0);
+
+	/* Sequencer registers */
+	outw(0x0300, 0x3c4);
+	outw(0x0001, 0x3c4);
+	outw(0x0302, 0x3c4);
+	outw(0x0003, 0x3c4);
+	outw(0x0204, 0x3c4);
+
+	/* Ensure CRTC regs 0-7 are unlocked by clearing bit 7 of CRTC[17] */
+	outw(0x0e11, 0x3d4);
+	/* CRTC registers */
+	outw(0x5f00, 0x3d4);
+	outw(0x4f01, 0x3d4);
+	outw(0x5002, 0x3d4);
+	outw(0x8203, 0x3d4);
+	outw(0x5504, 0x3d4);
+	outw(0x8105, 0x3d4);
+	outw(0xbf06, 0x3d4);
+	outw(0x1f07, 0x3d4);
+	outw(0x0008, 0x3d4);
+	outw(0x4f09, 0x3d4);
+	outw(0x200a, 0x3d4);
+	outw(0x0e0b, 0x3d4);
+	outw(0x000c, 0x3d4);
+	outw(0x000d, 0x3d4);
+	outw(0x010e, 0x3d4);
+	outw(0xe00f, 0x3d4);
+	outw(0x9c10, 0x3d4);
+	outw(0x8e11, 0x3d4);
+	outw(0x8f12, 0x3d4);
+	outw(0x2813, 0x3d4);
+	outw(0x1f14, 0x3d4);
+	outw(0x9615, 0x3d4);
+	outw(0xb916, 0x3d4);
+	outw(0xa317, 0x3d4);
+	outw(0xff18, 0x3d4);
+
+	/* Graphic registers */
+	outw(0x0000, 0x3ce);
+	outw(0x0001, 0x3ce);
+	outw(0x0002, 0x3ce);
+	outw(0x0003, 0x3ce);
+	outw(0x0004, 0x3ce);
+	outw(0x1005, 0x3ce);
+	outw(0x0e06, 0x3ce);
+	outw(0x0007, 0x3ce);
+	outw(0xff08, 0x3ce);
+
+	/* Attribute registers */
+	inb(0x3da);
+	outb(0x00, 0x3c0);
+	outb(0x00, 0x3c0);
+
+	inb(0x3da);
+	outb(0x01, 0x3c0);
+	outb(0x01, 0x3c0);
+
+	inb(0x3da);
+	outb(0x02, 0x3c0);
+	outb(0x02, 0x3c0);
+
+	inb(0x3da);
+	outb(0x03, 0x3c0);
+	outb(0x03, 0x3c0);
+
+	inb(0x3da);
+	outb(0x04, 0x3c0);
+	outb(0x04, 0x3c0);
+
+	inb(0x3da);
+	outb(0x05, 0x3c0);
+	outb(0x05, 0x3c0);
+
+	inb(0x3da);
+	outb(0x06, 0x3c0);
+	outb(0x14, 0x3c0);
+
+	inb(0x3da);
+	outb(0x07, 0x3c0);
+	outb(0x07, 0x3c0);
+
+	inb(0x3da);
+	outb(0x08, 0x3c0);
+	outb(0x38, 0x3c0);
+
+	inb(0x3da);
+	outb(0x09, 0x3c0);
+	outb(0x39, 0x3c0);
+
+	inb(0x3da);
+	outb(0x0a, 0x3c0);
+	outb(0x3a, 0x3c0);
+
+	inb(0x3da);
+	outb(0x0b, 0x3c0);
+	outb(0x3b, 0x3c0);
+
+	inb(0x3da);
+	outb(0x0c, 0x3c0);
+	outb(0x3c, 0x3c0);
+
+	inb(0x3da);
+	outb(0x0d, 0x3c0);
+	outb(0x3d, 0x3c0);
+
+	inb(0x3da);
+	outb(0x0e, 0x3c0);
+	outb(0x3e, 0x3c0);
+
+	inb(0x3da);
+	outb(0x0f, 0x3c0);
+	outb(0x3f, 0x3c0);
+
+	inb(0x3da);
+	outb(0x10, 0x3c0);
+	outb(0x0c, 0x3c0);
+
+	inb(0x3da);
+	outb(0x11, 0x3c0);
+	outb(0x00, 0x3c0);
+
+	inb(0x3da);
+	outb(0x12, 0x3c0);
+	outb(0x0f, 0x3c0);
+
+	inb(0x3da);
+	outb(0x13, 0x3c0);
+	outb(0x08, 0x3c0);
+
+	inb(0x3da);
+	outb(0x14, 0x3c0);
+	outb(0x00, 0x3c0);
+
+	/* Goodbye */
+	inb(0x3da);
+	outb(0x20, 0x3c0);
+}

[Index of Archives]     [Linux Kernel]     [Sparc Linux]     [DCCP]     [Linux ARM]     [Yosemite News]     [Linux SCSI]     [Linux x86_64]     [Linux for Ham Radio]

  Powered by Linux