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

Commit 4952b4d3 authored by Ben Skeggs's avatar Ben Skeggs
Browse files

drm/nouveau/disp: audit and version SCANOUTPOS method



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 67cb49c4
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -45,7 +45,7 @@ gm107_disp_sclass[] = {

static struct nouveau_oclass
gm107_disp_base_oclass[] = {
	{ GM107_DISP_CLASS, &nvd0_disp_base_ofuncs, nvd0_disp_base_omthds },
	{ GM107_DISP_CLASS, &nvd0_disp_base_ofuncs },
	{}
};

@@ -99,4 +99,5 @@ gm107_disp_oclass = &(struct nv50_disp_impl) {
	.mthd.base = &nvd0_disp_sync_mthd_chan,
	.mthd.ovly = &nve0_disp_ovly_mthd_chan,
	.mthd.prev = -0x020000,
	.head.scanoutpos = nvd0_disp_base_scanoutpos,
}.base.base;
+75 −35
Original line number Diff line number Diff line
@@ -24,60 +24,100 @@

#include "priv.h"

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

struct nv04_disp_priv {
	struct nouveau_disp base;
};

static int
nv04_disp_scanoutpos(struct nouveau_object *object, u32 mthd,
		     void *data, u32 size)
nv04_disp_scanoutpos(struct nouveau_object *object, struct nv04_disp_priv *priv,
		     void *data, u32 size, int head)
{
	struct nv04_disp_priv *priv = (void *)object->engine;
	struct nv04_display_scanoutpos *args = data;
	const int head = (mthd & NV04_DISP_MTHD_HEAD);
	const u32 hoff = head * 0x2000;
	union {
		struct nv04_disp_scanoutpos_v0 v0;
	} *args = data;
	u32 line;
	int ret;

	if (size < sizeof(*args))
		return -EINVAL;

	args->vblanks = nv_rd32(priv, 0x680800 + (head * 0x2000)) & 0xffff;
	args->vtotal  = nv_rd32(priv, 0x680804 + (head * 0x2000)) & 0xffff;
	args->vblanke = args->vtotal - 1;
	nv_ioctl(object, "disp scanoutpos size %d\n", size);
	if (nvif_unpack(args->v0, 0, 0, false)) {
		nv_ioctl(object, "disp scanoutpos vers %d\n", args->v0.version);
		args->v0.vblanks = nv_rd32(priv, 0x680800 + hoff) & 0xffff;
		args->v0.vtotal  = nv_rd32(priv, 0x680804 + hoff) & 0xffff;
		args->v0.vblanke = args->v0.vtotal - 1;

	args->hblanks = nv_rd32(priv, 0x680820 + (head * 0x2000)) & 0xffff;
	args->htotal  = nv_rd32(priv, 0x680824 + (head * 0x2000)) & 0xffff;
	args->hblanke = args->htotal - 1;
		args->v0.hblanks = nv_rd32(priv, 0x680820 + hoff) & 0xffff;
		args->v0.htotal  = nv_rd32(priv, 0x680824 + hoff) & 0xffff;
		args->v0.hblanke = args->v0.htotal - 1;

		/*
	 * If output is vga instead of digital then vtotal/htotal is invalid
	 * so we have to give up and trigger the timestamping fallback in the
	 * drm core.
		 * If output is vga instead of digital then vtotal/htotal is
		 * invalid so we have to give up and trigger the timestamping
		 * fallback in the drm core.
		 */
	if (!args->vtotal || !args->htotal)
		if (!args->v0.vtotal || !args->v0.htotal)
			return -ENOTSUPP;

	args->time[0] = ktime_to_ns(ktime_get());
	line = nv_rd32(priv, 0x600868 + (head * 0x2000));
	args->time[1] = ktime_to_ns(ktime_get());
	args->hline = (line & 0xffff0000) >> 16;
	args->vline = (line & 0x0000ffff);
		args->v0.time[0] = ktime_to_ns(ktime_get());
		line = nv_rd32(priv, 0x600868 + hoff);
		args->v0.time[1] = ktime_to_ns(ktime_get());
		args->v0.hline = (line & 0xffff0000) >> 16;
		args->v0.vline = (line & 0x0000ffff);
	} else
		return ret;

	return 0;
}

#define HEAD_MTHD(n) (n), (n) + 0x01
static int
nv04_disp_mthd(struct nouveau_object *object, u32 mthd, void *data, u32 size)
{
	union {
		struct nv04_disp_mthd_v0 v0;
	} *args = data;
	struct nv04_disp_priv *priv = (void *)object->engine;
	int head, ret;

	nv_ioctl(object, "disp mthd size %d\n", size);
	if (nvif_unpack(args->v0, 0, 0, true)) {
		nv_ioctl(object, "disp mthd vers %d mthd %02x head %d\n",
			 args->v0.version, args->v0.method, args->v0.head);
		mthd = args->v0.method;
		head = args->v0.head;
	} else
		return ret;

	if (head < 0 || head >= 2)
		return -ENXIO;

	switch (mthd) {
	case NV04_DISP_SCANOUTPOS:
		return nv04_disp_scanoutpos(object, priv, data, size, head);
	default:
		break;
	}

	return -EINVAL;
}

static struct nouveau_omthds
nv04_disp_omthds[] = {
	{ HEAD_MTHD(NV04_DISP_SCANOUTPOS), nv04_disp_scanoutpos },
	{}
static struct nouveau_ofuncs
nv04_disp_ofuncs = {
	.ctor = _nouveau_object_ctor,
	.dtor = nouveau_object_destroy,
	.init = nouveau_object_init,
	.fini = nouveau_object_fini,
	.mthd = nv04_disp_mthd,
};

static struct nouveau_oclass
nv04_disp_sclass[] = {
	{ NV04_DISP_CLASS, &nouveau_object_ofuncs, nv04_disp_omthds },
	{ NV04_DISP_CLASS, &nv04_disp_ofuncs },
	{},
};

+31 −30
Original line number Diff line number Diff line
@@ -814,31 +814,34 @@ nv50_disp_curs_ofuncs = {
 ******************************************************************************/

int
nv50_disp_base_scanoutpos(struct nouveau_object *object, u32 mthd,
			  void *data, u32 size)
nv50_disp_base_scanoutpos(NV50_DISP_MTHD_V0)
{
	struct nv50_disp_priv *priv = (void *)object->engine;
	struct nv04_display_scanoutpos *args = data;
	const int head = (mthd & NV50_DISP_MTHD_HEAD);
	u32 blanke, blanks, total;
	const u32 blanke = nv_rd32(priv, 0x610aec + (head * 0x540));
	const u32 blanks = nv_rd32(priv, 0x610af4 + (head * 0x540));
	const u32 total  = nv_rd32(priv, 0x610afc + (head * 0x540));
	union {
		struct nv04_disp_scanoutpos_v0 v0;
	} *args = data;
	int ret;

	nv_ioctl(object, "disp scanoutpos size %d\n", size);
	if (nvif_unpack(args->v0, 0, 0, false)) {
		nv_ioctl(object, "disp scanoutpos vers %d\n", args->v0.version);
		args->v0.vblanke = (blanke & 0xffff0000) >> 16;
		args->v0.hblanke = (blanke & 0x0000ffff);
		args->v0.vblanks = (blanks & 0xffff0000) >> 16;
		args->v0.hblanks = (blanks & 0x0000ffff);
		args->v0.vtotal  = ( total & 0xffff0000) >> 16;
		args->v0.htotal  = ( total & 0x0000ffff);
		args->v0.time[0] = ktime_to_ns(ktime_get());
		args->v0.vline = /* vline read locks hline */
			nv_rd32(priv, 0x616340 + (head * 0x800)) & 0xffff;
		args->v0.time[1] = ktime_to_ns(ktime_get());
		args->v0.hline =
			nv_rd32(priv, 0x616344 + (head * 0x800)) & 0xffff;
	} else
		return ret;

	if (size < sizeof(*args) || head >= priv->head.nr)
		return -EINVAL;
	blanke = nv_rd32(priv, 0x610aec + (head * 0x540));
	blanks = nv_rd32(priv, 0x610af4 + (head * 0x540));
	total  = nv_rd32(priv, 0x610afc + (head * 0x540));

	args->vblanke = (blanke & 0xffff0000) >> 16;
	args->hblanke = (blanke & 0x0000ffff);
	args->vblanks = (blanks & 0xffff0000) >> 16;
	args->hblanks = (blanks & 0x0000ffff);
	args->vtotal  = ( total & 0xffff0000) >> 16;
	args->htotal  = ( total & 0x0000ffff);

	args->time[0] = ktime_to_ns(ktime_get());
	args->vline   = nv_rd32(priv, 0x616340 + (head * 0x800)) & 0xffff;
	args->time[1] = ktime_to_ns(ktime_get()); /* vline read locks hline */
	args->hline   = nv_rd32(priv, 0x616344 + (head * 0x800)) & 0xffff;
	return 0;
}

@@ -846,6 +849,7 @@ int
nv50_disp_base_mthd(struct nouveau_object *object, u32 mthd,
		    void *data, u32 size)
{
	const struct nv50_disp_impl *impl = (void *)nv_oclass(object->engine);
	union {
		struct nv50_disp_mthd_v0 v0;
		struct nv50_disp_mthd_v1 v1;
@@ -894,6 +898,8 @@ nv50_disp_base_mthd(struct nouveau_object *object, u32 mthd,
	}

	switch (mthd) {
	case NV50_DISP_SCANOUTPOS:
		return impl->head.scanoutpos(object, priv, data, size, head);
	default:
		break;
	}
@@ -1081,15 +1087,9 @@ nv50_disp_base_ofuncs = {
	.mthd = nv50_disp_base_mthd,
};

static struct nouveau_omthds
nv50_disp_base_omthds[] = {
	{ HEAD_MTHD(NV50_DISP_SCANOUTPOS)     , nv50_disp_base_scanoutpos },
	{},
};

static struct nouveau_oclass
nv50_disp_base_oclass[] = {
	{ NV50_DISP_CLASS, &nv50_disp_base_ofuncs, nv50_disp_base_omthds },
	{ NV50_DISP_CLASS, &nv50_disp_base_ofuncs },
	{}
};

@@ -1859,4 +1859,5 @@ nv50_disp_oclass = &(struct nv50_disp_impl) {
	.mthd.base = &nv50_disp_sync_mthd_chan,
	.mthd.ovly = &nv50_disp_ovly_mthd_chan,
	.mthd.prev = 0x000004,
	.head.scanoutpos = nv50_disp_base_scanoutpos,
}.base.base;
+14 −19
Original line number Diff line number Diff line
@@ -14,16 +14,6 @@
#include "outp.h"
#include "outpdp.h"

struct nv50_disp_impl {
	struct nouveau_disp_impl base;
	struct {
		const struct nv50_disp_mthd_chan *core;
		const struct nv50_disp_mthd_chan *base;
		const struct nv50_disp_mthd_chan *ovly;
		int prev;
	} mthd;
};

#define NV50_DISP_MTHD_ struct nouveau_object *object,                         \
	struct nv50_disp_priv *priv, void *data, u32 size
#define NV50_DISP_MTHD_V0 NV50_DISP_MTHD_, int head
@@ -58,18 +48,27 @@ struct nv50_disp_priv {
	} pior;
};

#define HEAD_MTHD(n) (n), (n) + 0x03
struct nv50_disp_impl {
	struct nouveau_disp_impl base;
	struct {
		const struct nv50_disp_mthd_chan *core;
		const struct nv50_disp_mthd_chan *base;
		const struct nv50_disp_mthd_chan *ovly;
		int prev;
	} mthd;
	struct {
		int (*scanoutpos)(NV50_DISP_MTHD_V0);
	} head;
};

int nv50_disp_base_scanoutpos(struct nouveau_object *, u32, void *, u32);
int nv50_disp_base_scanoutpos(NV50_DISP_MTHD_V0);
int nv50_disp_base_mthd(struct nouveau_object *, u32, void *, u32);

#define DAC_MTHD(n) (n), (n) + 0x03
int nvd0_disp_base_scanoutpos(NV50_DISP_MTHD_V0);

int nv50_dac_power(NV50_DISP_MTHD_V1);
int nv50_dac_sense(NV50_DISP_MTHD_V1);

#define SOR_MTHD(n) (n), (n) + 0x3f

int nva3_hda_eld(NV50_DISP_MTHD_V1);
int nvd0_hda_eld(NV50_DISP_MTHD_V1);

@@ -97,8 +96,6 @@ int nvd0_sor_dp_lnkctl(struct nv50_disp_priv *, int, int, int, u16, u16, u32,
int nvd0_sor_dp_drvctl(struct nv50_disp_priv *, int, int, int, u16, u16, u32,
		       struct dcb_output *);

#define PIOR_MTHD(n) (n), (n) + 0x03

int nv50_pior_power(NV50_DISP_MTHD_V1);

struct nv50_disp_base {
@@ -203,7 +200,6 @@ extern const struct nv50_disp_mthd_list nv84_disp_mast_mthd_dac;
extern const struct nv50_disp_mthd_list nv84_disp_mast_mthd_head;
extern const struct nv50_disp_mthd_chan nv84_disp_sync_mthd_chan;
extern const struct nv50_disp_mthd_chan nv84_disp_ovly_mthd_chan;
extern struct nouveau_omthds nv84_disp_base_omthds[];

extern const struct nv50_disp_mthd_chan nv94_disp_mast_mthd_chan;

@@ -217,7 +213,6 @@ extern struct nv50_disp_chan_impl nvd0_disp_ovly_ofuncs;
extern const struct nv50_disp_mthd_chan nvd0_disp_sync_mthd_chan;
extern struct nv50_disp_chan_impl nvd0_disp_oimm_ofuncs;
extern struct nv50_disp_chan_impl nvd0_disp_curs_ofuncs;
extern struct nouveau_omthds nvd0_disp_base_omthds[];
extern struct nouveau_ofuncs nvd0_disp_base_ofuncs;
extern struct nouveau_oclass nvd0_disp_cclass;
void nvd0_disp_intr_supervisor(struct work_struct *);
+2 −7
Original line number Diff line number Diff line
@@ -212,15 +212,9 @@ nv84_disp_sclass[] = {
	{}
};

struct nouveau_omthds
nv84_disp_base_omthds[] = {
	{ HEAD_MTHD(NV50_DISP_SCANOUTPOS)     , nv50_disp_base_scanoutpos },
	{},
};

static struct nouveau_oclass
nv84_disp_base_oclass[] = {
	{ NV84_DISP_CLASS, &nv50_disp_base_ofuncs, nv84_disp_base_omthds },
	{ NV84_DISP_CLASS, &nv50_disp_base_ofuncs },
	{}
};

@@ -274,4 +268,5 @@ nv84_disp_oclass = &(struct nv50_disp_impl) {
	.mthd.base = &nv84_disp_sync_mthd_chan,
	.mthd.ovly = &nv84_disp_ovly_mthd_chan,
	.mthd.prev = 0x000004,
	.head.scanoutpos = nv50_disp_base_scanoutpos,
}.base.base;
Loading