Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2150459
  • 博文数量: 438
  • 博客积分: 3871
  • 博客等级: 中校
  • 技术积分: 6075
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-10 00:11
个人简介

邮箱: wangcong02345@163.com

文章分类

全部博文(438)

文章存档

2017年(15)

2016年(119)

2015年(91)

2014年(62)

2013年(56)

2012年(79)

2011年(16)

分类: LINUX

2016-10-29 11:26:28

一. 谁是最开始执行的代码
a. 最终生成的bzImage的大小
  1. cong@msi:/work/os/linux-2.4.12$ ls -alh ./arch/i386/boot/bzImage
  2. -rw-rw-r-- 1 cong cong 794K Oct 22 02:17 ./arch/i386/boot/bzImage
b.bochsr的配置
floppya: 1_44="./arch/i386/boot/bzImage", status=inserted
boot: floppy
c. zImage中的数据布局


d. 生成bzImage的过程
tools/build -b bbootsect bsetup compressed/bvmlinux.out CURRENT > bzImage
所以刚开始执行的是 arch/i386/boot/bootsect.S

二.最开始的bootsect.S分析
2.1 arch/i386/boot/bootsect.S分析

  1. #include <asm/boot.h>

  2. SETUPSECTS    = 4               /* default nr of setup-sectors */
  3. BOOTSEG        = 0x07C0        /* original address of boot-sector */
  4. INITSEG        = DEF_INITSEG        /* we move boot here - out of the way */
  5. SETUPSEG    = DEF_SETUPSEG        /* setup starts here */
  6. SYSSEG        = DEF_SYSSEG        /* system loaded at 0x10000 (65536) */
  7. SYSSIZE        = DEF_SYSSIZE        /* system size: # of 16-byte clicks */
  8.                     /* to be loaded */
  9. ROOT_DEV    = 0             /* ROOT_DEV is now written by "build" */
  10. SWAP_DEV    = 0            /* SWAP_DEV is now written by "build" */

  11. #ifndef SVGA_MODE
  12. #define SVGA_MODE ASK_VGA
  13. #endif

  14. #ifndef RAMDISK
  15. #define RAMDISK 0
  16. #endif

  17. #ifndef ROOT_RDONLY
  18. #define ROOT_RDONLY 1
  19. #endif

  20. .code16
  21. .text

  22. .global _start
  23. _start:

  24. # First things first. Move ourself from 0x7C00 -> 0x90000 and jump there.
  25. //1.把bootsect的512字节由0x7C00复制到0x90000,并跳转到0x90000处
  26.     movw    $BOOTSEG, %ax
  27.     movw    %ax, %ds        # %ds = BOOTSEG
  28.     movw    $INITSEG, %ax
  29.     movw    %ax, %es        # %ax = %es = INITSEG
  30.     movw    $256, %cx       #bootsect一共512个字节,这儿是2个字节一复制,所以是512/2=256次
  31.     subw    %si, %si
  32.     subw    %di, %di
  33.     cld
  34.     rep
  35.     movsw
  36.     ljmp    $INITSEG, $go  -->jmpf 0x9000:0019

  37. # bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde). We
  38. # wouldn't have to worry about this if we checked the top of memory. Also
  39. # my BIOS can be configured to put the wini drive tables in high memory
  40. # instead of in the vector table. The old stack might have clobbered the
  41. # drive table.

  42. go:    movw    $0x4000-12, %di        # 0x4000 is an arbitrary value >=
  43.                     # length of bootsect + length of
  44.                     # setup + room for stack;
  45.                     # 12 is disk parm size.
  46.     movw    %ax, %ds        # %ax and %es already contain INITSEG
  47.     movw    %ax, %ss
  48.     movw    %di, %sp        # put stack at INITSEG:0x4000-12.

  49. # Many BIOS's default disk parameter tables will not recognize
  50. # multi-sector reads beyond the maximum sector number specified
  51. # in the default diskette parameter tables - this may mean 7
  52. # sectors in some cases.
  53. #
  54. # Since single sector reads are slow and out of the question,
  55. # we must take care of this by creating new parameter tables
  56. # (for the first disk) in RAM. We will set the maximum sector
  57. # count to 36 - the most we will encounter on an ED 2.88.
  58. #
  59. # High doesn't hurt. Low does.
  60. #
  61. # Segments are as follows: %cs = %ds = %es = %ss = INITSEG, %fs = 0,
  62. # and %gs is unused.
  63. //0x78到0x80,0x81四个字节中存放着磁盘参数表的地址
  64. //这个地址刚开始是由fs:(%bx)指定,后来通过ldsw指令放在了ds:si中
  65. //rep movsw word ptr es:[di], word ptr ds:[si]把磁盘参数表的数据读到了0x93ff4
  66.     movw    %cx, %fs              # %fs = 0
  67.     movw    $0x78, %bx            -->fs:bx是磁盘参数表的地址# %fs:%bx is parameter table address
  68.     pushw    %ds
  69.     ldsw    %fs:(%bx), %si        -->把从fs:0x78磁盘参数表的地址复制到ds:si
  70.     movb    $6, %cl               -->读12个字节,每次读2个共6次
  71.     pushw    %di                  -->di=0x4000-12=0x3FF4 # %di = 0x4000-12.
  72.     rep                           -->把磁盘参数表的数据读到[0x93FF4-0x94000]处共12个字节
  73.     movsw                         -->rep movsw word ptr es:[di], word ptr ds:[si]
  74. //修改0x93ff4+0x4处的sector_count的值
  75.     popw    %di
  76.     popw    %ds
  77.     movb    $36, 0x4(%di)        # patch sector count
  78. //修改fs:(%bx)所指向的磁盘参数表的地址为0x93F44-->di=0x3F44,es=0x9000
  79.     movw    %di, %fs:(%bx)
  80.     movw    %es, %fs:2(%bx)

  81. # Get disk drive parameters, specifically number of sectors/track.

  82. # It seems that there is no BIOS call to get the number of sectors.
  83. # Guess 36 sectors if sector 36 can be read, 18 sectors if sector 18
  84. # can be read, 15 if sector 15 can be read. Otherwise guess 9.
  85. # Note that %cx = 0 from rep movsw above.
  86. //按36 18 15 9依次去测试磁道的扇区数:
  87. //第1次试的寄存器如下:
  88. //ax=0x0201: ah=02->读磁盘到内存 al=01-->需要读出的扇区数=1
  89. //cx=0x0024: ch=0-->柱面的低8位=0   cl=0x24-->开始扇区(位0-5)是24,柱面号高2位(6-7)
  90. //dx=0x0000:dh=00-->磁头号=0       dl=0-->驱动器号
  91. //es:bx=0x9000:0x200是数据缓冲区,如果出错CF标志置位,ah是出错码
  92. //综上,从0磁头0柱面36扇区开始读取一个扇区,如果没有出错,则说明sector_num=36
  93. //如果出错则试18扇区,再试15扇区,最后试9扇区
  94. //结果保存在sector中
  95.     movw    $disksizes, %si    #char disksizes[4]={36, 18, 15, 9}要试的扇区数表
  96. probe_loop:
  97.     lodsb                       -->lodsb al, byte ptr ds:[si]-->执行完后si+1 
  98.     cbtw                        -->将测试的setor_nr保存在al中,这儿扩展ax=00al
  99.     movw    %ax, sectors        -->将结果先保存在sector中ds:0x1c3
  100.     cmpw    $disksizes+4, %si   -->看si是不是到了disksize表的最后
  101.     jae    got_sectors          -->如果所有的测试都失败了,就认为sector_num是9

  102.     xchgw    %cx, %ax           -->交换ax与cx的值,则cx=要试的扇区数=36,18,15,9
  103.     xorw    %dx, %dx            -->dx清0
  104.     movw    $0x0200, %bx        -->ex:bx=0x90200,即setup要保存的位置
  105.     movw    $0x0201, %ax        -->int0x130x2服务, 01-->要读取的扇区数
  106.     int    $0x13
  107.     jc    probe_loop             # try next value
  108. //下面是获取光标信息
  109. got_sectors:
  110.     movb    $0x03, %ah        # read cursor pos
  111.     xorb    %bh, %bh
  112.     int    $0x10
  113.     movw    $9, %cx
  114.     movb    $0x07, %bl        # page 0, attribute 7 (normal)
  115.                     # %bh is set above; int10 doesn't
  116.                     # modify it
  117. //下面是打印字符Loading
  118.     movw    $msg1, %bp
  119.     movw    $0x1301, %ax        # write string, move cursor
  120.     int    $0x10            # tell the user we're loading..

  121. # Load the setup-sectors directly after the moved bootblock (at 0x90200).
  122. # We should know the drive geometry to do it, as setup may exceed first
  123. # cylinder (for 9-sector 360K and 720K floppies).
  124. //下面读取setup的内容到0x90200处
  125.     movw    $0x0001, %ax        # set sread (sector-to-read) to 1 as
  126.     movw    $sread, %si         # the boot sector has already been read
  127.     movw    %ax, (%si)          -->这三行是将sread中记录己读的扇区数设为1,因为bootsect己读所以设为1

  128.     xorw    %ax, %ax            # reset FDC
  129.     xorb    %dl, %dl
  130.     int    $0x13                -->这三行是将软盘复位 
  131.     movw    $0x0200, %bx        # address = 512, in INITSEG
  132. next_step:
  133.     movb    setup_sects, %al    -->setup_sects是setup的扇区数,即总共要读取的扇区数是5-->此后al=5
  134.     movw    sectors, %cx        -->secotrs是每个磁道的扇区数=0x12-->此后cx=0x12
  135.     subw    (%si), %cx          -->计算本磁道还可以读的扇区数=本磁盘的扇区-己读取的扇区:cx=0x12-0x1=0x11
  136.     cmpb    %cl, %al            -->cl=0x11:本磁道还要读取的扇区数-->al=5是总共要读取的扇区数
  137.     jbe    no_cyl_crossing      -->如果cl>al说明不需要跨磁道读扇区
  138.     movw    sectors, %ax
  139.     subw    (%si), %ax          # (%si) = sread
  140. no_cyl_crossing:
  141.     call    read_track          -->读取本磁道中剩余的扇区数到0x90200,即把setup放在了0x90200处
  142.     pushw    %ax                # save it
  143.     call    set_next            # set %bx properly; it uses %ax,%cx,%dx
  144.     popw    %ax                 # restore
  145.     subb    %al, setup_sects    # rest - for next step
  146.     jnz    next_step
  147. //下面把system的内容读取到0x100000
  148.     pushw    $SYSSEG
  149.     popw    %es              # %es = SYSSEG
  150.     call    read_it         -->将带解压头的vmlinux.out读到0x100000=1M处
  151.     call    kill_motor      -->关闭驱动器马达
  152.     call    print_nl        -->打印回车换行

  153. # After that we check which root-device to use. If the device is
  154. # defined (!= 0), nothing is done and the given device is used.
  155. # Otherwise, one of /dev/fd0H2880 (2,32) or /dev/PS0 (2,28) or /dev/at0 (2,8)
  156. # depending on the number of sectors we pretend to know we have.

  157. # Segments are as follows: %cs = %ds = %ss = INITSEG,
  158. #    %es = SYSSEG, %fs = 0, %gs is unused.

  159.     movw    root_dev, %ax   -->检查root_dev是否是0,如果不是0,认为文件系统的结点己定义,则跳到setup中了
  160.     orw    %ax, %ax
  161.     jne    root_defined

  162.     movw    sectors, %bx    -->如果文件系统结点没有定义,下面尝试
  163.     movw    $0x0208, %ax        # /dev/ps0 - 1.2Mb
  164.     cmpw    $15, %bx
  165.     je    root_defined

  166.     movb    $0x1c, %al        # /dev/PS0 - 1.44Mb
  167.     cmpw    $18, %bx
  168.     je    root_defined

  169.     movb    $0x20, %al        # /dev/fd0H2880 - 2.88Mb
  170.     cmpw    $36, %bx
  171.     je    root_defined

  172.     movb    $0, %al            # /dev/fd0 - autodetect
  173. root_defined:
  174.     movw    %ax, root_dev

  175. # After that (everything loaded), we jump to the setup-routine
  176. # loaded directly after the bootblock:

  177.     ljmp    $SETUPSEG, $0     -->跳到setup中去

  178. # These variables are addressed via %si register as it gives shorter code.

  179. sread:    .word 0                # sectors read of current track
  180. head:    .word 0                # current head
  181. track:    .word 0                # current track
  182. -->中间略过一些函数

  183. sectors:    .word 0
  184. disksizes:    .byte 36, 18, 15, 9
  185. msg1:        .byte 13, 10
  186.         .ascii "Loading"

  187. # XXX: This is a fairly snug fit.

  188. .org 497
  189. setup_sects:    .byte SETUPSECTS  -->这儿在编译时定义了一个默认值,但最后tools/build会修改这个值为编译出来的setup的扇区数
  190. root_flags:    .word ROOT_RDONLY
  191. syssize:    .word SYSSIZE         -->这儿在编译时定义了一个默认值,但最后tools/build会修改这个值为编译出来的vmlinux.out/16的值
  192. swap_dev:    .word SWAP_DEV
  193. ram_size:    .word RAMDISK
  194. vid_mode:    .word SVGA_MODE
  195. root_dev:    .word ROOT_DEV       -->这儿在编译时定义了一个默认值,但最后tools/build会修改这个值为CURRENT的值
  196. boot_flag:    .word 0xAA55
