📄 setup.s
字号:
/* * setup.S Copyright (C) 1991, 1992 Linus Torvalds * * setup.s is responsible for getting the system data from the BIOS, * and putting them into the appropriate places in system memory. * both setup.s and system has been loaded by the bootblock. * * This code asks the bios for memory/disk/other parameters, and * puts them in a "safe" place: 0x90000-0x901FF, ie where the * boot-block used to be. It is then up to the protected mode * system to read them from there before the area is overwritten * for buffer-blocks. * * Move PS/2 aux init code to psaux.c * (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92 * * some changes and additional features by Christoph Niemann, * March 1993/June 1994 (Christoph.Niemann@linux.org) * * add APM BIOS checking by Stephen Rothwell, May 1994 * (sfr@canb.auug.org.au) * * High load stuff, initrd support and position independency * by Hans Lermen & Werner Almesberger, February 1996 * <lermen@elserv.ffm.fgan.de>, <almesber@lrc.epfl.ch> * * Video handling moved to video.S by Martin Mares, March 1996 * <mj@k332.feld.cvut.cz> * * Extended memory detection scheme retwiddled by orc@pell.chi.il.us (david * parsons) to avoid loadlin confusion, July 1997 * * Transcribed from Intel (as86) -> AT&T (gas) by Chris Noe, May 1999. * <stiker@northlink.com> * * Fix to work around buggy BIOSes which don't use carry bit correctly * and/or report extended memory in CX/DX for e801h memory size detection * call. As a result the kernel got wrong figures. The int15/e801h docs * from Ralf Brown interrupt list seem to indicate AX/BX should be used * anyway. So to avoid breaking many machines (presumably there was a reason * to orginally use CX/DX instead of AX/BX), we do a kludge to see * if CX/DX have been changed in the e801 call and if so use AX/BX . * Michael Miller, April 2001 <michaelm@mjmm.org> * * Added long mode checking and SSE force. March 2003, Andi Kleen. */#include <linux/config.h>#include <asm/segment.h>#include <linux/version.h>#include <linux/compile.h>#include <asm/boot.h>#include <asm/e820.h>#include <asm/page.h>/* Signature words to ensure LILO loaded us right */#define SIG1 0xAA55#define SIG2 0x5A5AINITSEG = DEF_INITSEG # 0x9000, we move boot here, out of the waySYSSEG = DEF_SYSSEG # 0x1000, system loaded at 0x10000 (65536).SETUPSEG = DEF_SETUPSEG # 0x9020, this is the current segment # ... and the former contents of CSDELTA_INITSEG = SETUPSEG - INITSEG # 0x0020.code16.globl begtext, begdata, begbss, endtext, enddata, endbss.textbegtext:.databegdata:.bssbegbss:.textstart: jmp trampoline# This is the setup header, and it must start at %cs:2 (old 0x9020:2) .ascii "HdrS" # header signature .word 0x0204 # header version number (>= 0x0105) # or else old loadlin-1.5 will fail)realmode_swtch: .word 0, 0 # default_switch, SETUPSEGstart_sys_seg: .word SYSSEG .word kernel_version # pointing to kernel version string # above section of header is compatible # with loadlin-1.5 (header v1.5). Don't # change it.type_of_loader: .byte 0 # = 0, old one (LILO, Loadlin, # Bootlin, SYSLX, bootsect...) # See Documentation/i386/boot.txt for # assigned ids # flags, unused bits must be zero (RFU) bit within loadflagsloadflags:LOADED_HIGH = 1 # If set, the kernel is loaded highCAN_USE_HEAP = 0x80 # If set, the loader also has set # heap_end_ptr to tell how much # space behind setup.S can be used for # heap purposes. # Only the loader knows what is free#ifndef __BIG_KERNEL__ .byte 0#else .byte LOADED_HIGH#endifsetup_move_size: .word 0x8000 # size to move, when setup is not # loaded at 0x90000. We will move setup # to 0x90000 then just before jumping # into the kernel. However, only the # loader knows how much data behind # us also needs to be loaded.code32_start: # here loaders can put a different # start address for 32-bit code.#ifndef __BIG_KERNEL__ .long 0x1000 # 0x1000 = default for zImage#else .long 0x100000 # 0x100000 = default for big kernel#endiframdisk_image: .long 0 # address of loaded ramdisk image # Here the loader puts the 32-bit # address where it loaded the image. # This only will be read by the kernel.ramdisk_size: .long 0 # its size in bytesbootsect_kludge: .long 0 # obsoleteheap_end_ptr: .word modelist+1024 # (Header version 0x0201 or later) # space from here (exclusive) down to # end of setup code can be used by setup # for local heap purposes.pad1: .word 0cmd_line_ptr: .long 0 # (Header version 0x0202 or later) # If nonzero, a 32-bit pointer # to the kernel command line. # The command line should be # located between the start of # setup and the end of low # memory (0xa0000), or it may # get overwritten before it # gets read. If this field is # used, there is no longer # anything magical about the # 0x90000 segment; the setup # can be located anywhere in # low memory 0x10000 or higher.ramdisk_max: .long 0xffffffff trampoline: call start_of_setup .align 16 # The offset at this point is 0x240 .space (0xeff-0x240+1) # E820 & EDD space (ending at 0xeff)# End of setup header #####################################################start_of_setup:# Bootlin depends on this being done early movw $0x01500, %ax movb $0x81, %dl int $0x13#ifdef SAFE_RESET_DISK_CONTROLLER# Reset the disk controller. movw $0x0000, %ax movb $0x80, %dl int $0x13#endif# Set %ds = %cs, we know that SETUPSEG = %cs at this point movw %cs, %ax # aka SETUPSEG movw %ax, %ds# Check signature at end of setup cmpw $SIG1, setup_sig1 jne bad_sig cmpw $SIG2, setup_sig2 jne bad_sig jmp good_sig1# Routine to print asciiz string at ds:siprtstr: lodsb andb %al, %al jz fin call prtchr jmp prtstrfin: ret# Space printingprtsp2: call prtspc # Print double spaceprtspc: movb $0x20, %al # Print single space (note: fall-thru)prtchr: pushw %ax pushw %cx movw $0007,%bx movw $0x01, %cx movb $0x0e, %ah int $0x10 popw %cx popw %ax retbeep: movb $0x07, %al jmp prtchr no_sig_mess: .string "No setup signature found ..."good_sig1: jmp good_sig# We now have to find the rest of the setup code/databad_sig: movw %cs, %ax # SETUPSEG subw $DELTA_INITSEG, %ax # INITSEG movw %ax, %ds xorb %bh, %bh movb (497), %bl # get setup sect from bootsect subw $4, %bx # LILO loads 4 sectors of setup shlw $8, %bx # convert to words (1sect=2^8 words) movw %bx, %cx shrw $3, %bx # convert to segment addw $SYSSEG, %bx movw %bx, %cs:start_sys_seg# Move rest of setup code/data to here movw $2048, %di # four sectors loaded by LILO subw %si, %si movw %cs, %ax # aka SETUPSEG movw %ax, %es movw $SYSSEG, %ax movw %ax, %ds rep movsw movw %cs, %ax # aka SETUPSEG movw %ax, %ds cmpw $SIG1, setup_sig1 jne no_sig cmpw $SIG2, setup_sig2 jne no_sig jmp good_signo_sig: lea no_sig_mess, %si call prtstrno_sig_loop: jmp no_sig_loopgood_sig: movw %cs, %ax # aka SETUPSEG subw $DELTA_INITSEG, %ax # aka INITSEG movw %ax, %ds# Check if an old loader tries to load a big-kernel testb $LOADED_HIGH, %cs:loadflags # Do we have a big kernel? jz loader_ok # No, no danger for old loaders. cmpb $0, %cs:type_of_loader # Do we have a loader that # can deal with us? jnz loader_ok # Yes, continue. pushw %cs # No, we have an old loader, popw %ds # die. lea loader_panic_mess, %si call prtstr jmp no_sig_looploader_panic_mess: .string "Wrong loader, giving up..."loader_ok: /* check for long mode. */ /* we have to do this before the VESA setup, otherwise the user can't see the error message. */ pushw %ds movw %cs,%ax movw %ax,%ds /* minimum CPUID flags for x86-64 */ /* see http://www.x86-64.org/lists/discuss/msg02971.html */ #define SSE_MASK ((1<<25)|(1<<26))#define REQUIRED_MASK1 ((1<<0)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<8)|\ (1<<13)|(1<<15)|(1<<24))#define REQUIRED_MASK2 (1<<29) pushfl /* standard way to check for cpuid */ popl %eax movl %eax,%ebx xorl $0x200000,%eax pushl %eax popfl pushfl popl %eax cmpl %eax,%ebx jz no_longmode /* cpu has no cpuid */ movl $0x0,%eax cpuid cmpl $0x1,%eax jb no_longmode /* no cpuid 1 */ xor %di,%di cmpl $0x68747541,%ebx /* AuthenticAMD */ jnz noamd cmpl $0x69746e65,%edx jnz noamd cmpl $0x444d4163,%ecx jnz noamd mov $1,%di /* cpu is from AMD */noamd: movl $0x1,%eax cpuid andl $REQUIRED_MASK1,%edx xorl $REQUIRED_MASK1,%edx jnz no_longmode movl $0x80000000,%eax cpuid cmpl $0x80000001,%eax jb no_longmode /* no extended cpuid */ movl $0x80000001,%eax cpuid andl $REQUIRED_MASK2,%edx xorl $REQUIRED_MASK2,%edx jnz no_longmodesse_test: movl $1,%eax cpuid andl $SSE_MASK,%edx cmpl $SSE_MASK,%edx je sse_ok test %di,%di jz no_longmode /* only try to force SSE on AMD */ movl $0xc0010015,%ecx /* HWCR */ rdmsr btr $15,%eax /* enable SSE */ wrmsr xor %di,%di /* don't loop */ jmp sse_test /* try again */ no_longmode: call beep lea long_mode_panic,%si call prtstrno_longmode_loop: jmp no_longmode_looplong_mode_panic: .string "Your CPU does not support long mode. Use a 32bit distribution." .byte 0 sse_ok: popw %ds # tell BIOS we want to go to long mode movl $0xec00,%eax # declare target operating mode movl $2,%ebx # long mode int $0x15 # Get memory size (extended mem, kB) xorl %eax, %eax movl %eax, (0x1e0)#ifndef STANDARD_MEMORY_BIOS_CALL movb %al, (E820NR)# Try three different memory detection schemes. First, try# e820h, which lets us assemble a memory map, then try e801h,# which returns a 32-bit memory size, and finally 88h, which# returns 0-64m# method E820H:# the memory map from hell. e820h returns memory classified into# a whole bunch of different types, and allows memory holes and# everything. We scan through this memory map and build a list# of the first 32 memory areas, which we return at [E820MAP].# This is documented at http://www.acpi.info/, in the ACPI 2.0 specification.#define SMAP 0x534d4150meme820: xorl %ebx, %ebx # continuation counter movw $E820MAP, %di # point into the whitelist # so we can have the bios # directly write into it.jmpe820: movl $0x0000e820, %eax # e820, upper word zeroed movl $SMAP, %edx # ascii 'SMAP' movl $20, %ecx # size of the e820rec pushw %ds # data record. popw %es int $0x15 # make the call jc bail820 # fall to e801 if it fails cmpl $SMAP, %eax # check the return is `SMAP' jne bail820 # fall to e801 if it fails# cmpl $1, 16(%di) # is this usable memory?# jne again820 # If this is usable memory, we save it by simply advancing %di by # sizeof(e820rec). #good820: movb (E820NR), %al # up to 128 entries cmpb $E820MAX, %al jae bail820 incb (E820NR) movw %di, %ax addw $20, %ax movw %ax, %diagain820: cmpl $0, %ebx # check to see if jne jmpe820 # %ebx is set to EOFbail820:# method E801H:# memory size is in 1k chunksizes, to avoid confusing loadlin.# we store the 0xe801 memory size in a completely different place,# because it will most likely be longer than 16 bits.# (use 1e0 because that's what Larry Augustine uses in his# alternative new memory detection scheme, and it's sensible
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -