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

context.c (7870B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <linux/atomic.h>
      3#include <linux/mmu_context.h>
      4#include <linux/percpu.h>
      5#include <linux/spinlock.h>
      6
      7static DEFINE_RAW_SPINLOCK(cpu_mmid_lock);
      8
      9static atomic64_t mmid_version;
     10static unsigned int num_mmids;
     11static unsigned long *mmid_map;
     12
     13static DEFINE_PER_CPU(u64, reserved_mmids);
     14static cpumask_t tlb_flush_pending;
     15
     16static bool asid_versions_eq(int cpu, u64 a, u64 b)
     17{
     18	return ((a ^ b) & asid_version_mask(cpu)) == 0;
     19}
     20
     21void get_new_mmu_context(struct mm_struct *mm)
     22{
     23	unsigned int cpu;
     24	u64 asid;
     25
     26	/*
     27	 * This function is specific to ASIDs, and should not be called when
     28	 * MMIDs are in use.
     29	 */
     30	if (WARN_ON(IS_ENABLED(CONFIG_DEBUG_VM) && cpu_has_mmid))
     31		return;
     32
     33	cpu = smp_processor_id();
     34	asid = asid_cache(cpu);
     35
     36	if (!((asid += cpu_asid_inc()) & cpu_asid_mask(&cpu_data[cpu]))) {
     37		if (cpu_has_vtag_icache)
     38			flush_icache_all();
     39		local_flush_tlb_all();	/* start new asid cycle */
     40	}
     41
     42	set_cpu_context(cpu, mm, asid);
     43	asid_cache(cpu) = asid;
     44}
     45EXPORT_SYMBOL_GPL(get_new_mmu_context);
     46
     47void check_mmu_context(struct mm_struct *mm)
     48{
     49	unsigned int cpu = smp_processor_id();
     50
     51	/*
     52	 * This function is specific to ASIDs, and should not be called when
     53	 * MMIDs are in use.
     54	 */
     55	if (WARN_ON(IS_ENABLED(CONFIG_DEBUG_VM) && cpu_has_mmid))
     56		return;
     57
     58	/* Check if our ASID is of an older version and thus invalid */
     59	if (!asid_versions_eq(cpu, cpu_context(cpu, mm), asid_cache(cpu)))
     60		get_new_mmu_context(mm);
     61}
     62EXPORT_SYMBOL_GPL(check_mmu_context);
     63
     64static void flush_context(void)
     65{
     66	u64 mmid;
     67	int cpu;
     68
     69	/* Update the list of reserved MMIDs and the MMID bitmap */
     70	bitmap_clear(mmid_map, 0, num_mmids);
     71
     72	/* Reserve an MMID for kmap/wired entries */
     73	__set_bit(MMID_KERNEL_WIRED, mmid_map);
     74
     75	for_each_possible_cpu(cpu) {
     76		mmid = xchg_relaxed(&cpu_data[cpu].asid_cache, 0);
     77
     78		/*
     79		 * If this CPU has already been through a
     80		 * rollover, but hasn't run another task in
     81		 * the meantime, we must preserve its reserved
     82		 * MMID, as this is the only trace we have of
     83		 * the process it is still running.
     84		 */
     85		if (mmid == 0)
     86			mmid = per_cpu(reserved_mmids, cpu);
     87
     88		__set_bit(mmid & cpu_asid_mask(&cpu_data[cpu]), mmid_map);
     89		per_cpu(reserved_mmids, cpu) = mmid;
     90	}
     91
     92	/*
     93	 * Queue a TLB invalidation for each CPU to perform on next
     94	 * context-switch
     95	 */
     96	cpumask_setall(&tlb_flush_pending);
     97}
     98
     99static bool check_update_reserved_mmid(u64 mmid, u64 newmmid)
    100{
    101	bool hit;
    102	int cpu;
    103
    104	/*
    105	 * Iterate over the set of reserved MMIDs looking for a match.
    106	 * If we find one, then we can update our mm to use newmmid
    107	 * (i.e. the same MMID in the current generation) but we can't
    108	 * exit the loop early, since we need to ensure that all copies
    109	 * of the old MMID are updated to reflect the mm. Failure to do
    110	 * so could result in us missing the reserved MMID in a future
    111	 * generation.
    112	 */
    113	hit = false;
    114	for_each_possible_cpu(cpu) {
    115		if (per_cpu(reserved_mmids, cpu) == mmid) {
    116			hit = true;
    117			per_cpu(reserved_mmids, cpu) = newmmid;
    118		}
    119	}
    120
    121	return hit;
    122}
    123
    124static u64 get_new_mmid(struct mm_struct *mm)
    125{
    126	static u32 cur_idx = MMID_KERNEL_WIRED + 1;
    127	u64 mmid, version, mmid_mask;
    128
    129	mmid = cpu_context(0, mm);
    130	version = atomic64_read(&mmid_version);
    131	mmid_mask = cpu_asid_mask(&boot_cpu_data);
    132
    133	if (!asid_versions_eq(0, mmid, 0)) {
    134		u64 newmmid = version | (mmid & mmid_mask);
    135
    136		/*
    137		 * If our current MMID was active during a rollover, we
    138		 * can continue to use it and this was just a false alarm.
    139		 */
    140		if (check_update_reserved_mmid(mmid, newmmid)) {
    141			mmid = newmmid;
    142			goto set_context;
    143		}
    144
    145		/*
    146		 * We had a valid MMID in a previous life, so try to re-use
    147		 * it if possible.
    148		 */
    149		if (!__test_and_set_bit(mmid & mmid_mask, mmid_map)) {
    150			mmid = newmmid;
    151			goto set_context;
    152		}
    153	}
    154
    155	/* Allocate a free MMID */
    156	mmid = find_next_zero_bit(mmid_map, num_mmids, cur_idx);
    157	if (mmid != num_mmids)
    158		goto reserve_mmid;
    159
    160	/* We're out of MMIDs, so increment the global version */
    161	version = atomic64_add_return_relaxed(asid_first_version(0),
    162					      &mmid_version);
    163
    164	/* Note currently active MMIDs & mark TLBs as requiring flushes */
    165	flush_context();
    166
    167	/* We have more MMIDs than CPUs, so this will always succeed */
    168	mmid = find_first_zero_bit(mmid_map, num_mmids);
    169
    170reserve_mmid:
    171	__set_bit(mmid, mmid_map);
    172	cur_idx = mmid;
    173	mmid |= version;
    174set_context:
    175	set_cpu_context(0, mm, mmid);
    176	return mmid;
    177}
    178
    179void check_switch_mmu_context(struct mm_struct *mm)
    180{
    181	unsigned int cpu = smp_processor_id();
    182	u64 ctx, old_active_mmid;
    183	unsigned long flags;
    184
    185	if (!cpu_has_mmid) {
    186		check_mmu_context(mm);
    187		write_c0_entryhi(cpu_asid(cpu, mm));
    188		goto setup_pgd;
    189	}
    190
    191	/*
    192	 * MMID switch fast-path, to avoid acquiring cpu_mmid_lock when it's
    193	 * unnecessary.
    194	 *
    195	 * The memory ordering here is subtle. If our active_mmids is non-zero
    196	 * and the MMID matches the current version, then we update the CPU's
    197	 * asid_cache with a relaxed cmpxchg. Racing with a concurrent rollover
    198	 * means that either:
    199	 *
    200	 * - We get a zero back from the cmpxchg and end up waiting on
    201	 *   cpu_mmid_lock in check_mmu_context(). Taking the lock synchronises
    202	 *   with the rollover and so we are forced to see the updated
    203	 *   generation.
    204	 *
    205	 * - We get a valid MMID back from the cmpxchg, which means the
    206	 *   relaxed xchg in flush_context will treat us as reserved
    207	 *   because atomic RmWs are totally ordered for a given location.
    208	 */
    209	ctx = cpu_context(cpu, mm);
    210	old_active_mmid = READ_ONCE(cpu_data[cpu].asid_cache);
    211	if (!old_active_mmid ||
    212	    !asid_versions_eq(cpu, ctx, atomic64_read(&mmid_version)) ||
    213	    !cmpxchg_relaxed(&cpu_data[cpu].asid_cache, old_active_mmid, ctx)) {
    214		raw_spin_lock_irqsave(&cpu_mmid_lock, flags);
    215
    216		ctx = cpu_context(cpu, mm);
    217		if (!asid_versions_eq(cpu, ctx, atomic64_read(&mmid_version)))
    218			ctx = get_new_mmid(mm);
    219
    220		WRITE_ONCE(cpu_data[cpu].asid_cache, ctx);
    221		raw_spin_unlock_irqrestore(&cpu_mmid_lock, flags);
    222	}
    223
    224	/*
    225	 * Invalidate the local TLB if needed. Note that we must only clear our
    226	 * bit in tlb_flush_pending after this is complete, so that the
    227	 * cpu_has_shared_ftlb_entries case below isn't misled.
    228	 */
    229	if (cpumask_test_cpu(cpu, &tlb_flush_pending)) {
    230		if (cpu_has_vtag_icache)
    231			flush_icache_all();
    232		local_flush_tlb_all();
    233		cpumask_clear_cpu(cpu, &tlb_flush_pending);
    234	}
    235
    236	write_c0_memorymapid(ctx & cpu_asid_mask(&boot_cpu_data));
    237
    238	/*
    239	 * If this CPU shares FTLB entries with its siblings and one or more of
    240	 * those siblings hasn't yet invalidated its TLB following a version
    241	 * increase then we need to invalidate any TLB entries for our MMID
    242	 * that we might otherwise pick up from a sibling.
    243	 *
    244	 * We ifdef on CONFIG_SMP because cpu_sibling_map isn't defined in
    245	 * CONFIG_SMP=n kernels.
    246	 */
    247#ifdef CONFIG_SMP
    248	if (cpu_has_shared_ftlb_entries &&
    249	    cpumask_intersects(&tlb_flush_pending, &cpu_sibling_map[cpu])) {
    250		/* Ensure we operate on the new MMID */
    251		mtc0_tlbw_hazard();
    252
    253		/*
    254		 * Invalidate all TLB entries associated with the new
    255		 * MMID, and wait for the invalidation to complete.
    256		 */
    257		ginvt_mmid();
    258		sync_ginv();
    259	}
    260#endif
    261
    262setup_pgd:
    263	TLBMISS_HANDLER_SETUP_PGD(mm->pgd);
    264}
    265EXPORT_SYMBOL_GPL(check_switch_mmu_context);
    266
    267static int mmid_init(void)
    268{
    269	if (!cpu_has_mmid)
    270		return 0;
    271
    272	/*
    273	 * Expect allocation after rollover to fail if we don't have at least
    274	 * one more MMID than CPUs.
    275	 */
    276	num_mmids = asid_first_version(0);
    277	WARN_ON(num_mmids <= num_possible_cpus());
    278
    279	atomic64_set(&mmid_version, asid_first_version(0));
    280	mmid_map = kcalloc(BITS_TO_LONGS(num_mmids), sizeof(*mmid_map),
    281			   GFP_KERNEL);
    282	if (!mmid_map)
    283		panic("Failed to allocate bitmap for %u MMIDs\n", num_mmids);
    284
    285	/* Reserve an MMID for kmap/wired entries */
    286	__set_bit(MMID_KERNEL_WIRED, mmid_map);
    287
    288	pr_info("MMID allocator initialised with %u entries\n", num_mmids);
    289	return 0;
    290}
    291early_initcall(mmid_init);