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 <[email protected]> --- 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 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 @@ #include "hw/registerfields.h" -/* DSP0236 1.3.0, Section 8.3.1 */ +/* DSP0236 1.3.3, Section 8.4.2 Baseline transmission unit */ #define MCTP_BASELINE_MTU 64 -/* 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) -/* 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); -/* 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; 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 = 0x01, + MCTP_CONTROL_GET_EID = 0x02, + MCTP_CONTROL_GET_UUID = 0x03, + MCTP_CONTROL_GET_VERSION = 0x04, + MCTP_CONTROL_GET_MESSAGE_TYPE_SUPPORT = 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; #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) -/* 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 = 0x01, - MCTP_CONTROL_GET_EID = 0x02, - MCTP_CONTROL_GET_VERSION = 0x04, - MCTP_CONTROL_GET_MESSAGE_TYPE_SUPPORT = 0x05, -}; #define MCTP_CONTROL_ERROR_UNSUPPORTED_CMD 0x5 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 <[email protected]> + * + * CXL MCTP device + * + * TODO: + * - Respect MTU on packets being sent to host. For now it work in Linux but + * 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 MTU */ +} USBCLXMCTState; + +#define TYPE_USB_CXL_MCTP "usb-cxl-mctp" +OBJECT_DECLARE_SIMPLE_TYPE(USBCXLMCTPState, USB_CXL_MCTP) + +enum { + STR_MANUFACTURER = 1, + STR_PRODUCT, + STR_SERIALNUMBER, + STR_MCTP, + STR_CONFIG_FULL, + STR_CONFIG_HIGH, + STR_CONFIG_SUPER, +}; + +static const USBDescStrings desc_strings = { + [STR_MANUFACTURER] = "QEMU", + [STR_PRODUCT] = "QEMU CXL MCTP", + [STR_SERIALNUMBER] = "34618", + [STR_MCTP] = "MCTP", + [STR_CONFIG_FULL] = "Full speed config (usb 1.1)", + [STR_CONFIG_HIGH] = "High speed config (usb 2.0)", + [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)", +}; + +static const USBDescIface desc_iface_full = { + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_MCTP, + .bInterfaceSubClass = 0x0, + .bInterfaceProtocol = 0x1, + .iInterface = STR_MCTP, + .eps = (USBDescEndpoint[]) { + { + /* DSP0283 6.1.4.2.1 Out Bulk endpoint descriptor */ + .bEndpointAddress = USB_DIR_OUT | 0x1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 512, + .bInterval = 0x1, + }, { + /* DSP0283 6.1.4.2.2 In Bulk endpoint descriptor */ + .bEndpointAddress = USB_DIR_IN | 0x1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 512, + .bInterval = 0x1, + }, + }, +}; + +static const USBDescDevice desc_device_full = { + .bcdUSB = 0x200, + .bMaxPacketSize0 = 8, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG_FULL, + .bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_WAKEUP, + .bMaxPower = 2, + .nif = 1, + .ifs = &desc_iface_full, + }, + }, +}; + +static const USBDescMSOS desc_msos = { + .CompatibleID = "MCTP", + .SelectiveSuspendEnabled = true, +}; + +static const USBDesc desc = { + .id = { + .idVendor = 0x46f4, /* CRC16() of "QEMU" */ + .idProduct = 0x0006, + .bcdDevice = 0, + .iManufacturer = STR_MANUFACTURER, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device_full, + .high = &desc_device_full, + .str = desc_strings, + .msos = &desc_msos, +}; + +static void usb_cxl_mctp_handle_reset(USBDevice *dev) +{ + USBCXLMCTPState *s = USB_CXL_MCTP(dev); + + s->cached_tohost = NULL; + s->cached_fromhost = NULL; + s->building_input = 0; + s->my_eid = 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 = &o_usb_pkt->mctp; + uint8_t tag, flags; + + o_usb_pkt->dmtf_id = cpu_to_be16(0x1AB4); + o_usb_pkt->length = usb_pkt_len; + + o_mctp_pkt->hdr.version = 1; + o_mctp_pkt->hdr.eid.dest = i_usb_pkt->mctp.hdr.eid.source; + o_mctp_pkt->hdr.eid.source = i_usb_pkt->mctp.hdr.eid.dest; + + tag = FIELD_EX8(i_usb_pkt->mctp.hdr.flags, MCTP_H_FLAGS, TAG); + + flags = FIELD_DP8(0, MCTP_H_FLAGS, PKTSEQ, 0); + flags = FIELD_DP8(flags, MCTP_H_FLAGS, TAG, tag); + flags = FIELD_DP8(flags, MCTP_H_FLAGS, SOM, 1); + flags = FIELD_DP8(flags, MCTP_H_FLAGS, EOM, 1); + o_mctp_pkt->hdr.flags = flags; +} + +static MCTPUSBPacket *usb_mctp_handle_control(USBCXLMCTPState *s, + MCTPControlMessage *ctrlmsg, + USBPacket *p) +{ + switch (ctrlmsg->command_code) { + case MCTP_CONTROL_SET_EID: { + MCTPControlSetEIDReq *req = (MCTPControlSetEIDReq *)(ctrlmsg->data); + uint8_t usb_pkt_len = sizeof(MCTPUSBPacket) + + sizeof(MCTPControlMessage) + + sizeof(MCTPControlSetEIDRsp); + MCTPUSBPacket *o_usb_pkt = g_malloc0(usb_pkt_len); + MCTPPacket *o_mctp_pkt = &o_usb_pkt->mctp; + MCTPControlMessage *o_mctp_ctrl = + (MCTPControlMessage *)(o_mctp_pkt->payload); + MCTPControlSetEIDRsp *rsp = (MCTPControlSetEIDRsp *)o_mctp_ctrl->data; + + /* Todo - check flags in request */ + s->my_eid = req->eid; + + o_mctp_ctrl->type = 0; + o_mctp_ctrl->command_code = ctrlmsg->command_code; + o_mctp_ctrl->flags = ctrlmsg->flags & + ~(MCTP_CONTROL_FLAGS_RQ | MCTP_CONTROL_FLAGS_D); + *rsp = (MCTPControlSetEIDRsp) { + .completion_code = MCTP_CONTROL_CC_SUCCESS, + .operation_result = 0, + .eid_setting = s->my_eid, + .eid_pool_size = 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 = sizeof(MCTPUSBPacket) + + sizeof(MCTPControlMessage) + + sizeof(MCTPControlGetEIDRsp); + MCTPUSBPacket *o_usb_pkt = g_malloc0(usb_pkt_len); + MCTPPacket *o_mctp_pkt = &o_usb_pkt->mctp; + MCTPControlMessage *o_mctp_ctrl = + (MCTPControlMessage *)(o_mctp_pkt->payload); + MCTPControlGetEIDRsp *rsp = (MCTPControlGetEIDRsp *)o_mctp_ctrl->data; + + o_mctp_ctrl->type = 0; + o_mctp_ctrl->command_code = ctrlmsg->command_code; + o_mctp_ctrl->flags = ctrlmsg->flags & + ~(MCTP_CONTROL_FLAGS_RQ | MCTP_CONTROL_FLAGS_D); + *rsp = (MCTPControlGetEIDRsp) { + .completion_code = MCTP_CONTROL_CC_SUCCESS, + .endpoint_id = s->my_eid, + .endpoint_type = 0, + .medium_specific_info = 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 = sizeof(MCTPUSBPacket) + + sizeof(MCTPControlMessage) + + sizeof(MCTPControlGetUUIDRsp); + MCTPUSBPacket *o_usb_pkt = g_malloc0(usb_pkt_len); + MCTPPacket *o_mctp_pkt = &o_usb_pkt->mctp; + MCTPControlMessage *o_mctp_ctrl = + (MCTPControlMessage *)(o_mctp_pkt->payload); + MCTPControlGetUUIDRsp *rsp = (MCTPControlGetUUIDRsp *)o_mctp_ctrl->data; + + o_mctp_ctrl->type = 0; + o_mctp_ctrl->command_code = ctrlmsg->command_code; + o_mctp_ctrl->flags = ctrlmsg->flags & + ~(MCTP_CONTROL_FLAGS_RQ | MCTP_CONTROL_FLAGS_D); + *rsp = (MCTPControlGetUUIDRsp) { + .completion_code = 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[] = { MCTP_MT_CXL_FMAPI, MCTP_MT_CXL_TYPE3, }; + uint8_t usb_pkt_len = sizeof(MCTPUSBPacket) + + sizeof(MCTPControlMessage) + + sizeof(MCTPControlGetMessageTypeRsp) + + sizeof(types); + MCTPUSBPacket *o_usb_pkt = g_malloc0(usb_pkt_len); + MCTPPacket *o_mctp_pkt = &o_usb_pkt->mctp; + MCTPControlMessage *o_mctp_ctrl = + (MCTPControlMessage *)(o_mctp_pkt->payload); + MCTPControlGetMessageTypeRsp *rsp = + (MCTPControlGetMessageTypeRsp *)o_mctp_ctrl->data; + + o_mctp_ctrl->type = 0; + o_mctp_ctrl->command_code = ctrlmsg->command_code; + o_mctp_ctrl->flags = ctrlmsg->flags & + ~(MCTP_CONTROL_FLAGS_RQ | MCTP_CONTROL_FLAGS_D); + *rsp = (MCTPControlGetMessageTypeRsp) { + .completion_code = MCTP_CONTROL_CC_SUCCESS, + .message_type_count = 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 = sizeof(MCTPUSBPacket) + + sizeof(MCTPControlMessage) + + sizeof(MCTPControlErrRsp); + MCTPUSBPacket *o_usb_pkt = g_malloc0(usb_pkt_len); + MCTPPacket *o_mctp_pkt = &o_usb_pkt->mctp; + MCTPControlMessage *o_mctp_ctrl = + (MCTPControlMessage *)(o_mctp_pkt->payload); + MCTPControlErrRsp *rsp = (MCTPControlErrRsp *)o_mctp_ctrl->data; + + o_mctp_ctrl->type = 0; + o_mctp_ctrl->command_code = ctrlmsg->command_code; + o_mctp_ctrl->flags = ctrlmsg->flags & + ~(MCTP_CONTROL_FLAGS_RQ | MCTP_CONTROL_FLAGS_D); + *rsp = (MCTPControlErrRsp) { + .completion_code = 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 = 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 != 1) { + p->status = USB_RET_STALL; + return; + } + + /* + * Conservative approach - don't proceed until we have at least one packet + * in each direction. For fragmented messages cases we only need this to be + * true for the EOM packet (potential optimization). + */ + if (p->pid == USB_TOKEN_IN) { /* Direction from point of view of host */ + tohost = p; + if (s->cached_fromhost == NULL) { + s->cached_tohost = tohost; + tohost->status = USB_RET_ASYNC; + return; + } + fromhost = s->cached_fromhost; + } else { + fromhost = p; + if (s->cached_tohost == NULL) { + s->cached_fromhost = fromhost; + fromhost->status = USB_RET_ASYNC; + return; + } + tohost = 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) != 0x1AB4) || + (fromhost->iov.size != s->pack0->length)) { + goto err_drop; + } + + eom = FIELD_EX8(s->pack0->mctp.hdr.flags, MCTP_H_FLAGS, EOM); + som = 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 = true; + /* Put first part of full message in place */ + memcpy(s->pack, s->pack0, s->pack0->length); + } else { + size_t additional_len; + + additional_len = s->pack0->length - 8; + memcpy((void *)(s->pack) + s->pack->length, s->pack0->mctp.payload, + additional_len); + s->pack->length += additional_len; + } + + if (eom) { + s->building_input = false; + } else { /* More to come so complete message from host to let it flow */ + if (p->pid == USB_TOKEN_IN) { + /* Hold the tohost packet */ + p->status = USB_RET_ASYNC; + s->cached_tohost = p; + /* Release the fromhost packet */ + fromhost->status = USB_RET_SUCCESS; + s->cached_fromhost = NULL; + usb_packet_complete(dev, fromhost); + } else { + /* From host - handled synchronously */ + p->status = USB_RET_SUCCESS; + } + return; + } + + /* DSP0236 1.3.3 section 8.7 Dropped packets, bad header version */ + if (s->pack->mctp.hdr.version != 1) { + goto err_drop; + } + + /* DSP0236 1.3.3 section 8.7 Dropped packets, unknown eid */ + if ((s->pack->mctp.hdr.eid.dest != s->my_eid) && + (s->pack->mctp.hdr.eid.dest != 0)) { + goto err_drop; + } + + message_type = s->pack->mctp.payload[0]; + switch (message_type) { + case MCTP_MESSAGE_TYPE_CONTROL: { + MCTPUSBPacket *o_usb_pkt; + MCTPControlMessage *ctrlmsg = + (MCTPControlMessage *)(s->pack->mctp.payload); + + /* DSP0236 1.3.3 section 8.7 Dropped packets, physical layer errors */ + if (fromhost->iov.size < sizeof(s->pack->mctp) + sizeof(*ctrlmsg)) { + goto err_drop; + } + + o_usb_pkt = 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 = MCTPUSBCXL_MAX_SIZE; + g_autofree MCTPUSBPacket *o_usb_pkt = g_malloc0(usb_pkt_len); + MCTPPacket *o_mctp_pkt = &o_usb_pkt->mctp; + CXLMCTPMessage *rsp = (CXLMCTPMessage *)(o_mctp_pkt->payload); + CXLMCTPMessage *req = (CXLMCTPMessage *)(s->pack->mctp.payload); + bool bg_started; + size_t len_out = 0; + size_t len_in; + int rc; + + *rsp = (CXLMCTPMessage) { + .message_type = req->message_type, + .category = 1, + .tag = req->tag, + .command = req->command, + .command_set = req->command_set, + }; + + /* + * As it was not immediately obvious from the various specifications, + * clarification was sort for which binding applies for which command + * 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 == MCTP_MT_CXL_TYPE3 && + req->command_set < 0x51) && + !(req->message_type == MCTP_MT_CXL_FMAPI && + req->command_set >= 0x51 && req->command_set < 0x56)) { + len_out = 0; + usb_pkt_len = sizeof(MCTPUSBPacket) + sizeof(CXLMCTPMessage) + + len_out; + rsp->rc = 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 = req->pl_length[2] << 16 | req->pl_length[1] << 8 | + req->pl_length[0]; + rc = 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 = rc; + st24_le_p(rsp->pl_length, len_out); + usb_pkt_len = 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 = USB_RET_SUCCESS; + fromhost->status = USB_RET_SUCCESS; + if (p->pid == USB_TOKEN_IN) { + s->cached_fromhost = NULL; + usb_packet_complete(dev, fromhost); + } else { + s->cached_tohost = NULL; + usb_packet_complete(dev, tohost); + } + return; + +err_drop: + /* Reply with 'nothing' as dropping packet */ + fromhost->status = USB_RET_SUCCESS; + if (p->pid == USB_TOKEN_IN) { + /* Hold the tohost packet */ + tohost->status = USB_RET_ASYNC; + s->cached_tohost = p; + s->cached_fromhost = NULL; + usb_packet_complete(dev, fromhost); + } +} + +static void usb_cxl_mctp_realize(USBDevice *dev, Error **errp) +{ + USBCXLMCTPState *s = USB_CXL_MCTP(dev); + + s->pack = g_malloc0(MCTPUSBCXL_MAX_SIZE); + s->pack0 = 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 = CXL_USP(s->target); + + s->type = cxl_switch; + s->cci = &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 = CXL_TYPE3(s->target); + + s->type = cxl_type3; + s->cci = &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[] = { + DEFINE_PROP_LINK("target", USBCXLMCTPState, target, TYPE_PCI_DEVICE, + PCIDevice *), +}; + +static void usb_cxl_mctp_class_initfn(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + USBDeviceClass *uc = USB_DEVICE_CLASS(klass); + + uc->realize = usb_cxl_mctp_realize; + uc->product_desc = "QEMU USB CXL MCTP"; + uc->usb_desc = &desc; + uc->handle_attach = usb_desc_attach; + uc->handle_reset = usb_cxl_mctp_handle_reset; + uc->handle_control = usb_cxl_mctp_handle_control; + uc->handle_data = usb_cxl_mctp_handle_data; + dc->desc = "USB CXL MCTP device"; + dc->fw_name = "mctp"; + device_class_set_props(dc, usb_cxl_mctp_properties); +} + +static const TypeInfo usb_cxl_mctp_info = { + .name = TYPE_USB_CXL_MCTP, + .parent = TYPE_USB_DEVICE, + .instance_size = sizeof(USBCXLMCTPState), + .class_init = 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 +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: files('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 != 'windows' system_ss.add(when: 'CONFIG_USB_STORAGE_MTP', if_true: files('dev-mtp.c')) endif -- 2.48.1
