162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * S390 version 462306a36Sopenharmony_ci * Copyright IBM Corp. 1999 562306a36Sopenharmony_ci * Author(s): Hartmut Penner (hp@de.ibm.com) 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Derived from "arch/i386/mm/init.c" 862306a36Sopenharmony_ci * Copyright (C) 1995 Linus Torvalds 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/signal.h> 1262306a36Sopenharmony_ci#include <linux/sched.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/errno.h> 1562306a36Sopenharmony_ci#include <linux/string.h> 1662306a36Sopenharmony_ci#include <linux/types.h> 1762306a36Sopenharmony_ci#include <linux/ptrace.h> 1862306a36Sopenharmony_ci#include <linux/mman.h> 1962306a36Sopenharmony_ci#include <linux/mm.h> 2062306a36Sopenharmony_ci#include <linux/swap.h> 2162306a36Sopenharmony_ci#include <linux/swiotlb.h> 2262306a36Sopenharmony_ci#include <linux/smp.h> 2362306a36Sopenharmony_ci#include <linux/init.h> 2462306a36Sopenharmony_ci#include <linux/pagemap.h> 2562306a36Sopenharmony_ci#include <linux/memblock.h> 2662306a36Sopenharmony_ci#include <linux/memory.h> 2762306a36Sopenharmony_ci#include <linux/pfn.h> 2862306a36Sopenharmony_ci#include <linux/poison.h> 2962306a36Sopenharmony_ci#include <linux/initrd.h> 3062306a36Sopenharmony_ci#include <linux/export.h> 3162306a36Sopenharmony_ci#include <linux/cma.h> 3262306a36Sopenharmony_ci#include <linux/gfp.h> 3362306a36Sopenharmony_ci#include <linux/dma-direct.h> 3462306a36Sopenharmony_ci#include <linux/percpu.h> 3562306a36Sopenharmony_ci#include <asm/processor.h> 3662306a36Sopenharmony_ci#include <linux/uaccess.h> 3762306a36Sopenharmony_ci#include <asm/pgalloc.h> 3862306a36Sopenharmony_ci#include <asm/kfence.h> 3962306a36Sopenharmony_ci#include <asm/ptdump.h> 4062306a36Sopenharmony_ci#include <asm/dma.h> 4162306a36Sopenharmony_ci#include <asm/abs_lowcore.h> 4262306a36Sopenharmony_ci#include <asm/tlb.h> 4362306a36Sopenharmony_ci#include <asm/tlbflush.h> 4462306a36Sopenharmony_ci#include <asm/sections.h> 4562306a36Sopenharmony_ci#include <asm/ctl_reg.h> 4662306a36Sopenharmony_ci#include <asm/sclp.h> 4762306a36Sopenharmony_ci#include <asm/set_memory.h> 4862306a36Sopenharmony_ci#include <asm/kasan.h> 4962306a36Sopenharmony_ci#include <asm/dma-mapping.h> 5062306a36Sopenharmony_ci#include <asm/uv.h> 5162306a36Sopenharmony_ci#include <linux/virtio_anchor.h> 5262306a36Sopenharmony_ci#include <linux/virtio_config.h> 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cipgd_t swapper_pg_dir[PTRS_PER_PGD] __section(".bss..swapper_pg_dir"); 5562306a36Sopenharmony_cipgd_t invalid_pg_dir[PTRS_PER_PGD] __section(".bss..invalid_pg_dir"); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ciunsigned long __bootdata_preserved(s390_invalid_asce); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ciunsigned long empty_zero_page, zero_page_mask; 6062306a36Sopenharmony_ciEXPORT_SYMBOL(empty_zero_page); 6162306a36Sopenharmony_ciEXPORT_SYMBOL(zero_page_mask); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void __init setup_zero_pages(void) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci unsigned int order; 6662306a36Sopenharmony_ci struct page *page; 6762306a36Sopenharmony_ci int i; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* Latest machines require a mapping granularity of 512KB */ 7062306a36Sopenharmony_ci order = 7; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* Limit number of empty zero pages for small memory sizes */ 7362306a36Sopenharmony_ci while (order > 2 && (totalram_pages() >> 10) < (1UL << order)) 7462306a36Sopenharmony_ci order--; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci empty_zero_page = __get_free_pages(GFP_KERNEL | __GFP_ZERO, order); 7762306a36Sopenharmony_ci if (!empty_zero_page) 7862306a36Sopenharmony_ci panic("Out of memory in setup_zero_pages"); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci page = virt_to_page((void *) empty_zero_page); 8162306a36Sopenharmony_ci split_page(page, order); 8262306a36Sopenharmony_ci for (i = 1 << order; i > 0; i--) { 8362306a36Sopenharmony_ci mark_page_reserved(page); 8462306a36Sopenharmony_ci page++; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci zero_page_mask = ((PAGE_SIZE << order) - 1) & PAGE_MASK; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* 9162306a36Sopenharmony_ci * paging_init() sets up the page tables 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_civoid __init paging_init(void) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci unsigned long max_zone_pfns[MAX_NR_ZONES]; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci vmem_map_init(); 9862306a36Sopenharmony_ci sparse_init(); 9962306a36Sopenharmony_ci zone_dma_bits = 31; 10062306a36Sopenharmony_ci memset(max_zone_pfns, 0, sizeof(max_zone_pfns)); 10162306a36Sopenharmony_ci max_zone_pfns[ZONE_DMA] = virt_to_pfn(MAX_DMA_ADDRESS); 10262306a36Sopenharmony_ci max_zone_pfns[ZONE_NORMAL] = max_low_pfn; 10362306a36Sopenharmony_ci free_area_init(max_zone_pfns); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_civoid mark_rodata_ro(void) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci unsigned long size = __end_ro_after_init - __start_ro_after_init; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci __set_memory_ro(__start_ro_after_init, __end_ro_after_init); 11162306a36Sopenharmony_ci pr_info("Write protected read-only-after-init data: %luk\n", size >> 10); 11262306a36Sopenharmony_ci debug_checkwx(); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ciint set_memory_encrypted(unsigned long vaddr, int numpages) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci int i; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* make specified pages unshared, (swiotlb, dma_free) */ 12062306a36Sopenharmony_ci for (i = 0; i < numpages; ++i) { 12162306a36Sopenharmony_ci uv_remove_shared(virt_to_phys((void *)vaddr)); 12262306a36Sopenharmony_ci vaddr += PAGE_SIZE; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci return 0; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ciint set_memory_decrypted(unsigned long vaddr, int numpages) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci int i; 13062306a36Sopenharmony_ci /* make specified pages shared (swiotlb, dma_alloca) */ 13162306a36Sopenharmony_ci for (i = 0; i < numpages; ++i) { 13262306a36Sopenharmony_ci uv_set_shared(virt_to_phys((void *)vaddr)); 13362306a36Sopenharmony_ci vaddr += PAGE_SIZE; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/* are we a protected virtualization guest? */ 13962306a36Sopenharmony_cibool force_dma_unencrypted(struct device *dev) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci return is_prot_virt_guest(); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* protected virtualization */ 14562306a36Sopenharmony_cistatic void pv_init(void) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci if (!is_prot_virt_guest()) 14862306a36Sopenharmony_ci return; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci virtio_set_mem_acc_cb(virtio_require_restricted_mem_acc); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* make sure bounce buffers are shared */ 15362306a36Sopenharmony_ci swiotlb_init(true, SWIOTLB_FORCE | SWIOTLB_VERBOSE); 15462306a36Sopenharmony_ci swiotlb_update_mem_attributes(); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_civoid __init mem_init(void) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci cpumask_set_cpu(0, &init_mm.context.cpu_attach_mask); 16062306a36Sopenharmony_ci cpumask_set_cpu(0, mm_cpumask(&init_mm)); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci set_max_mapnr(max_low_pfn); 16362306a36Sopenharmony_ci high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci pv_init(); 16662306a36Sopenharmony_ci kfence_split_mapping(); 16762306a36Sopenharmony_ci /* Setup guest page hinting */ 16862306a36Sopenharmony_ci cmma_init(); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* this will put all low memory onto the freelists */ 17162306a36Sopenharmony_ci memblock_free_all(); 17262306a36Sopenharmony_ci setup_zero_pages(); /* Setup zeroed pages. */ 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci cmma_init_nodat(); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_civoid free_initmem(void) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci set_memory_rwnx((unsigned long)_sinittext, 18062306a36Sopenharmony_ci (unsigned long)(_einittext - _sinittext) >> PAGE_SHIFT); 18162306a36Sopenharmony_ci free_initmem_default(POISON_FREE_INITMEM); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ciunsigned long memory_block_size_bytes(void) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci /* 18762306a36Sopenharmony_ci * Make sure the memory block size is always greater 18862306a36Sopenharmony_ci * or equal than the memory increment size. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_ci return max_t(unsigned long, MIN_MEMORY_BLOCK_SIZE, sclp.rzm); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ciunsigned long __per_cpu_offset[NR_CPUS] __read_mostly; 19462306a36Sopenharmony_ciEXPORT_SYMBOL(__per_cpu_offset); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic int __init pcpu_cpu_distance(unsigned int from, unsigned int to) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci return LOCAL_DISTANCE; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int __init pcpu_cpu_to_node(int cpu) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_civoid __init setup_per_cpu_areas(void) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci unsigned long delta; 20962306a36Sopenharmony_ci unsigned int cpu; 21062306a36Sopenharmony_ci int rc; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* 21362306a36Sopenharmony_ci * Always reserve area for module percpu variables. That's 21462306a36Sopenharmony_ci * what the legacy allocator did. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ci rc = pcpu_embed_first_chunk(PERCPU_MODULE_RESERVE, 21762306a36Sopenharmony_ci PERCPU_DYNAMIC_RESERVE, PAGE_SIZE, 21862306a36Sopenharmony_ci pcpu_cpu_distance, 21962306a36Sopenharmony_ci pcpu_cpu_to_node); 22062306a36Sopenharmony_ci if (rc < 0) 22162306a36Sopenharmony_ci panic("Failed to initialize percpu areas."); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci delta = (unsigned long)pcpu_base_addr - (unsigned long)__per_cpu_start; 22462306a36Sopenharmony_ci for_each_possible_cpu(cpu) 22562306a36Sopenharmony_ci __per_cpu_offset[cpu] = delta + pcpu_unit_offsets[cpu]; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci#ifdef CONFIG_MEMORY_HOTPLUG 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci#ifdef CONFIG_CMA 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/* Prevent memory blocks which contain cma regions from going offline */ 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistruct s390_cma_mem_data { 23562306a36Sopenharmony_ci unsigned long start; 23662306a36Sopenharmony_ci unsigned long end; 23762306a36Sopenharmony_ci}; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic int s390_cma_check_range(struct cma *cma, void *data) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct s390_cma_mem_data *mem_data; 24262306a36Sopenharmony_ci unsigned long start, end; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci mem_data = data; 24562306a36Sopenharmony_ci start = cma_get_base(cma); 24662306a36Sopenharmony_ci end = start + cma_get_size(cma); 24762306a36Sopenharmony_ci if (end < mem_data->start) 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci if (start >= mem_data->end) 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci return -EBUSY; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int s390_cma_mem_notifier(struct notifier_block *nb, 25562306a36Sopenharmony_ci unsigned long action, void *data) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct s390_cma_mem_data mem_data; 25862306a36Sopenharmony_ci struct memory_notify *arg; 25962306a36Sopenharmony_ci int rc = 0; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci arg = data; 26262306a36Sopenharmony_ci mem_data.start = arg->start_pfn << PAGE_SHIFT; 26362306a36Sopenharmony_ci mem_data.end = mem_data.start + (arg->nr_pages << PAGE_SHIFT); 26462306a36Sopenharmony_ci if (action == MEM_GOING_OFFLINE) 26562306a36Sopenharmony_ci rc = cma_for_each_area(s390_cma_check_range, &mem_data); 26662306a36Sopenharmony_ci return notifier_from_errno(rc); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic struct notifier_block s390_cma_mem_nb = { 27062306a36Sopenharmony_ci .notifier_call = s390_cma_mem_notifier, 27162306a36Sopenharmony_ci}; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic int __init s390_cma_mem_init(void) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci return register_memory_notifier(&s390_cma_mem_nb); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_cidevice_initcall(s390_cma_mem_init); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci#endif /* CONFIG_CMA */ 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ciint arch_add_memory(int nid, u64 start, u64 size, 28262306a36Sopenharmony_ci struct mhp_params *params) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci unsigned long start_pfn = PFN_DOWN(start); 28562306a36Sopenharmony_ci unsigned long size_pages = PFN_DOWN(size); 28662306a36Sopenharmony_ci int rc; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (WARN_ON_ONCE(params->altmap)) 28962306a36Sopenharmony_ci return -EINVAL; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (WARN_ON_ONCE(params->pgprot.pgprot != PAGE_KERNEL.pgprot)) 29262306a36Sopenharmony_ci return -EINVAL; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci VM_BUG_ON(!mhp_range_allowed(start, size, true)); 29562306a36Sopenharmony_ci rc = vmem_add_mapping(start, size); 29662306a36Sopenharmony_ci if (rc) 29762306a36Sopenharmony_ci return rc; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci rc = __add_pages(nid, start_pfn, size_pages, params); 30062306a36Sopenharmony_ci if (rc) 30162306a36Sopenharmony_ci vmem_remove_mapping(start, size); 30262306a36Sopenharmony_ci return rc; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_civoid arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci unsigned long start_pfn = start >> PAGE_SHIFT; 30862306a36Sopenharmony_ci unsigned long nr_pages = size >> PAGE_SHIFT; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci __remove_pages(start_pfn, nr_pages, altmap); 31162306a36Sopenharmony_ci vmem_remove_mapping(start, size); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci#endif /* CONFIG_MEMORY_HOTPLUG */ 314