From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pl1-f228.google.com (mail-pl1-f228.google.com [209.85.214.228]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6533B3D9028 for ; Tue, 7 Jul 2026 09:58:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.228 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783418326; cv=none; b=ek0A9ICdfbeLQBmGq7q6YXknJa8oeP4u/8QnEDMQvrGRB5N44McYTtbHV1NX7tnetw42kpJFwncWUSnkrLA6UzJOJ72KsuRDsFfa8wuXCFzDnLcs2HGQbplly12UlaCh26VtWNMr14Uemz/vrSOFeqAbD6xXID7XBc2GvbmqSs8= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783418326; c=relaxed/simple; bh=tRAcMJx/OOdgSGOBk/KMfmd7lqgz+xiOI6PKYIav4Mw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=tIg1JNMqsu15uNialHcJ+awiCZaIWLwKSM1Me90+JkO4IjSeAbAfY6/v6d3Lmp7bG1o/QCq5KEGsjDCVBoRJ7UMzqOawudq+jMB41mh1j1YypOgXNGFVQfbCDGDUKkK2LNx/z65NILAECs6Vbb+UW46E18tiOEsIvkFCZwhZEqk= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=broadcom.com; spf=fail smtp.mailfrom=broadcom.com; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b=YKqHT9BC; arc=none smtp.client-ip=209.85.214.228 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=broadcom.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=broadcom.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="YKqHT9BC" Received: by mail-pl1-f228.google.com with SMTP id d9443c01a7336-2cc7e86e7aeso24527225ad.2 for ; Tue, 07 Jul 2026 02:58:44 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783418324; x=1784023124; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=JsYmczMZ2HrXVgHhON7dGS6lbhXo23CvXsjx78XYbGE=; b=apDiIsZ6kvdPVBGn2l9K9Jr57Gnyfs0LD5tmSSFw2Ykix26WqSRB8PMl9aklx/vK34 ldSl8Lux+F5F1WRburoAu151IPth7VEa9hRPkWtNmsF+ISpoaxNYYjZWjwXLHTiRbq6p QbVUMrCrxwm08EkSQjJxqH+vk4DjCw56uHx9uUrd8HMVGJjGtBJY2ypQa2rMpBgQotp7 Vkn34p8fiVkTfMe/Yz+vUzwvYhMwkx0O0vyJlS/nWSxu2uJZA2nRf3TIiC76z513u7wB zsgAsuPVdpgxnhG2hTo/tKXFVSaZTNWarHTCTBpI6p7uo5JDXmDduts4saiUOy3sIuiq 72jQ== X-Gm-Message-State: AOJu0YyrbWBBQvlPu7cIfzk80P+7K5/+uMFf+JsJSeo/lq2Dqfw9quoe heaTHC74Vh+cGIAT0IsK+JgwlUcG5vxCsKi4LrvUmlsLJgMTIvJ327Ou7K5+0PhNKbrdsrE5aj6 iGC7bAjlWrgrstet3NUtVaX2CoaNg5n7EAYED06zNuuLUEw/cDZiGml+Zw2/T2eD1P5byboygZk J0+G2vuvu7OMv/QsvIfCVqDbN8ScZ5WgBz8GnIdm1K7VGDZLtuPBeq+HDVHqjueBdwjtyw+YuDt G4nKsM1pBZyParvJJU= X-Gm-Gg: AfdE7cnsVqAQZOo5VZbeUogSvh7kOgEYVe31aJ63nxTDyieAqlCbwfRCQjovhTPKeAW rjbYq5xRuCnWsLYbVUudouHqnFewHQyWoYYx9q/hTXHwhXlq5IP6nPASlT7OrrSpH2i6afE08w2 PiGveubykECpCvyBIZY9cwOGZ26+BYChY5ALAIlKmJXHYw4DhAdPDJVDkXunCbLmlbVmmW+28E/ GZp3+756U7iCFUx9BIHwEVzEqoSCkJ/kIjkBmOy2MH4IPJ+fGGW6d+0W7FoB7GK2XGw034JlF1A oeGE9VNbZ/oYLbtj4TKxMp+xd3Ecl8Sg0bxXDStwy6fc63DGbb84MYbQeaB3npnRhNjtqtmhckr 848S4aBSZ0PQ6bCfToBxiFqHefnBdkVXBkOoGEJcl45WDCi4qEK2AR4JjtRY74rONHwQ1gvC3Vd BlHA2H8M1oldb1Tdo2bbBWqbWpgDbfULb3rcZgmyDaVWcLgw== X-Received: by 2002:a17:903:244e:b0:2ca:cb8a:f26b with SMTP id d9443c01a7336-2ccbe3f7890mr50526105ad.10.1783418323543; Tue, 07 Jul 2026 02:58:43 -0700 (PDT) Received: from smtp-us-east1-p01-i01-si01.dlp.protect.broadcom.com (address-144-49-247-11.dlp.protect.broadcom.com. [144.49.247.11]) by smtp-relay.gmail.com with ESMTPS id d9443c01a7336-2ccc9bc5636sm1845015ad.2.2026.07.07.02.58.43 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Tue, 07 Jul 2026 02:58:43 -0700 (PDT) X-Relaying-Domain: broadcom.com X-CFilter-Loop: Reflected Received: by mail-pl1-f199.google.com with SMTP id d9443c01a7336-2cacf17c7e0so49969525ad.0 for ; Tue, 07 Jul 2026 02:58:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; t=1783418321; x=1784023121; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=JsYmczMZ2HrXVgHhON7dGS6lbhXo23CvXsjx78XYbGE=; b=YKqHT9BCl5Wi25SUKi2arsIKgqvlxjxTYOqOAdHnwMLvhlXTAtjqsQbrsro0MimRfA bWBpeBglzUc06o7QGonjP8UYju5FGdncKY4sBBnQZybbptKa6jJjSHgCtIMu8QBRfM2Y 1VSQcteWObOlcxmtcKqnDTSFE01rQHoPyvDCQ= X-Received: by 2002:a05:6a21:2c11:b0:3b3:216b:274c with SMTP id adf61e73a8af0-3c08ed578b2mr4803981637.22.1783418321341; Tue, 07 Jul 2026 02:58:41 -0700 (PDT) X-Received: by 2002:a05:6a21:2c11:b0:3b3:216b:274c with SMTP id adf61e73a8af0-3c08ed578b2mr4803949637.22.1783418320847; Tue, 07 Jul 2026 02:58:40 -0700 (PDT) Received: from pc-hpe-top-git-sendmail-server.dhcp.broadcom.net ([192.19.234.250]) by smtp.gmail.com with ESMTPSA id a92af1059eb24-13b659666e7sm8407566c88.7.2026.07.07.02.58.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 07 Jul 2026 02:58:40 -0700 (PDT) From: Pavan Chebbi To: jgg@ziepe.ca, michael.chan@broadcom.com Cc: linux-kernel@vger.kernel.org, dave.jiang@intel.com, saeedm@nvidia.com, jic23@kernel.org, gospo@broadcom.com, selvin.xavier@broadcom.com, leon@kernel.org, kalesh-anakkur.purayil@broadcom.com, Pavan Chebbi Subject: [PATCH v5 fwctl 3/3] fwctl/bnxt: add DMA buffer support for HWRM commands Date: Tue, 7 Jul 2026 15:30:19 +0530 Message-ID: <20260707100019.60553-4-pavan.chebbi@broadcom.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260707100019.60553-1-pavan.chebbi@broadcom.com> References: <20260707100019.60553-1-pavan.chebbi@broadcom.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-DetectorID-Processed: b00c1d49-9d2e-4205-b15f-d015386d3d5e Several HWRM commands carry __le64 DMA address fields in their input structures; firmware reads from or writes to the memory those addresses point to. Because the kernel must own all DMA mappings, userspace shall describe the indirect buffers via fwctl_rpc.driver_data and the driver shall map and patch the commands before sending to firmware. Introduce UAPI types for bnxt_fwctl that help userspace describe the DMA buffers. Have a static per-command descriptor table in the driver that records the details of the DMA fields in each supported HWRM input struct. When a DMA-bearing HWRM command arrives, the driver validates it using the descriptor table. Scope-gated allow-list and timeout value list are updated with the new the commands. Signed-off-by: Pavan Chebbi --- drivers/fwctl/bnxt/main.c | 483 +++++++++++++++++++++++++++++++++++++- include/uapi/fwctl/bnxt.h | 35 +++ 2 files changed, 512 insertions(+), 6 deletions(-) diff --git a/drivers/fwctl/bnxt/main.c b/drivers/fwctl/bnxt/main.c index 5eb3651a1784..23cac9baa7bb 100644 --- a/drivers/fwctl/bnxt/main.c +++ b/drivers/fwctl/bnxt/main.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -31,7 +32,8 @@ static int bnxtctl_open_uctx(struct fwctl_uctx *uctx) bnxtctl_uctx->uctx_caps = BIT(FWCTL_BNXT_INLINE_COMMANDS) | BIT(FWCTL_BNXT_QUERY_COMMANDS) | - BIT(FWCTL_BNXT_SEND_COMMANDS); + BIT(FWCTL_BNXT_SEND_COMMANDS) | + BIT(FWCTL_BNXT_DMA_COMMANDS); return 0; } @@ -55,6 +57,371 @@ static void *bnxtctl_info(struct fwctl_uctx *uctx, size_t *length) return info; } +struct bnxtctl_dma_field { + size_t offset; /* offsetof(hwrm_xxx_input, addr_field) */ + u8 dir; + size_t len_offset; /* offsetof(hwrm_xxx_input, len_field); 0 if the + * command carries no transfer-length field + */ + u8 len_width; /* byte width of the length field: 2 or 4 */ + u8 len_unit; /* bytes represented by one unit of the length field */ + u32 buf_len; /* for commands with no length in payload */ +}; + +struct bnxtctl_cmd_dma_desc { + u16 req_type; + u8 num_fields; + u8 scope_min; + size_t req_size; /* sizeof(struct hwrm_xxx_input) */ + struct bnxtctl_dma_field fields[FWCTL_BNXT_MAX_BUFS]; +}; + +/* + * Per-command DMA buffer descriptor table for HWRM commands that + * carry __le64 DMA address fields in their input + */ +static const struct bnxtctl_cmd_dma_desc bnxtctl_dma_cmds[] = { + { HWRM_NVM_SET_VARIABLE, 1, FWCTL_RPC_CONFIGURATION, + sizeof(struct hwrm_nvm_set_variable_input), + {{ .offset = offsetof(struct hwrm_nvm_set_variable_input, src_data_addr), + .dir = FWCTL_BNXT_BUF_TO_DEVICE, + .len_offset = offsetof(struct hwrm_nvm_set_variable_input, data_len), + .len_width = 2, .len_unit = 1 }} }, + { HWRM_NVM_GET_VARIABLE, 1, FWCTL_RPC_CONFIGURATION, + sizeof(struct hwrm_nvm_get_variable_input), + {{ .offset = offsetof(struct hwrm_nvm_get_variable_input, dest_data_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_nvm_get_variable_input, data_len), + .len_width = 2, .len_unit = 1 }} }, + { HWRM_NVM_READ, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_nvm_read_input), + {{ .offset = offsetof(struct hwrm_nvm_read_input, host_dest_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_nvm_read_input, len), + .len_width = 4, .len_unit = 1 }} }, + { HWRM_NVM_GET_DIR_ENTRIES, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_nvm_get_dir_entries_input), + {{ .offset = offsetof(struct hwrm_nvm_get_dir_entries_input, host_dest_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, .buf_len = FWCTL_BNXT_MAX_DMABUF }} }, + { HWRM_NVM_WRITE, 1, FWCTL_RPC_DEBUG_WRITE, + sizeof(struct hwrm_nvm_write_input), + {{ .offset = offsetof(struct hwrm_nvm_write_input, host_src_addr), + .dir = FWCTL_BNXT_BUF_TO_DEVICE, + .len_offset = offsetof(struct hwrm_nvm_write_input, dir_data_length), + .len_width = 4, .len_unit = 1 }} }, + { HWRM_NVM_MODIFY, 1, FWCTL_RPC_DEBUG_WRITE, + sizeof(struct hwrm_nvm_modify_input), + {{ .offset = offsetof(struct hwrm_nvm_modify_input, host_src_addr), + .dir = FWCTL_BNXT_BUF_TO_DEVICE, + .len_offset = offsetof(struct hwrm_nvm_modify_input, len), + .len_width = 4, .len_unit = 1 }} }, + { HWRM_NVM_RAW_WRITE_BLK, 1, FWCTL_RPC_DEBUG_WRITE_FULL, + sizeof(struct hwrm_nvm_raw_write_blk_input), + {{ .offset = offsetof(struct hwrm_nvm_raw_write_blk_input, host_src_addr), + .dir = FWCTL_BNXT_BUF_TO_DEVICE, + .len_offset = offsetof(struct hwrm_nvm_raw_write_blk_input, len), + .len_width = 4, .len_unit = 1 }} }, + { HWRM_NVM_RAW_DUMP, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_nvm_raw_dump_input), + {{ .offset = offsetof(struct hwrm_nvm_raw_dump_input, host_dest_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_nvm_raw_dump_input, len), + .len_width = 4, .len_unit = 1 }} }, + + { HWRM_FW_GET_STRUCTURED_DATA, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_fw_get_structured_data_input), + {{ .offset = offsetof(struct hwrm_fw_get_structured_data_input, dest_data_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_fw_get_structured_data_input, data_len), + .len_width = 2, .len_unit = 1 }} }, + { HWRM_FW_SET_STRUCTURED_DATA, 1, FWCTL_RPC_DEBUG_WRITE, + sizeof(struct hwrm_fw_set_structured_data_input), + {{ .offset = offsetof(struct hwrm_fw_set_structured_data_input, src_data_addr), + .dir = FWCTL_BNXT_BUF_TO_DEVICE, + .len_offset = offsetof(struct hwrm_fw_set_structured_data_input, data_len), + .len_width = 2, .len_unit = 1 }} }, + { HWRM_FW_LIVEPATCH, 1, FWCTL_RPC_DEBUG_WRITE_FULL, + sizeof(struct hwrm_fw_livepatch_input), + {{ .offset = offsetof(struct hwrm_fw_livepatch_input, host_addr), + .dir = FWCTL_BNXT_BUF_TO_DEVICE, + .len_offset = offsetof(struct hwrm_fw_livepatch_input, patch_len), + .len_width = 4, .len_unit = 1 }} }, + + { HWRM_DBG_COREDUMP_LIST, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_dbg_coredump_list_input), + {{ .offset = offsetof(struct hwrm_dbg_coredump_list_input, host_dest_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_dbg_coredump_list_input, host_buf_len), + .len_width = 4, .len_unit = 1 }} }, + { HWRM_DBG_COREDUMP_RETRIEVE, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_dbg_coredump_retrieve_input), + {{ .offset = offsetof(struct hwrm_dbg_coredump_retrieve_input, host_dest_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_dbg_coredump_retrieve_input, host_buf_len), + .len_width = 4, .len_unit = 1 }} }, + /* read_len32 counts 32-bit words, not bytes (see bnxt_dbg_hwrm_rd_reg()). */ + { HWRM_DBG_READ_DIRECT, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_dbg_read_direct_input), + {{ .offset = offsetof(struct hwrm_dbg_read_direct_input, host_dest_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_dbg_read_direct_input, read_len32), + .len_width = 4, .len_unit = 4 }} }, + { HWRM_DBG_READ_INDIRECT, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_dbg_read_indirect_input), + {{ .offset = offsetof(struct hwrm_dbg_read_indirect_input, host_dest_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_dbg_read_indirect_input, host_dest_addr_len), + .len_width = 4, .len_unit = 1 }} }, + { HWRM_DBG_SERDES_TEST, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_dbg_serdes_test_input), + {{ .offset = offsetof(struct hwrm_dbg_serdes_test_input, resp_data_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_dbg_serdes_test_input, data_len), + .len_width = 2, .len_unit = 1 }} }, + { HWRM_DBG_TOKEN_CFG, 1, FWCTL_RPC_DEBUG_WRITE_FULL, + sizeof(struct hwrm_dbg_token_cfg_input), + {{ .offset = offsetof(struct hwrm_dbg_token_cfg_input, host_src_addr), + .dir = FWCTL_BNXT_BUF_TO_DEVICE, + .len_offset = offsetof(struct hwrm_dbg_token_cfg_input, dbg_token_len), + .len_width = 4, .len_unit = 1 }} }, + + { HWRM_QUEUE_DSCP2PRI_QCFG, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_queue_dscp2pri_qcfg_input), + {{ .offset = offsetof(struct hwrm_queue_dscp2pri_qcfg_input, dest_data_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_queue_dscp2pri_qcfg_input, + dest_data_buffer_size), + .len_width = 2, .len_unit = 1 }} }, + + { HWRM_PORT_QSTATS, 2, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_port_qstats_input), + {{ .offset = offsetof(struct hwrm_port_qstats_input, tx_stat_host_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .buf_len = sizeof(struct tx_port_stats) }, + { .offset = offsetof(struct hwrm_port_qstats_input, rx_stat_host_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .buf_len = sizeof(struct rx_port_stats) }} }, + { HWRM_PORT_QSTATS_EXT, 2, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_port_qstats_ext_input), + {{ .offset = offsetof(struct hwrm_port_qstats_ext_input, tx_stat_host_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_port_qstats_ext_input, tx_stat_size), + .len_width = 2, .len_unit = 1 }, + { .offset = offsetof(struct hwrm_port_qstats_ext_input, rx_stat_host_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_port_qstats_ext_input, rx_stat_size), + .len_width = 2, .len_unit = 1 }} }, + { HWRM_PORT_QSTATS_EXT_PFC_ADV, 2, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_port_qstats_ext_pfc_adv_input), + {{ .offset = offsetof(struct hwrm_port_qstats_ext_pfc_adv_input, + tx_pfc_adv_stat_host_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_port_qstats_ext_pfc_adv_input, + pfc_adv_stat_size), + .len_width = 2, .len_unit = 1 }, + { .offset = offsetof(struct hwrm_port_qstats_ext_pfc_adv_input, + rx_pfc_adv_stat_host_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_port_qstats_ext_pfc_adv_input, + pfc_adv_stat_size), + .len_width = 2, .len_unit = 1 }} }, + { HWRM_PCIE_QSTATS, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_pcie_qstats_input), + {{ .offset = offsetof(struct hwrm_pcie_qstats_input, pcie_stat_host_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_pcie_qstats_input, pcie_stat_size), + .len_width = 2, .len_unit = 1 }} }, + { HWRM_STAT_GENERIC_QSTATS, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_stat_generic_qstats_input), + {{ .offset = offsetof(struct hwrm_stat_generic_qstats_input, + generic_stat_host_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_stat_generic_qstats_input, + generic_stat_size), + .len_width = 2, .len_unit = 1 }} }, + { HWRM_STAT_QUERY_ROCE_STATS, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_stat_query_roce_stats_input), + {{ .offset = offsetof(struct hwrm_stat_query_roce_stats_input, + roce_stat_host_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_stat_query_roce_stats_input, + roce_stat_size), + .len_width = 2, .len_unit = 1 }} }, + { HWRM_STAT_QUERY_ROCE_STATS_EXT, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_stat_query_roce_stats_ext_input), + {{ .offset = offsetof(struct hwrm_stat_query_roce_stats_ext_input, + roce_stat_host_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_stat_query_roce_stats_ext_input, + roce_stat_size), + .len_width = 2, .len_unit = 1 }} }, + + { HWRM_PORT_EVENTS_LOG, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_port_events_log_input), + {{ .offset = offsetof(struct hwrm_port_events_log_input, host_dest_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_port_events_log_input, host_dest_addr_len), + .len_width = 4, .len_unit = 1 }} }, + { HWRM_PORT_PRBS_TEST, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_port_prbs_test_input), + {{ .offset = offsetof(struct hwrm_port_prbs_test_input, resp_data_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_port_prbs_test_input, data_len), + .len_width = 2, .len_unit = 1 }} }, + { HWRM_PORT_DSC_DUMP, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_port_dsc_dump_input), + {{ .offset = offsetof(struct hwrm_port_dsc_dump_input, resp_data_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_port_dsc_dump_input, data_len), + .len_width = 2, .len_unit = 1 }} }, + + { HWRM_SCH_GRP_CFG, 1, FWCTL_RPC_DEBUG_WRITE, + sizeof(struct hwrm_sch_grp_cfg_input), + {{ .offset = offsetof(struct hwrm_sch_grp_cfg_input, fid_table_addr), + .dir = FWCTL_BNXT_BUF_TO_DEVICE, + .len_offset = offsetof(struct hwrm_sch_grp_cfg_input, num_fids), + .len_width = 2, .len_unit = 2 }} }, + { HWRM_SCH_GRP_QCFG, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_sch_grp_qcfg_input), + {{ .offset = offsetof(struct hwrm_sch_grp_qcfg_input, fid_table_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_sch_grp_qcfg_input, fid_table_len), + .len_width = 2, .len_unit = 1 }} }, + + { HWRM_SELFTEST_RETRIEVE_SERDES_DATA, 1, FWCTL_RPC_DEBUG_READ_ONLY, + sizeof(struct hwrm_selftest_retrieve_serdes_data_input), + {{ .offset = offsetof(struct hwrm_selftest_retrieve_serdes_data_input, + resp_data_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_selftest_retrieve_serdes_data_input, + data_len), + .len_width = 2, .len_unit = 1 }} }, + + { HWRM_DBG_PTRACE, 2, FWCTL_RPC_DEBUG_WRITE, + sizeof(struct hwrm_dbg_ptrace_input), + {{ .offset = offsetof(struct hwrm_dbg_ptrace_input, pdi_cmd_buf_addr), + .dir = FWCTL_BNXT_BUF_TO_DEVICE, + .len_offset = offsetof(struct hwrm_dbg_ptrace_input, pdi_req_buf_len), + .len_width = 4, .len_unit = 1 }, + { .offset = offsetof(struct hwrm_dbg_ptrace_input, pdi_resp_buf_addr), + .dir = FWCTL_BNXT_BUF_FROM_DEVICE, + .len_offset = offsetof(struct hwrm_dbg_ptrace_input, pdi_req_buf_len), + .len_width = 4, .len_unit = 1 }} }, +}; + +static const struct bnxtctl_cmd_dma_desc * +bnxtctl_find_dma_desc(u16 req_type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bnxtctl_dma_cmds); i++) + if (bnxtctl_dma_cmds[i].req_type == req_type) + return &bnxtctl_dma_cmds[i]; + return NULL; +} + +static void bnxtctl_zero_dma_fields(void *cmd, + const struct bnxtctl_cmd_dma_desc *desc) +{ + int i; + + for (i = 0; i < desc->num_fields; i++) { + __le64 *field = cmd + desc->fields[i].offset; + + *field = 0; + } +} + +static u32 bnxtctl_read_len_field(void *cmd, const struct bnxtctl_dma_field *f) +{ + if (f->len_width == 2) + return le16_to_cpup((__le16 *)(cmd + f->len_offset)); + return le32_to_cpup((__le32 *)(cmd + f->len_offset)); +} + +static int bnxtctl_check_dma_lens(void *cmd, const struct bnxtctl_cmd_dma_desc *desc, + const struct fwctl_bnxt_driver_data *dd, u32 *lens) +{ + unsigned int i; + + for (i = 0; i < dd->num_bufs; i++) { + const struct bnxtctl_dma_field *f = &desc->fields[i]; + u64 len; + + if (dd->bufs[i].dir != f->dir) + return -EINVAL; + + if (f->len_offset) + len = (u64)bnxtctl_read_len_field(cmd, f) * f->len_unit; + else + len = f->buf_len; + + if (!len || len > FWCTL_BNXT_MAX_DMABUF) + return -EINVAL; + + lens[i] = len; + } + return 0; +} + +static int bnxtctl_map_dma_bufs(struct device *dev, void *cmd, + const struct bnxtctl_cmd_dma_desc *desc, + const struct fwctl_bnxt_driver_data *dd, + const u32 *lens, void **kbufs, + dma_addr_t *dma_addrs, unsigned int *num_mapped) +{ + unsigned int i; + + *num_mapped = 0; + for (i = 0; i < dd->num_bufs; i++) { + enum dma_data_direction dma_dir; + __le64 *field; + + dma_dir = (dd->bufs[i].dir == FWCTL_BNXT_BUF_TO_DEVICE) ? + DMA_TO_DEVICE : DMA_FROM_DEVICE; + + kbufs[i] = dma_alloc_coherent(dev, lens[i], + &dma_addrs[i], GFP_KERNEL); + if (!kbufs[i]) + return -ENOMEM; + + if (dma_dir == DMA_TO_DEVICE && + copy_from_user(kbufs[i], + u64_to_user_ptr(dd->bufs[i].addr), + lens[i])) { + dma_free_coherent(dev, lens[i], kbufs[i], + dma_addrs[i]); + kbufs[i] = NULL; + return -EFAULT; + } + + (*num_mapped)++; + + field = cmd + desc->fields[i].offset; + *field = cpu_to_le64(dma_addrs[i]); + } + return 0; +} + +static int bnxtctl_unmap_dma_bufs(struct device *dev, + const struct fwctl_bnxt_driver_data *dd, + const u32 *lens, void **kbufs, + dma_addr_t *dma_addrs, unsigned int num_mapped) +{ + unsigned int i; + int rc = 0; + + for (i = 0; i < num_mapped; i++) { + if (dd->bufs[i].dir == FWCTL_BNXT_BUF_FROM_DEVICE && + copy_to_user(u64_to_user_ptr(dd->bufs[i].addr), + kbufs[i], lens[i])) + rc = -EFAULT; + + dma_free_coherent(dev, lens[i], kbufs[i], dma_addrs[i]); + } + return rc; +} + /* Caller must hold edev->en_dev_lock */ static bool bnxtctl_validate_rpc(struct bnxt_en_dev *edev, struct bnxt_fw_msg *hwrm_in, @@ -82,6 +449,8 @@ static bool bnxtctl_validate_rpc(struct bnxt_en_dev *edev, case HWRM_NVM_ERASE_DIR_ENTRY: case HWRM_NVM_MOD_DIR_ENTRY: case HWRM_NVM_FIND_DIR_ENTRY: + case HWRM_NVM_SET_VARIABLE: + case HWRM_NVM_GET_VARIABLE: return scope >= FWCTL_RPC_CONFIGURATION; case HWRM_VER_GET: @@ -138,12 +507,45 @@ static bool bnxtctl_validate_rpc(struct bnxt_en_dev *edev, case HWRM_NVM_GET_DEV_INFO: case HWRM_NVM_GET_DIR_INFO: case HWRM_SELFTEST_QLIST: + case HWRM_NVM_READ: + case HWRM_NVM_GET_DIR_ENTRIES: + case HWRM_FW_GET_STRUCTURED_DATA: + case HWRM_DBG_COREDUMP_LIST: + case HWRM_DBG_COREDUMP_RETRIEVE: + case HWRM_DBG_COREDUMP_INITIATE: + case HWRM_DBG_READ_DIRECT: + case HWRM_QUEUE_DSCP2PRI_QCFG: + case HWRM_PORT_QSTATS: + case HWRM_PORT_QSTATS_EXT: + case HWRM_PORT_QSTATS_EXT_PFC_ADV: + case HWRM_PCIE_QSTATS: + case HWRM_STAT_GENERIC_QSTATS: + case HWRM_STAT_QUERY_ROCE_STATS: + case HWRM_STAT_QUERY_ROCE_STATS_EXT: + case HWRM_PORT_EVENTS_LOG: + case HWRM_PORT_PRBS_TEST: + case HWRM_PORT_DSC_DUMP: + case HWRM_DBG_READ_INDIRECT: + case HWRM_DBG_SERDES_TEST: + case HWRM_SCH_GRP_QCFG: + case HWRM_SELFTEST_RETRIEVE_SERDES_DATA: + case HWRM_NVM_RAW_DUMP: return scope >= FWCTL_RPC_DEBUG_READ_ONLY; case HWRM_PORT_PHY_I2C_WRITE: case HWRM_PORT_PHY_MDIO_WRITE: + case HWRM_NVM_WRITE: + case HWRM_NVM_MODIFY: + case HWRM_FW_SET_STRUCTURED_DATA: + case HWRM_SCH_GRP_CFG: + case HWRM_DBG_PTRACE: return scope >= FWCTL_RPC_DEBUG_WRITE; + case HWRM_FW_LIVEPATCH: + case HWRM_NVM_RAW_WRITE_BLK: + case HWRM_DBG_TOKEN_CFG: + return scope >= FWCTL_RPC_DEBUG_WRITE_FULL; + default: return false; } @@ -162,6 +564,15 @@ static unsigned int bnxtctl_get_timeout(struct input *req) case HWRM_NVM_VERIFY_UPDATE: case HWRM_NVM_ERASE_DIR_ENTRY: case HWRM_NVM_MOD_DIR_ENTRY: + case HWRM_NVM_WRITE: + case HWRM_FW_SYNC: + case HWRM_DBG_COREDUMP_LIST: + case HWRM_DBG_COREDUMP_RETRIEVE: + case HWRM_DBG_COREDUMP_INITIATE: + case HWRM_SELFTEST_RETRIEVE_SERDES_DATA: + case HWRM_DBG_SERDES_TEST: + case HWRM_NVM_RAW_WRITE_BLK: + case HWRM_FW_HEALTH_CHECK: return BNXTCTL_HWRM_CMD_TIMEOUT_LONG; case HWRM_FUNC_RESET: return BNXTCTL_HWRM_CMD_TIMEOUT_MEDM; @@ -178,21 +589,59 @@ static void *bnxtctl_fw_rpc(struct fwctl_uctx *uctx, struct bnxtctl_dev *bnxtctl = container_of(uctx->fwctl, struct bnxtctl_dev, fwctl); struct bnxt_en_dev *edev = bnxtctl->aux_priv->edev; + dma_addr_t dma_addrs[FWCTL_BNXT_MAX_BUFS]; + const struct bnxtctl_cmd_dma_desc *desc; + void *kbufs[FWCTL_BNXT_MAX_BUFS] = {0}; + struct fwctl_bnxt_driver_data dd = {0}; + struct device *dev = &edev->pdev->dev; + u32 dma_lens[FWCTL_BNXT_MAX_BUFS]; struct bnxt_fw_msg rpc_in = {0}; + unsigned int num_mapped = 0; + struct input *req = in; int rc; - if (driver_data) - return ERR_PTR(-EOPNOTSUPP); - if (in_len < sizeof(struct input) || in_len > HWRM_MAX_REQ_LEN) return ERR_PTR(-EINVAL); if (*out_len < sizeof(struct output)) return ERR_PTR(-EINVAL); + desc = bnxtctl_find_dma_desc(le16_to_cpu(req->req_type)); + + if (desc) { + if (in_len < desc->req_size) + return ERR_PTR(-EINVAL); + + if (!driver_data) + return ERR_PTR(-EINVAL); + + if (copy_from_user(&dd, u64_to_user_ptr(driver_data), + sizeof(dd))) + return ERR_PTR(-EFAULT); + + if (dd.rsvd || dd.num_bufs != desc->num_fields) + return ERR_PTR(-EINVAL); + + for (unsigned int i = 0; i < dd.num_bufs; i++) { + if (memchr_inv(dd.bufs[i].rsvd, 0, + sizeof(dd.bufs[i].rsvd))) + return ERR_PTR(-EINVAL); + } + + rc = bnxtctl_check_dma_lens(in, desc, &dd, dma_lens); + if (rc) + return ERR_PTR(rc); + + bnxtctl_zero_dma_fields(in, desc); + } else { + /* Non-DMA command: driver_data must be absent. */ + if (driver_data) + return ERR_PTR(-EOPNOTSUPP); + } + rpc_in.msg = in; rpc_in.msg_len = in_len; - rpc_in.resp = kzalloc(*out_len, GFP_KERNEL); + rpc_in.resp = kvzalloc(*out_len, GFP_KERNEL); if (!rpc_in.resp) return ERR_PTR(-ENOMEM); @@ -202,10 +651,21 @@ static void *bnxtctl_fw_rpc(struct fwctl_uctx *uctx, guard(mutex)(&edev->en_dev_lock); if (!bnxtctl_validate_rpc(edev, &rpc_in, scope)) { - kfree(rpc_in.resp); + kvfree(rpc_in.resp); return ERR_PTR(-EPERM); } + if (desc) { + rc = bnxtctl_map_dma_bufs(dev, in, desc, &dd, dma_lens, + kbufs, dma_addrs, &num_mapped); + if (rc) { + bnxtctl_unmap_dma_bufs(dev, &dd, dma_lens, + kbufs, dma_addrs, num_mapped); + kvfree(rpc_in.resp); + return ERR_PTR(rc); + } + } + rc = bnxt_send_msg(edev, &rpc_in); if (rc) { struct output *resp = rpc_in.resp; @@ -220,6 +680,17 @@ static void *bnxtctl_fw_rpc(struct fwctl_uctx *uctx, resp->error_code = cpu_to_le16(rc); } + if (desc) { + int unmap_rc; + + unmap_rc = bnxtctl_unmap_dma_bufs(dev, &dd, dma_lens, kbufs, + dma_addrs, num_mapped); + if (unmap_rc) { + kvfree(rpc_in.resp); + return ERR_PTR(unmap_rc); + } + } + return rpc_in.resp; } diff --git a/include/uapi/fwctl/bnxt.h b/include/uapi/fwctl/bnxt.h index 32e0bfb9a836..1aedae47f5c5 100644 --- a/include/uapi/fwctl/bnxt.h +++ b/include/uapi/fwctl/bnxt.h @@ -12,6 +12,7 @@ enum fwctl_bnxt_commands { FWCTL_BNXT_INLINE_COMMANDS = 0, FWCTL_BNXT_QUERY_COMMANDS, FWCTL_BNXT_SEND_COMMANDS, + FWCTL_BNXT_DMA_COMMANDS, }; /** @@ -23,4 +24,38 @@ enum fwctl_bnxt_commands { struct fwctl_info_bnxt { __u32 uctx_caps; }; + +enum fwctl_bnxt_buf_dir { + FWCTL_BNXT_BUF_TO_DEVICE = 0, + FWCTL_BNXT_BUF_FROM_DEVICE = 1, +}; + +/** + * struct fwctl_bnxt_buf - one indirect DMA buffer descriptor + * @addr: Userspace pointer to the payload data. + * @dir: One of enum fwctl_bnxt_buf_dir. + * @rsvd: Must be zero. + */ +struct fwctl_bnxt_buf { + __aligned_u64 addr; + __u32 dir; + __u32 rsvd[3]; +}; + +#define FWCTL_BNXT_MAX_BUFS 4 +#define FWCTL_BNXT_MAX_DMABUF 0x10000 /* 64 KiB */ + +/** + * struct fwctl_bnxt_driver_data - pointed to by fwctl_rpc::driver_data for bnxt + * @num_bufs: Number of valid entries in @bufs. Must be non-zero and no greater + * than the number of DMA address fields the specific HWRM command supports + * @rsvd: Must be zero. + * @bufs: Array of buffer descriptors. + */ +struct fwctl_bnxt_driver_data { + __u32 num_bufs; + __u32 rsvd; + struct fwctl_bnxt_buf bufs[FWCTL_BNXT_MAX_BUFS]; +}; + #endif -- 2.52.0