drm/nouveau/acr: implement new subdev to replace "secure boot"

ACR is responsible for managing the firmware for LS (Low Secure) falcons,
this was previously handled in the driver by SECBOOT.

This rewrite started from some test code that attempted to replicate the
procedure RM uses in order to debug early Turing ACR firmwares that were
provided by NVIDIA for development.

Compared with SECBOOT, the code is structured into more individual steps,
with the aim of making the process easier to follow/debug, whilst making
it possible to support newer firmware versions that may have a different
binary format or API interface.

The HS (High Secure) binary(s) are now booted earlier in device init, to
match the behaviour of RM, whereas SECBOOT would delay this until we try
to boot the first LS falcon.

There's also additional debugging features available, with the intention
of making it easier to solve issues during FW/HW bring-up in the future.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
Ben Skeggs 2020-01-15 06:34:22 +10:00
parent ebe52a58ac
commit 22dcda45a3
30 changed files with 2207 additions and 47 deletions

View File

@ -0,0 +1,152 @@
#ifndef __NVFW_ACR_H__
#define __NVFW_ACR_H__
struct wpr_header {
#define WPR_HEADER_V0_FALCON_ID_INVALID 0xffffffff
u32 falcon_id;
u32 lsb_offset;
u32 bootstrap_owner;
u32 lazy_bootstrap;
#define WPR_HEADER_V0_STATUS_NONE 0
#define WPR_HEADER_V0_STATUS_COPY 1
#define WPR_HEADER_V0_STATUS_VALIDATION_CODE_FAILED 2
#define WPR_HEADER_V0_STATUS_VALIDATION_DATA_FAILED 3
#define WPR_HEADER_V0_STATUS_VALIDATION_DONE 4
#define WPR_HEADER_V0_STATUS_VALIDATION_SKIPPED 5
#define WPR_HEADER_V0_STATUS_BOOTSTRAP_READY 6
u32 status;
};
void wpr_header_dump(struct nvkm_subdev *, const struct wpr_header *);
struct wpr_header_v1 {
#define WPR_HEADER_V1_FALCON_ID_INVALID 0xffffffff
u32 falcon_id;
u32 lsb_offset;
u32 bootstrap_owner;
u32 lazy_bootstrap;
u32 bin_version;
#define WPR_HEADER_V1_STATUS_NONE 0
#define WPR_HEADER_V1_STATUS_COPY 1
#define WPR_HEADER_V1_STATUS_VALIDATION_CODE_FAILED 2
#define WPR_HEADER_V1_STATUS_VALIDATION_DATA_FAILED 3
#define WPR_HEADER_V1_STATUS_VALIDATION_DONE 4
#define WPR_HEADER_V1_STATUS_VALIDATION_SKIPPED 5
#define WPR_HEADER_V1_STATUS_BOOTSTRAP_READY 6
#define WPR_HEADER_V1_STATUS_REVOCATION_CHECK_FAILED 7
u32 status;
};
void wpr_header_v1_dump(struct nvkm_subdev *, const struct wpr_header_v1 *);
struct lsf_signature {
u8 prd_keys[2][16];
u8 dbg_keys[2][16];
u32 b_prd_present;
u32 b_dbg_present;
u32 falcon_id;
};
struct lsf_signature_v1 {
u8 prd_keys[2][16];
u8 dbg_keys[2][16];
u32 b_prd_present;
u32 b_dbg_present;
u32 falcon_id;
u32 supports_versioning;
u32 version;
u32 depmap_count;
u8 depmap[11/*LSF_LSB_DEPMAP_SIZE*/ * 2 * 4];
u8 kdf[16];
};
struct lsb_header_tail {
u32 ucode_off;
u32 ucode_size;
u32 data_size;
u32 bl_code_size;
u32 bl_imem_off;
u32 bl_data_off;
u32 bl_data_size;
u32 app_code_off;
u32 app_code_size;
u32 app_data_off;
u32 app_data_size;
u32 flags;
};
struct lsb_header {
struct lsf_signature signature;
struct lsb_header_tail tail;
};
void lsb_header_dump(struct nvkm_subdev *, struct lsb_header *);
struct lsb_header_v1 {
struct lsf_signature_v1 signature;
struct lsb_header_tail tail;
};
void lsb_header_v1_dump(struct nvkm_subdev *, struct lsb_header_v1 *);
struct flcn_acr_desc {
union {
u8 reserved_dmem[0x200];
u32 signatures[4];
} ucode_reserved_space;
u32 wpr_region_id;
u32 wpr_offset;
u32 mmu_mem_range;
struct {
u32 no_regions;
struct {
u32 start_addr;
u32 end_addr;
u32 region_id;
u32 read_mask;
u32 write_mask;
u32 client_mask;
} region_props[2];
} regions;
u32 ucode_blob_size;
u64 ucode_blob_base __aligned(8);
struct {
u32 vpr_enabled;
u32 vpr_start;
u32 vpr_end;
u32 hdcp_policies;
} vpr_desc;
};
void flcn_acr_desc_dump(struct nvkm_subdev *, struct flcn_acr_desc *);
struct flcn_acr_desc_v1 {
u8 reserved_dmem[0x200];
u32 signatures[4];
u32 wpr_region_id;
u32 wpr_offset;
u32 mmu_memory_range;
struct {
u32 no_regions;
struct {
u32 start_addr;
u32 end_addr;
u32 region_id;
u32 read_mask;
u32 write_mask;
u32 client_mask;
u32 shadow_mem_start_addr;
} region_props[2];
} regions;
u32 ucode_blob_size;
u64 ucode_blob_base __aligned(8);
struct {
u32 vpr_enabled;
u32 vpr_start;
u32 vpr_end;
u32 hdcp_policies;
} vpr_desc;
};
void flcn_acr_desc_v1_dump(struct nvkm_subdev *, struct flcn_acr_desc_v1 *);
#endif

View File

@ -0,0 +1,97 @@
/* SPDX-License-Identifier: MIT */
#ifndef __NVFW_FLCN_H__
#define __NVFW_FLCN_H__
#include <core/os.h>
struct nvkm_subdev;
struct loader_config {
u32 dma_idx;
u32 code_dma_base;
u32 code_size_total;
u32 code_size_to_load;
u32 code_entry_point;
u32 data_dma_base;
u32 data_size;
u32 overlay_dma_base;
u32 argc;
u32 argv;
u32 code_dma_base1;
u32 data_dma_base1;
u32 overlay_dma_base1;
};
void
loader_config_dump(struct nvkm_subdev *, const struct loader_config *);
struct loader_config_v1 {
u32 reserved;
u32 dma_idx;
u64 code_dma_base;
u32 code_size_total;
u32 code_size_to_load;
u32 code_entry_point;
u64 data_dma_base;
u32 data_size;
u64 overlay_dma_base;
u32 argc;
u32 argv;
} __packed;
void
loader_config_v1_dump(struct nvkm_subdev *, const struct loader_config_v1 *);
struct flcn_bl_dmem_desc {
u32 reserved[4];
u32 signature[4];
u32 ctx_dma;
u32 code_dma_base;
u32 non_sec_code_off;
u32 non_sec_code_size;
u32 sec_code_off;
u32 sec_code_size;
u32 code_entry_point;
u32 data_dma_base;
u32 data_size;
u32 code_dma_base1;
u32 data_dma_base1;
};
void
flcn_bl_dmem_desc_dump(struct nvkm_subdev *, const struct flcn_bl_dmem_desc *);
struct flcn_bl_dmem_desc_v1 {
u32 reserved[4];
u32 signature[4];
u32 ctx_dma;
u64 code_dma_base;
u32 non_sec_code_off;
u32 non_sec_code_size;
u32 sec_code_off;
u32 sec_code_size;
u32 code_entry_point;
u64 data_dma_base;
u32 data_size;
} __packed;
void flcn_bl_dmem_desc_v1_dump(struct nvkm_subdev *,
const struct flcn_bl_dmem_desc_v1 *);
struct flcn_bl_dmem_desc_v2 {
u32 reserved[4];
u32 signature[4];
u32 ctx_dma;
u64 code_dma_base;
u32 non_sec_code_off;
u32 non_sec_code_size;
u32 sec_code_off;
u32 sec_code_size;
u32 code_entry_point;
u64 data_dma_base;
u32 data_size;
u32 argc;
u32 argv;
} __packed;
void flcn_bl_dmem_desc_v2_dump(struct nvkm_subdev *,
const struct flcn_bl_dmem_desc_v2 *);
#endif

View File

