AI Trajectory


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

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch adds a new MCTP over PCC driver and modifies the PCC mailbox driver. Both require specific ACPI tables (PCCT and a device with HID DMT0001) to be present in the firmware. Since these ACPI tables are not provided by standard virtualized environments (QEMU/GCE) and cannot be dynamically injected by syzkaller, the code is unreachable and cannot be fuzzed.",
  "WorthFuzzing": false
}

1/1 2026/07/22 14:00 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit ff78bd82b010e2ffe00855dd8c5b5f52160ce080\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Wed Jul 22 14:00:07 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/MAINTAINERS b/MAINTAINERS\nindex 6940aa3d498b99..9f48dee0f4c6dc 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -15582,6 +15582,11 @@ F:\tinclude/net/mctpdevice.h\n F:\tinclude/net/netns/mctp.h\n F:\tnet/mctp/\n \n+MANAGEMENT COMPONENT TRANSPORT PROTOCOL (MCTP) over PCC (MCTP-PCC) Driver\n+M:\tAdam Young \u003cadmiyo@os.amperecomputing.com\u003e\n+S:\tMaintained\n+F:\tdrivers/net/mctp/mctp-pcc.c\n+\n MAPLE TREE\n M:\tLiam R. Howlett \u003cliam@infradead.org\u003e\n R:\tAlice Ryhl \u003caliceryhl@google.com\u003e\ndiff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c\nindex 636879ae1db76b..dbfe8dd92ddd26 100644\n--- a/drivers/mailbox/pcc.c\n+++ b/drivers/mailbox/pcc.c\n@@ -270,6 +270,9 @@ static int pcc_mbox_error_check_and_clear(struct pcc_chan_info *pchan)\n \tu64 val;\n \tint ret;\n \n+\tif (pchan-\u003etype == ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE)\n+\t\treturn 0;\n+\n \tret = pcc_chan_reg_read(\u0026pchan-\u003eerror, \u0026val);\n \tif (ret)\n \t\treturn ret;\n@@ -314,6 +317,7 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)\n {\n \tstruct pcc_chan_info *pchan;\n \tstruct mbox_chan *chan = p;\n+\tint rc;\n \n \tpchan = chan-\u003econ_priv;\n \n@@ -321,14 +325,13 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)\n \t\treturn IRQ_NONE;\n \n \tif (pchan-\u003etype == ACPI_PCCT_TYPE_EXT_PCC_MASTER_SUBSPACE \u0026\u0026\n-\t    !pchan-\u003echan_in_use)\n+\t    !READ_ONCE(pchan-\u003echan_in_use))\n \t\treturn IRQ_NONE;\n \n \tif (!pcc_mbox_cmd_complete_check(pchan))\n \t\treturn IRQ_NONE;\n \n-\tif (pcc_mbox_error_check_and_clear(pchan))\n-\t\treturn IRQ_NONE;\n+\trc = pcc_mbox_error_check_and_clear(pchan);\n \n \t/*\n \t * Clear this flag after updating interrupt ack register and just\n@@ -336,15 +339,60 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)\n \t * where the flag is set again to start new transfer. This is\n \t * required to avoid any possible race in updatation of this flag.\n \t */\n-\tpchan-\u003echan_in_use = false;\n-\tmbox_chan_received_data(chan, NULL);\n-\tmbox_chan_txdone(chan, 0);\n+\tWRITE_ONCE(pchan-\u003echan_in_use, false);\n+\tif (!rc)\n+\t\tmbox_chan_received_data(chan, NULL);\n+\tmbox_chan_txdone(chan, rc);\n \n \tpcc_chan_acknowledge(pchan);\n \n \treturn IRQ_HANDLED;\n }\n \n+/**\n+ * pcc_mbox_query_channel - returns information about the channel\n+ *              without activating the channel.\n+ *\n+ * @q_chan: a pointer to an already allocated struct pcc_mbox_chan\n+ *              that will be populated with the channel data.\n+ * @subspace_id: The PCC Subspace index as parsed in the PCC client\n+ *      ACPI package. This is used to lookup the array of PCC\n+ *      subspaces as parsed by the PCC Mailbox controller.\n+ *\n+ * Return: 0 upon success or non-zero upon error.\n+ */\n+int\n+pcc_mbox_query_channel(struct pcc_mbox_chan *q_chan, int subspace_id)\n+{\n+\tstruct pcc_mbox_chan *pcc_mchan;\n+\tstruct pcc_chan_info *pchan;\n+\tstruct mbox_chan *chan;\n+\n+\tif (!q_chan)\n+\t\treturn -EINVAL;\n+\n+\tif (subspace_id \u003c 0 || subspace_id \u003e= pcc_chan_count)\n+\t\treturn -ENOENT;\n+\tpchan = chan_info + subspace_id;\n+\tchan = pchan-\u003echan.mchan;\n+\tif (IS_ERR(chan)) {\n+\t\tpr_err(\"Channel not found for idx: %d\\n\", subspace_id);\n+\t\treturn -EBUSY;\n+\t}\n+\tpcc_mchan = \u0026pchan-\u003echan;\n+\n+\tq_chan-\u003eshmem_base_addr = pcc_mchan-\u003eshmem_base_addr;\n+\tq_chan-\u003eshmem = NULL;\n+\tq_chan-\u003eshmem_size = pcc_mchan-\u003eshmem_size;\n+\tq_chan-\u003elatency = pcc_mchan-\u003elatency;\n+\tq_chan-\u003emax_access_rate = pcc_mchan-\u003emax_access_rate;\n+\tq_chan-\u003emin_turnaround_time = pcc_mchan-\u003emin_turnaround_time;\n+\tq_chan-\u003etype = pcc_mchan-\u003etype;\n+\n+\treturn 0;\n+}\n+EXPORT_SYMBOL_GPL(pcc_mbox_query_channel);\n+\n /**\n  * pcc_mbox_request_channel - PCC clients call this function to\n  *\t\trequest a pointer to their PCC subspace, from which they\n@@ -360,7 +408,6 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)\n struct pcc_mbox_chan *\n pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)\n {\n-\tstruct pcc_mbox_chan *pcc_mchan;\n \tstruct pcc_chan_info *pchan;\n \tstruct mbox_chan *chan;\n \tint rc;\n@@ -375,20 +422,10 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)\n \t\treturn ERR_PTR(-EBUSY);\n \t}\n \n-\tpcc_mchan = \u0026pchan-\u003echan;\n-\tpcc_mchan-\u003eshmem = acpi_os_ioremap(pcc_mchan-\u003eshmem_base_addr,\n-\t\t\t\t\t   pcc_mchan-\u003eshmem_size);\n-\tif (!pcc_mchan-\u003eshmem)\n-\t\treturn ERR_PTR(-ENXIO);\n-\n \trc = mbox_bind_client(chan, cl);\n-\tif (rc) {\n-\t\tiounmap(pcc_mchan-\u003eshmem);\n-\t\tpcc_mchan-\u003eshmem = NULL;\n-\t\treturn ERR_PTR(rc);\n-\t}\n-\n-\treturn pcc_mchan;\n+\tif (rc)\n+\t\treturn ERR_PTR(-ENXIO);\n+\treturn  \u0026pchan-\u003echan;\n }\n EXPORT_SYMBOL_GPL(pcc_mbox_request_channel);\n \n@@ -400,19 +437,13 @@ EXPORT_SYMBOL_GPL(pcc_mbox_request_channel);\n  */\n void pcc_mbox_free_channel(struct pcc_mbox_chan *pchan)\n {\n-\tstruct mbox_chan *chan = pchan-\u003emchan;\n-\tstruct pcc_chan_info *pchan_info;\n-\tstruct pcc_mbox_chan *pcc_mbox_chan;\n+\tstruct mbox_chan *chan;\n \n+\tif (!pchan)\n+\t\treturn;\n+\tchan = pchan-\u003emchan;\n \tif (!chan || !chan-\u003ecl)\n \t\treturn;\n-\tpchan_info = chan-\u003econ_priv;\n-\tpcc_mbox_chan = \u0026pchan_info-\u003echan;\n-\tif (pcc_mbox_chan-\u003eshmem) {\n-\t\tiounmap(pcc_mbox_chan-\u003eshmem);\n-\t\tpcc_mbox_chan-\u003eshmem = NULL;\n-\t}\n-\n \tmbox_free_channel(chan);\n }\n EXPORT_SYMBOL_GPL(pcc_mbox_free_channel);\n@@ -440,7 +471,7 @@ static int pcc_send_data(struct mbox_chan *chan, void *data)\n \n \tret = pcc_chan_reg_read_modify_write(\u0026pchan-\u003edb);\n \tif (!ret \u0026\u0026 pchan-\u003eplat_irq \u003e 0)\n-\t\tpchan-\u003echan_in_use = true;\n+\t\tWRITE_ONCE(pchan-\u003echan_in_use, true);\n \n \treturn ret;\n }\n@@ -462,9 +493,15 @@ static bool pcc_last_tx_done(struct mbox_chan *chan)\n static int pcc_startup(struct mbox_chan *chan)\n {\n \tstruct pcc_chan_info *pchan = chan-\u003econ_priv;\n+\tstruct pcc_mbox_chan *pcc_mchan;\n \tunsigned long irqflags;\n \tint rc;\n \n+\tpcc_mchan = \u0026pchan-\u003echan;\n+\tpcc_mchan-\u003eshmem = acpi_os_ioremap(pcc_mchan-\u003eshmem_base_addr,\n+\t\t\t\t\t   pcc_mchan-\u003eshmem_size);\n+\tif (pcc_mchan-\u003eshmem  == NULL)\n+\t\treturn -ENOMEM;\n \t/*\n \t * Clear and acknowledge any pending interrupts on responder channel\n \t * before enabling the interrupt\n@@ -479,6 +516,8 @@ static int pcc_startup(struct mbox_chan *chan)\n \t\tif (unlikely(rc)) {\n \t\t\tdev_err(chan-\u003embox-\u003edev, \"failed to register PCC interrupt %d\\n\",\n \t\t\t\tpchan-\u003eplat_irq);\n+\t\t\tiounmap(pcc_mchan-\u003eshmem);\n+\t\t\tpcc_mchan-\u003eshmem = NULL;\n \t\t\treturn rc;\n \t\t}\n \t}\n@@ -488,15 +527,23 @@ static int pcc_startup(struct mbox_chan *chan)\n \n /**\n  * pcc_shutdown - Called from Mailbox Controller code. Used here\n- *\t\tto free the interrupt.\n+ *\t\tto free the interrupt and unmap the shared memory.\n  * @chan: Pointer to Mailbox channel to shutdown.\n  */\n static void pcc_shutdown(struct mbox_chan *chan)\n {\n \tstruct pcc_chan_info *pchan = chan-\u003econ_priv;\n+\tstruct pcc_mbox_chan *pcc_mbox_chan;\n \n \tif (pchan-\u003eplat_irq \u003e 0)\n \t\tdevm_free_irq(chan-\u003embox-\u003edev, pchan-\u003eplat_irq, chan);\n+\tsynchronize_irq(pchan-\u003eplat_irq);\n+\n+\tpcc_mbox_chan = \u0026pchan-\u003echan;\n+\tif (pcc_mbox_chan-\u003eshmem) {\n+\t\tiounmap(pcc_mbox_chan-\u003eshmem);\n+\t\tpcc_mbox_chan-\u003eshmem = NULL;\n+\t}\n }\n \n static const struct mbox_chan_ops pcc_chan_ops = {\n@@ -831,6 +878,7 @@ static int pcc_mbox_probe(struct platform_device *pdev)\n \t\tpcc_parse_subspace_shmem(pchan, pcct_entry);\n \n \t\tpchan-\u003etype = pcct_entry-\u003etype;\n+\t\tpchan-\u003echan.type = pcct_entry-\u003etype;\n \t\tpcct_entry = (struct acpi_subtable_header *)\n \t\t\t((unsigned long) pcct_entry + pcct_entry-\u003elength);\n \t}\ndiff --git a/drivers/net/mctp/Kconfig b/drivers/net/mctp/Kconfig\nindex cf325ab0b1ef55..c9b8f38ff0fb92 100644\n--- a/drivers/net/mctp/Kconfig\n+++ b/drivers/net/mctp/Kconfig\n@@ -47,6 +47,21 @@ config MCTP_TRANSPORT_I3C\n \t  A MCTP protocol network device is created for each I3C bus\n \t  having a \"mctp-controller\" devicetree property.\n \n+config MCTP_TRANSPORT_PCC\n+\ttristate \"MCTP PCC transport\"\n+\tdepends on ACPI\n+\tdepends on PCC\n+\tdepends on CPU_LITTLE_ENDIAN\n+\thelp\n+\t  Provides a driver to access MCTP devices over PCC transport,\n+\t  A MCTP protocol network device is created via ACPI for each\n+\t  entry in the DSDT/SSDT that matches the identifier. The Platform\n+\t  communication channels are selected from the corresponding\n+\t  entries in the PCCT.\n+\n+\t  Say y here if you need to connect to MCTP endpoints over PCC. To\n+\t  compile as a module, use m; the module will be called mctp-pcc.\n+\n config MCTP_TRANSPORT_USB\n \ttristate \"MCTP USB transport\"\n \tdepends on USB\ndiff --git a/drivers/net/mctp/Makefile b/drivers/net/mctp/Makefile\nindex c36006849a1e7d..0a591299ffa92d 100644\n--- a/drivers/net/mctp/Makefile\n+++ b/drivers/net/mctp/Makefile\n@@ -1,4 +1,5 @@\n obj-$(CONFIG_MCTP_SERIAL) += mctp-serial.o\n obj-$(CONFIG_MCTP_TRANSPORT_I2C) += mctp-i2c.o\n obj-$(CONFIG_MCTP_TRANSPORT_I3C) += mctp-i3c.o\n+obj-$(CONFIG_MCTP_TRANSPORT_PCC) += mctp-pcc.o\n obj-$(CONFIG_MCTP_TRANSPORT_USB) += mctp-usb.o\ndiff --git a/drivers/net/mctp/mctp-pcc.c b/drivers/net/mctp/mctp-pcc.c\nnew file mode 100644\nindex 00000000000000..57c4f3b2151370\n--- /dev/null\n+++ b/drivers/net/mctp/mctp-pcc.c\n@@ -0,0 +1,466 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * mctp-pcc.c - Driver for MCTP over PCC.\n+ * Copyright (c) 2024-2026, Ampere Computing LLC\n+ *\n+ */\n+\n+/* Implementation of\n+ * https://www.dmtf.org/sites/default/files/standards/documents/DSP0292.pdf\n+ */\n+\n+#include \u003clinux/acpi.h\u003e\n+#include \u003clinux/hrtimer.h\u003e\n+#include \u003clinux/if_arp.h\u003e\n+#include \u003clinux/init.h\u003e\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/mailbox_client.h\u003e\n+#include \u003clinux/module.h\u003e\n+#include \u003clinux/netdevice.h\u003e\n+#include \u003clinux/platform_device.h\u003e\n+#include \u003clinux/skbuff.h\u003e\n+#include \u003clinux/string.h\u003e\n+\n+#include \u003cacpi/acpi_bus.h\u003e\n+#include \u003cacpi/acpi_drivers.h\u003e\n+#include \u003cacpi/acrestyp.h\u003e\n+#include \u003cacpi/actbl.h\u003e\n+#include \u003cacpi/pcc.h\u003e\n+#include \u003cnet/mctp.h\u003e\n+#include \u003cnet/mctpdevice.h\u003e\n+#include \u003cnet/pkt_sched.h\u003e\n+\n+#define MCTP_SIGNATURE          \"MCTP\"\n+#define MCTP_SIGNATURE_LENGTH   (sizeof(MCTP_SIGNATURE) - 1)\n+#define MCTP_MIN_MTU            68\n+#define PCC_HEADER_SIZE         sizeof(struct acpi_pcct_ext_pcc_shared_memory)\n+#define MCTP_PCC_MIN_SIZE       (PCC_HEADER_SIZE + MCTP_MIN_MTU)\n+#define PCC_EXTRA_LEN           (PCC_HEADER_SIZE - sizeof(pcc_header.command))\n+struct mctp_pcc_mailbox {\n+\tu32 index;\n+\tstruct pcc_mbox_chan *chan;\n+\tstruct mbox_client client;\n+};\n+\n+/* The netdev structure. One of these per PCC adapter. */\n+struct mctp_pcc_ndev {\n+\tstruct net_device *ndev;\n+\tstruct acpi_device *acpi_device;\n+\tstruct mctp_pcc_mailbox inbox;\n+\tstruct mctp_pcc_mailbox outbox;\n+};\n+\n+static void mctp_pcc_client_rx_callback(struct mbox_client *cl, void *mssg)\n+{\n+\tstruct acpi_pcct_ext_pcc_shared_memory pcc_header;\n+\tstruct mctp_pcc_ndev *mctp_pcc_ndev;\n+\tstruct mctp_pcc_mailbox *inbox;\n+\tstruct mctp_skb_cb *cb;\n+\tstruct sk_buff *skb;\n+\tint size;\n+\n+\tmctp_pcc_ndev = container_of(cl, struct mctp_pcc_ndev, inbox.client);\n+\tinbox = \u0026mctp_pcc_ndev-\u003einbox;\n+\tmemcpy_fromio(\u0026pcc_header, inbox-\u003echan-\u003eshmem, sizeof(pcc_header));\n+\n+\t// The message must at least have the PCC command indicating it is an MCTP\n+\t// message followed by the MCTP header, or we have a malformed message.\n+\tif (pcc_header.length \u003c sizeof(pcc_header.command) + sizeof(struct mctp_hdr))\n+\t\tgoto error;\n+\n+\t// If the reported size is larger than the shared memory minus headers,\n+\t// something is wrong and treat the buffer as corrupted data.\n+\tif (pcc_header.length \u003e inbox-\u003echan-\u003eshmem_size - PCC_EXTRA_LEN)\n+\t\tgoto error;\n+\n+\tif (memcmp(\u0026pcc_header.command, MCTP_SIGNATURE, MCTP_SIGNATURE_LENGTH) != 0)\n+\t\tgoto error;\n+\n+\tsize = pcc_header.length + PCC_EXTRA_LEN;\n+\tskb = netdev_alloc_skb(mctp_pcc_ndev-\u003endev, size);\n+\tif (!skb)\n+\t\tgoto error;\n+\n+\tskb_put(skb, size);\n+\tskb-\u003eprotocol = htons(ETH_P_MCTP);\n+\tmemcpy_fromio(skb-\u003edata, inbox-\u003echan-\u003eshmem, size);\n+\tdev_dstats_rx_add(mctp_pcc_ndev-\u003endev, size);\n+\tskb_pull(skb, sizeof(pcc_header));\n+\tskb_reset_mac_header(skb);\n+\tskb_reset_network_header(skb);\n+\tcb = __mctp_cb(skb);\n+\tcb-\u003ehalen = 0;\n+\tnetif_rx(skb);\n+\treturn;\n+\n+error:\n+\tdev_dstats_rx_dropped(mctp_pcc_ndev-\u003endev);\n+}\n+\n+static netdev_tx_t mctp_pcc_tx(struct sk_buff *skb, struct net_device *ndev)\n+{\n+\tstruct acpi_pcct_ext_pcc_shared_memory *pcc_header;\n+\tstruct mctp_pcc_ndev *mpnd = netdev_priv(ndev);\n+\tint len = skb-\u003elen;\n+\n+\tif (skb_cow_head(skb, sizeof(*pcc_header)))\n+\t\tgoto error;\n+\n+\tpcc_header = skb_push(skb, sizeof(*pcc_header));\n+\tpcc_header-\u003esignature = PCC_SIGNATURE | mpnd-\u003eoutbox.index;\n+\tpcc_header-\u003eflags = PCC_CMD_COMPLETION_NOTIFY;\n+\tmemcpy(\u0026pcc_header-\u003ecommand, MCTP_SIGNATURE, MCTP_SIGNATURE_LENGTH);\n+\tpcc_header-\u003elength = len + MCTP_SIGNATURE_LENGTH;\n+\n+\tif (skb-\u003elen \u003e mpnd-\u003eoutbox.chan-\u003eshmem_size)\n+\t\tgoto error;\n+\n+\t/*\n+\t * There is a possibility that the mailbox can be cleared on\n+\t * another thread. If that is the case, and we don't restart\n+\t * the queue, it will remain permanently stopped.\n+\t * Stopping the queue before attempting to send the message\n+\t * allows us to always restart it if mbox_send_message succeeds.\n+\t */\n+\tnetif_stop_queue(ndev);\n+\tif (mbox_send_message(mpnd-\u003eoutbox.chan-\u003emchan, skb) \u003e= 0) {\n+\t\tnetif_wake_queue(ndev);\n+\t} else {\n+\t\t// Remove the header in case it gets sent again\n+\t\tskb_pull(skb, sizeof(*pcc_header));\n+\t\treturn NETDEV_TX_BUSY;\n+\t}\n+\treturn NETDEV_TX_OK;\n+\n+error:\n+\tdev_dstats_tx_dropped(ndev);\n+\tkfree_skb(skb);\n+\treturn NETDEV_TX_OK;\n+}\n+\n+static void mctp_pcc_tx_prepare(struct mbox_client *cl, void *mssg)\n+{\n+\tstruct mctp_pcc_ndev *mctp_pcc_ndev;\n+\tstruct mctp_pcc_mailbox *outbox;\n+\tstruct sk_buff *skb = mssg;\n+\n+\tmctp_pcc_ndev = container_of(cl, struct mctp_pcc_ndev, outbox.client);\n+\toutbox = \u0026mctp_pcc_ndev-\u003eoutbox;\n+\n+\t/* The PCC Mailbox typically does not make use of the mssg pointer\n+\t * The mctp-over pcc driver is the only client that uses it.\n+\t * This value should always be non-null; it is possible\n+\t * that a change in the Mailbox level will break that assumption.\n+\t */\n+\tif (!skb) {\n+\t\tnetdev_warn_once(mctp_pcc_ndev-\u003endev,\n+\t\t\t\t \"%s called with null message.\\n\", __func__);\n+\t\treturn;\n+\t}\n+\tmemcpy_toio(outbox-\u003echan-\u003eshmem, skb-\u003edata, skb-\u003elen);\n+}\n+\n+static void mctp_pcc_tx_done(struct mbox_client *c, void *mssg, int rc)\n+{\n+\tstruct mctp_pcc_ndev *mctp_pcc_ndev;\n+\tstruct pcpu_dstats *dstats;\n+\tstruct sk_buff *skb = mssg;\n+\tunsigned long flags;\n+\n+\t/*\n+\t * If there is a packet in flight during driver cleanup\n+\t * It may have been freed already.\n+\t */\n+\tif (!mssg)\n+\t\treturn;\n+\tmctp_pcc_ndev = container_of(c, struct mctp_pcc_ndev, outbox.client);\n+\n+\t/* Use an IRQ safe update as this is called from HARD IRQ instead of\n+\t * dev_dstats_tx_add(mctp_pcc_ndev-\u003endev, skb-\u003elen);\n+\t */\n+\tdstats = this_cpu_ptr(mctp_pcc_ndev-\u003endev-\u003edstats);\n+\tflags = u64_stats_update_begin_irqsave(\u0026dstats-\u003esyncp);\n+\n+\tif (rc) {\n+\t\tu64_stats_inc(\u0026dstats-\u003etx_drops);\n+\t} else {\n+\t\tu64_stats_inc(\u0026dstats-\u003etx_packets);\n+\t\tu64_stats_add(\u0026dstats-\u003etx_bytes, skb-\u003elen);\n+\t}\n+\tu64_stats_update_end_irqrestore(\u0026dstats-\u003esyncp, flags);\n+\tdev_consume_skb_any(skb);\n+\tnetif_wake_queue(mctp_pcc_ndev-\u003endev);\n+}\n+\n+static int mctp_pcc_open(struct net_device *ndev)\n+{\n+\tstruct mctp_pcc_ndev *mctp_pcc_ndev = netdev_priv(ndev);\n+\tstruct mctp_pcc_mailbox *outbox, *inbox;\n+\n+\toutbox = \u0026mctp_pcc_ndev-\u003eoutbox;\n+\tinbox = \u0026mctp_pcc_ndev-\u003einbox;\n+\n+\toutbox-\u003echan = pcc_mbox_request_channel(\u0026outbox-\u003eclient, outbox-\u003eindex);\n+\tif (IS_ERR(outbox-\u003echan))\n+\t\treturn PTR_ERR(outbox-\u003echan);\n+\tif (outbox-\u003echan-\u003eshmem_size \u003c MCTP_PCC_MIN_SIZE) {\n+\t\tpcc_mbox_free_channel(outbox-\u003echan);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tinbox-\u003eclient.rx_callback = mctp_pcc_client_rx_callback;\n+\tinbox-\u003echan = pcc_mbox_request_channel(\u0026inbox-\u003eclient, inbox-\u003eindex);\n+\tif (IS_ERR(inbox-\u003echan)) {\n+\t\tpcc_mbox_free_channel(outbox-\u003echan);\n+\t\treturn PTR_ERR(inbox-\u003echan);\n+\t}\n+\tif (inbox-\u003echan-\u003eshmem_size \u003c MCTP_PCC_MIN_SIZE) {\n+\t\tpcc_mbox_free_channel(outbox-\u003echan);\n+\t\tpcc_mbox_free_channel(inbox-\u003echan);\n+\t\treturn -EINVAL;\n+\t}\n+\treturn 0;\n+}\n+\n+static int mctp_pcc_stop(struct net_device *ndev)\n+{\n+\tstruct mctp_pcc_ndev *mctp_pcc_ndev;\n+\tunsigned int count, idx;\n+\tstruct mbox_chan *chan;\n+\tstruct sk_buff *skb;\n+\n+\tmctp_pcc_ndev = netdev_priv(ndev);\n+\tchan = mctp_pcc_ndev-\u003eoutbox.chan-\u003emchan;\n+\tpcc_mbox_free_channel(mctp_pcc_ndev-\u003einbox.chan);\n+\tmctp_pcc_ndev-\u003einbox.chan = NULL;\n+\tscoped_guard(spinlock_irqsave, \u0026chan-\u003elock) {\n+\t\tif (chan-\u003eactive_req !=  MBOX_NO_MSG) {\n+\t\t\tskb = chan-\u003eactive_req;\n+\t\t\tchan-\u003eactive_req = MBOX_NO_MSG;\n+\t\t\tdev_dstats_tx_dropped(ndev);\n+\t\t\tdev_consume_skb_any(skb);\n+\t\t}\n+\t\twhile (chan-\u003emsg_count \u003e 0) {\n+\t\t\tcount = chan-\u003emsg_count;\n+\t\t\tidx = chan-\u003emsg_free;\n+\t\t\tif (idx \u003e= count)\n+\t\t\t\tidx -= count;\n+\t\t\telse\n+\t\t\t\tidx += MBOX_TX_QUEUE_LEN - count;\n+\t\t\tskb = chan-\u003emsg_data[idx];\n+\t\t\tdev_dstats_tx_dropped(ndev);\n+\t\t\tdev_consume_skb_any(skb);\n+\t\t\tchan-\u003emsg_count--;\n+\t\t}\n+\t}\n+\tpcc_mbox_free_channel(mctp_pcc_ndev-\u003eoutbox.chan);\n+\tmctp_pcc_ndev-\u003eoutbox.chan = NULL;\n+\t/*\n+\t * If the queue was stopped because the ring buffer was full\n+\t * we can restart it here as we now know the ring buffer has\n+\t * been emptied and the queue can be used again if the\n+\t * netdev is re-opened.\n+\t */\n+\tnetif_wake_queue(mctp_pcc_ndev-\u003endev);\n+\treturn 0;\n+}\n+\n+static const struct net_device_ops mctp_pcc_netdev_ops = {\n+\t.ndo_open = mctp_pcc_open,\n+\t.ndo_stop = mctp_pcc_stop,\n+\t.ndo_start_xmit = mctp_pcc_tx,\n+};\n+\n+static void mctp_pcc_setup(struct net_device *ndev)\n+{\n+\tndev-\u003etype = ARPHRD_MCTP;\n+\tndev-\u003ehard_header_len = sizeof(struct acpi_pcct_ext_pcc_shared_memory);\n+\tndev-\u003etx_queue_len = DEFAULT_TX_QUEUE_LEN;\n+\tndev-\u003eflags = IFF_NOARP;\n+\tndev-\u003enetdev_ops = \u0026mctp_pcc_netdev_ops;\n+\tndev-\u003eneeds_free_netdev = true;\n+\tndev-\u003epcpu_stat_type = NETDEV_PCPU_STAT_DSTATS;\n+}\n+\n+struct mctp_pcc_lookup_context {\n+\tint index;\n+\tu32 inbox_index;\n+\tu32 outbox_index;\n+};\n+\n+static acpi_status lookup_pcct_indices(struct acpi_resource *ares,\n+\t\t\t\t       void *context)\n+{\n+\tstruct mctp_pcc_lookup_context *luc = context;\n+\tstruct acpi_resource_address32 *addr;\n+\n+\tif (ares-\u003etype != ACPI_RESOURCE_TYPE_ADDRESS32)\n+\t\treturn AE_OK;\n+\n+\taddr = ACPI_CAST_PTR(struct acpi_resource_address32, \u0026ares-\u003edata);\n+\tswitch (luc-\u003eindex) {\n+\tcase 0:\n+\t\tluc-\u003eoutbox_index = addr[0].address.minimum;\n+\t\tbreak;\n+\tcase 1:\n+\t\tluc-\u003einbox_index = addr[0].address.minimum;\n+\t\tbreak;\n+\tdefault:\n+\t\treturn AE_ERROR;\n+\t}\n+\tluc-\u003eindex++;\n+\treturn AE_OK;\n+}\n+\n+static void mctp_cleanup_netdev(void *data)\n+{\n+\tstruct net_device *ndev = data;\n+\n+\tmctp_unregister_netdev(ndev);\n+}\n+\n+static int check_channel_types(struct mctp_pcc_ndev *mctp_pcc_ndev)\n+{\n+\tstruct mctp_pcc_mailbox *outbox;\n+\tstruct mctp_pcc_mailbox *inbox;\n+\tstruct pcc_mbox_chan chan;\n+\tint actual_type;\n+\n+\toutbox = \u0026mctp_pcc_ndev-\u003eoutbox;\n+\tif (pcc_mbox_query_channel(\u0026chan, outbox-\u003eindex))\n+\t\treturn -EINVAL;\n+\tactual_type = chan.type;\n+\tif (actual_type != ACPI_PCCT_TYPE_EXT_PCC_MASTER_SUBSPACE) {\n+\t\tpr_err(\"MCTP-PCC outbox channel wrong type: %d\", actual_type);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tinbox = \u0026mctp_pcc_ndev-\u003einbox;\n+\tif (pcc_mbox_query_channel(\u0026chan, inbox-\u003eindex))\n+\t\treturn -EINVAL;\n+\tactual_type = chan.type;\n+\tif (actual_type != ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE) {\n+\t\tpr_err(\"MCTP-PCC inbox channel wrong type: %d\", actual_type);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int initialize_mtu(struct net_device *ndev)\n+{\n+\tstruct mctp_pcc_ndev *mctp_pcc_ndev;\n+\tstruct mctp_pcc_mailbox *outbox;\n+\tstruct mctp_pcc_mailbox *inbox;\n+\tstruct pcc_mbox_chan out_chan;\n+\tstruct pcc_mbox_chan in_chan;\n+\tint mctp_pcc_max_mtu;\n+\tint inbox_max_mtu;\n+\n+\tmctp_pcc_ndev = netdev_priv(ndev);\n+\toutbox = \u0026mctp_pcc_ndev-\u003eoutbox;\n+\tif (pcc_mbox_query_channel(\u0026out_chan, outbox-\u003eindex))\n+\t\treturn -EINVAL;\n+\tif (out_chan.shmem_size \u003c MCTP_MIN_MTU + sizeof(struct acpi_pcct_ext_pcc_shared_memory))\n+\t\treturn -EINVAL;\n+\tmctp_pcc_max_mtu = out_chan.shmem_size - sizeof(struct acpi_pcct_ext_pcc_shared_memory);\n+\tinbox = \u0026mctp_pcc_ndev-\u003einbox;\n+\tif (pcc_mbox_query_channel(\u0026in_chan, inbox-\u003eindex))\n+\t\treturn -EINVAL;\n+\tif (in_chan.shmem_size \u003c MCTP_MIN_MTU + sizeof(struct acpi_pcct_ext_pcc_shared_memory))\n+\t\treturn -EINVAL;\n+\tinbox_max_mtu = in_chan.shmem_size - sizeof(struct acpi_pcct_ext_pcc_shared_memory);\n+\n+\tif (inbox_max_mtu \u003c mctp_pcc_max_mtu)\n+\t\tmctp_pcc_max_mtu = inbox_max_mtu;\n+\n+\tndev-\u003emtu = MCTP_MIN_MTU;\n+\tndev-\u003emax_mtu = mctp_pcc_max_mtu;\n+\tndev-\u003emin_mtu = MCTP_MIN_MTU;\n+\n+\treturn 0;\n+}\n+\n+static int mctp_pcc_driver_add(struct acpi_device *acpi_dev)\n+{\n+\tstruct mctp_pcc_lookup_context context = {0};\n+\tstruct mctp_pcc_ndev *mctp_pcc_ndev;\n+\tstruct device *dev = \u0026acpi_dev-\u003edev;\n+\tstruct net_device *ndev;\n+\tacpi_handle dev_handle;\n+\tacpi_status status;\n+\tchar name[32];\n+\tint rc;\n+\n+\tdev_dbg(dev, \"Adding mctp_pcc device for HID %s\\n\",\n+\t\tacpi_device_hid(acpi_dev));\n+\tdev_handle = acpi_device_handle(acpi_dev);\n+\tstatus = acpi_walk_resources(dev_handle, \"_CRS\", lookup_pcct_indices,\n+\t\t\t\t     \u0026context);\n+\tif (!ACPI_SUCCESS(status)) {\n+\t\tdev_err(dev, \"FAILED to lookup PCC indexes from CRS\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/*\n+\t * Ensure we have exactly 2 channels: an outbox and an inbox.\n+\t */\n+\tif (context.index != 2)\n+\t\treturn -EINVAL;\n+\n+\tsnprintf(name, sizeof(name), \"mctppcc%d\", context.inbox_index);\n+\tndev = alloc_netdev(sizeof(*mctp_pcc_ndev), name, NET_NAME_PREDICTABLE,\n+\t\t\t    mctp_pcc_setup);\n+\tif (!ndev)\n+\t\treturn -ENOMEM;\n+\n+\tmctp_pcc_ndev = netdev_priv(ndev);\n+\tmctp_pcc_ndev-\u003einbox.index = context.inbox_index;\n+\tmctp_pcc_ndev-\u003einbox.client.dev = dev;\n+\tmctp_pcc_ndev-\u003eoutbox.index = context.outbox_index;\n+\tmctp_pcc_ndev-\u003eoutbox.client.dev = dev;\n+\n+\tmctp_pcc_ndev-\u003eoutbox.client.tx_prepare = mctp_pcc_tx_prepare;\n+\tmctp_pcc_ndev-\u003eoutbox.client.tx_done = mctp_pcc_tx_done;\n+\tmctp_pcc_ndev-\u003eacpi_device = acpi_dev;\n+\tmctp_pcc_ndev-\u003endev = ndev;\n+\tacpi_dev-\u003edriver_data = mctp_pcc_ndev;\n+\trc = check_channel_types(mctp_pcc_ndev);\n+\tif (rc != 0)\n+\t\tgoto free_netdev;\n+\n+\trc = initialize_mtu(ndev);\n+\tif (rc)\n+\t\tgoto free_netdev;\n+\n+\trc = mctp_register_netdev(ndev, NULL, MCTP_PHYS_BINDING_PCC);\n+\tif (rc)\n+\t\tgoto free_netdev;\n+\n+\treturn devm_add_action_or_reset(dev, mctp_cleanup_netdev, ndev);\n+free_netdev:\n+\tfree_netdev(ndev);\n+\treturn rc;\n+}\n+\n+static const struct acpi_device_id mctp_pcc_device_ids[] = {\n+\t{ \"DMT0001\" },\n+\t{}\n+};\n+\n+static struct acpi_driver mctp_pcc_driver = {\n+\t.name = \"mctp_pcc\",\n+\t.class = \"Unknown\",\n+\t.ids = mctp_pcc_device_ids,\n+\t.ops = {\n+\t\t.add = mctp_pcc_driver_add,\n+\t},\n+};\n+\n+module_acpi_driver(mctp_pcc_driver);\n+\n+MODULE_DEVICE_TABLE(acpi, mctp_pcc_device_ids);\n+\n+MODULE_DESCRIPTION(\"MCTP PCC ACPI device\");\n+MODULE_LICENSE(\"GPL\");\n+MODULE_AUTHOR(\"Adam Young \u003cadmiyo@os.amperecomputing.com\u003e\");\ndiff --git a/include/acpi/pcc.h b/include/acpi/pcc.h\nindex 840bfc95bae332..bf97f407683de3 100644\n--- a/include/acpi/pcc.h\n+++ b/include/acpi/pcc.h\n@@ -8,6 +8,7 @@\n \n #include \u003clinux/mailbox_controller.h\u003e\n #include \u003clinux/mailbox_client.h\u003e\n+#include \u003clinux/acpi.h\u003e\n \n struct pcc_mbox_chan {\n \tstruct mbox_chan *mchan;\n@@ -17,6 +18,7 @@ struct pcc_mbox_chan {\n \tu32 latency;\n \tu32 max_access_rate;\n \tu16 min_turnaround_time;\n+\tenum acpi_pcct_type type;\n };\n \n /* Generic Communications Channel Shared Memory Region */\n@@ -37,6 +39,8 @@ struct pcc_mbox_chan {\n extern struct pcc_mbox_chan *\n pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id);\n extern void pcc_mbox_free_channel(struct pcc_mbox_chan *chan);\n+extern int\n+pcc_mbox_query_channel(struct pcc_mbox_chan *q_chan, int subspace_id);\n #else\n static inline struct pcc_mbox_chan *\n pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)\n@@ -44,6 +48,11 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)\n \treturn ERR_PTR(-ENODEV);\n }\n static inline void pcc_mbox_free_channel(struct pcc_mbox_chan *chan) { }\n+static inline int\n+pcc_mbox_query_channel(struct pcc_mbox_chan *q_chan, int subspace_id)\n+{\n+\treturn -ENODEV;\n+}\n #endif\n \n #endif /* _PCC_H */\n"
}

2/1 2026/07/22 14:00 agent patch-evaluator
1m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch adds a new MCTP over PCC driver and modifies the PCC mailbox driver. Both require specific ACPI tables (PCCT and a device with HID DMT0001) to be present in the firmware. Since these ACPI tables are not provided by standard virtualized environments (QEMU/GCE) and cannot be dynamically injected by syzkaller, the code is unreachable and cannot be fuzzed.",
  "WorthFuzzing": false
}

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 ff78bd82b010e2ffe00855dd8c5b5f52160ce080
Author: syz-cluster <triage@syzkaller.com>
Date:   Wed Jul 22 14:00:07 2026 +0000

    syz-cluster: applied patch under review

diff --git a/MAINTAINERS b/MAINTAINERS
index 6940aa3d498b99..9f48dee0f4c6dc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15582,6 +15582,11 @@ F:	include/net/mctpdevice.h
 F:	include/net/netns/mctp.h
 F:	net/mctp/
 
+MANAGEMENT COMPONENT TRANSPORT PROTOCOL (MCTP) over PCC (MCTP-PCC) Driver
+M:	Adam Young <admiyo@os.amperecomputing.com>
+S:	Maintained
+F:	drivers/net/mctp/mctp-pcc.c
+
 MAPLE TREE
 M:	Liam R. Howlett <liam@infradead.org>
 R:	Alice Ryhl <aliceryhl@google.com>
diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c
index 636879ae1db76b..dbfe8dd92ddd26 100644
--- a/drivers/mailbox/pcc.c
+++ b/drivers/mailbox/pcc.c
@@ -270,6 +270,9 @@ static int pcc_mbox_error_check_and_clear(struct pcc_chan_info *pchan)
 	u64 val;
 	int ret;
 
+	if (pchan->type == ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE)
+		return 0;
+
 	ret = pcc_chan_reg_read(&pchan->error, &val);
 	if (ret)
 		return ret;
@@ -314,6 +317,7 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
 {
 	struct pcc_chan_info *pchan;
 	struct mbox_chan *chan = p;
+	int rc;
 
 	pchan = chan->con_priv;
 
@@ -321,14 +325,13 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
 		return IRQ_NONE;
 
 	if (pchan->type == ACPI_PCCT_TYPE_EXT_PCC_MASTER_SUBSPACE &&
-	    !pchan->chan_in_use)
+	    !READ_ONCE(pchan->chan_in_use))
 		return IRQ_NONE;
 
 	if (!pcc_mbox_cmd_complete_check(pchan))
 		return IRQ_NONE;
 
-	if (pcc_mbox_error_check_and_clear(pchan))
-		return IRQ_NONE;
+	rc = pcc_mbox_error_check_and_clear(pchan);
 
 	/*
 	 * Clear this flag after updating interrupt ack register and just
@@ -336,15 +339,60 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
 	 * where the flag is set again to start new transfer. This is
 	 * required to avoid any possible race in updatation of this flag.
 	 */
-	pchan->chan_in_use = false;
-	mbox_chan_received_data(chan, NULL);
-	mbox_chan_txdone(chan, 0);
+	WRITE_ONCE(pchan->chan_in_use, false);
+	if (!rc)
+		mbox_chan_received_data(chan, NULL);
+	mbox_chan_txdone(chan, rc);
 
 	pcc_chan_acknowledge(pchan);
 
 	return IRQ_HANDLED;
 }
 
