arm64架构的linux中断分析(二)

news2024/10/7 2:18:57

文章目录

    • 3. GICv3中断控制器
      • 3.1 GICv3中断控制器设备树
      • 3.2 GICv3中断控制器驱动

3. GICv3中断控制器

gic在soc中的位置如下:
在这里插入图片描述

GICv3(Generic Interrupt Controller Version 3)是一种基于ARM Cortex-A架构的中断控制器,它提供了高度灵活和可扩展的中断架构,适用于多核系统和虚拟化环境中,它还提供对TrustZone安全性扩展的支持。
GICv3 控制器接受系统级别中断的产生,并可以发信号通知给它所连接的每个内核,从而有可能导致IRQ或FIQ异常发生。GICv3 还支持虚拟中断,允许将虚拟机中的中断映射到真实机器上,并为每个虚拟处理器提供独立的中断处理能力。
GICv3 中,中断处理程序可以以不同的模式运行:Secure EL1、Non-secure EL1、Secure EL2 和 Non-secure EL2。这使得 GICv3 在处理不同的安全级别和特权级别时,有更强的灵活性和可定制性。
在GICv3中,中断被分为三种类型:软件生成中断(SGI)、私有中断(PPI)和共享中断(SPI)。下面对这三种中断类型进行简要的介绍:

  1. 软件生成中断(SGI):SGI是由处理器上的软件或操作系统生成的中断,用于在特定的CPU上触发中断。它们是非可屏蔽中断(NMI),具有固定的优先级和中断号。在ARMv8-A体系结构中,每个处理器都支持4个SGI中断线,其中SGI 0用于调试控制,其他SGI可以用于特定的事件处理。

  2. 私有中断(PPI):PPI是由处理器上的硬件设备或系统外设生成的中断。PPI是由每个处理器的本地中断控制器(LPI)处理并响应的,因此不会发送到网络中的其他处理器。PPI的中断号和优先级是固定的,但不保证在所有处理器上相同,因为它们由LPI分配。

  3. 共享中断(SPI):SPI是由GICv3的分发器(Distributor)处理分配给多个处理器的中断。SPI可以由设备连接到任意处理器中的任意一个或多个中断信号线,因此在多核系统中,分配给不同处理器的SPI可以在生成它的设备上具有不同的优先级和中断号。

GICv3包括了Distributor和Redistributor子系统。Distributor子系统接收来自外部设备的中断请求,进行中断的分类处理,再分配到不同的Redistributor进行处理。Redistributor子系统接收来自Distributor的中断请求,将其分配到不同的CPU处理器进行响应。GICv3 可以通过软件配置来实现中断控制器的定制化和协同工作。这些寄存器包括:

  1. Distributor registers:用于将中断向量路由到正确的 CPU 指令队列和 CPU 核中。也就是GICD_的系列寄存器。
  2. Redistributor registers:用于将来自 Distributor 的中断路由到对应的 CPU 核。也就是GICR_的系列寄存器。
  3. CPU interface registers:用于在 CPU 上启用、禁用、优先级排序和处理中断。也就是GICC_的系列寄存器。

当外部设备发出中断请求时,请求被分配给Distributor子系统,由该子系统根据优先级进行分类。分类后的中断请求发送给不同的Redistributor子系统,再由Redistributor子系统将中断请求发送给对应的CPU处理器进行处理。处理器通过在向量地址映射到对应的中断服务程序,来响应中断请求。

GICv3控制器会记录中断的状态,中断可以处于多种不同状态:
① 非活动状态(Inactive)–这意味着该中断未触发。
② 挂起(Pending)–这意味着中断源已被触发,但正在等待CPU核处理。待处理的中断要通过转发到CPU接口单元,然后再由CPU接口单元转发到内核。
③ 活动(Active)–描述了一个已被内核接收并正在处理的中断。
④ 活动和挂起(Active and pending)–描述了一种情况,其中CPU核正在为中断服务,而GIC又收到来自同一源的中断。

