I/O位图是位于TSS中的,它可以存在也可以不存在,它只是用来设置对某些特定端口的访问,没有它的话便默认为禁止访问所有端口。正是由于它可有可用,所以TSS的段界限TSS limit(即实际大小-1)并不固定。当TSS中不包括I/O位图时,TSS只有104字节大小。话说回来了,当处理器执行某些IO指令时,若当前特权级比IOPL低,处理器就会认为也许只是给当前任务单独放行了某些端口,于是它就到TSS中找I/O位图,如果I/O位图不存在,即所有端口都禁止访问,于是处理器就会抛异常。
读到这里,有两个问题要解决:
- 处理器到TSS中哪里去找I/O位图呢?
- 怎样证明I/O位图不存在?
在TSS结构中,有一项是“I/O位图在TSS中的偏移地址”, 它在TSS中偏移102字节的地方,占2个字节空间,就是图5-47的左上角,此处用来存储I/O位图的偏移地址,即此地址是I/O位图在TSS中以0为起始的偏移量。如果某个TSS存在I/O位图的话,此处用来保存它的偏移地址,示意如图
如图所示,TSS中如果有I/O位图的话,它将位于TSS的顶端,这就是TSS的实际尺寸并不固定的原因,当包括I/O位图时,其大小是“I/O位图偏移地址”+8192+1字节,结尾这个1字节是I/O位图中最后的0xff,说来话长,一会再解释。若不包括I/O位图,其大小则为最小尺寸104字节。由于I/O位图偏移地址并不固定,可以大于等于104,所以在TSS中偏移102字节和I/O位图之间可能会有空闲区域。
您看,既然I/O位图位于TSS内,那它的地址必须是在TSS的尺寸范围之内,即地址的范围是在TSS偏移(104 ~ TSS 段界限limit)之间,如果偏移地址不在此范围,即大于等于TSS 段界限limit(TSS尺寸大小-1),则表明没有I/O位图。
现在来说下为什么在IO位图的结尾有个0xff。
在计算机系统硬件中,IO端口是按字节来编址的,意思是说一个端口只能读写1个字节的数据。如果对一个端口连续读写多个字节,实际上是从以该端口号为起始的多个端口一并读进来的。举个例子,比如in指令可以读取16位端口数据,即一次读取2字节,假设端口0x234是16位端口:
in ax,0x234
这相当于
in al,0x234
in ah,0x235
处理器在进行端口读写时,若当前特权级CPL低于IO特权级IOPL时,如果有I/O位图的话,处理器会在I/O位图中检查端口相应的bit是否为0。若在某个端口中读取多个字节,处理器必然会检查连续的多个端口在I/O位图中对应的多个bit,这些bit必须都得为0 才允许访问它们。
连续的多个bit也许会跨字节,比如端口0x234对应的bit在前一个字节的最后一位,0x235对应的bit在后一个字节的第0位,这样处理器必须将这两个字节都读进来处理。
大多数情况下跨字节都没问题,但当第1个bit在位图的最后一个字节时就会出问题,处理器要读进多个字节,所以,第2个bit所在的字节就越界了,该字节已经不属于位图范围。
为解决这个问题,处理器要求位图的最后一字节必须是0xFF,此字节有两个作用:
第一,处理器允许I/O位图中不映射所有的端口, 即I/O位图长度可以不足8KB,但位图的最后一字节必须为0xFF。如果在位图范围外的端口,处理器一律默认禁止访问。这样一来,如果位图最后一字节的0xFF属于全部65536个端口范围之内,字节各位全为1表示禁止访问此字节代表的全部端口,这并没什么过错。
第二,如果该字节已经超过了全部端口的范围,它并不用来映射端口,只是用来做为位图的边界标记,用于跨位图最后一个字节时的“余量字节”。避免越界访问TSS外的内存。
这就是位图中最后一字节必须为0xFF的原因。
您看I/O位图我说的这么热闹,其实咱们不用它,这里的介绍完全是为了让大伙了解IO访问的工作原理,只会有益无害。
到了这里,特权级就讲完了,本章也结束了,友情提示,我知道本章内容有点多,您已经很累了,现在所说的是结束语,所以可以略过这里不看啦。