summaryrefslogtreecommitdiff
path: root/drivers/cxl
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2022-01-25 21:24:09 -0800
committerDan Williams <dan.j.williams@intel.com>2022-02-08 23:15:10 -0800
commit7004cc9d1585fb7fc9ec7e3e92b70b65e13b11fd (patch)
treeb42cecc5e81e7c4bf4ca760ae0fea49b944c11d2 /drivers/cxl
parent0909b4e5287bcda34f00da878ac1f37a0921d959 (diff)
cxl/core/port: Handle invalid decoders
In case init_hdm_decoder() finds invalid settings, skip to the next valid decoder. Only fail port enumeration if zero valid decoders are found. This protects the driver init against broken hardware and / or future interleave capabilities. Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Link: https://lore.kernel.org/r/164317464918.3438644.12371149695618136198.stgit@dwillia2-desk3.amr.corp.intel.com Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/cxl')
-rw-r--r--drivers/cxl/core/hdm.c36
1 files changed, 30 insertions, 6 deletions
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 05b0b292e72d..0e89a7a932d4 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -149,8 +149,8 @@ static int to_interleave_ways(u32 ctrl)
}
}
-static void init_hdm_decoder(struct cxl_decoder *cxld, int *target_map,
- void __iomem *hdm, int which)
+static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
+ int *target_map, void __iomem *hdm, int which)
{
u64 size, base;
u32 ctrl;
@@ -166,6 +166,11 @@ static void init_hdm_decoder(struct cxl_decoder *cxld, int *target_map,
if (!(ctrl & CXL_HDM_DECODER0_CTRL_COMMITTED))
size = 0;
+ if (base == U64_MAX || size == U64_MAX) {
+ dev_warn(&port->dev, "decoder%d.%d: Invalid resource range\n",
+ port->id, cxld->id);
+ return -ENXIO;
+ }
cxld->decoder_range = (struct range) {
.start = base,
@@ -179,6 +184,12 @@ static void init_hdm_decoder(struct cxl_decoder *cxld, int *target_map,
cxld->flags |= CXL_DECODER_F_LOCK;
}
cxld->interleave_ways = to_interleave_ways(ctrl);
+ if (!cxld->interleave_ways) {
+ dev_warn(&port->dev,
+ "decoder%d.%d: Invalid interleave ways (ctrl: %#x)\n",
+ port->id, cxld->id, ctrl);
+ return -ENXIO;
+ }
cxld->interleave_granularity = to_interleave_granularity(ctrl);
if (FIELD_GET(CXL_HDM_DECODER0_CTRL_TYPE, ctrl))
@@ -187,12 +198,14 @@ static void init_hdm_decoder(struct cxl_decoder *cxld, int *target_map,
cxld->target_type = CXL_DECODER_ACCELERATOR;
if (is_cxl_endpoint(to_cxl_port(cxld->dev.parent)))
- return;
+ return 0;
target_list.value =
ioread64_hi_lo(hdm + CXL_HDM_DECODER0_TL_LOW(which));
for (i = 0; i < cxld->interleave_ways; i++)
target_map[i] = target_list.target_id[i];
+
+ return 0;
}
/**
@@ -203,7 +216,7 @@ int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm)
{
void __iomem *hdm = cxlhdm->regs.hdm_decoder;
struct cxl_port *port = cxlhdm->port;
- int i, committed;
+ int i, committed, failed;
u32 ctrl;
/*
@@ -223,7 +236,7 @@ int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm)
if (committed != cxlhdm->decoder_count)
msleep(20);
- for (i = 0; i < cxlhdm->decoder_count; i++) {
+ for (i = 0, failed = 0; i < cxlhdm->decoder_count; i++) {
int target_map[CXL_DECODER_MAX_INTERLEAVE] = { 0 };
int rc, target_count = cxlhdm->target_count;
struct cxl_decoder *cxld;
@@ -238,7 +251,13 @@ int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm)
return PTR_ERR(cxld);
}
- init_hdm_decoder(cxld, target_map, cxlhdm->regs.hdm_decoder, i);
+ rc = init_hdm_decoder(port, cxld, target_map,
+ cxlhdm->regs.hdm_decoder, i);
+ if (rc) {
+ put_device(&cxld->dev);
+ failed++;
+ continue;
+ }
rc = add_hdm_decoder(port, cxld, target_map);
if (rc) {
dev_warn(&port->dev,
@@ -247,6 +266,11 @@ int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm)
}
}
+ if (failed == cxlhdm->decoder_count) {
+ dev_err(&port->dev, "No valid decoders found\n");
+ return -ENXIO;
+ }
+
return 0;
}
EXPORT_SYMBOL_NS_GPL(devm_cxl_enumerate_decoders, CXL);