mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-15 21:30:43 +00:00
[PATCH] kexec: x86 kexec core
This is the i386 implementation of kexec. Signed-off-by: Eric Biederman <ebiederm@xmission.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
dd2a13054f
commit
5033cba087
@ -953,6 +953,23 @@ config PHYSICAL_START
|
||||
|
||||
Don't change this unless you know what you are doing.
|
||||
|
||||
config KEXEC
|
||||
bool "kexec system call (EXPERIMENTAL)"
|
||||
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
|
||||
but it is indepedent of the system firmware. And like a reboot
|
||||
you can start any kernel with it, not just Linux.
|
||||
|
||||
The name comes from the similiarity to the exec system call.
|
||||
|
||||
It is an ongoing process to be certain the hardware in a machine
|
||||
is properly shutdown, so do not be surprised if this code does not
|
||||
initially work for you. It may help to enable device hotplugging
|
||||
support. As of this writing the exact hardware interface is
|
||||
strongly in flux, so no good recommendation can be made.
|
||||
|
||||
endmenu
|
||||
|
||||
|
||||
|
@ -24,6 +24,7 @@ obj-$(CONFIG_X86_MPPARSE) += mpparse.o
|
||||
obj-$(CONFIG_X86_LOCAL_APIC) += apic.o nmi.o
|
||||
obj-$(CONFIG_X86_IO_APIC) += io_apic.o
|
||||
obj-$(CONFIG_X86_REBOOTFIXUPS) += reboot_fixups.o
|
||||
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o crash.o
|
||||
obj-$(CONFIG_X86_NUMAQ) += numaq.o
|
||||
obj-$(CONFIG_X86_SUMMIT_NUMA) += summit.o
|
||||
obj-$(CONFIG_KPROBES) += kprobes.o
|
||||
|
42
arch/i386/kernel/crash.c
Normal file
42
arch/i386/kernel/crash.c
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Architecture specific (i386) functions for kexec based crash dumps.
|
||||
*
|
||||
* Created by: Hariprasad Nellitheertha (hari@in.ibm.com)
|
||||
*
|
||||
* Copyright (C) IBM Corporation, 2004. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/elf.h>
|
||||
#include <linux/elfcore.h>
|
||||
|
||||
#include <asm/processor.h>
|
||||
#include <asm/hardirq.h>
|
||||
#include <asm/nmi.h>
|
||||
#include <asm/hw_irq.h>
|
||||
|
||||
#define MAX_NOTE_BYTES 1024
|
||||
typedef u32 note_buf_t[MAX_NOTE_BYTES/4];
|
||||
|
||||
note_buf_t crash_notes[NR_CPUS];
|
||||
|
||||
void machine_crash_shutdown(void)
|
||||
{
|
||||
/* This function is only called after the system
|
||||
* has paniced or is otherwise in a critical state.
|
||||
* The minimum amount of code to allow a kexec'd kernel
|
||||
* to run successfully needs to happen here.
|
||||
*
|
||||
* In practice this means shooting down the other cpus in
|
||||
* an SMP system.
|
||||
*/
|
||||
}
|
220
arch/i386/kernel/machine_kexec.c
Normal file
220
arch/i386/kernel/machine_kexec.c
Normal file
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* machine_kexec.c - handle transition of Linux booting another kernel
|
||||
* Copyright (C) 2002-2005 Eric Biederman <ebiederm@xmission.com>
|
||||
*
|
||||
* This source code is licensed under the GNU General Public License,
|
||||
* Version 2. See the file COPYING for more details.
|
||||
*/
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/apic.h>
|
||||
#include <asm/cpufeature.h>
|
||||
|
||||
static inline unsigned long read_cr3(void)
|
||||
{
|
||||
unsigned long cr3;
|
||||
asm volatile("movl %%cr3,%0": "=r"(cr3));
|
||||
return cr3;
|
||||
}
|
||||
|
||||
#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)
|
||||
|
||||
#ifndef CONFIG_X86_PAE
|
||||
#define LEVEL1_SIZE (1UL << 22UL)
|
||||
static u32 pgtable_level1[1024] PAGE_ALIGNED;
|
||||
|
||||
static void identity_map_page(unsigned long address)
|
||||
{
|
||||
unsigned long level1_index, level2_index;
|
||||
u32 *pgtable_level2;
|
||||
|
||||
/* Find the current page table */
|
||||
pgtable_level2 = __va(read_cr3());
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
#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;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void set_idt(void *newidt, __u16 limit)
|
||||
{
|
||||
unsigned char curidt[6];
|
||||
|
||||
/* ia32 supports unaliged loads & stores */
|
||||
(*(__u16 *)(curidt)) = limit;
|
||||
(*(__u32 *)(curidt +2)) = (unsigned long)(newidt);
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"lidt %0\n"
|
||||
: "=m" (curidt)
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
static void set_gdt(void *newgdt, __u16 limit)
|
||||
{
|
||||
unsigned char curgdt[6];
|
||||
|
||||
/* ia32 supports unaligned loads & stores */
|
||||
(*(__u16 *)(curgdt)) = limit;
|
||||
(*(__u32 *)(curgdt +2)) = (unsigned long)(newgdt);
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"lgdt %0\n"
|
||||
: "=m" (curgdt)
|
||||
);
|
||||
};
|
||||
|
||||
static void load_segments(void)
|
||||
{
|
||||
#define __STR(X) #X
|
||||
#define STR(X) __STR(X)
|
||||
|
||||
__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"
|
||||
);
|
||||
#undef STR
|
||||
#undef __STR
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Do not allocate memory (or fail in any way) in machine_kexec().
|
||||
* We are past the point of no return, committed to rebooting now.
|
||||
*/
|
||||
NORET_TYPE void machine_kexec(struct kimage *image)
|
||||
{
|
||||
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;
|
||||
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);
|
||||
|
||||
/* now call it */
|
||||
rnk = (relocate_new_kernel_t) reboot_code_buffer;
|
||||
(*rnk)(page_list, reboot_code_buffer, image->start, cpu_has_pae);
|
||||
}
|
120
arch/i386/kernel/relocate_kernel.S
Normal file
120
arch/i386/kernel/relocate_kernel.S
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* relocate_kernel.S - put the kernel image in place to boot
|
||||
* Copyright (C) 2002-2004 Eric Biederman <ebiederm@xmission.com>
|
||||
*
|
||||
* This source code is licensed under the GNU General Public License,
|
||||
* Version 2. See the file COPYING for more details.
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
|
||||
/*
|
||||
* Must be relocatable PIC code callable as a C function, that once
|
||||
* it starts can not use the previous processes stack.
|
||||
*/
|
||||
.globl relocate_new_kernel
|
||||
relocate_new_kernel:
|
||||
/* read the arguments and say goodbye to the stack */
|
||||
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 */
|
||||
|
||||
/* zero out flags, and disable interrupts */
|
||||
pushl $0
|
||||
popfl
|
||||
|
||||
/* set a new stack at the bottom of our page... */
|
||||
lea 4096(%ebp), %esp
|
||||
|
||||
/* 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
|
||||
* 16 0 == Write protect disabled
|
||||
* 3 0 == No task switch
|
||||
* 2 0 == Don't do FP software emulation.
|
||||
* 0 1 == Proctected mode enabled
|
||||
*/
|
||||
movl %cr0, %eax
|
||||
andl $~((1<<31)|(1<<18)|(1<<16)|(1<<3)|(1<<2)), %eax
|
||||
orl $(1<<0), %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
/* clear cr4 if applicable */
|
||||
testl %ecx, %ecx
|
||||
jz 1f
|
||||
/* Set cr4 to a known state:
|
||||
* Setting everything to zero seems safe.
|
||||
*/
|
||||
movl %cr4, %eax
|
||||
andl $0, %eax
|
||||
movl %eax, %cr4
|
||||
|
||||
jmp 1f
|
||||
1:
|
||||
|
||||
/* Flush the TLB (needed?) */
|
||||
xorl %eax, %eax
|
||||
movl %eax, %cr3
|
||||
|
||||
/* Do the copies */
|
||||
movl %ebx, %ecx
|
||||
jmp 1f
|
||||
|
||||
0: /* top, read another word from the indirection page */
|
||||
movl (%ebx), %ecx
|
||||
addl $4, %ebx
|
||||
1:
|
||||
testl $0x1, %ecx /* is it a destination page */
|
||||
jz 2f
|
||||
movl %ecx, %edi
|
||||
andl $0xfffff000, %edi
|
||||
jmp 0b
|
||||
2:
|
||||
testl $0x2, %ecx /* is it an indirection page */
|
||||
jz 2f
|
||||
movl %ecx, %ebx
|
||||
andl $0xfffff000, %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 /* For every source page do a copy */
|
||||
andl $0xfffff000, %esi
|
||||
|
||||
movl $1024, %ecx
|
||||
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, 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 %eax, %eax
|
||||
xorl %ebx, %ebx
|
||||
xorl %ecx, %ecx
|
||||
xorl %edx, %edx
|
||||
xorl %esi, %esi
|
||||
xorl %edi, %edi
|
||||
xorl %ebp, %ebp
|
||||
ret
|
||||
relocate_new_kernel_end:
|
||||
|
||||
.globl relocate_new_kernel_size
|
||||
relocate_new_kernel_size:
|
||||
.long relocate_new_kernel_end - relocate_new_kernel
|
@ -283,7 +283,7 @@ ENTRY(sys_call_table)
|
||||
.long sys_mq_timedreceive /* 280 */
|
||||
.long sys_mq_notify
|
||||
.long sys_mq_getsetattr
|
||||
.long sys_ni_syscall /* reserved for kexec */
|
||||
.long sys_kexec_load
|
||||
.long sys_waitid
|
||||
.long sys_ni_syscall /* 285 */ /* available */
|
||||
.long sys_add_key
|
||||
|
28
include/asm-i386/kexec.h
Normal file
28
include/asm-i386/kexec.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef _I386_KEXEC_H
|
||||
#define _I386_KEXEC_H
|
||||
|
||||
#include <asm/fixmap.h>
|
||||
|
||||
/*
|
||||
* KEXEC_SOURCE_MEMORY_LIMIT maximum page get_free_page can return.
|
||||
* I.e. Maximum page that is mapped directly into kernel memory,
|
||||
* and kmap is not required.
|
||||
*
|
||||
* Someone correct me if FIXADDR_START - PAGEOFFSET is not the correct
|
||||
* calculation for the amount of memory directly mappable into the
|
||||
* kernel memory space.
|
||||
*/
|
||||
|
||||
/* Maximum physical address we can use pages from */
|
||||
#define KEXEC_SOURCE_MEMORY_LIMIT (-1UL)
|
||||
/* Maximum address we can reach in physical address mode */
|
||||
#define KEXEC_DESTINATION_MEMORY_LIMIT (-1UL)
|
||||
/* Maximum address we can use for the control code buffer */
|
||||
#define KEXEC_CONTROL_MEMORY_LIMIT TASK_SIZE
|
||||
|
||||
#define KEXEC_CONTROL_CODE_SIZE 4096
|
||||
|
||||
/* The native architecture */
|
||||
#define KEXEC_ARCH KEXEC_ARCH_386
|
||||
|
||||
#endif /* _I386_KEXEC_H */
|
Loading…
Reference in New Issue
Block a user