144f93b20SBen Skeggs /* SPDX-License-Identifier: MIT 244f93b20SBen Skeggs * 344f93b20SBen Skeggs * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 444f93b20SBen Skeggs */ 544f93b20SBen Skeggs #include "priv.h" 644f93b20SBen Skeggs 744f93b20SBen Skeggs #include <nvhw/drf.h> 844f93b20SBen Skeggs #include <nvhw/ref/gh100/dev_fsp_pri.h> 944f93b20SBen Skeggs #include <nvhw/ref/gh100/dev_therm.h> 1044f93b20SBen Skeggs 1144f93b20SBen Skeggs #include <nvrm/nvtypes.h> 1244f93b20SBen Skeggs 1344f93b20SBen Skeggs #define MCTP_HEADER_VERSION 3:0 1444f93b20SBen Skeggs #define MCTP_HEADER_RSVD 7:4 1544f93b20SBen Skeggs 1644f93b20SBen Skeggs #define MCTP_HEADER_DEID 15:8 1744f93b20SBen Skeggs #define MCTP_HEADER_SEID 23:16 1844f93b20SBen Skeggs 1944f93b20SBen Skeggs #define MCTP_HEADER_TAG 26:24 2044f93b20SBen Skeggs #define MCTP_HEADER_TO 27:27 2144f93b20SBen Skeggs #define MCTP_HEADER_SEQ 29:28 2244f93b20SBen Skeggs #define MCTP_HEADER_EOM 30:30 2344f93b20SBen Skeggs #define MCTP_HEADER_SOM 31:31 2444f93b20SBen Skeggs 2544f93b20SBen Skeggs #define MCTP_MSG_HEADER_TYPE 6:0 2644f93b20SBen Skeggs #define MCTP_MSG_HEADER_IC 7:7 2744f93b20SBen Skeggs 2844f93b20SBen Skeggs #define MCTP_MSG_HEADER_VENDOR_ID 23:8 2944f93b20SBen Skeggs #define MCTP_MSG_HEADER_NVDM_TYPE 31:24 3044f93b20SBen Skeggs 3144f93b20SBen Skeggs #define MCTP_MSG_HEADER_TYPE_VENDOR_PCI 0x7e 3244f93b20SBen Skeggs #define MCTP_MSG_HEADER_VENDOR_ID_NV 0x10de 3344f93b20SBen Skeggs 3444f93b20SBen Skeggs #define NVDM_TYPE_COT 0x14 3544f93b20SBen Skeggs #define NVDM_TYPE_FSP_RESPONSE 0x15 3644f93b20SBen Skeggs 3744f93b20SBen Skeggs #pragma pack(1) 3844f93b20SBen Skeggs typedef struct nvdm_payload_cot 3944f93b20SBen Skeggs { 4044f93b20SBen Skeggs NvU16 version; 4144f93b20SBen Skeggs NvU16 size; 4244f93b20SBen Skeggs NvU64 gspFmcSysmemOffset; 4344f93b20SBen Skeggs NvU64 frtsSysmemOffset; 4444f93b20SBen Skeggs NvU32 frtsSysmemSize; 4544f93b20SBen Skeggs 4644f93b20SBen Skeggs // Note this is an offset from the end of FB 4744f93b20SBen Skeggs NvU64 frtsVidmemOffset; 4844f93b20SBen Skeggs NvU32 frtsVidmemSize; 4944f93b20SBen Skeggs 5044f93b20SBen Skeggs // Authentication related fields 5144f93b20SBen Skeggs NvU32 hash384[12]; 5244f93b20SBen Skeggs NvU32 publicKey[96]; 5344f93b20SBen Skeggs NvU32 signature[96]; 5444f93b20SBen Skeggs 5544f93b20SBen Skeggs NvU64 gspBootArgsSysmemOffset; 5644f93b20SBen Skeggs } NVDM_PAYLOAD_COT; 5744f93b20SBen Skeggs #pragma pack() 5844f93b20SBen Skeggs 5944f93b20SBen Skeggs #pragma pack(1) 6044f93b20SBen Skeggs typedef struct 6144f93b20SBen Skeggs { 6244f93b20SBen Skeggs NvU32 taskId; 6344f93b20SBen Skeggs NvU32 commandNvdmType; 6444f93b20SBen Skeggs NvU32 errorCode; 6544f93b20SBen Skeggs } NVDM_PAYLOAD_COMMAND_RESPONSE; 6644f93b20SBen Skeggs #pragma pack() 6744f93b20SBen Skeggs 6844f93b20SBen Skeggs static u32 6944f93b20SBen Skeggs gh100_fsp_poll(struct nvkm_fsp *fsp) 7044f93b20SBen Skeggs { 7144f93b20SBen Skeggs struct nvkm_device *device = fsp->subdev.device; 7244f93b20SBen Skeggs u32 head, tail; 7344f93b20SBen Skeggs 7444f93b20SBen Skeggs head = nvkm_rd32(device, NV_PFSP_MSGQ_HEAD(0)); 7544f93b20SBen Skeggs tail = nvkm_rd32(device, NV_PFSP_MSGQ_TAIL(0)); 7644f93b20SBen Skeggs 7744f93b20SBen Skeggs if (head == tail) 7844f93b20SBen Skeggs return 0; 7944f93b20SBen Skeggs 8044f93b20SBen Skeggs return (tail - head) + sizeof(u32); /* TAIL points at last DWORD written. */ 8144f93b20SBen Skeggs } 8244f93b20SBen Skeggs 8344f93b20SBen Skeggs static int 8444f93b20SBen Skeggs gh100_fsp_recv(struct nvkm_fsp *fsp, u8 *packet, u32 max_packet_size) 8544f93b20SBen Skeggs { 8644f93b20SBen Skeggs struct nvkm_device *device = fsp->subdev.device; 8744f93b20SBen Skeggs u32 packet_size; 8844f93b20SBen Skeggs int ret; 8944f93b20SBen Skeggs 9044f93b20SBen Skeggs packet_size = gh100_fsp_poll(fsp); 9144f93b20SBen Skeggs if (!packet_size || WARN_ON(packet_size % 4 || packet_size > max_packet_size)) 9244f93b20SBen Skeggs return -EINVAL; 9344f93b20SBen Skeggs 9444f93b20SBen Skeggs ret = nvkm_falcon_pio_rd(&fsp->falcon, 0, EMEM, 0, packet, 0, packet_size); 9544f93b20SBen Skeggs if (ret) 9644f93b20SBen Skeggs return ret; 9744f93b20SBen Skeggs 9844f93b20SBen Skeggs nvkm_wr32(device, NV_PFSP_MSGQ_TAIL(0), 0); 9944f93b20SBen Skeggs nvkm_wr32(device, NV_PFSP_MSGQ_HEAD(0), 0); 10044f93b20SBen Skeggs 10144f93b20SBen Skeggs return packet_size; 10244f93b20SBen Skeggs } 10344f93b20SBen Skeggs 10444f93b20SBen Skeggs static int 10544f93b20SBen Skeggs gh100_fsp_wait(struct nvkm_fsp *fsp) 10644f93b20SBen Skeggs { 10744f93b20SBen Skeggs int time = 1000; 10844f93b20SBen Skeggs 10944f93b20SBen Skeggs do { 11044f93b20SBen Skeggs if (gh100_fsp_poll(fsp)) 11144f93b20SBen Skeggs return 0; 11244f93b20SBen Skeggs 11344f93b20SBen Skeggs usleep_range(1000, 2000); 11444f93b20SBen Skeggs } while(time--); 11544f93b20SBen Skeggs 11644f93b20SBen Skeggs return -ETIMEDOUT; 11744f93b20SBen Skeggs } 11844f93b20SBen Skeggs 11944f93b20SBen Skeggs static int 12044f93b20SBen Skeggs gh100_fsp_send(struct nvkm_fsp *fsp, const u8 *packet, u32 packet_size) 12144f93b20SBen Skeggs { 12244f93b20SBen Skeggs struct nvkm_device *device = fsp->subdev.device; 12344f93b20SBen Skeggs int time = 1000, ret; 12444f93b20SBen Skeggs 12544f93b20SBen Skeggs if (WARN_ON(packet_size % sizeof(u32))) 12644f93b20SBen Skeggs return -EINVAL; 12744f93b20SBen Skeggs 12844f93b20SBen Skeggs /* Ensure any previously sent message has been consumed. */ 12944f93b20SBen Skeggs do { 13044f93b20SBen Skeggs u32 head = nvkm_rd32(device, NV_PFSP_QUEUE_HEAD(0)); 13144f93b20SBen Skeggs u32 tail = nvkm_rd32(device, NV_PFSP_QUEUE_TAIL(0)); 13244f93b20SBen Skeggs 13344f93b20SBen Skeggs if (tail == head) 13444f93b20SBen Skeggs break; 13544f93b20SBen Skeggs 13644f93b20SBen Skeggs usleep_range(1000, 2000); 13744f93b20SBen Skeggs } while(time--); 13844f93b20SBen Skeggs 13944f93b20SBen Skeggs if (time < 0) 14044f93b20SBen Skeggs return -ETIMEDOUT; 14144f93b20SBen Skeggs 14244f93b20SBen Skeggs /* Write message to EMEM. */ 14344f93b20SBen Skeggs ret = nvkm_falcon_pio_wr(&fsp->falcon, packet, 0, 0, EMEM, 0, packet_size, 0, false); 14444f93b20SBen Skeggs if (ret) 14544f93b20SBen Skeggs return ret; 14644f93b20SBen Skeggs 14744f93b20SBen Skeggs /* Update queue pointers - TAIL points at last DWORD written. */ 14844f93b20SBen Skeggs nvkm_wr32(device, NV_PFSP_QUEUE_TAIL(0), packet_size - sizeof(u32)); 14944f93b20SBen Skeggs nvkm_wr32(device, NV_PFSP_QUEUE_HEAD(0), 0); 15044f93b20SBen Skeggs return 0; 15144f93b20SBen Skeggs } 15244f93b20SBen Skeggs 15344f93b20SBen Skeggs static int 15444f93b20SBen Skeggs gh100_fsp_send_sync(struct nvkm_fsp *fsp, u8 nvdm_type, const u8 *packet, u32 packet_size) 15544f93b20SBen Skeggs { 15644f93b20SBen Skeggs struct nvkm_subdev *subdev = &fsp->subdev; 15744f93b20SBen Skeggs struct { 15844f93b20SBen Skeggs u32 mctp_header; 15944f93b20SBen Skeggs u32 nvdm_header; 16044f93b20SBen Skeggs NVDM_PAYLOAD_COMMAND_RESPONSE response; 16144f93b20SBen Skeggs } reply; 16244f93b20SBen Skeggs int ret; 16344f93b20SBen Skeggs 16444f93b20SBen Skeggs ret = gh100_fsp_send(fsp, packet, packet_size); 16544f93b20SBen Skeggs if (ret) 16644f93b20SBen Skeggs return ret; 16744f93b20SBen Skeggs 16844f93b20SBen Skeggs ret = gh100_fsp_wait(fsp); 16944f93b20SBen Skeggs if (ret) 17044f93b20SBen Skeggs return ret; 17144f93b20SBen Skeggs 17244f93b20SBen Skeggs ret = gh100_fsp_recv(fsp, (u8 *)&reply, sizeof(reply)); 17344f93b20SBen Skeggs if (ret < 0) 17444f93b20SBen Skeggs return ret; 17544f93b20SBen Skeggs 17644f93b20SBen Skeggs if (NVVAL_TEST(reply.mctp_header, MCTP, HEADER, SOM, !=, 1) || 17744f93b20SBen Skeggs NVVAL_TEST(reply.mctp_header, MCTP, HEADER, EOM, !=, 1)) { 17844f93b20SBen Skeggs nvkm_error(subdev, "unexpected MCTP header in reply: 0x%08x\n", reply.mctp_header); 17944f93b20SBen Skeggs return -EIO; 18044f93b20SBen Skeggs } 18144f93b20SBen Skeggs 18244f93b20SBen Skeggs if (NVDEF_TEST(reply.nvdm_header, MCTP, MSG_HEADER, TYPE, !=, VENDOR_PCI) || 18344f93b20SBen Skeggs NVDEF_TEST(reply.nvdm_header, MCTP, MSG_HEADER, VENDOR_ID, !=, NV) || 18444f93b20SBen Skeggs NVVAL_TEST(reply.nvdm_header, MCTP, MSG_HEADER, NVDM_TYPE, !=, NVDM_TYPE_FSP_RESPONSE)) { 18544f93b20SBen Skeggs nvkm_error(subdev, "unexpected NVDM header in reply: 0x%08x\n", reply.nvdm_header); 18644f93b20SBen Skeggs return -EIO; 18744f93b20SBen Skeggs } 18844f93b20SBen Skeggs 18944f93b20SBen Skeggs if (reply.response.commandNvdmType != nvdm_type) { 19044f93b20SBen Skeggs nvkm_error(subdev, "expected NVDM type 0x%02x in reply, got 0x%02x\n", 19144f93b20SBen Skeggs nvdm_type, reply.response.commandNvdmType); 19244f93b20SBen Skeggs return -EIO; 19344f93b20SBen Skeggs } 19444f93b20SBen Skeggs 19544f93b20SBen Skeggs if (reply.response.errorCode) { 19644f93b20SBen Skeggs nvkm_error(subdev, "NVDM command 0x%02x failed with error 0x%08x\n", 19744f93b20SBen Skeggs nvdm_type, reply.response.errorCode); 19844f93b20SBen Skeggs return -EIO; 19944f93b20SBen Skeggs } 20044f93b20SBen Skeggs 20144f93b20SBen Skeggs return 0; 20244f93b20SBen Skeggs } 20344f93b20SBen Skeggs 20444f93b20SBen Skeggs int 20544f93b20SBen Skeggs gh100_fsp_boot_gsp_fmc(struct nvkm_fsp *fsp, u64 args_addr, u32 rsvd_size, bool resume, 20644f93b20SBen Skeggs u64 img_addr, const u8 *hash, const u8 *pkey, const u8 *sig) 20744f93b20SBen Skeggs { 20844f93b20SBen Skeggs struct { 20944f93b20SBen Skeggs u32 mctp_header; 21044f93b20SBen Skeggs u32 nvdm_header; 21144f93b20SBen Skeggs NVDM_PAYLOAD_COT cot; 21244f93b20SBen Skeggs } msg = {}; 21344f93b20SBen Skeggs 21444f93b20SBen Skeggs msg.mctp_header = NVVAL(MCTP, HEADER, SOM, 1) | 21544f93b20SBen Skeggs NVVAL(MCTP, HEADER, EOM, 1) | 21644f93b20SBen Skeggs NVVAL(MCTP, HEADER, SEID, 0) | 21744f93b20SBen Skeggs NVVAL(MCTP, HEADER, SEQ, 0); 21844f93b20SBen Skeggs 21944f93b20SBen Skeggs msg.nvdm_header = NVDEF(MCTP, MSG_HEADER, TYPE, VENDOR_PCI) | 22044f93b20SBen Skeggs NVDEF(MCTP, MSG_HEADER, VENDOR_ID, NV) | 22144f93b20SBen Skeggs NVVAL(MCTP, MSG_HEADER, NVDM_TYPE, NVDM_TYPE_COT); 22244f93b20SBen Skeggs 22344f93b20SBen Skeggs msg.cot.version = fsp->func->cot.version; 22444f93b20SBen Skeggs msg.cot.size = sizeof(msg.cot); 22544f93b20SBen Skeggs msg.cot.gspFmcSysmemOffset = img_addr; 22644f93b20SBen Skeggs if (!resume) { 22744f93b20SBen Skeggs msg.cot.frtsVidmemOffset = ALIGN(rsvd_size, 0x200000); 22844f93b20SBen Skeggs msg.cot.frtsVidmemSize = 0x100000; 22944f93b20SBen Skeggs } 23044f93b20SBen Skeggs 23144f93b20SBen Skeggs memcpy(msg.cot.hash384, hash, fsp->func->cot.size_hash); 23244f93b20SBen Skeggs memcpy(msg.cot.publicKey, pkey, fsp->func->cot.size_pkey); 23344f93b20SBen Skeggs memcpy(msg.cot.signature, sig, fsp->func->cot.size_sig); 23444f93b20SBen Skeggs 23544f93b20SBen Skeggs msg.cot.gspBootArgsSysmemOffset = args_addr; 23644f93b20SBen Skeggs 23744f93b20SBen Skeggs return gh100_fsp_send_sync(fsp, NVDM_TYPE_COT, (const u8 *)&msg, sizeof(msg)); 23844f93b20SBen Skeggs } 23944f93b20SBen Skeggs 240*32cb1cc3SBen Skeggs int 24144f93b20SBen Skeggs gh100_fsp_wait_secure_boot(struct nvkm_fsp *fsp) 24244f93b20SBen Skeggs { 24344f93b20SBen Skeggs struct nvkm_device *device = fsp->subdev.device; 24444f93b20SBen Skeggs unsigned timeout_ms = 4000; 24544f93b20SBen Skeggs 24644f93b20SBen Skeggs do { 24744f93b20SBen Skeggs u32 status = NVKM_RD32(device, NV_THERM, I2CS_SCRATCH, FSP_BOOT_COMPLETE_STATUS); 24844f93b20SBen Skeggs 24944f93b20SBen Skeggs if (status == NV_THERM_I2CS_SCRATCH_FSP_BOOT_COMPLETE_STATUS_SUCCESS) 25044f93b20SBen Skeggs return 0; 25144f93b20SBen Skeggs 25244f93b20SBen Skeggs usleep_range(1000, 2000); 25344f93b20SBen Skeggs } while (timeout_ms--); 25444f93b20SBen Skeggs 25544f93b20SBen Skeggs return -ETIMEDOUT; 25644f93b20SBen Skeggs } 25744f93b20SBen Skeggs 25844f93b20SBen Skeggs static const struct nvkm_fsp_func 25944f93b20SBen Skeggs gh100_fsp = { 26044f93b20SBen Skeggs .wait_secure_boot = gh100_fsp_wait_secure_boot, 26144f93b20SBen Skeggs .cot = { 26244f93b20SBen Skeggs .version = 1, 26344f93b20SBen Skeggs .size_hash = 48, 26444f93b20SBen Skeggs .size_pkey = 384, 26544f93b20SBen Skeggs .size_sig = 384, 26644f93b20SBen Skeggs .boot_gsp_fmc = gh100_fsp_boot_gsp_fmc, 26744f93b20SBen Skeggs }, 26844f93b20SBen Skeggs }; 26944f93b20SBen Skeggs 27044f93b20SBen Skeggs int 27144f93b20SBen Skeggs gh100_fsp_new(struct nvkm_device *device, 27244f93b20SBen Skeggs enum nvkm_subdev_type type, int inst, struct nvkm_fsp **pfsp) 27344f93b20SBen Skeggs { 27444f93b20SBen Skeggs return nvkm_fsp_new_(&gh100_fsp, device, type, inst, pfsp); 27544f93b20SBen Skeggs } 276