cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

nv50.c (6313B)


      1/*
      2 * Copyright 2011 Red Hat Inc.
      3 *
      4 * Permission is hereby granted, free of charge, to any person obtaining a
      5 * copy of this software and associated documentation files (the "Software"),
      6 * to deal in the Software without restriction, including without limitation
      7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8 * and/or sell copies of the Software, and to permit persons to whom the
      9 * Software is furnished to do so, subject to the following conditions:
     10 *
     11 * The above copyright notice and this permission notice shall be included in
     12 * all copies or substantial portions of the Software.
     13 *
     14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     20 * OTHER DEALINGS IN THE SOFTWARE.
     21 *
     22 * Authors: Ben Skeggs
     23 */
     24#include "mxms.h"
     25
     26#include <subdev/bios.h>
     27#include <subdev/bios/conn.h>
     28#include <subdev/bios/dcb.h>
     29#include <subdev/bios/mxm.h>
     30
     31struct context {
     32	u32 *outp;
     33	struct mxms_odev desc;
     34};
     35
     36static bool
     37mxm_match_tmds_partner(struct nvkm_mxm *mxm, u8 *data, void *info)
     38{
     39	struct context *ctx = info;
     40	struct mxms_odev desc;
     41
     42	mxms_output_device(mxm, data, &desc);
     43	if (desc.outp_type == 2 &&
     44	    desc.dig_conn == ctx->desc.dig_conn)
     45		return false;
     46	return true;
     47}
     48
     49static bool
     50mxm_match_dcb(struct nvkm_mxm *mxm, u8 *data, void *info)
     51{
     52	struct nvkm_bios *bios = mxm->subdev.device->bios;
     53	struct context *ctx = info;
     54	u64 desc = *(u64 *)data;
     55
     56	mxms_output_device(mxm, data, &ctx->desc);
     57
     58	/* match dcb encoder type to mxm-ods device type */
     59	if ((ctx->outp[0] & 0x0000000f) != ctx->desc.outp_type)
     60		return true;
     61
     62	/* digital output, have some extra stuff to match here, there's a
     63	 * table in the vbios that provides a mapping from the mxm digital
     64	 * connection enum values to SOR/link
     65	 */
     66	if ((desc & 0x00000000000000f0) >= 0x20) {
     67		/* check against sor index */
     68		u8 link = mxm_sor_map(bios, ctx->desc.dig_conn);
     69		if ((ctx->outp[0] & 0x0f000000) != (link & 0x0f) << 24)
     70			return true;
     71
     72		/* check dcb entry has a compatible link field */
     73		link = (link & 0x30) >> 4;
     74		if ((link & ((ctx->outp[1] & 0x00000030) >> 4)) != link)
     75			return true;
     76	}
     77
     78	/* mark this descriptor accounted for by setting invalid device type,
     79	 * except of course some manufactures don't follow specs properly and
     80	 * we need to avoid killing off the TMDS function on DP connectors
     81	 * if MXM-SIS is missing an entry for it.
     82	 */
     83	data[0] &= ~0xf0;
     84	if (ctx->desc.outp_type == 6 && ctx->desc.conn_type == 6 &&
     85	    mxms_foreach(mxm, 0x01, mxm_match_tmds_partner, ctx)) {
     86		data[0] |= 0x20; /* modify descriptor to match TMDS now */
     87	} else {
     88		data[0] |= 0xf0;
     89	}
     90
     91	return false;
     92}
     93
     94static int
     95mxm_dcb_sanitise_entry(struct nvkm_bios *bios, void *data, int idx, u16 pdcb)
     96{
     97	struct nvkm_mxm *mxm = data;
     98	struct context ctx = { .outp = (u32 *)(bios->data + pdcb) };
     99	u8 type, i2cidx, link, ver, len;
    100	u8 *conn;
    101
    102	/* look for an output device structure that matches this dcb entry.
    103	 * if one isn't found, disable it.
    104	 */
    105	if (mxms_foreach(mxm, 0x01, mxm_match_dcb, &ctx)) {
    106		nvkm_debug(&mxm->subdev, "disable %d: %08x %08x\n",
    107			   idx, ctx.outp[0], ctx.outp[1]);
    108		ctx.outp[0] |= 0x0000000f;
    109		return 0;
    110	}
    111
    112	/* modify the output's ddc/aux port, there's a pointer to a table
    113	 * with the mapping from mxm ddc/aux port to dcb i2c_index in the
    114	 * vbios mxm table
    115	 */
    116	i2cidx = mxm_ddc_map(bios, ctx.desc.ddc_port);
    117	if ((ctx.outp[0] & 0x0000000f) != DCB_OUTPUT_DP)
    118		i2cidx = (i2cidx & 0x0f) << 4;
    119	else
    120		i2cidx = (i2cidx & 0xf0);
    121
    122	if (i2cidx != 0xf0) {
    123		ctx.outp[0] &= ~0x000000f0;
    124		ctx.outp[0] |= i2cidx;
    125	}
    126
    127	/* override dcb sorconf.link, based on what mxm data says */
    128	switch (ctx.desc.outp_type) {
    129	case 0x00: /* Analog CRT */
    130	case 0x01: /* Analog TV/HDTV */
    131		break;
    132	default:
    133		link = mxm_sor_map(bios, ctx.desc.dig_conn) & 0x30;
    134		ctx.outp[1] &= ~0x00000030;
    135		ctx.outp[1] |= link;
    136		break;
    137	}
    138
    139	/* we may need to fixup various other vbios tables based on what
    140	 * the descriptor says the connector type should be.
    141	 *
    142	 * in a lot of cases, the vbios tables will claim DVI-I is possible,
    143	 * and the mxm data says the connector is really HDMI.  another
    144	 * common example is DP->eDP.
    145	 */
    146	conn  = bios->data;
    147	conn += nvbios_connEe(bios, (ctx.outp[0] & 0x0000f000) >> 12, &ver, &len);
    148	type  = conn[0];
    149	switch (ctx.desc.conn_type) {
    150	case 0x01: /* LVDS */
    151		ctx.outp[1] |= 0x00000004; /* use_power_scripts */
    152		/* XXX: modify default link width in LVDS table */
    153		break;
    154	case 0x02: /* HDMI */
    155		type = DCB_CONNECTOR_HDMI_1;
    156		break;
    157	case 0x03: /* DVI-D */
    158		type = DCB_CONNECTOR_DVI_D;
    159		break;
    160	case 0x0e: /* eDP, falls through to DPint */
    161		ctx.outp[1] |= 0x00010000;
    162		fallthrough;
    163	case 0x07: /* DP internal, wtf is this?? HP8670w */
    164		ctx.outp[1] |= 0x00000004; /* use_power_scripts? */
    165		type = DCB_CONNECTOR_eDP;
    166		break;
    167	default:
    168		break;
    169	}
    170
    171	if (mxms_version(mxm) >= 0x0300)
    172		conn[0] = type;
    173
    174	return 0;
    175}
    176
    177static bool
    178mxm_show_unmatched(struct nvkm_mxm *mxm, u8 *data, void *info)
    179{
    180	struct nvkm_subdev *subdev = &mxm->subdev;
    181	u64 desc = *(u64 *)data;
    182	if ((desc & 0xf0) != 0xf0)
    183		nvkm_info(subdev, "unmatched output device %016llx\n", desc);
    184	return true;
    185}
    186
    187static void
    188mxm_dcb_sanitise(struct nvkm_mxm *mxm)
    189{
    190	struct nvkm_subdev *subdev = &mxm->subdev;
    191	struct nvkm_bios *bios = subdev->device->bios;
    192	u8  ver, hdr, cnt, len;
    193	u16 dcb = dcb_table(bios, &ver, &hdr, &cnt, &len);
    194	if (dcb == 0x0000 || (ver != 0x40 && ver != 0x41)) {
    195		nvkm_warn(subdev, "unsupported DCB version\n");
    196		return;
    197	}
    198
    199	dcb_outp_foreach(bios, mxm, mxm_dcb_sanitise_entry);
    200	mxms_foreach(mxm, 0x01, mxm_show_unmatched, NULL);
    201}
    202
    203int
    204nv50_mxm_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
    205	     struct nvkm_subdev **pmxm)
    206{
    207	struct nvkm_mxm *mxm;
    208	int ret;
    209
    210	ret = nvkm_mxm_new_(device, type, inst, &mxm);
    211	if (mxm)
    212		*pmxm = &mxm->subdev;
    213	if (ret)
    214		return ret;
    215
    216	if (mxm->action & MXM_SANITISE_DCB)
    217		mxm_dcb_sanitise(mxm);
    218
    219	return 0;
    220}