// SPDX-License-Identifier: GPL-2.0-only /* Copyright(c) 2022 Intel Corporation. All rights reserved. */ #include #include "core.h" #define CREATE_TRACE_POINTS #include "trace.h" static bool cxl_is_hpa_in_range(u64 hpa, struct cxl_region *cxlr, int pos) { struct cxl_region_params *p = &cxlr->params; int gran = p->interleave_granularity; int ways = p->interleave_ways; u64 offset; /* Is the hpa within this region at all */ if (hpa < p->res->start || hpa > p->res->end) { dev_dbg(&cxlr->dev, "Addr trans fail: hpa 0x%llx not in region\n", hpa); return false; } /* Is the hpa in an expected chunk for its pos(-ition) */ offset = hpa - p->res->start; offset = do_div(offset, gran * ways); if ((offset >= pos * gran) && (offset < (pos + 1) * gran)) return true; dev_dbg(&cxlr->dev, "Addr trans fail: hpa 0x%llx not in expected chunk\n", hpa); return false; } static u64 cxl_dpa_to_hpa(u64 dpa, struct cxl_region *cxlr, struct cxl_endpoint_decoder *cxled) { u64 dpa_offset, hpa_offset, bits_upper, mask_upper, hpa; struct cxl_region_params *p = &cxlr->params; int pos = cxled->pos; u16 eig = 0; u8 eiw = 0; ways_to_eiw(p->interleave_ways, &eiw); granularity_to_eig(p->interleave_granularity, &eig); /* * The device position in the region interleave set was removed * from the offset at HPA->DPA translation. To reconstruct the * HPA, place the 'pos' in the offset. * * The placement of 'pos' in the HPA is determined by interleave * ways and granularity and is defined in the CXL Spec 3.0 Section * 8.2.4.19.13 Implementation Note: Device Decode Logic */ /* Remove the dpa base */ dpa_offset = dpa - cxl_dpa_resource_start(cxled); mask_upper = GENMASK_ULL(51, eig + 8); if (eiw < 8) { hpa_offset = (dpa_offset & mask_upper) << eiw; hpa_offset |= pos << (eig + 8); } else { bits_upper = (dpa_offset & mask_upper) >> (eig + 8); bits_upper = bits_upper * 3; hpa_offset = ((bits_upper << (eiw - 8)) + pos) << (eig + 8); } /* The lower bits remain unchanged */ hpa_offset |= dpa_offset & GENMASK_ULL(eig + 7, 0); /* Apply the hpa_offset to the region base address */ hpa = hpa_offset + p->res->start; if (!cxl_is_hpa_in_range(hpa, cxlr, cxled->pos)) return ULLONG_MAX; return hpa; } u64 cxl_trace_hpa(struct cxl_region *cxlr, struct cxl_memdev *cxlmd, u64 dpa) { struct cxl_region_params *p = &cxlr->params; struct cxl_endpoint_decoder *cxled = NULL; for (int i = 0; i < p->nr_targets; i++) { cxled = p->targets[i]; if (cxlmd == cxled_to_memdev(cxled)) break; } if (!cxled || cxlmd != cxled_to_memdev(cxled)) return ULLONG_MAX; return cxl_dpa_to_hpa(dpa, cxlr, cxled); }