/*
* Copyright (C) 1995 Linus Torvalds
* Copyright 2010 Tilera Corporation. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for
* more details.
*/
#include <linux/module.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/hugetlb.h>
#include <linux/swap.h>
#include <linux/smp.h>
#include <linux/init.h>
#include <linux/highmem.h>
#include <linux/pagemap.h>
#include <linux/poison.h>
#include <linux/bootmem.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/efi.h>
#include <linux/memory_hotplug.h>
#include <linux/uaccess.h>
#include <asm/mmu_context.h>
#include <asm/processor.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/dma.h>
#include <asm/fixmap.h>
#include <asm/tlb.h>
#include <asm/tlbflush.h>
#include <asm/sections.h>
#include <asm/setup.h>
#include <asm/homecache.h>
#include <hv/hypervisor.h>
#include <arch/chip.h>
#include "migrate.h"
#define clear_pgd(pmdptr) (*(pmdptr) = hv_pte(0))
#ifndef __tilegx__
unsigned long VMALLOC_RESERVE = CONFIG_VMALLOC_RESERVE;
EXPORT_SYMBOL(VMALLOC_RESERVE);
#endif
/* Create an L2 page table */
static pte_t * __init alloc_pte(void)
{
return __alloc_bootmem(L2_KERNEL_PGTABLE_SIZE, HV_PAGE_TABLE_ALIGN, 0);
}
/*
* L2 page tables per controller. We allocate these all at once from
* the bootmem allocator and store them here. This saves on kernel L2
* page table memory, compared to allocating a full 64K page per L2
* page table, and also means that in cases where we use huge pages,
* we are guaranteed to later be able to shatter those huge pages and
* switch to using these page tables instead, without requiring
* further allocation. Each l2_ptes[] entry points to the first page
* table for the first hugepage-size piece of memory on the
* controller; other page tables are just indexed directly, i.e. the
* L2 page tables are contiguous in memory for each controller.
*/
static pte_t *l2_ptes[MAX_NUMNODES];
static int num_l2_ptes[MAX_NUMNODES];
static void init_prealloc_ptes(int node, int pages)
{
BUG_ON(pages & (PTRS_PER_PTE - 1));
if (pages) {
num_l2_ptes[node] = pages;
l2_ptes[node] = __alloc_bootmem(pages * sizeof(pte_t),
HV_PAGE_TABLE_ALIGN, 0);
}
}
pte_t *get_prealloc_pte(unsigned long pfn)
{
int node = pfn_to_nid(pfn);
pfn &= ~(-1UL << (NR_PA_HIGHBIT_SHIFT - PAGE_SHIFT));
BUG_ON(node >= MAX_NUMNODES);
BUG_ON(pfn >= num_l2_ptes[node]);
return &l2_ptes[node][pfn];
}
/*
* What caching do we expect pages from the heap to have when
* they are allocated during bootup? (Once we've installed the
* "real" swapper_pg_dir.)
*/
static int initial_heap_home(void)
{
#if CHIP_HAS_CBOX_HOME_MAP()
if (hash_default)
return PAGE_HOME_HASH;
#endif
return smp_processor_id();
}
/*
* Place a pointer to an L2 page table in a middle page
* directory entry.
*/
static void __init assign_pte(pmd_t *pmd, pte_t *page_table)
{
phys_addr_t pa = __pa(page_table);
unsigned long l2_ptfn = pa >> HV_LOG2_PAGE_TABLE_ALIGN;
pte_t pteval = hv_pte_set_ptfn(__pgprot(_PAGE_TABLE), l2_ptfn);
BUG_ON((pa & (HV_PAGE_TABLE_ALIGN-1)) != 0);
pteval = pte_set_home(pteval, initial_heap_home());
*(pte_t *)pmd = pteval;
if (page_table != (pte_t *)pmd_page_vaddr(*pmd))
BUG();
}
#ifdef __tilegx__
static inline pmd_t *alloc_pmd(void)
{
return __alloc_bootmem(L1_KERNEL_PGTABLE_SIZE, HV_PAGE_TABLE_ALIGN, 0);
}
static inline void assign_pmd(pud_t *pud, pmd_t *pmd)
{
assign_pte((pmd_t *)pud, (pte_t *)pmd);
}
#endif /* __tilegx__ */
/* Replace the given pmd with a full PTE table. */
void __init shatter_pmd(pmd_t *pmd)
{
pte_t *pte = get_prealloc_pte(pte_pfn(*(pte_t *)pmd));
assign_pte(pmd, pte);
}
#ifdef __tilegx__
static pmd_t *__init get_pmd(pgd_t pgtables[], unsigned long va)
{
pud_t *pud = pud_offset(&pgtables[pgd_index(va)], va);<