diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-03 11:44:24 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-03 11:44:24 -0700 |
commit | 2f34c1231bfc9f2550f934acb268ac7315fb3837 (patch) | |
tree | ff8114b3b4ec4723a11b041c6b74c389e9f0eeb9 /drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c | |
parent | a3719f34fdb664ffcfaec2160ef20fca7becf2ee (diff) | |
parent | 8b03d1ed2c43a2ba5ef3381322ee4515b97381bf (diff) | |
download | linux-2f34c1231bfc9f2550f934acb268ac7315fb3837.tar.gz linux-2f34c1231bfc9f2550f934acb268ac7315fb3837.tar.bz2 linux-2f34c1231bfc9f2550f934acb268ac7315fb3837.zip |
Merge tag 'drm-for-v4.12' of git://people.freedesktop.org/~airlied/linux
Pull drm u pdates from Dave Airlie:
"This is the main drm pull request for v4.12. Apart from two fixes
pulls, everything should have been in drm-next for at least 2 weeks.
The biggest thing in here is AMD released the public headers for their
upcoming VEGA GPUs. These as always are quite a sizeable chunk of
header files. They've also added initial non-display support for those
GPUs, though they aren't available in production yet.
Otherwise it's pretty much normal.
New bridge drivers:
- megachips-stdpxxxx-ge-b850v3-fw LVDS->DP++
- generic LVDS bridge support.
Core:
- Displayport link train failure reporting to userspace
- debugfs interface cleaned up
- subsystem TODO in kerneldoc now
- Extended fbdev support (flipping and vblank wait)
- drm_platform removed
- EDP CRC support in helper
- HF-VSDB SCDC support in EDID parser
- Lots of code cleanups and header extraction
- Thunderbolt external GPU awareness
- Atomic helper improvements
- Documentation improvements
panel:
- Sitronix and Samsung new panel support
amdgpu:
- Preliminary vega10 support
- Multi-level page table support
- GPU sensor support for userspace
- PRT support for sparse buffers
- SR-IOV improvements
- Non-contig VRAM CPU mapping
i915:
- Atomic modesetting enabled by default on Gen5+
- LSPCON improvements
- Atomic state handling for cdclk
- GPU reset improvements
- In-kernel unit tests
- Geminilake improvements and color manager support
- Designware i2c fixes
- vblank evasion improvements
- Hotplug safe connector iterators
- GVT scheduler QoS support
- GVT Kabylake support
nouveau:
- Acceleration support for Pascal (GP10x).
- Rearchitecture of code handling proprietary signed firmware
- Fix GTX 970 with odd MMU configuration
- GP10B support
- GP107 acceleration support
vmwgfx:
- Atomic modesetting support for vmwgfx
omapdrm:
- Support for render nodes
- Refactor omapdss code
- Fix some probe ordering issues
- Fix too dark RGB565 rendering
sunxi:
- prelim rework for multiple pipes.
mali-dp:
- Color management support
- Plane scaling
- Power management improvements
imx-drm:
- Prefetch Resolve Engine/Gasket on i.MX6QP
- Deferred plane disabling
- Separate alpha support
mediatek:
- Mediatek SoC MT2701 support
rcar-du:
- Gen3 HDMI support
msm:
- 4k support for newer chips
- OPP bindings for gpu
- prep work for per-process pagetables
vc4:
- HDMI audio support
- fixes
qxl:
- minor fixes.
dw-hdmi:
- PHY improvements
- CSC fixes
- Amlogic GX SoC support"
* tag 'drm-for-v4.12' of git://people.freedesktop.org/~airlied/linux: (1778 commits)
drm/nouveau/fb/gf100-: Fix 32 bit wraparound in new ram detection
drm/nouveau/secboot/gm20b: fix the error return code in gm20b_secboot_tegra_read_wpr()
drm/nouveau/kms: Increase max retries in scanout position queries.
drm/nouveau/bios/bitP: check that table is long enough for optional pointers
drm/nouveau/fifo/nv40: no ctxsw for pre-nv44 mpeg engine
drm: mali-dp: use div_u64 for expensive 64-bit divisions
drm/i915: Confirm the request is still active before adding it to the await
drm/i915: Avoid busy-spinning on VLV_GLTC_PW_STATUS mmio
drm/i915/selftests: Allocate inode/file dynamically
drm/i915: Fix system hang with EI UP masked on Haswell
drm/i915: checking for NULL instead of IS_ERR() in mock selftests
drm/i915: Perform link quality check unconditionally during long pulse
drm/i915: Fix use after free in lpe_audio_platdev_destroy()
drm/i915: Use the right mapping_gfp_mask for final shmem allocation
drm/i915: Make legacy cursor updates more unsynced
drm/i915: Apply a cond_resched() to the saturated signaler
drm/i915: Park the signaler before sleeping
drm: mali-dp: Check the mclk rate and allow up/down scaling
drm: mali-dp: Enable image enhancement when scaling
drm: mali-dp: Add plane upscaling support
...
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c | 565 |
1 files changed, 412 insertions, 153 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c index 1aa37ea18580..a721354249ce 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c @@ -21,35 +21,14 @@ */ #include "acr_r352.h" +#include "hs_ucode.h" #include <core/gpuobj.h> #include <core/firmware.h> #include <engine/falcon.h> - -/** - * struct hsf_fw_header - HS firmware descriptor - * @sig_dbg_offset: offset of the debug signature - * @sig_dbg_size: size of the debug signature - * @sig_prod_offset: offset of the production signature - * @sig_prod_size: size of the production signature - * @patch_loc: offset of the offset (sic) of where the signature is - * @patch_sig: offset of the offset (sic) to add to sig_*_offset - * @hdr_offset: offset of the load header (see struct hs_load_header) - * @hdr_size: size of above header - * - * This structure is embedded in the HS firmware image at - * hs_bin_hdr.header_offset. - */ -struct hsf_fw_header { - u32 sig_dbg_offset; - u32 sig_dbg_size; - u32 sig_prod_offset; - u32 sig_prod_size; - u32 patch_loc; - u32 patch_sig; - u32 hdr_offset; - u32 hdr_size; -}; +#include <subdev/pmu.h> +#include <core/msgqueue.h> +#include <engine/sec2.h> /** * struct acr_r352_flcn_bl_desc - DMEM bootloader descriptor @@ -95,15 +74,14 @@ struct acr_r352_flcn_bl_desc { */ static void acr_r352_generate_flcn_bl_desc(const struct nvkm_acr *acr, - const struct ls_ucode_img *_img, u64 wpr_addr, + const struct ls_ucode_img *img, u64 wpr_addr, void *_desc) { - struct ls_ucode_img_r352 *img = ls_ucode_img_r352(_img); struct acr_r352_flcn_bl_desc *desc = _desc; - const struct ls_ucode_img_desc *pdesc = &_img->ucode_desc; + const struct ls_ucode_img_desc *pdesc = &img->ucode_desc; u64 base, addr_code, addr_data; - base = wpr_addr + img->lsb_header.ucode_off + pdesc->app_start_offset; + base = wpr_addr + img->ucode_off + pdesc->app_start_offset; addr_code = (base + pdesc->app_resident_code_offset) >> 8; addr_data = (base + pdesc->app_resident_data_offset) >> 8; @@ -167,10 +145,101 @@ struct hsflcn_acr_desc { */ /** + * struct acr_r352_lsf_lsb_header - LS firmware header + * @signature: signature to verify the firmware against + * @ucode_off: offset of the ucode blob in the WPR region. The ucode + * blob contains the bootloader, code and data of the + * LS falcon + * @ucode_size: size of the ucode blob, including bootloader + * @data_size: size of the ucode blob data + * @bl_code_size: size of the bootloader code + * @bl_imem_off: offset in imem of the bootloader + * @bl_data_off: offset of the bootloader data in WPR region + * @bl_data_size: size of the bootloader data + * @app_code_off: offset of the app code relative to ucode_off + * @app_code_size: size of the app code + * @app_data_off: offset of the app data relative to ucode_off + * @app_data_size: size of the app data + * @flags: flags for the secure bootloader + * + * This structure is written into the WPR region for each managed falcon. Each + * instance is referenced by the lsb_offset member of the corresponding + * lsf_wpr_header. + */ +struct acr_r352_lsf_lsb_header { + /** + * LS falcon signatures + * @prd_keys: signature to use in production mode + * @dgb_keys: signature to use in debug mode + * @b_prd_present: whether the production key is present + * @b_dgb_present: whether the debug key is present + * @falcon_id: ID of the falcon the ucode applies to + */ + struct { + u8 prd_keys[2][16]; + u8 dbg_keys[2][16]; + u32 b_prd_present; + u32 b_dbg_present; + u32 falcon_id; + } signature; + 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 acr_r352_lsf_wpr_header - LS blob WPR Header + * @falcon_id: LS falcon ID + * @lsb_offset: offset of the lsb_lsf_header in the WPR region + * @bootstrap_owner: secure falcon reponsible for bootstrapping the LS falcon + * @lazy_bootstrap: skip bootstrapping by ACR + * @status: bootstrapping status + * + * An array of these is written at the beginning of the WPR region, one for + * each managed falcon. The array is terminated by an instance which falcon_id + * is LSF_FALCON_ID_INVALID. + */ +struct acr_r352_lsf_wpr_header { + u32 falcon_id; + u32 lsb_offset; + u32 bootstrap_owner; + u32 lazy_bootstrap; + u32 status; +#define LSF_IMAGE_STATUS_NONE 0 +#define LSF_IMAGE_STATUS_COPY 1 +#define LSF_IMAGE_STATUS_VALIDATION_CODE_FAILED 2 +#define LSF_IMAGE_STATUS_VALIDATION_DATA_FAILED 3 +#define LSF_IMAGE_STATUS_VALIDATION_DONE 4 +#define LSF_IMAGE_STATUS_VALIDATION_SKIPPED 5 +#define LSF_IMAGE_STATUS_BOOTSTRAP_READY 6 +}; + +/** + * struct ls_ucode_img_r352 - ucode image augmented with r352 headers + */ +struct ls_ucode_img_r352 { + struct ls_ucode_img base; + + struct acr_r352_lsf_wpr_header wpr_header; + struct acr_r352_lsf_lsb_header lsb_header; +}; +#define ls_ucode_img_r352(i) container_of(i, struct ls_ucode_img_r352, base) + +/** * ls_ucode_img_load() - create a lsf_ucode_img and load it */ struct ls_ucode_img * acr_r352_ls_ucode_img_load(const struct acr_r352 *acr, + const struct nvkm_secboot *sb, enum nvkm_secboot_falcon falcon_id) { const struct nvkm_subdev *subdev = acr->base.subdev; @@ -183,7 +252,7 @@ acr_r352_ls_ucode_img_load(const struct acr_r352 *acr, img->base.falcon_id = falcon_id; - ret = acr->func->ls_func[falcon_id]->load(subdev, &img->base); + ret = acr->func->ls_func[falcon_id]->load(sb, &img->base); if (ret) { kfree(img->base.ucode_data); @@ -255,7 +324,7 @@ acr_r352_ls_img_fill_headers(struct acr_r352 *acr, * image size */ offset = ALIGN(offset, LSF_UCODE_DATA_ALIGN); - lhdr->ucode_off = offset; + _img->ucode_off = lhdr->ucode_off = offset; offset += _img->ucode_size; /* @@ -341,7 +410,7 @@ acr_r352_ls_fill_headers(struct acr_r352 *acr, struct list_head *imgs) */ int acr_r352_ls_write_wpr(struct acr_r352 *acr, struct list_head *imgs, - struct nvkm_gpuobj *wpr_blob, u32 wpr_addr) + struct nvkm_gpuobj *wpr_blob, u64 wpr_addr) { struct ls_ucode_img *_img; u32 pos = 0; @@ -381,8 +450,8 @@ acr_r352_ls_write_wpr(struct acr_r352 *acr, struct list_head *imgs, return 0; } -/* Both size and address of WPR need to be 128K-aligned */ -#define WPR_ALIGNMENT 0x20000 +/* Both size and address of WPR need to be 256K-aligned */ +#define WPR_ALIGNMENT 0x40000 /** * acr_r352_prepare_ls_blob() - prepare the LS blob * @@ -392,14 +461,16 @@ acr_r352_ls_write_wpr(struct acr_r352 *acr, struct list_head *imgs, * will be copied into the WPR region by the HS firmware. */ static int -acr_r352_prepare_ls_blob(struct acr_r352 *acr, u64 wpr_addr, u32 wpr_size) +acr_r352_prepare_ls_blob(struct acr_r352 *acr, struct nvkm_secboot *sb) { const struct nvkm_subdev *subdev = acr->base.subdev; struct list_head imgs; struct ls_ucode_img *img, *t; unsigned long managed_falcons = acr->base.managed_falcons; + u64 wpr_addr = sb->wpr_addr; + u32 wpr_size = sb->wpr_size; int managed_count = 0; - u32 image_wpr_size; + u32 image_wpr_size, ls_blob_size; int falcon_id; int ret; @@ -409,8 +480,14 @@ acr_r352_prepare_ls_blob(struct acr_r352 *acr, u64 wpr_addr, u32 wpr_size) for_each_set_bit(falcon_id, &managed_falcons, NVKM_SECBOOT_FALCON_END) { struct ls_ucode_img *img; - img = acr->func->ls_ucode_img_load(acr, falcon_id); + img = acr->func->ls_ucode_img_load(acr, sb, falcon_id); if (IS_ERR(img)) { + if (acr->base.optional_falcons & BIT(falcon_id)) { + managed_falcons &= ~BIT(falcon_id); + nvkm_info(subdev, "skipping %s falcon...\n", + nvkm_secboot_falcon_name[falcon_id]); + continue; + } ret = PTR_ERR(img); goto cleanup; } @@ -419,6 +496,24 @@ acr_r352_prepare_ls_blob(struct acr_r352 *acr, u64 wpr_addr, u32 wpr_size) managed_count++; } + /* Commit the actual list of falcons we will manage from now on */ + acr->base.managed_falcons = managed_falcons; + + /* + * If the boot falcon has a firmare, let it manage the bootstrap of other + * falcons. + */ + if (acr->func->ls_func[acr->base.boot_falcon] && + (managed_falcons & BIT(acr->base.boot_falcon))) { + for_each_set_bit(falcon_id, &managed_falcons, + NVKM_SECBOOT_FALCON_END) { + if (falcon_id == acr->base.boot_falcon) + continue; + + acr->lazy_bootstrap |= BIT(falcon_id); + } + } + /* * Fill the WPR and LSF headers with the right offsets and compute * required WPR size @@ -426,8 +521,17 @@ acr_r352_prepare_ls_blob(struct acr_r352 *acr, u64 wpr_addr, u32 wpr_size) image_wpr_size = acr->func->ls_fill_headers(acr, &imgs); image_wpr_size = ALIGN(image_wpr_size, WPR_ALIGNMENT); + ls_blob_size = image_wpr_size; + + /* + * If we need a shadow area, allocate twice the size and use the + * upper half as WPR + */ + if (wpr_size == 0 && acr->func->shadow_blob) + ls_blob_size *= 2; + /* Allocate GPU object that will contain the WPR region */ - ret = nvkm_gpuobj_new(subdev->device, image_wpr_size, WPR_ALIGNMENT, + ret = nvkm_gpuobj_new(subdev->device, ls_blob_size, WPR_ALIGNMENT, false, NULL, &acr->ls_blob); if (ret) goto cleanup; @@ -438,6 +542,9 @@ acr_r352_prepare_ls_blob(struct acr_r352 *acr, u64 wpr_addr, u32 wpr_size) /* If WPR address and size are not fixed, set them to fit the LS blob */ if (wpr_size == 0) { wpr_addr = acr->ls_blob->addr; + if (acr->func->shadow_blob) + wpr_addr += acr->ls_blob->size / 2; + wpr_size = image_wpr_size; /* * But if the WPR region is set by the bootloader, it is illegal for @@ -469,41 +576,17 @@ cleanup: -/** - * acr_r352_hsf_patch_signature() - patch HS blob with correct signature - */ -static void -acr_r352_hsf_patch_signature(struct nvkm_secboot *sb, void *acr_image) -{ - struct fw_bin_header *hsbin_hdr = acr_image; - struct hsf_fw_header *fw_hdr = acr_image + hsbin_hdr->header_offset; - void *hs_data = acr_image + hsbin_hdr->data_offset; - void *sig; - u32 sig_size; - - /* Falcon in debug or production mode? */ - if (sb->boot_falcon->debug) { - sig = acr_image + fw_hdr->sig_dbg_offset; - sig_size = fw_hdr->sig_dbg_size; - } else { - sig = acr_image + fw_hdr->sig_prod_offset; - sig_size = fw_hdr->sig_prod_size; - } - - /* Patch signature */ - memcpy(hs_data + fw_hdr->patch_loc, sig + fw_hdr->patch_sig, sig_size); -} - -static void +void acr_r352_fixup_hs_desc(struct acr_r352 *acr, struct nvkm_secboot *sb, - struct hsflcn_acr_desc *desc) + void *_desc) { + struct hsflcn_acr_desc *desc = _desc; struct nvkm_gpuobj *ls_blob = acr->ls_blob; /* WPR region information if WPR is not fixed */ if (sb->wpr_size == 0) { - u32 wpr_start = ls_blob->addr; - u32 wpr_end = wpr_start + ls_blob->size; + u64 wpr_start = ls_blob->addr; + u64 wpr_end = wpr_start + ls_blob->size; desc->wpr_region_id = 1; desc->regions.no_regions = 2; @@ -533,8 +616,8 @@ acr_r352_generate_hs_bl_desc(const struct hsf_load_header *hdr, void *_bl_desc, bl_desc->code_dma_base = lower_32_bits(addr_code); bl_desc->non_sec_code_off = hdr->non_sec_code_off; bl_desc->non_sec_code_size = hdr->non_sec_code_size; - bl_desc->sec_code_off = hdr->app[0].sec_code_off; - bl_desc->sec_code_size = hdr->app[0].sec_code_size; + bl_desc->sec_code_off = hsf_load_header_app_off(hdr, 0); + bl_desc->sec_code_size = hsf_load_header_app_size(hdr, 0); bl_desc->code_entry_point = 0; bl_desc->data_dma_base = lower_32_bits(addr_data); bl_desc->data_size = hdr->data_size; @@ -562,7 +645,7 @@ acr_r352_prepare_hs_blob(struct acr_r352 *acr, struct nvkm_secboot *sb, void *acr_data; int ret; - acr_image = nvkm_acr_load_firmware(subdev, fw, 0); + acr_image = hs_ucode_load_blob(subdev, sb->boot_falcon, fw); if (IS_ERR(acr_image)) return PTR_ERR(acr_image); @@ -571,15 +654,12 @@ acr_r352_prepare_hs_blob(struct acr_r352 *acr, struct nvkm_secboot *sb, load_hdr = acr_image + fw_hdr->hdr_offset; acr_data = acr_image + hsbin_hdr->data_offset; - /* Patch signature */ - acr_r352_hsf_patch_signature(sb, acr_image); - /* Patch descriptor with WPR information? */ if (patch) { struct hsflcn_acr_desc *desc; desc = acr_data + load_hdr->data_dma_base; - acr_r352_fixup_hs_desc(acr, sb, desc); + acr->func->fixup_hs_desc(acr, sb, desc); } if (load_hdr->num_apps > ACR_R352_MAX_APPS) { @@ -589,7 +669,7 @@ acr_r352_prepare_hs_blob(struct acr_r352 *acr, struct nvkm_secboot *sb, goto cleanup; } memcpy(load_header, load_hdr, sizeof(*load_header) + - (sizeof(load_hdr->app[0]) * load_hdr->num_apps)); + (sizeof(load_hdr->apps[0]) * 2 * load_hdr->num_apps)); /* Create ACR blob and copy HS data to it */ ret = nvkm_gpuobj_new(subdev->device, ALIGN(hsbin_hdr->data_size, 256), @@ -607,30 +687,6 @@ cleanup: return ret; } -static int -acr_r352_prepare_hsbl_blob(struct acr_r352 *acr) -{ - const struct nvkm_subdev *subdev = acr->base.subdev; - struct fw_bin_header *hdr; - struct fw_bl_desc *hsbl_desc; - - acr->hsbl_blob = nvkm_acr_load_firmware(subdev, "acr/bl", 0); - if (IS_ERR(acr->hsbl_blob)) { - int ret = PTR_ERR(acr->hsbl_blob); - - acr->hsbl_blob = NULL; - return ret; - } - - hdr = acr->hsbl_blob; - hsbl_desc = acr->hsbl_blob + hdr->header_offset; - - /* virtual start address for boot vector */ - acr->base.start_address = hsbl_desc->start_tag << 8; - - return 0; -} - /** * acr_r352_load_blobs - load blobs common to all ACR V1 versions. * @@ -641,6 +697,7 @@ acr_r352_prepare_hsbl_blob(struct acr_r352 *acr) int acr_r352_load_blobs(struct acr_r352 *acr, struct nvkm_secboot *sb) { + struct nvkm_subdev *subdev = &sb->subdev; int ret; /* Firmware already loaded? */ @@ -648,7 +705,7 @@ acr_r352_load_blobs(struct acr_r352 *acr, struct nvkm_secboot *sb) return 0; /* Load and prepare the managed falcon's firmwares */ - ret = acr_r352_prepare_ls_blob(acr, sb->wpr_addr, sb->wpr_size); + ret = acr_r352_prepare_ls_blob(acr, sb); if (ret) return ret; @@ -672,9 +729,24 @@ acr_r352_load_blobs(struct acr_r352 *acr, struct nvkm_secboot *sb) /* Load the HS firmware bootloader */ if (!acr->hsbl_blob) { - ret = acr_r352_prepare_hsbl_blob(acr); - if (ret) + acr->hsbl_blob = nvkm_acr_load_firmware(subdev, "acr/bl", 0); + if (IS_ERR(acr->hsbl_blob)) { + ret = PTR_ERR(acr->hsbl_blob); + acr->hsbl_blob = NULL; return ret; + } + + if (acr->base.boot_falcon != NVKM_SECBOOT_FALCON_PMU) { + acr->hsbl_unload_blob = nvkm_acr_load_firmware(subdev, + "acr/unload_bl", 0); + if (IS_ERR(acr->hsbl_unload_blob)) { + ret = PTR_ERR(acr->hsbl_unload_blob); + acr->hsbl_unload_blob = NULL; + return ret; + } + } else { + acr->hsbl_unload_blob = acr->hsbl_blob; + } } acr->firmware_ok = true; @@ -684,35 +756,42 @@ acr_r352_load_blobs(struct acr_r352 *acr, struct nvkm_secboot *sb) } /** - * acr_r352_load() - prepare HS falcon to run the specified blob, mapped - * at GPU address offset. + * acr_r352_load() - prepare HS falcon to run the specified blob, mapped. + * + * Returns the start address to use, or a negative error value. */ static int -acr_r352_load(struct nvkm_acr *_acr, struct nvkm_secboot *sb, +acr_r352_load(struct nvkm_acr *_acr, struct nvkm_falcon *falcon, struct nvkm_gpuobj *blob, u64 offset) { struct acr_r352 *acr = acr_r352(_acr); - struct nvkm_falcon *falcon = sb->boot_falcon; - struct fw_bin_header *hdr = acr->hsbl_blob; - struct fw_bl_desc *hsbl_desc = acr->hsbl_blob + hdr->header_offset; - void *blob_data = acr->hsbl_blob + hdr->data_offset; - void *hsbl_code = blob_data + hsbl_desc->code_off; - void *hsbl_data = blob_data + hsbl_desc->data_off; - u32 code_size = ALIGN(hsbl_desc->code_size, 256); - const struct hsf_load_header *load_hdr; const u32 bl_desc_size = acr->func->hs_bl_desc_size; + const struct hsf_load_header *load_hdr; + struct fw_bin_header *bl_hdr; + struct fw_bl_desc *hsbl_desc; + void *bl, *blob_data, *hsbl_code, *hsbl_data; + u32 code_size; u8 bl_desc[bl_desc_size]; /* Find the bootloader descriptor for our blob and copy it */ if (blob == acr->load_blob) { load_hdr = &acr->load_bl_header; + bl = acr->hsbl_blob; } else if (blob == acr->unload_blob) { load_hdr = &acr->unload_bl_header; + bl = acr->hsbl_unload_blob; } else { nvkm_error(_acr->subdev, "invalid secure boot blob!\n"); return -EINVAL; } + bl_hdr = bl; + hsbl_desc = bl + bl_hdr->header_offset; + blob_data = bl + bl_hdr->data_offset; + hsbl_code = blob_data + hsbl_desc->code_off; + hsbl_data = blob_data + hsbl_desc->data_off; + code_size = ALIGN(hsbl_desc->code_size, 256); + /* * Copy HS bootloader data */ @@ -732,23 +811,32 @@ acr_r352_load(struct nvkm_acr *_acr, struct nvkm_secboot *sb, nvkm_falcon_load_dmem(falcon, bl_desc, hsbl_desc->dmem_load_off, bl_desc_size, 0); - return 0; + return hsbl_desc->start_tag << 8; } static int acr_r352_shutdown(struct acr_r352 *acr, struct nvkm_secboot *sb) { + struct nvkm_subdev *subdev = &sb->subdev; int i; /* Run the unload blob to unprotect the WPR region */ if (acr->unload_blob && sb->wpr_set) { int ret; - nvkm_debug(&sb->subdev, "running HS unload blob\n"); - ret = sb->func->run_blob(sb, acr->unload_blob); - if (ret) + nvkm_debug(subdev, "running HS unload blob\n"); + ret = sb->func->run_blob(sb, acr->unload_blob, sb->halt_falcon); + if (ret < 0) return ret; - nvkm_debug(&sb->subdev, "HS unload blob completed\n"); + /* + * Unload blob will return this error code - it is not an error + * and the expected behavior on RM as well + */ + if (ret && ret != 0x1d) { + nvkm_error(subdev, "HS unload failed, ret 0x%08x", ret); + return -EINVAL; + } + nvkm_debug(subdev, "HS unload blob completed\n"); } for (i = 0; i < NVKM_SECBOOT_FALCON_END; i++) @@ -759,9 +847,43 @@ acr_r352_shutdown(struct acr_r352 *acr, struct nvkm_secboot *sb) return 0; } +/** + * Check if the WPR region has been indeed set by the ACR firmware, and + * matches where it should be. + */ +static bool +acr_r352_wpr_is_set(const struct acr_r352 *acr, const struct nvkm_secboot *sb) +{ + const struct nvkm_subdev *subdev = &sb->subdev; + const struct nvkm_device *device = subdev->device; + u64 wpr_lo, wpr_hi; + u64 wpr_range_lo, wpr_range_hi; + + nvkm_wr32(device, 0x100cd4, 0x2); + wpr_lo = (nvkm_rd32(device, 0x100cd4) & ~0xff); + wpr_lo <<= 8; + nvkm_wr32(device, 0x100cd4, 0x3); + wpr_hi = (nvkm_rd32(device, 0x100cd4) & ~0xff); + wpr_hi <<= 8; + + if (sb->wpr_size != 0) { + wpr_range_lo = sb->wpr_addr; + wpr_range_hi = wpr_range_lo + sb->wpr_size; + } else { + wpr_range_lo = acr->ls_blob->addr; + wpr_range_hi = wpr_range_lo + acr->ls_blob->size; + } + + return (wpr_lo >= wpr_range_lo && wpr_lo < wpr_range_hi && + wpr_hi > wpr_range_lo && wpr_hi <= wpr_range_hi); +} + static int acr_r352_bootstrap(struct acr_r352 *acr, struct nvkm_secboot *sb) { + const struct nvkm_subdev *subdev = &sb->subdev; + unsigned long managed_falcons = acr->base.managed_falcons; + int falcon_id; int ret; if (sb->wpr_set) @@ -772,79 +894,127 @@ acr_r352_bootstrap(struct acr_r352 *acr, struct nvkm_secboot *sb) if (ret) return ret; - nvkm_debug(&sb->subdev, "running HS load blob\n"); - ret = sb->func->run_blob(sb, acr->load_blob); + nvkm_debug(subdev, "running HS load blob\n"); + ret = sb->func->run_blob(sb, acr->load_blob, sb->boot_falcon); /* clear halt interrupt */ nvkm_falcon_clear_interrupt(sb->boot_falcon, 0x10); - if (ret) + sb->wpr_set = acr_r352_wpr_is_set(acr, sb); + if (ret < 0) { return ret; - nvkm_debug(&sb->subdev, "HS load blob completed\n"); + } else if (ret > 0) { + nvkm_error(subdev, "HS load failed, ret 0x%08x", ret); + return -EINVAL; + } + nvkm_debug(subdev, "HS load blob completed\n"); + /* WPR must be set at this point */ + if (!sb->wpr_set) { + nvkm_error(subdev, "ACR blob completed but WPR not set!\n"); + return -EINVAL; + } + + /* Run LS firmwares post_run hooks */ + for_each_set_bit(falcon_id, &managed_falcons, NVKM_SECBOOT_FALCON_END) { + const struct acr_r352_ls_func *func = + acr->func->ls_func[falcon_id]; - sb->wpr_set = true; + if (func->post_run) { + ret = func->post_run(&acr->base, sb); + if (ret) + return ret; + } + } return 0; } -/* - * acr_r352_reset() - execute secure boot from the prepared state +/** + * acr_r352_reset_nopmu - dummy reset method when no PMU firmware is loaded * - * Load the HS bootloader and ask the falcon to run it. This will in turn - * load the HS firmware and run it, so once the falcon stops all the managed - * falcons should have their LS firmware loaded and be ready to run. + * Reset is done by re-executing secure boot from scratch, with lazy bootstrap + * disabled. This has the effect of making all managed falcons ready-to-run. */ static int -acr_r352_reset(struct nvkm_acr *_acr, struct nvkm_secboot *sb, - enum nvkm_secboot_falcon falcon) +acr_r352_reset_nopmu(struct acr_r352 *acr, struct nvkm_secboot *sb, + unsigned long falcon_mask) { - struct acr_r352 *acr = acr_r352(_acr); + int falcon; int ret; /* - * Dummy GM200 implementation: perform secure boot each time we are - * called on FECS. Since only FECS and GPCCS are managed and started - * together, this ought to be safe. - * - * Once we have proper PMU firmware and support, this will be changed - * to a proper call to the PMU method. + * Perform secure boot each time we are called on FECS. Since only FECS + * and GPCCS are managed and started together, this ought to be safe. */ - if (falcon != NVKM_SECBOOT_FALCON_FECS) + if (!(falcon_mask & BIT(NVKM_SECBOOT_FALCON_FECS))) goto end; ret = acr_r352_shutdown(acr, sb); if (ret) return ret; - acr_r352_bootstrap(acr, sb); + ret = acr_r352_bootstrap(acr, sb); if (ret) return ret; end: - acr->falcon_state[falcon] = RESET; + for_each_set_bit(falcon, &falcon_mask, NVKM_SECBOOT_FALCON_END) { + acr->falcon_state[falcon] = RESET; + } return 0; } +/* + * acr_r352_reset() - execute secure boot from the prepared state + * + * Load the HS bootloader and ask the falcon to run it. This will in turn + * load the HS firmware and run it, so once the falcon stops all the managed + * falcons should have their LS firmware loaded and be ready to run. + */ static int -acr_r352_start(struct nvkm_acr *_acr, struct nvkm_secboot *sb, - enum nvkm_secboot_falcon falcon) +acr_r352_reset(struct nvkm_acr *_acr, struct nvkm_secboot *sb, + unsigned long falcon_mask) { struct acr_r352 *acr = acr_r352(_acr); - const struct nvkm_subdev *subdev = &sb->subdev; - int base; + struct nvkm_msgqueue *queue; + int falcon; + bool wpr_already_set = sb->wpr_set; + int ret; + + /* Make sure secure boot is performed */ + ret = acr_r352_bootstrap(acr, sb); + if (ret) + return ret; - switch (falcon) { - case NVKM_SECBOOT_FALCON_FECS: - base = 0x409000; + /* No PMU interface? */ + if (!nvkm_secboot_is_managed(sb, _acr->boot_falcon)) { + /* Redo secure boot entirely if it was already done */ + if (wpr_already_set) + return acr_r352_reset_nopmu(acr, sb, falcon_mask); + /* Else return the result of the initial invokation */ + else + return ret; + } + + switch (_acr->boot_falcon) { + case NVKM_SECBOOT_FALCON_PMU: + queue = sb->subdev.device->pmu->queue; break; - case NVKM_SECBOOT_FALCON_GPCCS: - base = 0x41a000; + case NVKM_SECBOOT_FALCON_SEC2: + queue = sb->subdev.device->sec2->queue; break; default: - nvkm_error(subdev, "cannot start unhandled falcon!\n"); return -EINVAL; } - nvkm_wr32(subdev->device, base + 0x130, 0x00000002); - acr->falcon_state[falcon] = RUNNING; + /* Otherwise just ask the LS firmware to reset the falcon */ + for_each_set_bit(falcon, &falcon_mask, NVKM_SECBOOT_FALCON_END) + nvkm_debug(&sb->subdev, "resetting %s falcon\n", + nvkm_secboot_falcon_name[falcon]); + ret = nvkm_msgqueue_acr_boot_falcons(queue, falcon_mask); + if (ret) { + nvkm_error(&sb->subdev, "error during falcon reset: %d\n", ret); + return ret; + } + nvkm_debug(&sb->subdev, "falcon reset done\n"); return 0; } @@ -864,6 +1034,8 @@ acr_r352_dtor(struct nvkm_acr *_acr) nvkm_gpuobj_del(&acr->unload_blob); + if (_acr->boot_falcon != NVKM_SECBOOT_FALCON_PMU) + kfree(acr->hsbl_unload_blob); kfree(acr->hsbl_blob); nvkm_gpuobj_del(&acr->load_blob); nvkm_gpuobj_del(&acr->ls_blob); @@ -887,8 +1059,88 @@ acr_r352_ls_gpccs_func = { .lhdr_flags = LSF_FLAG_FORCE_PRIV_LOAD, }; + + +/** + * struct acr_r352_pmu_bl_desc - PMU DMEM bootloader descriptor + * @dma_idx: DMA context to be used by BL while loading code/data + * @code_dma_base: 256B-aligned Physical FB Address where code is located + * @total_code_size: total size of the code part in the ucode + * @code_size_to_load: size of the code part to load in PMU IMEM. + * @code_entry_point: entry point in the code. + * @data_dma_base: Physical FB address where data part of ucode is located + * @data_size: Total size of the data portion. + * @overlay_dma_base: Physical Fb address for resident code present in ucode + * @argc: Total number of args + * @argv: offset where args are copied into PMU's DMEM. + * + * Structure used by the PMU bootloader to load the rest of the code + */ +struct acr_r352_pmu_bl_desc { + 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; + u16 code_dma_base1; + u16 data_dma_base1; + u16 overlay_dma_base1; +}; + +/** + * acr_r352_generate_pmu_bl_desc() - populate a DMEM BL descriptor for PMU LS image + * + */ +static void +acr_r352_generate_pmu_bl_desc(const struct nvkm_acr *acr, + const struct ls_ucode_img *img, u64 wpr_addr, + void *_desc) +{ + const struct ls_ucode_img_desc *pdesc = &img->ucode_desc; + const struct nvkm_pmu *pmu = acr->subdev->device->pmu; + struct acr_r352_pmu_bl_desc *desc = _desc; + u64 base; + u64 addr_code; + u64 addr_data; + u32 addr_args; + + base = wpr_addr + img->ucode_off + pdesc->app_start_offset; + addr_code = (base + pdesc->app_resident_code_offset) >> 8; + addr_data = (base + pdesc->app_resident_data_offset) >> 8; + addr_args = pmu->falcon->data.limit; + addr_args -= NVKM_MSGQUEUE_CMDLINE_SIZE; + + desc->dma_idx = FALCON_DMAIDX_UCODE; + desc->code_dma_base = lower_32_bits(addr_code); + desc->code_dma_base1 = upper_32_bits(addr_code); + desc->code_size_total = pdesc->app_size; + desc->code_size_to_load = pdesc->app_resident_code_size; + desc->code_entry_point = pdesc->app_imem_entry; + desc->data_dma_base = lower_32_bits(addr_data); + desc->data_dma_base1 = upper_32_bits(addr_data); + desc->data_size = pdesc->app_resident_data_size; + desc->overlay_dma_base = lower_32_bits(addr_code); + desc->overlay_dma_base1 = upper_32_bits(addr_code); + desc->argc = 1; + desc->argv = addr_args; +} + +static const struct acr_r352_ls_func +acr_r352_ls_pmu_func = { + .load = acr_ls_ucode_load_pmu, + .generate_bl_desc = acr_r352_generate_pmu_bl_desc, + .bl_desc_size = sizeof(struct acr_r352_pmu_bl_desc), + .post_run = acr_ls_pmu_post_run, +}; + const struct acr_r352_func acr_r352_func = { + .fixup_hs_desc = acr_r352_fixup_hs_desc, .generate_hs_bl_desc = acr_r352_generate_hs_bl_desc, .hs_bl_desc_size = sizeof(struct acr_r352_flcn_bl_desc), .ls_ucode_img_load = acr_r352_ls_ucode_img_load, @@ -897,6 +1149,7 @@ acr_r352_func = { .ls_func = { [NVKM_SECBOOT_FALCON_FECS] = &acr_r352_ls_fecs_func, [NVKM_SECBOOT_FALCON_GPCCS] = &acr_r352_ls_gpccs_func, + [NVKM_SECBOOT_FALCON_PMU] = &acr_r352_ls_pmu_func, }, }; @@ -906,7 +1159,6 @@ acr_r352_base_func = { .fini = acr_r352_fini, .load = acr_r352_load, .reset = acr_r352_reset, - .start = acr_r352_start, }; struct nvkm_acr * @@ -915,6 +1167,13 @@ acr_r352_new_(const struct acr_r352_func *func, unsigned long managed_falcons) { struct acr_r352 *acr; + int i; + + /* Check that all requested falcons are supported */ + for_each_set_bit(i, &managed_falcons, NVKM_SECBOOT_FALCON_END) { + if (!func->ls_func[i]) + return ERR_PTR(-ENOTSUPP); + } acr = kzalloc(sizeof(*acr), GFP_KERNEL); if (!acr) |