diff mbox series

[v4,35/61] arm64: pgtable: Decouple PGDIR size macros from PGD/PUD/PMD levels

Message ID 20230912141549.278777-98-ardb@google.com (mailing list archive)
State New, archived
Headers show
Series arm64: Add support for LPA2 at stage1 and WXN | expand

Commit Message

Ard Biesheuvel Sept. 12, 2023, 2:16 p.m. UTC
From: Ard Biesheuvel <ardb@kernel.org>

The mapping from PGD/PUD/PMD to levels and shifts is very confusing,
given that, due to folding, the shifts may be equal for different
levels, if the macros are even #define'd to begin with.

In a subsequent patch, we will modify the ID mapping code to decouple
the number of levels from the kernel's view of how these types are
folded, so prepare for this by reformulating the macros without the use
of these types.

Instead, use SWAPPER_BLOCK_SHIFT as the base quantity, and derive it
from either PAGE_SHIFT or PMD_SHIFT, which -if defined at all- are
defined unambiguously for a given page size, regardless of the number of
configured levels.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 arch/arm64/include/asm/kernel-pgtable.h | 65 ++++++--------------
 1 file changed, 19 insertions(+), 46 deletions(-)

Comments

Catalin Marinas Oct. 19, 2023, 4:34 p.m. UTC | #1
On Tue, Sep 12, 2023 at 02:16:25PM +0000, Ard Biesheuvel wrote:
> From: Ard Biesheuvel <ardb@kernel.org>
> 
> The mapping from PGD/PUD/PMD to levels and shifts is very confusing,
> given that, due to folding, the shifts may be equal for different
> levels, if the macros are even #define'd to begin with.
> 
> In a subsequent patch, we will modify the ID mapping code to decouple
> the number of levels from the kernel's view of how these types are
> folded, so prepare for this by reformulating the macros without the use
> of these types.
> 
> Instead, use SWAPPER_BLOCK_SHIFT as the base quantity, and derive it
> from either PAGE_SHIFT or PMD_SHIFT, which -if defined at all- are
> defined unambiguously for a given page size, regardless of the number of
> configured levels.
> 
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
>  arch/arm64/include/asm/kernel-pgtable.h | 65 ++++++--------------
>  1 file changed, 19 insertions(+), 46 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/kernel-pgtable.h b/arch/arm64/include/asm/kernel-pgtable.h
> index 742a4b2778f7..5000f38ae0c6 100644
> --- a/arch/arm64/include/asm/kernel-pgtable.h
> +++ b/arch/arm64/include/asm/kernel-pgtable.h
> @@ -13,27 +13,22 @@
>  #include <asm/sparsemem.h>
>  
>  /*
> - * The linear mapping and the start of memory are both 2M aligned (per
> - * the arm64 booting.txt requirements). Hence we can use section mapping
> - * with 4K (section size = 2M) but not with 16K (section size = 32M) or
> - * 64K (section size = 512M).
> + * The physical and virtual addresses of the start of the kernel image are
> + * equal modulo 2 MiB (per the arm64 booting.txt requirements). Hence we can
> + * use section mapping with 4K (section size = 2M) but not with 16K (section
> + * size = 32M) or 64K (section size = 512M).
>   */
> -
> -/*
> - * The idmap and swapper page tables need some space reserved in the kernel
> - * image. Both require pgd, pud (4 levels only) and pmd tables to (section)
> - * map the kernel. With the 64K page configuration, swapper and idmap need to
> - * map to pte level. The swapper also maps the FDT (see __create_page_tables
> - * for more information). Note that the number of ID map translation levels
> - * could be increased on the fly if system RAM is out of reach for the default
> - * VA range, so pages required to map highest possible PA are reserved in all
> - * cases.
> - */
> -#ifdef CONFIG_ARM64_4K_PAGES
> -#define SWAPPER_PGTABLE_LEVELS	(CONFIG_PGTABLE_LEVELS - 1)
> +#if defined(PMD_SIZE) && PMD_SIZE <= MIN_KIMG_ALIGN

Nitpick: we always have PMD_SIZE defined, either directly in the arm64
headers if we have more than 2 levels or included from the generic
pgtable-nopmd.h. Otherwise the logic is fine.

> +#define SWAPPER_BLOCK_SHIFT	PMD_SHIFT
> +#define SWAPPER_SKIP_LEVEL	1
>  #else
> -#define SWAPPER_PGTABLE_LEVELS	(CONFIG_PGTABLE_LEVELS)
> +#define SWAPPER_BLOCK_SHIFT	PAGE_SHIFT
> +#define SWAPPER_SKIP_LEVEL	0
>  #endif
> +#define SWAPPER_BLOCK_SIZE	(UL(1) << SWAPPER_BLOCK_SHIFT)
> +#define SWAPPER_TABLE_SHIFT	(SWAPPER_BLOCK_SHIFT + PAGE_SHIFT - 3)
> +
> +#define SWAPPER_PGTABLE_LEVELS		(CONFIG_PGTABLE_LEVELS - SWAPPER_SKIP_LEVEL)

