From bf0eb89859439f016a1fab4cdf737c95f36dbb2e Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 10 Aug 2014 04:10:26 +1000 Subject: drm/nv50-/disp: audit and version DAC_PWR 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: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c | 34 +++++++--- drivers/gpu/drm/nouveau/core/engine/disp/nv50.c | 73 +++++++++++++++++++++- drivers/gpu/drm/nouveau/core/engine/disp/nv50.h | 10 ++- drivers/gpu/drm/nouveau/core/engine/disp/nv84.c | 1 - drivers/gpu/drm/nouveau/core/engine/disp/nv94.c | 1 - drivers/gpu/drm/nouveau/core/engine/disp/nva3.c | 1 - drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c | 2 +- drivers/gpu/drm/nouveau/core/engine/disp/outp.c | 1 + drivers/gpu/drm/nouveau/core/engine/disp/outp.h | 1 + drivers/gpu/drm/nouveau/core/include/core/class.h | 14 ----- drivers/gpu/drm/nouveau/nouveau_bios.c | 7 ++- drivers/gpu/drm/nouveau/nv50_display.c | 24 ++++--- drivers/gpu/drm/nouveau/nvif/class.h | 43 +++++++++++++ 13 files changed, 171 insertions(+), 41 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c index a66b27c0fcab..9fb1a8438b56 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c @@ -22,8 +22,10 @@ * Authors: Ben Skeggs */ -#include +#include #include +#include +#include #include #include @@ -32,13 +34,28 @@ #include "nv50.h" int -nv50_dac_power(struct nv50_disp_priv *priv, int or, u32 data) +nv50_dac_power(NV50_DISP_MTHD_V1) { - const u32 stat = (data & NV50_DISP_DAC_PWR_HSYNC) | - (data & NV50_DISP_DAC_PWR_VSYNC) | - (data & NV50_DISP_DAC_PWR_DATA) | - (data & NV50_DISP_DAC_PWR_STATE); - const u32 doff = (or * 0x800); + const u32 doff = outp->or * 0x800; + union { + struct nv50_disp_dac_pwr_v0 v0; + } *args = data; + u32 stat; + int ret; + + nv_ioctl(object, "disp dac pwr size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "disp dac pwr vers %d state %d data %d " + "vsync %d hsync %d\n", + args->v0.version, args->v0.state, args->v0.data, + args->v0.vsync, args->v0.hsync); + stat = 0x00000040 * !args->v0.state; + stat |= 0x00000010 * !args->v0.data; + stat |= 0x00000004 * !args->v0.vsync; + stat |= 0x00000001 * !args->v0.hsync; + } else + return ret; + nv_wait(priv, 0x61a004 + doff, 0x80000000, 0x00000000); nv_mask(priv, 0x61a004 + doff, 0xc000007f, 0x80000000 | stat); nv_wait(priv, 0x61a004 + doff, 0x80000000, 0x00000000); @@ -80,9 +97,6 @@ nv50_dac_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size) return -EINVAL; switch (mthd & ~0x3f) { - case NV50_DISP_DAC_PWR: - ret = priv->dac.power(priv, or, data[0]); - break; case NV50_DISP_DAC_LOAD: ret = priv->dac.sense(priv, or, data[0]); if (ret >= 0) { diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index 4f1ed8928c82..00d1e34cb2a6 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c @@ -23,10 +23,13 @@ */ #include +#include #include #include -#include #include +#include +#include +#include #include #include @@ -839,6 +842,72 @@ nv50_disp_base_scanoutpos(struct nouveau_object *object, u32 mthd, return 0; } +int +nv50_disp_base_mthd(struct nouveau_object *object, u32 mthd, + void *data, u32 size) +{ + union { + struct nv50_disp_mthd_v0 v0; + struct nv50_disp_mthd_v1 v1; + } *args = data; + struct nv50_disp_priv *priv = (void *)object->engine; + struct nvkm_output *outp = NULL; + struct nvkm_output *temp; + u16 type, mask = 0; + int head, ret; + + if (mthd != NV50_DISP_MTHD) + return -EINVAL; + + 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 + if (nvif_unpack(args->v1, 1, 1, true)) { + nv_ioctl(object, "disp mthd vers %d mthd %02x " + "type %04x mask %04x\n", + args->v1.version, args->v1.method, + args->v1.hasht, args->v1.hashm); + mthd = args->v1.method; + type = args->v1.hasht; + mask = args->v1.hashm; + head = ffs((mask >> 8) & 0x0f) - 1; + } else + return ret; + + if (head < 0 || head >= priv->head.nr) + return -ENXIO; + + if (mask) { + list_for_each_entry(temp, &priv->base.outp, head) { + if ((temp->info.hasht == type) && + (temp->info.hashm & mask) == mask) { + outp = temp; + break; + } + } + if (outp == NULL) + return -ENXIO; + } + + switch (mthd) { + default: + break; + } + + switch (mthd * !!outp) { + case NV50_DISP_MTHD_V1_DAC_PWR: + return priv->dac.power(object, priv, data, size, head, outp); + default: + break; + } + + return -EINVAL; +} + int nv50_disp_base_ctor(struct nouveau_object *parent, struct nouveau_object *engine, @@ -954,6 +1023,7 @@ nv50_disp_base_ofuncs = { .dtor = nv50_disp_base_dtor, .init = nv50_disp_base_init, .fini = nv50_disp_base_fini, + .mthd = nv50_disp_base_mthd, }; static struct nouveau_omthds @@ -961,7 +1031,6 @@ nv50_disp_base_omthds[] = { { HEAD_MTHD(NV50_DISP_SCANOUTPOS) , nv50_disp_base_scanoutpos }, { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, - { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd }, { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd }, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h index a1ec6a556187..4a874d776368 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h @@ -24,6 +24,11 @@ struct nv50_disp_impl { } 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 +#define NV50_DISP_MTHD_V1 NV50_DISP_MTHD_, int head, struct nvkm_output *outp + struct nv50_disp_priv { struct nouveau_disp base; struct nouveau_oclass *sclass; @@ -36,7 +41,7 @@ struct nv50_disp_priv { } head; struct { int nr; - int (*power)(struct nv50_disp_priv *, int dac, u32 data); + int (*power)(NV50_DISP_MTHD_V1); int (*sense)(struct nv50_disp_priv *, int dac, u32 load); } dac; struct { @@ -56,11 +61,12 @@ struct nv50_disp_priv { #define HEAD_MTHD(n) (n), (n) + 0x03 int nv50_disp_base_scanoutpos(struct nouveau_object *, u32, void *, u32); +int nv50_disp_base_mthd(struct nouveau_object *, u32, void *, u32); #define DAC_MTHD(n) (n), (n) + 0x03 int nv50_dac_mthd(struct nouveau_object *, u32, void *, u32); -int nv50_dac_power(struct nv50_disp_priv *, int, u32); +int nv50_dac_power(NV50_DISP_MTHD_V1); int nv50_dac_sense(struct nv50_disp_priv *, int, u32); #define SOR_MTHD(n) (n), (n) + 0x3f diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c index 43ef1fab52f1..f2414a4bc000 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c @@ -218,7 +218,6 @@ nv84_disp_base_omthds[] = { { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, - { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd }, { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd }, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c index 2e38a79b19ca..7ae79cfbdbf4 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c @@ -78,7 +78,6 @@ nv94_disp_base_omthds[] = { { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, { SOR_MTHD(NV94_DISP_SOR_DP_PWR) , nv50_sor_mthd }, - { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd }, { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd }, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c index 4b601c87c318..31cdf7a612c4 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c @@ -51,7 +51,6 @@ nva3_disp_base_omthds[] = { { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, { SOR_MTHD(NV94_DISP_SOR_DP_PWR) , nv50_sor_mthd }, - { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd }, { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd }, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c index 57d6cb3ed0f7..66959cde0403 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c @@ -706,6 +706,7 @@ nvd0_disp_base_ofuncs = { .dtor = nv50_disp_base_dtor, .init = nvd0_disp_base_init, .fini = nvd0_disp_base_fini, + .mthd = nv50_disp_base_mthd, }; struct nouveau_omthds @@ -716,7 +717,6 @@ nvd0_disp_base_omthds[] = { { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, { SOR_MTHD(NV94_DISP_SOR_DP_PWR) , nv50_sor_mthd }, - { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, { PIOR_MTHD(NV50_DISP_PIOR_PWR) , nv50_pior_mthd }, { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR) , nv50_pior_mthd }, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outp.c b/drivers/gpu/drm/nouveau/core/engine/disp/outp.c index ad9ba7ccec7f..a5ff00a9cedc 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/outp.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/outp.c @@ -78,6 +78,7 @@ nvkm_output_create_(struct nouveau_object *parent, outp->info = *dcbE; outp->index = index; + outp->or = ffs(outp->info.or) - 1; DBG("type %02x loc %d or %d link %d con %x edid %x bus %d head %x\n", dcbE->type, dcbE->location, dcbE->or, dcbE->type >= 2 ? diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/outp.h b/drivers/gpu/drm/nouveau/core/engine/disp/outp.h index bc76fbf85710..187f435ad0e2 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/outp.h +++ b/drivers/gpu/drm/nouveau/core/engine/disp/outp.h @@ -9,6 +9,7 @@ struct nvkm_output { struct dcb_output info; int index; + int or; struct nouveau_i2c_port *port; struct nouveau_i2c_port *edid; diff --git a/drivers/gpu/drm/nouveau/core/include/core/class.h b/drivers/gpu/drm/nouveau/core/include/core/class.h index d3180e51ce62..026b123d1422 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/class.h +++ b/drivers/gpu/drm/nouveau/core/include/core/class.h @@ -49,7 +49,6 @@ struct nv04_display_scanoutpos { #define NVF0_DISP_CLASS 0x00009270 #define GM107_DISP_CLASS 0x00009470 -#define NV50_DISP_MTHD 0x00000000 #define NV50_DISP_MTHD_HEAD 0x00000003 #define NV50_DISP_SCANOUTPOS 0x00000000 @@ -82,19 +81,6 @@ struct nv04_display_scanoutpos { #define NV50_DISP_DAC_MTHD_TYPE 0x0000f000 #define NV50_DISP_DAC_MTHD_OR 0x00000003 -#define NV50_DISP_DAC_PWR 0x00020000 -#define NV50_DISP_DAC_PWR_HSYNC 0x00000001 -#define NV50_DISP_DAC_PWR_HSYNC_ON 0x00000000 -#define NV50_DISP_DAC_PWR_HSYNC_LO 0x00000001 -#define NV50_DISP_DAC_PWR_VSYNC 0x00000004 -#define NV50_DISP_DAC_PWR_VSYNC_ON 0x00000000 -#define NV50_DISP_DAC_PWR_VSYNC_LO 0x00000004 -#define NV50_DISP_DAC_PWR_DATA 0x00000010 -#define NV50_DISP_DAC_PWR_DATA_ON 0x00000000 -#define NV50_DISP_DAC_PWR_DATA_LO 0x00000010 -#define NV50_DISP_DAC_PWR_STATE 0x00000040 -#define NV50_DISP_DAC_PWR_STATE_ON 0x00000000 -#define NV50_DISP_DAC_PWR_STATE_OFF 0x00000040 #define NV50_DISP_DAC_LOAD 0x00020100 #define NV50_DISP_DAC_LOAD_VALUE 0x00000007 diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index ffdf85f7814e..dae2c96deef8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -1397,6 +1397,7 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, uint32_t conn, uint32_t conf, struct dcb_output *entry) { struct nouveau_drm *drm = nouveau_drm(dev); + int link = 0; entry->type = conn & 0xf; entry->i2c_index = (conn >> 4) & 0xf; @@ -1442,6 +1443,7 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, if (conf & 0x4) entry->lvdsconf.use_power_scripts = true; entry->lvdsconf.sor.link = (conf & 0x00000030) >> 4; + link = entry->lvdsconf.sor.link; } if (conf & mask) { /* @@ -1490,17 +1492,18 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, entry->dpconf.link_nr = 1; break; } + link = entry->dpconf.sor.link; break; case DCB_OUTPUT_TMDS: if (dcb->version >= 0x40) { entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4; entry->extdev = (conf & 0x0000ff00) >> 8; + link = entry->tmdsconf.sor.link; } else if (dcb->version >= 0x30) entry->tmdsconf.slave_addr = (conf & 0x00000700) >> 8; else if (dcb->version >= 0x22) entry->tmdsconf.slave_addr = (conf & 0x00000070) >> 4; - break; case DCB_OUTPUT_EOL: /* weird g80 mobile type that "nv" treats as a terminator */ @@ -1524,6 +1527,8 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, if (conf & 0x100000) entry->i2c_upper_default = true; + entry->hasht = (entry->location << 4) | entry->type; + entry->hashm = (entry->heads << 8) | (link << 6) | entry->or; return true; } diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 2d9a02c4b87e..d3e6bd33670e 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -1466,16 +1466,24 @@ nv50_dac_dpms(struct drm_encoder *encoder, int mode) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nv50_disp *disp = nv50_disp(encoder->dev); - int or = nv_encoder->or; - u32 dpms_ctrl; + struct { + struct nv50_disp_mthd_v1 base; + struct nv50_disp_dac_pwr_v0 pwr; + } args = { + .base.version = 1, + .base.method = NV50_DISP_MTHD_V1_DAC_PWR, + .base.hasht = nv_encoder->dcb->hasht, + .base.hashm = nv_encoder->dcb->hashm, + .pwr.state = 1, + .pwr.data = 1, + .pwr.vsync = (mode != DRM_MODE_DPMS_SUSPEND && + mode != DRM_MODE_DPMS_OFF), + .pwr.hsync = (mode != DRM_MODE_DPMS_STANDBY && + mode != DRM_MODE_DPMS_OFF), + }; - dpms_ctrl = 0x00000000; - if (mode == DRM_MODE_DPMS_STANDBY || mode == DRM_MODE_DPMS_OFF) - dpms_ctrl |= 0x00000001; - if (mode == DRM_MODE_DPMS_SUSPEND || mode == DRM_MODE_DPMS_OFF) - dpms_ctrl |= 0x00000004; - nvif_exec(disp->disp, NV50_DISP_DAC_PWR + or, &dpms_ctrl, sizeof(dpms_ctrl)); + nvif_mthd(disp->disp, 0, &args, sizeof(args)); } static bool diff --git a/drivers/gpu/drm/nouveau/nvif/class.h b/drivers/gpu/drm/nouveau/nvif/class.h index 50c5413ff523..9681a102970d 100644 --- a/drivers/gpu/drm/nouveau/nvif/class.h +++ b/drivers/gpu/drm/nouveau/nvif/class.h @@ -290,4 +290,47 @@ struct kepler_channel_gpfifo_a_v0 { __u64 ioffset; }; +/******************************************************************************* + * legacy display + ******************************************************************************/ + + +/******************************************************************************* + * display + ******************************************************************************/ + +#define NV50_DISP_MTHD 0x00 + +struct nv50_disp_mthd_v0 { + __u8 version; + __u8 method; + __u8 head; + __u8 pad03[5]; +}; + +struct nv50_disp_mthd_v1 { + __u8 version; +#define NV50_DISP_MTHD_V1_DAC_PWR 0x10 +#define NV50_DISP_MTHD_V1_DAC_LOAD 0x11 +#define NV50_DISP_MTHD_V1_SOR_PWR 0x20 +#define NV50_DISP_MTHD_V1_SOR_HDA_ELD 0x21 +#define NV50_DISP_MTHD_V1_SOR_HDMI_PWR 0x22 +#define NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT 0x23 +#define NV50_DISP_MTHD_V1_SOR_DP_PWR 0x24 +#define NV50_DISP_MTHD_V1_PIOR_PWR 0x30 + __u8 method; + __u16 hasht; + __u16 hashm; + __u8 pad06[2]; +}; + +struct nv50_disp_dac_pwr_v0 { + __u8 version; + __u8 state; + __u8 data; + __u8 vsync; + __u8 hsync; + __u8 pad05[3]; +}; + #endif -- cgit v1.2.3