📄 startup.s
字号:
/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2005 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. *//* * Note: These functions defined in this file may be called from C. * Be careful of that you must not modify some registers. Quote * from gcc-2.95.2/gcc/config/i386/i386.h: 1 for registers not available across function calls. These must include the FIXED_REGISTERS and also any registers that can be used without being saved. The latter must include the registers where values are returned and the register where structure-value addresses are passed. Aside from that, you can include as many other registers as you like. ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg{ 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } *//* * Note: GRUB is compiled with the options -mrtd and -mregparm=3. * So the first three arguments are passed in %eax, %edx, and %ecx, * respectively, and if a function has a fixed number of arguments * and the number if greater than three, the function must return * with "ret $N" where N is ((the number of arguments) - 3) * 4. */#include <config.h>#include <grub/symbol.h>#include <grub/boot.h>#include <grub/machine/boot.h>#include <grub/machine/memory.h>#include <grub/machine/console.h>#include <grub/machine/linux.h>#include <grub/machine/kernel.h>#include <grub/machine/multiboot.h> #define ABS(x) ((x) - EXT_C(start) + GRUB_BOOT_MACHINE_KERNEL_ADDR + 0x200) .file "startup.S" .text /* Tell GAS to generate 16-bit instructions so that this code works in real mode. */ .code16 .globl start, _startstart:_start: /* * Guarantee that "main" is loaded at 0x0:0x8200. */ ljmp $0, $ABS(codestart) /* * Compatibility version number * * These MUST be at byte offset 6 and 7 of the executable * DO NOT MOVE !!! */ . = EXT_C(start) + 0x6 .byte GRUB_BOOT_VERSION_MAJOR, GRUB_BOOT_VERSION_MINOR /* * This is a special data area 8 bytes from the beginning. */ . = EXT_C(start) + 0x8VARIABLE(grub_total_module_size) .long 0VARIABLE(grub_kernel_image_size) .long 0VARIABLE(grub_compressed_size) .long 0VARIABLE(grub_install_dos_part) .long 0xFFFFFFFFVARIABLE(grub_install_bsd_part) .long 0xFFFFFFFFVARIABLE(grub_prefix) .string "/boot/grub" /* * Leave some breathing room for the prefix. */ . = EXT_C(start) + 0x50/* * Support for booting GRUB from a Multiboot boot loader (e.g. GRUB itself). * This uses the a.out kludge to load raw binary to the area starting at 1MB, * and relocates itself after loaded. */multiboot_header: /* magic */ .long 0x1BADB002 /* flags */ .long (1 << 16) /* checksum */ .long -0x1BADB002 - (1 << 16) /* header addr */ .long multiboot_header - _start + 0x100000 + 0x200 /* load addr */ .long 0x100000 /* load end addr */ .long 0 /* bss end addr */ .long 0 /* entry addr */ .long multiboot_entry - _start + 0x100000 + 0x200 multiboot_entry: .code32 /* obtain the boot device */ movl 12(%ebx), %edx /* relocate the code */ movl $(GRUB_KERNEL_MACHINE_RAW_SIZE + 0x200), %ecx addl EXT_C(grub_compressed_size) - _start + 0x100000 + 0x200, %ecx movl $0x100000, %esi movl $GRUB_BOOT_MACHINE_KERNEL_ADDR, %edi cld rep movsb /* jump to the real address */ movl $multiboot_trampoline, %eax jmp *%eax multiboot_trampoline: /* fill the boot information */ movl %edx, %eax shrl $8, %eax xorl %ebx, %ebx cmpb $0xFF, %ah je 1f movb %ah, %bl movl %ebx, EXT_C(grub_install_dos_part)1: cmpb $0xFF, %al je 2f movb %al, %bl movl %ebx, EXT_C(grub_install_bsd_part)2: shrl $24, %edx /* enter the usual booting */ call prot_to_real .code16 /* the real mode code continues... */codestart: cli /* we're not safe here! */ /* set up %ds, %ss, and %es */ xorw %ax, %ax movw %ax, %ds movw %ax, %ss movw %ax, %es /* set up the real mode/BIOS stack */ movl $GRUB_MEMORY_MACHINE_REAL_STACK, %ebp movl %ebp, %esp sti /* we're safe again */ /* save boot drive reference */ ADDR32 movb %dl, EXT_C(grub_boot_drive) /* reset disk system (%ah = 0) */ int $0x13 /* transition to protected mode */ DATA32 call real_to_prot /* The ".code32" directive takes GAS out of 16-bit mode. */ .code32 incl %eax call EXT_C(grub_gate_a20) /* decompress the compressed part and put the result at 1MB */ movl $0x100000, %esi movl $(START_SYMBOL + GRUB_KERNEL_MACHINE_RAW_SIZE), %edi pushl %esi pushl EXT_C(grub_compressed_size) pushl %edi call lzo1x_decompress addl $12, %esp /* copy back the decompressed part */ movl %eax, %ecx cld rep movsb /* copy modules before cleaning out the bss */ movl EXT_C(grub_total_module_size), %ecx movl EXT_C(grub_kernel_image_size), %esi addl %ecx, %esi addl $START_SYMBOL, %esi decl %esi movl $END_SYMBOL, %edi addl %ecx, %edi decl %edi std rep movsb /* clean out the bss */ movl $BSS_START_SYMBOL, %edi /* compute the bss length */ movl $END_SYMBOL, %ecx subl %edi, %ecx /* clean out */ xorl %eax, %eax cld rep stosb /* * Call the start of main body of C code. */ call EXT_C(grub_main)/* * This is the area for all of the special variables. */ .p2align 2 /* force 4-byte alignment */protstack: .long GRUB_MEMORY_MACHINE_PROT_STACKVARIABLE(grub_boot_drive) .long 0VARIABLE(grub_start_addr) .long START_SYMBOLVARIABLE(grub_end_addr) .long END_SYMBOL VARIABLE(grub_apm_bios_info) .word 0 /* version */ .word 0 /* cseg */ .long 0 /* offset */ .word 0 /* cseg_16 */ .word 0 /* dseg_16 */ .word 0 /* cseg_len */ .word 0 /* cseg_16_len */ .word 0 /* dseg_16_len */ /* * This is the Global Descriptor Table * * An entry, a "Segment Descriptor", looks like this: * * 31 24 19 16 7 0 * ------------------------------------------------------------ * | | |B| |A| | | |1|0|E|W|A| | * | BASE 31..24 |G|/|L|V| LIMIT |P|DPL| TYPE | BASE 23:16 | 4 * | | |D| |L| 19..16| | |1|1|C|R|A| | * ------------------------------------------------------------ * | | | * | BASE 15..0 | LIMIT 15..0 | 0 * | | | * ------------------------------------------------------------ * * Note the ordering of the data items is reversed from the above * description. */ .p2align 2 /* force 4-byte alignment */gdt: .word 0, 0 .byte 0, 0, 0, 0 /* -- code segment -- * base = 0x00000000, limit = 0xFFFFF (4 KiB Granularity), present * type = 32bit code execute/read, DPL = 0 */ .word 0xFFFF, 0 .byte 0, 0x9A, 0xCF, 0 /* -- data segment -- * base = 0x00000000, limit 0xFFFFF (4 KiB Granularity), present * type = 32 bit data read/write, DPL = 0 */ .word 0xFFFF, 0 .byte 0, 0x92, 0xCF, 0 /* -- 16 bit real mode CS -- * base = 0x00000000, limit 0x0FFFF (1 B Granularity), present * type = 16 bit code execute/read only/conforming, DPL = 0 */ .word 0xFFFF, 0 .byte 0, 0x9E, 0, 0 /* -- 16 bit real mode DS -- * base = 0x00000000, limit 0x0FFFF (1 B Granularity), present * type = 16 bit data read/write, DPL = 0 */ .word 0xFFFF, 0 .byte 0, 0x92, 0, 0/* this is the GDT descriptor */gdtdesc: .word 0x27 /* limit */ .long gdt /* addr */ /* * These next two routines, "real_to_prot" and "prot_to_real" are structured * in a very specific way. Be very careful when changing them. * * NOTE: Use of either one messes up %eax and %ebp. */real_to_prot: .code16 cli /* load the GDT register */ DATA32 ADDR32 lgdt gdtdesc /* turn on protected mode */ movl %cr0, %eax orl $GRUB_MEMORY_MACHINE_CR0_PE_ON, %eax movl %eax, %cr0 /* jump to relocation, flush prefetch queue, and reload %cs */ DATA32 ljmp $GRUB_MEMORY_MACHINE_PROT_MODE_CSEG, $protcseg .code32protcseg: /* reload other segment registers */ movw $GRUB_MEMORY_MACHINE_PROT_MODE_DSEG, %ax movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs movw %ax, %ss /* put the return address in a known safe location */ movl (%esp), %eax movl %eax, GRUB_MEMORY_MACHINE_REAL_STACK /* get protected mode stack */ movl protstack, %eax movl %eax, %esp movl %eax, %ebp /* get return address onto the right stack */ movl GRUB_MEMORY_MACHINE_REAL_STACK, %eax movl %eax, (%esp) /* zero %eax */ xorl %eax, %eax /* return on the old (or initialized) stack! */ retprot_to_real: /* just in case, set GDT */ lgdt gdtdesc /* save the protected mode stack */ movl %esp, %eax movl %eax, protstack /* get the return address */ movl (%esp), %eax movl %eax, GRUB_MEMORY_MACHINE_REAL_STACK /* set up new stack */ movl $GRUB_MEMORY_MACHINE_REAL_STACK, %eax movl %eax, %esp movl %eax, %ebp /* set up segment limits */ movw $GRUB_MEMORY_MACHINE_PSEUDO_REAL_DSEG, %ax movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs movw %ax, %ss /* this might be an extra step */ /* jump to a 16 bit segment */ ljmp $GRUB_MEMORY_MACHINE_PSEUDO_REAL_CSEG, $tmpcsegtmpcseg: .code16 /* clear the PE bit of CR0 */ movl %cr0, %eax andl $(~GRUB_MEMORY_MACHINE_CR0_PE_ON), %eax movl %eax, %cr0 /* flush prefetch queue, reload %cs */ DATA32 ljmp $0, $realcsegrealcseg: /* we are in real mode now * set up the real mode segment registers : DS, SS, ES */ /* zero %eax */ xorl %eax, %eax movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs movw %ax, %ss /* restore interrupts */ sti /* return on new stack! */ DATA32 ret .code32/* * grub_gate_a20(int on) * * Gate address-line 20 for high memory. * * This routine is probably overconservative in what it does, but so what? * * It also eats any keystrokes in the keyboard buffer. :-( */FUNCTION(grub_gate_a20) movl %eax, %edxgate_a20_test_current_state: /* first of all, test if already in a good state */ call gate_a20_check_state cmpb %al, %dl jnz gate_a20_try_bios retgate_a20_try_bios: /* second, try a BIOS call */ pushl %ebp call prot_to_real .code16 movw $0x2400, %ax testb %dl, %dl jz 1f incw %ax1: int $0x15 DATA32 call real_to_prot .code32 popl %ebp call gate_a20_check_state cmpb %al, %dl jnz gate_a20_try_keyboard_controller ret gate_a20_flush_keyboard_buffer: inb $0x64 andb $0x02, %al jnz gate_a20_flush_keyboard_buffer2: inb $0x64 andb $0x01, %al jz 3f inb $0x60 jmp 2b3: ret gate_a20_try_keyboard_controller: /* third, try the keyboard controller */ call gate_a20_flush_keyboard_buffer movb $0xd1, %al outb $0x644: inb $0x64 andb $0x02, %al jnz 4b movb $0xdd, %al testb %dl, %dl jz 5f orb $0x02, %al5: outb $0x60 call gate_a20_flush_keyboard_buffer /* output a dummy command (USB keyboard hack) */ movb $0xff, %al outb $0x64 call gate_a20_flush_keyboard_buffer call gate_a20_check_state cmpb %al, %dl jnz gate_a20_try_system_control_port_a retgate_a20_try_system_control_port_a: /* fourth, try the system control port A */ inb $0x92 andb $(~0x03), %al testb %dl, %dl jz 6f orb $0x02, %al6: outb $0x92 /* When turning off Gate A20, do not check the state strictly, because a failure is not fatal usually, and Gate A20 is always on some modern machines. */ testb %dl, %dl jz 7f call gate_a20_check_state cmpb %al, %dl /* everything failed, so restart from the beginning */ jnz gate_a20_try_bios7: ret gate_a20_check_state: /* iterate the checking for a while */ movl $100, %ecx1: call 3f cmpb %al, %dl jz 2f loop 1b2: ret3: pushl %ebx pushl %ecx xorl %eax, %eax /* compare the byte at 0x8000 with that at 0x108000 */ movl $GRUB_BOOT_MACHINE_KERNEL_ADDR, %ebx pushl %ebx /* save the original byte in CL */ movb (%ebx), %cl /* store the value at 0x108000 in AL */ addl $0x100000, %ebx movb (%ebx), %al /* try to set one less value at 0x8000 */ popl %ebx movb %al, %ch decb %ch movb %ch, (%ebx) /* serialize */ outb %al, $0x80 outb %al, $0x80 /* obtain the value at 0x108000 in CH */ pushl %ebx addl $0x100000, %ebx movb (%ebx), %ch /* this result is 1 if A20 is on or 0 if it is off */ subb %ch, %al xorb $1, %al /* restore the original */ popl %ebx movb %cl, (%ebx) popl %ecx popl %ebx ret#include "lzo1x.S"/* * This call is special... it never returns... in fact it should simply * hang at this point! */FUNCTION(grub_stop) call prot_to_real /* * This next part is sort of evil. It takes advantage of the * byte ordering on the x86 to work in either 16-bit or 32-bit * mode, so think about it before changing it. */FUNCTION(grub_hard_stop) hlt jmp EXT_C(grub_hard_stop)/* * grub_stop_floppy() * * Stop the floppy drive from spinning, so that other software is * jumped to with a known state. */FUNCTION(grub_stop_floppy) movw $0x3F2, %dx xorb %al, %al outb %al, %dx ret/* * grub_reboot() * * Reboot the system. At the moment, rely on BIOS. */FUNCTION(grub_reboot) call prot_to_real .code16 /* cold boot */ movw $0x0472, %di movw %ax, (%di) ljmp $0xFFFF, $0x0000 .code32 /* * grub_halt(int no_apm) * * Halt the system, using APM if possible. If NO_APM is true, don't use * APM even if it is available. */FUNCTION(grub_halt) /* see if zero */ testl %eax, %eax jnz EXT_C(grub_stop) call prot_to_real .code16 /* detect APM */ movw $0x5300, %ax xorw %bx, %bx int $0x15 jc EXT_C(grub_hard_stop) /* don't check %bx for buggy BIOSes... */ /* disconnect APM first */ movw $0x5304, %ax xorw %bx, %bx int $0x15 /* connect APM */ movw $0x5301, %ax xorw %bx, %bx int $0x15 jc EXT_C(grub_hard_stop) /* set APM protocol level - 1.1 or bust. (this covers APM 1.2 also) */ movw $0x530E, %ax xorw %bx, %bx movw $0x0101, %cx int $0x15 jc EXT_C(grub_hard_stop) /* set the power state to off */ movw $0x5307, %ax movw $1, %bx movw $3, %cx int $0x15 /* shouldn't reach here */ jmp EXT_C(grub_hard_stop) .code32 /* * void grub_chainloader_real_boot (int drive, void *part_addr) * * This starts another boot loader. */FUNCTION(grub_chainloader_real_boot) pushl %edx pushl %eax call EXT_C(grub_dl_unload_all) /* set up to pass boot drive */ popl %edx /* ESI must point to a partition table entry */ popl %esi /* Turn off Gate A20 */ xorl %eax, %eax call EXT_C(grub_gate_a20) call prot_to_real .code16 ljmp $0, $GRUB_MEMORY_MACHINE_BOOT_LOADER_ADDR .code32/* * void grub_linux_boot_zimage (void) */VARIABLE(grub_linux_prot_size) .long 0
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -