[PATCH 05/21] x86/mm/pat: mirror direct map changes to ASI

Brendan Jackman posted 21 patches 1 week ago
[PATCH 05/21] x86/mm/pat: mirror direct map changes to ASI
Posted by Brendan Jackman 1 week ago
ASI has a separate PGD for the physmap, which needs to be kept in sync
with the unrestricted physmap with respect to permissions.

Since only the direct map is currently populated in that address space,
just ignore everything else. Handling of holes in that map is left
behaving the same as the unrestricted pagetables.

Co-developed-by: Yosry Ahmed <yosryahmed@google.com>
Signed-off-by: Yosry Ahmed <yosryahmed@google.com>
Signed-off-by: Brendan Jackman <jackmanb@google.com>
---
 arch/x86/mm/pat/set_memory.c | 32 ++++++++++++++++++++++++++++++--
 1 file changed, 30 insertions(+), 2 deletions(-)

diff --git a/arch/x86/mm/pat/set_memory.c b/arch/x86/mm/pat/set_memory.c
index d2d54b8c4dbb04cf276d074ddee3ffde2f48e381..53c3ac0ba55d6b6992db6f6761ffdfbd52bf3688 100644
--- a/arch/x86/mm/pat/set_memory.c
+++ b/arch/x86/mm/pat/set_memory.c
@@ -3,6 +3,7 @@
  * Copyright 2002 Andi Kleen, SuSE Labs.
  * Thanks to Ben LaHaise for precious feedback.
  */
+#include <linux/asi.h>
 #include <linux/highmem.h>
 #include <linux/memblock.h>
 #include <linux/sched.h>
@@ -1780,6 +1781,11 @@ static int populate_pgd(struct cpa_data *cpa, unsigned long addr)
 	cpa->numpages = ret;
 	return 0;
 }
+static inline bool is_direct_map(unsigned long vaddr)
+{
+	return within(vaddr, PAGE_OFFSET,
+		      PAGE_OFFSET + (max_pfn_mapped << PAGE_SHIFT));
+}
 
 static int __cpa_process_fault(struct cpa_data *cpa, unsigned long vaddr,
 			       int primary)
