From: Geoff Levand Date: Mon, 15 Jul 2013 23:32:36 +0000 (-0700) Subject: Add arm64 support X-Git-Url: https://git.linaro.org/gitweb?p=people%2Fgeoff%2Fkexec-tools.git;a=commitdiff_plain;h=fbf5ac6c2c70ec0f6da2b9ff563e573999752c01 Add arm64 support Signed-off-by: Geoff Levand Get patch from: https://fedorapeople.org/~hrw/aarch64/for-fedora/kexec-aarch64.patch Upstream-Status: Pending Signed-off-by: Kai Kang --- --- configure.ac | 3 kexec/Makefile | 1 kexec/arch/arm64/Makefile | 13 + kexec/arch/arm64/crashdump-arm64.c | 305 ++++++++++++++++++++++++++++++++ kexec/arch/arm64/include/arch/options.h | 26 ++ kexec/arch/arm64/kexec-arm64.c | 177 ++++++++++++++++++ kexec/arch/arm64/kexec-arm64.h | 20 ++ kexec/arch/arm64/kexec-elf-arm64.c | 114 +++++++++++ kexec/kexec-syscall.h | 9 kexec/kexec.c | 2 purgatory/arch/arm64/Makefile | 7 11 files changed, 675 insertions(+), 2 deletions(-) --- a/configure.ac +++ b/configure.ac @@ -35,6 +35,9 @@ case $target_cpu in ARCH="ppc64" SUBARCH="LE" ;; + aarch64* ) + ARCH="arm64" + ;; arm* ) ARCH="arm" ;; --- a/kexec/Makefile +++ b/kexec/Makefile @@ -71,6 +71,7 @@ KEXEC_SRCS += $($(ARCH)_FS2DT) include $(srcdir)/kexec/arch/alpha/Makefile include $(srcdir)/kexec/arch/arm/Makefile +include $(srcdir)/kexec/arch/arm64/Makefile include $(srcdir)/kexec/arch/i386/Makefile include $(srcdir)/kexec/arch/ia64/Makefile include $(srcdir)/kexec/arch/m68k/Makefile --- /dev/null +++ b/kexec/arch/arm64/Makefile @@ -0,0 +1,13 @@ + +arm64_KEXEC_SRCS += \ + kexec/arch/arm64/kexec-arm64.c \ + kexec/arch/arm64/kexec-elf-arm64.c \ + kexec/arch/arm64/crashdump-arm64.c + +arm64_ARCH_REUSE_INITRD = +arm64_ADD_SEGMENT = +arm64_VIRT_TO_PHYS = + +dist += $(arm64_KEXEC_SRCS) \ + kexec/arch/arm64/Makefile \ + kexec/arch/arm64/kexec-arm64.h --- /dev/null +++ b/kexec/arch/arm64/crashdump-arm64.c @@ -0,0 +1,305 @@ +/* + * 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 + * the Free Software Foundation (version 2 of the License). + */ + +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../crashdump.h" + +int is_crashkernel_mem_reserved(void) +{ + return 0; +} + +#if 0 +/* + * Used to save various memory ranges/regions needed for the captured + * kernel to boot. (lime memmap= option in other archs) + */ +static struct memory_range crash_memory_ranges[CRASH_MAX_MEMORY_RANGES]; +struct memory_ranges usablemem_rgns = { + .size = 0, + .ranges = crash_memory_ranges, +}; + +/* memory range reserved for crashkernel */ +static struct memory_range crash_reserved_mem; + +static struct crash_elf_info elf_info = { + .class = ELFCLASS32, + .data = ELFDATA2LSB, + .machine = EM_ARM, + .page_offset = PAGE_OFFSET, +}; + +unsigned long phys_offset; + +/** + * crash_range_callback() - callback called for each iomem region + * @data: not used + * @nr: not used + * @str: name of the memory region + * @base: start address of the memory region + * @length: size of the memory region + * + * This function is called once for each memory region found in /proc/iomem. It + * locates system RAM and crashkernel reserved memory and places these to + * variables: @crash_memory_ranges and @crash_reserved_mem. Number of memory + * regions is placed in @crash_memory_nr_ranges. + */ +static int crash_range_callback(void *UNUSED(data), int UNUSED(nr), + char *str, unsigned long base, + unsigned long length) +{ + struct memory_range *range; + + if (usablemem_rgns.size >= CRASH_MAX_MEMORY_RANGES) + return 1; + + range = usablemem_rgns.ranges + usablemem_rgns.size; + + if (strncmp(str, "System RAM\n", 11) == 0) { + range->start = base; + range->end = base + length - 1; + range->type = RANGE_RAM; + usablemem_rgns.size++; + } else if (strncmp(str, "Crash kernel\n", 13) == 0) { + crash_reserved_mem.start = base; + crash_reserved_mem.end = base + length - 1; + crash_reserved_mem.type = RANGE_RAM; + } + + return 0; +} + +/** + * crash_exclude_range() - excludes memory region reserved for crashkernel + * + * Function locates where crashkernel reserved memory is and removes that region + * from the available memory regions. + */ +static void crash_exclude_range(void) +{ + const struct memory_range *range = &crash_reserved_mem; + int i; + + for (i = 0; i < usablemem_rgns.size; i++) { + struct memory_range *r = usablemem_rgns.ranges + i; + + /* + * We assume that crash area is fully contained in + * some larger memory area. + */ + if (r->start <= range->start && r->end >= range->end) { + struct memory_range *new; + /* + * Let's split this area into 2 smaller ones and + * remove excluded range from between. First create + * new entry for the remaining area. + */ + new = usablemem_rgns.ranges + usablemem_rgns.size; + new->start = range->end + 1; + new->end = r->end; + usablemem_rgns.size++; + /* + * Next update this area to end before excluded range. + */ + r->end = range->start - 1; + break; + } + } +} + +static int range_cmp(const void *a1, const void *a2) +{ + const struct memory_range *r1 = a1; + const struct memory_range *r2 = a2; + + if (r1->start > r2->start) + return 1; + if (r1->start < r2->start) + return -1; + + return 0; +} + +/** + * crash_get_memory_ranges() - read system physical memory + * + * Function reads through system physical memory and stores found memory regions + * in @crash_memory_ranges. Number of memory regions found is placed in + * @crash_memory_nr_ranges. Regions are sorted in ascending order. + * + * Returns %0 in case of success and %-1 otherwise (errno is set). + */ +static int crash_get_memory_ranges(void) +{ + /* + * First read all memory regions that can be considered as + * system memory including the crash area. + */ + kexec_iomem_for_each_line(NULL, crash_range_callback, NULL); + + if (usablemem_rgns.size < 1) { + errno = EINVAL; + return -1; + } + + /* + * Exclude memory reserved for crashkernel (this may result a split memory + * region). + */ + crash_exclude_range(); + + /* + * Make sure that the memory regions are sorted. + */ + qsort(usablemem_rgns.ranges, usablemem_rgns.size, + sizeof(*usablemem_rgns.ranges), range_cmp); + + return 0; +} + +/** + * cmdline_add_elfcorehdr() - adds elfcorehdr= to @cmdline + * @cmdline: buffer where parameter is placed + * @elfcorehdr: physical address of elfcorehdr + * + * Function appends 'elfcorehdr=start' at the end of the command line given in + * @cmdline. Note that @cmdline must be at least %COMMAND_LINE_SIZE bytes long + * (inclunding %NUL). + */ +static void cmdline_add_elfcorehdr(char *cmdline, unsigned long elfcorehdr) +{ + char buf[COMMAND_LINE_SIZE]; + int buflen; + + buflen = snprintf(buf, sizeof(buf), "%s elfcorehdr=%#lx", + cmdline, elfcorehdr); + if (buflen < 0) + die("Failed to construct elfcorehdr= command line parameter\n"); + if (buflen >= sizeof(buf)) + die("Command line overflow\n"); + + (void) strncpy(cmdline, buf, COMMAND_LINE_SIZE); + cmdline[COMMAND_LINE_SIZE - 1] = '\0'; +} + +/** + * cmdline_add_mem() - adds mem= parameter to kernel command line + * @cmdline: buffer where parameter is placed + * @size: size of the kernel reserved memory (in bytes) + * + * This function appends 'mem=size' at the end of the command line given in + * @cmdline. Note that @cmdline must be at least %COMMAND_LINE_SIZE bytes long + * (including %NUL). + */ +static void cmdline_add_mem(char *cmdline, unsigned long size) +{ + char buf[COMMAND_LINE_SIZE]; + int buflen; + + buflen = snprintf(buf, sizeof(buf), "%s mem=%ldK", cmdline, size >> 10); + if (buflen < 0) + die("Failed to construct mem= command line parameter\n"); + if (buflen >= sizeof(buf)) + die("Command line overflow\n"); + + (void) strncpy(cmdline, buf, COMMAND_LINE_SIZE); + cmdline[COMMAND_LINE_SIZE - 1] = '\0'; +} + +static unsigned long long range_size(const struct memory_range *r) +{ + return r->end - r->start + 1; +} + +static void dump_memory_ranges(void) +{ + int i; + + if (!kexec_debug) + return; + + dbgprintf("crashkernel: [%#llx - %#llx] (%ldM)\n", + crash_reserved_mem.start, crash_reserved_mem.end, + (unsigned long)range_size(&crash_reserved_mem) >> 20); + + for (i = 0; i < usablemem_rgns.size; i++) { + struct memory_range *r = usablemem_rgns.ranges + i; + dbgprintf("memory range: [%#llx - %#llx] (%ldM)\n", + r->start, r->end, (unsigned long)range_size(r) >> 20); + } +} + +/** + * load_crashdump_segments() - loads additional segments needed for kdump + * @info: kexec info structure + * @mod_cmdline: kernel command line + * + * This function loads additional segments which are needed for the dump capture + * kernel. It also updates kernel command line passed in @mod_cmdline to have + * right parameters for the dump capture kernel. + * + * Return %0 in case of success and %-1 in case of error. + */ +int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline) +{ + unsigned long elfcorehdr; + unsigned long bufsz; + void *buf; + int err; + + /* + * First fetch all the memory (RAM) ranges that we are going to pass to + * the crashdump kernel during panic. + */ + err = crash_get_memory_ranges(); + if (err) + return err; + + /* + * Now that we have memory regions sorted, we can use first memory + * region as PHYS_OFFSET. + */ + phys_offset = usablemem_rgns.ranges->start; + dbgprintf("phys_offset: %#lx\n", phys_offset); + + err = crash_create_elf32_headers(info, &elf_info, + usablemem_rgns.ranges, + usablemem_rgns.size, &buf, &bufsz, + ELF_CORE_HEADER_ALIGN); + if (err) + return err; + + /* + * We allocate ELF core header from the end of the memory area reserved + * for the crashkernel. We align the header to SECTION_SIZE (which is + * 1MB) so that available memory passed in kernel command line will be + * aligned to 1MB. This is because kernel create_mapping() wants memory + * regions to be aligned to SECTION_SIZE. + */ + elfcorehdr = add_buffer_phys_virt(info, buf, bufsz, bufsz, 1 << 20, + crash_reserved_mem.start, + crash_reserved_mem.end, -1, 0); + + dbgprintf("elfcorehdr: %#lx\n", elfcorehdr); + cmdline_add_elfcorehdr(mod_cmdline, elfcorehdr); + + /* + * Add 'mem=size' parameter to dump capture kernel command line. This + * prevents the dump capture kernel from using any other memory regions + * which belong to the primary kernel. + */ + cmdline_add_mem(mod_cmdline, elfcorehdr - crash_reserved_mem.start); + + dump_memory_ranges(); + dbgprintf("kernel command line: \"%s\"\n", mod_cmdline); + + return 0; +} + +#endif + --- /dev/null +++ b/kexec/arch/arm64/include/arch/options.h @@ -0,0 +1,26 @@ +#ifndef KEXEC_ARCH_ARM64_OPTIONS_H +#define KEXEC_ARCH_ARM64_OPTIONS_H + +//#define OPT_ARCH_MAX ((OPT_MAX)+0) + +#define OPT_APPEND ((OPT_MAX)+0) +#define OPT_RAMDISK ((OPT_MAX)+1) +#define OPT_DTB ((OPT_MAX)+2) + +#define OPT_ARCH_MAX ((OPT_MAX)+3) + + +#define KEXEC_ARCH_OPTIONS \ + KEXEC_OPTIONS \ + { "append", 1, NULL, OPT_APPEND }, \ + { "command-line", 1, NULL, OPT_APPEND }, \ + { "dtb", 1, NULL, OPT_DTB }, \ + { "initrd", 1, NULL, OPT_RAMDISK }, \ + { "ramdisk", 1, NULL, OPT_RAMDISK }, \ + +#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR /* Only accept long arch options. */ + +#define KEXEC_ALL_OPTIONS KEXEC_ARCH_OPTIONS +#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR + +#endif /* KEXEC_ARCH_ARM64_OPTIONS_H */ --- /dev/null +++ b/kexec/arch/arm64/kexec-arm64.c @@ -0,0 +1,177 @@ +/* + * ARM64 kexec support. + */ + +#define _GNU_SOURCE + +#include +#include + +//#include + +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "kexec-arm64.h" + + +void arch_usage(void) +{ + fprintf(stderr, "%s:%d: ->\n", __func__, __LINE__); + + printf( +" --append=STRING Set the kernel command line to STRING.\n" +" --command-line=STRING Set the kernel command line to STRING.\n" +" --dtb=FILE Use FILE as the device tree blob.\n" +" --initrd=FILE Use FILE as the kernel initial ramdisk.\n" +" --ramdisk=FILE Use FILE as the kernel initial ramdisk.\n"); + + fprintf(stderr, "%s:%d: <-\n", __func__, __LINE__); +} + +int arch_process_options(int UNUSED(argc), char **UNUSED(argv)) +{ + fprintf(stderr, "%s:%d: do\n", __func__, __LINE__); + return 0; +} + +const struct arch_map_entry arches[] = { + { "aarch64", KEXEC_ARCH_ARM64 }, + { NULL, 0 }, +}; + +void arch_update_purgatory(struct kexec_info *UNUSED(info)) +{ + fprintf(stderr, "%s:%d: do\n", __func__, __LINE__); +} + +unsigned long virt_to_phys(unsigned long addr) +{ + fprintf(stderr, "%s:%d: %016lx -> %016lx\n", __func__, __LINE__, addr, + addr + 0x080000000UL); + return addr + 0x080000000UL; +} + +void add_segment(struct kexec_info *info, const void *buf, size_t bufsz, + unsigned long base, size_t memsz) +{ + fprintf(stderr, "%s:%d: ->\n", __func__, __LINE__); + add_segment_phys_virt(info, buf, bufsz, base, memsz, 1); + fprintf(stderr, "%s:%d: <-\n", __func__, __LINE__); +} + +static int get_memory_ranges_1(struct memory_range **range, int *ranges, + unsigned long kexec_flags) +{ + static struct memory_range memory_range[KEXEC_SEGMENT_MAX]; + const char *iomem; + int range_count = 0; + char line[MAX_LINE]; + FILE *fp; + + iomem = proc_iomem(); + fp = fopen(iomem, "r"); + + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + iomem, strerror(errno)); + return -1; + } + + dbgprintf("memory ranges:\n"); + + while(fgets(line, sizeof(line), fp) != 0) { + struct memory_range r; + char *str; + int consumed; + + if (range_count >= KEXEC_SEGMENT_MAX) + break; + + if (sscanf(line, "%Lx-%Lx : %n", &r.start, &r.end, &consumed) + != 2) + continue; + + str = line + consumed; + r.end++; + + if (memcmp(str, "System RAM\n", 11)) { + dbgprintf(" Skip: %016Lx - %016Lx : %s", r.start, r.end, + str); + continue; + } + + r.type = RANGE_RAM; + memory_range[range_count] = r; + range_count++; + + dbgprintf(" Add: %016Lx - %016Lx : %s", r.start, r.end, str); + } + + fclose(fp); + *range = memory_range; + *ranges = range_count; + + return 0; +} + +static int get_memory_ranges_2(struct memory_range **range, int *ranges, + unsigned long UNUSED(kexec_flags)) +{ + static struct memory_range memory_range[2]; + + memory_range[0].start = 0x080000000; + memory_range[0].end = 0x100000000; + memory_range[0].type = RANGE_RAM; + + memory_range[1].start = 0x900000000; + memory_range[1].end = 0x880000000; + memory_range[1].type = RANGE_RAM; + + *range = memory_range; + *ranges = sizeof(memory_range) / sizeof(memory_range[0]); + + return 0; +} + +int get_memory_ranges(struct memory_range **range, int *ranges, + unsigned long kexec_flags) +{ + /* FIXME: Should get this info from device tree. */ + + return get_memory_ranges_1(range, ranges, kexec_flags); +} + +struct file_type file_type[] = { + { "elf-arm64", elf_arm64_probe, elf_arm64_load, elf_arm64_usage }, +}; + +int file_types = sizeof(file_type) / sizeof(file_type[0]); + +int arch_compat_trampoline(struct kexec_info *info) +{ + fprintf(stderr, "%s:%d: do\n", __func__, __LINE__); + return 0; +} + +void arch_reuse_initrd(void) +{ +} + +int machine_verify_elf_rel(struct mem_ehdr *ehdr) +{ + (void)ehdr; + + fprintf(stderr, "%s:%d: do\n", __func__, __LINE__); + return 0; +} + +void machine_apply_elf_rel(struct mem_ehdr *ehdr, unsigned long r_type, + void *location, unsigned long address, unsigned long value) +{ + (void)ehdr; + (void)r_type; + (void)location; + (void)address; + (void)value; + fprintf(stderr, "%s:%d: do\n", __func__, __LINE__); +} --- /dev/null +++ b/kexec/arch/arm64/kexec-arm64.h @@ -0,0 +1,20 @@ +/* + * ARM64 kexec support. + */ + +#if !defined(KEXEC_ARM64_H) +#define KEXEC_ARM64_H + +/* #include FIXME: this is broken */ +#include + +#include "../../kexec.h" + +#define KEXEC_SEGMENT_MAX 16 /* FIXME: this should come from */ + +int elf_arm64_probe(const char *buf, off_t len); +int elf_arm64_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void elf_arm64_usage(void); + +#endif \ No newline at end of file --- /dev/null +++ b/kexec/arch/arm64/kexec-elf-arm64.c @@ -0,0 +1,114 @@ +/* + * ARM64 kexec support. + */ + +#define _GNU_SOURCE + +#include +#include + +#include "../../kexec-syscall.h" + +#include "kexec-arm64.h" +#include "arch/options.h" + +#if !defined(EM_AARCH64) +# define EM_AARCH64 183 +#endif + +int elf_arm64_probe(const char *buf, off_t len) +{ + int result; + struct mem_ehdr ehdr; + + fprintf(stderr, "%s:%d: ->\n", __func__, __LINE__); + + result = build_elf_exec_info(buf, len, &ehdr, 0); + + if (result < 0) { + dbgprintf("Not an ELF executable\n"); + goto out; + } + + if (ehdr.e_machine != EM_AARCH64) { + dbgprintf("Not an AARCH64 executable\n"); + result = -1; + goto out; + } + + result = 0; + +out: + free_elf_info(&ehdr); + fprintf(stderr, "%s:%d: <-\n", __func__, __LINE__); + return result; +} + +int elf_arm64_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { 0 } + }; + static const char short_options[] = KEXEC_OPT_STR ""; + const char *command_line = NULL; + unsigned int command_line_len = 0; + const char *ramdisk = NULL; + const char *dtb = NULL; + int opt; + struct mem_ehdr ehdr; + int result; + + fprintf(stderr, "%s:%d: ->\n", __func__, __LINE__); + + while ((opt = getopt_long(argc, argv, short_options, options, 0)) + != -1) { + switch (opt) { + default: + if (opt < OPT_MAX) /* Ignore core options */ + break; + case OPT_APPEND: + command_line = optarg; + command_line_len = strlen(command_line) + 1; + break; + case OPT_RAMDISK: + ramdisk = optarg; + break; + case OPT_DTB: + dtb = optarg; + break; + } + } + + fprintf(stderr, "%s:%d: command_line: %s\n", __func__, __LINE__, command_line); + fprintf(stderr, "%s:%d: ramdisk: %s\n", __func__, __LINE__, ramdisk); + fprintf(stderr, "%s:%d: dtb: %s\n", __func__, __LINE__, dtb); + + if (info->kexec_flags & KEXEC_ON_CRASH) { + fprintf(stderr, "kexec: kdump not yet supported on arm64\n"); + return -1; + } + + result = build_elf_exec_info(buf, len, &ehdr, 0); + + if (result < 0) { + free_elf_info(&ehdr); + fprintf(stderr, "%s:%d: free_elf_info failed\n", __func__, + __LINE__); + return result; + } + + elf_exec_build_load(info, &ehdr, buf, len, 0); + + info->entry = (void*)0x80080000UL; // FIXME + + fprintf(stderr, "%s:%d: <-\n", __func__, __LINE__); + return 0; +} + +void elf_arm64_usage(void) +{ + fprintf(stderr, "%s:%d: ->\n", __func__, __LINE__); + fprintf(stderr, "%s:%d: <-\n", __func__, __LINE__); +} --- a/kexec/kexec-syscall.h +++ b/kexec/kexec-syscall.h @@ -39,8 +39,8 @@ #ifdef __s390__ #define __NR_kexec_load 277 #endif -#ifdef __arm__ -#define __NR_kexec_load __NR_SYSCALL_BASE + 347 +#if defined(__arm__) || defined(__arm64__) +#define __NR_kexec_load __NR_SYSCALL_BASE + 347 #endif #if defined(__mips__) #define __NR_kexec_load 4311 @@ -108,6 +108,8 @@ static inline long kexec_file_load(int k #define KEXEC_ARCH_PPC64 (21 << 16) #define KEXEC_ARCH_IA_64 (50 << 16) #define KEXEC_ARCH_ARM (40 << 16) +#define KEXEC_ARCH_ARM64 (183 << 16) +/* #define KEXEC_ARCH_AARCH64 (183 << 16) */ #define KEXEC_ARCH_S390 (22 << 16) #define KEXEC_ARCH_SH (42 << 16) #define KEXEC_ARCH_MIPS_LE (10 << 16) @@ -153,5 +155,8 @@ static inline long kexec_file_load(int k #ifdef __m68k__ #define KEXEC_ARCH_NATIVE KEXEC_ARCH_68K #endif +#if defined(__arm64__) +#define KEXEC_ARCH_NATIVE KEXEC_ARCH_ARM64 +#endif #endif /* KEXEC_SYSCALL_H */ --- a/kexec/kexec.c +++ b/kexec/kexec.c @@ -664,6 +664,8 @@ static int my_load(const char *type, int memset(&info, 0, sizeof(info)); info.kexec_flags = kexec_flags; + fprintf(stderr, "%s:%d: do\n", __func__, __LINE__); + result = 0; if (argc - fileind <= 0) { fprintf(stderr, "No kernel specified\n"); --- /dev/null +++ b/purgatory/arch/arm64/Makefile @@ -0,0 +1,7 @@ +# +# Purgatory arm64 +# + +arm64_PURGATORY_SRCS = + +dist += purgatory/arch/arm64/Makefile $(arm64_PURGATORY_SRCS)