Do you use SWAPPER_SKIP_LEVEL anywhere else? If not we might as well
define SWAPPER_PGTABLE_LEVELS directly under the #if/#else blocks.

>  #define IDMAP_VA_BITS		48
>  #define IDMAP_LEVELS		ARM64_HW_PGTABLE_LEVELS(IDMAP_VA_BITS)
> @@ -53,24 +48,13 @@
>  #define EARLY_ENTRIES(vstart, vend, shift, add) \
>  	(SPAN_NR_ENTRIES(vstart, vend, shift) + (add))
>  
> -#define EARLY_PGDS(vstart, vend, add) (EARLY_ENTRIES(vstart, vend, PGDIR_SHIFT, add))
> -
> -#if SWAPPER_PGTABLE_LEVELS > 3
> -#define EARLY_PUDS(vstart, vend, add) (EARLY_ENTRIES(vstart, vend, PUD_SHIFT, add))
> -#else
> -#define EARLY_PUDS(vstart, vend, add) (0)
> -#endif
> +#define EARLY_LEVEL(l, vstart, vend, add)	\
> +	(SWAPPER_PGTABLE_LEVELS > l ? EARLY_ENTRIES(vstart, vend, SWAPPER_BLOCK_SHIFT + l * (PAGE_SHIFT - 3), add) : 0)

It took me a while to realise that this is 'l' and not '1' (I need
better glasses). Maybe if you respin, change this to 'lvl'.
Catalin Marinas Oct. 24, 2023, 5:58 p.m. UTC | #2
On Thu, Oct 19, 2023 at 05:34:33PM +0100, Catalin Marinas wrote:
> On Tue, Sep 12, 2023 at 02:16:25PM +0000, Ard Biesheuvel wrote:
> > diff --git a/arch/arm64/include/asm/kernel-pgtable.h b/arch/arm64/include/asm/kernel-pgtable.h
> > index 742a4b2778f7..5000f38ae0c6 100644
> > --- a/arch/arm64/include/asm/kernel-pgtable.h
> > +++ b/arch/arm64/include/asm/kernel-pgtable.h
> > @@ -13,27 +13,22 @@
> >  #include <asm/sparsemem.h>
> >  
> >  /*
> > - * The linear mapping and the start of memory are both 2M aligned (per
> > - * the arm64 booting.txt requirements). Hence we can use section mapping
> > - * with 4K (section size = 2M) but not with 16K (section size = 32M) or
> > - * 64K (section size = 512M).
> > + * The physical and virtual addresses of the start of the kernel image are
> > + * equal modulo 2 MiB (per the arm64 booting.txt requirements). Hence we can
> > + * use section mapping with 4K (section size = 2M) but not with 16K (section
> > + * size = 32M) or 64K (section size = 512M).
> >   */
> > -
> > -/*
> > - * The idmap and swapper page tables need some space reserved in the kernel
> > - * image. Both require pgd, pud (4 levels only) and pmd tables to (section)
> > - * map the kernel. With the 64K page configuration, swapper and idmap need to
> > - * map to pte level. The swapper also maps the FDT (see __create_page_tables
> > - * for more information). Note that the number of ID map translation levels
> > - * could be increased on the fly if system RAM is out of reach for the default
> > - * VA range, so pages required to map highest possible PA are reserved in all
> > - * cases.
> > - */
> > -#ifdef CONFIG_ARM64_4K_PAGES
> > -#define SWAPPER_PGTABLE_LEVELS	(CONFIG_PGTABLE_LEVELS - 1)
> > +#if defined(PMD_SIZE) && PMD_SIZE <= MIN_KIMG_ALIGN
> 
> Nitpick: we always have PMD_SIZE defined, either directly in the arm64
> headers if we have more than 2 levels or included from the generic
> pgtable-nopmd.h. Otherwise the logic is fine.

I pushed a patch removing this defined(PMD_SIZE) but it turns out that
we include this file in assembly and while the generic PMD_SIZE is only
defined if !defined(__ASSEMBLY__). Not sure the macro is used or just
the linker complaining.

