Skip to content

Latest commit

 

History

History
1378 lines (1082 loc) · 43.9 KB

File metadata and controls

1378 lines (1082 loc) · 43.9 KB

多架构中断处理系统

SimpleKernel 实现了跨多个架构的完整中断处理系统,每个架构都有其特定的中断控制器和处理机制。

🏗️ 架构对比总览

特性 RISC-V 64 AArch64
中断控制器 PLIC/CLINT GIC
中断描述符 CSR + trap handler 异常向量表
定时器 SBI Timer Generic Timer
外部中断 PLIC中断路由 GIC分发
多核启动 SBI Hart管理 PSCI
特权级别 M/S/U Mode EL0-3

RISC-V 64 中断处理系统

🏗️ 系统架构

核心组件

┌─────────────────────────────────────────────────────────────┐
│                   RISC-V 中断处理系统                        │
├─────────────────────────────────────────────────────────────┤
│  InterruptBase (抽象基类)                                   │
│  ├── Do(cause, context)                                    │
│  └── RegisterInterruptFunc(cause, func)                    │
├─────────────────────────────────────────────────────────────┤
│  Interrupt (RISC-V实现)                                     │
│  ├── trap 处理程序入口                                      │
│  ├── 异常/中断分发机制                                       │
│  ├── 中断处理函数数组管理                                    │
│  └── PLIC 中断控制器集成                                    │
├─────────────────────────────────────────────────────────────┤
│  PLIC (Platform-Level Interrupt Controller)                │
│  ├── 外部中断源管理                                         │
│  ├── 优先级仲裁机制                                         │
│  ├── 中断路由配置                                           │
│  └── 中断完成确认                                           │
├─────────────────────────────────────────────────────────────┤
│  SBI 定时器集成                                             │
│  ├── 机器模式定时器中断                                      │
│  ├── 监管模式定时器代理                                      │
│  └── 时间片调度支持                                         │
├─────────────────────────────────────────────────────────────┤
│  设备树(DT)中断配置                                         │
│  ├── PLIC 基地址解析                                       │
│  ├── 中断号映射                                             │
│  └── 设备中断配置                                           │
└─────────────────────────────────────────────────────────────┘

📋 CSR 寄存器管理

关键控制状态寄存器

// 中断使能配置
void SetupInterrupts() {
    // 使能监管模式外部中断、定时器中断和软件中断
    cpu_io::SIE::SEIE::Set();  // 外部中断使能
    cpu_io::SIE::STIE::Set();  // 定时器中断使能
    cpu_io::SIE::SSIE::Set();  // 软件中断使能

    // 设置 trap 处理程序入口
    cpu_io::STVEC::Write(reinterpret_cast<uint64_t>(TrapEntry));

    // 全局中断使能
    cpu_io::SSTATUS::SIE::Set();
}

Trap 处理程序入口

extern "C" void TrapEntry();  // 汇编实现的trap入口

// trap 分发函数
extern "C" uint64_t TrapHandler(uint64_t cause, uint64_t epc,
                               uint64_t context) {
    // 检查是否为中断(最高位为1)
    if (cause & (1ULL << 63)) {
        uint64_t interrupt_cause = cause & ~(1ULL << 63);
        return InterruptSingleton::instance().Do(
            interrupt_cause,
            reinterpret_cast<uint8_t*>(context)
        );
    } else {
        // 处理异常(最高位为0)
        return HandleException(cause, epc, context);
    }
}

🎯 中断处理流程

1. 硬件中断流程

外部设备 → PLIC → Hart 中断线 → 读取 SCAUSE → 分发处理

2. 定时器中断流程

SBI定时器 → M模式中断 → 代理到S模式 → SCAUSE[5] → 处理函数

3. 中断分发机制

class Interrupt : public InterruptBase {
    static constexpr size_t kInterruptMaxCount = 64;
    static std::array<InterruptFunc, kInterruptMaxCount> interrupt_handlers;

public:
    uint64_t Do(uint64_t cause, uint8_t *context) override {
        if (cause < kInterruptMaxCount && interrupt_handlers[cause]) {
            return interrupt_handlers[cause](cause, context);
        }

        klog::Warn("Unhandled interrupt: cause=%lu\n", cause);
        return 0;
    }
};

🎛️ PLIC 中断控制器

PLIC 初始化配置

void SetupPlic() {
    auto plic_base = GetPlicBaseFromDT();  // 从设备树获取基地址

    // 1. 设置所有中断源优先级(非零值启用)
    for (uint32_t irq = 1; irq <= kMaxInterruptSources; irq++) {
        WritePlicReg(plic_base, PLIC_PRIORITY_BASE + irq * 4, 1);
    }

    // 2. 为当前hart启用所有中断源
    uint32_t hart_id = cpu_io::GetCurrentCoreId();
    uint64_t enable_reg = plic_base + PLIC_ENABLE_BASE +
                         hart_id * PLIC_ENABLE_STRIDE;

    // 启用中断源 1-63 (每个bit代表一个中断源)
    WritePlicReg(enable_reg, 0x0, 0xFFFFFFFE);  // bits 1-31
    WritePlicReg(enable_reg, 0x4, 0xFFFFFFFF);  // bits 32-63

    // 3. 设置中断优先级阈值(0表示接受所有优先级)
    uint64_t threshold_reg = plic_base + PLIC_THRESHOLD_BASE +
                            hart_id * PLIC_THRESHOLD_STRIDE;
    WritePlicReg(threshold_reg, 0x0, 0);
}

PLIC 中断处理

