xref: /kernel/linux/linux-6.6/arch/loongarch/mm/init.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/init.h>
662306a36Sopenharmony_ci#include <linux/export.h>
762306a36Sopenharmony_ci#include <linux/signal.h>
862306a36Sopenharmony_ci#include <linux/sched.h>
962306a36Sopenharmony_ci#include <linux/smp.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/string.h>
1362306a36Sopenharmony_ci#include <linux/types.h>
1462306a36Sopenharmony_ci#include <linux/pagemap.h>
1562306a36Sopenharmony_ci#include <linux/memblock.h>
1662306a36Sopenharmony_ci#include <linux/memremap.h>
1762306a36Sopenharmony_ci#include <linux/mm.h>
1862306a36Sopenharmony_ci#include <linux/mman.h>
1962306a36Sopenharmony_ci#include <linux/highmem.h>
2062306a36Sopenharmony_ci#include <linux/swap.h>
2162306a36Sopenharmony_ci#include <linux/proc_fs.h>
2262306a36Sopenharmony_ci#include <linux/pfn.h>
2362306a36Sopenharmony_ci#include <linux/hardirq.h>
2462306a36Sopenharmony_ci#include <linux/gfp.h>
2562306a36Sopenharmony_ci#include <linux/hugetlb.h>
2662306a36Sopenharmony_ci#include <linux/mmzone.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <asm/asm-offsets.h>
2962306a36Sopenharmony_ci#include <asm/bootinfo.h>
3062306a36Sopenharmony_ci#include <asm/cpu.h>
3162306a36Sopenharmony_ci#include <asm/dma.h>
3262306a36Sopenharmony_ci#include <asm/mmu_context.h>
3362306a36Sopenharmony_ci#include <asm/sections.h>
3462306a36Sopenharmony_ci#include <asm/pgtable.h>
3562306a36Sopenharmony_ci#include <asm/pgalloc.h>
3662306a36Sopenharmony_ci#include <asm/tlb.h>
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ciunsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)] __page_aligned_bss;
3962306a36Sopenharmony_ciEXPORT_SYMBOL(empty_zero_page);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_civoid copy_user_highpage(struct page *to, struct page *from,
4262306a36Sopenharmony_ci	unsigned long vaddr, struct vm_area_struct *vma)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	void *vfrom, *vto;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	vfrom = kmap_local_page(from);
4762306a36Sopenharmony_ci	vto = kmap_local_page(to);
4862306a36Sopenharmony_ci	copy_page(vto, vfrom);
4962306a36Sopenharmony_ci	kunmap_local(vfrom);
5062306a36Sopenharmony_ci	kunmap_local(vto);
5162306a36Sopenharmony_ci	/* Make sure this page is cleared on other CPU's too before using it */
5262306a36Sopenharmony_ci	smp_wmb();
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ciint __ref page_is_ram(unsigned long pfn)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	unsigned long addr = PFN_PHYS(pfn);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	return memblock_is_memory(addr) && !memblock_is_reserved(addr);
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#ifndef CONFIG_NUMA
6362306a36Sopenharmony_civoid __init paging_init(void)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	unsigned long max_zone_pfns[MAX_NR_ZONES];
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#ifdef CONFIG_ZONE_DMA
6862306a36Sopenharmony_ci	max_zone_pfns[ZONE_DMA] = MAX_DMA_PFN;
6962306a36Sopenharmony_ci#endif
7062306a36Sopenharmony_ci#ifdef CONFIG_ZONE_DMA32
7162306a36Sopenharmony_ci	max_zone_pfns[ZONE_DMA32] = MAX_DMA32_PFN;
7262306a36Sopenharmony_ci#endif
7362306a36Sopenharmony_ci	max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	free_area_init(max_zone_pfns);
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_civoid __init mem_init(void)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	max_mapnr = max_low_pfn;
8162306a36Sopenharmony_ci	high_memory = (void *) __va(max_low_pfn << PAGE_SHIFT);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	memblock_free_all();
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci#endif /* !CONFIG_NUMA */
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_civoid __ref free_initmem(void)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	free_initmem_default(POISON_FREE_INITMEM);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci#ifdef CONFIG_MEMORY_HOTPLUG
9362306a36Sopenharmony_ciint arch_add_memory(int nid, u64 start, u64 size, struct mhp_params *params)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	unsigned long start_pfn = start >> PAGE_SHIFT;
9662306a36Sopenharmony_ci	unsigned long nr_pages = size >> PAGE_SHIFT;
9762306a36Sopenharmony_ci	int ret;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	ret = __add_pages(nid, start_pfn, nr_pages, params);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (ret)
10262306a36Sopenharmony_ci		pr_warn("%s: Problem encountered in __add_pages() as ret=%d\n",
10362306a36Sopenharmony_ci				__func__,  ret);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return ret;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_civoid arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	unsigned long start_pfn = start >> PAGE_SHIFT;
11162306a36Sopenharmony_ci	unsigned long nr_pages = size >> PAGE_SHIFT;
11262306a36Sopenharmony_ci	struct page *page = pfn_to_page(start_pfn);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* With altmap the first mapped page is offset from @start */
11562306a36Sopenharmony_ci	if (altmap)
11662306a36Sopenharmony_ci		page += vmem_altmap_offset(altmap);
11762306a36Sopenharmony_ci	__remove_pages(start_pfn, nr_pages, altmap);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci#ifdef CONFIG_NUMA
12162306a36Sopenharmony_ciint memory_add_physaddr_to_nid(u64 start)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	return pa_to_nid(start);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(memory_add_physaddr_to_nid);
12662306a36Sopenharmony_ci#endif
12762306a36Sopenharmony_ci#endif
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci#ifdef CONFIG_SPARSEMEM_VMEMMAP
13062306a36Sopenharmony_civoid __meminit vmemmap_set_pmd(pmd_t *pmd, void *p, int node,
13162306a36Sopenharmony_ci			       unsigned long addr, unsigned long next)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	pmd_t entry;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	entry = pfn_pmd(virt_to_pfn(p), PAGE_KERNEL);
13662306a36Sopenharmony_ci	pmd_val(entry) |= _PAGE_HUGE | _PAGE_HGLOBAL;
13762306a36Sopenharmony_ci	set_pmd_at(&init_mm, addr, pmd, entry);
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ciint __meminit vmemmap_check_pmd(pmd_t *pmd, int node,
14162306a36Sopenharmony_ci				unsigned long addr, unsigned long next)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	int huge = pmd_val(*pmd) & _PAGE_HUGE;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (huge)
14662306a36Sopenharmony_ci		vmemmap_verify((pte_t *)pmd, node, addr, next);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	return huge;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ciint __meminit vmemmap_populate(unsigned long start, unsigned long end,
15262306a36Sopenharmony_ci			       int node, struct vmem_altmap *altmap)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS == 2
15562306a36Sopenharmony_ci	return vmemmap_populate_basepages(start, end, node, NULL);
15662306a36Sopenharmony_ci#else
15762306a36Sopenharmony_ci	return vmemmap_populate_hugepages(start, end, node, NULL);
15862306a36Sopenharmony_ci#endif
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci#ifdef CONFIG_MEMORY_HOTPLUG
16262306a36Sopenharmony_civoid vmemmap_free(unsigned long start, unsigned long end, struct vmem_altmap *altmap)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci#endif
16662306a36Sopenharmony_ci#endif
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cipte_t * __init populate_kernel_pte(unsigned long addr)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	pgd_t *pgd = pgd_offset_k(addr);
17162306a36Sopenharmony_ci	p4d_t *p4d = p4d_offset(pgd, addr);
17262306a36Sopenharmony_ci	pud_t *pud;
17362306a36Sopenharmony_ci	pmd_t *pmd;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (p4d_none(*p4d)) {
17662306a36Sopenharmony_ci		pud = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
17762306a36Sopenharmony_ci		if (!pud)
17862306a36Sopenharmony_ci			panic("%s: Failed to allocate memory\n", __func__);
17962306a36Sopenharmony_ci		p4d_populate(&init_mm, p4d, pud);
18062306a36Sopenharmony_ci#ifndef __PAGETABLE_PUD_FOLDED
18162306a36Sopenharmony_ci		pud_init(pud);
18262306a36Sopenharmony_ci#endif
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	pud = pud_offset(p4d, addr);
18662306a36Sopenharmony_ci	if (pud_none(*pud)) {
18762306a36Sopenharmony_ci		pmd = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
18862306a36Sopenharmony_ci		if (!pmd)
18962306a36Sopenharmony_ci			panic("%s: Failed to allocate memory\n", __func__);
19062306a36Sopenharmony_ci		pud_populate(&init_mm, pud, pmd);
19162306a36Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED
19262306a36Sopenharmony_ci		pmd_init(pmd);
19362306a36Sopenharmony_ci#endif
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	pmd = pmd_offset(pud, addr);
19762306a36Sopenharmony_ci	if (!pmd_present(*pmd)) {
19862306a36Sopenharmony_ci		pte_t *pte;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci		pte = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
20162306a36Sopenharmony_ci		if (!pte)
20262306a36Sopenharmony_ci			panic("%s: Failed to allocate memory\n", __func__);
20362306a36Sopenharmony_ci		pmd_populate_kernel(&init_mm, pmd, pte);
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	return pte_offset_kernel(pmd, addr);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_civoid __init __set_fixmap(enum fixed_addresses idx,
21062306a36Sopenharmony_ci			       phys_addr_t phys, pgprot_t flags)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	unsigned long addr = __fix_to_virt(idx);
21362306a36Sopenharmony_ci	pte_t *ptep;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	BUG_ON(idx <= FIX_HOLE || idx >= __end_of_fixed_addresses);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	ptep = populate_kernel_pte(addr);
21862306a36Sopenharmony_ci	if (!pte_none(*ptep)) {
21962306a36Sopenharmony_ci		pte_ERROR(*ptep);
22062306a36Sopenharmony_ci		return;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (pgprot_val(flags))
22462306a36Sopenharmony_ci		set_pte(ptep, pfn_pte(phys >> PAGE_SHIFT, flags));
22562306a36Sopenharmony_ci	else {
22662306a36Sopenharmony_ci		pte_clear(&init_mm, addr, ptep);
22762306a36Sopenharmony_ci		flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci/*
23262306a36Sopenharmony_ci * Align swapper_pg_dir in to 64K, allows its address to be loaded
23362306a36Sopenharmony_ci * with a single LUI instruction in the TLB handlers.  If we used
23462306a36Sopenharmony_ci * __aligned(64K), its size would get rounded up to the alignment
23562306a36Sopenharmony_ci * size, and waste space.  So we place it in its own section and align
23662306a36Sopenharmony_ci * it in the linker script.
23762306a36Sopenharmony_ci */
23862306a36Sopenharmony_cipgd_t swapper_pg_dir[_PTRS_PER_PGD] __section(".bss..swapper_pg_dir");
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cipgd_t invalid_pg_dir[_PTRS_PER_PGD] __page_aligned_bss;
24162306a36Sopenharmony_ci#ifndef __PAGETABLE_PUD_FOLDED
24262306a36Sopenharmony_cipud_t invalid_pud_table[PTRS_PER_PUD] __page_aligned_bss;
24362306a36Sopenharmony_ciEXPORT_SYMBOL(invalid_pud_table);
24462306a36Sopenharmony_ci#endif
24562306a36Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED
24662306a36Sopenharmony_cipmd_t invalid_pmd_table[PTRS_PER_PMD] __page_aligned_bss;
24762306a36Sopenharmony_ciEXPORT_SYMBOL(invalid_pmd_table);
24862306a36Sopenharmony_ci#endif
24962306a36Sopenharmony_cipte_t invalid_pte_table[PTRS_PER_PTE] __page_aligned_bss;
25062306a36Sopenharmony_ciEXPORT_SYMBOL(invalid_pte_table);
251