首页 > 其他分享 >HNU-操作系统实验Lab5-2022级

HNU-操作系统实验Lab5-2022级

时间:2024-10-27 18:46:11浏览次数:3  
标签:Lab5 中断 GIC 2022 寄存器 HNU define OS GICD

实验目的

深刻理解中断的原理和机制,掌握CPU访问中断控制器的方法,掌握Arm体系结构的中断机制和规范,实现时钟中断服务和部分异常处理等。

实验过程

前言

中断是一种硬件机制。借助于中断,CPU可以不必再采用轮询这种低效的方式访问外部设备。将所有的外部设备与CPU直接相连是不现实的,外部设备的中断请求一般经由中断控制器,由中断控制器仲裁后再转发给CPU。如下图所示Arm的中断系统。image-20240514232009793

其中nIRQ是普通中断,nFIQ是快速中断。 Arm采用的中断控制器叫做GIC,即general interrupt controller。gic包括多个版本,如GICv1(已弃用),GICv2,GICv3,GICv4。简单起见,我们实验将选用GICv2版本。

  • GICv2 最多支持8个核的中断管理。
  • GIC包括两大主要部分,分别是:
    • Distributor,其通过GICD_开头的寄存器进行控制
    • CPU Interface,其通过GICC_开头的寄存器进行控制
  • 中断类型分为以下几类:
    • SPI:(shared peripheral interrupt),共享外设中断。该中断来源于外设,通过Distributor分发给特定的core,其中断编号为32-1019。
    • PPI:(private peripheral interrupt),私有外设中断。该中断来源于外设,但只对指定的core有效,中断信号只会发送给指定的core,其中断编号为16-31。
    • SGI:(software-generated interrupt),软中断。软件产生的中断,用于给其他的core发送中断信号,其中断编号为0-15。
    • virtual interrupt,虚拟中断,用于支持虚拟机

因为soc中,中断有很多,为了方便对中断的管理,对每个中断,附加了中断优先级。在中断仲裁时,高优先级的中断,会优于低优先级的中断,发送给cpu处理。当cpu在响应低优先级中断时,如果此时来了高优先级中断,那么高优先级中断会抢占低优先级中断,而被处理器响应。

GICv2初始化

virt.dts中intc和timer:

intc@8000000 {
        phandle = <0x8001>;
        reg = <0x00 0x8000000 0x00 0x10000 0x00 0x8010000 0x00 0x10000>;
        compatible = "arm,cortex-a15-gic";
        ranges;
        #size-cells = <0x02>;
        #address-cells = <0x02>;
        interrupt-controller;
        #interrupt-cells = <0x03>;

        v2m@8020000 {
                phandle = <0x8002>;
                reg = <0x00 0x8020000 0x00 0x1000>;
                msi-controller;
                compatible = "arm,gic-v2m-frame";
        };
};

timer {
        interrupts = <0x01 0x0d 0x104 0x01 0x0e 0x104 0x01 0x0b 0x104 0x01 0x0a 0x104>;
        always-on;
        compatible = "arm,armv8-timer\0arm,armv7-timer";
};
  • intc中的 reg 指明GICD寄存器映射到内存的位置为0x8000000,长度为0x10000, GICC寄存器映射到内存的位置为0x8010000,长度为0x10000
  • intc中的 #interrupt-cells 指明 interrupts 包括3个cells。第一个cell为中断类型,0表示SPI,1表示PPI;第二个cell为中断号,SPI范围为[0-987],PPI为[0-15];第三个cell为flags,其中[3:0]位表示触发类型,4表示高电平触发,[15:8]为PPI的cpu中断掩码,每1位对应一个cpu,为1表示该中断会连接到对应的cpu。
  • 以timer设备为例,其中包括4个中断。以第二个中断的参数 0x01 0x0e 0x104 为例,其指明该中断为PPI类型的中断,中断号14, 路由到第一个cpu,且高电平触发。但注意到PPI的起始中断号为16,所以实际上该中断在GICv2中的中断号应为16 + 14 = 30。

hwi_init.c 文件,初始化 GIC:

#include "prt_typedef.h"
#include "os_attr_armv8_external.h"

#define OS_GIC_VER                 2

#define GIC_DIST_BASE              0x08000000
#define GIC_CPU_BASE               0x08010000

#define GICD_CTLR                  (GIC_DIST_BASE + 0x0000U)
#define GICD_TYPER                 (GIC_DIST_BASE + 0x0004U)
#define GICD_IIDR                  (GIC_DIST_BASE + 0x0008U)
#define GICD_IGROUPRn              (GIC_DIST_BASE + 0x0080U)
#define GICD_ISENABLERn            (GIC_DIST_BASE + 0x0100U)
#define GICD_ICENABLERn            (GIC_DIST_BASE + 0x0180U)
#define GICD_ISPENDRn              (GIC_DIST_BASE + 0x0200U)
#define GICD_ICPENDRn              (GIC_DIST_BASE + 0x0280U)
#define GICD_ISACTIVERn            (GIC_DIST_BASE + 0x0300U)
#define GICD_ICACTIVERn            (GIC_DIST_BASE + 0x0380U)
#define GICD_IPRIORITYn            (GIC_DIST_BASE + 0x0400U)
#define GICD_ICFGR                 (GIC_DIST_BASE + 0x0C00U)


