summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/nouveau/nvkm/subdev/fsp/gh100.c
blob: 2815be4bf5de888f76d8977f62245fe71d216fea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
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);
}