From e781ff9118fbbb7f91f55e3076bd0885d7609b59 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Wed, 14 Dec 2016 17:02:37 +0900 Subject: [PATCH] drm/nouveau/secboot: add low-secure firmware hooks Secure firmwares provided by NVIDIA will follow the same overall principle, but may slightly differ in format, or not use the same bootloader descriptor even on the same chip. In order to handle this as gracefully as possible, turn the LS firmware functions into hooks that can be overloaded as needed. The current hooks cover the external firmware loading as well as the bootloader descriptor generation. Signed-off-by: Alexandre Courbot Signed-off-by: Ben Skeggs --- .../drm/nouveau/nvkm/subdev/secboot/gm200.c | 239 +++--------------- .../drm/nouveau/nvkm/subdev/secboot/gm20b.c | 30 +++ .../drm/nouveau/nvkm/subdev/secboot/priv.h | 193 ++++++++++++++ 3 files changed, 265 insertions(+), 197 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm200.c index 27479891a973..94cdc979a770 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm200.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm200.c @@ -131,175 +131,6 @@ struct fw_bl_desc { }; -/* - * - * LS blob structures - * - */ - -/** - * struct lsf_ucode_desc - LS falcon signatures - * @prd_keys: signature to use when the GPU is in production mode - * @dgb_keys: signature to use when the GPU is 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 - * - * Directly loaded from a signature file. - */ -struct lsf_ucode_desc { - u8 prd_keys[2][16]; - u8 dbg_keys[2][16]; - u32 b_prd_present; - u32 b_dbg_present; - u32 falcon_id; -}; - -/** - * struct 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 lsf_lsb_header { - struct lsf_ucode_desc 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; -#define LSF_FLAG_LOAD_CODE_AT_0 1 -#define LSF_FLAG_DMACTL_REQ_CTX 4 -#define LSF_FLAG_FORCE_PRIV_LOAD 8 -}; - -/** - * struct 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 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_desc - descriptor of firmware image - * @descriptor_size: size of this descriptor - * @image_size: size of the whole image - * @bootloader_start_offset: start offset of the bootloader in ucode image - * @bootloader_size: size of the bootloader - * @bootloader_imem_offset: start off set of the bootloader in IMEM - * @bootloader_entry_point: entry point of the bootloader in IMEM - * @app_start_offset: start offset of the LS firmware - * @app_size: size of the LS firmware's code and data - * @app_imem_offset: offset of the app in IMEM - * @app_imem_entry: entry point of the app in IMEM - * @app_dmem_offset: offset of the data in DMEM - * @app_resident_code_offset: offset of app code from app_start_offset - * @app_resident_code_size: size of the code - * @app_resident_data_offset: offset of data from app_start_offset - * @app_resident_data_size: size of data - * - * A firmware image contains the code, data, and bootloader of a given LS - * falcon in a single blob. This structure describes where everything is. - * - * This can be generated from a (bootloader, code, data) set if they have - * been loaded separately, or come directly from a file. - */ -struct ls_ucode_img_desc { - u32 descriptor_size; - u32 image_size; - u32 tools_version; - u32 app_version; - char date[64]; - u32 bootloader_start_offset; - u32 bootloader_size; - u32 bootloader_imem_offset; - u32 bootloader_entry_point; - u32 app_start_offset; - u32 app_size; - u32 app_imem_offset; - u32 app_imem_entry; - u32 app_dmem_offset; - u32 app_resident_code_offset; - u32 app_resident_code_size; - u32 app_resident_data_offset; - u32 app_resident_data_size; - u32 nb_overlays; - struct {u32 start; u32 size; } load_ovl[64]; - u32 compressed; -}; - -/** - * struct ls_ucode_img - temporary storage for loaded LS firmwares - * @node: to link within lsf_ucode_mgr - * @falcon_id: ID of the falcon this LS firmware is for - * @ucode_desc: loaded or generated map of ucode_data - * @ucode_header: header of the firmware - * @ucode_data: firmware payload (code and data) - * @ucode_size: size in bytes of data in ucode_data - * @wpr_header: WPR header to be written to the LS blob - * @lsb_header: LSB header to be written to the LS blob - * - * Preparing the WPR LS blob requires information about all the LS firmwares - * (size, etc) to be known. This structure contains all the data of one LS - * firmware. - */ -struct ls_ucode_img { - struct list_head node; - enum nvkm_secboot_falcon falcon_id; - - struct ls_ucode_img_desc ucode_desc; - u32 *ucode_header; - u8 *ucode_data; - u32 ucode_size; - - struct lsf_wpr_header wpr_header; - struct lsf_lsb_header lsb_header; -}; - /** * struct ls_ucode_mgr - manager for all LS falcon firmwares * @count: number of managed LS falcons @@ -364,7 +195,7 @@ struct hsf_load_header { * it has the required minimum size. */ static void * -gm200_secboot_load_firmware(struct nvkm_subdev *subdev, const char *name, +gm200_secboot_load_firmware(const struct nvkm_subdev *subdev, const char *name, size_t min_size) { const struct firmware *fw; @@ -457,7 +288,7 @@ ls_ucode_img_build(const struct firmware *bl, const struct firmware *code, * blob. Also generate the corresponding ucode descriptor. */ static int -ls_ucode_img_load_generic(struct nvkm_subdev *subdev, +ls_ucode_img_load_generic(const struct nvkm_subdev *subdev, struct ls_ucode_img *img, const char *falcon_name, const u32 falcon_id) { @@ -518,17 +349,17 @@ error: return ret; } -typedef int (*lsf_load_func)(struct nvkm_subdev *, struct ls_ucode_img *); +typedef int (*lsf_load_func)(const struct nvkm_subdev *, struct ls_ucode_img *); -static int -ls_ucode_img_load_fecs(struct nvkm_subdev *subdev, struct ls_ucode_img *img) +int +gm200_ls_load_fecs(const struct nvkm_subdev *subdev, struct ls_ucode_img *img) { return ls_ucode_img_load_generic(subdev, img, "fecs", NVKM_SECBOOT_FALCON_FECS); } -static int -ls_ucode_img_load_gpccs(struct nvkm_subdev *subdev, struct ls_ucode_img *img) +int +gm200_ls_load_gpccs(const struct nvkm_subdev *subdev, struct ls_ucode_img *img) { return ls_ucode_img_load_generic(subdev, img, "gpccs", NVKM_SECBOOT_FALCON_GPCCS); @@ -556,14 +387,8 @@ ls_ucode_img_load(struct nvkm_subdev *subdev, lsf_load_func load_func) return img; } -static const lsf_load_func lsf_load_funcs[] = { - [NVKM_SECBOOT_FALCON_END] = NULL, /* reserve enough space */ - [NVKM_SECBOOT_FALCON_FECS] = ls_ucode_img_load_fecs, - [NVKM_SECBOOT_FALCON_GPCCS] = ls_ucode_img_load_gpccs, -}; - /** - * ls_ucode_img_populate_bl_desc() - populate a DMEM BL descriptor for LS image + * gm200_secboot_ls_bl_desc() - populate a DMEM BL descriptor for LS image * @img: ucode image to generate against * @desc: descriptor to populate * @sb: secure boot state to use for base addresses @@ -573,10 +398,11 @@ static const lsf_load_func lsf_load_funcs[] = { * */ static void -ls_ucode_img_populate_bl_desc(struct ls_ucode_img *img, u64 wpr_addr, - struct gm200_flcn_bl_desc *desc) +gm200_secboot_ls_bl_desc(const struct ls_ucode_img *img, u64 wpr_addr, + void *_desc) { - struct ls_ucode_img_desc *pdesc = &img->ucode_desc; + struct gm200_flcn_bl_desc *desc = _desc; + const struct ls_ucode_img_desc *pdesc = &img->ucode_desc; u64 addr_base; addr_base = wpr_addr + img->lsb_header.ucode_off + @@ -621,6 +447,8 @@ ls_ucode_img_fill_headers(struct gm200_secboot *gsb, struct ls_ucode_img *img, struct lsf_wpr_header *whdr = &img->wpr_header; struct lsf_lsb_header *lhdr = &img->lsb_header; struct ls_ucode_img_desc *desc = &img->ucode_desc; + const struct secboot_ls_single_func *func = + (*gsb->ls_func)[img->falcon_id]; if (img->ucode_header) { nvkm_fatal(&gsb->base.subdev, @@ -681,9 +509,9 @@ ls_ucode_img_fill_headers(struct gm200_secboot *gsb, struct ls_ucode_img *img, if (img->falcon_id == NVKM_SECBOOT_FALCON_GPCCS) lhdr->flags |= LSF_FLAG_FORCE_PRIV_LOAD; - /* Align (size bloat) and save off BL descriptor size */ - lhdr->bl_data_size = ALIGN(sizeof(struct gm200_flcn_bl_desc), - LSF_BL_DATA_SIZE_ALIGN); + /* Align and save off BL descriptor size */ + lhdr->bl_data_size = ALIGN(func->bl_desc_size, LSF_BL_DATA_SIZE_ALIGN); + /* * Align, save off, and include the additional BL data */ @@ -769,15 +597,16 @@ ls_ucode_mgr_write_wpr(struct gm200_secboot *gsb, struct ls_ucode_mgr *mgr, /* Generate and write BL descriptor */ if (!img->ucode_header) { - u8 desc[gsb->func->bl_desc_size]; - struct gm200_flcn_bl_desc gdesc; + const struct secboot_ls_single_func *ls_func = + (*gsb->ls_func)[img->falcon_id]; + u8 gdesc[ls_func->bl_desc_size]; + + ls_func->generate_bl_desc(img, gsb->acr_wpr_addr, + &gdesc); - ls_ucode_img_populate_bl_desc(img, gsb->acr_wpr_addr, - &gdesc); - gsb->func->fixup_bl_desc(&gdesc, &desc); nvkm_gpuobj_memcpy_to(wpr_blob, img->lsb_header.bl_data_off, - &desc, gsb->func->bl_desc_size); + &gdesc, ls_func->bl_desc_size); } /* Copy ucode */ @@ -816,11 +645,12 @@ gm200_secboot_prepare_ls_blob(struct gm200_secboot *gsb) ls_ucode_mgr_init(&mgr); /* Load all LS blobs */ - for_each_set_bit(falcon_id, &gsb->base.func->managed_falcons, + for_each_set_bit(falcon_id, &sb->func->managed_falcons, NVKM_SECBOOT_FALCON_END) { struct ls_ucode_img *img; - img = ls_ucode_img_load(&sb->subdev, lsf_load_funcs[falcon_id]); + img = ls_ucode_img_load(&sb->subdev, + (*gsb->ls_func)[falcon_id]->load); if (IS_ERR(img)) { ret = PTR_ERR(img); @@ -865,6 +695,20 @@ cleanup: return ret; } +static const secboot_ls_func +gm200_ls_func = { + [NVKM_SECBOOT_FALCON_FECS] = &(struct secboot_ls_single_func) { + .load = gm200_ls_load_fecs, + .generate_bl_desc = gm200_secboot_ls_bl_desc, + .bl_desc_size = sizeof(struct gm200_flcn_bl_desc), + }, + [NVKM_SECBOOT_FALCON_GPCCS] = &(struct secboot_ls_single_func) { + .load = gm200_ls_load_gpccs, + .generate_bl_desc = gm200_secboot_ls_bl_desc, + .bl_desc_size = sizeof(struct gm200_flcn_bl_desc), + }, +}; + /* * High-secure blob creation */ @@ -1443,6 +1287,7 @@ gm200_secboot_new(struct nvkm_device *device, int index, return ret; gsb->func = &gm200_secboot_func; + gsb->ls_func = &gm200_ls_func; return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm20b.c index 5f38187be857..16b9aa31b7f0 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm20b.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm20b.c @@ -23,6 +23,7 @@ #include "priv.h" #include +#include /* * The BL header format used by GM20B's firmware is slightly different @@ -42,6 +43,25 @@ struct gm20b_flcn_bl_desc { u32 data_size; }; +static void +gm20b_secboot_ls_bl_desc(const struct ls_ucode_img *img, u64 wpr_addr, + void *_desc) +{ + struct gm20b_flcn_bl_desc *desc = _desc; + const struct ls_ucode_img_desc *pdesc = &img->ucode_desc; + u64 base; + + base = wpr_addr + img->lsb_header.ucode_off + pdesc->app_start_offset; + + memset(desc, 0, sizeof(*desc)); + desc->ctx_dma = FALCON_DMAIDX_UCODE; + desc->code_dma_base = (base + pdesc->app_resident_code_offset) >> 8; + desc->non_sec_code_size = pdesc->app_resident_code_size; + desc->data_dma_base = (base + pdesc->app_resident_data_offset) >> 8; + desc->data_size = pdesc->app_resident_data_size; + desc->code_entry_point = pdesc->app_imem_entry; +} + static int gm20b_secboot_prepare_blobs(struct gm200_secboot *gsb) { @@ -184,6 +204,15 @@ gm20b_secboot = { .boot_falcon = NVKM_SECBOOT_FALCON_PMU, }; +static const secboot_ls_func +gm20b_ls_func = { + [NVKM_SECBOOT_FALCON_FECS] = &(struct secboot_ls_single_func) { + .load = gm200_ls_load_fecs, + .generate_bl_desc = gm20b_secboot_ls_bl_desc, + .bl_desc_size = sizeof(struct gm20b_flcn_bl_desc), + }, +}; + int gm20b_secboot_new(struct nvkm_device *device, int index, struct nvkm_secboot **psb) @@ -203,6 +232,7 @@ gm20b_secboot_new(struct nvkm_device *device, int index, return ret; gsb->func = &gm20b_secboot_func; + gsb->ls_func = &gm20b_ls_func; return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/priv.h index e1caa6faeed2..a0ecd3536208 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/priv.h @@ -41,6 +41,175 @@ struct nvkm_secboot_func { int nvkm_secboot_ctor(const struct nvkm_secboot_func *, struct nvkm_device *, int index, struct nvkm_secboot *); +/* + * + * LS blob structures + * + */ + +/** + * struct lsf_ucode_desc - LS falcon signatures + * @prd_keys: signature to use when the GPU is in production mode + * @dgb_keys: signature to use when the GPU is 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 + * + * Directly loaded from a signature file. + */ +struct lsf_ucode_desc { + u8 prd_keys[2][16]; + u8 dbg_keys[2][16]; + u32 b_prd_present; + u32 b_dbg_present; + u32 falcon_id; +}; + +/** + * struct 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 lsf_lsb_header { + struct lsf_ucode_desc 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; +#define LSF_FLAG_LOAD_CODE_AT_0 1 +#define LSF_FLAG_DMACTL_REQ_CTX 4 +#define LSF_FLAG_FORCE_PRIV_LOAD 8 +}; + +/** + * struct 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 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_desc - descriptor of firmware image + * @descriptor_size: size of this descriptor + * @image_size: size of the whole image + * @bootloader_start_offset: start offset of the bootloader in ucode image + * @bootloader_size: size of the bootloader + * @bootloader_imem_offset: start off set of the bootloader in IMEM + * @bootloader_entry_point: entry point of the bootloader in IMEM + * @app_start_offset: start offset of the LS firmware + * @app_size: size of the LS firmware's code and data + * @app_imem_offset: offset of the app in IMEM + * @app_imem_entry: entry point of the app in IMEM + * @app_dmem_offset: offset of the data in DMEM + * @app_resident_code_offset: offset of app code from app_start_offset + * @app_resident_code_size: size of the code + * @app_resident_data_offset: offset of data from app_start_offset + * @app_resident_data_size: size of data + * + * A firmware image contains the code, data, and bootloader of a given LS + * falcon in a single blob. This structure describes where everything is. + * + * This can be generated from a (bootloader, code, data) set if they have + * been loaded separately, or come directly from a file. + */ +struct ls_ucode_img_desc { + u32 descriptor_size; + u32 image_size; + u32 tools_version; + u32 app_version; + char date[64]; + u32 bootloader_start_offset; + u32 bootloader_size; + u32 bootloader_imem_offset; + u32 bootloader_entry_point; + u32 app_start_offset; + u32 app_size; + u32 app_imem_offset; + u32 app_imem_entry; + u32 app_dmem_offset; + u32 app_resident_code_offset; + u32 app_resident_code_size; + u32 app_resident_data_offset; + u32 app_resident_data_size; + u32 nb_overlays; + struct {u32 start; u32 size; } load_ovl[64]; + u32 compressed; +}; + +/** + * struct ls_ucode_img - temporary storage for loaded LS firmwares + * @node: to link within lsf_ucode_mgr + * @falcon_id: ID of the falcon this LS firmware is for + * @ucode_desc: loaded or generated map of ucode_data + * @ucode_header: header of the firmware + * @ucode_data: firmware payload (code and data) + * @ucode_size: size in bytes of data in ucode_data + * @wpr_header: WPR header to be written to the LS blob + * @lsb_header: LSB header to be written to the LS blob + * + * Preparing the WPR LS blob requires information about all the LS firmwares + * (size, etc) to be known. This structure contains all the data of one LS + * firmware. + */ +struct ls_ucode_img { + struct list_head node; + enum nvkm_secboot_falcon falcon_id; + + struct ls_ucode_img_desc ucode_desc; + u32 *ucode_header; + u8 *ucode_data; + u32 ucode_size; + + struct lsf_wpr_header wpr_header; + struct lsf_lsb_header lsb_header; +}; + struct flcn_u64 { u32 lo; u32 hi; @@ -87,6 +256,29 @@ struct gm200_flcn_bl_desc { u32 data_size; }; +/** + * struct secboot_ls_single_func - manages a single LS firmware + * + * @load: load the external firmware into a ls_ucode_img + * @generate_bl_desc: function called on a block of bl_desc_size to generate the + * proper bootloader descriptor for this LS firmware + * @bl_desc_size: size of the bootloader descriptor + */ +struct secboot_ls_single_func { + int (*load)(const struct nvkm_subdev *, struct ls_ucode_img *); + void (*generate_bl_desc)(const struct ls_ucode_img *, u64, void *); + u32 bl_desc_size; +}; + +/** + * typedef secboot_ls_func - manages all the LS firmwares for this ACR + */ +typedef const struct secboot_ls_single_func * +secboot_ls_func[NVKM_SECBOOT_FALCON_END]; + +int gm200_ls_load_fecs(const struct nvkm_subdev *, struct ls_ucode_img *); +int gm200_ls_load_gpccs(const struct nvkm_subdev *, struct ls_ucode_img *); + /** * Contains the whole secure boot state, allowing it to be performed as needed * @wpr_addr: physical address of the WPR region @@ -107,6 +299,7 @@ struct gm200_flcn_bl_desc { struct gm200_secboot { struct nvkm_secboot base; const struct gm200_secboot_func *func; + const secboot_ls_func *ls_func; /* * Address and size of the fixed WPR region, if any. On Tegra this -- 2.20.1