【自制操作系統(tǒng)】(四)加載GDT

GDT簡介

在intel 8086體系結(jié)構(gòu)中,有6個段寄存器,CPU取址采用段:偏移模式犹撒。從80286開始,為描述不同的段結(jié)構(gòu)粒褒,x86架構(gòu)引入了GDT(Global Descriptor Table识颊,全局描述符表)。GDT可以描述程序各段的組成結(jié)構(gòu)奕坟,其中主要包含了基地址(base address)祥款、大小(limit)、權(quán)限(privilege)等信息月杉。值得一提的是刃跛,GDT中還可以包含LDT的描述符。LDT苛萎,即Local Descriptor Table(局部描述表)桨昙,可以用來描述每個程序獨(dú)立的局部段描述信息。

這種架構(gòu)的出現(xiàn)腌歉,其實(shí)和當(dāng)時操作系統(tǒng)的發(fā)展緊密相關(guān)蛙酪。因為操作系統(tǒng)的實(shí)現(xiàn),除了要依靠軟件設(shè)計翘盖,還需要底層硬件對某些特性的支持桂塞。我們可以簡單地看看GDT的結(jié)構(gòu),沒必要進(jìn)行深入的理解最仑。因為GDT中某些丑陋的設(shè)計是出于對老平臺的兼容以及對當(dāng)時的操作系統(tǒng)的考量藐俺,所以我們沒有必要將精力放在研究這些遺留問題之上。

萬惡的“Backward Campability”泥彤,因此我們在實(shí)現(xiàn)操作系統(tǒng)時沒有辦法繞過GDT欲芹,因此我們必須對其有個簡單的認(rèn)識。

簡要的理論知識(可跳過)

GDT描述符

GDT長度是可變的
因此我們需要告訴底層硬件吟吝,我們操作系統(tǒng)中的GDT位置及大小菱父。為此x86體系定義了如下數(shù)據(jù)結(jié)構(gòu)用以描述GDT信息(包括GDT的大小,以及存放GDT的線性地址)剑逃。

對上圖需要進(jìn)行簡單的補(bǔ)充:Size為GDT所占的字節(jié)大小-1浙宜。因為size為兩字節(jié),所以GDT最大的大小為65535字節(jié)(供8192表項)蛹磺。

x86體系架構(gòu)梭姓,提供lgdt指令來加載gdt描述符烙丛。即lgdt [上圖所示的表的地址]

GDT表項

GDT的每個表項,抽象地可以看成包含四個字段的數(shù)據(jù)結(jié)構(gòu):基地址(Base)赴背,大芯哉搿(Limit),標(biāo)志(Flag),訪問信息(Access Byte)。

GDT中每個表項在內(nèi)存中的實(shí)際布局如下圖所示:

為什么描述段基地址的Base以及段大小的Limit字段會被拆成這種丑陋的結(jié)構(gòu)岁忘?
因為需要兼容286架構(gòu),啊哈哈哈哈~区匠。

Base和Limit字段很好理解干像,F(xiàn)lag和Access Byte字段如下圖所示:

其中每個字段代表的意義在此不做展開,這里只指出一點(diǎn):Privl字段描述了對應(yīng)段的權(quán)限等級(Ring 0~3)驰弄。

LDT

LDT麻汰,Local Descriptor Table,局部描述符表揩懒。在分頁機(jī)制出現(xiàn)以前的操作系統(tǒng)中并沒有虛擬內(nèi)存(Virtual Memory)這個概念什乙。為了讓不同程序的數(shù)據(jù)彼此互不干擾挽封,x86架構(gòu)引入了LDT概念已球,期望操作系統(tǒng)可以通過為不同的應(yīng)用程序設(shè)置不同的LDT而隔離程序間的數(shù)據(jù)。程序在使用分段機(jī)制取址的時候辅愿,可是通過設(shè)置選擇子(selector)的特定位而告訴CPU是從GDT還是從LDT中選擇對應(yīng)的段信息智亮,如下圖所示。更多的細(xì)節(jié)在此不做展開点待。

<a id="gdtend" name="gdtend"></a>

隨著分頁機(jī)制的提出阔蛉,GDT所代表的分段機(jī)制逐漸廢棄。對于現(xiàn)代操作系統(tǒng)而言癞埠,GDT的作用幾乎只是用來改變當(dāng)前CPU執(zhí)行的特權(quán)級状原,并且改變CPU的特權(quán)級也只有這種方式。

用代碼操作GDT

加載GDT描述符的任務(wù)苗踪,必須由匯編指令完成颠区,因此我們實(shí)現(xiàn)了一個匯編方法_flush_gdt, 并將其暴露給C文件通铲。注釋雖然用英文寫的毕莱,但是我相信大家應(yīng)該能閱讀。匯編代碼使用AT&T語法颅夺,使用gas作為編譯器朋截。

使用匯編代碼加載GDT

(完成這段代碼花了我一個小時,淚奔~~吧黄。因為一個小小的typo導(dǎo)致debug了好久部服,各位可直接拿去使用)

#function for loading gdt
.global _flush_gdt
.type _flush_gdt, @function
_flush_gdt:
    movl 4(%esp), %eax #Get the first argument, which is the pointer to gdt descriptor

    lgdt (%eax) #load gdt descriptor

    movw $0x10, %ax #0x10 is the offset in the GDT to our data segment
    mov %ax, %ds #copy %ax to ds,es,fs,gs,ss
    mov %ax, %es
    mov %ax, %fs
    mov %ax, %gs
    mov %ax, %ss

    ljmp $0x08,$flush_lable #Using a long jump to set %cs to 0x8

flush_lable:
    ret

使用C語言填充GDT表項

由于代碼里都有很好的注釋,我就不再做過多介紹了拗慨。

gdt.h

主要是定義了RING 0和RING 3下數(shù)據(jù)段和代碼段的Flag與Access Byte字段廓八。

#ifndef _KERNEL_GDT_H
#define _KERNEL_GDT_H

#include <stdint.h>

//Each define here is for a specific bit(bits) flag for a gdt entry
#define SEG_DESCTYPE(x) ((x) << 0x04) //Descriptor type (0 for system, 1 for code/data)
#define SEG_PRES(x)      ((x) << 0x07) // Present
#define SEG_SAVL(x)      ((x) << 0x0C) // Available for system use
#define SEG_LONG(x)      ((x) << 0x0D) // Long mode
#define SEG_SIZE(x)      ((x) << 0x0E) // Size (0 for 16-bit, 1 for 32)
#define SEG_GRAN(x)      ((x) << 0x0F) // Granularity (0 for 1B - 1MB, 1 for 4KB - 4GB)
#define SEG_PRIV(x)     (((x) &  0x03) << 0x05)   // Set privilege level (0 - 3)

#define SEG_DATA_RD        0x00 // Read-Only
#define SEG_DATA_RDA       0x01 // Read-Only, accessed
#define SEG_DATA_RDWR      0x02 // Read/Write
#define SEG_DATA_RDWRA     0x03 // Read/Write, accessed
#define SEG_DATA_RDEXPD    0x04 // Read-Only, expand-down
#define SEG_DATA_RDEXPDA   0x05 // Read-Only, expand-down, accessed
#define SEG_DATA_RDWREXPD  0x06 // Read/Write, expand-down
#define SEG_DATA_RDWREXPDA 0x07 // Read/Write, expand-down, accessed
#define SEG_CODE_EX        0x08 // Execute-Only
#define SEG_CODE_EXA       0x09 // Execute-Only, accessed
#define SEG_CODE_EXRD      0x0A // Execute/Read
#define SEG_CODE_EXRDA     0x0B // Execute/Read, accessed
#define SEG_CODE_EXC       0x0C // Execute-Only, conforming
#define SEG_CODE_EXCA      0x0D // Execute-Only, conforming, accessed
#define SEG_CODE_EXRDC     0x0E // Execute/Read, conforming
#define SEG_CODE_EXRDCA    0x0F // Execute/Read, conforming, accessed

#define GDT_CODE_PL0 (SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | SEG_PRIV(0) | SEG_CODE_EXRD)

#define GDT_DATA_PL0 (SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | SEG_PRIV(0) | SEG_DATA_RDWR)

#define GDT_CODE_PL3 (SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | SEG_PRIV(3) | SEG_CODE_EXRD)

#define GDT_DATA_PL3 (SEG_DESCTYPE(1) | SEG_PRES(1) | SEG_SAVL(0) | SEG_LONG(0) | SEG_SIZE(1) | SEG_GRAN(1) | SEG_PRIV(3) | SEG_DATA_RDWR)

struct gdt_descriptor_struct{
    uint16_t limit;
    uint32_t base;
}
__attribute__((packed));

