簡化版
使用IOPL設定一個特權級的使用者程式對所有埠的訪問許可權,使用I/O點陣圖對一個特權級的使用者程式設定個性化的埠訪問許可權(能訪問部分埠、不能訪問另外的埠)。
使用者程式的CPL<IOPL,使用者程式能訪問所有埠。否則,從I/O點陣圖中查詢使用者程式對埠的訪問許可權。
IOPL儲存在eflags中,只能在0特權級的下通過popf
、iretd
修改。
I/O點陣圖儲存在TSS中。
I/O操作也可以看作一種特權資源,也有“訪問門檻”。程式碼段和資料段的訪問“門檻”是DPL
,儲存在段描述符中。
I/O操作的訪問門檻儲存在eflags
的IOPL
位。但是否具有I/O操作
的許可權,還受I/O點陣圖
影響。
敏感指令是指這些指令:in、ins、out、outs、pof、iretd
。
IOPL
規則
數值上,CPL <= IOPL,許可權上,當前程式碼段的特權級高於或等於IOPL中儲存的特權等級,當前程式碼段才能執行I/O
操作。
CPL = 0,使用者程式才能執行敏感指令。
eflags
eflags
是flags
的32位擴充套件暫存器,保護許多標誌位,例如cf、zf
等。IOPL
也儲存在eflags中,佔用2個bit,正好能表示四個特權級:00、01、10、11
。
更新
沒有能直接更新eflags
的指令,只能間接修改eflags
從而修改IOPL
。方法是使用兩個指令:popf
和iretd
。
只有特權級是0的使用者程式才能成功修改eflags
中的IOPL
。特權不是0的使用者程式也能執行popf
和iretd
來修改IOPL
,只不過修改無效,也就是說,能執行、無效果、不報錯。
popf
popf
的示意程式碼如下:
pushf ; 把eflags中的值入棧
mov [esp], 要更新到eflags中的值
popf ; 把棧中的值儲存到eflags
popf
除了能修改IOPL
,還能修改IF
。這不難理解。因為popf
是把棧中的整個符合eflags
結構的資料更新到eflags
中,只需把那個結構的IF
位修改為目標值就能修改eflags
中的IF
位。
popf
用來修改IF
位的時候,和sti、cli
一樣是敏感指令,需要滿足特權級檢查規則:CPL <= IOPL。
popf
用來修改IOPL
位的時候,需要滿足特權級檢查規則:CPL = 0。
同一個指令,修改的內容不同,需滿足的特權級檢查規則不同,這種設計,不好。若我來設計,我會設計成兩個指令,每個指令完成各自的功能。
iretd
中斷髮生時,eflags
會入棧中斷例程的堆疊。修改這個堆疊中的eflags
的值,然後再使用iretd
就能把棧中的新eflags
值更新到eflags
中。
IO點陣圖
IOPL
對一個特權級的所有使用者程式的I/O許可權
做“一刀切”的限制,要麼這個特權級的所有使用者程式擁有所有埠的I/O
許可權,要麼擁有0個埠的I/O
許可權。IO點陣圖允許對每個使用者程式在I/O埠限制上做個性化配置。
如果使用者程式的CPL <= IOPL,那麼,IO點陣圖的值是多少不影響使用者程式的I/O許可權。如果使用者程式的CPL > IOPL,那麼,IO點陣圖的值決定了使用者程式能讀寫哪些I/O埠。
點陣圖
點陣圖,又叫bitmap
。一個bit能表示兩種值,0或1。1kb的空間擁有1024個bit,1Mb的空間擁有1024*1024
個bit。
假如我們的硬體一共有65536個I/O埠,只需要8Kb的空間就能標識出這麼多埠的許可權狀態。給8kb空間中的65536個bit依次編號,若第0號I/O埠在能被當前使用者程式讀寫,將此bit設定為1,否則設定為0;剩餘的第1到第65535個bit依次按對應的I/O埠依次設定值。
TSS的尺寸
如果使用者程式存在I/O點陣圖,它將出現在使用者程式的TSS的頂端。正因為如此,TSS的尺寸是不固定的。如果不包含I/O點陣圖,TSS的尺寸是104位元組。如果包含I/O點陣圖,TSS的尺寸是“I/O點陣圖地址 + 8192位元組 + 1位元組”。
包含/IO點陣圖,TSS的尺寸為什麼不是“104位元組 + 8192位元組 + 1位元組”?
因為I/O點陣圖有可能不是緊挨著儲存"I/O點陣圖地址"的那個 位元組,二者之間可能有空白空間。I/O點陣圖地址是I/O點陣圖在TSS中的偏移量,注意,I/O點陣圖在TSS中,它與TSS中的其他元素都在TSS中。也就是說,從TSS的初始位置開始到I/O點陣圖地址,也許包含部分沒有儲存資料的空間,都是TSS的一部分。TSS的空間被I/O點陣圖地址分割為兩個部分,前一部分的大小是I/O點陣圖地址,後一部分是8192個位元組(65536個埠需要65536個bit)+1位元組(點陣圖的結束標誌必須是0xff)。
說得簡單點,就是,I/O點陣圖和TSS中的其他元素可能不是緊緊挨著。
0xff
點陣圖為什麼要用0xff
作為結束標誌?我沒有弄明白。
程式碼
不知道TSS中的保留位是什麼,更不知道怎麼在程式碼中表示保留位。這裡的程式碼只保證正確表示I/O點陣圖。
怎麼寫程式碼?
- 表示I/O點陣圖地址、I/O點陣圖自身。
- I/O點陣圖自身
- 全部bit的數量並不一定需要是65536個。
- 只需用
0xff
結尾就行。
[SECTION .tss3]
LABEL_TSS3:
;TSS中的其他元素
; 最糾結的語句。$ - LABEL_TSS3是當前行在TSS中的偏移量。
; 本語句佔用2個位元組,2個位元組之後是I/O點陣圖。
dw $ - LABEL_TSS3 + 2
times 12 db 0ffh ; 12個位元組,96個bit,都設定為1,表示當前使用者程式有埠0到埠95的讀寫許可權。
; 埠96到102只有埠97對當前使用者程式開放了讀寫許可權。從右到左編號,所以第2個0表示97號埠。
db 1111101b
; I/O點陣圖的結束標誌
db 0xff
TSS3_LEN equ $$ - LABEL_TSS3