@ -3,7 +3,7 @@
#define __NVKM_ACR_H__
#define nvkm_acr(p) container_of((p), struct nvkm_acr, subdev)
#include <core/subdev.h>
struct nvkm_falcon;
#include <core/falcon.h>
enum nvkm_acr_lsf_id {
NVKM_ACR_LSF_PMU = 0,
@ -36,9 +36,25 @@ struct nvkm_acr {
const struct nvkm_acr_func *func;
struct nvkm_subdev subdev;
struct list_head hsfw, hsf;
struct list_head lsfw, lsf;
struct nvkm_memory *wpr;
u64 wpr_start;
u64 wpr_end;
u64 shadow_start;
struct nvkm_memory *inst;
struct nvkm_vmm *vmm;
bool done;
const struct firmware *wpr_fw;
bool wpr_comp;
u64 wpr_prev;
};
bool nvkm_acr_managed_falcon(struct nvkm_device *, enum nvkm_acr_lsf_id);
int nvkm_acr_bootstrap_falcons(struct nvkm_device *, unsigned long mask);
int gm200_acr_new(struct nvkm_device *, int, struct nvkm_acr **);
@ -71,9 +87,24 @@ struct nvkm_acr_lsfw {
u32 ucode_size;
u32 data_size;
struct {
u32 lsb;
u32 img;
u32 bld;
} offset;
u32 bl_data_size;
};
struct nvkm_acr_lsf_func {
/* The (currently) map directly to LSB header flags. */
#define NVKM_ACR_LSF_LOAD_CODE_AT_0 0x00000001
#define NVKM_ACR_LSF_DMACTL_REQ_CTX 0x00000004
#define NVKM_ACR_LSF_FORCE_PRIV_LOAD 0x00000008
u32 flags;
u32 bld_size;
void (*bld_write)(struct nvkm_acr *, u32 bld, struct nvkm_acr_lsfw *);
void (*bld_patch)(struct nvkm_acr *, u32 bld, s64 adjust);
int (*boot)(struct nvkm_falcon *);
int (*bootstrap_falcon)(struct nvkm_falcon *, enum nvkm_acr_lsf_id);
int (*bootstrap_multiple_falcons)(struct nvkm_falcon *, u32 mask);

View File

@ -2048,7 +2048,6 @@ nv120_chipset = {
.pci = gk104_pci_new,
.pmu = gm107_pmu_new,
.therm = gm200_therm_new,
.secboot = gm200_secboot_new,
.timer = gk20a_timer_new,
.top = gk104_top_new,
.volt = gk104_volt_new,
@ -2087,7 +2086,6 @@ nv124_chipset = {
.pci = gk104_pci_new,
.pmu = gm107_pmu_new,
.therm = gm200_therm_new,
.secboot = gm200_secboot_new,
.timer = gk20a_timer_new,
.top = gk104_top_new,
.volt = gk104_volt_new,
@ -2126,7 +2124,6 @@ nv126_chipset = {
.pci = gk104_pci_new,
.pmu = gm107_pmu_new,
.therm = gm200_therm_new,
.secboot = gm200_secboot_new,
.timer = gk20a_timer_new,
.top = gk104_top_new,
.volt = gk104_volt_new,
@ -2157,7 +2154,6 @@ nv12b_chipset = {
.mc = gk20a_mc_new,
.mmu = gm20b_mmu_new,
.pmu = gm20b_pmu_new,
.secboot = gm20b_secboot_new,
.timer = gk20a_timer_new,
.top = gk104_top_new,
.ce[2] = gm200_ce_new,
@ -2187,7 +2183,6 @@ nv130_chipset = {
.mc = gp100_mc_new,
.mmu = gp100_mmu_new,
.therm = gp100_therm_new,
.secboot = gm200_secboot_new,
.pci = gp100_pci_new,
.pmu = gp100_pmu_new,
.timer = gk20a_timer_new,
@ -2228,7 +2223,6 @@ nv132_chipset = {
.mc = gp100_mc_new,
.mmu = gp100_mmu_new,
.therm = gp100_therm_new,
.secboot = gp102_secboot_new,
.pci = gp100_pci_new,
.pmu = gp102_pmu_new,
.timer = gk20a_timer_new,
@ -2267,7 +2261,6 @@ nv134_chipset = {
.mc = gp100_mc_new,
.mmu = gp100_mmu_new,
.therm = gp100_therm_new,
.secboot = gp102_secboot_new,
.pci = gp100_pci_new,
.pmu = gp102_pmu_new,
.timer = gk20a_timer_new,
@ -2306,7 +2299,6 @@ nv136_chipset = {
.mc = gp100_mc_new,
.mmu = gp100_mmu_new,
.therm = gp100_therm_new,
.secboot = gp102_secboot_new,
.pci = gp100_pci_new,
.pmu = gp102_pmu_new,
.timer = gk20a_timer_new,
@ -2344,7 +2336,6 @@ nv137_chipset = {
.mc = gp100_mc_new,
.mmu = gp100_mmu_new,
.therm = gp100_therm_new,
.secboot = gp102_secboot_new,
.pci = gp100_pci_new,
.pmu = gp102_pmu_new,
.timer = gk20a_timer_new,
@ -2383,7 +2374,6 @@ nv138_chipset = {
.mc = gp100_mc_new,
.mmu = gp100_mmu_new,
.therm = gp100_therm_new,
.secboot = gp108_secboot_new,
.pci = gp100_pci_new,
.pmu = gp102_pmu_new,
.timer = gk20a_timer_new,
@ -2415,7 +2405,6 @@ nv13b_chipset = {
.ltc = gp10b_ltc_new,
.mc = gp10b_mc_new,
.mmu = gp10b_mmu_new,
.secboot = gp10b_secboot_new,
.pmu = gp10b_pmu_new,
.timer = gk20a_timer_new,
.top = gk104_top_new,
@ -2447,7 +2436,6 @@ nv140_chipset = {
.mmu = gv100_mmu_new,
.pci = gp100_pci_new,
.pmu = gp102_pmu_new,
.secboot = gp108_secboot_new,
.therm = gp100_therm_new,
.timer = gk20a_timer_new,
.top = gk104_top_new,

View File

@ -26,9 +26,9 @@
#include "fuc/os.h"
#include <core/client.h>
#include <core/option.h>
#include <core/firmware.h>
#include <subdev/secboot.h>
#include <core/option.h>
#include <subdev/acr.h>
#include <subdev/fb.h>
#include <subdev/mc.h>
#include <subdev/pmu.h>
@ -1690,28 +1690,30 @@ gf100_gr_init_ctxctl_ext(struct gf100_gr *gr)
{
struct nvkm_subdev *subdev = &gr->base.engine.subdev;
struct nvkm_device *device = subdev->device;
struct nvkm_secboot *sb = device->secboot;
u32 secboot_mask = 0;
u32 lsf_mask = 0;
int ret;
/* load fuc microcode */
nvkm_mc_unk260(device, 0);
/* securely-managed falcons must be reset using secure boot */
if (nvkm_secboot_is_managed(sb, NVKM_SECBOOT_FALCON_FECS))
secboot_mask |= BIT(NVKM_SECBOOT_FALCON_FECS);
else
if (!nvkm_acr_managed_falcon(device, NVKM_ACR_LSF_FECS)) {
gf100_gr_init_fw(&gr->fecs.falcon, &gr->fecs.inst,
&gr->fecs.data);
} else {
lsf_mask |= BIT(NVKM_ACR_LSF_FECS);
}
if (nvkm_secboot_is_managed(sb, NVKM_SECBOOT_FALCON_GPCCS))
secboot_mask |= BIT(NVKM_SECBOOT_FALCON_GPCCS);
else
if (!nvkm_acr_managed_falcon(device, NVKM_ACR_LSF_GPCCS)) {
gf100_gr_init_fw(&gr->gpccs.falcon, &gr->gpccs.inst,
&gr->gpccs.data);
} else {
lsf_mask |= BIT(NVKM_ACR_LSF_GPCCS);
}
if (secboot_mask != 0) {
int ret = nvkm_secboot_reset(sb, secboot_mask);
if (lsf_mask) {
ret = nvkm_acr_bootstrap_falcons(device, lsf_mask);
if (ret)
return ret;
}

View File

@ -31,6 +31,8 @@
#include <subdev/mmu.h>
#include <engine/falcon.h>
struct nvkm_acr_lsfw;
#define GPC_MAX 32
#define TPC_MAX_PER_GPC 8
#define TPC_MAX (GPC_MAX * TPC_MAX_PER_GPC)
@ -400,6 +402,8 @@ extern const struct nvkm_acr_lsf_func gm200_gr_gpccs_acr;
extern const struct nvkm_acr_lsf_func gm200_gr_fecs_acr;
extern const struct nvkm_acr_lsf_func gm20b_gr_fecs_acr;
void gm20b_gr_acr_bld_write(struct nvkm_acr *, u32, struct nvkm_acr_lsfw *);
void gm20b_gr_acr_bld_patch(struct nvkm_acr *, u32, s64);
extern const struct nvkm_acr_lsf_func gp108_gr_gpccs_acr;
extern const struct nvkm_acr_lsf_func gp108_gr_fecs_acr;

View File

@ -28,18 +28,58 @@
#include <subdev/acr.h>
#include <subdev/secboot.h>
#include <nvfw/flcn.h>
#include <nvif/class.h>
/*******************************************************************************
* PGRAPH engine/subdev functions
******************************************************************************/
static void
gm200_gr_acr_bld_patch(struct nvkm_acr *acr, u32 bld, s64 adjust)
{
struct flcn_bl_dmem_desc_v1 hdr;
nvkm_robj(acr->wpr, bld, &hdr, sizeof(hdr));
hdr.code_dma_base = hdr.code_dma_base + adjust;
hdr.data_dma_base = hdr.data_dma_base + adjust;
nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr));
flcn_bl_dmem_desc_v1_dump(&acr->subdev, &hdr);
}
static void
gm200_gr_acr_bld_write(struct nvkm_acr *acr, u32 bld,
struct nvkm_acr_lsfw *lsfw)
{
const u64 base = lsfw->offset.img + lsfw->app_start_offset;
const u64 code = base + lsfw->app_resident_code_offset;
const u64 data = base + lsfw->app_resident_data_offset;
const struct flcn_bl_dmem_desc_v1 hdr = {
.ctx_dma = FALCON_DMAIDX_UCODE,
.code_dma_base = code,
.non_sec_code_off = lsfw->app_resident_code_offset,
.non_sec_code_size = lsfw->app_resident_code_size,
.code_entry_point = lsfw->app_imem_entry,
.data_dma_base = data,
.data_size = lsfw->app_resident_data_size,
};
nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr));
}
const struct nvkm_acr_lsf_func
gm200_gr_gpccs_acr = {
.flags = NVKM_ACR_LSF_FORCE_PRIV_LOAD,
.bld_size = sizeof(struct flcn_bl_dmem_desc_v1),
.bld_write = gm200_gr_acr_bld_write,
.bld_patch = gm200_gr_acr_bld_patch,
};
const struct nvkm_acr_lsf_func
gm200_gr_fecs_acr = {
.bld_size = sizeof(struct flcn_bl_dmem_desc_v1),
.bld_write = gm200_gr_acr_bld_write,
.bld_patch = gm200_gr_acr_bld_patch,
};
int

View File

@ -26,10 +26,55 @@
#include <subdev/acr.h>
#include <subdev/timer.h>
#include <nvfw/flcn.h>
#include <nvif/class.h>
void
gm20b_gr_acr_bld_patch(struct nvkm_acr *acr, u32 bld, s64 adjust)
{
struct flcn_bl_dmem_desc hdr;
u64 addr;
nvkm_robj(acr->wpr, bld, &hdr, sizeof(hdr));
addr = ((u64)hdr.code_dma_base1 << 40 | hdr.code_dma_base << 8);
hdr.code_dma_base = lower_32_bits((addr + adjust) >> 8);
hdr.code_dma_base1 = upper_32_bits((addr + adjust) >> 8);
addr = ((u64)hdr.data_dma_base1 << 40 | hdr.data_dma_base << 8);
hdr.data_dma_base = lower_32_bits((addr + adjust) >> 8);
hdr.data_dma_base1 = upper_32_bits((addr + adjust) >> 8);
nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr));
flcn_bl_dmem_desc_dump(&acr->subdev, &hdr);
}
void
gm20b_gr_acr_bld_write(struct nvkm_acr *acr, u32 bld,
struct nvkm_acr_lsfw *lsfw)
{
const u64 base = lsfw->offset.img + lsfw->app_start_offset;
const u64 code = (base + lsfw->app_resident_code_offset) >> 8;
const u64 data = (base + lsfw->app_resident_data_offset) >> 8;
const struct flcn_bl_dmem_desc hdr = {
.ctx_dma = FALCON_DMAIDX_UCODE,
.code_dma_base = lower_32_bits(code),
.non_sec_code_off = lsfw->app_resident_code_offset,
.non_sec_code_size = lsfw->app_resident_code_size,
.code_entry_point = lsfw->app_imem_entry,
.data_dma_base = lower_32_bits(data),
.data_size = lsfw->app_resident_data_size,
.code_dma_base1 = upper_32_bits(code),
.data_dma_base1 = upper_32_bits(data),
};
nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr));
}
const struct nvkm_acr_lsf_func
gm20b_gr_fecs_acr = {
.bld_size = sizeof(struct flcn_bl_dmem_desc),
.bld_write = gm20b_gr_acr_bld_write,
.bld_patch = gm20b_gr_acr_bld_patch,
};
static void

View File

@ -23,12 +23,52 @@
#include <subdev/acr.h>
#include <nvfw/flcn.h>
static void
gp108_gr_acr_bld_patch(struct nvkm_acr *acr, u32 bld, s64 adjust)
{
struct flcn_bl_dmem_desc_v2 hdr;
nvkm_robj(acr->wpr, bld, &hdr, sizeof(hdr));
hdr.code_dma_base = hdr.code_dma_base + adjust;
hdr.data_dma_base = hdr.data_dma_base + adjust;
nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr));
flcn_bl_dmem_desc_v2_dump(&acr->subdev, &hdr);
}
static void
gp108_gr_acr_bld_write(struct nvkm_acr *acr, u32 bld,
struct nvkm_acr_lsfw *lsfw)
{
const u64 base = lsfw->offset.img + lsfw->app_start_offset;
const u64 code = base + lsfw->app_resident_code_offset;
const u64 data = base + lsfw->app_resident_data_offset;
const struct flcn_bl_dmem_desc_v2 hdr = {
.ctx_dma = FALCON_DMAIDX_UCODE,
.code_dma_base = code,
.non_sec_code_off = lsfw->app_resident_code_offset,
.non_sec_code_size = lsfw->app_resident_code_size,
.code_entry_point = lsfw->app_imem_entry,
.data_dma_base = data,
.data_size = lsfw->app_resident_data_size,
};
nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr));
}
const struct nvkm_acr_lsf_func
gp108_gr_gpccs_acr = {
.flags = NVKM_ACR_LSF_FORCE_PRIV_LOAD,
.bld_size = sizeof(struct flcn_bl_dmem_desc_v2),
.bld_write = gp108_gr_acr_bld_write,
.bld_patch = gp108_gr_acr_bld_patch,
};
const struct nvkm_acr_lsf_func
gp108_gr_fecs_acr = {
.bld_size = sizeof(struct flcn_bl_dmem_desc_v2),
.bld_write = gp108_gr_acr_bld_write,
.bld_patch = gp108_gr_acr_bld_patch,
};
MODULE_FIRMWARE("nvidia/gp108/gr/fecs_bl.bin");

View File

@ -27,8 +27,14 @@
#include <nvif/class.h>
#include <nvfw/flcn.h>
static const struct nvkm_acr_lsf_func
gp10b_gr_gpccs_acr = {
.flags = NVKM_ACR_LSF_FORCE_PRIV_LOAD,
.bld_size = sizeof(struct flcn_bl_dmem_desc),
.bld_write = gm20b_gr_acr_bld_write,
.bld_patch = gm20b_gr_acr_bld_patch,
};
static const struct gf100_gr_func

View File

@ -21,9 +21,11 @@
*/
#include "priv.h"
#include <core/memory.h>
#include <subdev/acr.h>
#include <subdev/timer.h>
#include <nvfw/flcn.h>
#include <nvfw/sec2.h>
static int
@ -74,8 +76,44 @@ gp102_sec2_acr_boot(struct nvkm_falcon *falcon)
return 0;
}
static void
gp102_sec2_acr_bld_patch(struct nvkm_acr *acr, u32 bld, s64 adjust)
{
struct loader_config_v1 hdr;
nvkm_robj(acr->wpr, bld, &hdr, sizeof(hdr));
hdr.code_dma_base = hdr.code_dma_base + adjust;
hdr.data_dma_base = hdr.data_dma_base + adjust;
hdr.overlay_dma_base = hdr.overlay_dma_base + adjust;
nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr));
loader_config_v1_dump(&acr->subdev, &hdr);
}
static void
gp102_sec2_acr_bld_write(struct nvkm_acr *acr, u32 bld,
struct nvkm_acr_lsfw *lsfw)
{
const struct loader_config_v1 hdr = {
.dma_idx = FALCON_SEC2_DMAIDX_UCODE,
.code_dma_base = lsfw->offset.img + lsfw->app_start_offset,
.code_size_total = lsfw->app_size,
.code_size_to_load = lsfw->app_resident_code_size,
.code_entry_point = lsfw->app_imem_entry,
.data_dma_base = lsfw->offset.img + lsfw->app_start_offset +
lsfw->app_resident_data_offset,
.data_size = lsfw->app_resident_data_size,
.overlay_dma_base = lsfw->offset.img + lsfw->app_start_offset,
.argc = 1,
.argv = lsfw->falcon->func->emem_addr,
};
nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr));
}
static const struct nvkm_acr_lsf_func
gp102_sec2_acr_0 = {
.bld_size = sizeof(struct loader_config_v1),
.bld_write = gp102_sec2_acr_bld_write,
.bld_patch = gp102_sec2_acr_bld_patch,
.boot = gp102_sec2_acr_boot,
.bootstrap_falcon = gp102_sec2_acr_bootstrap_falcon,
};
@ -219,8 +257,42 @@ MODULE_FIRMWARE("nvidia/gp107/sec2/desc.bin");
MODULE_FIRMWARE("nvidia/gp107/sec2/image.bin");
MODULE_FIRMWARE("nvidia/gp107/sec2/sig.bin");
static void
gp102_sec2_acr_bld_patch_1(struct nvkm_acr *acr, u32 bld, s64 adjust)
{
struct flcn_bl_dmem_desc_v2 hdr;
nvkm_robj(acr->wpr, bld, &hdr, sizeof(hdr));
hdr.code_dma_base = hdr.code_dma_base + adjust;
hdr.data_dma_base = hdr.data_dma_base + adjust;
nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr));
flcn_bl_dmem_desc_v2_dump(&acr->subdev, &hdr);
}
static void
gp102_sec2_acr_bld_write_1(struct nvkm_acr *acr, u32 bld,
struct nvkm_acr_lsfw *lsfw)
{
const struct flcn_bl_dmem_desc_v2 hdr = {
.ctx_dma = FALCON_SEC2_DMAIDX_UCODE,
.code_dma_base = lsfw->offset.img + lsfw->app_start_offset,
.non_sec_code_off = lsfw->app_resident_code_offset,
.non_sec_code_size = lsfw->app_resident_code_size,
.code_entry_point = lsfw->app_imem_entry,
.data_dma_base = lsfw->offset.img + lsfw->app_start_offset +
lsfw->app_resident_data_offset,
.data_size = lsfw->app_resident_data_size,
.argc = 1,
.argv = lsfw->falcon->func->emem_addr,
};
nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr));
}
const struct nvkm_acr_lsf_func
gp102_sec2_acr_1 = {
.bld_size = sizeof(struct flcn_bl_dmem_desc_v2),
.bld_write = gp102_sec2_acr_bld_write_1,
.bld_patch = gp102_sec2_acr_bld_patch_1,
.boot = gp102_sec2_acr_boot,
.bootstrap_falcon = gp102_sec2_acr_bootstrap_falcon,
};

View File

@ -11,6 +11,7 @@ struct nvkm_sec2_func {
};
void gp102_sec2_intr(struct nvkm_sec2 *);
int gp102_sec2_initmsg(struct nvkm_sec2 *);
struct nvkm_sec2_fwif {
int version;

View File

@ -20,6 +20,7 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "priv.h"
#include <subdev/acr.h>
static const struct nvkm_falcon_func
tu102_sec2_flcn = {
@ -43,7 +44,9 @@ tu102_sec2_flcn = {
static const struct nvkm_sec2_func
tu102_sec2 = {
.flcn = &tu102_sec2_flcn,
.unit_acr = 0x07,
.intr = gp102_sec2_intr,
.initmsg = gp102_sec2_initmsg,
};
static int
@ -55,6 +58,7 @@ tu102_sec2_nofw(struct nvkm_sec2 *sec2, int ver,
static const struct nvkm_sec2_fwif
tu102_sec2_fwif[] = {
{ 0, gp102_sec2_load, &tu102_sec2, &gp102_sec2_acr_1 },
{ -1, tu102_sec2_nofw, &tu102_sec2 }
};

View File

@ -2,3 +2,6 @@
nvkm-y += nvkm/nvfw/fw.o
nvkm-y += nvkm/nvfw/hs.o
nvkm-y += nvkm/nvfw/ls.o
nvkm-y += nvkm/nvfw/acr.o
nvkm-y += nvkm/nvfw/flcn.o

View File

@ -0,0 +1,165 @@
/*
* 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 <core/subdev.h>
#include <nvfw/acr.h>
void
wpr_header_dump(struct nvkm_subdev *subdev, const struct wpr_header *hdr)
{
nvkm_debug(subdev, "wprHeader\n");
nvkm_debug(subdev, "\tfalconID : %d\n", hdr->falcon_id);
nvkm_debug(subdev, "\tlsbOffset : 0x%x\n", hdr->lsb_offset);
nvkm_debug(subdev, "\tbootstrapOwner: %d\n", hdr->bootstrap_owner);
nvkm_debug(subdev, "\tlazyBootstrap : %d\n", hdr->lazy_bootstrap);
nvkm_debug(subdev, "\tstatus : %d\n", hdr->status);
}
void
wpr_header_v1_dump(struct nvkm_subdev *subdev, const struct wpr_header_v1 *hdr)
{
nvkm_debug(subdev, "wprHeader\n");
nvkm_debug(subdev, "\tfalconID : %d\n", hdr->falcon_id);
nvkm_debug(subdev, "\tlsbOffset : 0x%x\n", hdr->lsb_offset);
nvkm_debug(subdev, "\tbootstrapOwner: %d\n", hdr->bootstrap_owner);
nvkm_debug(subdev, "\tlazyBootstrap : %d\n", hdr->lazy_bootstrap);
nvkm_debug(subdev, "\tbinVersion : %d\n", hdr->bin_version);
nvkm_debug(subdev, "\tstatus : %d\n", hdr->status);
}
void
lsb_header_tail_dump(struct nvkm_subdev *subdev,
struct lsb_header_tail *hdr)
{
nvkm_debug(subdev, "lsbHeader\n");
nvkm_debug(subdev, "\tucodeOff : 0x%x\n", hdr->ucode_off);
nvkm_debug(subdev, "\tucodeSize : 0x%x\n", hdr->ucode_size);
nvkm_debug(subdev, "\tdataSize : 0x%x\n", hdr->data_size);
nvkm_debug(subdev, "\tblCodeSize : 0x%x\n", hdr->bl_code_size);
nvkm_debug(subdev, "\tblImemOff : 0x%x\n", hdr->bl_imem_off);
nvkm_debug(subdev, "\tblDataOff : 0x%x\n", hdr->bl_data_off);
nvkm_debug(subdev, "\tblDataSize : 0x%x\n", hdr->bl_data_size);
nvkm_debug(subdev, "\tappCodeOff : 0x%x\n", hdr->app_code_off);
nvkm_debug(subdev, "\tappCodeSize : 0x%x\n", hdr->app_code_size);
nvkm_debug(subdev, "\tappDataOff : 0x%x\n", hdr->app_data_off);
nvkm_debug(subdev, "\tappDataSize : 0x%x\n", hdr->app_data_size);
nvkm_debug(subdev, "\tflags : 0x%x\n", hdr->flags);
}
void
lsb_header_dump(struct nvkm_subdev *subdev, struct lsb_header *hdr)
{
lsb_header_tail_dump(subdev, &hdr->tail);
}
void
lsb_header_v1_dump(struct nvkm_subdev *subdev, struct lsb_header_v1 *hdr)
{
lsb_header_tail_dump(subdev, &hdr->tail);
}
void
flcn_acr_desc_dump(struct nvkm_subdev *subdev, struct flcn_acr_desc *hdr)
{
int i;
nvkm_debug(subdev, "acrDesc\n");
nvkm_debug(subdev, "\twprRegionId : %d\n", hdr->wpr_region_id);
nvkm_debug(subdev, "\twprOffset : 0x%x\n", hdr->wpr_offset);
nvkm_debug(subdev, "\tmmuMemRange : 0x%x\n",
hdr->mmu_mem_range);
nvkm_debug(subdev, "\tnoRegions : %d\n",
hdr->regions.no_regions);
for (i = 0; i < ARRAY_SIZE(hdr->regions.region_props); i++) {
nvkm_debug(subdev, "\tregion[%d] :\n", i);
nvkm_debug(subdev, "\t startAddr : 0x%x\n",
hdr->regions.region_props[i].start_addr);
nvkm_debug(subdev, "\t endAddr : 0x%x\n",
hdr->regions.region_props[i].end_addr);
nvkm_debug(subdev, "\t regionId : %d\n",
hdr->regions.region_props[i].region_id);
nvkm_debug(subdev, "\t readMask : 0x%x\n",
hdr->regions.region_props[i].read_mask);
nvkm_debug(subdev, "\t writeMask : 0x%x\n",
hdr->regions.region_props[i].write_mask);
nvkm_debug(subdev, "\t clientMask : 0x%x\n",
hdr->regions.region_props[i].client_mask);
}
nvkm_debug(subdev, "\tucodeBlobSize: %d\n",
hdr->ucode_blob_size);
nvkm_debug(subdev, "\tucodeBlobBase: 0x%llx\n",
hdr->ucode_blob_base);
nvkm_debug(subdev, "\tvprEnabled : %d\n",
hdr->vpr_desc.vpr_enabled);
nvkm_debug(subdev, "\tvprStart : 0x%x\n",
hdr->vpr_desc.vpr_start);
nvkm_debug(subdev, "\tvprEnd : 0x%x\n",
hdr->vpr_desc.vpr_end);
nvkm_debug(subdev, "\thdcpPolicies : 0x%x\n",
hdr->vpr_desc.hdcp_policies);
}
void
flcn_acr_desc_v1_dump(struct nvkm_subdev *subdev, struct flcn_acr_desc_v1 *hdr)
{
int i;
nvkm_debug(subdev, "acrDesc\n");
nvkm_debug(subdev, "\twprRegionId : %d\n", hdr->wpr_region_id);
nvkm_debug(subdev, "\twprOffset : 0x%x\n", hdr->wpr_offset);
nvkm_debug(subdev, "\tmmuMemoryRange : 0x%x\n",
hdr->mmu_memory_range);
nvkm_debug(subdev, "\tnoRegions : %d\n",
hdr->regions.no_regions);
for (i = 0; i < ARRAY_SIZE(hdr->regions.region_props); i++) {
nvkm_debug(subdev, "\tregion[%d] :\n", i);
nvkm_debug(subdev, "\t startAddr : 0x%x\n",
hdr->regions.region_props[i].start_addr);
nvkm_debug(subdev, "\t endAddr : 0x%x\n",
hdr->regions.region_props[i].end_addr);
nvkm_debug(subdev, "\t regionId : %d\n",
hdr->regions.region_props[i].region_id);
nvkm_debug(subdev, "\t readMask : 0x%x\n",
hdr->regions.region_props[i].read_mask);
nvkm_debug(subdev, "\t writeMask : 0x%x\n",
hdr->regions.region_props[i].write_mask);
nvkm_debug(subdev, "\t clientMask : 0x%x\n",
hdr->regions.region_props[i].client_mask);
nvkm_debug(subdev, "\t shadowMemStartAddr: 0x%x\n",
hdr->regions.region_props[i].shadow_mem_start_addr);
}
nvkm_debug(subdev, "\tucodeBlobSize : %d\n",
hdr->ucode_blob_size);
nvkm_debug(subdev, "\tucodeBlobBase : 0x%llx\n",
hdr->ucode_blob_base);
nvkm_debug(subdev, "\tvprEnabled : %d\n",
hdr->vpr_desc.vpr_enabled);
nvkm_debug(subdev, "\tvprStart : 0x%x\n",
hdr->vpr_desc.vpr_start);
nvkm_debug(subdev, "\tvprEnd : 0x%x\n",
hdr->vpr_desc.vpr_end);
nvkm_debug(subdev, "\thdcpPolicies : 0x%x\n",
hdr->vpr_desc.hdcp_policies);
}

View File

@ -0,0 +1,115 @@
/*
* 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 <core/subdev.h>
#include <nvfw/flcn.h>
void
loader_config_dump(struct nvkm_subdev *subdev, const struct loader_config *hdr)
{
nvkm_debug(subdev, "loaderConfig\n");
nvkm_debug(subdev, "\tdmaIdx : %d\n", hdr->dma_idx);
nvkm_debug(subdev, "\tcodeDmaBase : 0x%xx\n", hdr->code_dma_base);
nvkm_debug(subdev, "\tcodeSizeTotal : 0x%x\n", hdr->code_size_total);
nvkm_debug(subdev, "\tcodeSizeToLoad: 0x%x\n", hdr->code_size_to_load);
nvkm_debug(subdev, "\tcodeEntryPoint: 0x%x\n", hdr->code_entry_point);
nvkm_debug(subdev, "\tdataDmaBase : 0x%x\n", hdr->data_dma_base);
nvkm_debug(subdev, "\tdataSize : 0x%x\n", hdr->data_size);
nvkm_debug(subdev, "\toverlayDmaBase: 0x%x\n", hdr->overlay_dma_base);
nvkm_debug(subdev, "\targc : 0x%08x\n", hdr->argc);
nvkm_debug(subdev, "\targv : 0x%08x\n", hdr->argv);
nvkm_debug(subdev, "\tcodeDmaBase1 : 0x%x\n", hdr->code_dma_base1);
nvkm_debug(subdev, "\tdataDmaBase1 : 0x%x\n", hdr->data_dma_base1);
nvkm_debug(subdev, "\tovlyDmaBase1 : 0x%x\n", hdr->overlay_dma_base1);
}
void
loader_config_v1_dump(struct nvkm_subdev *subdev,
const struct loader_config_v1 *hdr)
{
nvkm_debug(subdev, "loaderConfig\n");
nvkm_debug(subdev, "\treserved : 0x%08x\n", hdr->reserved);
nvkm_debug(subdev, "\tdmaIdx : %d\n", hdr->dma_idx);
nvkm_debug(subdev, "\tcodeDmaBase : 0x%llxx\n", hdr->code_dma_base);
nvkm_debug(subdev, "\tcodeSizeTotal : 0x%x\n", hdr->code_size_total);
nvkm_debug(subdev, "\tcodeSizeToLoad: 0x%x\n", hdr->code_size_to_load);
nvkm_debug(subdev, "\tcodeEntryPoint: 0x%x\n", hdr->code_entry_point);
nvkm_debug(subdev, "\tdataDmaBase : 0x%llx\n", hdr->data_dma_base);
nvkm_debug(subdev, "\tdataSize : 0x%x\n", hdr->data_size);
nvkm_debug(subdev, "\toverlayDmaBase: 0x%llx\n", hdr->overlay_dma_base);
nvkm_debug(subdev, "\targc : 0x%08x\n", hdr->argc);
nvkm_debug(subdev, "\targv : 0x%08x\n", hdr->argv);
}
void
flcn_bl_dmem_desc_dump(struct nvkm_subdev *subdev,
const struct flcn_bl_dmem_desc *hdr)
{
nvkm_debug(subdev, "flcnBlDmemDesc\n");
nvkm_debug(subdev, "\treserved : 0x%08x 0x%08x 0x%08x 0x%08x\n",
hdr->reserved[0], hdr->reserved[1], hdr->reserved[2],
hdr->reserved[3]);
nvkm_debug(subdev, "\tsignature : 0x%08x 0x%08x 0x%08x 0x%08x\n",
hdr->signature[0], hdr->signature[1], hdr->signature[2],
hdr->signature[3]);
nvkm_debug(subdev, "\tctxDma : %d\n", hdr->ctx_dma);
nvkm_debug(subdev, "\tcodeDmaBase : 0x%x\n", hdr->code_dma_base);
nvkm_debug(subdev, "\tnonSecCodeOff : 0x%x\n", hdr->non_sec_code_off);
nvkm_debug(subdev, "\tnonSecCodeSize: 0x%x\n", hdr->non_sec_code_size);
nvkm_debug(subdev, "\tsecCodeOff : 0x%x\n", hdr->sec_code_off);
nvkm_debug(subdev, "\tsecCodeSize : 0x%x\n", hdr->sec_code_size);
nvkm_debug(subdev, "\tcodeEntryPoint: 0x%x\n", hdr->code_entry_point);
nvkm_debug(subdev, "\tdataDmaBase : 0x%x\n", hdr->data_dma_base);
nvkm_debug(subdev, "\tdataSize : 0x%x\n", hdr->data_size);
nvkm_debug(subdev, "\tcodeDmaBase1 : 0x%x\n", hdr->code_dma_base1);
nvkm_debug(subdev, "\tdataDmaBase1 : 0x%x\n", hdr->data_dma_base1);
}
void
flcn_bl_dmem_desc_v1_dump(struct nvkm_subdev *subdev,
const struct flcn_bl_dmem_desc_v1 *hdr)
{
nvkm_debug(subdev, "flcnBlDmemDesc\n");
nvkm_debug(subdev, "\treserved : 0x%08x 0x%08x 0x%08x 0x%08x\n",
hdr->reserved[0], hdr->reserved[1], hdr->reserved[2],
hdr->reserved[3]);
nvkm_debug(subdev, "\tsignature : 0x%08x 0x%08x 0x%08x 0x%08x\n",
hdr->signature[0], hdr->signature[1], hdr->signature[2],
hdr->signature[3]);
nvkm_debug(subdev, "\tctxDma : %d\n", hdr->ctx_dma);
nvkm_debug(subdev, "\tcodeDmaBase : 0x%llx\n", hdr->code_dma_base);
nvkm_debug(subdev, "\tnonSecCodeOff : 0x%x\n", hdr->non_sec_code_off);
nvkm_debug(subdev, "\tnonSecCodeSize: 0x%x\n", hdr->non_sec_code_size);
nvkm_debug(subdev, "\tsecCodeOff : 0x%x\n", hdr->sec_code_off);
nvkm_debug(subdev, "\tsecCodeSize : 0x%x\n", hdr->sec_code_size);
nvkm_debug(subdev, "\tcodeEntryPoint: 0x%x\n", hdr->code_entry_point);
nvkm_debug(subdev, "\tdataDmaBase : 0x%llx\n", hdr->data_dma_base);
nvkm_debug(subdev, "\tdataSize : 0x%x\n", hdr->data_size);
}
void
flcn_bl_dmem_desc_v2_dump(struct nvkm_subdev *subdev,
const struct flcn_bl_dmem_desc_v2 *hdr)
{
flcn_bl_dmem_desc_v1_dump(subdev, (void *)hdr);
nvkm_debug(subdev, "\targc : 0x%08x\n", hdr->argc);
nvkm_debug(subdev, "\targv : 0x%08x\n", hdr->argv);
}

View File

@ -1,5 +1,6 @@
# SPDX-License-Identifier: MIT
nvkm-y += nvkm/subdev/acr/base.o
nvkm-y += nvkm/subdev/acr/hsfw.o
nvkm-y += nvkm/subdev/acr/lsfw.o
nvkm-y += nvkm/subdev/acr/gm200.o
nvkm-y += nvkm/subdev/acr/gm20b.o

View File

@ -22,6 +22,101 @@
#include "priv.h"
#include <core/firmware.h>
#include <core/memory.h>
#include <subdev/mmu.h>
static struct nvkm_acr_hsf *
nvkm_acr_hsf_find(struct nvkm_acr *acr, const char *name)
{
struct nvkm_acr_hsf *hsf;
list_for_each_entry(hsf, &acr->hsf, head) {
if (!strcmp(hsf->name, name))
return hsf;
}
return NULL;
}
int
nvkm_acr_hsf_boot(struct nvkm_acr *acr, const char *name)
{
struct nvkm_subdev *subdev = &acr->subdev;
struct nvkm_acr_hsf *hsf;
int ret;
hsf = nvkm_acr_hsf_find(acr, name);
if (!hsf)
return -EINVAL;
nvkm_debug(subdev, "executing %s binary\n", hsf->name);
ret = nvkm_falcon_get(hsf->falcon, subdev);
if (ret)
return ret;
ret = hsf->func->boot(acr, hsf);
nvkm_falcon_put(hsf->falcon, subdev);
if (ret) {
nvkm_error(subdev, "%s binary failed\n", hsf->name);
return ret;
}
nvkm_debug(subdev, "%s binary completed successfully\n", hsf->name);
return 0;
}
static void
nvkm_acr_unload(struct nvkm_acr *acr)
{
if (acr->done) {
nvkm_acr_hsf_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 *lsf;
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;
list_for_each_entry(lsf, &acr->lsf, head) {
if (lsf->func->boot) {
ret = lsf->func->boot(lsf->falcon);
if (ret)
break;
}
}
return ret;
}
static int
nvkm_acr_reload(struct nvkm_acr *acr)
{
nvkm_acr_unload(acr);
return nvkm_acr_load(acr);
}
static struct nvkm_acr_lsf *
nvkm_acr_falcon(struct nvkm_device *device)
@ -43,10 +138,16 @@ int
nvkm_acr_bootstrap_falcons(struct nvkm_device *device, unsigned long mask)
{
struct nvkm_acr_lsf *acrflcn = nvkm_acr_falcon(device);
struct nvkm_acr *acr = device->acr;
unsigned long id;
if (!acrflcn)
return -ENOSYS;
if (!acrflcn) {
int ret = nvkm_acr_reload(acr);
if (ret)
return ret;
return acr->done ? 0 : -EINVAL;
}
if (acrflcn->func->bootstrap_multiple_falcons) {
return acrflcn->func->
@ -62,41 +163,79 @@ nvkm_acr_bootstrap_falcons(struct nvkm_device *device, unsigned long mask)
return 0;
}
int
nvkm_acr_boot_ls_falcons(struct nvkm_device *device)
bool
nvkm_acr_managed_falcon(struct nvkm_device *device, enum nvkm_acr_lsf_id id)
{
struct nvkm_acr *acr = device->acr;
struct nvkm_acr_lsf *lsf;
int ret;
if (!acr)
return -ENOSYS;
list_for_each_entry(lsf, &acr->lsf, head) {
if (lsf->func->boot) {
ret = lsf->func->boot(lsf->falcon);
if (ret)
break;
if (acr) {
list_for_each_entry(lsf, &acr->lsf, head) {
if (lsf->id == id)
return true;
}
}
return ret;
return false;
}
static int
nvkm_acr_fini(struct nvkm_subdev *subdev, bool suspend)
{
nvkm_acr_unload(nvkm_acr(subdev));
return 0;
}
static int
nvkm_acr_init(struct nvkm_subdev *subdev)
{
if (!nvkm_acr_falcon(subdev->device))
return 0;
return nvkm_acr_load(nvkm_acr(subdev));
}
static void
nvkm_acr_cleanup(struct nvkm_acr *acr)
{
nvkm_acr_lsfw_del_all(acr);
nvkm_acr_hsfw_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_lsfw *lsfw;
struct nvkm_acr_hsfw *hsfw;
struct nvkm_acr_lsfw *lsfw, *lsft;
struct nvkm_acr_lsf *lsf;
u32 wpr_size = 0;
int ret, i;
/* 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);
list_for_each_entry(lsfw, &acr->lsfw, head) {
if (!(lsf = kmalloc(sizeof(*lsf), GFP_KERNEL)))
return -ENOMEM;
lsf->func = lsfw->func;
@ -105,6 +244,70 @@ nvkm_acr_oneinit(struct nvkm_subdev *subdev)
list_add_tail(&lsf->head, &acr->lsf);
}
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, nvkm_acr_falcon(device));
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);
}
}
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) {
nvkm_debug(subdev, "loading %s fw\n", hsfw->name);
ret = hsfw->func->load(acr, hsfw);
if (ret)
return ret;
}
/* Kill temporary data. */
nvkm_acr_cleanup(acr);
return 0;
}
@ -113,8 +316,23 @@ static void *
nvkm_acr_dtor(struct nvkm_subdev *subdev)
{
struct nvkm_acr *acr = nvkm_acr(subdev);
struct nvkm_acr_hsf *hsf, *hst;
struct nvkm_acr_lsf *lsf, *lst;
list_for_each_entry_safe(hsf, hst, &acr->hsf, head) {
nvkm_vmm_put(acr->vmm, &hsf->vma);
nvkm_memory_unref(&hsf->ucode);
kfree(hsf->imem);
list_del(&hsf->head);
kfree(hsf);
}
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);
@ -128,18 +346,47 @@ 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_version(subdev, "acr/wpr", ver, 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,
int index, struct nvkm_acr **pacr)
{
struct nvkm_acr *acr;
long wprfw;
if (!(acr = *pacr = kzalloc(sizeof(*acr), GFP_KERNEL)))
return -ENOMEM;
nvkm_subdev_ctor(&nvkm_acr, device, index, &acr->subdev);
INIT_LIST_HEAD(&acr->hsfw);
INIT_LIST_HEAD(&acr->lsfw);
INIT_LIST_HEAD(&acr->hsf);
INIT_LIST_HEAD(&acr->lsf);
fwif = nvkm_firmware_load(&acr->subdev, fwif, "Acr", acr);
@ -147,5 +394,13 @@ nvkm_acr_new_(const struct nvkm_acr_fwif *fwif, struct nvkm_device *device,
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;
}

View File

@ -21,11 +21,391 @@
*/
#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>
int
gm200_acr_init(struct nvkm_acr *acr)
{
return nvkm_acr_hsf_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;
}
void
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);
}
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;
while (hdr->falcon_id != WPR_HEADER_V0_FALCON_ID_INVALID) {
wpr_header_dump(&acr->subdev, hdr);
if (!nvkm_acr_lsfw_add(NULL, acr, NULL, (hdr++)->falcon_id))
return -ENOMEM;
}
return 0;
}
void
gm200_acr_hsfw_bld(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf)
{
struct flcn_bl_dmem_desc_v1 hsdesc = {
.ctx_dma = FALCON_DMAIDX_VIRT,
.code_dma_base = hsf->vma->addr,
.non_sec_code_off = hsf->non_sec_addr,
.non_sec_code_size = hsf->non_sec_size,
.sec_code_off = hsf->sec_addr,
.sec_code_size = hsf->sec_size,
.code_entry_point = 0,
.data_dma_base = hsf->vma->addr + hsf->data_addr,
.data_size = hsf->data_size,
};
flcn_bl_dmem_desc_v1_dump(&acr->subdev, &hsdesc);
nvkm_falcon_load_dmem(hsf->falcon, &hsdesc, 0, sizeof(hsdesc), 0);
}
int
gm200_acr_hsfw_boot(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf,
u32 intr_clear, u32 mbox0_ok)
{
struct nvkm_subdev *subdev = &acr->subdev;
struct nvkm_device *device = subdev->device;
struct nvkm_falcon *falcon = hsf->falcon;
u32 mbox0, mbox1;
int ret;
/* Reset falcon. */
nvkm_falcon_reset(falcon);
nvkm_falcon_bind_context(falcon, acr->inst);
/* Load bootloader into IMEM. */
nvkm_falcon_load_imem(falcon, hsf->imem,
falcon->code.limit - hsf->imem_size,
hsf->imem_size,
hsf->imem_tag,
0, false);
/* Load bootloader data into DMEM. */
hsf->func->bld(acr, hsf);
/* Boot the falcon. */
nvkm_mc_intr_mask(device, falcon->owner->index, false);
nvkm_falcon_wr32(falcon, 0x040, 0xdeada5a5);
nvkm_falcon_set_start_addr(falcon, hsf->imem_tag << 8);
nvkm_falcon_start(falcon);
ret = nvkm_falcon_wait_for_halt(falcon, 100);
if (ret)
return ret;
/* Check for successful completion. */
mbox0 = nvkm_falcon_rd32(falcon, 0x040);
mbox1 = nvkm_falcon_rd32(falcon, 0x044);
nvkm_debug(subdev, "mailbox %08x %08x\n", mbox0, mbox1);
if (mbox0 && mbox0 != mbox0_ok)
return -EIO;
nvkm_falcon_clear_interrupt(falcon, intr_clear);
nvkm_mc_intr_mask(device, falcon->owner->index, true);
return ret;
}
int
gm200_acr_hsfw_load(struct nvkm_acr *acr, struct nvkm_acr_hsfw *hsfw,
struct nvkm_falcon *falcon)
{
struct nvkm_subdev *subdev = &acr->subdev;
struct nvkm_acr_hsf *hsf;
int ret;
/* Patch the appropriate signature (production/debug) into the FW
* image, as determined by the mode the falcon is in.
*/
ret = nvkm_falcon_get(falcon, subdev);
if (ret)
return ret;
if (hsfw->sig.patch_loc) {
if (!falcon->debug) {
nvkm_debug(subdev, "patching production signature\n");
memcpy(hsfw->image + hsfw->sig.patch_loc,
hsfw->sig.prod.data,
hsfw->sig.prod.size);
} else {
nvkm_debug(subdev, "patching debug signature\n");
memcpy(hsfw->image + hsfw->sig.patch_loc,
hsfw->sig.dbg.data,
hsfw->sig.dbg.size);
}
}
nvkm_falcon_put(falcon, subdev);
if (!(hsf = kzalloc(sizeof(*hsf), GFP_KERNEL)))
return -ENOMEM;
hsf->func = hsfw->func;
hsf->name = hsfw->name;
list_add_tail(&hsf->head, &acr->hsf);
hsf->imem_size = hsfw->imem_size;
hsf->imem_tag = hsfw->imem_tag;
hsf->imem = kmemdup(hsfw->imem, hsfw->imem_size, GFP_KERNEL);
if (!hsf->imem)
return -ENOMEM;
hsf->non_sec_addr = hsfw->non_sec_addr;
hsf->non_sec_size = hsfw->non_sec_size;
hsf->sec_addr = hsfw->sec_addr;
hsf->sec_size = hsfw->sec_size;
hsf->data_addr = hsfw->data_addr;
hsf->data_size = hsfw->data_size;
/* Make the FW image accessible to the HS bootloader. */
ret = nvkm_memory_new(subdev->device, NVKM_MEM_TARGET_INST,
hsfw->image_size, 0x1000, false, &hsf->ucode);
if (ret)
return ret;
nvkm_kmap(hsf->ucode);
nvkm_wobj(hsf->ucode, 0, hsfw->image, hsfw->image_size);
nvkm_done(hsf->ucode);
ret = nvkm_vmm_get(acr->vmm, 12, nvkm_memory_size(hsf->ucode),
&hsf->vma);
if (ret)
return ret;
ret = nvkm_memory_map(hsf->ucode, 0, acr->vmm, hsf->vma, NULL, 0);
if (ret)
return ret;
hsf->falcon = falcon;
return 0;
}
int
gm200_acr_unload_boot(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf)
{
return gm200_acr_hsfw_boot(acr, hsf, 0, 0x1d);
}
int
gm200_acr_unload_load(struct nvkm_acr *acr, struct nvkm_acr_hsfw *hsfw)
{
return gm200_acr_hsfw_load(acr, hsfw, &acr->subdev.device->pmu->falcon);
}
const struct nvkm_acr_hsf_func
gm200_acr_unload_0 = {
.load = gm200_acr_unload_load,
.boot = gm200_acr_unload_boot,
.bld = gm200_acr_hsfw_bld,
};
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, nvkm_acr_hsfw_load, &gm200_acr_unload_0 },
{}
};
int
gm200_acr_load_boot(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf)
{
return gm200_acr_hsfw_boot(acr, hsf, 0x10, 0);
}
static int
gm200_acr_load_load(struct nvkm_acr *acr, struct nvkm_acr_hsfw *hsfw)
{
struct flcn_acr_desc *desc = (void *)&hsfw->image[hsfw->data_addr];
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 gm200_acr_hsfw_load(acr, hsfw, &acr->subdev.device->pmu->falcon);
}
static const struct nvkm_acr_hsf_func
gm200_acr_load_0 = {
.load = gm200_acr_load_load,
.boot = gm200_acr_load_boot,
.bld = gm200_acr_hsfw_bld,
};
MODULE_FIRMWARE("nvidia/gm200/acr/bl.bin");
MODULE_FIRMWARE("nvidia/gm200/acr/ucode_load.bin");
@ -38,13 +418,42 @@ 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, nvkm_acr_hsfw_load, &gm200_acr_load_0 },
{}
};
static const struct nvkm_acr_func
gm200_acr = {
.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,
};
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;
}

