/* * kexec: Linux boots Linux * * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com) * Copyright (C) 2005 R Sharada (sharada@in.ibm.com), IBM Corporation * * 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-syscall.h" #include "kexec-ppc64.h" #include "../../fs2dt.h" #include "crashdump-ppc64.h" #include static struct memory_range *exclude_range = NULL; static struct memory_range *memory_range = NULL; static struct memory_range *base_memory_range = NULL; static uint64_t rma_top; uint64_t memory_max = 0; uint64_t memory_limit; static int nr_memory_ranges, nr_exclude_ranges; uint64_t crash_base, crash_size; unsigned int rtas_base, rtas_size; uint64_t opal_base, opal_size; int max_memory_ranges; static void cleanup_memory_ranges(void) { if (memory_range) free(memory_range); if (base_memory_range) free(base_memory_range); if (exclude_range) free(exclude_range); if (usablemem_rgns.ranges) free(usablemem_rgns.ranges); } /* * Allocate memory for various data structures used to hold * values of different memory ranges */ static int alloc_memory_ranges(void) { int memory_range_len; memory_range_len = sizeof(struct memory_range) * max_memory_ranges; memory_range = (struct memory_range *) malloc(memory_range_len); if (!memory_range) return -1; base_memory_range = (struct memory_range *) malloc(memory_range_len); if (!base_memory_range) goto err1; exclude_range = (struct memory_range *) malloc(memory_range_len); if (!exclude_range) goto err1; usablemem_rgns.ranges = (struct memory_range *) malloc(memory_range_len); if (!(usablemem_rgns.ranges)) goto err1; memset(memory_range, 0, memory_range_len); memset(base_memory_range, 0, memory_range_len); memset(exclude_range, 0, memory_range_len); memset(usablemem_rgns.ranges, 0, memory_range_len); return 0; err1: fprintf(stderr, "memory range structure allocation failure\n"); cleanup_memory_ranges(); return -1; } static int realloc_memory_ranges(void) { size_t memory_range_len; max_memory_ranges++; memory_range_len = sizeof(struct memory_range) * max_memory_ranges; memory_range = (struct memory_range *) realloc(memory_range, memory_range_len); if (!memory_range) goto err; base_memory_range = (struct memory_range *) realloc(base_memory_range, memory_range_len); if (!base_memory_range) goto err; exclude_range = (struct memory_range *) realloc(exclude_range, memory_range_len); if (!exclude_range) goto err; usablemem_rgns.ranges = (struct memory_range *) realloc(usablemem_rgns.ranges, memory_range_len); if (!(usablemem_rgns.ranges)) goto err; return 0; err: fprintf(stderr, "memory range structure re-allocation failure\n"); return -1; } static void add_base_memory_range(uint64_t start, uint64_t end) { base_memory_range[nr_memory_ranges].start = start; base_memory_range[nr_memory_ranges].end = end; base_memory_range[nr_memory_ranges].type = RANGE_RAM; nr_memory_ranges++; if (nr_memory_ranges >= max_memory_ranges) realloc_memory_ranges(); dbgprintf("%016llx-%016llx : %x\n", base_memory_range[nr_memory_ranges-1].start, base_memory_range[nr_memory_ranges-1].end, base_memory_range[nr_memory_ranges-1].type); } static int get_dyn_reconf_base_ranges(void) { uint64_t start, end; uint64_t size; char fname[128], buf[32]; FILE *file; unsigned int i; int n; strcpy(fname, "/proc/device-tree/"); strcat(fname, "ibm,dynamic-reconfiguration-memory/ibm,lmb-size"); if ((file = fopen(fname, "r")) == NULL) { perror(fname); return -1; } if (fread(buf, 1, 8, file) != 8) { perror(fname); fclose(file); return -1; } /* * lmb_size, num_of_lmb_sets(global variables) are * initialized once here. */ size = lmb_size = be64_to_cpu(((uint64_t *)buf)[0]); fclose(file); strcpy(fname, "/proc/device-tree/"); strcat(fname, "ibm,dynamic-reconfiguration-memory/ibm,dynamic-memory"); if ((file = fopen(fname, "r")) == NULL) { strcat(fname, "-v2"); if ((file = fopen(fname, "r")) == NULL) { perror(fname); return -1; } is_dyn_mem_v2 = 1; } /* first 4 bytes tell the number of lmb set entries */ if (fread(buf, 1, 4, file) != 4) { perror(fname); fclose(file); return -1; } num_of_lmb_sets = be32_to_cpu(((unsigned int *)buf)[0]); for (i = 0; i < num_of_lmb_sets; i++) { if ((n = fread(buf, 1, LMB_ENTRY_SIZE, file)) < 0) { perror(fname); fclose(file); return -1; } if (nr_memory_ranges >= max_memory_ranges) { fclose(file); return -1; } /* * If the property is ibm,dynamic-memory-v2, the first 4 bytes * tell the number of sequential LMBs in this entry. */ if (is_dyn_mem_v2) size = be32_to_cpu(((unsigned int *)buf)[0]) * lmb_size; start = be64_to_cpu(*((uint64_t *)&buf[DRCONF_ADDR])); end = start + size; add_base_memory_range(start, end); } fclose(file); return 0; } /* Sort the base ranges in memory - this is useful for ensuring that our * ranges are in ascending order, even if device-tree read of memory nodes * is done differently. Also, could be used for other range coalescing later */ static int sort_base_ranges(void) { int i, j; unsigned long long tstart, tend; for (i = 0; i < nr_memory_ranges - 1; i++) { for (j = 0; j < nr_memory_ranges - i - 1; j++) { if (base_memory_range[j].start > base_memory_range[j+1].start) { tstart = base_memory_range[j].start; tend = base_memory_range[j].end; base_memory_range[j].start = base_memory_range[j+1].start; base_memory_range[j].end = base_memory_range[j+1].end; base_memory_range[j+1].start = tstart; base_memory_range[j+1].end = tend; } } } return 0; } /* Get base memory ranges */ static int get_base_ranges(void) { uint64_t start, end; char device_tree[256] = "/proc/device-tree/"; char fname[256]; char buf[MAXBYTES]; DIR *dir, *dmem; FILE *file; struct dirent *dentry, *mentry; int n; if ((dir = opendir(device_tree)) == NULL) { perror(device_tree); return -1; } while ((dentry = readdir(dir)) != NULL) { if (!strncmp(dentry->d_name, "ibm,dynamic-reconfiguration-memory", 35)) { get_dyn_reconf_base_ranges(); continue; } if (strncmp(dentry->d_name, "memory@", 7) && strcmp(dentry->d_name, "memory")) continue; strcpy(fname, device_tree); strcat(fname, dentry->d_name); if ((dmem = opendir(fname)) == NULL) { perror(fname); closedir(dir); return -1; } while ((mentry = readdir(dmem)) != NULL) { if (strcmp(mentry->d_name, "reg")) continue; strcat(fname, "/reg"); if ((file = fopen(fname, "r")) == NULL) { perror(fname); closedir(dmem); closedir(dir); return -1; } if ((n = fread(buf, 1, MAXBYTES, file)) < 0) { perror(fname); fclose(file); closedir(dmem); closedir(dir); return -1; } if (nr_memory_ranges >= max_memory_ranges) { if (realloc_memory_ranges() < 0) break; } start = be64_to_cpu(((uint64_t *)buf)[0]); end = start + be64_to_cpu(((uint64_t *)buf)[1]); add_base_memory_range(start, end); fclose(file); } closedir(dmem); } closedir(dir); sort_base_ranges(); memory_max = base_memory_range[nr_memory_ranges - 1].end; dbgprintf("get base memory ranges:%d\n", nr_memory_ranges); return 0; } /* Sort the exclude ranges in memory */ static int sort_ranges(void) { int i, j; uint64_t tstart, tend; for (i = 0; i < nr_exclude_ranges - 1; i++) { for (j = 0; j < nr_exclude_ranges - i - 1; j++) { if (exclude_range[j].start > exclude_range[j+1].start) { tstart = exclude_range[j].start; tend = exclude_range[j].end; exclude_range[j].start = exclude_range[j+1].start; exclude_range[j].end = exclude_range[j+1].end; exclude_range[j+1].start = tstart; exclude_range[j+1].end = tend; } } } return 0; } void scan_reserved_ranges(unsigned long kexec_flags, int *range_index) { char fname[256], buf[16]; FILE *file; int i = *range_index; strcpy(fname, "/proc/device-tree/reserved-ranges"); file = fopen(fname, "r"); if (file == NULL) { if (errno != ENOENT) { perror(fname); return; } errno = 0; /* File not present. Non PowerKVM system. */ return; } /* * Each reserved range is an (address,size) pair, 2 cells each, * totalling 4 cells per range. */ while (fread(buf, sizeof(uint64_t) * 2, 1, file) == 1) { uint64_t base, size; base = be64_to_cpu(((uint64_t *)buf)[0]); size = be64_to_cpu(((uint64_t *)buf)[1]); exclude_range[i].start = base; exclude_range[i].end = base + size; i++; if (i >= max_memory_ranges) realloc_memory_ranges(); reserve(base, size); } fclose(file); *range_index = i; } /* Return 0 if fname/value valid, -1 otherwise */ int get_devtree_value(const char *fname, unsigned long long *value) { FILE *file; char buf[MAXBYTES]; int n = -1; if ((file = fopen(fname, "r"))) { n = fread(buf, 1, MAXBYTES, file); fclose(file); } if (n == sizeof(uint32_t)) *value = ((uint32_t *)buf)[0]; else if (n == sizeof(uint64_t)) *value = ((uint64_t *)buf)[0]; else { fprintf(stderr, "%s node has invalid size: %d\n", fname, n); return -1; } return 0; } /* Get devtree details and create exclude_range array * Also create usablemem_ranges for KEXEC_ON_CRASH */ static int get_devtree_details(unsigned long kexec_flags) { uint64_t rma_base = -1, base; uint64_t tce_base; unsigned int tce_size; uint64_t htab_base, htab_size; uint64_t kernel_end; uint64_t initrd_start, initrd_end; char buf[MAXBYTES]; char device_tree[256] = "/proc/device-tree/"; char fname[256]; DIR *dir, *cdir; FILE *file; struct dirent *dentry; struct stat fstat; int n, i = 0; if ((dir = opendir(device_tree)) == NULL) { perror(device_tree); return -1; } scan_reserved_ranges(kexec_flags, &i); while ((dentry = readdir(dir)) != NULL) { if (strncmp(dentry->d_name, "chosen", 6) && strncmp(dentry->d_name, "memory@", 7) && strcmp(dentry->d_name, "memory") && strncmp(dentry->d_name, "pci@", 4) && strncmp(dentry->d_name, "rtas", 4) && strncmp(dentry->d_name, "ibm,opal", 8)) continue; strcpy(fname, device_tree); strcat(fname, dentry->d_name); if ((cdir = opendir(fname)) == NULL) { perror(fname); goto error_opendir; } if (strncmp(dentry->d_name, "chosen", 6) == 0) { strcat(fname, "/linux,kernel-end"); if ((file = fopen(fname, "r")) == NULL) { perror(fname); goto error_opencdir; } if (fread(&kernel_end, sizeof(uint64_t), 1, file) != 1) { perror(fname); goto error_openfile; } fclose(file); kernel_end = be64_to_cpu(kernel_end); /* Add kernel memory to exclude_range */ exclude_range[i].start = 0x0UL; exclude_range[i].end = kernel_end; i++; if (i >= max_memory_ranges) realloc_memory_ranges(); if (kexec_flags & KEXEC_ON_CRASH) { memset(fname, 0, sizeof(fname)); strcpy(fname, device_tree); strcat(fname, dentry->d_name); strcat(fname, "/linux,crashkernel-base"); if ((file = fopen(fname, "r")) == NULL) { perror(fname); goto error_opencdir; } if (fread(&crash_base, sizeof(uint64_t), 1, file) != 1) { perror(fname); goto error_openfile; } fclose(file); crash_base = be64_to_cpu(crash_base); memset(fname, 0, sizeof(fname)); strcpy(fname, device_tree); strcat(fname, dentry->d_name); strcat(fname, "/linux,crashkernel-size"); if ((file = fopen(fname, "r")) == NULL) { perror(fname); goto error_opencdir; } if (fread(&crash_size, sizeof(uint64_t), 1, file) != 1) { perror(fname); goto error_openfile; } fclose(file); crash_size = be64_to_cpu(crash_size); if (crash_base > mem_min) mem_min = crash_base; if (crash_base + crash_size < mem_max) mem_max = crash_base + crash_size; add_usable_mem_rgns(0, crash_base + crash_size); reserve(KDUMP_BACKUP_LIMIT, crash_base-KDUMP_BACKUP_LIMIT); } /* * Read the first kernel's memory limit. * If the first kernel is booted with mem= option then * it would export "linux,memory-limit" file * reflecting value for the same. */ memset(fname, 0, sizeof(fname)); strcpy(fname, device_tree); strcat(fname, dentry->d_name); strcat(fname, "/linux,memory-limit"); if ((file = fopen(fname, "r")) == NULL) { if (errno != ENOENT) { perror(fname); goto error_opencdir; } errno = 0; /* * File not present. * fall through. On older kernel this file * is not present. */ } else { if (fread(&memory_limit, sizeof(uint64_t), 1, file) != 1) { perror(fname); goto error_openfile; } fclose(file); memory_limit = be64_to_cpu(memory_limit); } memset(fname, 0, sizeof(fname)); strcpy(fname, device_tree); strcat(fname, dentry->d_name); strcat(fname, "/linux,htab-base"); if ((file = fopen(fname, "r")) == NULL) { closedir(cdir); if (errno == ENOENT) { /* Non LPAR */ errno = 0; continue; } perror(fname); goto error_opendir; } if (fread(&htab_base, sizeof(uint64_t), 1, file) != 1) { perror(fname); goto error_openfile; } fclose(file); htab_base = be64_to_cpu(htab_base); memset(fname, 0, sizeof(fname)); strcpy(fname, device_tree); strcat(fname, dentry->d_name); strcat(fname, "/linux,htab-size"); if ((file = fopen(fname, "r")) == NULL) { perror(fname); goto error_opencdir; } if (fread(&htab_size, sizeof(uint64_t), 1, file) != 1) { perror(fname); goto error_openfile; } fclose(file); htab_size = be64_to_cpu(htab_size); /* Add htab address to exclude_range - NON-LPAR only */ exclude_range[i].start = htab_base; exclude_range[i].end = htab_base + htab_size; i++; if (i >= max_memory_ranges) realloc_memory_ranges(); /* reserve the initrd_start and end locations. */ if (reuse_initrd) { memset(fname, 0, sizeof(fname)); strcpy(fname, device_tree); strcat(fname, dentry->d_name); strcat(fname, "/linux,initrd-start"); if ((file = fopen(fname, "r")) == NULL) { perror(fname); goto error_opencdir; } /* check for 4 and 8 byte initrd offset sizes */ if (stat(fname, &fstat) != 0) { perror(fname); goto error_openfile; } if (fread(&initrd_start, fstat.st_size, 1, file) != 1) { perror(fname); goto error_openfile; } initrd_start = be64_to_cpu(initrd_start); fclose(file); memset(fname, 0, sizeof(fname)); strcpy(fname, device_tree); strcat(fname, dentry->d_name); strcat(fname, "/linux,initrd-end"); if ((file = fopen(fname, "r")) == NULL) { perror(fname); goto error_opencdir; } /* check for 4 and 8 byte initrd offset sizes */ if (stat(fname, &fstat) != 0) { perror(fname); goto error_openfile; } if (fread(&initrd_end, fstat.st_size, 1, file) != 1) { perror(fname); goto error_openfile; } initrd_end = be64_to_cpu(initrd_end); fclose(file); /* Add initrd address to exclude_range */ exclude_range[i].start = initrd_start; exclude_range[i].end = initrd_end; i++; if (i >= max_memory_ranges) realloc_memory_ranges(); } } /* chosen */ if (strncmp(dentry->d_name, "rtas", 4) == 0) { strcat(fname, "/linux,rtas-base"); if ((file = fopen(fname, "r")) == NULL) { perror(fname); goto error_opencdir; } if (fread(&rtas_base, sizeof(unsigned int), 1, file) != 1) { perror(fname); goto error_openfile; } fclose(file); rtas_base = be32_to_cpu(rtas_base); memset(fname, 0, sizeof(fname)); strcpy(fname, device_tree); strcat(fname, dentry->d_name); strcat(fname, "/rtas-size"); if ((file = fopen(fname, "r")) == NULL) { perror(fname); goto error_opencdir; } if (fread(&rtas_size, sizeof(unsigned int), 1, file) != 1) { perror(fname); goto error_openfile; } fclose(file); closedir(cdir); rtas_size = be32_to_cpu(rtas_size); /* Add rtas to exclude_range */ exclude_range[i].start = rtas_base; exclude_range[i].end = rtas_base + rtas_size; i++; if (i >= max_memory_ranges) realloc_memory_ranges(); if (kexec_flags & KEXEC_ON_CRASH) add_usable_mem_rgns(rtas_base, rtas_size); } /* rtas */ if (strncmp(dentry->d_name, "ibm,opal", 8) == 0) { strcat(fname, "/opal-base-address"); file = fopen(fname, "r"); if (file == NULL) { perror(fname); goto error_opencdir; } if (fread(&opal_base, sizeof(uint64_t), 1, file) != 1) { perror(fname); goto error_openfile; } opal_base = be64_to_cpu(opal_base); fclose(file); memset(fname, 0, sizeof(fname)); strcpy(fname, device_tree); strcat(fname, dentry->d_name); strcat(fname, "/opal-runtime-size"); file = fopen(fname, "r"); if (file == NULL) { perror(fname); goto error_opencdir; } if (fread(&opal_size, sizeof(uint64_t), 1, file) != 1) { perror(fname); goto error_openfile; } fclose(file); closedir(cdir); opal_size = be64_to_cpu(opal_size); /* Add OPAL to exclude_range */ exclude_range[i].start = opal_base; exclude_range[i].end = opal_base + opal_size; i++; if (i >= max_memory_ranges) realloc_memory_ranges(); if (kexec_flags & KEXEC_ON_CRASH) add_usable_mem_rgns(opal_base, opal_size); } /* ibm,opal */ if (!strncmp(dentry->d_name, "memory@", 7) || !strcmp(dentry->d_name, "memory")) { strcat(fname, "/reg"); if ((file = fopen(fname, "r")) == NULL) { perror(fname); goto error_opencdir; } if ((n = fread(buf, 1, MAXBYTES, file)) < 0) { perror(fname); goto error_openfile; } base = be64_to_cpu(((uint64_t *)buf)[0]); if (base < rma_base) { rma_base = base; rma_top = base + be64_to_cpu(((uint64_t *)buf)[1]); } fclose(file); closedir(cdir); } /* memory */ if (strncmp(dentry->d_name, "pci@", 4) == 0) { strcat(fname, "/linux,tce-base"); if ((file = fopen(fname, "r")) == NULL) { closedir(cdir); if (errno == ENOENT) { /* Non LPAR */ errno = 0; continue; } perror(fname); goto error_opendir; } if (fread(&tce_base, sizeof(uint64_t), 1, file) != 1) { perror(fname); goto error_openfile; } fclose(file); tce_base = be64_to_cpu(tce_base); memset(fname, 0, sizeof(fname)); strcpy(fname, device_tree); strcat(fname, dentry->d_name); strcat(fname, "/linux,tce-size"); if ((file = fopen(fname, "r")) == NULL) { perror(fname); goto error_opencdir; } if (fread(&tce_size, sizeof(unsigned int), 1, file) != 1) { perror(fname); goto error_openfile; } fclose(file); tce_size = be32_to_cpu(tce_size); /* Add tce to exclude_range - NON-LPAR only */ exclude_range[i].start = tce_base; exclude_range[i].end = tce_base + tce_size; i++; if (i >= max_memory_ranges) realloc_memory_ranges(); if (kexec_flags & KEXEC_ON_CRASH) add_usable_mem_rgns(tce_base, tce_size); closedir(cdir); } /* pci */ } closedir(dir); nr_exclude_ranges = i; sort_ranges(); int k; for (k = 0; k < i; k++) dbgprintf("exclude_range sorted exclude_range[%d] " "start:%llx, end:%llx\n", k, exclude_range[k].start, exclude_range[k].end); return 0; error_openfile: fclose(file); error_opencdir: closedir(cdir); error_opendir: closedir(dir); return -1; } /* Setup a sorted list of memory ranges. */ int setup_memory_ranges(unsigned long kexec_flags) { int i, j = 0; /* Get the base list of memory ranges from /proc/device-tree/memory * nodes. Build list of ranges to be excluded from valid memory */ if (get_base_ranges()) goto out; if (get_devtree_details(kexec_flags)) goto out; for (i = 0; i < nr_exclude_ranges; i++) { /* If first exclude range does not start with 0, include the * first hole of valid memory from 0 - exclude_range[0].start */ if (i == 0) { if (exclude_range[i].start != 0) { memory_range[j].start = 0; memory_range[j].end = exclude_range[i].start - 1; memory_range[j].type = RANGE_RAM; j++; if (j >= max_memory_ranges) realloc_memory_ranges(); } } /* i == 0 */ /* If the last exclude range does not end at memory_max, include * the last hole of valid memory from exclude_range[last].end - * memory_max */ if (i == nr_exclude_ranges - 1) { if (exclude_range[i].end < memory_max) { memory_range[j].start = exclude_range[i].end + 1; memory_range[j].end = memory_max; memory_range[j].type = RANGE_RAM; j++; if (j >= max_memory_ranges) realloc_memory_ranges(); /* Limit the end to rma_top */ if (memory_range[j-1].start >= rma_top) { j--; break; } if ((memory_range[j-1].start < rma_top) && (memory_range[j-1].end >= rma_top)) { memory_range[j-1].end = rma_top; break; } continue; } } /* i == nr_exclude_ranges - 1 */ /* contiguous exclude ranges - skip */ if (exclude_range[i+1].start == exclude_range[i].end + 1) continue; memory_range[j].start = exclude_range[i].end + 1; memory_range[j].end = exclude_range[i+1].start - 1; memory_range[j].type = RANGE_RAM; j++; if (j >= max_memory_ranges) realloc_memory_ranges(); /* Limit range to rma_top */ if (memory_range[j-1].start >= rma_top) { j--; break; } if ((memory_range[j-1].start < rma_top) && (memory_range[j-1].end >= rma_top)) { memory_range[j-1].end = rma_top; break; } } nr_memory_ranges = j; int k; for (k = 0; k < j; k++) dbgprintf("setup_memory_ranges memory_range[%d] " "start:%llx, end:%llx\n", k, memory_range[k].start, memory_range[k].end); return 0; out: cleanup_memory_ranges(); return -1; } /* Return a list of valid memory ranges */ int get_memory_ranges(struct memory_range **range, int *ranges, unsigned long kexec_flags) { /* allocate memory_range dynamically */ max_memory_ranges = 1; if (alloc_memory_ranges()) return -1; if (setup_memory_ranges(kexec_flags)) return -1; /* * copy the memory here, another realloc_memory_ranges might * corrupt the old memory */ *range = calloc(sizeof(struct memory_range), nr_memory_ranges); if (*range == NULL) return -1; memmove(*range, memory_range, sizeof(struct memory_range) * nr_memory_ranges); *ranges = nr_memory_ranges; dbgprintf("get memory ranges:%d\n", nr_memory_ranges); return 0; } struct file_type file_type[] = { { "elf-ppc64", elf_ppc64_probe, elf_ppc64_load, elf_ppc64_usage }, }; int file_types = sizeof(file_type) / sizeof(file_type[0]); void arch_usage(void) { fprintf(stderr, " --elf64-core-headers Prepare core headers in ELF64 format\n"); fprintf(stderr, " --dt-no-old-root Do not reuse old kernel root= param.\n" \ " while creating flatten device tree.\n"); } struct arch_options_t arch_options = { .core_header_type = CORE_TYPE_ELF64, }; int arch_process_options(int argc, char **argv) { /* We look for all options so getopt_long doesn't start reordering * argv[] before file_type[n].load() gets a look in. */ static const struct option options[] = { KEXEC_ALL_OPTIONS { 0, 0, NULL, 0 }, }; static const char short_options[] = KEXEC_ALL_OPT_STR; int opt; opterr = 0; /* Don't complain about unrecognized options here */ while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { switch(opt) { default: break; case OPT_ELF64_CORE: arch_options.core_header_type = CORE_TYPE_ELF64; break; case OPT_DT_NO_OLD_ROOT: dt_no_old_root = 1; break; } } /* Reset getopt for the next pass; called in other source modules */ opterr = 1; optind = 1; return 0; } const struct arch_map_entry arches[] = { /* We are running a 32-bit kexec-tools on 64-bit ppc64. * So pass KEXEC_ARCH_PPC64 here */ { "ppc64", KEXEC_ARCH_PPC64 }, { "ppc64le", KEXEC_ARCH_PPC64 }, { NULL, 0 }, }; int arch_compat_trampoline(struct kexec_info *UNUSED(info)) { return 0; } void arch_update_purgatory(struct kexec_info *UNUSED(info)) { }