X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fx86_64%2Fkernel%2Fmachine_kexec.c;fp=arch%2Fx86_64%2Fkernel%2Fmachine_kexec.c;h=25ac8a3faae635417ae7377cadf7f56ae2dec823;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=347387814268b48f05a961da15403c7ba2b3039c;hpb=cee37fe97739d85991964371c1f3a745c00dd236;p=linux-2.6.git diff --git a/arch/x86_64/kernel/machine_kexec.c b/arch/x86_64/kernel/machine_kexec.c index 347387814..25ac8a3fa 100644 --- a/arch/x86_64/kernel/machine_kexec.c +++ b/arch/x86_64/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. @@ -8,163 +8,148 @@ #include #include -#include #include #include -#include #include -#include #include #include #include -#include -#include -#include - -#define LEVEL0_SIZE (1UL << 12UL) -#define LEVEL1_SIZE (1UL << 21UL) -#define LEVEL2_SIZE (1UL << 30UL) -#define LEVEL3_SIZE (1UL << 39UL) -#define LEVEL4_SIZE (1UL << 48UL) - -#define L0_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) -#define L1_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_PSE) -#define L2_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) -#define L3_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) - -static void init_level2_page( - uint64_t *level2p, unsigned long addr) + +static void init_level2_page(pmd_t *level2p, unsigned long addr) { unsigned long end_addr; + addr &= PAGE_MASK; - end_addr = addr + LEVEL2_SIZE; - while(addr < end_addr) { - *(level2p++) = addr | L1_ATTR; - addr += LEVEL1_SIZE; + end_addr = addr + PUD_SIZE; + while (addr < end_addr) { + set_pmd(level2p++, __pmd(addr | __PAGE_KERNEL_LARGE_EXEC)); + addr += PMD_SIZE; } } -static int init_level3_page(struct kimage *image, - uint64_t *level3p, unsigned long addr, unsigned long last_addr) +static int init_level3_page(struct kimage *image, pud_t *level3p, + unsigned long addr, unsigned long last_addr) { unsigned long end_addr; int result; + result = 0; addr &= PAGE_MASK; - end_addr = addr + LEVEL3_SIZE; - while((addr < last_addr) && (addr < end_addr)) { + end_addr = addr + PGDIR_SIZE; + while ((addr < last_addr) && (addr < end_addr)) { struct page *page; - uint64_t *level2p; + pmd_t *level2p; + page = kimage_alloc_control_pages(image, 0); if (!page) { result = -ENOMEM; goto out; } - level2p = (uint64_t *)page_address(page); + level2p = (pmd_t *)page_address(page); init_level2_page(level2p, addr); - *(level3p++) = __pa(level2p) | L2_ATTR; - addr += LEVEL2_SIZE; + set_pud(level3p++, __pud(__pa(level2p) | _KERNPG_TABLE)); + addr += PUD_SIZE; } /* clear the unused entries */ - while(addr < end_addr) { - *(level3p++) = 0; - addr += LEVEL2_SIZE; + while (addr < end_addr) { + pud_clear(level3p++); + addr += PUD_SIZE; } out: return result; } -static int init_level4_page(struct kimage *image, - uint64_t *level4p, unsigned long addr, unsigned long last_addr) +static int init_level4_page(struct kimage *image, pgd_t *level4p, + unsigned long addr, unsigned long last_addr) { unsigned long end_addr; int result; + result = 0; addr &= PAGE_MASK; - end_addr = addr + LEVEL4_SIZE; - while((addr < last_addr) && (addr < end_addr)) { + end_addr = addr + (PTRS_PER_PGD * PGDIR_SIZE); + while ((addr < last_addr) && (addr < end_addr)) { struct page *page; - uint64_t *level3p; + pud_t *level3p; + page = kimage_alloc_control_pages(image, 0); if (!page) { result = -ENOMEM; goto out; } - level3p = (uint64_t *)page_address(page); + level3p = (pud_t *)page_address(page); result = init_level3_page(image, level3p, addr, last_addr); if (result) { goto out; } - *(level4p++) = __pa(level3p) | L3_ATTR; - addr += LEVEL3_SIZE; + set_pgd(level4p++, __pgd(__pa(level3p) | _KERNPG_TABLE)); + addr += PGDIR_SIZE; } /* clear the unused entries */ - while(addr < end_addr) { - *(level4p++) = 0; - addr += LEVEL3_SIZE; + while (addr < end_addr) { + pgd_clear(level4p++); + addr += PGDIR_SIZE; } - out: +out: return result; } static int init_pgtable(struct kimage *image, unsigned long start_pgtable) { - uint64_t *level4p; - level4p = (uint64_t *)__va(start_pgtable); - return init_level4_page(image, level4p, 0, end_pfn << PAGE_SHIFT); + pgd_t *level4p; + level4p = (pgd_t *)__va(start_pgtable); + return init_level4_page(image, level4p, 0, end_pfn << PAGE_SHIFT); } -static void set_idt(void *newidt, __u16 limit) +static void set_idt(void *newidt, u16 limit) { - unsigned char curidt[10]; + struct desc_ptr curidt; /* x86-64 supports unaliged loads & stores */ - (*(__u16 *)(curidt)) = limit; - (*(__u64 *)(curidt +2)) = (unsigned long)(newidt); + curidt.size = limit; + curidt.address = (unsigned long)newidt; __asm__ __volatile__ ( - "lidt %0\n" - : "=m" (curidt) + "lidtq %0\n" + : : "m" (curidt) ); }; -static void set_gdt(void *newgdt, __u16 limit) +static void set_gdt(void *newgdt, u16 limit) { - unsigned char curgdt[10]; + struct desc_ptr curgdt; /* x86-64 supports unaligned loads & stores */ - (*(__u16 *)(curgdt)) = limit; - (*(__u64 *)(curgdt +2)) = (unsigned long)(newgdt); + curgdt.size = limit; + curgdt.address = (unsigned long)newgdt; __asm__ __volatile__ ( - "lgdt %0\n" - : "=m" (curgdt) + "lgdtq %0\n" + : : "m" (curgdt) ); }; static void load_segments(void) { __asm__ __volatile__ ( - "\tmovl $"STR(__KERNEL_DS)",%eax\n" - "\tmovl %eax,%ds\n" - "\tmovl %eax,%es\n" - "\tmovl %eax,%ss\n" - "\tmovl %eax,%fs\n" - "\tmovl %eax,%gs\n" + "\tmovl %0,%%ds\n" + "\tmovl %0,%%es\n" + "\tmovl %0,%%ss\n" + "\tmovl %0,%%fs\n" + "\tmovl %0,%%gs\n" + : : "a" (__KERNEL_DS) : "memory" ); -#undef STR -#undef __STR } -typedef void (*relocate_new_kernel_t)( - unsigned long indirection_page, unsigned long control_code_buffer, - unsigned long start_address, unsigned long pgtable); +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; const extern unsigned char relocate_new_kernel[]; -extern void relocate_new_kernel_end(void); const extern unsigned long relocate_new_kernel_size; int machine_kexec_prepare(struct kimage *image) @@ -173,17 +158,17 @@ int machine_kexec_prepare(struct kimage *image) int result; /* Calculate the offsets */ - start_pgtable = page_to_pfn(image->control_code_page) << PAGE_SHIFT; - control_code_buffer = start_pgtable + 4096UL; + 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) { + if (result) return result; - } /* Place the code in the reboot code buffer */ - memcpy(__va(control_code_buffer), relocate_new_kernel, relocate_new_kernel_size); + memcpy(__va(control_code_buffer), relocate_new_kernel, + relocate_new_kernel_size); return 0; } @@ -197,9 +182,9 @@ 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 control_code_buffer; unsigned long start_pgtable; relocate_new_kernel_t rnk; @@ -208,9 +193,9 @@ void machine_kexec(struct kimage *image) local_irq_disable(); /* Calculate the offsets */ - indirection_page = image->head & PAGE_MASK; - start_pgtable = page_to_pfn(image->control_code_page) << PAGE_SHIFT; - control_code_buffer = start_pgtable + 4096UL; + page_list = image->head; + start_pgtable = page_to_pfn(image->control_code_page) << PAGE_SHIFT; + control_code_buffer = start_pgtable + PAGE_SIZE; /* Set the low half of the page table to my identity mapped * page table for kexec. Leave the high half pointing at the @@ -218,7 +203,7 @@ void machine_kexec(struct kimage *image) * as that will happen when I fully switch to my identity mapped * page table anyway. */ - memcpy((void *)read_pda(level4_pgt), __va(start_pgtable), PAGE_SIZE/2); + memcpy(__va(read_cr3()), __va(start_pgtable), PAGE_SIZE/2); __flush_tlb(); @@ -242,5 +227,5 @@ void machine_kexec(struct kimage *image) set_idt(phys_to_virt(0),0); /* now call it */ rnk = (relocate_new_kernel_t) control_code_buffer; - (*rnk)(indirection_page, control_code_buffer, image->start, start_pgtable); + (*rnk)(page_list, control_code_buffer, image->start, start_pgtable); }