View File

@ -21,18 +21,103 @@
*/
#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 void
gm20b_acr_load_bld(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf)
{
struct flcn_bl_dmem_desc hsdesc = {
.ctx_dma = FALCON_DMAIDX_VIRT,
.code_dma_base = hsf->vma->addr >> 8,
.non_sec_code_off = hsf->non_sec_addr,
.non_sec_code_size = hsf->non_sec_size,
.sec_code_off = hsf->sec_addr,
.sec_code_size = hsf->sec_size,
.code_entry_point = 0,
.data_dma_base = (hsf->vma->addr + hsf->data_addr) >> 8,
.data_size = hsf->data_size,
};
flcn_bl_dmem_desc_dump(&acr->subdev, &hsdesc);
nvkm_falcon_load_dmem(hsf->falcon, &hsdesc, 0, sizeof(hsdesc), 0);
}
static int
gm20b_acr_load_load(struct nvkm_acr *acr, struct nvkm_acr_hsfw *hsfw)
{
struct flcn_acr_desc *desc = (void *)&hsfw->image[hsfw->data_addr];
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 gm200_acr_hsfw_load(acr, hsfw, &acr->subdev.device->pmu->falcon);
}
const struct nvkm_acr_hsf_func
gm20b_acr_load_0 = {
.load = gm20b_acr_load_load,
.boot = gm200_acr_load_boot,
.bld = gm20b_acr_load_bld,
};
#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, nvkm_acr_hsfw_load, &gm20b_acr_load_0 },
{}
};
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;
}

