[RFC PATCH 0/6] hw/{cxl, i386, arm}: PoC: Emulated MCTP over I2C for CXL Fabric / Device management

Jonathan Cameron via posted 6 patches 11 months ago
Failed in applying to current master (apply log)
cxl_fmapi.h |  72 +++++++++++
mctp.h      |  68 ++++++++++
test.c      | 361 ++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 501 insertions(+)
create mode 100644 cxl_fmapi.h
create mode 100644 mctp.h
create mode 100644 test.c
[RFC PATCH 0/6] hw/{cxl, i386, arm}: PoC: Emulated MCTP over I2C for CXL Fabric / Device management
Posted by Jonathan Cameron via 11 months ago
Not intended for merge in anything like current form!

CC list is a bit random and I'll admit I added a few people just to make them groan.

Depends on patches 1 and 2 from.
[PATCH v2 0/3] hw/{i2c, nvme}: mctp endpoint, nvme management interface model.

Based-on: Message-ID: 20230425063540.46143-1-its@irrelevant.dk
Arm parts also need the out of tree ARM64 cxl patches.
Generally working  with the kernel CXL stack requires event emulation.

A git tree with this near the top and all prereqs is available at:
http://gitlab.com/jic23/qemu  cxl-2023-05-25

Just over a year ago (yikes) I posted [RFC PATCH 0/2] CXL FMAPI interface over MCTP/I2C
https://lore.kernel.org/linux-cxl/20220520170128.4436-1-Jonathan.Cameron@huawei.com/

Since that time I've had a number of questions about how to build on that
PoC to enable control of actual CXL devices.

The fundamental problem at that stage was the PoC only worked with device tree
bindings and the rest of CXL software assumes ACPI (adding DT support for
CXL in general is a long term project).  So this series attempts to 'solve'
that.

Currently I'm only aware of one emulated I2C controller with the right
characteristics to be able to support MCTP over I2C - aspeed-i2c.
So lets add that to the main architectures we care about for CXL:
- ARM64 (on top of my out of tree support github.com/jic23/qemu)
- X86 - yup I just added an aspeed-i2c controller to pc :)

The actual emulated device has been rebased on Klaus Jensen's work
for the nvme-mi which massively reduces the amount of boilerplate
/ state machine handling that needs to be done in this emulation.
(great work btw Klaius!)

So how to use this:

For ACPI you need the kernel patches from:
https://lore.kernel.org/linux-cxl/20230525152203.32190-1-Jonathan.Cameron@huawei.com/T/#u
To make the aspeed i2c controller play nicely with PRP0001 based ACPI description.

Example qemu command line - stripped back to include only relevant bits

qemu-system-x86_64 -M q35,cxl=on -m 4g,maxmem=8G,slots=8 -cpu max -smp 4 \
...
 -object memory-backend-file,id=cxl-mem1,share=on,mem-path=/tmp/cxltest.raw,size=256M,align=256M \
 -object memory-backend-file,id=cxl-mem2,share=on,mem-path=/tmp/cxltest2.raw,size=256M,align=256M \
 -object memory-backend-file,id=cxl-mem3,share=on,mem-path=/tmp/cxltest3.raw,size=256M,align=256M \
 -object memory-backend-file,id=cxl-lsa1,share=on,mem-path=/tmp/lsa.raw,size=1M,align=1M \
 -object memory-backend-file,id=cxl-lsa2,share=on,mem-path=/tmp/lsa2.raw,size=1M,align=1M \
 -object memory-backend-file,id=cxl-lsa3,share=on,mem-path=/tmp/lsa3.raw,size=1M,align=1M \
 -object memory-backend-file,id=cxl-mem5,share=on,mem-path=/tmp/cxltest5.raw,size=256M,align=256M \
 -object memory-backend-file,id=cxl-mem6,share=on,mem-path=/tmp/cxltest6.raw,size=256M,align=256M \
 -object memory-backend-file,id=cxl-mem7,share=on,mem-path=/tmp/cxltest7.raw,size=256M,align=256M \
 -object memory-backend-file,id=cxl-lsa5,share=on,mem-path=/tmp/lsa5.raw,size=1M,align=1M \
 -object memory-backend-file,id=cxl-lsa6,share=on,mem-path=/tmp/lsa6.raw,size=1M,align=1M \
 -object memory-backend-file,id=cxl-lsa7,share=on,mem-path=/tmp/lsa7.raw,size=1M,align=1M \
 -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \
 -device cxl-rp,port=0,bus=cxl.1,id=root_port0,chassis=0,slot=2 \
 -device cxl-rp,port=1,bus=cxl.1,id=root_port2,chassis=0,slot=3 \
 -device cxl-upstream,port=33,bus=root_port0,id=us0,multifunction=on,addr=0.0,sn=12345678 \
 -device cxl-downstream,port=0,bus=us0,id=swport0,chassis=0,slot=4 \
 -device cxl-downstream,port=1,bus=us0,id=swport1,chassis=0,slot=5 \
 -device cxl-downstream,port=2,bus=us0,id=swport2,chassis=0,slot=6 \
 -device cxl-type3,bus=swport0,persistent-memdev=cxl-mem1,id=cxl-pmem0,lsa=cxl-lsa1,sn=3 \
 -device cxl-type3,bus=swport1,persistent-memdev=cxl-mem2,id=cxl-pmem1,lsa=cxl-lsa2,sn=4 \
 -device cxl-type3,bus=swport2,persistent-memdev=cxl-mem3,id=cxl-pmem2,lsa=cxl-lsa3,sn=5 \
 -machine cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=1k \
 -device i2c_mctp_cxl,bus=aspeed.i2c.bus.0,address=4,target=us0 \
 -device i2c_mctp_cxl,bus=aspeed.i2c.bus.0,address=5,target=cxl-pmem0 \
 -device i2c_mctp_cxl,bus=aspeed.i2c.bus.0,address=6,target=cxl-pmem1 \
 -device i2c_mctp_cxl,bus=aspeed.i2c.bus.0,address=7,target=cxl-pmem2 \
  

Install the MCTP daemon from:
  https://github.com/CodeConstruct/mctp

Bring up and enumerate the MCTP EPs:

# Bring up the link
mctp link set mctpi2c0 up
# Assign an address to the aspeed-i2c controller
mctp addr add 50 dev mctpi2c0
# Assign a neetwork ID to the link (11)
mctp link set mctpi2c0 net 11
# Start the daemon that uses dbus for configuration.
systemctl start mctpd.service
# Assign EIDs to the EPs
busctl call xyz.openbmc_project.MCTP /xyz/openbmc_project/mctp au.com.CodeConstruct.MCTP AssignEndpoint say mctpi2c0 1 0x4
busctl call xyz.openbmc_project.MCTP /xyz/openbmc_project/mctp au.com.CodeConstruct.MCTP AssignEndpoint say mctpi2c0 1 0x5
busctl call xyz.openbmc_project.MCTP /xyz/openbmc_project/mctp au.com.CodeConstruct.MCTP AssignEndpoint say mctpi2c0 1 0x6
busctl call xyz.openbmc_project.MCTP /xyz/openbmc_project/mctp au.com.CodeConstruct.MCTP AssignEndpoint say mctpi2c0 1 0x7

# Check it worked by dumping some state.
busctl introspect xyz.openbmc_project.MCTP /xyz/openbmc_project/mctp/11/8 xyz.openbmc_project.MCTP.Endpoint
busctl introspect xyz.openbmc_project.MCTP /xyz/openbmc_project/mctp/11/9 xyz.openbmc_project.MCTP.Endpoint
busctl introspect xyz.openbmc_project.MCTP /xyz/openbmc_project/mctp/11/10 xyz.openbmc_project.MCTP.Endpoint
busctl introspect xyz.openbmc_project.MCTP /xyz/openbmc_project/mctp/11/11 xyz.openbmc_project.MCTP.Endpoint

Use the following test program to poke them

./test 8 etc (address will be printed out during configuration above, but usually starts at 8)


From 14ac4ad4eb97bd0536c332f08c639f980c280b93 Mon Sep 17 00:00:00 2001
From: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Date: Thu, 25 May 2023 11:36:36 +0100
Subject: [PATCH] Trivial MCTP CXL CCI test prog

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 cxl_fmapi.h |  72 +++++++++++
 mctp.h      |  68 ++++++++++
 test.c      | 361 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 501 insertions(+)
 create mode 100644 cxl_fmapi.h
 create mode 100644 mctp.h
 create mode 100644 test.c

