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

loader.c (15355B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2//
      3// Copyright(c) 2020 Intel Corporation. All rights reserved.
      4//
      5// Author: Cezary Rojewski <cezary.rojewski@intel.com>
      6//
      7
      8#include <linux/dma-mapping.h>
      9#include <linux/firmware.h>
     10#include <linux/slab.h>
     11#include "core.h"
     12#include "registers.h"
     13
     14/* FW load (200ms) plus operational delays */
     15#define FW_READY_TIMEOUT_MS	250
     16
     17#define FW_SIGNATURE		"$SST"
     18#define FW_SIGNATURE_SIZE	4
     19
     20struct catpt_fw_hdr {
     21	char signature[FW_SIGNATURE_SIZE];
     22	u32 file_size;
     23	u32 modules;
     24	u32 file_format;
     25	u32 reserved[4];
     26} __packed;
     27
     28struct catpt_fw_mod_hdr {
     29	char signature[FW_SIGNATURE_SIZE];
     30	u32 mod_size;
     31	u32 blocks;
     32	u16 slot;
     33	u16 module_id;
     34	u32 entry_point;
     35	u32 persistent_size;
     36	u32 scratch_size;
     37} __packed;
     38
     39enum catpt_ram_type {
     40	CATPT_RAM_TYPE_IRAM = 1,
     41	CATPT_RAM_TYPE_DRAM = 2,
     42	/* DRAM with module's initial state */
     43	CATPT_RAM_TYPE_INSTANCE = 3,
     44};
     45
     46struct catpt_fw_block_hdr {
     47	u32 ram_type;
     48	u32 size;
     49	u32 ram_offset;
     50	u32 rsvd;
     51} __packed;
     52
     53void catpt_sram_init(struct resource *sram, u32 start, u32 size)
     54{
     55	sram->start = start;
     56	sram->end = start + size - 1;
     57}
     58
     59void catpt_sram_free(struct resource *sram)
     60{
     61	struct resource *res, *save;
     62
     63	for (res = sram->child; res;) {
     64		save = res->sibling;
     65		release_resource(res);
     66		kfree(res);
     67		res = save;
     68	}
     69}
     70
     71struct resource *
     72catpt_request_region(struct resource *root, resource_size_t size)
     73{
     74	struct resource *res = root->child;
     75	resource_size_t addr = root->start;
     76
     77	for (;;) {
     78		if (res->start - addr >= size)
     79			break;
     80		addr = res->end + 1;
     81		res = res->sibling;
     82		if (!res)
     83			return NULL;
     84	}
     85
     86	return __request_region(root, addr, size, NULL, 0);
     87}
     88
     89int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan)
     90{
     91	struct catpt_stream_runtime *stream;
     92
     93	list_for_each_entry(stream, &cdev->stream_list, node) {
     94		u32 off, size;
     95		int ret;
     96
     97		off = stream->persistent->start;
     98		size = resource_size(stream->persistent);
     99		dev_dbg(cdev->dev, "storing stream %d ctx: off 0x%08x size %d\n",
    100			stream->info.stream_hw_id, off, size);
    101
    102		ret = catpt_dma_memcpy_fromdsp(cdev, chan,
    103					       cdev->dxbuf_paddr + off,
    104					       cdev->lpe_base + off,
    105					       ALIGN(size, 4));
    106		if (ret) {
    107			dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
    108			return ret;
    109		}
    110	}
    111
    112	return 0;
    113}
    114
    115int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan)
    116{
    117	int i;
    118
    119	for (i = 0; i < ARRAY_SIZE(cdev->modules); i++) {
    120		struct catpt_module_type *type;
    121		u32 off;
    122		int ret;
    123
    124		type = &cdev->modules[i];
    125		if (!type->loaded || !type->state_size)
    126			continue;
    127
    128		off = type->state_offset;
    129		dev_dbg(cdev->dev, "storing mod %d state: off 0x%08x size %d\n",
    130			i, off, type->state_size);
    131
    132		ret = catpt_dma_memcpy_fromdsp(cdev, chan,
    133					       cdev->dxbuf_paddr + off,
    134					       cdev->lpe_base + off,
    135					       ALIGN(type->state_size, 4));
    136		if (ret) {
    137			dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
    138			return ret;
    139		}
    140	}
    141
    142	return 0;
    143}
    144
    145int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan)
    146{
    147	int i;
    148
    149	for (i = 0; i < cdev->dx_ctx.num_meminfo; i++) {
    150		struct catpt_save_meminfo *info;
    151		u32 off;
    152		int ret;
    153
    154		info = &cdev->dx_ctx.meminfo[i];
    155		if (info->source != CATPT_DX_TYPE_MEMORY_DUMP)
    156			continue;
    157
    158		off = catpt_to_host_offset(info->offset);
    159		if (off < cdev->dram.start || off > cdev->dram.end)
    160			continue;
    161
    162		dev_dbg(cdev->dev, "storing memdump: off 0x%08x size %d\n",
    163			off, info->size);
    164
    165		ret = catpt_dma_memcpy_fromdsp(cdev, chan,
    166					       cdev->dxbuf_paddr + off,
    167					       cdev->lpe_base + off,
    168					       ALIGN(info->size, 4));
    169		if (ret) {
    170			dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
    171			return ret;
    172		}
    173	}
    174
    175	return 0;
    176}
    177
    178static int
    179catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan)
    180{
    181	struct catpt_stream_runtime *stream;
    182
    183	list_for_each_entry(stream, &cdev->stream_list, node) {
    184		u32 off, size;
    185		int ret;
    186
    187		off = stream->persistent->start;
    188		size = resource_size(stream->persistent);
    189		dev_dbg(cdev->dev, "restoring stream %d ctx: off 0x%08x size %d\n",
    190			stream->info.stream_hw_id, off, size);
    191
    192		ret = catpt_dma_memcpy_todsp(cdev, chan,
    193					     cdev->lpe_base + off,
    194					     cdev->dxbuf_paddr + off,
    195					     ALIGN(size, 4));
    196		if (ret) {
    197			dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
    198			return ret;
    199		}
    200	}
    201
    202	return 0;
    203}
    204
    205static int catpt_restore_memdumps(struct catpt_dev *cdev, struct dma_chan *chan)
    206{
    207	int i;
    208
    209	for (i = 0; i < cdev->dx_ctx.num_meminfo; i++) {
    210		struct catpt_save_meminfo *info;
    211		u32 off;
    212		int ret;
    213
    214		info = &cdev->dx_ctx.meminfo[i];
    215		if (info->source != CATPT_DX_TYPE_MEMORY_DUMP)
    216			continue;
    217
    218		off = catpt_to_host_offset(info->offset);
    219		if (off < cdev->dram.start || off > cdev->dram.end)
    220			continue;
    221
    222		dev_dbg(cdev->dev, "restoring memdump: off 0x%08x size %d\n",
    223			off, info->size);
    224
    225		ret = catpt_dma_memcpy_todsp(cdev, chan,
    226					     cdev->lpe_base + off,
    227					     cdev->dxbuf_paddr + off,
    228					     ALIGN(info->size, 4));
    229		if (ret) {
    230			dev_err(cdev->dev, "restore block failed: %d\n", ret);
    231			return ret;
    232		}
    233	}
    234
    235	return 0;
    236}
    237
    238static int catpt_restore_fwimage(struct catpt_dev *cdev,
    239				 struct dma_chan *chan, dma_addr_t paddr,
    240				 struct catpt_fw_block_hdr *blk)
    241{
    242	struct resource r1, r2, common;
    243	int i;
    244
    245	print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
    246			     blk, sizeof(*blk), false);
    247
    248	r1.start = cdev->dram.start + blk->ram_offset;
    249	r1.end = r1.start + blk->size - 1;
    250	/* advance to data area */
    251	paddr += sizeof(*blk);
    252
    253	for (i = 0; i < cdev->dx_ctx.num_meminfo; i++) {
    254		struct catpt_save_meminfo *info;
    255		u32 off;
    256		int ret;
    257
    258		info = &cdev->dx_ctx.meminfo[i];
    259
    260		if (info->source != CATPT_DX_TYPE_FW_IMAGE)
    261			continue;
    262
    263		off = catpt_to_host_offset(info->offset);
    264		if (off < cdev->dram.start || off > cdev->dram.end)
    265			continue;
    266
    267		r2.start = off;
    268		r2.end = r2.start + info->size - 1;
    269
    270		if (!resource_intersection(&r2, &r1, &common))
    271			continue;
    272		/* calculate start offset of common data area */
    273		off = common.start - r1.start;
    274
    275		dev_dbg(cdev->dev, "restoring fwimage: %pr\n", &common);
    276
    277		ret = catpt_dma_memcpy_todsp(cdev, chan, common.start,
    278					     paddr + off,
    279					     resource_size(&common));
    280		if (ret) {
    281			dev_err(cdev->dev, "memcpy todsp failed: %d\n", ret);
    282			return ret;
    283		}
    284	}
    285
    286	return 0;
    287}
    288
    289static int catpt_load_block(struct catpt_dev *cdev,
    290			    struct dma_chan *chan, dma_addr_t paddr,
    291			    struct catpt_fw_block_hdr *blk, bool alloc)
    292{
    293	struct resource *sram, *res;
    294	dma_addr_t dst_addr;
    295	int ret;
    296
    297	print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
    298			     blk, sizeof(*blk), false);
    299
    300	switch (blk->ram_type) {
    301	case CATPT_RAM_TYPE_IRAM:
    302		sram = &cdev->iram;
    303		break;
    304	default:
    305		sram = &cdev->dram;
    306		break;
    307	}
    308
    309	dst_addr = sram->start + blk->ram_offset;
    310	if (alloc) {
    311		res = __request_region(sram, dst_addr, blk->size, NULL, 0);
    312		if (!res)
    313			return -EBUSY;
    314	}
    315
    316	/* advance to data area */
    317	paddr += sizeof(*blk);
    318
    319	ret = catpt_dma_memcpy_todsp(cdev, chan, dst_addr, paddr, blk->size);
    320	if (ret) {
    321		dev_err(cdev->dev, "memcpy error: %d\n", ret);
    322		__release_region(sram, dst_addr, blk->size);
    323	}
    324
    325	return ret;
    326}
    327
    328static int catpt_restore_basefw(struct catpt_dev *cdev,
    329				struct dma_chan *chan, dma_addr_t paddr,
    330				struct catpt_fw_mod_hdr *basefw)
    331{
    332	u32 offset = sizeof(*basefw);
    333	int ret, i;
    334
    335	print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
    336			     basefw, sizeof(*basefw), false);
    337
    338	/* restore basefw image */
    339	for (i = 0; i < basefw->blocks; i++) {
    340		struct catpt_fw_block_hdr *blk;
    341
    342		blk = (struct catpt_fw_block_hdr *)((u8 *)basefw + offset);
    343
    344		switch (blk->ram_type) {
    345		case CATPT_RAM_TYPE_IRAM:
    346			ret = catpt_load_block(cdev, chan, paddr + offset,
    347					       blk, false);
    348			break;
    349		default:
    350			ret = catpt_restore_fwimage(cdev, chan, paddr + offset,
    351						    blk);
    352			break;
    353		}
    354
    355		if (ret) {
    356			dev_err(cdev->dev, "restore block failed: %d\n", ret);
    357			return ret;
    358		}
    359
    360		offset += sizeof(*blk) + blk->size;
    361	}
    362
    363	/* then proceed with memory dumps */
    364	ret = catpt_restore_memdumps(cdev, chan);
    365	if (ret)
    366		dev_err(cdev->dev, "restore memdumps failed: %d\n", ret);
    367
    368	return ret;
    369}
    370
    371static int catpt_restore_module(struct catpt_dev *cdev,
    372				struct dma_chan *chan, dma_addr_t paddr,
    373				struct catpt_fw_mod_hdr *mod)
    374{
    375	u32 offset = sizeof(*mod);
    376	int i;
    377
    378	print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
    379			     mod, sizeof(*mod), false);
    380
    381	for (i = 0; i < mod->blocks; i++) {
    382		struct catpt_fw_block_hdr *blk;
    383		int ret;
    384
    385		blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset);
    386
    387		switch (blk->ram_type) {
    388		case CATPT_RAM_TYPE_INSTANCE:
    389			/* restore module state */
    390			ret = catpt_dma_memcpy_todsp(cdev, chan,
    391					cdev->lpe_base + blk->ram_offset,
    392					cdev->dxbuf_paddr + blk->ram_offset,
    393					ALIGN(blk->size, 4));
    394			break;
    395		default:
    396			ret = catpt_load_block(cdev, chan, paddr + offset,
    397					       blk, false);
    398			break;
    399		}
    400
    401		if (ret) {
    402			dev_err(cdev->dev, "restore block failed: %d\n", ret);
    403			return ret;
    404		}
    405
    406		offset += sizeof(*blk) + blk->size;
    407	}
    408
    409	return 0;
    410}
    411
    412static int catpt_load_module(struct catpt_dev *cdev,
    413			     struct dma_chan *chan, dma_addr_t paddr,
    414			     struct catpt_fw_mod_hdr *mod)
    415{
    416	struct catpt_module_type *type;
    417	u32 offset = sizeof(*mod);
    418	int i;
    419
    420	print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
    421			     mod, sizeof(*mod), false);
    422
    423	type = &cdev->modules[mod->module_id];
    424
    425	for (i = 0; i < mod->blocks; i++) {
    426		struct catpt_fw_block_hdr *blk;
    427		int ret;
    428
    429		blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset);
    430
    431		ret = catpt_load_block(cdev, chan, paddr + offset, blk, true);
    432		if (ret) {
    433			dev_err(cdev->dev, "load block failed: %d\n", ret);
    434			return ret;
    435		}
    436
    437		/*
    438		 * Save state window coordinates - these will be
    439		 * used to capture module state on D0 exit.
    440		 */
    441		if (blk->ram_type == CATPT_RAM_TYPE_INSTANCE) {
    442			type->state_offset = blk->ram_offset;
    443			type->state_size = blk->size;
    444		}
    445
    446		offset += sizeof(*blk) + blk->size;
    447	}
    448
    449	/* init module type static info */
    450	type->loaded = true;
    451	/* DSP expects address from module header substracted by 4 */
    452	type->entry_point = mod->entry_point - 4;
    453	type->persistent_size = mod->persistent_size;
    454	type->scratch_size = mod->scratch_size;
    455
    456	return 0;
    457}
    458
    459static int catpt_restore_firmware(struct catpt_dev *cdev,
    460				  struct dma_chan *chan, dma_addr_t paddr,
    461				  struct catpt_fw_hdr *fw)
    462{
    463	u32 offset = sizeof(*fw);
    464	int i;
    465
    466	print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
    467			     fw, sizeof(*fw), false);
    468
    469	for (i = 0; i < fw->modules; i++) {
    470		struct catpt_fw_mod_hdr *mod;
    471		int ret;
    472
    473		mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset);
    474		if (strncmp(fw->signature, mod->signature,
    475			    FW_SIGNATURE_SIZE)) {
    476			dev_err(cdev->dev, "module signature mismatch\n");
    477			return -EINVAL;
    478		}
    479
    480		if (mod->module_id > CATPT_MODID_LAST)
    481			return -EINVAL;
    482
    483		switch (mod->module_id) {
    484		case CATPT_MODID_BASE_FW:
    485			ret = catpt_restore_basefw(cdev, chan, paddr + offset,
    486						   mod);
    487			break;
    488		default:
    489			ret = catpt_restore_module(cdev, chan, paddr + offset,
    490						   mod);
    491			break;
    492		}
    493
    494		if (ret) {
    495			dev_err(cdev->dev, "restore module failed: %d\n", ret);
    496			return ret;
    497		}
    498
    499		offset += sizeof(*mod) + mod->mod_size;
    500	}
    501
    502	return 0;
    503}
    504
    505static int catpt_load_firmware(struct catpt_dev *cdev,
    506			       struct dma_chan *chan, dma_addr_t paddr,
    507			       struct catpt_fw_hdr *fw)
    508{
    509	u32 offset = sizeof(*fw);
    510	int i;
    511
    512	print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
    513			     fw, sizeof(*fw), false);
    514
    515	for (i = 0; i < fw->modules; i++) {
    516		struct catpt_fw_mod_hdr *mod;
    517		int ret;
    518
    519		mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset);
    520		if (strncmp(fw->signature, mod->signature,
    521			    FW_SIGNATURE_SIZE)) {
    522			dev_err(cdev->dev, "module signature mismatch\n");
    523			return -EINVAL;
    524		}
    525
    526		if (mod->module_id > CATPT_MODID_LAST)
    527			return -EINVAL;
    528
    529		ret = catpt_load_module(cdev, chan, paddr + offset, mod);
    530		if (ret) {
    531			dev_err(cdev->dev, "load module failed: %d\n", ret);
    532			return ret;
    533		}
    534
    535		offset += sizeof(*mod) + mod->mod_size;
    536	}
    537
    538	return 0;
    539}
    540
    541static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan,
    542			    const char *name, const char *signature,
    543			    bool restore)
    544{
    545	struct catpt_fw_hdr *fw;
    546	struct firmware *img;
    547	dma_addr_t paddr;
    548	void *vaddr;
    549	int ret;
    550
    551	ret = request_firmware((const struct firmware **)&img, name, cdev->dev);
    552	if (ret)
    553		return ret;
    554
    555	fw = (struct catpt_fw_hdr *)img->data;
    556	if (strncmp(fw->signature, signature, FW_SIGNATURE_SIZE)) {
    557		dev_err(cdev->dev, "firmware signature mismatch\n");
    558		ret = -EINVAL;
    559		goto release_fw;
    560	}
    561
    562	vaddr = dma_alloc_coherent(cdev->dev, img->size, &paddr, GFP_KERNEL);
    563	if (!vaddr) {
    564		ret = -ENOMEM;
    565		goto release_fw;
    566	}
    567
    568	memcpy(vaddr, img->data, img->size);
    569	fw = (struct catpt_fw_hdr *)vaddr;
    570	if (restore)
    571		ret = catpt_restore_firmware(cdev, chan, paddr, fw);
    572	else
    573		ret = catpt_load_firmware(cdev, chan, paddr, fw);
    574
    575	dma_free_coherent(cdev->dev, img->size, vaddr, paddr);
    576release_fw:
    577	release_firmware(img);
    578	return ret;
    579}
    580
    581static int catpt_load_images(struct catpt_dev *cdev, bool restore)
    582{
    583	static const char *const names[] = {
    584		"intel/IntcSST1.bin",
    585		"intel/IntcSST2.bin",
    586	};
    587	struct dma_chan *chan;
    588	int ret;
    589
    590	chan = catpt_dma_request_config_chan(cdev);
    591	if (IS_ERR(chan))
    592		return PTR_ERR(chan);
    593
    594	ret = catpt_load_image(cdev, chan, names[cdev->spec->core_id - 1],
    595			       FW_SIGNATURE, restore);
    596	if (ret)
    597		goto release_dma_chan;
    598
    599	if (!restore)
    600		goto release_dma_chan;
    601	ret = catpt_restore_streams_context(cdev, chan);
    602	if (ret)
    603		dev_err(cdev->dev, "restore streams ctx failed: %d\n", ret);
    604release_dma_chan:
    605	dma_release_channel(chan);
    606	return ret;
    607}
    608
    609int catpt_boot_firmware(struct catpt_dev *cdev, bool restore)
    610{
    611	int ret;
    612
    613	catpt_dsp_stall(cdev, true);
    614
    615	ret = catpt_load_images(cdev, restore);
    616	if (ret) {
    617		dev_err(cdev->dev, "load binaries failed: %d\n", ret);
    618		return ret;
    619	}
    620
    621	reinit_completion(&cdev->fw_ready);
    622	catpt_dsp_stall(cdev, false);
    623
    624	ret = wait_for_completion_timeout(&cdev->fw_ready,
    625			msecs_to_jiffies(FW_READY_TIMEOUT_MS));
    626	if (!ret) {
    627		dev_err(cdev->dev, "firmware ready timeout\n");
    628		return -ETIMEDOUT;
    629	}
    630
    631	/* update sram pg & clock once done booting */
    632	catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);
    633	catpt_dsp_update_srampge(cdev, &cdev->iram, cdev->spec->iram_mask);
    634
    635	return catpt_dsp_update_lpclock(cdev);
    636}
    637
    638int catpt_first_boot_firmware(struct catpt_dev *cdev)
    639{
    640	struct resource *res;
    641	int ret;
    642
    643	ret = catpt_boot_firmware(cdev, false);
    644	if (ret) {
    645		dev_err(cdev->dev, "basefw boot failed: %d\n", ret);
    646		return ret;
    647	}
    648
    649	/* restrict FW Core dump area */
    650	__request_region(&cdev->dram, 0, 0x200, NULL, 0);
    651	/* restrict entire area following BASE_FW - highest offset in DRAM */
    652	for (res = cdev->dram.child; res->sibling; res = res->sibling)
    653		;
    654	__request_region(&cdev->dram, res->end + 1,
    655			 cdev->dram.end - res->end, NULL, 0);
    656
    657	ret = catpt_ipc_get_mixer_stream_info(cdev, &cdev->mixer);
    658	if (ret)
    659		return CATPT_IPC_ERROR(ret);
    660
    661	ret = catpt_arm_stream_templates(cdev);
    662	if (ret) {
    663		dev_err(cdev->dev, "arm templates failed: %d\n", ret);
    664		return ret;
    665	}
    666
    667	/* update dram pg for scratch and restricted regions */
    668	catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);
    669
    670	return 0;
    671}