Skip to content

Commit e0c818e

Browse files
Robert Richterdjbw
authored andcommitted
cxl/pci: Get rid of pointer arithmetic reading CDAT table
Reading the CDAT table using DOE requires a Table Access Response Header in addition to the CDAT entry. In current implementation this has caused offsets with sizeof(__le32) to the actual buffers. This led to hardly readable code and even bugs. E.g., see fix of devm_kfree() in read_cdat_data(): commit c65efe3 ("cxl/cdat: Free correct buffer on checksum error") Rework code to avoid calculations with sizeof(__le32). Introduce struct cdat_doe_rsp for this which contains the Table Access Response Header and a variable payload size for various data structures afterwards to access the CDAT table and its CDAT Data Structures without recalculating buffer offsets. Cc: Lukas Wunner <lukas@wunner.de> Cc: Fan Ni <nifan.cxl@gmail.com> Reviewed-by: Dave Jiang <dave.jiang@intel.com> Signed-off-by: Robert Richter <rrichter@amd.com> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Link: https://lore.kernel.org/r/20240216155844.406996-3-rrichter@amd.com Signed-off-by: Dan Williams <dan.j.williams@intel.com>
1 parent ec8ffff commit e0c818e

File tree

2 files changed

+65
-36
lines changed

2 files changed

+65
-36
lines changed

drivers/cxl/core/pci.c

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -544,55 +544,57 @@ static int cxl_cdat_get_length(struct device *dev,
544544

545545
static int cxl_cdat_read_table(struct device *dev,
546546
struct pci_doe_mb *doe_mb,
547-
void *cdat_table, size_t *cdat_length)
547+
struct cdat_doe_rsp *rsp, size_t *length)
548548
{
549-
size_t length = *cdat_length + sizeof(__le32);
550-
__le32 *data = cdat_table;
551-
int entry_handle = 0;
549+
size_t received, remaining = *length;
550+
unsigned int entry_handle = 0;
551+
union cdat_data *data;
552552
__le32 saved_dw = 0;
553553

554554
do {
555555
__le32 request = CDAT_DOE_REQ(entry_handle);
556-
struct cdat_entry_header *entry;
557-
size_t entry_dw;
558556
int rc;
559557

560558
rc = pci_doe(doe_mb, PCI_DVSEC_VENDOR_ID_CXL,
561559
CXL_DOE_PROTOCOL_TABLE_ACCESS,
562560
&request, sizeof(request),
563-
data, length);
561+
rsp, sizeof(*rsp) + remaining);
564562
if (rc < 0) {
565563
dev_err(dev, "DOE failed: %d", rc);
566564
return rc;
567565
}
568566

569-
/* 1 DW Table Access Response Header + CDAT entry */
570-
entry = (struct cdat_entry_header *)(data + 1);
571-
if ((entry_handle == 0 &&
572-
rc != sizeof(__le32) + sizeof(struct cdat_header)) ||
573-
(entry_handle > 0 &&
574-
(rc < sizeof(__le32) + sizeof(*entry) ||
575-
rc != sizeof(__le32) + le16_to_cpu(entry->length))))
567+
if (rc < sizeof(*rsp))
576568
return -EIO;
577569

570+
data = (union cdat_data *)rsp->data;
571+
received = rc - sizeof(*rsp);
572+
573+
if (entry_handle == 0) {
574+
if (received != sizeof(data->header))
575+
return -EIO;
576+
} else {
577+
if (received < sizeof(data->entry) ||
578+
received != le16_to_cpu(data->entry.length))
579+
return -EIO;
580+
}
581+
578582
/* Get the CXL table access header entry handle */
579583
entry_handle = FIELD_GET(CXL_DOE_TABLE_ACCESS_ENTRY_HANDLE,
580-
le32_to_cpu(data[0]));
581-
entry_dw = rc / sizeof(__le32);
582-
/* Skip Header */
583-
entry_dw -= 1;
584+
le32_to_cpu(rsp->doe_header));
585+
584586
/*
585587
* Table Access Response Header overwrote the last DW of
586588
* previous entry, so restore that DW
587589
*/
588-
*data = saved_dw;
589-
length -= entry_dw * sizeof(__le32);
590-
data += entry_dw;
591-
saved_dw = *data;
590+
rsp->doe_header = saved_dw;
591+
remaining -= received;
592+
rsp = (void *)rsp + received;
593+
saved_dw = rsp->doe_header;
592594
} while (entry_handle != CXL_DOE_TABLE_ACCESS_LAST_ENTRY);
593595

