x86 保護模式與內核 知識點彙總

本文是《x86彙編語言 從實模式到保護模式》一書的學習筆記。

在這裏插入圖片描述

正文

  • Bochs調試指令:點這裏

  • 處理器引腳中,有一個是RESET,用於接受復位信號。每當處理器加電,或者RESET引腳的電平由低變高時,處理器都會執行一個硬件初始化,以及一個可選的內部自測試(Build-in Self-Test,BIST),然後將內部所有寄存器的內容初始到一個預置的狀態

  • 處理器加電之後,它無法從內存中取得任何指令。

  • 與DRAM不同,只讀存儲器(ROM)不需要刷新,它的內容是預先寫入的,佔據0xF0000~0xfffff的內存空間。0xffff0處往往是一條跳轉指令,防止因爲溢出而變成0x00001。

  • 8086加電或者復位時,CS=0xffff,ip=ox0000,正好指向了ROM開始的地址。

  • 每個盤片都有兩個磁頭(Head),上面一個,下面一個,所以經常用磁頭來指代盤面。磁頭都有編號,第1個盤片,上面的磁頭編號爲0,下面的磁頭編號爲1;第2個盤片,上面的磁頭編號爲2,下面的磁頭編號爲3,以此類推。

  • 柱面從盤面最邊緣的磁道開始,向着圓心從0開始編號

  • 爲了加速數據在硬盤上的讀寫,最好的辦法就是儘量不移動磁頭。(尋道時間長)

  • 磁道劃分爲許多分段之後,每一部分都呈扇形,這就是扇區的由來。

  • 扇區和扇區之間使用空白間隔開,每個扇區以扇區頭開始,然後是512個字節的數據區。扇區頭包含了每個扇區自己的信息,主要有磁道號、磁頭號和盤區號,用來供硬盤定位機構使用。

  • 值得注意的是,磁頭和磁道是從0開始編號的,扇區則是從1開始編號的。

  • ROM-BIOS將讀取硬盤主引導扇區的內容,將其加載到內存地址0x0000:0x7c00處,至於爲什麼是這個地址,沒有什麼特別的理由。

  • 如果更改了這個地址(0x0000:0x7c00)的內容,就會在操作系統被加載之前加載你自定義的程序,代價是操作系統的崩潰。

  • 要訪問硬盤,運行中的程序必須至少向硬盤控制器提供4個參數,分別是磁頭號、磁道號、扇區號,以及訪問意圖(是讀還是寫)。

  • conectix標識符用來告訴虛擬機這是一個合法的VHD文件。

  • LBA(邏輯塊地址)模式:自動分塊和換區來訪問硬盤。扇區在編號時,以柱面爲單位,減少了磁頭的移動次數。

  • CHS模式:採用磁頭、磁道、扇區來訪問硬盤的模式

  • LBA=C*磁頭總數*每道扇區數+H*每道扇區數+(S-1)LBA:邏輯號,C、H、S:磁道、磁頭、扇區號

  • $用於表示當前指令的地址,$$用於表示程序開始地址

  • 一個有效的主引導扇區,最後兩個字節應該是0x55和0xAA

  • 8086可以訪問1MB的內存,其中0x00000~9FFFF屬於常規內存,由內存條提供;0xF0000~0xFFFFF由主板上的一個芯片提供,即ROM-BIOS;中間320KB的空間由特定的外圍設備提供。除非顯卡出了問題,否則0xB8000~0xBFFFF這段空間總是可以訪問的。

  • 當編譯好的程序加載到物理內存後,它在段內的偏移地址和它在編譯階段的彙編地址是相同的。(這一點可以從lst文件中得知)

  • 機器碼EA表示直接絕對轉移指令;E9表示相對轉移指令

  • 現代處理器在加電啓動時,處理器的高位部分會被強制轉換爲高電平,直到遇到第一個段間轉移指令,這個指令改變了CS和IP的值,以後處理器發出的物理地址僅僅取決於CS和IP了。

  • 32位x86處理器對寄存器做了擴展,使之達到32位,以處理32位的數據。這8個32位寄存器分別是EAX、EBX、ECX、EDX、ESI、EDI、EBP和ESP,它們可以在程序中直接做爲32位寄存器使用。同時,指令指針寄存器IP也做了擴展,達到32位,即EIP。爲了保持同8086的兼容性,這些寄存器的低16位依然保持以前的用法,這使得以前的程序可以在32位處理器上正常運行。

  • 在64位處理器上,這些寄存器再次被擴展,達到了64位,即RAX、RBX、RCX、RDX、RSI、RDI、RBP、RSP和RIP。同時,它們的低32位(包括低16位)依然保持從前的用法。除此之外,64位的x86處理器還新增了8個64位的寄存器R8、R9、R10、R11、R12、R13、R14和R15,它們只能整體作爲64位的寄存器來用

  • 在32位和64位處理器中,以上6個段寄存器(CS、SS、DS、ES、FS、GS)都依然是16位的,但都額外增加了一個不可訪問的部分,叫做段描述符高速緩存器。段描述符高速緩存器由處理器內部使用,不能在程序中訪問,裏面存放了段的起始地址、段的擴展範圍,以及段和各種屬性,比如它是代碼段還是數據段,是否可以寫入,是否被訪問過,等等。

  • ‘\’代表續行符

  • Bochs調試指令總結
    s:單步執行
    n:自動完成循環過程,並在循環體外的下一條指令之前停住
    u:反彙編指令(兩個參數,第一個參數是跟在’/'後面的指令,指定反彙編出多少條指令,第二個參數用於指定一個內存地址,從此處開始反彙編)
    b:設置斷點
    c:連續執行程序
    info:顯示寄存器的狀態(info eflags)

  • Bochs跳轉主引導扇區:
    b 0x7c00
    c

  • 在一些計算機系統中,端口號是映射到內存地址空間的,而在另一些計算機系統中,端口是獨立編址的,不和內存發生關係。

  • “of”是溢出標誌;“df”是方向標誌;“if”和“tf”是和中斷有關標誌;“sf”是符號標誌;“zf”是零標誌;“af”是輔助進位標誌;“pf”是奇偶標誌;“cf”是進位標誌。如果顯示的標誌名稱是小寫的,那麼,說明該標誌爲“0”;否則,該標誌的狀態爲“1”。

  • NASM編譯器使用匯編指令“SECTION”或者“SEGMENT”來定義段。它的一般格式是SECTION 段名稱或者SEGMENT 段名稱

  • 在段定義中使用“align=”子句,用於指定SECTION的彙編地址對齊方式

  • ror指令:循環右移

  • 設置扇區數量;設置起始LBA扇區號,將28位的扇區號寫入端口0x13、0x1f4、0x1f5和0x1f6(0x1f6端口的低4位用於存放邏輯扇區號的24~27位,第4位用於指示硬盤號,0表示主盤,1表示從盤。高3位是“111”,表示LBA模式。);向端口0xlf7寫入0x20,請求硬盤讀;等待讀寫操作完成。端口0xlf7既是命令端口,又是狀態端口;連續取出數據。0x1f0是硬盤接口的數據端口,而且還是一個16位端口。一旦硬盤控制器空閒,且準備就緒,就可以連續從這個端口寫入或者讀取數據。

  • 外部硬件中斷是通過兩個信號線引入處理器內部的,這兩根線名字叫NMI和INTR
    在這裏插入圖片描述

  • 幾乎所有的NMI事件對於處理器來說都是致命的,所以在實模式中NMI被賦予了統一的中斷號2

  • Intel處理器允許256箇中斷,中斷號的範圍是0~255,8259負責提供其中的15個,但中斷號並不固定。之所以不固定,是因爲當初設計的時候,允許軟件根據自己的需要靈活設置中斷號,以防止發生衝突。該中斷控制器芯片有自己的端口號,可以像訪問其他外部設備一樣用in和out 指令來改變它的狀態,包括各引腳的中斷號。正是因爲這樣,它又叫可編程中斷控制器(Programmable Interrupt Controller,PIC)。
    在這裏插入圖片描述

  • 每一片8259芯片只有8箇中斷輸入引腳;第一塊8259芯片的代理輸出INT直接送到處理器的INTR引腳,這是主片(Master);第二塊8259芯片的INT輸出送到第一塊的引腳2上,是從片(Slave),兩塊芯片之間形成級聯(Cascade)關係。如此一來,兩塊8259芯片可以向處理器提供15箇中斷信號。當時,接在8259上的15個設備都是相當重要的,如PS/2鍵盤和鼠標、串行口、並行口、軟磁盤驅動器、IDE硬盤等。現在,這些設備很多都已淘汰或者正在淘汰中,根據需要,這些中斷引腳可以被其他設備使用。

  • 當IF爲0時,所有從處理器INTR引腳來的中斷信號都被忽略掉;當其爲1時,處理器可以接受和響應中斷。

  • 在8259芯片內部,有中斷屏蔽寄存器(Interrupt Mask Register,IMR),這是個8位寄存器,對應着該芯片的8箇中斷輸入引腳,對應的位是0還是1,決定了從該引腳來的中斷信號是否能夠通過8259送往處理器(0表示允許,1表示阻斷)。當外部設備通過某個引腳送來一箇中斷請求信號時,如果它沒有被IMR阻斷,那麼,它可以被送往處理器。

  • 8259芯片是可編程的,主片的端口號是0x20和0x21,從片的端口號是0xa0和0xal,可以通過這些端口訪問8259芯片,設置它的工作方式,包括IMR的內容。

  • 8259的主片引腳0(IR0)接的是系統定時器/計數器芯片;從片的引腳0(IRO)接的是實時時鐘芯片RTC。

  • 複習一下中斷向量表(IVT)的位置:從內存0x00000開始,到0x003ff結束

  • 每個中斷在中斷向量表中佔四個字節

  • NMI發生時,處理器不會從外部獲得中斷號,它自動獲得中斷號2

  • 中斷隨時可能發生,中斷向量表的建立和初始化工作是由BIOS在計算機啓動時負責完成的。BIOS爲每個中斷號填寫入口地址,因爲它不知道多數中斷處理程序的位置,所以,一律將它們指向一個相同的入口地址,在那裏,只有一條指令:iret。

  • 南橋內部集成了實時時鐘電路(RTC)和兩小塊由互補金屬氧化物組成的靜態存儲器(CMOS RAM)

  • CMOSRAM的訪問,需要通過兩個端口來進行。0x70或者0x74是索引端口,用來指定CMOSRAM內的單元;0x71或者0x75是數據端口,用來讀寫相應單元裏的內容。

  • 端口0×70的最高位(bit 7)是控制NMl中斷的開關。當它爲0時,允許NMI中斷到達處理器,爲1時,則阻斷所有的NMl信號;其他7個比特,即0~6位,則實際上用於指定CMOSRAM單元的索引號。
    在這裏插入圖片描述

  • 在這裏插入圖片描述

  • not按位取反

  • test指令和and指令相同,都是將兩個操作數進行邏輯與

  • 軟中斷是由int指令引起的中斷處理。這類中斷也不需要中斷識別總線週期,中斷號在指令中給出。

  • int3是斷點中斷指令,機器指令碼爲CC。

  • int n 一類軟中斷

  • into 溢出中斷

  • 32位寄存器的高16位是不可獨立使用的,但是低16位保持了同16位處理器的兼容性

  • 因爲64位寄存器有32根地址線,能夠訪問完整的4G內存,所以提供了一種平坦模型(Flat Mode)只分一個段,可以視爲不分段。

  • 在32位模式下,處理器要求在加載程序時,先定義該程序所擁有的段,然後允許使用這些段。定義段時,除了基地址(起始地址)外,還附加了段界限、特權級別、類型等屬性。當程序訪問一個段時,處理器將用固件實施各種檢查工作,以防止對內存的違規訪問。

  • 傳統的段寄存器保存的不再是16位段基地址,而是段的選擇子,用於選擇所要訪問的段。出了段選擇器之外,每個段寄存器還包括一個不可見的部分,叫做描述符高速緩存器。

  • 32位處理器提供虛擬8086模式(V86模式),V86模式是保護模式的一種

  • 未開啓頁功能時,段部件產生的線性地址就是物理地址;開啓頁功能後,段部件產生的地址是線性地址,需要經過頁部件轉換,纔是物理地址。

  • 寄存器的速度是最快的,原因在於它使用了觸發器,這是一種利用反饋原理製作的存儲電路。觸發器的工作速度是納秒(ns)級別的,當然也可以用來作爲內存的基本單元,即靜態存儲器(SRAM),缺點是成本太高,價格也不菲。所以,製作內存芯片的材料一般是電容和單個的晶體管,由於電容需要定時刷新,使得它的訪問速度變得很慢,通常是幾十個納秒。因此,它也獲得了一個恰當的名字:動態存儲器(DRAM),我們所用的內存芯片,大部分都是DRAM。最後,硬盤是機電設備,是機械和電子的混合體,它的速度最慢,通常在毫秒級(ms)。

  • 在處理器內部,有一個小容量的高速緩存器,叫分支目標緩存器(Branch Target Buffer,BTB)。當處理器執行了一條分支語句後,它會在BTB中記錄當前指令的地址、分支目標的地址,以及本次分支預測的結果。下一次,在那條轉移指令實際執行前,處理器會查找BTB,看有沒有最近的轉移記錄。如果能找到對應的條目,則推測執行和上一次相同的分支,把該分支的指令送入流水線。

  • 在這裏插入圖片描述

  • 在這裏插入圖片描述

  • 一個段有關的信息需要8個字節來描述,稱爲段描述符

  • 存放段描述符的表稱爲描述符表,最主要的描述符表是全局描述符表(GDT),通常定義在1MB以下的內存中(實模式只能訪問1MB的內存)。

  • 爲了跟蹤全局描述符表,處理器提供了一個48位的寄存器稱爲全局描述符表寄存器(GDTR),前16位存放邊界,後32位存放線性地址

  • 段描述符格式
    高32位

長度 含義 解釋
0~7 段基地址23~16
8~11 TYPE 描述符子類型
12 S 段描述符的類型,0系統段/1數據段
13~14 DPL 特權級別
15 P 段存在位
16~19 段界限19~16
20 AVL 軟件可以使用的位
21 L 64位代碼段標誌
22 D/B 默認的操作數大小
23 G 粒度,解釋段界限的含義
24~31 段基地址31~24

低32位

長度 含義 解釋
0~15 段界限15~0
16~31 段基地址15~0
  • 界限值*粒度=段內偏移量(向上擴展的段)
  • 界限值*粒度+1=段內偏移量(向下擴展的段)
  • 在這裏插入圖片描述
  • 第一個描述符必須是空描述符,這個描述符的訪問被禁止
  • cli禁止中斷
  • 在32位cpu上,每當引用一個段時。處理器自動將段地址左移四位,傳送到描述符高速緩存器,此後,一直使用描述符高速緩存器的內容作爲段地址。(實模式下,段寄存器描述符高速緩存器的內容僅僅低20位有效)
  • 在保護模式下,傳送到段選擇器的內容不是邏輯段地址,而是段描述符在描述符表中的索引號
  • 在這裏插入圖片描述
  • TI是描述符表指示器,TI=0,表示描述符在GDT中,TI=1,描述符在LDT中。
  • RPL是請求特權級,表示給出當前選擇子的那個程序的特權級別。
  • 處理器將索引號乘以8找到GDT中的描述符之後將描述符加載到描述符高速緩存中的過程在這裏插入圖片描述
  • 保護模式下,不允許使用mov指令改變段寄存器CS的內容
  • 在這裏插入圖片描述
  • 有前綴的指令比沒有前綴的指令多花一個時鐘週期
進入保護模式的過程:首先是創建GDT,並且安裝進入保護模式時要使用的描述符,包括代碼段、數據段、別名、棧段
然後初始化描述符表寄存器GDTR
mov word [cs: pgdt+0x7c00],39 ;描述符表的界限
lgdt [cs: pgdt+0x7c00]
然後取消A20屏蔽
關閉中斷機制
設置PE位,正式進入保護模式
  • 在進行棧操作時,必須符合以下規則:實際使用的段界限+1≤(ESP的內容一操作數的長度)≤OxFFFFFFFF
  • 處理器訪問數據段時,必須符合以下規則:0≤(EA+操作數大小一1)實際使用的段界限
  • 實際段界限=(描述符中的段界限+1)*4KB-1
  • 內核
段名 作用
公用例程段 提供各種用途的子程序
內核數據段 供內核自己使用的可讀寫內存空間
內核代碼段 分配內存,讀取和加載用戶程序,控制用戶程序執行
尾部 用於計算內核長度
內存佈局圖
用戶程序和數據區
文本模式顯示緩衝區
系統核心程序和數據
全局描述符表
初始化代碼段(主引導程序)
系統核心棧
  • 內核要具有訪問全部內存空間的數據段在GDT中聲明

  • 我們使用lgdt [cs:pgdt+0x7c00]指令來加載全局描述符表寄存器,要訪問pgdt的內存空間,使用0x7c00+pgdt+0x02

  • 在加載內核中的段並且生成新的描述符表的時候,因爲描述符並非顯而易見,所以需要一個過程(假如是make_gdt_descriptor)來得到描述符,需要的參數是:段的基地址、段界限、段屬性。生成後需要重新加載GDTR。

  • 僞指令助記符
    DB 定義的變量爲字節型
    DW 定義的變量爲字類型(雙字節)
    DD 定義的變量爲雙字型(4字節)
    DQ 定義的變量爲4字型(8字節)
    DT 定義的變量爲10字節型

  • 定位類型
    PARA: 段的起點從節邊界開始
    (16個字節爲1節)
    BYTE: 段的起點從存儲器任何地址開始
    WORD:段的起點從偶地址開始
    PAGE: 段的起點從頁邊界開始
    (256個字節爲1頁)

  • 爲了有效地在任務之間實施隔離,處理器建議每個任務都應當具有自己的描述符表,稱爲局部描述符表LDT(Local Descriptor Table),並且把專屬於自己的那些段放到LDT中。

  • 和GDTR一樣,LDTR包含了32位線性基地址字段和16位段界限字段,以指示當前LDT的位置和大小。

  • 段選擇子(16位)結構:
    INDEX:在GDT數組或LDT數組的索引號
    TI:Table Indicator,這個值爲0表示查找GDT,1則查找LDT
    RPL:請求特權級。以什麼樣的權限去訪問段。

  • 爲了保存任務的狀態,並在下次重新執行時恢復它們,每個任務都應當用一個額外的內存區域保存相關信息,這叫做任務狀態段(Task State Segment:TSS)。處理器用TR寄存器(任務寄存器)來指向當前任務的TSS。

  • 特權級(Privilege Level),也叫特權級別,是存在於描述符及其選擇子中的一個數值,當這些描述符或者選擇子所指向的對象要進行某種操作,或者被別的對象訪問時,該數值用於控制它們所能進行的操作,或者限制它們的可訪問性。

  • 當任務在自己的局部空間內執行時,當前特權級CPL是3;當它通過調用系統服務,進入操作系統內核,在全局空間執行時,當前特權級CPL就變成了0。總之,很重要的一點是,不能僵化地看待任務和任務的特權級別。

  • 那些只有在當前特權級CPL爲0時才能執行的指令,稱爲特權指令(Privileged Instructions)。典型的特權指令包括加載全局描述符表的指令lgdt(它在實模式下也可執行)、加載局部描述符表的指令lldt、加載任務寄存器的指令ltr、讀寫控制寄存器的mov指令、停機指令hlt等十幾條。

  • 一般來說,控制轉移只允許發生在兩個特權級相同的代碼段之間。如果當前特權級爲2,那麼,它可以轉移到另一個DPL爲2的代碼段接着執行,但不允許轉移到DPL爲0、1和3的代碼段執行。不過,爲了讓特權級低的應用程序可以調用特權級高的操作系統例程,處理器也提供了相應的解決辦法。

  • 第一種方法是將高特權級的代碼段定義爲依從的。代碼段描述符的TYPE字段有C位,如果C=0,這樣的代碼段只能供同特權級的程序使用;否則,如果C=1,則這樣的代碼段稱爲依從的代碼段,可以從特權級比它低的程序調用並進入。

  • 但是,即使是將控制轉移到依從的代碼段,也是有條件的,要求當前特權級CPL必須低於,或者和目標代碼段描述符的DPL相同。即,在數值上,CPL=目標代碼段描述符的DPL

  • 依從的代碼段不是在它的DPL特權級上運行,而是在調用程序的特權級上運行。就是說,當控制轉移到依從的代碼段上執行時,不改變當前特權級CPL,段寄存器CS的CPL字段不發生變化,被調用過程的特權級依從於調用者的特權級,這就是爲什麼它被稱爲“依從的”代碼段。

  • 進入保護模式之後,處理器自動將當前特權級CPL設定爲0,以0特權級的身份開始執行保護模式的初始指令。

特權級別縮寫 含義
CPL 當前進程的權限級別
RPL 位於段選擇子中,指請求特權級
DPL 存儲在段描述符中,規定訪問該段的權限級別
  • 調用門(Call-Gate)用於在不同特權級的程序之間進行控制轉移。本質上,它只是一個描述符,一個不同於代碼段和數據段的描述符,可以安裝在GDT或者LDT中。該描述符的格式如圖14-9所示,下面是低32位,上面是高32位。
    在這裏插入圖片描述
  • 爲了切換棧,每個任務除了自己固有的棧之外,還必須額外定義幾套棧,具體數量取決於任務的特權級別。0特權級任務不需要額外的棧,它自己固有的棧就足夠使用,因爲除了調用返回外,不可能將控制轉移到低特權級的段;1特權級的任務需要額外定義一個描述符特權級DPL爲0的棧,以便將控制轉移到0特權級時使用;2特權級的任務則需要額外定義兩個棧,描述符特權級DPL分別是0和1,在控制轉移到0特權級和1特權級時使用;3特權級的任務最多額外定義3個棧,描述符特權級分別是0、1和2,在控制轉移到0、1和2特權級時使用。
  • 加載程序並創建一個任務,需要用到很多數據,比如程序的大小、加載的位置,等等。當任務執行結束,還要依據這些信息來回收它所佔用的內存空間。還有,多任務系統是多個任務同時運行的,特別是在一個單處理器(核)的系統中,爲了在任務之間切換和輪轉,必須能追蹤到所有正在運行的任務,記錄它們的狀態,或者根據它們的當前狀態來採取適當的操作。
  • 爲了滿足上述的需求,內核應當爲每一個任務創建一個內存區域,來記錄任務的信息和狀態,稱爲任務控制塊(TCB)。
  • 在這裏插入圖片描述
  • 當用戶程序被讀入內存,並處於運行或者等待運行的狀態時,就視爲一個任務。任務有自己的代碼段和數據段(包括棧),這些段必須通過描述符來引用。
  • 任務狀態段TSS在這裏插入圖片描述
  • EFLAGS寄存器的IOPL位決定了當前任務的IVO特權級別。如果當前特權級CPL高於,或者和任務的I/O特權級IOPL相同時,即,在數值上,CPL≤IOPL時,所有VO操作都是允許的,針對任何硬件端口的訪問都可以通過
  • 在TSS內偏移爲102的那個字單元,保存着IVO許可位串(IVO許可位映射區)的起始位置,從TSS的起始處(0)算起。因此,如果該字單元的內容大於或者等於TSS的段界限(在TSS描述符中),則表明沒有I/O許可位串。在這種情況下,如果當前特權級CPL低於當前的I/O特權級IOPL,執行任何硬件I/O指令都會引發處理器異常中斷。說明一下,和LDT一樣,必須在GDT中創建TSS的描述符,TSS描述符中包括了TSS的基地址和界限,該界限值包括I/O許可位映射區在內。
  • 爲了解決字節越界問題,要求TSS映射區最後一個字節的所有比特必須都是1,即0xff
  • 未完待續
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章