中断的优先级和可接收中断的核都在分发器(distributor)中配置。外设发给分发器的中断将标记为pending状态(或Active and Pending状态,如触发时果状态是active)。distributor确定可以传递给CPU核的优先级最高的pending中断,并将其转发给内核的CPU interface。通过CPU interface,该中断又向CPU核发出信号,此时CPU核将触发FIQ或IRQ异常。
作为响应,CPU核执行异常处理程序。异常处理程序必须从CPU interface寄存器查询中断ID,并开始为中断源提供服务。完成后,处理程序必须写入CPU interface寄存器以报告处理结束。然后CPU interface准备转发distributor发给它的下一个中断。在处理中断时,中断的状态开始为pending,active,结束时变成inactive。中断状态保存在distributor寄存器中。

3.1 GICv3中断控制器设备树

	gic: interrupt-controller@30800000 {
		compatible = "arm,gic-v3";
		#interrupt-cells = <3>;
		#address-cells = <2>;
		#size-cells = <2>;
		ranges;
		interrupt-controller;
		reg = <0x0 0x30800000 0 0x20000>,	/* GICD */
		      <0x0 0x30880000 0 0x80000>,	/* GICR */
		      <0x0 0x30840000 0 0x10000>,	/* GICC */
		      <0x0 0x30850000 0 0x10000>,	/* GICH */
		      <0x0 0x30860000 0 0x10000>;	/* GICV */
		interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_LOW>;

		its: gic-its@30820000 {
			compatible = "arm,gic-v3-its";
			msi-controller;
			reg = <0x0 0x30820000 0x0 0x20000>;
		};
	};

这是飞腾的gic的设备树,跟瑞芯微的差不多,不过瑞芯微的的reg只有GICD和GICR,虽然飞腾的设备树多了这个多寄存器,实际上驱动也是没有去读取那些寄存器的。its的我们不用看,这是PCIE才用到的。

3.2 GICv3中断控制器驱动

驱动代码在drivers/irqchip/irq-gic-v3.c文件中,首先我们看看compatible 是怎么匹配的:

IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);

展开后为:

static const struct of_device_id __of_table_gic_v3		\
	__used __section(__irqchip_of_table)				\
	 = { .compatible = "arm,gic-v3",					\
		 .data = gic_of_init  }

那么gic_of_init 函数是怎么调用到的呢?其调用路径如下:
start_kernel (init\main.c) → init_IRQ (arch\arm64\kernel\irq.c) → irqchip_init (drivers\irqchip\irqchip.c) → of_irq_init(__irqchip_of_table);(drivers\of\irq.c) → desc->irq_init_cb = match->data;和 ret = desc->irq_init_cb(desc->dev,desc->interrupt_parent);
前面的调用路径都好理解,就是start_kernel一直调用到 of_irq_init这个函数,我们的宏申明了__irqchip_of_table这个段,所以在 of_irq_init这个函数里面会遍历整个设备树,找到所有compatible匹配的节点,并且判断是否为interrupt-controller,是否有data成员,如果都有,则把date成员赋值给desc->irq_init_cb,最后调用desc->irq_init_cb。这就会让"arm,gic-v3"的date成员gic_of_init函数调用起来,精简过的of_irq_init函数如下:

void __init of_irq_init(const struct of_device_id *matches)
{
	...
	for_each_matching_node_and_match(np, matches, &match) {
		if (!of_property_read_bool(np, "interrupt-controller") ||
				!of_device_is_available(np))
			continue;

		if (WARN(!match->data, "of_irq_init: no init function for %s\n",
			 match->compatible))
			continue;
		...
		desc = kzalloc(sizeof(*desc), GFP_KERNEL);

		desc->irq_init_cb = match->data;
		...
	}

	while (!list_empty(&intc_desc_list)) {
		list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
			ret = desc->irq_init_cb(desc->dev,
						desc->interrupt_parent);
		...
		}
	}
}

