diff options
| -rw-r--r-- | .gitignore | 15 | ||||
| -rw-r--r-- | kernel/gsp_rm_emulation.c | 154 | ||||
| -rw-r--r-- | kernel/gsp_rm_tu104.h | 49 | ||||
| -rw-r--r-- | tools/guest-diag/ioctl_trace.c | 35 |
4 files changed, 190 insertions, 63 deletions
@@ -116,14 +116,19 @@ tools/debug/nv2080_ctrl vgcore.* userspace/tests/test_vgpu_userspace -# Maintainer notes: study index (never commit) +# Local-only material (never commit). ARCHAEOLOGY.md - -# Local analysis notes adjacent to public analysis (never commit). -# Public analysis refers to them by name only. -**/LOCAL-ANALYSIS.md **/PRIVATE-*.md +# CUDA analysis area: `userspace/cuda/*/run/` is the per-version scratch +# tree: extracted vendor binaries, Ghidra projects, decompiled corpora and +# annotated corpora - all vendor-derived or huge, so the whole tree is +# excluded regardless of extension. +userspace/cuda/run/ +userspace/cuda/*/run/ +# per-area coordination scratch (local only) +userspace/cuda/WORKING-HERE.md + # generated at runtime by tools/guest-diag/vmcycle.sh tools/guest-diag/launch_qemu_rootport.run.sh diff --git a/kernel/gsp_rm_emulation.c b/kernel/gsp_rm_emulation.c index b9f8417..44de6c0 100644 --- a/kernel/gsp_rm_emulation.c +++ b/kernel/gsp_rm_emulation.c @@ -352,6 +352,10 @@ static u32 gsp_rm_checksum32(const void *data, u32 len) */ #define GSPRM_ELEM_HDR_SIZE 0x30 +/* rpc_gsp_rm_alloc_v03_00 fixed header: hClient, hParent, hObject, hClass, + * status, paramsSize, flags, reserved[4]. */ +#define GSPRM_ALLOC_HDR_SIZE 32 + /* * Max RPC data that fits in one queue element: * 4096 (element) - 0x30 (element hdr) - 32 (rpc hdr) = 4016 bytes. @@ -817,8 +821,15 @@ static void gsp_rm_build_static_info(u8 *buf) val32 = 0; memcpy(buf + TU104_GSCI_OFF_sriovMaxGfid, &val32, 4); - /* engineCaps (offset 1204, 12 bytes) */ - val32 = 0x60003E02; /* GR0|CE0-4|NVDEC0|NVDEC1 */ + /* engineCaps (offset 1204, 12 bytes) + * + * Bit N = RM_ENGINE_TYPE_<N> present (RM_ENGINE_TYPE_* equals the bit + * index, see gpu_engine_type.h). The async copy engine is + * RM_ENGINE_TYPE_COPY14 (0x17); clients (libcuda) refuse to create a + * context without it: the secondary/async engine record stays empty and + * the engine-context RM alloc is issued with class 0 (NV01_ROOT), which + * the driver must reject. */ + val32 = 0x60803E02; /* GR0|CE0-4|COPY14|NVDEC0|NVDEC1 */ memcpy(buf + TU104_GSCI_OFF_engineCaps, &val32, 4); val32 = 0x00362020; /* NVENC0|SW|SEC2|HOST|PMU|FBFLCN */ memcpy(buf + TU104_GSCI_OFF_engineCaps + 4, &val32, 4); @@ -1308,8 +1319,19 @@ static void gsp_rm_handle_ce_physical_caps(struct gsp_emu_context *ctx, if (ce_type >= TU104_ENGINE_TYPE_COPY0 && ce_type < TU104_ENGINE_TYPE_COPY0 + TU104_NUM_CES) { + u32 idx = ce_type - TU104_ENGINE_TYPE_COPY0; + out_params[4] = TU104_CE_CAPS0; out_params[5] = TU104_CE_CAPS1; + + /* The GR-synchronous CEs (GRCE) are the first ones: the client + * picks the last GRCE as its compute companion ("ceForCompute", + * rm_device.c) and every non-GRCE CE becomes an async CE. With + * no GRCE at all the client's context-creation walk ends with an + * empty secondary-engine record and the engine RM alloc is issued + * with class 0. */ + if (idx == TU104_GRCE_IDX) + out_params[4] |= TU104_CE_CAPS0_GRCE; } else { *out_status = 0x56; /* NV_ERR_NOT_SUPPORTED */ *out_params_size = req_size; @@ -1342,6 +1364,8 @@ static void gsp_rm_handle_ce_all_caps(struct gsp_emu_context *ctx, for (i = 0; i < TU104_NUM_CES; i++) { out_params[i * 2 + 0] = TU104_CE_CAPS0; out_params[i * 2 + 1] = TU104_CE_CAPS1; + if (i == TU104_GRCE_IDX) + out_params[i * 2 + 0] |= TU104_CE_CAPS0_GRCE; present |= 1ULL << i; } memcpy(out_params + 128, &present, 8); @@ -1898,6 +1922,87 @@ static void gsp_rm_handle_copy_server_pdes(struct gsp_emu_context *ctx, } } +static bool gsp_rm_alloc_interesting(u32 class_id); + +/* + * GSP_RM_ALLOC (103) - rpc_gsp_rm_alloc_v03_00 (32 byte header) followed by + * the class-specific allocation parameters. + * + * The reply MUST echo the request payload back: the client copies the reply + * params over the caller's params buffer using the class-expected size (not + * the size the caller reserved), so replying with zeroed params smashes + * whatever sits above the caller's params buffer. The alloc params are + * in/out for the classes we do not model in detail, so echoing the input is + * both the safe and the correct "unchanged" answer. status (offset 16) is + * the only field we deliberately rewrite (to NV_OK). + * + * NOTE: the RPC header 'length' only counts the fixed struct size, so it + * must NOT be used to bound the params - read the paramsSize field from the + * alloc header instead (see handoff PART 6). + */ +static void gsp_rm_handle_rm_alloc(struct gsp_emu_context *ctx, + const u8 *elem, u32 sequence) +{ + struct gsp_rm_emu *rm = ctx->rm_emu; + const u8 *p = elem + GSPRM_ELEM_HDR_SIZE + + sizeof(struct gsprm_rpc_header); + const u8 *params = p + GSPRM_ALLOC_HDR_SIZE; + u32 hc = 0, hp = 0, ho = 0, class_id = 0; + u32 params_size = 0, flags = 0; + u32 echo_len, reply_len; + u8 *reply; + + memcpy(&hc, p + 0, 4); + memcpy(&hp, p + 4, 4); + memcpy(&ho, p + 8, 4); + memcpy(&class_id, p + 12, 4); + memcpy(¶ms_size, p + 20, 4); + memcpy(&flags, p + 24, 4); + + nv_vgpu_info(ctx->vgpu, + "GSPRM: GSP_RM_ALLOC class=0x%x paramsSize=%u flags=0x%x hClient=0x%x hParent=0x%x hObject=0x%x", + class_id, params_size, flags, hc, hp, ho); + + /* Full param dump for memory/channel classes and any class we have not + * classified yet (rate-limited). %phN is capped at 64 bytes per print, + * so chunk it. */ + if (gsp_rm_alloc_interesting(class_id) && rm->alloc_param_dumps < 60) { + u32 shown = min(params_size, (u32)384); + u32 off; + + rm->alloc_param_dumps++; + for (off = 0; off < shown; off += 64) { + u32 n = min(shown - off, (u32)64); + + nv_vgpu_info(ctx->vgpu, + "GSPRM: alloc class=0x%x params[%u..%u]=%*phN", + class_id, off, off + n, (int)n, + params + off); + } + } + + /* Echo the fixed alloc header + as many param bytes as are contiguous + * in the first queue element. */ + echo_len = min(params_size, + (u32)GSPRM_MAX_SINGLE_ELEM_DATA - GSPRM_ALLOC_HDR_SIZE); + reply_len = GSPRM_ALLOC_HDR_SIZE + echo_len; + + reply = kmalloc(reply_len, GFP_KERNEL); + if (!reply) { + nv_vgpu_err(ctx->vgpu, "GSPRM: GSP_RM_ALLOC reply alloc failed"); + return; + } + + memcpy(reply, p, reply_len); + memset(reply + 16, 0, 4); /* status = NV_OK */ + + if (gsp_rm_send(ctx, GSPRM_RPC_FUNC_GSP_RM_ALLOC, sequence, 0, + reply, reply_len)) + nv_vgpu_err(ctx->vgpu, "GSPRM: GSP_RM_ALLOC reply send failed"); + + kfree(reply); +} + static void gsp_rm_handle_rm_control(struct gsp_emu_context *ctx, const u8 *elem, u32 sequence) { @@ -2200,6 +2305,10 @@ static void gsp_rm_handle_element(struct gsp_emu_context *ctx, const u8 *elem) gsp_rm_handle_get_static_info(ctx, sequence); return; + case GSPRM_RPC_FUNC_GSP_RM_ALLOC: + gsp_rm_handle_rm_alloc(ctx, elem, sequence); + return; + case GSPRM_RPC_FUNC_GSP_RM_CONTROL: gsp_rm_handle_rm_control(ctx, elem, sequence); return; @@ -2217,47 +2326,6 @@ static void gsp_rm_handle_element(struct gsp_emu_context *ctx, const u8 *elem) nv_vgpu_info(ctx->vgpu, "GSPRM: RPC %s(%u) seq=%u cmd_read=%u stat_write=%u stat_seq=%u", gsp_rm_rpc_name(function), function, sequence, rm->cmd_read, rm->stat_write, rm->stat_seq); - } else if (function == GSPRM_RPC_FUNC_GSP_RM_ALLOC) { - /* GSP_RM_ALLOC (103): rpc_gsp_rm_alloc_v03_00 at - * elem+GSPRM_ELEM_HDR_SIZE. NOTE: the RPC header - * 'length' only counts the fixed struct size (64), so - * it must NOT be used to bound the params - read the - * alloc header directly. See handoff PART 6. */ - const u8 *p = elem + GSPRM_ELEM_HDR_SIZE + - sizeof(struct gsprm_rpc_header); - const u8 *params = p + 32; - u32 hc = 0, hp = 0, ho = 0, class_id = 0; - u32 params_size = 0, flags = 0; - - memcpy(&hc, p + 0, 4); - memcpy(&hp, p + 4, 4); - memcpy(&ho, p + 8, 4); - memcpy(&class_id, p + 12, 4); - memcpy(¶ms_size, p + 20, 4); - memcpy(&flags, p + 24, 4); - - nv_vgpu_info(ctx->vgpu, - "GSPRM: GSP_RM_ALLOC class=0x%x paramsSize=%u flags=0x%x hClient=0x%x hParent=0x%x hObject=0x%x", - class_id, params_size, flags, hc, hp, ho); - - /* Full param dump for memory/channel classes and any - * class we have not classified yet (rate-limited). - * %phN is capped at 64 bytes per print, so chunk it. */ - if (gsp_rm_alloc_interesting(class_id) && - rm->alloc_param_dumps < 60) { - u32 shown = min(params_size, (u32)384); - u32 off; - - rm->alloc_param_dumps++; - for (off = 0; off < shown; off += 64) { - u32 n = min(shown - off, (u32)64); - - nv_vgpu_info(ctx->vgpu, - "GSPRM: alloc class=0x%x params[%u..%u]=%*phN", - class_id, off, off + n, (int)n, - params + off); - } - } } else { nv_vgpu_info(ctx->vgpu, "GSPRM: RPC %s(%u) seq=%u len=%u -> generic SUCCESS reply (unknown #%llu)", gsp_rm_rpc_name(function), function, sequence, diff --git a/kernel/gsp_rm_tu104.h b/kernel/gsp_rm_tu104.h index 6a19726..74d1ba3 100644 --- a/kernel/gsp_rm_tu104.h +++ b/kernel/gsp_rm_tu104.h @@ -142,6 +142,7 @@ struct tu104_get_constructed_falcon_info_params { #define TU104_ENG_CE2 0x793ceb02 #define TU104_ENG_CE3 0x793ceb03 #define TU104_ENG_CE4 0x793ceb04 +#define TU104_ENG_CES 0x793ceb0e /* classId(OBJCE) inst 14 (async) */ #define TU104_ENG_NVDEC0 0x8f99e100 /* classId(OBJBSP)=0x8f99e1, inst 0 */ #define TU104_ENG_NVDEC1 0x8f99e101 #define TU104_ENG_NVENC0 0xe97b6c00 /* classId(OBJMSENC)=0xe97b6c, inst 0 */ @@ -159,6 +160,7 @@ struct tu104_get_constructed_falcon_info_params { #define TU104_RM_ENGINE_CE2 0x0b #define TU104_RM_ENGINE_CE3 0x0c #define TU104_RM_ENGINE_CE4 0x0d +#define TU104_RM_ENGINE_CES 0x17 /* RM_ENGINE_TYPE_COPY14 */ #define TU104_RM_ENGINE_NVDEC0 0x1d #define TU104_RM_ENGINE_NVDEC1 0x1e #define TU104_RM_ENGINE_NVENC0 0x25 @@ -178,6 +180,7 @@ struct tu104_get_constructed_falcon_info_params { #define TU104_MC_ENGINE_CE2 17 #define TU104_MC_ENGINE_CE3 18 #define TU104_MC_ENGINE_CE4 19 +#define TU104_MC_ENGINE_CES 29 /* MC_ENGINE_IDX_CE14 */ #define TU104_MC_ENGINE_NVDEC0 65 #define TU104_MC_ENGINE_NVDEC1 66 #define TU104_MC_ENGINE_NVENC0 38 @@ -193,6 +196,7 @@ struct tu104_get_constructed_falcon_info_params { #define TU104_FAULT_ID_CE2 17 #define TU104_FAULT_ID_CE3 18 #define TU104_FAULT_ID_CE4 19 +#define TU104_FAULT_ID_CES 29 #define TU104_FAULT_ID_NVDEC0 10 #define TU104_FAULT_ID_NVDEC1 25 #define TU104_FAULT_ID_NVENC0 11 @@ -241,6 +245,7 @@ struct tu104_get_constructed_falcon_info_params { #define TU104_TAG_CE2 3 #define TU104_TAG_CE3 4 #define TU104_TAG_CE4 5 +#define TU104_TAG_CES 6 #define TU104_TAG_NVDEC0 10 #define TU104_TAG_NVDEC1 11 #define TU104_TAG_NVENC0 13 @@ -282,7 +287,7 @@ static const struct tu104_fifo_device_entry tu104_engine_table[] = { .numPbdmas = 1, .engineName = "GRAPHICS", }, - /* [1] CE0 */ + /* [1] CE0 - the GR-synchronous CE (GRCE); shares the GR PBDMA */ { .engineData = { [EI_ENG_DESC] = TU104_ENG_CE0, @@ -302,8 +307,8 @@ static const struct tu104_fifo_device_entry tu104_engine_table[] = { [EI_CHRAM_PRI_BASE] = 0, [EI_KERNEL_RM_MAX] = 0, }, - .pbdmaIds = {1, 0}, - .pbdmaFaultIds = {TU104_PBDMA_FAULT_ID_BASE + 1, 0}, + .pbdmaIds = {0, 0}, + .pbdmaFaultIds = {TU104_PBDMA_FAULT_ID_BASE + 0, 0}, .numPbdmas = 1, .engineName = "CE0", }, @@ -407,7 +412,36 @@ static const struct tu104_fifo_device_entry tu104_engine_table[] = { .numPbdmas = 1, .engineName = "CE4", }, - /* [6] NVDEC0 */ + /* [6] CES - async copy engine (RM COPY14 / OBJCE instance 14). + * Clients (libcuda) refuse to finish context creation without it: the + * async/secondary engine record stays empty and the engine-context RM + * alloc is issued with class 0. Not on a runlist of its own; shares the + * CE runlist. */ + { + .engineData = { + [EI_ENG_DESC] = TU104_ENG_CES, + [EI_FIFO_TAG] = TU104_TAG_CES, + [EI_RM_ENGINE_TYPE] = TU104_RM_ENGINE_CES, + [EI_RUNLIST] = 5, + [EI_MMU_FAULT_ID] = TU104_FAULT_ID_CES, + [EI_RC_MASK] = TU104_RC_CE4, + [EI_RESET] = 5, + [EI_INTR] = 5, + [EI_MC] = TU104_MC_ENGINE_CES, + [EI_DEV_TYPE_ENUM] = TU104_PTOP_TYPE_LCE, + [EI_INSTANCE_ID] = 14, + [EI_RUNLIST_PRI_BASE] = 0, + [EI_IS_HOST_DRIVEN_ENGINE] = 1, + [EI_RUNLIST_ENGINE_ID] = 0, + [EI_CHRAM_PRI_BASE] = 0, + [EI_KERNEL_RM_MAX] = 0, + }, + .pbdmaIds = {5, 0}, + .pbdmaFaultIds = {TU104_PBDMA_FAULT_ID_BASE + 5, 0}, + .numPbdmas = 1, + .engineName = "CES", + }, + /* [7] NVDEC0 */ { .engineData = { [EI_ENG_DESC] = TU104_ENG_NVDEC0, @@ -559,7 +593,7 @@ static const struct tu104_fifo_device_entry tu104_engine_table[] = { }, }; -#define TU104_ENGINE_TABLE_SIZE 12 +#define TU104_ENGINE_TABLE_SIZE 13 /* ============================================================ * TU104 Interrupt Table (from intr_phys_tu102.c, kernel table) @@ -706,6 +740,7 @@ static const struct tu104_internal_device_info tu104_internal_device_table[] = { #define TU104_CTRL_CE_GET_CE_PCE_MASK 0x20802a02 /* CE capability bits (NV2080_CTRL_CE_CAPS_*, ctrl2080ce.h: byte:bit) */ +#define TU104_CE_CAPS0_GRCE 0x01 /* synchronous with GR (GRCE) */ #define TU104_CE_CAPS0_SYSMEM_READ 0x04 #define TU104_CE_CAPS0_SYSMEM_WRITE 0x08 #define TU104_CE_CAPS0_SYSMEM 0x20 @@ -721,6 +756,10 @@ static const struct tu104_internal_device_info tu104_internal_device_table[] = { /* ENGINE_TYPE_COPY0 = 0x9, COPY(i) = COPY0 + i (cl2080_notification.h) */ #define TU104_ENGINE_TYPE_COPY0 0x00000009 #define TU104_NUM_CES 5 +/* The GR-synchronous CE (GRCE): the one sharing the GR PBDMA. Clients pick + * it as their compute companion (ceForCompute); the driver accepts a CE on + * the GR runqueue only when it shares GR's PBDMA ids. */ +#define TU104_GRCE_IDX 0 #define TU104_CTRL_INTERNAL_GMMU_GET_STATIC_INFO 0x20800a59 #define TU104_CTRL_STATIC_KGR_GET_GLOBAL_SM_ORDER 0x20800a22 #define TU104_CTRL_STATIC_KGR_GET_INFO 0x20800a2a diff --git a/tools/guest-diag/ioctl_trace.c b/tools/guest-diag/ioctl_trace.c index fd1802a..04fea05 100644 --- a/tools/guest-diag/ioctl_trace.c +++ b/tools/guest-diag/ioctl_trace.c @@ -38,17 +38,29 @@ static int read_child(pid_t pid, unsigned long addr, void *buf, size_t len) return 0; } -static void dump_alloc(pid_t pid, unsigned long arg) +static void dump_alloc(pid_t pid, unsigned long arg, unsigned int size) { unsigned int v[12]; + unsigned int st; if (read_child(pid, arg, v, sizeof(v))) return; - /* NVOS21_PARAMETERS: hRoot, hObjectParent, hObjectNew, hClass, - * pAllocParms, pRightsRequested, paramsSize, flags, status */ + /* NVOS21_PARAMETERS (0x20): ... paramsSize@0x14, status@0x10 + * NVOS64_PARAMETERS (0x30): ... status@0x28 */ + st = (size == 0x30) ? v[10] : v[4]; fprintf(stderr, - "[iot] alloc root=0x%x parent=0x%x new=0x%x class=0x%x params=%u flags=0x%x\n", - v[0], v[1], v[2], v[3], v[6], v[7]); + "[iot] alloc root=0x%x parent=0x%x new=0x%x class=0x%x st=0x%x\n", + v[0], v[1], v[2], v[3], st); +} + +static void dump_free(pid_t pid, unsigned long arg) +{ + unsigned int v[4]; + + if (read_child(pid, arg, v, sizeof(v))) + return; + /* NVOS00_PARAMETERS: hRoot, hObjectParent, hObjectOld, status */ + fprintf(stderr, "[iot] free old=0x%x st=0x%x\n", v[2], v[3]); } static void dump_control(pid_t pid, unsigned long arg) @@ -59,12 +71,13 @@ static void dump_control(pid_t pid, unsigned long arg) if (read_child(pid, arg, v, sizeof(v))) return; - /* NVOS54_PARAMETERS: hClient, hObject, cmd, flags, params, paramsSize, ... */ + /* NVOS54_PARAMETERS: hClient, hObject, cmd, flags, params, paramsSize, + * status@0x1c */ cmd = v[2]; memcpy(&pParams, &v[4], sizeof(pParams)); fprintf(stderr, - "[iot] control client=0x%x object=0x%x cmd=0x%x params=0x%llx size=%u\n", - v[0], v[1], cmd, pParams, v[5]); + "[iot] control client=0x%x object=0x%x cmd=0x%x params=0x%llx size=%u st=0x%x\n", + v[0], v[1], cmd, pParams, v[5], v[7]); } int main(int argc, char **argv) @@ -148,8 +161,10 @@ int main(int argc, char **argv) fprintf(stderr, "[iot] req=0x%llx arg=0x%llx ret=0x%llx\n", req, a2, ret); - if (req == 0xc030462bULL) - dump_alloc(pid, a2); + if ((req & 0xff) == 0x2b) + dump_alloc(pid, a2, (req >> 16) & 0x3fff); + else if ((req & 0xff) == 0x29) + dump_free(pid, a2); else if ((req & 0xff) == 0x2a) dump_control(pid, a2); } |