diff --git a/cxl_fmapi.h b/cxl_fmapi.h
new file mode 100644
index 0000000..6e861ed
--- /dev/null
+++ b/cxl_fmapi.h
@@ -0,0 +1,72 @@
+
+#include <stdint.h>
+
+#define CXL_CCI_CMD_SET_INFO 0x0
+#define  CXL_IDENTIFY 0x0
+#define CXL_FM_API_CMD_SET_PHYSICAL_SWITCH 0x51
+#define  CXL_IDENTIFY_SWITCH_DEVICE 0x00
+#define  CXL_GET_PHYSICAL_PORT_STATE 0x01
+
+struct cxl_fmapi_header {
+  #define CXL_MCTP_CATEGORY_REQ 0
+  #define CXL_MCTP_CATEGORY_RESP 1
+  uint8_t category;
+  uint8_t tag;
+  uint8_t rsv1;
+  uint8_t command;
+  uint8_t command_set;
+  uint8_t pl_length[3]; /* 20 bit little endian, BO bit at bit 23 */
+  uint16_t return_code;
+  uint16_t vendor_ext_status;
+} __attribute__ ((packed));
+
+struct cxl_cci_infostat_identify_resp_pl {
+    uint16_t vendor_id;
+    uint16_t device_id;
+    uint16_t subsys_vendor_id;
+    uint16_t subsys_id;
+    uint8_t serial_num[8];
+    uint8_t max_msg;
+    uint8_t component_type;
+} __attribute__((packed));
+
+struct cxl_fmapi_ident_switch_dev_resp_pl {
+  uint8_t ingres_port_id;
+  uint8_t rsv1;
+  uint8_t num_physical_ports;
+  uint8_t num_vcs;
+  uint8_t active_port_bitmask[32];
+  uint8_t active_vcs_bitmask[32];
+  uint16_t num_total_vppb;
+  uint16_t num_active_vppb;
+} __attribute__((packed));
+
+struct cxl_fmapi_get_phys_port_state_req_pl {
+  uint8_t num_ports; /* CHECK. may get too large for MCTP message size */
+  uint8_t ports[];
+} __attribute__((packed));
+
+struct cxl_fmapi_port_state_info_block {
+  uint8_t port_id;
+  uint8_t config_state;
+  uint8_t connected_device_cxl_version;
+  uint8_t rsv1;
+  uint8_t connected_device_type;
+  uint8_t port_cxl_version_bitmask;
+  uint8_t max_link_width;
+  uint8_t negotiated_link_width;
+  uint8_t supported_link_speeds_vector;
+  uint8_t max_link_speed;
+  uint8_t current_link_speed;
+  uint8_t ltssm_state;
+  uint8_t first_lane_num;
+  uint16_t link_state;
+  uint8_t supported_ld_count;
+} __attribute__((packed));
+
+struct cxl_fmapi_get_phys_port_state_resp_pl {
+  uint8_t num_ports;
+  uint8_t rsv1[3];
+  struct cxl_fmapi_port_state_info_block ports[];
+} __attribute__((packed));
+
diff --git a/mctp.h b/mctp.h
new file mode 100644
index 0000000..154ab56
--- /dev/null
+++ b/mctp.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Management Component Transport Protocol (MCTP)
+ *
+ * Copyright (c) 2021 Code Construct
+ * Copyright (c) 2021 Google
+ */
+
+#ifndef __UAPI_MCTP_H
+#define __UAPI_MCTP_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/netdevice.h>
+
+typedef __u8			mctp_eid_t;
+
+struct mctp_addr {
+	mctp_eid_t		s_addr;
+};
+
+struct sockaddr_mctp {
+	__kernel_sa_family_t	smctp_family;
+	__u16			__smctp_pad0;
+	unsigned int		smctp_network;
+	struct mctp_addr	smctp_addr;
+	__u8			smctp_type;
+	__u8			smctp_tag;
+	__u8			__smctp_pad1;
+};
+
+struct sockaddr_mctp_ext {
+	struct sockaddr_mctp	smctp_base;
+	int			smctp_ifindex;
+	__u8			smctp_halen;
+	__u8			__smctp_pad0[3];
+	__u8			smctp_haddr[MAX_ADDR_LEN];
+};
+
+#define MCTP_NET_ANY		0x0
+
+#define MCTP_ADDR_NULL		0x00
+#define MCTP_ADDR_ANY		0xff
+
+#define MCTP_TAG_MASK		0x07
+#define MCTP_TAG_OWNER		0x08
+#define MCTP_TAG_PREALLOC	0x10
+
+#define MCTP_OPT_ADDR_EXT	1
+
+#define SIOCMCTPALLOCTAG	(SIOCPROTOPRIVATE + 0)
+#define SIOCMCTPDROPTAG		(SIOCPROTOPRIVATE + 1)
+
+struct mctp_ioc_tag_ctl {
+	mctp_eid_t	peer_addr;
+
+	/* For SIOCMCTPALLOCTAG: must be passed as zero, kernel will
+	 * populate with the allocated tag value. Returned tag value will
+	 * always have TO and PREALLOC set.
+	 *
+	 * For SIOCMCTPDROPTAG: userspace provides tag value to drop, from
+	 * a prior SIOCMCTPALLOCTAG call (and so must have TO and PREALLOC set).
+	 */
+	__u8		tag;
+	__u16		flags;
+};
+
+#endif /* __UAPI_MCTP_H */
diff --git a/test.c b/test.c
new file mode 100644
index 0000000..20f1dbb
--- /dev/null
+++ b/test.c
@@ -0,0 +1,361 @@
+/*
+ * Trivial example program to exercise QEMU FMAPI Emulation over MCTP over I2C
+ */
+#include <sys/socket.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include "mctp.h"
+#include "cxl_fmapi.h"
+
+enum cxl_type {
+	cxl_switch,
+	cxl_type3,
+};
+
+static int query_cci_identify(int sd, struct sockaddr_mctp *addr, int *tag, enum cxl_type *type)
+{
+	struct cxl_cci_infostat_identify_resp_pl *pl;
+	uint8_t buf[1024];
+	struct cxl_fmapi_header *rsp_head = buf;
+
+	int rc;
+	ssize_t len;
+	struct sockaddr_mctp addrrx;
+	socklen_t addrlen = sizeof(addrrx);
+	struct cxl_fmapi_header req = {
+		.category = CXL_MCTP_CATEGORY_REQ,
+		.tag = *tag++,
+		.command = 1,
+		.command_set = 0,
+		.vendor_ext_status = 0xabcd,
+	};
+
+	printf("trying to identify\n");
+	len = sendto(sd, &req, sizeof(req), 0, (struct sockaddr *)addr, sizeof(*addr));
+	if (len != sizeof(req)) {
+		printf("Failed to send whole request for phys switch info %d %d\n", len, sizeof(req));
+		return -1;
+	}
+
+	len = recvfrom(sd, buf, sizeof(buf), 0, (struct sockaddr *)&addrrx, &addrlen);
+	if (len < 0) {
+		printf("Failed to receive response\n");
+		return -1;
+	}
+	printf("length %d\n", len);
+	pl = (void *)(rsp_head + 1);
+	printf("Vendor id  : %04x\n", pl->vendor_id);
+	printf("Device id  : %04x\n", pl->device_id);
+	printf("Subsys vid : %04x\n", pl->subsys_vendor_id);
+	printf("Subsys id  : %04x\n", pl->subsys_id);
+
+	switch (pl->component_type) {
+	case 0x00:
+		printf("Switch!\n");
+		*type = cxl_switch;
+		break;
+	case 0x03:
+		printf("Type3!\n");
+		*type = cxl_type3;
+		break;
+	}
+
+	return 0;
+}
+
+static int parse_physical_switch_identify_switch_device(void *buf, size_t buf_len)
+{
+	struct cxl_fmapi_header *rsp_head = buf;
+	struct cxl_fmapi_ident_switch_dev_resp_pl *pl = (void *)(rsp_head + 1);
+	uint8_t *b;
+	if (rsp_head->return_code != 0) {
+		printf("Error code in response %d\n", rsp_head->return_code);
+		return -1;
+	}
+	printf("Num total vppb %d\n", pl->num_total_vppb);
+	printf("Ports %d\n", pl->num_physical_ports);
+	b = pl->active_port_bitmask;
+	printf("ActivePortMask");
+	for (int i = 0; i < 32; i++)
+		printf("%02x", b[i]);
+	printf("\n");
+	return 0;
+}
+
+int query_physical_switch_info(int sd, struct sockaddr_mctp *addr, int *tag)
+{
+	uint8_t buf[1024];
+	int rc;
+	ssize_t len;
+	struct sockaddr_mctp addrrx;
+	socklen_t addrlen = sizeof(addrrx);
+	struct cxl_fmapi_header req = {
+		.category = CXL_MCTP_CATEGORY_REQ,
+		.tag = *tag++,
+		.command = CXL_IDENTIFY_SWITCH_DEVICE,
+		.command_set = CXL_FM_API_CMD_SET_PHYSICAL_SWITCH,
+		.vendor_ext_status = 0xabcd,
+	};
+
+	len = sendto(sd, &req, sizeof(req), 0, (struct sockaddr *)addr, sizeof(*addr));
+	if (len != sizeof(req)) {
+		printf("Failed to send whole request for phys switch info %d %d\n", len, sizeof(req));
+		return -1;
+	}
+
+	len = recvfrom(sd, buf, sizeof(buf), 0, (struct sockaddr *)&addrrx, &addrlen);
+	if (len < 0) {
+		printf("Failed to receive response\n");
+		return -1;
+	}
+	printf("length %d\n", len);
+	rc = parse_physical_switch_identify_switch_device(buf, len);
+	if (rc)
+		return -1;
+
+	return 0;
+}
+
+static int parse_phys_port_state_rsp(void * buf, size_t buf_len, struct cxl_fmapi_header *head)
+{
+	struct cxl_fmapi_header *rsp_head = buf;
+	struct cxl_fmapi_get_phys_port_state_resp_pl *pl = (void *)(rsp_head + 1);
+	uint32_t pl_length = rsp_head->pl_length[0] |
+		(rsp_head->pl_length[1] << 8) |
+		((rsp_head->pl_length[2] & 0xf) << 16);
+
+	if (rsp_head->category != CXL_MCTP_CATEGORY_RESP) {
+		printf("Message not a response\n");
+		return -1;
+	}
+	if (rsp_head->tag != head->tag) {
+		printf("Reply has wrong tag\n");
+		return -1;
+	}
+	if ((rsp_head->command != head->command) ||
+		(rsp_head->command_set != head->command_set)) {
+		printf("Response to wrong command\n");
+		return -1;
+	}
+
+	if (rsp_head->return_code != 0) {
+		printf("Error code in response %d\n", rsp_head->return_code);
+		printf("%x %x %x %x %x %x %x %x %x %x %x %x\n", ((uint8_t *)rsp_head)[0],((uint8_t *)rsp_head)[1],
+			((uint8_t *)rsp_head)[2],((uint8_t *)rsp_head)[3],
+			((uint8_t *)rsp_head)[4],((uint8_t *)rsp_head)[5],((uint8_t *)rsp_head)[6],
+			((uint8_t *)rsp_head)[7], ((uint8_t *)rsp_head)[8], ((uint8_t *)rsp_head)[9],
+			((uint8_t *)rsp_head)[10], ((uint8_t *)rsp_head)[11]);
+		return -1;
+	}
+
+	if (pl_length < 4 ||  pl_length < (pl->num_ports * sizeof(pl->ports[0]))) {
+		printf("too short %d %d\n", pl->num_ports);
+		return -1;
+	}
+
+	for (int i = 0; i < pl->num_ports; i++) {
+		struct cxl_fmapi_port_state_info_block *port = &pl->ports[i];
+		const char *port_states[] = {
+			[0x0] = "Disabled",
+			[0x1] = "Bind in progress",
+			[0x2] = "Unbind in progress",
+			[0x3] = "DSP",
+			[0x4] = "USP",
+			[0x5] = "Reserved",
+			//other values not present.
+			[0xf] = "Invalid Port ID"
+		  };
+		const char *connected_device_modes[] = {
+			[0] = "Not CXL / connected",
+			[1] = "CXL 1.1",
+			[2] = "CXL 2.0",
+		};
+		const char *connected_device_type[] = {
+			[0] = "No device detected",
+			[1] = "PCIe device",
+			[2] = "CXL type 1 device",
+			[3] = "CXL type 2 device",
+			[4] = "CXL type 3 device",
+			[5] = "CXL type 3 pooled device",
+			[6] = "Reserved",
+		};
+		const char *ltssm_states[] = {
+			[0] = "Detect",
+			[1] = "Polling",
+			[2] = "Configuration",
+			[3] = "Recovery",
+			[4] = "L0",
+			[5] = "L0s",
+			[6] = "L1",
+			[7] = "L2",
+			[8] = "Disabled",
+			[9] = "Loop Back",
+			[10] = "Hot Reset",
+		};
+		printf("Port%02d:\n ", port->port_id);
+		printf("\tPort state: ");
+		if (port_states[port->config_state & 0xf])
+			printf("%s\n", port_states[port->config_state]);
+		else
+			printf("Unknown state\n");
+
+		if (port->config_state == 3) { /* DSP so device could be there */
+			printf("\tConnected Device CXL Version: ");
+			if (port->connected_device_cxl_version > 2)
+				printf("Unknown CXL Version\n");
+			else
+				printf("%s\n", connected_device_modes[port->connected_device_cxl_version]);
+
+			printf("\tConnected Device Type: ");
+			if (port->connected_device_type > 7)
+				printf("Unknown\n");
+			else
+				printf("%s\n", connected_device_type[port->connected_device_type]);
+		}
+
+		printf("\tSupported CXL Modes:");
+		if (port->port_cxl_version_bitmask & 0x1)
+			printf(" 1.1");
+		if (port->port_cxl_version_bitmask & 0x2)
+			printf(" 2.0");
+		printf("\n");
+
+		printf("\tMaximum Link Width: %d Negotiated Width %d\n",
+			   port->max_link_width,
+			   port->negotiated_link_width);
+		printf("\tSupported Speeds: ");
+		if (port->supported_link_speeds_vector & 0x1)
+			printf(" 2.5 GT/s");
+		if (port->supported_link_speeds_vector & 0x2)
+			printf(" 5.0 GT/s");
+		if (port->supported_link_speeds_vector & 0x4)
+			printf(" 8.0 GT/s");
+		if (port->supported_link_speeds_vector & 0x8)
+			printf(" 16.0 GT/s");
+		if (port->supported_link_speeds_vector & 0x10)
+			printf(" 32.0 GT/s");
+		if (port->supported_link_speeds_vector & 0x20)
+			printf(" 64.0 GT/s");
+		printf("\n");
+
+		printf("\tLTSSM: ");
+		if (port->ltssm_state >= sizeof(ltssm_states))
+			printf("Unkown\n");
+		else
+			printf("%s\n", ltssm_states[port->ltssm_state]);
+	}
+}
+
+int query_ports(int sd, struct sockaddr_mctp *addr, int *tag)
+{
+	uint8_t buf[1024];
+	ssize_t len;
+	int num_ports = 4;
+	int rc;
+	uint8_t port_list[4] = { 0, 3, 7, 4 };
+	struct sockaddr_mctp addrrx;
+	socklen_t addrlen = sizeof(addrrx);
+	struct cxl_fmapi_header *head;
+	struct cxl_fmapi_get_phys_port_state_req_pl *reqpl;
+	size_t req_sz = sizeof(*reqpl) + num_ports + sizeof(*head) ;
+
+	head = malloc(req_sz);
+	*head = (struct cxl_fmapi_header) {
+		.category = CXL_MCTP_CATEGORY_REQ,
+		.tag = *tag++,
+		.command = CXL_GET_PHYSICAL_PORT_STATE,
+		.command_set = CXL_FM_API_CMD_SET_PHYSICAL_SWITCH,
+		.pl_length = {
+			req_sz & 0xff,
+			(req_sz >> 8) & 0xff,
+			(req_sz >> 16) & 0xf },
+		.vendor_ext_status = 0x1234,
+	};
+	reqpl = (void *)(head + 1);
+	*reqpl = (struct cxl_fmapi_get_phys_port_state_req_pl) {
+		.num_ports = num_ports,
+	};
+	for (int j = 0; j < num_ports; j++)
+		reqpl->ports[j] = port_list[j];
+
+	len = sendto(sd, head, req_sz, 0,
+				 (struct sockaddr *)addr, sizeof(*addr));
+
+	len = recvfrom(sd, buf, sizeof(buf), 0,
+				   (struct sockaddr *)&addrrx, &addrlen);
+	if (len < sizeof(struct cxl_fmapi_header)) {
+		printf("Too short for header\n");
+	}
+	//TODO generic check of reply.
+	if (addrrx.smctp_type != 0x7) {
+		printf("Reply does not match expected message type %x\n", addrrx.smctp_type);
+	}
+
+	rc = parse_phys_port_state_rsp(buf, len, head);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+int main(int argv, char **argc)
+{
+	int rc, cci_sd, fmapi_sd;
+	int tag = 0; /* will increment on each use */
+	ssize_t len;
+	if (argv < 2) {
+		printf("Give an address\n");
+		return -1;
+	}
+	int dev_addr = atoi(argc[1]);
+	struct sockaddr_mctp cci_addr = {
+		.smctp_family = AF_MCTP,
+		.smctp_network = 11,
+		.smctp_addr.s_addr = dev_addr,
+		.smctp_type = 0x8, /* CXL CCI */
+		.smctp_tag = MCTP_TAG_OWNER,
+	};
+	struct sockaddr_mctp fmapi_addr = {
+		.smctp_family = AF_MCTP,
+		.smctp_network = 11,
+		.smctp_addr.s_addr = dev_addr,
+		.smctp_type = 0x7, /* CXL FMAPI */
+		.smctp_tag = MCTP_TAG_OWNER,
+	};
+	struct sockaddr_mctp addrrx;
+	socklen_t addrlen = sizeof(addrrx);
+	enum cxl_type type;
+
+	cci_sd = socket(AF_MCTP, SOCK_DGRAM, 0);
+	rc = bind(cci_sd, (struct sockaddr *)&cci_addr, sizeof(cci_addr));
+	if (rc) {
+		printf("Bind failed\n");
+		return -1;
+	}
+
+	rc = query_cci_identify(cci_sd, &cci_addr, &tag, &type);
+	if (rc)
+		return rc;
+
+	if (type == cxl_switch) {
+		fmapi_sd = socket(AF_MCTP, SOCK_DGRAM, 0);
+		rc = bind(fmapi_sd, (struct sockaddr *)&fmapi_addr, sizeof(fmapi_addr));
+		if (rc) {
+			printf("Bind failed\n");
+			return -1;
+		}
+
+		rc = query_physical_switch_info(fmapi_sd, &fmapi_addr, &tag);
+		if (rc)
+			return rc;
+
+		/* Next query some of the ports */
+		rc = query_ports(fmapi_sd, &fmapi_addr, &tag);
+		if (rc)
+			return rc;
+	}
+
+	return 0;
+}
-- 
2.39.2

Jonathan Cameron (6):
  hw/acpi/aml-build: add function for i2c slave device serial bus
    description
  HACK: arm/virt: Add aspeed-i2c controller and MCTP EP to enable MCTP
    testing
  HACK: hw/arm/virt: Add ACPI support for aspeed-i2c / mctp
  HACK: hw/i386/pc: Add Aspeed i2c controller + MCTP with ACPI tables
  misc/i2c_mctp_cxl: Initial device emulation
  docs: cxl: Add example commandline for MCTP CXL CCIs

 docs/system/devices/cxl.rst |  27 +++
 include/hw/acpi/aml-build.h |   1 +
 include/hw/arm/virt.h       |   2 +
 include/hw/cxl/cxl_fmapi.h  | 102 ++++++++++
 include/hw/i386/pc.h        |   1 +
 hw/acpi/aml-build.c         |  17 ++
 hw/arm/virt-acpi-build.c    |  60 ++++++
 hw/arm/virt.c               |  92 ++++++++-
 hw/cxl/i2c_mctp_cxl.c       | 369 ++++++++++++++++++++++++++++++++++++
 hw/i386/acpi-build.c        |  65 +++++++
 hw/i386/pc.c                |  20 +-
 hw/arm/Kconfig              |   2 +
 hw/cxl/Kconfig              |   3 +
 hw/cxl/meson.build          |   1 +
 hw/i2c/meson.build          |   2 +-
 hw/i386/Kconfig             |   2 +
 hw/misc/meson.build         |   1 +
 17 files changed, 762 insertions(+), 5 deletions(-)
 create mode 100644 include/hw/cxl/cxl_fmapi.h
 create mode 100644 hw/cxl/i2c_mctp_cxl.c

-- 
2.39.2
[RFC PATCH 1/6] hw/acpi/aml-build: add function for i2c slave device serial bus description
Posted by Jonathan Cameron via 11 months ago
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 include/hw/acpi/aml-build.h |  1 +
 hw/acpi/aml-build.c         | 17 +++++++++++++++++
 2 files changed, 18 insertions(+)

diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h
index fc2b949fb5..28db028b17 100644
--- a/include/hw/acpi/aml-build.h
+++ b/include/hw/acpi/aml-build.h
@@ -382,6 +382,7 @@ Aml *aml_dma(AmlDmaType typ, AmlDmaBusMaster bm, AmlTransferSize sz,
              uint8_t channel);
 Aml *aml_sleep(uint64_t msec);
 Aml *aml_i2c_serial_bus_device(uint16_t address, const char *resource_source);
+Aml *aml_i2c_slv_serial_bus_device(uint16_t address, const char *resource_source);
 
 /* Block AML object primitives */
 Aml *aml_scope(const char *name_format, ...) G_GNUC_PRINTF(1, 2);
diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c
index 918cbb5b9d..22d7ecd753 100644
--- a/hw/acpi/aml-build.c
+++ b/hw/acpi/aml-build.c
@@ -2478,3 +2478,20 @@ Aml *aml_i2c_serial_bus_device(uint16_t address, const char *resource_source)
 
     return var;
 }
