1. 程式人生 > >linux0.11中head.s分析

linux0.11中head.s分析

/*
*注意!32位啟動程式碼是從絕對地址0x0000 0000開始的,這裡同樣也是頁目錄
*將要存在的地方,因此啟動程式碼會被頁目錄覆蓋掉
*/
.text
.globl _idt,_gdt,_pg_dir,_tmp_floppy_area
_pg_dir:     !頁目錄將會放在這裡
startup_32: 
/*
*注意!!!這裡已處於3位執行模式,因此這裡的0x10並不是把地址0x10放入到各個段暫存器中,它現在**其實是全域性段描述符表中的偏移值,或者更正確的說是一個描述符項的選擇符。這裡0x10含義是請求
*特權級0,選擇全域性描述符表,選擇表中的第二項。正好指向表中的資料段描述符項。
*下面程式碼含義是ds,es,fs,gs中的選擇符為setup中構造的資料段。並將堆疊放置在stack_start指向*的user_stack陣列區.然後使用本程式後面定義的新中斷描述符表和全域性段描述符表.新全域性段描述**符表中初始內容和setup中基本一樣,僅段限長修改成了16M,stack_start定義在kernel/sched.s中**,是指向user_stack陣列末端的一個長指標。
*/    
				movl $0x10,%eax				!對GNU彙編來說,每個直接數要以"$"開始要不就表示地址
				mov %ax,%ds           !
				mov %ax,%es           !
				mov %ax,%fs           !
				mov %ax,%gs           !
				lss _stack_start,%esp !表示_stack_start->ss:esp,設定系統堆疊
				call setup_idt        !呼叫設定中斷描述符表子程式
				call setup_gdt        !呼叫設定全域性描述符表子程式
				movl $0x10,%eax       !因為修改了gdt,所以重新裝載所有的段暫存器
				mov %ax,%ds           !
				mov %ax,%es           !
				mov %ax,%fs           !
				mov %ax,%gs           !
				lss _stack_start,%esp !
				xorl %eax,%eax        !
                              !
1:                            !
				incl %eax             !檢測A20地址線是否開啟.採用的方法是向0x000000處寫入任意一
				movl %eax,0x000000    !個數值,然後看記憶體0x100000處是否也是這個數值.如果一直相同
				cmpl %eax,0x100000    !的話,就一直比較下去,即宕機.表示沒有選通A20,結果核心不能
				je 1b                 !使用1M以上的記憶體
                              
                              !檢查數學協處理器是否存在.方法是修改控制暫存器CR0,在假設存
                              !在處理器的情況下執行一個協處理器指令,出錯就不存在,需要   
                              !設定CR0中的協處理器模擬位EM,並復位協處理器存在標誌MP     
				movl %cr0,%eax        !
				andl $0x80000011,%eax !save PG,PE,ET
				orl $2,%eax           !set MP
				movl %eax,%cr0        !
				call check_x86        !
				jmp after_page_tables !
				                      !
check_x86:                    !
				fninit                !
				fstsw %ax             !
				cmpb $0,%al           !
				je 1f                 !
				movl %cr0,%eax        !
				xorl $6,%eax          !
				movl %eax,%cr0        !
				ret                   !
.align 2                      !儲存邊界對齊,2表示調整到地址最後2位為零,即4位元組對齊
1:                            !
				.byte 0xDB,0xE4       !
				ret                   !
!中斷描述符表idt有256項,並都指向ignore_int中斷門,然後載入中斷描述符表暫存器,真正實用
!的中斷門以後再安裝.認為其他地方都正常時在開啟中斷.該子程式會被也表覆蓋掉
!中斷描述符表中斷是8位元組構成,格式與全域性表不同,被稱為們描述符(Gate Descriptor).0-1,6-7字
!節是偏移量,2-3是選擇符,4-5位元組是標誌
set_idt:                      !
				lea ignore_int,%edx   !ignore_int有效地址->edx暫存器
				movl $0x00080000,%eax !選擇符放入eax的高16位,selector=0x0008=cs
				movw %dx,%ax          !偏移值低16位放入eax的低16位中.eax含有們描述符低4位元組的值
				movw $0x8E00,%dx      !edx含有門描述符高4位元組的值
				                      !
				lea _idt,%edi         !_idt是中斷描述符表的地址
				mov $256,%ecx         !
rp_sidt:                      !
				movl %eax,(%edi)      !將啞中斷門描述符存入表中
				movl %edx,4(%edi)     !
				addl $8,%edi          !edi指向表中下一項
				dec %ecx              !
				jne rp_sidt           !
				lidt idt_descr        !載入中斷描述符表暫存器值
				ret                   !
                              !