+/**
+ * pcc_mbox_query_channel - returns information about the channel
+ *              without activating the channel.
+ *
+ * @q_chan: a pointer to an already allocated struct pcc_mbox_chan
+ *              that will be populated with the channel data.
+ * @subspace_id: The PCC Subspace index as parsed in the PCC client
+ *      ACPI package. This is used to lookup the array of PCC
+ *      subspaces as parsed by the PCC Mailbox controller.
+ *
+ * Return: 0 upon success or non-zero upon error.
+ */
+int
+pcc_mbox_query_channel(struct pcc_mbox_chan *q_chan, int subspace_id)
+{
+	struct pcc_mbox_chan *pcc_mchan;
+	struct pcc_chan_info *pchan;
+	struct mbox_chan *chan;
+
+	if (!q_chan)
+		return -EINVAL;
+
+	if (subspace_id < 0 || subspace_id >= pcc_chan_count)
+		return -ENOENT;
+	pchan = chan_info + subspace_id;
+	chan = pchan->chan.mchan;
+	if (IS_ERR(chan)) {
+		pr_err("Channel not found for idx: %d\n", subspace_id);
+		return -EBUSY;
+	}
+	pcc_mchan = &pchan->chan;
+
+	q_chan->shmem_base_addr = pcc_mchan->shmem_base_addr;
+	q_chan->shmem = NULL;
+	q_chan->shmem_size = pcc_mchan->shmem_size;
+	q_chan->latency = pcc_mchan->latency;
+	q_chan->max_access_rate = pcc_mchan->max_access_rate;
+	q_chan->min_turnaround_time = pcc_mchan->min_turnaround_time;
+	q_chan->type = pcc_mchan->type;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pcc_mbox_query_channel);
+
 /**
  * pcc_mbox_request_channel - PCC clients call this function to
  *		request a pointer to their PCC subspace, from which they
@@ -360,7 +408,6 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
 struct pcc_mbox_chan *
 pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
 {
-	struct pcc_mbox_chan *pcc_mchan;
 	struct pcc_chan_info *pchan;
 	struct mbox_chan *chan;
 	int rc;
@@ -375,20 +422,10 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
 		return ERR_PTR(-EBUSY);
 	}
 
-	pcc_mchan = &pchan->chan;
-	pcc_mchan->shmem = acpi_os_ioremap(pcc_mchan->shmem_base_addr,
-					   pcc_mchan->shmem_size);
-	if (!pcc_mchan->shmem)
-		return ERR_PTR(-ENXIO);
-
 	rc = mbox_bind_client(chan, cl);
-	if (rc) {
-		iounmap(pcc_mchan->shmem);
-		pcc_mchan->shmem = NULL;
-		return ERR_PTR(rc);
-	}
-
-	return pcc_mchan;
+	if (rc)
+		return ERR_PTR(-ENXIO);
+	return  &pchan->chan;
 }
 EXPORT_SYMBOL_GPL(pcc_mbox_request_channel);
 
@@ -400,19 +437,13 @@ EXPORT_SYMBOL_GPL(pcc_mbox_request_channel);
  */
 void pcc_mbox_free_channel(struct pcc_mbox_chan *pchan)
 {
-	struct mbox_chan *chan = pchan->mchan;
-	struct pcc_chan_info *pchan_info;
-	struct pcc_mbox_chan *pcc_mbox_chan;
+	struct mbox_chan *chan;
 
+	if (!pchan)
+		return;
+	chan = pchan->mchan;
 	if (!chan || !chan->cl)
 		return;
-	pchan_info = chan->con_priv;
-	pcc_mbox_chan = &pchan_info->chan;
-	if (pcc_mbox_chan->shmem) {
-		iounmap(pcc_mbox_chan->shmem);
-		pcc_mbox_chan->shmem = NULL;
-	}
-
 	mbox_free_channel(chan);
 }
 EXPORT_SYMBOL_GPL(pcc_mbox_free_channel);
