kgsl mmu

Posted bubbleben

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kgsl mmu相关的知识,希望对你有一定的参考价值。

&soc 
    ....
    kgsl_msm_iommu: qcom,kgsl-iommu@0x02CA0000 
        compatible = "qcom,kgsl-smmu-v2";

        reg = <0x02CA0000 0x10000>;
        /* CB5(ATOS) & CB5/6/7 are protected by HYP*/
        qcom,protect = <0xa0000 0xc000>;

        clocks = <&clock_gcc_GCC_GPU_CFG_AHB_CLK>,
            <&clock_gcc_GCC_DDRSS_GPU_AXI_CLK>,
            <&clock_gcc_GCC_GPU_MEMNOC_GFX_CLK>,    

        clock-names = "iface_clk", "mem_clk", "mem_iface_clk"; 

        qcom,secure_align_mask = <0xfff>;
        qcom,retention;
        qcom,hyp_secure_alloc;
        
        gfx3d_user: gfx3d_user 
            compatible = "qcom,smmu-kgsl-cb";
            label = "gfx3d_user";
            iommus = <&kgsl_smmu 0x0 0x401>;
            qcom,gpu-offset = <0xa8000>;
        ;

        gfx3d_secure: gfx3d_secure 
            compatible = "qcom,smmu-kgsl-cb";
            label = "gfx3d_secure";
            iommus = <&kgsl_smmu 0x2 0x400>;
        ;
    ;
    ....

1. kgsl_mmu_probe

int kgsl_mmu_probe(struct kgsl_device *device)

    // 
	struct kgsl_mmu *mmu = &device->mmu;
	int ret;

	/*
	 * Try to probe for the IOMMU and if it doesn't exist for some reason
	 * go for the NOMMU option instead
	 */
    // 2节
	ret = kgsl_iommu_probe(device);
	if (!ret || ret == -EPROBE_DEFER)
		return ret;

	mmu->mmu_ops = &kgsl_nommu_ops;
	mmu->type = KGSL_MMU_TYPE_NONE;
	return 0;

1.1 kgsl_mmu

/**
 * struct kgsl_mmu - Master definition for KGSL MMU devices
 * @flags: MMU device flags
 * @type: Type of MMU that is attached
 * @subtype: Sub Type of MMU that is attached
 * @defaultpagetable: Default pagetable object for the MMU
 * @securepagetable: Default secure pagetable object for the MMU
 * @mmu_ops: Function pointers for the MMU sub-type
 * @secured: True if the MMU needs to be secured
 * @feature: Static list of MMU features
 */
struct kgsl_mmu 
	unsigned long flags;
	enum kgsl_mmutype type;
	u32 subtype;
	struct kgsl_pagetable *defaultpagetable;
	struct kgsl_pagetable *securepagetable;
    // 1.1.1节
	const struct kgsl_mmu_ops *mmu_ops;
	bool secured;
	unsigned long features;
	/** @pfpolicy: The current pagefault policy for the device */
	unsigned long pfpolicy;
	/** mmu: Pointer to the IOMMU sub-device */
    // IOMMU子设备[见1.1.2节]
	struct kgsl_iommu iommu;
;

1.1.1 kgsl_mmu_ops

struct kgsl_mmu_ops 
	void (*mmu_close)(struct kgsl_mmu *mmu);
	int (*mmu_start)(struct kgsl_mmu *mmu);
	uint64_t (*mmu_get_current_ttbr0)(struct kgsl_mmu *mmu);
	void (*mmu_pagefault_resume)(struct kgsl_mmu *mmu, bool terminate);
	void (*mmu_clear_fsr)(struct kgsl_mmu *mmu);
	void (*mmu_enable_clk)(struct kgsl_mmu *mmu);
	void (*mmu_disable_clk)(struct kgsl_mmu *mmu);
	int (*mmu_set_pf_policy)(struct kgsl_mmu *mmu, unsigned long pf_policy);
	int (*mmu_init_pt)(struct kgsl_mmu *mmu, struct kgsl_pagetable *pt);
	struct kgsl_pagetable * (*mmu_getpagetable)(struct kgsl_mmu *mmu,
			unsigned long name);
	void (*mmu_map_global)(struct kgsl_mmu *mmu,
		struct kgsl_memdesc *memdesc, u32 padding);
;

1.1.2 kgsl_iommu

/*
 * struct kgsl_iommu - Structure holding iommu data for kgsl driver
 * @regbase: Virtual address of the IOMMU register base
 * @regstart: Physical address of the iommu registers
 * @regsize: Length of the iommu register region.
 * @setstate: Scratch GPU memory for IOMMU operations
 * @clk_enable_count: The ref count of clock enable calls
 * @clks: Array of pointers to IOMMU clocks
 * @smmu_info: smmu info used in a5xx preemption
 */
