• <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>

            天衣有縫

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

            4課:中斷和異常1


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

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

            原作者:xiaoming.mo at skelix dot org

            MSN & Email: jinglexy at yahoo dot com dot cn

            目標                  下載源程序

             

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


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

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

             

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

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

             

            中斷分類

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

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

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

            IRQ 引腳

            中斷向量

            中斷

            IRQ0

            08

            系統(tǒng)時鐘

            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兼容機器上有16個引腳。

            異常分離

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

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

             

            中斷向量

            異常

            00

            除零錯

            01

            調(diào)試異常

            02

            非可屏蔽中斷 (NMI)

            03

            斷點 (INT 3 指令)

            04

            溢出 (INTO 指令)

            05

            越界 (BOUND指令)

            06

            無效的指令

            07

            無協(xié)處理器

            08

            雙重錯誤

            09

            協(xié)處理器越界

            0A

            無效的 TSS

            0B

            段不存在

            0C

            棧溢出

            0D

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

            0E

            頁錯誤

            0F

            Intel 保留

            10

            協(xié)處理器錯誤

            11-19

            Intel保留

            1A-FF

            未使用

             

             

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


            04/init.c

            static void
            pic_install(void) {
                outb(0x11, 0x20);            // ICW1
            命令字,使用邊沿觸發(fā)中斷,多片8259A級聯(lián)
                outb(0x11, 0xa0);
                outb(0x20, 0x21);            // ICW2
            命令字,重新分配中斷向量編號IRQ0IRQ7
                outb(0x28, 0xa1);            // ICW2
            命令字,重新分配中斷向量編號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所有中斷
            }

             

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

            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)在我們知道了怎么重新分配中斷向量,但是另外一個問題:實模式下的中斷向量表已經(jīng)被內(nèi)核覆蓋了,怎么辦呢?我們不得不重寫它們了。這一點也不有趣。
            IDT and ISR

            處理器執(zhí)行中斷和異常的方法都是一樣的,當某個中斷或異常發(fā)生時,處理器停止當前任務(wù)跳轉(zhuǎn)到特定的例程中去,這個例程就是ISR。當ISR執(zhí)行完后,返回控制到原來的任務(wù)中去。那么處理器又是怎么樣路由這些中斷的呢?原因是系統(tǒng)中存在一個叫IDTRIDT register)的寄存器,它指向內(nèi)存中的一個叫中斷描述符表的緩沖,這個描述符表就定義了所有中斷例程(即ISR)的邏輯地址等。它和GDT看起來很像,只有個別的位不同而已。IDT中為每個中斷或異常都準備了獨立的一項,我們常稱之為向量(就是上面重新分配的中斷向量編號)。IDT可以看作是一個64位長整型的數(shù)組,最多可有256項。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)很熟悉了,后面的代碼中我會詳細講解到。實際上,有多種描述符,但這里我們只用到中斷門。現(xiàn)在我們設(shè)置CPU使其路由到正確的中斷例程ISR中去,那么ISR到底是什么呢?因為中斷和異常會停止當前執(zhí)行的任務(wù)并且需要返回回來繼續(xù)執(zhí)行,所以ISR需要保存當前任務(wù)的運行環(huán)境,就是一大堆寄存器。需要在進入ISR的時候保存這些寄存器,并在離開的時候回復(fù)它們。

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

             

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

             _________________        _________________ 

            |                 |      |                 |      |

            |_________________|      |_________________|      |

            |                 |      |                 |      |

            |_________________|      |_________________|      | 棧增長方向

            |    Old EFLAGS   |      |    Old EFLAGS   |      |

            |_________________|      |_________________|      |

            |    Old CS       |      |    Old CS       |      |

            |_________________|      |_________________|      |

            |    Old EIP      |      |    Old EIP      |     \|/

            |_________________|      |_________________|

            |                 |      |    Error Code   |

            |_________________|      |_________________|

             

             

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

             _________________        _________________ 

            |    Old SS       |      |    Old SS       |      |

            |_________________|      |_________________|      |

            |    Old ESP      |      |    Old ESP      |      |

            |_________________|      |_________________|      | 棧增長方向

            |    Old EFLAGS   |      |    Old EFLAGS   |      |

            |_________________|      |_________________|      |

            |    Old CS       |      |    Old CS       |      |

            |_________________|      |_________________|      |

            |    Old EIP      |      |    Old EIP      |     \|/

            |_________________|      |_________________|

            |                 |      |    Error Code   |

            |_________________|      |_________________|


            我假定讀者看到這里還沒有昏菜,如果是的話找些保護模式的書補一補哦。也許看到下面的程序可能更清晰一些。在前面課程中我們在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      #
            進入到C語言編寫的程序中

             

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

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

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

             

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

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

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

            }

            // 這個函數(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};    //
            防止默認4字節(jié)對齊,該變量占6字節(jié)

                for (i=0; i<VALID_ISR; ++i)        // isr
            數(shù)組存放著所有的ISR例程地址,后面會講到
                    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;                //
            測試除零異常,看看發(fā)生了什么
            }

             

             

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

             

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


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

             

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

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

            查找2007.4月份文檔即可。

             

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

            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
                    +-----------+

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

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

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

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


                    addl     $4,      %esp      //
            我們當然不能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)在階段中,所有異常暫時打印一些寄存器,只是演示一下而已:
            04/exceptions.c

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

            info 是這個文件最后定義的一個函數(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ù),不費口舌了這里。
            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 不使用標準庫, -nostdinc -I include 告訴 GCC 只在本目錄的include文件夾下找尋頭文件。-Wno-long-long 上面已經(jīng)說了。-fomit-frame-pointer 告訴編譯器可能優(yōu)化而不使用棧寄存器,個人覺得不要使用這個選項為好,用-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

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

             

            久久精品国产99久久香蕉| 久久久久国色AV免费看图片| 欧美激情精品久久久久久久九九九| 久久久精品2019免费观看| 久久99热这里只频精品6| 精品99久久aaa一级毛片| 国产精品天天影视久久综合网| 久久婷婷五月综合97色一本一本 | 伊人久久大香线蕉av不变影院| 久久九九免费高清视频| 国产精品欧美亚洲韩国日本久久 | 国产69精品久久久久777| 麻豆成人久久精品二区三区免费 | 久久www免费人成精品香蕉| 亚洲一区二区三区日本久久九| 99久久中文字幕| 免费观看久久精彩视频| 人人狠狠综合久久亚洲88| 天天爽天天爽天天片a久久网| 天天久久狠狠色综合| 久久精品亚洲精品国产欧美| 久久无码精品一区二区三区| 亚洲国产成人久久精品99| 中文字幕久久精品无码| 欧美大香线蕉线伊人久久| 久久91精品国产91久久麻豆| 国产精品免费久久久久久久久| 久久人妻少妇嫩草AV无码蜜桃| 久久只这里是精品66| 伊人久久大香线蕉av一区| 99久久人妻无码精品系列蜜桃| 久久免费小视频| 久久久久国产一级毛片高清板| 久久伊人精品一区二区三区| 久久精品国产亚洲av高清漫画| 亚洲成色999久久网站| 亚洲国产精品综合久久网络| 日韩人妻无码一区二区三区久久| 久久精品一区二区三区不卡| 亚洲日韩欧美一区久久久久我 | 精品无码久久久久国产|