为什么8259的IRQ0(8253定时器中断)不能执行ISR?

问题描述:

Here是我的操作系统的源代码。为什么8259的IRQ0(8253定时器中断)不能执行ISR?

我确定当CPU从8259 PIC(可编程中断控制器)中获得一个中断时,它总是可以正确地“偏移”到ISR(中断服务程序)阵列中。 (你可以找到整个代码here):尚未实施

isrs: 
    dd _isr0 
    dd _isr1 
    dd _irq_unhandled 
    dd _irq_unhandled 
    dd _irq_unhandled 
    .... 
    dd _isr32 
    dd _isr33 

中断处理程序由_irq_unhandled表示。到目前为止,我得到了CPU异常和软件中断(陷阱)正常工作。例如,当我的程序试图用零除时,它跳转到_isr0。或者当我尝试使用int 1,int 2,int 7或其他类似的方法时,isrs数组(IDT;中断描述符表)中的正确ISR被索引和调用。但现在,我不能让PIC执行我的中断处理程序_isr32

_isr32: 
    mov bl, 5 
    mov bh, 15 
    ; mov eax, MovCur 
    ; and eax, 0xFFFF 
    call MovCur 

    mov eax, PicIntrMsg 
    ; and eax, 0xFFFF ; retrieve offset only when base address is something different than 0 
    ; call 0x38:0x1008a 
    call Puts32 

    mov al, 0x20 
    out 0x20, al 
    ret 

_isr32只打印一条消息,以表明它被称为和发送EOI(完中断)消息给PIC。这里是常规使PIC,与除定时器和键盘禁用所有中断:

%define IRQ_0 0x20    ; IRQs 0-7 mapped to use interrupts 0x20-0x27 
%define IRQ_8 0x28    ; IRQs 8-15 mapped to use interrupts 0x28-0x36 

; Initialization Control Word 1 
%define ICW1_SEND_IC4 0x1 
%define ICW1_SINGLE 0x2 
%define ICW1_ADDRESS_INTERVAL_4 0x4 ; if set, use addresss inter, else 8 
%define ICW1_LEVEL_TRIGGERED 0x8 
%define ICW1_PIC_INITIALIZED 0x10 
%define ICW1_IVT_ADDR1 0x20 
%define ICW1_IVT_ADDR2 0x40 
%define ICW1_IVT_ADDR3 0x80 

; Initialization 
; 1. write ICW1 to port 20h 
; 2. write ICW2 to port 21h 
; 3. if ICW1 bit D1=1 do nothing 
; if ICW1 bit D1=0 write ICW3 to port 20h 
; 4. write ICW4 to port 21h 
; 5. OCW's can follow in any order 
; http://stanislavs.org/helppc/8259.html 
MapPIC: 
    cli 
    ; Setup ICW1 
    mov al, (ICW1_SEND_IC4 | ICW1_PIC_INITIALIZED) 
    out 0x20, al 
    out 0xa0, al 

    ; Setup ICW2 
    ; send ICW 2 to primary PIC 
    ; the first 31 interrupts (0x0-0x1F) are reserved 
    mov al, IRQ_0  ; Primary PIC handled IRQ 0..7. IRQ 0 is now mapped to interrupt number 0x20 
    out 0x21, al 

    ; send ICW 2 to secondary controller 
    mov al, IRQ_8  ; Secondary PIC handles IRQ's 8..15. IRQ 8 is now mapped to use interrupt 0x28 
    out 0xa1, al 

    ; Setup ICW3 
    mov al, 0x4     ; 0x4 = 0100 Second bit (IR Line 2) 
    out 0x21, al ; send to data register 

    ; Send ICW 3 to secondary PIC 
    mov al, 0x2  ; 0010=> IR line 2 
    out 0xa1, al ; write to data register of secondary PIC 

    ; Setup ICW4 
    mov al, 0x1  ; bit 0 enables 80x86 mode 

    ; send ICW 4 to both primary and secondary PICs 
    out 0x21, al 
    out 0xA1, al 

    ; All done. Null out the data registers 
    mov al, 0 
    out 0x21, al 
    out 0xa1, al 

    ; Disable all IRQs, except the timer and the keyboard 
    mov al, 0xfc 
    out 0x21, al 
    out 0xA1, al 

    ret 

(完整的源代码是在pic.inc)。

检查Bochs的日志,IRQ0没有来自PIC,以及键盘中断:

... 
00030543642d[PIC ] IRQ line 0 now low 
00030543646d[PIC ] IRQ line 0 now high 
00030763342d[PIC ] IRQ line 0 now low 
00030763346d[PIC ] IRQ line 0 now high 
00030916000i[KBD ] internal keyboard buffer full, ignoring scancode.(27) 
00030983046d[PIC ] IRQ line 0 now low 
00030983050d[PIC ] IRQ line 0 now high 
00031048000i[KBD ] internal keyboard buffer full, ignoring scancode.(26) 
00031202746d[PIC ] IRQ line 0 now low 
00031202750d[PIC ] IRQ line 0 now high 
... 

您可以查看完整的日志here。根据日志,PIC正确初始化:

