diff options
author | Dmitry V. Levin <ldv@altlinux.org> | 2014-02-05 13:48:26 +0000 |
---|---|---|
committer | Dmitry V. Levin <ldv@altlinux.org> | 2014-02-05 14:25:20 +0000 |
commit | 90aa9f4d72e26581ea0a39c02db625d7bd4e2962 (patch) | |
tree | 3bab8cebbe335010cb7ee1402904320e78e649e7 | |
parent | d21f186a94086fae25788e14c584fcce52bd67b6 (diff) | |
download | strace-90aa9f4d72e26581ea0a39c02db625d7bd4e2962.tar.gz strace-90aa9f4d72e26581ea0a39c02db625d7bd4e2962.tar.bz2 strace-90aa9f4d72e26581ea0a39c02db625d7bd4e2962.tar.xz |
Implement kexec_load decoding
* kexec.c: New file.
* linux/kexec.h: Likewise.
* Makefile.am (strace_SOURCES): Add kexec.c.
(EXTRA_DIST): Add linux/kexec.h.
* linux/dummy.h (sys_kexec_load): Remove.
* linux/syscall.h (sys_kexec_load): New prototype.
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | kexec.c | 108 | ||||
-rw-r--r-- | linux/dummy.h | 1 | ||||
-rw-r--r-- | linux/kexec.h | 50 | ||||
-rw-r--r-- | linux/syscall.h | 1 |
5 files changed, 161 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index e9e4d21..55cd4cc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,6 +25,7 @@ strace_SOURCES = \ io.c \ ioctl.c \ ipc.c \ + kexec.c \ loop.c \ mem.c \ mtd.c \ @@ -109,6 +110,7 @@ EXTRA_DIST = \ linux/ioctlent.h.in \ linux/ioctlent.sh \ linux/ioctlsort.c \ + linux/kexec.h \ linux/m68k/ioctlent.h.in \ linux/m68k/syscallent.h \ linux/metag/ioctlent.h.in \ @@ -0,0 +1,108 @@ +#include "defs.h" +#include <linux/kexec.h> + +static const struct xlat kexec_arch_values[] = { + XLAT(KEXEC_ARCH_DEFAULT), + XLAT(KEXEC_ARCH_386), + XLAT(KEXEC_ARCH_X86_64), + XLAT(KEXEC_ARCH_PPC), + XLAT(KEXEC_ARCH_PPC64), + XLAT(KEXEC_ARCH_IA_64), + XLAT(KEXEC_ARCH_ARM), + XLAT(KEXEC_ARCH_S390), + XLAT(KEXEC_ARCH_SH), + XLAT(KEXEC_ARCH_MIPS_LE), + XLAT(KEXEC_ARCH_MIPS), + XLAT_END +}; + +static const struct xlat kexec_flags[] = { + XLAT(KEXEC_ON_CRASH), + XLAT(KEXEC_PRESERVE_CONTEXT), + XLAT_END +}; + +static void +print_kexec_segments(struct tcb *tcp, unsigned long addr, unsigned long len) +{ +#if SUPPORTED_PERSONALITIES > 1 + union { + struct { u_int32_t buf, bufsz, mem, memsz; } seg32; + struct { u_int64_t buf, bufsz, mem, memsz; } seg64; + } seg; +# define sizeof_seg \ + (current_wordsize == 4 ? sizeof(seg.seg32) : sizeof(seg.seg64)) +# define seg_buf \ + (current_wordsize == 4 ? (uint64_t) seg.seg32.buf : seg.seg64.buf) +# define seg_bufsz \ + (current_wordsize == 4 ? (uint64_t) seg.seg32.bufsz : seg.seg64.bufsz) +# define seg_mem \ + (current_wordsize == 4 ? (uint64_t) seg.seg32.mem : seg.seg64.mem) +# define seg_memsz \ + (current_wordsize == 4 ? (uint64_t) seg.seg32.memsz : seg.seg64.memsz) +#else + struct kexec_segment seg; +# define sizeof_seg sizeof(seg) +# define seg_buf seg.seg_buf +# define seg_bufsz seg.seg_bufsz +# define seg_mem seg.seg_mem +# define seg_memsz seg.seg_memsz +#endif + unsigned int i, failed; + + if (!len) { + tprints("[]"); + return; + } + + if (len > KEXEC_SEGMENT_MAX) { + tprintf("%#lx", addr); + return; + } + + failed = 0; + tprints("["); + for (i = 0; i < len; ++i) { + if (i) + tprints(", "); + if (umoven(tcp, addr + i * sizeof_seg, sizeof_seg, + (char *) &seg) < 0) { + tprints("?"); + failed = 1; + break; + } + tprintf("{%#lx, %lu, %#lx, %lu}", + (long) seg_buf, (unsigned long) seg_bufsz, + (long) seg_mem, (unsigned long) seg_memsz); + } + tprints("]"); + if (failed) + tprintf(" %#lx", addr); +} + +int +sys_kexec_load(struct tcb *tcp) +{ + unsigned long n; + + if (exiting(tcp)) + return 0; + + /* entry, nr_segments */ + tprintf("%#lx, %lu, ", tcp->u_arg[0], tcp->u_arg[1]); + + /* segments */ + print_kexec_segments(tcp, tcp->u_arg[2], tcp->u_arg[1]); + tprints(", "); + + /* flags */ + n = tcp->u_arg[3]; + printxval(kexec_arch_values, n & KEXEC_ARCH_MASK, "KEXEC_ARCH_???"); + n &= ~KEXEC_ARCH_MASK; + if (n) { + tprints("|"); + printflags(kexec_flags, n, "KEXEC_???"); + } + + return 0; +} diff --git a/linux/dummy.h b/linux/dummy.h index 979ed37..78d264f 100644 --- a/linux/dummy.h +++ b/linux/dummy.h @@ -39,7 +39,6 @@ #define sys_ioprio_get printargs #define sys_ioprio_set printargs #define sys_kcmp printargs -#define sys_kexec_load printargs #define sys_keyctl printargs #define sys_lookup_dcookie printargs #define sys_name_to_handle_at printargs diff --git a/linux/kexec.h b/linux/kexec.h new file mode 100644 index 0000000..767ccb5 --- /dev/null +++ b/linux/kexec.h @@ -0,0 +1,50 @@ +#ifndef LINUX_KEXEC_H +#define LINUX_KEXEC_H + +/* kexec system call - It loads the new kernel to boot into. + * kexec does not sync, or unmount filesystems so if you need + * that to happen you need to do that yourself. + */ + +/* kexec flags for different usage scenarios */ +#define KEXEC_ON_CRASH 0x00000001 +#define KEXEC_PRESERVE_CONTEXT 0x00000002 +#define KEXEC_ARCH_MASK 0xffff0000 + +/* These values match the ELF architecture values. + * Unless there is a good reason that should continue to be the case. + */ +#define KEXEC_ARCH_DEFAULT ( 0 << 16) +#define KEXEC_ARCH_386 ( 3 << 16) +#define KEXEC_ARCH_X86_64 (62 << 16) +#define KEXEC_ARCH_PPC (20 << 16) +#define KEXEC_ARCH_PPC64 (21 << 16) +#define KEXEC_ARCH_IA_64 (50 << 16) +#define KEXEC_ARCH_ARM (40 << 16) +#define KEXEC_ARCH_S390 (22 << 16) +#define KEXEC_ARCH_SH (42 << 16) +#define KEXEC_ARCH_MIPS_LE (10 << 16) +#define KEXEC_ARCH_MIPS ( 8 << 16) + +/* The artificial cap on the number of segments passed to kexec_load. */ +#define KEXEC_SEGMENT_MAX 16 + +/* + * This structure is used to hold the arguments that are used when + * loading kernel binaries. + */ +struct kexec_segment { + const void *buf; + size_t bufsz; + const void *mem; + size_t memsz; +}; + +/* Load a new kernel image as described by the kexec_segment array + * consisting of passed number of segments at the entry-point address. + * The flags allow different useage types. + */ +extern int kexec_load(void *, size_t, struct kexec_segment *, + unsigned long int); + +#endif /* LINUX_KEXEC_H */ diff --git a/linux/syscall.h b/linux/syscall.h index d2ba126..d8ce91f 100644 --- a/linux/syscall.h +++ b/linux/syscall.h @@ -121,6 +121,7 @@ int sys_io_setup(); int sys_io_submit(); int sys_ioctl(); int sys_ipc(); +int sys_kexec_load(); int sys_kill(); int sys_link(); int sys_linkat(); |