diff options
Diffstat (limited to 'purgatory')
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 = ¶ms->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; + +} + |