【发布时间】:2016-04-25 10:11:11
【问题描述】:
我正在开发一个玩具 unix 克隆,我正在尝试正确连接我的中断。我遇到了一个问题,即我的键盘 IRQ(IRQ 1)即使在我正确确认它之后也只触发一次,依此类推。我也启用了 PIT 中断,以仔细检查我的 ACK 是否可以发送到 PIC,这似乎工作正常。 (多次触发)
interrup.s 的一个问题是我在堆栈上传递了 struct register_t(按值),编译器在它从 C 中断处理程序返回后将其丢弃。令人惊讶的是,唯一被丢弃的值是堆栈顶部的值(在本例中为数据段寄存器),我已经通过在堆栈之前和之后打印堆栈来验证堆栈中的其余值看起来没问题发生对中断处理程序的调用。我添加了一个临时解决方法来解决此问题,但我稍后会清理它。
我还通过多次触发int $3 验证了软件中断可以正常工作。
感谢任何建议!代码如下:
interrupt.s
.macro ISR_NOERRCODE int_no # A macro for ISRs that don't push an error code
.global isr\int_no
isr\int_no:
cli
push $0
push $\int_no
jmp isr_irq_common_stub
.endm
.macro ISR_ERRORCODE int_no # A macro for ISRs that do push an error code
.global isr\int_no
isr\int_no:
cli
push $\int_no
jmp isr_irq_common_stub
.endm
.macro IRQ irq_no, isr_map # A macro for IRQs from the PIC
.global irq\irq_no
irq\irq_no:
xchgw %bx, %bx
cli
push $0 # Error code
push $\isr_map # Interrupt number
jmp isr_irq_common_stub
.endm
ISR_NOERRCODE 0
ISR_NOERRCODE 1
ISR_NOERRCODE 2
ISR_NOERRCODE 3
ISR_NOERRCODE 4
ISR_NOERRCODE 5
ISR_NOERRCODE 6
ISR_NOERRCODE 7
ISR_ERRORCODE 8 # ISR 8 pushes error code onto stack
ISR_NOERRCODE 9
ISR_ERRORCODE 10 # ISR 10 - 14 push error codes onto stack
ISR_ERRORCODE 11
ISR_ERRORCODE 12
ISR_ERRORCODE 13
ISR_ERRORCODE 14
ISR_NOERRCODE 15
ISR_NOERRCODE 16
ISR_ERRORCODE 17
ISR_NOERRCODE 18
ISR_NOERRCODE 19
ISR_NOERRCODE 20
ISR_NOERRCODE 21
ISR_NOERRCODE 22
ISR_NOERRCODE 23
ISR_NOERRCODE 24
ISR_NOERRCODE 25
ISR_NOERRCODE 26
ISR_NOERRCODE 27
ISR_NOERRCODE 28
ISR_NOERRCODE 29
ISR_ERRORCODE 30
ISR_NOERRCODE 31
IRQ 0, 32
IRQ 1, 33
IRQ 2, 34
IRQ 3, 35
IRQ 4, 36
IRQ 5, 37
IRQ 6, 38
IRQ 7, 39
IRQ 8, 40
IRQ 9, 41
IRQ 10, 42
IRQ 11, 43
IRQ 12, 44
IRQ 13, 45
IRQ 14, 46
IRQ 15, 47
# This is in isr.c
.extern isr_irq_handler
# This is our common isr stub. It saves the processor state, sets up for kernel
# mode segments, calls the C-level fault handler, and finally restores the stack
# frame
isr_irq_common_stub:
pusha # Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax
mov %ds, %ax # Lower 16-bits of eax = ds.
push %eax # save the data segment descriptor
mov $0x10, %ax # load the kernel data segment descriptor
mov %ax, %ds # Right now, we dont really have to do this
mov %ax, %es # but after we enter the user mode, the segment
mov %ax, %fs # registers will be different (0x18? and 0x20?)
mov %ax, %gs
call isr_irq_handler
# This does not work because the structure value we passed earlier
# is being messed up by the compiler. It does not preserve the previous eax
# we pushed on to the stack.
pop %eax
mov $0x10, %ax # reload the original data segment descriptor
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
popa # Pops edi,esi,ebp...
add $8, %esp # Cleans up the pushed error code and pushed ISR number
sti
iret # pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
isr.h
#ifndef __isr_h
#define __isr_h
#include <stdint.h>
struct Registers {
uint32_t ds;
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;
uint32_t int_no, err_code;
uint32_t eip, cs, eflags, useresp, ss;
} __attribute__((packed));
typedef struct Registers register_t;
typedef void (*isr_t)(registers_t);
void register_interrupt_handler(uint8_t n, isr_t handler);
void isr_irq_handler(register_t regs);
#endif
isr.c
#include <kernel/isr.h>
#include <kernel/pic.h>
#include <stdio.h>
#include <log.h>
#include <kernel/tty.h>
isr_t interrupt_handlers[256];
void register_interrupt_handler(uint8_t n, isr_t handler)
{
interrupt_handlers[n] = handler;
}
void isr_irq_handler(register_t regs)
{
printf("Received ISR/IRQ: %d\n", regs.int_no);
if (interrupt_handlers[regs.int_no]) {
interrupt_handlers[regs.int_no]();
}
if (regs.int_no >= 32 && regs.int_no <= 47) {
pic_send_eoi(regs.int_no - 32);
}
return;
}
pic.c
#include <kernel/pic.h>
#include <asm.h>
#define PIC1 0x20 /* IO base address for master PIC */
#define PIC2 0xA0 /* IO base address for slave PIC */
#define PIC1_COMMAND PIC1
#define PIC1_DATA (PIC1+1)
#define PIC2_COMMAND PIC2
#define PIC2_DATA (PIC2+1)
#define PIC_EOI 0x20 /* End-of-interrupt command code */
#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) */
#define PIC_READ_IRR 0x0a /* OCW3 irq ready next CMD read */
#define PIC_READ_ISR 0x0b /* OCW3 irq service next CMD read */
#define PIC1_OFFSET 0x20
#define PIC2_OFFSET 0x28
static void pic_mask(int pic_num, uint16_t mask);
static uint16_t pic_get_irq_reg(int ocw3);
static uint16_t pic_get_irr(void);
static uint16_t pic_get_isr(void);
void setup_remap_pics()
{
uint8_t a1, a2;
// Save existing masks
a1 = inb(PIC1_DATA);
a2 = inb(PIC2_DATA);
outb(PIC1_COMMAND, ICW1_INIT);
io_wait();
outb(PIC2_COMMAND, ICW1_INIT);
io_wait();
outb(PIC1_DATA, PIC1_OFFSET); // We're remapping the PICs interrupt codes from (0x07-0x7F) to (offset, offset + 8)
io_wait();
outb(PIC2_DATA, PIC2_OFFSET);
io_wait();
outb(PIC1_DATA, 4); // Tell the master PIC that there is a slave PIC at IRQ2 (00000100)
io_wait();
outb(PIC2_DATA, 2); // Tell the slave pic it's cascade identity (00000010)
io_wait();
outb(PIC1_DATA, ICW4_8086);
io_wait();
outb(PIC2_DATA, ICW4_8086);
io_wait();
// Restore saved masks
outb(PIC1_DATA, a1);
outb(PIC2_DATA, a2);
enable_interrupts();
// Mask everything except the keyboard, timer
pic_mask(1, 0xFD);
pic_mask(2, 0xFF);
}
static void pic_mask(int pic_num, uint16_t mask) {
uint16_t port = (pic_num == 1) ? PIC1_DATA : PIC2_DATA;
outb(port, mask);
}
// MARK :- Helpers
void pic_send_eoi(uint8_t irq)
{
if (irq >= 8) {
outb(PIC2_COMMAND, PIC_EOI);
}
printf("Sending EOI for IRQ: %d, EOI: %x, to CMD: %x\n", irq, PIC_EOI, PIC1_COMMAND);
// Always signal PIC1 that an interrupt has been handled
// because it's the PIC that forwards PIC2's irqs as well.
outb(PIC1_COMMAND, PIC_EOI);
}
static uint16_t pic_get_irq_reg(int ocw3)
{
/* OCW3 to PIC CMD to get the register values. PIC2 is chained, and
* represents IRQs 8-15. PIC1 is IRQs 0-7, with 2 being the chain */
outb(PIC1_COMMAND, ocw3);
outb(PIC2_COMMAND, ocw3);
return (inb(PIC2_COMMAND) << 8) | inb(PIC1_COMMAND);
}
/* Returns the combined value of the cascaded PICs irq request register */
static uint16_t pic_get_irr(void)
{
return pic_get_irq_reg(PIC_READ_IRR);
}
/* Returns the combined value of the cascaded PICs in-service register */
static uint16_t pic_get_isr(void)
{
return pic_get_irq_reg(PIC_READ_ISR);
}
【问题讨论】:
-
您已经展示了除键盘处理程序之外的所有内容。你必须确保它不断发送中断,你知道的。
-
键盘处理程序现在做的不多。我正在尝试正确设置扫描代码集并在那里遇到不同的问题,所以我已经完全删除了那个位。不管键盘处理程序发生什么,IRQ 不应该一直触发吗?
-
@TejaswiYerukalapudi 尝试向我们展示一切。处理程序是发生的事情的关键部分。
-
不,为什么会这样?是键盘控制器发送 IRQ。
-
/刘海在墙上。你是对的,如果我不读取输出缓冲区,我在文档中错过了关于键盘不再发送中断的那一点。现在一切正常。谢谢!
标签: c assembly operating-system pic interrupt-handling