+
+/* ACPI 5.0: 6.4.3.8.2.1 I2C Serial Bus Connection Resource Descriptor */
+Aml *aml_i2c_slv_serial_bus_device(uint16_t address, const char *resource_source)
+{
+    uint16_t resource_source_len = strlen(resource_source) + 1;
+    Aml *var = aml_serial_bus_device(AML_SERIAL_BUS_TYPE_I2C, 1, 0, 1,
+                                     6, resource_source_len);
+
+    /* Connection Speed.  Just set to 100K for now, it doesn't really matter. */
+    build_append_int_noprefix(var->buf, 100000, 4);
+    build_append_int_noprefix(var->buf, address, sizeof(address));
+
+    /* This is a string, not a name, so just copy it directly in. */
+    g_array_append_vals(var->buf, resource_source, resource_source_len);
+
+    return var;
+}
-- 
2.39.2
[RFC PATCH 2/6] HACK: arm/virt: Add aspeed-i2c controller and MCTP EP to enable MCTP testing
Posted by Jonathan Cameron via 11 months ago
As the only I2C emulation in QEMU that supports being both
a master and a slave, suitable for MCTP over i2c is aspeed-i2c
add this controller to the arm virt model and hook up our new
i2c_mctp_cxl_fmapi device.

The current Linux driver for aspeed-i2c has a hard requirement on
a reset controller.  Throw down the simplest reset controller
I could find so as to avoid need to make any changes to the kernel
code.

