linux/drivers/gpu/drm/nouveau/nvkm/subdev/fsp/gh100.c

276 lines
6.6 KiB
C
Raw Permalink Normal View History

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