@@ -1808,8 +1814,7 @@ static int __cpa_process_fault(struct cpa_data *cpa, unsigned long vaddr,
 	 * one virtual address page and its pfn. TBD: numpages can be set based
 	 * on the initial value and the level returned by lookup_address().
 	 */
-	if (within(vaddr, PAGE_OFFSET,
-		   PAGE_OFFSET + (max_pfn_mapped << PAGE_SHIFT))) {
+	if (is_direct_map(vaddr)) {
 		cpa->numpages = 1;
 		cpa->pfn = __pa(vaddr) >> PAGE_SHIFT;
 		return 0;
@@ -1981,6 +1986,27 @@ static int cpa_process_alias(struct cpa_data *cpa)
 	return 0;
 }
 
+/*
+ * Having updated the unrestricted PGD, reflect this change in the ASI
+ * restricted address space too.
+ */
+static inline int mirror_asi_direct_map(struct cpa_data *cpa, int primary)
+{
+	struct cpa_data asi_cpa = *cpa;
+
+	if (!asi_enabled_static())
+		return 0;
+
+	/* Only need to do this for the real unrestricted direct map. */
+	if ((cpa->pgd && cpa->pgd != init_mm.pgd) || !is_direct_map(*cpa->vaddr))
+		return 0;
+	VM_WARN_ON_ONCE(!is_direct_map(*cpa->vaddr + (cpa->numpages * PAGE_SIZE)));
+
+	asi_cpa.pgd = asi_nonsensitive_pgd;
+	asi_cpa.curpage = 0;
+	return __change_page_attr(cpa, primary);
+}
+
 static int __change_page_attr_set_clr(struct cpa_data *cpa, int primary)
 {
 	unsigned long numpages = cpa->numpages;
@@ -2007,6 +2033,8 @@ static int __change_page_attr_set_clr(struct cpa_data *cpa, int primary)
 		if (!debug_pagealloc_enabled())
 			spin_lock(&cpa_lock);
 		ret = __change_page_attr(cpa, primary);
+		if (!ret)
+			ret = mirror_asi_direct_map(cpa, primary);
 		if (!debug_pagealloc_enabled())
 			spin_unlock(&cpa_lock);
 		if (ret)

-- 
2.50.1
Re: [PATCH 05/21] x86/mm/pat: mirror direct map changes to ASI
Posted by Dave Hansen 4 hours ago
On 9/24/25 07:59, Brendan Jackman wrote:
> ASI has a separate PGD for the physmap, which needs to be kept in sync
> with the unrestricted physmap with respect to permissions.

So that leads to another thing... What about vmalloc()? Why doesn't it
need to be in the ASI pgd?

> +static inline bool is_direct_map(unsigned long vaddr)
> +{
> +	return within(vaddr, PAGE_OFFSET,
> +		      PAGE_OFFSET + (max_pfn_mapped << PAGE_SHIFT));
> +}
>  
>  static int __cpa_process_fault(struct cpa_data *cpa, unsigned long vaddr,
>  			       int primary)
> @@ -1808,8 +1814,7 @@ static int __cpa_process_fault(struct cpa_data *cpa, unsigned long vaddr,
>  	 * one virtual address page and its pfn. TBD: numpages can be set based
>  	 * on the initial value and the level returned by lookup_address().
>  	 */
> -	if (within(vaddr, PAGE_OFFSET,
> -		   PAGE_OFFSET + (max_pfn_mapped << PAGE_SHIFT))) {
> +	if (is_direct_map(vaddr)) {
>  		cpa->numpages = 1;
>  		cpa->pfn = __pa(vaddr) >> PAGE_SHIFT;
>  		return 0;
> @@ -1981,6 +1986,27 @@ static int cpa_process_alias(struct cpa_data *cpa)
>  	return 0;
>  }
>  
> +/*
> + * Having updated the unrestricted PGD, reflect this change in the ASI
> + * restricted address space too.
> + */
> +static inline int mirror_asi_direct_map(struct cpa_data *cpa, int primary)
> +{
> +	struct cpa_data asi_cpa = *cpa;
> +
> +	if (!asi_enabled_static())
> +		return 0;
> +
> +	/* Only need to do this for the real unrestricted direct map. */
> +	if ((cpa->pgd && cpa->pgd != init_mm.pgd) || !is_direct_map(*cpa->vaddr))
> +		return 0;
> +	VM_WARN_ON_ONCE(!is_direct_map(*cpa->vaddr + (cpa->numpages * PAGE_SIZE)));
> +
> +	asi_cpa.pgd = asi_nonsensitive_pgd;
> +	asi_cpa.curpage = 0;

Please document what functionality this curpage=0 has. It's not clear.

> +	return __change_page_attr(cpa, primary);
> +}

But let's say someone is doing something silly like:

	set_memory_np(addr, size);
	set_memory_p(addr, size);

Won't that end up in here and make the "unrestricted PGD" have
_PAGE_PRESENT==1 entries?

Also, could we try and make the nomenclature consistent? We've got
"unrestricted direct map" and "asi_nonsensitive_pgd" being used (at
least). Could the terminology be made more consistent?

One subtle thing here is that it's OK to allocate memory here when
mirroring changes into 'asi_nonsensitive_pgd'. It's just not OK when
flipping sensitivity. That seems worth a comment.

>  static int __change_page_attr_set_clr(struct cpa_data *cpa, int primary)
>  {
>  	unsigned long numpages = cpa->numpages;
> @@ -2007,6 +2033,8 @@ static int __change_page_attr_set_clr(struct cpa_data *cpa, int primary)
>  		if (!debug_pagealloc_enabled())
>  			spin_lock(&cpa_lock);
>  		ret = __change_page_attr(cpa, primary);
> +		if (!ret)
> +			ret = mirror_asi_direct_map(cpa, primary);
>  		if (!debug_pagealloc_enabled())
>  			spin_unlock(&cpa_lock);
>  		if (ret)
> 

Is cpa->pgd ever have any values other than NULL or init_mm->pgd? I
didn't see anything in a quick grep.
Re: [PATCH 05/21] x86/mm/pat: mirror direct map changes to ASI
Posted by kernel test robot 6 days, 11 hours ago
Hi Brendan,

kernel test robot noticed the following build warnings:

[auto build test WARNING on bf2602a3cb2381fb1a04bf1c39a290518d2538d1]

url:    https://github.com/intel-lab-lkp/linux/commits/Brendan-Jackman/x86-mm-asi-Add-CONFIG_MITIGATION_ADDRESS_SPACE_ISOLATION/20250924-230633
base:   bf2602a3cb2381fb1a04bf1c39a290518d2538d1
patch link:    https://lore.kernel.org/r/20250924-b4-asi-page-alloc-v1-5-2d861768041f%40google.com
patch subject: [PATCH 05/21] x86/mm/pat: mirror direct map changes to ASI
config: x86_64-buildonly-randconfig-002-20250925 (https://download.01.org/0day-ci/archive/20250925/202509252153.JhjsdZ6c-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250925/202509252153.JhjsdZ6c-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202509252153.JhjsdZ6c-lkp@intel.com/

All warnings (new ones prefixed by >>):

   arch/x86/mm/pat/set_memory.c: In function 'mirror_asi_direct_map':
>> arch/x86/mm/pat/set_memory.c:1995:25: warning: variable 'asi_cpa' set but not used [-Wunused-but-set-variable]
    1995 |         struct cpa_data asi_cpa = *cpa;
         |                         ^~~~~~~


vim +/asi_cpa +1995 arch/x86/mm/pat/set_memory.c

  1988	
  1989	/*
  1990	 * Having updated the unrestricted PGD, reflect this change in the ASI
  1991	 * restricted address space too.
  1992	 */
  1993	static inline int mirror_asi_direct_map(struct cpa_data *cpa, int primary)
  1994	{
> 1995		struct cpa_data asi_cpa = *cpa;
  1996	
  1997		if (!asi_enabled_static())
  1998			return 0;
  1999	
  2000		/* Only need to do this for the real unrestricted direct map. */
  2001		if ((cpa->pgd && cpa->pgd != init_mm.pgd) || !is_direct_map(*cpa->vaddr))
  2002			return 0;
  2003		VM_WARN_ON_ONCE(!is_direct_map(*cpa->vaddr + (cpa->numpages * PAGE_SIZE)));
  2004	
  2005		asi_cpa.pgd = asi_nonsensitive_pgd;
  2006		asi_cpa.curpage = 0;
  2007		return __change_page_attr(cpa, primary);
  2008	}
  2009	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki