为什么我的ISR不是来自执行的8259的IRQ0(8253定时器中断)?

时间:2015-10-20 18:21:58

标签: assembly x86 operating-system interrupt-handling

Here是我的操作系统的源代码。

我确保当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 1int 2int 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(End Of Interrupt)消息发送回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

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

1 个答案:

答案 0 :(得分:2)

切换到用户空间使我的操作系统无法再接收来自PIC的中断。如果我不进入用户空间(即内核空间中的无限循环),OS可以很好地接收PIC中断。事实证明sysenter disable IF bit in EFLAGS register。当我将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 放入系统调用条目(跳转到例程取决于系统调用号)时,中断再次正常工作。在我的代码中,例程名为Sysenter_Entry,它比较iret中的系统调用号并相应地跳转(我将来需要转换为函数指针数组)。

IF

此外,当第一次使用pushf切换到用户空间时,sti位也被禁用,我需要设置IF位并将其与EFLAGS一起放回EFLAGS寄存器中断在用户空间中起作用。

相应地设置中断位(通过.... 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 .... 或修改http://jsfiddle.net/bqx5u125/ )后,我可以通过查看ISR被调用来验证中断是否正常工作并使用以下序列检查Bochs日志:

.arrange-horizontally > *{
display: inline-block;}  .arrange-vertically > *{
display: block;}

也就是说,IRQ0为高电平,然后CPU响应0x20以确认中断,IRQ0再次为低电平。