日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux0.00 代码解析(二)

發布時間:2025/3/15 linux 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux0.00 代码解析(二) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Linux 0.00 的編譯、運行、源碼下載:
http://blog.csdn.net/longintchar/article/details/78757065
Linux 0.00 Makefile 解讀:
http://blog.csdn.net/longintchar/article/details/78857966
Linux 0.00 代碼解析——boot.s:
http://blog.csdn.net/longintchar/article/details/78766916


Linux-0.00的代碼分為兩部分——boot.s和head.s.

boot.s采用as86語言編寫,是引導啟動程序,先把內核代碼加載到物理地址0x10000處,然后把內核代碼移動到物理地址0處,接下來設置臨時GDT表等信息,再把處理器設置成保護模式,最后跳轉到內核代碼處(0地址)運行。

head.s是內核代碼,采用GNU as匯編語言編寫,實現了2個運行在特權及3上的任務,它們在時鐘中斷控制下相互切換運行,一個在屏幕上打印“A”,另一個在屏幕上打印“B”。

本文要分析的是head.s。請注意,這段代碼在運行的時候,它的起始位置在物理地址0處

1. 設置DS,ES,SS,ESP

SCRN_SEL = 0x18 TSS0_SEL = 0x20 LDT0_SEL = 0x28 TSS1_SEL = 0X30 LDT1_SEL = 0x38

SCRN_SEL等都是符號常量,代表某選擇子的值,這樣寫可讀性好。相當于c語言中的#define SCRN_SEL 0x18.

.code32 .global startup_32 .text startup_32:movl $0x10,%eaxmov %ax,%ds

.code32 是我自己加的,不然編譯會報錯。這句偽指令告訴編譯器,下面的代碼要編譯成32位代碼。
.global 表示標識符是外部的或者全局的。
.text 標識正文段的開始,并切換到text段。

movl $0x10,%eax , 0x10是數據段(在boot.s文件中定義)的選擇子,此數據段的基地址為0,界限值是0x7FF(10進制2047),粒度4KB;因為粒度是4KB,所以段長度是(2047+1)*4KB=8MB;DPL=0,向上擴展,可讀可寫。
mov %ax,%ds ,加載ds。

lss init_stack,%esp

init_stack處有6個字節,見

init_stack: # Will be used as user stack for task0..long init_stack.word 0x10

這是一個遠指針,前4個字節是偏移,后2個字節是段選擇子,這句代碼表示用偏移加載esp,用數據段選擇子0x10加載ss.

2. setup_idt

此過程用于在IDT(中斷描述符表)中安裝中斷門。代碼是

setup_idt:lea ignore_int,%edxmovl $0x00080000,%eaxmovw %dx,%ax /* selector = 0x0008 = cs */movw $0x8E00,%dx /* interrupt gate - dpl=0, present */lea idt,%edimov $256,%ecx rp_sidt:movl %eax,(%edi)movl %edx,4(%edi)addl $8,%edidec %ecxjne rp_sidtlidt lidt_opcoderet

中斷門描述符

中斷門描述符如下圖:

下面是低32位(代碼中用eax存儲),上面是高32位(代碼中用edx存儲)。

可以看出,中斷門定義了一個長指針(段選擇符:過程入口點偏移值),當發生中斷的時候,處理器使用這個長指針把程序執行權轉移到中斷處理過程中。

在edx和eax中組裝中斷門描述符

lea指令是取有效地址(偏移值)。lea ignore_int,%edx表示把ignore_int處的有效地址傳給edx. 注意,是取ignore_int處的偏移地址,而不是ignore_int處存儲的內容。這樣,過程入口點偏移值31-16組裝完畢。
movl $0x00080000,%eax, 段選擇符(=0x08,索引1,內核代碼段)組裝完畢。
movw %dx,%ax, 過程入口點偏移值15-0組裝完畢。
movw $0x8E00,%dx edx的低16位組裝完畢。

中斷處理過程就是ignore_int,用于在屏幕上打印一個’c’.

ignore_int:push %dspushl %eaxmovl $0x10, %eaxmov %ax, %ds #上一行和此行用內核數據段加載dsmovl $67, %eax #打印字符'c',實際上用AL來傳參call write_char #調用過程 write_charpopl %eaxpop %dsiret

注意:write_char這個過程沒有指定DS,但是確引用了DS,比如指令movl scr_loc, %ebx. 所以在調用write_char之前,一定要給DS賦合適的值。

write_char這個過程,我已經在代碼后面添加了注釋。

write_char:push %gspushl %ebxmov $SCRN_SEL, %ebx #SCRN_SEL是顯存段的選擇子mov %bx, %gs #gs指向顯存段movl scr_loc, %ebx #scr_loc處存放的是顯示位置shl $1, %ebx #ebx*2,得到偏移,因為一個字符用2個字節來描述movb %al, %gs:(%ebx) #al中是字符的ASCII碼,屬性用默認的shr $1, %ebx #還原ebxincl %ebx #ebx自增1,算出下一個位置cmpl $2000, %ebx #比較ebx和2000jb 1f #若 ebx < 2000 則跳轉到1movl $0, %ebx #說明ebx==2000,因為位置只有0~1999,所以把ebx置為0 1: movl %ebx, scr_loc #把ebx存入scr_loc處,更新顯示位置popl %ebxpop %gsret

可以看出,write_char的功能是把AL中的字符打印到屏幕上。
位置由scr_loc處存儲的4字節的值指定(實際上取值0~1999),打印后更新位置(計數加1)。

scr_loc:.long 0 #代碼中留出了4字節存放位置

填寫IDT

lea idt,%edi表示把idt處的有效地址加載到edi.

