AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/06/22 08:14 flow patch-triage
2m Args:
null

Results:
{
  "EnableConfigs": [
    "PCIE_TPH"
  ],
  "FocusSymbols": [
    "vfio_pci_core_feature_tph_enable",
    "vfio_pci_core_feature_tph_st_config",
    "vfio_tph_config_write",
    "vfio_pci_tph_init",
    "pci_tph_init",
    "enable_tph",
    "tph_cpu_st_read"
  ],
  "Reasoning": "This patch introduces PCIe TPH (Transaction Processing Hints) support to VFIO PCI and adds a sysfs attribute for CPU-to-ST mapping. The VFIO ioctls and config space handlers are reachable and should be fuzzed, but they require the `vfio_pci.enable_unsafe_tph=1` module parameter to be set on the kernel command line. The core PCI TPH initialization is reachable during virtual PCI device probe.",
  "WorthFuzzing": true
}

1/1 2026/06/22 08:14 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit a82543a1ed09a26d646658202437d614735b7387\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Jun 22 08:14:42 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci\nindex b767db2c52cb70..edc64e4e56404e 100644\n--- a/Documentation/ABI/testing/sysfs-bus-pci\n+++ b/Documentation/ABI/testing/sysfs-bus-pci\n@@ -702,3 +702,27 @@ Description:\n \t\tWhen present and the tsm/ attribute directory is present, the\n \t\tauthenticated attribute is an alias for the device 'connect'\n \t\tstate. See the 'tsm/connect' attribute for more details.\n+\n+What:\t\t/sys/bus/pci/devices/\u003croot-port-bdf\u003e/tph_cpu_st\n+Contact:\tlinux-pci@vger.kernel.org\n+Description:\n+\t\tRead-only binary attribute only exposed on PCIe Root Ports that\n+\t\tsupport TPH Completer capability and implement the ACPI DSM\n+\t\tmethod for CPU-to-ST mapping. File permission is root-only\n+\t\t(0400).\n+\n+\t\tThe blob is a sequence of fixed-size 8-byte entries defined by\n+\t\tstruct pci_tph_cpu_st in uapi/linux/pci.h:\n+\t\t  __u8 vm_st;\n+\t\t  __u8 pm_st;\n+\t\t  __u16 vm_xst;\n+\t\t  __u16 pm_xst;\n+\t\t  __u16 reserved;\n+\n+\t\tEach entry corresponds to a logical CPU index. Seek offset =\n+\t\tcpu_id * PCI_TPH_CPU_ST_ENTRY_SZ. Arbitrary unaligned partial\n+\t\treads are supported; no alignment restriction enforced.\n+\n+\t\tFor CPUs outside cpu_possible_mask or offline CPUs, the entry\n+\t\tis filled with all zeros to avoid breaking sequential dump tools\n+\t\tlike cat/hexdump on sparse CPU topologies.\ndiff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c\nindex d37860841260ce..ad9e4e8d320b07 100644\n--- a/drivers/pci/pci-sysfs.c\n+++ b/drivers/pci/pci-sysfs.c\n@@ -1832,6 +1832,9 @@ const struct attribute_group *pci_dev_attr_groups[] = {\n #ifdef CONFIG_PCI_TSM\n \t\u0026pci_tsm_auth_attr_group,\n \t\u0026pci_tsm_attr_group,\n+#endif\n+#ifdef CONFIG_PCIE_TPH\n+\t\u0026pcie_tph_cpu_st_attr_group,\n #endif\n \tNULL,\n };\ndiff --git a/drivers/pci/pci.h b/drivers/pci/pci.h\nindex e8ad27abb1cfe4..1abe7fa1fcc7ed 100644\n--- a/drivers/pci/pci.h\n+++ b/drivers/pci/pci.h\n@@ -1375,6 +1375,10 @@ static inline pci_power_t acpi_pci_choose_state(struct pci_dev *pdev)\n extern const struct attribute_group aspm_ctrl_attr_group;\n #endif\n \n+#ifdef CONFIG_PCIE_TPH\n+extern const struct attribute_group pcie_tph_cpu_st_attr_group;\n+#endif\n+\n #ifdef CONFIG_X86_INTEL_MID\n bool pci_use_mid_pm(void);\n int mid_pci_set_power_state(struct pci_dev *pdev, pci_power_t state);\ndiff --git a/drivers/pci/tph.c b/drivers/pci/tph.c\nindex 91145e8d9d9512..47402d4b88998f 100644\n--- a/drivers/pci/tph.c\n+++ b/drivers/pci/tph.c\n@@ -11,6 +11,7 @@\n #include \u003clinux/msi.h\u003e\n #include \u003clinux/bitfield.h\u003e\n #include \u003clinux/pci-tph.h\u003e\n+#include \u003clinux/sched.h\u003e\n \n #include \"pci.h\"\n \n@@ -130,8 +131,47 @@ static acpi_status tph_invoke_dsm(acpi_handle handle, u32 cpu_uid,\n \n \treturn AE_OK;\n }\n+\n+static int tph_get_cpu_st_info(struct pci_dev *pdev, unsigned int cpu,\n+\t\t\t       union st_info *info)\n+{\n+\tacpi_handle rp_acpi_handle;\n+\tstruct pci_dev *rp;\n+\tu32 cpu_uid;\n+\tint ret;\n+\n+\tret = acpi_get_cpu_uid(cpu, \u0026cpu_uid);\n+\tif (ret != 0)\n+\t\treturn ret;\n+\n+\trp = pcie_find_root_port(pdev);\n+\tif (!rp || !rp-\u003ebus || !rp-\u003ebus-\u003ebridge)\n+\t\treturn -ENODEV;\n+\n+\trp_acpi_handle = ACPI_HANDLE(rp-\u003ebus-\u003ebridge);\n+\tif (tph_invoke_dsm(rp_acpi_handle, cpu_uid, info) != AE_OK)\n+\t\treturn -EINVAL;\n+\n+\treturn 0;\n+}\n #endif\n \n+static bool 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 rp_acpi_handle;\n+\n+\tif (!rp || !rp-\u003ebus || !rp-\u003ebus-\u003ebridge)\n+\t\treturn false;\n+\n+\trp_acpi_handle = ACPI_HANDLE(rp-\u003ebus-\u003ebridge);\n+\treturn acpi_check_dsm(rp_acpi_handle, \u0026pci_acpi_dsm_guid, 7,\n+\t\t\t      BIT(TPH_ST_DSM_FUNC_INDEX));\n+#endif\n+\treturn false;\n+}\n+\n /* Update the TPH Requester Enable field of TPH Control Register */\n static void set_ctrl_reg_req_en(struct pci_dev *pdev, u8 req_type)\n {\n@@ -166,11 +206,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@@ -185,9 +228,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@@ -231,6 +271,49 @@ 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+static int get_cpu_all_st(struct pci_dev *pdev, unsigned int cpu,\n+\t\t\t   struct pci_tph_cpu_st *st)\n+{\n+#ifdef CONFIG_ACPI\n+\tunion st_info info;\n+\tint ret;\n+\n+\tret = tph_get_cpu_st_info(pdev, cpu, \u0026info);\n+\tif (ret == 0) {\n+\t\tst-\u003evm_st = info.vm_st_valid ? info.vm_st : 0;\n+\t\tst-\u003epm_st = info.pm_st_valid ? info.pm_st : 0;\n+\t\tst-\u003evm_xst = info.vm_xst_valid ? info.vm_xst : 0;\n+\t\tst-\u003epm_xst = info.pm_xst_valid ? info.pm_xst : 0;\n+\t}\n+\n+\treturn ret;\n+#endif\n+\treturn -ENODEV;\n+}\n+\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+\tunion st_info info;\n+\tint ret;\n+\n+\tret = tph_get_cpu_st_info(pdev, cpu, \u0026info);\n+\tif (ret != 0)\n+\t\treturn ret;\n+\n+\t*tag = tph_extract_tag(mem_type, req_type, \u0026info);\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\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@@ -247,40 +330,35 @@ static int write_tag_to_st_table(struct pci_dev *pdev, int index, u16 tag)\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-#ifdef CONFIG_ACPI\n-\tstruct pci_dev *rp;\n-\tacpi_handle rp_acpi_handle;\n-\tunion st_info info;\n-\tu32 cpu_uid;\n-\tint ret;\n-\n-\tret = acpi_get_cpu_uid(cpu, \u0026cpu_uid);\n-\tif (ret != 0)\n-\t\treturn ret;\n-\n-\trp = pcie_find_root_port(pdev);\n-\tif (!rp || !rp-\u003ebus || !rp-\u003ebus-\u003ebridge)\n-\t\treturn -ENODEV;\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-\trp_acpi_handle = ACPI_HANDLE(rp-\u003ebus-\u003ebridge);\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 (tph_invoke_dsm(rp_acpi_handle, cpu_uid, \u0026info) != AE_OK) {\n-\t\t*tag = 0;\n+\tif (extended \u0026\u0026 !pdev-\u003etph_ext_support)\n \t\treturn -EINVAL;\n-\t}\n-\n-\t*tag = tph_extract_tag(mem_type, pdev-\u003etph_req_type, \u0026info);\n-\n-\tpci_dbg(pdev, \"get steering tag: mem_type=%s, cpu=%d, tag=%#04x\\n\",\n-\t\t(mem_type == TPH_MEM_TYPE_VM) ? \"volatile\" : \"persistent\",\n-\t\tcpu, *tag);\n \n-\treturn 0;\n-#else\n-\treturn -ENODEV;\n-#endif\n+\treturn get_cpu_st(pdev, mem_type, req_type, cpu, tag);\n }\n-EXPORT_SYMBOL(pcie_tph_get_cpu_st);\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@@ -316,8 +394,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@@ -366,27 +442,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@@ -404,26 +463,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@@ -431,7 +476,7 @@ int pcie_enable_tph(struct pci_dev *pdev, int mode)\n \treg |= FIELD_PREP(PCI_TPH_CTRL_MODE_SEL_MASK, pdev-\u003etph_mode);\n \n \treg \u0026= ~PCI_TPH_CTRL_REQ_EN_MASK;\n-\treg |= FIELD_PREP(PCI_TPH_CTRL_REQ_EN_MASK, pdev-\u003etph_req_type);\n+\treg |= FIELD_PREP(PCI_TPH_CTRL_REQ_EN_MASK, req_type);\n \n \tpci_write_config_dword(pdev, pdev-\u003etph_cap + PCI_TPH_CTRL, reg);\n \n@@ -439,8 +484,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@@ -512,14 +612,131 @@ 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+static ssize_t tph_cpu_st_read(struct file *filp, struct kobject *kobj,\n+\t\t\t       const struct bin_attribute *bin_attr, char *buf,\n+\t\t\t       loff_t off, size_t count)\n+{\n+\tstruct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));\n+\tconst size_t entry_sz = PCI_TPH_CPU_ST_ENTRY_SZ;\n+\tconst size_t total_size = nr_cpu_ids * entry_sz;\n+\tsize_t copied = 0;\n+\tloff_t pos = off;\n+\n+\tif (pos \u003e= total_size)\n+\t\treturn 0;\n+\n+\tcount = min_t(size_t, count, total_size - pos);\n+\n+\twhile (copied \u003c count) {\n+\t\tunsigned int cpu_idx = pos / entry_sz;\n+\t\tsize_t entry_off = pos % entry_sz;\n+\t\tsize_t remain = entry_sz - entry_off;\n+\t\tsize_t chunk = min_t(size_t, remain, count - copied);\n+\t\tstruct pci_tph_cpu_st st = {0};\n+\n+\t\tif (cpu_possible(cpu_idx))\n+\t\t\tget_cpu_all_st(pdev, cpu_idx, \u0026st);\n+\n+\t\tmemcpy(buf + copied, (char *)\u0026st + entry_off, chunk);\n+\n+\t\tcopied += chunk;\n+\t\tpos += chunk;\n+\n+\t\tcond_resched();\n+\t}\n+\n+\treturn copied;\n+}\n+static BIN_ATTR(tph_cpu_st, 0400, tph_cpu_st_read, NULL, 0);\n+\n+static const struct bin_attribute *const tph_cpu_st_bin_attrs[] = {\n+\t\u0026bin_attr_tph_cpu_st,\n+\tNULL,\n+};\n+\n+static size_t tph_cpu_st_bin_size(struct kobject *kobj,\n+\t\t\t\t  const struct bin_attribute *a, int n)\n+{\n+\treturn nr_cpu_ids * PCI_TPH_CPU_ST_ENTRY_SZ;\n+}\n+\n+static umode_t tph_cpu_st_attr_is_visible(struct kobject *kobj,\n+\t\t\t\t\t  const struct bin_attribute *a, int n)\n+{\n+\tstruct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));\n+\tbool is_root_port = pci_is_pcie(pdev) \u0026\u0026\n+\t\t\t\tpci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT;\n+\tu32 devcap2 = 0;\n+\n+\tif (!is_root_port)\n+\t\treturn 0;\n+\n+\tpci_read_config_dword(pdev, PCI_EXP_DEVCAP2, \u0026devcap2);\n+\tif (!(devcap2 \u0026 PCI_EXP_DEVCAP2_TPH_COMP_MASK))\n+\t\treturn 0;\n+\n+\tif (!tph_dsm_supported(pdev))\n+\t\treturn 0;\n+\n+\treturn a-\u003eattr.mode;\n+}\n+\n+const struct attribute_group pcie_tph_cpu_st_attr_group = {\n+\t.bin_attrs = tph_cpu_st_bin_attrs,\n+\t.bin_size = tph_cpu_st_bin_size,\n+\t.is_bin_visible = tph_cpu_st_attr_is_visible,\n+};\ndiff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c\nindex 0c771064c0b844..6d73668459cf55 100644\n--- a/drivers/vfio/pci/vfio_pci.c\n+++ b/drivers/vfio/pci/vfio_pci.c\n@@ -60,6 +60,12 @@ 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+#ifdef CONFIG_PCIE_TPH\n+static bool enable_unsafe_tph;\n+module_param(enable_unsafe_tph, bool, 0444);\n+MODULE_PARM_DESC(enable_unsafe_tph, \"Enable PCIe TPH (Transaction Processing Hints) support. It may break platform isolation. If you do not know what this is for, step away. (default: false)\");\n+#endif\n+\n static bool vfio_pci_dev_in_denylist(struct pci_dev *pdev)\n {\n \tswitch (pdev-\u003evendor) {\n@@ -257,12 +263,17 @@ static int __init vfio_pci_init(void)\n {\n \tint ret;\n \tbool is_disable_vga = true;\n+\tbool is_enable_unsafe_tph = false;\n \n #ifdef CONFIG_VFIO_PCI_VGA\n \tis_disable_vga = disable_vga;\n #endif\n+#ifdef CONFIG_PCIE_TPH\n+\tis_enable_unsafe_tph = enable_unsafe_tph;\n+#endif\n \n-\tvfio_pci_core_set_params(nointxmask, is_disable_vga, disable_idle_d3);\n+\tvfio_pci_core_set_params(nointxmask, is_disable_vga, disable_idle_d3,\n+\t\t\t\t is_enable_unsafe_tph);\n \n \t/* Register and scan for devices */\n \tret = pci_register_driver(\u0026vfio_pci_driver);\ndiff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c\nindex a10ed733f0e3af..10f4e9fabea7ca 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,118 @@ 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, ALL_WRITE);\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_mask_ext_tph_bit(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;\n+\n+\tif (!pcie_tph_supported(pdev, true)) {\n+\t\tval = le32_to_cpu(*vptr);\n+\t\tval \u0026= ~PCI_TPH_CAP_EXT_TPH;\n+\t\t*vptr = cpu_to_le32(val);\n+\t}\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+\tint req_en_byte = PCI_TPH_CTRL + 1;\n+\tstruct pci_dev *pdev = vdev-\u003epdev;\n+\t__le32 org_val = 0;\n+\tbool extended;\n+\tu8 mode, req;\n+\tint i, ret;\n+\tu16 start;\n+\tu32 data;\n+\n+\tif (!vdev-\u003etph_permit)\n+\t\treturn count;\n+\n+\tdown_write(\u0026vdev-\u003ememory_lock);\n+\n+\t/* Back up the original values in order rollback when fail */\n+\tif (offset \u003c= req_en_byte \u0026\u0026 offset + count \u003e req_en_byte)\n+\t\tvfio_default_config_read(vdev, pos, count, perm, offset,\n+\t\t\t\t\t \u0026org_val);\n+\n+\tret = vfio_default_config_write(vdev, pos, count, perm, offset, val);\n+\tif (ret != count) {\n+\t\tup_write(\u0026vdev-\u003ememory_lock);\n+\t\treturn ret;\n+\t}\n+\n+\t/* Skip if write range does not cover Requester Enable byte */\n+\tif (offset \u003e req_en_byte || offset + count \u003c= req_en_byte) {\n+\t\tup_write(\u0026vdev-\u003ememory_lock);\n+\t\treturn count;\n+\t}\n+\n+\tret = vfio_pci_set_power_state(vdev, PCI_D0);\n+\tif (ret) {\n+\t\tvfio_default_config_write(vdev, pos, count, perm, offset,\n+\t\t\t\t\t  org_val);\n+\t\tup_write(\u0026vdev-\u003ememory_lock);\n+\t\treturn count;\n+\t}\n+\n+\tstart = vfio_find_cap_start(vdev, pos);\n+\tdata = le32_to_cpu(*(__le32 *)\u0026vdev-\u003evconfig[start + PCI_TPH_CTRL]);\n+\tmode = FIELD_GET(PCI_TPH_CTRL_MODE_SEL_MASK, data);\n+\treq = FIELD_GET(PCI_TPH_CTRL_REQ_EN_MASK, data);\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 \u0026\u0026 vdev-\u003etph_st_shadow) {\n+\t\t\tfor (i = 0; i \u003c vdev-\u003etph_st_entries; i++)\n+\t\t\t\tpcie_tph_set_st_entry(pdev, i,\n+\t\t\t\t\t\t      vdev-\u003etph_st_shadow[i]);\n+\t\t}\n+\t\tif (ret)\n+\t\t\tvfio_default_config_write(vdev, pos, count, perm,\n+\t\t\t\t\t\t  offset, org_val);\n+\t} else if (req == PCI_TPH_REQ_DISABLE) {\n+\t\tpcie_disable_tph(vdev-\u003epdev);\n+\t} else {\n+\t\tvfio_default_config_write(vdev, pos, count, perm, offset,\n+\t\t\t\t\t  org_val);\n+\t}\n+\n+\tup_write(\u0026vdev-\u003ememory_lock);\n+\n+\treturn count;\n+}\n+\n /*\n  * Initialize the shared permission tables\n  */\n@@ -1100,6 +1213,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 +1234,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 +1566,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 +1819,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_mask_ext_tph_bit(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 a28f1e99362c1e..1e706a690dbd75 100644\n--- a/drivers/vfio/pci/vfio_pci_core.c\n+++ b/drivers/vfio/pci/vfio_pci_core.c\n@@ -29,6 +29,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 #if IS_ENABLED(CONFIG_EEH)\n #include \u003casm/eeh.h\u003e\n #endif\n@@ -41,6 +42,7 @@\n static bool nointxmask;\n static bool disable_vga;\n static bool disable_idle_d3;\n+static bool enable_unsafe_tph;\n \n static void vfio_pci_eventfd_rcu_free(struct rcu_head *rcu)\n {\n@@ -531,6 +533,52 @@ static const struct dev_pm_ops vfio_pci_core_pm_ops = {\n \t\t\t   NULL)\n };\n \n+static int vfio_pci_tph_st_shadow_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 vfio_pci_tph_init(struct vfio_pci_core_device *vdev)\n+{\n+\tvdev-\u003etph_st_entries = 0;\n+\tvdev-\u003etph_st_shadow = NULL;\n+\tvdev-\u003etph_permit = false;\n+\n+\tif (!enable_unsafe_tph)\n+\t\treturn 0;\n+\n+\tvdev-\u003etph_st_entries = vfio_pci_tph_st_shadow_size(vdev);\n+\tif (vdev-\u003etph_st_entries) {\n+\t\tvdev-\u003etph_st_shadow = kcalloc(vdev-\u003etph_st_entries, sizeof(u16),\n+\t\t\t\t\t      GFP_KERNEL_ACCOUNT);\n+\t\tif (!vdev-\u003etph_st_shadow)\n+\t\t\treturn -ENOMEM;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void vfio_pci_tph_deinit(struct vfio_pci_core_device *vdev)\n+{\n+\tkfree(vdev-\u003etph_st_shadow);\n+\tvdev-\u003etph_st_shadow = NULL;\n+\tvdev-\u003etph_st_entries = 0;\n+\tvdev-\u003etph_permit = false;\n+}\n+\n int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)\n {\n \tstruct pci_dev *pdev = vdev-\u003epdev;\n@@ -557,6 +605,13 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)\n \t\tgoto out_disable_device;\n \n \tvdev-\u003ereset_works = !ret;\n+\n+\t/* Reset TPH status on new user session */\n+\tpcie_disable_tph(vdev-\u003epdev);\n+\tret = vfio_pci_tph_init(vdev);\n+\tif (ret)\n+\t\tgoto out_disable_device;\n+\n \tpci_save_state(pdev);\n \tvdev-\u003epci_saved_state = pci_store_saved_state(pdev);\n \tif (!vdev-\u003epci_saved_state)\n@@ -614,6 +669,7 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)\n out_free_state:\n \tkfree(vdev-\u003epci_saved_state);\n \tvdev-\u003epci_saved_state = NULL;\n+\tvfio_pci_tph_deinit(vdev);\n out_disable_device:\n \tpci_disable_device(pdev);\n out_power:\n@@ -682,6 +738,9 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev)\n \tkfree(vdev-\u003eregion);\n \tvdev-\u003eregion = NULL; /* don't krealloc a freed pointer */\n \n+\t/* Reset TPH status on session exit */\n+\tpcie_disable_tph(vdev-\u003epdev);\n+\tvfio_pci_tph_deinit(vdev);\n \tvfio_config_free(vdev);\n \n \tfor (i = 0; i \u003c PCI_STD_NUM_BARS; i++) {\n@@ -1554,6 +1613,95 @@ 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_enable(struct vfio_pci_core_device *vdev,\n+\t\t\t\t\t    u32 flags, size_t argsz)\n+{\n+\tint ret;\n+\n+\tif (!enable_unsafe_tph)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_SET, 0);\n+\tif (ret \u003c= 0)\n+\t\treturn ret;\n+\n+\tif (!vdev-\u003etph_permit)\n+\t\tvdev-\u003etph_permit = 1;\n+\n+\treturn 0;\n+}\n+\n+static int vfio_pci_core_feature_tph_st_config(\n+\t\t\tstruct vfio_pci_core_device *vdev,\n+\t\t\tu32 flags,\n+\t\t\tstruct vfio_device_feature_tph_st_config __user *arg,\n+\t\t\tsize_t argsz)\n+{\n+\tstruct vfio_device_feature_tph_st_config config;\n+\tstruct pci_dev *pdev = vdev-\u003epdev;\n+\tvoid __user *uptr;\n+\tint i, idx, ret;\n+\tsize_t sz;\n+\tu16 *sts;\n+\n+\tif (!enable_unsafe_tph)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_SET,\n+\t\t\t\t sizeof(config));\n+\tif (ret \u003c= 0)\n+\t\treturn ret;\n+\n+\tif (!vdev-\u003etph_permit || !vdev-\u003etph_st_shadow)\n+\t\treturn -EINVAL;\n+\n+\tif (copy_from_user(\u0026config, arg, sizeof(config)))\n+\t\treturn -EFAULT;\n+\n+\tif (config.count == 0 || config.reserved != 0 ||\n+\t\tconfig.index \u003e= vdev-\u003etph_st_entries ||\n+\t\tconfig.count \u003e vdev-\u003etph_st_entries - config.index)\n+\t\treturn -EINVAL;\n+\n+\tuptr = u64_to_user_ptr(config.data_uptr);\n+\tsts = memdup_array_user(uptr, config.count, sizeof(u16));\n+\tsz = config.count * sizeof(u16);\n+\tif (IS_ERR(sts))\n+\t\treturn PTR_ERR(sts);\n+\n+\tdown_write(\u0026vdev-\u003ememory_lock);\n+\tret = vfio_pci_set_power_state(vdev, PCI_D0);\n+\tif (ret)\n+\t\tgoto out_unlock_memory;\n+\n+\tif (pcie_tph_enabled_req_type(pdev) == PCI_TPH_REQ_DISABLE)\n+\t\tgoto update_shadow;\n+\n+\tfor (i = 0; i \u003c config.count; i++) {\n+\t\tidx = config.index + i;\n+\t\tret = pcie_tph_set_st_entry(pdev, idx, sts[i]);\n+\t\tif (ret)\n+\t\t\tgoto rollback;\n+\t}\n+\n+update_shadow:\n+\tmemcpy(\u0026vdev-\u003etph_st_shadow[config.index], sts, sz);\n+\tret = 0;\n+\tgoto out_unlock_memory;\n+\n+rollback:\n+\twhile (i-- \u003e 0) {\n+\t\tidx = config.index + i;\n+\t\tpcie_tph_set_st_entry(pdev, idx, vdev-\u003etph_st_shadow[idx]);\n+\t}\n+\n+out_unlock_memory:\n+\tup_write(\u0026vdev-\u003ememory_lock);\n+\n+\tkfree(sts);\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@@ -1572,6 +1720,11 @@ 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_ENABLE:\n+\t\treturn vfio_pci_core_feature_tph_enable(vdev, flags, argsz);\n+\tcase VFIO_DEVICE_FEATURE_TPH_ST_CONFIG:\n+\t\treturn vfio_pci_core_feature_tph_st_config(vdev, flags,\n+\t\t\t\t\t\t\t   arg, argsz);\n \tdefault:\n \t\treturn -ENOTTY;\n \t}\n@@ -2615,11 +2768,13 @@ static void vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set)\n }\n \n void vfio_pci_core_set_params(bool is_nointxmask, bool is_disable_vga,\n-\t\t\t      bool is_disable_idle_d3)\n+\t\t\t      bool is_disable_idle_d3,\n+\t\t\t      bool is_enable_unsafe_tph)\n {\n \tnointxmask = is_nointxmask;\n \tdisable_vga = is_disable_vga;\n \tdisable_idle_d3 = is_disable_idle_d3;\n+\tenable_unsafe_tph = is_enable_unsafe_tph;\n }\n EXPORT_SYMBOL_GPL(vfio_pci_core_set_params);\n \ndiff --git a/include/linux/pci-tph.h b/include/linux/pci-tph.h\nindex be68cd17f2f833..5917a0694c1d66 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,16 @@ 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 #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 +45,24 @@ 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 #endif\n \n #endif /* LINUX_PCI_TPH_H */\ndiff --git a/include/linux/pci.h b/include/linux/pci.h\nindex 2c4454583c115b..285c0f00882e0e 100644\n--- a/include/linux/pci.h\n+++ b/include/linux/pci.h\n@@ -471,7 +471,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@@ -589,7 +588,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 5fc6ce4dd78616..527c84f042aa6f 100644\n--- a/include/linux/vfio_pci_core.h\n+++ b/include/linux/vfio_pci_core.h\n@@ -128,6 +128,7 @@ struct vfio_pci_core_device {\n \tbool\t\t\tpm_intx_masked:1;\n \tbool\t\t\tpm_runtime_engaged:1;\n \tbool\t\t\tsriov_active;\n+\tbool\t\t\ttph_permit;\n \tstruct pci_saved_state\t*pci_saved_state;\n \tstruct pci_saved_state\t*pm_save;\n \tint\t\t\tioeventfds_nr;\n@@ -143,6 +144,8 @@ struct vfio_pci_core_device {\n \tstruct notifier_block\tnb;\n \tstruct rw_semaphore\tmemory_lock;\n \tstruct list_head\tdmabufs;\n+\tu16\t\t\t*tph_st_shadow;\n+\tu16\t\t\ttph_st_entries;\n };\n \n enum vfio_pci_io_width {\n@@ -158,7 +161,8 @@ int vfio_pci_core_register_dev_region(struct vfio_pci_core_device *vdev,\n \t\t\t\t      const struct vfio_pci_regops *ops,\n \t\t\t\t      size_t size, u32 flags, void *data);\n void vfio_pci_core_set_params(bool nointxmask, bool is_disable_vga,\n-\t\t\t      bool is_disable_idle_d3);\n+\t\t\t      bool is_disable_idle_d3,\n+\t\t\t      bool is_enable_unsafe_tph);\n void vfio_pci_core_close_device(struct vfio_device *core_vdev);\n int vfio_pci_core_init_dev(struct vfio_device *core_vdev);\n void vfio_pci_core_release_dev(struct vfio_device *core_vdev);\ndiff --git a/include/uapi/linux/pci.h b/include/uapi/linux/pci.h\nindex 4f150028965d8b..5c4ea44d66d206 100644\n--- a/include/uapi/linux/pci.h\n+++ b/include/uapi/linux/pci.h\n@@ -19,6 +19,7 @@\n #define _UAPILINUX_PCI_H\n \n #include \u003clinux/pci_regs.h\u003e\t/* The pci register defines */\n+#include \u003clinux/types.h\u003e\n \n /*\n  * The PCI interface treats multi-function devices as independent\n@@ -46,4 +47,19 @@ enum pci_hotplug_event {\n \tPCI_HOTPLUG_CARD_NOT_PRESENT,\n };\n \n+/*\n+ * PCIe TPH sysfs binary entry for CPU-to-ST mapping\n+ * Sysfs file: /sys/bus/pci/devices/\u003cBDF\u003e/tph_cpu_st\n+ * Each entry is 8 bytes aligned, seek offset = cpu_id * PCI_TPH_CPU_ST_ENTRY_SZ\n+ */\n+struct pci_tph_cpu_st {\n+\t__u8  vm_st;        /* Volatile Memory Steering Tag (1 byte) */\n+\t__u8  pm_st;        /* Persistent Memory Steering Tag (1 byte) */\n+\t__u16 vm_xst;       /* Volatile Memory Extended Steering Tag (2 bytes) */\n+\t__u16 pm_xst;       /* Persistent Memory Extended Steering Tag (2 bytes) */\n+\t__u16 reserved;     /* Padding to 8 bytes for aligned offset lookup */\n+} __packed;\n+\n+#define PCI_TPH_CPU_ST_ENTRY_SZ sizeof(struct pci_tph_cpu_st)\n+\n #endif /* _UAPILINUX_PCI_H */\ndiff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h\nindex 5de618a3a5eeee..61079594a91f5a 100644\n--- a/include/uapi/linux/vfio.h\n+++ b/include/uapi/linux/vfio.h\n@@ -1534,6 +1534,35 @@ struct vfio_device_feature_dma_buf {\n  */\n #define VFIO_DEVICE_FEATURE_MIG_PRECOPY_INFOv2  12\n \n+/*\n+ * Device-level opt-in for TPH (Transaction Processing Hints) support.\n+ * When set, allows access to TPH_CPU_ST and TPH_ST_CONFIG features.\n+ * Requires global enable_unsafe_tph module parameter to be enabled.\n+ */\n+#define VFIO_DEVICE_FEATURE_TPH_ENABLE\t13\n+\n+/**\n+ * VFIO_DEVICE_FEATURE_TPH_ST_CONFIG - Configure PCIe TPH Steering Tag entries\n+ *\n+ * Provides userspace interface to configure PCIe TPH ST table entries.\n+ *\n+ * @index: Start entry offset within ST table\n+ * @count: Number of consecutive entries to configure\n+ * @data_uptr: Userspace data buffer for 16-bit raw ST values\n+ *\n+ * This feature requires two preconditions:\n+ * 1. Global enable_unsafe_tph module parameter is enabled;\n+ * 2. VFIO_DEVICE_FEATURE_TPH_ENABLE has been SET on the device beforehand.\n+ */\n+#define VFIO_DEVICE_FEATURE_TPH_ST_CONFIG\t14\n+\n+struct vfio_device_feature_tph_st_config {\n+\t__u16 index;\n+\t__u16 count;\n+\t__u32 reserved; /* Reserved for future use, must be zero */\n+\t__aligned_u64 data_uptr;\n+};\n+\n /* -------- API for Type1 VFIO IOMMU -------- */\n \n /**\n"
}

