开启_禁止中断

禁止/开启中断

软件可以禁止中断,使处理器不再响应任何中断请求,但是不可屏蔽中断(NMI,Non Maskable Interrupt)例外。禁止中断在需要临界区保护的场景下非常重要,避免中断打断正在执行的代码。

禁止中断的接口如下。

(1)local_irq_disable()。

(2)local_irq_save(flags):首先把中断状态保存在参数flags中,然后禁止中断。

这两个接口只能禁止当前处理器的中断,不能禁止其他处理器的中断。禁止中断以后,处理器不再响应中断请求。

开启中断的接口如下。

(1)local_irq_enable()。

(2)local irq_restore(flags):恢复本处理器的中断状态。

local_irq_disable()和local_irq_enable()不能嵌套使用,local_irq_save(flags)和local_irq_restore(flags)可以嵌套使用。

禁止中断

ARM64架构禁止中断的函数local_irq_disable()如下:


local_irq_disable() -> raw_local_irq_disable() -> arch_local_irq_disable()

c 复制代码
#define local_irq_disable()	do { raw_local_irq_disable(); } while (0)

#define raw_local_irq_disable()		arch_local_irq_disable()
c 复制代码
static inline void arch_local_irq_disable(void)
{
	asm volatile(
		"msr	daifset, #2		// arch_local_irq_disable"
		:
		:
		: "memory");
}

arch_local_irq_disable(),通过汇编指令禁止中断;

  1. msr指令

• msr 是 ARM 汇编中的指令,表示 "Move to Special Register"。

• 它将立即数或寄存器的值写入特定的系统寄存器。

  1. daifset:

• daif 是一个包含中断屏蔽位的寄存器,具体表示:

D (Debug Mask): 调试异常屏蔽位。

A (SError Mask): 同步错误异常屏蔽位。

I (IRQ Mask): 普通中断屏蔽位。

F (FIQ Mask): 快速中断屏蔽位。

• daifset 是 daif 的一个操作子集,用于设置(屏蔽)指定的中断类型。

  1. #2:

• #2 表示屏蔽普通中断(IRQ)。

• 屏蔽操作会将 I 位设置为 1,从而禁止普通中断。

  1. memoryclobber:

• 通知编译器汇编指令可能对内存产生副作用,确保在此指令前后的内存访问不会被重排序。

开启中断

ARM64架构开启中断的函数local_irq_enable()如下:


raw_local_irq_enable() -> raw_local_irq_enable() -> arch_local_irq_enable()

c 复制代码
#define local_irq_enable()	do { raw_local_irq_enable(); } while (0)

#define raw_local_irq_enable()		arch_local_irq_enable()
c 复制代码
static inline void arch_local_irq_enable(void)
{
	asm volatile(
		"msr	daifclr, #2		// arch_local_irq_enable"
		:
		:
		: "memory");
}

将daif寄存器中断I 位设置为 0,从而使能普通中断。

禁止/开启单个中断

软件可以禁止某个外围设备的中断,中断控制器不会把该设备发送的中断转发给处理器。

禁止中断

禁止单个中断的函数如下:

c 复制代码
/**
 *	disable_irq - disable an irq and wait for completion
 *	@irq: Interrupt to disable
 *
 *	Disable the selected interrupt line.  Enables and Disables are
 *	nested.
 *	This function waits for any pending IRQ handlers for this interrupt
 *	to complete before returning. If you use this function while
 *	holding a resource the IRQ handler may need you will deadlock.
 *
 *	This function may be called - with care - from IRQ context.
 */
void disable_irq(unsigned int irq)
{
	if (!__disable_irq_nosync(irq))
		synchronize_irq(irq);
}

此函数调用 __disable_irq_nosync() 函数,并将中断号 (irq) 作为参数传入。此函数用于禁用指定的中断,而不必等待任何现有的中断处理程序完成。首先找到中断描述符(irq_desc),之后对irq_data进行set_mask操作。