00014765045d[PIC ] master: init command 1 found 
00014765045d[PIC ]   requires 4 = 1 
00014765045d[PIC ]   cascade mode: [0=cascade,1=single] 0 
00014765045d[PIC ] master: ICW1: edge triggered mode selected 
00014765046d[PIC ] IO write to 00a0 = 11 
00014765046d[PIC ] slave: init command 1 found 
00014765046d[PIC ]  requires 4 = 1 
00014765046d[PIC ]  cascade mode: [0=cascade,1=single] 0 
00014765046d[PIC ] slave: ICW1: edge triggered mode selected 
00014765048d[PIC ] IO write to 0021 = 20 
00014765048d[PIC ] master: init command 2 = 20 
00014765048d[PIC ]   offset = INT 20 
00014765050d[PIC ] IO write to 00a1 = 28 
00014765050d[PIC ] slave: init command 2 = 28 
00014765050d[PIC ]  offset = INT 28 
00014765052d[PIC ] IO write to 0021 = 04 
00014765052d[PIC ] master: init command 3 = 04 
00014765054d[PIC ] IO write to 00a1 = 02 
00014765054d[PIC ] slave: init command 3 = 02 
00014765056d[PIC ] IO write to 0021 = 01 
00014765056d[PIC ] master: init command 4 = 01 
00014765056d[PIC ] normal EOI interrupt 
00014765056d[PIC ]  80x86 mode 
00014765057d[PIC ] IO write to 00a1 = 01 
00014765057d[PIC ] slave: init command 4 = 01 
00014765057d[PIC ] normal EOI interrupt 
00014765057d[PIC ]  80x86 mode 
00014765059d[PIC ] IO write to 0021 = fb 
00014765059d[PIC ] setting master pic IMR to fb 
00014765060d[PIC ] IO write to 00a1 = fb 
00014765060d[PIC ] setting slave pic IMR to fb 
00014765064d[PIC ] IO write to 0021 = 00 
00014765064d[PIC ] setting master pic IMR to 00 

尽管如此,它仍然出错,我不知道我错过了什么。有谁能够帮助我?

+0

PIC初始化确实看起来正确...你确定要“清零”数据寄存器吗? – Downvoter

+0

你初始化了'ss:esp'吗? – Downvoter

+0

@cad我试图没有归零,但它是一样的。但是,输入ISR时我没有设置“ss”。我只设置了其他的段寄存器,你可以看到[这里](https://github.com/tuhdo/os-study/blob/master/include/idt.inc#L141)。它是重要的,我应该设置什么值'ss'?目前,当发生异常(即除以0)时,我的OS可以进入中断,选择正确的ISR,执行并返回到上一执行指令。有什么缺失? –

切换到用户空间让我的操作系统无法再接收来自PIC的中断。如果我没有输入用户空间(即内核空间中的无限循环),则操作系统可以很好地接收PIC中断。原来,sysenter disable IF bit in EFLAGS register。当我把sti放入系统调用入口(跳转到例程取决于系统调用号)时,中断再次正常工作。在我的代码中,该例程名为Sysenter_Entry,它比较eax中的系统调用号并相应地跳转(我将来需要转换为一个函数指针数组)。

Sysenter_Entry: 
    sti ; This solved the problem. VERY IMPORTANT. 
    mov  bx, 0x10  ; set data segments to data selector (0x10) 
    mov  ds, bx 
    ; sysenter jumps here, is is executing this code at prividege level 0. Simular to Call Gates, normally we will 
    ; provide a single entry point for all system calls. 
    cmp eax, 0 
    je clrscr 
    cmp eax, 1 
    je monitor_out 
    cmp eax, 2 
    je test_intr_kernel_space 
    cmp eax, 3 
    je test_intr_pic 
    cmp eax, 4 
    je STOP 
    ; mov eax, GoodbyeMsg 
    ; call Puts32 
syscall_exit: 
    ; restore back the stack for userspace afterward 
    mov bx, 0x23 
    mov ds, bx 
    sysexit 

此外,开关与iret用户空间第一次时,IF位也被禁止了,我需要设置IF位,并把它回EFLAGS与pushf在用户空间中断功能寄存器。中断位设置相应

后(通过sti或修改EFLAGS),我可以确认中断被看到ISR工作得到了被称为和检查Bochs的记录与这样的序列:

.... 
04433276227d[PIC ] IRQ line 0 now high 
04433276227d[PIC ] signalling IRQ(0) 
04433277486d[PIC ] IO write to 0020 = 20 
04433372617d[PIC ] IRQ line 0 now low 
.... 

也就是说, IRQ0为高电平,则CPU响应0x20来确认中断,IRQ0再次变为低电平。

+1

我很高兴你把它分类:)我讨厌8259.我做过的第一份工作涉及到这个设备,我不可能让它中断不止一次,不管是什么模式,有多少中断使能和EOI 0x20的我发送它.... –

+0

我可以感受到痛苦。太令人沮丧了。我很好奇:你的问题是什么,如何解决它? –

+1

恐怕我放弃和调查设备,而不是使用正确的驱动程序:( –