📄 asm.s
字号:
/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2004 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 } */#define ASM_FILE#include "shared.h"#ifdef STAGE1_5# define ABS(x) ((x) - EXT_C(main) + 0x2200)#else# define ABS(x) ((x) - EXT_C(main) + 0x8200)#endif .file "asm.S" .text /* Tell GAS to generate 16-bit instructions so that this code works in real mode. */ .code16#ifndef STAGE1_5 /* * In stage2, do not link start.S with the rest of the source * files directly, so define the start symbols here just to * force ld quiet. These are not referred anyway. */ .globl start, _startstart:_start:#endif /* ! STAGE1_5 */ ENTRY(main) /* * Guarantee that "main" is loaded at 0x0:0x8200 in stage2 and * at 0x0:0x2200 in stage1.5. */ ljmp $0, $ABS(codestart) /* * Compatibility version number * * These MUST be at byte offset 6 and 7 of the executable * DO NOT MOVE !!! */ . = EXT_C(main) + 0x6 .byte COMPAT_VERSION_MAJOR, COMPAT_VERSION_MINOR /* * This is a special data area 8 bytes from the beginning. */ . = EXT_C(main) + 0x8VARIABLE(install_partition) .long 0xFFFFFFVARIABLE(saved_entryno) .long 0VARIABLE(stage2_id) .byte STAGE2_IDVARIABLE(force_lba) .byte 0VARIABLE(version_string) .string VERSIONVARIABLE(config_file)#ifndef STAGE1_5 .string "/boot/grub/menu.lst"#else /* STAGE1_5 */ .long 0xffffffff .string "/boot/grub/stage2"#endif /* STAGE1_5 */ /* * Leave some breathing room for the config file name. */ . = EXT_C(main) + 0x70/* 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#ifndef SUPPORT_DISKLESS /* * Save the sector number of the second sector (i.e. this sector) * in INSTALL_SECOND_SECTOR. See also "stage2/start.S". */ ADDR32 movl %ebp, EXT_C(install_second_sector)#endif /* set up the real mode/BIOS stack */ movl $STACKOFF, %ebp movl %ebp, %esp sti /* we're safe again */#ifndef SUPPORT_DISKLESS /* save boot drive reference */ ADDR32 movb %dl, EXT_C(boot_drive) /* reset disk system (%ah = 0) */ int $0x13#endif /* transition to protected mode */ DATA32 call EXT_C(real_to_prot) /* The ".code32" directive takes GAS out of 16-bit mode. */ .code32 /* clean out the bss */ /* set %edi to the bss starting address */#if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL) movl $__bss_start, %edi#elif defined(HAVE_USCORE_EDATA_SYMBOL) movl $_edata, %edi#elif defined(HAVE_EDATA_SYMBOL) movl $edata, %edi#endif /* set %ecx to the bss end */ #if defined(HAVE_END_SYMBOL) movl $end, %ecx#elif defined(HAVE_USCORE_END_SYMBOL) movl $_end, %ecx#endif /* compute the bss length */ subl %edi, %ecx /* zero %al */ xorb %al, %al /* set the direction */ cld /* clean out */ rep stosb /* * Call the start of main body of C code, which does some * of it's own initialization before transferring to "cmain". */ call EXT_C(init_bios_info)/* * This call is special... it never returns... in fact it should simply * hang at this point! */ENTRY(stop) call EXT_C(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. */ENTRY(hard_stop) hlt jmp EXT_C(hard_stop)#ifndef STAGE1_5/* * stop_floppy() * * Stops the floppy drive from spinning, so that other software is * jumped to with a known state. */ENTRY(stop_floppy) pusha call EXT_C(prot_to_real) .code16 xorb %dl, %dl int $0x13 DATA32 call EXT_C(real_to_prot) .code32 popa ret/* * grub_reboot() * * Reboot the system. At the moment, rely on BIOS. */ENTRY(grub_reboot) call EXT_C(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. */ENTRY(grub_halt) /* get the argument */ movl 4(%esp), %eax /* see if zero */ testl %eax, %eax jnz EXT_C(stop) call EXT_C(prot_to_real) .code16 /* detect APM */ movw $0x5300, %ax xorw %bx, %bx int $0x15 jc EXT_C(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(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(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(hard_stop) .code32 /* * track_int13(int drive) * * Track the int13 handler to probe I/O address space. */ENTRY(track_int13) pushl %ebp movl %esp, %ebp pushl %ebx pushl %edi /* copy the original int13 handler segment:offset */ movl $0x4c, %edi movl (%edi), %eax movl %eax, track_int13_addr /* replace the int1 handler */ movl $0x4, %edi pushl (%edi) movl $ABS(int1_handler), %eax movl %eax, (%edi) /* read the MBR to call int13 successfully */ movb 8(%ebp), %dl call EXT_C(prot_to_real) .code16 movw $SCRATCHSEG, %ax movw %ax, %es xorw %bx, %bx movw $1, %cx xorb %dh, %dh /* save FLAGS on the stack to emulate int13 */ pushfw /* set the TF flag */ /* FIXME: this can be simplified not to use AX */ pushfw popw %ax orw $0x100, %ax pushw %ax popfw movw $0x0201, %ax .byte 0x9a /* lcall */track_int13_addr: .word 0 /* offset */ .word 0 /* segment */ /* TF is cleared here automatically */ DATA32 call EXT_C(real_to_prot) .code32 /* restore the int1 handler */ movl $0x4, %edi popl (%edi) popl %edi popl %ebx popl %ebp ret/* * Check if the next instruction is I/O, and if this is true, add the * port into the io map. * * Note: Probably this will make the execution of int13 very slow. * * Note2: In this implementation, all we can know is I/O-mapped I/O. It * is impossible to detect memory-mapped I/O. */int1_handler: .code16 pushw %bp movw %sp, %bp pushw %ds pushw %ax pushw %si pushw %dx /* IP */ movw 2(%bp), %si /* CS */ movw 4(%bp), %ax movw %ax, %ds /* examine the next instruction */1: lodsb (%si), %al /* skip this code if it is a prefix */ cmpb $0x2E, %al je 1b cmpb $0x36, %al je 1b cmpb $0x3E, %al je 1b cmpb $0x26, %al je 1b cmpb $0x64, %al jl 2f cmpb $0x67, %al jle 1b2: cmpb $0xF0, %al jl 3f cmpb $0xF3, %al jle 1b 3: /* check if this code is out* or in* */ /* ins? or outs? */ cmpb $0x6C, %al jl 4f cmpb $0x6F, %al jle 5f4: /* in? or out? (register operand version) */ cmpb $0xEC, %al jl 6f cmpb $0xEF, %al jle 5f 6: /* in? or out? (immediate operand version) */ cmpb $0xE4, %al jl 8f cmpb $0xE7, %al jg 8f7: /* immediate has a port */ lodsb (%si), %al movzbw %al, %dx 5: /* %dx has a port */ /* set %ds to zero */ xorw %ax, %ax movw %ax, %ds /* set %si to the io map */ movw $ABS(EXT_C(io_map)), %si 9: /* check if the io map already has the port */ lodsw (%si), %ax /* check if this is the end */ testw %ax, %ax jz 1f /* check if this matches the port */ cmpw %ax, %dx jne 9b /* if so, leave from this handler */ jmp 8f 1: /* check for the buffer overrun */ cmpw $(ABS(EXT_C(io_map)) + (IO_MAP_SIZE + 1) * 2), %si je 8f /* add the port into the io map */ movw %dx, -2(%si)8: /* restore registers */ popw %dx popw %si popw %ax popw %ds popw %bp iret .code32ENTRY(io_map) .space (IO_MAP_SIZE + 1) * 2 /* * set_int15_handler(void) * * Set up int15_handler. */ENTRY(set_int15_handler) pushl %edi /* save the original int15 handler */ movl $0x54, %edi movw (%edi), %ax movw %ax, ABS(int15_offset) movw 2(%edi), %ax movw %ax, ABS(int15_segment) /* save the new int15 handler */ movw $ABS(int15_handler), %ax movw %ax, (%edi) xorw %ax, %ax movw %ax, 2(%edi) popl %edi ret/* * unset_int15_handler(void) * * Restore the original int15 handler */ENTRY(unset_int15_handler) pushl %edi /* check if int15_handler is set */ movl $0x54, %edi movw $ABS(int15_handler), %ax cmpw %ax, (%edi) jne 1f xorw %ax, %ax cmpw %ax, 2(%edi) jne 1f /* restore the original */ movw ABS(int15_offset), %ax movw %ax, (%edi) movw ABS(int15_segment), %ax movw %ax, 2(%edi)1: popl %edi ret/* * Translate a key code to another. * * Note: This implementation cannot handle more than one length * scancodes (such as Right Ctrl). */ .code16int15_handler: /* if non-carrier, ignore it */ jnc 1f /* check if AH=4F */ cmpb $0x4F, %ah jne 1f /* E0 and E1 are special */ cmpb $0xE1, %al je 4f cmpb $0xE0, %al /* this flag is actually the machine code (je or jmp) */int15_skip_flag: je 4f pushw %bp movw %sp, %bp pushw %bx pushw %dx pushw %ds pushw %si /* save bits 0-6 of %al in %dl */ movw %ax, %dx andb $0x7f, %dl /* save the highest bit in %bl */ movb %al, %bl xorb %dl, %bl /* set %ds to 0 */ xorw %ax, %ax movw %ax, %ds /* set %si to the key map */ movw $ABS(EXT_C(bios_key_map)), %si /* find the key code from the key map */2: lodsw /* check if this is the end */ testw %ax, %ax jz 3f /* check if this matches the key code */ cmpb %al, %dl jne 2b /* if so, perform the mapping */ movb %ah, %dl3: /* restore %ax */ movw %dx, %ax orb %bl, %al /* make sure that CF is set */ orw $1, 6(%bp) /* restore other registers */ popw %si popw %ds popw %dx popw %bx popw %bp iret 4: /* tricky: jmp (0x74) <-> je (0xeb) */ xorb $(0x74 ^ 0xeb), ABS(int15_skip_flag)1: /* just cascade to the original */ /* ljmp */ .byte 0xeaint15_offset: .word 0int15_segment: .word 0 .code32 .align 4 ENTRY(bios_key_map) .space (KEY_MAP_SIZE + 1) * 2 /* * set_int13_handler(map) * * Copy MAP to the drive map and set up int13_handler. */ENTRY(set_int13_handler) pushl %ebp movl %esp, %ebp pushl %edi pushl %esi /* copy MAP to the drive map */ movl $(DRIVE_MAP_SIZE * 2), %ecx movl $ABS(drive_map), %edi movl 8(%ebp), %esi cld rep movsb /* save the original int13 handler */ movl $0x4c, %edi movw (%edi), %ax movw %ax, ABS(int13_offset) movw 2(%edi), %ax movw %ax, ABS(int13_segment) /* decrease the lower memory size and set it to the BIOS memory */ movl $0x413, %edi decw (%edi) xorl %eax, %eax movw (%edi), %ax /* compute the segment */ shll $6, %eax /* save the new int13 handler */ movl $0x4c, %edi movw %ax, 2(%edi) xorw %cx, %cx movw %cx, (%edi) /* copy int13_handler to the reserved area */ shll $4, %eax movl %eax, %edi movl $ABS(int13_handler), %esi movl $(int13_handler_end - int13_handler), %ecx rep movsb popl %esi popl %edi popl %ebp ret /* * Map a drive to another drive. */ .code16 int13_handler: pushw %ax pushw %bp movw %sp, %bp pushw %si /* set %si to the drive map */ movw $(drive_map - int13_handler), %si /* find the drive number from the drive map */ cld1: lodsw %cs:(%si), %ax /* check if this is the end */ testw %ax, %ax jz 2f /* check if this matches the drive number */ cmpb %al, %dl jne 1b /* if so, perform the mapping */ movb %ah, %dl2: /* restore %si */ popw %si /* save %ax in the stack */ pushw %ax /* simulate the interrupt call */ pushw 8(%bp) /* set %ax and %bp to the original values */ movw 2(%bp), %ax movw (%bp), %bp /* lcall */ .byte 0x9aint13_offset: .word 0int13_segment: .word 0 /* save flags */ pushf /* restore %bp */ movw %sp, %bp /* save %ax */ pushw %ax /* set the flags in the stack to the value returned by int13 */ movw (%bp), %ax movw %ax, 0xc(%bp) /* check if should map the drive number */ movw 6(%bp), %ax cmpw $0x8, %ax jne 3f cmpw $0x15, %ax jne 3f /* check if the mapping was performed */ movw 2(%bp), %ax testw %ax, %ax jz 3f /* perform the mapping */ movb %al, %dl3: popw %ax movw 4(%bp), %bp addw $8, %sp iret .align 4drive_map: .space (DRIVE_MAP_SIZE + 1) * 2int13_handler_end: .code32 /* * chain_stage1(segment, offset, part_table_addr) * * This starts another stage1 loader, at segment:offset. */ENTRY(chain_stage1) /* no need to save anything, just use %esp */ /* store %ESI, presuming %ES is 0 */ movl 0xc(%esp), %esi /* store new offset */ movl 0x8(%esp), %eax movl %eax, offset /* store new segment */ movw 0x4(%esp), %ax movw %ax, segment /* set up to pass boot drive */ movb EXT_C(boot_drive), %dl call EXT_C(prot_to_real) .code16#ifdef ABSOLUTE_WITHOUT_ASTERISK DATA32 ADDR32 ljmp (offset)#else DATA32 ADDR32 ljmp *(offset)#endif .code32#endif /* STAGE1_5 */#ifdef STAGE1_5/* * chain_stage2(segment, offset, second_sector) * * This starts another stage2 loader, at segment:offset. It presumes * that the other one starts with this same "asm.S" file, and passes * parameters by writing the embedded install variables. */ENTRY(chain_stage2) /* no need to save anything, just use %esp */ /* store new offset */ movl 0x8(%esp), %eax movl %eax, offset movl %eax, %ebx
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -