diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 02:56:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 02:56:35 +0000 |
commit | eba0cfa6b0bef4f2e73c8630a7efa3944df8b0f8 (patch) | |
tree | 74c37eede1f0634cc5de1c63c934edaa1630c6bc /purgatory/arch/i386 | |
parent | Initial commit. (diff) | |
download | kexec-tools-upstream/1%2.0.27.tar.xz kexec-tools-upstream/1%2.0.27.zip |
Adding upstream version 1:2.0.27.upstream/1%2.0.27upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'purgatory/arch/i386')
-rw-r--r-- | purgatory/arch/i386/Makefile | 21 | ||||
-rw-r--r-- | purgatory/arch/i386/compat_x86_64.S | 100 | ||||
-rw-r--r-- | purgatory/arch/i386/console-x86.c | 135 | ||||
-rw-r--r-- | purgatory/arch/i386/crashdump_backup.c | 51 | ||||
-rw-r--r-- | purgatory/arch/i386/entry32-16-debug.S | 200 | ||||
-rw-r--r-- | purgatory/arch/i386/entry32-16.S | 165 | ||||
-rw-r--r-- | purgatory/arch/i386/entry32.S | 110 | ||||
-rw-r--r-- | purgatory/arch/i386/include/arch/debug.h | 316 | ||||
-rw-r--r-- | purgatory/arch/i386/include/arch/io.h | 110 | ||||
-rw-r--r-- | purgatory/arch/i386/pic.c | 51 | ||||
-rw-r--r-- | purgatory/arch/i386/purgatory-x86.c | 57 | ||||
-rw-r--r-- | purgatory/arch/i386/purgatory-x86.h | 9 | ||||
-rw-r--r-- | purgatory/arch/i386/setup-x86.S | 75 | ||||
-rw-r--r-- | purgatory/arch/i386/stack.S | 39 | ||||
-rw-r--r-- | purgatory/arch/i386/vga.c | 152 |
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); +} |