From: Horms Subject: [PATCH] kexec: framework and i386 (Take X) Date: Thu, 25 May 2006 16:20:19 +0900 Message-ID: <20060525072017.GA25881@verge.net.au> References: <3c347db5fc144e0809c47b5efe8f6582@cl.cam.ac.uk> <12c36e8e9c573c900cd75f27b3d650d3@cl.cam.ac.uk> <20060517024402.GA13874@verge.net.au> <20060517045322.GA24072@verge.net.au> <20060517095213.GA31686@verge.net.au> <805b0fdb70b2ca542d6f9f43c0936928@cl.cam.ac.uk> <20060518033753.GB13670@verge.net.au> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="opJtzjQTFsWo+cga" Return-path: Content-Disposition: inline In-Reply-To: <20060518033753.GB13670@verge.net.au> List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xensource.com Errors-To: xen-devel-bounces@lists.xensource.com To: Keir Fraser Cc: Ian Pratt , Kazuo Moriwaka , xen-devel@lists.xensource.com, Akio Takebe , Isaku Yamahata , Magnus Damm , Mark Williamson List-Id: xen-devel@lists.xenproject.org --opJtzjQTFsWo+cga Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Hi, sorry for the somewhat long delay between sending updates. I'm happy to announce tenth take of the kexec/kdump patch. I'll address Keir's questions from the 9th release below, but first I would like to quickly summarise the patches. Kexec/kdump is implemented by moving the privelaged portions (and related plumbing where needed) from linux into the hypervisor. This is primarily done by implementing kexec's architecture independent hooks as hypercalls. Both Kexec is working for x86_32 and x86_64 for SMP and UP. Kdump is also working for SMP and UP on x86_32. x86_64 may work, but still needs more attention. In particular the register saving code has not been implemented. These patches also include some reworking of kexec's internals in order that the page table is not mangled on kdump. These changes also make x86_64 kexec/kdump somewhat easier to implement. Collectively this is the pagetable_a approach developed by my colleague Magnus Damm, and he is working with the linux kexec maintainers to get it merged there. The code is broken out into four patches. They should apply cleanly to xen-unstable.hg 10151. 1. 51.1-kexec-generic-upstream.patch * Common code for all architectures, the basic plumbing for kexec/kdump 2. 51.2.1-kexec-x86-upstream.patch * Glue between 1, and 3 and 4. This would not be needed for ppc or ia64, but neither have been written yet. We are planning to commence work on ia64 soon. * Depends on 1 3. 51.2.1.1-kexec-x86_32-upstream.patch * Kexec/kdump for x86_32 * Depends on 2 (and 1) 4. 51.2.31.2-kexec-x86_64-upstream.patch * * Kexec/kdump for x86_64 * Depends on 2 (and 1) On Thu, May 18, 2006 at 12:37:54PM +0900, Horms wrote: > On Wed, May 17, 2006 at 11:10:45AM +0100, Keir Fraser wrote: > > > > On 17 May 2006, at 10:52, Horms wrote: > > > > >as promised earlier in the day, here is an update on the kexec/kdump > > >patch. The main changes are that SMP now works, and the dumping of > > >cpu registers for kdump has been moved into the hypervisor so as to > > >allow all CPUs to be captured, not just dom0's VCPUs. > > > > Just looking at the generic patch: > > * Define KEXEC_CMD_* in your public kexec.h header, not xen.h. > > * Don't pack all the different arg structs into a union -- the union > > will change in size if you ever add a bigger argument substructure, > > plus it's ugly. Split them out and put a comment by each KEXEC_CMD_* > > definition explaining what its argument parameter points at (see other > > header files like vcpu.h for an example). I have changed both of these things. > > * Can you explain the need for all the changesin your kexec.patch? I > > guess there are some virt_to_phys address translations that need fixing > > up, but you also scatter a few hypercalls around in there (e.g., in > > base/cpu.c) -- can they not be handled more cleanly, or is kexec-on-xen > > somehow more special than kexec on any native architecture? Sure. There are several areas of change, I will address them one by one. If I have missed any, please let me know * pfn vs mfn Linux kexec works in pfns, but as kexec needs to work in real mode in Xen mfns are needed. This change should be fairly obvious, though more invasive than I would have liked. * get_crash_notes When a kernel is loaded for kexec or kdump part of the work is done in user-space. In particular the elf header is created in user-space and it needs to know the location of the elf notes where the registers are saved on crash dump. As only xen knows where all the CPUs the notes are handled by the hypervisor and a hypercall is used by get_crash_notes() to get the address of the notes which is exposed to userspace as required by kexec-tool. It is worth noting that only dom0's vcpus are exposed to user space, however all CPUs notes will be written by xen. In practice I stronly suspect that a customised tool will be needed to analyise crash dumps, well the xen specific parts anyway, and such a tool should be able to find the crash notes that are not in the elf header. Actually, I'm not really sure why the crash notes need to be in the elf header at all. In essence this code is really just there to keep kexec-tool happy and avoid having to modify it. To that end I am happy to say that neither kexec-tool nor the target kernel (crash or kexec kernel) need to be modified in order to kexec or kdump from xen. * xen_machine_kexec_load and xen_machine_kexec_unload It was originally hoped that the machine_kexec_prepare and machine_kexec_cleanup hooks could be used, however it turns out that the place that they are called in is not very useful for xen. Well, on x86_32 and x86_64 at least. So instead xen_machine_kexec_load and xen_machine_kexec_unload were added. xen_machine_kexec_load loads the kernel into xen. It is at this time that all preparation is work is done. Leavking xen_machine_kexec as just a trigger. xen_machine_kexec_unload reverses the work of xen_machine_kexec_load. -- Horms http://www.vergenet.net/~horms/ --opJtzjQTFsWo+cga Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="51.1-kexec-generic-upstream.patch" kexec: framework This is an implementation of kexec for dom0/xen, that allows kexecing of the physical machine from xen. The approach taken is to move the architecture-dependant kexec code into a new hypercall. This patch only includes the framework, it cann't be used without architecture dependant hooks, however the code should compile as is. Signed-Off-By: Horms Signed-Off-By: Magnus Damm linux-2.6-xen-sparse/drivers/xen/core/Makefile | 1 linux-2.6-xen-sparse/drivers/xen/core/crash.c | 48 + linux-2.6-xen-sparse/drivers/xen/core/machine_kexec.c | 84 ++ linux-2.6-xen-sparse/drivers/xen/core/reboot.c | 4 patches/linux-2.6.16.13/0-linux-2.6.16-kexec_page_table_a_stubs.patch | 85 ++ patches/linux-2.6.16.13/kexec-generic.patch | 294 ++++++++++ xen/arch/x86/Makefile | 2 xen/arch/x86/crash.c | 26 xen/arch/x86/machine_kexec.c | 51 + xen/common/Makefile | 1 xen/common/kexec.c | 188 ++++++ xen/common/page_alloc.c | 33 - xen/drivers/char/console.c | 3 xen/include/asm-x86/kexec.h | 32 + xen/include/public/kexec.h | 85 ++ xen/include/public/xen.h | 1 xen/include/xen/elfcore.h | 73 ++ xen/include/xen/kexec.h | 33 + xen/include/xen/mm.h | 1 19 files changed, 1034 insertions(+), 11 deletions(-) --- x/linux-2.6-xen-sparse/drivers/xen/core/Makefile +++ x/linux-2.6-xen-sparse/drivers/xen/core/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_SMP) += smpboot.o obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o obj-$(CONFIG_SYSFS) += hypervisor_sysfs.o obj-$(CONFIG_XEN_SYSFS) += xen_sysfs.o +obj-$(CONFIG_KEXEC) += machine_kexec.o crash.o --- /dev/null +++ x/linux-2.6-xen-sparse/drivers/xen/core/crash.c @@ -0,0 +1,48 @@ +/* + * Architecture independent functions for kexec based crash dumps in xen. + * + * Created by: Horms + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This passes the registers's down to the hypervisor and has it kexec() + * This is a bit different to the linux implementation which + * has this call save registers and stop CPUs and then goes into + * machine_kexec() later. But for Xen it makes more sense to + * have the kexec hypercall do everything, and this call + * has the registers parameter that is needed. + * to the hypervisor to allow the hypervisor to kdump itself + * on an internal panic + */ +void machine_crash_shutdown(struct pt_regs *regs) +{ + struct cpu_user_regs xen_regs; + printk("machine_crash_shutdown: %d\n", smp_processor_id()); + local_irq_disable(); +#ifdef CONFIG_X86_IO_APIC + disable_IO_APIC(); +#endif + crash_translate_regs(regs, &xen_regs); + HYPERVISOR_kexec(KEXEC_CMD_kexec, KEXEC_TYPE_CRASH, &xen_regs); +} + +/* + * Local variables: + * c-file-style: "linux" + * indent-tabs-mode: t + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ --- /dev/null +++ x/linux-2.6-xen-sparse/drivers/xen/core/machine_kexec.c @@ -0,0 +1,84 @@ +/* + * drivers/xen/core/machine_kexec.c + * handle transition of Linux booting another kernel + * + * Created By: Horms + * + * Losely based on arch/i386/kernel/machine_kexec.c + */ + +#include +#include +#include +#include +#include + +const extern unsigned char relocate_new_kernel[]; +extern unsigned int relocate_new_kernel_size; + +static inline unsigned long machine_address(struct page *page) +{ + return pfn_to_mfn(page_to_pfn(page)) << PAGE_SHIFT; +} + +static void setup_hypercall_arg(xen_kexec_image_t *xki, struct kimage *image) +{ + memset(xki, 0, sizeof(*xki)); + + xki->indirection_page = image->head; + xki->reboot_code_buffer = + machine_address(image->control_code_page); + xki->start_address = image->start; +} + +/* + * Load the image into xen so xen can kdump itself + * This might have been done in prepare, but prepare + * is currently called too early. It might make sense + * to move prepare, but for now, just add an extra hook. + */ +int xen_machine_kexec_load(struct kimage *image) +{ + xen_kexec_image_t xki; + + setup_hypercall_arg(&xki, image); + return HYPERVISOR_kexec(KEXEC_CMD_kexec_load, image->type, &xki); +} + +/* + * Unload the image that was stored by machine_kexec_load() + * This might have been done in machine_kexec_cleanup() but it + * is called too late, and its possible xen could try and kdump + * using resources that have been freed. + */ +void xen_machine_kexec_unload(struct kimage *image) +{ + xen_kexec_image_t xki; + + setup_hypercall_arg(&xki, image); + HYPERVISOR_kexec(KEXEC_CMD_kexec_unload, image->type, &xki); +} + +/* + * Do not allocate memory (or fail in any way) in machine_kexec(). + * We are past the point of no return, committed to rebooting now. + * + * This has the hypervisor move to the prefered reboot CPU, + * stop all CPUs and kexec. That is it combines machine_shutdown() + * and machine_kexec() in Linux kexec terms. + */ +NORET_TYPE void xen_machine_kexec(struct kimage *image) +{ + HYPERVISOR_kexec(KEXEC_CMD_kexec, image->type, NULL); + panic("KEXEC_CMD_kexec hypercall should not return\n"); +} + +/* + * Local variables: + * c-file-style: "linux" + * indent-tabs-mode: t + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ --- x/linux-2.6-xen-sparse/drivers/xen/core/reboot.c +++ x/linux-2.6-xen-sparse/drivers/xen/core/reboot.c @@ -67,6 +67,10 @@ void machine_power_off(void) HYPERVISOR_shutdown(SHUTDOWN_poweroff); } +#ifdef CONFIG_KEXEC +void machine_shutdown(void) { } +#endif + int reboot_thru_bios = 0; /* for dmi_scan.c */ EXPORT_SYMBOL(machine_restart); EXPORT_SYMBOL(machine_halt); --- x/xen/arch/x86/Makefile +++ x/xen/arch/x86/Makefile @@ -39,6 +39,8 @@ obj-y += trampoline.o obj-y += traps.o obj-y += usercopy.o obj-y += x86_emulate.o +obj-y += machine_kexec.o +obj-y += crash.o ifneq ($(pae),n) obj-$(x86_32) += shadow.o shadow_public.o shadow_guest32.o --- /dev/null +++ x/xen/arch/x86/crash.c @@ -0,0 +1,26 @@ +/****************************************************************************** + * arch/x86/crash.c + * + * Created By: Horms + * + * Should be based heavily on arch/i386/kernel/crash.c from Linux 2.6.16 + */ + +#include /* for printk() used in stub */ +#include +#include + +void machine_crash_shutdown(struct cpu_user_regs *regs) +{ + printk("STUB: arch/x86/crash.c: machine_crash_shutdown: not implemented\n"); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ --- /dev/null +++ x/xen/arch/x86/machine_kexec.c @@ -0,0 +1,51 @@ +/****************************************************************************** + * arch/x86/machine_kexec.c + * + * Created By: Horms + * + */ + +#include /* for printk() used in stubs */ +#include +#include + +int machine_kexec_load(int type, xen_kexec_image_t *image) +{ + printk("STUB: arch/x86/machine_kexec.c: machine_kexec_load: " + "not implemented\n"); + return -1; +} + +void machine_kexec_unload(int type, xen_kexec_image_t *image) +{ + printk("STUB: arch/x86/machine_kexec.c: machine_kexec_unload: " + "not implemented\n"); +} + +void machine_kexec_reserved(xen_kexec_reserve_t *reservation) +{ + printk("STUB: arch/x86/machine_kexec.c: machine_kexec_reserved: " + "not implemented\n"); +} + +void machine_kexec(xen_kexec_image_t *image) +{ + printk("STUB: arch/x86/machine_kexec.c: machine_kexec: " + "not implemented\n"); +} + +void machine_shutdown(xen_kexec_image_t *image) +{ + printk("STUB: arch/x86/machine_shutdown.c: machine_shutdown: " + "not implemented\n"); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ --- x/xen/common/Makefile +++ x/xen/common/Makefile @@ -7,6 +7,7 @@ obj-y += event_channel.o obj-y += grant_table.o obj-y += kernel.o obj-y += keyhandler.o +obj-y += kexec.o obj-y += lib.o obj-y += memory.o obj-y += multicall.o --- /dev/null +++ x/xen/common/kexec.c @@ -0,0 +1,188 @@ +/* + * common/kexec.c - Achitecture independent kexec code for Xen + * + * Created By: Horms + * + * Based in part on Linux 2.6.16's kernel/kexec.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +DEFINE_PER_CPU (note_buf_t, crash_notes); + +static xen_kexec_image_t kexec_image; +static int kexec_image_set = 0; +static xen_kexec_image_t kexec_crash_image; +static int kexec_crash_image_set = 0; +static int kexec_crash_lock = 0; + +/* Must call with kexec_crash_lock held */ +void __crash_kexec(struct cpu_user_regs *regs) +{ + struct cpu_user_regs fixed_regs; + + if (!kexec_crash_image_set) + return; + crash_setup_regs(&fixed_regs, regs); + machine_crash_shutdown(&fixed_regs); + machine_kexec(&kexec_crash_image); /* Does not return */ +} + +void crash_kexec(struct cpu_user_regs *regs) +{ + int locked; + + locked = xchg(&kexec_crash_lock, 1); + if (locked) + return; + __crash_kexec(regs); + xchg(&kexec_crash_lock, 0); +} + +static int get_crash_note(int vcpuid, XEN_GUEST_HANDLE(void) uarg) +{ + struct domain *domain = current->domain; + unsigned long crash_note; + struct vcpu *vcpu; + int locked; + + if (vcpuid < 0 || vcpuid > MAX_VIRT_CPUS) + return -EINVAL; + + if ( ! (vcpu = domain->vcpu[vcpuid]) ) + return -EINVAL; + + locked = xchg(&kexec_crash_lock, 1); + if (locked) + { + printk("do_kexec: (CMD_kexec_crash_note): dump is locked\n"); + return -EFAULT; + } + crash_note = __pa((unsigned long)per_cpu(crash_notes, vcpu->processor)); + xchg(&kexec_crash_lock, 0); + + if ( unlikely(copy_to_guest(uarg, &crash_note, 1) != 0) ) + { + printk("do_kexec: (CMD_kexec_crash_note): copy_to_guest failed\n"); + return -EFAULT; + } + + return 0; +} + +static int get_reserve(XEN_GUEST_HANDLE(void) uarg) +{ + xen_kexec_reserve_t reservation; + + machine_kexec_reserved(&reservation); + if ( unlikely(copy_to_guest(uarg, &reservation, 1) != 0) ) + { + printk("do_kexec (CMD_kexec_reserve): copy_to_guest failed\n"); + return -EFAULT; + } + + return 0; +} + +static int __do_kexec(unsigned long type, XEN_GUEST_HANDLE(void) uarg, + xen_kexec_image_t *image) +{ + cpu_user_regs_t regs; + + if (type == KEXEC_TYPE_DEFAULT) + machine_shutdown(image); /* Does not return */ + else + { + if ( unlikely(copy_from_guest(®s, uarg, 1) != 0) ) + { + printk("do_kexec (CMD_kexec): copy_from_guest failed\n"); + return -EFAULT; + } + __crash_kexec(®s); /* Does not return */ + } + + return -EINVAL; +} + +int do_kexec(unsigned long op, int arg1, XEN_GUEST_HANDLE(void) uarg) +{ + xen_kexec_image_t *image; + int locked; + int *image_set; + int status = -EINVAL; + + if ( !IS_PRIV(current->domain) ) + return -EPERM; + + switch (op) + { + case KEXEC_CMD_kexec_crash_note: + return get_crash_note(arg1, uarg); + case KEXEC_CMD_kexec_reserve: + return get_reserve(uarg); + } + + /* For all other ops, arg1 is the type of kexec, that is + * KEXEC_TYPE_DEFAULT or KEXEC_TYPE_CRASH */ + if (arg1 == KEXEC_TYPE_CRASH) + { + image = &kexec_crash_image; + image_set = &kexec_crash_image_set; + locked = xchg(&kexec_crash_lock, 1); + if (locked) + { + printk("do_kexec: dump is locked\n"); + return -EFAULT; + } + } + else + { + image = &kexec_image; + image_set = &kexec_image_set; + } + + switch(op) { + case KEXEC_CMD_kexec: + BUG_ON(!*image_set); + status = __do_kexec(arg1, uarg, image); + break; + case KEXEC_CMD_kexec_load: + BUG_ON(*image_set); + if ( unlikely(copy_from_guest(image, uarg, 1) != 0) ) + { + printk("do_kexec (CMD_kexec_load): copy_from_guest failed\n"); + status = -EFAULT; + break; + } + *image_set = 1; + status = machine_kexec_load(arg1, image); + break; + case KEXEC_CMD_kexec_unload: + BUG_ON(!*image_set); + *image_set = 0; + machine_kexec_unload(arg1, image); + status = 0; + break; + } + + if (arg1 == KEXEC_TYPE_CRASH) + xchg(&kexec_crash_lock, 0); + return status; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ --- x/xen/common/page_alloc.c +++ x/xen/common/page_alloc.c @@ -212,24 +212,35 @@ void init_boot_pages(paddr_t ps, paddr_t } } +unsigned long alloc_boot_pages_at(unsigned long nr_pfns, unsigned long pfn_at) +{ + unsigned long i; + + for ( i = 0; i < nr_pfns; i++ ) + if ( allocated_in_map(pfn_at + i) ) + break; + + if ( i == nr_pfns ) + { + map_alloc(pfn_at, nr_pfns); + return pfn_at; + } + + return 0; +} + unsigned long alloc_boot_pages(unsigned long nr_pfns, unsigned long pfn_align) { - unsigned long pg, i; + unsigned long pg, i = 0; for ( pg = 0; (pg + nr_pfns) < max_page; pg += pfn_align ) { - for ( i = 0; i < nr_pfns; i++ ) - if ( allocated_in_map(pg + i) ) - break; - - if ( i == nr_pfns ) - { - map_alloc(pg, nr_pfns); - return pg; - } + i = alloc_boot_pages_at(nr_pfns, pg); + if (i != 0) + break; } - return 0; + return i; } --- x/xen/drivers/char/console.c +++ x/xen/drivers/char/console.c @@ -677,6 +677,7 @@ void panic(const char *fmt, ...) unsigned long flags; static spinlock_t lock = SPIN_LOCK_UNLOCKED; extern void machine_restart(char *); + extern void crash_kexec(struct cpu_user_regs *regs); debugtrace_dump(); @@ -696,6 +697,8 @@ void panic(const char *fmt, ...) debugger_trap_immediate(); + crash_kexec(NULL); + watchdog_disable(); mdelay(5000); machine_restart(0); --- /dev/null +++ x/xen/include/asm-x86/kexec.h @@ -0,0 +1,32 @@ +/****************************************************************************** + * include/asm-x86/kexec.h + * + * Created By: Horms + * + */ + +#ifndef __X86_KEXEC_H__ +#define __X86_KEXEC_H__ + +#include /* for printk() used in stub */ +#include +#include + +static void crash_setup_regs(struct cpu_user_regs *newregs, + struct cpu_user_regs *oldregs) +{ + printk("STUB: include/asm-x86/kexec.h: crash_setup_regs: " + "not implemented\n"); +} + +#endif /* __X86_KEXEC_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ --- /dev/null +++ x/xen/include/public/kexec.h @@ -0,0 +1,85 @@ +/* + * kexec.h - Public portion + * + * Created By: Horms + * + * Types based on those in ./vcpu.h on request from Keir Frasier + */ + +#ifndef _XEN_PUBLIC_KEXEC_H +#define _XEN_PUBLIC_KEXEC_H + +#include "xen.h" + +/* + * Prototype for this hypercall is: + * int kexec_op(int cmd, int type, void *extra_args) + * @cmd == KEXEC_CMD_... + * KEXEC operation to perform + * @arg1 == Operation-specific unsigned long argument + * This could be in extra_args, but by putting it here + * copy_from_user can be avoided, inparticular in + * KEXEC_CMD_kexec during a crash dump, which is a failry + * critical section of code.If this turns out not to be + * important then it can be collapsed into extra_args. + * @extra_args == Operation-specific extra arguments (NULL if none). + */ + +#define KEXEC_TYPE_DEFAULT 0 +#define KEXEC_TYPE_CRASH 1 + +/* + * Perform kexec having previously loaded a kexec or kdump kernel + * as appropritate. + * @arg1 == KEXEC_TYPE_DEFAULT or KEXEC_TYPE_CRASH + * @extra_arg == pointer to cpu_user_regs_t structure. + */ +#define KEXEC_CMD_kexec 0 + +/* + * Load kernel image in preperation for kexec or kdump. + * @arg1 == KEXEC_TYPE_DEFAULT or KEXEC_TYPE_CRASH + * @extra_arg == pointer to xen_kexec_image_t structure. + */ +#define KEXEC_CMD_kexec_load 1 +typedef struct xen_kexec_image { + unsigned long indirection_page; + unsigned long reboot_code_buffer; + unsigned long start_address; +} xen_kexec_image_t; + +/* + * Clean up image loaded by KEXEC_CMD_kexec_load + * @arg1 == KEXEC_TYPE_DEFAULT or KEXEC_TYPE_CRASH + */ +#define KEXEC_CMD_kexec_unload 2 + +/* + * Find the base pointer and size of the area that xen has + * reserved for use by the crash kernel. + * @extra_arg == pointer to xen_kexec_reserve_t structure. + */ +#define KEXEC_CMD_kexec_reserve 3 +typedef struct xen_kexec_reserve { + unsigned long size; + unsigned long start; +} xen_kexec_reserve_t; + +/* + * Find the base pointer of the area that xen has + * reserved for use by a crash note for a given VCPU + * @extra_arg == pointer to unsigned long. + */ +#define KEXEC_CMD_kexec_crash_note 4 + +#endif /* _XEN_PUBLIC_KEXEC_H */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ --- x/xen/include/public/xen.h +++ x/xen/include/public/xen.h @@ -64,6 +64,7 @@ #define __HYPERVISOR_xenoprof_op 31 #define __HYPERVISOR_event_channel_op 32 #define __HYPERVISOR_physdev_op 33 +#define __HYPERVISOR_kexec_op 34 /* Architecture-specific hypercall definitions. */ #define __HYPERVISOR_arch_0 48 --- /dev/null +++ x/xen/include/xen/elfcore.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * include/xen/elfcore.h + * + * Created By: Horms + * + * Based heavily on include/linux/elfcore.h from Linux 2.6.16 + * Naming scheeme based on include/xen/elf.h (not include/linux/elfcore.h) + * + */ + +#ifndef __ELFCOREC_H__ +#define __ELFCOREC_H__ + +#include +#include +#include + +#define NT_PRSTATUS 1 + +typedef struct +{ + int signo; /* signal number */ + int code; /* extra code */ + int errno; /* errno */ +} ELF_Signifo; + +/* These seem to be the same length on all architectures on Linux */ +typedef int ELF_Pid; +typedef struct { + long tv_sec; + long tv_usec; +} ELF_Timeval; +typedef unsigned long ELF_Greg; +#define ELF_NGREG (sizeof (struct cpu_user_regs) / sizeof(ELF_Greg)) +typedef ELF_Greg ELF_Gregset[ELF_NGREG]; + +/* + * Definitions to generate Intel SVR4-like core files. + * These mostly have the same names as the SVR4 types with "elf_" + * tacked on the front to prevent clashes with linux definitions, + * and the typedef forms have been avoided. This is mostly like + * the SVR4 structure, but more Linuxy, with things that Linux does + * not support and which gdb doesn't really use excluded. + */ +typedef struct +{ + ELF_Signifo pr_info; /* Info associated with signal */ + short pr_cursig; /* Current signal */ + unsigned long pr_sigpend; /* Set of pending signals */ + unsigned long pr_sighold; /* Set of held signals */ + ELF_Pid pr_pid; + ELF_Pid pr_ppid; + ELF_Pid pr_pgrp; + ELF_Pid pr_sid; + ELF_Timeval pr_utime; /* User time */ + ELF_Timeval pr_stime; /* System time */ + ELF_Timeval pr_cutime; /* Cumulative user time */ + ELF_Timeval pr_cstime; /* Cumulative system time */ + ELF_Gregset pr_reg; /* GP registers */ + int pr_fpvalid; /* True if math co-processor being used. */ +} ELF_Prstatus; + +#endif /* __ELFCOREC_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ --- /dev/null +++ x/xen/include/xen/kexec.h @@ -0,0 +1,33 @@ +/* + * include/xen/kexec.h - Internal archtecture independant portion + * + * Created By: Horms + * + */ + +#include + +#define MAX_NOTE_BYTES 1024 + +typedef u32 note_buf_t[MAX_NOTE_BYTES/4]; +DECLARE_PER_CPU (note_buf_t, crash_notes); + +int machine_kexec_load(int type, xen_kexec_image_t *image); +void machine_kexec_unload(int type, xen_kexec_image_t *image); +void machine_kexec_reserved(xen_kexec_reserve_t *reservation); +void machine_kexec(xen_kexec_image_t *image); +void machine_shutdown(xen_kexec_image_t *image); +void machine_crash_shutdown(cpu_user_regs_t *regs); + +extern unsigned int opt_kdump_megabytes; +extern unsigned int opt_kdump_megabytes_base; + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ --- x/xen/include/xen/mm.h +++ x/xen/include/xen/mm.h @@ -40,6 +40,7 @@ struct page_info; paddr_t init_boot_allocator(paddr_t bitmap_start); void init_boot_pages(paddr_t ps, paddr_t pe); unsigned long alloc_boot_pages(unsigned long nr_pfns, unsigned long pfn_align); +unsigned long alloc_boot_pages_at(unsigned long nr_pfns, unsigned long pfn_at); void end_boot_allocator(void); /* Generic allocator. These functions are *not* interrupt-safe. */ --- /dev/null +++ x/patches/linux-2.6.16.13/kexec-generic.patch @@ -0,0 +1,294 @@ + drivers/base/cpu.c | 20 ++++++++++++++ + include/linux/kexec.h | 5 +++ + kernel/kexec.c | 68 ++++++++++++++++++++++++++++++++++++++++--------- + kernel/sys.c | 4 ++ + 4 files changed, 85 insertions(+), 12 deletions(-) + +--- x/drivers/base/cpu.c ++++ x/drivers/base/cpu.c +@@ -11,6 +11,10 @@ + + #include "base.h" + ++#ifdef CONFIG_XEN ++#include ++#endif ++ + struct sysdev_class cpu_sysdev_class = { + set_kset_name("cpu"), + }; +@@ -86,6 +90,18 @@ static inline void register_cpu_control( + #ifdef CONFIG_KEXEC + #include + ++#ifdef CONFIG_XEN ++static unsigned long get_crash_notes(int cpu) ++{ ++ unsigned long crash_note; ++ ++ if (HYPERVISOR_kexec(KEXEC_CMD_kexec_crash_note, cpu, &crash_note) < 0) ++ return 0UL; ++ return crash_note; ++} ++#endif ++ ++/* XXX: This only finds dom0's CPU's */ + static ssize_t show_crash_notes(struct sys_device *dev, char *buf) + { + struct cpu *cpu = container_of(dev, struct cpu, sysdev); +@@ -101,7 +117,11 @@ static ssize_t show_crash_notes(struct s + * boot up and this data does not change there after. Hence this + * operation should be safe. No locking required. + */ ++#ifndef CONFIG_XEN + addr = __pa(per_cpu_ptr(crash_notes, cpunum)); ++#else ++ addr = (unsigned long long)get_crash_notes(cpunum); ++#endif + rc = sprintf(buf, "%Lx\n", addr); + return rc; + } +--- x/include/linux/kexec.h ++++ x/include/linux/kexec.h +@@ -91,6 +91,11 @@ struct kimage { + extern NORET_TYPE void machine_kexec(struct kimage *image) ATTRIB_NORET; + extern int machine_kexec_prepare(struct kimage *image); + extern void machine_kexec_cleanup(struct kimage *image); ++#ifdef CONFIG_XEN ++extern int xen_machine_kexec_load(struct kimage *image); ++extern void xen_machine_kexec_unload(struct kimage *image); ++extern NORET_TYPE void xen_machine_kexec(struct kimage *image) ATTRIB_NORET; ++#endif + extern asmlinkage long sys_kexec_load(unsigned long entry, + unsigned long nr_segments, + struct kexec_segment __user *segments, +--- x/kernel/kexec.c ++++ x/kernel/kexec.c +@@ -38,6 +38,20 @@ struct resource crashk_res = { + .flags = IORESOURCE_BUSY | IORESOURCE_MEM + }; + ++/* Kexec needs to know about the actually physical addresss. ++ * But in xen, a physical address is a pseudo-physical addresss. */ ++#ifndef CONFIG_XEN ++#define kexec_page_to_pfn(page) page_to_pfn(page) ++#define kexec_pfn_to_page(pfn) pfn_to_page(pfn) ++#define kexec_virt_to_phys(addr) virt_to_phys(addr) ++#define kexec_phys_to_virt(addr) phys_to_virt(addr) ++#else ++#define kexec_page_to_pfn(page) pfn_to_mfn(page_to_pfn(page)) ++#define kexec_pfn_to_page(pfn) pfn_to_page(mfn_to_pfn(pfn)) ++#define kexec_virt_to_phys(addr) virt_to_machine(addr) ++#define kexec_phys_to_virt(addr) phys_to_virt(machine_to_phys(addr)) ++#endif ++ + int kexec_should_crash(struct task_struct *p) + { + if (in_interrupt() || !p->pid || p->pid == 1 || panic_on_oops) +@@ -403,7 +417,7 @@ static struct page *kimage_alloc_normal_ + pages = kimage_alloc_pages(GFP_KERNEL, order); + if (!pages) + break; +- pfn = page_to_pfn(pages); ++ pfn = kexec_page_to_pfn(pages); + epfn = pfn + count; + addr = pfn << PAGE_SHIFT; + eaddr = epfn << PAGE_SHIFT; +@@ -437,6 +451,7 @@ static struct page *kimage_alloc_normal_ + return pages; + } + ++#ifndef CONFIG_XEN + static struct page *kimage_alloc_crash_control_pages(struct kimage *image, + unsigned int order) + { +@@ -490,7 +505,7 @@ static struct page *kimage_alloc_crash_c + } + /* If I don't overlap any segments I have found my hole! */ + if (i == image->nr_segments) { +- pages = pfn_to_page(hole_start >> PAGE_SHIFT); ++ pages = kexec_pfn_to_page(hole_start >> PAGE_SHIFT); + break; + } + } +@@ -517,6 +532,13 @@ struct page *kimage_alloc_control_pages( + + return pages; + } ++#else /* !CONFIG_XEN */ ++struct page *kimage_alloc_control_pages(struct kimage *image, ++ unsigned int order) ++{ ++ return kimage_alloc_normal_control_pages(image, order); ++} ++#endif + + static int kimage_add_entry(struct kimage *image, kimage_entry_t entry) + { +@@ -532,7 +554,7 @@ static int kimage_add_entry(struct kimag + return -ENOMEM; + + ind_page = page_address(page); +- *image->entry = virt_to_phys(ind_page) | IND_INDIRECTION; ++ *image->entry = kexec_virt_to_phys(ind_page) | IND_INDIRECTION; + image->entry = ind_page; + image->last_entry = ind_page + + ((PAGE_SIZE/sizeof(kimage_entry_t)) - 1); +@@ -593,13 +615,13 @@ static int kimage_terminate(struct kimag + #define for_each_kimage_entry(image, ptr, entry) \ + for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); \ + ptr = (entry & IND_INDIRECTION)? \ +- phys_to_virt((entry & PAGE_MASK)): ptr +1) ++ kexec_phys_to_virt((entry & PAGE_MASK)): ptr +1) + + static void kimage_free_entry(kimage_entry_t entry) + { + struct page *page; + +- page = pfn_to_page(entry >> PAGE_SHIFT); ++ page = kexec_pfn_to_page(entry >> PAGE_SHIFT); + kimage_free_pages(page); + } + +@@ -611,6 +633,10 @@ static void kimage_free(struct kimage *i + if (!image) + return; + ++#ifdef CONFIG_XEN ++ xen_machine_kexec_unload(image); ++#endif ++ + kimage_free_extra_pages(image); + for_each_kimage_entry(image, ptr, entry) { + if (entry & IND_INDIRECTION) { +@@ -686,7 +712,7 @@ static struct page *kimage_alloc_page(st + * have a match. + */ + list_for_each_entry(page, &image->dest_pages, lru) { +- addr = page_to_pfn(page) << PAGE_SHIFT; ++ addr = kexec_page_to_pfn(page) << PAGE_SHIFT; + if (addr == destination) { + list_del(&page->lru); + return page; +@@ -701,12 +727,12 @@ static struct page *kimage_alloc_page(st + if (!page) + return NULL; + /* If the page cannot be used file it away */ +- if (page_to_pfn(page) > ++ if (kexec_page_to_pfn(page) > + (KEXEC_SOURCE_MEMORY_LIMIT >> PAGE_SHIFT)) { + list_add(&page->lru, &image->unuseable_pages); + continue; + } +- addr = page_to_pfn(page) << PAGE_SHIFT; ++ addr = kexec_page_to_pfn(page) << PAGE_SHIFT; + + /* If it is the destination page we want use it */ + if (addr == destination) +@@ -729,7 +755,7 @@ static struct page *kimage_alloc_page(st + struct page *old_page; + + old_addr = *old & PAGE_MASK; +- old_page = pfn_to_page(old_addr >> PAGE_SHIFT); ++ old_page = kexec_pfn_to_page(old_addr >> PAGE_SHIFT); + copy_highpage(page, old_page); + *old = addr | (*old & ~PAGE_MASK); + +@@ -779,7 +805,7 @@ static int kimage_load_normal_segment(st + result = -ENOMEM; + goto out; + } +- result = kimage_add_page(image, page_to_pfn(page) ++ result = kimage_add_page(image, kexec_page_to_pfn(page) + << PAGE_SHIFT); + if (result < 0) + goto out; +@@ -811,6 +837,7 @@ out: + return result; + } + ++#ifndef CONFIG_XEN + static int kimage_load_crash_segment(struct kimage *image, + struct kexec_segment *segment) + { +@@ -833,7 +860,7 @@ static int kimage_load_crash_segment(str + char *ptr; + size_t uchunk, mchunk; + +- page = pfn_to_page(maddr >> PAGE_SHIFT); ++ page = kexec_pfn_to_page(maddr >> PAGE_SHIFT); + if (page == 0) { + result = -ENOMEM; + goto out; +@@ -881,6 +908,13 @@ static int kimage_load_segment(struct ki + + return result; + } ++#else /* CONFIG_XEN */ ++static int kimage_load_segment(struct kimage *image, ++ struct kexec_segment *segment) ++{ ++ return kimage_load_normal_segment(image, segment); ++} ++#endif + + /* + * Exec Kernel system call: for obvious reasons only root may call it. +@@ -991,6 +1025,11 @@ asmlinkage long sys_kexec_load(unsigned + if (result) + goto out; + } ++#ifdef CONFIG_XEN ++ result = xen_machine_kexec_load(image); ++ if (result) ++ goto out; ++#endif + /* Install the new kernel, and Uninstall the old */ + image = xchg(dest_image, image); + +@@ -1045,7 +1084,6 @@ void crash_kexec(struct pt_regs *regs) + struct kimage *image; + int locked; + +- + /* Take the kexec_lock here to prevent sys_kexec_load + * running on one cpu from replacing the crash kernel + * we are using after a panic on a different cpu. +@@ -1061,12 +1099,17 @@ void crash_kexec(struct pt_regs *regs) + struct pt_regs fixed_regs; + crash_setup_regs(&fixed_regs, regs); + machine_crash_shutdown(&fixed_regs); ++#ifdef CONFIG_XEN ++ xen_machine_kexec(image); ++#else + machine_kexec(image); ++#endif + } + xchg(&kexec_lock, 0); + } + } + ++#ifndef CONFIG_XEN + static int __init crash_notes_memory_init(void) + { + /* Allocate memory for saving cpu registers. */ +@@ -1079,3 +1122,4 @@ static int __init crash_notes_memory_ini + return 0; + } + module_init(crash_notes_memory_init) ++#endif +--- x/kernel/sys.c ++++ x/kernel/sys.c +@@ -435,8 +435,12 @@ void kernel_kexec(void) + kernel_restart_prepare(NULL); + printk(KERN_EMERG "Starting new kernel\n"); + machine_shutdown(); ++#ifdef CONFIG_XEN ++ xen_machine_kexec(image); ++#else + machine_kexec(image); + #endif ++#endif + } + EXPORT_SYMBOL_GPL(kernel_kexec); + --- /dev/null +++ x/patches/linux-2.6.16.13/0-linux-2.6.16-kexec_page_table_a_stubs.patch @@ -0,0 +1,85 @@ +kexec: Avoid overwriting the current pgd (V2, stubs) + +This patch adds an architecture specific structure "struct kimage_arch" to +struct kimage. This structure is filled in with members by the architecture +specific patches followed by this one. + +Signed-off-by: Magnus Damm +--- + + Applies on top of 2.6.16 and 2.6.17-rc4. + + include/asm-i386/kexec.h | 2 ++ + include/asm-powerpc/kexec.h | 2 ++ + include/asm-s390/kexec.h | 2 ++ + include/asm-sh/kexec.h | 2 ++ + include/asm-x86_64/kexec.h | 2 ++ + include/linux/kexec.h | 2 ++ + 6 files changed, 12 insertions(+) + +--- x/include/asm-i386/kexec.h ++++ x/include/asm-i386/kexec.h +@@ -29,6 +29,8 @@ + + #define MAX_NOTE_BYTES 1024 + ++struct kimage_arch {}; ++ + /* CPU does not save ss and esp on stack if execution is already + * running in kernel mode at the time of NMI occurrence. This code + * fixes it. +--- x/include/asm-powerpc/kexec.h ++++ x/include/asm-powerpc/kexec.h +@@ -108,6 +108,8 @@ static inline void crash_setup_regs(stru + + #define MAX_NOTE_BYTES 1024 + ++struct kimage_arch {}; ++ + #ifdef __powerpc64__ + extern void kexec_smp_wait(void); /* get and clear naca physid, wait for + master to copy new code to 0 */ +--- x/include/asm-s390/kexec.h ++++ x/include/asm-s390/kexec.h +@@ -36,6 +36,8 @@ + + #define MAX_NOTE_BYTES 1024 + ++struct kimage_arch {}; ++ + /* Provide a dummy definition to avoid build failures. */ + static inline void crash_setup_regs(struct pt_regs *newregs, + struct pt_regs *oldregs) { } +--- x/include/asm-sh/kexec.h ++++ x/include/asm-sh/kexec.h +@@ -25,6 +25,8 @@ + + #ifndef __ASSEMBLY__ + ++struct kimage_arch {}; ++ + extern void machine_shutdown(void); + extern void *crash_notes; + +--- x/include/asm-x86_64/kexec.h ++++ x/include/asm-x86_64/kexec.h +@@ -29,6 +29,8 @@ + + #define MAX_NOTE_BYTES 1024 + ++struct kimage_arch {}; ++ + /* + * Saving the registers of the cpu on which panic occured in + * crash_kexec to save a valid sp. The registers of other cpus +--- x/include/linux/kexec.h ++++ x/include/linux/kexec.h +@@ -69,6 +69,8 @@ struct kimage { + unsigned long start; + struct page *control_code_page; + ++ struct kimage_arch arch_data; ++ + unsigned long nr_segments; + struct kexec_segment segment[KEXEC_SEGMENT_MAX]; + --opJtzjQTFsWo+cga Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="51.2.1-kexec-x86-upstream.patch" kexec: x86 This is the x86 component of kexec for xen. The generic component is a prerequsite for this patch. The x86_64 or x86_32 (i386) patch is also needed in order to use this code, however the code should compile is. Signed-Off-By: Horms Signed-Off-By: Magnus Damm linux-2.6-xen-sparse/drivers/xen/core/machine_kexec.c | 14 + xen/arch/x86/crash.c | 171 ++++++++++++++++- xen/arch/x86/dom0_ops.c | 3 xen/arch/x86/machine_kexec.c | 150 +++++++++++++- xen/arch/x86/setup.c | 75 ++++++- xen/arch/x86/x86_32/Makefile | 1 xen/arch/x86/x86_32/machine_kexec.c | 27 ++ xen/arch/x86/x86_64/Makefile | 1 xen/arch/x86/x86_64/machine_kexec.c | 28 ++ xen/include/asm-x86/elf.h | 27 ++ xen/include/asm-x86/fixmap.h | 1 xen/include/asm-x86/hypercall.h | 5 xen/include/asm-x86/kexec.h | 14 - xen/include/asm-x86/x86_32/elf.h | 30 ++ xen/include/asm-x86/x86_32/kexec.h | 51 +++++ xen/include/asm-x86/x86_64/elf.h | 30 ++ xen/include/asm-x86/x86_64/kexec.h | 50 ++++ xen/include/public/kexec.h | 2 18 files changed, 649 insertions(+), 31 deletions(-) --- x/linux-2.6-xen-sparse/drivers/xen/core/machine_kexec.c +++ x/linux-2.6-xen-sparse/drivers/xen/core/machine_kexec.c @@ -23,7 +23,21 @@ static inline unsigned long machine_addr static void setup_hypercall_arg(xen_kexec_image_t *xki, struct kimage *image) { +#if defined(CONFIG_X86_32) || defined(CONFIG_X86_64) + struct kimage_arch *arch = &image->arch_data; + int k, n = sizeof(arch->page_table_a) / sizeof(arch->page_table_a[0]); + + memset(xki, 0, sizeof(*xki)); + + for (k = 0; k < n; k++) + xki->page_table_a[k] = machine_address(arch->page_table_a[k]); + +#ifdef CONFIG_X86_64 + xki->page_table_b = machine_address(arch->page_table_b); +#endif +#else memset(xki, 0, sizeof(*xki)); +#endif xki->indirection_page = image->head; xki->reboot_code_buffer = --- x/xen/arch/x86/crash.c +++ x/xen/arch/x86/crash.c @@ -3,16 +3,181 @@ * * Created By: Horms * - * Should be based heavily on arch/i386/kernel/crash.c from Linux 2.6.16 + * Based heavily on arch/i386/kernel/crash.c from Linux 2.6.16 */ -#include /* for printk() used in stub */ +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +static int crashing_cpu; + +static u32 *append_elf_note(u32 *buf, char *name, unsigned type, void *data, + size_t data_len) +{ + Elf_Note note; + + note.namesz = strlen(name) + 1; + note.descsz = data_len; + note.type = type; + memcpy(buf, ¬e, sizeof(note)); + buf += (sizeof(note) +3)/4; + memcpy(buf, name, note.namesz); + buf += (note.namesz + 3)/4; + memcpy(buf, data, note.descsz); + buf += (note.descsz + 3)/4; + + return buf; +} + +static void final_note(u32 *buf) +{ + Elf_Note note; + + note.namesz = 0; + note.descsz = 0; + note.type = 0; + memcpy(buf, ¬e, sizeof(note)); +} + +static void crash_save_this_cpu(struct cpu_user_regs *regs, int cpu) +{ + ELF_Prstatus prstatus; + uint32_t *buf; + + printk("crash_save_this_cpu: %d\n", cpu); + + if ((cpu < 0) || (cpu >= NR_CPUS)) + return; + + /* Using ELF notes here is opportunistic. + * A well defined structure format with tags is needed + * ELF notes happen to provide this and there is infastructure + * in the Linux kernel to supprot them. In order to make + * crash dumps produced by xen the same, the same + * technique is used here. + */ + + /* It should be safe to use per_cpu() here instead of per_cpu_ptr() + * (which does not exist in xen) as kexecing_lock must be held in + * order to get anywhere near here */ + buf = (uint32_t *)per_cpu(crash_notes, cpu); + if (!buf) /* XXX: Can this ever occur? */ + return; + memset(&prstatus, 0, sizeof(prstatus)); + /* XXX: Xen does not have processes. For the crashing CPU on a dom0 + * crash this could be pased down from dom0, but is this + * neccessary? + * prstatus.pr_pid = current->pid; */ + ELF_CORE_COPY_REGS(prstatus.pr_reg, regs); + buf = append_elf_note(buf, "CORE", NT_PRSTATUS, &prstatus, + sizeof(prstatus)); + final_note(buf); +} + +static void crash_save_self(struct cpu_user_regs *regs) +{ + crash_save_this_cpu(regs, smp_processor_id()); +} + +#ifdef CONFIG_SMP +static atomic_t waiting_for_crash_ipi; + +static int crash_nmi_callback(struct cpu_user_regs *regs, int cpu) +{ + struct cpu_user_regs fixed_regs; + + /* Don't do anything if this handler is invoked on crashing cpu. + * Otherwise, system will completely hang. Crashing cpu can get + * an NMI if system was initially booted with nmi_watchdog parameter. + */ + if (cpu == crashing_cpu) + return 1; + local_irq_disable(); + + if (!user_mode(regs)) { + crash_fixup_ss_esp(&fixed_regs, regs); + regs = &fixed_regs; + } + crash_save_this_cpu(regs, cpu); + disable_local_APIC(); + atomic_dec(&waiting_for_crash_ipi); + /* Assume hlt works */ + __asm__ __volatile__ ( "hlt" ); + for(;;); + + return 1; + + /* Need to use this somewhere as Xen builds with -Werror */ + crash_setup_regs(&fixed_regs, regs); +} + +/* + * By using the NMI code instead of a vector we just sneak thru the + * word generator coming out with just what we want. AND it does + * not matter if clustered_apic_mode is set or not. + */ +static void smp_send_nmi_allbutself(void) +{ + cpumask_t allbutself = cpu_online_map; + cpu_clear(smp_processor_id(), allbutself); + send_IPI_mask(allbutself, APIC_DM_NMI); +} + +static void nmi_shootdown_cpus(void) +{ + unsigned long msecs; + + atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1); + /* Would it be better to replace the trap vector here? */ + set_nmi_callback(crash_nmi_callback); + /* Ensure the new callback function is set before sending + * out the NMI + */ + wmb(); + + smp_send_nmi_allbutself(); + + msecs = 1000; /* Wait at most a second for the other cpus to stop */ + while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) { + mdelay(1); + msecs--; + } + + /* Leave the nmi callback set */ + disable_local_APIC(); +} +#else +static void nmi_shootdown_cpus(void) +{ + /* There are no cpus to shootdown */ +} +#endif + void machine_crash_shutdown(struct cpu_user_regs *regs) { - printk("STUB: arch/x86/crash.c: machine_crash_shutdown: not implemented\n"); + printk("machine_crash_shutdown: %d\n", smp_processor_id()); + local_irq_disable(); + + crashing_cpu = smp_processor_id(); + nmi_shootdown_cpus(); +#ifdef CONFIG_X86_IO_APIC + disable_IO_APIC(); +#endif + crash_save_self(regs); } /* --- x/xen/arch/x86/dom0_ops.c +++ x/xen/arch/x86/dom0_ops.c @@ -29,6 +29,9 @@ #include #include "cpu/mtrr/mtrr.h" +extern unsigned int opt_kdump_megabytes; +extern unsigned int opt_kdump_megabytes_base; + #define TRC_DOM0OP_ENTER_BASE 0x00020000 #define TRC_DOM0OP_LEAVE_BASE 0x00030000 --- x/xen/arch/x86/machine_kexec.c +++ x/xen/arch/x86/machine_kexec.c @@ -6,38 +6,164 @@ */ #include /* for printk() used in stubs */ +#include +#include +#include +#include +#include +#include #include +#include +#include #include +#include +#include + +#define create_level_mapping(lvl, next, pages, nopages, k, va) \ +{ \ + lvl##_pgentry_t *table; \ + void *old = next; \ + \ + table = (lvl##_pgentry_t *)next + lvl##_table_offset(va); \ + if (!(lvl##e_get_flags(*table) & _PAGE_PRESENT)) { \ + if (k >= nopages || pages[k] == 0) \ + return -1; \ + *table = lvl##e_from_pfn(pages[k++]>>PAGE_SHIFT, __PAGE_HYPERVISOR); \ + } \ + next = map_domain_page(lvl##e_get_pfn(*table)); \ + unmap_domain_page(old); \ +} + +#define create_level_1_mapping(next, nopages, va, pa) \ +{ \ + l1_pgentry_t *table; \ + \ + table = (l1_pgentry_t *)next + l1_table_offset(va); \ + if (!(l1e_get_flags(*table) & _PAGE_PRESENT)) { \ + *table = l1e_from_pfn(pa >> PAGE_SHIFT, __PAGE_HYPERVISOR); \ + } \ + unmap_domain_page(next); \ +} + +static int create_mapping(unsigned long root, + unsigned long *pages, int nopages, + unsigned long va, unsigned long pa) +{ + void *next = map_domain_page(root >> PAGE_SHIFT); + int k = 0; + +#if CONFIG_PAGING_LEVELS >= 4 + create_level_mapping(l4, next, pages, nopages, k, va); +#endif +#if CONFIG_PAGING_LEVELS >= 3 + create_level_mapping(l3, next, pages, nopages, k, va); +#endif + create_level_mapping(l2, next, pages, nopages, k, va); + + create_level_1_mapping(next, nopages, va, pa); + + return k; +} + +static int setup_page_table_a(xen_kexec_image_t *image) +{ + void *page; + int k, n = sizeof(image->page_table_a) / sizeof(image->page_table_a[0]); + + /* clear page_table_a pages */ + + for (k = 0; k < n; k++) { + if (!image->page_table_a[k]) + break; + + page = map_domain_page(image->page_table_a[k] >> PAGE_SHIFT); + clear_page(page); + unmap_domain_page(page); + } + + /* check that the first page (root page) is actually non-zero */ + + if (k == 0) + return -1; + + /* setup fixmap to point to our control page */ + + set_fixmap(FIX_KEXEC_PAGE, image->reboot_code_buffer); + + /* fill in page_table_a: create mapping at fixmap address */ + + k = create_mapping(image->page_table_a[0], + &image->page_table_a[1], + n - 1, fix_to_virt(FIX_KEXEC_PAGE), + image->reboot_code_buffer); + if (k < 0) + return -1; + + /* fill in page_table_a: create identity mapping */ + + k = create_mapping(image->page_table_a[0], + &image->page_table_a[1 + k], + n - (1 + k), image->reboot_code_buffer, + image->reboot_code_buffer); + if (k < 0) + return -1; + + return 0; +} int machine_kexec_load(int type, xen_kexec_image_t *image) { - printk("STUB: arch/x86/machine_kexec.c: machine_kexec_load: " - "not implemented\n"); - return -1; + return setup_page_table_a(image); } void machine_kexec_unload(int type, xen_kexec_image_t *image) { - printk("STUB: arch/x86/machine_kexec.c: machine_kexec_unload: " - "not implemented\n"); } void machine_kexec_reserved(xen_kexec_reserve_t *reservation) { - printk("STUB: arch/x86/machine_kexec.c: machine_kexec_reserved: " - "not implemented\n"); + reservation->size = opt_kdump_megabytes << 20; + reservation->start = opt_kdump_megabytes_base << 20; } -void machine_kexec(xen_kexec_image_t *image) +static void __machine_shutdown(void *data) { - printk("STUB: arch/x86/machine_kexec.c: machine_kexec: " - "not implemented\n"); + xen_kexec_image_t *image = (xen_kexec_image_t *)data; + + printk("__machine_shutdown: cpu=%u\n", smp_processor_id()); + + watchdog_disable(); + console_start_sync(); + + smp_send_stop(); + +#ifdef CONFIG_X86_IO_APIC + disable_IO_APIC(); +#endif + + machine_kexec(image); } void machine_shutdown(xen_kexec_image_t *image) { - printk("STUB: arch/x86/machine_shutdown.c: machine_shutdown: " - "not implemented\n"); + int reboot_cpu_id; + cpumask_t reboot_cpu; + + reboot_cpu_id = 0; + + if (!cpu_isset(reboot_cpu_id, cpu_online_map)) + reboot_cpu_id = smp_processor_id(); + + if (reboot_cpu_id != smp_processor_id()) { + cpus_clear(reboot_cpu); + cpu_set(reboot_cpu_id, reboot_cpu); + on_selected_cpus(reboot_cpu, __machine_shutdown, image, 1, 0); + for (;;) + ; /* nothing */ + } + else + __machine_shutdown(image); + BUG(); } /* --- x/xen/arch/x86/setup.c +++ x/xen/arch/x86/setup.c @@ -38,6 +38,11 @@ static unsigned int opt_xenheap_megabyte integer_param("xenheap_megabytes", opt_xenheap_megabytes); #endif +unsigned int opt_kdump_megabytes = 0; +integer_param("kdump_megabytes", opt_kdump_megabytes); +unsigned int opt_kdump_megabytes_base = 0; +integer_param("kdump_megabytes_base", opt_kdump_megabytes_base); + /* opt_nosmp: If true, secondary processors are ignored. */ static int opt_nosmp = 0; boolean_param("nosmp", opt_nosmp); @@ -192,6 +197,20 @@ static void percpu_free_unused_areas(voi __pa(__per_cpu_end)); } +void __init move_memory(unsigned long dst, + unsigned long src_start, unsigned long src_end) +{ +#if defined(CONFIG_X86_32) + memmove((void *)dst, /* use low mapping */ + (void *)src_start, /* use low mapping */ + src_end - src_start); +#elif defined(CONFIG_X86_64) + memmove(__va(dst), + __va(src_start), + src_end - src_start); +#endif +} + void __init __start_xen(multiboot_info_t *mbi) { char __cmdline[] = "", *cmdline = __cmdline; @@ -327,15 +346,8 @@ void __init __start_xen(multiboot_info_t initial_images_start = xenheap_phys_end; initial_images_end = initial_images_start + modules_length; -#if defined(CONFIG_X86_32) - memmove((void *)initial_images_start, /* use low mapping */ - (void *)mod[0].mod_start, /* use low mapping */ - mod[mbi->mods_count-1].mod_end - mod[0].mod_start); -#elif defined(CONFIG_X86_64) - memmove(__va(initial_images_start), - __va(mod[0].mod_start), - mod[mbi->mods_count-1].mod_end - mod[0].mod_start); -#endif + move_memory(initial_images_start, + mod[0].mod_start, mod[mbi->mods_count-1].mod_end); /* Initialise boot-time allocator with all RAM situated after modules. */ xenheap_phys_start = init_boot_allocator(__pa(&_end)); @@ -383,6 +395,51 @@ void __init __start_xen(multiboot_info_t #endif } + if (opt_kdump_megabytes) { + unsigned long kdump_start, kdump_size, k; + + /* mark images pages as free for now */ + + init_boot_pages(initial_images_start, initial_images_end); + + kdump_start = opt_kdump_megabytes_base << 20; + kdump_size = opt_kdump_megabytes << 20; + + printk("Kdump: %luMB (%lukB) at 0x%lx\n", + kdump_size >> 20, + kdump_size >> 10, + kdump_start); + + if ((kdump_start & ~PAGE_MASK) || (kdump_size & ~PAGE_MASK)) + panic("Kdump parameters not page aligned\n"); + + kdump_start >>= PAGE_SHIFT; + kdump_size >>= PAGE_SHIFT; + + /* allocate pages for Kdump memory area */ + + k = alloc_boot_pages_at(kdump_size, kdump_start); + + if (k != kdump_start) + panic("Unable to reserve Kdump memory\n"); + + /* allocate pages for relocated initial images */ + + k = ((initial_images_end - initial_images_start) & ~PAGE_MASK) ? 1 : 0; + k += (initial_images_end - initial_images_start) >> PAGE_SHIFT; + + k = alloc_boot_pages(k, 1); + + if (!k) + panic("Unable to allocate initial images memory\n"); + + move_memory(k << PAGE_SHIFT, initial_images_start, initial_images_end); + + initial_images_end -= initial_images_start; + initial_images_start = k << PAGE_SHIFT; + initial_images_end += initial_images_start; + } + memguard_init(); printk("System RAM: %luMB (%lukB)\n", --- x/xen/arch/x86/x86_32/Makefile +++ x/xen/arch/x86/x86_32/Makefile @@ -3,5 +3,6 @@ obj-y += entry.o obj-y += mm.o obj-y += seg_fixup.o obj-y += traps.o +obj-y += machine_kexec.o obj-$(supervisor_mode_kernel) += supervisor_mode_kernel.o --- /dev/null +++ x/xen/arch/x86/x86_32/machine_kexec.c @@ -0,0 +1,27 @@ +/* + * arch/x86/x86_32/machine_kexec.c + * Handle transition of Linux booting another kernel + * + * Created By: Horms + * + * Should be losely based on arch/i386/kernel/machine_kexec.c + */ + +#include /* for printk() used in stub */ +#include + +void machine_kexec(xen_kexec_image_t *image) +{ + printk("STUB: arch/x86/x86_32/machine_kexec.c: machine_kexec: " + "not implemented\n"); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ --- x/xen/arch/x86/x86_64/Makefile +++ x/xen/arch/x86/x86_64/Makefile @@ -1,3 +1,4 @@ obj-y += entry.o obj-y += mm.o obj-y += traps.o +obj-y += machine_kexec.o --- /dev/null +++ x/xen/arch/x86/x86_64/machine_kexec.c @@ -0,0 +1,28 @@ +/* + * arch/x86/x86_64/machine_kexec.c + * Handle transition of Linux booting another kernel + * + * Created By: Horms + * + * Should be losely based on arch/x86_64/kernel/machine_kexec.c + */ + +#include /* for printk() used in stubs */ +#include +#include + +void machine_kexec(xen_kexec_image_t *image) +{ + printk("STUB: arch/x86/x86_64/machine_kexec.c: machine_kexec: " + "not implemented\n"); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ --- /dev/null +++ x/xen/include/asm-x86/elf.h @@ -0,0 +1,27 @@ +/****************************************************************************** + * include/asm-x86/elf.h + * + * Created By: Horms + * + */ + +#ifndef __X86_ELF_H__ +#define __X86_ELF_H__ + +#ifdef __x86_64__ +#include +#else +#include +#endif + +#endif /* __X86_ELF_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ --- x/xen/include/asm-x86/fixmap.h +++ x/xen/include/asm-x86/fixmap.h @@ -32,6 +32,7 @@ enum fixed_addresses { FIX_ACPI_END = FIX_ACPI_BEGIN + FIX_ACPI_PAGES - 1, FIX_HPET_BASE, FIX_CYCLONE_TIMER, + FIX_KEXEC_PAGE, __end_of_fixed_addresses }; --- x/xen/include/asm-x86/hypercall.h +++ x/xen/include/asm-x86/hypercall.h @@ -6,6 +6,7 @@ #define __ASM_X86_HYPERCALL_H__ #include +#include extern long do_event_channel_op_compat( @@ -87,6 +88,10 @@ extern long arch_do_vcpu_op( int cmd, struct vcpu *v, XEN_GUEST_HANDLE(void) arg); +extern int +do_kexec( + unsigned long op, unsigned arg1, XEN_GUEST_HANDLE(void) uarg); + #ifdef __x86_64__ extern long --- x/xen/include/asm-x86/kexec.h +++ x/xen/include/asm-x86/kexec.h @@ -8,16 +8,16 @@ #ifndef __X86_KEXEC_H__ #define __X86_KEXEC_H__ -#include /* for printk() used in stub */ +#include #include +#include #include -static void crash_setup_regs(struct cpu_user_regs *newregs, - struct cpu_user_regs *oldregs) -{ - printk("STUB: include/asm-x86/kexec.h: crash_setup_regs: " - "not implemented\n"); -} +#ifdef __x86_64__ +#include +#else +#include +#endif #endif /* __X86_KEXEC_H__ */ --- /dev/null +++ x/xen/include/asm-x86/x86_32/elf.h @@ -0,0 +1,30 @@ +/****************************************************************************** + * include/asm-x86/x86_32/elf.h + * + * Created By: Horms + * + * Should pull be based on include/asm-i386/elf.h:ELF_CORE_COPY_REGS + * from Linux 2.6.16 + */ + +#ifndef __X86_ELF_X86_32_H__ +#define __X86_ELF_X86_32_H__ + +#include /* for printk() used in stub */ + +#define ELF_CORE_COPY_REGS(pr_reg, regs) \ + printk("STUB: include/asm-x86/x86_32/kexec.h: ELF_CORE_COPY_REGS: " \ + "not implemented\n") + + +#endif /* __X86_ELF_X86_32_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ --- /dev/null +++ x/xen/include/asm-x86/x86_32/kexec.h @@ -0,0 +1,51 @@ +/****************************************************************************** + * include/asm-x86/x86_32/kexec.h + * + * Created By: Horms + * + * Should be based heavily on include/asm-i386/kexec.h from Linux 2.6.16 + * + */ + +#ifndef __X86_32_KEXEC_H__ +#define __X86_32_KEXEC_H__ + +#include /* for printk() used in stub */ +#include +#include + +static void crash_fixup_ss_esp(struct cpu_user_regs *newregs, + struct cpu_user_regs *oldregs) +{ + printk("STUB: include/asm-x86/x86_32/kexec.h: crash_fixup_ss_esp: " + "not implemented\n"); + return; + crash_fixup_ss_esp(newregs, oldregs); +} + +static void crash_setup_regs(struct cpu_user_regs *newregs, + struct cpu_user_regs *oldregs) +{ + printk("STUB: include/asm-x86/x86_32/kexec.h: crash_setup_regs: " + "not implemented\n"); +} + +static inline int user_mode(struct cpu_user_regs *regs) +{ + printk("STUB: include/asm-x86/x86_32/kexec.h: user_mode: " + "not implemented\n"); + return -1; +} + + +#endif /* __X86_32_KEXEC_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ --- /dev/null +++ x/xen/include/asm-x86/x86_64/elf.h @@ -0,0 +1,30 @@ +/****************************************************************************** + * include/asm-x86/x86_64/elf.h + * + * Created By: Horms + * + * Should pull be based on include/asm-x86_64/elf.h:ELF_CORE_COPY_REGS + * from Linux 2.6.16 + */ + +#ifndef __X86_ELF_X86_64_H__ +#define __X86_ELF_X86_64_H__ + +#include /* for printk() used in stub */ + +#define ELF_CORE_COPY_REGS(pr_reg, regs) \ + printk("STUB: include/asm-x86/x86_64/kexec.h: ELF_CORE_COPY_REGS: " \ + "not implemented\n") + + +#endif /* __X86_ELF_X86_64_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ --- /dev/null +++ x/xen/include/asm-x86/x86_64/kexec.h @@ -0,0 +1,50 @@ +/****************************************************************************** + * include/asm-x86/x86_64/kexec.h + * + * Created By: Horms + * + * Should be based heavily on include/asm-x86_64/kexec.h from Linux 2.6.16 + * + */ + +#ifndef __X86_64_KEXEC_H__ +#define __X86_64_KEXEC_H__ + +#include /* for printk() used in stub */ +#include +#include + +static void crash_fixup_ss_esp(struct cpu_user_regs *newregs, + struct cpu_user_regs *oldregs) +{ + printk("STUB: include/asm-x86/x86_64/kexec.h: crash_fixup_ss_esp: " + "not implemented\n"); +} + +static void crash_setup_regs(struct cpu_user_regs *newregs, + struct cpu_user_regs *oldregs) +{ + printk("STUB: include/asm-x86/x86_64/kexec.h: crash_setup_regs: " + "not implemented\n"); + return; + crash_fixup_ss_esp(newregs, oldregs); +} + +static inline int user_mode(struct cpu_user_regs *regs) +{ + printk("STUB: include/asm-x86/x86_64/kexec.h: user_mode: " + "not implemented\n"); + return -1; +} + +#endif /* __X86_64_KEXEC_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ --- x/xen/include/public/kexec.h +++ x/xen/include/public/kexec.h @@ -43,6 +43,8 @@ */ #define KEXEC_CMD_kexec_load 1 typedef struct xen_kexec_image { + unsigned long page_table_a[7]; + unsigned long page_table_b; unsigned long indirection_page; unsigned long reboot_code_buffer; unsigned long start_address; --opJtzjQTFsWo+cga Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="51.2.1.1-kexec-x86_32-upstream.patch" kexec: x86_32 This is the x86_32 component of kexec for xen. The x86 component is a prerequsite for this patch. Signed-Off-By: Horms Signed-Off-By: Magnus Damm buildconfigs/linux-defconfig_xen_x86_32 | 4 linux-2.6-xen-sparse/arch/i386/Kconfig | 2 linux-2.6-xen-sparse/arch/i386/kernel/Makefile | 2 linux-2.6-xen-sparse/arch/i386/kernel/setup-xen.c | 28 linux-2.6-xen-sparse/include/asm-i386/kexec-xen.h | 42 linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypercall.h | 8 patches/linux-2.6.16.13/1-linux-2.6.16-kexec_page_table_a_i386.patch | 457 ++++++++++ xen/arch/x86/x86_32/entry.S | 2 xen/arch/x86/x86_32/machine_kexec.c | 28 xen/include/asm-x86/x86_32/elf.h | 34 xen/include/asm-x86/x86_32/kexec.h | 68 + 11 files changed, 635 insertions(+), 40 deletions(-) --- x/buildconfigs/linux-defconfig_xen_x86_32 +++ x/buildconfigs/linux-defconfig_xen_x86_32 @@ -184,10 +184,11 @@ CONFIG_MTRR=y CONFIG_REGPARM=y CONFIG_SECCOMP=y CONFIG_HZ_100=y +CONFIG_KEXEC=y # CONFIG_HZ_250 is not set # CONFIG_HZ_1000 is not set CONFIG_HZ=100 -# CONFIG_CRASH_DUMP is not set +CONFIG_CRASH_DUMP=y CONFIG_PHYSICAL_START=0x100000 CONFIG_HOTPLUG_CPU=y @@ -2774,6 +2775,7 @@ CONFIG_NTFS_FS=m # CONFIG_PROC_FS=y CONFIG_PROC_KCORE=y +# CONFIG_PROC_VMCORE is not set CONFIG_SYSFS=y CONFIG_TMPFS=y # CONFIG_HUGETLB_PAGE is not set --- x/linux-2.6-xen-sparse/arch/i386/Kconfig +++ x/linux-2.6-xen-sparse/arch/i386/Kconfig @@ -726,7 +726,7 @@ source kernel/Kconfig.hz config KEXEC bool "kexec system call (EXPERIMENTAL)" - depends on EXPERIMENTAL && !X86_XEN + 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 --- x/linux-2.6-xen-sparse/arch/i386/kernel/Makefile +++ x/linux-2.6-xen-sparse/arch/i386/kernel/Makefile @@ -89,7 +89,7 @@ include $(srctree)/scripts/Makefile.xen obj-y += fixup.o microcode-$(subst m,y,$(CONFIG_MICROCODE)) := microcode-xen.o -n-obj-xen := i8259.o timers/ reboot.o smpboot.o trampoline.o +n-obj-xen := i8259.o timers/ reboot.o smpboot.o trampoline.o crash.o obj-y := $(call filterxen, $(obj-y), $(n-obj-xen)) obj-y := $(call cherrypickxen, $(obj-y)) --- x/linux-2.6-xen-sparse/arch/i386/kernel/setup-xen.c +++ x/linux-2.6-xen-sparse/arch/i386/kernel/setup-xen.c @@ -68,6 +68,10 @@ #include "setup_arch_pre.h" #include +#ifdef CONFIG_XEN +#include +#endif + /* Forward Declaration. */ void __init find_max_pfn(void); @@ -941,6 +945,7 @@ static void __init parse_cmdline_early ( * after a kernel panic. */ else if (!memcmp(from, "crashkernel=", 12)) { +#ifndef CONFIG_XEN unsigned long size, base; size = memparse(from+12, &from); if (*from == '@') { @@ -951,6 +956,10 @@ static void __init parse_cmdline_early ( crashk_res.start = base; crashk_res.end = base + size - 1; } +#else + printk("Ignoring crashkernel command line, " + "parameter will be supplied by xen\n"); +#endif } #endif #ifdef CONFIG_PROC_VMCORE @@ -1320,9 +1329,22 @@ void __init setup_bootmem_allocator(void } #endif #ifdef CONFIG_KEXEC +#ifndef CONFIG_XEN if (crashk_res.start != crashk_res.end) reserve_bootmem(crashk_res.start, crashk_res.end - crashk_res.start + 1); +#else + { + xen_kexec_reserve_t reservation; + BUG_ON(HYPERVISOR_kexec(KEXEC_CMD_kexec_reserve, 0, + &reservation)); + if (reservation.size) { + crashk_res.start = reservation.start; + crashk_res.end = reservation.start + + reservation.size - 1; + } + } +#endif #endif if (!xen_feature(XENFEAT_auto_translated_physmap)) @@ -1378,6 +1400,9 @@ legacy_init_iomem_resources(struct e820e res->end = res->start + e820[i].size - 1; res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; request_resource(&iomem_resource, res); +#ifdef CONFIG_KEXEC + request_resource(res, &crashk_res); +#endif #ifndef CONFIG_XEN if (e820[i].type == E820_RAM) { /* @@ -1387,9 +1412,6 @@ legacy_init_iomem_resources(struct e820e */ request_resource(res, code_resource); request_resource(res, data_resource); -#ifdef CONFIG_KEXEC - request_resource(res, &crashk_res); -#endif } #endif } --- /dev/null +++ x/linux-2.6-xen-sparse/include/asm-i386/kexec-xen.h @@ -0,0 +1,42 @@ +/* + * include/asm-i386/kexec-xen.h + * + * Created By: Horms + */ + +#ifndef _I386_KEXEC_XEN_H +#define _I386_KEXEC_XEN_H + +#include +#include +#include + +static inline void crash_translate_regs(struct pt_regs *linux_regs, + struct cpu_user_regs *xen_regs) +{ + xen_regs->ebx = linux_regs->ebx; + xen_regs->ecx = linux_regs->ecx; + xen_regs->edx = linux_regs->edx; + xen_regs->esi = linux_regs->esi; + xen_regs->edi = linux_regs->edi; + xen_regs->ebp = linux_regs->ebp; + xen_regs->eax = linux_regs->eax; + xen_regs->esp = linux_regs->esp; + xen_regs->ss = linux_regs->xss; + xen_regs->cs = linux_regs->xcs; + xen_regs->ds = linux_regs->xds; + xen_regs->es = linux_regs->xes; + xen_regs->eflags = linux_regs->eflags; +} + +#endif /* _I386_KEXEC_XEN_H */ + +/* + * Local variables: + * c-file-style: "linux" + * indent-tabs-mode: t + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ --- x/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypercall.h +++ x/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypercall.h @@ -359,5 +359,13 @@ HYPERVISOR_xenoprof_op( return _hypercall2(int, xenoprof_op, op, arg); } +static inline int +HYPERVISOR_kexec( + unsigned long op, unsigned int arg1, void * extra_args) +{ + return _hypercall3(int, kexec_op, op, arg1, extra_args); +} + + #endif /* __HYPERCALL_H__ */ --- x/xen/arch/x86/x86_32/entry.S +++ x/xen/arch/x86/x86_32/entry.S @@ -648,6 +648,7 @@ ENTRY(hypercall_table) .long do_xenoprof_op .long do_event_channel_op .long do_physdev_op + .long do_kexec .rept NR_hypercalls-((.-hypercall_table)/4) .long do_ni_hypercall .endr @@ -687,6 +688,7 @@ ENTRY(hypercall_args_table) .byte 2 /* do_xenoprof_op */ .byte 2 /* do_event_channel_op */ .byte 2 /* do_physdev_op */ + .byte 3 /* do_kexec */ .rept NR_hypercalls-(.-hypercall_args_table) .byte 0 /* do_ni_hypercall */ .endr --- x/xen/arch/x86/x86_32/machine_kexec.c +++ x/xen/arch/x86/x86_32/machine_kexec.c @@ -1,19 +1,31 @@ -/* +/****************************************************************************** * arch/x86/x86_32/machine_kexec.c - * Handle transition of Linux booting another kernel - * - * Created By: Horms + * + * Created By: Horms * - * Should be losely based on arch/i386/kernel/machine_kexec.c + * Based heavily on arch/i386/machine_kexec.c from Linux 2.6.16 */ -#include /* for printk() used in stub */ +#include #include +#include +#include + +typedef asmlinkage void (*relocate_new_kernel_t)( + unsigned long indirection_page, + unsigned long reboot_code_buffer, + unsigned long start_address, + unsigned long page_table_a, + unsigned long has_pae); void machine_kexec(xen_kexec_image_t *image) { - printk("STUB: arch/x86/x86_32/machine_kexec.c: machine_kexec: " - "not implemented\n"); + relocate_new_kernel_t rnk; + + rnk = (relocate_new_kernel_t) fix_to_virt(FIX_KEXEC_PAGE); + (*rnk)(image->indirection_page, image->reboot_code_buffer, + image->start_address, image->page_table_a[0], + (unsigned long)cpu_has_pae); } /* --- x/xen/include/asm-x86/x86_32/elf.h +++ x/xen/include/asm-x86/x86_32/elf.h @@ -3,19 +3,39 @@ * * Created By: Horms * - * Should pull be based on include/asm-i386/elf.h:ELF_CORE_COPY_REGS - * from Linux 2.6.16 + * Based heavily on include/asm-i386/elf.h and + * include/asm-i386/system.h from Linux 2.6.16 */ #ifndef __X86_ELF_X86_32_H__ #define __X86_ELF_X86_32_H__ -#include /* for printk() used in stub */ +/* XXX: Xen doesn't have orig_eax. For kdump, on a dom0 crash, the values + * for the crashing CPU could could be passed down from dom0, but is that + * neccessary? + * Also, I'm not sure why fs and gs are derived from the CPU + * rather than regs */ -#define ELF_CORE_COPY_REGS(pr_reg, regs) \ - printk("STUB: include/asm-x86/x86_32/kexec.h: ELF_CORE_COPY_REGS: " \ - "not implemented\n") - +#define ELF_CORE_COPY_REGS(pr_reg, regs) do { \ + unsigned i; \ + pr_reg[0] = regs->ebx; \ + pr_reg[1] = regs->ecx; \ + pr_reg[2] = regs->edx; \ + pr_reg[3] = regs->esi; \ + pr_reg[4] = regs->edi; \ + pr_reg[5] = regs->ebp; \ + pr_reg[6] = regs->eax; \ + pr_reg[7] = regs->ds; \ + pr_reg[8] = regs->es; \ + asm volatile("mov %%fs,%0":"=rm" (i)); pr_reg[9] = i; \ + asm volatile("mov %%gs,%0":"=rm" (i)); pr_reg[10] = i; \ + pr_reg[11] = 0; /* regs->orig_eax; */ \ + pr_reg[12] = regs->eip; \ + pr_reg[13] = regs->cs; \ + pr_reg[14] = regs->eflags; \ + pr_reg[15] = regs->esp; \ + pr_reg[16] = regs->ss; \ +} while(0); #endif /* __X86_ELF_X86_32_H__ */ --- x/xen/include/asm-x86/x86_32/kexec.h +++ x/xen/include/asm-x86/x86_32/kexec.h @@ -3,42 +3,72 @@ * * Created By: Horms * - * Should be based heavily on include/asm-i386/kexec.h from Linux 2.6.16 - * + * Based heavily on include/asm-i386/kexec.h from Linux 2.6.16 */ -#ifndef __X86_32_KEXEC_H__ -#define __X86_32_KEXEC_H__ - -#include /* for printk() used in stub */ -#include -#include +#ifndef __X86_KEXEC_X86_32_H__ +#define __X86_KEXEC_X86_32_H__ +/* CPU does not save ss and esp on stack if execution is already + * running in kernel mode at the time of NMI occurrence. This code + * fixes it. + */ static void crash_fixup_ss_esp(struct cpu_user_regs *newregs, - struct cpu_user_regs *oldregs) + struct cpu_user_regs *oldregs) { - printk("STUB: include/asm-x86/x86_32/kexec.h: crash_fixup_ss_esp: " - "not implemented\n"); - return; - crash_fixup_ss_esp(newregs, oldregs); + memcpy(newregs, oldregs, sizeof(*newregs)); + newregs->esp = (unsigned long)&(oldregs->esp); + __asm__ __volatile__( + "xorl %%eax, %%eax\n\t" + "movw %%ss, %%ax\n\t" + :"=a"(newregs->ss)); } +/* + * This function is responsible for capturing register states if coming + * via panic otherwise just fix up the ss and esp if coming via kernel + * mode exception. + */ static void crash_setup_regs(struct cpu_user_regs *newregs, struct cpu_user_regs *oldregs) { - printk("STUB: include/asm-x86/x86_32/kexec.h: crash_setup_regs: " - "not implemented\n"); + if (oldregs) + crash_fixup_ss_esp(newregs, oldregs); + else { + __asm__ __volatile__("movl %%ebx,%0" : "=m"(newregs->ebx)); + __asm__ __volatile__("movl %%ecx,%0" : "=m"(newregs->ecx)); + __asm__ __volatile__("movl %%edx,%0" : "=m"(newregs->edx)); + __asm__ __volatile__("movl %%esi,%0" : "=m"(newregs->esi)); + __asm__ __volatile__("movl %%edi,%0" : "=m"(newregs->edi)); + __asm__ __volatile__("movl %%ebp,%0" : "=m"(newregs->ebp)); + __asm__ __volatile__("movl %%eax,%0" : "=m"(newregs->eax)); + __asm__ __volatile__("movl %%esp,%0" : "=m"(newregs->esp)); + __asm__ __volatile__("movw %%ss, %%ax;" :"=a"(newregs->ss)); + __asm__ __volatile__("movw %%cs, %%ax;" :"=a"(newregs->cs)); + __asm__ __volatile__("movw %%ds, %%ax;" :"=a"(newregs->ds)); + __asm__ __volatile__("movw %%es, %%ax;" :"=a"(newregs->es)); + __asm__ __volatile__("pushfl; popl %0" :"=m"(newregs->eflags)); + + newregs->eip = (unsigned long)current_text_addr(); + } } +/* + * From Linux 2.6.16's include/asm-i386/mach-xen/asm/ptrace.h + * + * user_mode_vm(regs) determines whether a register set came from user mode. + * This is true if V8086 mode was enabled OR if the register set was from + * protected mode with RPL-3 CS value. This tricky test checks that with + * one comparison. Many places in the kernel can bypass this full check + * if they have already ruled out V8086 mode, so user_mode(regs) can be used. + */ static inline int user_mode(struct cpu_user_regs *regs) { - printk("STUB: include/asm-x86/x86_32/kexec.h: user_mode: " - "not implemented\n"); - return -1; + return (regs->cs & 2) != 0; } -#endif /* __X86_32_KEXEC_H__ */ +#endif /* __X86_KEXEC_X86_32_H__ */ /* * Local variables: --- /dev/null +++ x/patches/linux-2.6.16.13/1-linux-2.6.16-kexec_page_table_a_i386.patch @@ -0,0 +1,457 @@ +kexec: Avoid overwriting the current pgd (V2, i386) + +This patch upgrades the i386-specific kexec code to avoid overwriting the +current pgd. Overwriting the current pgd is bad when CONFIG_CRASH_DUMP is used +to start a secondary kernel that dumps the memory of the previous kernel. + +The code introduces a new set of page tables called "page_table_a". These +tables are used to provide an executable identity mapping without overwriting +the current pgd. This updated version of the patch fixes a PAE bug and moves +the segment handling code into the reloacte_kernel.S. + +Signed-off-by: Magnus Damm +--- + + The patch has been tested with regular kexec and CONFIG_CRASH_DUMP. + Both PAE and non-PAE configurations work well. + Applies on top of 2.6.16 and 2.6.17-rc4. + + arch/i386/kernel/machine_kexec.c | 230 ++++++++++++++---------------------- + arch/i386/kernel/relocate_kernel.S | 92 ++++++++++++++ + include/asm-i386/kexec.h | 12 + + 3 files changed, 192 insertions(+), 142 deletions(-) + +--- x/arch/i386/kernel/machine_kexec.c ++++ x/arch/i386/kernel/machine_kexec.c +@@ -2,6 +2,10 @@ + * machine_kexec.c - handle transition of Linux booting another kernel + * Copyright (C) 2002-2005 Eric Biederman + * ++ * 2006-05-19 Magnus Damm : ++ * - rewrote identity map code to avoid overwriting current pgd ++ * - moved segment handling code into relocate_kernel.S ++ * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ +@@ -19,123 +23,73 @@ + #include + #include + +-#define PAGE_ALIGNED __attribute__ ((__aligned__(PAGE_SIZE))) +- +-#define L0_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) +-#define L1_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) +-#define L2_ATTR (_PAGE_PRESENT) +- +-#define LEVEL0_SIZE (1UL << 12UL) ++typedef asmlinkage NORET_TYPE void (*relocate_new_kernel_t)( ++ unsigned long indirection_page, ++ unsigned long reboot_code_buffer, ++ unsigned long start_address, ++ unsigned long page_table_a, ++ unsigned long has_pae) ATTRIB_NORET; + +-#ifndef CONFIG_X86_PAE +-#define LEVEL1_SIZE (1UL << 22UL) +-static u32 pgtable_level1[1024] PAGE_ALIGNED; ++const extern unsigned char relocate_new_kernel[]; ++extern void relocate_new_kernel_end(void); ++const extern unsigned int relocate_new_kernel_size; + +-static void identity_map_page(unsigned long address) ++static int allocate_page_table_a(struct kimage *image) + { +- unsigned long level1_index, level2_index; +- u32 *pgtable_level2; +- +- /* Find the current page table */ +- pgtable_level2 = __va(read_cr3()); ++ struct kimage_arch *arch = &image->arch_data; ++ struct page *page; ++ int k = sizeof(arch->page_table_a) / sizeof(arch->page_table_a[0]); ++ ++ for (; k > 0; k--) { ++ page = kimage_alloc_control_pages(image, 0); ++ if (!page) ++ return -ENOMEM; ++ ++ clear_page(page_address(page)); ++ arch->page_table_a[k - 1] = page; ++ } + +- /* Find the indexes of the physical address to identity map */ +- level1_index = (address % LEVEL1_SIZE)/LEVEL0_SIZE; +- level2_index = address / LEVEL1_SIZE; +- +- /* Identity map the page table entry */ +- pgtable_level1[level1_index] = address | L0_ATTR; +- pgtable_level2[level2_index] = __pa(pgtable_level1) | L1_ATTR; +- +- /* Flush the tlb so the new mapping takes effect. +- * Global tlb entries are not flushed but that is not an issue. +- */ +- load_cr3(pgtable_level2); ++ return 0; + } + +-#else +-#define LEVEL1_SIZE (1UL << 21UL) +-#define LEVEL2_SIZE (1UL << 30UL) +-static u64 pgtable_level1[512] PAGE_ALIGNED; +-static u64 pgtable_level2[512] PAGE_ALIGNED; +- +-static void identity_map_page(unsigned long address) +-{ +- unsigned long level1_index, level2_index, level3_index; +- u64 *pgtable_level3; ++/* workaround for include/asm-i386/pgtable-3level.h */ + +- /* Find the current page table */ +- pgtable_level3 = __va(read_cr3()); +- +- /* Find the indexes of the physical address to identity map */ +- level1_index = (address % LEVEL1_SIZE)/LEVEL0_SIZE; +- level2_index = (address % LEVEL2_SIZE)/LEVEL1_SIZE; +- level3_index = address / LEVEL2_SIZE; +- +- /* Identity map the page table entry */ +- pgtable_level1[level1_index] = address | L0_ATTR; +- pgtable_level2[level2_index] = __pa(pgtable_level1) | L1_ATTR; +- set_64bit(&pgtable_level3[level3_index], +- __pa(pgtable_level2) | L2_ATTR); +- +- /* Flush the tlb so the new mapping takes effect. +- * Global tlb entries are not flushed but that is not an issue. +- */ +- load_cr3(pgtable_level3); +-} ++#ifdef CONFIG_X86_PAE ++#undef pgd_present ++#define pgd_present(pgd) (pgd_val(pgd) & _PAGE_PRESENT) ++#define _PGD_ATTR _PAGE_PRESENT ++#else ++#define _PGD_ATTR _KERNPG_TABLE + #endif + +-static void set_idt(void *newidt, __u16 limit) +-{ +- struct Xgt_desc_struct curidt; +- +- /* ia32 supports unaliged loads & stores */ +- curidt.size = limit; +- curidt.address = (unsigned long)newidt; +- +- load_idt(&curidt); +-}; ++#define pa_page(page) __pa(page_address(page)) + +- +-static void set_gdt(void *newgdt, __u16 limit) ++static int create_mapping(struct page *root, struct page **pages, ++ unsigned long va, unsigned long pa) + { +- struct Xgt_desc_struct curgdt; +- +- /* ia32 supports unaligned loads & stores */ +- curgdt.size = limit; +- curgdt.address = (unsigned long)newgdt; ++ pgd_t *pgd; ++ pud_t *pud; ++ pmd_t *pmd; ++ pte_t *pte; ++ int k = 0; + +- load_gdt(&curgdt); +-}; ++ pgd = (pgd_t *)page_address(root) + pgd_index(va); ++ if (!pgd_present(*pgd)) ++ set_pgd(pgd, __pgd(pa_page(pages[k++]) | _PGD_ATTR)); + +-static void load_segments(void) +-{ +-#define __STR(X) #X +-#define STR(X) __STR(X) ++ pud = pud_offset(pgd, va); ++ if (!pud_present(*pud)) ++ set_pud(pud, __pud(pa_page(pages[k++]) | _KERNPG_TABLE)); + +- __asm__ __volatile__ ( +- "\tljmp $"STR(__KERNEL_CS)",$1f\n" +- "\t1:\n" +- "\tmovl $"STR(__KERNEL_DS)",%%eax\n" +- "\tmovl %%eax,%%ds\n" +- "\tmovl %%eax,%%es\n" +- "\tmovl %%eax,%%fs\n" +- "\tmovl %%eax,%%gs\n" +- "\tmovl %%eax,%%ss\n" +- ::: "eax", "memory"); +-#undef STR +-#undef __STR +-} ++ pmd = pmd_offset(pud, va); ++ if (!pmd_present(*pmd)) ++ set_pmd(pmd, __pmd(pa_page(pages[k++]) | _KERNPG_TABLE)); + +-typedef asmlinkage NORET_TYPE void (*relocate_new_kernel_t)( +- unsigned long indirection_page, +- unsigned long reboot_code_buffer, +- unsigned long start_address, +- unsigned int has_pae) ATTRIB_NORET; ++ pte = (pte_t *)page_address(pmd_page(*pmd)) + pte_index(va); ++ set_pte(pte, __pte(pa | _PAGE_KERNEL_EXEC)); + +-const extern unsigned char relocate_new_kernel[]; +-extern void relocate_new_kernel_end(void); +-const extern unsigned int relocate_new_kernel_size; ++ return k; ++} + + /* + * A architecture hook called to validate the +@@ -147,11 +101,38 @@ const extern unsigned int relocate_new_k + * Do what every setup is needed on image and the + * reboot code buffer to allow us to avoid allocations + * later. +- * +- * Currently nothing. + */ + int machine_kexec_prepare(struct kimage *image) + { ++ void *control_page; ++ unsigned long pa; ++ int k; ++ ++ memset(&image->arch_data, 0, sizeof(image->arch_data)); ++ ++ k = allocate_page_table_a(image); ++ if (k) ++ return k; ++ ++ /* fill in control_page with assembly code */ ++ ++ control_page = page_address(image->control_code_page); ++ memcpy(control_page, relocate_new_kernel, relocate_new_kernel_size); ++ ++ /* map the control_page at the virtual address of relocate_kernel.S */ ++ ++ pa = __pa(control_page); ++ ++ k = create_mapping(image->arch_data.page_table_a[0], ++ &image->arch_data.page_table_a[1], ++ (unsigned long)relocate_new_kernel, pa); ++ ++ /* identity map the control_page */ ++ ++ create_mapping(image->arch_data.page_table_a[0], ++ &image->arch_data.page_table_a[k + 1], ++ pa, pa); ++ + return 0; + } + +@@ -170,45 +151,16 @@ void machine_kexec_cleanup(struct kimage + NORET_TYPE void machine_kexec(struct kimage *image) + { + unsigned long page_list; +- unsigned long reboot_code_buffer; +- ++ unsigned long control_code; ++ unsigned long page_table_a; + relocate_new_kernel_t rnk; + +- /* Interrupts aren't acceptable while we reboot */ +- local_irq_disable(); +- +- /* Compute some offsets */ +- reboot_code_buffer = page_to_pfn(image->control_code_page) +- << PAGE_SHIFT; + page_list = image->head; +- +- /* Set up an identity mapping for the reboot_code_buffer */ +- identity_map_page(reboot_code_buffer); +- +- /* copy it out */ +- memcpy((void *)reboot_code_buffer, relocate_new_kernel, +- relocate_new_kernel_size); +- +- /* The segment registers are funny things, they are +- * automatically loaded from a table, in memory wherever you +- * set them to a specific selector, but this table is never +- * accessed again you set the segment to a different selector. +- * +- * The more common model is are caches where the behide +- * the scenes work is done, but is also dropped at arbitrary +- * times. +- * +- * I take advantage of this here by force loading the +- * segments, before I zap the gdt with an invalid value. +- */ +- load_segments(); +- /* The gdt & idt are now invalid. +- * If you want to load them you must set up your own idt & gdt. +- */ +- set_gdt(phys_to_virt(0),0); +- set_idt(phys_to_virt(0),0); ++ control_code = __pa(page_address(image->control_code_page)); ++ page_table_a = __pa(page_address(image->arch_data.page_table_a[0])); + + /* now call it */ +- rnk = (relocate_new_kernel_t) reboot_code_buffer; +- (*rnk)(page_list, reboot_code_buffer, image->start, cpu_has_pae); ++ rnk = (relocate_new_kernel_t) relocate_new_kernel; ++ (*rnk)(page_list, control_code, image->start, ++ page_table_a, (unsigned long)cpu_has_pae); + } +--- x/arch/i386/kernel/relocate_kernel.S ++++ x/arch/i386/kernel/relocate_kernel.S +@@ -2,12 +2,20 @@ + * relocate_kernel.S - put the kernel image in place to boot + * Copyright (C) 2002-2004 Eric Biederman + * ++ * 2006-05-19 Magnus Damm : ++ * - moved segment handling code from machine_kexec.c ++ * - gdt tables stolen from arch/i386/boot/setup.S ++ * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + + #include ++#include + ++.text ++.align (1 << PAGE_SHIFT) ++ + /* + * Must be relocatable PIC code callable as a C function, that once + * it starts can not use the previous processes stack. +@@ -18,18 +26,68 @@ relocate_new_kernel: + movl 4(%esp), %ebx /* page_list */ + movl 8(%esp), %ebp /* reboot_code_buffer */ + movl 12(%esp), %edx /* start address */ +- movl 16(%esp), %ecx /* cpu_has_pae */ ++ movl 16(%esp), %edi /* page_table_a */ ++ movl 20(%esp), %ecx /* cpu_has_pae */ + + /* zero out flags, and disable interrupts */ + pushl $0 + popfl + ++ /* switch to page_table_a */ ++ movl %edi, %eax ++ movl %eax, %cr3 ++ ++ /* setup idt */ ++ ++ movl %ebp, %eax ++ addl $(idt_48 - relocate_new_kernel), %eax ++ lidtl (%eax) ++ ++ /* setup gdt */ ++ ++ movl %ebp, %eax ++ addl $(gdt - relocate_new_kernel), %eax ++ movl %ebp, %esi ++ addl $((gdt_48 - relocate_new_kernel) + 2), %esi ++ movl %eax, (%esi) ++ ++ movl %ebp, %eax ++ addl $(gdt_48 - relocate_new_kernel), %eax ++ lgdtl (%eax) ++ ++ /* setup data segment registers */ ++ ++ mov $(gdt_ds - gdt), %eax ++ mov %eax, %ds ++ mov %eax, %es ++ mov %eax, %fs ++ mov %eax, %gs ++ mov %eax, %ss ++ + /* set a new stack at the bottom of our page... */ + lea 4096(%ebp), %esp + ++ /* load new code segment */ ++ ++ movl %ebp, %esi ++ xorl %eax, %eax ++ pushl %eax ++ pushl %esi ++ pushl %eax ++ ++ movl $(gdt_cs - gdt), %eax ++ pushl %eax ++ ++ movl %ebp, %eax ++ addl $(identity_mapped - relocate_new_kernel),%eax ++ pushl %eax ++ iretl ++ ++identity_mapped: ++ + /* store the parameters back on the stack */ + pushl %edx /* store the start address */ +- ++ + /* Set cr0 to a known state: + * 31 0 == Paging disabled + * 18 0 == Alignment check disabled +@@ -113,6 +171,36 @@ relocate_new_kernel: + xorl %edi, %edi + xorl %ebp, %ebp + ret ++ ++ .align 16 ++gdt: ++ .fill 1,8,0 ++ ++gdt_cs: ++ .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb) ++ .word 0 # base address = 0 ++ .word 0x9A00 # code read/exec ++ .word 0x00CF # granularity = 4096, 386 ++ # (+5th nibble of limit) ++gdt_ds: ++ .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb) ++ .word 0 # base address = 0 ++ .word 0x9200 # data read/write ++ .word 0x00CF # granularity = 4096, 386 ++ # (+5th nibble of limit) ++gdt_end: ++ .align 4 ++ ++ .word 0 # alignment byte ++idt_48: ++ .word 0 # idt limit = 0 ++ .word 0, 0 # idt base = 0L ++ ++ .word 0 # alignment byte ++gdt_48: ++ .word gdt_end - gdt - 1 # gdt limit ++ .word 0, 0 # gdt base (filled in later) ++ + relocate_new_kernel_end: + + .globl relocate_new_kernel_size +--- x/include/asm-i386/kexec.h ++++ x/include/asm-i386/kexec.h +@@ -29,7 +29,17 @@ + + #define MAX_NOTE_BYTES 1024 + +-struct kimage_arch {}; ++struct kimage_arch { ++ /* page_table_a[] holds enough pages to create a new page table ++ * that maps the control page twice.. ++ */ ++ ++#if defined(CONFIG_X86_PAE) ++ struct page *page_table_a[5]; /* (2 * pte) + (2 * pmd) + pgd */ ++#else ++ struct page *page_table_a[3]; /* (2 * pte) + pgd */ ++#endif ++}; + + /* CPU does not save ss and esp on stack if execution is already + * running in kernel mode at the time of NMI occurrence. This code --opJtzjQTFsWo+cga Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="51.2.1.2-kexec-x86_64-upstream.patch" kexec: x86_64 This is the x86_64 component of kexec for xen. The x86 component is a prerequsite for this patch. Signed-Off-By: Magnus Damm Signed-Off-By: Horms buildconfigs/linux-defconfig_xen_x86_64 | 2 linux-2.6-xen-sparse/arch/x86_64/Kconfig | 2 linux-2.6-xen-sparse/arch/x86_64/kernel/Makefile | 2 linux-2.6-xen-sparse/arch/x86_64/kernel/setup-xen.c | 26 linux-2.6-xen-sparse/include/asm-x86_64/kexec-xen.h | 30 linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/hypercall.h | 7 linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/ptrace.h | 2 patches/linux-2.6.16.13/2-linux-2.6.16-kexec_page_table_a_x86_64.patch | 421 ++++++++++ patches/linux-2.6.16.13/3-linux-2.6.16-kexec_page_table_a_x86_64-xen.patch | 116 ++ xen/arch/x86/x86_64/entry.S | 2 xen/arch/x86/x86_64/machine_kexec.c | 16 11 files changed, 621 insertions(+), 5 deletions(-) --- x/buildconfigs/linux-defconfig_xen_x86_64 +++ x/buildconfigs/linux-defconfig_xen_x86_64 @@ -139,6 +139,8 @@ CONFIG_SWIOTLB=y CONFIG_PHYSICAL_START=0x100000 CONFIG_SECCOMP=y CONFIG_HZ_100=y +CONFIG_KEXEC=y +CONFIG_CRASH_DUMP=y # CONFIG_HZ_250 is not set # CONFIG_HZ_1000 is not set CONFIG_HZ=100 --- x/linux-2.6-xen-sparse/arch/x86_64/Kconfig +++ x/linux-2.6-xen-sparse/arch/x86_64/Kconfig @@ -433,7 +433,7 @@ config X86_MCE_AMD config KEXEC bool "kexec system call (EXPERIMENTAL)" - depends on EXPERIMENTAL && !X86_64_XEN + 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 --- x/linux-2.6-xen-sparse/arch/x86_64/kernel/Makefile +++ x/linux-2.6-xen-sparse/arch/x86_64/kernel/Makefile @@ -59,7 +59,7 @@ pci-dma-y += ../../i386/kernel/pci-dma microcode-$(subst m,y,$(CONFIG_MICROCODE)) := ../../i386/kernel/microcode-xen.o quirks-y := ../../i386/kernel/quirks-xen.o -n-obj-xen := i8259.o reboot.o i8237.o smpboot.o trampoline.o +n-obj-xen := i8259.o reboot.o i8237.o smpboot.o trampoline.o crash.o include $(srctree)/scripts/Makefile.xen --- x/linux-2.6-xen-sparse/arch/x86_64/kernel/setup-xen.c +++ x/linux-2.6-xen-sparse/arch/x86_64/kernel/setup-xen.c @@ -79,6 +79,10 @@ #include #include +#ifdef CONFIG_XEN +#include +#endif + extern unsigned long start_pfn; extern struct edid_info edid_info; @@ -446,6 +450,7 @@ static __init void parse_cmdline_early ( * after a kernel panic. */ else if (!memcmp(from, "crashkernel=", 12)) { +#ifndef CONFIG_XEN unsigned long size, base; size = memparse(from+12, &from); if (*from == '@') { @@ -456,6 +461,10 @@ static __init void parse_cmdline_early ( crashk_res.start = base; crashk_res.end = base + size - 1; } +#else + printk("Ignoring crashkernel command line, " + "parameter will be supplied by xen\n"); +#endif } #endif @@ -810,10 +819,23 @@ void __init setup_arch(char **cmdline_p) #endif #endif /* !CONFIG_XEN */ #ifdef CONFIG_KEXEC +#ifndef CONFIG_XEN if (crashk_res.start != crashk_res.end) { reserve_bootmem(crashk_res.start, crashk_res.end - crashk_res.start + 1); } +#else + { + xen_kexec_reserve_t reservation; + BUG_ON(HYPERVISOR_kexec(KEXEC_CMD_kexec_reserve, 0, + &reservation)); + if (reservation.size) { + crashk_res.start = reservation.start; + crashk_res.end = reservation.start + + reservation.size - 1; + } + } +#endif #endif paging_init(); @@ -972,6 +994,10 @@ void __init setup_arch(char **cmdline_p) iommu_hole_init(); #endif +#ifdef CONFIG_KEXEC + request_resource(&ioport_resource, &crashk_res); +#endif + #ifdef CONFIG_XEN { struct physdev_set_iopl set_iopl; --- /dev/null +++ x/linux-2.6-xen-sparse/include/asm-x86_64/kexec-xen.h @@ -0,0 +1,30 @@ +/* + * include/asm-x86_64/kexec-xen.h + * + * Created By: Horms + */ + +#ifndef _X86_64_KEXEC_XEN_H +#define _X86_64_KEXEC_XEN_H + +#include +#include +#include + +static inline void crash_translate_regs(struct pt_regs *linux_regs, + struct cpu_user_regs *xen_regs) +{ +#warning Implement me! +} + +#endif /* _X86_64_KEXEC_XEN_H */ + +/* + * Local variables: + * c-file-style: "linux" + * indent-tabs-mode: t + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ --- x/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/hypercall.h +++ x/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/hypercall.h @@ -360,4 +360,11 @@ HYPERVISOR_xenoprof_op( return _hypercall2(int, xenoprof_op, op, arg); } +static inline int +HYPERVISOR_kexec( + unsigned long op, unsigned int arg1, void * extra_args) +{ + return _hypercall3(int, kexec_op, op, arg1, extra_args); +} + #endif /* __HYPERCALL_H__ */ --- x/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/ptrace.h +++ x/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/ptrace.h @@ -90,6 +90,8 @@ extern unsigned long profile_pc(struct p #define profile_pc(regs) instruction_pointer(regs) #endif +#include + void signal_fault(struct pt_regs *regs, void __user *frame, char *where); struct task_struct; --- x/xen/arch/x86/x86_64/entry.S +++ x/xen/arch/x86/x86_64/entry.S @@ -556,6 +556,7 @@ ENTRY(hypercall_table) .quad do_xenoprof_op .quad do_event_channel_op .quad do_physdev_op + .quad do_kexec .rept NR_hypercalls-((.-hypercall_table)/8) .quad do_ni_hypercall .endr @@ -595,6 +596,7 @@ ENTRY(hypercall_args_table) .byte 2 /* do_xenoprof_op */ .byte 2 /* do_event_channel_op */ .byte 2 /* do_physdev_op */ + .byte 3 /* do_kexec */ .rept NR_hypercalls-(.-hypercall_args_table) .byte 0 /* do_ni_hypercall */ .endr --- x/xen/arch/x86/x86_64/machine_kexec.c +++ x/xen/arch/x86/x86_64/machine_kexec.c @@ -7,14 +7,24 @@ * Should be losely based on arch/x86_64/kernel/machine_kexec.c */ -#include /* for printk() used in stubs */ #include #include +#include + +typedef void (*relocate_new_kernel_t)(unsigned long indirection_page, + unsigned long control_code_buffer, + unsigned long start_address, + unsigned long page_table_a, + unsigned long page_table_b); void machine_kexec(xen_kexec_image_t *image) { - printk("STUB: arch/x86/x86_64/machine_kexec.c: machine_kexec: " - "not implemented\n"); + relocate_new_kernel_t rnk; + + rnk = (relocate_new_kernel_t) fix_to_virt(FIX_KEXEC_PAGE); + (*rnk)(image->indirection_page, image->reboot_code_buffer, + image->start_address, image->page_table_a[0], + image->page_table_b); } /* --- /dev/null +++ x/patches/linux-2.6.16.13/2-linux-2.6.16-kexec_page_table_a_x86_64.patch @@ -0,0 +1,421 @@ +kexec: Avoid overwriting the current pgd (V2, x86_64) + +This patch upgrades the x86_64-specific kexec code to avoid overwriting the +current pgd. Overwriting the current pgd is bad when CONFIG_CRASH_DUMP is used +to start a secondary kernel that dumps the memory of the previous kernel. + +The code introduces a new set of page tables called "page_table_a". These +tables are used to provide an executable identity mapping without overwriting +the current pgd. The already existing page table is renamed to "page_table_b". + +KEXEC_CONTROL_CODE_SIZE is changed into a single page. This updated version of +the patch also moves the segment handling code into the reloacte_kernel.S. + +Signed-off-by: Magnus Damm +--- + + The patch has been tested with regular kexec and CONFIG_CRASH_DUMP. + Applies on top of 2.6.16 and 2.6.17-rc4. + + arch/x86_64/kernel/machine_kexec.c | 193 +++++++++++++++++----------------- + arch/x86_64/kernel/relocate_kernel.S | 84 +++++++++++++- + include/asm-x86_64/kexec.h | 15 ++ + 3 files changed, 189 insertions(+), 103 deletions(-) + +--- x/arch/x86_64/kernel/machine_kexec.c ++++ x/arch/x86_64/kernel/machine_kexec.c +@@ -2,6 +2,10 @@ + * machine_kexec.c - handle transition of Linux booting another kernel + * Copyright (C) 2002-2005 Eric Biederman + * ++ * 2006-05-19 Magnus Damm : ++ * - rewrote identity map code to avoid overwriting current pgd ++ * - moved segment handling code into relocate_kernel.S ++ * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ +@@ -96,81 +100,110 @@ out: + } + + +-static int init_pgtable(struct kimage *image, unsigned long start_pgtable) ++static int create_page_table_b(struct kimage *image) + { +- pgd_t *level4p; +- level4p = (pgd_t *)__va(start_pgtable); +- return init_level4_page(image, level4p, 0, end_pfn << PAGE_SHIFT); +-} ++ struct kimage_arch *arch = &image->arch_data; + +-static void set_idt(void *newidt, u16 limit) +-{ +- struct desc_ptr curidt; ++ arch->page_table_b = kimage_alloc_control_pages(image, 0); + +- /* x86-64 supports unaliged loads & stores */ +- curidt.size = limit; +- curidt.address = (unsigned long)newidt; ++ if (!arch->page_table_b) ++ return -ENOMEM; + +- __asm__ __volatile__ ( +- "lidtq %0\n" +- : : "m" (curidt) +- ); +-}; ++ return init_level4_page(image, page_address(arch->page_table_b), ++ 0, end_pfn << PAGE_SHIFT); ++} + ++typedef NORET_TYPE void (*relocate_new_kernel_t)(unsigned long indirection_page, ++ unsigned long control_code_buffer, ++ unsigned long start_address, ++ unsigned long page_table_a, ++ unsigned long page_table_b) ATTRIB_NORET; ++ ++const extern unsigned char relocate_new_kernel[]; ++const extern unsigned long relocate_new_kernel_size; + +-static void set_gdt(void *newgdt, u16 limit) ++static int allocate_page_table_a(struct kimage *image) + { +- struct desc_ptr curgdt; ++ struct kimage_arch *arch = &image->arch_data; ++ struct page *page; ++ int k = sizeof(arch->page_table_a) / sizeof(arch->page_table_a[0]); + +- /* x86-64 supports unaligned loads & stores */ +- curgdt.size = limit; +- curgdt.address = (unsigned long)newgdt; ++ for (; k > 0; k--) { ++ page = kimage_alloc_control_pages(image, 0); ++ if (!page) ++ return -ENOMEM; + +- __asm__ __volatile__ ( +- "lgdtq %0\n" +- : : "m" (curgdt) +- ); +-}; ++ clear_page(page_address(page)); ++ arch->page_table_a[k - 1] = page; ++ } + +-static void load_segments(void) +-{ +- __asm__ __volatile__ ( +- "\tmovl %0,%%ds\n" +- "\tmovl %0,%%es\n" +- "\tmovl %0,%%ss\n" +- "\tmovl %0,%%fs\n" +- "\tmovl %0,%%gs\n" +- : : "a" (__KERNEL_DS) : "memory" +- ); ++ return 0; + } + +-typedef NORET_TYPE void (*relocate_new_kernel_t)(unsigned long indirection_page, +- unsigned long control_code_buffer, +- unsigned long start_address, +- unsigned long pgtable) ATTRIB_NORET; ++#define _PAGE_KERNEL_EXEC __PAGE_KERNEL_EXEC ++#define pa_page(page) __pa_symbol(page_address(page)) /* __pa() miscompiles */ + +-const extern unsigned char relocate_new_kernel[]; +-const extern unsigned long relocate_new_kernel_size; ++static int create_mapping(struct page *root, struct page **pages, ++ unsigned long va, unsigned long pa) ++{ ++ pgd_t *pgd; ++ pud_t *pud; ++ pmd_t *pmd; ++ pte_t *pte; ++ int k = 0; ++ ++ pgd = (pgd_t *)page_address(root) + pgd_index(va); ++ if (!pgd_present(*pgd)) ++ set_pgd(pgd, __pgd(pa_page(pages[k++]) | _KERNPG_TABLE)); ++ ++ pud = pud_offset(pgd, va); ++ if (!pud_present(*pud)) ++ set_pud(pud, __pud(pa_page(pages[k++]) | _KERNPG_TABLE)); ++ ++ pmd = pmd_offset(pud, va); ++ if (!pmd_present(*pmd)) ++ set_pmd(pmd, __pmd(pa_page(pages[k++]) | _KERNPG_TABLE)); ++ ++ pte = (pte_t *)page_address(pmd_page(*pmd)) + pte_index(va); ++ set_pte(pte, __pte(pa | _PAGE_KERNEL_EXEC)); ++ ++ return k; ++} + + int machine_kexec_prepare(struct kimage *image) + { +- unsigned long start_pgtable, control_code_buffer; +- int result; ++ void *control_page; ++ unsigned long pa; ++ int k; + +- /* Calculate the offsets */ +- start_pgtable = page_to_pfn(image->control_code_page) << PAGE_SHIFT; +- control_code_buffer = start_pgtable + PAGE_SIZE; +- +- /* Setup the identity mapped 64bit page table */ +- result = init_pgtable(image, start_pgtable); +- if (result) +- return result; +- +- /* Place the code in the reboot code buffer */ +- memcpy(__va(control_code_buffer), relocate_new_kernel, +- relocate_new_kernel_size); ++ memset(&image->arch_data, 0, sizeof(image->arch_data)); + +- return 0; ++ k = allocate_page_table_a(image); ++ if (k) ++ return k; ++ ++ /* fill in control_page with assembly code */ ++ ++ control_page = page_address(image->control_code_page); ++ memcpy(control_page, relocate_new_kernel, relocate_new_kernel_size); ++ ++ /* map the control_page at the virtual address of relocate_kernel.S */ ++ ++ pa = __pa(control_page); ++ ++ k = create_mapping(image->arch_data.page_table_a[0], ++ &image->arch_data.page_table_a[1], ++ (unsigned long)relocate_new_kernel, pa); ++ ++ /* identity map the control_page */ ++ ++ create_mapping(image->arch_data.page_table_a[0], ++ &image->arch_data.page_table_a[k + 1], ++ pa, pa); ++ ++ /* create identity mapped page table aka page_table_b */ ++ ++ return create_page_table_b(image); + } + + void machine_kexec_cleanup(struct kimage *image) +@@ -185,47 +218,17 @@ void machine_kexec_cleanup(struct kimage + NORET_TYPE void machine_kexec(struct kimage *image) + { + unsigned long page_list; +- unsigned long control_code_buffer; +- unsigned long start_pgtable; ++ unsigned long control_code; ++ unsigned long page_table_a; ++ unsigned long page_table_b; + relocate_new_kernel_t rnk; + +- /* Interrupts aren't acceptable while we reboot */ +- local_irq_disable(); +- +- /* Calculate the offsets */ + page_list = image->head; +- start_pgtable = page_to_pfn(image->control_code_page) << PAGE_SHIFT; +- control_code_buffer = start_pgtable + PAGE_SIZE; ++ control_code = __pa(page_address(image->control_code_page)); ++ page_table_a = __pa(page_address(image->arch_data.page_table_a[0])); ++ page_table_b = __pa(page_address(image->arch_data.page_table_b)); + +- /* Set the low half of the page table to my identity mapped +- * page table for kexec. Leave the high half pointing at the +- * kernel pages. Don't bother to flush the global pages +- * as that will happen when I fully switch to my identity mapped +- * page table anyway. +- */ +- memcpy(__va(read_cr3()), __va(start_pgtable), PAGE_SIZE/2); +- __flush_tlb(); +- +- +- /* The segment registers are funny things, they are +- * automatically loaded from a table, in memory wherever you +- * set them to a specific selector, but this table is never +- * accessed again unless you set the segment to a different selector. +- * +- * The more common model are caches where the behide +- * the scenes work is done, but is also dropped at arbitrary +- * times. +- * +- * I take advantage of this here by force loading the +- * segments, before I zap the gdt with an invalid value. +- */ +- load_segments(); +- /* The gdt & idt are now invalid. +- * If you want to load them you must set up your own idt & gdt. +- */ +- set_gdt(phys_to_virt(0),0); +- set_idt(phys_to_virt(0),0); + /* now call it */ +- rnk = (relocate_new_kernel_t) control_code_buffer; +- (*rnk)(page_list, control_code_buffer, image->start, start_pgtable); ++ rnk = (relocate_new_kernel_t) relocate_new_kernel; ++ (*rnk)(page_list, control_code, image->start, page_table_a, page_table_b); + } +--- x/arch/x86_64/kernel/relocate_kernel.S ++++ x/arch/x86_64/kernel/relocate_kernel.S +@@ -2,11 +2,18 @@ + * relocate_kernel.S - put the kernel image in place to boot + * Copyright (C) 2002-2005 Eric Biederman + * ++ * 2006-05-19 Magnus Damm : ++ * - moved segment handling code from machine_kexec.c ++ * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + + #include ++#include ++ ++.text ++.align (1 << PAGE_SHIFT) + + /* + * Must be relocatable PIC code callable as a C function, that once +@@ -18,21 +25,69 @@ relocate_new_kernel: + /* %rdi page_list + * %rsi reboot_code_buffer + * %rdx start address +- * %rcx page_table +- * %r8 arg5 ++ * %rcx page_table_a ++ * %r8 page_table_b + * %r9 arg6 + */ +- ++ + /* zero out flags, and disable interrupts */ + pushq $0 + popfq + ++ /* switch to page_table_a */ ++ movq %rcx, %cr3 ++ ++ /* setup idt */ ++ ++ movq %rsi, %rax ++ addq $(idt_48 - relocate_new_kernel), %rax ++ lidtq (%rax) ++ ++ /* setup gdt */ ++ ++ movq %rsi, %rax ++ addq $(gdt - relocate_new_kernel), %rax ++ movq %rsi, %r9 ++ addq $((gdt_48 - relocate_new_kernel) + 2), %r9 ++ movq %rax, (%r9) ++ ++ movq %rsi, %rax ++ addq $(gdt_48 - relocate_new_kernel), %rax ++ lgdtq (%rax) ++ ++ /* setup data segment registers */ ++ ++ xorl %eax,%eax ++ movl %eax, %ds ++ movl %eax, %es ++ movl %eax, %fs ++ movl %eax, %gs ++ movl %eax, %ss ++ + /* set a new stack at the bottom of our page... */ + lea 4096(%rsi), %rsp + ++ /* load new code segment */ ++ ++ movq %rsp, %rcx ++ xorq %rax, %rax ++ pushq %rax /* SS */ ++ pushq %rcx /* ESP */ ++ pushq %rax /* RFLAGS */ ++ ++ movq $(gdt_code - gdt), %rax ++ pushq %rax /* CS */ ++ ++ movq %rsi, %rax ++ addq $(identity_mapped - relocate_new_kernel), %rax ++ pushq %rax /* RIP */ ++ ++ iretq ++ ++identity_mapped: + /* store the parameters back on the stack */ + pushq %rdx /* store the start address */ +- ++ + /* Set cr0 to a known state: + * 31 1 == Paging enabled + * 18 0 == Alignment check disabled +@@ -69,7 +124,7 @@ relocate_new_kernel: + /* Switch to the identity mapped page tables, + * and flush the TLB. + */ +- movq %rcx, %cr3 ++ movq %r8, %cr3 + + /* Do the copies */ + movq %rdi, %rcx /* Put the page_list in %rcx */ +@@ -136,6 +191,25 @@ relocate_new_kernel: + xorq %r15, %r15 + + ret ++ .align 16 ++gdt: ++ .long 0x00000000 /* NULL descriptor */ ++ .long 0x00000000 ++gdt_code: ++ .long 0x00000000 /* code descriptor */ ++ .long 0x00209800 ++ ++gdt_end: ++ .align 4 ++ ++idt_48: ++ .word 0 # idt limit = 0 ++ .quad 0, 0 # idt base = 0L ++ ++gdt_48: ++ .word gdt_end - gdt - 1 # gdt limit ++ .quad 0, 0 # gdt base (filled in later) ++ + relocate_new_kernel_end: + + .globl relocate_new_kernel_size +--- x/include/asm-x86_64/kexec.h ++++ x/include/asm-x86_64/kexec.h +@@ -21,15 +21,24 @@ + /* Maximum address we can use for the control pages */ + #define KEXEC_CONTROL_MEMORY_LIMIT (0xFFFFFFFFFFUL) + +-/* Allocate one page for the pdp and the second for the code */ +-#define KEXEC_CONTROL_CODE_SIZE (4096UL + 4096UL) ++#define KEXEC_CONTROL_CODE_SIZE 4096 + + /* The native architecture */ + #define KEXEC_ARCH KEXEC_ARCH_X86_64 + + #define MAX_NOTE_BYTES 1024 + +-struct kimage_arch {}; ++struct kimage_arch { ++ /* page_table_a[] holds enough pages to create a new page table ++ * that maps the control page twice.. ++ * ++ * page_table_b points to the root page of a page table which is used ++ * to provide identity mapping of all ram. ++ */ ++ ++ struct page *page_table_a[7]; /* 2 * (pte + pud + pmd) + pgd */ ++ struct page *page_table_b; ++}; + + /* + * Saving the registers of the cpu on which panic occured in --- /dev/null +++ x/patches/linux-2.6.16.13/3-linux-2.6.16-kexec_page_table_a_x86_64-xen.patch @@ -0,0 +1,116 @@ + arch/x86_64/kernel/machine_kexec.c | 61 ++++++++++++++++++++++++++++++++---- + 1 file changed, 55 insertions(+), 6 deletions(-) + +--- x/arch/x86_64/kernel/machine_kexec.c ++++ x/arch/x86_64/kernel/machine_kexec.c +@@ -19,6 +19,48 @@ + #include + #include + ++#ifdef CONFIG_XEN ++#include ++#define x__pmd(x) ((pmd_t) { (x) } ) ++#define x__pud(x) ((pud_t) { (x) } ) ++#define x__pgd(x) ((pgd_t) { (x) } ) ++ ++#define x_pmd_val(x) ((x).pmd) ++#define x_pud_val(x) ((x).pud) ++#define x_pgd_val(x) ((x).pgd) ++ ++static inline void x_set_pmd(pmd_t *dst, pmd_t val) ++{ ++ x_pmd_val(*dst) = x_pmd_val(val); ++} ++ ++static inline void x_set_pud(pud_t *dst, pud_t val) ++{ ++ x_pud_val(*dst) = phys_to_machine(x_pud_val(val)); ++} ++ ++static inline void x_pud_clear (pud_t *pud) ++{ ++ x_pud_val(*pud) = 0; ++} ++ ++static inline void x_set_pgd(pgd_t *dst, pgd_t val) ++{ ++ x_pgd_val(*dst) = phys_to_machine(x_pgd_val(val)); ++} ++ ++static inline void x_pgd_clear (pgd_t * pgd) ++{ ++ x_pgd_val(*pgd) = 0; ++} ++ ++#define MY_LARGE_EXEC _PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_PSE ++#define MY_TABLE _PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY ++#else ++#define MY_LARGE_EXEC __PAGE_KERNEL_LARGE_EXEC ++#define MY_TABLE _KERNPG_TABLE ++#endif ++ + static void init_level2_page(pmd_t *level2p, unsigned long addr) + { + unsigned long end_addr; +@@ -26,7 +68,7 @@ + addr &= PAGE_MASK; + end_addr = addr + PUD_SIZE; + while (addr < end_addr) { +- set_pmd(level2p++, __pmd(addr | __PAGE_KERNEL_LARGE_EXEC)); ++ x_set_pmd(level2p++, x__pmd(addr | MY_LARGE_EXEC)); + addr += PMD_SIZE; + } + } +@@ -51,12 +93,12 @@ + } + level2p = (pmd_t *)page_address(page); + init_level2_page(level2p, addr); +- set_pud(level3p++, __pud(__pa(level2p) | _KERNPG_TABLE)); ++ x_set_pud(level3p++, x__pud(__pa(level2p) | MY_TABLE)); + addr += PUD_SIZE; + } + /* clear the unused entries */ + while (addr < end_addr) { +- pud_clear(level3p++); ++ x_pud_clear(level3p++); + addr += PUD_SIZE; + } + out: +@@ -87,12 +129,12 @@ + if (result) { + goto out; + } +- set_pgd(level4p++, __pgd(__pa(level3p) | _KERNPG_TABLE)); ++ x_set_pgd(level4p++, x__pgd(__pa(level3p) | MY_TABLE)); + addr += PGDIR_SIZE; + } + /* clear the unused entries */ + while (addr < end_addr) { +- pgd_clear(level4p++); ++ x_pgd_clear(level4p++); + addr += PGDIR_SIZE; + } + out: +@@ -103,14 +145,21 @@ + static int create_page_table_b(struct kimage *image) + { + struct kimage_arch *arch = &image->arch_data; ++ unsigned long last_page; + + arch->page_table_b = kimage_alloc_control_pages(image, 0); + + if (!arch->page_table_b) + return -ENOMEM; + ++#ifdef CONFIG_XEN ++ last_page = HYPERVISOR_memory_op(XENMEM_maximum_ram_page, NULL); ++#else ++ last_page = end_pfn; ++#endif ++ + return init_level4_page(image, page_address(arch->page_table_b), +- 0, end_pfn << PAGE_SHIFT); ++ 0, last_page << PAGE_SHIFT); + } + + typedef NORET_TYPE void (*relocate_new_kernel_t)(unsigned long indirection_page, --opJtzjQTFsWo+cga Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel --opJtzjQTFsWo+cga--