summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-22 23:32:44 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-09-22 23:32:44 -0400
commit14283fe73fb5e432a7af45de94961655714910f6 (patch)
treee993bf56f3e2ef93195019ac0fd996d82204384a /kernel
parentb34e4531d8f0b984dc15660d3ab406ae48822931 (diff)
downloadopen-iov-14283fe73fb5e432a7af45de94961655714910f6.tar.gz
open-iov-14283fe73fb5e432a7af45de94961655714910f6.zip
kernel: FB/BUS info-list controls + PCIe link caps for the UVM path
CUDA (cuInit) now reaches UVM's device create: libcuda opens nvidia-uvm, UVM calls nvGpuOpsDeviceCreate/gpuGetGpuInfo, and the driver fails at getPCIELinkRateMBps -> calculatePCIELinkRateMBps ("Unknown PCIe speed", NV_ERR_INVALID_STATE) -> nv_open_device aborts -> cuInit returns CUDA_ERROR_NOT_INITIALIZED (3). Implemented along the way: - FB_GET_INFO_V2 (0x20801303): echo the requested FB info indices and answer TU104 values (256MB RAM/heap/BAR1, GDDR6, 8 LTCs, 4MB L2, ...). Fixes the kern_mem_sys_ctrl.c:911 asserts and gives CUDA/UVM real memory sizes. - BUS_GET_INFO_V2 (0x20801823): echo indices; PCIe Gen3 x16 link caps / GEN info / link status, SYSMEM_CONNECTION_TYPE=PCIE. UVM's gpuGetSysmemLinkInfo reads SYSMEM_CONNECTION_TYPE and the nominal link bandwidth from here. - PCIe Link Capabilities in the emulated config space (0x8C/0x92) and in the BAR0 PCFG mirror (gpuReadBusConfigReg -> NV_PCFG+0x84 = BAR0+ 0x88084): MAX_SPEED=3 (8GT/s Gen3), MAX_WIDTH=0x10 (x16); link status mirror at 0x88088. Also mirrored under the VF-window/low aliases. - Temporary change-watch on PCFG[0x88084] to see if the guest overwrites it (it does not - stays 0x103). Verified: guest nvidia-smi -L still works and reports PCIe Generation Max/Current = 3. cuInit still fails; the remaining question is which address the driver's kbifGetGpuLinkCapabilities read sees as zero (the PCFG shadow holds 0x103). Guest-side kprobe tracing of that function panicked the VM (free_swap_cache page fault on a corrupted pointer) - possible guest memory corruption to investigate separately.
Diffstat (limited to 'kernel')
-rw-r--r--kernel/bar_emulation.c30
-rw-r--r--kernel/gsp_emulation.c12
-rw-r--r--kernel/gsp_rm_emulation.c201
-rw-r--r--kernel/gsp_rm_tu104.h99
-rw-r--r--kernel/main.c8
5 files changed, 346 insertions, 4 deletions
diff --git a/kernel/bar_emulation.c b/kernel/bar_emulation.c
index eaa006c..3448b8c 100644
--- a/kernel/bar_emulation.c
+++ b/kernel/bar_emulation.c
@@ -280,6 +280,36 @@ int nv_vgpu_bar_emul_init(struct nv_vgpu_device *vgpu)
*(u32 *)((u8 *)ctx->bar0_shadow + 0x100ce0) = 0x00000018;
/*
+ * NV_PCFG (BAR0+0x88000) - PCI config space mirror the driver reads
+ * via gpuReadBusConfigReg_HAL (GPU_BUS_CFG_RD32):
+ * 0x88084 NV_XVE_LINK_CAPABILITIES: MAX_LINK_SPEED 3:0 = 3 (8 GT/s,
+ * Gen3), MAX_LINK_WIDTH 9:4 = 0x10 (x16).
+ * 0x88088 NV_XVE_LINK_CONTROL_STATUS: negotiated speed 19:16 = 3,
+ * negotiated width 25:20 = 0x10 (x16).
+ * UVM's nvGpuOpsDeviceCreate -> getPCIELinkRateMBps ->
+ * kbifControlGetPCIEInfo -> kbifGetGpuLinkCapabilities reads 0x88084;
+ * zeros make calculatePCIELinkRateMBps log "Unknown PCIe speed" and
+ * return NV_ERR_INVALID_STATE, which fails cuInit
+ * (CUDA_ERROR_NOT_INITIALIZED in the guest).
+ *
+ * DEVICE_BASE(NV_PCFG) expands to 0 in this driver (nvmisc.h:
+ * "#define DEVICE_BASE(d) (0?d)"), so the read is actually
+ * gpuNvAddr + 0x84 - mirror the values at the low alias as well.
+ */
+ *(u32 *)((u8 *)ctx->bar0_shadow + 0x88084) = 0x00000103;
+ *(u32 *)((u8 *)ctx->bar0_shadow + 0x88088) = 0x01030000;
+ *(u32 *)((u8 *)ctx->bar0_shadow + 0x00084) = 0x00000103;
+ *(u32 *)((u8 *)ctx->bar0_shadow + 0x00088) = 0x01030000;
+ /* Same values under the VF register window aliases, in case this
+ * driver reaches the config mirror through virtualRegPhysOffset. */
+ *(u32 *)((u8 *)ctx->bar0_shadow + 0xB80084) = 0x00000103;
+ *(u32 *)((u8 *)ctx->bar0_shadow + 0xB80088) = 0x01030000;
+ *(u32 *)((u8 *)ctx->bar0_shadow + 0xB88084) = 0x00000103;
+ *(u32 *)((u8 *)ctx->bar0_shadow + 0xB88088) = 0x01030000;
+ *(u32 *)((u8 *)ctx->bar0_shadow + 0xC08084) = 0x00000103;
+ *(u32 *)((u8 *)ctx->bar0_shadow + 0xC08088) = 0x01030000;
+
+ /*
* Serve a valid VBIOS at the PROM window (BAR0+0x300000).
*
* The guest driver's physical GSP boot (kgspInitRm -> TU102 path)
diff --git a/kernel/gsp_emulation.c b/kernel/gsp_emulation.c
index 0b6bba0..969d4fd 100644
--- a/kernel/gsp_emulation.c
+++ b/kernel/gsp_emulation.c
@@ -305,6 +305,18 @@ static void gsp_emu_poll_boot(struct gsp_emu_context *ctx)
}
}
+ /* Temporary: watch the PCFG link-caps mirror for guest writes */
+ {
+ static u32 last_linkcap;
+ u32 v = gsp_emu_shadow_read32(ctx, 0x88084);
+
+ if (v != last_linkcap) {
+ nv_vgpu_info(ctx->vgpu, "GSP-BOOT: PCFG[0x88084] = 0x%x (was 0x%x)",
+ v, last_linkcap);
+ last_linkcap = v;
+ }
+ }
+
/* GSP ENGINE reset asserted -> falcon rebooted, restart stage tracking */
if ((engine & 0x1) && ctx->boot_stage != GSP_BOOT_STAGE_IDLE) {
nv_vgpu_info(ctx->vgpu, "GSP-BOOT: falcon reset, back to IDLE");
diff --git a/kernel/gsp_rm_emulation.c b/kernel/gsp_rm_emulation.c
index f18d17c..743a260 100644
--- a/kernel/gsp_rm_emulation.c
+++ b/kernel/gsp_rm_emulation.c
@@ -1269,6 +1269,193 @@ static void gsp_rm_handle_kgr_get_context_buffers_info(
TU104_CTX_GPU_MAX_ENGINES, TU104_CTX_ENGINE_ID_COUNT);
}
+/*
+ * FB_GET_INFO_V2 (0x20801303)
+ *
+ * The client (kern_mem_sys_ctrl.c _fbGetFbInfos) sends a list of FB info
+ * indices to the physical RM and expects the same indices back with data
+ * populated. A zeroed reply trips "pFbInfos[i].index == ..." asserts and
+ * leaves all FB info zero - CUDA/UVM read total/usable memory, BAR1 size,
+ * L2/LTC topology from here. Echo the requested indices and fill in the
+ * emulated TU104 values.
+ */
+static u32 tu104_fb_info_data(struct gsp_emu_context *ctx, u32 index)
+{
+ struct nv_vgpu_device *vgpu = ctx->vgpu;
+ u64 fb = vgpu->fb_size ? vgpu->fb_size : NV_VGPU_BAR1_SIZE;
+
+ switch (index) {
+ case TU104_FB_INFO_IDX_TILE_REGION_COUNT:
+ return 0;
+ case TU104_FB_INFO_IDX_COMPRESSION_SIZE:
+ return 0;
+ case TU104_FB_INFO_IDX_PARTITION_COUNT:
+ return 1;
+ case TU104_FB_INFO_IDX_BAR1_SIZE:
+ return (u32)fb;
+ case TU104_FB_INFO_IDX_RAM_SIZE:
+ case TU104_FB_INFO_IDX_TOTAL_RAM_SIZE:
+ case TU104_FB_INFO_IDX_USABLE_RAM_SIZE:
+ return (u32)fb;
+ case TU104_FB_INFO_IDX_HEAP_SIZE:
+ case TU104_FB_INFO_IDX_MAPPABLE_HEAP_SIZE:
+ return (u32)fb;
+ case TU104_FB_INFO_IDX_BUS_WIDTH:
+ return 256;
+ case TU104_FB_INFO_IDX_RAM_TYPE:
+ return TU104_RAM_TYPE_GDDR6;
+ case TU104_FB_INFO_IDX_BANK_COUNT:
+ return 8;
+ case TU104_FB_INFO_IDX_FB_TAX_SIZE_KB:
+ case TU104_FB_INFO_IDX_LARGEST_FREE_REGION_SIZE_KB:
+ return (u32)(fb >> 10);
+ case TU104_FB_INFO_IDX_PARTITION_MASK:
+ return 1;
+ case TU104_FB_INFO_IDX_HEAP_FREE:
+ return (u32)fb;
+ case TU104_FB_INFO_IDX_RAM_LOCATION:
+ return TU104_RAM_LOCATION_GPU_DEDICATED;
+ case TU104_FB_INFO_IDX_FBP_COUNT:
+ return 1;
+ case TU104_FB_INFO_IDX_FBP_MASK:
+ return 1;
+ case TU104_FB_INFO_IDX_L2CACHE_SIZE:
+ return TU104_L2CACHE_SIZE;
+ case TU104_FB_INFO_IDX_MEMORYINFO_VENDOR_ID:
+ return TU104_MEMORYINFO_VENDOR_ID_SAMSUNG;
+ case TU104_FB_INFO_IDX_BAR1_AVAIL_SIZE:
+ case TU104_FB_INFO_IDX_BAR1_MAX_CONTIG_AVAIL_SIZE:
+ return (u32)fb;
+ case TU104_FB_INFO_IDX_LTC_COUNT:
+ return TU104_LTC_COUNT;
+ case TU104_FB_INFO_IDX_LTS_COUNT:
+ return TU104_LTS_COUNT;
+ case TU104_FB_INFO_IDX_PSEUDO_CHANNEL_MODE:
+ return TU104_PSEUDO_CHANNEL_MODE_DISABLED;
+ case TU104_FB_INFO_IDX_ALLOW_PAGE_RETIREMENT:
+ return 0;
+ case TU104_FB_INFO_IDX_LTC_MASK:
+ return (1U << TU104_LTC_COUNT) - 1;
+ default:
+ return 0;
+ }
+}
+
+static void gsp_rm_handle_fb_get_info_v2(struct gsp_emu_context *ctx,
+ const u8 *req_params,
+ u32 req_params_size,
+ u32 *out_status,
+ u8 *out_params,
+ u32 *out_params_size)
+{
+ struct tu104_fb_get_info_v2_params *reply =
+ (struct tu104_fb_get_info_v2_params *)out_params;
+ u32 count = 0;
+ u32 i;
+
+ if (req_params_size >= sizeof(count))
+ memcpy(&count, req_params, sizeof(count));
+ if (count > TU104_FB_INFO_MAX_LIST_SIZE)
+ count = TU104_FB_INFO_MAX_LIST_SIZE;
+
+ memset(out_params, 0, sizeof(*reply));
+ reply->fbInfoListSize = count;
+
+ for (i = 0; i < count; i++) {
+ const u8 *in = req_params + sizeof(count) +
+ i * sizeof(struct tu104_fb_info_entry);
+ u32 index = 0;
+
+ if (sizeof(count) + (i + 1) * sizeof(struct tu104_fb_info_entry) <=
+ req_params_size)
+ memcpy(&index, in, sizeof(index));
+
+ reply->fbInfoList[i].index = index;
+ reply->fbInfoList[i].data = tu104_fb_info_data(ctx, index);
+ }
+
+ *out_params_size = sizeof(*reply);
+ *out_status = 0;
+
+ nv_vgpu_info(ctx->vgpu, "GSPRM: FB_GET_INFO_V2 -> %u entries populated (first idx=0x%x)",
+ count, count ? reply->fbInfoList[0].index : 0);
+}
+
+/*
+ * BUS_GET_INFO_V2 (0x20801823)
+ *
+ * Same flat index/data list pattern as FB_GET_INFO_V2. UVM's
+ * gpuGetSysmemLinkInfo() asks for SYSMEM_CONNECTION_TYPE and then
+ * PCIE_GPU_LINK_CAPS to compute the sysmem link bandwidth; a zeroed reply
+ * makes getPCIELinkRateMBps() return NV_ERR_INVALID_STATE ("Unsupported
+ * sysmem connection type"). Report a PCIe Gen3 x16 link for the emulated
+ * vGPU and echo every requested index.
+ */
+static u32 tu104_bus_info_data(u32 index)
+{
+ switch (index) {
+ case TU104_BUS_INFO_IDX_TYPE:
+ return TU104_BUS_INFO_TYPE_PCI_EXPRESS;
+ case TU104_BUS_INFO_IDX_INTLINE:
+ return 0;
+ case TU104_BUS_INFO_IDX_PCIE_GPU_LINK_CAPS:
+ case TU104_BUS_INFO_IDX_PCIE_ROOT_LINK_CAPS:
+ case TU104_BUS_INFO_IDX_PCIE_UPSTREAM_LINK_CAPS:
+ case TU104_BUS_INFO_IDX_PCIE_DOWNSTREAM_LINK_CAPS:
+ case TU104_BUS_INFO_IDX_PCIE_GEN_INFO:
+ return TU104_PCIE_LINK_CAP_GEN3_X16;
+ case TU104_BUS_INFO_IDX_PCIE_GPU_LINK_CTRL_STATUS:
+ case TU104_BUS_INFO_IDX_PCIE_ROOT_LINK_CTRL_STATUS:
+ case TU104_BUS_INFO_IDX_PCIE_UPSTREAM_LINK_CTRL:
+ case TU104_BUS_INFO_IDX_PCIE_DOWNSTREAM_LINK_CTRL:
+ return TU104_PCIE_LINK_CTRL_STATUS_GEN3_X16;
+ case TU104_BUS_INFO_IDX_SYSMEM_CONNECTION_TYPE:
+ return TU104_BUS_INFO_SYSMEM_CONN_PCIE;
+ default:
+ return 0;
+ }
+}
+
+static void gsp_rm_handle_bus_get_info_v2(struct gsp_emu_context *ctx,
+ const u8 *req_params,
+ u32 req_params_size,
+ u32 *out_status,
+ u8 *out_params,
+ u32 *out_params_size)
+{
+ struct tu104_bus_get_info_v2_params *reply =
+ (struct tu104_bus_get_info_v2_params *)out_params;
+ u32 count = 0;
+ u32 i;
+
+ if (req_params_size >= sizeof(count))
+ memcpy(&count, req_params, sizeof(count));
+ if (count > TU104_BUS_INFO_MAX_LIST_SIZE)
+ count = TU104_BUS_INFO_MAX_LIST_SIZE;
+
+ memset(out_params, 0, sizeof(*reply));
+ reply->busInfoListSize = count;
+
+ for (i = 0; i < count; i++) {
+ const u8 *in = req_params + sizeof(count) +
+ i * sizeof(struct tu104_bus_info_entry);
+ u32 index = 0;
+
+ if (sizeof(count) + (i + 1) * sizeof(struct tu104_bus_info_entry) <=
+ req_params_size)
+ memcpy(&index, in, sizeof(index));
+
+ reply->busInfoList[i].index = index;
+ reply->busInfoList[i].data = tu104_bus_info_data(index);
+ }
+
+ *out_params_size = sizeof(*reply);
+ *out_status = 0;
+
+ nv_vgpu_info(ctx->vgpu, "GSPRM: BUS_GET_INFO_V2 -> %u entries populated (first idx=0x%x)",
+ count, count ? reply->busInfoList[0].index : 0);
+}
+
static void gsp_rm_handle_rm_control(struct gsp_emu_context *ctx,
const u8 *elem, u32 sequence)
{
@@ -1375,6 +1562,20 @@ static void gsp_rm_handle_rm_control(struct gsp_emu_context *ctx,
reply + GSP_RM_CTRL_HDR_SIZE, &out_params_size);
break;
+ case TU104_CTRL_FB_GET_INFO_V2:
+ gsp_rm_handle_fb_get_info_v2(ctx,
+ req_data + GSP_RM_CTRL_HDR_SIZE, req.paramsSize,
+ &out_status,
+ reply + GSP_RM_CTRL_HDR_SIZE, &out_params_size);
+ break;
+
+ case TU104_CTRL_BUS_GET_INFO_V2:
+ gsp_rm_handle_bus_get_info_v2(ctx,
+ req_data + GSP_RM_CTRL_HDR_SIZE, req.paramsSize,
+ &out_status,
+ reply + GSP_RM_CTRL_HDR_SIZE, &out_params_size);
+ break;
+
case TU104_CTRL_STATIC_KGR_GET_FLOORSWEEPING_MASKS:
gsp_rm_handle_kgr_get_floorsweeping_masks(ctx, sequence,
&out_status,
diff --git a/kernel/gsp_rm_tu104.h b/kernel/gsp_rm_tu104.h
index f40c77d..2edeeb4 100644
--- a/kernel/gsp_rm_tu104.h
+++ b/kernel/gsp_rm_tu104.h
@@ -780,6 +780,105 @@ struct tu104_gr_get_context_buffers_info_params {
engineContextBuffersInfo[TU104_CTX_GPU_MAX_ENGINES];
};
+/* FB_GET_INFO_V2 (ctrl2080fb.h): the client sends a list of requested FB info
+ * indices; the physical RM must echo each index back with its data.
+ * _fbGetFbInfos() asserts pFbInfos[i].index == returned index, so a zeroed
+ * reply fires kern_mem_sys_ctrl.c:911 asserts and leaves all FB info zero
+ * (CUDA/UVMLite read total/usable memory, BAR1 size, ECC/LTC info from here). */
+#define TU104_CTRL_FB_GET_INFO_V2 0x20801303
+#define TU104_FB_INFO_MAX_LIST_SIZE 0x80
+
+struct tu104_fb_info_entry {
+ u32 index;
+ u32 data;
+};
+
+struct tu104_fb_get_info_v2_params {
+ u32 fbInfoListSize;
+ struct tu104_fb_info_entry fbInfoList[TU104_FB_INFO_MAX_LIST_SIZE];
+};
+
+/* NV2080_CTRL_FB_INFO_INDEX_* (subset we populate) */
+#define TU104_FB_INFO_IDX_TILE_REGION_COUNT 0x00
+#define TU104_FB_INFO_IDX_COMPRESSION_SIZE 0x01
+#define TU104_FB_INFO_IDX_PARTITION_COUNT 0x04
+#define TU104_FB_INFO_IDX_BAR1_SIZE 0x05
+#define TU104_FB_INFO_IDX_RAM_SIZE 0x07
+#define TU104_FB_INFO_IDX_TOTAL_RAM_SIZE 0x08
+#define TU104_FB_INFO_IDX_HEAP_SIZE 0x09
+#define TU104_FB_INFO_IDX_MAPPABLE_HEAP_SIZE 0x0a
+#define TU104_FB_INFO_IDX_BUS_WIDTH 0x0b
+#define TU104_FB_INFO_IDX_RAM_TYPE 0x0d
+#define TU104_FB_INFO_IDX_BANK_COUNT 0x0e
+#define TU104_FB_INFO_IDX_FB_TAX_SIZE_KB 0x10
+#define TU104_FB_INFO_IDX_LARGEST_FREE_REGION_SIZE_KB 0x12
+#define TU104_FB_INFO_IDX_PARTITION_MASK 0x14
+#define TU104_FB_INFO_IDX_HEAP_FREE 0x16
+#define TU104_FB_INFO_IDX_RAM_LOCATION 0x17
+#define TU104_FB_INFO_IDX_FBP_COUNT 0x19
+#define TU104_FB_INFO_IDX_FBP_MASK 0x1a
+#define TU104_FB_INFO_IDX_L2CACHE_SIZE 0x1b
+#define TU104_FB_INFO_IDX_MEMORYINFO_VENDOR_ID 0x1c
+#define TU104_FB_INFO_IDX_BAR1_AVAIL_SIZE 0x1d
+#define TU104_FB_INFO_IDX_BAR1_MAX_CONTIG_AVAIL_SIZE 0x1f
+#define TU104_FB_INFO_IDX_USABLE_RAM_SIZE 0x20
+#define TU104_FB_INFO_IDX_LTC_COUNT 0x22
+#define TU104_FB_INFO_IDX_LTS_COUNT 0x23
+#define TU104_FB_INFO_IDX_PSEUDO_CHANNEL_MODE 0x25
+#define TU104_FB_INFO_IDX_ALLOW_PAGE_RETIREMENT 0x2a
+#define TU104_FB_INFO_IDX_LTC_MASK 0x2b
+
+#define TU104_RAM_TYPE_GDDR6 0x11
+#define TU104_RAM_LOCATION_GPU_DEDICATED 0x00
+#define TU104_PSEUDO_CHANNEL_MODE_DISABLED 0x01
+#define TU104_MEMORYINFO_VENDOR_ID_SAMSUNG 0x01
+#define TU104_L2CACHE_SIZE 0x400000 /* TU104: 4MB */
+#define TU104_LTC_COUNT 8
+#define TU104_LTS_COUNT 16
+
+/* BUS_GET_INFO_V2 (ctrl2080bus.h) - same flat index/data list pattern as FB.
+ * UVM's gpuGetSysmemLinkInfo() reads SYSMEM_CONNECTION_TYPE (PCIE) and then
+ * PCIE_GPU_LINK_CAPS for the link bandwidth; a zeroed reply makes
+ * getPCIELinkRateMBps() fail with NV_ERR_INVALID_STATE. */
+#define TU104_CTRL_BUS_GET_INFO_V2 0x20801823
+#define TU104_BUS_INFO_MAX_LIST_SIZE 0x34
+
+struct tu104_bus_info_entry {
+ u32 index;
+ u32 data;
+};
+
+struct tu104_bus_get_info_v2_params {
+ u32 busInfoListSize;
+ struct tu104_bus_info_entry busInfoList[TU104_BUS_INFO_MAX_LIST_SIZE];
+};
+
+/* NV2080_CTRL_BUS_INFO_INDEX_* (subset we populate) */
+#define TU104_BUS_INFO_IDX_TYPE 0x00
+#define TU104_BUS_INFO_IDX_INTLINE 0x01
+#define TU104_BUS_INFO_IDX_PCIE_GPU_LINK_CAPS 0x03
+#define TU104_BUS_INFO_IDX_PCIE_ROOT_LINK_CAPS 0x04
+#define TU104_BUS_INFO_IDX_PCIE_UPSTREAM_LINK_CAPS 0x05
+#define TU104_BUS_INFO_IDX_PCIE_DOWNSTREAM_LINK_CAPS 0x06
+#define TU104_BUS_INFO_IDX_PCIE_GPU_LINK_CTRL_STATUS 0x07
+#define TU104_BUS_INFO_IDX_PCIE_ROOT_LINK_CTRL_STATUS 0x08
+#define TU104_BUS_INFO_IDX_PCIE_UPSTREAM_LINK_CTRL 0x09
+#define TU104_BUS_INFO_IDX_PCIE_DOWNSTREAM_LINK_CTRL 0x0a
+#define TU104_BUS_INFO_IDX_PCIE_GEN_INFO 0x2d
+#define TU104_BUS_INFO_IDX_SYSMEM_CONNECTION_TYPE 0x33
+
+#define TU104_BUS_INFO_TYPE_PCI_EXPRESS 0x03
+#define TU104_BUS_INFO_SYSMEM_CONN_PCIE 0x00
+/* PCIE link caps / GEN info layout: MAX_SPEED 3:0 (8000MBPS=3), MAX_WIDTH
+ * 9:4 (lanes), ASPM 11:10, GEN 15:12, CURR_LEVEL 19:16, GPU_GEN 23:20
+ * (all GENx = 2 for Gen3), SPEED_CHANGES 24. */
+#define TU104_PCIE_LINK_CAP_GEN3_X16 (0x3u | (16u << 4) | \
+ (2u << 12) | (2u << 16) | \
+ (2u << 20))
+/* LINK_CTRL_STATUS: ASPM 1:0 (0=disabled), LINK_SPEED 19:16,
+ * LINK_WIDTH 25:20 (LINK_WIDTH_X16=0x10). */
+#define TU104_PCIE_LINK_CTRL_STATUS_GEN3_X16 ((0x3u << 16) | (0x10u << 20))
+
/* NV0080_CTRL_GR_INFO entry + infoList indices (ctrl0080gr.h) */
struct tu104_gr_info_entry {
diff --git a/kernel/main.c b/kernel/main.c
index 0fea09e..209942b 100644
--- a/kernel/main.c
+++ b/kernel/main.c
@@ -1723,14 +1723,14 @@ static int nv_vgpu_config_space_init(struct nv_vgpu_device *vgpu, struct pci_dev
cfg[0x89] = 0x00;
cfg[0x8a] = 0x10; /* Device Status */
cfg[0x8b] = 0x00;
- cfg[0x8c] = 0x00; /* Link Capabilities */
- cfg[0x8d] = 0x00;
+ cfg[0x8c] = 0x03; /* Link Capabilities: MAX_SPEED 3:0 = 3 (8 GT/s) */
+ cfg[0x8d] = 0x01; /* ... MAX_WIDTH 9:4 = 0x10 (x16) -> 0x103 */
cfg[0x8e] = 0x00;
cfg[0x8f] = 0x00;
cfg[0x90] = 0x00; /* Link Control */
cfg[0x91] = 0x00;
- cfg[0x92] = 0x00; /* Link Status */
- cfg[0x93] = 0x00;
+ cfg[0x92] = 0x03; /* Link Status: speed 3:0 = 3 (Gen3) */
+ cfg[0x93] = 0x01; /* ... width 9:4 = 0x10 (x16) */
cfg[0x94] = 0x00; /* Slot Capabilities */
cfg[0x95] = 0x00;
cfg[0x96] = 0x00;