zoukankan      html  css  js  c++  java
  • iommu分析之intel iommu初始化

    intel 的iommu 是iommu框架的一个实现案例。
    由于intel 的iommu 实现得比arm smmv3复杂得多,里面概念也多,所以针对intel 实现的iommu 案例的初始化部分进行一些讲解,本文针对4.19内核。
    Intel IOMMU的初始化函数在哪调用的呢?
    它的初始化函数是:

    int __init intel_iommu_init(void)
    {
    	int ret = -ENODEV;
    	struct dmar_drhd_unit *drhd;
    	struct intel_iommu *iommu;
    
    	/* VT-d is required for a TXT/tboot launch, so enforce that */
    	force_on = tboot_force_iommu();
    

    那这个函数是在常见的模块初始化里面调用的么?
    事实上,它的调用链是这样的,

    int __init detect_intel_iommu(void)//caq:IOMMU_INIT_POST调用这个,有amd的,ibm大型机的
    {//caq:此时还没有memory allocator
    	int ret;
    	struct dmar_res_callback validate_drhd_cb = {
    		.cb[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &dmar_validate_one_drhd,
    		.ignore_unhandled = true,
    	};
    
    	down_write(&dmar_global_lock);
    	ret = dmar_table_detect();//caq:bios/uefi提供的dmar table
    	if (!ret)
    		ret = dmar_walk_dmar_table((struct acpi_table_dmar *)dmar_tbl,
    					   &validate_drhd_cb);
    	if (!ret && !no_iommu && !iommu_detected && !dmar_disabled) {
    		iommu_detected = 1;//caq:返回正常就设定这个标志位
    		/* Make sure ACS will be enabled */
    		pci_request_acs();
    	}
    
    #ifdef CONFIG_X86
    	if (!ret)
    		**x86_init.iommu.iommu_init = intel_iommu_init;//caq:pci_iommu_init 会统一执行iommu_init**
    #endif
    
    	if (dmar_tbl) {
    		acpi_put_table(dmar_tbl);//caq:释放资源
    		dmar_tbl = NULL;
    	}
    	up_write(&dmar_global_lock);
    
    	return ret ? ret : 1;
    }
    

    在 detect_intel_iommu 函数中,有一个关键行:

    x86_init.iommu.iommu_init = intel_iommu_init;//caq:pci_iommu_init 会统一执行iommu_init
    然后在 pci_iommu_init 会统一执行 iommu_init 。如下:

    static int __init pci_iommu_init(void)
    {
    	struct iommu_table_entry *p;
    
    	**x86_init.iommu.iommu_init();//caq:执行前面 detect_intel_iommu 里面对 x86_init.iommu 赋值的指针。**
    
    	for (p = __iommu_table; p < __iommu_table_end; p++) {
    		if (p && (p->flags & IOMMU_DETECTED) && p->late_init)
    			p->late_init();
    	}
    
    	return 0;
    }
    

    如下的调用链:

    kernel_init ->
    	kernel_init_freeable ->
    		do_one_initcall ->
    			pci_iommu_init ->
    				x86_init.iommu.iommu_init
    

    以上就是intel 的iommu 初始化调用链分析。

    常见问题:
    1、intel怎么管理那么多iommu硬件?
    使用一个全局的 dmar_drhd_units 链表来管理对应的iommu硬件,dmar_drhd_unit 的list 成员串接在这个全局链表中。

    crash> list -H dmar_drhd_units -s dmar_drhd_unit.segment,devices_cnt
    ffff8b83ff078780
      segment = 0
      devices_cnt = 0
    ffff8b83ff078060
      segment = 0
      devices_cnt = 0
    ffff8b83ff065680---------管理了三个设备
      segment = 0
      devices_cnt = 3
    ffff8b83ff078ae0
      segment = 0
      devices_cnt = 0
    ffff8b83ff078c60
      segment = 0
      devices_cnt = 0
    ffff8b83ff078480
      segment = 0
      devices_cnt = 0
    ffff8b83ff072180--------------管理了8个设备
      segment = 0
      devices_cnt = 8
    ffff8b83ff078240
      segment = 0
      devices_cnt = 0
    
    

    我们可以看到,bios/uefi至少枚举了8个iommu的硬件,其中两个intel_iommu设备 分别管理的3个和8个设备。
    但是需要注意的是,如果intel_iommu=on 没有设置,且 CONFIG_INTEL_IOMMU_DEFAULT_ON 这个config没有开启,则会导致 dmar_disabled 为1.

    #ifdef CONFIG_INTEL_IOMMU_DEFAULT_ON
    int dmar_disabled = 0;//caq:默认是否开启取决于 CONFIG_INTEL_IOMMU_DEFAULT_ON 是否配置
    #else
    int dmar_disabled = 1;
    #endif /*CONFIG_INTEL_IOMMU_DEFAULT_ON*/
    

    这个 dmar_disabled 的值会影响 int __init intel_iommu_init(void) 函数的行为,

    int __init intel_iommu_init(void)
    {
    .....
    if (no_iommu || dmar_disabled) {//caq: 
    		/*
    		 * We exit the function here to ensure IOMMU's remapping and
    		 * mempool aren't setup, which means that the IOMMU's PMRs
    		 * won't be disabled via the call to init_dmars(). So disable
    		 * it explicitly here. The PMRs were setup by tboot prior to
    		 * calling SENTER, but the kernel is expected to reset/tear
    		 * down the PMRs.
    		 */
    		if (intel_iommu_tboot_noforce) {
    			for_each_iommu(iommu, drhd)
    				iommu_disable_protect_mem_regions(iommu);
    		}
    
    		/*
    		 * Make sure the IOMMUs are switched off, even when we
    		 * boot into a kexec kernel and the previous kernel left
    		 * them enabled
    		 */
    		intel_disable_iommus();//caq:导致intel_iommu的功能被关闭,也就是调用 iommu_disable_translation
    		goto out_free_dmar;
    	}
    .....
    

    所以我们经常能看到硬件上是有很多drhd的硬件,但是软件上并没有使能,当然也就没有注册到 iommu框架中的,iommu_device_register不会被调用,
    全局的 iommu_device_list 只是空的,留下寂寞空虚冷。

    而如果软件上使能了的话,iommu_device_list 就会保留有当前期active的 iommu device,如下:

    crash> list -H iommu_device_list -s iommu_device.ops-----------//caq:注意,这个链表只有active的成员。
    ffff9994ff0709c8
      ops = 0xffffffff952ebcc0
    ffff99a53f043dc8
      ops = 0xffffffff952ebcc0
    crash> dis -l 0xffffffff952ebcc0
    0xffffffff952ebcc0 <intel_iommu_ops>:   adcb   $0xff,-0x6b72(%rdi)
    
    
    水平有限,如果有错误,请帮忙提醒我。如果您觉得本文对您有帮助,可以点击下面的 推荐 支持一下我。版权所有,需要转发请带上本文源地址,博客一直在更新,欢迎 关注 。
  • 相关阅读:
    stone brook Introduction to Mathematical Statistics
    mongodb python
    CodesBay
    人机交互的本质是画图
    Latex模版–Review and Response Letters
    mongo with python
    OpenStack架构学习与解析
    Python类方法
    python装饰器
    java.sql.SQLException: Io 异常: Connection reset by peer: socket write error
  • 原文地址:https://www.cnblogs.com/10087622blog/p/15494657.html
Copyright © 2011-2022 走看看