Linux0.11内核--启动引导代码分析bootsect.s

Linux内核中的系统启动引导代码位于/boot目录下

但是,由于。Linus当时是在MINIX系统上开发Linux的,最初MINIX系统上还没有移植gas程序,因此Linus就使用了MINIX系统上的as86。

bootsect需要是16位的实模式程序。目前gas汇编器也支持16位的编译。所以现在也可以直接用as编译器直接编译。

下面是用at&t格式改写的bootsect.s代码

  1.     .code16  
  2. # rewrite with AT&T syntax by falcon <wuzhangjin@gmail.com> at 081012  
  3. #  
  4. # SYS_SIZE is the number of clicks (16 bytes) to be loaded.  
  5. # 0x3000 is 0x30000 bytes = 196kB, more than enough for current  
  6. # versions of linux  
  7. #  
  8.     .equ SYSSIZE, 0x3000  
  9. #  
  10. #   bootsect.s      (C) 1991 Linus Torvalds  
  11. #  
  12. # bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves  
  13. # iself out of the way to address 0x90000, and jumps there.  
  14. #  
  15. # It then loads 'setup' directly after itself (0x90200), and the system  
  16. # at 0x10000, using BIOS interrupts.   
  17. #  
  18. # NOTE! currently system is at most 8*65536 bytes long. This should be no  
  19. # problem, even in the future. I want to keep it simple. This 512 kB  
  20. # kernel size should be enough, especially as this doesn't contain the  
  21. # buffer cache as in minix  
  22. #  
  23. # The loader has been made as simple as possible, and continuos  
  24. # read errors will result in a unbreakable loop. Reboot by hand. It  
  25. # loads pretty fast by getting whole sectors at a time whenever possible.  
  26.     .global _start, begtext, begdata, begbss, endtext, enddata, endbss  
  27.     .text  
  28.     begtext:  
  29.     .data  
  30.     begdata:  
  31.     .bss  
  32.     begbss:  
  33.     .text  
  34.     .equ SETUPLEN, 4        # nr of setup-sectors  
  35.     .equ BOOTSEG, 0x07c0        # original address of boot-sector  
  36.     .equ INITSEG, 0x9000        # we move boot here - out of the way  
  37.     .equ SETUPSEG, 0x9020       # setup starts here  
  38.     .equ SYSSEG, 0x1000     # system loaded at 0x10000 (65536).  
  39.     .equ ENDSEG, SYSSEG + SYSSIZE   # where to stop loading  
  40. # ROOT_DEV: 0x000 - same type of floppy as boot.  
  41. #       0x301 - first partition on first drive etc  
  42.     .equ ROOT_DEV, 0x301  
  43.     ljmp    $BOOTSEG, $_start  
  44. _start:  
  45.     mov $BOOTSEG, %ax  
  46.     mov %ax, %ds  
  47.     mov $INITSEG, %ax  
  48.     mov %ax, %es  
  49.     mov $256, %cx  
  50.     sub %si, %si  
  51.     sub %di, %di  
  52.     rep   
  53.     movsw  
  54.     ljmp    $INITSEG, $go  
  55. go: mov %cs, %ax  
  56.     mov %ax, %ds  
  57.     mov %ax, %es  
  58. # put stack at 0x9ff00.  
  59.     mov %ax, %ss  
  60.     mov $0xFF00, %sp        # arbitrary value >>512  
  61. # load the setup-sectors directly after the bootblock.  
  62. # Note that 'es' is already set up.  
  63. load_setup:  
  64.     mov $0x0000, %dx        # drive 0, head 0  
  65.     mov $0x0002, %cx        # sector 2, track 0  
  66.     mov $0x0200, %bx        # address = 512, in INITSEG  
  67.     .equ    AX, 0x0200+SETUPLEN  
  68.     mov     $AX, %ax        # service 2, nr of sectors  
  69.     int $0x13           # read it  
  70.     jnc ok_load_setup       # ok - continue  
  71.     mov $0x0000, %dx  
  72.     mov $0x0000, %ax        # reset the diskette  
  73.     int $0x13  
  74.     jmp load_setup  
  75. ok_load_setup:  
  76. # Get disk drive parameters, specifically nr of sectors/track  
  77.     mov $0x00, %dl  
  78.     mov $0x0800, %ax        # AH=8 is get drive parameters  
  79.     int $0x13  
  80.     mov $0x00, %ch  
  81.     #seg cs  
  82.     mov %cx, %cs:sectors+0  # %cs means sectors is in %cs  
  83.     mov $INITSEG, %ax  
  84.     mov %ax, %es  
  85. # Print some inane message  
  86.     mov $0x03, %ah      # read cursor pos  
  87.     xor %bh, %bh  
  88.     int $0x10  
  89.       
  90.     mov $38, %cx  
  91.     mov $0x0007, %bx        # page 0, attribute 7 (normal)  
  92.     #lea    msg1, %bp  
  93.     mov     $msg1, %bp  
  94.     mov $0x1301, %ax        # write string, move cursor  
  95.     int $0x10  
  96. # ok, we've written the message, now  
  97. # we want to load the system (at 0x10000)  
  98.     mov $SYSSEG, %ax  
  99.     mov %ax, %es        # segment of 0x010000  
  100.     call    read_it  
  101.     call    kill_motor  
  102. # After that we check which root-device to use. If the device is  
  103. # defined (#= 0), nothing is done and the given device is used.  
  104. # Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending  
  105. # on the number of sectors that the BIOS reports currently.  
  106.     #seg cs  
  107.     mov %cs:root_dev+0, %ax  
  108.     cmp $0, %ax  
  109.     jne root_defined  
  110.     #seg cs  
  111.     mov %cs:sectors+0, %bx  
  112.     mov $0x0208, %ax        # /dev/ps0 - 1.2Mb  
  113.     cmp $15, %bx  
  114.     je  root_defined  
  115.     mov $0x021c, %ax        # /dev/PS0 - 1.44Mb  
  116.     cmp $18, %bx  
  117.     je  root_defined  
  118. undef_root:  
  119.     jmp undef_root  
  120. root_defined:  
  121.     #seg cs  
  122.     mov %ax, %cs:root_dev+0  
  123. # after that (everyting loaded), we jump to  
  124. # the setup-routine loaded directly after  
  125. # the bootblock:  
  126.     ljmp    $SETUPSEG, $0       #jump to  0x9020:0000(setup.s 程序的开始处)  
  127. # This routine loads the system at address 0x10000, making sure  
  128. # no 64kB boundaries are crossed. We try to load it as fast as  
  129. # possible, loading whole tracks whenever we can.  
  130. #  
  131. # in:   es - starting address segment (normally 0x1000)  
  132. #  
  133. sread:  .word 1+ SETUPLEN   # sectors read of current track  
  134. head:   .word 0         # current head  
  135. track:  .word 0         # current track  
  136. read_it:  
  137.     mov %es, %ax  
  138.     test    $0x0fff, %ax  
  139. die:    jne     die         # es must be at 64kB boundary  
  140.     xor     %bx, %bx        # bx is starting address within segment  
  141. rp_read:  
  142.     mov     %es, %ax  
  143.     cmp     $ENDSEG, %ax        # have we loaded all yet?  
  144.     jb  ok1_read  
  145.     ret  
  146. ok1_read:  
  147.     #seg cs  
  148.     mov %cs:sectors+0, %ax  
  149.     sub sread, %ax  
  150.     mov %ax, %cx  
  151.     shl $9, %cx  
  152.     add %bx, %cx  
  153.     jnc     ok2_read  
  154.     je  ok2_read  
  155.     xor     %ax, %ax  
  156.     sub     %bx, %ax  
  157.     shr     $9, %ax  
  158. ok2_read:  
  159.     call    read_track  
  160.     mov     %ax, %cx  
  161.     add     sread, %ax  
  162.     #seg cs  
  163.     cmp     %cs:sectors+0, %ax  
  164.     jne     ok3_read  
  165.     mov     $1, %ax  
  166.     sub     head, %ax  
  167.     jne     ok4_read  
  168.     incw    track   
  169. ok4_read:  
  170.     mov %ax, head  
  171.     xor %ax, %ax  
  172. ok3_read:  
  173.     mov %ax, sread  
  174.     shl $9, %cx  
  175.     add %cx, %bx  
  176.     jnc rp_read  
  177.     mov %es, %ax  
  178.     add $0x1000, %ax  
  179.     mov %ax, %es  
  180.     xor %bx, %bx  
  181.     jmp rp_read  
  182. read_track:  
  183.     push    %ax  
  184.     push    %bx  
  185.     push    %cx  
  186.     push    %dx  
  187.     mov track, %dx  
  188.     mov sread, %cx  
  189.     inc %cx  
  190.     mov %dl, %ch  
  191.     mov head, %dx  
  192.     mov %dl, %dh  
  193.     mov $0, %dl  
  194.     and $0x0100, %dx  
  195.     mov $2, %ah  
  196.     int $0x13  
  197.     jc  bad_rt  
  198.     pop %dx  
  199.     pop %cx  
  200.     pop %bx  
  201.     pop %ax  
  202.     ret  
  203. bad_rt: mov $0, %ax  
  204.     mov $0, %dx  
  205.     int $0x13  
  206.     pop %dx  
  207.     pop %cx  
  208.     pop %bx  
  209.     pop %ax  
  210.     jmp read_track  
  211. #/*  
  212. # * This procedure turns off the floppy drive motor, so  
  213. # * that we enter the kernel in a known state, and  
  214. # * don't have to worry about it later.  
  215. # */  
  216. kill_motor:  
  217.     push    %dx  
  218.     mov $0x3f2, %dx  
  219.     mov $0, %al  
  220.     outsb  
  221.     pop %dx  
  222.     ret  
  223. sectors:  
  224.     .word 0  
  225. msg1:  
  226.     .byte 13,10  
  227.     .ascii "Loading system ... compiled by yan!"  
  228.     .byte 13,10,13,10  
  229.     .org 508  
  230. root_dev:  
  231.     .word ROOT_DEV  
  232. boot_flag:  
  233.     .word 0xAA55  
  234.       
  235.     .text  
  236.     endtext:  
  237.     .data  
  238.     enddata:  
  239.     .bss  
  240.     endbss:  
 

 