View File

@ -21,6 +21,156 @@
*/
#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>
void
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);
}
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;
while (hdr->falcon_id != WPR_HEADER_V1_FALCON_ID_INVALID) {
wpr_header_v1_dump(&acr->subdev, hdr);
if (!nvkm_acr_lsfw_add(NULL, acr, NULL, (hdr++)->falcon_id))
return -ENOMEM;
}
return 0;
}
MODULE_FIRMWARE("nvidia/gp102/acr/unload_bl.bin");
MODULE_FIRMWARE("nvidia/gp102/acr/ucode_unload.bin");
@ -33,6 +183,40 @@ 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, nvkm_acr_hsfw_load, &gm200_acr_unload_0 },
{}
};
int
gp102_acr_load_load(struct nvkm_acr *acr, struct nvkm_acr_hsfw *hsfw)
{
struct flcn_acr_desc_v1 *desc = (void *)&hsfw->image[hsfw->data_addr];
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 gm200_acr_hsfw_load(acr, hsfw,
&acr->subdev.device->sec2->falcon);
}
static const struct nvkm_acr_hsf_func
gp102_acr_load_0 = {
.load = gp102_acr_load_load,
.boot = gm200_acr_load_boot,
.bld = gm200_acr_hsfw_bld,
};
MODULE_FIRMWARE("nvidia/gp102/acr/bl.bin");
MODULE_FIRMWARE("nvidia/gp102/acr/ucode_load.bin");
@ -45,13 +229,42 @@ 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, nvkm_acr_hsfw_load, &gp102_acr_load_0 },
{}
};
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;
}

