diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/acpi/tables.c | 2 | ||||
| -rw-r--r-- | drivers/cxl/core/cdat.c | 6 | ||||
| -rw-r--r-- | drivers/cxl/core/pci.c | 99 | ||||
| -rw-r--r-- | drivers/cxl/cxlpci.h | 24 | 
4 files changed, 83 insertions, 48 deletions
| diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index b07f7d091d13..b976e5fc3fbc 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -253,7 +253,7 @@ int __init_or_acpilib acpi_table_parse_entries_array(  	count = acpi_parse_entries_array(id, table_size,  					 (union fw_table_header *)table_header, -					 proc, proc_num, max_entries); +					 0, proc, proc_num, max_entries);  	acpi_put_table(table_header);  	return count; diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c index 10d4f1d7dad1..e8c066293b31 100644 --- a/drivers/cxl/core/cdat.c +++ b/drivers/cxl/core/cdat.c @@ -150,13 +150,13 @@ static int cxl_cdat_endpoint_process(struct cxl_port *port,  	int rc;  	rc = cdat_table_parse(ACPI_CDAT_TYPE_DSMAS, cdat_dsmas_handler, -			      dsmas_xa, port->cdat.table); +			      dsmas_xa, port->cdat.table, port->cdat.length);  	rc = cdat_table_parse_output(rc);  	if (rc)  		return rc;  	rc = cdat_table_parse(ACPI_CDAT_TYPE_DSLBIS, cdat_dslbis_handler, -			      dsmas_xa, port->cdat.table); +			      dsmas_xa, port->cdat.table, port->cdat.length);  	return cdat_table_parse_output(rc);  } @@ -486,7 +486,7 @@ void cxl_switch_parse_cdat(struct cxl_port *port)  		return;  	rc = cdat_table_parse(ACPI_CDAT_TYPE_SSLBIS, cdat_sslbis_handler, -			      port, port->cdat.table); +			      port, port->cdat.table, port->cdat.length);  	rc = cdat_table_parse_output(rc);  	if (rc)  		dev_dbg(&port->dev, "Failed to parse SSLBIS: %d\n", rc); diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c index e9e6c81ce034..0df09bd79408 100644 --- a/drivers/cxl/core/pci.c +++ b/drivers/cxl/core/pci.c @@ -518,14 +518,14 @@ EXPORT_SYMBOL_NS_GPL(cxl_hdm_decode_init, CXL);  	 FIELD_PREP(CXL_DOE_TABLE_ACCESS_ENTRY_HANDLE, (entry_handle)))  static int cxl_cdat_get_length(struct device *dev, -			       struct pci_doe_mb *cdat_doe, +			       struct pci_doe_mb *doe_mb,  			       size_t *length)  {  	__le32 request = CDAT_DOE_REQ(0);  	__le32 response[2];  	int rc; -	rc = pci_doe(cdat_doe, PCI_DVSEC_VENDOR_ID_CXL, +	rc = pci_doe(doe_mb, PCI_DVSEC_VENDOR_ID_CXL,  		     CXL_DOE_PROTOCOL_TABLE_ACCESS,  		     &request, sizeof(request),  		     &response, sizeof(response)); @@ -543,56 +543,58 @@ static int cxl_cdat_get_length(struct device *dev,  }  static int cxl_cdat_read_table(struct device *dev, -			       struct pci_doe_mb *cdat_doe, -			       void *cdat_table, size_t *cdat_length) +			       struct pci_doe_mb *doe_mb, +			       struct cdat_doe_rsp *rsp, size_t *length)  { -	size_t length = *cdat_length + sizeof(__le32); -	__le32 *data = cdat_table; -	int entry_handle = 0; +	size_t received, remaining = *length; +	unsigned int entry_handle = 0; +	union cdat_data *data;  	__le32 saved_dw = 0;  	do {  		__le32 request = CDAT_DOE_REQ(entry_handle); -		struct cdat_entry_header *entry; -		size_t entry_dw;  		int rc; -		rc = pci_doe(cdat_doe, PCI_DVSEC_VENDOR_ID_CXL, +		rc = pci_doe(doe_mb, PCI_DVSEC_VENDOR_ID_CXL,  			     CXL_DOE_PROTOCOL_TABLE_ACCESS,  			     &request, sizeof(request), -			     data, length); +			     rsp, sizeof(*rsp) + remaining);  		if (rc < 0) {  			dev_err(dev, "DOE failed: %d", rc);  			return rc;  		} -		/* 1 DW Table Access Response Header + CDAT entry */ -		entry = (struct cdat_entry_header *)(data + 1); -		if ((entry_handle == 0 && -		     rc != sizeof(__le32) + sizeof(struct cdat_header)) || -		    (entry_handle > 0 && -		     (rc < sizeof(__le32) + sizeof(*entry) || -		      rc != sizeof(__le32) + le16_to_cpu(entry->length)))) +		if (rc < sizeof(*rsp))  			return -EIO; +		data = (union cdat_data *)rsp->data; +		received = rc - sizeof(*rsp); + +		if (entry_handle == 0) { +			if (received != sizeof(data->header)) +				return -EIO; +		} else { +			if (received < sizeof(data->entry) || +			    received != le16_to_cpu(data->entry.length)) +				return -EIO; +		} +  		/* Get the CXL table access header entry handle */  		entry_handle = FIELD_GET(CXL_DOE_TABLE_ACCESS_ENTRY_HANDLE, -					 le32_to_cpu(data[0])); -		entry_dw = rc / sizeof(__le32); -		/* Skip Header */ -		entry_dw -= 1; +					 le32_to_cpu(rsp->doe_header)); +  		/*  		 * Table Access Response Header overwrote the last DW of  		 * previous entry, so restore that DW  		 */ -		*data = saved_dw; -		length -= entry_dw * sizeof(__le32); -		data += entry_dw; -		saved_dw = *data; +		rsp->doe_header = saved_dw; +		remaining -= received; +		rsp = (void *)rsp + received; +		saved_dw = rsp->doe_header;  	} while (entry_handle != CXL_DOE_TABLE_ACCESS_LAST_ENTRY);  	/* Length in CDAT header may exceed concatenation of CDAT entries */ -	*cdat_length -= length - sizeof(__le32); +	*length -= remaining;  	return 0;  } @@ -617,11 +619,11 @@ void read_cdat_data(struct cxl_port *port)  {  	struct device *uport = port->uport_dev;  	struct device *dev = &port->dev; -	struct pci_doe_mb *cdat_doe; +	struct pci_doe_mb *doe_mb;  	struct pci_dev *pdev = NULL;  	struct cxl_memdev *cxlmd; -	size_t cdat_length; -	void *cdat_table, *cdat_buf; +	struct cdat_doe_rsp *buf; +	size_t table_length, length;  	int rc;  	if (is_cxl_memdev(uport)) { @@ -638,39 +640,48 @@ void read_cdat_data(struct cxl_port *port)  	if (!pdev)  		return; -	cdat_doe = pci_find_doe_mailbox(pdev, PCI_DVSEC_VENDOR_ID_CXL, -					CXL_DOE_PROTOCOL_TABLE_ACCESS); -	if (!cdat_doe) { +	doe_mb = pci_find_doe_mailbox(pdev, PCI_DVSEC_VENDOR_ID_CXL, +				      CXL_DOE_PROTOCOL_TABLE_ACCESS); +	if (!doe_mb) {  		dev_dbg(dev, "No CDAT mailbox\n");  		return;  	}  	port->cdat_available = true; -	if (cxl_cdat_get_length(dev, cdat_doe, &cdat_length)) { +	if (cxl_cdat_get_length(dev, doe_mb, &length)) {  		dev_dbg(dev, "No CDAT length\n");  		return;  	} -	cdat_buf = devm_kzalloc(dev, cdat_length + sizeof(__le32), GFP_KERNEL); -	if (!cdat_buf) -		return; +	/* +	 * The begin of the CDAT buffer needs space for additional 4 +	 * bytes for the DOE header. Table data starts afterwards. +	 */ +	buf = devm_kzalloc(dev, sizeof(*buf) + length, GFP_KERNEL); +	if (!buf) +		goto err; + +	table_length = length; -	rc = cxl_cdat_read_table(dev, cdat_doe, cdat_buf, &cdat_length); +	rc = cxl_cdat_read_table(dev, doe_mb, buf, &length);  	if (rc)  		goto err; -	cdat_table = cdat_buf + sizeof(__le32); -	if (cdat_checksum(cdat_table, cdat_length)) +	if (table_length != length) +		dev_warn(dev, "Malformed CDAT table length (%zu:%zu), discarding trailing data\n", +			table_length, length); + +	if (cdat_checksum(buf->data, length))  		goto err; -	port->cdat.table = cdat_table; -	port->cdat.length = cdat_length; -	return; +	port->cdat.table = buf->data; +	port->cdat.length = length; +	return;  err:  	/* Don't leave table data allocated on error */ -	devm_kfree(dev, cdat_buf); +	devm_kfree(dev, buf);  	dev_err(dev, "Failed to read/validate CDAT.\n");  }  EXPORT_SYMBOL_NS_GPL(read_cdat_data, CXL); diff --git a/drivers/cxl/cxlpci.h b/drivers/cxl/cxlpci.h index 711b05d9a370..93992a1c8eec 100644 --- a/drivers/cxl/cxlpci.h +++ b/drivers/cxl/cxlpci.h @@ -71,6 +71,15 @@ enum cxl_regloc_type {  	CXL_REGLOC_RBI_TYPES  }; +/* + * Table Access DOE, CDAT Read Entry Response + * + * Spec refs: + * + * CXL 3.1 8.1.11, Table 8-14: Read Entry Response + * CDAT Specification 1.03: 2 CDAT Data Structures + */ +  struct cdat_header {  	__le32 length;  	u8 revision; @@ -86,6 +95,21 @@ struct cdat_entry_header {  } __packed;  /* + * The DOE CDAT read response contains a CDAT read entry (either the + * CDAT header or a structure). + */ +union cdat_data { +	struct cdat_header header; +	struct cdat_entry_header entry; +} __packed; + +/* There is an additional CDAT response header of 4 bytes. */ +struct cdat_doe_rsp { +	__le32 doe_header; +	u8 data[]; +} __packed; + +/*   * CXL v3.0 6.2.3 Table 6-4   * The table indicates that if PCIe Flit Mode is set, then CXL is in 256B flits   * mode, otherwise it's 68B flits mode. |