@@ -440,7 +471,7 @@ static int pcc_send_data(struct mbox_chan *chan, void *data)
 
 	ret = pcc_chan_reg_read_modify_write(&pchan->db);
 	if (!ret && pchan->plat_irq > 0)
-		pchan->chan_in_use = true;
+		WRITE_ONCE(pchan->chan_in_use, true);
 
 	return ret;
 }
@@ -462,9 +493,15 @@ static bool pcc_last_tx_done(struct mbox_chan *chan)
 static int pcc_startup(struct mbox_chan *chan)
 {
 	struct pcc_chan_info *pchan = chan->con_priv;
+	struct pcc_mbox_chan *pcc_mchan;
 	unsigned long irqflags;
 	int rc;
 
+	pcc_mchan = &pchan->chan;
+	pcc_mchan->shmem = acpi_os_ioremap(pcc_mchan->shmem_base_addr,
+					   pcc_mchan->shmem_size);
+	if (pcc_mchan->shmem  == NULL)
+		return -ENOMEM;
 	/*
 	 * Clear and acknowledge any pending interrupts on responder channel
 	 * before enabling the interrupt
@@ -479,6 +516,8 @@ static int pcc_startup(struct mbox_chan *chan)
 		if (unlikely(rc)) {
 			dev_err(chan->mbox->dev, "failed to register PCC interrupt %d\n",
 				pchan->plat_irq);
+			iounmap(pcc_mchan->shmem);
+			pcc_mchan->shmem = NULL;
 			return rc;
 		}
 	}
@@ -488,15 +527,23 @@ static int pcc_startup(struct mbox_chan *chan)
 
 /**
  * pcc_shutdown - Called from Mailbox Controller code. Used here
- *		to free the interrupt.
+ *		to free the interrupt and unmap the shared memory.
  * @chan: Pointer to Mailbox channel to shutdown.
  */
 static void pcc_shutdown(struct mbox_chan *chan)
 {
 	struct pcc_chan_info *pchan = chan->con_priv;
+	struct pcc_mbox_chan *pcc_mbox_chan;
 
 	if (pchan->plat_irq > 0)
 		devm_free_irq(chan->mbox->dev, pchan->plat_irq, chan);
+	synchronize_irq(pchan->plat_irq);
+
+	pcc_mbox_chan = &pchan->chan;
+	if (pcc_mbox_chan->shmem) {
+		iounmap(pcc_mbox_chan->shmem);
+		pcc_mbox_chan->shmem = NULL;
+	}
 }
 
 static const struct mbox_chan_ops pcc_chan_ops = {
@@ -831,6 +878,7 @@ static int pcc_mbox_probe(struct platform_device *pdev)
 		pcc_parse_subspace_shmem(pchan, pcct_entry);
 
 		pchan->type = pcct_entry->type;
+		pchan->chan.type = pcct_entry->type;
 		pcct_entry = (struct acpi_subtable_header *)
 			((unsigned long) pcct_entry + pcct_entry->length);
 	}
diff --git a/drivers/net/mctp/Kconfig b/drivers/net/mctp/Kconfig
index cf325ab0b1ef55..c9b8f38ff0fb92 100644
--- a/drivers/net/mctp/Kconfig
+++ b/drivers/net/mctp/Kconfig
@@ -47,6 +47,21 @@ config MCTP_TRANSPORT_I3C
 	  A MCTP protocol network device is created for each I3C bus
 	  having a "mctp-controller" devicetree property.
 
+config MCTP_TRANSPORT_PCC
+	tristate "MCTP PCC transport"
+	depends on ACPI
+	depends on PCC
+	depends on CPU_LITTLE_ENDIAN
+	help
+	  Provides a driver to access MCTP devices over PCC transport,
+	  A MCTP protocol network device is created via ACPI for each
+	  entry in the DSDT/SSDT that matches the identifier. The Platform
+	  communication channels are selected from the corresponding
+	  entries in the PCCT.
+
+	  Say y here if you need to connect to MCTP endpoints over PCC. To
+	  compile as a module, use m; the module will be called mctp-pcc.
+
 config MCTP_TRANSPORT_USB
 	tristate "MCTP USB transport"
 	depends on USB
diff --git a/drivers/net/mctp/Makefile b/drivers/net/mctp/Makefile
index c36006849a1e7d..0a591299ffa92d 100644
--- a/drivers/net/mctp/Makefile
+++ b/drivers/net/mctp/Makefile
@@ -1,4 +1,5 @@
 obj-$(CONFIG_MCTP_SERIAL) += mctp-serial.o
 obj-$(CONFIG_MCTP_TRANSPORT_I2C) += mctp-i2c.o
 obj-$(CONFIG_MCTP_TRANSPORT_I3C) += mctp-i3c.o
+obj-$(CONFIG_MCTP_TRANSPORT_PCC) += mctp-pcc.o
 obj-$(CONFIG_MCTP_TRANSPORT_USB) += mctp-usb.o
diff --git a/drivers/net/mctp/mctp-pcc.c b/drivers/net/mctp/mctp-pcc.c
new file mode 100644
index 00000000000000..57c4f3b2151370
--- /dev/null
+++ b/drivers/net/mctp/mctp-pcc.c
@@ -0,0 +1,466 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mctp-pcc.c - Driver for MCTP over PCC.
+ * Copyright (c) 2024-2026, Ampere Computing LLC
+ *
+ */
+
+/* Implementation of
+ * https://www.dmtf.org/sites/default/files/standards/documents/DSP0292.pdf
+ */
+
+#include <linux/acpi.h>
+#include <linux/hrtimer.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/skbuff.h>
+#include <linux/string.h>
+
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/acrestyp.h>
+#include <acpi/actbl.h>
+#include <acpi/pcc.h>
+#include <net/mctp.h>
+#include <net/mctpdevice.h>
+#include <net/pkt_sched.h>
+
+#define MCTP_SIGNATURE          "MCTP"
+#define MCTP_SIGNATURE_LENGTH   (sizeof(MCTP_SIGNATURE) - 1)
+#define MCTP_MIN_MTU            68
+#define PCC_HEADER_SIZE         sizeof(struct acpi_pcct_ext_pcc_shared_memory)
+#define MCTP_PCC_MIN_SIZE       (PCC_HEADER_SIZE + MCTP_MIN_MTU)
+#define PCC_EXTRA_LEN           (PCC_HEADER_SIZE - sizeof(pcc_header.command))
+struct mctp_pcc_mailbox {
+	u32 index;
+	struct pcc_mbox_chan *chan;
+	struct mbox_client client;
+};
+
+/* The netdev structure. One of these per PCC adapter. */
+struct mctp_pcc_ndev {
+	struct net_device *ndev;
+	struct acpi_device *acpi_device;
+	struct mctp_pcc_mailbox inbox;
+	struct mctp_pcc_mailbox outbox;
+};
+
+static void mctp_pcc_client_rx_callback(struct mbox_client *cl, void *mssg)
+{
+	struct acpi_pcct_ext_pcc_shared_memory pcc_header;
+	struct mctp_pcc_ndev *mctp_pcc_ndev;
+	struct mctp_pcc_mailbox *inbox;
+	struct mctp_skb_cb *cb;
+	struct sk_buff *skb;
+	int size;
+
+	mctp_pcc_ndev = container_of(cl, struct mctp_pcc_ndev, inbox.client);
+	inbox = &mctp_pcc_ndev->inbox;
+	memcpy_fromio(&pcc_header, inbox->chan->shmem, sizeof(pcc_header));
+
+	// The message must at least have the PCC command indicating it is an MCTP
+	// message followed by the MCTP header, or we have a malformed message.
+	if (pcc_header.length < sizeof(pcc_header.command) + sizeof(struct mctp_hdr))
+		goto error;
+
+	// If the reported size is larger than the shared memory minus headers,
+	// something is wrong and treat the buffer as corrupted data.
+	if (pcc_header.length > inbox->chan->shmem_size - PCC_EXTRA_LEN)
+		goto error;
+
+	if (memcmp(&pcc_header.command, MCTP_SIGNATURE, MCTP_SIGNATURE_LENGTH) != 0)
+		goto error;
+
+	size = pcc_header.length + PCC_EXTRA_LEN;
+	skb = netdev_alloc_skb(mctp_pcc_ndev->ndev, size);
+	if (!skb)
+		goto error;
+
+	skb_put(skb, size);
+	skb->protocol = htons(ETH_P_MCTP);
+	memcpy_fromio(skb->data, inbox->chan->shmem, size);
+	dev_dstats_rx_add(mctp_pcc_ndev->ndev, size);
+	skb_pull(skb, sizeof(pcc_header));
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+	cb = __mctp_cb(skb);
+	cb->halen = 0;
+	netif_rx(skb);
+	return;
+
+error:
+	dev_dstats_rx_dropped(mctp_pcc_ndev->ndev);
+}
+
+static netdev_tx_t mctp_pcc_tx(struct sk_buff *skb, struct net_device *ndev)
+{
+	struct acpi_pcct_ext_pcc_shared_memory *pcc_header;
+	struct mctp_pcc_ndev *mpnd = netdev_priv(ndev);
+	int len = skb->len;
+
+	if (skb_cow_head(skb, sizeof(*pcc_header)))
+		goto error;
+
+	pcc_header = skb_push(skb, sizeof(*pcc_header));
+	pcc_header->signature = PCC_SIGNATURE | mpnd->outbox.index;
+	pcc_header->flags = PCC_CMD_COMPLETION_NOTIFY;
+	memcpy(&pcc_header->command, MCTP_SIGNATURE, MCTP_SIGNATURE_LENGTH);
+	pcc_header->length = len + MCTP_SIGNATURE_LENGTH;
+
+	if (skb->len > mpnd->outbox.chan->shmem_size)
+		goto error;
+
+	/*
+	 * There is a possibility that the mailbox can be cleared on
+	 * another thread. If that is the case, and we don't restart
+	 * the queue, it will remain permanently stopped.
+	 * Stopping the queue before attempting to send the message
+	 * allows us to always restart it if mbox_send_message succeeds.
+	 */
+	netif_stop_queue(ndev);
+	if (mbox_send_message(mpnd->outbox.chan->mchan, skb) >= 0) {
+		netif_wake_queue(ndev);
+	} else {
+		// Remove the header in case it gets sent again
+		skb_pull(skb, sizeof(*pcc_header));
+		return NETDEV_TX_BUSY;
+	}
+	return NETDEV_TX_OK;
+
+error:
+	dev_dstats_tx_dropped(ndev);
+	kfree_skb(skb);
+	return NETDEV_TX_OK;
+}
+
+static void mctp_pcc_tx_prepare(struct mbox_client *cl, void *mssg)
+{
+	struct mctp_pcc_ndev *mctp_pcc_ndev;
+	struct mctp_pcc_mailbox *outbox;
+	struct sk_buff *skb = mssg;
+
+	mctp_pcc_ndev = container_of(cl, struct mctp_pcc_ndev, outbox.client);
+	outbox = &mctp_pcc_ndev->outbox;
+
+	/* The PCC Mailbox typically does not make use of the mssg pointer
+	 * The mctp-over pcc driver is the only client that uses it.
+	 * This value should always be non-null; it is possible
+	 * that a change in the Mailbox level will break that assumption.
+	 */
+	if (!skb) {
+		netdev_warn_once(mctp_pcc_ndev->ndev,
+				 "%s called with null message.\n", __func__);
+		return;
+	}
+	memcpy_toio(outbox->chan->shmem, skb->data, skb->len);
+}
+
+static void mctp_pcc_tx_done(struct mbox_client *c, void *mssg, int rc)
+{
+	struct mctp_pcc_ndev *mctp_pcc_ndev;
+	struct pcpu_dstats *dstats;
+	struct sk_buff *skb = mssg;
+	unsigned long flags;
+
+	/*
+	 * If there is a packet in flight during driver cleanup
+	 * It may have been freed already.
+	 */
+	if (!mssg)
+		return;
+	mctp_pcc_ndev = container_of(c, struct mctp_pcc_ndev, outbox.client);
+
+	/* Use an IRQ safe update as this is called from HARD IRQ instead of
+	 * dev_dstats_tx_add(mctp_pcc_ndev->ndev, skb->len);
+	 */
+	dstats = this_cpu_ptr(mctp_pcc_ndev->ndev->dstats);
+	flags = u64_stats_update_begin_irqsave(&dstats->syncp);
+
+	if (rc) {
+		u64_stats_inc(&dstats->tx_drops);
+	} else {
+		u64_stats_inc(&dstats->tx_packets);
+		u64_stats_add(&dstats->tx_bytes, skb->len);
+	}
+	u64_stats_update_end_irqrestore(&dstats->syncp, flags);
+	dev_consume_skb_any(skb);
+	netif_wake_queue(mctp_pcc_ndev->ndev);
+}
+
+static int mctp_pcc_open(struct net_device *ndev)
+{
+	struct mctp_pcc_ndev *mctp_pcc_ndev = netdev_priv(ndev);
+	struct mctp_pcc_mailbox *outbox, *inbox;
+
+	outbox = &mctp_pcc_ndev->outbox;
+	inbox = &mctp_pcc_ndev->inbox;
+
+	outbox->chan = pcc_mbox_request_channel(&outbox->client, outbox->index);
+	if (IS_ERR(outbox->chan))
+		return PTR_ERR(outbox->chan);
+	if (outbox->chan->shmem_size < MCTP_PCC_MIN_SIZE) {
+		pcc_mbox_free_channel(outbox->chan);
+		return -EINVAL;
+	}
+
+	inbox->client.rx_callback = mctp_pcc_client_rx_callback;
+	inbox->chan = pcc_mbox_request_channel(&inbox->client, inbox->index);
+	if (IS_ERR(inbox->chan)) {
+		pcc_mbox_free_channel(outbox->chan);
+		return PTR_ERR(inbox->chan);
+	}
+	if (inbox->chan->shmem_size < MCTP_PCC_MIN_SIZE) {
+		pcc_mbox_free_channel(outbox->chan);
+		pcc_mbox_free_channel(inbox->chan);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int mctp_pcc_stop(struct net_device *ndev)
+{
+	struct mctp_pcc_ndev *mctp_pcc_ndev;
+	unsigned int count, idx;
+	struct mbox_chan *chan;
+	struct sk_buff *skb;
+
+	mctp_pcc_ndev = netdev_priv(ndev);
+	chan = mctp_pcc_ndev->outbox.chan->mchan;
+	pcc_mbox_free_channel(mctp_pcc_ndev->inbox.chan);
+	mctp_pcc_ndev->inbox.chan = NULL;
+	scoped_guard(spinlock_irqsave, &chan->lock) {
+		if (chan->active_req !=  MBOX_NO_MSG) {
+			skb = chan->active_req;
+			chan->active_req = MBOX_NO_MSG;
+			dev_dstats_tx_dropped(ndev);
+			dev_consume_skb_any(skb);
+		}
+		while (chan->msg_count > 0) {
+			count = chan->msg_count;
+			idx = chan->msg_free;
+			if (idx >= count)
+				idx -= count;
+			else
+				idx += MBOX_TX_QUEUE_LEN - count;
+			skb = chan->msg_data[idx];
+			dev_dstats_tx_dropped(ndev);
+			dev_consume_skb_any(skb);
+			chan->msg_count--;
+		}
+	}
+	pcc_mbox_free_channel(mctp_pcc_ndev->outbox.chan);
+	mctp_pcc_ndev->outbox.chan = NULL;
+	/*
+	 * If the queue was stopped because the ring buffer was full
+	 * we can restart it here as we now know the ring buffer has
+	 * been emptied and the queue can be used again if the
+	 * netdev is re-opened.
+	 */
+	netif_wake_queue(mctp_pcc_ndev->ndev);
+	return 0;
+}
+
+static const struct net_device_ops mctp_pcc_netdev_ops = {
+	.ndo_open = mctp_pcc_open,
+	.ndo_stop = mctp_pcc_stop,
+	.ndo_start_xmit = mctp_pcc_tx,
+};
+
+static void mctp_pcc_setup(struct net_device *ndev)
+{
+	ndev->type = ARPHRD_MCTP;
+	ndev->hard_header_len = sizeof(struct acpi_pcct_ext_pcc_shared_memory);
+	ndev->tx_queue_len = DEFAULT_TX_QUEUE_LEN;
+	ndev->flags = IFF_NOARP;
+	ndev->netdev_ops = &mctp_pcc_netdev_ops;
+	ndev->needs_free_netdev = true;
+	ndev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS;
+}
+
+struct mctp_pcc_lookup_context {
+	int index;
+	u32 inbox_index;
+	u32 outbox_index;
+};
+
+static acpi_status lookup_pcct_indices(struct acpi_resource *ares,
+				       void *context)
+{
+	struct mctp_pcc_lookup_context *luc = context;
+	struct acpi_resource_address32 *addr;
+
+	if (ares->type != ACPI_RESOURCE_TYPE_ADDRESS32)
+		return AE_OK;
+
+	addr = ACPI_CAST_PTR(struct acpi_resource_address32, &ares->data);
+	switch (luc->index) {
+	case 0:
+		luc->outbox_index = addr[0].address.minimum;
+		break;
+	case 1:
+		luc->inbox_index = addr[0].address.minimum;
+		break;
+	default:
+		return AE_ERROR;
+	}
+	luc->index++;
+	return AE_OK;
+}
+
+static void mctp_cleanup_netdev(void *data)
+{
+	struct net_device *ndev = data;
+
+	mctp_unregister_netdev(ndev);
+}
+
+static int check_channel_types(struct mctp_pcc_ndev *mctp_pcc_ndev)
+{
+	struct mctp_pcc_mailbox *outbox;
+	struct mctp_pcc_mailbox *inbox;
+	struct pcc_mbox_chan chan;
+	int actual_type;
+
+	outbox = &mctp_pcc_ndev->outbox;
+	if (pcc_mbox_query_channel(&chan, outbox->index))
+		return -EINVAL;
+	actual_type = chan.type;
+	if (actual_type != ACPI_PCCT_TYPE_EXT_PCC_MASTER_SUBSPACE) {
+		pr_err("MCTP-PCC outbox channel wrong type: %d", actual_type);
+		return -EINVAL;
+	}
+
+	inbox = &mctp_pcc_ndev->inbox;
+	if (pcc_mbox_query_channel(&chan, inbox->index))
+		return -EINVAL;
+	actual_type = chan.type;
+	if (actual_type != ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE) {
+		pr_err("MCTP-PCC inbox channel wrong type: %d", actual_type);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int initialize_mtu(struct net_device *ndev)
+{
+	struct mctp_pcc_ndev *mctp_pcc_ndev;
+	struct mctp_pcc_mailbox *outbox;
+	struct mctp_pcc_mailbox *inbox;
+	struct pcc_mbox_chan out_chan;
+	struct pcc_mbox_chan in_chan;
+	int mctp_pcc_max_mtu;
+	int inbox_max_mtu;
+
+	mctp_pcc_ndev = netdev_priv(ndev);
+	outbox = &mctp_pcc_ndev->outbox;
+	if (pcc_mbox_query_channel(&out_chan, outbox->index))
+		return -EINVAL;
+	if (out_chan.shmem_size < MCTP_MIN_MTU + sizeof(struct acpi_pcct_ext_pcc_shared_memory))
+		return -EINVAL;
+	mctp_pcc_max_mtu = out_chan.shmem_size - sizeof(struct acpi_pcct_ext_pcc_shared_memory);
+	inbox = &mctp_pcc_ndev->inbox;
+	if (pcc_mbox_query_channel(&in_chan, inbox->index))
+		return -EINVAL;
+	if (in_chan.shmem_size < MCTP_MIN_MTU + sizeof(struct acpi_pcct_ext_pcc_shared_memory))
+		return -EINVAL;
+	inbox_max_mtu = in_chan.shmem_size - sizeof(struct acpi_pcct_ext_pcc_shared_memory);
+
+	if (inbox_max_mtu < mctp_pcc_max_mtu)
+		mctp_pcc_max_mtu = inbox_max_mtu;
+
+	ndev->mtu = MCTP_MIN_MTU;
+	ndev->max_mtu = mctp_pcc_max_mtu;
+	ndev->min_mtu = MCTP_MIN_MTU;
+
+	return 0;
+}
+
+static int mctp_pcc_driver_add(struct acpi_device *acpi_dev)
+{
+	struct mctp_pcc_lookup_context context = {0};
+	struct mctp_pcc_ndev *mctp_pcc_ndev;
+	struct device *dev = &acpi_dev->dev;
+	struct net_device *ndev;
+	acpi_handle dev_handle;
+	acpi_status status;
+	char name[32];
+	int rc;
+
+	dev_dbg(dev, "Adding mctp_pcc device for HID %s\n",
+		acpi_device_hid(acpi_dev));
+	dev_handle = acpi_device_handle(acpi_dev);
+	status = acpi_walk_resources(dev_handle, "_CRS", lookup_pcct_indices,
+				     &context);
+	if (!ACPI_SUCCESS(status)) {
+		dev_err(dev, "FAILED to lookup PCC indexes from CRS\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Ensure we have exactly 2 channels: an outbox and an inbox.
+	 */
+	if (context.index != 2)
+		return -EINVAL;
+
+	snprintf(name, sizeof(name), "mctppcc%d", context.inbox_index);
+	ndev = alloc_netdev(sizeof(*mctp_pcc_ndev), name, NET_NAME_PREDICTABLE,
+			    mctp_pcc_setup);
+	if (!ndev)
+		return -ENOMEM;
+
+	mctp_pcc_ndev = netdev_priv(ndev);
+	mctp_pcc_ndev->inbox.index = context.inbox_index;
+	mctp_pcc_ndev->inbox.client.dev = dev;
+	mctp_pcc_ndev->outbox.index = context.outbox_index;
+	mctp_pcc_ndev->outbox.client.dev = dev;
+
+	mctp_pcc_ndev->outbox.client.tx_prepare = mctp_pcc_tx_prepare;
+	mctp_pcc_ndev->outbox.client.tx_done = mctp_pcc_tx_done;
+	mctp_pcc_ndev->acpi_device = acpi_dev;
+	mctp_pcc_ndev->ndev = ndev;
+	acpi_dev->driver_data = mctp_pcc_ndev;
+	rc = check_channel_types(mctp_pcc_ndev);
+	if (rc != 0)
+		goto free_netdev;
+
+	rc = initialize_mtu(ndev);
+	if (rc)
+		goto free_netdev;
+
+	rc = mctp_register_netdev(ndev, NULL, MCTP_PHYS_BINDING_PCC);
+	if (rc)
+		goto free_netdev;
+
+	return devm_add_action_or_reset(dev, mctp_cleanup_netdev, ndev);
+free_netdev:
+	free_netdev(ndev);
+	return rc;
+}
+
+static const struct acpi_device_id mctp_pcc_device_ids[] = {
+	{ "DMT0001" },
+	{}
+};
+
+static struct acpi_driver mctp_pcc_driver = {
+	.name = "mctp_pcc",
+	.class = "Unknown",
+	.ids = mctp_pcc_device_ids,
+	.ops = {
+		.add = mctp_pcc_driver_add,
+	},
+};
+
+module_acpi_driver(mctp_pcc_driver);
+
+MODULE_DEVICE_TABLE(acpi, mctp_pcc_device_ids);
+
+MODULE_DESCRIPTION("MCTP PCC ACPI device");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Adam Young <admiyo@os.amperecomputing.com>");
diff --git a/include/acpi/pcc.h b/include/acpi/pcc.h
index 840bfc95bae332..bf97f407683de3 100644
--- a/include/acpi/pcc.h
+++ b/include/acpi/pcc.h
@@ -8,6 +8,7 @@
 
 #include <linux/mailbox_controller.h>
 #include <linux/mailbox_client.h>
+#include <linux/acpi.h>
 
 struct pcc_mbox_chan {
 	struct mbox_chan *mchan;
@@ -17,6 +18,7 @@ struct pcc_mbox_chan {
 	u32 latency;
 	u32 max_access_rate;
 	u16 min_turnaround_time;
+	enum acpi_pcct_type type;
 };
 
 /* Generic Communications Channel Shared Memory Region */
@@ -37,6 +39,8 @@ struct pcc_mbox_chan {
 extern struct pcc_mbox_chan *
 pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id);
 extern void pcc_mbox_free_channel(struct pcc_mbox_chan *chan);
+extern int
+pcc_mbox_query_channel(struct pcc_mbox_chan *q_chan, int subspace_id);
 #else
 static inline struct pcc_mbox_chan *
 pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
@@ -44,6 +48,11 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
 	return ERR_PTR(-ENODEV);
 }
 static inline void pcc_mbox_free_channel(struct pcc_mbox_chan *chan) { }
+static inline int
+pcc_mbox_query_channel(struct pcc_mbox_chan *q_chan, int subspace_id)
+{
+	return -ENODEV;
+}
 #endif
 
 #endif /* _PCC_H */

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

Args:
null

Results:
null

Tokens:
							input: 12070
							output: 24
							thoughts: 786
						

Thoughts:
**MCTP/PCC Driver Fuzzing Feasibility**

Okay, so we've got a new driver, `mctp-pcc.c`, implementing MCTP over PCC. That's interesting, a new network driver, and my spidey-sense is tingling – new network drivers are always worth a look for fuzzing potential. It introduces a `MCTP_TRANSPORT_PCC` Kconfig option and touches `pcc.c` with the addition of `pcc_mbox_query_channel` and modifications to the interrupt and channel request logic.

My initial thought is to check reachability in a virtualized environment. PCC, being ACPI-based, makes this a key question. This driver specifically uses the ACPI device ID `DMT0001`. Can we trick QEMU into emulating this? Syzkaller's ACPI table injection capabilities come to mind. But, wait... Syzkaller's ACPI table injection for PCC isn't exactly straightforward or generic. This isn't a *direct* ACPI driver. it's a *network* driver.

Okay, let's refocus. If the device isn't present, `mctp_pcc_driver_add` won't even be called. We need to find a trigger. Can syzkaller do *anything* with ACPI device instantiation here? Nope, the generic `syz_acpi_...` won't help. We can fuzz netdev ops, but we need the device to be created somehow. No, it's an ACPI driver.

Thinking about it more, syzkaller doesn't typically touch ACPI drivers unless they can be instantiated. But hey, there are modifications in `pcc.c`. Given that `pcc_mbox_query_channel` is added, Is `MCTP_TRANSPORT_PCC` still worth fuzzing? Yes, a new network driver.

Alright, let's get back to the code. Specifically, the `mctp_pcc.c`. It registers a netdev using `mctp_register_netdev`, with `mctp_pcc_netdev_ops` containing `ndo_start_xmit = mctp_pcc_tx`. Okay, so if the device *can* be created, we're golden. Fuzzing the netdev operations becomes feasible.

Can syzkaller create ACPI devices? Again, the answer is no. Syzkaller doesn't have a generic ACPI device instantiator.

Let's check the prompt! It says we need to be concerned with "Code paths that are impossible to reach in virtualized environments". Is PCC emulated in QEMU? QEMU has ACPI support, but PCC, specifically? Probably not. It's an ACPI driver that depends on the firmware providing ACPI tables. If the firmware doesn't provide it, the driver won't probe.

So, do we mark it as `WorthFuzzing=true` and see what happens, or is this dead end?

However, the prompt also says, "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."

So let's find if we can execute `mctp_pcc_driver_add`.



4/2 2026/07/22 14:00 tool grepper
0m Args:
{
  "Expression": "mctp_pcc_driver_add"
}

Results:
{
  "Output": "drivers/net/mctp/mctp-pcc.c=350=static int initialize_mtu(struct net_device *ndev)\n--\ndrivers/net/mctp/mctp-pcc.c-383-\ndrivers/net/mctp/mctp-pcc.c:384:static int mctp_pcc_driver_add(struct acpi_device *acpi_dev)\ndrivers/net/mctp/mctp-pcc.c-385-{\n--\ndrivers/net/mctp/mctp-pcc.c=451=static struct acpi_driver mctp_pcc_driver = {\n--\ndrivers/net/mctp/mctp-pcc.c-455-\t.ops = {\ndrivers/net/mctp/mctp-pcc.c:456:\t\t.add = mctp_pcc_driver_add,\ndrivers/net/mctp/mctp-pcc.c-457-\t},\n"
}

5/2 2026/07/22 14:00 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 13878
							output: 21
							thoughts: 215
						

Thoughts:
**Analyzing the ACPI Driver Patch**

Okay, so this is an ACPI driver, which means it's dealing with devices described in the DSDT/SSDT tables, either provided by the firmware or, in the case of something like QEMU, the emulator itself. I know that injecting ACPI tables into Linux is possible – we can do it through configfs (`CONFIG_ACPI_CONFIGFS`) or via initrd. I'm pretty sure Syzkaller isn't set up to do that dynamically for just any arbitrary device right now, though.

Hmm, wait... is there software emulation for MCTP? Ah, yes! I'm seeing it now: MCTP over serial (`mctp-serial`), I2C (`mctp-i2c`), I3C (`mctp-i3c`), and USB (`mctp-usb`). This is specifically MCTP over PCC. The challenge is, how do we trigger `mctp_pcc_driver_add`? It looks like it would be tough without crafting custom ACPI tables.

But wait, the patch also touches `drivers/mailbox/pcc.c`. That's interesting. I need to take a closer look at what modifications they've made to `pcc.c`. That's where the real action is probably happening.



6/2 2026/07/22 14:00 tool grepper
0m Args:
{
  "Expression": "pcc_mbox_irq"
}

Results:
{
  "Output": "drivers/mailbox/pcc.c=289=static void pcc_chan_acknowledge(struct pcc_chan_info *pchan)\n--\ndrivers/mailbox/pcc.c-309-/**\ndrivers/mailbox/pcc.c:310: * pcc_mbox_irq - PCC mailbox interrupt handler\ndrivers/mailbox/pcc.c-311- * @irq:\tinterrupt number\n--\ndrivers/mailbox/pcc.c-315- */\ndrivers/mailbox/pcc.c:316:static irqreturn_t pcc_mbox_irq(int irq, void *p)\ndrivers/mailbox/pcc.c-317-{\n--\ndrivers/mailbox/pcc.c=493=static int pcc_startup(struct mbox_chan *chan)\n--\ndrivers/mailbox/pcc.c-513-\t\t\t\t\t\tIRQF_SHARED : 0;\ndrivers/mailbox/pcc.c:514:\t\trc = devm_request_irq(chan-\u003embox-\u003edev, pchan-\u003eplat_irq, pcc_mbox_irq,\ndrivers/mailbox/pcc.c-515-\t\t\t\t      irqflags, MBOX_IRQ_NAME, chan);\n"
}

7/2 2026/07/22 14:00 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14711
							output: 20
							thoughts: 748
						

Thoughts:
**Analyzing the PCC Mailbox Interrupt Handler**

Okay, so I'm looking at this patch for `pcc.c` and immediately, I see a few key areas that warrant my attention. The primary focus seems to be on how error handling and the `chan_in_use` flag are managed within the interrupt handler `pcc_mbox_irq`. I'm already thinking about potential race conditions here, especially with the use of the `chan_in_use` flag and the interrupt context.

The most notable change is the addition of a check within `pcc_mbox_error_check_and_clear`. Now, if the channel type is `ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE`, the error check is skipped altogether. I need to understand the implications of this. Why are slave subspaces treated differently in this respect? Is this a known hardware quirk, a specific optimization, or a potential source of future issues if not handled carefully? Perhaps this relates to the extended PCC functionality.

Then, down in `pcc_mbox_irq`, I see that the return code from `pcc_mbox_error_check_and_clear` is being used to determine the `mbox_chan_txdone` return value. I need to look at what `pcc_mbox_error_check_and_clear` can return and how that value will affect the overall flow. And the flag `chan_in_use` is using the `READ_ONCE` and `WRITE_ONCE` macros. This is good because it enforces proper memory ordering to prevent races.

The logic flow now is: check if an interrupt is for the PCC channel, check if the channel is in use, and then check for command completion. Only after these checks, is the error check done. Now, I see the error is handled differently for slave subspaces. Finally, if all these checks are successful, the `chan_in_use` flag is cleared using `WRITE_ONCE`. And finally, acknowledge the interrupt in the hardware.

My next step is to trace back to where this `pcc_mbox_irq` function is registered and understand its calling context. Is it called often, and what are the critical paths? Then, I will probe `pcc_mbox_probe` to see how PCC is initialized and if this code is even reachable, or if it handles a seldom used subsystem. Knowing that PCC is an ACPI feature, I'll also consider scenarios where the system might not even have PCC tables. I need to make sure this code path is properly guarded and doesn't lead to unexpected behavior in those cases.



8/2 2026/07/22 14:00 tool grepper
0m Args:
{
  "Expression": "pcc_mbox_probe"
}

Results:
{
  "Output": "drivers/mailbox/pcc.c=758=static int __init acpi_pcc_probe(void)\n--\ndrivers/mailbox/pcc.c-796-/**\ndrivers/mailbox/pcc.c:797: * pcc_mbox_probe - Called when we find a match for the\ndrivers/mailbox/pcc.c-798- *\tPCCT platform device. This is purely used to represent\n--\ndrivers/mailbox/pcc.c-806- */\ndrivers/mailbox/pcc.c:807:static int pcc_mbox_probe(struct platform_device *pdev)\ndrivers/mailbox/pcc.c-808-{\n--\ndrivers/mailbox/pcc.c=905=static struct platform_driver pcc_mbox_driver = {\ndrivers/mailbox/pcc.c:906:\t.probe = pcc_mbox_probe,\ndrivers/mailbox/pcc.c-907-\t.driver = {\n--\ndrivers/mailbox/pcc.c=912=static int __init pcc_init(void)\n--\ndrivers/mailbox/pcc.c-928-\tpcc_pdev = platform_create_bundle(\u0026pcc_mbox_driver,\ndrivers/mailbox/pcc.c:929:\t\t\tpcc_mbox_probe, NULL, 0, NULL, 0);\ndrivers/mailbox/pcc.c-930-\n"
}

9/2 2026/07/22 14:00 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 16399
							output: 21
							thoughts: 83
						

Thoughts:
Here's my understanding of the situation, given my expertise:

**Dissecting the PCC Initialization Sequence**

Okay, so I'm looking at how the Platform Communication Channel (PCC) is being initialized. The core of this seems to be the `pcc_init` function. Right off the bat, I see it's calling `platform_create_bundle` with a few key pieces: the PCC mailbox driver (`pcc_mbox_driver`), a probe function (`pcc_mbox_probe`), and some null arguments. This is standard fare for a platform driver setup; it looks like we're registering the driver and its associated probing mechanism.

But then, I remember this `pcc_init` is called with `subsys_initcall(pcc_init);`. That means it's part of the kernel's subsystem initialization flow, which generally points to early device or module setup, and means that `pcc_init` isn't called directly. It's deferred until a later stage of the boot.

Hang on... I'm missing something, because `pcc_init` checks the return value of `acpi_pcc_probe()`. That immediately changes the game. This whole sequence is going to be ACPI-dependent. This means it is likely probing the ACPI namespace for the PCC device and deciding whether to initialize the PCC functionality based on whether that device is found in the ACPI tables. This explains why `platform_create_bundle` is set up here, and the call to `pcc_mbox_probe` is deferred.



10/2 2026/07/22 14:00 tool grepper
0m Args:
{
  "Expression": "acpi_pcc_probe"
}

Results:
{
  "Output": "drivers/mailbox/pcc.c=729=static void pcc_parse_subspace_shmem(struct pcc_chan_info *pchan,\n--\ndrivers/mailbox/pcc.c-753-/**\ndrivers/mailbox/pcc.c:754: * acpi_pcc_probe - Parse the ACPI tree for the PCCT.\ndrivers/mailbox/pcc.c-755- *\n--\ndrivers/mailbox/pcc.c-757- */\ndrivers/mailbox/pcc.c:758:static int __init acpi_pcc_probe(void)\ndrivers/mailbox/pcc.c-759-{\n--\ndrivers/mailbox/pcc.c=912=static int __init pcc_init(void)\n--\ndrivers/mailbox/pcc.c-920-\t/* Check if PCC support is available. */\ndrivers/mailbox/pcc.c:921:\tret = acpi_pcc_probe();\ndrivers/mailbox/pcc.c-922-\n"
}

11/2 2026/07/22 14:00 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 17104
							output: 36
							thoughts: 13
						

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

**Diving into `acpi_pcc_probe`**

Okay, let's get right down to it. My initial thought is to dive into the `acpi_pcc_probe` function. I need to understand what it's doing, how it's handling the detection of PCC (Platform Communications Channel) devices, and any potential issues that might arise during the probing process. I'm thinking about the common pitfalls with ACPI device enumeration, the various quirks, and the potential for device conflicts. What are the key checks? What ACPI methods is it evaluating? Are there any obvious areas for optimization, or common sources of bugs, like incorrect resource allocation or unexpected return values? I'll be looking for things like mutex contention, potential race conditions, or anything that could lead to deadlocks. I'll need to consider specific hardware platforms and their ACPI implementations, since that can vary wildly. This probe function is really the linchpin. I'll need to understand it intimately.



12/2 2026/07/22 14:00 tool read-file
0m Args:
{
  "File": "drivers/mailbox/pcc.c",
  "FirstLine": 758,
  "LineCount": 40
}

Results:
{
  "Contents": " 758:\tstatic int __init acpi_pcc_probe(void)\n 759:\t{\n 760:\t\tint count, i, rc = 0;\n 761:\t\tacpi_status status;\n 762:\t\tstruct acpi_table_header *pcct_tbl;\n 763:\t\tstruct acpi_subtable_proc proc[ACPI_PCCT_TYPE_RESERVED];\n 764:\t\n 765:\t\tstatus = acpi_get_table(ACPI_SIG_PCCT, 0, \u0026pcct_tbl);\n 766:\t\tif (ACPI_FAILURE(status) || !pcct_tbl)\n 767:\t\t\treturn -ENODEV;\n 768:\t\n 769:\t\t/* Set up the subtable handlers */\n 770:\t\tfor (i = ACPI_PCCT_TYPE_GENERIC_SUBSPACE;\n 771:\t\t     i \u003c ACPI_PCCT_TYPE_RESERVED; i++) {\n 772:\t\t\tproc[i].id = i;\n 773:\t\t\tproc[i].count = 0;\n 774:\t\t\tproc[i].handler = parse_pcc_subspace;\n 775:\t\t}\n 776:\t\n 777:\t\tcount = acpi_table_parse_entries_array(ACPI_SIG_PCCT,\n 778:\t\t\t\tsizeof(struct acpi_table_pcct), proc,\n 779:\t\t\t\tACPI_PCCT_TYPE_RESERVED, MAX_PCC_SUBSPACES);\n 780:\t\tif (count \u003c= 0 || count \u003e MAX_PCC_SUBSPACES) {\n 781:\t\t\tif (count \u003c 0)\n 782:\t\t\t\tpr_warn(\"Error parsing PCC subspaces from PCCT\\n\");\n 783:\t\t\telse\n 784:\t\t\t\tpr_warn(\"Invalid PCCT: %d PCC subspaces\\n\", count);\n 785:\t\n 786:\t\t\trc = -EINVAL;\n 787:\t\t} else {\n 788:\t\t\tpcc_chan_count = count;\n 789:\t\t}\n 790:\t\n 791:\t\tacpi_put_table(pcct_tbl);\n 792:\t\n 793:\t\treturn rc;\n 794:\t}\n 795:\t\n 796:\t/**\n 797:\t * pcc_mbox_probe - Called when we find a match for the\n"
}

13/2 2026/07/22 14:00 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 18029
							output: 19
							thoughts: 574
						

Thoughts:
**Unreachable Code Path Analysis**

Okay, so I'm looking at `acpi_pcc_probe`. It's pretty clear that this function absolutely needs the `PCCT` ACPI table to exist. If it's not there, I get a `-ENODEV` error. My initial thought was whether syzkaller could somehow inject ACPI tables, but, as I understand it, syzkaller doesn't have a built-in mechanism for dynamically injecting arbitrary ACPI tables during fuzzing for, well, *most* devices. Configfs or something similar might offer a workaround but syzkaller doesn't seem to natively do this. No, it relies on QEMU, which in turn, doesn't provide the `PCCT` table by default. So, that's a dead end.

I wondered if there was another entry point to this code, and the `pcc_mbox_query_channel` function *is* exported, but again, this seems to be part of the `acpi_pcc` subsystem. The `mctp_pcc_driver_add` function is called when an ACPI device with HID `DMT0001` is found, and once again, this *requires* ACPI tables. The `mctp_pcc_driver` is an `acpi_driver`, there's no way around it. And the `pcc.c` code is also dependent on the PCCT table.

So, here's my critical question: Given that this code path is unreachable in standard QEMU/GCE environments because of the missing ACPI tables (specifically, the PCCT table and the HID DMT0001 device) and the lack of a straightforward ACPI table injection method in syzkaller, should I mark this as `WorthFuzzing=false`?

The prompt I am working from is pretty clear on this: "Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware." Bingo. Since these particular ACPI tables are *not* present in standard QEMU/GCE environments, and syzkaller does not seem to have the ability to dynamically inject them in a standard way, then it is, indeed, impossible to reach this code.

Wait. Am I missing something? Does syzkaller leverage `CONFIG_ACPI_CONFIGFS`? That would allow userspace to load ACPI tables. But no, syzkaller doesn't use `CONFIG_ACPI_CONFIGFS` to inject arbitrary tables. Wait... has syzkaller created a syz_acpi_... system call? No.

Okay, let's just make sure I'm not missing something with syzkaller's ACPI injection capabilities.



14/2 2026/07/22 14:01 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "syz_acpi"
}

Results:
{
  "error": "no matches"
}

15/2 2026/07/22 14:01 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19232
							output: 117
							thoughts: 1141
						

Thoughts:
**Unreachable Code in a Virtualized Environment**

Okay, let me think this through. The code changes seem related to `configfs`, but I quickly realized Syzkaller won't be able to generate valid ACPI tables to reach it. That's a non-starter. Alright, let's step through the possible entry points into the code. The core functions I'm looking at are all tied to the PCC channel, which, in turn, hinges on the PCCT ACPI table. QEMU, as I know, doesn't provide this table by default, and Syzkaller can't inject it. So, that's already a significant hurdle.

I double-checked to see if QEMU provides the PCCT table in any specific emulation scenario. Nope. It's not standard. Could `mctp-pcc` be fuzzed in some roundabout way? If I set `WorthFuzzing=true`, Syzkaller *could* try, but it's just going to waste time since it won't actually be able to reach any of the code.

Now I remember the prompt: "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." That's not the case here. This is an ACPI driver for very specific hardware (MCTP over PCC), and it relies on ACPI tables that simply aren't present. It is simply unreachable.

I looked for alternatives. Is there a `netdev` entry point? There's `mctp_pcc_tx`, but that only happens if a netdev is created, and the netdev is only created in `mctp_pcc_driver_add`. That, in turn, is an ACPI probe function. So that's also going to fail. That driver, along with other entry points, is never going to run without the PCCT and DMT0001.

Time to review the prompt again: "Return WorthFuzzing=false if the patch only contains: ... 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)." Perfect. This falls squarely into that category.

Let me confirm there's no way to trigger `pcc_mbox_probe` using some platform device creation. That function is probed through a platform driver, but it checks for ACPI tables first before creating the platform device. The ACPI table is not present. And `mctp_pcc_driver`, is an ACPI driver, so is probed by the ACPI subsystem. Since QEMU's ACPI tables do not contain the `DMT0001` HID, it won't be probed either.

Conclusion: the whole patch is dead code in a standard virtualized environment.
I'm returning `WorthFuzzing=false` because the changes are fundamentally unreachable given the current ACPI table configuration in virtualized environments like QEMU.



16/2 2026/07/22 14:01 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch adds a new MCTP over PCC driver and modifies the PCC mailbox driver. Both require specific ACPI tables (PCCT and a device with HID DMT0001) to be present in the firmware. Since these ACPI tables are not provided by standard virtualized environments (QEMU/GCE) and cannot be dynamically injected by syzkaller, the code is unreachable and cannot be fuzzed.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch adds a new MCTP over PCC driver and modifies the PCC mailbox driver. Both require specific ACPI tables (PCCT and a device with HID DMT0001) to be present in the firmware. Since these ACPI tables are not provided by standard virtualized environments (QEMU/GCE) and cannot be dynamically injected by syzkaller, the code is unreachable and cannot be fuzzed.",
  "WorthFuzzing": false
}

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)