From nobody Tue Dec 16 05:49:25 2025 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.10]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7240020C037; Tue, 14 Jan 2025 20:31:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.10 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736886714; cv=none; b=o2HRbjOjLd9j+gD4N0ZTXUOeQ7rKNbo/ewLL7uia3KeS6YIKCUnp4qgp+YoTKHup4pgDoJMkLd6q6SPu1BiyhifDyYNWMWYrjU1vgFDzZ254V8tNqdjE8BZSKxYdRApxR/SOUerch7ZCBZNhk3uGjuSYClYLVIV+jZNmV7cSuvk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736886714; c=relaxed/simple; bh=aCrISHIL2GN3gkdhdf+hSyNqCqRx4Ax4D3iKtqhp8gY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GQKh50Yvor2MkngUknLgvuk0gTvepKDRmrMwTGhNIkZ5JrZWYpSWSUjRIo9n5w3/3bWSUqaBblemG/jdtsA663DeIr/ugd6EEVlXXu02p5L9vLZW7wV1iuPziZ3YkPreZpwHqM1dok1XhaF4nQWQQtLG8l/J/iJL6XFOBDy2A80= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=none smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=UdlD6kth; arc=none smtp.client-ip=198.175.65.10 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="UdlD6kth" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1736886713; x=1768422713; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=aCrISHIL2GN3gkdhdf+hSyNqCqRx4Ax4D3iKtqhp8gY=; b=UdlD6kthx23BIb2PyxMkyh7IrrQnTByUX9jL6McMtfoX0VGjkpibne2j FTLNO5yKTKvsCvbLogCLpnE+JjPTB2x3WIkAEX6342CJoFUmvDZEnXqz9 N+k0AlaLDBYNKfcWKZRJiYOlO/aeSedtYAiYA49EWzgcd+Tms5xiJ1Vs3 A6hsSsbsPvaTrPbKlC7DNf+H9OYfzXvInX+u1nD3ky3M+PACgsbsyoQeb NuaHSe0IMHUEmY5u7XaJi7i+oltQTyStaWeHbe1SZqsyoKt2x0rMsWY/9 U2+zL6cDdEnEje8XUeDvh5Qi5Ho3I8fPKzdO6mGuez1Wewzc+3Pj3mMKC g==; X-CSE-ConnectionGUID: hDQP5+9aQxOCo4XTBuFmcA== X-CSE-MsgGUID: GaJzh8uXRwG0HJZPEZzqdQ== X-IronPort-AV: E=McAfee;i="6700,10204,11315"; a="54621474" X-IronPort-AV: E=Sophos;i="6.12,315,1728975600"; d="scan'208";a="54621474" Received: from orviesa001.jf.intel.com ([10.64.159.141]) by orvoesa102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 14 Jan 2025 12:31:53 -0800 X-CSE-ConnectionGUID: dZ9uMk2hQJGbEVQ+PhjfVw== X-CSE-MsgGUID: OZ6ULfxOQIyhWaNwmlp37w== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,224,1728975600"; d="scan'208";a="142184191" Received: from fdefranc-mobl3.ger.corp.intel.com (HELO fdefranc-mobl3.intel.com) ([10.245.244.88]) by smtpauth.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 14 Jan 2025 12:31:48 -0800 From: "Fabio M. De Francesco" To: Davidlohr Bueso , Jonathan Cameron , Dave Jiang , Alison Schofield , Vishal Verma , Ira Weiny , Dan Williams Cc: Robert Richter , ming.li@zohomail.com, linux-kernel@vger.kernel.org, linux-cxl@vger.kernel.org, "Fabio M. De Francesco" Subject: [PATCH 1/4] cxl/core: Change match_*_by_range() calling convention Date: Tue, 14 Jan 2025 21:31:01 +0100 Message-ID: <20250114203136.31704-2-fabio.m.de.francesco@linux.intel.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250114203136.31704-1-fabio.m.de.francesco@linux.intel.com> References: <20250114203136.31704-1-fabio.m.de.francesco@linux.intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Replace struct range parameter with struct cxl_endpoint_decoder of which range is a member in the match_*_by_range() functions. This is in preparation for expanding these helpers to perform arch specific region matching that requires a cxl_endpoint_decoder. No functional changes. Cc: Alison Schofield Cc: Dan Williams Cc: Ira Weiny Reviewed-by: Alison Schofield Reviewed-by: Ira Weiny Signed-off-by: Fabio M. De Francesco --- drivers/cxl/core/region.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index b98b1ccffd1ca..9d2c31f5caf26 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -1735,23 +1735,27 @@ static struct cxl_port *next_port(struct cxl_port *= port) =20 static int match_switch_decoder_by_range(struct device *dev, void *data) { + struct cxl_endpoint_decoder *cxled =3D data; struct cxl_switch_decoder *cxlsd; - struct range *r1, *r2 =3D data; + struct range *r1, *r2; =20 if (!is_switch_decoder(dev)) return 0; =20 cxlsd =3D to_cxl_switch_decoder(dev); r1 =3D &cxlsd->cxld.hpa_range; + r2 =3D &cxled->cxld.hpa_range; =20 if (is_root_decoder(dev)) return range_contains(r1, r2); return (r1->start =3D=3D r2->start && r1->end =3D=3D r2->end); } =20 -static int find_pos_and_ways(struct cxl_port *port, struct range *range, +static int find_pos_and_ways(struct cxl_port *port, + struct cxl_endpoint_decoder *cxled, int *pos, int *ways) { + struct range *range =3D &cxled->cxld.hpa_range; struct cxl_switch_decoder *cxlsd; struct cxl_port *parent; struct device *dev; @@ -1761,7 +1765,7 @@ static int find_pos_and_ways(struct cxl_port *port, s= truct range *range, if (!parent) return rc; =20 - dev =3D device_find_child(&parent->dev, range, + dev =3D device_find_child(&parent->dev, cxled, match_switch_decoder_by_range); if (!dev) { dev_err(port->uport_dev, @@ -1841,7 +1845,7 @@ static int cxl_calc_interleave_pos(struct cxl_endpoin= t_decoder *cxled) if (is_cxl_root(iter)) break; =20 - rc =3D find_pos_and_ways(iter, range, &parent_pos, &parent_ways); + rc =3D find_pos_and_ways(iter, cxled, &parent_pos, &parent_ways); if (rc) return rc; =20 @@ -3189,22 +3193,26 @@ static int devm_cxl_add_dax_region(struct cxl_regio= n *cxlr) =20 static int match_root_decoder_by_range(struct device *dev, void *data) { - struct range *r1, *r2 =3D data; + struct cxl_endpoint_decoder *cxled =3D data; struct cxl_root_decoder *cxlrd; + struct range *r1, *r2; =20 if (!is_root_decoder(dev)) return 0; =20 cxlrd =3D to_cxl_root_decoder(dev); r1 =3D &cxlrd->cxlsd.cxld.hpa_range; + r2 =3D &cxled->cxld.hpa_range; + return range_contains(r1, r2); } =20 static int match_region_by_range(struct device *dev, void *data) { + struct cxl_endpoint_decoder *cxled =3D data; + struct range *r =3D &cxled->cxld.hpa_range; struct cxl_region_params *p; struct cxl_region *cxlr; - struct range *r =3D data; int rc =3D 0; =20 if (!is_cxl_region(dev)) @@ -3308,7 +3316,6 @@ static struct cxl_region *construct_region(struct cxl= _root_decoder *cxlrd, int cxl_add_to_region(struct cxl_port *root, struct cxl_endpoint_decoder *= cxled) { struct cxl_memdev *cxlmd =3D cxled_to_memdev(cxled); - struct range *hpa =3D &cxled->cxld.hpa_range; struct cxl_decoder *cxld =3D &cxled->cxld; struct device *cxlrd_dev, *region_dev; struct cxl_root_decoder *cxlrd; @@ -3317,7 +3324,7 @@ int cxl_add_to_region(struct cxl_port *root, struct c= xl_endpoint_decoder *cxled) bool attach =3D false; int rc; =20 - cxlrd_dev =3D device_find_child(&root->dev, &cxld->hpa_range, + cxlrd_dev =3D device_find_child(&root->dev, cxled, match_root_decoder_by_range); if (!cxlrd_dev) { dev_err(cxlmd->dev.parent, @@ -3334,7 +3341,7 @@ int cxl_add_to_region(struct cxl_port *root, struct c= xl_endpoint_decoder *cxled) * one does the construction and the others add to that. */ mutex_lock(&cxlrd->range_lock); - region_dev =3D device_find_child(&cxlrd->cxlsd.cxld.dev, hpa, + region_dev =3D device_find_child(&cxlrd->cxlsd.cxld.dev, cxled, match_region_by_range); if (!region_dev) { cxlr =3D construct_region(cxlrd, cxled); --=20 2.47.1 From nobody Tue Dec 16 05:49:25 2025 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.10]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CBE7B20CCD3; Tue, 14 Jan 2025 20:31:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.10 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736886718; cv=none; b=XCB4qdfj5dDo4Fjn4zwEC9b+acetxqYyY2tCHINC2205Zhz78WFtiUnAgmKwgOAYT06qe5sGXUuptsYvHU3GVE0F2bRezOz31TqN5gsrK5Jq3e+2GdBDAdim3eZm72AfpDfx2r+w3/DevyhrHiRAMVOtgY1nwKxAWPygQJP8l04= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736886718; c=relaxed/simple; bh=UMYJZN/yNBUypCwr4Ub6K9pfLsSa0lKo6t07HsBmrWw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YqLwxePaIHAmevMq11OS9RovdJD2DKMfmYBTp6CWeUFRuIq6pkZqGmwdrOd8C2Pdd8DTpijUYsMhk0PBwWuG7wiyRMjowOYdAPNdstJ3m5Fl2BNcdfnFxF4CFUsBBTkt29mqmRvq3JMUFBSFjlxitvnTuVSFQnNG4R1dJLhdEXM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=none smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=WcXONp12; arc=none smtp.client-ip=198.175.65.10 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="WcXONp12" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1736886717; x=1768422717; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=UMYJZN/yNBUypCwr4Ub6K9pfLsSa0lKo6t07HsBmrWw=; b=WcXONp12s+ab5uJR0wismHkIipaGxFteL1S77owSBVuKGIMbWorA639A 3qpnQK2QPQOGwfgaczzZGeF+u1NjZQnjgRj03EOgegQDf5IIwjmAhnu/x QCbH4ZLlTkYK9EWFQJmOHHn4tGOxzOx4PpXZusYX1L3ctGPz4cFgK1YIy 6aSTK8EhR6LOWqrSVEbxvXIms8RfKJ0jydDpo4Za3OeCW3oz0UM9jDD+z /IS5GjY8wEYWDEkeKQqMkLZUPRfMd3ji8c5ZxjY8bsbGGyHwNWgQWi4Nm vyKx79cVsNfyyrBD9RhFgZZHJLVnKDKnaYti25D9viB2FjPNDFs6fEt0L Q==; X-CSE-ConnectionGUID: d2yvpZ9nT+OSCp1HyfKksw== X-CSE-MsgGUID: LfU4HPTJS8+KmIm8x+SRAg== X-IronPort-AV: E=McAfee;i="6700,10204,11315"; a="54621485" X-IronPort-AV: E=Sophos;i="6.12,315,1728975600"; d="scan'208";a="54621485" Received: from orviesa001.jf.intel.com ([10.64.159.141]) by orvoesa102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 14 Jan 2025 12:31:57 -0800 X-CSE-ConnectionGUID: 61HtVg3YQGOdMwpfltckfw== X-CSE-MsgGUID: bsuQEu2cTHWhCx/N05TnYA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,224,1728975600"; d="scan'208";a="142184201" Received: from fdefranc-mobl3.ger.corp.intel.com (HELO fdefranc-mobl3.intel.com) ([10.245.244.88]) by smtpauth.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 14 Jan 2025 12:31:53 -0800 From: "Fabio M. De Francesco" To: Davidlohr Bueso , Jonathan Cameron , Dave Jiang , Alison Schofield , Vishal Verma , Ira Weiny , Dan Williams Cc: Robert Richter , ming.li@zohomail.com, linux-kernel@vger.kernel.org, linux-cxl@vger.kernel.org, "Fabio M. De Francesco" Subject: [PATCH 2/4] cxl/core: Add helpers to detect Low memory Holes on x86 Date: Tue, 14 Jan 2025 21:31:02 +0100 Message-ID: <20250114203136.31704-3-fabio.m.de.francesco@linux.intel.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250114203136.31704-1-fabio.m.de.francesco@linux.intel.com> References: <20250114203136.31704-1-fabio.m.de.francesco@linux.intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" In x86 with Low memory Hole, the BIOS may publishes CFMWS that describe SPA ranges which are subsets of the corresponding CXL Endpoint Decoders HPA's because the CFMWS never intersects LMH's while EP Decoders HPA's ranges are always guaranteed to align to the NIW * 256M rule. In order to construct Regions and attach Decoders, the driver needs to match Root Decoders and Regions with Endpoint Decoders, but it fails and the entire process returns errors because it doesn't expect to deal with SPA range lengths smaller than corresponding HPA's. Introduce functions that indirectly detect x86 LMH's by comparing SPA's with corresponding HPA's. They will be used in the process of Regions creation and Endpoint attachments to prevent driver failures in a few steps of the above-mentioned process. The helpers return true when HPA/SPA misalignments are detected under specific conditions: both the SPA and HPA ranges must start at LMH_CFMWS_RANGE_START (that in x86 with LMH's is 0x0), SPA range sizes be less than HPA's, SPA's range's size be less than 4G, HPA's size be aligned to the NIW * 256M rule. Also introduce a function to adjust the range end of the Regions to be created on x86 with LMH's. Cc: Alison Schofield Cc: Dan Williams Cc: Ira Weiny Signed-off-by: Fabio M. De Francesco --- drivers/cxl/core/lmh.c | 55 ++++++++++++++++++++++++++++++++++++++++++ drivers/cxl/core/lmh.h | 28 +++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 drivers/cxl/core/lmh.c create mode 100644 drivers/cxl/core/lmh.h diff --git a/drivers/cxl/core/lmh.c b/drivers/cxl/core/lmh.c new file mode 100644 index 0000000000000..232ebea0a8364 --- /dev/null +++ b/drivers/cxl/core/lmh.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include "lmh.h" + +/* Start of CFMWS range that end before x86 Low Memory Holes */ +#define LMH_CFMWS_RANGE_START 0x0ULL + +/* + * Match CXL Root and Endpoint Decoders by comparing SPA and HPA ranges. + * + * On x86, CFMWS ranges never intersect memory holes while endpoint decode= rs + * HPA range sizes are always guaranteed aligned to NIW * 256MB; therefore, + * the given endpoint decoder HPA range size is always expected aligned and + * also larger than that of the matching root decoder. If there are LMH's, + * the root decoder range end is always less than SZ_4G. + */ +bool arch_match_spa(struct cxl_root_decoder *cxlrd, + struct cxl_endpoint_decoder *cxled) +{ + struct range *r1, *r2; + int niw; + + r1 =3D &cxlrd->cxlsd.cxld.hpa_range; + r2 =3D &cxled->cxld.hpa_range; + niw =3D cxled->cxld.interleave_ways; + + if (r1->start =3D=3D LMH_CFMWS_RANGE_START && r1->start =3D=3D r2->start = && + r1->end < (LMH_CFMWS_RANGE_START + SZ_4G) && r1->end < r2->end && + IS_ALIGNED(range_len(r2), niw * SZ_256M)) + return true; + + return false; +} + +/* Similar to arch_match_spa(), it matches regions and decoders */ +bool arch_match_region(struct cxl_region_params *p, struct cxl_decoder *cx= ld) +{ + struct range *r =3D &cxld->hpa_range; + struct resource *res =3D p->res; + int niw =3D cxld->interleave_ways; + + if (res->start =3D=3D LMH_CFMWS_RANGE_START && res->start =3D=3D r->start= && + res->end < (LMH_CFMWS_RANGE_START + SZ_4G) && res->end < r->end && + IS_ALIGNED(range_len(r), niw * SZ_256M)) + return true; + + return false; +} + +void arch_adjust_region_resource(struct resource *res, + struct cxl_root_decoder *cxlrd) +{ + res->end =3D cxlrd->res->end; +} diff --git a/drivers/cxl/core/lmh.h b/drivers/cxl/core/lmh.h new file mode 100644 index 0000000000000..ec8907145afe8 --- /dev/null +++ b/drivers/cxl/core/lmh.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include "cxl.h" + +#ifdef CONFIG_CXL_ARCH_LOW_MEMORY_HOLE +bool arch_match_spa(struct cxl_root_decoder *cxlrd, + struct cxl_endpoint_decoder *cxled); +bool arch_match_region(struct cxl_region_params *p, struct cxl_decoder *cx= ld); +void arch_adjust_region_resource(struct resource *res, + struct cxl_root_decoder *cxlrd); +#else +static bool arch_match_spa(struct cxl_root_decoder *cxlrd, + struct cxl_endpoint_decoder *cxled) +{ + return false; +} + +static bool arch_match_region(struct cxl_region_params *p, + struct cxl_decoder *cxld) +{ + return false; +} + +static void arch_adjust_region_resource(struct resource *res, + struct cxl_root_decoder *cxlrd) +{ +} +#endif /* CXL_ARCH_LOW_MEMORY_HOLE */ --=20 2.47.1 From nobody Tue Dec 16 05:49:25 2025 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.10]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6F3FB20C033; Tue, 14 Jan 2025 20:32:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.10 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736886723; cv=none; b=B0UtjO/BeILmQ1Xx/ZDoBZlmk53whlne6pLrEDUJFnrD82QlNMF87EDAgfl/ucESp+JVRs99aflZt/Va97A2dSEkyPrzP4NcwFkzIOGksMkjp4gGLQoGF1PKOTBUR3N/W6p+pZD7LV/dwSmS+7f6drfuUbSr74AQdfXSrbn+6Nw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736886723; c=relaxed/simple; bh=2aKty44xiRVvvM3/8cgVtjinfhkxhp4f4neBBgaazo0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mhq9cQZtefecToD+wNu8AeTgpGMcJDYODoTMpDD3yseKIU5abkT/f1xl8phOY0+gsy7tGhIO9i53hy4P0NE8Yjsz6BIbibFxRp8sSaa8N6ti6GJp82uCwpRl/wleSyELGluqm1hAcGlRqC6tyoXEpNSmWXiM5ook3K8z0zzedjo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=none smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=Bq/xLBxi; arc=none smtp.client-ip=198.175.65.10 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="Bq/xLBxi" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1736886722; x=1768422722; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=2aKty44xiRVvvM3/8cgVtjinfhkxhp4f4neBBgaazo0=; b=Bq/xLBxipIz0OOD9lzlzDOJle0ZbjXCea+FYGmVTQhTuhpbIB496SUp/ bi5UbMeOPNRq6w2dYjnOt8I9RZZF7eyqRbG3uAN3j4GQjcWLF/c5LS3Zk +B+f4Xeq0m3smLAsQ85I5H9Paa3dcE5DLteBtXYJfJN2FuB9xJ/gp/OwE aP0QLNPmnEbrYYb6NIuXjC0l4Lf/KNihDyp/25XDJHIVZtKGDTg0HKMf6 9f8pPHwQI3T7qtfCyKlIr4YUVVcmWImbRuEbVR0TbCSKn8s9NEqN7np9u BVp9I4d2TNM0ko3C6+p9SXit1qlI7iSTBW3dxyLxWc8RiBgP6lMfycgob w==; X-CSE-ConnectionGUID: nhyz7W/TQvOfqaftulOlmA== X-CSE-MsgGUID: +tUg9YpjRime6J2pm4QfPw== X-IronPort-AV: E=McAfee;i="6700,10204,11315"; a="54621497" X-IronPort-AV: E=Sophos;i="6.12,315,1728975600"; d="scan'208";a="54621497" Received: from orviesa001.jf.intel.com ([10.64.159.141]) by orvoesa102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 14 Jan 2025 12:32:02 -0800 X-CSE-ConnectionGUID: HeN91O4ASX2V4UXB/W4JJg== X-CSE-MsgGUID: uR3G8bX6TEyWTcwROtqxlQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,224,1728975600"; d="scan'208";a="142184212" Received: from fdefranc-mobl3.ger.corp.intel.com (HELO fdefranc-mobl3.intel.com) ([10.245.244.88]) by smtpauth.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 14 Jan 2025 12:31:57 -0800 From: "Fabio M. De Francesco" To: Davidlohr Bueso , Jonathan Cameron , Dave Jiang , Alison Schofield , Vishal Verma , Ira Weiny , Dan Williams Cc: Robert Richter , ming.li@zohomail.com, linux-kernel@vger.kernel.org, linux-cxl@vger.kernel.org, "Fabio M. De Francesco" Subject: [PATCH 3/4] cxl/core: Enable Region creation on x86 with Low Memory Hole Date: Tue, 14 Jan 2025 21:31:03 +0100 Message-ID: <20250114203136.31704-4-fabio.m.de.francesco@linux.intel.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250114203136.31704-1-fabio.m.de.francesco@linux.intel.com> References: <20250114203136.31704-1-fabio.m.de.francesco@linux.intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The CXL Fixed Memory Window Structure (CFMWS) describes zero or more Host Physical Address (HPA) windows that are associated with each CXL Host Bridge. Each window represents a contiguous HPA that may be interleaved with one or more targets (CXL v3.1 - 9.18.1.3). The Low Memory Hole (LMH) of x86 is a range of addresses of physical low memory to which systems cannot send transactions. In some cases the size of that hole is not compatible with the CXL hardware decoder constraint that the size is always aligned to 256M * Interleave Ways. On those systems, BIOS publishes CFMWS which communicate the active System Physical Address (SPA) ranges that map to a subset of the Host Physical Address (HPA) ranges. The SPA range trims out the hole, and capacity in the endpoint is lost with no SPA to map to CXL HPA in that hole. In the early stages of CXL Regions construction and attach on platforms with Low Memory Holes, cxl_add_to_region() fails and returns an error because it can't find any CXL Window that matches a given CXL Endpoint Decoder. Detect a Low Memory Hole by comparing Root Decoders and Endpoint Decoders ranges with the use of arch_match_{spa,region}() helpers. Match Root Decoders and CXL Regions with corresponding CXL Endpoint Decoders. Currently a Low Memory Holes would prevent the matching functions to return true. Construct CXL Regions with HPA range's end adjusted to the matching SPA. Allow the attach target process to complete by allowing Regions to not comply with alignment constraints (i.e., alignment to NIW * 256M rule). Cc: Alison Schofield Cc: Dan Williams Cc: Ira Weiny Signed-off-by: Fabio M. De Francesco --- drivers/cxl/Kconfig | 5 ++++ drivers/cxl/core/Makefile | 1 + drivers/cxl/core/region.c | 56 ++++++++++++++++++++++++++++++++------- tools/testing/cxl/Kbuild | 1 + 4 files changed, 54 insertions(+), 9 deletions(-) diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig index 876469e23f7a7..07b87f217e590 100644 --- a/drivers/cxl/Kconfig +++ b/drivers/cxl/Kconfig @@ -128,6 +128,11 @@ config CXL_REGION =20 If unsure say 'y' =20 +config CXL_ARCH_LOW_MEMORY_HOLE + def_bool y + depends on CXL_REGION + depends on X86 + config CXL_REGION_INVALIDATION_TEST bool "CXL: Region Cache Management Bypass (TEST)" depends on CXL_REGION diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile index 9259bcc6773c8..6e80215e8444f 100644 --- a/drivers/cxl/core/Makefile +++ b/drivers/cxl/core/Makefile @@ -15,4 +15,5 @@ cxl_core-y +=3D hdm.o cxl_core-y +=3D pmu.o cxl_core-y +=3D cdat.o cxl_core-$(CONFIG_TRACING) +=3D trace.o +cxl_core-$(CONFIG_CXL_ARCH_LOW_MEMORY_HOLE) +=3D lmh.o cxl_core-$(CONFIG_CXL_REGION) +=3D region.o diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 9d2c31f5caf26..b25e48da17d53 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -13,6 +13,7 @@ #include #include #include "core.h" +#include "lmh.h" =20 /** * DOC: cxl core region @@ -836,8 +837,12 @@ static int match_auto_decoder(struct device *dev, void= *data) cxld =3D to_cxl_decoder(dev); r =3D &cxld->hpa_range; =20 - if (p->res && p->res->start =3D=3D r->start && p->res->end =3D=3D r->end) - return 1; + if (p->res) { + if (p->res->start =3D=3D r->start && p->res->end =3D=3D r->end) + return 1; + if (arch_match_region(p, cxld)) + return 1; + } =20 return 0; } @@ -1425,7 +1430,8 @@ static int cxl_port_setup_targets(struct cxl_port *po= rt, if (cxld->interleave_ways !=3D iw || cxld->interleave_granularity !=3D ig || cxld->hpa_range.start !=3D p->res->start || - cxld->hpa_range.end !=3D p->res->end || + (cxld->hpa_range.end !=3D p->res->end && + !arch_match_region(p, cxld)) || ((cxld->flags & CXL_DECODER_F_ENABLE) =3D=3D 0)) { dev_err(&cxlr->dev, "%s:%s %s expected iw: %d ig: %d %pr\n", @@ -1737,6 +1743,7 @@ static int match_switch_decoder_by_range(struct devic= e *dev, void *data) { struct cxl_endpoint_decoder *cxled =3D data; struct cxl_switch_decoder *cxlsd; + struct cxl_root_decoder *cxlrd; struct range *r1, *r2; =20 if (!is_switch_decoder(dev)) @@ -1746,8 +1753,13 @@ static int match_switch_decoder_by_range(struct devi= ce *dev, void *data) r1 =3D &cxlsd->cxld.hpa_range; r2 =3D &cxled->cxld.hpa_range; =20 - if (is_root_decoder(dev)) - return range_contains(r1, r2); + if (is_root_decoder(dev)) { + if (range_contains(r1, r2)) + return 1; + cxlrd =3D to_cxl_root_decoder(dev); + if (arch_match_spa(cxlrd, cxled)) + return 1; + } return (r1->start =3D=3D r2->start && r1->end =3D=3D r2->end); } =20 @@ -1954,7 +1966,8 @@ static int cxl_region_attach(struct cxl_region *cxlr, } =20 if (resource_size(cxled->dpa_res) * p->interleave_ways !=3D - resource_size(p->res)) { + resource_size(p->res) && + !arch_match_spa(cxlrd, cxled)) { dev_dbg(&cxlr->dev, "%s:%s: decoder-size-%#llx * ways-%d !=3D region-size-%#llx\n", dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), @@ -3204,7 +3217,12 @@ static int match_root_decoder_by_range(struct device= *dev, void *data) r1 =3D &cxlrd->cxlsd.cxld.hpa_range; r2 =3D &cxled->cxld.hpa_range; =20 - return range_contains(r1, r2); + if (range_contains(r1, r2)) + return true; + if (arch_match_spa(cxlrd, cxled)) + return true; + + return false; } =20 static int match_region_by_range(struct device *dev, void *data) @@ -3222,8 +3240,12 @@ static int match_region_by_range(struct device *dev,= void *data) p =3D &cxlr->params; =20 down_read(&cxl_region_rwsem); - if (p->res && p->res->start =3D=3D r->start && p->res->end =3D=3D r->end) - rc =3D 1; + if (p->res) { + if (p->res->start =3D=3D r->start && p->res->end =3D=3D r->end) + rc =3D 1; + if (arch_match_region(p, &cxled->cxld)) + rc =3D 1; + } up_read(&cxl_region_rwsem); =20 return rc; @@ -3275,6 +3297,22 @@ static struct cxl_region *construct_region(struct cx= l_root_decoder *cxlrd, =20 *res =3D DEFINE_RES_MEM_NAMED(hpa->start, range_len(hpa), dev_name(&cxlr->dev)); + + /* + * Trim the HPA retrieved from hardware to fit the SPA mapped by the + * platform + */ + if (arch_match_spa(cxlrd, cxled)) { + dev_dbg(cxlmd->dev.parent, "(LMH) Resource (%s: %pr)\n", + dev_name(&cxled->cxld.dev), res); + + arch_adjust_region_resource(res, cxlrd); + + dev_dbg(cxlmd->dev.parent, + "(LMH) has been adjusted (%s: %pr)\n", + dev_name(&cxled->cxld.dev), res); + } + rc =3D insert_resource(cxlrd->res, res); if (rc) { /* diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild index b1256fee3567f..fe9c4480f7583 100644 --- a/tools/testing/cxl/Kbuild +++ b/tools/testing/cxl/Kbuild @@ -62,6 +62,7 @@ cxl_core-y +=3D $(CXL_CORE_SRC)/hdm.o cxl_core-y +=3D $(CXL_CORE_SRC)/pmu.o cxl_core-y +=3D $(CXL_CORE_SRC)/cdat.o cxl_core-$(CONFIG_TRACING) +=3D $(CXL_CORE_SRC)/trace.o +cxl_core-$(CONFIG_CXL_ARCH_LOW_MEMORY_HOLE) +=3D $(CXL_CORE_SRC)/lmh.o cxl_core-$(CONFIG_CXL_REGION) +=3D $(CXL_CORE_SRC)/region.o cxl_core-y +=3D config_check.o cxl_core-y +=3D cxl_core_test.o --=20 2.47.1 From nobody Tue Dec 16 05:49:25 2025 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.10]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CF81B1459FD; Tue, 14 Jan 2025 20:32:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.10 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736886727; cv=none; b=F071GBIUKdhP8VjhNSuTTrb6uGLOkobxGB6Cbb8JrW92r9w3wJyTfLaJV51eDA8Z0fRooxY7cjeI46s4zZRlGoWBRINYovIl2R/qvPsItzljKAmtdSxzhOBzW56YFukGQsR0Bia+Ibogau7fb0s6ttAA79NI89e55TJnJCGJUgQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736886727; c=relaxed/simple; bh=/APxzXDFKLv/4MMx+aCv6YJHiEB/aU+KzNUx3IBKBME=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jW7sylbmBuxZRjbeVvgk9++/ywMEL3Mgi7E0DicKMjAgD4GgFWX0GRCSaVKpITj5WvWx1/0UmZSj6a80zDoPxTSaa6r60HDqwwRo+HoNFUklCe8FXfFBPB4gez7nxUnGwCsHw9FqwXnJsn/7GH5o+rIjviqcfN8rwBvXYyjd46w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=none smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=gM8D+lpI; arc=none smtp.client-ip=198.175.65.10 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="gM8D+lpI" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1736886726; x=1768422726; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=/APxzXDFKLv/4MMx+aCv6YJHiEB/aU+KzNUx3IBKBME=; b=gM8D+lpIa4DcTXmowohw/245X9ZiRKKD1AS+8jeAskYooArKCkrAW4UR KpV9fz9D66n4rf4xjzbQdh9cgHRA8vH2T3iKD1Qp6+//GkXd9Xar3410+ m2NYa+99ZqDdOjHf8bx/AyZrllCbhdf2dacEca9eKXDA7RqVoyjSgiasd Yn/6U8u58wBMVx6ENKM7g2sYGnzrC8cSkjyMd9Cdg/m+foBOxxyaim185 lfgm22A1SYSPB4QzA9Uq+H990lEjOofOASK3IwIrm1kCZCXGZsEOio8GP N6/B57w9RxPOg1AzDplIiZUV/15ZJFBniMB+frwi4omqUOwFLnUsLWHfA A==; X-CSE-ConnectionGUID: cIys1L7JRFa8ENn9WgKNHA== X-CSE-MsgGUID: Fkb39rmWRLecir+JfrguDA== X-IronPort-AV: E=McAfee;i="6700,10204,11315"; a="54621510" X-IronPort-AV: E=Sophos;i="6.12,315,1728975600"; d="scan'208";a="54621510" Received: from orviesa001.jf.intel.com ([10.64.159.141]) by orvoesa102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 14 Jan 2025 12:32:06 -0800 X-CSE-ConnectionGUID: dNu+neEfTeutzsI53D+VXA== X-CSE-MsgGUID: 0ScvnQB0TsOwNVdL+7TDbA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,224,1728975600"; d="scan'208";a="142184225" Received: from fdefranc-mobl3.ger.corp.intel.com (HELO fdefranc-mobl3.intel.com) ([10.245.244.88]) by smtpauth.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 14 Jan 2025 12:32:02 -0800 From: "Fabio M. De Francesco" To: Davidlohr Bueso , Jonathan Cameron , Dave Jiang , Alison Schofield , Vishal Verma , Ira Weiny , Dan Williams Cc: Robert Richter , ming.li@zohomail.com, linux-kernel@vger.kernel.org, linux-cxl@vger.kernel.org, "Fabio M. De Francesco" Subject: [PATCH 4/4] cxl/test: Simulate an x86 Low Memory Hole for tests Date: Tue, 14 Jan 2025 21:31:04 +0100 Message-ID: <20250114203136.31704-5-fabio.m.de.francesco@linux.intel.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20250114203136.31704-1-fabio.m.de.francesco@linux.intel.com> References: <20250114203136.31704-1-fabio.m.de.francesco@linux.intel.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Simulate an x86 Low Memory Hole for the CXL tests by changing the first mock CFMWS range size to 768MB and the CXL Endpoint Decoder HPA range sizes to 1GB. Since the auto-created region of cxl-test uses mock_cfmws[0], whose range base address is typically different from the one published by the BIOS on real hardware, the driver would fail to create and attach CXL Regions if it was run on the mock environment created by cxl-tests. Therefore, save the mock_cfmsw[0] range base_hpa and reuse it to match CXL Root Decoders and Regions with Endpoint Decoders when the driver is run on mock devices. Since the auto-created region of cxl-test uses mock_cfmws[0], the LMH path in the CXL Driver will be exercised every time the cxl-test module is loaded. Executing unit test: cxl-topology.sh, confirms the region created successfully with a LMH. Cc: Alison Schofield Cc: Dan Williams Cc: Ira Weiny Signed-off-by: Fabio M. De Francesco --- drivers/cxl/core/lmh.c | 32 +++++++++++++++++++++++++--- drivers/cxl/core/lmh.h | 2 ++ tools/testing/cxl/cxl_core_exports.c | 2 ++ tools/testing/cxl/test/cxl.c | 10 +++++++++ 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/drivers/cxl/core/lmh.c b/drivers/cxl/core/lmh.c index 232ebea0a8364..b981aeea805d6 100644 --- a/drivers/cxl/core/lmh.c +++ b/drivers/cxl/core/lmh.c @@ -1,11 +1,28 @@ // SPDX-License-Identifier: GPL-2.0-only =20 #include +#include + #include "lmh.h" =20 /* Start of CFMWS range that end before x86 Low Memory Holes */ #define LMH_CFMWS_RANGE_START 0x0ULL =20 +static u64 mock_cfmws0_range_start =3D ULLONG_MAX; + +void set_mock_cfmws0_range_start(u64 start) +{ + mock_cfmws0_range_start =3D start; +} + +static u64 get_cfmws_range_start(struct device *dev) +{ + if (dev_is_pci(dev)) + return LMH_CFMWS_RANGE_START; + + return mock_cfmws0_range_start; +} + /* * Match CXL Root and Endpoint Decoders by comparing SPA and HPA ranges. * @@ -18,9 +35,14 @@ bool arch_match_spa(struct cxl_root_decoder *cxlrd, struct cxl_endpoint_decoder *cxled) { + u64 cfmws_range_start; struct range *r1, *r2; int niw; =20 + cfmws_range_start =3D get_cfmws_range_start(&cxled->cxld.dev); + if (cfmws_range_start =3D=3D ULLONG_MAX) + return false; + r1 =3D &cxlrd->cxlsd.cxld.hpa_range; r2 =3D &cxled->cxld.hpa_range; niw =3D cxled->cxld.interleave_ways; @@ -36,13 +58,17 @@ bool arch_match_spa(struct cxl_root_decoder *cxlrd, /* Similar to arch_match_spa(), it matches regions and decoders */ bool arch_match_region(struct cxl_region_params *p, struct cxl_decoder *cx= ld) { + u64 cfmws_range_start; struct range *r =3D &cxld->hpa_range; struct resource *res =3D p->res; int niw =3D cxld->interleave_ways; =20 - if (res->start =3D=3D LMH_CFMWS_RANGE_START && res->start =3D=3D r->start= && - res->end < (LMH_CFMWS_RANGE_START + SZ_4G) && res->end < r->end && - IS_ALIGNED(range_len(r), niw * SZ_256M)) + cfmws_range_start =3D get_cfmws_range_start(&cxld->dev); + if (cfmws_range_start =3D=3D ULLONG_MAX) + return false; + + if (res->start =3D=3D cfmws_range_start && res->start =3D=3D r->start && + res->end < r->end && IS_ALIGNED(range_len(r), niw * SZ_256M)) return true; =20 return false; diff --git a/drivers/cxl/core/lmh.h b/drivers/cxl/core/lmh.h index ec8907145afe8..d804108fbb41a 100644 --- a/drivers/cxl/core/lmh.h +++ b/drivers/cxl/core/lmh.h @@ -2,6 +2,8 @@ =20 #include "cxl.h" =20 +void set_mock_cfmws0_range_start(u64 start); + #ifdef CONFIG_CXL_ARCH_LOW_MEMORY_HOLE bool arch_match_spa(struct cxl_root_decoder *cxlrd, struct cxl_endpoint_decoder *cxled); diff --git a/tools/testing/cxl/cxl_core_exports.c b/tools/testing/cxl/cxl_c= ore_exports.c index f088792a8925f..7b20f9fcf0d75 100644 --- a/tools/testing/cxl/cxl_core_exports.c +++ b/tools/testing/cxl/cxl_core_exports.c @@ -2,6 +2,8 @@ /* Copyright(c) 2022 Intel Corporation. All rights reserved. */ =20 #include "cxl.h" +#include "lmh.h" =20 /* Exporting of cxl_core symbols that are only used by cxl_test */ EXPORT_SYMBOL_NS_GPL(cxl_num_decoders_committed, "CXL"); +EXPORT_SYMBOL_NS_GPL(set_mock_cfmws0_range_start, "CXL"); diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index d0337c11f9ee6..6bd305e778687 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -9,6 +9,7 @@ #include #include #include +#include =20 #include "../watermark.h" #include "mock.h" @@ -212,7 +213,11 @@ static struct { .restrictions =3D ACPI_CEDT_CFMWS_RESTRICT_TYPE3 | ACPI_CEDT_CFMWS_RESTRICT_VOLATILE, .qtg_id =3D FAKE_QTG_ID, +#if defined(CONFIG_CXL_ARCH_LOW_MEMORY_HOLE) + .window_size =3D SZ_256M * 3UL, +#else .window_size =3D SZ_256M * 4UL, +#endif }, .target =3D { 0 }, }, @@ -454,6 +459,7 @@ static int populate_cedt(void) return -ENOMEM; window->base_hpa =3D res->range.start; } + set_mock_cfmws0_range_start(mock_cfmws[0]->base_hpa); =20 return 0; } @@ -744,7 +750,11 @@ static void mock_init_hdm_decoder(struct cxl_decoder *= cxld) struct cxl_endpoint_decoder *cxled; struct cxl_switch_decoder *cxlsd; struct cxl_port *port, *iter; +#if defined(CONFIG_CXL_ARCH_LOW_MEMORY_HOLE) + const int size =3D SZ_1G; +#else const int size =3D SZ_512M; +#endif struct cxl_memdev *cxlmd; struct cxl_dport *dport; struct device *dev; --=20 2.47.1