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

intel_dsb.c (9434B)


      1// SPDX-License-Identifier: MIT
      2/*
      3 * Copyright © 2019 Intel Corporation
      4 *
      5 */
      6
      7#include "gem/i915_gem_internal.h"
      8
      9#include "i915_drv.h"
     10#include "intel_de.h"
     11#include "intel_display_types.h"
     12
     13#define DSB_BUF_SIZE    (2 * PAGE_SIZE)
     14
     15/**
     16 * DOC: DSB
     17 *
     18 * A DSB (Display State Buffer) is a queue of MMIO instructions in the memory
     19 * which can be offloaded to DSB HW in Display Controller. DSB HW is a DMA
     20 * engine that can be programmed to download the DSB from memory.
     21 * It allows driver to batch submit display HW programming. This helps to
     22 * reduce loading time and CPU activity, thereby making the context switch
     23 * faster. DSB Support added from Gen12 Intel graphics based platform.
     24 *
     25 * DSB's can access only the pipe, plane, and transcoder Data Island Packet
     26 * registers.
     27 *
     28 * DSB HW can support only register writes (both indexed and direct MMIO
     29 * writes). There are no registers reads possible with DSB HW engine.
     30 */
     31
     32/* DSB opcodes. */
     33#define DSB_OPCODE_SHIFT		24
     34#define DSB_OPCODE_MMIO_WRITE		0x1
     35#define DSB_OPCODE_INDEXED_WRITE	0x9
     36#define DSB_BYTE_EN			0xF
     37#define DSB_BYTE_EN_SHIFT		20
     38#define DSB_REG_VALUE_MASK		0xfffff
     39
     40static bool is_dsb_busy(struct drm_i915_private *i915, enum pipe pipe,
     41			enum dsb_id id)
     42{
     43	return DSB_STATUS & intel_de_read(i915, DSB_CTRL(pipe, id));
     44}
     45
     46static bool intel_dsb_enable_engine(struct drm_i915_private *i915,
     47				    enum pipe pipe, enum dsb_id id)
     48{
     49	u32 dsb_ctrl;
     50
     51	dsb_ctrl = intel_de_read(i915, DSB_CTRL(pipe, id));
     52	if (DSB_STATUS & dsb_ctrl) {
     53		drm_dbg_kms(&i915->drm, "DSB engine is busy.\n");
     54		return false;
     55	}
     56
     57	dsb_ctrl |= DSB_ENABLE;
     58	intel_de_write(i915, DSB_CTRL(pipe, id), dsb_ctrl);
     59
     60	intel_de_posting_read(i915, DSB_CTRL(pipe, id));
     61	return true;
     62}
     63
     64static bool intel_dsb_disable_engine(struct drm_i915_private *i915,
     65				     enum pipe pipe, enum dsb_id id)
     66{
     67	u32 dsb_ctrl;
     68
     69	dsb_ctrl = intel_de_read(i915, DSB_CTRL(pipe, id));
     70	if (DSB_STATUS & dsb_ctrl) {
     71		drm_dbg_kms(&i915->drm, "DSB engine is busy.\n");
     72		return false;
     73	}
     74
     75	dsb_ctrl &= ~DSB_ENABLE;
     76	intel_de_write(i915, DSB_CTRL(pipe, id), dsb_ctrl);
     77
     78	intel_de_posting_read(i915, DSB_CTRL(pipe, id));
     79	return true;
     80}
     81
     82/**
     83 * intel_dsb_indexed_reg_write() -Write to the DSB context for auto
     84 * increment register.
     85 * @crtc_state: intel_crtc_state structure
     86 * @reg: register address.
     87 * @val: value.
     88 *
     89 * This function is used for writing register-value pair in command
     90 * buffer of DSB for auto-increment register. During command buffer overflow,
     91 * a warning is thrown and rest all erroneous condition register programming
     92 * is done through mmio write.
     93 */
     94
     95void intel_dsb_indexed_reg_write(const struct intel_crtc_state *crtc_state,
     96				 i915_reg_t reg, u32 val)
     97{
     98	struct intel_dsb *dsb = crtc_state->dsb;
     99	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
    100	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
    101	u32 *buf;
    102	u32 reg_val;
    103
    104	if (!dsb) {
    105		intel_de_write_fw(dev_priv, reg, val);
    106		return;
    107	}
    108	buf = dsb->cmd_buf;
    109	if (drm_WARN_ON(&dev_priv->drm, dsb->free_pos >= DSB_BUF_SIZE)) {
    110		drm_dbg_kms(&dev_priv->drm, "DSB buffer overflow\n");
    111		return;
    112	}
    113
    114	/*
    115	 * For example the buffer will look like below for 3 dwords for auto
    116	 * increment register:
    117	 * +--------------------------------------------------------+
    118	 * | size = 3 | offset &| value1 | value2 | value3 | zero   |
    119	 * |          | opcode  |        |        |        |        |
    120	 * +--------------------------------------------------------+
    121	 * +          +         +        +        +        +        +
    122	 * 0          4         8        12       16       20       24
    123	 * Byte
    124	 *
    125	 * As every instruction is 8 byte aligned the index of dsb instruction
    126	 * will start always from even number while dealing with u32 array. If
    127	 * we are writing odd no of dwords, Zeros will be added in the end for
    128	 * padding.
    129	 */
    130	reg_val = buf[dsb->ins_start_offset + 1] & DSB_REG_VALUE_MASK;
    131	if (reg_val != i915_mmio_reg_offset(reg)) {
    132		/* Every instruction should be 8 byte aligned. */
    133		dsb->free_pos = ALIGN(dsb->free_pos, 2);
    134
    135		dsb->ins_start_offset = dsb->free_pos;
    136
    137		/* Update the size. */
    138		buf[dsb->free_pos++] = 1;
    139
    140		/* Update the opcode and reg. */
    141		buf[dsb->free_pos++] = (DSB_OPCODE_INDEXED_WRITE  <<
    142					DSB_OPCODE_SHIFT) |
    143					i915_mmio_reg_offset(reg);
    144
    145		/* Update the value. */
    146		buf[dsb->free_pos++] = val;
    147	} else {
    148		/* Update the new value. */
    149		buf[dsb->free_pos++] = val;
    150
    151		/* Update the size. */
    152		buf[dsb->ins_start_offset]++;
    153	}
    154
    155	/* if number of data words is odd, then the last dword should be 0.*/
    156	if (dsb->free_pos & 0x1)
    157		buf[dsb->free_pos] = 0;
    158}
    159
    160/**
    161 * intel_dsb_reg_write() -Write to the DSB context for normal
    162 * register.
    163 * @crtc_state: intel_crtc_state structure
    164 * @reg: register address.
    165 * @val: value.
    166 *
    167 * This function is used for writing register-value pair in command
    168 * buffer of DSB. During command buffer overflow, a warning  is thrown
    169 * and rest all erroneous condition register programming is done
    170 * through mmio write.
    171 */
    172void intel_dsb_reg_write(const struct intel_crtc_state *crtc_state,
    173			 i915_reg_t reg, u32 val)
    174{
    175	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
    176	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
    177	struct intel_dsb *dsb;
    178	u32 *buf;
    179
    180	dsb = crtc_state->dsb;
    181	if (!dsb) {
    182		intel_de_write_fw(dev_priv, reg, val);
    183		return;
    184	}
    185
    186	buf = dsb->cmd_buf;
    187	if (drm_WARN_ON(&dev_priv->drm, dsb->free_pos >= DSB_BUF_SIZE)) {
    188		drm_dbg_kms(&dev_priv->drm, "DSB buffer overflow\n");
    189		return;
    190	}
    191
    192	dsb->ins_start_offset = dsb->free_pos;
    193	buf[dsb->free_pos++] = val;
    194	buf[dsb->free_pos++] = (DSB_OPCODE_MMIO_WRITE  << DSB_OPCODE_SHIFT) |
    195			       (DSB_BYTE_EN << DSB_BYTE_EN_SHIFT) |
    196			       i915_mmio_reg_offset(reg);
    197}
    198
    199/**
    200 * intel_dsb_commit() - Trigger workload execution of DSB.
    201 * @crtc_state: intel_crtc_state structure
    202 *
    203 * This function is used to do actual write to hardware using DSB.
    204 * On errors, fall back to MMIO. Also this function help to reset the context.
    205 */
    206void intel_dsb_commit(const struct intel_crtc_state *crtc_state)
    207{
    208	struct intel_dsb *dsb = crtc_state->dsb;
    209	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
    210	struct drm_device *dev = crtc->base.dev;
    211	struct drm_i915_private *dev_priv = to_i915(dev);
    212	enum pipe pipe = crtc->pipe;
    213	u32 tail;
    214
    215	if (!(dsb && dsb->free_pos))
    216		return;
    217
    218	if (!intel_dsb_enable_engine(dev_priv, pipe, dsb->id))
    219		goto reset;
    220
    221	if (is_dsb_busy(dev_priv, pipe, dsb->id)) {
    222		drm_err(&dev_priv->drm,
    223			"HEAD_PTR write failed - dsb engine is busy.\n");
    224		goto reset;
    225	}
    226	intel_de_write(dev_priv, DSB_HEAD(pipe, dsb->id),
    227		       i915_ggtt_offset(dsb->vma));
    228
    229	tail = ALIGN(dsb->free_pos * 4, CACHELINE_BYTES);
    230	if (tail > dsb->free_pos * 4)
    231		memset(&dsb->cmd_buf[dsb->free_pos], 0,
    232		       (tail - dsb->free_pos * 4));
    233
    234	if (is_dsb_busy(dev_priv, pipe, dsb->id)) {
    235		drm_err(&dev_priv->drm,
    236			"TAIL_PTR write failed - dsb engine is busy.\n");
    237		goto reset;
    238	}
    239	drm_dbg_kms(&dev_priv->drm,
    240		    "DSB execution started - head 0x%x, tail 0x%x\n",
    241		    i915_ggtt_offset(dsb->vma), tail);
    242	intel_de_write(dev_priv, DSB_TAIL(pipe, dsb->id),
    243		       i915_ggtt_offset(dsb->vma) + tail);
    244	if (wait_for(!is_dsb_busy(dev_priv, pipe, dsb->id), 1)) {
    245		drm_err(&dev_priv->drm,
    246			"Timed out waiting for DSB workload completion.\n");
    247		goto reset;
    248	}
    249
    250reset:
    251	dsb->free_pos = 0;
    252	dsb->ins_start_offset = 0;
    253	intel_dsb_disable_engine(dev_priv, pipe, dsb->id);
    254}
    255
    256/**
    257 * intel_dsb_prepare() - Allocate, pin and map the DSB command buffer.
    258 * @crtc_state: intel_crtc_state structure to prepare associated dsb instance.
    259 *
    260 * This function prepare the command buffer which is used to store dsb
    261 * instructions with data.
    262 */
    263void intel_dsb_prepare(struct intel_crtc_state *crtc_state)
    264{
    265	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
    266	struct drm_i915_private *i915 = to_i915(crtc->base.dev);
    267	struct intel_dsb *dsb;
    268	struct drm_i915_gem_object *obj;
    269	struct i915_vma *vma;
    270	u32 *buf;
    271	intel_wakeref_t wakeref;
    272
    273	if (!HAS_DSB(i915))
    274		return;
    275
    276	dsb = kmalloc(sizeof(*dsb), GFP_KERNEL);
    277	if (!dsb) {
    278		drm_err(&i915->drm, "DSB object creation failed\n");
    279		return;
    280	}
    281
    282	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
    283
    284	obj = i915_gem_object_create_internal(i915, DSB_BUF_SIZE);
    285	if (IS_ERR(obj)) {
    286		kfree(dsb);
    287		goto out;
    288	}
    289
    290	vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, 0);
    291	if (IS_ERR(vma)) {
    292		i915_gem_object_put(obj);
    293		kfree(dsb);
    294		goto out;
    295	}
    296
    297	buf = i915_gem_object_pin_map_unlocked(vma->obj, I915_MAP_WC);
    298	if (IS_ERR(buf)) {
    299		i915_vma_unpin_and_release(&vma, I915_VMA_RELEASE_MAP);
    300		kfree(dsb);
    301		goto out;
    302	}
    303
    304	dsb->id = DSB1;
    305	dsb->vma = vma;
    306	dsb->cmd_buf = buf;
    307	dsb->free_pos = 0;
    308	dsb->ins_start_offset = 0;
    309	crtc_state->dsb = dsb;
    310out:
    311	if (!crtc_state->dsb)
    312		drm_info(&i915->drm,
    313			 "DSB queue setup failed, will fallback to MMIO for display HW programming\n");
    314
    315	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
    316}
    317
    318/**
    319 * intel_dsb_cleanup() - To cleanup DSB context.
    320 * @crtc_state: intel_crtc_state structure to cleanup associated dsb instance.
    321 *
    322 * This function cleanup the DSB context by unpinning and releasing
    323 * the VMA object associated with it.
    324 */
    325void intel_dsb_cleanup(struct intel_crtc_state *crtc_state)
    326{
    327	if (!crtc_state->dsb)
    328		return;
    329
    330	i915_vma_unpin_and_release(&crtc_state->dsb->vma, I915_VMA_RELEASE_MAP);
    331	kfree(crtc_state->dsb);
    332	crtc_state->dsb = NULL;
    333}