I'll revert my patch temporarily on top of for-next/core and think about
it tomorrow. I don't like that this #if check is only false because some
other header bracketed it with #ifndef __ASSEMBLY__.
Ard Biesheuvel Oct. 25, 2023, 6:39 a.m. UTC | #3
On Tue, 24 Oct 2023 at 19:58, Catalin Marinas <catalin.marinas@arm.com> wrote:
>
> On Thu, Oct 19, 2023 at 05:34:33PM +0100, Catalin Marinas wrote:
> > On Tue, Sep 12, 2023 at 02:16:25PM +0000, Ard Biesheuvel wrote:
> > > diff --git a/arch/arm64/include/asm/kernel-pgtable.h b/arch/arm64/include/asm/kernel-pgtable.h
> > > index 742a4b2778f7..5000f38ae0c6 100644
> > > --- a/arch/arm64/include/asm/kernel-pgtable.h
> > > +++ b/arch/arm64/include/asm/kernel-pgtable.h
> > > @@ -13,27 +13,22 @@
> > >  #include <asm/sparsemem.h>
> > >
> > >  /*
> > > - * The linear mapping and the start of memory are both 2M aligned (per
> > > - * the arm64 booting.txt requirements). Hence we can use section mapping
> > > - * with 4K (section size = 2M) but not with 16K (section size = 32M) or
> > > - * 64K (section size = 512M).
> > > + * The physical and virtual addresses of the start of the kernel image are
> > > + * equal modulo 2 MiB (per the arm64 booting.txt requirements). Hence we can
> > > + * use section mapping with 4K (section size = 2M) but not with 16K (section
> > > + * size = 32M) or 64K (section size = 512M).
> > >   */
> > > -
> > > -/*
> > > - * The idmap and swapper page tables need some space reserved in the kernel
> > > - * image. Both require pgd, pud (4 levels only) and pmd tables to (section)
> > > - * map the kernel. With the 64K page configuration, swapper and idmap need to
> > > - * map to pte level. The swapper also maps the FDT (see __create_page_tables
> > > - * for more information). Note that the number of ID map translation levels
> > > - * could be increased on the fly if system RAM is out of reach for the default
> > > - * VA range, so pages required to map highest possible PA are reserved in all
> > > - * cases.
> > > - */
> > > -#ifdef CONFIG_ARM64_4K_PAGES
> > > -#define SWAPPER_PGTABLE_LEVELS     (CONFIG_PGTABLE_LEVELS - 1)
> > > +#if defined(PMD_SIZE) && PMD_SIZE <= MIN_KIMG_ALIGN
> >
> > Nitpick: we always have PMD_SIZE defined, either directly in the arm64
> > headers if we have more than 2 levels or included from the generic
> > pgtable-nopmd.h. Otherwise the logic is fine.
>
> I pushed a patch removing this defined(PMD_SIZE) but it turns out that
> we include this file in assembly and while the generic PMD_SIZE is only
> defined if !defined(__ASSEMBLY__). Not sure the macro is used or just
> the linker complaining.
>
> I'll revert my patch temporarily on top of for-next/core and think about
> it tomorrow. I don't like that this #if check is only false because some
> other header bracketed it with #ifndef __ASSEMBLY__.
>

I don't remember exactly as it's been a while but I suppose this is
why I added the check in the first place.

I agree that having this conditional is not great, but it is fine for
deciding whether to skip a level, given that we only do so when we
have more than 2 levels of paging.
diff mbox series

Patch

diff --git a/arch/arm64/include/asm/kernel-pgtable.h b/arch/arm64/include/asm/kernel-pgtable.h
index 742a4b2778f7..5000f38ae0c6 100644
--- a/arch/arm64/include/asm/kernel-pgtable.h
+++ b/arch/arm64/include/asm/kernel-pgtable.h
@@ -13,27 +13,22 @@ 
 #include <asm/sparsemem.h>
 
 /*
- * The linear mapping and the start of memory are both 2M aligned (per
- * the arm64 booting.txt requirements). Hence we can use section mapping
- * with 4K (section size = 2M) but not with 16K (section size = 32M) or
- * 64K (section size = 512M).
+ * The physical and virtual addresses of the start of the kernel image are
+ * equal modulo 2 MiB (per the arm64 booting.txt requirements). Hence we can
+ * use section mapping with 4K (section size = 2M) but not with 16K (section
+ * size = 32M) or 64K (section size = 512M).
  */
-
-/*
- * The idmap and swapper page tables need some space reserved in the kernel
- * image. Both require pgd, pud (4 levels only) and pmd tables to (section)
- * map the kernel. With the 64K page configuration, swapper and idmap need to
- * map to pte level. The swapper also maps the FDT (see __create_page_tables
- * for more information). Note that the number of ID map translation levels
- * could be increased on the fly if system RAM is out of reach for the default
- * VA range, so pages required to map highest possible PA are reserved in all
- * cases.
- */
-#ifdef CONFIG_ARM64_4K_PAGES
-#define SWAPPER_PGTABLE_LEVELS	(CONFIG_PGTABLE_LEVELS - 1)
+#if defined(PMD_SIZE) && PMD_SIZE <= MIN_KIMG_ALIGN
+#define SWAPPER_BLOCK_SHIFT	PMD_SHIFT
+#define SWAPPER_SKIP_LEVEL	1
 #else
