summary refs log tree commit diff
path: root/drivers/misc/lkdtm_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/lkdtm_core.c')
-rw-r--r--drivers/misc/lkdtm_core.c668
1 files changed, 205 insertions, 463 deletions
diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm_core.c
index 8b90220216e2..de29a339242a 100644
--- a/drivers/misc/lkdtm_core.c
+++ b/drivers/misc/lkdtm_core.c
@@ -53,8 +53,16 @@
 
 #define DEFAULT_COUNT 10
 
-static int lkdtm_parse_commandline(void);
 static void lkdtm_handler(void);
+static int lkdtm_debugfs_open(struct inode *inode, struct file *file);
+static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf,
+		size_t count, loff_t *off);
+static ssize_t direct_entry(struct file *f, const char __user *user_buf,
+			    size_t count, loff_t *off);
+static ssize_t lkdtm_debugfs_entry(struct file *f,
+				   const char __user *user_buf,
+				   size_t count, loff_t *off);
+
 
 /* jprobe entry point handlers. */
 static unsigned int jp_do_irq(unsigned int irq)
@@ -121,118 +129,114 @@ static int jp_generic_ide_ioctl(ide_drive_t *drive, struct file *file,
 }
 #endif
 
-enum cname {
-	CN_INVALID,
-	CN_INT_HARDWARE_ENTRY,
-	CN_INT_HW_IRQ_EN,
-	CN_INT_TASKLET_ENTRY,
-	CN_FS_DEVRW,
-	CN_MEM_SWAPOUT,
-	CN_TIMERADD,
-	CN_SCSI_DISPATCH_CMD,
-	CN_IDE_CORE_CP,
-	CN_DIRECT,
+
+/* Crash points */
+struct crashpoint {
+	const char *name;
+	const struct file_operations fops;
+	struct jprobe jprobe;
 };
 
-enum ctype {
-	CT_NONE,
-	CT_PANIC,
-	CT_BUG,
-	CT_WARNING,
-	CT_EXCEPTION,
-	CT_LOOP,
-	CT_OVERFLOW,
-	CT_CORRUPT_STACK,
-	CT_UNALIGNED_LOAD_STORE_WRITE,
-	CT_OVERWRITE_ALLOCATION,
-	CT_WRITE_AFTER_FREE,
-	CT_READ_AFTER_FREE,
-	CT_WRITE_BUDDY_AFTER_FREE,
-	CT_READ_BUDDY_AFTER_FREE,
-	CT_SOFTLOCKUP,
-	CT_HARDLOCKUP,
-	CT_SPINLOCKUP,
-	CT_HUNG_TASK,
-	CT_EXEC_DATA,
-	CT_EXEC_STACK,
-	CT_EXEC_KMALLOC,
-	CT_EXEC_VMALLOC,
-	CT_EXEC_RODATA,
-	CT_EXEC_USERSPACE,
-	CT_ACCESS_USERSPACE,
-	CT_WRITE_RO,
-	CT_WRITE_RO_AFTER_INIT,
-	CT_WRITE_KERN,
-	CT_ATOMIC_UNDERFLOW,
-	CT_ATOMIC_OVERFLOW,
-	CT_USERCOPY_HEAP_SIZE_TO,
-	CT_USERCOPY_HEAP_SIZE_FROM,
-	CT_USERCOPY_HEAP_FLAG_TO,
-	CT_USERCOPY_HEAP_FLAG_FROM,
-	CT_USERCOPY_STACK_FRAME_TO,
-	CT_USERCOPY_STACK_FRAME_FROM,
-	CT_USERCOPY_STACK_BEYOND,
-	CT_USERCOPY_KERNEL,
+#define CRASHPOINT(_name, _write, _symbol, _entry)		\
+	{							\
+		.name = _name,					\
+		.fops = {					\
+			.read	= lkdtm_debugfs_read,		\
+			.llseek	= generic_file_llseek,		\
+			.open	= lkdtm_debugfs_open,		\
+			.write	= _write,			\
+		},						\
+		.jprobe = {					\
+			.kp.symbol_name = _symbol,		\
+			.entry = (kprobe_opcode_t *)_entry,	\
+		},						\
+	}
+
+/* Define the possible places where we can trigger a crash point. */
+struct crashpoint crashpoints[] = {
+	CRASHPOINT("DIRECT",			direct_entry,
+		   NULL,			NULL),
+#ifdef CONFIG_KPROBES
+	CRASHPOINT("INT_HARDWARE_ENTRY",	lkdtm_debugfs_entry,
+		   "do_IRQ",			jp_do_irq),
+	CRASHPOINT("INT_HW_IRQ_EN",		lkdtm_debugfs_entry,
+		   "handle_IRQ_event",		jp_handle_irq_event),
+	CRASHPOINT("INT_TASKLET_ENTRY",		lkdtm_debugfs_entry,
+		   "tasklet_action",		jp_tasklet_action),
+	CRASHPOINT("FS_DEVRW",			lkdtm_debugfs_entry,
+		   "ll_rw_block",		jp_ll_rw_block),
+	CRASHPOINT("MEM_SWAPOUT",		lkdtm_debugfs_entry,
+		   "shrink_inactive_list",	jp_shrink_inactive_list),
+	CRASHPOINT("TIMERADD",			lkdtm_debugfs_entry,
+		   "hrtimer_start",		jp_hrtimer_start),
+	CRASHPOINT("SCSI_DISPATCH_CMD",		lkdtm_debugfs_entry,
+		   "scsi_dispatch_cmd",		jp_scsi_dispatch_cmd),
+# ifdef CONFIG_IDE
+	CRASHPOINT("IDE_CORE_CP",		lkdtm_debugfs_entry,
+		   "generic_ide_ioctl",		jp_generic_ide_ioctl),
+# endif
+#endif
 };
 
-static char* cp_name[] = {
-	"INVALID",
-	"INT_HARDWARE_ENTRY",
-	"INT_HW_IRQ_EN",
-	"INT_TASKLET_ENTRY",
-	"FS_DEVRW",
-	"MEM_SWAPOUT",
-	"TIMERADD",
-	"SCSI_DISPATCH_CMD",
-	"IDE_CORE_CP",
-	"DIRECT",
+
+/* Crash types. */
+struct crashtype {
+	const char *name;
+	void (*func)(void);
 };
 
-static char* cp_type[] = {
-	"NONE",
-	"PANIC",
-	"BUG",
-	"WARNING",
-	"EXCEPTION",
-	"LOOP",
-	"OVERFLOW",
-	"CORRUPT_STACK",
-	"UNALIGNED_LOAD_STORE_WRITE",
-	"OVERWRITE_ALLOCATION",
-	"WRITE_AFTER_FREE",
-	"READ_AFTER_FREE",
-	"WRITE_BUDDY_AFTER_FREE",
-	"READ_BUDDY_AFTER_FREE",
-	"SOFTLOCKUP",
-	"HARDLOCKUP",
-	"SPINLOCKUP",
-	"HUNG_TASK",
-	"EXEC_DATA",
-	"EXEC_STACK",
-	"EXEC_KMALLOC",
-	"EXEC_VMALLOC",
-	"EXEC_RODATA",
-	"EXEC_USERSPACE",
-	"ACCESS_USERSPACE",
-	"WRITE_RO",
-	"WRITE_RO_AFTER_INIT",
-	"WRITE_KERN",
-	"ATOMIC_UNDERFLOW",
-	"ATOMIC_OVERFLOW",
-	"USERCOPY_HEAP_SIZE_TO",
-	"USERCOPY_HEAP_SIZE_FROM",
-	"USERCOPY_HEAP_FLAG_TO",
-	"USERCOPY_HEAP_FLAG_FROM",
-	"USERCOPY_STACK_FRAME_TO",
-	"USERCOPY_STACK_FRAME_FROM",
-	"USERCOPY_STACK_BEYOND",
-	"USERCOPY_KERNEL",
+#define CRASHTYPE(_name)			\
+	{					\
+		.name = __stringify(_name),	\
+		.func = lkdtm_ ## _name,	\
+	}
+
+/* Define the possible types of crashes that can be triggered. */
+struct crashtype crashtypes[] = {
+	CRASHTYPE(PANIC),
+	CRASHTYPE(BUG),
+	CRASHTYPE(WARNING),
+	CRASHTYPE(EXCEPTION),
+	CRASHTYPE(LOOP),
+	CRASHTYPE(OVERFLOW),
+	CRASHTYPE(CORRUPT_STACK),
+	CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE),
+	CRASHTYPE(OVERWRITE_ALLOCATION),
+	CRASHTYPE(WRITE_AFTER_FREE),
+	CRASHTYPE(READ_AFTER_FREE),
+	CRASHTYPE(WRITE_BUDDY_AFTER_FREE),
+	CRASHTYPE(READ_BUDDY_AFTER_FREE),
+	CRASHTYPE(SOFTLOCKUP),
+	CRASHTYPE(HARDLOCKUP),
+	CRASHTYPE(SPINLOCKUP),
+	CRASHTYPE(HUNG_TASK),
+	CRASHTYPE(EXEC_DATA),
+	CRASHTYPE(EXEC_STACK),
+	CRASHTYPE(EXEC_KMALLOC),
+	CRASHTYPE(EXEC_VMALLOC),
+	CRASHTYPE(EXEC_RODATA),
+	CRASHTYPE(EXEC_USERSPACE),
+	CRASHTYPE(ACCESS_USERSPACE),
+	CRASHTYPE(WRITE_RO),
+	CRASHTYPE(WRITE_RO_AFTER_INIT),
+	CRASHTYPE(WRITE_KERN),
+	CRASHTYPE(ATOMIC_UNDERFLOW),
+	CRASHTYPE(ATOMIC_OVERFLOW),
+	CRASHTYPE(USERCOPY_HEAP_SIZE_TO),
+	CRASHTYPE(USERCOPY_HEAP_SIZE_FROM),
+	CRASHTYPE(USERCOPY_HEAP_FLAG_TO),
+	CRASHTYPE(USERCOPY_HEAP_FLAG_FROM),
+	CRASHTYPE(USERCOPY_STACK_FRAME_TO),
+	CRASHTYPE(USERCOPY_STACK_FRAME_FROM),
+	CRASHTYPE(USERCOPY_STACK_BEYOND),
+	CRASHTYPE(USERCOPY_KERNEL),
 };
 
+
 /* Global jprobe entry and crashtype. */
-static struct jprobe lkdtm_jprobe;
-static enum cname lkdtm_crashpoint = CN_INVALID;
-static enum ctype lkdtm_crashtype = CT_NONE;
+static struct jprobe *lkdtm_jprobe;
+struct crashpoint *lkdtm_crashpoint;
+struct crashtype *lkdtm_crashtype;
 
 /* Global crash counter and spinlock. */
 static int crash_count = DEFAULT_COUNT;
@@ -257,206 +261,42 @@ module_param(cpoint_count, int, 0644);
 MODULE_PARM_DESC(cpoint_count, " Crash Point Count, number of times the "\
 				"crash point is to be hit to trigger action");
 
-/* Return the crashpoint number or NONE if the name is invalid */
-static enum ctype parse_cp_type(const char *what, size_t count)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(cp_type); i++) {
-		if (!strcmp(what, cp_type[i]))
-			return i;
-	}
-
-	return CT_NONE;
-}
-
-static const char *cp_type_to_str(enum ctype type)
-{
-	if (type == CT_NONE || type < 0 || type > ARRAY_SIZE(cp_type))
-		return "NONE";
-
-	return cp_type[type];
-}
-
-static const char *cp_name_to_str(enum cname name)
-{
-	if (name == CN_INVALID || name < 0 || name > ARRAY_SIZE(cp_name))
-		return "INVALID";
-
-	return cp_name[name];
-}
-
 
-static int lkdtm_parse_commandline(void)
+/* Return the crashtype number or NULL if the name is invalid */
+static struct crashtype *find_crashtype(const char *name)
 {
 	int i;
-	unsigned long flags;
-
-	if (cpoint_count < 1 || recur_count < 1)
-		return -EINVAL;
-
-	spin_lock_irqsave(&crash_count_lock, flags);
-	crash_count = cpoint_count;
-	spin_unlock_irqrestore(&crash_count_lock, flags);
 
-	/* No special parameters */
-	if (!cpoint_type && !cpoint_name)
-		return 0;
-
-	/* Neither or both of these need to be set */
-	if (!cpoint_type || !cpoint_name)
-		return -EINVAL;
-
-	lkdtm_crashtype = parse_cp_type(cpoint_type, strlen(cpoint_type));
-	if (lkdtm_crashtype == CT_NONE)
-		return -EINVAL;
-
-	/* Refuse INVALID as a selectable crashpoint name. */
-	if (!strcmp(cpoint_name, "INVALID"))
-		return -EINVAL;
-
-	for (i = 0; i < ARRAY_SIZE(cp_name); i++) {
-		if (!strcmp(cpoint_name, cp_name[i])) {
-			lkdtm_crashpoint = i;
-			return 0;
-		}
+	for (i = 0; i < ARRAY_SIZE(crashtypes); i++) {
+		if (!strcmp(name, crashtypes[i].name))
+			return &crashtypes[i];
 	}
 
-	/* Could not find a valid crash point */
-	return -EINVAL;
+	return NULL;
 }
 
-static void lkdtm_do_action(enum ctype which)
+/*
+ * This is forced noinline just so it distinctly shows up in the stackdump
+ * which makes validation of expected lkdtm crashes easier.
+ */
+static noinline void lkdtm_do_action(struct crashtype *crashtype)
 {
-	switch (which) {
-	case CT_PANIC:
-		lkdtm_PANIC();
-		break;
-	case CT_BUG:
-		lkdtm_BUG();
-		break;
-	case CT_WARNING:
-		lkdtm_WARNING();
-		break;
-	case CT_EXCEPTION:
-		lkdtm_EXCEPTION();
-		break;
-	case CT_LOOP:
-		lkdtm_LOOP();
-		break;
-	case CT_OVERFLOW:
-		lkdtm_OVERFLOW();
-		break;
-	case CT_CORRUPT_STACK:
-		lkdtm_CORRUPT_STACK();
-		break;
-	case CT_UNALIGNED_LOAD_STORE_WRITE:
-		lkdtm_UNALIGNED_LOAD_STORE_WRITE();
-		break;
-	case CT_OVERWRITE_ALLOCATION:
-		lkdtm_OVERWRITE_ALLOCATION();
-		break;
-	case CT_WRITE_AFTER_FREE:
-		lkdtm_WRITE_AFTER_FREE();
-		break;
-	case CT_READ_AFTER_FREE:
-		lkdtm_READ_AFTER_FREE();
-		break;
-	case CT_WRITE_BUDDY_AFTER_FREE:
-		lkdtm_WRITE_BUDDY_AFTER_FREE();
-		break;
-	case CT_READ_BUDDY_AFTER_FREE:
-		lkdtm_READ_BUDDY_AFTER_FREE();
-		break;
-	case CT_SOFTLOCKUP:
-		lkdtm_SOFTLOCKUP();
-		break;
-	case CT_HARDLOCKUP:
-		lkdtm_HARDLOCKUP();
-		break;
-	case CT_SPINLOCKUP:
-		lkdtm_SPINLOCKUP();
-		break;
-	case CT_HUNG_TASK:
-		lkdtm_HUNG_TASK();
-		break;
-	case CT_EXEC_DATA:
-		lkdtm_EXEC_DATA();
-		break;
-	case CT_EXEC_STACK:
-		lkdtm_EXEC_STACK();
-		break;
-	case CT_EXEC_KMALLOC:
-		lkdtm_EXEC_KMALLOC();
-		break;
-	case CT_EXEC_VMALLOC:
-		lkdtm_EXEC_VMALLOC();
-		break;
-	case CT_EXEC_RODATA:
-		lkdtm_EXEC_RODATA();
-		break;
-	case CT_EXEC_USERSPACE:
-		lkdtm_EXEC_USERSPACE();
-		break;
-	case CT_ACCESS_USERSPACE:
-		lkdtm_ACCESS_USERSPACE();
-		break;
-	case CT_WRITE_RO:
-		lkdtm_WRITE_RO();
-		break;
-	case CT_WRITE_RO_AFTER_INIT:
-		lkdtm_WRITE_RO_AFTER_INIT();
-		break;
-	case CT_WRITE_KERN:
-		lkdtm_WRITE_KERN();
-		break;
-	case CT_ATOMIC_UNDERFLOW:
-		lkdtm_ATOMIC_UNDERFLOW();
-		break;
-	case CT_ATOMIC_OVERFLOW:
-		lkdtm_ATOMIC_OVERFLOW();
-		break;
-	case CT_USERCOPY_HEAP_SIZE_TO:
-		lkdtm_USERCOPY_HEAP_SIZE_TO();
-		break;
-	case CT_USERCOPY_HEAP_SIZE_FROM:
-		lkdtm_USERCOPY_HEAP_SIZE_FROM();
-		break;
-	case CT_USERCOPY_HEAP_FLAG_TO:
-		lkdtm_USERCOPY_HEAP_FLAG_TO();
-		break;
-	case CT_USERCOPY_HEAP_FLAG_FROM:
-		lkdtm_USERCOPY_HEAP_FLAG_FROM();
-		break;
-	case CT_USERCOPY_STACK_FRAME_TO:
-		lkdtm_USERCOPY_STACK_FRAME_TO();
-		break;
-	case CT_USERCOPY_STACK_FRAME_FROM:
-		lkdtm_USERCOPY_STACK_FRAME_FROM();
-		break;
-	case CT_USERCOPY_STACK_BEYOND:
-		lkdtm_USERCOPY_STACK_BEYOND();
-		break;
-	case CT_USERCOPY_KERNEL:
-		lkdtm_USERCOPY_KERNEL();
-		break;
-	case CT_NONE:
-	default:
-		break;
-	}
-
+	BUG_ON(!crashtype || !crashtype->func);
+	crashtype->func();
 }
 
+/* Called by jprobe entry points. */
 static void lkdtm_handler(void)
 {
 	unsigned long flags;
 	bool do_it = false;
 
+	BUG_ON(!lkdtm_crashpoint || !lkdtm_crashtype);
+
 	spin_lock_irqsave(&crash_count_lock, flags);
 	crash_count--;
 	pr_info("Crash point %s of type %s hit, trigger in %d rounds\n",
-		cp_name_to_str(lkdtm_crashpoint),
-		cp_type_to_str(lkdtm_crashtype), crash_count);
+		lkdtm_crashpoint->name, lkdtm_crashtype->name, crash_count);
 
 	if (crash_count == 0) {
 		do_it = true;
@@ -468,72 +308,41 @@ static void lkdtm_handler(void)
 		lkdtm_do_action(lkdtm_crashtype);
 }
 
-static int lkdtm_register_cpoint(enum cname which)
+static int lkdtm_register_cpoint(struct crashpoint *crashpoint,
+				 struct crashtype *crashtype)
 {
 	int ret;
 
-	lkdtm_crashpoint = CN_INVALID;
-	if (lkdtm_jprobe.entry != NULL)
-		unregister_jprobe(&lkdtm_jprobe);
-
-	switch (which) {
-	case CN_DIRECT:
-		lkdtm_do_action(lkdtm_crashtype);
+	/* If this doesn't have a symbol, just call immediately. */
+	if (!crashpoint->jprobe.kp.symbol_name) {
+		lkdtm_do_action(crashtype);
 		return 0;
-	case CN_INT_HARDWARE_ENTRY:
-		lkdtm_jprobe.kp.symbol_name = "do_IRQ";
-		lkdtm_jprobe.entry = (kprobe_opcode_t*) jp_do_irq;
-		break;
-	case CN_INT_HW_IRQ_EN:
-		lkdtm_jprobe.kp.symbol_name = "handle_IRQ_event";
-		lkdtm_jprobe.entry = (kprobe_opcode_t*) jp_handle_irq_event;
-		break;
-	case CN_INT_TASKLET_ENTRY:
-		lkdtm_jprobe.kp.symbol_name = "tasklet_action";
-		lkdtm_jprobe.entry = (kprobe_opcode_t*) jp_tasklet_action;
-		break;
-	case CN_FS_DEVRW:
-		lkdtm_jprobe.kp.symbol_name = "ll_rw_block";
-		lkdtm_jprobe.entry = (kprobe_opcode_t*) jp_ll_rw_block;
-		break;
-	case CN_MEM_SWAPOUT:
-		lkdtm_jprobe.kp.symbol_name = "shrink_inactive_list";
-		lkdtm_jprobe.entry = (kprobe_opcode_t*) jp_shrink_inactive_list;
-		break;
-	case CN_TIMERADD:
-		lkdtm_jprobe.kp.symbol_name = "hrtimer_start";
-		lkdtm_jprobe.entry = (kprobe_opcode_t*) jp_hrtimer_start;
-		break;
-	case CN_SCSI_DISPATCH_CMD:
-		lkdtm_jprobe.kp.symbol_name = "scsi_dispatch_cmd";
-		lkdtm_jprobe.entry = (kprobe_opcode_t*) jp_scsi_dispatch_cmd;
-		break;
-	case CN_IDE_CORE_CP:
-#ifdef CONFIG_IDE
-		lkdtm_jprobe.kp.symbol_name = "generic_ide_ioctl";
-		lkdtm_jprobe.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl;
-#else
-		pr_info("Crash point not available\n");
-		return -EINVAL;
-#endif
-		break;
-	default:
-		pr_info("Invalid Crash Point\n");
-		return -EINVAL;
 	}
 
-	lkdtm_crashpoint = which;
-	if ((ret = register_jprobe(&lkdtm_jprobe)) < 0) {
-		pr_info("Couldn't register jprobe\n");
-		lkdtm_crashpoint = CN_INVALID;
+	if (lkdtm_jprobe != NULL)
+		unregister_jprobe(lkdtm_jprobe);
+
+	lkdtm_crashpoint = crashpoint;
+	lkdtm_crashtype = crashtype;
+	lkdtm_jprobe = &crashpoint->jprobe;
+	ret = register_jprobe(lkdtm_jprobe);
+	if (ret < 0) {
+		pr_info("Couldn't register jprobe %s\n",
+			crashpoint->jprobe.kp.symbol_name);
+		lkdtm_jprobe = NULL;
+		lkdtm_crashpoint = NULL;
+		lkdtm_crashtype = NULL;
 	}
 
 	return ret;
 }
 
-static ssize_t do_register_entry(enum cname which, struct file *f,
-		const char __user *user_buf, size_t count, loff_t *off)
+static ssize_t lkdtm_debugfs_entry(struct file *f,
+				   const char __user *user_buf,
+				   size_t count, loff_t *off)
 {
+	struct crashpoint *crashpoint = file_inode(f)->i_private;
+	struct crashtype *crashtype = NULL;
 	char *buf;
 	int err;
 
@@ -551,13 +360,13 @@ static ssize_t do_register_entry(enum cname which, struct file *f,
 	buf[count] = '\0';
 	strim(buf);
 
-	lkdtm_crashtype = parse_cp_type(buf, count);
-	free_page((unsigned long) buf);
+	crashtype = find_crashtype(buf);
+	free_page((unsigned long)buf);
 
-	if (lkdtm_crashtype == CT_NONE)
+	if (!crashtype)
 		return -EINVAL;
 
-	err = lkdtm_register_cpoint(which);
+	err = lkdtm_register_cpoint(crashpoint, crashtype);
 	if (err < 0)
 		return err;
 
@@ -578,8 +387,10 @@ static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf,
 		return -ENOMEM;
 
 	n = snprintf(buf, PAGE_SIZE, "Available crash types:\n");
-	for (i = 0; i < ARRAY_SIZE(cp_type); i++)
-		n += snprintf(buf + n, PAGE_SIZE - n, "%s\n", cp_type[i]);
+	for (i = 0; i < ARRAY_SIZE(crashtypes); i++) {
+		n += snprintf(buf + n, PAGE_SIZE - n, "%s\n",
+			      crashtypes[i].name);
+	}
 	buf[n] = '\0';
 
 	out = simple_read_from_buffer(user_buf, count, off,
@@ -594,60 +405,11 @@ static int lkdtm_debugfs_open(struct inode *inode, struct file *file)
 	return 0;
 }
 
-
-static ssize_t int_hardware_entry(struct file *f, const char __user *buf,
-		size_t count, loff_t *off)
-{
-	return do_register_entry(CN_INT_HARDWARE_ENTRY, f, buf, count, off);
-}
-
-static ssize_t int_hw_irq_en(struct file *f, const char __user *buf,
-		size_t count, loff_t *off)
-{
-	return do_register_entry(CN_INT_HW_IRQ_EN, f, buf, count, off);
-}
-
-static ssize_t int_tasklet_entry(struct file *f, const char __user *buf,
-		size_t count, loff_t *off)
-{
-	return do_register_entry(CN_INT_TASKLET_ENTRY, f, buf, count, off);
-}
-
-static ssize_t fs_devrw_entry(struct file *f, const char __user *buf,
-		size_t count, loff_t *off)
-{
-	return do_register_entry(CN_FS_DEVRW, f, buf, count, off);
-}
-
-static ssize_t mem_swapout_entry(struct file *f, const char __user *buf,
-		size_t count, loff_t *off)
-{
-	return do_register_entry(CN_MEM_SWAPOUT, f, buf, count, off);
-}
-
-static ssize_t timeradd_entry(struct file *f, const char __user *buf,
-		size_t count, loff_t *off)
-{
-	return do_register_entry(CN_TIMERADD, f, buf, count, off);
-}
-
-static ssize_t scsi_dispatch_cmd_entry(struct file *f,
-		const char __user *buf, size_t count, loff_t *off)
-{
-	return do_register_entry(CN_SCSI_DISPATCH_CMD, f, buf, count, off);
-}
-
-static ssize_t ide_core_cp_entry(struct file *f, const char __user *buf,
-		size_t count, loff_t *off)
-{
-	return do_register_entry(CN_IDE_CORE_CP, f, buf, count, off);
-}
-
 /* Special entry to just crash directly. Available without KPROBEs */
 static ssize_t direct_entry(struct file *f, const char __user *user_buf,
 		size_t count, loff_t *off)
 {
-	enum ctype type;
+	struct crashtype *crashtype;
 	char *buf;
 
 	if (count >= PAGE_SIZE)
@@ -666,70 +428,57 @@ static ssize_t direct_entry(struct file *f, const char __user *user_buf,
 	buf[count] = '\0';
 	strim(buf);
 
-	type = parse_cp_type(buf, count);
+	crashtype = find_crashtype(buf);
 	free_page((unsigned long) buf);
-	if (type == CT_NONE)
+	if (!crashtype)
 		return -EINVAL;
 
-	pr_info("Performing direct entry %s\n", cp_type_to_str(type));
-	lkdtm_do_action(type);
+	pr_info("Performing direct entry %s\n", crashtype->name);
+	lkdtm_do_action(crashtype);
 	*off += count;
 
 	return count;
 }
 
-struct crash_entry {
-	const char *name;
-	const struct file_operations fops;
-};
-
-static const struct crash_entry crash_entries[] = {
-	{"DIRECT", {.read = lkdtm_debugfs_read,
-			.llseek = generic_file_llseek,
-			.open = lkdtm_debugfs_open,
-			.write = direct_entry} },
-	{"INT_HARDWARE_ENTRY", {.read = lkdtm_debugfs_read,
-			.llseek = generic_file_llseek,
-			.open = lkdtm_debugfs_open,
-			.write = int_hardware_entry} },
-	{"INT_HW_IRQ_EN", {.read = lkdtm_debugfs_read,
-			.llseek = generic_file_llseek,
-			.open = lkdtm_debugfs_open,
-			.write = int_hw_irq_en} },
-	{"INT_TASKLET_ENTRY", {.read = lkdtm_debugfs_read,
-			.llseek = generic_file_llseek,
-			.open = lkdtm_debugfs_open,
-			.write = int_tasklet_entry} },
-	{"FS_DEVRW", {.read = lkdtm_debugfs_read,
-			.llseek = generic_file_llseek,
-			.open = lkdtm_debugfs_open,
-			.write = fs_devrw_entry} },
-	{"MEM_SWAPOUT", {.read = lkdtm_debugfs_read,
-			.llseek = generic_file_llseek,
-			.open = lkdtm_debugfs_open,
-			.write = mem_swapout_entry} },
-	{"TIMERADD", {.read = lkdtm_debugfs_read,
-			.llseek = generic_file_llseek,
-			.open = lkdtm_debugfs_open,
-			.write = timeradd_entry} },
-	{"SCSI_DISPATCH_CMD", {.read = lkdtm_debugfs_read,
-			.llseek = generic_file_llseek,
-			.open = lkdtm_debugfs_open,
-			.write = scsi_dispatch_cmd_entry} },
-	{"IDE_CORE_CP",	{.read = lkdtm_debugfs_read,
-			.llseek = generic_file_llseek,
-			.open = lkdtm_debugfs_open,
-			.write = ide_core_cp_entry} },
-};
-
 static struct dentry *lkdtm_debugfs_root;
 
 static int __init lkdtm_module_init(void)
 {
+	struct crashpoint *crashpoint = NULL;
+	struct crashtype *crashtype = NULL;
 	int ret = -EINVAL;
-	int n_debugfs_entries = 1; /* Assume only the direct entry */
 	int i;
 
+	/* Neither or both of these need to be set */
+	if ((cpoint_type || cpoint_name) && !(cpoint_type && cpoint_name)) {
+		pr_err("Need both cpoint_type and cpoint_name or neither\n");
+		return -EINVAL;
+	}
+
+	if (cpoint_type) {
+		crashtype = find_crashtype(cpoint_type);
+		if (!crashtype) {
+			pr_err("Unknown crashtype '%s'\n", cpoint_type);
+			return -EINVAL;
+		}
+	}
+
+	if (cpoint_name) {
+		for (i = 0; i < ARRAY_SIZE(crashpoints); i++) {
+			if (!strcmp(cpoint_name, crashpoints[i].name))
+				crashpoint = &crashpoints[i];
+		}
+
+		/* Refuse unknown crashpoints. */
+		if (!crashpoint) {
+			pr_err("Invalid crashpoint %s\n", cpoint_name);
+			return -EINVAL;
+		}
+	}
+
+	/* Set crash count. */
+	crash_count = cpoint_count;
+
 	/* Handle test-specific initialization. */
 	lkdtm_bugs_init(&recur_count);
 	lkdtm_perms_init();
@@ -742,35 +491,28 @@ static int __init lkdtm_module_init(void)
 		return -ENODEV;
 	}
 
-#ifdef CONFIG_KPROBES
-	n_debugfs_entries = ARRAY_SIZE(crash_entries);
-#endif
-
-	for (i = 0; i < n_debugfs_entries; i++) {
-		const struct crash_entry *cur = &crash_entries[i];
+	/* Install debugfs trigger files. */
+	for (i = 0; i < ARRAY_SIZE(crashpoints); i++) {
+		struct crashpoint *cur = &crashpoints[i];
 		struct dentry *de;
 
 		de = debugfs_create_file(cur->name, 0644, lkdtm_debugfs_root,
-				NULL, &cur->fops);
+					 cur, &cur->fops);
 		if (de == NULL) {
-			pr_err("could not create %s\n", cur->name);
+			pr_err("could not create crashpoint %s\n", cur->name);
 			goto out_err;
 		}
 	}
 
-	if (lkdtm_parse_commandline() == -EINVAL) {
-		pr_info("Invalid command\n");
-		goto out_err;
-	}
-
-	if (lkdtm_crashpoint != CN_INVALID && lkdtm_crashtype != CT_NONE) {
-		ret = lkdtm_register_cpoint(lkdtm_crashpoint);
+	/* Install crashpoint if one was selected. */
+	if (crashpoint) {
+		ret = lkdtm_register_cpoint(crashpoint, crashtype);
 		if (ret < 0) {
-			pr_info("Invalid crash point %d\n", lkdtm_crashpoint);
+			pr_info("Invalid crashpoint %s\n", crashpoint->name);
 			goto out_err;
 		}
 		pr_info("Crash point %s of type %s registered\n",
-			cpoint_name, cpoint_type);
+			crashpoint->name, cpoint_type);
 	} else {
 		pr_info("No crash points registered, enable through debugfs\n");
 	}
@@ -789,7 +531,7 @@ static void __exit lkdtm_module_exit(void)
 	/* Handle test-specific clean-up. */
 	lkdtm_usercopy_exit();
 
-	unregister_jprobe(&lkdtm_jprobe);
+	unregister_jprobe(lkdtm_jprobe);
 	pr_info("Crash point unregistered\n");
 }
 
@@ -797,4 +539,4 @@ module_init(lkdtm_module_init);
 module_exit(lkdtm_module_exit);
 
 MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Kprobe module for testing crash dumps");
+MODULE_DESCRIPTION("Kernel crash testing module");