summaryrefslogtreecommitdiffstats
path: root/purgatory/arch/i386
diff options
context:
space:
mode:
Diffstat (limited to 'purgatory/arch/i386')
-rw-r--r--purgatory/arch/i386/Makefile21
-rw-r--r--purgatory/arch/i386/compat_x86_64.S100
-rw-r--r--purgatory/arch/i386/console-x86.c135
-rw-r--r--purgatory/arch/i386/crashdump_backup.c51
-rw-r--r--purgatory/arch/i386/entry32-16-debug.S200
-rw-r--r--purgatory/arch/i386/entry32-16.S165
-rw-r--r--purgatory/arch/i386/entry32.S110
-rw-r--r--purgatory/arch/i386/include/arch/debug.h316
-rw-r--r--purgatory/arch/i386/include/arch/io.h110
-rw-r--r--purgatory/arch/i386/pic.c51
-rw-r--r--purgatory/arch/i386/purgatory-x86.c57
-rw-r--r--purgatory/arch/i386/purgatory-x86.h9
-rw-r--r--purgatory/arch/i386/setup-x86.S75
-rw-r--r--purgatory/arch/i386/stack.S39
-rw-r--r--purgatory/arch/i386/vga.c152
15 files changed, 1591 insertions, 0 deletions
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);
+}