Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+242
View File
@@ -0,0 +1,242 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Modifications by Matt Porter (mporter@mvista.com) to support
* PPC44x Book E processors.
*
* This file contains the routines for initializing the MMU
* on the 4xx series of chips.
* -- paulus
*
* Derived from arch/ppc/mm/init.c:
* Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
*
* Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
* and Cort Dougan (PReP) (cort@cs.nmt.edu)
* Copyright (C) 1996 Paul Mackerras
*
* Derived from "arch/i386/mm/init.c"
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*/
#include <linux/init.h>
#include <linux/memblock.h>
#include <asm/mmu.h>
#include <asm/page.h>
#include <asm/cacheflush.h>
#include <asm/text-patching.h>
#include <asm/smp.h>
#include <mm/mmu_decl.h>
/* Used by the 44x TLB replacement exception handler.
* Just needed it declared someplace.
*/
unsigned int tlb_44x_index; /* = 0 */
unsigned int tlb_44x_hwater = PPC44x_TLB_SIZE - 1 - PPC44x_EARLY_TLBS;
int icache_44x_need_flush;
unsigned long tlb_47x_boltmap[1024/8];
static void __init ppc44x_update_tlb_hwater(void)
{
/* The TLB miss handlers hard codes the watermark in a cmpli
* instruction to improve performances rather than loading it
* from the global variable. Thus, we patch the instructions
* in the 2 TLB miss handlers when updating the value
*/
modify_instruction_site(&patch__tlb_44x_hwater_D, 0xffff, tlb_44x_hwater);
modify_instruction_site(&patch__tlb_44x_hwater_I, 0xffff, tlb_44x_hwater);
}
/*
* "Pins" a 256MB TLB entry in AS0 for kernel lowmem for 44x type MMU
*/
static void __init ppc44x_pin_tlb(unsigned int virt, unsigned int phys)
{
unsigned int entry = tlb_44x_hwater--;
ppc44x_update_tlb_hwater();
mtspr(SPRN_MMUCR, 0);
__asm__ __volatile__(
"tlbwe %2,%3,%4\n"
"tlbwe %1,%3,%5\n"
"tlbwe %0,%3,%6\n"
:
: "r" (PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_G),
"r" (phys),
"r" (virt | PPC44x_TLB_VALID | PPC44x_TLB_256M),
"r" (entry),
"i" (PPC44x_TLB_PAGEID),
"i" (PPC44x_TLB_XLAT),
"i" (PPC44x_TLB_ATTRIB));
}
static int __init ppc47x_find_free_bolted(void)
{
unsigned int mmube0 = mfspr(SPRN_MMUBE0);
unsigned int mmube1 = mfspr(SPRN_MMUBE1);
if (!(mmube0 & MMUBE0_VBE0))
return 0;
if (!(mmube0 & MMUBE0_VBE1))
return 1;
if (!(mmube0 & MMUBE0_VBE2))
return 2;
if (!(mmube1 & MMUBE1_VBE3))
return 3;
if (!(mmube1 & MMUBE1_VBE4))
return 4;
if (!(mmube1 & MMUBE1_VBE5))
return 5;
return -1;
}
static void __init ppc47x_update_boltmap(void)
{
unsigned int mmube0 = mfspr(SPRN_MMUBE0);
unsigned int mmube1 = mfspr(SPRN_MMUBE1);
if (mmube0 & MMUBE0_VBE0)
__set_bit((mmube0 >> MMUBE0_IBE0_SHIFT) & 0xff,
tlb_47x_boltmap);
if (mmube0 & MMUBE0_VBE1)
__set_bit((mmube0 >> MMUBE0_IBE1_SHIFT) & 0xff,
tlb_47x_boltmap);
if (mmube0 & MMUBE0_VBE2)
__set_bit((mmube0 >> MMUBE0_IBE2_SHIFT) & 0xff,
tlb_47x_boltmap);
if (mmube1 & MMUBE1_VBE3)
__set_bit((mmube1 >> MMUBE1_IBE3_SHIFT) & 0xff,
tlb_47x_boltmap);
if (mmube1 & MMUBE1_VBE4)
__set_bit((mmube1 >> MMUBE1_IBE4_SHIFT) & 0xff,
tlb_47x_boltmap);
if (mmube1 & MMUBE1_VBE5)
__set_bit((mmube1 >> MMUBE1_IBE5_SHIFT) & 0xff,
tlb_47x_boltmap);
}
/*
* "Pins" a 256MB TLB entry in AS0 for kernel lowmem for 47x type MMU
*/
static void __init ppc47x_pin_tlb(unsigned int virt, unsigned int phys)
{
unsigned int rA;
int bolted;
/* Base rA is HW way select, way 0, bolted bit set */
rA = 0x88000000;
/* Look for a bolted entry slot */
bolted = ppc47x_find_free_bolted();
BUG_ON(bolted < 0);
/* Insert bolted slot number */
rA |= bolted << 24;
pr_debug("256M TLB entry for 0x%08x->0x%08x in bolt slot %d\n",
virt, phys, bolted);
mtspr(SPRN_MMUCR, 0);
__asm__ __volatile__(
"tlbwe %2,%3,0\n"
"tlbwe %1,%3,1\n"
"tlbwe %0,%3,2\n"
:
: "r" (PPC47x_TLB2_SW | PPC47x_TLB2_SR |
PPC47x_TLB2_SX
#ifdef CONFIG_SMP
| PPC47x_TLB2_M
#endif
),
"r" (phys),
"r" (virt | PPC47x_TLB0_VALID | PPC47x_TLB0_256M),
"r" (rA));
}
void __init MMU_init_hw(void)
{
/* This is not useful on 47x but won't hurt either */
ppc44x_update_tlb_hwater();
flush_instruction_cache();
}
unsigned long __init mmu_mapin_ram(unsigned long base, unsigned long top)
{
unsigned long addr;
unsigned long memstart = memstart_addr & ~(PPC_PIN_SIZE - 1);
/* Pin in enough TLBs to cover any lowmem not covered by the
* initial 256M mapping established in head_44x.S */
for (addr = memstart + PPC_PIN_SIZE; addr < lowmem_end_addr;
addr += PPC_PIN_SIZE) {
if (mmu_has_feature(MMU_FTR_TYPE_47x))
ppc47x_pin_tlb(addr + PAGE_OFFSET, addr);
else
ppc44x_pin_tlb(addr + PAGE_OFFSET, addr);
}
if (mmu_has_feature(MMU_FTR_TYPE_47x)) {
ppc47x_update_boltmap();
#ifdef DEBUG
{
int i;
printk(KERN_DEBUG "bolted entries: ");
for (i = 0; i < 255; i++) {
if (test_bit(i, tlb_47x_boltmap))
printk("%d ", i);
}
printk("\n");
}
#endif /* DEBUG */
}
return total_lowmem;
}
void setup_initial_memory_limit(phys_addr_t first_memblock_base,
phys_addr_t first_memblock_size)
{
u64 size;
#ifndef CONFIG_NONSTATIC_KERNEL
/* We don't currently support the first MEMBLOCK not mapping 0
* physical on those processors
*/
BUG_ON(first_memblock_base != 0);
#endif
/* 44x has a 256M TLB entry pinned at boot */
size = (min_t(u64, first_memblock_size, PPC_PIN_SIZE));
memblock_set_current_limit(first_memblock_base + size);
}
#ifdef CONFIG_SMP
void __init mmu_init_secondary(int cpu)
{
unsigned long addr;
unsigned long memstart = memstart_addr & ~(PPC_PIN_SIZE - 1);
/* Pin in enough TLBs to cover any lowmem not covered by the
* initial 256M mapping established in head_44x.S
*
* WARNING: This is called with only the first 256M of the
* linear mapping in the TLB and we can't take faults yet
* so beware of what this code uses. It runs off a temporary
* stack. current (r2) isn't initialized, smp_processor_id()
* will not work, current thread info isn't accessible, ...
*/
for (addr = memstart + PPC_PIN_SIZE; addr < lowmem_end_addr;
addr += PPC_PIN_SIZE) {
if (mmu_has_feature(MMU_FTR_TYPE_47x))
ppc47x_pin_tlb(addr + PAGE_OFFSET, addr);
else
ppc44x_pin_tlb(addr + PAGE_OFFSET, addr);
}
}
#endif /* CONFIG_SMP */
+222
View File
@@ -0,0 +1,222 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* This file contains the routines for initializing the MMU
* on the 8xx series of chips.
* -- christophe
*
* Derived from arch/powerpc/mm/40x_mmu.c:
*/
#include <linux/memblock.h>
#include <linux/hugetlb.h>
#include <asm/fixmap.h>
#include <asm/pgalloc.h>
#include <mm/mmu_decl.h>
#define IMMR_SIZE (FIX_IMMR_SIZE << PAGE_SHIFT)
static unsigned long block_mapped_ram;
/*
* Return PA for this VA if it is in an area mapped with LTLBs or fixmap.
* Otherwise, returns 0
*/
phys_addr_t v_block_mapped(unsigned long va)
{
unsigned long p = PHYS_IMMR_BASE;
if (va >= VIRT_IMMR_BASE && va < VIRT_IMMR_BASE + IMMR_SIZE)
return p + va - VIRT_IMMR_BASE;
if (va >= PAGE_OFFSET && va < PAGE_OFFSET + block_mapped_ram)
return __pa(va);
return 0;
}
/*
* Return VA for a given PA mapped with LTLBs or fixmap
* Return 0 if not mapped
*/
unsigned long p_block_mapped(phys_addr_t pa)
{
unsigned long p = PHYS_IMMR_BASE;
if (pa >= p && pa < p + IMMR_SIZE)
return VIRT_IMMR_BASE + pa - p;
if (pa < block_mapped_ram)
return (unsigned long)__va(pa);
return 0;
}
static int __ref __early_map_kernel_hugepage(unsigned long va, phys_addr_t pa,
pgprot_t prot, int psize, bool new)
{
pmd_t *pmdp = pmd_off_k(va);
pte_t *ptep;
unsigned int shift = mmu_psize_to_shift(psize);
if (new) {
if (WARN_ON(slab_is_available()))
return -EINVAL;
if (psize == MMU_PAGE_8M) {
if (WARN_ON(!pmd_none(*pmdp) || !pmd_none(*(pmdp + 1))))
return -EINVAL;
ptep = early_alloc_pgtable(PTE_FRAG_SIZE);
pmd_populate_kernel(&init_mm, pmdp, ptep);
ptep = early_alloc_pgtable(PTE_FRAG_SIZE);
pmd_populate_kernel(&init_mm, pmdp + 1, ptep);
ptep = (pte_t *)pmdp;
} else {
ptep = early_pte_alloc_kernel(pmdp, va);
/* The PTE should never be already present */
if (WARN_ON(pte_present(*ptep) && pgprot_val(prot)))
return -EINVAL;
}
} else {
if (psize == MMU_PAGE_8M)
ptep = (pte_t *)pmdp;
else
ptep = pte_offset_kernel(pmdp, va);
}
if (WARN_ON(!ptep))
return -ENOMEM;
set_huge_pte_at(&init_mm, va, ptep,
arch_make_huge_pte(pfn_pte(pa >> PAGE_SHIFT, prot), shift, 0),
1UL << shift);
return 0;
}
/*
* MMU_init_hw does the chip-specific initialization of the MMU hardware.
*/
void __init MMU_init_hw(void)
{
}
static bool immr_is_mapped __initdata;
void __init mmu_mapin_immr(void)
{
if (immr_is_mapped)
return;
immr_is_mapped = true;
__early_map_kernel_hugepage(VIRT_IMMR_BASE, PHYS_IMMR_BASE,
PAGE_KERNEL_NCG, MMU_PAGE_512K, true);
}
static int mmu_mapin_ram_chunk(unsigned long offset, unsigned long top,
pgprot_t prot, bool new)
{
unsigned long v = PAGE_OFFSET + offset;
unsigned long p = offset;
int err = 0;
WARN_ON(!IS_ALIGNED(offset, SZ_16K) || !IS_ALIGNED(top, SZ_16K));
for (; p < ALIGN(p, SZ_512K) && p < top && !err; p += SZ_16K, v += SZ_16K)
err = __early_map_kernel_hugepage(v, p, prot, MMU_PAGE_16K, new);
for (; p < ALIGN(p, SZ_8M) && p < top && !err; p += SZ_512K, v += SZ_512K)
err = __early_map_kernel_hugepage(v, p, prot, MMU_PAGE_512K, new);
for (; p < ALIGN_DOWN(top, SZ_8M) && p < top && !err; p += SZ_8M, v += SZ_8M)
err = __early_map_kernel_hugepage(v, p, prot, MMU_PAGE_8M, new);
for (; p < ALIGN_DOWN(top, SZ_512K) && p < top && !err; p += SZ_512K, v += SZ_512K)
err = __early_map_kernel_hugepage(v, p, prot, MMU_PAGE_512K, new);
for (; p < ALIGN_DOWN(top, SZ_16K) && p < top && !err; p += SZ_16K, v += SZ_16K)
err = __early_map_kernel_hugepage(v, p, prot, MMU_PAGE_16K, new);
if (!new)
flush_tlb_kernel_range(PAGE_OFFSET + v, PAGE_OFFSET + top);
return err;
}
unsigned long __init mmu_mapin_ram(unsigned long base, unsigned long top)
{
unsigned long etext8 = ALIGN(__pa(_etext), SZ_8M);
unsigned long sinittext = __pa(_sinittext);
bool strict_boundary = strict_kernel_rwx_enabled() || debug_pagealloc_enabled_or_kfence();
unsigned long boundary = strict_boundary ? sinittext : etext8;
unsigned long einittext8 = ALIGN(__pa(_einittext), SZ_8M);
WARN_ON(top < einittext8);
mmu_mapin_immr();
mmu_mapin_ram_chunk(0, boundary, PAGE_KERNEL_X, true);
if (debug_pagealloc_enabled_or_kfence()) {
top = boundary;
} else {
mmu_mapin_ram_chunk(boundary, einittext8, PAGE_KERNEL_X, true);
mmu_mapin_ram_chunk(einittext8, top, PAGE_KERNEL, true);
}
if (top > SZ_32M)
memblock_set_current_limit(top);
block_mapped_ram = top;
return top;
}
int mmu_mark_initmem_nx(void)
{
unsigned long etext8 = ALIGN(__pa(_etext), SZ_8M);
unsigned long sinittext = __pa(_sinittext);
unsigned long boundary = strict_kernel_rwx_enabled() ? sinittext : etext8;
unsigned long einittext8 = ALIGN(__pa(_einittext), SZ_8M);
int err = 0;
if (!debug_pagealloc_enabled_or_kfence())
err = mmu_mapin_ram_chunk(boundary, einittext8, PAGE_KERNEL, false);
if (IS_ENABLED(CONFIG_PIN_TLB_TEXT))
mmu_pin_tlb(block_mapped_ram, false);
return err;
}
#ifdef CONFIG_STRICT_KERNEL_RWX
int mmu_mark_rodata_ro(void)
{
unsigned long sinittext = __pa(_sinittext);
int err;
err = mmu_mapin_ram_chunk(0, sinittext, PAGE_KERNEL_ROX, false);
if (IS_ENABLED(CONFIG_PIN_TLB_DATA))
mmu_pin_tlb(block_mapped_ram, true);
return err;
}
#endif
void __init setup_initial_memory_limit(phys_addr_t first_memblock_base,
phys_addr_t first_memblock_size)
{
/* We don't currently support the first MEMBLOCK not mapping 0
* physical on those processors
*/
BUG_ON(first_memblock_base != 0);
/* 8xx can only access 32MB at the moment */
memblock_set_current_limit(min_t(u64, first_memblock_size, SZ_32M));
}
int pud_clear_huge(pud_t *pud)
{
return 0;
}
int pmd_clear_huge(pmd_t *pmd)
{
return 0;
}
@@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0
obj-y += mmu_context.o tlb.o tlb_low.o kup.o
obj-$(CONFIG_PPC_BOOK3E_64) += tlb_64e.o tlb_low_64e.o book3e_pgtable.o
obj-$(CONFIG_44x) += 44x.o
obj-$(CONFIG_PPC_8xx) += 8xx.o
obj-$(CONFIG_PPC_E500) += e500.o
obj-$(CONFIG_RANDOMIZE_BASE) += kaslr_booke.o
ifdef CONFIG_HUGETLB_PAGE
obj-$(CONFIG_PPC_E500) += e500_hugetlbpage.o
endif
# Disable kcov instrumentation on sensitive code
# This is necessary for booting with kcov enabled on book3e machines
KCOV_INSTRUMENT_tlb.o := n
KCOV_INSTRUMENT_e500.o := n
@@ -0,0 +1,132 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2005, Paul Mackerras, IBM Corporation.
* Copyright 2009, Benjamin Herrenschmidt, IBM Corporation.
* Copyright 2015-2016, Aneesh Kumar K.V, IBM Corporation.
*/
#include <linux/sched.h>
#include <linux/memblock.h>
#include <asm/pgalloc.h>
#include <asm/tlb.h>
#include <asm/dma.h>
#include <asm/text-patching.h>
#include <mm/mmu_decl.h>
#ifdef CONFIG_SPARSEMEM_VMEMMAP
/*
* On Book3E CPUs, the vmemmap is currently mapped in the top half of
* the vmalloc space using normal page tables, though the size of
* pages encoded in the PTEs can be different
*/
int __meminit vmemmap_create_mapping(unsigned long start,
unsigned long page_size,
unsigned long phys)
{
/* Create a PTE encoding without page size */
unsigned long i, flags = _PAGE_PRESENT | _PAGE_ACCESSED |
_PAGE_KERNEL_RW;
/* PTEs only contain page size encodings up to 32M */
BUG_ON(mmu_psize_defs[mmu_vmemmap_psize].shift - 10 > 0xf);
/* Encode the size in the PTE */
flags |= (mmu_psize_defs[mmu_vmemmap_psize].shift - 10) << 8;
/* For each PTE for that area, map things. Note that we don't
* increment phys because all PTEs are of the large size and
* thus must have the low bits clear
*/
for (i = 0; i < page_size; i += PAGE_SIZE)
BUG_ON(map_kernel_page(start + i, phys, __pgprot(flags)));
return 0;
}
#ifdef CONFIG_MEMORY_HOTPLUG
void vmemmap_remove_mapping(unsigned long start,
unsigned long page_size)
{
}
#endif
#endif /* CONFIG_SPARSEMEM_VMEMMAP */
static void __init *early_alloc_pgtable(unsigned long size)
{
void *ptr;
ptr = memblock_alloc_try_nid(size, size, MEMBLOCK_LOW_LIMIT,
__pa(MAX_DMA_ADDRESS), NUMA_NO_NODE);
if (!ptr)
panic("%s: Failed to allocate %lu bytes align=0x%lx max_addr=%lx\n",
__func__, size, size, __pa(MAX_DMA_ADDRESS));
return ptr;
}
/*
* map_kernel_page currently only called by __ioremap
* map_kernel_page adds an entry to the ioremap page table
* and adds an entry to the HPT, possibly bolting it
*/
int __ref map_kernel_page(unsigned long ea, phys_addr_t pa, pgprot_t prot)
{
pgd_t *pgdp;
p4d_t *p4dp;
pud_t *pudp;
pmd_t *pmdp;
pte_t *ptep;
BUILD_BUG_ON(TASK_SIZE_USER64 > PGTABLE_RANGE);
if (slab_is_available()) {
pgdp = pgd_offset_k(ea);
p4dp = p4d_offset(pgdp, ea);
pudp = pud_alloc(&init_mm, p4dp, ea);
if (!pudp)
return -ENOMEM;
pmdp = pmd_alloc(&init_mm, pudp, ea);
if (!pmdp)
return -ENOMEM;
ptep = pte_alloc_kernel(pmdp, ea);
if (!ptep)
return -ENOMEM;
} else {
pgdp = pgd_offset_k(ea);
p4dp = p4d_offset(pgdp, ea);
if (p4d_none(*p4dp)) {
pudp = early_alloc_pgtable(PUD_TABLE_SIZE);
p4d_populate(&init_mm, p4dp, pudp);
}
pudp = pud_offset(p4dp, ea);
if (pud_none(*pudp)) {
pmdp = early_alloc_pgtable(PMD_TABLE_SIZE);
pud_populate(&init_mm, pudp, pmdp);
}
pmdp = pmd_offset(pudp, ea);
if (!pmd_present(*pmdp)) {
ptep = early_alloc_pgtable(PTE_TABLE_SIZE);
pmd_populate_kernel(&init_mm, pmdp, ptep);
}
ptep = pte_offset_kernel(pmdp, ea);
}
set_pte_at(&init_mm, ea, ptep, pfn_pte(pa >> PAGE_SHIFT, prot));
smp_wmb();
return 0;
}
void __patch_exception(int exc, unsigned long addr)
{
unsigned int *ibase = &interrupt_base_book3e;
/*
* Our exceptions vectors start with a NOP and -then- a branch
* to deal with single stepping from userspace which stops on
* the second instruction. Thus we need to patch the second
* instruction of the exception, not the first one.
*/
patch_branch(ibase + (exc / 4) + 1, addr, 0);
}
+379
View File
@@ -0,0 +1,379 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Modifications by Kumar Gala (galak@kernel.crashing.org) to support
* E500 Book E processors.
*
* Copyright 2004,2010 Freescale Semiconductor, Inc.
*
* This file contains the routines for initializing the MMU
* on the 4xx series of chips.
* -- paulus
*
* Derived from arch/ppc/mm/init.c:
* Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
*
* Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
* and Cort Dougan (PReP) (cort@cs.nmt.edu)
* Copyright (C) 1996 Paul Mackerras
*
* Derived from "arch/i386/mm/init.c"
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*/
#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/swap.h>
#include <linux/stddef.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/highmem.h>
#include <linux/memblock.h>
#include <linux/of_fdt.h>
#include <asm/io.h>
#include <asm/mmu_context.h>
#include <asm/mmu.h>
#include <linux/uaccess.h>
#include <asm/smp.h>
#include <asm/machdep.h>
#include <asm/setup.h>
#include <asm/paca.h>
#include <mm/mmu_decl.h>
unsigned int tlbcam_index;
struct tlbcam TLBCAM[NUM_TLBCAMS];
static struct {
unsigned long start;
unsigned long limit;
phys_addr_t phys;
} tlbcam_addrs[NUM_TLBCAMS];
#ifdef CONFIG_PPC_85xx
/*
* Return PA for this VA if it is mapped by a CAM, or 0
*/
phys_addr_t v_block_mapped(unsigned long va)
{
int b;
for (b = 0; b < tlbcam_index; ++b)
if (va >= tlbcam_addrs[b].start && va < tlbcam_addrs[b].limit)
return tlbcam_addrs[b].phys + (va - tlbcam_addrs[b].start);
return 0;
}
/*
* Return VA for a given PA or 0 if not mapped
*/
unsigned long p_block_mapped(phys_addr_t pa)
{
int b;
for (b = 0; b < tlbcam_index; ++b)
if (pa >= tlbcam_addrs[b].phys
&& pa < (tlbcam_addrs[b].limit-tlbcam_addrs[b].start)
+tlbcam_addrs[b].phys)
return tlbcam_addrs[b].start+(pa-tlbcam_addrs[b].phys);
return 0;
}
#endif
/*
* Set up a variable-size TLB entry (tlbcam). The parameters are not checked;
* in particular size must be a power of 4 between 4k and the max supported by
* an implementation; max may further be limited by what can be represented in
* an unsigned long (for example, 32-bit implementations cannot support a 4GB
* size).
*/
static void settlbcam(int index, unsigned long virt, phys_addr_t phys,
unsigned long size, unsigned long flags, unsigned int pid)
{
unsigned int tsize;
tsize = __ilog2(size) - 10;
#if defined(CONFIG_SMP) || defined(CONFIG_PPC_E500MC)
if ((flags & _PAGE_NO_CACHE) == 0)
flags |= _PAGE_COHERENT;
#endif
TLBCAM[index].MAS0 = MAS0_TLBSEL(1) | MAS0_ESEL(index) | MAS0_NV(index+1);
TLBCAM[index].MAS1 = MAS1_VALID | MAS1_IPROT | MAS1_TSIZE(tsize) | MAS1_TID(pid);
TLBCAM[index].MAS2 = virt & PAGE_MASK;
TLBCAM[index].MAS2 |= (flags & _PAGE_WRITETHRU) ? MAS2_W : 0;
TLBCAM[index].MAS2 |= (flags & _PAGE_NO_CACHE) ? MAS2_I : 0;
TLBCAM[index].MAS2 |= (flags & _PAGE_COHERENT) ? MAS2_M : 0;
TLBCAM[index].MAS2 |= (flags & _PAGE_GUARDED) ? MAS2_G : 0;
TLBCAM[index].MAS2 |= (flags & _PAGE_ENDIAN) ? MAS2_E : 0;
TLBCAM[index].MAS3 = (phys & MAS3_RPN) | MAS3_SR;
TLBCAM[index].MAS3 |= (flags & _PAGE_WRITE) ? MAS3_SW : 0;
if (mmu_has_feature(MMU_FTR_BIG_PHYS))
TLBCAM[index].MAS7 = (u64)phys >> 32;
/* Below is unlikely -- only for large user pages or similar */
if (!is_kernel_addr(virt)) {
TLBCAM[index].MAS3 |= MAS3_UR;
TLBCAM[index].MAS3 |= (flags & _PAGE_EXEC) ? MAS3_UX : 0;
TLBCAM[index].MAS3 |= (flags & _PAGE_WRITE) ? MAS3_UW : 0;
} else {
TLBCAM[index].MAS3 |= (flags & _PAGE_EXEC) ? MAS3_SX : 0;
}
tlbcam_addrs[index].start = virt;
tlbcam_addrs[index].limit = virt + size - 1;
tlbcam_addrs[index].phys = phys;
}
static unsigned long calc_cam_sz(unsigned long ram, unsigned long virt,
phys_addr_t phys)
{
unsigned int camsize = __ilog2(ram);
unsigned int align = __ffs(virt | phys);
unsigned long max_cam;
if ((mfspr(SPRN_MMUCFG) & MMUCFG_MAVN) == MMUCFG_MAVN_V1) {
/* Convert (4^max) kB to (2^max) bytes */
max_cam = ((mfspr(SPRN_TLB1CFG) >> 16) & 0xf) * 2 + 10;
camsize &= ~1U;
align &= ~1U;
} else {
/* Convert (2^max) kB to (2^max) bytes */
max_cam = __ilog2(mfspr(SPRN_TLB1PS)) + 10;
}
if (camsize > align)
camsize = align;
if (camsize > max_cam)
camsize = max_cam;
return 1UL << camsize;
}
static unsigned long map_mem_in_cams_addr(phys_addr_t phys, unsigned long virt,
unsigned long ram, int max_cam_idx,
bool dryrun, bool init)
{
int i;
unsigned long amount_mapped = 0;
unsigned long boundary;
if (strict_kernel_rwx_enabled())
boundary = (unsigned long)(_sinittext - _stext);
else
boundary = ram;
/* Calculate CAM values */
for (i = 0; boundary && i < max_cam_idx; i++) {
unsigned long cam_sz;
pgprot_t prot = init ? PAGE_KERNEL_X : PAGE_KERNEL_ROX;
cam_sz = calc_cam_sz(boundary, virt, phys);
if (!dryrun)
settlbcam(i, virt, phys, cam_sz, pgprot_val(prot), 0);
boundary -= cam_sz;
amount_mapped += cam_sz;
virt += cam_sz;
phys += cam_sz;
}
for (ram -= amount_mapped; ram && i < max_cam_idx; i++) {
unsigned long cam_sz;
pgprot_t prot = init ? PAGE_KERNEL_X : PAGE_KERNEL;
cam_sz = calc_cam_sz(ram, virt, phys);
if (!dryrun)
settlbcam(i, virt, phys, cam_sz, pgprot_val(prot), 0);
ram -= cam_sz;
amount_mapped += cam_sz;
virt += cam_sz;
phys += cam_sz;
}
if (dryrun)
return amount_mapped;
if (init) {
loadcam_multi(0, i, max_cam_idx);
tlbcam_index = i;
} else {
loadcam_multi(0, i, 0);
WARN_ON(i > tlbcam_index);
}
#ifdef CONFIG_PPC64
get_paca()->tcd.esel_next = i;
get_paca()->tcd.esel_max = mfspr(SPRN_TLB1CFG) & TLBnCFG_N_ENTRY;
get_paca()->tcd.esel_first = i;
#endif
return amount_mapped;
}
unsigned long map_mem_in_cams(unsigned long ram, int max_cam_idx, bool dryrun, bool init)
{
unsigned long virt = PAGE_OFFSET;
phys_addr_t phys = memstart_addr;
return map_mem_in_cams_addr(phys, virt, ram, max_cam_idx, dryrun, init);
}
#ifdef CONFIG_PPC32
#if defined(CONFIG_LOWMEM_CAM_NUM_BOOL) && (CONFIG_LOWMEM_CAM_NUM >= NUM_TLBCAMS)
#error "LOWMEM_CAM_NUM must be less than NUM_TLBCAMS"
#endif
unsigned long __init mmu_mapin_ram(unsigned long base, unsigned long top)
{
return tlbcam_addrs[tlbcam_index - 1].limit - PAGE_OFFSET + 1;
}
void flush_instruction_cache(void)
{
unsigned long tmp;
tmp = mfspr(SPRN_L1CSR1);
tmp |= L1CSR1_ICFI | L1CSR1_ICLFR;
mtspr(SPRN_L1CSR1, tmp);
isync();
}
/*
* MMU_init_hw does the chip-specific initialization of the MMU hardware.
*/
void __init MMU_init_hw(void)
{
flush_instruction_cache();
}
static unsigned long __init tlbcam_sz(int idx)
{
return tlbcam_addrs[idx].limit - tlbcam_addrs[idx].start + 1;
}
void __init adjust_total_lowmem(void)
{
unsigned long ram;
int i;
/* adjust lowmem size to __max_low_memory */
ram = min((phys_addr_t)__max_low_memory, (phys_addr_t)total_lowmem);
i = switch_to_as1();
__max_low_memory = map_mem_in_cams(ram, CONFIG_LOWMEM_CAM_NUM, false, true);
restore_to_as0(i, 0, NULL, 1);
pr_info("Memory CAM mapping: ");
for (i = 0; i < tlbcam_index - 1; i++)
pr_cont("%lu/", tlbcam_sz(i) >> 20);
pr_cont("%lu Mb, residual: %dMb\n", tlbcam_sz(tlbcam_index - 1) >> 20,
(unsigned int)((total_lowmem - __max_low_memory) >> 20));
memblock_set_current_limit(memstart_addr + __max_low_memory);
}
#ifdef CONFIG_STRICT_KERNEL_RWX
int mmu_mark_rodata_ro(void)
{
unsigned long remapped;
remapped = map_mem_in_cams(__max_low_memory, CONFIG_LOWMEM_CAM_NUM, false, false);
if (WARN_ON(__max_low_memory != remapped))
return -EINVAL;
return 0;
}
#endif
int mmu_mark_initmem_nx(void)
{
/* Everything is done in mmu_mark_rodata_ro() */
return 0;
}
void setup_initial_memory_limit(phys_addr_t first_memblock_base,
phys_addr_t first_memblock_size)
{
phys_addr_t limit = first_memblock_base + first_memblock_size;
/* 64M mapped initially according to head_fsl_booke.S */
memblock_set_current_limit(min_t(u64, limit, 0x04000000));
}
#ifdef CONFIG_RELOCATABLE
int __initdata is_second_reloc;
notrace void __init relocate_init(u64 dt_ptr, phys_addr_t start)
{
unsigned long base = kernstart_virt_addr;
phys_addr_t size;
kernstart_addr = start;
if (is_second_reloc) {
virt_phys_offset = PAGE_OFFSET - memstart_addr;
kaslr_late_init();
return;
}
/*
* Relocatable kernel support based on processing of dynamic
* relocation entries. Before we get the real memstart_addr,
* We will compute the virt_phys_offset like this:
* virt_phys_offset = stext.run - kernstart_addr
*
* stext.run = (KERNELBASE & ~0x3ffffff) +
* (kernstart_addr & 0x3ffffff)
* When we relocate, we have :
*
* (kernstart_addr & 0x3ffffff) = (stext.run & 0x3ffffff)
*
* hence:
* virt_phys_offset = (KERNELBASE & ~0x3ffffff) -
* (kernstart_addr & ~0x3ffffff)
*
*/
start &= ~0x3ffffff;
base &= ~0x3ffffff;
virt_phys_offset = base - start;
early_get_first_memblock_info(__va(dt_ptr), &size);
/*
* We now get the memstart_addr, then we should check if this
* address is the same as what the PAGE_OFFSET map to now. If
* not we have to change the map of PAGE_OFFSET to memstart_addr
* and do a second relocation.
*/
if (start != memstart_addr) {
int n;
long offset = start - memstart_addr;
is_second_reloc = 1;
n = switch_to_as1();
/* map a 64M area for the second relocation */
if (memstart_addr > start)
map_mem_in_cams(0x4000000, CONFIG_LOWMEM_CAM_NUM,
false, true);
else
map_mem_in_cams_addr(start, PAGE_OFFSET + offset,
0x4000000, CONFIG_LOWMEM_CAM_NUM,
false, true);
restore_to_as0(n, offset, __va(dt_ptr), 1);
/* We should never reach here */
panic("Relocation error");
}
kaslr_early_init(__va(dt_ptr), size);
}
#endif
#endif
@@ -0,0 +1,193 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PPC Huge TLB Page Support for Book3E MMU
*
* Copyright (C) 2009 David Gibson, IBM Corporation.
* Copyright (C) 2011 Becky Bruce, Freescale Semiconductor
*
*/
#include <linux/mm.h>
#include <linux/hugetlb.h>
#include <asm/mmu.h>
#ifdef CONFIG_PPC64
#include <asm/paca.h>
static inline int tlb1_next(void)
{
struct paca_struct *paca = get_paca();
struct tlb_core_data *tcd;
int this, next;
tcd = paca->tcd_ptr;
this = tcd->esel_next;
next = this + 1;
if (next >= tcd->esel_max)
next = tcd->esel_first;
tcd->esel_next = next;
return this;
}
static inline void book3e_tlb_lock(void)
{
struct paca_struct *paca = get_paca();
unsigned long tmp;
int token = smp_processor_id() + 1;
/*
* Besides being unnecessary in the absence of SMT, this
* check prevents trying to do lbarx/stbcx. on e5500 which
* doesn't implement either feature.
*/
if (!cpu_has_feature(CPU_FTR_SMT))
return;
asm volatile(".machine push;"
".machine e6500;"
"1: lbarx %0, 0, %1;"
"cmpwi %0, 0;"
"bne 2f;"
"stbcx. %2, 0, %1;"
"bne 1b;"
"b 3f;"
"2: lbzx %0, 0, %1;"
"cmpwi %0, 0;"
"bne 2b;"
"b 1b;"
"3:"
".machine pop;"
: "=&r" (tmp)
: "r" (&paca->tcd_ptr->lock), "r" (token)
: "memory");
}
static inline void book3e_tlb_unlock(void)
{
struct paca_struct *paca = get_paca();
if (!cpu_has_feature(CPU_FTR_SMT))
return;
isync();
paca->tcd_ptr->lock = 0;
}
#else
static inline int tlb1_next(void)
{
int index, ncams;
ncams = mfspr(SPRN_TLB1CFG) & TLBnCFG_N_ENTRY;
index = this_cpu_read(next_tlbcam_idx);
/* Just round-robin the entries and wrap when we hit the end */
if (unlikely(index == ncams - 1))
__this_cpu_write(next_tlbcam_idx, tlbcam_index);
else
__this_cpu_inc(next_tlbcam_idx);
return index;
}
static inline void book3e_tlb_lock(void)
{
}
static inline void book3e_tlb_unlock(void)
{
}
#endif
static inline int book3e_tlb_exists(unsigned long ea, unsigned long pid)
{
int found = 0;
mtspr(SPRN_MAS6, pid << 16);
asm volatile(
"tlbsx 0,%1\n"
"mfspr %0,0x271\n"
"srwi %0,%0,31\n"
: "=&r"(found) : "r"(ea));
return found;
}
static void
book3e_hugetlb_preload(struct vm_area_struct *vma, unsigned long ea, pte_t pte)
{
unsigned long mas1, mas2;
u64 mas7_3;
unsigned long psize, tsize, shift;
unsigned long flags;
struct mm_struct *mm;
int index;
if (unlikely(is_kernel_addr(ea)))
return;
mm = vma->vm_mm;
psize = vma_mmu_pagesize(vma);
shift = __ilog2(psize);
tsize = shift - 10;
/*
* We can't be interrupted while we're setting up the MAS
* registers or after we've confirmed that no tlb exists.
*/
local_irq_save(flags);
book3e_tlb_lock();
if (unlikely(book3e_tlb_exists(ea, mm->context.id))) {
book3e_tlb_unlock();
local_irq_restore(flags);
return;
}
/* We have to use the CAM(TLB1) on FSL parts for hugepages */
index = tlb1_next();
mtspr(SPRN_MAS0, MAS0_ESEL(index) | MAS0_TLBSEL(1));
mas1 = MAS1_VALID | MAS1_TID(mm->context.id) | MAS1_TSIZE(tsize);
mas2 = ea & ~((1UL << shift) - 1);
mas2 |= (pte_val(pte) >> PTE_WIMGE_SHIFT) & MAS2_WIMGE_MASK;
mas7_3 = (u64)pte_pfn(pte) << PAGE_SHIFT;
mas7_3 |= (pte_val(pte) >> PTE_BAP_SHIFT) & MAS3_BAP_MASK;
if (!pte_dirty(pte))
mas7_3 &= ~(MAS3_SW|MAS3_UW);
mtspr(SPRN_MAS1, mas1);
mtspr(SPRN_MAS2, mas2);
if (mmu_has_feature(MMU_FTR_BIG_PHYS))
mtspr(SPRN_MAS7, upper_32_bits(mas7_3));
mtspr(SPRN_MAS3, lower_32_bits(mas7_3));
asm volatile ("tlbwe");
book3e_tlb_unlock();
local_irq_restore(flags);
}
/*
* This is called at the end of handling a user page fault, when the
* fault has been handled by updating a PTE in the linux page tables.
*
* This must always be called with the pte lock held.
*/
void __update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
{
if (is_vm_hugetlb_page(vma))
book3e_hugetlb_preload(vma, address, *ptep);
}
void flush_hugetlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
{
struct hstate *hstate = hstate_file(vma->vm_file);
unsigned long tsize = huge_page_shift(hstate) - 10;
__flush_tlb_page(vma->vm_mm, vmaddr, tsize, 0);
}
@@ -0,0 +1,395 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright (C) 2019 Jason Yan <yanaijie@huawei.com>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/stddef.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/memblock.h>
#include <linux/libfdt.h>
#include <linux/crash_reserve.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <asm/cacheflush.h>
#include <asm/kdump.h>
#include <mm/mmu_decl.h>
struct regions {
unsigned long pa_start;
unsigned long pa_end;
unsigned long kernel_size;
unsigned long dtb_start;
unsigned long dtb_end;
unsigned long initrd_start;
unsigned long initrd_end;
unsigned long crash_start;
unsigned long crash_end;
int reserved_mem;
int reserved_mem_addr_cells;
int reserved_mem_size_cells;
};
struct regions __initdata regions;
static __init void kaslr_get_cmdline(void *fdt)
{
early_init_dt_scan_chosen(boot_command_line);
}
static unsigned long __init rotate_xor(unsigned long hash, const void *area,
size_t size)
{
size_t i;
const unsigned long *ptr = area;
for (i = 0; i < size / sizeof(hash); i++) {
/* Rotate by odd number of bits and XOR. */
hash = (hash << ((sizeof(hash) * 8) - 7)) | (hash >> 7);
hash ^= ptr[i];
}
return hash;
}
/* Attempt to create a simple starting entropy. This can make it defferent for
* every build but it is still not enough. Stronger entropy should
* be added to make it change for every boot.
*/
static unsigned long __init get_boot_seed(void *fdt)
{
unsigned long hash = 0;
/* build-specific string for starting entropy. */
hash = rotate_xor(hash, linux_banner, strlen(linux_banner));
hash = rotate_xor(hash, fdt, fdt_totalsize(fdt));
return hash;
}
static __init u64 get_kaslr_seed(void *fdt)
{
int node, len;
fdt64_t *prop;
u64 ret;
node = fdt_path_offset(fdt, "/chosen");
if (node < 0)
return 0;
prop = fdt_getprop_w(fdt, node, "kaslr-seed", &len);
if (!prop || len != sizeof(u64))
return 0;
ret = fdt64_to_cpu(*prop);
*prop = 0;
return ret;
}
static __init bool regions_overlap(u32 s1, u32 e1, u32 s2, u32 e2)
{
return e1 >= s2 && e2 >= s1;
}
static __init bool overlaps_reserved_region(const void *fdt, u32 start,
u32 end)
{
int subnode, len, i;
u64 base, size;
/* check for overlap with /memreserve/ entries */
for (i = 0; i < fdt_num_mem_rsv(fdt); i++) {
if (fdt_get_mem_rsv(fdt, i, &base, &size) < 0)
continue;
if (regions_overlap(start, end, base, base + size))
return true;
}
if (regions.reserved_mem < 0)
return false;
/* check for overlap with static reservations in /reserved-memory */
for (subnode = fdt_first_subnode(fdt, regions.reserved_mem);
subnode >= 0;
subnode = fdt_next_subnode(fdt, subnode)) {
const fdt32_t *reg;
u64 rsv_end;
len = 0;
reg = fdt_getprop(fdt, subnode, "reg", &len);
while (len >= (regions.reserved_mem_addr_cells +
regions.reserved_mem_size_cells)) {
base = fdt32_to_cpu(reg[0]);
if (regions.reserved_mem_addr_cells == 2)
base = (base << 32) | fdt32_to_cpu(reg[1]);
reg += regions.reserved_mem_addr_cells;
len -= 4 * regions.reserved_mem_addr_cells;
size = fdt32_to_cpu(reg[0]);
if (regions.reserved_mem_size_cells == 2)
size = (size << 32) | fdt32_to_cpu(reg[1]);
reg += regions.reserved_mem_size_cells;
len -= 4 * regions.reserved_mem_size_cells;
if (base >= regions.pa_end)
continue;
rsv_end = min(base + size, (u64)U32_MAX);
if (regions_overlap(start, end, base, rsv_end))
return true;
}
}
return false;
}
static __init bool overlaps_region(const void *fdt, u32 start,
u32 end)
{
if (regions_overlap(start, end, __pa(_stext), __pa(_end)))
return true;
if (regions_overlap(start, end, regions.dtb_start,
regions.dtb_end))
return true;
if (regions_overlap(start, end, regions.initrd_start,
regions.initrd_end))
return true;
if (regions_overlap(start, end, regions.crash_start,
regions.crash_end))
return true;
return overlaps_reserved_region(fdt, start, end);
}
static void __init get_crash_kernel(void *fdt, unsigned long size)
{
#ifdef CONFIG_CRASH_RESERVE
unsigned long long crash_size, crash_base;
int ret;
ret = parse_crashkernel(boot_command_line, size, &crash_size,
&crash_base, NULL, NULL, NULL);
if (ret != 0 || crash_size == 0)
return;
if (crash_base == 0)
crash_base = KDUMP_KERNELBASE;
regions.crash_start = (unsigned long)crash_base;
regions.crash_end = (unsigned long)(crash_base + crash_size);
pr_debug("crash_base=0x%llx crash_size=0x%llx\n", crash_base, crash_size);
#endif
}
static void __init get_initrd_range(void *fdt)
{
u64 start, end;
int node, len;
const __be32 *prop;
node = fdt_path_offset(fdt, "/chosen");
if (node < 0)
return;
prop = fdt_getprop(fdt, node, "linux,initrd-start", &len);
if (!prop)
return;
start = of_read_number(prop, len / 4);
prop = fdt_getprop(fdt, node, "linux,initrd-end", &len);
if (!prop)
return;
end = of_read_number(prop, len / 4);
regions.initrd_start = (unsigned long)start;
regions.initrd_end = (unsigned long)end;
pr_debug("initrd_start=0x%llx initrd_end=0x%llx\n", start, end);
}
static __init unsigned long get_usable_address(const void *fdt,
unsigned long start,
unsigned long offset)
{
unsigned long pa;
unsigned long pa_end;
for (pa = offset; (long)pa > (long)start; pa -= SZ_16K) {
pa_end = pa + regions.kernel_size;
if (overlaps_region(fdt, pa, pa_end))
continue;
return pa;
}
return 0;
}
static __init void get_cell_sizes(const void *fdt, int node, int *addr_cells,
int *size_cells)
{
const int *prop;
int len;
/*
* Retrieve the #address-cells and #size-cells properties
* from the 'node', or use the default if not provided.
*/
*addr_cells = *size_cells = 1;
prop = fdt_getprop(fdt, node, "#address-cells", &len);
if (len == 4)
*addr_cells = fdt32_to_cpu(*prop);
prop = fdt_getprop(fdt, node, "#size-cells", &len);
if (len == 4)
*size_cells = fdt32_to_cpu(*prop);
}
static unsigned long __init kaslr_legal_offset(void *dt_ptr, unsigned long index,
unsigned long offset)
{
unsigned long koffset = 0;
unsigned long start;
while ((long)index >= 0) {
offset = memstart_addr + index * SZ_64M + offset;
start = memstart_addr + index * SZ_64M;
koffset = get_usable_address(dt_ptr, start, offset);
if (koffset)
break;
index--;
}
if (koffset != 0)
koffset -= memstart_addr;
return koffset;
}
static inline __init bool kaslr_disabled(void)
{
return strstr(boot_command_line, "nokaslr") != NULL;
}
static unsigned long __init kaslr_choose_location(void *dt_ptr, phys_addr_t size,
unsigned long kernel_sz)
{
unsigned long offset, random;
unsigned long ram, linear_sz;
u64 seed;
unsigned long index;
kaslr_get_cmdline(dt_ptr);
if (kaslr_disabled())
return 0;
random = get_boot_seed(dt_ptr);
seed = get_tb() << 32;
seed ^= get_tb();
random = rotate_xor(random, &seed, sizeof(seed));
/*
* Retrieve (and wipe) the seed from the FDT
*/
seed = get_kaslr_seed(dt_ptr);
if (seed)
random = rotate_xor(random, &seed, sizeof(seed));
else
pr_warn("KASLR: No safe seed for randomizing the kernel base.\n");
ram = min_t(phys_addr_t, __max_low_memory, size);
ram = map_mem_in_cams(ram, CONFIG_LOWMEM_CAM_NUM, true, true);
linear_sz = min_t(unsigned long, ram, SZ_512M);
/* If the linear size is smaller than 64M, do not randomize */
if (linear_sz < SZ_64M)
return 0;
/* check for a reserved-memory node and record its cell sizes */
regions.reserved_mem = fdt_path_offset(dt_ptr, "/reserved-memory");
if (regions.reserved_mem >= 0)
get_cell_sizes(dt_ptr, regions.reserved_mem,
&regions.reserved_mem_addr_cells,
&regions.reserved_mem_size_cells);
regions.pa_start = memstart_addr;
regions.pa_end = memstart_addr + linear_sz;
regions.dtb_start = __pa(dt_ptr);
regions.dtb_end = __pa(dt_ptr) + fdt_totalsize(dt_ptr);
regions.kernel_size = kernel_sz;
get_initrd_range(dt_ptr);
get_crash_kernel(dt_ptr, ram);
/*
* Decide which 64M we want to start
* Only use the low 8 bits of the random seed
*/
index = random & 0xFF;
index %= linear_sz / SZ_64M;
/* Decide offset inside 64M */
offset = random % (SZ_64M - kernel_sz);
offset = round_down(offset, SZ_16K);
return kaslr_legal_offset(dt_ptr, index, offset);
}
/*
* To see if we need to relocate the kernel to a random offset
* void *dt_ptr - address of the device tree
* phys_addr_t size - size of the first memory block
*/
notrace void __init kaslr_early_init(void *dt_ptr, phys_addr_t size)
{
unsigned long tlb_virt;
phys_addr_t tlb_phys;
unsigned long offset;
unsigned long kernel_sz;
kernel_sz = (unsigned long)_end - (unsigned long)_stext;
offset = kaslr_choose_location(dt_ptr, size, kernel_sz);
if (offset == 0)
return;
kernstart_virt_addr += offset;
kernstart_addr += offset;
is_second_reloc = 1;
if (offset >= SZ_64M) {
tlb_virt = round_down(kernstart_virt_addr, SZ_64M);
tlb_phys = round_down(kernstart_addr, SZ_64M);
/* Create kernel map to relocate in */
create_kaslr_tlb_entry(1, tlb_virt, tlb_phys);
}
/* Copy the kernel to its new location and run */
memcpy((void *)kernstart_virt_addr, (void *)_stext, kernel_sz);
flush_icache_range(kernstart_virt_addr, kernstart_virt_addr + kernel_sz);
reloc_kernel_entry(dt_ptr, kernstart_virt_addr);
}
void __init kaslr_late_init(void)
{
/* If randomized, clear the original kernel */
if (kernstart_virt_addr != KERNELBASE) {
unsigned long kernel_sz;
kernel_sz = (unsigned long)_end - kernstart_virt_addr;
memzero_explicit((void *)KERNELBASE, kernel_sz);
}
}
+27
View File
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* This file contains the routines for initializing kernel userspace protection
*/
#include <linux/export.h>
#include <linux/init.h>
#include <linux/printk.h>
#include <linux/smp.h>
#include <asm/kup.h>
#include <asm/smp.h>
#ifdef CONFIG_PPC_KUAP
void setup_kuap(bool disabled)
{
if (disabled) {
if (smp_processor_id() == boot_cpuid)
cur_cpu_spec->mmu_features &= ~MMU_FTR_KUAP;
return;
}
pr_info("Activating Kernel Userspace Access Protection\n");
prevent_user_access(KUAP_READ_WRITE);
}
#endif
@@ -0,0 +1,404 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* This file contains the routines for handling the MMU on those
* PowerPC implementations where the MMU is not using the hash
* table, such as 8xx, 4xx, BookE's etc...
*
* Copyright 2008 Ben Herrenschmidt <benh@kernel.crashing.org>
* IBM Corp.
*
* Derived from previous arch/powerpc/mm/mmu_context.c
* and arch/powerpc/include/asm/mmu_context.h
*
* TODO:
*
* - The global context lock will not scale very well
* - The maps should be dynamically allocated to allow for processors
* that support more PID bits at runtime
* - Implement flush_tlb_mm() by making the context stale and picking
* a new one
* - More aggressively clear stale map bits and maybe find some way to
* also clear mm->cpu_vm_mask bits when processes are migrated
*/
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/memblock.h>
#include <linux/notifier.h>
#include <linux/cpu.h>
#include <linux/slab.h>
#include <asm/mmu_context.h>
#include <asm/tlbflush.h>
#include <asm/smp.h>
#include <asm/kup.h>
#include <mm/mmu_decl.h>
/*
* Room for two PTE table pointers, usually the kernel and current user
* pointer to their respective root page table (pgdir).
*/
void *abatron_pteptrs[2];
/*
* The MPC8xx has only 16 contexts. We rotate through them on each task switch.
* A better way would be to keep track of tasks that own contexts, and implement
* an LRU usage. That way very active tasks don't always have to pay the TLB
* reload overhead. The kernel pages are mapped shared, so the kernel can run on
* behalf of any task that makes a kernel entry. Shared does not mean they are
* not protected, just that the ASID comparison is not performed. -- Dan
*
* The IBM4xx has 256 contexts, so we can just rotate through these as a way of
* "switching" contexts. If the TID of the TLB is zero, the PID/TID comparison
* is disabled, so we can use a TID of zero to represent all kernel pages as
* shared among all contexts. -- Dan
*
* The IBM 47x core supports 16-bit PIDs, thus 65535 contexts. We should
* normally never have to steal though the facility is present if needed.
* -- BenH
*/
#define FIRST_CONTEXT 1
#if defined(CONFIG_PPC_8xx)
#define LAST_CONTEXT 16
#elif defined(CONFIG_PPC_47x)
#define LAST_CONTEXT 65535
#else
#define LAST_CONTEXT 255
#endif
static unsigned int next_context, nr_free_contexts;
static unsigned long *context_map;
static unsigned long *stale_map[NR_CPUS];
static struct mm_struct **context_mm;
static DEFINE_RAW_SPINLOCK(context_lock);
#define CTX_MAP_SIZE \
(sizeof(unsigned long) * (LAST_CONTEXT / BITS_PER_LONG + 1))
/* Steal a context from a task that has one at the moment.
*
* This is used when we are running out of available PID numbers
* on the processors.
*
* This isn't an LRU system, it just frees up each context in
* turn (sort-of pseudo-random replacement :). This would be the
* place to implement an LRU scheme if anyone was motivated to do it.
* -- paulus
*
* For context stealing, we use a slightly different approach for
* SMP and UP. Basically, the UP one is simpler and doesn't use
* the stale map as we can just flush the local CPU
* -- benh
*/
static unsigned int steal_context_smp(unsigned int id)
{
struct mm_struct *mm;
unsigned int cpu, max, i;
max = LAST_CONTEXT - FIRST_CONTEXT;
/* Attempt to free next_context first and then loop until we manage */
while (max--) {
/* Pick up the victim mm */
mm = context_mm[id];
/* We have a candidate victim, check if it's active, on SMP
* we cannot steal active contexts
*/
if (mm->context.active) {
id++;
if (id > LAST_CONTEXT)
id = FIRST_CONTEXT;
continue;
}
/* Mark this mm has having no context anymore */
mm->context.id = MMU_NO_CONTEXT;
/* Mark it stale on all CPUs that used this mm. For threaded
* implementations, we set it on all threads on each core
* represented in the mask. A future implementation will use
* a core map instead but this will do for now.
*/
for_each_cpu(cpu, mm_cpumask(mm)) {
for (i = cpu_first_thread_sibling(cpu);
i <= cpu_last_thread_sibling(cpu); i++) {
if (stale_map[i])
__set_bit(id, stale_map[i]);
}
cpu = i - 1;
}
return id;
}
/* This will happen if you have more CPUs than available contexts,
* all we can do here is wait a bit and try again
*/
raw_spin_unlock(&context_lock);
cpu_relax();
raw_spin_lock(&context_lock);
/* This will cause the caller to try again */
return MMU_NO_CONTEXT;
}
static unsigned int steal_all_contexts(void)
{
struct mm_struct *mm;
int cpu = smp_processor_id();
unsigned int id;
for (id = FIRST_CONTEXT; id <= LAST_CONTEXT; id++) {
/* Pick up the victim mm */
mm = context_mm[id];
/* Mark this mm as having no context anymore */
mm->context.id = MMU_NO_CONTEXT;
if (id != FIRST_CONTEXT) {
context_mm[id] = NULL;
__clear_bit(id, context_map);
}
if (IS_ENABLED(CONFIG_SMP))
__clear_bit(id, stale_map[cpu]);
}
/* Flush the TLB for all contexts (not to be used on SMP) */
_tlbil_all();
nr_free_contexts = LAST_CONTEXT - FIRST_CONTEXT;
return FIRST_CONTEXT;
}
/* Note that this will also be called on SMP if all other CPUs are
* offlined, which means that it may be called for cpu != 0. For
* this to work, we somewhat assume that CPUs that are onlined
* come up with a fully clean TLB (or are cleaned when offlined)
*/
static unsigned int steal_context_up(unsigned int id)
{
struct mm_struct *mm;
int cpu = smp_processor_id();
/* Pick up the victim mm */
mm = context_mm[id];
/* Flush the TLB for that context */
local_flush_tlb_mm(mm);
/* Mark this mm has having no context anymore */
mm->context.id = MMU_NO_CONTEXT;
/* XXX This clear should ultimately be part of local_flush_tlb_mm */
if (IS_ENABLED(CONFIG_SMP))
__clear_bit(id, stale_map[cpu]);
return id;
}
static void set_context(unsigned long id, pgd_t *pgd)
{
if (IS_ENABLED(CONFIG_PPC_8xx)) {
mtspr(SPRN_M_TWB, __pa(pgd));
/* Update context */
mtspr(SPRN_M_CASID, id - 1);
/* sync */
mb();
} else if (kuap_is_disabled()) {
mtspr(SPRN_PID, id);
isync();
}
}
void switch_mmu_context(struct mm_struct *prev, struct mm_struct *next,
struct task_struct *tsk)
{
unsigned int id;
unsigned int i, cpu = smp_processor_id();
unsigned long *map;
/* No lockless fast path .. yet */
raw_spin_lock(&context_lock);
if (IS_ENABLED(CONFIG_SMP)) {
/* Mark us active and the previous one not anymore */
next->context.active++;
if (prev) {
WARN_ON(prev->context.active < 1);
prev->context.active--;
}
}
again:
/* If we already have a valid assigned context, skip all that */
id = next->context.id;
if (likely(id != MMU_NO_CONTEXT))
goto ctxt_ok;
/* We really don't have a context, let's try to acquire one */
id = next_context;
if (id > LAST_CONTEXT)
id = FIRST_CONTEXT;
map = context_map;
/* No more free contexts, let's try to steal one */
if (nr_free_contexts == 0) {
if (num_online_cpus() > 1) {
id = steal_context_smp(id);
if (id == MMU_NO_CONTEXT)
goto again;
goto stolen;
}
if (IS_ENABLED(CONFIG_PPC_8xx))
id = steal_all_contexts();
else
id = steal_context_up(id);
goto stolen;
}
nr_free_contexts--;
/* We know there's at least one free context, try to find it */
while (__test_and_set_bit(id, map)) {
id = find_next_zero_bit(map, LAST_CONTEXT+1, id);
if (id > LAST_CONTEXT)
id = FIRST_CONTEXT;
}
stolen:
next_context = id + 1;
context_mm[id] = next;
next->context.id = id;
ctxt_ok:
/* If that context got marked stale on this CPU, then flush the
* local TLB for it and unmark it before we use it
*/
if (IS_ENABLED(CONFIG_SMP) && test_bit(id, stale_map[cpu])) {
local_flush_tlb_mm(next);
/* XXX This clear should ultimately be part of local_flush_tlb_mm */
for (i = cpu_first_thread_sibling(cpu);
i <= cpu_last_thread_sibling(cpu); i++) {
if (stale_map[i])
__clear_bit(id, stale_map[i]);
}
}
/* Flick the MMU and release lock */
if (IS_ENABLED(CONFIG_BDI_SWITCH))
abatron_pteptrs[1] = next->pgd;
set_context(id, next->pgd);
#if defined(CONFIG_BOOKE) && defined(CONFIG_PPC_KUAP)
tsk->thread.pid = id;
#endif
raw_spin_unlock(&context_lock);
}
/*
* Set up the context for a new address space.
*/
int init_new_context(struct task_struct *t, struct mm_struct *mm)
{
mm->context.id = MMU_NO_CONTEXT;
mm->context.active = 0;
pte_frag_set(&mm->context, NULL);
return 0;
}
/*
* We're finished using the context for an address space.
*/
void destroy_context(struct mm_struct *mm)
{
unsigned long flags;
unsigned int id;
if (mm->context.id == MMU_NO_CONTEXT)
return;
WARN_ON(mm->context.active != 0);
raw_spin_lock_irqsave(&context_lock, flags);
id = mm->context.id;
if (id != MMU_NO_CONTEXT) {
__clear_bit(id, context_map);
mm->context.id = MMU_NO_CONTEXT;
context_mm[id] = NULL;
nr_free_contexts++;
}
raw_spin_unlock_irqrestore(&context_lock, flags);
}
static int mmu_ctx_cpu_prepare(unsigned int cpu)
{
/* We don't touch CPU 0 map, it's allocated at aboot and kept
* around forever
*/
if (cpu == boot_cpuid)
return 0;
stale_map[cpu] = kzalloc(CTX_MAP_SIZE, GFP_KERNEL);
return 0;
}
static int mmu_ctx_cpu_dead(unsigned int cpu)
{
#ifdef CONFIG_HOTPLUG_CPU
if (cpu == boot_cpuid)
return 0;
kfree(stale_map[cpu]);
stale_map[cpu] = NULL;
/* We also clear the cpu_vm_mask bits of CPUs going away */
clear_tasks_mm_cpumask(cpu);
#endif
return 0;
}
/*
* Initialize the context management stuff.
*/
void __init mmu_context_init(void)
{
/* Mark init_mm as being active on all possible CPUs since
* we'll get called with prev == init_mm the first time
* we schedule on a given CPU
*/
init_mm.context.active = NR_CPUS;
/*
* Allocate the maps used by context management
*/
context_map = memblock_alloc_or_panic(CTX_MAP_SIZE, SMP_CACHE_BYTES);
context_mm = memblock_alloc_or_panic(sizeof(void *) * (LAST_CONTEXT + 1),
SMP_CACHE_BYTES);
if (IS_ENABLED(CONFIG_SMP)) {
stale_map[boot_cpuid] = memblock_alloc_or_panic(CTX_MAP_SIZE, SMP_CACHE_BYTES);
cpuhp_setup_state_nocalls(CPUHP_POWERPC_MMU_CTX_PREPARE,
"powerpc/mmu/ctx:prepare",
mmu_ctx_cpu_prepare, mmu_ctx_cpu_dead);
}
printk(KERN_INFO
"MMU: Allocated %zu bytes of context maps for %d contexts\n",
2 * CTX_MAP_SIZE + (sizeof(void *) * (LAST_CONTEXT + 1)),
LAST_CONTEXT - FIRST_CONTEXT + 1);
/*
* Some processors have too few contexts to reserve one for
* init_mm, and require using context 0 for a normal task.
* Other processors reserve the use of context zero for the kernel.
* This code assumes FIRST_CONTEXT < 32.
*/
context_map[0] = (1 << FIRST_CONTEXT) - 1;
next_context = FIRST_CONTEXT;
nr_free_contexts = LAST_CONTEXT - FIRST_CONTEXT + 1;
}
+341
View File
@@ -0,0 +1,341 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* This file contains the routines for TLB flushing.
* On machines where the MMU does not use a hash table to store virtual to
* physical translations (ie, SW loaded TLBs or Book3E compilant processors,
* this does -not- include 603 however which shares the implementation with
* hash based processors)
*
* -- BenH
*
* Copyright 2008,2009 Ben Herrenschmidt <benh@kernel.crashing.org>
* IBM Corp.
*
* Derived from arch/ppc/mm/init.c:
* Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
*
* Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
* and Cort Dougan (PReP) (cort@cs.nmt.edu)
* Copyright (C) 1996 Paul Mackerras
*
* Derived from "arch/i386/mm/init.c"
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*/
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/highmem.h>
#include <linux/pagemap.h>
#include <linux/preempt.h>
#include <linux/spinlock.h>
#include <linux/memblock.h>
#include <linux/of_fdt.h>
#include <linux/hugetlb.h>
#include <asm/pgalloc.h>
#include <asm/tlbflush.h>
#include <asm/tlb.h>
#include <asm/text-patching.h>
#include <asm/cputhreads.h>
#include <asm/hugetlb.h>
#include <asm/paca.h>
#include <mm/mmu_decl.h>
/*
* This struct lists the sw-supported page sizes. The hardawre MMU may support
* other sizes not listed here. The .ind field is only used on MMUs that have
* indirect page table entries.
*/
#ifdef CONFIG_PPC_E500
struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT] = {
[MMU_PAGE_4K] = {
.shift = 12,
},
[MMU_PAGE_2M] = {
.shift = 21,
},
[MMU_PAGE_4M] = {
.shift = 22,
},
[MMU_PAGE_16M] = {
.shift = 24,
},
[MMU_PAGE_64M] = {
.shift = 26,
},
[MMU_PAGE_256M] = {
.shift = 28,
},
[MMU_PAGE_1G] = {
.shift = 30,
},
};
static inline int mmu_get_tsize(int psize)
{
return mmu_psize_defs[psize].shift - 10;
}
#else
static inline int mmu_get_tsize(int psize)
{
/* This isn't used on !Book3E for now */
return 0;
}
#endif
#ifdef CONFIG_PPC_8xx
struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT] = {
[MMU_PAGE_4K] = {
.shift = 12,
},
[MMU_PAGE_16K] = {
.shift = 14,
},
[MMU_PAGE_512K] = {
.shift = 19,
},
[MMU_PAGE_8M] = {
.shift = 23,
},
};
#endif
#ifdef CONFIG_PPC_E500
/* next_tlbcam_idx is used to round-robin tlbcam entry assignment */
DEFINE_PER_CPU(int, next_tlbcam_idx);
EXPORT_PER_CPU_SYMBOL(next_tlbcam_idx);
#endif
/*
* Base TLB flushing operations:
*
* - flush_tlb_mm(mm) flushes the specified mm context TLB's
* - flush_tlb_page(vma, vmaddr) flushes one page
* - flush_tlb_range(vma, start, end) flushes a range of pages
* - flush_tlb_kernel_range(start, end) flushes kernel pages
*
* - local_* variants of page and mm only apply to the current
* processor
*/
#ifndef CONFIG_PPC_8xx
/*
* These are the base non-SMP variants of page and mm flushing
*/
void local_flush_tlb_mm(struct mm_struct *mm)
{
unsigned int pid;
preempt_disable();
pid = mm->context.id;
if (pid != MMU_NO_CONTEXT)
_tlbil_pid(pid);
preempt_enable();
}
EXPORT_SYMBOL(local_flush_tlb_mm);
void __local_flush_tlb_page(struct mm_struct *mm, unsigned long vmaddr,
int tsize, int ind)
{
unsigned int pid;
preempt_disable();
pid = mm ? mm->context.id : 0;
if (pid != MMU_NO_CONTEXT)
_tlbil_va(vmaddr, pid, tsize, ind);
preempt_enable();
}
void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
{
__local_flush_tlb_page(vma ? vma->vm_mm : NULL, vmaddr,
mmu_get_tsize(mmu_virtual_psize), 0);
}
EXPORT_SYMBOL(local_flush_tlb_page);
void local_flush_tlb_page_psize(struct mm_struct *mm,
unsigned long vmaddr, int psize)
{
__local_flush_tlb_page(mm, vmaddr, mmu_get_tsize(psize), 0);
}
EXPORT_SYMBOL(local_flush_tlb_page_psize);
#endif
/*
* And here are the SMP non-local implementations
*/
#ifdef CONFIG_SMP
static DEFINE_RAW_SPINLOCK(tlbivax_lock);
struct tlb_flush_param {
unsigned long addr;
unsigned int pid;
unsigned int tsize;
unsigned int ind;
};
static void do_flush_tlb_mm_ipi(void *param)
{
struct tlb_flush_param *p = param;
_tlbil_pid(p ? p->pid : 0);
}
static void do_flush_tlb_page_ipi(void *param)
{
struct tlb_flush_param *p = param;
_tlbil_va(p->addr, p->pid, p->tsize, p->ind);
}
/* Note on invalidations and PID:
*
* We snapshot the PID with preempt disabled. At this point, it can still
* change either because:
* - our context is being stolen (PID -> NO_CONTEXT) on another CPU
* - we are invaliating some target that isn't currently running here
* and is concurrently acquiring a new PID on another CPU
* - some other CPU is re-acquiring a lost PID for this mm
* etc...
*
* However, this shouldn't be a problem as we only guarantee
* invalidation of TLB entries present prior to this call, so we
* don't care about the PID changing, and invalidating a stale PID
* is generally harmless.
*/
void flush_tlb_mm(struct mm_struct *mm)
{
unsigned int pid;
preempt_disable();
pid = mm->context.id;
if (unlikely(pid == MMU_NO_CONTEXT))
goto no_context;
if (!mm_is_core_local(mm)) {
struct tlb_flush_param p = { .pid = pid };
/* Ignores smp_processor_id() even if set. */
smp_call_function_many(mm_cpumask(mm),
do_flush_tlb_mm_ipi, &p, 1);
}
_tlbil_pid(pid);
no_context:
preempt_enable();
}
EXPORT_SYMBOL(flush_tlb_mm);
void __flush_tlb_page(struct mm_struct *mm, unsigned long vmaddr,
int tsize, int ind)
{
struct cpumask *cpu_mask;
unsigned int pid;
/*
* This function as well as __local_flush_tlb_page() must only be called
* for user contexts.
*/
if (WARN_ON(!mm))
return;
preempt_disable();
pid = mm->context.id;
if (unlikely(pid == MMU_NO_CONTEXT))
goto bail;
cpu_mask = mm_cpumask(mm);
if (!mm_is_core_local(mm)) {
/* If broadcast tlbivax is supported, use it */
if (mmu_has_feature(MMU_FTR_USE_TLBIVAX_BCAST)) {
int lock = mmu_has_feature(MMU_FTR_LOCK_BCAST_INVAL);
if (lock)
raw_spin_lock(&tlbivax_lock);
_tlbivax_bcast(vmaddr, pid, tsize, ind);
if (lock)
raw_spin_unlock(&tlbivax_lock);
goto bail;
} else {
struct tlb_flush_param p = {
.pid = pid,
.addr = vmaddr,
.tsize = tsize,
.ind = ind,
};
/* Ignores smp_processor_id() even if set in cpu_mask */
smp_call_function_many(cpu_mask,
do_flush_tlb_page_ipi, &p, 1);
}
}
_tlbil_va(vmaddr, pid, tsize, ind);
bail:
preempt_enable();
}
void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
{
#ifdef CONFIG_HUGETLB_PAGE
if (vma && is_vm_hugetlb_page(vma))
flush_hugetlb_page(vma, vmaddr);
#endif
__flush_tlb_page(vma ? vma->vm_mm : NULL, vmaddr,
mmu_get_tsize(mmu_virtual_psize), 0);
}
EXPORT_SYMBOL(flush_tlb_page);
#endif /* CONFIG_SMP */
/*
* Flush kernel TLB entries in the given range
*/
#ifndef CONFIG_PPC_8xx
void flush_tlb_kernel_range(unsigned long start, unsigned long end)
{
#ifdef CONFIG_SMP
preempt_disable();
smp_call_function(do_flush_tlb_mm_ipi, NULL, 1);
_tlbil_pid(0);
preempt_enable();
#else
_tlbil_pid(0);
#endif
}
EXPORT_SYMBOL(flush_tlb_kernel_range);
#endif
/*
* Currently, for range flushing, we just do a full mm flush. This should
* be optimized based on a threshold on the size of the range, since
* some implementation can stack multiple tlbivax before a tlbsync but
* for now, we keep it that way
*/
void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
unsigned long end)
{
if (end - start == PAGE_SIZE && !(start & ~PAGE_MASK))
flush_tlb_page(vma, start);
else
flush_tlb_mm(vma->vm_mm);
}
EXPORT_SYMBOL(flush_tlb_range);
void tlb_flush(struct mmu_gather *tlb)
{
flush_tlb_mm(tlb->mm);
}
#ifndef CONFIG_PPC64
void __init early_init_mmu(void)
{
unsigned long root = of_get_flat_dt_root();
if (IS_ENABLED(CONFIG_PPC_47x) && IS_ENABLED(CONFIG_SMP) &&
of_get_flat_dt_prop(root, "cooperative-partition", NULL))
mmu_clear_feature(MMU_FTR_USE_TLBIVAX_BCAST);
}
#endif /* CONFIG_PPC64 */
@@ -0,0 +1,314 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2008,2009 Ben Herrenschmidt <benh@kernel.crashing.org>
* IBM Corp.
*
* Derived from arch/ppc/mm/init.c:
* Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
*
* Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
* and Cort Dougan (PReP) (cort@cs.nmt.edu)
* Copyright (C) 1996 Paul Mackerras
*
* Derived from "arch/i386/mm/init.c"
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
*/
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/pagemap.h>
#include <linux/memblock.h>
#include <asm/pgalloc.h>
#include <asm/tlbflush.h>
#include <asm/tlb.h>
#include <asm/text-patching.h>
#include <asm/cputhreads.h>
#include <mm/mmu_decl.h>
/* The variables below are currently only used on 64-bit Book3E
* though this will probably be made common with other nohash
* implementations at some point
*/
static int mmu_pte_psize; /* Page size used for PTE pages */
int mmu_vmemmap_psize; /* Page size used for the virtual mem map */
int book3e_htw_mode; /* HW tablewalk? Value is PPC_HTW_* */
unsigned long linear_map_top; /* Top of linear mapping */
/*
* Number of bytes to add to SPRN_SPRG_TLB_EXFRAME on crit/mcheck/debug
* exceptions. This is used for bolted and e6500 TLB miss handlers which
* do not modify this SPRG in the TLB miss code; for other TLB miss handlers,
* this is set to zero.
*/
int extlb_level_exc;
/*
* Handling of virtual linear page tables or indirect TLB entries
* flushing when PTE pages are freed
*/
void tlb_flush_pgtable(struct mmu_gather *tlb, unsigned long address)
{
int tsize = mmu_psize_defs[mmu_pte_psize].shift - 10;
if (book3e_htw_mode != PPC_HTW_NONE) {
unsigned long start = address & PMD_MASK;
unsigned long end = address + PMD_SIZE;
unsigned long size = 1UL << mmu_psize_defs[mmu_pte_psize].shift;
/* This isn't the most optimal, ideally we would factor out the
* while preempt & CPU mask mucking around, or even the IPI but
* it will do for now
*/
while (start < end) {
__flush_tlb_page(tlb->mm, start, tsize, 1);
start += size;
}
} else {
unsigned long rmask = 0xf000000000000000ul;
unsigned long rid = (address & rmask) | 0x1000000000000000ul;
unsigned long vpte = address & ~rmask;
vpte = (vpte >> (PAGE_SHIFT - 3)) & ~0xffful;
vpte |= rid;
__flush_tlb_page(tlb->mm, vpte, tsize, 0);
}
}
static void __init setup_page_sizes(void)
{
unsigned int tlb0cfg;
unsigned int eptcfg;
int psize;
unsigned int mmucfg = mfspr(SPRN_MMUCFG);
if ((mmucfg & MMUCFG_MAVN) == MMUCFG_MAVN_V1) {
unsigned int tlb1cfg = mfspr(SPRN_TLB1CFG);
unsigned int min_pg, max_pg;
min_pg = (tlb1cfg & TLBnCFG_MINSIZE) >> TLBnCFG_MINSIZE_SHIFT;
max_pg = (tlb1cfg & TLBnCFG_MAXSIZE) >> TLBnCFG_MAXSIZE_SHIFT;
for (psize = 0; psize < MMU_PAGE_COUNT; ++psize) {
struct mmu_psize_def *def;
unsigned int shift;
def = &mmu_psize_defs[psize];
shift = def->shift;
if (shift == 0 || shift & 1)
continue;
/* adjust to be in terms of 4^shift Kb */
shift = (shift - 10) >> 1;
if ((shift >= min_pg) && (shift <= max_pg))
def->flags |= MMU_PAGE_SIZE_DIRECT;
}
goto out;
}
if ((mmucfg & MMUCFG_MAVN) == MMUCFG_MAVN_V2) {
u32 tlb1cfg, tlb1ps;
tlb0cfg = mfspr(SPRN_TLB0CFG);
tlb1cfg = mfspr(SPRN_TLB1CFG);
tlb1ps = mfspr(SPRN_TLB1PS);
eptcfg = mfspr(SPRN_EPTCFG);
if ((tlb1cfg & TLBnCFG_IND) && (tlb0cfg & TLBnCFG_PT))
book3e_htw_mode = PPC_HTW_E6500;
/*
* We expect 4K subpage size and unrestricted indirect size.
* The lack of a restriction on indirect size is a Freescale
* extension, indicated by PSn = 0 but SPSn != 0.
*/
if (eptcfg != 2)
book3e_htw_mode = PPC_HTW_NONE;
for (psize = 0; psize < MMU_PAGE_COUNT; ++psize) {
struct mmu_psize_def *def = &mmu_psize_defs[psize];
if (!def->shift)
continue;
if (tlb1ps & (1U << (def->shift - 10))) {
def->flags |= MMU_PAGE_SIZE_DIRECT;
if (book3e_htw_mode && psize == MMU_PAGE_2M)
def->flags |= MMU_PAGE_SIZE_INDIRECT;
}
}
goto out;
}
out:
/* Cleanup array and print summary */
pr_info("MMU: Supported page sizes\n");
for (psize = 0; psize < MMU_PAGE_COUNT; ++psize) {
struct mmu_psize_def *def = &mmu_psize_defs[psize];
const char *__page_type_names[] = {
"unsupported",
"direct",
"indirect",
"direct & indirect"
};
if (def->flags == 0) {
def->shift = 0;
continue;
}
pr_info(" %8ld KB as %s\n", 1ul << (def->shift - 10),
__page_type_names[def->flags & 0x3]);
}
}
/*
* Early initialization of the MMU TLB code
*/
static void early_init_this_mmu(void)
{
unsigned int mas4;
/* Set MAS4 based on page table setting */
mas4 = 0x4 << MAS4_WIMGED_SHIFT;
switch (book3e_htw_mode) {
case PPC_HTW_E6500:
mas4 |= MAS4_INDD;
mas4 |= BOOK3E_PAGESZ_2M << MAS4_TSIZED_SHIFT;
mas4 |= MAS4_TLBSELD(1);
mmu_pte_psize = MMU_PAGE_2M;
break;
case PPC_HTW_NONE:
mas4 |= BOOK3E_PAGESZ_4K << MAS4_TSIZED_SHIFT;
mmu_pte_psize = mmu_virtual_psize;
break;
}
mtspr(SPRN_MAS4, mas4);
unsigned int num_cams;
bool map = true;
/* use a quarter of the TLBCAM for bolted linear map */
num_cams = (mfspr(SPRN_TLB1CFG) & TLBnCFG_N_ENTRY) / 4;
/*
* Only do the mapping once per core, or else the
* transient mapping would cause problems.
*/
#ifdef CONFIG_SMP
if (hweight32(get_tensr()) > 1)
map = false;
#endif
if (map)
linear_map_top = map_mem_in_cams(linear_map_top,
num_cams, false, true);
/* A sync won't hurt us after mucking around with
* the MMU configuration
*/
mb();
}
static void __init early_init_mmu_global(void)
{
/*
* Freescale booke only supports 4K pages in TLB0, so use that.
*/
mmu_vmemmap_psize = MMU_PAGE_4K;
/* XXX This code only checks for TLB 0 capabilities and doesn't
* check what page size combos are supported by the HW. It
* also doesn't handle the case where a separate array holds
* the IND entries from the array loaded by the PT.
*/
/* Look for supported page sizes */
setup_page_sizes();
/*
* If we want to use HW tablewalk, enable it by patching the TLB miss
* handlers to branch to the one dedicated to it.
*/
extlb_level_exc = EX_TLB_SIZE;
switch (book3e_htw_mode) {
case PPC_HTW_E6500:
patch_exception(0x1c0, exc_data_tlb_miss_e6500_book3e);
patch_exception(0x1e0, exc_instruction_tlb_miss_e6500_book3e);
break;
}
pr_info("MMU: Book3E HW tablewalk %s\n",
book3e_htw_mode != PPC_HTW_NONE ? "enabled" : "not supported");
/* Set the global containing the top of the linear mapping
* for use by the TLB miss code
*/
linear_map_top = memblock_end_of_DRAM();
ioremap_bot = IOREMAP_BASE;
}
static void __init early_mmu_set_memory_limit(void)
{
/*
* Limit memory so we dont have linear faults.
* Unlike memblock_set_current_limit, which limits
* memory available during early boot, this permanently
* reduces the memory available to Linux. We need to
* do this because highmem is not supported on 64-bit.
*/
memblock_enforce_memory_limit(linear_map_top);
memblock_set_current_limit(linear_map_top);
}
/* boot cpu only */
void __init early_init_mmu(void)
{
early_init_mmu_global();
early_init_this_mmu();
early_mmu_set_memory_limit();
}
void early_init_mmu_secondary(void)
{
early_init_this_mmu();
}
void setup_initial_memory_limit(phys_addr_t first_memblock_base,
phys_addr_t first_memblock_size)
{
/*
* On FSL Embedded 64-bit, usually all RAM is bolted, but with
* unusual memory sizes it's possible for some RAM to not be mapped
* (such RAM is not used at all by Linux, since we don't support
* highmem on 64-bit). We limit ppc64_rma_size to what would be
* mappable if this memblock is the only one. Additional memblocks
* can only increase, not decrease, the amount that ends up getting
* mapped. We still limit max to 1G even if we'll eventually map
* more. This is due to what the early init code is set up to do.
*
* We crop it to the size of the first MEMBLOCK to
* avoid going over total available memory just in case...
*/
unsigned long linear_sz;
unsigned int num_cams;
/* use a quarter of the TLBCAM for bolted linear map */
num_cams = (mfspr(SPRN_TLB1CFG) & TLBnCFG_N_ENTRY) / 4;
linear_sz = map_mem_in_cams(first_memblock_size, num_cams, true, true);
ppc64_rma_size = min_t(u64, linear_sz, 0x40000000);
/* Finally limit subsequent allocations */
memblock_set_current_limit(first_memblock_base + ppc64_rma_size);
}
@@ -0,0 +1,447 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* This file contains low-level functions for performing various
* types of TLB invalidations on various processors with no hash
* table.
*
* This file implements the following functions for all no-hash
* processors. Some aren't implemented for some variants. Some
* are inline in tlbflush.h
*
* - tlbil_va
* - tlbil_pid
* - tlbil_all
* - tlbivax_bcast
*
* Code mostly moved over from misc_32.S
*
* Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
*
* Partially rewritten by Cort Dougan (cort@cs.nmt.edu)
* Paul Mackerras, Kumar Gala and Benjamin Herrenschmidt.
*/
#include <asm/reg.h>
#include <asm/page.h>
#include <asm/cputable.h>
#include <asm/mmu.h>
#include <asm/ppc_asm.h>
#include <asm/asm-offsets.h>
#include <asm/processor.h>
#include <asm/bug.h>
#include <asm/asm-compat.h>
#include <asm/feature-fixups.h>
#if defined(CONFIG_PPC_8xx)
/*
* Nothing to do for 8xx, everything is inline
*/
#elif defined(CONFIG_44x) /* Includes 47x */
/*
* 440 implementation uses tlbsx/we for tlbil_va and a full sweep
* of the TLB for everything else.
*/
_GLOBAL(__tlbil_va)
mfspr r5,SPRN_MMUCR
mfmsr r10
/*
* We write 16 bits of STID since 47x supports that much, we
* will never be passed out of bounds values on 440 (hopefully)
*/
rlwimi r5,r4,0,16,31
/* We have to run the search with interrupts disabled, otherwise
* an interrupt which causes a TLB miss can clobber the MMUCR
* between the mtspr and the tlbsx.
*
* Critical and Machine Check interrupts take care of saving
* and restoring MMUCR, so only normal interrupts have to be
* taken care of.
*/
wrteei 0
mtspr SPRN_MMUCR,r5
tlbsx. r6,0,r3
bne 10f
sync
#ifndef CONFIG_PPC_47x
/* On 440 There are only 64 TLB entries, so r3 < 64, which means bit
* 22, is clear. Since 22 is the V bit in the TLB_PAGEID, loading this
* value will invalidate the TLB entry.
*/
tlbwe r6,r6,PPC44x_TLB_PAGEID
#else
oris r7,r6,0x8000 /* specify way explicitly */
clrrwi r4,r3,12 /* get an EPN for the hashing with V = 0 */
ori r4,r4,PPC47x_TLBE_SIZE
tlbwe r4,r7,0 /* write it */
#endif /* !CONFIG_PPC_47x */
isync
10: wrtee r10
blr
_GLOBAL(_tlbil_all)
_GLOBAL(_tlbil_pid)
#ifndef CONFIG_PPC_47x
li r3,0
sync
/* Load high watermark */
lis r4,tlb_44x_hwater@ha
lwz r5,tlb_44x_hwater@l(r4)
1: tlbwe r3,r3,PPC44x_TLB_PAGEID
addi r3,r3,1
cmpw 0,r3,r5
ble 1b
isync
blr
#else
/* 476 variant. There's not simple way to do this, hopefully we'll
* try to limit the amount of such full invalidates
*/
mfmsr r11 /* Interrupts off */
wrteei 0
li r3,-1 /* Current set */
lis r10,tlb_47x_boltmap@h
ori r10,r10,tlb_47x_boltmap@l
lis r7,0x8000 /* Specify way explicitly */
b 9f /* For each set */
1: li r9,4 /* Number of ways */
li r4,0 /* Current way */
li r6,0 /* Default entry value 0 */
andi. r0,r8,1 /* Check if way 0 is bolted */
mtctr r9 /* Load way counter */
bne- 3f /* Bolted, skip loading it */
2: /* For each way */
or r5,r3,r4 /* Make way|index for tlbre */
rlwimi r5,r5,16,8,15 /* Copy index into position */
tlbre r6,r5,0 /* Read entry */
3: addis r4,r4,0x2000 /* Next way */
andi. r0,r6,PPC47x_TLB0_VALID /* Valid entry ? */
beq 4f /* Nope, skip it */
rlwimi r7,r5,0,1,2 /* Insert way number */
rlwinm r6,r6,0,21,19 /* Clear V */
tlbwe r6,r7,0 /* Write it */
4: bdnz 2b /* Loop for each way */
srwi r8,r8,1 /* Next boltmap bit */
9: cmpwi cr1,r3,255 /* Last set done ? */
addi r3,r3,1 /* Next set */
beq cr1,1f /* End of loop */
andi. r0,r3,0x1f /* Need to load a new boltmap word ? */
bne 1b /* No, loop */
lwz r8,0(r10) /* Load boltmap entry */
addi r10,r10,4 /* Next word */
b 1b /* Then loop */
1: isync /* Sync shadows */
wrtee r11
blr
#endif /* !CONFIG_PPC_47x */
#ifdef CONFIG_PPC_47x
/*
* _tlbivax_bcast is only on 47x. We don't bother doing a runtime
* check though, it will blow up soon enough if we mistakenly try
* to use it on a 440.
*/
_GLOBAL(_tlbivax_bcast)
mfspr r5,SPRN_MMUCR
mfmsr r10
rlwimi r5,r4,0,16,31
wrteei 0
mtspr SPRN_MMUCR,r5
isync
PPC_TLBIVAX(0, R3)
isync
mbar
tlbsync
BEGIN_FTR_SECTION
b 1f
END_FTR_SECTION_IFSET(CPU_FTR_476_DD2)
sync
wrtee r10
blr
/*
* DD2 HW could hang if in instruction fetch happens before msync completes.
* Touch enough instruction cache lines to ensure cache hits
*/
1: mflr r9
bcl 20,31,$+4
2: mflr r6
li r7,32
PPC_ICBT(0,R6,R7) /* touch next cache line */
add r6,r6,r7
PPC_ICBT(0,R6,R7) /* touch next cache line */
add r6,r6,r7
PPC_ICBT(0,R6,R7) /* touch next cache line */
sync
nop
nop
nop
nop
nop
nop
nop
nop
mtlr r9
wrtee r10
blr
#endif /* CONFIG_PPC_47x */
#elif defined(CONFIG_PPC_85xx)
/*
* FSL BookE implementations.
*
* Since feature sections are using _SECTION_ELSE we need
* to have the larger code path before the _SECTION_ELSE
*/
/*
* Flush MMU TLB on the local processor
*/
_GLOBAL(_tlbil_all)
BEGIN_MMU_FTR_SECTION
li r3,(MMUCSR0_TLBFI)@l
mtspr SPRN_MMUCSR0, r3
1:
mfspr r3,SPRN_MMUCSR0
andi. r3,r3,MMUCSR0_TLBFI@l
bne 1b
MMU_FTR_SECTION_ELSE
PPC_TLBILX_ALL(0,R0)
ALT_MMU_FTR_SECTION_END_IFCLR(MMU_FTR_USE_TLBILX)
msync
isync
blr
_GLOBAL(_tlbil_pid)
BEGIN_MMU_FTR_SECTION
slwi r3,r3,16
mfmsr r10
wrteei 0
mfspr r4,SPRN_MAS6 /* save MAS6 */
mtspr SPRN_MAS6,r3
PPC_TLBILX_PID(0,R0)
mtspr SPRN_MAS6,r4 /* restore MAS6 */
wrtee r10
MMU_FTR_SECTION_ELSE
li r3,(MMUCSR0_TLBFI)@l
mtspr SPRN_MMUCSR0, r3
1:
mfspr r3,SPRN_MMUCSR0
andi. r3,r3,MMUCSR0_TLBFI@l
bne 1b
ALT_MMU_FTR_SECTION_END_IFSET(MMU_FTR_USE_TLBILX)
msync
isync
blr
/*
* Flush MMU TLB for a particular address, but only on the local processor
* (no broadcast)
*/
_GLOBAL(__tlbil_va)
mfmsr r10
wrteei 0
slwi r4,r4,16
ori r4,r4,(MAS6_ISIZE(BOOK3E_PAGESZ_4K))@l
mtspr SPRN_MAS6,r4 /* assume AS=0 for now */
BEGIN_MMU_FTR_SECTION
tlbsx 0,r3
mfspr r4,SPRN_MAS1 /* check valid */
andis. r3,r4,MAS1_VALID@h
beq 1f
rlwinm r4,r4,0,1,31
mtspr SPRN_MAS1,r4
tlbwe
MMU_FTR_SECTION_ELSE
PPC_TLBILX_VA(0,R3)
ALT_MMU_FTR_SECTION_END_IFCLR(MMU_FTR_USE_TLBILX)
msync
isync
1: wrtee r10
blr
#elif defined(CONFIG_PPC_BOOK3E_64)
/*
* New Book3E (>= 2.06) implementation
*
* Note: We may be able to get away without the interrupt masking stuff
* if we save/restore MAS6 on exceptions that might modify it
*/
_GLOBAL(_tlbil_pid)
slwi r4,r3,MAS6_SPID_SHIFT
mfmsr r10
wrteei 0
mtspr SPRN_MAS6,r4
PPC_TLBILX_PID(0,R0)
wrtee r10
msync
isync
blr
_GLOBAL(_tlbil_pid_noind)
slwi r4,r3,MAS6_SPID_SHIFT
mfmsr r10
ori r4,r4,MAS6_SIND
wrteei 0
mtspr SPRN_MAS6,r4
PPC_TLBILX_PID(0,R0)
wrtee r10
msync
isync
blr
_GLOBAL(_tlbil_all)
PPC_TLBILX_ALL(0,R0)
msync
isync
blr
_GLOBAL(_tlbil_va)
mfmsr r10
wrteei 0
cmpwi cr0,r6,0
slwi r4,r4,MAS6_SPID_SHIFT
rlwimi r4,r5,MAS6_ISIZE_SHIFT,MAS6_ISIZE_MASK
beq 1f
rlwimi r4,r6,MAS6_SIND_SHIFT,MAS6_SIND
1: mtspr SPRN_MAS6,r4 /* assume AS=0 for now */
PPC_TLBILX_VA(0,R3)
msync
isync
wrtee r10
blr
_GLOBAL(_tlbivax_bcast)
mfmsr r10
wrteei 0
cmpwi cr0,r6,0
slwi r4,r4,MAS6_SPID_SHIFT
rlwimi r4,r5,MAS6_ISIZE_SHIFT,MAS6_ISIZE_MASK
beq 1f
rlwimi r4,r6,MAS6_SIND_SHIFT,MAS6_SIND
1: mtspr SPRN_MAS6,r4 /* assume AS=0 for now */
PPC_TLBIVAX(0,R3)
mbar
tlbsync
sync
wrtee r10
blr
#else
#error Unsupported processor type !
#endif
#if defined(CONFIG_PPC_E500)
/*
* extern void loadcam_entry(unsigned int index)
*
* Load TLBCAM[index] entry in to the L2 CAM MMU
* Must preserve r7, r8, r9, r10, r11, r12
*/
_GLOBAL(loadcam_entry)
mflr r5
LOAD_REG_ADDR_PIC(r4, TLBCAM)
mtlr r5
mulli r5,r3,TLBCAM_SIZE
add r3,r5,r4
lwz r4,TLBCAM_MAS0(r3)
mtspr SPRN_MAS0,r4
lwz r4,TLBCAM_MAS1(r3)
mtspr SPRN_MAS1,r4
PPC_LL r4,TLBCAM_MAS2(r3)
mtspr SPRN_MAS2,r4
lwz r4,TLBCAM_MAS3(r3)
mtspr SPRN_MAS3,r4
BEGIN_MMU_FTR_SECTION
lwz r4,TLBCAM_MAS7(r3)
mtspr SPRN_MAS7,r4
END_MMU_FTR_SECTION_IFSET(MMU_FTR_BIG_PHYS)
isync
tlbwe
isync
blr
/*
* Load multiple TLB entries at once, using an alternate-space
* trampoline so that we don't have to care about whether the same
* TLB entry maps us before and after.
*
* r3 = first entry to write
* r4 = number of entries to write
* r5 = temporary tlb entry (0 means no switch to AS1)
*/
_GLOBAL(loadcam_multi)
mflr r8
/* Don't switch to AS=1 if already there */
mfmsr r11
andi. r11,r11,MSR_IS
bne 10f
mr. r12, r5
beq 10f
/*
* Set up temporary TLB entry that is the same as what we're
* running from, but in AS=1.
*/
bcl 20,31,$+4
1: mflr r6
tlbsx 0,r8
mfspr r6,SPRN_MAS1
ori r6,r6,MAS1_TS
mtspr SPRN_MAS1,r6
mfspr r6,SPRN_MAS0
rlwimi r6,r5,MAS0_ESEL_SHIFT,MAS0_ESEL_MASK
mr r7,r5
mtspr SPRN_MAS0,r6
isync
tlbwe
isync
/* Switch to AS=1 */
mfmsr r6
ori r6,r6,MSR_IS|MSR_DS
mtmsr r6
isync
10:
mr r9,r3
add r10,r3,r4
2: bl loadcam_entry
addi r9,r9,1
cmpw r9,r10
mr r3,r9
blt 2b
/* Don't return to AS=0 if we were in AS=1 at function start */
andi. r11,r11,MSR_IS
bne 3f
cmpwi r12, 0
beq 3f
/* Return to AS=0 and clear the temporary entry */
mfmsr r6
rlwinm. r6,r6,0,~(MSR_IS|MSR_DS)
mtmsr r6
isync
li r6,0
mtspr SPRN_MAS1,r6
rlwinm r6,r7,MAS0_ESEL_SHIFT,MAS0_ESEL_MASK
oris r6,r6,MAS0_TLBSEL(1)@h
mtspr SPRN_MAS0,r6
isync
tlbwe
isync
3:
mtlr r8
blr
#endif
@@ -0,0 +1,743 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Low level TLB miss handlers for Book3E
*
* Copyright (C) 2008-2009
* Ben. Herrenschmidt (benh@kernel.crashing.org), IBM Corp.
*/
#include <linux/pgtable.h>
#include <asm/processor.h>
#include <asm/reg.h>
#include <asm/page.h>
#include <asm/mmu.h>
#include <asm/ppc_asm.h>
#include <asm/asm-offsets.h>
#include <asm/cputable.h>
#include <asm/exception-64e.h>
#include <asm/ppc-opcode.h>
#include <asm/kvm_asm.h>
#include <asm/kvm_booke_hv_asm.h>
#include <asm/feature-fixups.h>
#define VPTE_PMD_SHIFT (PTE_INDEX_SIZE)
#define VPTE_PUD_SHIFT (VPTE_PMD_SHIFT + PMD_INDEX_SIZE)
#define VPTE_PGD_SHIFT (VPTE_PUD_SHIFT + PUD_INDEX_SIZE)
#define VPTE_INDEX_SIZE (VPTE_PGD_SHIFT + PGD_INDEX_SIZE)
/**********************************************************************
* *
* TLB miss handling for Book3E with a bolted linear mapping *
* No virtual page table, no nested TLB misses *
* *
**********************************************************************/
/*
* Note that, unlike non-bolted handlers, TLB_EXFRAME is not
* modified by the TLB miss handlers themselves, since the TLB miss
* handler code will not itself cause a recursive TLB miss.
*
* TLB_EXFRAME will be modified when crit/mc/debug exceptions are
* entered/exited.
*/
.macro tlb_prolog_bolted intnum addr
mtspr SPRN_SPRG_GEN_SCRATCH,r12
mfspr r12,SPRN_SPRG_TLB_EXFRAME
std r13,EX_TLB_R13(r12)
std r10,EX_TLB_R10(r12)
mfspr r13,SPRN_SPRG_PACA
mfcr r10
std r11,EX_TLB_R11(r12)
#ifdef CONFIG_KVM_BOOKE_HV
BEGIN_FTR_SECTION
mfspr r11, SPRN_SRR1
END_FTR_SECTION_IFSET(CPU_FTR_EMB_HV)
#endif
DO_KVM \intnum, SPRN_SRR1
std r16,EX_TLB_R16(r12)
mfspr r16,\addr /* get faulting address */
std r14,EX_TLB_R14(r12)
ld r14,PACAPGD(r13)
std r15,EX_TLB_R15(r12)
std r10,EX_TLB_CR(r12)
START_BTB_FLUSH_SECTION
mfspr r11, SPRN_SRR1
andi. r10,r11,MSR_PR
beq 1f
BTB_FLUSH(r10)
1:
END_BTB_FLUSH_SECTION
std r7,EX_TLB_R7(r12)
.endm
.macro tlb_epilog_bolted
ld r14,EX_TLB_CR(r12)
ld r7,EX_TLB_R7(r12)
ld r10,EX_TLB_R10(r12)
ld r11,EX_TLB_R11(r12)
ld r13,EX_TLB_R13(r12)
mtcr r14
ld r14,EX_TLB_R14(r12)
ld r15,EX_TLB_R15(r12)
ld r16,EX_TLB_R16(r12)
mfspr r12,SPRN_SPRG_GEN_SCRATCH
.endm
/* Data TLB miss */
START_EXCEPTION(data_tlb_miss_bolted)
tlb_prolog_bolted BOOKE_INTERRUPT_DTLB_MISS SPRN_DEAR
/* We need _PAGE_PRESENT and _PAGE_ACCESSED set */
/* We do the user/kernel test for the PID here along with the RW test
*/
/* We pre-test some combination of permissions to avoid double
* faults:
*
* We move the ESR:ST bit into the position of _PAGE_BAP_SW in the PTE
* ESR_ST is 0x00800000
* _PAGE_BAP_SW is 0x00000010
* So the shift is >> 19. This tests for supervisor writeability.
* If the page happens to be supervisor writeable and not user
* writeable, we will take a new fault later, but that should be
* a rare enough case.
*
* We also move ESR_ST in _PAGE_DIRTY position
* _PAGE_DIRTY is 0x00001000 so the shift is >> 11
*
* MAS1 is preset for all we need except for TID that needs to
* be cleared for kernel translations
*/
mfspr r11,SPRN_ESR
srdi r15,r16,60 /* get region */
rldicl. r10,r16,64-PGTABLE_EADDR_SIZE,PGTABLE_EADDR_SIZE+4
bne- dtlb_miss_fault_bolted /* Bail if fault addr is invalid */
rlwinm r10,r11,32-19,27,27
rlwimi r10,r11,32-16,19,19
cmpwi r15,0 /* user vs kernel check */
ori r10,r10,_PAGE_PRESENT
oris r11,r10,_PAGE_ACCESSED@h
bne tlb_miss_kernel_bolted
tlb_miss_user_bolted:
#ifdef CONFIG_PPC_KUAP
mfspr r10,SPRN_MAS1
rlwinm. r10,r10,0,0x3fff0000
beq- tlb_miss_fault_bolted /* KUAP fault */
#endif
tlb_miss_common_bolted:
/*
* This is the guts of the TLB miss handler for bolted-linear.
* We are entered with:
*
* r16 = faulting address
* r15 = crap (free to use)
* r14 = page table base
* r13 = PACA
* r11 = PTE permission mask
* r10 = crap (free to use)
*/
rldicl r15,r16,64-PGDIR_SHIFT+3,64-PGD_INDEX_SIZE-3
cmpldi cr0,r14,0
clrrdi r15,r15,3
beq tlb_miss_fault_bolted /* No PGDIR, bail */
ldx r14,r14,r15 /* grab pgd entry */
rldicl r15,r16,64-PUD_SHIFT+3,64-PUD_INDEX_SIZE-3
clrrdi r15,r15,3
cmpdi cr0,r14,0
bge tlb_miss_fault_bolted /* Bad pgd entry or hugepage; bail */
ldx r14,r14,r15 /* grab pud entry */
rldicl r15,r16,64-PMD_SHIFT+3,64-PMD_INDEX_SIZE-3
clrrdi r15,r15,3
cmpdi cr0,r14,0
bge tlb_miss_fault_bolted
ldx r14,r14,r15 /* Grab pmd entry */
rldicl r15,r16,64-PAGE_SHIFT+3,64-PTE_INDEX_SIZE-3
clrrdi r15,r15,3
cmpdi cr0,r14,0
bge tlb_miss_fault_bolted
ldx r14,r14,r15 /* Grab PTE, normal (!huge) page */
/* Check if required permissions are met */
andc. r15,r11,r14
rldicr r15,r14,64-(PTE_RPN_SHIFT-PAGE_SHIFT),63-PAGE_SHIFT
bne- tlb_miss_fault_bolted
/* Now we build the MAS:
*
* MAS 0 : Fully setup with defaults in MAS4 and TLBnCFG
* MAS 1 : Almost fully setup
* - PID already updated by caller if necessary
* - TSIZE need change if !base page size, not
* yet implemented for now
* MAS 2 : Defaults not useful, need to be redone
* MAS 3+7 : Needs to be done
*/
clrrdi r11,r16,12 /* Clear low crap in EA */
clrldi r15,r15,12 /* Clear crap at the top */
rlwimi r11,r14,32-19,27,31 /* Insert WIMGE */
rlwimi r15,r14,32-8,22,25 /* Move in U bits */
mtspr SPRN_MAS2,r11
andi. r11,r14,_PAGE_DIRTY
rlwimi r15,r14,32-2,26,31 /* Move in BAP bits */
/* Mask out SW and UW if !DIRTY (XXX optimize this !) */
bne 1f
li r11,MAS3_SW|MAS3_UW
andc r15,r15,r11
1:
mtspr SPRN_MAS7_MAS3,r15
tlbwe
tlb_miss_done_bolted:
tlb_epilog_bolted
rfi
itlb_miss_kernel_bolted:
li r11,_PAGE_PRESENT|_PAGE_BAP_SX /* Base perm */
oris r11,r11,_PAGE_ACCESSED@h
tlb_miss_kernel_bolted:
mfspr r10,SPRN_MAS1
ld r14,PACA_KERNELPGD(r13)
srdi r15,r16,44 /* get kernel region */
andi. r15,r15,1 /* Check for vmalloc region */
rlwinm r10,r10,0,16,1 /* Clear TID */
mtspr SPRN_MAS1,r10
bne+ tlb_miss_common_bolted
tlb_miss_fault_bolted:
/* We need to check if it was an instruction miss */
andi. r10,r11,_PAGE_BAP_UX|_PAGE_BAP_SX
bne itlb_miss_fault_bolted
dtlb_miss_fault_bolted:
tlb_epilog_bolted
b exc_data_storage_book3e
itlb_miss_fault_bolted:
tlb_epilog_bolted
b exc_instruction_storage_book3e
/* Instruction TLB miss */
START_EXCEPTION(instruction_tlb_miss_bolted)
tlb_prolog_bolted BOOKE_INTERRUPT_ITLB_MISS SPRN_SRR0
rldicl. r10,r16,64-PGTABLE_EADDR_SIZE,PGTABLE_EADDR_SIZE+4
srdi r15,r16,60 /* get region */
bne- itlb_miss_fault_bolted
li r11,_PAGE_PRESENT|_PAGE_BAP_UX /* Base perm */
/* We do the user/kernel test for the PID here along with the RW test
*/
cmpldi cr0,r15,0 /* Check for user region */
oris r11,r11,_PAGE_ACCESSED@h
beq tlb_miss_user_bolted
b itlb_miss_kernel_bolted
/*
* TLB miss handling for e6500 and derivatives, using hardware tablewalk.
*
* Linear mapping is bolted: no virtual page table or nested TLB misses
* Indirect entries in TLB1, hardware loads resulting direct entries
* into TLB0
* No HES or NV hint on TLB1, so we need to do software round-robin
* No tlbsrx. so we need a spinlock, and we have to deal
* with MAS-damage caused by tlbsx
* 4K pages only
*/
START_EXCEPTION(instruction_tlb_miss_e6500)
tlb_prolog_bolted BOOKE_INTERRUPT_ITLB_MISS SPRN_SRR0
ld r11,PACA_TCD_PTR(r13)
srdi. r15,r16,60 /* get region */
ori r16,r16,1
bne tlb_miss_kernel_e6500 /* user/kernel test */
b tlb_miss_common_e6500
START_EXCEPTION(data_tlb_miss_e6500)
tlb_prolog_bolted BOOKE_INTERRUPT_DTLB_MISS SPRN_DEAR
ld r11,PACA_TCD_PTR(r13)
srdi. r15,r16,60 /* get region */
rldicr r16,r16,0,62
bne tlb_miss_kernel_e6500 /* user vs kernel check */
/*
* This is the guts of the TLB miss handler for e6500 and derivatives.
* We are entered with:
*
* r16 = page of faulting address (low bit 0 if data, 1 if instruction)
* r15 = crap (free to use)
* r14 = page table base
* r13 = PACA
* r11 = tlb_per_core ptr
* r10 = crap (free to use)
* r7 = esel_next
*/
tlb_miss_common_e6500:
crmove cr2*4+2,cr0*4+2 /* cr2.eq != 0 if kernel address */
BEGIN_FTR_SECTION /* CPU_FTR_SMT */
/*
* Search if we already have an indirect entry for that virtual
* address, and if we do, bail out.
*
* MAS6:IND should be already set based on MAS4
*/
lhz r10,PACAPACAINDEX(r13)
addi r10,r10,1
crclr cr1*4+eq /* set cr1.eq = 0 for non-recursive */
1: lbarx r15,0,r11
cmpdi r15,0
bne 2f
stbcx. r10,0,r11
bne 1b
3:
.subsection 1
2: cmpd cr1,r15,r10 /* recursive lock due to mcheck/crit/etc? */
beq cr1,3b /* unlock will happen if cr1.eq = 0 */
10: lbz r15,0(r11)
cmpdi r15,0
bne 10b
b 1b
.previous
END_FTR_SECTION_IFSET(CPU_FTR_SMT)
lbz r7,TCD_ESEL_NEXT(r11)
BEGIN_FTR_SECTION /* CPU_FTR_SMT */
/*
* Erratum A-008139 says that we can't use tlbwe to change
* an indirect entry in any way (including replacing or
* invalidating) if the other thread could be in the process
* of a lookup. The workaround is to invalidate the entry
* with tlbilx before overwriting.
*/
rlwinm r10,r7,16,0xff0000
oris r10,r10,MAS0_TLBSEL(1)@h
mtspr SPRN_MAS0,r10
isync
tlbre
mfspr r15,SPRN_MAS1
andis. r15,r15,MAS1_VALID@h
beq 5f
BEGIN_FTR_SECTION_NESTED(532)
mfspr r10,SPRN_MAS8
rlwinm r10,r10,0,0x80000fff /* tgs,tlpid -> sgs,slpid */
mtspr SPRN_MAS5,r10
END_FTR_SECTION_NESTED(CPU_FTR_EMB_HV,CPU_FTR_EMB_HV,532)
mfspr r10,SPRN_MAS1
rlwinm r15,r10,0,0x3fff0000 /* tid -> spid */
rlwimi r15,r10,20,0x00000003 /* ind,ts -> sind,sas */
mfspr r10,SPRN_MAS6
mtspr SPRN_MAS6,r15
mfspr r15,SPRN_MAS2
isync
PPC_TLBILX_VA(0,R15)
isync
mtspr SPRN_MAS6,r10
5:
BEGIN_FTR_SECTION_NESTED(532)
li r10,0
mtspr SPRN_MAS8,r10
mtspr SPRN_MAS5,r10
END_FTR_SECTION_NESTED(CPU_FTR_EMB_HV,CPU_FTR_EMB_HV,532)
tlbsx 0,r16
mfspr r10,SPRN_MAS1
andis. r15,r10,MAS1_VALID@h
bne tlb_miss_done_e6500
FTR_SECTION_ELSE
mfspr r10,SPRN_MAS1
ALT_FTR_SECTION_END_IFSET(CPU_FTR_SMT)
oris r10,r10,MAS1_VALID@h
beq cr2,4f
rlwinm r10,r10,0,16,1 /* Clear TID */
4: mtspr SPRN_MAS1,r10
/* Now, we need to walk the page tables. First check if we are in
* range.
*/
rldicl. r10,r16,64-PGTABLE_EADDR_SIZE,PGTABLE_EADDR_SIZE+4
bne- tlb_miss_fault_e6500
rldicl r15,r16,64-PGDIR_SHIFT+3,64-PGD_INDEX_SIZE-3
cmpldi cr0,r14,0
clrrdi r15,r15,3
beq- tlb_miss_fault_e6500 /* No PGDIR, bail */
ldx r14,r14,r15 /* grab pgd entry */
rldicl r15,r16,64-PUD_SHIFT+3,64-PUD_INDEX_SIZE-3
clrrdi r15,r15,3
cmpdi cr0,r14,0
bge tlb_miss_huge_e6500 /* Bad pgd entry or hugepage; bail */
ldx r14,r14,r15 /* grab pud entry */
rldicl r15,r16,64-PMD_SHIFT+3,64-PMD_INDEX_SIZE-3
clrrdi r15,r15,3
cmpdi cr0,r14,0
bge tlb_miss_huge_e6500
ldx r14,r14,r15 /* Grab pmd entry */
mfspr r10,SPRN_MAS0
cmpdi cr0,r14,0
bge tlb_miss_huge_e6500
/* Now we build the MAS for a 2M indirect page:
*
* MAS 0 : ESEL needs to be filled by software round-robin
* MAS 1 : Fully set up
* - PID already updated by caller if necessary
* - TSIZE for now is base ind page size always
* - TID already cleared if necessary
* MAS 2 : Default not 2M-aligned, need to be redone
* MAS 3+7 : Needs to be done
*/
ori r14,r14,(BOOK3E_PAGESZ_4K << MAS3_SPSIZE_SHIFT)
mtspr SPRN_MAS7_MAS3,r14
clrrdi r15,r16,21 /* make EA 2M-aligned */
mtspr SPRN_MAS2,r15
tlb_miss_huge_done_e6500:
lbz r16,TCD_ESEL_MAX(r11)
lbz r14,TCD_ESEL_FIRST(r11)
rlwimi r10,r7,16,0x00ff0000 /* insert esel_next into MAS0 */
addi r7,r7,1 /* increment esel_next */
mtspr SPRN_MAS0,r10
cmpw r7,r16
iseleq r7,r14,r7 /* if next == last use first */
stb r7,TCD_ESEL_NEXT(r11)
tlbwe
tlb_miss_done_e6500:
.macro tlb_unlock_e6500
BEGIN_FTR_SECTION
beq cr1,1f /* no unlock if lock was recursively grabbed */
li r15,0
isync
stb r15,0(r11)
1:
END_FTR_SECTION_IFSET(CPU_FTR_SMT)
.endm
tlb_unlock_e6500
tlb_epilog_bolted
rfi
tlb_miss_huge_e6500:
beq tlb_miss_fault_e6500
rlwinm r15,r14,32-_PAGE_PSIZE_SHIFT,0x1e
/*
* Now we build the MAS for a huge page.
*
* MAS 0 : ESEL needs to be filled by software round-robin
* - can be handled by indirect code
* MAS 1 : Need to clear IND and set TSIZE
* MAS 2,3+7: Needs to be redone similar to non-tablewalk handler
*/
mfspr r10,SPRN_MAS1
rlwinm r10,r10,0,~MAS1_IND
rlwimi r10,r15,MAS1_TSIZE_SHIFT,MAS1_TSIZE_MASK
mtspr SPRN_MAS1,r10
li r10,-0x400
sld r15,r10,r15 /* Generate mask based on size */
and r10,r16,r15
rldicr r15,r14,64-(PTE_RPN_SHIFT-PAGE_SHIFT),63-PAGE_SHIFT
rlwimi r10,r14,32-19,27,31 /* Insert WIMGE */
clrldi r15,r15,PAGE_SHIFT /* Clear crap at the top */
rlwimi r15,r14,32-8,22,25 /* Move in U bits */
mtspr SPRN_MAS2,r10
andi. r10,r14,_PAGE_DIRTY
rlwimi r15,r14,32-2,26,31 /* Move in BAP bits */
/* Mask out SW and UW if !DIRTY (XXX optimize this !) */
bne 1f
li r10,MAS3_SW|MAS3_UW
andc r15,r15,r10
1:
mtspr SPRN_MAS7_MAS3,r15
mfspr r10,SPRN_MAS0
b tlb_miss_huge_done_e6500
tlb_miss_kernel_e6500:
ld r14,PACA_KERNELPGD(r13)
srdi r15,r16,44 /* get kernel region */
xoris r15,r15,0xc /* Check for vmalloc region */
cmplwi cr1,r15,1
beq+ cr1,tlb_miss_common_e6500
tlb_miss_fault_e6500:
tlb_unlock_e6500
/* We need to check if it was an instruction miss */
andi. r16,r16,1
bne itlb_miss_fault_e6500
dtlb_miss_fault_e6500:
tlb_epilog_bolted
b exc_data_storage_book3e
itlb_miss_fault_e6500:
tlb_epilog_bolted
b exc_instruction_storage_book3e
/*
* This is the guts of the second-level TLB miss handler for direct
* misses. We are entered with:
*
* r16 = virtual page table faulting address
* r15 = region (top 4 bits of address)
* r14 = crap (free to use)
* r13 = PACA
* r12 = TLB exception frame in PACA
* r11 = crap (free to use)
* r10 = crap (free to use)
*
* Note that this should only ever be called as a second level handler
* with the current scheme when using SW load.
* That means we can always get the original fault DEAR at
* EX_TLB_DEAR-EX_TLB_SIZE(r12)
*
* It can be re-entered by the linear mapping miss handler. However, to
* avoid too much complication, it will restart the whole fault at level
* 0 so we don't care too much about clobbers
*
* XXX That code was written back when we couldn't clobber r14. We can now,
* so we could probably optimize things a bit
*/
virt_page_table_tlb_miss:
/* Are we hitting a kernel page table ? */
srdi r15,r16,60
andi. r10,r15,0x8
/* The cool thing now is that r10 contains 0 for user and 8 for kernel,
* and we happen to have the swapper_pg_dir at offset 8 from the user
* pgdir in the PACA :-).
*/
add r11,r10,r13
/* If kernel, we need to clear MAS1 TID */
beq 1f
/* XXX replace the RMW cycles with immediate loads + writes */
mfspr r10,SPRN_MAS1
rlwinm r10,r10,0,16,1 /* Clear TID */
mtspr SPRN_MAS1,r10
#ifdef CONFIG_PPC_KUAP
b 2f
1:
mfspr r10,SPRN_MAS1
rlwinm. r10,r10,0,0x3fff0000
beq- virt_page_table_tlb_miss_fault /* KUAP fault */
2:
#else
1:
#endif
/* Now, we need to walk the page tables. First check if we are in
* range.
*/
rldicl r10,r16,64-(VPTE_INDEX_SIZE+3),VPTE_INDEX_SIZE+3+4
cmpldi r10,0x80
bne- virt_page_table_tlb_miss_fault
/* Get the PGD pointer */
ld r15,PACAPGD(r11)
cmpldi cr0,r15,0
beq- virt_page_table_tlb_miss_fault
/* Get to PGD entry */
rldicl r11,r16,64-VPTE_PGD_SHIFT,64-PGD_INDEX_SIZE-3
clrrdi r10,r11,3
ldx r15,r10,r15
cmpdi cr0,r15,0
bge virt_page_table_tlb_miss_fault
/* Get to PUD entry */
rldicl r11,r16,64-VPTE_PUD_SHIFT,64-PUD_INDEX_SIZE-3
clrrdi r10,r11,3
ldx r15,r10,r15
cmpdi cr0,r15,0
bge virt_page_table_tlb_miss_fault
/* Get to PMD entry */
rldicl r11,r16,64-VPTE_PMD_SHIFT,64-PMD_INDEX_SIZE-3
clrrdi r10,r11,3
ldx r15,r10,r15
cmpdi cr0,r15,0
bge virt_page_table_tlb_miss_fault
/* Ok, we're all right, we can now create a kernel translation for
* a 4K or 64K page from r16 -> r15.
*/
/* Now we build the MAS:
*
* MAS 0 : Fully setup with defaults in MAS4 and TLBnCFG
* MAS 1 : Almost fully setup
* - PID already updated by caller if necessary
* - TSIZE for now is base page size always
* MAS 2 : Use defaults
* MAS 3+7 : Needs to be done
*
* So we only do MAS 2 and 3 for now...
*/
clrldi r11,r15,4 /* remove region ID from RPN */
ori r10,r11,1 /* Or-in SR */
srdi r16,r10,32
mtspr SPRN_MAS3,r10
mtspr SPRN_MAS7,r16
tlbwe
/* Return to caller, normal case */
TLB_MISS_EPILOG_SUCCESS
rfi
virt_page_table_tlb_miss_fault:
/* If we fault here, things are a little bit tricky. We need to call
* either data or instruction store fault, and we need to retrieve
* the original fault address and ESR (for data).
*
* The thing is, we know that in normal circumstances, this is
* always called as a second level tlb miss for SW load or as a first
* level TLB miss for HW load, so we should be able to peek at the
* relevant information in the first exception frame in the PACA.
*
* However, we do need to double check that, because we may just hit
* a stray kernel pointer or a userland attack trying to hit those
* areas. If that is the case, we do a data fault. (We can't get here
* from an instruction tlb miss anyway).
*
* Note also that when going to a fault, we must unwind the previous
* level as well. Since we are doing that, we don't need to clear or
* restore the TLB reservation neither.
*/
subf r10,r13,r12
cmpldi cr0,r10,PACA_EXTLB+EX_TLB_SIZE
bne- virt_page_table_tlb_miss_whacko_fault
/* We dig the original DEAR and ESR from slot 0 */
ld r15,EX_TLB_DEAR+PACA_EXTLB(r13)
ld r16,EX_TLB_ESR+PACA_EXTLB(r13)
/* We check for the "special" ESR value for instruction faults */
cmpdi cr0,r16,-1
beq 1f
mtspr SPRN_DEAR,r15
mtspr SPRN_ESR,r16
TLB_MISS_EPILOG_ERROR
b exc_data_storage_book3e
1: TLB_MISS_EPILOG_ERROR
b exc_instruction_storage_book3e
virt_page_table_tlb_miss_whacko_fault:
/* The linear fault will restart everything so ESR and DEAR will
* not have been clobbered, let's just fault with what we have
*/
TLB_MISS_EPILOG_ERROR
b exc_data_storage_book3e
/*
* This is the guts of "any" level TLB miss handler for kernel linear
* mapping misses. We are entered with:
*
*
* r16 = faulting address
* r15 = crap (free to use)
* r14 = ESR (data) or -1 (instruction)
* r13 = PACA
* r12 = TLB exception frame in PACA
* r11 = crap (free to use)
* r10 = crap (free to use)
*
* In addition we know that we will not re-enter, so in theory, we could
* use a simpler epilog not restoring SRR0/1 etc.. but we'll do that later.
*
* We also need to be careful about MAS registers here & TLB reservation,
* as we know we'll have clobbered them if we interrupt the main TLB miss
* handlers in which case we probably want to do a full restart at level
* 0 rather than saving / restoring the MAS.
*
* Note: If we care about performance of that core, we can easily shuffle
* a few things around
*/
tlb_load_linear:
/* For now, we assume the linear mapping is contiguous and stops at
* linear_map_top. We also assume the size is a multiple of 1G, thus
* we only use 1G pages for now. That might have to be changed in a
* final implementation, especially when dealing with hypervisors
*/
__LOAD_PACA_TOC(r11)
LOAD_REG_ADDR_ALTTOC(r11, r11, linear_map_top)
ld r10,0(r11)
tovirt(10,10)
cmpld cr0,r16,r10
bge tlb_load_linear_fault
/* MAS1 need whole new setup. */
li r15,(BOOK3E_PAGESZ_1GB<<MAS1_TSIZE_SHIFT)
oris r15,r15,MAS1_VALID@h /* MAS1 needs V and TSIZE */
mtspr SPRN_MAS1,r15
/* Already somebody there ? */
PPC_TLBSRX_DOT(0,R16)
beq tlb_load_linear_done
/* Now we build the remaining MAS. MAS0 and 2 should be fine
* with their defaults, which leaves us with MAS 3 and 7. The
* mapping is linear, so we just take the address, clear the
* region bits, and or in the permission bits which are currently
* hard wired
*/
clrrdi r10,r16,30 /* 1G page index */
clrldi r10,r10,4 /* clear region bits */
ori r10,r10,MAS3_SR|MAS3_SW|MAS3_SX
srdi r16,r10,32
mtspr SPRN_MAS3,r10
mtspr SPRN_MAS7,r16
tlbwe
tlb_load_linear_done:
/* We use the "error" epilog for success as we do want to
* restore to the initial faulting context, whatever it was.
* We do that because we can't resume a fault within a TLB
* miss handler, due to MAS and TLB reservation being clobbered.
*/
TLB_MISS_EPILOG_ERROR
rfi
tlb_load_linear_fault:
/* We keep the DEAR and ESR around, this shouldn't have happened */
cmpdi cr0,r14,-1
beq 1f
TLB_MISS_EPILOG_ERROR_SPECIAL
b exc_data_storage_book3e
1: TLB_MISS_EPILOG_ERROR_SPECIAL
b exc_instruction_storage_book3e