Index: linux/arch/x86_64/kernel/machine_kexec.c =================================================================== --- linux.orig/arch/x86_64/kernel/machine_kexec.c +++ linux/arch/x86_64/kernel/machine_kexec.c @@ -8,43 +8,27 @@ #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) +#include -static void init_level2_page(u64 *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; + end_addr = addr + PUD_SIZE; while (addr < end_addr) { - *(level2p++) = addr | L1_ATTR; - addr += LEVEL1_SIZE; + set_pmd(level2p, __pmd(__pa(addr) | __PAGE_KERNEL_LARGE_EXEC)); + level2p++; + addr += PMD_SIZE; } } -static int init_level3_page(struct kimage *image, u64 *level3p, +static int init_level3_page(struct kimage *image, pud_t *level3p, unsigned long addr, unsigned long last_addr) { unsigned long end_addr; @@ -52,115 +36,98 @@ static int init_level3_page(struct kimag result = 0; addr &= PAGE_MASK; - end_addr = addr + LEVEL3_SIZE; + end_addr = addr + PGDIR_SIZE; while ((addr < last_addr) && (addr < end_addr)) { struct page *page; - u64 *level2p; + pmd_t *level2p; page = kimage_alloc_control_pages(image, 0); if (!page) { result = -ENOMEM; goto out; } - level2p = (u64 *)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)); + level3p++; + addr += PUD_SIZE; } /* clear the unused entries */ while (addr < end_addr) { - *(level3p++) = 0; - addr += LEVEL2_SIZE; + pud_clear(level3p); + level3p++; + addr += PUD_SIZE; } out: return result; } -static int init_level4_page(struct kimage *image, u64 *level4p, +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)) { + while (pgd_index(addr) < PTRS_PER_PGD && (addr < last_addr)) { struct page *page; - u64 *level3p; + pud_t *level3p; page = kimage_alloc_control_pages(image, 0); if (!page) { result = -ENOMEM; goto out; } - level3p = (u64 *)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)); + level3p++; + addr += PGDIR_SIZE; } /* clear the unused entries */ - while (addr < end_addr) { - *(level4p++) = 0; - addr += LEVEL3_SIZE; + while (pgd_index(addr) < PTRS_PER_PGD) { + pgd_clear(level4p); + level4p++; + addr += PGDIR_SIZE; } out: return result; } - static int init_pgtable(struct kimage *image, unsigned long start_pgtable) { - u64 *level4p; - level4p = (u64 *)__va(start_pgtable); - return init_level4_page(image, level4p, 0, end_pfn << PAGE_SHIFT); + return init_level4_page(image, (pgd_t *) __va(start_pgtable), + 0, end_pfn << PAGE_SHIFT); } static void set_idt(void *newidt, u16 limit) { - unsigned char curidt[10]; + struct desc_ptr curidt = { .size = limit, .address = (unsigned long)newidt }; - /* x86-64 supports unaliged loads & stores */ - (*(u16 *)(curidt)) = limit; - (*(u64 *)(curidt +2)) = (unsigned long)(newidt); - - __asm__ __volatile__ ( - "lidt %0\n" - : "=m" (curidt) - ); -}; + asm volatile("lidt %0\n" :: "m" (curidt)); +} static void set_gdt(void *newgdt, u16 limit) { - unsigned char curgdt[10]; + struct desc_ptr curgdt = { .size = limit, .address = (unsigned long)newgdt }; - /* x86-64 supports unaligned loads & stores */ - (*(u16 *)(curgdt)) = limit; - (*(u64 *)(curgdt +2)) = (unsigned long)(newgdt); - - __asm__ __volatile__ ( - "lgdt %0\n" - : "=m" (curgdt) - ); -}; + asm volatile( "lgdt %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" - ); -#undef STR -#undef __STR + "\tmovl %0,%%ds\n" + "\tmovl %0,%%es\n" + "\tmovl %0,%%ss\n" + "\tmovl %0,%%fs\n" + "\tmovl %0,%%gs\n" + :: "a" (__KERNEL_DS)); } typedef NORET_TYPE void (*relocate_new_kernel_t)(unsigned long indirection_page, @@ -178,9 +145,11 @@ int machine_kexec_prepare(struct kimage /* Calculate the offsets */ start_pgtable = page_to_pfn(image->control_code_page) << PAGE_SHIFT; - control_code_buffer = start_pgtable + 4096UL; + control_code_buffer = start_pgtable + PAGE_SIZE; /* Setup the identity mapped 64bit page table */ + /* AK: this will probably create cache aliases from other mappings + and is thus broken. */ result = init_pgtable(image, start_pgtable); if (result) return result; @@ -194,7 +163,6 @@ int machine_kexec_prepare(struct kimage void machine_kexec_cleanup(struct kimage *image) { - return; } /* @@ -214,7 +182,7 @@ NORET_TYPE void machine_kexec(struct kim /* Calculate the offsets */ page_list = image->head; start_pgtable = page_to_pfn(image->control_code_page) << PAGE_SHIFT; - control_code_buffer = start_pgtable + 4096UL; + 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 Index: linux/include/asm-x86_64/pgtable.h =================================================================== --- linux.orig/include/asm-x86_64/pgtable.h +++ linux/include/asm-x86_64/pgtable.h @@ -174,8 +174,8 @@ extern inline void pgd_clear (pgd_t * pg (_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED) #define __PAGE_KERNEL_VSYSCALL_NOCACHE \ (_PAGE_PRESENT | _PAGE_USER | _PAGE_ACCESSED | _PAGE_PCD) -#define __PAGE_KERNEL_LARGE \ - (__PAGE_KERNEL | _PAGE_PSE) +#define __PAGE_KERNEL_LARGE (__PAGE_KERNEL | _PAGE_PSE) +#define __PAGE_KERNEL_LARGE_EXEC (__PAGE_KERNEL_EXEC | _PAGE_PSE) #define MAKE_GLOBAL(x) __pgprot((x) | _PAGE_GLOBAL)