#define GICD_CTLR_ENABLE                1  /* Enable GICD */
#define GICD_CTLR_DISABLE               0     /* Disable GICD */
#define GICD_ISENABLER_SIZE             32
#define GICD_ICENABLER_SIZE             32
#define GICD_ICPENDR_SIZE               32
#define GICD_IPRIORITY_SIZE             4
#define GICD_IPRIORITY_BITS             8
#define GICD_ICFGR_SIZE                 16
#define GICD_ICFGR_BITS                 2

#define GICC_CTLR                  (GIC_CPU_BASE  + 0x0000U)
#define GICC_PMR                   (GIC_CPU_BASE  + 0x0004U)
#define GICC_BPR                   (GIC_CPU_BASE  + 0x0008U)
#define IAR_MASK        0x3FFU
#define GICC_IAR            (GIC_CPU_BASE + 0xc)
#define GICC_EOIR           (GIC_CPU_BASE + 0x10)
#define     GICD_SGIR               (GIC_DIST_BASE + 0xf00)

#define BIT(n)                     (1 << (n))

#define GICC_CTLR_ENABLEGRP0       BIT(0)
#define GICC_CTLR_ENABLEGRP1       BIT(1)
#define GICC_CTLR_FIQBYPDISGRP0    BIT(5)
#define GICC_CTLR_IRQBYPDISGRP0    BIT(6)
#define GICC_CTLR_FIQBYPDISGRP1    BIT(7)
#define GICC_CTLR_IRQBYPDISGRP1    BIT(8)

#define GICC_CTLR_ENABLE_MASK      (GICC_CTLR_ENABLEGRP0 | \
                                    GICC_CTLR_ENABLEGRP1)

#define GICC_CTLR_BYPASS_MASK      (GICC_CTLR_FIQBYPDISGRP0 | \
                                    GICC_CTLR_IRQBYPDISGRP0 | \
                                    GICC_CTLR_FIQBYPDISGRP1 | \
                                    GICC_CTLR_IRQBYPDISGRP1)

#define GICC_CTLR_ENABLE            1
#define GICC_CTLR_DISABLE           0
// Priority Mask Register. interrupt priority filter, Higher priority corresponds to a lower Priority field value.
#define GICC_PMR_PRIO_LOW           0xff
// The register defines the point at which the priority value fields split into two parts,
// the group priority field and the subpriority field. The group priority field is used to
// determine interrupt preemption. NO GROUP.
#define GICC_BPR_NO_GROUP           0x00

#define GIC_REG_READ(addr)         (*(volatile U32 *)((uintptr_t)(addr)))
#define GIC_REG_WRITE(addr, data)  (*(volatile U32 *)((uintptr_t)(addr)) = (U32)(data))


void OsGicInitCpuInterface(void)
{
    // 初始化Gicv2的distributor和cpu interface
    // 禁用distributor和cpu interface后进行相应配置
    GIC_REG_WRITE(GICD_CTLR, GICD_CTLR_DISABLE);
    GIC_REG_WRITE(GICC_CTLR, GICC_CTLR_DISABLE);
    GIC_REG_WRITE(GICC_PMR, GICC_PMR_PRIO_LOW);
    GIC_REG_WRITE(GICC_BPR, GICC_BPR_NO_GROUP);


    // 启用distributor和cpu interface
    GIC_REG_WRITE(GICD_CTLR, GICD_CTLR_ENABLE);
    GIC_REG_WRITE(GICC_CTLR, GICC_CTLR_ENABLE);

}

// src/arch/drv/gic/prt_gic_init.c
/*
* 描述: 去使能(禁用)指定中断
*/
OS_SEC_L4_TEXT void OsGicDisableInt(U32 intId)
{
    // Interrupt Clear-Enable Registers
}

/*
* 描述: 使能指定中断
*/
OS_SEC_L4_TEXT void OsGicEnableInt(U32 intId)
{
    // Interrupt Set-Enable Registers
}

OS_SEC_L4_TEXT void OsGicClearInt(uint32_t interrupt)
{
    GIC_REG_WRITE(GICD_ICPENDRn + (interrupt / GICD_ICPENDR_SIZE)*sizeof(U32), 1 << (interrupt % GICD_ICPENDR_SIZE));
}