idt標號處的代碼是:

.align 8 idt: .fill 256,8,0

fill偽指令的格式是
.fill repeat,size,value
表示產生repeat個大小為size字節的重復拷貝。size最大是8,size字節的值是value.
所以,.fill 256,8,0表示產生8*256字節,全部用0填充。IDT最多可有256個描述符,每個描述符占8個字節。

mov $256,%ecx rp_sidt:movl %eax,(%edi)movl %edx,4(%edi)addl $8,%edidec %ecx #當ecx為0時,會使ZF=0jne rp_sidt #若ZF!=0則跳轉到rp_sidt

Intel語法的間接內存引用的格式為:
section:[base + index * scale + displacement]
而在AT&T語法中對應的形式為:
section:displacement (base, index, scale)
其中,base和index是任意的32-bit base和index寄存器。scale可以取值1,2,4,8。如果不指定scale值,則默認值為1。section可以指定任意的段寄存器作為段前綴,默認的段寄存器在不同的情況下不一樣。

舉例:

IntelAT&T
[base + index * scale + displacement]section:displacement(base, index, scale)
[eax + _variable]_variable(%eax)
[eax * 4 + _array]_array(, %eax, 4)
[ebx + eax * 8 + _array]_array(%ebx, %eax, 8)

所以,movl %eax,(%edi)表示把eax的值傳送到地址edi處,即用eax填充IDT表的0~3字節;movl %edx,4(%edi)表示把edx的值傳送到地址[edi+4]處,即用edx填充IDT表的4~7字節;這樣,IDT表中第0個中斷門就安裝好了。同理,循環安裝,一共是256個中斷門。

加載IDTR

lidt lidt_opcode 加載IDTR寄存器,lidt_opcode處定義了6個字節。前2字節是界限值,界限值是表的總長度減去1;后4字節是IDT的線性基地址。因為本文件運行時的起始地址就在物理地址0處,所以線性基地址就是idt表示的值。

lidt_opcode:.word 256*8-1 # idt contains 256 entries.long idt # This will be rewrite by code.

3. setup_gdt

這個過程就一句話

setup_gdt:lgdt lgdt_opcoderet

加載GDTR寄存器。
看一下 lgdt_opcode 處都有什么:

lgdt_opcode:.word (end_gdt-gdt)-1 .long gdt # This will be rewrite by code.

前2字節是GDT的界限值,后4字節是GDT的線性基地址。

gdt:.quad 0x0000000000000000 /* NULL descriptor */.quad 0x00c09a00000007ff /* 8Mb 0x08, base = 0x00000 */.quad 0x00c09200000007ff /* 8Mb 0x10 */.quad 0x00c0920b80000002 /* screen 0x18 - for display */.word 0x0068, tss0, 0xe900, 0x0 # TSS0 descr 0x20.word 0x0040, ldt0, 0xe200, 0x0 # LDT0 descr 0x28.word 0x0068, tss1, 0xe900, 0x0 # TSS1 descr 0x30.word 0x0040, ldt1, 0xe200, 0x0 # LDT1 descr 0x38 end_gdt:

一共定義了8個段描述符。

索引號選擇子描述符類型基地址段界限粒度PDPL備注
0-空描述符------
10x08代碼段00X7FF4KB10內核代碼段,非一致性,可讀
20x10數據段00X7FF4KB10內核數據段,向上擴展,可寫
30x18數據段0XB80000X24KB10內核顯存段,向上擴展,可寫
40x20TSS段tss00X681B13任務0的TSS段,不忙
50x28LDT段ldt00X401B13任務0的LDT描述符
60x30TSS段tss10X681B13任務1的TSS段,不忙
70x38LDT段ldt10X401B13任務1的LDT描述符

4. 重新加載段寄存器

因為GDT的內容改變了,所以應該重新加載所有段寄存器。

movl $0x10,%eax # reload all the segment registersmov %ax,%ds # after changing gdt. mov %ax,%esmov %ax,%fsmov %ax,%gslss init_stack,%esp

注意:因為內核代碼段和boot.s文件中的定義一樣,所以不用重新加載CS

5. 設置定時芯片8253

關于這個定時芯片可以參考我的博文http://blog.csdn.net/longintchar/article/details/78885556

Intel 8253芯片是可編程計數器/定時器。該芯片提供了3個獨立的16位計數器通道,每個通道可以工作在不同的工作方式下。通過向8253寫入一個控制字和一個初始計數值,就可以使它開始計數。

控制字格式如下圖:

代碼中寫入的控制字是0x36,選中通道0,先讀寫低字節再讀寫高字節,工作方式3,采用二進制計數。
通道0的端口是0x40, 先向其寫入初始計數值的低字節,再寫入初始計數值的高字節。

假設N為初始計數值。在工作方式3下,方波的頻率是輸入時鐘頻率的N分之一,又因為計數器的輸入時鐘頻率是1.193180MHz=1193180Hz,所以

1193180/N = 方波的頻率(Hz)

movl $11930, %eax表示計數值N=11930,1193180/11930約等于100,
所以方波的頻率是100Hz,周期是10ms,也就是10ms產生一個方波上升沿,此上升沿可以產生中斷請求,即10ms產生一次中斷。

# setup up timer 8253 chip.movb $0x36, %al # al中是控制字movl $0x43, %edx # 端口是0x43outb %al, %dx # 把al中的控制字寫入端口0x43movl $11930, %eax # timer frequency 100 HZ movl $0x40, %edx # 端口是0x40outb %al, %dx # 先寫低字節movb %ah, %al # 再寫高字節outb %al, %dx

【未完待續】

與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的Linux0.00 代码解析(二)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。