21天 Intel CPU BIOS 学习专栏第七天(7)--中断处理

目录

21天 Intel CPU BIOS 学习专栏

第7天:中断处理

中断处理

1. 中断处理概述

2. 设置中断向量表

主要步骤

示例代码

3. 编写中断处理程序

示例代码

4. 编写中断处理模块

作业

明天的任务


21天 Intel CPU BIOS 学习专栏

第7天:中断处理

中断处理

今天我们将深入探讨中断处理在BIOS中的重要性,以及如何在BIOS初始化过程中正确地设置中断向量表、编写中断处理程序。中断处理是BIOS启动过程中的关键步骤之一,它确保硬件中断能够被正确处理,从而保证系统的稳定性和响应性。通过今天的课程,你将了解中断处理的基本概念、主要步骤以及如何编写和测试中断处理模块。

1. 中断处理概述

中断处理是指在计算机运行过程中,当某个事件发生时(如硬件中断、定时器中断等),CPU暂停当前任务,转而去处理中断事件的过程。在BIOS初始化过程中,我们需要设置中断向量表、编写中断处理程序,以确保中断能够被正确处理。

2. 设置中断向量表

中断向量表(Interrupt Vector Table, IVT)是一个数组,用于存储中断处理程序的地址。每个中断都有一个固定的中断向量号,对应于IVT中的一个条目。

主要步骤
  1. 初始化中断向量表:创建一个中断向量表,并将其加载到内存中。
  2. 设置中断向量表基址:设置中断描述符表寄存器(IDTR),指向中断向量表的基地址和大小。
示例代码
 

c

深色版本

VOID InitializeInterruptVectorTable() {
    // 初始化中断向量表
    IDT_ENTRY idt[IDT_SIZE];  // 假设IDT_SIZE为256

    // 清零中断向量表
    memset(idt, 0, sizeof(idt));

    // 设置中断处理程序地址
    SetIdtGate(idt, 0x20, (UINT32)TimerInterruptHandler, 0x08, 0x8E);  // 设置定时器中断处理程序
    SetIdtGate(idt, 0x21, (UINT32)KeyboardInterruptHandler, 0x08, 0x8E);  // 设置键盘中断处理程序

    // 设置中断描述符表寄存器IDTR
    IDT_POINTER idtr;
    idtr.Limit = sizeof(idt) - 1;
    idtr.Base = (UINT32)idt;
    LoadIdtr(idtr);

    // 开启中断
    EnableInterrupts();
}

VOID SetIdtGate(IDT_ENTRY *idt, UINT8 vector, UINT32 handler, UINT16 selector, UINT8 flags) {
    // 设置中断门描述符
    idt[vector].OffsetLow = handler & 0xFFFF;
    idt[vector].Selector = selector;
    idt[vector].Always0 = 0;
    idt[vector].Flags = flags;
    idt[vector].OffsetHigh = (handler >> 16) & 0xFFFF;
}

VOID LoadIdtr(IDT_POINTER idtr) {
    // 加载中断描述符表寄存器IDTR
    __asm__ __volatile__(
        "lidt %0"
        : : "m"(idtr)
    );
}

VOID EnableInterrupts() {
    // 开启中断
    __asm__ __volatile__("sti");
}
3. 编写中断处理程序

中断处理程序是处理特定中断事件的函数。每个中断处理程序都需要遵循一定的格式,并且通常会调用相应的硬件驱动程序来处理中断事件。

示例代码
 

c

深色版本

VOID TimerInterruptHandler() {
    // 处理定时器中断
    Print(L"Timer Interrupt Handler Called\n");

    // 发送EOI(End of Interrupt)信号给PIC(Programmable Interrupt Controller)
    SendEoiToPic(0x20);

    // 返回中断处理
    __asm__ __volatile__("iret");
}

