1101 lines · 30.0 KB · C
RawDownload
/* * Copyright 2010 Tilera Corporation. All Rights Reserved. * *   This program is free software; you can redistribute it and/or *   modify it under the terms of the GNU General Public License *   as published by the Free Software Foundation, version 2. * *   This program is distributed in the hope that it will be useful, but *   WITHOUT ANY WARRANTY; without even the implied warranty of *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or *   NON INFRINGEMENT.  See the GNU General Public License for *   more details. */ #include <linux/fs.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <linux/rwsem.h>#include <linux/kprobes.h>#include <linux/sched.h>#include <linux/hardirq.h>#include <linux/uaccess.h>#include <linux/smp.h>#include <linux/cdev.h>#include <linux/compat.h>#include <asm/hardwall.h>#include <asm/traps.h>#include <asm/siginfo.h>#include <asm/irq_regs.h> #include <arch/interrupts.h>#include <arch/spr_def.h>  /* * Implement a per-cpu "hardwall" resource class such as UDN or IPI. * We use "hardwall" nomenclature throughout for historical reasons. * The lock here controls access to the list data structure as well as * to the items on the list. */struct hardwall_type {	int index;	int is_xdn;	int is_idn;	int disabled;	const char *name;	struct list_head list;	spinlock_t lock;	struct proc_dir_entry *proc_dir;}; enum hardwall_index {	HARDWALL_UDN = 0,#ifndef __tilepro__	HARDWALL_IDN = 1,	HARDWALL_IPI = 2,#endif	_HARDWALL_TYPES}; static struct hardwall_type hardwall_types[] = {	{  /* user-space access to UDN */		0,		1,		0,		0,		"udn",		LIST_HEAD_INIT(hardwall_types[HARDWALL_UDN].list),		__SPIN_LOCK_UNLOCKED(hardwall_types[HARDWALL_UDN].lock),		NULL	},#ifndef __tilepro__	{  /* user-space access to IDN */		1,		1,		1,		1,  /* disabled pending hypervisor support */		"idn",		LIST_HEAD_INIT(hardwall_types[HARDWALL_IDN].list),		__SPIN_LOCK_UNLOCKED(hardwall_types[HARDWALL_IDN].lock),		NULL	},	{  /* access to user-space IPI */		2,		0,		0,		0,		"ipi",		LIST_HEAD_INIT(hardwall_types[HARDWALL_IPI].list),		__SPIN_LOCK_UNLOCKED(hardwall_types[HARDWALL_IPI].lock),		NULL	},#endif}; /* * This data structure tracks the cpu data, etc., associated * one-to-one with a "struct file *" from opening a hardwall device file. * Note that the file's private data points back to this structure. */struct hardwall_info {	struct list_head list;             /* for hardwall_types.list */	struct list_head task_head;        /* head of tasks in this hardwall */	struct hardwall_type *type;        /* type of this resource */	struct cpumask cpumask;            /* cpus reserved */	int id;                            /* integer id for this hardwall */	int teardown_in_progress;          /* are we tearing this one down? */ 	/* Remaining fields only valid for user-network resources. */	int ulhc_x;                        /* upper left hand corner x coord */	int ulhc_y;                        /* upper left hand corner y coord */	int width;                         /* rectangle width */	int height;                        /* rectangle height */#if CHIP_HAS_REV1_XDN()	atomic_t xdn_pending_count;        /* cores in phase 1 of drain */#endif};  /* /proc/tile/hardwall */static struct proc_dir_entry *hardwall_proc_dir; /* Functions to manage files in /proc/tile/hardwall. */static void hardwall_add_proc(struct hardwall_info *);static void hardwall_remove_proc(struct hardwall_info *); /* Allow disabling UDN access. */static int __init noudn(char *str){	pr_info("User-space UDN access is disabled\n");	hardwall_types[HARDWALL_UDN].disabled = 1;	return 0;}early_param("noudn", noudn); #ifndef __tilepro__/* Allow disabling IDN access. */static int __init noidn(char *str){	pr_info("User-space IDN access is disabled\n");	hardwall_types[HARDWALL_IDN].disabled = 1;	return 0;}early_param("noidn", noidn); /* Allow disabling IPI access. */static int __init noipi(char *str){	pr_info("User-space IPI access is disabled\n");	hardwall_types[HARDWALL_IPI].disabled = 1;	return 0;}early_param("noipi", noipi);#endif  /* * Low-level primitives for UDN/IDN */ #ifdef __tilepro__#define mtspr_XDN(hwt, name, val) \	do { (void)(hwt); __insn_mtspr(SPR_UDN_##name, (val)); } while (0)#define mtspr_MPL_XDN(hwt, name, val) \	do { (void)(hwt); __insn_mtspr(SPR_MPL_UDN_##name, (val)); } while (0)#define mfspr_XDN(hwt, name) \	((void)(hwt), __insn_mfspr(SPR_UDN_##name))#else#define mtspr_XDN(hwt, name, val)					\	do {								\		if ((hwt)->is_idn)					\			__insn_mtspr(SPR_IDN_##name, (val));		\		else							\			__insn_mtspr(SPR_UDN_##name, (val));		\	} while (0)#define mtspr_MPL_XDN(hwt, name, val)					\	do {								\		if ((hwt)->is_idn)					\			__insn_mtspr(SPR_MPL_IDN_##name, (val));	\		else							\			__insn_mtspr(SPR_MPL_UDN_##name, (val));	\	} while (0)#define mfspr_XDN(hwt, name) \  ((hwt)->is_idn ? __insn_mfspr(SPR_IDN_##name) : __insn_mfspr(SPR_UDN_##name))#endif /* Set a CPU bit if the CPU is online. */#define cpu_online_set(cpu, dst) do { \	if (cpu_online(cpu))          \		cpumask_set_cpu(cpu, dst);    \} while (0)  /* Does the given rectangle contain the given x,y coordinate? */static int contains(struct hardwall_info *r, int x, int y){	return (x >= r->ulhc_x && x < r->ulhc_x + r->width) &&		(y >= r->ulhc_y && y < r->ulhc_y + r->height);} /* Compute the rectangle parameters and validate the cpumask. */static int check_rectangle(struct hardwall_info *r, struct cpumask *mask){	int x, y, cpu, ulhc, lrhc; 	/* The first cpu is the ULHC, the last the LRHC. */	ulhc = find_first_bit(cpumask_bits(mask), nr_cpumask_bits);	lrhc = find_last_bit(cpumask_bits(mask), nr_cpumask_bits); 	/* Compute the rectangle attributes from the cpus. */	r->ulhc_x = cpu_x(ulhc);	r->ulhc_y = cpu_y(ulhc);	r->width = cpu_x(lrhc) - r->ulhc_x + 1;	r->height = cpu_y(lrhc) - r->ulhc_y + 1; 	/* Width and height must be positive */	if (r->width <= 0 || r->height <= 0)		return -EINVAL; 	/* Confirm that the cpumask is exactly the rectangle. */	for (y = 0, cpu = 0; y < smp_height; ++y)		for (x = 0; x < smp_width; ++x, ++cpu)			if (cpumask_test_cpu(cpu, mask) != contains(r, x, y))				return -EINVAL; 	/*	 * Note that offline cpus can't be drained when this user network	 * rectangle eventually closes.  We used to detect this	 * situation and print a warning, but it annoyed users and	 * they ignored it anyway, so now we just return without a	 * warning.	 */	return 0;} /* * Hardware management of hardwall setup, teardown, trapping, * and enabling/disabling PL0 access to the networks. */ /* Bit field values to mask together for writes to SPR_XDN_DIRECTION_PROTECT */enum direction_protect {	N_PROTECT = (1 << 0),	E_PROTECT = (1 << 1),	S_PROTECT = (1 << 2),	W_PROTECT = (1 << 3),	C_PROTECT = (1 << 4),}; static inline int xdn_which_interrupt(struct hardwall_type *hwt){#ifndef __tilepro__	if (hwt->is_idn)		return INT_IDN_FIREWALL;#endif	return INT_UDN_FIREWALL;} static void enable_firewall_interrupts(struct hardwall_type *hwt){	arch_local_irq_unmask_now(xdn_which_interrupt(hwt));} static void disable_firewall_interrupts(struct hardwall_type *hwt){	arch_local_irq_mask_now(xdn_which_interrupt(hwt));} /* Set up hardwall on this cpu based on the passed hardwall_info. */static void hardwall_setup_func(void *info){	struct hardwall_info *r = info;	struct hardwall_type *hwt = r->type; 	int cpu = smp_processor_id();  /* on_each_cpu disables preemption */	int x = cpu_x(cpu);	int y = cpu_y(cpu);	int bits = 0;	if (x == r->ulhc_x)		bits |= W_PROTECT;	if (x == r->ulhc_x + r->width - 1)		bits |= E_PROTECT;	if (y == r->ulhc_y)		bits |= N_PROTECT;	if (y == r->ulhc_y + r->height - 1)		bits |= S_PROTECT;	BUG_ON(bits == 0);	mtspr_XDN(hwt, DIRECTION_PROTECT, bits);	enable_firewall_interrupts(hwt);} /* Set up all cpus on edge of rectangle to enable/disable hardwall SPRs. */static void hardwall_protect_rectangle(struct hardwall_info *r){	int x, y, cpu, delta;	struct cpumask rect_cpus; 	cpumask_clear(&rect_cpus); 	/* First include the top and bottom edges */	cpu = r->ulhc_y * smp_width + r->ulhc_x;	delta = (r->height - 1) * smp_width;	for (x = 0; x < r->width; ++x, ++cpu) {		cpu_online_set(cpu, &rect_cpus);		cpu_online_set(cpu + delta, &rect_cpus);	} 	/* Then the left and right edges */	cpu -= r->width;	delta = r->width - 1;	for (y = 0; y < r->height; ++y, cpu += smp_width) {		cpu_online_set(cpu, &rect_cpus);		cpu_online_set(cpu + delta, &rect_cpus);	} 	/* Then tell all the cpus to set up their protection SPR */	on_each_cpu_mask(&rect_cpus, hardwall_setup_func, r, 1);} /* Entered from INT_xDN_FIREWALL interrupt vector with irqs disabled. */void __kprobes do_hardwall_trap(struct pt_regs* regs, int fault_num){	struct hardwall_info *rect;	struct hardwall_type *hwt;	struct task_struct *p;	struct siginfo info;	int cpu = smp_processor_id();	int found_processes;	struct pt_regs *old_regs = set_irq_regs(regs); 	irq_enter(); 	/* Figure out which network trapped. */	switch (fault_num) {#ifndef __tilepro__	case INT_IDN_FIREWALL:		hwt = &hardwall_types[HARDWALL_IDN];		break;#endif	case INT_UDN_FIREWALL:		hwt = &hardwall_types[HARDWALL_UDN];		break;	default:		BUG();	}	BUG_ON(hwt->disabled); 	/* This tile trapped a network access; find the rectangle. */	spin_lock(&hwt->lock);	list_for_each_entry(rect, &hwt->list, list) {		if (cpumask_test_cpu(cpu, &rect->cpumask))			break;	} 	/*	 * It shouldn't be possible not to find this cpu on the	 * rectangle list, since only cpus in rectangles get hardwalled.	 * The hardwall is only removed after the user network is drained.	 */	BUG_ON(&rect->list == &hwt->list); 	/*	 * If we already started teardown on this hardwall, don't worry;	 * the abort signal has been sent and we are just waiting for things	 * to quiesce.	 */	if (rect->teardown_in_progress) {		pr_notice("cpu %d: detected %s hardwall violation %#lx"		       " while teardown already in progress\n",			  cpu, hwt->name,			  (long)mfspr_XDN(hwt, DIRECTION_PROTECT));		goto done;	} 	/*	 * Kill off any process that is activated in this rectangle.	 * We bypass security to deliver the signal, since it must be	 * one of the activated processes that generated the user network	 * message that caused this trap, and all the activated	 * processes shared a single open file so are pretty tightly	 * bound together from a security point of view to begin with.	 */	rect->teardown_in_progress = 1;	wmb(); /* Ensure visibility of rectangle before notifying processes. */	pr_notice("cpu %d: detected %s hardwall violation %#lx...\n",		  cpu, hwt->name, (long)mfspr_XDN(hwt, DIRECTION_PROTECT));	info.si_signo = SIGILL;	info.si_errno = 0;	info.si_code = ILL_HARDWALL;	found_processes = 0;	list_for_each_entry(p, &rect->task_head,			    thread.hardwall[hwt->index].list) {		BUG_ON(p->thread.hardwall[hwt->index].info != rect);		if (!(p->flags & PF_EXITING)) {			found_processes = 1;			pr_notice("hardwall: killing %d\n", p->pid);			do_send_sig_info(info.si_signo, &info, p, false);		}	}	if (!found_processes)		pr_notice("hardwall: no associated processes!\n");  done:	spin_unlock(&hwt->lock); 	/*	 * We have to disable firewall interrupts now, or else when we	 * return from this handler, we will simply re-interrupt back to	 * it.  However, we can't clear the protection bits, since we	 * haven't yet drained the network, and that would allow packets	 * to cross out of the hardwall region.	 */	disable_firewall_interrupts(hwt); 	irq_exit();	set_irq_regs(old_regs);} /* Allow access from user space to the user network. */void grant_hardwall_mpls(struct hardwall_type *hwt){#ifndef __tilepro__	if (!hwt->is_xdn) {		__insn_mtspr(SPR_MPL_IPI_0_SET_0, 1);		return;	}#endif	mtspr_MPL_XDN(hwt, ACCESS_SET_0, 1);	mtspr_MPL_XDN(hwt, AVAIL_SET_0, 1);	mtspr_MPL_XDN(hwt, COMPLETE_SET_0, 1);	mtspr_MPL_XDN(hwt, TIMER_SET_0, 1);#if !CHIP_HAS_REV1_XDN()	mtspr_MPL_XDN(hwt, REFILL_SET_0, 1);	mtspr_MPL_XDN(hwt, CA_SET_0, 1);#endif} /* Deny access from user space to the user network. */void restrict_hardwall_mpls(struct hardwall_type *hwt){#ifndef __tilepro__	if (!hwt->is_xdn) {		__insn_mtspr(SPR_MPL_IPI_0_SET_1, 1);		return;	}#endif	mtspr_MPL_XDN(hwt, ACCESS_SET_1, 1);	mtspr_MPL_XDN(hwt, AVAIL_SET_1, 1);	mtspr_MPL_XDN(hwt, COMPLETE_SET_1, 1);	mtspr_MPL_XDN(hwt, TIMER_SET_1, 1);#if !CHIP_HAS_REV1_XDN()	mtspr_MPL_XDN(hwt, REFILL_SET_1, 1);	mtspr_MPL_XDN(hwt, CA_SET_1, 1);#endif} /* Restrict or deny as necessary for the task we're switching to. */void hardwall_switch_tasks(struct task_struct *prev,			   struct task_struct *next){	int i;	for (i = 0; i < HARDWALL_TYPES; ++i) {		if (prev->thread.hardwall[i].info != NULL) {			if (next->thread.hardwall[i].info == NULL)				restrict_hardwall_mpls(&hardwall_types[i]);		} else if (next->thread.hardwall[i].info != NULL) {			grant_hardwall_mpls(&hardwall_types[i]);		}	}} /* Does this task have the right to IPI the given cpu? */int hardwall_ipi_valid(int cpu){#ifdef __tilegx__	struct hardwall_info *info =		current->thread.hardwall[HARDWALL_IPI].info;	return info && cpumask_test_cpu(cpu, &info->cpumask);#else	return 0;#endif} /* * Code to create, activate, deactivate, and destroy hardwall resources. */ /* Create a hardwall for the given resource */static struct hardwall_info *hardwall_create(struct hardwall_type *hwt,					     size_t size,					     const unsigned char __user *bits){	struct hardwall_info *iter, *info;	struct cpumask mask;	unsigned long flags;	int rc; 	/* Reject crazy sizes out of hand, a la sys_mbind(). */	if (size > PAGE_SIZE)		return ERR_PTR(-EINVAL);

Lines 1–500 of 1101.