-#define SWAPPER_PGTABLE_LEVELS	(CONFIG_PGTABLE_LEVELS)
+#define SWAPPER_BLOCK_SHIFT	PAGE_SHIFT
+#define SWAPPER_SKIP_LEVEL	0
 #endif
+#define SWAPPER_BLOCK_SIZE	(UL(1) << SWAPPER_BLOCK_SHIFT)
+#define SWAPPER_TABLE_SHIFT	(SWAPPER_BLOCK_SHIFT + PAGE_SHIFT - 3)
+
+#define SWAPPER_PGTABLE_LEVELS		(CONFIG_PGTABLE_LEVELS - SWAPPER_SKIP_LEVEL)
 
 #define IDMAP_VA_BITS		48
 #define IDMAP_LEVELS		ARM64_HW_PGTABLE_LEVELS(IDMAP_VA_BITS)
@@ -53,24 +48,13 @@ 
 #define EARLY_ENTRIES(vstart, vend, shift, add) \
 	(SPAN_NR_ENTRIES(vstart, vend, shift) + (add))
 
-#define EARLY_PGDS(vstart, vend, add) (EARLY_ENTRIES(vstart, vend, PGDIR_SHIFT, add))
-
-#if SWAPPER_PGTABLE_LEVELS > 3
-#define EARLY_PUDS(vstart, vend, add) (EARLY_ENTRIES(vstart, vend, PUD_SHIFT, add))
-#else
-#define EARLY_PUDS(vstart, vend, add) (0)
-#endif
+#define EARLY_LEVEL(l, vstart, vend, add)	\
+	(SWAPPER_PGTABLE_LEVELS > l ? EARLY_ENTRIES(vstart, vend, SWAPPER_BLOCK_SHIFT + l * (PAGE_SHIFT - 3), add) : 0)
 
-#if SWAPPER_PGTABLE_LEVELS > 2
-#define EARLY_PMDS(vstart, vend, add) (EARLY_ENTRIES(vstart, vend, SWAPPER_TABLE_SHIFT, add))
-#else
-#define EARLY_PMDS(vstart, vend, add) (0)
-#endif
-
-#define EARLY_PAGES(vstart, vend, add) ( 1 			/* PGDIR page */				\
-			+ EARLY_PGDS((vstart), (vend), add) 	/* each PGDIR needs a next level page table */	\
-			+ EARLY_PUDS((vstart), (vend), add)	/* each PUD needs a next level page table */	\
-			+ EARLY_PMDS((vstart), (vend), add))	/* each PMD needs a next level page table */
+#define EARLY_PAGES(vstart, vend, add) (1 	/* PGDIR page */				\
+	+ EARLY_LEVEL(3, (vstart), (vend), add) /* each entry needs a next level page table */	\
+	+ EARLY_LEVEL(2, (vstart), (vend), add)	/* each entry needs a next level page table */	\
+	+ EARLY_LEVEL(1, (vstart), (vend), add))/* each entry needs a next level page table */
 #define INIT_DIR_SIZE (PAGE_SIZE * (EARLY_PAGES(KIMAGE_VADDR, _end, EXTRA_PAGE) + EARLY_SEGMENT_EXTRA_PAGES))
 
 /* the initial ID map may need two extra pages if it needs to be extended */
@@ -81,17 +65,6 @@ 
 #endif
 #define INIT_IDMAP_DIR_PAGES	EARLY_PAGES(KIMAGE_VADDR, _end + MAX_FDT_SIZE + SWAPPER_BLOCK_SIZE, 1)
 
-/* Initial memory map size */
-#ifdef CONFIG_ARM64_4K_PAGES
-#define SWAPPER_BLOCK_SHIFT	PMD_SHIFT
-#define SWAPPER_BLOCK_SIZE	PMD_SIZE
-#define SWAPPER_TABLE_SHIFT	PUD_SHIFT
-#else
-#define SWAPPER_BLOCK_SHIFT	PAGE_SHIFT
-#define SWAPPER_BLOCK_SIZE	PAGE_SIZE
-#define SWAPPER_TABLE_SHIFT	PMD_SHIFT
-#endif
-
 /* The number of segments in the kernel image (text, rodata, inittext, initdata, data+bss) */
 #define KERNEL_SEGMENT_COUNT	5