VOID KeyboardInterruptHandler() {
    // 处理键盘中断
    Print(L"Keyboard Interrupt Handler Called\n");

    // 读取键盘扫描码
    UINT8 scan_code = Inb(0x60);

    // 发送EOI信号给PIC
    SendEoiToPic(0x21);

    // 返回中断处理
    __asm__ __volatile__("iret");
}

VOID SendEoiToPic(UINT8 irq) {
    // 发送EOI信号给PIC
    if (irq >= 8) {
        Outb(0xA0, 0x20);  // 发送EOI给从PIC
    }
    Outb(0x20, 0x20);  // 发送EOI给主PIC
}

UINT8 Inb(UINT16 port) {
    // 从端口读取字节
    UINT8 value;
    __asm__ __volatile__(
        "inb %1, %0"
        : "=a"(value)
        : "d"(port)
    );
    return value;
}

VOID Outb(UINT16 port, UINT8 value) {
    // 向端口写入字节
    __asm__ __volatile__(
        "outb %0, %1"
        : : "a"(value), "d"(port)
    );
}
4. 编写中断处理模块

现在,我们将把这些初始化步骤和中断处理程序整合到一个完整的BIOS模块中。

  1. 创建项目目录

    • 在 edk2 目录下创建一个新的目录,例如 InterruptHandlingModule
    • 在 InterruptHandlingModule 目录下创建 InterruptHandlingModule.inf 文件,内容如下:
       ini 

      深色版本

      [Defines]
      INF_VERSION                    = 0x00010005
      BASE_NAME                      = InterruptHandlingModule
      FILE_GUID                      = 12345678-1234-1234-1234-1234567890AB
      MODULE_TYPE                    = UEFI_DRIVER
      VERSION_STRING                 = 1.0
      UNIVERSAL                      = TRUE
      PROCESSOR                      = IA32 X64
      
      [Sources]
      InterruptHandlingModule.c
      
      [Packages]
      MdePkg/MdePkg.dec
      MdeModulePkg/MdeModulePkg.dec
      
      [LibraryClasses]
      UefiLib
      UefiDriverEntryPoint
      
      [Protocols]
      gEfiShellProtocolGuid
      
      [FeaturePcd]
      gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x8000000F
  2. 编写模块代码

    • 在 InterruptHandlingModule 目录下创建 InterruptHandlingModule.c 文件,内容如下:
       c 

      深色版本

      #include <Uefi.h>
      #include <Library/BaseLib.h>
      #include <Library/UefiLib.h>
      #include <Library/UefiBootServicesTableLib.h>
      
      #define IDT_SIZE 256
      
      typedef struct {
          UINT16 OffsetLow;
          UINT16 Selector;
          UINT8 Always0;
          UINT8 Flags;
          UINT16 OffsetHigh;
      } IDT_ENTRY;
      
      typedef struct {
          UINT16 Limit;
          UINT32 Base;
      } IDT_POINTER;
      
      VOID InitializeInterruptVectorTable() {
          // 初始化中断向量表
          IDT_ENTRY idt[IDT_SIZE];  // 假设IDT_SIZE为256
      
          // 清零中断向量表
          memset(idt, 0, sizeof(idt));
      
          // 设置中断处理程序地址
          SetIdtGate(idt, 0x20, (UINT32)TimerInterruptHandler, 0x08, 0x8E);  // 设置定时器中断处理程序
          SetIdtGate(idt, 0x21, (UINT32)KeyboardInterruptHandler, 0x08, 0x8E);  // 设置键盘中断处理程序
      
          // 设置中断描述符表寄存器IDTR
          IDT_POINTER idtr;
          idtr.Limit = sizeof(idt) - 1;
          idtr.Base = (UINT32)idt;
          LoadIdtr(idtr);
      
          // 开启中断
          EnableInterrupts();
      }
      
      VOID SetIdtGate(IDT_ENTRY *idt, UINT8 vector, UINT32 handler, UINT16 selector, UINT8 flags) {
          // 设置中断门描述符
          idt[vector].OffsetLow = handler & 0xFFFF;
          idt[vector].Selector = selector;
          idt[vector].Always0 = 0;
          idt[vector].Flags = flags;
          idt[vector].OffsetHigh = (handler >> 16) & 0xFFFF;
      }
      
      VOID LoadIdtr(IDT_POINTER idtr) {
          // 加载中断描述符表寄存器IDTR
          __asm__ __volatile__(
              "lidt %0"
              : : "m"(idtr)
          );
      }
      
      VOID EnableInterrupts() {
          // 开启中断
          __asm__ __volatile__("sti");
      }
      
      VOID TimerInterruptHandler() {
          // 处理定时器中断
          Print(L"Timer Interrupt Handler Called\n");
      
          // 发送EOI(End of Interrupt)信号给PIC(Programmable Interrupt Controller)
          SendEoiToPic(0x20);
      
          // 返回中断处理
          __asm__ __volatile__("iret");
      }
      
      VOID KeyboardInterruptHandler() {
          // 处理键盘中断
          Print(L"Keyboard Interrupt Handler Called\n");
      
          // 读取键盘扫描码
          UINT8 scan_code = Inb(0x60);
      
          // 发送EOI信号给PIC
          SendEoiToPic(0x21);
      
          // 返回中断处理
          __asm__ __volatile__("iret");
      }
      
      VOID SendEoiToPic(UINT8 irq) {
          // 发送EOI信号给PIC
          if (irq >= 8) {
              Outb(0xA0, 0x20);  // 发送EOI给从PIC
          }
          Outb(0x20, 0x20);  // 发送EOI给主PIC
      }
      
      UINT8 Inb(UINT16 port) {
          // 从端口读取字节
          UINT8 value;
          __asm__ __volatile__(
              "inb %1, %0"
              : "=a"(value)
              : "d"(port)
          );
          return value;
      }
      
      VOID Outb(UINT16 port, UINT8 value) {
          // 向端口写入字节
          __asm__ __volatile__(
              "outb %0, %1"
              : : "a"(value), "d"(port)
          );
      }
      
      VOID MyInitializationFunction() {
          InitializeInterruptVectorTable();
      
          Print(L"Interrupt Handling Initialization Complete\n");
      }
      
      EFI_STATUS
      EFIAPI
      UefiMain (
        IN EFI_HANDLE        ImageHandle,
        IN EFI_SYSTEM_TABLE  *SystemTable
        )
      {
          MyInitializationFunction();
          return EFI_SUCCESS;
      }
  3. 编译模块

    • 打开命令提示符或终端。
    • 导航到 edk2 目录。
    • 运行以下命令编译模块:
       sh 

      深色版本

      build -p InterruptHandlingModule/InterruptHandlingModule.inf
  4. 运行模块

    • 编译成功后,你可以在 Build 目录下找到生成的 .efi 文件。例如:
       

      深色版本

      Build\InterruptHandlingModule\DEBUG_VS2019x86\X64\InterruptHandlingModule.efi
    • 使用QEMU或其他虚拟机工具运行生成的模块,验证其功能。例如,使用QEMU:
       sh 

      深色版本

      qemu-system-x86_64 -bios path\to\OVMF.fd -m 2G -hda path\to\mydisk.img -drive file=path\to\Build\InterruptHandlingModule\DEBUG_VS2019x86\X64\InterruptHandlingModule.efi,format=raw

作业

  1. 阅读材料

    • 阅读Intel的官方文档,了解中断处理和中断向量表的详细信息。
    • 阅读EDK II的官方文档,了解模块开发的最佳实践。
  2. 实践任务

    • 编写一个更复杂的中断处理模块,例如支持多个硬件设备的中断处理。
    • 编译并测试该模块,记录下遇到的问题和解决方法。

明天的任务

明天我们将继续深入探讨设备初始化,了解如何在BIOS中初始化各种硬件设备,如显卡、网卡等。准备好迎接新的挑战吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值