aboutsummaryrefslogblamecommitdiffstats
path: root/litmus/cache_proc.c
blob: 01350294afafb10d413019bac1115df95b4a80c9 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13












                                
                               




































































































































                                                               

                         























                                                                                      
                                     


















































































































































































































                                                                                                     


























                                                         


                                                                                                  






                                                        


















                                                                            





































                                                                                       

















































                                                                                                























                                                                         






























































































































































































































































                                                                                                     













                                                             




                                                             




                                                              




















































































































































                                                                                 





































                                                                               
                                                   

                                               
                                                   

































































































                                                                                 




































































































                                                                                                       

















                                                                     





















                                                                               
#include <linux/init.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sysctl.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/mutex.h>
#include <linux/time.h>

#include <litmus/litmus_proc.h>
#include <litmus/sched_trace.h>
#include <litmus/cache_proc.h>
#include <litmus/mc2_common.h>

#include <asm/hardware/cache-l2x0.h>
#include <asm/cacheflush.h>


#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<MAX_NR_WAYS;way++) {
		if (( (0x00000001 << way) & (prev_lbm_d_reg[cpu]) ) &&
			!all)
			continue;
		for (color=0;color<MAX_NR_COLORS;color++) {
			void *vaddr = flusher_pages[way][color];
			u32 lvalue  = unlocked_way[way];
			color_read_in_mem_lock(lvalue, LOCK_ALL,
					       vaddr, vaddr + PAGE_SIZE);
		}

	}

	writel_relaxed(prev_lbm_i_reg[cpu], ld_i_reg(cpu));
	writel_relaxed(prev_lbm_d_reg[cpu], ld_d_reg(cpu));
	raw_spin_unlock_irqrestore(&cache_lock, flags);
}

#define TRIALS 1000

static int perf_test(void) {
	struct timespec before, after;
	struct page *page;
	void *vaddr;
	u32 *data;
	long time, flush_time;
	int i, num_pages = 1;
	unsigned int order = 4;

	for (i = 0; i < order; i++) {
		num_pages = num_pages*2;
	}

	printk("Number of pages: %d\n", num_pages);
	//page = alloc_page(__GFP_MOVABLE);
	page = alloc_pages(__GFP_MOVABLE, order);
	if (!page) {
		printk(KERN_WARNING "No memory\n");
		return -ENOMEM;
	}

	vaddr = page_address(page);
	if (!vaddr)
		printk(KERN_WARNING "%s: vaddr is null\n", __FUNCTION__);
	data = (u32*) vaddr;

	getnstimeofday(&before);
	barrier();
	for (i = 0; i < TRIALS; i++) {
		color_flush_page(vaddr, PAGE_SIZE*num_pages);
	}
	barrier();
	getnstimeofday(&after);
	time = update_timeval(before, after);
	printk("Average for flushes without re-reading: %ld\n", time / TRIALS);
	flush_time = time / TRIALS;

	color_read_in_mem(nr_unlocked_way[2], UNLOCK_ALL, vaddr, vaddr + PAGE_SIZE*num_pages);
	
	barrier();
	getnstimeofday(&before);
	barrier();
	for (i = 0; i < TRIALS; i++) {
		color_read_in_mem(nr_unlocked_way[2], UNLOCK_ALL, vaddr, vaddr + PAGE_SIZE*num_pages);
	}
	barrier();
	getnstimeofday(&after);
	time = update_timeval(before, after);
	printk("Average for read from cache: %ld\n", time / TRIALS);

	getnstimeofday(&before);
	barrier();
	for (i = 0; i < TRIALS; i++) {
		color_read_in_mem(nr_unlocked_way[2], UNLOCK_ALL, vaddr, vaddr + PAGE_SIZE*num_pages);
		color_flush_page(vaddr, PAGE_SIZE*num_pages);
	}
	barrier();
	getnstimeofday(&after);
	time = update_timeval(before, after);
	printk("Average for read from mem: %ld (%ld)\n", time / TRIALS - flush_time, time / TRIALS);

	// write in locked way
	color_read_in_mem_lock(nr_unlocked_way[2], LOCK_ALL, vaddr, vaddr + PAGE_SIZE*num_pages);
	for (i = 0; i < PAGE_SIZE*num_pages/sizeof(u32); i++) {
		data[i] = i%63353;
	}
	// read
	barrier();
	getnstimeofday(&before);
	barrier();
	for (i = 0; i < TRIALS; i++) {
		color_read_in_mem(unlocked_way[0], UNLOCK_ALL, vaddr, vaddr + PAGE_SIZE*num_pages);
	}
	barrier();
	getnstimeofday(&after);
	time = update_timeval(before, after);
	printk("Average for read in after write: %ld\n", time / TRIALS);
	
	
	//free_page((unsigned long)vaddr);
	free_pages((unsigned long)vaddr, order);

	return 0;
}

int do_perf_test_proc_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *lenp, loff_t *ppos)
{
	int ret = 0;

	if (write) {
		ret = perf_test();
	}

	return ret;
}

int setup_flusher_proc_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *lenp, loff_t *ppos)
{
	int ret = -EINVAL;

	if (write && flusher_pages == NULL) {
		ret = setup_flusher_array();
		printk(KERN_INFO "setup flusher return: %d\n", ret);
	
	}
	else if (flusher_pages) {
		printk(KERN_INFO "flusher_pages is already set!\n");
		ret = 0;
	}
	
	return ret;
}

static struct ctl_table_header *litmus_sysctls;

static int __init litmus_sysctl_init(void)
{
	int ret = 0;

	printk(KERN_INFO "Registering LITMUS^RT proc sysctl.\n");
	litmus_sysctls = register_sysctl_table(litmus_dir_table);
	if (!litmus_sysctls) {
		printk(KERN_WARNING "Could not register LITMUS^RT sysctl.\n");
		ret = -EFAULT;
		goto out;
	}

	way_partition_min = 0x00000000;
	way_partition_max = 0x0000FFFF;
	
out:
	return ret;
}

module_init(litmus_sysctl_init);