2024-11-25 10:21:18 +10:00
|
|
|
/* 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));
|
|
|
|
}
|
|
|
|
|
2024-11-25 10:27:02 +10:00
|
|
|
int
|
2024-11-25 10:21:18 +10:00
|
|
|
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);
|
|
|
|
}
|