// 设置中断号为interrupt的中断的优先级为priority
OS_SEC_L4_TEXT void OsGicIntSetPriority(uint32_t interrupt, uint32_t priority) {
    uint32_t shift = (interrupt % GICD_IPRIORITY_SIZE) * GICD_IPRIORITY_BITS;
    volatile uint32_t* addr = ((volatile U32 *)(uintptr_t)(GICD_IPRIORITYn + (interrupt / GICD_IPRIORITY_SIZE) * sizeof(U32))) ;
    uint32_t value = GIC_REG_READ(addr);
    value &= ~(0xff << shift); // 每个中断占8位,所以掩码为 0xFF
    value |= priority << shift;
    GIC_REG_WRITE(addr, value);
}

// 设置中断号为interrupt的中断的属性为config
OS_SEC_L4_TEXT void OsGicIntSetConfig(uint32_t interrupt, uint32_t config) {
    uint32_t shift = (interrupt % GICD_ICFGR_SIZE) * GICD_ICFGR_BITS;
    volatile uint32_t* addr = ((volatile U32 *)(uintptr_t)(GICD_ICFGR + (interrupt / GICD_ICFGR_SIZE)*sizeof(U32)));
    uint32_t value = GIC_REG_READ(addr);
    value &= ~(0x03 << shift);
    value |= config << shift;
    GIC_REG_WRITE(addr, value);
}

/*
* 描述: 中断确认
*/
OS_SEC_L4_TEXT U32 OsGicIntAcknowledge(void)
{
    // reads this register to obtain the interrupt ID of the signaled interrupt.
    // This read acts as an acknowledge for the interrupt.
    U32 value = GIC_REG_READ(GICC_IAR);
    return value;
}

/*
* 描述: 标记中断完成,清除相应中断位
*/
OS_SEC_L4_TEXT void OsGicIntClear(U32 value)
{
    // A processor writes to this register to inform the CPU interface either:
    // • that it has completed the processing of the specified interrupt
    // • in a GICv2 implementation, when the appropriate GICC_CTLR.EOImode bit is set to 1, to indicate that the interface should perform priority drop for the specified interrupt.
    GIC_REG_WRITE(GICC_EOIR, value);
}

U32 OsHwiInit(void)
{

    OsGicInitCpuInterface();

    return OS_OK;
}

OsGicInitCpuInterface() 函数负责初始化 GIC 分发器和 CPU 接口。

  • 首先,禁用了 GIC 分发器和 CPU 接口,以确保在配置完成之前不会触发中断。
  • 然后,将 CPU 接口的优先级屏蔽寄存器(GICC_PMR)和中断优先级控制器(GICC_BPR)分别设置为较低优先级和无分组,这样所有中断都能触发。
  • 最后,启用了 GIC 分发器和 CPU 接口,使得中断能够被触发和处理。

OsGicClearInt() 函数用于清除指定中断。

  • 它通过计算出中断挂起寄存器(GICD_ICPENDRn)中相应的偏移量,然后写入一个特定的位来清除中断。

OsGicIntSetPriority() 函数用于设置特定中断的优先级。

  • 首先,根据中断号计算出在优先级寄存器中的偏移量,并计算出对应的地址。
  • 然后,读取当前寄存器的值,使用掩码操作清除原始优先级值的相应部分,并设置新的优先级值。
  • 最后,将修改后的值写回寄存器中。

OsGicIntSetConfig() 函数用于设置特定中断的属性。

  • 首先,根据中断号计算出在配置寄存器中的偏移量,并计算出对应的地址。
  • 然后,读取当前寄存器的值,使用掩码操作清除原始配置值的相应部分,并设置新的配置值。
  • 最后,将修改后的值写回寄存器中。

OsGicIntAcknowledge() 函数用于确认中断并获取其 ID。

  • 它从 GIC CPU 接口的中断响应寄存器(GICC_IAR)中读取中断 ID,并返回该值。

OsGicIntClear() 函数用于标记中断为完成状态并清除相应的中断位。

  • 它将中断 ID 写入 GIC CPU 接口的中断结束寄存器(GICC_EOIR),通知 CPU 接口中断已处理完毕,并清除相应的中断位。

OsHwiInit() 函数是硬件中断初始化函数。

  • 它调用了 OsGicInitCpuInterface() 函数来初始化 GIC 分发器和 CPU 接口。
  • 最后,返回一个成功的状态码。

关于以上定义的寄存器,可查看用户手册:

image-20241027184641028

image-20241027184705511

使能时钟中断

prt_config.h:

#define OS_TICK_PER_SECOND                              1000

Tick中断时间间隔,tick处理时间不能超过1/OS_TICK_PER_SECOND(s)。

os_cpu_armv8.h:

#ifndef OS_CPU_ARMV8_H
#define OS_CPU_ARMV8_H

#include "prt_typedef.h"

// CurrentEl等级
#define CURRENT_EL_2       0x8
#define CURRENT_EL_1       0x4
#define CURRENT_EL_0       0x0

#define DAIF_DBG_BIT      (1U << 3)
#define DAIF_ABT_BIT      (1U << 2)
#define DAIF_IRQ_BIT      (1U << 1)
#define DAIF_FIQ_BIT      (1U << 0)

