Index: usb-2.6/bptest/Makefile =================================================================== --- /dev/null +++ usb-2.6/bptest/Makefile @@ -0,0 +1 @@ +obj-m += bptest.o Index: usb-2.6/bptest/bptest.c =================================================================== --- /dev/null +++ usb-2.6/bptest/bptest.c @@ -0,0 +1,460 @@ +/* + * Test driver for hardware breakpoints. + * + * Copyright (C) 2007 Alan Stern + */ + +/* + * When this driver is loaded, it will create several attribute files + * under /sys/bus/platform/drivers/bptest: + * + * call, read, write, and bp0,..., bp3. + * + * It also allocates a 32-byte array (called "bytes") for testing data + * breakpoints, and it contains four do-nothing routines, r0(),..., r3(), + * for testing execution breakpoints. + * + * Writing to the "call" attribute causes the rN routines to be called; + * "echo >call N" will call rN(), where N is 0, 1, 2, or 3. Similarly, + * "echo >call" will call all four routines. + * + * The byte array can be accessed through the "read" and "write" + * attributes. "echo >read N" will read bytes[N], and "echo >write N V" + * will store V in bytes[N], where N is between 0 and 31. There are + * no provision for multi-byte accesses; they shouldn't be needed for + * simple testing. + * + * The driver contains four hw_breakpoint structures, which can be + * accessed through the "bpN" attributes. Reading the attribute file + * will yield the hw_breakpoint's current settings. The settings can be + * altered by writing the attribute. The format to use is: + * + * echo >bpN priority type address [len] + * + * priority must be a number between 0 and 255. type must be one of 'e' + * (execution), 'r' (read), 'w' (write), or 'b' (both read/write). + * address must be a number between 0 and 31; if type is 'e' then address + * must be between 0 and 3. len must 1, 2, 4, or 8, but if type is 'e' + * then len is optional and ignored. + * + * Execution breakpoints are set on the rN routine and data breakpoints + * are set on bytes[N], where N is the address value. You can unregister + * a breakpoint by doing "echo >bpN u", where 'u' is any non-digit. + * + * (Note: On i386 certain values are not implemented. len cannot be set + * to 8 and type cannot be set to 'r'.) + * + * The driver prints lots of information to the system log as it runs. + * To best see things as they happen, use a VT console and set the + * logging level high (I use Alt-SysRq-9). + */ + + +#include +#include +#include + +MODULE_AUTHOR("Alan Stern "); +MODULE_DESCRIPTION("Hardware Breakpoint test driver"); +MODULE_LICENSE("GPL"); + + +static struct hw_breakpoint bps[4]; + +#define NUM_BYTES 32 +static unsigned char bytes[NUM_BYTES] __attribute__((aligned(8))); + +/* Write n to read bytes[n] */ +static ssize_t read_store(struct device_driver *d, const char *buf, + size_t count) +{ + int n = -1; + + if (sscanf(buf, "%d", &n) < 1 || n < 0 || n >= NUM_BYTES) { + printk(KERN_WARNING "bptest: read: invalid index %d\n", n); + return -EINVAL; + } + printk(KERN_INFO "bptest: read: bytes[%d] = %d\n", n, bytes[n]); + return count; +} +static DRIVER_ATTR(read, 0200, NULL, read_store); + +/* Write n v to set bytes[n] = v */ +static ssize_t write_store(struct device_driver *d, const char *buf, + size_t count) +{ + int n = -1; + int v; + + if (sscanf(buf, "%d %d", &n, &v) < 2 || n < 0 || n >= NUM_BYTES) { + printk(KERN_WARNING "bptest: write: invalid index %d\n", n); + return -EINVAL; + } + bytes[n] = v; + printk(KERN_INFO "bptest: write: bytes[%d] <- %d\n", n, v); + return count; +} +static DRIVER_ATTR(write, 0200, NULL, write_store); + + +/* Dummy routines for testing instruction breakpoints */ +static void r0(void) +{ + printk(KERN_INFO "This is r%d\n", 0); +} +static void r1(void) +{ + printk(KERN_INFO "This is r%d\n", 1); +} +static void r2(void) +{ + printk(KERN_INFO "This is r%d\n", 2); +} +static void r3(void) +{ + printk(KERN_INFO "This is r%d\n", 3); +} + +static void (*rtns[])(void) = { + r0, r1, r2, r3 +}; + + +/* Write n to call routine r##n, or a blank line to call them all */ +static ssize_t call_store(struct device_driver *d, const char *buf, + size_t count) +{ + int n; + + if (sscanf(buf, "%d", &n) == 0) { + printk(KERN_INFO "bptest: call all routines\n"); + r0(); + r1(); + r2(); + r3(); + } else if (n >= 0 && n < 4) { + printk(KERN_INFO "bptest: call r%d\n", n); + rtns[n](); + } else { + printk(KERN_WARNING "bptest: call: invalid index: %d\n", n); + count = -EINVAL; + } + return count; +} +static DRIVER_ATTR(call, 0200, NULL, call_store); + + +/* Breakpoint callbacks */ +static void bptest_triggered(struct hw_breakpoint *bp, struct pt_regs *regs) +{ + printk(KERN_INFO "Breakpoint %d triggered\n", bp - bps); +} + +static void bptest_installed(struct hw_breakpoint *bp) +{ + printk(KERN_INFO "Breakpoint %d installed\n", bp - bps); +} + +static void bptest_uninstalled(struct hw_breakpoint *bp) +{ + printk(KERN_INFO "Breakpoint %d uninstalled\n", bp - bps); +} + + +/* Breakpoint attribute files for testing */ +static ssize_t bp_show(int n, char *buf) +{ + struct hw_breakpoint *bp = &bps[n]; + int a, len, type; + + if (!bp->status) + return sprintf(buf, "bp%d: unregistered\n", n); + + len = -1; + switch (hw_breakpoint_get_len(bp)) { +#ifdef HW_BREAKPOINT_LEN_1 + case HW_BREAKPOINT_LEN_1: len = 1; break; +#endif +#ifdef HW_BREAKPOINT_LEN_2 + case HW_BREAKPOINT_LEN_2: len = 2; break; +#endif +#ifdef HW_BREAKPOINT_LEN_4 + case HW_BREAKPOINT_LEN_4: len = 4; break; +#endif +#ifdef HW_BREAKPOINT_LEN_8 + case HW_BREAKPOINT_LEN_8: len = 8; break; +#endif + } + + type = '?'; + switch (hw_breakpoint_get_type(bp)) { +#ifdef HW_BREAKPOINT_READ + case HW_BREAKPOINT_READ: type = 'r'; break; +#endif +#ifdef HW_BREAKPOINT_WRITE + case HW_BREAKPOINT_WRITE: type = 'w'; break; +#endif +#ifdef HW_BREAKPOINT_RW + case HW_BREAKPOINT_RW: type = 'b'; break; +#endif +#ifdef HW_BREAKPOINT_EXECUTE + case HW_BREAKPOINT_EXECUTE: type = 'e'; break; +#endif + } + + a = -1; + if (type == 'e') { + const void *addr = hw_breakpoint_get_kaddr(bp); + + if (addr == r0) + a = 0; + else if (addr == r1) + a = 1; + else if (addr == r2) + a = 2; + else if (addr == r3) + a = 3; + } else { + const unsigned char *p = hw_breakpoint_get_kaddr(bp); + + if (p >= bytes && p < bytes + NUM_BYTES) + a = p - bytes; + } + + return sprintf(buf, "bp%d: %d %c %d %d [%sinstalled]\n", + n, bp->priority, type, a, len, + (bp->status < HW_BREAKPOINT_INSTALLED ? "not " : "")); +} + +static ssize_t bp_store(int n, const char *buf, size_t count) +{ + struct hw_breakpoint *bp = &bps[n]; + int prio, a, alen; + char atype; + unsigned len, type; + int i; + + if (count <= 1) { + printk(KERN_INFO "bptest: bp%d: format: priority type " + "address len\n", n); + printk(KERN_INFO " type = r, w, b, or e; address = 0 - 31; " + "len = 1, 2, 4, or 8\n"); + printk(KERN_INFO " Write any non-digit to unregister\n"); + return count; + } + + unregister_kernel_hw_breakpoint(bp); + printk(KERN_INFO "bptest: bp%d unregistered\n", n); + + alen = -1; + i = sscanf(buf, "%d %c %d %d", &prio, &atype, &a, &alen); + if (i == 0) + return count; + if (i < 3) { + printk(KERN_WARNING "bptest: bp%d: too few fields\n", n); + return -EINVAL; + } + + bp->priority = prio; + switch (atype) { +#ifdef HW_BREAKPOINT_EXECUTE + case 'e': + type = HW_BREAKPOINT_EXECUTE; + len = HW_BREAKPOINT_LEN_EXECUTE; + break; +#endif +#ifdef HW_BREAKPOINT_READ + case 'r': + type = HW_BREAKPOINT_READ; + break; +#endif +#ifdef HW_BREAKPOINT_WRITE + case 'w': + type = HW_BREAKPOINT_WRITE; + break; +#endif +#ifdef HW_BREAKPOINT_RW + case 'b': + type = HW_BREAKPOINT_RW; + break; +#endif + default: + printk(KERN_WARNING "bptest: bp%d: invalid type %c\n", + n, atype); + return -EINVAL; + } + + if (a < 0 || a >= NUM_BYTES || (a >= 4 && atype == 'e')) { + printk(KERN_WARNING "bptest: bp%d: invalid address %d\n", + n, a); + return -EINVAL; + } + if (atype == 'e') + hw_breakpoint_kinit(bp, rtns[a], len, type); + else { + switch (alen) { +#ifdef HW_BREAKPOINT_LEN_1 + case 1: len = HW_BREAKPOINT_LEN_1; break; +#endif +#ifdef HW_BREAKPOINT_LEN_2 + case 2: len = HW_BREAKPOINT_LEN_2; break; +#endif +#ifdef HW_BREAKPOINT_LEN_4 + case 4: len = HW_BREAKPOINT_LEN_4; break; +#endif +#ifdef HW_BREAKPOINT_LEN_8 + case 8: len = HW_BREAKPOINT_LEN_8; break; +#endif + default: + printk(KERN_WARNING "bptest: bp%d: invalid len %d\n", + n, alen); + return -EINVAL; + break; + } + hw_breakpoint_kinit(bp, &bytes[a], len, type); + } + + bp->triggered = bptest_triggered; + bp->installed = bptest_installed; + bp->uninstalled = bptest_uninstalled; + + i = register_kernel_hw_breakpoint(bp); + if (i < 0) { + printk(KERN_WARNING "bptest: bp%d: failed to register %d\n", + n, i); + count = i; + } else + printk(KERN_INFO "bptest: bp%d registered: %d\n", n, i); + return count; +} + + +static ssize_t bp0_show(struct device_driver *d, char *buf) +{ + return bp_show(0, buf); +} +static ssize_t bp0_store(struct device_driver *d, const char *buf, + size_t count) +{ + return bp_store(0, buf, count); +} +static DRIVER_ATTR(bp0, 0600, bp0_show, bp0_store); + +static ssize_t bp1_show(struct device_driver *d, char *buf) +{ + return bp_show(1, buf); +} +static ssize_t bp1_store(struct device_driver *d, const char *buf, + size_t count) +{ + return bp_store(1, buf, count); +} +static DRIVER_ATTR(bp1, 0600, bp1_show, bp1_store); + +static ssize_t bp2_show(struct device_driver *d, char *buf) +{ + return bp_show(2, buf); +} +static ssize_t bp2_store(struct device_driver *d, const char *buf, + size_t count) +{ + return bp_store(2, buf, count); +} +static DRIVER_ATTR(bp2, 0600, bp2_show, bp2_store); + +static ssize_t bp3_show(struct device_driver *d, char *buf) +{ + return bp_show(3, buf); +} +static ssize_t bp3_store(struct device_driver *d, const char *buf, + size_t count) +{ + return bp_store(3, buf, count); +} +static DRIVER_ATTR(bp3, 0600, bp3_show, bp3_store); + + +static int bptest_probe(struct platform_device *pdev) +{ + return -ENODEV; +} + +static int bptest_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver bptest_driver = { + .probe = bptest_probe, + .remove = bptest_remove, + .driver = { + .name = "bptest", + .owner = THIS_MODULE, + } +}; + + +static struct driver_attribute *(bptest_group[]) = { + &driver_attr_bp0, + &driver_attr_bp1, + &driver_attr_bp2, + &driver_attr_bp3, + &driver_attr_call, + &driver_attr_read, + &driver_attr_write, + NULL +}; + +static int add_files(void) +{ + int rc = 0; + struct driver_attribute **g; + + for (g = bptest_group; *g; ++g) { + rc = driver_create_file(&bptest_driver.driver, *g); + if (rc) + break; + } + return rc; +} + +static void remove_files(void) +{ + struct driver_attribute **g; + + for (g = bptest_group; *g; ++g) + driver_remove_file(&bptest_driver.driver, *g); +} + +static int __init bptest_init(void) +{ + int rc; + + rc = platform_driver_register(&bptest_driver); + if (rc) { + printk(KERN_ERR "Failed to register bptest driver: %d\n", rc); + return rc; + } + rc = add_files(); + if (rc) { + remove_files(); + platform_driver_unregister(&bptest_driver); + return rc; + } + printk("bptest loaded\n"); + return 0; +} + +static void __exit bptest_exit(void) +{ + int n; + + remove_files(); + for (n = 0; n < 4; ++n) + unregister_kernel_hw_breakpoint(&bps[n]); + platform_driver_unregister(&bptest_driver); + printk("bptest unloaded\n"); +} + +module_init(bptest_init); +module_exit(bptest_exit);