Xv6内核分析(二)

gewenbin
gewenbin
gewenbin
188
文章
15
评论
2020年12月20日15:13:20 评论 654

启动Xv6

1. Xv6入口代码

内核的入口代码在entry.S中,Xv6的内核是支持用grub引导的,grub引导的知识见此博客:https://blog.csdn.net/xiaolanmyself/article/details/16944175

1.1 entry.S

entry.S一开始就定义了用于grub引导的头部:

Xv6内核分析(二)

由Kernel.ld可以得知,内核的链接地址在0x80100000,系统的入口由_start标号地址指定,这个值会最终保存在elf文件中的entry字段中:

Xv6内核分析(二)

再来看看entry.S:

Xv6内核分析(二)

其中entry地址就是ld脚本中定义的0x80100000,但是在刚进入内核时,分页部件还没有启用,这时物理地址和虚拟地址相同,所以_start的值不能为0x80100000这么高内存的地址,需要转换为实际的物理加载地址,这个转换也很简单就是拿当前的虚拟地址减去0x80000000就是实际的物理地址了。在链接脚本中由ENTRY()指定的地址就是最终elf文件中的elf->entry值。

xv6的物理地址和虚拟地址映射关系如下:

Xv6内核分析(二)

1.2 使能分页功能

设置页目录地址,开启分页功能:

Xv6内核分析(二)

其中CR4寄存器的位域解释:

Xv6内核分析(二)

PSE位为0表示每页是4KB,为1表示每页是4MB。CR3寄存器的描述如下:

Xv6内核分析(二)

由于entrypgdir表示的是虚拟地址,所以需要转换成物理地址后才能写入CR3寄存器中。最后打开CR0中的PG和WP位,使能分页机制。

以上工作可以总结如下:

  • 设置页大小粒度
  • 设置页目录基址
  • 使能分页功能

1.3 页目录内容

entrypgdir在main.c文件结尾处定义:

Xv6内核分析(二)

页目录的物理地址必须是4KB对齐的。在内核初始化的时候,使用的页表是4MB大小的,这也是为什么将CR4寄存器中的PSE位设置为1的原因,目的的通知硬件可以使用4MB的页。

从图中可以看出,这个启动用的页表中只有两个条目。第一个条目将虚拟地址的0~4MB空间映射到物理地址的0~4MB上,也就是将内存低4MB空间进行平板映射;第二个条目是页目录中第512个位置,将内核起始虚拟地址的4MB空间也映射到低4MB的物理内存上。也就是低4MB空间的物理地址被映射到了2块虚拟地址中。

Xv6内核分析(二)

页目录和页表的具体条目解释如上图所示。将PS位置1表示一页是4MB大小。

1.4 内核映射关系

内核启动时的内存映射关系如下:

Xv6内核分析(二)

1.5 设置内核栈

回到entry.S,接着是设置内核栈地址,内核栈大小是4KB,栈顶是内核当前虚拟地址加上4KB,在代码中用.comm指定了一段内存空间作为栈空间:

Xv6内核分析(二)

comm用法如下:

Xv6内核分析(二)

内核栈设置好后,用间接跳转指令jmp跳转到main函数执行。之所以要用间接跳转指令是因为这时代码还在虚拟低地址处执行,但是内核编译时是按0x81000000高地址编译的,所以这里执行的指令必须都要是位置无关指令。在执行完jmp指令后,eip的值就是main的虚拟地址,内核也就真正的在高虚拟地址上运行了。

2. main函数

mian函数如下:

Xv6内核分析(二)

可以看出,都是一些初始化函数,这里我们先关心多核部分是如何启动的,也就是mpinit这个函数。

2.1 intel多处理器规范

intel的多处理规范:https://en.wikipedia.org/wiki/MultiProcessor_Specification

其中有这么一句话:由于大多数较新的机器都支持包含MPS功能的高级配置和电源接口(ACPI),因此MPS在很大程度上已被ACPI取代。MPS仍可用于计算机或不支持ACPI的操作系统。