2/1 2026/06/22 08:14 agent patch-evaluator
2m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": [
    "PCIE_TPH"
  ],
  "FocusSymbols": [
    "vfio_pci_core_feature_tph_enable",
    "vfio_pci_core_feature_tph_st_config",
    "vfio_tph_config_write",
    "vfio_pci_tph_init",
    "pci_tph_init",
    "enable_tph",
    "tph_cpu_st_read"
  ],
  "Reasoning": "This patch introduces PCIe TPH (Transaction Processing Hints) support to VFIO PCI and adds a sysfs attribute for CPU-to-ST mapping. The VFIO ioctls and config space handlers are reachable and should be fuzzed, but they require the `vfio_pci.enable_unsafe_tph=1` module parameter to be set on the kernel command line. The core PCI TPH initialization is reachable during virtual PCI device probe.",
  "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 a82543a1ed09a26d646658202437d614735b7387
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Jun 22 08:14:42 2026 +0000

    syz-cluster: applied patch under review

diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci
index b767db2c52cb70..edc64e4e56404e 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci
+++ b/Documentation/ABI/testing/sysfs-bus-pci
@@ -702,3 +702,27 @@ Description:
 		When present and the tsm/ attribute directory is present, the
 		authenticated attribute is an alias for the device 'connect'
 		state. See the 'tsm/connect' attribute for more details.
