brintos

brintos / linux-shallow public Read only

0
0
Text · 9.2 KiB · cac6d64 Raw
362 lines · c
1/*2 * Copyright 2022 Red Hat Inc.3 *4 * Permission is hereby granted, free of charge, to any person obtaining a5 * copy of this software and associated documentation files (the "Software"),6 * to deal in the Software without restriction, including without limitation7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,8 * and/or sell copies of the Software, and to permit persons to whom the9 * Software is furnished to do so, subject to the following conditions:10 *11 * The above copyright notice and this permission notice shall be included in12 * all copies or substantial portions of the Software.13 *14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR18 * 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 OR20 * OTHER DEALINGS IN THE SOFTWARE.21 */22#include "priv.h"23 24#include <core/memory.h>25#include <subdev/mmu.h>26 27#include <nvfw/fw.h>28#include <nvfw/hs.h>29 30int31nvkm_falcon_fw_patch(struct nvkm_falcon_fw *fw)32{33	struct nvkm_falcon *falcon = fw->falcon;34	u32 sig_base_src = fw->sig_base_prd;35	u32 src, dst, len, i;36	int idx = 0;37 38	FLCNFW_DBG(fw, "patching sigs:%d size:%d", fw->sig_nr, fw->sig_size);39	if (fw->func->signature) {40		idx = fw->func->signature(fw, &sig_base_src);41		if (idx < 0)42			return idx;43	}44 45	src = idx * fw->sig_size;46	dst = fw->sig_base_img;47	len = fw->sig_size / 4;48	FLCNFW_DBG(fw, "patch idx:%d src:%08x dst:%08x", idx, sig_base_src + src, dst);49	for (i = 0; i < len; i++) {50		u32 sig = *(u32 *)(fw->sigs + src);51 52		if (nvkm_printk_ok(falcon->owner, falcon->user, NV_DBG_TRACE)) {53			if (i % 8 == 0)54				printk(KERN_INFO "sig -> %08x:", dst);55			printk(KERN_CONT " %08x", sig);56		}57 58		*(u32 *)(fw->fw.img + dst) = sig;59		src += 4;60		dst += 4;61	}62 63	return 0;64}65 66static void67nvkm_falcon_fw_dtor_sigs(struct nvkm_falcon_fw *fw)68{69	kfree(fw->sigs);70	fw->sigs = NULL;71}72 73int74nvkm_falcon_fw_boot(struct nvkm_falcon_fw *fw, struct nvkm_subdev *user,75		    bool release, u32 *pmbox0, u32 *pmbox1, u32 mbox0_ok, u32 irqsclr)76{77	struct nvkm_falcon *falcon = fw->falcon;78	int ret;79 80	ret = nvkm_falcon_get(falcon, user);81	if (ret)82		return ret;83 84	if (fw->sigs) {85		ret = nvkm_falcon_fw_patch(fw);86		if (ret)87			goto done;88 89		nvkm_falcon_fw_dtor_sigs(fw);90	}91 92 93	FLCNFW_DBG(fw, "resetting");94	fw->func->reset(fw);95 96	FLCNFW_DBG(fw, "loading");97	if (fw->func->setup) {98		ret = fw->func->setup(fw);99		if (ret)100			goto done;101	}102 103	/* after last write to the img, sync dma mappings */104	dma_sync_single_for_device(fw->fw.device->dev,105				   fw->fw.phys,106				   sg_dma_len(&fw->fw.mem.sgl),107				   DMA_TO_DEVICE);108 109	ret = fw->func->load(fw);110	if (ret)111		goto done;112 113	FLCNFW_DBG(fw, "booting");114	ret = fw->func->boot(fw, pmbox0, pmbox1, mbox0_ok, irqsclr);115	if (ret)116		FLCNFW_ERR(fw, "boot failed: %d", ret);117	else118		FLCNFW_DBG(fw, "booted");119 120done:121	if (ret || release)122		nvkm_falcon_put(falcon, user);123	return ret;124}125 126int127nvkm_falcon_fw_oneinit(struct nvkm_falcon_fw *fw, struct nvkm_falcon *falcon,128		       struct nvkm_vmm *vmm, struct nvkm_memory *inst)129{130	int ret;131 132	fw->falcon = falcon;133	fw->vmm = nvkm_vmm_ref(vmm);134	fw->inst = nvkm_memory_ref(inst);135 136	if (fw->boot) {137		FLCN_DBG(falcon, "mapping %s fw", fw->fw.name);138		ret = nvkm_vmm_get(fw->vmm, 12, nvkm_memory_size(&fw->fw.mem.memory), &fw->vma);139		if (ret) {140			FLCN_ERR(falcon, "get %d", ret);141			return ret;142		}143 144		ret = nvkm_memory_map(&fw->fw.mem.memory, 0, fw->vmm, fw->vma, NULL, 0);145		if (ret) {146			FLCN_ERR(falcon, "map %d", ret);147			return ret;148		}149	}150 151	return 0;152}153 154void155nvkm_falcon_fw_dtor(struct nvkm_falcon_fw *fw)156{157	nvkm_vmm_put(fw->vmm, &fw->vma);158	nvkm_vmm_unref(&fw->vmm);159	nvkm_memory_unref(&fw->inst);160	nvkm_falcon_fw_dtor_sigs(fw);161	nvkm_firmware_dtor(&fw->fw);162}163 164static const struct nvkm_firmware_func165nvkm_falcon_fw_dma = {166	.type = NVKM_FIRMWARE_IMG_DMA,167};168 169static const struct nvkm_firmware_func170nvkm_falcon_fw = {171	.type = NVKM_FIRMWARE_IMG_RAM,172};173 174int175nvkm_falcon_fw_sign(struct nvkm_falcon_fw *fw, u32 sig_base_img, u32 sig_size, const u8 *sigs,176		    int sig_nr_prd, u32 sig_base_prd, int sig_nr_dbg, u32 sig_base_dbg)177{178	fw->sig_base_prd = sig_base_prd;179	fw->sig_base_dbg = sig_base_dbg;180	fw->sig_base_img = sig_base_img;181	fw->sig_size = sig_size;182	fw->sig_nr = sig_nr_prd + sig_nr_dbg;183 184	fw->sigs = kmalloc_array(fw->sig_nr, fw->sig_size, GFP_KERNEL);185	if (!fw->sigs)186		return -ENOMEM;187 188	memcpy(fw->sigs, sigs + sig_base_prd, sig_nr_prd * fw->sig_size);189	if (sig_nr_dbg)190		memcpy(fw->sigs + sig_size, sigs + sig_base_dbg, sig_nr_dbg * fw->sig_size);191 192	return 0;193}194 195int196nvkm_falcon_fw_ctor(const struct nvkm_falcon_fw_func *func, const char *name,197		    struct nvkm_device *device, bool dma, const void *src, u32 len,198		    struct nvkm_falcon *falcon, struct nvkm_falcon_fw *fw)199{200	const struct nvkm_firmware_func *type = dma ? &nvkm_falcon_fw_dma : &nvkm_falcon_fw;201	int ret;202 203	fw->func = func;204 205	ret = nvkm_firmware_ctor(type, name, device, src, len, &fw->fw);206	if (ret)207		return ret;208 209	return falcon ? nvkm_falcon_fw_oneinit(fw, falcon, NULL, NULL) : 0;210}211 212int213nvkm_falcon_fw_ctor_hs(const struct nvkm_falcon_fw_func *func, const char *name,214		       struct nvkm_subdev *subdev, const char *bl, const char *img, int ver,215		       struct nvkm_falcon *falcon, struct nvkm_falcon_fw *fw)216{217	const struct firmware *blob;218	const struct nvfw_bin_hdr *hdr;219	const struct nvfw_hs_header *hshdr;220	const struct nvfw_hs_load_header *lhdr;221	const struct nvfw_bl_desc *desc;222	u32 loc, sig;223	int ret;224 225	ret = nvkm_firmware_load_name(subdev, img, "", ver, &blob);226	if (ret)227		return ret;228 229	hdr = nvfw_bin_hdr(subdev, blob->data);230	hshdr = nvfw_hs_header(subdev, blob->data + hdr->header_offset);231 232	ret = nvkm_falcon_fw_ctor(func, name, subdev->device, bl != NULL,233				  blob->data + hdr->data_offset, hdr->data_size, falcon, fw);234	if (ret)235		goto done;236 237	/* Earlier FW releases by NVIDIA for Nouveau's use aren't in NVIDIA's238	 * standard format, and don't have the indirection seen in the 0x10de239	 * case.240	 */241	switch (hdr->bin_magic) {242	case 0x000010de:243		loc = *(u32 *)(blob->data + hshdr->patch_loc);244		sig = *(u32 *)(blob->data + hshdr->patch_sig);245		break;246	case 0x3b1d14f0:247		loc = hshdr->patch_loc;248		sig = hshdr->patch_sig;249		break;250	default:251		WARN_ON(1);252		ret = -EINVAL;253		goto done;254	}255 256	ret = nvkm_falcon_fw_sign(fw, loc, hshdr->sig_prod_size, blob->data,257				  1, hshdr->sig_prod_offset + sig,258				  1, hshdr->sig_dbg_offset + sig);259	if (ret)260		goto done;261 262	lhdr = nvfw_hs_load_header(subdev, blob->data + hshdr->hdr_offset);263 264	fw->nmem_base_img = 0;265	fw->nmem_base = lhdr->non_sec_code_off;266	fw->nmem_size = lhdr->non_sec_code_size;267 268	fw->imem_base_img = lhdr->apps[0];269	fw->imem_base = ALIGN(lhdr->apps[0], 0x100);270	fw->imem_size = lhdr->apps[lhdr->num_apps + 0];271 272	fw->dmem_base_img = lhdr->data_dma_base;273	fw->dmem_base = 0;274	fw->dmem_size = lhdr->data_size;275	fw->dmem_sign = loc - lhdr->data_dma_base;276 277	if (bl) {278		nvkm_firmware_put(blob);279 280		ret = nvkm_firmware_load_name(subdev, bl, "", ver, &blob);281		if (ret)282			return ret;283 284		hdr = nvfw_bin_hdr(subdev, blob->data);285		desc = nvfw_bl_desc(subdev, blob->data + hdr->header_offset);286 287		fw->boot_addr = desc->start_tag << 8;288		fw->boot_size = desc->code_size;289		fw->boot = kmemdup(blob->data + hdr->data_offset + desc->code_off,290				   fw->boot_size, GFP_KERNEL);291		if (!fw->boot)292			ret = -ENOMEM;293	} else {294		fw->boot_addr = fw->nmem_base;295	}296 297done:298	if (ret)299		nvkm_falcon_fw_dtor(fw);300 301	nvkm_firmware_put(blob);302	return ret;303}304 305int306nvkm_falcon_fw_ctor_hs_v2(const struct nvkm_falcon_fw_func *func, const char *name,307			  struct nvkm_subdev *subdev, const char *img, int ver,308			  struct nvkm_falcon *falcon, struct nvkm_falcon_fw *fw)309{310	const struct nvfw_bin_hdr *hdr;311	const struct nvfw_hs_header_v2 *hshdr;312	const struct nvfw_hs_load_header_v2 *lhdr;313	const struct firmware *blob;314	u32 loc, sig, cnt, *meta;315	int ret;316 317	ret = nvkm_firmware_load_name(subdev, img, "", ver, &blob);318	if (ret)319		return ret;320 321	hdr = nvfw_bin_hdr(subdev, blob->data);322	hshdr = nvfw_hs_header_v2(subdev, blob->data + hdr->header_offset);323	meta = (u32 *)(blob->data + hshdr->meta_data_offset);324	loc = *(u32 *)(blob->data + hshdr->patch_loc);325	sig = *(u32 *)(blob->data + hshdr->patch_sig);326	cnt = *(u32 *)(blob->data + hshdr->num_sig);327 328	ret = nvkm_falcon_fw_ctor(func, name, subdev->device, true,329				  blob->data + hdr->data_offset, hdr->data_size, falcon, fw);330	if (ret)331		goto done;332 333	ret = nvkm_falcon_fw_sign(fw, loc, hshdr->sig_prod_size / cnt, blob->data,334				  cnt, hshdr->sig_prod_offset + sig, 0, 0);335	if (ret)336		goto done;337 338	lhdr = nvfw_hs_load_header_v2(subdev, blob->data + hshdr->header_offset);339 340	fw->imem_base_img = lhdr->app[0].offset;341	fw->imem_base = 0;342	fw->imem_size = lhdr->app[0].size;343 344	fw->dmem_base_img = lhdr->os_data_offset;345	fw->dmem_base = 0;346	fw->dmem_size = lhdr->os_data_size;347	fw->dmem_sign = loc - lhdr->os_data_offset;348 349	fw->boot_addr = lhdr->app[0].offset;350 351	fw->fuse_ver = meta[0];352	fw->engine_id = meta[1];353	fw->ucode_id = meta[2];354 355done:356	if (ret)357		nvkm_falcon_fw_dtor(fw);358 359	nvkm_firmware_put(blob);360	return ret;361}362