【自制操作系統】(六)接受CPU外部中斷--PIC簡介

可編程中斷控制器PIC

x86體系架構包含一個可編程的中斷控制器PIC(Programmable Interrupt Controller),用于收集外部中斷并將其發送給CPU。外部設備不能直接和CPU鏈接^ ^_

intel體系結構的PIC通常包含兩種:8259,以及最新的APIC(Advanced PIC)。

8259芯片(重點)

簡介

8259芯片由IBM公司開發,用于接受外部設備(如鍵盤)的中斷請求并將其發送給CPU。

起初,只有一個8259芯片,能向CPU提供8種中斷請求。后來,通過將另一塊8259芯片與其相連,則能提供總共15種中斷請求。

其結構如下圖所示:

PIC與CPU交互

PIC作為可編程的硬件,我們可以通過CPU執行相關指令來對其進行操作。

每個8259芯片都有一個命令端口和數據端口,具體如下:

端口名稱 IO端口號
Master 命令 0x20
Master 數據 0x21
Slave 命令 0xA0
Slave 數據 0xA1

APIC(簡介)

Advanced PIC,常見于現代多核心CPU。、其實從P5開始就已經有APIC了,雖然當時并為內嵌進CPU中。相對8259,APIC有很多優勢,將來有時間再做詳細介紹。

為什么不詳細介紹?

我們現在常見的PC,以及模擬器其實都是有APIC的。之所以不詳細介紹APIC是因為市面上很多將操作系統的教材都只是在介紹8286,對APIC設計不多,考慮到學習曲線,故只做簡單介紹。

如果我有時間完成支持多核的操作系統,我會再詳細地介紹APIC。

架構簡介

APIC包含了LAPIC(Local APIC)、和I/O APIC。每個CPU都內嵌一個LAPIC,

關閉APIC

我們的項目中選擇使用遺留的8259,因此我們選擇關閉APIC。

Intel開發手冊卷三可知:關閉APIC有兩種方式。這里只簡單介紹一種:通過MSR來關閉apic。

MSR寄存器的第11位表示了APIC是否開啟。

因為通過這么久的聯系,自己對匯編也算相對收悉了,所以部分代碼采用匯編編寫。

static void disable_local_apic(){
    uint32_t eax;
    uint32_t edx;
    
    cpuid(1, &eax, &edx);

    if (edx & CPUID_FLAG_APIC ){
        printf("Deteced APIC, will disable it.\n");
        if (edx & CPUID_FLAG_MSR){
            _shutdown_apic();
            printf("Disabled\n");
        } else {
            printf("No MSR detected!\n");
        }
    }
}

代碼會首先檢查是否有APIC和MSR寄存器,然后調用_shutdown_apic將apic關閉。

.global _shutdown_apic
.type _shutdown_apic, @function
_shutdown_apic:
    movl $0x1b, %ecx
    rdmsr 
    andl $0xFFFFF7FF, %eax
    wrmsr
    ret

其中MSR的地址為0x1b。

問題

我在閱讀資料的時候,產生了一些小問題,怕將來產生同樣的困惑故稍作記錄。

  • 如何檢測一個8259芯片是否有slave芯片?

雖然不知道如何檢測是否有slave芯片,但是除了特別特別早期的CPU,8259芯片都有slave芯片。

  • 如何檢測是否有8259芯片?

雖然不知道如何檢測,但是常見的IBM-PC兼容機都有8259芯片。

  • APIC能與8259共存與一個芯片嗎?

可以并且使用APIC之前需要禁用8259。

接下來的內容,我們將進行如下假設:我們的硬件滿足通用的IBM-PC兼容機中斷

PIC(8259A)相關編程實現

重置PIC映射關系

啟動時,BIOS程序默認將8259的中斷映射為如下表所示關系:

芯片 中斷號(in 8259) CPU接收到的中斷號
Master 0~7 8~15
Slave 8~15 112~119

從上表中可以看出,由于IBM的設計失誤,8259Master芯片的默認映射的中斷號8~15與intel保留的中斷號相沖突。因此,我們必須對其進行重新配置。

具體的硬件細節不做過多介紹,可以參看代碼(后期會開源)。下述代碼將IRQ015映射到3247。

/* reinitialize the PIC controllers, giving them specified vector offsets
   rather than 8h and 70h, as configured by default */
 
#define ICW1_ICW4   0x01        /* ICW4 (not) needed */
#define ICW1_SINGLE 0x02        /* Single (cascade) mode */
#define ICW1_INTERVAL4  0x04        /* Call address interval 4 (8) */
#define ICW1_LEVEL  0x08        /* Level triggered (edge) mode */
#define ICW1_INIT   0x10        /* Initialization - required! */
 
#define ICW4_8086   0x01        /* 8086/88 (MCS-80/85) mode */
#define ICW4_AUTO   0x02        /* Auto (normal) EOI */
#define ICW4_BUF_SLAVE  0x08        /* Buffered mode/slave */
#define ICW4_BUF_MASTER 0x0C        /* Buffered mode/master */
#define ICW4_SFNM   0x10        /* Special fully nested (not) */
 