uint64_t PlicInterruptHandler(uint64_t cause, uint8_t *context) {
    uint32_t hart_id = cpu_io::GetCurrentCoreId();
    uint64_t plic_base = GetPlicBaseFromDT();

    // 1. 声明(claim)中断 - 获取最高优先级的待处理中断
    uint64_t claim_reg = plic_base + PLIC_CLAIM_BASE +
                        hart_id * PLIC_CLAIM_STRIDE;
    uint32_t irq = ReadPlicReg(claim_reg, 0x0);

    if (irq == 0) {
        klog::Warn("Spurious PLIC interrupt\n");
        return 0;
    }

    klog::Debug("PLIC interrupt: IRQ %u\n", irq);

    // 2. 调用特定设备的中断处理程序
    auto device_handler = GetDeviceHandler(irq);
    if (device_handler) {
        device_handler(irq, context);
    }

    // 3. 完成(complete)中断 - 通知PLIC中断处理完成
    WritePlicReg(claim_reg, 0x0, irq);

    return 0;
}

⏰ SBI 定时器中断

定时器配置

constexpr uint64_t kTimerIntervalCycles = 10000000;  // 10M cycles ≈ 100Hz

void SetupTimer() {
    // 设置下一次定时器中断时间
    uint64_t current_time = cpu_io::Time::Read();
    sbi::SetTimer(current_time + kTimerIntervalCycles);

    // 注册定时器中断处理函数 (监管模式定时器中断 = 5)
    InterruptSingleton::instance().RegisterInterruptFunc(
        5, TimerInterruptHandler
    );
}

定时器中断处理

uint64_t TimerInterruptHandler(uint64_t cause, uint8_t *context) {
    static uint64_t tick_count = 0;
    tick_count++;

    // 设置下一次定时器中断
    uint64_t current_time = cpu_io::Time::Read();
    sbi::SetTimer(current_time + kTimerIntervalCycles);

    // 降低日志输出频率
    if (tick_count % 100 == 0) {
        klog::Info("Timer interrupt %lu, cause %lu\n", tick_count, cause);
    }

    return 0;
}

🔌 UART 中断处理

UART 中断配置

void SetupUartInterrupt() {
    // 从设备树获取UART中断号
    uint32_t uart_irq = GetUartIrqFromDT();

    // 在PLIC中启用UART中断
    EnablePlicInterrupt(uart_irq);

    // 注册UART中断处理函数
    InterruptSingleton::instance().RegisterInterruptFunc(
        uart_irq, UartInterruptHandler
    );

    // 配置UART硬件启用中断
    ConfigureUartInterrupts();
}

UART 中断处理

uint64_t UartInterruptHandler(uint64_t cause, uint8_t *context) {
    // 读取UART中断状态
    auto uart_base = GetUartBaseFromDT();
    uint32_t interrupt_status = ReadUartReg(uart_base, UART_IIR_OFFSET);

    // 处理接收中断
    if (interrupt_status & UART_IIR_RDI) {
        while (ReadUartReg(uart_base, UART_LSR_OFFSET) & UART_LSR_DR) {
            char received_char = ReadUartReg(uart_base, UART_RBR_OFFSET);
            klog::Info("UART received: '%c' (0x%02X)\n",
                      received_char, received_char);

            // 回显字符
            WriteUartReg(uart_base, UART_THR_OFFSET, received_char);
        }
    }

    return 0;
}

🚀 系统初始化

主hart初始化

void InterruptInit(int, const char **) {
    // 1. 解析设备树获取中断配置
    auto kernel_fdt = KernelFdtSingleton::instance();

    // 2. 设置trap向量
    cpu_io::STVEC::Write(reinterpret_cast<uint64_t>(TrapEntry));

    // 3. 初始化PLIC
    SetupPlic();

    // 4. 注册定时器中断处理函数
    InterruptSingleton::instance().RegisterInterruptFunc(
        5, TimerInterruptHandler  // 监管模式定时器中断
    );

    // 5. 注册外部中断处理函数
    InterruptSingleton::instance().RegisterInterruptFunc(
        9, PlicInterruptHandler   // 监管模式外部中断
    );

    // 6. 启用中断
    cpu_io::SIE::SEIE::Set();   // 外部中断
    cpu_io::SIE::STIE::Set();   // 定时器中断
    cpu_io::SSTATUS::SIE::Set(); // 全局中断使能

    // 7. 启动定时器
    SetupTimer();

    // 8. 启动其他hart
    StartSecondaryHarts();

    klog::Info("RISC-V interrupt system initialized\n");
}

从hart初始化

void InterruptInitSMP(int, const char **) {
    // 1. 设置trap向量
    cpu_io::STVEC::Write(reinterpret_cast<uint64_t>(TrapEntry));

    // 2. 启用中断
    cpu_io::SIE::SEIE::Set();
    cpu_io::SIE::STIE::Set();
    cpu_io::SSTATUS::SIE::Set();

    // 3. 启动定时器
    SetupTimer();

    klog::Info("RISC-V SMP hart %lu initialized\n",
               cpu_io::GetCurrentCoreId());
}

📊 性能特点

优势

  • PLIC优先级仲裁:硬件级中断优先级管理
  • SBI定时器:高精度定时器服务
  • 设备树配置:动态硬件配置发现
  • 多hart支持:原生多核中断处理

设计考量

  • CSR直接访问:避免特权级切换开销
  • PLIC声明/完成机制:确保中断不丢失
  • OpenSBI集成:利用固件服务
  • trap统一入口:简化异常和中断处理

