summaryrefslogtreecommitdiffstats
path: root/kexec_test/kexec_test.S
diff options
context:
space:
mode:
Diffstat (limited to 'kexec_test/kexec_test.S')
-rw-r--r--kexec_test/kexec_test.S477
1 files changed, 477 insertions, 0 deletions
diff --git a/kexec_test/kexec_test.S b/kexec_test/kexec_test.S
new file mode 100644
index 0000000..ad081bc
--- /dev/null
+++ b/kexec_test/kexec_test.S
@@ -0,0 +1,477 @@
+/*
+ * 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 "config.h"
+
+ .equ PROT_CODE_SEG, pmcs - gdt
+ .equ REAL_CODE_SEG, rmcs - gdt
+ .equ PROT_DATA_SEG, pmds - gdt
+ .equ REAL_DATA_SEG, rmds - gdt
+ .equ CR0_PE, 1
+ /* Gas thinks the .equs for these are non-absolute so use a define */
+#define PROT_CODE_SEG 0x08
+#define REAL_CODE_SEG 0x18
+#undef i386
+
+ .text
+ .arch i386
+ .globl _start
+_start:
+ .code32
+ # Disable interrupts
+ cli
+
+ # Save the initial registers
+ movl %eax, orig_eax
+ movl %ebx, orig_ebx
+ movl %ecx, orig_ecx
+ movl %edx, orig_edx
+ movl %esi, orig_esi
+ movl %edi, orig_edi
+ movl %esp, orig_esp
+ movl %ebp, orig_ebp
+
+ # Setup a stack
+ movl $stack_end, %esp
+
+ # Display a message to say everything is working so far
+ pushl $s_hello
+ call print_string
+ addl $4, %esp
+
+ # Save the idt and gdt
+ sidt orig_idtp
+ sgdt orig_gdtp
+
+ # Display the initial register contents
+ call print_orig_regs
+
+ pushl $s_switching_descriptors
+ call print_string
+ addl $4, %esp
+
+ # Load descriptor pointers
+ lgdt gdtp
+ lidt idtp
+ # Reload the data segments
+ movl $PROT_DATA_SEG, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+ movl %eax, %fs
+ movl %eax, %gs
+
+ # Reload %cs
+ ljmp $PROT_CODE_SEG, $_start.1
+_start.1:
+
+ pushl $s_descriptors_changed
+ call print_string
+ addl $4, %esp
+
+ call setup_legacy_pic
+ pushl $s_legacy_pic_setup
+ call print_string
+ addl $4, %esp
+
+ call prot_to_real
+ .code16
+
+ callw test16
+
+ /* Return to 32bit mode */
+ data32 call real_to_prot
+ .code32
+ pushl $s_in_protected_mode
+ call print_string
+ addl $4, %esp
+
+ pushl $s_halting
+ call print_string
+ addl $4, %esp
+ jmp halt
+
+
+ /* Go from protected to real mode */
+prot_to_real:
+ .code32
+ /* Load the 16bit idt */
+ lidt idtp_real
+
+ popl %eax
+ subl $RELOC, %eax /* Adjust return address */
+ pushl %eax
+ subl $RELOC, %esp /* Adjust stack pointer */
+ ljmp $REAL_CODE_SEG, $1f - RELOC
+1:
+ .code16
+ /* Reload the segment registers to force a 16bit limit */
+ movw $REAL_DATA_SEG, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %ss
+ movw %ax, %fs
+ movw %ax, %gs
+
+ /* Clear the PE bit of CR0 */
+ movl %cr0, %eax
+ andl $0!CR0_PE, %eax
+ movl %eax, %cr0
+
+ /* make intersegment jmp to flush the processor pipeline
+ * and reload %cs:%eip (to clear upper 16 bits of %eip).
+ */
+ data32 ljmp $(RELOC)>>4,$2f- RELOC
+2:
+ /* we are in real mode now
+ * set up the real mode segment registers
+ */
+ movw %cs,%ax
+ movw %ax,%ds
+ movw %ax,%es
+ movw %ax,%ss
+ movw %ax,%fs
+ movw %ax,%gs
+ data32 ret
+
+real_to_prot:
+ .code16
+ pushl %ebx
+
+ /* Compute the address of gdtp */
+ movw %cs, %ax
+ shlw $4, %ax
+ movl $gdtp, %ebx
+ subw %ax, %bx
+
+ data32 lgdt %cs:(%bx)
+ movl %cr0, %eax
+ orl $CR0_PE, %eax
+ movl %eax, %cr0
+
+ /* flush prefetch queue and reload %cs:%eip */
+ data32 ljmp $PROT_CODE_SEG, $1f
+1:
+ .code32
+ /* reload other segment registers */
+ movl $PROT_DATA_SEG, %eax
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %ss
+ movl %eax, %fs
+ movl %eax, %gs
+
+ popl %ebx /* Restore %ebx */
+
+ addl $RELOC, %esp /* Fix up stack pointer */
+
+ popl %eax /* Fix up return address */
+ addl $RELOC, %eax
+ pushl %eax
+
+ lidt idtp /* Load a dummy idt */
+ ret
+
+
+halt:
+ .code32
+ hlt
+ jmp halt
+
+print_orig_regs:
+ .code32
+ # Display the initial register contents
+ pushl $s_eax
+ call print_string
+ pushl orig_eax
+ call print_hex
+ pushl $space
+ call print_string
+ addl $12, %esp
+
+ pushl $s_ebx
+ call print_string
+ pushl orig_ebx
+ call print_hex
+ pushl $space
+ call print_string
+ addl $12, %esp
+
+
+ pushl $s_ecx
+ call print_string
+ pushl orig_ecx
+ call print_hex
+ pushl $space
+ call print_string
+ addl $12, %esp
+
+
+ pushl $s_edx
+ call print_string
+ pushl orig_edx
+ call print_hex
+ pushl $crlf
+ call print_string
+ addl $12, %esp
+
+
+ pushl $s_esi
+ call print_string
+ pushl orig_esi
+ call print_hex
+ pushl $space
+ call print_string
+ addl $12, %esp
+
+ pushl $s_edi
+ call print_string
+ pushl orig_edi
+ call print_hex
+ pushl $space
+ call print_string
+ addl $12, %esp
+
+
+ pushl $s_esp
+ call print_string
+ pushl orig_esp
+ call print_hex
+ pushl $space
+ call print_string
+ addl $12, %esp
+
+
+ pushl $s_ebp
+ call print_string
+ pushl orig_ebp
+ call print_hex
+ pushl $crlf
+ call print_string
+ addl $12, %esp
+
+ # display the interrupt descritor table pointer
+ pushl $s_idtp
+ call print_string
+ movzwl orig_idtp, %eax
+ pushl %eax
+ call print_hex
+ pushl $space
+ call print_string
+ pushl orig_idt_base
+ call print_hex
+ pushl $crlf
+ call print_string
+ addl $20, %esp
+
+ # display the global descritor table pointer
+ pushl $s_gdtp
+ call print_string
+ movzwl orig_gdtp, %eax
+ pushl %eax
+ call print_hex
+ pushl $space
+ call print_string
+ pushl orig_gdt_base
+ call print_hex
+ pushl $crlf
+ call print_string
+ addl $20, %esp
+
+ ret
+
+
+print_string:
+ .code32
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %esi
+ movl 8(%ebp), %esi
+ xorl %eax, %eax
+print_string.1:
+ lodsb %ds:(%esi), %al
+ testb $0xff, %al
+ jz print_string.2
+ call print_char
+ jmp print_string.1
+print_string.2:
+ popl %esi
+ popl %ebp
+ ret
+
+
+print_hex:
+ .code32
+ pushl %ebp
+ movl %esp, %ebp
+ movb $32, %cl
+print_hex.1:
+ movl 8(%ebp), %eax
+ subb $4, %cl
+ shrl %cl, %eax
+ andb $0x0f, %al
+ cmpb $9, %al
+ ja print_hex.2
+ addb $'0', %al
+ jmp print_hex.3
+print_hex.2:
+ addb $'A' - 10, %al
+print_hex.3:
+ pushl %ecx
+ call print_char
+ popl %ecx
+ testb %cl, %cl
+ jnz print_hex.1
+
+ popl %ebp
+ ret
+
+print_char:
+ .code32
+ # The character to print is in al
+ call serial_print_char
+ retl
+
+
+#define TTYS0_BASE 0x3f8
+#define TTYS0_RBR (TTYS0_BASE + 0x00)
+#define TTYS0_TBR (TTYS0_BASE + 0x00)
+#define TTYS0_LSR (TTYS0_BASE + 0x05)
+serial_print_char:
+ .code32
+ # The character to print is in al
+ pushl %eax
+
+ # Wait until the serial port is ready to receive characters
+serial_print_char.1:
+ movl $TTYS0_LSR, %edx
+ inb %dx, %al
+ testb $0x20, %al
+ jz serial_print_char.1
+
+ # Output the character
+ movl $TTYS0_TBR, %edx
+ movb 0(%esp), %al
+ outb %al, %dx
+
+ # Wait until the serial port has transmitted the character
+serial_print_char.2:
+ movl $TTYS0_LSR, %edx
+ inb %dx, %al
+ testb $0x40, %al
+ jz serial_print_char.2
+
+ # Restore %eax
+ popl %eax
+ # Return to caller
+ ret
+
+ .code32
+
+idtp_real:
+ .word 0x400 # idt limit = 256
+ .word 0, 0
+idtp:
+ .word 0 # idt limit = 0
+ .word 0, 0 # idt base = 0L
+
+gdt:
+gdtp:
+ .word gdt_end - gdt - 1 # gdt limit
+ .long gdt # gdt base
+ .word 0 # dummy
+
+pmcs:
+ # the 32 bit protected mode code segment
+ .word 0xffff,0
+ .byte 0,0x9f,0xcf,0
+
+pmds:
+ # the 32 bit protected mode data segment
+ .word 0xffff,0
+ .byte 0,0x93,0xcf,0
+
+rmcs:
+ # the 16 bit real mode code segment
+ .word 0xffff,(RELOC&0xffff)
+ .byte (RELOC>>16),0x9b,0x00,(RELOC>>24)
+
+rmds:
+ # the 16 bit real mode data segment
+ .word 0xffff,(RELOC&0xffff)
+ .byte (RELOC>>16),0x93,0x00,(RELOC>>24)
+gdt_end:
+
+
+s_hello:
+ .ascii "kexec_test "
+ .ascii PACKAGE_VERSION
+ .asciz " starting...\r\n"
+s_switching_descriptors:
+ .asciz "Switching descriptors.\r\n"
+s_descriptors_changed:
+ .asciz "Descriptors changed.\r\n"
+s_legacy_pic_setup:
+ .asciz "Legacy pic setup.\r\n"
+s_in_protected_mode:
+ .asciz "In protected mode.\r\n"
+s_halting:
+ .asciz "Halting.\r\n"
+
+
+space: .asciz " "
+crlf: .asciz "\r\n"
+s_eax: .asciz "eax: "
+s_ebx: .asciz "ebx: "
+s_ecx: .asciz "ecx: "
+s_edx: .asciz "edx: "
+s_esi: .asciz "esi: "
+s_edi: .asciz "edi: "
+s_esp: .asciz "esp: "
+s_ebp: .asciz "ebp: "
+
+
+s_idtp: .asciz "idt: "
+s_gdtp: .asciz "gdt: "
+
+#include "x86-setup-legacy-pic.S"
+
+ .bss
+ .balign 4096
+stack:
+ .skip 4096
+stack_end:
+
+ .bss
+ .balign 4
+orig_eax: .long 0
+orig_ebx: .long 0
+orig_ecx: .long 0
+orig_edx: .long 0
+orig_esi: .long 0
+orig_edi: .long 0
+orig_esp: .long 0
+orig_ebp: .long 0
+
+ .balign 4
+orig_idtp: .short 0
+orig_idt_base: .long 0
+orig_gdtp: .short 0
+orig_gdt_base: .long 0
+