summaryrefslogtreecommitdiff
path: root/kexec
diff options
context:
space:
mode:
Diffstat (limited to 'kexec')
-rw-r--r--kexec/Makefile4
-rw-r--r--kexec/arch/ia64/kexec-elf-ia64.c60
-rw-r--r--kexec/arch/ia64/kexec-elf-rel-ia64.c96
-rw-r--r--kexec/arch/ia64/kexec-ia64.c41
-rw-r--r--kexec/kexec.846
5 files changed, 194 insertions, 53 deletions
diff --git a/kexec/Makefile b/kexec/Makefile
index 284b1e8..f8c2f18 100644
--- a/kexec/Makefile
+++ b/kexec/Makefile
@@ -28,6 +28,7 @@ KEXEC_SRCS:= $(KEXEC_C_SRCS) $(KEXEC_S_SRCS)
KEXEC_OBJS:= $(KEXEC_C_OBJS) $(KEXEC_S_OBJS)
KEXEC_DEPS:= $(KEXEC_C_DEPS) $(KEXEC_S_DEPS)
KEXEC:= $(SBINDIR)/kexec
+KEXEC_MANPAGE:= $(MANDIR)/man8/kexec.8
include $(KEXEC_DEPS)
@@ -51,6 +52,9 @@ $(KEXEC): $(KEXEC_OBJS) $(UTIL_LIB)
mkdir -p $(@D)
$(CC) $(KCFLAGS) -o $@ $(KEXEC_OBJS) $(UTIL_LIB) $(LIBS)
+$(KEXEC_MANPAGE): kexec/kexec.8
+ $(MKDIR) -p $(MANDIR)/man8
+ cp kexec/kexec.8 $(KEXEC_MANPAGE)
echo::
@echo "KEXEC_C_SRCS $(KEXEC_C_SRCS)"
@echo "KEXEC_C_DEPS $(KEXEC_C_DEPS)"
diff --git a/kexec/arch/ia64/kexec-elf-ia64.c b/kexec/arch/ia64/kexec-elf-ia64.c
index 4a91b95..06fe59b 100644
--- a/kexec/arch/ia64/kexec-elf-ia64.c
+++ b/kexec/arch/ia64/kexec-elf-ia64.c
@@ -6,6 +6,7 @@
* Copyright (C) 2004 Silicon Graphics, Inc.
* Jesse Barnes <jbarnes@sgi.com>
* Copyright (C) 2004 Khalid Aziz <khalid.aziz@hp.com> Hewlett Packard Co
+ * Copyright (C) 2005 Zou Nan hai <nanhai.zou@intel.com> Intel Corp
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -34,6 +35,7 @@
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>
+#include <limits.h>
#include <elf.h>
#include <boot/elf_boot.h>
#include <ip_checksum.h>
@@ -74,23 +76,29 @@ void elf_ia64_usage(void)
{
printf(
" --command-line=STRING Set the kernel command line to STRING.\n"
- " --append=STRING Set the kernel command line to STRING.\n");
+ " --append=STRING Set the kernel command line to STRING.\n"
+ " --initrd=FILE Use FILE as the kernel's initial ramdisk.\n");
}
int elf_ia64_load(int argc, char **argv, const char *buf, off_t len,
struct kexec_info *info)
{
struct mem_ehdr ehdr;
- const char *command_line;
- int command_line_len;
- unsigned long entry, max_addr;
+ const char *command_line, *ramdisk=0;
+ char *ramdisk_buf = NULL;
+ off_t ramdisk_size = 0;
+ unsigned long command_line_len;
+ unsigned long entry, max_addr, gp_value;
+ unsigned command_line_base, ramdisk_base;
int result;
int opt;
#define OPT_APPEND (OPT_ARCH_MAX+0)
+#define OPT_RAMDISK (OPT_ARCH_MAX+1)
static const struct option options[] = {
KEXEC_ARCH_OPTIONS
{"command-line", 1, 0, OPT_APPEND},
{"append", 1, 0, OPT_APPEND},
+ {"initrd", 1, 0, OPT_RAMDISK},
{0, 0, 0, 0},
};
@@ -110,11 +118,14 @@ int elf_ia64_load(int argc, char **argv, const char *buf, off_t len,
case OPT_APPEND:
command_line = optarg;
break;
+ case OPT_RAMDISK:
+ ramdisk = optarg;
+ break;
}
}
command_line_len = 0;
if (command_line) {
- command_line_len = strlen(command_line) + 1;
+ command_line_len = strlen(command_line) + 16;
}
/* Parse the Elf file */
@@ -129,13 +140,46 @@ int elf_ia64_load(int argc, char **argv, const char *buf, off_t len,
/* Load the Elf data */
result = elf_exec_load(&ehdr, info);
- free_elf_info(&ehdr);
if (result < 0) {
fprintf(stderr, "ELF load failed\n");
+ free_elf_info(&ehdr);
return result;
}
+
+
+ /* Load the setup code */
+ elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
+ 0x80000, ULONG_MAX, 1);
+
+ if (command_line_len) {
+ char *cmdline = xmalloc(command_line_len);
+ strcpy(cmdline, command_line);
+ command_line_len = (command_line_len + 15)&(~15);
+ elf_rel_set_symbol(&info->rhdr, "__command_line_len",
+ &command_line_len, sizeof(long));
+ command_line_base = add_buffer(info, cmdline,
+ command_line_len, command_line_len,
+ 16, 0, max_addr, 1);
+ elf_rel_set_symbol(&info->rhdr, "__command_line",
+ &command_line_base, sizeof(long));
+ }
- /* For now we don't have arguments to pass :( */
- info->entry = (void *)entry;
+ if (ramdisk) {
+ ramdisk_buf = slurp_file(ramdisk, &ramdisk_size);
+ ramdisk_base = add_buffer(info, ramdisk_buf, ramdisk_size,
+ ramdisk_size,
+ getpagesize(), 0, max_addr, 1);
+ elf_rel_set_symbol(&info->rhdr, "__ramdisk_base",
+ &ramdisk_base, sizeof(long));
+ elf_rel_set_symbol(&info->rhdr, "__ramdisk_size",
+ &ramdisk_size, sizeof(long));
+ }
+
+ gp_value = info->rhdr.rel_addr + 0x200000;
+ elf_rel_set_symbol(&info->rhdr, "__gp_value", &gp_value,
+ sizeof(gp_value));
+
+ elf_rel_set_symbol(&info->rhdr, "__kernel_entry", &entry, sizeof(entry));
+ free_elf_info(&ehdr);
return 0;
}
diff --git a/kexec/arch/ia64/kexec-elf-rel-ia64.c b/kexec/arch/ia64/kexec-elf-rel-ia64.c
index eb5f662..6b2c388 100644
--- a/kexec/arch/ia64/kexec-elf-rel-ia64.c
+++ b/kexec/arch/ia64/kexec-elf-rel-ia64.c
@@ -1,8 +1,14 @@
+/* Most of the code in this file is
+ * based on arch/ia64/kernel/module.c in Linux kernel
+ */
+
#include <stdio.h>
#include <elf.h>
#include "../../kexec.h"
#include "../../kexec-elf.h"
+#define MAX_LTOFF ((uint64_t) (1 << 22))
+
int machine_verify_elf_rel(struct mem_ehdr *ehdr)
{
if (ehdr->ei_data != ELFDATA2LSB) {
@@ -17,12 +23,40 @@ int machine_verify_elf_rel(struct mem_ehdr *ehdr)
return 1;
}
+static void
+ia64_patch (uint64_t insn_addr, uint64_t mask, uint64_t val)
+{
+ uint64_t m0, m1, v0, v1, b0, b1, *b = (uint64_t *) (insn_addr & -16);
+# define insn_mask ((1UL << 41) - 1)
+ unsigned long shift;
+
+ b0 = b[0]; b1 = b[1];
+ shift = 5 + 41 * (insn_addr % 16); /* 5 bits of template, then 3 x 41-bit instructions */
+ if (shift >= 64) {
+ m1 = mask << (shift - 64);
+ v1 = val << (shift - 64);
+ } else {
+ m0 = mask << shift; m1 = mask >> (64 - shift);
+ v0 = val << shift; v1 = val >> (64 - shift);
+ b[0] = (b0 & ~m0) | (v0 & m0);
+ }
+ b[1] = (b1 & ~m1) | (v1 & m1);
+}
+
+static inline uint64_t
+bundle (const uint64_t insn)
+{
+ return insn & ~0xfUL;
+}
+
void machine_apply_elf_rel(struct mem_ehdr *ehdr, unsigned long r_type,
void *location, unsigned long address, unsigned long value)
{
+ uint64_t gp_value = ehdr->rel_addr + 0x200000;
switch(r_type) {
case R_IA64_NONE:
break;
+ case R_IA64_SEGREL64LSB:
case R_IA64_DIR64LSB:
*((uint64_t *)location) = value;
break;
@@ -31,15 +65,67 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, unsigned long r_type,
if (value != *((uint32_t *)location))
goto overflow;
break;
- case R_IA64_PCREL21B:
+ case R_IA64_IMM64:
+ ia64_patch((uint64_t)location, 0x01fffefe000UL,
+ /* bit 63 -> 36 */
+ (((value & 0x8000000000000000UL) >> 27)
+ /* bit 21 -> 21 */
+ | ((value & 0x0000000000200000UL) << 0)
+ /* bit 16 -> 22 */
+ | ((value & 0x00000000001f0000UL) << 6)
+ /* bit 7 -> 27 */
+ | ((value & 0x000000000000ff80UL) << 20)
+ /* bit 0 -> 13 */
+ | ((value & 0x000000000000007fUL) << 13)));
+ ia64_patch((uint64_t)location - 1, 0x1ffffffffffUL, value>>22);
+ break;
+ case R_IA64_IMM22:
+ if (value + (1 << 21) >= (1 << 22))
+ die("value out of IMM22 range\n");
+ ia64_patch((uint64_t)location, 0x01fffcfe000UL,
+ /* bit 21 -> 36 */
+ (((value & 0x200000UL) << 15)
+ /* bit 16 -> 22 */
+ | ((value & 0x1f0000UL) << 6)
+ /* bit 7 -> 27 */
+ | ((value & 0x00ff80UL) << 20)
+ /* bit 0 -> 13 */
+ | ((value & 0x00007fUL) << 13) ));
+ break;
+ case R_IA64_PCREL21B: {
+ uint64_t delta = ((int64_t)value - (int64_t)address)/16;
+ if (delta + (1 << 20) >= (1 << 21))
+ die("value out of IMM21B range\n");
+ value = ((int64_t)(value - bundle(address)))/16;
+ ia64_patch((uint64_t)location, 0x11ffffe000UL,
+ (((value & 0x100000UL) << 16) /* bit 20 -> 36 */
+ | ((value & 0x0fffffUL) << 13) /* bit 0 -> 13 */));
+ }
+ break;
+ case R_IA64_LTOFF22X:
+ if (value - gp_value + MAX_LTOFF/2 >= MAX_LTOFF)
+ die("value out of gp relative range");
+ value -= gp_value;
+ ia64_patch((uint64_t)location, 0x01fffcfe000UL,
+ (((value & 0x200000UL) << 15) /* bit 21 -> 36 */
+ |((value & 0x1f0000UL) << 6) /* bit 16 -> 22 */
+ |((value & 0x00ff80UL) << 20) /* bit 7 -> 27 */
+ |((value & 0x00007fUL) << 13) /* bit 0 -> 13 */));
+ break;
+ case R_IA64_LDXMOV:
+ if (value - gp_value + MAX_LTOFF/2 >= MAX_LTOFF)
+ die("value out of gp relative range");
+ ia64_patch((uint64_t)location, 0x1fff80fe000UL, 0x10000000000UL);
+ break;
case R_IA64_LTOFF22:
- case R_IA64_SEGREL64LSB:
+
default:
- die("Unknown rela relocation: %lu\n", r_type);
+ die("Unknown rela relocation: 0x%lx 0x%lx\n",
+ r_type, address);
break;
}
return;
- overflow:
+overflow:
die("overflow in relocation type %lu val %Lx\n",
- r_type, value);
+ r_type, value);
}
diff --git a/kexec/arch/ia64/kexec-ia64.c b/kexec/arch/ia64/kexec-ia64.c
index f631cef..01ba805 100644
--- a/kexec/arch/ia64/kexec-ia64.c
+++ b/kexec/arch/ia64/kexec-ia64.c
@@ -27,6 +27,7 @@
#include <stdint.h>
#include <string.h>
#include <getopt.h>
+#include <sched.h>
#include <sys/utsname.h>
#include "../../kexec.h"
#include "../../kexec-syscall.h"
@@ -56,7 +57,7 @@ int get_memory_ranges(struct memory_range **range, int *ranges,
*/
fprintf(stderr, "Warning assuming memory at 0-64MB is present\n");
memory_ranges = 0;
- memory_range[memory_ranges].start = 0x00010000;
+ memory_range[memory_ranges].start = 0x00100000;
memory_range[memory_ranges].end = 0x10000000;
memory_range[memory_ranges].type = RANGE_RAM;
memory_ranges++;
@@ -76,9 +77,6 @@ void arch_usage(void)
{
}
-static struct {
-} arch_options = {
-};
int arch_process_options(int argc, char **argv)
{
static const struct option options[] = {
@@ -87,8 +85,12 @@ int arch_process_options(int argc, char **argv)
};
static const char short_options[] = KEXEC_ARCH_OPT_STR;
int opt;
- unsigned long value;
- char *end;
+
+ /* execute from monarch processor */
+ cpu_set_t affinity;
+ CPU_ZERO(&affinity);
+ CPU_SET(0, &affinity);
+ sched_setaffinity(0, sizeof(affinity), &affinity);
opterr = 0; /* Don't complain about unrecognized options here */
while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
@@ -115,32 +117,7 @@ int arch_compat_trampoline(struct kexec_info *info)
}
if (strcmp(utsname.machine, "ia64") == 0)
{
- info->kexec_flags |= KEXEC_ARCH_X86_64;
- }
- else {
- fprintf(stderr, "Unsupported machine type: %s\n",
- utsname.machine);
- return -1;
- }
- return 0;
-}
-
-int arch_compat_trampoline(struct kexec_info *info)
-{
- int result;
- struct utsname utsname;
- result = uname(&utsname);
- if (result < 0) {
- fprintf(stderr, "uname failed: %s\n",
- strerror(errno));
- return -1;
- }
- if (strcmp(utsname.machine, "ia64") == 0)
- {
- /* For compatibility with older patches
- * use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_IA64 here.
- */
- info->kexec_flags |= KEXEC_ARCH_DEFAULT;
+ info->kexec_flags |= KEXEC_ARCH_IA_64;
}
else {
fprintf(stderr, "Unsupported machine type: %s\n",
diff --git a/kexec/kexec.8 b/kexec/kexec.8
index 5dcbd80..daac8b0 100644
--- a/kexec/kexec.8
+++ b/kexec/kexec.8
@@ -2,7 +2,7 @@
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
-.TH KEXEC-TOOLS 8 "October 13, 2004"
+.TH KEXEC 8 "October 13, 2004"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
@@ -16,30 +16,60 @@
.\" .sp <n> insert n+1 empty lines
.\" for manpage-specific macros, see man(7)
.SH NAME
-kexec-tools \- Tool to load a kernel for warm reboot and initiate a warm reboot
+kexec \- Tool to load a kernel for warm reboot and initiate a warm reboot
.SH SYNOPSIS
-.B kexec-tools
+.B kexec
.RI [ options ] " files" ...
.SH DESCRIPTION
.PP
.\" TeX users may be more comfortable with the \fB<whatever>\fP and
.\" \fI<whatever>\fP escape sequences to invode bold face and italics,
.\" respectively.
-\fBkexec-tools\fP does not have a man page yet. Please use "kexec -h" for help.
+\fBkexec\fP allows one to load another kernel from the currently running
+Linux kernel. Normally one would load a kernel, and possibly an initial
+ramdisk, into the currently running kernel using kexec and then initiate
+a warm reboot by executing kexec again with appropriate option.
.SH OPTIONS
These programs follow the usual GNU command line syntax, with long
options starting with two dashes (`-').
A summary of options is included below.
-For a complete description, see the Info files.
.TP
.B \-h, \-\-help
Show summary of options.
.TP
.B \-v, \-\-version
Show version of program.
-.SH SEE ALSO
+.TP
+.B \-f, \-\-force
+Force an immediate kexec without calling shutdown.
+.TP
+.B \-x, \-\-no-ifdown
+Don't bring down network interfaces. (if used, must be last option specified)
+.TP
+.B \-l, \-\-load
+Load the new kernel into the current kernel.
+.TP
+.B \-p, \-\-load-panic
+Load the new kernel for use on panic.
+.TP
+.B \-u, \-\-unload
+Unload the current kexec target kernel.
+.TP
+.B \-e, \-\-exec
+Execute a currently loaded kernel.
+.TP
+.B \-t, \-\-type=TYPE
+Specify the new kernel is of this type.
+.TP
+.B \-\-mem\-min=<addr>
+Specify the lowest memory addres to load code into.
+.TP
+.B \-\-mem\-max=<addr>
+Specify the highest memory addres to load code into.
+.TP
+There may be additional options supported on individual architectures. Use --help option to see those options.
.SH AUTHOR
-kexec-tools was written by Eric Biederman.
+kexec was written by Eric Biederman.
.PP
-This manual page was written by Khalid Aziz <khalid_aziz@hp.com>,
+This manual page was written by Khalid Aziz <khalid.aziz@hp.com>,
for the Debian project (but may be used by others).