AArch64 中断处理系统

🏗️ 系统架构

核心组件

┌─────────────────────────────────────────────────────────────┐
│                   AArch64 中断处理系统                       │
├─────────────────────────────────────────────────────────────┤
│  InterruptBase (抽象基类)                                   │
│  ├── Do(cause, context)                                    │
│  └── RegisterInterruptFunc(cause, func)                    │
├─────────────────────────────────────────────────────────────┤
│  Interrupt (AArch64实现)                                    │
│  ├── 异常向量表(Vector Table)                              │
│  ├── 异常级别管理 (EL0-EL3)                                │
│  ├── 中断处理函数数组管理                                    │
│  └── GIC 中断控制器集成                                    │
├─────────────────────────────────────────────────────────────┤
│  GIC (Generic Interrupt Controller)                        │
│  ├── SGI (Software Generated Interrupts)                  │
│  ├── PPI (Private Peripheral Interrupts)                  │
│  ├── SPI (Shared Peripheral Interrupts)                   │
│  └── 中断优先级和路由管理                                    │
├─────────────────────────────────────────────────────────────┤
│  Generic Timer                                             │
│  ├── 虚拟定时器 (EL1 Virtual Timer)                        │
│  ├── 物理定时器 (EL1 Physical Timer)                       │
│  ├── 高分辨率时间计数                                        │
│  └── 定时器中断生成                                         │
├─────────────────────────────────────────────────────────────┤
│  设备树(DT)中断配置                                         │
│  ├── GIC 基地址解析                                        │
│  ├── 中断号映射关系                                         │
│  ├── 设备中断配置                                           │
│  └── PSCI 多核启动管理                                      │
└─────────────────────────────────────────────────────────────┘

📋 异常向量表管理

异常向量表结构

// 异常向量表入口定义
extern "C" void vector_table();  // 汇编实现的向量表

// 向量表包含16个入口,每个入口128字节
struct ExceptionVectors {
    // Current EL with SP0
    void curr_el_sp0_sync();     // 同步异常
    void curr_el_sp0_irq();      // IRQ中断
    void curr_el_sp0_fiq();      // FIQ中断
    void curr_el_sp0_serror();   // SError异常

    // Current EL with SPx
    void curr_el_spx_sync();
    void curr_el_spx_irq();
    void curr_el_spx_fiq();
    void curr_el_spx_serror();

    // Lower EL using AArch64
    void lower_el_aarch64_sync();
    void lower_el_aarch64_irq();
    void lower_el_aarch64_fiq();
    void lower_el_aarch64_serror();

    // Lower EL using AArch32
    void lower_el_aarch32_sync();
    void lower_el_aarch32_irq();
    void lower_el_aarch32_fiq();
    void lower_el_aarch32_serror();
};

异常处理器实现

// C++异常处理函数
extern "C" void sync_exception_handler(uint64_t esr, uint64_t elr,
                                       uint64_t context) {
    uint32_t ec = (esr >> 26) & 0x3F;  // Exception Class
    uint32_t iss = esr & 0xFFFFFF;     // Instruction Specific Syndrome

    klog::Error("Sync exception: EC=0x%X, ISS=0x%X, ELR=0x%lX\n",
                ec, iss, elr);

    // 根据异常类型进行处理
    switch (ec) {
        case 0x15:  // SVC instruction execution in AArch64 state
            HandleSvc(iss, context);
            break;
        case 0x21:  // Instruction abort from lower EL
            HandleInstructionAbort(iss, elr);
            break;
        case 0x25:  // Data abort from lower EL
            HandleDataAbort(iss, elr);
            break;
        default:
            klog::Error("Unhandled sync exception\n");
            break;
    }
}

extern "C" void irq_exception_handler(uint64_t context) {
    // 读取中断确认寄存器获取中断ID
    uint32_t intid = GicSingleton::instance().GetInterruptId();

    if (intid == 1023) {  // Spurious interrupt
        klog::Warn("Spurious interrupt\n");
        return;
    }

    // 调用注册的中断处理函数
    InterruptSingleton::instance().Do(intid,
        reinterpret_cast<uint8_t*>(context));

    // 发送End of Interrupt信号
    GicSingleton::instance().EndOfInterrupt(intid);
}

🎯 中断处理流程

1. 硬件中断流程

外部设备 → GIC分发器 → CPU接口 → 异常向量表 → IRQ处理器

2. 中断ID获取和处理

class Interrupt : public InterruptBase {
    static constexpr size_t kInterruptMaxCount = 1024;  // GIC支持1024个中断
    static std::array<InterruptFunc, kInterruptMaxCount> interrupt_handlers;

public:
    uint64_t Do(uint64_t cause, uint8_t *context) override {
        if (cause < kInterruptMaxCount && interrupt_handlers[cause]) {
            return interrupt_handlers[cause](cause, context);
        }

        klog::Warn("Unhandled interrupt: INTID=%lu\n", cause);
        return 0;
    }

    // 设置中断为SPI类型
    void SPI(uint32_t intid, uint32_t cpu_id) {
        GicSingleton::instance().SetInterruptType(intid,
            Gic::InterruptType::SPI);
        GicSingleton::instance().SetTargetCpu(intid, cpu_id);
        GicSingleton::instance().EnableInterrupt(intid);
    }

    // 设置中断为PPI类型
    void PPI(uint32_t intid, uint32_t cpu_id) {
        GicSingleton::instance().SetInterruptType(intid,
            Gic::InterruptType::PPI);
        GicSingleton::instance().EnableInterrupt(intid);
    }
};

