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

cache-sh4.c (9839B)


      1/*
      2 * arch/sh/mm/cache-sh4.c
      3 *
      4 * Copyright (C) 1999, 2000, 2002  Niibe Yutaka
      5 * Copyright (C) 2001 - 2009  Paul Mundt
      6 * Copyright (C) 2003  Richard Curnow
      7 * Copyright (c) 2007 STMicroelectronics (R&D) Ltd.
      8 *
      9 * This file is subject to the terms and conditions of the GNU General Public
     10 * License.  See the file "COPYING" in the main directory of this archive
     11 * for more details.
     12 */
     13#include <linux/init.h>
     14#include <linux/mm.h>
     15#include <linux/io.h>
     16#include <linux/mutex.h>
     17#include <linux/fs.h>
     18#include <linux/highmem.h>
     19#include <linux/pagemap.h>
     20#include <asm/mmu_context.h>
     21#include <asm/cache_insns.h>
     22#include <asm/cacheflush.h>
     23
     24/*
     25 * The maximum number of pages we support up to when doing ranged dcache
     26 * flushing. Anything exceeding this will simply flush the dcache in its
     27 * entirety.
     28 */
     29#define MAX_ICACHE_PAGES	32
     30
     31static void __flush_cache_one(unsigned long addr, unsigned long phys,
     32			       unsigned long exec_offset);
     33
     34/*
     35 * Write back the range of D-cache, and purge the I-cache.
     36 *
     37 * Called from kernel/module.c:sys_init_module and routine for a.out format,
     38 * signal handler code and kprobes code
     39 */
     40static void sh4_flush_icache_range(void *args)
     41{
     42	struct flusher_data *data = args;
     43	unsigned long start, end;
     44	unsigned long flags, v;
     45	int i;
     46
     47	start = data->addr1;
     48	end = data->addr2;
     49
     50	/* If there are too many pages then just blow away the caches */
     51	if (((end - start) >> PAGE_SHIFT) >= MAX_ICACHE_PAGES) {
     52		local_flush_cache_all(NULL);
     53		return;
     54	}
     55
     56	/*
     57	 * Selectively flush d-cache then invalidate the i-cache.
     58	 * This is inefficient, so only use this for small ranges.
     59	 */
     60	start &= ~(L1_CACHE_BYTES-1);
     61	end += L1_CACHE_BYTES-1;
     62	end &= ~(L1_CACHE_BYTES-1);
     63
     64	local_irq_save(flags);
     65	jump_to_uncached();
     66
     67	for (v = start; v < end; v += L1_CACHE_BYTES) {
     68		unsigned long icacheaddr;
     69		int j, n;
     70
     71		__ocbwb(v);
     72
     73		icacheaddr = CACHE_IC_ADDRESS_ARRAY | (v &
     74				cpu_data->icache.entry_mask);
     75
     76		/* Clear i-cache line valid-bit */
     77		n = boot_cpu_data.icache.n_aliases;
     78		for (i = 0; i < cpu_data->icache.ways; i++) {
     79			for (j = 0; j < n; j++)
     80				__raw_writel(0, icacheaddr + (j * PAGE_SIZE));
     81			icacheaddr += cpu_data->icache.way_incr;
     82		}
     83	}
     84
     85	back_to_cached();
     86	local_irq_restore(flags);
     87}
     88
     89static inline void flush_cache_one(unsigned long start, unsigned long phys)
     90{
     91	unsigned long flags, exec_offset = 0;
     92
     93	/*
     94	 * All types of SH-4 require PC to be uncached to operate on the I-cache.
     95	 * Some types of SH-4 require PC to be uncached to operate on the D-cache.
     96	 */
     97	if ((boot_cpu_data.flags & CPU_HAS_P2_FLUSH_BUG) ||
     98	    (start < CACHE_OC_ADDRESS_ARRAY))
     99		exec_offset = cached_to_uncached;
    100
    101	local_irq_save(flags);
    102	__flush_cache_one(start, phys, exec_offset);
    103	local_irq_restore(flags);
    104}
    105
    106/*
    107 * Write back & invalidate the D-cache of the page.
    108 * (To avoid "alias" issues)
    109 */
    110static void sh4_flush_dcache_page(void *arg)
    111{
    112	struct page *page = arg;
    113	unsigned long addr = (unsigned long)page_address(page);
    114#ifndef CONFIG_SMP
    115	struct address_space *mapping = page_mapping_file(page);
    116
    117	if (mapping && !mapping_mapped(mapping))
    118		clear_bit(PG_dcache_clean, &page->flags);
    119	else
    120#endif
    121		flush_cache_one(CACHE_OC_ADDRESS_ARRAY |
    122				(addr & shm_align_mask), page_to_phys(page));
    123
    124	wmb();
    125}
    126
    127/* TODO: Selective icache invalidation through IC address array.. */
    128static void flush_icache_all(void)
    129{
    130	unsigned long flags, ccr;
    131
    132	local_irq_save(flags);
    133	jump_to_uncached();
    134
    135	/* Flush I-cache */
    136	ccr = __raw_readl(SH_CCR);
    137	ccr |= CCR_CACHE_ICI;
    138	__raw_writel(ccr, SH_CCR);
    139
    140	/*
    141	 * back_to_cached() will take care of the barrier for us, don't add
    142	 * another one!
    143	 */
    144
    145	back_to_cached();
    146	local_irq_restore(flags);
    147}
    148
    149static void flush_dcache_all(void)
    150{
    151	unsigned long addr, end_addr, entry_offset;
    152
    153	end_addr = CACHE_OC_ADDRESS_ARRAY +
    154		(current_cpu_data.dcache.sets <<
    155		 current_cpu_data.dcache.entry_shift) *
    156			current_cpu_data.dcache.ways;
    157
    158	entry_offset = 1 << current_cpu_data.dcache.entry_shift;
    159
    160	for (addr = CACHE_OC_ADDRESS_ARRAY; addr < end_addr; ) {
    161		__raw_writel(0, addr); addr += entry_offset;
    162		__raw_writel(0, addr); addr += entry_offset;
    163		__raw_writel(0, addr); addr += entry_offset;
    164		__raw_writel(0, addr); addr += entry_offset;
    165		__raw_writel(0, addr); addr += entry_offset;
    166		__raw_writel(0, addr); addr += entry_offset;
    167		__raw_writel(0, addr); addr += entry_offset;
    168		__raw_writel(0, addr); addr += entry_offset;
    169	}
    170}
    171
    172static void sh4_flush_cache_all(void *unused)
    173{
    174	flush_dcache_all();
    175	flush_icache_all();
    176}
    177
    178/*
    179 * Note : (RPC) since the caches are physically tagged, the only point
    180 * of flush_cache_mm for SH-4 is to get rid of aliases from the
    181 * D-cache.  The assumption elsewhere, e.g. flush_cache_range, is that
    182 * lines can stay resident so long as the virtual address they were
    183 * accessed with (hence cache set) is in accord with the physical
    184 * address (i.e. tag).  It's no different here.
    185 *
    186 * Caller takes mm->mmap_lock.
    187 */
    188static void sh4_flush_cache_mm(void *arg)
    189{
    190	struct mm_struct *mm = arg;
    191
    192	if (cpu_context(smp_processor_id(), mm) == NO_CONTEXT)
    193		return;
    194
    195	flush_dcache_all();
    196}
    197
    198/*
    199 * Write back and invalidate I/D-caches for the page.
    200 *
    201 * ADDR: Virtual Address (U0 address)
    202 * PFN: Physical page number
    203 */
    204static void sh4_flush_cache_page(void *args)
    205{
    206	struct flusher_data *data = args;
    207	struct vm_area_struct *vma;
    208	struct page *page;
    209	unsigned long address, pfn, phys;
    210	int map_coherent = 0;
    211	pmd_t *pmd;
    212	pte_t *pte;
    213	void *vaddr;
    214
    215	vma = data->vma;
    216	address = data->addr1 & PAGE_MASK;
    217	pfn = data->addr2;
    218	phys = pfn << PAGE_SHIFT;
    219	page = pfn_to_page(pfn);
    220
    221	if (cpu_context(smp_processor_id(), vma->vm_mm) == NO_CONTEXT)
    222		return;
    223
    224	pmd = pmd_off(vma->vm_mm, address);
    225	pte = pte_offset_kernel(pmd, address);
    226
    227	/* If the page isn't present, there is nothing to do here. */
    228	if (!(pte_val(*pte) & _PAGE_PRESENT))
    229		return;
    230
    231	if ((vma->vm_mm == current->active_mm))
    232		vaddr = NULL;
    233	else {
    234		/*
    235		 * Use kmap_coherent or kmap_atomic to do flushes for
    236		 * another ASID than the current one.
    237		 */
    238		map_coherent = (current_cpu_data.dcache.n_aliases &&
    239			test_bit(PG_dcache_clean, &page->flags) &&
    240			page_mapcount(page));
    241		if (map_coherent)
    242			vaddr = kmap_coherent(page, address);
    243		else
    244			vaddr = kmap_atomic(page);
    245
    246		address = (unsigned long)vaddr;
    247	}
    248
    249	flush_cache_one(CACHE_OC_ADDRESS_ARRAY |
    250			(address & shm_align_mask), phys);
    251
    252	if (vma->vm_flags & VM_EXEC)
    253		flush_icache_all();
    254
    255	if (vaddr) {
    256		if (map_coherent)
    257			kunmap_coherent(vaddr);
    258		else
    259			kunmap_atomic(vaddr);
    260	}
    261}
    262
    263/*
    264 * Write back and invalidate D-caches.
    265 *
    266 * START, END: Virtual Address (U0 address)
    267 *
    268 * NOTE: We need to flush the _physical_ page entry.
    269 * Flushing the cache lines for U0 only isn't enough.
    270 * We need to flush for P1 too, which may contain aliases.
    271 */
    272static void sh4_flush_cache_range(void *args)
    273{
    274	struct flusher_data *data = args;
    275	struct vm_area_struct *vma;
    276	unsigned long start, end;
    277
    278	vma = data->vma;
    279	start = data->addr1;
    280	end = data->addr2;
    281
    282	if (cpu_context(smp_processor_id(), vma->vm_mm) == NO_CONTEXT)
    283		return;
    284
    285	/*
    286	 * If cache is only 4k-per-way, there are never any 'aliases'.  Since
    287	 * the cache is physically tagged, the data can just be left in there.
    288	 */
    289	if (boot_cpu_data.dcache.n_aliases == 0)
    290		return;
    291
    292	flush_dcache_all();
    293
    294	if (vma->vm_flags & VM_EXEC)
    295		flush_icache_all();
    296}
    297
    298/**
    299 * __flush_cache_one
    300 *
    301 * @addr:  address in memory mapped cache array
    302 * @phys:  P1 address to flush (has to match tags if addr has 'A' bit
    303 *         set i.e. associative write)
    304 * @exec_offset: set to 0x20000000 if flush has to be executed from P2
    305 *               region else 0x0
    306 *
    307 * The offset into the cache array implied by 'addr' selects the
    308 * 'colour' of the virtual address range that will be flushed.  The
    309 * operation (purge/write-back) is selected by the lower 2 bits of
    310 * 'phys'.
    311 */
    312static void __flush_cache_one(unsigned long addr, unsigned long phys,
    313			       unsigned long exec_offset)
    314{
    315	int way_count;
    316	unsigned long base_addr = addr;
    317	struct cache_info *dcache;
    318	unsigned long way_incr;
    319	unsigned long a, ea, p;
    320	unsigned long temp_pc;
    321
    322	dcache = &boot_cpu_data.dcache;
    323	/* Write this way for better assembly. */
    324	way_count = dcache->ways;
    325	way_incr = dcache->way_incr;
    326
    327	/*
    328	 * Apply exec_offset (i.e. branch to P2 if required.).
    329	 *
    330	 * FIXME:
    331	 *
    332	 *	If I write "=r" for the (temp_pc), it puts this in r6 hence
    333	 *	trashing exec_offset before it's been added on - why?  Hence
    334	 *	"=&r" as a 'workaround'
    335	 */
    336	asm volatile("mov.l 1f, %0\n\t"
    337		     "add   %1, %0\n\t"
    338		     "jmp   @%0\n\t"
    339		     "nop\n\t"
    340		     ".balign 4\n\t"
    341		     "1:  .long 2f\n\t"
    342		     "2:\n" : "=&r" (temp_pc) : "r" (exec_offset));
    343
    344	/*
    345	 * We know there will be >=1 iteration, so write as do-while to avoid
    346	 * pointless nead-of-loop check for 0 iterations.
    347	 */
    348	do {
    349		ea = base_addr + PAGE_SIZE;
    350		a = base_addr;
    351		p = phys;
    352
    353		do {
    354			*(volatile unsigned long *)a = p;
    355			/*
    356			 * Next line: intentionally not p+32, saves an add, p
    357			 * will do since only the cache tag bits need to
    358			 * match.
    359			 */
    360			*(volatile unsigned long *)(a+32) = p;
    361			a += 64;
    362			p += 64;
    363		} while (a < ea);
    364
    365		base_addr += way_incr;
    366	} while (--way_count != 0);
    367}
    368
    369extern void __weak sh4__flush_region_init(void);
    370
    371/*
    372 * SH-4 has virtually indexed and physically tagged cache.
    373 */
    374void __init sh4_cache_init(void)
    375{
    376	printk("PVR=%08x CVR=%08x PRR=%08x\n",
    377		__raw_readl(CCN_PVR),
    378		__raw_readl(CCN_CVR),
    379		__raw_readl(CCN_PRR));
    380
    381	local_flush_icache_range	= sh4_flush_icache_range;
    382	local_flush_dcache_page		= sh4_flush_dcache_page;
    383	local_flush_cache_all		= sh4_flush_cache_all;
    384	local_flush_cache_mm		= sh4_flush_cache_mm;
    385	local_flush_cache_dup_mm	= sh4_flush_cache_mm;
    386	local_flush_cache_page		= sh4_flush_cache_page;
    387	local_flush_cache_range		= sh4_flush_cache_range;
    388
    389	sh4__flush_region_init();
    390}