1. setup.S
setup.S is responsible for getting the system data from the BIOS and putting them into appropriate places in system memory.
kernel version : 2.6.11.12
grub version:0.97
kernel elf type: bzImage
1.1 Header
/* Signature words to ensure LILO loaded us right */
#define SIG1 0xAA55
#define SIG2 0x5A5A
INITSEG = DEF_INITSEG # 0x9000, we move boot here, out of the way
SYSSEG = DEF_SYSSEG # 0x1000, system loaded at 0x10000 (65536).
SETUPSEG = DEF_SETUPSEG # 0x9020, this is the current segment
# ... and the former contents of CS
DELTA_INITSEG = SETUPSEG - INITSEG # 0x0020
/* Signature words to ensure LILO loaded us right */
#define SIG1 0xAA55
#define SIG2 0x5A5A
INITSEG = DEF_INITSEG # 0x9000, we move boot here, out of the way
SYSSEG = DEF_SYSSEG # 0x1000, system loaded at 0x10000 (65536).
SETUPSEG = DEF_SETUPSEG # 0x9020, this is the current segment
# ... and the former contents of CS
DELTA_INITSEG = SETUPSEG - INITSEG # 0x0020
.code16
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
start:
jmp trampoline
# This is the setup header, and it must start at %cs:2 (old 0x9020:2)
.ascii "HdrS" # header signature
.word 0x0203 # header version number (>= 0x0105)
# or else old loadlin-1.5 will fail)
realmode_swtch: .word 0, 0 # default_switch, SETUPSEG
start_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 loadflags
loadflags:
LOADED_HIGH = 1 # If set, the kernel is loaded high
CAN_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
#endif
setup_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
#endif
ramdisk_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 bytes
bootsect_kludge:
.long 0 # obsolete
heap_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 0
cmd_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 (-__PAGE_OFFSET-(512 << 20)-1) & 0x7fffffff
# (Header version 0x0203 or later)
# The highest safe address for
# the contents of an initrd
trampoline: call start_of_setup
.align 16
# The offset at this point is 0x240
.space (0x7ff-0x240+1) # E820 & EDD space (ending at 0x7ff)
# End of setup header #####################################################
The __PAGE_OFFSET definition in linux/asm-i386/page.h
#define __PAGE_OFFSET (0xC0000000)
The setup header must follow some layout pattern. Refer to linux/Documentation/i386/boot.txt:
The header looks like:
Offset Proto Name Meaning
/Size
01F1/1 ALL setup_sects The size of the setup in sectors
01F2/2 ALL root_flags If set, the root is mounted readonly
01F4/2 ALL syssize DO NOT USE - for bootsect.S use only
01F6/2 ALL swap_dev DO NOT USE - obsolete
01F8/2 ALL ram_size DO NOT USE - for bootsect.S use only
01FA/2 ALL vid_mode Video mode control
01FC/2 ALL root_dev Default root device number
01FE/2 ALL boot_flag 0xAA55 magic number
0200/2 2.00+ jump Jump instruction
0202/4 2.00+ header Magic signature "HdrS"
0206/2 2.00+ version Boot protocol version supported
0208/4 2.00+ realmode_swtch Boot loader hook (see below)
020C/2 2.00+ start_sys The load-low segment (0x1000) (obsolete)
020E/2 2.00+ kernel_version Pointer to kernel version string
0210/1 2.00+ type_of_loader Boot loader identifier
0211/1 2.00+ loadflags Boot protocol option flags
0212/2 2.00+ setup_move_size Move to high memory size (used with hooks)
0214/4 2.00+ code32_start Boot loader hook (see below)
0218/4 2.00+ ramdisk_image initrd load address (set by boot loader)
021C/4 2.00+ ramdisk_size initrd size (set by boot loader)
0220/4 2.00+ bootsect_kludge DO NOT USE - for bootsect.S use only
0224/2 2.01+ heap_end_ptr Free memory after setup end
0226/2 N/A pad1 Unused
0228/4 2.02+ cmd_line_ptr 32-bit pointer to the kernel command line
022C/4 2.03+ initrd_addr_max Highest legal initrd address
1.2 Check Code Integrity
/////////////////////////
trampoline: call start_of_setup
.align 16
# The offset at this point is 0x240
.space (0x7ff-0x240+1) # E820 & EDD space (ending at 0x7ff)
//////////////////////
///////////////////////
start_of_setup:
# Bootlin depends on this being done early
movw $0x01500, %ax
movb $0x81, %dl
int $0x13
// int 13h/AH=15h----Get Disk Type return :AH=1,loppy without change-line support,value CX:DX when AL=3h
#ifdef SAFE_RESET_DISK_CONTROLLER
# Reset the disk controller.
movw $0x0000, %ax
movb $0x80, %dl
int $0x13
//int 13/AH=00h: DISK - RESET DISK SYSTEM
#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:si
prtstr:
lodsb
andb %al, %al
jz fin
call prtchr
jmp prtstr
fin: ret
////////////////////////
Signature is checked to verify code integrity.
If signature is not found, the rest setup code may precede vmlinux at SYSSEG:0.
bad_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
pushw %cs
popw %es
movw $SYSSEG, %ax
movw %ax, %ds
rep
movsw //source address DS:SI DS=$SYSSEG=0x1000 SI=0
movw %cs, %ax # aka SETUPSEG
movw %ax, %ds
cmpw $SIG1, setup_sig1
jne no_sig
cmpw $SIG2, setup_sig2
jne no_sig
jmp good_sig
no_sig:
lea no_sig_mess, %si
call prtstr
no_sig_loop:
hlt
jmp no_sig_loop
The setup code has been moved to correct place. Variable start_sys_seg points to where real system code starts. If “bad_sig” does not happen, start_sys_seg remains SYSSEG.
4.3 Check Loader type
# 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.
loadflags
// define in setup.S
loadflags:
LOADED_HIGH = 1 # If set, the kernel is loaded high
CAN_USE_HEAP = 0x80 # If set, the loader also has set
//modify in grub
stage2/shared.h:#define LINUX_FLAG_CAN_USE_HEAP 0x80
stage2/boot.c: lh->loadflags |= LINUX_FLAG_CAN_USE_HEAP;
loadflags=0x1|0x80=0x81
type_of_loader
// define in setup.S
type_of_loader: .byte 0
// modify in grub
stage2/shared.h:#define LINUX_BOOT_LOADER_TYPE 0x71
stage2/boot.c: lh->type_of_loader = LINUX_BOOT_LOADER_TYPE;
type_of_loader=0x71
Note that type_of_loader has been changed to 0x71, loadflags has changed to 0x81。
1.4 Get Memory Size
Try three different memory detection schemes to get the extended memory size (above 1M) in KB.
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.
目的:如果e820得不到,这可以从e801得到,最后从88h中得到。
loader_ok:
# 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 0x534d4150
meme820:
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 32 entries
cmpb $E820MAX, %al
jnl bail820
incb (E820NR)
movw %di, %ax
addw $20, %ax
movw %ax, %di
again820:
cmpl $0, %ebx # check to see if
jne jmpe820 # %ebx is set to EOF
bail820:
# 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
# to write everything into the same place.)
meme801:
stc # fix to work around buggy
xorw %cx,%cx # BIOSes which dont clear/set
xorw %dx,%dx # carry on pass/error of
# e801h memory size call
# or merely pass cx,dx though
# without changing them.
movw $0xe801, %ax
int $0x15
jc mem88
cmpw $0x0, %cx # Kludge to handle BIOSes
jne e801usecxdx # which report their extended
cmpw $0x0, %dx # memory in AX/BX rather than
jne e801usecxdx # CX/DX. The spec I have read
movw %ax, %cx # seems to indicate AX/BX
movw %bx, %dx # are more reasonable anyway...
e801usecxdx:
andl $0xffff, %edx # clear sign extend
shll $6, %edx # and go from 64k to 1k chunks
movl %edx, (0x1e0) # store extended memory size
andl $0xffff, %ecx # clear sign extend
addl %ecx, (0x1e0) # and add lower memory into
# total size.
# Ye Olde Traditional Methode. Returns the memory size (up to 16mb or
# 64mb, depending on the bios) in ax.
mem88:
#endif
movb $0x88, %ah
int $0x15
movw %ax, (2)
1.5检查硬件
1.5.1设置键盘速率为最大
# Set the keyboard repeat rate to the max
movw $0x0305, %ax
xorw %bx, %bx
int $0x16
1.5.2 设置video参数
# Check for video adapter and its parameters and allow the
# user to browse video modes.
call video # NOTE: we need %ds pointing
# to bootsector
1.5.3 检查键盘参数并保存到ES:DI(0x9000:0x80)
# Get hd0 data...
xorw %ax, %ax
movw %ax, %ds
ldsw (4 * 0x41), %si
movw %cs, %ax # aka SETUPSEG
subw $DELTA_INITSEG, %ax # aka INITSEG
pushw %ax
movw %ax, %es
movw $0x0080, %di
movw $0x10, %cx
pushw %cx
cld
rep
movsb
# Get hd1 data...
xorw %ax, %ax
movw %ax, %ds
ldsw (4 * 0x46), %si
popw %cx
popw %es
movw $0x0090, %di
rep
movsb
# Check that there IS a hd1 :-)
movw $0x01500, %ax
movb $0x81, %dl
int $0x13
jc no_disk1
cmpb $3, %ah
je is_disk1
no_disk1:
movw %cs, %ax # aka SETUPSEG
subw $DELTA_INITSEG, %ax # aka INITSEG
movw %ax, %es
movw $0x0090, %di
movw $0x10, %cx
xorw %ax, %ax
cld
rep
stosb
is_disk1:
#endif
检查 PS/2 设备,如果存在,这在0x901ff地址中
movw %cs, %ax # aka SETUPSEG
subw $DELTA_INITSEG, %ax # aka INITSEG
movw %ax, %ds
movw $0, (0x1ff) # default is no pointing device
int $0x11 # int 0x11: equipment list
testb $0x04, %al # check if mouse installed
jz no_psmouse
movw $0xAA, (0x1ff) # device present
no_psmouse:
得到IST的值,分别存在0x90096,0x90100,0x90104,0x90108处
movl $0x0000E980, %eax # IST Support
movl $0x47534943, %edx # Request value
int $0x15
movl %eax, (96)
movl %ebx, (100)
movl %ecx, (104)
movl %edx, (108)
APM保存BIOS相关信息
#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
# Then check for an APM BIOS...
# %ds points to the bootsector
movw $0, 0x40 # version = 0 means no APM BIOS
movw $0x05300, %ax # APM BIOS installation check
xorw %bx, %bx
int $0x15
jc done_apm_bios # Nope, no APM BIOS
cmpw $0x0504d, %bx # Check for "PM" signature
jne done_apm_bios # No signature, no APM BIOS
andw $0x02, %cx # Is 32 bit supported?
je done_apm_bios # No 32-bit, no (good) APM BIOS
movw $0x05304, %ax # Disconnect firstpb just in case
xorw %bx, %bx
int $0x15 # ignore return code
movw $0x05303, %ax # 32 bit connect
xorl %ebx, %ebx
xorw %cx, %cx # paranoia :-)
xorw %dx, %dx # ...
xorl %esi, %esi # ...
xorw %di, %di # ...
int $0x15
jc no_32_apm_bios # Ack, error.
movw %ax, (66) # BIOS code segment
movl %ebx, (68) # BIOS entry point offset
movw %cx, (72) # BIOS 16 bit code segment
movw %dx, (74) # BIOS data segment
movl %esi, (78) # BIOS code segment lengths
movw %di, (82) # BIOS data segment length
# Redo the installation check as the 32 bit connect
# modifies the flags returned on some BIOSs
movw $0x05300, %ax # APM BIOS installation check
xorw %bx, %bx
xorw %cx, %cx # paranoia
int $0x15
jc apm_disconnect # error -> shouldn't happen
cmpw $0x0504d, %bx # check for "PM" signature
jne apm_disconnect # no sig -> shouldn't happen
movw %ax, (64) # record the APM BIOS version
movw %cx, (76) # and flags
jmp done_apm_bios
apm_disconnect: # Tidy up
movw $0x05304, %ax # Disconnect
xorw %bx, %bx
int $0x15 # ignore return code
jmp done_apm_bios
no_32_apm_bios:
andw $0xfffd, (76) # remove 32 bit support bit
done_apm_bios:
#endif
说明:
-
把BIOS的数据段地址,代码段地址保存到地址0x90066,0x90068,0x90072,0x90074,0x90078,0x90082
-
把BIOS version信息保存到0x90064,0x90076中
movw %ax, (66) # BIOS code segment
movl %ebx, (68) # BIOS entry point offset
movw %cx, (72) # BIOS 16 bit code segment
movw %dx, (74) # BIOS data segment
movl %esi, (78) # BIOS code segment lengths
movw %di, (82) # BIOS data segment length
返回值分析
int 15h/eax=0xe820
E820MAP定义在文件include/asm/e820.h:
#define E820MAP 0x2d0
e820的返回值存放在E820MAP地址中,e801的返回值存放在0x1e0中,88h的返回值2中。
int 15h/eax=0xe801
ax=0x3c00 // extended memory between 1M and 16M, in K 15M
bx=0x100 //extended memory above 16M, in 64K blocks 16M
cx=0x3c00 //configured memory 1M to 16M, in K 15M
dx=0x100 //configured memory above 16M, in 64K blocks 16M
andl $0xffff, %edx # clear sign extend
shll $6, %edx # and go from 64k to 1k chunks
movl %edx, (0x1e0) # store extended memory size
andl $0xffff, %ecx # clear sign extend
addl %ecx, (0x1e0) # and add lower memory into
# total size.
//ds:0x1e0 store value extended momory size in K,bx<<6+ax , 31M in this example
int 15h/eax=0x88
eax=0x7c00 //number of contiguous KB starting at absolute address 100000h; 31M
movw %ax, (2) //store value to ds:2 *(0x90002)=0x7c00
int15/AH=0xC0---->Get system configuration
存放在ds:0xa0中
0x000900a0 <bogus+ 0>: 0x08 0x00 0xfc 0x00 0x01 0x74 0x40 0x00
0x000900a8 <bogus+ 8>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
model=oxfc submodel=0x00 version=0x1–>2nd
Model | Submdl | Rev | BIOS date | System |
---|---|---|---|---|
FCh | 00h | 01h | 06/10/85 | AT model 239 6 MHz 30MB |
从上面知道:BIOS date:06/10/85 System: AT model 239 6MHz 30MB
0x74支持下面功能:
Bit(s) Description (Table 00510)
6 2nd interrupt controller (8259) installed
5 Real-Time Clock installed
4 INT 15/AH=4Fh called upon INT 09h
2 extended BIOS area allocated (usually at top of RAM)
0x4支持下面功能:
Bit(s) Description
6 INT 16/AH=09h (keyboard functionality) supported (see #00585)
int11h: BIOS - GET EQUIPMENT LIST
eax=0x13134226
Bit(s) Description | (Table 00226) |
---|---|
1 | 80x87 coprocessor installed |
2 | pointing device installed (PS) |
5-4 | initial video mode. 10 80x25 color. |
11-9 | number of serial ports installed 1个串口 |
15-14 | number of parallel ports installed 1个并口 |
硬盘参数
int 0x41 的中断向量位置(4 * 0x41 =0x0000:0x0104)存放的并不是中断程序的地址而是第一个硬盘的基本参数表的地址
位移 | 大小 | 说明 |
---|---|---|
0x00 | 字 | 柱面数 |
0x02 | 字节 | 磁头数 |
0x03 | 字 | 开始减小写电流的柱面(仅PC XT 使用,其它为0) |
0x05 | 字 | 开始写前预补偿柱面号(乘4) |
0x07 | 字节 | 最大ECC 猝发长度(仅XT 使用,其它为0) |
0x08 | 字节 | 控制字节(驱动器步进选择)位0 未用 位1 保留(0) (关闭IRQ) 位2 允许复位位3 若磁头数大于8 则置1 位4 未用(0) 位5 若在柱面数+1 处有生产商的坏区图,则置1 位6 禁止ECC 重试 位7 禁止访问重试。 |
0x09 | 字节 | 标准超时值(仅XT 使用,其它为0) |
0x0A | 字节 | 格式化超时值(仅XT 使用,其它为0) |
0x0B | 字节 | 检测驱动器超时值(仅XT 使用,其它为0) |
0x0C | 字 | 磁头着陆(停止)柱面号 |
0x0E | 字节 | 每磁道扇区数 |
0x0F | 字节 | 保留 |
在0x90104处,通过rep搬移到0x90080处。
本实验磁盘的硬盘信息
0x00090080 <bogus+ 0>: 0x64 0x00 0x10 0x00 0x00 0xff 0xff 0x00
0x00090088 <bogus+ 8>: 0xc8 0x00 0x00 0x00 0x64 0x00 0x3f 0x00
柱面数: 0x64=100
磁头数: 0x10=16
每磁道扇区: 0x3f=63
int15/EAX=0xe980/EDX=47534943–> intel speed step (IST)
输出 | 含义 |
---|---|
eax | 信号值(signature) |
ebx | 命令(command) |
ecx | 事件(evetn) |
edx | 功耗登记(perf_level) |
键盘参数
repeat delay:
The pause between pressing the key and when it starts repeating is the repeat delay. (重复延迟就是你按一个键不松,多长时间后会再次重复这个字符)
repeat rate:
After you press and hold down a key on the keyboard, the key starts repeating itself. The speed at which it repeats is the repeat rate.(速率就是当开始重复以后,每隔多长时间重复一次)