1. 程式人生 > >Norlit OS —— 自制作業系統 第3章 長模式

Norlit OS —— 自制作業系統 第3章 長模式

3長模式

3.1何為長模式

本書雖然不是講述64位作業系統的,但是講到這裡就快能夠進入64位了,就寫下這張供極客研究。不想看這章的話也可以直接跳過其餘部分,只看3.3。這章的程式碼與前後一章是平行的,也就是說這章的程式碼是獨立的。

長模式是AMD64(與IntelIA-32e)中的一個模式。進入長模式後,暫存器變成了64位,我們也可以訪問64位的記憶體空間[1]了。長模式中也含有相容模式,如果GDT中當前程式碼段的CS.L0,就會進入相容模式。長模式忽略了分段,也就是說完全只靠分頁機制。

多說也不說,先來掌握以下基本概念。

3.2進入長模式的方法

我們知道,由真實模式進入到保護模式由兩步指令完成,

CR0.PE=1和一個過載CS的跳轉。

由保護模式進入長模式很相似,不過稍稍麻煩了點。我們看看步驟:

(1)首先,要進入保護模式,即CR0.PE=1

(2)其次,先初始化頁表!但注意,千萬不能開啟先分頁機制!

(3)開啟CR4.PAE

(4)開啟EFER.LME=1

(5)開啟CR0.PG=1,分頁使能。

(6)這時候處理器自動將EFER.LMA設定,進入長模式

(7)一個跳轉

你會想,這不是我們都做過的嗎。但是,你錯了,在長模式下,分頁機制變成了4層。

3.2.1地址轉換圖

先來拿圖嚇你一下,不過我們不會馬上講。我們還不知道我們的CPU夠不夠格呢。

3.3萬能工具
-CPUID的簡單介紹

CPUID是一個指令,沒有運算元。實際使用時,將一個入口引數放在EAX中然後執行指令CPUID,我們就可以在暫存器中找到CPU的各種特性。這個指令從486時代就有了。雖然我們應該先建立異常處理機制防止386的電腦出錯(其實也可以用EFLAGS來判斷CPUID是否可以用,但比之比較懶),但是現在已經沒有386的電腦了,我們大可以縮小我們的作業系統能支援的範圍。從486開始也包含了很多老式的電腦了呢。

我們先來簡單地熟悉一下CPUID命令。

CPUID指令功能號0h

入口引數

EAX

0h,表示獲得基本資訊

出口引數

EAX

最大可用功能號

EBX

Vendor(生產廠商)

Intel的處理器按順序(EBXEDXECX)是

GenuineIntel

EDX

ECX

3.3.1 CPUIDEAX=0

由於CPUID內容實在太多,我把東西都放在cpuid.inc內,在loaderinclude了一下。

;=====================================================

; ShowCPUFeatures();顯示CPU資訊

;-----------------------------------------------------

; registers changed:

;   - All GPRs

ShowCPUFeatures:

movesi, STR_CMIV

call   DispStrPM

moveax,0; CPU基本資訊

cpuid

call   DispInt

mov[VENDOR],ebx

mov[VENDOR+4],edx

mov[VENDOR+8],ecx

movesi,STR_VEN

call   DispStrPM

ret

_STR_CMIV  db13,10,"CPUIDMax Input Value: ",0

STR_CMIV   equ_STR_CMIV+LOADER_SEGPHYADDR

_STR_VEN   db13,10,"Vendor:"

_VENDOR    times13db0

STR_VEN    equ_STR_VEN+LOADER_SEGPHYADDR

VENDOR     equ _VENDOR+LOADER_SEGPHYADDR

程式碼3.3.1 ShowCPUFeatures V0.1(chapter3/a/boot/include/cpuid.inc)

執行一下,看到最大Bochs支援的最大EAX0x5CPU廠商是GenuineIntel

3.3.1 CPUID V0.1(被筆者擅自去掉了干擾資訊)

其實最重要的資訊為功能號0x1,但是筆者比較想知道處理器的名字。其實我們也可以用CPUID功能號1獲得CPU家族,但名字的話,這裡我們要用到擴充套件CPUID指令。

擴充套件CPUID指令功能號80000000h

入口引數

EAX

80000000h,表示獲得擴充套件資訊

出口引數

EAX

最大可用擴充套件功能號

EBX

保留

EDX

ECX

3.3.2擴充套件CPUID

如果出口EAX=0表示不支援擴充套件CPUID指令。如果不為0,我們可以接著獲得CPU名字了。

擴充套件CPUID指令功能號80000002h~80000004h

入口引數

EAX

80000002h~80000004h,表示獲得處理器品牌字串

出口引數

EAX~EDX

處理器品牌字串的一部分。

moveax,0x80000000; CPU擴充套件資訊

cpuid

cmpeax,0x80000004

jb      .end         ;不支援就算了

movesi, BRAND

moveax,0x80000002

cpuid

mov[esi],eax

mov[esi+4],ebx

mov[esi+8],ecx

mov[esi+12],edx

addesi,16

moveax,0x80000003

cpuid

mov[esi],eax

mov[esi+4],ebx

mov[esi+8],ecx

mov[esi+12],edx

addesi,16

moveax,0x80000004

cpuid

mov[esi],eax

mov[esi+4],ebx

mov[esi+8],ecx

mov[esi+12],edx

movesi, STR_BRD

call   DispStrPM

.end:

ret

_STR_BRD   db13,10,"Brand:"

_BRAND     times65db0

STR_BRD    equ_STR_BRD+LOADER_SEGPHYADDR

BRAND     equ _BRAND+LOADER_SEGPHYADDR

程式碼3.3.2超長的程式碼 V1.0 (chapter3/a/boot/include/cpuid.inc)

雖然長了點,但是一點都不難。執行一下。

3.3.2筆者破電腦的CPUID

由於Hyper-V利用硬體虛擬化,所以這個CPUID就是筆者電腦本身的CPUID。而且與控制面板裡的一模一樣。Well done

好了,略微扯遠了。我們現在就先看看這個CPU支不支援64位長模式。長模式標誌位是0x80000001號擴充套件CPUID功能返回值EDX29位。

moveax,0x80000001

cpuid

btedx,29

jnc    .end

movesi, STR_64

call   DispStrPM

.end:

ret

_STR_64    db13,10,"AMD64Supported!",0

STR_64     equ _STR_64+LOADER_SEGPHYADDR

程式碼3.3.3長模式支援檢測(chapter3/a/boot/include/cpuid.inc)

執行一下試試。

3.3.3 AMD64(長模式)支援!

這麼一來,長模式也支援了,我們開發過程中也沒有阻礙了。不過,為了以後著想,先看看支不支援PSEPAEPGE(全域性頁)。(你不是已經用過了麼<=Never Mind)。

我們來隆重介紹一下壓軸登場的0x1功能號。

CPUID指令功能號1h

入口引數

EAX

1h,表示獲得基本資訊

出口引數

EAX

CPU型號

EBX

CPU資訊,很重要,我們以後會用到

ECX

功能資訊

EDX

功能資訊

3.3.3壓軸好戲

3.3.4 CPUIDEAX=1ECX返回值

我們先了解一下這裡有個