struct kgsl_iommu 
	/** @user_context: Container for the user iommu context */
	struct kgsl_iommu_context user_context;
	/** @secure_context: Container for the secure iommu context */
	struct kgsl_iommu_context secure_context;
	/** @lpac_context: Container for the LPAC iommu context */
	struct kgsl_iommu_context lpac_context;
	void __iomem *regbase;
	struct kgsl_memdesc *setstate;
	atomic_t clk_enable_count;
	struct clk_bulk_data *clks;
	int num_clks;
	struct kgsl_memdesc *smmu_info;
	/** @pdev: Pointer to the platform device for the IOMMU device */
	struct platform_device *pdev;
	/**
	 * @ppt_active: Set when the first per process pagetable is created.
	 * This is used to warn when global buffers are created that might not
	 * be mapped in all contexts
	 */
	bool ppt_active;
	/** @cb0_offset: Offset of context bank 0 from iommu register base */
	u32 cb0_offset;
	/** @pagesize: Size of each context bank register space */
	u32 pagesize;
	/** @cx_gdsc: CX GDSC handle in case the IOMMU needs it */
	struct regulator *cx_gdsc;
;

2. kgsl_iommu_probe

int kgsl_mmu_probe(struct kgsl_device *device)

	struct kgsl_mmu *mmu = &device->mmu;
	int ret;

	/*
	 * Try to probe for the IOMMU and if it doesn't exist for some reason
	 * go for the NOMMU option instead
	 */
    // 2.1
	ret = kgsl_iommu_probe(device);
	if (!ret || ret == -EPROBE_DEFER)
		return ret;

	mmu->mmu_ops = &kgsl_nommu_ops;
	mmu->type = KGSL_MMU_TYPE_NONE;
	return 0;

2.1 kgsl_iommu_probe

int kgsl_iommu_probe(struct kgsl_device *device)

	u32 val[2];
	int ret, i;
	struct kgsl_iommu *iommu = KGSL_IOMMU(device);
	struct platform_device *pdev;
	struct kgsl_mmu *mmu = &device->mmu;
	struct device_node *node;
	struct kgsl_global_memdesc *md;

    // 读取qcom,kgsl-smmu-v2节点
	node = of_find_compatible_node(NULL, NULL, "qcom,kgsl-smmu-v2");
	if (!node)
		return -ENODEV;

	/* Create a kmem cache for the pagetable address objects */
	if (!addr_entry_cache) 
        // 创建kgsl_iommu_addr_entry缓存
		addr_entry_cache = KMEM_CACHE(kgsl_iommu_addr_entry, 0);
		if (!addr_entry_cache) 
			ret = -ENOMEM;
			goto err;
		
	
    
    // 读reg节点
	ret = of_property_read_u32_array(node, "reg", val, 2);
	if (ret) 
		dev_err(device->dev,
			"%pOF: Unable to read KGSL IOMMU register range\\n",
			node);
		goto err;
	

    // 根据读取的reg值初始化kgsl_iommu的IOMMU寄存器的虚拟基地址
	iommu->regbase = devm_ioremap(&device->pdev->dev, val[0], val[1]);
	if (!iommu->regbase) 
		dev_err(&device->pdev->dev, "Couldn't map IOMMU registers\\n");
		ret = -ENOMEM;
		goto err;
	

	pdev = of_find_device_by_node(node);
	iommu->pdev = pdev;
	iommu->num_clks = 0;

    // 创建kgsl_iommu的clk_bulk_data数组
	iommu->clks = devm_kcalloc(&pdev->dev, ARRAY_SIZE(kgsl_iommu_clocks),
				sizeof(*iommu->clks), GFP_KERNEL);
	if (!iommu->clks) 
		platform_device_put(pdev);
		ret = -ENOMEM;
		goto err;
	

    // 解析clocks节点
	for (i = 0; i < ARRAY_SIZE(kgsl_iommu_clocks); i++) 
		struct clk *c;

		c = devm_clk_get(&device->pdev->dev, kgsl_iommu_clocks[i]);
		if (IS_ERR(c))
			continue;

		iommu->clks[iommu->num_clks].id = kgsl_iommu_clocks[i];
		iommu->clks[iommu->num_clks++].clk = c;
	

	/* Get the CX regulator if it is available */
	iommu->cx_gdsc = devm_regulator_get(&pdev->dev, "vddcx");

	set_bit(KGSL_MMU_PAGED, &mmu->features);

    // kgsl_mmu的类型为IOMMU
	mmu->type = KGSL_MMU_TYPE_IOMMU;
    // kgsl_mmu的kgsl_mmu_ops为kgsl_iommu_ops[见2.2节]
	mmu->mmu_ops = &kgsl_iommu_ops;

	/* Fill out the rest of the devices in the node */
	of_platform_populate(node, NULL, NULL, &pdev->dev);

	/* Peek at the phandle to set up configuration */
	kgsl_iommu_check_config(mmu, node);

	/* Probe the default pagetable */
    // 解析gfx3d_user节点[见2.3节]
	ret = iommu_probe_user_context(device, node);
	if (ret) 
		of_platform_depopulate(&pdev->dev);
		platform_device_put(pdev);
		goto err;
	

	/* Probe the secure pagetable (this is optional) */
    // 解析gfx3d_secure节点
	iommu_probe_secure_context(device, node);
	of_node_put(node);

	/* Map any globals that might have been created early */
	list_for_each_entry(md, &device->globals, node) 
		if (md->memdesc.flags & KGSL_MEMFLAGS_SECURE) 
			if (IS_ERR_OR_NULL(mmu->securepagetable))
				continue;

			kgsl_iommu_secure_map(mmu->securepagetable,
				&md->memdesc);
		 else
			kgsl_iommu_default_map(mmu->defaultpagetable,
				&md->memdesc);
	

	/* QDSS is supported only when QCOM_KGSL_QDSS_STM is enabled */
	if (IS_ENABLED(CONFIG_QCOM_KGSL_QDSS_STM))
		device->qdss_desc = kgsl_allocate_global_fixed(device,
					"qcom,gpu-qdss-stm", "gpu-qdss");

	device->qtimer_desc = kgsl_allocate_global_fixed(device,
		"qcom,gpu-timer", "gpu-qtimer");

	/*
	 * Only support VBOs on MMU500 hardware that supports the PRR
	 * marker register to ignore writes to the zero page
	 */
	if (mmu->subtype == KGSL_IOMMU_SMMU_V500) 
		/*
		 * We need to allocate a page because we need a known physical
		 * address to program in the PRR register but the hardware
		 * should intercept accesses to the page before they go to DDR
		 * so this should be mostly just a placeholder
		 */
		kgsl_vbo_zero_page = alloc_page(GFP_KERNEL | __GFP_ZERO |
			__GFP_NORETRY | __GFP_HIGHMEM);
		if (kgsl_vbo_zero_page)
			set_bit(KGSL_MMU_SUPPORT_VBO, &mmu->features);
	

	return 0;

