[PATCH 3/3] ARM: Map the lowmem and kernel separately

Russell King (Oracle) linux at armlinux.org.uk
Wed Aug 4 03:36:58 PDT 2021


On Wed, Aug 04, 2021 at 12:05:54PM +0200, Linus Walleij wrote:
> On Tue, Aug 3, 2021 at 12:49 PM Russell King (Oracle)
> <linux at armlinux.org.uk> wrote:
> 
> > > It should be called on all keystone 2 platforms by default (LAPE is default mode for K2).
> > >
> > > Huh. Below as I remember it:
> > > - K2 starts using memory window (aliased memory) at 00 8000 0000 (No IO coherency is supported)
> > > - K2, early at boot, is switching to LPAE memory window 08 0000 0000 (IO coherency is supported)
> > >   so all, already mapped memory, has to be fixed - phys addresses.
> >
> > If I remember correctly, the code that fixes that up assumes that
> > (a) the kernel is mapped using section mappings in the lowmem mapping
> >     at PAGE_OFFSET.._end-1
> > (b) that no other RAM is mapped (iow, it's called before the kernel
> >     starts building the real page mappings in paging_init()).
> >
> > It looks to me like Linus missed the code in arch/arm/mm/pv-fixup-asm.S
> > and as the kernel is no longer mapped in the lowmem mapping, this
> > likely writes a load of entries in the page tables that are random...
> 
> I looked into this!
> 
> Indeed early_mm_init() is called before paging_init() and early_mm_init()
> calls lpae_pgtables_remap_asm() in pv-fixup-asm.S to add the offset
> to all the section mappings over the kernel.
> 
> The section mappings we have at this point are coming from head.S,
> so those get modified with the offset.
> 
> What I don't understand is what map_lowmem() was doing with the
> kernel mappings on the Keystone 2 before my patch.

Essentially, what happened with Keystone 2 is that the head.S code
does its usual thing, mapping the kernel using the physical address
that the early code is running at using section mappings, before
then switching the MMU on and running from the virtual address space.
At this point, the phys<->virt conversions work for this physical
address space (which, on Keystone 2 is the low physical space which
isn't coherent with devices.)

early_mm_init() gets called, we spot we're on Keystone 2, and we
initiate the change - the MMU needs a break-before-make for these
mappings, and as we're using these mappings to execute our code,
we have to turn the MMU off to do the update.

We update the phys<->virt conversion, and then fixup the page
tables. lpae_pgtables_remap() is passed the offset between the
low physical address and the high physical address, as well as the
low physical address of the page tables. (since when we turn the MMU
off, all we have access to are the low physical mapping.)

The structure of the LPAE page tables is:

- First 4k - L1 entries.
- Following 32k - L2 entries mapped as sections.

In pv-fixup-asm.S:

        ldr     r6, =(_end - 1)
        add     r7, r2, #0x1000
        add     r6, r7, r6, lsr #SECTION_SHIFT - L2_ORDER
        add     r7, r7, #PAGE_OFFSET >> (SECTION_SHIFT - L2_ORDER)

r7 is the low physical address in the L2 entries, r6 is the last
(inclusive) entry. Essentially, all we do is add "offset" to every
section mapping corresponding to addresses between PAGE_OFFSET and
_end.

We also do that for the boot data (two section mappings) at
FDT_FIXED_BASE, the four L1 entries, and the two TTBR physical
address register values.

> If it actually overwrites the kernel mapping with a new one, it will
> undo what lpae_pgtables_remap_asm() has done without applying
> the offset.
> 
> So I suppose map_lowmem(), due to the memory ranges passed,
> would just bail the kernel mappings and leave them as-is.

By the time map_lowmem() gets called, the phys<->virt mappings will
have been modified throughout the kernel so that virtual addresses
in the direct mapping will translate to the high physical addresses.
So, things like __pa(__init_end) in map_kernel() will translate
__init_end to the high physical address rather than the low physical
address.

My guess would be the problem is that kernel_sec_start and
kernel_sec_end are not being adjusted, and are still pointing at
the low physical addresses rather than the high ones.
Consequently, kernel_x_start and kernel_nx_end points at the low phys
space, but kernel_x_end and kernel_nx_start points to the high phys
space.

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!



More information about the linux-arm-kernel mailing list