C 键盘IRQ只触发一次

C 键盘IRQ只触发一次,c,assembly,operating-system,pic,interrupt-handling,C,Assembly,Operating System,Pic,Interrupt Handling,我正在开发一个玩具unix克隆,我正在尝试正确连接我的中断。我遇到了一个问题,我的键盘IRQ(IRQ1)即使在我正确地确认后也只触发一次,以此类推。我还启用了PIT中断,以再次检查我的ACK是否进入PIC,这似乎工作正常。(多次开火) interrup.s的一个缺陷是我在堆栈上传递结构寄存器(按值),编译器在它从C中断处理程序返回后破坏了它。令人惊讶的是,唯一被丢弃的值是堆栈顶部的值(本例中是数据段寄存器),我已经通过在调用中断处理程序之前和之后打印堆栈来验证堆栈中的其余值是否正常。我已经添加了

我正在开发一个玩具unix克隆,我正在尝试正确连接我的中断。我遇到了一个问题,我的键盘IRQ(IRQ1)即使在我正确地确认后也只触发一次,以此类推。我还启用了PIT中断,以再次检查我的ACK是否进入PIC,这似乎工作正常。(多次开火)

interrup.s的一个缺陷是我在堆栈上传递结构寄存器(按值),编译器在它从C中断处理程序返回后破坏了它。令人惊讶的是,唯一被丢弃的值是堆栈顶部的值(本例中是数据段寄存器),我已经通过在调用中断处理程序之前和之后打印堆栈来验证堆栈中的其余值是否正常。我已经添加了一个临时解决方案来解决这个问题,但稍后我会清理这个问题

我还通过多次触发
int$3
验证了软件中断工作正常

任何建议都将不胜感激!代码如下:

中断.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
\ifndef\uu isr\h
#定义isr
#包括
结构寄存器{
uint32_t ds;
uint32_t edi、esi、ebp、esp、ebx、edx、ecx、eax;
uint32国际编码,错误代码;
uint32_t eip、cs、eflags、useresp、ss;
}_uuu属性_uuu((压缩));
类型定义结构寄存器寄存器;
类型定义无效(*isr_t)(寄存器);
无效寄存器中断处理程序(uint8、isr处理程序);
无效isr\u irq\u处理程序(注册表);
#恩迪夫
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;
}
#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);
}
#包括
#包括
#包括
#包括
#包括
isr_t中断处理程序[256];
无效寄存器中断处理程序(uint8、isr处理程序)
{
中断处理程序[n]=处理程序;
}
无效isr\u irq\u处理程序(注册表)
{
printf(“收到的ISR/IRQ:%d\n”,注册号);
if(中断处理程序[regs.int\u no]){
中断处理程序[regs.int\u no]();
}
如果(regs.int\u no>=32&®s.int\u no=8){
outb(PIC2_命令,PIC_EOI);
}
printf(“发送IRQ的EOI:%d,EOI:%x,到CMD:%x\n”,IRQ,PIC_EOI,PIC1_命令);
//始终向PIC1发送已处理中断的信号
//因为它也是转发PIC2 IRQ的PIC。
outb(PIC1_命令,PIC_EOI);
}
静态uint16图片获取irq注册(int ocw3)
{
/*OCW3到PIC CMD以获取寄存器值。PIC2被链接,并且
*表示IRQs 8-15。PIC1表示IRQs 0-7,2表示链*/
outb(PIC1_命令,ocw3);
outb(PIC2_命令,ocw3);

return(inb(PIC2_命令)我没有看到任何键盘ISR代码,这很可能是您的问题所在。我猜您正在使用PC/AT风格的接口与KB控制器(端口60、61、64,如果我没记错的话)交互。一个(或多个)有一个或多个输出在这些端口生成另一个中断之前,如果内存可用

一般来说,任何硬件设备在产生中断后都需要注意。PIC产生时间刻度中断是一个例外

在建议方面,我有几点建议:

  • 检查您认为错误破坏AX寄存器和/或寄存器结构的编译器代码(我不完全确定您所说的OP是否被破坏)。请准确确定您认为编译器是如何不正确的。我怀疑编译器不是不正确的,而是从ISR调用C函数很棘手。在过去,为了正确调用C函数,我必须经过多次迭代

  • 提出一个调试方案,您可以在其中查看最近的事件

    a、 一种方法是使用VGA屏幕内存中的固定位置(我假设此时您正在以文本模式工作),在这里您可以编写单个字符来指示程序正在执行的操作。特别是在顶行末尾的几个字符,例如4。这4个字符表示最近的4个事件。当一个事件发生时(例如,用户模式程序对内核的中断或调用)将最新的3个字符向左(或向右)移动,然后将最新的字符放入“最新”插槽。当程序正常工作时,屏幕上的这些位置会变得模糊,因为它们会因各种事件而快速更新。但是,如果程序挂起,字符将停止更改,并且您将看到最近的4个事件,即使您无法使用调试器

    b、 创建循环内存跟踪日志。日志中的每个条目都代表一个事件,但可以包括有关事件的详细信息,如寄存器值或程序状态。如果使用调试器闯入,则可以将日志视为字节、字、DWORD,并对头部的事件进行解码。或者如果调试器允许自定义扩展,则编写支持显示和查询日志中感兴趣事件的扩展

  • FWIW,我实现了一个V86监视器(旧的Intel术语,用于虚拟化实模式虚拟机监控程序),因此这些建议来自经验。这些建议适用于在裸机上测试内核的开发人员,并在某种程度上使用VM作为开发人员测试平台


    有了虚拟机和正确的虚拟机监控程序,您可以访问专门为调试虚拟机而设计的调试器。例如,Virtual Box有这样一个调试器。Windbg可以在Hyper-V上使用,尽管我上次尝试时速度非常慢。但是有人编写了一个扩展,使Hyper-V虚拟机的内核调试更快。

    您已经展示了ev除了键盘处理程序之外的任何东西。你必须确保它一直发送中断,你知道。键盘处理程序现在做的不多。我正在尝试正确设置扫描代码集,但有一个不同的问题,所以我已经完全删除了该位。IRQ不应该不管键盘处理程序上发生了什么都继续启动吗?@TejaswiYerukalapudi试着向我们展示所有的东西