0


引导启动程序——bootsect.s

写在前面:

  1. 亲爱的小伙伴你们好,不知道你有没有在学习操作系统的过程中遇到过这样那样的困难,很多次想要放弃,很多次怀疑自己的能力,但是请你相信“未来的路就在脚下,不要悲伤,不要害怕”,一定要“充满信心,期盼着明天!”。**请你一定要相信Tandy12356_,他是未来的你派过来拯救你的救星,他一定会将你带入一段美妙的操作系统学习之旅!**

起初我以为自己是玫瑰花,不会表达爱,不会表达自己的感情,所以小王子离开了这座星球,留下我自己。

Tandy12356_小课堂

  1. **现在让我们坐上哆啦A梦的时光机,一起穿越回到1991年,**那时候Linus还在公司里忙忙碌碌地给资本家打工,**我们来尝试一下能不能赶在Linus之前把bootsect.s写出来,说不定现在Linux就应该改名叫Tandy12356_OS呢!**
  2. 由于在此之前从来没有人成功设计出来如此复杂的操作系统,**所以我们必须有一颗清醒的头脑,同时要有大局观和整体观。**
  3. **同时我们应当清楚地意识到当前的形势,即从计算机上电开始到现在我们都做了哪些工作?**
  4. **答:目前为止好像什么都没做哎。**
  5. 好像事实确实如此(;д;),不过在上集当中,**我们已经把引导扇区的内容加载到了内存的0x7C00处,并且将PC指针指向了这段内存的位置**,此时此刻这一段程序完全可以正常运行了。所以说呢,在此之前**我们需要做的工作就是要好好编写这份引导启动程序即可**。
  6. 可是到目前为止还存在一个严重的问题,就是虽然我们的引导扇区的内容被加载到了内存当中,而且是实实在在地可以被计算机执行了,但是呢!包括操作系统核心模块在内的我们的一堆模块现在都还在哪里呀?
  7. 答:**现在包括核心模块在内的操作系统的一堆模块都还让我们撂在磁盘上面**,在那里孤零零地呆着呢。所以身先士卒渗透到内存中去打探风声的引导启动程序——**bootsect.s需要做的最重要的事情就是要把自家老帅——System模块从磁盘上读入到内存当中。**
  8. 听到这,同学们便一个个摩拳擦掌,撸起袖子准备大干一场了!可是您先别着急哈,把老帅请到内存当中可不是一件小事,万一出了差错可是掉脑袋的事。因此我们需要做出一些比较具体的规划,比如应该把老帅放到内存的什么位置?
  9. ** 答:这个问题暂时不重要。**因为不论将来System模块被放置在内存的哪个位置,我们都需要将bootsect.s移动到高地址处,**毕竟到时候内核部分的代码大军压境,规模十分浩大,很有可能在之后把我们的bootsect.s给覆盖掉。所以为了防止灾难发生,聪明的bootsect.s首先需要做的第一件事情就应当是将自己的512个字节腾挪到内存的0x90000处。**

Linux0.11/boot/bootsect.s 源码:

  1. BOOTSEG=0x07c0
  2. INITSEG=0x9000
  3. entry start ;关键字entry告诉链接器"程序入口"
  4. start:
  5. mov ax,#BOOTSEG ;
  6. mov ds,ax ;ds=0x07c0
  7. mov ax,#INITSEG ;
  8. mov es,ax ;es=0x9000
  9. mov cx,#256 ;移动的字数存放在cx当中,此处移动了256个字
  10. sub si,si ;清零
  11. sub di,di ;清零
  12. rep movw ;用于把内容从ds:si 复制es:di 以字位单位
  13. jmpi go,INITSEG ;间接跳转 即程序跳到9000:0 去继续执行 CS=INITSEGIP=go(偏移地址)
  1. 这段代码的功能其实很简单,**就是将内存中开始地址为DS:SI256个字移动到开始地址为ES:DI的地方**,这个移动就是解释执行rep movw指定的结果,其中w就是要移动一个字,对应两个字节,**所以总共要移动512个字节,这正好是一个扇区的大小。因此这段程序就是将内存0X7C00处的512个字节(正好就是bootsect.s的全部程序)移动到内存0x90000开始的一段内存中,这样移动的作用就是为读入系统核心代码腾出空间,同时避免了自身被覆盖的情况发生。**
  2. 由于到目前为止bootsect.s在前线孤军作战太久无法立即等来system大军,于是机智的它决定向友军setup.s进行求助,以便将来把系统启动这项宏伟的使命交给下一任年轻的程序来做。**可是现在的问题是我们的友军setup.s也仍驻留在磁盘当中,因此bootsect需要做的第二件事毫无疑问就是将setup.s从磁盘读入到内存当中。**