2.2. 总结一下bootsect的作用
a. 先把bootsect自己这512个字节移动到0x90000,然后跳到0x90000+0x19去运行
b. 把磁盘上从第2个扇区开始的n=5个扇区复制到0x90200,要复制扇区的数目记录在mbr的497处
c. 把磁盘上从第n=5个扇区开始的n+N个扇区的kernel复制到0x100000=1M处,要复制的大小记录在mbr的500处



附录: 参考文章:
[精华] bootsect.S分析(一篇很有用的老东东)

附录2:CBW
CBW :字节转换为字执行的操作:AL的内容符号扩展到AH,形成AX中的字。即如果(AL)的最高有效位为0,则(AH)=0;如(AL)的最高有效位为1,则(AH)=0FFH。
三. setup分析
注意这儿的setup是由setup.S与video.S一起组成的
3.1 arch/i386/boot/setup.S代码分析
  1. #include <linux/config.h>
  2. #include <asm/segment.h>
  3. #include <linux/version.h>
  4. #include <linux/compile.h>
  5. #include <asm/boot.h>
  6. #include <asm/e820.h>

  7. /* Signature words to ensure LILO loaded us right */
  8. #define SIG1    0xAA55
  9. #define SIG2    0x5A5A

  10. INITSEG = DEF_INITSEG        # 0x9000, we move boot here, out of the way
  11. SYSSEG = DEF_SYSSEG        # 0x1000, system loaded at 0x10000 (65536).
  12. SETUPSEG = DEF_SETUPSEG        # 0x9020, this is the current segment
  13.                 # ... and the former contents of CS

  14. DELTA_INITSEG = SETUPSEG - INITSEG    # 0x0020

  15. .code16
  16. .globl begtext, begdata, begbss, endtext, enddata, endbss

  17. .text
  18. begtext:
  19. .data
  20. begdata:
  21. .bss
  22. begbss:
  23. .text
  24. //第1次跳
  25. start:
  26.     jmp    trampoline         -->0: eb 2a -->jmp 2c -->这个jmp放在setup的最开始,偏移是0 

  27. # This is the setup header, and it must start at %cs:2 (old 0x9020:2)

  28.         .ascii    "HdrS"        # header signature
  29.         .word    0x0202        # header version number (>= 0x0105)
  30.                     # or else old loadlin-1.5 will fail)
  31. realmode_swtch:    .word    0, 0        # default_switch, SETUPSEG
  32. start_sys_seg:    .word    SYSSEG
  33.         .word    kernel_version    # pointing to kernel version string
  34.                     # above section of header is compatible
  35.                     # with loadlin-1.5 (header v1.5). Don't
  36.                     # change it.

  37. type_of_loader:    .byte    0        # = 0, old one (LILO, Loadlin,
  38.                     # Bootlin, SYSLX, bootsect...)
  39.                     # See Documentation/i386/boot.txt for
  40.                     # assigned ids
  41.     
  42. # flags, unused bits must be zero (RFU) bit within loadflags
  43. loadflags:
  44. LOADED_HIGH    = 1            # If set, the kernel is loaded high
  45. CAN_USE_HEAP    = 0x80            # If set, the loader also has set
  46.                     # heap_end_ptr to tell how much
  47.                     # space behind setup.S can be used for
  48.                     # heap purposes.
  49.                     # Only the loader knows what is free
  50. #ifndef __BIG_KERNEL__
  51.         .byte    0
  52. #else
  53.         .byte    LOADED_HIGH
  54. #endif

  55. setup_move_size: .word 0x8000        # size to move, when setup is not
  56.                     # loaded at 0x90000. We will move setup
  57.                     # to 0x90000 then just before jumping
  58.                     # into the kernel. However, only the
  59.                     # loader knows how much data behind
  60.                     # us also needs to be loaded.

  61. code32_start:                # here loaders can put a different
  62.                     # start address for 32-bit code.
  63. #ifndef __BIG_KERNEL__
  64.         .long    0x1000        # 0x1000 = default for zImage
  65. #else
  66.         .long    0x100000    # 0x100000 = default for big kernel
  67. #endif

  68. ramdisk_image:    .long    0        # address of loaded ramdisk image
  69.                     # Here the loader puts the 32-bit
  70.                     # address where it loaded the image.
  71.                     # This only will be read by the kernel.

  72. ramdisk_size:    .long    0        # its size in bytes

  73. bootsect_kludge:
  74.         .word bootsect_helper, SETUPSEG

  75. heap_end_ptr:    .word    modelist+1024    # (Header version 0x0201 or later)
  76.                     # space from here (exclusive) down to
  77.                     # end of setup code can be used by setup
  78.                     # for local heap purposes.

  79. pad1:        .word    0
  80. cmd_line_ptr:    .long 0            # (Header version 0x0202 or later)
  81.                     # If nonzero, a 32-bit pointer
  82.                     # to the kernel command line.
  83.                     # The command line should be
  84.                     # located between the start of
  85.                     # setup and the end of low
  86.                     # memory (0xa0000), or it may
  87.                     # get overwritten before it
  88.                     # gets read. If this field is
  89.                     # used, there is no longer
  90.                     # anything magical about the
  91.                     # 0x90000 segment; the setup
  92.                     # can be located anywhere in
  93.                     # low memory 0x10000 or higher.
  94. //第2次跳
  95. trampoline:    call    start_of_setup          -->又跳了一次
  96.         .space    1024
  97. # End of setup header #####################################################

  98. start_of_setup:
  99. # Bootlin depends on this being done early
  100.     movw    $0x01500, %ax
  101.     movb    $0x81, %dl
  102.     int    $0x13

  103. #ifdef SAFE_RESET_DISK_CONTROLLER
  104. # Reset the disk controller.
  105.     movw    $0x0000, %ax
  106.     movb    $0x80, %dl
  107.     int    $0x13
  108. #endif

  109. //原先ds=0x9000,执行下面两行后ds=0x9020
  110.     movw    %cs, %ax        # aka SETUPSEG
  111.     movw    %ax, %ds
  112. //setup_sig1与setup_sig2都放在setup的最后,作用是防止引导程序LILO加载setup不完整
  113. //这儿用软盘启动,不担心这个,直接跳到good_sig1去 
  114.     cmpw    $SIG1, setup_sig1
  115.     jne    bad_sig

  116.     cmpw    $SIG2, setup_sig2
  117.     jne    bad_sig
  118. //第3次跳
  119.     jmp    good_sig1          -->直接跳到good_sig1去 

  120. # Routine to print asciiz string at ds:si
  121. prtstr:
  122.     lodsb
  123.     andb    %al, %al
  124.     jz    fin

  125.     call    prtchr
  126.     jmp    prtstr

  127. fin:    ret

  128. # Space printing
  129. prtsp2:    call    prtspc        # Print double space
  130. prtspc:    movb    $0x20, %al    # Print single space (note: fall-thru)

  131. # Part of above routine, this one just prints ascii al
  132. prtchr:    pushw    %ax
  133.     pushw    %cx
  134.     xorb    %bh, %bh
  135.     movw    $0x01, %cx
  136.     movb    $0x0e, %ah
  137.     int    $0x10
  138.     popw    %cx
  139.     popw    %ax
  140.     ret

  141. beep:    movb    $0x07, %al
  142.     jmp    prtchr
  143.     
  144. no_sig_mess: .string    "No setup signature found ..."
  145. //第4次跳
  146. good_sig1:
  147.     jmp    good_sig

  148. # We now have to find the rest of the setup code/data
  149. bad_sig:
  150.     movw    %cs, %ax            # SETUPSEG
  151.     subw    $DELTA_INITSEG, %ax        # INITSEG
  152.     movw    %ax, %ds
  153.     xorb    %bh, %bh
  154.     movb    (497), %bl            # get setup sect from bootsect
  155.     subw    $4, %bx                # LILO loads 4 sectors of setup
  156.     shlw    $8, %bx                # convert to words (1sect=2^8 words)
  157.     movw    %bx, %cx
  158.     shrw    $3, %bx                # convert to segment
  159.     addw    $SYSSEG, %bx
  160.     movw    %bx, %cs:start_sys_seg
  161. # Move rest of setup code/data to here
  162.     movw    $2048, %di            # four sectors loaded by LILO
  163.     subw    %si, %si
  164.     movw    %cs, %ax            # aka SETUPSEG
  165.     movw    %ax, %es
  166.     movw    $SYSSEG, %ax
  167.     movw    %ax, %ds
  168.     rep
  169.     movsw
  170.     movw    %cs, %ax            # aka SETUPSEG
  171.     movw    %ax, %ds
  172.     cmpw    $SIG1, setup_sig1
  173.     jne    no_sig

  174.     cmpw    $SIG2, setup_sig2
  175.     jne    no_sig

  176.     jmp    good_sig

  177. no_sig:
  178.     lea    no_sig_mess, %si
  179.     call    prtstr

  180. no_sig_loop:
  181.     jmp    no_sig_loop
  182. //第4次跳到这儿
  183. good_sig:
  184. //原先ds=0x9020,执行下面两行后ds=0x9000,跟上面一次是把ds变为0x9020现在又回去了
  185. //这儿的目的是:把memory的信息放在0x9000:offset处,所以要把ds变为0x9000
  186.     movw    %cs, %ax                    # aka SETUPSEG
  187.     subw    $DELTA_INITSEG, %ax         # aka INITSEG
  188.     movw    %ax, %ds 
  189. # Check if an old loader tries to load a big-kernel
  190. //查看loadflasgs的值是不是1,type_of_loaderr的值是不是0,如果都对就跳到loader_ok中
  191.     testb    $LOADED_HIGH, %cs:loadflags    # Do we have a big kernel?
  192.     jz    loader_ok            # No, no danger for old loaders.

  193.     cmpb    $0, %cs:type_of_loader         # Do we have a loader that
  194.                                            # can deal with us?
  195.     jnz    loader_ok                      //第5次跳   # Yes, continue.

  196.     pushw    %cs                # No, we have an old loader,
  197.     popw    %ds                # die.
  198.     lea    loader_panic_mess, %si
  199.     call    prtstr

  200.     jmp    no_sig_loop

  201. loader_panic_mess: .string "Wrong loader, giving up..."
  202. //第5次跳到这儿
  203. loader_ok:
  204. # Get memory size (extended mem, kB)

  205.     xorl    %eax, %eax
  206.     movl    %eax, (0x1e0)
  207. #ifndef STANDARD_MEMORY_BIOS_CALL
  208.     movb    %al, (E820NR)
  209. # Try three different memory detection schemes. First, try
  210. # e820h, which lets us assemble a memory map, then try e801h,
  211. # which returns a 32-bit memory size, and finally 88h, which
  212. # returns 0-64m
  213. //下面用三种不同的方法去获取memory_size,幸好我这儿是用的最简单的0x88 int 0x15的方式获得的
  214. //下面的一大堆代码都不需要看了,获取之后将memory的信息放在0x90002处
  215. # method E820H:
  216. # the memory map from hell. e820h returns memory classified into
  217. # a whole bunch of different types, and allows memory holes and
  218. # everything. We scan through this memory map and build a list
  219. # of the first 32 memory areas, which we return at [E820MAP].
  220. # This is documented at http://www.teleport.com/~acpi/acpihtml/topic245.htm

  221. #define SMAP 0x534d4150
  222. //第1种检测内存的方法是E820
  223. // include/asm-i386/e820.h中规定了
    // #define E820MAP 0x2d0      /* our map */
    // #define E820NR  0x1e8       /* # entries in E820MAP */
  224. //所以E820的结果会放在0x90000+0x2d0, 个数放在0x900000+0x1e8中
  225. meme820:
  226.     xorl    %ebx, %ebx            # continuation counter
  227.     movw    $E820MAP, %di         -->E820的数据结果会放在E820MAP中
  228. jmpe820:
  229.     movl    $0x0000e820, %eax        # e820, upper word zeroed
  230.     movl    $SMAP, %edx            # ascii 'SMAP'
  231.     movl    $20, %ecx            # size of the e820rec
  232.     pushw    %ds                # data record.
  233.     popw    %es
  234.     int    $0x15                # make the call
  235.     jc    bail820                # fall to e801 if it fails

  236.     cmpl    $SMAP, %eax            # check the return is `SMAP'
  237.     jne    bail820                # fall to e801 if it fails

  238. #    cmpl    $1, 16(%di)            # is this usable memory?
  239. #    jne    again820

  240.     # If this is usable memory, we save it by simply advancing %di by
  241.     # sizeof(e820rec).
  242.     #
  243. good820:
  244.     movb    (E820NR), %al            # up to 32 entries
  245.     cmpb    $E820MAX, %al
  246.     jnl    bail820

  247.     incb    (E820NR)                -->E820的map个数每次加1
  248.     movw    %di, %ax
  249.     addw    $20, %ax
  250.     movw    %ax, %di
  251. again820:
  252.     cmpl    $0, %ebx              # check to see if
  253.     jne    jmpe820                # %ebx is set to EOF
  254. bail820:


  255. # method E801H:
  256. # memory size is in 1k chunksizes, to avoid confusing loadlin.
  257. # we store the 0xe801 memory size in a completely different place,
  258. # because it will most likely be longer than 16 bits.
  259. # (use 1e0 because that's what Larry Augustine uses in his
  260. # alternative new memory detection scheme, and it's sensible
  261. # to write everything into the same place.)

  262. meme801:
  263.     stc                    # fix to work around buggy
  264.     xorw    %cx,%cx                # BIOSes which dont clear/set
  265.     xorw    %dx,%dx                # carry on pass/error of
  266.                         # e801h memory size call
  267.                         # or merely pass cx,dx though
  268.                         # without changing them.
  269.     movw    $0xe801, %ax
  270.     int    $0x15
  271.     jc    mem88

  272.     cmpw    $0x0, %cx            # Kludge to handle BIOSes
  273.     jne    e801usecxdx            # which report their extended
  274.     cmpw    $0x0, %dx            # memory in AX/BX rather than
  275.     jne    e801usecxdx            # CX/DX. The spec I have read
  276.     movw    %ax, %cx            # seems to indicate AX/BX
  277.     movw    %bx, %dx            # are more reasonable anyway...

  278. e801usecxdx:
  279.     andl    $0xffff, %edx            # clear sign extend
  280.     shll    $6, %edx            # and go from 64k to 1k chunks
  281.     movl    %edx, (0x1e0)            # store extended memory size
  282.     andl    $0xffff, %ecx            # clear sign extend
  283.     addl    %ecx, (0x1e0)            # and add lower memory into
  284.                         # total size.

  285. # Ye Olde Traditional Methode. Returns the memory size (up to 16mb or
  286. # 64mb, depending on the bios) in ax.
  287. mem88:

  288. #endif
  289.     movb    $0x88, %ah           -->0x88 int 0x15 中断获取扩展内存:即meory_size-1M大小的内存
  290.     int    $0x15
  291.     movw    %ax, (2)             -->将memory的信息放在0x90002处

  292. # Set the keyboard repeat rate to the max
  293.     movw    $0x0305, %ax
  294.     xorw    %bx, %bx
  295.     int    $0x16

  296. # Check for video adapter and its parameters and allow the
  297. # user to browse video modes.
  298.     call    video       -->setup模块是setup.s+video.S一起生成的
  299.                        
  300. //将第一块硬盘的参数表放在0x90080处   -->这个地方跟linux0.11是一样的,所以好熟悉好轻松
  301. # Get hd0 data...
  302.     xorw    %ax, %ax
  303.     movw    %ax, %ds
  304.     ldsw    (4 * 0x41), %si
  305.     movw    %cs, %ax            # aka SETUPSEG
  306.     subw    $DELTA_INITSEG, %ax        # aka INITSEG
  307.     pushw    %ax
  308.     movw    %ax, %es
  309.     movw    $0x0080, %di
  310.     movw    $0x10, %cx
  311.     pushw    %cx
  312.     cld
  313.     rep
  314.     movsb

  315. //将第二块硬盘的参数表放在0x90090处
  316. # Get hd1 data...
  317.     xorw    %ax, %ax
  318.     movw    %ax, %ds
  319.     ldsw    (4 * 0x46), %si
  320.     popw    %cx
  321.     popw    %es
  322.     movw    $0x0090, %di
  323.     rep
  324.     movsb
  325. //检查系统是不是有两个硬盘,如果没有把表2清0
  326. # Check that there IS a hd1 :-)
  327.     movw    $0x01500, %ax
  328.     movb    $0x81, %dl
  329.     int    $0x13
  330.     jc    no_disk1
  331.     
  332.     cmpb    $3, %ah
  333.     je    is_disk1

  334. no_disk1:
  335.     movw    %cs, %ax            # aka SETUPSEG
  336.     subw    $DELTA_INITSEG, %ax         # aka INITSEG
  337.     movw    %ax, %es
  338.     movw    $0x0090, %di
  339.     movw    $0x10, %cx
  340.     xorw    %ax, %ax
  341.     cld
  342.     rep
  343.     stosb
  344. is_disk1:
  345. # check for Micro Channel (MCA) bus
  346.     movw    %cs, %ax            # aka SETUPSEG
  347.     subw    $DELTA_INITSEG, %ax        # aka INITSEG
  348.     movw    %ax, %ds
  349.     xorw    %ax, %ax
  350.     movw    %ax, (0xa0)            # set table length to 0
  351.     movb    $0xc0, %ah
  352.     stc
  353.     int    $0x15                # moves feature table to es:bx
  354.     jc    no_mca

  355.     pushw    %ds
  356.     movw    %es, %ax
  357.     movw    %ax, %ds
  358.     movw    %cs, %ax            # aka SETUPSEG
  359.     subw    $DELTA_INITSEG, %ax        # aka INITSEG
  360.     movw    %ax, %es
  361.     movw    %bx, %si
  362.     movw    $0xa0, %di
  363.     movw    (%si), %cx
  364.     addw    $2, %cx                # table length is a short
  365.     cmpw    $0x10, %cx
  366.     jc    sysdesc_ok

  367.     movw    $0x10, %cx            # we keep only first 16 bytes
  368. sysdesc_ok:
  369.     rep
  370.     movsb
  371.     popw    %ds
  372. no_mca:
  373. # Check for PS/2 pointing device
  374.     movw    %cs, %ax            # aka SETUPSEG
  375.     subw    $DELTA_INITSEG, %ax        # aka INITSEG
  376.     movw    %ax, %ds
  377.     movw    $0, (0x1ff)            # default is no pointing device
  378.     int    $0x11                # int 0x11: equipment list
  379.     testb    $0x04, %al            # check if mouse installed
  380.     jz    no_psmouse

  381.     movw    $0xAA, (0x1ff)            # device present
  382. no_psmouse:

  383. #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
  384. 没有定义直接省略
  385. #endif

  386. # Now we want to move to protected mode ...
  387.     cmpw    $0, %cs:realmode_swtch
  388.     jz    rmodeswtch_normal         -->进入rmodeswtch_normal

  389.     lcall    %cs:realmode_swtch

  390.     jmp    rmodeswtch_end

  391. rmodeswtch_normal:
  392.     pushw    %cs
  393.     call    default_switch        -->关中断

  394. rmodeswtch_end:
  395. # we get the code32 start address and modify the below 'jmpi'
  396. # (loader may have changed it)
  397.     movl    %cs:code32_start, %eax      -->这儿是bzImage,所以执行后eax=0x00100000
  398.     movl    %eax, %cs:code32            -->执行后code32由原先的0x1000改变为0x100000 

  399. # Now we move the system to its rightful place ... but we check if we have a
  400. # big-kernel. In that case we *must* not move it ...
  401.     testb    $LOADED_HIGH, %cs:loadflags
  402.     jz    do_move0            # .. then we have a normal low
  403.                         # loaded zImage
  404.                         # .. or else we have a high
  405.                         # loaded bzImage
  406.     jmp    end_move     # ... and we skip moving

  407. do_move0:
  408.     movw    $0x100, %ax            # start of destination segment
  409.     movw    %cs, %bp            # aka SETUPSEG
  410.     subw    $DELTA_INITSEG, %bp        # aka INITSEG
  411.     movw    %cs:start_sys_seg, %bx        # start of source segment
  412.     cld
  413. do_move:
  414.     movw    %ax, %es            # destination segment
  415.     incb    %ah                # instead of add ax,#0x100
  416.     movw    %bx, %ds            # source segment
  417.     addw    $0x100, %bx
  418.     subw    %di, %di
  419.     subw    %si, %si
  420.     movw     $0x800, %cx
  421.     rep
  422.     movsw
  423.     cmpw    %bp, %bx            # assume start_sys_seg > 0x200,
  424.                         # so we will perhaps read one
  425.                         # page more than needed, but
  426.                         # never overwrite INITSEG
  427.                         # because destination is a
  428.                         # minimum one page below source
  429.     jb    do_move

  430. end_move:
  431. # then we load the segment descriptors
  432.     movw    %cs, %ax            # aka SETUPSEG
  433.     movw    %ax, %ds
  434.         
  435. # Check whether we need to be downward compatible with version <=201
  436.     cmpl    $0, cmd_line_ptr
  437.     jne    end_move_self        # loader uses version >=202 features
  438.     cmpb    $0x20, type_of_loader
  439.     je    end_move_self        # bootsect loader, we know of it

  440. # Boot loader doesnt support boot protocol version 2.02.
  441. # If we have our code not at 0x90000, we need to move it there now.
  442. # We also then need to move the params behind it (commandline)
  443. # Because we would overwrite the code on the current IP, we move
  444. # it in two steps, jumping high after the first one.
  445.     movw    %cs, %ax
  446.     cmpw    $SETUPSEG, %ax
  447.     je    end_move_self

  448.     cli                    # make sure we really have
  449.                         # interrupts disabled !
  450.                         # because after this the stack
  451.                         # should not be used
  452.     subw    $DELTA_INITSEG, %ax        # aka INITSEG
  453.     movw    %ss, %dx
  454.     cmpw    %ax, %dx
  455.     jb    move_self_1

  456.     addw    $INITSEG, %dx
  457.     subw    %ax, %dx            # this will go into %ss after
  458.                         # the move
  459. move_self_1:
  460.     movw    %ax, %ds
  461.     movw    $INITSEG, %ax            # real INITSEG
  462.     movw    %ax, %es
  463.     movw    %cs:setup_move_size, %cx
  464.     std                    # we have to move up, so we use
  465.                         # direction down because the
  466.                         # areas may overlap
  467.     movw    %cx, %di
  468.     decw    %di
  469.     movw    %di, %si
  470.     subw    $move_self_here+0x200, %cx
  471.     rep
  472.     movsb
  473.     ljmp    $SETUPSEG, $move_self_here

  474. move_self_here:
  475.     movw    $move_self_here+0x200, %cx
  476.     rep
  477.     movsb
  478.     movw    $SETUPSEG, %ax
  479.     movw    %ax, %ds
  480.     movw    %dx, %ss
  481. end_move_self:                    # now we are at the right place
  482.     lidt    idt_48                --> 加载idt # load idt with 0,0
  483.     xorl    %eax, %eax            # Compute gdt_base
  484.     movw    %ds, %ax              # (Convert %ds:gdt to a linear ptr)
  485.     shll    $4, %eax
  486.     addl    $gdt, %eax            -->将gdt的地址填充到gdt_48的基地址处
  487.     movl    %eax, (gdt_48+2)
  488.     lgdt    gdt_48                -->加载gdt

  489. # that was painless, now we enable a20
  490.     call    empty_8042

  491.     movb    $0xD1, %al            # command write
  492.     outb    %al, $0x64
  493.     call    empty_8042

  494.     movb    $0xDF, %al            # A20 on
  495.     outb    %al, $0x60
  496.     call    empty_8042

  497. #
  498. #    You must preserve the other bits here. Otherwise embarrasing things
  499. #    like laptops powering off on boot happen. Corrected version by Kira
  500. #    Brown from Linux 2.2
  501. #
  502. //打开A20
  503.     inb    $0x92, %al            #
  504.     orb    $02, %al            # "fast A20" version
  505.     outb    %al, $0x92            # some chips have only this

  506. # wait until a20 really *is* enabled; it can take a fair amount of
  507. # time on certain systems; Toshiba Tecras are known to have this
  508. # problem. The memory location used here (0x200) is the int 0x80
  509. # vector, which should be safe to use.

  510.     xorw    %ax, %ax            # segment 0x0000
  511.     movw    %ax, %fs
  512.     decw    %ax                # segment 0xffff (HMA)
  513.     movw    %ax, %gs
  514. a20_wait:
  515.     incw    %ax                # unused memory location <0xfff0
  516.     movw    %ax, %fs:(0x200)        # we use the "int 0x80" vector
  517.     cmpw    %gs:(0x210), %ax        # and its corresponding HMA addr
  518.     je    a20_wait            # loop until no longer aliased

  519. # make sure any possible coprocessor is properly reset..
  520.     xorw    %ax, %ax
  521.     outb    %al, $0xf0
  522.     call    delay

  523.     outb    %al, $0xf1
  524.     call    delay

  525. # well, that went ok, I hope. Now we mask all interrupts - the rest
  526. # is done in init_IRQ().
  527.     movb    $0xFF, %al            # mask all interrupts for now
  528.     outb    %al, $0xA1
  529.     call    delay
  530.     
  531.     movb    $0xFB, %al            # mask all irq's but irq2 which
  532.     outb    %al, $0x21            # is cascaded

  533. # Well, that certainly wasn't fun :-(. Hopefully it works, and we don't
  534. # need no steenking BIOS anyway (except for the initial loading :-).
  535. # The BIOS-routine wants lots of unnecessary data, and it's less
  536. # "interesting" anyway. This is how REAL programmers do it.
  537. #
  538. # Well, now's the time to actually move into protected mode. To make
  539. # things as simple as possible, we do no register set-up or anything,
  540. # we let the gnu-compiled 32-bit programs do that. We just jump to
  541. # absolute address 0x1000 (or the loader supplied one),
  542. # in 32-bit protected mode.
  543. #
  544. # Note that the short jump isn't strictly needed, although there are
  545. # reasons why it might be a good idea. It won't hurt in any case.
  546. //打开PE位-->将cr0第0位置1
  547.     movw    $1, %ax                # protected mode (PE) bit
  548.     lmsw    %ax                # This is
  549.     jmp    flush_instr

  550. flush_instr:
  551.     xorw    %bx, %bx            # Flag to indicate a boot
  552.     xorl    %esi, %esi            # Pointer to real-mode code
  553.     movw    %cs, %si
  554.     subw    $DELTA_INITSEG, %si
  555.     shll    $4, %esi            # Convert to 32-bit pointer
  556. # NOTE: For high loaded big kernels we need a
  557. #    jmpi 0x100000,__KERNEL_CS
  558. #
  559. #    but we yet haven't reloaded the CS register, so the default size
  560. #    of the target offset still is 16 bit.
  561. # However, using an operant prefix (0x66), the CPU will properly
  562. #    take our 48 bit far pointer. (INTeL 80386 Programmer's Reference
  563. #    Manual, Mixing 16-bit and 32-bit code, page 16-6)

  564.     .byte 0x66, 0xea                 # prefix + jmpi-opcode
  565. code32:    .long    0x1000           # will be set to 0x100000
  566.                                      # for big kernels
  567.     .word    __KERNEL_CS          -->jmpi的汇编代码,会修改这个地方
  568.                                   -->同时这会加载gdt表进入保护模式
  569. # Here's a bunch of information about your current kernel..
  570. kernel_version:    .ascii    UTS_RELEASE
  571.         .ascii    " ("
  572.         .ascii    LINUX_COMPILE_BY
  573.         .ascii    "@"
  574.         .ascii    LINUX_COMPILE_HOST
  575.         .ascii    ") "
  576.         .ascii    UTS_VERSION
  577.         .byte    0

  578. # This is the default real mode switch routine.
  579. # to be called just before protected mode transition
  580. default_switch:
  581.     cli                    # no interrupts allowed !
  582.     movb    $0x80, %al            # disable NMI for bootup
  583.                         # sequence
  584.     outb    %al, $0x70
  585.     lret

  586. # This routine only gets called, if we get loaded by the simple
  587. # bootsect loader _and_ have a bzImage to load.
  588. # Because there is no place left in the 512 bytes of the boot sector,
  589. # we must emigrate to code space here.
  590. bootsect_helper:
  591.     cmpw    $0, %cs:bootsect_es
  592.     jnz    bootsect_second

  593.     movb    $0x20, %cs:type_of_loader
  594.     movw    %es, %ax
  595.     shrw    $4, %ax
  596.     movb    %ah, %cs:bootsect_src_base+2
  597.     movw    %es, %ax
  598.     movw    %ax, %cs:bootsect_es
  599.     subw    $SYSSEG, %ax
  600.     lret                    # nothing else to do for now

  601. bootsect_second:
  602.     pushw    %cx
  603.     pushw    %si
  604.     pushw    %bx
  605.     testw    %bx, %bx            # 64K full?
  606.     jne    bootsect_ex

  607.     movw    $0x8000, %cx            # full 64K, INT15 moves words
  608.     pushw    %cs
  609.     popw    %es
  610.     movw    $bootsect_gdt, %si
  611.     movw    $0x8700, %ax
  612.     int    $0x15
  613.     jc    bootsect_panic            # this, if INT15 fails

  614.     movw    %cs:bootsect_es, %es        # we reset %es to always point
  615.     incb    %cs:bootsect_dst_base+2        # to 0x10000
  616. bootsect_ex:
  617.     movb    %cs:bootsect_dst_base+2, %ah
  618.     shlb    $4, %ah                # we now have the number of
  619.                         # moved frames in %ax
  620.     xorb    %al, %al
  621.     popw    %bx
  622.     popw    %si
  623.     popw    %cx
  624.     lret

  625. bootsect_gdt:
  626.     .word    0, 0, 0, 0
  627.     .word    0, 0, 0, 0

  628. bootsect_src:
  629.     .word    0xffff

  630. bootsect_src_base:
  631.     .byte    0x00, 0x00, 0x01        # base = 0x010000
  632.     .byte    0x93                # typbyte
  633.     .word    0                # limit16,base24 =0

  634. bootsect_dst:
  635.     .word    0xffff

  636. bootsect_dst_base:
  637.     .byte    0x00, 0x00, 0x10        # base = 0x100000
  638.     .byte    0x93                # typbyte
  639.     .word    0                # limit16,base24 =0
  640.     .word    0, 0, 0, 0            # BIOS CS
  641.     .word    0, 0, 0, 0            # BIOS DS

  642. bootsect_es:
  643.     .word    0

  644. bootsect_panic:
  645.     pushw    %cs
  646.     popw    %ds
  647.     cld
  648.     leaw    bootsect_panic_mess, %si
  649.     call    prtstr
  650.     
  651. bootsect_panic_loop:
  652.     jmp    bootsect_panic_loop

  653. bootsect_panic_mess:
  654.     .string    "INT15 refuses to access high mem, giving up."

  655. # This routine checks that the keyboard command queue is empty
  656. # (after emptying the output buffers)
  657. #
  658. # Some machines have delusions that the keyboard buffer is always full
  659. # with no keyboard attached...
  660. #
  661. # If there is no keyboard controller, we will usually get 0xff
  662. # to all the reads. With each IO taking a microsecond and
  663. # a timeout of 100,000 iterations, this can take about half a
  664. # second ("delay" == outb to port 0x80). That should be ok,
  665. # and should also be plenty of time for a real keyboard controller
  666. # to empty.
  667. #

  668. empty_8042:
  669.     pushl    %ecx
  670.     movl    $100000, %ecx

  671. empty_8042_loop:
  672.     decl    %ecx
  673.     jz    empty_8042_end_loop

  674.     call    delay

  675.     inb    $0x64, %al            # 8042 status port
  676.     testb    $1, %al                # output buffer?
  677.     jz    no_output

  678.     call    delay
  679.     inb    $0x60, %al            # read it
  680.     jmp    empty_8042_loop

  681. no_output:
  682.     testb    $2, %al                # is input buffer full?
  683.     jnz    empty_8042_loop            # yes - loop
  684. empty_8042_end_loop:
  685.     popl    %ecx
  686.     ret

  687. # Read the cmos clock. Return the seconds in al
  688. gettime:
  689.     pushw    %cx
  690.     movb    $0x02, %ah
  691.     int    $0x1a
  692.     movb    %dh, %al            # %dh contains the seconds
  693.     andb    $0x0f, %al
  694.     movb    %dh, %ah
  695.     movb    $0x04, %cl
  696.     shrb    %cl, %ah
  697.     aad
  698.     popw    %cx
  699.     ret

  700. # Delay is needed after doing I/O
  701. delay:
  702.     outb    %al,$0x80
  703.     ret

  704. # Descriptor tables
  705. gdt:
  706.     .word    0, 0, 0, 0            # dummy
  707.     .word    0, 0, 0, 0            # unused

  708.     .word    0xFFFF                # 4Gb - (0x100000*0x1000 = 4Gb)
  709.     .word    0                # base address = 0
  710.     .word    0x9A00                # code read/exec
  711.     .word    0x00CF                # granularity = 4096, 386
  712.                         # (+5th nibble of limit)

  713.     .word    0xFFFF                # 4Gb - (0x100000*0x1000 = 4Gb)
  714.     .word    0                # base address = 0
  715.     .word    0x9200                # data read/write
  716.     .word    0x00CF                # granularity = 4096, 386
  717.                         # (+5th nibble of limit)
  718. idt_48:
  719.     .word    0                # idt limit = 0
  720.     .word    0, 0                # idt base = 0L
  721. gdt_48:
  722.     .word    0x8000                # gdt limit=2048,
  723.                         # 256 GDT entries

  724.     .word    0, 0                # gdt base (filled in later)

  725. # Include video setup & detection code

  726. #include "video.S"                    -->将video.S插入到了setup.S

  727. # Setup signature -- must be last
  728. setup_sig1:    .word    SIG1
  729. setup_sig2:    .word    SIG2

  730. # After this point, there is some free space which is used by the video mode
  731. # handling code to store the temporary mode table (not used by the kernel).

  732. modelist:

  733. .text
  734. endtext:
  735. .data
  736. enddata:
  737. .bss
  738. endbss:
