| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/02 16:12 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": [
"PCIE_TPH",
"VFIO_PCI",
"PCI_P2PDMA",
"DMA_SHARED_BUFFER"
],
"FocusSymbols": [
"vfio_pci_core_feature_tph",
"vfio_pci_core_feature_tph_resolve",
"vfio_pci_core_feature_tph_st",
"vfio_tph_config_write",
"vfio_tph_capability_adjust",
"vfio_pci_dma_buf_get_tph_by_fd",
"vfio_pci_dma_buf_get_pci_tph",
"pcie_tph_get_cpu_st_explicit",
"pcie_enable_tph_explicit",
"pcie_tph_supported",
"pcie_tph_dsm_supported"
],
"Reasoning": "The patch adds new VFIO device features for PCIe TLP Processing Hints (TPH), introducing new ioctls and configuration space handling. This is a functional change that exposes new APIs to userspace, which should be fuzzed.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/02 16:12 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 6bdebca29e4f167ae3be8ed959a6159930b7fd1e\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Thu Jul 2 16:12:03 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/drivers/pci/tph.c b/drivers/pci/tph.c\nindex 655ffd60e62f6a..fcdba135a4f32f 100644\n--- a/drivers/pci/tph.c\n+++ b/drivers/pci/tph.c\n@@ -165,11 +165,14 @@ static u8 get_st_modes(struct pci_dev *pdev)\n */\n u32 pcie_tph_get_st_table_loc(struct pci_dev *pdev)\n {\n-\tu32 reg;\n+\tu32 reg = 0;\n+\n+\tif (!pdev-\u003etph_cap)\n+\t\treturn PCI_TPH_LOC_NONE;\n \n \tpci_read_config_dword(pdev, pdev-\u003etph_cap + PCI_TPH_CAP, \u0026reg);\n \n-\treturn FIELD_GET(PCI_TPH_CAP_LOC_MASK, reg);\n+\treturn reg \u0026 PCI_TPH_CAP_LOC_MASK;\n }\n EXPORT_SYMBOL(pcie_tph_get_st_table_loc);\n \n@@ -184,9 +187,6 @@ u16 pcie_tph_get_st_table_size(struct pci_dev *pdev)\n \n \t/* Check ST table location first */\n \tloc = pcie_tph_get_st_table_loc(pdev);\n-\n-\t/* Convert loc to match with PCI_TPH_LOC_* defined in pci_regs.h */\n-\tloc = FIELD_PREP(PCI_TPH_CAP_LOC_MASK, loc);\n \tif (loc != PCI_TPH_LOC_CAP)\n \t\treturn 0;\n \n@@ -230,21 +230,8 @@ static int write_tag_to_st_table(struct pci_dev *pdev, int index, u16 tag)\n \treturn pci_write_config_word(pdev, offset, tag);\n }\n \n-/**\n- * pcie_tph_get_cpu_st() - Retrieve Steering Tag for a target memory associated\n- * with a specific CPU\n- * @pdev: PCI device\n- * @mem_type: target memory type (volatile or persistent RAM)\n- * @cpu: associated CPU id\n- * @tag: Steering Tag to be returned\n- *\n- * Return the Steering Tag for a target memory that is associated with a\n- * specific CPU as indicated by cpu.\n- *\n- * Return: 0 if success, otherwise negative value (-errno)\n- */\n-int pcie_tph_get_cpu_st(struct pci_dev *pdev, enum tph_mem_type mem_type,\n-\t\t\tunsigned int cpu, u16 *tag)\n+static int get_cpu_st(struct pci_dev *pdev, enum tph_mem_type mem_type,\n+\t\t u8 req_type, unsigned int cpu, u16 *tag)\n {\n #ifdef CONFIG_ACPI\n \tstruct pci_dev *rp;\n@@ -268,19 +255,64 @@ int pcie_tph_get_cpu_st(struct pci_dev *pdev, enum tph_mem_type mem_type,\n \t\treturn -EINVAL;\n \t}\n \n-\t*tag = tph_extract_tag(mem_type, pdev-\u003etph_req_type, \u0026info);\n+\t*tag = tph_extract_tag(mem_type, req_type, \u0026info);\n \n-\tpci_dbg(pdev, \"get steering tag: mem_type=%s, cpu=%d, tag=%#04x\\n\",\n+\tpci_dbg(pdev, \"get steering tag: mem_type=%s, req_type=%u, cpu=%d, tag=%#04x\\n\",\n \t\t(mem_type == TPH_MEM_TYPE_VM) ? \"volatile\" : \"persistent\",\n-\t\tcpu, *tag);\n+\t\treq_type, cpu, *tag);\n \n \treturn 0;\n #else\n \treturn -ENODEV;\n #endif\n }\n+\n+/**\n+ * pcie_tph_get_cpu_st() - Retrieve Steering Tag for a target memory associated\n+ * with a specific CPU\n+ * @pdev: PCI device\n+ * @mem_type: target memory type (volatile or persistent RAM)\n+ * @cpu: associated CPU id\n+ * @tag: Steering Tag to be returned\n+ *\n+ * Return the Steering Tag for a target memory that is associated with a\n+ * specific CPU as indicated by cpu.\n+ *\n+ * Return: 0 if success, otherwise negative value (-errno)\n+ */\n+int pcie_tph_get_cpu_st(struct pci_dev *pdev, enum tph_mem_type mem_type,\n+\t\t\tunsigned int cpu, u16 *tag)\n+{\n+\treturn get_cpu_st(pdev, mem_type, pdev-\u003etph_req_type, cpu, tag);\n+}\n EXPORT_SYMBOL(pcie_tph_get_cpu_st);\n \n+/**\n+ * pcie_tph_get_cpu_st_explicit - Get ST with explicit requester type\n+ * @pdev: PCI device\n+ * @mem_type: target memory type (volatile or persistent RAM)\n+ * @extended: true=EXT_TPH, false=standard TPH only\n+ * @cpu: associated CPU id\n+ * @tag: output steering tag pointer\n+ *\n+ * Unlike auto pcie_tph_get_cpu_st(), caller manually picks requester type.\n+ * Rejects EXT request if device lacks extended requester capability.\n+ *\n+ * Return: 0 on success, negative errno on failure.\n+ */\n+int pcie_tph_get_cpu_st_explicit(struct pci_dev *pdev,\n+\t\t\t\t enum tph_mem_type mem_type,\n+\t\t\t\t bool extended, unsigned int cpu, u16 *tag)\n+{\n+\tu8 req_type = extended ? PCI_TPH_REQ_EXT_TPH : PCI_TPH_REQ_TPH_ONLY;\n+\n+\tif (extended \u0026\u0026 !pdev-\u003etph_ext_support)\n+\t\treturn -EINVAL;\n+\n+\treturn get_cpu_st(pdev, mem_type, req_type, cpu, tag);\n+}\n+EXPORT_SYMBOL(pcie_tph_get_cpu_st_explicit);\n+\n /**\n * pcie_tph_set_st_entry() - Set Steering Tag in the ST table entry\n * @pdev: PCI device\n@@ -315,8 +347,6 @@ int pcie_tph_set_st_entry(struct pci_dev *pdev, unsigned int index, u16 tag)\n \tset_ctrl_reg_req_en(pdev, PCI_TPH_REQ_DISABLE);\n \n \tloc = pcie_tph_get_st_table_loc(pdev);\n-\t/* Convert loc to match with PCI_TPH_LOC_* */\n-\tloc = FIELD_PREP(PCI_TPH_CAP_LOC_MASK, loc);\n \n \tswitch (loc) {\n \tcase PCI_TPH_LOC_MSIX:\n@@ -365,27 +395,10 @@ void pcie_disable_tph(struct pci_dev *pdev)\n }\n EXPORT_SYMBOL(pcie_disable_tph);\n \n-/**\n- * pcie_enable_tph - Enable TPH support for device using a specific ST mode\n- * @pdev: PCI device\n- * @mode: ST mode to enable. Current supported modes include:\n- *\n- * - PCI_TPH_ST_NS_MODE: NO ST Mode\n- * - PCI_TPH_ST_IV_MODE: Interrupt Vector Mode\n- * - PCI_TPH_ST_DS_MODE: Device Specific Mode\n- *\n- * Check whether the mode is actually supported by the device before enabling\n- * and return an error if not. Additionally determine what types of requests,\n- * TPH or extended TPH, can be issued by the device based on its TPH requester\n- * capability and the Root Port's completer capability.\n- *\n- * Return: 0 on success, otherwise negative value (-errno)\n- */\n-int pcie_enable_tph(struct pci_dev *pdev, int mode)\n+static int enable_tph(struct pci_dev *pdev, int mode, u8 req_type)\n {\n \tu32 reg;\n \tu8 dev_modes;\n-\tu8 rp_req_type;\n \n \t/* Honor \"notph\" kernel parameter */\n \tif (pci_tph_disabled)\n@@ -403,26 +416,12 @@ int pcie_enable_tph(struct pci_dev *pdev, int mode)\n \tif (!((1 \u003c\u003c mode) \u0026 dev_modes))\n \t\treturn -EINVAL;\n \n-\tpdev-\u003etph_mode = mode;\n-\n-\t/* Get req_type supported by device and its Root Port */\n-\tpci_read_config_dword(pdev, pdev-\u003etph_cap + PCI_TPH_CAP, \u0026reg);\n-\tif (FIELD_GET(PCI_TPH_CAP_EXT_TPH, reg))\n-\t\tpdev-\u003etph_req_type = PCI_TPH_REQ_EXT_TPH;\n-\telse\n-\t\tpdev-\u003etph_req_type = PCI_TPH_REQ_TPH_ONLY;\n-\n-\t/* Check if the device is behind a Root Port */\n-\tif (pci_pcie_type(pdev) != PCI_EXP_TYPE_RC_END) {\n-\t\trp_req_type = get_rp_completer_type(pdev);\n-\n-\t\t/* Final req_type is the smallest value of two */\n-\t\tpdev-\u003etph_req_type = min(pdev-\u003etph_req_type, rp_req_type);\n-\t}\n-\n-\tif (pdev-\u003etph_req_type == PCI_TPH_REQ_DISABLE)\n+\tif (req_type == PCI_TPH_REQ_EXT_TPH \u0026\u0026 !pdev-\u003etph_ext_support)\n \t\treturn -EINVAL;\n \n+\tpdev-\u003etph_mode = mode;\n+\tpdev-\u003etph_req_type = req_type;\n+\n \t/* Write them into TPH control register */\n \tpci_read_config_dword(pdev, pdev-\u003etph_cap + PCI_TPH_CTRL, \u0026reg);\n \n@@ -435,8 +434,63 @@ int pcie_enable_tph(struct pci_dev *pdev, int mode)\n \n \treturn 0;\n }\n+\n+/**\n+ * pcie_enable_tph - Enable TPH support for device using a specific ST mode\n+ * @pdev: PCI device\n+ * @mode: ST mode to enable. Current supported modes include:\n+ *\n+ * - PCI_TPH_ST_NS_MODE: NO ST Mode\n+ * - PCI_TPH_ST_IV_MODE: Interrupt Vector Mode\n+ * - PCI_TPH_ST_DS_MODE: Device Specific Mode\n+ *\n+ * Check whether the mode is actually supported by the device before enabling\n+ * and return an error if not. Additionally determine what types of requests,\n+ * TPH or extended TPH, can be issued by the device based on its TPH requester\n+ * capability and the Root Port's completer capability.\n+ *\n+ * Return: 0 on success, otherwise negative value (-errno)\n+ */\n+int pcie_enable_tph(struct pci_dev *pdev, int mode)\n+{\n+\tu8 req_type = pdev-\u003etph_ext_support ? PCI_TPH_REQ_EXT_TPH :\n+\t\t\t\t\t PCI_TPH_REQ_TPH_ONLY;\n+\treturn enable_tph(pdev, mode, req_type);\n+}\n EXPORT_SYMBOL(pcie_enable_tph);\n \n+/**\n+ * pcie_enable_tph_explicit - Enable TPH with explicit requester selection\n+ * @pdev: PCI device to operate\n+ * @mode: ST table operating mode (NS/IV/DS)\n+ * @extended: true = EXT_TPH, false = standard TPH only\n+ *\n+ * Unlike auto-detecting pcie_enable_tph(), caller selects requester type\n+ * manually instead of hardware auto-selection. Rejects EXT_TPH request\n+ * if device lacks extended requester capability.\n+ *\n+ * Return: 0 on success, negative errno on failure.\n+ */\n+int pcie_enable_tph_explicit(struct pci_dev *pdev, int mode, bool extended)\n+{\n+\tu8 req_type = extended ? PCI_TPH_REQ_EXT_TPH : PCI_TPH_REQ_TPH_ONLY;\n+\n+\treturn enable_tph(pdev, mode, req_type);\n+}\n+EXPORT_SYMBOL(pcie_enable_tph_explicit);\n+\n+/**\n+ * pcie_tph_enabled_req_type - Return the device's enabled TPH requester type\n+ * @pdev: PCI device to query\n+ *\n+ * Return: PCI_TPH_REQ_DISABLE, PCI_TPH_REQ_TPH_ONLY or PCI_TPH_REQ_EXT_TPH.\n+ */\n+u8 pcie_tph_enabled_req_type(struct pci_dev *pdev)\n+{\n+\treturn pdev-\u003etph_req_type;\n+}\n+EXPORT_SYMBOL(pcie_tph_enabled_req_type);\n+\n void pci_restore_tph_state(struct pci_dev *pdev)\n {\n \tstruct pci_cap_saved_state *save_state;\n@@ -508,14 +562,84 @@ void pci_no_tph(void)\n \n void pci_tph_init(struct pci_dev *pdev)\n {\n+\tu8 tph_req_type, rp_req_type;\n \tint num_entries;\n \tu32 save_size;\n+\tu32 reg = 0;\n \n \tpdev-\u003etph_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_TPH);\n \tif (!pdev-\u003etph_cap)\n \t\treturn;\n \n+\t/* Get req_type supported by device and its Root Port */\n+\tpci_read_config_dword(pdev, pdev-\u003etph_cap + PCI_TPH_CAP, \u0026reg);\n+\tif (FIELD_GET(PCI_TPH_CAP_EXT_TPH, reg))\n+\t\ttph_req_type = PCI_TPH_REQ_EXT_TPH;\n+\telse\n+\t\ttph_req_type = PCI_TPH_REQ_TPH_ONLY;\n+\n+\t/* Check if the device is behind a Root Port */\n+\tif (pci_pcie_type(pdev) != PCI_EXP_TYPE_RC_END) {\n+\t\trp_req_type = get_rp_completer_type(pdev);\n+\t\t/* Final req_type is the smallest value of two */\n+\t\ttph_req_type = min(tph_req_type, rp_req_type);\n+\t}\n+\n+\tif (tph_req_type == PCI_TPH_REQ_DISABLE) {\n+\t\tpdev-\u003etph_cap = 0;\n+\t\treturn;\n+\t}\n+\n+\tpdev-\u003etph_ext_support = !!(tph_req_type == PCI_TPH_REQ_EXT_TPH);\n+\n \tnum_entries = pcie_tph_get_st_table_size(pdev);\n \tsave_size = sizeof(u32) + num_entries * sizeof(u16);\n \tpci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_TPH, save_size);\n }\n+\n+/**\n+ * pcie_tph_supported - Check TPH capability attribute\n+ * @pdev: PCI device to query\n+ * @want_ext: false - check TPH cap exists; true - check EXT_TPH support\n+ *\n+ * Return: true on matched condition, false otherwise\n+ */\n+bool pcie_tph_supported(struct pci_dev *pdev, bool want_ext)\n+{\n+\tif (!pdev-\u003etph_cap)\n+\t\treturn false;\n+\n+\tif (!want_ext)\n+\t\treturn true;\n+\n+\treturn pdev-\u003etph_ext_support;\n+}\n+EXPORT_SYMBOL(pcie_tph_supported);\n+\n+/**\n+ * pcie_tph_dsm_supported - Check if root port exposes TPH ST _DSM\n+ * @pdev: target PCI device\n+ *\n+ * Return true if root port of @pdev provides TPH ST DSM function,\n+ * false otherwise (ACPI disabled or DSM missing).\n+ */\n+bool pcie_tph_dsm_supported(struct pci_dev *pdev)\n+{\n+#ifdef CONFIG_ACPI\n+\tstruct pci_dev *rp = pcie_find_root_port(pdev);\n+\tacpi_handle handle;\n+\n+\tif (!rp || !rp-\u003ebus || !rp-\u003ebus-\u003ebridge)\n+\t\treturn false;\n+\n+\thandle = ACPI_HANDLE(rp-\u003ebus-\u003ebridge);\n+\tif (!handle)\n+\t\treturn false;\n+\n+\treturn acpi_check_dsm(handle, \u0026pci_acpi_dsm_guid, 7,\n+\t\t\t BIT(TPH_ST_DSM_FUNC_INDEX));\n+#else\n+\treturn false;\n+#endif\n+}\n+EXPORT_SYMBOL_GPL(pcie_tph_dsm_supported);\ndiff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c\nindex 830369ff878dbe..7ef82b4457225a 100644\n--- a/drivers/vfio/pci/vfio_pci.c\n+++ b/drivers/vfio/pci/vfio_pci.c\n@@ -60,6 +60,28 @@ static bool disable_denylist;\n module_param(disable_denylist, bool, 0444);\n MODULE_PARM_DESC(disable_denylist, \"Disable use of device denylist. Disabling the denylist allows binding to devices with known errata that may lead to exploitable stability or security issues when accessed by untrusted users.\");\n \n+static unsigned int tph_policy;\n+static int tph_set(const char *val, const struct kernel_param *kp)\n+{\n+\treturn param_set_uint_minmax(val, kp, VFIO_PCI_TPH_POLICY_NO_ST,\n+\t\t\t\t VFIO_PCI_TPH_POLICY_LITERAL);\n+}\n+static const struct kernel_param_ops tph_param_ops = {\n+\t.set = tph_set,\n+\t.get = param_get_uint,\n+};\n+module_param_cb(tph, \u0026tph_param_ops, \u0026tph_policy, 0644);\n+MODULE_PARM_DESC(tph,\n+\t\"Global TPH policy level (0=default No-ST):\\n\"\n+\t\"0 = No-ST mode: RESOLVE only returns PH for DMABUF source;\\n\"\n+\t\" ST programming unavailable\\n\"\n+\t\"1 = Add IV mode: RESOLVE only returns PH for DMABUF source;\\n\"\n+\t\" ST program supports NONE/DMABUF/CPU sources\\n\"\n+\t\"2 = Add DS mode: RESOLVE returns PH+ST for DMABUF/CPU sources;\\n\"\n+\t\" ST program supports NONE/DMABUF/CPU sources\\n\"\n+\t\"3 = Add LITERAL: RESOLVE returns PH+ST for DMABUF/CPU sources;\\n\"\n+\t\" ST program supports NONE/DMABUF/CPU/LITERAL sources\\n\");\n+\n static bool vfio_pci_dev_in_denylist(struct pci_dev *pdev)\n {\n \tswitch (pdev-\u003evendor) {\n@@ -142,6 +164,9 @@ static int vfio_pci_init_dev(struct vfio_device *core_vdev)\n #ifdef CONFIG_VFIO_PCI_VGA\n \tvdev-\u003edisable_vga = disable_vga;\n #endif\n+#ifdef CONFIG_PCIE_TPH\n+\tvdev-\u003etph_policy = tph_policy;\n+#endif\n \n \treturn vfio_pci_core_init_dev(core_vdev);\n }\ndiff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c\nindex a10ed733f0e3af..388dd6fed16b5f 100644\n--- a/drivers/vfio/pci/vfio_pci_config.c\n+++ b/drivers/vfio/pci/vfio_pci_config.c\n@@ -22,6 +22,7 @@\n \n #include \u003clinux/fs.h\u003e\n #include \u003clinux/pci.h\u003e\n+#include \u003clinux/pci-tph.h\u003e\n #include \u003clinux/uaccess.h\u003e\n #include \u003clinux/vfio.h\u003e\n #include \u003clinux/slab.h\u003e\n@@ -1085,6 +1086,127 @@ static int __init init_pci_ext_cap_pwr_perm(struct perm_bits *perm)\n \treturn 0;\n }\n \n+/* Permissions for TPH extended capability */\n+static int __init init_pci_ext_cap_tph_perm(struct perm_bits *perm)\n+{\n+\tint i;\n+\n+\tif (alloc_perm_bits(perm, pci_ext_cap_length[PCI_EXT_CAP_ID_TPH]))\n+\t\treturn -ENOMEM;\n+\n+\tp_setd(perm, 0, ALL_VIRT, NO_WRITE);\n+\tp_setd(perm, PCI_TPH_CAP, ALL_VIRT, NO_WRITE);\n+\n+\tp_setd(perm, PCI_TPH_CTRL, ALL_VIRT,\n+\t PCI_TPH_CTRL_MODE_SEL_MASK | PCI_TPH_CTRL_REQ_EN_MASK);\n+\n+\t/* Per PCI specification: There is an upper limit of 64 entries\n+\t * when the ST table is located in the TPH Requester Extended\n+\t * Capability structure.\n+\t * And the pci_ext_cap_length[PCI_EXT_CAP_ID_TPH] is 0xFF, so the\n+\t * following operation is fine.\n+\t */\n+\tfor (i = 0; i \u003c 64; i++)\n+\t\tp_setw(perm, PCI_TPH_BASE_SIZEOF + i * sizeof(u16),\n+\t\t (u16)ALL_VIRT, (u16)ALL_WRITE);\n+\n+\treturn 0;\n+}\n+\n+static void vfio_tph_capability_adjust(struct vfio_pci_core_device *vdev,\n+\t\t\t\t int pos)\n+{\n+\t__le32 *vptr = (__le32 *)\u0026vdev-\u003evconfig[pos + PCI_TPH_CAP];\n+\tstruct pci_dev *pdev = vdev-\u003epdev;\n+\tu32 val = le32_to_cpu(*vptr);\n+\tbool need_adjust = false;\n+\n+\tif (!pcie_tph_supported(pdev, true)) {\n+\t\t/* Remove extend TPH if root-port don't support */\n+\t\tval \u0026= ~PCI_TPH_CAP_EXT_TPH;\n+\t\tneed_adjust = true;\n+\t}\n+\n+\tif (vdev-\u003etph_policy == VFIO_PCI_TPH_POLICY_NO_ST) {\n+\t\t/* Report only No-ST mode supported */\n+\t\tval \u0026= ~(PCI_TPH_CAP_ST_IV | PCI_TPH_CAP_ST_DS |\n+\t\t\t PCI_TPH_CAP_LOC_MASK | PCI_TPH_CAP_ST_MASK);\n+\t\tneed_adjust = true;\n+\t} else if (vdev-\u003etph_policy == VFIO_PCI_TPH_POLICY_IV_ST) {\n+\t\t/* Report only No-ST and IV modes supported */\n+\t\tval \u0026= ~PCI_TPH_CAP_ST_DS;\n+\t\t/* Remove ST location and size if dev don't support IV mode */\n+\t\tif (!(val \u0026 PCI_TPH_CAP_ST_IV))\n+\t\t\tval \u0026= ~(PCI_TPH_CAP_LOC_MASK | PCI_TPH_CAP_ST_MASK);\n+\t\tneed_adjust = true;\n+\t}\n+\n+\tif (need_adjust)\n+\t\t*vptr = cpu_to_le32(val);\n+}\n+\n+static int vfio_find_cap_start(struct vfio_pci_core_device *vdev, int pos);\n+static int vfio_tph_config_write(struct vfio_pci_core_device *vdev, int pos,\n+\t\t\t\t int count, struct perm_bits *perm,\n+\t\t\t\t int offset, __le32 val)\n+{\n+\tu16 start = vfio_find_cap_start(vdev, pos);\n+\tstruct pci_dev *pdev = vdev-\u003epdev;\n+\tu32 org_ctrl, new_ctrl, cap;\n+\tu8 mode, req, org_req;\n+\t__le32 org_val = 0;\n+\tbool extended;\n+\tint ret;\n+\n+\tif (!vdev-\u003etph_permit)\n+\t\treturn count;\n+\n+\tdown_write(\u0026vdev-\u003ememory_lock);\n+\n+\torg_ctrl = le32_to_cpu(*(__le32 *)\u0026vdev-\u003evconfig[start + PCI_TPH_CTRL]);\n+\tvfio_default_config_read(vdev, pos, count, perm, offset, \u0026org_val);\n+\n+\tret = vfio_default_config_write(vdev, pos, count, perm, offset, val);\n+\tif (ret != count)\n+\t\tgoto out;\n+\n+\tnew_ctrl = le32_to_cpu(*(__le32 *)\u0026vdev-\u003evconfig[start + PCI_TPH_CTRL]);\n+\tif (new_ctrl == org_ctrl)\n+\t\tgoto out; /* Only care about changes in TPH_CTRL. */\n+\n+\tcap = le32_to_cpu(*(__le32 *)\u0026vdev-\u003evconfig[start + PCI_TPH_CAP]);\n+\tmode = FIELD_GET(PCI_TPH_CTRL_MODE_SEL_MASK, new_ctrl);\n+\treq = FIELD_GET(PCI_TPH_CTRL_REQ_EN_MASK, new_ctrl);\n+\tif (mode \u003e PCI_TPH_ST_DS_MODE || !(cap \u0026 (1u \u003c\u003c mode)) || req == 0x2 ||\n+\t\t(req == PCI_TPH_REQ_EXT_TPH \u0026\u0026 !(cap \u0026 PCI_TPH_CAP_EXT_TPH)))\n+\t\tgoto restore; /* Drop invalid or unsupported write value */\n+\n+\torg_req = FIELD_GET(PCI_TPH_CTRL_REQ_EN_MASK, org_ctrl);\n+\tif (req == org_req)\n+\t\tgoto out; /* Only care about requester enable */\n+\n+\tret = vfio_pci_set_power_state(vdev, PCI_D0);\n+\tif (ret)\n+\t\tgoto restore; /* Drop this write */\n+\n+\tif (req == PCI_TPH_REQ_TPH_ONLY || req == PCI_TPH_REQ_EXT_TPH) {\n+\t\textended = !!(req == PCI_TPH_REQ_EXT_TPH);\n+\t\tret = pcie_enable_tph_explicit(pdev, mode, extended);\n+\t\tif (ret)\n+\t\t\tgoto restore;\n+\t} else if (req == PCI_TPH_REQ_DISABLE) {\n+\t\tpcie_disable_tph(vdev-\u003epdev);\n+\t}\n+\n+\tgoto out;\n+\n+restore:\n+\tvfio_default_config_write(vdev, pos, count, perm, offset, org_val);\n+out:\n+\tup_write(\u0026vdev-\u003ememory_lock);\n+\treturn count;\n+}\n+\n /*\n * Initialize the shared permission tables\n */\n@@ -1100,6 +1222,7 @@ void vfio_pci_uninit_perm_bits(void)\n \n \tfree_perm_bits(\u0026ecap_perms[PCI_EXT_CAP_ID_ERR]);\n \tfree_perm_bits(\u0026ecap_perms[PCI_EXT_CAP_ID_PWR]);\n+\tfree_perm_bits(\u0026ecap_perms[PCI_EXT_CAP_ID_TPH]);\n }\n \n int __init vfio_pci_init_perm_bits(void)\n@@ -1120,6 +1243,8 @@ int __init vfio_pci_init_perm_bits(void)\n \t/* Extended capabilities */\n \tret |= init_pci_ext_cap_err_perm(\u0026ecap_perms[PCI_EXT_CAP_ID_ERR]);\n \tret |= init_pci_ext_cap_pwr_perm(\u0026ecap_perms[PCI_EXT_CAP_ID_PWR]);\n+\tret |= init_pci_ext_cap_tph_perm(\u0026ecap_perms[PCI_EXT_CAP_ID_TPH]);\n+\tecap_perms[PCI_EXT_CAP_ID_TPH].writefn = vfio_tph_config_write;\n \tecap_perms[PCI_EXT_CAP_ID_VNDR].writefn = vfio_raw_config_write;\n \tecap_perms[PCI_EXT_CAP_ID_DVSEC].writefn = vfio_raw_config_write;\n \n@@ -1450,6 +1575,8 @@ static int vfio_ext_cap_len(struct vfio_pci_core_device *vdev, u16 ecap, u16 epo\n \t\tbyte \u0026= PCI_DPA_CAP_SUBSTATE_MASK;\n \t\treturn PCI_DPA_BASE_SIZEOF + byte + 1;\n \tcase PCI_EXT_CAP_ID_TPH:\n+\t\tif (!pcie_tph_supported(pdev, false))\n+\t\t\treturn 0;\n \t\tret = pci_read_config_dword(pdev, epos + PCI_TPH_CAP, \u0026dword);\n \t\tif (ret)\n \t\t\treturn pcibios_err_to_errno(ret);\n@@ -1701,6 +1828,8 @@ static int vfio_ecap_init(struct vfio_pci_core_device *vdev)\n \t\tret = vfio_fill_vconfig_bytes(vdev, epos, len);\n \t\tif (ret)\n \t\t\treturn ret;\n+\t\tif (ecap == PCI_EXT_CAP_ID_TPH \u0026\u0026 !hidden)\n+\t\t\tvfio_tph_capability_adjust(vdev, epos);\n \n \t\t/*\n \t\t * If we're just using this capability to anchor the list,\ndiff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c\nindex 3f11a9624b9c03..9c0f3c7a1ef735 100644\n--- a/drivers/vfio/pci/vfio_pci_core.c\n+++ b/drivers/vfio/pci/vfio_pci_core.c\n@@ -30,6 +30,7 @@\n #include \u003clinux/sched/mm.h\u003e\n #include \u003clinux/iommufd.h\u003e\n #include \u003clinux/pci-p2pdma.h\u003e\n+#include \u003clinux/pci-tph.h\u003e\n #include \u003clinux/seq_file.h\u003e\n #if IS_ENABLED(CONFIG_EEH)\n #include \u003casm/eeh.h\u003e\n@@ -127,10 +128,19 @@ static int vfio_pci_core_debugfs_disable_idle_d3(struct seq_file *seq,\n \treturn 0;\n }\n \n+static int vfio_pci_core_debugfs_tph_policy(struct seq_file *seq,\n+\t\t\t\t\t void *data)\n+{\n+\tstruct vfio_pci_core_device *vdev = vfio_pci_core_debugfs_private(seq);\n+\n+\tseq_printf(seq, \"%u\\n\", vdev-\u003etph_policy);\n+\treturn 0;\n+}\n+\n /*\n- * disable_idle_d3 and nointxmask are writable module parameters latched\n- * per device at init, so a device's effective value can differ from the\n- * current parameter setting. Expose the per-device (read-only) values\n+ * disable_idle_d3, nointxmask and tph_policy are writable module parameters\n+ * latched per device at init, so a device's effective value can differ from\n+ * the current parameter setting. Expose the per-device (read-only) values\n * here for visibility; read-only parameters can't drift and are omitted.\n */\n static void vfio_pci_core_debugfs_init(struct vfio_pci_core_device *vdev)\n@@ -146,6 +156,8 @@ static void vfio_pci_core_debugfs_init(struct vfio_pci_core_device *vdev)\n \t\t\t\t vfio_pci_core_debugfs_nointxmask);\n \tdebugfs_create_devm_seqfile(dev, \"disable_idle_d3\", pci_dir,\n \t\t\t\t vfio_pci_core_debugfs_disable_idle_d3);\n+\tdebugfs_create_devm_seqfile(dev, \"tph_policy\", pci_dir,\n+\t\t\t\t vfio_pci_core_debugfs_tph_policy);\n }\n #else\n static inline void vfio_pci_core_debugfs_init(struct vfio_pci_core_device *vdev)\n@@ -610,6 +622,9 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)\n \t\tgoto out_disable_device;\n \n \tvdev-\u003ereset_works = !ret;\n+\tvdev-\u003etph_permit = false;\n+\t/* Reset TPH status on new user session */\n+\tpcie_disable_tph(vdev-\u003epdev);\n \tpci_save_state(pdev);\n \tvdev-\u003epci_saved_state = pci_store_saved_state(pdev);\n \tif (!vdev-\u003epci_saved_state)\n@@ -757,6 +772,9 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev)\n \n \tvfio_pci_zdev_close_device(vdev);\n \n+\t/* Reset TPH status on exit user session */\n+\tpcie_disable_tph(vdev-\u003epdev);\n+\n \t/*\n \t * If we have saved state, restore it. If we can reset the device,\n \t * even better. Resetting with current state seems better than\n@@ -1607,6 +1625,227 @@ static int vfio_pci_core_feature_token(struct vfio_pci_core_device *vdev,\n \treturn 0;\n }\n \n+static int vfio_pci_core_feature_tph(struct vfio_pci_core_device *vdev,\n+\t\t\t\t u32 flags,\n+\t\t\t\t struct vfio_device_feature_tph __user *arg,\n+\t\t\t\t size_t argsz)\n+{\n+\tstruct vfio_device_feature_tph tph = {0};\n+\tint ret;\n+\n+\tif (!pcie_tph_supported(vdev-\u003epdev, false))\n+\t\treturn -EOPNOTSUPP;\n+\n+\tret = vfio_check_feature(flags, argsz,\n+\t\t\tVFIO_DEVICE_FEATURE_GET | VFIO_DEVICE_FEATURE_SET,\n+\t\t\tsizeof(tph));\n+\tif (ret \u003c= 0)\n+\t\treturn ret;\n+\n+\tif (flags \u0026 VFIO_DEVICE_FEATURE_SET) {\n+\t\tvdev-\u003etph_permit = 1;\n+\t\treturn 0;\n+\t}\n+\n+\ttph.flags = VFIO_DEVICE_TPH_CAP_DMABUF;\n+\tif (vdev-\u003etph_policy != VFIO_PCI_TPH_POLICY_NO_ST \u0026\u0026\n+\t\tpcie_tph_dsm_supported(vdev-\u003epdev))\n+\t\ttph.flags |= VFIO_DEVICE_TPH_CAP_CPU;\n+\tif (vdev-\u003etph_policy == VFIO_PCI_TPH_POLICY_LITERAL)\n+\t\ttph.flags |= VFIO_DEVICE_TPH_CAP_LITERAL;\n+\n+\treturn copy_to_user(arg, \u0026tph, sizeof(tph)) ? -EFAULT : 0;\n+}\n+\n+static int vfio_pci_core_feature_tph_resolve(struct vfio_pci_core_device *vdev,\n+\t\t\tu32 flags,\n+\t\t\tstruct vfio_device_feature_tph_resolve __user *arg,\n+\t\t\tsize_t argsz)\n+{\n+\tconst u32 permit_src_mask[] = {\n+\t\t[VFIO_PCI_TPH_POLICY_NO_ST] = VFIO_DEVICE_TPH_SRC_DMABUF,\n+\t\t[VFIO_PCI_TPH_POLICY_IV_ST] = VFIO_DEVICE_TPH_SRC_DMABUF,\n+\t\t[VFIO_PCI_TPH_POLICY_DS_ST ... VFIO_PCI_TPH_POLICY_LITERAL] =\n+\t\t\t\t\tVFIO_DEVICE_TPH_SRC_DMABUF |\n+\t\t\t\t\tVFIO_DEVICE_TPH_SRC_CPU_VOLATILE |\n+\t\t\t\t\tVFIO_DEVICE_TPH_SRC_CPU_PERSISTENT,\n+\t};\n+\tu32 permit_flags = permit_src_mask[vdev-\u003etph_policy] |\n+\t\t\t VFIO_DEVICE_TPH_EXTENDED;\n+\tstruct vfio_device_feature_tph_resolve resolve = {0};\n+\tenum tph_mem_type mtype;\n+\tbool extended;\n+\tu32 src_bits;\n+\tu16 tag = 0;\n+\tu8 ph = 0;\n+\tint ret;\n+\n+\tif (!vdev-\u003etph_permit)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_GET,\n+\t\t\t\t sizeof(resolve));\n+\tif (ret \u003c= 0)\n+\t\treturn ret;\n+\n+\tif (copy_from_user(\u0026resolve, arg, sizeof(resolve)))\n+\t\treturn -EFAULT;\n+\n+\tsrc_bits = resolve.flags \u0026 VFIO_DEVICE_TPH_SRC_MASK;\n+\tif (!(resolve.flags \u0026 permit_flags) || !is_power_of_2(src_bits))\n+\t\treturn -EINVAL;\n+\textended = !!(resolve.flags \u0026 VFIO_DEVICE_TPH_EXTENDED);\n+\tif (extended \u0026\u0026 !pcie_tph_supported(vdev-\u003epdev, true))\n+\t\treturn -EINVAL;\n+\n+\tresolve.valid = 0;\n+\tresolve.ph = 0;\n+\tresolve.st = 0;\n+\n+\tif (src_bits \u0026 VFIO_DEVICE_TPH_SRC_DMABUF) {\n+\t\tret = vfio_pci_dma_buf_get_tph_by_fd(resolve.src, extended,\n+\t\t\t\t\t\t \u0026tag, \u0026ph);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t\tresolve.ph = ph;\n+\t\tresolve.valid = VFIO_DEVICE_TPH_VALID_PH;\n+\t\tif (vdev-\u003etph_policy \u003e= VFIO_PCI_TPH_POLICY_DS_ST) {\n+\t\t\tresolve.st = tag;\n+\t\t\tresolve.valid |= VFIO_DEVICE_TPH_VALID_ST;\n+\t\t}\n+\t\tgoto out;\n+\t}\n+\n+\tmtype = (src_bits \u0026 VFIO_DEVICE_TPH_SRC_CPU_VOLATILE) ?\n+\t\t\tTPH_MEM_TYPE_VM : TPH_MEM_TYPE_PM;\n+\tret = pcie_tph_get_cpu_st_explicit(vdev-\u003epdev, mtype, extended,\n+\t\t\t\t\t resolve.src, \u0026tag);\n+\tif (ret)\n+\t\treturn ret;\n+\tresolve.valid = VFIO_DEVICE_TPH_VALID_ST;\n+\tresolve.st = tag;\n+\n+out:\n+\treturn copy_to_user(arg, \u0026resolve, sizeof(resolve)) ? -EFAULT : 0;\n+}\n+\n+static u32 tph_calc_st_size(struct vfio_pci_core_device *vdev)\n+{\n+\tstruct pci_dev *pdev = vdev-\u003epdev;\n+\tu32 loc = pcie_tph_get_st_table_loc(pdev);\n+\tint ret;\n+\n+\tif (loc == PCI_TPH_LOC_CAP) {\n+\t\treturn pcie_tph_get_st_table_size(pdev);\n+\t} else if (loc == PCI_TPH_LOC_MSIX) {\n+\t\tret = pci_msix_vec_count(pdev);\n+\t\tif (ret \u003c 0)\n+\t\t\treturn 0;\n+\t\treturn ret;\n+\t} else {\n+\t\treturn 0;\n+\t}\n+}\n+\n+static int tph_get_st_tag(struct pci_dev *pdev, u32 src_bits, u32 src_hndl,\n+\t\t\t bool extended, u16 *tag)\n+{\n+\tint ret = 0;\n+\tu8 ph;\n+\n+\tif (src_bits \u0026 VFIO_DEVICE_TPH_SRC_NONE)\n+\t\t*tag = 0;\n+\telse if (src_bits \u0026 VFIO_DEVICE_TPH_SRC_DMABUF)\n+\t\tret = vfio_pci_dma_buf_get_tph_by_fd(src_hndl, extended,\n+\t\t\t\t\t\t tag, \u0026ph);\n+\telse if (src_bits \u0026 VFIO_DEVICE_TPH_SRC_CPU_VOLATILE)\n+\t\tret = pcie_tph_get_cpu_st_explicit(pdev, TPH_MEM_TYPE_VM,\n+\t\t\t\t\textended, src_hndl, tag);\n+\telse if (src_bits \u0026 VFIO_DEVICE_TPH_SRC_CPU_PERSISTENT)\n+\t\tret = pcie_tph_get_cpu_st_explicit(pdev, TPH_MEM_TYPE_PM,\n+\t\t\t\t\textended, src_hndl, tag);\n+\telse if (src_bits \u0026 VFIO_DEVICE_TPH_SRC_LITERAL)\n+\t\t*tag = src_hndl;\n+\n+\tif (ret != 0)\n+\t\t*tag = 0;\n+\n+\treturn ret;\n+}\n+\n+static int vfio_pci_core_feature_tph_st(struct vfio_pci_core_device *vdev,\n+\t\t\t\tu32 flags,\n+\t\t\t\tstruct vfio_device_feature_tph_st __user *arg,\n+\t\t\t\tsize_t argsz)\n+{\n+\tu32 permit_flags = VFIO_DEVICE_TPH_SRC_MASK | VFIO_DEVICE_TPH_EXTENDED;\n+\tstruct vfio_device_feature_tph_st tph_st = {0};\n+\tstruct pci_dev *pdev = vdev-\u003epdev;\n+\tu32 src_bits, st_size;\n+\tbool stop_on_zero_st;\n+\tu32 *src_hndl = NULL;\n+\tvoid __user *uptr;\n+\tbool extended;\n+\tint ret, i;\n+\tu16 tag;\n+\n+\tif (!vdev-\u003etph_permit || vdev-\u003etph_policy == VFIO_PCI_TPH_POLICY_NO_ST)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_SET,\n+\t\t\t\t sizeof(tph_st));\n+\tif (ret \u003c= 0)\n+\t\treturn ret;\n+\n+\tif (copy_from_user(\u0026tph_st, arg, sizeof(tph_st)))\n+\t\treturn -EFAULT;\n+\n+\tsrc_bits = tph_st.flags \u0026 VFIO_DEVICE_TPH_SRC_MASK;\n+\tif (vdev-\u003etph_policy != VFIO_PCI_TPH_POLICY_LITERAL)\n+\t\tpermit_flags \u0026= ~VFIO_DEVICE_TPH_SRC_LITERAL;\n+\tif (!(tph_st.flags \u0026 permit_flags) || !is_power_of_2(src_bits))\n+\t\treturn -EINVAL;\n+\textended = !!(tph_st.flags \u0026 VFIO_DEVICE_TPH_EXTENDED);\n+\tif (extended \u0026\u0026 !pcie_tph_supported(vdev-\u003epdev, true))\n+\t\treturn -EINVAL;\n+\n+\tst_size = tph_calc_st_size(vdev);\n+\tif (tph_st.start \u003e= st_size || tph_st.count \u003e st_size - tph_st.start ||\n+\t\ttph_st.count == 0)\n+\t\treturn -EINVAL;\n+\n+\tuptr = u64_to_user_ptr(tph_st.dests);\n+\tif (!(src_bits \u0026 VFIO_DEVICE_TPH_SRC_NONE)) {\n+\t\tsrc_hndl = memdup_array_user(uptr, tph_st.count, sizeof(u32));\n+\t\tif (IS_ERR(src_hndl))\n+\t\t\treturn PTR_ERR(src_hndl);\n+\t}\n+\n+\tdown_write(\u0026vdev-\u003ememory_lock);\n+\tret = vfio_pci_set_power_state(vdev, PCI_D0);\n+\tif (ret)\n+\t\tgoto out;\n+\n+\tstop_on_zero_st = !!(tph_st.flags \u0026 VFIO_DEVICE_TPH_REQUIRE_ST);\n+\tif (tph_st.flags \u0026 VFIO_DEVICE_TPH_SRC_NONE)\n+\t\tstop_on_zero_st = false;\n+\tfor (i = 0; i \u003c tph_st.count; i++) {\n+\t\tret = tph_get_st_tag(pdev, src_bits, src_hndl ? src_hndl[i] : 0,\n+\t\t\t\t extended, \u0026tag);\n+\t\tif (ret || (stop_on_zero_st \u0026\u0026 tag == 0))\n+\t\t\tbreak;\n+\t\tret = pcie_tph_set_st_entry(pdev, tph_st.start + i, tag);\n+\t\tif (ret)\n+\t\t\tbreak;\n+\t}\n+\tret = i;\n+\n+out:\n+\tup_write(\u0026vdev-\u003ememory_lock);\n+\tkfree(src_hndl);\n+\treturn ret;\n+}\n+\n int vfio_pci_core_ioctl_feature(struct vfio_device *device, u32 flags,\n \t\t\t\tvoid __user *arg, size_t argsz)\n {\n@@ -1625,6 +1864,13 @@ int vfio_pci_core_ioctl_feature(struct vfio_device *device, u32 flags,\n \t\treturn vfio_pci_core_feature_token(vdev, flags, arg, argsz);\n \tcase VFIO_DEVICE_FEATURE_DMA_BUF:\n \t\treturn vfio_pci_core_feature_dma_buf(vdev, flags, arg, argsz);\n+\tcase VFIO_DEVICE_FEATURE_TPH:\n+\t\treturn vfio_pci_core_feature_tph(vdev, flags, arg, argsz);\n+\tcase VFIO_DEVICE_FEATURE_TPH_RESOLVE:\n+\t\treturn vfio_pci_core_feature_tph_resolve(vdev, flags,\n+\t\t\t\t\t\t\t arg, argsz);\n+\tcase VFIO_DEVICE_FEATURE_TPH_ST:\n+\t\treturn vfio_pci_core_feature_tph_st(vdev, flags, arg, argsz);\n \tdefault:\n \t\treturn -ENOTTY;\n \t}\ndiff --git a/drivers/vfio/pci/vfio_pci_dmabuf.c b/drivers/vfio/pci/vfio_pci_dmabuf.c\nindex c16f460c01d681..d9c5e434e586f9 100644\n--- a/drivers/vfio/pci/vfio_pci_dmabuf.c\n+++ b/drivers/vfio/pci/vfio_pci_dmabuf.c\n@@ -19,7 +19,14 @@ struct vfio_pci_dma_buf {\n \tu32 nr_ranges;\n \tstruct kref kref;\n \tstruct completion comp;\n-\tu8 revoked : 1;\n+\n+\t/* Protected by dmabuf-\u003eresv. */\n+\tu8 revoked:1;\n+\tu8 tph_ph:2;\n+\tu8 tph_st_valid:1;\n+\tu8 tph_xst_valid:1;\n+\tu8 tph_st;\n+\tu16 tph_xst;\n };\n \n static int vfio_pci_dma_buf_attach(struct dma_buf *dmabuf,\n@@ -81,6 +88,26 @@ static void vfio_pci_dma_buf_unmap(struct dma_buf_attachment *attachment,\n \tkref_put(\u0026priv-\u003ekref, vfio_pci_dma_buf_done);\n }\n \n+static int vfio_pci_dma_buf_get_pci_tph(struct dma_buf *dmabuf, bool extended,\n+\t\t\t\t\tu16 *tag, u8 *ph)\n+{\n+\tstruct vfio_pci_dma_buf *priv = dmabuf-\u003epriv;\n+\n+\tdma_resv_assert_held(priv-\u003edmabuf-\u003eresv);\n+\n+\tif (extended) {\n+\t\tif (!priv-\u003etph_xst_valid)\n+\t\t\treturn -EOPNOTSUPP;\n+\t\t*tag = priv-\u003etph_xst;\n+\t} else {\n+\t\tif (!priv-\u003etph_st_valid)\n+\t\t\treturn -EOPNOTSUPP;\n+\t\t*tag = priv-\u003etph_st;\n+\t}\n+\t*ph = priv-\u003etph_ph;\n+\treturn 0;\n+}\n+\n static void vfio_pci_dma_buf_release(struct dma_buf *dmabuf)\n {\n \tstruct vfio_pci_dma_buf *priv = dmabuf-\u003epriv;\n@@ -106,6 +133,26 @@ static const struct dma_buf_ops vfio_pci_dmabuf_ops = {\n \t.release = vfio_pci_dma_buf_release,\n };\n \n+int vfio_pci_dma_buf_get_tph_by_fd(int fd, bool extended, u16 *tag, u8 *ph)\n+{\n+\tstruct dma_buf *dmabuf;\n+\tint ret = 0;\n+\n+\tdmabuf = dma_buf_get(fd);\n+\tif (IS_ERR(dmabuf))\n+\t\treturn PTR_ERR(dmabuf);\n+\n+\tif (dmabuf-\u003eops != \u0026vfio_pci_dmabuf_ops) {\n+\t\tret = -EINVAL;\n+\t\tgoto out;\n+\t}\n+\n+\tret = vfio_pci_dma_buf_get_pci_tph(dmabuf, extended, tag, ph);\n+out:\n+\tdma_buf_put(dmabuf);\n+\treturn ret;\n+}\n+\n /*\n * This is a temporary \"private interconnect\" between VFIO DMABUF and iommufd.\n * It allows the two co-operating drivers to exchange the physical address of\ndiff --git a/drivers/vfio/pci/vfio_pci_priv.h b/drivers/vfio/pci/vfio_pci_priv.h\nindex fca9d0dfac90f8..d7bc52f488c62f 100644\n--- a/drivers/vfio/pci/vfio_pci_priv.h\n+++ b/drivers/vfio/pci/vfio_pci_priv.h\n@@ -11,6 +11,13 @@\n /* Cap maximum number of ioeventfds per device (arbitrary) */\n #define VFIO_PCI_IOEVENTFD_MAX\t\t1000\n \n+enum vfio_pci_tph_policy {\n+\tVFIO_PCI_TPH_POLICY_NO_ST = 0,\n+\tVFIO_PCI_TPH_POLICY_IV_ST = 1,\n+\tVFIO_PCI_TPH_POLICY_DS_ST = 2,\n+\tVFIO_PCI_TPH_POLICY_LITERAL = 3,\n+};\n+\n struct vfio_pci_ioeventfd {\n \tstruct list_head\tnext;\n \tstruct vfio_pci_core_device\t*vdev;\n@@ -115,12 +122,18 @@ static inline bool vfio_pci_is_vga(struct pci_dev *pdev)\n }\n \n #ifdef CONFIG_VFIO_PCI_DMABUF\n+int vfio_pci_dma_buf_get_tph_by_fd(int fd, bool extended, u16 *tag, u8 *ph);\n int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,\n \t\t\t\t struct vfio_device_feature_dma_buf __user *arg,\n \t\t\t\t size_t argsz);\n void vfio_pci_dma_buf_cleanup(struct vfio_pci_core_device *vdev);\n void vfio_pci_dma_buf_move(struct vfio_pci_core_device *vdev, bool revoked);\n #else\n+static inline int vfio_pci_dma_buf_get_tph_by_fd(int fd, bool extended,\n+\t\t\t\t\t\t u16 *tag, u8 *ph)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n static inline int\n vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,\n \t\t\t struct vfio_device_feature_dma_buf __user *arg,\ndiff --git a/include/linux/pci-tph.h b/include/linux/pci-tph.h\nindex be68cd17f2f833..ac9692d740a60d 100644\n--- a/include/linux/pci-tph.h\n+++ b/include/linux/pci-tph.h\n@@ -8,6 +8,7 @@\n */\n #ifndef LINUX_PCI_TPH_H\n #define LINUX_PCI_TPH_H\n+#include \u003clinux/pci.h\u003e\n \n /*\n * According to the ECN for PCI Firmware Spec, Steering Tag can be different\n@@ -26,10 +27,17 @@ int pcie_tph_set_st_entry(struct pci_dev *pdev,\n int pcie_tph_get_cpu_st(struct pci_dev *dev,\n \t\t\tenum tph_mem_type mem_type,\n \t\t\tunsigned int cpu, u16 *tag);\n+int pcie_tph_get_cpu_st_explicit(struct pci_dev *pdev,\n+\t\t\t\t enum tph_mem_type mem_type,\n+\t\t\t\t bool extended, unsigned int cpu, u16 *tag);\n void pcie_disable_tph(struct pci_dev *pdev);\n int pcie_enable_tph(struct pci_dev *pdev, int mode);\n+int pcie_enable_tph_explicit(struct pci_dev *pdev, int mode, bool extended);\n+u8 pcie_tph_enabled_req_type(struct pci_dev *pdev);\n u16 pcie_tph_get_st_table_size(struct pci_dev *pdev);\n u32 pcie_tph_get_st_table_loc(struct pci_dev *pdev);\n+bool pcie_tph_supported(struct pci_dev *pdev, bool want_ext);\n+bool pcie_tph_dsm_supported(struct pci_dev *pdev);\n #else\n static inline int pcie_tph_set_st_entry(struct pci_dev *pdev,\n \t\t\t\t\tunsigned int index, u16 tag)\n@@ -38,9 +46,26 @@ static inline int pcie_tph_get_cpu_st(struct pci_dev *dev,\n \t\t\t\t enum tph_mem_type mem_type,\n \t\t\t\t unsigned int cpu, u16 *tag)\n { return -EINVAL; }\n+static inline int pcie_tph_get_cpu_st_explicit(struct pci_dev *pdev,\n+\t\t\t\tenum tph_mem_type mem_type,\n+\t\t\t\tbool extended, unsigned int cpu, u16 *tag)\n+{ return -EINVAL; }\n+static inline u8 pcie_tph_enabled_req_type(struct pci_dev *pdev)\n+{ return PCI_TPH_REQ_DISABLE; }\n static inline void pcie_disable_tph(struct pci_dev *pdev) { }\n static inline int pcie_enable_tph(struct pci_dev *pdev, int mode)\n { return -EINVAL; }\n+static inline int pcie_enable_tph_explicit(struct pci_dev *pdev, int mode,\n+\t\t\t\t\t bool extended)\n+{ return -EINVAL; }\n+static inline u16 pcie_tph_get_st_table_size(struct pci_dev *pdev)\n+{ return 0; }\n+static inline u32 pcie_tph_get_st_table_loc(struct pci_dev *pdev)\n+{ return PCI_TPH_LOC_NONE; }\n+static inline bool pcie_tph_supported(struct pci_dev *pdev, bool want_ext)\n+{ return false; }\n+static inline bool pcie_tph_dsm_supported(struct pci_dev *pdev)\n+{ return false; }\n #endif\n \n #endif /* LINUX_PCI_TPH_H */\ndiff --git a/include/linux/pci.h b/include/linux/pci.h\nindex 64b308b6e61c19..a99eeea53c814a 100644\n--- a/include/linux/pci.h\n+++ b/include/linux/pci.h\n@@ -472,7 +472,6 @@ struct pci_dev {\n \tunsigned int\tats_enabled:1;\t\t/* Address Translation Svc */\n \tunsigned int\tpasid_enabled:1;\t/* Process Address Space ID */\n \tunsigned int\tpri_enabled:1;\t\t/* Page Request Interface */\n-\tunsigned int\ttph_enabled:1;\t\t/* TLP Processing Hints */\n \tunsigned int\tfm_enabled:1;\t\t/* Flit Mode (segment captured) */\n \tunsigned int\tis_managed:1;\t\t/* Managed via devres */\n \tunsigned int\tis_msi_managed:1;\t/* MSI release via devres installed */\n@@ -588,7 +587,10 @@ struct pci_dev {\n \tu8 reset_methods[PCI_NUM_RESET_METHODS]; /* In priority order */\n \n #ifdef CONFIG_PCIE_TPH\n-\tu16\t\ttph_cap;\t/* TPH capability offset */\n+\tu16\t\ttph_cap:14;\t/* TPH capability offset */\n+\tu16\t\ttph_ext_support:1; /* Indicate whether Extended TPH\n+\t\t\t\t\t * requester is supported */\n+\tu16\t\ttph_enabled:1;\t/* Whether TPH is enabled */\n \tu8\t\ttph_mode;\t/* TPH mode */\n \tu8\t\ttph_req_type;\t/* TPH requester type */\n #endif\ndiff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h\nindex 9a1674c152aa29..9a6e82c5c00cd3 100644\n--- a/include/linux/vfio_pci_core.h\n+++ b/include/linux/vfio_pci_core.h\n@@ -149,6 +149,8 @@ struct vfio_pci_core_device {\n \tstruct notifier_block\tnb;\n \tstruct rw_semaphore\tmemory_lock;\n \tstruct list_head\tdmabufs;\n+\tu8\t\t\ttph_policy;\n+\tbool\t\t\ttph_permit;\n };\n \n enum vfio_pci_io_width {\ndiff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h\nindex 5de618a3a5eeee..ed04b6280cd69e 100644\n--- a/include/uapi/linux/vfio.h\n+++ b/include/uapi/linux/vfio.h\n@@ -1534,6 +1534,68 @@ struct vfio_device_feature_dma_buf {\n */\n #define VFIO_DEVICE_FEATURE_MIG_PRECOPY_INFOv2 12\n \n+/* PCIe TPH device feature definitions for VFIO_DEVICE_FEATURE ioctl */\n+#define VFIO_DEVICE_FEATURE_TPH\t\t13\n+#define VFIO_DEVICE_FEATURE_TPH_RESOLVE\t14\n+#define VFIO_DEVICE_FEATURE_TPH_ST\t15\n+\n+/*\n+ * VFIO_DEVICE_FEATURE_TPH - Control and query PCI TPH capabilities\n+ *\n+ * SET: Opt-in to TPH feature; flags must be zero.\n+ * GET: Return supported TPH capability bits in flags.\n+ */\n+struct vfio_device_feature_tph {\n+\t__u32 flags;\n+};\n+\n+/* Capability bits returned by GET on VFIO_DEVICE_FEATURE_TPH */\n+#define VFIO_DEVICE_TPH_CAP_DMABUF\t(1u \u003c\u003c 0) /* DMABUF sources resolvable */\n+#define VFIO_DEVICE_TPH_CAP_CPU\t\t(1u \u003c\u003c 1) /* CPU sources resolvable via _DSM */\n+#define VFIO_DEVICE_TPH_CAP_LITERAL\t(1u \u003c\u003c 2) /* LITERAL ST source supported */\n+\n+/*\n+ * VFIO_DEVICE_FEATURE_TPH_RESOLVE - Resolve PH/ST tag value from source\n+ * Note: GET only\n+ */\n+struct vfio_device_feature_tph_resolve {\n+\t__u32 flags;\t/* IN: source type + namespace modifiers */\n+\t__u32 src;\t/* IN: dma-buf fd or CPU ID based on flags */\n+\t__u8 valid;\t/* OUT: bitmap indicating valid output fields */\n+#define VFIO_DEVICE_TPH_VALID_PH (1u \u003c\u003c 0) /* ph holds valid processing hint */\n+#define VFIO_DEVICE_TPH_VALID_ST (1u \u003c\u003c 1) /* st holds valid steering tag */\n+\t__u8 ph;\t/* OUT: TPH processing hint */\n+\t__u16 st;\t/* OUT: raw steering tag value */\n+};\n+\n+/*\n+ * VFIO_DEVICE_FEATURE_TPH_ST - Batch program architected ST table entries\n+ * Return value: number of successfully written entries\n+ * Note: SET only\n+ */\n+struct vfio_device_feature_tph_st {\n+\t__u32 flags;\t\t/* IN: source type + namespace modifiers */\n+\t__u16 start;\t\t/* IN: first ST table index / MSI-X vector */\n+\t__u16 count;\t\t/* IN: number of contiguous entries to program */\n+\t__aligned_u64 dests;\t/* IN: user pointer to array of source u32 values */\n+};\n+\n+/* Common flags shared by TPH_RESOLVE and TPH_ST features */\n+#define VFIO_DEVICE_TPH_SRC_NONE (1u \u003c\u003c 0) /* TPH_ST only: set ST=0 */\n+#define VFIO_DEVICE_TPH_SRC_DMABUF\t (1u \u003c\u003c 1) /* src = dma-buf fd */\n+#define VFIO_DEVICE_TPH_SRC_CPU_VOLATILE (1u \u003c\u003c 2) /* src = volatile memory CPU ID */\n+#define VFIO_DEVICE_TPH_SRC_CPU_PERSISTENT (1u \u003c\u003c 3) /* src = persistent memory CPU ID */\n+#define VFIO_DEVICE_TPH_SRC_LITERAL (1u \u003c\u003c 4) /* TPH_ST only: src = raw literal ST */\n+#define VFIO_DEVICE_TPH_EXTENDED (1u \u003c\u003c 30) /* Use extended 16-bit ST namespace */\n+#define VFIO_DEVICE_TPH_REQUIRE_ST (1u \u003c\u003c 31) /* TPH_ST only: fail if resolved ST=0 */\n+\n+/* Mask for mutually exclusive source type bits, only one may be set */\n+#define VFIO_DEVICE_TPH_SRC_MASK\t(VFIO_DEVICE_TPH_SRC_NONE | \\\n+\t\t\t\t\t VFIO_DEVICE_TPH_SRC_DMABUF | \\\n+\t\t\t\t\t VFIO_DEVICE_TPH_SRC_CPU_VOLATILE | \\\n+\t\t\t\t\t VFIO_DEVICE_TPH_SRC_CPU_PERSISTENT | \\\n+\t\t\t\t\t VFIO_DEVICE_TPH_SRC_LITERAL)\n+\n /* -------- API for Type1 VFIO IOMMU -------- */\n \n /**\n"
} |
| 2/1 | 2026/07/02 16:12 | agent | patch-evaluator |
1mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": [
"PCIE_TPH",
"VFIO_PCI",
"PCI_P2PDMA",
"DMA_SHARED_BUFFER"
],
"FocusSymbols": [
"vfio_pci_core_feature_tph",
"vfio_pci_core_feature_tph_resolve",
"vfio_pci_core_feature_tph_st",
"vfio_tph_config_write",
"vfio_tph_capability_adjust",
"vfio_pci_dma_buf_get_tph_by_fd",
"vfio_pci_dma_buf_get_pci_tph",
"pcie_tph_get_cpu_st_explicit",
"pcie_enable_tph_explicit",
"pcie_tph_supported",
"pcie_tph_dsm_supported"
],
"Reasoning": "The patch adds new VFIO device features for PCIe TLP Processing Hints (TPH), introducing new ioctls and configuration space handling. This is a functional change that exposes new APIs to userspace, which should be fuzzed.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit 6bdebca29e4f167ae3be8ed959a6159930b7fd1e
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Jul 2 16:12:03 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/pci/tph.c b/drivers/pci/tph.c
index 655ffd60e62f6a..fcdba135a4f32f 100644
--- a/drivers/pci/tph.c
+++ b/drivers/pci/tph.c
@@ -165,11 +165,14 @@ static u8 get_st_modes(struct pci_dev *pdev)
*/
u32 pcie_tph_get_st_table_loc(struct pci_dev *pdev)
{
- u32 reg;
+ u32 reg = 0;
+
+ if (!pdev->tph_cap)
+ return PCI_TPH_LOC_NONE;
pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CAP, ®);
- return FIELD_GET(PCI_TPH_CAP_LOC_MASK, reg);
+ return reg & PCI_TPH_CAP_LOC_MASK;
}
EXPORT_SYMBOL(pcie_tph_get_st_table_loc);
@@ -184,9 +187,6 @@ u16 pcie_tph_get_st_table_size(struct pci_dev *pdev)
/* Check ST table location first */
loc = pcie_tph_get_st_table_loc(pdev);
-
- /* Convert loc to match with PCI_TPH_LOC_* defined in pci_regs.h */
- loc = FIELD_PREP(PCI_TPH_CAP_LOC_MASK, loc);
if (loc != PCI_TPH_LOC_CAP)
return 0;
@@ -230,21 +230,8 @@ static int write_tag_to_st_table(struct pci_dev *pdev, int index, u16 tag)
return pci_write_config_word(pdev, offset, tag);
}
-/**
- * pcie_tph_get_cpu_st() - Retrieve Steering Tag for a target memory associated
- * with a specific CPU
- * @pdev: PCI device
- * @mem_type: target memory type (volatile or persistent RAM)
- * @cpu: associated CPU id
- * @tag: Steering Tag to be returned
- *
- * Return the Steering Tag for a target memory that is associated with a
- * specific CPU as indicated by cpu.
- *
- * Return: 0 if success, otherwise negative value (-errno)
- */
-int pcie_tph_get_cpu_st(struct pci_dev *pdev, enum tph_mem_type mem_type,
- unsigned int cpu, u16 *tag)
+static int get_cpu_st(struct pci_dev *pdev, enum tph_mem_type mem_type,
+ u8 req_type, unsigned int cpu, u16 *tag)
{
#ifdef CONFIG_ACPI
struct pci_dev *rp;
@@ -268,19 +255,64 @@ int pcie_tph_get_cpu_st(struct pci_dev *pdev, enum tph_mem_type mem_type,
return -EINVAL;
}
- *tag = tph_extract_tag(mem_type, pdev->tph_req_type, &info);
+ *tag = tph_extract_tag(mem_type, req_type, &info);
- pci_dbg(pdev, "get steering tag: mem_type=%s, cpu=%d, tag=%#04x\n",
+ pci_dbg(pdev, "get steering tag: mem_type=%s, req_type=%u, cpu=%d, tag=%#04x\n",
(mem_type == TPH_MEM_TYPE_VM) ? "volatile" : "persistent",
- cpu, *tag);
+ req_type, cpu, *tag);
return 0;
#else
return -ENODEV;
#endif
}
+
+/**
+ * pcie_tph_get_cpu_st() - Retrieve Steering Tag for a target memory associated
+ * with a specific CPU
+ * @pdev: PCI device
+ * @mem_type: target memory type (volatile or persistent RAM)
+ * @cpu: associated CPU id
+ * @tag: Steering Tag to be returned
+ *
+ * Return the Steering Tag for a target memory that is associated with a
+ * specific CPU as indicated by cpu.
+ *
+ * Return: 0 if success, otherwise negative value (-errno)
+ */
+int pcie_tph_get_cpu_st(struct pci_dev *pdev, enum tph_mem_type mem_type,
+ unsigned int cpu, u16 *tag)
+{
+ return get_cpu_st(pdev, mem_type, pdev->tph_req_type, cpu, tag);
+}
EXPORT_SYMBOL(pcie_tph_get_cpu_st);
+/**
+ * pcie_tph_get_cpu_st_explicit - Get ST with explicit requester type
+ * @pdev: PCI device
+ * @mem_type: target memory type (volatile or persistent RAM)
+ * @extended: true=EXT_TPH, false=standard TPH only
+ * @cpu: associated CPU id
+ * @tag: output steering tag pointer
+ *
+ * Unlike auto pcie_tph_get_cpu_st(), caller manually picks requester type.
+ * Rejects EXT request if device lacks extended requester capability.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int pcie_tph_get_cpu_st_explicit(struct pci_dev *pdev,
+ enum tph_mem_type mem_type,
+ bool extended, unsigned int cpu, u16 *tag)
+{
+ u8 req_type = extended ? PCI_TPH_REQ_EXT_TPH : PCI_TPH_REQ_TPH_ONLY;
+
+ if (extended && !pdev->tph_ext_support)
+ return -EINVAL;
+
+ return get_cpu_st(pdev, mem_type, req_type, cpu, tag);
+}
+EXPORT_SYMBOL(pcie_tph_get_cpu_st_explicit);
+
/**
* pcie_tph_set_st_entry() - Set Steering Tag in the ST table entry
* @pdev: PCI device
@@ -315,8 +347,6 @@ int pcie_tph_set_st_entry(struct pci_dev *pdev, unsigned int index, u16 tag)
set_ctrl_reg_req_en(pdev, PCI_TPH_REQ_DISABLE);
loc = pcie_tph_get_st_table_loc(pdev);
- /* Convert loc to match with PCI_TPH_LOC_* */
- loc = FIELD_PREP(PCI_TPH_CAP_LOC_MASK, loc);
switch (loc) {
case PCI_TPH_LOC_MSIX:
@@ -365,27 +395,10 @@ void pcie_disable_tph(struct pci_dev *pdev)
}
EXPORT_SYMBOL(pcie_disable_tph);
-/**
- * pcie_enable_tph - Enable TPH support for device using a specific ST mode
- * @pdev: PCI device
- * @mode: ST mode to enable. Current supported modes include:
- *
- * - PCI_TPH_ST_NS_MODE: NO ST Mode
- * - PCI_TPH_ST_IV_MODE: Interrupt Vector Mode
- * - PCI_TPH_ST_DS_MODE: Device Specific Mode
- *
- * Check whether the mode is actually supported by the device before enabling
- * and return an error if not. Additionally determine what types of requests,
- * TPH or extended TPH, can be issued by the device based on its TPH requester
- * capability and the Root Port's completer capability.
- *
- * Return: 0 on success, otherwise negative value (-errno)
- */
-int pcie_enable_tph(struct pci_dev *pdev, int mode)
+static int enable_tph(struct pci_dev *pdev, int mode, u8 req_type)
{
u32 reg;
u8 dev_modes;
- u8 rp_req_type;
/* Honor "notph" kernel parameter */
if (pci_tph_disabled)
@@ -403,26 +416,12 @@ int pcie_enable_tph(struct pci_dev *pdev, int mode)
if (!((1 << mode) & dev_modes))
return -EINVAL;
- pdev->tph_mode = mode;
-
- /* Get req_type supported by device and its Root Port */
- pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CAP, ®);
- if (FIELD_GET(PCI_TPH_CAP_EXT_TPH, reg))
- pdev->tph_req_type = PCI_TPH_REQ_EXT_TPH;
- else
- pdev->tph_req_type = PCI_TPH_REQ_TPH_ONLY;
-
- /* Check if the device is behind a Root Port */
- if (pci_pcie_type(pdev) != PCI_EXP_TYPE_RC_END) {
- rp_req_type = get_rp_completer_type(pdev);
-
- /* Final req_type is the smallest value of two */
- pdev->tph_req_type = min(pdev->tph_req_type, rp_req_type);
- }
-
- if (pdev->tph_req_type == PCI_TPH_REQ_DISABLE)
+ if (req_type == PCI_TPH_REQ_EXT_TPH && !pdev->tph_ext_support)
return -EINVAL;
+ pdev->tph_mode = mode;
+ pdev->tph_req_type = req_type;
+
/* Write them into TPH control register */
pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, ®);
@@ -435,8 +434,63 @@ int pcie_enable_tph(struct pci_dev *pdev, int mode)
return 0;
}
+
+/**
+ * pcie_enable_tph - Enable TPH support for device using a specific ST mode
+ * @pdev: PCI device
+ * @mode: ST mode to enable. Current supported modes include:
+ *
+ * - PCI_TPH_ST_NS_MODE: NO ST Mode
+ * - PCI_TPH_ST_IV_MODE: Interrupt Vector Mode
+ * - PCI_TPH_ST_DS_MODE: Device Specific Mode
+ *
+ * Check whether the mode is actually supported by the device before enabling
+ * and return an error if not. Additionally determine what types of requests,
+ * TPH or extended TPH, can be issued by the device based on its TPH requester
+ * capability and the Root Port's completer capability.
+ *
+ * Return: 0 on success, otherwise negative value (-errno)
+ */
+int pcie_enable_tph(struct pci_dev *pdev, int mode)
+{
+ u8 req_type = pdev->tph_ext_support ? PCI_TPH_REQ_EXT_TPH :
+ PCI_TPH_REQ_TPH_ONLY;
+ return enable_tph(pdev, mode, req_type);
+}
EXPORT_SYMBOL(pcie_enable_tph);
+/**
+ * pcie_enable_tph_explicit - Enable TPH with explicit requester selection
+ * @pdev: PCI device to operate
+ * @mode: ST table operating mode (NS/IV/DS)
+ * @extended: true = EXT_TPH, false = standard TPH only
+ *
+ * Unlike auto-detecting pcie_enable_tph(), caller selects requester type
+ * manually instead of hardware auto-selection. Rejects EXT_TPH request
+ * if device lacks extended requester capability.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int pcie_enable_tph_explicit(struct pci_dev *pdev, int mode, bool extended)
+{
+ u8 req_type = extended ? PCI_TPH_REQ_EXT_TPH : PCI_TPH_REQ_TPH_ONLY;
+
+ return enable_tph(pdev, mode, req_type);
+}
+EXPORT_SYMBOL(pcie_enable_tph_explicit);
+
+/**
+ * pcie_tph_enabled_req_type - Return the device's enabled TPH requester type
+ * @pdev: PCI device to query
+ *
+ * Return: PCI_TPH_REQ_DISABLE, PCI_TPH_REQ_TPH_ONLY or PCI_TPH_REQ_EXT_TPH.
+ */
+u8 pcie_tph_enabled_req_type(struct pci_dev *pdev)
+{
+ return pdev->tph_req_type;
+}
+EXPORT_SYMBOL(pcie_tph_enabled_req_type);
+
void pci_restore_tph_state(struct pci_dev *pdev)
{
struct pci_cap_saved_state *save_state;
@@ -508,14 +562,84 @@ void pci_no_tph(void)
void pci_tph_init(struct pci_dev *pdev)
{
+ u8 tph_req_type, rp_req_type;
int num_entries;
u32 save_size;
+ u32 reg = 0;
pdev->tph_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_TPH);
if (!pdev->tph_cap)
return;
+ /* Get req_type supported by device and its Root Port */
+ pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CAP, ®);
+ if (FIELD_GET(PCI_TPH_CAP_EXT_TPH, reg))
+ tph_req_type = PCI_TPH_REQ_EXT_TPH;
+ else
+ tph_req_type = PCI_TPH_REQ_TPH_ONLY;
+
+ /* Check if the device is behind a Root Port */
+ if (pci_pcie_type(pdev) != PCI_EXP_TYPE_RC_END) {
+ rp_req_type = get_rp_completer_type(pdev);
+ /* Final req_type is the smallest value of two */
+ tph_req_type = min(tph_req_type, rp_req_type);
+ }
+
+ if (tph_req_type == PCI_TPH_REQ_DISABLE) {
+ pdev->tph_cap = 0;
+ return;
+ }
+
+ pdev->tph_ext_support = !!(tph_req_type == PCI_TPH_REQ_EXT_TPH);
+
num_entries = pcie_tph_get_st_table_size(pdev);
save_size = sizeof(u32) + num_entries * sizeof(u16);
pci_add_ext_cap_save_buffer(pdev, PCI_EXT_CAP_ID_TPH, save_size);
}
+
+/**
+ * pcie_tph_supported - Check TPH capability attribute
+ * @pdev: PCI device to query
+ * @want_ext: false - check TPH cap exists; true - check EXT_TPH support
+ *
+ * Return: true on matched condition, false otherwise
+ */
+bool pcie_tph_supported(struct pci_dev *pdev, bool want_ext)
+{
+ if (!pdev->tph_cap)
+ return false;
+
+ if (!want_ext)
+ return true;
+
+ return pdev->tph_ext_support;
+}
+EXPORT_SYMBOL(pcie_tph_supported);
+
+/**
+ * pcie_tph_dsm_supported - Check if root port exposes TPH ST _DSM
+ * @pdev: target PCI device
+ *
+ * Return true if root port of @pdev provides TPH ST DSM function,
+ * false otherwise (ACPI disabled or DSM missing).
+ */
+bool pcie_tph_dsm_supported(struct pci_dev *pdev)
+{
+#ifdef CONFIG_ACPI
+ struct pci_dev *rp = pcie_find_root_port(pdev);
+ acpi_handle handle;
+
+ if (!rp || !rp->bus || !rp->bus->bridge)
+ return false;
+
+ handle = ACPI_HANDLE(rp->bus->bridge);
+ if (!handle)
+ return false;
+
+ return acpi_check_dsm(handle, &pci_acpi_dsm_guid, 7,
+ BIT(TPH_ST_DSM_FUNC_INDEX));
+#else
+ return false;
+#endif
+}
+EXPORT_SYMBOL_GPL(pcie_tph_dsm_supported);
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 830369ff878dbe..7ef82b4457225a 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -60,6 +60,28 @@ static bool disable_denylist;
module_param(disable_denylist, bool, 0444);
MODULE_PARM_DESC(disable_denylist, "Disable use of device denylist. Disabling the denylist allows binding to devices with known errata that may lead to exploitable stability or security issues when accessed by untrusted users.");
+static unsigned int tph_policy;
+static int tph_set(const char *val, const struct kernel_param *kp)
+{
+ return param_set_uint_minmax(val, kp, VFIO_PCI_TPH_POLICY_NO_ST,
+ VFIO_PCI_TPH_POLICY_LITERAL);
+}
+static const struct kernel_param_ops tph_param_ops = {
+ .set = tph_set,
+ .get = param_get_uint,
+};
+module_param_cb(tph, &tph_param_ops, &tph_policy, 0644);
+MODULE_PARM_DESC(tph,
+ "Global TPH policy level (0=default No-ST):\n"
+ "0 = No-ST mode: RESOLVE only returns PH for DMABUF source;\n"
+ " ST programming unavailable\n"
+ "1 = Add IV mode: RESOLVE only returns PH for DMABUF source;\n"
+ " ST program supports NONE/DMABUF/CPU sources\n"
+ "2 = Add DS mode: RESOLVE returns PH+ST for DMABUF/CPU sources;\n"
+ " ST program supports NONE/DMABUF/CPU sources\n"
+ "3 = Add LITERAL: RESOLVE returns PH+ST for DMABUF/CPU sources;\n"
+ " ST program supports NONE/DMABUF/CPU/LITERAL sources\n");
+
static bool vfio_pci_dev_in_denylist(struct pci_dev *pdev)
{
switch (pdev->vendor) {
@@ -142,6 +164,9 @@ static int vfio_pci_init_dev(struct vfio_device *core_vdev)
#ifdef CONFIG_VFIO_PCI_VGA
vdev->disable_vga = disable_vga;
#endif
+#ifdef CONFIG_PCIE_TPH
+ vdev->tph_policy = tph_policy;
+#endif
return vfio_pci_core_init_dev(core_vdev);
}
diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
index a10ed733f0e3af..388dd6fed16b5f 100644
--- a/drivers/vfio/pci/vfio_pci_config.c
+++ b/drivers/vfio/pci/vfio_pci_config.c
@@ -22,6 +22,7 @@
#include <linux/fs.h>
#include <linux/pci.h>
+#include <linux/pci-tph.h>
#include <linux/uaccess.h>
#include <linux/vfio.h>
#include <linux/slab.h>
@@ -1085,6 +1086,127 @@ static int __init init_pci_ext_cap_pwr_perm(struct perm_bits *perm)
return 0;
}
+/* Permissions for TPH extended capability */
+static int __init init_pci_ext_cap_tph_perm(struct perm_bits *perm)
+{
+ int i;
+
+ if (alloc_perm_bits(perm, pci_ext_cap_length[PCI_EXT_CAP_ID_TPH]))
+ return -ENOMEM;
+
+ p_setd(perm, 0, ALL_VIRT, NO_WRITE);
+ p_setd(perm, PCI_TPH_CAP, ALL_VIRT, NO_WRITE);
+
+ p_setd(perm, PCI_TPH_CTRL, ALL_VIRT,
+ PCI_TPH_CTRL_MODE_SEL_MASK | PCI_TPH_CTRL_REQ_EN_MASK);
+
+ /* Per PCI specification: There is an upper limit of 64 entries
+ * when the ST table is located in the TPH Requester Extended
+ * Capability structure.
+ * And the pci_ext_cap_length[PCI_EXT_CAP_ID_TPH] is 0xFF, so the
+ * following operation is fine.
+ */
+ for (i = 0; i < 64; i++)
+ p_setw(perm, PCI_TPH_BASE_SIZEOF + i * sizeof(u16),
+ (u16)ALL_VIRT, (u16)ALL_WRITE);
+
+ return 0;
+}
+
+static void vfio_tph_capability_adjust(struct vfio_pci_core_device *vdev,
+ int pos)
+{
+ __le32 *vptr = (__le32 *)&vdev->vconfig[pos + PCI_TPH_CAP];
+ struct pci_dev *pdev = vdev->pdev;
+ u32 val = le32_to_cpu(*vptr);
+ bool need_adjust = false;
+
+ if (!pcie_tph_supported(pdev, true)) {
+ /* Remove extend TPH if root-port don't support */
+ val &= ~PCI_TPH_CAP_EXT_TPH;
+ need_adjust = true;
+ }
+
+ if (vdev->tph_policy == VFIO_PCI_TPH_POLICY_NO_ST) {
+ /* Report only No-ST mode supported */
+ val &= ~(PCI_TPH_CAP_ST_IV | PCI_TPH_CAP_ST_DS |
+ PCI_TPH_CAP_LOC_MASK | PCI_TPH_CAP_ST_MASK);
+ need_adjust = true;
+ } else if (vdev->tph_policy == VFIO_PCI_TPH_POLICY_IV_ST) {
+ /* Report only No-ST and IV modes supported */
+ val &= ~PCI_TPH_CAP_ST_DS;
+ /* Remove ST location and size if dev don't support IV mode */
+ if (!(val & PCI_TPH_CAP_ST_IV))
+ val &= ~(PCI_TPH_CAP_LOC_MASK | PCI_TPH_CAP_ST_MASK);
+ need_adjust = true;
+ }
+
+ if (need_adjust)
+ *vptr = cpu_to_le32(val);
+}
+
+static int vfio_find_cap_start(struct vfio_pci_core_device *vdev, int pos);
+static int vfio_tph_config_write(struct vfio_pci_core_device *vdev, int pos,
+ int count, struct perm_bits *perm,
+ int offset, __le32 val)
+{
+ u16 start = vfio_find_cap_start(vdev, pos);
+ struct pci_dev *pdev = vdev->pdev;
+ u32 org_ctrl, new_ctrl, cap;
+ u8 mode, req, org_req;
+ __le32 org_val = 0;
+ bool extended;
+ int ret;
+
+ if (!vdev->tph_permit)
+ return count;
+
+ down_write(&vdev->memory_lock);
+
+ org_ctrl = le32_to_cpu(*(__le32 *)&vdev->vconfig[start + PCI_TPH_CTRL]);
+ vfio_default_config_read(vdev, pos, count, perm, offset, &org_val);
+
+ ret = vfio_default_config_write(vdev, pos, count, perm, offset, val);
+ if (ret != count)
+ goto out;
+
+ new_ctrl = le32_to_cpu(*(__le32 *)&vdev->vconfig[start + PCI_TPH_CTRL]);
+ if (new_ctrl == org_ctrl)
+ goto out; /* Only care about changes in TPH_CTRL. */
+
+ cap = le32_to_cpu(*(__le32 *)&vdev->vconfig[start + PCI_TPH_CAP]);
+ mode = FIELD_GET(PCI_TPH_CTRL_MODE_SEL_MASK, new_ctrl);
+ req = FIELD_GET(PCI_TPH_CTRL_REQ_EN_MASK, new_ctrl);
+ if (mode > PCI_TPH_ST_DS_MODE || !(cap & (1u << mode)) || req == 0x2 ||
+ (req == PCI_TPH_REQ_EXT_TPH && !(cap & PCI_TPH_CAP_EXT_TPH)))
+ goto restore; /* Drop invalid or unsupported write value */
+
+ org_req = FIELD_GET(PCI_TPH_CTRL_REQ_EN_MASK, org_ctrl);
+ if (req == org_req)
+ goto out; /* Only care about requester enable */
+
+ ret = vfio_pci_set_power_state(vdev, PCI_D0);
+ if (ret)
+ goto restore; /* Drop this write */
+
+ if (req == PCI_TPH_REQ_TPH_ONLY || req == PCI_TPH_REQ_EXT_TPH) {
+ extended = !!(req == PCI_TPH_REQ_EXT_TPH);
+ ret = pcie_enable_tph_explicit(pdev, mode, extended);
+ if (ret)
+ goto restore;
+ } else if (req == PCI_TPH_REQ_DISABLE) {
+ pcie_disable_tph(vdev->pdev);
+ }
+
+ goto out;
+
+restore:
+ vfio_default_config_write(vdev, pos, count, perm, offset, org_val);
+out:
+ up_write(&vdev->memory_lock);
+ return count;
+}
+
/*
* Initialize the shared permission tables
*/
@@ -1100,6 +1222,7 @@ void vfio_pci_uninit_perm_bits(void)
free_perm_bits(&ecap_perms[PCI_EXT_CAP_ID_ERR]);
free_perm_bits(&ecap_perms[PCI_EXT_CAP_ID_PWR]);
+ free_perm_bits(&ecap_perms[PCI_EXT_CAP_ID_TPH]);
}
int __init vfio_pci_init_perm_bits(void)
@@ -1120,6 +1243,8 @@ int __init vfio_pci_init_perm_bits(void)
/* Extended capabilities */
ret |= init_pci_ext_cap_err_perm(&ecap_perms[PCI_EXT_CAP_ID_ERR]);
ret |= init_pci_ext_cap_pwr_perm(&ecap_perms[PCI_EXT_CAP_ID_PWR]);
+ ret |= init_pci_ext_cap_tph_perm(&ecap_perms[PCI_EXT_CAP_ID_TPH]);
+ ecap_perms[PCI_EXT_CAP_ID_TPH].writefn = vfio_tph_config_write;
ecap_perms[PCI_EXT_CAP_ID_VNDR].writefn = vfio_raw_config_write;
ecap_perms[PCI_EXT_CAP_ID_DVSEC].writefn = vfio_raw_config_write;
@@ -1450,6 +1575,8 @@ static int vfio_ext_cap_len(struct vfio_pci_core_device *vdev, u16 ecap, u16 epo
byte &= PCI_DPA_CAP_SUBSTATE_MASK;
return PCI_DPA_BASE_SIZEOF + byte + 1;
case PCI_EXT_CAP_ID_TPH:
+ if (!pcie_tph_supported(pdev, false))
+ return 0;
ret = pci_read_config_dword(pdev, epos + PCI_TPH_CAP, &dword);
if (ret)
return pcibios_err_to_errno(ret);
@@ -1701,6 +1828,8 @@ static int vfio_ecap_init(struct vfio_pci_core_device *vdev)
ret = vfio_fill_vconfig_bytes(vdev, epos, len);
if (ret)
return ret;
+ if (ecap == PCI_EXT_CAP_ID_TPH && !hidden)
+ vfio_tph_capability_adjust(vdev, epos);
/*
* If we're just using this capability to anchor the list,
diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c
index 3f11a9624b9c03..9c0f3c7a1ef735 100644
--- a/drivers/vfio/pci/vfio_pci_core.c
+++ b/drivers/vfio/pci/vfio_pci_core.c
@@ -30,6 +30,7 @@
#include <linux/sched/mm.h>
#include <linux/iommufd.h>
#include <linux/pci-p2pdma.h>
+#include <linux/pci-tph.h>
#include <linux/seq_file.h>
#if IS_ENABLED(CONFIG_EEH)
#include <asm/eeh.h>
@@ -127,10 +128,19 @@ static int vfio_pci_core_debugfs_disable_idle_d3(struct seq_file *seq,
return 0;
}
+static int vfio_pci_core_debugfs_tph_policy(struct seq_file *seq,
+ void *data)
+{
+ struct vfio_pci_core_device *vdev = vfio_pci_core_debugfs_private(seq);
+
+ seq_printf(seq, "%u\n", vdev->tph_policy);
+ return 0;
+}
+
/*
- * disable_idle_d3 and nointxmask are writable module parameters latched
- * per device at init, so a device's effective value can differ from the
- * current parameter setting. Expose the per-device (read-only) values
+ * disable_idle_d3, nointxmask and tph_policy are writable module parameters
+ * latched per device at init, so a device's effective value can differ from
+ * the current parameter setting. Expose the per-device (read-only) values
* here for visibility; read-only parameters can't drift and are omitted.
*/
static void vfio_pci_core_debugfs_init(struct vfio_pci_core_device *vdev)
@@ -146,6 +156,8 @@ static void vfio_pci_core_debugfs_init(struct vfio_pci_core_device *vdev)
vfio_pci_core_debugfs_nointxmask);
debugfs_create_devm_seqfile(dev, "disable_idle_d3", pci_dir,
vfio_pci_core_debugfs_disable_idle_d3);
+ debugfs_create_devm_seqfile(dev, "tph_policy", pci_dir,
+ vfio_pci_core_debugfs_tph_policy);
}
#else
static inline void vfio_pci_core_debugfs_init(struct vfio_pci_core_device *vdev)
@@ -610,6 +622,9 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
goto out_disable_device;
vdev->reset_works = !ret;
+ vdev->tph_permit = false;
+ /* Reset TPH status on new user session */
+ pcie_disable_tph(vdev->pdev);
pci_save_state(pdev);
vdev->pci_saved_state = pci_store_saved_state(pdev);
if (!vdev->pci_saved_state)
@@ -757,6 +772,9 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev)
vfio_pci_zdev_close_device(vdev);
+ /* Reset TPH status on exit user session */
+ pcie_disable_tph(vdev->pdev);
+
/*
* If we have saved state, restore it. If we can reset the device,
* even better. Resetting with current state seems better than
@@ -1607,6 +1625,227 @@ static int vfio_pci_core_feature_token(struct vfio_pci_core_device *vdev,
return 0;
}
+static int vfio_pci_core_feature_tph(struct vfio_pci_core_device *vdev,
+ u32 flags,
+ struct vfio_device_feature_tph __user *arg,
+ size_t argsz)
+{
+ struct vfio_device_feature_tph tph = {0};
+ int ret;
+
+ if (!pcie_tph_supported(vdev->pdev, false))
+ return -EOPNOTSUPP;
+
+ ret = vfio_check_feature(flags, argsz,
+ VFIO_DEVICE_FEATURE_GET | VFIO_DEVICE_FEATURE_SET,
+ sizeof(tph));
+ if (ret <= 0)
+ return ret;
+
+ if (flags & VFIO_DEVICE_FEATURE_SET) {
+ vdev->tph_permit = 1;
+ return 0;
+ }
+
+ tph.flags = VFIO_DEVICE_TPH_CAP_DMABUF;
+ if (vdev->tph_policy != VFIO_PCI_TPH_POLICY_NO_ST &&
+ pcie_tph_dsm_supported(vdev->pdev))
+ tph.flags |= VFIO_DEVICE_TPH_CAP_CPU;
+ if (vdev->tph_policy == VFIO_PCI_TPH_POLICY_LITERAL)
+ tph.flags |= VFIO_DEVICE_TPH_CAP_LITERAL;
+
+ return copy_to_user(arg, &tph, sizeof(tph)) ? -EFAULT : 0;
+}
+
+static int vfio_pci_core_feature_tph_resolve(struct vfio_pci_core_device *vdev,
+ u32 flags,
+ struct vfio_device_feature_tph_resolve __user *arg,
+ size_t argsz)
+{
+ const u32 permit_src_mask[] = {
+ [VFIO_PCI_TPH_POLICY_NO_ST] = VFIO_DEVICE_TPH_SRC_DMABUF,
+ [VFIO_PCI_TPH_POLICY_IV_ST] = VFIO_DEVICE_TPH_SRC_DMABUF,
+ [VFIO_PCI_TPH_POLICY_DS_ST ... VFIO_PCI_TPH_POLICY_LITERAL] =
+ VFIO_DEVICE_TPH_SRC_DMABUF |
+ VFIO_DEVICE_TPH_SRC_CPU_VOLATILE |
+ VFIO_DEVICE_TPH_SRC_CPU_PERSISTENT,
+ };
+ u32 permit_flags = permit_src_mask[vdev->tph_policy] |
+ VFIO_DEVICE_TPH_EXTENDED;
+ struct vfio_device_feature_tph_resolve resolve = {0};
+ enum tph_mem_type mtype;
+ bool extended;
+ u32 src_bits;
+ u16 tag = 0;
+ u8 ph = 0;
+ int ret;
+
+ if (!vdev->tph_permit)
+ return -EOPNOTSUPP;
+
+ ret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_GET,
+ sizeof(resolve));
+ if (ret <= 0)
+ return ret;
+
+ if (copy_from_user(&resolve, arg, sizeof(resolve)))
+ return -EFAULT;
+
+ src_bits = resolve.flags & VFIO_DEVICE_TPH_SRC_MASK;
+ if (!(resolve.flags & permit_flags) || !is_power_of_2(src_bits))
+ return -EINVAL;
+ extended = !!(resolve.flags & VFIO_DEVICE_TPH_EXTENDED);
+ if (extended && !pcie_tph_supported(vdev->pdev, true))
+ return -EINVAL;
+
+ resolve.valid = 0;
+ resolve.ph = 0;
+ resolve.st = 0;
+
+ if (src_bits & VFIO_DEVICE_TPH_SRC_DMABUF) {
+ ret = vfio_pci_dma_buf_get_tph_by_fd(resolve.src, extended,
+ &tag, &ph);
+ if (ret)
+ return ret;
+ resolve.ph = ph;
+ resolve.valid = VFIO_DEVICE_TPH_VALID_PH;
+ if (vdev->tph_policy >= VFIO_PCI_TPH_POLICY_DS_ST) {
+ resolve.st = tag;
+ resolve.valid |= VFIO_DEVICE_TPH_VALID_ST;
+ }
+ goto out;
+ }
+
+ mtype = (src_bits & VFIO_DEVICE_TPH_SRC_CPU_VOLATILE) ?
+ TPH_MEM_TYPE_VM : TPH_MEM_TYPE_PM;
+ ret = pcie_tph_get_cpu_st_explicit(vdev->pdev, mtype, extended,
+ resolve.src, &tag);
+ if (ret)
+ return ret;
+ resolve.valid = VFIO_DEVICE_TPH_VALID_ST;
+ resolve.st = tag;
+
+out:
+ return copy_to_user(arg, &resolve, sizeof(resolve)) ? -EFAULT : 0;
+}
+
+static u32 tph_calc_st_size(struct vfio_pci_core_device *vdev)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ u32 loc = pcie_tph_get_st_table_loc(pdev);
+ int ret;
+
+ if (loc == PCI_TPH_LOC_CAP) {
+ return pcie_tph_get_st_table_size(pdev);
+ } else if (loc == PCI_TPH_LOC_MSIX) {
+ ret = pci_msix_vec_count(pdev);
+ if (ret < 0)
+ return 0;
+ return ret;
+ } else {
+ return 0;
+ }
+}
+
+static int tph_get_st_tag(struct pci_dev *pdev, u32 src_bits, u32 src_hndl,
+ bool extended, u16 *tag)
+{
+ int ret = 0;
+ u8 ph;
+
+ if (src_bits & VFIO_DEVICE_TPH_SRC_NONE)
+ *tag = 0;
+ else if (src_bits & VFIO_DEVICE_TPH_SRC_DMABUF)
+ ret = vfio_pci_dma_buf_get_tph_by_fd(src_hndl, extended,
+ tag, &ph);
+ else if (src_bits & VFIO_DEVICE_TPH_SRC_CPU_VOLATILE)
+ ret = pcie_tph_get_cpu_st_explicit(pdev, TPH_MEM_TYPE_VM,
+ extended, src_hndl, tag);
+ else if (src_bits & VFIO_DEVICE_TPH_SRC_CPU_PERSISTENT)
+ ret = pcie_tph_get_cpu_st_explicit(pdev, TPH_MEM_TYPE_PM,
+ extended, src_hndl, tag);
+ else if (src_bits & VFIO_DEVICE_TPH_SRC_LITERAL)
+ *tag = src_hndl;
+
+ if (ret != 0)
+ *tag = 0;
+
+ return ret;
+}
+
+static int vfio_pci_core_feature_tph_st(struct vfio_pci_core_device *vdev,
+ u32 flags,
+ struct vfio_device_feature_tph_st __user *arg,
+ size_t argsz)
+{
+ u32 permit_flags = VFIO_DEVICE_TPH_SRC_MASK | VFIO_DEVICE_TPH_EXTENDED;
+ struct vfio_device_feature_tph_st tph_st = {0};
+ struct pci_dev *pdev = vdev->pdev;
+ u32 src_bits, st_size;
+ bool stop_on_zero_st;
+ u32 *src_hndl = NULL;
+ void __user *uptr;
+ bool extended;
+ int ret, i;
+ u16 tag;
+
+ if (!vdev->tph_permit || vdev->tph_policy == VFIO_PCI_TPH_POLICY_NO_ST)
+ return -EOPNOTSUPP;
+
+ ret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_SET,
+ sizeof(tph_st));
+ if (ret <= 0)
+ return ret;
+
+ if (copy_from_user(&tph_st, arg, sizeof(tph_st)))
+ return -EFAULT;
+
+ src_bits = tph_st.flags & VFIO_DEVICE_TPH_SRC_MASK;
+ if (vdev->tph_policy != VFIO_PCI_TPH_POLICY_LITERAL)
+ permit_flags &= ~VFIO_DEVICE_TPH_SRC_LITERAL;
+ if (!(tph_st.flags & permit_flags) || !is_power_of_2(src_bits))
+ return -EINVAL;
+ extended = !!(tph_st.flags & VFIO_DEVICE_TPH_EXTENDED);
+ if (extended && !pcie_tph_supported(vdev->pdev, true))
+ return -EINVAL;
+
+ st_size = tph_calc_st_size(vdev);
+ if (tph_st.start >= st_size || tph_st.count > st_size - tph_st.start ||
+ tph_st.count == 0)
+ return -EINVAL;
+
+ uptr = u64_to_user_ptr(tph_st.dests);
+ if (!(src_bits & VFIO_DEVICE_TPH_SRC_NONE)) {
+ src_hndl = memdup_array_user(uptr, tph_st.count, sizeof(u32));
+ if (IS_ERR(src_hndl))
+ return PTR_ERR(src_hndl);
+ }
+
+ down_write(&vdev->memory_lock);
+ ret = vfio_pci_set_power_state(vdev, PCI_D0);
+ if (ret)
+ goto out;
+
+ stop_on_zero_st = !!(tph_st.flags & VFIO_DEVICE_TPH_REQUIRE_ST);
+ if (tph_st.flags & VFIO_DEVICE_TPH_SRC_NONE)
+ stop_on_zero_st = false;
+ for (i = 0; i < tph_st.count; i++) {
+ ret = tph_get_st_tag(pdev, src_bits, src_hndl ? src_hndl[i] : 0,
+ extended, &tag);
+ if (ret || (stop_on_zero_st && tag == 0))
+ break;
+ ret = pcie_tph_set_st_entry(pdev, tph_st.start + i, tag);
+ if (ret)
+ break;
+ }
+ ret = i;
+
+out:
+ up_write(&vdev->memory_lock);
+ kfree(src_hndl);
+ return ret;
+}
+
int vfio_pci_core_ioctl_feature(struct vfio_device *device, u32 flags,
void __user *arg, size_t argsz)
{
@@ -1625,6 +1864,13 @@ int vfio_pci_core_ioctl_feature(struct vfio_device *device, u32 flags,
return vfio_pci_core_feature_token(vdev, flags, arg, argsz);
case VFIO_DEVICE_FEATURE_DMA_BUF:
return vfio_pci_core_feature_dma_buf(vdev, flags, arg, argsz);
+ case VFIO_DEVICE_FEATURE_TPH:
+ return vfio_pci_core_feature_tph(vdev, flags, arg, argsz);
+ case VFIO_DEVICE_FEATURE_TPH_RESOLVE:
+ return vfio_pci_core_feature_tph_resolve(vdev, flags,
+ arg, argsz);
+ case VFIO_DEVICE_FEATURE_TPH_ST:
+ return vfio_pci_core_feature_tph_st(vdev, flags, arg, argsz);
default:
return -ENOTTY;
}
diff --git a/drivers/vfio/pci/vfio_pci_dmabuf.c b/drivers/vfio/pci/vfio_pci_dmabuf.c
index c16f460c01d681..d9c5e434e586f9 100644
--- a/drivers/vfio/pci/vfio_pci_dmabuf.c
+++ b/drivers/vfio/pci/vfio_pci_dmabuf.c
@@ -19,7 +19,14 @@ struct vfio_pci_dma_buf {
u32 nr_ranges;
struct kref kref;
struct completion comp;
- u8 revoked : 1;
+
+ /* Protected by dmabuf->resv. */
+ u8 revoked:1;
+ u8 tph_ph:2;
+ u8 tph_st_valid:1;
+ u8 tph_xst_valid:1;
+ u8 tph_st;
+ u16 tph_xst;
};
static int vfio_pci_dma_buf_attach(struct dma_buf *dmabuf,
@@ -81,6 +88,26 @@ static void vfio_pci_dma_buf_unmap(struct dma_buf_attachment *attachment,
kref_put(&priv->kref, vfio_pci_dma_buf_done);
}
+static int vfio_pci_dma_buf_get_pci_tph(struct dma_buf *dmabuf, bool extended,
+ u16 *tag, u8 *ph)
+{
+ struct vfio_pci_dma_buf *priv = dmabuf->priv;
+
+ dma_resv_assert_held(priv->dmabuf->resv);
+
+ if (extended) {
+ if (!priv->tph_xst_valid)
+ return -EOPNOTSUPP;
+ *tag = priv->tph_xst;
+ } else {
+ if (!priv->tph_st_valid)
+ return -EOPNOTSUPP;
+ *tag = priv->tph_st;
+ }
+ *ph = priv->tph_ph;
+ return 0;
+}
+
static void vfio_pci_dma_buf_release(struct dma_buf *dmabuf)
{
struct vfio_pci_dma_buf *priv = dmabuf->priv;
@@ -106,6 +133,26 @@ static const struct dma_buf_ops vfio_pci_dmabuf_ops = {
.release = vfio_pci_dma_buf_release,
};
+int vfio_pci_dma_buf_get_tph_by_fd(int fd, bool extended, u16 *tag, u8 *ph)
+{
+ struct dma_buf *dmabuf;
+ int ret = 0;
+
+ dmabuf = dma_buf_get(fd);
+ if (IS_ERR(dmabuf))
+ return PTR_ERR(dmabuf);
+
+ if (dmabuf->ops != &vfio_pci_dmabuf_ops) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = vfio_pci_dma_buf_get_pci_tph(dmabuf, extended, tag, ph);
+out:
+ dma_buf_put(dmabuf);
+ return ret;
+}
+
/*
* This is a temporary "private interconnect" between VFIO DMABUF and iommufd.
* It allows the two co-operating drivers to exchange the physical address of
diff --git a/drivers/vfio/pci/vfio_pci_priv.h b/drivers/vfio/pci/vfio_pci_priv.h
index fca9d0dfac90f8..d7bc52f488c62f 100644
--- a/drivers/vfio/pci/vfio_pci_priv.h
+++ b/drivers/vfio/pci/vfio_pci_priv.h
@@ -11,6 +11,13 @@
/* Cap maximum number of ioeventfds per device (arbitrary) */
#define VFIO_PCI_IOEVENTFD_MAX 1000
+enum vfio_pci_tph_policy {
+ VFIO_PCI_TPH_POLICY_NO_ST = 0,
+ VFIO_PCI_TPH_POLICY_IV_ST = 1,
+ VFIO_PCI_TPH_POLICY_DS_ST = 2,
+ VFIO_PCI_TPH_POLICY_LITERAL = 3,
+};
+
struct vfio_pci_ioeventfd {
struct list_head next;
struct vfio_pci_core_device *vdev;
@@ -115,12 +122,18 @@ static inline bool vfio_pci_is_vga(struct pci_dev *pdev)
}
#ifdef CONFIG_VFIO_PCI_DMABUF
+int vfio_pci_dma_buf_get_tph_by_fd(int fd, bool extended, u16 *tag, u8 *ph);
int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,
struct vfio_device_feature_dma_buf __user *arg,
size_t argsz);
void vfio_pci_dma_buf_cleanup(struct vfio_pci_core_device *vdev);
void vfio_pci_dma_buf_move(struct vfio_pci_core_device *vdev, bool revoked);
#else
+static inline int vfio_pci_dma_buf_get_tph_by_fd(int fd, bool extended,
+ u16 *tag, u8 *ph)
+{
+ return -EOPNOTSUPP;
+}
static inline int
vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,
struct vfio_device_feature_dma_buf __user *arg,
diff --git a/include/linux/pci-tph.h b/include/linux/pci-tph.h
index be68cd17f2f833..ac9692d740a60d 100644
--- a/include/linux/pci-tph.h
+++ b/include/linux/pci-tph.h
@@ -8,6 +8,7 @@
*/
#ifndef LINUX_PCI_TPH_H
#define LINUX_PCI_TPH_H
+#include <linux/pci.h>
/*
* According to the ECN for PCI Firmware Spec, Steering Tag can be different
@@ -26,10 +27,17 @@ int pcie_tph_set_st_entry(struct pci_dev *pdev,
int pcie_tph_get_cpu_st(struct pci_dev *dev,
enum tph_mem_type mem_type,
unsigned int cpu, u16 *tag);
+int pcie_tph_get_cpu_st_explicit(struct pci_dev *pdev,
+ enum tph_mem_type mem_type,
+ bool extended, unsigned int cpu, u16 *tag);
void pcie_disable_tph(struct pci_dev *pdev);
int pcie_enable_tph(struct pci_dev *pdev, int mode);
+int pcie_enable_tph_explicit(struct pci_dev *pdev, int mode, bool extended);
+u8 pcie_tph_enabled_req_type(struct pci_dev *pdev);
u16 pcie_tph_get_st_table_size(struct pci_dev *pdev);
u32 pcie_tph_get_st_table_loc(struct pci_dev *pdev);
+bool pcie_tph_supported(struct pci_dev *pdev, bool want_ext);
+bool pcie_tph_dsm_supported(struct pci_dev *pdev);
#else
static inline int pcie_tph_set_st_entry(struct pci_dev *pdev,
unsigned int index, u16 tag)
@@ -38,9 +46,26 @@ static inline int pcie_tph_get_cpu_st(struct pci_dev *dev,
enum tph_mem_type mem_type,
unsigned int cpu, u16 *tag)
{ return -EINVAL; }
+static inline int pcie_tph_get_cpu_st_explicit(struct pci_dev *pdev,
+ enum tph_mem_type mem_type,
+ bool extended, unsigned int cpu, u16 *tag)
+{ return -EINVAL; }
+static inline u8 pcie_tph_enabled_req_type(struct pci_dev *pdev)
+{ return PCI_TPH_REQ_DISABLE; }
static inline void pcie_disable_tph(struct pci_dev *pdev) { }
static inline int pcie_enable_tph(struct pci_dev *pdev, int mode)
{ return -EINVAL; }
+static inline int pcie_enable_tph_explicit(struct pci_dev *pdev, int mode,
+ bool extended)
+{ return -EINVAL; }
+static inline u16 pcie_tph_get_st_table_size(struct pci_dev *pdev)
+{ return 0; }
+static inline u32 pcie_tph_get_st_table_loc(struct pci_dev *pdev)
+{ return PCI_TPH_LOC_NONE; }
+static inline bool pcie_tph_supported(struct pci_dev *pdev, bool want_ext)
+{ return false; }
+static inline bool pcie_tph_dsm_supported(struct pci_dev *pdev)
+{ return false; }
#endif
#endif /* LINUX_PCI_TPH_H */
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 64b308b6e61c19..a99eeea53c814a 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -472,7 +472,6 @@ struct pci_dev {
unsigned int ats_enabled:1; /* Address Translation Svc */
unsigned int pasid_enabled:1; /* Process Address Space ID */
unsigned int pri_enabled:1; /* Page Request Interface */
- unsigned int tph_enabled:1; /* TLP Processing Hints */
unsigned int fm_enabled:1; /* Flit Mode (segment captured) */
unsigned int is_managed:1; /* Managed via devres */
unsigned int is_msi_managed:1; /* MSI release via devres installed */
@@ -588,7 +587,10 @@ struct pci_dev {
u8 reset_methods[PCI_NUM_RESET_METHODS]; /* In priority order */
#ifdef CONFIG_PCIE_TPH
- u16 tph_cap; /* TPH capability offset */
+ u16 tph_cap:14; /* TPH capability offset */
+ u16 tph_ext_support:1; /* Indicate whether Extended TPH
+ * requester is supported */
+ u16 tph_enabled:1; /* Whether TPH is enabled */
u8 tph_mode; /* TPH mode */
u8 tph_req_type; /* TPH requester type */
#endif
diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h
index 9a1674c152aa29..9a6e82c5c00cd3 100644
--- a/include/linux/vfio_pci_core.h
+++ b/include/linux/vfio_pci_core.h
@@ -149,6 +149,8 @@ struct vfio_pci_core_device {
struct notifier_block nb;
struct rw_semaphore memory_lock;
struct list_head dmabufs;
+ u8 tph_policy;
+ bool tph_permit;
};
enum vfio_pci_io_width {
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index 5de618a3a5eeee..ed04b6280cd69e 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -1534,6 +1534,68 @@ struct vfio_device_feature_dma_buf {
*/
#define VFIO_DEVICE_FEATURE_MIG_PRECOPY_INFOv2 12
+/* PCIe TPH device feature definitions for VFIO_DEVICE_FEATURE ioctl */
+#define VFIO_DEVICE_FEATURE_TPH 13
+#define VFIO_DEVICE_FEATURE_TPH_RESOLVE 14
+#define VFIO_DEVICE_FEATURE_TPH_ST 15
+
+/*
+ * VFIO_DEVICE_FEATURE_TPH - Control and query PCI TPH capabilities
+ *
+ * SET: Opt-in to TPH feature; flags must be zero.
+ * GET: Return supported TPH capability bits in flags.
+ */
+struct vfio_device_feature_tph {
+ __u32 flags;
+};
+
+/* Capability bits returned by GET on VFIO_DEVICE_FEATURE_TPH */
+#define VFIO_DEVICE_TPH_CAP_DMABUF (1u << 0) /* DMABUF sources resolvable */
+#define VFIO_DEVICE_TPH_CAP_CPU (1u << 1) /* CPU sources resolvable via _DSM */
+#define VFIO_DEVICE_TPH_CAP_LITERAL (1u << 2) /* LITERAL ST source supported */
+
+/*
+ * VFIO_DEVICE_FEATURE_TPH_RESOLVE - Resolve PH/ST tag value from source
+ * Note: GET only
+ */
+struct vfio_device_feature_tph_resolve {
+ __u32 flags; /* IN: source type + namespace modifiers */
+ __u32 src; /* IN: dma-buf fd or CPU ID based on flags */
+ __u8 valid; /* OUT: bitmap indicating valid output fields */
+#define VFIO_DEVICE_TPH_VALID_PH (1u << 0) /* ph holds valid processing hint */
+#define VFIO_DEVICE_TPH_VALID_ST (1u << 1) /* st holds valid steering tag */
+ __u8 ph; /* OUT: TPH processing hint */
+ __u16 st; /* OUT: raw steering tag value */
+};
+
+/*
+ * VFIO_DEVICE_FEATURE_TPH_ST - Batch program architected ST table entries
+ * Return value: number of successfully written entries
+ * Note: SET only
+ */
+struct vfio_device_feature_tph_st {
+ __u32 flags; /* IN: source type + namespace modifiers */
+ __u16 start; /* IN: first ST table index / MSI-X vector */
+ __u16 count; /* IN: number of contiguous entries to program */
+ __aligned_u64 dests; /* IN: user pointer to array of source u32 values */
+};
+
+/* Common flags shared by TPH_RESOLVE and TPH_ST features */
+#define VFIO_DEVICE_TPH_SRC_NONE (1u << 0) /* TPH_ST only: set ST=0 */
+#define VFIO_DEVICE_TPH_SRC_DMABUF (1u << 1) /* src = dma-buf fd */
+#define VFIO_DEVICE_TPH_SRC_CPU_VOLATILE (1u << 2) /* src = volatile memory CPU ID */
+#define VFIO_DEVICE_TPH_SRC_CPU_PERSISTENT (1u << 3) /* src = persistent memory CPU ID */
+#define VFIO_DEVICE_TPH_SRC_LITERAL (1u << 4) /* TPH_ST only: src = raw literal ST */
+#define VFIO_DEVICE_TPH_EXTENDED (1u << 30) /* Use extended 16-bit ST namespace */
+#define VFIO_DEVICE_TPH_REQUIRE_ST (1u << 31) /* TPH_ST only: fail if resolved ST=0 */
+
+/* Mask for mutually exclusive source type bits, only one may be set */
+#define VFIO_DEVICE_TPH_SRC_MASK (VFIO_DEVICE_TPH_SRC_NONE | \
+ VFIO_DEVICE_TPH_SRC_DMABUF | \
+ VFIO_DEVICE_TPH_SRC_CPU_VOLATILE | \
+ VFIO_DEVICE_TPH_SRC_CPU_PERSISTENT | \
+ VFIO_DEVICE_TPH_SRC_LITERAL)
+
/* -------- API for Type1 VFIO IOMMU -------- */
/**
|
| 3/2 | 2026/07/02 16:12 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 18269 output: 20 thoughts: 499 Thoughts: **Fuzzing Decision for the VFIO/PCI TPH Patch** Alright, let's break this down. My initial assessment is that this patch *absolutely* introduces functional changes that warrant fuzzing. The fact that it touches `drivers/pci/tph.c`, `drivers/vfio/*` and includes header file modifications is a huge indicator. This isn't just a simple bug fix. It's adding new features to VFIO specifically around PCIe TPH (TLP Processing Hints). Specifically, these new `VFIO_DEVICE_FEATURE_TPH`, `VFIO_DEVICE_FEATURE_TPH_RESOLVE`, and `VFIO_DEVICE_FEATURE_TPH_ST` ioctls scream "userspace interaction," and that's a prime target for fuzzing. Modifying the VFIO PCI configuration space handling to allow writing to the TPH capability also opens up new attack surfaces. The addition of a `tph` module parameter to `vfio_pci` further solidifies the impact. So, yeah, `WorthFuzzing=true`. Now, let's get into the specifics. I need to identify the key kernel functions that are relevant to this new functionality. From the code, I've got a list: * `vfio_pci_core_feature_tph` * `vfio_pci_core_feature_tph_resolve` * `vfio_pci_core_feature_tph_st` * `vfio_tph_config_write` * `vfio_pci_dma_buf_get_tph_by_fd` * `vfio_pci_dma_buf_get_pci_tph` Those are the specific entry points for fuzzing. They are all userspace accessible entry points. Finally, I need to know the necessary configuration options. The code seems to be gated by `CONFIG_PCIE_TPH` and `CONFIG_VFIO_PCI`. I should cross-reference `drivers/vfio/pci/Kconfig` and `drivers/pci/Kconfig` to confirm where `PCIE_TPH` is defined and make sure the fuzzing environment is properly configured. |
| 4/2 | 2026/07/02 16:12 | tool | grepper |
0mArgs:{
"Expression": "config PCIE_TPH"
}Results: {
"Output": "drivers/pci/Kconfig=185=config PCI_PASID\n--\ndrivers/pci/Kconfig-196-\ndrivers/pci/Kconfig:197:config PCIE_TPH\ndrivers/pci/Kconfig-198-\tbool \"TLP Processing Hints\"\n"
} |
| 5/2 | 2026/07/02 16:12 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 19366 output: 23 thoughts: 73 Thoughts: Okay, let's break this down. **Necessary Configuration for VFIO and DMA Buffers** Right, so I'm focusing on the configuration side of things here, particularly as it relates to VFIO. My initial thought is that `PCIE_TPH` is a configuration option that likely plays a crucial role in enabling certain functionalities. And, of course, the ever-present `VFIO_PCI` setting is also absolutely necessary. No surprises there. But now, I need to dig deeper. I'm wondering if there might be other related configuration options I should be aware of. Specifically, I'm thinking about that function call, `vfio_pci_dma_buf_get_tph_by_fd`. That screams DMA buffer support. So, `CONFIG_VFIO_PCI_DMABUF` seems like a potential candidate. It looks like that might be another requirement if I'm aiming to leverage `vfio_pci_dma_buf_get_tph_by_fd`. I need to confirm that relationship. |
| 6/2 | 2026/07/02 16:12 | tool | grepper |
0mArgs:{
"Expression": "config VFIO_PCI_DMABUF"
}Results: {
"Output": "drivers/vfio/pci/Kconfig=47=config VFIO_PCI_ZDEV_KVM\n--\ndrivers/vfio/pci/Kconfig-57-\ndrivers/vfio/pci/Kconfig:58:config VFIO_PCI_DMABUF\ndrivers/vfio/pci/Kconfig-59-\tdef_bool y if VFIO_PCI_CORE \u0026\u0026 PCI_P2PDMA \u0026\u0026 DMA_SHARED_BUFFER\n"
} |
| 7/2 | 2026/07/02 16:12 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 19808 output: 26 thoughts: 114 Thoughts: **Enabling VFIO_PCI_DMABUF and Related Features** Okay, so `VFIO_PCI_DMABUF` is a dependent boolean, right? It hinges on `VFIO_PCI_CORE`, `PCI_P2PDMA`, and `DMA_SHARED_BUFFER`. Makes sense. So, to ensure `VFIO_PCI_DMABUF` is enabled, we need to make sure `PCI_P2PDMA` and `DMA_SHARED_BUFFER` are also enabled, assuming they aren't already. Instead of explicitly mentioning every dependency, I'll just list `PCIE_TPH`, `VFIO_PCI`, and `PCI_P2PDMA` in the configuration. That should be sufficient, a concise and focused approach. Now, let's take a quick look at `vfio_pci_core_feature_tph` and the others to verify everything is in order. Gotta be thorough. |
| 8/2 | 2026/07/02 16:12 | tool | grepper |
0mArgs:{
"Expression": "vfio_pci_core_feature_tph"
}Results: {
"Output": "drivers/vfio/pci/vfio_pci_core.c=1601=static int vfio_pci_core_feature_token(struct vfio_pci_core_device *vdev,\n--\ndrivers/vfio/pci/vfio_pci_core.c-1627-\ndrivers/vfio/pci/vfio_pci_core.c:1628:static int vfio_pci_core_feature_tph(struct vfio_pci_core_device *vdev,\ndrivers/vfio/pci/vfio_pci_core.c-1629-\t\t\t\t u32 flags,\n--\ndrivers/vfio/pci/vfio_pci_core.c-1659-\ndrivers/vfio/pci/vfio_pci_core.c:1660:static int vfio_pci_core_feature_tph_resolve(struct vfio_pci_core_device *vdev,\ndrivers/vfio/pci/vfio_pci_core.c-1661-\t\t\tu32 flags,\n--\ndrivers/vfio/pci/vfio_pci_core.c=1750=static int tph_get_st_tag(struct pci_dev *pdev, u32 src_bits, u32 src_hndl,\n--\ndrivers/vfio/pci/vfio_pci_core.c-1775-\ndrivers/vfio/pci/vfio_pci_core.c:1776:static int vfio_pci_core_feature_tph_st(struct vfio_pci_core_device *vdev,\ndrivers/vfio/pci/vfio_pci_core.c-1777-\t\t\t\tu32 flags,\n--\ndrivers/vfio/pci/vfio_pci_core.c=1849=int vfio_pci_core_ioctl_feature(struct vfio_device *device, u32 flags,\n--\ndrivers/vfio/pci/vfio_pci_core.c-1867-\tcase VFIO_DEVICE_FEATURE_TPH:\ndrivers/vfio/pci/vfio_pci_core.c:1868:\t\treturn vfio_pci_core_feature_tph(vdev, flags, arg, argsz);\ndrivers/vfio/pci/vfio_pci_core.c-1869-\tcase VFIO_DEVICE_FEATURE_TPH_RESOLVE:\ndrivers/vfio/pci/vfio_pci_core.c:1870:\t\treturn vfio_pci_core_feature_tph_resolve(vdev, flags,\ndrivers/vfio/pci/vfio_pci_core.c-1871-\t\t\t\t\t\t\t arg, argsz);\ndrivers/vfio/pci/vfio_pci_core.c-1872-\tcase VFIO_DEVICE_FEATURE_TPH_ST:\ndrivers/vfio/pci/vfio_pci_core.c:1873:\t\treturn vfio_pci_core_feature_tph_st(vdev, flags, arg, argsz);\ndrivers/vfio/pci/vfio_pci_core.c-1874-\tdefault:\n"
} |
| 9/2 | 2026/07/02 16:12 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20981 output: 23 thoughts: 114 Thoughts: **Analyzing VFIO-PCI TPH Implementation** Alright, let's zero in on these functions: `vfio_pci_core_feature_tph`, `vfio_pci_core_feature_tph_resolve`, `vfio_pci_core_feature_tph_st`, `vfio_tph_config_write`, and `vfio_pci_dma_buf_get_tph_by_fd`. These are the key players in the TPH (Translated Physical Address) feature for VFIO-PCI, and I need to understand their interaction. My immediate focus is on how the translation and configuration aspects work. First order of business: confirm that `vfio_tph_config_write` is actually implemented in this version. This is the bedrock of the configuration path, and knowing if it's there is essential to understanding the rest. We need this function to be present so the feature can work. |
| 10/2 | 2026/07/02 16:12 | tool | grepper |
0mArgs:{
"Expression": "vfio_tph_config_write"
}Results: {
"Output": "drivers/vfio/pci/vfio_pci_config.c=1148=static int vfio_find_cap_start(struct vfio_pci_core_device *vdev, int pos);\ndrivers/vfio/pci/vfio_pci_config.c:1149:static int vfio_tph_config_write(struct vfio_pci_core_device *vdev, int pos,\ndrivers/vfio/pci/vfio_pci_config.c-1150-\t\t\t\t int count, struct perm_bits *perm,\n--\ndrivers/vfio/pci/vfio_pci_config.c=1228=int __init vfio_pci_init_perm_bits(void)\n--\ndrivers/vfio/pci/vfio_pci_config.c-1246-\tret |= init_pci_ext_cap_tph_perm(\u0026ecap_perms[PCI_EXT_CAP_ID_TPH]);\ndrivers/vfio/pci/vfio_pci_config.c:1247:\tecap_perms[PCI_EXT_CAP_ID_TPH].writefn = vfio_tph_config_write;\ndrivers/vfio/pci/vfio_pci_config.c-1248-\tecap_perms[PCI_EXT_CAP_ID_VNDR].writefn = vfio_raw_config_write;\n"
} |
| 11/2 | 2026/07/02 16:12 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 21686 output: 33 thoughts: 31 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Checking `vfio_pci_dma_buf_get_tph_by_fd`** Right, that's definitely the case. Now, let me just double-check that `vfio_pci_dma_buf_get_tph_by_fd` is behaving as I expect it to. This is a critical function, and a quick re-examination never hurts, given its role within the vfio-pci DMA buffer handling. I need to refresh my mind on the subtleties in this area. Time to take a closer look... |
| 12/2 | 2026/07/02 16:12 | tool | grepper |
0mArgs:{
"Expression": "vfio_pci_dma_buf_get_tph_by_fd"
}Results: {
"Output": "drivers/vfio/pci/vfio_pci_core.c=1660=static int vfio_pci_core_feature_tph_resolve(struct vfio_pci_core_device *vdev,\n--\ndrivers/vfio/pci/vfio_pci_core.c-1705-\tif (src_bits \u0026 VFIO_DEVICE_TPH_SRC_DMABUF) {\ndrivers/vfio/pci/vfio_pci_core.c:1706:\t\tret = vfio_pci_dma_buf_get_tph_by_fd(resolve.src, extended,\ndrivers/vfio/pci/vfio_pci_core.c-1707-\t\t\t\t\t\t \u0026tag, \u0026ph);\n--\ndrivers/vfio/pci/vfio_pci_core.c=1750=static int tph_get_st_tag(struct pci_dev *pdev, u32 src_bits, u32 src_hndl,\n--\ndrivers/vfio/pci/vfio_pci_core.c-1758-\telse if (src_bits \u0026 VFIO_DEVICE_TPH_SRC_DMABUF)\ndrivers/vfio/pci/vfio_pci_core.c:1759:\t\tret = vfio_pci_dma_buf_get_tph_by_fd(src_hndl, extended,\ndrivers/vfio/pci/vfio_pci_core.c-1760-\t\t\t\t\t\t tag, \u0026ph);\n--\ndrivers/vfio/pci/vfio_pci_dmabuf.c=129=static const struct dma_buf_ops vfio_pci_dmabuf_ops = {\n--\ndrivers/vfio/pci/vfio_pci_dmabuf.c-135-\ndrivers/vfio/pci/vfio_pci_dmabuf.c:136:int vfio_pci_dma_buf_get_tph_by_fd(int fd, bool extended, u16 *tag, u8 *ph)\ndrivers/vfio/pci/vfio_pci_dmabuf.c-137-{\n--\ndrivers/vfio/pci/vfio_pci_priv.h=119=static inline bool vfio_pci_is_vga(struct pci_dev *pdev)\n--\ndrivers/vfio/pci/vfio_pci_priv.h-124-#ifdef CONFIG_VFIO_PCI_DMABUF\ndrivers/vfio/pci/vfio_pci_priv.h:125:int vfio_pci_dma_buf_get_tph_by_fd(int fd, bool extended, u16 *tag, u8 *ph);\ndrivers/vfio/pci/vfio_pci_priv.h-126-int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,\n--\ndrivers/vfio/pci/vfio_pci_priv.h=130=void vfio_pci_dma_buf_move(struct vfio_pci_core_device *vdev, bool revoked);\ndrivers/vfio/pci/vfio_pci_priv.h-131-#else\ndrivers/vfio/pci/vfio_pci_priv.h:132:static inline int vfio_pci_dma_buf_get_tph_by_fd(int fd, bool extended,\ndrivers/vfio/pci/vfio_pci_priv.h-133-\t\t\t\t\t\t u16 *tag, u8 *ph)\n"
} |
| 13/2 | 2026/07/02 16:12 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 22824 output: 32 thoughts: 23 Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person: **Investigating DMA Buffer Handling in VFIO-PCI** Alright, I'm digging deeper into the VFIO-PCI driver, and my focus has shifted to DMA buffer management. Specifically, I'm drilling down into how it interfaces with PCI Translation and Protection (TPH) for DMA transfers. The architecture and its implications for performance and security are what I'm interested in here. My train of thought is that I'm encountering a new function related to the handling of DMA buffers. The name `vfio_pci_dma_buf_get_pci_tph` immediately piques my interest. It implies a direct connection between DMA buffers managed via the `dma_buf` framework and the PCI Translation and Protection features. I need to understand what this function *does*. How does it interact with the DMA buffer framework? What are the implications of TPH being involved? Is this related to SVA? This could be a critical piece of the puzzle regarding secure and efficient DMA access within a virtualized VFIO environment. It probably holds clues to the relationship between the dma_buf and pci layers in these specific scenarios, particularly where direct memory access is required. |
| 14/2 | 2026/07/02 16:12 | tool | grepper |
0mArgs:{
"Expression": "vfio_pci_dma_buf_get_pci_tph"
}Results: {
"Output": "drivers/vfio/pci/vfio_pci_dmabuf.c=79=static void vfio_pci_dma_buf_unmap(struct dma_buf_attachment *attachment,\n--\ndrivers/vfio/pci/vfio_pci_dmabuf.c-90-\ndrivers/vfio/pci/vfio_pci_dmabuf.c:91:static int vfio_pci_dma_buf_get_pci_tph(struct dma_buf *dmabuf, bool extended,\ndrivers/vfio/pci/vfio_pci_dmabuf.c-92-\t\t\t\t\tu16 *tag, u8 *ph)\n--\ndrivers/vfio/pci/vfio_pci_dmabuf.c=136=int vfio_pci_dma_buf_get_tph_by_fd(int fd, bool extended, u16 *tag, u8 *ph)\n--\ndrivers/vfio/pci/vfio_pci_dmabuf.c-149-\ndrivers/vfio/pci/vfio_pci_dmabuf.c:150:\tret = vfio_pci_dma_buf_get_pci_tph(dmabuf, extended, tag, ph);\ndrivers/vfio/pci/vfio_pci_dmabuf.c-151-out:\n"
} |
| 15/2 | 2026/07/02 16:12 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 23510 output: 23 thoughts: 294 Thoughts: **Analyzing VFIO-PCI TPH Implementation** Alright, let's break down this VFIO-PCI TPH stuff. I need to get a clear picture of the relevant symbols and configurations. I see that the primary focus symbols are: `vfio_pci_core_feature_tph`, `vfio_pci_core_feature_tph_resolve`, `vfio_pci_core_feature_tph_st`, `vfio_tph_config_write`, `vfio_pci_dma_buf_get_tph_by_fd`, and `vfio_pci_dma_buf_get_pci_tph`. Those are the core functions I need to understand for now. Now, what are the relevant configurations? I've got `PCIE_TPH`, `VFIO_PCI`, and `VFIO_PCI_DMABUF`. Hold on, looks like `VFIO_PCI_DMABUF` is a dependent bool. It's only set to 'y' if `VFIO_PCI_CORE`, `PCI_P2PDMA` and `DMA_SHARED_BUFFER` are enabled. Right. I should include `PCI_P2PDMA` and `DMA_SHARED_BUFFER` in the list, just in case. They're part of the dependencies. I should probably double-check the `drivers/vfio/pci/Kconfig` file again to make sure I haven't missed anything, but this should be enough of a start. |
| 16/2 | 2026/07/02 16:13 | tool | grepper |
0mArgs:{
"Expression": "config VFIO_PCI_DMABUF"
}Results: {
"Output": "drivers/vfio/pci/Kconfig=47=config VFIO_PCI_ZDEV_KVM\n--\ndrivers/vfio/pci/Kconfig-57-\ndrivers/vfio/pci/Kconfig:58:config VFIO_PCI_DMABUF\ndrivers/vfio/pci/Kconfig-59-\tdef_bool y if VFIO_PCI_CORE \u0026\u0026 PCI_P2PDMA \u0026\u0026 DMA_SHARED_BUFFER\n"
} |
| 17/2 | 2026/07/02 16:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 24280 output: 20 thoughts: 53 Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, as if they were my own: **Enabling DMA-BUF Sharing with VFIO** Right, let's see, to get `VFIO_PCI_DMABUF` working properly, I'm going to need to ensure both `PCI_P2PDMA` and `DMA_SHARED_BUFFER` are enabled. That's a fundamental requirement for the underlying DMA infrastructure. I can’t remember off the top of my head if there are any specific dependencies or interactions with other features. Now, let me just double-check the `drivers/pci/Kconfig` file. I seem to recall something about `PCIE_TPH` potentially playing a role in this DMA path, so I need to verify its status and any potential configuration conflicts. Better safe than sorry. I'll make sure it’s configured correctly within the context of the larger setup. It's crucial to understand how this all interrelates for optimal performance and stability. |
| 18/2 | 2026/07/02 16:13 | tool | grepper |
0mArgs:{
"Expression": "config PCIE_TPH"
}Results: {
"Output": "drivers/pci/Kconfig=185=config PCI_PASID\n--\ndrivers/pci/Kconfig-196-\ndrivers/pci/Kconfig:197:config PCIE_TPH\ndrivers/pci/Kconfig-198-\tbool \"TLP Processing Hints\"\n"
} |
| 19/2 | 2026/07/02 16:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 24658 output: 19 thoughts: 36 Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience: **Analyzing the `PCIE_TPH` Boolean** Alright, so I'm looking at `PCIE_TPH`, and it's flagged as a boolean. Immediately, my mind starts tracing the implications. A boolean indicates a simple "enabled/disabled" state. This triggers a quick memory check: what dependencies are in play? Where is this being used? What control does this provide? My next instinct is to jump directly into the source. Specifically, I need to know where `VFIO_PCI` is set. It seems this would be useful in order to understand if the configuration of `VFIO_PCI` has any bearing on `PCIE_TPH`, so I'm heading to `drivers/vfio/pci/Kconfig`. Let's see what the Kconfig file has to reveal. I expect to see the relationships between these features defined there. Understanding those relationships is the key to managing this configuration effectively. |
| 20/2 | 2026/07/02 16:13 | tool | grepper |
0mArgs:{
"Expression": "config VFIO_PCI"
}Results: {
"Output": "drivers/vfio/pci/Kconfig=2=menu \"VFIO support for PCI devices\"\n--\ndrivers/vfio/pci/Kconfig-4-\ndrivers/vfio/pci/Kconfig:5:config VFIO_PCI_CORE\ndrivers/vfio/pci/Kconfig-6-\ttristate\n--\ndrivers/vfio/pci/Kconfig-9-\ndrivers/vfio/pci/Kconfig:10:config VFIO_PCI_INTX\ndrivers/vfio/pci/Kconfig-11-\tdef_bool y if !S390\n--\ndrivers/vfio/pci/Kconfig-13-\ndrivers/vfio/pci/Kconfig:14:config VFIO_PCI\ndrivers/vfio/pci/Kconfig-15-\ttristate \"Generic VFIO support for any PCI device\"\n--\ndrivers/vfio/pci/Kconfig=23=if VFIO_PCI\ndrivers/vfio/pci/Kconfig:24:config VFIO_PCI_VGA\ndrivers/vfio/pci/Kconfig-25-\tbool \"Generic VFIO PCI support for VGA devices\"\n--\ndrivers/vfio/pci/Kconfig-33-\ndrivers/vfio/pci/Kconfig:34:config VFIO_PCI_IGD\ndrivers/vfio/pci/Kconfig-35-\tbool \"Generic VFIO PCI extensions for Intel graphics (GVT-d)\"\n--\ndrivers/vfio/pci/Kconfig=45=endif\ndrivers/vfio/pci/Kconfig-46-\ndrivers/vfio/pci/Kconfig:47:config VFIO_PCI_ZDEV_KVM\ndrivers/vfio/pci/Kconfig-48-\tbool \"VFIO PCI extensions for s390x KVM passthrough\"\n--\ndrivers/vfio/pci/Kconfig-57-\ndrivers/vfio/pci/Kconfig:58:config VFIO_PCI_DMABUF\ndrivers/vfio/pci/Kconfig-59-\tdef_bool y if VFIO_PCI_CORE \u0026\u0026 PCI_P2PDMA \u0026\u0026 DMA_SHARED_BUFFER\n"
} |
| 21/2 | 2026/07/02 16:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 25467 output: 21 thoughts: 35 Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, as if they were my own: **VFIO & PCI Configuration Review** Alright, so the core of this operation hinges on `VFIO_PCI`, that's the starting point for this configuration. We know VFIO is the underlying virtualization framework, and PCI is the mechanism for direct device access. Given that, I want to confirm our dependencies and configurations are solid. Let's dive into the kernel configuration, specifically the `drivers/pci/Kconfig` file. I'm focusing on that directory because of the PCI focus. The key parameter to check in that file would be `PCI_P2PDMA`. I need to verify that DMA functionality is correctly enabled and configured in our setup, and I'm using `PCI_P2PDMA` as the flag to ensure that the settings match what we're expecting for this hardware. I will need to check how it's defined and what dependencies it has. |
| 22/2 | 2026/07/02 16:13 | tool | grepper |
0mArgs:{
"Expression": "config PCI_P2PDMA"
}Results: {
"Output": "drivers/pci/Kconfig=197=config PCIE_TPH\n--\ndrivers/pci/Kconfig-205-\ndrivers/pci/Kconfig:206:config PCI_P2PDMA\ndrivers/pci/Kconfig-207-\tbool \"PCI peer-to-peer transfer support\"\n"
} |
| 23/2 | 2026/07/02 16:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 25826 output: 20 thoughts: 21 Thoughts: Here's my take on those thoughts: **Analyzing `DMA_SHARED_BUFFER` Configuration** Okay, so I'm digging into the DMA buffer situation and trying to understand where this particular configuration option, `DMA_SHARED_BUFFER`, is defined. Based on the file path, `drivers/dma-buf/Kconfig`, I'm pretty sure that's where I need to be looking. The `Kconfig` files are the standard configuration files in the kernel, so this is the logical place to find the `DMA_SHARED_BUFFER` option. This helps me understand the available features when dealing with DMA buffers, which are obviously critical to optimize the driver's performance. So, yeah, that's my focus now, figuring out the implications of enabling or disabling this. |
| 24/2 | 2026/07/02 16:13 | tool | grepper |
0mArgs:{
"Expression": "config DMA_SHARED_BUFFER"
}Results: {
"Output": "drivers/base/Kconfig=213=source \"drivers/base/regmap/Kconfig\"\ndrivers/base/Kconfig-214-\ndrivers/base/Kconfig:215:config DMA_SHARED_BUFFER\ndrivers/base/Kconfig-216-\tbool\n"
} |
| 25/2 | 2026/07/02 16:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 26113 output: 276 thoughts: 551 Thoughts: **My Current Assessment of the PCI/VFIO TPH Landscape** Okay, so I'm focusing on TPH (Translated Physical Address Header) support within the PCI and VFIO subsystems. First, I'm noting that `DMA_SHARED_BUFFER` is a boolean, which is good to keep in mind for how it might interact. My initial thought is to enumerate the key configuration flags that are relevant, which I've just written down: `PCIE_TPH`, `VFIO_PCI`, `PCI_P2PDMA`, and `DMA_SHARED_BUFFER`. I’m using these to set the stage for further analysis. Now, I want to make sure I've identified all the relevant function symbols. I started by looking in `drivers/pci/tph.c` and found a bunch of them: `pcie_tph_get_cpu_st_explicit`, `pcie_enable_tph_explicit`, `pcie_tph_enabled_req_type`, `pcie_tph_supported`, and `pcie_tph_dsm_supported`. I added these to my growing list of focus symbols. Then, I looked in other relevant locations. In `drivers/vfio/pci/vfio_pci_core.c`, I found: `tph_get_st_tag` and `tph_calc_st_size`, which are important for VFIO interactions. In `drivers/vfio/pci/vfio_pci_config.c`, I saw: `vfio_tph_capability_adjust`, `vfio_tph_config_write`, and `init_pci_ext_cap_tph_perm`. I am thinking about how the configuration might interact with those. This led me to identify more relevant symbols, particularly from `vfio_pci_core.c` and `vfio_pci_config.c`. I have added these symbols: `vfio_pci_core_feature_tph`, `vfio_pci_core_feature_tph_resolve`, `vfio_pci_core_feature_tph_st`, `vfio_tph_config_write`, `vfio_tph_capability_adjust`, `vfio_pci_dma_buf_get_tph_by_fd`, `vfio_pci_dma_buf_get_pci_tph`, `pcie_tph_get_cpu_st_explicit`, `pcie_enable_tph_explicit`, `pcie_tph_supported`, and `pcie_tph_dsm_supported`. I believe this is a comprehensive list, at least for the initial overview. Now, I feel ready to proceed with the analysis, so I'll call `set-results`. |
| 26/2 | 2026/07/02 16:14 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"PCIE_TPH",
"VFIO_PCI",
"PCI_P2PDMA",
"DMA_SHARED_BUFFER"
],
"FocusSymbols": [
"vfio_pci_core_feature_tph",
"vfio_pci_core_feature_tph_resolve",
"vfio_pci_core_feature_tph_st",
"vfio_tph_config_write",
"vfio_tph_capability_adjust",
"vfio_pci_dma_buf_get_tph_by_fd",
"vfio_pci_dma_buf_get_pci_tph",
"pcie_tph_get_cpu_st_explicit",
"pcie_enable_tph_explicit",
"pcie_tph_supported",
"pcie_tph_dsm_supported"
],
"Reasoning": "The patch adds new VFIO device features for PCIe TLP Processing Hints (TPH), introducing new ioctls and configuration space handling. This is a functional change that exposes new APIs to userspace, which should be fuzzed.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"PCIE_TPH",
"VFIO_PCI",
"PCI_P2PDMA",
"DMA_SHARED_BUFFER"
],
"FocusSymbols": [
"vfio_pci_core_feature_tph",
"vfio_pci_core_feature_tph_resolve",
"vfio_pci_core_feature_tph_st",
"vfio_tph_config_write",
"vfio_tph_capability_adjust",
"vfio_pci_dma_buf_get_tph_by_fd",
"vfio_pci_dma_buf_get_pci_tph",
"pcie_tph_get_cpu_st_explicit",
"pcie_enable_tph_explicit",
"pcie_tph_supported",
"pcie_tph_dsm_supported"
],
"Reasoning": "The patch adds new VFIO device features for PCIe TLP Processing Hints (TPH), introducing new ioctls and configuration space handling. This is a functional change that exposes new APIs to userspace, which should be fuzzed.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|