🎛️ GIC 中断控制器

GIC 初始化配置

class Gic {
public:
    static constexpr uint32_t kSGIBase = 0;      // SGI: 0-15
    static constexpr uint32_t kPPIBase = 16;     // PPI: 16-31
    static constexpr uint32_t kSPIBase = 32;     // SPI: 32-1019

    void SetUP() {
        auto gic_base = GetGicBaseFromDT();  // 从设备树获取基地址

        // 1. 初始化分发器(Distributor)
        InitDistributor(gic_base);

        // 2. 初始化CPU接口
        InitCpuInterface(gic_base);

        // 3. 启用分发器
        EnableDistributor();
    }

private:
    void InitDistributor(uint64_t dist_base) {
        // 禁用分发器进行配置
        WriteReg(dist_base + GICD_CTLR, 0);

        // 配置所有SPI为边沿触发,高优先级
        for (uint32_t i = kSPIBase; i < 1020; i += 32) {
            WriteReg(dist_base + GICD_ICFGR + (i/16)*4, 0x55555555);
        }

        // 设置所有中断优先级
        for (uint32_t i = 0; i < 1020; i += 4) {
            WriteReg(dist_base + GICD_IPRIORITYR + i, 0xA0A0A0A0);
        }

        // 设置SPI目标CPU为CPU0
        for (uint32_t i = kSPIBase; i < 1020; i += 4) {
            WriteReg(dist_base + GICD_ITARGETSR + i, 0x01010101);
        }
    }

    void InitCpuInterface(uint64_t cpu_base) {
        // 设置优先级掩码(允许所有优先级)
        WriteReg(cpu_base + GICC_PMR, 0xFF);

        // 启用CPU接口
        WriteReg(cpu_base + GICC_CTLR, GICC_CTLR_ENABLE);
    }
};

GIC 中断处理

uint32_t Gic::GetInterruptId() {
    uint64_t cpu_base = GetGicCpuBaseFromDT();

    // 读取中断确认寄存器
    uint32_t iar = ReadReg(cpu_base + GICC_IAR);
    return iar & 0x3FF;  // INTID在低10位
}

void Gic::EndOfInterrupt(uint32_t intid) {
    uint64_t cpu_base = GetGicCpuBaseFromDT();

    // 写入中断结束寄存器
    WriteReg(cpu_base + GICC_EOIR, intid);
}

void Gic::EnableInterrupt(uint32_t intid) {
    uint64_t dist_base = GetGicDistBaseFromDT();

    uint32_t reg_offset = (intid / 32) * 4;
    uint32_t bit_offset = intid % 32;

    // 设置使能位
    WriteReg(dist_base + GICD_ISENABLER + reg_offset,
             1U << bit_offset);
}

⏰ Generic Timer 中断

定时器配置

void SetupTimer() {
    // 从设备树获取定时器中断ID
    auto timer_intid = KernelFdtSingleton::instance()
        .GetAarch64Intid("arm,armv8-timer") + Gic::kPPIBase;

    // 配置为PPI类型中断
    InterruptSingleton::instance().PPI(timer_intid,
        cpu_io::GetCurrentCoreId());

    // 注册定时器中断处理函数
    InterruptSingleton::instance().RegisterInterruptFunc(
        timer_intid, timer_handler);

    // 配置虚拟定时器
    cpu_io::CNTV_CTL_EL0::ENABLE::Clear();  // 先禁用
    cpu_io::CNTV_CTL_EL0::IMASK::Set();     // 屏蔽中断

    // 设置定时器间隔 (2秒)
    uint64_t interval_clk = 2 * cpu_io::CNTFRQ_EL0::Read();
    cpu_io::CNTV_TVAL_EL0::Write(interval_clk);

    // 启用定时器和中断
    cpu_io::CNTV_CTL_EL0::ENABLE::Set();    // 启用定时器
    cpu_io::CNTV_CTL_EL0::IMASK::Clear();   // 取消中断屏蔽
}

定时器中断处理

uint64_t timer_handler(uint64_t cause, uint8_t *context) {
    static uint64_t tick_count = 0;
    tick_count++;

    // 重新设置下一次定时器中断
    uint64_t interval_clk = 2 * cpu_io::CNTFRQ_EL0::Read();
    cpu_io::CNTV_TVAL_EL0::Write(interval_clk);

    // 降低日志输出频率
    if (tick_count % 10 == 0) {
        klog::Info("Timer interrupt %lu, INTID %lu\n", tick_count, cause);
    }

    return 0;
}

🔌 UART 中断处理

UART 中断配置

void SetupUartInterrupt() {
    // 从设备树获取UART中断ID
    auto uart_intid = KernelFdtSingleton::instance()
        .GetAarch64Intid("arm,pl011") + Gic::kSPIBase;

    // 配置为SPI类型中断,路由到当前CPU
    InterruptSingleton::instance().SPI(uart_intid,
        cpu_io::GetCurrentCoreId());

    // 注册UART中断处理函数
    InterruptSingleton::instance().RegisterInterruptFunc(
        uart_intid, uart_handler);
}

UART 中断处理

