OS - LA32R移植报告
这一部分由我与另一位同学共同完成。我主要完成的是SD卡部分的工作。仅此向Lab4就已探索出前三个Lab移植的他表示敬意…
指令集适配
这一部分只介绍较为关键的改动。有关Makefile中编译选项的修改,指令名称、格式变动引起的修改,在此不多加赘述。
例外有关
LA32R需要在状态控制寄存器(CSR)中,配置EENTRY和TLBRENTRY两项,分别对应普通例外的入口地址,以及TLB重填例外的入口地址。不难注意到,LA32R的TLB重填例外,是与其他例外分开处理的。
此外,由于触发 TLB 重填例外之后,处理器核将进入直接地址翻译模式,TLBRENTRY处所填入口地址应当是物理地址。EENTRY不受影F响。
配置两个入口的工作,也在start.S中进行:
1 | la a0, exc_gen_entry |
随后,是异常处理程序入口的获取。
这需要修改kern/entry.S:
- 在出现TLB有关异常时,LA32R核会直接由先前已经规定的TLB例外入口,进入对应的处理程序,我们在这里只需要关心其他例外的处理程序入口;
- 与先前由
CP0.CAUSE获取例外编号类似,我们由CSR.ESTAT获取例外编号,进入对应的处理程序。
虚拟地址翻译
LA32R引入了 “直接地址翻译模式”与“映射地址翻译模式”。在我们的实验中,我们的虚拟地址与物理地址位数相同,前者的虚拟地址直接就是物理地址,后者的虚拟地址则是由MMU进行管理,与常见实现类似。
LA32R还提供了“直接映射地址翻译模式”,允许在映射地址翻译模式下,按照CSR.DMW0/1中配置好的地址翻译窗口,将某个虚拟地址不通过页表,直接翻译为某个物理地址。
对于某些地址,我们希望其不通过Cache,直接访问内存(如MMIO有关)。LA32R提供了一致可缓存、强序非缓存两种模式,粗浅理解就是可否通过缓存访问。
LA32R并没有体系结构约定的虚拟地址空间。我们决定沿用原有的地址空间设计。因此,我们需要借助直接映射地址翻译,进行原设计中kseg0、kseg1的配置,是否可缓存的规则也与先前设计一致。
这一部分的配置工作在start.S中,于跳转到la32r_init()前进行:
1 | // Configure KSEG0/1 |
此外,在start.S中,除了原有的禁用中断外,我们还需启用映射地址翻译模式:
1 | li.w a0, 0xb0 // PLV = 0, ID = 0, DA = 0, PG = 1, DATF = DATM = 01 |
LA32R亦对TLB/页表的表项结构产生了影响,大致可以归纳为以下两点:
- 引入
PLV位,对访问权限进行控制;这需要在涉及页表操作时,对应设置权限位(pmap.c与env.c中涉及更改居多。大部分为PLV3,即用户态) - 表项属性位置的变化。
TLB异常有关
正如前文所言,TLB重填的处理入口,在LA32R中是与普通异常独立的,这为我们的实现带来了便利。
tlb_out被弃用,tlb_invalidate()使用invtlb指令即可实现
- 原有设计,是去除指定
asid与va的TLB项目 - 内存共享问题此处已被考虑,就是要去除这一被共享的TLB项
- 查询LA32R手册,得知其对应模式标志为0x6
invtlb 0x6, a0, a1即可,$a0/1对应先前tlb_out参数中的asid/va。
tlb_asm.S内直接实现tlb_miss_entry中的重填逻辑。整体流程:
- 将处理过程中,要用到的寄存器原始内容保存进CSR
- 读取CSR中,一级页表地址
- 按照BADV,算出页目录索引
- 按页表索引,得出页目录项;检查之是否合法
- 合法,继续读取页表,将页表项存入TLB
- 不合法,则缺页,写两个空项进入TLB
- 处理完后,恢复寄存器
原有的TLB项对应页无效逻辑变化不大,此处不加赘述。
对应,kern/tlbex.c受到影响。
计时器
首先,要配置计时器的中断使能,需要配置CSR.ECFG。这在进程创建时(env.c)进行;在进程切换,根据Trapframe恢复现场时,时钟中断对应就会启用。
1 | e->env_tf.csr_ecfg = ECFG_TIE; |
其次,是计时器的控制,需要配置CSR.TCFG。实现在include/kclock.h中:
1 | .macro RESET_KCLOCK |
LA32R提供了Periodic的计时器复位方法:在时钟中断发生后,计时器自动复位成CSR.TVAL中预设值。为了简化设计,我们选择MIPS中原有的计时器复位思路,即时钟中断发生后,由软件手动配置计时器。
最后,则是时钟中断的处理。在LA32R中,我们需要手动写CSR.TICLR寄存器,进行时钟中断的清除。这部分逻辑在genex.S处实现:
1 | handle_int: |
上下文保存
首先,我们需要修改Trapframe结构体的定义,使得移植后,我们仍能得知异常处理所需要的CSR信息:
- PRMD:出错时的当前状态;这主要是为了进程的创建,使得进程退出后能够正常让系统回到内核态
- BADV:出错的访存地址
- ESTAT:例外状态,旨在得知发生了什么异常
- ERA:异常处理后,返回的程序PC
- ECFG:旨在方便创建进程时,打开时钟中断
单独存储PRMD、BADV,而不是直接从CSR内读取,主要是为了处理异常的嵌套,单层异常的话,其实等价于此时CSR.PRMD与CSR.BADV中存储的值。
对应的,我们需要修改include/stackframe.h中的SAVE/RESTORE_ALL宏,更新指令为LA32R格式,并适应现在的Trapframe设计,使得上下文能够正常保存与恢复。
此外,在LA32R的寄存器定义下,发生了以下两个关键变动:
$sp以及返回值寄存器的编号发生了变化;- LA32R下,参数寄存器有5个,不再需要在栈帧中存放额外参数
这需要我们对设计这两个更改的地方进行修正。
SD卡驱动
这部分的工作,大体可以分为SD卡的初始化,以及SD卡的读写两个部分。
- 自然,我们需要规定需要访问的SD Host Controller寄存器的地址;本实现选择跟串口地址的定义放在一起。
- SD卡的初始化,本实现选择放在
la32r_init()中。 - SD卡的核心交互逻辑,存储在
kern/sd.c中。 - 对应有
include/sd.h,规定了ADMA2描述符结构体adma2_desc_entry,SD Host Controller寄存器的访问宏SDREG(),以及向SD卡发指令操作的函数定义。
为方便表述,下称Host Controller为控制器。
初始化
这里主要分为三步:
- 配置控制器与SD卡的时钟;
- 配置控制器的一般/错误中断状态使能;
- 给SD卡发送指令,初始化SD卡。
配置时钟控制,通过配置控制器的Clock Control Register实现。
1 | void sd_clk_init() { |
在时钟配置完成后,我们才能对SD卡发送指令,进行初始化。
一般/错误中断状态使能,则是通过配置控制器的Normal/Error Interrupt Status Enable Register实现的。
1 | void sd_intr_init() { |
对于NISER,我们只需要知道指令完成、数据发送完成(用于SD卡读写时)、DMA中断(DMA出错时有用)三个中断。而EISER,只需配置ADMA错误,控制器的CMD/DAT两条线的超时/CRC校验错误即可。
这两步完成后,即可按照手册中的初始化步骤,对SD卡自身进行初始化了。
我们使用ADMA2方法进行SD卡的读写。本实现在SD卡初始化时,就对SD卡的传输方式就进行配置。这通过配置Host Control 1 Register实现。
1 | SDREG_8(MEGASOC_SD_HC1R) = 0x2 << 3; // Setup ADMA2 |
在进行SD卡的读写前,我们还需要让SD卡进入传输状态。
- 在上面的初始化过程中,我们通过
CMD3的返回值,得知了当前已插入SD卡的相对地址RCA; - 我们需要通过发送
CMD7,以这一RCA为参数,使得被插入的卡被选中,进入传输状态 - 只有在传输状态下,SD卡才能接收并执行
CMD18/25两个读写指令,进行正常读写。 - 同样,本实现在
sd_init()中就发送CMD7,简化SD卡读写逻辑的编写
至此,整个SD卡的初始化流程就结束了。此后,我们即可调用SD卡的读写逻辑,对SD卡进行读写。
读写逻辑
整个读写逻辑,我们打包在sys_read/write_block()系统调用中。文件服务在需要进行读写时,直接调用syscall_(read/write)_block(secno, (dst/src), nsecs),即可对SD卡进行读写。增加系统调用的操作在此不加赘述。
具体的读写逻辑,在kern/syscall_all.c中定义。读、写过程十分相近,大体可以分为以下步骤:
- 配置ADMA2标志符表,并将标志符表的物理地址,写入控制器的ADMA System Address寄存器,告知控制器标志符表的地址
- 配置控制器的Transfer Mode寄存器,为读/写操作做准备
- 配置控制器的块大小、块计数寄存器;
- 发送多块读/写指令(CMD18/25)
此处以读操作的实现为例,进行具体分析。
描述符表项的结构体定义如下:
1 | typedef struct { |
1 | int sys_read_block(u_int secno, void *dst, u_int nsecs) { |
至此,我们就完成了一个基本的SD卡驱动。
OS - LA32R移植报告