* [PATCH v2 1/2] Fix kexec on powerpc32
@ 2010-05-12 7:08 Maxim Uvarov
2010-05-12 7:08 ` [PATCH v2 2/2] powerpc new toolchains fix (crt.S) Maxim Uvarov
0 siblings, 1 reply; 5+ messages in thread
From: Maxim Uvarov @ 2010-05-12 7:08 UTC (permalink / raw)
To: linuxppc-dev, kexec; +Cc: lists, horms
Hello everybody,
Changes from previous version:
- removed bogus hyphen from the patch;
- move ifdefs to crt.S instead of Makefile
Please find here patch for user land kexec-tools application. Following
patch makes kexec-tools work for both kexec and kdump. I tested it with
git kernel (linus-tree) and Freescale/Logic MPC8360ERDK board with
mpc83xx_defconfig kernel config.
kexec:
kexec -l vmlinux --command-line="console= ... etc"
kexec -e
kdump:
kexec -p vmlinux_dump --command-line="console=... etc"
echo c > /proc/sysrq-trigger
I also think that is is reasonable:
- put GAME_CUBE specific code to separate files;
- combine ppc and ppc64 to powerpc directory (I'm planning to do it.
And that why in some places my patch have ifdefs for PPC64);
Best regards,
Maxim Uvarov.
From: Maxim Uvarov <muvarov@gmail.com>
Signed-off-by: Maxim Uvarov <muvarov@gmail.com>
Signed-off-by: Maxim Uvarov <muvarov@gmail.com>
---
kexec/arch/ppc/Makefile | 2
kexec/arch/ppc/crashdump-powerpc.c | 439 ++++++++++++++++++++++++++++++++++
kexec/arch/ppc/crashdump-powerpc.h | 38 +++
kexec/arch/ppc/fs2dt.c | 460 ++++++++++++++++++++++++++++++++++++
kexec/arch/ppc/kexec-elf-ppc.c | 186 +++++++++++++--
kexec/arch/ppc/kexec-ppc.c | 275 ++++++++++++++++++++--
kexec/arch/ppc/kexec-ppc.h | 32 +++
purgatory/arch/ppc/Makefile | 2
purgatory/arch/ppc/purgatory-ppc.c | 38 ++-
purgatory/arch/ppc/purgatory-ppc.h | 4
purgatory/arch/ppc/v2wrap.S | 66 -----
purgatory/arch/ppc/v2wrap_32.S | 91 +++++++
12 files changed, 1524 insertions(+), 109 deletions(-)
create mode 100644 kexec/arch/ppc/crashdump-powerpc.c
create mode 100644 kexec/arch/ppc/crashdump-powerpc.h
create mode 100644 kexec/arch/ppc/fs2dt.c
delete mode 100644 purgatory/arch/ppc/v2wrap.S
create mode 100644 purgatory/arch/ppc/v2wrap_32.S
diff --git a/kexec/arch/ppc/Makefile b/kexec/arch/ppc/Makefile
index 1c7441c..5988213 100644
--- a/kexec/arch/ppc/Makefile
+++ b/kexec/arch/ppc/Makefile
@@ -11,6 +11,8 @@ ppc_KEXEC_SRCS += kexec/arch/ppc/kexec-uImage-ppc.c
ppc_KEXEC_SRCS += kexec/arch/ppc/ppc-setup-simple.S
ppc_KEXEC_SRCS += kexec/arch/ppc/ppc-setup-dol.S
ppc_KEXEC_SRCS += kexec/arch/ppc/fixup_dtb.c
+ppc_KEXEC_SRCS += kexec/arch/ppc/fs2dt.c
+ppc_KEXEC_SRCS += kexec/arch/ppc/crashdump-powerpc.c
ppc_KEXEC_SRCS += kexec/kexec-uImage.c
libfdt_SRCS = kexec/arch/ppc/libfdt-wrapper.c
diff --git a/kexec/arch/ppc/crashdump-powerpc.c b/kexec/arch/ppc/crashdump-powerpc.c
new file mode 100644
index 0000000..7bfad20
--- /dev/null
+++ b/kexec/arch/ppc/crashdump-powerpc.c
@@ -0,0 +1,439 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <elf.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "../../kexec.h"
+#include "../../kexec-elf.h"
+#include "../../kexec-syscall.h"
+#include "../../crashdump.h"
+#include "kexec-ppc.h"
+#include "crashdump-powerpc.h"
+
+#ifdef CONFIG_PPC64
+static struct crash_elf_info elf_info64 = {
+class: ELFCLASS64,
+data: ELFDATA2MSB,
+machine: EM_PPC64,
+backup_src_start: BACKUP_SRC_START,
+backup_src_end: BACKUP_SRC_END,
+page_offset: PAGE_OFFSET,
+lowmem_limit: MAXMEM,
+};
+#endif
+static struct crash_elf_info elf_info32 = {
+class: ELFCLASS32,
+data: ELFDATA2MSB,
+#ifdef CONFIG_PPC64
+machine: EM_PPC64,
+#else
+machine: EM_PPC,
+#endif
+backup_src_start: BACKUP_SRC_START,
+backup_src_end: BACKUP_SRC_END,
+page_offset: PAGE_OFFSET,
+lowmem_limit: MAXMEM,
+};
+
+/* 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;
+
+/* Define a variable to replace the CRASH_MAX_MEMORY_RANGES macro */
+static int crash_max_memory_ranges;
+
+/*
+ * Used to save various memory ranges/regions needed for the captured
+ * kernel to boot. (lime memmap= option in other archs)
+ */
+mem_rgns_t usablemem_rgns = {0, NULL};
+
+/*
+ * To store the memory size of the first kernel and this value will be
+ * passed to the second kernel as command line (savemaxmem=xM).
+ * The second kernel will be calculated saved_max_pfn based on this
+ * variable.
+ * Since we are creating/using usable-memory property, there is no way
+ * we can determine the RAM size unless parsing the device-tree/memoy@/reg
+ * property in the kernel.
+ */
+unsigned long long saved_max_mem;
+
+/* 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)
+{
+
+ int 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 i, n, crash_rng_len = 0;
+ unsigned long long start, end, cstart, cend;
+
+ crash_max_memory_ranges = max_memory_ranges + 6;
+ crash_rng_len = sizeof(struct memory_range) * crash_max_memory_ranges;
+
+ crash_memory_range = (struct memory_range *) malloc(crash_rng_len);
+ if (!crash_memory_range) {
+ fprintf(stderr, "Allocation for crash memory range failed\n");
+ return -1;
+ }
+ memset(crash_memory_range, 0, crash_rng_len);
+
+ /* create a separate program header for the backup region */
+ crash_memory_range[0].start = BACKUP_SRC_START;
+ crash_memory_range[0].end = BACKUP_SRC_END + 1;
+ crash_memory_range[0].type = RANGE_RAM;
+ memory_ranges++;
+
+ dir = opendir(device_tree);
+ if (!dir) {
+ perror(device_tree);
+ goto err;
+ }
+ while ((dentry = readdir(dir)) != NULL) {
+ if (strncmp(dentry->d_name, "memory@", 7)
+ && strcmp(dentry->d_name, "memory"))
+ continue;
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ dmem = opendir(fname);
+ if (!dmem) {
+ perror(fname);
+ closedir(dir);
+ goto err;
+ }
+ while ((mentry = readdir(dmem)) != NULL) {
+ if (strcmp(mentry->d_name, "reg"))
+ continue;
+ strcat(fname, "/reg");
+ file = fopen(fname, "r");
+ if (!file) {
+ perror(fname);
+ closedir(dmem);
+ closedir(dir);
+ goto err;
+ }
+ n = fread(buf, 1, MAXBYTES, file);
+ if (n < 0) {
+ perror(fname);
+ fclose(file);
+ closedir(dmem);
+ closedir(dir);
+ goto err;
+ }
+ if (memory_ranges >= (max_memory_ranges + 1)) {
+ /* No space to insert another element. */
+ fprintf(stderr,
+ "Error: Number of crash memory ranges"
+ " excedeed the max limit\n");
+ goto err;
+ }
+
+ /*
+ * FIXME: This code fails on platforms that
+ * have more than one memory range specified
+ * in the device-tree's /memory/reg property.
+ * or where the #address-cells and #size-cells
+ * are not identical.
+ *
+ * We should interpret the /memory/reg property
+ * based on the values of the #address-cells and
+ * #size-cells properites.
+ */
+ if (n == (sizeof(unsigned long) * 2)) {
+ start = ((unsigned long *)buf)[0];
+ end = start + ((unsigned long *)buf)[1];
+ } else {
+ start = ((unsigned long long *)buf)[0];
+ end = start + ((unsigned long long *)buf)[1];
+ }
+ if (start == 0 && end >= (BACKUP_SRC_END + 1))
+ start = BACKUP_SRC_END + 1;
+
+ cstart = crash_base;
+ cend = crash_base + crash_size;
+ /*
+ * Exclude the region that lies within crashkernel
+ */
+ if (cstart < end && cend > start) {
+ if (start < cstart && end > cend) {
+ crash_memory_range[memory_ranges].start
+ = start;
+ crash_memory_range[memory_ranges].end
+ = cstart;
+ crash_memory_range[memory_ranges].type
+ = RANGE_RAM;
+ memory_ranges++;
+ crash_memory_range[memory_ranges].start
+ = cend;
+ crash_memory_range[memory_ranges].end
+ = end;
+ crash_memory_range[memory_ranges].type
+ = RANGE_RAM;
+ memory_ranges++;
+ } else if (start < cstart) {
+ crash_memory_range[memory_ranges].start
+ = start;
+ crash_memory_range[memory_ranges].end
+ = cstart;
+ crash_memory_range[memory_ranges].type
+ = RANGE_RAM;
+ memory_ranges++;
+ } else if (end > cend) {
+ crash_memory_range[memory_ranges].start
+ = cend;
+ crash_memory_range[memory_ranges].end
+ = end;
+ crash_memory_range[memory_ranges].type
+ = RANGE_RAM;
+ memory_ranges++;
+ }
+ } else {
+ crash_memory_range[memory_ranges].start = start;
+ crash_memory_range[memory_ranges].end = end;
+ crash_memory_range[memory_ranges].type
+ = RANGE_RAM;
+ memory_ranges++;
+ }
+ fclose(file);
+ }
+ closedir(dmem);
+ }
+ closedir(dir);
+
+ /*
+ * If RTAS region is overlapped with crashkernel, need to create ELF
+ * Program header for the overlapped memory.
+ */
+ if (crash_base < rtas_base + rtas_size &&
+ rtas_base < crash_base + crash_size) {
+ cstart = rtas_base;
+ cend = rtas_base + rtas_size;
+ if (cstart < crash_base)
+ cstart = crash_base;
+ if (cend > crash_base + crash_size)
+ cend = crash_base + crash_size;
+ crash_memory_range[memory_ranges].start = cstart;
+ crash_memory_range[memory_ranges++].end = cend;
+ }
+ /*
+ * Can not trust the memory regions order that we read from
+ * device-tree. Hence, get the MAX end value.
+ */
+ for (i = 0; i < memory_ranges; i++)
+ if (saved_max_mem < crash_memory_range[i].end)
+ saved_max_mem = crash_memory_range[i].end;
+
+ *range = crash_memory_range;
+ *ranges = memory_ranges;
+#if DEBUG
+ int j;
+ printf("CRASH MEMORY RANGES\n");
+ for (j = 0; j < *ranges; j++) {
+ start = crash_memory_range[j].start;
+ end = crash_memory_range[j].end;
+ fprintf(stderr, "%016Lx-%016Lx\n", start, end);
+ }
+#endif
+ return 0;
+
+err:
+ if (crash_memory_range)
+ free(crash_memory_range);
+ return -1;
+}
+
+/* Converts unsigned long to ascii string. */
+static void ulltoa(unsigned long long i, char *str)
+{
+ int j = 0, k;
+ char tmp;
+
+ do {
+ str[j++] = i % 10 + '0';
+ } while ((i /= 10) > 0);
+ str[j] = '\0';
+
+ /* Reverse the string. */
+ for (j = 0, k = strlen(str) - 1; j < k; j++, k--) {
+ tmp = str[k];
+ str[k] = str[j];
+ str[j] = tmp;
+ }
+}
+
+static int add_cmdline_param(char *cmdline, unsigned long long addr,
+ char *cmdstr, char *byte)
+{
+ int cmdlen, len, align = 1024;
+ char str[COMMAND_LINE_SIZE], *ptr;
+
+ /* Passing in =xxxK / =xxxM format. Saves space required in cmdline.*/
+ switch (byte[0]) {
+ case 'K':
+ if (addr%align)
+ return -1;
+ addr = addr/align;
+ break;
+ case 'M':
+ addr = addr/(align *align);
+ break;
+ }
+ ptr = str;
+ strcpy(str, cmdstr);
+ ptr += strlen(str);
+ ulltoa(addr, ptr);
+ strcat(str, byte);
+ len = strlen(str);
+ cmdlen = strlen(cmdline) + len;
+ if (cmdlen > (COMMAND_LINE_SIZE - 1))
+ die("Command line overflow\n");
+ strcat(cmdline, str);
+#if DEBUG
+ fprintf(stderr, "Command line after adding elfcorehdr: %s\n", cmdline);
+#endif
+ return 0;
+}
+
+/* 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 max_addr, unsigned long min_base)
+{
+ void *tmp;
+ unsigned long sz, elfcorehdr;
+ int nr_ranges, align = 1024, i;
+ unsigned long long end;
+ struct memory_range *mem_range;
+
+ if (get_crash_memory_ranges(&mem_range, &nr_ranges) < 0)
+ return -1;
+
+ /* Create a backup region segment to store backup data*/
+ sz = (BACKUP_SRC_SIZE + align - 1) & ~(align - 1);
+ tmp = xmalloc(sz);
+ memset(tmp, 0, sz);
+ info->backup_start = add_buffer(info, tmp, sz, sz, align,
+ 0, max_addr, 1);
+ reserve(info->backup_start, sz);
+
+ /* On powerpc memory ranges in device-tree is denoted as start
+ * and size rather than start and end, as is the case with
+ * other architectures like i386 . Because of this when loading
+ * the memory ranges in crashdump-elf.c the filesz calculation
+ * [ end - start + 1 ] goes for a toss.
+ *
+ * To be in sync with other archs adjust the end value for
+ * every crash memory range before calling the generic function
+ */
+
+ for (i = 0; i < nr_ranges; i++) {
+ end = crash_memory_range[i].end - 1;
+ crash_memory_range[i].end = end;
+ }
+
+
+#ifdef CONFIG_PPC64
+ /* Create elf header segment and store crash image data. */
+ if (arch_options.core_header_type == CORE_TYPE_ELF64) {
+ if (crash_create_elf64_headers(info, &elf_info64,
+ crash_memory_range, nr_ranges, &tmp,
+ &sz, ELF_CORE_HEADER_ALIGN) < 0)
+ return -1;
+ } else if (crash_create_elf32_headers(info, &elf_info32,
+ crash_memory_range, nr_ranges, &tmp, &sz,
+ ELF_CORE_HEADER_ALIGN) < 0)
+ return -1;
+#else
+ if (crash_create_elf32_headers(info, &elf_info32, crash_memory_range,
+ nr_ranges, &tmp, &sz, ELF_CORE_HEADER_ALIGN)
+ < 0)
+ return -1;
+#endif
+
+ elfcorehdr = add_buffer(info, tmp, sz, sz, align,
+ min_base, max_addr, 1);
+ reserve(elfcorehdr, sz);
+ /* modify and store the cmdline in a global array. This is later
+ * read by flatten_device_tree and modified if required
+ */
+ add_cmdline_param(mod_cmdline, elfcorehdr, " elfcorehdr=", "K");
+ add_cmdline_param(mod_cmdline, saved_max_mem, " savemaxmem=", "M");
+ return 0;
+}
+
+/*
+ * Used to save various memory regions needed for the captured kernel.
+ */
+
+void add_usable_mem_rgns(unsigned long long base, unsigned long long size)
+{
+ int i;
+ unsigned long long end = base + size;
+ unsigned long long ustart, uend;
+
+ base = _ALIGN_DOWN(base, getpagesize());
+ end = _ALIGN_UP(end, getpagesize());
+
+ for (i = 0; i < usablemem_rgns.size; i++) {
+ ustart = usablemem_rgns.ranges[i].start;
+ uend = usablemem_rgns.ranges[i].end;
+ if (base < uend && end > ustart) {
+ if ((base >= ustart) && (end <= uend))
+ return;
+ if (base < ustart && end > uend) {
+ usablemem_rgns.ranges[i].start = base;
+ usablemem_rgns.ranges[i].end = end;
+ return;
+ } else if (base < ustart) {
+ usablemem_rgns.ranges[i].start = base;
+ return;
+ } else if (end > uend) {
+ usablemem_rgns.ranges[i].end = end;
+ return;
+ }
+ }
+ }
+ usablemem_rgns.ranges[usablemem_rgns.size].start = base;
+ usablemem_rgns.ranges[usablemem_rgns.size++].end = end;
+
+#ifdef DEBUG
+ fprintf(stderr, "usable memory rgns size:%u base:%llx size:%llx\n",
+ usablemem_rgns.size, base, size);
+#endif
+}
+
+int is_crashkernel_mem_reserved(void)
+{
+ int fd;
+
+ fd = open("/proc/device-tree/chosen/linux,crashkernel-base", O_RDONLY);
+ if (fd < 0)
+ return 0;
+ close(fd);
+ return 1;
+}
+
diff --git a/kexec/arch/ppc/crashdump-powerpc.h b/kexec/arch/ppc/crashdump-powerpc.h
new file mode 100644
index 0000000..dc2772d
--- /dev/null
+++ b/kexec/arch/ppc/crashdump-powerpc.h
@@ -0,0 +1,38 @@
+#ifndef CRASHDUMP_POWERPC_H
+#define CRASHDUMP_POWERPC_H
+
+struct kexec_info;
+int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline,
+ unsigned long max_addr, unsigned long min_base);
+void add_usable_mem_rgns(unsigned long long base, unsigned long long size);
+
+extern struct arch_options_t arch_options;
+
+#ifdef CONFIG_PPC64
+#define PAGE_OFFSET 0xC000000000000000UL
+#define VMALLOCBASE 0xD000000000000000UL
+#define MAXMEM (-KERNELBASE-VMALLOCBASE)
+#else
+#define PAGE_OFFSET 0xC0000000
+#define MAXMEM 0x30000000 /* Use CONFIG_LOWMEM_SIZE from kernel */
+#endif
+
+#define KERNELBASE PAGE_OFFSET
+#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
+
+#define COMMAND_LINE_SIZE 512 /* from kernel */
+/* Backup Region, First 64K of System RAM. */
+#define BACKUP_SRC_START 0x0000
+#define BACKUP_SRC_END 0xffff
+#define BACKUP_SRC_SIZE (BACKUP_SRC_END - BACKUP_SRC_START + 1)
+
+#define KDUMP_BACKUP_LIMIT BACKUP_SRC_SIZE
+#define _ALIGN_UP(addr, size) (((addr)+((size)-1))&(~((size)-1)))
+#define _ALIGN_DOWN(addr, size) ((addr)&(~((size)-1)))
+
+extern unsigned long long crash_base;
+extern unsigned long long crash_size;
+extern unsigned int rtas_base;
+extern unsigned int rtas_size;
+
+#endif /* CRASHDUMP_POWERPC_H */
diff --git a/kexec/arch/ppc/fs2dt.c b/kexec/arch/ppc/fs2dt.c
new file mode 100644
index 0000000..238a3f2
--- /dev/null
+++ b/kexec/arch/ppc/fs2dt.c
@@ -0,0 +1,460 @@
+/*
+ * fs2dt: creates a flattened device-tree
+ *
+ * Copyright (C) 2004,2005 Milton D Miller II, IBM Corporation
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include "../../kexec.h"
+#include "kexec-ppc.h"
+
+#define MAXPATH 1024 /* max path name length */
+#define NAMESPACE 16384 /* max bytes for property names */
+#define TREEWORDS 65536 /* max 32 bit words for properties */
+#define MEMRESERVE 256 /* max number of reserved memory blks */
+#define MAX_MEMORY_RANGES 1024
+#define COMMAND_LINE_SIZE 512 /* from kernel */
+
+static char pathname[MAXPATH];
+static char propnames[NAMESPACE] = { 0 };
+static unsigned dtstruct[TREEWORDS], *dt;
+static unsigned long long mem_rsrv[2*MEMRESERVE] = { 0, 0 };
+
+static int crash_param;
+static char local_cmdline[COMMAND_LINE_SIZE] = { "" };
+static unsigned *dt_len; /* changed len of modified cmdline
+ in flat device-tree */
+static struct bootblock bb[1];
+
+void reserve(unsigned long long where, unsigned long long length)
+{
+ size_t offset;
+
+ for (offset = 0; mem_rsrv[offset + 1]; offset += 2)
+ ;
+
+ if (offset + 4 >= 2 * MEMRESERVE)
+ die("unrecoverable error: exhasuted reservation meta data\n");
+
+ mem_rsrv[offset] = where;
+ mem_rsrv[offset + 1] = length;
+ mem_rsrv[offset + 3] = 0; /* N.B: don't care about offset + 2 */
+}
+
+/* look for properties we need to reserve memory space for */
+static void checkprop(char *name, unsigned *data, int len)
+{
+ static unsigned long long base, size, end;
+
+ if ((data == NULL) && (base || size || end))
+ die("unrecoverable error: no property data");
+ else if (!strcmp(name, "linux,rtas-base"))
+ base = *data;
+ else if (!strcmp(name, "linux,tce-base"))
+ base = *(unsigned long long *) data;
+ else if (!strcmp(name, "rtas-size") ||
+ !strcmp(name, "linux,tce-size"))
+ size = *data;
+ else if (reuse_initrd && !strcmp(name, "linux,initrd-start"))
+ if (len == 8)
+ base = *(unsigned long long *) data;
+ else
+ base = *data;
+ else if (reuse_initrd && !strcmp(name, "linux,initrd-end"))
+ end = *(unsigned long long *) data;
+
+ if (size && end)
+ die("unrecoverable error: size and end set at same time\n");
+ if (base && size) {
+ reserve(base, size);
+ base = 0;
+ size = 0;
+ }
+ if (base && end) {
+ reserve(base, end-base);
+ base = 0;
+ end = 0;
+ }
+}
+
+/*
+ * return the property index for a property name, creating a new one
+ * if needed.
+ */
+static unsigned propnum(const char *name)
+{
+ unsigned offset = 0;
+
+ while (propnames[offset])
+ if (strcmp(name, propnames+offset))
+ offset += strlen(propnames+offset)+1;
+ else
+ return offset;
+
+ if (NAMESPACE - offset < strlen(name) + 1)
+ die("unrecoverable error: propnames overrun\n");
+
+ strcpy(propnames+offset, name);
+
+ return offset;
+}
+
+static void add_usable_mem_property(int fd, int len)
+{
+ char fname[MAXPATH], *bname;
+ unsigned long buf[2];
+ unsigned long ranges[2*MAX_MEMORY_RANGES];
+ unsigned long long base, end, loc_base, loc_end;
+ int range, rlen = 0;
+
+ strcpy(fname, pathname);
+ bname = strrchr(fname, '/');
+ bname[0] = '\0';
+ bname = strrchr(fname, '/');
+ if (strncmp(bname, "/memory@", 8) && strcmp(bname, "/memory"))
+ return;
+
+ if (len < 2 * sizeof(unsigned long))
+ die("unrecoverable error: not enough data for mem property\n");
+ len = 2 * sizeof(unsigned long);
+
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ die("unrecoverable error: error seeking in \"%s\": %s\n",
+ pathname, strerror(errno));
+ if (read(fd, buf, len) != len)
+ die("unrecoverable error: error reading \"%s\": %s\n",
+ pathname, strerror(errno));
+
+ if (~0ULL - buf[0] < buf[1])
+ die("unrecoverable error: mem property overflow\n");
+ base = buf[0];
+ end = base + buf[1];
+
+ for (range = 0; range < usablemem_rgns.size; range++) {
+ loc_base = usablemem_rgns.ranges[range].start;
+ loc_end = usablemem_rgns.ranges[range].end;
+ if (loc_base >= base && loc_end <= end) {
+ ranges[rlen++] = loc_base;
+ ranges[rlen++] = loc_end - loc_base;
+ } else if (base < loc_end && end > loc_base) {
+ if (loc_base < base)
+ loc_base = base;
+ if (loc_end > end)
+ loc_end = end;
+ ranges[rlen++] = loc_base;
+ ranges[rlen++] = loc_end - loc_base;
+ }
+ }
+
+ if (!rlen) {
+ /*
+ * User did not pass any ranges for thsi region. Hence, write
+ * (0,0) duple in linux,usable-memory property such that
+ * this region will be ignored.
+ */
+ ranges[rlen++] = 0;
+ ranges[rlen++] = 0;
+ }
+
+ rlen = rlen * sizeof(unsigned long);
+ /*
+ * No add linux,usable-memory property.
+ */
+ *dt++ = 3;
+ *dt++ = rlen;
+ *dt++ = propnum("linux,usable-memory");
+ memcpy(dt, &ranges, rlen);
+ dt += (rlen + 3)/4;
+}
+
+/* put all properties (files) in the property structure */
+static void putprops(char *fn, struct dirent **nlist, int numlist)
+{
+ struct dirent *dp;
+ int i = 0, fd, len;
+ struct stat statbuf;
+
+ for (i = 0; i < numlist; i++) {
+ dp = nlist[i];
+ strcpy(fn, dp->d_name);
+
+ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
+ continue;
+
+ if (lstat(pathname, &statbuf))
+ die("unrecoverable error: could not stat \"%s\": %s\n",
+ pathname, strerror(errno));
+
+ if (!crash_param && !strcmp(fn, "linux,crashkernel-base"))
+ continue;
+
+ if (!crash_param && !strcmp(fn, "linux,crashkernel-size"))
+ continue;
+
+ /*
+ * This property will be created for each node during kexec
+ * boot. So, ignore it.
+ */
+ if (!strcmp(dp->d_name, "linux,pci-domain") ||
+ !strcmp(dp->d_name, "linux,htab-base") ||
+ !strcmp(dp->d_name, "linux,htab-size") ||
+ !strcmp(dp->d_name, "linux,kernel-end") ||
+ !strcmp(dp->d_name, "linux,usable-memory"))
+ continue;
+
+ /* This property will be created/modified later in putnode()
+ * So ignore it, unless we are reusing the initrd.
+ */
+ if ((!strcmp(dp->d_name, "linux,initrd-start") ||
+ !strcmp(dp->d_name, "linux,initrd-end")) &&
+ !reuse_initrd)
+ continue;
+
+ if (!S_ISREG(statbuf.st_mode))
+ continue;
+
+ len = statbuf.st_size;
+
+ *dt++ = 3;
+ dt_len = dt;
+ *dt++ = len;
+ *dt++ = propnum(fn);
+
+ fd = open(pathname, O_RDONLY);
+ if (fd == -1)
+ die("unrecoverable error: could not open \"%s\": %s\n",
+ pathname, strerror(errno));
+
+ if (read(fd, dt, len) != len)
+ die("unrecoverable error: could not read \"%s\": %s\n",
+ pathname, strerror(errno));
+
+ checkprop(fn, dt, len);
+
+ /* Get the cmdline from the device-tree and modify it */
+ if (!strcmp(dp->d_name, "bootargs")) {
+ int cmd_len;
+ char temp_cmdline[COMMAND_LINE_SIZE] = { "" };
+ char *param = NULL;
+ cmd_len = strlen(local_cmdline);
+ if (cmd_len != 0) {
+ param = strstr(local_cmdline, "crashkernel=");
+ if (param)
+ crash_param = 1;
+ param = strstr(local_cmdline, "root=");
+ }
+ if (!param) {
+ char *old_param;
+ memcpy(temp_cmdline, dt, len);
+ param = strstr(temp_cmdline, "root=");
+ if (param) {
+ old_param = strtok(param, " ");
+ if (cmd_len != 0)
+ strcat(local_cmdline, " ");
+ strcat(local_cmdline, old_param);
+ }
+ }
+ strcat(local_cmdline, " ");
+ cmd_len = strlen(local_cmdline);
+ cmd_len = cmd_len + 1;
+ memcpy(dt, local_cmdline, cmd_len);
+ len = cmd_len;
+ *dt_len = cmd_len;
+#if DEBUG
+ fprintf(stderr, "Modified cmdline:%s\n", local_cmdline);
+#endif
+ }
+
+ dt += (len + 3)/4;
+ if (!strcmp(dp->d_name, "reg") && usablemem_rgns.size)
+ add_usable_mem_property(fd, len);
+ close(fd);
+ }
+
+ fn[0] = '\0';
+ checkprop(pathname, NULL, 0);
+}
+
+/*
+ * Compare function used to sort the device-tree directories
+ * This function will be passed to scandir.
+ */
+static int comparefunc(const void *dentry1, const void *dentry2)
+{
+ char *str1 = (*(struct dirent **)dentry1)->d_name;
+ char *str2 = (*(struct dirent **)dentry2)->d_name;
+
+ /*
+ * strcmp scans from left to right and fails to idetify for some
+ * strings such as memory@10000000 and memory@f000000.
+ * Therefore, we get the wrong sorted order like memory@10000000 and
+ * memory@f000000.
+ */
+ if (strchr(str1, '@') && strchr(str2, '@') &&
+ (strlen(str1) > strlen(str2)))
+ return 1;
+
+ return strcmp(str1, str2);
+}
+
+/*
+ * put a node (directory) in the property structure. first properties
+ * then children.
+ */
+static void putnode(void)
+{
+ char *dn;
+ struct dirent *dp;
+ char *basename;
+ struct dirent **namelist;
+ int numlist, i;
+ struct stat statbuf;
+
+ numlist = scandir(pathname, &namelist, 0, comparefunc);
+ if (numlist < 0)
+ die("unrecoverable error: could not scan \"%s\": %s\n",
+ pathname, strerror(errno));
+ if (numlist == 0)
+ die("unrecoverable error: no directory entries in \"%s\"",
+ pathname);
+
+ basename = strrchr(pathname, '/') + 1;
+
+ *dt++ = 1;
+ strcpy((void *)dt, *basename ? basename : "");
+ dt += strlen((void *)dt) / sizeof(unsigned) + 1;
+
+ strcat(pathname, "/");
+ dn = pathname + strlen(pathname);
+
+ putprops(dn, namelist, numlist);
+
+ /* Add initrd entries to the second kernel */
+ if (initrd_base && !strcmp(basename, "chosen/")) {
+ int len = 8;
+ unsigned long long initrd_end;
+ *dt++ = 3;
+ *dt++ = len;
+ *dt++ = propnum("linux,initrd-start");
+
+ memcpy(dt, &initrd_base, len);
+ dt += (len + 3)/4;
+
+ len = 8;
+ *dt++ = 3;
+ *dt++ = len;
+ *dt++ = propnum("linux,initrd-end");
+
+ initrd_end = initrd_base + initrd_size;
+
+ memcpy(dt, &initrd_end, len);
+ dt += (len + 3)/4;
+
+ reserve(initrd_base, initrd_size);
+ }
+
+ for (i = 0; i < numlist; i++) {
+ dp = namelist[i];
+ strcpy(dn, dp->d_name);
+ free(namelist[i]);
+
+ if (!strcmp(dn, ".") || !strcmp(dn, ".."))
+ continue;
+
+ if (lstat(pathname, &statbuf))
+ die("unrecoverable error: could not stat \"%s\": %s\n",
+ pathname, strerror(errno));
+
+ if (S_ISDIR(statbuf.st_mode))
+ putnode();
+ }
+
+ *dt++ = 2;
+ dn[-1] = '\0';
+ free(namelist);
+}
+
+int create_flatten_tree(struct kexec_info *info, unsigned char **bufp,
+ unsigned long *sizep, char *cmdline)
+{
+ unsigned long len;
+ unsigned long tlen;
+ unsigned char *buf;
+ unsigned long me;
+
+ me = 0;
+
+ strcpy(pathname, "/proc/device-tree/");
+
+ dt = dtstruct;
+
+ if (cmdline)
+ strcpy(local_cmdline, cmdline);
+
+ putnode();
+ *dt++ = 9;
+
+ len = sizeof(bb[0]);
+ len += 7; len &= ~7;
+
+ bb->off_mem_rsvmap = len;
+
+ for (len = 1; mem_rsrv[len]; len += 2)
+ ;
+ len += 3;
+ len *= sizeof(mem_rsrv[0]);
+
+ bb->off_dt_struct = bb->off_mem_rsvmap + len;
+
+ len = dt - dtstruct;
+ len *= sizeof(unsigned);
+ bb->dt_struct_size = len;
+ bb->off_dt_strings = bb->off_dt_struct + len;
+
+ len = propnum("");
+ bb->dt_strings_size = len;
+ len += 3; len &= ~3;
+ bb->totalsize = bb->off_dt_strings + len;
+
+ bb->magic = 0xd00dfeed;
+ bb->version = 17;
+ bb->last_comp_version = 16;
+
+ reserve(me, bb->totalsize); /* patched later in kexec_load */
+
+ buf = (unsigned char *) malloc(bb->totalsize);
+ *bufp = buf;
+ memcpy(buf, bb, bb->off_mem_rsvmap);
+ tlen = bb->off_mem_rsvmap;
+ memcpy(buf+tlen, mem_rsrv, bb->off_dt_struct - bb->off_mem_rsvmap);
+ tlen = tlen + (bb->off_dt_struct - bb->off_mem_rsvmap);
+ memcpy(buf+tlen, dtstruct, bb->off_dt_strings - bb->off_dt_struct);
+ tlen = tlen + (bb->off_dt_strings - bb->off_dt_struct);
+ memcpy(buf+tlen, propnames, bb->totalsize - bb->off_dt_strings);
+ tlen = tlen + bb->totalsize - bb->off_dt_strings;
+ *sizep = tlen;
+ return 0;
+}
diff --git a/kexec/arch/ppc/kexec-elf-ppc.c b/kexec/arch/ppc/kexec-elf-ppc.c
index a54a5d5..4bcac26 100644
--- a/kexec/arch/ppc/kexec-elf-ppc.c
+++ b/kexec/arch/ppc/kexec-elf-ppc.c
@@ -11,6 +11,7 @@
#include <string.h>
#include <stdlib.h>
#include <errno.h>
+#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -23,15 +24,22 @@
#include "../../kexec-elf.h"
#include "kexec-ppc.h"
#include <arch/options.h>
+#include "../../kexec-syscall.h"
+#include "crashdump-powerpc.h"
#include "config.h"
#include "fixup_dtb.h"
static const int probe_debug = 0;
-#define MAX_COMMAND_LINE 256
+unsigned long long initrd_base, initrd_size;
+unsigned char reuse_initrd;
+const char *ramdisk;
+int create_flatten_tree(struct kexec_info *, unsigned char **, unsigned long *,
+ char *);
#define UPSZ(X) ((sizeof(X) + 3) & ~3)
+#ifdef WITH_GAMECUBE
static struct boot_notes {
Elf_Bhdr hdr;
Elf_Nhdr bl_hdr;
@@ -65,7 +73,7 @@ static struct boot_notes {
.n_type = EBN_COMMAND_LINE,
},
};
-
+#endif
int elf_ppc_probe(const char *buf, off_t len)
{
@@ -92,6 +100,7 @@ int elf_ppc_probe(const char *buf, off_t len)
return result;
}
+#ifdef WITH_GAMECUBE
static void gamecube_hack_addresses(struct mem_ehdr *ehdr)
{
struct mem_phdr *phdr, *phdr_end;
@@ -112,6 +121,7 @@ static void gamecube_hack_addresses(struct mem_ehdr *ehdr)
phdr->p_paddr &= ~0xf0000000; /* clear bits 0-3, ibm syntax */
}
}
+#endif
#define OPT_APPEND (OPT_ARCH_MAX+0)
#define OPT_GAMECUBE (OPT_ARCH_MAX+1)
@@ -145,10 +155,24 @@ int elf_ppc_load(int argc, char **argv, const char *buf, off_t len,
struct kexec_info *info)
{
struct mem_ehdr ehdr;
- char *command_line;
+ char *command_line, *crash_cmdline, *cmdline_buf;
int command_line_len;
char *dtb;
int result;
+ unsigned long max_addr, hole_addr;
+ struct mem_phdr *phdr;
+ size_t size;
+ unsigned long long *rsvmap_ptr;
+ struct bootblock *bb_ptr;
+ unsigned int nr_segments;
+ unsigned long my_kernel, my_dt_offset;
+ unsigned long my_stack, my_backup_start;
+#ifdef CONFIG_PPC64
+ unsigned long toc_addr;
+#endif
+ unsigned int slave_code[256 / sizeof(unsigned int)], master_entry;
+ unsigned char *seg_buf = NULL;
+ off_t seg_size = 0;
#ifdef WITH_GAMECUBE
int target_is_gamecube = 1;
char *arg_buf;
@@ -170,6 +194,9 @@ int elf_ppc_load(int argc, char **argv, const char *buf, off_t len,
command_line = NULL;
dtb = NULL;
+ max_addr = LONG_MAX;
+ hole_addr = 0;
+
while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
switch (opt) {
default:
@@ -209,15 +236,45 @@ int elf_ppc_load(int argc, char **argv, const char *buf, off_t len,
fixup_nodes[cur_fixup] = NULL;
+ /* 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;
+
/* Parse the Elf file */
result = build_elf_exec_info(buf, len, &ehdr, 0);
if (result < 0) {
free_elf_info(&ehdr);
return result;
}
+
+#ifdef WITH_GAMECUBE
if (target_is_gamecube) {
gamecube_hack_addresses(&ehdr);
}
+#endif
+
+ /* Load the Elf data. Physical load addresses in elf64 header do not
+ * show up correctly. Use user supplied address for now to patch the
+ * elf header
+ */
+
+ phdr = &ehdr.e_phdr[0];
+ size = phdr->p_filesz;
+ if (size > phdr->p_memsz)
+ size = phdr->p_memsz;
+
+ hole_addr = locate_hole(info, size, 0, 0, max_addr, 1);
+#ifdef CONFIG_PPC64
+ ehdr.e_phdr[0].p_paddr = (Elf64_Addr)hole_addr;
+#else
+ ehdr.e_phdr[0].p_paddr = hole_addr;
+#endif
+
/* Load the Elf data */
result = elf_exec_load(&ehdr, info);
if (result < 0) {
@@ -225,6 +282,27 @@ int elf_ppc_load(int argc, char **argv, const char *buf, off_t len,
return result;
}
+ /* If panic kernel is being loaded, additional segments need
+ * to be created.
+ */
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ result = load_crashdump_segments(info, crash_cmdline,
+ max_addr, 0);
+ if (result < 0) {
+ free(crash_cmdline);
+ return -1;
+ }
+ }
+
+ cmdline_buf = xmalloc(COMMAND_LINE_SIZE);
+ memset((void *)cmdline_buf, 0, COMMAND_LINE_SIZE);
+ if (command_line)
+ strncat(cmdline_buf, command_line, command_line_len);
+ if (crash_cmdline)
+ strncat(cmdline_buf, crash_cmdline,
+ sizeof(crash_cmdline) -
+ strlen(crash_cmdline) - 1);
+
/*
* In case of a toy we take the hardcoded things and an easy setup via
* one of the assembly startups. Every thing else should be grown up
@@ -269,31 +347,105 @@ int elf_ppc_load(int argc, char **argv, const char *buf, off_t len,
blob_buf = slurp_file(dtb, &blob_size);
if (!blob_buf || !blob_size)
die("Device tree seems to be an empty file.\n");
- blob_buf = fixup_dtb_nodes(blob_buf, &blob_size, fixup_nodes, command_line);
+ blob_buf = fixup_dtb_nodes(blob_buf, &blob_size, fixup_nodes,
+ cmdline_buf);
dtb_addr = add_buffer(info, blob_buf, blob_size, blob_size, 0, 0,
KERNEL_ACCESS_TOP, -1);
} else {
- dtb_addr = 0;
+ /* create from fs2dt */
+ seg_buf = NULL;
+ seg_size = 0;
+ create_flatten_tree(info, (unsigned char **)&seg_buf,
+ (unsigned long *)&seg_size, cmdline_buf);
+ add_buffer(info, seg_buf, seg_size, seg_size,
+#ifdef CONFIG_PPC64
+ 0, 0, max_addr, -1);
+#else
+ /* load dev tree at 16 Mb offset from kernel load address */
+ 0, 0, ehdr.e_phdr[0].p_paddr + SIZE_16M, -1);
+#endif
}
- /* set various variables for the purgatory */
- addr = ehdr.e_entry;
- elf_rel_set_symbol(&info->rhdr, "kernel", &addr, sizeof(addr));
- addr = dtb_addr;
- elf_rel_set_symbol(&info->rhdr, "dt_offset", &addr, sizeof(addr));
+ if (dtb) {
+ /* set various variables for the purgatory */
+ addr = ehdr.e_entry;
+ elf_rel_set_symbol(&info->rhdr, "kernel", &addr, sizeof(addr));
+
+ addr = dtb_addr;
+ elf_rel_set_symbol(&info->rhdr, "dt_offset",
+ &addr, sizeof(addr));
- addr = rmo_top;
- elf_rel_set_symbol(&info->rhdr, "mem_size", &addr, sizeof(addr));
+ addr = rmo_top;
+
+ elf_rel_set_symbol(&info->rhdr, "mem_size",
+ &addr, sizeof(addr));
#define PUL_STACK_SIZE (16 * 1024)
- addr = locate_hole(info, PUL_STACK_SIZE, 0, 0, elf_max_addr(&ehdr), 1);
- addr += PUL_STACK_SIZE;
- elf_rel_set_symbol(&info->rhdr, "pul_stack", &addr, sizeof(addr));
+ addr = locate_hole(info, PUL_STACK_SIZE, 0, 0,
+ elf_max_addr(&ehdr), 1);
+ addr += PUL_STACK_SIZE;
+ elf_rel_set_symbol(&info->rhdr, "stack", &addr, sizeof(addr));
#undef PUL_STACK_SIZE
- addr = elf_rel_get_addr(&info->rhdr, "purgatory_start");
- info->entry = (void *)addr;
+ addr = elf_rel_get_addr(&info->rhdr, "purgatory_start");
+ info->entry = (void *)addr;
+
+ } else { /*from fs2dt*/
+
+ /* patch reserve map address for flattened device-tree
+ * find last entry (both 0) in the reserve mem list. Assume DT
+ * entry is before this one
+ */
+ bb_ptr = (struct bootblock *)(
+ (unsigned char *)info->segment[(info->nr_segments) -
+ 1].buf);
+ rsvmap_ptr = (unsigned long long *)(
+ (unsigned char *)info->segment[(info->nr_segments) -
+ 1].buf + bb_ptr->off_mem_rsvmap);
+ while (*rsvmap_ptr || *(rsvmap_ptr + 1))
+ rsvmap_ptr += 2;
+ rsvmap_ptr -= 2;
+ *rsvmap_ptr = (unsigned long)(
+ info->segment[(info->nr_segments)-1].mem);
+ rsvmap_ptr++;
+ *rsvmap_ptr = (unsigned long long)bb_ptr->totalsize;
+
+ nr_segments = info->nr_segments;
+
+ /* Set kernel */
+ my_kernel = (unsigned long)info->segment[0].mem;
+ elf_rel_set_symbol(&info->rhdr, "kernel", &my_kernel,
+ sizeof(my_kernel));
+
+ /* Set dt_offset */
+ my_dt_offset = (unsigned long)info->segment[nr_segments -
+ 1].mem;
+ elf_rel_set_symbol(&info->rhdr, "dt_offset", &my_dt_offset,
+ sizeof(my_dt_offset));
+
+ /* get slave code from new kernel, put in purgatory */
+ elf_rel_get_symbol(&info->rhdr, "purgatory_start", slave_code,
+ sizeof(slave_code));
+ master_entry = slave_code[0];
+ memcpy(slave_code, info->segment[0].buf, sizeof(slave_code));
+ slave_code[0] = master_entry;
+ elf_rel_set_symbol(&info->rhdr, "purgatory_start", slave_code,
+ sizeof(slave_code));
+
+ /* Set stack address */
+ my_stack = locate_hole(info, 16*1024, 0, 0, max_addr, 1);
+ my_stack += 16*1024;
+ elf_rel_set_symbol(&info->rhdr, "stack", &my_stack,
+ sizeof(my_stack));
+ }
+
+ if (info->kexec_flags & KEXEC_ON_CRASH) {
+ /* Set backup address */
+ my_backup_start = info->backup_start;
+ elf_rel_set_symbol(&info->rhdr, "backup_start",
+ &my_backup_start, sizeof(my_backup_start));
+ }
#endif
return 0;
}
diff --git a/kexec/arch/ppc/kexec-ppc.c b/kexec/arch/ppc/kexec-ppc.c
index f552d79..255d541 100644
--- a/kexec/arch/ppc/kexec-ppc.c
+++ b/kexec/arch/ppc/kexec-ppc.c
@@ -21,6 +21,7 @@
#include "../../kexec.h"
#include "../../kexec-syscall.h"
#include "kexec-ppc.h"
+#include "crashdump-powerpc.h"
#include <arch/options.h>
#include "config.h"
@@ -45,13 +46,14 @@ static int get_memory_ranges_gc(struct memory_range **range, int *ranges,
}
#else
static int use_new_dtb;
-static int max_memory_ranges;
+int max_memory_ranges;
static int nr_memory_ranges, nr_exclude_ranges;
static struct memory_range *exclude_range;
static struct memory_range *memory_range;
static struct memory_range *base_memory_range;
static uint64_t memory_max;
uint64_t rmo_top;
+unsigned long long crash_base, crash_size;
unsigned int rtas_base, rtas_size;
/*
@@ -174,6 +176,40 @@ static int sort_base_ranges(void)
#define MAXBYTES 128
+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 *) malloc(memory_range_len);
+ if (!memory_range)
+ goto err;
+
+ base_memory_range = (struct memory_range *) realloc(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;
+}
+
/* Get base memory ranges */
static int get_base_ranges(void)
{
@@ -219,8 +255,10 @@ static int get_base_ranges(void)
return -1;
}
if (local_memory_ranges >= max_memory_ranges) {
- fclose(file);
- break;
+ if (realloc_memory_ranges() < 0){
+ fclose(file);
+ break;
+ }
}
base_memory_range[local_memory_ranges].start =
((uint32_t *)buf)[0];
@@ -250,16 +288,23 @@ static int get_base_ranges(void)
/* Get devtree details and create exclude_range array
* Also create usablemem_ranges for KEXEC_ON_CRASH
*/
-static int get_devtree_details(unsigned long UNUSED(kexec_flags))
+static int get_devtree_details(unsigned long kexec_flags)
{
uint64_t rmo_base;
- char buf[MAXBYTES];
+ unsigned long long tce_base;
+ unsigned int tce_size;
+ unsigned long long htab_base, htab_size;
+ unsigned long long kernel_end;
+ unsigned long long initrd_start, initrd_end;
+ char buf[MAXBYTES-1];
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;
+ unsigned long tmp_long;
if ((dir = opendir(device_tree)) == NULL) {
perror(device_tree);
@@ -269,10 +314,10 @@ static int get_devtree_details(unsigned long UNUSED(kexec_flags))
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, "memory", 6) &&
+ 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) {
@@ -280,13 +325,172 @@ static int get_devtree_details(unsigned long UNUSED(kexec_flags))
goto error_opendir;
}
+ if (strncmp(dentry->d_name, "chosen", 6) == 0) {
+ strcat(fname, "/linux,kernel-end");
+ file = fopen(fname, "r");
+ if (!file) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (fread(&tmp_long, sizeof(unsigned long), 1, file)
+ != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ kernel_end = tmp_long;
+ fclose(file);
+
+ /* 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");
+ file = fopen(fname, "r");
+ if (!file) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (fread(&tmp_long, sizeof(unsigned long), 1,
+ file) != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ crash_base = tmp_long;
+ fclose(file);
+
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,crashkernel-size");
+ file = fopen(fname, "r");
+ if (!file) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (fread(&tmp_long, sizeof(unsigned long), 1,
+ file) != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ crash_size = tmp_long;
+
+ 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);
+ }
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,htab-base");
+ file = fopen(fname, "r");
+ if (!file) {
+ closedir(cdir);
+ if (errno == ENOENT) {
+ /* Non LPAR */
+ errno = 0;
+ continue;
+ }
+ perror(fname);
+ goto error_opendir;
+ }
+ if (fread(&htab_base, sizeof(unsigned long), 1, file)
+ != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,htab-size");
+ file = fopen(fname, "r");
+ if (!file) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (fread(&htab_size, sizeof(unsigned long), 1, file)
+ != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ /* 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");
+ file = fopen(fname, "r");
+ if (!file) {
+ 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;
+ }
+ fclose(file);
+
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,initrd-end");
+ file = fopen(fname, "r");
+ if (!file) {
+ 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;
+ }
+ 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) {
+ if (fread(&rtas_base, sizeof(unsigned int), 1, file)
+ != 1) {
perror(fname);
goto error_openfile;
}
@@ -298,7 +502,8 @@ static int get_devtree_details(unsigned long UNUSED(kexec_flags))
perror(fname);
goto error_opencdir;
}
- if (fread(&rtas_size, sizeof(unsigned int), 1, file) != 1) {
+ if (fread(&rtas_size, sizeof(unsigned int), 1, file)
+ != 1) {
perror(fname);
goto error_openfile;
}
@@ -307,6 +512,8 @@ static int get_devtree_details(unsigned long UNUSED(kexec_flags))
exclude_range[i].start = rtas_base;
exclude_range[i].end = rtas_base + rtas_size;
i++;
+ if (kexec_flags & KEXEC_ON_CRASH)
+ add_usable_mem_rgns(rtas_base, rtas_size);
} /* rtas */
if (!strncmp(dentry->d_name, "memory@", 7) ||
@@ -336,6 +543,48 @@ static int get_devtree_details(unsigned long UNUSED(kexec_flags))
fclose(file);
closedir(cdir);
} /* memory */
+
+ if (strncmp(dentry->d_name, "pci@", 4) == 0) {
+ strcat(fname, "/linux,tce-base");
+ file = fopen(fname, "r");
+ if (!file) {
+ closedir(cdir);
+ if (errno == ENOENT) {
+ /* Non LPAR */
+ errno = 0;
+ continue;
+ }
+ perror(fname);
+ goto error_opendir;
+ }
+ if (fread(&tce_base, sizeof(unsigned long), 1, file)
+ != 1) {
+ perror(fname);
+ goto error_openfile;
+ return -1;
+ }
+ memset(fname, 0, sizeof(fname));
+ strcpy(fname, device_tree);
+ strcat(fname, dentry->d_name);
+ strcat(fname, "/linux,tce-size");
+ file = fopen(fname, "r");
+ if (!file) {
+ perror(fname);
+ goto error_opencdir;
+ }
+ if (fread(&tce_size, sizeof(unsigned int), 1, file)
+ != 1) {
+ perror(fname);
+ goto error_openfile;
+ }
+ /* Add tce to exclude_range - NON-LPAR only */
+ exclude_range[i].start = tce_base;
+ exclude_range[i].end = tce_base + tce_size;
+ i++;
+ if (kexec_flags & KEXEC_ON_CRASH)
+ add_usable_mem_rgns(tce_base, tce_size);
+ closedir(cdir);
+ } /* pci */
}
closedir(dir);
@@ -347,8 +596,8 @@ static int get_devtree_details(unsigned long UNUSED(kexec_flags))
int k;
for (k = 0; k < i; k++)
fprintf(stderr, "exclude_range sorted exclude_range[%d] "
- "start:%llx, end:%llx\n", k, exclude_range[k].start,
- exclude_range[k].end);
+ "start:%llx, end:%llx\n", k, exclude_range[k].start,
+ exclude_range[k].end);
#endif
return 0;
@@ -532,7 +781,3 @@ void arch_update_purgatory(struct kexec_info *UNUSED(info))
{
}
-int is_crashkernel_mem_reserved(void)
-{
- return 0; /* kdump is not supported on this platform (yet) */
-}
diff --git a/kexec/arch/ppc/kexec-ppc.h b/kexec/arch/ppc/kexec-ppc.h
index 6cec467..fc0471f 100644
--- a/kexec/arch/ppc/kexec-ppc.h
+++ b/kexec/arch/ppc/kexec-ppc.h
@@ -1,6 +1,11 @@
#ifndef KEXEC_PPC_H
#define KEXEC_PPC_H
+#define MAXBYTES 128
+#define MAX_LINE 160
+#define CORE_TYPE_ELF32 1
+#define CORE_TYPE_ELF64 2
+
extern unsigned char setup_simple_start[];
extern uint32_t setup_simple_size;
@@ -16,6 +21,8 @@ extern struct {
uint32_t spr8;
} setup_dol_regs;
+#define SIZE_16M (16*1024*1024UL)
+
int elf_ppc_probe(const char *buf, off_t len);
int elf_ppc_load(int argc, char **argv, const char *buf, off_t len,
struct kexec_info *info);
@@ -37,4 +44,29 @@ void dol_ppc_usage(void);
*/
#define KERNEL_ACCESS_TOP (24 * 1024 * 1024)
+/* boot block version 17 as defined by the linux kernel */
+struct bootblock {
+ unsigned magic,
+ totalsize,
+ off_dt_struct,
+ off_dt_strings,
+ off_mem_rsvmap,
+ version,
+ last_comp_version,
+ boot_physid,
+ dt_strings_size,
+ dt_struct_size;
+};
+
+typedef struct mem_rgns {
+ unsigned int size;
+ struct memory_range *ranges;
+} mem_rgns_t;
+extern mem_rgns_t usablemem_rgns;
+extern int max_memory_ranges;
+extern unsigned long long initrd_base, initrd_size;
+extern unsigned char reuse_initrd;
+#define COMMAND_LINE_SIZE 512 /* from kernel */
+/*fs2dt*/
+void reserve(unsigned long long where, unsigned long long length);
#endif /* KEXEC_PPC_H */
diff --git a/purgatory/arch/ppc/Makefile b/purgatory/arch/ppc/Makefile
index 0dd18b6..72289a0 100644
--- a/purgatory/arch/ppc/Makefile
+++ b/purgatory/arch/ppc/Makefile
@@ -2,7 +2,7 @@
# Purgatory ppc
#
-ppc_PURGATORY_SRCS += purgatory/arch/ppc/v2wrap.S
+ppc_PURGATORY_SRCS += purgatory/arch/ppc/v2wrap_32.S
ppc_PURGATORY_SRCS += purgatory/arch/ppc/misc.S
ppc_PURGATORY_SRCS += purgatory/arch/ppc/purgatory-ppc.c
ppc_PURGATORY_SRCS += purgatory/arch/ppc/console-ppc.c
diff --git a/purgatory/arch/ppc/purgatory-ppc.c b/purgatory/arch/ppc/purgatory-ppc.c
index 01d0f38..3d7d484 100644
--- a/purgatory/arch/ppc/purgatory-ppc.c
+++ b/purgatory/arch/ppc/purgatory-ppc.c
@@ -1,19 +1,41 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Created by: Mohan Kumar M (mohan@in.ibm.com)
+ *
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * 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 <purgatory.h>
#include "purgatory-ppc.h"
-unsigned int pul_stack = 0;
-unsigned int dt_offset = 0;
-unsigned int kernel = 0;
-unsigned int epapr_magic = 0;
-unsigned int mem_size = 0;
+unsigned int panic_kernel = 0;
+unsigned long backup_start = 0;
+unsigned long stack = 0;
+unsigned long dt_offset = 0;
+unsigned long my_toc = 0;
+unsigned long kernel = 0;
void setup_arch(void)
{
- /* Nothing for now */
+ return;
}
-/* This function can be used to execute after the SHA256 verification. */
void post_verification_setup_arch(void)
{
- /* Nothing for now */
+ if (panic_kernel)
+ crashdump_backup_memory();
}
diff --git a/purgatory/arch/ppc/purgatory-ppc.h b/purgatory/arch/ppc/purgatory-ppc.h
index e931cae..7eff8aa 100644
--- a/purgatory/arch/ppc/purgatory-ppc.h
+++ b/purgatory/arch/ppc/purgatory-ppc.h
@@ -1,6 +1,6 @@
#ifndef PURGATORY_PPC_H
#define PURGATORY_PPC_H
-/* nothing yet */
-
+void crashdump_backup_memory(void);
+void post_verification_setup_arch(void);
#endif /* PURGATORY_PPC_H */
diff --git a/purgatory/arch/ppc/v2wrap.S b/purgatory/arch/ppc/v2wrap.S
deleted file mode 100644
index 79d188f..0000000
--- a/purgatory/arch/ppc/v2wrap.S
+++ /dev/null
@@ -1,66 +0,0 @@
-#
-# kexec: Linux boots Linux
-#
-# Copyright (C) 2004 - 2005, Milton D Miller II, IBM Corporation
-# Copyright (C) 2006, Mohan Kumar M (mohan@in.ibm.com), IBM Corporation
-# Copyright (C) 2008, Sebastian Andrzej Siewior (bigeasy@linutronix.de), linutronix
-#
-# 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 "ppc_asm.h"
-
-# v2wrap.S
-# a wrapper to call purgatory code
-# Invokes ppc kernel with the arguments according to ePAPR v1.0
-# It assumes that the MSR is allready correct.
-
-# calling convention:
-# no register are considred
-#
-
-#define LOADADDR(rn,name) \
- lis rn,name##@h; \
- ori rn,rn,name##@l; \
-
- .globl purgatory_start
-purgatory_start:
-
- LOADADDR(r6,pul_stack)
- lwz r1,0(r6) #setup stack
-
- subi r1, r1, 112
- bl purgatory
- nop
-
- LOADADDR(r6,kernel)
- lwz r4,0(r6) # load the kernel address
- mtlr r4 # prepare branch too
-
- LOADADDR(r6, dt_offset)
- lwz r3, 0(r6) # load device-tree address
-
- li r4, 0
- li r5, 0
-
- LOADADDR(r6, epapr_magic) # ePAPR magic value
- lwz r6, 0(r6)
-
- LOADADDR(r7, mem_size) # the Initial Mapped Area
- lwz r7, 0(r6)
-
- li r8, 0
- li r9, 0
-
- blr # start kernel
diff --git a/purgatory/arch/ppc/v2wrap_32.S b/purgatory/arch/ppc/v2wrap_32.S
new file mode 100644
index 0000000..8442d16
--- /dev/null
+++ b/purgatory/arch/ppc/v2wrap_32.S
@@ -0,0 +1,91 @@
+#
+# kexec: Linux boots Linux
+#
+# Copyright (C) 2004 - 2005, Milton D Miller II, IBM Corporation
+# Copyright (C) 2006, Mohan Kumar M (mohan@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.
+#
+
+# v2wrap.S
+# a wrapper to call purgatory code to backup first
+# 32kB of first kernel into the backup region
+# reserved by kexec-tools.
+# Invokes powerpc kernel with the expected arguments
+# of kernel(device-tree, phys-offset, 0)
+
+#
+# calling convention:
+# r3 = physical number of this cpu (all cpus)
+# r4 = address of this chunk (master only)
+# master enters at purgatory_start (aka first byte of this chunk)
+# slaves (additional cpus), if any, enter a copy of the
+# first 0x100 bytes of this code relocated to 0x0
+#
+# in other words,
+# a copy of the first 0x100 bytes of this code is copied to 0
+# and the slaves are sent to address 0x60
+# with r3 = their physical cpu number.
+
+ .globl purgatory_start
+purgatory_start: b master
+ .org purgatory_start + 0x60 # ABI: slaves start at 60 with r3=phys
+slave: b $
+ .org purgatory_start + 0x100 # ABI: end of copied region
+ .size purgatory_start, . - purgatory_start
+
+#
+# The above 0x100 bytes at purgatory_start are replaced with the
+# code from the kernel (or next stage) by kexec/arch/powerpc/kexec-powerpc.c
+#
+
+master:
+ or 1,1,1 # low priority to let other threads catchup
+ isync
+ mr 17,3 # save cpu id to r17
+ mr 15,4 # save physical address in reg15
+
+ lis 6,stack@h
+ ori 6,6,stack@l
+ lwz 1,0(6) #setup stack
+
+ subi 1,1,112
+ bl purgatory
+ nop
+
+ or 3,3,3 # ok now to high priority, lets boot
+ lis 6,0x1
+ mtctr 6 # delay a bit for slaves to catch up
+83: bdnz 83b # before we overwrite 0-100 again
+
+ lis 6,dt_offset@h
+ ori 6,6,dt_offset@l
+ lwz 3,0(6) # load device-tree address
+ lwz 6,20(3) # fetch version number
+ cmpwi 0,6,2 # v2 ?
+ blt 80f
+ stw 17,28(3) # save my cpu number as boot_cpu_phys
+80:
+ lis 6,kernel@h
+ ori 6,6,kernel@l
+ lwz 4,0(6) # load the kernel address
+ li 5,0 # r5 will be 0 for kernel
+ li 6,0 # clear r6 for good measure
+ mtctr 4 # prepare branch too
+
+ lwz 8,0(4) # get the first instruction that we stole
+ stw 8,0(0) # and put it in the slave loop at 0
+ # skip cache flush, do we care?
+
+ bctr # start kernel
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 2/2] powerpc new toolchains fix (crt.S)
2010-05-12 7:08 [PATCH v2 1/2] Fix kexec on powerpc32 Maxim Uvarov
@ 2010-05-12 7:08 ` Maxim Uvarov
2010-05-13 15:00 ` Simon Horman
2010-05-13 17:27 ` Segher Boessenkool
0 siblings, 2 replies; 5+ messages in thread
From: Maxim Uvarov @ 2010-05-12 7:08 UTC (permalink / raw)
To: linuxppc-dev, kexec; +Cc: lists, horms
Hello everybody,
Changes from previous version:
- removed bogus hyphen from the patch;
- move ifdefs to crt.S instead of Makefile
Please find here patch for user land kexec-tools application. Following
patch makes kexec-tools work for both kexec and kdump. I tested it with
git kernel (linus-tree) and Freescale/Logic MPC8360ERDK board with
mpc83xx_defconfig kernel config.
kexec:
kexec -l vmlinux --command-line="console= ... etc"
kexec -e
kdump:
kexec -p vmlinux_dump --command-line="console=... etc"
echo c > /proc/sysrq-trigger
I also think that is is reasonable:
- put GAME_CUBE specific code to separate files;
- combine ppc and ppc64 to powerpc directory (I'm planning to do it.
And that why in some places my patch have ifdefs for PPC64);
Best regards,
Maxim Uvarov.
From: Maxim Uvarov <muvarov@gmail.com>
Linker does not provide some vital functions when building freestanding
applications with a new toolchain, so we have to provide our own CRT.
p.s.
Without the CRT we won't see any build errors (since the purgatory is
linked with --no-undefined), but the purgatory code won't work,
'kexec -e' will just hang the board.
I added option to configure to keep code buildable for old toolchais.
But there should be way to do this automatically.
Author: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: Maxim Uvarov <muvarov@gmail.com>
Signed-off-by: Maxim Uvarov <muvarov@gmail.com>
---
configure.ac | 9 +
purgatory/arch/ppc/Makefile | 1
purgatory/arch/ppc/crt.S | 263 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 273 insertions(+), 0 deletions(-)
create mode 100644 purgatory/arch/ppc/crt.S
diff --git a/configure.ac b/configure.ac
index fcf50e4..63606bc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -159,6 +159,15 @@ if test "$with_xen" = yes ; then
AC_MSG_NOTICE([Xen support disabled])))
fi
+dnl new toolchains
+if test "$ARCH" = ppc; then
+AC_ARG_WITH([oldtoolchain],
+ AC_HELP_STRING([--with-oldtoolchain],[compile without crt.S
+ required for new toolchains]),
+ AC_DEFINE(PPC_OLDTOOLCHAIN, 1,
+ [Define to compile with old toolchains]))
+fi
+
dnl ---Sanity checks
if test "$CC" = "no"; then AC_MSG_ERROR([cc not found]); fi
if test "$CPP" = "no"; then AC_MSG_ERROR([cpp not found]); fi
diff --git a/purgatory/arch/ppc/Makefile b/purgatory/arch/ppc/Makefile
index 72289a0..4020778 100644
--- a/purgatory/arch/ppc/Makefile
+++ b/purgatory/arch/ppc/Makefile
@@ -6,6 +6,7 @@ ppc_PURGATORY_SRCS += purgatory/arch/ppc/v2wrap_32.S
ppc_PURGATORY_SRCS += purgatory/arch/ppc/misc.S
ppc_PURGATORY_SRCS += purgatory/arch/ppc/purgatory-ppc.c
ppc_PURGATORY_SRCS += purgatory/arch/ppc/console-ppc.c
+ppc_PURGATORY_SRCS += purgatory/arch/ppc/crt.S
dist += purgatory/arch/ppc/Makefile $(ppc_PURGATORY_SRCS) \
purgatory/arch/ppc/purgatory-ppc.h purgatory/arch/ppc/ppc_asm.h
diff --git a/purgatory/arch/ppc/crt.S b/purgatory/arch/ppc/crt.S
new file mode 100644
index 0000000..70f4d61
--- /dev/null
+++ b/purgatory/arch/ppc/crt.S
@@ -0,0 +1,263 @@
+/* This is from linux-2.6/arch/powerpc/lib/crtsavres.S:
+ *
+ * Special support for eabi and SVR4
+ *
+ * Copyright (C) 1995, 1996, 1998, 2000, 2001 Free Software Foundation, Inc.
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ * Written By Michael Meissner
+ *
+ * Based on gcc/config/rs6000/crtsavres.asm from gcc
+ *
+ * This file 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; either version 2, or (at your option) any
+ * later version.
+ *
+ * In addition to the permissions in the GNU General Public License, the
+ * Free Software Foundation gives you unlimited permission to link the
+ * compiled version of this file with other programs, and to distribute
+ * those programs without any restriction coming from the use of this
+ * file. (The General Public License restrictions do apply in other
+ * respects; for example, they cover modification of the file, and
+ * distribution when not linked into another program.)
+ *
+ * This file 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * As a special exception, if you link this library with files
+ * compiled with GCC to produce an executable, this does not cause
+ * the resulting executable to be covered by the GNU General Public License.
+ * This exception does not however invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public License.
+ */
+#include "config.h"
+
+/* On PowerPC64 Linux, these functions are provided by the linker. */
+#ifndef PPC_OLDTOOLCHAIN
+#ifndef __powerpc64__
+#define _GLOBAL(name) \
+ .type name,@function; \
+ .globl name; \
+name:
+
+/* Routines for saving integer registers, called by the compiler. */
+/* Called with r11 pointing to the stack header word of the caller of the */
+/* function, just beyond the end of the integer save area. */
+
+_GLOBAL(_savegpr_14)
+_GLOBAL(_save32gpr_14)
+ stw 14,-72(11) /* save gp registers */
+_GLOBAL(_savegpr_15)
+_GLOBAL(_save32gpr_15)
+ stw 15,-68(11)
+_GLOBAL(_savegpr_16)
+_GLOBAL(_save32gpr_16)
+ stw 16,-64(11)
+_GLOBAL(_savegpr_17)
+_GLOBAL(_save32gpr_17)
+ stw 17,-60(11)
+_GLOBAL(_savegpr_18)
+_GLOBAL(_save32gpr_18)
+ stw 18,-56(11)
+_GLOBAL(_savegpr_19)
+_GLOBAL(_save32gpr_19)
+ stw 19,-52(11)
+_GLOBAL(_savegpr_20)
+_GLOBAL(_save32gpr_20)
+ stw 20,-48(11)
+_GLOBAL(_savegpr_21)
+_GLOBAL(_save32gpr_21)
+ stw 21,-44(11)
+_GLOBAL(_savegpr_22)
+_GLOBAL(_save32gpr_22)
+ stw 22,-40(11)
+_GLOBAL(_savegpr_23)
+_GLOBAL(_save32gpr_23)
+ stw 23,-36(11)
+_GLOBAL(_savegpr_24)
+_GLOBAL(_save32gpr_24)
+ stw 24,-32(11)
+_GLOBAL(_savegpr_25)
+_GLOBAL(_save32gpr_25)
+ stw 25,-28(11)
+_GLOBAL(_savegpr_26)
+_GLOBAL(_save32gpr_26)
+ stw 26,-24(11)
+_GLOBAL(_savegpr_27)
+_GLOBAL(_save32gpr_27)
+ stw 27,-20(11)
+_GLOBAL(_savegpr_28)
+_GLOBAL(_save32gpr_28)
+ stw 28,-16(11)
+_GLOBAL(_savegpr_29)
+_GLOBAL(_save32gpr_29)
+ stw 29,-12(11)
+_GLOBAL(_savegpr_30)
+_GLOBAL(_save32gpr_30)
+ stw 30,-8(11)
+_GLOBAL(_savegpr_31)
+_GLOBAL(_save32gpr_31)
+ stw 31,-4(11)
+ blr
+
+/* Routines for restoring integer registers, called by the compiler. */
+/* Called with r11 pointing to the stack header word of the caller of the */
+/* function, just beyond the end of the integer restore area. */
+
+_GLOBAL(_restgpr_14)
+_GLOBAL(_rest32gpr_14)
+ lwz 14,-72(11) /* restore gp registers */
+_GLOBAL(_restgpr_15)
+_GLOBAL(_rest32gpr_15)
+ lwz 15,-68(11)
+_GLOBAL(_restgpr_16)
+_GLOBAL(_rest32gpr_16)
+ lwz 16,-64(11)
+_GLOBAL(_restgpr_17)
+_GLOBAL(_rest32gpr_17)
+ lwz 17,-60(11)
+_GLOBAL(_restgpr_18)
+_GLOBAL(_rest32gpr_18)
+ lwz 18,-56(11)
+_GLOBAL(_restgpr_19)
+_GLOBAL(_rest32gpr_19)
+ lwz 19,-52(11)
+_GLOBAL(_restgpr_20)
+_GLOBAL(_rest32gpr_20)
+ lwz 20,-48(11)
+_GLOBAL(_restgpr_21)
+_GLOBAL(_rest32gpr_21)
+ lwz 21,-44(11)
+_GLOBAL(_restgpr_22)
+_GLOBAL(_rest32gpr_22)
+ lwz 22,-40(11)
+_GLOBAL(_restgpr_23)
+_GLOBAL(_rest32gpr_23)
+ lwz 23,-36(11)
+_GLOBAL(_restgpr_24)
+_GLOBAL(_rest32gpr_24)
+ lwz 24,-32(11)
+_GLOBAL(_restgpr_25)
+_GLOBAL(_rest32gpr_25)
+ lwz 25,-28(11)
+_GLOBAL(_restgpr_26)
+_GLOBAL(_rest32gpr_26)
+ lwz 26,-24(11)
+_GLOBAL(_restgpr_27)
+_GLOBAL(_rest32gpr_27)
+ lwz 27,-20(11)
+_GLOBAL(_restgpr_28)
+_GLOBAL(_rest32gpr_28)
+ lwz 28,-16(11)
+_GLOBAL(_restgpr_29)
+_GLOBAL(_rest32gpr_29)
+ lwz 29,-12(11)
+_GLOBAL(_restgpr_30)
+_GLOBAL(_rest32gpr_30)
+ lwz 30,-8(11)
+_GLOBAL(_restgpr_31)
+_GLOBAL(_rest32gpr_31)
+ lwz 31,-4(11)
+ blr
+
+/* Routines for restoring integer registers, called by the compiler. */
+/* Called with r11 pointing to the stack header word of the caller of the */
+/* function, just beyond the end of the integer restore area. */
+
+_GLOBAL(_restgpr_14_x)
+_GLOBAL(_rest32gpr_14_x)
+ lwz 14,-72(11) /* restore gp registers */
+_GLOBAL(_restgpr_15_x)
+_GLOBAL(_rest32gpr_15_x)
+ lwz 15,-68(11)
+_GLOBAL(_restgpr_16_x)
+_GLOBAL(_rest32gpr_16_x)
+ lwz 16,-64(11)
+_GLOBAL(_restgpr_17_x)
+_GLOBAL(_rest32gpr_17_x)
+ lwz 17,-60(11)
+_GLOBAL(_restgpr_18_x)
+_GLOBAL(_rest32gpr_18_x)
+ lwz 18,-56(11)
+_GLOBAL(_restgpr_19_x)
+_GLOBAL(_rest32gpr_19_x)
+ lwz 19,-52(11)
+_GLOBAL(_restgpr_20_x)
+_GLOBAL(_rest32gpr_20_x)
+ lwz 20,-48(11)
+_GLOBAL(_restgpr_21_x)
+_GLOBAL(_rest32gpr_21_x)
+ lwz 21,-44(11)
+_GLOBAL(_restgpr_22_x)
+_GLOBAL(_rest32gpr_22_x)
+ lwz 22,-40(11)
+_GLOBAL(_restgpr_23_x)
+_GLOBAL(_rest32gpr_23_x)
+ lwz 23,-36(11)
+_GLOBAL(_restgpr_24_x)
+_GLOBAL(_rest32gpr_24_x)
+ lwz 24,-32(11)
+_GLOBAL(_restgpr_25_x)
+_GLOBAL(_rest32gpr_25_x)
+ lwz 25,-28(11)
+_GLOBAL(_restgpr_26_x)
+_GLOBAL(_rest32gpr_26_x)
+ lwz 26,-24(11)
+_GLOBAL(_restgpr_27_x)
+_GLOBAL(_rest32gpr_27_x)
+ lwz 27,-20(11)
+_GLOBAL(_restgpr_28_x)
+_GLOBAL(_rest32gpr_28_x)
+ lwz 28,-16(11)
+_GLOBAL(_restgpr_29_x)
+_GLOBAL(_rest32gpr_29_x)
+ lwz 29,-12(11)
+_GLOBAL(_restgpr_30_x)
+_GLOBAL(_rest32gpr_30_x)
+ lwz 30,-8(11)
+_GLOBAL(_restgpr_31_x)
+_GLOBAL(_rest32gpr_31_x)
+ lwz 0,4(11)
+ lwz 31,-4(11)
+ mtlr 0
+ mr 1,11
+ blr
+
+/* This is from linux-2.6/arch/powerpc/kernel/misc_32.S
+ *
+ * This file contains miscellaneous low-level functions.
+ * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
+ *
+ * Largely rewritten by Cort Dougan (cort@cs.nmt.edu)
+ * and Paul Mackerras.
+ *
+ * kexec bits:
+ * Copyright (C) 2002-2003 Eric Biederman <ebiederm@xmission.com>
+ * GameCube/ppc32 port Copyright (C) 2004 Albert Herranz
+ *
+ * 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; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+_GLOBAL(__lshrdi3)
+ subfic 6,5,32
+ srw 4,4,5 # LSW = count > 31 ? 0 : LSW >> count
+ addi 7,5,32 # could be xori, or addi with -32
+ slw 6,3,6 # t1 = count > 31 ? 0 : MSW << (32-count)
+ srw 7,3,7 # t2 = count < 32 ? 0 : MSW >> (count-32)
+ or 4,4,6 # LSW |= t1
+ srw 3,3,5 # MSW = MSW >> count
+ or 4,4,7 # LSW |= t2
+ blr
+#endif
+#endif
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2 2/2] powerpc new toolchains fix (crt.S)
2010-05-12 7:08 ` [PATCH v2 2/2] powerpc new toolchains fix (crt.S) Maxim Uvarov
@ 2010-05-13 15:00 ` Simon Horman
2010-05-13 17:27 ` Segher Boessenkool
1 sibling, 0 replies; 5+ messages in thread
From: Simon Horman @ 2010-05-13 15:00 UTC (permalink / raw)
To: Maxim Uvarov; +Cc: lists, kexec, linuxppc-dev
On Wed, May 12, 2010 at 11:08:49AM +0400, Maxim Uvarov wrote:
>
> Hello everybody,
>
> Changes from previous version:
> - removed bogus hyphen from the patch;
> - move ifdefs to crt.S instead of Makefile
>
>
> Please find here patch for user land kexec-tools application. Following
> patch makes kexec-tools work for both kexec and kdump. I tested it with
> git kernel (linus-tree) and Freescale/Logic MPC8360ERDK board with
> mpc83xx_defconfig kernel config.
>
> kexec:
> kexec -l vmlinux --command-line="console= ... etc"
> kexec -e
>
> kdump:
> kexec -p vmlinux_dump --command-line="console=... etc"
> echo c > /proc/sysrq-trigger
>
> I also think that is is reasonable:
> - put GAME_CUBE specific code to separate files;
> - combine ppc and ppc64 to powerpc directory (I'm planning to do it.
> And that why in some places my patch have ifdefs for PPC64);
>
> Best regards,
> Maxim Uvarov.
Thanks,
I'm assuming that the silence that this patch set received,
in contrast to the first revision where there was some quick discussion,
means that everyone is ok with these changes. So I've gone ahead and pushed
them into the tree.
> From: Maxim Uvarov <muvarov@gmail.com>
>
> Linker does not provide some vital functions when building freestanding
> applications with a new toolchain, so we have to provide our own CRT.
>
> p.s.
> Without the CRT we won't see any build errors (since the purgatory is
> linked with --no-undefined), but the purgatory code won't work,
> 'kexec -e' will just hang the board.
>
>
> I added option to configure to keep code buildable for old toolchais.
> But there should be way to do this automatically.
I seem to have an old toolchain :-)
> Author: Anton Vorontsov <avorontsov@ru.mvista.com>
> Signed-off-by: Maxim Uvarov <muvarov@gmail.com>
> Signed-off-by: Maxim Uvarov <muvarov@gmail.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 2/2] powerpc new toolchains fix (crt.S)
2010-05-12 7:08 ` [PATCH v2 2/2] powerpc new toolchains fix (crt.S) Maxim Uvarov
2010-05-13 15:00 ` Simon Horman
@ 2010-05-13 17:27 ` Segher Boessenkool
2010-05-13 19:07 ` Anton Vorontsov
1 sibling, 1 reply; 5+ messages in thread
From: Segher Boessenkool @ 2010-05-13 17:27 UTC (permalink / raw)
To: Maxim Uvarov; +Cc: lists, kexec, horms, linuxppc-dev
> Linker does not provide some vital functions when building
> freestanding
> applications with a new toolchain,
That's because the compiler provides those functions, not the linker.
> so we have to provide our own CRT.
...in libgcc. Why don't you link against that?
> p.s.
> Without the CRT we won't see any build errors (since the purgatory is
> linked with --no-undefined), but the purgatory code won't work,
It would be nice if the build would check if anything unexpectedly
undefined is left.
Segher
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 2/2] powerpc new toolchains fix (crt.S)
2010-05-13 17:27 ` Segher Boessenkool
@ 2010-05-13 19:07 ` Anton Vorontsov
0 siblings, 0 replies; 5+ messages in thread
From: Anton Vorontsov @ 2010-05-13 19:07 UTC (permalink / raw)
To: Segher Boessenkool; +Cc: Maxim Uvarov, lists, horms, kexec, linuxppc-dev
On Thu, May 13, 2010 at 07:27:59PM +0200, Segher Boessenkool wrote:
> >Linker does not provide some vital functions when building
> >freestanding
> >applications with a new toolchain,
>
> That's because the compiler provides those functions, not the linker.
>
> >so we have to provide our own CRT.
>
> ...in libgcc. Why don't you link against that?
For the same reason we don't link kernel against libgcc?
I.e. just a matter of taste. There are two camps: those who
argue against libgcc, and those who argue for libgcc.
Hey! You were in this thread! :-)
http://www.mail-archive.com/linuxppc-dev@lists.ozlabs.org/msg17224.html
Purgatory is just a small (tiny) piece of code that runs
between kernels. Whether use libgcc or not, personally, I have
no strong opinion. Kexec didn't use it, so we kept it the same
way.
Thanks,
--
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2010-05-13 19:07 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-05-12 7:08 [PATCH v2 1/2] Fix kexec on powerpc32 Maxim Uvarov
2010-05-12 7:08 ` [PATCH v2 2/2] powerpc new toolchains fix (crt.S) Maxim Uvarov
2010-05-13 15:00 ` Simon Horman
2010-05-13 17:27 ` Segher Boessenkool
2010-05-13 19:07 ` Anton Vorontsov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).