[PATCH 2/4] get memory ranges
R Sharada
sharada at in.ibm.com
Wed Sep 14 22:46:45 EST 2005
This patch adds functionality to obtain the base memory ranges from
/proc/device-tree/memory at xxx nodes and exclude ranges not valid for
passing into kexec-tools. It adds the functionality of the get_memory_ranges
function.
Signed-off-by: R Sharada <sharada at in.ibm.com>
---
kexec-tools-1.101-sharada/kexec/arch/ppc64/Makefile | 2
kexec-tools-1.101-sharada/kexec/arch/ppc64/kexec-ppc64.c | 526 +++++++++++++++
2 files changed, 528 insertions(+)
diff -puN kexec/arch/ppc64/Makefile~get_memory_ranges kexec/arch/ppc64/Makefile
--- kexec-tools-1.101/kexec/arch/ppc64/Makefile~get_memory_ranges 2005-09-14 12:59:22.000000000 +0530
+++ kexec-tools-1.101-sharada/kexec/arch/ppc64/Makefile 2005-09-14 12:59:33.000000000 +0530
@@ -4,5 +4,7 @@
KEXEC_C_SRCS+= kexec/arch/ppc64/kexec-elf-rel-ppc64.c
KEXEC_C_SRCS+= kexec/arch/ppc64/kexec-zImage-ppc64.c
KEXEC_C_SRCS+= kexec/arch/ppc64/fs2dt.c
+KEXEC_C_SRCS+= kexec/arch/ppc64/kexec-elf-ppc64.c
+KEXEC_C_SRCS+= kexec/arch/ppc64/kexec-ppc64.c
KEXEC_S_SRCS+=
diff -puN /dev/null kexec/arch/ppc64/kexec-ppc64.c
--- /dev/null 2005-09-09 01:53:00.469998000 +0530
+++ kexec-tools-1.101-sharada/kexec/arch/ppc64/kexec-ppc64.c 2005-09-14 13:07:10.000000000 +0530
@@ -0,0 +1,526 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003-2005 Eric Biederman (ebiederm at xmission.com)
+ * Copyright (C) 2005 R Sharada (sharada at 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 <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <getopt.h>
+#include <sys/utsname.h>
+#include "../../kexec.h"
+#include "../../kexec-syscall.h"
+#include "kexec-ppc64.h"
+#include <arch/options.h>
+
+#define MAX_MEMORY_RANGES 64
+#define MAX_LINE 160
+#define MAXBYTES 128
+/* Platforms supported by kexec on PPC64 */
+#define PLATFORM_PSERIES 0x0100
+#define PLATFORM_PSERIES_LPAR 0x0101
+
+struct exclude_range {
+ unsigned long long start, end;
+};
+static struct exclude_range exclude_range[MAX_MEMORY_RANGES];
+
+static unsigned long long rmo_top;
+static unsigned int platform;
+static struct memory_range memory_range[MAX_MEMORY_RANGES];
+static struct memory_range base_memory_range[MAX_MEMORY_RANGES];
+unsigned long long memory_max = 0;
+static int nr_memory_ranges;
+static int nr_exclude_ranges;
+
+/* Get base memory ranges */
+static int get_base_ranges()
+{
+ int local_memory_ranges = 0;
+ char device_tree[256] = "/proc/device-tree/";
+ char fname[256];
+ char buf[MAXBYTES-1];
+ DIR *dir, *dmem;
+ FILE *file;
+ struct dirent *dentry, *mentry;
+ int n;
+ unsigned long long start, end;
+
+ if ((dir = opendir(device_tree)) == NULL) {
+ perror(device_tree);
+ return -1;
+ }
+ while ((dentry = readdir(dir)) != NULL) {
+ if (strncmp(dentry->d_name, "memory@", 7))
+ 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 (local_memory_ranges >= MAX_MEMORY_RANGES)
+ break;
+ base_memory_range[local_memory_ranges].start =
+ ((unsigned long long *)buf)[0];
+ base_memory_range[local_memory_ranges].end =
+ base_memory_range[local_memory_ranges].start +
+ ((unsigned long long *)buf)[1];
+ base_memory_range[local_memory_ranges].type = RANGE_RAM;
+ local_memory_ranges++;
+#if 0
+ fprintf(stderr, "%016Lx-%016Lx : %x\n", memory_range[local_memory_ranges-1].start, memory_range[local_memory_ranges-1].end, memory_range[local_memory_ranges].type);
+#endif
+ fclose(file);
+ }
+ memory_max = base_memory_range[local_memory_ranges - 1].end;
+ closedir(dmem);
+ }
+ closedir(dir);
+ nr_memory_ranges = local_memory_ranges;
+ fprintf(stderr, "get base memory ranges:%d\n", nr_memory_ranges);
+ return 0;
+}
+
+/* Sort the exclude ranges in memory */
+static int sort_ranges()
+{
+ int i, j;
+ unsigned long long 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;
+}
+
+/* Get devtree details and create exclude_range array */
+static int get_devtree_details()
+{
+ unsigned long long rmo_base;
+ unsigned long long tce_base;
+ unsigned int tce_size;
+ char buf[MAXBYTES-1];
+ char device_tree[256] = "/proc/device-tree/";
+ char fname[256];
+ DIR *dir, *cdir;
+ FILE *file;
+ struct dirent *dentry;
+ int n, i = 0;
+
+ if ((dir = opendir(device_tree)) == NULL) {
+ perror(device_tree);
+ return -1;
+ }
+
+ while ((dentry = readdir(dir)) != NULL) {
+ if (strncmp(dentry->d_name, "chosen", 6) &&
+ strncmp(dentry->d_name, "memory at 0", 8) &&
+ strncmp(dentry->d_name, "pci@", 4) &&
+ strncmp(dentry->d_name, "rtas", 4))
+ continue;
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ if ((cdir = opendir(fname)) == NULL) {
+ perror(fname);
+ closedir(dir);
+ return -1;
+ }
+
+ if (strncmp(dentry->d_name, "chosen", 6) == 0) {
+ /* get platform details from /chosen node */
+ strcat(fname, "/linux,platform");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ if (fread(&platform, sizeof(int), 1, file) != 1) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ fclose(file);
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/kernel_end");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ unsigned long long kernel_end;
+ if (fread(&kernel_end, sizeof(unsigned long), 1, file) != 1) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ /* Add kernel memory to exclude_range */
+ exclude_range[i].start = 0x0UL;
+ exclude_range[i].end = kernel_end;
+ i++;
+ /* if LPAR, no need to read any more from /chosen */
+ if (platform != PLATFORM_PSERIES) {
+ closedir(cdir);
+ continue;
+ }
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/htab_base");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ unsigned long long htab_base, htab_size;
+ if (fread(&htab_base, sizeof(unsigned long), 1, file) != 1) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/htab_size");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ if (fread(&htab_size, sizeof(unsigned long), 1, file) != 1) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ /* Add htab address to exclude_range - NON-LPAR only */
+ exclude_range[i].start = htab_base;
+ exclude_range[i].end = htab_base + htab_size;
+ i++;
+ } /* chosen */
+
+ if (strncmp(dentry->d_name, "rtas", 4) == 0) {
+ strcat(fname, "/linux,rtas-base");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ unsigned int rtas_base, rtas_size;
+ if (fread(&rtas_base, sizeof(unsigned int), 1, file) != 1) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ 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);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ if (fread(&rtas_size, sizeof(unsigned int), 1, file) != 1) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ closedir(cdir);
+ /* Add rtas to exclude_range */
+ exclude_range[i].start = rtas_base;
+ exclude_range[i].end = rtas_base + rtas_size;
+ i++;
+ } /* rtas */
+
+ if (strncmp(dentry->d_name, "memory at 0", 8) == 0) {
+ strcat(fname, "/reg");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ if ((n = fread(buf, 1, MAXBYTES, file)) < 0) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ rmo_base = ((unsigned long long *)buf)[0];
+ rmo_top = rmo_base + ((unsigned long long *)buf)[1];
+ if (platform == PLATFORM_PSERIES)
+ if (memory_max > 0x40000000UL? (rmo_top = 0x40000000UL) : (rmo_top = memory_max));
+ fclose(file);
+ closedir(cdir);
+ } /* memory */
+
+ if (strncmp(dentry->d_name, "pci@", 4) == 0) {
+ if (platform != PLATFORM_PSERIES) {
+ closedir(cdir);
+ continue;
+ }
+ strcat(fname, "/linux,tce-base");
+ if ((file = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ if (fread(&tce_base, sizeof(unsigned long), 1, file) != 1) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ 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);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ if (fread(&tce_size, sizeof(unsigned int), 1, file) != 1) {
+ perror(fname);
+ fclose(file);
+ closedir(cdir);
+ closedir(dir);
+ return -1;
+ }
+ /* Add tce to exclude_range - NON-LPAR only */
+ exclude_range[i].start = tce_base;
+ exclude_range[i].end = tce_base + tce_size;
+ i++;
+ closedir(cdir);
+ } /* pci */
+ }
+ closedir(dir);
+
+ nr_exclude_ranges = i;
+
+ sort_ranges();
+#if 0
+ int k;
+ for (k = 0; k < i; k++)
+ fprintf(stderr, "exclude_range sorted exclude_range[%d] start:%lx, end:%lx\n", k, exclude_range[k].start, exclude_range[k].end);
+#endif
+ return 0;
+}
+
+/* Setup a sorted list of memory ranges. */
+int setup_memory_ranges(void)
+{
+ 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
+ */
+
+ get_base_ranges();
+ get_devtree_details();
+
+ 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++;
+ }
+ } /* 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++;
+ /* Limit the end to rmo_top */
+ if (memory_range[j-1].start >= rmo_top) {
+ j--;
+ break;
+ }
+ if ((memory_range[j-1].start < rmo_top) &&
+ (memory_range[j-1].end >= rmo_top)) {
+ memory_range[j-1].end = rmo_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++;
+ /* Limit range to rmo_top */
+ if (memory_range[j-1].start >= rmo_top) {
+ j--;
+ break;
+ }
+ if ((memory_range[j-1].start < rmo_top) && (memory_range[j-1].end >= rmo_top)) {
+ memory_range[j-1].end = rmo_top;
+ break;
+ }
+ }
+ nr_memory_ranges = j;
+
+#if 0
+ int k;
+ for (k = 0; k < j; k++)
+ fprintf(stderr, "seup_memory_ranges memory_range[%d] start:%lx, end:%lx\n", k, memory_range[k].start, memory_range[k].end);
+#endif
+ return 0;
+}
+
+/* Return a list of valid memory ranges */
+int get_memory_ranges(struct memory_range **range, int *ranges)
+{
+ setup_memory_ranges();
+ *range = memory_range;
+ *ranges = nr_memory_ranges;
+ fprintf(stderr, "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)
+{
+}
+
+static struct {
+} arch_options = {
+};
+
+int arch_process_options(int argc, char **argv)
+{
+ static const struct option options[] = {
+ KEXEC_ARCH_OPTIONS
+ { 0, 0, NULL, 0 },
+ };
+ static const char short_options[] = KEXEC_ARCH_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;
+ }
+ }
+ /* Reset getopt for the next pass; called in other source modules */
+ opterr = 1;
+ optind = 1;
+ return 0;
+}
+
+int arch_compat_trampoline(struct kexec_info *info, unsigned long *flags)
+{
+ int result;
+ struct utsname utsname;
+ result = uname(&utsname);
+ if (result < 0) {
+ fprintf(stderr, "uname failed: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ if (strcmp(utsname.machine, "ppc64") == 0)
+ {
+ /* We are running a 32-bit kexec-tools on 64-bit ppc64.
+ * So pass KEXEC_ARCH_PPC64 here
+ */
+ *flags |= KEXEC_ARCH_PPC64;
+ }
+ else {
+ fprintf(stderr, "Unsupported machine type: %s\n",
+ utsname.machine);
+ return -1;
+ }
+ return 0;
+}
+
+void arch_update_purgatory(struct kexec_info *info)
+{
+}
_
More information about the Linuxppc64-dev
mailing list