Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,12 @@
# SPDX-License-Identifier: MIT
nvkm-y += nvkm/subdev/acr/base.o
nvkm-y += nvkm/subdev/acr/lsfw.o
nvkm-y += nvkm/subdev/acr/gm200.o
nvkm-y += nvkm/subdev/acr/gm20b.o
nvkm-y += nvkm/subdev/acr/gp102.o
nvkm-y += nvkm/subdev/acr/gp108.o
nvkm-y += nvkm/subdev/acr/gv100.o
nvkm-y += nvkm/subdev/acr/gp10b.o
nvkm-y += nvkm/subdev/acr/tu102.o
nvkm-y += nvkm/subdev/acr/ga100.o
nvkm-y += nvkm/subdev/acr/ga102.o
@@ -0,0 +1,447 @@
/*
* Copyright 2019 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "priv.h"
#include <core/firmware.h>
#include <core/memory.h>
#include <subdev/mmu.h>
#include <subdev/gsp.h>
#include <subdev/pmu.h>
#include <engine/sec2.h>
#include <engine/nvdec.h>
static struct nvkm_acr_hsfw *
nvkm_acr_hsfw_find(struct nvkm_acr *acr, const char *name)
{
struct nvkm_acr_hsfw *hsfw;
list_for_each_entry(hsfw, &acr->hsfw, head) {
if (!strcmp(hsfw->fw.fw.name, name))
return hsfw;
}
return NULL;
}
int
nvkm_acr_hsfw_boot(struct nvkm_acr *acr, const char *name)
{
struct nvkm_subdev *subdev = &acr->subdev;
struct nvkm_acr_hsfw *hsfw;
hsfw = nvkm_acr_hsfw_find(acr, name);
if (!hsfw)
return -EINVAL;
return nvkm_falcon_fw_boot(&hsfw->fw, subdev, true, NULL, NULL,
hsfw->boot_mbox0, hsfw->intr_clear);
}
static struct nvkm_acr_lsf *
nvkm_acr_rtos(struct nvkm_acr *acr)
{
struct nvkm_acr_lsf *lsf;
if (acr) {
list_for_each_entry(lsf, &acr->lsf, head) {
if (lsf->func->bootstrap_falcon)
return lsf;
}
}
return NULL;
}
static void
nvkm_acr_unload(struct nvkm_acr *acr)
{
if (acr->done) {
if (acr->rtos) {
nvkm_subdev_unref(acr->rtos->falcon->owner);
acr->rtos = NULL;
}
nvkm_acr_hsfw_boot(acr, "unload");
acr->done = false;
}
}
static int
nvkm_acr_load(struct nvkm_acr *acr)
{
struct nvkm_subdev *subdev = &acr->subdev;
struct nvkm_acr_lsf *rtos = nvkm_acr_rtos(acr);
u64 start, limit;
int ret;
if (list_empty(&acr->lsf)) {
nvkm_debug(subdev, "No LSF(s) present.\n");
return 0;
}
ret = acr->func->init(acr);
if (ret)
return ret;
acr->func->wpr_check(acr, &start, &limit);
if (start != acr->wpr_start || limit != acr->wpr_end) {
nvkm_error(subdev, "WPR not configured as expected: "
"%016llx-%016llx vs %016llx-%016llx\n",
acr->wpr_start, acr->wpr_end, start, limit);
return -EIO;
}
acr->done = true;
if (rtos) {
ret = nvkm_subdev_ref(rtos->falcon->owner);
if (ret)
return ret;
acr->rtos = rtos;
}
return ret;
}
static int
nvkm_acr_reload(struct nvkm_acr *acr)
{
nvkm_acr_unload(acr);
return nvkm_acr_load(acr);
}
int
nvkm_acr_bootstrap_falcons(struct nvkm_device *device, unsigned long mask)
{
struct nvkm_acr *acr = device->acr;
struct nvkm_acr_lsf *rtos = nvkm_acr_rtos(acr);
unsigned long id;
/* If there's no LS FW managing bootstrapping of other LS falcons,
* we depend on the HS firmware being able to do it instead.
*/
if (!rtos) {
/* Which isn't possible everywhere... */
if ((mask & acr->func->bootstrap_falcons) == mask) {
int ret = nvkm_acr_reload(acr);
if (ret)
return ret;
return acr->done ? 0 : -EINVAL;
}
return -ENOSYS;
}
if ((mask & rtos->func->bootstrap_falcons) != mask)
return -ENOSYS;
if (rtos->func->bootstrap_multiple_falcons)
return rtos->func->bootstrap_multiple_falcons(rtos->falcon, mask);
for_each_set_bit(id, &mask, NVKM_ACR_LSF_NUM) {
int ret = rtos->func->bootstrap_falcon(rtos->falcon, id);
if (ret)
return ret;
}
return 0;
}
bool
nvkm_acr_managed_falcon(struct nvkm_device *device, enum nvkm_acr_lsf_id id)
{
struct nvkm_acr *acr = device->acr;
if (acr) {
if (acr->managed_falcons & BIT_ULL(id))
return true;
}
return false;
}
static int
nvkm_acr_fini(struct nvkm_subdev *subdev, enum nvkm_suspend_state suspend)
{
if (!subdev->use.enabled)
return 0;
nvkm_acr_unload(nvkm_acr(subdev));
return 0;
}
static int
nvkm_acr_init(struct nvkm_subdev *subdev)
{
struct nvkm_acr *acr = nvkm_acr(subdev);
if (!nvkm_acr_rtos(acr))
return 0;
return nvkm_acr_load(acr);
}
static void
nvkm_acr_cleanup(struct nvkm_acr *acr)
{
nvkm_acr_lsfw_del_all(acr);
nvkm_firmware_put(acr->wpr_fw);
acr->wpr_fw = NULL;
}
static int
nvkm_acr_oneinit(struct nvkm_subdev *subdev)
{
struct nvkm_device *device = subdev->device;
struct nvkm_acr *acr = nvkm_acr(subdev);
struct nvkm_acr_hsfw *hsfw;
struct nvkm_acr_lsfw *lsfw, *lsft;
struct nvkm_acr_lsf *lsf, *rtos;
struct nvkm_falcon *falcon;
u32 wpr_size = 0;
u64 falcons;
int ret, i;
if (list_empty(&acr->hsfw) || !acr->func || !acr->func->wpr_layout) {
nvkm_debug(subdev, "No HSFW(s)\n");
nvkm_acr_cleanup(acr);
return 0;
}
/* Determine layout/size of WPR image up-front, as we need to know
* it to allocate memory before we begin constructing it.
*/
list_for_each_entry_safe(lsfw, lsft, &acr->lsfw, head) {
/* Cull unknown falcons that are present in WPR image. */
if (acr->wpr_fw) {
if (!lsfw->func) {
nvkm_acr_lsfw_del(lsfw);
continue;
}
wpr_size = acr->wpr_fw->size;
}
/* Ensure we've fetched falcon configuration. */
ret = nvkm_falcon_get(lsfw->falcon, subdev);
if (ret)
return ret;
nvkm_falcon_put(lsfw->falcon, subdev);
if (!(lsf = kmalloc_obj(*lsf)))
return -ENOMEM;
lsf->func = lsfw->func;
lsf->falcon = lsfw->falcon;
lsf->id = lsfw->id;
list_add_tail(&lsf->head, &acr->lsf);
acr->managed_falcons |= BIT_ULL(lsf->id);
}
/* Ensure the falcon that'll provide ACR functions is booted first. */
rtos = nvkm_acr_rtos(acr);
if (rtos) {
falcons = rtos->func->bootstrap_falcons;
list_move(&rtos->head, &acr->lsf);
} else {
falcons = acr->func->bootstrap_falcons;
}
/* Cull falcons that can't be bootstrapped, or the HSFW can fail to
* boot and leave the GPU in a weird state.
*/
list_for_each_entry_safe(lsfw, lsft, &acr->lsfw, head) {
if (!(falcons & BIT_ULL(lsfw->id))) {
nvkm_warn(subdev, "%s falcon cannot be bootstrapped\n",
nvkm_acr_lsf_id(lsfw->id));
nvkm_acr_lsfw_del(lsfw);
}
}
if (!acr->wpr_fw || acr->wpr_comp)
wpr_size = acr->func->wpr_layout(acr);
/* Allocate/Locate WPR + fill ucode blob pointer.
*
* dGPU: allocate WPR + shadow blob
* Tegra: locate WPR with regs, ensure size is sufficient,
* allocate ucode blob.
*/
ret = acr->func->wpr_alloc(acr, wpr_size);
if (ret)
return ret;
nvkm_debug(subdev, "WPR region is from 0x%llx-0x%llx (shadow 0x%llx)\n",
acr->wpr_start, acr->wpr_end, acr->shadow_start);
/* Write WPR to ucode blob. */
nvkm_kmap(acr->wpr);
if (acr->wpr_fw && !acr->wpr_comp)
nvkm_wobj(acr->wpr, 0, acr->wpr_fw->data, acr->wpr_fw->size);
if (!acr->wpr_fw || acr->wpr_comp)
acr->func->wpr_build(acr, rtos);
acr->func->wpr_patch(acr, (s64)acr->wpr_start - acr->wpr_prev);
if (acr->wpr_fw && acr->wpr_comp) {
nvkm_kmap(acr->wpr);
for (i = 0; i < acr->wpr_fw->size; i += 4) {
u32 us = nvkm_ro32(acr->wpr, i);
u32 fw = ((u32 *)acr->wpr_fw->data)[i/4];
if (fw != us) {
nvkm_warn(subdev, "%08x: %08x %08x\n",
i, us, fw);
}
}
nvkm_done(acr->wpr);
return -EINVAL;
}
nvkm_done(acr->wpr);
/* Allocate instance block for ACR-related stuff. */
ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x1000, 0, true,
&acr->inst);
if (ret)
return ret;
ret = nvkm_vmm_new(device, 0, 0, NULL, 0, NULL, "acr", &acr->vmm);
if (ret)
return ret;
acr->vmm->debug = acr->subdev.debug;
ret = nvkm_vmm_join(acr->vmm, acr->inst);
if (ret)
return ret;
/* Load HS firmware blobs into ACR VMM. */
list_for_each_entry(hsfw, &acr->hsfw, head) {
switch (hsfw->falcon_id) {
case NVKM_ACR_HSF_PMU : falcon = &device->pmu->falcon; break;
case NVKM_ACR_HSF_SEC2: falcon = &device->sec2->falcon; break;
case NVKM_ACR_HSF_GSP : falcon = &device->gsp->falcon; break;
default:
WARN_ON(1);
return -EINVAL;
}
ret = nvkm_falcon_fw_oneinit(&hsfw->fw, falcon, acr->vmm, acr->inst);
if (ret)
return ret;
}
/* Kill temporary data. */
nvkm_acr_cleanup(acr);
return 0;
}
static void *
nvkm_acr_dtor(struct nvkm_subdev *subdev)
{
struct nvkm_acr *acr = nvkm_acr(subdev);
struct nvkm_acr_hsfw *hsfw, *hsft;
struct nvkm_acr_lsf *lsf, *lst;
list_for_each_entry_safe(hsfw, hsft, &acr->hsfw, head) {
nvkm_falcon_fw_dtor(&hsfw->fw);
list_del(&hsfw->head);
kfree(hsfw);
}
nvkm_vmm_part(acr->vmm, acr->inst);
nvkm_vmm_unref(&acr->vmm);
nvkm_memory_unref(&acr->inst);
nvkm_memory_unref(&acr->wpr);
list_for_each_entry_safe(lsf, lst, &acr->lsf, head) {
list_del(&lsf->head);
kfree(lsf);
}
nvkm_acr_cleanup(acr);
return acr;
}
static const struct nvkm_subdev_func
nvkm_acr = {
.dtor = nvkm_acr_dtor,
.oneinit = nvkm_acr_oneinit,
.init = nvkm_acr_init,
.fini = nvkm_acr_fini,
};
static int
nvkm_acr_ctor_wpr(struct nvkm_acr *acr, int ver)
{
struct nvkm_subdev *subdev = &acr->subdev;
struct nvkm_device *device = subdev->device;
int ret;
ret = nvkm_firmware_get(subdev, "acr/wpr", ver, &acr->wpr_fw);
if (ret < 0)
return ret;
/* Pre-add LSFs in the order they appear in the FW WPR image so that
* we're able to do a binary comparison with our own generator.
*/
ret = acr->func->wpr_parse(acr);
if (ret)
return ret;
acr->wpr_comp = nvkm_boolopt(device->cfgopt, "NvAcrWprCompare", false);
acr->wpr_prev = nvkm_longopt(device->cfgopt, "NvAcrWprPrevAddr", 0);
return 0;
}
int
nvkm_acr_new_(const struct nvkm_acr_fwif *fwif, struct nvkm_device *device,
enum nvkm_subdev_type type, int inst, struct nvkm_acr **pacr)
{
struct nvkm_acr *acr;
long wprfw;
if (!(acr = *pacr = kzalloc_obj(*acr)))
return -ENOMEM;
nvkm_subdev_ctor(&nvkm_acr, device, type, inst, &acr->subdev);
INIT_LIST_HEAD(&acr->hsfw);
INIT_LIST_HEAD(&acr->lsfw);
INIT_LIST_HEAD(&acr->lsf);
fwif = nvkm_firmware_load(&acr->subdev, fwif, "Acr", acr);
if (IS_ERR(fwif))
return PTR_ERR(fwif);
acr->func = fwif->func;
wprfw = nvkm_longopt(device->cfgopt, "NvAcrWpr", -1);
if (wprfw >= 0) {
int ret = nvkm_acr_ctor_wpr(acr, wprfw);
if (ret)
return ret;
}
return 0;
}
@@ -0,0 +1,49 @@
/*
* Copyright 2021 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "priv.h"
void
ga100_acr_wpr_check(struct nvkm_acr *acr, u64 *start, u64 *limit)
{
struct nvkm_device *device = acr->subdev.device;
*start = (u64)(nvkm_rd32(device, 0x1fa81c) & 0xffffff00) << 8;
*limit = (u64)(nvkm_rd32(device, 0x1fa820) & 0xffffff00) << 8;
*limit = *limit + 0x20000;
}
int
ga100_acr_hsfw_ctor(struct nvkm_acr *acr, const char *bl, const char *fw,
const char *name, int ver, const struct nvkm_acr_hsf_fwif *fwif)
{
struct nvkm_acr_hsfw *hsfw;
if (!(hsfw = kzalloc_obj(*hsfw)))
return -ENOMEM;
hsfw->falcon_id = fwif->falcon_id;
hsfw->boot_mbox0 = fwif->boot_mbox0;
hsfw->intr_clear = fwif->intr_clear;
list_add_tail(&hsfw->head, &acr->hsfw);
return nvkm_falcon_fw_ctor_hs_v2(fwif->func, name, &acr->subdev, fw, ver, NULL, &hsfw->fw);
}
@@ -0,0 +1,330 @@
/*
* Copyright 2021 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "priv.h"
#include <subdev/gsp.h>
#include <nvfw/acr.h>
static int
ga102_acr_wpr_patch(struct nvkm_acr *acr, s64 adjust)
{
struct wpr_header_v2 hdr;
struct lsb_header_v2 *lsb;
struct nvkm_acr_lsfw *lsfw;
u32 offset = 0;
lsb = kvmalloc_obj(*lsb);
if (!lsb)
return -ENOMEM;
do {
nvkm_robj(acr->wpr, offset, &hdr, sizeof(hdr));
wpr_header_v2_dump(&acr->subdev, &hdr);
list_for_each_entry(lsfw, &acr->lsfw, head) {
if (lsfw->id != hdr.wpr.falcon_id)
continue;
nvkm_robj(acr->wpr, hdr.wpr.lsb_offset, lsb, sizeof(*lsb));
lsb_header_v2_dump(&acr->subdev, lsb);
lsfw->func->bld_patch(acr, lsb->bl_data_off, adjust);
break;
}
offset += sizeof(hdr);
} while (hdr.wpr.falcon_id != WPR_HEADER_V1_FALCON_ID_INVALID);
kvfree(lsb);
return 0;
}
static int
ga102_acr_wpr_build_lsb(struct nvkm_acr *acr, struct nvkm_acr_lsfw *lsfw)
{
struct lsb_header_v2 *hdr;
int ret = 0;
if (WARN_ON(lsfw->sig->size != sizeof(hdr->signature)))
return -EINVAL;
hdr = kvzalloc_obj(*hdr);
if (!hdr)
return -ENOMEM;
hdr->hdr.identifier = WPR_GENERIC_HEADER_ID_LSF_LSB_HEADER;
hdr->hdr.version = 2;
hdr->hdr.size = sizeof(*hdr);
memcpy(&hdr->signature, lsfw->sig->data, lsfw->sig->size);
hdr->ucode_off = lsfw->offset.img;
hdr->ucode_size = lsfw->ucode_size;
hdr->data_size = lsfw->data_size;
hdr->bl_code_size = lsfw->bootloader_size;
hdr->bl_imem_off = lsfw->bootloader_imem_offset;
hdr->bl_data_off = lsfw->offset.bld;
hdr->bl_data_size = lsfw->bl_data_size;
hdr->app_code_off = lsfw->app_start_offset + lsfw->app_resident_code_offset;
hdr->app_code_size = ALIGN(lsfw->app_resident_code_size, 0x100);
hdr->app_data_off = lsfw->app_start_offset + lsfw->app_resident_data_offset;
hdr->app_data_size = ALIGN(lsfw->app_resident_data_size, 0x100);
hdr->app_imem_offset = lsfw->app_imem_offset;
hdr->app_dmem_offset = lsfw->app_dmem_offset;
hdr->flags = lsfw->func->flags;
hdr->monitor_code_offset = 0;
hdr->monitor_data_offset = 0;
hdr->manifest_offset = 0;
if (lsfw->secure_bootloader) {
struct nvkm_falcon_fw fw = {
.fw.img = hdr->hs_fmc_params.pkc_signature,
.fw.name = "LSFW",
.func = &(const struct nvkm_falcon_fw_func) {
.signature = ga100_flcn_fw_signature,
},
.sig_size = lsfw->sig_size,
.sig_nr = lsfw->sig_nr,
.sigs = lsfw->sigs,
.fuse_ver = lsfw->fuse_ver,
.engine_id = lsfw->engine_id,
.ucode_id = lsfw->ucode_id,
.falcon = lsfw->falcon,
};
ret = nvkm_falcon_get(fw.falcon, &acr->subdev);
if (ret == 0) {
hdr->hs_fmc_params.hs_fmc = 1;
hdr->hs_fmc_params.pkc_algo = 0;
hdr->hs_fmc_params.pkc_algo_version = 1;
hdr->hs_fmc_params.engid_mask = lsfw->engine_id;
hdr->hs_fmc_params.ucode_id = lsfw->ucode_id;
hdr->hs_fmc_params.fuse_ver = lsfw->fuse_ver;
ret = nvkm_falcon_fw_patch(&fw);
nvkm_falcon_put(fw.falcon, &acr->subdev);
}
}
nvkm_wobj(acr->wpr, lsfw->offset.lsb, hdr, sizeof(*hdr));
kvfree(hdr);
return ret;
}
static int
ga102_acr_wpr_build(struct nvkm_acr *acr, struct nvkm_acr_lsf *rtos)
{
struct nvkm_acr_lsfw *lsfw;
struct wpr_header_v2 hdr;
u32 offset = 0;
int ret;
/*XXX: shared sub-WPR headers, fill terminator for now. */
nvkm_wo32(acr->wpr, 0x300, (2 << 16) | WPR_GENERIC_HEADER_ID_LSF_SHARED_SUB_WPR);
nvkm_wo32(acr->wpr, 0x304, 0x14);
nvkm_wo32(acr->wpr, 0x308, 0xffffffff);
nvkm_wo32(acr->wpr, 0x30c, 0);
nvkm_wo32(acr->wpr, 0x310, 0);
/* Fill per-LSF structures. */
list_for_each_entry(lsfw, &acr->lsfw, head) {
struct lsf_signature_v2 *sig = (void *)lsfw->sig->data;
hdr.hdr.identifier = WPR_GENERIC_HEADER_ID_LSF_WPR_HEADER;
hdr.hdr.version = 2;
hdr.hdr.size = sizeof(hdr);
hdr.wpr.falcon_id = lsfw->id;
hdr.wpr.lsb_offset = lsfw->offset.lsb;
hdr.wpr.bootstrap_owner = NVKM_ACR_LSF_GSPLITE;
hdr.wpr.lazy_bootstrap = 1;
hdr.wpr.bin_version = sig->ls_ucode_version;
hdr.wpr.status = WPR_HEADER_V1_STATUS_COPY;
/* Write WPR header. */
nvkm_wobj(acr->wpr, offset, &hdr, sizeof(hdr));
offset += sizeof(hdr);
/* Write LSB header. */
ret = ga102_acr_wpr_build_lsb(acr, lsfw);
if (ret)
return ret;
/* Write ucode image. */
nvkm_wobj(acr->wpr, lsfw->offset.img,
lsfw->img.data,
lsfw->img.size);
/* Write bootloader data. */
lsfw->func->bld_write(acr, lsfw->offset.bld, lsfw);
}
/* Finalise WPR. */
hdr.hdr.identifier = WPR_GENERIC_HEADER_ID_LSF_WPR_HEADER;
hdr.hdr.version = 2;
hdr.hdr.size = sizeof(hdr);
hdr.wpr.falcon_id = WPR_HEADER_V1_FALCON_ID_INVALID;
nvkm_wobj(acr->wpr, offset, &hdr, sizeof(hdr));
return 0;
}
static u32
ga102_acr_wpr_layout(struct nvkm_acr *acr)
{
struct nvkm_acr_lsfw *lsfw;
u32 wpr = 0;
wpr += 21 /* MAX_LSF */ * sizeof(struct wpr_header_v2);
wpr = ALIGN(wpr, 256);
wpr += 0x100; /* Shared sub-WPR headers. */
list_for_each_entry(lsfw, &acr->lsfw, head) {
wpr = ALIGN(wpr, 256);
lsfw->offset.lsb = wpr;
wpr += sizeof(struct lsb_header_v2);
wpr = ALIGN(wpr, 4096);
lsfw->offset.img = wpr;
wpr += lsfw->img.size;
wpr = ALIGN(wpr, 256);
lsfw->offset.bld = wpr;
lsfw->bl_data_size = ALIGN(lsfw->func->bld_size, 256);
wpr += lsfw->bl_data_size;
}
return wpr;
}
static int
ga102_acr_wpr_parse(struct nvkm_acr *acr)
{
const struct wpr_header_v2 *hdr = (void *)acr->wpr_fw->data;
while (hdr->wpr.falcon_id != WPR_HEADER_V1_FALCON_ID_INVALID) {
wpr_header_v2_dump(&acr->subdev, hdr);
if (!nvkm_acr_lsfw_add(NULL, acr, NULL, (hdr++)->wpr.falcon_id))
return -ENOMEM;
}
return 0;
}
MODULE_FIRMWARE("nvidia/ga102/acr/ucode_unload.bin");
MODULE_FIRMWARE("nvidia/ga103/acr/ucode_unload.bin");
MODULE_FIRMWARE("nvidia/ga104/acr/ucode_unload.bin");
MODULE_FIRMWARE("nvidia/ga106/acr/ucode_unload.bin");
MODULE_FIRMWARE("nvidia/ga107/acr/ucode_unload.bin");
static const struct nvkm_acr_hsf_fwif
ga102_acr_unload_fwif[] = {
{ 0, ga100_acr_hsfw_ctor, &ga102_flcn_fw, NVKM_ACR_HSF_SEC2 },
{}
};
MODULE_FIRMWARE("nvidia/ga102/acr/ucode_asb.bin");
MODULE_FIRMWARE("nvidia/ga103/acr/ucode_asb.bin");
MODULE_FIRMWARE("nvidia/ga104/acr/ucode_asb.bin");
MODULE_FIRMWARE("nvidia/ga106/acr/ucode_asb.bin");
MODULE_FIRMWARE("nvidia/ga107/acr/ucode_asb.bin");
static const struct nvkm_acr_hsf_fwif
ga102_acr_asb_fwif[] = {
{ 0, ga100_acr_hsfw_ctor, &ga102_flcn_fw, NVKM_ACR_HSF_GSP },
{}
};
static const struct nvkm_falcon_fw_func
ga102_acr_ahesasc_0 = {
.signature = ga100_flcn_fw_signature,
.reset = gm200_flcn_fw_reset,
.setup = gp102_acr_load_setup,
.load = ga102_flcn_fw_load,
.boot = ga102_flcn_fw_boot,
};
MODULE_FIRMWARE("nvidia/ga102/acr/ucode_ahesasc.bin");
MODULE_FIRMWARE("nvidia/ga103/acr/ucode_ahesasc.bin");
MODULE_FIRMWARE("nvidia/ga104/acr/ucode_ahesasc.bin");
MODULE_FIRMWARE("nvidia/ga106/acr/ucode_ahesasc.bin");
MODULE_FIRMWARE("nvidia/ga107/acr/ucode_ahesasc.bin");
static const struct nvkm_acr_hsf_fwif
ga102_acr_ahesasc_fwif[] = {
{ 0, ga100_acr_hsfw_ctor, &ga102_acr_ahesasc_0, NVKM_ACR_HSF_SEC2 },
{}
};
static const struct nvkm_acr_func
ga102_acr = {
.ahesasc = ga102_acr_ahesasc_fwif,
.asb = ga102_acr_asb_fwif,
.unload = ga102_acr_unload_fwif,
.wpr_parse = ga102_acr_wpr_parse,
.wpr_layout = ga102_acr_wpr_layout,
.wpr_alloc = gp102_acr_wpr_alloc,
.wpr_patch = ga102_acr_wpr_patch,
.wpr_build = ga102_acr_wpr_build,
.wpr_check = ga100_acr_wpr_check,
.init = tu102_acr_init,
};
static int
ga102_acr_load(struct nvkm_acr *acr, int version,
const struct nvkm_acr_fwif *fwif)
{
struct nvkm_subdev *subdev = &acr->subdev;
const struct nvkm_acr_hsf_fwif *hsfwif;
hsfwif = nvkm_firmware_load(subdev, fwif->func->ahesasc, "AcrAHESASC",
acr, NULL, "acr/ucode_ahesasc", "AHESASC");
if (IS_ERR(hsfwif))
return PTR_ERR(hsfwif);
hsfwif = nvkm_firmware_load(subdev, fwif->func->asb, "AcrASB",
acr, NULL, "acr/ucode_asb", "ASB");
if (IS_ERR(hsfwif))
return PTR_ERR(hsfwif);
hsfwif = nvkm_firmware_load(subdev, fwif->func->unload, "AcrUnload",
acr, NULL, "acr/ucode_unload", "unload");
if (IS_ERR(hsfwif))
return PTR_ERR(hsfwif);
return 0;
}
static const struct nvkm_acr_fwif
ga102_acr_fwif[] = {
{ 0, ga102_acr_load, &ga102_acr },
{ -1, gm200_acr_nofw, &gm200_acr },
{}
};
int
ga102_acr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_acr **pacr)
{
if (nvkm_gsp_rm(device->gsp))
return -ENODEV;
return nvkm_acr_new_(ga102_acr_fwif, device, type, inst, pacr);
}
@@ -0,0 +1,374 @@
/*
* Copyright 2019 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "priv.h"
#include <core/falcon.h>
#include <core/firmware.h>
#include <core/memory.h>
#include <subdev/mc.h>
#include <subdev/mmu.h>
#include <subdev/pmu.h>
#include <subdev/timer.h>
#include <nvfw/acr.h>
#include <nvfw/flcn.h>
const struct nvkm_acr_func
gm200_acr = {
};
int
gm200_acr_nofw(struct nvkm_acr *acr, int ver, const struct nvkm_acr_fwif *fwif)
{
nvkm_warn(&acr->subdev, "firmware unavailable\n");
return 0;
}
int
gm200_acr_init(struct nvkm_acr *acr)
{
return nvkm_acr_hsfw_boot(acr, "load");
}
void
gm200_acr_wpr_check(struct nvkm_acr *acr, u64 *start, u64 *limit)
{
struct nvkm_device *device = acr->subdev.device;
nvkm_wr32(device, 0x100cd4, 2);
*start = (u64)(nvkm_rd32(device, 0x100cd4) & 0xffffff00) << 8;
nvkm_wr32(device, 0x100cd4, 3);
*limit = (u64)(nvkm_rd32(device, 0x100cd4) & 0xffffff00) << 8;
*limit = *limit + 0x20000;
}
int
gm200_acr_wpr_patch(struct nvkm_acr *acr, s64 adjust)
{
struct nvkm_subdev *subdev = &acr->subdev;
struct wpr_header hdr;
struct lsb_header lsb;
struct nvkm_acr_lsf *lsfw;
u32 offset = 0;
do {
nvkm_robj(acr->wpr, offset, &hdr, sizeof(hdr));
wpr_header_dump(subdev, &hdr);
list_for_each_entry(lsfw, &acr->lsfw, head) {
if (lsfw->id != hdr.falcon_id)
continue;
nvkm_robj(acr->wpr, hdr.lsb_offset, &lsb, sizeof(lsb));
lsb_header_dump(subdev, &lsb);
lsfw->func->bld_patch(acr, lsb.tail.bl_data_off, adjust);
break;
}
offset += sizeof(hdr);
} while (hdr.falcon_id != WPR_HEADER_V0_FALCON_ID_INVALID);
return 0;
}
void
gm200_acr_wpr_build_lsb_tail(struct nvkm_acr_lsfw *lsfw,
struct lsb_header_tail *hdr)
{
hdr->ucode_off = lsfw->offset.img;
hdr->ucode_size = lsfw->ucode_size;
hdr->data_size = lsfw->data_size;
hdr->bl_code_size = lsfw->bootloader_size;
hdr->bl_imem_off = lsfw->bootloader_imem_offset;
hdr->bl_data_off = lsfw->offset.bld;
hdr->bl_data_size = lsfw->bl_data_size;
hdr->app_code_off = lsfw->app_start_offset +
lsfw->app_resident_code_offset;
hdr->app_code_size = lsfw->app_resident_code_size;
hdr->app_data_off = lsfw->app_start_offset +
lsfw->app_resident_data_offset;
hdr->app_data_size = lsfw->app_resident_data_size;
hdr->flags = lsfw->func->flags;
}
static int
gm200_acr_wpr_build_lsb(struct nvkm_acr *acr, struct nvkm_acr_lsfw *lsfw)
{
struct lsb_header hdr;
if (WARN_ON(lsfw->sig->size != sizeof(hdr.signature)))
return -EINVAL;
memcpy(&hdr.signature, lsfw->sig->data, lsfw->sig->size);
gm200_acr_wpr_build_lsb_tail(lsfw, &hdr.tail);
nvkm_wobj(acr->wpr, lsfw->offset.lsb, &hdr, sizeof(hdr));
return 0;
}
int
gm200_acr_wpr_build(struct nvkm_acr *acr, struct nvkm_acr_lsf *rtos)
{
struct nvkm_acr_lsfw *lsfw;
u32 offset = 0;
int ret;
/* Fill per-LSF structures. */
list_for_each_entry(lsfw, &acr->lsfw, head) {
struct wpr_header hdr = {
.falcon_id = lsfw->id,
.lsb_offset = lsfw->offset.lsb,
.bootstrap_owner = NVKM_ACR_LSF_PMU,
.lazy_bootstrap = rtos && lsfw->id != rtos->id,
.status = WPR_HEADER_V0_STATUS_COPY,
};
/* Write WPR header. */
nvkm_wobj(acr->wpr, offset, &hdr, sizeof(hdr));
offset += sizeof(hdr);
/* Write LSB header. */
ret = gm200_acr_wpr_build_lsb(acr, lsfw);
if (ret)
return ret;
/* Write ucode image. */
nvkm_wobj(acr->wpr, lsfw->offset.img,
lsfw->img.data,
lsfw->img.size);
/* Write bootloader data. */
lsfw->func->bld_write(acr, lsfw->offset.bld, lsfw);
}
/* Finalise WPR. */
nvkm_wo32(acr->wpr, offset, WPR_HEADER_V0_FALCON_ID_INVALID);
return 0;
}
static int
gm200_acr_wpr_alloc(struct nvkm_acr *acr, u32 wpr_size)
{
int ret = nvkm_memory_new(acr->subdev.device, NVKM_MEM_TARGET_INST,
ALIGN(wpr_size, 0x40000), 0x40000, true,
&acr->wpr);
if (ret)
return ret;
acr->wpr_start = nvkm_memory_addr(acr->wpr);
acr->wpr_end = acr->wpr_start + nvkm_memory_size(acr->wpr);
return 0;
}
u32
gm200_acr_wpr_layout(struct nvkm_acr *acr)
{
struct nvkm_acr_lsfw *lsfw;
u32 wpr = 0;
wpr += 11 /* MAX_LSF */ * sizeof(struct wpr_header);
list_for_each_entry(lsfw, &acr->lsfw, head) {
wpr = ALIGN(wpr, 256);
lsfw->offset.lsb = wpr;
wpr += sizeof(struct lsb_header);
wpr = ALIGN(wpr, 4096);
lsfw->offset.img = wpr;
wpr += lsfw->img.size;
wpr = ALIGN(wpr, 256);
lsfw->offset.bld = wpr;
lsfw->bl_data_size = ALIGN(lsfw->func->bld_size, 256);
wpr += lsfw->bl_data_size;
}
return wpr;
}
int
gm200_acr_wpr_parse(struct nvkm_acr *acr)
{
const struct wpr_header *hdr = (void *)acr->wpr_fw->data;
struct nvkm_acr_lsfw *lsfw;
while (hdr->falcon_id != WPR_HEADER_V0_FALCON_ID_INVALID) {
wpr_header_dump(&acr->subdev, hdr);
lsfw = nvkm_acr_lsfw_add(NULL, acr, NULL, (hdr++)->falcon_id);
if (IS_ERR(lsfw))
return PTR_ERR(lsfw);
}
return 0;
}
int
gm200_acr_hsfw_load_bld(struct nvkm_falcon_fw *fw)
{
struct flcn_bl_dmem_desc_v1 hsdesc = {
.ctx_dma = FALCON_DMAIDX_VIRT,
.code_dma_base = fw->vma->addr,
.non_sec_code_off = fw->nmem_base,
.non_sec_code_size = fw->nmem_size,
.sec_code_off = fw->imem_base,
.sec_code_size = fw->imem_size,
.code_entry_point = 0,
.data_dma_base = fw->vma->addr + fw->dmem_base_img,
.data_size = fw->dmem_size,
};
flcn_bl_dmem_desc_v1_dump(fw->falcon->user, &hsdesc);
return nvkm_falcon_pio_wr(fw->falcon, (u8 *)&hsdesc, 0, 0, DMEM, 0, sizeof(hsdesc), 0, 0);
}
int
gm200_acr_hsfw_ctor(struct nvkm_acr *acr, const char *bl, const char *fw, const char *name, int ver,
const struct nvkm_acr_hsf_fwif *fwif)
{
struct nvkm_acr_hsfw *hsfw;
if (!(hsfw = kzalloc_obj(*hsfw)))
return -ENOMEM;
hsfw->falcon_id = fwif->falcon_id;
hsfw->boot_mbox0 = fwif->boot_mbox0;
hsfw->intr_clear = fwif->intr_clear;
list_add_tail(&hsfw->head, &acr->hsfw);
return nvkm_falcon_fw_ctor_hs(fwif->func, name, &acr->subdev, bl, fw, ver, NULL, &hsfw->fw);
}
const struct nvkm_falcon_fw_func
gm200_acr_unload_0 = {
.signature = gm200_flcn_fw_signature,
.reset = gm200_flcn_fw_reset,
.load = gm200_flcn_fw_load,
.load_bld = gm200_acr_hsfw_load_bld,
.boot = gm200_flcn_fw_boot,
};
MODULE_FIRMWARE("nvidia/gm200/acr/ucode_unload.bin");
MODULE_FIRMWARE("nvidia/gm204/acr/ucode_unload.bin");
MODULE_FIRMWARE("nvidia/gm206/acr/ucode_unload.bin");
MODULE_FIRMWARE("nvidia/gp100/acr/ucode_unload.bin");
static const struct nvkm_acr_hsf_fwif
gm200_acr_unload_fwif[] = {
{ 0, gm200_acr_hsfw_ctor, &gm200_acr_unload_0, NVKM_ACR_HSF_PMU, 0, 0x00000010 },
{}
};
static int
gm200_acr_load_setup(struct nvkm_falcon_fw *fw)
{
struct flcn_acr_desc *desc = (void *)&fw->fw.img[fw->dmem_base_img];
struct nvkm_acr *acr = fw->falcon->owner->device->acr;
desc->wpr_region_id = 1;
desc->regions.no_regions = 2;
desc->regions.region_props[0].start_addr = acr->wpr_start >> 8;
desc->regions.region_props[0].end_addr = acr->wpr_end >> 8;
desc->regions.region_props[0].region_id = 1;
desc->regions.region_props[0].read_mask = 0xf;
desc->regions.region_props[0].write_mask = 0xc;
desc->regions.region_props[0].client_mask = 0x2;
flcn_acr_desc_dump(&acr->subdev, desc);
return 0;
}
static const struct nvkm_falcon_fw_func
gm200_acr_load_0 = {
.signature = gm200_flcn_fw_signature,
.reset = gm200_flcn_fw_reset,
.setup = gm200_acr_load_setup,
.load = gm200_flcn_fw_load,
.load_bld = gm200_acr_hsfw_load_bld,
.boot = gm200_flcn_fw_boot,
};
MODULE_FIRMWARE("nvidia/gm200/acr/bl.bin");
MODULE_FIRMWARE("nvidia/gm200/acr/ucode_load.bin");
MODULE_FIRMWARE("nvidia/gm204/acr/bl.bin");
MODULE_FIRMWARE("nvidia/gm204/acr/ucode_load.bin");
MODULE_FIRMWARE("nvidia/gm206/acr/bl.bin");
MODULE_FIRMWARE("nvidia/gm206/acr/ucode_load.bin");
MODULE_FIRMWARE("nvidia/gp100/acr/bl.bin");
MODULE_FIRMWARE("nvidia/gp100/acr/ucode_load.bin");
static const struct nvkm_acr_hsf_fwif
gm200_acr_load_fwif[] = {
{ 0, gm200_acr_hsfw_ctor, &gm200_acr_load_0, NVKM_ACR_HSF_PMU, 0, 0x00000010 },
{}
};
static const struct nvkm_acr_func
gm200_acr_0 = {
.load = gm200_acr_load_fwif,
.unload = gm200_acr_unload_fwif,
.wpr_parse = gm200_acr_wpr_parse,
.wpr_layout = gm200_acr_wpr_layout,
.wpr_alloc = gm200_acr_wpr_alloc,
.wpr_build = gm200_acr_wpr_build,
.wpr_patch = gm200_acr_wpr_patch,
.wpr_check = gm200_acr_wpr_check,
.init = gm200_acr_init,
.bootstrap_falcons = BIT_ULL(NVKM_ACR_LSF_FECS) |
BIT_ULL(NVKM_ACR_LSF_GPCCS),
};
static int
gm200_acr_load(struct nvkm_acr *acr, int ver, const struct nvkm_acr_fwif *fwif)
{
struct nvkm_subdev *subdev = &acr->subdev;
const struct nvkm_acr_hsf_fwif *hsfwif;
hsfwif = nvkm_firmware_load(subdev, fwif->func->load, "AcrLoad",
acr, "acr/bl", "acr/ucode_load", "load");
if (IS_ERR(hsfwif))
return PTR_ERR(hsfwif);
hsfwif = nvkm_firmware_load(subdev, fwif->func->unload, "AcrUnload",
acr, "acr/bl", "acr/ucode_unload",
"unload");
if (IS_ERR(hsfwif))
return PTR_ERR(hsfwif);
return 0;
}
static const struct nvkm_acr_fwif
gm200_acr_fwif[] = {
{ 0, gm200_acr_load, &gm200_acr_0 },
{ -1, gm200_acr_nofw, &gm200_acr },
{}
};
int
gm200_acr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_acr **pacr)
{
return nvkm_acr_new_(gm200_acr_fwif, device, type, inst, pacr);
}
@@ -0,0 +1,140 @@
/*
* Copyright 2019 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "priv.h"
#include <core/firmware.h>
#include <core/memory.h>
#include <subdev/mmu.h>
#include <subdev/pmu.h>
#include <nvfw/acr.h>
#include <nvfw/flcn.h>
int
gm20b_acr_wpr_alloc(struct nvkm_acr *acr, u32 wpr_size)
{
struct nvkm_subdev *subdev = &acr->subdev;
acr->func->wpr_check(acr, &acr->wpr_start, &acr->wpr_end);
if ((acr->wpr_end - acr->wpr_start) < wpr_size) {
nvkm_error(subdev, "WPR image too big for WPR!\n");
return -ENOSPC;
}
return nvkm_memory_new(subdev->device, NVKM_MEM_TARGET_INST,
wpr_size, 0, true, &acr->wpr);
}
static int
gm20b_acr_hsfw_load_bld(struct nvkm_falcon_fw *fw)
{
struct flcn_bl_dmem_desc hsdesc = {
.ctx_dma = FALCON_DMAIDX_VIRT,
.code_dma_base = fw->vma->addr >> 8,
.non_sec_code_off = fw->nmem_base,
.non_sec_code_size = fw->nmem_size,
.sec_code_off = fw->imem_base,
.sec_code_size = fw->imem_size,
.code_entry_point = 0,
.data_dma_base = (fw->vma->addr + fw->dmem_base_img) >> 8,
.data_size = fw->dmem_size,
};
flcn_bl_dmem_desc_dump(fw->falcon->user, &hsdesc);
return nvkm_falcon_pio_wr(fw->falcon, (u8 *)&hsdesc, 0, 0, DMEM, 0, sizeof(hsdesc), 0, 0);
}
static int
gm20b_acr_load_setup(struct nvkm_falcon_fw *fw)
{
struct flcn_acr_desc *desc = (void *)&fw->fw.img[fw->dmem_base_img];
struct nvkm_acr *acr = fw->falcon->owner->device->acr;
desc->ucode_blob_base = nvkm_memory_addr(acr->wpr);
desc->ucode_blob_size = nvkm_memory_size(acr->wpr);
flcn_acr_desc_dump(&acr->subdev, desc);
return 0;
}
const struct nvkm_falcon_fw_func
gm20b_acr_load_0 = {
.signature = gm200_flcn_fw_signature,
.reset = gm200_flcn_fw_reset,
.setup = gm20b_acr_load_setup,
.load = gm200_flcn_fw_load,
.load_bld = gm20b_acr_hsfw_load_bld,
.boot = gm200_flcn_fw_boot,
};
#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC)
MODULE_FIRMWARE("nvidia/gm20b/acr/bl.bin");
MODULE_FIRMWARE("nvidia/gm20b/acr/ucode_load.bin");
#endif
static const struct nvkm_acr_hsf_fwif
gm20b_acr_load_fwif[] = {
{ 0, gm200_acr_hsfw_ctor, &gm20b_acr_load_0, NVKM_ACR_HSF_PMU, 0, 0x10 },
{}
};
static const struct nvkm_acr_func
gm20b_acr = {
.load = gm20b_acr_load_fwif,
.wpr_parse = gm200_acr_wpr_parse,
.wpr_layout = gm200_acr_wpr_layout,
.wpr_alloc = gm20b_acr_wpr_alloc,
.wpr_build = gm200_acr_wpr_build,
.wpr_patch = gm200_acr_wpr_patch,
.wpr_check = gm200_acr_wpr_check,
.init = gm200_acr_init,
};
int
gm20b_acr_load(struct nvkm_acr *acr, int ver, const struct nvkm_acr_fwif *fwif)
{
struct nvkm_subdev *subdev = &acr->subdev;
const struct nvkm_acr_hsf_fwif *hsfwif;
hsfwif = nvkm_firmware_load(subdev, fwif->func->load, "AcrLoad",
acr, "acr/bl", "acr/ucode_load", "load");
if (IS_ERR(hsfwif))
return PTR_ERR(hsfwif);
return 0;
}
static const struct nvkm_acr_fwif
gm20b_acr_fwif[] = {
{ 0, gm20b_acr_load, &gm20b_acr },
{ -1, gm200_acr_nofw, &gm200_acr },
{}
};
int
gm20b_acr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_acr **pacr)
{
return nvkm_acr_new_(gm20b_acr_fwif, device, type, inst, pacr);
}
@@ -0,0 +1,288 @@
/*
* Copyright 2019 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "priv.h"
#include <core/firmware.h>
#include <core/memory.h>
#include <subdev/mmu.h>
#include <engine/sec2.h>
#include <nvfw/acr.h>
#include <nvfw/flcn.h>
int
gp102_acr_wpr_patch(struct nvkm_acr *acr, s64 adjust)
{
struct wpr_header_v1 hdr;
struct lsb_header_v1 lsb;
struct nvkm_acr_lsfw *lsfw;
u32 offset = 0;
do {
nvkm_robj(acr->wpr, offset, &hdr, sizeof(hdr));
wpr_header_v1_dump(&acr->subdev, &hdr);
list_for_each_entry(lsfw, &acr->lsfw, head) {
if (lsfw->id != hdr.falcon_id)
continue;
nvkm_robj(acr->wpr, hdr.lsb_offset, &lsb, sizeof(lsb));
lsb_header_v1_dump(&acr->subdev, &lsb);
lsfw->func->bld_patch(acr, lsb.tail.bl_data_off, adjust);
break;
}
offset += sizeof(hdr);
} while (hdr.falcon_id != WPR_HEADER_V1_FALCON_ID_INVALID);
return 0;
}
int
gp102_acr_wpr_build_lsb(struct nvkm_acr *acr, struct nvkm_acr_lsfw *lsfw)
{
struct lsb_header_v1 hdr;
if (WARN_ON(lsfw->sig->size != sizeof(hdr.signature)))
return -EINVAL;
memcpy(&hdr.signature, lsfw->sig->data, lsfw->sig->size);
gm200_acr_wpr_build_lsb_tail(lsfw, &hdr.tail);
nvkm_wobj(acr->wpr, lsfw->offset.lsb, &hdr, sizeof(hdr));
return 0;
}
int
gp102_acr_wpr_build(struct nvkm_acr *acr, struct nvkm_acr_lsf *rtos)
{
struct nvkm_acr_lsfw *lsfw;
u32 offset = 0;
int ret;
/* Fill per-LSF structures. */
list_for_each_entry(lsfw, &acr->lsfw, head) {
struct lsf_signature_v1 *sig = (void *)lsfw->sig->data;
struct wpr_header_v1 hdr = {
.falcon_id = lsfw->id,
.lsb_offset = lsfw->offset.lsb,
.bootstrap_owner = NVKM_ACR_LSF_SEC2,
.lazy_bootstrap = rtos && lsfw->id != rtos->id,
.bin_version = sig->version,
.status = WPR_HEADER_V1_STATUS_COPY,
};
/* Write WPR header. */
nvkm_wobj(acr->wpr, offset, &hdr, sizeof(hdr));
offset += sizeof(hdr);
/* Write LSB header. */
ret = gp102_acr_wpr_build_lsb(acr, lsfw);
if (ret)
return ret;
/* Write ucode image. */
nvkm_wobj(acr->wpr, lsfw->offset.img,
lsfw->img.data,
lsfw->img.size);
/* Write bootloader data. */
lsfw->func->bld_write(acr, lsfw->offset.bld, lsfw);
}
/* Finalise WPR. */
nvkm_wo32(acr->wpr, offset, WPR_HEADER_V1_FALCON_ID_INVALID);
return 0;
}
int
gp102_acr_wpr_alloc(struct nvkm_acr *acr, u32 wpr_size)
{
int ret = nvkm_memory_new(acr->subdev.device, NVKM_MEM_TARGET_INST,
ALIGN(wpr_size, 0x40000) << 1, 0x40000, true,
&acr->wpr);
if (ret)
return ret;
acr->shadow_start = nvkm_memory_addr(acr->wpr);
acr->wpr_start = acr->shadow_start + (nvkm_memory_size(acr->wpr) >> 1);
acr->wpr_end = acr->wpr_start + (nvkm_memory_size(acr->wpr) >> 1);
return 0;
}
u32
gp102_acr_wpr_layout(struct nvkm_acr *acr)
{
struct nvkm_acr_lsfw *lsfw;
u32 wpr = 0;
wpr += 11 /* MAX_LSF */ * sizeof(struct wpr_header_v1);
wpr = ALIGN(wpr, 256);
wpr += 0x100; /* Shared sub-WPR headers. */
list_for_each_entry(lsfw, &acr->lsfw, head) {
wpr = ALIGN(wpr, 256);
lsfw->offset.lsb = wpr;
wpr += sizeof(struct lsb_header_v1);
wpr = ALIGN(wpr, 4096);
lsfw->offset.img = wpr;
wpr += lsfw->img.size;
wpr = ALIGN(wpr, 256);
lsfw->offset.bld = wpr;
lsfw->bl_data_size = ALIGN(lsfw->func->bld_size, 256);
wpr += lsfw->bl_data_size;
}
return wpr;
}
int
gp102_acr_wpr_parse(struct nvkm_acr *acr)
{
const struct wpr_header_v1 *hdr = (void *)acr->wpr_fw->data;
struct nvkm_acr_lsfw *lsfw;
while (hdr->falcon_id != WPR_HEADER_V1_FALCON_ID_INVALID) {
wpr_header_v1_dump(&acr->subdev, hdr);
lsfw = nvkm_acr_lsfw_add(NULL, acr, NULL, (hdr++)->falcon_id);
if (IS_ERR(lsfw))
return PTR_ERR(lsfw);
}
return 0;
}
MODULE_FIRMWARE("nvidia/gp102/acr/unload_bl.bin");
MODULE_FIRMWARE("nvidia/gp102/acr/ucode_unload.bin");
MODULE_FIRMWARE("nvidia/gp104/acr/unload_bl.bin");
MODULE_FIRMWARE("nvidia/gp104/acr/ucode_unload.bin");
MODULE_FIRMWARE("nvidia/gp106/acr/unload_bl.bin");
MODULE_FIRMWARE("nvidia/gp106/acr/ucode_unload.bin");
MODULE_FIRMWARE("nvidia/gp107/acr/unload_bl.bin");
MODULE_FIRMWARE("nvidia/gp107/acr/ucode_unload.bin");
static const struct nvkm_acr_hsf_fwif
gp102_acr_unload_fwif[] = {
{ 0, gm200_acr_hsfw_ctor, &gm200_acr_unload_0, NVKM_ACR_HSF_PMU, 0x1d, 0x00000010 },
{}
};
int
gp102_acr_load_setup(struct nvkm_falcon_fw *fw)
{
struct flcn_acr_desc_v1 *desc = (void *)&fw->fw.img[fw->dmem_base_img];
struct nvkm_acr *acr = fw->falcon->owner->device->acr;
desc->wpr_region_id = 1;
desc->regions.no_regions = 2;
desc->regions.region_props[0].start_addr = acr->wpr_start >> 8;
desc->regions.region_props[0].end_addr = acr->wpr_end >> 8;
desc->regions.region_props[0].region_id = 1;
desc->regions.region_props[0].read_mask = 0xf;
desc->regions.region_props[0].write_mask = 0xc;
desc->regions.region_props[0].client_mask = 0x2;
desc->regions.region_props[0].shadow_mem_start_addr = acr->shadow_start >> 8;
flcn_acr_desc_v1_dump(&acr->subdev, desc);
return 0;
}
static const struct nvkm_falcon_fw_func
gp102_acr_load_0 = {
.signature = gm200_flcn_fw_signature,
.reset = gm200_flcn_fw_reset,
.setup = gp102_acr_load_setup,
.load = gm200_flcn_fw_load,
.load_bld = gm200_acr_hsfw_load_bld,
.boot = gm200_flcn_fw_boot,
};
MODULE_FIRMWARE("nvidia/gp102/acr/bl.bin");
MODULE_FIRMWARE("nvidia/gp102/acr/ucode_load.bin");
MODULE_FIRMWARE("nvidia/gp104/acr/bl.bin");
MODULE_FIRMWARE("nvidia/gp104/acr/ucode_load.bin");
MODULE_FIRMWARE("nvidia/gp106/acr/bl.bin");
MODULE_FIRMWARE("nvidia/gp106/acr/ucode_load.bin");
MODULE_FIRMWARE("nvidia/gp107/acr/bl.bin");
MODULE_FIRMWARE("nvidia/gp107/acr/ucode_load.bin");
static const struct nvkm_acr_hsf_fwif
gp102_acr_load_fwif[] = {
{ 0, gm200_acr_hsfw_ctor, &gp102_acr_load_0, NVKM_ACR_HSF_SEC2, 0, 0x00000010 },
{}
};
static const struct nvkm_acr_func
gp102_acr = {
.load = gp102_acr_load_fwif,
.unload = gp102_acr_unload_fwif,
.wpr_parse = gp102_acr_wpr_parse,
.wpr_layout = gp102_acr_wpr_layout,
.wpr_alloc = gp102_acr_wpr_alloc,
.wpr_build = gp102_acr_wpr_build,
.wpr_patch = gp102_acr_wpr_patch,
.wpr_check = gm200_acr_wpr_check,
.init = gm200_acr_init,
};
int
gp102_acr_load(struct nvkm_acr *acr, int ver, const struct nvkm_acr_fwif *fwif)
{
struct nvkm_subdev *subdev = &acr->subdev;
const struct nvkm_acr_hsf_fwif *hsfwif;
hsfwif = nvkm_firmware_load(subdev, fwif->func->load, "AcrLoad",
acr, "acr/bl", "acr/ucode_load", "load");
if (IS_ERR(hsfwif))
return PTR_ERR(hsfwif);
hsfwif = nvkm_firmware_load(subdev, fwif->func->unload, "AcrUnload",
acr, "acr/unload_bl", "acr/ucode_unload",
"unload");
if (IS_ERR(hsfwif))
return PTR_ERR(hsfwif);
return 0;
}
static const struct nvkm_acr_fwif
gp102_acr_fwif[] = {
{ 0, gp102_acr_load, &gp102_acr },
{ -1, gm200_acr_nofw, &gm200_acr },
{}
};
int
gp102_acr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_acr **pacr)
{
return nvkm_acr_new_(gp102_acr_fwif, device, type, inst, pacr);
}
@@ -0,0 +1,112 @@
/*
* Copyright 2019 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "priv.h"
#include <subdev/mmu.h>
#include <nvfw/flcn.h>
int
gp108_acr_hsfw_load_bld(struct nvkm_falcon_fw *fw)
{
struct flcn_bl_dmem_desc_v2 hsdesc = {
.ctx_dma = FALCON_DMAIDX_VIRT,
.code_dma_base = fw->vma->addr,
.non_sec_code_off = fw->nmem_base,
.non_sec_code_size = fw->nmem_size,
.sec_code_off = fw->imem_base,
.sec_code_size = fw->imem_size,
.code_entry_point = 0,
.data_dma_base = fw->vma->addr + fw->dmem_base_img,
.data_size = fw->dmem_size,
.argc = 0,
.argv = 0,
};
flcn_bl_dmem_desc_v2_dump(fw->falcon->user, &hsdesc);
return nvkm_falcon_pio_wr(fw->falcon, (u8 *)&hsdesc, 0, 0, DMEM, 0, sizeof(hsdesc), 0, 0);
}
const struct nvkm_falcon_fw_func
gp108_acr_hsfw_0 = {
.signature = gm200_flcn_fw_signature,
.reset = gm200_flcn_fw_reset,
.load = gm200_flcn_fw_load,
.load_bld = gp108_acr_hsfw_load_bld,
.boot = gm200_flcn_fw_boot,
};
MODULE_FIRMWARE("nvidia/gp108/acr/unload_bl.bin");
MODULE_FIRMWARE("nvidia/gp108/acr/ucode_unload.bin");
static const struct nvkm_acr_hsf_fwif
gp108_acr_unload_fwif[] = {
{ 0, gm200_acr_hsfw_ctor, &gp108_acr_hsfw_0, NVKM_ACR_HSF_PMU, 0x1d, 0x00000010 },
{}
};
const struct nvkm_falcon_fw_func
gp108_acr_load_0 = {
.signature = gm200_flcn_fw_signature,
.reset = gm200_flcn_fw_reset,
.setup = gp102_acr_load_setup,
.load = gm200_flcn_fw_load,
.load_bld = gp108_acr_hsfw_load_bld,
.boot = gm200_flcn_fw_boot,
};
MODULE_FIRMWARE("nvidia/gp108/acr/bl.bin");
MODULE_FIRMWARE("nvidia/gp108/acr/ucode_load.bin");
static const struct nvkm_acr_hsf_fwif
gp108_acr_load_fwif[] = {
{ 0, gm200_acr_hsfw_ctor, &gp108_acr_load_0, NVKM_ACR_HSF_SEC2, 0, 0x00000010 },
{}
};
static const struct nvkm_acr_func
gp108_acr = {
.load = gp108_acr_load_fwif,
.unload = gp108_acr_unload_fwif,
.wpr_parse = gp102_acr_wpr_parse,
.wpr_layout = gp102_acr_wpr_layout,
.wpr_alloc = gp102_acr_wpr_alloc,
.wpr_build = gp102_acr_wpr_build,
.wpr_patch = gp102_acr_wpr_patch,
.wpr_check = gm200_acr_wpr_check,
.init = gm200_acr_init,
};
static const struct nvkm_acr_fwif
gp108_acr_fwif[] = {
{ 0, gp102_acr_load, &gp108_acr },
{ -1, gm200_acr_nofw, &gm200_acr },
{}
};
int
gp108_acr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_acr **pacr)
{
return nvkm_acr_new_(gp108_acr_fwif, device, type, inst, pacr);
}
@@ -0,0 +1,59 @@
/*
* Copyright 2019 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "priv.h"
#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC)
MODULE_FIRMWARE("nvidia/gp10b/acr/bl.bin");
MODULE_FIRMWARE("nvidia/gp10b/acr/ucode_load.bin");
#endif
static const struct nvkm_acr_hsf_fwif
gp10b_acr_load_fwif[] = {
{ 0, gm200_acr_hsfw_ctor, &gm20b_acr_load_0, NVKM_ACR_HSF_PMU, 0, 0x00000010 },
{}
};
static const struct nvkm_acr_func
gp10b_acr = {
.load = gp10b_acr_load_fwif,
.wpr_parse = gm200_acr_wpr_parse,
.wpr_layout = gm200_acr_wpr_layout,
.wpr_alloc = gm20b_acr_wpr_alloc,
.wpr_build = gm200_acr_wpr_build,
.wpr_patch = gm200_acr_wpr_patch,
.wpr_check = gm200_acr_wpr_check,
.init = gm200_acr_init,
};
static const struct nvkm_acr_fwif
gp10b_acr_fwif[] = {
{ 0, gm20b_acr_load, &gp10b_acr },
{ -1, gm200_acr_nofw, &gm200_acr },
{}
};
int
gp10b_acr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_acr **pacr)
{
return nvkm_acr_new_(gp10b_acr_fwif, device, type, inst, pacr);
}
@@ -0,0 +1,67 @@
/*
* Copyright 2022 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "priv.h"
MODULE_FIRMWARE("nvidia/gv100/acr/unload_bl.bin");
MODULE_FIRMWARE("nvidia/gv100/acr/ucode_unload.bin");
static const struct nvkm_acr_hsf_fwif
gv100_acr_unload_fwif[] = {
{ 0, gm200_acr_hsfw_ctor, &gp108_acr_hsfw_0, NVKM_ACR_HSF_PMU, 0, 0x00000000 },
{}
};
MODULE_FIRMWARE("nvidia/gv100/acr/bl.bin");
MODULE_FIRMWARE("nvidia/gv100/acr/ucode_load.bin");
static const struct nvkm_acr_hsf_fwif
gv100_acr_load_fwif[] = {
{ 0, gm200_acr_hsfw_ctor, &gp108_acr_load_0, NVKM_ACR_HSF_SEC2, 0, 0x00000010 },
{}
};
static const struct nvkm_acr_func
gv100_acr = {
.load = gv100_acr_load_fwif,
.unload = gv100_acr_unload_fwif,
.wpr_parse = gp102_acr_wpr_parse,
.wpr_layout = gp102_acr_wpr_layout,
.wpr_alloc = gp102_acr_wpr_alloc,
.wpr_build = gp102_acr_wpr_build,
.wpr_patch = gp102_acr_wpr_patch,
.wpr_check = gm200_acr_wpr_check,
.init = gm200_acr_init,
};
static const struct nvkm_acr_fwif
gv100_acr_fwif[] = {
{ 0, gp102_acr_load, &gv100_acr },
{ -1, gm200_acr_nofw, &gm200_acr },
{}
};
int
gv100_acr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_acr **pacr)
{
return nvkm_acr_new_(gv100_acr_fwif, device, type, inst, pacr);
}
@@ -0,0 +1,397 @@
/*
* Copyright 2019 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "priv.h"
#include <core/falcon.h>
#include <core/firmware.h>
#include <nvfw/fw.h>
#include <nvfw/ls.h>
void
nvkm_acr_lsfw_del(struct nvkm_acr_lsfw *lsfw)
{
nvkm_blob_dtor(&lsfw->img);
kfree(lsfw->sigs);
nvkm_firmware_put(lsfw->sig);
list_del(&lsfw->head);
kfree(lsfw);
}
void
nvkm_acr_lsfw_del_all(struct nvkm_acr *acr)
{
struct nvkm_acr_lsfw *lsfw, *lsft;
list_for_each_entry_safe(lsfw, lsft, &acr->lsfw, head) {
nvkm_acr_lsfw_del(lsfw);
}
}
static struct nvkm_acr_lsfw *
nvkm_acr_lsfw_get(struct nvkm_acr *acr, enum nvkm_acr_lsf_id id)
{
struct nvkm_acr_lsfw *lsfw;
list_for_each_entry(lsfw, &acr->lsfw, head) {
if (lsfw->id == id)
return lsfw;
}
return NULL;
}
struct nvkm_acr_lsfw *
nvkm_acr_lsfw_add(const struct nvkm_acr_lsf_func *func, struct nvkm_acr *acr,
struct nvkm_falcon *falcon, enum nvkm_acr_lsf_id id)
{
struct nvkm_acr_lsfw *lsfw;
if (!acr || list_empty(&acr->hsfw))
return ERR_PTR(-ENOSYS);
lsfw = nvkm_acr_lsfw_get(acr, id);
if (lsfw && lsfw->func) {
nvkm_error(&acr->subdev, "LSFW %d redefined\n", id);
return ERR_PTR(-EEXIST);
}
if (!lsfw) {
if (!(lsfw = kzalloc_obj(*lsfw)))
return ERR_PTR(-ENOMEM);
lsfw->id = id;
list_add_tail(&lsfw->head, &acr->lsfw);
}
lsfw->func = func;
lsfw->falcon = falcon;
return lsfw;
}
static struct nvkm_acr_lsfw *
nvkm_acr_lsfw_load_sig_image_desc_(struct nvkm_subdev *subdev,
struct nvkm_falcon *falcon,
enum nvkm_acr_lsf_id id,
const char *path, int ver,
const struct nvkm_acr_lsf_func *func,
const struct firmware **pdesc)
{
struct nvkm_acr *acr = subdev->device->acr;
struct nvkm_acr_lsfw *lsfw;
int ret;
if (IS_ERR((lsfw = nvkm_acr_lsfw_add(func, acr, falcon, id))))
return lsfw;
ret = nvkm_firmware_load_name(subdev, path, "sig", ver, &lsfw->sig);
if (ret)
goto done;
ret = nvkm_firmware_load_blob(subdev, path, "image", ver, &lsfw->img);
if (ret)
goto done;
ret = nvkm_firmware_load_name(subdev, path, "desc", ver, pdesc);
done:
if (ret) {
nvkm_acr_lsfw_del(lsfw);
return ERR_PTR(ret);
}
return lsfw;
}
static void
nvkm_acr_lsfw_from_desc(const struct nvfw_ls_desc_head *desc,
struct nvkm_acr_lsfw *lsfw)
{
lsfw->bootloader_size = ALIGN(desc->bootloader_size, 256);
lsfw->bootloader_imem_offset = desc->bootloader_imem_offset;
lsfw->app_size = ALIGN(desc->app_size, 256);
lsfw->app_start_offset = desc->app_start_offset;
lsfw->app_imem_entry = desc->app_imem_entry;
lsfw->app_resident_code_offset = desc->app_resident_code_offset;
lsfw->app_resident_code_size = desc->app_resident_code_size;
lsfw->app_resident_data_offset = desc->app_resident_data_offset;
lsfw->app_resident_data_size = desc->app_resident_data_size;
lsfw->ucode_size = ALIGN(lsfw->app_resident_data_offset, 256) +
lsfw->bootloader_size;
lsfw->data_size = lsfw->app_size + lsfw->bootloader_size -
lsfw->ucode_size;
}
int
nvkm_acr_lsfw_load_sig_image_desc(struct nvkm_subdev *subdev,
struct nvkm_falcon *falcon,
enum nvkm_acr_lsf_id id,
const char *path, int ver,
const struct nvkm_acr_lsf_func *func)
{
const struct firmware *fw;
struct nvkm_acr_lsfw *lsfw;
lsfw = nvkm_acr_lsfw_load_sig_image_desc_(subdev, falcon, id, path, ver,
func, &fw);
if (IS_ERR(lsfw))
return PTR_ERR(lsfw);
nvkm_acr_lsfw_from_desc(&nvfw_ls_desc(subdev, fw->data)->head, lsfw);
nvkm_firmware_put(fw);
return 0;
}
int
nvkm_acr_lsfw_load_sig_image_desc_v1(struct nvkm_subdev *subdev,
struct nvkm_falcon *falcon,
enum nvkm_acr_lsf_id id,
const char *path, int ver,
const struct nvkm_acr_lsf_func *func)
{
const struct firmware *fw;
struct nvkm_acr_lsfw *lsfw;
lsfw = nvkm_acr_lsfw_load_sig_image_desc_(subdev, falcon, id, path, ver,
func, &fw);
if (IS_ERR(lsfw))
return PTR_ERR(lsfw);
nvkm_acr_lsfw_from_desc(&nvfw_ls_desc_v1(subdev, fw->data)->head, lsfw);
nvkm_firmware_put(fw);
return 0;
}
int
nvkm_acr_lsfw_load_sig_image_desc_v2(struct nvkm_subdev *subdev,
struct nvkm_falcon *falcon,
enum nvkm_acr_lsf_id id,
const char *path, int ver,
const struct nvkm_acr_lsf_func *func)
{
const struct firmware *fw;
struct nvkm_acr_lsfw *lsfw;
const struct nvfw_ls_desc_v2 *desc;
int ret = 0;
lsfw = nvkm_acr_lsfw_load_sig_image_desc_(subdev, falcon, id, path, ver, func, &fw);
if (IS_ERR(lsfw))
return PTR_ERR(lsfw);
desc = nvfw_ls_desc_v2(subdev, fw->data);
lsfw->secure_bootloader = desc->secure_bootloader;
lsfw->bootloader_size = ALIGN(desc->bootloader_size, 256);
lsfw->bootloader_imem_offset = desc->bootloader_imem_offset;
lsfw->app_size = ALIGN(desc->app_size, 256);
lsfw->app_start_offset = desc->app_start_offset;
lsfw->app_imem_entry = desc->app_imem_entry;
lsfw->app_resident_code_offset = desc->app_resident_code_offset;
lsfw->app_resident_code_size = desc->app_resident_code_size;
lsfw->app_resident_data_offset = desc->app_resident_data_offset;
lsfw->app_resident_data_size = desc->app_resident_data_size;
lsfw->app_imem_offset = desc->app_imem_offset;
lsfw->app_dmem_offset = desc->app_dmem_offset;
lsfw->ucode_size = ALIGN(lsfw->app_resident_data_offset, 256) + lsfw->bootloader_size;
lsfw->data_size = lsfw->app_size + lsfw->bootloader_size - lsfw->ucode_size;
nvkm_firmware_put(fw);
if (lsfw->secure_bootloader) {
const struct firmware *hsbl;
const struct nvfw_ls_hsbl_bin_hdr *hdr;
const struct nvfw_ls_hsbl_hdr *hshdr;
u32 sig, cnt, *meta;
ret = nvkm_firmware_load_name(subdev, path, "hs_bl_sig", ver, &hsbl);
if (ret)
return ret;
hdr = nvfw_ls_hsbl_bin_hdr(subdev, hsbl->data);
hshdr = nvfw_ls_hsbl_hdr(subdev, hsbl->data + hdr->header_offset);
meta = (u32 *)(hsbl->data + hshdr->meta_data_offset);
sig = *(u32 *)(hsbl->data + hshdr->patch_sig);
cnt = *(u32 *)(hsbl->data + hshdr->num_sig);
lsfw->fuse_ver = meta[0];
lsfw->engine_id = meta[1];
lsfw->ucode_id = meta[2];
lsfw->sig_size = hshdr->sig_prod_size / cnt;
lsfw->sig_nr = cnt;
lsfw->sigs = kmemdup(hsbl->data + hshdr->sig_prod_offset + sig,
lsfw->sig_nr * lsfw->sig_size, GFP_KERNEL);
nvkm_firmware_put(hsbl);
if (!lsfw->sigs)
ret = -ENOMEM;
}
return ret;
}
int
nvkm_acr_lsfw_load_bl_inst_data_sig(struct nvkm_subdev *subdev,
struct nvkm_falcon *falcon,
enum nvkm_acr_lsf_id id,
const char *path, int ver,
const struct nvkm_acr_lsf_func *func)
{
struct nvkm_acr *acr = subdev->device->acr;
struct nvkm_acr_lsfw *lsfw;
const struct firmware *bl = NULL, *inst = NULL, *data = NULL;
const struct nvfw_bin_hdr *hdr;
const struct nvfw_bl_desc *desc;
u32 *bldata;
int ret;
if (IS_ERR((lsfw = nvkm_acr_lsfw_add(func, acr, falcon, id))))
return PTR_ERR(lsfw);
ret = nvkm_firmware_load_name(subdev, path, "bl", ver, &bl);
if (ret)
goto done;
hdr = nvfw_bin_hdr(subdev, bl->data);
desc = nvfw_bl_desc(subdev, bl->data + hdr->header_offset);
bldata = (void *)(bl->data + hdr->data_offset);
ret = nvkm_firmware_load_name(subdev, path, "inst", ver, &inst);
if (ret)
goto done;
ret = nvkm_firmware_load_name(subdev, path, "data", ver, &data);
if (ret)
goto done;
ret = nvkm_firmware_load_name(subdev, path, "sig", ver, &lsfw->sig);
if (ret)
goto done;
lsfw->bootloader_size = ALIGN(desc->code_size, 256);
lsfw->bootloader_imem_offset = desc->start_tag << 8;
lsfw->app_start_offset = lsfw->bootloader_size;
lsfw->app_imem_entry = 0;
lsfw->app_resident_code_offset = 0;
lsfw->app_resident_code_size = ALIGN(inst->size, 256);
lsfw->app_resident_data_offset = lsfw->app_resident_code_size;
lsfw->app_resident_data_size = ALIGN(data->size, 256);
lsfw->app_size = lsfw->app_resident_code_size +
lsfw->app_resident_data_size;
lsfw->img.size = lsfw->bootloader_size + lsfw->app_size;
if (!(lsfw->img.data = kzalloc(lsfw->img.size, GFP_KERNEL))) {
ret = -ENOMEM;
goto done;
}
memcpy(lsfw->img.data, bldata, lsfw->bootloader_size);
memcpy(lsfw->img.data + lsfw->app_start_offset +
lsfw->app_resident_code_offset, inst->data, inst->size);
memcpy(lsfw->img.data + lsfw->app_start_offset +
lsfw->app_resident_data_offset, data->data, data->size);
lsfw->ucode_size = ALIGN(lsfw->app_resident_data_offset, 256) +
lsfw->bootloader_size;
lsfw->data_size = lsfw->app_size + lsfw->bootloader_size -
lsfw->ucode_size;
done:
if (ret)
nvkm_acr_lsfw_del(lsfw);
nvkm_firmware_put(data);
nvkm_firmware_put(inst);
nvkm_firmware_put(bl);
return ret;
}
int
nvkm_acr_lsfw_load_bl_sig_net(struct nvkm_subdev *subdev,
struct nvkm_falcon *falcon,
enum nvkm_acr_lsf_id id,
const char *path, int ver,
const struct nvkm_acr_lsf_func *func,
const void *inst_data, u32 inst_size,
const void *data_data, u32 data_size)
{
struct nvkm_acr *acr = subdev->device->acr;
struct nvkm_acr_lsfw *lsfw;
const struct firmware _inst = { .data = inst_data, .size = inst_size };
const struct firmware _data = { .data = data_data, .size = data_size };
const struct firmware *bl = NULL, *inst = &_inst, *data = &_data;
const struct {
int bin_magic;
int bin_version;
int bin_size;
int header_offset;
int header_size;
} *hdr;
u32 *bldata;
int ret;
if (IS_ERR((lsfw = nvkm_acr_lsfw_add(func, acr, falcon, id))))
return PTR_ERR(lsfw);
ret = nvkm_firmware_load_name(subdev, path, "bl", ver, &bl);
if (ret)
goto done;
hdr = (const void *)bl->data;
bldata = (void *)(bl->data + hdr->header_offset);
ret = nvkm_firmware_load_name(subdev, path, "sig", ver, &lsfw->sig);
if (ret)
goto done;
lsfw->bootloader_size = ALIGN(hdr->header_size, 256);
lsfw->bootloader_imem_offset = func->bl_entry;
lsfw->app_start_offset = lsfw->bootloader_size;
lsfw->app_imem_entry = 0;
lsfw->app_resident_code_offset = 0;
lsfw->app_resident_code_size = ALIGN(inst->size, 256);
lsfw->app_resident_data_offset = lsfw->app_resident_code_size;
lsfw->app_resident_data_size = ALIGN(data->size, 256);
lsfw->app_imem_offset = 0;
lsfw->app_dmem_offset = 0;
lsfw->app_size = lsfw->app_resident_code_size + lsfw->app_resident_data_size;
lsfw->img.size = lsfw->bootloader_size + lsfw->app_size;
if (!(lsfw->img.data = kzalloc(lsfw->img.size, GFP_KERNEL))) {
ret = -ENOMEM;
goto done;
}
memcpy(lsfw->img.data, bldata, lsfw->bootloader_size);
memcpy(lsfw->img.data + lsfw->app_start_offset +
lsfw->app_resident_code_offset, inst->data, inst->size);
memcpy(lsfw->img.data + lsfw->app_start_offset +
lsfw->app_resident_data_offset, data->data, data->size);
lsfw->ucode_size = ALIGN(lsfw->app_resident_data_offset, 256) +
lsfw->bootloader_size;
lsfw->data_size = lsfw->app_size + lsfw->bootloader_size -
lsfw->ucode_size;
done:
if (ret)
nvkm_acr_lsfw_del(lsfw);
nvkm_firmware_put(bl);
return ret;
}
@@ -0,0 +1,118 @@
#ifndef __NVKM_ACR_PRIV_H__
#define __NVKM_ACR_PRIV_H__
#include <subdev/acr.h>
struct lsb_header_tail;
struct nvkm_acr_fwif {
int version;
int (*load)(struct nvkm_acr *, int version,
const struct nvkm_acr_fwif *);
const struct nvkm_acr_func *func;
};
int gm200_acr_nofw(struct nvkm_acr *, int, const struct nvkm_acr_fwif *);
int gm20b_acr_load(struct nvkm_acr *, int, const struct nvkm_acr_fwif *);
int gp102_acr_load(struct nvkm_acr *, int, const struct nvkm_acr_fwif *);
struct nvkm_acr_lsf;
struct nvkm_acr_func {
const struct nvkm_acr_hsf_fwif *load;
const struct nvkm_acr_hsf_fwif *ahesasc;
const struct nvkm_acr_hsf_fwif *asb;
const struct nvkm_acr_hsf_fwif *unload;
int (*wpr_parse)(struct nvkm_acr *);
u32 (*wpr_layout)(struct nvkm_acr *);
int (*wpr_alloc)(struct nvkm_acr *, u32 wpr_size);
int (*wpr_build)(struct nvkm_acr *, struct nvkm_acr_lsf *rtos);
int (*wpr_patch)(struct nvkm_acr *, s64 adjust);
void (*wpr_check)(struct nvkm_acr *, u64 *start, u64 *limit);
int (*init)(struct nvkm_acr *);
void (*fini)(struct nvkm_acr *);
u64 bootstrap_falcons;
};
extern const struct nvkm_acr_func gm200_acr;
int gm200_acr_wpr_parse(struct nvkm_acr *);
u32 gm200_acr_wpr_layout(struct nvkm_acr *);
int gm200_acr_wpr_build(struct nvkm_acr *, struct nvkm_acr_lsf *);
int gm200_acr_wpr_patch(struct nvkm_acr *, s64);
void gm200_acr_wpr_check(struct nvkm_acr *, u64 *, u64 *);
void gm200_acr_wpr_build_lsb_tail(struct nvkm_acr_lsfw *,
struct lsb_header_tail *);
int gm200_acr_init(struct nvkm_acr *);
int gm20b_acr_wpr_alloc(struct nvkm_acr *, u32 wpr_size);
int gp102_acr_wpr_parse(struct nvkm_acr *);
u32 gp102_acr_wpr_layout(struct nvkm_acr *);
int gp102_acr_wpr_alloc(struct nvkm_acr *, u32 wpr_size);
int gp102_acr_wpr_build(struct nvkm_acr *, struct nvkm_acr_lsf *);
int gp102_acr_wpr_build_lsb(struct nvkm_acr *, struct nvkm_acr_lsfw *);
int gp102_acr_wpr_patch(struct nvkm_acr *, s64);
int tu102_acr_init(struct nvkm_acr *);
void ga100_acr_wpr_check(struct nvkm_acr *, u64 *, u64 *);
struct nvkm_acr_hsfw {
struct nvkm_falcon_fw fw;
enum nvkm_acr_hsf_id {
NVKM_ACR_HSF_PMU,
NVKM_ACR_HSF_SEC2,
NVKM_ACR_HSF_GSP,
} falcon_id;
u32 boot_mbox0;
u32 intr_clear;
struct list_head head;
};
int nvkm_acr_hsfw_boot(struct nvkm_acr *, const char *name);
struct nvkm_acr_hsf_fwif {
int version;
int (*load)(struct nvkm_acr *, const char *bl, const char *fw,
const char *name, int version,
const struct nvkm_acr_hsf_fwif *);
const struct nvkm_falcon_fw_func *func;
enum nvkm_acr_hsf_id falcon_id;
u32 boot_mbox0;
u32 intr_clear;
};
int gm200_acr_hsfw_ctor(struct nvkm_acr *, const char *, const char *, const char *, int,
const struct nvkm_acr_hsf_fwif *);
int gm200_acr_hsfw_load_bld(struct nvkm_falcon_fw *);
extern const struct nvkm_falcon_fw_func gm200_acr_unload_0;
extern const struct nvkm_falcon_fw_func gm20b_acr_load_0;
int gp102_acr_load_setup(struct nvkm_falcon_fw *);
extern const struct nvkm_falcon_fw_func gp108_acr_load_0;
extern const struct nvkm_falcon_fw_func gp108_acr_hsfw_0;
int gp108_acr_hsfw_load_bld(struct nvkm_falcon_fw *);
int ga100_acr_hsfw_ctor(struct nvkm_acr *, const char *, const char *, const char *, int,
const struct nvkm_acr_hsf_fwif *);
int nvkm_acr_new_(const struct nvkm_acr_fwif *, struct nvkm_device *, enum nvkm_subdev_type,
int inst, struct nvkm_acr **);
struct nvkm_acr_lsf {
const struct nvkm_acr_lsf_func *func;
struct nvkm_falcon *falcon;
enum nvkm_acr_lsf_id id;
struct list_head head;
};
struct nvkm_acr_lsfw *nvkm_acr_lsfw_add(const struct nvkm_acr_lsf_func *,
struct nvkm_acr *, struct nvkm_falcon *,
enum nvkm_acr_lsf_id);
void nvkm_acr_lsfw_del(struct nvkm_acr_lsfw *);
void nvkm_acr_lsfw_del_all(struct nvkm_acr *);
#endif
@@ -0,0 +1,208 @@
/*
* Copyright 2019 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "priv.h"
#include <core/firmware.h>
#include <core/memory.h>
#include <subdev/gsp.h>
#include <subdev/pmu.h>
#include <engine/sec2.h>
#include <nvfw/acr.h>
int
tu102_acr_init(struct nvkm_acr *acr)
{
int ret = nvkm_acr_hsfw_boot(acr, "AHESASC");
if (ret)
return ret;
return nvkm_acr_hsfw_boot(acr, "ASB");
}
static int
tu102_acr_wpr_build(struct nvkm_acr *acr, struct nvkm_acr_lsf *rtos)
{
struct nvkm_acr_lsfw *lsfw;
u32 offset = 0;
int ret;
/*XXX: shared sub-WPR headers, fill terminator for now. */
nvkm_wo32(acr->wpr, 0x200, 0xffffffff);
/* Fill per-LSF structures. */
list_for_each_entry(lsfw, &acr->lsfw, head) {
struct lsf_signature_v1 *sig = (void *)lsfw->sig->data;
struct wpr_header_v1 hdr = {
.falcon_id = lsfw->id,
.lsb_offset = lsfw->offset.lsb,
.bootstrap_owner = NVKM_ACR_LSF_GSPLITE,
.lazy_bootstrap = 1,
.bin_version = sig->version,
.status = WPR_HEADER_V1_STATUS_COPY,
};
/* Write WPR header. */
nvkm_wobj(acr->wpr, offset, &hdr, sizeof(hdr));
offset += sizeof(hdr);
/* Write LSB header. */
ret = gp102_acr_wpr_build_lsb(acr, lsfw);
if (ret)
return ret;
/* Write ucode image. */
nvkm_wobj(acr->wpr, lsfw->offset.img,
lsfw->img.data,
lsfw->img.size);
/* Write bootloader data. */
lsfw->func->bld_write(acr, lsfw->offset.bld, lsfw);
}
/* Finalise WPR. */
nvkm_wo32(acr->wpr, offset, WPR_HEADER_V1_FALCON_ID_INVALID);
return 0;
}
static int
tu102_acr_hsfw_nofw(struct nvkm_acr *acr, const char *bl, const char *fw,
const char *name, int version,
const struct nvkm_acr_hsf_fwif *fwif)
{
return 0;
}
MODULE_FIRMWARE("nvidia/tu102/acr/unload_bl.bin");
MODULE_FIRMWARE("nvidia/tu102/acr/ucode_unload.bin");
MODULE_FIRMWARE("nvidia/tu104/acr/unload_bl.bin");
MODULE_FIRMWARE("nvidia/tu104/acr/ucode_unload.bin");
MODULE_FIRMWARE("nvidia/tu106/acr/unload_bl.bin");
MODULE_FIRMWARE("nvidia/tu106/acr/ucode_unload.bin");
MODULE_FIRMWARE("nvidia/tu116/acr/unload_bl.bin");
MODULE_FIRMWARE("nvidia/tu116/acr/ucode_unload.bin");
MODULE_FIRMWARE("nvidia/tu117/acr/unload_bl.bin");
MODULE_FIRMWARE("nvidia/tu117/acr/ucode_unload.bin");
static const struct nvkm_acr_hsf_fwif
tu102_acr_unload_fwif[] = {
{ 0, gm200_acr_hsfw_ctor, &gp108_acr_hsfw_0, NVKM_ACR_HSF_PMU, 0, 0x00000000 },
{ -1, tu102_acr_hsfw_nofw },
{}
};
MODULE_FIRMWARE("nvidia/tu102/acr/ucode_asb.bin");
MODULE_FIRMWARE("nvidia/tu104/acr/ucode_asb.bin");
MODULE_FIRMWARE("nvidia/tu106/acr/ucode_asb.bin");
MODULE_FIRMWARE("nvidia/tu116/acr/ucode_asb.bin");
MODULE_FIRMWARE("nvidia/tu117/acr/ucode_asb.bin");
static const struct nvkm_acr_hsf_fwif
tu102_acr_asb_fwif[] = {
{ 0, gm200_acr_hsfw_ctor, &gp108_acr_hsfw_0, NVKM_ACR_HSF_GSP, 0, 0x00000000 },
{ -1, tu102_acr_hsfw_nofw },
{}
};
MODULE_FIRMWARE("nvidia/tu102/acr/bl.bin");
MODULE_FIRMWARE("nvidia/tu102/acr/ucode_ahesasc.bin");
MODULE_FIRMWARE("nvidia/tu104/acr/bl.bin");
MODULE_FIRMWARE("nvidia/tu104/acr/ucode_ahesasc.bin");
MODULE_FIRMWARE("nvidia/tu106/acr/bl.bin");
MODULE_FIRMWARE("nvidia/tu106/acr/ucode_ahesasc.bin");
MODULE_FIRMWARE("nvidia/tu116/acr/bl.bin");
MODULE_FIRMWARE("nvidia/tu116/acr/ucode_ahesasc.bin");
MODULE_FIRMWARE("nvidia/tu117/acr/bl.bin");
MODULE_FIRMWARE("nvidia/tu117/acr/ucode_ahesasc.bin");
static const struct nvkm_acr_hsf_fwif
tu102_acr_ahesasc_fwif[] = {
{ 0, gm200_acr_hsfw_ctor, &gp108_acr_load_0, NVKM_ACR_HSF_SEC2, 0, 0x00000000 },
{ -1, tu102_acr_hsfw_nofw },
{}
};
static const struct nvkm_acr_func
tu102_acr = {
.ahesasc = tu102_acr_ahesasc_fwif,
.asb = tu102_acr_asb_fwif,
.unload = tu102_acr_unload_fwif,
.wpr_parse = gp102_acr_wpr_parse,
.wpr_layout = gp102_acr_wpr_layout,
.wpr_alloc = gp102_acr_wpr_alloc,
.wpr_patch = gp102_acr_wpr_patch,
.wpr_build = tu102_acr_wpr_build,
.wpr_check = gm200_acr_wpr_check,
.init = tu102_acr_init,
};
static int
tu102_acr_load(struct nvkm_acr *acr, int version,
const struct nvkm_acr_fwif *fwif)
{
struct nvkm_subdev *subdev = &acr->subdev;
const struct nvkm_acr_hsf_fwif *hsfwif;
hsfwif = nvkm_firmware_load(subdev, fwif->func->ahesasc, "AcrAHESASC",
acr, "acr/bl", "acr/ucode_ahesasc",
"AHESASC");
if (IS_ERR(hsfwif))
return PTR_ERR(hsfwif);
hsfwif = nvkm_firmware_load(subdev, fwif->func->asb, "AcrASB",
acr, "acr/bl", "acr/ucode_asb", "ASB");
if (IS_ERR(hsfwif))
return PTR_ERR(hsfwif);
hsfwif = nvkm_firmware_load(subdev, fwif->func->unload, "AcrUnload",
acr, "acr/unload_bl", "acr/ucode_unload",
"unload");
if (IS_ERR(hsfwif))
return PTR_ERR(hsfwif);
return 0;
}
static const struct nvkm_acr_fwif
tu102_acr_fwif[] = {
{ 0, tu102_acr_load, &tu102_acr },
{ -1, gm200_acr_nofw, &gm200_acr },
{}
};
int
tu102_acr_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_acr **pacr)
{
if (nvkm_gsp_rm(device->gsp))
return -ENODEV;
return nvkm_acr_new_(tu102_acr_fwif, device, type, inst, pacr);
}