青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

天衣有縫

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

4課:中斷和異常1


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

譯者http://m.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ù)做剛才的事了。

 

簡單的說,中斷和異常都是停止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)行測試),表示這個(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)程序片:例如INTOINT 3INT 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處理IRQ0IRQ7,PIC2處理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位長整型的數(shù)組,最多可有256項(xiàng)。LIDT指令可以加載IDT的地址到IDTR中,就像LGDT加載GDT地址到GDTR一樣?,F(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è)咚咚)中加載新的CS,EIP和棧,并按順序保存所有的寄存器到新的堆棧中。當(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í)不變

 _________________        _________________ 

|                 |      |                 |      |

|_________________|      |_________________|      |

|                 |      |                 |      |

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

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

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

|    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;                //
測試除零異常,看看發(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://m.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)庫, -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ì)了。

 

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            欧美久久在线| 午夜精品久久久| 欧美专区亚洲专区| 欧美国产高清| 亚洲国产婷婷综合在线精品| 国产精品亚洲а∨天堂免在线| 欧美国产日韩精品| 在线观看一区| 女主播福利一区| 欧美二区在线播放| 亚洲黄网站黄| 欧美三级在线视频| 亚洲免费成人| 亚洲天堂av图片| 国产精品永久免费观看| 亚洲一区二区三区乱码aⅴ蜜桃女 亚洲一区二区三区乱码aⅴ | 亚洲天堂av在线免费观看| 亚洲精品综合精品自拍| 欧美视频在线观看视频极品| 99在线精品观看| 午夜在线观看免费一区| 亚洲福利一区| 欧美1区免费| 亚洲精品影院在线观看| 久久精品一区二区三区中文字幕| 久久在精品线影院精品国产| 激情久久久久久久久久久久久久久久| 亚洲欧美激情四射在线日| 免费看亚洲片| 亚洲免费激情| 国产精品xxxav免费视频| 久久久精品欧美丰满| 欧美国产高潮xxxx1819| 一区二区三区www| 欧美亚洲日本一区| 久久九九精品| 久久午夜激情| 亚洲大胆人体在线| 亚洲一区二区三区高清不卡| 国产欧美视频一区二区| 欧美va天堂| 亚洲一区日韩在线| 亚洲小说欧美另类婷婷| 91久久午夜| 国产女人精品视频| 欧美成人在线免费观看| 久久精品日韩欧美| 夜夜嗨av一区二区三区免费区| 久久久国产一区二区三区| 亚洲天堂激情| 亚洲国产一区在线观看| 国产精品乱码一区二区三区| 欧美片网站免费| 欧美一级视频| 艳妇臀荡乳欲伦亚洲一区| 久久亚洲视频| 亚洲视频狠狠| 伊人色综合久久天天| 国产精品都在这里| 欧美全黄视频| 久久尤物视频| 亚洲专区一区二区三区| 宅男精品视频| 亚洲精品欧美一区二区三区| 久久精品一区二区三区不卡| 亚洲欧洲精品成人久久奇米网| 好男人免费精品视频| 欧美午夜视频| 欧美二区在线看| 欧美精品18+| 久久久天天操| 欧美淫片网站| 久久精品主播| 久久国产一二区| 午夜免费久久久久| 久久成人一区| 久久精品99国产精品| 亚洲尤物视频在线| 性做久久久久久免费观看欧美| 亚洲精品乱码| 亚洲日本成人| 亚洲深夜福利在线| 日韩亚洲精品视频| 夜夜嗨av一区二区三区网站四季av| 一本久道久久综合婷婷鲸鱼| 亚洲日本va午夜在线电影| 亚洲午夜一区二区| 久久国内精品视频| 销魂美女一区二区三区视频在线| 亚洲国产综合在线看不卡| 一区二区三区无毛| 国内久久婷婷综合| 黑人极品videos精品欧美裸| 国产综合精品| 欧美美女bb生活片| 欧美午夜寂寞影院| 国产精品日韩欧美大师| 老司机成人在线视频| 欧美日韩国产成人高清视频| 欧美日韩国产限制| 国产精品日日摸夜夜摸av| 激情久久久久久| 136国产福利精品导航| 18成人免费观看视频| 中文亚洲免费| 欧美一区二区三区在线观看| 久久精品夜色噜噜亚洲aⅴ| 亚洲高清av| 国产精品99久久久久久宅男 | 亚洲人成网站精品片在线观看 | 国产精品视频自拍| 激情欧美一区二区三区在线观看| 国产精品激情电影| 国产在线视频欧美| 91久久精品一区二区三区| 一区二区国产精品| 久久精品国产清自在天天线| 亚洲一区网站| 欧美国产日韩一区| 亚洲永久免费av| 亚洲少妇一区| 欧美日韩亚洲一区二区| 91久久国产综合久久蜜月精品 | 99国产精品国产精品毛片| 亚洲在线观看视频| 亚洲欧美一区二区原创| 欧美日韩国产综合一区二区| 国产免费亚洲高清| 国产在线视频欧美一区二区三区| 亚洲视频在线视频| 欧美~级网站不卡| 亚洲自拍偷拍视频| 国产精品欧美一区喷水| 久久一区二区三区av| 老牛嫩草一区二区三区日本| 欧美美女喷水视频| 日韩亚洲在线| 免费欧美在线| 欧美一区二区三区的| 国产欧美一区二区精品仙草咪| 日韩西西人体444www| 久久精品国产综合| 亚洲男女自偷自拍图片另类| 欧美日韩一区二区三| 亚洲欧洲午夜| 亚洲黄色片网站| 久久久久久久综合| 国产老肥熟一区二区三区| 1024国产精品| 欧美激情第10页| 久久久久国产一区二区| 欧美日韩午夜精品| 亚洲欧美中文另类| 夜夜嗨av一区二区三区四区 | 欧美日本在线一区| 亚洲欧洲美洲综合色网| 免费观看日韩av| 欧美国产日韩在线观看| 亚洲国产精品女人久久久| 久久精选视频| 欧美本精品男人aⅴ天堂| 在线免费精品视频| 老牛影视一区二区三区| 久久精品国产一区二区三区免费看| 国产精品视频一二三| 午夜精品偷拍| 欧美一区二区在线免费观看 | 国产精品高潮呻吟久久av无限| 欧美一级免费视频| 亚洲欧美在线免费| 好看不卡的中文字幕| 欧美国产综合一区二区| 麻豆国产精品一区二区三区 | 99国产精品国产精品久久| 国产精品久久看| 久久久久国产精品一区三寸| 蜜桃av久久久亚洲精品| 一本色道久久99精品综合| 在线亚洲高清视频| 国产一区日韩一区| 欧美sm视频| 欧美系列一区| 亚洲国产高清自拍| 国产精品国产亚洲精品看不卡15| 香港成人在线视频| 欧美精品在线免费| 欧美一区二区三区在线| 美国成人毛片| 久久久久久久久久码影片| 欧美高清视频| 欧美丰满少妇xxxbbb| 欧美图区在线视频| 欧美不卡在线| 欧美日韩影院| 久久一区二区三区av| 国产精品久久久久久久久久久久| 久久激情五月激情| 免费成人av在线看| 老司机午夜精品| 国产精品久久久久久久久免费樱桃|