xref: /linux/drivers/gpu/drm/nouveau/nvkm/subdev/fsp/gh100.c (revision ab93e0dd72c37d378dd936f031ffb83ff2bd87ce)
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