blob: 611809f3ae1462dccd582d203439a4d281a2d741 [file] [log] [blame]
/*
* 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 <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <getopt.h>
#include "../../kexec.h"
#include "../../kexec-syscall.h"
#include "kexec-ppc64.h"
#include "../../fs2dt.h"
#include "crashdump-ppc64.h"
#include <arch/options.h>
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))
{
}