err:
	kmem_cache_destroy(addr_entry_cache);
	addr_entry_cache = NULL;

	of_node_put(node);
	return ret;

2.2 kgsl_iommu_ops

static const struct kgsl_mmu_ops kgsl_iommu_ops = 
	.mmu_close = kgsl_iommu_close,
	.mmu_start = kgsl_iommu_start,
	.mmu_clear_fsr = kgsl_iommu_clear_fsr,
	.mmu_get_current_ttbr0 = kgsl_iommu_get_current_ttbr0,
	.mmu_enable_clk = kgsl_iommu_enable_clk,
	.mmu_disable_clk = kgsl_iommu_disable_clk,
	.mmu_set_pf_policy = kgsl_iommu_set_pf_policy,
	.mmu_pagefault_resume = kgsl_iommu_pagefault_resume,
    // 获取页表
	.mmu_getpagetable = kgsl_iommu_getpagetable,
	.mmu_map_global = kgsl_iommu_map_global,
;

2.2.1 kgsl_iommu_getpagetable

static struct kgsl_pagetable *kgsl_iommu_getpagetable(struct kgsl_mmu *mmu,
		unsigned long name)

	struct kgsl_pagetable *pt;

	/* If we already know the pagetable, return it */
	pt = kgsl_get_pagetable(name);
	if (pt)
		return pt;

	/* If io-pgtables are not in effect, just use the default pagetable */
	if (!test_bit(KGSL_MMU_IOPGTABLE, &mmu->features))
		return mmu->defaultpagetable;

    // 2.2.2
	pt = kgsl_iopgtbl_pagetable(mmu, name);

	/*
	 * If the io-pgtable allocation didn't work then fall back to the
	 * default pagetable for this cycle
	 */
	if (!pt)
		return mmu->defaultpagetable;

	return pt;

2.2.2 kgsl_iopgtbl_pagetable

