summaryrefslogtreecommitdiffstats
path: root/drivers/misc/issei/ham.c
diff options
context:
space:
mode:
authorYazen Ghannam <yazen.ghannam@amd.com>2026-09-03 10:43:25 -0500
committerBorislav Petkov (AMD) <bp@alien8.de>2026-09-10 18:05:43 -0700
commit27600805e62f800bacf990354632eae4e487d34c (patch)
treee8aec0ddfaa0bc264d79af632e3de3f1b75ae4c8 /drivers/misc/issei/ham.c
downloadlinux-stable-27600805e62f800bacf990354632eae4e487d34c.tar.gz
linux-stable-27600805e62f800bacf990354632eae4e487d34c.zip
x86/amd_node: Fix PCI device reference counting in amd_smn_init()grafted
The local "root" pointer is a temporary variable used during the device search. Therefore, refcount related to the search iterators should be cleaned up after the search is complete. Use the __free() cleanup macro to ensure the refcount is decremented when the temporary pointer goes out of scope. Additionally, increment the refcount when caching a root pointer. This ensures the in-use refcount is separate from the temporary search refcounting. Finally, drop the redundant "root = NULL" before the second search loop. The pci_get_class() iterator always decrements the refcount of its "from" argument, so the first loop can only fall through with "root" already NULL. Fixes: 0a4b61d9c2e4 ("x86/amd_node: Fix AMD root device caching") Closes: https://sashiko.dev/#/patchset/20260806160159.230453-1-jason.andryuk%40amd.com Reported-by: Sashiko <sashiko-bot@kernel.org> Assisted-by: LLM Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com> Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org> Cc: <stable@kernel.org> Link: https://patch.msgid.link/20260903154325.74343-1-yazen.ghannam@amd.com
Diffstat (limited to 'drivers/misc/issei/ham.c')
-rw-r--r--drivers/misc/issei/ham.c169
1 files changed, 169 insertions, 0 deletions
diff --git a/drivers/misc/issei/ham.c b/drivers/misc/issei/ham.c
new file mode 100644
index 000000000..674d9733d
--- /dev/null
+++ b/drivers/misc/issei/ham.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2023-2026 Intel Corporation */
+#include <linux/dev_printk.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "dma.h"
+#include "fw_client.h"
+#include "ham.h"
+#include "hw_msg.h"
+#include "issei_dev.h"
+
+static int __issei_ham_send_msg(struct issei_device *idev, u32 length, void *buf)
+{
+ struct issei_dma_data data = { };
+ int ret;
+
+ data.length = length;
+ data.buf = buf;
+ ret = issei_dma_write(idev, &data);
+ if (ret)
+ return ret;
+ return idev->ops->irq_write_generate(idev);
+}
+
+/**
+ * issei_ham_send_start_req - send start request to firmware
+ * @idev: issei device object
+ *
+ * Return: 0 on success, <0 on failures
+ */
+int issei_ham_send_start_req(struct issei_device *idev)
+{
+ struct ham_start_message_req req;
+
+ req.header.cmd = HAM_BUS_CMD_START_REQ;
+ req.supported_version = ISSEI_SUPPORTED_PROTOCOL_VER;
+ req.heci_capabilities_length = 0;
+
+ return __issei_ham_send_msg(idev, sizeof(req), &req);
+}
+
+/**
+ * issei_ham_send_clients_req - send clients request to firmware
+ * @idev: issei device object
+ *
+ * Return: 0 on success, <0 on failures
+ */
+int issei_ham_send_clients_req(struct issei_device *idev)
+{
+ struct ham_get_clients_req req;
+
+ req.header.cmd = HAM_BUS_CMD_CLIENT_REQ;
+
+ return __issei_ham_send_msg(idev, sizeof(req), &req);
+}
+
+static int issei_ham_start_rsp(struct issei_device *idev, const u8 *buf, size_t length)
+{
+ struct ham_start_message_res *res = (struct ham_start_message_res *)buf;
+ int ret;
+
+ if (idev->rst_state != ISSEI_RST_STATE_START) {
+ dev_err(&idev->dev, "Wrong state %d != %d\n",
+ idev->rst_state, ISSEI_RST_STATE_START);
+ return -EPROTO;
+ }
+
+ if (length < sizeof(*res)) {
+ dev_err(&idev->dev, "Small start response size %zu < %zu\n",
+ length, sizeof(*res));
+ return -EPROTO;
+ }
+
+ if (length - sizeof(*res) != res->heci_capabilities_length) {
+ dev_err(&idev->dev, "Wrong start response size %zu != %u\n",
+ length - sizeof(*res), res->heci_capabilities_length);
+ return -EPROTO;
+ }
+
+ memcpy(idev->fw_version, res->fw_version, sizeof(idev->fw_version));
+ idev->fw_protocol_ver = res->supported_version;
+ dev_dbg(&idev->dev, "FW protocol: %u FW version %u.%u.%u.%u", idev->fw_protocol_ver,
+ idev->fw_version[0], idev->fw_version[1],
+ idev->fw_version[2], idev->fw_version[3]);
+
+ ret = issei_ham_send_clients_req(idev);
+ if (ret == -EBUSY)
+ ret = 0;
+
+ return ret;
+}
+
+static int issei_ham_client_rsp(struct issei_device *idev, const u8 *buf, size_t length)
+{
+ struct ham_get_clients_res *res = (struct ham_get_clients_res *)buf;
+ struct ham_client_properties *client;
+
+ if (idev->rst_state != ISSEI_RST_STATE_CLIENT_ENUM) {
+ dev_err(&idev->dev, "Wrong state %d != %d\n",
+ idev->rst_state, ISSEI_RST_STATE_CLIENT_ENUM);
+ return -EPROTO;
+ }
+
+ if (length < sizeof(*res)) {
+ dev_err(&idev->dev, "Small response size %zu < %zu\n", length, sizeof(*res));
+ return -EPROTO;
+ }
+
+ if (length - sizeof(*res) != res->client_count * sizeof(struct ham_client_properties)) {
+ dev_err(&idev->dev, "Wrong response size %zu < %zu\n",
+ length - sizeof(*res),
+ res->client_count * sizeof(struct ham_client_properties));
+ return -EPROTO;
+ }
+
+ guard(mutex)(&idev->client_lock);
+
+ for (size_t i = 0; i < res->client_count; i++) {
+ client = &res->clients_props[i];
+ dev_dbg(&idev->dev, "client: id = %u ver = %u uuid = %pUb mtu = %u flags = %u",
+ client->client_number, client->protocol_ver, &client->client_uuid,
+ client->client_mtu, client->flags);
+ issei_fw_cl_create(idev, client->client_number, client->protocol_ver,
+ &client->client_uuid, client->client_mtu, client->flags);
+ }
+ return 0;
+}
+
+static int __issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length)
+{
+ struct ham_bus_message *hdr = (struct ham_bus_message *)buf;
+
+ if (length < sizeof(*hdr)) {
+ dev_err(&idev->dev, "Small bus message size %zu < %zu\n",
+ length, sizeof(*hdr));
+ return -EPROTO;
+ }
+
+ switch (hdr->cmd) {
+ case HAM_BUS_CMD_START_RSP:
+ return issei_ham_start_rsp(idev, buf, length);
+
+ case HAM_BUS_CMD_CLIENT_RSP:
+ return issei_ham_client_rsp(idev, buf, length);
+
+ default:
+ dev_err(&idev->dev, "Unexpected command 0x%x", hdr->cmd);
+ return -EPROTO;
+ }
+}
+
+/**
+ * issei_ham_process_ham_rsp - process response from firmware and release buffer
+ * @idev: issei device object
+ * @buf: response buffer
+ * @length: response buffer length
+ *
+ * Return: 0 on success, <0 on failures
+ */
+int issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, size_t length)
+{
+ int ret;
+
+ ret = __issei_ham_process_ham_rsp(idev, buf, length);
+ kfree(buf);
+ return ret;
+}