#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define UNLOCK_ALL 0x00000000 /* allocation in any way */ #define LOCK_ALL (~UNLOCK_ALL) #define MAX_NR_WAYS 16 #define MAX_NR_COLORS 16 void mem_lock(u32 lock_val, int cpu); /* * unlocked_way[i] : allocation can occur in way i * * 0 = allocation can occur in the corresponding way * 1 = allocation cannot occur in the corresponding way */ u32 unlocked_way[MAX_NR_WAYS] = { 0xFFFFFFFE, /* way 0 unlocked */ 0xFFFFFFFD, 0xFFFFFFFB, 0xFFFFFFF7, 0xFFFFFFEF, /* way 4 unlocked */ 0xFFFFFFDF, 0xFFFFFFBF, 0xFFFFFF7F, 0xFFFFFEFF, /* way 8 unlocked */ 0xFFFFFDFF, 0xFFFFFBFF, 0xFFFFF7FF, 0xFFFFEFFF, /* way 12 unlocked */ 0xFFFFDFFF, 0xFFFFBFFF, 0xFFFF7FFF, }; u32 nr_unlocked_way[MAX_NR_WAYS+1] = { 0x0000FFFF, /* all ways are locked. usable = 0*/ 0x0000FFFE, /* way ~0 unlocked. usable = 1 */ 0x0000FFFC, 0x0000FFF8, 0x0000FFF0, 0x0000FFE0, 0x0000FFC0, 0x0000FF80, 0x0000FF00, 0x0000FE00, 0x0000FC00, 0x0000F800, 0x0000F000, 0x0000E000, 0x0000C000, 0x00008000, 0x00000000, /* way ~15 unlocked. usable = 16 */ }; u32 way_partition[4] = { 0xfffffff0, /* cpu0 */ 0xffffff0f, /* cpu1 */ 0xfffff0ff, /* cpu2 */ 0xffff0fff, /* cpu3 */ }; u32 way_partitions[9] = { 0xffff0003, /* cpu0 A */ 0xffff0003, /* cpu0 B */ 0xffff000C, /* cpu1 A */ 0xffff000C, /* cpu1 B */ 0xffff0030, /* cpu2 A */ 0xffff0030, /* cpu2 B */ 0xffff00C0, /* cpu3 A */ 0xffff00C0, /* cpu3 B */ 0xffffff00, /* lv C */ }; u32 prev_lockdown_d_reg[5] = { 0x0000FF00, 0x0000FF00, 0x0000FF00, 0x0000FF00, 0x000000FF, /* share with level-C */ }; u32 prev_lockdown_i_reg[5] = { 0x0000FF00, 0x0000FF00, 0x0000FF00, 0x0000FF00, 0x000000FF, /* share with level-C */ }; u32 prev_lbm_i_reg[8] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; u32 prev_lbm_d_reg[8] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static void __iomem *cache_base; static void __iomem *lockreg_d; static void __iomem *lockreg_i; static u32 cache_id; struct mutex actlr_mutex; struct mutex l2x0_prefetch_mutex; struct mutex lockdown_proc; static u32 way_partition_min; static u32 way_partition_max; static int zero = 0; static int one = 1; static int l1_prefetch_proc; static int l2_prefetch_hint_proc; static int l2_double_linefill_proc; static int l2_data_prefetch_proc; static int os_isolation; static int use_part; u32 lockdown_reg[9] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; #define ld_d_reg(cpu) ({ int __cpu = cpu; \ void __iomem *__v = cache_base + L2X0_LOCKDOWN_WAY_D_BASE + \ __cpu * L2X0_LOCKDOWN_STRIDE; __v; }) #define ld_i_reg(cpu) ({ int __cpu = cpu; \ void __iomem *__v = cache_base + L2X0_LOCKDOWN_WAY_I_BASE + \ __cpu * L2X0_LOCKDOWN_STRIDE; __v; }) int lock_all; int nr_lockregs; static raw_spinlock_t cache_lock; static raw_spinlock_t prefetch_lock; static void ***flusher_pages = NULL; extern void l2c310_flush_all(void); static inline void cache_wait_way(void __iomem *reg, unsigned long mask) { /* wait for cache operation by line or way to complete */ while (readl_relaxed(reg) & mask) cpu_relax(); } #ifdef CONFIG_CACHE_L2X0 static inline void cache_wait(void __iomem *reg, unsigned long mask) { /* cache operations by line are atomic on PL310 */ } #else #define cache_wait cache_wait_way #endif static inline void cache_sync(void) { void __iomem *base = cache_base; writel_relaxed(0, base + L2X0_CACHE_SYNC); cache_wait(base + L2X0_CACHE_SYNC, 1); } static void print_lockdown_registers(int cpu) { int i; //for (i = 0; i < nr_lockregs; i++) { for (i = 0; i < 4; i++) { printk("P%d Lockdown Data CPU %2d: 0x%04x\n", cpu, i, readl_relaxed(ld_d_reg(i))); printk("P%d Lockdown Inst CPU %2d: 0x%04x\n", cpu, i, readl_relaxed(ld_i_reg(i))); } } static void test_lockdown(void *ignore) { int i, cpu; cpu = smp_processor_id(); printk("Start lockdown test on CPU %d.\n", cpu); for (i = 0; i < nr_lockregs; i++) { printk("CPU %2d data reg: 0x%8p\n", i, ld_d_reg(i)); printk("CPU %2d inst reg: 0x%8p\n", i, ld_i_reg(i)); } printk("Lockdown initial state:\n"); print_lockdown_registers(cpu); printk("---\n"); for (i = 0; i < nr_lockregs; i++) { writel_relaxed(1, ld_d_reg(i)); writel_relaxed(2, ld_i_reg(i)); } printk("Lockdown all data=1 instr=2:\n"); print_lockdown_registers(cpu); printk("---\n"); for (i = 0; i < nr_lockregs; i++) { writel_relaxed((1 << i), ld_d_reg(i)); writel_relaxed(((1 << 8) >> i), ld_i_reg(i)); } printk("Lockdown varies:\n"); print_lockdown_registers(cpu); printk("---\n"); for (i = 0; i < nr_lockregs; i++) { writel_relaxed(UNLOCK_ALL, ld_d_reg(i)); writel_relaxed(UNLOCK_ALL, ld_i_reg(i)); } printk("Lockdown all zero:\n"); print_lockdown_registers(cpu); printk("End lockdown test.\n"); } void litmus_setup_lockdown(void __iomem *base, u32 id) { cache_base = base; cache_id = id; lockreg_d = cache_base + L2X0_LOCKDOWN_WAY_D_BASE; lockreg_i = cache_base + L2X0_LOCKDOWN_WAY_I_BASE; if (L2X0_CACHE_ID_PART_L310 == (cache_id & L2X0_CACHE_ID_PART_MASK)) { nr_lockregs = 8; } else { printk("Unknown cache ID!\n"); nr_lockregs = 1; } mutex_init(&actlr_mutex); mutex_init(&l2x0_prefetch_mutex); mutex_init(&lockdown_proc); raw_spin_lock_init(&cache_lock); raw_spin_lock_init(&prefetch_lock); test_lockdown(NULL); } int way_partition_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret = 0, i; unsigned long flags; mutex_lock(&lockdown_proc); ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); if (ret) goto out; if (write) { printk("Way-partition settings:\n"); for (i = 0; i < 9; i++) { printk("0x%08X\n", way_partitions[i]); } for (i = 0; i < 4; i++) { writel_relaxed(~way_partitions[i*2], cache_base + L2X0_LOCKDOWN_WAY_D_BASE + i * L2X0_LOCKDOWN_STRIDE); writel_relaxed(~way_partitions[i*2], cache_base + L2X0_LOCKDOWN_WAY_I_BASE + i * L2X0_LOCKDOWN_STRIDE); } } local_irq_save(flags); print_lockdown_registers(smp_processor_id()); l2c310_flush_all(); local_irq_restore(flags); out: mutex_unlock(&lockdown_proc); return ret; } int lock_all_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret = 0, i; unsigned long flags; mutex_lock(&lockdown_proc); ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); if (ret) goto out; if (write && lock_all == 1) { for (i = 0; i < nr_lockregs; i++) { writel_relaxed(0xFFFF, cache_base + L2X0_LOCKDOWN_WAY_D_BASE + i * L2X0_LOCKDOWN_STRIDE); writel_relaxed(0xFFFF, cache_base + L2X0_LOCKDOWN_WAY_I_BASE + i * L2X0_LOCKDOWN_STRIDE); } /* for (i = 0; i < nr_lockregs; i++) { barrier(); mem_lock(LOCK_ALL, i); barrier(); //writel_relaxed(nr_unlocked_way[0], ld_d_reg(i)); //writel_relaxed(nr_unlocked_way[0], ld_i_reg(i)); } */ } if (write && lock_all == 0) { for (i = 0; i < nr_lockregs; i++) { writel_relaxed(0x0, cache_base + L2X0_LOCKDOWN_WAY_D_BASE + i * L2X0_LOCKDOWN_STRIDE); writel_relaxed(0x0, cache_base + L2X0_LOCKDOWN_WAY_I_BASE + i * L2X0_LOCKDOWN_STRIDE); } /* for (i = 0; i < nr_lockregs; i++) { barrier(); mem_lock(UNLOCK_ALL, i); barrier(); //writel_relaxed(nr_unlocked_way[16], ld_d_reg(i)); //writel_relaxed(nr_unlocked_way[16], ld_i_reg(i)); } */ } printk("LOCK_ALL HANDLER\n"); local_irq_save(flags); print_lockdown_registers(smp_processor_id()); l2c310_flush_all(); local_irq_restore(flags); out: mutex_unlock(&lockdown_proc); return ret; } void cache_lockdown(u32 lock_val, int cpu) { //unsigned long flags; //raw_spin_lock_irqsave(&cache_lock, flags); __asm__ __volatile__ ( " str %[lockval], [%[dcachereg]]\n" " str %[lockval], [%[icachereg]]\n" : : [dcachereg] "r" (ld_d_reg(cpu)), [icachereg] "r" (ld_i_reg(cpu)), [lockval] "r" (lock_val) : "cc"); //raw_spin_unlock_irqrestore(&cache_lock, flags); } void do_partition(enum crit_level lv, int cpu) { u32 regs; unsigned long flags; if (lock_all || !use_part) return; raw_spin_lock_irqsave(&cache_lock, flags); switch(lv) { case CRIT_LEVEL_A: regs = ~way_partitions[cpu*2]; regs &= 0x0000ffff; break; case CRIT_LEVEL_B: regs = ~way_partitions[cpu*2+1]; regs &= 0x0000ffff; break; case CRIT_LEVEL_C: case NUM_CRIT_LEVELS: regs = ~way_partitions[8]; regs &= 0x0000ffff; break; default: BUG(); } barrier(); //cache_lockdown(regs, cpu); writel_relaxed(regs, cache_base + L2X0_LOCKDOWN_WAY_D_BASE + cpu * L2X0_LOCKDOWN_STRIDE); writel_relaxed(regs, cache_base + L2X0_LOCKDOWN_WAY_I_BASE + cpu * L2X0_LOCKDOWN_STRIDE); barrier(); raw_spin_unlock_irqrestore(&cache_lock, flags); flush_cache(0); } void lock_cache(int cpu, u32 val) { unsigned long flags; local_irq_save(flags); if (val != 0xffffffff) { writel_relaxed(val, cache_base + L2X0_LOCKDOWN_WAY_D_BASE + cpu * L2X0_LOCKDOWN_STRIDE); writel_relaxed(val, cache_base + L2X0_LOCKDOWN_WAY_I_BASE + cpu * L2X0_LOCKDOWN_STRIDE); } else { int i; for (i = 0; i < 4; i++) do_partition(CRIT_LEVEL_A, i); } local_irq_restore(flags); } int use_part_proc_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret = 0; mutex_lock(&lockdown_proc); ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); if (ret) goto out; printk("USE_PART HANDLER = %d\n", use_part); out: mutex_unlock(&lockdown_proc); return ret; } int os_isolation_proc_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret = 0; mutex_lock(&lockdown_proc); ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); if (ret) goto out; printk("OS_ISOLATION HANDLER = %d\n", os_isolation); out: mutex_unlock(&lockdown_proc); return ret; } int lockdown_reg_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret = 0, i; mutex_lock(&lockdown_proc); ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); if (ret) goto out; if (write) { for (i = 0; i < nr_lockregs; i++) { writel_relaxed(lockdown_reg[i], cache_base + L2X0_LOCKDOWN_WAY_D_BASE + i * L2X0_LOCKDOWN_STRIDE); writel_relaxed(lockdown_reg[i], cache_base + L2X0_LOCKDOWN_WAY_I_BASE + i * L2X0_LOCKDOWN_STRIDE); } } out: mutex_unlock(&lockdown_proc); return ret; } int lockdown_global_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret = 0, i; mutex_lock(&lockdown_proc); ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); if (ret) goto out; if (write) { for (i = 0; i < nr_lockregs; i++) { writel_relaxed(lockdown_reg[8], cache_base + L2X0_LOCKDOWN_WAY_D_BASE + i * L2X0_LOCKDOWN_STRIDE); writel_relaxed(lockdown_reg[8], cache_base + L2X0_LOCKDOWN_WAY_I_BASE + i * L2X0_LOCKDOWN_STRIDE); } } out: mutex_unlock(&lockdown_proc); return ret; } void inline enter_irq_mode(void) { int cpu = smp_processor_id(); if (os_isolation == 0) return; prev_lockdown_i_reg[cpu] = readl_relaxed(ld_i_reg(cpu)); prev_lockdown_d_reg[cpu] = readl_relaxed(ld_d_reg(cpu)); writel_relaxed(way_partitions[8], ld_i_reg(cpu)); writel_relaxed(way_partitions[8], ld_d_reg(cpu)); } void inline exit_irq_mode(void) { int cpu = smp_processor_id(); if (os_isolation == 0) return; writel_relaxed(prev_lockdown_i_reg[cpu], ld_i_reg(cpu)); writel_relaxed(prev_lockdown_d_reg[cpu], ld_d_reg(cpu)); } /* Operate on the Cortex-A9's ACTLR register */ #define ACTLR_L2_PREFETCH_HINT (1 << 1) #define ACTLR_L1_PREFETCH (1 << 2) /* * Change the ACTLR. * @mode - If 1 (0), set (clear) the bit given in @mask in the ACTLR. * @mask - A mask in which one bit is set to operate on the ACTLR. */ static void actlr_change(int mode, int mask) { u32 orig_value, new_value, reread_value; if (0 != mode && 1 != mode) { printk(KERN_WARNING "Called %s with mode != 0 and mode != 1.\n", __FUNCTION__); return; } /* get the original value */ asm volatile("mrc p15, 0, %0, c1, c0, 1" : "=r" (orig_value)); if (0 == mode) new_value = orig_value & ~(mask); else new_value = orig_value | mask; asm volatile("mcr p15, 0, %0, c1, c0, 1" : : "r" (new_value)); asm volatile("mrc p15, 0, %0, c1, c0, 1" : "=r" (reread_value)); printk("ACTLR: orig: 0x%8x wanted: 0x%8x new: 0x%8x\n", orig_value, new_value, reread_value); } int litmus_l1_prefetch_proc_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret, mode; mutex_lock(&actlr_mutex); ret = proc_dointvec(table, write, buffer, lenp, ppos); if (!ret && write) { mode = *((int*)table->data); actlr_change(mode, ACTLR_L1_PREFETCH); } mutex_unlock(&actlr_mutex); return ret; } int litmus_l2_prefetch_hint_proc_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret, mode; mutex_lock(&actlr_mutex); ret = proc_dointvec(table, write, buffer, lenp, ppos); if (!ret && write) { mode = *((int*)table->data); actlr_change(mode, ACTLR_L2_PREFETCH_HINT); } mutex_unlock(&actlr_mutex); return ret; } /* Operate on the PL-310's Prefetch Control Register, L310_PREFETCH_CTRL */ #define L2X0_PREFETCH_DOUBLE_LINEFILL (1 << 30) #define L2X0_PREFETCH_INST_PREFETCH (1 << 29) #define L2X0_PREFETCH_DATA_PREFETCH (1 << 28) static void l2x0_prefetch_change(int mode, int mask) { u32 orig_value, new_value, reread_value; if (0 != mode && 1 != mode) { printk(KERN_WARNING "Called %s with mode != 0 and mode != 1.\n", __FUNCTION__); return; } orig_value = readl_relaxed(cache_base + L310_PREFETCH_CTRL); if (0 == mode) new_value = orig_value & ~(mask); else new_value = orig_value | mask; writel_relaxed(new_value, cache_base + L310_PREFETCH_CTRL); reread_value = readl_relaxed(cache_base + L310_PREFETCH_CTRL); printk("l2x0 prefetch: orig: 0x%8x wanted: 0x%8x new: 0x%8x\n", orig_value, new_value, reread_value); } int litmus_l2_double_linefill_proc_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret, mode; mutex_lock(&l2x0_prefetch_mutex); ret = proc_dointvec(table, write, buffer, lenp, ppos); if (!ret && write) { mode = *((int*)table->data); l2x0_prefetch_change(mode, L2X0_PREFETCH_DOUBLE_LINEFILL); } mutex_unlock(&l2x0_prefetch_mutex); return ret; } int litmus_l2_data_prefetch_proc_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret, mode; mutex_lock(&l2x0_prefetch_mutex); ret = proc_dointvec(table, write, buffer, lenp, ppos); if (!ret && write) { mode = *((int*)table->data); l2x0_prefetch_change(mode, L2X0_PREFETCH_DATA_PREFETCH|L2X0_PREFETCH_INST_PREFETCH); } mutex_unlock(&l2x0_prefetch_mutex); return ret; } int do_perf_test_proc_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); int setup_flusher_proc_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); static struct ctl_table cache_table[] = { { .procname = "C0_LA_way", .mode = 0666, .proc_handler = way_partition_handler, .data = &way_partitions[0], .maxlen = sizeof(way_partitions[0]), .extra1 = &way_partition_min, .extra2 = &way_partition_max, }, { .procname = "C0_LB_way", .mode = 0666, .proc_handler = way_partition_handler, .data = &way_partitions[1], .maxlen = sizeof(way_partitions[1]), .extra1 = &way_partition_min, .extra2 = &way_partition_max, }, { .procname = "C1_LA_way", .mode = 0666, .proc_handler = way_partition_handler, .data = &way_partitions[2], .maxlen = sizeof(way_partitions[2]), .extra1 = &way_partition_min, .extra2 = &way_partition_max, }, { .procname = "C1_LB_way", .mode = 0666, .proc_handler = way_partition_handler, .data = &way_partitions[3], .maxlen = sizeof(way_partitions[3]), .extra1 = &way_partition_min, .extra2 = &way_partition_max, }, { .procname = "C2_LA_way", .mode = 0666, .proc_handler = way_partition_handler, .data = &way_partitions[4], .maxlen = sizeof(way_partitions[4]), .extra1 = &way_partition_min, .extra2 = &way_partition_max, }, { .procname = "C2_LB_way", .mode = 0666, .proc_handler = way_partition_handler, .data = &way_partitions[5], .maxlen = sizeof(way_partitions[5]), .extra1 = &way_partition_min, .extra2 = &way_partition_max, }, { .procname = "C3_LA_way", .mode = 0666, .proc_handler = way_partition_handler, .data = &way_partitions[6], .maxlen = sizeof(way_partitions[6]), .extra1 = &way_partition_min, .extra2 = &way_partition_max, }, { .procname = "C3_LB_way", .mode = 0666, .proc_handler = way_partition_handler, .data = &way_partitions[7], .maxlen = sizeof(way_partitions[7]), .extra1 = &way_partition_min, .extra2 = &way_partition_max, }, { .procname = "Call_LC_way", .mode = 0666, .proc_handler = way_partition_handler, .data = &way_partitions[8], .maxlen = sizeof(way_partitions[8]), .extra1 = &way_partition_min, .extra2 = &way_partition_max, }, { .procname = "lock_all", .mode = 0666, .proc_handler = lock_all_handler, .data = &lock_all, .maxlen = sizeof(lock_all), .extra1 = &zero, .extra2 = &one, }, { .procname = "l1_prefetch", .mode = 0644, .proc_handler = litmus_l1_prefetch_proc_handler, .data = &l1_prefetch_proc, .maxlen = sizeof(l1_prefetch_proc), }, { .procname = "l2_prefetch_hint", .mode = 0644, .proc_handler = litmus_l2_prefetch_hint_proc_handler, .data = &l2_prefetch_hint_proc, .maxlen = sizeof(l2_prefetch_hint_proc), }, { .procname = "l2_double_linefill", .mode = 0644, .proc_handler = litmus_l2_double_linefill_proc_handler, .data = &l2_double_linefill_proc, .maxlen = sizeof(l2_double_linefill_proc), }, { .procname = "l2_data_prefetch", .mode = 0644, .proc_handler = litmus_l2_data_prefetch_proc_handler, .data = &l2_data_prefetch_proc, .maxlen = sizeof(l2_data_prefetch_proc), }, { .procname = "os_isolation", .mode = 0644, .proc_handler = os_isolation_proc_handler, .data = &os_isolation, .maxlen = sizeof(os_isolation), }, { .procname = "use_part", .mode = 0644, .proc_handler = use_part_proc_handler, .data = &use_part, .maxlen = sizeof(use_part), }, { .procname = "do_perf_test", .mode = 0644, .proc_handler = do_perf_test_proc_handler, }, { .procname = "setup_flusher", .mode = 0644, .proc_handler = setup_flusher_proc_handler, }, { .procname = "lockdown_reg_0", .mode = 0644, .proc_handler = lockdown_reg_handler, .data = &lockdown_reg[0], .maxlen = sizeof(lockdown_reg[0]), .extra1 = &way_partition_min, .extra2 = &way_partition_max, }, { .procname = "lockdown_reg_1", .mode = 0644, .proc_handler = lockdown_reg_handler, .data = &lockdown_reg[1], .maxlen = sizeof(lockdown_reg[1]), .extra1 = &way_partition_min, .extra2 = &way_partition_max, }, { .procname = "lockdown_reg_2", .mode = 0644, .proc_handler = lockdown_reg_handler, .data = &lockdown_reg[2], .maxlen = sizeof(lockdown_reg[2]), .extra1 = &way_partition_min, .extra2 = &way_partition_max, }, { .procname = "lockdown_reg_3", .mode = 0644, .proc_handler = lockdown_reg_handler, .data = &lockdown_reg[3], .maxlen = sizeof(lockdown_reg[3]), .extra1 = &way_partition_min, .extra2 = &way_partition_max, }, { .procname = "lockdown_regs", .mode = 0644, .proc_handler = lockdown_global_handler, .data = &lockdown_reg[8], .maxlen = sizeof(lockdown_reg[8]), .extra1 = &way_partition_min, .extra2 = &way_partition_max, }, { } }; static struct ctl_table litmus_dir_table[] = { { .procname = "litmus", .mode = 0555, .child = cache_table, }, { } }; u32 color_read_in_mem(u32 lock_val, u32 unlock_val, void *start, void *end) { u32 v = 0; __asm__ __volatile__ ( " .align 5\n" " str %[lockval], [%[cachereg]]\n" "1: ldr %[val], [%[addr]], #32 @ 32 bytes = 1 cache line\n" " cmp %[end], %[addr] @ subtracts addr from end\n" " bgt 1b\n @ read more, if necessary\n" : [addr] "+r" (start), [val] "+r" (v) : [end] "r" (end), #ifdef CONFIG_CACHE_L2X0 [cachereg] "r" (ld_d_reg(raw_smp_processor_id())), #else [cachereg] "r" (lockreg_d), #endif [lockval] "r" (lock_val) : "cc"); return v; } /* * Prefetch by reading the first word of each cache line in a page. * * @lockdown_reg: address of the lockdown register to write * @lock_val: value to be written to @lockdown_reg * @unlock_val: will unlock the cache to this value * @addr: start address to be prefetched * @end_addr: end address to prefetch (exclusive) * * Assumes: addr < end_addr AND addr != end_addr */ u32 color_read_in_mem_lock(u32 lock_val, u32 unlock_val, void *start, void *end) { #ifndef CONFIG_CACHE_L2X0 unsigned long flags; #endif u32 v = 0; #ifndef CONFIG_CACHE_L2X0 raw_spin_lock_irqsave(&prefetch_lock, flags); #endif __asm__ __volatile__ ( " .align 5\n" " str %[lockval], [%[cachereg]]\n" "1: ldr %[val], [%[addr]], #32 @ 32 bytes = 1 cache line\n" " cmp %[end], %[addr] @ subtracts addr from end\n" " bgt 1b\n @ read more, if necessary\n" " str %[unlockval], [%[cachereg]]\n" : [addr] "+r" (start), [val] "+r" (v) : [end] "r" (end), #ifdef CONFIG_CACHE_L2X0 [cachereg] "r" (ld_d_reg(raw_smp_processor_id())), #else [cachereg] "r" (lockreg_d), #endif [lockval] "r" (lock_val), [unlockval] "r" (unlock_val) : "cc"); #ifndef CONFIG_CACHE_L2X0 raw_spin_unlock_irqrestore(&prefetch_lock, flags); #endif return v; } static long update_timeval(struct timespec lhs, struct timespec rhs) { long val; struct timespec ts; ts = timespec_sub(rhs, lhs); val = ts.tv_sec*NSEC_PER_SEC + ts.tv_nsec; return val; } extern void v7_flush_kern_dcache_area(void *, size_t); extern void v7_flush_kern_cache_all(void); /* * Ensure that this page is not in the L1 or L2 cache. * Since the L1 cache is VIPT and the L2 cache is PIPT, we can use either the * kernel or user vaddr. */ void color_flush_page(void *vaddr, size_t size) { v7_flush_kern_dcache_area(vaddr, size); //v7_flush_kern_cache_all(); } extern struct page* get_colored_page(unsigned long color); int setup_flusher_array(void) { int color, way, ret = 0; struct page *page; if (flusher_pages != NULL) goto out; flusher_pages = (void***) kmalloc(MAX_NR_WAYS * sizeof(*flusher_pages), GFP_KERNEL); if (!flusher_pages) { printk(KERN_WARNING "No memory for flusher array!\n"); ret = -EINVAL; goto out; } for (way = 0; way < MAX_NR_WAYS; way++) { void **flusher_color_arr; flusher_color_arr = (void**) kmalloc(sizeof(**flusher_pages) * MAX_NR_COLORS, GFP_KERNEL); if (!flusher_color_arr) { printk(KERN_WARNING "No memory for flusher array!\n"); ret = -ENOMEM; goto out_free; } flusher_pages[way] = flusher_color_arr; for (color = 0; color < MAX_NR_COLORS; color++) { int node; switch (color) { case 0: node = 48; break; case 1: node = 49; break; case 2: node = 50; break; case 3: node = 51; break; case 4: node = 68; break; case 5: node = 69; break; case 6: node = 86; break; case 7: node = 87; break; case 8: node = 88; break; case 9: node = 105; break; case 10: node = 106; break; case 11: node = 107; break; case 12: node = 108; break; case 13: node = 125; break; case 14: node = 126; break; case 15: node = 127; break; } page = get_colored_page(node); if (!page) { printk(KERN_WARNING "no more colored pages\n"); ret = -EINVAL; goto out_free; } flusher_pages[way][color] = page_address(page); if (!flusher_pages[way][color]) { printk(KERN_WARNING "bad page address\n"); ret = -EINVAL; goto out_free; } } } out: return ret; out_free: for (way = 0; way < MAX_NR_WAYS; way++) { for (color = 0; color < MAX_NR_COLORS; color++) { /* not bothering to try and give back colored pages */ } kfree(flusher_pages[way]); } kfree(flusher_pages); flusher_pages = NULL; return ret; } void flush_cache(int all) { int way, color, cpu; unsigned long flags; raw_spin_lock_irqsave(&cache_lock, flags); cpu = raw_smp_processor_id(); prev_lbm_i_reg[cpu] = readl_relaxed(ld_i_reg(cpu)); prev_lbm_d_reg[cpu] = readl_relaxed(ld_d_reg(cpu)); for (way=0;way