Linux0.11/boot/bootsect.s 源码:

  1. SETUPLEN=4
  2. go: mov ax,cs
  3. mov ds,ax
  4. mov es,ax
  5. ! put stack at 0x9ff00.
  6. mov ss,ax
  7. mov sp,#0xFF00 ! arbitrary value >>512
  8. ! load the setup-sectors directly after the bootblock.
  9. ! Note that 'es' is already set up.
  10. load_setup:
  11. mov dx,#0x0000 ! drive 0, head 0
  12. mov cx,#0x0002 ! sector 2, track 0
  13. mov bx,#0x0200 ! address = 512, in INITSEG
  14. mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors
  15. int 0x13 ! read it
  16. jnc ok_load_setup ! ok - continue
  17. mov dx,#0x0000
  18. mov ax,#0x0000 ! reset the diskette
  19. int 0x13
  1. **下面继续由Tandy小熊来带领大家一起分析一下这段代码吧!**

** 首先我们需要明确:这段代码的核心是语句是什么?**

  1. **答:"int0x13",即调用0x13号中断。这是一个BIOS中断**,(BIOS的每项输入输出功能,都·是以中断调用的形式提供给用户的)查阅BIOS手册不难发现,**这是一个读写磁盘的中断调用。因此我们不难想到,这段代码就是用来从磁盘上读入操作系统的setup模块。**

** 具体调用0x13号中断时,寄存器AH=0x02表示要读磁盘的内容到内存,寄存器AL=0x04表示要读入4个扇区,寄存器CL=0x02表示要读的磁盘扇区从2号扇区开始,寄存器CH=00表示要读的磁盘扇区所在的柱面号为0,寄存器DH=0x00表示要读的磁盘扇区所在的磁头号为0,寄存器DL=0x00表示要读扇区所在的驱动号为0,寄存器ES:BX合在一起形成的内存地址表示要从磁盘读入的内容要放到的内存起始地址,此处是0x90200正好是移动到bootsect.s的后面因此这段代码的含义就是将0号驱动器中从0号柱面0号磁头2号扇区开始的4个扇区读入到内存当中,放到bootsect.s后面。这4个扇区的内容又是什么?这也是操作系统要编写的另外一个重要的文件,即setup.s。**

** OK,下面我们来一起梳理一下,到目前为止的启动过程。**

  1. **1) BIOS读入操作系统的第1个扇区bootsect.s文件,然后执行bootsect.s文件.**

** 2) bootsect.s继续读入操作系统的setup.s文件,将来交给setup.s来执行,在其中会完成一些操作系统设置的工作。**

** 3) 接下来bootsect.s还要读入操作系统的主体模块system,setup.s执行完后,就开始正式执行我们的系统模块system当中的代码了。**

  1. ** 到现在我们对操作系统引导已经有了一个初步认识,概括起来一句话:就是将操作系统对应的程序代码读入到内存的指定位置。**

