保护模式I/O
毫无疑问,对I/O 的控制权限是很重要的内容,保护模式对此也做了限制,用户进程如果不被许可是无法进行IO操作的。 这种限制通过两个方面实现的,它们就是IOPL 和 I/O许可位图。
IOPL
前面代码提到IOPL ,它是IO 保护机制的关键之一,位于寄存器eflags的第12、13位,如图所示:
指令in、ins、out、outs、cli、sti只有在CPL<= IOPL时候才能执行!
这些指令被称为I/O 敏感指令。如果低特权级的指令试图访问这些I/O指令将会导致常规保护错误(#GP)。
可以改变IOPL 的指令是popf 和 iretd,但是只有运行在ring0的程序才能将其改变。 运行在低特权级下的程序无法改变IOPL,书上说是你可以试试改,但是IOPL 不会变化,还是保持原样的。
指令popf也能改变IF (好像执行了cli和sti指令)。 所以popf也变成了I/O 敏感指令。只有CPL<=IOPL时,popf才能改变IF,否则IF 将维持原值,不会产生任何异常。
I/O许可位图
前面章节的TSS 的知识中,有一个图上面提到了“ I/O 位图基址”,它是一个以TSS的地址为基址的偏移,指向的便是I/O许可位图。
为什么叫位图,是因为它的每一位表示一个字节的端口地址是否可用。如果某一位为0,则表示此位对应的端口号可以用,为1则不可用。由于每一个任务都可以有单独的TSS,所以每一个任务可以有它单独的I/O许可位图。
比如,一个任务的TSS 是这样的:
1 [SECTION .tss3]
2 LABEL_TSS3:
3 ...
4 DD SelectorLDT3 ; LDT
5 DW 0 ; 调试陷阱标志
6 DW $ - LABEL_TSS3 + 2 ; 指向I/O 许可位图
7 times 12 DB 0FFh ; 端口 00h -- 5fh
8 DB 11111101b ; 端口 60h -- 67h,只允许端口61h的操作
9 DB 0FFh ; I/O 许可位图结束标志
10 TSS3Len equ $ - LABEL_TSS3
由于I/O 许可位图开始有12字节内容为0FFh,即有12 X 8 = 96位被置为1,所以从端口00h 到 5Fh 共有96个端口地址对此任务不可用。同理,接下来的1字节只有第1位(从0开始数)是0,表示这一位对用的端口 (61h) 可用!
I/O 许可位图必须以0FFh结尾,代码第9行就是这样做的!
如果I/O位图基址大于或等于TSS 段界限,就表示没有I/O 许可位图,如果CPL <= IOPL ,则所有I/O 指令都会引起异常。
I/O 许可位图的使用使得即便在同一特权级别下不同的任务有不同的I/O 访问权限。
保护模式小结
- 在GDT LDT 以及IDT中,每一个描述符都有自己的界限和属性等内容,是对描述符所描述对象的一种限定和保护
- 分页机制中的PDE 和 PTE都含有R/W以及U/S 位。提供页级保护。
- 页式存储的使用使应用程序使用的线性地址空间而不是物理地址,于是物理地址就被保存起来了。
- 中断不再像实模式一样使用,也提供特权检验等内容
- I/O指令不再随便使用,于是端口被保护起来。
- 在程序运行过程中,如果遇到不同特权级间的访问情况,会对CPL、RPL、DPL、IOPL等内容进行非常严格的检验,同时可能伴随堆栈的切换,这都对不同层级的程序进行了保护!