Xv6内核分析(一)

gewenbin
gewenbin
gewenbin
188
文章
15
评论
2020年12月20日14:28:46 1 894

Bootloader引导

Bootloader部分的代码主要负责主核的启动,保护模式的设置等。代码主要在bootasm.S、bootmain.c中。

1.bootasm.S

bootasm.S主要工作就是设置CPU进入32位保护模式。

1.1 关主核CPU中断,清零各个段寄存器

Xv6内核分析(一)

1.2 使能A20地址线

为了访问1MB以上的内存空间,需要使能A20地址线。

Xv6内核分析(一)

1.3 设置GDT全局描述符表

Xv6内核分析(一)

GDTR是一个48位的寄存器,各个位域的意义如下:

Xv6内核分析(一)

其中32位线性基地址指的是GDT表的起始物理地址,表长度是GDT表的大小-1。来看看xv6用于启动引导的GDT内容:

Xv6内核分析(一)

GDT表中的每一项表示一个段描述符,用来表示一段内存的属性,每一项占8个字节:

Xv6内核分析(一)

特别注意的是:GDT表中第一项必须是空条目,否则会报错。

SEG_NULLASM和SEG_ASM的宏定义在asm.h中:

Xv6内核分析(一)

通过宏定义可知,黄色部分P=1,DPL=00,S=1,标明设置的是数据段或者代码段,非系统段。黄色部分G=1,D/B=1,表示段限长的单位是4KB。

SEG_ASM(type,base,lim)可以设置段类型,段基址,段大小。TYPE的具体设置如下:

Xv6内核分析(一)

再来看看xv6的GDT描述符项:

Xv6内核分析(一)

可以看出,代码段和数据段的基地址都是0,段大小是4GB。

1.4 使能保护模式

使能保护就是将CR0寄存器的PE位置1:

Xv6内核分析(一)

1.5 进入32位模式

最后一步进入32位模式,通过一个长跳转指令进入,这会使得处理器重新加载CS,这时CS中的值代表的是段选择子,段选择子的具体结构如下:

Xv6内核分析(一)

在xv6中的具体代码如下:

Xv6内核分析(一)

1.6 重新设置段寄存器

进入32位后的第一件事就是重新设置其他段寄存器:

Xv6内核分析(一)

如图所示,DS,ES,SS用的是数据段选择子,FS,GS段用的是空选择子。

1.7 设置栈指针

这里初始的栈指针被设置为0x7c00,也就是bootasm.S被加载的地方,然后就调用c函数bootmain:

Xv6内核分析(一)

2. bootmain.c

bootmain.c只干一件事,将elf格式的内核从硬盘加载到内存中,然后跳转到入口执行。

2.1 内核链接脚本

Xv6内核分析(一)

框里的地址表示的是内核代码段的链接(运行)地址,用AT()括起来的地址表示内核的加载地址即0x100000,表示内核的代码段、数据段等内容应该要拷贝到0x100000地址处,但是内核的链接地址是0x80100000,所以内核被启动时,会将虚拟地址0x80100000映射到0x100000地址处。

2.2 bootmain主要流程

Xv6内核分析(一)

  • 首先从磁盘的第一个扇区开始读取4KB的数据到0x10000地址处,内核是从磁盘第一个扇区开始存放的。
  • 校验是否是elf头。
  • 解析elf文件,将代码段和数据段等信息拷贝到对应的加载地址处。
  • 跳转到内核的入口代码处执行。

2.3 readseg函数实现

Xv6内核分析(一)

  • 首先根据起始地址pa和大小count计算出结束地址epa。
  • pa -= offset % SECTSIZE;这一句是为了计算出从硬盘拷贝扇区到内存中的实际起始地址。因为硬盘读数据是按扇区大小来读的,也就是每次至少512字节数据。但是offset并不一定是512的整数倍,所以要对pa做校正。同时,这里也有一个风险,那就是如果offset不是512倍数,那么pa就要往低地址处校正,但是在pa往下的地址中可能存在有用的数据,如果pa往下校正,就可能破坏这些有用的数据。
  • offset = (offset / SECTSIZE) + 1;计算offset在实际哪个硬盘分区中。offset表示的是离内核起始处的偏移,而内核是从硬盘第一个扇区开始的,所以要加一。
  • 然后就循环读数据,每次读一个扇区也就是512字节。注意这里有可能实际读的字节数比count多。

2.4 读硬盘数据流程

Xv6内核分析(一)

各个端口的解释:

Xv6内核分析(一)

  • 0x1F7端口表示硬盘的状态。
  • 0x1F2表示要操作的硬盘扇区数目。
  • 0x1F3~0x1F6表示起始操作的硬盘扇区号。
  • 0x1F0表示要写的或者要读的数据。

其中状态端口和扇区偏移端口的具体位域描述:

Xv6内核分析(一)

具体的硬盘操作知识请见《操作系统真相还原》3.5章节。

具体的读数据是通过insl函数完成的,由于每次通过数据端口只能读4个字节的数据,所以要读一个扇区的话要读(512 / 4)次。

2.5 处理elf文件

Xv6内核分析(一)

其中ph->paddr表示编译时为每个段分配的内存物理加载地址,起始地址在链接脚本中指定。

3. 内核加载后的内存分布

Xv6内核分析(一)

 

gewenbin
  • 本文由 发表于 2020年12月20日14:28:46
  • 转载请务必保留本文链接:http://www.databusworld.cn/9213.html
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

评论:1   其中:访客  1   博主  0
    • lrj lrj 4