This purgatory implementation comes from kexec-tools, almost unchanged.

The only changes were that the sha256_regions global variable was
renamed to sha_regions to match what kexec_file_load expects, and to
use the sha256.c file from x86's purgatory to avoid adding yet another
SHA-256 implementation.

Cc: ke...@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
---
 arch/powerpc/Makefile                     |   4 +
 arch/powerpc/purgatory/.gitignore         |   2 +
 arch/powerpc/purgatory/Makefile           |  36 +++++++
 arch/powerpc/purgatory/console-ppc64.c    |  43 ++++++++
 arch/powerpc/purgatory/crashdump-ppc64.h  |  42 ++++++++
 arch/powerpc/purgatory/crashdump_backup.c |  40 +++++++
 arch/powerpc/purgatory/crtsavres.S        |   5 +
 arch/powerpc/purgatory/hvCall.S           |  27 +++++
 arch/powerpc/purgatory/hvCall.h           |   8 ++
 arch/powerpc/purgatory/kexec-sha256.h     |  11 ++
 arch/powerpc/purgatory/ppc64_asm.h        |  18 ++++
 arch/powerpc/purgatory/printf.c           | 171 ++++++++++++++++++++++++++++++
 arch/powerpc/purgatory/purgatory-ppc64.c  |  46 ++++++++
 arch/powerpc/purgatory/purgatory-ppc64.h  |   6 ++
 arch/powerpc/purgatory/purgatory.c        |  66 ++++++++++++
 arch/powerpc/purgatory/purgatory.h        |  11 ++
 arch/powerpc/purgatory/sha256.c           |   6 ++
 arch/powerpc/purgatory/sha256.h           |   1 +
 arch/powerpc/purgatory/string.S           |   1 +
 arch/powerpc/purgatory/v2wrap.S           | 139 ++++++++++++++++++++++++
 20 files changed, 683 insertions(+)

diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
index 709a22a3e824..293322855cce 100644
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -249,6 +249,7 @@ core-y                              += arch/powerpc/kernel/ 
\
 core-$(CONFIG_XMON)            += arch/powerpc/xmon/
 core-$(CONFIG_KVM)             += arch/powerpc/kvm/
 core-$(CONFIG_PERF_EVENTS)     += arch/powerpc/perf/
+core-$(CONFIG_KEXEC_FILE)      += arch/powerpc/purgatory/
 
 drivers-$(CONFIG_OPROFILE)     += arch/powerpc/oprofile/
 
@@ -370,6 +371,9 @@ archclean:
        $(Q)$(MAKE) $(clean)=$(boot)
 
 archprepare: checkbin
+ifeq ($(CONFIG_KEXEC_FILE),y)
+       $(Q)$(MAKE) $(build)=arch/powerpc/purgatory 
arch/powerpc/purgatory/kexec-purgatory.c
+endif
 
 # Use the file '.tmp_gas_check' for binutils tests, as gas won't output
 # to stdout and these checks are run even on install targets.
