Commit 871d812a authored by Rob Clark's avatar Rob Clark
Browse files

drm/msm: add support for non-IOMMU systems



Add a VRAM carveout that is used for systems which do not have an IOMMU.

The VRAM carveout uses CMA.  The arch code must setup a CMA pool for the
device (preferrably in highmem.. a 256m-512m VRAM pool in lowmem is not
cool).  The user can configure the VRAM pool size using msm.vram module
param.

Technically, the abstraction of IOMMU behind msm_mmu is not strictly
needed, but it simplifies the GEM code a bit, and will be useful later
when I add support for a2xx devices with GPUMMU, so I decided to keep
this part.

It appears to be possible to configure the GPU to restrict access to
addresses within the VRAM pool, but this is not done yet.  So for now
the GPU will refuse to load if there is no sort of mmu.  Once address
based limits are supported and tested to confirm that we aren't giving
the GPU access to arbitrary memory, this restriction can be lifted

Signed-off-by: default avatarRob Clark <robdclark@gmail.com>
parent bf2b33af
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ msm-y := \
	msm_gem_prime.o \
	msm_gem_submit.o \
	msm_gpu.o \
	msm_iommu.o \
	msm_ringbuffer.o

msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o
+14 −1
Original line number Diff line number Diff line
@@ -426,7 +426,20 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
	if (ret)
		goto fail;

	return &a3xx_gpu->base.base;
	if (!gpu->mmu) {
		/* TODO we think it is possible to configure the GPU to
		 * restrict access to VRAM carveout.  But the required
		 * registers are unknown.  For now just bail out and
		 * limp along with just modesetting.  If it turns out
		 * to not be possible to restrict access, then we must
		 * implement a cmdstream validator.
		 */
		dev_err(dev->dev, "No memory protection without IOMMU\n");
		ret = -ENXIO;
		goto fail;
	}

	return gpu;

fail:
	if (a3xx_gpu)
+9 −4
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@

#include "adreno_gpu.h"
#include "msm_gem.h"
#include "msm_mmu.h"

struct adreno_info {
	struct adreno_rev rev;
@@ -291,6 +292,7 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
		struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs,
		struct adreno_rev rev)
{
	struct msm_mmu *mmu;
	int i, ret;

	/* identify gpu: */
@@ -338,10 +340,13 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
	if (ret)
		return ret;

	ret = msm_iommu_attach(drm, gpu->base.iommu,
			iommu_ports, ARRAY_SIZE(iommu_ports));
	mmu = gpu->base.mmu;
	if (mmu) {
		ret = mmu->funcs->attach(mmu, iommu_ports,
				ARRAY_SIZE(iommu_ports));
		if (ret)
			return ret;
	}

	gpu->memptrs_bo = msm_gem_new(drm, sizeof(*gpu->memptrs),
			MSM_BO_UNCACHED);
+18 −11
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@


#include "msm_drv.h"
#include "msm_mmu.h"
#include "mdp4_kms.h"

static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev);
@@ -260,6 +261,7 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)
	struct mdp4_platform_config *config = mdp4_get_config(pdev);
	struct mdp4_kms *mdp4_kms;
	struct msm_kms *kms = NULL;
	struct msm_mmu *mmu;
	int ret;

	mdp4_kms = kzalloc(sizeof(*mdp4_kms), GFP_KERNEL);
