Add expected I/O regions and provide a way to check memory region properties of a physical address. We also bump the initial number of regions and even prepare for a unit test to reallocate for growth if necessary. Signed-off-by: Andrew Jones <drjones@xxxxxxxxxx> --- lib/arm/asm/setup.h | 8 +++++-- lib/arm/mmu.c | 24 ++++++------------- lib/arm/setup.c | 56 +++++++++++++++++++++++++++++++++------------ 3 files changed, 55 insertions(+), 33 deletions(-) diff --git a/lib/arm/asm/setup.h b/lib/arm/asm/setup.h index 81cac019b1d1..c8afb2493f8d 100644 --- a/lib/arm/asm/setup.h +++ b/lib/arm/asm/setup.h @@ -13,16 +13,20 @@ extern u64 cpus[NR_CPUS]; /* per-cpu IDs (MPIDRs) */ extern int nr_cpus; -#define NR_MEM_REGIONS 8 #define MR_F_PRIMARY (1U << 0) +#define MR_F_IO (1U << 1) +#define MR_F_UNKNOWN (1U << 31) + struct mem_region { phys_addr_t start; phys_addr_t end; unsigned int flags; }; -extern struct mem_region mem_regions[NR_MEM_REGIONS]; +extern struct mem_region *mem_regions; extern phys_addr_t __phys_offset, __phys_end; +extern unsigned int mem_region_get_flags(phys_addr_t paddr); + #define PHYS_OFFSET (__phys_offset) #define PHYS_END (__phys_end) diff --git a/lib/arm/mmu.c b/lib/arm/mmu.c index 5fb56180d334..540a1e842d5b 100644 --- a/lib/arm/mmu.c +++ b/lib/arm/mmu.c @@ -152,6 +152,7 @@ void mmu_set_range_sect(pgd_t *pgtable, uintptr_t virt_offset, void *setup_mmu(phys_addr_t phys_end) { uintptr_t code_end = (uintptr_t)&etext; + struct mem_region *r; /* 0G-1G = I/O, 1G-3G = identity, 3G-4G = vmalloc */ if (phys_end > (3ul << 30)) @@ -163,23 +164,12 @@ void *setup_mmu(phys_addr_t phys_end) mmu_idmap = alloc_page(); - /* - * mach-virt I/O regions: - * - The first 1G (arm/arm64) - * - 512M at 256G (arm64, arm uses highmem=off) - * - 512G at 512G (arm64, arm uses highmem=off) - */ - mmu_set_range_sect(mmu_idmap, - 0, 0, (1ul << 30), - __pgprot(PMD_SECT_UNCACHED | PMD_SECT_USER)); -#ifdef __aarch64__ - mmu_set_range_sect(mmu_idmap, - (1ul << 38), (1ul << 38), (1ul << 38) | (1ul << 29), - __pgprot(PMD_SECT_UNCACHED | PMD_SECT_USER)); - mmu_set_range_sect(mmu_idmap, - (1ul << 39), (1ul << 39), (1ul << 40), - __pgprot(PMD_SECT_UNCACHED | PMD_SECT_USER)); -#endif + for (r = mem_regions; r->end; ++r) { + if (!(r->flags & MR_F_IO)) + continue; + mmu_set_range_sect(mmu_idmap, r->start, r->start, r->end, + __pgprot(PMD_SECT_UNCACHED | PMD_SECT_USER)); + } /* armv8 requires code shared between EL1 and EL0 to be read-only */ mmu_set_range_ptes(mmu_idmap, PHYS_OFFSET, diff --git a/lib/arm/setup.c b/lib/arm/setup.c index 4f02fca85607..385e135f4865 100644 --- a/lib/arm/setup.c +++ b/lib/arm/setup.c @@ -24,6 +24,8 @@ #include "io.h" +#define NR_INITIAL_MEM_REGIONS 16 + extern unsigned long stacktop; char *initrd; @@ -32,7 +34,8 @@ u32 initrd_size; u64 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (u64)~0 }; int nr_cpus; -struct mem_region mem_regions[NR_MEM_REGIONS]; +static struct mem_region __initial_mem_regions[NR_INITIAL_MEM_REGIONS + 1]; +struct mem_region *mem_regions = __initial_mem_regions; phys_addr_t __phys_offset, __phys_end; int mpidr_to_cpu(uint64_t mpidr) @@ -65,41 +68,66 @@ static void cpu_init(void) set_cpu_online(0, true); } +unsigned int mem_region_get_flags(phys_addr_t paddr) +{ + struct mem_region *r; + + for (r = mem_regions; r->end; ++r) { + if (paddr >= r->start && paddr < r->end) + return r->flags; + } + + return MR_F_UNKNOWN; +} + static void mem_init(phys_addr_t freemem_start) { - struct dt_pbus_reg regs[NR_MEM_REGIONS]; + struct dt_pbus_reg regs[NR_INITIAL_MEM_REGIONS]; struct mem_region primary, mem = { .start = (phys_addr_t)-1, }; phys_addr_t base, top; - int nr_regs, i; + int nr_regs, nr_io = 0, i; - nr_regs = dt_get_memory_params(regs, NR_MEM_REGIONS); + /* + * mach-virt I/O regions: + * - The first 1G (arm/arm64) + * - 512M at 256G (arm64, arm uses highmem=off) + * - 512G at 512G (arm64, arm uses highmem=off) + */ + mem_regions[nr_io++] = (struct mem_region){ 0, (1ul << 30), MR_F_IO }; +#ifdef __aarch64__ + mem_regions[nr_io++] = (struct mem_region){ (1ul << 38), (1ul << 38) | (1ul << 29), MR_F_IO }; + mem_regions[nr_io++] = (struct mem_region){ (1ul << 39), (1ul << 40), MR_F_IO }; +#endif + + nr_regs = dt_get_memory_params(regs, NR_INITIAL_MEM_REGIONS - nr_io); assert(nr_regs > 0); primary = (struct mem_region){ 0 }; for (i = 0; i < nr_regs; ++i) { - mem_regions[i].start = regs[i].addr; - mem_regions[i].end = regs[i].addr + regs[i].size; + struct mem_region *r = &mem_regions[nr_io + i]; + + r->start = regs[i].addr; + r->end = regs[i].addr + regs[i].size; /* * pick the region we're in for our primary region */ - if (freemem_start >= mem_regions[i].start - && freemem_start < mem_regions[i].end) { - mem_regions[i].flags |= MR_F_PRIMARY; - primary = mem_regions[i]; + if (freemem_start >= r->start && freemem_start < r->end) { + r->flags |= MR_F_PRIMARY; + primary = *r; } /* * set the lowest and highest addresses found, * ignoring potential gaps */ - if (mem_regions[i].start < mem.start) - mem.start = mem_regions[i].start; - if (mem_regions[i].end > mem.end) - mem.end = mem_regions[i].end; + if (r->start < mem.start) + mem.start = r->start; + if (r->end > mem.end) + mem.end = r->end; } assert(primary.end != 0); assert(!(mem.start & ~PHYS_MASK) && !((mem.end - 1) & ~PHYS_MASK)); -- 2.21.1