为什么我的 8259 的 IRQ0(8253 定时器中断)的 ISR 没有被执行?
Why isn't my ISR for IRQ0 (8253 Timer Interrupt) from the 8259 executed?
Here是我的OS.
的源代码
我确保当 CPU 从 8259 PIC(可编程中断控制器)获得中断时,它总是 "offsets" 正确进入 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;中断描述符 Table)中的正确 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
尽管如此,它仍然出错,我不知道我错过了什么。有人可以帮助我吗?
切换到用户space 使我的 OS 无法再接收来自 PIC 的中断。如果我不输入 userspace(即内核中的无限循环 space),OS 可以很好地接收 PIC 中断。原来是sysenter
disable IF
bit in EFLAGS
register. When I put sti
in the system call entry (that jumps to a routine depends on syscall number), interrupt works fine again. In my code, the routine is named Sysenter_Entry比较了eax
中的syscall number,并据此跳转(我以后需要转成函数指针数组)
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
切换到用户space时,IF
位也被禁用,我需要设置IF位并将其放回EFLAGS向 pushf
注册以在用户 space.
中运行中断
相应地设置中断位后(通过 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 再次为低电平。
Here是我的OS.
的源代码我确保当 CPU 从 8259 PIC(可编程中断控制器)获得中断时,它总是 "offsets" 正确进入 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;中断描述符 Table)中的正确 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
尽管如此,它仍然出错,我不知道我错过了什么。有人可以帮助我吗?
切换到用户space 使我的 OS 无法再接收来自 PIC 的中断。如果我不输入 userspace(即内核中的无限循环 space),OS 可以很好地接收 PIC 中断。原来是sysenter
disable IF
bit in EFLAGS
register. When I put sti
in the system call entry (that jumps to a routine depends on syscall number), interrupt works fine again. In my code, the routine is named Sysenter_Entry比较了eax
中的syscall number,并据此跳转(我以后需要转成函数指针数组)
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
切换到用户space时,IF
位也被禁用,我需要设置IF位并将其放回EFLAGS向 pushf
注册以在用户 space.
相应地设置中断位后(通过 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 再次为低电平。