系统加电启动时,首先由BIOS读取bootsect代码到地址0x7c00处,然后首先将自己移动至地址0x9000处,大小是256个字,相当于512

字节。

 

如何实现内存块的复制,运用ds:si->es:di的方式,将ds所指的段移 到ps所指的段中去,cx中指定移动的次数。gas中的段间跳转指令 ljmp A,B,其中A代表目标段地址,B代表A段中的偏移值。

 

然后跳转到移动后代码的下一条语句执行,这是由于

ljmp $INITSEG, $go

跳转后,该指令自动修改CS代码段的值。

移动代码后,由于这是会使用pop,push,call等指令,需要使用堆栈段,所以需要重新设置段基址,0x9ff00。

然后通过BIOS的INT 0x13中断来从磁盘加载setup模块。

 

***************************************

BIOS中断INT 0x13读磁盘功能

读扇区:

ah=0x02 - 读磁盘扇区到内存;a1=需要读出的扇区数量

ch=磁道(柱面)号的低8位; c1=开始扇区(位0-5),磁道号高2 位(位6-7);

dh=磁头号; d1=驱动器号(如果是硬盘则位7要 置位),0为当前A驱动器

es:bx->指向数据缓冲区;如果出错则CF标志置位。

***************************************
然后再用BIOS的INT 0x13中断读磁盘参数
***************************************
BIOS中断INT 0x13取磁盘驱动器参数功能
ah=0x08 dl=驱动器号(如果是硬盘则要置位7为1)
返回信息
如果出错则CF置位,并且ah=状态码
ah=0,al=0,bl=驱动器类型(AT/PS2)
ch=最大磁道号的低8位,cl=每磁道最大扇区数(位0-5),最大磁道 号高2位(位6-7)
dh=最大磁头数,dl=驱动器数量
es:di->软驱磁盘参数表
***************************************
然后利用BIOS的INT 0x10中断读取光标位置并且输出信息
***************************************
BIOS中断0x10,读光标位置功能
功能号ah=0x03
输入:bh=页号
返回:ch=扫描开始线;cl=扫描结束线;dh=行号(0x00顶端);dl= 列号(0x00最左边)
***************************************
***************************************
BIOS中断0x10,显示字符串
功能号ah=0x13
输入:al=放置光标的方式及规定属性,0x01表示使用bl中的属性 值,光标停留在字符串结尾处。es:bp此寄存器指向要显示的字符串 起始位置处。cx=显示的字符串字符数。bh=显示页面号;bl=字符属 性,7为normal,dh=行号,dl=列号。
从磁盘上都数据必须位于64KB边界开始处。因为实模式下的寄存器为 16位,所以最大寻址为2^16=64KB,即段限长也为64KB,一次性最大 能拷贝的字节数也是64KB。
***************************************
.org伪指令
.org 508
指令表示表示后面的指令从地址508开始,linux0.11初始化时根文件 系统所在设备号就存放在0x90508(0x90000是bootsect.s程序的开始 处)开始的两个字节处。供后面的程序main.c调用。
为什么是508而不是510呢,因为510和511这首个扇区的最后两个字节 必须存放0xaa55,作为该盘是有效引导扇区的标志,仅供bios中的程 序加载引导扇区时识别使用。
最后加载system到0x10000处。
下面read_it和kill_monitor两个子程序待续.......
下面是加载内存的模块分布和移动。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值