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 <acourbot@nvidia.com>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
Alexandre Courbot 2016-12-14 17:02:37 +09:00 committed by Ben Skeggs
parent a9b333a50e
commit e781ff9118
3 changed files with 265 additions and 197 deletions

View File

@ -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 @@ ls_ucode_img_load_generic(struct nvkm_subdev *subdev,
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 @@ gm200_secboot_prepare_ls_blob(struct gm200_secboot *gsb)
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;
}

View File

@ -23,6 +23,7 @@
#include "priv.h"
#include <core/gpuobj.h>
#include <engine/falcon.h>
/*
* 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;
}

View File

@ -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