From nobody Sat Nov 15 16:09:07 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1749486887; cv=none; d=zohomail.com; s=zohoarc; b=hRF8QJ16gxrcd5KqsU6Qa9o/BqWKIXfCwB3IcE1is2Lap7ZPzRa5f51mZj42WOzmHW7FQ2caOeFmg0Lia4kESVsnjOrJ4bWnAKr2xIC4pM8ylsejLTrgBvEcAKRpdx263T6Pm1N8NsWBTJPWUNhb6TZdwvbVrbaY8klKNlsbRRg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749486887; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Sender:Subject:Subject:To:To:Message-Id; bh=j/NflZww90rTdO2AvZOIhqyHNfBq4ET39HHwz13O6fA=; b=JGZbqTaMBivMSOCuMgcqGuJ39cYpxPiu4ReAtoOINClwmqf/GSSp7+fPd54yPG0jNrvk4UU2L8F1fFPoubhQoz3PN4khcgihBohk9z0VqapaqN0TyEBCRrrgz/pSIjdAY1WzpIzNKe0Biu1rFr+rxpBMs17ndJDf0BvkogvOomw= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749486887452978.9854205407235; Mon, 9 Jun 2025 09:34:47 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uOfSJ-00025w-Ou; Mon, 09 Jun 2025 12:34:39 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uOfRt-0001YB-26 for qemu-devel@nongnu.org; Mon, 09 Jun 2025 12:34:21 -0400 Received: from [185.176.79.56] (helo=frasgout.his.huawei.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uOfRq-0001YA-UD for qemu-devel@nongnu.org; Mon, 09 Jun 2025 12:34:12 -0400 Received: from mail.maildlp.com (unknown [172.18.186.231]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4bGHYP30K0z6M52w; Tue, 10 Jun 2025 00:33:45 +0800 (CST) Received: from frapeml500008.china.huawei.com (unknown [7.182.85.71]) by mail.maildlp.com (Postfix) with ESMTPS id F0FC91404C5; Tue, 10 Jun 2025 00:34:06 +0800 (CST) Received: from SecurePC-101-06.china.huawei.com (10.122.19.247) by frapeml500008.china.huawei.com (7.182.85.71) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2507.39; Mon, 9 Jun 2025 18:34:06 +0200 To: Klaus Jensen , , Fan Ni , Anisa Su , , , CC: , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Subject: [RFC PATCH qemu 1/5] hw/i2c: add smbus pec utility function Date: Mon, 9 Jun 2025 17:33:29 +0100 Message-ID: <20250609163334.922346-2-Jonathan.Cameron@huawei.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250609163334.922346-1-Jonathan.Cameron@huawei.com> References: <20250609163334.922346-1-Jonathan.Cameron@huawei.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Originating-IP: [10.122.19.247] X-ClientProxiedBy: lhrpeml100012.china.huawei.com (7.191.174.184) To frapeml500008.china.huawei.com (7.182.85.71) X-Host-Lookup-Failed: Reverse DNS lookup failed for 185.176.79.56 (deferred) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=185.176.79.56; envelope-from=jonathan.cameron@huawei.com; helo=frasgout.his.huawei.com X-Spam_score_int: -33 X-Spam_score: -3.4 X-Spam_bar: --- X-Spam_report: (-3.4 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-to: Jonathan Cameron From: Jonathan Cameron via Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1749486887914116600 Content-Type: text/plain; charset="utf-8" From: Klaus Jensen Add i2c_smbus_pec() to calculate the SMBus Packet Error Code for a message. Reviewed-by: Jonathan Cameron Signed-off-by: Klaus Jensen Acked-by: Corey Minyard Link: https://lore.kernel.org/r/20230914-nmi-i2c-v6-1-11bbb4f74d18@samsung.= com Signed-off-by: Jonathan Cameron --- include/hw/i2c/smbus_master.h | 2 ++ hw/i2c/smbus_master.c | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/include/hw/i2c/smbus_master.h b/include/hw/i2c/smbus_master.h index bb13bc423c..d90f81767d 100644 --- a/include/hw/i2c/smbus_master.h +++ b/include/hw/i2c/smbus_master.h @@ -27,6 +27,8 @@ =20 #include "hw/i2c/i2c.h" =20 +uint8_t i2c_smbus_pec(uint8_t crc, uint8_t *buf, size_t len); + /* Master device commands. */ int smbus_quick_command(I2CBus *bus, uint8_t addr, int read); int smbus_receive_byte(I2CBus *bus, uint8_t addr); diff --git a/hw/i2c/smbus_master.c b/hw/i2c/smbus_master.c index 6a53c34e70..01a8e47002 100644 --- a/hw/i2c/smbus_master.c +++ b/hw/i2c/smbus_master.c @@ -15,6 +15,32 @@ #include "hw/i2c/i2c.h" #include "hw/i2c/smbus_master.h" =20 +static uint8_t crc8(uint16_t data) +{ + int i; + + for (i =3D 0; i < 8; i++) { + if (data & 0x8000) { + data ^=3D 0x1070U << 3; + } + + data <<=3D 1; + } + + return (uint8_t)(data >> 8); +} + +uint8_t i2c_smbus_pec(uint8_t crc, uint8_t *buf, size_t len) +{ + int i; + + for (i =3D 0; i < len; i++) { + crc =3D crc8((crc ^ buf[i]) << 8); + } + + return crc; +} + /* Master device commands. */ int smbus_quick_command(I2CBus *bus, uint8_t addr, int read) { --=20 2.48.1 From nobody Sat Nov 15 16:09:07 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1749486929; cv=none; d=zohomail.com; s=zohoarc; b=Rjcp/t+z5NpAFdWN6BrtRUG9+OtZYE4WJdHAfEXl5p3l+tfA3pwner0X8UWXSuujxx9/QmJRNrO9BKPWBtTHmLGxtfh89wmPgUf3++I8YMpORkabrs4YL2NTcqBzQhbcRnCiWplpu8UW8hQxTtFUujy31QaxsUGna6W7gwuBYGY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749486929; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Sender:Subject:Subject:To:To:Message-Id; bh=FJbFlQycHNCMzQqBRJmdgtTLYCMleFBTWzDG8Rvcmkw=; b=d1r7ODU8kc7ZJ+WZgvEQjsq4RL0BOV9NJozTIQj0HY+/tLNvhgNqSs0+wje5xyudEPFlkyZPoLdQAcVuN0SIdEpYmBKlt/15l6nJx5SkI7FMgHLzLVHH+Al1gNa+pdXhMluTsy8TabXx+Zt2PyxkRtw1ZVjFqhsF3UTapbyjz4M= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749486929392970.3262300379158; Mon, 9 Jun 2025 09:35:29 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uOfSg-0002W3-1l; Mon, 09 Jun 2025 12:35:02 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uOfST-0002Ix-2l for qemu-devel@nongnu.org; Mon, 09 Jun 2025 12:34:52 -0400 Received: from [185.176.79.56] (helo=frasgout.his.huawei.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uOfSP-0001oP-UQ for qemu-devel@nongnu.org; Mon, 09 Jun 2025 12:34:48 -0400 Received: from mail.maildlp.com (unknown [172.18.186.216]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4bGHTY58rfz6L5QM; Tue, 10 Jun 2025 00:30:25 +0800 (CST) Received: from frapeml500008.china.huawei.com (unknown [7.182.85.71]) by mail.maildlp.com (Postfix) with ESMTPS id 30D7514020C; Tue, 10 Jun 2025 00:34:38 +0800 (CST) Received: from SecurePC-101-06.china.huawei.com (10.122.19.247) by frapeml500008.china.huawei.com (7.182.85.71) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2507.39; Mon, 9 Jun 2025 18:34:37 +0200 To: Klaus Jensen , , Fan Ni , Anisa Su , , , CC: , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Subject: [RFC PATCH qemu 2/5] hw/i2c: add mctp core Date: Mon, 9 Jun 2025 17:33:30 +0100 Message-ID: <20250609163334.922346-3-Jonathan.Cameron@huawei.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250609163334.922346-1-Jonathan.Cameron@huawei.com> References: <20250609163334.922346-1-Jonathan.Cameron@huawei.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Originating-IP: [10.122.19.247] X-ClientProxiedBy: lhrpeml100012.china.huawei.com (7.191.174.184) To frapeml500008.china.huawei.com (7.182.85.71) X-Host-Lookup-Failed: Reverse DNS lookup failed for 185.176.79.56 (deferred) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=185.176.79.56; envelope-from=jonathan.cameron@huawei.com; helo=frasgout.his.huawei.com X-Spam_score_int: -33 X-Spam_score: -3.4 X-Spam_bar: --- X-Spam_report: (-3.4 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-to: Jonathan Cameron From: Jonathan Cameron via Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1749486931168116600 From: Klaus Jensen Add an abstract MCTP over I2C endpoint model. This implements MCTP control message handling as well as handling the actual I2C transport (packetization). Devices are intended to derive from this and implement the class methods. Parts of this implementation is inspired by code[1] previously posted by Jonathan Cameron. Squashed a fix[2] from Matt Johnston. [1]: https://lore.kernel.org/qemu-devel/20220520170128.4436-1-Jonathan.Ca= meron@huawei.com/ [2]: https://lore.kernel.org/qemu-devel/20221121080445.GA29062@codeconstr= uct.com.au/ Tested-by: Jonathan Cameron Reviewed-by: Jonathan Cameron Signed-off-by: Klaus Jensen Acked-by: Corey Minyard Link: https://lore.kernel.org/r/20230914-nmi-i2c-v6-2-11bbb4f74d18@samsung.= com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 7 + include/hw/i2c/mctp.h | 125 ++++++++++++ include/net/mctp.h | 35 ++++ hw/i2c/mctp.c | 431 ++++++++++++++++++++++++++++++++++++++++++ hw/arm/Kconfig | 1 + hw/i2c/Kconfig | 4 + hw/i2c/meson.build | 1 + hw/i2c/trace-events | 14 ++ 8 files changed, 618 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index aa6763077e..4ad35a1aa3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3782,6 +3782,13 @@ F: include/hw/fsi/* F: docs/specs/fsi.rst F: tests/qtest/aspeed_fsi-test.c =20 +MCTP I2C Transport +M: Klaus Jensen +S: Maintained +F: hw/i2c/mctp.c +F: include/hw/i2c/mctp.h +F: include/net/mctp.h + Firmware schema specifications M: Philippe Mathieu-Daud=C3=A9 R: Daniel P. Berrange diff --git a/include/hw/i2c/mctp.h b/include/hw/i2c/mctp.h new file mode 100644 index 0000000000..10c3fb9048 --- /dev/null +++ b/include/hw/i2c/mctp.h @@ -0,0 +1,125 @@ +#ifndef QEMU_I2C_MCTP_H +#define QEMU_I2C_MCTP_H + +#include "qom/object.h" +#include "hw/qdev-core.h" + +#define TYPE_MCTP_I2C_ENDPOINT "mctp-i2c-endpoint" +OBJECT_DECLARE_TYPE(MCTPI2CEndpoint, MCTPI2CEndpointClass, MCTP_I2C_ENDPOI= NT) + +struct MCTPI2CEndpointClass { + I2CSlaveClass parent_class; + + /** + * put_buf() - receive incoming message fragment + * + * Return 0 for success or negative for error. + */ + int (*put_buf)(MCTPI2CEndpoint *mctp, uint8_t *buf, size_t len); + + /** + * get_buf() - provide pointer to message fragment + * + * Called by the mctp subsystem to request a pointer to the next messa= ge + * fragment. Subsequent calls MUST return next fragment (if any). + * + * Must return the number of bytes in message fragment. + */ + size_t (*get_buf)(MCTPI2CEndpoint *mctp, const uint8_t **buf, + size_t maxlen, uint8_t *mctp_flags); + + /** + * handle() - handle an MCTP message + * + * Called by the mctp subsystem when a full message has been delivered= and + * may be parsed and processed. + */ + void (*handle)(MCTPI2CEndpoint *mctp); + + /** + * reset() - reset internal state + * + * Called by the mctp subsystem in the event of some transport error. + * Implementation must reset its internal state and drop any fragments + * previously receieved. + */ + void (*reset)(MCTPI2CEndpoint *mctp); + + /** + * get_types() - provide supported mctp message types + * + * Must provide a buffer with a full MCTP supported message types payl= oad + * (i.e. `0x0(SUCCESS),0x1(COUNT),0x4(NMI)`). + * + * Returns the size of the response. + */ + size_t (*get_types)(MCTPI2CEndpoint *mctp, const uint8_t **data); +}; + +/* + * Maximum value of the SMBus Block Write "Byte Count" field (8 bits). + * + * This is the count of bytes that follow the Byte Count field and up to, = but + * not including, the PEC byte. + */ +#define I2C_MCTP_MAXBLOCK 255 + +/* + * Maximum Transmission Unit under I2C. + * + * This is for the MCTP Packet Payload (255, subtracting the 4 byte MCTP P= acket + * Header and the 1 byte MCTP/I2C piggy-backed source address). + */ +#define I2C_MCTP_MAXMTU (I2C_MCTP_MAXBLOCK - (sizeof(MCTPPacketHeader) + 1= )) + +/* + * Maximum length of an MCTP/I2C packet. + * + * This is the sum of the three I2C header bytes (Destination target addre= ss, + * Command Code and Byte Count), the maximum number of bytes in a message = (255) + * and the 1 byte Packet Error Code. + */ +#define I2C_MCTP_MAX_LENGTH (3 + I2C_MCTP_MAXBLOCK + 1) + +typedef enum { + I2C_MCTP_STATE_IDLE, + I2C_MCTP_STATE_RX_STARTED, + I2C_MCTP_STATE_RX, + I2C_MCTP_STATE_WAIT_TX, + I2C_MCTP_STATE_TX, +} MCTPState; + +typedef enum { + I2C_MCTP_STATE_TX_START_SEND, + I2C_MCTP_STATE_TX_SEND_BYTE, +} MCTPTxState; + +typedef struct MCTPI2CEndpoint { + I2CSlave parent_obj; + I2CBus *i2c; + + MCTPState state; + + /* mctp endpoint identifier */ + uint8_t my_eid; + + uint8_t buffer[I2C_MCTP_MAX_LENGTH]; + uint64_t pos; + size_t len; + + struct { + MCTPTxState state; + bool is_control; + + uint8_t eid; + uint8_t addr; + uint8_t pktseq; + uint8_t tag; + + QEMUBH *bh; + } tx; +} MCTPI2CEndpoint; + +void i2c_mctp_schedule_send(MCTPI2CEndpoint *mctp); + +#endif /* QEMU_I2C_MCTP_H */ diff --git a/include/net/mctp.h b/include/net/mctp.h new file mode 100644 index 0000000000..5d26d855db --- /dev/null +++ b/include/net/mctp.h @@ -0,0 +1,35 @@ +#ifndef QEMU_MCTP_H +#define QEMU_MCTP_H + +#include "hw/registerfields.h" + +/* DSP0236 1.3.0, Section 8.3.1 */ +#define MCTP_BASELINE_MTU 64 + +/* DSP0236 1.3.0, Table 1, Message body */ +FIELD(MCTP_MESSAGE_H, TYPE, 0, 7) +FIELD(MCTP_MESSAGE_H, IC, 7, 1) + +/* DSP0236 1.3.0, Table 1, MCTP transport header */ +FIELD(MCTP_H_FLAGS, TAG, 0, 3); +FIELD(MCTP_H_FLAGS, TO, 3, 1); +FIELD(MCTP_H_FLAGS, PKTSEQ, 4, 2); +FIELD(MCTP_H_FLAGS, EOM, 6, 1); +FIELD(MCTP_H_FLAGS, SOM, 7, 1); + +/* DSP0236 1.3.0, Figure 4 */ +typedef struct MCTPPacketHeader { + uint8_t version; + struct { + uint8_t dest; + uint8_t source; + } eid; + uint8_t flags; +} MCTPPacketHeader; + +typedef struct MCTPPacket { + MCTPPacketHeader hdr; + uint8_t payload[]; +} MCTPPacket; + +#endif /* QEMU_MCTP_H */ diff --git a/hw/i2c/mctp.c b/hw/i2c/mctp.c new file mode 100644 index 0000000000..cf45a46706 --- /dev/null +++ b/hw/i2c/mctp.c @@ -0,0 +1,431 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * SPDX-FileCopyrightText: Copyright (c) 2023 Samsung Electronics Co., Ltd. + * SPDX-FileContributor: Klaus Jensen + */ + +#include "qemu/osdep.h" +#include "qemu/main-loop.h" + +#include "hw/qdev-properties.h" +#include "hw/i2c/i2c.h" +#include "hw/i2c/smbus_master.h" +#include "hw/i2c/mctp.h" +#include "net/mctp.h" + +#include "trace.h" + +/* DSP0237 1.2.0, Figure 1 */ +typedef struct MCTPI2CPacketHeader { + uint8_t dest; +#define MCTP_I2C_COMMAND_CODE 0xf + uint8_t command_code; + uint8_t byte_count; + uint8_t source; +} MCTPI2CPacketHeader; + +typedef struct MCTPI2CPacket { + MCTPI2CPacketHeader i2c; + MCTPPacket mctp; +} MCTPI2CPacket; + +#define i2c_mctp_payload_offset offsetof(MCTPI2CPacket, mctp.payload) +#define i2c_mctp_payload(buf) (buf + i2c_mctp_payload_offset) + +/* DSP0236 1.3.0, Figure 20 */ +typedef struct MCTPControlMessage { +#define MCTP_MESSAGE_TYPE_CONTROL 0x0 + uint8_t type; +#define MCTP_CONTROL_FLAGS_RQ (1 << 7) +#define MCTP_CONTROL_FLAGS_D (1 << 6) + uint8_t flags; + uint8_t command_code; + uint8_t data[]; +} MCTPControlMessage; + +enum MCTPControlCommandCodes { + MCTP_CONTROL_SET_EID =3D 0x01, + MCTP_CONTROL_GET_EID =3D 0x02, + MCTP_CONTROL_GET_VERSION =3D 0x04, + MCTP_CONTROL_GET_MESSAGE_TYPE_SUPPORT =3D 0x05, +}; + +#define MCTP_CONTROL_ERROR_UNSUPPORTED_CMD 0x5 + +#define i2c_mctp_control_data_offset \ + (i2c_mctp_payload_offset + offsetof(MCTPControlMessage, data)) +#define i2c_mctp_control_data(buf) (buf + i2c_mctp_control_data_offset) + +/** + * The byte count field in the SMBUS Block Write containers the number of = bytes + * *following* the field itself. + * + * This is at least 5. + * + * 1 byte for the MCTP/I2C piggy-backed I2C source address in addition to = the + * size of the MCTP transport/packet header. + */ +#define MCTP_I2C_BYTE_COUNT_OFFSET (sizeof(MCTPPacketHeader) + 1) + +void i2c_mctp_schedule_send(MCTPI2CEndpoint *mctp) +{ + I2CBus *i2c =3D I2C_BUS(qdev_get_parent_bus(DEVICE(mctp))); + + mctp->tx.state =3D I2C_MCTP_STATE_TX_START_SEND; + + i2c_bus_master(i2c, mctp->tx.bh); +} + +static void i2c_mctp_tx(void *opaque) +{ + DeviceState *dev =3D DEVICE(opaque); + I2CBus *i2c =3D I2C_BUS(qdev_get_parent_bus(dev)); + I2CSlave *slave =3D I2C_SLAVE(dev); + MCTPI2CEndpoint *mctp =3D MCTP_I2C_ENDPOINT(dev); + MCTPI2CEndpointClass *mc =3D MCTP_I2C_ENDPOINT_GET_CLASS(mctp); + MCTPI2CPacket *pkt =3D (MCTPI2CPacket *)mctp->buffer; + uint8_t flags =3D 0; + + switch (mctp->tx.state) { + case I2C_MCTP_STATE_TX_SEND_BYTE: + if (mctp->pos < mctp->len) { + uint8_t byte =3D mctp->buffer[mctp->pos]; + + trace_i2c_mctp_tx_send_byte(mctp->pos, byte); + + /* send next byte */ + i2c_send_async(i2c, byte); + + mctp->pos++; + + break; + } + + /* packet sent */ + i2c_end_transfer(i2c); + + /* end of any control data */ + mctp->len =3D 0; + + /* fall through */ + + case I2C_MCTP_STATE_TX_START_SEND: + if (mctp->tx.is_control) { + /* packet payload is already in buffer; max 1 packet */ + flags =3D FIELD_DP8(flags, MCTP_H_FLAGS, SOM, 1); + flags =3D FIELD_DP8(flags, MCTP_H_FLAGS, EOM, 1); + } else { + const uint8_t *payload; + + /* get message bytes from derived device */ + mctp->len =3D mc->get_buf(mctp, &payload, I2C_MCTP_MAXMTU, &fl= ags); + assert(mctp->len <=3D I2C_MCTP_MAXMTU); + + memcpy(pkt->mctp.payload, payload, mctp->len); + } + + if (!mctp->len) { + trace_i2c_mctp_tx_done(); + + /* no more packets needed; release the bus */ + i2c_bus_release(i2c); + + mctp->state =3D I2C_MCTP_STATE_IDLE; + mctp->tx.is_control =3D false; + + break; + } + + mctp->state =3D I2C_MCTP_STATE_TX; + + pkt->i2c =3D (MCTPI2CPacketHeader) { + .dest =3D mctp->tx.addr << 1, + .command_code =3D MCTP_I2C_COMMAND_CODE, + .byte_count =3D MCTP_I2C_BYTE_COUNT_OFFSET + mctp->len, + + /* DSP0237 1.2.0, Figure 1 */ + .source =3D slave->address << 1 | 0x1, + }; + + pkt->mctp.hdr =3D (MCTPPacketHeader) { + .version =3D 0x1, + .eid.dest =3D mctp->tx.eid, + .eid.source =3D mctp->my_eid, + .flags =3D flags, + }; + + pkt->mctp.hdr.flags =3D FIELD_DP8(pkt->mctp.hdr.flags, MCTP_H_FLAG= S, + PKTSEQ, mctp->tx.pktseq++); + pkt->mctp.hdr.flags =3D FIELD_DP8(pkt->mctp.hdr.flags, MCTP_H_FLAG= S, TAG, + mctp->tx.tag); + + mctp->len +=3D sizeof(MCTPI2CPacket); + assert(mctp->len < I2C_MCTP_MAX_LENGTH); + + mctp->buffer[mctp->len] =3D i2c_smbus_pec(0, mctp->buffer, mctp->l= en); + mctp->len++; + + trace_i2c_mctp_tx_start_send(mctp->len); + + i2c_start_send_async(i2c, pkt->i2c.dest >> 1); + + /* already "sent" the destination slave address */ + mctp->pos =3D 1; + + mctp->tx.state =3D I2C_MCTP_STATE_TX_SEND_BYTE; + + break; + } +} + +static void i2c_mctp_set_control_data(MCTPI2CEndpoint *mctp, const void * = buf, + size_t len) +{ + assert(i2c_mctp_control_data_offset < I2C_MCTP_MAX_LENGTH - len); + memcpy(i2c_mctp_control_data(mctp->buffer), buf, len); + + assert(mctp->len < I2C_MCTP_MAX_LENGTH - len); + mctp->len +=3D len; +} + +static void i2c_mctp_handle_control_set_eid(MCTPI2CEndpoint *mctp, uint8_t= eid) +{ + mctp->my_eid =3D eid; + + uint8_t buf[] =3D { + 0x0, 0x0, eid, 0x0, + }; + + i2c_mctp_set_control_data(mctp, buf, sizeof(buf)); +} + +static void i2c_mctp_handle_control_get_eid(MCTPI2CEndpoint *mctp) +{ + uint8_t buf[] =3D { + 0x0, mctp->my_eid, 0x0, 0x0, + }; + + i2c_mctp_set_control_data(mctp, buf, sizeof(buf)); +} + +static void i2c_mctp_handle_control_get_version(MCTPI2CEndpoint *mctp) +{ + uint8_t buf[] =3D { + 0x0, 0x1, 0x0, 0x1, 0x3, 0x1, + }; + + i2c_mctp_set_control_data(mctp, buf, sizeof(buf)); +} + +static void i2c_mctp_handle_get_message_type_support(MCTPI2CEndpoint *mctp) +{ + MCTPI2CEndpointClass *mc =3D MCTP_I2C_ENDPOINT_GET_CLASS(mctp); + const uint8_t *types; + size_t len; + + len =3D mc->get_types(mctp, &types); + assert(mctp->len <=3D MCTP_BASELINE_MTU - len); + + i2c_mctp_set_control_data(mctp, types, len); +} + +static void i2c_mctp_handle_control(MCTPI2CEndpoint *mctp) +{ + MCTPControlMessage *msg =3D (MCTPControlMessage *)i2c_mctp_payload(mct= p->buffer); + + /* clear Rq/D */ + msg->flags &=3D ~(MCTP_CONTROL_FLAGS_RQ | MCTP_CONTROL_FLAGS_D); + + mctp->len =3D sizeof(MCTPControlMessage); + + trace_i2c_mctp_handle_control(msg->command_code); + + switch (msg->command_code) { + case MCTP_CONTROL_SET_EID: + i2c_mctp_handle_control_set_eid(mctp, msg->data[1]); + break; + + case MCTP_CONTROL_GET_EID: + i2c_mctp_handle_control_get_eid(mctp); + break; + + case MCTP_CONTROL_GET_VERSION: + i2c_mctp_handle_control_get_version(mctp); + break; + + case MCTP_CONTROL_GET_MESSAGE_TYPE_SUPPORT: + i2c_mctp_handle_get_message_type_support(mctp); + break; + + default: + trace_i2c_mctp_unhandled_control(msg->command_code); + + msg->data[0] =3D MCTP_CONTROL_ERROR_UNSUPPORTED_CMD; + mctp->len++; + + break; + } + + assert(mctp->len <=3D MCTP_BASELINE_MTU); + + i2c_mctp_schedule_send(mctp); +} + +static int i2c_mctp_event_cb(I2CSlave *i2c, enum i2c_event event) +{ + MCTPI2CEndpoint *mctp =3D MCTP_I2C_ENDPOINT(i2c); + MCTPI2CEndpointClass *mc =3D MCTP_I2C_ENDPOINT_GET_CLASS(mctp); + MCTPI2CPacket *pkt =3D (MCTPI2CPacket *)mctp->buffer; + size_t payload_len; + uint8_t pec, pktseq, msgtype; + int ret; + + switch (event) { + case I2C_START_SEND: + if (mctp->state =3D=3D I2C_MCTP_STATE_IDLE) { + mctp->state =3D I2C_MCTP_STATE_RX_STARTED; + } else if (mctp->state !=3D I2C_MCTP_STATE_RX) { + return -1; + } + + /* the i2c core eats the slave address, so put it back in */ + pkt->i2c.dest =3D i2c->address << 1; + mctp->len =3D 1; + + return 0; + + case I2C_FINISH: + if (mctp->len < sizeof(MCTPI2CPacket) + 1) { + trace_i2c_mctp_drop_short_packet(mctp->len); + goto drop; + } + + payload_len =3D mctp->len - (1 + offsetof(MCTPI2CPacket, mctp.payl= oad)); + + if (pkt->i2c.byte_count + 3 !=3D mctp->len - 1) { + trace_i2c_mctp_drop_invalid_length(pkt->i2c.byte_count + 3, + mctp->len - 1); + goto drop; + } + + pec =3D i2c_smbus_pec(0, mctp->buffer, mctp->len - 1); + if (mctp->buffer[mctp->len - 1] !=3D pec) { + trace_i2c_mctp_drop_invalid_pec(mctp->buffer[mctp->len - 1], p= ec); + goto drop; + } + + if (!(pkt->mctp.hdr.eid.dest =3D=3D mctp->my_eid || + pkt->mctp.hdr.eid.dest =3D=3D 0)) { + trace_i2c_mctp_drop_invalid_eid(pkt->mctp.hdr.eid.dest, + mctp->my_eid); + goto drop; + } + + pktseq =3D FIELD_EX8(pkt->mctp.hdr.flags, MCTP_H_FLAGS, PKTSEQ); + + if (FIELD_EX8(pkt->mctp.hdr.flags, MCTP_H_FLAGS, SOM)) { + mctp->tx.is_control =3D false; + + if (mctp->state =3D=3D I2C_MCTP_STATE_RX) { + mc->reset(mctp); + } + + mctp->state =3D I2C_MCTP_STATE_RX; + + mctp->tx.addr =3D pkt->i2c.source >> 1; + mctp->tx.eid =3D pkt->mctp.hdr.eid.source; + mctp->tx.tag =3D FIELD_EX8(pkt->mctp.hdr.flags, MCTP_H_FLAGS, = TAG); + mctp->tx.pktseq =3D pktseq; + + msgtype =3D FIELD_EX8(pkt->mctp.payload[0], MCTP_MESSAGE_H, TY= PE); + + if (msgtype =3D=3D MCTP_MESSAGE_TYPE_CONTROL) { + mctp->tx.is_control =3D true; + + i2c_mctp_handle_control(mctp); + + return 0; + } + } else if (mctp->state =3D=3D I2C_MCTP_STATE_RX_STARTED) { + trace_i2c_mctp_drop_expected_som(); + goto drop; + } else if (pktseq !=3D (++mctp->tx.pktseq & 0x3)) { + trace_i2c_mctp_drop_invalid_pktseq(pktseq, mctp->tx.pktseq & 0= x3); + goto drop; + } + + ret =3D mc->put_buf(mctp, i2c_mctp_payload(mctp->buffer), payload_= len); + if (ret < 0) { + goto drop; + } + + if (FIELD_EX8(pkt->mctp.hdr.flags, MCTP_H_FLAGS, EOM)) { + mc->handle(mctp); + mctp->state =3D I2C_MCTP_STATE_WAIT_TX; + } + + return 0; + + default: + return -1; + } + +drop: + mc->reset(mctp); + + mctp->state =3D I2C_MCTP_STATE_IDLE; + + return 0; +} + +static int i2c_mctp_send_cb(I2CSlave *i2c, uint8_t data) +{ + MCTPI2CEndpoint *mctp =3D MCTP_I2C_ENDPOINT(i2c); + + if (mctp->len < I2C_MCTP_MAX_LENGTH) { + mctp->buffer[mctp->len++] =3D data; + return 0; + } + + return -1; +} + +static void i2c_mctp_instance_init(Object *obj) +{ + MCTPI2CEndpoint *mctp =3D MCTP_I2C_ENDPOINT(obj); + + mctp->tx.bh =3D qemu_bh_new(i2c_mctp_tx, mctp); +} + +static const Property mctp_i2c_props[] =3D { + DEFINE_PROP_UINT8("eid", MCTPI2CEndpoint, my_eid, 0x9), +}; + +static void i2c_mctp_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(oc); + I2CSlaveClass *k =3D I2C_SLAVE_CLASS(oc); + + k->event =3D i2c_mctp_event_cb; + k->send =3D i2c_mctp_send_cb; + + device_class_set_props(dc, mctp_i2c_props); +} + +static const TypeInfo i2c_mctp_info =3D { + .name =3D TYPE_MCTP_I2C_ENDPOINT, + .parent =3D TYPE_I2C_SLAVE, + .abstract =3D true, + .instance_init =3D i2c_mctp_instance_init, + .instance_size =3D sizeof(MCTPI2CEndpoint), + .class_init =3D i2c_mctp_class_init, + .class_size =3D sizeof(MCTPI2CEndpointClass), +}; + +static void register_types(void) +{ + type_register_static(&i2c_mctp_info); +} + +type_init(register_types) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index f543d944c3..d12f575c8d 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -530,6 +530,7 @@ config ASPEED_SOC select DS1338 select FTGMAC100 select I2C + select I2C_MCTP select DPS310 select PCA9552 select SERIAL_MM diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig index 596a7a3165..794cccc053 100644 --- a/hw/i2c/Kconfig +++ b/hw/i2c/Kconfig @@ -6,6 +6,10 @@ config I2C_DEVICES # to any board's i2c bus bool =20 +config I2C_MCTP + bool + select I2C + config SMBUS bool select I2C diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build index c459adcb59..a86457e610 100644 --- a/hw/i2c/meson.build +++ b/hw/i2c/meson.build @@ -1,5 +1,6 @@ i2c_ss =3D ss.source_set() i2c_ss.add(when: 'CONFIG_I2C', if_true: files('core.c')) +i2c_ss.add(when: 'CONFIG_I2C_MCTP', if_true: files('mctp.c')) i2c_ss.add(when: 'CONFIG_SMBUS', if_true: files('smbus_slave.c', 'smbus_ma= ster.c')) i2c_ss.add(when: 'CONFIG_ACPI_SMBUS', if_true: files('pm_smbus.c')) i2c_ss.add(when: 'CONFIG_ACPI_ICH9', if_true: files('smbus_ich9.c')) diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events index 1ad0e95c0e..da78fa327f 100644 --- a/hw/i2c/trace-events +++ b/hw/i2c/trace-events @@ -61,3 +61,17 @@ pca954x_read_data(uint8_t value) "PCA954X read data: 0x%= 02x" =20 imx_i2c_read(const char *id, const char *reg, uint64_t ofs, uint64_t value= ) "%s:[%s (0x%" PRIx64 ")] -> 0x%02" PRIx64 imx_i2c_write(const char *id, const char *reg, uint64_t ofs, uint64_t valu= e) "%s:[%s (0x%" PRIx64 ")] <- 0x%02" PRIx64 + +# mctp.c + +i2c_mctp_tx_start_send(size_t len) "len %zu" +i2c_mctp_tx_send_byte(size_t pos, uint8_t byte) "pos %zu byte 0x%"PRIx8"" +i2c_mctp_tx_done(void) "packet sent" +i2c_mctp_handle_control(uint8_t command) "command 0x%"PRIx8"" +i2c_mctp_unhandled_control(uint8_t command) "command 0x%"PRIx8"" +i2c_mctp_drop_invalid_length(unsigned byte_count, size_t expected) "byte_c= ount %u expected %zu" +i2c_mctp_drop_invalid_pec(uint8_t pec, uint8_t expected) "pec 0x%"PRIx8" e= xpected 0x%"PRIx8"" +i2c_mctp_drop_invalid_eid(uint8_t eid, uint8_t expected) "eid 0x%"PRIx8" e= xpected 0x%"PRIx8"" +i2c_mctp_drop_invalid_pktseq(uint8_t pktseq, uint8_t expected) "pktseq 0x%= "PRIx8" expected 0x%"PRIx8"" +i2c_mctp_drop_short_packet(size_t len) "len %zu" +i2c_mctp_drop_expected_som(void) "" --=20 2.48.1 From nobody Sat Nov 15 16:09:07 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1749486952; cv=none; d=zohomail.com; s=zohoarc; b=FU8oam2r5XWDaTp9sOeIXJavLWZBmmkHmnOfD11fA6ZfHNY9DuofwHY4ZFZ89obowcB9nOEJqolbh5/bfp1p7HWro9qbJmjAax9MLutwPW9TZ+DSeN9/g8sUuzitIEj8TNQkTCp5v6eOFSXj5gJ3uTnSa211TLcErSHntaco0Tg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749486952; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Sender:Subject:Subject:To:To:Message-Id; bh=HsIIG/HOB+iop5xRWOkPGQ0anMv1sdnrONlSHZR1y8I=; b=MThOZTZ267/q2SpQupMPllMzYTn7E/GitzntKS55lR9tT2sfZLIvEicTO/zNGBoEVwQgvGXmGhLEQfJPkqXfbcxz7Dc96xTLAUM2wBln5tMC1o9MaMWiZoM6TBGq2Ga2HUBCKPWbmJlzTMdSi7VdXypvcAnj83Cyz6Ha3x2Pj2Y= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749486952800635.5595280066747; Mon, 9 Jun 2025 09:35:52 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uOfT9-00038f-24; Mon, 09 Jun 2025 12:35:34 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uOfSr-0002zy-Ff for qemu-devel@nongnu.org; Mon, 09 Jun 2025 12:35:15 -0400 Received: from [185.176.79.56] (helo=frasgout.his.huawei.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uOfSo-00024b-No for qemu-devel@nongnu.org; Mon, 09 Jun 2025 12:35:13 -0400 Received: from mail.maildlp.com (unknown [172.18.186.31]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4bGHZb4bMjz6M52w; Tue, 10 Jun 2025 00:34:47 +0800 (CST) Received: from frapeml500008.china.huawei.com (unknown [7.182.85.71]) by mail.maildlp.com (Postfix) with ESMTPS id 33AC01400D3; Tue, 10 Jun 2025 00:35:09 +0800 (CST) Received: from SecurePC-101-06.china.huawei.com (10.122.19.247) by frapeml500008.china.huawei.com (7.182.85.71) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2507.39; Mon, 9 Jun 2025 18:35:08 +0200 To: Klaus Jensen , , Fan Ni , Anisa Su , , , CC: , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Subject: [RFC PATCH qemu 3/5] hw/cxl/i2c_mctp_cxl: Initial device emulation Date: Mon, 9 Jun 2025 17:33:31 +0100 Message-ID: <20250609163334.922346-4-Jonathan.Cameron@huawei.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250609163334.922346-1-Jonathan.Cameron@huawei.com> References: <20250609163334.922346-1-Jonathan.Cameron@huawei.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Originating-IP: [10.122.19.247] X-ClientProxiedBy: lhrpeml100012.china.huawei.com (7.191.174.184) To frapeml500008.china.huawei.com (7.182.85.71) X-Host-Lookup-Failed: Reverse DNS lookup failed for 185.176.79.56 (deferred) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=185.176.79.56; envelope-from=jonathan.cameron@huawei.com; helo=frasgout.his.huawei.com X-Spam_score_int: -33 X-Spam_score: -3.4 X-Spam_bar: --- X-Spam_report: (-3.4 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-to: Jonathan Cameron From: Jonathan Cameron via Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1749486955026116600 Content-Type: text/plain; charset="utf-8" The CCI and Fabric Manager APIs are used to configure CXL switches and devices. DMTF has defined an MCTP binding specification to carry these messages. The end goal of this work is to hook this up to emulated CXL switches and devices to allow control of the configuration. Since this relies on i2c target mode, this can currently only be used with an SoC that includes the Aspeed I2C controller. Note, only get timestamp added for now. Signed-off-by: Jonathan Cameron --- include/hw/cxl/cxl_device.h | 8 + include/hw/pci-bridge/cxl_upstream_port.h | 1 + hw/cxl/cxl-mailbox-utils.c | 49 ++++ hw/cxl/i2c_mctp_cxl.c | 289 ++++++++++++++++++++++ hw/cxl/Kconfig | 4 + hw/cxl/meson.build | 4 + 6 files changed, 355 insertions(+) diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 6086d4c85b..8d87c7151e 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -360,6 +360,10 @@ int cxl_process_cci_message(CXLCCI *cci, uint8_t set, = uint8_t cmd, size_t len_in, uint8_t *pl_in, size_t *len_out, uint8_t *pl_out, bool *bg_started); + +void cxl_initialize_t3_mctpcci(CXLCCI *cci, DeviceState *d, DeviceState *i= ntf, + size_t payload_max); + void cxl_initialize_t3_fm_owned_ld_mctpcci(CXLCCI *cci, DeviceState *d, DeviceState *intf, size_t payload_max); @@ -367,6 +371,9 @@ void cxl_initialize_t3_fm_owned_ld_mctpcci(CXLCCI *cci,= DeviceState *d, void cxl_initialize_t3_ld_cci(CXLCCI *cci, DeviceState *d, DeviceState *intf, size_t payload_max); =20 +void cxl_initialize_usp_mctpcci(CXLCCI *cci, DeviceState *d, DeviceState *= intf, + size_t payload_max); + #define cxl_device_cap_init(dstate, reg, cap_id, ver) = \ do { = \ uint32_t *cap_hdrs =3D dstate->caps_reg_state32; = \ @@ -606,6 +613,7 @@ struct CXLType3Dev { CXLComponentState cxl_cstate; CXLDeviceState cxl_dstate; CXLCCI cci; /* Primary PCI mailbox CCI */ + CXLCCI oob_mctp_cci; /* Initialized only if targetted */ /* Always initialized as no way to know if a VDM might show up */ CXLCCI vdm_fm_owned_ld_mctp_cci; CXLCCI ld0_cci; diff --git a/include/hw/pci-bridge/cxl_upstream_port.h b/include/hw/pci-bri= dge/cxl_upstream_port.h index f208397ffe..7e529e0b5a 100644 --- a/include/hw/pci-bridge/cxl_upstream_port.h +++ b/include/hw/pci-bridge/cxl_upstream_port.h @@ -12,6 +12,7 @@ typedef struct CXLUpstreamPort { /*< public >*/ CXLComponentState cxl_cstate; CXLCCI swcci; + CXLCCI mctpcci; =20 PCIExpLinkSpeed speed; PCIExpLinkWidth width; diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 475547f212..4c9852642e 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -3690,6 +3690,29 @@ void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceSt= ate *d, size_t payload_max) cxl_init_cci(cci, payload_max); } =20 +static const struct cxl_cmd cxl_cmd_set_t3_mctp[256][256] =3D { + [INFOSTAT][IS_IDENTIFY] =3D { "IDENTIFY", cmd_infostat_identify, 0, 0 = }, + [INFOSTAT][GET_RESPONSE_MSG_LIMIT] =3D { "GET_RESPONSE_MSG_LIMIT", + cmd_get_response_msg_limit, 0, = 0 }, + [INFOSTAT][SET_RESPONSE_MSG_LIMIT] =3D { "SET_RESPONSE_MSG_LIMIT", + cmd_set_response_msg_limit, 1, = 0 }, + [TIMESTAMP][GET] =3D { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, + [LOGS][GET_SUPPORTED] =3D { "LOGS_GET_SUPPORTED", cmd_logs_get_support= ed, 0, + 0 }, + [LOGS][GET_LOG] =3D { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, + [TUNNEL][MANAGEMENT_COMMAND] =3D { "TUNNEL_MANAGEMENT_COMMAND", + cmd_tunnel_management_cmd, ~0, 0 }, +}; + +void cxl_initialize_t3_mctpcci(CXLCCI *cci, DeviceState *d, DeviceState *i= ntf, + size_t payload_max) +{ + cxl_copy_cci_commands(cci, cxl_cmd_set_t3_mctp); + cci->d =3D d; + cci->intf =3D intf; + cxl_init_cci(cci, payload_max); +} + static const struct cxl_cmd cxl_cmd_set_t3_ld[256][256] =3D { [INFOSTAT][IS_IDENTIFY] =3D { "IDENTIFY", cmd_infostat_identify, 0, 0 = }, [LOGS][GET_SUPPORTED] =3D { "LOGS_GET_SUPPORTED", cmd_logs_get_support= ed, 0, @@ -3729,3 +3752,29 @@ void cxl_initialize_t3_fm_owned_ld_mctpcci(CXLCCI *c= ci, DeviceState *d, cci->intf =3D intf; cxl_init_cci(cci, payload_max); } + +static const struct cxl_cmd cxl_cmd_set_usp_mctp[256][256] =3D { + [INFOSTAT][IS_IDENTIFY] =3D { "IDENTIFY", cmd_infostat_identify, 0, 0 = }, + [INFOSTAT][GET_RESPONSE_MSG_LIMIT] =3D { "GET_RESPONSE_MSG_LIMIT", + cmd_get_response_msg_limit, 0, = 0 }, + [INFOSTAT][SET_RESPONSE_MSG_LIMIT] =3D { "SET_RESPONSE_MSG_LIMIT", + cmd_set_response_msg_limit, 1, = 0 }, + [LOGS][GET_SUPPORTED] =3D { "LOGS_GET_SUPPORTED", cmd_logs_get_support= ed, + 0, 0 }, + [LOGS][GET_LOG] =3D { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, + [PHYSICAL_SWITCH][IDENTIFY_SWITCH_DEVICE] =3D { "IDENTIFY_SWITCH_DEVIC= E", + cmd_identify_switch_device, 0, 0 }, + [PHYSICAL_SWITCH][GET_PHYSICAL_PORT_STATE] =3D { "SWITCH_PHYSICAL_PORT= _STATS", + cmd_get_physical_port_state, ~0, 0 }, + [TUNNEL][MANAGEMENT_COMMAND] =3D { "TUNNEL_MANAGEMENT_COMMAND", + cmd_tunnel_management_cmd, ~0, 0 }, +}; + +void cxl_initialize_usp_mctpcci(CXLCCI *cci, DeviceState *d, DeviceState *= intf, + size_t payload_max) +{ + cxl_copy_cci_commands(cci, cxl_cmd_set_usp_mctp); + cci->d =3D d; + cci->intf =3D intf; + cxl_init_cci(cci, payload_max); +} diff --git a/hw/cxl/i2c_mctp_cxl.c b/hw/cxl/i2c_mctp_cxl.c new file mode 100644 index 0000000000..1714f36e8e --- /dev/null +++ b/hw/cxl/i2c_mctp_cxl.c @@ -0,0 +1,289 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Emulation of a CXL Switch Fabric Management interface over MCTP over I2= C. + * + * Copyright (c) 2023 Huawei Technologies. + * + * Reference list: + * From www.dmtf.org + * DSP0236 Management Component Transport Protocol (MCTP) Base Specificati= on + * 1.3.0 + * DPS0234 CXL Fabric Manager API over MCTP Binding Specification 1.0.0 + * DSP0281 CXL Type 3 Device Component Command Interface over MCTP Binding + * Specification (note some commands apply to switches as well) + * From www.computeexpresslink.org + * Compute Express Link (CXL) Specification revision 3.0 Version 1.0 + */ + +#include "qemu/osdep.h" +#include "hw/i2c/i2c.h" +#include "hw/i2c/mctp.h" +#include "net/mctp.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "hw/cxl/cxl.h" +#include "hw/pci-bridge/cxl_upstream_port.h" +#include "hw/pci/pcie.h" +#include "hw/pci/pcie_port.h" +#include "hw/qdev-properties.h" +#include "hw/registerfields.h" + +#define TYPE_I2C_MCTP_CXL "i2c_mctp_cxl" + +/* DMTF DSP0234 CXL Fabric Manager API over MCTP Binding Specification */ +#define MCTP_MT_CXL_FMAPI 0x7 +/* + * DMTF DSP0281 CXL Type 3 Deivce Component Command Interface over MCTP + * Binding Specification + */ +#define MCTP_MT_CXL_TYPE3 0x8 + +/* FMAPI binding specification defined */ +#define MCTP_CXL_MAX_MSG_LEN 1088 + +/* Implementation choice - may make this configurable */ +#define MCTP_CXL_MAILBOX_BYTES 512 + +typedef struct CXLMCTPMessage { + /* + * DSP0236 (MCTP Base) Integrity Check + Message Type + * DSP0234/DSP0281 (CXL bindings) state no Integrity Check + * so just the message type. + */ + uint8_t message_type; + /* Remaing fields from CXL r3.0 Table 7-14 CCI Message Format */ + uint8_t category; + uint8_t tag; + uint8_t rsvd; + /* + * CXL r3.0 - Table 8-36 Generic Component Command Opcodes: + * Command opcode is split into two sub fields + */ + uint8_t command; + uint8_t command_set; + uint8_t pl_length[3]; + uint16_t rc; + uint16_t vendor_status; + uint8_t payload[]; +} QEMU_PACKED CXLMCTPMessage; + +enum cxl_dev_type { + cxl_type3, + cxl_switch, +}; + +struct I2C_MCTP_CXL_State { + MCTPI2CEndpoint mctp; + PCIDevice *target; + CXLCCI *cci; + enum cxl_dev_type type; + size_t len; + int64_t pos; + uint8_t buffer[MCTP_CXL_MAX_MSG_LEN]; + uint8_t scratch[MCTP_CXL_MAX_MSG_LEN]; +}; + +OBJECT_DECLARE_SIMPLE_TYPE(I2C_MCTP_CXL_State, I2C_MCTP_CXL) + +static const Property i2c_mctp_cxl_props[] =3D { + DEFINE_PROP_LINK("target", I2C_MCTP_CXL_State, + target, TYPE_PCI_DEVICE, PCIDevice *), +}; + +static size_t i2c_mctp_cxl_get_buf(MCTPI2CEndpoint *mctp, + const uint8_t **buf, + size_t maxlen, + uint8_t *mctp_flags) +{ + I2C_MCTP_CXL_State *s =3D I2C_MCTP_CXL(mctp); + size_t len; + + len =3D MIN(maxlen, s->len - s->pos); + + if (len =3D=3D 0) { + return 0; + } + + if (s->pos =3D=3D 0) { + *mctp_flags =3D FIELD_DP8(*mctp_flags, MCTP_H_FLAGS, SOM, 1); + } + + *buf =3D s->scratch + s->pos; + s->pos +=3D len; + + if (s->pos =3D=3D s->len) { + *mctp_flags =3D FIELD_DP8(*mctp_flags, MCTP_H_FLAGS, EOM, 1); + + s->pos =3D s->len =3D 0; + } + + return len; +} + +static int i2c_mctp_cxl_put_buf(MCTPI2CEndpoint *mctp, + uint8_t *buf, size_t len) +{ + I2C_MCTP_CXL_State *s =3D I2C_MCTP_CXL(mctp); + + if (s->len + len > MCTP_CXL_MAX_MSG_LEN) { + return -1; + } + + memcpy(s->buffer + s->len, buf, len); + s->len +=3D len; + + return 0; +} + +static size_t i2c_mctp_cxl_get_types(MCTPI2CEndpoint *mctp, + const uint8_t **data) +{ + static const uint8_t buf[] =3D { + 0x0, /* Success */ + 2, /* Message types in list - supported in addition to control */ + MCTP_MT_CXL_FMAPI, + MCTP_MT_CXL_TYPE3, + }; + *data =3D buf; + + return sizeof(buf); +} + +static void i2c_mctp_cxl_reset_message(MCTPI2CEndpoint *mctp) +{ + I2C_MCTP_CXL_State *s =3D I2C_MCTP_CXL(mctp); + + s->len =3D 0; +} + +static void i2c_mctp_cxl_handle_message(MCTPI2CEndpoint *mctp) +{ + I2C_MCTP_CXL_State *s =3D I2C_MCTP_CXL(mctp); + CXLMCTPMessage *msg =3D (CXLMCTPMessage *)s->buffer; + CXLMCTPMessage *buf =3D (CXLMCTPMessage *)s->scratch; + + *buf =3D (CXLMCTPMessage) { + .message_type =3D msg->message_type, + .category =3D 1, + .tag =3D msg->tag, + .command =3D msg->command, + .command_set =3D msg->command_set, + }; + s->pos =3D sizeof(*buf); + if (s->cci) { + bool bg_started; + size_t len_out =3D 0; + size_t len_in; + int rc; + + /* + * As it was not immediately obvious from the various specificatio= ns, + * clarification was sort for which binding applies for which comm= and + * set. The outcome was: + * + * Any command forming part of the CXL FM-API command set + * e.g. Present in CXL r3.0 Table 8-132: CXL FM API Command Opcodes + * (and equivalent in later CXL specifications) is valid only with + * the CXL Fabric Manager API over MCTP binding (DSP0234). + * + * Any other CXL command currently should be sent using the + * CXL Type 3 Device Component Command interface over MCTP binding, + * even if it is being sent to a switch. + * + * If tunneling is used, the component creating the PCIe VDMs must + * use the appropriate binding for sending the tunnel contents + * onwards. + */ + + if (!(msg->message_type =3D=3D MCTP_MT_CXL_TYPE3 && + msg->command_set < 0x51) && + !(msg->message_type =3D=3D MCTP_MT_CXL_FMAPI && + msg->command_set >=3D 0x51 && msg->command_set < 0x56)) { + buf->rc =3D CXL_MBOX_UNSUPPORTED; + st24_le_p(buf->pl_length, len_out); + s->len =3D s->pos; + s->pos =3D 0; + i2c_mctp_schedule_send(mctp); + return; + } + + len_in =3D msg->pl_length[2] << 16 | msg->pl_length[1] << 8 | + msg->pl_length[0]; + + rc =3D cxl_process_cci_message(s->cci, msg->command_set, msg->comm= and, + len_in, msg->payload, + &len_out, + s->scratch + sizeof(CXLMCTPMessage), + &bg_started); + buf->rc =3D rc; + s->pos +=3D len_out; + s->len =3D s->pos; + st24_le_p(buf->pl_length, len_out); + s->pos =3D 0; + i2c_mctp_schedule_send(mctp); + } else { + g_assert_not_reached(); /* The cci must be hooked up */ + } +} + +static void i2c_mctp_cxl_realize(DeviceState *d, Error **errp) +{ + I2C_MCTP_CXL_State *s =3D I2C_MCTP_CXL(d); + + /* Check this is a type we support */ + if (object_dynamic_cast(OBJECT(s->target), TYPE_CXL_USP)) { + CXLUpstreamPort *usp =3D CXL_USP(s->target); + + s->type =3D cxl_switch; + s->cci =3D &usp->mctpcci; + + cxl_initialize_usp_mctpcci(s->cci, DEVICE(s->target), d, + MCTP_CXL_MAILBOX_BYTES); + + return; + } + + if (object_dynamic_cast(OBJECT(s->target), TYPE_CXL_TYPE3)) { + CXLType3Dev *ct3d =3D CXL_TYPE3(s->target); + + s->type =3D cxl_type3; + s->cci =3D &ct3d->oob_mctp_cci; + + cxl_initialize_t3_fm_owned_ld_mctpcci(s->cci, DEVICE(s->target), d, + MCTP_CXL_MAILBOX_BYTES); + return; + } + + error_setg(errp, "Unhandled target type for CXL MCTP EP"); +} + +static void i2c_mctp_cxl_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + MCTPI2CEndpointClass *mc =3D MCTP_I2C_ENDPOINT_CLASS(klass); + + dc->realize =3D i2c_mctp_cxl_realize; + mc->get_types =3D i2c_mctp_cxl_get_types; + mc->get_buf =3D i2c_mctp_cxl_get_buf; + mc->put_buf =3D i2c_mctp_cxl_put_buf; + + mc->handle =3D i2c_mctp_cxl_handle_message; + mc->reset =3D i2c_mctp_cxl_reset_message; + device_class_set_props(dc, i2c_mctp_cxl_props); +} + +static const TypeInfo i2c_mctp_cxl_info =3D { + .name =3D TYPE_I2C_MCTP_CXL, + .parent =3D TYPE_MCTP_I2C_ENDPOINT, + .instance_size =3D sizeof(I2C_MCTP_CXL_State), + .class_init =3D i2c_mctp_cxl_class_init, +}; + +static void i2c_mctp_cxl_register_types(void) +{ + type_register_static(&i2c_mctp_cxl_info); +} + +type_init(i2c_mctp_cxl_register_types) diff --git a/hw/cxl/Kconfig b/hw/cxl/Kconfig index 8e67519b16..bc259fdf67 100644 --- a/hw/cxl/Kconfig +++ b/hw/cxl/Kconfig @@ -1,3 +1,7 @@ config CXL bool default y if PCI_EXPRESS + +config I2C_MCTP_CXL + bool + default y if CXL && I2C_MCTP diff --git a/hw/cxl/meson.build b/hw/cxl/meson.build index e3abb49d27..90fd83f680 100644 --- a/hw/cxl/meson.build +++ b/hw/cxl/meson.build @@ -12,3 +12,7 @@ system_ss.add(when: 'CONFIG_CXL', if_false: files( 'cxl-host-stubs.c', )) +system_ss.add(when: 'CONFIG_I2C_MCTP_CXL', if_true: files('i2c_mctp_cxl.c'= )) + +system_ss.add(when: 'CONFIG_ALL', if_true: files('cxl-host-stubs.c')) + --=20 2.48.1 From nobody Sat Nov 15 16:09:07 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1749486981; cv=none; d=zohomail.com; s=zohoarc; b=E3I44kPLnIylFiiNjAm5LQShT+BPZ7b51owYlR5iCFofqZ8OKyV1Gw0aDstb7gBAODh6t6ufpLoRuN3G3zp38pBHHFb8jbfCT2owgw+dalg3Bb1tth6fv1rCj2ZQBksWtg4cpvN+vhg6EowprqJk+oHxmJu8Be4TXOX1o6yhc74= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749486981; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Sender:Subject:Subject:To:To:Message-Id; bh=3o6d+Lt9Guw7HMCgxPWtvOYh4QTOTQ1TBu5ylb+7Nuc=; b=CLo/v9DdGVdeUz0+otOt2OAXYSx1YnETAKm9lPvtTotqQXiC03Hcflqa9ZmBGAJPDvRz6fxZkMY88CZd5T/jH3p9l/7TmHuFJhoJyfiwT6u509X03GVKDvKbzfHxBYQhywfsN2Vvp2DFRmP8klvmNFgAxrwLRgHw+KNIo0vipFw= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749486981816255.5179242813614; Mon, 9 Jun 2025 09:36:21 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uOfTj-0003ul-V8; Mon, 09 Jun 2025 12:36:12 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uOfTN-0003fr-Ix for qemu-devel@nongnu.org; Mon, 09 Jun 2025 12:35:46 -0400 Received: from [185.176.79.56] (helo=frasgout.his.huawei.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uOfTL-0002CB-UF for qemu-devel@nongnu.org; Mon, 09 Jun 2025 12:35:45 -0400 Received: from mail.maildlp.com (unknown [172.18.186.31]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4bGHVl688pz6L5LT; Tue, 10 Jun 2025 00:31:27 +0800 (CST) Received: from frapeml500008.china.huawei.com (unknown [7.182.85.71]) by mail.maildlp.com (Postfix) with ESMTPS id 5559F1400D3; Tue, 10 Jun 2025 00:35:40 +0800 (CST) Received: from SecurePC-101-06.china.huawei.com (10.122.19.247) by frapeml500008.china.huawei.com (7.182.85.71) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2507.39; Mon, 9 Jun 2025 18:35:39 +0200 To: Klaus Jensen , , Fan Ni , Anisa Su , , , CC: , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Subject: [RFC PATCH qemu 4/5] docs: cxl: Add example commandline for MCTP CXL CCIs Date: Mon, 9 Jun 2025 17:33:32 +0100 Message-ID: <20250609163334.922346-5-Jonathan.Cameron@huawei.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250609163334.922346-1-Jonathan.Cameron@huawei.com> References: <20250609163334.922346-1-Jonathan.Cameron@huawei.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Originating-IP: [10.122.19.247] X-ClientProxiedBy: lhrpeml100012.china.huawei.com (7.191.174.184) To frapeml500008.china.huawei.com (7.182.85.71) X-Host-Lookup-Failed: Reverse DNS lookup failed for 185.176.79.56 (deferred) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=185.176.79.56; envelope-from=jonathan.cameron@huawei.com; helo=frasgout.his.huawei.com X-Spam_score_int: -33 X-Spam_score: -3.4 X-Spam_bar: --- X-Spam_report: (-3.4 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-to: Jonathan Cameron From: Jonathan Cameron via Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1749486982935116600 Content-Type: text/plain; charset="utf-8" Add initial documentation for the MCTP over I2C management device. At current time this can only be used with the Aspeed I2C controller which is only available in aspeed SoCs, though can be added to other emulated boards. Signed-off-by: Jonathan Cameron --- docs/system/devices/cxl.rst | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/system/devices/cxl.rst b/docs/system/devices/cxl.rst index e307caf3f8..523f99f205 100644 --- a/docs/system/devices/cxl.rst +++ b/docs/system/devices/cxl.rst @@ -406,6 +406,33 @@ OS management of CXL memory devices as described here. * CONFIG_CXL_PORT * CONFIG_CXL_REGION =20 + +CCI access via MCTP over I2C +---------------------------- + +In order to make use of this device, an I2C controller that supports MCTP +is required. The aspeed-i2c controller is an example of such a controller. + +Both CXL switches and CXL Type 3 devices support configuration via +MCTP access to Component Command Interfaces (CCIs) on the devices. + +Example configuration: + + -device cxl-upstream,port=3D33,bus=3Droot_port0,id=3Dus0,multifunction=3D= on,addr=3D0.0,sn=3D12345678 \ + -device cxl-downstream,port=3D0,bus=3Dus0,id=3Dswport0,chassis=3D0,slot= =3D4 \ + -device cxl-downstream,port=3D1,bus=3Dus0,id=3Dswport1,chassis=3D0,slot= =3D5 \ + -device cxl-downstream,port=3D2,bus=3Dus0,id=3Dswport2,chassis=3D0,slot= =3D6 \ + -device cxl-type3,bus=3Dswport0,persistent-memdev=3Dcxl-mem1,id=3Dcxl-pme= m0,lsa=3Dcxl-lsa1,sn=3D3 \ + -device cxl-type3,bus=3Dswport1,persistent-memdev=3Dcxl-mem2,id=3Dcxl-pme= m1,lsa=3Dcxl-lsa2,sn=3D4 \ + -device cxl-type3,bus=3Dswport2,persistent-memdev=3Dcxl-mem3,id=3Dcxl-pme= m2,lsa=3Dcxl-lsa3,sn=3D5 \ + -device i2c_mctp_cxl,bus=3Daspeed.i2c.bus.0,address=3D4,target=3Dus0 \ + -device i2c_mctp_cxl,bus=3Daspeed.i2c.bus.0,address=3D5,target=3Dcxl-pmem= 0 \ + -device i2c_mctp_cxl,bus=3Daspeed.i2c.bus.0,address=3D6,target=3Dcxl-pmem= 1 \ + -device i2c_mctp_cxl,bus=3Daspeed.i2c.bus.0,address=3D7,target=3Dcxl-pmem2 + +Guest OS communication with the MCTP CCI can then be established using sta= ndard +MCTP configuration tools. + References ---------- =20 --=20 2.48.1 From nobody Sat Nov 15 16:09:07 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1749486996; cv=none; d=zohomail.com; s=zohoarc; b=eg0xLeyVCb84B4v0ZPNhu+HcUyrlQ+N8SKiNt1kV1IpIQAX8o4XXQLC0Ij47lmttCT2tdBTH8BkrOL4VXMjx1GYdbPZD1gEo/D8vhTYkK72siJ/bC3iYfM12LhMFSndyDXaFLV+z20RXqwYTjYZphyGb8Wi45OLb2ybBMoWRjKs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1749486996; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Sender:Subject:Subject:To:To:Message-Id; bh=alHHIm22m8vvxiPV1bhKL+WYcMgRmjN0p2MekVKZzoo=; b=VJKdcR+R12K76aZzdNpAtK86FuBt7QL/jiqaQThaDaZzOP4p5sx17iPfwfym7CC+uVFBZPLaAHC+dmBWAJLNPCVliA2UU4jPYuruNyw+ZiBhH+bn21N/VbuH+MLjCR9No+z0kuf+Js28rWGkPdo9I74Tm+sYFDnONwY6PHOG1MI= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1749486996435858.3771207516113; Mon, 9 Jun 2025 09:36:36 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uOfU5-0004KS-QC; Mon, 09 Jun 2025 12:36:30 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uOfTw-00046g-Ee for qemu-devel@nongnu.org; Mon, 09 Jun 2025 12:36:22 -0400 Received: from [185.176.79.56] (helo=frasgout.his.huawei.com) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uOfTq-0002GW-8W for qemu-devel@nongnu.org; Mon, 09 Jun 2025 12:36:19 -0400 Received: from mail.maildlp.com (unknown [172.18.186.231]) by frasgout.his.huawei.com (SkyGuard) with ESMTP id 4bGHZ75lPwz6L5LY; Tue, 10 Jun 2025 00:34:23 +0800 (CST) Received: from frapeml500008.china.huawei.com (unknown [7.182.85.71]) by mail.maildlp.com (Postfix) with ESMTPS id 28368140276; Tue, 10 Jun 2025 00:36:11 +0800 (CST) Received: from SecurePC-101-06.china.huawei.com (10.122.19.247) by frapeml500008.china.huawei.com (7.182.85.71) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2507.39; Mon, 9 Jun 2025 18:36:10 +0200 To: Klaus Jensen , , Fan Ni , Anisa Su , , , CC: , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Subject: [RFC PATCH qemu 5/5] usb/mctp/cxl: CXL FMAPI interface via MCTP over usb. Date: Mon, 9 Jun 2025 17:33:33 +0100 Message-ID: <20250609163334.922346-6-Jonathan.Cameron@huawei.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250609163334.922346-1-Jonathan.Cameron@huawei.com> References: <20250609163334.922346-1-Jonathan.Cameron@huawei.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Originating-IP: [10.122.19.247] X-ClientProxiedBy: lhrpeml100012.china.huawei.com (7.191.174.184) To frapeml500008.china.huawei.com (7.182.85.71) X-Host-Lookup-Failed: Reverse DNS lookup failed for 185.176.79.56 (deferred) Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=185.176.79.56; envelope-from=jonathan.cameron@huawei.com; helo=frasgout.his.huawei.com X-Spam_score_int: -33 X-Spam_score: -3.4 X-Spam_bar: --- X-Spam_report: (-3.4 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_MSPIKE_H5=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, RDNS_NONE=0.793, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-to: Jonathan Cameron From: Jonathan Cameron via Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1749486997371116600 Content-Type: text/plain; charset="utf-8" Given the challenges in testing systems with the previously posted i2c based mctp support for x86 / arm64 machines (as only emulated controller that is suitably capable is the aspeed one) providing a USB interface greatly simplifies things. This is a PoC of such an interface. RFC reasons: - Not all error conditions are fully checked / handled. - MTU in the 'to host' direction is ignored. Seems the Linux stack doesn't care about this today but that should be brought into compliance with the specification anyway. Signed-off-by: Jonathan Cameron --- include/hw/usb.h | 1 + include/net/mctp.h | 77 +++++- hw/i2c/mctp.c | 17 -- hw/usb/dev-mctp.c | 639 +++++++++++++++++++++++++++++++++++++++++++++ hw/usb/Kconfig | 5 + hw/usb/meson.build | 1 + 6 files changed, 717 insertions(+), 23 deletions(-) diff --git a/include/hw/usb.h b/include/hw/usb.h index 26a9f3ecde..ae5f3f77cb 100644 --- a/include/hw/usb.h +++ b/include/hw/usb.h @@ -81,6 +81,7 @@ #define USB_CLASS_CDC_DATA 0x0a #define USB_CLASS_CSCID 0x0b #define USB_CLASS_CONTENT_SEC 0x0d +#define USB_CLASS_MCTP 0x14 #define USB_CLASS_APP_SPEC 0xfe #define USB_CLASS_VENDOR_SPEC 0xff =20 diff --git a/include/net/mctp.h b/include/net/mctp.h index 5d26d855db..f278b17ce2 100644 --- a/include/net/mctp.h +++ b/include/net/mctp.h @@ -3,21 +3,21 @@ =20 #include "hw/registerfields.h" =20 -/* DSP0236 1.3.0, Section 8.3.1 */ +/* DSP0236 1.3.3, Section 8.4.2 Baseline transmission unit */ #define MCTP_BASELINE_MTU 64 =20 -/* DSP0236 1.3.0, Table 1, Message body */ +/* DSP0236 1.3.3, Table 1, Message body */ FIELD(MCTP_MESSAGE_H, TYPE, 0, 7) FIELD(MCTP_MESSAGE_H, IC, 7, 1) =20 -/* DSP0236 1.3.0, Table 1, MCTP transport header */ +/* DSP0236 1.3.3, Table 1, MCTP transport header */ FIELD(MCTP_H_FLAGS, TAG, 0, 3); FIELD(MCTP_H_FLAGS, TO, 3, 1); FIELD(MCTP_H_FLAGS, PKTSEQ, 4, 2); FIELD(MCTP_H_FLAGS, EOM, 6, 1); FIELD(MCTP_H_FLAGS, SOM, 7, 1); =20 -/* DSP0236 1.3.0, Figure 4 */ +/* DSP0236 1.3.3, Figure 4 Generic message fields */ typedef struct MCTPPacketHeader { uint8_t version; struct { @@ -25,11 +25,76 @@ typedef struct MCTPPacketHeader { uint8_t source; } eid; uint8_t flags; -} MCTPPacketHeader; +} QEMU_PACKED MCTPPacketHeader; =20 typedef struct MCTPPacket { MCTPPacketHeader hdr; uint8_t payload[]; -} MCTPPacket; +} QEMU_PACKED MCTPPacket; + +/* DSP0236 1.3.3, Figure 20 MCTP control message format */ +typedef struct MCTPControlMessage { +#define MCTP_MESSAGE_TYPE_CONTROL 0x0 + uint8_t type; +#define MCTP_CONTROL_FLAGS_RQ (1 << 7) +#define MCTP_CONTROL_FLAGS_D (1 << 6) + uint8_t flags; + uint8_t command_code; + uint8_t data[]; +} QEMU_PACKED MCTPControlMessage; + +enum MCTPControlCommandCodes { + MCTP_CONTROL_SET_EID =3D 0x01, + MCTP_CONTROL_GET_EID =3D 0x02, + MCTP_CONTROL_GET_UUID =3D 0x03, + MCTP_CONTROL_GET_VERSION =3D 0x04, + MCTP_CONTROL_GET_MESSAGE_TYPE_SUPPORT =3D 0x05, +}; + +/* DSP0236 1.3.3, Table 13 MCTP control message completion codes */ +#define MCTP_CONTROL_CC_SUCCESS 0x0 +#define MCTP_CONTROL_CC_ERROR 0x1 +#define MCTP_CONTROL_CC_ERROR_INVALID_DATA 0x2 +#define MCTP_CONTROL_CC_ERROR_INVALID_LENGTH 0x3 +#define MCTP_CONTROL_CC_ERROR_NOT_READY 0x4 +#define MCTP_CONTROL_CC_ERROR_UNSUP_COMMAND 0x5 + +typedef struct MCTPControlErrRsp { + uint8_t completion_code; +} MCTPControlErrRsp; + +/* DSP0236 1.3.3 Table 14 - Set Endpoint ID message */ +typedef struct MCTPControlSetEIDReq { + uint8_t operation; + uint8_t eid; +} MCTPControlSetEIDReq; + +typedef struct MCTPControlSetEIDRsp { + uint8_t completion_code; + uint8_t operation_result; /* Not named in spec */ + uint8_t eid_setting; + uint8_t eid_pool_size; +} MCTPControlSetEIDRsp; + +/* DSP0236 1.3.3 Table 15 - Get Endpoint ID message */ +typedef struct MCTPControlGetEIDRsp { + uint8_t completion_code; + uint8_t endpoint_id; + uint8_t endpoint_type; + uint8_t medium_specific_info; +} MCTPControlGetEIDRsp; + +/* DSP0236 1.3.3 Table 16 - Get Endpoint UUID message format */ +typedef struct MCTPControlGetUUIDRsp { + uint8_t completion_code; + uint8_t uuid[0x10]; +} MCTPControlGetUUIDRsp; + +/* DSP0236 1.3.3 Table 19 - Get Message Type Support message */ +typedef struct MCTPControlGetMessageTypeRsp { + uint8_t completion_code; + uint8_t message_type_count; + uint8_t types[]; +} MCTPControlGetMessageTypeRsp; =20 #endif /* QEMU_MCTP_H */ diff --git a/hw/i2c/mctp.c b/hw/i2c/mctp.c index cf45a46706..ac9bebb3c4 100644 --- a/hw/i2c/mctp.c +++ b/hw/i2c/mctp.c @@ -33,23 +33,6 @@ typedef struct MCTPI2CPacket { #define i2c_mctp_payload_offset offsetof(MCTPI2CPacket, mctp.payload) #define i2c_mctp_payload(buf) (buf + i2c_mctp_payload_offset) =20 -/* DSP0236 1.3.0, Figure 20 */ -typedef struct MCTPControlMessage { -#define MCTP_MESSAGE_TYPE_CONTROL 0x0 - uint8_t type; -#define MCTP_CONTROL_FLAGS_RQ (1 << 7) -#define MCTP_CONTROL_FLAGS_D (1 << 6) - uint8_t flags; - uint8_t command_code; - uint8_t data[]; -} MCTPControlMessage; - -enum MCTPControlCommandCodes { - MCTP_CONTROL_SET_EID =3D 0x01, - MCTP_CONTROL_GET_EID =3D 0x02, - MCTP_CONTROL_GET_VERSION =3D 0x04, - MCTP_CONTROL_GET_MESSAGE_TYPE_SUPPORT =3D 0x05, -}; =20 #define MCTP_CONTROL_ERROR_UNSUPPORTED_CMD 0x5 =20 diff --git a/hw/usb/dev-mctp.c b/hw/usb/dev-mctp.c new file mode 100644 index 0000000000..aafb9e7e96 --- /dev/null +++ b/hw/usb/dev-mctp.c @@ -0,0 +1,639 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Copyright Huawei 2025 + * + * Author: Jonathan Cameron + * + * CXL MCTP device + * + * TODO: + * - Respect MTU on packets being sent to host. For now it work in Linux b= ut + * who knows longer term. + * - More complete sanity checking of command flags etc. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" + +#include "hw/pci/pci_device.h" +#include "hw/qdev-properties.h" +#include "hw/cxl/cxl.h" +#include "hw/pci-bridge/cxl_upstream_port.h" +#include "hw/pci/pcie.h" +#include "hw/pci/pcie_port.h" +#include "hw/usb.h" +#include "hw/usb/desc.h" +#include "net/mctp.h" + +/* TODO: Move to header */ + +/* DMTF DSP0234 CXL Fabric Manager API over MCTP Binding Specification */ +#define MCTP_MT_CXL_FMAPI 0x7 +/* + * DMTF DSP0281 CXL Type 3 Device Component Command Interface over MCTP + * Binding Specification + */ +#define MCTP_MT_CXL_TYPE3 0x8 +typedef struct CXLMCTPMessage { + /* + * DSP0236 (MCTP Base) Integrity Check + Message Type + * DSP0234/DSP0281 (CXL bindings) state no Integrity Check + * so just the message type. + */ + uint8_t message_type; + /* Remaining fields from CXL r3.0 Table 7-14 CCI Message Format */ + uint8_t category; + uint8_t tag; + uint8_t rsvd; + /* + * CXL r3.0 - Table 8-36 Generic Component Command Opcodes: + * Command opcode is split into two sub fields + */ + uint8_t command; + uint8_t command_set; + /* Only bits 4:0 of pl_length[2] are part of the length */ + uint8_t pl_length[3]; + uint16_t rc; + uint16_t vendor_status; + uint8_t payload[]; +} QEMU_PACKED CXLMCTPMessage; + +/* DSP0283 1.0.0 Figure 5 */ +typedef struct MCTPUSBPacket { + uint16_t dmtf_id; + uint8_t resv; + uint8_t length; + /* Do we see the header? */ + MCTPPacket mctp; +} MCTPUSBPacket; + +#define MCTP_CXL_MAILBOX_BYTES 128 + +enum cxl_dev_type { + cxl_type3, + cxl_switch, +}; + +typedef struct USBCXLMCTPState { + USBDevice dev; + PCIDevice *target; + CXLCCI *cci; + enum cxl_dev_type type; + USBPacket *cached_tohost; + USBPacket *cached_fromhost; + uint8_t my_eid; + bool building_input; +/* TODO, match size to what we report as acceptable */ +#define MCTPUSBCXL_MAX_SIZE 1024 * 1024 + MCTPUSBPacket *pack0; /* Current incoming packet */ + MCTPUSBPacket *pack; /* Aggregate packet - what we'd get with large MT= U */ +} USBCLXMCTState; + +#define TYPE_USB_CXL_MCTP "usb-cxl-mctp" +OBJECT_DECLARE_SIMPLE_TYPE(USBCXLMCTPState, USB_CXL_MCTP) + +enum { + STR_MANUFACTURER =3D 1, + STR_PRODUCT, + STR_SERIALNUMBER, + STR_MCTP, + STR_CONFIG_FULL, + STR_CONFIG_HIGH, + STR_CONFIG_SUPER, +}; + +static const USBDescStrings desc_strings =3D { + [STR_MANUFACTURER] =3D "QEMU", + [STR_PRODUCT] =3D "QEMU CXL MCTP", + [STR_SERIALNUMBER] =3D "34618", + [STR_MCTP] =3D "MCTP", + [STR_CONFIG_FULL] =3D "Full speed config (usb 1.1)", + [STR_CONFIG_HIGH] =3D "High speed config (usb 2.0)", + [STR_CONFIG_SUPER] =3D "Super speed config (usb 3.0)", +}; + +static const USBDescIface desc_iface_full =3D { + .bInterfaceNumber =3D 0, + .bNumEndpoints =3D 2, + .bInterfaceClass =3D USB_CLASS_MCTP, + .bInterfaceSubClass =3D 0x0, + .bInterfaceProtocol =3D 0x1, + .iInterface =3D STR_MCTP, + .eps =3D (USBDescEndpoint[]) { + { + /* DSP0283 6.1.4.2.1 Out Bulk endpoint descriptor */ + .bEndpointAddress =3D USB_DIR_OUT | 0x1, + .bmAttributes =3D USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize =3D 512, + .bInterval =3D 0x1, + }, { + /* DSP0283 6.1.4.2.2 In Bulk endpoint descriptor */ + .bEndpointAddress =3D USB_DIR_IN | 0x1, + .bmAttributes =3D USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize =3D 512, + .bInterval =3D 0x1, + }, + }, +}; + +static const USBDescDevice desc_device_full =3D { + .bcdUSB =3D 0x200, + .bMaxPacketSize0 =3D 8, + .bNumConfigurations =3D 1, + .confs =3D (USBDescConfig[]) { + { + .bNumInterfaces =3D 1, + .bConfigurationValue =3D 1, + .iConfiguration =3D STR_CONFIG_FULL, + .bmAttributes =3D USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, + .bMaxPower =3D 2, + .nif =3D 1, + .ifs =3D &desc_iface_full, + }, + }, +}; + +static const USBDescMSOS desc_msos =3D { + .CompatibleID =3D "MCTP", + .SelectiveSuspendEnabled =3D true, +}; + +static const USBDesc desc =3D { + .id =3D { + .idVendor =3D 0x46f4, /* CRC16() of "QEMU" */ + .idProduct =3D 0x0006, + .bcdDevice =3D 0, + .iManufacturer =3D STR_MANUFACTURER, + .iSerialNumber =3D STR_SERIALNUMBER, + }, + .full =3D &desc_device_full, + .high =3D &desc_device_full, + .str =3D desc_strings, + .msos =3D &desc_msos, +}; + +static void usb_cxl_mctp_handle_reset(USBDevice *dev) +{ + USBCXLMCTPState *s =3D USB_CXL_MCTP(dev); + + s->cached_tohost =3D NULL; + s->cached_fromhost =3D NULL; + s->building_input =3D 0; + s->my_eid =3D 0; +} + +static void usb_cxl_mctp_handle_control(USBDevice *dev, USBPacket *p, + int request, int value, int index, + int length, uint8_t *data) +{ + usb_desc_handle_control(dev, p, request, value, index, length, data); +} + +/* A lot of fields are the same for all control responses */ +static void usb_mctp_fill_common(MCTPUSBPacket *o_usb_pkt, + MCTPUSBPacket *i_usb_pkt, + uint8_t usb_pkt_len) +{ + MCTPPacket *o_mctp_pkt =3D &o_usb_pkt->mctp; + uint8_t tag, flags; + + o_usb_pkt->dmtf_id =3D cpu_to_be16(0x1AB4); + o_usb_pkt->length =3D usb_pkt_len; + + o_mctp_pkt->hdr.version =3D 1; + o_mctp_pkt->hdr.eid.dest =3D i_usb_pkt->mctp.hdr.eid.source; + o_mctp_pkt->hdr.eid.source =3D i_usb_pkt->mctp.hdr.eid.dest; + + tag =3D FIELD_EX8(i_usb_pkt->mctp.hdr.flags, MCTP_H_FLAGS, TAG); + + flags =3D FIELD_DP8(0, MCTP_H_FLAGS, PKTSEQ, 0); + flags =3D FIELD_DP8(flags, MCTP_H_FLAGS, TAG, tag); + flags =3D FIELD_DP8(flags, MCTP_H_FLAGS, SOM, 1); + flags =3D FIELD_DP8(flags, MCTP_H_FLAGS, EOM, 1); + o_mctp_pkt->hdr.flags =3D flags; +} + +static MCTPUSBPacket *usb_mctp_handle_control(USBCXLMCTPState *s, + MCTPControlMessage *ctrlmsg, + USBPacket *p) +{ + switch (ctrlmsg->command_code) { + case MCTP_CONTROL_SET_EID: { + MCTPControlSetEIDReq *req =3D (MCTPControlSetEIDReq *)(ctrlmsg->da= ta); + uint8_t usb_pkt_len =3D sizeof(MCTPUSBPacket) + + sizeof(MCTPControlMessage) + + sizeof(MCTPControlSetEIDRsp); + MCTPUSBPacket *o_usb_pkt =3D g_malloc0(usb_pkt_len); + MCTPPacket *o_mctp_pkt =3D &o_usb_pkt->mctp; + MCTPControlMessage *o_mctp_ctrl =3D + (MCTPControlMessage *)(o_mctp_pkt->payload); + MCTPControlSetEIDRsp *rsp =3D (MCTPControlSetEIDRsp *)o_mctp_ctrl-= >data; + + /* Todo - check flags in request */ + s->my_eid =3D req->eid; + + o_mctp_ctrl->type =3D 0; + o_mctp_ctrl->command_code =3D ctrlmsg->command_code; + o_mctp_ctrl->flags =3D ctrlmsg->flags & + ~(MCTP_CONTROL_FLAGS_RQ | MCTP_CONTROL_FLAGS_D); + *rsp =3D (MCTPControlSetEIDRsp) { + .completion_code =3D MCTP_CONTROL_CC_SUCCESS, + .operation_result =3D 0, + .eid_setting =3D s->my_eid, + .eid_pool_size =3D 0, + }; + + usb_mctp_fill_common(o_usb_pkt, s->pack, usb_pkt_len); + + return o_usb_pkt; + } + case MCTP_CONTROL_GET_EID: { + uint8_t usb_pkt_len =3D sizeof(MCTPUSBPacket) + + sizeof(MCTPControlMessage) + + sizeof(MCTPControlGetEIDRsp); + MCTPUSBPacket *o_usb_pkt =3D g_malloc0(usb_pkt_len); + MCTPPacket *o_mctp_pkt =3D &o_usb_pkt->mctp; + MCTPControlMessage *o_mctp_ctrl =3D + (MCTPControlMessage *)(o_mctp_pkt->payload); + MCTPControlGetEIDRsp *rsp =3D (MCTPControlGetEIDRsp *)o_mctp_ctrl-= >data; + + o_mctp_ctrl->type =3D 0; + o_mctp_ctrl->command_code =3D ctrlmsg->command_code; + o_mctp_ctrl->flags =3D ctrlmsg->flags & + ~(MCTP_CONTROL_FLAGS_RQ | MCTP_CONTROL_FLAGS_D); + *rsp =3D (MCTPControlGetEIDRsp) { + .completion_code =3D MCTP_CONTROL_CC_SUCCESS, + .endpoint_id =3D s->my_eid, + .endpoint_type =3D 0, + .medium_specific_info =3D 0, + }; + usb_mctp_fill_common(o_usb_pkt, s->pack, usb_pkt_len); + + return o_usb_pkt; + } + case MCTP_CONTROL_GET_UUID: { + uint8_t usb_pkt_len =3D sizeof(MCTPUSBPacket) + + sizeof(MCTPControlMessage) + + sizeof(MCTPControlGetUUIDRsp); + MCTPUSBPacket *o_usb_pkt =3D g_malloc0(usb_pkt_len); + MCTPPacket *o_mctp_pkt =3D &o_usb_pkt->mctp; + MCTPControlMessage *o_mctp_ctrl =3D + (MCTPControlMessage *)(o_mctp_pkt->payload); + MCTPControlGetUUIDRsp *rsp =3D (MCTPControlGetUUIDRsp *)o_mctp_ctr= l->data; + + o_mctp_ctrl->type =3D 0; + o_mctp_ctrl->command_code =3D ctrlmsg->command_code; + o_mctp_ctrl->flags =3D ctrlmsg->flags & + ~(MCTP_CONTROL_FLAGS_RQ | MCTP_CONTROL_FLAGS_D); + *rsp =3D (MCTPControlGetUUIDRsp) { + .completion_code =3D MCTP_CONTROL_CC_SUCCESS, + /* TODO: Fill in uuid */ + }; + usb_mctp_fill_common(o_usb_pkt, s->pack, usb_pkt_len); + + return o_usb_pkt; + } + case MCTP_CONTROL_GET_MESSAGE_TYPE_SUPPORT: { + const uint8_t types[] =3D { MCTP_MT_CXL_FMAPI, MCTP_MT_CXL_TYPE3, = }; + uint8_t usb_pkt_len =3D sizeof(MCTPUSBPacket) + + sizeof(MCTPControlMessage) + + sizeof(MCTPControlGetMessageTypeRsp) + + sizeof(types); + MCTPUSBPacket *o_usb_pkt =3D g_malloc0(usb_pkt_len); + MCTPPacket *o_mctp_pkt =3D &o_usb_pkt->mctp; + MCTPControlMessage *o_mctp_ctrl =3D + (MCTPControlMessage *)(o_mctp_pkt->payload); + MCTPControlGetMessageTypeRsp *rsp =3D + (MCTPControlGetMessageTypeRsp *)o_mctp_ctrl->data; + + o_mctp_ctrl->type =3D 0; + o_mctp_ctrl->command_code =3D ctrlmsg->command_code; + o_mctp_ctrl->flags =3D ctrlmsg->flags & + ~(MCTP_CONTROL_FLAGS_RQ | MCTP_CONTROL_FLAGS_D); + *rsp =3D (MCTPControlGetMessageTypeRsp) { + .completion_code =3D MCTP_CONTROL_CC_SUCCESS, + .message_type_count =3D sizeof(types), + }; + memcpy(rsp->types, types, sizeof(types)); + + usb_mctp_fill_common(o_usb_pkt, s->pack, usb_pkt_len); + + return o_usb_pkt; + } + default: { + uint8_t usb_pkt_len =3D sizeof(MCTPUSBPacket) + + sizeof(MCTPControlMessage) + + sizeof(MCTPControlErrRsp); + MCTPUSBPacket *o_usb_pkt =3D g_malloc0(usb_pkt_len); + MCTPPacket *o_mctp_pkt =3D &o_usb_pkt->mctp; + MCTPControlMessage *o_mctp_ctrl =3D + (MCTPControlMessage *)(o_mctp_pkt->payload); + MCTPControlErrRsp *rsp =3D (MCTPControlErrRsp *)o_mctp_ctrl->data; + + o_mctp_ctrl->type =3D 0; + o_mctp_ctrl->command_code =3D ctrlmsg->command_code; + o_mctp_ctrl->flags =3D ctrlmsg->flags & + ~(MCTP_CONTROL_FLAGS_RQ | MCTP_CONTROL_FLAGS_D); + *rsp =3D (MCTPControlErrRsp) { + .completion_code =3D MCTP_CONTROL_CC_ERROR_UNSUP_COMMAND, + }; + usb_mctp_fill_common(o_usb_pkt, s->pack, usb_pkt_len); + return o_usb_pkt; + } + } +} + +static void usb_cxl_mctp_handle_data(USBDevice *dev, USBPacket *p) +{ + USBCXLMCTPState *s =3D USB_CXL_MCTP(dev); + USBPacket *tohost, *fromhost; + uint8_t message_type; + bool som, eom; + + /* + * In and out on EP 0x1: anything else is bug. + * Not sure what right answer is. + */ + if (p->ep->nr !=3D 1) { + p->status =3D USB_RET_STALL; + return; + } + + /* + * Conservative approach - don't proceed until we have at least one pa= cket + * in each direction. For fragmented messages cases we only need this = to be + * true for the EOM packet (potential optimization). + */ + if (p->pid =3D=3D USB_TOKEN_IN) { /* Direction from point of view of h= ost */ + tohost =3D p; + if (s->cached_fromhost =3D=3D NULL) { + s->cached_tohost =3D tohost; + tohost->status =3D USB_RET_ASYNC; + return; + } + fromhost =3D s->cached_fromhost; + } else { + fromhost =3D p; + if (s->cached_tohost =3D=3D NULL) { + s->cached_fromhost =3D fromhost; + fromhost->status =3D USB_RET_ASYNC; + return; + } + tohost =3D s->cached_tohost; + } + + /* DSP0236 1.3.3 section 8.7 Dropped packets, physical layer errors */ + if (fromhost->iov.size < sizeof(s->pack->mctp)) { + goto err_drop; + } + + usb_packet_copy(fromhost, s->pack0, fromhost->iov.size); + /* DSP0236 1.3.3 section 8.7 Dropped packets, physical layer errors */ + if ((be16_to_cpu(s->pack0->dmtf_id) !=3D 0x1AB4) || + (fromhost->iov.size !=3D s->pack0->length)) { + goto err_drop; + } + + eom =3D FIELD_EX8(s->pack0->mctp.hdr.flags, MCTP_H_FLAGS, EOM); + som =3D FIELD_EX8(s->pack0->mctp.hdr.flags, MCTP_H_FLAGS, SOM); + + /* DSP0236 1.3.3 section 8.7 Dropped packets, unexpected middle or end= */ + if (!som && !s->building_input) { + goto err_drop; + } + + if (som) { + /* Note repeated SOM without EOM is not an error */ + s->building_input =3D true; + /* Put first part of full message in place */ + memcpy(s->pack, s->pack0, s->pack0->length); + } else { + size_t additional_len; + + additional_len =3D s->pack0->length - 8; + memcpy((void *)(s->pack) + s->pack->length, s->pack0->mctp.payload, + additional_len); + s->pack->length +=3D additional_len; + } + + if (eom) { + s->building_input =3D false; + } else { /* More to come so complete message from host to let it flow = */ + if (p->pid =3D=3D USB_TOKEN_IN) { + /* Hold the tohost packet */ + p->status =3D USB_RET_ASYNC; + s->cached_tohost =3D p; + /* Release the fromhost packet */ + fromhost->status =3D USB_RET_SUCCESS; + s->cached_fromhost =3D NULL; + usb_packet_complete(dev, fromhost); + } else { + /* From host - handled synchronously */ + p->status =3D USB_RET_SUCCESS; + } + return; + } + + /* DSP0236 1.3.3 section 8.7 Dropped packets, bad header version */ + if (s->pack->mctp.hdr.version !=3D 1) { + goto err_drop; + } + + /* DSP0236 1.3.3 section 8.7 Dropped packets, unknown eid */ + if ((s->pack->mctp.hdr.eid.dest !=3D s->my_eid) && + (s->pack->mctp.hdr.eid.dest !=3D 0)) { + goto err_drop; + } + + message_type =3D s->pack->mctp.payload[0]; + switch (message_type) { + case MCTP_MESSAGE_TYPE_CONTROL: { + MCTPUSBPacket *o_usb_pkt; + MCTPControlMessage *ctrlmsg =3D + (MCTPControlMessage *)(s->pack->mctp.payload); + + /* DSP0236 1.3.3 section 8.7 Dropped packets, physical layer error= s */ + if (fromhost->iov.size < sizeof(s->pack->mctp) + sizeof(*ctrlmsg))= { + goto err_drop; + } + + o_usb_pkt =3D usb_mctp_handle_control(s, ctrlmsg, tohost); + + usb_packet_copy(tohost, o_usb_pkt, o_usb_pkt->length); + g_free(o_usb_pkt); + + break; + } + case MCTP_MT_CXL_TYPE3: + case MCTP_MT_CXL_FMAPI: { + size_t usb_pkt_len =3D MCTPUSBCXL_MAX_SIZE; + g_autofree MCTPUSBPacket *o_usb_pkt =3D g_malloc0(usb_pkt_len); + MCTPPacket *o_mctp_pkt =3D &o_usb_pkt->mctp; + CXLMCTPMessage *rsp =3D (CXLMCTPMessage *)(o_mctp_pkt->payload); + CXLMCTPMessage *req =3D (CXLMCTPMessage *)(s->pack->mctp.payload); + bool bg_started; + size_t len_out =3D 0; + size_t len_in; + int rc; + + *rsp =3D (CXLMCTPMessage) { + .message_type =3D req->message_type, + .category =3D 1, + .tag =3D req->tag, + .command =3D req->command, + .command_set =3D req->command_set, + }; + + /* + * As it was not immediately obvious from the various specificatio= ns, + * clarification was sort for which binding applies for which comm= and + * set. The outcome was: + * + * Any command forming part of the CXL FM-API command set + * e.g. Present in CXL r3.0 Table 8-132: CXL FM API Command Opcodes + * (and equivalent in later CXL specifications) is valid only with + * the CXL Fabric Manager API over MCTP binding (DSP0234). + * + * Any other CXL command currently should be sent using the + * CXL Type 3 Device Component Command interface over MCTP binding, + * even if it is being sent to a switch. + * + * If tunneling is used, the component creating the PCIe VDMs must + * use the appropriate binding for sending the tunnel contents + * onwards. + */ + if (!(req->message_type =3D=3D MCTP_MT_CXL_TYPE3 && + req->command_set < 0x51) && + !(req->message_type =3D=3D MCTP_MT_CXL_FMAPI && + req->command_set >=3D 0x51 && req->command_set < 0x56)) { + len_out =3D 0; + usb_pkt_len =3D sizeof(MCTPUSBPacket) + sizeof(CXLMCTPMessage)= + + len_out; + rsp->rc =3D CXL_MBOX_UNSUPPORTED; + st24_le_p(rsp->pl_length, len_out); + + usb_mctp_fill_common(o_usb_pkt, s->pack, usb_pkt_len); + usb_packet_copy(tohost, o_usb_pkt, usb_pkt_len); + break; + } + /* TODO: add ld24_le_p */ + len_in =3D req->pl_length[2] << 16 | req->pl_length[1] << 8 | + req->pl_length[0]; + rc =3D cxl_process_cci_message(s->cci, req->command_set, + req->command, + len_in, req->payload, + &len_out, + (uint8_t *)(rsp + 1), + &bg_started); + rsp->rc =3D rc; + st24_le_p(rsp->pl_length, len_out); + usb_pkt_len =3D sizeof(MCTPUSBPacket) + sizeof(CXLMCTPMessage) + + len_out; + + usb_mctp_fill_common(o_usb_pkt, s->pack, usb_pkt_len); + usb_packet_copy(tohost, o_usb_pkt, usb_pkt_len); + break; + } + default: + /* + * 8.9 Dropped messages - message type unsuported. + * Dropping after assembly + */ + goto err_drop; + } + /* Something to send */ + tohost->status =3D USB_RET_SUCCESS; + fromhost->status =3D USB_RET_SUCCESS; + if (p->pid =3D=3D USB_TOKEN_IN) { + s->cached_fromhost =3D NULL; + usb_packet_complete(dev, fromhost); + } else { + s->cached_tohost =3D NULL; + usb_packet_complete(dev, tohost); + } + return; + +err_drop: + /* Reply with 'nothing' as dropping packet */ + fromhost->status =3D USB_RET_SUCCESS; + if (p->pid =3D=3D USB_TOKEN_IN) { + /* Hold the tohost packet */ + tohost->status =3D USB_RET_ASYNC; + s->cached_tohost =3D p; + s->cached_fromhost =3D NULL; + usb_packet_complete(dev, fromhost); + } +} + +static void usb_cxl_mctp_realize(USBDevice *dev, Error **errp) +{ + USBCXLMCTPState *s =3D USB_CXL_MCTP(dev); + + s->pack =3D g_malloc0(MCTPUSBCXL_MAX_SIZE); + s->pack0 =3D g_malloc0(MCTPUSBCXL_MAX_SIZE); + usb_desc_create_serial(dev); + usb_desc_init(dev); + + /* Check this is a type we support */ + if (object_dynamic_cast(OBJECT(s->target), TYPE_CXL_USP)) { + CXLUpstreamPort *usp =3D CXL_USP(s->target); + + s->type =3D cxl_switch; + s->cci =3D &usp->mctpcci; + + cxl_initialize_usp_mctpcci(s->cci, DEVICE(s->target), DEVICE(dev), + MCTP_CXL_MAILBOX_BYTES); + + return; + } + + if (object_dynamic_cast(OBJECT(s->target), TYPE_CXL_TYPE3)) { + CXLType3Dev *ct3d =3D CXL_TYPE3(s->target); + + s->type =3D cxl_type3; + s->cci =3D &ct3d->oob_mctp_cci; + + cxl_initialize_t3_fm_owned_ld_mctpcci(s->cci, DEVICE(s->target), + DEVICE(dev), + MCTP_CXL_MAILBOX_BYTES); + return; + } + + error_setg(errp, "Unhandled target type for CXL MCTP EP"); +} + +static const Property usb_cxl_mctp_properties[] =3D { + DEFINE_PROP_LINK("target", USBCXLMCTPState, target, TYPE_PCI_DEVICE, + PCIDevice *), +}; + +static void usb_cxl_mctp_class_initfn(ObjectClass *klass, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + USBDeviceClass *uc =3D USB_DEVICE_CLASS(klass); + + uc->realize =3D usb_cxl_mctp_realize; + uc->product_desc =3D "QEMU USB CXL MCTP"; + uc->usb_desc =3D &desc; + uc->handle_attach =3D usb_desc_attach; + uc->handle_reset =3D usb_cxl_mctp_handle_reset; + uc->handle_control =3D usb_cxl_mctp_handle_control; + uc->handle_data =3D usb_cxl_mctp_handle_data; + dc->desc =3D "USB CXL MCTP device"; + dc->fw_name =3D "mctp"; + device_class_set_props(dc, usb_cxl_mctp_properties); +} + +static const TypeInfo usb_cxl_mctp_info =3D { + .name =3D TYPE_USB_CXL_MCTP, + .parent =3D TYPE_USB_DEVICE, + .instance_size =3D sizeof(USBCXLMCTPState), + .class_init =3D usb_cxl_mctp_class_initfn, +}; + +static void usb_cxl_mctp_register_types(void) +{ + type_register_static(&usb_cxl_mctp_info); +} +type_init(usb_cxl_mctp_register_types) diff --git a/hw/usb/Kconfig b/hw/usb/Kconfig index 69c663be52..4340475708 100644 --- a/hw/usb/Kconfig +++ b/hw/usb/Kconfig @@ -105,6 +105,11 @@ config USB_SERIAL default y depends on USB =20 +config USB_MCTP + bool + default y if CXL + depends on USB + config USB_NETWORK bool default y diff --git a/hw/usb/meson.build b/hw/usb/meson.build index 17360a5b5a..c2910f9818 100644 --- a/hw/usb/meson.build +++ b/hw/usb/meson.build @@ -43,6 +43,7 @@ system_ss.add(when: 'CONFIG_USB_STORAGE_UAS', if_true: fi= les('dev-uas.c')) system_ss.add(when: 'CONFIG_USB_AUDIO', if_true: files('dev-audio.c')) system_ss.add(when: 'CONFIG_USB_SERIAL', if_true: files('dev-serial.c')) system_ss.add(when: 'CONFIG_USB_NETWORK', if_true: files('dev-network.c')) +system_ss.add(when: 'CONFIG_USB_MCTP', if_true: files('dev-mctp.c')) if host_os !=3D 'windows' system_ss.add(when: 'CONFIG_USB_STORAGE_MTP', if_true: files('dev-mtp.c'= )) endif --=20 2.48.1