Patch also builds appropriate device tree.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 include/hw/arm/virt.h |  2 +
 hw/arm/virt.c         | 92 +++++++++++++++++++++++++++++++++++++++++--
 hw/arm/Kconfig        |  1 +
 3 files changed, 92 insertions(+), 3 deletions(-)

diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 9fc582fc5f..ea3a64f4a8 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -72,6 +72,8 @@ enum {
     VIRT_SMMU,
     VIRT_UART,
     VIRT_MMIO,
+    VIRT_I2C,
+    VIRT_RESET_FAKE,
     VIRT_RTC,
     VIRT_FW_CFG,
     VIRT_PCIE,
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index f0be654cef..b664c849fa 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -85,6 +85,8 @@
 #include "hw/cxl/cxl.h"
 #include "hw/cxl/cxl_host.h"
 #include "qemu/guest-random.h"
+#include "hw/i2c/i2c.h"
+#include "hw/i2c/aspeed_i2c.h"
 
 #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
     static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
@@ -161,6 +163,8 @@ static const MemMapEntry base_memmap[] = {
     [VIRT_PVTIME] =             { 0x090a0000, 0x00010000 },
     [VIRT_SECURE_GPIO] =        { 0x090b0000, 0x00001000 },
     [VIRT_MMIO] =               { 0x0a000000, 0x00000200 },
+    [VIRT_I2C] =                { 0x0b000000, 0x00004000 },
+    [VIRT_RESET_FAKE] =         { 0x0b004000, 0x00000010 },
     /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
     [VIRT_PLATFORM_BUS] =       { 0x0c000000, 0x02000000 },
     [VIRT_SECURE_MEM] =         { 0x0e000000, 0x01000000 },
@@ -203,6 +207,7 @@ static const int a15irqmap[] = {
     [VIRT_GPIO] = 7,
     [VIRT_SECURE_UART] = 8,
     [VIRT_ACPI_GED] = 9,
+    [VIRT_I2C] = 10,
     [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
     [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
     [VIRT_SMMU] = 74,    /* ...to 74 + NUM_SMMU_IRQS - 1 */
@@ -2275,6 +2280,85 @@ static void virt_cpu_post_init(VirtMachineState *vms, MemoryRegion *sysmem)
     }
 }
 
+static void create_mctp(MachineState *ms)
+{
+    VirtMachineState *vms = VIRT_MACHINE(ms);
+    MemoryRegion *sysmem = get_system_memory();
+    AspeedI2CState *aspeedi2c;
+    struct DeviceState  *dev;
+    char *nodename_i2c_master;
+    char *nodename_i2c_sub;
+    char *nodename_reset;
+    uint32_t clk_phandle, reset_phandle;
+    MemoryRegion *sysmem2;
+
+    dev = qdev_new("aspeed.i2c-ast2600");
+    aspeedi2c = ASPEED_I2C(dev);
+    object_property_set_link(OBJECT(dev), "dram", OBJECT(ms->ram),
+                             &error_fatal);
+    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_I2C].base);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&aspeedi2c->busses[0]), 0,
+                       qdev_get_gpio_in(vms->gic, vms->irqmap[VIRT_I2C]));
+
+    /* I2C bus DT */
+    reset_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+    nodename_reset = g_strdup_printf("/reset@%" PRIx64,
+                                     vms->memmap[VIRT_RESET_FAKE].base);
+    qemu_fdt_add_subnode(ms->fdt, nodename_reset);
+    qemu_fdt_setprop_string(ms->fdt, nodename_reset,
+                            "compatible", "snps,dw-low-reset");
+    qemu_fdt_setprop_sized_cells(ms->fdt, nodename_reset, "reg",
+                                 2, vms->memmap[VIRT_RESET_FAKE].base,
+                                 2, vms->memmap[VIRT_RESET_FAKE].size);
+    qemu_fdt_setprop_cell(ms->fdt, nodename_reset, "#reset-cells", 0x1);
+    qemu_fdt_setprop_cell(ms->fdt, nodename_reset, "phandle", reset_phandle);
+    sysmem2 =  g_new(MemoryRegion, 1);
+    memory_region_init_ram(sysmem2, NULL, "reset",
+                           vms->memmap[VIRT_RESET_FAKE].size, NULL);
+    memory_region_add_subregion(sysmem,
+                                vms->memmap[VIRT_RESET_FAKE].base, sysmem2);
+
+    clk_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+
+    qemu_fdt_add_subnode(ms->fdt, "/mclk");
+    qemu_fdt_setprop_string(ms->fdt, "/mclk", "compatible", "fixed-clock");
+    qemu_fdt_setprop_cell(ms->fdt, "/mclk", "#clock-cells", 0x0);
+    qemu_fdt_setprop_cell(ms->fdt, "/mclk", "clock-frequency", 24000);
+    qemu_fdt_setprop_string(ms->fdt, "/mclk", "clock-output-names", "bobsclk");
+    qemu_fdt_setprop_cell(ms->fdt, "/mclk", "phandle", clk_phandle);
+
+    nodename_i2c_master = g_strdup_printf("/i2c@%" PRIx64,
+                                          vms->memmap[VIRT_I2C].base);
+    qemu_fdt_add_subnode(ms->fdt, nodename_i2c_master);
+    qemu_fdt_setprop_string(ms->fdt, nodename_i2c_master,
+                            "compatible",  "aspeed,ast2600-i2c-bus");
+    qemu_fdt_setprop_cells(ms->fdt, nodename_i2c_master, "multi-master");
+    qemu_fdt_setprop_cell(ms->fdt, nodename_i2c_master, "#size-cells", 0);
+    qemu_fdt_setprop_cell(ms->fdt, nodename_i2c_master, "#address-cells", 1);
+    qemu_fdt_setprop_cell(ms->fdt, nodename_i2c_master, "clocks", clk_phandle);
+    qemu_fdt_setprop_string(ms->fdt, nodename_i2c_master,
+                            "clock-names", "bobsclk");
+    qemu_fdt_setprop(ms->fdt, nodename_i2c_master, "mctp-controller", NULL, 0);
+    qemu_fdt_setprop_cells(ms->fdt, nodename_i2c_master,
+                           "interrupts", GIC_FDT_IRQ_TYPE_SPI,
+                           vms->irqmap[VIRT_I2C], GIC_FDT_IRQ_FLAGS_LEVEL_HI);
+    /* Offset to the first bus is 0x80, next one at 0x100 etc */
+    qemu_fdt_setprop_sized_cells(ms->fdt, nodename_i2c_master, "reg",
+                                 2, vms->memmap[VIRT_I2C].base + 0x80,
+                                 2, 0x80);
+    qemu_fdt_setprop_cells(ms->fdt, nodename_i2c_master,
+                           "resets", reset_phandle,  0);
+
+    nodename_i2c_sub = g_strdup_printf("/i2c@%" PRIx64 "/mctp@%" PRIx64,
+                                       vms->memmap[VIRT_I2C].base, 0x50l);
+    qemu_fdt_add_subnode(ms->fdt, nodename_i2c_sub);
+    qemu_fdt_setprop_string(ms->fdt, nodename_i2c_sub,
+                            "compatible",  "mctp-i2c-controller");
+    qemu_fdt_setprop_sized_cells(ms->fdt, nodename_i2c_sub,
+                                 "reg", 1, 0x50 | 0x40000000);
+}
+
 static void machvirt_init(MachineState *machine)
 {
     VirtMachineState *vms = VIRT_MACHINE(machine);
@@ -2565,9 +2649,11 @@ static void machvirt_init(MachineState *machine)
         create_gpio_devices(vms, VIRT_SECURE_GPIO, secure_sysmem);
     }
 
