[PATCH v3 7/7] arm64: allow kernel Image to be loaded anywhere in physical memory

Ard Biesheuvel ard.biesheuvel at linaro.org
Mon Dec 7 07:40:20 PST 2015


On 7 December 2015 at 16:30, Catalin Marinas <catalin.marinas at arm.com> wrote:
> On Mon, Nov 16, 2015 at 12:23:18PM +0100, Ard Biesheuvel wrote:
>> diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
>> index 3148691bc80a..d6a237bda1f9 100644
>> --- a/arch/arm64/include/asm/memory.h
>> +++ b/arch/arm64/include/asm/memory.h
>> @@ -120,13 +120,10 @@ extern phys_addr_t              memstart_addr;
>>  extern u64 kernel_va_offset;
>>
>>  /*
>> - * The maximum physical address that the linear direct mapping
>> - * of system RAM can cover. (PAGE_OFFSET can be interpreted as
>> - * a 2's complement signed quantity and negated to derive the
>> - * maximum size of the linear mapping.)
>> + * Allow all memory at the discovery stage. We will clip it later.
>>   */
>> -#define MAX_MEMBLOCK_ADDR    ({ memstart_addr - PAGE_OFFSET - 1; })
>> -#define MIN_MEMBLOCK_ADDR    __pa(KIMAGE_VADDR)
>> +#define MIN_MEMBLOCK_ADDR    0
>> +#define MAX_MEMBLOCK_ADDR    U64_MAX
>
> Just in case we get some random memblock information, shall we cap the
> maximum to PHYS_MASK?
>

Yes, that makes sense.

>> diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
>> index b3b0175d7135..29a7dc5327b6 100644
>> --- a/arch/arm64/mm/init.c
>> +++ b/arch/arm64/mm/init.c
>> @@ -158,9 +159,55 @@ static int __init early_mem(char *p)
>>  }
>>  early_param("mem", early_mem);
>>
>> +static void __init enforce_memory_limit(void)
>> +{
>> +     const phys_addr_t kbase = round_down(__pa(_text), MIN_KIMG_ALIGN);
>> +     u64 to_remove = memblock_phys_mem_size() - memory_limit;
>> +     phys_addr_t max_addr = 0;
>> +     struct memblock_region *r;
>> +
>> +     if (memory_limit == (phys_addr_t)ULLONG_MAX)
>> +             return;
>> +
>> +     /*
>> +      * The kernel may be high up in physical memory, so try to apply the
>> +      * limit below the kernel first, and only let the generic handling
>> +      * take over if it turns out we haven't clipped enough memory yet.
>> +      */
>> +     for_each_memblock(memory, r) {
>> +             if (r->base + r->size > kbase) {
>> +                     u64 rem = min(to_remove, kbase - r->base);
>> +
>> +                     max_addr = r->base + rem;
>> +                     to_remove -= rem;
>> +                     break;
>> +             }
>> +             if (to_remove <= r->size) {
>> +                     max_addr = r->base + to_remove;
>> +                     to_remove = 0;
>> +                     break;
>> +             }
>> +             to_remove -= r->size;
>> +     }
>> +
>> +     memblock_remove(0, max_addr);
>
> I don't fully get the reason for this function. Do you want to keep the
> kernel around in memblock? How do we guarantee that the call below
> wouldn't remove it anyway?
>

The problem is that the ordinary memblock_enforce_memory_limit()
removes memory from the top, which means it will happily remove the
memory that covers your kernel image if it happens to be loaded high
up in physical memory.

>> +
>> +     if (to_remove)
>> +             memblock_enforce_memory_limit(memory_limit);
>
> Shouldn't this be memblock_enforce_memory_limit(to_remove)?
>

No, it takes the memory limit as input. 'to_remove + memory_limit'
will be exactly the remaining memory at this point.

>> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
>> index 526eeb7e1e97..1b9d7e48ba1e 100644
>> --- a/arch/arm64/mm/mmu.c
>> +++ b/arch/arm64/mm/mmu.c
>
>> @@ -481,11 +482,33 @@ static void __init bootstrap_linear_mapping(unsigned long va_offset)
>>  static void __init map_mem(void)
>>  {
>>       struct memblock_region *reg;
>> +     u64 new_memstart_addr;
>> +     u64 new_va_offset;
>>
>> -     bootstrap_linear_mapping(KIMAGE_OFFSET);
>> +     /*
>> +      * Select a suitable value for the base of physical memory.
>> +      * This should be equal to or below the lowest usable physical
>> +      * memory address, and aligned to PUD/PMD size so that we can map
>> +      * it efficiently.
>> +      */
>> +     new_memstart_addr = round_down(memblock_start_of_DRAM(), SZ_1G);
>
> With this trick, we can no longer assume we have a mapping at
> PAGE_OFFSET. I don't think we break any expectations but we probably
> don't free the unused memmap at the beginning. We can probably set
> prev_end to this rounded down address in free_unused_memmap().
>

I will look into that.



More information about the linux-arm-kernel mailing list