/* * Noisy Interface for Linux * * This driver allows arbitrary printk's to be inserted into * executing kernel code by using the new kprobes interface. * A message is attached to an address, and when that address * is reached, the message is printed. * * This uses a sysfs control file to manage a list of probes. * The sysfs directory is at * * /sys/noisy/ * * and the control is named 'ctl'. * * A Noisy Probe can be added by echoing into the file, like: * * $ echo "add
" > /sys/noisy/ctl * * where
is the address to break on, and * is the message to print when the address is reached. * * Probes can be removed by doing: * * $ echo "del
" > /sys/noisy/ctl * * where
is the address of the probe. * * The probes themselves get a directory under /sys/noisy/, and * the name of the directory is the address of the probe. Each * probe directory contains one file ('message') that contains * the message to be printed. (More may be added later). * * Copyright (C) 2002 Rusty Lynch */ #include #include #include #include #include /* exported by arch/YOURARCH/kernel/traps.c */ extern int valid_kernel_address(unsigned long); #define MAX_MSG_SIZE 128 #define to_nprobe(entry) container_of(entry,struct nprobe,kobj.entry); static DECLARE_MUTEX(noisy_sem); static struct subsystem noisy_subsys; /* * struct nrpobe: data structure for managing list of probe points */ struct nprobe { struct kprobe probe; char message[MAX_MSG_SIZE + 1]; struct kobject kobj; }; /* * Probe handlers. * Only one is used (pre) to print the message out. */ static void noisy_pre_handler(struct kprobe *p, struct pt_regs *r) { struct nprobe *c = container_of(p, struct nprobe, probe); printk(KERN_CRIT "%s: %s\n", __FUNCTION__, c->message); } static void noisy_post_handler(struct kprobe *p, struct pt_regs *r, unsigned long flags) { } static int noisy_fault_handler(struct kprobe *p, struct pt_regs *r, int trapnr) { /* Let the kernel handle this fault */ return 0; } /* * struct noisy_attribute - used for defining probe attributes, with a * typesafe show method. */ struct noisy_attribute { struct attribute attr; ssize_t (*show)(struct nprobe *,char *,size_t,loff_t); }; /** * noisy_attr_show - forward sysfs read call to proper handler. * @kobj: kobject of probe being acessed. * @attr: generic attribute portion of struct noisy_attribute. * @page: buffer to write into. * @count: number of bytes requested. * @off: offset into buffer. * * This is called from sysfs and is necessary to convert the generic * kobject into the right type, and to convert the attribute into the * right attribute type. */ static ssize_t noisy_attr_show(struct kobject * kobj, struct attribute * attr, char * page, size_t count, loff_t off) { struct nprobe * n = container_of(kobj,struct nprobe,kobj); struct noisy_attribute * noisy_attr = container_of(attr,struct noisy_attribute,attr); return noisy_attr->show ? noisy_attr->show(n,page,count,off) : 0; } /* * noisy_sysfs_ops - sysfs operations for struct nprobes. */ static struct sysfs_ops noisy_sysfs_ops = { .show = noisy_attr_show, }; /* Default Attribute - the message to print out. */ static ssize_t noisy_message_read(struct nprobe * n, char * page, size_t count, loff_t off) { return off ? 0: snprintf(page,MAX_MSG_SIZE,"%s\n",n->message); } static struct noisy_attribute attr_message = { .attr = { .name = "message", .mode = S_IRUGO }, .show = noisy_message_read, }; /* Declare array of default attributes to be added when an nprobe is added */ static struct attribute * default_attrs[] = { &attr_message.attr, NULL, }; /* Declare noisy_subsys for addition to sysfs */ static struct subsystem noisy_subsys = { .kobj = { .name = "noisy" }, .default_attrs = default_attrs, .sysfs_ops = &noisy_sysfs_ops, }; /* * noisy ctl attribute. * This is declared as an attribute of the subsystem, and added in * noisy_init(). * * Reading this attribute dumps all the registered noisy probes. * Writing to it either adds or deletes a noisy probe, as described at * the beginning of the file. */ static ssize_t ctl_show(struct subsystem * s, char * page, size_t count, loff_t off) { char * str = page; int ret = 0; down(&noisy_sem); if (!off) { struct list_head * entry, * next; list_for_each_safe(entry,next,&noisy_subsys.list) { struct nprobe * n = to_nprobe(entry); if ((ret + MAX_MSG_SIZE) > PAGE_SIZE) break; str += snprintf(str,PAGE_SIZE - ret,"%p: %s\n", n->probe.addr,n->message); ret = str - page; } } up(&noisy_sem); return ret; } static int add(unsigned long addr, char * message) { struct nprobe * n; int error = 0; if (!valid_kernel_address(addr)) return -EFAULT; n = kmalloc(sizeof(struct nprobe),GFP_KERNEL); if (!n) return -ENOMEM; memset(n,0,sizeof(struct nprobe)); n->probe.addr = (kprobe_opcode_t *)addr; strncpy(n->message,message,MAX_MSG_SIZE); n->probe.pre_handler = noisy_pre_handler; n->probe.post_handler = noisy_post_handler; n->probe.fault_handler = noisy_fault_handler; /* Doing this manually will be unnecessary soon. */ kobject_init(&n->kobj); n->kobj.subsys = &noisy_subsys; snprintf(n->kobj.name, KOBJ_NAME_LEN, "%p", n->probe.addr); if ((error = register_kprobe(&n->probe))) { printk(KERN_ERR "Unable to register probe at %p\n", (n->probe).addr); goto Error; } if ((error = kobject_register(&n->kobj))) { unregister_kprobe(&n->probe); goto Error; } goto Done; Error: kfree(n); Done: return error; } static int del(unsigned long addr) { struct list_head * entry; struct nprobe * n; list_for_each(entry,&noisy_subsys.list) { n = to_nprobe(entry); if ((unsigned long)(n->probe.addr) == addr) { kobject_unregister(&n->kobj); unregister_kprobe(&n->probe); return 0; } } return -EFAULT; } static ssize_t ctl_store(struct subsystem * s, const char * page, size_t count, loff_t off) { char message[MAX_MSG_SIZE]; char ctl[16]; unsigned long addr; int num; int error; int ret = 0; down(&noisy_sem); if (off) goto Done; num = sscanf(page,"%15s 0x%lx %128s",ctl,&addr,message); if (!num) { error = -EINVAL; goto Done; } if (!strcmp(ctl,"add") && num == 3) error = add(addr,message); else if (!strcmp(ctl,"del") && num == 2) error = del(addr); else error = -EINVAL; ret = error ? error : count; Done: up(&noisy_sem); return ret; } static struct subsys_attribute subsys_attr_ctl = { .attr = { .name = "ctl", .mode = 0644 }, .show = ctl_show, .store = ctl_store, }; static int __init noisy_init(void) { subsystem_register(&noisy_subsys); subsys_create_file(&noisy_subsys,&subsys_attr_ctl); return 0; } static void __exit noisy_exit (void) { subsys_remove_file(&noisy_subsys,&subsys_attr_ctl); subsystem_unregister(&noisy_subsys); } module_init(noisy_init); module_exit(noisy_exit); MODULE_AUTHOR("Rusty Lynch"); MODULE_LICENSE("GPL");