<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      自制x86 Bootloader開發筆記(3)——— 進入長模式

      前言

      本項目是基于IA32架構架構(32位Intel架構)的,而IA32架構有以下的操作模式:

      • 實模式、保護模式、虛擬8086模式和系統管理模式。這些模式被稱為 傳統模式

      實模式是計算機剛啟動時的模式,在實模式下可以隨意訪問可用的內存地址,實模式比較簡單直接,但是隨著操作系統的發展,實模式這種直接操作物理內存的方式已經不適合現代操作系統了,試想一下內核數據和用戶進程的數據如果都在同一個地址空間,而雙方又可以互訪隨意訪問所有的數據,那整個系統將會變得危險起來,因為這時候用戶進程的一個錯誤操作導致內核數據被修改,就可能使得整個系統崩潰。因此,保護模式 就應運而生,保護模式提供了虛擬內存,內存分段、分頁等新特性,使得每個進程有自己的虛擬內存,其真實使用的物理地址不會被非法地修改,增加了系統的穩定性。

      而為了支持64位的程序,傳統保護模式經過擴展就發展出了 長模式。長模式(Long Mode)又叫IA-32e(Intel Architecture 32bit extension, Intel對64位技術最初的稱呼)模式,在長模式下,軟件可以使用以下兩個子模式:

      1. 64-bit mode, 支持64位操作系統和64位的應用程序。
      2. 兼容模式,支持傳統保護模式下的軟件運行,使得他們可以在64位操作系統中和64位的軟件共存。

      進入長模式

      Intel開發者手冊中的這張圖很好地描述了進入長模式的過程:

      圖片名稱

      我們一開始位于實模式,從圖中可以看出想要轉到IA32-e模式,需要設置PE位LME位CR0.PG位。PE位是控制寄存器CR0中的一個標志位,表示開啟保護功能,而在開啟保護功能之前我們必須先開啟分段的功能。CR0.PG表示是CR0寄存器中的分頁標志位,將其置為1表示開啟分頁,當然在開啟之前我們需要先設置好頁表。LME表示MSR寄存器的充模式標志位,將其置為1表示開啟長模式。

      設置分段

      想要進入保護模式就必須開啟分段,實際上我們Bootloader的內存模型并不是很需要分段,分頁就足夠了。但是在IA32架構下,分段是必須的(而分頁才是可選的),因此我們必須要設置分段。在介紹如何開啟分段之前,我們先介紹一下內存分段是如何工作的。

      圖片名稱

      圖3-1的左半部分描述了分段的實現,在開啟分段和分頁的情況下,程序所使用的內存地址并不是物理地址,而需要通過分段和分頁的處理之后,才能得到最終的物理地址。我們把程序使用的地址稱為邏輯地址,邏輯地址由段選擇子偏移組成,段選擇子指向了段描述符,段描述符中有目標內存段的基地址,通過基地址和偏移地址的組合我們就得到了線性地址。

      段選擇子是是16位的,它指向了一個段描述符,段選擇子的結構如下:

      圖片名稱

      RPL 表示特權級。TI 表示指向的段是GDT中的還是LDT中的,GDT是全局描述符表,整個系統一張,LDT是局部描述符表, 每個任務可以有一張,我們只需要設置GDT即可。Index則表示指向的是GDT或這LDT中的第幾個段描述符。

      為了降低地址翻譯的之間和編碼的復雜度,通常把段選擇自放在CS,SS,DS,ES,FS,GS這些段寄存器中。段寄存器由可見部分隱藏部分這兩部分組成,可見部分就是段選擇子,而隱藏部分就是段描述符緩存(包括段基地址,段長度限制等信息),有了段寄存器的緩存,CPU在進行地址翻譯時就不用每次都去讀取段描述符的數據。如果想要刷新段寄存器的緩存,可以使用MOV等指令更新段寄存器的值,或者使用long jmp, long call等指令來更新段寄存器(CS)的值。

      段描述符的結構如圖3-8所示:

      圖片名稱

      可以看見段描述符包含了段的基地址和長度限制等信息,如果我們的目標是進入保護模式,需要對段描述符進行設置。但是在IA32-e模式下,分段“基本上”是被關閉的,說基本上關閉因為分段機制并沒有完全關閉,Intel開發者手冊3.2.4節原話:

      In IA-32e mode of Intel 64 architecture, the effects of segmentation depend on whether the processor is running in compatibility mode or 64-bit mode. In compatibility mode, segmentation functions just as it does using legacy 16-bit or 32-bit protected mode semantics.
      In 64-bit mode, segmentation is generally (but not completely) disabled, creating a flat 64-bit linear-address space. The processor treats the segment base of CS, DS, ES, SS as zero, creating a linear address that is equal to the effective address. The FS and GS segments are exceptions. These segment registers (which hold the segment base) can be used as additional base registers in linear address calculations. They facilitate addressing local data and certain operating system data structures.

      可見在64-bit模式下,CPU直接使用了一個64位的平坦線性地址,CS,DS,ES,SS等段寄存器直接以段基地址為0進行處理。不過我們仍然需要對代碼段進行設置,因為在做特權級檢查代碼段的段描述符仍然被使用。

      Intel Manual V3 5.2.1:
      Code segments continue to exist in 64-bit mode even though, for address calculations, the segment base is treated as zero. Some code-segment (CS) descriptor content (the base address and limit fields) is ignored; the remaining fields function normally (except for the readable bit in the type field).

      長模式下的段描述符中關于基地址和段長限制等比特位被忽略:

      圖片名稱

      各個field的含義如下:

      field 含義
      A 是否被被訪問,設置成0即可,CPU訪問該段時會將其置1
      R 可讀
      C 1:一致性代碼段,0:非一致性代碼段
      DPL 特權級
      P Present,該段是否在內存中
      AVL 是否被系統軟件可用
      L 64位標記
      D 默認操作符大小,如果L為0,當前為兼容模式,D為0表示16位,1表示32位。L位1表示64bit模式,D設為0
      G 段長限制粒度,0: 字節粒度,1: 4kb粒度

      我們直接采用硬編碼的方式來編寫段描述符:

      gdt64:
      .Null:
          dq 0                                                 ; gdt中第一項必須為空描述符
      .Code:
          dq 0x00209A0000000000         ; 64-bit code descriptor (exec/read).
      .pointer:
          dw $ - gdt64 - 1                             ; 16-bit Size (Limit) of GDT.
          dd gdt64
      

      設置分頁

      再次回到圖3-1,在分段之后,分頁機制通過設置好的頁目錄和頁表,將線性地址翻譯為真正的物理地址。而在開啟之前,自然需要設置好頁目錄和頁面。在正式介紹分頁模式之前,首先介紹一下分頁模式。

      圖片名稱

      從圖中可以看出,有不分頁,32-bit Paging, PAE Paging和4-level Paging這幾種模式。

      當CR0.PG = 1 and CR4.PAE = 0時處理器采用32-bit Paging模式。32-bit Paging模式采用二級頁表的結構,將32位的線性地址翻譯成40位的物理地址。當CR0.PG = 1, CR4.PAE = 1, IA32_EFER.LME = 0是處理器采用PAE Paging模式, PAE Paging模式采用三級頁表的模式,將32位的線性地址翻譯成52位的物理地址。當CR0.PG = 1, CR4.PAE = 1, IA32_EFER.LME = 1時處理器使用4-level paging模式,4-level Paging采用四級分頁,將48位的線性地址翻譯成52位的物理地址,最多支持256TB的線性地址空間(CPU位于長模式時,支持64位的指令集,但并不等同于支持64位的線性地址,4級頁表只支持到48位,對于高位地址的比特位填0處理)。

      這里不展開32-bit Paging和PAE Paging,因為從圖2-3中看出,進入IA32-e模式需要將PE,PG,LME位都置為1,而這正是4-level paging模式的開啟條件,所以我們詳細描述一下四級頁表的設置方式。四級頁表支持4KB的頁表,2MB的頁表和1GB的頁表,我們選擇4KB的頁表。線性地址在使用4KB頁表的情況下翻譯成物理地址的過程如下圖所示:

      圖片名稱

      可以看出,48位的線性地址被分成了PML4, Directory Ptr, Directory, Table, Offset 這五個部分。其中Offset是12位,表示在頁內的偏移,12個比特位正好覆蓋了4KB。PML4, Directory Ptr, Directory, Table長度都是9位, 這幾個部分雖然名字不同,但是它們的作用是一樣的,就是表示了一個偏移,通過這個偏移取得對應表的表項,表項指向下一級的頁表。方便描述起見,將PML4, Directory Ptr, Directory, Table對應的表稱為四級頁表,三級頁表,二級頁表和一級頁表。每個頁表是一個數組,翻譯過程可以如下描述:

      三級頁表 = 四級頁表[PML4] 
      二級頁表 = 三級頁表[Directory Ptr]
      一級頁表 = 二級頁表[Directory]
      頁面 = 一級頁表[Table]
      物理地址 = 頁面[Offset]
      

      可見頁表中的項除了一級頁表指向了物理頁,其他都指向了下一級的頁表,而我們需要做的就是對頁表中的每一項進行設置,頁表項的格式如下圖:

      圖片名稱

      可以看出,各級頁表項的格式都一樣,其中高位存放下一級頁表或者頁面的地址的高位地址,低12位則存儲一些其它信息(這樣做完全沒有問題,因為頁表要求是4KB對齊的,這意味著頁表的地址低12位一定都是0,所以頁表項對于頁表的低12位我們不用進行設置,可以利用這些空間存儲一些其他的信息)。其中頁表項各個比特位的含義如下:

      BIt Position 含義
      0 (P) Present, 是否在內存中
      1 (R/W) Read/write
      2 (U/S) 1:一致性代碼段,0:非一致性代碼段
      3 (PWT) PWT位,間接決定頁面緩存類型
      4 (PCD) PCD位,間接決定頁面緩存類型
      5 (A) Accessed, 被訪問
      6 (D) Dirty
      7 (PAT) PAT位,間接決定頁面緩存類型
      8 (G) Global,當CR4.PGE為1時并且Global為1時該頁面為全局頁面。重新裝入CR3不會使全局頁面的TLB項無效。
      11:9 Ignored

      在填充頁表項時,我們只需要將P位和R/W位設置為1即可。具體代碼如下:

      %define PAGE_TABLE 0x40000
      
      fill_page_table:
          mov edi, PAGE_TABLE   ; page talbe start at 0x40000, occupy 20KB memroy and map the first 26MB
          push edi
          mov ecx, 0x10000
          xor eax, eax
          cld
          rep stosd          ; zero out 64KB memory
          pop edi
      
          lea eax, [es:edi + 0x1000]     
          or eax, 3 
          mov [es:edi], eax
      
          lea eax, [es:edi + 0x2000]
      	or eax, 3 
          mov [es:edi + 0x1000], eax
      
          mov ebx, 0x3000
          mov edx, 0x2000
          mov ecx, 52
          .loop_p4:
              lea eax, [es:edi + ebx]        
              or eax, 3
              mov [es:edi + edx], eax
      
              add ebx, 0x1000
              add edx, 8
              dec ecx
              cmp ecx, 0
              jne .loop_p4
      
          push edi               
          lea edi, [es:edi + 0x3000]
          mov eax, 3 
          .loop_page_table:
              mov [es:edi], eax
              add eax, 0x1000
              add edi, 8
              cmp eax, 0x1a00000       
              jb .loop_page_table
          pop edi
      

      我們首先在0x40000處建立臨時頁表首先映射26MB供之后加載內核的工作使用。在設置完頁表之后,就可以正式進入長模式了:

      enter_long_mode:
          call fill_page_table
          call enable_paging
          lgdt [gdt64.pointer]
      
          jmp CODE_SEG:long_mode_entry
          jmp $
      
      enable_paging:
          ; enable pae and pge
          mov eax, 10100000b
          mov cr4, eax
      
          mov eax, PAGE_TABLE
          mov cr3, eax
      
          ; set the long mode bit in the EFER MSR (model specific register)
          mov ecx, 0xC0000080
          rdmsr
          or eax, 0x00000100 
          wrmsr
      
          ; enable paging and protection
          mov eax, cr0
          or eax, 0x80000001
          mov cr0, eax
          ret
      
      [BITS 64]
      long_mode_entry:
          jmp 0x8000
          jmp $
      

      enter_long_mode函數包含三個階段fill_page_table,enable_paginglgdtjmp,第一個階段是填充頁表。第二個階段是開啟分頁,其中通過CR4開啟了分頁,將4級頁表的地址加載到CR3寄存器實現了頁表切換,然后通過對 EFER MSR進行寫入設置長模式位,最后修改CR0寄存器開啟分頁和保護正式進入長模式。lgdt加載全局描述符,jmp命令刷新段寄存器,并且跳轉到長模式下64位代碼的入口,正式進入長模式。


      項目地址: https://github.com/basic60/ARCUS

      引用

      1. https://os.phil-opp.com/entering-longmode/
      2. https://software.intel.com/en-us/articles/intel-sdm ,英特爾? 64 位和 IA-32 架構開發人員手冊:卷 3A
      3. https://stackoverflow.com/questions/49811461/why-segmentation-cannot-be-completely-disable
      posted @ 2023-11-11 17:00  basic60  閱讀(801)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 忍着娇喘人妻被中出中文字幕| 亚洲国产无套无码av电影| 国产一区在线播放av| 精品人妻人人做人人爽夜夜爽| 国产无遮挡无码视频在线观看 | 国内自拍第一区二区三区| 亚洲精品二区在线播放| 黄色三级亚洲男人的天堂| 国产精品久久久久久久专区| 国产乱码精品一区二区麻豆| 精品亚洲国产成人av| 视频二区中文字幕在线| 91精品久久一区二区三区| 午夜在线观看成人av| 国产sm调教折磨视频| 毛葺葺老太做受视频| 少妇精品视频一码二码三| 精品一区二区三区四区五区| 日本亚洲一区二区精品| 国产乱子影视频上线免费观看| 国产高清午夜人成在线观看,| 国产精品激情| 伊人色综合九久久天天蜜桃| 日韩精品人妻av一区二区三区| 国产精品成人国产乱| 久久精品国产亚洲成人av| 精品国产中文字幕在线| 在线亚洲高清揄拍自拍一品区| 国产亚洲精品超碰热| 亚洲一区二区精品另类| 五月婷婷久久中文字幕| 色综合天天综合天天综| 亚洲国产激情一区二区三区| 人妻无码中文字幕| 国产成人女人在线观看| 亚洲综合国产精品第一页| 精品无码老熟妇magnet| av偷拍亚洲一区二区三区| 九九热在线观看免费视频| 人妻在线无码一区二区三区| 精品国产这么小也不放过|