typedef struct gdt_descriptor_struct gdt_descriptor;
typedef uint64_t gdt_entry;

void init_gdt();

#endif

gdt.c

init_gdt()函數(shù)完成了對GDT的設(shè)置和加載厦酬。主要是設(shè)置了ring 0的代碼段和數(shù)據(jù)段(各一個),ring 3的代碼段和數(shù)據(jù)段(各一個)瘫想。最開始的空表項是硬件必須的(如果沒有這項仗阅,bochs會報錯)。

#include <kernel/gdt.h>

gdt_entry entrys[5];
gdt_descriptor gdtd;

extern void _flush_gdt(uint32_t gdtp);

static gdt_entry create_entry(uint32_t base, uint32_t limit, uint16_t flag){
  uint64_t entry ;
  // Create the high 32 bit segment
  entry =  limit       & 0x000F0000;         // set limit bits 19:16
  entry |= (flag <<  8) & 0x00F0FF00;         // set type, p, dpl, s, g, d/b, l and avl fields
  entry |= (base >> 16) & 0x000000FF;         // set base bits 23:16
  entry |=  base        & 0xFF000000;        // set base bits 31:24 

  // Shift by 32 to allow for low part of segment
  entry <<= 32;

  // Create the low 32 bit segment
  entry |= base  << 16;                       // set base bits 15:0
  entry |= limit  & 0x0000FFFF;               // set limit bits 15:0

  return entry;
}

void init_gdt(){
    gdtd.limit = (sizeof(gdt_entry) * 5) - 1;
    gdtd.base = (uint32_t)entrys;

    //Fill data to gdt entries
    entrys[0] = create_entry(0, 0, 0); //Needed
    entrys[1] = create_entry(0, 0x000FFFFF, (GDT_CODE_PL0));   // Ring 0 code section
    entrys[2] = create_entry(0, 0x000FFFFF, (GDT_DATA_PL0));   // Ring 0 data section
    entrys[3] = create_entry(0, 0x000FFFFF, (GDT_CODE_PL3));   // Ring 3 code section
    entrys[4] = create_entry(0, 0x000FFFFF, (GDT_DATA_PL3));   // Ring 3 data section

    _flush_gdt((uint32_t)&gdtd);

}

實(shí)際運(yùn)行結(jié)果

如下圖所示国夜,使用bochs加載我的操作系統(tǒng)之后减噪,通過自帶的debug功能能得到下述的GDT信息,和我們在C代碼中設(shè)置的一致车吹。Succeed~

參考文獻(xiàn)

  1. https://www.wikiwand.com/en/Global_Descriptor_Table
  2. http://www.jamesmolloy.co.uk/tutorial_html/4.-The%20GDT%20and%20IDT.html
  3. http://wiki.osdev.org/GDT_Tutorial
  4. http://wiki.osdev.org/Global_Descriptor_Table
  5. http://www.osdever.net/bkerndev/Docs/gdt.htm
  6. http://bochs.sourceforge.net/doc/docbook/user/internal-debugger.html
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末筹裕,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子窄驹,更是在濱河造成了極大的恐慌朝卒,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乐埠,死亡現(xiàn)場離奇詭異抗斤,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)丈咐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門瑞眼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人棵逊,你說我怎么就攤上這事伤疙。” “怎么了辆影?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵徒像,是天一觀的道長。 經(jīng)常有香客問我蛙讥,道長锯蛀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任键菱,我火速辦了婚禮谬墙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘经备。我一直安慰自己拭抬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布侵蒙。 她就那樣靜靜地躺著造虎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纷闺。 梳的紋絲不亂的頭發(fā)上算凿,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天份蝴,我揣著相機(jī)與錄音,去河邊找鬼氓轰。 笑死婚夫,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的署鸡。 我是一名探鬼主播案糙,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼靴庆!你這毒婦竟也來了时捌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤炉抒,失蹤者是張志新(化名)和其女友劉穎奢讨,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體焰薄,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拿诸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蛤奥。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片佳镜。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡僚稿,死狀恐怖凡桥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蚀同,我是刑警寧澤缅刽,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站蠢络,受9級特大地震影響衰猛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜刹孔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一啡省、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧髓霞,春花似錦卦睹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至纵潦,卻和暖如春徐鹤,著一層夾襖步出監(jiān)牢的瞬間垃环,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工返敬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留遂庄,地道東北人。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓劲赠,卻偏偏與公主長得像涧团,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子经磅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容