购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.1.1 加载并执行setup

Linux内核将setup放到bootsect之后,使用BIOS中断将setup加载进内存。先来修改bootsect,加载setup模块,然后再跳转进setup模块执行,加载setup模块的具体实现如代码清单2-1所示。

代码清单2-1加载setup模块

1  SETUPLEN=4

2

3  BOOTSEG=0x7c0

4

5  INITSEG=0x9000

6

7  SETUPSEG=0x9020

8

9  SYSSEG=0x1000

10

11 ENDSEG=SYSSEG+SYSSIZE

12

13 ROOT_DEV=0x000

14

15 .code16

16 .text

17

18 .global_start

19 _start:

20

21  jmpl$BOOTSEG,$start2

22

23 start2:

24  movw $BOOTSEG,%ax

25  movw %ax,%ds

26  movw $INITSEG,%ax

27  movw %ax,%es

28  movw $256,%cx

29  subw%si,%si

30  subw%di,%di

31

32  rep

33  movsw

34

35  jmpl$INITSEG,$go

36

37 go:

38  movw %cs,%ax

39  movw %ax,%ds

40  movw %ax,%es

41  movw %ax,%ss

42  movw $0xFF00,%sp

43

44  load_setup:

45  movw $0x0000,%dx

46  movw $0x0002,%cx

47  movw $0x0200,%bx

48  movb $SETUPLEN,%al

49  movb $0x02,%ah

50  int $0x13

51  jncok_load_setup

52  movw $0x0000,%dx

53  movw $0x0000,%ax

54  int $0x13

55  jmpload_setup

56

57 ok_load_setup:

58

59  movw $msg,%ax

60  movw %ax,%bp

61  movw $0x01301,%ax

62  movw $0x0c,%bx

63  movw $21,%cx

64  movb $0,%dl

65  int $0x010

66

67  jmpl$SETUPSEG,$0

68

69 msg:

70 .ascii"Setup has been loaded"

71

72 .org 508

73 root_dev:

74  .word ROOT_DEV

75 boot_flag:

76  .word 0xaa55

在上述代码中:第24行到第33行将bootsect搬移到0x90000(INITSEG左移4位)。这是因为0x7c00是一个比较低的地址,这一块内存将来会被覆盖,所以bootsect一开始就把自己搬到一块更高的地址。

通过第35行的跳转指令进入新地址里的bootsect继续执行,也就是第38行。注意,第35行的跳转会使cs寄存器变成0x9000(即INITSEG),所以第38行到第42行的代码就是为了把其他段寄存器也设置成0x9000,顺便把栈指针寄存器sp也设置了。在接下来的很长一段时间内,内核都不会使用到栈,但让sp寄存器指向合理的位置是一个好的编程习惯。

接下来的代码使用了BIOS的0x13号中断,从软盘中将setup模块加载进内存。0x13号中断的入参通过各个寄存器传递。

1)ah寄存器是功能号,其值为02,代表读磁盘扇区到内存。

2)al寄存器的值代表了需要读出的扇区数量。

3)ch代表柱面号的低8位。

4)cl的0~5位代表开始扇区号,6~7位代表磁道号的高2位。

5)dh代表磁头号。

6)dl代表驱动器号,0代表软盘,7代表硬盘,es:bx指向数据缓冲区。如果读取出错,则CF标志置位。

综上,第45行至第50行代码的作用就是从0号柱面、0号磁头的第2个扇区开始,连续读取4个扇区数据进内存,数据会被存储在0x90200(INITSEG:0x200)。如果成功,则CF标志不会置位,且第51行的条件跳转语句就会跳到ok_load_setup处继续执行;如果不成功,则使用0x13号中断重置磁盘(ah=0),然后再跳转回ok_load_setup处循环执行。

第59行至第65行使用了0x010(对应的十六进制为0x10)号中断向屏幕上打印“Setup has been loaded”。最后通过第67行的跳转进入setup模块执行。

接下来的代码便是setup模块(见代码清单2-2)的入口。这段代码的功能非常简单,就是在屏幕上打印提示信息,表示setup模块已经开始真正执行了。

代码清单2-2 setup模块

1 .code16

2 .text

3 .globl_start_setup

4

5 _start_setup:

6   movw %cs,%ax

7   movw %ax,%ds

8   movw %ax,%es

9   10movw $setup_msg,%ax

11   movw %ax,%bp

12   movw $0x01301,%ax

13   movw $0x0c,%bx

14   movw $16,%cx

15   movb $3,%dh

16   movb $0,%dl

17   int $0x010

18 setup_msg:

19  .ascii"setup is running"

0x10号中断已经出现过很多次了,它只是简单地向屏幕上打印一行信息。这段代码就不再过多解释了。增加了一个新的文件setup.S,makefile文件也要做相应的修改,将这个文件编译进内核镜像,如代码清单2-3所示。

代码清单2-3 makefile

1  AS:=as

2  LD:=ld-m elf_x86_64

3

4  LDFLAG:=-Ttext 0x0-s--oformat binary

5

6  image:linux.img

7

8  linux.img:tools/build bootsect setup

9   ./tools/build bootsect setup>$@

10

11 tools/build:tools/build.c

12     gcc-o$@ $<

13

14 bootsect:bootsect.o

15   $(LD) $(LDFLAG)-o$@ $<

16

17 bootsect.o:bootsect.S

18   $(AS)-o$@ $<

19

20 setup:setup.o

21     $(LD) $(LDFLAG)-e_start_setup-o$@ $<

22

23 setup.o:setup.S

24     $(AS)-o$@ $<

25 clean:

26    rm-f *.o

27    rm-f bootsect

28    rm-f setup

29    rm-f tools/build

30    rm-f linux.img

上面的构建文件里使用了一个名为build的工具,它的作用是将bootsect和setup模块拼接在一起,并且保证bootsect的长度为512B,而setup的长度是2KB,所以经过build工具的拼接,生成的linux.img文件的大小一定是准确的2560B,一共是5个扇区。因为build.c文件代码比较长,逻辑又非常简单,所以这里就不再引录了。读者可以在代码仓库中找到相应版本的build.c文件。

构建并运行以后,可以看到屏幕上正确地显示了两行文字。 pr4zODuOTgsQeyqjphh0dy/33QLL1MKYHXxuZH8Pu2FNj46eqvP0OcZoOPuyzQjN

点击中间区域
呼出菜单
上一章
目录
下一章
×