现在我们看看gic_of_init函数主要是获取设备树中的信息,然后初始化gic控制器:

static int __init gic_of_init(struct device_node *node, struct device_node *parent)
{
	void __iomem *dist_base;
	struct redist_region *rdist_regs;
	u64 redist_stride;
	u32 nr_redist_regions;
	int err, i;

	dist_base = of_iomap(node, 0);//获取GICD的地址,并且进行了映射
	if (!dist_base) {
		pr_err("%pOF: unable to map gic dist registers\n", node);
		return -ENXIO;
	}

	err = gic_validate_dist_version(dist_base);//验证gic版本
	if (err) {
		pr_err("%pOF: no distributor detected, giving up\n", node);
		goto out_unmap_dist;
	}

	if (of_property_read_u32(node, "#redistributor-regions", &nr_redist_regions))
		nr_redist_regions = 1;

	rdist_regs = kcalloc(nr_redist_regions, sizeof(*rdist_regs),
			     GFP_KERNEL);
	if (!rdist_regs) {
		err = -ENOMEM;
		goto out_unmap_dist;
	}

	//redistributor-regions不存在,nr_redist_regions值为1,获取GICR的地址
	for (i = 0; i < nr_redist_regions; i++) {
		struct resource res;
		int ret;

		ret = of_address_to_resource(node, 1 + i, &res);
		rdist_regs[i].redist_base = of_iomap(node, 1 + i);
		if (ret || !rdist_regs[i].redist_base) {
			pr_err("%pOF: couldn't map region %d\n", node, i);
			err = -ENODEV;
			goto out_unmap_rdist;
		}
		rdist_regs[i].phys_base = res.start;
	}

	if (of_property_read_u64(node, "redistributor-stride", &redist_stride))
		redist_stride = 0;

	gic_enable_of_quirks(node, gic_quirks, &gic_data);

	//初始化GIC控制器,设置GICD等寄存器来配置NMI、SGI、PPI和SPI中断
	err = gic_init_bases(dist_base, rdist_regs, nr_redist_regions,
			     redist_stride, &node->fwnode);
	if (err)
		goto out_unmap_rdist;

	gic_populate_ppi_partitions(node);

	if (static_branch_likely(&supports_deactivate_key))
		gic_of_setup_kvm_info(node);
	return 0;

out_unmap_rdist:
	for (i = 0; i < nr_redist_regions; i++)
		if (rdist_regs[i].redist_base)
			iounmap(rdist_regs[i].redist_base);
	kfree(rdist_regs);
out_unmap_dist:
	iounmap(dist_base);
	return err;
}

gic_of_init主要做了一下几件事:

  1. 获取GICD的地址,并且进行了映射,映射地址存到dist_base
  2. 调用函数gic_validate_dist_version验证gic版本
  3. 获取GICR的地址存到rdist_regs数组中
  4. 调用函数gic_init_bases初始化GIC控制器,设置GICD等寄存器来配置NMI、SGI、PPI和SPI中断

我们再看看gic_init_bases函数:

static int __init gic_init_bases(void __iomem *dist_base,
				 struct redist_region *rdist_regs,
				 u32 nr_redist_regions,
				 u64 redist_stride,
				 struct fwnode_handle *handle)
{
	u32 typer;
	int err;

	if (!is_hyp_mode_available())
		static_branch_disable(&supports_deactivate_key);

	if (static_branch_likely(&supports_deactivate_key))
		pr_info("GIC: Using split EOI/Deactivate mode\n");

	//设置全局变量gic_data
	gic_data.fwnode = handle;
	gic_data.dist_base = dist_base;
	gic_data.redist_regions = rdist_regs;
	gic_data.nr_redist_regions = nr_redist_regions;
	gic_data.redist_stride = redist_stride;