View File

@ -21,20 +21,81 @@
*/
#include "priv.h"
#include <subdev/mmu.h>
#include <nvfw/flcn.h>
void
gp108_acr_hsfw_bld(struct nvkm_acr *acr, struct nvkm_acr_hsf *hsf)
{
struct flcn_bl_dmem_desc_v2 hsdesc = {
.ctx_dma = FALCON_DMAIDX_VIRT,
.code_dma_base = hsf->vma->addr,
.non_sec_code_off = hsf->non_sec_addr,
.non_sec_code_size = hsf->non_sec_size,
.sec_code_off = hsf->sec_addr,
.sec_code_size = hsf->sec_size,
.code_entry_point = 0,
.data_dma_base = hsf->vma->addr + hsf->data_addr,
.data_size = hsf->data_size,
.argc = 0,
.argv = 0,
};
flcn_bl_dmem_desc_v2_dump(&acr->subdev, &hsdesc);
nvkm_falcon_load_dmem(hsf->falcon, &hsdesc, 0, sizeof(hsdesc), 0);
}
const struct nvkm_acr_hsf_func
gp108_acr_unload_0 = {
.load = gm200_acr_unload_load,
.boot = gm200_acr_unload_boot,
.bld = gp108_acr_hsfw_bld,
};
MODULE_FIRMWARE("nvidia/gp108/acr/unload_bl.bin");
MODULE_FIRMWARE("nvidia/gp108/acr/ucode_unload.bin");
MODULE_FIRMWARE("nvidia/gv100/acr/unload_bl.bin");
MODULE_FIRMWARE("nvidia/gv100/acr/ucode_unload.bin");
static const struct nvkm_acr_hsf_fwif
gp108_acr_unload_fwif[] = {
{ 0, nvkm_acr_hsfw_load, &gp108_acr_unload_0 },
{}
};
static const struct nvkm_acr_hsf_func
gp108_acr_load_0 = {
.load = gp102_acr_load_load,
.boot = gm200_acr_load_boot,
.bld = gp108_acr_hsfw_bld,
};
MODULE_FIRMWARE("nvidia/gp108/acr/bl.bin");
MODULE_FIRMWARE("nvidia/gp108/acr/ucode_load.bin");
MODULE_FIRMWARE("nvidia/gv100/acr/bl.bin");
MODULE_FIRMWARE("nvidia/gv100/acr/ucode_load.bin");
static const struct nvkm_acr_hsf_fwif
gp108_acr_load_fwif[] = {
{ 0, nvkm_acr_hsfw_load, &gp108_acr_load_0 },
{}
};
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