-     /* connect powerdown request */
-     vms->powerdown_notifier.notify = virt_powerdown_req;
-     qemu_register_powerdown_notifier(&vms->powerdown_notifier);
+    create_mctp(machine);
+
+    /* connect powerdown request */
+    vms->powerdown_notifier.notify = virt_powerdown_req;
+    qemu_register_powerdown_notifier(&vms->powerdown_notifier);
 
     /* Create mmio transports, so the user can create virtio backends
      * (which will be automatically plugged in to the transports). If
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 223597ff13..298134ce85 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -32,6 +32,7 @@ config ARM_VIRT
     select VIRTIO_MEM_SUPPORTED
     select ACPI_CXL
     select ACPI_HMAT
+    select I2C_MCTP_CXL_FMAPI
 
 config CHEETAH
     bool
-- 
2.39.2
[RFC PATCH 3/6] HACK: hw/arm/virt: Add ACPI support for aspeed-i2c / mctp
Posted by Jonathan Cameron via 11 months ago
Enable this for FM-API testing for CXL devices via MCTP over I2C

Example DSDT block:

Device (MCTP)
{
    Name (_HID, "PRP0001")  // _HID: Hardware ID
    Name (_DSD, Package (0x02)  // _DSD: Device-Specific Data
    {
        ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /* Device Properties for _DSD */,
        Package (0x03)
        {
            Package (0x02)
            {
                "compatible",
                "aspeed,ast2600-i2c-bus"
            },
            Package (0x02)
            {
                "bus-frequency",
                0x00061A80
            },
            Package (0x02)
            {
                "mctp-controller",
                One
            }
        }
    })
    Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings
    {
        Memory32Fixed (ReadWrite,
            0x0B000080,         // Address Base
            0x00000080,         // Address Length
            )
        Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, )
        {
            0x0000002A,
        }
    })
}
Device (MCTS)
{
    Name (_HID, "PRP0001")  // _HID: Hardware ID
    Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings
    {
        I2cSerialBusV2 (0x0050, DeviceInitiated, 0x000186A0,
           AddressingMode7Bit, "\\_SB.MCTP",
           0x00, ResourceProducer, , Exclusive,
            )
    })
    Name (_DSD, Package (0x02)  // _DSD: Device-Specific Data
    {
        ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /* Device Properties for _DSD */,
        Package (0x01)
        {
            Package (0x02)
            {
                "compatible",
                "mctp-i2c-controller"
            }
        }
    })
}

Tests not updated given I'm not currently proposing this for upstream.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 hw/arm/virt-acpi-build.c | 60 ++++++++++++++++++++++++++++++++++++++++
 hw/i2c/meson.build       |  2 +-
 2 files changed, 61 insertions(+), 1 deletion(-)

diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 939a736342..2c170f75a3 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -77,6 +77,65 @@ static void acpi_dsdt_add_cpus(Aml *scope, VirtMachineState *vms)
     }
 }
 
+static void acpi_dsdt_add_mctp(Aml *scope, VirtMachineState *vms)
+{
+    uint32_t interrupt = vms->irqmap[VIRT_I2C] + ARM_SPI_BASE;
+    Aml *main_dev = aml_device("MCTP");
+    Aml *sub_dev = aml_device("MCTS");
+    Aml *dsd_pkg = aml_package(2);
+    Aml *props_pkg = aml_package(3);
+    Aml *pkg = aml_package(2);
+    Aml *crs = aml_resource_template();
+
+    aml_append(main_dev, aml_name_decl("_HID", aml_string("PRP0001")));
+
+    aml_append(pkg, aml_string("compatible"));
+    aml_append(pkg, aml_string("aspeed,ast2600-i2c-bus"));
+    aml_append(props_pkg, pkg);
+
+    pkg = aml_package(2);
+    aml_append(pkg, aml_string("bus-frequency"));
+    aml_append(pkg, aml_int(400000));
+    aml_append(props_pkg, pkg);
+
+    pkg = aml_package(2);
+    aml_append(pkg, aml_string("mctp-controller"));
+    aml_append(pkg, aml_int(1));
+    aml_append(props_pkg, pkg);
+
+    aml_append(dsd_pkg, aml_touuid("DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301"));
+    aml_append(dsd_pkg, props_pkg);
+    aml_append(main_dev, aml_name_decl("_DSD", dsd_pkg));
+
+    aml_append(crs, aml_memory32_fixed(vms->memmap[VIRT_I2C].base + 0x80,
+                                      0x80, AML_READ_WRITE));
+    aml_append(crs,
+               aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
+                             AML_EXCLUSIVE, &interrupt, 1));
+    aml_append(main_dev, aml_name_decl("_CRS", crs));
+
+    aml_append(sub_dev, aml_name_decl("_HID", aml_string("PRP0001")));
+
+    dsd_pkg = aml_package(2);
+    aml_append(dsd_pkg, aml_touuid("DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301"));
+
+    props_pkg = aml_package(1);
+
+    pkg = aml_package(2);
+    aml_append(pkg, aml_string("compatible"));
+    aml_append(pkg, aml_string("mctp-i2c-controller"));
+    aml_append(props_pkg, pkg);
+    aml_append(dsd_pkg, props_pkg);
+
+    crs = aml_resource_template();
+    aml_append(crs, aml_i2c_slv_serial_bus_device(0x50, "\\_SB.MCTP"));
+    aml_append(sub_dev, aml_name_decl("_CRS", crs));
+    aml_append(sub_dev, aml_name_decl("_DSD", dsd_pkg));
+
+    aml_append(scope, main_dev);
+    aml_append(scope, sub_dev);
+}
+
 static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
                                            uint32_t uart_irq)
 {
@@ -887,6 +946,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
      */
     scope = aml_scope("\\_SB");
     acpi_dsdt_add_cpus(scope, vms);
+    acpi_dsdt_add_mctp(scope, vms);
     acpi_dsdt_add_uart(scope, &memmap[VIRT_UART],
                        (irqmap[VIRT_UART] + ARM_SPI_BASE));
     if (vmc->acpi_expose_flash) {
diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build
index fd1f9022fd..08fbbc1831 100644
--- a/hw/i2c/meson.build
+++ b/hw/i2c/meson.build
@@ -4,7 +4,7 @@ i2c_ss.add(when: 'CONFIG_MCTP_I2C', if_true: files('mctp.c'))
 i2c_ss.add(when: 'CONFIG_SMBUS', if_true: files('smbus_slave.c', 'smbus_master.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'))
-i2c_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_i2c.c'))
+i2c_ss.add(when: 'CONFIG_I2C', if_true: files('aspeed_i2c.c'))
 i2c_ss.add(when: 'CONFIG_BITBANG_I2C', if_true: files('bitbang_i2c.c'))
 i2c_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_i2c.c'))
 i2c_ss.add(when: 'CONFIG_IMX_I2C', if_true: files('imx_i2c.c'))
-- 
2.39.2
[RFC PATCH 4/6] HACK: hw/i386/pc: Add Aspeed i2c controller + MCTP with ACPI tables
Posted by Jonathan Cameron via 11 months ago
CXL devices provide a standard Fabric Management API - FM-API.
See CXL specification r3.0 from https://www.computeexpresslink.org
In many real setups that will be used by a separate host from the
one actually using the CXL devices (BMC or similar) but it is
helpful to be able to use the main CXL emulation and the
Fabric Management emulation on a single host.  This 'hack' enables
that (with minor kernel driver changes).

There are many many things wrong with how this is done but for
now it enables use of this aspeed controller with ACPI FW
on an x86 host.  That is useful for testing MCTP over I2C.

If anyone has either:
1) Docs for an I2C controller with MCTP support that might actually
   appear on an x86 host.
2) A nice solution for how wrap this up in a device whilst minimising
   kernel changes.
3) A guide / reference example to how to do the interrupt 'right'
   (I'm an ARM focused developer so got lost in the x86 interrupt
    stuff).
then let me know.

For now this works and I will carry it out of tree on
gitlab.com/jic23/qemu.

DSDT blob - as this is a hack I haven't included test updates

Scope (_SB)
{
    Device (MCTP)
    {
        Name (_HID, "PRP0001")  // _HID: Hardware ID
        Name (_DSD, Package (0x02)  // _DSD: Device-Specific Data
        {
            ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /* Device Properties for _DSD */,
            Package (0x03)
            {
                Package (0x02)
                {
                    "compatible",
                    "aspeed,ast2600-i2c-bus"
                },

                Package (0x02)
                {
                    "bus-frequency",
                    0x00061A80
                },

                Package (0x02)
                {
                    "mctp-controller",
                    One
                }
            }
        })
        Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings
        {
            QWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite,
                0x0000000000000000, // Granularity
                0x00000004800FC080, // Range Minimum
                0x00000004800FC0FF, // Range Maximum
                0x0000000000000000, // Translation Offset
                0x0000000000000080, // Length
                ,, , AddressRangeMemory, TypeStatic)
            Interrupt (ResourceConsumer, Level, ActiveHigh, Shared, ,, )
            {
                0x00000007,
            }
        })
    }

    Device (MCTS)
    {
        Name (_HID, "PRP0001")  // _HID: Hardware ID
        Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings
        {
            I2cSerialBusV2 (0x0050, DeviceInitiated, 0x000186A0,
                AddressingMode7Bit, "\\_SB.MCTP",
                0x00, ResourceProducer, , Exclusive,
                )
        })
        Name (_DSD, Package (0x02)  // _DSD: Device-Specific Data
        {
            ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /* Device Properties for _DSD */,
            Package (0x01)
            {
                Package (0x02)
                {
                    "compatible",
                     "mctp-i2c-controller"
                }
            }
        })
    }
}