MP配置表的知识可以参考这个博客:https://blog.csdn.net/stupid_haiou/article/details/46430749

多处理器的配置信息表如下:

Xv6内核分析(二)

2.2 MP Floating Pointer Structure

要想获取x86多处理器的信息,就必须首先找到MP Floating Pointer Structure这个结构体所在的位置,根据手册规范,这个结构体位置可以在如下位置寻找:

Xv6内核分析(二)

在mp.c中mpsearch函数的功能就是在这三个位置寻找MP Floating Pointer Structure,我们来看看:

Xv6内核分析(二)

首先,bios的数据区的物理地址是0x400开始,大小是256字节,如下:

Xv6内核分析(二)

Xv6内核分析(二)

mpsearch首先将0x400物理地址转换为虚拟地址,然后才能访问bios data。首先查找的是EBDA区域,通过真相还原可以看出,EBDA的物理地址在0x9fc00开始的地方。但是在mpsearch函数中并没有直接在0x9fc00地址处搜索,而是通过BDA区域先找到EBDA的地址,然后从这个地址处搜索:

Xv6内核分析(二)

bda的0xe和0xf偏移处的定义如下:

Xv6内核分析(二)

可知,这两个地方定义的就是EBDA所在的物理地址,注意,这里的单位表示的是16字节,所以在代码中拼出来的地址左移了4位。如果在EBDA不存在,那么就会到系统base mem内存的最后1KB区域内寻找:

Xv6内核分析(二)

bda的0x13和0x14偏移处的定义如下:

Xv6内核分析(二)

这个表示系统base mem的大小???,单位是1KB。如果在这个区域也没找到,那么只能在最后的0F0000h到0FFFFFh区间寻找了,如下:

Xv6内核分析(二)

2.3 mpsearch1函数

Xv6内核分析(二)

寻找MP Floating Pointer Structure的原理也很简单,判断条件只有两个:

  • 开始的四个字节为字符_MP_。
  • 整个结构的字节加起来结果为0(规范要求)。

MP头定义如下:

Xv6内核分析(二)

sum函数很简单,就是将n个字节值相加,返回结果:

Xv6内核分析(二)

2.4 mpconfig函数

Xv6内核分析(二)

  • 首先通过mpsearch函数找到有效的MP Floating Pointer Structure。
  • 然后通过其中记录的physaddr找到MP配置表头所在的位置,然后转换成虚拟地址来访问。
  • 对MP表头进行检查,从头部标识、版本、校验和三方面来检查。
  • 检查通过的话,返回MP表头位置和MP Floating Pointer Structure的位置。

MP配置表头的内容描述如下:

Xv6内核分析(二)

2.5 MP配置表

MP配置表中工有5种类型的配置项:

Xv6内核分析(二)

看看mpinit函数:

Xv6内核分析(二)

找到MP配置表头后,将其中的lapicaddr记录下来,然后遍历配置表的每一个表项:

Xv6内核分析(二)

可以看出,xv6中只处理Processor和I/O APIC Entries这两种类型的配置项。如果是Processor类型,就将配置表中的apicid记录在全局的cpus数组对应的cpu信息中,同时将检测到的cpu个数加一,然后跳过这个Processor配置表,继续解析下一个配置表。当有n个cpu时,就会有n个Processor类型的配置表。xv6中默认最多支持8个cpu。

Processor类型的具体描述如下:

Xv6内核分析(二)

如果检测到是I/O APIC Entries,那么就会将其中的I/O APIC ID记录下来,I/O APIC Entries内容如下:

Xv6内核分析(二)

可以看出,遍历配置表主要就是为了获取每个CPU的local apic id和io apic id。

2.6 imcr功能

在mpinit的最后检查cpu是否支持imcr功能:

Xv6内核分析(二)

如果cpu支持imcr功能,那么就设置IMCR寄存器,退出PIC Mode,强制所有中断都经过IO APIC。0x22和0x23端口描述如下:

Xv6内核分析(二)

Xv6内核分析(二)

Xv6内核分析(二)

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

发表评论

匿名网友 填写信息

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