162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
362306a36Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
462306a36Sopenharmony_ci * for more details.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 1998-2003 Hewlett-Packard Co
762306a36Sopenharmony_ci *	David Mosberger-Tang <davidm@hpl.hp.com>
862306a36Sopenharmony_ci *	Stephane Eranian <eranian@hpl.hp.com>
962306a36Sopenharmony_ci * Copyright (C) 2000, Rohit Seth <rohit.seth@intel.com>
1062306a36Sopenharmony_ci * Copyright (C) 1999 VA Linux Systems
1162306a36Sopenharmony_ci * Copyright (C) 1999 Walt Drummond <drummond@valinux.com>
1262306a36Sopenharmony_ci * Copyright (C) 2003 Silicon Graphics, Inc. All rights reserved.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * Routines used by ia64 machines with contiguous (or virtually contiguous)
1562306a36Sopenharmony_ci * memory.
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci#include <linux/efi.h>
1862306a36Sopenharmony_ci#include <linux/memblock.h>
1962306a36Sopenharmony_ci#include <linux/mm.h>
2062306a36Sopenharmony_ci#include <linux/nmi.h>
2162306a36Sopenharmony_ci#include <linux/swap.h>
2262306a36Sopenharmony_ci#include <linux/sizes.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <asm/efi.h>
2562306a36Sopenharmony_ci#include <asm/meminit.h>
2662306a36Sopenharmony_ci#include <asm/sections.h>
2762306a36Sopenharmony_ci#include <asm/mca.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/* physical address where the bootmem map is located */
3062306a36Sopenharmony_ciunsigned long bootmap_start;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#ifdef CONFIG_SMP
3362306a36Sopenharmony_cistatic void *cpu_data;
3462306a36Sopenharmony_ci/**
3562306a36Sopenharmony_ci * per_cpu_init - setup per-cpu variables
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * Allocate and setup per-cpu data areas.
3862306a36Sopenharmony_ci */
3962306a36Sopenharmony_civoid *per_cpu_init(void)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	static bool first_time = true;
4262306a36Sopenharmony_ci	void *cpu0_data = __cpu0_per_cpu;
4362306a36Sopenharmony_ci	unsigned int cpu;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	if (!first_time)
4662306a36Sopenharmony_ci		goto skip;
4762306a36Sopenharmony_ci	first_time = false;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/*
5062306a36Sopenharmony_ci	 * get_free_pages() cannot be used before cpu_init() done.
5162306a36Sopenharmony_ci	 * BSP allocates PERCPU_PAGE_SIZE bytes for all possible CPUs
5262306a36Sopenharmony_ci	 * to avoid that AP calls get_zeroed_page().
5362306a36Sopenharmony_ci	 */
5462306a36Sopenharmony_ci	for_each_possible_cpu(cpu) {
5562306a36Sopenharmony_ci		void *src = cpu == 0 ? cpu0_data : __phys_per_cpu_start;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci		memcpy(cpu_data, src, __per_cpu_end - __per_cpu_start);
5862306a36Sopenharmony_ci		__per_cpu_offset[cpu] = (char *)cpu_data - __per_cpu_start;
5962306a36Sopenharmony_ci		per_cpu(local_per_cpu_offset, cpu) = __per_cpu_offset[cpu];
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		/*
6262306a36Sopenharmony_ci		 * percpu area for cpu0 is moved from the __init area
6362306a36Sopenharmony_ci		 * which is setup by head.S and used till this point.
6462306a36Sopenharmony_ci		 * Update ar.k3.  This move is ensures that percpu
6562306a36Sopenharmony_ci		 * area for cpu0 is on the correct node and its
6662306a36Sopenharmony_ci		 * virtual address isn't insanely far from other
6762306a36Sopenharmony_ci		 * percpu areas which is important for congruent
6862306a36Sopenharmony_ci		 * percpu allocator.
6962306a36Sopenharmony_ci		 */
7062306a36Sopenharmony_ci		if (cpu == 0)
7162306a36Sopenharmony_ci			ia64_set_kr(IA64_KR_PER_CPU_DATA, __pa(cpu_data) -
7262306a36Sopenharmony_ci				    (unsigned long)__per_cpu_start);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci		cpu_data += PERCPU_PAGE_SIZE;
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ciskip:
7762306a36Sopenharmony_ci	return __per_cpu_start + __per_cpu_offset[smp_processor_id()];
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic inline __init void
8162306a36Sopenharmony_cialloc_per_cpu_data(void)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	size_t size = PERCPU_PAGE_SIZE * num_possible_cpus();
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	cpu_data = memblock_alloc_from(size, PERCPU_PAGE_SIZE,
8662306a36Sopenharmony_ci				       __pa(MAX_DMA_ADDRESS));
8762306a36Sopenharmony_ci	if (!cpu_data)
8862306a36Sopenharmony_ci		panic("%s: Failed to allocate %lu bytes align=%lx from=%lx\n",
8962306a36Sopenharmony_ci		      __func__, size, PERCPU_PAGE_SIZE, __pa(MAX_DMA_ADDRESS));
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/**
9362306a36Sopenharmony_ci * setup_per_cpu_areas - setup percpu areas
9462306a36Sopenharmony_ci *
9562306a36Sopenharmony_ci * Arch code has already allocated and initialized percpu areas.  All
9662306a36Sopenharmony_ci * this function has to do is to teach the determined layout to the
9762306a36Sopenharmony_ci * dynamic percpu allocator, which happens to be more complex than
9862306a36Sopenharmony_ci * creating whole new ones using helpers.
9962306a36Sopenharmony_ci */
10062306a36Sopenharmony_civoid __init
10162306a36Sopenharmony_cisetup_per_cpu_areas(void)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct pcpu_alloc_info *ai;
10462306a36Sopenharmony_ci	struct pcpu_group_info *gi;
10562306a36Sopenharmony_ci	unsigned int cpu;
10662306a36Sopenharmony_ci	ssize_t static_size, reserved_size, dyn_size;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	ai = pcpu_alloc_alloc_info(1, num_possible_cpus());
10962306a36Sopenharmony_ci	if (!ai)
11062306a36Sopenharmony_ci		panic("failed to allocate pcpu_alloc_info");
11162306a36Sopenharmony_ci	gi = &ai->groups[0];
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	/* units are assigned consecutively to possible cpus */
11462306a36Sopenharmony_ci	for_each_possible_cpu(cpu)
11562306a36Sopenharmony_ci		gi->cpu_map[gi->nr_units++] = cpu;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/* set parameters */
11862306a36Sopenharmony_ci	static_size = __per_cpu_end - __per_cpu_start;
11962306a36Sopenharmony_ci	reserved_size = PERCPU_MODULE_RESERVE;
12062306a36Sopenharmony_ci	dyn_size = PERCPU_PAGE_SIZE - static_size - reserved_size;
12162306a36Sopenharmony_ci	if (dyn_size < 0)
12262306a36Sopenharmony_ci		panic("percpu area overflow static=%zd reserved=%zd\n",
12362306a36Sopenharmony_ci		      static_size, reserved_size);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	ai->static_size		= static_size;
12662306a36Sopenharmony_ci	ai->reserved_size	= reserved_size;
12762306a36Sopenharmony_ci	ai->dyn_size		= dyn_size;
12862306a36Sopenharmony_ci	ai->unit_size		= PERCPU_PAGE_SIZE;
12962306a36Sopenharmony_ci	ai->atom_size		= PAGE_SIZE;
13062306a36Sopenharmony_ci	ai->alloc_size		= PERCPU_PAGE_SIZE;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	pcpu_setup_first_chunk(ai, __per_cpu_start + __per_cpu_offset[0]);
13362306a36Sopenharmony_ci	pcpu_free_alloc_info(ai);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci#else
13662306a36Sopenharmony_ci#define alloc_per_cpu_data() do { } while (0)
13762306a36Sopenharmony_ci#endif /* CONFIG_SMP */
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/**
14062306a36Sopenharmony_ci * find_memory - setup memory map
14162306a36Sopenharmony_ci *
14262306a36Sopenharmony_ci * Walk the EFI memory map and find usable memory for the system, taking
14362306a36Sopenharmony_ci * into account reserved areas.
14462306a36Sopenharmony_ci */
14562306a36Sopenharmony_civoid __init
14662306a36Sopenharmony_cifind_memory (void)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	reserve_memory();
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* first find highest page frame number */
15162306a36Sopenharmony_ci	min_low_pfn = ~0UL;
15262306a36Sopenharmony_ci	max_low_pfn = 0;
15362306a36Sopenharmony_ci	efi_memmap_walk(find_max_min_low_pfn, NULL);
15462306a36Sopenharmony_ci	max_pfn = max_low_pfn;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	memblock_add_node(0, PFN_PHYS(max_low_pfn), 0, MEMBLOCK_NONE);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	find_initrd();
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	alloc_per_cpu_data();
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic int __init find_largest_hole(u64 start, u64 end, void *arg)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	u64 *max_gap = arg;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	static u64 last_end = PAGE_OFFSET;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	/* NOTE: this algorithm assumes efi memmap table is ordered */
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (*max_gap < (start - last_end))
17262306a36Sopenharmony_ci		*max_gap = start - last_end;
17362306a36Sopenharmony_ci	last_end = end;
17462306a36Sopenharmony_ci	return 0;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void __init verify_gap_absence(void)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	unsigned long max_gap;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* Forbid FLATMEM if hole is > than 1G */
18262306a36Sopenharmony_ci	efi_memmap_walk(find_largest_hole, (u64 *)&max_gap);
18362306a36Sopenharmony_ci	if (max_gap >= SZ_1G)
18462306a36Sopenharmony_ci		panic("Cannot use FLATMEM with %ldMB hole\n"
18562306a36Sopenharmony_ci		      "Please switch over to SPARSEMEM\n",
18662306a36Sopenharmony_ci		      (max_gap >> 20));
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci/*
19062306a36Sopenharmony_ci * Set up the page tables.
19162306a36Sopenharmony_ci */
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_civoid __init
19462306a36Sopenharmony_cipaging_init (void)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	unsigned long max_dma;
19762306a36Sopenharmony_ci	unsigned long max_zone_pfns[MAX_NR_ZONES];
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	memset(max_zone_pfns, 0, sizeof(max_zone_pfns));
20062306a36Sopenharmony_ci	max_dma = virt_to_phys((void *) MAX_DMA_ADDRESS) >> PAGE_SHIFT;
20162306a36Sopenharmony_ci	max_zone_pfns[ZONE_DMA32] = max_dma;
20262306a36Sopenharmony_ci	max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	verify_gap_absence();
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	free_area_init(max_zone_pfns);
20762306a36Sopenharmony_ci	zero_page_memmap_ptr = virt_to_page(ia64_imva(empty_zero_page));
20862306a36Sopenharmony_ci}
209