X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fi386%2Fkernel%2Fmachine_kexec.c;h=f73d7374a2ba8db6cd903bc4e316df2ecdb4260b;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=3a9e878f8262938e181cef8f559df029c96b9697;hpb=cee37fe97739d85991964371c1f3a745c00dd236;p=linux-2.6.git diff --git a/arch/i386/kernel/machine_kexec.c b/arch/i386/kernel/machine_kexec.c index 3a9e878f8..f73d7374a 100644 --- a/arch/i386/kernel/machine_kexec.c +++ b/arch/i386/kernel/machine_kexec.c @@ -1,6 +1,6 @@ /* * machine_kexec.c - handle transition of Linux booting another kernel - * Copyright (C) 2002-2004 Eric Biederman + * Copyright (C) 2002-2005 Eric Biederman * * This source code is licensed under the GNU General Public License, * Version 2. See the file COPYING for more details. @@ -16,13 +16,8 @@ #include #include #include - -static inline unsigned long read_cr3(void) -{ - unsigned long cr3; - asm volatile("movl %%cr3,%0": "=r"(cr3)); - return cr3; -} +#include +#include #define PAGE_ALIGNED __attribute__ ((__aligned__(PAGE_SIZE))) @@ -80,7 +75,8 @@ static void identity_map_page(unsigned long address) /* 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); + 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. @@ -89,34 +85,27 @@ static void identity_map_page(unsigned long address) } #endif - static void set_idt(void *newidt, __u16 limit) { - unsigned char curidt[6]; + struct Xgt_desc_struct curidt; /* ia32 supports unaliged loads & stores */ - (*(__u16 *)(curidt)) = limit; - (*(__u32 *)(curidt +2)) = (unsigned long)(newidt); + curidt.size = limit; + curidt.address = (unsigned long)newidt; - __asm__ __volatile__ ( - "lidt %0\n" - : "=m" (curidt) - ); + load_idt(&curidt); }; static void set_gdt(void *newgdt, __u16 limit) { - unsigned char curgdt[6]; + struct Xgt_desc_struct curgdt; /* ia32 supports unaligned loads & stores */ - (*(__u16 *)(curgdt)) = limit; - (*(__u32 *)(curgdt +2)) = (unsigned long)(newgdt); + curgdt.size = limit; + curgdt.address = (unsigned long)newgdt; - __asm__ __volatile__ ( - "lgdt %0\n" - : "=m" (curgdt) - ); + load_gdt(&curgdt); }; static void load_segments(void) @@ -127,35 +116,49 @@ static void load_segments(void) __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" - ); + "\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 } -typedef asmlinkage void (*relocate_new_kernel_t)( - unsigned long indirection_page, unsigned long reboot_code_buffer, - unsigned long start_address, unsigned int has_pae); +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; const extern unsigned char relocate_new_kernel[]; extern void relocate_new_kernel_end(void); const extern unsigned int relocate_new_kernel_size; /* + * A architecture hook called to validate the + * proposed image and prepare the control pages + * as needed. The pages for KEXEC_CONTROL_CODE_SIZE + * have been allocated, but the segments have yet + * been copied into the kernel. + * * Do what every setup is needed on image and the * reboot code buffer to allow us to avoid allocations - * later. Currently nothing. + * later. + * + * Currently nothing. */ int machine_kexec_prepare(struct kimage *image) { return 0; } +/* + * Undo anything leftover by machine_kexec_prepare + * when an image is freed. + */ void machine_kexec_cleanup(struct kimage *image) { } @@ -164,24 +167,27 @@ void machine_kexec_cleanup(struct kimage *image) * Do not allocate memory (or fail in any way) in machine_kexec(). * We are past the point of no return, committed to rebooting now. */ -void machine_kexec(struct kimage *image) +NORET_TYPE void machine_kexec(struct kimage *image) { - unsigned long indirection_page; + unsigned long page_list; unsigned long reboot_code_buffer; + 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; - indirection_page = image->head & PAGE_MASK; + 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); + 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 @@ -204,5 +210,5 @@ void machine_kexec(struct kimage *image) /* now call it */ rnk = (relocate_new_kernel_t) reboot_code_buffer; - (*rnk)(indirection_page, reboot_code_buffer, image->start, cpu_has_pae); + (*rnk)(page_list, reboot_code_buffer, image->start, cpu_has_pae); }