12 | 设置工作模式与环境(下):探查和收集信息
LMOS
该思维导图由 AI 生成,仅供参考
你好,我是 LMOS。
上节课我们动手实现了自己的二级引导器。今天这节课我们将进入二级引导器,完成具体工作的环节。
在二级引导器中,我们要检查 CPU 是否支持 64 位的工作模式、收集内存布局信息,看看是不是合乎我们操作系统的最低运行要求,还要设置操作系统需要的 MMU 页表、设置显卡模式、释放中文字体文件。
检查与收集机器信息
如果 ldrkrl_entry() 函数是总裁,那么 init_bstartparm() 函数则是经理,它负责管理检查 CPU 模式、收集内存信息,设置内核栈,设置内核字体、建立内核 MMU 页表数据。
为了使代码更加清晰,我们并不直接在 ldrkrl_entry() 函数中搞事情,而是准备在另一个 bstartparm.c 文件中实现一个 init_bstartparm()。
下面我们就来动手实现它,如下所示。
目前我们的经理 init_bstartparm() 函数只是调用了一个 machbstart_t_init() 函数,在 1MB 内存地址处初始化了一个机器信息结构 machbstart_t,后面随着干活越来越多,还会调用更多的函数的。
检查 CPU
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
本文深入介绍了在操作系统开发中设置工作模式与环境的关键步骤。首先,二级引导器脱离GRUB控制后,进行了CPU检查、内存布局信息获取、内核栈初始化、内核文件和字库文件放置、MMU页表数据建立以及图形模式设置等工作。随后,二级引导器显示操作系统的logo,标志着一切正常。最后,通过跳转到Cosmos的入口,二级引导器结束自身使命,进入Cosmos操作系统。文章内容技术性强,适合对操作系统开发感兴趣的读者阅读。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《操作系统实战 45 讲》,新⼈⾸单¥68
《操作系统实战 45 讲》,新⼈⾸单¥68
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(95)
- 最新
- 精选
- 言C置顶经过一段时间的摸索,今天终于成功显示了Cosmos的logo界面了,遇到了几个坑,特此来给大家填坑了: 1、出现无法找到Cosmos的错误提示,如下: 正在生成Cosmos内核映像文件,请稍后…… 文件数:6 映像文件大小:4911104 Converting from raw image file="../hd.img" to file="../exckrnl/hd.vdi"... Creating dynamic image with size 104857600 bytes (100MB)... VBoxManage: error: Could not find a registered machine named 'Cosmos' VBoxManage: error: Details: code VBOX_E_OBJECT_NOT_FOUND (0x80bb0001), component VirtualBoxWrap, interface IVirtualBox, callee nsISupports VBoxManage: error: Context: "FindMachine(Bstr(a->argv[0]).raw(), machine.asOutParam())" at line 1044 of file VBoxManageStorageController.cpp make[1]: *** [vbox.mkf:13:idectrnul] 错误 1 make: *** [Makefile:101:VBOXRUN] 错误 2 解决办法:需要自己建立一个虚拟机,命名为 Cosmos 2、运行 make virtualtest 后,virtualbox启动后没有出现logo图片,而出现错误提示:INITKLDR DIE ERROR:S 解决办法:建立虚拟机时,需要选择为 64bit 的系统 3、这个问题是与第二个的关联问题,当你在建立虚拟机时,想选择64bit的系统,却发现只有 32bit 的 解决办法: VirtualBox安装64位的系统需要满足以下条件: 1. 64位的cpu 2. cpu允许硬件虚拟化 先来看第一个条件,64位的CPU,这个嘛,现在的笔记本一般都是64位的了,所以不用担心,除非是好几年之间的电脑。如果你不清楚,可以打开命令行,输入systeminfo,在输出的信息中找到CPU这一行,如果是X86_64的,就是64位CPU; 第二条,是否开启CPU硬件虚拟化1,这个嘛,各大厂商的情况不大相同,有的电脑默认开启了(比如,我的HP),有的没有,所以需要自行开启,开启方法:开机时按某个键进入BIOS设置界面2。 然后,setup==>security==>cpu virtualization,将cpu virtualization这一项由Disable设置为Enable。保存,然后重启电脑,硬件虚拟化就开启成功了。 如果你是通过windows + vmware + virtualbox 的方式实现的,还需要设置 VMware,打开vmware,找到对应的虚拟机,虚拟机设置->硬件->处理器->虚拟化引擎,这里一般会有三个选项,全都勾上 然后在打开该虚拟机,去virtualbox中建立虚拟机时,就会发现有 64bit 的选项了
作者回复: 厉害了
2021-10-1729 - neohope置顶大体上整理了一下,有几处没弄清楚【下半部分】: 11、返回到bstartparm.c 调用了chkcpmm.c的init_bstartpages 12、然后调用到了fs.c的move_krlimg函数申请了内存,建立了MMU页表: 顶级页目录,开始于0x1000000 页目录指针目录,开始于0x1001000,,共16项 ,其中每一项都指向一个页目录 页目录,开始于0x1002000, 每页指向512 个物理页,每页2MB【 0x200000】 让物理地址p[0]和虚拟地址p[((KRNL_VIRTUAL_ADDRESS_START) >> KPML4_SHIFT) & 0x1ff],指向同一个页目录指针页,确保内核在启动初期,虚拟地址和物理地址要保持相同 没搞清楚为什么虚拟地址是这个,也暂时没搞清楚为何要指向(u64_t)((u32_t)pdpte | KPML4_RW | KPML4_P) 最后,把页表首地址保存在机器信息结构中 13、返回到bstartparm.c 调用了graph.c的init_graph A、初始化了数据结构 B、调用init_bgadevice 首先获取GBA设备ID 检查设备最大分辨率 设置显示参数,并将参数保存到mbsp结构中 C、如果不是图形模式,要通过BIOS中断进行切换,设置显示参数,并将参数保存到mbsp结构中: 获取VBE模式,通过BIOS中断 获取一个具体VBE模式的信息,通过BIOS中断 设置VBE模式,通过BIOS中断 这三个方法同样用到了realadr_call_entry,调用路径与上面_getmmap类似,不再展开 D、初始化了一块儿内存 感觉会与物理地址与虚拟地址之间转换由一定关系 E、进行logo显示 调用get_file_rpadrandsz定位到位图文件 调用bmp_print,读入像素点,BGRA转换 最后调用write_pixcolor,写入到mbsp->mb_ghparm正确的位置,图像就显示出来了 14、然后一路返回 到bstartparm.c的init_bstartparm 到ldrkrlentry.c的ldrkrl_entry 到ldrkrl32.asm的call ldrkrl_entry 再往下是jmp 0x2000000 这个地址就是IMGKRNL_PHYADR,就是刚才放Cosmos.eki的位置 15、然后就接上了本节最后一部分内容了,不容易啊!哈哈哈!
作者回复: 因为有一段 地址是物理地址,二级引导器中,并没有切换到长模式,是没有办法使用高端的物理地址,只能从低端地址切换
2021-06-05220 - neohope置顶大体上整理了一下,有几处没弄清楚【上半部分】: 1、bstartparm.c从init_bstartparm函数开始 A、初始化machbstart_t 2、跳转到chkcpmm.c的init_chkcpu函数,检查是否支持CPUID功能 A、init_chkcpu函数 CPU自带检查方式:无法反转 Eflags第21位,表示CPU支持CPUID功能 如果反转成功,说明不支持CPUID,打印内核错误并退出 B、然后调用CPUID功能,判断是否支持长模式 先通过通过0x80000000参数,调用cpuid命令,判断CPU是否支持扩展处理器信息【返回值比0x80000000大】 如果支持,通过0x80000001参数,调用cpuid命令,获取扩展处理器信息,然后检测第29位,判断是否支持长模式 如果不支持,打印内核错误并退出 C、设置mbsp中cpumode为64位 3、返回chkcpmm.c,继续检测内存信息 A、跳转到chkcpmm.c的init_mem函数 B、通过mmap调用realadr_call_entry(RLINTNR(0),0,0) C、实际会执行ldrkrl32.asm的realadr_call_entry D、跳转到save_eip_jmp E、最后在cpmty_mode处,把 0x18:0x1000 [段描述索引:段内的偏移],装入到 CS:EIP 寄存器中 F、而EIP这个地址恰恰是内存中initldrsve.bin的位置,因为之前write_realintsvefile把数据加载到了REALDRV_PHYADR 0x1000【而且在initldrsve.lds好像也指定了段内偏移0x1000】 同时CS中段描述符为k16cd_dsc,说明是16位代码段,可以执行,CPU继续从EIP地址执行 G、而initldrsve.bin是由realintsve.asm编译得到的,所以实际会继续执行realintsve.asm中代码 H、然后到real_entry这里,通过传入的参数ax,判断调用func_table哪个方法 当前参数位0,ax就是0,也就是调用了func_table的第一个函数_getmmap I、_getmmap中,通过BIOS的15h中断,获取内存信息 J、检查内存信息,如果小于128M,打印内核错误并退出 K、设置machbstart_t内存相关参数 L、然后调用了init_acpi 4、在init_acpi中 通过“RSD PTR ”及校验,判断是否支持ACPI功能 不支持则 打印内核错误并退出 5、返回到bstartparm.c 好像是确认了一下initldrsve.bin的状态,获取了一下文件内存地址及大小 6、返回到bstartparm.c,继续调用到chkcpmm.c的init_krlinitstack函数 7、然后调用到了fs.c的move_krlimg函数 首先判断新申请的地址,是否可用 -》如果可用直接使用 -》如果不可用,则判断申请的内存大小是否超出设备物理大小 -》-》如果超出大小,系统打印内核错误并退出 -》-》如果不超出大小,系统会将内存对齐到0x1000后,将initldrsve.bin移动到新位置,并修正地址 整体来说move_krlimg更像是申请内存,但不知道为何要不断驱赶initldrsve.bin文件 8、返回到chkcpmm.c 初始化栈顶和栈大小 9、返回到bstartparm.c 调用fs.c的init_krlfile函数,将Cosmos.eki加载到了IMGKRNL_PHYADR 并填写了mbsp相关内容 10、返回到bstartparm.c 调用了chkcpmm.c的init_meme820函数 然后调用到了fs.c的move_krlimg函数申请了内存,拷贝了一份e820map_t到Cosmos.eki之后的地址,并修正mbsp指向新地址 感觉和内存保护 或 物理地址与虚拟地址之间转换有一定关系
作者回复: 是的,你学的很透
2021-06-05617 - pedro思考题还是挺麻烦的,主要是没有注释啊,很多字段的含义都是靠猜,文章也没有介绍到这些。 首先是 init_mem820 这个函数本身: ```c void init_meme820(machbstart_t *mbsp) { e820map_t *semp = (e820map_t *)((u32_t)(mbsp->mb_e820padr)); // e820数组地址 u64_t senr = mbsp->mb_e820nr; // 个数 e820map_t *demp = (e820map_t *)((u32_t)(mbsp->mb_nextwtpadr)); if (1 > move_krlimg(mbsp, (u64_t)((u32_t)demp), (senr * (sizeof(e820map_t))))) { kerror("move_krlimg err"); } m2mcopy(semp, demp, (sint_t)(senr * (sizeof(e820map_t)))); mbsp->mb_e820padr = (u64_t)((u32_t)(demp)); mbsp->mb_e820sz = senr * (sizeof(e820map_t)); mbsp->mb_nextwtpadr = P4K_ALIGN((u32_t)(demp) + (u32_t)(senr * (sizeof(e820map_t)))); mbsp->mb_kalldendpadr = mbsp->mb_e820padr + mbsp->mb_e820sz; return; } ``` 我们发现这个函数实际上在拷贝内存,即将 semp 指针处的 senr * (sizeof(e820map_t) 内存大小拷贝到 demp 处,而 demp 的地址正是 mb_nextwtpadr,那么这个 mb_nextwtpadr 是怎么来的呢?在init_krlfile函数中可以大致猜测: ```c void init_krlfile(machbstart_t *mbsp) { u64_t sz = r_file_to_padr(mbsp, IMGKRNL_PHYADR, "kernel.bin"); if (0 == sz) { kerror("r_file_to_padr err"); } mbsp->mb_krlimgpadr = IMGKRNL_PHYADR; mbsp->mb_krlsz = sz; // 页对齐 mbsp->mb_nextwtpadr = P4K_ALIGN(mbsp->mb_krlimgpadr + mbsp->mb_krlsz); mbsp->mb_kalldendpadr = mbsp->mb_krlimgpadr + mbsp->mb_krlsz; return; } ``` 没错,mb_nextwtpadr 正是跳过内核起始地址+内核大小后的第一个页地址,注意需要4k对齐。 那么刚才内存拷贝的意图也很明显,对于初始化后的内存,内核本身的内存映射是不可访问的,必须保护充分内核,所以 init_mem820 函数的作用是跳过内核初始化内存。 由于代码无注释,文中篇幅有限,如有错误,多多指正,望海涵~
作者回复: 你猜的很对 看来我确实不用写注释
2021-06-0417 - blentle首先,这个函数为啥这么命名,没整明白
作者回复: 这个问题不重要啊
2021-06-04713 - neohope大体上整理了一下,有几处没弄清楚【补充最后一段,发漏了】 15、然后就接上了本节最后一部分内容了,不容易啊!哈哈哈! Cosmos.bin中【前面写错为Cosmos.eki了】,ld设置的程序入口为init_entry.asm的_start: 16、 init_entry.asm中_start: A、关闭中断 B、通过LGDT命令,指定长度和初始位置,加载GDT C、设置页表,开启PAE【CR4第五位设置为1】,将页表顶级目录放入CR3 D、读取EFER,将第八位设置为1,写回EFER,设置为长模式 E、开启保护模式【CR0第0位设置为1】,开启分页【CR0第31位设置为1】,开启CACHE【CR0第30位设置为0】,开启WriteThrough【CR0第29位设置为0】 F、初始化寄存器们 G、将之前复制到Cosmos.bin之后的mbsp地址,放入rsp H、0入栈,0x8入栈, hal_start 函数地址入栈 I、调用机器指令“0xcb48”,做一个“返回”操作,同时从栈中弹出两个数据[0x8:hal_start 函数地址],到[CS:RIP] 长模式下,指令寄存器为RIP,也就是说下一个指令为hal_start 函数地址 CS中为0x8,对应到ekrnl_c64_dsc,对应到内核代码段,可以执行,CPU继续冲RIP地址执行 17、hal_start.c A、执行hal_start函数
作者回复: 嗯 就是这样的
2021-06-057 - 嗣树回答下思考题,init_mem820() 的作用? 先说结论,init_mem820 函数的作用是将 init_mem() 中获取到的内存信息转存到字体文件之后,其实就是为它找一个安稳的地方存放。 简单分析一下: init_mem() -> mmap() -> realadr_call_entry() 然后进入实模式调用中断,实模式的访址能力是有限,我们为中断处理指定临时的地址(E80MAP_ADRADR) 存放内存信息数组 #define ETYBAK_ADR 0x2000 #define E80MAP_ADRADR (ETYBAK_ADR+68) 接下来在把内核文件加载到指定内存地址,字体文件紧随其后,然后就是我们收集到的信息,我们还要把放在临时位置的信息复制出来。
作者回复: 对 的 对的 就是这样的
2021-06-0427 - scutgj2010我是vmware虚拟机,不支持VBE的118模式,一直在报错vbe mode not 118,手动改成117或者120,还是继续报错,这个怎么解决呀?
作者回复: 必须要118 否则要改图形驱动的
2021-08-1565 - 然init_mem820()函数作用是把e820map结构数组从低地址处(0x2068)搬到高地址处(0x2000000之上)。 问什么要搬?在实模式下能用的地址空间有限,只能访问低地址。所以e820map结构数组不能一直存在低地址处。
作者回复: 对 对 对 你理解的很好
2021-06-0545 - XF-薛峰这一讲的实验,我的配置是Win10+Virtualbox+Ubuntu用来做make编译,使用make vboxtest命令编译,有关Vbox的报错不用理会(我没在虚拟机下的虚拟机里安装),只要找到hd.vdi文件就好了,退出Ubuntu,另新建虚拟机使用这个hd.vdi就行了。如果只使用make,没有转换成hd.vdi
作者回复: 你好,你需要转换成 hd.vdi
2021-06-2074
收起评论