summaryrefslogtreecommitdiff
path: root/kexec_tools
diff options
context:
space:
mode:
authorSzymon Starzycki <sstar@google.com>2013-07-15 15:17:07 -0700
committerSzymon Starzycki <sstar@google.com>2013-07-25 13:47:42 -0700
commit3d86b018b6e08a84af214206ebedf342a8576a91 (patch)
treee2a1282238bbb01dd0cc8a430b86f9b8653cda31 /kexec_tools
parent252f2fd4b01cacc20c9655fefc6e842635ba224b (diff)
downloadextras-3d86b018b6e08a84af214206ebedf342a8576a91.tar.gz
Kexec tools for replacing working kernel during crash
Change-Id: I3d2bfacbe2f0e338712babf81efcecb79e9758ad
Diffstat (limited to 'kexec_tools')
-rw-r--r--kexec_tools/Android.mk23
-rw-r--r--kexec_tools/kexec.h24
-rw-r--r--kexec_tools/kexecload.c166
3 files changed, 213 insertions, 0 deletions
diff --git a/kexec_tools/Android.mk b/kexec_tools/Android.mk
new file mode 100644
index 00000000..f6bf7dc2
--- /dev/null
+++ b/kexec_tools/Android.mk
@@ -0,0 +1,23 @@
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Copyright The Android Open Source Project
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := kexecload
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := eng
+LOCAL_SRC_FILES := kexecload.c
+include $(BUILD_EXECUTABLE)
diff --git a/kexec_tools/kexec.h b/kexec_tools/kexec.h
new file mode 100644
index 00000000..2300ff56
--- /dev/null
+++ b/kexec_tools/kexec.h
@@ -0,0 +1,24 @@
+#ifndef _SYS_KEXEC_H
+#define _SYS_KEXEC_H
+
+#include <sys/cdefs.h>
+#include <uapi/linux/kexec.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include "kexec.h"
+
+#define KEXEC_SEGMENT_MAX 16
+
+#define KEXEC_TYPE_DEFAULT 0
+#define KEXEC_TYPE_CRASH 1
+
+/*
+ * Prototypes
+ */
+
+static inline long kexec_load(unsigned int entry, unsigned long nr_segments,
+ struct kexec_segment *segment, unsigned long flags) {
+ return syscall(__NR_kexec_load, entry, nr_segments, segment, flags);
+}
+
+#endif /* _SYS_KEXEC_H */
diff --git a/kexec_tools/kexecload.c b/kexec_tools/kexecload.c
new file mode 100644
index 00000000..2574e4a6
--- /dev/null
+++ b/kexec_tools/kexecload.c
@@ -0,0 +1,166 @@
+#include <errno.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "kexec.h"
+
+// Offsets same as in kernel asm/kexec.h
+#define KEXEC_ARM_ATAGS_OFFSET 0x1000
+#define KEXEC_ARM_ZIMAGE_OFFSET 0x8000
+
+#define MEMORY_SIZE 0x0800000
+// Physical buffer address cannot overlap with other regions
+#define START_ADDRESS 0x44000000
+
+#define ROUND_TO_PAGE(address,pagesize) ((address + pagesize - 1) & (~(pagesize - 1)))
+
+/*
+ * Gives file position and resets current position to begining of file
+ */
+int get_file_size(int f)
+{
+ struct stat st;
+ fstat(f, &st);
+ return st.st_size;
+}
+
+int test_kexeccall() {
+ int rv;
+
+ rv = kexec_load(0, 0, NULL, KEXEC_ARCH_DEFAULT);
+
+ if (rv != 0) {
+ printf("ERROR: kexec_load: %d \n", errno);
+ return 1;
+ }
+
+ printf("Kexec test: Success \n");
+
+ return 0;
+}
+
+void usage(void)
+{
+ fprintf(stderr,
+ "usage: kexecload [ <option> ] <atags path> <kernel path>\n"
+ "\n"
+ "options:\n"
+ " -t tests syscall\n"
+ " -s <start address> specify start address of kernel\n"
+ );
+}
+
+/*
+ * Loads kexec into the kernel and sets kexec on crash
+ */
+int main(int argc, char *argv[])
+{
+ int rv;
+ int atag_file,
+ zimage_file;
+ int atag_size,
+ zimage_size,
+ total_size;
+ void *atag_buffer;
+ void *zimage_buffer;
+ struct kexec_segment segment[2];
+ int page_size = getpagesize();
+ void *start_address = (void *)START_ADDRESS;
+ int c;
+
+ const struct option longopts[] = {
+ {"start_address", required_argument, 0, 's'},
+ {"test", 0, 0, 't'},
+ {"help", 0, 0, 'h'},
+ {0, 0, 0, 0}
+ };
+
+ while (1) {
+ int option_index = 0;
+ c = getopt_long(argc, argv, "s:th", longopts, NULL);
+ if (c < 0) {
+ break;
+ }
+ /* Alphabetical cases */
+ switch (c) {
+ case 's':
+ start_address = (void *) strtoul(optarg, 0, 16);
+ break;
+ case 'h':
+ usage();
+ return 1;
+ case 't':
+ test_kexeccall();
+ return 1;
+ case '?':
+ return 1;
+ default:
+ abort();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2) {
+ usage();
+ return 1;
+ }
+
+ atag_file = open(argv[0], O_RDONLY);
+ zimage_file = open(argv[1], O_RDONLY);
+
+ if (atag_file < 0 || zimage_file < 0) {
+ fprintf(stderr, "Error during opening of atag file or the zImage file %s\n", strerror(errno));
+ return 1;
+ }
+
+ atag_size = ROUND_TO_PAGE(get_file_size(atag_file), page_size);
+ zimage_size = ROUND_TO_PAGE(get_file_size(zimage_file), page_size);
+
+ if (atag_size >= KEXEC_ARM_ZIMAGE_OFFSET - KEXEC_ARM_ATAGS_OFFSET) {
+ fprintf(stderr, "Atag file is too large\n");
+ return 1;
+ }
+
+ atag_buffer = (char *) mmap(NULL, atag_size, PROT_READ, MAP_POPULATE | MAP_PRIVATE, atag_file, 0);
+ zimage_buffer = (char *) mmap(NULL, zimage_size, PROT_READ, MAP_POPULATE | MAP_PRIVATE, zimage_file, 0);
+
+ if(atag_buffer == MAP_FAILED || zimage_buffer == MAP_FAILED) {
+ fprintf(stderr, "Unable to map files into memory");
+ return 1;
+ }
+
+ segment[0].buf = zimage_buffer;
+ segment[0].bufsz = zimage_size;
+ segment[0].mem = (void *) ((unsigned) start_address + KEXEC_ARM_ZIMAGE_OFFSET);
+ segment[0].memsz = zimage_size;
+
+ segment[1].buf = atag_buffer;
+ segment[1].bufsz = atag_size;
+ segment[1].mem = (void *) ((unsigned) start_address + KEXEC_ARM_ATAGS_OFFSET);
+ segment[1].memsz = atag_size;
+
+ rv = kexec_load(((unsigned) start_address + KEXEC_ARM_ZIMAGE_OFFSET),
+ 2, (void *) segment, KEXEC_ARCH_DEFAULT | KEXEC_ON_CRASH);
+
+ if (rv != 0) {
+ fprintf(stderr, "Kexec_load returned non-zero exit code: %d with errno %d\n", rv, errno);
+ return 1;
+ }
+
+ printf("Done! Kexec loaded\n");
+ printf("New kernel should start at 0x%08x\n", START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET);
+
+ return 0;
+
+}
+