uint64_t uart_handler(uint64_t cause, uint8_t *context) {
    auto uart_base = GetUartBaseFromDT();

    // 读取中断状态
    uint32_t mis = ReadReg(uart_base + PL011_MIS);

    // 处理接收中断
    if (mis & PL011_MIS_RXMIS) {
        while (!(ReadReg(uart_base + PL011_FR) & PL011_FR_RXFE)) {
            char received_char = ReadReg(uart_base + PL011_DR) & 0xFF;
            klog::Info("UART received: '%c' (0x%02X)\n",
                      received_char, received_char);

            // 回显字符
            WriteReg(uart_base + PL011_DR, received_char);
        }

        // 清除接收中断
        WriteReg(uart_base + PL011_ICR, PL011_ICR_RXIC);
    }

    // 处理发送中断
    if (mis & PL011_MIS_TXMIS) {
        // 发送缓冲区空,可以发送更多数据
        WriteReg(uart_base + PL011_ICR, PL011_ICR_TXIC);
    }

    return 0;
}

🚀 系统初始化

主处理器初始化 (Primary CPU)

void InterruptInit(int, const char **) {
    // 1. 设置异常向量表基地址
    cpu_io::VBAR_EL1::Write(reinterpret_cast<uint64_t>(vector_table));

    // 2. 初始化GIC
    InterruptSingleton::instance().SetUP();

    // 3. 配置定时器中断
    auto timer_intid = KernelFdtSingleton::instance()
        .GetAarch64Intid("arm,armv8-timer") + Gic::kPPIBase;
    InterruptSingleton::instance().PPI(timer_intid,
        cpu_io::GetCurrentCoreId());
    InterruptSingleton::instance().RegisterInterruptFunc(
        timer_intid, timer_handler);

    // 4. 配置UART中断
    auto uart_intid = KernelFdtSingleton::instance()
        .GetAarch64Intid("arm,pl011") + Gic::kSPIBase;
    InterruptSingleton::instance().SPI(uart_intid,
        cpu_io::GetCurrentCoreId());
    InterruptSingleton::instance().RegisterInterruptFunc(
        uart_intid, uart_handler);

    // 5. 启用中断
    cpu_io::EnableInterrupt();

    // 6. 启动定时器
    SetupTimer();

    // 7. 启动其他CPU核心
    StartSecondaryCpus();

    klog::Info("AArch64 interrupt system initialized\n");
}

从处理器初始化 (Secondary CPUs)

void InterruptInitSMP(int, const char **) {
    // 1. 设置异常向量表
    cpu_io::VBAR_EL1::Write(reinterpret_cast<uint64_t>(vector_table));

    // 2. 初始化GIC CPU接口
    InterruptSingleton::instance().SetUP();

    // 3. 配置定时器中断
    auto timer_intid = KernelFdtSingleton::instance()
        .GetAarch64Intid("arm,armv8-timer") + Gic::kPPIBase;
    InterruptSingleton::instance().PPI(timer_intid,
        cpu_io::GetCurrentCoreId());
    InterruptSingleton::instance().RegisterInterruptFunc(
        timer_intid, timer_handler);

    // 4. 启用中断
    cpu_io::EnableInterrupt();

    // 5. 启动定时器
    SetupTimer();

    klog::Info("AArch64 SMP CPU %u initialized\n",
               cpu_io::GetCurrentCoreId());
}

多核启动流程

void StartSecondaryCpus() {
    size_t core_count = BasicInfoSingleton::instance().core_count;

    for (size_t i = 1; i < core_count; i++) {  // 跳过CPU 0
        // 使用PSCI启动CPU
        auto ret = cpu_io::psci::CpuOn(i,
            reinterpret_cast<uint64_t>(_boot), 0);

        if ((ret != cpu_io::psci::SUCCESS) &&
            (ret != cpu_io::psci::ALREADY_ON)) {
            klog::Warn("CPU %d start failed: %d\n", i, ret);
        } else {
            klog::Info("CPU %d started successfully\n", i);
        }
    }
}

📊 性能特点

优势

  • 异常级别隔离:EL0-EL3提供完整的特权级保护
  • GIC硬件优先级:高效的中断仲裁和路由
  • Generic Timer:高精度系统定时器
  • PSCI标准:标准化的多核电源管理

设计考量

  • 向量表对齐:2KB对齐要求确保性能
  • 中断类型分类:SGI/PPI/SPI满足不同场景需求
  • 设备树集成:动态硬件配置发现
  • 编译器限制:手动异常处理避免属性限制

🔍 多架构对比分析

中断控制器对比

特性 RISC-V PLIC AArch64 GIC
中断数量 可配置(通常1024) 1024个INTID
优先级支持 可配置级别 256级
多核支持 每hart独立 分发器+CPU接口
中断类型 外部中断 SGI/PPI/SPI
EOI机制 PLIC Complete GIC EOIR寄存器

定时器机制对比

架构 定时器类型 精度 编程接口
RISC-V Machine Timer 固定频率 SBI调用
AArch64 Generic Timer 系统频率 系统寄存器

初始化复杂度

  1. RISC-V: 设备树解析 + CSR配置
  2. AArch64: 异常向量表 + GIC多阶段初始化

性能特征

  • RISC-V: 简洁的CSR接口,SBI服务集成
  • AArch64: 硬件优先级仲裁,多级异常处理

这种多架构设计确保了SimpleKernel在不同硬件平台上的高效运行,同时保持了统一的编程接口。

🏗️ 系统架构

核心组件