setup_gdt:                    !
				lgdt gdt_descr        !載入全域性描述符表暫存器
				ret                   !
!核心的記憶體也表直接放在頁目錄之後,使用了4個表來定址16M實體記憶體.每個也表長4KB位元組,每個頁!!表需要4自己,因此一個頁表可以存放1024個表項,如果一個頁表項定址4KB,則一個頁表可以定址4M,
!頁表項格式:項前0-11位存放一些標誌,例如是否存在記憶體中(P位0),讀寫許可(R/W位1),普通使用者還
!是超級使用者(U/S位2),是否修改過(是否髒了D位6).表項位12-31是頁框地址,用於指出一頁記憶體物理
!起始地址
.org 0x1000                   !偏移0x1000處開始時第一個頁表(偏移0是頁表目錄)
pg0                           !
                              !
.org 0x2000                   !
pg1                           !
                              !
.org 0x3000                   !
pg2                           !
                              !
.org 0x4000                   !
pg3                           !
                              !
.org 0x5000                   !定義下面的記憶體資料從偏移0x5000開始
!當DMA不能訪問緩衝塊時,_tmp_floppy_area記憶體塊就可供軟盤驅動使用,其地址需要對齊調整,這樣!!就不會跨越64K邊界
!
_tmp_floppy_area:             !
						.fill 1024,1,0    !
!這幾個入棧操作(pushl)用於為呼叫/init/main.c程式和返回作準備,前面3個入棧0應該分別是envp,
!argv,argc值,但main沒有用到.pushl $L6是模擬呼叫main程式時首先將返回地址入棧的操作,所以
!main真退出時,就會返回到這裡L6繼續執行下去.入棧完成後就進行分頁處理,分頁處理完成後執行
!ret指令,此時就會將main程式地址彈出堆疊,並執行main程式去了
after_page_tables:            !
						pushl $0          !
						pushl $0          !
						pushl $0          !
						pushl $L6         !
						pushl $_main      !
						jmp setup_paging  !
L6:                           !
					jmp L6              !
					
int_msg:
					.asciz "Unknown interrupt\n\r"
.align 2
ignore_int:
						pushl %eax        !    
						pushl %ecx        !
						pushl %edx        !
						push %ds          !
						push %es          !
						push %fs          !這裡ds es fs gs雖然是16位暫存器,但入棧後以32位儲存
						movl $0x10,%eax   !置段選擇符(使ds,es,fs指向gdt表中的資料段)
						mov %ax,%ds       !
						mov %ax,%es       !
						mov %ax,%fs       !
						pushl $int_msg    !把呼叫printk函式的引數指標入棧
						call _printk      !
						popl %eax         !
						pop %fs           !
						pop %es           !
						pop %ds           !
						popl %edx         !
						popl %ecx         !
						popl %eax         !
						iret              !

!這個程式通過設定控制暫存器cr0的標誌PG位31來啟動對分頁的分頁處理功能,並設定各個頁表項
!的內容,以恆等於前16M的實體記憶體,分頁器假定不會產生非法的地址對映.注意,儘管所有的實體地址
!都應該由這個子程式進行恆等對映,但只有核心頁面管理函式能直接使用>1M的地址,所有""一般"
!函式僅使用低於1M的地址空間,或者是使用區域性資料空間,地址空間將被對映到其他一些地方去,
!mm(記憶體管理程式)會管理這些事.
!在記憶體實體地址0x0處開始存放1頁頁目錄表和4頁頁表.頁目錄表是系統所有程序公用,而這裡的4頁
!表示核心專用的.對於新程序,系統會再主記憶體區為其申請頁面存放頁表.
!
.align 2                      !
setup_paging:                 !
						movl $1024*5,%ecx !首先對5頁記憶體清零
						xorl %eax,%eax    !
						xorl %edi,%edi    !
						cld               !
						rep               !
						stosl             !
!下面4句是設定目錄表中的項,因為核心共有4個頁表所以只需要設定4項,頁目錄項的結構與頁表中
!項的結構一樣,4位元組為1項,$pg0+7表示:0x0000 1007 是頁目錄中的第一項
!則第一個頁表所在的地址0x0000 1007 & 0xffff f000=0x1000
!第一個頁表屬性標誌 0x0000 1007 & 0x0000 0fff=0x07 表示該頁存在,使用者可讀寫
						movl $pg0+7,_pg_dir !
						movl $pg1+7,_pg_dir !
						movl $pg2+7,_pg_dir !
						movl $pg3+7,_pg_dir !