#define INT_MASK          (1U << 7)

#define PRT_DSB() OS_EMBED_ASM("DSB sy" : : : "memory")
#define PRT_DMB() OS_EMBED_ASM("DMB sy" : : : "memory")
#define PRT_ISB() OS_EMBED_ASM("ISB" : : : "memory")

#endif /* OS_CPU_ARMV8_H */

宏定义了 DAIF 寄存器(异常屏蔽寄存器)中各个位的位置:

  • D(Debug)位(第 3 位):当该位被设置为 1 时,表示调试异常(Debug Exception)被屏蔽。调试异常通常由调试器使用,以实现对程序的调试和跟踪。当该位被屏蔽时,处理器将不会响应调试异常。
  • A(Asynchronous Abort)位(第 2 位):当该位被设置为 1 时,表示异步异常(Asynchronous Abort Exception)被屏蔽。异步异常通常由硬件错误或外部事件引起,例如内存错误、总线错误等。当该位被屏蔽时,处理器将不会响应异步异常。
  • I(IRQ)位(第 1 位):当该位被设置为 1 时,表示 IRQ(普通中断)被屏蔽。IRQ 是由外部设备产生的可屏蔽中断。当该位被屏蔽时,处理器将不会响应 IRQ 中断。
  • F(FIQ)位(第 0 位):当该位被设置为 1 时,表示 FIQ(快速中断)被屏蔽。FIQ 是由外部设备产生的紧急且优先级较高的中断。当该位被屏蔽时,处理器将不会响应 FIQ 中断。

timer.c:

#include "prt_typedef.h"
#include "prt_config.h"
#include "os_cpu_armv8.h"

U64 g_timerFrequency;
extern void OsGicIntSetConfig(uint32_t interrupt, uint32_t config);
extern void OsGicIntSetPriority(uint32_t interrupt, uint32_t priority);
extern void OsGicEnableInt(U32 intId);
extern void OsGicClearInt(uint32_t interrupt);

void CoreTimerInit(void)
{
    // 配置中断控制器
    OsGicIntSetConfig(30, 0); // 配置为电平触发
    OsGicIntSetPriority(30, 0); // 优先级为0
    OsGicClearInt(30); // 清除中断
    OsGicEnableInt(30); // 启用中断

    // 配置定时器
    OS_EMBED_ASM("MRS %0, CNTFRQ_EL0" : "=r"(g_timerFrequency) : : "memory", "cc"); //读取时钟频率

    U32 cfgMask = 0x0;
    U64 cycle = g_timerFrequency / OS_TICK_PER_SECOND;

    OS_EMBED_ASM("MSR CNTP_CTL_EL0, %0" : : "r"(cfgMask) : "memory");
    PRT_ISB();
    OS_EMBED_ASM("MSR CNTP_TVAL_EL0, %0" : : "r"(cycle) : "memory", "cc"); //设置中断周期

    cfgMask = 0x1;
    OS_EMBED_ASM("MSR CNTP_CTL_EL0, %0" : : "r"(cfgMask) : "memory"); //启用定时器 enable=1, imask=0, istatus= 0,
    OS_EMBED_ASM("MSR DAIFCLR, #2");
}

时钟中断处理

将 prt_vector.S 中的 EXC_HANDLE 5 OsExcDispatch 改为 EXC_HANDLE 5 OsHwiDispatcher,表明我们将对 IRQ 类型的异常(即中断)使用 OsHwiDispatcher 处理。

OsExcDispatchOsHwiDispatcher 是操作系统中用于处理不同类型异常的函数,它们的主要区别在于处理的异常类型不同。

  1. OsExcDispatch
    • OsExcDispatch 是用于处理软件异常(Exception)的函数。软件异常通常是由程序执行期间的错误或非法操作引起的,例如空指针访问、除零错误等。
    • 在操作系统中,软件异常可能会触发任务切换、异常处理程序的执行或系统的崩溃等操作,因此 OsExcDispatch 函数可能会涉及到任务管理、异常处理以及系统状态的恢复等操作。
  2. OsHwiDispatcher
    • OsHwiDispatcher 是用于处理硬件中断(Hardware Interrupt)的函数。硬件中断是由外部设备发出的信号,用于通知处理器需要处理某种事件,例如定时器到期、外部设备的输入等。
    • 在操作系统中,硬件中断通常会触发相应的中断服务程序(Interrupt Service Routine,ISR)来处理,而 OsHwiDispatcher 函数则负责将中断事件分派给相应的中断服务程序。

prt_vector.S 中加入 OsHwiDispatcher 处理代码:

 .globl OsHwiDispatcher
    .type OsHwiDispatcher, @function
    .align 4