┌─────────────────────────────────────────────────────────────┐
│                  RISC-V 64 中断处理系统                      │
├─────────────────────────────────────────────────────────────┤
│  InterruptBase (抽象基类)                                   │
│  ├── Do(cause, context)                                    │
│  └── RegisterInterruptFunc(cause, func)                    │
├─────────────────────────────────────────────────────────────┤
│  Interrupt (RISC-V实现)                                     │
│  ├── 中断/异常分离处理                                       │
│  ├── CSR scause 解码                                       │
│  ├── 中断处理函数数组                                        │
│  └── 异常处理函数数组                                        │
├─────────────────────────────────────────────────────────────┤
│  PLIC (Platform-Level Interrupt Controller)                │
│  ├── 外部中断路由                                           │
│  ├── 中断优先级管理                                         │
│  ├── 多 Hart 中断分发                                       │
│  └── 中断完成确认                                           │
├─────────────────────────────────────────────────────────────┤
│  OpenSBI 接口                                              │
│  ├── SBI 定时器服务                                         │
│  ├── Hart 启动管理                                          │
│  ├── 系统调用接口                                           │
│  └── 调试控制台                                             │
├─────────────────────────────────────────────────────────────┤
│  设备特定中断                                               │
│  ├── 定时器中断 (SBI Timer)                                 │
│  ├── 串口中断 (NS16550A)                                    │
│  ├── 外部中断 (PLIC)                                        │
│  └── 断点异常 (ebreak)                                      │
└─────────────────────────────────────────────────────────────┘

📋 CSR 寄存器管理

关键 CSR 寄存器

// 中断/异常处理相关的CSR寄存器
- stvec:   Supervisor Trap Vector Base Address (陷阱向量基址)
- scause:  Supervisor Cause Register (陷阱原因)
- sstatus: Supervisor Status Register (监管者状态)
- sie:     Supervisor Interrupt Enable (监管者中断使能)
- sip:     Supervisor Interrupt Pending (监管者中断挂起)
- sepc:    Supervisor Exception Program Counter (异常PC)
- stval:   Supervisor Trap Value (陷阱值)

scause 寄存器解码

void Interrupt::Do(uint64_t cause, uint8_t *context) {
    // 解析 scause 寄存器
    auto interrupt = cpu_io::Scause::Interrupt::Get(cause);     // 最高位
    auto exception_code = cpu_io::Scause::ExceptionCode::Get(cause); // 低位

    if (interrupt) {
        // 处理中断 (异步事件)
        if (exception_code < kInterruptMaxCount) {
            interrupt_handlers_[exception_code](exception_code, context);
        }
    } else {
        // 处理异常 (同步事件)
        if (exception_code < kExceptionMaxCount) {
            exception_handlers_[exception_code](exception_code, context);
        }
    }
}

陷阱入口设置

// 设置陷阱向量 - 直接模式
__attribute__((interrupt("supervisor")))
alignas(4) void TarpEntry() {
    InterruptSingleton::instance().Do(
        static_cast<uint64_t>(cpu_io::Scause::Read()), nullptr
    );
}

// 初始化时设置
cpu_io::Stvec::SetDirect(reinterpret_cast<uint64_t>(TarpEntry));

🎯 中断/异常分类处理

中断类型 (scause[63] = 1)

enum SupervisorInterrupts {
    kSupervisorSoftwareInterrupt = 1,    // S模式软件中断
    kSupervisorTimerInterrupt = 5,       // S模式定时器中断
    kSupervisorExternalInterrupt = 9,    // S模式外部中断
};

异常类型 (scause[63] = 0)

enum SupervisorExceptions {
    kInstructionAddressMisaligned = 0,   // 指令地址不对齐
    kInstructionAccessFault = 1,         // 指令访问错误
    kIllegalInstruction = 2,             // 非法指令
    kBreakpoint = 3,                     // 断点异常
    kLoadAddressMisaligned = 4,          // 加载地址不对齐
    kLoadAccessFault = 5,                // 加载访问错误
    kStoreAddressMisaligned = 6,         // 存储地址不对齐
    kStoreAccessFault = 7,               // 存储访问错误
    kEnvironmentCall = 8,                // 环境调用
    kInstructionPageFault = 12,          // 指令页错误
    kLoadPageFault = 13,                 // 加载页错误
    kStorePageFault = 15,                // 存储页错误
};

⏰ SBI 定时器中断

定时器配置和处理

namespace {
    uint64_t kInterval = 0;  // 定时器间隔
}

void InterruptInit(int, const char **) {
    // 从设备树获取时钟频率
    kInterval = KernelFdtSingleton::instance().GetTimebaseFrequency();
    klog::Info("kInterval: 0x%X\n", kInterval);

    // 注册定时器中断处理函数
    InterruptSingleton::instance().RegisterInterruptFunc(
        kSupervisorTimerInterrupt,
        [](uint64_t exception_code, uint8_t *) -> uint64_t {
            // 设置下一次定时器中断
            sbi_set_timer(cpu_io::Time::Read() + kInterval);
            klog::Info("Handle %s\n", kInterruptNames[exception_code]);
            return 0;
        }
    );

    // 启用定时器中断
    cpu_io::Sie::Stie::Set();

    // 设置首次定时器中断
    sbi_set_timer(kInterval);
}

OpenSBI 定时器接口

// 设置定时器中断时间 (绝对时间)
sbi_set_timer(cpu_io::Time::Read() + kInterval);

// 读取当前时间
uint64_t current_time = cpu_io::Time::Read();

🔌 PLIC 外部中断控制

PLIC 初始化

