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

falcon.c (5757B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (c) 2015, NVIDIA Corporation.
      4 */
      5
      6#include <linux/platform_device.h>
      7#include <linux/dma-mapping.h>
      8#include <linux/firmware.h>
      9#include <linux/pci_ids.h>
     10#include <linux/iopoll.h>
     11
     12#include "falcon.h"
     13#include "drm.h"
     14
     15enum falcon_memory {
     16	FALCON_MEMORY_IMEM,
     17	FALCON_MEMORY_DATA,
     18};
     19
     20static void falcon_writel(struct falcon *falcon, u32 value, u32 offset)
     21{
     22	writel(value, falcon->regs + offset);
     23}
     24
     25int falcon_wait_idle(struct falcon *falcon)
     26{
     27	u32 value;
     28
     29	return readl_poll_timeout(falcon->regs + FALCON_IDLESTATE, value,
     30				  (value == 0), 10, 100000);
     31}
     32
     33static int falcon_dma_wait_idle(struct falcon *falcon)
     34{
     35	u32 value;
     36
     37	return readl_poll_timeout(falcon->regs + FALCON_DMATRFCMD, value,
     38				  (value & FALCON_DMATRFCMD_IDLE), 10, 100000);
     39}
     40
     41static int falcon_copy_chunk(struct falcon *falcon,
     42			     phys_addr_t base,
     43			     unsigned long offset,
     44			     enum falcon_memory target)
     45{
     46	u32 cmd = FALCON_DMATRFCMD_SIZE_256B;
     47
     48	if (target == FALCON_MEMORY_IMEM)
     49		cmd |= FALCON_DMATRFCMD_IMEM;
     50
     51	falcon_writel(falcon, offset, FALCON_DMATRFMOFFS);
     52	falcon_writel(falcon, base, FALCON_DMATRFFBOFFS);
     53	falcon_writel(falcon, cmd, FALCON_DMATRFCMD);
     54
     55	return falcon_dma_wait_idle(falcon);
     56}
     57
     58static void falcon_copy_firmware_image(struct falcon *falcon,
     59				       const struct firmware *firmware)
     60{
     61	u32 *virt = falcon->firmware.virt;
     62	size_t i;
     63
     64	/* copy the whole thing taking into account endianness */
     65	for (i = 0; i < firmware->size / sizeof(u32); i++)
     66		virt[i] = le32_to_cpu(((__le32 *)firmware->data)[i]);
     67}
     68
     69static int falcon_parse_firmware_image(struct falcon *falcon)
     70{
     71	struct falcon_fw_bin_header_v1 *bin = (void *)falcon->firmware.virt;
     72	struct falcon_fw_os_header_v1 *os;
     73
     74	/* endian problems would show up right here */
     75	if (bin->magic != PCI_VENDOR_ID_NVIDIA && bin->magic != 0x10fe) {
     76		dev_err(falcon->dev, "incorrect firmware magic\n");
     77		return -EINVAL;
     78	}
     79
     80	/* currently only version 1 is supported */
     81	if (bin->version != 1) {
     82		dev_err(falcon->dev, "unsupported firmware version\n");
     83		return -EINVAL;
     84	}
     85
     86	/* check that the firmware size is consistent */
     87	if (bin->size > falcon->firmware.size) {
     88		dev_err(falcon->dev, "firmware image size inconsistency\n");
     89		return -EINVAL;
     90	}
     91
     92	os = falcon->firmware.virt + bin->os_header_offset;
     93
     94	falcon->firmware.bin_data.size = bin->os_size;
     95	falcon->firmware.bin_data.offset = bin->os_data_offset;
     96	falcon->firmware.code.offset = os->code_offset;
     97	falcon->firmware.code.size = os->code_size;
     98	falcon->firmware.data.offset = os->data_offset;
     99	falcon->firmware.data.size = os->data_size;
    100
    101	return 0;
    102}
    103
    104int falcon_read_firmware(struct falcon *falcon, const char *name)
    105{
    106	int err;
    107
    108	/* request_firmware prints error if it fails */
    109	err = request_firmware(&falcon->firmware.firmware, name, falcon->dev);
    110	if (err < 0)
    111		return err;
    112
    113	falcon->firmware.size = falcon->firmware.firmware->size;
    114
    115	return 0;
    116}
    117
    118int falcon_load_firmware(struct falcon *falcon)
    119{
    120	const struct firmware *firmware = falcon->firmware.firmware;
    121	int err;
    122
    123	/* copy firmware image into local area. this also ensures endianness */
    124	falcon_copy_firmware_image(falcon, firmware);
    125
    126	/* parse the image data */
    127	err = falcon_parse_firmware_image(falcon);
    128	if (err < 0) {
    129		dev_err(falcon->dev, "failed to parse firmware image\n");
    130		return err;
    131	}
    132
    133	release_firmware(firmware);
    134	falcon->firmware.firmware = NULL;
    135
    136	return 0;
    137}
    138
    139int falcon_init(struct falcon *falcon)
    140{
    141	falcon->firmware.virt = NULL;
    142
    143	return 0;
    144}
    145
    146void falcon_exit(struct falcon *falcon)
    147{
    148	if (falcon->firmware.firmware)
    149		release_firmware(falcon->firmware.firmware);
    150}
    151
    152int falcon_boot(struct falcon *falcon)
    153{
    154	unsigned long offset;
    155	u32 value;
    156	int err;
    157
    158	if (!falcon->firmware.virt)
    159		return -EINVAL;
    160
    161	err = readl_poll_timeout(falcon->regs + FALCON_DMACTL, value,
    162				 (value & (FALCON_DMACTL_IMEM_SCRUBBING |
    163					   FALCON_DMACTL_DMEM_SCRUBBING)) == 0,
    164				 10, 10000);
    165	if (err < 0)
    166		return err;
    167
    168	falcon_writel(falcon, 0, FALCON_DMACTL);
    169
    170	/* setup the address of the binary data so Falcon can access it later */
    171	falcon_writel(falcon, (falcon->firmware.iova +
    172			       falcon->firmware.bin_data.offset) >> 8,
    173		      FALCON_DMATRFBASE);
    174
    175	/* copy the data segment into Falcon internal memory */
    176	for (offset = 0; offset < falcon->firmware.data.size; offset += 256)
    177		falcon_copy_chunk(falcon,
    178				  falcon->firmware.data.offset + offset,
    179				  offset, FALCON_MEMORY_DATA);
    180
    181	/* copy the code segment into Falcon internal memory */
    182	for (offset = 0; offset < falcon->firmware.code.size; offset += 256)
    183		falcon_copy_chunk(falcon, falcon->firmware.code.offset + offset,
    184				  offset, FALCON_MEMORY_IMEM);
    185
    186	/* setup falcon interrupts */
    187	falcon_writel(falcon, FALCON_IRQMSET_EXT(0xff) |
    188			      FALCON_IRQMSET_SWGEN1 |
    189			      FALCON_IRQMSET_SWGEN0 |
    190			      FALCON_IRQMSET_EXTERR |
    191			      FALCON_IRQMSET_HALT |
    192			      FALCON_IRQMSET_WDTMR,
    193		      FALCON_IRQMSET);
    194	falcon_writel(falcon, FALCON_IRQDEST_EXT(0xff) |
    195			      FALCON_IRQDEST_SWGEN1 |
    196			      FALCON_IRQDEST_SWGEN0 |
    197			      FALCON_IRQDEST_EXTERR |
    198			      FALCON_IRQDEST_HALT,
    199		      FALCON_IRQDEST);
    200
    201	/* enable interface */
    202	falcon_writel(falcon, FALCON_ITFEN_MTHDEN |
    203			      FALCON_ITFEN_CTXEN,
    204		      FALCON_ITFEN);
    205
    206	/* boot falcon */
    207	falcon_writel(falcon, 0x00000000, FALCON_BOOTVEC);
    208	falcon_writel(falcon, FALCON_CPUCTL_STARTCPU, FALCON_CPUCTL);
    209
    210	err = falcon_wait_idle(falcon);
    211	if (err < 0) {
    212		dev_err(falcon->dev, "Falcon boot failed due to timeout\n");
    213		return err;
    214	}
    215
    216	return 0;
    217}
    218
    219void falcon_execute_method(struct falcon *falcon, u32 method, u32 data)
    220{
    221	falcon_writel(falcon, method >> 2, FALCON_UCLASS_METHOD_OFFSET);
    222	falcon_writel(falcon, data, FALCON_UCLASS_METHOD_DATA);
    223}