Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4acfd707 authored by Ben Skeggs's avatar Ben Skeggs
Browse files

drm/nouveau/dma: audit and version NV_DMA classes



The full object interfaces are about to be exposed to userspace, so we
need to check for any security-related issues and version the structs
to make it easier to handle any changes we may need in the future.

Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent b2c81703
Loading
Loading
Loading
Loading
+53 −20
Original line number Diff line number Diff line
@@ -23,9 +23,12 @@
 */

#include <core/object.h>
#include <core/class.h>
#include <core/client.h>
#include <nvif/unpack.h>
#include <nvif/class.h>

#include <subdev/fb.h>
#include <subdev/instmem.h>

#include "priv.h"

@@ -57,57 +60,87 @@ nvkm_dmaobj_create_(struct nouveau_object *parent,
		    struct nouveau_oclass *oclass, void **pdata, u32 *psize,
		    int length, void **pobject)
{
	struct nv_dma_class *args = *pdata;
	union {
		struct nv_dma_v0 v0;
	} *args = *pdata;
	struct nouveau_instmem *instmem = nouveau_instmem(parent);
	struct nouveau_client *client = nouveau_client(parent);
	struct nouveau_device *device = nv_device(parent);
	struct nouveau_fb *pfb = nouveau_fb(parent);
	struct nouveau_dmaobj *dmaobj;
	void *data = *pdata;
	u32 size = *psize;
	int ret;

	if (*psize < sizeof(*args))
		return -EINVAL;
	*pdata = &args->conf0;

	ret = nouveau_object_create_(parent, engine, oclass, 0, length, pobject);
	dmaobj = *pobject;
	if (ret)
		return ret;

	switch (args->flags & NV_DMA_TARGET_MASK) {
	case NV_DMA_TARGET_VM:
	nv_ioctl(parent, "create dma size %d\n", *psize);
	if (nvif_unpack(args->v0, 0, 0, true)) {
		nv_ioctl(parent, "create dma vers %d target %d access %d "
				 "start %016llx limit %016llx\n",
			 args->v0.version, args->v0.target, args->v0.access,
			 args->v0.start, args->v0.limit);
		dmaobj->target = args->v0.target;
		dmaobj->access = args->v0.access;
		dmaobj->start  = args->v0.start;
		dmaobj->limit  = args->v0.limit;
	} else
		return ret;

	*pdata = data;
	*psize = size;

	if (dmaobj->start > dmaobj->limit)
		return -EINVAL;

	switch (dmaobj->target) {
	case NV_DMA_V0_TARGET_VM:
		dmaobj->target = NV_MEM_TARGET_VM;
		break;
	case NV_DMA_TARGET_VRAM:
	case NV_DMA_V0_TARGET_VRAM:
		if (!client->super) {
			if (dmaobj->limit >= pfb->ram->size - instmem->reserved)
				return -EACCES;
			if (device->card_type >= NV_50)
				return -EACCES;
		}
		dmaobj->target = NV_MEM_TARGET_VRAM;
		break;
	case NV_DMA_TARGET_PCI:
	case NV_DMA_V0_TARGET_PCI:
		if (!client->super)
			return -EACCES;
		dmaobj->target = NV_MEM_TARGET_PCI;
		break;
	case NV_DMA_TARGET_PCI_US:
	case NV_DMA_TARGET_AGP:
	case NV_DMA_V0_TARGET_PCI_US:
	case NV_DMA_V0_TARGET_AGP:
		if (!client->super)
			return -EACCES;
		dmaobj->target = NV_MEM_TARGET_PCI_NOSNOOP;
		break;
	default:
		return -EINVAL;
	}

	switch (args->flags & NV_DMA_ACCESS_MASK) {
	case NV_DMA_ACCESS_VM:
	switch (dmaobj->access) {
	case NV_DMA_V0_ACCESS_VM:
		dmaobj->access = NV_MEM_ACCESS_VM;
		break;
	case NV_DMA_ACCESS_RD:
	case NV_DMA_V0_ACCESS_RD:
		dmaobj->access = NV_MEM_ACCESS_RO;
		break;
	case NV_DMA_ACCESS_WR:
	case NV_DMA_V0_ACCESS_WR:
		dmaobj->access = NV_MEM_ACCESS_WO;
		break;
	case NV_DMA_ACCESS_RDWR:
	case NV_DMA_V0_ACCESS_RDWR:
		dmaobj->access = NV_MEM_ACCESS_RW;
		break;
	default:
		return -EINVAL;
	}

	dmaobj->start = args->start;
	dmaobj->limit = args->limit;
	dmaobj->conf0 = args->conf0;
	return ret;
}

+5 −4
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@

#include <core/gpuobj.h>
#include <core/class.h>
#include <nvif/class.h>

#include <subdev/fb.h>
#include <subdev/vm/nv04.h>
@@ -94,7 +95,7 @@ nv04_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,

	ret = nvkm_dmaobj_create(parent, engine, oclass, &data, &size, &priv);
	*pobject = nv_object(priv);
	if (ret)
	if (ret || (ret = -ENOSYS, size))
		return ret;

	if (priv->base.target == NV_MEM_TARGET_VM) {
@@ -145,9 +146,9 @@ nv04_dmaobj_ofuncs = {

static struct nouveau_oclass
nv04_dmaeng_sclass[] = {
	{ NV_DMA_FROM_MEMORY_CLASS, &nv04_dmaobj_ofuncs },
	{ NV_DMA_TO_MEMORY_CLASS, &nv04_dmaobj_ofuncs },
	{ NV_DMA_IN_MEMORY_CLASS, &nv04_dmaobj_ofuncs },
	{ NV_DMA_FROM_MEMORY, &nv04_dmaobj_ofuncs },
	{ NV_DMA_TO_MEMORY, &nv04_dmaobj_ofuncs },
	{ NV_DMA_IN_MEMORY, &nv04_dmaobj_ofuncs },
	{}
};

+36 −20
Original line number Diff line number Diff line
@@ -22,8 +22,11 @@
 * Authors: Ben Skeggs
 */

#include <core/client.h>
#include <core/gpuobj.h>
#include <core/class.h>
#include <nvif/unpack.h>
#include <nvif/class.h>

#include <subdev/fb.h>

@@ -90,10 +93,11 @@ nv50_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
		 struct nouveau_object **pobject)
{
	struct nouveau_dmaeng *dmaeng = (void *)engine;
	struct nv50_dmaobj_priv *priv;
	union {
		u32 conf0;
		struct nv50_dma_v0 v0;
	} *args;
	struct nv50_dmaobj_priv *priv;
	u32 user, part, comp, kind;
	int ret;

	ret = nvkm_dmaobj_create(parent, engine, oclass, &data, &size, &priv);
@@ -102,24 +106,36 @@ nv50_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
		return ret;
	args = data;

	if (!(args->conf0 & NV50_DMA_CONF0_ENABLE)) {
		if (priv->base.target == NV_MEM_TARGET_VM) {
			args->conf0  = NV50_DMA_CONF0_PRIV_VM;
			args->conf0 |= NV50_DMA_CONF0_PART_VM;
			args->conf0 |= NV50_DMA_CONF0_COMP_VM;
			args->conf0 |= NV50_DMA_CONF0_TYPE_VM;
	nv_ioctl(parent, "create nv50 dma size %d\n", size);
	if (nvif_unpack(args->v0, 0, 0, false)) {
		nv_ioctl(parent, "create nv50 dma vers %d priv %d part %d "
				 "comp %d kind %02x\n", args->v0.version,
			 args->v0.priv, args->v0.part, args->v0.comp,
			 args->v0.kind);
		user = args->v0.priv;
		part = args->v0.part;
		comp = args->v0.comp;
		kind = args->v0.kind;
	} else
	if (size == 0) {
		if (priv->base.target != NV_MEM_TARGET_VM) {
			user = NV50_DMA_V0_PRIV_US;
			part = NV50_DMA_V0_PART_256;
			comp = NV50_DMA_V0_COMP_NONE;
			kind = NV50_DMA_V0_KIND_PITCH;
		} else {
			args->conf0  = NV50_DMA_CONF0_PRIV_US;
			args->conf0 |= NV50_DMA_CONF0_PART_256;
			args->conf0 |= NV50_DMA_CONF0_COMP_NONE;
			args->conf0 |= NV50_DMA_CONF0_TYPE_LINEAR;
		}
			user = NV50_DMA_V0_PRIV_VM;
			part = NV50_DMA_V0_PART_VM;
			comp = NV50_DMA_V0_COMP_VM;
			kind = NV50_DMA_V0_KIND_VM;
		}
	} else
		return ret;

	priv->flags0 |= (args->conf0 & NV50_DMA_CONF0_COMP) << 22;
	priv->flags0 |= (args->conf0 & NV50_DMA_CONF0_TYPE) << 22;
	priv->flags0 |= (args->conf0 & NV50_DMA_CONF0_PRIV);
	priv->flags5 |= (args->conf0 & NV50_DMA_CONF0_PART);
	if (user > 2 || part > 2 || comp > 3 || kind > 0x7f)
		return -EINVAL;
	priv->flags0 = (comp << 29) | (kind << 22) | (user << 20);
	priv->flags5 = (part << 16);

	switch (priv->base.target) {
	case NV_MEM_TARGET_VM:
@@ -165,9 +181,9 @@ nv50_dmaobj_ofuncs = {

static struct nouveau_oclass
nv50_dmaeng_sclass[] = {
	{ NV_DMA_FROM_MEMORY_CLASS, &nv50_dmaobj_ofuncs },
	{ NV_DMA_TO_MEMORY_CLASS, &nv50_dmaobj_ofuncs },
	{ NV_DMA_IN_MEMORY_CLASS, &nv50_dmaobj_ofuncs },
	{ NV_DMA_FROM_MEMORY, &nv50_dmaobj_ofuncs },
	{ NV_DMA_TO_MEMORY, &nv50_dmaobj_ofuncs },
	{ NV_DMA_IN_MEMORY, &nv50_dmaobj_ofuncs },
	{}
};

+31 −16
Original line number Diff line number Diff line
@@ -22,9 +22,12 @@
 * Authors: Ben Skeggs
 */

#include <core/client.h>
#include <core/device.h>
#include <core/gpuobj.h>
#include <core/class.h>
#include <nvif/unpack.h>
#include <nvif/class.h>

#include <subdev/fb.h>

@@ -76,10 +79,11 @@ nvc0_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
		 struct nouveau_object **pobject)
{
	struct nouveau_dmaeng *dmaeng = (void *)engine;
	struct nvc0_dmaobj_priv *priv;
	union {
		u32 conf0;
		struct gf100_dma_v0 v0;
	} *args;
	struct nvc0_dmaobj_priv *priv;
	u32 kind, user, unkn;
	int ret;

	ret = nvkm_dmaobj_create(parent, engine, oclass, &data, &size, &priv);
@@ -88,20 +92,31 @@ nvc0_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
		return ret;
	args = data;

	if (!(args->conf0 & NVC0_DMA_CONF0_ENABLE)) {
		if (priv->base.target == NV_MEM_TARGET_VM) {
			args->conf0  = NVC0_DMA_CONF0_PRIV_VM;
			args->conf0 |= NVC0_DMA_CONF0_TYPE_VM;
	nv_ioctl(parent, "create gf100 dma size %d\n", size);
	if (nvif_unpack(args->v0, 0, 0, false)) {
		nv_ioctl(parent, "create gf100 dma vers %d priv %d kind %02x\n",
			 args->v0.version, args->v0.priv, args->v0.kind);
		kind = args->v0.kind;
		user = args->v0.priv;
		unkn = 0;
	} else
	if (size == 0) {
		if (priv->base.target != NV_MEM_TARGET_VM) {
			kind = GF100_DMA_V0_KIND_PITCH;
			user = GF100_DMA_V0_PRIV_US;
			unkn = 2;
		} else {
			args->conf0  = NVC0_DMA_CONF0_PRIV_US;
			args->conf0 |= NVC0_DMA_CONF0_TYPE_LINEAR;
			args->conf0 |= 0x00020000;
		}
			kind = GF100_DMA_V0_KIND_VM;
			user = GF100_DMA_V0_PRIV_VM;
			unkn = 0;
		}
	} else
		return ret;

	priv->flags0 |= (args->conf0 & NVC0_DMA_CONF0_TYPE) << 22;
	priv->flags0 |= (args->conf0 & NVC0_DMA_CONF0_PRIV);
	priv->flags5 |= (args->conf0 & NVC0_DMA_CONF0_UNKN);
	if (user > 2)
		return -EINVAL;
	priv->flags0 |= (kind << 22) | (user << 20);
	priv->flags5 |= (unkn << 16);

	switch (priv->base.target) {
	case NV_MEM_TARGET_VM:
@@ -145,9 +160,9 @@ nvc0_dmaobj_ofuncs = {

static struct nouveau_oclass
nvc0_dmaeng_sclass[] = {
	{ NV_DMA_FROM_MEMORY_CLASS, &nvc0_dmaobj_ofuncs },
	{ NV_DMA_TO_MEMORY_CLASS, &nvc0_dmaobj_ofuncs },
	{ NV_DMA_IN_MEMORY_CLASS, &nvc0_dmaobj_ofuncs },
	{ NV_DMA_FROM_MEMORY, &nvc0_dmaobj_ofuncs },
	{ NV_DMA_TO_MEMORY, &nvc0_dmaobj_ofuncs },
	{ NV_DMA_IN_MEMORY, &nvc0_dmaobj_ofuncs },
	{}
};

+27 −14
Original line number Diff line number Diff line
@@ -22,9 +22,12 @@
 * Authors: Ben Skeggs
 */

#include <core/client.h>
#include <core/device.h>
#include <core/gpuobj.h>
#include <core/class.h>
#include <nvif/unpack.h>
#include <nvif/class.h>

#include <subdev/fb.h>

@@ -83,10 +86,11 @@ nvd0_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
		 struct nouveau_object **pobject)
{
	struct nouveau_dmaeng *dmaeng = (void *)engine;
	struct nvd0_dmaobj_priv *priv;
	union {
		u32 conf0;
		struct gf110_dma_v0 v0;
	} *args;
	struct nvd0_dmaobj_priv *priv;
	u32 kind, page;
	int ret;

	ret = nvkm_dmaobj_create(parent, engine, oclass, &data, &size, &priv);
@@ -95,18 +99,27 @@ nvd0_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
		return ret;
	args = data;

	if (!(args->conf0 & NVD0_DMA_CONF0_ENABLE)) {
		if (priv->base.target == NV_MEM_TARGET_VM) {
			args->conf0 |= NVD0_DMA_CONF0_TYPE_VM;
			args->conf0 |= NVD0_DMA_CONF0_PAGE_LP;
	nv_ioctl(parent, "create gf110 dma size %d\n", size);
	if (nvif_unpack(args->v0, 0, 0, false)) {
		nv_ioctl(parent, "create gf100 dma vers %d page %d kind %02x\n",
			 args->v0.version, args->v0.page, args->v0.kind);
		kind = args->v0.kind;
		page = args->v0.page;
	} else
	if (size == 0) {
		if (priv->base.target != NV_MEM_TARGET_VM) {
			kind = GF110_DMA_V0_KIND_PITCH;
			page = GF110_DMA_V0_PAGE_SP;
		} else {
			args->conf0 |= NVD0_DMA_CONF0_TYPE_LINEAR;
			args->conf0 |= NVD0_DMA_CONF0_PAGE_SP;
		}
			kind = GF110_DMA_V0_KIND_VM;
			page = GF110_DMA_V0_PAGE_LP;
		}
	} else
		return ret;

	priv->flags0 |= (args->conf0 & NVD0_DMA_CONF0_TYPE) << 20;
	priv->flags0 |= (args->conf0 & NVD0_DMA_CONF0_PAGE) >> 4;
	if (page > 1)
		return -EINVAL;
	priv->flags0 = (kind << 20) | (page << 6);

	switch (priv->base.target) {
	case NV_MEM_TARGET_VRAM:
@@ -138,9 +151,9 @@ nvd0_dmaobj_ofuncs = {

static struct nouveau_oclass
nvd0_dmaeng_sclass[] = {
	{ NV_DMA_FROM_MEMORY_CLASS, &nvd0_dmaobj_ofuncs },
	{ NV_DMA_TO_MEMORY_CLASS, &nvd0_dmaobj_ofuncs },
	{ NV_DMA_IN_MEMORY_CLASS, &nvd0_dmaobj_ofuncs },
	{ NV_DMA_FROM_MEMORY, &nvd0_dmaobj_ofuncs },
	{ NV_DMA_TO_MEMORY, &nvd0_dmaobj_ofuncs },
	{ NV_DMA_IN_MEMORY, &nvd0_dmaobj_ofuncs },
	{}
};

Loading