	/*
	 * Find out how many interrupts are supported.
	 */
	typer = readl_relaxed(gic_data.dist_base + GICD_TYPER);
	gic_data.rdists.gicd_typer = typer;

	gic_enable_quirks(readl_relaxed(gic_data.dist_base + GICD_IIDR),
			  gic_quirks, &gic_data);

	pr_info("%d SPIs implemented\n", GIC_LINE_NR - 32);
	pr_info("%d Extended SPIs implemented\n", GIC_ESPI_NR);

	/*
	 * ThunderX1 explodes on reading GICD_TYPER2, in violation of the
	 * architecture spec (which says that reserved registers are RES0).
	 */
	if (!(gic_data.flags & FLAGS_WORKAROUND_CAVIUM_ERRATUM_38539))
		gic_data.rdists.gicd_typer2 = readl_relaxed(gic_data.dist_base + GICD_TYPER2);

	//创建并且初始化domain,最重要的是gic_irq_domain_ops方法
	gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops,
						 &gic_data);
	gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist));
	gic_data.rdists.has_rvpeid = true;
	gic_data.rdists.has_vlpis = true;
	gic_data.rdists.has_direct_lpi = true;
	gic_data.rdists.has_vpend_valid_dirty = true;

	if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdists.rdist)) {
		err = -ENOMEM;
		goto out_free;
	}

	irq_domain_update_bus_token(gic_data.domain, DOMAIN_BUS_WIRED);

	gic_data.has_rss = !!(typer & GICD_TYPER_RSS);
	pr_info("Distributor has %sRange Selector support\n",
		gic_data.has_rss ? "" : "no ");

	if (typer & GICD_TYPER_MBIS) {
		err = mbi_init(handle, gic_data.domain);
		if (err)
			pr_err("Failed to initialize MBIs\n");
	}

	//设置handle_arch_irq为gic_handle_irq,cpu触发irq就会跑到gic_handle_irq
	set_handle_irq(gic_handle_irq);

	gic_update_rdist_properties();

	gic_dist_init();//初始化GICD分发器,配置控制器的中断路由
	gic_cpu_init();//初始化GICR,配置PPI,
	gic_smp_init();//初始化SGI中断
	gic_cpu_pm_init();//初始化CPU的GIC控制器用于中断的电源管理特性

	if (gic_dist_supports_lpis()) {
		its_init(handle, &gic_data.rdists, gic_data.domain);
		its_cpu_init();
	} else {
		if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
			gicv2m_init(handle, gic_data.domain);
	}

	gic_enable_nmi_support();//初始化NMI中断

	return 0;

out_free:
	if (gic_data.domain)
		irq_domain_remove(gic_data.domain);
	free_percpu(gic_data.rdists.rdist);
	return err;
}

gic_init_bases主要做了一下几件事:

  1. 设置全局变量gic_data,
  2. 创建并且初始化domain,
  3. 调用函数set_handle_irq设置handle_arch_irq为gic_handle_irq,handle_arch_irq就是上面异常向量表的未知函数
  4. 调用函数gic_dist_ini初始化GICD分发器,配置控制器的中断路由
  5. 调用函数gic_cpu_ini初始化GICR,配置PPI,
  6. 调用函数gic_smp_ini初始化SGI中断
  7. 调用函数 gic_cpu_pm_init初始化CPU的GIC控制器用于中断的电源管理特性
  8. 调用函数 gic_enable_nmi_suppor初始化NMI中断

到这里,这个gic控制器的初始化流程就讲完了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/665835.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

联邦元学习综述

联邦元学习综述 张传尧1,2, 司世景1, 王健宗1&#xff0c;肖京1 1 平安科技&#xff08;深圳&#xff09;有限公司&#xff0c;广东 深圳 518063 2 中国科学技术大学&#xff0c;安徽 合肥 230026 摘要&#xff1a;随着移动设备的普及&#xff0c;海量的数据在不断产生。数据隐…