@@ -322,12 +324,6 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)
	clk_set_rate(mdp4_kms->clk, config->max_clk);
	clk_set_rate(mdp4_kms->lut_clk, config->max_clk);

	if (!config->iommu) {
		dev_err(dev->dev, "no iommu\n");
		ret = -ENXIO;
		goto fail;
	}

	/* make sure things are off before attaching iommu (bootloader could
	 * have left things on, in which case we'll start getting faults if
	 * we don't disable):
@@ -337,12 +333,23 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)
	mdp4_write(mdp4_kms, REG_MDP4_DSI_ENABLE, 0);
	mdelay(16);

	ret = msm_iommu_attach(dev, config->iommu,
			iommu_ports, ARRAY_SIZE(iommu_ports));
	if (config->iommu) {
		mmu = msm_iommu_new(dev, config->iommu);
		if (IS_ERR(mmu)) {
			ret = PTR_ERR(mmu);
			goto fail;
		}
		ret = mmu->funcs->attach(mmu, iommu_ports,
				ARRAY_SIZE(iommu_ports));
		if (ret)
			goto fail;
	} else {
		dev_info(dev->dev, "no iommu, fallback to phys "
				"contig buffers for scanout\n");
		mmu = NULL;
	}

	mdp4_kms->id = msm_register_iommu(dev, config->iommu);
	mdp4_kms->id = msm_register_mmu(dev, mmu);
	if (mdp4_kms->id < 0) {
		ret = mdp4_kms->id;
		dev_err(dev->dev, "failed to register mdp4 iommu: %d\n", ret);
+52 −35
Original line number Diff line number Diff line
@@ -30,50 +30,19 @@ static const struct drm_mode_config_funcs mode_config_funcs = {
	.output_poll_changed = msm_fb_output_poll_changed,
};

static int msm_fault_handler(struct iommu_domain *iommu, struct device *dev,
		unsigned long iova, int flags, void *arg)
{
	DBG("*** fault: iova=%08lx, flags=%d", iova, flags);
	return 0;
}

int msm_register_iommu(struct drm_device *dev, struct iommu_domain *iommu)
int msm_register_mmu(struct drm_device *dev, struct msm_mmu *mmu)
{
	struct msm_drm_private *priv = dev->dev_private;
	int idx = priv->num_iommus++;
	int idx = priv->num_mmus++;

	if (WARN_ON(idx >= ARRAY_SIZE(priv->iommus)))
	if (WARN_ON(idx >= ARRAY_SIZE(priv->mmus)))
		return -EINVAL;

	priv->iommus[idx] = iommu;

	iommu_set_fault_handler(iommu, msm_fault_handler, dev);

	/* need to iommu_attach_device() somewhere??  on resume?? */
	priv->mmus[idx] = mmu;

	return idx;
}

int msm_iommu_attach(struct drm_device *dev, struct iommu_domain *iommu,
		const char **names, int cnt)
{
	int i, ret;

	for (i = 0; i < cnt; i++) {
		/* TODO maybe some day msm iommu won't require this hack: */
		struct device *msm_iommu_get_ctx(const char *ctx_name);
		struct device *ctx = msm_iommu_get_ctx(names[i]);
		if (!ctx)
			continue;
		ret = iommu_attach_device(iommu, ctx);
		if (ret) {
			dev_warn(dev->dev, "could not attach iommu to %s", names[i]);
			return ret;
		}
	}
	return 0;
}

#ifdef CONFIG_DRM_MSM_REGISTER_LOGGING
static bool reglog = false;
MODULE_PARM_DESC(reglog, "Enable register read/write logging");
@@ -82,6 +51,10 @@ module_param(reglog, bool, 0600);
#define reglog 0
#endif

static char *vram;
MODULE_PARM_DESC(vram, "Configure VRAM size (for devices without IOMMU/GPUMMU");
module_param(vram, charp, 0);

void __iomem *msm_ioremap(struct platform_device *pdev, const char *name,
		const char *dbgname)
{
@@ -161,6 +134,14 @@ static int msm_unload(struct drm_device *dev)
		mutex_unlock(&dev->struct_mutex);
	}

	if (priv->vram.paddr) {
		DEFINE_DMA_ATTRS(attrs);
		dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs);
		drm_mm_takedown(&priv->vram.mm);
		dma_free_attrs(dev->dev, priv->vram.size, NULL,
				priv->vram.paddr, &attrs);
	}

	dev->dev_private = NULL;

	kfree(priv);
@@ -191,6 +172,41 @@ static int msm_load(struct drm_device *dev, unsigned long flags)

	drm_mode_config_init(dev);

	/* if we have no IOMMU, then we need to use carveout allocator.
	 * Grab the entire CMA chunk carved out in early startup in
	 * mach-msm:
	 */
	if (!iommu_present(&platform_bus_type)) {
		DEFINE_DMA_ATTRS(attrs);
		unsigned long size;
		void *p;

		DBG("using %s VRAM carveout", vram);
		size = memparse(vram, NULL);
		priv->vram.size = size;

		drm_mm_init(&priv->vram.mm, 0, (size >> PAGE_SHIFT) - 1);

		dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs);
		dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);

		/* note that for no-kernel-mapping, the vaddr returned
		 * is bogus, but non-null if allocation succeeded:
		 */
		p = dma_alloc_attrs(dev->dev, size,
				&priv->vram.paddr, 0, &attrs);
		if (!p) {
			dev_err(dev->dev, "failed to allocate VRAM\n");
			priv->vram.paddr = 0;
			ret = -ENOMEM;
			goto fail;
		}

		dev_info(dev->dev, "VRAM: %08x->%08x\n",
				(uint32_t)priv->vram.paddr,
				(uint32_t)(priv->vram.paddr + size));
	}

	kms = mdp4_kms_init(dev);
	if (IS_ERR(kms)) {
		/*
@@ -778,6 +794,7 @@ static const struct dev_pm_ops msm_pm_ops = {

static int msm_pdev_probe(struct platform_device *pdev)
{
	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
	return drm_platform_init(&msm_driver, pdev);
}

Loading