[PATCH 2/2] arm64: mm: always map fixmap at page granularity

Mark Rutland mark.rutland at arm.com
Tue Mar 21 07:18:28 PDT 2023


On Wed, Mar 15, 2023 at 10:23:31AM +0000, Ryan Roberts wrote:
> On 14/03/2023 14:21, Mark Rutland wrote:

> > diff --git a/arch/arm64/include/asm/fixmap.h b/arch/arm64/include/asm/fixmap.h
> > index c153f069e9c9..c59d433c1801 100644
> > --- a/arch/arm64/include/asm/fixmap.h
> > +++ b/arch/arm64/include/asm/fixmap.h
> > @@ -36,17 +36,13 @@ enum fixed_addresses {
> >  	FIX_HOLE,
> >  
> >  	/*
> > -	 * Reserve a virtual window for the FDT that is 2 MB larger than the
> > -	 * maximum supported size, and put it at the top of the fixmap region.
> > -	 * The additional space ensures that any FDT that does not exceed
> > -	 * MAX_FDT_SIZE can be mapped regardless of whether it crosses any
> > -	 * 2 MB alignment boundaries.
> > -	 *
> > -	 * Keep this at the top so it remains 2 MB aligned.
> > +	 * Reserve a virtual window for the FDT that is a page bigger than the
> > +	 * maximum supported size. The additional space ensures that any FDT
> > +	 * that does not exceed MAX_FDT_SIZE can be mapped regardless of
> > +	 * whether it crosses any page boundary.
> >  	 */
> > -#define FIX_FDT_SIZE		(MAX_FDT_SIZE + SZ_2M)
> >  	FIX_FDT_END,
> > -	FIX_FDT = FIX_FDT_END + FIX_FDT_SIZE / PAGE_SIZE - 1,
> > +	FIX_FDT = FIX_FDT_END + MAX_FDT_SIZE / PAGE_SIZE - 1,
> 
> I don't think this is consistent with your comment - needs a +1 for the extra
> page? On a 4K system, FIX_FDT will be calculated as 512, but we need it to be
> 513 to deal with the case where the FDT is 2MB and not aligned to a page boundary.

Indeed; the intent was to have 513 in that case, but I clearly either forgot to
fix the calculation or messed that up when rebasing. I've fixed that to:

	FIX_FDT_END + DIV_ROUND_UP(MAX_FDT_SIZE, PAGE_SIZE) + 1,

... which'll work even if MAX_FDT_SIZE weren't a multiple of PAGE_SIZE.

[...]

> > diff --git a/arch/arm64/mm/fixmap.c b/arch/arm64/mm/fixmap.c
> > index 54e50552bfe3..d7a6a485f361 100644
> > --- a/arch/arm64/mm/fixmap.c
> > +++ b/arch/arm64/mm/fixmap.c
> > @@ -16,34 +16,77 @@
> >  #include <asm/pgalloc.h>
> >  #include <asm/tlbflush.h>
> >  
> > -static pte_t bm_pte[PTRS_PER_PTE] __page_aligned_bss;
> > +#define NR_BM_PTE_TABLES \
> > +	SPAN_NR_ENTRIES(FIXADDR_START, FIXADDR_TOP, PMD_SHIFT)
> > +#define NR_BM_PMD_TABLES \
> > +	SPAN_NR_ENTRIES(FIXADDR_START, FIXADDR_TOP, PUD_SHIFT)
> 
> One of the bear traps I stepped in last week when I was wrestling with this is
> that FIXADDR_START isn't actually the address of the first slot! It is
> __end_of_permanent_fixed_addresses. But you then have all the FIX_BTMAP slots,
> FIX_PTE/PMD/PUD/PGD before FIXADDR_START. So I think some refactoring is in
> order to properly cover all the slots.

Ugh, yes. This is a total mess.

> Honestly, the fact that the fixed_addresses enum is backwards is mind-bending
> too. 

That has been a long standing bugbear of mine, too.

> Any chance we can just define FIXADDR_START and FIXADDR_TOP to cover the
> whole lot, then calculate the slot address as (FIXADDR_START + (<ENUM> <<
> PAGE_SHIFT))?

Unfortunately, some code is (implicitly) relying on FIXADDR_START..FIXADDR_TOP
*not* including the FIX_BTMAP slots (e.g. virt_to_fix() and fix_to_virt() use
that to implicitly catch dodgy usage). Other code really wants FIXADDR_START to
be the base of the whole fixmap region (e.g. ptdump).

Cleaning that up is going to be a bit churny/painful. I'll take a look.

> (plus reorder of start/end enum labels for FDT and BTMAP). Or if
> we really care about reclaiming the virtual address space between
> __end_of_permanent_fixed_addresses and __end_of_fixed_addresses, create a new
> FIXADDR_BOOT_START or whatever.

I don't think we care about reclaiming the address space; it's a few pages at
best. I'll go take another look at how churny it'd be clean this up...

> > +
> > +static_assert(NR_BM_PMD_TABLES == 1);
> > +
> > +#define __BM_TABLE_IDX(addr, shift) \
> > +	(((addr) >> (shift)) - (FIXADDR_START >> (shift)))
> 
> Doesn't this run the risk of being negative for one of the slots below
> FIXADDR_START?

Yes; I had erroneously thought FIXADDR_START was below all of those, and the
intent was to use the base of the entire fixmap. So as above we'll need to do
some more preparatory cleanup...

> > +void __init early_fixmap_init_pte(pmd_t *pmdp, unsigned long addr)
> 
> static? - same comment for pmd/pud below.

Yup; done.

> > +{
> > +	pmd_t pmd = READ_ONCE(*pmdp);
> > +	pte_t *ptep;
> >  
> > -	return pud_offset_kimg(p4dp, addr);
> > +	if (pmd_none(pmd)) {
> > +		ptep = bm_pte[BM_PTE_TABLE_IDX(addr)];;
> 
> nit: remove one of the semi-colons.

Whoops; rebase error; done.

Thanks,
Mark.



More information about the linux-arm-kernel mailing list