diff --git a/arch/powerpc/purgatory/.gitignore 
b/arch/powerpc/purgatory/.gitignore
new file mode 100644
index 000000000000..e9e66f178a6d
--- /dev/null
+++ b/arch/powerpc/purgatory/.gitignore
@@ -0,0 +1,2 @@
+kexec-purgatory.c
+purgatory.ro
diff --git a/arch/powerpc/purgatory/Makefile b/arch/powerpc/purgatory/Makefile
new file mode 100644
index 000000000000..63daf95e5703
--- /dev/null
+++ b/arch/powerpc/purgatory/Makefile
@@ -0,0 +1,36 @@
+purgatory-y := purgatory.o printf.o string.o v2wrap.o hvCall.o \
+               purgatory-ppc64.o console-ppc64.o crashdump_backup.o \
+               crtsavres.o sha256.o
+
+targets += $(purgatory-y)
+PURGATORY_OBJS = $(addprefix $(obj)/,$(purgatory-y))
+
+LDFLAGS_purgatory.ro := -e purgatory_start -r --no-undefined -nostartfiles \
+                       -nostdlib -nodefaultlibs
+targets += purgatory.ro
+
+# Default KBUILD_CFLAGS can have -pg option set when FTRACE is enabled. That
+# in turn leaves some undefined symbols like __fentry__ in purgatory and not
+# sure how to relocate those. Like kexec-tools, use custom flags.
+
+KBUILD_CFLAGS := -Wall -Wstrict-prototypes -fno-strict-aliasing \
+               -fno-zero-initialized-in-bss -fno-builtin -ffreestanding \
+               -fno-PIC -fno-PIE -fno-stack-protector  -fno-exceptions \
+               -msoft-float -MD -Os
+KBUILD_CFLAGS += -m$(CONFIG_WORD_SIZE)
+
+$(obj)/purgatory.ro: $(PURGATORY_OBJS) FORCE
+               $(call if_changed,ld)
+
+targets += kexec-purgatory.c
+
+CMD_BIN2C = $(objtree)/scripts/basic/bin2c
+quiet_cmd_bin2c = BIN2C   $@
+      cmd_bin2c = $(CMD_BIN2C) kexec_purgatory < $< > $@
+
+$(obj)/kexec-purgatory.c: $(obj)/purgatory.ro FORCE
+       $(call if_changed,bin2c)
+       @:
+
+
+obj-$(CONFIG_KEXEC_FILE)       += kexec-purgatory.o
diff --git a/arch/powerpc/purgatory/console-ppc64.c 
b/arch/powerpc/purgatory/console-ppc64.c
new file mode 100644
index 000000000000..a52e043b4813
--- /dev/null
+++ b/arch/powerpc/purgatory/console-ppc64.c
@@ -0,0 +1,43 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Created by: Mohan Kumar M (mo...@in.ibm.com)
+ *
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * Code taken from kexec-tools.
+ *
+ * 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 "hvCall.h"
+#include <asm/byteorder.h>
+
+extern int debug;
+
+void putchar(int c)
+{
+       char buff[8];
+       unsigned long *lbuf = (unsigned long *)buff;
+
+       if (!debug) /* running on non pseries */
+               return;
+
+       if (c == '\n')
+               putchar('\r');
+
+       buff[0] = c;
+       plpar_hcall_norets(H_PUT_TERM_CHAR, 0, 1, __cpu_to_be64(*lbuf), 0);
+       return;
+}
diff --git a/arch/powerpc/purgatory/crashdump-ppc64.h 
b/arch/powerpc/purgatory/crashdump-ppc64.h
new file mode 100644
index 000000000000..90064b49ebfe
--- /dev/null
+++ b/arch/powerpc/purgatory/crashdump-ppc64.h
@@ -0,0 +1,42 @@
+#ifndef CRASHDUMP_PPC64_H
+#define CRASHDUMP_PPC64_H
+
+#include <linux/types.h>
+
+struct kexec_info;
+int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline,
+                               uint64_t max_addr, unsigned long min_base);
+void add_usable_mem_rgns(unsigned long long base, unsigned long long size);
+
+#define PAGE_OFFSET     0xC000000000000000ULL
+#define KERNELBASE      PAGE_OFFSET
+#define VMALLOCBASE     0xD000000000000000ULL
+
+#define __pa(x)         ((unsigned long)(x)-PAGE_OFFSET)
+#define MAXMEM          (-KERNELBASE-VMALLOCBASE)
+
+#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 KERNEL_RUN_AT_ZERO_MAGIC 0x72756e30    /* "run0" */
+
+extern uint64_t crash_base;
+extern uint64_t crash_size;
+extern uint64_t memory_limit;
+extern unsigned int rtas_base;
+extern unsigned int rtas_size;
+extern uint64_t opal_base;
+extern uint64_t opal_size;
+
+uint64_t lmb_size;
+unsigned int num_of_lmbs;
+
+#define DRCONF_ADDR    0
+#define DRCONF_FLAGS   20
+
+#endif /* CRASHDUMP_PPC64_H */
diff --git a/arch/powerpc/purgatory/crashdump_backup.c 
b/arch/powerpc/purgatory/crashdump_backup.c
new file mode 100644
index 000000000000..e8491617527a
--- /dev/null
+++ b/arch/powerpc/purgatory/crashdump_backup.c
@@ -0,0 +1,40 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Created by: Mohan Kumar M (mo...@in.ibm.com)
+ *
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * Code taken from kexec-tools.
+ *
+ * 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 "../boot/string.h"
+#include "crashdump-ppc64.h"
+
+extern unsigned long backup_start;
+
+/* Backup first 32KB of memory to backup region reserved by kexec */
+void crashdump_backup_memory(void)
+{
+       void *dest, *src;
+
+       src = (void *)BACKUP_SRC_START;
+
+       if (backup_start) {
+               dest = (void *)(backup_start);
+               memcpy(dest, src, BACKUP_SRC_SIZE);
+       }
+}
diff --git a/arch/powerpc/purgatory/crtsavres.S 
b/arch/powerpc/purgatory/crtsavres.S
new file mode 100644
index 000000000000..5d17e1c0d575
--- /dev/null
+++ b/arch/powerpc/purgatory/crtsavres.S
@@ -0,0 +1,5 @@
+#ifndef CONFIG_CC_OPTIMIZE_FOR_SIZE
+#define CONFIG_CC_OPTIMIZE_FOR_SIZE 1
+#endif
+
+#include "../lib/crtsavres.S"
diff --git a/arch/powerpc/purgatory/hvCall.S b/arch/powerpc/purgatory/hvCall.S
new file mode 100644
index 000000000000..a96c4898f1d8
--- /dev/null
+++ b/arch/powerpc/purgatory/hvCall.S
@@ -0,0 +1,27 @@
+/*
+ * This file contains the generic function to perform a call to the
+ * pSeries LPAR hypervisor.
+ *
+ * Taken from linux/arch/powerpc/platforms/pseries/hvCall.S
+ *
+ * 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.
+ */
+#include "ppc64_asm.h"
+
+#define HVSC   .long 0x44000022
+.text
+       .machine ppc64
+.globl DOTSYM(plpar_hcall_norets)
+DOTSYM(plpar_hcall_norets):
+       or      6,6,6                   # medium low priority
+        mfcr   0
+        stw    0,8(1)
+
+        HVSC                           /* invoke the hypervisor */
+
+        lwz    0,8(1)
+        mtcrf  0xff,0
+        blr                             /* return r3 = status */
diff --git a/arch/powerpc/purgatory/hvCall.h b/arch/powerpc/purgatory/hvCall.h
new file mode 100644
index 000000000000..187e24d8b964
--- /dev/null
+++ b/arch/powerpc/purgatory/hvCall.h
@@ -0,0 +1,8 @@
+#ifndef HVCALL_H
+#define HVCALL_H
+
+#define H_PUT_TERM_CHAR        0x58
+
+long plpar_hcall_norets(unsigned long opcode, ...);
+
+#endif
diff --git a/arch/powerpc/purgatory/kexec-sha256.h 
b/arch/powerpc/purgatory/kexec-sha256.h
new file mode 100644
index 000000000000..4418ed02c052
--- /dev/null
+++ b/arch/powerpc/purgatory/kexec-sha256.h
@@ -0,0 +1,11 @@
+#ifndef KEXEC_SHA256_H
+#define KEXEC_SHA256_H
+
+struct kexec_sha_region {
+       unsigned long start;
+       unsigned long len;
+};
+
+#define SHA256_REGIONS 16
+
+#endif /* KEXEC_SHA256_H */
diff --git a/arch/powerpc/purgatory/ppc64_asm.h 
b/arch/powerpc/purgatory/ppc64_asm.h
new file mode 100644
index 000000000000..3410cf66ae35
--- /dev/null
+++ b/arch/powerpc/purgatory/ppc64_asm.h
@@ -0,0 +1,18 @@
+/*
+ * ppc64_asm.h - common defines for PPC64 assembly parts
+ *
+ * Code taken from kexec-tools.
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2.  See the file COPYING for more details.
+ */
+
+/*
+ * ABIv1 requires dot symbol while ABIv2 does not.
+ */
+#if defined(_CALL_ELF) && _CALL_ELF == 2
+#define DOTSYM(a)      a
+#else
+#define GLUE(a,b)      a##b
+#define DOTSYM(a)      GLUE(.,a)
+#endif
diff --git a/arch/powerpc/purgatory/printf.c b/arch/powerpc/purgatory/printf.c
new file mode 100644
index 000000000000..73c85f251ae6
--- /dev/null
+++ b/arch/powerpc/purgatory/printf.c
@@ -0,0 +1,171 @@
+/*
+ * Code taken from kexec-tools.
+ *
+ * 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 <stdarg.h>
+#include "purgatory.h"
+#include "../boot/string.h"
+
+#define CHAR_BIT 8
+
+/*
+ * Output
+ * 
=============================================================================
+ */
+
+#define LONG_LONG_SHIFT  ((int)((sizeof(unsigned long long)*CHAR_BIT) - 4))
+#define LONG_SHIFT  ((int)((sizeof(unsigned long)*CHAR_BIT) - 4))
+#define INT_SHIFT   ((int)((sizeof(unsigned int)*CHAR_BIT) - 4))
+#define SHRT_SHIFT  ((int)((sizeof(unsigned short)*CHAR_BIT) - 4))
+#define CHAR_SHIFT  ((int)((sizeof(unsigned char)*CHAR_BIT) - 4))
+
+/**************************************************************************
+PRINTF and friends
+
+       Formats:
+               %x      - 4 bytes int (8 hex digits, lower case)
+               %X      - 4 bytes int (8 hex digits, upper case)
+               %lx     - 8 bytes long (16 hex digits, lower case)
+               %lX     - 8 bytes long (16 hex digits, upper case)
+               %hx     - 2 bytes int (4 hex digits, lower case)
+               %hX     - 2 bytes int (4 hex digits, upper case)
+               %hhx    - 1 byte int (2 hex digits, lower case)
+               %hhX    - 1 byte int (2 hex digits, upper case)
+                       - optional # prefixes 0x or 0X
+               %d      - decimal int
+               %c      - char
+               %s      - string
+       Note: width specification not supported
+**************************************************************************/
+void vsprintf(char *buffer, const char *fmt, va_list args)
+{
+       char *p;
+       for ( ; *fmt != '\0'; ++fmt) {
+               if (*fmt != '%') {
+                       if (buffer)
+                               *buffer++ = *fmt;
+                       else
+                               putchar(*fmt);
+                       continue;
+               }
+               if (*++fmt == 's') {
+                       for(p = va_arg(args, char *); *p != '\0'; p++)
+                               if (buffer)
+                                       *buffer++ = *p;
+                               else
+                                       putchar(*p);
+               }
+               else {  /* Length of item is bounded */
+                       char tmp[40], *q = tmp;
+                       int shift = INT_SHIFT;
+                       if (*fmt == 'L') {
+                               shift = LONG_LONG_SHIFT;
+                               fmt++;
+                       }
+                       else if (*fmt == 'l') {
+                               shift = LONG_SHIFT;
+                               fmt++;
+                       }
+                       else if (*fmt == 'h') {
+                               shift = SHRT_SHIFT;
+                               fmt++;
+                               if (*fmt == 'h') {
+                                       shift = CHAR_SHIFT;
+                                       fmt++;
+                               }
+                       }
+
+                       /*
+                        * Before each format q points to tmp buffer
+                        * After each format q points past end of item
+                        */
+                       if ((*fmt | 0x20) == 'x') {
+                               /* With x86 gcc, sizeof(long) == sizeof(int) */
+                               unsigned long long h;
+                               int ncase;
+                               if (shift > LONG_SHIFT) {
+                                       h = va_arg(args, unsigned long long);
+                               }
+                               else if (shift > INT_SHIFT) {
+                                       h = va_arg(args, unsigned long);
+                               } else {
+                                       h = va_arg(args, unsigned int);
+                               }
+                               ncase = (*fmt & 0x20);
+                               for ( ; shift >= 0; shift -= 4)
+                                       *q++ = "0123456789ABCDEF"[(h >> shift) 
& 0xF] | ncase;
+                       }
+                       else if (*fmt == 'd') {
+                               char *r;
+                               long i;
+                               if (shift > LONG_SHIFT) {
+                                       i = va_arg(args, long long);
+                               }
+                               else if (shift > INT_SHIFT) {
+                                       i = va_arg(args, long);
+                               } else {
+                                       i = va_arg(args, int);
+                               }
+                               if (i < 0) {
+                                       *q++ = '-';
+                                       i = -i;
+                               }
+                               p = q;          /* save beginning of digits */
+                               do {
+                                       *q++ = '0' + (i % 10);
+                                       i /= 10;
+                               } while (i);
+                               /* reverse digits, stop in middle */
+                               r = q;          /* don't alter q */
+                               while (--r > p) {
+                                       i = *r;
+                                       *r = *p;
+                                       *p++ = i;
+                               }
+                       }
+                       else if (*fmt == 'c')
+                               *q++ = va_arg(args, int);
+                       else
+                               *q++ = *fmt;
+                       /* now output the saved string */
+                       for (p = tmp; p < q; ++p)
+                               if (buffer)
+                                       *buffer++ = *p;
+                               else
+                                       putchar(*p);
+               }
+       }
+       if (buffer)
+               *buffer = '\0';
+}
+
+void sprintf(char *buffer, const char *fmt, ...)
+{
+       va_list args;
+
+       va_start(args, fmt);
+       vsprintf(buffer, fmt, args);
+       va_end(args);
+}
+
+void printf(const char *fmt, ...)
+{
+       va_list args;
+
+       va_start(args, fmt);
+       vsprintf(0, fmt, args);
+       va_end(args);
+}
diff --git a/arch/powerpc/purgatory/purgatory-ppc64.c 
b/arch/powerpc/purgatory/purgatory-ppc64.c
new file mode 100644
index 000000000000..01145d9ff4c1
--- /dev/null
+++ b/arch/powerpc/purgatory/purgatory-ppc64.c
@@ -0,0 +1,46 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Created by: Mohan Kumar M (mo...@in.ibm.com)
+ *
+ * Copyright (C) IBM Corporation, 2005. All rights reserved
+ *
+ * Code taken from kexec-tools.
+ *
+ * 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-ppc64.h"
+
+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;
+unsigned int debug = 0;
+unsigned long opal_base = 0;
+unsigned long opal_entry = 0;
+
+void setup_arch(void)
+{
+       return;
+}
+
+void post_verification_setup_arch(void)
+{
+       if (panic_kernel)
+               crashdump_backup_memory();
+}
diff --git a/arch/powerpc/purgatory/purgatory-ppc64.h 
b/arch/powerpc/purgatory/purgatory-ppc64.h
new file mode 100644
index 000000000000..52eaf4394c48
--- /dev/null
+++ b/arch/powerpc/purgatory/purgatory-ppc64.h
@@ -0,0 +1,6 @@
+#ifndef PURGATORY_PPC64_H
+#define PURGATORY_PPC64_H
+
+void crashdump_backup_memory(void);
+
+#endif /* PURGATORY_PPC64_H */
diff --git a/arch/powerpc/purgatory/purgatory.c 
b/arch/powerpc/purgatory/purgatory.c
new file mode 100644
index 000000000000..7d2d83466e4c
--- /dev/null
+++ b/arch/powerpc/purgatory/purgatory.c
@@ -0,0 +1,66 @@
+/*
+ * Code taken from kexec-tools.
+ *
+ * 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 "sha256.h"
+#include "../boot/string.h"
+#include "kexec-sha256.h"
+
+struct kexec_sha_region sha_regions[SHA256_REGIONS] = {};
+u8 sha256_digest[SHA256_DIGEST_SIZE] = { 0 };
+
+int verify_sha256_digest(void)
+{
+       struct kexec_sha_region *ptr, *end;
+       u8 digest[SHA256_DIGEST_SIZE];
+       size_t i;
+       struct sha256_state sctx;
+
+       sha256_init(&sctx);
+       end = &sha_regions[sizeof(sha_regions)/sizeof(sha_regions[0])];
+       for (ptr = sha_regions; ptr < end; ptr++)
+               sha256_update(&sctx, (uint8_t *)(ptr->start), ptr->len);
+       sha256_final(&sctx, digest);
+
+       if (memcmp(digest, sha256_digest, sizeof(digest)) != 0) {
+               printf("sha256 digests do not match :(\n");
+               printf("       digest: ");
+               for(i = 0; i < sizeof(digest); i++)
+                       printf("%hhx ", digest[i]);
+               printf("\n");
+
+               printf("sha256_digest: ");
+               for(i = 0; i < sizeof(sha256_digest); i++)
+                       printf("%hhx ", sha256_digest[i]);
+
+               printf("\n");
+               return 1;
+       }
+       return 0;
+}
+
+void purgatory(void)
+{
+       printf("I'm in purgatory\n");
+       setup_arch();
+       if (verify_sha256_digest()) {
+               /* loop forever */
+               for(;;)
+                       ;
+       }
+       post_verification_setup_arch();
+}
diff --git a/arch/powerpc/purgatory/purgatory.h 
b/arch/powerpc/purgatory/purgatory.h
new file mode 100644
index 000000000000..788ce4930a30
--- /dev/null
+++ b/arch/powerpc/purgatory/purgatory.h
@@ -0,0 +1,11 @@
+#ifndef PURGATORY_H
+#define PURGATORY_H
+
+void putchar(int ch);
+void sprintf(char *buffer, const char *fmt, ...)
+       __attribute__ ((format (printf, 2, 3)));
+void printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+void setup_arch(void);
+void post_verification_setup_arch(void);
+
+#endif /* PURGATORY_H */
diff --git a/arch/powerpc/purgatory/sha256.c b/arch/powerpc/purgatory/sha256.c
new file mode 100644
index 000000000000..6abee1877d56
--- /dev/null
+++ b/arch/powerpc/purgatory/sha256.c
@@ -0,0 +1,6 @@
+#include "../boot/string.h"
+
+/* Avoid including x86's boot/string.h in sha256.c. */
+#define BOOT_STRING_H
+
+#include "../../x86/purgatory/sha256.c"
diff --git a/arch/powerpc/purgatory/sha256.h b/arch/powerpc/purgatory/sha256.h
new file mode 100644
index 000000000000..72818f3a207e
--- /dev/null
+++ b/arch/powerpc/purgatory/sha256.h
@@ -0,0 +1 @@
+#include "../../x86/purgatory/sha256.h"
diff --git a/arch/powerpc/purgatory/string.S b/arch/powerpc/purgatory/string.S
new file mode 100644
index 000000000000..3a1e23ff4017
--- /dev/null
+++ b/arch/powerpc/purgatory/string.S
@@ -0,0 +1 @@
+#include "../boot/string.S"
diff --git a/arch/powerpc/purgatory/v2wrap.S b/arch/powerpc/purgatory/v2wrap.S
new file mode 100644
index 000000000000..c7791819c0c6
--- /dev/null
+++ b/arch/powerpc/purgatory/v2wrap.S
@@ -0,0 +1,139 @@
+#
+#  kexec: Linux boots Linux
+#
+#  Copyright (C) 2004 - 2005, Milton D Miller II, IBM Corporation
+#  Copyright (C) 2006, Mohan Kumar M (mo...@in.ibm.com), IBM Corporation
+#
+# Code taken from kexec-tools.
+#
+#  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 "ppc64_asm.h"
+
+# v2wrap.S
+# a wrapper to call purgatory code to backup first
+# 32kB of first kernel into the backup region
+# reserved by kexec-tools.
+# Invokes ppc64 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.
+
+#define LOADADDR(rn,name) \
+       lis     rn,name##@highest;      \
+       ori     rn,rn,name##@higher;    \
+       rldicr  rn,rn,32,31;            \
+       oris    rn,rn,name##@h;         \
+       ori     rn,rn,name##@l
+
+       .machine ppc64
+       .align 8
+       .globl purgatory_start
+purgatory_start:       b       master
+       .org purgatory_start + 0x5c     # ABI: possible run_at_load flag at 0x5c
+       .globl run_at_load
+run_at_load:
+       .long 0
+       .size run_at_load, . - run_at_load
+       .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/ppc64/kexec-elf-ppc64.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
+
+       LOADADDR(6,my_toc)
+       ld      2,0(6)          #setup toc
+
+       LOADADDR(6,stack)
+       ld      1,0(6)          #setup stack
+
+       subi    1,1,112
+       bl      DOTSYM(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
+
+       LOADADDR(16, dt_offset)
+       ld      3,0(16)         # load device-tree address
+       mr      16,3            # save dt address in reg16
+#ifdef __BIG_ENDIAN__
+       lwz     6,20(3)         # fetch version number
+#else
+       li      4,20
+       lwbrx   6,3,4           # fetch BE version number
+#endif
+       cmpwi   0,6,2           # v2 ?
+       blt     80f
+#ifdef __BIG_ENDIAN__
+       stw     17,28(3)        # save my cpu number as boot_cpu_phys
+#else
+       li      4,28
+       stwbrx  17,3,4          # Store my cpu as BE value
+#endif
+80:
+       LOADADDR(6,opal_base)   # For OPAL early debug
+       ld      8,0(6)          # load the OPAL base address in r8
+       LOADADDR(6,opal_entry)  # For OPAL early debug
+       ld      9,0(6)          # load the OPAL entry address in r9
+       LOADADDR(6,kernel)
+       ld      4,0(6)          # load the kernel address
+       LOADADDR(6,run_at_load) # the load flag
+       lwz     7,0(6)          # possibly patched by kexec-elf-ppc64
+       stw     7,0x5c(4)       # and patch it into the kernel
+       mr      3,16            # restore dt address
+
+       mfmsr   5
+       andi.   10,5,1          # test MSR_LE
+       bne     little_endian
+
+       li      5,0             # r5 will be 0 for kernel
+       mtctr   4               # prepare branch to
+       bctr                    # start kernel
+
+little_endian:                 # book3s-only
+       mtsrr0  4               # prepare branch to
+
+       clrrdi  5,5,1           # clear MSR_LE
+       mtsrr1  5
+
+       li      5,0             # r5 will be 0 for kernel
+
+                               # skip cache flush, do we care?
+
+       rfid                    # update MSR and start kernel
-- 
1.9.1

Reply via email to