** ** OKOK,到现在我们可以说已经做了许多了不起的事情了,可是到目前为止我们还没有看到自己写的操作系统给自己做出过什么回应,谁知道这孩子是不是亲生的呢?所以我们需要在屏幕上输出一些信息证明这真的是我们自己写的系统,当然了最重要的是给用户一个提示,还记得我们在上集中提到的BIOS自检吗?也就是说如果我们能够在显示器上打印出来我们自己的Logo,就说明这台计算机的硬件设备是完好无损的,只需要对自己的软件系统进行检查就好了!

** 下面就由Tandy小熊带领大家一起来分析一下如何打印出这个Logo吧!**

** 这将是我们在自己的计算机上做出的第一个伟大创举!**

** **

Linux0.11/boot/bootsect.s源码:

  1. ok_load_setup:
  2. ! Print some inane message
  3. mov ah,#0x03 ! read cursor pos
  4. xor bh,bh
  5. int 0x10
  6. mov cx,#24
  7. mov bx,#0x0007 ! page 0, attribute 7 (normal)
  8. mov bp,#msg1
  9. mov ax,#0x1301 ! write string, move cursor
  10. int 0x10
  11. sectors:
  12. .word 0
  13. msg1:
  14. .byte 13,10
  15. .ascii "Loading system ..."
  16. .byte 13,10,13,10
  17. .org 508
  18. root_dev:
  19. .word ROOT_DEV
  20. boot_flag:
  21. .word 0xAA55

** 首先我们仍然需要明确:这段代码的核心是语句是什么?**

  1. **答:这段代码主要调用了BIOSint 0x10号中断。**
  2. **该中断的作用是在屏幕上输出信息**,当用寄存器AH=0x03(AH中的值通常被称为BIOS的调用功能号)来调用BIOS中断0X10时,**会取出当前光标的位置,之后再调用BIOS中断int 0x10时就会在屏幕上输出信息,其中ES:BP用来说明输出字符串所在的内存地址,DH,DL给出屏幕输出的光标位置.。**
  3. **语句"mov bp #msg"告诉BIOS要输出的信息位置放在标号为msg的地方**,根据定义就是一段包括"Loading system..."31310在内的24个字符,当然ES应当设置成合适的段地址,此处的ES应该是0x9000,**"mov cx,#24"语句表示要显示24个字符,寄存器BL=0x07用来设置字符的属性,其中7表示显示正常的黑底白字,当然可以通过修改这个值来显示出红底或者闪烁效果等。**
  4. 现在屏幕上会输出"Loading system..."当然是夹在两个回车换行之间,**如果通过精心设计,还可以在屏幕上输出各种漂亮的图案,比如windows的窗口。**因此到现在为止,我们就知道了计算机的开机界面是从哪里来的了。

  1. 等下,俺狼人杀的身份是不是暴露了?^_^
  2. **Loading system...顾名思义,接下来就是要从磁盘上读入操作系统的主体部分,但是在读取之前要获得一些磁盘参数,比如每个磁道上的扇区个数等。因为现在要读的内容通常要比一个磁道的容量大得多,所以需要应用这些参数计算出所需要读取的磁道数量等。下面的代码就是要通过功能号AH=0x08来调用0x13BIOS中断,从而获取每个磁道的扇区个数。**
  1. !获取磁道容量
  2. mov dl,#0x00
  3. mov ah,#0x08
  4. int 0x13
  5. mov ch,#0x00
  6. and cl,#0x3F
  7. mov sectors,cx
  8. sectors: .word 0
  1. ** 接下来的工作是设置ES:BX,这个内存地址是用来存放从磁盘中读出的内容**,因此下面的代码段设**置了操作系统的主体代码在内存当中的开始位置。此处设置为0x10000**,实际上bootsect.s0x7C00处移动到0x90000处,**就是为这里读入系统主体部分代码腾出空间的!**
  1. SYSSEG=0x1000
  2. mov ax,#SYSSEG
  3. mov es,ax
  4. xor bx,bx
  1. 一切准备就绪以后,现在开始要真正读入系统模块的代码了,**虽然看起来很长,但是实际上它的核心功能却很简单,就是利用一个循环实现一个磁道一个磁道地读入,同时磁道的读入地址ESBX跟着不断地往前移动,直到系统模块被全部读入。**因此循环条件就是要判断AX是否大于ENDSEG=SYSSEG+SYSSIZE,由于AX初始化为SYSSIZE,所以如果AX增加了SYSSIZE即系统模块尺寸以后,磁盘读取工作就结束了,**而SYSSIZE是在编写操作系统的时候候写在bootsect.s里面的。**

