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);
}
|