| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) 1995 Linus Torvalds |
| */ |
| |
| /* 2.3.x zone allocator, 1999 Andrea Arcangeli <andrea@suse.de> */ |
| |
| #include <linux/mm.h> |
| #include <linux/swap.h> |
| #include <linux/memblock.h> |
| #include <linux/swiotlb.h> |
| #include <linux/acpi.h> |
| #include <linux/memory.h> |
| |
| #include <asm/mmu_context.h> |
| |
| struct mem_desc_t mem_desc; |
| #ifndef CONFIG_NUMA |
| struct numa_node_desc_t numa_nodes_desc[1]; |
| #endif /* CONFIG_NUMA */ |
| |
| /* |
| * empty_zero_page is a special page that is used for |
| * zero-initialized data and COW. |
| */ |
| struct page *empty_zero_page; |
| EXPORT_SYMBOL(empty_zero_page); |
| pg_data_t *node_data[MAX_NUMNODES] __read_mostly; |
| EXPORT_SYMBOL(node_data); |
| |
| pgd_t swapper_pg_dir[1024] __attribute__((__aligned__(PAGE_SIZE))); |
| static pud_t vmalloc_pud[1024] __attribute__((__aligned__(PAGE_SIZE))); |
| |
| static phys_addr_t mem_start; |
| static phys_addr_t mem_size_limit; |
| |
| #ifdef CONFIG_MEMORY_HOTPLUG_SPARSE |
| unsigned long memory_block_size_bytes(void) |
| { |
| if (is_in_guest()) |
| return MIN_MEMORY_BLOCK_SIZE_VM_MEMHP; |
| else |
| return MIN_MEMORY_BLOCK_SIZE; |
| } |
| #endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */ |
| |
| static int __init setup_mem_size(char *p) |
| { |
| char *oldp; |
| unsigned long start, size; |
| |
| start = 0; |
| oldp = p; |
| size = memparse(p, &p); |
| if (p == oldp) |
| return -EINVAL; |
| |
| if (*p == '@') |
| start = memparse(p + 1, &p); |
| |
| mem_start = start; |
| mem_size_limit = size; |
| return 0; |
| } |
| early_param("mem", setup_mem_size); |
| |
| pgd_t * |
| pgd_alloc(struct mm_struct *mm) |
| { |
| pgd_t *ret, *init; |
| |
| ret = (pgd_t *)__get_free_page(GFP_KERNEL | __GFP_ZERO); |
| init = pgd_offset(&init_mm, 0UL); |
| if (ret) |
| pgd_val(ret[PTRS_PER_PGD-2]) = pgd_val(init[PTRS_PER_PGD-2]); |
| |
| return ret; |
| } |
| |
| /* Set up initial PCB, VPTB, and other such nicities. */ |
| |
| static inline void |
| switch_to_system_map(void) |
| { |
| memset(swapper_pg_dir, 0, PAGE_SIZE); |
| wrptbr(virt_to_phys(swapper_pg_dir)); |
| tbiv(); |
| } |
| |
| void __init callback_init(void) |
| { |
| pgd_t *pgd; |
| p4d_t *p4d; |
| |
| switch_to_system_map(); |
| |
| /* Allocate one PGD and one PUD. */ |
| pgd = pgd_offset_k(VMALLOC_START); |
| p4d = p4d_offset(pgd, VMALLOC_START); |
| p4d_set(p4d, (pud_t *)vmalloc_pud); |
| } |
| |
| void __init zone_sizes_init(void) |
| { |
| unsigned long max_zone_pfns[MAX_NR_ZONES]; |
| unsigned long dma_pfn; |
| |
| memset(max_zone_pfns, 0, sizeof(max_zone_pfns)); |
| |
| dma_pfn = PFN_DOWN(virt_to_phys((void *)MAX_DMA_ADDRESS)); |
| |
| #ifdef CONFIG_ZONE_DMA32 |
| max_zone_pfns[ZONE_DMA32] = min(dma_pfn, max_low_pfn); |
| #endif |
| max_zone_pfns[ZONE_NORMAL] = max_low_pfn; |
| |
| free_area_init(max_zone_pfns); |
| } |
| |
| /* |
| * paging_init() sets up the memory map. |
| */ |
| void __init paging_init(void) |
| { |
| void *zero_page; |
| |
| zero_page = __va(memblock_phys_alloc(PAGE_SIZE, PAGE_SIZE)); |
| pr_info("zero page start: %p\n", zero_page); |
| memset(zero_page, 0, PAGE_SIZE); |
| empty_zero_page = virt_to_page(zero_page); |
| } |
| |
| void __init mem_detect(void) |
| { |
| int i; |
| |
| mem_desc.phys_base = 0; |
| for (i = 0; i < MAX_NUMSOCKETS; i++) { |
| if (socket_desc[i].is_online) |
| mem_desc.phys_size += socket_desc[i].socket_mem; |
| } |
| |
| if (mem_start >= NODE0_START) { |
| mem_desc.base = mem_start; |
| } else { |
| mem_desc.base = NODE0_START; |
| mem_size_limit -= NODE0_START - mem_start; |
| } |
| |
| if (mem_size_limit && mem_size_limit < mem_desc.phys_size - NODE0_START) |
| mem_desc.size = mem_size_limit; |
| else |
| mem_desc.size = mem_desc.phys_size - NODE0_START; |
| } |
| |
| void __init sw64_memblock_init(void) |
| { |
| memblock_add(mem_desc.base, mem_desc.size); |
| |
| memblock_remove(1ULL << MAX_PHYSMEM_BITS, PHYS_ADDR_MAX); |
| |
| max_pfn = max_low_pfn = PFN_DOWN(memblock_end_of_DRAM()); |
| |
| memblock_allow_resize(); |
| memblock_initialized = true; |
| process_memmap(); |
| |
| /* Make sure kernel text is in memory range. */ |
| memblock_add(__pa_symbol(_text), _end - _text); |
| memblock_reserve(__pa_symbol(_text), _end - _text); |
| |
| /* Make sure initrd is in memory range. */ |
| if (sunway_boot_params->initrd_start) { |
| phys_addr_t base = __pa(sunway_boot_params->initrd_start); |
| phys_addr_t size = sunway_boot_params->initrd_size; |
| |
| memblock_add(base, size); |
| memblock_reserve(base, size); |
| } |
| |
| /* end of DRAM range may have been changed */ |
| max_pfn = max_low_pfn = PFN_DOWN(memblock_end_of_DRAM()); |
| } |
| |
| #ifndef CONFIG_NUMA |
| void __init sw64_numa_init(void) |
| { |
| const size_t nd_size = roundup(sizeof(pg_data_t), SMP_CACHE_BYTES); |
| u64 nd_pa; |
| void *nd; |
| int tnid; |
| |
| memblock_set_node(mem_desc.base, mem_desc.size, &memblock.memory, 0); |
| nd_pa = memblock_phys_alloc(nd_size, SMP_CACHE_BYTES); |
| nd = __va(nd_pa); |
| |
| /* report and initialize */ |
| pr_info("NODE_DATA [mem %#018llx-%#018llx]\n", |
| nd_pa, nd_pa + nd_size - 1); |
| tnid = early_pfn_to_nid(nd_pa >> PAGE_SHIFT); |
| if (tnid != 0) |
| pr_info("NODE_DATA(%d) on node %d\n", 0, tnid); |
| |
| node_data[0] = nd; |
| memset(NODE_DATA(0), 0, sizeof(pg_data_t)); |
| NODE_DATA(0)->node_id = 0; |
| NODE_DATA(0)->node_start_pfn = mem_desc.base >> PAGE_SHIFT; |
| NODE_DATA(0)->node_spanned_pages = mem_desc.size >> PAGE_SHIFT; |
| node_set_online(0); |
| } |
| #endif /* CONFIG_NUMA */ |
| |
| void __init |
| mem_init(void) |
| { |
| set_max_mapnr(max_low_pfn); |
| high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); |
| #ifdef CONFIG_SWIOTLB |
| swiotlb_init(1); |
| #endif |
| memblock_free_all(); |
| mem_init_print_info(NULL); |
| } |
| |
| #ifdef CONFIG_SPARSEMEM_VMEMMAP |
| int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, |
| struct vmem_altmap *altmap) |
| { |
| return vmemmap_populate_basepages(start, end, node, altmap); |
| } |
| |
| void vmemmap_free(unsigned long start, unsigned long end, |
| struct vmem_altmap *altmap) |
| { |
| } |
| #endif |
| |
| #ifdef CONFIG_HAVE_MEMBLOCK |
| #ifndef MIN_MEMBLOCK_ADDR |
| #define MIN_MEMBLOCK_ADDR __pa(PAGE_OFFSET) |
| #endif |
| #ifndef MAX_MEMBLOCK_ADDR |
| #define MAX_MEMBLOCK_ADDR ((phys_addr_t)~0) |
| #endif |
| void __init early_init_dt_add_memory_arch(u64 base, u64 size) |
| { |
| const u64 phys_offset = MIN_MEMBLOCK_ADDR; |
| |
| if (acpi_disabled) { |
| if (!PAGE_ALIGNED(base)) { |
| if (size < PAGE_SIZE - (base & ~PAGE_MASK)) { |
| pr_warn("Ignoring memory block 0x%llx - 0x%llx\n", |
| base, base + size); |
| return; |
| } |
| size -= PAGE_SIZE - (base & ~PAGE_MASK); |
| base = PAGE_ALIGN(base); |
| } |
| size &= PAGE_MASK; |
| |
| if (base > MAX_MEMBLOCK_ADDR) { |
| pr_warn("Ignoring memory block 0x%llx - 0x%llx\n", |
| base, base + size); |
| return; |
| } |
| |
| if (base + size - 1 > MAX_MEMBLOCK_ADDR) { |
| pr_warn("Ignoring memory range 0x%llx - 0x%llx\n", |
| ((u64)MAX_MEMBLOCK_ADDR) + 1, base + size); |
| size = MAX_MEMBLOCK_ADDR - base + 1; |
| } |
| |
| if (base + size < phys_offset) { |
| pr_warn("Ignoring memory block 0x%llx - 0x%llx\n", |
| base, base + size); |
| return; |
| } |
| |
| if (base < phys_offset) { |
| pr_warn("Ignoring memory range 0x%llx - 0x%llx\n", |
| base, phys_offset); |
| size -= phys_offset - base; |
| base = phys_offset; |
| } |
| memblock_add(base, size); |
| } else |
| return; |
| } |
| #endif |
| |
| #ifdef CONFIG_MEMORY_HOTPLUG |
| int arch_add_memory(int nid, u64 start, u64 size, struct mhp_params *params) |
| { |
| unsigned long start_pfn = start >> PAGE_SHIFT; |
| unsigned long nr_pages = size >> PAGE_SHIFT; |
| int ret; |
| |
| ret = __add_pages(nid, start_pfn, nr_pages, params); |
| if (ret) |
| printk("%s: Problem encountered in __add_pages() as ret=%d\n", |
| __func__, ret); |
| |
| return ret; |
| } |
| |
| void arch_remove_memory(int nid, u64 start, u64 size, |
| struct vmem_altmap *altmap) |
| { |
| unsigned long start_pfn = start >> PAGE_SHIFT; |
| unsigned long nr_pages = size >> PAGE_SHIFT; |
| |
| __remove_pages(start_pfn, nr_pages, altmap); |
| } |
| #endif |