Linux0.11/boot/bootsect.s源码:

  1. read_it:
  2. mov ax,es
  3. test ax,#0x0fff
  4. die: jne die ! es must be at 64kB boundary
  5. xor bx,bx ! bx is starting address within segment
  6. rp_read:
  7. mov ax,es
  8. cmp ax,#ENDSEG ! have we loaded all yet?
  9. jb ok1_read
  10. ret
  11. ok1_read:
  12. seg cs
  13. mov ax,sectors
  14. sub ax,sread
  15. mov cx,ax
  16. shl cx,#9
  17. add cx,bx
  18. jnc ok2_read
  19. je ok2_read
  20. xor ax,ax
  21. sub ax,bx
  22. shr ax,#9
  23. ok2_read:
  24. call read_track
  25. sread: .word 1+SETUPLEN ! sectors read of current track
  26. head: .word 0 ! current head
  27. track: .word 0 ! current track
  1. 至此,bootsect.s的核心功能我们基本上已经实现了!

** 下面让Tandy小熊带领大家来回顾一下bootsect.s都做了哪些事吧^_^**

  1. **1 首先将自身移动到内存的绝对地址0X90000处并继续执行**

** 2)然后利用BIOS int 0x13号中断,将磁盘上第2个扇区开始的4个扇区的即setup模块加载到内存紧挨着bootsect.s后面**

** 3)紧接着在屏幕上打出"Loading system..."字符串,用来提示用户硬件检测通过**

** 4)最后在磁盘上再把setup模块后面的System的一堆模块加载到内存0x10000处**

  1. 下面我们的操作系统应该执行什么操作,相信大家应该都能推测出来了吧。
  2. 没错!到目前为止bootsect.s已经将系统启动的接力棒传递给了setup模块,**毫无疑问下面很快就要跳转到setup.s进行执行操作,至此bootsect.s全面退出了历史的舞台。 **
  3. OK,今天的学习就要告一段落了,**以后Tandy小熊可能也要忙于自己的学业啦,文章更新速度可能会有所减慢^_^,不过如果大家喜欢小熊的文章,也可以私信小熊来催更呢!!!**

写在后面:

  1. ** **不知道大家有没有喜欢Tandy12356_的文章呢? ( ・´ω`・ ),如果喜欢的话希望大家能点个关注或者收藏的啦!

** ** 在这里给大家预告下期内容啦:随着bootsect.s退出历史舞台,一段年轻的程序setup.s已经跃跃欲试地要去干一番事业了。在下一回当中Tandy小熊会带大家一起追踪setup.s的足迹,以第一人称视角感受下setup.s到底为我们的OS加载做出了什么重大的贡献并为大家揭露这段年轻程序的一些不为人知的秘密!

** ** 如果大家喜欢的话,可不可以给个支持呢,您的点赞收藏就是Tandy小熊创作下去的动力捏!!

  1. ** ( ̀ω•́ )✧( ̀ω•́ )✧( ̀ω•́ )✧**

后记:

以下是Linux0.11/boot/bootsect.s的全部源码:

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

本文转载自: https://blog.csdn.net/Tandy12356_/article/details/124352858
版权归原作者 Tandy12356_ 所有, 如有侵权,请联系我们删除。

“引导启动程序——bootsect.s”的评论:

还没有评论