express的使用(六) 中间件的理解

原文链接 express的使用(六) 中间件的理解") 不要脸的求关注&#xff0c;希望能让大家批评我的不足点&#xff0c;方便学习&#xff0c;一键三连最好不过了~另外&#xff0c;乌鸦玩心之钢是真的爽&#xff01; 看前提示 本篇主要讲的是关于express中间件的一些基础概念…

商品支付金额篡改测试-业务安全测试实操(16)

商品支付金额篡改测试,商品订购数量篡改测试 商品支付金额篡改测试 测试原理和方法 电商类网站在业务流程整个环节,需要对业务数据的完整性和一致性进行保护,特别是确保在用户客户端与服务、业务系统接口之间的数据传输的一致性,通常在订购类交易流程中,容易出现服务器端未…

如何在纺织服装行业运用IPD?

纺织服装行业是我国的传统支柱产业&#xff0c;对促进国民经济发展、解决就业、增加国民收入、促进社会和谐发展等方面具有十分重要的意义。纺织服装行业属于劳动密集型产业&#xff0c;产业链上下游关联度较大。产业链上游原材料主要包括棉花、麻、蚕茧丝等天然纤维以及人造纤…

艾默生CE4001S2T2B4控制模块

​ 艾默生CE4001S2T2B4控制模块 艾默生CE4001S2T2B4控制模块 集散控制系统简称dcs&#xff0c;也可直译为“分散控制系统”或“分布式计算机控制系统”。它采用控制分散、操作和管理集中的基本设计思想&#xff0c;采用多层分级、合作自治的结构形式。其主要特征是它的集中管理…

spring boot 校运会赛事管理系统-计算机毕设 附源码87890

spring boot校运会赛事管理系统 摘 要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。在现实运…

TC8:ICMPv4_TYPE_18-22

ICMPv4_TYPE_18: Send ICMP Destination Unreachable for unknown protocol 目的 主机收到IP Header中协议字段不支持的IP数据包时,回复ICMP目的不可达报文(未知协议) 协议不可达报文的Type为3,Code为2 测试步骤 Tester:发送一条IP报文,其中协议字段值为无效值DUT:发送…

MySQL数据库基础 14

第十四章 视图 1. 常见的数据库对象2. 视图概述2.1 为什么使用视图&#xff1f;2.2 视图的理解 3. 创建视图3.1 创建单表视图3.2 创建多表联合视图3.3 基于视图创建视图 4. 查看视图5. 更新视图的数据5.1 一般情况5.2 不可更新的视图 6. 修改、删除视图6.1 修改视图6.2 删除视图…

软考高级系统架构设计师(三) 基础知识之操作系统2(分页/分段/段页存储)

目录 存储管理 页式存储 段式存储 段页式存储 存储管理 存储管理的主要目的&#xff1a;解决多个用户共同使用主存的问题&#xff08;怎么分配内存&#xff1f;&#xff1f;&#xff09; 主要包括分区存储管理、分页存储管理、分段存储器管理、段页式存储管理以及虚拟存储…

eNSP中对NAT的配置(网络地址转换)

地址转换是把局域网的私有地址转换为公有地址&#xff0c;如果想要上外网&#xff0c;而公有地址是有限的&#xff0c;则需要将私有地址转换成公有地址&#xff0c;用端口号进行区分。 一.基本原理 NAT是改变IP报文中的源或目的地址的一种处理方式&#xff1b;让局域网用户…

清微智能TX5368A与飞桨完成Ⅱ级兼容性测试,助力全行业智能化升级

近日&#xff0c;清微智能的高性能视觉芯片TX5368A与飞桨完成Ⅱ级兼容性测试&#xff08;基于Paddle2ONNX工具&#xff09;。测试结果显示&#xff0c;双方兼容性表现良好&#xff0c;整体运行稳定。这是清微智能加入“硬件生态共创计划”后取得的又一阶段性成果。 产品兼容性证…

