├── .gitignore ├── README.md ├── kernel ├── Kbuild ├── Makefile ├── kexec-drv.c ├── kexec-mod.h ├── kexec.c └── machine_kexec.c └── user ├── Makefile └── redir.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kexec-module 2 | Light scaffolding for building kexec as a loadable kernel module 3 | 4 | `kernel/`: build `kexec-mod.ko` module that exposes kexec functionality as 5 | ioctls on `/dev/kexec`. 6 | Unlike [ford_kexec](https://github.com/chaosmaster/ford_kexec) this avoids 7 | files that are almost, but not quite, exact copies of in-tree functionality. 8 | Some hacks are still needed of course, but at least you can easily see what 9 | they are. The hacks are currently tuned for 3.8 x86 and arm kernels. 10 | 11 | `user/`: build `redir.so` that acts as an LD_PRELOAD interposer for syscall() 12 | and reboot() libc.so calls to allow use of unpatched `kexec-tools`. 13 | -------------------------------------------------------------------------------- /kernel/Kbuild: -------------------------------------------------------------------------------- 1 | obj-m := kexec-mod.o 2 | kexec-mod-y := kexec-drv.o kexec.o 3 | kexec-mod-y += machine_kexec.o relocate_kernel.o 4 | ccflags-y := -include kexec-mod.h 5 | -------------------------------------------------------------------------------- /kernel/Makefile: -------------------------------------------------------------------------------- 1 | KDIR ?= /lib/modules/`uname -r`/build 2 | ARCH ?= arm 3 | 4 | default: orig 5 | $(MAKE) -C $(KDIR) M=$$PWD 6 | 7 | orig: 8 | mkdir $@ 9 | ln -s $(KDIR)/arch/$(ARCH)/kernel/relocate_kernel.S 10 | ln -s $(KDIR)/arch/$(ARCH)/kernel/machine_kexec.c $@/ 11 | ln -s $(KDIR)/kernel/kexec.c $@/ 12 | -------------------------------------------------------------------------------- /kernel/kexec-drv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | static void (*machine_shutdown_ptr)(void); 11 | static void (*kernel_restart_prepare_ptr)(char*); 12 | static void (*soft_restart_ptr)(unsigned long); 13 | static int (*memblock_is_region_memory_ptr)(phys_addr_t, phys_addr_t); 14 | 15 | void machine_shutdown(void) 16 | { 17 | machine_shutdown_ptr(); 18 | } 19 | void kernel_restart_prepare(char *cmd) 20 | { 21 | kernel_restart_prepare_ptr(cmd); 22 | } 23 | void soft_restart(unsigned long addr) 24 | { 25 | soft_restart_ptr(addr); 26 | } 27 | int memblock_is_region_memory(phys_addr_t base, phys_addr_t size) 28 | { 29 | return memblock_is_region_memory_ptr(base, size); 30 | } 31 | 32 | static long kexecmod_ioctl(struct file *file, unsigned req, unsigned long arg) 33 | { 34 | struct { 35 | unsigned long entry; 36 | unsigned long nr_segs; 37 | struct kexec_segment *segs; 38 | unsigned long flags; 39 | } ap; 40 | switch (req) { 41 | case LINUX_REBOOT_CMD_KEXEC - 1: 42 | if (copy_from_user(&ap, (void*)arg, sizeof ap)) 43 | return -EFAULT; 44 | return sys_kexec_load(ap.entry, ap.nr_segs, ap.segs, ap.flags); 45 | case LINUX_REBOOT_CMD_KEXEC: 46 | return kernel_kexec(); 47 | } 48 | return -EINVAL; 49 | } 50 | 51 | static const struct file_operations fops = { 52 | .owner = THIS_MODULE, 53 | .unlocked_ioctl = kexecmod_ioctl, 54 | }; 55 | 56 | static void *ksym(const char *name) 57 | { 58 | return (void *)kallsyms_lookup_name(name); 59 | } 60 | 61 | static int __init 62 | kexecmod_init(void) 63 | { 64 | int maj; 65 | struct class *class; 66 | struct device *device; 67 | if (!(machine_shutdown_ptr = ksym("machine_shutdown")) 68 | #ifdef CONFIG_ARM 69 | //|| !(machine_shutdown_ptr = ksym("disable_nonboot_cpus")) 70 | || !(soft_restart_ptr = ksym("soft_restart")) 71 | || !(memblock_is_region_memory_ptr = ksym("memblock_is_region_memory")) 72 | #endif 73 | || !(kernel_restart_prepare_ptr = ksym("kernel_restart_prepare"))) 74 | return -ENOENT; 75 | 76 | maj = register_chrdev(0, "kexec", &fops); 77 | if (maj < 0) 78 | return maj; 79 | class = class_create(THIS_MODULE, "kexec"); 80 | if (IS_ERR(class)) 81 | return PTR_ERR(class); 82 | device = device_create(class, 0, MKDEV(maj, 0), 0, "kexec"); 83 | if (IS_ERR(device)) 84 | return PTR_ERR(device); 85 | return 0; 86 | } 87 | 88 | module_init(kexecmod_init) 89 | 90 | MODULE_LICENSE("GPL v2"); 91 | -------------------------------------------------------------------------------- /kernel/kexec-mod.h: -------------------------------------------------------------------------------- 1 | #define CONFIG_KEXEC 1 2 | #include 3 | #undef VMCOREINFO_SYMBOL 4 | #define VMCOREINFO_SYMBOL(_) do {} while (0) 5 | -------------------------------------------------------------------------------- /kernel/kexec.c: -------------------------------------------------------------------------------- 1 | #include 2 | #undef module_init 3 | #define module_init(initfn) __attribute__((unused)) static int initfn(void); 4 | 5 | #include "orig/kexec.c" 6 | 7 | int panic_on_oops; 8 | 9 | int insert_resource(struct resource *parent, struct resource *res) { return 0; } 10 | 11 | __attribute__((weak)) 12 | void machine_crash_shutdown(struct pt_regs *regs) {} 13 | -------------------------------------------------------------------------------- /kernel/machine_kexec.c: -------------------------------------------------------------------------------- 1 | #include "orig/machine_kexec.c" 2 | 3 | #ifdef CONFIG_X86 4 | void disable_IO_APIC(void) {} 5 | unsigned long max_pfn; 6 | #include 7 | module_param(max_pfn, ulong, 0); 8 | #endif 9 | -------------------------------------------------------------------------------- /user/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS ?= -O2 -Wall 2 | 3 | all: redir.so 4 | 5 | %.so: %.c 6 | $(CC) $(CFLAGS) -shared -fpic -o $@ $< 7 | -------------------------------------------------------------------------------- /user/redir.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define LINUX_REBOOT_CMD_KEXEC 0x45584543 9 | 10 | static long dev_kexec_ioctl(int cmd, void *arg) 11 | { 12 | return ioctl(open("/dev/kexec", O_RDONLY), cmd, arg); 13 | } 14 | 15 | long syscall(long num, ...) 16 | { 17 | if (num != SYS_kexec_load) 18 | abort(); 19 | struct { 20 | long entry; 21 | long nsegs; 22 | void *segs; 23 | long flags; 24 | } ap; 25 | va_list va; 26 | va_start(va, num); 27 | ap.entry = va_arg(va, long); 28 | ap.nsegs = va_arg(va, long); 29 | ap.segs = va_arg(va, void *); 30 | ap.flags = va_arg(va, long); 31 | va_end(va); 32 | return dev_kexec_ioctl(LINUX_REBOOT_CMD_KEXEC - 1, &ap); 33 | } 34 | 35 | int reboot(int cmd) 36 | { 37 | if (cmd != LINUX_REBOOT_CMD_KEXEC) 38 | abort(); 39 | return dev_kexec_ioctl(cmd, 0); 40 | } 41 | --------------------------------------------------------------------------------