To add devices to the bus use something like:
 -device i2c_mctp_cxl_switch,bus=aspeed.i2c.bus.0,address=4,target=us0

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 include/hw/i386/pc.h |  1 +
 hw/i386/acpi-build.c | 65 ++++++++++++++++++++++++++++++++++++++++++++
 hw/i386/pc.c         | 20 +++++++++++++-
 hw/i386/Kconfig      |  1 +
 4 files changed, 86 insertions(+), 1 deletion(-)

diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index c661e9cc80..2050dabc5f 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -56,6 +56,7 @@ typedef struct PCMachineState {
 
     SGXEPCState sgx_epc;
     CXLState cxl_devices_state;
+    hwaddr i2c_base;
 } PCMachineState;
 
 #define PC_MACHINE_ACPI_DEVICE_PROP "acpi-device"
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index d0c8e8f045..511a637114 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -1424,6 +1424,68 @@ static void build_acpi0017(Aml *table)
     aml_append(table, scope);
 }
 
+static void acpi_dsdt_add_mctp(Aml *scope, PCMachineState *pcms)
+{
+    uint32_t interrupt = 7;
+    Aml *main_dev = aml_device("MCTP");
+    Aml *sub_dev = aml_device("MCTS");
+    Aml *dsd_pkg = aml_package(2);
+    Aml *props_pkg = aml_package(3);
+    Aml *pkg = aml_package(2);
+    Aml *crs = aml_resource_template();
+
+    aml_append(main_dev, aml_name_decl("_HID", aml_string("PRP0001")));
+
+    aml_append(pkg, aml_string("compatible"));
+    aml_append(pkg, aml_string("aspeed,ast2600-i2c-bus"));
+    aml_append(props_pkg, pkg);
+
+    pkg = aml_package(2);
+    aml_append(pkg, aml_string("bus-frequency"));
+    aml_append(pkg, aml_int(400000));
+    aml_append(props_pkg, pkg);
+
+    pkg = aml_package(2);
+    aml_append(pkg, aml_string("mctp-controller"));
+    aml_append(pkg, aml_int(1));
+    aml_append(props_pkg, pkg);
+
+    aml_append(dsd_pkg, aml_touuid("DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301"));
+    aml_append(dsd_pkg, props_pkg);
+    aml_append(main_dev, aml_name_decl("_DSD", dsd_pkg));
+
+    aml_append(crs, aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED,
+                                     AML_MAX_FIXED, AML_NON_CACHEABLE,
+                                     AML_READ_WRITE, 0, pcms->i2c_base + 0x80,
+                                     pcms->i2c_base + 0x80 + 0x80 - 1,
+                                     0, 0x80));
+    aml_append(crs,
+               aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
+                             AML_SHARED, &interrupt, 1));
+    aml_append(main_dev, aml_name_decl("_CRS", crs));
+
+    aml_append(sub_dev, aml_name_decl("_HID", aml_string("PRP0001")));
+
+    dsd_pkg = aml_package(2);
+    aml_append(dsd_pkg, aml_touuid("DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301"));
+
+    props_pkg = aml_package(1);
+
+    pkg = aml_package(2);
+    aml_append(pkg, aml_string("compatible"));
+    aml_append(pkg, aml_string("mctp-i2c-controller"));
+    aml_append(props_pkg, pkg);
+    aml_append(dsd_pkg, props_pkg);
+
+    crs = aml_resource_template();
+    aml_append(crs, aml_i2c_slv_serial_bus_device(0x50, "\\_SB.MCTP"));
+    aml_append(sub_dev, aml_name_decl("_CRS", crs));
+    aml_append(sub_dev, aml_name_decl("_DSD", dsd_pkg));
+
+    aml_append(scope, main_dev);
+    aml_append(scope, sub_dev);
+}
+
 /*
  * Precompute the crs ranges and bus numbers that will be used in PXB entries
  * in PXB SSDT.
@@ -1652,6 +1714,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
         build_hpet_aml(dsdt);
     }
 
+    sb_scope = aml_scope("_SB");
+    acpi_dsdt_add_mctp(sb_scope, pcms);
+    aml_append(dsdt, sb_scope);
     if (vmbus_bridge) {
         sb_scope = aml_scope("_SB");
         aml_append(sb_scope, build_vmbus_device_aml(vmbus_bridge));
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index bb62c994fa..6d77c8970e 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -95,6 +95,7 @@
 #include "hw/i386/kvm/xen_gnttab.h"
 #include "hw/i386/kvm/xen_xenstore.h"
 #include "hw/mem/memory-device.h"
+#include "hw/i2c/aspeed_i2c.h"
 #include "sysemu/replay.h"
 #include "target/i386/cpu.h"
 #include "e820_memory_layout.h"
@@ -1083,6 +1084,8 @@ void pc_memory_init(PCMachineState *pcms,
         memory_region_init(mr, OBJECT(machine), "cxl_host_reg", cxl_size);
         memory_region_add_subregion(system_memory, cxl_base, mr);
         cxl_resv_end = cxl_base + cxl_size;
+        pcms->i2c_base = cxl_resv_end - 0x4000;
+
         if (pcms->cxl_devices_state.fixed_windows) {
             hwaddr cxl_fmw_base;
             GList *it;
@@ -1166,7 +1169,7 @@ uint64_t pc_pci_hole64_start(void)
     ram_addr_t size = 0;
 
     if (pcms->cxl_devices_state.is_enabled) {
-        hole64_start = pc_get_cxl_range_end(pcms);
+        hole64_start = pc_get_cxl_range_end(pcms) + 0x4000;
     } else if (pcmc->has_reserved_memory && (ms->ram_size < ms->maxram_size)) {
         pc_get_device_memory_range(pcms, &hole64_start, &size);
         if (!pcmc->broken_reserved_end) {
@@ -1360,6 +1363,21 @@ void pc_basic_device_init(struct PCMachineState *pcms,
     /* Super I/O */
     pc_superio_init(isa_bus, create_fdctrl, pcms->i8042_enabled,
                     pcms->vmport != ON_OFF_AUTO_ON);
+
+    {
+        AspeedI2CState *aspeed_i2c;
+        struct DeviceState *dev;
+
+        dev = qdev_new("aspeed.i2c-ast2600");
+        aspeed_i2c = ASPEED_I2C(dev);
+        object_property_set_link(OBJECT(dev), "dram",
+                                 OBJECT(MACHINE(pcms)->ram), &error_fatal);
+        sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+        sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, pcms->i2c_base);
+        /* Hack ;) - Steal unused interrupt 7 */
+        sysbus_connect_irq(SYS_BUS_DEVICE(&aspeed_i2c->busses[0]), 0,
+                           x86ms->gsi[7]);
+    }
 }
 
 void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus)
diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig
index 9051083c1e..31e1958368 100644
--- a/hw/i386/Kconfig
+++ b/hw/i386/Kconfig
@@ -45,6 +45,7 @@ config PC
     select ACPI_VMGENID
     select VIRTIO_PMEM_SUPPORTED
     select VIRTIO_MEM_SUPPORTED
+    select I2C_MCTP_CXL_FMAPI
 
 config PC_PCI
     bool
-- 
2.39.2
[RFC PATCH 5/6] misc/i2c_mctp_cxl: Initial device emulation
Posted by Jonathan Cameron via 11 months ago
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.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 include/hw/cxl/cxl_fmapi.h | 102 ++++++++++
 hw/cxl/i2c_mctp_cxl.c      | 369 +++++++++++++++++++++++++++++++++++++
 hw/arm/Kconfig             |   3 +-
 hw/cxl/Kconfig             |   3 +
 hw/cxl/meson.build         |   1 +
 hw/i386/Kconfig            |   3 +-
 hw/misc/meson.build        |   1 +
 7 files changed, 480 insertions(+), 2 deletions(-)