+
+What:		/sys/bus/pci/devices/<root-port-bdf>/tph_cpu_st
+Contact:	linux-pci@vger.kernel.org
+Description:
+		Read-only binary attribute only exposed on PCIe Root Ports that
+		support TPH Completer capability and implement the ACPI DSM
+		method for CPU-to-ST mapping. File permission is root-only
+		(0400).
+
+		The blob is a sequence of fixed-size 8-byte entries defined by
+		struct pci_tph_cpu_st in uapi/linux/pci.h:
+		  __u8 vm_st;
+		  __u8 pm_st;
+		  __u16 vm_xst;
+		  __u16 pm_xst;
+		  __u16 reserved;
+
+		Each entry corresponds to a logical CPU index. Seek offset =
+		cpu_id * PCI_TPH_CPU_ST_ENTRY_SZ. Arbitrary unaligned partial
+		reads are supported; no alignment restriction enforced.
+
+		For CPUs outside cpu_possible_mask or offline CPUs, the entry
+		is filled with all zeros to avoid breaking sequential dump tools
+		like cat/hexdump on sparse CPU topologies.
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index d37860841260ce..ad9e4e8d320b07 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -1832,6 +1832,9 @@ const struct attribute_group *pci_dev_attr_groups[] = {
 #ifdef CONFIG_PCI_TSM
 	&pci_tsm_auth_attr_group,
 	&pci_tsm_attr_group,
+#endif
+#ifdef CONFIG_PCIE_TPH
+	&pcie_tph_cpu_st_attr_group,
 #endif
 	NULL,
 };
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index e8ad27abb1cfe4..1abe7fa1fcc7ed 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -1375,6 +1375,10 @@ static inline pci_power_t acpi_pci_choose_state(struct pci_dev *pdev)
 extern const struct attribute_group aspm_ctrl_attr_group;
 #endif
 
