mirror of
https://github.com/torvalds/linux.git
synced 2026-04-21 00:04:01 -04:00
The LOGINIT, LOGINTR, LOGRM, and LOGPMU buffers are circular buffers
that have printf-like logs from GSP-RM and PMU encoded in them.
LOGINIT, LOGINTR, and LOGRM are allocated by Nouveau and their DMA
addresses are passed to GSP-RM during initialization. The buffers are
required for GSP-RM to initialize properly.
LOGPMU is also allocated by Nouveau, but its contents are updated
when Nouveau receives an NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT RPC from
GSP-RM. Nouveau then copies the RPC to the buffer.
The messages are encoded as an array of variable-length structures that
contain the parameters to an NV_PRINTF call. The format string and
parameter count are stored in a special ELF image that contains only
logging strings. This image is not currently shipped with the Nvidia
driver.
There are two methods to extract the logs.
OpenRM tries to load the logging ELF, and if present, parses the log
buffers in real time and outputs the strings to the kernel console.
Alternatively, and this is the method used by this patch, the buffers
can be exposed to user space, and a user-space tool (along with the
logging ELF image) can parse the buffer and dump the logs.
This method has the advantage that it allows the buffers to be parsed
even when the logging ELF file is not available to the user. However,
it has the disadvantage the debugfs entries need to remain until the
driver is unloaded.
The buffers are exposed via debugfs. If GSP-RM fails to initialize, then
Nouveau immediately shuts down the GSP interface. This would normally
also deallocate the logging buffers, thereby preventing the user from
capturing the debug logs.
To avoid this, introduce the keep-gsp-logging command line parameter. If
specified, and if at least one logging buffer has content, then Nouveau
will migrate these buffers into new debugfs entries that are retained
until the driver unloads.
An end-user can capture the logs using the following commands:
cp /sys/kernel/debug/nouveau/<path>/loginit loginit
cp /sys/kernel/debug/nouveau/<path>/logrm logrm
cp /sys/kernel/debug/nouveau/<path>/logintr logintr
cp /sys/kernel/debug/nouveau/<path>/logpmu logpmu
where (for a PCI device) <path> is the PCI ID of the GPU (e.g.
0000:65:00.0).
Since LOGPMU is not needed for normal GSP-RM operation, it is only
created if debugfs is available. Otherwise, the
NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT RPCs are ignored.
A simple way to test the buffer migration feature is to have
nvkm_gsp_init() return an error code.
Tested-by: Ben Skeggs <bskeggs@nvidia.com>
Signed-off-by: Timur Tabi <ttabi@nvidia.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20241030202952.694055-2-ttabi@nvidia.com
484 lines
10 KiB
C
484 lines
10 KiB
C
#ifndef __NVKM_GSP_H__
|
|
#define __NVKM_GSP_H__
|
|
#define nvkm_gsp(p) container_of((p), struct nvkm_gsp, subdev)
|
|
#include <core/subdev.h>
|
|
#include <core/falcon.h>
|
|
#include <core/firmware.h>
|
|
|
|
#include <linux/debugfs.h>
|
|
|
|
#define GSP_PAGE_SHIFT 12
|
|
#define GSP_PAGE_SIZE BIT(GSP_PAGE_SHIFT)
|
|
|
|
struct nvkm_gsp_mem {
|
|
struct device *dev;
|
|
size_t size;
|
|
void *data;
|
|
dma_addr_t addr;
|
|
};
|
|
|
|
struct nvkm_gsp_radix3 {
|
|
struct nvkm_gsp_mem lvl0;
|
|
struct nvkm_gsp_mem lvl1;
|
|
struct sg_table lvl2;
|
|
};
|
|
|
|
int nvkm_gsp_sg(struct nvkm_device *, u64 size, struct sg_table *);
|
|
void nvkm_gsp_sg_free(struct nvkm_device *, struct sg_table *);
|
|
|
|
typedef int (*nvkm_gsp_msg_ntfy_func)(void *priv, u32 fn, void *repv, u32 repc);
|
|
|
|
struct nvkm_gsp_event;
|
|
typedef void (*nvkm_gsp_event_func)(struct nvkm_gsp_event *, void *repv, u32 repc);
|
|
|
|
struct nvkm_gsp {
|
|
const struct nvkm_gsp_func *func;
|
|
struct nvkm_subdev subdev;
|
|
|
|
struct nvkm_falcon falcon;
|
|
|
|
struct {
|
|
struct {
|
|
const struct firmware *load;
|
|
const struct firmware *unload;
|
|
} booter;
|
|
const struct firmware *bl;
|
|
const struct firmware *rm;
|
|
} fws;
|
|
|
|
struct nvkm_firmware fw;
|
|
struct nvkm_gsp_mem sig;
|
|
struct nvkm_gsp_radix3 radix3;
|
|
|
|
struct {
|
|
struct {
|
|
struct {
|
|
u64 addr;
|
|
u64 size;
|
|
} vga_workspace;
|
|
u64 addr;
|
|
u64 size;
|
|
} bios;
|
|
struct {
|
|
struct {
|
|
u64 addr;
|
|
u64 size;
|
|
} frts, boot, elf, heap;
|
|
u64 addr;
|
|
u64 size;
|
|
} wpr2;
|
|
struct {
|
|
u64 addr;
|
|
u64 size;
|
|
} heap;
|
|
u64 addr;
|
|
u64 size;
|
|
|
|
struct {
|
|
u64 addr;
|
|
u64 size;
|
|
} region[16];
|
|
int region_nr;
|
|
u32 rsvd_size;
|
|
} fb;
|
|
|
|
struct {
|
|
struct nvkm_falcon_fw load;
|
|
struct nvkm_falcon_fw unload;
|
|
} booter;
|
|
|
|
struct {
|
|
struct nvkm_gsp_mem fw;
|
|
u32 code_offset;
|
|
u32 data_offset;
|
|
u32 manifest_offset;
|
|
u32 app_version;
|
|
} boot;
|
|
|
|
struct nvkm_gsp_mem libos;
|
|
struct nvkm_gsp_mem loginit;
|
|
struct nvkm_gsp_mem logintr;
|
|
struct nvkm_gsp_mem logrm;
|
|
struct nvkm_gsp_mem rmargs;
|
|
|
|
struct nvkm_gsp_mem wpr_meta;
|
|
|
|
struct {
|
|
struct sg_table sgt;
|
|
struct nvkm_gsp_radix3 radix3;
|
|
struct nvkm_gsp_mem meta;
|
|
} sr;
|
|
|
|
struct {
|
|
struct nvkm_gsp_mem mem;
|
|
|
|
struct {
|
|
int nr;
|
|
u32 size;
|
|
u64 *ptr;
|
|
} ptes;
|
|
|
|
struct {
|
|
u32 size;
|
|
void *ptr;
|
|
} cmdq, msgq;
|
|
} shm;
|
|
|
|
struct nvkm_gsp_cmdq {
|
|
struct mutex mutex;
|
|
u32 cnt;
|
|
u32 seq;
|
|
u32 *wptr;
|
|
u32 *rptr;
|
|
} cmdq;
|
|
|
|
struct nvkm_gsp_msgq {
|
|
struct mutex mutex;
|
|
u32 cnt;
|
|
u32 *wptr;
|
|
u32 *rptr;
|
|
struct nvkm_gsp_msgq_ntfy {
|
|
u32 fn;
|
|
nvkm_gsp_msg_ntfy_func func;
|
|
void *priv;
|
|
} ntfy[16];
|
|
int ntfy_nr;
|
|
struct work_struct work;
|
|
} msgq;
|
|
|
|
bool running;
|
|
|
|
/* Internal GSP-RM control handles. */
|
|
struct {
|
|
struct nvkm_gsp_client {
|
|
struct nvkm_gsp_object {
|
|
struct nvkm_gsp_client *client;
|
|
struct nvkm_gsp_object *parent;
|
|
u32 handle;
|
|
} object;
|
|
|
|
struct nvkm_gsp *gsp;
|
|
|
|
struct list_head events;
|
|
} client;
|
|
|
|
struct nvkm_gsp_device {
|
|
struct nvkm_gsp_object object;
|
|
struct nvkm_gsp_object subdevice;
|
|
} device;
|
|
} internal;
|
|
|
|
struct {
|
|
enum nvkm_subdev_type type;
|
|
int inst;
|
|
u32 stall;
|
|
u32 nonstall;
|
|
} intr[32];
|
|
int intr_nr;
|
|
|
|
struct {
|
|
u64 rm_bar1_pdb;
|
|
u64 rm_bar2_pdb;
|
|
} bar;
|
|
|
|
struct {
|
|
u8 gpcs;
|
|
u8 tpcs;
|
|
} gr;
|
|
|
|
const struct nvkm_gsp_rm {
|
|
void *(*rpc_get)(struct nvkm_gsp *, u32 fn, u32 argc);
|
|
void *(*rpc_push)(struct nvkm_gsp *, void *argv, bool wait, u32 repc);
|
|
void (*rpc_done)(struct nvkm_gsp *gsp, void *repv);
|
|
|
|
void *(*rm_ctrl_get)(struct nvkm_gsp_object *, u32 cmd, u32 argc);
|
|
int (*rm_ctrl_push)(struct nvkm_gsp_object *, void **argv, u32 repc);
|
|
void (*rm_ctrl_done)(struct nvkm_gsp_object *, void *repv);
|
|
|
|
void *(*rm_alloc_get)(struct nvkm_gsp_object *, u32 oclass, u32 argc);
|
|
void *(*rm_alloc_push)(struct nvkm_gsp_object *, void *argv, u32 repc);
|
|
void (*rm_alloc_done)(struct nvkm_gsp_object *, void *repv);
|
|
|
|
int (*rm_free)(struct nvkm_gsp_object *);
|
|
|
|
int (*client_ctor)(struct nvkm_gsp *, struct nvkm_gsp_client *);
|
|
void (*client_dtor)(struct nvkm_gsp_client *);
|
|
|
|
int (*device_ctor)(struct nvkm_gsp_client *, struct nvkm_gsp_device *);
|
|
void (*device_dtor)(struct nvkm_gsp_device *);
|
|
|
|
int (*event_ctor)(struct nvkm_gsp_device *, u32 handle, u32 id,
|
|
nvkm_gsp_event_func, struct nvkm_gsp_event *);
|
|
void (*event_dtor)(struct nvkm_gsp_event *);
|
|
} *rm;
|
|
|
|
struct {
|
|
struct mutex mutex;
|
|
struct idr idr;
|
|
} client_id;
|
|
|
|
/* A linked list of registry items. The registry RPC will be built from it. */
|
|
struct list_head registry_list;
|
|
|
|
/* The size of the registry RPC */
|
|
size_t registry_rpc_size;
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
/*
|
|
* Logging buffers in debugfs. The wrapper objects need to remain
|
|
* in memory until the dentry is deleted.
|
|
*/
|
|
struct {
|
|
struct dentry *parent;
|
|
struct dentry *init;
|
|
struct dentry *rm;
|
|
struct dentry *intr;
|
|
struct dentry *pmu;
|
|
} debugfs;
|
|
struct debugfs_blob_wrapper blob_init;
|
|
struct debugfs_blob_wrapper blob_intr;
|
|
struct debugfs_blob_wrapper blob_rm;
|
|
struct debugfs_blob_wrapper blob_pmu;
|
|
#endif
|
|
};
|
|
|
|
static inline bool
|
|
nvkm_gsp_rm(struct nvkm_gsp *gsp)
|
|
{
|
|
return gsp && (gsp->fws.rm || gsp->fw.img);
|
|
}
|
|
|
|
static inline void *
|
|
nvkm_gsp_rpc_get(struct nvkm_gsp *gsp, u32 fn, u32 argc)
|
|
{
|
|
return gsp->rm->rpc_get(gsp, fn, argc);
|
|
}
|
|
|
|
static inline void *
|
|
nvkm_gsp_rpc_push(struct nvkm_gsp *gsp, void *argv, bool wait, u32 repc)
|
|
{
|
|
return gsp->rm->rpc_push(gsp, argv, wait, repc);
|
|
}
|
|
|
|
static inline void *
|
|
nvkm_gsp_rpc_rd(struct nvkm_gsp *gsp, u32 fn, u32 argc)
|
|
{
|
|
void *argv = nvkm_gsp_rpc_get(gsp, fn, argc);
|
|
|
|
if (IS_ERR_OR_NULL(argv))
|
|
return argv;
|
|
|
|
return nvkm_gsp_rpc_push(gsp, argv, true, argc);
|
|
}
|
|
|
|
static inline int
|
|
nvkm_gsp_rpc_wr(struct nvkm_gsp *gsp, void *argv, bool wait)
|
|
{
|
|
void *repv = nvkm_gsp_rpc_push(gsp, argv, wait, 0);
|
|
|
|
if (IS_ERR(repv))
|
|
return PTR_ERR(repv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
nvkm_gsp_rpc_done(struct nvkm_gsp *gsp, void *repv)
|
|
{
|
|
gsp->rm->rpc_done(gsp, repv);
|
|
}
|
|
|
|
static inline void *
|
|
nvkm_gsp_rm_ctrl_get(struct nvkm_gsp_object *object, u32 cmd, u32 argc)
|
|
{
|
|
return object->client->gsp->rm->rm_ctrl_get(object, cmd, argc);
|
|
}
|
|
|
|
static inline int
|
|
nvkm_gsp_rm_ctrl_push(struct nvkm_gsp_object *object, void *argv, u32 repc)
|
|
{
|
|
return object->client->gsp->rm->rm_ctrl_push(object, argv, repc);
|
|
}
|
|
|
|
static inline void *
|
|
nvkm_gsp_rm_ctrl_rd(struct nvkm_gsp_object *object, u32 cmd, u32 repc)
|
|
{
|
|
void *argv = nvkm_gsp_rm_ctrl_get(object, cmd, repc);
|
|
int ret;
|
|
|
|
if (IS_ERR(argv))
|
|
return argv;
|
|
|
|
ret = nvkm_gsp_rm_ctrl_push(object, &argv, repc);
|
|
if (ret)
|
|
return ERR_PTR(ret);
|
|
return argv;
|
|
}
|
|
|
|
static inline int
|
|
nvkm_gsp_rm_ctrl_wr(struct nvkm_gsp_object *object, void *argv)
|
|
{
|
|
int ret = nvkm_gsp_rm_ctrl_push(object, &argv, 0);
|
|
|
|
if (ret)
|
|
return ret;
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
nvkm_gsp_rm_ctrl_done(struct nvkm_gsp_object *object, void *repv)
|
|
{
|
|
object->client->gsp->rm->rm_ctrl_done(object, repv);
|
|
}
|
|
|
|
static inline void *
|
|
nvkm_gsp_rm_alloc_get(struct nvkm_gsp_object *parent, u32 handle, u32 oclass, u32 argc,
|
|
struct nvkm_gsp_object *object)
|
|
{
|
|
struct nvkm_gsp_client *client = parent->client;
|
|
struct nvkm_gsp *gsp = client->gsp;
|
|
void *argv;
|
|
|
|
object->client = parent->client;
|
|
object->parent = parent;
|
|
object->handle = handle;
|
|
|
|
argv = gsp->rm->rm_alloc_get(object, oclass, argc);
|
|
if (IS_ERR_OR_NULL(argv)) {
|
|
object->client = NULL;
|
|
return argv;
|
|
}
|
|
|
|
return argv;
|
|
}
|
|
|
|
static inline void *
|
|
nvkm_gsp_rm_alloc_push(struct nvkm_gsp_object *object, void *argv, u32 repc)
|
|
{
|
|
void *repv = object->client->gsp->rm->rm_alloc_push(object, argv, repc);
|
|
|
|
if (IS_ERR(repv))
|
|
object->client = NULL;
|
|
|
|
return repv;
|
|
}
|
|
|
|
static inline int
|
|
nvkm_gsp_rm_alloc_wr(struct nvkm_gsp_object *object, void *argv)
|
|
{
|
|
void *repv = nvkm_gsp_rm_alloc_push(object, argv, 0);
|
|
|
|
if (IS_ERR(repv))
|
|
return PTR_ERR(repv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
nvkm_gsp_rm_alloc_done(struct nvkm_gsp_object *object, void *repv)
|
|
{
|
|
object->client->gsp->rm->rm_alloc_done(object, repv);
|
|
}
|
|
|
|
static inline int
|
|
nvkm_gsp_rm_alloc(struct nvkm_gsp_object *parent, u32 handle, u32 oclass, u32 argc,
|
|
struct nvkm_gsp_object *object)
|
|
{
|
|
void *argv = nvkm_gsp_rm_alloc_get(parent, handle, oclass, argc, object);
|
|
|
|
if (IS_ERR_OR_NULL(argv))
|
|
return argv ? PTR_ERR(argv) : -EIO;
|
|
|
|
return nvkm_gsp_rm_alloc_wr(object, argv);
|
|
}
|
|
|
|
static inline int
|
|
nvkm_gsp_rm_free(struct nvkm_gsp_object *object)
|
|
{
|
|
if (object->client)
|
|
return object->client->gsp->rm->rm_free(object);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
nvkm_gsp_client_ctor(struct nvkm_gsp *gsp, struct nvkm_gsp_client *client)
|
|
{
|
|
if (WARN_ON(!gsp->rm))
|
|
return -ENOSYS;
|
|
|
|
return gsp->rm->client_ctor(gsp, client);
|
|
}
|
|
|
|
static inline void
|
|
nvkm_gsp_client_dtor(struct nvkm_gsp_client *client)
|
|
{
|
|
if (client->gsp)
|
|
client->gsp->rm->client_dtor(client);
|
|
}
|
|
|
|
static inline int
|
|
nvkm_gsp_device_ctor(struct nvkm_gsp_client *client, struct nvkm_gsp_device *device)
|
|
{
|
|
return client->gsp->rm->device_ctor(client, device);
|
|
}
|
|
|
|
static inline void
|
|
nvkm_gsp_device_dtor(struct nvkm_gsp_device *device)
|
|
{
|
|
if (device->object.client)
|
|
device->object.client->gsp->rm->device_dtor(device);
|
|
}
|
|
|
|
static inline int
|
|
nvkm_gsp_client_device_ctor(struct nvkm_gsp *gsp,
|
|
struct nvkm_gsp_client *client, struct nvkm_gsp_device *device)
|
|
{
|
|
int ret = nvkm_gsp_client_ctor(gsp, client);
|
|
|
|
if (ret == 0) {
|
|
ret = nvkm_gsp_device_ctor(client, device);
|
|
if (ret)
|
|
nvkm_gsp_client_dtor(client);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct nvkm_gsp_event {
|
|
struct nvkm_gsp_device *device;
|
|
u32 id;
|
|
nvkm_gsp_event_func func;
|
|
|
|
struct nvkm_gsp_object object;
|
|
|
|
struct list_head head;
|
|
};
|
|
|
|
static inline int
|
|
nvkm_gsp_device_event_ctor(struct nvkm_gsp_device *device, u32 handle, u32 id,
|
|
nvkm_gsp_event_func func, struct nvkm_gsp_event *event)
|
|
{
|
|
return device->object.client->gsp->rm->event_ctor(device, handle, id, func, event);
|
|
}
|
|
|
|
static inline void
|
|
nvkm_gsp_event_dtor(struct nvkm_gsp_event *event)
|
|
{
|
|
struct nvkm_gsp_device *device = event->device;
|
|
|
|
if (device)
|
|
device->object.client->gsp->rm->event_dtor(event);
|
|
}
|
|
|
|
int nvkm_gsp_intr_stall(struct nvkm_gsp *, enum nvkm_subdev_type, int);
|
|
int nvkm_gsp_intr_nonstall(struct nvkm_gsp *, enum nvkm_subdev_type, int);
|
|
|
|
int gv100_gsp_new(struct nvkm_device *, enum nvkm_subdev_type, int, struct nvkm_gsp **);
|
|
int tu102_gsp_new(struct nvkm_device *, enum nvkm_subdev_type, int, struct nvkm_gsp **);
|
|
int tu116_gsp_new(struct nvkm_device *, enum nvkm_subdev_type, int, struct nvkm_gsp **);
|
|
int ga100_gsp_new(struct nvkm_device *, enum nvkm_subdev_type, int, struct nvkm_gsp **);
|
|
int ga102_gsp_new(struct nvkm_device *, enum nvkm_subdev_type, int, struct nvkm_gsp **);
|
|
int ad102_gsp_new(struct nvkm_device *, enum nvkm_subdev_type, int, struct nvkm_gsp **);
|
|
#endif
|