static void remap_pic(){
    //reinitialize pic
    outb(PIC1_COMMAND, ICW1_INIT+ICW1_ICW4);    //starts the initialization sequence (in cascade mode)
    io_wait();  //on older machines its necessary to give the PIC some time to react to commands as they might not be processed quickly
    outb(PIC2_COMMAND, ICW1_INIT+ICW1_ICW4);
    io_wait();
    outb(PIC1_DATA, ICW2_PIC1);     //ICW2: Master PIC vector offset
    io_wait();
    outb(PIC2_DATA, ICW2_PIC2);   //ICW2: Slave PIC vector offset
    io_wait();
    outb(PIC1_DATA, 4);         //ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100)
    io_wait();
    outb(PIC2_DATA, 2);     //ICW3: tell Slave PIC its cascade identity (0000 0010)
    io_wait();

    outb(PIC1_DATA, ICW4_8086);
    io_wait();
    outb(PIC2_DATA, ICW4_8086);
    io_wait();

    //Enable all
    outb(PIC1_DATA, 0);
    outb(PIC2_DATA, 0);
}

添加相應的中斷處理程序

和之前處理IDT一樣,我們同樣采用兩段式的中斷處理程序。但是和通用的中斷處理程序不一樣的是:必須通知PIC,我們已經完成了對其中斷的處理,它可以進行下一輪的中斷請求。

匯編層“接收”中斷

提供如下匯編代碼作為參考:注意!在我的實現中,我用err_code來保存PIC的IRQ號,而int_num保存的是IRQ映射的中斷號

.macro IRQ irq_num,idt_num
    .global irq\irq_num
    .type irq\irq_num, @function
    irq\irq_num:
        cli
        pushl $\irq_num
        pushl $\idt_num
        jmp irq_comman_stub
.endm

IRQ 0, 32
IRQ 1, 33
...
IRQ 15, 47

上訴代碼中的irq_comman_stub和上一篇博客內的isr_comman_stub除了調用irq_handler之外,其它一致。

處理中斷,通知PIC

我們的操作系統,在接收到IRQ之后,應該通知PIC它可以處理下一個中斷。需要注意的是,如果IRQ由slave芯片發出,那么我們必須對兩塊芯片都進行通知:

#define PIC_EOI     0x20
//Send EOI to PIC
if (regs->err_code >= 8){ //Also send EOI to slave chip
    outb(PIC2_COMMAND,PIC_EOI);
}
outb(PIC1_COMMAND,PIC_EOI);

其它邏輯

參考源碼或者上一篇介紹IDT的文章。

PIC示例:接收時鐘中斷

PIT,Programmable Interval Timer,是連接在8259的第0號輸入針腳(即IRQ0)的定時器。可用于以指定的時間間隔想CPU產生中斷。

因為沒有什么復雜的知識,在此不做過多介紹。可以直接參看以下代碼:

void init_timer(uint32_t frequency){
   //Regiser timer callback
   register_i_handler(IRQ0, timer_callback);

   // The value we send to the PIT is the value to divide it's input clock
   // (1193180 Hz) by, to get our required frequency. Important to note is
   // that the divisor must be small enough to fit into 16-bits.
    uint32_t divisor = 1193180 / frequency;
    
    // Send command type
    outb(0x43, 0x36);

    //Divisor has to be sent byte-wise, so split here into upper/lower bytes.
    uint8_t l = (uint8_t)(divisor & 0xFF);
    uint8_t h = (uint8_t)((divisor >> 8) & 0xff);

    // Send the frequency divisor.
    outb(0x40, l);
    outb(0x40, h);
}

static uint32_t count = 0;
void timer_callback(registers_t *regs){
    printf("Tick: %d\n", count++);
}

VWware 運行截圖

從現在開始,我將在VWware Fusion上運行玩具系統。

可能會遇到的問題:接收不到8259中斷?

確定啟用了中斷(sti),并且能通過軟中斷的方式確認IDT能正常工作。

參考資料

  1. http://jamesmolloy.co.uk/tutorial_html/5.-IRQs%20and%20the%20PIT.html
  2. http://wiki.osdev.org/PIC
  3. http://wiki.osdev.org/APIC
  4. http://wiki.osdev.org/IOAPIC
  5. http://wiki.osdev.org/Interrupts#General_IBM-PC_Compatible_Interrupt_Information
  6. http://wiki.osdev.org/Inline_Assembly
  7. http://wiki.osdev.org/Inline_Assembly/Examples
  8. http://wiki.osdev.org/Model_Specific_Registers
  9. http://wiki.osdev.org/CPUID
  10. http://forum.osdev.org/viewtopic.php?t=11998
  11. http://www.jaist.ac.jp/iscenter-new/mpc/altix/altixdata/opt/intel/vtune/doc/users_guide/mergedProjects/analyzer_ec/mergedProjects/reference_olh/mergedProjects/instructions/instruct32_hh/vc273.htm
  12. http://ethv.net/workshops/osdev/notes/notes-3
  13. http://www.intel.cn/content/www/cn/zh/processors/architectures-software-developer-manuals.html
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,565評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,115評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,577評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,514評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,234評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,621評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,641評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,822評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,380評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,128評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,319評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,879評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,548評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,970評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,229評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,048評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,285評論 2 376

推薦閱讀更多精彩內容