void InterruptInit(int, const char **) {
    // 从设备树获取PLIC信息
    auto [plic_addr, plic_size, ndev, context_count] =
        KernelFdtSingleton::instance().GetPlic();

    // 初始化PLIC实例
    PlicSingleton::create(plic_addr, ndev, context_count);

    // 注册外部中断处理函数
    InterruptSingleton::instance().RegisterInterruptFunc(
        kSupervisorExternalInterrupt,
        [](uint64_t exception_code, uint8_t *) -> uint64_t {
            // 查询中断源
            auto source_id = PlicSingleton::instance().Which();
            klog::Debug("External interrupt from source %d\n", source_id);

            // 执行中断处理
            PlicSingleton::instance().Do(source_id, nullptr);

            // 完成中断处理
            PlicSingleton::instance().Done(source_id);
            return 0;
        }
    );

    // 启用外部中断
    cpu_io::Sie::Seie::Set();
}

PLIC 寄存器布局

class Plic {
private:
    static constexpr uint64_t kSourcePriorityOffset = 0x000000;  // 优先级
    static constexpr uint64_t kPendingBitsOffset = 0x001000;     // 挂起位
    static constexpr uint64_t kEnableBitsOffset = 0x002000;      // 使能位
    static constexpr uint64_t kContextOffset = 0x200000;        // 上下文

    static constexpr uint64_t kContextSize = 0x1000;            // 上下文大小
    static constexpr uint64_t kPriorityThresholdOffset = 0x0;   // 优先级阈值
    static constexpr uint64_t kClaimCompleteOffset = 0x4;       // 声明/完成
};

PLIC 中断处理流程

// 1. 中断触发 → PLIC 路由到指定 Hart
// 2. Hart 接收外部中断 → 查询中断源
auto source_id = PlicSingleton::instance().Which();

// 3. 执行设备特定的中断处理
PlicSingleton::instance().Do(source_id, nullptr);

// 4. 通知 PLIC 中断处理完成
PlicSingleton::instance().Done(source_id);

📡 串口中断处理

NS16550A 串口中断

void InterruptInit(int, const char **) {
    // 从设备树获取串口信息
    auto [base, size, irq] = KernelFdtSingleton::instance().GetSerial();

    // 初始化串口驱动
    Ns16550aSingleton::create(base);

    // 注册串口中断处理函数
    PlicSingleton::instance().RegisterInterruptFunc(
        irq,  // 串口IRQ号
        [](uint64_t, uint8_t *) -> uint64_t {
            // 读取串口字符并回显
            sk_putchar(Ns16550aSingleton::instance().TryGetChar(), nullptr);
            return 0;
        }
    );

    // 为当前核心启用串口中断
    PlicSingleton::instance().Set(
        cpu_io::GetCurrentCoreId(),  // Hart ID
        irq,                         // 中断源ID
        1,                          // 优先级
        true                        // 启用
    );
}

🛑 异常处理

断点异常 (ebreak)

// 注册断点异常处理
InterruptSingleton::instance().RegisterInterruptFunc(
    kBreakpoint,
    [](uint64_t exception_code, uint8_t *) -> uint64_t {
        // ebreak 指令是2字节,需要跳过
        cpu_io::Sepc::Write(cpu_io::Sepc::Read() + 2);
        klog::Info("Handle %s\n", kExceptionNames[exception_code]);
        return 0;
    }
);

默认异常处理

Interrupt::Interrupt() {
    // 默认异常处理 - 系统停机
    for (auto &i : exception_handlers_) {
        i = [](uint64_t cause, uint8_t *context) -> uint64_t {
            klog::Err("Default Exception handler [%s] 0x%X, 0x%p\n",
                     kExceptionNames[cause], cause, context);
            while (1) ; // 停机等待
            return 0;
        };
    }
}

🚀 多核系统初始化

主核心 (BSP) 初始化

void InterruptInit(int, const char **) {
    // 1. 获取系统配置信息
    kInterval = KernelFdtSingleton::instance().GetTimebaseFrequency();

    // 2. 注册所有中断/异常处理函数
    RegisterTimerInterrupt();
    RegisterExternalInterrupt();
    RegisterSerialInterrupt();
    RegisterBreakpointException();

    // 3. 初始化硬件控制器
    InitializePlic();
    InitializeSerial();

    // 4. 设置陷阱向量和启用中断
    cpu_io::Stvec::SetDirect(reinterpret_cast<uint64_t>(TarpEntry));
    cpu_io::Sstatus::Sie::Set();  // 启用监管者中断
    cpu_io::Sie::Ssie::Set();     // 启用软件中断
    cpu_io::Sie::Stie::Set();     // 启用定时器中断
    cpu_io::Sie::Seie::Set();     // 启用外部中断

    // 5. 启动定时器
    sbi_set_timer(kInterval);

    // 6. 启动其他核心
    for (size_t i = 0; i < core_count; i++) {
        auto ret = sbi_hart_start(i, reinterpret_cast<uint64_t>(_boot), 0);
        if (ret.error != SBI_SUCCESS && ret.error != SBI_ERR_ALREADY_AVAILABLE) {
            klog::Warn("hart %d start failed: %d\n", i, ret.error);
        }
    }
}

应用核心 (AP) 初始化

void InterruptInitSMP(int, const char **) {
    // 1. 设置陷阱向量 (与BSP相同)
    cpu_io::Stvec::SetDirect(reinterpret_cast<uint64_t>(TarpEntry));

    // 2. 启用中断 (与BSP相同)
    cpu_io::Sstatus::Sie::Set();
    cpu_io::Sie::Ssie::Set();
    cpu_io::Sie::Stie::Set();
    cpu_io::Sie::Seie::Set();

    // 3. 启动定时器 (使用全局间隔)
    sbi_set_timer(kInterval);

    klog::Info("Hello InterruptInitSMP\n");
}

🔧 OpenSBI 集成