static struct kgsl_pagetable *kgsl_iopgtbl_pagetable(struct kgsl_mmu *mmu, u32 name)

	struct kgsl_iommu *iommu = &mmu->iommu;
	struct kgsl_iommu_pt *pt;
	int ret;

	pt = kzalloc(sizeof(*pt), GFP_KERNEL);
	if (!pt)
		return ERR_PTR(-ENOMEM);

	kgsl_mmu_pagetable_init(mmu, &pt->base, name);

	pt->base.fault_addr = U64_MAX;
	pt->base.rbtree = RB_ROOT;
    // 设置kgsl_mmu_pt_ops[见2.2.3节]
	pt->base.pt_ops = &iopgtbl_pt_ops;

	if (test_bit(KGSL_MMU_64BIT, &mmu->features)) 
		pt->base.compat_va_start = KGSL_IOMMU_SVM_BASE32;
		pt->base.compat_va_end = KGSL_IOMMU_GLOBAL_MEM_BASE(mmu);
		pt->base.va_start = KGSL_IOMMU_VA_BASE64;
		pt->base.va_end = KGSL_IOMMU_VA_END64;

		if (is_compat_task()) 
			pt->base.svm_start = KGSL_IOMMU_SVM_BASE32;
			pt->base.svm_end = KGSL_IOMMU_GLOBAL_MEM_BASE(mmu);
		 else 
			pt->base.svm_start = KGSL_IOMMU_SVM_BASE64;
			pt->base.svm_end = KGSL_IOMMU_SVM_END64;
		

	 else 
		pt->base.va_start = KGSL_IOMMU_SVM_BASE32;

		if (mmu->secured)
			pt->base.va_end = KGSL_IOMMU_SECURE_BASE(mmu);
		else
			pt->base.va_end = KGSL_IOMMU_GLOBAL_MEM_BASE(mmu);

		pt->base.compat_va_start = pt->base.va_start;
		pt->base.compat_va_end = pt->base.va_end;
		pt->base.svm_start = KGSL_IOMMU_SVM_BASE32;
		pt->base.svm_end = KGSL_IOMMU_SVM_END32;
	

	ret = kgsl_iopgtbl_alloc(&iommu->user_context, pt);
	if (ret) 
		kfree(pt);
		return ERR_PTR(ret);
	

	kgsl_mmu_pagetable_add(mmu, &pt->base);
	return &pt->base;

2.2.3 iopgtbl_pt_ops

static const struct kgsl_mmu_pt_ops iopgtbl_pt_ops = 
	.mmu_map = kgsl_iopgtbl_map,
	.mmu_map_child = kgsl_iopgtbl_map_child,
	.mmu_map_zero_page_to_range = kgsl_iopgtbl_map_zero_page_to_range,
	.mmu_unmap = kgsl_iopgtbl_unmap,
	.mmu_unmap_range = kgsl_iopgtbl_unmap_range,
	.mmu_destroy_pagetable = kgsl_iommu_destroy_pagetable,
	.get_ttbr0 = kgsl_iommu_get_ttbr0,
	.get_context_bank = kgsl_iommu_get_context_bank,
    // 获取GPU虚拟地址
	.get_gpuaddr = kgsl_iommu_get_gpuaddr,
	.put_gpuaddr = kgsl_iommu_put_gpuaddr,
	.set_svm_region = kgsl_iommu_set_svm_region,
	.find_svm_region = kgsl_iommu_find_svm_region,
	.svm_range = kgsl_iommu_svm_range,
	.addr_in_range = kgsl_iommu_addr_in_range,
;

2.3 iommu_probe_user_context

static int iommu_probe_user_context(struct kgsl_device *device,
		struct device_node *node)

	struct kgsl_iommu *iommu = KGSL_IOMMU(device);
	struct kgsl_mmu *mmu = &device->mmu;
	int ret;

    // 初始化kgsl_iommu的kgsl_iommu_context
	ret = kgsl_iommu_setup_context(mmu, node, &iommu->user_context,
		"gfx3d_user", kgsl_iommu_default_fault_handler);
	if (ret)
		return ret;

	/* LPAC is optional so don't worry if it returns error */
	kgsl_iommu_setup_context(mmu, node, &iommu->lpac_context,
		"gfx3d_lpac", kgsl_iommu_lpac_fault_handler);

	/*
	 * FIXME: If adreno_smmu->cookie wasn't initialized then we can't do
	 * IOPGTABLE
	 */

	/* Make the default pagetable */
    // 设置kgsl_mmu的默认页表[见2.3.1节]
	mmu->defaultpagetable = kgsl_iommu_default_pagetable(mmu);
	if (IS_ERR(mmu->defaultpagetable))
		return PTR_ERR(mmu->defaultpagetable);

	/* If IOPGTABLE isn't enabled then we are done */
	if (!test_bit(KGSL_MMU_IOPGTABLE, &mmu->features))
		return 0;

	/* Enable TTBR0 on the default and LPAC contexts */
	kgsl_iommu_enable_ttbr0(&

以上是关于kgsl mmu的主要内容,如果未能解决你的问题,请参考以下文章

深入剖析mmu地址转化

kgsl pool

kgsl_ioctl_gpu_command

kgsl_ioctl_gpumem_alloc

kgsl_ioctl_drawctxt_create

adreno源码系列打开kgsl