13 | 第一个C函数:如何实现板级初始化?
LMOS
该思维导图由 AI 生成,仅供参考
你好,我是 LMOS。
前面三节课,我们为调用 Cosmos 的第一个 C 函数 hal_start 做了大量工作。这节课我们要让操作系统 Cosmos 里的第一个 C 函数真正跑起来啦,也就是说,我们会真正进入到我们的内核中。
今天我们会继续在这个 hal_start 函数里,首先执行板级初始化,其实就是 hal 层(硬件抽象层,下同)初始化,其中执行了平台初始化,hal 层的内存初始化,中断初始化,最后进入到内核层的初始化。
第一个 C 函数
任何软件工程,第一个函数总是简单的,因为它是总调用者,像是一个管理者,坐在那里发号施令,自己却是啥活也不干。
由于这是第一个 C 函数,也是初始化函数,我们还是要为它单独建立一个文件,以显示对它的尊重,依然在 Cosmos/hal/x86/ 下建立一个 hal_start.c 文件。写上这样一个函数。
根据前面的设计,Cosmos 是有 hal 层和内核层之分,所以在上述代码中,要分两步走。第一步是初始化 hal 层;第二步,初始化内核层。只是这两步的函数我们还没有写。
然而最后的死循环却有点奇怪,其实它的目的很简单,就是避免这个函数返回,因为这个返回了就无处可去,避免走回头路。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
在操作系统Cosmos中实现板级初始化的过程以及在Cosmos/hal/x86/下建立一个halmm.c文件,用于初始化内存的具体步骤和技术细节。作者详细介绍了编写第一个C函数hal_start以及其中执行的板级初始化过程,包括平台初始化、内存初始化和中断初始化。文章展示了相关代码,并介绍了初始化中断的重要性和实现过程,包括中断表的定义和中断门描述符的设置。此外,作者还展示了在halsgdidt.c文件中编写的初始化中断描述符表的函数。整篇文章通过详细的代码和注释,帮助读者了解了在Cosmos中实现板级初始化和初始化内存的具体步骤和技术细节。文章内容涵盖了操作系统底层的关键技术,对于想深入了解操作系统内部实现的读者具有很高的参考价值。文章还介绍了中断控制器的初始化和进入内核层的过程,为读者提供了全面的操作系统底层知识。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《操作系统实战 45 讲》,新⼈⾸单¥68
《操作系统实战 45 讲》,新⼈⾸单¥68
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(35)
- 最新
- 精选
- 李亮亮置顶录了一个 win11+virtualbox的操作视频 https://www.bilibili.com/video/BV1yb4y1N7ya/?share_source=copy_web&vd_source=2e2d7d5fe4bfed959537727318688414
编辑回复: 感谢你分享,行动力超赞!期待后面看到你的更多思考和输出。
2024-01-23归属地:湖南1 - 老王有了前面基础实验很快就做通了,只是实验步骤课程没有说 1.下载最新源码 git clone https://gitee.com/lmos/cosmos.git 2 进入课程的目录 cd cosmos/lesson13/Cosmos 3 编译 make all 这个过程中可能会报告错误 ../hal/x86/kernel.asm:6: fatal: unable to open include file `kernel.inc' krnlbuidrule.mk:14: recipe for target 'kernel.o' failed make[2]: *** [kernel.o] Error 1 Makefile.x86:28: recipe for target 'all' failed make[1]: *** [all] Error 2 Makefile:59: recipe for target 'all' failed make: *** [all] Error 2 警告不管 解决错误即可 使用 find -name "kernel.inc" 搜索头文件的位置 ./include/halinc/kernel.inc 把这个头文件拷贝到和kernel.asm相同的目录。或者是更改../hal/x86/kernel.asm 第6行 改为%include "../include/halinc/kernel.inc" 再次make 可以正常编译 4.生成内核镜像文件 make cplmildr (这一步会拷贝 initldrimh.bin initldrkrl.bin initldrsve.bin 到源码顶层目录的release下 ) make cprelease (这一步会拷贝 Cosmos.bin 到源码顶层目录的release下 ) make KIMG (这一步会调用lmoskrlimg 把initldrimh.bin initldrkrl.bin initldrsve.bin Cosmos.bin logo.bmp background.bmp font.fnt按一定的格式打包成Cosmos.eki镜像文件 ) 5.拷贝Cosmos.eki镜像文件到虚拟磁盘 源码目录已经创建了磁盘文件hd.img(如果没有这个文件可以按照前面的课程自己创建) sudo mount -o loop ./hd.img ./hdisk/ (挂载虚拟磁盘到hidsk目录,hd.img hidsk目录已经存在) sudo cp release/Cosmos.eki hdisk/boot (拷贝编译好的镜像Cosmos.eki 到虚拟磁盘中) sudo umount hdisk (卸载挂载目录/或者是目录和磁盘中的内容) VBoxManage convertfromraw ./hd.img --format VDI ./hd.vdi (把hd.img转为hd.vdi格式,因为课程使用的虚拟机是VirtualBox) 6.参考前面课程使用hd.vdi启动系统 总结: 要想搞清楚整个程序的流程,除了分析代码本身,还需要深入分析Makefile和各个链接脚本
编辑回复: 感谢老铁的分享,特别赞。
2021-06-071042 - neohope稍微整理了一下: 一、HAL层调用链 hal_start() A、先去处理HAL层的初始化 ->init_hal() ->->init_halplaltform()初始化平台 ->->->init_machbstart() 主要是把二级引导器建立的机器信息结构,复制到了hal层一份给内核使用,同时也为释放二级引导器占用内存做好准备。 其做法就是拷贝了一份mbsp到kmbsp,其中用到了虚拟地址转换hyadr_to_viradr ->->->init_bdvideo() 初始化图形机构 初始化BGA显卡 或 VBE图形显卡信息【函数指针的使用】 清空屏幕 找到"background.bmp",并显示背景图片 ->->->->hal_dspversion() 输出版本号等信息【vsprintfk】 其中,用ret_charsinfo根据字体文件获取字符像素信息 ->->move_img2maxpadr() 将移动initldrsve.bin到最大地址 ->->init_halmm()初始化内存 ->->->init_phymmarge 申请phymmarge_t内存 根据 e820map_t 结构数组,复制数据到phymmarge_t 结构数组 按内存开始地址进行排序 ->->init_halintupt();初始化中断 ->->->init_descriptor();初始化GDT描述符x64_gdt ->->->init_idt_descriptor();初始化IDT描述符x64_idt,绑定了中断编号及中断处理函数 ->->->init_intfltdsc();初始化中断异常表machintflt,拷贝了中断相关信息 ->->->init_i8259();初始化8529芯片中断 ->->->i8259_enabled_line(0);好像是取消mask,开启中断请求 最后,跳转去处理内核初始化 ->init_krl() 二、中断调用链,以硬件中断为例 A、kernel.inc中,通过宏定义,进行了中断定义。以硬件中断为例,可以在kernel.inc中看到: 宏为HARWINT,硬件中断分发器函数为hal_hwint_allocator %macro HARWINT 1 保存现场...... mov rdi, %1 mov rsi,rsp call hal_hwint_allocator 恢复现场...... %endmacro B、而在kernel.asm中,定义了各种硬件中断编号,比如hxi_hwint00,作为中断处理入口 ALIGN 16 hxi_hwint00: HARWINT (INT_VECTOR_IRQ0+0) C、有硬件中断时,会先到达中断处理入口,然后调用到硬件中断分发器函数hal_hwint_allocator 第一个参数为中断编号,在rdi 第二个参数为中断发生时的栈指针,在rsi 然后调用异常处理函数hal_do_hwint D、hal_do_hwint 加锁 调用中断回调函数hal_run_intflthandle 释放锁 E、hal_run_intflthandle 先获取中断异常表machintflt 然后调用i_serlist 链表上所有挂载intserdsc_t 结构中的中断处理的回调函数,是否处理由函数自己判断 F、中断处理完毕 G、异常处理类似,只是触发源头不太一样而已
作者回复: 老哥 6666
2021-06-08321 - pedro[ hal_start ] --> [ init_hal ] --> [ init_krl ] [ init_hal ] --> [ init_halplaltform ] --> [ move_img2maxpadr ] --> [ init_halmm ] --> [ init_halintupt ] [ init_krl ] --> [ die ] [ init_halplaltform ] --> [ init_machbstart ] --> [ init_bdvideo ] [ init_halmm ] --> [ init_phymmarge ] [ init_halintupt ] --> [ init_descriptor ] --> [ init_idt_descriptor ] --> [ init_intfltdsc ] --> [ init_i8259 ] --> [ i8259_enabled_line ] 如果有 graph-easy 的同学,直接 CV,然后: ```sh graph-easy calltree.txt ```
作者回复: 铁汁牛皮
2021-06-07415 - LunaElfCosmos hal 层函数调用关系: 1. `hal_start()` 1. `init_hal()` 1. `init_halplatform()` 1. `init_machbstart()` 1. `machbstart_t_init()` 2. `init_bdvideo()` 2. `init_halmm()` 1. `init_phymmarge()` 1. `initpmrge_core()` 3. `init_halintupt()` 1. `init_idt_descriptor()` 1. `set_idt_desc()` 2. `init_intfltdsc()` 3. `init_i8259()` 2. `init_krl()` 1. `hal_fault_allocator()` 1. `hal_do_hwint()` 1. `hal_run_intflthandle()` 1. `hal_hwint_allocator()` 1. `hal_do_hwint()` 1. `hal_run_intflthandle()`
作者回复: 66666
2021-10-194 - 卢承灏有一个问题,回过头来二刷的时候没想明白,如果中断传递的只是一个中断号,然后中断号是进行共用的,那在hal_run_intflthandle 中的list_for_each 中,每个设备注册的handler方法,怎么判断自己需不需要执行呢? handler传入的s->device 也是从循环中的每一个intserdsc_t取出,和最开始的中断号看不出中什么关联。还希望大神们解答
作者回复: 中断共享 需要设备驱动程序自己处理
2021-09-0724 - 云师兄太硬了啊,有点磕牙,不过再咬两口试试😬
编辑回复: 哈哈哈,舌尖上的操作系统,老铁加油!
2021-06-094 - 吴建平代码走查出一个安全问题,下面这个函数里,如果for里一个满足条件的都没找到,那么后面校验的时候 retemp->saddr 就空指针了。 e820map_t *ret_kmaxmpadrcmpsz_e820map(machbstart_t *mbsp, u64_t mappadr, u64_t cpsz) { if (NULL == mbsp) { return NULL; } u64_t enr = mbsp->mb_e820nr; e820map_t *emp = (e820map_t *)phyadr_to_viradr((adr_t)mbsp->mb_e820padr); e820map_t *retemp = NULL; u64_t maxadr = emp[0].saddr; for (u64_t i = 0; i < enr; i++) { if (emp[i].type == RAM_USABLE) { if (emp[i].saddr >= maxadr && //内存区首地址大于已知最大区域起始地址(初始化位第一个区首地址 (mappadr > (emp[i].saddr + emp[i].lsize)) && //内存区尾地址小于内存映射最大地址 (emp[i].lsize >= cpsz)) //内存区大小大于镜像文件大小 { maxadr = emp[i].saddr; //已知最大区域起始地址 retemp = &emp[i]; //更新最后满足条件内存区域 } } } if ((mappadr > (retemp->saddr + retemp->lsize)) && (retemp->lsize >= cpsz)) //校验,但除非一个都不满足条件 { return retemp; } return NULL; }
作者回复: 对的 厉害了 找出了BUG
2021-07-012 - 然很好奇move_img2maxpadr(&kmachbsp);这个函数 功能:move_img2maxpadr(&kmachbsp);这个函数是把镜像文件搬到最大的物理地址处。 作用:我感觉是因为镜像文件是加载在0x4000000处,而空闲地址是从内核文件加载处开始计算的(0x2000000+内核大小),随着内存的分配,空闲地址不断向上增长,迟早会覆盖镜像文件,所以提前把镜像文件搬走了。
作者回复: 对,你理解的很正确
2021-06-072 - Victor在ubuntu 18.04环境下make时报错: root@ubuntu1804:~/LMOS/cosmos/lesson13/Cosmos# make all Initldr:清理全部已构建文件... ^_^ *********正在开始编译构建系统************* AS -[M] 正在构建... ../ldrkrl/imginithead.asm CC -[M] 正在构建... ../ldrkrl/inithead.c CC -[M] 正在构建... ../ldrkrl/vgastr.c AS -[M] 正在构建... ../ldrkrl/ldrkrl32.asm CC -[M] 正在构建... ../ldrkrl/ldrkrlentry.c CC -[M] 正在构建... ../ldrkrl/fs.c CC -[M] 正在构建... ../ldrkrl/chkcpmm.c CC -[M] 正在构建... ../ldrkrl/graph.c CC -[M] 正在构建... ../ldrkrl/bstartparm.c AS -[M] 正在构建... ../ldrkrl/realintsve.asm OBJCOPY -[M] 正在构建... initldrimh.bin OBJCOPY -[M] 正在构建... initldrkrl.bin OBJCOPY -[M] 正在构建... initldrsve.bin 恭喜我,Initldr编译构建完成! ^_^ ../hal/x86/kernel.asm:6: fatal: unable to open include file `kernel.inc' krnlbuidrule.mk:14: recipe for target 'kernel.o' failed make[2]: *** [kernel.o] Error 1 Makefile.x86:28: recipe for target 'all' failed make[1]: *** [all] Error 2 Makefile:59: recipe for target 'all' failed make: *** [all] Error 2
作者回复: 是不是下载 的完整代码
2021-06-0732
收起评论