!下面6行填寫4個頁表中所有項的內容,共有4(頁表)*1024(項/頁表)=4096項(0-0xfff)
!也即能對映實體記憶體4096*4kb=16M
!每項的內容是:當前項所對映的實體記憶體地址+該頁的標誌(這裡都是7)
!使用的方法是從最後一個頁表的最後一項開始按倒退順序填寫,一個頁表的最後一項在頁表中的位置
!是1023*4=4092.因此最後一頁的最後一項位置是$pg3+4092
!						
						movl $pg3+4092,%edi !edi->指向最後一頁最後一項
						movl $0xfff007,%eax !
						std                 !方向位,edi遞減
1:					stosl               !
						subl $0x1000,%eax   !每填好一項,實體地址值減0x1000
						jge 1b              !如果小於0則說明全添寫好了
!設定頁目錄基地址暫存器cr3的值,指向頁目錄表
						xorl %eax,%eax      !頁目錄表在0x0000
						movl %eax,%cr3      !cr3=0
						movl %cr0,%eax      !啟動分頁標誌
						orl $0x80000000,%eax!
						movl %eax,%cr0      !
						ret                 !去執行main
.align 2                        !
.word 0                         !
idt_descr:                      !下面是lidt指令6位元組運算元:
					.word 256*8-1         !長度
					.long _idt            !基址
.align 2                        !
.word 0                         !
gdt_descr:                      !下面是lgdt指令的6位元組運算元
					.word 256*8-1         !長度
					.long _gdt            !基址
.align 3                        !
_idt:			.fill 256,8,0         !256項,每項8位元組,填0

!全域性表.前4項是空項(不用),程式碼段描述符,資料段描述符,系統段描述符.其中系統段描述符linux沒
!有用上.後面預留了252項空間,使用者放置所建立人物的區域性描述符LDT和對應的任務狀態段TSS描述符
!0-null  1-cs  2-ds  3-sys 4-TSS0 5-LDT0 6-TSS1 7-LDT1 8-TSS2 etc...
_gdt:			.quad 0x0000 0000 0000 0000 !
					.quad 0x00c0 9a00 0000 0fff !
					.quad 0x00c0 9200 0000 0fff !
					.quad 0x0000 0000 0000 0000 !
					.fill 252,8,0               !

head.s執行完成後,記憶體的分配示意圖如下:


相關推薦

linux0.11head.s分析

/* *注意!32位啟動程式碼是從絕對地址0x0000 0000開始的,這裡同樣也是頁目錄 *將要存在的地方,因此啟動程式碼會被頁目錄覆蓋掉 */ .text .globl _idt,_gdt,_pg_dir,_tmp_floppy_area _pg_dir: !頁

Linux0.11---head.s分析

head.s位於system模組的頭部,故其命名為head。 system模組位於磁碟上setup模組之後(4個扇區),即從磁碟上第6個扇區開始的位置 從此核心完全開始執行在保護模式下運行了。 head.s的彙編程式與前面的語法格式不同,它採用的是AT&T的組合語言格

嵌入式 arm平臺kernel啟動第一階段彙編head.s分析

arm_linux核心生成過程: 1. 依據arch/arm/kernel/vmlinux.lds 生成linux核心原始碼根目錄下的vmlinux,這個vmlinux屬於未壓縮,帶除錯資訊、符號表的最初的核心,大小約23MB;  命令:arm-linux-gnu-ld

linux0.11 execve系統呼叫分析

    在Linux平臺下,我們一般都是在命令列下鍵入"./hello"來執行一個當前目錄下的hello應用程式("./"指定當前目錄)。雖然看似很簡單,但這麼小小的一個操作其實涉及到了很多的知識。比如:shell是如何將hello調入記憶體的?hello在執行前shell

linux2.4 啟動程式碼head.S分析

32位啟動程式碼,暫時不考慮SMP的情況。關鍵程式碼分析 頁目錄表的起始地址在0x101000,由於目前仍然處於真實模式,地址都是 實體地址 開始啟動核心 startup_32:  清方向標誌位  cld  用核心資料段的地址來初始化ds,es,fs,gs暫存器  巨

閱讀Linux0.11——head.s

最近經過反饋,博主得知了自己在輸出自己的知識方面做的非常不好。經過認真的反思,好吧主要是思考別人是怎麼做的,以及結合我的特點,得出如下結論: 1、認為每一個聽眾或者讀者都是零基礎,要直白的講出想要講給別人的知識。雖然這句話聽起來像是廢話,但是挺不容易的。舉個栗

第一次作業:基於Linux0.11操作系統的進程模型分析

機制 中斷處理程序 soft page tab nr_open sched move 關閉 1.前言 本文基於Linux0.11操作系統的源代碼,分析其進程模型。 Linux0.11下載地址:https://zhidao.baidu.com/share/20396e1704

LoadRunner 11 Analysis分析