View File

@ -26,8 +26,22 @@ 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, nvkm_acr_hsfw_load, &gm20b_acr_load_0 },
{}
};
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

View File

@ -0,0 +1,180 @@
/*
* 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 <nvfw/fw.h>
#include <nvfw/hs.h>
static void
nvkm_acr_hsfw_del(struct nvkm_acr_hsfw *hsfw)
{
list_del(&hsfw->head);
kfree(hsfw->imem);
kfree(hsfw->image);
kfree(hsfw->sig.prod.data);
kfree(hsfw->sig.dbg.data);
kfree(hsfw);
}
void
nvkm_acr_hsfw_del_all(struct nvkm_acr *acr)
{
struct nvkm_acr_hsfw *hsfw, *hsft;
list_for_each_entry_safe(hsfw, hsft, &acr->hsfw, head) {
nvkm_acr_hsfw_del(hsfw);
}
}
static int
nvkm_acr_hsfw_load_image(struct nvkm_acr *acr, const char *name, int ver,
struct nvkm_acr_hsfw *hsfw)
{
struct nvkm_subdev *subdev = &acr->subdev;
const struct firmware *fw;
const struct nvfw_bin_hdr *hdr;
const struct nvfw_hs_header *fwhdr;
const struct nvfw_hs_load_header *lhdr;
u32 loc, sig;
int ret;
ret = nvkm_firmware_get_version(subdev, name, ver, ver, &fw);
if (ret < 0)
return ret;
hdr = nvfw_bin_hdr(subdev, fw->data);
fwhdr = nvfw_hs_header(subdev, fw->data + hdr->header_offset);
/* Earlier FW releases by NVIDIA for Nouveau's use aren't in NVIDIA's
* standard format, and don't have the indirection seen in the 0x10de
* case.
*/
switch (hdr->bin_magic) {
case 0x000010de:
loc = *(u32 *)(fw->data + fwhdr->patch_loc);
sig = *(u32 *)(fw->data + fwhdr->patch_sig);
break;
case 0x3b1d14f0:
loc = fwhdr->patch_loc;
sig = fwhdr->patch_sig;
break;
default:
ret = -EINVAL;
goto done;
}
lhdr = nvfw_hs_load_header(subdev, fw->data + fwhdr->hdr_offset);
if (!(hsfw->image = kmalloc(hdr->data_size, GFP_KERNEL))) {
ret = -ENOMEM;
goto done;
}
memcpy(hsfw->image, fw->data + hdr->data_offset, hdr->data_size);
hsfw->image_size = hdr->data_size;
hsfw->non_sec_addr = lhdr->non_sec_code_off;
hsfw->non_sec_size = lhdr->non_sec_code_size;
hsfw->sec_addr = lhdr->apps[0];
hsfw->sec_size = lhdr->apps[lhdr->num_apps];
hsfw->data_addr = lhdr->data_dma_base;
hsfw->data_size = lhdr->data_size;
hsfw->sig.prod.size = fwhdr->sig_prod_size;
hsfw->sig.prod.data = kmalloc(hsfw->sig.prod.size, GFP_KERNEL);
if (!hsfw->sig.prod.data) {
ret = -ENOMEM;
goto done;
}
memcpy(hsfw->sig.prod.data, fw->data + fwhdr->sig_prod_offset + sig,
hsfw->sig.prod.size);
hsfw->sig.dbg.size = fwhdr->sig_dbg_size;
hsfw->sig.dbg.data = kmalloc(hsfw->sig.dbg.size, GFP_KERNEL);
if (!hsfw->sig.dbg.data) {
ret = -ENOMEM;
goto done;
}
memcpy(hsfw->sig.dbg.data, fw->data + fwhdr->sig_dbg_offset + sig,
hsfw->sig.dbg.size);
hsfw->sig.patch_loc = loc;
done:
nvkm_firmware_put(fw);
return ret;
}
static int
nvkm_acr_hsfw_load_bl(struct nvkm_acr *acr, const char *name, int ver,
struct nvkm_acr_hsfw *hsfw)
{
struct nvkm_subdev *subdev = &acr->subdev;
const struct nvfw_bin_hdr *hdr;
const struct nvfw_bl_desc *desc;
const struct firmware *fw;
u8 *data;
int ret;
ret = nvkm_firmware_get_version(subdev, name, ver, ver, &fw);
if (ret)
return ret;
hdr = nvfw_bin_hdr(subdev, fw->data);
desc = nvfw_bl_desc(subdev, fw->data + hdr->header_offset);
data = (void *)fw->data + hdr->data_offset;
hsfw->imem_size = desc->code_size;
hsfw->imem_tag = desc->start_tag;
hsfw->imem = kmalloc(desc->code_size, GFP_KERNEL);
memcpy(hsfw->imem, data + desc->code_off, desc->code_size);
nvkm_firmware_put(fw);
return 0;
}
int
nvkm_acr_hsfw_load(struct nvkm_acr *acr, const char *bl, const char *fw,
const char *name, int version,
const struct nvkm_acr_hsf_fwif *fwif)
{
struct nvkm_acr_hsfw *hsfw;
int ret;
if (!(hsfw = kzalloc(sizeof(*hsfw), GFP_KERNEL)))
return -ENOMEM;
hsfw->func = fwif->func;
hsfw->name = name;
list_add_tail(&hsfw->head, &acr->hsfw);
ret = nvkm_acr_hsfw_load_bl(acr, bl, version, hsfw);
if (ret)
goto done;
ret = nvkm_acr_hsfw_load_image(acr, fw, version, hsfw);
done:
if (ret)
nvkm_acr_hsfw_del(hsfw);
return ret;
}

