* Re: [PATCH v3 06/11] x86/xen: Add i386 kexec/kdump implementation
@ 2012-12-27 23:23 Daniel Kiper
2012-12-27 23:37 ` H. Peter Anvin
0 siblings, 1 reply; 6+ messages in thread
From: Daniel Kiper @ 2012-12-27 23:23 UTC (permalink / raw)
To: hpa
Cc: xen-devel, konrad.wilk, andrew.cooper3, x86, kexec, linux-kernel,
virtualization, mingo, ebiederm, jbeulich, maxim.uvarov, tglx,
vgoyal
> On 12/26/2012 06:18 PM, Daniel Kiper wrote:
> > Add i386 kexec/kdump implementation.
> >
> > v2 - suggestions/fixes:
> > - allocate transition page table pages below 4 GiB
> > (suggested by Jan Beulich).
>
> Why?
Sadly all addresses are passed via unsigned long
variable to kexec hypercall.
Daniel
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3 06/11] x86/xen: Add i386 kexec/kdump implementation 2012-12-27 23:23 [PATCH v3 06/11] x86/xen: Add i386 kexec/kdump implementation Daniel Kiper @ 2012-12-27 23:37 ` H. Peter Anvin 2012-12-28 3:16 ` Eric W. Biederman 0 siblings, 1 reply; 6+ messages in thread From: H. Peter Anvin @ 2012-12-27 23:37 UTC (permalink / raw) To: Daniel Kiper Cc: xen-devel, konrad.wilk, andrew.cooper3, x86, kexec, linux-kernel, virtualization, mingo, ebiederm, jbeulich, maxim.uvarov, tglx, vgoyal On 12/27/2012 03:23 PM, Daniel Kiper wrote: >> On 12/26/2012 06:18 PM, Daniel Kiper wrote: >>> Add i386 kexec/kdump implementation. >>> >>> v2 - suggestions/fixes: >>> - allocate transition page table pages below 4 GiB >>> (suggested by Jan Beulich). >> >> Why? > > Sadly all addresses are passed via unsigned long > variable to kexec hypercall. > So can you unf*ck your broken interface before imposing it on anyone else? -hpa -- H. Peter Anvin, Intel Open Source Technology Center I work for Intel. I don't speak on their behalf. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3 06/11] x86/xen: Add i386 kexec/kdump implementation 2012-12-27 23:37 ` H. Peter Anvin @ 2012-12-28 3:16 ` Eric W. Biederman 2013-01-02 14:43 ` [Xen-devel] " Ian Campbell 0 siblings, 1 reply; 6+ messages in thread From: Eric W. Biederman @ 2012-12-28 3:16 UTC (permalink / raw) To: H. Peter Anvin Cc: xen-devel, konrad.wilk, andrew.cooper3, Daniel Kiper, x86, kexec, linux-kernel, virtualization, mingo, jbeulich, maxim.uvarov, tglx, vgoyal "H. Peter Anvin" <hpa@zytor.com> writes: > On 12/27/2012 03:23 PM, Daniel Kiper wrote: >>> On 12/26/2012 06:18 PM, Daniel Kiper wrote: >>>> Add i386 kexec/kdump implementation. >>>> >>>> v2 - suggestions/fixes: >>>> - allocate transition page table pages below 4 GiB >>>> (suggested by Jan Beulich). >>> >>> Why? >> >> Sadly all addresses are passed via unsigned long >> variable to kexec hypercall. >> > > So can you unf*ck your broken interface before imposing it on anyone > else? Hasn't 32bit dom0 been retired? Untill the kexec firmware pass through and the normal kexec code paths are separated I can't make sense out of this code. The normal kexec code path should be doable without any special constraints on the kernel. Just leaning on some xen paravirt calls. The kexec pass through probably should not even touch x86 arch code. Certainly the same patch should not have code for both the xen kexec pass through and the paravirt arch code for the normal kexec path. Eric ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Xen-devel] [PATCH v3 06/11] x86/xen: Add i386 kexec/kdump implementation 2012-12-28 3:16 ` Eric W. Biederman @ 2013-01-02 14:43 ` Ian Campbell 0 siblings, 0 replies; 6+ messages in thread From: Ian Campbell @ 2013-01-02 14:43 UTC (permalink / raw) To: Eric W. Biederman Cc: xen-devel@lists.xensource.com, konrad.wilk@oracle.com, Andrew Cooper, Daniel Kiper, x86@kernel.org, kexec@lists.infradead.org, linux-kernel@vger.kernel.org, virtualization@lists.linux-foundation.org, mingo@redhat.com, jbeulich@suse.com, H. Peter Anvin, maxim.uvarov@oracle.com, tglx@linutronix.de, vgoyal@redhat.com On Fri, 2012-12-28 at 03:16 +0000, Eric W. Biederman wrote: > Hasn't 32bit dom0 been retired? The 32 bit hypervisor has been but 32 bit (PAE) guests (which includes dom0) are still supported on top of a 64 bit hypervisor. There are no plans to remove that support. Ian ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v3 00/11] xen: Initial kexec/kdump implementation @ 2012-12-27 2:18 Daniel Kiper 2012-12-27 2:18 ` [PATCH v3 01/11] kexec: introduce kexec firmware support Daniel Kiper 0 siblings, 1 reply; 6+ messages in thread From: Daniel Kiper @ 2012-12-27 2:18 UTC (permalink / raw) To: andrew.cooper3-Sxgqhf6Nn4DQT0dZR+AlfA, ebiederm-aS9lmoZGLiVWk0Htik3J/w, hpa-YMNOUZJC4hwAvxtiuMwx3w, jbeulich-IBi9RG/b67k, konrad.wilk-QHcLZuEGTsvQT0dZR+AlfA, maxim.uvarov-QHcLZuEGTsvQT0dZR+AlfA, mingo-H+wXaHxf7aLQT0dZR+AlfA, tglx-hfZtesqFncYOwBW4kG4KsQ, vgoyal-H+wXaHxf7aLQT0dZR+AlfA, x86-DgEjT+Ai2ygdnm+yROfE0A, kexec-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, linux-kernel-u79uwXL29TY76Z2rM5mHXA, virtualization-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA, xen-devel-GuqFBffKawuULHF6PoxzQEEOCMrvLtNR Hi, This set of patches contains initial kexec/kdump implementation for Xen v3. Currently only dom0 is supported, however, almost all infrustructure required for domU support is ready. Jan Beulich suggested to merge Xen x86 assembler code with baremetal x86 code. This could simplify and reduce a bit size of kernel code. However, this solution requires some changes in baremetal x86 code. First of all code which establishes transition page table should be moved back from machine_kexec_$(BITS).c to relocate_kernel_$(BITS).S. Another important thing which should be changed in that case is format of page_list array. Xen kexec hypercall requires to alternate physical addresses with virtual ones. These and other required stuff have not been done in that version because I am not sure that solution will be accepted by kexec/kdump maintainers. I hope that this email spark discussion about that topic. Daniel arch/x86/Kconfig | 3 + arch/x86/include/asm/kexec.h | 10 +- arch/x86/include/asm/xen/hypercall.h | 6 + arch/x86/include/asm/xen/kexec.h | 79 ++++ arch/x86/kernel/machine_kexec_64.c | 12 +- arch/x86/kernel/vmlinux.lds.S | 7 +- arch/x86/xen/Kconfig | 1 + arch/x86/xen/Makefile | 3 + arch/x86/xen/enlighten.c | 11 + arch/x86/xen/kexec.c | 150 +++++++ arch/x86/xen/machine_kexec_32.c | 226 +++++++++++ arch/x86/xen/machine_kexec_64.c | 318 +++++++++++++++ arch/x86/xen/relocate_kernel_32.S | 323 +++++++++++++++ arch/x86/xen/relocate_kernel_64.S | 309 ++++++++++++++ drivers/xen/sys-hypervisor.c | 42 ++- include/linux/kexec.h | 26 ++- include/xen/interface/xen.h | 33 ++ kernel/Makefile | 1 + kernel/kexec-firmware.c | 743 ++++++++++++++++++++++++++++++++++ kernel/kexec.c | 46 ++- 20 files changed, 2331 insertions(+), 18 deletions(-) Daniel Kiper (11): kexec: introduce kexec firmware support x86/kexec: Add extra pointers to transition page table PGD, PUD, PMD and PTE xen: Introduce architecture independent data for kexec/kdump x86/xen: Introduce architecture dependent data for kexec/kdump x86/xen: Register resources required by kexec-tools x86/xen: Add i386 kexec/kdump implementation x86/xen: Add x86_64 kexec/kdump implementation x86/xen: Add kexec/kdump Kconfig and makefile rules x86/xen/enlighten: Add init and crash kexec/kdump hooks drivers/xen: Export vmcoreinfo through sysfs x86: Add Xen kexec control code size check to linker script ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v3 01/11] kexec: introduce kexec firmware support @ 2012-12-27 2:18 ` Daniel Kiper 2012-12-27 2:18 ` [PATCH v3 02/11] x86/kexec: Add extra pointers to transition page table PGD, PUD, PMD and PTE Daniel Kiper 0 siblings, 1 reply; 6+ messages in thread From: Daniel Kiper @ 2012-12-27 2:18 UTC (permalink / raw) To: andrew.cooper3-Sxgqhf6Nn4DQT0dZR+AlfA, ebiederm-aS9lmoZGLiVWk0Htik3J/w, hpa-YMNOUZJC4hwAvxtiuMwx3w, jbeulich-IBi9RG/b67k, konrad.wilk-QHcLZuEGTsvQT0dZR+AlfA, maxim.uvarov-QHcLZuEGTsvQT0dZR+AlfA, mingo-H+wXaHxf7aLQT0dZR+AlfA, tglx-hfZtesqFncYOwBW4kG4KsQ, vgoyal-H+wXaHxf7aLQT0dZR+AlfA, x86-DgEjT+Ai2ygdnm+yROfE0A, kexec-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, linux-kernel-u79uwXL29TY76Z2rM5mHXA, virtualization-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA, xen-devel-GuqFBffKawuULHF6PoxzQEEOCMrvLtNR Cc: Daniel Kiper Some kexec/kdump implementations (e.g. Xen PVOPS) could not use default Linux infrastructure and require some support from firmware and/or hypervisor. To cope with that problem kexec firmware infrastructure was introduced. It allows a developer to use all kexec/kdump features of given firmware or hypervisor. v3 - suggestions/fixes: - replace kexec_ops struct by kexec firmware infrastructure (suggested by Eric Biederman). v2 - suggestions/fixes: - add comment for kexec_ops.crash_alloc_temp_store member (suggested by Konrad Rzeszutek Wilk), - simplify kexec_ops usage (suggested by Konrad Rzeszutek Wilk). Signed-off-by: Daniel Kiper <daniel.kiper-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org> --- include/linux/kexec.h | 26 ++- kernel/Makefile | 1 + kernel/kexec-firmware.c | 743 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/kexec.c | 46 +++- 4 files changed, 809 insertions(+), 7 deletions(-) create mode 100644 kernel/kexec-firmware.c diff --git a/include/linux/kexec.h b/include/linux/kexec.h index d0b8458..9568457 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -116,17 +116,34 @@ struct kimage { #endif }; - - /* kexec interface functions */ extern void machine_kexec(struct kimage *image); extern int machine_kexec_prepare(struct kimage *image); extern void machine_kexec_cleanup(struct kimage *image); +extern struct page *mf_kexec_kimage_alloc_pages(gfp_t gfp_mask, + unsigned int order, + unsigned long limit); +extern void mf_kexec_kimage_free_pages(struct page *page); +extern unsigned long mf_kexec_page_to_pfn(struct page *page); +extern struct page *mf_kexec_pfn_to_page(unsigned long mfn); +extern unsigned long mf_kexec_virt_to_phys(volatile void *address); +extern void *mf_kexec_phys_to_virt(unsigned long address); +extern int mf_kexec_prepare(struct kimage *image); +extern int mf_kexec_load(struct kimage *image); +extern void mf_kexec_cleanup(struct kimage *image); +extern void mf_kexec_unload(struct kimage *image); +extern void mf_kexec_shutdown(void); +extern void mf_kexec(struct kimage *image); extern asmlinkage long sys_kexec_load(unsigned long entry, unsigned long nr_segments, struct kexec_segment __user *segments, unsigned long flags); +extern long firmware_sys_kexec_load(unsigned long entry, + unsigned long nr_segments, + struct kexec_segment __user *segments, + unsigned long flags); extern int kernel_kexec(void); +extern int firmware_kernel_kexec(void); #ifdef CONFIG_COMPAT extern asmlinkage long compat_sys_kexec_load(unsigned long entry, unsigned long nr_segments, @@ -135,7 +152,10 @@ extern asmlinkage long compat_sys_kexec_load(unsigned long entry, #endif extern struct page *kimage_alloc_control_pages(struct kimage *image, unsigned int order); +extern struct page *firmware_kimage_alloc_control_pages(struct kimage *image, + unsigned int order); extern void crash_kexec(struct pt_regs *); +extern void firmware_crash_kexec(struct pt_regs *); int kexec_should_crash(struct task_struct *); void crash_save_cpu(struct pt_regs *regs, int cpu); void crash_save_vmcoreinfo(void); @@ -168,6 +188,8 @@ unsigned long paddr_vmcoreinfo_note(void); #define VMCOREINFO_CONFIG(name) \ vmcoreinfo_append_str("CONFIG_%s=y\n", #name) +extern bool kexec_use_firmware; + extern struct kimage *kexec_image; extern struct kimage *kexec_crash_image; diff --git a/kernel/Makefile b/kernel/Makefile index 6c072b6..bc96b2f 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_MODULE_SIG) += module_signing.o modsign_pubkey.o modsign_certificat obj-$(CONFIG_KALLSYMS) += kallsyms.o obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o obj-$(CONFIG_KEXEC) += kexec.o +obj-$(CONFIG_KEXEC_FIRMWARE) += kexec-firmware.o obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o obj-$(CONFIG_COMPAT) += compat.o obj-$(CONFIG_CGROUPS) += cgroup.o diff --git a/kernel/kexec-firmware.c b/kernel/kexec-firmware.c new file mode 100644 index 0000000..f6ddd4c --- /dev/null +++ b/kernel/kexec-firmware.c @@ -0,0 +1,743 @@ +/* + * Copyright (C) 2002-2004 Eric Biederman <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org> + * Copyright (C) 2012 Daniel Kiper, Oracle Corporation + * + * Most of the code here is a copy of kernel/kexec.c. + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/atomic.h> +#include <linux/errno.h> +#include <linux/highmem.h> +#include <linux/kernel.h> +#include <linux/kexec.h> +#include <linux/list.h> +#include <linux/mm.h> +#include <linux/reboot.h> +#include <linux/slab.h> + +#include <asm/uaccess.h> + +/* + * KIMAGE_NO_DEST is an impossible destination address..., for + * allocating pages whose destination address we do not care about. + */ +#define KIMAGE_NO_DEST (-1UL) + +static int kimage_is_destination_range(struct kimage *image, + unsigned long start, unsigned long end); +static struct page *kimage_alloc_page(struct kimage *image, + gfp_t gfp_mask, + unsigned long dest); + +static int do_kimage_alloc(struct kimage **rimage, unsigned long entry, + unsigned long nr_segments, + struct kexec_segment __user *segments) +{ + size_t segment_bytes; + struct kimage *image; + unsigned long i; + int result; + + /* Allocate a controlling structure */ + result = -ENOMEM; + image = kzalloc(sizeof(*image), GFP_KERNEL); + if (!image) + goto out; + + image->head = 0; + image->entry = &image->head; + image->last_entry = &image->head; + image->control_page = ~0; /* By default this does not apply */ + image->start = entry; + image->type = KEXEC_TYPE_DEFAULT; + + /* Initialize the list of control pages */ + INIT_LIST_HEAD(&image->control_pages); + + /* Initialize the list of destination pages */ + INIT_LIST_HEAD(&image->dest_pages); + + /* Initialize the list of unusable pages */ + INIT_LIST_HEAD(&image->unuseable_pages); + + /* Read in the segments */ + image->nr_segments = nr_segments; + segment_bytes = nr_segments * sizeof(*segments); + result = copy_from_user(image->segment, segments, segment_bytes); + if (result) { + result = -EFAULT; + goto out; + } + + /* + * Verify we have good destination addresses. The caller is + * responsible for making certain we don't attempt to load + * the new image into invalid or reserved areas of RAM. This + * just verifies it is an address we can use. + * + * Since the kernel does everything in page size chunks ensure + * the destination addresses are page aligned. Too many + * special cases crop of when we don't do this. The most + * insidious is getting overlapping destination addresses + * simply because addresses are changed to page size + * granularity. + */ + result = -EADDRNOTAVAIL; + for (i = 0; i < nr_segments; i++) { + unsigned long mstart, mend; + + mstart = image->segment[i].mem; + mend = mstart + image->segment[i].memsz; + if ((mstart & ~PAGE_MASK) || (mend & ~PAGE_MASK)) + goto out; + if (mend >= KEXEC_DESTINATION_MEMORY_LIMIT) + goto out; + } + + /* Verify our destination addresses do not overlap. + * If we alloed overlapping destination addresses + * through very weird things can happen with no + * easy explanation as one segment stops on another. + */ + result = -EINVAL; + for (i = 0; i < nr_segments; i++) { + unsigned long mstart, mend; + unsigned long j; + + mstart = image->segment[i].mem; + mend = mstart + image->segment[i].memsz; + for (j = 0; j < i; j++) { + unsigned long pstart, pend; + pstart = image->segment[j].mem; + pend = pstart + image->segment[j].memsz; + /* Do the segments overlap ? */ + if ((mend > pstart) && (mstart < pend)) + goto out; + } + } + + /* Ensure our buffer sizes are strictly less than + * our memory sizes. This should always be the case, + * and it is easier to check up front than to be surprised + * later on. + */ + result = -EINVAL; + for (i = 0; i < nr_segments; i++) { + if (image->segment[i].bufsz > image->segment[i].memsz) + goto out; + } + + result = 0; +out: + if (result == 0) + *rimage = image; + else + kfree(image); + + return result; + +} + +static int kimage_normal_alloc(struct kimage **rimage, unsigned long entry, + unsigned long nr_segments, + struct kexec_segment __user *segments) +{ + int result; + struct kimage *image; + + /* Allocate and initialize a controlling structure */ + image = NULL; + result = do_kimage_alloc(&image, entry, nr_segments, segments); + if (result) + goto out; + + *rimage = image; + + /* + * Find a location for the control code buffer, and add it + * the vector of segments so that it's pages will also be + * counted as destination pages. + */ + result = -ENOMEM; + image->control_code_page = firmware_kimage_alloc_control_pages(image, + get_order(KEXEC_CONTROL_PAGE_SIZE)); + if (!image->control_code_page) { + printk(KERN_ERR "Could not allocate control_code_buffer\n"); + goto out; + } + + image->swap_page = firmware_kimage_alloc_control_pages(image, 0); + if (!image->swap_page) { + printk(KERN_ERR "Could not allocate swap buffer\n"); + goto out; + } + + result = 0; + out: + if (result == 0) + *rimage = image; + else + kfree(image); + + return result; +} + +static int kimage_crash_alloc(struct kimage **rimage, unsigned long entry, + unsigned long nr_segments, + struct kexec_segment __user *segments) +{ + int result; + struct kimage *image; + unsigned long i; + + image = NULL; + /* Verify we have a valid entry point */ + if ((entry < crashk_res.start) || (entry > crashk_res.end)) { + result = -EADDRNOTAVAIL; + goto out; + } + + /* Allocate and initialize a controlling structure */ + result = do_kimage_alloc(&image, entry, nr_segments, segments); + if (result) + goto out; + + /* Enable the special crash kernel control page + * allocation policy. + */ + image->control_page = crashk_res.start; + image->type = KEXEC_TYPE_CRASH; + + /* + * Verify we have good destination addresses. Normally + * the caller is responsible for making certain we don't + * attempt to load the new image into invalid or reserved + * areas of RAM. But crash kernels are preloaded into a + * reserved area of ram. We must ensure the addresses + * are in the reserved area otherwise preloading the + * kernel could corrupt things. + */ + result = -EADDRNOTAVAIL; + for (i = 0; i < nr_segments; i++) { + unsigned long mstart, mend; + + mstart = image->segment[i].mem; + mend = mstart + image->segment[i].memsz - 1; + /* Ensure we are within the crash kernel limits */ + if ((mstart < crashk_res.start) || (mend > crashk_res.end)) + goto out; + } + + /* + * Find a location for the control code buffer, and add + * the vector of segments so that it's pages will also be + * counted as destination pages. + */ + result = -ENOMEM; + image->control_code_page = firmware_kimage_alloc_control_pages(image, + get_order(KEXEC_CONTROL_PAGE_SIZE)); + if (!image->control_code_page) { + printk(KERN_ERR "Could not allocate control_code_buffer\n"); + goto out; + } + + result = 0; +out: + if (result == 0) + *rimage = image; + else + kfree(image); + + return result; +} + +static int kimage_is_destination_range(struct kimage *image, + unsigned long start, + unsigned long end) +{ + unsigned long i; + + for (i = 0; i < image->nr_segments; i++) { + unsigned long mstart, mend; + + mstart = image->segment[i].mem; + mend = mstart + image->segment[i].memsz; + if ((end > mstart) && (start < mend)) + return 1; + } + + return 0; +} + +static void kimage_free_page_list(struct list_head *list) +{ + struct list_head *pos, *next; + + list_for_each_safe(pos, next, list) { + struct page *page; + + page = list_entry(pos, struct page, lru); + list_del(&page->lru); + mf_kexec_kimage_free_pages(page); + } +} + +static struct page *kimage_alloc_normal_control_pages(struct kimage *image, + unsigned int order) +{ + /* Control pages are special, they are the intermediaries + * that are needed while we copy the rest of the pages + * to their final resting place. As such they must + * not conflict with either the destination addresses + * or memory the kernel is already using. + * + * The only case where we really need more than one of + * these are for architectures where we cannot disable + * the MMU and must instead generate an identity mapped + * page table for all of the memory. + * + * At worst this runs in O(N) of the image size. + */ + struct list_head extra_pages; + struct page *pages; + unsigned int count; + + count = 1 << order; + INIT_LIST_HEAD(&extra_pages); + + /* Loop while I can allocate a page and the page allocated + * is a destination page. + */ + do { + unsigned long pfn, epfn, addr, eaddr; + + pages = mf_kexec_kimage_alloc_pages(GFP_KERNEL, order, + KEXEC_CONTROL_MEMORY_LIMIT); + if (!pages) + break; + pfn = mf_kexec_page_to_pfn(pages); + epfn = pfn + count; + addr = pfn << PAGE_SHIFT; + eaddr = epfn << PAGE_SHIFT; + if ((epfn >= (KEXEC_CONTROL_MEMORY_LIMIT >> PAGE_SHIFT)) || + kimage_is_destination_range(image, addr, eaddr)) { + list_add(&pages->lru, &extra_pages); + pages = NULL; + } + } while (!pages); + + if (pages) { + /* Remember the allocated page... */ + list_add(&pages->lru, &image->control_pages); + + /* Because the page is already in it's destination + * location we will never allocate another page at + * that address. Therefore mf_kexec_kimage_alloc_pages + * will not return it (again) and we don't need + * to give it an entry in image->segment[]. + */ + } + /* Deal with the destination pages I have inadvertently allocated. + * + * Ideally I would convert multi-page allocations into single + * page allocations, and add everything to image->dest_pages. + * + * For now it is simpler to just free the pages. + */ + kimage_free_page_list(&extra_pages); + + return pages; +} + +struct page *firmware_kimage_alloc_control_pages(struct kimage *image, + unsigned int order) +{ + return kimage_alloc_normal_control_pages(image, order); +} + +static int kimage_add_entry(struct kimage *image, kimage_entry_t entry) +{ + if (*image->entry != 0) + image->entry++; + + if (image->entry == image->last_entry) { + kimage_entry_t *ind_page; + struct page *page; + + page = kimage_alloc_page(image, GFP_KERNEL, KIMAGE_NO_DEST); + if (!page) + return -ENOMEM; + + ind_page = page_address(page); + *image->entry = mf_kexec_virt_to_phys(ind_page) | IND_INDIRECTION; + image->entry = ind_page; + image->last_entry = ind_page + + ((PAGE_SIZE/sizeof(kimage_entry_t)) - 1); + } + *image->entry = entry; + image->entry++; + *image->entry = 0; + + return 0; +} + +static int kimage_set_destination(struct kimage *image, + unsigned long destination) +{ + int result; + + destination &= PAGE_MASK; + result = kimage_add_entry(image, destination | IND_DESTINATION); + if (result == 0) + image->destination = destination; + + return result; +} + + +static int kimage_add_page(struct kimage *image, unsigned long page) +{ + int result; + + page &= PAGE_MASK; + result = kimage_add_entry(image, page | IND_SOURCE); + if (result == 0) + image->destination += PAGE_SIZE; + + return result; +} + + +static void kimage_free_extra_pages(struct kimage *image) +{ + /* Walk through and free any extra destination pages I may have */ + kimage_free_page_list(&image->dest_pages); + + /* Walk through and free any unusable pages I have cached */ + kimage_free_page_list(&image->unuseable_pages); + +} +static void kimage_terminate(struct kimage *image) +{ + if (*image->entry != 0) + image->entry++; + + *image->entry = IND_DONE; +} + +#define for_each_kimage_entry(image, ptr, entry) \ + for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); \ + ptr = (entry & IND_INDIRECTION)? \ + mf_kexec_phys_to_virt((entry & PAGE_MASK)): ptr +1) + +static void kimage_free_entry(kimage_entry_t entry) +{ + struct page *page; + + page = mf_kexec_pfn_to_page(entry >> PAGE_SHIFT); + mf_kexec_kimage_free_pages(page); +} + +static void kimage_free(struct kimage *image) +{ + kimage_entry_t *ptr, entry; + kimage_entry_t ind = 0; + + if (!image) + return; + + kimage_free_extra_pages(image); + for_each_kimage_entry(image, ptr, entry) { + if (entry & IND_INDIRECTION) { + /* Free the previous indirection page */ + if (ind & IND_INDIRECTION) + kimage_free_entry(ind); + /* Save this indirection page until we are + * done with it. + */ + ind = entry; + } + else if (entry & IND_SOURCE) + kimage_free_entry(entry); + } + /* Free the final indirection page */ + if (ind & IND_INDIRECTION) + kimage_free_entry(ind); + + /* Handle any machine specific cleanup */ + mf_kexec_cleanup(image); + + /* Free the kexec control pages... */ + kimage_free_page_list(&image->control_pages); + kfree(image); +} + +static kimage_entry_t *kimage_dst_used(struct kimage *image, + unsigned long page) +{ + kimage_entry_t *ptr, entry; + unsigned long destination = 0; + + for_each_kimage_entry(image, ptr, entry) { + if (entry & IND_DESTINATION) + destination = entry & PAGE_MASK; + else if (entry & IND_SOURCE) { + if (page == destination) + return ptr; + destination += PAGE_SIZE; + } + } + + return NULL; +} + +static struct page *kimage_alloc_page(struct kimage *image, + gfp_t gfp_mask, + unsigned long destination) +{ + /* + * Here we implement safeguards to ensure that a source page + * is not copied to its destination page before the data on + * the destination page is no longer useful. + * + * To do this we maintain the invariant that a source page is + * either its own destination page, or it is not a + * destination page at all. + * + * That is slightly stronger than required, but the proof + * that no problems will not occur is trivial, and the + * implementation is simply to verify. + * + * When allocating all pages normally this algorithm will run + * in O(N) time, but in the worst case it will run in O(N^2) + * time. If the runtime is a problem the data structures can + * be fixed. + */ + struct page *page; + unsigned long addr; + + /* + * Walk through the list of destination pages, and see if I + * have a match. + */ + list_for_each_entry(page, &image->dest_pages, lru) { + addr = mf_kexec_page_to_pfn(page) << PAGE_SHIFT; + if (addr == destination) { + list_del(&page->lru); + return page; + } + } + page = NULL; + while (1) { + kimage_entry_t *old; + + /* Allocate a page, if we run out of memory give up */ + page = mf_kexec_kimage_alloc_pages(gfp_mask, 0, + KEXEC_SOURCE_MEMORY_LIMIT); + if (!page) + return NULL; + /* If the page cannot be used file it away */ + if (mf_kexec_page_to_pfn(page) > + (KEXEC_SOURCE_MEMORY_LIMIT >> PAGE_SHIFT)) { + list_add(&page->lru, &image->unuseable_pages); + continue; + } + addr = mf_kexec_page_to_pfn(page) << PAGE_SHIFT; + + /* If it is the destination page we want use it */ + if (addr == destination) + break; + + /* If the page is not a destination page use it */ + if (!kimage_is_destination_range(image, addr, + addr + PAGE_SIZE)) + break; + + /* + * I know that the page is someones destination page. + * See if there is already a source page for this + * destination page. And if so swap the source pages. + */ + old = kimage_dst_used(image, addr); + if (old) { + /* If so move it */ + unsigned long old_addr; + struct page *old_page; + + old_addr = *old & PAGE_MASK; + old_page = mf_kexec_pfn_to_page(old_addr >> PAGE_SHIFT); + copy_highpage(page, old_page); + *old = addr | (*old & ~PAGE_MASK); + + /* The old page I have found cannot be a + * destination page, so return it if it's + * gfp_flags honor the ones passed in. + */ + if (!(gfp_mask & __GFP_HIGHMEM) && + PageHighMem(old_page)) { + mf_kexec_kimage_free_pages(old_page); + continue; + } + addr = old_addr; + page = old_page; + break; + } + else { + /* Place the page on the destination list I + * will use it later. + */ + list_add(&page->lru, &image->dest_pages); + } + } + + return page; +} + +static int kimage_load_normal_segment(struct kimage *image, + struct kexec_segment *segment) +{ + unsigned long maddr; + unsigned long ubytes, mbytes; + int result; + unsigned char __user *buf; + + result = 0; + buf = segment->buf; + ubytes = segment->bufsz; + mbytes = segment->memsz; + maddr = segment->mem; + + result = kimage_set_destination(image, maddr); + if (result < 0) + goto out; + + while (mbytes) { + struct page *page; + char *ptr; + size_t uchunk, mchunk; + + page = kimage_alloc_page(image, GFP_HIGHUSER, maddr); + if (!page) { + result = -ENOMEM; + goto out; + } + result = kimage_add_page(image, mf_kexec_page_to_pfn(page) + << PAGE_SHIFT); + if (result < 0) + goto out; + + ptr = kmap(page); + /* Start with a clear page */ + clear_page(ptr); + ptr += maddr & ~PAGE_MASK; + mchunk = PAGE_SIZE - (maddr & ~PAGE_MASK); + if (mchunk > mbytes) + mchunk = mbytes; + + uchunk = mchunk; + if (uchunk > ubytes) + uchunk = ubytes; + + result = copy_from_user(ptr, buf, uchunk); + kunmap(page); + if (result) { + result = -EFAULT; + goto out; + } + ubytes -= uchunk; + maddr += mchunk; + buf += mchunk; + mbytes -= mchunk; + } +out: + return result; +} + +static int kimage_load_segment(struct kimage *image, + struct kexec_segment *segment) +{ + return kimage_load_normal_segment(image, segment); +} + +long firmware_sys_kexec_load(unsigned long entry, unsigned long nr_segments, + struct kexec_segment __user *segments, + unsigned long flags) +{ + struct kimage **dest_image, *image = NULL; + int result = 0; + + dest_image = &kexec_image; + if (flags & KEXEC_ON_CRASH) + dest_image = &kexec_crash_image; + if (nr_segments > 0) { + unsigned long i; + + /* Loading another kernel to reboot into */ + if ((flags & KEXEC_ON_CRASH) == 0) + result = kimage_normal_alloc(&image, entry, + nr_segments, segments); + /* Loading another kernel to switch to if this one crashes */ + else if (flags & KEXEC_ON_CRASH) { + /* Free any current crash dump kernel before + * we corrupt it. + */ + mf_kexec_unload(image); + kimage_free(xchg(&kexec_crash_image, NULL)); + result = kimage_crash_alloc(&image, entry, + nr_segments, segments); + } + if (result) + goto out; + + if (flags & KEXEC_PRESERVE_CONTEXT) + image->preserve_context = 1; + result = mf_kexec_prepare(image); + if (result) + goto out; + + for (i = 0; i < nr_segments; i++) { + result = kimage_load_segment(image, &image->segment[i]); + if (result) + goto out; + } + kimage_terminate(image); + } + + result = mf_kexec_load(image); + + if (result) + goto out; + + /* Install the new kernel, and Uninstall the old */ + image = xchg(dest_image, image); + +out: + mf_kexec_unload(image); + + kimage_free(image); + + return result; +} + +void firmware_crash_kexec(struct pt_regs *regs) +{ + struct pt_regs fixed_regs; + + crash_setup_regs(&fixed_regs, regs); + crash_save_vmcoreinfo(); + machine_crash_shutdown(&fixed_regs); + mf_kexec(kexec_crash_image); +} + +int firmware_kernel_kexec(void) +{ + kernel_restart_prepare(NULL); + printk(KERN_EMERG "Starting new kernel\n"); + mf_kexec_shutdown(); + mf_kexec(kexec_image); + + return 0; +} diff --git a/kernel/kexec.c b/kernel/kexec.c index 5e4bd78..9f3b6cb 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -38,6 +38,10 @@ #include <asm/io.h> #include <asm/sections.h> +#ifdef CONFIG_KEXEC_FIRMWARE +bool kexec_use_firmware = false; +#endif + /* Per cpu memory for storing cpu states in case of system crash. */ note_buf_t __percpu *crash_notes; @@ -924,7 +928,7 @@ static int kimage_load_segment(struct kimage *image, * the devices in a consistent state so a later kernel can * reinitialize them. * - * - A machine specific part that includes the syscall number + * - A machine/firmware specific part that includes the syscall number * and the copies the image to it's final destination. And * jumps into the image at entry. * @@ -978,6 +982,17 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments, if (!mutex_trylock(&kexec_mutex)) return -EBUSY; +#ifdef CONFIG_KEXEC_FIRMWARE + if (kexec_use_firmware) { + result = firmware_sys_kexec_load(entry, nr_segments, + segments, flags); + + mutex_unlock(&kexec_mutex); + + return result; + } +#endif + dest_image = &kexec_image; if (flags & KEXEC_ON_CRASH) dest_image = &kexec_crash_image; @@ -1091,10 +1106,17 @@ void crash_kexec(struct pt_regs *regs) if (kexec_crash_image) { struct pt_regs fixed_regs; - crash_setup_regs(&fixed_regs, regs); - crash_save_vmcoreinfo(); - machine_crash_shutdown(&fixed_regs); - machine_kexec(kexec_crash_image); +#ifdef CONFIG_KEXEC_FIRMWARE + if (kexec_use_firmware) + firmware_crash_kexec(regs); + else +#endif + { + crash_setup_regs(&fixed_regs, regs); + crash_save_vmcoreinfo(); + machine_crash_shutdown(&fixed_regs); + machine_kexec(kexec_crash_image); + } } mutex_unlock(&kexec_mutex); } @@ -1132,6 +1154,13 @@ int crash_shrink_memory(unsigned long new_size) mutex_lock(&kexec_mutex); +#ifdef CONFIG_KEXEC_FIRMWARE + if (kexec_use_firmware) { + ret = -ENOSYS; + goto unlock; + } +#endif + if (kexec_crash_image) { ret = -ENOENT; goto unlock; @@ -1536,6 +1565,13 @@ int kernel_kexec(void) goto Unlock; } +#ifdef CONFIG_KEXEC_FIRMWARE + if (kexec_use_firmware) { + error = firmware_kernel_kexec(); + goto Unlock; + } +#endif + #ifdef CONFIG_KEXEC_JUMP if (kexec_image->preserve_context) { lock_system_sleep(); -- 1.5.6.5 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 02/11] x86/kexec: Add extra pointers to transition page table PGD, PUD, PMD and PTE @ 2012-12-27 2:18 ` Daniel Kiper 2012-12-27 2:18 ` [PATCH v3 03/11] xen: Introduce architecture independent data for kexec/kdump Daniel Kiper 0 siblings, 1 reply; 6+ messages in thread From: Daniel Kiper @ 2012-12-27 2:18 UTC (permalink / raw) To: andrew.cooper3-Sxgqhf6Nn4DQT0dZR+AlfA, ebiederm-aS9lmoZGLiVWk0Htik3J/w, hpa-YMNOUZJC4hwAvxtiuMwx3w, jbeulich-IBi9RG/b67k, konrad.wilk-QHcLZuEGTsvQT0dZR+AlfA, maxim.uvarov-QHcLZuEGTsvQT0dZR+AlfA, mingo-H+wXaHxf7aLQT0dZR+AlfA, tglx-hfZtesqFncYOwBW4kG4KsQ, vgoyal-H+wXaHxf7aLQT0dZR+AlfA, x86-DgEjT+Ai2ygdnm+yROfE0A, kexec-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, linux-kernel-u79uwXL29TY76Z2rM5mHXA, virtualization-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA, xen-devel-GuqFBffKawuULHF6PoxzQEEOCMrvLtNR Cc: Daniel Kiper Some implementations (e.g. Xen PVOPS) could not use part of identity page table to construct transition page table. It means that they require separate PUDs, PMDs and PTEs for virtual and physical (identity) mapping. To satisfy that requirement add extra pointer to PGD, PUD, PMD and PTE and align existing code. Signed-off-by: Daniel Kiper <daniel.kiper-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org> --- arch/x86/include/asm/kexec.h | 10 +++++++--- arch/x86/kernel/machine_kexec_64.c | 12 ++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/arch/x86/include/asm/kexec.h b/arch/x86/include/asm/kexec.h index 6080d26..cedd204 100644 --- a/arch/x86/include/asm/kexec.h +++ b/arch/x86/include/asm/kexec.h @@ -157,9 +157,13 @@ struct kimage_arch { }; #else struct kimage_arch { - pud_t *pud; - pmd_t *pmd; - pte_t *pte; + pgd_t *pgd; + pud_t *pud0; + pud_t *pud1; + pmd_t *pmd0; + pmd_t *pmd1; + pte_t *pte0; + pte_t *pte1; }; #endif diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c index b3ea9db..976e54b 100644 --- a/arch/x86/kernel/machine_kexec_64.c +++ b/arch/x86/kernel/machine_kexec_64.c @@ -137,9 +137,9 @@ out: static void free_transition_pgtable(struct kimage *image) { - free_page((unsigned long)image->arch.pud); - free_page((unsigned long)image->arch.pmd); - free_page((unsigned long)image->arch.pte); + free_page((unsigned long)image->arch.pud0); + free_page((unsigned long)image->arch.pmd0); + free_page((unsigned long)image->arch.pte0); } static int init_transition_pgtable(struct kimage *image, pgd_t *pgd) @@ -157,7 +157,7 @@ static int init_transition_pgtable(struct kimage *image, pgd_t *pgd) pud = (pud_t *)get_zeroed_page(GFP_KERNEL); if (!pud) goto err; - image->arch.pud = pud; + image->arch.pud0 = pud; set_pgd(pgd, __pgd(__pa(pud) | _KERNPG_TABLE)); } pud = pud_offset(pgd, vaddr); @@ -165,7 +165,7 @@ static int init_transition_pgtable(struct kimage *image, pgd_t *pgd) pmd = (pmd_t *)get_zeroed_page(GFP_KERNEL); if (!pmd) goto err; - image->arch.pmd = pmd; + image->arch.pmd0 = pmd; set_pud(pud, __pud(__pa(pmd) | _KERNPG_TABLE)); } pmd = pmd_offset(pud, vaddr); @@ -173,7 +173,7 @@ static int init_transition_pgtable(struct kimage *image, pgd_t *pgd) pte = (pte_t *)get_zeroed_page(GFP_KERNEL); if (!pte) goto err; - image->arch.pte = pte; + image->arch.pte0 = pte; set_pmd(pmd, __pmd(__pa(pte) | _KERNPG_TABLE)); } pte = pte_offset_kernel(pmd, vaddr); -- 1.5.6.5 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 03/11] xen: Introduce architecture independent data for kexec/kdump @ 2012-12-27 2:18 ` Daniel Kiper 2012-12-27 2:18 ` [PATCH v3 04/11] x86/xen: Introduce architecture dependent " Daniel Kiper 0 siblings, 1 reply; 6+ messages in thread From: Daniel Kiper @ 2012-12-27 2:18 UTC (permalink / raw) To: andrew.cooper3-Sxgqhf6Nn4DQT0dZR+AlfA, ebiederm-aS9lmoZGLiVWk0Htik3J/w, hpa-YMNOUZJC4hwAvxtiuMwx3w, jbeulich-IBi9RG/b67k, konrad.wilk-QHcLZuEGTsvQT0dZR+AlfA, maxim.uvarov-QHcLZuEGTsvQT0dZR+AlfA, mingo-H+wXaHxf7aLQT0dZR+AlfA, tglx-hfZtesqFncYOwBW4kG4KsQ, vgoyal-H+wXaHxf7aLQT0dZR+AlfA, x86-DgEjT+Ai2ygdnm+yROfE0A, kexec-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, linux-kernel-u79uwXL29TY76Z2rM5mHXA, virtualization-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA, xen-devel-GuqFBffKawuULHF6PoxzQEEOCMrvLtNR Cc: Daniel Kiper Introduce architecture independent constants and structures required by Xen kexec/kdump implementation. Signed-off-by: Daniel Kiper <daniel.kiper-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org> --- include/xen/interface/xen.h | 33 +++++++++++++++++++++++++++++++++ 1 files changed, 33 insertions(+), 0 deletions(-) diff --git a/include/xen/interface/xen.h b/include/xen/interface/xen.h index 886a5d8..09c16ab 100644 --- a/include/xen/interface/xen.h +++ b/include/xen/interface/xen.h @@ -57,6 +57,7 @@ #define __HYPERVISOR_event_channel_op 32 #define __HYPERVISOR_physdev_op 33 #define __HYPERVISOR_hvm_op 34 +#define __HYPERVISOR_kexec_op 37 #define __HYPERVISOR_tmem_op 38 /* Architecture-specific hypercall definitions. */ @@ -231,7 +232,39 @@ DEFINE_GUEST_HANDLE_STRUCT(mmuext_op); #define VMASST_TYPE_pae_extended_cr3 3 #define MAX_VMASST_TYPE 3 +/* + * Commands to HYPERVISOR_kexec_op(). + */ +#define KEXEC_CMD_kexec 0 +#define KEXEC_CMD_kexec_load 1 +#define KEXEC_CMD_kexec_unload 2 +#define KEXEC_CMD_kexec_get_range 3 + +/* + * Memory ranges for kdump (utilized by HYPERVISOR_kexec_op()). + */ +#define KEXEC_RANGE_MA_CRASH 0 +#define KEXEC_RANGE_MA_XEN 1 +#define KEXEC_RANGE_MA_CPU 2 +#define KEXEC_RANGE_MA_XENHEAP 3 +#define KEXEC_RANGE_MA_BOOT_PARAM 4 +#define KEXEC_RANGE_MA_EFI_MEMMAP 5 +#define KEXEC_RANGE_MA_VMCOREINFO 6 + #ifndef __ASSEMBLY__ +struct xen_kexec_exec { + int type; +}; + +struct xen_kexec_range { + int range; + int nr; + unsigned long size; + unsigned long start; +}; + +extern unsigned long xen_vmcoreinfo_maddr; +extern unsigned long xen_vmcoreinfo_max_size; typedef uint16_t domid_t; -- 1.5.6.5 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 04/11] x86/xen: Introduce architecture dependent data for kexec/kdump @ 2012-12-27 2:18 ` Daniel Kiper 2012-12-27 2:18 ` [PATCH v3 05/11] x86/xen: Register resources required by kexec-tools Daniel Kiper 0 siblings, 1 reply; 6+ messages in thread From: Daniel Kiper @ 2012-12-27 2:18 UTC (permalink / raw) To: andrew.cooper3-Sxgqhf6Nn4DQT0dZR+AlfA, ebiederm-aS9lmoZGLiVWk0Htik3J/w, hpa-YMNOUZJC4hwAvxtiuMwx3w, jbeulich-IBi9RG/b67k, konrad.wilk-QHcLZuEGTsvQT0dZR+AlfA, maxim.uvarov-QHcLZuEGTsvQT0dZR+AlfA, mingo-H+wXaHxf7aLQT0dZR+AlfA, tglx-hfZtesqFncYOwBW4kG4KsQ, vgoyal-H+wXaHxf7aLQT0dZR+AlfA, x86-DgEjT+Ai2ygdnm+yROfE0A, kexec-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, linux-kernel-u79uwXL29TY76Z2rM5mHXA, virtualization-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA, xen-devel-GuqFBffKawuULHF6PoxzQEEOCMrvLtNR Cc: Daniel Kiper Introduce architecture dependent constants, structures and functions required by Xen kexec/kdump implementation. Signed-off-by: Daniel Kiper <daniel.kiper-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org> --- arch/x86/include/asm/xen/hypercall.h | 6 +++ arch/x86/include/asm/xen/kexec.h | 79 ++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 0 deletions(-) create mode 100644 arch/x86/include/asm/xen/kexec.h diff --git a/arch/x86/include/asm/xen/hypercall.h b/arch/x86/include/asm/xen/hypercall.h index c20d1ce..e76a1b8 100644 --- a/arch/x86/include/asm/xen/hypercall.h +++ b/arch/x86/include/asm/xen/hypercall.h @@ -459,6 +459,12 @@ HYPERVISOR_hvm_op(int op, void *arg) } static inline int +HYPERVISOR_kexec_op(unsigned long op, void *args) +{ + return _hypercall2(int, kexec_op, op, args); +} + +static inline int HYPERVISOR_tmem_op( struct tmem_op *op) { diff --git a/arch/x86/include/asm/xen/kexec.h b/arch/x86/include/asm/xen/kexec.h new file mode 100644 index 0000000..d09b52f --- /dev/null +++ b/arch/x86/include/asm/xen/kexec.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2011 Daniel Kiper + * Copyright (c) 2012 Daniel Kiper, Oracle Corporation + * + * kexec/kdump implementation for Xen was written by Daniel Kiper. + * Initial work on it was sponsored by Google under Google Summer + * of Code 2011 program and Citrix. Konrad Rzeszutek Wilk from Oracle + * was the mentor for this project. + * + * Some ideas are taken from: + * - native kexec/kdump implementation, + * - kexec/kdump implementation for Xen Linux Kernel Ver. 2.6.18, + * - PV-GRUB. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _ASM_X86_XEN_KEXEC_H +#define _ASM_X86_XEN_KEXEC_H + +#define KEXEC_XEN_NO_PAGES 17 + +#define XK_MA_CONTROL_PAGE 0 +#define XK_VA_CONTROL_PAGE 1 +#define XK_MA_PGD_PAGE 2 +#define XK_VA_PGD_PAGE 3 +#define XK_MA_PUD0_PAGE 4 +#define XK_VA_PUD0_PAGE 5 +#define XK_MA_PUD1_PAGE 6 +#define XK_VA_PUD1_PAGE 7 +#define XK_MA_PMD0_PAGE 8 +#define XK_VA_PMD0_PAGE 9 +#define XK_MA_PMD1_PAGE 10 +#define XK_VA_PMD1_PAGE 11 +#define XK_MA_PTE0_PAGE 12 +#define XK_VA_PTE0_PAGE 13 +#define XK_MA_PTE1_PAGE 14 +#define XK_VA_PTE1_PAGE 15 +#define XK_MA_TABLE_PAGE 16 + +#ifndef __ASSEMBLY__ +struct xen_kexec_image { + unsigned long page_list[KEXEC_XEN_NO_PAGES]; + unsigned long indirection_page; + unsigned long start_address; +}; + +struct xen_kexec_load { + int type; + struct xen_kexec_image image; +}; + +extern unsigned int xen_kexec_control_code_size; + +#ifdef CONFIG_X86_32 +extern void xen_relocate_kernel(unsigned long indirection_page, + unsigned long *page_list, + unsigned long start_address, + unsigned int has_pae, + unsigned int preserve_context); +#else +extern void xen_relocate_kernel(unsigned long indirection_page, + unsigned long *page_list, + unsigned long start_address, + unsigned int preserve_context); +#endif +#endif +#endif /* _ASM_X86_XEN_KEXEC_H */ -- 1.5.6.5 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 05/11] x86/xen: Register resources required by kexec-tools @ 2012-12-27 2:18 ` Daniel Kiper [not found] ` <1356574740-6806-6-git-send-email-daniel.kiper-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org> 0 siblings, 1 reply; 6+ messages in thread From: Daniel Kiper @ 2012-12-27 2:18 UTC (permalink / raw) To: andrew.cooper3-Sxgqhf6Nn4DQT0dZR+AlfA, ebiederm-aS9lmoZGLiVWk0Htik3J/w, hpa-YMNOUZJC4hwAvxtiuMwx3w, jbeulich-IBi9RG/b67k, konrad.wilk-QHcLZuEGTsvQT0dZR+AlfA, maxim.uvarov-QHcLZuEGTsvQT0dZR+AlfA, mingo-H+wXaHxf7aLQT0dZR+AlfA, tglx-hfZtesqFncYOwBW4kG4KsQ, vgoyal-H+wXaHxf7aLQT0dZR+AlfA, x86-DgEjT+Ai2ygdnm+yROfE0A, kexec-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, linux-kernel-u79uwXL29TY76Z2rM5mHXA, virtualization-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA, xen-devel-GuqFBffKawuULHF6PoxzQEEOCMrvLtNR Cc: Daniel Kiper Register resources required by kexec-tools. v2 - suggestions/fixes: - change logging level (suggested by Konrad Rzeszutek Wilk). Signed-off-by: Daniel Kiper <daniel.kiper-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org> --- arch/x86/xen/kexec.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 150 insertions(+), 0 deletions(-) create mode 100644 arch/x86/xen/kexec.c diff --git a/arch/x86/xen/kexec.c b/arch/x86/xen/kexec.c new file mode 100644 index 0000000..7ec4c45 --- /dev/null +++ b/arch/x86/xen/kexec.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2011 Daniel Kiper + * Copyright (c) 2012 Daniel Kiper, Oracle Corporation + * + * kexec/kdump implementation for Xen was written by Daniel Kiper. + * Initial work on it was sponsored by Google under Google Summer + * of Code 2011 program and Citrix. Konrad Rzeszutek Wilk from Oracle + * was the mentor for this project. + * + * Some ideas are taken from: + * - native kexec/kdump implementation, + * - kexec/kdump implementation for Xen Linux Kernel Ver. 2.6.18, + * - PV-GRUB. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/kexec.h> +#include <linux/slab.h> +#include <linux/string.h> + +#include <xen/interface/platform.h> +#include <xen/interface/xen.h> +#include <xen/xen.h> + +#include <asm/xen/hypercall.h> + +unsigned long xen_vmcoreinfo_maddr = 0; +unsigned long xen_vmcoreinfo_max_size = 0; + +static int __init xen_init_kexec_resources(void) +{ + int rc; + static struct resource xen_hypervisor_res = { + .name = "Hypervisor code and data", + .flags = IORESOURCE_BUSY | IORESOURCE_MEM + }; + struct resource *cpu_res; + struct xen_kexec_range xkr; + struct xen_platform_op cpuinfo_op; + uint32_t cpus, i; + + if (!xen_initial_domain()) + return 0; + + if (strstr(boot_command_line, "crashkernel=")) + pr_warn("kexec: Ignoring crashkernel option. " + "It should be passed to Xen hypervisor.\n"); + + /* Register Crash kernel resource. */ + xkr.range = KEXEC_RANGE_MA_CRASH; + rc = HYPERVISOR_kexec_op(KEXEC_CMD_kexec_get_range, &xkr); + + if (rc) { + pr_warn("kexec: %s: HYPERVISOR_kexec_op(KEXEC_RANGE_MA_CRASH)" + ": %i\n", __func__, rc); + return rc; + } + + if (!xkr.size) + return 0; + + crashk_res.start = xkr.start; + crashk_res.end = xkr.start + xkr.size - 1; + insert_resource(&iomem_resource, &crashk_res); + + /* Register Hypervisor code and data resource. */ + xkr.range = KEXEC_RANGE_MA_XEN; + rc = HYPERVISOR_kexec_op(KEXEC_CMD_kexec_get_range, &xkr); + + if (rc) { + pr_warn("kexec: %s: HYPERVISOR_kexec_op(KEXEC_RANGE_MA_XEN)" + ": %i\n", __func__, rc); + return rc; + } + + xen_hypervisor_res.start = xkr.start; + xen_hypervisor_res.end = xkr.start + xkr.size - 1; + insert_resource(&iomem_resource, &xen_hypervisor_res); + + /* Determine maximum number of physical CPUs. */ + cpuinfo_op.cmd = XENPF_get_cpuinfo; + cpuinfo_op.u.pcpu_info.xen_cpuid = 0; + rc = HYPERVISOR_dom0_op(&cpuinfo_op); + + if (rc) { + pr_warn("kexec: %s: HYPERVISOR_dom0_op(): %i\n", __func__, rc); + return rc; + } + + cpus = cpuinfo_op.u.pcpu_info.max_present + 1; + + /* Register CPUs Crash note resources. */ + cpu_res = kcalloc(cpus, sizeof(struct resource), GFP_KERNEL); + + if (!cpu_res) { + pr_warn("kexec: %s: kcalloc(): %i\n", __func__, -ENOMEM); + return -ENOMEM; + } + + for (i = 0; i < cpus; ++i) { + xkr.range = KEXEC_RANGE_MA_CPU; + xkr.nr = i; + rc = HYPERVISOR_kexec_op(KEXEC_CMD_kexec_get_range, &xkr); + + if (rc) { + pr_warn("kexec: %s: cpu: %u: HYPERVISOR_kexec_op" + "(KEXEC_RANGE_MA_XEN): %i\n", __func__, i, rc); + continue; + } + + cpu_res->name = "Crash note"; + cpu_res->start = xkr.start; + cpu_res->end = xkr.start + xkr.size - 1; + cpu_res->flags = IORESOURCE_BUSY | IORESOURCE_MEM; + insert_resource(&iomem_resource, cpu_res++); + } + + /* Get vmcoreinfo address and maximum allowed size. */ + xkr.range = KEXEC_RANGE_MA_VMCOREINFO; + rc = HYPERVISOR_kexec_op(KEXEC_CMD_kexec_get_range, &xkr); + + if (rc) { + pr_warn("kexec: %s: HYPERVISOR_kexec_op(KEXEC_RANGE_MA_VMCOREINFO)" + ": %i\n", __func__, rc); + return rc; + } + + xen_vmcoreinfo_maddr = xkr.start; + xen_vmcoreinfo_max_size = xkr.size; + + return 0; +} + +core_initcall(xen_init_kexec_resources); -- 1.5.6.5 ^ permalink raw reply related [flat|nested] 6+ messages in thread
[parent not found: <1356574740-6806-6-git-send-email-daniel.kiper-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org>]
* [PATCH v3 06/11] x86/xen: Add i386 kexec/kdump implementation [not found] ` <1356574740-6806-6-git-send-email-daniel.kiper-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org> @ 2012-12-27 2:18 ` Daniel Kiper 2012-12-27 4:00 ` H. Peter Anvin 0 siblings, 1 reply; 6+ messages in thread From: Daniel Kiper @ 2012-12-27 2:18 UTC (permalink / raw) To: andrew.cooper3-Sxgqhf6Nn4DQT0dZR+AlfA, ebiederm-aS9lmoZGLiVWk0Htik3J/w, hpa-YMNOUZJC4hwAvxtiuMwx3w, jbeulich-IBi9RG/b67k, konrad.wilk-QHcLZuEGTsvQT0dZR+AlfA, maxim.uvarov-QHcLZuEGTsvQT0dZR+AlfA, mingo-H+wXaHxf7aLQT0dZR+AlfA, tglx-hfZtesqFncYOwBW4kG4KsQ, vgoyal-H+wXaHxf7aLQT0dZR+AlfA, x86-DgEjT+Ai2ygdnm+yROfE0A, kexec-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, linux-kernel-u79uwXL29TY76Z2rM5mHXA, virtualization-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA, xen-devel-GuqFBffKawuULHF6PoxzQEEOCMrvLtNR Cc: Daniel Kiper Add i386 kexec/kdump implementation. v2 - suggestions/fixes: - allocate transition page table pages below 4 GiB (suggested by Jan Beulich). Signed-off-by: Daniel Kiper <daniel.kiper-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org> --- arch/x86/xen/machine_kexec_32.c | 226 ++++++++++++++++++++++++++ arch/x86/xen/relocate_kernel_32.S | 323 +++++++++++++++++++++++++++++++++++++ 2 files changed, 549 insertions(+), 0 deletions(-) create mode 100644 arch/x86/xen/machine_kexec_32.c create mode 100644 arch/x86/xen/relocate_kernel_32.S diff --git a/arch/x86/xen/machine_kexec_32.c b/arch/x86/xen/machine_kexec_32.c new file mode 100644 index 0000000..011a5e8 --- /dev/null +++ b/arch/x86/xen/machine_kexec_32.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2011 Daniel Kiper + * Copyright (c) 2012 Daniel Kiper, Oracle Corporation + * + * kexec/kdump implementation for Xen was written by Daniel Kiper. + * Initial work on it was sponsored by Google under Google Summer + * of Code 2011 program and Citrix. Konrad Rzeszutek Wilk from Oracle + * was the mentor for this project. + * + * Some ideas are taken from: + * - native kexec/kdump implementation, + * - kexec/kdump implementation for Xen Linux Kernel Ver. 2.6.18, + * - PV-GRUB. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/kexec.h> +#include <linux/mm.h> +#include <linux/string.h> + +#include <xen/xen.h> +#include <xen/xen-ops.h> + +#include <asm/xen/hypercall.h> +#include <asm/xen/kexec.h> +#include <asm/xen/page.h> + +#define __ma(vaddr) (virt_to_machine(vaddr).maddr) + +static void *alloc_pgtable_page(struct kimage *image) +{ + struct page *page; + + page = firmware_kimage_alloc_control_pages(image, 0); + + if (!page || !page_address(page)) + return NULL; + + memset(page_address(page), 0, PAGE_SIZE); + + return page_address(page); +} + +static int alloc_transition_pgtable(struct kimage *image) +{ + image->arch.pgd = alloc_pgtable_page(image); + + if (!image->arch.pgd) + return -ENOMEM; + + image->arch.pmd0 = alloc_pgtable_page(image); + + if (!image->arch.pmd0) + return -ENOMEM; + + image->arch.pmd1 = alloc_pgtable_page(image); + + if (!image->arch.pmd1) + return -ENOMEM; + + image->arch.pte0 = alloc_pgtable_page(image); + + if (!image->arch.pte0) + return -ENOMEM; + + image->arch.pte1 = alloc_pgtable_page(image); + + if (!image->arch.pte1) + return -ENOMEM; + + return 0; +} + +struct page *mf_kexec_kimage_alloc_pages(gfp_t gfp_mask, + unsigned int order, + unsigned long limit) +{ + struct page *pages; + unsigned int address_bits, i; + + pages = alloc_pages(gfp_mask, order); + + if (!pages) + return NULL; + + address_bits = (limit == ULONG_MAX) ? BITS_PER_LONG : ilog2(limit); + + /* Relocate set of pages below given limit. */ + if (xen_create_contiguous_region((unsigned long)page_address(pages), + order, address_bits)) { + __free_pages(pages, order); + return NULL; + } + + BUG_ON(PagePrivate(pages)); + + pages->mapping = NULL; + set_page_private(pages, order); + + for (i = 0; i < (1 << order); ++i) + SetPageReserved(pages + i); + + return pages; +} + +void mf_kexec_kimage_free_pages(struct page *page) +{ + unsigned int i, order; + + order = page_private(page); + + for (i = 0; i < (1 << order); ++i) + ClearPageReserved(page + i); + + xen_destroy_contiguous_region((unsigned long)page_address(page), order); + __free_pages(page, order); +} + +unsigned long mf_kexec_page_to_pfn(struct page *page) +{ + return pfn_to_mfn(page_to_pfn(page)); +} + +struct page *mf_kexec_pfn_to_page(unsigned long mfn) +{ + return pfn_to_page(mfn_to_pfn(mfn)); +} + +unsigned long mf_kexec_virt_to_phys(volatile void *address) +{ + return virt_to_machine(address).maddr; +} + +void *mf_kexec_phys_to_virt(unsigned long address) +{ + return phys_to_virt(machine_to_phys(XMADDR(address)).paddr); +} + +int mf_kexec_prepare(struct kimage *image) +{ +#ifdef CONFIG_KEXEC_JUMP + if (image->preserve_context) { + pr_info_once("kexec: Context preservation is not " + "supported in Xen domains.\n"); + return -ENOSYS; + } +#endif + + return alloc_transition_pgtable(image); +} + +int mf_kexec_load(struct kimage *image) +{ + void *control_page; + struct xen_kexec_load xkl = {}; + + /* Image is unloaded, nothing to do. */ + if (!image) + return 0; + + control_page = page_address(image->control_code_page); + memcpy(control_page, xen_relocate_kernel, xen_kexec_control_code_size); + + xkl.type = image->type; + xkl.image.page_list[XK_MA_CONTROL_PAGE] = __ma(control_page); + xkl.image.page_list[XK_MA_TABLE_PAGE] = 0; /* Unused. */ + xkl.image.page_list[XK_MA_PGD_PAGE] = __ma(image->arch.pgd); + xkl.image.page_list[XK_MA_PUD0_PAGE] = 0; /* Unused. */ + xkl.image.page_list[XK_MA_PUD1_PAGE] = 0; /* Unused. */ + xkl.image.page_list[XK_MA_PMD0_PAGE] = __ma(image->arch.pmd0); + xkl.image.page_list[XK_MA_PMD1_PAGE] = __ma(image->arch.pmd1); + xkl.image.page_list[XK_MA_PTE0_PAGE] = __ma(image->arch.pte0); + xkl.image.page_list[XK_MA_PTE1_PAGE] = __ma(image->arch.pte1); + xkl.image.indirection_page = image->head; + xkl.image.start_address = image->start; + + return HYPERVISOR_kexec_op(KEXEC_CMD_kexec_load, &xkl); +} + +void mf_kexec_cleanup(struct kimage *image) +{ +} + +void mf_kexec_unload(struct kimage *image) +{ + int rc; + struct xen_kexec_load xkl = {}; + + if (!image) + return; + + xkl.type = image->type; + rc = HYPERVISOR_kexec_op(KEXEC_CMD_kexec_unload, &xkl); + + WARN(rc, "kexec: %s: HYPERVISOR_kexec_op(): %i\n", __func__, rc); +} + +void mf_kexec_shutdown(void) +{ +} + +void mf_kexec(struct kimage *image) +{ + int rc; + struct xen_kexec_exec xke = {}; + + xke.type = image->type; + rc = HYPERVISOR_kexec_op(KEXEC_CMD_kexec, &xke); + + pr_emerg("kexec: %s: HYPERVISOR_kexec_op(): %i\n", __func__, rc); + BUG(); +} diff --git a/arch/x86/xen/relocate_kernel_32.S b/arch/x86/xen/relocate_kernel_32.S new file mode 100644 index 0000000..0e81830 --- /dev/null +++ b/arch/x86/xen/relocate_kernel_32.S @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2002-2005 Eric Biederman <ebiederm-aS9lmoZGLiVWk0Htik3J/w@public.gmane.org> + * Copyright (c) 2011 Daniel Kiper + * Copyright (c) 2012 Daniel Kiper, Oracle Corporation + * + * kexec/kdump implementation for Xen was written by Daniel Kiper. + * Initial work on it was sponsored by Google under Google Summer + * of Code 2011 program and Citrix. Konrad Rzeszutek Wilk from Oracle + * was the mentor for this project. + * + * Some ideas are taken from: + * - native kexec/kdump implementation, + * - kexec/kdump implementation for Xen Linux Kernel Ver. 2.6.18, + * - PV-GRUB. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either veesion 2 of the License, or + * (at your option) any later veesion. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <asm/cache.h> +#include <asm/page_types.h> +#include <asm/pgtable_types.h> +#include <asm/processor-flags.h> + +#include <asm/xen/kexec.h> + +#define ARG_INDIRECTION_PAGE 0x4 +#define ARG_PAGE_LIST 0x8 +#define ARG_START_ADDRESS 0xc + +#define PTR(x) (x << 2) + + .text + .align PAGE_SIZE + .globl xen_kexec_control_code_size, xen_relocate_kernel + +xen_relocate_kernel: + /* + * Must be relocatable PIC code callable as a C function. + * + * This function is called by Xen but here hypervisor is dead. + * We are playing on bare metal. + * + * Every machine address passed to this function through + * page_list (e.g. XK_MA_CONTROL_PAGE) is established + * by dom0 during kexec load phase. + * + * Every virtual address passed to this function through page_list + * (e.g. XK_VA_CONTROL_PAGE) is established by hypervisor during + * HYPERVISOR_kexec_op(KEXEC_CMD_kexec_load) hypercall. + * + * 0x4(%esp) - indirection_page, + * 0x8(%esp) - page_list, + * 0xc(%esp) - start_address, + * 0x10(%esp) - cpu_has_pae (ignored), + * 0x14(%esp) - preserve_context (ignored). + */ + + /* Zero out flags, and disable interrupts. */ + pushl $0 + popfl + + /* Get page_list address. */ + movl ARG_PAGE_LIST(%esp), %esi + + /* + * Map the control page at its virtual address + * in transition page table. + */ + movl PTR(XK_VA_CONTROL_PAGE)(%esi), %eax + + /* Get PGD address and PGD entry index. */ + movl PTR(XK_VA_PGD_PAGE)(%esi), %ebx + movl %eax, %ecx + shrl $PGDIR_SHIFT, %ecx + andl $(PTRS_PER_PGD - 1), %ecx + + /* Fill PGD entry with PMD0 reference. */ + movl PTR(XK_MA_PMD0_PAGE)(%esi), %edx + orl $_PAGE_PRESENT, %edx + movl %edx, (%ebx, %ecx, 8) + + /* Get PMD0 address and PMD0 entry index. */ + movl PTR(XK_VA_PMD0_PAGE)(%esi), %ebx + movl %eax, %ecx + shrl $PMD_SHIFT, %ecx + andl $(PTRS_PER_PMD - 1), %ecx + + /* Fill PMD0 entry with PTE0 reference. */ + movl PTR(XK_MA_PTE0_PAGE)(%esi), %edx + orl $_KERNPG_TABLE, %edx + movl %edx, (%ebx, %ecx, 8) + + /* Get PTE0 address and PTE0 entry index. */ + movl PTR(XK_VA_PTE0_PAGE)(%esi), %ebx + movl %eax, %ecx + shrl $PAGE_SHIFT, %ecx + andl $(PTRS_PER_PTE - 1), %ecx + + /* Fill PTE0 entry with control page reference. */ + movl PTR(XK_MA_CONTROL_PAGE)(%esi), %edx + orl $__PAGE_KERNEL_EXEC, %edx + movl %edx, (%ebx, %ecx, 8) + + /* + * Identity map the control page at its machine address + * in transition page table. + */ + movl PTR(XK_MA_CONTROL_PAGE)(%esi), %eax + + /* Get PGD address and PGD entry index. */ + movl PTR(XK_VA_PGD_PAGE)(%esi), %ebx + movl %eax, %ecx + shrl $PGDIR_SHIFT, %ecx + andl $(PTRS_PER_PGD - 1), %ecx + + /* Fill PGD entry with PMD1 reference. */ + movl PTR(XK_MA_PMD1_PAGE)(%esi), %edx + orl $_PAGE_PRESENT, %edx + movl %edx, (%ebx, %ecx, 8) + + /* Get PMD1 address and PMD1 entry index. */ + movl PTR(XK_VA_PMD1_PAGE)(%esi), %ebx + movl %eax, %ecx + shrl $PMD_SHIFT, %ecx + andl $(PTRS_PER_PMD - 1), %ecx + + /* Fill PMD1 entry with PTE1 reference. */ + movl PTR(XK_MA_PTE1_PAGE)(%esi), %edx + orl $_KERNPG_TABLE, %edx + movl %edx, (%ebx, %ecx, 8) + + /* Get PTE1 address and PTE1 entry index. */ + movl PTR(XK_VA_PTE1_PAGE)(%esi), %ebx + movl %eax, %ecx + shrl $PAGE_SHIFT, %ecx + andl $(PTRS_PER_PTE - 1), %ecx + + /* Fill PTE1 entry with control page reference. */ + movl PTR(XK_MA_CONTROL_PAGE)(%esi), %edx + orl $__PAGE_KERNEL_EXEC, %edx + movl %edx, (%ebx, %ecx, 8) + + /* + * Get machine address of control page now. + * This is impossible after page table switch. + */ + movl PTR(XK_MA_CONTROL_PAGE)(%esi), %ebx + + /* Get machine address of transition page table now too. */ + movl PTR(XK_MA_PGD_PAGE)(%esi), %ecx + + /* Get start_address too. */ + movl ARG_START_ADDRESS(%esp), %edx + + /* Get indirection_page address too. */ + movl ARG_INDIRECTION_PAGE(%esp), %edi + + /* Switch to transition page table. */ + movl %ecx, %cr3 + + /* Load IDT. */ + lidtl (idt_48 - xen_relocate_kernel)(%ebx) + + /* Load GDT. */ + leal (gdt - xen_relocate_kernel)(%ebx), %eax + movl %eax, (gdt_48 - xen_relocate_kernel + 2)(%ebx) + lgdtl (gdt_48 - xen_relocate_kernel)(%ebx) + + /* Load data segment registers. */ + movl $(gdt_ds - gdt), %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %fs + movl %eax, %gs + movl %eax, %ss + + /* Setup a new stack at the end of machine address of control page. */ + leal PAGE_SIZE(%ebx), %esp + + /* Store start_address on the stack. */ + pushl %edx + + /* Jump to identity mapped page. */ + pushl $0 + pushl $(gdt_cs - gdt) + addl $(identity_mapped - xen_relocate_kernel), %ebx + pushl %ebx + iretl + +identity_mapped: + /* + * Set %cr0 to a known state: + * - disable alignment check, + * - disable floating point emulation, + * - disable paging, + * - no task switch, + * - disable write protect, + * - enable protected mode. + */ + movl %cr0, %eax + andl $~(X86_CR0_AM | X86_CR0_EM | X86_CR0_PG | X86_CR0_TS | X86_CR0_WP), %eax + orl $(X86_CR0_PE), %eax + movl %eax, %cr0 + + /* Set %cr4 to a known state. */ + xorl %eax, %eax + movl %eax, %cr4 + + jmp 1f + +1: + /* Flush the TLB (needed?). */ + movl %eax, %cr3 + + /* Do the copies. */ + movl %edi, %ecx /* Put the indirection_page in %ecx. */ + xorl %edi, %edi + xorl %esi, %esi + jmp 1f + +0: + /* + * Top, read another doubleword from the indirection page. + * Indirection page is an array which contains source + * and destination address pairs. If all pairs could + * not fit in one page then at the end of given + * indirection page is pointer to next one. + * Copy is stopped when done indicator + * is found in indirection page. + */ + movl (%ebx), %ecx + addl $4, %ebx + +1: + testl $0x1, %ecx /* Is it a destination page? */ + jz 2f + + movl %ecx, %edi + andl $PAGE_MASK, %edi + jmp 0b + +2: + testl $0x2, %ecx /* Is it an indirection page? */ + jz 2f + + movl %ecx, %ebx + andl $PAGE_MASK, %ebx + jmp 0b + +2: + testl $0x4, %ecx /* Is it the done indicator? */ + jz 2f + jmp 3f + +2: + testl $0x8, %ecx /* Is it the source indicator? */ + jz 0b /* Ignore it otherwise. */ + + movl %ecx, %esi + andl $PAGE_MASK, %esi + movl $1024, %ecx + + /* Copy page. */ + rep movsl + jmp 0b + +3: + /* + * To be certain of avoiding problems with self-modifying code + * I need to execute a serializing instruction here. + * So I flush the TLB by reloading %cr3 here, it's handy, + * and not processor dependent. + */ + xorl %eax, %eax + movl %eax, %cr3 + + /* + * Set all of the registers to known values. + * Leave %esp alone. + */ + xorl %ebx, %ebx + xorl %ecx, %ecx + xorl %edx, %edx + xorl %esi, %esi + xorl %edi, %edi + xorl %ebp, %ebp + + /* Jump to start_address. */ + retl + + .align L1_CACHE_BYTES + +gdt: + .quad 0x0000000000000000 /* NULL descriptor. */ + +gdt_cs: + .quad 0x00cf9a000000ffff /* 4 GiB code segment at 0x00000000. */ + +gdt_ds: + .quad 0x00cf92000000ffff /* 4 GiB data segment at 0x00000000. */ +gdt_end: + +gdt_48: + .word gdt_end - gdt - 1 /* GDT limit. */ + .long 0 /* GDT base - filled in by code above. */ + +idt_48: + .word 0 /* IDT limit. */ + .long 0 /* IDT base. */ + +xen_kexec_control_code_size: + .long . - xen_relocate_kernel -- 1.5.6.5 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v3 06/11] x86/xen: Add i386 kexec/kdump implementation 2012-12-27 2:18 ` [PATCH v3 06/11] x86/xen: Add i386 kexec/kdump implementation Daniel Kiper @ 2012-12-27 4:00 ` H. Peter Anvin 0 siblings, 0 replies; 6+ messages in thread From: H. Peter Anvin @ 2012-12-27 4:00 UTC (permalink / raw) To: Daniel Kiper Cc: xen-devel, konrad.wilk, andrew.cooper3, x86, kexec, linux-kernel, virtualization, mingo, ebiederm, jbeulich, maxim.uvarov, tglx, vgoyal On 12/26/2012 06:18 PM, Daniel Kiper wrote: > Add i386 kexec/kdump implementation. > > v2 - suggestions/fixes: > - allocate transition page table pages below 4 GiB > (suggested by Jan Beulich). > Why? -hpa -- H. Peter Anvin, Intel Open Source Technology Center I work for Intel. I don't speak on their behalf. ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2013-01-02 14:43 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-12-27 23:23 [PATCH v3 06/11] x86/xen: Add i386 kexec/kdump implementation Daniel Kiper 2012-12-27 23:37 ` H. Peter Anvin 2012-12-28 3:16 ` Eric W. Biederman 2013-01-02 14:43 ` [Xen-devel] " Ian Campbell -- strict thread matches above, loose matches on Subject: below -- 2012-12-27 2:18 [PATCH v3 00/11] xen: Initial " Daniel Kiper 2012-12-27 2:18 ` [PATCH v3 01/11] kexec: introduce kexec firmware support Daniel Kiper 2012-12-27 2:18 ` [PATCH v3 02/11] x86/kexec: Add extra pointers to transition page table PGD, PUD, PMD and PTE Daniel Kiper 2012-12-27 2:18 ` [PATCH v3 03/11] xen: Introduce architecture independent data for kexec/kdump Daniel Kiper 2012-12-27 2:18 ` [PATCH v3 04/11] x86/xen: Introduce architecture dependent " Daniel Kiper 2012-12-27 2:18 ` [PATCH v3 05/11] x86/xen: Register resources required by kexec-tools Daniel Kiper [not found] ` <1356574740-6806-6-git-send-email-daniel.kiper-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org> 2012-12-27 2:18 ` [PATCH v3 06/11] x86/xen: Add i386 kexec/kdump implementation Daniel Kiper 2012-12-27 4:00 ` H. Peter Anvin
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).