其實在 ane 可能 大於 操作系統 摘要 類型 觀察 9.png 原文:http://www.cnblogs.com/Chilam007/p/6445165.html analysis簡介 分析器就是對測試結果數據進行分析的組件,它

linux-2.6.22.6內核啟動分析head.S引導段代碼

common begin ber 字節數 static smd 我們 ovs pro 學習目標: 了解arch/arm/kernel/head.S作為內核啟動的第一個文件所實現的功能! 前面通過對內核Makefile的分析,可以知道arch/arm/kernel/head

C++ 11的一些典型概念與分析

相比傳統的C++98與C++03, C++11中新提出了很多新的概念,本文根據C++佈道師Scott Meyers 在Youtube上的培訓視訊展開,介紹C++11中的一些典型概念,並進行分析。 1. 左值(lvalue)與右值(rvalue) C++11之前已

除錯大叔V2.0.2(2018.11.18)|http/s介面除錯、資料分析程式設計師輔助開發神器

2018.11.18 - 除錯大叔 V2.0.2*支援http/https協議的get/post除錯與反饋;*可儲存請求協議的記錄;*內建一批動態引數,可應用於Referer、URL、引數;支援自定義常量引數;支援自定義UA資訊(解決模擬不同網站或手機請求); *可自由管理cookie;*支援請求的代理與模擬

C++11once_flag,call_once實現分析

本文的分析基於llvm的libc++,而不是gun的libstdc++,因為libstdc++的程式碼裡太多巨集了,看起來蛋疼。 在多執行緒程式設計中,有一個常見的情景是某個任務只需要執行一次。在C++11中提供了很方便的輔助類once_flag,call_once。 宣告 首先來看一下

linux0.11程序睡眠sleep_on函式和喚醒wake_up函式分析

核心中的這兩個函式主要用於訪問資源時的同步操作。高速緩衝區的訪問就是其中的一個例子:如果兩個程序都要訪問同一個緩衝塊,那麼其中的一個程序就必然睡眠等待,直到該緩衝塊被釋放才可訪問。趙炯博士所著的linux0.11核心完全註釋一書中也是對該問題進行詳細的討論,但是我在閱讀這部

總結linux0.11核心的主,次裝置號

老會忘,記下來方便後面查閱 主裝置 型別 說明 請求操作函式 0 無 無 NULL 1 塊/字元 ram,記憶體裝置(虛擬盤等) do_rd_request 2 塊 fd,軟碟機裝置 do_fd_request 3 塊 hd,硬碟裝置 do_hd_request 4 字元

linux0.11字元裝置的讀寫過程分析

首先要知道linux系統/dev目錄下的各種裝置檔案(檔案屬性c打頭)並不佔用空間,你可以發現他們的大小為0位元組,他們的區別在於檔案的i節點的成員i_zone[0]的值不同,該值標識不同的裝置號。比如tty0檔案的裝置號為0x0400,tty1裝置號為0x0401,hd0

linux0.00 "head.s"程式詳解

// head.s包含32位保護模式初始化設定程式碼、時鐘中斷程式碼、系統呼叫中斷程式碼和兩個任務的程式碼。 // 在初始化完成之後程式移動到任務0開始執行,並在時鐘中斷控制下進行任務0和1之間的切換操作。 LATCH = 119

linux 0.11 核心學習 -- head.s

# # 這段程式碼被連線到system模組的最前面,這也是它為什麼稱之為head.s的原因。 # 從這裡開始核心完全執行在保護模式下。head.s採用的是at&t格式的 # 彙編。注意的是程式碼中的賦值方向是從左到右。 # # 這段程式實際上是出於記憶體的絕對地址0開始處。首先是載入各個

Linux0.11核心讀書筆記/boot/bootsect.s

果凍QQ:457283! 本程式完成的主要功能! 1.bootsect.s從0x7c00處開始執行! 2.將自己複製到0x90000處! 3.將setup.s程式從磁碟第2扇區讀取到0x90200處! 4.將system讀取到0x10000處! 5.獲取根檔案系統裝置號! 6

nasm重寫linux-0.11 head.s (博古以通今)

;檔名:followking/boot/head.s;本檔案改寫linux-0.11/boot/head.s,目的是為了體驗整個系統構建的過程。;我是看著趙炯《Linux核心0.11完全註釋》編寫的。不過,我是編寫程式碼,有疑問再看。;我用的nasm的語法格式。我想寫一個作業

C++11智能指針的原理、使用、實現

his animal something include expire another .cn 表現 oid 目錄 理解智能指針的原理 智能指針的使用 智能指針的設計和實現 1.智能指針的作用 C++程序設計中使用堆內存是非