3.2 总结一下head.S与video.S所形成的bsetup的作用
a. 获取memory的size放在0x90002处
b. 获取hd的参数放在0x90080-0x900A0处
c. 关中断,加载idt gdt,开启A20,打开PE位
d. 跳进保护模式-->0010:00100000处

四.一些问题的说明
4.1 关于bsetup的生成
a.在 ./linux-2.4.12/arch/i386/boot/Makefile中
  1. 69 setup.s: setup.S video.S Makefile $(BOOT_INCL) $(TOPDIR)/include/linux/version.h $(TOPDIR)/include/linux/compile.h
  2. 70 $(CPP) $(CPPFLAGS) -D__ASSEMBLY__ -traditional $(SVGA_MODE) $(RAMDISK) $< -o $@
看清楚,L70的规则是$<(第一个依赖文件)不是$^(所有的依赖文件)
b. 再看一下编译的过程
  1. make[1]: Entering directory `/work/os/linux-2.4.12/arch/i386/boot'
  2. gcc -E -D__KERNEL__ -I/work/os/linux-2.4.12/include -D__BIG_KERNEL__ -D__ASSEMBLY__ -traditional -DSVGA_MODE=NORMAL_VGA setup.S -o bsetup.
  3. as -o bsetup.o bsetup.s
  4. ld -m elf_i386 -Ttext 0x0 -s --oformat binary -e begtext -o bsetup bsetup.o
c. 的确是这样的vidoe.S没有参与编译,那vidoe.S中的内容是在什么地方添加进去的呢?
答案:是在setup.S的最后 #include "video.S",所以在编译时没有显示的出现video.S,但在查看bsetup.s时,会知道这个viode.S插入到了setup.S中。

4.2 如何兼容bzImage的内核与zImage的内核?
a. 在./linux-2.4.12/arch/i386/boot/setup.S中定义了code32_start,
如果在编译时指定用bzImage,则code32_start=0x100000,反之code32_start=0x1000
  1. 117 code32_start: # here loaders can put a different
  2. 118 # start address for 32-bit code.
  3. 119 #ifndef __BIG_KERNEL__
  4. 120 .long 0x1000 # 0x1000 = default for zImage
  5. 121 #else
  6. 122 .long 0x100000 # 0x100000 = default for big kernel
  7. 123 #endif

b. 在./linux-2.4.12/arch/i386/boot/setup.S中setup向内核的跳转的指令前加了一个标签code32
  1. 728 # NOTE: For high loaded big kernels we need a
  2. 729 # jmpi 0x100000,__KERNEL_CS
  3. 730 #
  4. 731 # but we yet haven't reloaded the CS register, so the default size
  5. 732 # of the target offset still is 16 bit.
  6. 733 # However, using an operant prefix (0x66), the CPU will properly
  7. 734 # take our 48 bit far pointer. (INTeL 80386 Programmer's Reference
  8. 735 # Manual, Mixing 16-bit and 32-bit code, page 16-6)
  9. 736
  10. 737 .byte 0x66, 0xea # prefix + jmpi-opcode
  11. 738 code32: .long 0x1000 # will be set to 0x100000
  12. 739 # for big kernels
  13. 740 .word __KERNEL_CS
c.在./linux-2.4.12/arch/i386/boot/setup.S中setup在运行期间动态的改变了这个跳转指令的值
  1. 549 rmodeswtch_end:
  2. 550 # we get the code32 start address and modify the below 'jmpi'
  3. 551 # (loader may have changed it)
  4. 552 movl %cs:code32_start, %eax
  5. 553 movl %eax, %cs:code32
 66 ea 00 10 00 00       ljmpw  $0x0,$0x1000
 66 ea 00 00 10 00       ljmpw  $0x0,$0x100000
 jmpf 0x0010:00100000      ; 66ea000010001000

阅读(2888) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~