cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tlb-r4k.c (13824B)


      1/*
      2 * This file is subject to the terms and conditions of the GNU General Public
      3 * License.  See the file "COPYING" in the main directory of this archive
      4 * for more details.
      5 *
      6 * Copyright (C) 1996 David S. Miller (davem@davemloft.net)
      7 * Copyright (C) 1997, 1998, 1999, 2000 Ralf Baechle ralf@gnu.org
      8 * Carsten Langgaard, carstenl@mips.com
      9 * Copyright (C) 2002 MIPS Technologies, Inc.  All rights reserved.
     10 */
     11#include <linux/cpu_pm.h>
     12#include <linux/init.h>
     13#include <linux/sched.h>
     14#include <linux/smp.h>
     15#include <linux/mm.h>
     16#include <linux/hugetlb.h>
     17#include <linux/export.h>
     18
     19#include <asm/cpu.h>
     20#include <asm/cpu-type.h>
     21#include <asm/bootinfo.h>
     22#include <asm/hazards.h>
     23#include <asm/mmu_context.h>
     24#include <asm/tlb.h>
     25#include <asm/tlbmisc.h>
     26
     27extern void build_tlb_refill_handler(void);
     28
     29/*
     30 * LOONGSON-2 has a 4 entry itlb which is a subset of jtlb, LOONGSON-3 has
     31 * a 4 entry itlb and a 4 entry dtlb which are subsets of jtlb. Unfortunately,
     32 * itlb/dtlb are not totally transparent to software.
     33 */
     34static inline void flush_micro_tlb(void)
     35{
     36	switch (current_cpu_type()) {
     37	case CPU_LOONGSON2EF:
     38		write_c0_diag(LOONGSON_DIAG_ITLB);
     39		break;
     40	case CPU_LOONGSON64:
     41		write_c0_diag(LOONGSON_DIAG_ITLB | LOONGSON_DIAG_DTLB);
     42		break;
     43	default:
     44		break;
     45	}
     46}
     47
     48static inline void flush_micro_tlb_vm(struct vm_area_struct *vma)
     49{
     50	if (vma->vm_flags & VM_EXEC)
     51		flush_micro_tlb();
     52}
     53
     54void local_flush_tlb_all(void)
     55{
     56	unsigned long flags;
     57	unsigned long old_ctx;
     58	int entry, ftlbhighset;
     59
     60	local_irq_save(flags);
     61	/* Save old context and create impossible VPN2 value */
     62	old_ctx = read_c0_entryhi();
     63	htw_stop();
     64	write_c0_entrylo0(0);
     65	write_c0_entrylo1(0);
     66
     67	entry = num_wired_entries();
     68
     69	/*
     70	 * Blast 'em all away.
     71	 * If there are any wired entries, fall back to iterating
     72	 */
     73	if (cpu_has_tlbinv && !entry) {
     74		if (current_cpu_data.tlbsizevtlb) {
     75			write_c0_index(0);
     76			mtc0_tlbw_hazard();
     77			tlbinvf();  /* invalidate VTLB */
     78		}
     79		ftlbhighset = current_cpu_data.tlbsizevtlb +
     80			current_cpu_data.tlbsizeftlbsets;
     81		for (entry = current_cpu_data.tlbsizevtlb;
     82		     entry < ftlbhighset;
     83		     entry++) {
     84			write_c0_index(entry);
     85			mtc0_tlbw_hazard();
     86			tlbinvf();  /* invalidate one FTLB set */
     87		}
     88	} else {
     89		while (entry < current_cpu_data.tlbsize) {
     90			/* Make sure all entries differ. */
     91			write_c0_entryhi(UNIQUE_ENTRYHI(entry));
     92			write_c0_index(entry);
     93			mtc0_tlbw_hazard();
     94			tlb_write_indexed();
     95			entry++;
     96		}
     97	}
     98	tlbw_use_hazard();
     99	write_c0_entryhi(old_ctx);
    100	htw_start();
    101	flush_micro_tlb();
    102	local_irq_restore(flags);
    103}
    104EXPORT_SYMBOL(local_flush_tlb_all);
    105
    106void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
    107	unsigned long end)
    108{
    109	struct mm_struct *mm = vma->vm_mm;
    110	int cpu = smp_processor_id();
    111
    112	if (cpu_context(cpu, mm) != 0) {
    113		unsigned long size, flags;
    114
    115		local_irq_save(flags);
    116		start = round_down(start, PAGE_SIZE << 1);
    117		end = round_up(end, PAGE_SIZE << 1);
    118		size = (end - start) >> (PAGE_SHIFT + 1);
    119		if (size <= (current_cpu_data.tlbsizeftlbsets ?
    120			     current_cpu_data.tlbsize / 8 :
    121			     current_cpu_data.tlbsize / 2)) {
    122			unsigned long old_entryhi, old_mmid;
    123			int newpid = cpu_asid(cpu, mm);
    124
    125			old_entryhi = read_c0_entryhi();
    126			if (cpu_has_mmid) {
    127				old_mmid = read_c0_memorymapid();
    128				write_c0_memorymapid(newpid);
    129			}
    130
    131			htw_stop();
    132			while (start < end) {
    133				int idx;
    134
    135				if (cpu_has_mmid)
    136					write_c0_entryhi(start);
    137				else
    138					write_c0_entryhi(start | newpid);
    139				start += (PAGE_SIZE << 1);
    140				mtc0_tlbw_hazard();
    141				tlb_probe();
    142				tlb_probe_hazard();
    143				idx = read_c0_index();
    144				write_c0_entrylo0(0);
    145				write_c0_entrylo1(0);
    146				if (idx < 0)
    147					continue;
    148				/* Make sure all entries differ. */
    149				write_c0_entryhi(UNIQUE_ENTRYHI(idx));
    150				mtc0_tlbw_hazard();
    151				tlb_write_indexed();
    152			}
    153			tlbw_use_hazard();
    154			write_c0_entryhi(old_entryhi);
    155			if (cpu_has_mmid)
    156				write_c0_memorymapid(old_mmid);
    157			htw_start();
    158		} else {
    159			drop_mmu_context(mm);
    160		}
    161		flush_micro_tlb();
    162		local_irq_restore(flags);
    163	}
    164}
    165
    166void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
    167{
    168	unsigned long size, flags;
    169
    170	local_irq_save(flags);
    171	size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
    172	size = (size + 1) >> 1;
    173	if (size <= (current_cpu_data.tlbsizeftlbsets ?
    174		     current_cpu_data.tlbsize / 8 :
    175		     current_cpu_data.tlbsize / 2)) {
    176		int pid = read_c0_entryhi();
    177
    178		start &= (PAGE_MASK << 1);
    179		end += ((PAGE_SIZE << 1) - 1);
    180		end &= (PAGE_MASK << 1);
    181		htw_stop();
    182
    183		while (start < end) {
    184			int idx;
    185
    186			write_c0_entryhi(start);
    187			start += (PAGE_SIZE << 1);
    188			mtc0_tlbw_hazard();
    189			tlb_probe();
    190			tlb_probe_hazard();
    191			idx = read_c0_index();
    192			write_c0_entrylo0(0);
    193			write_c0_entrylo1(0);
    194			if (idx < 0)
    195				continue;
    196			/* Make sure all entries differ. */
    197			write_c0_entryhi(UNIQUE_ENTRYHI(idx));
    198			mtc0_tlbw_hazard();
    199			tlb_write_indexed();
    200		}
    201		tlbw_use_hazard();
    202		write_c0_entryhi(pid);
    203		htw_start();
    204	} else {
    205		local_flush_tlb_all();
    206	}
    207	flush_micro_tlb();
    208	local_irq_restore(flags);
    209}
    210
    211void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
    212{
    213	int cpu = smp_processor_id();
    214
    215	if (cpu_context(cpu, vma->vm_mm) != 0) {
    216		unsigned long old_mmid;
    217		unsigned long flags, old_entryhi;
    218		int idx;
    219
    220		page &= (PAGE_MASK << 1);
    221		local_irq_save(flags);
    222		old_entryhi = read_c0_entryhi();
    223		htw_stop();
    224		if (cpu_has_mmid) {
    225			old_mmid = read_c0_memorymapid();
    226			write_c0_entryhi(page);
    227			write_c0_memorymapid(cpu_asid(cpu, vma->vm_mm));
    228		} else {
    229			write_c0_entryhi(page | cpu_asid(cpu, vma->vm_mm));
    230		}
    231		mtc0_tlbw_hazard();
    232		tlb_probe();
    233		tlb_probe_hazard();
    234		idx = read_c0_index();
    235		write_c0_entrylo0(0);
    236		write_c0_entrylo1(0);
    237		if (idx < 0)
    238			goto finish;
    239		/* Make sure all entries differ. */
    240		write_c0_entryhi(UNIQUE_ENTRYHI(idx));
    241		mtc0_tlbw_hazard();
    242		tlb_write_indexed();
    243		tlbw_use_hazard();
    244
    245	finish:
    246		write_c0_entryhi(old_entryhi);
    247		if (cpu_has_mmid)
    248			write_c0_memorymapid(old_mmid);
    249		htw_start();
    250		flush_micro_tlb_vm(vma);
    251		local_irq_restore(flags);
    252	}
    253}
    254
    255/*
    256 * This one is only used for pages with the global bit set so we don't care
    257 * much about the ASID.
    258 */
    259void local_flush_tlb_one(unsigned long page)
    260{
    261	unsigned long flags;
    262	int oldpid, idx;
    263
    264	local_irq_save(flags);
    265	oldpid = read_c0_entryhi();
    266	htw_stop();
    267	page &= (PAGE_MASK << 1);
    268	write_c0_entryhi(page);
    269	mtc0_tlbw_hazard();
    270	tlb_probe();
    271	tlb_probe_hazard();
    272	idx = read_c0_index();
    273	write_c0_entrylo0(0);
    274	write_c0_entrylo1(0);
    275	if (idx >= 0) {
    276		/* Make sure all entries differ. */
    277		write_c0_entryhi(UNIQUE_ENTRYHI(idx));
    278		mtc0_tlbw_hazard();
    279		tlb_write_indexed();
    280		tlbw_use_hazard();
    281	}
    282	write_c0_entryhi(oldpid);
    283	htw_start();
    284	flush_micro_tlb();
    285	local_irq_restore(flags);
    286}
    287
    288/*
    289 * We will need multiple versions of update_mmu_cache(), one that just
    290 * updates the TLB with the new pte(s), and another which also checks
    291 * for the R4k "end of page" hardware bug and does the needy.
    292 */
    293void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte)
    294{
    295	unsigned long flags;
    296	pgd_t *pgdp;
    297	p4d_t *p4dp;
    298	pud_t *pudp;
    299	pmd_t *pmdp;
    300	pte_t *ptep;
    301	int idx, pid;
    302
    303	/*
    304	 * Handle debugger faulting in for debugee.
    305	 */
    306	if (current->active_mm != vma->vm_mm)
    307		return;
    308
    309	local_irq_save(flags);
    310
    311	htw_stop();
    312	address &= (PAGE_MASK << 1);
    313	if (cpu_has_mmid) {
    314		write_c0_entryhi(address);
    315	} else {
    316		pid = read_c0_entryhi() & cpu_asid_mask(&current_cpu_data);
    317		write_c0_entryhi(address | pid);
    318	}
    319	pgdp = pgd_offset(vma->vm_mm, address);
    320	mtc0_tlbw_hazard();
    321	tlb_probe();
    322	tlb_probe_hazard();
    323	p4dp = p4d_offset(pgdp, address);
    324	pudp = pud_offset(p4dp, address);
    325	pmdp = pmd_offset(pudp, address);
    326	idx = read_c0_index();
    327#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
    328	/* this could be a huge page  */
    329	if (pmd_huge(*pmdp)) {
    330		unsigned long lo;
    331		write_c0_pagemask(PM_HUGE_MASK);
    332		ptep = (pte_t *)pmdp;
    333		lo = pte_to_entrylo(pte_val(*ptep));
    334		write_c0_entrylo0(lo);
    335		write_c0_entrylo1(lo + (HPAGE_SIZE >> 7));
    336
    337		mtc0_tlbw_hazard();
    338		if (idx < 0)
    339			tlb_write_random();
    340		else
    341			tlb_write_indexed();
    342		tlbw_use_hazard();
    343		write_c0_pagemask(PM_DEFAULT_MASK);
    344	} else
    345#endif
    346	{
    347		ptep = pte_offset_map(pmdp, address);
    348
    349#if defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)
    350#ifdef CONFIG_XPA
    351		write_c0_entrylo0(pte_to_entrylo(ptep->pte_high));
    352		if (cpu_has_xpa)
    353			writex_c0_entrylo0(ptep->pte_low & _PFNX_MASK);
    354		ptep++;
    355		write_c0_entrylo1(pte_to_entrylo(ptep->pte_high));
    356		if (cpu_has_xpa)
    357			writex_c0_entrylo1(ptep->pte_low & _PFNX_MASK);
    358#else
    359		write_c0_entrylo0(ptep->pte_high);
    360		ptep++;
    361		write_c0_entrylo1(ptep->pte_high);
    362#endif
    363#else
    364		write_c0_entrylo0(pte_to_entrylo(pte_val(*ptep++)));
    365		write_c0_entrylo1(pte_to_entrylo(pte_val(*ptep)));
    366#endif
    367		mtc0_tlbw_hazard();
    368		if (idx < 0)
    369			tlb_write_random();
    370		else
    371			tlb_write_indexed();
    372	}
    373	tlbw_use_hazard();
    374	htw_start();
    375	flush_micro_tlb_vm(vma);
    376	local_irq_restore(flags);
    377}
    378
    379void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
    380		     unsigned long entryhi, unsigned long pagemask)
    381{
    382#ifdef CONFIG_XPA
    383	panic("Broken for XPA kernels");
    384#else
    385	unsigned int old_mmid;
    386	unsigned long flags;
    387	unsigned long wired;
    388	unsigned long old_pagemask;
    389	unsigned long old_ctx;
    390
    391	local_irq_save(flags);
    392	if (cpu_has_mmid) {
    393		old_mmid = read_c0_memorymapid();
    394		write_c0_memorymapid(MMID_KERNEL_WIRED);
    395	}
    396	/* Save old context and create impossible VPN2 value */
    397	old_ctx = read_c0_entryhi();
    398	htw_stop();
    399	old_pagemask = read_c0_pagemask();
    400	wired = num_wired_entries();
    401	write_c0_wired(wired + 1);
    402	write_c0_index(wired);
    403	tlbw_use_hazard();	/* What is the hazard here? */
    404	write_c0_pagemask(pagemask);
    405	write_c0_entryhi(entryhi);
    406	write_c0_entrylo0(entrylo0);
    407	write_c0_entrylo1(entrylo1);
    408	mtc0_tlbw_hazard();
    409	tlb_write_indexed();
    410	tlbw_use_hazard();
    411
    412	write_c0_entryhi(old_ctx);
    413	if (cpu_has_mmid)
    414		write_c0_memorymapid(old_mmid);
    415	tlbw_use_hazard();	/* What is the hazard here? */
    416	htw_start();
    417	write_c0_pagemask(old_pagemask);
    418	local_flush_tlb_all();
    419	local_irq_restore(flags);
    420#endif
    421}
    422
    423#ifdef CONFIG_TRANSPARENT_HUGEPAGE
    424
    425int has_transparent_hugepage(void)
    426{
    427	static unsigned int mask = -1;
    428
    429	if (mask == -1) {	/* first call comes during __init */
    430		unsigned long flags;
    431
    432		local_irq_save(flags);
    433		write_c0_pagemask(PM_HUGE_MASK);
    434		back_to_back_c0_hazard();
    435		mask = read_c0_pagemask();
    436		write_c0_pagemask(PM_DEFAULT_MASK);
    437		local_irq_restore(flags);
    438	}
    439	return mask == PM_HUGE_MASK;
    440}
    441EXPORT_SYMBOL(has_transparent_hugepage);
    442
    443#endif /* CONFIG_TRANSPARENT_HUGEPAGE  */
    444
    445/*
    446 * Used for loading TLB entries before trap_init() has started, when we
    447 * don't actually want to add a wired entry which remains throughout the
    448 * lifetime of the system
    449 */
    450
    451int temp_tlb_entry;
    452
    453__init int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1,
    454			       unsigned long entryhi, unsigned long pagemask)
    455{
    456	int ret = 0;
    457	unsigned long flags;
    458	unsigned long wired;
    459	unsigned long old_pagemask;
    460	unsigned long old_ctx;
    461
    462	local_irq_save(flags);
    463	/* Save old context and create impossible VPN2 value */
    464	htw_stop();
    465	old_ctx = read_c0_entryhi();
    466	old_pagemask = read_c0_pagemask();
    467	wired = num_wired_entries();
    468	if (--temp_tlb_entry < wired) {
    469		printk(KERN_WARNING
    470		       "No TLB space left for add_temporary_entry\n");
    471		ret = -ENOSPC;
    472		goto out;
    473	}
    474
    475	write_c0_index(temp_tlb_entry);
    476	write_c0_pagemask(pagemask);
    477	write_c0_entryhi(entryhi);
    478	write_c0_entrylo0(entrylo0);
    479	write_c0_entrylo1(entrylo1);
    480	mtc0_tlbw_hazard();
    481	tlb_write_indexed();
    482	tlbw_use_hazard();
    483
    484	write_c0_entryhi(old_ctx);
    485	write_c0_pagemask(old_pagemask);
    486	htw_start();
    487out:
    488	local_irq_restore(flags);
    489	return ret;
    490}
    491
    492static int ntlb;
    493static int __init set_ntlb(char *str)
    494{
    495	get_option(&str, &ntlb);
    496	return 1;
    497}
    498
    499__setup("ntlb=", set_ntlb);
    500
    501/*
    502 * Configure TLB (for init or after a CPU has been powered off).
    503 */
    504static void r4k_tlb_configure(void)
    505{
    506	/*
    507	 * You should never change this register:
    508	 *   - On R4600 1.7 the tlbp never hits for pages smaller than
    509	 *     the value in the c0_pagemask register.
    510	 *   - The entire mm handling assumes the c0_pagemask register to
    511	 *     be set to fixed-size pages.
    512	 */
    513	write_c0_pagemask(PM_DEFAULT_MASK);
    514	back_to_back_c0_hazard();
    515	if (read_c0_pagemask() != PM_DEFAULT_MASK)
    516		panic("MMU doesn't support PAGE_SIZE=0x%lx", PAGE_SIZE);
    517
    518	write_c0_wired(0);
    519	if (current_cpu_type() == CPU_R10000 ||
    520	    current_cpu_type() == CPU_R12000 ||
    521	    current_cpu_type() == CPU_R14000 ||
    522	    current_cpu_type() == CPU_R16000)
    523		write_c0_framemask(0);
    524
    525	if (cpu_has_rixi) {
    526		/*
    527		 * Enable the no read, no exec bits, and enable large physical
    528		 * address.
    529		 */
    530#ifdef CONFIG_64BIT
    531		set_c0_pagegrain(PG_RIE | PG_XIE | PG_ELPA);
    532#else
    533		set_c0_pagegrain(PG_RIE | PG_XIE);
    534#endif
    535	}
    536
    537	temp_tlb_entry = current_cpu_data.tlbsize - 1;
    538
    539	/* From this point on the ARC firmware is dead.	 */
    540	local_flush_tlb_all();
    541
    542	/* Did I tell you that ARC SUCKS?  */
    543}
    544
    545void tlb_init(void)
    546{
    547	r4k_tlb_configure();
    548
    549	if (ntlb) {
    550		if (ntlb > 1 && ntlb <= current_cpu_data.tlbsize) {
    551			int wired = current_cpu_data.tlbsize - ntlb;
    552			write_c0_wired(wired);
    553			write_c0_index(wired-1);
    554			printk("Restricting TLB to %d entries\n", ntlb);
    555		} else
    556			printk("Ignoring invalid argument ntlb=%d\n", ntlb);
    557	}
    558
    559	build_tlb_refill_handler();
    560}
    561
    562static int r4k_tlb_pm_notifier(struct notifier_block *self, unsigned long cmd,
    563			       void *v)
    564{
    565	switch (cmd) {
    566	case CPU_PM_ENTER_FAILED:
    567	case CPU_PM_EXIT:
    568		r4k_tlb_configure();
    569		break;
    570	}
    571
    572	return NOTIFY_OK;
    573}
    574
    575static struct notifier_block r4k_tlb_pm_notifier_block = {
    576	.notifier_call = r4k_tlb_pm_notifier,
    577};
    578
    579static int __init r4k_tlb_init_pm(void)
    580{
    581	return cpu_pm_register_notifier(&r4k_tlb_pm_notifier_block);
    582}
    583arch_initcall(r4k_tlb_init_pm);