summaryrefslogtreecommitdiffstats
path: root/purgatory
diff options
context:
space:
mode:
Diffstat (limited to 'purgatory')
-rw-r--r--purgatory/Makefile76
-rw-r--r--purgatory/arch/alpha/Makefile8
-rw-r--r--purgatory/arch/arm/Makefile9
-rw-r--r--purgatory/arch/arm64/Makefile19
-rw-r--r--purgatory/arch/arm64/entry.S51
-rw-r--r--purgatory/arch/arm64/purgatory-arm64.c34
-rw-r--r--purgatory/arch/i386/Makefile21
-rw-r--r--purgatory/arch/i386/compat_x86_64.S100
-rw-r--r--purgatory/arch/i386/console-x86.c135
-rw-r--r--purgatory/arch/i386/crashdump_backup.c51
-rw-r--r--purgatory/arch/i386/entry32-16-debug.S200
-rw-r--r--purgatory/arch/i386/entry32-16.S165
-rw-r--r--purgatory/arch/i386/entry32.S110
-rw-r--r--purgatory/arch/i386/include/arch/debug.h316
-rw-r--r--purgatory/arch/i386/include/arch/io.h110
-rw-r--r--purgatory/arch/i386/pic.c51
-rw-r--r--purgatory/arch/i386/purgatory-x86.c57
-rw-r--r--purgatory/arch/i386/purgatory-x86.h9
-rw-r--r--purgatory/arch/i386/setup-x86.S75
-rw-r--r--purgatory/arch/i386/stack.S39
-rw-r--r--purgatory/arch/i386/vga.c152
-rw-r--r--purgatory/arch/ia64/Makefile17
-rw-r--r--purgatory/arch/ia64/console-ia64.c47
-rw-r--r--purgatory/arch/ia64/entry.S73
-rw-r--r--purgatory/arch/ia64/io.h108
-rw-r--r--purgatory/arch/ia64/purgatory-ia64.c307
-rw-r--r--purgatory/arch/ia64/purgatory-ia64.h5
-rw-r--r--purgatory/arch/ia64/vga.c143
-rw-r--r--purgatory/arch/loongarch/Makefile10
-rw-r--r--purgatory/arch/loongarch/console-loongarch.c7
-rw-r--r--purgatory/arch/loongarch/purgatory-loongarch.c7
-rw-r--r--purgatory/arch/loongarch/purgatory-loongarch.h6
-rw-r--r--purgatory/arch/mips/Makefile10
-rw-r--r--purgatory/arch/mips/console-mips.c7
-rw-r--r--purgatory/arch/mips/purgatory-mips.c7
-rw-r--r--purgatory/arch/mips/purgatory-mips.h6
-rw-r--r--purgatory/arch/ppc/Makefile14
-rw-r--r--purgatory/arch/ppc/console-ppc.c7
-rw-r--r--purgatory/arch/ppc/misc.S301
-rw-r--r--purgatory/arch/ppc/ppc_asm.h506
-rw-r--r--purgatory/arch/ppc/purgatory-ppc.c48
-rw-r--r--purgatory/arch/ppc/purgatory-ppc.h6
-rw-r--r--purgatory/arch/ppc/v2wrap_32.S95
-rw-r--r--purgatory/arch/ppc64/Makefile28
-rw-r--r--purgatory/arch/ppc64/console-ppc64.c45
-rw-r--r--purgatory/arch/ppc64/crashdump_backup.c39
-rw-r--r--purgatory/arch/ppc64/hvCall.S27
-rw-r--r--purgatory/arch/ppc64/hvCall.h8
-rw-r--r--purgatory/arch/ppc64/misc.S197
-rw-r--r--purgatory/arch/ppc64/ppc64_asm.h16
-rw-r--r--purgatory/arch/ppc64/purgatory-ppc64.c44
-rw-r--r--purgatory/arch/ppc64/purgatory-ppc64.h6
-rw-r--r--purgatory/arch/ppc64/v2wrap.S137
-rw-r--r--purgatory/arch/s390/Makefile10
-rw-r--r--purgatory/arch/s390/console-s390.c14
-rw-r--r--purgatory/arch/s390/purgatory-s390.c93
-rw-r--r--purgatory/arch/s390/setup-s390.S47
-rw-r--r--purgatory/arch/sh/Makefile8
-rw-r--r--purgatory/arch/x86_64/Makefile28
-rw-r--r--purgatory/arch/x86_64/entry64-32.S164
-rw-r--r--purgatory/arch/x86_64/entry64.S107
-rw-r--r--purgatory/arch/x86_64/include/arch/debug.h317
-rw-r--r--purgatory/arch/x86_64/include/arch/io.h7
-rw-r--r--purgatory/arch/x86_64/purgatory-x86_64.c30
-rw-r--r--purgatory/arch/x86_64/purgatory-x86_64.h4
-rw-r--r--purgatory/arch/x86_64/setup-x86_64.S76
-rw-r--r--purgatory/arch/x86_64/stack.S44
-rw-r--r--purgatory/include/purgatory.h11
-rw-r--r--purgatory/include/string.h12
-rw-r--r--purgatory/printf.c154
-rw-r--r--purgatory/purgatory.c53
-rw-r--r--purgatory/string.c55
72 files changed, 5306 insertions, 0 deletions
diff --git a/purgatory/Makefile b/purgatory/Makefile
new file mode 100644
index 0000000..4d2d071
--- /dev/null
+++ b/purgatory/Makefile
@@ -0,0 +1,76 @@
+#
+# Purgatory (an uncomfortable intermediate state)
+# In this case the code that runs between kernels
+#
+
+# There is probably a cleaner way to do this but for now this
+# should keep us from accidentially include unsafe library functions
+# or headers.
+
+PURGATORY = purgatory/purgatory.ro
+PURGATORY_SRCS =
+PURGATORY_SRCS += purgatory/purgatory.c
+PURGATORY_SRCS += purgatory/printf.c
+PURGATORY_SRCS += purgatory/string.c
+PURGATORY_MAP = purgatory/purgatory.map
+
+dist += purgatory/Makefile $(PURGATORY_SRCS) \
+ purgatory/include/purgatory.h purgatory/include/string.h
+
+include $(srcdir)/purgatory/arch/alpha/Makefile
+include $(srcdir)/purgatory/arch/arm/Makefile
+include $(srcdir)/purgatory/arch/arm64/Makefile
+include $(srcdir)/purgatory/arch/i386/Makefile
+include $(srcdir)/purgatory/arch/ia64/Makefile
+include $(srcdir)/purgatory/arch/mips/Makefile
+include $(srcdir)/purgatory/arch/ppc/Makefile
+include $(srcdir)/purgatory/arch/ppc64/Makefile
+include $(srcdir)/purgatory/arch/s390/Makefile
+include $(srcdir)/purgatory/arch/sh/Makefile
+include $(srcdir)/purgatory/arch/x86_64/Makefile
+include $(srcdir)/purgatory/arch/loongarch/Makefile
+
+PURGATORY_SRCS+=$($(ARCH)_PURGATORY_SRCS)
+
+PURGATORY_OBJS = $(call objify, $(PURGATORY_SRCS)) purgatory/sha256.o
+PURGATORY_DEPS = $(call depify, $(PURGATORY_OBJS))
+
+clean += $(PURGATORY_OBJS) $(PURGATORY_DEPS) $(PURGATORY) $(PURGATORY_MAP) $(PURGATORY).sym
+
+-include $(PURGATORY_DEPS)
+
+purgatory/sha256.o: CFLAGS += -O2 $($(ARCH)_PURGATORY_SHA256_CFLAGS)
+
+purgatory/sha256.o: $(srcdir)/util_lib/sha256.c
+ mkdir -p $(@D)
+ $(COMPILE.c) -o $@ $^
+
+$(PURGATORY): CC=$(TARGET_CC)
+$(PURGATORY): CFLAGS=$(PURGATORY_EXTRA_CFLAGS) \
+ $($(ARCH)_PURGATORY_EXTRA_CFLAGS) \
+ -Os -fno-builtin -ffreestanding \
+ -fno-zero-initialized-in-bss \
+ -fno-PIC -fno-PIE -fno-stack-protector -fno-tree-vectorize
+
+$(PURGATORY): CPPFLAGS=$($(ARCH)_PURGATORY_EXTRA_CFLAGS) \
+ -I$(srcdir)/purgatory/include \
+ -I$(srcdir)/purgatory/arch/$(ARCH)/include \
+ -I$(srcdir)/util_lib/include \
+ -I$(srcdir)/include \
+ -Iinclude \
+ -I$(shell $(CC) -print-file-name=include)
+$(PURGATORY): LDFLAGS=$($(ARCH)_PURGATORY_EXTRA_CFLAGS)\
+ -Wl,--no-undefined -nostartfiles -nostdlib \
+ -nodefaultlibs -e purgatory_start -r \
+ -Wl,-Map=$(PURGATORY_MAP)
+
+$(PURGATORY): $(PURGATORY_OBJS)
+ $(MKDIR) -p $(@D)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@.sym $^
+# $(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) --no-undefined -e purgatory_start -r -o $@ $(PURGATORY_OBJS) $(UTIL_LIB)
+ $(STRIP) --strip-debug -o $@ $@.sym
+
+echo::
+ @echo "PURGATORY_SRCS $(PURGATORY_SRCS)"
+ @echo "PURGATORY_DEPS $(PURGATORY_DEPS)"
+ @echo "PURGATORY_OBJS $(PURGATORY_OBJS)"
diff --git a/purgatory/arch/alpha/Makefile b/purgatory/arch/alpha/Makefile
new file mode 100644
index 0000000..c58693f
--- /dev/null
+++ b/purgatory/arch/alpha/Makefile
@@ -0,0 +1,8 @@
+#
+# Purgatory alpha
+#
+
+alpha_PURGATORY_SRCS =
+
+dist += purgatory/arch/alpha/Makefile $(alpha_PURGATORY_SRCS)
+
diff --git a/purgatory/arch/arm/Makefile b/purgatory/arch/arm/Makefile
new file mode 100644
index 0000000..690377e
--- /dev/null
+++ b/purgatory/arch/arm/Makefile
@@ -0,0 +1,9 @@
+#
+# Purgatory arm
+#
+
+arm_PURGATORY_SRCS =
+
+dist += purgatory/arch/arm/Makefile $(arm_PURGATORY_SRCS)
+
+
diff --git a/purgatory/arch/arm64/Makefile b/purgatory/arch/arm64/Makefile
new file mode 100644
index 0000000..80068ca
--- /dev/null
+++ b/purgatory/arch/arm64/Makefile
@@ -0,0 +1,19 @@
+
+arm64_PURGATORY_EXTRA_CFLAGS = \
+ -mcmodel=large \
+ -fno-PIC \
+ -fno-stack-protector \
+ -fno-asynchronous-unwind-tables \
+ -Wundef \
+ -Werror-implicit-function-declaration \
+ -Wdeclaration-after-statement \
+ -Werror=implicit-int \
+ -Werror=strict-prototypes
+
+arm64_PURGATORY_SRCS += \
+ purgatory/arch/arm64/entry.S \
+ purgatory/arch/arm64/purgatory-arm64.c
+
+dist += \
+ $(arm64_PURGATORY_SRCS) \
+ purgatory/arch/arm64/Makefile
diff --git a/purgatory/arch/arm64/entry.S b/purgatory/arch/arm64/entry.S
new file mode 100644
index 0000000..adf16f4
--- /dev/null
+++ b/purgatory/arch/arm64/entry.S
@@ -0,0 +1,51 @@
+/*
+ * ARM64 purgatory.
+ */
+
+.macro size, sym:req
+ .size \sym, . - \sym
+.endm
+
+.text
+
+.globl purgatory_start
+purgatory_start:
+
+ adr x19, .Lstack
+ mov sp, x19
+
+ bl purgatory
+
+ /* Start new image. */
+ ldr x17, arm64_kernel_entry
+ ldr x0, arm64_dtb_addr
+ mov x1, xzr
+ mov x2, xzr
+ mov x3, xzr
+ br x17
+
+size purgatory_start
+
+.ltorg
+
+.align 4
+ .rept 256
+ .quad 0
+ .endr
+.Lstack:
+
+.data
+
+.align 3
+
+.globl arm64_kernel_entry
+arm64_kernel_entry:
+ .quad 0
+size arm64_kernel_entry
+
+.globl arm64_dtb_addr
+arm64_dtb_addr:
+ .quad 0
+size arm64_dtb_addr
+
+.end \ No newline at end of file
diff --git a/purgatory/arch/arm64/purgatory-arm64.c b/purgatory/arch/arm64/purgatory-arm64.c
new file mode 100644
index 0000000..b4d8578
--- /dev/null
+++ b/purgatory/arch/arm64/purgatory-arm64.c
@@ -0,0 +1,34 @@
+/*
+ * ARM64 purgatory.
+ */
+
+#include <stdint.h>
+#include <purgatory.h>
+
+/* Symbols set by kexec. */
+
+uint8_t *arm64_sink __attribute__ ((section ("data")));
+extern void (*arm64_kernel_entry)(uint64_t, uint64_t, uint64_t, uint64_t);
+extern uint64_t arm64_dtb_addr;
+
+void putchar(int ch)
+{
+ if (!arm64_sink)
+ return;
+
+ *arm64_sink = ch;
+
+ if (ch == '\n')
+ *arm64_sink = '\r';
+}
+
+void post_verification_setup_arch(void)
+{
+ printf("purgatory: booting kernel now\n");
+}
+
+void setup_arch(void)
+{
+ printf("purgatory: entry=%lx\n", (unsigned long)arm64_kernel_entry);
+ printf("purgatory: dtb=%lx\n", arm64_dtb_addr);
+}
diff --git a/purgatory/arch/i386/Makefile b/purgatory/arch/i386/Makefile
new file mode 100644
index 0000000..1532219
--- /dev/null
+++ b/purgatory/arch/i386/Makefile
@@ -0,0 +1,21 @@
+#
+# Purgatory i386
+#
+
+i386_PURGATORY_SRCS = purgatory/arch/i386/entry32-16.S
+i386_PURGATORY_SRCS += purgatory/arch/i386/entry32-16-debug.S
+i386_PURGATORY_SRCS += purgatory/arch/i386/entry32.S
+i386_PURGATORY_SRCS += purgatory/arch/i386/setup-x86.S
+i386_PURGATORY_SRCS += purgatory/arch/i386/stack.S
+i386_PURGATORY_SRCS += purgatory/arch/i386/compat_x86_64.S
+i386_PURGATORY_SRCS += purgatory/arch/i386/purgatory-x86.c
+i386_PURGATORY_SRCS += purgatory/arch/i386/console-x86.c
+i386_PURGATORY_SRCS += purgatory/arch/i386/vga.c
+i386_PURGATORY_SRCS += purgatory/arch/i386/pic.c
+i386_PURGATORY_SRCS += purgatory/arch/i386/crashdump_backup.c
+
+dist += purgatory/arch/i386/Makefile $(i386_PURGATORY_SRCS) \
+ purgatory/arch/i386/purgatory-x86.h \
+ purgatory/arch/i386/include/arch/io.h \
+ purgatory/arch/i386/include/arch/debug.h
+
diff --git a/purgatory/arch/i386/compat_x86_64.S b/purgatory/arch/i386/compat_x86_64.S
new file mode 100644
index 0000000..1649085
--- /dev/null
+++ b/purgatory/arch/i386/compat_x86_64.S
@@ -0,0 +1,100 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003,2004,2005 Eric Biederman (ebiederm@xmission.com)
+ *
+ * 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.
+ */
+
+ .equ MSR_K6_EFER, 0xC0000080
+ .equ EFER_LME, 0x00000100
+ .equ X86_CR4_PAE, 0x00000020
+ .equ CR0_PG, 0x80000000
+
+ .globl compat_x86_64, compat_x86_64_entry32
+ .text
+ .code64
+ .balign 16
+compat_x86_64:
+ /* Setup a temporary gdt */
+ /* This also acts as a serializing instruction ensuring
+ * my self modifying code works.
+ */
+ lgdt gdt(%rip)
+
+ /* Switch to 32bit compatiblity mode */
+ ljmp *lm_exit_addr(%rip)
+lm_exit:
+ .code32
+
+ /* Disable paging */
+ movl %cr0, %eax
+ andl $~CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Disable long mode */
+ movl $MSR_K6_EFER, %ecx
+ rdmsr
+ andl $~EFER_LME, %eax
+ wrmsr
+
+ /* Disable PAE */
+ xorl %eax, %eax
+ movl %eax, %cr4
+
+ /* load the data segments */
+ movl $0x18, %eax /* data segment */
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+ movl %eax, %fs
+ movl %eax, %gs
+
+ /* set all of the registers to known values */
+ /* leave %esp alone */
+
+ xorl %eax, %eax
+ xorl %ebx, %ebx
+ xorl %ecx, %ecx
+ xorl %edx, %edx
+ xorl %esi, %esi
+ xorl %edi, %edi
+ xorl %ebp, %ebp
+
+ jmp *compat_x86_64_entry32
+
+ .section ".rodata"
+ .balign 16
+gdt: /* 0x00 unusable segment
+ * 0x08 unused
+ * so use them as the gdt ptr
+ */
+ .word gdt_end - gdt - 1
+ # A quad word pointer to the gdt with the high 32bits 0
+ .long gdt, 0
+ .word 0, 0, 0
+
+ /* 0x10 4GB flat code segment */
+ .word 0xFFFF, 0x0000, 0x9A00, 0x00CF
+ /* 0x18 4GB flat data segment */
+ .word 0xFFFF, 0x0000, 0x9200, 0x00CF
+gdt_end:
+
+lm_exit_addr:
+ .long lm_exit
+ .long 0x10
+
+compat_x86_64_entry32:
+ .long 0
+.size compat_x86_64_entry32, . - compat_x86_64_entry32
diff --git a/purgatory/arch/i386/console-x86.c b/purgatory/arch/i386/console-x86.c
new file mode 100644
index 0000000..9773573
--- /dev/null
+++ b/purgatory/arch/i386/console-x86.c
@@ -0,0 +1,135 @@
+#include <stdint.h>
+#include <arch/io.h>
+#include <purgatory.h>
+
+/*
+ * VGA
+ * =============================================================================
+ */
+
+/* Simple VGA output */
+
+#define VGABASE ((void *)0xb8000)
+
+#define MAX_YPOS 25
+#define MAX_XPOS 80
+
+static int current_ypos = 1, current_xpos = 0;
+uint8_t console_vga = 0;
+
+static void putchar_vga(int ch)
+{
+ int i, k, j;
+ if (!console_vga)
+ return;
+
+ if (current_ypos >= MAX_YPOS) {
+ /* scroll 1 line up */
+ for (k = 1, j = 0; k < MAX_YPOS; k++, j++) {
+ for (i = 0; i < MAX_XPOS; i++) {
+ writew(readw(VGABASE + 2*(MAX_XPOS*k + i)),
+ VGABASE + 2*(MAX_XPOS*j + i));
+ }
+ }
+ for (i = 0; i < MAX_XPOS; i++)
+ writew(0x720, VGABASE + 2*(MAX_XPOS*j + i));
+ current_ypos = MAX_YPOS-1;
+ }
+ if (ch == '\n') {
+ current_xpos = 0;
+ current_ypos++;
+ } else if (ch != '\r') {
+ writew(((0x7 << 8) | (unsigned short) ch),
+ VGABASE + 2*(MAX_XPOS*current_ypos +
+ current_xpos++));
+ if (current_xpos >= MAX_XPOS) {
+ current_xpos = 0;
+ current_ypos++;
+ }
+ }
+}
+
+/*
+ * Serial
+ * =============================================================================
+ */
+
+/* Base Address */
+uint8_t console_serial = 0;
+uint16_t serial_base = 0x3f8; /* TTYS0 */
+uint32_t serial_baud = 0;
+
+#define XMTRDY 0x20
+
+#define DLAB 0x80
+
+#define TXR 0 /* Transmit register (WRITE) */
+#define TBR 0 /* Transmit register (WRITE) */
+#define RXR 0 /* Receive register (READ) */
+#define IER 1 /* Interrupt Enable */
+#define IIR 2 /* Interrupt ID */
+#define FCR 2 /* FIFO control */
+#define LCR 3 /* Line control */
+#define MCR 4 /* Modem control */
+#define LSR 5 /* Line Status */
+#define MSR 6 /* Modem Status */
+#define DLL 0 /* Divisor Latch Low */
+#define DLH 1 /* Divisor latch High */
+
+static void serial_init(void)
+{
+ static int initialized = 0;
+ if (!initialized) {
+ unsigned lcr;
+ outb(0x3, serial_base + LCR); /* 8n1 */
+ outb(0, serial_base + IER); /* no interrupt */
+ outb(0, serial_base + FCR); /* no fifo */
+ outb(0x3, serial_base + MCR); /* DTR + RTS */
+
+ lcr = inb(serial_base + LCR);
+ outb(lcr | DLAB, serial_base + LCR);
+ /* By default don't change the serial port baud rate */
+ if (serial_baud) {
+ unsigned divisor = 115200 / serial_baud;
+ outb(divisor & 0xff, serial_base + DLL);
+ outb((divisor >> 8) & 0xff, serial_base + DLH);
+ }
+ outb(lcr & ~DLAB, serial_base + LCR);
+ initialized = 1;
+ }
+}
+
+static void serial_tx_byte(unsigned byte)
+{
+ /* Ensure the serial port is initialized */
+ serial_init();
+
+ /* Wait until I can send a byte */
+ while((inb(serial_base + LSR) & 0x20) == 0)
+ ;
+ outb(byte, serial_base + TBR);
+ /* Wait until the byte is transmitted */
+ while(!(inb(serial_base + LSR) & 0x40))
+ ;
+}
+
+static void putchar_serial(int ch)
+{
+ if (!console_serial) {
+ return;
+ }
+ if (ch == '\n') {
+ serial_tx_byte('\r');
+ }
+ serial_tx_byte(ch);
+}
+
+/* Generic wrapper function */
+
+void putchar(int ch)
+{
+ putchar_vga(ch);
+ putchar_serial(ch);
+}
+
+
diff --git a/purgatory/arch/i386/crashdump_backup.c b/purgatory/arch/i386/crashdump_backup.c
new file mode 100644
index 0000000..365eb5d
--- /dev/null
+++ b/purgatory/arch/i386/crashdump_backup.c
@@ -0,0 +1,51 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Created by: Vivek goyal (vgoyal@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 <stdint.h>
+#include <string.h>
+
+/* Backup region start gets set after /proc/iomem has been parsed. */
+/* We reuse the same code for x86_64 also so changing backup_start to
+ unsigned long */
+unsigned long backup_start = 0;
+
+unsigned long backup_src_start = 0;
+unsigned long backup_src_size = 0;
+
+/* Backup first 640K of memory to backup region as reserved by kexec.
+ * Assuming first 640K has to be present on i386 machines and no address
+ * validity checks have to be performed. */
+
+void crashdump_backup_memory(void)
+{
+ void *dest, *src;
+ size_t size;
+
+ src = (void *) backup_src_start;
+ size = (size_t) backup_src_size;
+
+ if (!size)
+ return;
+
+ if (backup_start) {
+ dest = (void *)(backup_start);
+ memcpy(dest, src, size);
+ }
+}
diff --git a/purgatory/arch/i386/entry32-16-debug.S b/purgatory/arch/i386/entry32-16-debug.S
new file mode 100644
index 0000000..5167944
--- /dev/null
+++ b/purgatory/arch/i386/entry32-16-debug.S
@@ -0,0 +1,200 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com)
+ *
+ * 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 "arch/debug.h"
+
+#undef i386
+ .text
+ .globl entry16_debug, entry16_debug_regs
+ .globl entry16_debug_pre32
+ .globl entry16_debug_first32
+ .globl entry16_debug_old_first32
+ .arch i386
+ .balign 16
+entry16_debug:
+ .code32
+ /* Compute where I am running at (assumes esp valid) */
+ call 1f
+1: popl %ebx
+ subl $(1b - entry16_debug), %ebx
+
+ /* Fixup my real mode segment */
+ movl %ebx, %eax
+ shrl $4, %eax
+ movw %ax, (2 + realptr - entry16_debug)(%ebx)
+
+ /* Fixup the gdt */
+ leal (gdt - entry16_debug)(%ebx), %eax
+ movl %eax, (0x02 + gdt - entry16_debug)(%ebx)
+
+ movl %ebx, %eax
+ shll $16, %eax
+
+ movl %ebx, %ecx
+ shrl $16, %ecx
+ andl $0xff, %ecx
+
+ movl %ebx, %edx
+ andl $0xff000000, %edx
+ orl %edx, %ecx
+
+ orl %eax, (0x08 + gdt - entry16_debug)(%ebx)
+ orl %ecx, (0x0c + gdt - entry16_debug)(%ebx)
+ orl %eax, (0x10 + gdt - entry16_debug)(%ebx)
+ orl %ecx, (0x14 + gdt - entry16_debug)(%ebx)
+
+
+DEBUG_CHAR('a')
+ /* Setup the classic BIOS interrupt table at 0x0 */
+ lidt (idtptr - entry16_debug)(%ebx)
+
+DEBUG_CHAR('b')
+ /* Provide us with 16bit segments that we can use */
+ lgdt (gdt - entry16_debug)(%ebx)
+
+DEBUG_CHAR('c')
+ /* Note we don't disable the a20 line, (this shouldn't be required)
+ * The code to do it is in kexec_test and it is a real pain.
+ * I will worry about that when I need it.
+ */
+
+ /* Load 16bit data segments, to ensure the segment limits are set */
+ movl $0x10, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+ movl %eax, %fs
+ movl %eax, %gs
+
+DEBUG_CHAR('d')
+
+ /* switch to 16bit mode */
+ ljmp $0x08, $1f - entry16_debug
+1:
+ .code16
+DEBUG_CHAR('e')
+ /* Disable Paging and protected mode */
+ /* clear the PG & PE bits of CR0 */
+ movl %cr0,%eax
+ andl $~((1 << 31)|(1<<0)),%eax
+ movl %eax,%cr0
+
+DEBUG_CHAR('f')
+ /* make intersegment jmp to flush the processor pipeline
+ * and reload %cs:%eip (to clear upper 16 bits of %eip).
+ */
+ ljmp *(realptr - entry16_debug)
+3:
+DEBUG_CHAR('g')
+ /* we are in real mode now
+ * set up the real mode segment registers : %ds, $ss, %es
+ */
+ /* Setup the data segment */
+ movw %cs, %ax
+ movw %ax, %ds
+
+DEBUG_CHAR('h')
+ /* Load the registers */
+ movl eax - entry16_debug, %eax
+ movl ebx - entry16_debug, %ebx
+ movl ecx - entry16_debug, %ecx
+ movl edx - entry16_debug, %edx
+ movl esi - entry16_debug, %esi
+ movl edi - entry16_debug, %esi
+ movl esp - entry16_debug, %esp
+ movl ebp - entry16_debug, %ebp
+ movw es - entry16_debug, %es
+ movw ss - entry16_debug, %ss
+ movw fs - entry16_debug, %fs
+ movw gs - entry16_debug, %gs
+ movw ds - entry16_debug, %ds
+
+ /* Jump to the kernel entrypoint */
+ ljmp %cs:*(realdest - entry16_debug)
+
+ .balign 4
+entry16_debug_regs:
+eax: .long 0x00000000
+ebx: .long 0x00000000
+ecx: .long 0x00000000
+edx: .long 0x00000000
+esi: .long 0x00000000
+edi: .long 0x00000000
+esp: .long 0x00000000
+ebp: .long 0x00000000
+ds: .word 0x0000
+es: .word 0x0000
+ss: .word 0x0000
+fs: .word 0x0000
+gs: .word 0x0000
+realdest:
+ip: .word 0x0000
+cs: .word 0x0000
+pad: .word 0x0000
+ .size entry16_debug_regs, . - entry16_debug_regs
+
+ .balign 16
+realptr:
+ .word 3b - entry16_debug
+ .word 0x0000
+
+ .data
+ .balign 16
+
+idtptr:
+ /* 256 entry idt at 0 */
+ .word 0x400 - 1
+ .word 0, 0
+
+gdt:
+ /* 0x00 unusable segment so used as the gdt ptr */
+ .word gdt_end - gdt - 1
+ .long 0 /* gdt */
+ .word 0
+
+ /* 0x08 16 bit real mode code segment */
+ .word 0xffff, 0x0000
+ .byte 0x00, 0x9b, 0x00, 0x00
+
+ /* 0x10 16 bit real mode data segment */
+ .word 0xffff, 0x0000
+ .byte 0x00, 0x93, 0x00, 0x00
+gdt_end:
+ .size gdt, . - gdt
+
+ .text
+entry16_debug_pre32:
+ .code16
+DEBUG_CHAR('i')
+ cli # no interrupts allowed !
+ movb $0x80, %al # disable NMI for bootup
+ # sequence
+ outb %al, $0x70
+ lret
+.size entry16_debug_pre32, . - entry16_debug_pre32
+
+entry16_debug_first32:
+ .code32
+DEBUG_CHAR('j')
+ .byte 0xb8 /* movl $0x10000, %eax */
+entry16_debug_old_first32:
+ .long 0x100000
+ .size entry16_debug_old_first32, . - entry16_debug_old_first32
+ jmp *%eax
+.size entry16_debug_first32, . - entry16_debug_first32
diff --git a/purgatory/arch/i386/entry32-16.S b/purgatory/arch/i386/entry32-16.S
new file mode 100644
index 0000000..c051aab
--- /dev/null
+++ b/purgatory/arch/i386/entry32-16.S
@@ -0,0 +1,165 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com)
+ *
+ * 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.
+ */
+
+#undef i386
+ .text
+ .globl entry16, entry16_regs
+ .arch i386
+ .balign 16
+entry16:
+ .code32
+ /* Compute where I am running at (assumes esp valid) */
+ call 1f
+1: popl %ebx
+ subl $(1b - entry16), %ebx
+
+ /* Fixup my real mode segment */
+ movl %ebx, %eax
+ shrl $4, %eax
+ movw %ax, (2 + realptr - entry16)(%ebx)
+
+ /* Fixup the gdt */
+ leal (gdt - entry16)(%ebx), %eax
+ movl %eax, (0x02 + gdt - entry16)(%ebx)
+
+ movl %ebx, %eax
+ shll $16, %eax
+
+ movl %ebx, %ecx
+ shrl $16, %ecx
+ andl $0xff, %ecx
+
+ movl %ebx, %edx
+ andl $0xff000000, %edx
+ orl %edx, %ecx
+
+ orl %eax, (0x08 + gdt - entry16)(%ebx)
+ orl %ecx, (0x0c + gdt - entry16)(%ebx)
+ orl %eax, (0x10 + gdt - entry16)(%ebx)
+ orl %ecx, (0x14 + gdt - entry16)(%ebx)
+
+
+ /* Setup the classic BIOS interrupt table at 0x0 */
+ lidt (idtptr - entry16)(%ebx)
+
+ /* Provide us with 16bit segments that we can use */
+ lgdt (gdt - entry16)(%ebx)
+
+ /* Note we don't disable the a20 line, (this shouldn't be required)
+ * The code to do it is in kexec_test and it is a real pain.
+ * I will worry about that when I need it.
+ */
+
+ /* Load 16bit data segments, to ensure the segment limits are set */
+ movl $0x10, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+ movl %eax, %fs
+ movl %eax, %gs
+
+ /* switch to 16bit mode */
+ ljmp $0x08, $1f - entry16
+1:
+ .code16
+ /* Disable Paging and protected mode */
+ /* clear the PG & PE bits of CR0 */
+ movl %cr0,%eax
+ andl $~((1 << 31)|(1<<0)),%eax
+ movl %eax,%cr0
+
+ /* make intersegment jmp to flush the processor pipeline
+ * and reload %cs:%eip (to clear upper 16 bits of %eip).
+ */
+ ljmp *(realptr - entry16)
+3:
+ /* we are in real mode now
+ * set up the real mode segment registers : %ds, $ss, %es
+ */
+ /* Setup the data segment */
+ movw %cs, %ax
+ movw %ax, %ds
+
+ /* Load the registers */
+ movl eax - entry16, %eax
+ movl ebx - entry16, %ebx
+ movl ecx - entry16, %ecx
+ movl edx - entry16, %edx
+ movl esi - entry16, %esi
+ movl edi - entry16, %esi
+ movl esp - entry16, %esp
+ movl ebp - entry16, %ebp
+ movw es - entry16, %es
+ movw ss - entry16, %ss
+ movw fs - entry16, %fs
+ movw gs - entry16, %gs
+ movw ds - entry16, %ds
+
+ /* Jump to the kernel entrypoint */
+ ljmp %cs:*(realdest - entry16)
+
+ .balign 4
+entry16_regs:
+eax: .long 0x00000000
+ebx: .long 0x00000000
+ecx: .long 0x00000000
+edx: .long 0x00000000
+esi: .long 0x00000000
+edi: .long 0x00000000
+esp: .long 0x00000000
+ebp: .long 0x00000000
+ds: .word 0x0000
+es: .word 0x0000
+ss: .word 0x0000
+fs: .word 0x0000
+gs: .word 0x0000
+realdest:
+ip: .word 0x0000
+cs: .word 0x0000
+pad: .word 0x0000
+ .size entry16_regs, . - entry16_regs
+
+ .balign 16
+realptr:
+ .word 3b - entry16
+ .word 0x0000
+
+ .data
+ .balign 16
+
+idtptr:
+ /* 256 entry idt at 0 */
+ .word 0x400 - 1
+ .word 0, 0
+
+ .balign 16
+gdt:
+ /* 0x00 unusable segment so used as the gdt ptr */
+ .word gdt_end - gdt - 1
+ .long 0 /* gdt */
+ .word 0
+
+ /* 0x08 16 bit real mode code segment */
+ .word 0xffff, 0x0000
+ .byte 0x00, 0x9b, 0x00, 0x00
+
+ /* 0x10 16 bit real mode data segment */
+ .word 0xffff, 0x0000
+ .byte 0x00, 0x93, 0x00, 0x00
+gdt_end:
diff --git a/purgatory/arch/i386/entry32.S b/purgatory/arch/i386/entry32.S
new file mode 100644
index 0000000..f7a494f
--- /dev/null
+++ b/purgatory/arch/i386/entry32.S
@@ -0,0 +1,110 @@
+/*
+ * purgatory: setup code
+ *
+ * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com)
+ *
+ * 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.
+ */
+
+#undef i386
+
+ .text
+ .arch i386
+ .globl entry32, entry32_regs
+entry32:
+ .code32
+
+ /* Setup a gdt that should that is generally usefully */
+ lgdt %cs:gdt
+
+ /* load the data segments */
+ movl $0x18, %eax /* data segment */
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+ movl %eax, %fs
+ movl %eax, %gs
+
+ /* load the code segment */
+ ljmp $0x10,$1f
+1:
+
+ /* Load the registers */
+ movl eax, %eax
+ movl ecx, %ecx
+ movl edx, %edx
+ movl esi, %esi
+ movl edi, %edi
+ movl esp, %esp
+ movl ebp, %ebp
+ movl ebx, %ebx
+
+ /* Jump to the loaded image */
+ jmpl *(eip)
+
+ .section ".rodata"
+ .balign 16
+gdt:
+ /* 0x00 unusable segment so used as the gdt ptr */
+ .word gdt_end - gdt - 1
+ .long gdt
+ .word 0
+
+ /* 0x08 dummy */
+ .word 0x0000, 0x0000, 0x0000, 0x000
+
+ /* Documented linux kernel segments */
+ /* 0x10 4GB flat code segment */
+ .word 0xFFFF, 0x0000, 0x9A00, 0x00CF
+ /* 0x18 4GB flat data segment */
+ .word 0xFFFF, 0x0000, 0x9200, 0x00CF
+
+ /* 0x20 dummy */
+ .word 0x0000, 0x0000, 0x0000, 0x000
+ /* 0x28 dummy */
+ .word 0x0000, 0x0000, 0x0000, 0x000
+ /* 0x30 dummy */
+ .word 0x0000, 0x0000, 0x0000, 0x000
+ /* 0x38 dummy */
+ .word 0x0000, 0x0000, 0x0000, 0x000
+ /* 0x40 dummy */
+ .word 0x0000, 0x0000, 0x0000, 0x000
+ /* 0x48 dummy */
+ .word 0x0000, 0x0000, 0x0000, 0x000
+ /* 0x50 dummy */
+ .word 0x0000, 0x0000, 0x0000, 0x000
+ /* 0x58 dummy */
+ .word 0x0000, 0x0000, 0x0000, 0x000
+
+ /* Segments used by the 2.5.x kernel */
+ /* 0x60 4GB flat code segment */
+ .word 0xFFFF, 0x0000, 0x9A00, 0x00CF
+ /* 0x68 4GB flat data segment */
+ .word 0xFFFF, 0x0000, 0x9200, 0x00CF
+gdt_end:
+
+ .data
+ .balign 4
+entry32_regs:
+eax: .long 0x00000000
+ebx: .long 0x00000000
+ecx: .long 0x00000000
+edx: .long 0x00000000
+esi: .long 0x00000000
+edi: .long 0x00000000
+esp: .long 0x00000000
+ebp: .long 0x00000000
+eip: .long entry16
+ .size entry32_regs, . - entry32_regs
+
diff --git a/purgatory/arch/i386/include/arch/debug.h b/purgatory/arch/i386/include/arch/debug.h
new file mode 100644
index 0000000..25853cd
--- /dev/null
+++ b/purgatory/arch/i386/include/arch/debug.h
@@ -0,0 +1,316 @@
+/* Base Address */
+#define TTYS0_BASE 0x3f8
+/* Data */
+#define TTYS0_RBR (TTYS0_BASE+0x00)
+#define TTYS0_TBR (TTYS0_BASE+0x00)
+/* Control */
+#define TTYS0_IER (TTYS0_BASE+0x01)
+#define TTYS0_IIR (TTYS0_BASE+0x02)
+#define TTYS0_FCR (TTYS0_BASE+0x02)
+#define TTYS0_LCR (TTYS0_BASE+0x03)
+#define TTYS0_MCR (TTYS0_BASE+0x04)
+
+#define TTYS0_DLL (TTYS0_BASE+0x00)
+#define TTYS0_DLM (TTYS0_BASE+0x01)
+/* Status */
+#define TTYS0_LSR (TTYS0_BASE+0x05)
+#define TTYS0_MSR (TTYS0_BASE+0x06)
+#define TTYS0_SCR (TTYS0_BASE+0x07)
+
+#define TTYS0_BAUD 9600
+#define TTYS0_DIV (115200/TTYS0_BAUD)
+#define TTYS0_DIV_LO (TTYS0_DIV&0xFF)
+#define TTYS0_DIV_HI ((TTYS0_DIV >> 8)&0xFF)
+
+#if ((115200%TTYS0_BAUD) != 0)
+#error Bad ttyS0 baud rate
+#endif
+
+#define TTYS0_INIT \
+ /* disable interrupts */ \
+ movb $0x00, %al ; \
+ movw $TTYS0_IER, %dx ; \
+ outb %al, %dx ; \
+ ; \
+ /* enable fifos */ \
+ movb $0x01, %al ; \
+ movw $TTYS0_FCR, %dx ; \
+ outb %al, %dx ; \
+ ; \
+ /* Set Baud Rate Divisor to TTYS0_BAUD */ \
+ movw $TTYS0_LCR, %dx ; \
+ movb $0x83, %al ; \
+ outb %al, %dx ; \
+ ; \
+ movw $TTYS0_DLL, %dx ; \
+ movb $TTYS0_DIV_LO, %al ; \
+ outb %al, %dx ; \
+ ; \
+ movw $TTYS0_DLM, %dx ; \
+ movb $TTYS0_DIV_HI, %al ; \
+ outb %al, %dx ; \
+ ; \
+ movw $TTYS0_LCR, %dx ; \
+ movb $0x03, %al ; \
+ outb %al, %dx
+
+
+ /* uses: ax, dx */
+#define TTYS0_TX_AL \
+ mov %al, %ah ; \
+9: mov $TTYS0_LSR, %dx ; \
+ inb %dx, %al ; \
+ test $0x20, %al ; \
+ je 9b ; \
+ mov $TTYS0_TBR, %dx ; \
+ mov %ah, %al ; \
+ outb %al, %dx
+
+ /* uses: ax, dx */
+#define TTYS0_TX_CHAR(byte) \
+ mov byte, %al ; \
+ TTYS0_TX_AL
+
+ /* uses: eax, dx */
+#define TTYS0_TX_HEX32(lword) \
+ mov lword, %eax ; \
+ shr $28, %eax ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %eax ; \
+ shr $24, %eax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %eax ; \
+ shr $20, %eax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %eax ; \
+ shr $16, %eax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %eax ; \
+ shr $12, %eax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %eax ; \
+ shr $8, %eax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %eax ; \
+ shr $4, %eax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %eax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL
+
+ /* uses: rax, dx */
+#define TTYS0_TX_HEX64(lword) \
+ mov lword, %rax ; \
+ shr $60, %rax ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $56, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $52, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $48, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $44, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $40, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $36, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $32, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $28, %rax ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $24, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $20, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $16, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $12, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $8, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $4, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL
+
+
+#define DEBUG_CHAR(x) TTYS0_TX_CHAR($x) ; TTYS0_TX_CHAR($'\r') ; TTYS0_TX_CHAR($'\n')
+#define DEBUG_TX_HEX32(x) TTYS0_TX_HEX32(x); TTYS0_TX_CHAR($'\r') ; TTYS0_TX_CHAR($'\n')
+#define DEBUG_TX_HEX64(x) TTYS0_TX_HEX64(x); TTYS0_TX_CHAR($'\r') ; TTYS0_TX_CHAR($'\n')
diff --git a/purgatory/arch/i386/include/arch/io.h b/purgatory/arch/i386/include/arch/io.h
new file mode 100644
index 0000000..0a9430a
--- /dev/null
+++ b/purgatory/arch/i386/include/arch/io.h
@@ -0,0 +1,110 @@
+#ifndef ARCH_I386_IO_H
+#define ARCH_I386_IO_H
+
+#include <stdint.h>
+/* Helper functions for directly doing I/O */
+
+static inline __attribute__((__always_inline__))
+uint8_t inb(uint16_t port)
+{
+ uint8_t result;
+
+ __asm__ __volatile__ (
+ "inb %w1,%0"
+ :"=a" (result)
+ :"Nd" (port));
+ return result;
+}
+
+static inline __attribute__((__always_inline__))
+uint16_t inw(uint16_t port)
+{
+ uint16_t result;
+
+ __asm__ __volatile__ (
+ "inw %w1,%0"
+ :"=a" (result)
+ :"Nd" (port));
+ return result;
+}
+
+static inline __attribute__((__always_inline__))
+uint32_t inl(uint32_t port)
+{
+ uint32_t result;
+
+ __asm__ __volatile__ (
+ "inl %w1,%0"
+ :"=a" (result)
+ :"Nd" (port));
+ return result;
+}
+
+static inline __attribute__((__always_inline__))
+void outb (uint8_t value, uint16_t port)
+{
+ __asm__ __volatile__ (
+ "outb %b0,%w1"
+ :
+ :"a" (value), "Nd" (port));
+}
+
+static inline __attribute__((__always_inline__))
+void outw (uint16_t value, uint16_t port)
+{
+ __asm__ __volatile__ (
+ "outw %w0,%w1"
+ :
+ :"a" (value), "Nd" (port));
+}
+
+static inline __attribute__((__always_inline__))
+void outl (uint32_t value, uint16_t port)
+{
+ __asm__ __volatile__ (
+ "outl %0,%w1"
+ :
+ :"a" (value), "Nd" (port));
+}
+
+
+/*
+ * readX/writeX() are used to access memory mapped devices. On some
+ * architectures the memory mapped IO stuff needs to be accessed
+ * differently. On the x86 architecture, we just read/write the
+ * memory location directly.
+ */
+
+static inline __attribute__((__always_inline__))
+unsigned char readb(const volatile void *addr)
+{
+ return *(volatile unsigned char *) addr;
+}
+static inline __attribute__((__always_inline__))
+unsigned short readw(const volatile void *addr)
+{
+ return *(volatile unsigned short *) addr;
+}
+static inline __attribute__((__always_inline__))
+unsigned int readl(const volatile void *addr)
+{
+ return *(volatile unsigned int *) addr;
+}
+
+static inline __attribute__((__always_inline__))
+void writeb(unsigned char b, volatile void *addr)
+{
+ *(volatile unsigned char *) addr = b;
+}
+static inline __attribute__((__always_inline__))
+void writew(unsigned short b, volatile void *addr)
+{
+ *(volatile unsigned short *) addr = b;
+}
+static inline __attribute__((__always_inline__))
+void writel(unsigned int b, volatile void *addr)
+{
+ *(volatile unsigned int *) addr = b;
+}
+
+#endif /* ARCH_I386_IO_H */
diff --git a/purgatory/arch/i386/pic.c b/purgatory/arch/i386/pic.c
new file mode 100644
index 0000000..c23c459
--- /dev/null
+++ b/purgatory/arch/i386/pic.c
@@ -0,0 +1,51 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com)
+ *
+ * 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/io.h>
+#include <purgatory.h>
+#include "purgatory-x86.h"
+
+
+void x86_setup_legacy_pic(void)
+{
+ /* Load the legacy dos settings into the 8259A pic */
+ outb(0xff, 0x21); /* mask all of 8259A-1 */
+ outb(0xff, 0xa1); /* mask all of 8259A-2 */
+
+ outb(0x11, 0x20); /* ICW1: select 8259A-1 init */
+ outb(0x11, 0x80); /* A short delay */
+
+ outb(0x08, 0x21); /* ICW2: 8259A-1 IR0-7 mappend to 0x8-0xf */
+ outb(0x08, 0x80); /* A short delay */
+
+ outb(0x01, 0x21); /* Normal 8086 auto EOI mode */
+ outb(0x01, 0x80); /* A short delay */
+
+ outb(0x11, 0xa0); /* ICW1: select 8259A-2 init */
+ outb(0x11, 0x80); /* A short delay */
+
+ outb(0x70, 0xa1); /* ICW2: 8259A-2 IR0-7 mappend to 0x70-0x77 */
+ outb(0x70, 0x80); /* A short delay */
+
+ outb(0x01, 0xa1); /* Normal 8086 auto EOI mode */
+ outb(0x01, 0x80); /* A short delay */
+
+ outb(0x00, 0x21); /* Unmask all of 8259A-1 */
+ outb(0x00, 0xa1); /* Unmask all of 8259A-2 */
+}
+
diff --git a/purgatory/arch/i386/purgatory-x86.c b/purgatory/arch/i386/purgatory-x86.c
new file mode 100644
index 0000000..075e518
--- /dev/null
+++ b/purgatory/arch/i386/purgatory-x86.c
@@ -0,0 +1,57 @@
+#include <stdint.h>
+#include <purgatory.h>
+#include "purgatory-x86.h"
+
+/*
+ * CPU
+ * =============================================================================
+ */
+
+void x86_setup_cpu(void)
+{
+#if 0
+ /* This code is only needed for old versions of the kexec kernel patch.
+ * While it is still a good idea doing this unconditionally breaks
+ * on older cpus that did not implemented cr4.
+ * So this code is disabled for now. If this is revisited
+ * I first need to detect cpuid support and then use cpuid
+ * to conditionally change newer cpu registers.
+ */
+ /* clear special bits in %cr4 */
+ asm volatile(
+ "movl %0, %%eax\n\t"
+ "movl %%eax, %%cr4\n\t"
+ : /* outputs */
+ : "r" (0)
+ );
+#endif
+}
+
+uint8_t reset_vga = 0;
+uint8_t legacy_timer = 0;
+uint8_t legacy_pic = 0;
+uint8_t panic_kernel = 0;
+unsigned long jump_back_entry = 0;
+char *cmdline_end = 0;
+
+void setup_arch(void)
+{
+ x86_setup_cpu();
+ if (reset_vga) x86_reset_vga();
+ if (legacy_pic) x86_setup_legacy_pic();
+ /* if (legacy_timer) x86_setup_legacy_timer(); */
+}
+
+static void x86_setup_jump_back_entry(void)
+{
+ if (cmdline_end)
+ sprintf(cmdline_end, " kexec_jump_back_entry=0x%x",
+ jump_back_entry);
+}
+
+/* This function can be used to execute after the SHA256 verification. */
+void post_verification_setup_arch(void)
+{
+ if (panic_kernel) crashdump_backup_memory();
+ if (jump_back_entry) x86_setup_jump_back_entry();
+}
diff --git a/purgatory/arch/i386/purgatory-x86.h b/purgatory/arch/i386/purgatory-x86.h
new file mode 100644
index 0000000..02039c9
--- /dev/null
+++ b/purgatory/arch/i386/purgatory-x86.h
@@ -0,0 +1,9 @@
+#ifndef PURGATORY_X86_H
+#define PURGATORY_X86_H
+
+void x86_reset_vga(void);
+void x86_setup_legacy_pic(void);
+void x86_setup_legacy_timer(void);
+void crashdump_backup_memory(void);
+
+#endif /* PURGATORY_X86_H */
diff --git a/purgatory/arch/i386/setup-x86.S b/purgatory/arch/i386/setup-x86.S
new file mode 100644
index 0000000..201bb2c
--- /dev/null
+++ b/purgatory/arch/i386/setup-x86.S
@@ -0,0 +1,75 @@
+/*
+ * purgatory: setup code
+ *
+ * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com)
+ *
+ * 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.
+ */
+
+
+#undef i386
+
+ .text
+ .arch i386
+ .globl purgatory_start
+purgatory_start:
+ .code32
+
+ /* Load a gdt so I know what the segment registers are */
+ lgdt %cs:gdt
+
+ /* load the data segments */
+ movl $0x08, %eax /* data segment */
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+ movl %eax, %fs
+ movl %eax, %gs
+
+ /* load the code segment */
+ ljmp $0x10,$1f
+1:
+
+ movl 0(%esp), %eax
+ movl %eax, jump_back_entry
+
+ /* Setup a stack */
+ movl $lstack_end, %esp
+
+ /* Call the C code */
+ call purgatory
+ jmp entry32
+
+ .section ".rodata"
+ .balign 16
+gdt:
+ /* 0x00 unusable segment so used as the gdt ptr */
+ .word gdt_end - gdt - 1
+ .long gdt
+ .word 0
+
+ /* 0x8 4GB flat data segment */
+ .word 0xFFFF, 0x0000, 0x9200, 0x00CF
+
+ /* 0x10 4GB flat code segment */
+ .word 0xFFFF, 0x0000, 0x9A00, 0x00CF
+gdt_end:
+
+ /* A stack for the purgatory code */
+ .bss
+ .balign 4096
+lstack:
+ .skip 4096
+lstack_end:
+
diff --git a/purgatory/arch/i386/stack.S b/purgatory/arch/i386/stack.S
new file mode 100644
index 0000000..a597b0f
--- /dev/null
+++ b/purgatory/arch/i386/stack.S
@@ -0,0 +1,39 @@
+/*
+ * purgatory: stack
+ *
+ * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com)
+ *
+ * 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.
+ */
+
+ /* A stack for the loaded kernel.
+ * Seperate and in the data section so it can be prepopulated.
+ */
+ .data
+ .globl stack, stack_end
+ .globl stack_arg32_1, stack_arg32_2, stack_arg32_3 ,stack_arg32_4
+ .globl stack_arg32_5, stack_arg32_6, stack_arg32_7 ,stack_arg32_8
+ .balign 4096
+stack:
+ .skip 4096 - (8*4)
+stack_arg32_8: .long 0 ; .size stack_arg32_8, 4
+stack_arg32_7: .long 0 ; .size stack_arg32_7, 4
+stack_arg32_6: .long 0 ; .size stack_arg32_6, 4
+stack_arg32_5: .long 0 ; .size stack_arg32_5, 4
+stack_arg32_4: .long 0 ; .size stack_arg32_4, 4
+stack_arg32_3: .long 0 ; .size stack_arg32_3, 4
+stack_arg32_2: .long 0 ; .size stack_arg32_2, 4
+stack_arg32_1: .long 0 ; .size stack_arg32_1, 4
+stack_end:
+
diff --git a/purgatory/arch/i386/vga.c b/purgatory/arch/i386/vga.c
new file mode 100644
index 0000000..e65976c
--- /dev/null
+++ b/purgatory/arch/i386/vga.c
@@ -0,0 +1,152 @@
+#include <sys/io.h>
+#include <purgatory.h>
+#include "purgatory-x86.h"
+
+/* Crudely reset a VGA card to text mode 3, by writing plausible default */
+/* values into its registers. */
+/* Tim Deegan (tjd21 at cl.cam.ac.uk), March 2003 */
+/* Based on Keir Fraser's start-of-day reset code from the Xen hypervisor */
+/* Converted from Assembly 20 December 2004 -- Eric Biederman */
+
+void x86_reset_vga(void)
+{
+ /* Hello */
+ inb(0x3da);
+ outb(0, 0x3c0);
+
+ /* Sequencer registers */
+ outw(0x0300, 0x3c4);
+ outw(0x0001, 0x3c4);
+ outw(0x0302, 0x3c4);
+ outw(0x0003, 0x3c4);
+ outw(0x0204, 0x3c4);
+
+ /* Ensure CRTC regs 0-7 are unlocked by clearing bit 7 of CRTC[17] */
+ outw(0x0e11, 0x3d4);
+ /* CRTC registers */
+ outw(0x5f00, 0x3d4);
+ outw(0x4f01, 0x3d4);
+ outw(0x5002, 0x3d4);
+ outw(0x8203, 0x3d4);
+ outw(0x5504, 0x3d4);
+ outw(0x8105, 0x3d4);
+ outw(0xbf06, 0x3d4);
+ outw(0x1f07, 0x3d4);
+ outw(0x0008, 0x3d4);
+ outw(0x4f09, 0x3d4);
+ outw(0x200a, 0x3d4);
+ outw(0x0e0b, 0x3d4);
+ outw(0x000c, 0x3d4);
+ outw(0x000d, 0x3d4);
+ outw(0x010e, 0x3d4);
+ outw(0xe00f, 0x3d4);
+ outw(0x9c10, 0x3d4);
+ outw(0x8e11, 0x3d4);
+ outw(0x8f12, 0x3d4);
+ outw(0x2813, 0x3d4);
+ outw(0x1f14, 0x3d4);
+ outw(0x9615, 0x3d4);
+ outw(0xb916, 0x3d4);
+ outw(0xa317, 0x3d4);
+ outw(0xff18, 0x3d4);
+
+ /* Graphic registers */
+ outw(0x0000, 0x3ce);
+ outw(0x0001, 0x3ce);
+ outw(0x0002, 0x3ce);
+ outw(0x0003, 0x3ce);
+ outw(0x0004, 0x3ce);
+ outw(0x1005, 0x3ce);
+ outw(0x0e06, 0x3ce);
+ outw(0x0007, 0x3ce);
+ outw(0xff08, 0x3ce);
+
+ /* Attribute registers */
+ inb(0x3da);
+ outb(0x00, 0x3c0);
+ outb(0x00, 0x3c0);
+
+ inb(0x3da);
+ outb(0x01, 0x3c0);
+ outb(0x01, 0x3c0);
+
+ inb(0x3da);
+ outb(0x02, 0x3c0);
+ outb(0x02, 0x3c0);
+
+ inb(0x3da);
+ outb(0x03, 0x3c0);
+ outb(0x03, 0x3c0);
+
+ inb(0x3da);
+ outb(0x04, 0x3c0);
+ outb(0x04, 0x3c0);
+
+ inb(0x3da);
+ outb(0x05, 0x3c0);
+ outb(0x05, 0x3c0);
+
+ inb(0x3da);
+ outb(0x06, 0x3c0);
+ outb(0x14, 0x3c0);
+
+ inb(0x3da);
+ outb(0x07, 0x3c0);
+ outb(0x07, 0x3c0);
+
+ inb(0x3da);
+ outb(0x08, 0x3c0);
+ outb(0x38, 0x3c0);
+
+ inb(0x3da);
+ outb(0x09, 0x3c0);
+ outb(0x39, 0x3c0);
+
+ inb(0x3da);
+ outb(0x0a, 0x3c0);
+ outb(0x3a, 0x3c0);
+
+ inb(0x3da);
+ outb(0x0b, 0x3c0);
+ outb(0x3b, 0x3c0);
+
+ inb(0x3da);
+ outb(0x0c, 0x3c0);
+ outb(0x3c, 0x3c0);
+
+ inb(0x3da);
+ outb(0x0d, 0x3c0);
+ outb(0x3d, 0x3c0);
+
+ inb(0x3da);
+ outb(0x0e, 0x3c0);
+ outb(0x3e, 0x3c0);
+
+ inb(0x3da);
+ outb(0x0f, 0x3c0);
+ outb(0x3f, 0x3c0);
+
+ inb(0x3da);
+ outb(0x10, 0x3c0);
+ outb(0x0c, 0x3c0);
+
+ inb(0x3da);
+ outb(0x11, 0x3c0);
+ outb(0x00, 0x3c0);
+
+ inb(0x3da);
+ outb(0x12, 0x3c0);
+ outb(0x0f, 0x3c0);
+
+ inb(0x3da);
+ outb(0x13, 0x3c0);
+ outb(0x08, 0x3c0);
+
+ inb(0x3da);
+ outb(0x14, 0x3c0);
+ outb(0x00, 0x3c0);
+
+ /* Goodbye */
+ inb(0x3da);
+ outb(0x20, 0x3c0);
+}
diff --git a/purgatory/arch/ia64/Makefile b/purgatory/arch/ia64/Makefile
new file mode 100644
index 0000000..4a2564c
--- /dev/null
+++ b/purgatory/arch/ia64/Makefile
@@ -0,0 +1,17 @@
+#
+# Purgatory ia64
+#
+ia64_PURGATORY_SRCS += purgatory/arch/ia64/entry.S
+ia64_PURGATORY_SRCS += purgatory/arch/ia64/purgatory-ia64.c
+ia64_PURGATORY_SRCS += purgatory/arch/ia64/console-ia64.c
+ia64_PURGATORY_SRCS += purgatory/arch/ia64/vga.c
+
+ia64_PURGATORY_EXTRA_CFLAGS = -ffixed-r28
+
+# sha256.c needs to be compiled without optimization, else
+# purgatory fails to execute on ia64.
+ia64_PURGATORY_SHA256_CFLAGS = -O0
+
+dist += purgatory/arch/ia64/Makefile $(ia64_PURGATORY_SRCS) \
+ purgatory/arch/ia64/io.h purgatory/arch/ia64/purgatory-ia64.h
+
diff --git a/purgatory/arch/ia64/console-ia64.c b/purgatory/arch/ia64/console-ia64.c
new file mode 100644
index 0000000..99d97ca
--- /dev/null
+++ b/purgatory/arch/ia64/console-ia64.c
@@ -0,0 +1,47 @@
+#include <purgatory.h>
+#include "io.h"
+
+#define VGABASE UNCACHED(0xb8000)
+
+/* code based on i386 console code
+ * TODO add serial support
+ */
+#define MAX_YPOS 25
+#define MAX_XPOS 80
+
+unsigned long current_ypos = 1, current_xpos = 0;
+
+static void putchar_vga(int ch)
+{
+ int i, k, j;
+
+ if (current_ypos >= MAX_YPOS) {
+ /* scroll 1 line up */
+ for (k = 1, j = 0; k < MAX_YPOS; k++, j++) {
+ for (i = 0; i < MAX_XPOS; i++) {
+ writew(readw(VGABASE + 2*(MAX_XPOS*k + i)),
+ VGABASE + 2*(MAX_XPOS*j + i));
+ }
+ }
+ for (i = 0; i < MAX_XPOS; i++)
+ writew(0x720, VGABASE + 2*(MAX_XPOS*j + i));
+ current_ypos = MAX_YPOS-1;
+ }
+ if (ch == '\n') {
+ current_xpos = 0;
+ current_ypos++;
+ } else if (ch != '\r') {
+ writew(((0x7 << 8) | (unsigned short) ch),
+ VGABASE + 2*(MAX_XPOS*current_ypos +
+ current_xpos++));
+ if (current_xpos >= MAX_XPOS) {
+ current_xpos = 0;
+ current_ypos++;
+ }
+ }
+}
+
+void putchar(int ch)
+{
+ putchar_vga(ch);
+}
diff --git a/purgatory/arch/ia64/entry.S b/purgatory/arch/ia64/entry.S
new file mode 100644
index 0000000..f05434f
--- /dev/null
+++ b/purgatory/arch/ia64/entry.S
@@ -0,0 +1,73 @@
+/*
+ * purgatory: setup code
+ *
+ * Copyright (C) 2005-2006 Zou Nan hai (nanhai.zou@intel.com)
+ *
+ * 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.
+ */
+#define DECLARE_DATA8(name) \
+.global name; \
+.size name, 8; \
+name: data8 0x0
+
+.global __dummy_efi_function
+.align 32
+.proc __dummy_efi_function
+__dummy_efi_function:
+ mov r8=r0;;
+ br.ret.sptk.many rp;;
+.global __dummy_efi_function_end
+__dummy_efi_function_end:
+.endp __dummy_efi_function
+
+.global purgatory_start
+.align 32
+.proc purgatory_start
+purgatory_start:
+ movl r2=__gp_value;;
+ ld8 gp=[r2];;
+ br.call.sptk.many b0=purgatory
+ ;;
+ alloc r2 = ar.pfs, 0, 0, 2, 0
+ ;;
+ mov out0=r28
+ movl out1=__vmcode_base;
+ br.call.sptk.many b0=ia64_env_setup
+ movl r10=__kernel_entry;;
+ ld8 r14=[r10];;
+ movl r10=__boot_param_base;;
+ ld8 r28=[r10];;
+ mov b6=r14;;
+ mov ar.lc=r0
+ mov ar.ec=r0
+ cover;;
+ invala;;
+ br.call.sptk.many b0=b6
+.endp purgatory_start
+
+DECLARE_DATA8(__kernel_entry)
+DECLARE_DATA8(__vmcode_base)
+DECLARE_DATA8(__vmcode_size)
+DECLARE_DATA8(__ramdisk_base)
+DECLARE_DATA8(__ramdisk_size)
+DECLARE_DATA8(__command_line)
+DECLARE_DATA8(__command_line_len)
+DECLARE_DATA8(__efi_memmap_base)
+DECLARE_DATA8(__efi_memmap_size)
+DECLARE_DATA8(__boot_param_base)
+DECLARE_DATA8(__loaded_segments)
+DECLARE_DATA8(__loaded_segments_num)
+
+DECLARE_DATA8(__gp_value)
+DECLARE_DATA8(__noio)
diff --git a/purgatory/arch/ia64/io.h b/purgatory/arch/ia64/io.h
new file mode 100644
index 0000000..0f78580
--- /dev/null
+++ b/purgatory/arch/ia64/io.h
@@ -0,0 +1,108 @@
+#ifndef IO_H
+#define IO_H
+#define UNCACHED(x) (void *)((x)|(1UL<<63))
+#define MF() asm volatile ("mf.a" ::: "memory")
+#define IO_SPACE_ENCODING(p) ((((p) >> 2) << 12) | (p & 0xfff))
+extern long __noio;
+static inline void *io_addr (unsigned long port)
+{
+ unsigned long offset;
+ unsigned long io_base;
+ asm volatile ("mov %0=ar.k0":"=r"(io_base));
+ offset = IO_SPACE_ENCODING(port);
+ return UNCACHED(io_base | offset);
+}
+
+static inline unsigned int inb (unsigned long port)
+{
+ volatile unsigned char *addr = io_addr(port);
+ unsigned char ret = 0;
+ if (!__noio) {
+ ret = *addr;
+ MF();
+ }
+ return ret;
+}
+
+static inline unsigned int inw (unsigned long port)
+{
+ volatile unsigned short *addr = io_addr(port);
+ unsigned short ret = 0;
+
+ if (!__noio) {
+ ret = *addr;
+ MF();
+ }
+ return ret;
+}
+
+static inline unsigned int inl (unsigned long port)
+{
+ volatile unsigned int *addr = io_addr(port);
+ unsigned int ret ;
+ if (!__noio) {
+ ret = *addr;
+ MF();
+ }
+ return ret;
+}
+
+static inline void outb (unsigned char val, unsigned long port)
+{
+ volatile unsigned char *addr = io_addr(port);
+
+ if (!__noio) {
+ *addr = val;
+ MF();
+ }
+}
+
+static inline void outw (unsigned short val, unsigned long port)
+{
+ volatile unsigned short *addr = io_addr(port);
+
+ if (!__noio) {
+ *addr = val;
+ MF();
+ }
+}
+
+static inline void outl (unsigned int val, unsigned long port)
+{
+ volatile unsigned int *addr = io_addr(port);
+
+ if (!__noio) {
+ *addr = val;
+ MF();
+ }
+}
+
+static inline unsigned char readb(const volatile void *addr)
+{
+ return __noio ? 0 :*(volatile unsigned char *) addr;
+}
+static inline unsigned short readw(const volatile void *addr)
+{
+ return __noio ? 0 :*(volatile unsigned short *) addr;
+}
+static inline unsigned int readl(const volatile void *addr)
+{
+ return __noio ? 0 :*(volatile unsigned int *) addr;
+}
+
+static inline void writeb(unsigned char b, volatile void *addr)
+{
+ if (!__noio)
+ *(volatile unsigned char *) addr = b;
+}
+static inline void writew(unsigned short b, volatile void *addr)
+{
+ if (!__noio)
+ *(volatile unsigned short *) addr = b;
+}
+static inline void writel(unsigned int b, volatile void *addr)
+{
+ if (!__noio)
+ *(volatile unsigned int *) addr = b;
+}
+#endif
diff --git a/purgatory/arch/ia64/purgatory-ia64.c b/purgatory/arch/ia64/purgatory-ia64.c
new file mode 100644
index 0000000..e138e9c
--- /dev/null
+++ b/purgatory/arch/ia64/purgatory-ia64.c
@@ -0,0 +1,307 @@
+/*
+ * purgatory: setup code
+ *
+ * Copyright (C) 2005-2006 Zou Nan hai (nanhai.zou@intel.com)
+ *
+ * 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 <stdint.h>
+#include <string.h>
+#include "purgatory-ia64.h"
+
+#define PAGE_OFFSET 0xe000000000000000UL
+
+#define EFI_PAGE_SHIFT 12
+#define EFI_PAGE_SIZE (1UL<<EFI_PAGE_SHIFT)
+#define EFI_PAGE_ALIGN(x) ((x + EFI_PAGE_SIZE - 1)&~(EFI_PAGE_SIZE-1))
+/* Memory types: */
+#define EFI_RESERVED_TYPE 0
+#define EFI_LOADER_CODE 1
+#define EFI_LOADER_DATA 2
+#define EFI_BOOT_SERVICES_CODE 3
+#define EFI_BOOT_SERVICES_DATA 4
+#define EFI_RUNTIME_SERVICES_CODE 5
+#define EFI_RUNTIME_SERVICES_DATA 6
+#define EFI_CONVENTIONAL_MEMORY 7
+#define EFI_UNUSABLE_MEMORY 8
+#define EFI_ACPI_RECLAIM_MEMORY 9
+#define EFI_ACPI_MEMORY_NVS 10
+#define EFI_MEMORY_MAPPED_IO 11
+#define EFI_MEMORY_MAPPED_IO_PORT_SPACE 12
+#define EFI_PAL_CODE 13
+#define EFI_MAX_MEMORY_TYPE 14
+
+typedef struct {
+ uint64_t signature;
+ uint32_t revision;
+ uint32_t headersize;
+ uint32_t crc32;
+ uint32_t reserved;
+} efi_table_hdr_t;
+
+typedef struct {
+ efi_table_hdr_t hdr;
+ unsigned long get_time;
+ unsigned long set_time;
+ unsigned long get_wakeup_time;
+ unsigned long set_wakeup_time;
+ unsigned long set_virtual_address_map;
+ unsigned long convert_pointer;
+ unsigned long get_variable;
+ unsigned long get_next_variable;
+ unsigned long set_variable;
+ unsigned long get_next_high_mono_count;
+ unsigned long reset_system;
+} efi_runtime_services_t;
+
+typedef struct {
+ efi_table_hdr_t hdr;
+ unsigned long fw_vendor; /* physical addr of
+ CHAR16 vendor string */
+ uint32_t fw_revision;
+ unsigned long con_in_handle;
+ unsigned long con_in;
+ unsigned long con_out_handle;
+ unsigned long con_out;
+ unsigned long stderr_handle;
+ unsigned long stderr;
+ unsigned long runtime;
+ unsigned long boottime;
+ unsigned long nr_tables;
+ unsigned long tables;
+} efi_system_table_t;
+
+struct ia64_boot_param {
+ uint64_t command_line; /* physical address of
+ command linearguments */
+ uint64_t efi_systab; /* physical address of
+ EFI system table */
+ uint64_t efi_memmap; /* physical address of
+ EFI memory map */
+ uint64_t efi_memmap_size; /* size of EFI memory map */
+ uint64_t efi_memdesc_size; /* size of an EFI memory map
+ descriptor */
+ uint32_t efi_memdesc_version; /* memory descriptor version */
+ struct {
+ uint16_t num_cols; /* number of columns on console
+ output device */
+ uint16_t num_rows; /* number of rows on console
+ output device */
+ uint16_t orig_x; /* cursor's x position */
+ uint16_t orig_y; /* cursor's y position */
+ } console_info;
+ uint64_t fpswa; /* physical address of
+ the fpswa interface */
+ uint64_t initrd_start;
+ uint64_t initrd_size;
+
+ uint64_t vmcode_start;
+ uint64_t vmcode_size;
+};
+
+typedef struct {
+ uint32_t type;
+ uint32_t pad;
+ uint64_t phys_addr;
+ uint64_t virt_addr;
+ uint64_t num_pages;
+ uint64_t attribute;
+} efi_memory_desc_t;
+
+struct loaded_segment {
+ unsigned long start;
+ unsigned long end;
+};
+
+struct kexec_boot_params {
+ uint64_t vmcode_base;
+ uint64_t vmcode_size;
+ uint64_t ramdisk_base;
+ uint64_t ramdisk_size;
+ uint64_t command_line;
+ uint64_t command_line_len;
+ uint64_t efi_memmap_base;
+ uint64_t efi_memmap_size;
+ uint64_t boot_param_base;
+ struct loaded_segment *loaded_segments;
+ unsigned long loaded_segments_num;
+};
+
+void
+setup_arch(void)
+{
+ reset_vga();
+}
+
+inline unsigned long PA(unsigned long addr)
+{
+ return addr & 0x0fffffffffffffffLL;
+}
+
+void
+patch_efi_memmap(struct kexec_boot_params *params,
+ struct ia64_boot_param *boot_param)
+{
+ void *dest = (void *)params->efi_memmap_base;
+ void *src = (void *)boot_param->efi_memmap;
+ uint64_t orig_type;
+ efi_memory_desc_t *src_md, *dst_md;
+ void *src_end = src + boot_param->efi_memmap_size;
+ unsigned long i;
+ for (; src < src_end; src += boot_param->efi_memdesc_size,
+ dest += boot_param->efi_memdesc_size) {
+ unsigned long mstart, mend;
+ src_md = src;
+ dst_md = dest;
+ if (src_md->num_pages == 0)
+ continue;
+ mstart = src_md->phys_addr;
+ mend = src_md->phys_addr +
+ (src_md->num_pages << EFI_PAGE_SHIFT);
+ *dst_md = *src_md;
+ if (src_md->type == EFI_LOADER_DATA)
+ dst_md->type = EFI_CONVENTIONAL_MEMORY;
+ /* segments are already sorted and aligned to 4K */
+ orig_type = dst_md->type;
+ for (i = 0; i < params->loaded_segments_num; i++) {
+ struct loaded_segment *seg;
+ unsigned long start_pages, mid_pages, end_pages;
+
+ seg = &params->loaded_segments[i];
+ if (seg->start < mstart || seg->start >= mend)
+ continue;
+
+ while (seg->end > mend && src < src_end) {
+ src += boot_param->efi_memdesc_size;
+ src_md = src;
+ /* TODO check contig and attribute here */
+ mend = src_md->phys_addr +
+ (src_md->num_pages << EFI_PAGE_SHIFT);
+ }
+ if (seg->end < mend && src < src_end) {
+ void *src_next;
+ efi_memory_desc_t *src_next_md;
+ src_next = src + boot_param->efi_memdesc_size;
+ src_next_md = src_next;
+ if (src_next_md->type ==
+ EFI_CONVENTIONAL_MEMORY) {
+ /* TODO check contig and attribute */
+ src += boot_param->efi_memdesc_size;
+ src_md = src;
+ mend = src_md->phys_addr +
+ (src_md->num_pages <<
+ EFI_PAGE_SHIFT);
+ }
+
+ }
+ start_pages = (seg->start - mstart) >> EFI_PAGE_SHIFT;
+ mid_pages = (seg->end - seg->start) >> EFI_PAGE_SHIFT;
+ end_pages = (mend - seg->end) >> EFI_PAGE_SHIFT;
+ if (start_pages) {
+ dst_md->num_pages = start_pages;
+ dest += boot_param->efi_memdesc_size;
+ dst_md = dest;
+ *dst_md = *src_md;
+ }
+ dst_md->phys_addr = seg->start;
+ dst_md->num_pages = mid_pages;
+ dst_md->type = EFI_LOADER_DATA;
+ if (!end_pages)
+ break;
+ dest += boot_param->efi_memdesc_size;
+ dst_md = dest;
+ *dst_md = *src_md;
+ dst_md->phys_addr = seg->end;
+ dst_md->num_pages = end_pages;
+ dst_md->type = orig_type;
+ mstart = seg->end;
+ }
+ }
+
+ boot_param->efi_memmap_size = dest - (void *)params->efi_memmap_base;
+}
+
+void
+flush_icache_range(char *start, unsigned long len)
+{
+ unsigned long i, addr;
+ addr = (unsigned long)start & ~31UL;
+ len += (unsigned long)start - addr;
+ for (i = 0;i < len; i += 32)
+ asm volatile("fc.i %0"::"r"(start + i):"memory");
+ asm volatile (";;sync.i;;":::"memory");
+ asm volatile ("srlz.i":::"memory");
+}
+
+extern char __dummy_efi_function[], __dummy_efi_function_end[];
+
+
+void
+ia64_env_setup(struct ia64_boot_param *boot_param,
+ struct kexec_boot_params *params)
+{
+ unsigned long len;
+ efi_system_table_t *systab;
+ efi_runtime_services_t *runtime;
+ unsigned long *set_virtual_address_map;
+ char *command_line = (char *)params->command_line;
+ uint64_t command_line_len = params->command_line_len;
+ struct ia64_boot_param *new_boot_param =
+ (struct ia64_boot_param *) params->boot_param_base;
+ memcpy(new_boot_param, boot_param, 4096);
+
+ /*
+ * patch efi_runtime->set_virtual_address_map to a dummy function
+ *
+ * The EFI specification mandates that set_virtual_address_map only
+ * takes effect the first time that it is called, and that
+ * subsequent calls will return error. By replacing it with a
+ * dummy function the new OS can think it is calling it again
+ * without either the OS or any buggy EFI implementations getting
+ * upset.
+ *
+ * Note: as the EFI specification says that set_virtual_address_map
+ * will only take affect the first time it is called, the mapping
+ * can't be updated, and thus mapping of the old and new OS really
+ * needs to be the same.
+ */
+ len = __dummy_efi_function_end - __dummy_efi_function;
+ memcpy(command_line + command_line_len,
+ __dummy_efi_function, len);
+ systab = (efi_system_table_t *)new_boot_param->efi_systab;
+ runtime = (efi_runtime_services_t *)PA(systab->runtime);
+ set_virtual_address_map =
+ (unsigned long *)PA(runtime->set_virtual_address_map);
+ *(set_virtual_address_map) =
+ (unsigned long)(command_line + command_line_len);
+ flush_icache_range(command_line + command_line_len, len);
+
+ patch_efi_memmap(params, new_boot_param);
+
+ new_boot_param->efi_memmap = params->efi_memmap_base;
+ new_boot_param->command_line = params->command_line;
+ new_boot_param->console_info.orig_x = 0;
+ new_boot_param->console_info.orig_y = 0;
+ new_boot_param->initrd_start = params->ramdisk_base;
+ new_boot_param->initrd_size = params->ramdisk_size;
+ new_boot_param->vmcode_start = params->vmcode_base;
+ new_boot_param->vmcode_size = params->vmcode_size;
+}
+
+/* This function can be used to execute after the SHA256 verification. */
+void post_verification_setup_arch(void)
+{
+ /* Nothing for now */
+}
diff --git a/purgatory/arch/ia64/purgatory-ia64.h b/purgatory/arch/ia64/purgatory-ia64.h
new file mode 100644
index 0000000..8fd3af1
--- /dev/null
+++ b/purgatory/arch/ia64/purgatory-ia64.h
@@ -0,0 +1,5 @@
+#ifndef PURGATORY_IA64_H
+#define PURGATORY_IA64_H
+
+void reset_vga(void);
+#endif /* PURGATORY_IA64_H */
diff --git a/purgatory/arch/ia64/vga.c b/purgatory/arch/ia64/vga.c
new file mode 100644
index 0000000..dceadb7
--- /dev/null
+++ b/purgatory/arch/ia64/vga.c
@@ -0,0 +1,143 @@
+#include "io.h"
+void reset_vga(void)
+{
+ /* Hello */
+ inb(0x3da);
+ outb(0, 0x3c0);
+
+ /* Sequencer registers */
+ outw(0x0300, 0x3c4);
+ outw(0x0001, 0x3c4);
+ outw(0x0302, 0x3c4);
+ outw(0x0003, 0x3c4);
+ outw(0x0204, 0x3c4);
+
+ /* Ensure CRTC regs 0-7 are unlocked by clearing bit 7 of CRTC[17] */
+ outw(0x0e11, 0x3d4);
+ /* CRTC registers */
+ outw(0x5f00, 0x3d4);
+ outw(0x4f01, 0x3d4);
+ outw(0x5002, 0x3d4);
+ outw(0x8203, 0x3d4);
+ outw(0x5504, 0x3d4);
+ outw(0x8105, 0x3d4);
+ outw(0xbf06, 0x3d4);
+ outw(0x1f07, 0x3d4);
+ outw(0x0008, 0x3d4);
+ outw(0x4f09, 0x3d4);
+ outw(0x200a, 0x3d4);
+ outw(0x0e0b, 0x3d4);
+ outw(0x000c, 0x3d4);
+ outw(0x000d, 0x3d4);
+ outw(0x010e, 0x3d4);
+ outw(0xe00f, 0x3d4);
+ outw(0x9c10, 0x3d4);
+ outw(0x8e11, 0x3d4);
+ outw(0x8f12, 0x3d4);
+ outw(0x2813, 0x3d4);
+ outw(0x1f14, 0x3d4);
+ outw(0x9615, 0x3d4);
+ outw(0xb916, 0x3d4);
+ outw(0xa317, 0x3d4);
+ outw(0xff18, 0x3d4);
+
+ /* Graphic registers */
+ outw(0x0000, 0x3ce);
+ outw(0x0001, 0x3ce);
+ outw(0x0002, 0x3ce);
+ outw(0x0003, 0x3ce);
+ outw(0x0004, 0x3ce);
+ outw(0x1005, 0x3ce);
+ outw(0x0e06, 0x3ce);
+ outw(0x0007, 0x3ce);
+ outw(0xff08, 0x3ce);
+
+ /* Attribute registers */
+ inb(0x3da);
+ outb(0x00, 0x3c0);
+ outb(0x00, 0x3c0);
+
+ inb(0x3da);
+ outb(0x01, 0x3c0);
+ outb(0x01, 0x3c0);
+
+ inb(0x3da);
+ outb(0x02, 0x3c0);
+ outb(0x02, 0x3c0);
+
+ inb(0x3da);
+ outb(0x03, 0x3c0);
+ outb(0x03, 0x3c0);
+
+ inb(0x3da);
+ outb(0x04, 0x3c0);
+ outb(0x04, 0x3c0);
+
+ inb(0x3da);
+ outb(0x05, 0x3c0);
+ outb(0x05, 0x3c0);
+
+ inb(0x3da);
+ outb(0x06, 0x3c0);
+ outb(0x14, 0x3c0);
+
+ inb(0x3da);
+ outb(0x07, 0x3c0);
+ outb(0x07, 0x3c0);
+
+ inb(0x3da);
+ outb(0x08, 0x3c0);
+ outb(0x38, 0x3c0);
+
+ inb(0x3da);
+ outb(0x09, 0x3c0);
+ outb(0x39, 0x3c0);
+
+ inb(0x3da);
+ outb(0x0a, 0x3c0);
+ outb(0x3a, 0x3c0);
+
+ inb(0x3da);
+ outb(0x0b, 0x3c0);
+ outb(0x3b, 0x3c0);
+
+ inb(0x3da);
+ outb(0x0c, 0x3c0);
+ outb(0x3c, 0x3c0);
+
+ inb(0x3da);
+ outb(0x0d, 0x3c0);
+ outb(0x3d, 0x3c0);
+
+ inb(0x3da);
+ outb(0x0e, 0x3c0);
+ outb(0x3e, 0x3c0);
+
+ inb(0x3da);
+ outb(0x0f, 0x3c0);
+ outb(0x3f, 0x3c0);
+
+ inb(0x3da);
+ outb(0x10, 0x3c0);
+ outb(0x0c, 0x3c0);
+
+ inb(0x3da);
+ outb(0x11, 0x3c0);
+ outb(0x00, 0x3c0);
+
+ inb(0x3da);
+ outb(0x12, 0x3c0);
+ outb(0x0f, 0x3c0);
+
+ inb(0x3da);
+ outb(0x13, 0x3c0);
+ outb(0x08, 0x3c0);
+
+ inb(0x3da);
+ outb(0x14, 0x3c0);
+ outb(0x00, 0x3c0);
+
+ /* Goodbye */
+ inb(0x3da);
+ outb(0x20, 0x3c0);
+}
diff --git a/purgatory/arch/loongarch/Makefile b/purgatory/arch/loongarch/Makefile
new file mode 100644
index 0000000..b0c47b2
--- /dev/null
+++ b/purgatory/arch/loongarch/Makefile
@@ -0,0 +1,10 @@
+#
+# Purgatory loongarch
+#
+
+loongarch_PURGATORY_SRCS+= purgatory/arch/loongarch/purgatory-loongarch.c
+loongarch_PURGATORY_SRCS+= purgatory/arch/loongarch/console-loongarch.c
+
+dist += purgatory/arch/loongarch/Makefile $(loongarch_PURGATORY_SRCS) \
+ purgatory/arch/loongarch/purgatory-loongarch.h
+
diff --git a/purgatory/arch/loongarch/console-loongarch.c b/purgatory/arch/loongarch/console-loongarch.c
new file mode 100644
index 0000000..af34ecf
--- /dev/null
+++ b/purgatory/arch/loongarch/console-loongarch.c
@@ -0,0 +1,7 @@
+#include <purgatory.h>
+#include "unused.h"
+
+void putchar(int UNUSED(ch))
+{
+ /* Nothing for now */
+}
diff --git a/purgatory/arch/loongarch/purgatory-loongarch.c b/purgatory/arch/loongarch/purgatory-loongarch.c
new file mode 100644
index 0000000..abe9297
--- /dev/null
+++ b/purgatory/arch/loongarch/purgatory-loongarch.c
@@ -0,0 +1,7 @@
+#include <purgatory.h>
+#include "purgatory-loongarch.h"
+
+void setup_arch(void)
+{
+ /* Nothing for now */
+}
diff --git a/purgatory/arch/loongarch/purgatory-loongarch.h b/purgatory/arch/loongarch/purgatory-loongarch.h
new file mode 100644
index 0000000..cd1ab97
--- /dev/null
+++ b/purgatory/arch/loongarch/purgatory-loongarch.h
@@ -0,0 +1,6 @@
+#ifndef PURGATORY_LOONGARCH_H
+#define PURGATORY_LOONGARCH_H
+
+/* nothing yet */
+
+#endif /* PURGATORY_LOONGARCH_H */
diff --git a/purgatory/arch/mips/Makefile b/purgatory/arch/mips/Makefile
new file mode 100644
index 0000000..87888f3
--- /dev/null
+++ b/purgatory/arch/mips/Makefile
@@ -0,0 +1,10 @@
+#
+# Purgatory mips
+#
+
+mips_PURGATORY_SRCS+= purgatory/arch/mips/purgatory-mips.c
+mips_PURGATORY_SRCS+= purgatory/arch/mips/console-mips.c
+
+dist += purgatory/arch/mips/Makefile $(mips_PURGATORY_SRCS) \
+ purgatory/arch/mips/purgatory-mips.h
+
diff --git a/purgatory/arch/mips/console-mips.c b/purgatory/arch/mips/console-mips.c
new file mode 100644
index 0000000..af34ecf
--- /dev/null
+++ b/purgatory/arch/mips/console-mips.c
@@ -0,0 +1,7 @@
+#include <purgatory.h>
+#include "unused.h"
+
+void putchar(int UNUSED(ch))
+{
+ /* Nothing for now */
+}
diff --git a/purgatory/arch/mips/purgatory-mips.c b/purgatory/arch/mips/purgatory-mips.c
new file mode 100644
index 0000000..cb25bf7
--- /dev/null
+++ b/purgatory/arch/mips/purgatory-mips.c
@@ -0,0 +1,7 @@
+#include <purgatory.h>
+#include "purgatory-mips.h"
+
+void setup_arch(void)
+{
+ /* Nothing for now */
+}
diff --git a/purgatory/arch/mips/purgatory-mips.h b/purgatory/arch/mips/purgatory-mips.h
new file mode 100644
index 0000000..c57b402
--- /dev/null
+++ b/purgatory/arch/mips/purgatory-mips.h
@@ -0,0 +1,6 @@
+#ifndef PURGATORY_MIPSEL_H
+#define PURGATORY_MIPSEL_H
+
+/* nothing yet */
+
+#endif /* PURGATORY_MIPSEL_H */
diff --git a/purgatory/arch/ppc/Makefile b/purgatory/arch/ppc/Makefile
new file mode 100644
index 0000000..c85c58a
--- /dev/null
+++ b/purgatory/arch/ppc/Makefile
@@ -0,0 +1,14 @@
+#
+# Purgatory ppc
+#
+
+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_EXTRA_CFLAGS += -msoft-float
+
+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/console-ppc.c b/purgatory/arch/ppc/console-ppc.c
new file mode 100644
index 0000000..af34ecf
--- /dev/null
+++ b/purgatory/arch/ppc/console-ppc.c
@@ -0,0 +1,7 @@
+#include <purgatory.h>
+#include "unused.h"
+
+void putchar(int UNUSED(ch))
+{
+ /* Nothing for now */
+}
diff --git a/purgatory/arch/ppc/misc.S b/purgatory/arch/ppc/misc.S
new file mode 100644
index 0000000..bc6a18d
--- /dev/null
+++ b/purgatory/arch/ppc/misc.S
@@ -0,0 +1,301 @@
+/*
+ * 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.
+ *
+ * Rewrittten to work with /sbin/kexec 20 December 2004 Eric Biederman
+ *
+ * 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 "ppc_asm.h"
+
+/* 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"
+
+#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
+
+ .text
+
+/*
+ * Extended precision shifts.
+ *
+ * Updated to be valid for shift counts from 0 to 63 inclusive.
+ * -- Gabriel
+ *
+ * R3/R4 has 64 bit value
+ * R5 has shift count
+ * result in R3/R4
+ *
+ * ashrdi3: arithmetic right shift (sign propagation)
+ * lshrdi3: logical right shift
+ * ashldi3: left shift
+ */
+ .globl __ashrdi3
+__ashrdi3:
+ subfic r6,r5,32
+ srw r4,r4,r5 # LSW = count > 31 ? 0 : LSW >> count
+ addi r7,r5,32 # could be xori, or addi with -32
+ slw r6,r3,r6 # t1 = count > 31 ? 0 : MSW << (32-count)
+ rlwinm r8,r7,0,32 # t3 = (count < 32) ? 32 : 0
+ sraw r7,r3,r7 # t2 = MSW >> (count-32)
+ or r4,r4,r6 # LSW |= t1
+ slw r7,r7,r8 # t2 = (count < 32) ? 0 : t2
+ sraw r3,r3,r5 # MSW = MSW >> count
+ or r4,r4,r7 # LSW |= t2
+ blr
+
+ .globl __ashldi3
+__ashldi3:
+ subfic r6,r5,32
+ slw r3,r3,r5 # MSW = count > 31 ? 0 : MSW << count
+ addi r7,r5,32 # could be xori, or addi with -32
+ srw r6,r4,r6 # t1 = count > 31 ? 0 : LSW >> (32-count)
+ slw r7,r4,r7 # t2 = count < 32 ? 0 : LSW << (count-32)
+ or r3,r3,r6 # MSW |= t1
+ slw r4,r4,r5 # LSW = LSW << count
+ or r3,r3,r7 # MSW |= t2
+ blr
+
+ .globl __lshrdi3
+__lshrdi3:
+ subfic r6,r5,32
+ srw r4,r4,r5 # LSW = count > 31 ? 0 : LSW >> count
+ addi r7,r5,32 # could be xori, or addi with -32
+ slw r6,r3,r6 # t1 = count > 31 ? 0 : MSW << (32-count)
+ srw r7,r3,r7 # t2 = count < 32 ? 0 : MSW >> (count-32)
+ or r4,r4,r6 # LSW |= t1
+ srw r3,r3,r5 # MSW = MSW >> count
+ or r4,r4,r7 # LSW |= t2
+ blr
+
diff --git a/purgatory/arch/ppc/ppc_asm.h b/purgatory/arch/ppc/ppc_asm.h
new file mode 100644
index 0000000..36503a9
--- /dev/null
+++ b/purgatory/arch/ppc/ppc_asm.h
@@ -0,0 +1,506 @@
+/*
+ * ppc_asm.h - mainly bits stolen from Linux kernel asm/reg.h and asm/ppc_asm.h
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+/* Condition Register Bit Fields */
+
+#define cr0 0
+#define cr1 1
+#define cr2 2
+#define cr3 3
+#define cr4 4
+#define cr5 5
+#define cr6 6
+#define cr7 7
+
+
+/* General Purpose Registers (GPRs) */
+
+#define r0 0
+#define r1 1
+#define r2 2
+#define r3 3
+#define r4 4
+#define r5 5
+#define r6 6
+#define r7 7
+#define r8 8
+#define r9 9
+#define r10 10
+#define r11 11
+#define r12 12
+#define r13 13
+#define r14 14
+#define r15 15
+#define r16 16
+#define r17 17
+#define r18 18
+#define r19 19
+#define r20 20
+#define r21 21
+#define r22 22
+#define r23 23
+#define r24 24
+#define r25 25
+#define r26 26
+#define r27 27
+#define r28 28
+#define r29 29
+#define r30 30
+#define r31 31
+
+/* Machine State Register (MSR) Fields */
+#define MSR_SF (1<<63)
+#define MSR_ISF (1<<61)
+#define MSR_VEC (1<<25) /* Enable AltiVec */
+#define MSR_POW (1<<18) /* Enable Power Management */
+#define MSR_WE (1<<18) /* Wait State Enable */
+#define MSR_TGPR (1<<17) /* TLB Update registers in use */
+#define MSR_CE (1<<17) /* Critical Interrupt Enable */
+#define MSR_ILE (1<<16) /* Interrupt Little Endian */
+#define MSR_EE (1<<15) /* External Interrupt Enable */
+#define MSR_PR (1<<14) /* Problem State / Privilege Level */
+#define MSR_FP (1<<13) /* Floating Point enable */
+#define MSR_ME (1<<12) /* Machine Check Enable */
+#define MSR_FE0 (1<<11) /* Floating Exception mode 0 */
+#define MSR_SE (1<<10) /* Single Step */
+#define MSR_BE (1<<9) /* Branch Trace */
+#define MSR_DE (1<<9) /* Debug Exception Enable */
+#define MSR_FE1 (1<<8) /* Floating Exception mode 1 */
+#define MSR_IP (1<<6) /* Exception prefix 0x000/0xFFF */
+#define MSR_IR (1<<5) /* Instruction Relocate */
+#define MSR_DR (1<<4) /* Data Relocate */
+#define MSR_PE (1<<3) /* Protection Enable */
+#define MSR_PX (1<<2) /* Protection Exclusive Mode */
+#define MSR_RI (1<<1) /* Recoverable Exception */
+#define MSR_LE (1<<0) /* Little Endian */
+
+/* Special Purpose Registers (SPRNs)*/
+#define SPRN_CTR 0x009 /* Count Register */
+#define SPRN_DABR 0x3F5 /* Data Address Breakpoint Register */
+#define SPRN_DAR 0x013 /* Data Address Register */
+#define SPRN_TBRL 0x10C /* Time Base Read Lower Register (user, R/O) */
+#define SPRN_TBRU 0x10D /* Time Base Read Upper Register (user, R/O) */
+#define SPRN_TBWL 0x11C /* Time Base Lower Register (super, R/W) */
+#define SPRN_TBWU 0x11D /* Time Base Upper Register (super, R/W) */
+#define SPRN_HIOR 0x137 /* 970 Hypervisor interrupt offset */
+#define SPRN_DBAT0L 0x219 /* Data BAT 0 Lower Register */
+#define SPRN_DBAT0U 0x218 /* Data BAT 0 Upper Register */
+#define SPRN_DBAT1L 0x21B /* Data BAT 1 Lower Register */
+#define SPRN_DBAT1U 0x21A /* Data BAT 1 Upper Register */
+#define SPRN_DBAT2L 0x21D /* Data BAT 2 Lower Register */
+#define SPRN_DBAT2U 0x21C /* Data BAT 2 Upper Register */
+#define SPRN_DBAT3L 0x21F /* Data BAT 3 Lower Register */
+#define SPRN_DBAT3U 0x21E /* Data BAT 3 Upper Register */
+#define SPRN_DBAT4L 0x239 /* Data BAT 4 Lower Register */
+#define SPRN_DBAT4U 0x238 /* Data BAT 4 Upper Register */
+#define SPRN_DBAT5L 0x23B /* Data BAT 5 Lower Register */
+#define SPRN_DBAT5U 0x23A /* Data BAT 5 Upper Register */
+#define SPRN_DBAT6L 0x23D /* Data BAT 6 Lower Register */
+#define SPRN_DBAT6U 0x23C /* Data BAT 6 Upper Register */
+#define SPRN_DBAT7L 0x23F /* Data BAT 7 Lower Register */
+#define SPRN_DBAT7U 0x23E /* Data BAT 7 Upper Register */
+
+#define SPRN_DEC 0x016 /* Decrement Register */
+#define SPRN_DER 0x095 /* Debug Enable Regsiter */
+#define DER_RSTE 0x40000000 /* Reset Interrupt */
+#define DER_CHSTPE 0x20000000 /* Check Stop */
+#define DER_MCIE 0x10000000 /* Machine Check Interrupt */
+#define DER_EXTIE 0x02000000 /* External Interrupt */
+#define DER_ALIE 0x01000000 /* Alignment Interrupt */
+#define DER_PRIE 0x00800000 /* Program Interrupt */
+#define DER_FPUVIE 0x00400000 /* FP Unavailable Interrupt */
+#define DER_DECIE 0x00200000 /* Decrementer Interrupt */
+#define DER_SYSIE 0x00040000 /* System Call Interrupt */
+#define DER_TRE 0x00020000 /* Trace Interrupt */
+#define DER_SEIE 0x00004000 /* FP SW Emulation Interrupt */
+#define DER_ITLBMSE 0x00002000 /* Imp. Spec. Instruction TLB Miss */
+#define DER_ITLBERE 0x00001000 /* Imp. Spec. Instruction TLB Error */
+#define DER_DTLBMSE 0x00000800 /* Imp. Spec. Data TLB Miss */
+#define DER_DTLBERE 0x00000400 /* Imp. Spec. Data TLB Error */
+#define DER_LBRKE 0x00000008 /* Load/Store Breakpoint Interrupt */
+#define DER_IBRKE 0x00000004 /* Instruction Breakpoint Interrupt */
+#define DER_EBRKE 0x00000002 /* External Breakpoint Interrupt */
+#define DER_DPIE 0x00000001 /* Dev. Port Nonmaskable Request */
+#define SPRN_DMISS 0x3D0 /* Data TLB Miss Register */
+#define SPRN_DSISR 0x012 /* Data Storage Interrupt Status Register */
+#define SPRN_EAR 0x11A /* External Address Register */
+#define SPRN_HASH1 0x3D2 /* Primary Hash Address Register */
+#define SPRN_HASH2 0x3D3 /* Secondary Hash Address Resgister */
+#define SPRN_HID0 0x3F0 /* Hardware Implementation Register 0 */
+#define HID0_EMCP (1<<31) /* Enable Machine Check pin */
+#define HID0_EBA (1<<29) /* Enable Bus Address Parity */
+#define HID0_EBD (1<<28) /* Enable Bus Data Parity */
+#define HID0_SBCLK (1<<27)
+#define HID0_EICE (1<<26)
+#define HID0_TBEN (1<<26) /* Timebase enable - 745x */
+#define HID0_ECLK (1<<25)
+#define HID0_PAR (1<<24)
+#define HID0_STEN (1<<24) /* Software table search enable - 745x */
+#define HID0_HIGH_BAT (1<<23) /* Enable high BATs - 7455 */
+#define HID0_DOZE (1<<23)
+#define HID0_NAP (1<<22)
+#define HID0_SLEEP (1<<21)
+#define HID0_DPM (1<<20)
+#define HID0_BHTCLR (1<<18) /* Clear branch history table - 7450 */
+#define HID0_XAEN (1<<17) /* Extended addressing enable - 7450 */
+#define HID0_NHR (1<<16) /* Not hard reset (software bit-7450)*/
+#define HID0_ICE (1<<15) /* Instruction Cache Enable */
+#define HID0_DCE (1<<14) /* Data Cache Enable */
+#define HID0_ILOCK (1<<13) /* Instruction Cache Lock */
+#define HID0_DLOCK (1<<12) /* Data Cache Lock */
+#define HID0_ICFI (1<<11) /* Instr. Cache Flash Invalidate */
+#define HID0_DCI (1<<10) /* Data Cache Invalidate */
+#define HID0_SPD (1<<9) /* Speculative disable */
+#define HID0_SGE (1<<7) /* Store Gathering Enable */
+#define HID0_SIED (1<<7) /* Serial Instr. Execution [Disable] */
+#define HID0_DFCA (1<<6) /* Data Cache Flush Assist */
+#define HID0_LRSTK (1<<4) /* Link register stack - 745x */
+#define HID0_BTIC (1<<5) /* Branch Target Instr Cache Enable */
+#define HID0_ABE (1<<3) /* Address Broadcast Enable */
+#define HID0_FOLD (1<<3) /* Branch Folding enable - 745x */
+#define HID0_BHTE (1<<2) /* Branch History Table Enable */
+#define HID0_BTCD (1<<1) /* Branch target cache disable */
+#define HID0_NOPDST (1<<1) /* No-op dst, dstt, etc. instr. */
+#define HID0_NOPTI (1<<0) /* No-op dcbt and dcbst instr. */
+
+#define SPRN_HID1 0x3F1 /* Hardware Implementation Register 1 */
+#define HID1_EMCP (1<<31) /* 7450 Machine Check Pin Enable */
+#define HID1_PC0 (1<<16) /* 7450 PLL_CFG[0] */
+#define HID1_PC1 (1<<15) /* 7450 PLL_CFG[1] */
+#define HID1_PC2 (1<<14) /* 7450 PLL_CFG[2] */
+#define HID1_PC3 (1<<13) /* 7450 PLL_CFG[3] */
+#define HID1_SYNCBE (1<<11) /* 7450 ABE for sync, eieio */
+#define HID1_ABE (1<<10) /* 7450 Address Broadcast Enable */
+#define SPRN_HID2 0x3F8 /* Hardware Implementation Register 2 */
+#define SPRN_IABR 0x3F2 /* Instruction Address Breakpoint Register */
+#define SPRN_HID4 0x3F4 /* 970 HID4 */
+#define SPRN_HID5 0x3F6 /* 970 HID5 */
+#if !defined(SPRN_IAC1) && !defined(SPRN_IAC2)
+#define SPRN_IAC1 0x3F4 /* Instruction Address Compare 1 */
+#define SPRN_IAC2 0x3F5 /* Instruction Address Compare 2 */
+#endif
+#define SPRN_IBAT0L 0x211 /* Instruction BAT 0 Lower Register */
+#define SPRN_IBAT0U 0x210 /* Instruction BAT 0 Upper Register */
+#define SPRN_IBAT1L 0x213 /* Instruction BAT 1 Lower Register */
+#define SPRN_IBAT1U 0x212 /* Instruction BAT 1 Upper Register */
+#define SPRN_IBAT2L 0x215 /* Instruction BAT 2 Lower Register */
+#define SPRN_IBAT2U 0x214 /* Instruction BAT 2 Upper Register */
+#define SPRN_IBAT3L 0x217 /* Instruction BAT 3 Lower Register */
+#define SPRN_IBAT3U 0x216 /* Instruction BAT 3 Upper Register */
+#define SPRN_IBAT4L 0x231 /* Instruction BAT 4 Lower Register */
+#define SPRN_IBAT4U 0x230 /* Instruction BAT 4 Upper Register */
+#define SPRN_IBAT5L 0x233 /* Instruction BAT 5 Lower Register */
+#define SPRN_IBAT5U 0x232 /* Instruction BAT 5 Upper Register */
+#define SPRN_IBAT6L 0x235 /* Instruction BAT 6 Lower Register */
+#define SPRN_IBAT6U 0x234 /* Instruction BAT 6 Upper Register */
+#define SPRN_IBAT7L 0x237 /* Instruction BAT 7 Lower Register */
+#define SPRN_IBAT7U 0x236 /* Instruction BAT 7 Upper Register */
+#define SPRN_ICMP 0x3D5 /* Instruction TLB Compare Register */
+#define SPRN_ICTC 0x3FB /* Instruction Cache Throttling Control Reg */
+#define SPRN_ICTRL 0x3F3 /* 1011 7450 icache and interrupt ctrl */
+#define ICTRL_EICE 0x08000000 /* enable icache parity errs */
+#define ICTRL_EDC 0x04000000 /* enable dcache parity errs */
+#define ICTRL_EICP 0x00000100 /* enable icache par. check */
+#define SPRN_IMISS 0x3D4 /* Instruction TLB Miss Register */
+#define SPRN_IMMR 0x27E /* Internal Memory Map Register */
+#define SPRN_L2CR 0x3F9 /* Level 2 Cache Control Regsiter */
+#define SPRN_L2CR2 0x3f8
+#define L2CR_L2E 0x80000000 /* L2 enable */
+#define L2CR_L2PE 0x40000000 /* L2 parity enable */
+#define L2CR_L2SIZ_MASK 0x30000000 /* L2 size mask */
+#define L2CR_L2SIZ_256KB 0x10000000 /* L2 size 256KB */
+#define L2CR_L2SIZ_512KB 0x20000000 /* L2 size 512KB */
+#define L2CR_L2SIZ_1MB 0x30000000 /* L2 size 1MB */
+#define L2CR_L2CLK_MASK 0x0e000000 /* L2 clock mask */
+#define L2CR_L2CLK_DISABLED 0x00000000 /* L2 clock disabled */
+#define L2CR_L2CLK_DIV1 0x02000000 /* L2 clock / 1 */
+#define L2CR_L2CLK_DIV1_5 0x04000000 /* L2 clock / 1.5 */
+#define L2CR_L2CLK_DIV2 0x08000000 /* L2 clock / 2 */
+#define L2CR_L2CLK_DIV2_5 0x0a000000 /* L2 clock / 2.5 */
+#define L2CR_L2CLK_DIV3 0x0c000000 /* L2 clock / 3 */
+#define L2CR_L2RAM_MASK 0x01800000 /* L2 RAM type mask */
+#define L2CR_L2RAM_FLOW 0x00000000 /* L2 RAM flow through */
+#define L2CR_L2RAM_PIPE 0x01000000 /* L2 RAM pipelined */
+#define L2CR_L2RAM_PIPE_LW 0x01800000 /* L2 RAM pipelined latewr */
+#define L2CR_L2DO 0x00400000 /* L2 data only */
+#define L2CR_L2I 0x00200000 /* L2 global invalidate */
+#define L2CR_L2CTL 0x00100000 /* L2 RAM control */
+#define L2CR_L2WT 0x00080000 /* L2 write-through */
+#define L2CR_L2TS 0x00040000 /* L2 test support */
+#define L2CR_L2OH_MASK 0x00030000 /* L2 output hold mask */
+#define L2CR_L2OH_0_5 0x00000000 /* L2 output hold 0.5 ns */
+#define L2CR_L2OH_1_0 0x00010000 /* L2 output hold 1.0 ns */
+#define L2CR_L2SL 0x00008000 /* L2 DLL slow */
+#define L2CR_L2DF 0x00004000 /* L2 differential clock */
+#define L2CR_L2BYP 0x00002000 /* L2 DLL bypass */
+#define L2CR_L2IP 0x00000001 /* L2 GI in progress */
+#define SPRN_L3CR 0x3FA /* Level 3 Cache Control Regsiter */
+#define L3CR_L3E 0x80000000 /* L3 enable */
+#define L3CR_L3PE 0x40000000 /* L3 data parity enable */
+#define L3CR_L3APE 0x20000000 /* L3 addr parity enable */
+#define L3CR_L3SIZ 0x10000000 /* L3 size */
+#define L3CR_L3CLKEN 0x08000000 /* L3 clock enable */
+#define L3CR_L3RES 0x04000000 /* L3 special reserved bit */
+#define L3CR_L3CLKDIV 0x03800000 /* L3 clock divisor */
+#define L3CR_L3IO 0x00400000 /* L3 instruction only */
+#define L3CR_L3SPO 0x00040000 /* L3 sample point override */
+#define L3CR_L3CKSP 0x00030000 /* L3 clock sample point */
+#define L3CR_L3PSP 0x0000e000 /* L3 P-clock sample point */
+#define L3CR_L3REP 0x00001000 /* L3 replacement algorithm */
+#define L3CR_L3HWF 0x00000800 /* L3 hardware flush */
+#define L3CR_L3I 0x00000400 /* L3 global invalidate */
+#define L3CR_L3RT 0x00000300 /* L3 SRAM type */
+#define L3CR_L3NIRCA 0x00000080 /* L3 non-integer ratio clock adj. */
+#define L3CR_L3DO 0x00000040 /* L3 data only mode */
+#define L3CR_PMEN 0x00000004 /* L3 private memory enable */
+#define L3CR_PMSIZ 0x00000001 /* L3 private memory size */
+#define SPRN_MSSCR0 0x3f6 /* Memory Subsystem Control Register 0 */
+#define SPRN_MSSSR0 0x3f7 /* Memory Subsystem Status Register 1 */
+#define SPRN_LDSTCR 0x3f8 /* Load/Store control register */
+#define SPRN_LDSTDB 0x3f4 /* */
+#define SPRN_LR 0x008 /* Link Register */
+#define SPRN_MMCR0 0x3B8 /* Monitor Mode Control Register 0 */
+#define SPRN_MMCR1 0x3BC /* Monitor Mode Control Register 1 */
+#ifndef SPRN_PIR
+#define SPRN_PIR 0x3FF /* Processor Identification Register */
+#endif
+#define SPRN_PMC1 0x3B9 /* Performance Counter Register 1 */
+#define SPRN_PMC2 0x3BA /* Performance Counter Register 2 */
+#define SPRN_PMC3 0x3BD /* Performance Counter Register 3 */
+#define SPRN_PMC4 0x3BE /* Performance Counter Register 4 */
+#define SPRN_PTEHI 0x3D5 /* 981 7450 PTE HI word (S/W TLB load) */
+#define SPRN_PTELO 0x3D6 /* 982 7450 PTE LO word (S/W TLB load) */
+#define SPRN_PVR 0x11F /* Processor Version Register */
+#define SPRN_RPA 0x3D6 /* Required Physical Address Register */
+#define SPRN_SDA 0x3BF /* Sampled Data Address Register */
+#define SPRN_SDR1 0x019 /* MMU Hash Base Register */
+#define SPRN_SIA 0x3BB /* Sampled Instruction Address Register */
+#define SPRN_SPRG0 0x110 /* Special Purpose Register General 0 */
+#define SPRN_SPRG1 0x111 /* Special Purpose Register General 1 */
+#define SPRN_SPRG2 0x112 /* Special Purpose Register General 2 */
+#define SPRN_SPRG3 0x113 /* Special Purpose Register General 3 */
+#define SPRN_SPRG4 0x114 /* Special Purpose Register General 4 */
+#define SPRN_SPRG5 0x115 /* Special Purpose Register General 5 */
+#define SPRN_SPRG6 0x116 /* Special Purpose Register General 6 */
+#define SPRN_SPRG7 0x117 /* Special Purpose Register General 7 */
+#define SPRN_SRR0 0x01A /* Save/Restore Register 0 */
+#define SPRN_SRR1 0x01B /* Save/Restore Register 1 */
+#define SPRN_THRM1 0x3FC /* Thermal Management Register 1 */
+/* these bits were defined in inverted endian sense originally, ugh, confusing */
+#define THRM1_TIN (1 << 31)
+#define THRM1_TIV (1 << 30)
+#define THRM1_THRES(x) ((x&0x7f)<<23)
+#define THRM3_SITV(x) ((x&0x3fff)<<1)
+#define THRM1_TID (1<<2)
+#define THRM1_TIE (1<<1)
+#define THRM1_V (1<<0)
+#define SPRN_THRM2 0x3FD /* Thermal Management Register 2 */
+#define SPRN_THRM3 0x3FE /* Thermal Management Register 3 */
+#define THRM3_E (1<<0)
+#define SPRN_TLBMISS 0x3D4 /* 980 7450 TLB Miss Register */
+#define SPRN_UMMCR0 0x3A8 /* User Monitor Mode Control Register 0 */
+#define SPRN_UMMCR1 0x3AC /* User Monitor Mode Control Register 0 */
+#define SPRN_UPMC1 0x3A9 /* User Performance Counter Register 1 */
+#define SPRN_UPMC2 0x3AA /* User Performance Counter Register 2 */
+#define SPRN_UPMC3 0x3AD /* User Performance Counter Register 3 */
+#define SPRN_UPMC4 0x3AE /* User Performance Counter Register 4 */
+#define SPRN_USIA 0x3AB /* User Sampled Instruction Address Register */
+#define SPRN_VRSAVE 0x100 /* Vector Register Save Register */
+#define SPRN_XER 0x001 /* Fixed Point Exception Register */
+
+/* Bit definitions for MMCR0 and PMC1 / PMC2. */
+#define MMCR0_PMC1_CYCLES (1 << 7)
+#define MMCR0_PMC1_ICACHEMISS (5 << 7)
+#define MMCR0_PMC1_DTLB (6 << 7)
+#define MMCR0_PMC2_DCACHEMISS 0x6
+#define MMCR0_PMC2_CYCLES 0x1
+#define MMCR0_PMC2_ITLB 0x7
+#define MMCR0_PMC2_LOADMISSTIME 0x5
+
+/* Short-hand versions for a number of the above SPRNs */
+#define CTR SPRN_CTR /* Counter Register */
+#define DAR SPRN_DAR /* Data Address Register */
+#define DABR SPRN_DABR /* Data Address Breakpoint Register */
+#define DBAT0L SPRN_DBAT0L /* Data BAT 0 Lower Register */
+#define DBAT0U SPRN_DBAT0U /* Data BAT 0 Upper Register */
+#define DBAT1L SPRN_DBAT1L /* Data BAT 1 Lower Register */
+#define DBAT1U SPRN_DBAT1U /* Data BAT 1 Upper Register */
+#define DBAT2L SPRN_DBAT2L /* Data BAT 2 Lower Register */
+#define DBAT2U SPRN_DBAT2U /* Data BAT 2 Upper Register */
+#define DBAT3L SPRN_DBAT3L /* Data BAT 3 Lower Register */
+#define DBAT3U SPRN_DBAT3U /* Data BAT 3 Upper Register */
+#define DBAT4L SPRN_DBAT4L /* Data BAT 4 Lower Register */
+#define DBAT4U SPRN_DBAT4U /* Data BAT 4 Upper Register */
+#define DBAT5L SPRN_DBAT5L /* Data BAT 5 Lower Register */
+#define DBAT5U SPRN_DBAT5U /* Data BAT 5 Upper Register */
+#define DBAT6L SPRN_DBAT6L /* Data BAT 6 Lower Register */
+#define DBAT6U SPRN_DBAT6U /* Data BAT 6 Upper Register */
+#define DBAT7L SPRN_DBAT7L /* Data BAT 7 Lower Register */
+#define DBAT7U SPRN_DBAT7U /* Data BAT 7 Upper Register */
+#define DEC SPRN_DEC /* Decrement Register */
+#define DMISS SPRN_DMISS /* Data TLB Miss Register */
+#define DSISR SPRN_DSISR /* Data Storage Interrupt Status Register */
+#define EAR SPRN_EAR /* External Address Register */
+#define HASH1 SPRN_HASH1 /* Primary Hash Address Register */
+#define HASH2 SPRN_HASH2 /* Secondary Hash Address Register */
+#define HID0 SPRN_HID0 /* Hardware Implementation Register 0 */
+#define HID1 SPRN_HID1 /* Hardware Implementation Register 1 */
+#define IABR SPRN_IABR /* Instruction Address Breakpoint Register */
+#define IBAT0L SPRN_IBAT0L /* Instruction BAT 0 Lower Register */
+#define IBAT0U SPRN_IBAT0U /* Instruction BAT 0 Upper Register */
+#define IBAT1L SPRN_IBAT1L /* Instruction BAT 1 Lower Register */
+#define IBAT1U SPRN_IBAT1U /* Instruction BAT 1 Upper Register */
+#define IBAT2L SPRN_IBAT2L /* Instruction BAT 2 Lower Register */
+#define IBAT2U SPRN_IBAT2U /* Instruction BAT 2 Upper Register */
+#define IBAT3L SPRN_IBAT3L /* Instruction BAT 3 Lower Register */
+#define IBAT3U SPRN_IBAT3U /* Instruction BAT 3 Upper Register */
+#define IBAT4L SPRN_IBAT4L /* Instruction BAT 4 Lower Register */
+#define IBAT4U SPRN_IBAT4U /* Instruction BAT 4 Upper Register */
+#define IBAT5L SPRN_IBAT5L /* Instruction BAT 5 Lower Register */
+#define IBAT5U SPRN_IBAT5U /* Instruction BAT 5 Upper Register */
+#define IBAT6L SPRN_IBAT6L /* Instruction BAT 6 Lower Register */
+#define IBAT6U SPRN_IBAT6U /* Instruction BAT 6 Upper Register */
+#define IBAT7L SPRN_IBAT7L /* Instruction BAT 7 Lower Register */
+#define IBAT7U SPRN_IBAT7U /* Instruction BAT 7 Upper Register */
+#define ICMP SPRN_ICMP /* Instruction TLB Compare Register */
+#define IMISS SPRN_IMISS /* Instruction TLB Miss Register */
+#define IMMR SPRN_IMMR /* PPC 860/821 Internal Memory Map Register */
+#define L2CR SPRN_L2CR /* Classic PPC L2 cache control register */
+#define L3CR SPRN_L3CR /* PPC 745x L3 cache control register */
+#define LR SPRN_LR
+#define PVR SPRN_PVR /* Processor Version */
+#define RPA SPRN_RPA /* Required Physical Address Register */
+#define SDR1 SPRN_SDR1 /* MMU hash base register */
+#define SPR0 SPRN_SPRG0 /* Supervisor Private Registers */
+#define SPR1 SPRN_SPRG1
+#define SPR2 SPRN_SPRG2
+#define SPR3 SPRN_SPRG3
+#define SPR4 SPRN_SPRG4
+#define SPR5 SPRN_SPRG5
+#define SPR6 SPRN_SPRG6
+#define SPR7 SPRN_SPRG7
+#define SPRG0 SPRN_SPRG0
+#define SPRG1 SPRN_SPRG1
+#define SPRG2 SPRN_SPRG2
+#define SPRG3 SPRN_SPRG3
+#define SPRG4 SPRN_SPRG4
+#define SPRG5 SPRN_SPRG5
+#define SPRG6 SPRN_SPRG6
+#define SPRG7 SPRN_SPRG7
+#define SRR0 SPRN_SRR0 /* Save and Restore Register 0 */
+#define SRR1 SPRN_SRR1 /* Save and Restore Register 1 */
+#define SRR2 SPRN_SRR2 /* Save and Restore Register 2 */
+#define SRR3 SPRN_SRR3 /* Save and Restore Register 3 */
+#define ICTC SPRN_ICTC /* Instruction Cache Throttling Control Reg */
+#define THRM1 SPRN_THRM1 /* Thermal Management Register 1 */
+#define THRM2 SPRN_THRM2 /* Thermal Management Register 2 */
+#define THRM3 SPRN_THRM3 /* Thermal Management Register 3 */
+#define XER SPRN_XER
+#define TBRL SPRN_TBRL /* Time Base Read Lower Register */
+#define TBRU SPRN_TBRU /* Time Base Read Upper Register */
+#define TBWL SPRN_TBWL /* Time Base Write Lower Register */
+#define TBWU SPRN_TBWU /* Time Base Write Upper Register */
+
+/* Processor Version Register */
+
+/* Processor Version Register (PVR) field extraction */
+
+#define PVR_VER(pvr) (((pvr) >> 16) & 0xFFFF) /* Version field */
+#define PVR_REV(pvr) (((pvr) >> 0) & 0xFFFF) /* Revison field */
+
+/*
+ * IBM has further subdivided the standard PowerPC 16-bit version and
+ * revision subfields of the PVR for the PowerPC 403s into the following:
+ */
+
+#define PVR_FAM(pvr) (((pvr) >> 20) & 0xFFF) /* Family field */
+#define PVR_MEM(pvr) (((pvr) >> 16) & 0xF) /* Member field */
+#define PVR_CORE(pvr) (((pvr) >> 12) & 0xF) /* Core field */
+#define PVR_CFG(pvr) (((pvr) >> 8) & 0xF) /* Configuration field */
+#define PVR_MAJ(pvr) (((pvr) >> 4) & 0xF) /* Major revision field */
+#define PVR_MIN(pvr) (((pvr) >> 0) & 0xF) /* Minor revision field */
+
+/* Processor Version Numbers */
+
+#define PVR_403GA 0x00200000
+#define PVR_403GB 0x00200100
+#define PVR_403GC 0x00200200
+#define PVR_403GCX 0x00201400
+#define PVR_405GP 0x40110000
+#define PVR_STB03XXX 0x40310000
+#define PVR_NP405H 0x41410000
+#define PVR_NP405L 0x41610000
+#define PVR_440GP_RB 0x40120440
+#define PVR_440GP_RC1 0x40120481
+#define PVR_440GP_RC2 0x40200481
+#define PVR_440GX_RA 0x51b21850
+#define PVR_440GX_RB 0x51b21851
+#define PVR_440GX_RB1 0x51b21852
+#define PVR_601 0x00010000
+#define PVR_602 0x00050000
+#define PVR_603 0x00030000
+#define PVR_603e 0x00060000
+#define PVR_603ev 0x00070000
+#define PVR_603r 0x00071000
+#define PVR_604 0x00040000
+#define PVR_604e 0x00090000
+#define PVR_604r 0x000A0000
+#define PVR_620 0x00140000
+#define PVR_740 0x00080000
+#define PVR_750 PVR_740
+#define PVR_740P 0x10080000
+#define PVR_750P PVR_740P
+#define PVR_7400 0x000C0000
+#define PVR_7410 0x800C0000
+#define PVR_7450 0x80000000
+/*
+ * For the 8xx processors, all of them report the same PVR family for
+ * the PowerPC core. The various versions of these processors must be
+ * differentiated by the version number in the Communication Processor
+ * Module (CPM).
+ */
+#define PVR_821 0x00500000
+#define PVR_823 PVR_821
+#define PVR_850 PVR_821
+#define PVR_860 PVR_821
+#define PVR_8240 0x00810100
+#define PVR_8245 0x80811014
+#define PVR_8260 PVR_8240
+
+/* Segment Registers */
+#define SR0 0
+#define SR1 1
+#define SR2 2
+#define SR3 3
+#define SR4 4
+#define SR5 5
+#define SR6 6
+#define SR7 7
+#define SR8 8
+#define SR9 9
+#define SR10 10
+#define SR11 11
+#define SR12 12
+#define SR13 13
+#define SR14 14
+#define SR15 15
+
+
+/* returns r3 = relocated address of sym */
+/* modifies r0 */
+#define RELOC_SYM(sym) \
+ mflr r3; \
+ bl 1f; \
+1: mflr r0; \
+ mtlr r3; \
+ lis r3, 1b@ha; \
+ ori r3, r3, 1b@l; \
+ subf r0, r3, r0; \
+ lis r3, sym@ha; \
+ ori r3, r3, sym@l; \
+ add r3, r3, r0
+
diff --git a/purgatory/arch/ppc/purgatory-ppc.c b/purgatory/arch/ppc/purgatory-ppc.c
new file mode 100644
index 0000000..3df3767
--- /dev/null
+++ b/purgatory/arch/ppc/purgatory-ppc.c
@@ -0,0 +1,48 @@
+/*
+ * 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 panic_kernel = 0;
+unsigned long backup_start = 0;
+unsigned long stack = 0;
+unsigned long dt_offset = 0;
+unsigned long my_thread_ptr = 0;
+unsigned long kernel = 0;
+
+void setup_arch(void)
+{
+ return;
+}
+
+void post_verification_setup_arch(void)
+{
+#ifndef CONFIG_BOOKE
+ if (panic_kernel)
+ crashdump_backup_memory();
+#endif
+}
+
+void crashdump_backup_memory(void)
+{
+ return;
+}
diff --git a/purgatory/arch/ppc/purgatory-ppc.h b/purgatory/arch/ppc/purgatory-ppc.h
new file mode 100644
index 0000000..7eff8aa
--- /dev/null
+++ b/purgatory/arch/ppc/purgatory-ppc.h
@@ -0,0 +1,6 @@
+#ifndef PURGATORY_PPC_H
+#define PURGATORY_PPC_H
+
+void crashdump_backup_memory(void);
+void post_verification_setup_arch(void);
+#endif /* PURGATORY_PPC_H */
diff --git a/purgatory/arch/ppc/v2wrap_32.S b/purgatory/arch/ppc/v2wrap_32.S
new file mode 100644
index 0000000..8b60677
--- /dev/null
+++ b/purgatory/arch/ppc/v2wrap_32.S
@@ -0,0 +1,95 @@
+#
+# 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,my_thread_ptr@h
+ ori 6,6,my_thread_ptr@l
+ lwz 2,0(6) # setup ThreadPointer(TP)
+
+ 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
diff --git a/purgatory/arch/ppc64/Makefile b/purgatory/arch/ppc64/Makefile
new file mode 100644
index 0000000..8ca2719
--- /dev/null
+++ b/purgatory/arch/ppc64/Makefile
@@ -0,0 +1,28 @@
+#
+# Purgatory ppc64
+#
+
+ppc64_PURGATORY_SRCS += purgatory/arch/ppc64/v2wrap.S
+ppc64_PURGATORY_SRCS += purgatory/arch/ppc64/hvCall.S
+ppc64_PURGATORY_SRCS += purgatory/arch/ppc64/purgatory-ppc64.c
+ppc64_PURGATORY_SRCS += purgatory/arch/ppc64/console-ppc64.c
+ppc64_PURGATORY_SRCS += purgatory/arch/ppc64/crashdump_backup.c
+ppc64_PURGATORY_SRCS += purgatory/arch/ppc64/misc.S
+
+ppc64_PURGATORY_EXTRA_CFLAGS += -m64 -msoft-float \
+ -fno-exceptions
+ppc64_PURGATORY_EXTRA_ASFLAGS += -m64
+ifeq ($(SUBARCH),BE)
+ ppc64_PURGATORY_EXTRA_LDFLAGS += -melf64ppc
+ ppc64_PURGATORY_EXTRA_CFLAGS += -mcall-aixdesc
+ ppc64_PURGATORY_EXTRA_ASFLAGS += -mcall-aixdesc
+else
+ ppc64_PURGATORY_EXTRA_LDFLAGS += -melf64lppc
+ ppc64_PURGATORY_EXTRA_CFLAGS += -mlittle-endian
+ ppc64_PURGATORY_EXTRA_ASFLAGS += -mlittle-endian
+endif
+
+dist += purgatory/arch/ppc64/Makefile $(ppc64_PURGATORY_SRCS) \
+ purgatory/arch/ppc64/hvCall.h \
+ purgatory/arch/ppc64/ppc64_asm.h \
+ purgatory/arch/ppc64/purgatory-ppc64.h
diff --git a/purgatory/arch/ppc64/console-ppc64.c b/purgatory/arch/ppc64/console-ppc64.c
new file mode 100644
index 0000000..2403312
--- /dev/null
+++ b/purgatory/arch/ppc64/console-ppc64.c
@@ -0,0 +1,45 @@
+/*
+ * 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 "hvCall.h"
+#include <byteswap.h>
+#include <endian.h>
+#include <asm/byteorder.h>
+
+extern int debug;
+
+void putchar(int c)
+{
+ char buff[16] = "";
+ 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]), __cpu_to_be64(lbuf[1]));
+ return;
+}
diff --git a/purgatory/arch/ppc64/crashdump_backup.c b/purgatory/arch/ppc64/crashdump_backup.c
new file mode 100644
index 0000000..7ba5aff
--- /dev/null
+++ b/purgatory/arch/ppc64/crashdump_backup.c
@@ -0,0 +1,39 @@
+/*
+ * 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 <stdint.h>
+#include <string.h>
+#include "../../../kexec/arch/ppc64/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/purgatory/arch/ppc64/hvCall.S b/purgatory/arch/ppc64/hvCall.S
new file mode 100644
index 0000000..a96c489
--- /dev/null
+++ b/purgatory/arch/ppc64/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/purgatory/arch/ppc64/hvCall.h b/purgatory/arch/ppc64/hvCall.h
new file mode 100644
index 0000000..187e24d
--- /dev/null
+++ b/purgatory/arch/ppc64/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/purgatory/arch/ppc64/misc.S b/purgatory/arch/ppc64/misc.S
new file mode 100644
index 0000000..4ee188d
--- /dev/null
+++ b/purgatory/arch/ppc64/misc.S
@@ -0,0 +1,197 @@
+/*
+ * This is from linux/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
+ * 64 bit additions from reading the PPC elf64abi document.
+ *
+ * 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.
+ */
+
+#define r0 0
+#define r1 1
+#define r2 2
+#define r3 3
+#define r4 4
+#define r5 5
+#define r6 6
+#define r7 7
+#define r8 8
+#define r9 9
+#define r10 10
+#define r11 11
+#define r12 12
+#define r13 13
+#define r14 14
+#define r15 15
+#define r16 16
+#define r17 17
+#define r18 18
+#define r19 19
+#define r20 20
+#define r21 21
+#define r22 22
+#define r23 23
+#define r24 24
+#define r25 25
+#define r26 26
+#define r27 27
+#define r28 28
+#define r29 29
+#define r30 30
+#define r31 31
+
+ .text
+
+.globl _savegpr0_14
+_savegpr0_14:
+ std r14,-144(r1)
+.globl _savegpr0_15
+_savegpr0_15:
+ std r15,-136(r1)
+.globl _savegpr0_16
+_savegpr0_16:
+ std r16,-128(r1)
+.globl _savegpr0_17
+_savegpr0_17:
+ std r17,-120(r1)
+.globl _savegpr0_18
+_savegpr0_18:
+ std r18,-112(r1)
+.globl _savegpr0_19
+_savegpr0_19:
+ std r19,-104(r1)
+.globl _savegpr0_20
+_savegpr0_20:
+ std r20,-96(r1)
+.globl _savegpr0_21
+_savegpr0_21:
+ std r21,-88(r1)
+.globl _savegpr0_22
+_savegpr0_22:
+ std r22,-80(r1)
+.globl _savegpr0_23
+_savegpr0_23:
+ std r23,-72(r1)
+.globl _savegpr0_24
+_savegpr0_24:
+ std r24,-64(r1)
+.globl _savegpr0_25
+_savegpr0_25:
+ std r25,-56(r1)
+.globl _savegpr0_26
+_savegpr0_26:
+ std r26,-48(r1)
+.globl _savegpr0_27
+_savegpr0_27:
+ std r27,-40(r1)
+.globl _savegpr0_28
+_savegpr0_28:
+ std r28,-32(r1)
+.globl _savegpr0_29
+_savegpr0_29:
+ std r29,-24(r1)
+.globl _savegpr0_30
+_savegpr0_30:
+ std r30,-16(r1)
+.globl _savegpr0_31
+_savegpr0_31:
+ std r31,-8(r1)
+ std r0,16(r1)
+ blr
+
+.globl _restgpr0_14
+_restgpr0_14:
+ ld r14,-144(r1)
+.globl _restgpr0_15
+_restgpr0_15:
+ ld r15,-136(r1)
+.globl _restgpr0_16
+_restgpr0_16:
+ ld r16,-128(r1)
+.globl _restgpr0_17
+_restgpr0_17:
+ ld r17,-120(r1)
+.globl _restgpr0_18
+_restgpr0_18:
+ ld r18,-112(r1)
+.globl _restgpr0_19
+_restgpr0_19:
+ ld r19,-104(r1)
+.globl _restgpr0_20
+_restgpr0_20:
+ ld r20,-96(r1)
+.globl _restgpr0_21
+_restgpr0_21:
+ ld r21,-88(r1)
+.globl _restgpr0_22
+_restgpr0_22:
+ ld r22,-80(r1)
+.globl _restgpr0_23
+_restgpr0_23:
+ ld r23,-72(r1)
+.globl _restgpr0_24
+_restgpr0_24:
+ ld r24,-64(r1)
+.globl _restgpr0_25
+_restgpr0_25:
+ ld r25,-56(r1)
+.globl _restgpr0_26
+_restgpr0_26:
+ ld r26,-48(r1)
+.globl _restgpr0_27
+_restgpr0_27:
+ ld r27,-40(r1)
+.globl _restgpr0_28
+_restgpr0_28:
+ ld r28,-32(r1)
+.globl _restgpr0_29
+_restgpr0_29:
+ ld r0,16(r1)
+ ld r29,-24(r1)
+ mtlr r0
+ ld r30,-16(r1)
+ ld r31,-8(r1)
+ blr
+
+.globl _restgpr0_30
+_restgpr0_30:
+ ld r30,-16(r1)
+.globl _restgpr0_31
+_restgpr0_31:
+ ld r0,16(r1)
+ ld r31,-8(r1)
+ mtlr r0
+ blr
diff --git a/purgatory/arch/ppc64/ppc64_asm.h b/purgatory/arch/ppc64/ppc64_asm.h
new file mode 100644
index 0000000..b8746fd
--- /dev/null
+++ b/purgatory/arch/ppc64/ppc64_asm.h
@@ -0,0 +1,16 @@
+/*
+ * ppc64_asm.h - common defines for PPC64 assembly parts
+ *
+ * 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/purgatory/arch/ppc64/purgatory-ppc64.c b/purgatory/arch/ppc64/purgatory-ppc64.c
new file mode 100644
index 0000000..7248ac8
--- /dev/null
+++ b/purgatory/arch/ppc64/purgatory-ppc64.c
@@ -0,0 +1,44 @@
+/*
+ * 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-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/purgatory/arch/ppc64/purgatory-ppc64.h b/purgatory/arch/ppc64/purgatory-ppc64.h
new file mode 100644
index 0000000..52eaf43
--- /dev/null
+++ b/purgatory/arch/ppc64/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/purgatory/arch/ppc64/v2wrap.S b/purgatory/arch/ppc64/v2wrap.S
new file mode 100644
index 0000000..3534080
--- /dev/null
+++ b/purgatory/arch/ppc64/v2wrap.S
@@ -0,0 +1,137 @@
+#
+# 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.
+#
+
+#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
diff --git a/purgatory/arch/s390/Makefile b/purgatory/arch/s390/Makefile
new file mode 100644
index 0000000..c94cc3c
--- /dev/null
+++ b/purgatory/arch/s390/Makefile
@@ -0,0 +1,10 @@
+#
+# Purgatory s390
+#
+
+s390_PURGATORY_SRCS += purgatory/arch/s390/console-s390.c
+s390_PURGATORY_SRCS += purgatory/arch/s390/setup-s390.S
+s390_PURGATORY_SRCS += purgatory/arch/s390/purgatory-s390.c
+
+dist += purgatory/arch/s390/Makefile $(s390_PURGATORY_SRCS)
+
diff --git a/purgatory/arch/s390/console-s390.c b/purgatory/arch/s390/console-s390.c
new file mode 100644
index 0000000..fe4bd91
--- /dev/null
+++ b/purgatory/arch/s390/console-s390.c
@@ -0,0 +1,14 @@
+/*
+ * S390 console code (currently not implemented)
+ *
+ * Copyright IBM Corp. 2011
+ *
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
+ */
+
+#include <purgatory.h>
+#include "unused.h"
+
+void putchar(int UNUSED(ch))
+{
+}
diff --git a/purgatory/arch/s390/purgatory-s390.c b/purgatory/arch/s390/purgatory-s390.c
new file mode 100644
index 0000000..f438cf6
--- /dev/null
+++ b/purgatory/arch/s390/purgatory-s390.c
@@ -0,0 +1,93 @@
+/*
+ * S390 purgatory
+ *
+ * Copyright IBM Corp. 2011
+ *
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include "../../../kexec/kexec-sha256.h"
+
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+
+extern struct sha256_region sha256_regions[SHA256_REGIONS];
+
+unsigned long crash_base = (unsigned long) -1;
+unsigned long crash_size = (unsigned long) -1;
+
+/*
+ * Implement memcpy using the mvcle instruction
+ */
+static void memcpy_fast(void *target, void *src, unsigned long size)
+{
+ register unsigned long __target asm("2") = (unsigned long) target;
+ register unsigned long __size1 asm("3") = size;
+ register unsigned long __src asm("4") = (unsigned long) src;
+ register unsigned long __size2 asm("5") = size;
+
+ asm volatile (
+ "0: mvcle %0,%2,0\n"
+ " jo 0b\n"
+ : "+d" (__target), "+d" (__size1), "+d" (__src), "+d" (__size2)
+ :
+ : "cc", "memory"
+ );
+}
+
+/*
+ * Swap memory areas
+ */
+static void memswap(void *addr1, void *addr2, unsigned long size)
+{
+ unsigned long off, copy_len;
+ static char buf[1024];
+
+ for (off = 0; off < size; off += sizeof(buf)) {
+ copy_len = MIN(size - off, sizeof(buf));
+ memcpy_fast(buf, (void *) addr2 + off, copy_len);
+ memcpy_fast(addr2 + off, addr1 + off, copy_len);
+ memcpy_fast(addr1 + off, buf, copy_len);
+ }
+}
+
+/*
+ * Nothing to do
+ */
+void setup_arch(void)
+{
+}
+
+/*
+ * Do swap of [crash base - crash base + size] with [0 - crash size]
+ *
+ * We swap all kexec segments except of purgatory. The rest is copied
+ * from [0 - crash size] to [crash base - crash base + size].
+ * We use [0x2000 - 0x10000] for purgatory. This area is never used
+ * by s390 Linux kernels.
+ *
+ * This functions assumes that the sha256_regions[] is sorted.
+ */
+void post_verification_setup_arch(void)
+{
+ unsigned long start, len, last = crash_base + 0x10000;
+ struct sha256_region *ptr, *end;
+
+ end = &sha256_regions[sizeof(sha256_regions)/sizeof(sha256_regions[0])];
+ for (ptr = sha256_regions; ptr < end; ptr++) {
+ if (!ptr->start)
+ continue;
+ start = MAX(ptr->start, crash_base + 0x10000);
+ len = ptr->len - (start - ptr->start);
+ memcpy_fast((void *) last, (void *) last - crash_base,
+ start - last);
+ memswap((void *) start - crash_base, (void *) start, len);
+ last = start + len;
+ }
+ memcpy_fast((void *) last, (void *) last - crash_base,
+ crash_base + crash_size - last);
+ memcpy_fast((void *) crash_base, (void *) 0, 0x2000);
+}
diff --git a/purgatory/arch/s390/setup-s390.S b/purgatory/arch/s390/setup-s390.S
new file mode 100644
index 0000000..215478a
--- /dev/null
+++ b/purgatory/arch/s390/setup-s390.S
@@ -0,0 +1,47 @@
+/*
+ * Purgatory setup code
+ *
+ * Copyright IBM Corp. 2011
+ *
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
+ */
+
+ .text
+ .globl purgatory_start
+ .balign 16
+purgatory_start:
+#ifdef __s390x__
+ larl %r5,gprs_save_area
+ stmg %r6,%r15,0(%r5)
+ larl %r15,lstack_end
+ aghi %r15,-160
+
+ cghi %r2,0
+ je verify_checksums
+
+ brasl %r14,purgatory
+ larl %r14,kdump_psw
+ lpswe 0(%r14)
+
+verify_checksums:
+ brasl %r14,verify_sha256_digest
+ larl %r5,gprs_save_area
+ lmg %r6,%r15,0(%r5)
+ br %r14
+ .section ".data"
+ .balign 16
+kdump_psw:
+ .quad 0x0000000180000000
+ .quad 0x0000000000010010
+
+ .bss
+gprs_save_area:
+ .fill 80
+
+ .balign 4096
+lstack:
+ .skip 4096
+lstack_end:
+#else
+0: j 0
+#endif
diff --git a/purgatory/arch/sh/Makefile b/purgatory/arch/sh/Makefile
new file mode 100644
index 0000000..33ad4db
--- /dev/null
+++ b/purgatory/arch/sh/Makefile
@@ -0,0 +1,8 @@
+#
+# Purgatory sh
+#
+
+sh_PURGATORY_SRCS =
+
+dist += purgatory/arch/sh/Makefile $(sh_PURGATORY_SRCS)
+
diff --git a/purgatory/arch/x86_64/Makefile b/purgatory/arch/x86_64/Makefile
new file mode 100644
index 0000000..b0e277a
--- /dev/null
+++ b/purgatory/arch/x86_64/Makefile
@@ -0,0 +1,28 @@
+#
+# Purgatory x86_64
+#
+
+x86_64_PURGATORY_SRCS_native = purgatory/arch/x86_64/entry64-32.S
+x86_64_PURGATORY_SRCS_native += purgatory/arch/x86_64/entry64.S
+x86_64_PURGATORY_SRCS_native += purgatory/arch/x86_64/setup-x86_64.S
+x86_64_PURGATORY_SRCS_native += purgatory/arch/x86_64/stack.S
+x86_64_PURGATORY_SRCS_native += purgatory/arch/x86_64/purgatory-x86_64.c
+
+x86_64_PURGATORY_SRCS += $(x86_64_PURGATORY_SRCS_native)
+
+dist += purgatory/arch/x86_64/Makefile $(x86_64_PURGATORY_SRCS_native) \
+ purgatory/arch/x86_64/include/arch/io.h \
+ purgatory/arch/x86_64/include/arch/debug.h \
+ purgatory/arch/x86_64/purgatory-x86_64.h
+
+# Don't add sources in i386/ to dist, as i386/Makefile adds them
+x86_64_PURGATORY_SRCS += purgatory/arch/i386/entry32-16.S
+x86_64_PURGATORY_SRCS += purgatory/arch/i386/entry32-16-debug.S
+x86_64_PURGATORY_SRCS += purgatory/arch/i386/crashdump_backup.c
+x86_64_PURGATORY_SRCS += purgatory/arch/i386/console-x86.c
+x86_64_PURGATORY_SRCS += purgatory/arch/i386/vga.c
+x86_64_PURGATORY_SRCS += purgatory/arch/i386/pic.c
+
+ifneq ($(SUBARCH),x32)
+x86_64_PURGATORY_EXTRA_CFLAGS = -mcmodel=large
+endif
diff --git a/purgatory/arch/x86_64/entry64-32.S b/purgatory/arch/x86_64/entry64-32.S
new file mode 100644
index 0000000..f2b6377
--- /dev/null
+++ b/purgatory/arch/x86_64/entry64-32.S
@@ -0,0 +1,164 @@
+/*
+ * purgatory: setup code
+ *
+ * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com)
+ *
+ * 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.
+ */
+
+.data
+ .equ MSR_K6_EFER, 0xC0000080
+ .equ EFER_LME, 0x00000100
+ .equ X86_CR4_PAE, 0x00000020
+ .equ CR0_PG, 0x80000000
+
+ .text
+ .balign 16
+ .globl entry32, entry32_regs
+entry32:
+ .code64
+
+ /* Setup the 4G offset of entry32 lm_exit code segment */
+ movq $0x00CF9A000000ffff, %rax
+
+ leaq entry32(%rip), %rbx /* Low 24 bits */
+ andq $0xffffff, %rbx
+ shlq $16, %rbx
+ orq %rbx, %rax
+
+ leaq entry32(%rip), %rbx /* High 8 bits */
+ movq $0xff000000, %rdx
+ andq %rdx, %rbx
+ shlq $32, %rbx
+ orq %rbx, %rax
+
+ movq %rax, (gdt + 0x20)(%rip)
+
+ /* Setup a gdt that is generally usefully */
+ lgdt gdt(%rip)
+
+ /* Setup the far pointer to the entry point */
+ movl eip(%rip), %eax
+ movl %eax, entry32_addr(%rip)
+
+ /* Switch to 32bit compatiblity mode */
+ ljmp *lm_exit_addr(%rip)
+lm_exit:
+ .code32
+
+ /* Disable paging */
+ movl %cr0, %eax
+ andl $~CR0_PG, %eax
+ movl %eax, %cr0
+
+ /* Disable long mode */
+ movl $MSR_K6_EFER, %ecx
+ rdmsr
+ andl $~EFER_LME, %eax
+ wrmsr
+
+ /* Disable PAE */
+ xorl %eax, %eax
+ movl %eax, %cr4
+
+ /* load the data segments */
+ movl $0x18, %eax /* data segment */
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+ movl %eax, %fs
+ movl %eax, %gs
+
+ /* Load the registers */
+ movl %cs:eax - entry32, %eax
+ movl %cs:ecx - entry32, %ecx
+ movl %cs:edx - entry32, %edx
+ movl %cs:esi - entry32, %esi
+ movl %cs:edi - entry32, %edi
+ movl %cs:esp - entry32, %esp
+ movl %cs:ebp - entry32, %ebp
+ movl %cs:ebx - entry32, %ebx
+
+ /* Jump to the loaded image */
+ ljmp *%cs:entry32_addr - entry32
+
+ .section ".data"
+ .balign 16
+gdt: /* 0x00 unusable segment
+ * 0x08 unused
+ * so use them as the gdt ptr
+ */
+ .word gdt_end - gdt - 1
+ .quad gdt
+ .word 0, 0, 0
+
+ /* Documented linux kernel segments */
+ /* 0x10 4GB flat code segment */
+ .word 0xFFFF, 0x0000, 0x9A00, 0x00CF
+ /* 0x18 4GB flat data segment */
+ .word 0xFFFF, 0x0000, 0x9200, 0x00CF
+
+ /* 0x20 4GB flat code segment base at entry32 */
+ .word 0xFFFF, 0x0000, 0x9A00, 0x0CF
+ /* 0x28 dummy */
+ .word 0x0000, 0x0000, 0x0000, 0x000
+ /* 0x30 dummy */
+ .word 0x0000, 0x0000, 0x0000, 0x000
+ /* 0x38 dummy */
+ .word 0x0000, 0x0000, 0x0000, 0x000
+ /* 0x40 dummy */
+ .word 0x0000, 0x0000, 0x0000, 0x000
+ /* 0x48 dummy */
+ .word 0x0000, 0x0000, 0x0000, 0x000
+ /* 0x50 dummy */
+ .word 0x0000, 0x0000, 0x0000, 0x000
+ /* 0x58 dummy */
+ .word 0x0000, 0x0000, 0x0000, 0x000
+
+ /* Segments used by the 2.5.x kernel */
+ /* 0x60 4GB flat code segment */
+ .word 0xFFFF, 0x0000, 0x9A00, 0x00CF
+ /* 0x68 4GB flat data segment */
+ .word 0xFFFF, 0x0000, 0x9200, 0x00CF
+gdt_end:
+
+ .section ".rodata"
+ .balign 4
+lm_exit_addr:
+ .long lm_exit - entry32
+ .long 0x20
+
+ .section ".data"
+ .balign 4
+entry32_addr:
+ .long 0x00000000
+ .long 0x10
+
+ .section ".rodata"
+ .balign 4
+entry32_regs:
+eax: .long 0x00000000
+ebx: .long 0x00000000
+ecx: .long 0x00000000
+edx: .long 0x00000000
+esi: .long 0x00000000
+edi: .long 0x00000000
+esp: .long 0x00000000
+ebp: .long 0x00000000
+eip: .quad entry16 /* low 32 bits address
+ * high 32bits zeros
+ * uses 64bit reloc
+ */
+ .size entry32_regs, (. - 4) - entry32_regs
+
diff --git a/purgatory/arch/x86_64/entry64.S b/purgatory/arch/x86_64/entry64.S
new file mode 100644
index 0000000..e3223b7
--- /dev/null
+++ b/purgatory/arch/x86_64/entry64.S
@@ -0,0 +1,107 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com)
+ *
+ * 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 "arch/debug.h"
+
+.text
+.code64
+ .balign 16
+ .globl entry64, entry64_regs
+entry64:
+ /* Don't worry about special registers... */
+
+ /* Setup a gdt that should be preserved */
+ lgdt gdt(%rip)
+
+ /* load the data segments */
+ movl $0x18, %eax /* data segment */
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+ movl %eax, %fs
+ movl %eax, %gs
+
+ leaq stack_init(%rip), %rsp
+ pushq $0x10 /* CS */
+ leaq new_cs_exit(%rip), %rax
+ pushq %rax
+ lretq
+new_cs_exit:
+
+ /* Load the registers */
+ movq rax(%rip), %rax
+ movq rbx(%rip), %rbx
+ movq rcx(%rip), %rcx
+ movq rdx(%rip), %rdx
+ movq rsi(%rip), %rsi
+ movq rdi(%rip), %rdi
+ movq rsp(%rip), %rsp
+ movq rbp(%rip), %rbp
+ movq r8(%rip), %r8
+ movq r9(%rip), %r9
+ movq r10(%rip), %r10
+ movq r11(%rip), %r11
+ movq r12(%rip), %r12
+ movq r13(%rip), %r13
+ movq r14(%rip), %r14
+ movq r15(%rip), %r15
+
+ /* Jump to the new code... */
+ jmpq *rip(%rip)
+
+ .section ".rodata"
+ .balign 4
+entry64_regs:
+rax: .quad 0x00000000
+rbx: .quad 0x00000000
+rcx: .quad 0x00000000
+rdx: .quad 0x00000000
+rsi: .quad 0x00000000
+rdi: .quad 0x00000000
+rsp: .quad 0x00000000
+rbp: .quad 0x00000000
+r8: .quad 0x00000000
+r9: .quad 0x00000000
+r10: .quad 0x00000000
+r11: .quad 0x00000000
+r12: .quad 0x00000000
+r13: .quad 0x00000000
+r14: .quad 0x00000000
+r15: .quad 0x00000000
+rip: .quad entry32
+ .size entry64_regs, . - entry64_regs
+
+ .section ".rodata"
+ .balign 16
+gdt: /* 0x00 unusable segment
+ * 0x08 unused
+ * so use them as the gdt ptr
+ */
+ .word gdt_end - gdt - 1
+ .quad gdt
+ .word 0, 0, 0
+
+ /* 0x10 4GB flat code segment */
+ .word 0xFFFF, 0x0000, 0x9A00, 0x00AF
+
+ /* 0x18 4GB flat data segment */
+ .word 0xFFFF, 0x0000, 0x9200, 0x00CF
+gdt_end:
+stack: .quad 0, 0
+stack_init:
diff --git a/purgatory/arch/x86_64/include/arch/debug.h b/purgatory/arch/x86_64/include/arch/debug.h
new file mode 100644
index 0000000..e050942
--- /dev/null
+++ b/purgatory/arch/x86_64/include/arch/debug.h
@@ -0,0 +1,317 @@
+/* Base Address */
+#define TTYS0_BASE 0x3f8
+/* Data */
+#define TTYS0_RBR (TTYS0_BASE+0x00)
+#define TTYS0_TBR (TTYS0_BASE+0x00)
+/* Control */
+#define TTYS0_IER (TTYS0_BASE+0x01)
+#define TTYS0_IIR (TTYS0_BASE+0x02)
+#define TTYS0_FCR (TTYS0_BASE+0x02)
+#define TTYS0_LCR (TTYS0_BASE+0x03)
+#define TTYS0_MCR (TTYS0_BASE+0x04)
+
+#define TTYS0_DLL (TTYS0_BASE+0x00)
+#define TTYS0_DLM (TTYS0_BASE+0x01)
+/* Status */
+#define TTYS0_LSR (TTYS0_BASE+0x05)
+#define TTYS0_MSR (TTYS0_BASE+0x06)
+#define TTYS0_SCR (TTYS0_BASE+0x07)
+
+#define TTYS0_BAUD 9600
+#define TTYS0_DIV (115200/TTYS0_BAUD)
+#define TTYS0_DIV_LO (TTYS0_DIV&0xFF)
+#define TTYS0_DIV_HI ((TTYS0_DIV >> 8)&0xFF)
+
+#if ((115200%TTYS0_BAUD) != 0)
+#error Bad ttyS0 baud rate
+#endif
+
+#define TTYS0_INIT \
+ /* disable interrupts */ \
+ movb $0x00, %al ; \
+ movw $TTYS0_IER, %dx ; \
+ outb %al, %dx ; \
+ ; \
+ /* enable fifos */ \
+ movb $0x01, %al ; \
+ movw $TTYS0_FCR, %dx ; \
+ outb %al, %dx ; \
+ ; \
+ /* Set Baud Rate Divisor to TTYS0_BAUD */ \
+ movw $TTYS0_LCR, %dx ; \
+ movb $0x83, %al ; \
+ outb %al, %dx ; \
+ ; \
+ movw $TTYS0_DLL, %dx ; \
+ movb $TTYS0_DIV_LO, %al ; \
+ outb %al, %dx ; \
+ ; \
+ movw $TTYS0_DLM, %dx ; \
+ movb $TTYS0_DIV_HI, %al ; \
+ outb %al, %dx ; \
+ ; \
+ movw $TTYS0_LCR, %dx ; \
+ movb $0x03, %al ; \
+ outb %al, %dx
+
+
+ /* uses: ax, dx */
+#define TTYS0_TX_AL \
+ mov %al, %ah ; \
+9: mov $TTYS0_LSR, %dx ; \
+ inb %dx, %al ; \
+ test $0x20, %al ; \
+ je 9b ; \
+ mov $TTYS0_TBR, %dx ; \
+ mov %ah, %al ; \
+ outb %al, %dx
+
+ /* uses: ax, dx */
+#define TTYS0_TX_CHAR(byte) \
+ mov byte, %al ; \
+ TTYS0_TX_AL
+
+ /* uses: eax, dx */
+#define TTYS0_TX_HEX32(lword) \
+ mov lword, %eax ; \
+ shr $28, %eax ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %eax ; \
+ shr $24, %eax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %eax ; \
+ shr $20, %eax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %eax ; \
+ shr $16, %eax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %eax ; \
+ shr $12, %eax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %eax ; \
+ shr $8, %eax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %eax ; \
+ shr $4, %eax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %eax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL
+
+ /* uses: rax, dx */
+#define TTYS0_TX_HEX64(lword) \
+ mov lword, %rax ; \
+ shr $60, %rax ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $56, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $52, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $48, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $44, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $40, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $36, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $32, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $28, %rax ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $24, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $20, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $16, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $12, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $8, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ shr $4, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL ; \
+ ; \
+ mov lword, %rax ; \
+ and $0x0f, %al ; \
+ add $'0', %al ; \
+ cmp $'9', %al ; \
+ jle 9f ; \
+ add $39, %al ; \
+9: ; \
+ TTYS0_TX_AL
+
+
+#define DEBUG_CHAR(x) TTYS0_TX_CHAR($x) ; TTYS0_TX_CHAR($'\r') ; TTYS0_TX_CHAR($'\n')
+#define DEBUG_TX_HEX32(x) TTYS0_TX_HEX32(x); TTYS0_TX_CHAR($'\r') ; TTYS0_TX_CHAR($'\n')
+#define DEBUG_TX_HEX64(x) TTYS0_TX_HEX64(x); TTYS0_TX_CHAR($'\r') ; TTYS0_TX_CHAR($'\n')
+
diff --git a/purgatory/arch/x86_64/include/arch/io.h b/purgatory/arch/x86_64/include/arch/io.h
new file mode 100644
index 0000000..464cd14
--- /dev/null
+++ b/purgatory/arch/x86_64/include/arch/io.h
@@ -0,0 +1,7 @@
+#ifndef ARCH_X86_64_IO_H
+#define ARCH_X86_64_IO_H
+
+#include <stdint.h>
+#include "../../../i386/include/arch/io.h"
+
+#endif /* ARCH_X86_64_IO_H */
diff --git a/purgatory/arch/x86_64/purgatory-x86_64.c b/purgatory/arch/x86_64/purgatory-x86_64.c
new file mode 100644
index 0000000..c25a9c2
--- /dev/null
+++ b/purgatory/arch/x86_64/purgatory-x86_64.c
@@ -0,0 +1,30 @@
+#include <stdint.h>
+#include <stddef.h>
+#include <purgatory.h>
+#include "purgatory-x86_64.h"
+
+uint8_t reset_vga = 0;
+uint8_t legacy_pic = 0;
+uint8_t panic_kernel = 0;
+unsigned long jump_back_entry = 0;
+char *cmdline_end = NULL;
+
+void setup_arch(void)
+{
+ if (reset_vga) x86_reset_vga();
+ if (legacy_pic) x86_setup_legacy_pic();
+}
+
+void x86_setup_jump_back_entry(void)
+{
+ if (cmdline_end)
+ sprintf(cmdline_end, " kexec_jump_back_entry=0x%lx",
+ jump_back_entry);
+}
+
+/* This function can be used to execute after the SHA256 verification. */
+void post_verification_setup_arch(void)
+{
+ if (panic_kernel) crashdump_backup_memory();
+ if (jump_back_entry) x86_setup_jump_back_entry();
+}
diff --git a/purgatory/arch/x86_64/purgatory-x86_64.h b/purgatory/arch/x86_64/purgatory-x86_64.h
new file mode 100644
index 0000000..209edea
--- /dev/null
+++ b/purgatory/arch/x86_64/purgatory-x86_64.h
@@ -0,0 +1,4 @@
+#ifndef PURGATORY_X86_64_H
+#define PURGATORY_X86_64_H
+#include "../i386/purgatory-x86.h"
+#endif /* PURGATORY_X86_64_H */
diff --git a/purgatory/arch/x86_64/setup-x86_64.S b/purgatory/arch/x86_64/setup-x86_64.S
new file mode 100644
index 0000000..95572d8
--- /dev/null
+++ b/purgatory/arch/x86_64/setup-x86_64.S
@@ -0,0 +1,76 @@
+/*
+ * purgatory: setup code
+ *
+ * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com)
+ *
+ * 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 "arch/debug.h"
+
+
+#undef i386
+
+ .text
+ .globl purgatory_start
+ .balign 16
+purgatory_start:
+ .code64
+
+ /* Load a gdt so I know what the segment registers are */
+ lgdt gdt(%rip)
+
+ /* load the data segments */
+ movl $0x18, %eax /* data segment */
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+ movl %eax, %fs
+ movl %eax, %gs
+
+ /* In 64bit mode the code segment is meaningless */
+
+ movq 0(%rsp), %rax
+ movq %rax, jump_back_entry(%rip)
+
+ /* Setup a stack */
+ leaq lstack_end(%rip), %rsp
+
+ /* Call the C code */
+ call purgatory
+ jmp entry64
+
+ .section ".rodata"
+ .balign 16
+gdt: /* 0x00 unusable segment
+ * 0x08 unused
+ * so use them as the gdt ptr
+ */
+ .word gdt_end - gdt - 1
+ .quad gdt
+ .word 0, 0, 0
+
+ /* 0x10 4GB flat code segment */
+ .word 0xFFFF, 0x0000, 0x9A00, 0x00AF
+
+ /* 0x18 4GB flat data segment */
+ .word 0xFFFF, 0x0000, 0x9200, 0x00CF
+gdt_end:
+
+ .bss
+ .balign 4096
+lstack:
+ .skip 4096
+lstack_end:
+
diff --git a/purgatory/arch/x86_64/stack.S b/purgatory/arch/x86_64/stack.S
new file mode 100644
index 0000000..4188ea3
--- /dev/null
+++ b/purgatory/arch/x86_64/stack.S
@@ -0,0 +1,44 @@
+/*
+ * purgatory: stack
+ *
+ * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com)
+ *
+ * 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.
+ */
+
+ /* A stack for the loaded kernel.
+ * Seperate and in the data section so it can be prepopulated.
+ */
+ .data
+ .balign 4096
+ .globl stack, stack_end
+ .globl stack_arg32_1, stack_arg32_2, stack_arg32_3 ,stack_arg32_4
+ .globl stack_arg32_5, stack_arg32_6, stack_arg32_7 ,stack_arg32_8
+ .globl stack_arg64_1, stack_arg64_2, stack_arg64_3 ,stack_arg64_4
+
+stack:
+ .skip 4096 - (8*4)
+stack_arg64_4: ; .size stack_arg64_4, 8
+stack_arg32_8: .long 0 ; .size stack_arg32_8, 4
+stack_arg32_7: .long 0 ; .size stack_arg32_7, 4
+stack_arg64_3: ; .size stack_arg64_3, 8
+stack_arg32_6: .long 0 ; .size stack_arg32_6, 4
+stack_arg32_5: .long 0 ; .size stack_arg32_5, 4
+stack_arg64_2: ; .size stack_arg64_2, 8
+stack_arg32_4: .long 0 ; .size stack_arg32_4, 4
+stack_arg32_3: .long 0 ; .size stack_arg32_3, 4
+stack_arg64_1: ; .size stack_arg64_1, 8
+stack_arg32_2: .long 0 ; .size stack_arg32_2, 4
+stack_arg32_1: .long 0 ; .size stack_arg32_1, 4
+stack_end:
diff --git a/purgatory/include/purgatory.h b/purgatory/include/purgatory.h
new file mode 100644
index 0000000..788ce49
--- /dev/null
+++ b/purgatory/include/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/purgatory/include/string.h b/purgatory/include/string.h
new file mode 100644
index 0000000..14e172d
--- /dev/null
+++ b/purgatory/include/string.h
@@ -0,0 +1,12 @@
+#ifndef STRING_H
+#define STRING_H
+
+#include <stddef.h>
+
+size_t strnlen(const char *s, size_t max);
+void* memset(void* s, int c, size_t n);
+void* memcpy(void *dest, const void *src, size_t len);
+int memcmp(void *src1, void *src2, size_t len);
+
+
+#endif /* STRING_H */
diff --git a/purgatory/printf.c b/purgatory/printf.c
new file mode 100644
index 0000000..9a78243
--- /dev/null
+++ b/purgatory/printf.c
@@ -0,0 +1,154 @@
+#include <stdarg.h>
+#include <limits.h>
+#include <stdint.h>
+#include <purgatory.h>
+#include <string.h>
+
+/*
+ * 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/purgatory/purgatory.c b/purgatory/purgatory.c
new file mode 100644
index 0000000..73930aa
--- /dev/null
+++ b/purgatory/purgatory.c
@@ -0,0 +1,53 @@
+
+#include <limits.h>
+#include <stdint.h>
+#include <purgatory.h>
+#include <sha256.h>
+#include <string.h>
+#include "../kexec/kexec-sha256.h"
+
+struct sha256_region sha256_regions[SHA256_REGIONS] = {};
+sha256_digest_t sha256_digest = { };
+int skip_checks = 0;
+
+int verify_sha256_digest(void)
+{
+ struct sha256_region *ptr, *end;
+ sha256_digest_t digest;
+ size_t i;
+ sha256_context ctx;
+ sha256_starts(&ctx);
+ end = &sha256_regions[sizeof(sha256_regions)/sizeof(sha256_regions[0])];
+ for(ptr = sha256_regions; ptr < end; ptr++) {
+ sha256_update(&ctx, (uint8_t *)((uintptr_t)ptr->start),
+ ptr->len);
+ }
+ sha256_finish(&ctx, 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 (!skip_checks && verify_sha256_digest()) {
+ for(;;) {
+ /* loop forever */
+ }
+ }
+ post_verification_setup_arch();
+}
diff --git a/purgatory/string.c b/purgatory/string.c
new file mode 100644
index 0000000..f06c460
--- /dev/null
+++ b/purgatory/string.c
@@ -0,0 +1,55 @@
+#include <stddef.h>
+#include <string.h>
+
+size_t strnlen(const char *s, size_t max)
+{
+ size_t len = 0;
+ while(len < max && *s) {
+ len++;
+ s++;
+ }
+ return len;
+}
+
+void* memset(void* s, int c, size_t n)
+{
+ size_t i;
+ char *ss = (char*)s;
+
+ for (i=0;i<n;i++) ss[i] = c;
+ return s;
+}
+
+
+void* memcpy(void *dest, const void *src, size_t len)
+{
+ size_t i;
+ unsigned char *d;
+ const unsigned char *s;
+ d = dest;
+ s = src;
+
+ for (i=0; i < len; i++)
+ d[i] = s[i];
+
+ return dest;
+}
+
+
+int memcmp(void *src1, void *src2, size_t len)
+{
+ unsigned char *s1, *s2;
+ size_t i;
+ s1 = src1;
+ s2 = src2;
+ for(i = 0; i < len; i++) {
+ if (*s1 != *s2) {
+ return *s2 - *s1;
+ }
+ s1++;
+ s2++;
+ }
+ return 0;
+
+}
+