diff options
Diffstat (limited to 'kernel/gsp_phys_gpu.c')
| -rw-r--r-- | kernel/gsp_phys_gpu.c | 182 |
1 files changed, 139 insertions, 43 deletions
diff --git a/kernel/gsp_phys_gpu.c b/kernel/gsp_phys_gpu.c index a507793..79c79bf 100644 --- a/kernel/gsp_phys_gpu.c +++ b/kernel/gsp_phys_gpu.c @@ -61,6 +61,7 @@ #include "gsp_phys_gpu.h" #include "gsp_phys_boot.h" +#include "gsp_phys_booter.h" #include "gsp_phys_fwsec.h" #define GSP_PHYS_VENDOR 0x10de @@ -73,6 +74,12 @@ */ #define GSP_PHYS_FW_DEFAULT "nvidia/570.211.01/gsp_tu10x.bin" +/* + * Signature section of the GSP-RM ELF the booter verifies against + * (TU102/TU104 use the tu10x generation name). + */ +#define GSP_PHYS_FW_SIGNATURE_SECTION ".fwsignature_tu10x" + static char *gsp_phys_fw_name = GSP_PHYS_FW_DEFAULT; module_param_named(gsp_phys_fw, gsp_phys_fw_name, charp, 0644); MODULE_PARM_DESC(gsp_phys_fw, "GSP firmware image to load (default " GSP_PHYS_FW_DEFAULT ")"); @@ -134,7 +141,10 @@ struct gsp_phys_gpu { resource_size_t bar3_len; const struct firmware *fw; u32 fw_crc32; + u64 fw_image_offset; u64 fw_image_size; + u64 fw_sig_offset; + u64 fw_sig_size; bool fw_loaded; bool enabled; bool regions; @@ -202,7 +212,10 @@ static void gsp_phys_fw_unload(void) phys.fw = NULL; phys.fw_loaded = false; phys.fw_crc32 = 0; + phys.fw_image_offset = 0; phys.fw_image_size = 0; + phys.fw_sig_offset = 0; + phys.fw_sig_size = 0; } static const char *gsp_phys_fw_section_name(const struct firmware *fw, @@ -279,8 +292,14 @@ static int gsp_phys_fw_load(void) sh->sh_size > phys.fw->size - sh->sh_offset) continue; name = gsp_phys_fw_section_name(phys.fw, sh); - if (!strcmp(name, ".fwimage")) + if (!strcmp(name, ".fwimage")) { + phys.fw_image_offset = sh->sh_offset; phys.fw_image_size = sh->sh_size; + } + if (!strcmp(name, GSP_PHYS_FW_SIGNATURE_SECTION)) { + phys.fw_sig_offset = sh->sh_offset; + phys.fw_sig_size = sh->sh_size; + } pr_info("gsp-phys-fw: [%02u] %-24s type=%u addr=0x%llx " "off=0x%llx size=0x%llx\n", i, name, sh->sh_type, (u64)sh->sh_addr, @@ -289,8 +308,13 @@ static int gsp_phys_fw_load(void) if (phys.fw_image_size) pr_info("gsp-phys-fw: GSP-RM image section .fwimage " - "size=0x%llx (boot-layout input)\n", - phys.fw_image_size); + "off=0x%llx size=0x%llx (boot-layout input)\n", + phys.fw_image_offset, phys.fw_image_size); + if (phys.fw_sig_size) + pr_info("gsp-phys-fw: boot signature section %s " + "off=0x%llx size=0x%llx (boot input)\n", + GSP_PHYS_FW_SIGNATURE_SECTION, + phys.fw_sig_offset, phys.fw_sig_size); } return 0; @@ -627,6 +651,14 @@ static void gsp_phys_claim_failed(struct pci_dev *pdev) gsp_phys_power_unlock(); } +static void gsp_phys_fw_boot_sources(struct gsp_phys_boot_sources *src) +{ + src->fw_image = (const u8 *)phys.fw->data + phys.fw_image_offset; + src->fw_image_size = phys.fw_image_size; + src->fw_signature = (const u8 *)phys.fw->data + phys.fw_sig_offset; + src->fw_signature_size = phys.fw_sig_size; +} + int gsp_phys_gpu_claim(void) { struct pci_dev *pdev; @@ -715,43 +747,102 @@ int gsp_phys_gpu_claim(void) } else { pr_info("gsp-phys: real GSP firmware staged (backend rung 2)\n"); - if (gsp_phys_layout_enabled()) { - if (gsp_phys_boot_layout_init(&phys.boot, phys.pdev, - phys.bar0, - phys.fw_image_size, - phys.bar1_len)) { - pr_warn("gsp-phys: boot layout/allocation failed; " - "backend stays at rung 2\n"); - } else if (gsp_phys_fwsec_enabled() || - gsp_phys_fwsec_dryrun_enabled()) { - if (gsp_phys_fwsec_enabled() && - gsp_phys_fwsec_dryrun_enabled()) { - pr_err("gsp-phys: gsp_phys_fwsec=1 and " - "gsp_phys_fwsec_dryrun=1 are " - "mutually exclusive; refusing " - "both (no writes, nothing run)\n"); - } else if (gsp_phys_fwsec_dryrun_enabled()) { - if (gsp_phys_fwsec_dryrun_run( - &phys.boot, phys.pdev, - phys.bar0)) - pr_warn("gsp-phys: stage 3c " - "dry-run failed " - "(read-only; see log " - "above)\n"); - else - pr_info("gsp-phys: stage 3c " - "dry-run complete " - "(read-only; nothing " - "written)\n"); - } else if (gsp_phys_fwsec_run(&phys.boot, + { + bool boot = gsp_phys_boot_enabled(); + bool boot_dry = gsp_phys_boot_dryrun_enabled(); + bool fwsec = gsp_phys_fwsec_enabled(); + bool fwsec_dry = gsp_phys_fwsec_dryrun_enabled(); + + /* + * Mutually exclusive stage combinations refuse loudly + * and write nothing. The boot lifecycle (gsp_phys_boot) + * owns the only device-writing FWSEC path: FWSEC alone + * is not host-recoverable (its WPR2 carve cannot pass + * the booter-unload handoff gate), so it is refused + * outright. + */ + if ((boot && boot_dry) || (boot && fwsec_dry) || + (boot_dry && fwsec)) { + pr_err("gsp-phys: REFUSED: conflicting stage parameters (gsp_phys_boot=%u gsp_phys_boot_dryrun=%u gsp_phys_fwsec=%u gsp_phys_fwsec_dryrun=%u); refusing all stages (nothing written)\n", + (unsigned int)boot, (unsigned int)boot_dry, + (unsigned int)fwsec, + (unsigned int)fwsec_dry); + } else if (fwsec && !boot) { + pr_err("gsp-phys-fwsec: REFUSED: FWSEC-only carve is not host-recoverable; use gsp_phys_boot=1 (full boot lifecycle) or gsp_phys_fwsec_dryrun=1 (read-only)\n"); + } else if (gsp_phys_layout_enabled() || + gsp_phys_boot_requested() || fwsec_dry) { + if (!gsp_phys_layout_enabled() && + !gsp_phys_boot_requested() && fwsec_dry) + pr_info("gsp-phys: gsp_phys_fwsec_dryrun set without gsp_phys_layout (or gsp_phys_boot); initialising the stage-3b boot layout for the read-only dry-run (nothing written)\n"); + if (gsp_phys_boot_layout_init(&phys.boot, phys.pdev, - phys.bar0)) { - pr_warn("gsp-phys: stage 3c FWSEC failed " - "(see recovery log above)\n"); - } else { - pr_info("gsp-phys: stage 3c FWSEC complete " - "(WPR2 up; release will tear it " - "down)\n"); + phys.bar0, + phys.fw_image_size, + phys.bar1_len)) { + pr_warn("gsp-phys: boot layout/allocation failed; backend stays at rung 2\n"); + } else if (boot) { + struct gsp_phys_boot_sources src; + + if (!phys.fw_loaded || !phys.fw_image_size || + !phys.fw_sig_size) { + pr_err("gsp-phys: gsp_phys_boot=1 but the staged firmware lacks .fwimage/%s; refusing the boot stages (no writes)\n", + GSP_PHYS_FW_SIGNATURE_SECTION); + } else if (gsp_phys_fwsec_board_debug(phys.bar0)) { + pr_err("gsp-phys: gsp_phys_boot=1 on a debug-fuse board but only production boot images are staged; refusing all boot stages before FWSEC (nothing written)\n"); + } else if (gsp_phys_fwsec_run(&phys.boot, + phys.pdev, + phys.bar0)) { + pr_warn("gsp-phys: stage 3c FWSEC failed (see recovery log above)\n"); + } else { + gsp_phys_fw_boot_sources(&src); + pr_info("gsp-phys: stage 3c FWSEC complete (WPR2 up; release will tear it down)\n"); + if (gsp_phys_boot_prime(&phys.boot, + phys.pdev, + phys.bar0, + &src, + false)) { + pr_warn("gsp-phys: stage 3d boot staging failed (see log above)\n"); + } else if (gsp_phys_boot_run(&phys.boot, + phys.pdev, + phys.bar0)) { + pr_warn("gsp-phys: stage 3d boot failed (see log above); release runs the existing WPR2 teardown\n"); + } else { + pr_info("gsp-phys: stage 3d boot complete\n"); + gsp_phys_boot_rmwait(&phys.boot, + phys.bar0, + phys.bar0_len); + } + } + } else if (fwsec_dry || boot_dry) { + struct gsp_phys_boot_sources src; + + if (fwsec_dry) { + if (gsp_phys_fwsec_dryrun_run( + &phys.boot, phys.pdev, + phys.bar0)) + pr_warn("gsp-phys: stage 3c dry-run failed (read-only; see log above)\n"); + else + pr_info("gsp-phys: stage 3c dry-run complete (read-only; nothing written)\n"); + } + if (boot_dry) { + if (!phys.fw_loaded || + !phys.fw_image_size || + !phys.fw_sig_size) { + pr_warn("gsp-phys: stage 3d dry-run unavailable: staged firmware lacks .fwimage/%s (read-only)\n", + GSP_PHYS_FW_SIGNATURE_SECTION); + } else { + gsp_phys_fw_boot_sources(&src); + if (gsp_phys_boot_prime( + &phys.boot, + phys.pdev, + phys.bar0, + &src, + true)) + pr_warn("gsp-phys: stage 3d dry-run prime failed (read-only; see log above)\n"); + else + pr_info("gsp-phys: stage 3d dry-run prime complete (read-only; nothing written)\n"); + } + } } } } @@ -795,18 +886,23 @@ void gsp_phys_gpu_release(void) * WPR2 teardown must run while this module still owns BAR0. A * successful stage 3c leaves WPR2 carved in FB; the distribution * driver refuses to boot against an unexpected WPR2, so it has to be - * removed (FWSEC-SB + booter unload) before the hand-back. If the - * board stopped answering, skip the teardown (writes to a dead - * device are pointless) and let the fail-safe below refuse the - * hand-back. + * removed (FWSEC-SB + booter unload) before the hand-back. If stage 3d + * issued a Booter Load kick, the GSP falcon is quiesced first; the boot + * staging is then freed only after the teardown has reset both falcons, + * so no ucode can still DMA from it. If the board stopped answering, + * skip device writes (staging is still freed) and let the fail-safe + * below refuse the hand-back. */ if (phys.pdev) { if (gsp_phys_board_check(phys.pdev, "before WPR2 teardown")) { wpr2_ret = -EIO; + gsp_phys_boot_staging_free(phys.pdev); } else { + gsp_phys_boot_quiesce(phys.bar0); wpr2_ret = gsp_phys_fwsec_release(&phys.boot, phys.pdev, phys.bar0); + gsp_phys_boot_staging_free(phys.pdev); } } |
