diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index 5628289b9d5e..507812ebc910 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -530,6 +530,13 @@ extern pgd_t tramp_pg_dir[PTRS_PER_PGD]; extern void set_swapper_pgd(pgd_t *pgdp, pgd_t pgd); +#ifdef CONFIG_MEMORY_HOTPLUG +extern int populate_range_driver_managed(u64 start, u64 size, + const char *resource_name); +extern int depopulate_range_driver_managed(u64 start, u64 size, + const char *resource_name); +#endif + static inline bool in_swapper_pgdir(void *addr) { return ((unsigned long)addr & PAGE_MASK) == diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 567021f15e7d..66f3367dc0a3 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -1496,6 +1496,71 @@ void arch_remove_memory(int nid, u64 start, u64 size, __remove_pgd_mapping(swapper_pg_dir, __phys_to_virt(start), size); } +int check_range_driver_managed(u64 start, u64 size, const char *resource_name) +{ + struct mem_section *ms; + unsigned long pfn = __phys_to_pfn(start); + unsigned long end_pfn = __phys_to_pfn(start + size); + struct resource *res; + unsigned long flags; + + res = lookup_resource(&iomem_resource, start); + if (!res) { + pr_err("%s: couldn't find memory resource for start 0x%lx\n", + __func__, start); + return -EINVAL; + } + + flags = res->flags; + + if (!(flags & IORESOURCE_SYSRAM_DRIVER_MANAGED) || + strstr(resource_name, "System RAM (") != resource_name) + return -EINVAL; + + for (; pfn < end_pfn; pfn += PAGES_PER_SECTION) { + ms = __pfn_to_section(pfn); + if (early_section(ms)) + return -EINVAL; + } + + return 0; +} + +int populate_range_driver_managed(u64 start, u64 size, + const char *resource_name) +{ + unsigned long virt = (unsigned long)phys_to_virt(start); + int flags = 0; + + if (check_range_driver_managed(start, size, resource_name)) + return -EINVAL; + + /* + * When rodata_full is enabled, memory is mapped at page size granule, + * as opposed to block mapping. + */ + if (rodata_full || debug_pagealloc_enabled()) + flags = NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS; + + __create_pgd_mapping(init_mm.pgd, start, virt, size, + PAGE_KERNEL, NULL, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(populate_range_driver_managed); + +int depopulate_range_driver_managed(u64 start, u64 size, + const char *resource_name) +{ + if (check_range_driver_managed(start, size, resource_name)) + return -EINVAL; + + unmap_hotplug_range(start, start + size, false, NULL); + + return 0; +} +EXPORT_SYMBOL_GPL(depopulate_range_driver_managed); + /* * This memory hotplug notifier helps prevent boot memory from being * inadvertently removed as it blocks pfn range offlining process in