diff --git a/include/hw/cxl/cxl_fmapi.h b/include/hw/cxl/cxl_fmapi.h
new file mode 100644
index 0000000000..d5195a9fcc
--- /dev/null
+++ b/include/hw/cxl/cxl_fmapi.h
@@ -0,0 +1,102 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * CXL Fabric Manager API definitions
+ *
+ * Copyright (c) 2023 Huawei Technologies.
+ *
+ * Refs to: Compute Express Link (CXL) Specification revision 3.0 Version 1.0
+ * from www.computeexpresslink.org
+ *
+ * FM-API commands can be carried over various transports (MCTP, switch-CCI etc)
+ * so define the payloads in a common header.
+ */
+
+#ifndef CXL_FMAPI_H
+#define CXL_FMAPI_H
+
+#include "qemu/osdep.h"
+
+/*
+ * TODO: Confirm which commands sent via FM-API binding and which via Type 3 CCI
+ * binding.  For now I'm assuming only stuff in the FM-API table goes via
+ * FM-API.
+ */
+
+/*
+ * CXL r3.0 Table 8-36 Generic Component Command Opcodes
+ */
+
+/* CXL r3.0 8.2.9.1.1 Identify (Opcode 0001h) */
+#define CXL_CCI_CMD_SET_INFOSTAT 0x00
+#define   CXL_CCI_INFOSTAT_IDENTIFY 0x01
+
+struct cxl_cci_infostat_identify_resp_pl {
+    uint16_t vendor_id;
+    uint16_t device_id;
+    uint16_t subsystem_vendor_id;
+    uint16_t subsystem_id;
+    uint8_t serial_num[8];
+    uint8_t max_msg;
+    uint8_t component_type;
+};
+
+/*
+ * CXL r3.0 7.6.7 Fabric Management Application Programming Interface
+ */
+#define CXL_FMAPI_CMD_SET_PHYSICAL_SWITCH 0x51
+#define   CXL_FMAPI_PHYSICAL_SWITCH_IDENTIFY_SWITCH 0x00
+#define   CXL_FMAPI_GET_PHYSICAL_PORT_STATE 0x01
+
+/*
+ * CXL r3.0 7.6.7.1.1 Identify Switch Device (Opcode 5100h)
+ */
+struct cxl_fmapi_ident_switch_dev_resp_pl {
+    uint8_t ingres_port_id;
+    uint8_t rsv1;
+    uint8_t num_physical_ports;
+    uint8_t num_vcs;
+    uint8_t active_port_bitmask[32];
+    uint8_t active_vcs_bitmask[32];
+    uint16_t num_total_vppb;
+    uint16_t num_active_vppb;
+    uint8_t num_hdm_decoders;
+} QEMU_PACKED;
+
+/*
+ * CXL r3.0 7.6.7.1.2 Get Physical Port State (Opcode 5101h)
+ */
+
+/* CXL r3.0 Table 7-18 Get Physical Port State Request Payload */
+struct cxl_fmapi_get_phys_port_state_req_pl {
+    uint8_t num_ports; /* CHECK. may get too large for MCTP message size */
+    uint8_t ports[];
+} QEMU_PACKED;
+
+/* CXL r3.0 Table 7-20 Get Physical Port State Port Information Block Format */
+struct cxl_fmapi_port_state_info_block {
+    uint8_t port_id;
+    uint8_t config_state;
+    uint8_t connected_device_cxl_version;
+    uint8_t rsv1;
+    uint8_t connected_device_type;
+    uint8_t port_cxl_version_bitmask;
+    uint8_t max_link_width;
+    uint8_t negotiated_link_width;
+    uint8_t supported_link_speeds_vector;
+    uint8_t max_link_speed;
+    uint8_t current_link_speed;
+    uint8_t ltssm_state;
+    uint8_t first_lane_num;
+    uint16_t link_state;
+    uint8_t supported_ld_count;
+} QEMU_PACKED;
+
+/* CXL r3.0 Table 7-19 Get Physical Port State Response Payload */
+struct cxl_fmapi_get_phys_port_state_resp_pl {
+    uint8_t num_ports;
+    uint8_t rsv1[3];
+    struct cxl_fmapi_port_state_info_block ports[];
+} QEMU_PACKED;
+
+#endif /* CXL_FMAPI_H */
diff --git a/hw/cxl/i2c_mctp_cxl.c b/hw/cxl/i2c_mctp_cxl.c
new file mode 100644
index 0000000000..8ce4c25eef
--- /dev/null
+++ b/hw/cxl/i2c_mctp_cxl.c
@@ -0,0 +1,369 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Emulation of a CXL Switch Fabric Management interface over MCTP over I2C.
+ *
+ * Copyright (c) 2023 Huawei Technologies.
+ *
+ * Reference list:
+ * From www.dmtf.org
+ * DSP0236 Management Component Transport Protocol (MCTP) Base Specification 1.3.0
+ * DPS0234 CXL Fabric Manager API over MCTP Binding Specification 1.0.0
+ * DSP0281 CXL Type 3 Deivce 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 "hw/irq.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "hw/cxl/cxl.h"
+#include "hw/cxl/cxl_fmapi.h"
+#include "hw/pci/pcie.h"
+#include "hw/pci/pcie_port.h"
+#include "hw/qdev-properties.h"
+
+#define TYPE_I2C_MCTP_CXL "i2c_mctp_cxl"
+
+#define MCTP_CXL_MAX_MSG_LEN 1088 /* CXL FMAPI binding spec */
+
+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 vendor_tatus;
+    uint16_t rc;
+    uint8_t payload[];
+} QEMU_PACKED CXLMCTPMessage;
+
+enum cxl_dev_type {
+    cxl_type3,
+    cxl_switch,
+};
+
+struct I2C_MCTP_CXL_State {
+    MCTPI2CEndpoint mctp;
+    PCIDevice *target;
+    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 void cxl_cci_cmd_set_info_and_status_parse(I2C_MCTP_CXL_State *s,
+                                                  CXLMCTPMessage *msg)
+{
+    CXLMCTPMessage *out = (void *)s->scratch;
+    PCIDeviceClass *class = PCI_DEVICE_GET_CLASS(s->target);
+
+    switch (msg->command) {
+    case CXL_CCI_INFOSTAT_IDENTIFY:
+    {
+        struct cxl_cci_infostat_identify_resp_pl *pl =
+            (struct cxl_cci_infostat_identify_resp_pl *)&s->scratch[s->pos];
+
+        *pl = (struct cxl_cci_infostat_identify_resp_pl) {
+            .vendor_id = class->vendor_id,
+            .device_id = class->device_id,
+            .subsystem_vendor_id = class->subsystem_vendor_id,
+            .subsystem_id = class->subsystem_id,
+            /* TODO : Get serial number  - a bit fiddly */
+            .max_msg = 9, /* 512 - no need to chunk mctp for this */
+        };
+        switch (s->type) {
+        case cxl_type3:
+            pl->component_type = 0x3;
+            break;
+        case cxl_switch:
+            pl->component_type = 0x0;
+            break;
+        }
+
+        s->len += sizeof(*pl);
+        out->rc = CXL_MBOX_SUCCESS;
+        return;
+    }
+
+    default:
+        out->rc = CXL_MBOX_UNSUPPORTED;
+        return;
+    }
+}
+
+/* May make sense to push some of this to individual device emulation */
+static void cxl_fmapi_cmd_set_physical_switch_parse(I2C_MCTP_CXL_State *s,
+                                                    CXLMCTPMessage *msg)
+{
+    CXLMCTPMessage *out = (void *)s->scratch;
+
+    if (s->type != cxl_switch) {
+        /* TODO: Rename return codes as used for this as well as mailbox */
+        out->rc = CXL_MBOX_UNSUPPORTED;
+        return;
+    }
+
+    switch (msg->command) {
+    case CXL_FMAPI_PHYSICAL_SWITCH_IDENTIFY_SWITCH:
+    {
+        PCIEPort *usp = PCIE_PORT(s->target);
+        PCIBus *bus = &PCI_BRIDGE(s->target)->sec_bus;
+        struct cxl_fmapi_ident_switch_dev_resp_pl *pl =
+            (struct cxl_fmapi_ident_switch_dev_resp_pl *)&s->scratch[s->pos];
+        int num_phys_ports = pcie_count_ds_ports(bus);
+        int devfn;
+
+        *pl = (struct cxl_fmapi_ident_switch_dev_resp_pl) {
+             /* TODO: Should be parameterized to support multiple instances */
+            .ingres_port_id = 0,
+            .num_physical_ports = num_phys_ports + 1, /* 1 USP */
+            .num_vcs = 1, /* Not yet support multiple VCS - potentialy tricky */
+            .active_vcs_bitmask[0] = 0x1,
+            .num_total_vppb = num_phys_ports + 1,
+            .num_active_vppb = num_phys_ports + 1,
+            .num_hdm_decoders = 4,
+        };
+
+        /* Fill in the active ports bitmask with all USP and DSP port numbers */
+        for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
+            PCIDevice *d = bus->devices[devfn];
+
+            if (!d || !pci_is_express(d) || !d->exp.exp_cap) {
+                continue;
+            }
+            if (object_dynamic_cast(OBJECT(d), TYPE_PCIE_PORT)) {
+                PCIEPort *port = PCIE_PORT(d);
+                uint8_t portnum = port->port;
+                pl->active_port_bitmask[portnum / 8] |= (1 << portnum % 8);
+            }
+        }
+        pl->active_port_bitmask[usp->port / 8] |= (1 << usp->port % 8);
+
+        s->len += sizeof(*pl);
+        out->rc = CXL_MBOX_SUCCESS;
+
+        return;
+    }
+
+    case CXL_FMAPI_GET_PHYSICAL_PORT_STATE:
+    {
+        size_t pl_size;
+        uint8_t num_ports = msg->payload[0];
+        int num_phys_ports = pcie_count_ds_ports(&PCI_BRIDGE(s->target)->sec_bus);
+        struct cxl_fmapi_get_phys_port_state_resp_pl *pl =
+            (struct cxl_fmapi_get_phys_port_state_resp_pl *)&s->scratch[s->pos];
+        int i;
+
+        /* TODO: Should match against particular ports requested... */
+        pl->num_ports = num_phys_ports;
+        for (i = 0; i < pl->num_ports; i++) {
+            struct cxl_fmapi_port_state_info_block *port;
+            port = &pl->ports[i];
+            port->port_id = i; /* TODO: Right port number */
+            if (port->port_id < 1) { /* 1 upstream ports */
+                port->config_state = 4;
+                port->connected_device_type = 0;
+            } else { /* remainder downstream ports */
+                port->config_state = 3;
+                port->connected_device_type = 4; /* TODO: Check. CXL type 3 */
+                port->supported_ld_count = 3;
+            }
+            port->connected_device_cxl_version = 2;
+            port->port_cxl_version_bitmask = 0x2;
+            port->max_link_width = 0x10; /* x16 */
+            port->negotiated_link_width = 0x10;
+            port->supported_link_speeds_vector = 0x1c; /* 8, 16, 32 GT/s */
+            port->max_link_speed = 5;
+            port->current_link_speed = 5; /* 32 */
+            port->ltssm_state = 0x7; /* L2 */
+            port->first_lane_num = 0;
+            port->link_state = 0;
+        }
+
+        pl_size = sizeof(pl) + sizeof(*pl->ports) * num_ports;
+
+        st24_le_p(out->pl_length, pl_size);
+        s->len += pl_size;
+        msg->rc = CXL_MBOX_SUCCESS;
+
+        return;
+    }
+
+    default:
+        msg->rc = CXL_MBOX_UNSUPPORTED;
+        return;
+    }
+}
+
+static Property i2c_mctp_cxl_props[] = {
+    DEFINE_PROP_LINK("target", I2C_MCTP_CXL_State,
+                     target, TYPE_PCI_DEVICE, PCIDevice *),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static size_t i2c_mctp_cxl_get_message_bytes(MCTPI2CEndpoint *mctp,
+                                             uint8_t *buf,
+                                             size_t maxlen,
+                                             uint8_t *mctp_flags)
+{
+    I2C_MCTP_CXL_State *s = I2C_MCTP_CXL(mctp);
+    size_t len;
+
+    len = MIN(maxlen, s->len - s->pos);
+
+    if (len == 0) {
+        return 0;
+    }
+
+    if (s->pos == 0) {
+        *mctp_flags |= MCTP_H_FLAGS_SOM;
+    }
+
+    memcpy(buf, s->scratch + s->pos, len);
+    s->pos += len;
+
+    if (s->pos == s->len) {
+        *mctp_flags |= MCTP_H_FLAGS_EOM;
+
+        s->pos = s->len = 0;
+    }
+
+    return len;
+}
+
+static int i2c_mctp_cxl_put_message_bytes(MCTPI2CEndpoint *mctp,
+                                          uint8_t *buf, size_t len)
+{
+    I2C_MCTP_CXL_State *s = I2C_MCTP_CXL(mctp);
+
+    if (s->len + len > MCTP_CXL_MAX_MSG_LEN) {
+        return -1;
+    }
+
+    memcpy(s->buffer + s->len, buf, len);
+    s->len += len;
+
+    return 0;
+}
+
+static size_t i2c_mctp_cxl_get_message_types(MCTPI2CEndpoint *mctp,
+                                             uint8_t *data,
+                                             size_t maxlen)
+{
+    uint8_t buf[] = {
+        0x0, 0x7, 0x8, /* Control, CXL FM-API and CXL CCI */
+    };
+
+    memcpy(data, buf, sizeof(buf));
+
+    return sizeof(buf);
+}
+
+static void i2c_mctp_cxl_reset_message(MCTPI2CEndpoint *mctp)
+{
+    I2C_MCTP_CXL_State *s = I2C_MCTP_CXL(mctp);
+
+    s->len = 0;
+}
+
+static void i2c_mctp_cxl_handle_message(MCTPI2CEndpoint *mctp)
+{
+    I2C_MCTP_CXL_State *s = I2C_MCTP_CXL(mctp);
+    CXLMCTPMessage *msg = (CXLMCTPMessage *)s->buffer;
+    CXLMCTPMessage buf = {
+        .message_type = msg->message_type,
+        .category = 1,
+        .tag = msg->tag,
+        .command = msg->command,
+        .command_set = msg->command_set,
+    };
+
+    memcpy(s->scratch, &buf, sizeof(buf));
+    s->pos = sizeof(buf);
+
+    switch (msg->message_type) {
+    case 0x7:
+        switch (msg->command_set)  {
+        case CXL_FMAPI_CMD_SET_PHYSICAL_SWITCH:
+            cxl_fmapi_cmd_set_physical_switch_parse(s, msg);
+            break;
+        }
+        break;
+    case 0x8:
+        switch (msg->command_set) {
+        case CXL_CCI_CMD_SET_INFOSTAT:
+            cxl_cci_cmd_set_info_and_status_parse(s, msg);
+            break;
+        }
+        break;
+    }
+    s->pos = 0;
+
+    i2c_mctp_schedule_send(mctp);
+}
+
+static void i2c_mctp_cxl_realize(DeviceState *d, Error **errp)
+{
+    I2C_MCTP_CXL_State *s = I2C_MCTP_CXL(d);
+
+    /* Check this is a type we support */
+    if (object_dynamic_cast(OBJECT(s->target), TYPE_CXL_USP)) {
+        s->type = cxl_switch;
+        return;
+    }
+
+    if (object_dynamic_cast(OBJECT(s->target), TYPE_CXL_TYPE3)) {
+        s->type = cxl_type3;
+        return;
+    }
+    error_setg(errp, "Unhandled target type for CXL MCTP EP");
+}
+
+static void i2c_mctp_cxl_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    MCTPI2CEndpointClass *mc = MCTP_I2C_ENDPOINT_CLASS(klass);
+
+    dc->realize = i2c_mctp_cxl_realize;
+    mc->get_message_types = i2c_mctp_cxl_get_message_types;
+    mc->get_message_bytes = i2c_mctp_cxl_get_message_bytes;
+    mc->put_message_bytes = i2c_mctp_cxl_put_message_bytes;
+
+    mc->handle_message = i2c_mctp_cxl_handle_message;
+    mc->reset_message = i2c_mctp_cxl_reset_message;
+    device_class_set_props(dc, i2c_mctp_cxl_props);
+}
+
+static const TypeInfo i2c_mctp_cxl_info = {
+    .name = TYPE_I2C_MCTP_CXL,
+    .parent = TYPE_MCTP_I2C_ENDPOINT,
+    .instance_size = sizeof(I2C_MCTP_CXL_State),
+    .class_init = 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/arm/Kconfig b/hw/arm/Kconfig
index 298134ce85..f0c6f91abe 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -32,7 +32,8 @@ config ARM_VIRT
     select VIRTIO_MEM_SUPPORTED
     select ACPI_CXL
     select ACPI_HMAT
-    select I2C_MCTP_CXL_FMAPI
+    select MCTP_I2C
+    select I2C_MCTP_CXL
 
 config CHEETAH
     bool
diff --git a/hw/cxl/Kconfig b/hw/cxl/Kconfig
index 8e67519b16..c9b2e46bac 100644
--- a/hw/cxl/Kconfig
+++ b/hw/cxl/Kconfig
@@ -1,3 +1,6 @@
 config CXL
     bool
     default y if PCI_EXPRESS
+
+config I2C_MCTP_CXL
+    bool
diff --git a/hw/cxl/meson.build b/hw/cxl/meson.build
index 99ee564ce8..5fba90e5b5 100644
--- a/hw/cxl/meson.build
+++ b/hw/cxl/meson.build
@@ -12,5 +12,6 @@ softmmu_ss.add(when: 'CONFIG_CXL',
                if_false: files(
                    'cxl-host-stubs.c',
                ))
+softmmu_ss.add(when: 'CONFIG_I2C_MCTP_CXL', if_true: files('i2c_mctp_cxl.c'))
 
 softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('cxl-host-stubs.c'))
diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig
index 31e1958368..9644e81254 100644
--- a/hw/i386/Kconfig
+++ b/hw/i386/Kconfig
@@ -45,7 +45,8 @@ config PC
     select ACPI_VMGENID
     select VIRTIO_PMEM_SUPPORTED
     select VIRTIO_MEM_SUPPORTED
-    select I2C_MCTP_CXL_FMAPI
+    select MCTP_I2C
+    select I2C_MCTP_CXL
 
 config PC_PCI
     bool
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index a40245ad44..ce5891716b 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -128,6 +128,7 @@ softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_rng.c'))
 
 softmmu_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_ahb_apb_pnp.c'))
 
+
 softmmu_ss.add(when: 'CONFIG_I2C', if_true: files('i2c-echo.c'))
 
 specific_ss.add(when: 'CONFIG_AVR_POWER', if_true: files('avr_power.c'))
-- 
2.39.2
[RFC PATCH 6/6] docs: cxl: Add example commandline for MCTP CXL CCIs
Posted by Jonathan Cameron via 11 months ago
A lot more needed here on what these are for and what can be done
with them.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 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 f12011e230..5374b44f43 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
 
+
+CCI access via MCTP over I2C
+----------------------------
+
+TODO: Add some more info here on what this actually is.
+
+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=33,bus=root_port0,id=us0,multifunction=on,addr=0.0,sn=12345678 \
+ -device cxl-downstream,port=0,bus=us0,id=swport0,chassis=0,slot=4 \
+ -device cxl-downstream,port=1,bus=us0,id=swport1,chassis=0,slot=5 \
+ -device cxl-downstream,port=2,bus=us0,id=swport2,chassis=0,slot=6 \
+ -device cxl-type3,bus=swport0,persistent-memdev=cxl-mem1,id=cxl-pmem0,lsa=cxl-lsa1,sn=3 \
+ -device cxl-type3,bus=swport1,persistent-memdev=cxl-mem2,id=cxl-pmem1,lsa=cxl-lsa2,sn=4 \
+ -device cxl-type3,bus=swport2,persistent-memdev=cxl-mem3,id=cxl-pmem2,lsa=cxl-lsa3,sn=5 \
+ -machine cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=1k \
+ -device i2c_mctp_cxl,bus=aspeed.i2c.bus.0,address=4,target=us0 \
+ -device i2c_mctp_cxl,bus=aspeed.i2c.bus.0,address=5,target=cxl-pmem0 \
+ -device i2c_mctp_cxl,bus=aspeed.i2c.bus.0,address=6,target=cxl-pmem1 \
+ -device i2c_mctp_cxl,bus=aspeed.i2c.bus.0,address=7,target=cxl-pmem2
+
+Communication with the MCTP CCI can then be established using standard MCTP configuration
+tools.
+
 References
 ----------
 
-- 
2.39.2