OsHwiDispatcher:
    mrs    x5, esr_el1
    mrs    x4, far_el1
    mrs    x3, spsr_el1
    mrs    x2, elr_el1
    stp    x4, x5, [sp,#-16]!
    stp    x2, x3, [sp,#-16]!
  • 这部分代码用于保存当前异常处理器的上下文信息。
  • mrs 指令用于将系统寄存器的值读取到通用寄存器中,分别读取了 esr_el1far_el1spsr_el1elr_el1 的值。
    • esr_el1 寄存器用于存储最近发生异常时的异常信息。它包含了异常的原因和其他与异常相关的信息。
    • far_el1 寄存器用于存储最近发生数据或指令访问异常时导致异常的虚拟地址。
    • spsr_el1 寄存器用于保存 EL1(Exception Level 1)的程序状态。它存储了 EL1 状态下的程序执行信息,例如当前执行的模式、中断屏蔽状态、处理器状态标志等。
    • elr_el1 寄存器用于保存异常返回地址。在发生异常时,处理器会将异常发生时的下一条指令的地址保存在 elr_el1 寄存器中。
  • stp 指令将这些值存储到栈上,为了避免覆盖其他数据,栈指针 sp 向下移动了 16 字节。
mov    x0, x1  // 异常类型0~15,参见异常向量表
mov    x1, sp  // 异常时寄存器信息,通过栈及其sp指针提供
bl     OsHwiDispatch
  • 这部分代码设置参数并调用 OsHwiDispatch 函数来处理硬件中断。
  • x0 寄存器中存放了异常类型,通常用于表示中断号。
  • x1 寄存器中存放了栈指针 sp,即异常发生时寄存器的信息。
  • bl 指令用于调用 OsHwiDispatch 函数,执行中断的处理。
ldp    x2, x3, [sp],#16
add    sp, sp, #16        // 跳过far, esr, HCR_EL2.TRVM==1的时候,EL1不能写far, esr
msr    spsr_el1, x3
msr    elr_el1, x2
dsb    sy
isb

RESTORE_EXC_REGS // 恢复上下文

eret //从异常返回
  • 这部分代码用于恢复异常处理器的上下文信息。
  • ldp 指令从栈中加载之前保存的 elr_el1spsr_el1 寄存器的值到 x2x3 寄存器中。
  • add 指令将栈指针 sp 恢复到之前的位置。
  • msr 指令将恢复的 spsr_el1 寄存器的值写回 spsr_el1 寄存器中,将恢复的 elr_el1 寄存器的值写回 elr_el1 寄存器中。
  • dsb 指令和 isb 指令是数据同步屏障和指令同步屏障,用于确保数据和指令的同步性。

在 prt_exc.c 中添加:

extern void OsTickDispatcher(void);
OS_SEC_ALW_INLINE INLINE void OsHwiHandleActive(U32 irqNum)
{
    switch(irqNum){
        case 30:
            OsTickDispatcher();
            // PRT_Printf(".");
            break;
        default:
            break;
    }
}

extern  U32 OsGicIntAcknowledge(void);
extern void OsGicIntClear(U32 value);
// src/arch/cpu/armv8/common/hwi/prt_hwi.c  OsHwiDispatch(),OsHwiDispatchHandle()
/*
* 描述: 中断处理入口, 调用处外部已关中断
*/
OS_SEC_L0_TEXT void OsHwiDispatch( U32 excType, struct ExcRegInfo *excRegs) //src/arch/cpu/armv8/common/hwi/prt_hwi.c
{
    // 中断确认,相当于OsHwiNumGet()
    U32 value = OsGicIntAcknowledge();
    U32 irq_num = value & 0x1ff;
    U32 core_num = value & 0xe00;

    OsHwiHandleActive(irq_num);

    // 清除中断,相当于 OsHwiClear(hwiNum);
    OsGicIntClear(irq_num|core_num);
}

OsHwiHandleActive用于处理激活状态的中断:

  • 如果中断号为 30,则调用 OsTickDispatcher 函数,即处理定时器中断。
  • 如果中断号不是 30,则不执行任何操作。

OsGicIntAcknowledgeOsGicIntClear,用于中断处理的相关操作,分别是中断确认和中断清除。

OsHwiDispatch是中断处理的入口函数:

  • 首先调用 OsGicIntAcknowledge 函数确认中断,并从返回值中提取中断号和核心号。
  • 然后调用 OsHwiHandleActive 函数处理中断号。
  • 最后,调用 OsGicIntClear 函数清除中断。

prt_tick.c 文件,提供 OsTickDispatcher 时钟中断处理函数:

#include "os_attr_armv8_external.h"
#include "prt_typedef.h"
#include "prt_config.h"
#include "os_cpu_armv8_external.h"

extern U64 g_timerFrequency;

/* Tick计数 */
OS_SEC_BSS U64 g_uniTicks; //src/core/kernel/sys/prt_sys.c

/*
* 描述:Tick中断的处理函数。扫描任务超时链表、扫描超时软件定时器、扫描TSKMON等。
*/
OS_SEC_TEXT void OsTickDispatcher(void)
{
    uintptr_t intSave;

    intSave = OsIntLock();
    g_uniTicks++;
    OsIntRestore(intSave);

    U64 cycle = g_timerFrequency / OS_TICK_PER_SECOND;
    OS_EMBED_ASM("MSR CNTP_TVAL_EL0, %0" : : "r"(cycle) : "memory", "cc"); //设置中断周期

}

/*
* 描述:获取当前的tick计数
*/
OS_SEC_L2_TEXT U64 PRT_TickGetCount(void) //src/core/kernel/sys/prt_sys_time.c
{
    return g_uniTicks;
}
  1. OsTickDispatcher 函数:
    • OsTickDispatcher 函数是 Tick 中断的处理函数。
    • 首先,使用 OsIntLock 函数锁定中断,确保对全局变量 g_uniTicks 的操作是原子的。
    • 然后,递增 g_uniTicks 变量,表示 Tick 计数增加了一个 Tick。
    • 最后,计算新的中断周期,并通过内联汇编指令 MSR CNTP_TVAL_EL0 设置下一次中断的时间。
  2. PRT_TickGetCount 函数:
    • PRT_TickGetCount 函数用于获取当前的 Tick 计数值。
    • 在这段代码中,它简单地返回全局变量 g_uniTicks 的值,即当前的 Tick 计数。

这段代码实现了 Tick 计数的功能,并提供了 Tick 中断的处理函数,用于更新 Tick 计数并设置下一次中断的时间。PRT_TickGetCount 函数用于获取当前的 Tick 计数值。

将新增文件加入构造系统:

image-20240515185908933

image-20240515185940586

OsIntLock 和 OsIntRestore 函数用于关中断和开中断。将其放入 prt_exc.c 中:

/*
* 描述: 开启全局可屏蔽中断。
*/
OS_SEC_L0_TEXT uintptr_t PRT_HwiUnLock(void) //src/arch/cpu/armv8/common/hwi/prt_hwi.c
{
    uintptr_t state = 0;

    OS_EMBED_ASM(
        "mrs %0, DAIF      \n"
        "msr DAIFClr, %1   \n"
        : "=r"(state)
        : "i"(DAIF_IRQ_BIT)
        : "memory", "cc");

    return state & INT_MASK;
}

/*
* 描述: 关闭全局可屏蔽中断。
*/
OS_SEC_L0_TEXT uintptr_t PRT_HwiLock(void) //src/arch/cpu/armv8/common/hwi/prt_hwi.c
{
    uintptr_t state = 0;
    OS_EMBED_ASM(
        "mrs %0, DAIF      \n"
        "msr DAIFSet, %1   \n"
        : "=r"(state)
        : "i"(DAIF_IRQ_BIT)
        : "memory", "cc");
    return state & INT_MASK;
}

/*
* 描述: 恢复原中断状态寄存器。
*/
OS_SEC_L0_TEXT void PRT_HwiRestore(uintptr_t intSave) //src/arch/cpu/armv8/common/hwi/prt_hwi.c
{
    if ((intSave & INT_MASK) == 0) {
        OS_EMBED_ASM(
            "msr DAIFClr, %0\n"
            :
            : "i"(DAIF_IRQ_BIT)
            : "memory", "cc");
    } else {
        OS_EMBED_ASM(
            "msr DAIFSet, %0\n"
            :
            : "i"(DAIF_IRQ_BIT)
            : "memory", "cc");
    }
    return;
}
  1. PRT_HwiUnLock 函数:
    • 此函数用于开启全局可屏蔽中断。
    • 首先,它声明了一个名为 state 的本地变量用于保存当前中断状态。
    • 然后,通过内联汇编嵌入式指令,使用 mrs 指令将当前的 DAIF 寄存器(包含了中断屏蔽位)的值加载到 state 变量中。
    • 接着,使用 msr 指令清除 DAIF 寄存器的中断屏蔽位(IRQ bit),以开启全局可屏蔽中断。
    • 最后,返回之前保存的中断状态。
  2. PRT_HwiLock 函数:
    • 此函数用于关闭全局可屏蔽中断。
    • PRT_HwiUnLock 函数类似,它也声明了一个名为 state 的本地变量用于保存当前中断状态。
    • 通过内联汇编嵌入式指令,使用 mrs 指令将当前的 DAIF 寄存器的值加载到 state 变量中。
    • 然后,使用 msr 指令设置 DAIF 寄存器的中断屏蔽位(IRQ bit),以关闭全局可屏蔽中断。
    • 最后,返回之前保存的中断状态。
  3. PRT_HwiRestore 函数:
    • 此函数用于恢复原中断状态寄存器。
    • 它接受一个参数 intSave,表示先前保存的中断状态。
    • 如果先前的中断状态为非屏蔽状态,则使用 msr 指令清除 DAIF 寄存器的中断屏蔽位(IRQ bit)。
    • 如果先前的中断状态为屏蔽状态,则使用 msr 指令设置 DAIF 寄存器的中断屏蔽位(IRQ bit)。
    • 这样可以将中断状态恢复到调用 PRT_HwiUnLockPRT_HwiLock 函数前的状态。

读取系统Tick值

main函数:

#include "prt_typedef.h"
#include "prt_tick.h"

extern U32 PRT_Printf(const char *format, ...);
extern void PRT_UartInit(void);
extern void CoreTimerInit(void);
extern U32 OsHwiInit(void);

U64 delay_time = 10000;

S32 main(void)
{
    // 初始化GIC
    OsHwiInit();
    // 启用Timer
    CoreTimerInit();

    PRT_UartInit();

    PRT_Printf("            _       _ _____      _             _             _   _ _   _ _   _           \n");
    PRT_Printf("  _ __ ___ (_)_ __ (_) ____|   _| | ___ _ __  | |__  _   _  | | | | \\ | | | | | ___ _ __ \n");
    PRT_Printf(" | '_ ` _ \\| | '_ \\| |  _|| | | | |/ _ \\ '__| | '_ \\| | | | | |_| |  \\| | | | |/ _ \\ '__|\n");
    PRT_Printf(" | | | | | | | | | | | |__| |_| | |  __/ |    | |_) | |_| | |  _  | |\\  | |_| |  __/ |   \n");
    PRT_Printf(" |_| |_| |_|_|_| |_|_|_____\\__,_|_|\\___|_|    |_.__/ \\__, | |_| |_|_| \\_|\\___/ \\___|_|   \n");
    PRT_Printf("                                                     |___/                               \n");

    PRT_Printf("ctr-a h: print help of qemu emulator. ctr-a x: quit emulator.\n\n");

    for(int i = 0; i < 10; i++)
    {

        U32 tick = PRT_TickGetCount();
        PRT_Printf("[%d] current tick: %u\n", i, tick);

        //delay
        int delay_time = 10000000;  // 根据自己机器计算能力不同调整该值
        while(delay_time>0){
            PRT_TickGetCount();  //消耗时间,防止延时代码被编译器优化
            delay_time--;
        }

    }

    while(1);
    return 0;

}

image-20241027184856274

作业

实现 hwi_init.c 中缺失的 OsGicEnableIntOsGicDisableInt 函数。

OsGicDisableInt:这个函数是用来禁用特定的中断。它通过写入 GICD_ICENABLERn 寄存器来实现这个功能。GICD_ICENABLERnInterrupt Clear-Enable Registers,每一位代表一个中断,当位被设置为1时,对应的中断被禁用。阅读手册可知,GICD_ICENABLERs为GIC支持的每个中断提供了一个清除使能位。向清除使能位写入1将禁止从分发器向CPU接口转发相应的中断。读取某一位可以确定该中断是否已启用。

/*
* 描述: 去使能(禁用)指定中断
*/
OS_SEC_L4_TEXT void OsGicDisableInt(U32 intId)
{
    // Interrupt Clear-Enable Registers
    // 计算寄存器偏移
    uintptr_t reg_offset = GICD_ICENABLERn + (intId / GICD_ICENABLER_SIZE) * sizeof(U32);
    // 计算对应中断位
   uint32_t bit_position = intId % GICD_ICENABLER_SIZE;
    // 写入对应的寄存器,将对应中断位置 1,即禁用该中断
    GIC_REG_WRITE(reg_offset, 1 << bit_position);
}

首先计算偏移量和中断位,计算方法可由手册给出:

image-20240515204501528

OsGicEnableInt:这个函数是用来启用特定的中断。它通过写入 GICD_ISENABLERn 寄存器来实现这个功能。GICD_ISENABLERn Interrupt Set-Enable Registers,每一位代表一个中断,当位被设置为1时,对应的中断被启用。阅读手册可知,GICD_ISENABLERs 为 GIC 支持的每个中断提供了一个 Set-enable 位。向 Set-enable 位写入 1 可以启用将相应的中断从 Distributor 转发到 CPU 接口。读取一个位可以确定该中断是否已启用。

/*
* 描述: 使能指定中断
*/
OS_SEC_L4_TEXT void OsGicEnableInt(U32 intId)
{
    // Interrupt Set-Enable Registers
    // 计算寄存器偏移
    uintptr_t reg_offset = GICD_ISENABLERn + (intId / GICD_ISENABLER_SIZE) * sizeof(U32);
    // 计算对应中断位
    uint32_t bit_position = intId % GICD_ISENABLER_SIZE;
    // 写入对应的寄存器,将对应中断位置 1,即使能该中断
     GIC_REG_WRITE(reg_offset, 1 << bit_position);
}

首先计算偏移量和中断位,计算方法可由手册给出:

image-20240515203304308

  • intId / GICD_ICENABLER_SIZE:这部分代码是计算出中断ID intId 对应的中断在哪一个中断清除使能寄存器中。每个中断清除使能寄存器可以控制 GICD_ICENABLER_SIZE 个中断,所以我们用中断ID除以这个值可以得到对应的寄存器的编号。
  • * sizeof(U32):这部分代码是将寄存器的编号转换为实际的内存地址。因为每个寄存器的大小是 sizeof(U32) 字节,所以我们用编号乘以这个值就得到了相对于基地址的偏移。

image-20241027184949295

标签:Lab5,中断,GIC,2022,寄存器,HNU,define,OS,GICD
From: https://blog.csdn.net/2301_76658831/article/details/143272524

相关文章

  • Windows Server 2022 中文版、英文版下载 (updated Oct 2024)
    WindowsServer2022中文版、英文版下载(updatedOct2024)WindowsServer2022x64,Version21H2请访问原文链接:https://sysin.org/blog/windows-server-2022/查看最新版。原创作品,转载请保留出处。作者主页:sysin.orgWindowsServer2022采用先进的多层安全机制......
  • Windows Server 2022 中文版、英文版下载 (updated Oct 2024)
    WindowsServer2022中文版、英文版下载(updatedOct2024)WindowsServer2022x64,Version21H2请访问原文链接:https://sysin.org/blog/windows-server-2022/查看最新版。原创作品,转载请保留出处。作者主页:sysin.orgWindowsServer2022采用先进的多层安全机制、Azure......
  • 20222408 2024-2025-1 《网络与系统攻防技术》实验三实验报告
    1.实验内容1.1回答问题(1)杀软是如何检测出恶意代码的?①基于特征码的检测:AV软件厂商搜集最全最新的特征码库,并以此来尝试匹配文件中的一个或几个片段②启发式恶意软件检测:根据片面特征推断,包括行为(如连接恶意网站、开放端口、修改系统文件等),外观(文件签名、结构、厂商等)。③基于行......
  • VS2022 添加旧版本.NET Framework 3.5/4.0支持
    鉴于vs2022最旧只支持到.netframework4.6.2有些项目.netframework版本比较低,又想要用新版本vs以3.5为例要使vs2022支持低版本.netframework项目,可参考以下步骤实现下载.netframeworknuget包下载链接如下,可根据需要下载对应版本v3.5v4.0v4.5修改后缀为zip或直接......
  • 20222326 2024-2025-1 《网络与系统攻防技术》实验三实验报告
    1.实验内容实验具体内容正确使用msf编码器,veil-evasion,自己利用shellcode编程等免杀工具或技巧正确使用msf编码器,使用msfvenom生成如jar之类的其他文件veil,加壳工具使用C+shellcode编程通过组合应用各种技术实现恶意代码免杀用另一电脑实测,在杀软开启的情况下,可......
  • Visual Studio 2022工作原理及相关配置参数(干货满满)——C++
    最近工作有点忙,毕业也没多久,确实在企业和学校还是有很大的差距的,这段时间学到了很多很多,也没时间顾及博客了,刚好趁着这个1024稍微放慢脚步,总结总结。最近用VisualStudio比较频繁,也学到了很多相关的内容,借此博文简单记录一下,全是个人理解,若有地方理解有误还请各位大佬评论......
  • P8164 [JOI 2022 Final] 沙堡 2 题解
    DescriptionJOI君在沙滩上堆沙堡,他已经做好了一个沙堡,沙堡可以使用一个\(H\timesW\)的二维矩形表示,其被划分成若干个\(1\times1\)的小格子,格子高度互相不同。JOI君决定在沙堡上游走,他可以从任意一个点出发,向上下左右四个方向行走,必须满足他行走的路径单调下降。出于一......
  • 20222403 2024-2025-1 《网络与系统攻防技术》实验三实验报告
    1.实验内容1.1.实践内容(1)正确使用msf编码器,veil-evasion,自己利用shellcode编程等免杀工具或技巧正确使用msf编码器,使用msfvenom生成如jar之类的其他文件veil,加壳工具使用C+shellcode编程(2)通过组合应用各种技术实现恶意代码免杀如果成功实现了免杀的,简单语言描述原......
  • 20222305 2024-2025-1 《网络与系统攻防技术》实验三实验报告
    网络攻防实验报告姓名:田青学号:20222305实验日期:2024/10/18—2024/10/31实验名称:后门原理与实践指导教师:王志强1.实验内容本周学习内容总结:1.用户态(ring3)和内核态(ring0),切换时机:系统调用、中断、异常。2.自启动技术。3.进程隐藏技术实现:1>改名2>基于系统服务的伪装3>......
  • 20222327 2024-2025-1 《网络与系统攻防技术》实验三实验报告
    一、实验内容1.正确使用msf编码器,veil-evasion,自己利用shellcode编程等免杀工具或技巧2.通过组合应用各种技术实现恶意代码免杀3.用另一电脑实测,在杀软开启的情况下,可运行并回连成功,注明电脑的杀软名称与版本4.问题回答(1)杀软是如何检测出恶意代码的?基于特征码检测:杀毒软件中......