SBI 调用接口

// Hart 管理
sbi_hart_start(hart_id, start_addr, priv);  // 启动指定Hart
sbi_hart_stop();                            // 停止当前Hart
sbi_hart_get_status(hart_id);              // 获取Hart状态

// 定时器服务
sbi_set_timer(stime_value);                // 设置定时器
uint64_t time = cpu_io::Time::Read();      // 读取当前时间

// 调试服务
sbi_debug_console_write_byte(ch);          // 调试控制台输出

// 系统重置
sbi_system_reset(reset_type, reset_reason); // 系统重置

SBI 错误处理

auto ret = sbi_hart_start(i, start_addr, 0);
switch (ret.error) {
    case SBI_SUCCESS:
        klog::Info("Hart %zu started successfully\n", i);
        break;
    case SBI_ERR_ALREADY_AVAILABLE:
        klog::Debug("Hart %zu already running\n", i);
        break;
    default:
        klog::Warn("Hart %zu start failed: %ld\n", i, ret.error);
        break;
}

📊 设备树 (Device Tree) 集成

从设备树获取硬件信息

class KernelFdt {
public:
    // 获取时钟频率
    uint64_t GetTimebaseFrequency();

    // 获取PLIC信息 <地址, 大小, 设备数, 上下文数>
    std::tuple<uint64_t, uint64_t, size_t, size_t> GetPlic();

    // 获取串口信息 <地址, 大小, IRQ号>
    std::tuple<uint64_t, uint64_t, uint32_t> GetSerial();
};

// 使用示例
auto [plic_addr, plic_size, ndev, context_count] =
    KernelFdtSingleton::instance().GetPlic();

auto [serial_base, serial_size, serial_irq] =
    KernelFdtSingleton::instance().GetSerial();

⚡ 性能优化特性

1. 对齐优化

// 4字节对齐的中断处理函数数组
alignas(4) std::array<InterruptFunc, kInterruptMaxCount> interrupt_handlers_;
alignas(4) std::array<InterruptFunc, kExceptionMaxCount> exception_handlers_;

// 4字节对齐的陷阱入口
__attribute__((interrupt("supervisor"))) alignas(4) void TarpEntry();

2. 分离的中断/异常处理

// 分离的处理数组避免无效的范围检查
static std::array<InterruptFunc, kInterruptMaxCount> interrupt_handlers_;
static std::array<InterruptFunc, kExceptionMaxCount> exception_handlers_;

3. 硬件特定优化

// 利用RISC-V CSR指令的原子性
auto cause = cpu_io::Scause::Read();  // 原子读取
cpu_io::Sepc::Write(pc + 2);         // 原子写入

🔄 多核处理特性

BSP vs AP 初始化差异

组件 BSP (主核心) AP (应用核心)
设备树解析 ❌ (共享)
中断处理函数注册 ❌ (共享)
PLIC初始化 ❌ (共享)
串口配置 ❌ (共享)
CSR配置
定时器启动
其他核心启动

多核中断安全性

  • 每核独立CSR: 每个Hart有自己的CSR副本
  • 共享PLIC: 所有Hart共享PLIC,通过上下文隔离
  • 共享处理函数: 中断处理函数在所有核心间共享
  • 原子操作: CSR访问具有硬件原子性保证

🐛 调试和监控

中断信息输出

// 中断注册调试信息
klog::Info("RegisterInterruptFunc [%s] 0x%X, 0x%p\n",
          kInterruptNames[exception_code], cause, func);

// PLIC外部中断调试
klog::Debug("External interrupt from source %d\n", source_id);

// CSR状态监控
klog::Debug("scause: 0x%lX, sepc: 0x%lX\n",
           cpu_io::Scause::Read(), cpu_io::Sepc::Read());

异常调试

// 异常处理时的详细信息输出
klog::Err("Exception [%s] at PC 0x%lX, tval: 0x%lX\n",
         kExceptionNames[cause],
         cpu_io::Sepc::Read(),
         cpu_io::Stval::Read());

📚 相关文档

🔗 关键数据结构

RISC-V 中断/异常编号

// 监管者模式中断 (scause[63]=1)
#define S_SOFTWARE_INTERRUPT     1    // 软件中断
#define S_TIMER_INTERRUPT        5    // 定时器中断
#define S_EXTERNAL_INTERRUPT     9    // 外部中断

// 监管者模式异常 (scause[63]=0)
#define INSTRUCTION_MISALIGNED   0    // 指令不对齐
#define INSTRUCTION_ACCESS_FAULT 1    // 指令访问错误
#define ILLEGAL_INSTRUCTION      2    // 非法指令
#define BREAKPOINT              3    // 断点
#define LOAD_MISALIGNED         4    // 加载不对齐
#define LOAD_ACCESS_FAULT       5    // 加载访问错误
#define STORE_MISALIGNED        6    // 存储不对齐
#define STORE_ACCESS_FAULT      7    // 存储访问错误
#define ECALL_FROM_U_MODE       8    // 用户模式环境调用
#define ECALL_FROM_S_MODE       9    // 监管者模式环境调用
#define INSTRUCTION_PAGE_FAULT  12   // 指令页错误
#define LOAD_PAGE_FAULT         13   // 加载页错误
#define STORE_PAGE_FAULT        15   // 存储页错误

PLIC 上下文映射

// PLIC 上下文计算 (通常为 2 * hart_count)
Context 0: Hart 0 M-mode
Context 1: Hart 0 S-mode
Context 2: Hart 1 M-mode
Context 3: Hart 1 S-mode
...