如果 __disable_irq_nosync() 函数的返回值为 0,表示中断已成功禁用。在这种情况下,将调用 synchronize_irq() 函数来确保在返回之前,已禁用的中断的任何挂起的中断处理程序都已完成。

如果 __disable_irq_nosync() 函数的返回值不为 0,表示中断无法被禁用。在这种情况下,不会执行任何进一步的操作。

disable_irq() 函数为用户提供了一个易于使用的接口,用于禁用中断,并确保在返回之前完成所有挂起的中断处理程序。

开启中断

开启单个中断函数如下:

c 复制代码
/**
 *	enable_irq - enable handling of an irq
 *	@irq: Interrupt to enable
 *
 *	Undoes the effect of one call to disable_irq().  If this
 *	matches the last disable, processing of interrupts on this
 *	IRQ line is re-enabled.
 *
 *	This function may be called from IRQ context only when
 *	desc->irq_data.chip->bus_lock and desc->chip->bus_sync_unlock are NULL !
 */
void enable_irq(unsigned int irq)
{
	unsigned long flags;
	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);

	if (!desc)
		return;
	if (WARN(!desc->irq_data.chip,
		 KERN_ERR "enable_irq before setup/request_irq: irq %u\n", irq))
		goto out;

	__enable_irq(desc);
out:
	irq_put_desc_busunlock(desc, flags);
}

GIC控制器实现

对于ARM64架构的GIC控制器,如果需要开启硬件中断n,那么设置分发器的寄存器GICD_ISENABLERn(Interrupt Set-Enable Register);如果需需要禁止硬件中断n,那么设置分发器的寄存器GICD_ICENABLERn (Interrupt Clear-EnableRegister)。

假设某个外围设备的硬件中断号是n,当这个外围设备发送中断给分发器的时候,只有在分发器上开启了硬件中断n,分发器才会把硬件中断n转发给处理器。

以下是GIC控制器禁止/启用中断,设置分发器寄存器的代码:

c 复制代码
static void gic_poke_irq(struct irq_data *d, u32 offset)
{
	u32 mask = 1 << (gic_irq(d) % 32);
	writel_relaxed(mask, gic_dist_base(d) + offset + (gic_irq(d) / 32) * 4);
}

static void gic_mask_irq(struct irq_data *d)
{
	gic_poke_irq(d, GIC_DIST_ENABLE_CLEAR);
}

static void gic_unmask_irq(struct irq_data *d)
{
	gic_poke_irq(d, GIC_DIST_ENABLE_SET);
}

参考资料

  1. Professional Linux Kernel Architecture,Wolfgang Mauerer
  2. Linux内核深度解析,余华兵
  3. Linux设备驱动开发详解,宋宝华
相关推荐
合天网安实验室3 个月前
Linux kernel 堆溢出利用方法(三)
ctf·linux kernel·堆溢出
百里杨6 个月前
RISC-V特权架构 - 时钟中断处理
risc-v·委托·注入·中断处理
Eloudy7 个月前
一键编译并启动一个 ARM Linux qemu 虚拟机
arm·qemu·linux kernel
Eloudy7 个月前
NVidia 的 gpu 开源 Linux Kernel Module Driver 编译 安装 使用
gpu·linux kernel·kmd
黑不溜秋的7 个月前
驱动开发系列-中断处理
驱动·中断处理
kelebukele7 个月前
Linux内核编译安装 - Deepin,Debian系
linux·debian·linux kernel·deepin
Eloudy8 个月前
qemu 安装ubuntu -纯命令行-可ssh-带网络-可gdb linux kernel
mmu·linux kernel·linuxkernel
charlie11451419110 个月前
Linux内核深入学习 - 中断与异常(上)
linux·操作系统·c·中断处理
独上西楼影三人1 年前
【Linux】如何关闭 swappiness ?
linux·服务器·linux kernel·swappiness