View File

@ -1,6 +1,7 @@
#ifndef __NVKM_ACR_PRIV_H__
#define __NVKM_ACR_PRIV_H__
#include <subdev/acr.h>
struct lsb_header_tail;
struct nvkm_acr_fwif {
int version;
@ -12,11 +13,128 @@ 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);
void (*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 *);
};
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 *);
void 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 *);
void gp102_acr_wpr_patch(struct nvkm_acr *, s64);
struct nvkm_acr_hsfw {
const struct nvkm_acr_hsf_func *func;
const char *name;
struct list_head head;
u32 imem_size;
u32 imem_tag;
u32 *imem;
u8 *image;
u32 image_size;
u32 non_sec_addr;
u32 non_sec_size;
u32 sec_addr;
u32 sec_size;
u32 data_addr;
u32 data_size;
struct {
struct {
void *data;
u32 size;
} prod, dbg;
u32 patch_loc;
} sig;
};
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_acr_hsf_func *func;
};
int nvkm_acr_hsfw_load(struct nvkm_acr *, const char *, const char *,
const char *, int, const struct nvkm_acr_hsf_fwif *);
void nvkm_acr_hsfw_del_all(struct nvkm_acr *);
struct nvkm_acr_hsf {
const struct nvkm_acr_hsf_func *func;
const char *name;
struct list_head head;
u32 imem_size;
u32 imem_tag;
u32 *imem;
u32 non_sec_addr;
u32 non_sec_size;
u32 sec_addr;
u32 sec_size;
u32 data_addr;
u32 data_size;
struct nvkm_memory *ucode;
struct nvkm_vma *vma;
struct nvkm_falcon *falcon;
};
struct nvkm_acr_hsf_func {
int (*load)(struct nvkm_acr *, struct nvkm_acr_hsfw *);
int (*boot)(struct nvkm_acr *, struct nvkm_acr_hsf *);
void (*bld)(struct nvkm_acr *, struct nvkm_acr_hsf *);
};
int gm200_acr_hsfw_load(struct nvkm_acr *, struct nvkm_acr_hsfw *,
struct nvkm_falcon *);
int gm200_acr_hsfw_boot(struct nvkm_acr *, struct nvkm_acr_hsf *,
u32 clear_intr, u32 mbox0_ok);
int gm200_acr_load_boot(struct nvkm_acr *, struct nvkm_acr_hsf *);
extern const struct nvkm_acr_hsf_func gm200_acr_unload_0;
int gm200_acr_unload_load(struct nvkm_acr *, struct nvkm_acr_hsfw *);
int gm200_acr_unload_boot(struct nvkm_acr *, struct nvkm_acr_hsf *);
void gm200_acr_hsfw_bld(struct nvkm_acr *, struct nvkm_acr_hsf *);
extern const struct nvkm_acr_hsf_func gm20b_acr_load_0;
int gp102_acr_load_load(struct nvkm_acr *, struct nvkm_acr_hsfw *);
extern const struct nvkm_acr_hsf_func gp108_acr_unload_0;
void gp108_acr_hsfw_bld(struct nvkm_acr *, struct nvkm_acr_hsf *);
int nvkm_acr_new_(const struct nvkm_acr_fwif *, struct nvkm_device *, int,
struct nvkm_acr **);
int nvkm_acr_hsf_boot(struct nvkm_acr *, const char *name);
struct nvkm_acr_lsf {
const struct nvkm_acr_lsf_func *func;

View File

@ -21,9 +21,10 @@
*/
#include "priv.h"
#include <core/msgqueue.h>
#include <core/memory.h>
#include <subdev/acr.h>
#include <nvfw/flcn.h>
#include <nvfw/pmu.h>
static int
@ -60,14 +61,65 @@ int
gm20b_pmu_acr_boot(struct nvkm_falcon *falcon)
{
struct nv_pmu_args args = { .secure_mode = true };
const u32 addr_args = falcon->data.limit - NVKM_MSGQUEUE_CMDLINE_SIZE; /*XXX*/
const u32 addr_args = falcon->data.limit - sizeof(struct nv_pmu_args);
nvkm_falcon_load_dmem(falcon, &args, addr_args, sizeof(args), 0);
nvkm_falcon_start(falcon);
return 0;
}
void
gm20b_pmu_acr_bld_patch(struct nvkm_acr *acr, u32 bld, s64 adjust)
{
struct loader_config hdr;
u64 addr;
nvkm_robj(acr->wpr, bld, &hdr, sizeof(hdr));
addr = ((u64)hdr.code_dma_base1 << 40 | hdr.code_dma_base << 8);
hdr.code_dma_base = lower_32_bits((addr + adjust) >> 8);
hdr.code_dma_base1 = upper_32_bits((addr + adjust) >> 8);
addr = ((u64)hdr.data_dma_base1 << 40 | hdr.data_dma_base << 8);
hdr.data_dma_base = lower_32_bits((addr + adjust) >> 8);
hdr.data_dma_base1 = upper_32_bits((addr + adjust) >> 8);
addr = ((u64)hdr.overlay_dma_base1 << 40 | hdr.overlay_dma_base << 8);
hdr.overlay_dma_base = lower_32_bits((addr + adjust) << 8);
hdr.overlay_dma_base1 = upper_32_bits((addr + adjust) << 8);
nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr));
loader_config_dump(&acr->subdev, &hdr);
}
void
gm20b_pmu_acr_bld_write(struct nvkm_acr *acr, u32 bld,
struct nvkm_acr_lsfw *lsfw)
{
const u64 base = lsfw->offset.img + lsfw->app_start_offset;
const u64 code = (base + lsfw->app_resident_code_offset) >> 8;
const u64 data = (base + lsfw->app_resident_data_offset) >> 8;
const struct loader_config hdr = {
.dma_idx = FALCON_DMAIDX_UCODE,
.code_dma_base = lower_32_bits(code),
.code_size_total = lsfw->app_size,
.code_size_to_load = lsfw->app_resident_code_size,
.code_entry_point = lsfw->app_imem_entry,
.data_dma_base = lower_32_bits(data),
.data_size = lsfw->app_resident_data_size,
.overlay_dma_base = lower_32_bits(code),
.argc = 1,
.argv = lsfw->falcon->data.limit - sizeof(struct nv_pmu_args),
.code_dma_base1 = upper_32_bits(code),
.data_dma_base1 = upper_32_bits(data),
.overlay_dma_base1 = upper_32_bits(code),
};
nvkm_wobj(acr->wpr, bld, &hdr, sizeof(hdr));
}
static const struct nvkm_acr_lsf_func
gm20b_pmu_acr = {
.flags = NVKM_ACR_LSF_DMACTL_REQ_CTX,
.bld_size = sizeof(struct loader_config),
.bld_write = gm20b_pmu_acr_bld_write,
.bld_patch = gm20b_pmu_acr_bld_patch,
.boot = gm20b_pmu_acr_boot,
.bootstrap_falcon = gm20b_pmu_acr_bootstrap_falcon,
};

View File

@ -23,6 +23,7 @@
#include <subdev/acr.h>
#include <nvfw/flcn.h>
#include <nvfw/pmu.h>
static int
@ -58,6 +59,11 @@ gp10b_pmu_acr_bootstrap_multiple_falcons(struct nvkm_falcon *falcon, u32 mask)
static const struct nvkm_acr_lsf_func
gp10b_pmu_acr = {
.flags = NVKM_ACR_LSF_DMACTL_REQ_CTX,
.bld_size = sizeof(struct loader_config),
.bld_write = gm20b_pmu_acr_bld_write,
.bld_patch = gm20b_pmu_acr_bld_patch,
.boot = gm20b_pmu_acr_boot,
.bootstrap_falcon = gm20b_pmu_acr_bootstrap_falcon,
.bootstrap_multiple_falcons = gp10b_pmu_acr_bootstrap_multiple_falcons,
};

View File

@ -5,6 +5,7 @@
#include <subdev/pmu.h>
#include <subdev/pmu/fuc/os.h>
enum nvkm_acr_lsf_id;
struct nvkm_acr_lsfw;
struct nvkm_pmu_func {
const struct nvkm_falcon_func *flcn;
@ -43,6 +44,9 @@ void gf100_pmu_reset(struct nvkm_pmu *);
void gk110_pmu_pgob(struct nvkm_pmu *, bool);
void gm20b_pmu_acr_bld_patch(struct nvkm_acr *, u32, s64);
void gm20b_pmu_acr_bld_write(struct nvkm_acr *, u32, struct nvkm_acr_lsfw *);
int gm20b_pmu_acr_boot(struct nvkm_falcon *);
int gm20b_pmu_acr_bootstrap_falcon(struct nvkm_falcon *, enum nvkm_acr_lsf_id);
void gm20b_pmu_recv(struct nvkm_pmu *);
int gm20b_pmu_initmsg(struct nvkm_pmu *);

View File

@ -900,8 +900,6 @@ acr_r352_wpr_is_set(const struct acr_r352 *acr, const struct nvkm_secboot *sb)
wpr_hi > wpr_range_lo && wpr_hi <= wpr_range_hi);
}
int nvkm_acr_boot_ls_falcons(struct nvkm_device *);
static int
acr_r352_bootstrap(struct acr_r352 *acr, struct nvkm_secboot *sb)
{
@ -934,7 +932,7 @@ acr_r352_bootstrap(struct acr_r352 *acr, struct nvkm_secboot *sb)
return -EINVAL;
}
return nvkm_acr_boot_ls_falcons(subdev->device);
return 0;
}
/**

View File

@ -22,7 +22,6 @@
#ifndef __NVKM_SECBOOT_ACR_R370_H__
#define __NVKM_SECBOOT_ACR_R370_H__
#include "priv.h"
struct hsf_load_header;