diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/subdev/fsp/gh100.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/fsp/gh100.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fsp/gh100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fsp/gh100.c new file mode 100644 index 000000000000..2815be4bf5de --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fsp/gh100.c @@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. + */ +#include "priv.h" + +#include <nvhw/drf.h> +#include <nvhw/ref/gh100/dev_fsp_pri.h> +#include <nvhw/ref/gh100/dev_therm.h> + +#include <nvrm/nvtypes.h> + +#define MCTP_HEADER_VERSION 3:0 +#define MCTP_HEADER_RSVD 7:4 + +#define MCTP_HEADER_DEID 15:8 +#define MCTP_HEADER_SEID 23:16 + +#define MCTP_HEADER_TAG 26:24 +#define MCTP_HEADER_TO 27:27 +#define MCTP_HEADER_SEQ 29:28 +#define MCTP_HEADER_EOM 30:30 +#define MCTP_HEADER_SOM 31:31 + +#define MCTP_MSG_HEADER_TYPE 6:0 +#define MCTP_MSG_HEADER_IC 7:7 + +#define MCTP_MSG_HEADER_VENDOR_ID 23:8 +#define MCTP_MSG_HEADER_NVDM_TYPE 31:24 + +#define MCTP_MSG_HEADER_TYPE_VENDOR_PCI 0x7e +#define MCTP_MSG_HEADER_VENDOR_ID_NV 0x10de + +#define NVDM_TYPE_COT 0x14 +#define NVDM_TYPE_FSP_RESPONSE 0x15 + +#pragma pack(1) +typedef struct nvdm_payload_cot +{ + NvU16 version; + NvU16 size; + NvU64 gspFmcSysmemOffset; + NvU64 frtsSysmemOffset; + NvU32 frtsSysmemSize; + + // Note this is an offset from the end of FB + NvU64 frtsVidmemOffset; + NvU32 frtsVidmemSize; + + // Authentication related fields + NvU32 hash384[12]; + NvU32 publicKey[96]; + NvU32 signature[96]; + + NvU64 gspBootArgsSysmemOffset; +} NVDM_PAYLOAD_COT; +#pragma pack() + +#pragma pack(1) +typedef struct +{ + NvU32 taskId; + NvU32 commandNvdmType; + NvU32 errorCode; +} NVDM_PAYLOAD_COMMAND_RESPONSE; +#pragma pack() + +static u32 +gh100_fsp_poll(struct nvkm_fsp *fsp) +{ + struct nvkm_device *device = fsp->subdev.device; + u32 head, tail; + + head = nvkm_rd32(device, NV_PFSP_MSGQ_HEAD(0)); + tail = nvkm_rd32(device, NV_PFSP_MSGQ_TAIL(0)); + + if (head == tail) + return 0; + + return (tail - head) + sizeof(u32); /* TAIL points at last DWORD written. */ +} + +static int +gh100_fsp_recv(struct nvkm_fsp *fsp, u8 *packet, u32 max_packet_size) +{ + struct nvkm_device *device = fsp->subdev.device; + u32 packet_size; + int ret; + + packet_size = gh100_fsp_poll(fsp); + if (!packet_size || WARN_ON(packet_size % 4 || packet_size > max_packet_size)) + return -EINVAL; + + ret = nvkm_falcon_pio_rd(&fsp->falcon, 0, EMEM, 0, packet, 0, packet_size); + if (ret) + return ret; + + nvkm_wr32(device, NV_PFSP_MSGQ_TAIL(0), 0); + nvkm_wr32(device, NV_PFSP_MSGQ_HEAD(0), 0); + + return packet_size; +} + +static int +gh100_fsp_wait(struct nvkm_fsp *fsp) +{ + int time = 1000; + + do { + if (gh100_fsp_poll(fsp)) + return 0; + + usleep_range(1000, 2000); + } while(time--); + + return -ETIMEDOUT; +} + +static int +gh100_fsp_send(struct nvkm_fsp *fsp, const u8 *packet, u32 packet_size) +{ + struct nvkm_device *device = fsp->subdev.device; + int time = 1000, ret; + + if (WARN_ON(packet_size % sizeof(u32))) + return -EINVAL; + + /* Ensure any previously sent message has been consumed. */ + do { + u32 head = nvkm_rd32(device, NV_PFSP_QUEUE_HEAD(0)); + u32 tail = nvkm_rd32(device, NV_PFSP_QUEUE_TAIL(0)); + + if (tail == head) + break; + + usleep_range(1000, 2000); + } while(time--); + + if (time < 0) + return -ETIMEDOUT; + + /* Write message to EMEM. */ + ret = nvkm_falcon_pio_wr(&fsp->falcon, packet, 0, 0, EMEM, 0, packet_size, 0, false); + if (ret) + return ret; + + /* Update queue pointers - TAIL points at last DWORD written. */ + nvkm_wr32(device, NV_PFSP_QUEUE_TAIL(0), packet_size - sizeof(u32)); + nvkm_wr32(device, NV_PFSP_QUEUE_HEAD(0), 0); + return 0; +} + +static int +gh100_fsp_send_sync(struct nvkm_fsp *fsp, u8 nvdm_type, const u8 *packet, u32 packet_size) +{ + struct nvkm_subdev *subdev = &fsp->subdev; + struct { + u32 mctp_header; + u32 nvdm_header; + NVDM_PAYLOAD_COMMAND_RESPONSE response; + } reply; + int ret; + + ret = gh100_fsp_send(fsp, packet, packet_size); + if (ret) + return ret; + + ret = gh100_fsp_wait(fsp); + if (ret) + return ret; + + ret = gh100_fsp_recv(fsp, (u8 *)&reply, sizeof(reply)); + if (ret < 0) + return ret; + + if (NVVAL_TEST(reply.mctp_header, MCTP, HEADER, SOM, !=, 1) || + NVVAL_TEST(reply.mctp_header, MCTP, HEADER, EOM, !=, 1)) { + nvkm_error(subdev, "unexpected MCTP header in reply: 0x%08x\n", reply.mctp_header); + return -EIO; + } + + if (NVDEF_TEST(reply.nvdm_header, MCTP, MSG_HEADER, TYPE, !=, VENDOR_PCI) || + NVDEF_TEST(reply.nvdm_header, MCTP, MSG_HEADER, VENDOR_ID, !=, NV) || + NVVAL_TEST(reply.nvdm_header, MCTP, MSG_HEADER, NVDM_TYPE, !=, NVDM_TYPE_FSP_RESPONSE)) { + nvkm_error(subdev, "unexpected NVDM header in reply: 0x%08x\n", reply.nvdm_header); + return -EIO; + } + + if (reply.response.commandNvdmType != nvdm_type) { + nvkm_error(subdev, "expected NVDM type 0x%02x in reply, got 0x%02x\n", + nvdm_type, reply.response.commandNvdmType); + return -EIO; + } + + if (reply.response.errorCode) { + nvkm_error(subdev, "NVDM command 0x%02x failed with error 0x%08x\n", + nvdm_type, reply.response.errorCode); + return -EIO; + } + + return 0; +} + +int +gh100_fsp_boot_gsp_fmc(struct nvkm_fsp *fsp, u64 args_addr, u32 rsvd_size, bool resume, + u64 img_addr, const u8 *hash, const u8 *pkey, const u8 *sig) +{ + struct { + u32 mctp_header; + u32 nvdm_header; + NVDM_PAYLOAD_COT cot; + } msg = {}; + + msg.mctp_header = NVVAL(MCTP, HEADER, SOM, 1) | + NVVAL(MCTP, HEADER, EOM, 1) | + NVVAL(MCTP, HEADER, SEID, 0) | + NVVAL(MCTP, HEADER, SEQ, 0); + + msg.nvdm_header = NVDEF(MCTP, MSG_HEADER, TYPE, VENDOR_PCI) | + NVDEF(MCTP, MSG_HEADER, VENDOR_ID, NV) | + NVVAL(MCTP, MSG_HEADER, NVDM_TYPE, NVDM_TYPE_COT); + + msg.cot.version = fsp->func->cot.version; + msg.cot.size = sizeof(msg.cot); + msg.cot.gspFmcSysmemOffset = img_addr; + if (!resume) { + msg.cot.frtsVidmemOffset = ALIGN(rsvd_size, 0x200000); + msg.cot.frtsVidmemSize = 0x100000; + } + + memcpy(msg.cot.hash384, hash, fsp->func->cot.size_hash); + memcpy(msg.cot.publicKey, pkey, fsp->func->cot.size_pkey); + memcpy(msg.cot.signature, sig, fsp->func->cot.size_sig); + + msg.cot.gspBootArgsSysmemOffset = args_addr; + + return gh100_fsp_send_sync(fsp, NVDM_TYPE_COT, (const u8 *)&msg, sizeof(msg)); +} + +int +gh100_fsp_wait_secure_boot(struct nvkm_fsp *fsp) +{ + struct nvkm_device *device = fsp->subdev.device; + unsigned timeout_ms = 4000; + + do { + u32 status = NVKM_RD32(device, NV_THERM, I2CS_SCRATCH, FSP_BOOT_COMPLETE_STATUS); + + if (status == NV_THERM_I2CS_SCRATCH_FSP_BOOT_COMPLETE_STATUS_SUCCESS) + return 0; + + usleep_range(1000, 2000); + } while (timeout_ms--); + + return -ETIMEDOUT; +} + +static const struct nvkm_fsp_func +gh100_fsp = { + .wait_secure_boot = gh100_fsp_wait_secure_boot, + .cot = { + .version = 1, + .size_hash = 48, + .size_pkey = 384, + .size_sig = 384, + .boot_gsp_fmc = gh100_fsp_boot_gsp_fmc, + }, +}; + +int +gh100_fsp_new(struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_fsp **pfsp) +{ + return nvkm_fsp_new_(&gh100_fsp, device, type, inst, pfsp); +} |