338 lines
7.7 KiB
C
338 lines
7.7 KiB
C
// 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 <asm/mmu_context.h>
|
|
|
|
extern void die_if_kernel(char *, struct pt_regs *, long);
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
static inline unsigned long
|
|
load_PCB(struct pcb_struct *pcb)
|
|
{
|
|
register unsigned long sp __asm__("$30");
|
|
pcb->ksp = sp;
|
|
return __reload_thread(pcb);
|
|
}
|
|
|
|
/* Set up initial PCB, VPTB, and other such nicities. */
|
|
|
|
static inline void
|
|
switch_to_system_map(void)
|
|
{
|
|
unsigned long newptbr;
|
|
unsigned long original_pcb_ptr;
|
|
|
|
/*
|
|
* Initialize the kernel's page tables. Linux puts the vptb in
|
|
* the last slot of the L1 page table.
|
|
*/
|
|
memset(swapper_pg_dir, 0, PAGE_SIZE);
|
|
newptbr = __pa(swapper_pg_dir) >> PAGE_SHIFT;
|
|
|
|
/* Also set up the real kernel PCB while we're at it. */
|
|
init_thread_info.pcb.ptbr = newptbr;
|
|
init_thread_info.pcb.flags = 1; /* set FEN, clear everything else */
|
|
original_pcb_ptr = load_PCB(&init_thread_info.pcb);
|
|
tbia();
|
|
}
|
|
|
|
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_DISCONTIGMEM
|
|
int pfn_valid(unsigned long pfn)
|
|
{
|
|
phys_addr_t addr = pfn << PAGE_SHIFT;
|
|
|
|
if ((addr >> PAGE_SHIFT) != pfn)
|
|
return 0;
|
|
return memblock_is_map_memory(addr);
|
|
}
|
|
EXPORT_SYMBOL(pfn_valid);
|
|
#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
|