• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            天衣有縫

            冠蓋滿京華,斯人獨(dú)憔悴~
            posts - 35, comments - 115, trackbacks - 0, articles - 0
               :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            4課:中斷和異常1


            聲明:轉(zhuǎn)載請(qǐng)保留

            譯者http://www.shnenglu.com/jinglexy

            原作者:xiaoming.mo at skelix dot org

            MSN & Email: jinglexy at yahoo dot com dot cn

            目標(biāo)                  下載源程序

             

            在本課中,我們學(xué)習(xí)如何處理中斷和異常。并且加入中斷處理程序到skelix內(nèi)核中,這樣可以在中斷或異常來臨時(shí)打印一些可以看得到的東西,這些程序是后面課程的基礎(chǔ)。


            那么中斷和異常到底是什么咚咚呢?

            舉例來說吧,小胖正在家里吃飯,吃的很歪歪的時(shí)候,MM來了一個(gè)電話,于是接電話告訴她晚上七點(diǎn)在村口YY樹下不見不散,這就是一個(gè)突發(fā)事件,接完電話繼續(xù)吃飯。突然小胖在米飯里面發(fā)現(xiàn)一個(gè)蟑螂,errr~~~~。只好結(jié)束晚餐了,這個(gè)就是異常了。中斷是可以返回來的,比如上面的接電話,但異常就不能返回繼續(xù)做剛才的事了。

             

            簡(jiǎn)單的說,中斷和異常都是停止CPU正在做的事情,強(qiáng)制它做另外一件事,然后回到原來的控制流上繼續(xù)執(zhí)行。中斷和異常的區(qū)別在于它們的外部觸發(fā)源,對(duì)于硬件設(shè)備來說,如鍵盤輸入,系統(tǒng)時(shí)鐘等就會(huì)觸發(fā)中斷。而保護(hù)模式下的指令執(zhí)行才會(huì)引發(fā)異常,例如除零錯(cuò)誤,雙重錯(cuò)誤:),INT 3指令就是主動(dòng)觸發(fā)異常(可以在用戶態(tài)進(jìn)行測(cè)試),表示這個(gè)進(jìn)程進(jìn)入調(diào)試狀態(tài)。

            處理器為每個(gè)中斷或異常分配一個(gè)獨(dú)立的編號(hào),這個(gè)編號(hào)實(shí)際上就是中斷向量表,在實(shí)模式下它是從物理地址0開始的,現(xiàn)在早已被我們的內(nèi)核‘skelix’覆蓋了,即便沒有覆蓋,我們?nèi)匀粺o法在保護(hù)模式中使用這些實(shí)模式的中斷。在Intel手冊(cè)上,有兩種外部中斷和兩種異常。

             

            中斷分類

            1)可屏蔽中斷:通過INTR引腳告訴CPU來了一個(gè)中斷,可以被寄存器設(shè)置屏蔽調(diào)

            2)非可屏蔽中斷:通過NMI引腳告訴CPU來了一個(gè)中斷,不可屏蔽

            下面是系統(tǒng)啟動(dòng)默認(rèn)設(shè)置的中斷向量:

            IRQ 引腳

            中斷向量

            中斷

            IRQ0

            08

            系統(tǒng)時(shí)鐘

            IRQ1

            09

            鍵盤

            IRQ2

            0A

            PIC2橋接,即從8259A

            IRQ3

            0B

            COM2

            IRQ4

            0C

            COM1

            IRQ5

            0D

            LPT2

            IRQ6

            0E

            軟盤

            IRQ7

            0F

            LPT1

            IRQ8

            70

            CMOS Real Time Clock

            IRQ9

            71

             

            IRQ10

            72

             

            IRQ11

            73

             

            IRQ12

            74

            PS/2 Mouse

            IRQ13

            75

            數(shù)學(xué)協(xié)處理器

            IRQ14

            76

            硬盤設(shè)備 IDE0

            IRQ15

            77

            硬盤設(shè)備 IDE1

            上面表格中的IRQ是中斷控制器的物理引腳,直接連到外部硬件設(shè)備上的。在AT兼容機(jī)器上有16個(gè)引腳。

            異常分離

            1)處理器檢查:頁故障,陷阱等

            2)程序片:例如INTO,INT 3,INT n等系統(tǒng)調(diào)用

             

            中斷向量

            異常

            00

            除零錯(cuò)

            01

            調(diào)試異常

            02

            非可屏蔽中斷 (NMI)

            03

            斷點(diǎn) (INT 3 指令)

            04

            溢出 (INTO 指令)

            05

            越界 (BOUND指令)

            06

            無效的指令

            07

            無協(xié)處理器

            08

            雙重錯(cuò)誤

            09

            協(xié)處理器越界

            0A

            無效的 TSS

            0B

            段不存在

            0C

            棧溢出

            0D

            通用保護(hù)異常(內(nèi)存引用或其他檢查保護(hù)),Windows 9x藍(lán)屏就是它的杰作

            0E

            頁錯(cuò)誤

            0F

            Intel 保留

            10

            協(xié)處理器錯(cuò)誤

            11-19

            Intel保留

            1A-FF

            未使用

             

             

            如果讀者仔細(xì)的話,可能注意到中斷和異常編號(hào)會(huì)有沖突。IRQ0IRQ7中斷向量和異常的0x080x10重疊了,所以我們得處理一下。中斷的屏蔽可以通過8259A PICprogrammable interrupt controllers)設(shè)置。PIC1處理IRQ0IRQ7PIC2處理IRQ8IRQ15,當(dāng)中斷到來時(shí),PIC得到信號(hào)并通知CPU。CPU收到后會(huì)停止當(dāng)前任務(wù)執(zhí)行,并轉(zhuǎn)向中斷向量中指向的處理代碼。我們稱之為ISRinterrupt service routine)。8259A設(shè)置的中斷向量編號(hào)可重新分配,為了做到這點(diǎn),我們需要對(duì)8259A進(jìn)行編程,這個(gè)是有難度的事情,你可以在網(wǎng)絡(luò)上搜索相關(guān)的文章自己看下(否則本文可能很難看下去,如果你一點(diǎn)基礎(chǔ)也沒有的話,最好能很好的理解ICWOCW的幾個(gè)命令字),下面的程序就是干這事的:


            04/init.c

            static void
            pic_install(void) {
                outb(0x11, 0x20);            // ICW1
            命令字,使用邊沿觸發(fā)中斷,多片8259A級(jí)聯(lián)
                outb(0x11, 0xa0);
                outb(0x20, 0x21);            // ICW2
            命令字,重新分配中斷向量編號(hào)IRQ0IRQ7
                outb(0x28, 0xa1);            // ICW2
            命令字,重新分配中斷向量編號(hào)IRQ8IRQ15
                outb(0x04, 0x21);            // ICW3
            命令字,主從8259A鏈接設(shè)置
                outb(0x02, 0xa1);
                outb(0x01, 0x21);            // ICW4
            命令字,設(shè)置EIO/AEIO模式,緩沖方式等
                outb(0x01, 0xa1);

             

                outb(0xff, 0x21);            // 屏蔽IRQ0-IRQ7所有中斷

                outb(0xff, 0xa1);            // 屏蔽IRQ8-IRQ15所有中斷
            }

             

            一個(gè)好消息:我們現(xiàn)在開始終于使用C語言了。類似outb這樣的宏會(huì)替代一些匯編語言,這樣看起來會(huì)好過一些。不熟悉AT&T匯編的可能要補(bǔ)一下了,否則可能認(rèn)為進(jìn)度太快了。

            04/include/asm.h

            #define cli() __asm__ ("cli\n\t")
            #define sti() __asm__ ("sti\n\t")

            #define halt() __asm__ ("cli;hlt\n\t");
            #define idle() __asm__ ("jmp .\n\t");

            #define inb(port) (__extension__({    \
            unsigned char __res;    \
            __asm__ ("inb    %%dx,    %%al\n\t"    \
                                 :"=a"(__res)    \
                                 :"dx"(port));    \
            __res;    \
            }))

            #define outb(value, port) __asm__ (    \
            "outb    %%al,    %%dx\n\t"::"al"(value), "dx"(port))

            #define insl(port, buf, nr) \
            __asm__ ("cld;rep;insl\n\t"    \
            ::"d"(port), "D"(buf), "c"(nr))

            #define outsl(buf, nr, port) \
            __asm__ ("cld;rep;outsl\n\t"    \
            ::"d"(port), "S" (buf), "c" (nr))

             

            現(xiàn)在我們知道了怎么重新分配中斷向量,但是另外一個(gè)問題:實(shí)模式下的中斷向量表已經(jīng)被內(nèi)核覆蓋了,怎么辦呢?我們不得不重寫它們了。這一點(diǎn)也不有趣。
            IDT and ISR

            處理器執(zhí)行中斷和異常的方法都是一樣的,當(dāng)某個(gè)中斷或異常發(fā)生時(shí),處理器停止當(dāng)前任務(wù)跳轉(zhuǎn)到特定的例程中去,這個(gè)例程就是ISR。當(dāng)ISR執(zhí)行完后,返回控制到原來的任務(wù)中去。那么處理器又是怎么樣路由這些中斷的呢?原因是系統(tǒng)中存在一個(gè)叫IDTRIDT register)的寄存器,它指向內(nèi)存中的一個(gè)叫中斷描述符表的緩沖,這個(gè)描述符表就定義了所有中斷例程(即ISR)的邏輯地址等。它和GDT看起來很像,只有個(gè)別的位不同而已。IDT中為每個(gè)中斷或異常都準(zhǔn)備了獨(dú)立的一項(xiàng),我們常稱之為向量(就是上面重新分配的中斷向量編號(hào))。IDT可以看作是一個(gè)64位長(zhǎng)整型的數(shù)組,最多可有256項(xiàng)。LIDT指令可以加載IDT的地址到IDTR中,就像LGDT加載GDT地址到GDTR一樣。現(xiàn)在我們來看一下IDT描述符:

             

                                圖-0

             

             

             63_______________56__55__54__53__52__51_____________48_

            |                  偏移地址(3116位)                 |

            |_______________________________________________________|

             

             _47__46__45________________________________36________32_

            | P |  DPL |           01110000            |  未使用    |

            |_______________________________________________________|

             

             31____________________________________________________16

            |                     描述符選擇子                      |

            |_______________________________________________________|

             

             16_____________________________________________________

            |                 偏移地址150位)                   |

            |_______________________________________________________|

            大多數(shù)的域我們已經(jīng)很熟悉了,后面的代碼中我會(huì)詳細(xì)講解到。實(shí)際上,有多種描述符,但這里我們只用到中斷門?,F(xiàn)在我們?cè)O(shè)置CPU使其路由到正確的中斷例程ISR中去,那么ISR到底是什么呢?因?yàn)橹袛嗪彤惓?huì)停止當(dāng)前執(zhí)行的任務(wù)并且需要返回回來繼續(xù)執(zhí)行,所以ISR需要保存當(dāng)前任務(wù)的運(yùn)行環(huán)境,就是一大堆寄存器。需要在進(jìn)入ISR的時(shí)候保存這些寄存器,并在離開的時(shí)候回復(fù)它們。

            如果ISR代碼和當(dāng)前任務(wù)特權(quán)級(jí)相同,那么ISR將會(huì)使用當(dāng)前任務(wù)堆棧(這個(gè)任務(wù)一般是內(nèi)核線程,當(dāng)然也有例外),或者就是切換到內(nèi)核棧中, 即從TSS(后面介紹這個(gè)咚咚)中加載新的CSEIP和棧,并按順序保存所有的寄存器到新的堆棧中。當(dāng)堆棧切換后(如果有的話),CPU會(huì)自動(dòng)保存SS, ESP, EEFLAGS, CS  EIP等寄存器到棧中。有些異常會(huì)帶一個(gè)錯(cuò)誤號(hào)(表示錯(cuò)誤的一些信息),這個(gè)錯(cuò)誤號(hào)也會(huì)自動(dòng)壓入棧中。之后IDT中的CSEIP將會(huì)被加載從而執(zhí)行ISR例程。因?yàn)槲覀兪褂玫氖侵袛嚅T,所以IF標(biāo)識(shí)(EFLAGS寄存器)將會(huì)被清掉。從ISR例程返回逆序做上面的步驟即可,不值得一提。

             

                        1-特權(quán)級(jí)不變

             _________________        _________________ 

            |                 |      |                 |      |

            |_________________|      |_________________|      |

            |                 |      |                 |      |

            |_________________|      |_________________|      | 棧增長(zhǎng)方向

            |    Old EFLAGS   |      |    Old EFLAGS   |      |

            |_________________|      |_________________|      |

            |    Old CS       |      |    Old CS       |      |

            |_________________|      |_________________|      |

            |    Old EIP      |      |    Old EIP      |     \|/

            |_________________|      |_________________|

            |                 |      |    Error Code   |

            |_________________|      |_________________|

             

             

                        2-特權(quán)級(jí)變化

             _________________        _________________ 

            |    Old SS       |      |    Old SS       |      |

            |_________________|      |_________________|      |

            |    Old ESP      |      |    Old ESP      |      |

            |_________________|      |_________________|      | 棧增長(zhǎng)方向

            |    Old EFLAGS   |      |    Old EFLAGS   |      |

            |_________________|      |_________________|      |

            |    Old CS       |      |    Old CS       |      |

            |_________________|      |_________________|      |

            |    Old EIP      |      |    Old EIP      |     \|/

            |_________________|      |_________________|

            |                 |      |    Error Code   |

            |_________________|      |_________________|


            我假定讀者看到這里還沒有昏菜,如果是的話找些保護(hù)模式的書補(bǔ)一補(bǔ)哦。也許看到下面的程序可能更清晰一些。在前面課程中我們?cè)?span lang="EN-US">load.s程序的最后面打印"Hello World!",本課中我們先跳轉(zhuǎn)到C代碼中去執(zhí)行,稍后就是中斷和異常相關(guān)程序了。
            04/load.s

                    .text
                    .globl    pm_mode
                    .include "kernel.inc"
                    .org 0
            pm_mode:
                    movl    $DATA_SEL,%eax
                    movw    %ax,    %ds
                    movw    %ax,    %es
                    movw    %ax,    %fs
                    movw    %ax,    %gs
                    movw    %ax,    %ss
                    movl    $STACK_BOT,%esp

                    cld
                    movl    $0x10200,%esi
                    movl    $0x200, %edi
                    movl    $KERNEL_SECT<<7,%ecx
                    rep
                    movsl

                    call    init      #
            進(jìn)入到C語言編寫的程序中

             

            init函數(shù)將會(huì)初始化硬件和系統(tǒng)表(idtgdt):
            04/init.c

            unsigned long long *idt = ((unsigned long long *)IDT_ADDR);
            unsigned long long *gdt = ((unsigned long long *)GDT_ADDR);

            為了方便存取,我們使用一個(gè)long longia32上是8個(gè)字節(jié))表示一個(gè)描述符,GCC也許會(huì)給出警告(對(duì)于這種類型),使用--Wno-long-long 就可以了.

             

            這個(gè)函數(shù)用來填充IDT項(xiàng),index參數(shù)是索引,第二個(gè)參數(shù)offsetISR例程的地址。
            static void
            isr_entry(int index, unsigned long long offset) {                // IDT
            描述符格式請(qǐng)參考圖-0
                unsigned long long idt_entry = 0x00008e0000000000ULL |
                        ((unsigned long long)CODE_SEL<<16);

                // 通過上面設(shè)置后,idt項(xiàng)值變?yōu)?span lang="EN-US">0x00008e0000080000,表示偏移地址為0,使用0x8作為描述符選擇子(即內(nèi)核代碼段描述符),該ISR是存在的且特權(quán)級(jí)DPL0

                idt_entry |= (offset<<32) & 0xffff000000000000ULL;
                idt_entry |= (offset) & 0xffff;
                //
            上面兩行填充ISR地址
                idt[index] = idt_entry;
                //
            填充值到idt

            }

            // 這個(gè)函數(shù)安裝所有的256 ISR例程
            static void
            idt_install(void) {
                unsigned int i = 0;
                struct DESCR {
                    unsigned short length;
                    unsigned long address;
                } __attribute__((packed)) idt_descr = {256*8-1, IDT_ADDR};    //
            防止默認(rèn)4字節(jié)對(duì)齊,該變量占6字節(jié)

                for (i=0; i<VALID_ISR; ++i)        // isr
            數(shù)組存放著所有的ISR例程地址,后面會(huì)講到
                    isr_entry(i, (unsigned int)(isr[(i<<1)+1]));              //
            安裝異常,就是 i * 2 + 1

                for (++i; i<256; ++i)
                    isr_entry(i, (unsigned int)default_isr);                  //
            安裝中斷

                __asm__ __volatile__ ("lidt    %0\n\t"::"m"(idt_descr));
            }

            static void
            pic_install(void) {
            This function has been explained earlier    outb(0x11, 0x20);
                outb(0x11, 0xa0);
                outb(0x20, 0x21);
                outb(0x28, 0xa1);
                outb(0x04, 0x21);
                outb(0x02, 0xa1);
                outb(0x01, 0x21);
                outb(0x01, 0xa1);
                outb(0xff, 0x21);
                outb(0xff, 0xa1);
            }

            void
            init(void) {
                int a = 3, b = 0;

                idt_install();
                pic_install();
                sti();
                a /= b;                //
            測(cè)試除零異常,看看發(fā)生了什么
            }

             

             

            好了,現(xiàn)在進(jìn)入核心程序。我們不得不又和一些匯編程序打交道。我們知道,當(dāng)硬件向PIC發(fā)送一個(gè)信號(hào)后,PIC通知處理器停止當(dāng)前任務(wù)執(zhí)行,然后處理器查找ISR例程,然后執(zhí)行ISR,再返回控制流。所以ISR就是我們關(guān)注的地方:
            04/isr.s

             

                    .text
                    .include "kernel.inc"
                    .globl   default_isr, isr
                   
                    .macro   isrnoerror        nr            //
            用于無錯(cuò)誤碼異常的宏
                    isr\nr:
                    pushl    $0                              // push
            一個(gè)額外的0
                    pushl    $\nr
                    jmp       isr_comm
                    .endm


                    .macro    isrerror        nr
                    isr\nr:
                    pushl    $\nr
                    jmp        isr_comm
                    .endm

             

            關(guān)于宏的說明,可以參考linuxas的幫助頁。使用pinfo可以很好的瀏覽,

            這個(gè)指令在我的博客上有介紹(http://www.shnenglu.com/jinglexy),

            查找2007.4月份文檔即可。

             

            使用上面兩個(gè)宏,我們可以很方便的定義中斷和異常函數(shù)及編號(hào):

            isr:    .long    divide_error, isr0x00, debug_exception, isr0x01
                    .long    breakpoint, isr0x02, nmi, isr0x03
                    .long    overflow, isr0x04, bounds_check, isr0x05
                    .long    invalid_opcode, isr0x06, cop_not_avalid, isr0x07
                    .long    double_fault, isr0x08, overrun, isr0x09
                    .long    invalid_tss, isr0x0a, seg_not_present, isr0x0b
                    .long    stack_exception, isr0x0c, general_protection, isr0x0d
                    .long    page_fault, isr0x0e, reversed, isr0x0f
                    .long    coprocessor_error, isr0x10, reversed, isr0x11
                    .long    reversed, isr0x12, reversed, isr0x13
                    .long    reversed, isr0x14, reversed, isr0x15
                    .long    reversed, isr0x16, reversed, isr0x17
                    .long    reversed, isr0x18, reversed, isr0x19
                    .long    reversed, isr0x1a, reversed, isr0x1b
                    .long    reversed, isr0x1c, reversed, isr0x1d
                    .long    reversed, isr0x1e, reversed, isr0x1f

            上面就是init.c中使用的isr例程數(shù)組,注意類似isr0x00的咚咚就是代碼地址。

             

             

                         圖-3


                    +-----------+
                    |  old  ss  |    76
                    +-----------+
                    |  old esp  |    72
                    +-----------+
                    |  eflags   |    68
                    +-----------+
                    |    cs     |    64
                    +-----------+
                    |   eip     |    60
                    +-----------+
                    |  0/err    |    56
                    +-----------+
                    |  isr_nr   | tmp = esp
                    +-----------+
                    |   eax     |    48
                    +-----------+
                    |   ecx     |    44
                    +-----------+
                    |   edx     |    40
                    +-----------+
                    |   ebx     |    36
                    +-----------+
                    |   tmp     |    32
                    +-----------+
                    |   ebp     |    28
                    +-----------+
                    |   esi     |    24
                    +-----------+
                    |   edi     |    20
                    +-----------+
                    |    ds     |    16
                    +-----------+
                    |    es     |    12
                    +-----------+
                    |    fs     |    8
                    +-----------+
                    |    gs     |    4
                    +-----------+
                    |    ss     |    0
                    +-----------+

            恐怖的堆棧圖,我花了很多時(shí)間才把它畫出來,汗一個(gè)先:)有沒有推薦更好的工具?

            對(duì)于所有的中斷和異常來說,該堆棧幀結(jié)構(gòu)都是一樣的。

            isr_comm:
                    pushal                      //
            依次把寄存器AX、CX、DXBX、SP、BP、SIDI壓棧
                    pushl    %ds                //
            入棧,入棧,入棧......
                    pushl    %es
                    pushl    %fs
                    pushl    %gs
                    pushl    %ss
                    movw     $DATA_SEL,%ax      //
            所有數(shù)據(jù)段特權(quán)級(jí)都是0
                    movw     %ax,    %ds
                    movw     %ax,    %es
                    movw     %ax,    %fs
                    movw     %ax,    %gs

                    movl     52(%esp),%ecx      //
            看上面的堆棧圖,52就是ISR例程的編號(hào)
                    call     *isr(, %ecx, 8)    //
            不帶參數(shù)執(zhí)行isr例程


                    addl     $4,      %esp      //
            我們當(dāng)然不能popl %ss,所以就這樣跳過去了
                    popl     %gs
                    popl     %fs
                    popl     %es
                    popl     %ds
                    popal
                    addl     $8,      %esp      //
            跳過 isr_nr err_code
                    iret                        //
            返回到原來的控制流繼續(xù)執(zhí)行

                    isrNoError        0x00
                    isrNoError        0x01
                    isrNoError        0x02
                    isrNoError        0x03
                    isrNoError        0x04
                    isrNoError        0x05
                    isrNoError        0x06
                    isrNoError        0x07
                    isrError          0x08
                    isrNoError        0x09
                    isrError          0x0a
                    isrError          0x0b
                    isrError          0x0c
                    isrError          0x0d
                    isrError          0x0e
                    isrNoError        0x0f
                    isrError          0x10
                    isrNoError        0x11
                    isrNoError        0x12
                    isrNoError        0x13
                    isrNoError        0x14
                    isrNoError        0x15
                    isrNoError        0x16
                    isrNoError        0x17
                    isrNoError        0x18
                    isrNoError        0x19
                    isrNoError        0x1a
                    isrNoError        0x1b
                    isrNoError        0x1c
                    isrNoError        0x1d
                    isrNoError        0x1e
                    isrNoError        0x1f

            default_isr:                        # 硬件中斷處理例程
                    incb    0xb8000
                    movb    $2,     0xb8001
                    movb    $0x20,  %al
                    outb    %al,    $0x20       #
            發(fā)送OCW2,告訴 PIC1 ISR執(zhí)行完畢

                    outb    %al,    $0xa0       # 發(fā)送OCW2,告訴 PIC2 ISR執(zhí)行完畢
                    iret

            可以在這里找到OCW2的資料:

            http://docs.huihoo.com/gnu_linux/own_os/interrupt-8259_5.htm,

            我一直都想找一份端口大全的資料,如果哪位有可以發(fā)一份給我:

            jinglexy at yahoo dot com dot cn

            在現(xiàn)在階段中,所有異常暫時(shí)打印一些寄存器,只是演示一下而已:
            04/exceptions.c

            void
            divide_error(void) {
                __asm__ ("pushl    %%eax;call    info"::"a"(KPL_PANIC));
                halt();
            }

            info 是這個(gè)文件最后定義的一個(gè)函數(shù),它的參數(shù)就是上面圖-3中的堆棧

            void
            debug_exception(void) {
                __asm__ ("pushl    %%eax;call    info"::"a"(KPL_PANIC));
                halt();
            }

            void
            breakpoint(void) {
                __asm__ ("pushl    %%eax;call    info"::"a"(KPL_PANIC));
                halt();
            }

            void
            nmi(void) {
                __asm__ ("pushl    %%eax;call    info"::"a"(KPL_PANIC));
                halt();
            }

            void
            overflow(void) {
                __asm__ ("pushl    %%eax;call    info"::"a"(KPL_PANIC));
                halt();
            }

            void
            bounds_check(void) {
                __asm__ ("pushl    %%eax;call    info"::"a"(KPL_PANIC));
                halt();
            }

            void
            invalid_opcode(void) {
                __asm__ ("pushl    %%eax;call    info"::"a"(KPL_PANIC));
                halt();
            }

            void
            cop_not_avalid(void) {
                __asm__ ("pushl    %%eax;call    info"::"a"(KPL_PANIC));
                halt();
            }

            void
            double_fault(void) {
                __asm__ ("pushl    %%eax;call    info"::"a"(KPL_PANIC));
                halt();
            }

            void
            overrun(void) {
                __asm__ ("pushl    %%eax;call    info"::"a"(KPL_PANIC));
                halt();
            }

            void
            invalid_tss(void) {
                __asm__ ("pushl    %%eax;call    info"::"a"(KPL_PANIC));
                halt();
            }

            void
            seg_not_present(void) {
                __asm__ ("pushl    %%eax;call    info"::"a"(KPL_PANIC));
                halt();
            }

            void
            stack_exception(void) {
                __asm__ ("pushl    %%eax;call    info"::"a"(KPL_PANIC));
                halt();
            }

            void
            general_protection(void) {
                __asm__ ("pushl    %%eax;call    info"::"a"(KPL_PANIC));
                halt();
            }

            void
            page_fault(void) {
                __asm__ ("pushl    %%eax;call    info"::"a"(KPL_PANIC));
                halt();
            }

            void
            reversed(void) {
                __asm__ ("pushl    %%eax;call    info"::"a"(KPL_PANIC));
                halt();
            }

            void
            coprocessor_error(void) {
                __asm__ ("pushl    %%eax;call    info"::"a"(KPL_PANIC));
                halt();
            }


            //
            很好理解的函數(shù),不費(fèi)口舌了這里。
            info(enum KP_LEVEL kl,
                 unsigned int ret_ip, unsigned int ss, unsigned int gs, unsigned int fs,
                 unsigned int es, unsigned int ds, unsigned int edi, unsigned int esi,
                 unsigned int ebp, unsigned int esp, unsigned int ebx, unsigned int edx,
                 unsigned int ecx, unsigned int eax, unsigned int isr_nr, unsigned int err,
                 unsigned int eip, unsigned int cs, unsigned int eflags,
                 unsigned int old_esp, unsigned int old_ss) {
                static const char *exception_msg[] = {
                    "DIVIDE ERROR",
                    "DEBUG EXCEPTION",
                    "BREAK POINT",
                    "NMI",
                    "OVERFLOW",
                    "BOUNDS CHECK",
                    "INVALID OPCODE",
                    "COPROCESSOR NOT VALID",
                    "DOUBLE FAULT",
                    "OVERRUN",
                    "INVALID TSS",
                    "SEGMENTATION NOT PRESENT",
                    "STACK EXCEPTION",
                    "GENERAL PROTECTION",
                    "PAGE FAULT",
                    "REVERSED",
                    "COPROCESSOR_ERROR",
                };
                unsigned int cr2, cr3;
                (void)ret_ip;
                __asm__ ("movl    %%cr2,    %%eax":"=a"(cr2));
                __asm__ ("movl    %%cr3,    %%eax":"=a"(cr3));
                if (isr_nr < sizeof exception_msg)
                    kprintf(kl, "EXCEPTION %d: %s\n=======================\n",
                            isr_nr, exception_msg[isr_nr]);
                else
                    kprintf(kl, "INTERRUPT %d\n=======================\n", isr_nr);
                kprintf(kl, "cs:\t%x\teip:\t%x\teflags:\t%x\n", cs, eip, eflags);
                kprintf(kl, "ss:\t%x\tesp:\t%x\n", ss, esp);
                kprintf(kl, "old ss:\t%x\told esp:%x\n", old_ss, old_esp);
                kprintf(kl, "errcode:%x\tcr2:\t%x\tcr3:\t%x\n", err, cr2, cr3);
                kprintf(kl, "General Registers:\n=======================\n");
                kprintf(kl, "eax:\t%x\tebx:\t%x\n", eax, ebx);
                kprintf(kl, "ecx:\t%x\tedx:\t%x\n", ecx, edx);
                kprintf(kl, "esi:\t%x\tedi:\t%x\tebp:\t%x\n", esi, edi, ebp);
                kprintf(kl, "Segment Registers:\n=======================\n");
                kprintf(kl, "ds:\t%x\tes:\t%x\n", ds, es);
                kprintf(kl, "fs:\t%x\tgs:\t%x\n", fs, gs);
            }

            最后,還得改一下Makefile。
            04/Makefile

            AS=as -Iinclude
            LD=ld
            CC=gcc                        #
            不用說,我們開始使用gcc
            CPP=gcc -E -nostdinc -Iinclude

            CFLAGS=-Wall -pedantic -W -nostdlib -nostdinc -Wno-long-long -I include -fomit-frame-pointer

            -Wall -pedantic -W 打開所有的編譯警告,-nostdlib 告訴 GCC 不使用標(biāo)準(zhǔn)庫(kù), -nostdinc -I include 告訴 GCC 只在本目錄的include文件夾下找尋頭文件。-Wno-long-long 上面已經(jīng)說了。-fomit-frame-pointer 告訴編譯器可能優(yōu)化而不使用棧寄存器,個(gè)人覺得不要使用這個(gè)選項(xiàng)為好,用-fnoomit-frame-pointer就一定可以正確的回溯堆棧。

             

            KERNEL_OBJS= load.o init.o isr.o libcc.o scr.o kprintf.o exceptions.o
            Adds new modules into kernel.s.o:
                ${AS} -a $< -o $*.o >$*.map

            all: final.img

            final.img: bootsect kernel
                cat bootsect kernel > final.img
                @wc -c final.img

            bootsect: bootsect.o
                ${LD} --oformat binary -N -e start -Ttext 0x7c00 -o bootsect $<

            kernel: ${KERNEL_OBJS}
                ${LD} --oformat binary -N -e pm_mode -Ttext 0x0000 -o $@ ${KERNEL_OBJS}
                @wc -c kernel

            clean:
                rm -f *.img kernel bootsect *.o

            dep:
                sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
                (for i in *.c;do ${CPP} -M $$i;done) >> tmp_make
                mv tmp_make Makefile

            上面的這個(gè)自動(dòng)產(chǎn)生依賴是從linux-0.11里面來的。趙博的書上講的很詳細(xì)了。

             

            无码乱码观看精品久久| 色成年激情久久综合| 日产精品久久久久久久| 香蕉久久夜色精品国产2020| 久久夜色精品国产| 久久人人爽人人爽人人片AV不 | 久久天天躁狠狠躁夜夜avapp| 国内精品久久久久久久97牛牛| 久久亚洲高清观看| 久久人人爽人人人人爽AV| 久久99精品久久久久婷婷| 国产福利电影一区二区三区,免费久久久久久久精 | 亚洲国产精品久久久久婷婷软件 | 亚洲精品无码久久久久久| 日本福利片国产午夜久久| 思思久久好好热精品国产| 91精品国产综合久久精品| 欧美日韩中文字幕久久久不卡 | 国产精品综合久久第一页| 少妇久久久久久被弄到高潮| 精品国际久久久久999波多野| 亚洲精品国产自在久久| 国产精品va久久久久久久| 久久亚洲国产精品成人AV秋霞 | 国产99精品久久| 青春久久| 久久人人爽人人澡人人高潮AV | 久久综合丁香激情久久| 国内精品综合久久久40p| 久久久精品久久久久久| 久久综合丝袜日本网| 69久久精品无码一区二区| 午夜天堂av天堂久久久| 婷婷久久五月天| 麻豆av久久av盛宴av| 午夜福利91久久福利| 一级A毛片免费观看久久精品| 久久精品中文字幕一区| 国产精品成人久久久久三级午夜电影| 久久精品中文騷妇女内射| 久久国产乱子伦免费精品|