差分信号隔离放大变送模块光电转换器0-10mV/0-20mV/0-±10mV/0-±20mV转0-5V/0-10V/4-20mA

概述&#xff1a; DIN11 IPO 压力应变桥信号处理系列隔离放大器是一种将差分输入信号隔离放大、转换成按比例输出的直流信号导轨安装变送模块。产品广泛应用在电力、远程监控、仪器仪表、医疗设备、工业自控等行业。此系列模块内部嵌入了一个高效微功率的电源&#xff0c;向输…

软考高级系统架构设计师(三) 基础知识之操作系统1进程

目录 概要 操作系统 概述 ​编辑 进程管理 进程的状态 进程管理-PV操作 利用PV操作实现进程的同步 进程管理-前趋图 进程管理-死锁 概要 操作系统 概述 操作系统的功能&#xff1a; 进程管理、存储管理、文件管理、作业管理、设备管理 操作系统的特征 并发性共享性虚拟性不…

竞逐对话式AI,百度、字节各有千秋

随着OpenAI陆续发布的ChatGPT引发了AI界热议&#xff0c;新一代的AI热度便开始持续走高。与此同时&#xff0c;以ChatGPT模型为代表的大型预训练模型的出现&#xff0c;也使得对话式AI的生成能力和智能水平得到了飞跃式的提升&#xff0c;得益于此&#xff0c;对话式AI的发展又…

FPGA_学习_10_IP核_PLL

1 PLL IP核配置步骤 (Vivado 赛灵思) 我看的教程里面&#xff0c;那个兄弟是选的下面这个。 看来还是比较注重开发效率。 下面按照截图路径打开这个veo文件&#xff0c;学习如何在FPGA程序中例化IP核&#xff08;有点像C你创建了一个类&#xff0c;然后你实例化一个&#xff…

MES生产管理系统简介与实践经验分享,一篇就够了

MES系统是制造业企业数字化转型的重要组成部分&#xff0c;也是实现智能制造的基础。本文将从MES系统的定义、功能和应用实践等方面进行介绍和分享。 一、MES系统简介 MES系统&#xff08;Manufacturing Execution System&#xff09;又称生产执行系统&#xff0c;是在企业信…

JS自定义打印网页内容(详细全面)

浏览器自身打印 (不在赘述,重点是讲述打印自定义的几种方法与常见的问题) 使用 window.print() 调起浏览器自带的打印预览弹框打印默认会打印 body 里面所有内容const handlePrintPdf = () => {window.print(); }方式一:通过 iframe 打印部分区域 动态创建一个不可见的 i…

【Android -- JNI 和 NDK】认识 NDK

简介 定义&#xff1a;Native Development Kit &#xff0c;是 Android 的一个工具开发包。 NDK 是属于 Android 的&#xff0c;与 Java 并无直接关系 作用&#xff1a;快速开发 C、 C 的动态库&#xff0c;并自动将 so 和应用一起打包成 APK 即可通过 NDK 在 Android 中 使用…

拼多多买家订单API

目录 订单查询支持两种方式 请求数据格式&#xff08;指定账号&#xff09; 请求头示例 数据格式说明 返回数据格式&#xff08;指定账号&#xff09; 返回头示例 数据格式说明 不同页面层级各自有什么订单数据 请求数据格式&#xff08;指定订单编号&#xff09; 请…

电脑桌面图标打不开?尝试这3个解决方法!

Dam在使用电脑时&#xff0c;无论怎么点击桌面图标都无法打开&#xff0c;他感到很苦恼。遇到电脑桌面图标打不开的情况应该怎么办呢&#xff1f; 电脑桌面上的图标是我们经常使用的快捷方式&#xff0c;但有时候它们可能会出现无法打开的问题。这可能是由于各种原因引起的&…