/* * 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