diff --git a/config/config.guess b/config/config.guess index c626f7a..b017a9e 100755 --- a/config/config.guess +++ b/config/config.guess @@ -937,6 +937,11 @@ EOF if test "$?" = 0 ; then LIBC=gnulibc1 ; fi echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; + sw_64:Linux:*:*) + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; diff --git a/config/config.sub b/config/config.sub index dae00e6..d6ff133 100755 --- a/config/config.sub +++ b/config/config.sub @@ -1241,6 +1241,7 @@ case $cpu-$vendor in | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \ | spu \ + | sw_64 \ | tahoe \ | tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \ | tron \ diff --git a/configure.ac b/configure.ac index 767574e..ff595f8 100644 --- a/configure.ac +++ b/configure.ac @@ -52,7 +52,7 @@ case $target_cpu in cris|crisv32 ) ARCH="cris" ;; - ia64|x86_64|alpha|m68k ) + ia64|x86_64|alpha|m68k|sw_64 ) ARCH="$target_cpu" ;; hppa*) diff --git a/include/elf.h b/include/elf.h index 1c8d2cc..93eb69d 100644 --- a/include/elf.h +++ b/include/elf.h @@ -267,6 +267,7 @@ typedef struct chances of collision with official or non-GNU unofficial values. */ #define EM_ALPHA 0x9026 +#define EM_SW_64 0x9916 /* Legal values for e_version (version). */ diff --git a/kexec/Makefile b/kexec/Makefile index 8a52e8d..5a102b7 100644 --- a/kexec/Makefile +++ b/kexec/Makefile @@ -90,6 +90,7 @@ include $(srcdir)/kexec/arch/ppc/Makefile include $(srcdir)/kexec/arch/ppc64/Makefile include $(srcdir)/kexec/arch/s390/Makefile include $(srcdir)/kexec/arch/sh/Makefile +include $(srcdir)/kexec/arch/sw_64/Makefile include $(srcdir)/kexec/arch/x86_64/Makefile include $(srcdir)/kexec/arch/hppa/Makefile include $(srcdir)/kexec/arch/loongarch/Makefile diff --git a/kexec/arch/sw_64/Makefile b/kexec/arch/sw_64/Makefile new file mode 100644 index 0000000..753c43c --- /dev/null +++ b/kexec/arch/sw_64/Makefile @@ -0,0 +1,16 @@ +# +# kexec sw_64 (linux booting linux) +# +sw_64_KEXEC_SRCS = kexec/arch/sw_64/kexec-sw_64.c +sw_64_KEXEC_SRCS += kexec/arch/sw_64/kexec-elf-sw_64.c +sw_64_KEXEC_SRCS += kexec/arch/sw_64/kexec-elf-rel-sw_64.c +sw_64_KEXEC_SRCS += kexec/arch/sw_64/crashdump-sw_64.c + +sw_64_ADD_BUFFER = +sw_64_ADD_SEGMENT = +sw_64_VIRT_TO_PHYS = + +dist += kexec/arch/sw_64/Makefile $(sw_64_KEXEC_SRCS) \ + kexec/arch/sw_64/kexec-sw_64.h \ + kexec/arch/sw_64/crashdump-sw_64.h \ + kexec/arch/sw_64/include/arch/options.h diff --git a/kexec/arch/sw_64/crashdump-sw_64.c b/kexec/arch/sw_64/crashdump-sw_64.c new file mode 100644 index 0000000..1e8237c --- /dev/null +++ b/kexec/arch/sw_64/crashdump-sw_64.c @@ -0,0 +1,352 @@ +/* + * kexec: Linux boots Linux + * + * 2005 (C) IBM Corporation. + * 2008 (C) MontaVista Software, Inc. + * + * 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). + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "../../crashdump.h" +#include "kexec-sw_64.h" +#include "crashdump-sw_64.h" +#include "unused.h" + +/* Stores a sorted list of RAM memory ranges for which to create elf headers. + * A separate program header is created for backup region */ +static struct memory_range crash_memory_range[CRASH_MAX_MEMORY_RANGES]; + +/* Not used currently but required by generic fs2dt code */ +struct memory_ranges usablemem_rgns; + +/* Memory region reserved for storing panic kernel and other data. */ +struct memory_range crash_reserved_mem; + +/* Read kernel physical load addr from the file returned by proc_iomem() + * (Kernel Code) and store in kexec_info */ +static int get_kernel_paddr(struct crash_elf_info *elf_info) +{ + uint64_t start; + + if (xen_present()) /* Kernel not entity mapped under Xen */ + return 0; + + if (parse_iomem_single("Kernel code\n", &start, NULL) == 0) { + elf_info->kern_paddr_start = start; + dbgprintf("kernel load physical addr start = 0x%" PRIu64 "\n", start); + return 0; + } + + fprintf(stderr, "Cannot determine kernel physical load addr\n"); + return -1; +} + +static int get_kernel_vaddr_and_size(struct crash_elf_info *elf_info, + unsigned long start_offset) +{ + uint64_t end; + + if (!elf_info->kern_paddr_start) + return -1; + + elf_info->kern_vaddr_start = elf_info->kern_paddr_start | + start_offset; + /* If "Kernel bss" exists, the kernel ends there, else fall + * through and say that it ends at "Kernel data" */ + if (parse_iomem_single("Kernel bss\n", NULL, &end) == 0 || + parse_iomem_single("Kernel data\n", NULL, &end) == 0) { + elf_info->kern_size = end - elf_info->kern_paddr_start; + dbgprintf("kernel_vaddr= 0x%llx paddr %llx\n", + elf_info->kern_vaddr_start, + elf_info->kern_paddr_start); + dbgprintf("kernel size = 0x%lx\n", elf_info->kern_size); + return 0; + } + fprintf(stderr, "Cannot determine kernel virtual load addr and size\n"); + return -1; +} + +/* Removes crash reserve region from list of memory chunks for whom elf program + * headers have to be created. Assuming crash reserve region to be a single + * continuous area fully contained inside one of the memory chunks */ +static int exclude_crash_reserve_region(int *nr_ranges) +{ + int i, j, tidx = -1; + unsigned long long cstart, cend; + struct memory_range temp_region = { + .start = 0, + .end = 0 + }; + + /* Crash reserved region. */ + cstart = crash_reserved_mem.start; + cend = crash_reserved_mem.end; + + for (i = 0; i < (*nr_ranges); i++) { + unsigned long long mstart, mend; + mstart = crash_memory_range[i].start; + mend = crash_memory_range[i].end; + if (cstart < mend && cend > mstart) { + if (cstart != mstart && cend != mend) { + /* Split memory region */ + crash_memory_range[i].end = cstart - 1; + temp_region.start = cend + 1; + temp_region.end = mend; + temp_region.type = RANGE_RAM; + tidx = i+1; + } else if (cstart != mstart) + crash_memory_range[i].end = cstart - 1; + else + crash_memory_range[i].start = cend + 1; + } + } + /* Insert split memory region, if any. */ + if (tidx >= 0) { + if (*nr_ranges == CRASH_MAX_MEMORY_RANGES) { + /* No space to insert another element. */ + fprintf(stderr, "Error: Number of crash memory ranges" + " excedeed the max limit\n"); + return -1; + } + for (j = (*nr_ranges - 1); j >= tidx; j--) + crash_memory_range[j+1] = crash_memory_range[j]; + crash_memory_range[tidx].start = temp_region.start; + crash_memory_range[tidx].end = temp_region.end; + crash_memory_range[tidx].type = temp_region.type; + (*nr_ranges)++; + } + return 0; +} +/* Reads the appropriate file and retrieves the SYSTEM RAM regions for whom to + * create Elf headers. Keeping it separate from get_memory_ranges() as + * requirements are different in the case of normal kexec and crashdumps. + * + * Normal kexec needs to look at all of available physical memory irrespective + * of the fact how much of it is being used by currently running kernel. + * Crashdumps need to have access to memory regions actually being used by + * running kernel. Expecting a different file/data structure than /proc/iomem + * to look into down the line. May be something like /proc/kernelmem or may + * be zone data structures exported from kernel. + */ +static int get_crash_memory_ranges(struct memory_range **range, int *ranges) +{ + const char iomem[] = "/proc/iomem"; + int memory_ranges = 0; + char line[MAX_LINE]; + FILE *fp; + unsigned long long start, end; + + fp = fopen(iomem, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + iomem, strerror(errno)); + return -1; + } + /* Separate segment for backup region */ + crash_memory_range[0].start = BACKUP_SRC_START; + crash_memory_range[0].end = BACKUP_SRC_END; + crash_memory_range[0].type = RANGE_RAM; + memory_ranges++; + + while (fgets(line, sizeof(line), fp) != 0) { + char *str; + int type, consumed, count; + if (memory_ranges >= CRASH_MAX_MEMORY_RANGES) + break; + count = sscanf(line, "%llx-%llx : %n", + &start, &end, &consumed); + if (count != 2) + continue; + str = line + consumed; + + /* Only Dumping memory of type System RAM. */ + if (memcmp(str, "System RAM\n", 11) == 0) { + type = RANGE_RAM; + } else if (memcmp(str, "Crash kernel\n", 13) == 0) { + /* Reserved memory region. New kernel can + * use this region to boot into. */ + crash_reserved_mem.start = start; + crash_reserved_mem.end = end; + crash_reserved_mem.type = RANGE_RAM; + continue; + } else + continue; + + if (start == BACKUP_SRC_START && end >= (BACKUP_SRC_END + 1)) + start = BACKUP_SRC_END + 1; + + crash_memory_range[memory_ranges].start = start; + crash_memory_range[memory_ranges].end = end; + crash_memory_range[memory_ranges].type = type; + memory_ranges++; + + /* Segregate linearly mapped region. */ + if (MAXMEM && (MAXMEM - 1) >= start && (MAXMEM - 1) <= end) { + crash_memory_range[memory_ranges - 1].end = MAXMEM - 1; + + /* Add segregated region. */ + crash_memory_range[memory_ranges].start = MAXMEM; + crash_memory_range[memory_ranges].end = end; + crash_memory_range[memory_ranges].type = type; + memory_ranges++; + } + } + fclose(fp); + + if (exclude_crash_reserve_region(&memory_ranges) < 0) + return -1; + + *range = crash_memory_range; + *ranges = memory_ranges; + return 0; +} + +/* Adds the appropriate mem= options to command line, indicating the + * memory region the new kernel can use to boot into. */ +static int cmdline_add_mem(char *cmdline, unsigned long addr, + unsigned long size) +{ + int cmdlen, len; + char str[50], *ptr; + + addr = addr/1024; + size = size/1024; + ptr = str; + strcpy(str, " mem="); + ptr += strlen(str); + ultoa(size, ptr); + strcat(str, "K@"); + ptr = str + strlen(str); + ultoa(addr, ptr); + strcat(str, "K"); + len = strlen(str); + cmdlen = strlen(cmdline) + len; + if (cmdlen > (COMMAND_LINE_SIZE - 1)) + die("Command line overflow\n"); + strcat(cmdline, str); + + return 0; +} + +/* Adds the elfcorehdr= command line parameter to command line. */ +static int cmdline_add_elfcorehdr(char *cmdline, unsigned long addr) +{ + int cmdlen, len, align = 1024; + char str[30], *ptr; + + /* Passing in elfcorehdr=xxxK format. Saves space required in cmdline. + * Ensure 1K alignment*/ + if (addr%align) + return -1; + addr = addr/align; + ptr = str; + strcpy(str, " elfcorehdr="); + ptr += strlen(str); + ultoa(addr, ptr); + strcat(str, "K"); + len = strlen(str); + cmdlen = strlen(cmdline) + len; + if (cmdlen > (COMMAND_LINE_SIZE - 1)) + die("Command line overflow\n"); + strcat(cmdline, str); + return 0; +} + +static struct crash_elf_info elf_info64 = { + class: ELFCLASS64, + data : ELFDATA2LSB, + machine : EM_SW_64, + page_offset : PAGE_OFFSET, + lowmem_limit : 0, /* 0 == no limit */ +}; + + +/* Loads additional segments in case of a panic kernel is being loaded. + * One segment for backup region, another segment for storing elf headers + * for crash memory image. + */ +int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline, + unsigned long UNUSED(max_addr), + unsigned long UNUSED(min_base)) +{ + void *tmp; + unsigned long sz, elfcorehdr; + int nr_ranges, align = 1024; + struct memory_range *mem_range; + struct crash_elf_info * elf_info = &elf_info64; + + if (get_kernel_paddr(elf_info)) + return -1; + + if (get_kernel_vaddr_and_size(elf_info, START_OFFSET)) + return -1; + + if (get_crash_memory_ranges(&mem_range, &nr_ranges) < 0) + return -1; + + info->backup_src_start = BACKUP_SRC_START; + info->backup_src_size = BACKUP_SRC_SIZE; + /* Create a backup region segment to store backup data*/ + sz = _ALIGN(BACKUP_SRC_SIZE, align); + tmp = xmalloc(sz); + memset(tmp, 0, sz); + info->backup_start = add_buffer(info, tmp, sz, sz, align, + crash_reserved_mem.start, + crash_reserved_mem.end, -1); + + if (crash_create_elf64_headers(info, elf_info, crash_memory_range, nr_ranges, + &tmp, &sz, ELF_CORE_HEADER_ALIGN) < 0) + return -1; + elfcorehdr = add_buffer(info, tmp, sz, sz, align, + crash_reserved_mem.start, + crash_reserved_mem.end, -1); + + /* + * backup segment is after elfcorehdr, so use elfcorehdr as top of + * kernel's available memory + */ + cmdline_add_mem(mod_cmdline, crash_reserved_mem.start, + elfcorehdr - crash_reserved_mem.start); + cmdline_add_elfcorehdr(mod_cmdline, elfcorehdr); + + dbgprintf("CRASH MEMORY RANGES:\n"); + dbgprintf("%016Lx-%016Lx\n", crash_reserved_mem.start, + crash_reserved_mem.end); + return 0; +} + +int is_crashkernel_mem_reserved(void) +{ + uint64_t start, end; + + return parse_iomem_single("Crash kernel\n", &start, &end) == 0 ? + (start != end) : 0; +} + +int get_crash_kernel_load_range(uint64_t *start, uint64_t *end) +{ + return parse_iomem_single("Crash kernel\n", start, end); +} diff --git a/kexec/arch/sw_64/crashdump-sw_64.h b/kexec/arch/sw_64/crashdump-sw_64.h new file mode 100644 index 0000000..65d92bc --- /dev/null +++ b/kexec/arch/sw_64/crashdump-sw_64.h @@ -0,0 +1,27 @@ +#ifndef CRASHDUMP_SW_64_H +#define CRASHDUMP_SW_64_H + +struct kexec_info; +int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline, + unsigned long max_addr, unsigned long min_base); + +#define PAGE_OFFSET 0xfff0000000000000UL +#define MAXMEM 0 +#define __pa(x) ((unsigned long)(X) & ~PAGE_OFFSET) + + +#define CRASH_MAX_MEMMAP_NR (KEXEC_MAX_SEGMENTS + 1) +#define CRASH_MAX_MEMORY_RANGES (MAX_MEMORY_RANGES + 2) + +#define COMMAND_LINE_SIZE 2048 + +/* Backup Region, First 1M of System RAM. */ +#define BACKUP_SRC_START 0x00000000 +#define BACKUP_SRC_END 0x000fffff +#define BACKUP_SRC_SIZE (BACKUP_SRC_END - BACKUP_SRC_START + 1) + +/* Kernel text size */ +#define SW64_KERNEL_TEXT_SIZE (512UL*1024*1024) + +extern struct arch_options_t arch_options; +#endif /* CRASHDUMP_SW_64_H */ diff --git a/kexec/arch/sw_64/include/arch/options.h b/kexec/arch/sw_64/include/arch/options.h new file mode 100644 index 0000000..1214d4b --- /dev/null +++ b/kexec/arch/sw_64/include/arch/options.h @@ -0,0 +1,41 @@ +#ifndef KEXEC_ARCH_SW_64_OPTIONS_H +#define KEXEC_ARCH_SW_64_OPTIONS_H + +#define OPT_ARCH_MAX (OPT_MAX+0) +#define OPT_APPEND (OPT_ARCH_MAX+0) +#define OPT_DTB (OPT_ARCH_MAX+1) +#define OPT_RAMDISK (OPT_ARCH_MAX+2) + +/* Options relevant to the architecture (excluding loader-specific ones), + * in this case none: + */ +#define KEXEC_ARCH_OPTIONS \ + KEXEC_OPTIONS \ + {"command-line", 1, 0, OPT_APPEND}, \ + {"append", 1, 0, OPT_APPEND}, \ + {"dtb", 1, 0, OPT_DTB }, \ + {"initrd", 1, 0, OPT_RAMDISK }, + + +#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR "" + +/* The following two #defines list ALL of the options added by all of the + * architecture's loaders. + * o main() uses this complete list to scan for its options, ignoring + * arch-specific/loader-specific ones. + * o Then, arch_process_options() uses this complete list to scan for its + * options, ignoring general/loader-specific ones. + * o Then, the file_type[n].load re-scans for options, using + * KEXEC_ARCH_OPTIONS plus its loader-specific options subset. + * Any unrecognised options cause an error here. + * + * This is done so that main()'s/arch_process_options()'s getopt_long() calls + * don't choose a kernel filename from random arguments to options they don't + * recognise -- as they now recognise (if not act upon) all possible options. + */ +#define KEXEC_ALL_OPTIONS \ + KEXEC_ARCH_OPTIONS + +#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR + +#endif /* KEXEC_ARCH_SW_64_OPTIONS_H */ diff --git a/kexec/arch/sw_64/kexec-elf-rel-sw_64.c b/kexec/arch/sw_64/kexec-elf-rel-sw_64.c new file mode 100644 index 0000000..aeaa966 --- /dev/null +++ b/kexec/arch/sw_64/kexec-elf-rel-sw_64.c @@ -0,0 +1,46 @@ +/* + * kexec-elf-rel-sw_64.c - kexec Elf relocation routines + * Copyright (C) 2007 Francesco Chiechi, Alessandro Rubini + * Copyright (C) 2007 Tvblob s.r.l. + * + * derived from ../ppc/kexec-elf-rel-ppc.c + * Copyright (C) 2004 Albert Herranz + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. +*/ + +#include +#include +#include "../../kexec.h" +#include "../../kexec-elf.h" + +int machine_verify_elf_rel(struct mem_ehdr *ehdr) +{ + if (ehdr->ei_data != ELFDATA2LSB) { + return 0; + } + if (ehdr->ei_class != ELFCLASS64) { + return 0; + } + if (ehdr->e_machine != EM_SW_64) { + return 0; + } + return 1; +} + +void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr), + struct mem_sym *UNUSED(sym), + unsigned long r_type, + void *UNUSED(location), + unsigned long UNUSED(address), + unsigned long UNUSED(value)) +{ + switch(r_type) { + + default: + die("Unknown rela relocation: %lu\n", r_type); + break; + } + return; +} diff --git a/kexec/arch/sw_64/kexec-elf-sw_64.c b/kexec/arch/sw_64/kexec-elf-sw_64.c new file mode 100644 index 0000000..ea99e26 --- /dev/null +++ b/kexec/arch/sw_64/kexec-elf-sw_64.c @@ -0,0 +1,212 @@ +/* + * kexec-elf-sw_64.c - kexec Elf loader for sw_64 + * Copyright (C) 2007 Francesco Chiechi, Alessandro Rubini + * Copyright (C) 2007 Tvblob s.r.l. + * + * derived from ../ppc/kexec-elf-ppc.c + * Copyright (C) 2004 Albert Herranz + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. +*/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "kexec-sw_64.h" +#include "crashdump-sw_64.h" +#include +#include "../../fs2dt.h" +#include "../../dt-ops.h" + +extern struct memory_range crash_reserved_mem; + +static const int probe_debug = 0; + +#define BOOTLOADER "kexec" +#define UPSZ(X) _ALIGN_UP(sizeof(X), 4) + +#define CMDLINE_PREFIX "kexec " +static char cmdline_buf[COMMAND_LINE_SIZE] = CMDLINE_PREFIX; + +int elf_sw_64_probe(const char *buf, off_t len) +{ + struct mem_ehdr ehdr; + int result; + result = build_elf_exec_info(buf, len, &ehdr, 0); + if (result < 0) { + goto out; + } + + /* Verify the architecuture specific bits */ + if (ehdr.e_machine != EM_SW_64) { + /* for a different architecture */ + if (probe_debug) { + fprintf(stderr, "Not for this architecture.\n"); + } + result = -1; + goto out; + } + result = 0; + out: + free_elf_info(&ehdr); + return result; +} + +void elf_sw_64_usage(void) +{ +} + +#define INITRD_START_OFFSET 0x100 +#define INITRD_SIZE_OFFSET 0x108 +#define COMMAND_LINE_OFFSET 0x1000 + +int elf_sw_64_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + struct mem_ehdr ehdr; + int command_line_len = 0; + char *crash_cmdline; + int result; + unsigned long cmdline_addr; + size_t i; + off_t dtb_length = 0; + //char *dtb_buf = NULL; + char *initrd_buf = NULL; + char * param_page = NULL; + unsigned long param_page_addr; + unsigned long long kernel_addr = 0, kernel_size = 0; + unsigned long pagesize = getpagesize(); + + /* + * ZERO_PGE of SW_64, offset is 0x0A000 from CONFIG_PHYSICAL_START + */ + param_page = xmalloc(pagesize); + memset(param_page, 0, pagesize); + /* Need to append some command line parameters internally in case of + * taking crash dumps. + */ + if (info->kexec_flags & KEXEC_ON_CRASH) { + crash_cmdline = xmalloc(COMMAND_LINE_SIZE); + memset((void *)crash_cmdline, 0, COMMAND_LINE_SIZE); + } else + crash_cmdline = NULL; + + if (info->kexec_flags & KEXEC_ON_CRASH) + result = build_elf_exec_info(buf, len, &ehdr, 1); + else + result = build_elf_exec_info(buf, len, &ehdr, 0); + + if (result < 0) + die("ELF exec parse failed\n"); + + /* Read in the PT_LOAD segments and remove CKSEG0 mask from address*/ + for (i = 0; i < ehdr.e_phnum; i++) { + struct mem_phdr *phdr; + phdr = &ehdr.e_phdr[i]; + if (phdr->p_type == PT_LOAD) { + phdr->p_paddr = virt_to_phys(phdr->p_paddr); + kernel_addr = phdr->p_paddr; + kernel_size = phdr->p_memsz; + if (!(kernel_size < SW64_KERNEL_TEXT_SIZE)) + die("The size of second kernrl is too large, \ + reserverd space is not enough!\n"); + } + } + /* Load the Elf data */ + result = elf_exec_load(&ehdr, info); + if (result < 0) + die("ELF exec load failed\n"); + + info->entry = (void *)virt_to_phys(ehdr.e_entry); + + if (arch_options.command_line) + command_line_len = strlen(arch_options.command_line) + 1; + + if (info->kexec_flags & KEXEC_ON_CRASH) { + result = load_crashdump_segments(info, crash_cmdline, 0, 0); + if (result < 0) { + free(crash_cmdline); + return -1; + } + } + + if (arch_options.command_line) + strncat(cmdline_buf, arch_options.command_line, command_line_len); + if (crash_cmdline) + strncat(cmdline_buf, crash_cmdline, + sizeof(crash_cmdline) - + strlen(crash_cmdline) - 1); + + if (info->kexec_flags & KEXEC_ON_CRASH) + /* In case of crashdump segment[0] is kernel. + * Put cmdline just after it. */ + cmdline_addr = (unsigned long)info->segment[0].mem + + info->segment[0].memsz; + else + cmdline_addr = 0; + + param_page_addr = (unsigned long)info->segment[0].mem - 0x6000; + /* sw_64 systems that have been converted to use device tree + * passed through UHI will use commandline in the DTB and + * the DTB passed as a separate buffer. Note that + * CMDLINE_PREFIX is skipped here intentionally, as it is + * used only in the legacy method */ +#if 0 + if (arch_options.dtb_file) { + dtb_buf = slurp_file(arch_options.dtb_file, &dtb_length); + } else { + create_flatten_tree(&dtb_buf, &dtb_length, cmdline_buf + strlen(CMDLINE_PREFIX)); + } +#endif + if (arch_options.initrd_file) { + initrd_buf = slurp_file(arch_options.initrd_file, &initrd_size); +#if 0 + /* Create initrd entries in dtb - although at this time + * they would not point to the correct location */ + dtb_set_initrd(&dtb_buf, &dtb_length, initrd_buf, initrd_buf + initrd_size); +#endif + unsigned long initrd_mem_min = _ALIGN_UP(kernel_addr + kernel_size + dtb_length, pagesize); + if (info->kexec_flags & KEXEC_ON_CRASH) + initrd_base = add_buffer(info, initrd_buf, initrd_size, + initrd_size, sizeof(void *), initrd_mem_min, crash_reserved_mem.end, 1); + else + initrd_base = add_buffer(info, initrd_buf, initrd_size, + initrd_size, sizeof(void *), initrd_mem_min, 0xfffffff, 1); + *(unsigned long *)(param_page + INITRD_START_OFFSET) = PAGE_OFFSET + initrd_mem_min; + *(unsigned long *)(param_page + INITRD_SIZE_OFFSET) = initrd_size; +#if 0 + /* Now that the buffer for initrd is prepared, update the dtb + * with an appropriate location */ + dtb_set_initrd(&dtb_buf, &dtb_length, initrd_base, initrd_base + initrd_size); +#endif + } + /* COMMAND_LINE offset 0x1000 from ZERO_PGE */ + memcpy(param_page + COMMAND_LINE_OFFSET, cmdline_buf, sizeof(cmdline_buf)); + add_buffer(info, param_page, pagesize, pagesize, sizeof(void *), + param_page_addr, param_page_addr + pagesize, 1); +#if 0 + add_buffer(info, cmdline_buf, sizeof(cmdline_buf), + sizeof(cmdline_buf), sizeof(void *), + cmdline_addr, crash_reserved_mem.end, 1); + + add_buffer(info, dtb_buf, dtb_length, dtb_length, 0, + _ALIGN_UP(kernel_addr + kernel_size, pagesize), + crash_reserved_mem.end, 1); +#endif + return 0; +} + diff --git a/kexec/arch/sw_64/kexec-sw_64.c b/kexec/arch/sw_64/kexec-sw_64.c new file mode 100644 index 0000000..b9fe106 --- /dev/null +++ b/kexec/arch/sw_64/kexec-sw_64.c @@ -0,0 +1,172 @@ +/* + * kexec-sw_64.c - kexec for sw_64 + * Copyright (C) 2007 Francesco Chiechi, Alessandro Rubini + * Copyright (C) 2007 Tvblob s.r.l. + * + * derived from ../ppc/kexec-sw_64.c + * Copyright (C) 2004, 2005 Albert Herranz + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "kexec-sw_64.h" +#include "crashdump-sw_64.h" +#include + +/* Currently not used but required by top-level fs2dt code */ +off_t initrd_base = 0; +off_t initrd_size = 0; + +static struct memory_range memory_range[MAX_MEMORY_RANGES]; + +/* Return a sorted list of memory ranges. */ +int get_memory_ranges(struct memory_range **range, int *ranges, + unsigned long UNUSED(kexec_flags)) +{ + int memory_ranges = 0; + + const char iomem[] = "/proc/iomem"; + char line[MAX_LINE]; + FILE *fp; + unsigned long long start, end; + char *str; + int type, consumed, count; + + fp = fopen(iomem, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", iomem, strerror(errno)); + return -1; + } + while (fgets(line, sizeof(line), fp) != 0) { + if (memory_ranges >= MAX_MEMORY_RANGES) + break; + count = sscanf(line, "%llx-%llx : %n", &start, &end, &consumed); + if (count != 2) + continue; + str = line + consumed; + end = end + 1; + if (memcmp(str, "System RAM\n", 11) == 0) { + type = RANGE_RAM; + } else if (memcmp(str, "reserved\n", 9) == 0) { + type = RANGE_RESERVED; + } else { + continue; + } + if (memory_ranges > 0 && + memory_range[memory_ranges - 1].end == start && + memory_range[memory_ranges - 1].type == type) { + memory_range[memory_ranges - 1].end = end; + } else { + memory_range[memory_ranges].start = start; + memory_range[memory_ranges].end = end; + memory_range[memory_ranges].type = type; + memory_ranges++; + } + } + fclose(fp); + *range = memory_range; + *ranges = memory_ranges; + return 0; +} + +struct file_type file_type[] = { + {"elf-sw_64", elf_sw_64_probe, elf_sw_64_load, elf_sw_64_usage}, +}; +int file_types = sizeof(file_type) / sizeof(file_type[0]); + +void arch_usage(void) +{ + printf( + " --command-line=STRING Set the kernel command line to STRING.\n" + " --append=STRING Set the kernel command line to STRING.\n" + " --dtb=FILE Use FILE as the device tree blob.\n" + " --initrd=FILE Use FILE as initial ramdisk.\n" + ); +} + +struct arch_options_t arch_options = { + .core_header_type = CORE_TYPE_ELF64, +}; + +int arch_process_options(int argc, char **argv) +{ + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { 0 }, + }; + static const char short_options[] = KEXEC_ARCH_OPT_STR; + int opt; + + while ((opt = getopt_long(argc, argv, short_options, + options, 0)) != -1) { + switch (opt) { + case OPT_APPEND: + arch_options.command_line = optarg; + break; + case OPT_DTB: + arch_options.dtb_file = optarg; + break; + case OPT_RAMDISK: + arch_options.initrd_file = optarg; + break; + default: + break; + } + } + + return 0; +} + +const struct arch_map_entry arches[] = { + /* For compatibility with older patches + * use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_SW_64 here. + */ + { "sw_64", KEXEC_ARCH_SW_64 }, + { NULL, 0 }, +}; + +int arch_compat_trampoline(struct kexec_info *UNUSED(info)) +{ + + return 0; +} + +void arch_update_purgatory(struct kexec_info *UNUSED(info)) +{ +} + +unsigned long virt_to_phys(unsigned long addr) +{ + return addr & ~PAGE_OFFSET; +} + +/* + * add_segment() should convert base to a physical address on sw_64, + * while the default is just to work with base as is */ +void add_segment(struct kexec_info *info, const void *buf, size_t bufsz, + unsigned long base, size_t memsz) +{ + add_segment_phys_virt(info, buf, bufsz, virt_to_phys(base), memsz, 1); +} + +/* + * add_buffer() should convert base to a physical address on sw_64, + * while the default is just to work with base as is */ +unsigned long add_buffer(struct kexec_info *info, const void *buf, + unsigned long bufsz, unsigned long memsz, + unsigned long buf_align, unsigned long buf_min, + unsigned long buf_max, int buf_end) +{ + return add_buffer_phys_virt(info, buf, bufsz, memsz, buf_align, + buf_min, buf_max, buf_end, 1); +} + diff --git a/kexec/arch/sw_64/kexec-sw_64.h b/kexec/arch/sw_64/kexec-sw_64.h new file mode 100644 index 0000000..1df0bcd --- /dev/null +++ b/kexec/arch/sw_64/kexec-sw_64.h @@ -0,0 +1,30 @@ +#ifndef KEXEC_SW_64_H +#define KEXEC_SW_64_H + +#include + +#define BOOT_BLOCK_VERSION 17 +#define BOOT_BLOCK_LAST_COMP_VERSION 16 + +#define MAX_MEMORY_RANGES 64 +#define MAX_LINE 160 + +#define CORE_TYPE_ELF32 1 +#define CORE_TYPE_ELF64 2 + +int elf_sw_64_probe(const char *buf, off_t len); +int elf_sw_64_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void elf_sw_64_usage(void); + +struct arch_options_t { + char *command_line; + char *dtb_file; + char *initrd_file; + int core_header_type; +}; + +extern struct memory_ranges usablemem_rgns; +extern off_t initrd_base, initrd_size; + +#endif /* KEXEC_SW_64_H */ diff --git a/kexec/crashdump.h b/kexec/crashdump.h index 18bd691..b8a231e 100644 --- a/kexec/crashdump.h +++ b/kexec/crashdump.h @@ -16,6 +16,8 @@ extern int get_xen_vmcoreinfo(uint64_t *addr, uint64_t *len); */ #define ELF_CORE_HEADER_ALIGN 1024 +#define START_OFFSET 0xfff0000000000000UL + /* structure passed to crash_create_elf32/64_headers() */ struct crash_elf_info { diff --git a/kexec/kexec-elf.c b/kexec/kexec-elf.c index be60bbd..62d8319 100644 --- a/kexec/kexec-elf.c +++ b/kexec/kexec-elf.c @@ -168,7 +168,8 @@ static int build_mem_elf32_ehdr(const char *buf, off_t len, struct mem_ehdr *ehd return 0; } -static int build_mem_elf64_ehdr(const char *buf, off_t len, struct mem_ehdr *ehdr) +static int build_mem_elf64_ehdr(const char *buf, off_t len, + struct mem_ehdr *ehdr, int64_t offset) { Elf64_Ehdr lehdr; if ((uintmax_t)len < (uintmax_t)sizeof(lehdr)) { @@ -210,7 +211,7 @@ static int build_mem_elf64_ehdr(const char *buf, off_t len, struct mem_ehdr *ehd ehdr->e_type = elf16_to_cpu(ehdr, lehdr.e_type); ehdr->e_machine = elf16_to_cpu(ehdr, lehdr.e_machine); ehdr->e_version = elf32_to_cpu(ehdr, lehdr.e_version); - ehdr->e_entry = elf64_to_cpu(ehdr, lehdr.e_entry); + ehdr->e_entry = elf64_to_cpu(ehdr, lehdr.e_entry + offset); ehdr->e_phoff = elf64_to_cpu(ehdr, lehdr.e_phoff); ehdr->e_shoff = elf64_to_cpu(ehdr, lehdr.e_shoff); ehdr->e_flags = elf32_to_cpu(ehdr, lehdr.e_flags); @@ -240,7 +241,8 @@ static int build_mem_elf64_ehdr(const char *buf, off_t len, struct mem_ehdr *ehd return 0; } -static int build_mem_ehdr(const char *buf, off_t len, struct mem_ehdr *ehdr) +static int build_mem_ehdr(const char *buf, off_t len, + struct mem_ehdr *ehdr, uint64_t offset) { unsigned char e_ident[EI_NIDENT]; int result; @@ -286,7 +288,7 @@ static int build_mem_ehdr(const char *buf, off_t len, struct mem_ehdr *ehdr) result = build_mem_elf32_ehdr(buf, len, ehdr); } else if (ehdr->ei_class == ELFCLASS64) { - result = build_mem_elf64_ehdr(buf, len, ehdr); + result = build_mem_elf64_ehdr(buf, len, ehdr, offset); } if (result < 0) { return result; @@ -335,7 +337,8 @@ static int build_mem_elf32_phdr(const char *buf, struct mem_ehdr *ehdr, int idx) return 0; } -static int build_mem_elf64_phdr(const char *buf, struct mem_ehdr *ehdr, int idx) +static int build_mem_elf64_phdr(const char *buf, + struct mem_ehdr *ehdr, int idx, uint64_t offset) { struct mem_phdr *phdr; const char *pbuf; @@ -356,8 +359,8 @@ static int build_mem_elf64_phdr(const char *buf, struct mem_ehdr *ehdr, int idx) } phdr->p_type = elf32_to_cpu(ehdr, lphdr.p_type); - phdr->p_paddr = elf64_to_cpu(ehdr, lphdr.p_paddr); - phdr->p_vaddr = elf64_to_cpu(ehdr, lphdr.p_vaddr); + phdr->p_paddr = elf64_to_cpu(ehdr, lphdr.p_paddr + offset); + phdr->p_vaddr = elf64_to_cpu(ehdr, lphdr.p_vaddr + offset); phdr->p_filesz = elf64_to_cpu(ehdr, lphdr.p_filesz); phdr->p_memsz = elf64_to_cpu(ehdr, lphdr.p_memsz); phdr->p_offset = elf64_to_cpu(ehdr, lphdr.p_offset); @@ -368,7 +371,7 @@ static int build_mem_elf64_phdr(const char *buf, struct mem_ehdr *ehdr, int idx) } static int build_mem_phdrs(const char *buf, off_t len, struct mem_ehdr *ehdr, - uint32_t flags) + uint32_t flags, uint64_t offset) { size_t phdr_size, mem_phdr_size, i; @@ -412,7 +415,7 @@ static int build_mem_phdrs(const char *buf, off_t len, struct mem_ehdr *ehdr, } else if (ehdr->ei_class == ELFCLASS64) { - result = build_mem_elf64_phdr(buf, ehdr, i); + result = build_mem_elf64_phdr(buf, ehdr, i, offset); } if (result < 0) { return result; @@ -515,14 +518,33 @@ static int build_mem_elf32_shdr(const char *buf, struct mem_ehdr *ehdr, int idx) return 0; } -static int build_mem_elf64_shdr(const char *buf, struct mem_ehdr *ehdr, int idx) +static int build_mem_elf64_shdr(const char *buf, struct mem_ehdr *ehdr, + int idx, uint64_t offset, + unsigned long long kernel_start, + unsigned long long crash_start) { struct mem_shdr *shdr; const char *sbuf; - int size_ok; + int size_ok, res; Elf64_Shdr lshdr; + uint64_t sh_off, sh_size, *content, count = 1; + uint32_t sh_type; sbuf = buf + ehdr->e_shoff + (idx * sizeof(lshdr)); shdr = &ehdr->e_shdr[idx]; + + sh_type = *(unsigned long *)(sbuf + 4); + sh_off = *(unsigned long long *)(sbuf + 24); + sh_size = *(unsigned long long *)(sbuf + 32); + content = (unsigned long long *)(buf + sh_off); + if (sh_type == SHT_PROGBITS || sh_type == SHT_SYMTAB) { + while (count < (sh_size / 8)) { + if (*(content + count) >= kernel_start + && *(content + count) < crash_start) + *(content + count) += offset; + count++; + } + } + memcpy(&lshdr, sbuf, sizeof(lshdr)); if ( (elf64_to_cpu(ehdr, lshdr.sh_flags) > UINT64_MAX) || @@ -585,7 +607,9 @@ static int build_mem_elf64_shdr(const char *buf, struct mem_ehdr *ehdr, int idx) } static int build_mem_shdrs(const char *buf, off_t len, struct mem_ehdr *ehdr, - uint32_t flags) + uint32_t flags, uint64_t offset, + unsigned long long kernel_start, + unsigned long long crash_start) { size_t shdr_size, mem_shdr_size, i; @@ -625,7 +649,9 @@ static int build_mem_shdrs(const char *buf, off_t len, struct mem_ehdr *ehdr, result = build_mem_elf32_shdr(buf, ehdr, i); } else if (ehdr->ei_class == ELFCLASS64) { - result = build_mem_elf64_shdr(buf, ehdr, i); + result = build_mem_elf64_shdr(buf, ehdr, i, + offset, kernel_start, + crash_start); } if (result < 0) { return result; @@ -759,19 +785,52 @@ int build_elf_info(const char *buf, off_t len, struct mem_ehdr *ehdr, uint32_t flags) { int result; - result = build_mem_ehdr(buf, len, ehdr); + const char iomem[] = "/proc/iomem"; + char line[160]; + uint64_t entry, offset = 0; + unsigned long long start, end, kernel_start, crash_start = 0; + + FILE *io_fp = fopen(iomem, "r"); + + if (!io_fp) { + fprintf(stderr, "Cannot open %s: %s\n", + iomem, strerror(errno)); + return -1; + } + while (fgets(line, sizeof(line), io_fp) != 0) { + char *str; + int type, consumed, count; + + count = sscanf(line, "%llx-%llx : %n", + &start, &end, &consumed); + if (count != 2) + continue; + str = line + consumed; + + if (memcmp(str, "Crash kernel\n", 13) == 0 && flags) { + crash_start = start + START_OFFSET + 0x10000; + break; + } + } + kernel_start = *(unsigned long long *)(buf + 24); + if (crash_start != 0) + offset = crash_start - kernel_start; + + flags = 0; + result = build_mem_ehdr(buf, len, ehdr, offset); if (result < 0) { return result; } if ((ehdr->e_phoff > 0) && (ehdr->e_phnum > 0)) { - result = build_mem_phdrs(buf, len, ehdr, flags); + result = build_mem_phdrs(buf, len, ehdr, flags, offset); if (result < 0) { free_elf_info(ehdr); return result; } } if ((ehdr->e_shoff > 0) && (ehdr->e_shnum > 0)) { - result = build_mem_shdrs(buf, len, ehdr, flags); + result = build_mem_shdrs(buf, len, ehdr, flags, + offset, kernel_start, crash_start); if (result < 0) { free_elf_info(ehdr); return result; diff --git a/kexec/kexec-syscall.h b/kexec/kexec-syscall.h index be6ccd5..7d837d5 100644 --- a/kexec/kexec-syscall.h +++ b/kexec/kexec-syscall.h @@ -54,6 +54,9 @@ #ifdef __alpha__ #define __NR_kexec_load 448 #endif +#ifdef __sw_64__ +#define __NR_kexec_load 448 +#endif #ifndef __NR_kexec_load #error Unknown processor architecture. Needs a kexec_load syscall number. #endif @@ -138,6 +141,7 @@ static inline long kexec_file_load(int kernel_fd, int initrd_fd, #define KEXEC_ARCH_MIPS ( 8 << 16) #define KEXEC_ARCH_CRIS (76 << 16) #define KEXEC_ARCH_LOONGARCH (258 << 16) +#define KEXEC_ARCH_SW_64 (0x9916UL << 16) #define KEXEC_MAX_SEGMENTS 16 diff --git a/kexec/kexec.c b/kexec/kexec.c index 0e92d96..b75705d 100644 --- a/kexec/kexec.c +++ b/kexec/kexec.c @@ -830,7 +830,8 @@ static int my_load(const char *type, int fileind, int argc, char **argv, fprintf(stderr, "entry = %p flags = 0x%lx\n", info.entry, info.kexec_flags); print_segments(stderr, &info); - } + } else + printf("kexec_load success!\n"); return result; }