+#ifdef CONFIG_PCIE_TPH
+extern const struct attribute_group pcie_tph_cpu_st_attr_group;
+#endif
+
 #ifdef CONFIG_X86_INTEL_MID
 bool pci_use_mid_pm(void);
 int mid_pci_set_power_state(struct pci_dev *pdev, pci_power_t state);
diff --git a/drivers/pci/tph.c b/drivers/pci/tph.c
index 91145e8d9d9512..47402d4b88998f 100644
--- a/drivers/pci/tph.c
+++ b/drivers/pci/tph.c
@@ -11,6 +11,7 @@
 #include <linux/msi.h>
 #include <linux/bitfield.h>
 #include <linux/pci-tph.h>
+#include <linux/sched.h>
 
 #include "pci.h"
 
@@ -130,8 +131,47 @@ static acpi_status tph_invoke_dsm(acpi_handle handle, u32 cpu_uid,
 
 	return AE_OK;
 }
+
+static int tph_get_cpu_st_info(struct pci_dev *pdev, unsigned int cpu,
+			       union st_info *info)
+{
+	acpi_handle rp_acpi_handle;
+	struct pci_dev *rp;
+	u32 cpu_uid;
+	int ret;
+
+	ret = acpi_get_cpu_uid(cpu, &cpu_uid);
+	if (ret != 0)
+		return ret;
+
+	rp = pcie_find_root_port(pdev);
+	if (!rp || !rp->bus || !rp->bus->bridge)
+		return -ENODEV;
+
+	rp_acpi_handle = ACPI_HANDLE(rp->bus->bridge);
+	if (tph_invoke_dsm(rp_acpi_handle, cpu_uid, info) != AE_OK)
+		return -EINVAL;
+
+	return 0;
+}
 #endif
 
+static bool tph_dsm_supported(struct pci_dev *pdev)
+{
+#ifdef CONFIG_ACPI
+	struct pci_dev *rp = pcie_find_root_port(pdev);
+	acpi_handle rp_acpi_handle;
+
+	if (!rp || !rp->bus || !rp->bus->bridge)
+		return false;
+
+	rp_acpi_handle = ACPI_HANDLE(rp->bus->bridge);
+	return acpi_check_dsm(rp_acpi_handle, &pci_acpi_dsm_guid, 7,
+			      BIT(TPH_ST_DSM_FUNC_INDEX));
+#endif
+	return false;
+}
+
 /* Update the TPH Requester Enable field of TPH Control Register */
 static void set_ctrl_reg_req_en(struct pci_dev *pdev, u8 req_type)
 {
@@ -166,11 +206,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, &reg);
 
-	return FIELD_GET(PCI_TPH_CAP_LOC_MASK, reg);
+	return reg & PCI_TPH_CAP_LOC_MASK;
 }
 EXPORT_SYMBOL(pcie_tph_get_st_table_loc);
 
@@ -185,9 +228,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;
 
@@ -231,6 +271,49 @@ static int write_tag_to_st_table(struct pci_dev *pdev, int index, u16 tag)
 	return pci_write_config_word(pdev, offset, tag);
 }
 
+static int get_cpu_all_st(struct pci_dev *pdev, unsigned int cpu,
+			   struct pci_tph_cpu_st *st)
+{
+#ifdef CONFIG_ACPI
+	union st_info info;
+	int ret;
+
+	ret = tph_get_cpu_st_info(pdev, cpu, &info);
+	if (ret == 0) {
+		st->vm_st = info.vm_st_valid ? info.vm_st : 0;
+		st->pm_st = info.pm_st_valid ? info.pm_st : 0;
+		st->vm_xst = info.vm_xst_valid ? info.vm_xst : 0;
+		st->pm_xst = info.pm_xst_valid ? info.pm_xst : 0;
+	}
+
+	return ret;
+#endif
+	return -ENODEV;
+}
+
+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
+	union st_info info;
+	int ret;
+
+	ret = tph_get_cpu_st_info(pdev, cpu, &info);
+	if (ret != 0)
+		return ret;
+
+	*tag = tph_extract_tag(mem_type, req_type, &info);
+
+	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",
+		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
@@ -247,40 +330,35 @@ static int write_tag_to_st_table(struct pci_dev *pdev, int index, u16 tag)
 int pcie_tph_get_cpu_st(struct pci_dev *pdev, enum tph_mem_type mem_type,
 			unsigned int cpu, u16 *tag)
 {
-#ifdef CONFIG_ACPI
-	struct pci_dev *rp;
-	acpi_handle rp_acpi_handle;
-	union st_info info;
-	u32 cpu_uid;
-	int ret;
-
-	ret = acpi_get_cpu_uid(cpu, &cpu_uid);
-	if (ret != 0)
-		return ret;
-
-	rp = pcie_find_root_port(pdev);
-	if (!rp || !rp->bus || !rp->bus->bridge)
-		return -ENODEV;
+	return get_cpu_st(pdev, mem_type, pdev->tph_req_type, cpu, tag);
+}
+EXPORT_SYMBOL(pcie_tph_get_cpu_st);
 
-	rp_acpi_handle = ACPI_HANDLE(rp->bus->bridge);
+/**
+ * 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 (tph_invoke_dsm(rp_acpi_handle, cpu_uid, &info) != AE_OK) {
-		*tag = 0;
+	if (extended && !pdev->tph_ext_support)
 		return -EINVAL;
-	}
-
-	*tag = tph_extract_tag(mem_type, pdev->tph_req_type, &info);
-
-	pci_dbg(pdev, "get steering tag: mem_type=%s, cpu=%d, tag=%#04x\n",
-		(mem_type == TPH_MEM_TYPE_VM) ? "volatile" : "persistent",
-		cpu, *tag);
 
-	return 0;
-#else
-	return -ENODEV;
-#endif
+	return get_cpu_st(pdev, mem_type, req_type, cpu, tag);
 }
-EXPORT_SYMBOL(pcie_tph_get_cpu_st);
+EXPORT_SYMBOL(pcie_tph_get_cpu_st_explicit);
 
 /**
  * pcie_tph_set_st_entry() - Set Steering Tag in the ST table entry
@@ -316,8 +394,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:
@@ -366,27 +442,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)
@@ -404,26 +463,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, &reg);
-	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, &reg);
 
@@ -431,7 +476,7 @@ int pcie_enable_tph(struct pci_dev *pdev, int mode)
 	reg |= FIELD_PREP(PCI_TPH_CTRL_MODE_SEL_MASK, pdev->tph_mode);
 
 	reg &= ~PCI_TPH_CTRL_REQ_EN_MASK;
-	reg |= FIELD_PREP(PCI_TPH_CTRL_REQ_EN_MASK, pdev->tph_req_type);
+	reg |= FIELD_PREP(PCI_TPH_CTRL_REQ_EN_MASK, req_type);
 
 	pci_write_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, reg);
 
@@ -439,8 +484,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;
@@ -512,14 +612,131 @@ 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, &reg);
+	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);
+
+static ssize_t tph_cpu_st_read(struct file *filp, struct kobject *kobj,
+			       const struct bin_attribute *bin_attr, char *buf,
+			       loff_t off, size_t count)
+{
+	struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
+	const size_t entry_sz = PCI_TPH_CPU_ST_ENTRY_SZ;
+	const size_t total_size = nr_cpu_ids * entry_sz;
+	size_t copied = 0;
+	loff_t pos = off;
+
+	if (pos >= total_size)
+		return 0;
+
+	count = min_t(size_t, count, total_size - pos);
+
+	while (copied < count) {
+		unsigned int cpu_idx = pos / entry_sz;
+		size_t entry_off = pos % entry_sz;
+		size_t remain = entry_sz - entry_off;
+		size_t chunk = min_t(size_t, remain, count - copied);
+		struct pci_tph_cpu_st st = {0};
+
+		if (cpu_possible(cpu_idx))
+			get_cpu_all_st(pdev, cpu_idx, &st);
+
+		memcpy(buf + copied, (char *)&st + entry_off, chunk);
+
+		copied += chunk;
+		pos += chunk;
+
+		cond_resched();
+	}
+
+	return copied;
+}
+static BIN_ATTR(tph_cpu_st, 0400, tph_cpu_st_read, NULL, 0);
+
+static const struct bin_attribute *const tph_cpu_st_bin_attrs[] = {
+	&bin_attr_tph_cpu_st,
+	NULL,
+};
+
+static size_t tph_cpu_st_bin_size(struct kobject *kobj,
+				  const struct bin_attribute *a, int n)
+{
+	return nr_cpu_ids * PCI_TPH_CPU_ST_ENTRY_SZ;
+}
+
+static umode_t tph_cpu_st_attr_is_visible(struct kobject *kobj,
+					  const struct bin_attribute *a, int n)
+{
+	struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
+	bool is_root_port = pci_is_pcie(pdev) &&
+				pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT;
+	u32 devcap2 = 0;
+
+	if (!is_root_port)
+		return 0;
+
+	pci_read_config_dword(pdev, PCI_EXP_DEVCAP2, &devcap2);
+	if (!(devcap2 & PCI_EXP_DEVCAP2_TPH_COMP_MASK))
+		return 0;
+
+	if (!tph_dsm_supported(pdev))
+		return 0;
+
+	return a->attr.mode;
+}
+
+const struct attribute_group pcie_tph_cpu_st_attr_group = {
+	.bin_attrs = tph_cpu_st_bin_attrs,
+	.bin_size = tph_cpu_st_bin_size,
+	.is_bin_visible = tph_cpu_st_attr_is_visible,
+};
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index 0c771064c0b844..6d73668459cf55 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -60,6 +60,12 @@ 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.");
 
+#ifdef CONFIG_PCIE_TPH
+static bool enable_unsafe_tph;
+module_param(enable_unsafe_tph, bool, 0444);
+MODULE_PARM_DESC(enable_unsafe_tph, "Enable PCIe TPH (Transaction Processing Hints) support. It may break platform isolation. If you do not know what this is for, step away. (default: false)");
+#endif
+
 static bool vfio_pci_dev_in_denylist(struct pci_dev *pdev)
 {
 	switch (pdev->vendor) {
@@ -257,12 +263,17 @@ static int __init vfio_pci_init(void)
 {
 	int ret;
 	bool is_disable_vga = true;
+	bool is_enable_unsafe_tph = false;
 
 #ifdef CONFIG_VFIO_PCI_VGA
 	is_disable_vga = disable_vga;
 #endif
+#ifdef CONFIG_PCIE_TPH
+	is_enable_unsafe_tph = enable_unsafe_tph;
+#endif
 
-	vfio_pci_core_set_params(nointxmask, is_disable_vga, disable_idle_d3);
+	vfio_pci_core_set_params(nointxmask, is_disable_vga, disable_idle_d3,
+				 is_enable_unsafe_tph);
 
 	/* Register and scan for devices */
 	ret = pci_register_driver(&vfio_pci_driver);
diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
index a10ed733f0e3af..10f4e9fabea7ca 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,118 @@ 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, ALL_WRITE);
+
+	/* 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_mask_ext_tph_bit(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;
+
+	if (!pcie_tph_supported(pdev, true)) {
+		val = le32_to_cpu(*vptr);
+		val &= ~PCI_TPH_CAP_EXT_TPH;
+		*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)
+{
+	int req_en_byte = PCI_TPH_CTRL + 1;
+	struct pci_dev *pdev = vdev->pdev;
+	__le32 org_val = 0;
+	bool extended;
+	u8 mode, req;
+	int i, ret;
+	u16 start;
+	u32 data;
+
+	if (!vdev->tph_permit)
+		return count;
+
+	down_write(&vdev->memory_lock);
+
+	/* Back up the original values in order rollback when fail */
+	if (offset <= req_en_byte && offset + count > req_en_byte)
+		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) {
+		up_write(&vdev->memory_lock);
+		return ret;
+	}
+
+	/* Skip if write range does not cover Requester Enable byte */
+	if (offset > req_en_byte || offset + count <= req_en_byte) {
+		up_write(&vdev->memory_lock);
+		return count;
+	}
+
+	ret = vfio_pci_set_power_state(vdev, PCI_D0);
+	if (ret) {
+		vfio_default_config_write(vdev, pos, count, perm, offset,
+					  org_val);
+		up_write(&vdev->memory_lock);
+		return count;
+	}
+
+	start = vfio_find_cap_start(vdev, pos);
+	data = le32_to_cpu(*(__le32 *)&vdev->vconfig[start + PCI_TPH_CTRL]);
+	mode = FIELD_GET(PCI_TPH_CTRL_MODE_SEL_MASK, data);
+	req = FIELD_GET(PCI_TPH_CTRL_REQ_EN_MASK, data);
+
+	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 && vdev->tph_st_shadow) {
+			for (i = 0; i < vdev->tph_st_entries; i++)
+				pcie_tph_set_st_entry(pdev, i,
+						      vdev->tph_st_shadow[i]);
+		}
+		if (ret)
+			vfio_default_config_write(vdev, pos, count, perm,
+						  offset, org_val);
+	} else if (req == PCI_TPH_REQ_DISABLE) {
+		pcie_disable_tph(vdev->pdev);
+	} else {
+		vfio_default_config_write(vdev, pos, count, perm, offset,
+					  org_val);
+	}
+
+	up_write(&vdev->memory_lock);
+
+	return count;
+}
+
 /*
  * Initialize the shared permission tables
  */
@@ -1100,6 +1213,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 +1234,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 +1566,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 +1819,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_mask_ext_tph_bit(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 a28f1e99362c1e..1e706a690dbd75 100644
--- a/drivers/vfio/pci/vfio_pci_core.c
+++ b/drivers/vfio/pci/vfio_pci_core.c
@@ -29,6 +29,7 @@
 #include <linux/sched/mm.h>
 #include <linux/iommufd.h>
 #include <linux/pci-p2pdma.h>
+#include <linux/pci-tph.h>
 #if IS_ENABLED(CONFIG_EEH)
 #include <asm/eeh.h>
 #endif
@@ -41,6 +42,7 @@
 static bool nointxmask;
 static bool disable_vga;
 static bool disable_idle_d3;
+static bool enable_unsafe_tph;
 
 static void vfio_pci_eventfd_rcu_free(struct rcu_head *rcu)
 {
@@ -531,6 +533,52 @@ static const struct dev_pm_ops vfio_pci_core_pm_ops = {
 			   NULL)
 };
 
+static int vfio_pci_tph_st_shadow_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 vfio_pci_tph_init(struct vfio_pci_core_device *vdev)
+{
+	vdev->tph_st_entries = 0;
+	vdev->tph_st_shadow = NULL;
+	vdev->tph_permit = false;
+
+	if (!enable_unsafe_tph)
+		return 0;
+
+	vdev->tph_st_entries = vfio_pci_tph_st_shadow_size(vdev);
+	if (vdev->tph_st_entries) {
+		vdev->tph_st_shadow = kcalloc(vdev->tph_st_entries, sizeof(u16),
+					      GFP_KERNEL_ACCOUNT);
+		if (!vdev->tph_st_shadow)
+			return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void vfio_pci_tph_deinit(struct vfio_pci_core_device *vdev)
+{
+	kfree(vdev->tph_st_shadow);
+	vdev->tph_st_shadow = NULL;
+	vdev->tph_st_entries = 0;
+	vdev->tph_permit = false;
+}
+
 int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
 {
 	struct pci_dev *pdev = vdev->pdev;
@@ -557,6 +605,13 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
 		goto out_disable_device;
 
 	vdev->reset_works = !ret;
+
+	/* Reset TPH status on new user session */
+	pcie_disable_tph(vdev->pdev);
+	ret = vfio_pci_tph_init(vdev);
+	if (ret)
+		goto out_disable_device;
+
 	pci_save_state(pdev);
 	vdev->pci_saved_state = pci_store_saved_state(pdev);
 	if (!vdev->pci_saved_state)
@@ -614,6 +669,7 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
 out_free_state:
 	kfree(vdev->pci_saved_state);
 	vdev->pci_saved_state = NULL;
+	vfio_pci_tph_deinit(vdev);
 out_disable_device:
 	pci_disable_device(pdev);
 out_power:
@@ -682,6 +738,9 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev)
 	kfree(vdev->region);
 	vdev->region = NULL; /* don't krealloc a freed pointer */
 
+	/* Reset TPH status on session exit */
+	pcie_disable_tph(vdev->pdev);
+	vfio_pci_tph_deinit(vdev);
 	vfio_config_free(vdev);
 
 	for (i = 0; i < PCI_STD_NUM_BARS; i++) {
@@ -1554,6 +1613,95 @@ static int vfio_pci_core_feature_token(struct vfio_pci_core_device *vdev,
 	return 0;
 }
 
+static int vfio_pci_core_feature_tph_enable(struct vfio_pci_core_device *vdev,
+					    u32 flags, size_t argsz)
+{
+	int ret;
+
+	if (!enable_unsafe_tph)
+		return -EOPNOTSUPP;
+
+	ret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_SET, 0);
+	if (ret <= 0)
+		return ret;
+
+	if (!vdev->tph_permit)
+		vdev->tph_permit = 1;
+
+	return 0;
+}
+
+static int vfio_pci_core_feature_tph_st_config(
+			struct vfio_pci_core_device *vdev,
+			u32 flags,
+			struct vfio_device_feature_tph_st_config __user *arg,
+			size_t argsz)
+{
+	struct vfio_device_feature_tph_st_config config;
+	struct pci_dev *pdev = vdev->pdev;
+	void __user *uptr;
+	int i, idx, ret;
+	size_t sz;
+	u16 *sts;
+
+	if (!enable_unsafe_tph)
+		return -EOPNOTSUPP;
+
+	ret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_SET,
+				 sizeof(config));
+	if (ret <= 0)
+		return ret;
+
+	if (!vdev->tph_permit || !vdev->tph_st_shadow)
+		return -EINVAL;
+
+	if (copy_from_user(&config, arg, sizeof(config)))
+		return -EFAULT;
+
+	if (config.count == 0 || config.reserved != 0 ||
+		config.index >= vdev->tph_st_entries ||
+		config.count > vdev->tph_st_entries - config.index)
+		return -EINVAL;
+
+	uptr = u64_to_user_ptr(config.data_uptr);
+	sts = memdup_array_user(uptr, config.count, sizeof(u16));
+	sz = config.count * sizeof(u16);
+	if (IS_ERR(sts))
+		return PTR_ERR(sts);
+
+	down_write(&vdev->memory_lock);
+	ret = vfio_pci_set_power_state(vdev, PCI_D0);
+	if (ret)
+		goto out_unlock_memory;
+
+	if (pcie_tph_enabled_req_type(pdev) == PCI_TPH_REQ_DISABLE)
+		goto update_shadow;
+
+	for (i = 0; i < config.count; i++) {
+		idx = config.index + i;
+		ret = pcie_tph_set_st_entry(pdev, idx, sts[i]);
+		if (ret)
+			goto rollback;
+	}
+
+update_shadow:
+	memcpy(&vdev->tph_st_shadow[config.index], sts, sz);
+	ret = 0;
+	goto out_unlock_memory;
+
+rollback:
+	while (i-- > 0) {
+		idx = config.index + i;
+		pcie_tph_set_st_entry(pdev, idx, vdev->tph_st_shadow[idx]);
+	}
+
+out_unlock_memory:
+	up_write(&vdev->memory_lock);
+
+	kfree(sts);
+	return ret;
+}
+
 int vfio_pci_core_ioctl_feature(struct vfio_device *device, u32 flags,
 				void __user *arg, size_t argsz)
 {
@@ -1572,6 +1720,11 @@ 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_ENABLE:
+		return vfio_pci_core_feature_tph_enable(vdev, flags, argsz);
+	case VFIO_DEVICE_FEATURE_TPH_ST_CONFIG:
+		return vfio_pci_core_feature_tph_st_config(vdev, flags,
+							   arg, argsz);
 	default:
 		return -ENOTTY;
 	}
@@ -2615,11 +2768,13 @@ static void vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set)
 }
 
 void vfio_pci_core_set_params(bool is_nointxmask, bool is_disable_vga,
-			      bool is_disable_idle_d3)
+			      bool is_disable_idle_d3,
+			      bool is_enable_unsafe_tph)
 {
 	nointxmask = is_nointxmask;
 	disable_vga = is_disable_vga;
 	disable_idle_d3 = is_disable_idle_d3;
+	enable_unsafe_tph = is_enable_unsafe_tph;
 }
 EXPORT_SYMBOL_GPL(vfio_pci_core_set_params);
 