594596
/* Length in CDAT header may exceed concatenation of CDAT entries */
595-
*cdat_length -= length - sizeof(__le32);
597+
*length -= remaining;
596598

597599
return 0;
598600
}
@@ -620,8 +622,8 @@ void read_cdat_data(struct cxl_port *port)
620622
struct pci_doe_mb *doe_mb;
621623
struct pci_dev *pdev = NULL;
622624
struct cxl_memdev *cxlmd;
623-
size_t cdat_length;
624-
void *cdat_table, *cdat_buf;
625+
struct cdat_doe_rsp *buf;
626+
size_t length;
625627
int rc;
626628

627629
if (is_cxl_memdev(uport)) {
@@ -647,30 +649,33 @@ void read_cdat_data(struct cxl_port *port)
647649

648650
port->cdat_available = true;
649651

650-
if (cxl_cdat_get_length(dev, doe_mb, &cdat_length)) {
652+
if (cxl_cdat_get_length(dev, doe_mb, &length)) {
651653
dev_dbg(dev, "No CDAT length\n");
652654
return;
653655
}
654656

655-
cdat_buf = devm_kzalloc(dev, cdat_length + sizeof(__le32), GFP_KERNEL);
656-
if (!cdat_buf)
657-
return;
657+
/*
658+
* The begin of the CDAT buffer needs space for additional 4
659+
* bytes for the DOE header. Table data starts afterwards.
660+
*/
661+
buf = devm_kzalloc(dev, sizeof(*buf) + length, GFP_KERNEL);
662+
if (!buf)
663+
goto err;
658664

659-
rc = cxl_cdat_read_table(dev, doe_mb, cdat_buf, &cdat_length);
665+
rc = cxl_cdat_read_table(dev, doe_mb, buf, &length);
660666
if (rc)
661667
goto err;
662668

663-
cdat_table = cdat_buf + sizeof(__le32);
664-
if (cdat_checksum(cdat_table, cdat_length))
669+
if (cdat_checksum(buf->data, length))
665670
goto err;
666671

667-
port->cdat.table = cdat_table;
668-
port->cdat.length = cdat_length;
669-
return;
672+
port->cdat.table = buf->data;
673+
port->cdat.length = length;
670674

675+
return;
671676
err:
672677
/* Don't leave table data allocated on error */
673-
devm_kfree(dev, cdat_buf);
678+
devm_kfree(dev, buf);
674679
dev_err(dev, "Failed to read/validate CDAT.\n");
675680
}
676681
EXPORT_SYMBOL_NS_GPL(read_cdat_data, CXL);

drivers/cxl/cxlpci.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ enum cxl_regloc_type {
7171
CXL_REGLOC_RBI_TYPES
7272
};
7373

74+
/*
75+
* Table Access DOE, CDAT Read Entry Response
76+
*
77+
* Spec refs:
78+
*
79+
* CXL 3.1 8.1.11, Table 8-14: Read Entry Response
80+
* CDAT Specification 1.03: 2 CDAT Data Structures
81+
*/
82+
7483
struct cdat_header {
7584
__le32 length;
7685
u8 revision;
@@ -85,6 +94,21 @@ struct cdat_entry_header {
8594
__le16 length;
8695
} __packed;
8796

97+
/*
98+
* The DOE CDAT read response contains a CDAT read entry (either the
99+
* CDAT header or a structure).
100+
*/
101+
union cdat_data {
102+
struct cdat_header header;
103+
struct cdat_entry_header entry;
104+
} __packed;
105+
106+
/* There is an additional CDAT response header of 4 bytes. */
107+
struct cdat_doe_rsp {
108+
__le32 doe_header;
109+
u8 data[];
110+
} __packed;
111+
88112
/*
89113
* CXL v3.0 6.2.3 Table 6-4
90114
* The table indicates that if PCIe Flit Mode is set, then CXL is in 256B flits

0 commit comments

Comments
 (0)