Skip to contentSkip to conversation

arm64: mm: limit linear region to 51 bits for KVM in nVHE mode

RFC PATCH Superseded Ard Biesheuvel 3 participants 7 messages Aug 10, 2021 – Aug 26, 2021 arm64

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 arm64: mm: limit linear region to 51 bits for KVM in nVHE mode

KVM in nVHE mode divides up its VA space into two equal halves, and picks the half that does not conflict with the HYP ID map to map its linear region. This worked fine when the kernel's linear map itself was guaranteed to cover precisely as many bits of VA space, but this was changed by commit f4693c2716b35d08 ("arm64: mm: extend linear region for 52-bit VA configurations").

The result is that, depending on the placement of the ID map, kernel-VA to hyp-VA translations may produce addresses that either conflict with other HYP mappings (including the ID map itself) or generate addresses outside of the 52-bit addressable range, neither of which is likely to lead to anything useful.

Given that 52-bit capable cores are guaranteed to implement VHE, this only affects configurations such as pKVM where we opt into non-VHE mode even if the hardware is VHE capable. So just for these configurations, let's limit the kernel linear map to 51 bits and work around the problem.

Fixes f4693c2716b35d08 ("arm64: mm: extend linear region for 52-bit VA configurations")

---
NOTE: build tested only

 arch/arm64/mm/init.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)
arch/arm64/mm/init.c +15 -1
index 8490ed2917ff..542dad13e2fc 100644
@@ -282,7 +282,21 @@ static void __init fdt_enforce_memory_region(void)
 
 void __init arm64_memblock_init(void)
 {
-	const s64 linear_region_size = PAGE_END - _PAGE_OFFSET(vabits_actual);
+	s64 linear_region_size = PAGE_END - _PAGE_OFFSET(vabits_actual);
+
+	/*
+	 * Corner case: 52-bit VA capable systems running KVM in nVHE mode may
+	 * be limited in their ability to support a linear map that exceeds 51
+	 * bits of VA space, depending on the placement of the ID map. Given
+	 * that the placement of the ID map may be randomized, let's simply
+	 * limit the kernel's linear map to 51 bits as well if we detect this
+	 * configuration.
+	 */
+	if (IS_ENABLED(CONFIG_KVM) && vabits_actual == 52 &&
+	    !is_kernel_in_hyp_mode()) {
+		pr_info("Capping linear region to 51 bits for KVM in nVHE mode on LVA capable hardware.\n");
+		linear_region_size = BIT(51);
+	}
 
 	/* Handle linux,usable-memory-range property */
 	fdt_enforce_memory_region();
Signature · 7 lines
2.20.1


_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
maintainer
#1 lore raw
Quoted 41 lines · Ard Biesheuvel

KVM in nVHE mode divides up its VA space into two equal halves, and picks the half that does not conflict with the HYP ID map to map its linear region. This worked fine when the kernel's linear map itself was guaranteed to cover precisely as many bits of VA space, but this was changed by commit f4693c2716b35d08 ("arm64: mm: extend linear region for 52-bit VA configurations").

The result is that, depending on the placement of the ID map, kernel-VA to hyp-VA translations may produce addresses that either conflict with other HYP mappings (including the ID map itself) or generate addresses outside of the 52-bit addressable range, neither of which is likely to lead to anything useful.

Given that 52-bit capable cores are guaranteed to implement VHE, this only affects configurations such as pKVM where we opt into non-VHE mode even if the hardware is VHE capable. So just for these configurations, let's limit the kernel linear map to 51 bits and work around the problem.

Fixes f4693c2716b35d08 ("arm64: mm: extend linear region for 52-bit VA configurations")

---
NOTE: build tested only

 arch/arm64/mm/init.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)