diff --git a/include/linux/pci-tph.h b/include/linux/pci-tph.h
index be68cd17f2f833..5917a0694c1d66 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,16 @@ 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);
 #else
 static inline int pcie_tph_set_st_entry(struct pci_dev *pdev,
 					unsigned int index, u16 tag)
@@ -38,9 +45,24 @@ 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; }
 #endif
 
 #endif /* LINUX_PCI_TPH_H */
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 2c4454583c115b..285c0f00882e0e 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -471,7 +471,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 */
@@ -589,7 +588,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 5fc6ce4dd78616..527c84f042aa6f 100644
--- a/include/linux/vfio_pci_core.h
+++ b/include/linux/vfio_pci_core.h
@@ -128,6 +128,7 @@ struct vfio_pci_core_device {
 	bool			pm_intx_masked:1;
 	bool			pm_runtime_engaged:1;
 	bool			sriov_active;
+	bool			tph_permit;
 	struct pci_saved_state	*pci_saved_state;
 	struct pci_saved_state	*pm_save;
 	int			ioeventfds_nr;
@@ -143,6 +144,8 @@ struct vfio_pci_core_device {
 	struct notifier_block	nb;
 	struct rw_semaphore	memory_lock;
 	struct list_head	dmabufs;
+	u16			*tph_st_shadow;
+	u16			tph_st_entries;
 };
 
 enum vfio_pci_io_width {
@@ -158,7 +161,8 @@ int vfio_pci_core_register_dev_region(struct vfio_pci_core_device *vdev,
 				      const struct vfio_pci_regops *ops,
 				      size_t size, u32 flags, void *data);
 void vfio_pci_core_set_params(bool nointxmask, bool is_disable_vga,
-			      bool is_disable_idle_d3);
+			      bool is_disable_idle_d3,
+			      bool is_enable_unsafe_tph);
 void vfio_pci_core_close_device(struct vfio_device *core_vdev);
 int vfio_pci_core_init_dev(struct vfio_device *core_vdev);
 void vfio_pci_core_release_dev(struct vfio_device *core_vdev);
diff --git a/include/uapi/linux/pci.h b/include/uapi/linux/pci.h
index 4f150028965d8b..5c4ea44d66d206 100644
--- a/include/uapi/linux/pci.h
+++ b/include/uapi/linux/pci.h
@@ -19,6 +19,7 @@
 #define _UAPILINUX_PCI_H
 
 #include <linux/pci_regs.h>	/* The pci register defines */
+#include <linux/types.h>
 
 /*
  * The PCI interface treats multi-function devices as independent
@@ -46,4 +47,19 @@ enum pci_hotplug_event {
 	PCI_HOTPLUG_CARD_NOT_PRESENT,
 };
 
+/*
+ * PCIe TPH sysfs binary entry for CPU-to-ST mapping
+ * Sysfs file: /sys/bus/pci/devices/<BDF>/tph_cpu_st
+ * Each entry is 8 bytes aligned, seek offset = cpu_id * PCI_TPH_CPU_ST_ENTRY_SZ
+ */
+struct pci_tph_cpu_st {
+	__u8  vm_st;        /* Volatile Memory Steering Tag (1 byte) */
+	__u8  pm_st;        /* Persistent Memory Steering Tag (1 byte) */
+	__u16 vm_xst;       /* Volatile Memory Extended Steering Tag (2 bytes) */
+	__u16 pm_xst;       /* Persistent Memory Extended Steering Tag (2 bytes) */
+	__u16 reserved;     /* Padding to 8 bytes for aligned offset lookup */
+} __packed;
+
+#define PCI_TPH_CPU_ST_ENTRY_SZ sizeof(struct pci_tph_cpu_st)
+
 #endif /* _UAPILINUX_PCI_H */
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index 5de618a3a5eeee..61079594a91f5a 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -1534,6 +1534,35 @@ struct vfio_device_feature_dma_buf {
  */
 #define VFIO_DEVICE_FEATURE_MIG_PRECOPY_INFOv2  12
 
+/*
+ * Device-level opt-in for TPH (Transaction Processing Hints) support.
+ * When set, allows access to TPH_CPU_ST and TPH_ST_CONFIG features.
+ * Requires global enable_unsafe_tph module parameter to be enabled.
+ */
+#define VFIO_DEVICE_FEATURE_TPH_ENABLE	13
+
+/**
+ * VFIO_DEVICE_FEATURE_TPH_ST_CONFIG - Configure PCIe TPH Steering Tag entries
+ *
+ * Provides userspace interface to configure PCIe TPH ST table entries.
+ *
+ * @index: Start entry offset within ST table
+ * @count: Number of consecutive entries to configure
+ * @data_uptr: Userspace data buffer for 16-bit raw ST values
+ *
+ * This feature requires two preconditions:
+ * 1. Global enable_unsafe_tph module parameter is enabled;
+ * 2. VFIO_DEVICE_FEATURE_TPH_ENABLE has been SET on the device beforehand.
+ */
+#define VFIO_DEVICE_FEATURE_TPH_ST_CONFIG	14
+
+struct vfio_device_feature_tph_st_config {
+	__u16 index;
+	__u16 count;
+	__u32 reserved; /* Reserved for future use, must be zero */
+	__aligned_u64 data_uptr;
+};
+
 /* -------- API for Type1 VFIO IOMMU -------- */
 
 /**

3/2 2026/06/22 08:14 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 17404
							output: 20
							thoughts: 343
						

Thoughts:
**Analyzing the New PCIe TPH VFIO Patch Series**

Okay, so I'm looking at this patch series that introduces PCIe TPH support within VFIO. It seems like the core changes are in `drivers/pci/tph.c` which now contains the functions for retrieving CPU ST information, explicitly enabling or disabling TPH, and exposes this information via the `tph_cpu_st` sysfs attribute. That's straightforward enough.

The real meat of the changes appears in the VFIO PCI code: `vfio_pci.c`, `vfio_pci_config.c`, and `vfio_pci_core.c`. This is where the TPH support is being integrated, and it's controlled by a module parameter, `enable_unsafe_tph`. This parameter flags a potential risk with enabling this behavior, so it is necessary to consider the full implications of this support. Now, they've also added these new VFIO device features: `VFIO_DEVICE_FEATURE_TPH_ENABLE` and `VFIO_DEVICE_FEATURE_TPH_ST_CONFIG`. That's a reasonable approach to expose this functionality to the user space.

Now, the question is, how does this all play out in virtualized environments like GCE or QEMU? Obviously, VFIO PCI is only reachable if you have a PCI device passed through directly. Alternatively, if a user makes use of `vfio-pci` with a dummy device, or a virtual device, you could test the changes.

Hold on, this `enable_unsafe_tph` parameter is specific to `vfio_pci`. That's important to remember.

Okay, I need to check something quickly. Are we even building this stuff into our fuzzing kernels? First, is `CONFIG_PCIE_TPH` even enabled? Let's verify that. Wait, that's a configuration option, not a module, so I need to check the kernel config directly. Next, I should check that `CONFIG_VFIO_PCI` is definitely enabled, as that's the core dependency. Time to break out the tools and run some quick config checks.



4/2 2026/06/22 08:14 tool grepper
0m Args:
{
  "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/06/22 08:14 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 18316
							output: 19
							thoughts: 28
						

Thoughts:
Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:

**Kernel Configuration Verification**

Alright, so `CONFIG_PCIE_TPH`, that's definitely a legitimate kernel configuration option, isn't it? Good. I just want to quickly verify its context before moving on. Now, let me see... I'm really curious about how it interacts with `CONFIG_VFIO_PCI`. I want to investigate the dependency or potential conflicts between the two. What's the relationship here? Are they mutually exclusive? Does one implicitly require the other? I'm already formulating some hypotheses about potential implications for IOMMU configuration and device passthrough scenarios. Let's dig in a bit further to see how these options interact, especially within the context of hardware virtualization.



6/2 2026/06/22 08:15 tool grepper
0m Args:
{
  "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"
}

7/2 2026/06/22 08:15 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19073
							output: 25
							thoughts: 322
						

Thoughts:
**Evaluating the Fuzzing Potential of New VFIO IOCTLs**

Okay, so this patch introduces `VFIO_DEVICE_FEATURE_TPH_ENABLE` and `VFIO_DEVICE_FEATURE_TPH_ST_CONFIG` ioctls. These are handled within the `vfio_pci_core_ioctl_feature` function, and crucially, they depend on the `enable_unsafe_tph` module parameter. My initial thought is, "Can syzkaller easily reach this new code?" Typically, module parameters in syzkaller require a configuration change, which adds overhead. We could try sysfs but it is not exposed this way, so it is unlikely.

But, the real question is whether it is worth fuzzing. The prompt reminds me to return `WorthFuzzing=false` if the patch concerns unreachable code paths within virtualized environments like QEMU. However, VFIO PCI _is_ reachable in QEMU, right? Syzkaller has the primitives to do that with `syz_kvm_setup_cpu` and so on, and I know it has fuzzing capabilities for `vfio`. In fact, it has *descriptions* for `vfio`.

Therefore, the obvious next step is to see if `vfio_pci` itself is being actively fuzzed by syzkaller. If it is, and these new ioctls are integrated into the existing VFIO fuzzing strategy, that adds significant value. If it isn't, the effort would be worth more discussion, as the likelihood of hitting this code path decreases.



8/2 2026/06/22 08:15 tool grepper
0m Args:
{
  "Expression": "vfio_pci_core_ioctl_feature"
}

Results:
{
  "Output": "drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c=1581=static const struct vfio_device_ops hisi_acc_vfio_pci_migrn_ops = {\n--\ndrivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c-1588-\t.get_region_info_caps = hisi_acc_vfio_ioctl_get_region,\ndrivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c:1589:\t.device_feature = vfio_pci_core_ioctl_feature,\ndrivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c-1590-\t.read = hisi_acc_vfio_pci_read,\n--\ndrivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c=1602=static const struct vfio_device_ops hisi_acc_vfio_pci_ops = {\n--\ndrivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c-1609-\t.get_region_info_caps = vfio_pci_ioctl_get_region_info,\ndrivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c:1610:\t.device_feature = vfio_pci_core_ioctl_feature,\ndrivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c-1611-\t.read = vfio_pci_core_read,\n--\ndrivers/vfio/pci/ism/main.c=335=static const struct vfio_device_ops ism_pci_ops = {\n--\ndrivers/vfio/pci/ism/main.c-342-\t.get_region_info_caps = ism_vfio_pci_ioctl_get_region_info,\ndrivers/vfio/pci/ism/main.c:343:\t.device_feature = vfio_pci_core_ioctl_feature,\ndrivers/vfio/pci/ism/main.c-344-\t.read = ism_vfio_pci_read,\n--\ndrivers/vfio/pci/mlx5/main.c=1386=static const struct vfio_device_ops mlx5vf_pci_ops = {\n--\ndrivers/vfio/pci/mlx5/main.c-1393-\t.get_region_info_caps = vfio_pci_ioctl_get_region_info,\ndrivers/vfio/pci/mlx5/main.c:1394:\t.device_feature = vfio_pci_core_ioctl_feature,\ndrivers/vfio/pci/mlx5/main.c-1395-\t.read = vfio_pci_core_read,\n--\ndrivers/vfio/pci/nvgrace-gpu/main.c=1064=static const struct vfio_device_ops nvgrace_gpu_pci_ops = {\n--\ndrivers/vfio/pci/nvgrace-gpu/main.c-1071-\t.get_region_info_caps = nvgrace_gpu_ioctl_get_region_info,\ndrivers/vfio/pci/nvgrace-gpu/main.c:1072:\t.device_feature\t= vfio_pci_core_ioctl_feature,\ndrivers/vfio/pci/nvgrace-gpu/main.c-1073-\t.read\t\t= nvgrace_gpu_read,\n--\ndrivers/vfio/pci/nvgrace-gpu/main.c=1089=static const struct vfio_device_ops nvgrace_gpu_pci_core_ops = {\n--\ndrivers/vfio/pci/nvgrace-gpu/main.c-1096-\t.get_region_info_caps = vfio_pci_ioctl_get_region_info,\ndrivers/vfio/pci/nvgrace-gpu/main.c:1097:\t.device_feature\t= vfio_pci_core_ioctl_feature,\ndrivers/vfio/pci/nvgrace-gpu/main.c-1098-\t.read\t\t= vfio_pci_core_read,\n--\ndrivers/vfio/pci/pds/vfio_dev.c=191=static const struct vfio_device_ops pds_vfio_ops = {\n--\ndrivers/vfio/pci/pds/vfio_dev.c-198-\t.get_region_info_caps = vfio_pci_ioctl_get_region_info,\ndrivers/vfio/pci/pds/vfio_dev.c:199:\t.device_feature = vfio_pci_core_ioctl_feature,\ndrivers/vfio/pci/pds/vfio_dev.c-200-\t.read = vfio_pci_core_read,\n--\ndrivers/vfio/pci/vfio_pci.c=134=static const struct vfio_device_ops vfio_pci_ops = {\n--\ndrivers/vfio/pci/vfio_pci.c-141-\t.get_region_info_caps = vfio_pci_ioctl_get_region_info,\ndrivers/vfio/pci/vfio_pci.c:142:\t.device_feature = vfio_pci_core_ioctl_feature,\ndrivers/vfio/pci/vfio_pci.c-143-\t.read\t\t= vfio_pci_core_read,\n--\ndrivers/vfio/pci/vfio_pci_core.c=1634=static int vfio_pci_core_feature_tph_st_config(\n--\ndrivers/vfio/pci/vfio_pci_core.c-1704-\ndrivers/vfio/pci/vfio_pci_core.c:1705:int vfio_pci_core_ioctl_feature(struct vfio_device *device, u32 flags,\ndrivers/vfio/pci/vfio_pci_core.c-1706-\t\t\t\tvoid __user *arg, size_t argsz)\n--\ndrivers/vfio/pci/vfio_pci_core.c-1731-}\ndrivers/vfio/pci/vfio_pci_core.c:1732:EXPORT_SYMBOL_GPL(vfio_pci_core_ioctl_feature);\ndrivers/vfio/pci/vfio_pci_core.c-1733-\n--\ndrivers/vfio/pci/virtio/main.c=84=static const struct vfio_device_ops virtiovf_vfio_pci_lm_ops = {\n--\ndrivers/vfio/pci/virtio/main.c-91-\t.get_region_info_caps = vfio_pci_ioctl_get_region_info,\ndrivers/vfio/pci/virtio/main.c:92:\t.device_feature = vfio_pci_core_ioctl_feature,\ndrivers/vfio/pci/virtio/main.c-93-\t.read = vfio_pci_core_read,\n--\ndrivers/vfio/pci/virtio/main.c=106=static const struct vfio_device_ops virtiovf_vfio_pci_tran_lm_ops = {\n--\ndrivers/vfio/pci/virtio/main.c-113-\t.get_region_info_caps = virtiovf_pci_ioctl_get_region_info,\ndrivers/vfio/pci/virtio/main.c:114:\t.device_feature = vfio_pci_core_ioctl_feature,\ndrivers/vfio/pci/virtio/main.c-115-\t.read = virtiovf_pci_core_read,\n--\ndrivers/vfio/pci/virtio/main.c=128=static const struct vfio_device_ops virtiovf_vfio_pci_ops = {\n--\ndrivers/vfio/pci/virtio/main.c-135-\t.get_region_info_caps = vfio_pci_ioctl_get_region_info,\ndrivers/vfio/pci/virtio/main.c:136:\t.device_feature = vfio_pci_core_ioctl_feature,\ndrivers/vfio/pci/virtio/main.c-137-\t.read = vfio_pci_core_read,\n--\ndrivers/vfio/pci/xe/main.c=522=static const struct vfio_device_ops xe_vfio_pci_ops = {\n--\ndrivers/vfio/pci/xe/main.c-529-\t.get_region_info_caps = vfio_pci_ioctl_get_region_info,\ndrivers/vfio/pci/xe/main.c:530:\t.device_feature = vfio_pci_core_ioctl_feature,\ndrivers/vfio/pci/xe/main.c-531-\t.read = vfio_pci_core_read,\n--\ninclude/linux/vfio_pci_core.h=174=long vfio_pci_core_ioctl(struct vfio_device *core_vdev, unsigned int cmd,\ninclude/linux/vfio_pci_core.h-175-\t\tunsigned long arg);\ninclude/linux/vfio_pci_core.h:176:int vfio_pci_core_ioctl_feature(struct vfio_device *device, u32 flags,\ninclude/linux/vfio_pci_core.h-177-\t\t\t\tvoid __user *arg, size_t argsz);\n"
}

9/2 2026/06/22 08:15 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 22232
							output: 21
							thoughts: 1619
						

Thoughts:
**Analysis of PCIe TPH Code Path Reachability**

Okay, so I'm looking at this `vfio_pci_core_ioctl_feature` function, and it seems reachable. But there's this `enable_unsafe_tph` module parameter that's the gatekeeper. By default, it's `false`, which is a problem. Syzkaller can't easily enable module parameters unless they're in the config or writable via sysfs, and this one is read-only. We're evaluating if the patch makes functional changes to the kernel that *should* be fuzzed, and this looks tricky. If it requires a module parameter, we should probably mention it in the reasoning.

The prompt says to return `WorthFuzzing=false` if the code is unreachable in a virtualized environment. With the module parameter disabled, the code is indeed unreachable. But if we *wanted* to fuzz it, we'd need to enable that parameter somehow. Syzkaller doesn't have a direct way to do that in the `EnableConfigs`.

Now, there's a sysfs attribute, `tph_cpu_st`, added in `drivers/pci/tph.c`. It's exposed on PCIe Root Ports with TPH Completer capability and ACPI DSM support. In QEMU, does the PCIe Root Port support this? It might not. Even if it did, the `tph_cpu_st_attr_is_visible` function checks for the TPH Completer capability and ACPI DSM support. If QEMU doesn't have that, the sysfs file won't be visible.

Looking at the VFIO PCI part, if `enable_unsafe_tph` is false, `vfio_pci_core_feature_tph_enable` returns `-EOPNOTSUPP`. That means the ioctl will just return an error. In `vfio_pci_config.c`, `vdev->tph_permit` is only set in `vfio_pci_core_feature_tph_enable`, which requires the module parameter. So, all the VFIO TPH code paths are gated.

Since `enable_unsafe_tph` is read-only and defaults to false, it can't be enabled at runtime. Syzkaller can pass boot parameters, but not in this `set-results` situation. I can't even tell Syzkaller to pass `vfio_pci.enable_unsafe_tph=1`.

If the code is unreachable without a disabled-by-default module parameter, is it "impossible to reach"? Yes, pretty much, unless that parameter is specified. But wait, `CONFIG_PCIE_TPH` is a config option, we can enable it... is it still worth fuzzing then? Yes, but only if we can find a way to specify the param.

What if `vfio_pci` is built-in? Then the kernel command line parameter `vfio_pci.enable_unsafe_tph=1` can be passed... If it's a module, it's a module parameter. Since we can't specify a kernel command line parameter here, it's a bit of a dilemma.

Syzkaller does have `kernel_cmdline` in its config, but we can't set that here. The prompt is still relevant though: "Code paths that are impossible to reach". Without that parameter, it *is* unreachable, but we could try other methods.

Maybe we should say it's `WorthFuzzing=true` and let the syzkaller maintainers figure it out?

Wait, what about the exported symbols: `pcie_tph_get_cpu_st`, `pcie_tph_set_st_entry`, `pcie_disable_tph`, `pcie_enable_tph`. Are they used by any other drivers?



10/2 2026/06/22 08:15 tool grepper
0m Args:
{
  "Expression": "pcie_enable_tph"
}

Results:
{
  "Output": "Documentation/PCI/tph.rst=48=To enable TPH for a device, use the following function::\nDocumentation/PCI/tph.rst-49-\nDocumentation/PCI/tph.rst:50:  int pcie_enable_tph(struct pci_dev *pdev, int mode);\nDocumentation/PCI/tph.rst-51-\n--\nDocumentation/PCI/tph.rst=53=Current supported modes include:\n--\nDocumentation/PCI/tph.rst-58-\nDocumentation/PCI/tph.rst:59:`pcie_enable_tph()` checks whether the requested mode is actually\nDocumentation/PCI/tph.rst-60-supported by the device before enabling. The device driver can figure out\nDocumentation/PCI/tph.rst=61=which TPH mode is supported and can be properly enabled based on the\nDocumentation/PCI/tph.rst:62:return value of `pcie_enable_tph()`.\nDocumentation/PCI/tph.rst-63-\n--\ndrivers/net/ethernet/broadcom/bnxt/bnxt.c=11842=static int bnxt_request_irq(struct bnxt *bp)\n--\ndrivers/net/ethernet/broadcom/bnxt/bnxt.c-11858-\t/* Enable TPH support as part of IRQ request */\ndrivers/net/ethernet/broadcom/bnxt/bnxt.c:11859:\trc = pcie_enable_tph(bp-\u003epdev, PCI_TPH_ST_IV_MODE);\ndrivers/net/ethernet/broadcom/bnxt/bnxt.c-11860-\tif (!rc)\n--\ndrivers/net/ethernet/mellanox/mlx5/core/lib/st.c=25=struct mlx5_st *mlx5_st_create(struct mlx5_core_dev *dev)\n--\ndrivers/net/ethernet/mellanox/mlx5/core/lib/st.c-57-\t/* The OS doesn't support ST */\ndrivers/net/ethernet/mellanox/mlx5/core/lib/st.c:58:\tret = pcie_enable_tph(pdev, PCI_TPH_ST_DS_MODE);\ndrivers/net/ethernet/mellanox/mlx5/core/lib/st.c-59-\tif (ret)\n--\ndrivers/pci/tph.c=445=static int enable_tph(struct pci_dev *pdev, int mode, u8 req_type)\n--\ndrivers/pci/tph.c-488-/**\ndrivers/pci/tph.c:489: * pcie_enable_tph - Enable TPH support for device using a specific ST mode\ndrivers/pci/tph.c-490- * @pdev: PCI device\n--\ndrivers/pci/tph.c-503- */\ndrivers/pci/tph.c:504:int pcie_enable_tph(struct pci_dev *pdev, int mode)\ndrivers/pci/tph.c-505-{\n--\ndrivers/pci/tph.c-509-}\ndrivers/pci/tph.c:510:EXPORT_SYMBOL(pcie_enable_tph);\ndrivers/pci/tph.c-511-\ndrivers/pci/tph.c-512-/**\ndrivers/pci/tph.c:513: * pcie_enable_tph_explicit - Enable TPH with explicit requester selection\ndrivers/pci/tph.c-514- * @pdev: PCI device to operate\n--\ndrivers/pci/tph.c-517- *\ndrivers/pci/tph.c:518: * Unlike auto-detecting pcie_enable_tph(), caller selects requester type\ndrivers/pci/tph.c-519- * manually instead of hardware auto-selection. Rejects EXT_TPH request\n--\ndrivers/pci/tph.c-523- */\ndrivers/pci/tph.c:524:int pcie_enable_tph_explicit(struct pci_dev *pdev, int mode, bool extended)\ndrivers/pci/tph.c-525-{\n--\ndrivers/pci/tph.c-529-}\ndrivers/pci/tph.c:530:EXPORT_SYMBOL(pcie_enable_tph_explicit);\ndrivers/pci/tph.c-531-\n--\ndrivers/vfio/pci/vfio_pci_config.c=1130=static int vfio_tph_config_write(struct vfio_pci_core_device *vdev, int pos,\n--\ndrivers/vfio/pci/vfio_pci_config.c-1179-\t\textended = !!(req == PCI_TPH_REQ_EXT_TPH);\ndrivers/vfio/pci/vfio_pci_config.c:1180:\t\tret = pcie_enable_tph_explicit(pdev, mode, extended);\ndrivers/vfio/pci/vfio_pci_config.c-1181-\t\tif (!ret \u0026\u0026 vdev-\u003etph_st_shadow) {\n--\ninclude/linux/pci-tph.h=33=void pcie_disable_tph(struct pci_dev *pdev);\ninclude/linux/pci-tph.h:34:int pcie_enable_tph(struct pci_dev *pdev, int mode);\ninclude/linux/pci-tph.h:35:int pcie_enable_tph_explicit(struct pci_dev *pdev, int mode, bool extended);\ninclude/linux/pci-tph.h-36-u8 pcie_tph_enabled_req_type(struct pci_dev *pdev);\n--\ninclude/linux/pci-tph.h=54=static inline void pcie_disable_tph(struct pci_dev *pdev) { }\ninclude/linux/pci-tph.h:55:static inline int pcie_enable_tph(struct pci_dev *pdev, int mode)\ninclude/linux/pci-tph.h-56-{ return -EINVAL; }\ninclude/linux/pci-tph.h:57:static inline int pcie_enable_tph_explicit(struct pci_dev *pdev, int mode,\ninclude/linux/pci-tph.h-58-\t\t\t\t\t   bool extended)\n"
}

11/2 2026/06/22 08:15 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 26345
							output: 20
							thoughts: 2929
						

Thoughts:
**Analysis of TPH Patch and Reachability in Virtualized Environments**

Okay, so I'm looking at this patch and trying to figure out what's actually reachable by something like syzkaller. The initial code modifications seem straightforward – changes to `tph.c`, `vfio_pci_config.c`, and `vfio_pci_core.c`, plus the addition of some new functions (`pcie_enable_tph_explicit`, etc.) and a sysfs attribute (`tph_cpu_st`).

My first thought is always, "Where does this actually *run*?" The patch mentions `bnxt` and `mlx5`, but those are hardware-specific drivers, so probably not syzkaller's usual playground.

The VFIO code is a big question mark. It looks like it's *mostly* unreachable unless `enable_unsafe_tph` is set. The `-EOPNOTSUPP` returns, the `vdev->tph_permit` check... it all points to that being the gatekeeper. And the sysfs attribute? That has multiple checks: whether the device is a Root Port, if it supports TPH Completer, and whether the ACPI DSM is present. QEMU and GCE often have limited capabilities in these areas.

Now, let's break this down:

*   **VFIO:** Virtually unreachable without a module parameter.
*   **Sysfs attribute:** Probably unreachable because syzkaller can't inject ACPI DSMs.
*   **Other functions:** The `pci_tph_init` function *is* reachable, because syzkaller can inject virtual PCI devices. This code reads some config space and sets a few bits, but no real side effects. Then there is `pcie_tph_enabled_req_type`, `pcie_enable_tph_explicit` that can be reached if `vfio_pci` is used.
*   **Modified `pcie_enable_tph`:** Reached if the `bnxt` or `mlx5` drivers are loaded, but unlikely in a standard syzkaller setup.

The crucial point is that this patch adds *new* VFIO functionality. Even though it's gated by `enable_unsafe_tph`, this is a valid feature.

Therefore, despite the default settings and the difficulty in reaching it *directly* in a virtualized environment, I should consider the patch's functionality and mark it as fuzzer-worthy. The reasoning would be that this patch adds TPH support to VFIO PCI. It is gated by the `vfio_pci.enable_unsafe_tph` module parameter, which needs to be enabled for fuzzing.



12/2 2026/06/22 08:16 tool grepper
0m Args:
{
  "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"
}

13/2 2026/06/22 08:16 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 29961
							output: 35
							thoughts: 20
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Diving into PCI Configuration**

Okay, so I'm digging into some PCI configuration options within the kernel, specifically starting with `drivers/pci/Kconfig` around line 197. This likely centers on a group of related options, probably within a defined "menu" or block, given the `Kconfig` file structure. I'm guessing that line number is a good jumping-off point for a particular set of features. I want to understand how certain PCI-related features are enabled or disabled, possibly dependent on hardware, other kernel configurations, or specific use-cases. I need to get a high-level picture of those dependencies. I'm expecting to find conditional statements, `depends on` clauses, and maybe some help text.



14/2 2026/06/22 08:16 tool read-file
0m Args:
{
  "File": "drivers/pci/Kconfig",
  "FirstLine": 190,
  "LineCount": 20
}

Results:
{
  "Contents": " 190:\t\t  to access more than one IO address space at the same time. To make\n 191:\t\t  use of this feature an IOMMU is required which also supports PASIDs.\n 192:\t\t  Select this option if you have such an IOMMU and want to compile the\n 193:\t\t  driver for it into your kernel.\n 194:\t\n 195:\t\t  If unsure, say N.\n 196:\t\n 197:\tconfig PCIE_TPH\n 198:\t\tbool \"TLP Processing Hints\"\n 199:\t\thelp\n 200:\t\t  This option adds support for PCIe TLP Processing Hints (TPH).\n 201:\t\t  TPH allows endpoint devices to provide optimization hints, such as\n 202:\t\t  desired caching behavior, for requests that target memory space.\n 203:\t\t  These hints, called Steering Tags, can empower the system hardware\n 204:\t\t  to optimize the utilization of platform resources.\n 205:\t\n 206:\tconfig PCI_P2PDMA\n 207:\t\tbool \"PCI peer-to-peer transfer support\"\n 208:\t\tdepends on ZONE_DEVICE\n 209:\t\t#\n"
}

15/2 2026/06/22 08:16 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 30510
							output: 220
							thoughts: 2168
						

Thoughts:
**PCIe TPH and VFIO PCI Fuzzing Targets**

Okay, let's break this down. First, it looks like `PCIE_TPH` might be disabled by default, so we definitely want to add it to the `EnableConfigs`. We should also ensure `VFIO_PCI` is enabled, although that's likely already the case in syzkaller configs. It appears the prompt wants us to focus on the specific changes, so `PCIE_TPH` is the key.

Now, which functions are the key entry points and the ones we want syzkaller to target? Let's zoom in on `drivers/vfio/pci/vfio_pci_core.c` and `drivers/vfio/pci/vfio_pci_config.c`, and `drivers/pci/tph.c` since these are the main changes. We should prioritize functions in the VFIO space as these are what userspace interacts with, so those are key.

Specifically, I'm thinking:
*   `vfio_pci_core_feature_tph_enable`
*   `vfio_pci_core_feature_tph_st_config`
*   `vfio_tph_config_write`
*   `vfio_pci_tph_init`
*   `pci_tph_init`
*   `tph_cpu_st_read`
*   `enable_tph`

We want `tph_cpu_st_read` to be visible, so syzkaller might be able to create an edge case. Even if it is not visible due to `tph_dsm_supported` being false, it might still be worth listing it. `vfio_tph_config_write` is definitely reachable because syzkaller fuzzes writes to the VFIO PCI config space. `vfio_pci_core_feature_tph_enable` is reachable via `VFIO_DEVICE_FEATURE` ioctl.

A key point is the module parameter `enable_unsafe_tph` which, if *not* set, will make the code just return `-EOPNOTSUPP`. That said, the patch introduces new functionality, and it's worth fuzzing since it may reveal new flaws or areas that could benefit from fuzzing. Syzkaller's maintainers should be aware that setting the `vfio_pci.enable_unsafe_tph=1` kernel command line parameter will be *essential* to actually reach the target logic.

So, for `FocusSymbols`, I will add the VFIO functions first. Then I am adding the PCI initialization code as it's reachable during device probe.
I'm including `tph_cpu_st_read` and `enable_tph`.

My Reasoning is, "This patch introduces PCIe TPH (Transaction Processing Hints) support to VFIO PCI and adds a sysfs attribute for CPU-to-ST mapping. The VFIO ioctls and config space handlers are reachable and should be fuzzed, but they require the `vfio_pci.enable_unsafe_tph=1` module parameter to be set on the kernel command line. The core PCI TPH initialization is reachable during virtual PCI device probe. We include `enable_tph` as it's part of the write path. `tph_cpu_st_read` is included."

The critical config we need is `PCIE_TPH`.



16/2 2026/06/22 08:17 tool set-results
0m Args:
{
  "EnableConfigs": [
    "PCIE_TPH"
  ],
  "FocusSymbols": [
    "vfio_pci_core_feature_tph_enable",
    "vfio_pci_core_feature_tph_st_config",
    "vfio_tph_config_write",
    "vfio_pci_tph_init",
    "pci_tph_init",
    "enable_tph",
    "tph_cpu_st_read"
  ],
  "Reasoning": "This patch introduces PCIe TPH (Transaction Processing Hints) support to VFIO PCI and adds a sysfs attribute for CPU-to-ST mapping. The VFIO ioctls and config space handlers are reachable and should be fuzzed, but they require the `vfio_pci.enable_unsafe_tph=1` module parameter to be set on the kernel command line. The core PCI TPH initialization is reachable during virtual PCI device probe.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "PCIE_TPH"
  ],
  "FocusSymbols": [
    "vfio_pci_core_feature_tph_enable",
    "vfio_pci_core_feature_tph_st_config",
    "vfio_tph_config_write",
    "vfio_pci_tph_init",
    "pci_tph_init",
    "enable_tph",
    "tph_cpu_st_read"
  ],
  "Reasoning": "This patch introduces PCIe TPH (Transaction Processing Hints) support to VFIO PCI and adds a sysfs attribute for CPU-to-ST mapping. The VFIO ioctls and config space handlers are reachable and should be fuzzed, but they require the `vfio_pci.enable_unsafe_tph=1` module parameter to be set on the kernel command line. The core PCI TPH initialization is reachable during virtual PCI device probe.",
  "WorthFuzzing": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)