前面我们已经对MIPS架构CPU有了粗略的了解。显然,它提供了众多优秀的功能。但是,应用的场景不同,往往需要CPU做的事情也不一样,这就需要必须能够对CPU以及它提供的功能进行有选择的配置。这是协处理器诞生的根本原因。
ARM架构也使用协处理器进行控制,称为协处理器15,(cp15)。
MIPS架构CPU使用协处理器0进行CPU的配置和管理。那么,它到底能够干什么呢?
对于相关的寄存器,在此,不再详述。使用时,参阅相关的数据手册即可。
mtc0 s, <n> # 把数据拷贝到协处理器0
这条指令的作用是把通用寄存器s中的值拷贝到协处理器的寄存器n中,数据位数是32位。大部分的协处理器寄存器是32位的,对于少数的64位协处理器寄存器可以使用dmtc0
指令进行操作。这是设置CPU控制寄存器的唯一方法。
32位架构的时候,最多有32个协处理器寄存器。但是MIPS32/64架构扩展到了256个寄存器,为了向前兼容,在指令中添加select域来控制多个寄存器。比如
mtc0 s, $12, 1
select域的值等于1,其作用就是把通用寄存器s的值写入到协处理器的寄存器12组中的编号为1的寄存器中。也就是说,寄存器12可以有多个具体的寄存器,使用select域选择对应的哪个寄存器。
mfc0 d, $n # 把协处理器第n个寄存器中的值写入到通用寄存器d中
上述指令的作用是把协处理器0中的第n个寄存器中的内容读取到通用寄存器d中。
所有的架构的CPU在面对特权等级切换的时候(一般就是异常返回时),都会面临一个问题:一方面,在返回用户态程序之前就降低特权等级,那么会立即引发一个异常指令访问的二次异常;另一方面,如果返回到用户程序之后再降低特权等级,那么可能会被恶意程序利用内核态运行某些指令。解决这个问题的办法就是,保证异常返回时的指令是原子操作。MIPS架构的CPU提供了这个指令eret
。
v0
和v1
。我们的程序可以把一些异常需要的重要信息保存在这儿。但是,通用寄存器极易发生变化,大部分时候,这两个寄存器不建议使用。
可以通过查看Cause寄存器,判断属于哪类异常,从而做相应的处理。我们知道CPU的指令是按照流水线的方式执行。有可能,操作协处理器的指令还没执行彻底,其它指令就已经开始执行了。如何才能保证CP0的操作生效后,再执行相关指令呢?
因为MIPS架构的设计理念是 硬件尽量简单,辅以软件实现。所以,早期的软件开发人员使用nop操作,保证操作协处理器的正确性。但是,这无疑增加了软件开发人员的难度。于是,MIPS32/64架构定义了新的指令:避险指令。
三个避险指令:
指令危险和用户危险通常发生在改变CP0状态的时候(比如,改变某个寄存器、TLB项、或者一个cache行),这会影响我们普通的取值指令(在某些情况下,还会影响load/store指令访问内存的方式)。
我们必须规避这种不可控的风险。在改变CP0操作之后,添加危险屏障指令,消除这种可能产生的不可控的危险。
这类危险都有:
它们中大部分都是指令危险,可以使用jr.hb
或jalr.hb
指令避免这种指令危险。
mfc0
、tlbwi
、tlbwr
、tlbr
指令、读取CP0的cache指令以及tlbp
指令都依赖于CP0寄存器中的值。所以,这些指令执行时,有可能发生执行危险。为了保证安全,可以在
可以在读取CP0寄存器值的指令之前,添加ehb
指令。
本文分享自 嵌入式ARM和Linux 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!