arch/arm64/mm/init.c +5 -1
index 8490ed2917ff..542dad13e2fc 100644
@@ -282,7 +282,21 @@ static void __init fdt_enforce_memory_region(void)
 
 void __init arm64_memblock_init(void)
 {
-	const s64 linear_region_size = PAGE_END - _PAGE_OFFSET(vabits_actual);
+	s64 linear_region_size = PAGE_END - _PAGE_OFFSET(vabits_actual);
+
+	/*
+	 * Corner case: 52-bit VA capable systems running KVM in nVHE mode may
+	 * be limited in their ability to support a linear map that exceeds 51

arch/arm64/mm/init.c static void __init fdt_enforce_memory_region(void) · line 299

Quoted 10 lines
arch/arm64/mm/init.c +10 -0
+	 * bits of VA space, depending on the placement of the ID map. Given
+	 * that the placement of the ID map may be randomized, let's simply
+	 * limit the kernel's linear map to 51 bits as well if we detect this
+	 * configuration.
+	 */
+	if (IS_ENABLED(CONFIG_KVM) && vabits_actual == 52 &&
+	    !is_kernel_in_hyp_mode()) {
+		pr_info("Capping linear region to 51 bits for KVM in nVHE mode on LVA capable hardware.\n");
+		linear_region_size = BIT(51);
+	}

Slight nit, but to avoid having to think about PAGE_END I think this would be a little clearer as:

	if (IS_ENABLED(CONFIG_KVM) && !is_kernel_in_hyp_mode())
		linear_region_size = min_t(u64, linear_region_size, BIT(51));

Will

_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

#2 lore raw
Quoted 57 lines · Will Deacon
Quoted 41 lines · Ard Biesheuvel

KVM in nVHE mode divides up its VA space into two equal halves, and picks the half that does not conflict with the HYP ID map to map its linear region. This worked fine when the kernel's linear map itself was guaranteed to cover precisely as many bits of VA space, but this was changed by commit f4693c2716b35d08 ("arm64: mm: extend linear region for 52-bit VA configurations").

The result is that, depending on the placement of the ID map, kernel-VA to hyp-VA translations may produce addresses that either conflict with other HYP mappings (including the ID map itself) or generate addresses outside of the 52-bit addressable range, neither of which is likely to lead to anything useful.

Given that 52-bit capable cores are guaranteed to implement VHE, this only affects configurations such as pKVM where we opt into non-VHE mode even if the hardware is VHE capable. So just for these configurations, let's limit the kernel linear map to 51 bits and work around the problem.

Fixes f4693c2716b35d08 ("arm64: mm: extend linear region for 52-bit VA configurations")

---
NOTE: build tested only

 arch/arm64/mm/init.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)
arch/arm64/mm/init.c +5 -1
index 8490ed2917ff..542dad13e2fc 100644
@@ -282,7 +282,21 @@ static void __init fdt_enforce_memory_region(void)
 
 void __init arm64_memblock_init(void)
 {
-     const s64 linear_region_size = PAGE_END - _PAGE_OFFSET(vabits_actual);
+     s64 linear_region_size = PAGE_END - _PAGE_OFFSET(vabits_actual);
+
+     /*
+      * Corner case: 52-bit VA capable systems running KVM in nVHE mode may
+      * be limited in their ability to support a linear map that exceeds 51

arch/arm64/mm/init.c static void __init fdt_enforce_memory_region(void) · line 299

Quoted 10 lines
arch/arm64/mm/init.c +10 -0
+      * bits of VA space, depending on the placement of the ID map. Given
+      * that the placement of the ID map may be randomized, let's simply
+      * limit the kernel's linear map to 51 bits as well if we detect this
+      * configuration.
+      */
+     if (IS_ENABLED(CONFIG_KVM) && vabits_actual == 52 &&
+         !is_kernel_in_hyp_mode()) {
+             pr_info("Capping linear region to 51 bits for KVM in nVHE mode on LVA capable hardware.\n");
+             linear_region_size = BIT(51);
+     }

Slight nit, but to avoid having to think about PAGE_END I think this would be a little clearer as:

Quoted 3 lines
        if (IS_ENABLED(CONFIG_KVM) && !is_kernel_in_hyp_mode())
                linear_region_size = min_t(u64, linear_region_size, BIT(51));

Fair enough.

_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

maintainer
#3 lore raw
Quoted 30 lines · Ard Biesheuvel
Quoted 29 lines · Will Deacon
Quoted 14 lines · Ard Biesheuvel
arch/arm64/mm/init.c +5 -1
index 8490ed2917ff..542dad13e2fc 100644
@@ -282,7 +282,21 @@ static void __init fdt_enforce_memory_region(void)
 
 void __init arm64_memblock_init(void)
 {
-     const s64 linear_region_size = PAGE_END - _PAGE_OFFSET(vabits_actual);
+     s64 linear_region_size = PAGE_END - _PAGE_OFFSET(vabits_actual);
+
+     /*
+      * Corner case: 52-bit VA capable systems running KVM in nVHE mode may
+      * be limited in their ability to support a linear map that exceeds 51

arch/arm64/mm/init.c static void __init fdt_enforce_memory_region(void) · line 299

Quoted 10 lines
arch/arm64/mm/init.c +10 -0
+      * bits of VA space, depending on the placement of the ID map. Given
+      * that the placement of the ID map may be randomized, let's simply
+      * limit the kernel's linear map to 51 bits as well if we detect this
+      * configuration.
+      */
+     if (IS_ENABLED(CONFIG_KVM) && vabits_actual == 52 &&
+         !is_kernel_in_hyp_mode()) {
+             pr_info("Capping linear region to 51 bits for KVM in nVHE mode on LVA capable hardware.\n");
+             linear_region_size = BIT(51);
+     }

Slight nit, but to avoid having to think about PAGE_END I think this would be a little clearer as:

Quoted 5 lines
Quoted 3 lines
        if (IS_ENABLED(CONFIG_KVM) && !is_kernel_in_hyp_mode())
                linear_region_size = min_t(u64, linear_region_size, BIT(51));

Fair enough.

Ard, do you plan to post a new version? I can make the change myself locally as well.

Signature · 6 lines
Catalin

_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
#4 lore raw
Quoted 32 lines · Catalin Marinas
Quoted 30 lines · Ard Biesheuvel
Quoted 29 lines · Will Deacon
Quoted 14 lines · Ard Biesheuvel
arch/arm64/mm/init.c +5 -1
index 8490ed2917ff..542dad13e2fc 100644
@@ -282,7 +282,21 @@ static void __init fdt_enforce_memory_region(void)
 
 void __init arm64_memblock_init(void)
 {
-     const s64 linear_region_size = PAGE_END - _PAGE_OFFSET(vabits_actual);
+     s64 linear_region_size = PAGE_END - _PAGE_OFFSET(vabits_actual);
+
+     /*
+      * Corner case: 52-bit VA capable systems running KVM in nVHE mode may
+      * be limited in their ability to support a linear map that exceeds 51

arch/arm64/mm/init.c static void __init fdt_enforce_memory_region(void) · line 299

Quoted 10 lines
arch/arm64/mm/init.c +10 -0
+      * bits of VA space, depending on the placement of the ID map. Given
+      * that the placement of the ID map may be randomized, let's simply
+      * limit the kernel's linear map to 51 bits as well if we detect this
+      * configuration.
+      */
+     if (IS_ENABLED(CONFIG_KVM) && vabits_actual == 52 &&
+         !is_kernel_in_hyp_mode()) {
+             pr_info("Capping linear region to 51 bits for KVM in nVHE mode on LVA capable hardware.\n");
+             linear_region_size = BIT(51);
+     }

Slight nit, but to avoid having to think about PAGE_END I think this would be a little clearer as:

Quoted 9 lines
Quoted 5 lines
Quoted 3 lines
        if (IS_ENABLED(CONFIG_KVM) && !is_kernel_in_hyp_mode())
                linear_region_size = min_t(u64, linear_region_size, BIT(51));

Fair enough.

Ard, do you plan to post a new version? I can make the change myself locally as well.

If you don't mind doing so, please go ahead.

_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

maintainer
#5 lore raw
In reply to Will Deacon
Quoted 56 lines · Will Deacon
Quoted 41 lines · Ard Biesheuvel

KVM in nVHE mode divides up its VA space into two equal halves, and picks the half that does not conflict with the HYP ID map to map its linear region. This worked fine when the kernel's linear map itself was guaranteed to cover precisely as many bits of VA space, but this was changed by commit f4693c2716b35d08 ("arm64: mm: extend linear region for 52-bit VA configurations").

The result is that, depending on the placement of the ID map, kernel-VA to hyp-VA translations may produce addresses that either conflict with other HYP mappings (including the ID map itself) or generate addresses outside of the 52-bit addressable range, neither of which is likely to lead to anything useful.

Given that 52-bit capable cores are guaranteed to implement VHE, this only affects configurations such as pKVM where we opt into non-VHE mode even if the hardware is VHE capable. So just for these configurations, let's limit the kernel linear map to 51 bits and work around the problem.

Fixes f4693c2716b35d08 ("arm64: mm: extend linear region for 52-bit VA configurations")

---
NOTE: build tested only

 arch/arm64/mm/init.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)
arch/arm64/mm/init.c +5 -1
index 8490ed2917ff..542dad13e2fc 100644
@@ -282,7 +282,21 @@ static void __init fdt_enforce_memory_region(void)
 
 void __init arm64_memblock_init(void)
 {
-	const s64 linear_region_size = PAGE_END - _PAGE_OFFSET(vabits_actual);
+	s64 linear_region_size = PAGE_END - _PAGE_OFFSET(vabits_actual);
+
+	/*
+	 * Corner case: 52-bit VA capable systems running KVM in nVHE mode may
+	 * be limited in their ability to support a linear map that exceeds 51

arch/arm64/mm/init.c static void __init fdt_enforce_memory_region(void) · line 299

Quoted 10 lines
arch/arm64/mm/init.c +10 -0
+	 * bits of VA space, depending on the placement of the ID map. Given
+	 * that the placement of the ID map may be randomized, let's simply
+	 * limit the kernel's linear map to 51 bits as well if we detect this
+	 * configuration.
+	 */
+	if (IS_ENABLED(CONFIG_KVM) && vabits_actual == 52 &&
+	    !is_kernel_in_hyp_mode()) {
+		pr_info("Capping linear region to 51 bits for KVM in nVHE mode on LVA capable hardware.\n");
+		linear_region_size = BIT(51);
+	}

Slight nit, but to avoid having to think about PAGE_END I think this would be a little clearer as:

Quoted 2 lines
	if (IS_ENABLED(CONFIG_KVM) && !is_kernel_in_hyp_mode())
		linear_region_size = min_t(u64, linear_region_size, BIT(51));

BTW, do we also need to check is_hyp_mode_available() (instead of just CONFIG_KVM)? We don't need this reduction for a guest.

Signature · 6 lines
Catalin

_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
#6 lore raw
Quoted 58 lines · Catalin Marinas
Quoted 56 lines · Will Deacon
Quoted 41 lines · Ard Biesheuvel

KVM in nVHE mode divides up its VA space into two equal halves, and picks the half that does not conflict with the HYP ID map to map its linear region. This worked fine when the kernel's linear map itself was guaranteed to cover precisely as many bits of VA space, but this was changed by commit f4693c2716b35d08 ("arm64: mm: extend linear region for 52-bit VA configurations").

The result is that, depending on the placement of the ID map, kernel-VA to hyp-VA translations may produce addresses that either conflict with other HYP mappings (including the ID map itself) or generate addresses outside of the 52-bit addressable range, neither of which is likely to lead to anything useful.

Given that 52-bit capable cores are guaranteed to implement VHE, this only affects configurations such as pKVM where we opt into non-VHE mode even if the hardware is VHE capable. So just for these configurations, let's limit the kernel linear map to 51 bits and work around the problem.

Fixes f4693c2716b35d08 ("arm64: mm: extend linear region for 52-bit VA configurations")

---
NOTE: build tested only

 arch/arm64/mm/init.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)
arch/arm64/mm/init.c +5 -1
index 8490ed2917ff..542dad13e2fc 100644
@@ -282,7 +282,21 @@ static void __init fdt_enforce_memory_region(void)
 
 void __init arm64_memblock_init(void)
 {
-   const s64 linear_region_size = PAGE_END - _PAGE_OFFSET(vabits_actual);
+   s64 linear_region_size = PAGE_END - _PAGE_OFFSET(vabits_actual);
+
+   /*
+    * Corner case: 52-bit VA capable systems running KVM in nVHE mode may
+    * be limited in their ability to support a linear map that exceeds 51

arch/arm64/mm/init.c static void __init fdt_enforce_memory_region(void) · line 299

Quoted 10 lines
arch/arm64/mm/init.c +10 -0
+    * bits of VA space, depending on the placement of the ID map. Given
+    * that the placement of the ID map may be randomized, let's simply
+    * limit the kernel's linear map to 51 bits as well if we detect this
+    * configuration.
+    */
+   if (IS_ENABLED(CONFIG_KVM) && vabits_actual == 52 &&
+       !is_kernel_in_hyp_mode()) {
+           pr_info("Capping linear region to 51 bits for KVM in nVHE mode on LVA capable hardware.\n");
+           linear_region_size = BIT(51);
+   }

Slight nit, but to avoid having to think about PAGE_END I think this would be a little clearer as:

Quoted 6 lines
Quoted 2 lines
      if (IS_ENABLED(CONFIG_KVM) && !is_kernel_in_hyp_mode())
              linear_region_size = min_t(u64, linear_region_size, BIT(51));

BTW, do we also need to check is_hyp_mode_available() (instead of just CONFIG_KVM)? We don't need this reduction for a guest.

Hmm, good point. I'll respin with both suggestions incorporated.

_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel