将Circle驱动框架移植到一个自制微内核操作系统的过程

描述

一 前言

什么是Circle?

Circle是一个叫rsta2的大佬用C++写的bare-metal的树莓派驱动框架,同时支持现存的几乎所有版本树莓派,能够驱动树莓派上的大部分设备,包SD卡控制器、有线和无线网卡、GPIO、USB控制器及一些常用USB设备等。

这些设备驱动中有一些是rsta2参考Linux或其它bare-metal的实现自己写的,还有一些是他直接从其它系统移植过来的。各驱动对外封装成了C++类的形式,可以按需对其实例化和使用。对于在树莓派上编写自制操作系统并且希望尽快驱动一些设备的场景,将Circle整个移植过来是非常值得考虑的选项。当然,需要注意的是,Circle的开源协议是GPLv3,具有传染性,应该把它作为系统的非必要组件来使用。

本文干了什么?

本文将简要记述将Circle驱动框架移植到一个自制微内核操作系统(实际上是实验室项目),作为用户态驱动进程的过程。

由于只是简要地记录移植过程和经验总结,本文不会深入到每一个具体的细节,如果你恰好有相似的需求,除了参考本文,还需要具体地去研究Circle的代码,并根据具体情况具体分析。

二 确定需求

首先一开始不要盲目移植,先确定一下自己需要Circle中的哪些设备驱动,比如说,如果你只是需要USB相关驱动,那么在移植过程中可以不关心WLAN那块是否有不兼容。

然后跑一些Circle提供的sample,确定Circle确实可以满足需求。

三 移植基本部分

这部分包括一些非常必要的组件,比如mailbox访问,许多驱动都需要通过mailbox获取信息或申请资源等。这部分移植完成后,将可以在启动时获取机器信息,并点亮LED灯。

具体地,主要需要做下面这些事情:

1.修改Rules.mkMakefile,去掉任何boot相关的代码。

2.重新实现一些模块,去除需要特权指令的地方,并使用用户态lib提供的功能代替:

assert:assert失败后不要真的关机或重启,可以exit;

interrupt:一开始可简单打印点内容,不用真的实现;

logger:改为printf输出;

new:使用malloc分配;

sysinit:提供假实现;

timer:使用nanosleep实现SimpleusDelay;

memory:CMemorySystem类可以直接去掉,一开始会有地方用到CMemorySystem::GetCoherentPage;

改用系统提供的分配物理上连续的non-cacheable内存的接口。

3.Circle进程启动时将物理地址的外设区域直接对等映射到当前进程的虚拟地址空间,这样将不需要改动Circle中通过MMIO访问外设时使用的地址。

经过一些调试后,可以点亮LED灯,输出日志到stdout,然后退出(需要编写适当的kernel.cpp,可参考sample),这意味着简单的MMIO已经可以了。

四 驱动屏幕

这一步同样需要重新实现一些模块:

synchronize:主要是刷cache相关操作;

bcmframebuffer:向GPU申请frame buffer后需要将其映射到当前进程的虚拟地址。

经过一些调试后,可以通过HDMI输出内容到屏幕。

五 模拟实现Timer

对于一些稍复杂的驱动,例如USB和WLAN,会依赖timer获取当前tick,因此需要重新实现timer模块。

具体地,可以在CTimer::Initialize中创建一个新的线程,每隔10ms(利用nanosleep等函数)调用一次CTimer::InterruptHandler,其它代码几乎不用改动。此外,还需要实现CTimer::GetClockTicks以获得当前 tick数。

经过一些调试后,输出的日志中能够包含当前时间,此时说明timer基本实现对了。

六 解决内存相关的一系列问题

许多驱动在运行的过程中需要分配内存以供外设进行DMA,同时又需要在进程内访问这块内存以读写跟外设交互的数据。因此,这块内存既需要能通过虚拟地址访问,又需要能获取到物理地址,同时在物理地址上连续且non-cacheable。malloc是不能满足这个需求的,因为malloc只保证分配出的内存虚拟地址连续,不能保证物理地址连续,也无法配置成non-cacheable。

要解决这个问题,需要内核提供分配物理上连续且non-cacheable的内存并映射到虚拟地址空间的相关系统调用,然后再在Circle中利用这些系统调用重新实现内存相关模块。其实在前面已经粗略地实现了,但在这一步需要确保实现的正确性。对Circle的修改主要涉及CMemorySystem::GetCoherentPage、DMA_BUFFER、BUS_ADDRESS、new(HEAP_DMA)的定义及使用它们的地方。

七 用户态处理中断

对于像USB和WLAN这些需要利用中断通知操作系统发生了特定事件的设备,还需要把之前虚假实现的interrupt模块实现对。

首先要求内核提供让特定用户态进程处理特定IRQ的能力,具体来说就是驱动进程要能够通过系统调用注册一个函数作为特定编号的IRQ的用户态处理函数,然后内核在收到IRQ后调用此函数来处理。

接着重新实现interrupt模块,把CInterruptSystem::ConnectIRQ改为使用上述注册IRQ处理函数的系统调用,暂时用不到的函数可以不实现,比如与FIQ相关的。

八 驱动USB

Timer、DMA buffer、中断这几个重要的部分移植完成后,比较容易就可以驱动USB控制器,进而可以检测并驱动USB键盘、鼠标、存储、串口转换器等设备,对于树莓派3,还可以驱动有线网卡(LAN7800)。

九 其它驱动

到目前为止已经移植了大部分驱动所需的运行环境,之后的移植工作主要看具体的需求了,比如如果需要网络协议栈,还要重新实现sched模块,里面包括线程抽象、调度、线程同步机制等。





审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分